Files
GameList/TESTS.md
2026-02-05 17:55:12 +01:00

90 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Test Suite Plan — Pick'n'Play
Purpose: full coverage of backend + critical UI flows using a mock (in-memory) SQLite DB; validates roles, phases, and data rules described in SPEC.md/API.md.
## Stakeholders & Permissions by Phase
| Role | Suggest phase | Vote phase | Results phase | Anytime |
| --- | --- | --- | --- | --- |
| Unauthenticated visitor | No API access; only static assets | — | — | Health check only |
| Player (non-admin) | Create/see own suggestions (≤5), edit all fields, delete own; can advance to Vote; title locks after leaving phase | View all suggestions, vote 010, finalize/unfinalize, use joker once to add a game; cannot go backward | Read leaderboard only when resultsOpen=true; no writes | Login/logout, set display name, read /state and /me |
| Admin (isAdmin=true) | Same as player; may edit/delete any suggestion | All player actions; may grant jokers, link/unlink games, delete players | Open/close results; sees leaderboard like player | Toggle results, reset/factory-reset DB, fetch vote status, move self backward |
## Phase/Permission Chart (for tests)
```mermaid
stateDiagram-v2
[*] --> Suggest
Suggest --> Vote: player POST /api/me/phase/next
Vote --> Results: admin opens results OR player next when resultsOpen=true
Results --> Vote: admin closes results
Admin: Admin-only actions
Admin --> Vote: grant joker/link/unlink/delete players/reset/factory-reset
Admin --> Results: toggle results
```
## Test Strategy (mock DB)
- Use xUnit + WebApplicationFactory; replace DbContext with in-memory SQLite (shared connection) seeded per test.
- Mock IHttpClientFactory with TestHttpMessageHandler to control image URL reachability; avoid real network.
- Helper builders: `TestServerClient` to act as specific user (cookie auth), `SeedData` for players/suggestions/votes, `PhaseHelper` to set phases/resultsOpen directly in DB.
- Run migrations once per test suite against in-memory DB to mirror schema.
## Test Cases (ordered by feature)
### 1) Authentication & Identity
- Register success (player, admin key path) issues cookie, trims fields, stores normalized username, hashes password.
- Register rejects missing/long username, missing password, missing display name, duplicate username, bad admin key, >24 chars username, >16 display name.
- Login success updates LastLoginAt and sets DisplayName if null; rejects wrong password/username; enforces length limits.
- Logout clears cookie.
- EnsurePlayerExistsMiddleware: signed cookie for deleted player returns 401 and clears auth.
- Cookie contains admin claim; non-admin cookie cannot access admin routes (401/403 via filter).
### 2) State & Phase Alignment (/api/state, /api/me)
- /api/state returns player-specific phase, votesFinal, hasJoker, counts; unauthorized returns 401.
- GetPhase auto-upgrades legacy Reveal -> Vote and realigns when resultsOpen toggles (to Results and back to Vote clearing votesFinal).
- /me/phase/next: moves Suggest->Vote, Vote->Results only when resultsOpen true; clears votesFinal; rejects when results locked.
- /me/phase/prev: admin only; moves back one step, clears votesFinal, rejects for player.
- /me/name: trims/limits to 16, rejects blank; persists change.
### 3) Suggestions
- GET /mine returns only callers suggestions ordered by CreatedAt.
- POST /: success with valid data; enforces ≤5 per player; trims optional fields; requires display name; rejects bad image URL/ext, unreachable image (mocked), invalid game/youtube URLs, invalid player counts, missing name/too long.
- Joker path: when phase=Vote and HasJoker=true allows creation, consumes joker, resets VotesFinal for all players.
- Phase gating: non-admin cannot create/update/delete outside Suggest (except joker create); admin bypasses phase checks for update/delete.
- PUT /{id}: player can edit own in Suggest; name locked outside Suggest; admin can edit any time; validation mirrors create.
- DELETE /{id}: player deletes own in Suggest; admin any time; also breaks child links and deletes related votes.
- GET /all: accessible from Vote+, orders by CreatedAt, includes link metadata, enforces phase mismatch before Vote.
### 4) Votes
- GET /mine: only in Vote, returns player votes; unauthorized/phase mismatch handled.
- POST /: creates or updates vote; rejects score outside 010; rejects when VotesFinal=true; enforces display name requirement and phase gating.
- Linked votes: when suggestions are linked, a single post updates all linked IDs; invalid suggestionId returns 400; linking root detection works for nested links.
- Finalize: POST /finalize toggles VotesFinal flag; allowed only in Vote.
### 5) Results
- GET /api/results: requires auth, resultsOpen=true, phase=Results; returns ordered leaderboard with totals/count/avg, callers vote, link metadata, and handles empty vote lists (Average=0).
- Phase mismatch and locked results return 400; unauthorized 401.
### 6) Admin Operations
- POST /admin/results toggles resultsOpen and aligns all player phases (to Results or back to Vote clearing votesFinal); updates UpdatedAt.
- GET /admin/vote-status returns list ordered by display/username with suggestion counts, finalized flag, joker flag; ready/waiting derived correctly.
- POST /admin/joker grants joker only when target in Vote; resets VotesFinal for target.
- DELETE /admin/players/{id}: removes player, cascades suggestions, breaks links to their suggestions, deletes related votes, wrapped in transaction.
- POST /admin/link-suggestions: only in Vote; errors on same ids/already linked/not found; re-parents groups correctly; deletes votes for affected group and unfinalizes affected players.
- POST /admin/unlink-suggestions: only in Vote; clears parents for group, deletes votes in group, unfinalizes affected players; no-op safe when missing.
- POST /admin/reset: wipes suggestions/votes, resets phases to Suggest, clears votesFinal/hasJoker, closes results, updates timestamp.
- POST /admin/factory-reset: wipes all players/suggestions/votes/state; reseeds AppState with defaults; transactional.
### 7) Infrastructure/Helpers
- PasswordHasher: hash+verify roundtrip, rejects empty password, constant-time compare (FixedTimeEquals usage).
- EndpointHelpers.IsValidImageUrl/IsValidHttpUrl: accepts empty, http/https; rejects others/invalid ext.
- IsReachableImageAsync: with mocked Http responses covers head success, get fallback, redirect rejection, size guard, invalid host (private/loopback) detection.
- BuildLinkRoots/LinkedIdsFor/FindRootId: cover disjoint groups, chains, cycles guard (visited set), non-existent ids.
- UpdateIndexMetaBase (Program.cs): rewrites app-base meta when BasePath set; no change when matching/marker missing; safe exceptions swallowed.
- Global exception handler returns 500 with JSON body and logs error.
- /health returns {status:"ok"}.
## Execution Notes
- Use named test data builders for players/suggestions to keep cases small and isolated.
- Reset in-memory DB per test to avoid cross-contamination; assert timestamps using time providers or approximate windows.
- Cover success + failure for every endpoint status path to reach 100% line/branch coverage.