Testing
Test pyramid
Section titled “Test pyramid” ┌──────────────────────┐ │ E2E (Playwright) │ ~20 specs │ Full stack in Docker│ ├──────────────────────┤ │ Integration tests │ Real Postgres + Valkey │ (testcontainers) │ ├──────────────────────┤ │ Unit tests (Rust) │ No I/O, fast ├──────────────────────┤ │ Component tests │ React Testing Library + MSW └──────────────────────┘Running tests
Section titled “Running tests”# All Rust tests (spins real Postgres + Valkey via testcontainers)cargo test --workspace
# Single cratecargo test -p tundrad-api
# Single testcargo test -p tundrad-api auth::tests::login_rate_limit
# Panel unit + component testscd panel && pnpm test --run
# E2E tests (requires e2e stack running)cd docs/09-deployment-bundle/e2e && bash scripts/run.sh upcd panel && pnpm playwright test --project=chromiumIntegration test harness
Section titled “Integration test harness”tundra-test-harness::TestEnv provides:
- Real PostgreSQL 18 container (via testcontainers)
- Real Valkey container
- Migrations applied
seed_operator()factory for test data
#[tokio::test]async fn test_login() { let env = TestEnv::new().await; let op = env.seed_operator("test@example.com", "password123").await; // test against real DB...}Never mock the database. Integration tests must use TestEnv or a real DB. Mocked tests were the cause of a past production incident (mocked behavior diverged silently from actual DB constraints).
Authorization matrix
Section titled “Authorization matrix”Every new route needs a row in tests/authz_matrix.rs:
// Unauthenticated → 401// Wrong role → 403// Correct role → 2xxmatrix_test!( get_sites, method: GET, path: "/api/v1/sites", unauthed: 401, operator_role: 200, admin_role: 200, owner_role: 200,);Snapshot tests
Section titled “Snapshot tests”Generated configs (Nginx blocks, systemd units) use insta for snapshot testing:
insta::assert_snapshot!(generate_caddy_config(&site));Update snapshots with:
cargo insta reviewE2E conventions
Section titled “E2E conventions”- One spec file per feature area (
create-site.spec.ts,wordpress.spec.ts) loginAs()helper for authentication (usesowner@example.comseed credentials)POST /api/v1/test/resetto reset state between tests- Use
--profile multi-serverfor cross-server tests