managarten/tests/integration/README.md
Till JS 5af4ddab3c test(integration): end-to-end auth flow test with Mailpit + CI gating
Adds a 13-step integration test that exercises register → email
verification → login → JWT validation → /me/data → encryption-vault
init/key → logout against a real stack of postgres + redis + mailpit +
mana-auth + mana-notify in docker compose.

Verified locally that this catches every regression we hit on
2026-04-08 in well under a second:

  - missing nanoid dependency → register endpoint 500
  - missing MANA_AUTH_KEK env passthrough → mana-auth never starts
  - missing encryption-vault SQL migrations → vault endpoints 500
  - wrong cookie name in /api/v1/auth/login → no accessToken in response
  - mana-notify SMTP misconfigured → mailpit poll times out

Files:

- docker-compose.test.yml — minimal isolated stack on alt ports
  (postgres 5443, redis 6390, mailpit 1026/8026, mana-auth 3091,
  mana-notify 3092). Runs alongside the dev stack without collision.
  Postgres healthcheck runs a real query rather than just pg_isready
  to avoid the race where pg_isready reports healthy while the docker
  init scripts are still running on a unix socket.

- tests/integration/auth-flow.test.ts — bun test that drives the full
  flow via fetch + mailpit's REST API. Cleans up its test user from
  postgres in afterAll. Self-contained, no extra deps.

- tests/integration/README.md — what's covered, why it exists, how
  to run locally + extend.

- scripts/run-integration-tests.sh — orchestrator. Brings up the
  stack, pushes the @mana/auth Drizzle schema, applies the
  encryption-vault SQL migrations (002, 003), restarts mana-auth so
  it sees the fresh tables, runs the test, tears down on exit.
  KEEP_STACK=1 to leave it up for manual mailpit inspection.

- docker-compose.dev.yml — also adds Mailpit as a regular dev service
  (ports 1025/8025) so local development can have a working email
  capture without spinning up the test stack.

- .github/workflows/ci.yml — new auth-integration job that runs on
  every PR. Calls run-integration-tests.sh; on failure dumps
  mana-auth + mana-notify logs and the mailpit message queue. Marked
  as a required check via the existing PR validation pipeline.

Reproduced 3 clean runs and 1 negative-control run (removed nanoid
from package.json → mana-auth container exits → script aborts with
non-zero) before committing. Full happy path runs in ~22s on a warm
Docker cache.
2026-04-08 17:14:02 +02:00

2.7 KiB

Integration tests

End-to-end tests that exercise real services against real Postgres + Redis + a fake SMTP server (Mailpit), via docker-compose.test.yml.

What's covered

File Flow under test
auth-flow.test.ts register → email verification (via Mailpit) → login → JWT validation → /me/data → encryption vault init/key → logout

Running locally

./scripts/run-integration-tests.sh

That script:

  1. Brings up docker-compose.test.yml (postgres, redis, mailpit, mana-auth, mana-notify) on isolated ports (5443, 6390, 8026, 3091, 3092)
  2. Waits for everything to be healthy
  3. Pushes the @mana/auth Drizzle schema into the test database
  4. Applies the encryption-vault SQL migrations (002_encryption_vaults.sql, 003_recovery_wrap.sql)
  5. Runs bun test auth-flow.test.ts from this directory
  6. Tears the stack down on exit (success or failure)

The whole thing runs in well under a minute on a warm Docker cache.

Mailpit web UI

While the stack is up you can also browse incoming mail manually at http://127.0.0.1:8026.

Why this exists

Bugs caught by this test the first time it ran:

  • services/mana-auth imported nanoid but didn't declare it in its package.jsonCannot find package 'nanoid' at startup, register endpoint 500'd. Local pnpm install resolved it transitively via postcss → nanoid@3.3.11, an isolated container build couldn't.
  • MANA_AUTH_KEK was never passed through to the mana-auth container in docker-compose.macmini.yml, so the prod service hard-failed at startup with MANA_AUTH_KEK env var is required in production.
  • The encryption-vault SQL migrations (002, 003) had never been applied to prod Postgres, so any vault endpoint 500'd with relation "auth.encryption_vaults" does not exist.
  • /api/v1/auth/login minted a JWT by reconstructing the session cookie under the wrong name (mana.session_token instead of __Secure-mana.session_token), so the JWT-mint silently fell through and clients got accessToken: undefined.
  • mana-notify SMTP credentials were misconfigured against Stalwart, so no verification email actually went out — the failure was buried in mana-notify worker logs and the auth flow appeared to "work" only because the user could be flipped to verified by other means.

Each of those would have been a single red bun test run instead of a multi-hour debugging session.

Adding more flows

Drop another <name>.test.ts next to auth-flow.test.ts and update package.json to include it. Use the same helpers (postJson, waitForMail, pgExec) — they're free to copy.

CI

The same script runs in .github/workflows/ci.yml as a required PR check. Don't bypass it.