managarten/tests/integration
Till JS 09f81d77de test(integration): assert security_events audit rows after register + login
Adds two assertions to the auth-flow integration test that exercise the
audit-log path:

  - after register: expect exactly 1 REGISTER row in auth.security_events
    for the new user
  - after login: expect exactly 1 LOGIN_SUCCESS row for the same user

This locks in the fix from the previous commit (security.ts ?? null
guard for optional fields) and catches any future regression where
security.logEvent silently swallows a SQL error and the audit log goes
into the void.

Verified by reverting security.ts to the broken pre-fix version and
re-running — the test fails with `Expected: 1, Received: 0` at the
register-audit assert in 211ms instead of taking hours of production
debugging.

Also adds an explicit DELETE FROM auth.security_events to the afterAll
cleanup. The FK from security_events.user_id to auth.users(id) is
ON DELETE CASCADE so it would clean up implicitly anyway, but listing
it explicitly makes the cleanup intent obvious from the test source.

Net: 24 → 26 expects per run. Still ~22s end-to-end on a warm cache.
2026-04-08 18:05:57 +02:00
..
auth-flow.test.ts test(integration): assert security_events audit rows after register + login 2026-04-08 18:05:57 +02:00
package.json test(integration): end-to-end auth flow test with Mailpit + CI gating 2026-04-08 17:14:02 +02:00
README.md test(integration): end-to-end auth flow test with Mailpit + CI gating 2026-04-08 17:14:02 +02:00

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.