Initial Commit
This commit is contained in:
34
openspec/changes/enterprise-testing-contract/design.md
Normal file
34
openspec/changes/enterprise-testing-contract/design.md
Normal file
@@ -0,0 +1,34 @@
|
||||
## Context
|
||||
|
||||
The four packages targeted by this change all sit on a network boundary: `oauth-client` calls Authorization Code / PKCE / token / discovery endpoints; `webhook-client` both signs outbound webhook deliveries (consumer of the receiver) and ships a receiver shape (provider for downstream consumers); `schema-registry` is itself a registry server but is also a consumer of upstream registries during compatibility checks; `api-schemas` defines the wire shapes that every API plugin consumer sends. Each of these surfaces has a real, reproducible failure mode where the provider shape mutates while consumer expectations do not — exactly the failure class Pact-style contract testing is designed to catch.
|
||||
|
||||
The audit (`/mnt/s/web/genesis/docs/superpowers/specs/2026-04-25-test-suite-audit.md` §2 row "No contract tests") flags this as a critical missing tier. The audit also explicitly names these four packages as the canonical first-wave consumers.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- A self-contained `@suaveplan/testing-contract` package with zero `@suaveplan/*` runtime deps (testing-infrastructure exception per `CLAUDE.md` rule 8) that produces and verifies Pact Specification v3 files.
|
||||
- A normative requirement on each of the four consumer packages: ship `*.contract.test.ts`, commit pact files to `pacts/`, run provider verification where a server is present.
|
||||
- Optional Pact Broker integration so multi-repo deployments can publish/pull pacts without the file commit.
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- Replicating the entire `@pact-foundation/pact` feature surface (matchers like `like`/`term`/`eachLike` are out of scope for v0; we ship exact-shape recording first and add matcher syntax in a follow-up).
|
||||
- Pulling `@pact-foundation/pact` itself as a dependency — it is Node-only and ships a Ruby standalone, neither of which fit the universal package constraint.
|
||||
- Bidirectional contract testing (Pactflow's bi-di flow). Classical consumer-driven only.
|
||||
|
||||
## Decisions
|
||||
|
||||
- **Pact Specification v3 as the wire format.** Future migration to v4 is plausible but v3 is the broadest-supported version across brokers (Pactflow, OSS Pact Broker) and matches the field shapes our `oauth-client` / `webhook-client` consumers already think in.
|
||||
- **`pacts/` committed in-repo.** Cross-package contract files live next to the consumer, not in a broker-only flow, because the genesis monorepo gives us atomic provider+consumer commits — broker integration is opt-in for downstream multi-repo deployments.
|
||||
- **`recordPactInteraction(httpClient)` rather than middleware.** A `fetch`-shaped wrapper composes with every consumer's existing injectable-fetch DI surface (every package on genesis exposes `{ fetch?: FetchLike }` or equivalent). No instrumentation of the consumer's internals is required.
|
||||
- **`verifyProviderAgainstPact` is HTTP-only.** Replaying a recorded pact against a live provider URL covers OAuth, webhook receivers, schema registry, and API-schemas consumers. Message contract testing (queues, pubsub) is deferred until a `messaging` package lands.
|
||||
- **Errors via local `TestingContractError`.** Mirrors the `TestingError` pattern in `@suaveplan/testing` — testing-infrastructure exception keeps the package free of `@suaveplan/error`.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **No matcher syntax in v0** means consumer pacts are exact-shape; provider drift on incidental fields (timestamps, request ids) will trigger spurious failures. Mitigated by an explicit `ignoreFields: string[]` option on `verifyProviderAgainstPact` for v0; full matcher support tracked as follow-up.
|
||||
- **Pact files committed in-repo** add line-noise to PRs that touch wire formats. This is the point — wire-format drift becomes reviewable diff, not silent runtime breakage.
|
||||
- **Broker auth via `PACT_BROKER_TOKEN`** keeps secrets out of pact files but requires CI secret plumbing; documented as a follow-up in `tasks.md`.
|
||||
- **Universal-package constraint** means the package cannot use `@pact-foundation/pact`'s native verifier; the in-house verifier must be kept simple and well-tested (100% coverage gate enforces this).
|
||||
Reference in New Issue
Block a user