Skip to content

fix(server): Postgres integration tests + unique-violation detection bug#4

Merged
danjdewhurst merged 1 commit into
mainfrom
review-followups
Jun 13, 2026
Merged

fix(server): Postgres integration tests + unique-violation detection bug#4
danjdewhurst merged 1 commit into
mainfrom
review-followups

Conversation

@danjdewhurst

Copy link
Copy Markdown
Contributor

Summary

Follow-up #1 from the review: a real-Postgres integration tier for the Drizzle stores (review finding 7.5 — they were previously only exercised against fake chainable doubles). It immediately paid for itself by catching a production bug.

Bug found and fixed

isUniqueViolation only checked the top-level error's code === "23505". But Drizzle wraps the driver error in .cause, and Bun's SQL driver puts the SQLSTATE in errno (with code set to the generic ERR_POSTGRES_SERVER_ERROR). So a duplicate username threw a raw error that surfaced as 503 AUTH_UNAVAILABLE instead of a clean 409 USERNAME_TAKEN. The fix walks the cause chain and checks code, errno, and the message at each level. The fake-double unit tests couldn't catch this — only a real driver does.

Integration tests

apps/server/src/db/integration.test.ts exercises all three stores against real Postgres:

  • DrizzleAuthStore: column mapping, username_key unique constraint → UsernameTakenError, token-version revocation round-trip.
  • DrizzleFriendStore: canonical friendship storage, countFriends, bidirectional listFriends, and the no-self-friend CHECK constraint.
  • DrizzlePersistenceStore: room seeding, visibility/owner filtering, and last-room session save/get/clear.

Opt-in via RUN_DB_TESTS=1 so the default bun test stays database-free (skips with a placeholder when the var is unset).

CI

  • postgres:16 service + RUN_DB_TESTS=1 + a db:migrate step before tests (which also validates the migration set, including the new 0008).
  • New test:integration script for local runs (bun run db:up first).

Test plan

  • bun run lint ✅ · bun run typecheck
  • bun test (no DB) ✅ 250 pass, 1 skip · bun run coverage:check95.24%
  • RUN_DB_TESTS=1 bun run test:coverage (real PG) ✅ 254 pass · coverage:check95.68%

Not in this PR (the other two follow-ups)

HttpOnly cookie auth (1.8) and the Avatar.ts / createApp.ts refactors (8.1/8.6/5.3) both need a running-app/browser check — cross-origin cookie + CORS-credentials behavior, and pixel-level rendering output respectively (the two "hair switches" turned out to be different renderers sharing a style enum, not a mechanical dedup). I'd rather land those alongside a /verify pass than ship them blind.

🤖 Generated with Claude Code

Add a real-Postgres integration tier for the Drizzle stores and fix a bug it
immediately surfaced.

Bug: isUniqueViolation only checked the top-level error's `code === "23505"`,
but Drizzle wraps the driver error in `.cause` and Bun's SQL driver exposes the
SQLSTATE as `errno` (with `code` set to the generic ERR_POSTGRES_SERVER_ERROR).
So a duplicate username threw a raw error that surfaced as 503 AUTH_UNAVAILABLE
instead of a clean 409 USERNAME_TAKEN. Now walk the cause chain and check code,
errno, and the message at each level. The fake-double unit tests could not catch
this because they never hit a real driver.

Tests / CI:
- apps/server/src/db/integration.test.ts exercises DrizzleAuthStore,
  DrizzleFriendStore, and DrizzlePersistenceStore against real Postgres: column
  mappings, the username unique constraint, token-version revocation, canonical
  friendships listed both directions, the no-self-friend CHECK, and room/session
  persistence. Opt-in via RUN_DB_TESTS=1 so the default `bun test` stays
  database-free.
- ci.yml: add a postgres:16 service, RUN_DB_TESTS=1, and a db:migrate step
  before the test run (which also validates migrations, including 0008).
- Add a test:integration script.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@danjdewhurst danjdewhurst merged commit 135b00f into main Jun 13, 2026
2 checks passed
@danjdewhurst danjdewhurst deleted the review-followups branch June 13, 2026 22:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant