Skip to main content

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

LayerTargetTool
Unit (domain + application)≥ 85%Vitest
Integration (adapters + DB)≥ 80%Vitest + testcontainers
Contract (API)100% of public and internal endpointsPact
Performancep95 latency ≤ SLO targetsk6

2. Test Layers

2.1 Unit Tests

Scope: Domain invariants, query handler business logic, cache key construction, import row parsing.

TestWhat it validates
Concept uniqueness invariantCreating a duplicate (system, code) for global scope throws ConceptDuplicateError
Concept deactivation is one-wayRe-activating a concept via the API throws InvalidOperationError
Tenant scope isolationSearchConceptsQuery with tenantId=A excludes concepts scoped to tenantId=B
Drug pair canonical orderingdrug1_code <= drug2_code invariant enforced before DB lookup
Import CSV parserValid and malformed rows parsed correctly; error rows do not abort batch
Cache key generationLookup cache key includes system, code, tenantId hash

2.2 Integration Tests

Scope: Repository adapters against real PostgreSQL (testcontainers), outbox relay, event publication.

TestWhat 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.tsCRUD operations against PostgreSQL; uniqueness constraint enforced
drug-interaction.repository.spec.tsPair query returns correct active interactions; inactive excluded
drug-class.repository.spec.tsClass lookup by RxNorm code; inactive excluded
drug-contraindication.repository.spec.tsContraindication query by RxNorm + ICD-10 list
value-set.repository.spec.tsExpansion returns active concepts within the value set
cache-adapter.spec.tsCache hit returns value without DB call; miss writes to cache
import.spec.tsBulk 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.

ConsumerContract scenarios
medication-serviceGET /v1/terminology/search?system=rxnorm, POST /v1/terminology/drugs/interactions, POST /v1/terminology/drugs/duplicate-therapy, POST /v1/terminology/drugs/contraindications
laboratory-serviceGET /v1/terminology/lookup?system=http://loinc.org&code=2345-7
orders-serviceGET /v1/terminology/search?system=snomed
patient-chart-serviceGET /v1/terminology/validate, GET /v1/terminology/lookup

2.4 Performance Tests (k6)

Run against a loaded dataset (≥ 300,000 concepts, per NFR-TERM-002).

ScenarioTarget
Full-text search under 100 concurrent usersp95 ≤ 200 ms
Concept lookup (cache warm) under 200 concurrent usersp95 ≤ 50 ms
Drug interaction check (50 code pairs)p95 ≤ 300 ms
ValueSet expand (500-concept set)p95 ≤ 500 ms
Sustained throughput over 10 minutesError rate ≤ 0.1%

3. Security-Specific Tests

TestValidates
Unauthenticated request → 401All /v1/terminology/* and /fhir/R4/* without JWT
Wrong tenant JWTTenant B token cannot retrieve Tenant A custom concepts
platform:admin creates global conceptSucceeds
tenant:admin attempts global concept creationReturns 403 FORBIDDEN
clinical role attempts concept mutationReturns 403 FORBIDDEN
IMPORT_ENABLED=false → 405Import endpoint disabled in production mode
Internal token header validatedRequest 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

StepTriggerCommand
Unit testsEvery PRpnpm test:unit
Integration testsEvery PRpnpm test:integration
Contract testsEvery PRpnpm test:contract
Performance testsMerge to main (scheduled)pnpm test:perf
Coverage reportEvery PRpnpm test:coverage (fails < 80%)