bmad-method-test-architecture-enterprise 1.23.4 → 1.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +19 -0
- package/docs/how-to/customization/integrate-pactjs-utils.md +47 -0
- package/docs/reference/knowledge-base.md +5 -5
- package/package.json +1 -1
- package/src/agents/bmad-tea/resources/knowledge/contract-testing.md +48 -3
- package/src/agents/bmad-tea/resources/knowledge/pact-broker-webhooks.md +44 -5
- package/src/agents/bmad-tea/resources/knowledge/pact-consumer-framework-setup.md +98 -21
- package/src/agents/bmad-tea/resources/knowledge/pactjs-utils-mandate.md +28 -13
- package/src/agents/bmad-tea/resources/knowledge/pactjs-utils-overview.md +17 -3
- package/src/agents/bmad-tea/resources/knowledge/pactjs-utils-provider-verifier.md +125 -20
- package/src/agents/bmad-tea/resources/tea-index.csv +5 -5
- package/src/workflows/testarch/bmad-testarch-atdd/resources/knowledge/contract-testing.md +48 -3
- package/src/workflows/testarch/bmad-testarch-atdd/resources/knowledge/pact-broker-webhooks.md +44 -5
- package/src/workflows/testarch/bmad-testarch-atdd/resources/knowledge/pact-consumer-framework-setup.md +98 -21
- package/src/workflows/testarch/bmad-testarch-atdd/resources/knowledge/pactjs-utils-mandate.md +28 -13
- package/src/workflows/testarch/bmad-testarch-atdd/resources/knowledge/pactjs-utils-overview.md +17 -3
- package/src/workflows/testarch/bmad-testarch-atdd/resources/knowledge/pactjs-utils-provider-verifier.md +125 -20
- package/src/workflows/testarch/bmad-testarch-atdd/resources/tea-index.csv +5 -5
- package/src/workflows/testarch/bmad-testarch-automate/resources/knowledge/contract-testing.md +48 -3
- package/src/workflows/testarch/bmad-testarch-automate/resources/knowledge/pact-broker-webhooks.md +44 -5
- package/src/workflows/testarch/bmad-testarch-automate/resources/knowledge/pact-consumer-framework-setup.md +98 -21
- package/src/workflows/testarch/bmad-testarch-automate/resources/knowledge/pactjs-utils-mandate.md +28 -13
- package/src/workflows/testarch/bmad-testarch-automate/resources/knowledge/pactjs-utils-overview.md +17 -3
- package/src/workflows/testarch/bmad-testarch-automate/resources/knowledge/pactjs-utils-provider-verifier.md +125 -20
- package/src/workflows/testarch/bmad-testarch-automate/resources/tea-index.csv +5 -5
- package/src/workflows/testarch/bmad-testarch-automate/steps-c/step-03a-subagent-api.md +1 -1
- package/src/workflows/testarch/bmad-testarch-ci/resources/knowledge/contract-testing.md +48 -3
- package/src/workflows/testarch/bmad-testarch-ci/resources/knowledge/pact-broker-webhooks.md +44 -5
- package/src/workflows/testarch/bmad-testarch-ci/resources/knowledge/pact-consumer-framework-setup.md +98 -21
- package/src/workflows/testarch/bmad-testarch-ci/resources/knowledge/pactjs-utils-mandate.md +28 -13
- package/src/workflows/testarch/bmad-testarch-ci/resources/knowledge/pactjs-utils-overview.md +17 -3
- package/src/workflows/testarch/bmad-testarch-ci/resources/knowledge/pactjs-utils-provider-verifier.md +125 -20
- package/src/workflows/testarch/bmad-testarch-ci/resources/tea-index.csv +5 -5
- package/src/workflows/testarch/bmad-testarch-ci/steps-c/step-02-generate-pipeline.md +10 -7
- package/src/workflows/testarch/bmad-testarch-ci/steps-c/step-03-configure-quality-gates.md +1 -1
- package/src/workflows/testarch/bmad-testarch-framework/resources/knowledge/contract-testing.md +48 -3
- package/src/workflows/testarch/bmad-testarch-framework/resources/knowledge/pact-broker-webhooks.md +44 -5
- package/src/workflows/testarch/bmad-testarch-framework/resources/knowledge/pact-consumer-framework-setup.md +98 -21
- package/src/workflows/testarch/bmad-testarch-framework/resources/knowledge/pactjs-utils-mandate.md +28 -13
- package/src/workflows/testarch/bmad-testarch-framework/resources/knowledge/pactjs-utils-overview.md +17 -3
- package/src/workflows/testarch/bmad-testarch-framework/resources/knowledge/pactjs-utils-provider-verifier.md +125 -20
- package/src/workflows/testarch/bmad-testarch-framework/resources/tea-index.csv +5 -5
- package/src/workflows/testarch/bmad-testarch-framework/steps-c/step-03-scaffold-framework.md +6 -5
- package/src/workflows/testarch/bmad-testarch-nfr/resources/knowledge/contract-testing.md +48 -3
- package/src/workflows/testarch/bmad-testarch-nfr/resources/knowledge/pact-broker-webhooks.md +44 -5
- package/src/workflows/testarch/bmad-testarch-nfr/resources/knowledge/pact-consumer-framework-setup.md +98 -21
- package/src/workflows/testarch/bmad-testarch-nfr/resources/knowledge/pactjs-utils-mandate.md +28 -13
- package/src/workflows/testarch/bmad-testarch-nfr/resources/knowledge/pactjs-utils-overview.md +17 -3
- package/src/workflows/testarch/bmad-testarch-nfr/resources/knowledge/pactjs-utils-provider-verifier.md +125 -20
- package/src/workflows/testarch/bmad-testarch-nfr/resources/tea-index.csv +5 -5
- package/src/workflows/testarch/bmad-testarch-test-design/resources/knowledge/contract-testing.md +48 -3
- package/src/workflows/testarch/bmad-testarch-test-design/resources/knowledge/pact-broker-webhooks.md +44 -5
- package/src/workflows/testarch/bmad-testarch-test-design/resources/knowledge/pact-consumer-framework-setup.md +98 -21
- package/src/workflows/testarch/bmad-testarch-test-design/resources/knowledge/pactjs-utils-mandate.md +28 -13
- package/src/workflows/testarch/bmad-testarch-test-design/resources/knowledge/pactjs-utils-overview.md +17 -3
- package/src/workflows/testarch/bmad-testarch-test-design/resources/knowledge/pactjs-utils-provider-verifier.md +125 -20
- package/src/workflows/testarch/bmad-testarch-test-design/resources/tea-index.csv +5 -5
- package/src/workflows/testarch/bmad-testarch-test-review/resources/knowledge/contract-testing.md +48 -3
- package/src/workflows/testarch/bmad-testarch-test-review/resources/knowledge/pact-broker-webhooks.md +44 -5
- package/src/workflows/testarch/bmad-testarch-test-review/resources/knowledge/pact-consumer-framework-setup.md +98 -21
- package/src/workflows/testarch/bmad-testarch-test-review/resources/knowledge/pactjs-utils-mandate.md +28 -13
- package/src/workflows/testarch/bmad-testarch-test-review/resources/knowledge/pactjs-utils-overview.md +17 -3
- package/src/workflows/testarch/bmad-testarch-test-review/resources/knowledge/pactjs-utils-provider-verifier.md +125 -20
- package/src/workflows/testarch/bmad-testarch-test-review/resources/tea-index.csv +5 -5
- package/src/workflows/testarch/bmad-testarch-test-review/steps-c/criteria-registry.md +12 -12
- package/src/workflows/testarch/bmad-testarch-test-review/steps-c/step-03c-subagent-maintainability.md +1 -0
- package/src/workflows/testarch/bmad-testarch-trace/resources/knowledge/contract-testing.md +48 -3
- package/src/workflows/testarch/bmad-testarch-trace/resources/knowledge/pact-broker-webhooks.md +44 -5
- package/src/workflows/testarch/bmad-testarch-trace/resources/knowledge/pact-consumer-framework-setup.md +98 -21
- package/src/workflows/testarch/bmad-testarch-trace/resources/knowledge/pactjs-utils-mandate.md +28 -13
- package/src/workflows/testarch/bmad-testarch-trace/resources/knowledge/pactjs-utils-overview.md +17 -3
- package/src/workflows/testarch/bmad-testarch-trace/resources/knowledge/pactjs-utils-provider-verifier.md +125 -20
- package/src/workflows/testarch/bmad-testarch-trace/resources/tea-index.csv +5 -5
- package/tools/build-docs.js +4 -0
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"name": "bmad-method-test-architecture-enterprise",
|
|
32
32
|
"source": "./",
|
|
33
33
|
"description": "Master Test Architect module for quality strategy, test automation, CI/CD quality gates, and structured testing education. Part of the BMad Method ecosystem.",
|
|
34
|
-
"version": "1.
|
|
34
|
+
"version": "1.24.0",
|
|
35
35
|
"author": {
|
|
36
36
|
"name": "Murat K Ozcan (TEA Creator) & Brian (BMad) Madison"
|
|
37
37
|
},
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.24.0] - 2026-09-01
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Pact guidance now covers the short-lived branch coordination shipped in
|
|
15
|
+
`@seontechnologies/pactjs-utils` 1.2.0: scoped `consumerBranch` selectors for
|
|
16
|
+
HTTP and message provider verification, PR-only consumer/provider branch
|
|
17
|
+
detection, exact PactFlow provider-revision checkout, and an additive
|
|
18
|
+
`can-i-deploy` branch check that preserves the environment-wide safety gate.
|
|
19
|
+
It also adopts `isBreakingChangeTolerantBranch` with the explicit-consumer
|
|
20
|
+
no-pacts guard that prevents typo-driven false greens. The Pact mandate,
|
|
21
|
+
framework scaffolding, knowledge selection metadata, and public integration
|
|
22
|
+
documentation use the same rules.
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- Documentation builds clear Astro's content cache before reading the symlinked
|
|
27
|
+
`docs/` tree, preventing duplicate content IDs on repeated builds.
|
|
28
|
+
|
|
10
29
|
## [1.23.4] - 2026-08-31
|
|
11
30
|
|
|
12
31
|
### Fixed
|
|
@@ -134,6 +134,53 @@ await new Verifier(
|
|
|
134
134
|
|
|
135
135
|
State handler names and their `params` must match the consumer's `createProviderState` exactly. That pairing is the contract's own contract.
|
|
136
136
|
|
|
137
|
+
## Coordinate Different PR Branch Names
|
|
138
|
+
|
|
139
|
+
`matchingBranch: true` covers teams that use the same branch name in consumer
|
|
140
|
+
and provider repositories. Pact.js Utils 1.2.0 also covers short-lived branches
|
|
141
|
+
with different names.
|
|
142
|
+
|
|
143
|
+
On the provider side, `buildVerifierOptions` and
|
|
144
|
+
`buildMessageVerifierOptions` accept `consumerBranch`, defaulting to
|
|
145
|
+
`PACT_CONSUMER_BRANCH`:
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
buildVerifierOptions({
|
|
149
|
+
provider: 'SampleMoviesAPI',
|
|
150
|
+
port: '3001',
|
|
151
|
+
includeMainAndDeployed: true,
|
|
152
|
+
consumer: 'SampleAppConsumer',
|
|
153
|
+
consumerBranch: process.env.PACT_CONSUMER_BRANCH,
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The explicit selector is scoped to `consumer`; the builders throw when a
|
|
158
|
+
consumer branch is supplied without one. It stays alongside the matching,
|
|
159
|
+
main, and deployed selectors.
|
|
160
|
+
|
|
161
|
+
On the consumer side, copy the package's `detect-provider-branch` composite
|
|
162
|
+
action and add `Pact provider branch: <name>` to the PR template. During the PR,
|
|
163
|
+
`can-i-deploy.sh`:
|
|
164
|
+
|
|
165
|
+
1. checks the target environment while ignoring only that named provider;
|
|
166
|
+
2. checks the same consumer version against the provider branch tip.
|
|
167
|
+
|
|
168
|
+
Both calls are required. A branch check proves less than an environment check,
|
|
169
|
+
so the override is read on pull requests only and disappears on push to main.
|
|
170
|
+
|
|
171
|
+
The provider has a mirror `detect-consumer-branch` action for manual
|
|
172
|
+
coordination. Keep that PR flow separate from PactFlow's
|
|
173
|
+
`contract_requiring_verification_published` webhook. The webhook identifies an
|
|
174
|
+
exact provider version that needs a result. Check out its
|
|
175
|
+
`providerVersionNumber`, verify that commit belongs to
|
|
176
|
+
`providerVersionBranch`, and publish against those values.
|
|
177
|
+
|
|
178
|
+
Provider suites with an explicit breaking-change tolerance policy should use
|
|
179
|
+
`isBreakingChangeTolerantBranch`. It recognizes only `main`, `master`, and
|
|
180
|
+
`release/**`. Check and reject a missing hand-entered `PACT_CONSUMER_BRANCH`
|
|
181
|
+
before applying that tolerance, so a typo cannot turn an unexecuted
|
|
182
|
+
cross-branch verification green.
|
|
183
|
+
|
|
137
184
|
## Which Workflows Change
|
|
138
185
|
|
|
139
186
|
| Workflow | What the flag changes |
|
|
@@ -139,16 +139,16 @@ Contract testing fundamentals plus Pact.js Utils, Pact MCP, and broker operation
|
|
|
139
139
|
|
|
140
140
|
| Fragment | Tier | Description | Key Topics |
|
|
141
141
|
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
|
|
142
|
-
| [contract-testing](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/contract-testing.md) | specialized | Raw Pact patterns, publishing, verification,
|
|
143
|
-
| [pactjs-utils-overview](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pactjs-utils-overview.md) | specialized | Installation, flow decision tree, utility map
|
|
142
|
+
| [contract-testing](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/contract-testing.md) | specialized | Raw Pact patterns, publishing, verification, short-lived branch coordination, resilience, and FFI safety | Contract testing, Pact fundamentals |
|
|
143
|
+
| [pactjs-utils-overview](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pactjs-utils-overview.md) | specialized | Installation, flow decision tree, utility map, and branch coordination | pactjs-utils, CDCT/BDCT, integration strategy |
|
|
144
144
|
| [pactjs-utils-mandate](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pactjs-utils-mandate.md) | core | The binding rule when `tea_use_pactjs_utils` is true: substitution table, the relevance gate before scaffolding, banned patterns, pre-emit self-check, broker degradation | Standards, contract testing, generation, review |
|
|
145
145
|
| [pactjs-utils-zod-to-pact](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pactjs-utils-zod-to-pact.md) | specialized | `zodToPactMatchers` for consumer-curated schemas, example precedence, Pact V3 matcher mapping, anti-patterns | pactjs-utils, zod, consumer schemas, matchers |
|
|
146
146
|
| [pactjs-utils-consumer-helpers](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pactjs-utils-consumer-helpers.md) | specialized | Provider-state helpers: `createProviderState`, `toJsonMap`; request/response callback helpers: `setJsonBody`, `setJsonContent` | pactjs-utils, consumer testing, provider state |
|
|
147
|
-
| [pactjs-utils-provider-verifier](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pactjs-utils-provider-verifier.md) | specialized |
|
|
147
|
+
| [pactjs-utils-provider-verifier](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pactjs-utils-provider-verifier.md) | specialized | Verifier builders, scoped `consumerBranch`, provider revision metadata, branch classification | pactjs-utils, provider verification, CI |
|
|
148
148
|
| [pactjs-utils-request-filter](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pactjs-utils-request-filter.md) | specialized | `createRequestFilter`, `noOpRequestFilter` auth/header patterns | pactjs-utils, request filter, auth injection |
|
|
149
149
|
| [pact-mcp](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pact-mcp.md) | specialized | SmartBear MCP tools for provider states, review, can-i-deploy, matrix | pact-mcp, broker interaction, pactflow |
|
|
150
|
-
| [pact-consumer-framework-setup](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pact-consumer-framework-setup.md) | specialized | Consumer CDC
|
|
151
|
-
| [pact-broker-webhooks](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pact-broker-webhooks.md) | specialized | PactFlow
|
|
150
|
+
| [pact-consumer-framework-setup](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pact-consumer-framework-setup.md) | specialized | Consumer CDC scaffolding with deterministic tests, PR-only provider branch detection, and additive branch-aware `can-i-deploy` | pactjs-utils, consumer CDC, framework setup |
|
|
151
|
+
| [pact-broker-webhooks](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pact-broker-webhooks.md) | specialized | PactFlow dispatch auth, exact provider target checkout, staleness monitoring, and PAT rotation | pact broker, webhooks, CI operations, security |
|
|
152
152
|
| [pact-consumer-di](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/blob/main/src/agents/bmad-tea/resources/knowledge/pact-consumer-di.md) | extended | Dependency-injection pattern for Pact consumer tests using real client code | pact, consumer, DI, contract accuracy |
|
|
153
153
|
|
|
154
154
|
**Used in:** `framework`, `test-design`, `atdd`, `automate`, `test-review`, `ci` (conditioned by `tea_use_pactjs_utils` and `tea_pact_mcp`)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "bmad-method-test-architecture-enterprise",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.24.0",
|
|
5
5
|
"description": "Master Test Architect for quality strategy, test automation, and release gates",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"bmad",
|
|
@@ -458,6 +458,43 @@ jobs:
|
|
|
458
458
|
|
|
459
459
|
---
|
|
460
460
|
|
|
461
|
+
### Coordinating Different Short-Lived Branch Names
|
|
462
|
+
|
|
463
|
+
`matchingBranch: true` works only when consumer and provider branches share a
|
|
464
|
+
name. Release trains often break that assumption: a consumer feature branch may
|
|
465
|
+
need a provider at `release/week-32`.
|
|
466
|
+
|
|
467
|
+
Treat this as two separate checks:
|
|
468
|
+
|
|
469
|
+
1. **Provider selects the consumer branch.** Pass a scoped `consumer` plus
|
|
470
|
+
`consumerBranch` to `buildVerifierOptions` or
|
|
471
|
+
`buildMessageVerifierOptions`. `PACT_CONSUMER_BRANCH` is the default input.
|
|
472
|
+
The explicit `{ consumer, branch }` selector is additive to matching, main,
|
|
473
|
+
and deployed selectors.
|
|
474
|
+
2. **Consumer selects the provider branch.** On PRs only, parse `Pact provider
|
|
475
|
+
branch: <name>` into `PACT_PROVIDER_BRANCH`. Keep the environment-wide
|
|
476
|
+
`can-i-deploy` call, use `--ignore <provider>` for that one in-flight
|
|
477
|
+
pacticipant, then run a second `can-i-deploy` with the provider's
|
|
478
|
+
`--branch`. Both calls must fail hard.
|
|
479
|
+
|
|
480
|
+
The second check is weaker than `--to-environment`: it proves compatibility
|
|
481
|
+
with a branch tip, not with deployed software. Its safety comes from narrow
|
|
482
|
+
scope and short lifetime. Never read the override from the merged PR on push,
|
|
483
|
+
and never replace the environment check globally.
|
|
484
|
+
|
|
485
|
+
Keep manual PR branch selection separate from PactFlow's
|
|
486
|
+
`contract_requiring_verification_published` webhook. That event names the exact
|
|
487
|
+
provider version missing a verification result. Check out its
|
|
488
|
+
`providerVersionNumber`, confirm that commit belongs to
|
|
489
|
+
`providerVersionBranch`, and publish with the same version and branch. Stop the
|
|
490
|
+
job when the target cannot be checked out. A substitute revision produces no
|
|
491
|
+
evidence for the Broker's requested target.
|
|
492
|
+
|
|
493
|
+
See `pactjs-utils-provider-verifier.md`,
|
|
494
|
+
`pact-consumer-framework-setup.md`, and `pact-broker-webhooks.md`.
|
|
495
|
+
|
|
496
|
+
---
|
|
497
|
+
|
|
461
498
|
### Example 4: Resilience Coverage (Testing Fallback Behavior)
|
|
462
499
|
|
|
463
500
|
**Context**: Capture timeout, retry, and error handling behavior explicitly in contracts.
|
|
@@ -1014,6 +1051,8 @@ Before implementing contract testing, verify:
|
|
|
1014
1051
|
- [ ] **Provider verification**: Runs on PR, verifies all consumer pacts
|
|
1015
1052
|
- [ ] **State handlers**: Provider implements all given() states
|
|
1016
1053
|
- [ ] **can-i-deploy**: Blocks deployment if contracts incompatible
|
|
1054
|
+
- [ ] **Short-lived branch overrides**: PR-only, scoped to one pacticipant, and
|
|
1055
|
+
additive to the environment gate
|
|
1017
1056
|
- [ ] **Webhooks configured**: Consumer changes trigger provider verification
|
|
1018
1057
|
- [ ] **Retention policy**: Old pacts archived (keep 30 days, all production tags)
|
|
1019
1058
|
- [ ] **Resilience tested**: Timeouts, retries, error codes in contracts
|
|
@@ -1039,25 +1078,31 @@ When `tea_use_pactjs_utils` is enabled, the following utilities replace manual b
|
|
|
1039
1078
|
| Repeated builder callbacks for query/header/body | `setJsonContent({ query, headers, body })` | Reusable callback for `.withRequest(...)` and `.willRespondWith(...)` |
|
|
1040
1079
|
| Inline body lambda `(builder) => builder.jsonBody(body)` | `setJsonBody(body)` | Body-only shorthand for cleaner response builders |
|
|
1041
1080
|
| 30+ lines of `VerifierOptions` assembly | `buildVerifierOptions({ provider, port, includeMainAndDeployed, stateHandlers })` | One-call setup, env-aware, flow auto-detection |
|
|
1042
|
-
| Manual broker URL + selector logic from env vars | `handlePactBrokerUrlAndSelectors({ ..., options })`
|
|
1081
|
+
| Manual broker URL + selector logic from env vars | `handlePactBrokerUrlAndSelectors({ ..., consumerBranch, options })` | Handles standard selectors and a scoped named consumer branch |
|
|
1043
1082
|
| DIY Express middleware for auth injection | `createRequestFilter({ tokenGenerator })` | Bearer prefix contract prevents double-prefix bugs |
|
|
1044
1083
|
| Manual CI branch/tag extraction | `getProviderVersionTags()` | CI-aware (GitHub Actions, GitLab CI, etc.) |
|
|
1084
|
+
| Repeated main/master/release branch classification | `isBreakingChangeTolerantBranch(branch)` | One exact boundary for an explicit breaking-change tolerance policy |
|
|
1045
1085
|
| Message verifier config assembly | `buildMessageVerifierOptions({ provider, messageProviders })` | Same one-call pattern for Kafka/async contracts |
|
|
1046
1086
|
| Inline no-op filter `(req, res, next) => next()` | `noOpRequestFilter` | Pre-built pass-through for no-auth providers |
|
|
1047
1087
|
| Hand-written matcher helper duplicating a Zod/TS type | `zodToPactMatchers(ConsumerMovieSchema, example)` | Single source of truth for response shape; consumer-curated scope keeps contracts lean and consumer-driven |
|
|
1048
1088
|
|
|
1049
1089
|
See the `pactjs-utils-*.md` knowledge fragments for complete examples and anti-patterns (`pactjs-utils-zod-to-pact.md` covers the consumer-curated schema pattern).
|
|
1050
1090
|
|
|
1091
|
+
For differently named in-flight branches, use pactjs-utils 1.2.0 or newer.
|
|
1092
|
+
That release adds `consumerBranch` to both verifier builders and ships the
|
|
1093
|
+
PR-only provider-branch detection, additive `can-i-deploy` templates, and
|
|
1094
|
+
`isBreakingChangeTolerantBranch`.
|
|
1095
|
+
|
|
1051
1096
|
### PactV4 Determinism & FFI Safety (Mandatory)
|
|
1052
1097
|
|
|
1053
1098
|
Four rules that together prevent both (a) non-deterministic pact generation failures that cause `Cannot change pact content for already published pact` errors at PactFlow publish, and (b) "request was expected but not received" flakes observed on Linux CI once a consumer+provider pair has more than one `.pacttest.ts` file:
|
|
1054
1099
|
|
|
1055
1100
|
1. **Consumer Vitest `fileParallelism: false`** in `vitest.config.pact.ts` — prevents parallel workers from racing on the shared pact JSON. See `pact-consumer-framework-setup.md` Example 2.
|
|
1056
|
-
2. **Consumer Vitest `pool: 'forks'` + `poolOptions.forks.singleFork: true`** in `vitest.config.pact.ts` — same config as the provider side (`pactjs-utils-provider-verifier.md` Example
|
|
1101
|
+
2. **Consumer Vitest `pool: 'forks'` + `poolOptions.forks.singleFork: true`** in `vitest.config.pact.ts` — same config as the provider side (`pactjs-utils-provider-verifier.md` Example 8). Best current understanding: the `@pact-foundation/pact` napi-rs binding is not robust across Vitest worker threads sharing a process; serialization alone (via `fileParallelism: false`) is insufficient on the default threads pool in Vitest v1. Forks + `singleFork: true` runs every pact file in one subprocess with a coherent FFI handle and eliminated a reproducible Linux-CI flake across multiple repos. Single-file consumer suites have not been observed to flake; this rule is still recommended as a future-proof. See `pact-consumer-framework-setup.md` Example 2.
|
|
1057
1102
|
3. **One `addInteraction()` per `it()` block** — see `pactjs-utils-consumer-helpers.md` Example 6.
|
|
1058
1103
|
4. **`publish-pact.sh` jq normalization** sorts interactions before publish — ensures byte-stable payload to PactFlow regardless of generator ordering quirks. See `pact-consumer-framework-setup.md` Example 4.
|
|
1059
1104
|
|
|
1060
|
-
Provider suites require the same `pool: 'forks'` + `singleFork: true` combination — see `pactjs-utils-provider-verifier.md` Example
|
|
1105
|
+
Provider suites require the same `pool: 'forks'` + `singleFork: true` combination — see `pactjs-utils-provider-verifier.md` Example 8.
|
|
1061
1106
|
|
|
1062
1107
|
### Webhook Auth & Staleness
|
|
1063
1108
|
|
|
@@ -9,6 +9,10 @@ Configure PactFlow webhooks to trigger provider verification in GitHub Actions v
|
|
|
9
9
|
### Why webhooks matter
|
|
10
10
|
|
|
11
11
|
- PactFlow's `contract_requiring_verification_published` webhook is the mechanism that notifies a provider repo (via `repository_dispatch`) that a consumer has published a contract needing verification.
|
|
12
|
+
- The webhook carries `${pactbroker.providerVersionNumber}` and
|
|
13
|
+
`${pactbroker.providerVersionBranch}` for the provider version missing a
|
|
14
|
+
result. The provider workflow checks out that exact registered revision
|
|
15
|
+
before publishing verification.
|
|
12
16
|
- Without a working webhook, `can-i-deploy` in the consumer CI **times out** (900s) and eventually fails with `There is no verified pact between <consumer-version> and the version of <provider> currently in <env>` — even though nothing is wrong in either codebase.
|
|
13
17
|
- Webhook failures are **silent by default**: PactFlow keeps emitting requests, GitHub keeps returning `401 Unauthorized`, but nothing alerts the team until a PR is blocked.
|
|
14
18
|
|
|
@@ -85,13 +89,38 @@ jobs:
|
|
|
85
89
|
PACT_BROKER_TOKEN: ${{ secrets.PACT_BROKER_TOKEN }}
|
|
86
90
|
# Pulled from webhook client_payload when triggered by PactFlow:
|
|
87
91
|
PACT_PAYLOAD_URL: ${{ github.event.client_payload.pact_url }}
|
|
88
|
-
|
|
89
|
-
|
|
92
|
+
PACT_PROVIDER_VERSION: ${{ github.event.client_payload.sha }}
|
|
93
|
+
PACT_PROVIDER_BRANCH: ${{ github.event.client_payload.branch }}
|
|
90
94
|
steps:
|
|
91
95
|
- uses: actions/checkout@v4
|
|
92
96
|
with:
|
|
93
|
-
|
|
94
|
-
|
|
97
|
+
fetch-depth: 0
|
|
98
|
+
- name: Select provider revision
|
|
99
|
+
id: provider-revision
|
|
100
|
+
run: |
|
|
101
|
+
if [ -z "$PACT_PROVIDER_BRANCH" ] || [ -z "$PACT_PROVIDER_VERSION" ]; then
|
|
102
|
+
echo "Webhook payload is missing the provider branch or version."
|
|
103
|
+
exit 1
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
git fetch origin -- "$PACT_PROVIDER_BRANCH"
|
|
107
|
+
|
|
108
|
+
if ! git rev-parse --verify --quiet "$PACT_PROVIDER_VERSION^{commit}" >/dev/null; then
|
|
109
|
+
echo "Provider version $PACT_PROVIDER_VERSION is unavailable."
|
|
110
|
+
exit 1
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
if ! git merge-base --is-ancestor "$PACT_PROVIDER_VERSION" FETCH_HEAD; then
|
|
114
|
+
echo "Provider version $PACT_PROVIDER_VERSION is not on $PACT_PROVIDER_BRANCH."
|
|
115
|
+
exit 1
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
git checkout --detach "$PACT_PROVIDER_VERSION"
|
|
119
|
+
SELECTED_VERSION="$(git rev-parse HEAD)"
|
|
120
|
+
|
|
121
|
+
echo "PACT_PROVIDER_VERSION=$SELECTED_VERSION" >> "$GITHUB_ENV"
|
|
122
|
+
echo "PACT_PROVIDER_BRANCH=$PACT_PROVIDER_BRANCH" >> "$GITHUB_ENV"
|
|
123
|
+
echo "GITHUB_BRANCH=$PACT_PROVIDER_BRANCH" >> "$GITHUB_ENV"
|
|
95
124
|
- uses: actions/setup-node@v4
|
|
96
125
|
with:
|
|
97
126
|
node-version: 20
|
|
@@ -107,7 +136,14 @@ jobs:
|
|
|
107
136
|
|
|
108
137
|
- `repository_dispatch` is the event type emitted by GitHub when the webhook's REST call hits `/repos/<org>/<repo>/dispatches`.
|
|
109
138
|
- The `types` filter must match the webhook's `event_type` (`contract_requiring_verification_published` here).
|
|
110
|
-
-
|
|
139
|
+
- Check out the exact `providerVersionNumber` on `providerVersionBranch`.
|
|
140
|
+
PactFlow emits this event for each main, deployed, or released provider
|
|
141
|
+
version missing a result.
|
|
142
|
+
- Verify that the commit exists and belongs to the registered branch before
|
|
143
|
+
detaching to it. A force-pushed or deleted revision fails the job and leaves
|
|
144
|
+
the requested result unknown.
|
|
145
|
+
- After checkout, overwrite `PACT_PROVIDER_VERSION` with `git rev-parse HEAD`
|
|
146
|
+
and publish the result against `PACT_PROVIDER_BRANCH`.
|
|
111
147
|
- `PACT_PAYLOAD_URL` makes `buildVerifierOptions` verify only the triggering pact (see `pactjs-utils-provider-verifier.md` Example 1).
|
|
112
148
|
|
|
113
149
|
### Example 3: Secret Rotation Runbook
|
|
@@ -180,6 +216,9 @@ jobs:
|
|
|
180
216
|
- **Classic PAT, `repo` scope, no expiration** — required for `repository_dispatch`. The "no expiration" trade-off is accepted in exchange for machine-user ownership + PactFlow-secret storage + staleness monitoring.
|
|
181
217
|
- **Store the PAT as a PactFlow secret** at `/settings/secrets`, reference it from the webhook via `${user.<secret-name>}`. Never inline the token.
|
|
182
218
|
- **Monitor for silence** — at minimum, a daily scheduled CI job that asserts a recent verification timestamp exists for each critical consumer/provider pair.
|
|
219
|
+
- **Provider targets need broker history** — publish provider verification
|
|
220
|
+
results with branch metadata. Record deployments and releases so PactFlow can
|
|
221
|
+
identify every provider version that this event must verify.
|
|
183
222
|
- **Rotation is a runbook, not an emergency** — document it (see Example 3), keep it in the repo, and do a practice rotation once a year so it stays fresh.
|
|
184
223
|
- **Symptom to remember**: "consumer `can-i-deploy` timeout after 900s with `There is no verified pact...`" + "provider's `contract-test-provider` workflow has no recent runs" = expired/revoked PAT. Start with Example 3.
|
|
185
224
|
|
|
@@ -35,8 +35,10 @@ scripts/
|
|
|
35
35
|
|
|
36
36
|
.github/
|
|
37
37
|
├── actions/
|
|
38
|
-
│
|
|
39
|
-
│
|
|
38
|
+
│ ├── detect-breaking-change/
|
|
39
|
+
│ │ └── action.yml # PR checkbox-driven breaking change detection
|
|
40
|
+
│ └── detect-provider-branch/
|
|
41
|
+
│ └── action.yml # PR-only provider branch coordination
|
|
40
42
|
└── workflows/
|
|
41
43
|
└── contract-test-consumer.yml # Consumer CDC CI workflow
|
|
42
44
|
```
|
|
@@ -77,8 +79,8 @@ export default defineConfig({
|
|
|
77
79
|
**Key Points**:
|
|
78
80
|
|
|
79
81
|
- **`fileParallelism: false` is required** — primary defense against non-deterministic pact generation. Without it, parallel workers race on the shared pact JSON file and corrupt interactions. Symptom: local runs pass, CI randomly fails with `Cannot change pact content for already published pact`. The `publish-pact.sh` `jq` sort (Example 4) provides byte-stability at publish time.
|
|
80
|
-
- **`pool: 'forks'` + `singleFork: true` is required for multi-file consumer suites** — same config the provider side uses (`pactjs-utils-provider-verifier.md` Example
|
|
81
|
-
- **One `.pacttest.ts` per consumer+provider pair is the canonical pattern** — not just an observation. Two files for the same pair in one process (which `singleFork: true` guarantees) cause an FFI handle collision: the second file's `new PactV4(...)` call re-enters the FFI handle still holding stale state from the first file → "request was expected but not received" sporadically on Linux CI. The fix is structural — merge the files, not the config. `pool: 'forks'` is still required for pact JSON write safety but does NOT prevent same-pair file splits from colliding. Multiple files for **different** pairs (different consumer or provider name) are correct and safe. See Example
|
|
82
|
+
- **`pool: 'forks'` + `singleFork: true` is required for multi-file consumer suites** — same config the provider side uses (`pactjs-utils-provider-verifier.md` Example 8). Best current understanding: the `@pact-foundation/pact` napi-rs binding is not robust across Vitest worker threads sharing a process; with the default threads pool (Vitest v1) and multiple `.pacttest.ts` files on the same consumer+provider pair, we observed reproducible "request was expected but not received" flakes on Linux CI only. `singleFork: true` serializes every pact file into one forked subprocess and eliminated the flake across multiple repos. Vitest v2+ defaults to `forks`, but set the pool explicitly so the contract does not drift with Vitest version bumps.
|
|
83
|
+
- **One `.pacttest.ts` per consumer+provider pair is the canonical pattern** — not just an observation. Two files for the same pair in one process (which `singleFork: true` guarantees) cause an FFI handle collision: the second file's `new PactV4(...)` call re-enters the FFI handle still holding stale state from the first file → "request was expected but not received" sporadically on Linux CI. The fix is structural — merge the files, not the config. `pool: 'forks'` is still required for pact JSON write safety but does NOT prevent same-pair file splits from colliding. Multiple files for **different** pairs (different consumer or provider name) are correct and safe. See Example 11 for the ✅/❌ pattern.
|
|
82
84
|
- **Interacting settings**: leave `isolate` at its default (`true`). Do NOT set `sequence.concurrent: true`, `maxConcurrency > 1`, or `maxWorkers > 1` in this config — they defeat the serialization this rule relies on. `hookTimeout` may be raised if mock-server startup is slow, but keep `testTimeout` ≥ `hookTimeout`.
|
|
83
85
|
- Do NOT add `setupFiles`, `coverage`, or other settings from the unit test config
|
|
84
86
|
- Keep it minimal — Pact tests run in Node environment with extended timeout
|
|
@@ -98,13 +100,16 @@ export default defineConfig({
|
|
|
98
100
|
"scripts": {
|
|
99
101
|
"test:pact:consumer": "vitest run --config vitest.config.pact.ts",
|
|
100
102
|
"publish:pact": ". ./scripts/env-setup.sh && ./scripts/publish-pact.sh",
|
|
101
|
-
"can:i:deploy:consumer": ". ./scripts/env-setup.sh && PACTICIPANT=<
|
|
103
|
+
"can:i:deploy:consumer": ". ./scripts/env-setup.sh && PACTICIPANT=<consumer-name> PROVIDER_PACTICIPANT=<provider-name> ./scripts/can-i-deploy.sh",
|
|
102
104
|
"record:consumer:deployment": ". ./scripts/env-setup.sh && PACTICIPANT=<service-name> ./scripts/record-deployment.sh"
|
|
103
105
|
}
|
|
104
106
|
}
|
|
105
107
|
```
|
|
106
108
|
|
|
107
|
-
Replace `<
|
|
109
|
+
Replace `<consumer-name>` and `<provider-name>` with the Pact Broker
|
|
110
|
+
pacticipant names. `PROVIDER_PACTICIPANT` scopes the optional short-lived
|
|
111
|
+
provider branch override; the plain environment gate still works when no
|
|
112
|
+
override is set.
|
|
108
113
|
|
|
109
114
|
**Key Points**:
|
|
110
115
|
|
|
@@ -185,12 +190,33 @@ set -euo pipefail
|
|
|
185
190
|
PACTICIPANT="${PACTICIPANT:?PACTICIPANT env var is required}"
|
|
186
191
|
ENVIRONMENT="${ENVIRONMENT:-dev}"
|
|
187
192
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
193
|
+
# PR-only override set by detect-provider-branch. Preserve the environment-wide
|
|
194
|
+
# gate for every other dependency, and check the in-flight provider branch
|
|
195
|
+
# separately. Both calls fail hard under set -e.
|
|
196
|
+
if [ -n "${PACT_PROVIDER_BRANCH:-}" ] && [ -n "${PROVIDER_PACTICIPANT:-}" ]; then
|
|
197
|
+
pact-broker can-i-deploy \
|
|
198
|
+
--pacticipant "$PACTICIPANT" \
|
|
199
|
+
--version="$GITHUB_SHA" \
|
|
200
|
+
--to-environment "$ENVIRONMENT" \
|
|
201
|
+
--ignore "$PROVIDER_PACTICIPANT" \
|
|
202
|
+
--retry-while-unknown=10 \
|
|
203
|
+
--retry-interval=30
|
|
204
|
+
|
|
205
|
+
pact-broker can-i-deploy \
|
|
206
|
+
--pacticipant "$PACTICIPANT" \
|
|
207
|
+
--version="$GITHUB_SHA" \
|
|
208
|
+
--pacticipant "$PROVIDER_PACTICIPANT" \
|
|
209
|
+
--branch="$PACT_PROVIDER_BRANCH" \
|
|
210
|
+
--retry-while-unknown=10 \
|
|
211
|
+
--retry-interval=30
|
|
212
|
+
else
|
|
213
|
+
pact-broker can-i-deploy \
|
|
214
|
+
--pacticipant "$PACTICIPANT" \
|
|
215
|
+
--version="$GITHUB_SHA" \
|
|
216
|
+
--to-environment "$ENVIRONMENT" \
|
|
217
|
+
--retry-while-unknown=10 \
|
|
218
|
+
--retry-interval=30
|
|
219
|
+
fi
|
|
194
220
|
```
|
|
195
221
|
|
|
196
222
|
#### `scripts/record-deployment.sh` — Record Deployment
|
|
@@ -224,6 +250,9 @@ fi
|
|
|
224
250
|
- Use `pact-broker` directly, NOT `npx pact-broker`
|
|
225
251
|
- Use `PACTICIPANT` env var (required via `${PACTICIPANT:?...}`), not hardcoded service names
|
|
226
252
|
- `can-i-deploy` includes `--retry-while-unknown=10 --retry-interval=30` (waits for provider verification)
|
|
253
|
+
- A PR-only `PACT_PROVIDER_BRANCH` override is additive: the environment check
|
|
254
|
+
ignores only `PROVIDER_PACTICIPANT`, then a second check targets that
|
|
255
|
+
pacticipant's branch. `--branch` never silently replaces `--to-environment`.
|
|
227
256
|
- `record-deployment` has branch guard (only records on main/master)
|
|
228
257
|
- **`publish-pact.sh` normalizes interactions with `jq -S` + `sort_by(...)` before publishing** — ensures byte-stable payload to the broker regardless of generator ordering quirks.
|
|
229
258
|
- Do NOT invent custom env vars like `PACT_CONSUMER_VERSION` or `PACT_BREAKING_CHANGE` in scripts — those are handled by `env-setup.sh` and the CI detect-breaking-change action respectively
|
|
@@ -269,6 +298,9 @@ jobs:
|
|
|
269
298
|
- name: Detect Pact breaking change
|
|
270
299
|
uses: ./.github/actions/detect-breaking-change
|
|
271
300
|
|
|
301
|
+
- name: Detect Pact provider branch
|
|
302
|
+
uses: ./.github/actions/detect-provider-branch
|
|
303
|
+
|
|
272
304
|
- name: Install dependencies
|
|
273
305
|
run: npm ci
|
|
274
306
|
|
|
@@ -284,10 +316,15 @@ jobs:
|
|
|
284
316
|
# the provider's contract-test-provider.yml workflow.
|
|
285
317
|
# can-i-deploy retries while waiting for provider verification.
|
|
286
318
|
|
|
287
|
-
# (4) Check deployment safety
|
|
288
|
-
-
|
|
289
|
-
|
|
319
|
+
# (4) Check deployment safety.
|
|
320
|
+
# NOTE: First-time bootstrap: if no verified contract exists on the broker yet,
|
|
321
|
+
# gate this to main only: if: github.ref == 'refs/heads/main' && env.PACT_BREAKING_CHANGE != 'true'
|
|
322
|
+
# Once the first contract is published and verified on main, remove the main-only condition.
|
|
323
|
+
- name: Can I deploy consumer?
|
|
324
|
+
if: env.PACT_BREAKING_CHANGE != 'true'
|
|
290
325
|
run: npm run can:i:deploy:consumer
|
|
326
|
+
env:
|
|
327
|
+
PACT_PROVIDER_BRANCH: ${{ env.PACT_PROVIDER_BRANCH }}
|
|
291
328
|
|
|
292
329
|
# (5) Record deployment (main only)
|
|
293
330
|
- name: Record consumer deployment (main only)
|
|
@@ -300,8 +337,12 @@ jobs:
|
|
|
300
337
|
- **1:1 local/CI parity is a hard rule**: every CI step is `npm run <same-name-a-dev-uses>`. Never let CI invoke `vitest` or `pact-broker` directly — that divergence is how "works on my machine" slips in. Consumer tests, publish, can-i-deploy, and record-deployment are all the same commands a developer runs locally.
|
|
301
338
|
- **Workflow-level `env` block** for broker secrets and git vars — not per-step
|
|
302
339
|
- **`detect-breaking-change` step** runs before install to set `PACT_BREAKING_CHANGE` env var
|
|
340
|
+
- **`detect-provider-branch` step** runs on PR events before install and exports
|
|
341
|
+
a short-lived `PACT_PROVIDER_BRANCH` hint from the PR description
|
|
303
342
|
- **Step numbering skips (3)** — step 3 is the webhook-triggered provider verification (happens externally)
|
|
304
|
-
- **can-i-deploy condition**: `
|
|
343
|
+
- **can-i-deploy condition**: `env.PACT_BREAKING_CHANGE != 'true'` after the
|
|
344
|
+
first main contract has been published and verified. During one-time broker
|
|
345
|
+
bootstrap, gate it to main until that verification exists.
|
|
305
346
|
- **Comment on (4)**: "on PRs, local verification is the gate"
|
|
306
347
|
- **No upload-artifact step** — the broker is the source of truth for pact files
|
|
307
348
|
- **`dependabot[bot]` skip** on the job (contract tests don't run for dependency updates)
|
|
@@ -377,7 +418,38 @@ runs:
|
|
|
377
418
|
|
|
378
419
|
---
|
|
379
420
|
|
|
380
|
-
### Example 7:
|
|
421
|
+
### Example 7: Detect Provider Branch Composite Action
|
|
422
|
+
|
|
423
|
+
**Context**: A consumer PR needs verification against a provider branch that
|
|
424
|
+
has not merged or deployed yet. Add `Pact provider branch: <name>` to the PR
|
|
425
|
+
template and parse it only for `pull_request` events.
|
|
426
|
+
|
|
427
|
+
```yaml
|
|
428
|
+
name: 'Detect Pact Provider Branch'
|
|
429
|
+
description: 'Exports a PR-only provider branch override'
|
|
430
|
+
|
|
431
|
+
runs:
|
|
432
|
+
using: 'composite'
|
|
433
|
+
steps:
|
|
434
|
+
- name: Set PACT_PROVIDER_BRANCH from PR description
|
|
435
|
+
if: github.event_name == 'pull_request'
|
|
436
|
+
uses: actions/github-script@v7
|
|
437
|
+
with:
|
|
438
|
+
script: |
|
|
439
|
+
const body = context.payload.pull_request.body || '';
|
|
440
|
+
const match = body.match(
|
|
441
|
+
/^[^\S\r\n]*Pact provider branch:[^\S\r\n]*(\S+)[^\S\r\n]*$/im
|
|
442
|
+
);
|
|
443
|
+
core.exportVariable('PACT_PROVIDER_BRANCH', match?.[1] || '');
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
Do not read this field from the merged PR on push. It is a coordination hint for
|
|
447
|
+
one open PR, not durable deployment metadata. After merge, the normal
|
|
448
|
+
`--to-environment` gate must be authoritative again.
|
|
449
|
+
|
|
450
|
+
---
|
|
451
|
+
|
|
452
|
+
### Example 8: Consumer Test Using PactV4 Builder
|
|
381
453
|
|
|
382
454
|
**Context**: Consumer pact test using PactV4 `addInteraction()` builder pattern. The test MUST call **real consumer code** (your actual API client/service functions) against the mock server — not raw `fetch()`. Using `fetch()` directly defeats the purpose of CDC testing because it doesn't verify your actual consumer code works with the contract.
|
|
383
455
|
|
|
@@ -503,7 +575,7 @@ describe('Movies API Consumer Contract', () => {
|
|
|
503
575
|
|
|
504
576
|
---
|
|
505
577
|
|
|
506
|
-
### Example
|
|
578
|
+
### Example 9: Support Files
|
|
507
579
|
|
|
508
580
|
#### Pact Config Factory
|
|
509
581
|
|
|
@@ -608,7 +680,7 @@ export const setJsonBody = (body: unknown) => setJsonContent({ body });
|
|
|
608
680
|
|
|
609
681
|
---
|
|
610
682
|
|
|
611
|
-
### Example
|
|
683
|
+
### Example 10: .gitignore Entries
|
|
612
684
|
|
|
613
685
|
**Context**: Pact-specific entries to add to `.gitignore`.
|
|
614
686
|
|
|
@@ -620,7 +692,7 @@ pact-logs/
|
|
|
620
692
|
|
|
621
693
|
---
|
|
622
694
|
|
|
623
|
-
### Example
|
|
695
|
+
### Example 11: Test File Organization — One File Per Consumer+Provider Pair
|
|
624
696
|
|
|
625
697
|
**Context**: Avoiding Pact Rust FFI handle collisions when structuring consumer test files.
|
|
626
698
|
|
|
@@ -664,7 +736,7 @@ describe('Movies API', () => {
|
|
|
664
736
|
Before presenting the consumer CDC framework to the user, verify:
|
|
665
737
|
|
|
666
738
|
- [ ] `vitest.config.pact.ts` is minimal **and sets `fileParallelism: false` AND `pool: 'forks'` with `poolOptions.forks.singleFork: true`** (`fileParallelism: false` prevents shared pact JSON corruption from parallel workers; forks + `singleFork: true` is required for pact JSON write safety across files — see Example 2 Key Points for mechanism and evidence)
|
|
667
|
-
- [ ] Each consumer+provider pair is covered by exactly ONE `.pacttest.ts` file — never split interactions for the same pair across multiple files (two `PactV4` instances for the same pair in one process cause FFI handle collision → "request was expected but not received" on Linux CI; `singleFork: true` does NOT prevent this — it ensures both files share one process, which guarantees the collision; see Example
|
|
739
|
+
- [ ] Each consumer+provider pair is covered by exactly ONE `.pacttest.ts` file — never split interactions for the same pair across multiple files (two `PactV4` instances for the same pair in one process cause FFI handle collision → "request was expected but not received" on Linux CI; `singleFork: true` does NOT prevent this — it ensures both files share one process, which guarantees the collision; see Example 11)
|
|
668
740
|
- [ ] `vitest.config.pact.ts` does NOT set `sequence.concurrent: true`, `maxConcurrency > 1`, `maxWorkers > 1`, or `isolate: false` — all four defeat the serialization the rule relies on
|
|
669
741
|
- [ ] `scripts/publish-pact.sh` normalizes interactions with `jq -S '.interactions |= sort_by(.description, (.providerStates[0].name // ""), .request.method, .request.path)'` before the `pact-broker publish` call (ensures byte-stable payload to PactFlow regardless of generator ordering)
|
|
670
742
|
- [ ] Script names match pactjs-utils (`test:pact:consumer`, `publish:pact`, `can:i:deploy:consumer`, `record:consumer:deployment`)
|
|
@@ -672,6 +744,9 @@ Before presenting the consumer CDC framework to the user, verify:
|
|
|
672
744
|
- [ ] Shell scripts use `pact-broker` not `npx pact-broker`
|
|
673
745
|
- [ ] Shell scripts use `PACTICIPANT` env var pattern
|
|
674
746
|
- [ ] `can-i-deploy.sh` has `--retry-while-unknown=10 --retry-interval=30`
|
|
747
|
+
- [ ] `can-i-deploy.sh` keeps the environment-wide check and, only when both
|
|
748
|
+
`PACT_PROVIDER_BRANCH` and `PROVIDER_PACTICIPANT` exist, ignores that one
|
|
749
|
+
provider there and checks its branch in a second failing command
|
|
675
750
|
- [ ] `record-deployment.sh` has branch guard
|
|
676
751
|
- [ ] `env-setup.sh` uses `set -eu`; broker scripts use `set -euo pipefail` — each with explanatory comment
|
|
677
752
|
- [ ] CI workflow named `contract-test-consumer.yml`
|
|
@@ -683,6 +758,8 @@ Before presenting the consumer CDC framework to the user, verify:
|
|
|
683
758
|
- [ ] CI can-i-deploy has `PACT_BREAKING_CHANGE != 'true'` condition
|
|
684
759
|
- [ ] CI has NO upload-artifact step
|
|
685
760
|
- [ ] `.github/actions/detect-breaking-change/action.yml` exists
|
|
761
|
+
- [ ] `.github/actions/detect-provider-branch/action.yml` reads `Pact provider
|
|
762
|
+
branch: <name>` on PR events only
|
|
686
763
|
- [ ] Consumer tests use `.pacttest.ts` extension
|
|
687
764
|
- [ ] Consumer tests use PactV4 `addInteraction()` builder
|
|
688
765
|
- [ ] `uponReceiving()` names follow `"a request to <action> <resource> [<condition>]"` pattern and are unique within the consumer-provider pair
|
|
@@ -58,19 +58,20 @@ These are correctness rules from the per-utility fragments, not style preference
|
|
|
58
58
|
|
|
59
59
|
## Substitution Table
|
|
60
60
|
|
|
61
|
-
| Need | Raw Pact — do not emit
|
|
62
|
-
| -------------------------------------------- |
|
|
63
|
-
| Provider state on an interaction | `.given('name', { id: 1 } as JsonMap)`, hand-cast params
|
|
64
|
-
| Coercing params to Pact's `JsonMap` | Manual casts, `String(date)`, `null` handling per call site
|
|
65
|
-
| PactV4 request/response builder callbacks | Repeated inline `(b) => { b.query(...); b.headers(...); b.jsonBody(...) }` lambdas
|
|
66
|
-
| HTTP provider verification options | A hand-assembled 30-line `VerifierOptions` object
|
|
67
|
-
| Message/Kafka provider verification options | A second hand-assembled options object
|
|
68
|
-
| Broker URL and consumer version selectors | Hand-written env
|
|
69
|
-
| Provider version tags in CI | Hand-written branch/tag extraction per CI platform
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
|
|
|
73
|
-
|
|
|
61
|
+
| Need | Raw Pact — do not emit | pactjs-utils — emit this | Level | Fragment |
|
|
62
|
+
| -------------------------------------------- | ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- | ----------- | ----------------------------------- |
|
|
63
|
+
| Provider state on an interaction | `.given('name', { id: 1 } as JsonMap)`, hand-cast params | `.given(...createProviderState({ name, params }))` | REQUIRED | `pactjs-utils-consumer-helpers.md` |
|
|
64
|
+
| Coercing params to Pact's `JsonMap` | Manual casts, `String(date)`, `null` handling per call site | `toJsonMap(value)` | REQUIRED | `pactjs-utils-consumer-helpers.md` |
|
|
65
|
+
| PactV4 request/response builder callbacks | Repeated inline `(b) => { b.query(...); b.headers(...); b.jsonBody(...) }` lambdas | `setJsonContent({ query?, headers?, body? })`, or `setJsonBody(body)` for body-only | REQUIRED | `pactjs-utils-consumer-helpers.md` |
|
|
66
|
+
| HTTP provider verification options | A hand-assembled 30-line `VerifierOptions` object | `buildVerifierOptions({ provider, port, includeMainAndDeployed, stateHandlers })` | REQUIRED | `pactjs-utils-provider-verifier.md` |
|
|
67
|
+
| Message/Kafka provider verification options | A second hand-assembled options object | `buildMessageVerifierOptions({ ... })` | REQUIRED | `pactjs-utils-provider-verifier.md` |
|
|
68
|
+
| Broker URL and consumer version selectors | Hand-written env branching for local, remote, breaking-change, or named-branch flows | `handlePactBrokerUrlAndSelectors(...)` (or let `buildVerifierOptions` read the env) | REQUIRED | `pactjs-utils-provider-verifier.md` |
|
|
69
|
+
| Provider version tags in CI | Hand-written branch/tag extraction per CI platform | `getProviderVersionTags()` | REQUIRED | `pactjs-utils-provider-verifier.md` |
|
|
70
|
+
| Breaking-change tolerant branch check | Repeated `main` / `master` / `release/` string logic | `isBreakingChangeTolerantBranch(branch)` | REQUIRED | `pactjs-utils-provider-verifier.md` |
|
|
71
|
+
| Auth injection during provider verification | A bespoke Express middleware, with its recurring `Bearer Bearer` bug | `createRequestFilter({ tokenGenerator })` | REQUIRED | `pactjs-utils-request-filter.md` |
|
|
72
|
+
| A provider that needs no auth injection | Omitting `requestFilter`, or an empty inline function | `noOpRequestFilter` | REQUIRED | `pactjs-utils-request-filter.md` |
|
|
73
|
+
| Response matchers where a Zod schema exists | Hand-written `MatchersV3` trees duplicating the schema | `zodToPactMatchers(schema, examples?)` | RECOMMENDED | `pactjs-utils-zod-to-pact.md` |
|
|
74
|
+
| Exercising the consumer inside `executeTest` | Raw `fetch(`${mockServer.url}/...`)` | Inject `mockServer.url` as `baseUrl` and call the real client | RECOMMENDED | `pact-consumer-di.md` |
|
|
74
75
|
|
|
75
76
|
`zodToPactMatchers` is RECOMMENDED because it needs a Zod schema to exist. Where the project has one, derive the matchers from it rather than maintaining a parallel matcher tree. Where it does not, write matchers from provider scrutiny and say so.
|
|
76
77
|
|
|
@@ -82,6 +83,11 @@ When this mandate is active, these are defects in generated or reviewed code:
|
|
|
82
83
|
|
|
83
84
|
- `.given('state name', someObject as JsonMap)` — a hand-cast provider state where `createProviderState` applies.
|
|
84
85
|
- A literal `VerifierOptions` object passed to `new Verifier(...)`, where `buildVerifierOptions` applies.
|
|
86
|
+
- Hand-built `{ branch: process.env.PACT_CONSUMER_BRANCH }` selectors where the
|
|
87
|
+
builder's scoped `consumer` + `consumerBranch` inputs apply.
|
|
88
|
+
- Hand-written `branch === 'main' || branch === 'master' ||
|
|
89
|
+
branch.startsWith('release/')` checks where
|
|
90
|
+
`isBreakingChangeTolerantBranch` applies.
|
|
85
91
|
- A bespoke `requestFilter` middleware that prefixes a bearer token by hand.
|
|
86
92
|
- Repeated inline PactV4 builder lambdas that `setJsonContent` or `setJsonBody` would replace.
|
|
87
93
|
- Raw `fetch` inside `executeTest` in a project whose consumer client is importable, with no note saying why.
|
|
@@ -159,6 +165,8 @@ await new Verifier(
|
|
|
159
165
|
provider: 'SampleMoviesAPI',
|
|
160
166
|
port: '3001',
|
|
161
167
|
includeMainAndDeployed: process.env.PACT_BREAKING_CHANGE !== 'true',
|
|
168
|
+
consumer: 'movie-web',
|
|
169
|
+
consumerBranch: process.env.PACT_CONSUMER_BRANCH,
|
|
162
170
|
stateHandlers,
|
|
163
171
|
requestFilter: createRequestFilter({ tokenGenerator: () => process.env.TEST_AUTH_TOKEN ?? 'test-token' }),
|
|
164
172
|
}),
|
|
@@ -179,6 +187,13 @@ Any `yes` is a blocker.
|
|
|
179
187
|
6. Does `executeTest` call raw `fetch` while the consumer client is importable, with no stated reason?
|
|
180
188
|
7. Is any response matcher derived from consumer-side types rather than provider source, OpenAPI, or broker data?
|
|
181
189
|
8. Is any interaction missing its `// Provider endpoint:` comment?
|
|
190
|
+
9. Does provider verification hand-build an explicit consumer branch selector,
|
|
191
|
+
or set `consumerBranch` without a scoped `consumer`?
|
|
192
|
+
10. Does a breaking-change catch hand-roll tolerant branch classification
|
|
193
|
+
instead of `isBreakingChangeTolerantBranch`, or check breaking-change
|
|
194
|
+
tolerance before rejecting a "no pacts found" result that occurred with
|
|
195
|
+
an explicit `PACT_CONSUMER_BRANCH` set (letting a typo masquerade as a
|
|
196
|
+
tolerated breaking change)?
|
|
182
197
|
|
|
183
198
|
Fix, or record a deviation. Do not emit unresolved.
|
|
184
199
|
|