3.0 KiB
3.0 KiB
TESTING.md
Runner: built-in node:test + node:assert/strict. No Jest/Mocha.
Script: "test": "NODE_ENV=test node --test --test-concurrency=1".
Do NOT pass a directory or an unquoted glob (node --test tests/ finds nothing on Node 21+; tests/**/*.test.js is expanded by sh and skips top-level files). Plain node --test auto-discovers every *.test.js.
Test files MUST be named *.test.js.
Layout
tests/unit/— pure functions insrc/gameandsrc/utils. No DB, no HTTP.tests/integration/— bootappwith a temporary SQLite file, run migrations + seeds, call the real HTTP API.
Test DB setup helper (tests/helpers.js)
- Create temp path via
os.tmpdir(); setprocess.env.DB_PATHBEFORE requiringsrc/db. - Run migrate + seed programmatically.
- Start the app on port 0 (
app.listen(0)), return base URL and aclose()fn. - Provide
registerAndLogin(base)which returns a cookie string to reuse infetchheaders. - Delete the temp file in
after().
What MUST be tested
| Area | Cases |
|---|---|
| energy | no time passed; partial tick; full cap; clock went backwards; large gap |
| leveling | exact XP threshold; multi-level jump; max level cap |
| stats | each class; equipment bonuses applied |
| combat | fixed seed → identical log; hero win; hero loss; round cap; min damage 1 |
| rewards | level-diff multiplier clamps; loot chance 0% / 100% |
| auth | register ok; duplicate (case-insensitive); weak password; login ok/fail; session persists; logout |
| hunt | spends energy; insufficient energy rejected; HP too low rejected; locked zone rejected; rewards persisted |
| inventory | equip swaps slot; can't equip others' items; use potion consumes 1 |
| shop | insufficient gold; level gate; sell equipped rejected; quantities validated |
| expedition | claim before end rejected; claim once only; second start while active rejected |
| leaderboard | sort allowlist; ordering correct |
Determinism
Inject RNG: createRng(seed) (mulberry32) in src/utils/rng.js exposing .float(), .int(min,max), .range(min,max). In integration tests, allow overriding the RNG via an app factory option or env seed.
Inject time: tests set time.setNow(fn) to fast-forward energy/expeditions instead of sleeping.
Naming
describe('energy.computeEnergy', () => { it('caps at max', ...) }).
Rules
- Run
npm testand check the TOTAL against the sum of tests in all*.test.jsfiles. A bad glob once silently skipped 17 tests. - Integration tests use
setup()fromtests/helpers.js, which clears everysrc/module from the require cache so each test gets a fresh app and DB. Never hand-pick modules to clear. - Force game outcomes with test-only zones/monsters and the
{ rng, now }options of services — don't rely on luck, and don't weaken assertions to "winner is hero or monster". - A failing test is never deleted to make the suite green. Fix the code or log a Known Issue.
- New bug → write the failing test first, then fix.
- Update the Test Summary table in STATUS.md after each phase.