Skip to main content

Billing Service — Testing Strategy

Status: populated Owner: Platform Engineering + QA Last updated: 2026-04-18

1. Coverage Targets

LayerTargetTool
Domain aggregates95%Vitest
Value objects (Money, etc.)100%Vitest
Domain services (PricingResolver, MarginCalculator)90%Vitest
Application use cases90% (branch)Vitest
Integration (NATS consumer + PG + Redis)All critical pathsTestcontainers
Mutation on domain≥ 75%Stryker

2. Unit Tests

  • Money VO: arithmetic, currency mismatch throws, negative amounts rejected.
  • PricingResolver: cache hit, cache miss, no matching rule throws PricingNotFoundError.
  • MarginCalculator: positive margin, zero margin, negative margin (allowed, flagged).
  • IngestBillingEventUseCase: happy path, duplicate messageId (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_events row persisted; usage_summaries upserted.
  • test/integration/ingest-idempotency.spec.ts — duplicate messageId → 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; invoice FINALIZED.
  • test/integration/invoice-void.spec.ts — admin void sets status, records voided_by/voided_at/void_reason.
  • test/integration/pricing-version.spec.ts — PATCH creates new row, old row gets effectiveTo; overlap prevented by constraint.

4. Contract Tests

  • Schema conformance for consumed billing.message.charged.v1 against schema registry.
  • Schema conformance for produced billing.invoice.generated.v1 against schema registry.
  • OpenAPI diff gate: no breaking changes to REST API without version bump.

5. Financial Correctness Tests

  • Property-based test: customerPrice = unitPrice * segmentCount for all integer segment counts 1–10.
  • margin = customerPrice - operatorCost holds for 1000 random pricing combinations.
  • Invoice subtotal = SUM of customerPrice across all billing_events for 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.