cbc-bridge-service — Testing Strategy
Version: 1.0 Status: Draft Owner: Government / Emergency + QA Last Updated: 2026-04-21 References: SERVICE_OVERVIEW.md, SECURITY_MODEL.md, FAILURE_MODES.md, docs/standards/DEFINITION_OF_DONE.md
Testing strategy for cbc-bridge-service. Given this service is a civil-emergency tier with direct MNO RAN impact, the testing bar is elevated: zero tolerance for silent regressions in the PKI verification path, the CBS encoder, or the audit hash-chain.
1. Test Pyramid
┌─────────────────┐
│ Drill + GameDay │ ← real-MNO staging, quarterly
├─────────────────┤
│ E2E tests │ ← full flow through mock MNO CBE
├─────────────────┤
│ Contract tests │ ← with regulator-portal + gov-client clients
├─────────────────┤
│ Integration │ ← Postgres, NATS, HSM (softhsm2), CBE adapters
├─────────────────┤
│ Unit tests │ ← dominant — domain logic, no I/O
└─────────────────┘
Distribution: ~70% unit, ~20% integration, ~6% contract, ~3% E2E, ~1% drill/GameDay.
2. Unit Tests
Framework: Vitest. Must NOT import infrastructure (no NestJS, no @grpc, no Postgres, no NATS). Pure domain logic only.
2.1 CBS PDU encoder tests
- Per severity (P0/P1/P2): Message Identifier mapping correct (4370/4371/4372).
- Per language: DCS correct (GSM7 for
en, UCS-2 forfa/ps/ar). - Per-language body exceeding 1 page: correct Page Parameter pagination.
- Serial Number allocation monotonic.
- ≥ 40 tests covering boundary conditions (exactly 82 GSM7 / 93 UCS-2 chars, single / multi-page, mixed-language body bundles).
2.2 UCS-2 / GSM7 conformance
Corpus test: 100 strings per language (en/fa/ps/ar), round-trip through encode → decode → byte-equal check. Includes ZWJ, ZWNJ, Tatweel, Arabic-Indic digits, mixed Latin+Arabic.
2.3 Domain-state-machine tests
Broadcast state transitions:
- ACCEPTED → DISPATCHING: all happy path permutations.
- ACCEPTED → CANCELLED: only within grace window + only with dual-control.
- DISPATCHING → ACKED: all MNOs succeed.
- DISPATCHING → PARTIAL: mixed MNO outcome.
- DISPATCHING → FAILED: all MNOs failed.
- Invalid transitions (e.g., ACKED → CANCELLED) rejected.
≥ 30 tests. Error cases must assert specific error codes.
2.4 Hash-chain tests
- Chain-append correctness:
record_hash = sha256(canonical(payload) || prev_hash). - Canonicalisation per RFC 8785 (JCS): deterministic across object-key orderings, number representations, whitespace.
- Tamper detection: single-bit flip in a row must be detected by verifier.
- Genesis row prev_hash = 32 zero bytes.
- Concurrent-append safety: two workers appending simultaneously must not corrupt ordering.
2.5 Authorisation-gate tests
- Caller cert-subject matching (
cbc.authorised_callerslookup). allowedSeveritiesenforcement: caller authorised for P1 cannot submit P0.allowedRegionsenforcement: caller authorised for Kabul cannot target Balkh without explicit grant.- Expiry enforcement:
notBefore/notAfteron caller registry row.
2.6 Adapter selection tests
Per-MNO configured adapter chosen at dispatch; unknown MNO → ADAPTER_NOT_CONFIGURED; fallback preference logic; config-reload without restart.
2.7 Property-based tests
fast-check properties:
- Hash chain appended N times remains monotonic and verifiable.
- Any permutation of per-MNO dispatch order yields same final-status aggregate.
- CBS PDU encode then decode == original (for all language × severity combinations).
- Canonicalisation invariant under object-key reordering.
At least 10 properties, 500+ runs each.
Unit coverage targets: ≥ 90% line on domain layer; ≥ 80% branch.
3. Integration Tests
Framework: Vitest + Testcontainers + softhsm2. Real Postgres, real NATS, real Redis in containers; HSM via softhsm2.
3.1 End-to-end persistence
- Accept broadcast → row in
cbc.broadcasts, row incbc.mno_dispatchesper MNO, audit row appended. - Re-accept of same
correlationIdwithin dedup window returns originalbroadcastId(idempotency). - Hash-chain verifier over 10 000 inserted rows completes < 30 s.
3.2 PKI + HSM paths
Using softhsm2 pre-loaded with test caller certs:
- Valid cert + valid signature → SUCCESS.
- Cert revoked (CRL) → FAILURE
CRL_REVOKED. - Cert revoked (OCSP-stapled) → FAILURE
OCSP_REJECT. - Cert not in
cbc.authorised_callers→ FAILURECALLER_NOT_REGISTERED. - Signature over tampered request body → FAILURE
BAD_SIG. - Replay of earlier signature (nonce / timestamp window) → FAILURE
REPLAY_NONCE.
3.3 MNO CBE adapter integration
Mock CBE endpoints (Standard3gpp, Ericsson, Huawei):
- Happy dispatch path.
- CBE timeout → FAILED for that MNO; others unaffected.
- CBE reject (vendor-proprietary error) → adapter translates to canonical
CBE_REJECT. - Mid-dispatch adapter circuit-break: after 3 failures, circuit opens.
3.4 Drill mode
- Scheduled drill fires on time.
- Drill uses CBS test-range Message Identifier (4370..4379 test slot).
- Drill
is_drill=truein audit. - Drill-result report written to S3 + email sent to NDMA + NOC.
3.5 NATS event emission
- Produced events match JSON Schema.
cbc.audit.v1carries chainrecordHash.- Outbox pattern: NATS publish + DB write atomic (durable outbox table).
3.6 Cancellation dual-control
- Single-approver cancel → rejected.
- Two-approver cancel within window → CANCELLED for pending MNOs.
- Already-ACKED MNOs retained; audit breakdown shows effective vs. ineffective.
4. Contract Tests
Pact (provider) + event-schema conformance.
4.1 Provider contract
cbc-bridge-service is provider for:
regulator-portal-service— gRPCBroadcastEmergency,GetBroadcastStatus,CancelBroadcast.- Civil Defence / NDMA client — same gRPC with different auth material.
4.2 Consumer contract
cbc-bridge-service is consumer of:
- HSM PKCS#11 library (vendor-tested).
- MNO CBE adapters (standard3gpp specified; proprietary adapters tested against vendor stubs).
4.3 Event schema conformance
JSON Schema for every produced NATS event; ajv-based contract test fails the build if a field is removed or renamed without a schema-version bump.
5. End-to-End Tests
Env: dedicated cbc-e2e Kubernetes namespace with real service + Postgres + Redis + NATS + HSM + 3 mock MNO CBE pods.
Scenarios:
5.1 Happy-path emergency broadcast
Government client submits signed P0 → accepted → dispatched to all 5 MNO mocks → all ACK → DELIVERED → audit chain intact → regulator-portal SIEM stream event received.
5.2 Partial success
One MNO mock configured to time out → final status PARTIAL → per-MNO breakdown correct → retry of failed MNO dispatches on human-triggered resubmit succeeds.
5.3 Authentication failure under storm
10 concurrent requests with revoked certs → all rejected → CbcPkiVerifyFailureSpike alert fires → legitimate request still succeeds.
5.4 Drill
Monthly drill fires → drill broadcast visible in NOC dashboard → after-action report generated → email to NDMA + NOC.
5.5 Cancellation
Government client submits → initiates cancel → second approver confirms within 60 s → CANCELLED for pending MNOs → audit shows both initiator + approver.
5.6 Audit verification
Insert 10 000 audit rows, daily verifier runs → OK. Manually corrupt one row → next run detects break at specific row.
6. Drill & GameDay
Quarterly GameDay against real MNO staging endpoints (per MoU with MNO partners).
6.1 GameDay scope
- Submit authorised test broadcast from government-PKI client.
- Verify CBS PDU arrives at MNO CBE correctly.
- Verify subscriber handsets (test devices per MNO) receive + display correctly in all 4 languages.
- Confirm ATRA observer acknowledges drill reception via regulator portal.
6.2 GameDay chaos injection
- Kill primary HSM pod → verify HSM HA fail-over + broadcast completes.
- Kill one MNO CBE adapter → verify PARTIAL status + NOC paging.
- Network partition Kabul ↔ Mazar → verify region-local operation continues.
6.3 GameDay pass criteria
- 100% test broadcasts delivered to ≥ 4 of 5 MNOs within 60 s.
- Zero PKI verification false-failures.
- Zero audit chain breaks.
- All chaos injections recovered per runbook within declared RTO.
7. Load Tests
7.1 Normal load
Expected traffic is low: ≤ 10 broadcasts / month steady-state. Load testing focuses on concurrency + burst resilience.
7.2 Burst load
100 concurrent cancellation requests. Assert no state-machine race; all cancellations audit-logged correctly.
7.3 Fingerprint-storm load
500 bad-cert requests / second for 5 min → CbcPkiVerifyFailureSpike fires → legitimate requests still accepted < 500 ms P99.
8. Security Tests
8.1 PKI-bypass corpus
500+ crafted attack vectors:
- Cert chain manipulation.
- Signature malleability.
- Replay attack (reused signature with stale timestamp).
- Nonce window evasion.
- Homoglyph attack on
callerOrg.
All must produce specific FAILURE reason + audit row.
8.2 Authorisation escalation
- Caller authorised for P1 attempts P0 → rejected.
- Caller authorised for Kabul attempts Balkh → rejected.
- Caller with expired
notAfterrow → rejected.
8.3 Audit tamper
- UPDATE on
cbc.audit→ rejected by Postgres trigger. - DELETE on
cbc.audit→ rejected by Postgres trigger. - Manual INSERT with fake
prev_hash→ detected by verifier.
8.4 Dual-control bypass attempts
- Single approver attempts to approve own initiation → rejected.
- Approver's cert revoked between initiation and approval → rejected.
- Approval outside 60 s window → rejected.
8.5 Injection tests
- SQL injection in REST admin endpoints.
- Path traversal in drill S3 reports.
- XSS in NOC dashboard inputs.
9. Coverage Targets
| Layer | Line | Branch |
|---|---|---|
| Domain | ≥ 90% | ≥ 80% |
| Application | ≥ 80% | ≥ 75% |
| Infrastructure | ≥ 60% | ≥ 60% |
| Value Objects | 100% | 95% |
Mutation testing (stryker-js): ≥ 75% for aggregates, ≥ 85% for VOs.
10. CI Gates
- TypeScript strict + ESLint domain-isolation.
- OpenAPI / proto diff gate: no breaking changes without major bump.
- Pact broker verification.
- Event schema registry conformance.
- Security tests must all pass; PKI-bypass corpus 0 bypasses.
- 80% mutation tests survived.
Failing any gate blocks merge.