# Testing Ekklesia separates fast code-level evidence from live infrastructure evidence. ## Unit and repository verification ```bash pnpm test pnpm verify ``` These commands exercise package tests, repository boundaries, type checking, offline Core and module migration discovery, identity schema drift detection, formatting, linting, and builds. Migration-runner unit tests use a narrow database/session port to prove deterministic planning, approval, transaction orchestration, cleanup, and sanitized errors. They do not claim to prove PostgreSQL behavior. Identity unit tests cover branded opaque-ID contracts, principal shapes, configuration validation (secret, base URL, and trusted-origin rules), the Core identity runtime (explicit construction, exact schema mapping, only email/password and rate limiting enabled, sanitized failures, and that a database outage during principal resolution is never silently reported as anonymous), and Core migration discovery (deterministic ordering, the reserved `core` identity, and collision rejection) — all without a live database. `apps/api`'s own unit tests cover its pure Hono app factory (route shapes, CORS allow/deny/preflight, deterministic 404 and sanitized error responses), its runtime configuration, and that importing any of its modules — including the executable server module without calling its exported `main` — never reads the environment, connects to PostgreSQL, or starts a server. `pnpm verify` is intentionally Docker-free. It never plans or applies migrations against a live database. ## Live PostgreSQL migration evidence Docker and the Docker Compose plugin are required for the live suite: ```bash pnpm test:migrations:integration ``` The command first builds the workspace through the repository's Turbo build graph, then starts the repository's pinned PostgreSQL image in a unique Compose project, waits for bounded health readiness, runs the production PostgreSQL adapter and migration services, and removes the container, network, and volume. A build failure occurs before Docker provisioning. Each scenario also receives a safely named disposable database. Direct host runs use a Docker-assigned random port bound only to `127.0.0.1`; a containerized Gitea job joins the isolated Compose network and uses the PostgreSQL service name. The live suite proves fresh read-only planning, initial and incremental application, genuine no-op behavior, exact applied checksums and history, edited or corrupt history rejection, whole-batch DDL rollback, Drizzle breakpoint execution, real advisory-lock concurrency and timeouts, lock release after failure, exact destructive approvals, and public-error sanitization. It does not make migrations automatic during Core startup. A dedicated set of scenarios exercises the committed Core identity migration against real PostgreSQL: read-only planning on a fresh database, applying it and confirming exactly the four expected `ekklesia_core_*` tables and their indexes exist, a genuine no-op on a second apply, Core executing before a fixture module migration in one batch, previously applied module-only history staying byte-for-byte unchanged once Core is introduced, and a later module migration's failure rolling back cleanly without damaging the already-committed Core identity migration or leaking the advisory lock. A second dedicated suite (`apps/api/tests/api-http.integration.ts`, included in the same `pnpm test:migrations:integration` run) starts the real `apps/api` HTTP server against a freshly migrated disposable database and exercises it with real HTTP requests: public sign-up (with no automatic session, per `autoSignIn: false`), explicit sign-in producing a host-only `HttpOnly`/`SameSite=Lax`/secure session cookie, `/api/v1/principal` for both the authenticated and anonymous shapes, sign-out actually invalidating the session, a malformed or missing cookie resolving as anonymous, duplicate sign-up creating no second identity row, two distinct accounts producing identical principal shapes with no elevation and no owner/role/permission data at the database level, no plaintext password ever stored, CORS never returning an allow-origin header for an untrusted origin, Better Auth's handler being unreachable outside `/api/auth/*`, a database outage during principal resolution producing a sanitized `503` rather than a false anonymous `200`, starting the API against an unmigrated database creating no tables at all, and a clean listener/pool shutdown. See [ADR 0011](../architecture/decisions/0011-identity-http-transport-and-public-enrollment.md) for one CSRF/origin guarantee that was verified manually against the real server but could not be reproduced reliably inside this test runner, and is documented rather than silently asserted. CI runs the same command in the dedicated `migration-smoke` job. After this change is merged, add the following pull-request status context to `main` branch protection: ```text CI / migration-smoke (pull_request) ``` ## Safe troubleshooting Confirm the prerequisites without printing configuration: ```bash docker info docker compose version ``` Then rerun `pnpm test:migrations:integration`. The harness emits fixed failure messages and does not dump PostgreSQL logs, SQL, container inspection output, or the generated database URL. Do not enable query logging, print the test environment, run `docker compose config` without redaction, or paste Docker inspection output into an issue. If the Docker daemon itself stopped during cleanup, restore it before rerunning so the harness can create a fresh uniquely named project.