apps/api test suite
End-to-end tests for the Hono Worker that lives at apps/api/. The Worker is
the migration target replacing the legacy Next.js routes under cloud/app/api/
(MIGRATION_NOTES.md tracks the conversion progress).
Layout
apps/api/test/
├── COVERAGE.md ← auto-generated audit. Re-run `bun run test:audit`.
├── FANOUT.md ← parallel-agent assignments for filling gaps.
├── README.md ← this file.
├── _audit-coverage.mjs ← regenerates COVERAGE.md.
├── _router-contract.mjs ← verifies generated router inventory + mount semantics.
├── auth/ ← runtime route, NOT a test (Playwright session helper).
└── e2e/
├── _helpers/api.ts ← fetch wrapper bound to TEST_API_BASE_URL.
├── preload.ts ← seeds DB + exports TEST_API_KEY before tests run.
└── agent-token-flow.test.ts ← foundation test: "agent gets token, uses API."
Running locally
The test runner expects the Worker to already be listening. Start it in one terminal:
# from cloud/
cd apps/api
PLAYWRIGHT_TEST_AUTH=true bun run dev # wrangler dev → http://localhost:8787
Then run the foundation test in another terminal:
# from cloud/apps/api/
bun run test:e2e:foundation
To target a different host (staging, a deployed preview):
TEST_API_BASE_URL=https://api-staging.elizacloud.ai bun run test:e2e:foundation
If the Worker isn't reachable, every test inside the suite skips with a warning — by design. The preload bootstraps the DB independently, so a stale Worker doesn't poison subsequent runs.
Required env
| Var | Why | Default |
|---|---|---|
TEST_API_BASE_URL |
Where to send requests. | http://localhost:8787 |
TEST_DATABASE_URL or DATABASE_URL |
The local Postgres the preload seeds + the Worker reads. | local Docker fallback (db:local:start) |
PLAYWRIGHT_TEST_AUTH (on the Worker) |
Enables /api/test/auth/session. |
unset; foundation test will fail without it |
PLAYWRIGHT_TEST_AUTH_SECRET (on both) |
HS256 secret for the test session JWT. | playwright-local-auth-secret |
CRON_SECRET |
For cron-route tests. | test-cron-secret |
Launch QA fixtures
Fixture-dependent launch QA uses the non-secret contract in
../../../docs/launchdocs/integration-test-fixtures.md.
The e2e harness can exercise cloud routes, connector contracts, and
direct-crypto boundaries, but live Codex/Gmail accounts, Eliza Cloud orgs, and
funded wallets must be provided through the approved secret store and evidenced
on the issue or PR.
Why a separate preload from packages/tests/e2e/preload.ts?
The packaged preload (cloud/packages/tests/e2e/preload.ts) imports
setup-server.ts, which spawns Next.js on port 3000. That's the wrong server
for the Worker suite — we want wrangler dev on 8787 instead. This local
preload only does the DB bootstrap and trusts the operator to run the Worker.
Coverage audit
COVERAGE.md is the source of truth for "which mounted Hono route has which
test." It's regenerated by _audit-coverage.mjs:
bun run test:audit
The script walks _router.generated.ts for mounted routes and grep-matches
quoted "/api/..." strings in any *.test.ts file under
cloud/packages/tests/e2e/ plus apps/api/test/e2e/. It's a coarse heuristic
— a route is "covered" if its path appears in any test, even one that only
asserts 401. That's still a useful signal: an uncovered route has zero tests.
Router contract
_router-contract.mjs checks that _router.generated.ts is in sync with the
current route tree and that generated mount order preserves Next.js-style route
precedence for static, dynamic, catch-all, method, and trailing-slash behavior:
node test/_router-contract.mjs
Fanout plan
FANOUT.md slices the 145 uncovered routes into eight independent groups,
each scoped for a parallel coding agent. The foundation test in this commit
exists so each parallel agent has a working harness to copy.