| name | masoi-be-testing |
| description | Create and maintain backend unit tests, integration tests, and API tests for Ma Sói server. Covers gameEngine.js pure functions, Socket.IO handler logic, Redis/PostgreSQL data layer, authentication, room lifecycle, and timer-driven resolution. Use when adding server-side test coverage, debugging server logic, or verifying backend changes. |
Ma Sói Backend Testing
Workflow
- Identify the backend component to test:
server/gameEngine.js → pure function unit tests (role config, win conditions, wolf count).
server/index.js socket handlers → integration tests with mock socket/Redis.
server/db/redis.js → Redis data layer tests with mock/in-memory store.
server/db/postgres.js → PostgreSQL data layer tests with mock client.
- Authentication flow → register/login handler tests with bcrypt mock.
- Write tests following patterns in
references/be-test-patterns.md.
- Run tests:
node --check server/index.js (syntax baseline).
node --check server/gameEngine.js (syntax baseline).
cd server && npm test (if test script configured).
- For concurrency tests, simulate concurrent socket events and verify
withRoomLock serial execution.
Test Categories
Unit Tests — gameEngine.js
calcWolfCount(n) returns correct wolf count for player counts 4–18.
getRoleConfig(n, customRoles) produces valid role list with correct wolf/villager ratio.
sanitizeCustomRoles(roles, n) enforces wolf count limits and role constraints.
assignRoles(players, customRoles) returns exactly n assignments with shuffled distribution.
checkWinCondition(players) for all win/draw/continue states.
generateRoomCode() produces valid 6-character alphanumeric codes.
Unit Tests — Socket Handler Logic
auth handler validates credentials and maps userId to socketId.
create_room handler creates room with correct initial state.
join_room handler enforces room existence, capacity, and game-not-started.
toggle_ready handler toggles player ready state correctly.
start_game handler validates player count, ready state, and assigns roles.
night_action handler validates role, phase, and alive status before accepting action.
cast_vote handler validates phase, alive status, and target validity.
Integration Tests — Night Resolution
- Wolf kill with no saves → target dies.
- Doctor saves wolf target → target lives.
- Witch saves wolf target → target lives, save potion consumed.
- Witch poisons + wolf kill → 2 deaths in one night.
- Doctor saves target, witch also saves same target → no double-save error.
- Hunter dies at night →
hunter_must_shoot triggers correctly.
- All night actions skip → peaceful night, no deaths.
- Timer-forced resolution → pending actions auto-resolved as skip.
Integration Tests — Vote Resolution
- Majority vote → target dies.
- Tie vote → nobody dies.
- Idiot first hanging → survives,
idiotRevealed = true.
- Idiot second hanging → actually dies.
- Wolf King hanged →
wolf_king_must_choose triggers drag mechanic.
- Hunter hanged →
hunter_must_shoot triggers.
- Chain: Wolf King drags Hunter → Hunter shoots → chain resolution.
Integration Tests — Room Lifecycle
- Create room → join → ready → start → play → end → play again.
- Host disconnect → host transfer to next player.
- All players disconnect → room cleanup after timeout.
- Kick player → player removed, room state updated.
- Leave room → player removed, host transfer if needed.
- Close room → all players notified, room deleted.
Data Layer Tests — Redis
saveRoom(room) serializes and stores room state.
getRoom(code) deserializes room state correctly.
deleteRoom(code) removes room and associated keys.
saveUserRoom(userId, roomCode) and getUserRoom(userId) mapping.
clearUserRoom(userId) cleanup.
- Room TTL and expiry behavior.
Data Layer Tests — PostgreSQL
createUser(username, passwordHash) inserts correctly.
findUserByUsername(username) returns user or null.
saveGameResult(gameData) persists game history.
getGameHistory(userId) returns sorted game history.
- Connection pool handling and error recovery.
Authentication Tests
- Register with valid credentials → user created, token returned.
- Register with duplicate username → error returned.
- Login with correct password → authenticated.
- Login with wrong password → rejected.
- Guest auth → temporary userId assigned.
- Socket reconnect with existing userId → session restored.
Concurrency & Lock Tests
withRoomLock(code, fn) ensures serial execution.
- Concurrent
night_action from two wolves → only one WOLF_ATTACK set.
- Concurrent
resolveNight calls → only one resolution executes.
- Concurrent
resolveVote calls → only one resolution executes.
- Lock cleanup after function throws error.
Test Infrastructure
Recommended Setup
- Test runner: Vitest (preferred) or Node.js built-in test runner (
node --test).
- Mock Redis: In-memory Map-based mock for
server/db/redis.js.
- Mock PostgreSQL: In-memory array-based mock for
server/db/postgres.js.
- Mock Socket.IO: Custom mock tracking emitted events, connected clients, and rooms.
- Mock bcrypt: Deterministic mock for faster tests (skip real hashing).
Test File Structure
server/
__tests__/
unit/
gameEngine.test.js # Pure function unit tests
roomCode.test.js # Room code generation tests
integration/
nightResolve.test.js # Night resolution scenarios
voteResolve.test.js # Vote resolution scenarios
roomLifecycle.test.js # Room create/join/leave/close
auth.test.js # Register/login/guest auth
data/
redis.test.js # Redis data layer tests
postgres.test.js # PostgreSQL data layer tests
concurrency/
roomLock.test.js # withRoomLock serial execution
raceCondition.test.js # Concurrent action tests
mocks/
mockRedis.js # In-memory Redis mock
mockPostgres.js # In-memory PostgreSQL mock
mockSocket.js # Socket.IO mock with event tracking
Verification Commands
node --check server/index.js
node --check server/gameEngine.js
cd server && npm test
cd server && npx vitest run __tests__/unit/gameEngine.test.js
cd server && npx vitest run --coverage
References
Load references/be-test-patterns.md when writing or reviewing backend tests.