Testing strategy
Baseleg uses a layered testing approach designed for a Cloudflare runtime and a modular monolith.
Unit + application tests (now)
- Use Vitest for:
- Domain rule tests (invariants, policies).
- Application use case tests (happy paths + key failure paths).
- Domain tests should not depend on Astro or D1 bindings.
Runtime-aware tests (now, for packages/infrastructure/repositories)
D1-bound flows and Workers bindings are tested with @cloudflare/vitest-pool-workers, which
runs tests inside a real workerd runtime against a genuine (local, simulated) D1 database —
not a hand-mocked DB object. This catches row↔entity mapping bugs (wrong column name, wrong
nullability handling, a botched WHERE clause) that compile cleanly against a mock but fail
against the real schema.
Version note: @cloudflare/vitest-pool-workers@0.13.0+ requires vitest@^4.1.0. This repo
is on vitest@3.2.4, so it’s pinned to ^0.12.21 — the last minor compatible with
2.0.x - 3.2.x. Re-check compatibility before bumping either package.
Running locally:
pnpm -F @baseleg/infrastructure-repositories test
No wrangler login or Cloudflare account needed — the D1 binding is fully local/simulated.
How it’s wired (packages/infrastructure/repositories/):
vitest.config.ts—defineWorkersConfigwith aDBD1 binding, compatibility date/flags mirroringapps/web/wrangler.jsonc, and this repo’s realdb/migrations/*.sqlloaded viareadD1Migrationsand passed in as aTEST_MIGRATIONSbinding.src/test/applyMigrations.ts(asetupFilesentry) — callsapplyD1Migrations(env.DB, env.TEST_MIGRATIONS)before each test file runs, so tests run against the actual current schema, not a separately-maintained fixture that could drift from it. A broken migration file fails the whole test run loudly (aD1_ERRORfrom the SQL engine) — it does not silently skip or run against a partial schema.src/test/env.d.ts— types thecloudflare:testmodule’senv.DB/env.TEST_MIGRATIONS.
CI: no separate job — .github/workflows/ci.yml’s existing Test step (pnpm test,
which is turbo run test) already fans out to this package’s own test script, D1-bound
tests included, since the pool is scoped to this one package only.
Pattern for adding a new D1-bound test for another *RepositoryD1 class: name the file
<Name>.d1.test.ts (distinct from any mocked <Name>.test.ts in the same directory), import
env from cloudflare:test, build a real DB via createDb(env.DB) — no manual binding
setup needed, the harness above handles it. Seed any foreign-key parent rows directly via the
Drizzle schema tables first: D1 enforces foreign keys, unlike default SQLite.
Backfilling D1-bound tests for the other existing *RepositoryD1/*D1 classes
(AircraftRepositoryD1, BookingRepositoryD1, PersonRepositoryD1,
OperatingHoursRepositoryD1, AircraftDispatcherD1, UsageEventRecorderD1) is real,
separate follow-up work — only FlightRepositoryD1 has one today (a single smoke test
proving the harness itself, not full coverage).
End-to-end journeys (later)
Use Playwright for critical user journeys (e.g. booking creation, conflict detection, grounded aircraft protection).
Regression requirement
Every bug fix requires a regression test:
- Prefer adding a failing test first (where feasible).
- Fix the bug.
- Ensure the new test covers the regression scenario.