Infrastructure (T01): - tests/conftest.py: sync schema setup (psycopg2), per-test table truncation, async ASGI client with get_session override - pyproject.toml: [tool.pytest.ini_options] asyncio_mode=auto - Makefile: make test target with TEST_DATABASE_URL Core router tests (T02): 19 tests - domains, topics, workstreams, tasks, decisions + state summary - Caught real bug: topic router missing duplicate-slug 409 guard (fixed) TD/EP/Contributions/SBOM tests (T03): 10 tests - CRUD + status transitions + lifecycle guard + SBOM ingest MCP smoke tests (T04): 12 tests - get_state_summary, create_task, update_task_status, add_progress_event, flag_for_human HTTP shapes CI gate (T05): make test documented in CLAUDE.md session protocol Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3.3 KiB
id, type, title, domain, status, owner, topic_slug, created, updated, state_hub_workstream_id
| id | type | title | domain | status | owner | topic_slug | created | updated | state_hub_workstream_id |
|---|---|---|---|---|---|---|---|---|---|
| CUST-WP-0020 | workplan | State Hub — pytest Test Suite | custodian | done | custodian | custodian | 2026-03-18 | 2026-03-18 | 56379f10-bad9-49b4-bbd0-e4a0de5c6a81 |
State Hub — pytest Test Suite
Summary
The API, MCP server, seed script, and migration scripts currently have zero automated test coverage (TD-CUST-014). Regressions are caught at runtime. This workplan introduces a pytest-asyncio test suite using HTTPX's async test client against a real test database (no mocking — see ADR below).
ADR
Do NOT mock the database in these tests. The state-hub has had past incidents
where mocked tests passed but the real DB diverged (see memory: feedback on
not mocking the database). Use a real PostgreSQL test database via
TEST_DATABASE_URL env var (docker-compose already provides postgres).
Tasks
T01 — Test infrastructure: pytest-asyncio + async HTTP client + fixtures
id: CUST-WP-0020-T01
status: done
priority: high
state_hub_task_id: "35a52abb-15b1-4c12-b1c6-5e321377ddfa"
- Add
pytest,pytest-asyncio,httpxtostate-hub/pyproject.tomldev deps - Create
state-hub/tests/conftest.py:enginefixture: creates a fresh async engine againstTEST_DATABASE_URLdbfixture: runsalembic upgrade headthenalembic downgrade basearound each test session (or truncates tables between tests)clientfixture:httpx.AsyncClientpointed at the test app instance
- Create
state-hub/tests/__init__.py - Add
make testtarget instate-hub/Makefile
T02 — Core router tests: topics, workstreams, tasks, decisions
id: CUST-WP-0020-T02
status: done
priority: high
state_hub_task_id: "85b0b6e2-d66b-4619-8e1f-2056862a0d97"
Coverage targets (happy path + key error cases):
POST /topics/→ 201, duplicate slug → 409POST /workstreams/+GET /workstreams/?topic_id=+ status transitionsPOST /tasks/+GET /tasks/?workstream_id=+needs_humanflagPOST /decisions/+PATCH /{id}/resolve/GET /state/summary→ returns expected shape with counts
T03 — TD, EP, contributions router tests
id: CUST-WP-0020-T03
status: done
priority: medium
state_hub_task_id: "41106482-5b1c-4ee9-8979-377595f704b9"
- TD CRUD + workflow status transitions + notes endpoints
- EP CRUD + status transitions
- Contribution lifecycle guard (invalid transitions → 422)
- SBOM ingest +
/sbom/snapshots/aggregation
T04 — MCP server smoke tests
id: CUST-WP-0020-T04
status: done
priority: medium
state_hub_task_id: "6fa30dd1-065d-4158-8ef9-ed5ff7001083"
For each MCP tool, call the underlying HTTP helper against the test client
and assert the result shape. No need to test the MCP stdio protocol itself —
just the HTTP-level correctness. Focus on: get_state_summary,
create_task, update_task_status, add_progress_event, flag_for_human.
T05 — CI: add pytest to pre-commit or Makefile gate
id: CUST-WP-0020-T05
status: done
priority: low
state_hub_task_id: "5d206df8-c902-4c5b-8a0b-58b600480c0f"
Add make test as a step in any existing CI pipeline, or document the
manual pre-push gate in CLAUDE.md. Ensure TEST_DATABASE_URL is set to the
docker-compose postgres instance in the local dev workflow.