Skip to main content

Testing Standard (Summary)

In one line: No mocking by default, deterministic tests only, actual output as evidence, fast by default — the enforcement behind "evidence over claims" (Section 2.3).

The testing standard governs how tests are written, what counts as valid, and what thresholds apply.

Key Principles:

  1. No mocking by default. Mocking and in-memory databases are forbidden unless explicitly approved with a documented MOCK APPROVED comment that states the reason, the approver, the date, and the alternative for running against real services. Use testcontainers for PostgreSQL, MinIO, Redis, and Temporal.

  2. Deterministic tests only. No time.sleep() in tests. For time-dependent behavior, use the project's standard time-control library (canonical default per Section 4.5: freezegun; Temporal-workflow tests use WorkflowEnvironment.start_time_skipping() instead). Tests must produce the same result regardless of execution timing.

  3. Evidence requirements. Every implementation response must include actual test output — pytest results and coverage report with timestamps. The statement "tests should pass" is not evidence.

  4. Fast by default. Tests that require more than 30 seconds are marked with @pytest.mark.slow and excluded from the default test run. Long-running integration tests are opt-in, not mandatory for every commit.

  5. Mutation discipline. Coverage says a line executed; only a mutation says a test would have noticed it change. A guard ships with at least one recorded mutation of what the guard readsdata files (reference JSON, fixtures, config) are first-class mutation targets alongside code, because a guard whose subject is a file's contents cannot be proven by editing Python. Three rules make a mutation run trustworthy: a mutation that did not apply reports INCONCLUSIVE, never PASS (it is asserted that the baseline collected tests, the baseline was green, the anchor occurs exactly once, the bytes changed, and the mutated run still collects tests); a SURVIVED mutation means suspect the test first; and INCONCLUSIVE is a distinct outcome from SURVIVED because they have different owners — INCONCLUSIVE is a defective test, SURVIVED is a missing assertion. This is the run-time complement to §7's field (d): field (d) asks whether the gate was ever seen to fail, mutation discipline asks whether the test would notice if it did. Full specification and the shipped probe (templates/scripts/mutation-probe.sh) in appendix-a-testing.md §14.

PoC vs Production Mode:

DimensionPoC ModeProduction Mode
Test timingCode first, tests afterTDD encouraged
Edge case testsSkip initiallyRequired
E2E testsSkipRequired for user-facing apps
Security testsDeferredRequired for auth/data endpoints
Failure branch coverageNot measured85% minimum

Coverage Targets:

LayerPoCProduction
Workflow state machine + activities90%90%
FastAPI endpoints70%90%
React components70%90%
Integration layers (document parsing, object storage)70%90%
Line coverage (overall)70%90%
Failure branch coverage--85%
Real integration ratio--80%

Evidence: Backed by the testcontainers requirement and the MOCK APPROVED annotation pattern. A mock without the structured approval comment is flagged at review by the "Test evidence" checklist block carried in all four reviewer definitions (templates/agents/*.md) — that block is the artifact this sentence names; before 2026-08-01 the sentence claimed a review check that appeared in none of them. The pre-push gate additionally requires fresh passing output. Reproducible: add a bare mock and dispatch a reviewer agent over the diff. This is review, not a gate — §14 lists the testing standard as Core-asserted precisely because no script greps for a bare mock or for ORM create_all; the checklist raises the odds a human sees it, and does not make it detected. See appendix-a-testing.md.

Full specification: appendix-a-testing.md