Testing — Domain (L0)
Testing domain — L0
Purpose
Posthaste tests are executable behavior contracts. They prove that provider observations, local reconciliation, live query updates, API invalidation, and frontend state converge to the expected user-visible model. A test must assert expected behavior, not snapshot a current implementation quirk.
This domain exists because Posthaste’s hardest bugs — missed view updates, over-broad cache invalidation, sync-convergence races, provider projection mismatches — are difficult to reproduce by hand. A disciplined, layered harness with shared support code is the only economical way to catch them before release and to let coding agents verify fixes reliably. It supersedes the legacy testing-spec framing.
Coverage is by behavior boundary, not file count
Tests are organized by the boundary whose behavior they prove, not by source file. The boundaries, from the network edge inward:
- Provider observation — raw remote observations become the canonical local
model (Gmail labels as mailboxes, shared
Message-IDcopies, MODSEQ/VANISHED, JMAPcannotCalculateChanges). - Store reconciliation —
SyncBatchapplication is atomic, account-scoped, projection-safe, and event-complete. - Live query update — mutations and sync deltas bump the store generation and converge every affected query; no missed invalidation, no over-broad refetch.
- API boundary — error mapping, pagination, the SSE event stream, command responses.
- Frontend state — React Query invalidation, surface routing, overlay stacking.
- Live-provider parity — JMAP and IMAP projections agree on the same seeded real server fixture.
The detailed observation matrix and per-boundary rules live in L1.
Layering stakes
Each layer catches a different bug class and costs a different amount to run. Skipping a layer leaves a blind spot; running only the expensive layer is too slow to iterate on.
- Unit/integration contracts (Rust) prove domain, store, sync, API logic.
- Frontend logic contracts (Bun) prove cache invalidation, routing, adapters.
- Live-provider parity (Rust, gated) proves the app’s real sync path against a seeded real Stalwart.
- Agent-driven full-app scenarios (planned) prove multi-step, multi-message convergence through a headless driver and declarative fixtures.
The shared posthaste-testkit crate standardizes the harness, fixtures, and
assertions across the lower layers so each test catches wide and reports
clearly. Its contract is L1; declarative TOML fixtures have landed —
the remaining forward contract (the headless driver for agent-driven
scenarios) is in docs/eph/PLAN-L2-testkit-roadmap.md.
Assertions
| ID | Sev. | Assertion |
|---|---|---|
| behavior-not-current-code | MUST | Tests assert expected protocol/domain behavior, not implementation quirks |
| layered-coverage | MUST | Coverage spans provider, store, live-query, API, frontend, and parity boundaries rather than one layer |
| shared-testkit | MUST | Lower-layer tests share harness, fixtures, and assertions through posthaste-testkit instead of ad-hoc per-crate copies |
| red-first | SHOULD | New behavior coverage is developed red-first or justified as already covered |
| spec-linked-coverage | SHOULD | Tests for SPECial assertions link to the assertion with a spec: comment |