Files
mudserver/TESTING.md
AI Agent 2689f9e29e
Some checks failed
Smoke tests / Build and smoke test (push) Has been cancelled
Smoke tests / Build and smoke test (pull_request) Successful in 1m23s
Add tests for weather and time of day system
2026-03-19 17:01:31 -06:00

14 KiB

Pre-Commit Test Checklist

Automated smoke test (same as CI): run from the repo root:

./run-tests.sh

This builds the server and mudtool, starts the server with a temporary DB, and runs the same sequence as the smoke steps in .gitea/workflows/smoke-tests.yml (new player, persistence, mudtool admin, in-game admin, registration gate, tick combat), then cleans up. Use MUD_TEST_DB (default ./mudserver.db.test) so you do not overwrite your normal mudserver.db.

Prerequisites: Rust toolchain (cargo), OpenSSH client, and OpenBSD nc (netcat-openbsd on Debian/Ubuntu) for scripts/ci/wait-for-tcp.sh. CI installs these explicitly before the smoke steps.


Run through the checks below before every commit to ensure consistent feature coverage.

Build

  • cargo build succeeds with no errors
  • cargo build --bin mudtool succeeds

Server Startup

  • Server starts: RUST_LOG=info ./target/debug/mudserver
  • World loads all rooms, NPCs, objects, races, classes (check log output)
  • Database opens (or creates) successfully
  • Tick engine starts and logs tick rate

Character Creation

  • New player SSH → gets chargen flow (race + class selection)
  • Chargen accepts both number and name input
  • All races display with expanded info (size, traits, natural attacks, vision)
  • Dragon race shows custom body slots, natural armor, fire breath, vision types
  • After chargen, player appears in spawn room with correct stats
  • Stats reflect race modifiers (STR, DEX, CON, INT, WIS, PER, CHA)
  • Player saved to DB after creation

Player Persistence

  • Reconnecting player skips chargen, sees "Welcome back!"
  • Room, stats, inventory, equipment all restored from DB
  • Status effects persist across logout/login (stored in DB)
  • Status effects continue ticking while player is offline
  • On reconnect, expired effects are gone; active effects resume
  • Verify with: sqlite3 mudserver.db "SELECT * FROM players;"

Look Command

  • look with no args shows the room (NPCs, objects, exits, players)
  • look <npc> shows NPC description, stats, and attitude
  • look <object> shows object description (floor or inventory)
  • look <exit> shows where the exit leads
  • look <player> shows player race, level, combat status
  • look <invalid> shows "You don't see X here."

Movement & Navigation

  • go north, n, south, s, etc. all work
  • Invalid direction shows error
  • Room view shows: NPCs (colored by attitude), objects, exits, other players
  • Cannot move while in combat (combat lockout)

NPC Interaction

  • examine <npc> shows description, stats, attitude label
  • talk <friendly> shows greeting dialogue
  • talk <hostile> shows snarl message
  • Dead NPCs don't appear in room view

Combat - Tick-Based

  • attack <npc> enters combat state with any NPC that has combat stats
  • Attacking friendly/neutral NPCs is allowed but incurs attitude penalties
  • Attacking non-hostile NPC: attitude shift -30 individual, -15 faction
  • "The locals look on in horror" message when attacking non-hostile
  • Combat rounds resolve automatically on server ticks (not on command)
  • Player receives tick-by-tick combat output (damage dealt, damage taken)
  • Default combat action is "attack" if no other action queued
  • defend / def sets defensive stance (reduced incoming damage next tick)
  • NPC death: awards XP, shifts attitude -10, shifts faction -5
  • Player death: respawns at spawn room with full HP, combat cleared, effects cleared
  • NPCs respawn after configured time
  • Combat lockout: can only attack/defend/flee/look/stats/inv/use/quit during combat
  • flee queues escape attempt — may fail based on stats
  • use <item> in combat queues item use for next tick
  • Multiple ticks of combat resolve correctly without player input
  • Combat ends when NPC dies (player exits combat state)
  • Combat ends when player flees successfully
  • NPCs without explicit [combat] section get default stats (20 HP, 4 ATK, 2 DEF, 5 XP)

