Terminology Service — Testing Strategy
Status: populated Owner: TBD Last updated: 2026-04-18 Companion: Service Template · docs/standards/TESTING_STANDARDS.md · 02 DDD
1. Coverage Targets
| Layer | Target | Tool |
|---|---|---|
| Unit (domain + application) | ≥ 85% | Vitest |
| Integration (adapters + DB) | ≥ 80% | Vitest + testcontainers |
| Contract (API) | 100% of public and internal endpoints | Pact |
| Performance | p95 latency ≤ SLO targets | k6 |
2. Test Layers
2.1 Unit Tests
Scope: Domain invariants, query handler business logic, cache key construction, import row parsing.
| Test | What it validates |
|---|---|
| Concept uniqueness invariant | Creating a duplicate (system, code) for global scope throws ConceptDuplicateError |
| Concept deactivation is one-way | Re-activating a concept via the API throws InvalidOperationError |
| Tenant scope isolation | SearchConceptsQuery with tenantId=A excludes concepts scoped to tenantId=B |
| Drug pair canonical ordering | drug1_code <= drug2_code invariant enforced before DB lookup |
| Import CSV parser | Valid and malformed rows parsed correctly; error rows do not abort batch |
| Cache key generation | Lookup cache key includes system, code, tenantId hash |
2.2 Integration Tests
Scope: Repository adapters against real PostgreSQL (testcontainers), outbox relay, event publication.
| Test | What it validates |
|---|---|
tenant-isolation.spec.ts (mandatory) | Tenant A query cannot retrieve Tenant B custom concepts via any code path |
outbox.spec.ts (mandatory) | TerminologyConcept.Created event published to NATS after concept creation |
concept.repository.spec.ts | CRUD operations against PostgreSQL; uniqueness constraint enforced |
drug-interaction.repository.spec.ts | Pair query returns correct active interactions; inactive excluded |
drug-class.repository.spec.ts | Class lookup by RxNorm code; inactive excluded |
drug-contraindication.repository.spec.ts | Contraindication query by RxNorm + ICD-10 list |
value-set.repository.spec.ts | Expansion returns active concepts within the value set |
cache-adapter.spec.ts | Cache hit returns value without DB call; miss writes to cache |
import.spec.ts | Bulk import upserts correctly; errors logged per row; batch not aborted on row error |
2.3 Contract Tests (Pact)
The terminology-service is a provider. Consumer services (medication-service, laboratory-service, orders-service, patient-chart-service) generate Pact consumer contracts.
| Consumer | Contract scenarios |
|---|---|
medication-service | GET /v1/terminology/search?system=rxnorm, POST /v1/terminology/drugs/interactions, POST /v1/terminology/drugs/duplicate-therapy, POST /v1/terminology/drugs/contraindications |
laboratory-service | GET /v1/terminology/lookup?system=http://loinc.org&code=2345-7 |
orders-service | GET /v1/terminology/search?system=snomed |
patient-chart-service | GET /v1/terminology/validate, GET /v1/terminology/lookup |
2.4 Performance Tests (k6)
Run against a loaded dataset (≥ 300,000 concepts, per NFR-TERM-002).
| Scenario | Target |
|---|---|
| Full-text search under 100 concurrent users | p95 ≤ 200 ms |
| Concept lookup (cache warm) under 200 concurrent users | p95 ≤ 50 ms |
| Drug interaction check (50 code pairs) | p95 ≤ 300 ms |
| ValueSet expand (500-concept set) | p95 ≤ 500 ms |
| Sustained throughput over 10 minutes | Error rate ≤ 0.1% |
3. Security-Specific Tests
| Test | Validates |
|---|---|
| Unauthenticated request → 401 | All /v1/terminology/* and /fhir/R4/* without JWT |
| Wrong tenant JWT | Tenant B token cannot retrieve Tenant A custom concepts |
platform:admin creates global concept | Succeeds |
tenant:admin attempts global concept creation | Returns 403 FORBIDDEN |
clinical role attempts concept mutation | Returns 403 FORBIDDEN |
IMPORT_ENABLED=false → 405 | Import endpoint disabled in production mode |
| Internal token header validated | Request with wrong token returns 401 when INTERNAL_TOKEN is set |
4. Test Data
Seed data: test/fixtures/terminology-seed.csv (~5,000 synthetic concepts across LOINC, SNOMED, RxNorm, ICD-10 + custom tenant concepts).
Drug interaction test data: test/fixtures/drug-interactions-seed.csv (20 pairs covering CONTRAINDICATED, HIGH, MODERATE, LOW severity).
Seed script: pnpm db:seed — loads all fixtures before integration tests.
5. CI Integration
| Step | Trigger | Command |
|---|---|---|
| Unit tests | Every PR | pnpm test:unit |
| Integration tests | Every PR | pnpm test:integration |
| Contract tests | Every PR | pnpm test:contract |
| Performance tests | Merge to main (scheduled) | pnpm test:perf |
| Coverage report | Every PR | pnpm test:coverage (fails < 80%) |