5.0 KiB
Why
Section 2 of the test-suite audit (/mnt/s/web/genesis/docs/superpowers/specs/2026-04-25-test-suite-audit.md row "No contract tests") lists consumer/provider contract testing as a critical missing tier of the test pyramid: @suaveplan/oauth-client, @suaveplan/webhook-client, @suaveplan/schema-registry, and @suaveplan/api-schemas are textbook Pact candidates whose failure modes (provider drifting from consumer expectations, schema breaking changes, OAuth token-endpoint shape mutations, webhook signature drift) are not catchable by today's unit, integration, or E2E tiers.
Today the only schema-shaped guard is hand-written Zod validation in each package's tests, which proves the consumer's encoder agrees with itself, not that a real provider's responses still match what the consumer was built against. This change introduces a project-wide Pact-style consumer/provider contract testing capability and wires it into every consumer package whose surface crosses a network boundary.
What Changes
New package — @suaveplan/testing-contract
- Lives at
packages/testing/testing-contractand follows the same dependency-graph exception as@suaveplan/testing-integration: zero@suaveplan/*runtime deps except@suaveplan/types(perCLAUDE.mdrule 8 testing-infrastructure carve-out). - Public API:
defineConsumerInteraction({ provider, request, response, state? })— declarative interaction record.recordPactInteraction(httpClient)— wraps afetch-shaped client; while wrapped, every request/response pair is recorded against the matchingdefineConsumerInteractiondeclarations into a.pact.jsonfile.verifyProviderAgainstPact(pactFile, providerUrl, options)— replays a pact file's interactions against a running provider and asserts each response matches the recorded shape.publishPact(brokerUrl, version)andpullPactsForVerification(brokerUrl, providerName)— optional Pact Broker integration; auth viaPACT_BROKER_TOKENenv.
- Pact file shape conforms to Pact Specification v3
interactions[](consumer/provider names, request, response, providerStates). - Errors prefixed
TESTING_CONTRACT.*. Coverage gate at 100% (Istanbul, all four metrics).
Cross-cutting requirements on consumer packages
Each of the four packages SHALL ship a *.contract.test.ts that records interactions against its public API surface using recordPactInteraction. The recorded .pact.json files are committed to pacts/<consumer>__<provider>.json so PR review surfaces wire-format drift. Provider-side specs (where the package itself implements a server / receiver — webhook-client receivers, schema-registry server) SHALL run verifyProviderAgainstPact in CI against the same pact files.
Capability deltas
Each consumer package gets an ## ADDED Requirements block carrying:
- A Consumer-side pact recording requirement (must produce a pact file in
pacts/). - For server-shaped packages (
webhook-clientreceiver,schema-registryserver), a Provider-side pact verification requirement.
Capabilities
New Capabilities
testing-contract— Pact-style consumer/provider contract testing primitives (defineConsumerInteraction,recordPactInteraction,verifyProviderAgainstPact,publishPact,pullPactsForVerification).
Modified Capabilities
oauth-client— adds consumer-side pact requirement against the OAuth/OIDC provider's token + discovery endpoints.webhook-client— adds consumer-side (signing) and provider-side (receiver) pact requirements.schema-registry— adds consumer-side (schema lookup) and provider-side (registry server) pact requirements.api-schemas— adds consumer-side pact recording over thecommon/auth/admin/eventsenvelope shapes that consumers send to API plugins.
(None of these capabilities have a pre-existing openspec/specs/<capability>/spec.md yet; the deltas in this change land them as new files when folded.)
Impact
- Affected code: new package at
/mnt/s/web/genesis/packages/testing/testing-contract/**; new*.contract.test.tsfiles inside the four consumer packages; newpacts/directories per consumer package. - Affected consumers: every
@suaveplan/*package whose surface crosses a network boundary inherits the requirement on its first OpenSpec change after this one lands. - Catalog additions (root
package.json): none —testing-contractsynthesises pact JSON itself rather than depending on@pact-foundation/pact(which is Node-only and pulls a Ruby toolchain). The optional broker-publish path uses nativefetch. - CI: a new
pact:verifyTurborepo task runsverifyProviderAgainstPacton every commit for the provider-shaped packages. - Pre-publish gate: every package with contract tests must pass
pact:verifyin addition tolint typecheck testand 100% coverage. - Cross-cutting: five new spec files land on archive (
testing-contract,oauth-client,webhook-client,schema-registry,api-schemas).