Audit Service — Testing Strategy
Status: populated Owner: TBD Last updated: 2026-04-18 Companion: Service Template · TESTING_STANDARDS
1. Coverage targets
| Layer | Target | Tool |
|---|---|---|
| Domain (AuditEntry, AuditExport, ChainService) | ≥ 90 % | Vitest |
| Application (use cases, ingestion) | ≥ 85 % | Vitest |
| Infrastructure (adapters, NATS consumer) | ≥ 70 % | Vitest + TestContainers |
| Integration | ≥ 70 % | Vitest + TestContainers |
| Contract | 100 % of consumed event schemas | Schema registry conformance |
2. Test types and mandatory tests
2.1 Unit tests (test/unit/)
| Module | Scenarios |
|---|---|
ChainService | Correct SHA-256 computation; chain links prev_id correctly; deterministic output |
AuditEntry (domain) | Immutability: no methods mutate state; all fields set at creation only |
IngestionService | Event normalisation for each AuditEventType; malformed event → DLQ path |
DedupService | Duplicate source_event_id returns true; new ID returns false |
AuditExport (domain) | State machine: queued → processing → completed / failed; fileUrl required before completed |
AuditQueryFilters | Value object: immutable; range > 90 days rejected |
2.2 Integration tests (test/integration/) — MANDATORY
| Test file | What it verifies | Block on failure |
|---|---|---|
tenant-isolation.spec.ts | Tenant Admin sees only own tenant's audit_entries; cross-tenant query returns 403 | Hard block |
outbox.spec.ts | N/A — audit service has no outbox. audit.export.completed.v1 published directly by export worker; verify NATS message emitted. | CI |
inbox.spec.ts | N/A — audit service does not consume commands. Wildcard NATS consumer verified by ingestion.spec.ts | CI |
ingestion.spec.ts | Full path: NATS message received → dedup check → normalise → chain-hash compute → INSERT → ACK | Hard block |
dedup.spec.ts | Duplicate source_event_id → silently skipped; ACK returned; row count unchanged | Hard block |
chain-integrity.spec.ts | Chain-hash computed correctly; tampered chain_hash detected by verification job | Hard block |
db-role-permissions.spec.ts | audit_app role: INSERT succeeds; UPDATE fails with permission error; DELETE fails | Hard block |
query-api.spec.ts | Filter combinations; date range > 90 days → 400; pagination correct | |
export.spec.ts | Export job: queued → processing → completed; file written to object storage; signed URL returned | |
disclosure.spec.ts | GET /api/v1/audit/disclosures returns only entries for the specified patient |
2.3 Contract / schema conformance tests (test/contract/)
The audit service consumes events from all platform services. For each event type, a schema conformance test verifies the ingested payload conforms to the registered schema version.
| Event | Conformance test |
|---|---|
identity.user.suspended.v1 | user-suspended.schema.spec.ts |
tenant.tenant.activated.v1 | tenant-activated.schema.spec.ts |
patient_chart.note.signed.v1 | note-signed.schema.spec.ts |
patient_chart.breakglass.invoked.v1 | breakglass-invoked.schema.spec.ts |
ai_gateway.decision.accepted.v1 | ai-decision-accepted.schema.spec.ts |
| … (all registered event types) | *.schema.spec.ts |
Malformed events (not conforming to schema) must route to DLQ.
2.4 E2E tests (test/e2e/)
| Flow | Test |
|---|---|
| Event emitted → ingested → queryable | ingestion-to-query.e2e.spec.ts |
| Export requested → file available → signed URL downloadable | export-flow.e2e.spec.ts |
| Cross-tenant isolation under real JWT | tenant-isolation.e2e.spec.ts |
| Meta-audit: export request creates AuditEntry | meta-audit.e2e.spec.ts |
3. Test data strategy
- All test data uses synthetic events with stable
source_event_idvalues. - TestContainers provides an isolated Postgres 16 instance per integration test run.
- NATS: TestContainers NATS image for integration tests; mock for unit tests.
- Object storage: MinIO TestContainers for export tests.
4. CI pipeline
┌─────────────────────────────────────────────────────┐
│ 1. lint + typecheck │
│ 2. unit tests (vitest) │
│ 3. integration tests (TestContainers) │
│ ├── tenant-isolation.spec.ts (MANDATORY) │
│ ├── ingestion.spec.ts (MANDATORY) │
│ ├── dedup.spec.ts (MANDATORY) │
│ ├── chain-integrity.spec.ts (MANDATORY) │
│ └── db-role-permissions.spec.ts (MANDATORY) │
│ 4. schema conformance tests (schema registry) │
│ 5. coverage gate (≥ 80 % overall) │
│ 6. e2e tests (staging deploy) │
└─────────────────────────────────────────────────────┘
5. Quality gates
| Gate | Threshold | Enforced by |
|---|---|---|
| Overall coverage | ≥ 80 % | Vitest coverage in CI |
| Domain coverage | ≥ 90 % | Path-filtered coverage |
tenant-isolation | Must pass | CI hard block |
chain-integrity | Must pass | CI hard block |
db-role-permissions | Must pass | CI hard block |
| Schema conformance | Must pass | Schema registry CI step |
6. Security-specific tests
| Test | Description |
|---|---|
audit_entries UPDATE via audit_app role | Expect permission denied error from Postgres |
audit_entries DELETE via audit_app role | Expect permission denied error from Postgres |
| Chain-hash recompute after field mutation | Expect verification job to report mismatch |
| Query with foreign tenant JWT | Expect empty result (RLS filters to zero rows) |