Backend Verification
Canonical gates
cd kxinspect_backend_python
uv sync --locked
uv run ruff format --check .
uv run ruff check .
uv run mypy app # strict: disallow_any_generics, no_implicit_reexport
uv run pytest -q --cov=app --cov-fail-under=85
uv run python scripts/export_openapi.py --check docs/openapi-v1.json
uv run python scripts/verify_fixture_manifest.py
CI must run export_openapi --check before any deploy — a mismatch means contract drift and must be negotiated at G-01.
Test harness guarantees
- No test writes to the real
runtime/— each constructsbuild_app(settings=Settings(runtime_root=tmp), clock=ManualClock(...), ...)withtmp_pathandTemporaryDirectory. - No network —
httpxTestClientwraps the in-process FastAPI app. - No wall clock —
ManualClock.instant = parse_instant("2026-08-01T12:00:00Z"),advance(timedelta(...))explicitly. - Deterministic ids —
SequentialIdGeneratorreplacesUuidGeneratorin unit/api tests.
Suites
| Path | What it covers |
|---|---|
tests/unit/test_domain.py | exhaustive (status,event) transition matrix, actor_for, open/history tabs, deadlineAt arithmetic, inclusive boundary, grapheme min/max, contest payload |
tests/unit/test_store.py | JsonStore commit, atomic replace, epoch/version checks, ring trim, idempotency ledger replay, OperationBarrier shared/exclusive, flock, marker guard, GC of uploads |
tests/api/test_reads.py | GET /health, /bookings, /hub, /inspections, /charges, /notifications, /sync-snapshot + deadline reconciliation on read |
tests/api/test_commands.py | accept/contest/pay/task/notification read, Idempotency-Key required, RFC 8785 digest, expectedVersion/Epoch checks, race with deadline, error shapes |
tests/api/test_attachments_events_dev.py | attachment staging/finalize/rollback, limits, multipart parse, SSE frames/heartbeat/replay with Last-Event-ID, dev reset/marker/chaos |
tests/contract/test_contract_v1.py | golden docs/contracts/examples/*.json matches live responses byte-for-byte; OpenAPI snapshot invariants |
Scripts
| Script | Gate |
|---|---|
scripts/export_fixtures.py --output <dir> | validates seed, writes byte-stable bundle (app_state.json + manifest.json + media) with sorted keys |
scripts/verify_fixture_manifest.py [--bundle <dir>] | checks manifest SHA-256 vs actual bytes; default checks committed assets/fixtures/ |
scripts/export_openapi.py --check <path> | diffs generated OpenAPI vs docs/openapi-v1.json |
scripts/reset_runtime.py --runtime-root <path> | destructive reset; refuses unless .kxinspect-runtime marker UUID matches |
Coverage & typing
tool.coveragebranch=true, source=[app], omit=[app/__main__.py];fail_under=85.tool.mypystrict=true, warn_unused_configs, disallow_any_generics;sse_starletteignored via override;RuntimeLockedErrorpaths marked# pragma: no coveronly where filesystem guard cannot be exercised without a second process.
Manual spot-checks (for reviewers)
# deadline inclusive at boundary
KX_DEMO_NOW=2026-08-01T12:00:00Z uv run uvicorn app.main:create_app --factory &
sleep 2; curl -s http://127.0.0.1:8000/api/v1/charges/CHG-001 | python -m json.tool | grep deadlineAt
# expect CHG-001 that is 1s before deadline stays outstanding, at deadline becomes accepted
# idempotency replay
EPOCH=$(curl -s http://127.0.0.1:8000/api/v1/charges/CHG-002 | python -c 'import json,sys;print(json.load(sys.stdin)["meta"]["stateEpoch"])')
KEY=$(uuidgen)
for i in 1 2; do curl -s -X POST http://127.0.0.1:8000/api/v1/charges/CHG-002/accept -H 'Content-Type: application/json' -H "Idempotency-Key: $KEY" -d "{\"expectedStateEpoch\":\"$EPOCH\",\"expectedVersion\":1}" | python -m json.tool | grep storeRevision; done
# second storeRevision must not increment