# Billing Money Domain Test Harness v1

## Purpose

Billing changes need a stronger regression baseline than ordinary CRUD because money
bugs are hard to repair after ledger or invoice evidence reaches users. This harness
defines the required regression packs for the billing overhaul and maps them to the
current codebase.

## Required Regression Packs

| Pack | Required coverage | Current baseline | Next gap |
|---|---|---|---|
| Ledger immutability | Ledger-affecting changes create additive rows; no mutable balance source of truth | `cmd/billing-worker/worker_integration_test.go` verifies usage close and accrual create ledger rows idempotently. `cmd/api/routes_integration_test.go` covers admin payment recovery and ledger credits. `ledger_entries_immutable` rejects UPDATE/DELETE by default in the database schema. | Add correction-flow tests as product correction workflows are introduced. |
| Idempotent retry | Retried close/accrual/payment/recovery operations do not double-post ledger entries | Allocation close integration test calls `HandleAllocationClosed` twice and expects one ledger debit. Payment-session idempotency exists by unique key and admin reconcile guards credited sessions. | Add shared fixture helpers for idempotency keys across rating, posting, invoice generation, refunds, and provider webhooks. |
| Rating determinism | Same usage window plus same pricing snapshot produces same rated identity/output | Current `computeUsageCostMinor` worker unit test covers deterministic allocation cost arithmetic. `POST /api/v1/admin/billing/shadow-rating` provides a bounded no-write dry-run for selected historical allocation usage. | Add broader shadow-rating fixtures for non-allocation units once those units exist. |
| Time-window boundaries | UTC windows, zero/negative duration, DST boundaries, open/closed usage windows | Current worker tests cover positive duration and active accrual. | Add explicit UTC/DST boundary cases and fixed-clock helpers before adding invoices or monthly windows. |
| Numeric precision | Integer minor units, rounding behavior, overflow protection, no floating point money outputs | Current code computes allocation cost with `math.Round` and stores integer minor units. | Add table-driven tests for fractional hours, large GPU counts, multi-currency non-mixing, and overflow bounds. |
| Payment reconciliation | Provider replay/mismatch/failure paths do not double-credit and leave evidence | `cmd/webhook-worker/routes_integration_test.go` covers mismatch to `failed_reconcile`, audit, and outbox. `cmd/api` tests cover payment session recovery. | Add production Stripe initiated-session tests once real provider path is implemented. |
| Invoice total reconciliation | Invoice headers equal line totals; invoice evidence links to rated usage and ledger postings | Schema exists only. | Add once invoice worker/API exists; no implementation should ship without this pack. |
| Budget threshold | Threshold crossings are first-seen/last-seen/count based and do not spam events | Current legacy low-balance events are worker-owned. | Add notify-only budget tests before replacing low-balance behavior. |
| Delinquency transition | Financial restriction states are explicit and audited; destructive enforcement is gated | Current balance-depleted force-release is legacy behavior. | Add state-machine tests before introducing `restricted`/`suspended`/`collections_hold`. |
| Privileged-action audit | Admin balance, refund, payment recovery, pricing, invoice, and delinquency mutations audit actor/action/result/correlation | Existing admin refund/reconciliation/payment recovery tests cover parts of this. | Add a finance mutation audit helper and use it across all future billing tests. |
| Currency and tax | Per-currency balances do not mix; FX and tax snapshots are additive and explainable | Currency fields exist; no FX/tax logic. | Add before multi-currency tenants, reseller currency splits, or invoice tax support. |
| Reseller attribution | Raw usage remains end-customer attributed; reseller margin is rating/invoice layer only | Design-only. | Add before partner-channel billing. |

## Commands

Fast baseline for current harness:

```bash
go test ./packages/services/billing ./packages/services/payments ./cmd/api
```

Extended money-domain integration baseline:

```bash
go test -tags integration ./cmd/billing-worker ./cmd/webhook-worker ./cmd/api
```

The extended baseline requires initialized local Postgres via the normal local-dev
schema/seed flow.

## CI/CD Gate Split

Billing uses the same platform-control release machinery as the rest of the
control plane, but money-domain changes need explicit correctness tiers so slow
tests do not either block every small PR or get skipped before release.

### Fast PR Gates

Run on every PR that touches billing, payments, contracts, ledger schema, policy
keys, or billing/account frontend surfaces:

1. contract validation and breaking-change checks for billing/payment API and
   event fragments;
2. focused unit tests for `packages/services/billing`,
   `packages/services/payments`, `cmd/api`, `cmd/billing-worker`, and
   `cmd/webhook-worker`;
3. static governance guards for outbox transactions, audit presence, policy
   literals, canonical errors, log sanitization, and trace/correlation headers;
4. web typecheck and focused account billing tests when `packages/web/**` or
   billing page contracts change;
5. no shared environment mutation and no Stripe/provider side effects.

Target: keep normal billing PR feedback under 10 minutes by using Go build/test
caches, pnpm store cache, Next cache, and the prebuilt CI base image documented
in `.gitlab-ci.yml`.

### Nightly And Deploy Gates

Run on schedule, explicit operator trigger, and before demo/staging/prod deploys
when money-domain inputs changed:

1. full integration pack:
   `go test -tags integration ./cmd/billing-worker ./cmd/webhook-worker ./cmd/api`;
