feat: [T05] implement score tracking API#13
Merged
agnt-platform[bot] merged 2 commits intoMay 15, 2026
Conversation
Adds the PostgreSQL schema for the snake leaderboard: - users: one row per player handle, with bearer token for X-Player-Token auth - sessions: one row per game played (start/end + final score + meta JSONB) - scores: append-only score feed indexed for top-N reads Indexes: - users_player_lower_uniq case-insensitive unique handle - users_api_token_uniq token lookup for write auth - sessions_user_started_idx recent sessions per user - scores_score_created_idx global top-N (ties resolved by earliest submit) - scores_user_score_idx best-score-per-user Migrations live as numbered .sql files under backend/migrations/ and are idempotent. backend/src/db.ts exposes a lazy pool plus a runMigrations() helper for tests/bootstrap so typecheck does not require a running DB. Adds pg + @types/pg to backend.
REST endpoints under /api: POST /api/users/register create user, returns X-Player-Token POST /api/scores submit a score (auth required) GET /api/leaderboard?limit=N top-N (limit clamped 1..100, default 10) GET /api/users/:id/best all-time best for one user Layered as: routes/leaderboard.ts -> repo.ts -> db.ts. SQL lives only in repo.ts; routes do validation + auth. Validation uses zod (handle regex, score >= 0 and bounded, limit clamp). Auth model: a single bearer token per user, sent as X-Player-Token. That's sufficient for a hobby leaderboard and avoids dragging in JWT/bcrypt for T05's scope. Tests: 9 integration tests under src/__tests__/leaderboard.test.ts using pg-mem to provide an in-memory PostgreSQL adapter, so CI does not need a real database. Migrations are applied through the same runMigrations() helper used in production. Test runner: node --test (--import tsx). Shared types extended additively: - SubmitScoreRequest gains optional `meta` - ScoreEntry gains optional `rank` - new SubmitScoreResponse / LeaderboardResponse / RegisterUserResponse Adds zod (runtime), pg-mem + supertest (tests).
This was referenced May 14, 2026
Contributor
|
✅ Revalidation passed. An admin will merge. |
Contributor
|
💸 Reward sent: 1.999 TON
|
Contributor
|
💸 Reward sent: 180 SNAKE
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements T05 — Implement Score Tracking API.
Endpoints
All mounted under
/api:POST/api/users/registerPOST/api/scoresX-Player-TokenGET/api/leaderboard?limit=NGET/api/users/:id/bestDesign
routes/leaderboard.ts->repo.ts->db.ts. SQL only lives inrepo.ts; routes own validation + auth.zod. Handle regex^[A-Za-z0-9._-]+$(1-32 chars),score >= 0and<= 1_000_000, leaderboard limit clamped 1..100.X-Player-Token.registermints it;POST /scoresrequires it. Full JWT/refresh-token auth is out of scope for T05.sessionsrow, so the audit trail stays 1:1 even though a/sessionsendpoint isn't part of T05.Tests
backend/src/__tests__/leaderboard.test.ts— 9 integration tests usingpg-memso CI runs without a real Postgres. Covers happy path, auth failure modes, validation, idempotent register, limit clamping, and 404s. Migrations are applied via the samerunMigrations()helper used in production.Shared types (additive)
SubmitScoreRequest-> optionalmetaScoreEntry-> optionalrankSubmitScoreResponse,LeaderboardResponse,RegisterUserResponseNo existing field types changed.
Verification
npm run typecheck— passesnpm run build— passesnpm test --workspace @snake/backend— 9/9 pass