Billing Service — Testing Strategy
Status: populated Owner: Platform Engineering + QA Last updated: 2026-04-18
1. Coverage Targets
| Layer | Target | Tool |
|---|---|---|
| Domain aggregates | 95% | Vitest |
Value objects (Money, etc.) | 100% | Vitest |
| Domain services (PricingResolver, MarginCalculator) | 90% | Vitest |
| Application use cases | 90% (branch) | Vitest |
| Integration (NATS consumer + PG + Redis) | All critical paths | Testcontainers |
| Mutation on domain | ≥ 75% | Stryker |
2. Unit Tests
MoneyVO: arithmetic, currency mismatch throws, negative amounts rejected.PricingResolver: cache hit, cache miss, no matching rule throwsPricingNotFoundError.MarginCalculator: positive margin, zero margin, negative margin (allowed, flagged).IngestBillingEventUseCase: happy path, duplicatemessageId(idempotent no-op), pricing not found (error escalation).GenerateMonthlyInvoicesUseCase: correct period calculation, PDF render called, S3 PUT called, NATS publish called.
3. Integration Tests
test/integration/tenant-isolation.spec.ts— RLS rejects cross-tenant usage query.test/integration/ingest-happy-path.spec.ts— NATS event consumed;billing_eventsrow persisted;usage_summariesupserted.test/integration/ingest-idempotency.spec.ts— duplicatemessageId→ second insert ignored; usage_summaries not double-counted.test/integration/pricing-cache.spec.ts— Redis hit serves stale; cache miss falls through to PG; invalidation clears key.test/integration/invoice-generation.spec.ts— cron trigger; summaries aggregated; PDF rendered (mock renderer); S3 PUT called; invoiceFINALIZED.test/integration/invoice-void.spec.ts— admin void sets status, recordsvoided_by/voided_at/void_reason.test/integration/pricing-version.spec.ts— PATCH creates new row, old row getseffectiveTo; overlap prevented by constraint.
4. Contract Tests
- Schema conformance for consumed
billing.message.charged.v1against schema registry. - Schema conformance for produced
billing.invoice.generated.v1against schema registry. - OpenAPI diff gate: no breaking changes to REST API without version bump.
5. Financial Correctness Tests
- Property-based test:
customerPrice = unitPrice * segmentCountfor all integer segment counts 1–10. margin = customerPrice - operatorCostholds for 1000 random pricing combinations.- Invoice subtotal = SUM of
customerPriceacross allbilling_eventsfor the period (reconciliation test).
6. E2E
test/e2e/billing-flow.e2e.spec.ts: dlr-processor stub → NATS → billing-service → PG persisted → usage query API returns correct totals.
7. Load / Soak
- Target: 10,000 events/sec ingestion sustained; consumer lag stabilizes < 5000.
- k6 scenario: publish events to NATS at target rate; measure PG insert latency and consumer lag over 10 min.