2. idempotency/replay pack for accrual, allocation close, webhook replay, admin
   recovery, refunds, and future invoice posting;
3. numeric/time-boundary pack with fixed clocks, UTC windows, fractional hours,
   large quantities, and overflow bounds;
4. frontend e2e for account billing and platform finance surfaces through
   `scripts/ci/frontend_e2e.sh`, using the web-only mocked suite for PRs and the
   real local stack for nightly/deploy confidence;
5. observability checks for billing-worker lag, failed outbox rows, DLQ posture,
   and payment reconciliation alerts.

Nightly failures should create or update queue-visible work with the failing
pack, correlation IDs, and artifact links. Deploy gates block promotion when a
ledger, payment, audit, or idempotency invariant fails.

### Release Promotion

Release promotion remains manifest-based:

1. merge fixes to `master`;
2. promote with `scripts/ci/platform_control_promote_release_branch.sh`;
3. publish immutable runtime images and release artifacts only when inputs
   changed;
4. deploy from `dist/platform-control-release-manifest.json`;
5. run remote validation phases against the deployed manifest without rebuilding
   artifacts.

For billing safety, production promotion requires passing evidence for the fast
PR gates, the latest nightly/deploy money-domain pack, migration/schema
compatibility, and billing-worker image digest inclusion in the release manifest.
Hotfixes may run a targeted money-domain pack, but they still need replay and
ledger immutability evidence for the affected path.

### Prebuilt Image And Cache Opportunities

1. Use the CI base image for Go, Ruby, pnpm, Playwright dependencies, OpenAPI
   tooling, and security scanners so billing PR jobs do not reinstall tools.
2. Split future billing CI scripts into focused commands such as
   `billing_fast_pr`, `billing_money_integration`, and
   `billing_release_evidence` while keeping GitLab/GitHub wiring as wrappers.
3. Reuse Go module/build caches and pnpm/Next caches across billing API and
   account billing frontend jobs.
4. Reuse already-published commit-tagged `billing-worker` and `webhook-worker`
   images during release manifest assembly unless their runtime inputs changed.
5. Persist Playwright browser dependencies in the CI base image or a runner-level
   cache; keep screenshots, traces, and `.ci-artifacts/test-results/` as release
   evidence for failed deploy gates.

### Trigger Rules

| Changed inputs | Fast PR gate | Nightly/deploy gate | Release requirement |
|---|---|---|---|
| `packages/services/billing/**`, `cmd/billing-worker/**` | focused Go tests plus governance guards | billing-worker integration, time/numeric/idempotency packs | billing-worker digest and money-domain evidence |
| `packages/services/payments/**`, `cmd/webhook-worker/**` | payment unit tests, webhook/audit guards | provider replay/reconciliation pack | webhook-worker digest and reconciliation evidence |
| `doc/api/**` billing/payment contracts | contract and codegen smoke | contract-backed integration pack | regenerated artifacts in manifest evidence |
| `doc/architecture/db_schema_v1.sql`, `scripts/seed.sql` billing policy/ledger inputs | migration/schema validation and policy literal guard | real Postgres money pack | schema/seed identifiers in manifest |
| `packages/web/**` account billing or finance surfaces | typecheck, unit tests, web contract guard | billing/finance e2e through frontend harness | frontend artifact digest and e2e evidence |
| `.gitlab-ci.yml`, `scripts/ci/**` billing/frontend harness | CI script smoke and queue validation | dry-run or scheduled full harness | documented gate behavior and rollback path |

## Harness Rules

1. All money values are integer minor units plus currency.
2. Ledger entries are immutable; corrections are additive.
3. Rating determinism tests must run before ledger posting tests.
4. Provider webhook replay tests must be idempotent by provider event/session ID.
5. Invoice total tests must reconcile line totals, rated usage, and ledger postings.
6. Budget threshold tests must prove repeated worker ticks summarize rather than spam.
7. Privileged mutation tests must assert audit records and correlation IDs.
8. Tests that write JSON metadata or money rows should run against real Postgres when
   bind typing, numeric precision, or constraint behavior matters.

## File Map

| Area | Current files |
|---|---|
| Billing service unit tests | `packages/services/billing/service_test.go`, `packages/services/billing/managed_ingress_usage_test.go` |
| Payments service unit tests | `packages/services/payments/service_test.go`, `packages/services/payments/provider_factory_test.go` |
| Billing worker integration | `cmd/billing-worker/worker_integration_test.go` |
| Webhook reconciliation integration | `cmd/webhook-worker/routes_integration_test.go` |
| API handler and integration coverage | `cmd/api/routes_test.go`, `cmd/api/routes_integration_test.go`, `cmd/api/routes_v3_readmodels_test.go` |
| Current state inventory | `doc/architecture/Billing_Current_State_Gap_Matrix_v1.md` |
| Billing invariants | `doc/architecture/Billing_Architectural_Invariants_v1.md` |

## Stop Conditions

Do not start broad billing schema or worker implementation if:

1. the fast baseline fails;
2. a new money-affecting path has no idempotency test;
3. a new privileged finance mutation has no audit assertion;
4. a new usage unit lacks attribution anchors;
5. a new invoice/tax/reseller path lacks an evidence and reconciliation test.
