QA plan: FastAPI skeleton and /healthz (#12)
Story: FastAPI skeleton, /healthz, provider abstraction (#12)
Capability: Retrieval & generation API (#11)
Spec: specs/retrieval.md
Test scope
Section titled “Test scope”| Type | Coverage |
|---|---|
| Unit | GET /healthz returns {"status": "ok", "db": true} when the DB repository returns healthy |
| Unit | GET /healthz returns {"status": "ok", "db": false} when the DB is unreachable (repository stub raises) |
| Unit | Provider protocol: EmbeddingProvider and LLMProvider protocol stubs satisfy the interface |
| Unit | Providers are instantiated lazily; app starts and serves /healthz with no Bedrock credentials configured |
| Integration | FastAPI app via TestClient: /healthz returns 200 against Docker Postgres |
| Contract | OpenAPI spec: /healthz response schema in committed openapi.yaml matches the FastAPI annotation (RAG005 CI diff check) |
| Security | /healthz is excluded from the Lambda authorizer route binding (RAG009); confirmed by route configuration test |
Entry criteria
Section titled “Entry criteria”- Story #10 (schema) merged
FakeEmbeddingProvider,FakeLLMProviderintests/stubs.py
Exit criteria
Section titled “Exit criteria”GET /healthzunit tests pass for healthy and DB-down casesTestClientintegration test: 200 + correct JSON body against live Docker Postgres- Provider protocol tests pass
- Lazy instantiation test: app imports without Bedrock credentials;
/healthzreturns 200 - OpenAPI diff CI job passes (no committed spec drift)
- Coverage:
api/app.py,api/routes/health.py,providers/base.pyeach exceed 70%
Environments
Section titled “Environments”Unit tests: in-process with TestClient and stubs. Integration: Docker Postgres. No AWS access.
Tooling
Section titled “Tooling”pytest, FastAPI TestClient, pytest-cov. Newman runs the openapi-check job (RAG005).
Fixtures and data
Section titled “Fixtures and data”apppytest fixture: FastAPI app instance with stubs wired via dependency injectionpostgres_dbfixture from Story #10
How tests are written
Section titled “How tests are written”def test_healthz_db_down(app_with_failing_db): # Given the DB repository is configured to raise on ping client = TestClient(app_with_failing_db) # When GET /healthz is called response = client.get("/healthz") # Then status is 200 and db field is false assert response.status_code == 200 assert response.json() == {"status": "ok", "db": False}- Lazy instantiation test may pass even if providers are module-level if imports are cached. Use a subprocess or
importlib.reloadto isolate the import-time check. - OpenAPI spec drift: developers forget to regenerate
openapi.yamlafter annotation changes. RAG005 CI job catches this but the friction is the developer remembering to run the regen script locally first.
Capability acceptance criteria link
Section titled “Capability acceptance criteria link”Spec: GET /healthz returns 200 with {"status": "ok", "db": true} when Postgres is reachable. Directly tested by the integration exit criterion above.
© 2026 Benjamin Arunski