Combat - NPC AI

  • Hostile NPCs auto-engage players who enter their room
  • Aggressive NPCs do NOT auto-engage (only attackable, not initiating)
  • NPC attacks resolve on tick alongside player attacks
  • NPCs in combat continue attacking even if player sends no commands

Combat - RNG

  • Damage varies between hits (not identical each time)
  • Multiple rapid attacks produce different damage values

Items & Equipment Slots

  • take <item> picks up takeable objects
  • drop <item> places item in room
  • equip <weapon> equips to main_hand slot (backwards-compat via kind)
  • equip <armor> equips to appropriate slot (obj slot field or fallback)
  • Equipping to an occupied slot returns old item to inventory
  • equip fails if race doesn't have the required slot
  • Objects with explicit slot field use that slot
  • use <consumable> heals and removes item (immediate out of combat)
  • use <consumable> in combat queues for next tick
  • inventory shows equipped items by slot name + bag items
  • stats shows equipment bonuses and natural bonuses separately

Status Effects

  • Poison deals damage each tick, shows message to player
  • Regeneration heals each tick, shows message to player
  • Status effects expire after their duration (in ticks)
  • stats command shows active status effects and remaining duration
  • Multiple status effects can be active simultaneously
  • Negative status effects cleared on player death/respawn
  • Status effects on offline players resolve by wall-clock time on next login

Weather & Time

  • Outdoor rooms display time of day (e.g., [Night], [Morning]).
  • Outdoor rooms display current weather (e.g., The sky is clear, It is raining).
  • Indoor rooms do not show weather or time of day.
  • Rain or storm applies the wet status effect to players in outdoor rooms.
  • Weather changes periodically and broadcasts messages to players in outdoor rooms.

Guilds

  • guild list shows all available guilds with descriptions
  • guild info <name> shows guild details, growth stats, and spell list
  • guild join <name> adds player to guild at level 1
  • guild join grants base mana/endurance from guild
  • guild leave <name> removes guild membership
  • Cannot join a guild twice
  • Cannot leave a guild you're not in
  • Race-restricted guilds reject restricted races
  • Player level requirement enforced on guild join
  • Multi-guild: player can be in multiple guilds simultaneously
  • Guild membership persists across logout/login (stored in player_guilds table)
  • stats shows guild memberships with levels

Spells & Casting

  • spells lists known spells grouped by guild with cost and cooldown
  • Spells filtered by guild level (only shows spells at or below current guild level)
  • cast <spell> out of combat: heal/utility resolves immediately
  • cast <spell> out of combat: offensive spells blocked ("enter combat first")
  • cast <spell> in combat: queues as CombatAction, resolves on tick
  • Spell resolves: offensive deals damage to NPC, shows damage + type
  • Spell resolves: heal restores HP, shows amount healed
  • Spell resolves: utility applies status effect
  • Mana deducted on cast; blocked if insufficient ("Not enough mana")
  • Endurance deducted on cast; blocked if insufficient
  • Cooldowns applied after casting; blocked if on cooldown ("X ticks remaining")
  • Cooldowns tick down each server tick
  • NPC can die from spell damage (awards XP, ends combat)
  • Spell partial name matching works ("magic" matches "Magic Missile")

Chargen & Guild Seeding

  • Class selection shows "→ joins " when class has a guild
  • Creating a character with a class that references a guild auto-joins that guild
  • Starting mana/endurance calculated from guild base + racial stat modifiers
  • Guild membership saved to DB at character creation

Passive Regeneration

  • Players out of combat slowly regenerate HP over ticks
  • Players out of combat slowly regenerate mana over ticks
  • Players out of combat slowly regenerate endurance over ticks
  • Regeneration does not occur while in combat
  • HP/mana/endurance do not exceed their maximums

Tick Engine

  • Tick runs at configured interval (~3 seconds)
  • Tick processes: NPC AI → combat rounds → status effects → respawns → regen
  • Tick output is delivered to players promptly
  • Server remains responsive to immediate commands between ticks
  • Multiple players in separate combats are processed independently per tick

