319 lines
14 KiB
Markdown
319 lines
14 KiB
Markdown
# Pre-Commit Test Checklist
|
|
|
|
**Automated smoke test (same as CI):** run from the repo root:
|
|
|
|
```bash
|
|
./run-tests.sh
|
|
```
|
|
|
|
This builds the server and mudtool, starts the server with a temporary DB, runs the smoke test below (new player, persistence, admin, registration gate, combat), then cleans up. Use it locally to match what Gitea Actions run on push/pull_request. The script uses `MUD_TEST_DB` (default `./mudserver.db.test`) so it does not overwrite your normal `mudserver.db`.
|
|
|
|
Prerequisites: Rust toolchain (cargo), ssh client. In CI, Rust is installed by the workflow.
|
|
|
|
---
|
|
|
|
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
|
|
|
|
## 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 <Guild Name>" 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 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
|
|
|
|
## Quick Smoke Test Script
|
|
|
|
The canonical implementation is **`./run-tests.sh`** (see top of this file). The following is the same sequence for reference; when writing or extending tests, keep `run-tests.sh` and this section in sync.
|
|
|
|
```bash
|
|
# Start server in background (use -d for test DB so you don't overwrite mudserver.db)
|
|
TEST_DB=./mudserver.db.test
|
|
RUST_LOG=info ./target/debug/mudserver -d "$TEST_DB" &
|
|
SERVER_PID=$!
|
|
sleep 2
|
|
|
|
# Test 1: New player creation + basic commands
|
|
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222 smoketest@localhost <<'EOF'
|
|
1
|
|
1
|
|
look
|
|
stats
|
|
go north
|
|
talk barkeep
|
|
go south
|
|
go south
|
|
examine thief
|
|
attack thief
|
|
flee
|
|
quit
|
|
EOF
|
|
|
|
# Test 2: Persistence - reconnect
|
|
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222 smoketest@localhost <<'EOF'
|
|
look
|
|
stats
|
|
quit
|
|
EOF
|
|
|
|
# Test 3: Admin via mudtool (use same test DB)
|
|
./target/debug/mudtool -d "$TEST_DB" players list
|
|
./target/debug/mudtool -d "$TEST_DB" players set-admin smoketest true
|
|
./target/debug/mudtool -d "$TEST_DB" players show smoketest
|
|
./target/debug/mudtool -d "$TEST_DB" settings set registration_open false
|
|
./target/debug/mudtool -d "$TEST_DB" settings list
|
|
|
|
# Test 4: Admin commands in-game
|
|
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222 smoketest@localhost <<'EOF'
|
|
admin help
|
|
admin list
|
|
admin registration on
|
|
admin info smoketest
|
|
quit
|
|
EOF
|
|
|
|
# Test 5: Registration gate
|
|
./target/debug/mudtool -d "$TEST_DB" settings set registration_open false
|
|
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222 newplayer@localhost <<'EOF'
|
|
quit
|
|
EOF
|
|
|
|
# Test 6: Tick-based combat (connect and wait for ticks)
|
|
./target/debug/mudtool -d "$TEST_DB" settings set registration_open true
|
|
./target/debug/mudtool -d "$TEST_DB" players delete smoketest
|
|
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222 smoketest@localhost <<'EOF'
|
|
1
|
|
1
|
|
go south
|
|
go south
|
|
attack thief
|
|
EOF
|
|
# Wait for several combat ticks to resolve
|
|
sleep 8
|
|
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222 smoketest@localhost <<'EOF'
|
|
stats
|
|
quit
|
|
EOF
|
|
# Verify XP changed (combat happened via ticks)
|
|
|
|
# Cleanup
|
|
./target/debug/mudtool -d "$TEST_DB" settings set registration_open true
|
|
./target/debug/mudtool -d "$TEST_DB" players delete smoketest
|
|
kill $SERVER_PID
|
|
```
|