Skip to main content

Ghasi e-Prescribing Gateway Service — Testing Strategy

Status: populated Owner: TBD Last updated: 2026-04-18 Companion: Service Template · 03 platform-services · 02 DDD

Coverage Targets

LayerMinimum coverage
Unit (domain + use cases)90%
Integration85%
Contract (Pact + golden FHIR)All API endpoints and all events
E2EPrescribe → dispense → notify three-party correlation (AC-RX-006)

Unit Tests (test/unit/)

  • Persona enforcement logic — ehr-backend cannot write MD; pharmacy-backend cannot write MR
  • FHIR resource mapper — MR/MD domain entity ↔ FHIR R4
  • Idempotency key fingerprint computation
  • ETag computation (sha256 of canonical resource)
  • Pharmacy routing resolver — preferred, fallback, region logic
  • Outbox event construction per event type
  • Subscription criteria matching
  • FHIR validation Zod schemas (Phase 1)

Integration Tests (test/integration/)

Mandatory CI gates:

Test fileWhat it proves
tenant-isolation.integration.spec.tsTenant A cannot read or write Tenant B's MR/MD; zero cross-tenant leakage
outbox.integration.spec.tsDomain events written to outbox in same DB transaction; relay publishes to NATS
inbox.integration.spec.tsConsumed events deduplicated; no side effects on duplicate delivery

Additional:

Test fileScenario
create-medication-request.integration.spec.tsFull create → DB → outbox → event published; 201 + ETag
idempotency.integration.spec.tsSame Idempotency-Key + same payload → same resource (no duplicate); different payload → 409
etag-conflict.integration.spec.tsStale If-Match → 412; fresh ETag → 200
persona-enforcement.integration.spec.tsEHR writes MR = 201; EHR writes MD = 403; Pharmacy writes MR = 403; Pharmacy writes MD = 201
subscription-notification.integration.spec.tsSubscription fires after MR/MD creation within SLO
dlq-replay.integration.spec.tsFailed delivery → DLQ; replay delivers to endpoint
ig-validation.integration.spec.tsValid FHIR MR → 201; invalid (missing required field) → 422 OperationOutcome
licensing-gate.integration.spec.tsUnlicensed tenant → 403 MODULE_NOT_LICENSED

Contract Tests (test/contract/)

  • medication-request-api.pact.spec.ts — Pact contracts for orders-service (consumer) ↔ gateway
  • medication-dispense-api.pact.spec.ts — Pact contracts for pharmacy-service (consumer) ↔ gateway
  • medication-request.fhir.schema.spec.ts — Golden FHIR MR Bundle validated against R4 StructureDefinition
  • medication-dispense.fhir.schema.spec.ts — Golden FHIR MD Bundle validated against R4
  • eprescribing-events.schema.spec.ts — All produced events validated against schema registry

Golden fixtures stored in test/fixtures/fhir/:

  • medication-request-valid.json — valid AFG/UAE profile MR
  • medication-request-invalid-missing-requester.json — expected OperationOutcome
  • medication-dispense-valid.json
  • three-party-bundle.json — full prescribe → dispense → notify scenario

E2E Tests (test/e2e/)

ScenarioFileNotes
Three-party: prescribe → dispense → notify (AC-RX-001..006)three-party-correlation.e2e.spec.tsEHR creates MR; pharmacy gets notification; pharmacy creates MD; EHR gets notification; audit trail verified
Idempotent create on network retryidempotent-retry.e2e.spec.tsRetry with same Idempotency-Key returns same resource
ETag conflict recoveryetag-recovery.e2e.spec.tsClient recovers from 412 correctly
Subscription replay from DLQdlq-replay.e2e.spec.tsEndpoint down → DLQ → manual replay → delivery

Performance Tests

  • Load test: 100 concurrent MR creates; verify p95 < 800 ms (NFR-RX-001).
  • Subscription fan-out: 1 MR; 10 registered subscriptions; verify all notified within SLO (NFR-RX-002).

Test Infrastructure

  • Vitest for unit and integration
  • Testcontainers (Postgres, NATS)
  • Pact broker for consumer-driven contracts
  • WireMock / MSW for mocking fhir-gateway and provider-directory-service in integration tests
  • FHIR golden fixtures in test/fixtures/fhir/ — CI gate: all valid fixtures must pass R4 validator (NFR-RX-004)