NPC Race & Class

  • NPCs with fixed race/class in TOML show that race/class
  • NPCs without race get a random non-hidden race at spawn
  • NPCs without class: race default_class used, or random non-hidden if no default
  • look <npc> shows NPC race and class
  • examine <npc> shows NPC race and class
  • Rat shows "Beast Creature" (fixed race/class)
  • Barkeep shows a random race + Peasant (no fixed race, human default class)
  • Thief shows random race + Rogue (no fixed race, fixed class)
  • Guard shows random race + Warrior (no fixed race, fixed class)
  • On NPC respawn, race/class re-rolled if not fixed in TOML
  • Hidden races (Beast) do not appear in character creation
  • Hidden classes (Peasant, Creature) do not appear in character creation

Race System

  • Existing races (Human, Elf, Dwarf, Orc, Halfling) load with expanded fields
  • Dragon race loads with custom body, natural attacks, resistances, traits
  • Dragon gets custom equipment slots (forelegs, hindlegs, wings, tail)
  • Dragon's natural armor (8) shows in stats and affects defense
  • Dragon's natural attacks (fire breath 15dmg) affect effective attack
  • Items magically resize — no size restrictions on gear (dragon can use swords)
  • Races without explicit [body.slots] get default humanoid slots
  • Stat modifiers include PER (perception) and CHA (charisma)
  • Race traits and disadvantages display during chargen
  • XP rate modifier stored per race (dragon = 0.7x)
  • Regen modifiers stored per race (dragon HP regen = 1.5x)

Attitude System

  • Per-player NPC attitudes stored in DB
  • examine shows attitude label per-player
  • Killing NPC shifts attitude (individual -10, faction -5)
  • Verify: sqlite3 mudserver.db "SELECT * FROM npc_attitudes;"

Admin System

  • Non-admin can't use admin commands (gets error)
  • Set admin via mudtool: mudtool players set-admin <name> true
  • admin help shows admin command list
  • admin promote <player> grants admin (verify in DB)
  • admin demote <player> revokes admin
  • admin kick <player> disconnects target player
  • admin teleport <room_id> warps to room (shows room list on invalid)
  • admin registration off blocks new player creation
  • admin registration on re-enables it
  • admin announce <msg> broadcasts to all players
  • admin heal heals self; admin heal <player> heals target
  • admin info <player> shows detailed stats + attitudes
  • admin setattitude <player> <npc> <value> modifies attitude
  • admin list shows all players with online/offline status

Registration Gate

  • With registration open (default), new players can create characters
  • With registration off, new SSH connections get rejection message
  • Existing players can still log in when registration is closed

MUD Tool - CLI

  • mudtool validate -w ./world checks world data (schemas, references, values)
  • mudtool players list shows all players
  • mudtool players show <name> shows details
  • mudtool players set-admin <name> true works
  • mudtool players delete <name> removes player + attitudes
  • mudtool settings list shows settings
  • mudtool settings set registration_open false works
  • mudtool attitudes list <player> shows attitudes
  • mudtool attitudes set <player> <npc> <value> works

MUD Tool - TUI

  • mudtool tui launches interactive interface
  • Tab/1/2/3 switches between Players, Settings, Attitudes tabs
  • Arrow keys navigate rows
  • 'a' toggles admin on Players tab
  • 'd' prompts delete confirmation on Players tab
  • Enter edits value on Settings and Attitudes tabs
  • ←→ switches player on Attitudes tab
  • 'q' exits TUI

JSON-RPC Interface

  • list_commands returns the currently handleable command list
  • New commands added in commands.rs are automatically discovered
  • login accepts an existing player name (requires character to be created first)
  • Command output is stripped of ANSI color codes for API consumption
  • Verify manually with: echo '{"_jsonrpc": "2.0", "method": "list_commands", "params": {}, "id": 1}' | nc localhost 2223

Quick Smoke Test Script

CI: each scenario is a separate step in .gitea/workflows/smoke-tests.yml (each SSH step starts mudserver, runs the block, stops it; the same TEST_DB file carries state between steps). The last step exercises JSON-RPC login / list_commands on port 2223 (expects shop in the command list). Local: ./run-tests.sh runs the full sequence in one process. When you add or change coverage, update the workflow steps and run-tests.sh together, and keep the checklist sections above aligned.