autotel-pact 0.2.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/README.md +385 -0
- package/dist/audit.cjs +412 -0
- package/dist/audit.cjs.map +1 -0
- package/dist/audit.d.cts +25 -0
- package/dist/audit.d.ts +25 -0
- package/dist/audit.js +403 -0
- package/dist/audit.js.map +1 -0
- package/dist/auto-wrap.cjs +255 -0
- package/dist/auto-wrap.cjs.map +1 -0
- package/dist/auto-wrap.d.cts +57 -0
- package/dist/auto-wrap.d.ts +57 -0
- package/dist/auto-wrap.js +248 -0
- package/dist/auto-wrap.js.map +1 -0
- package/dist/broker.cjs +84 -0
- package/dist/broker.cjs.map +1 -0
- package/dist/broker.d.cts +23 -0
- package/dist/broker.d.ts +23 -0
- package/dist/broker.js +80 -0
- package/dist/broker.js.map +1 -0
- package/dist/cli.cjs +662 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +4 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.js +656 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +967 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +200 -0
- package/dist/index.d.ts +200 -0
- package/dist/index.js +932 -0
- package/dist/index.js.map +1 -0
- package/dist/ledger-BuBmfWNc.d.ts +22 -0
- package/dist/ledger-D88TzN1c.d.cts +22 -0
- package/dist/processor.cjs +200 -0
- package/dist/processor.cjs.map +1 -0
- package/dist/processor.d.cts +58 -0
- package/dist/processor.d.ts +58 -0
- package/dist/processor.js +193 -0
- package/dist/processor.js.map +1 -0
- package/dist/provider.cjs +219 -0
- package/dist/provider.cjs.map +1 -0
- package/dist/provider.d.cts +41 -0
- package/dist/provider.d.ts +41 -0
- package/dist/provider.js +213 -0
- package/dist/provider.js.map +1 -0
- package/dist/tag.cjs +50 -0
- package/dist/tag.cjs.map +1 -0
- package/dist/tag.d.cts +9 -0
- package/dist/tag.d.ts +9 -0
- package/dist/tag.js +48 -0
- package/dist/tag.js.map +1 -0
- package/dist/types-BHGiwqcp.d.cts +157 -0
- package/dist/types-BHGiwqcp.d.ts +157 -0
- package/package.json +108 -0
- package/schemas/README.md +24 -0
- package/schemas/audit-matrix-v0.2.0.json +78 -0
- package/schemas/ledger-entry-v0.2.0.json +77 -0
- package/src/attrs.test.ts +35 -0
- package/src/attrs.ts +53 -0
- package/src/audit.test.ts +189 -0
- package/src/audit.ts +251 -0
- package/src/auto-wrap.test.ts +149 -0
- package/src/auto-wrap.ts +283 -0
- package/src/broker.test.ts +175 -0
- package/src/broker.ts +118 -0
- package/src/cli.test.ts +148 -0
- package/src/cli.ts +287 -0
- package/src/index.ts +94 -0
- package/src/labels.ts +25 -0
- package/src/ledger-normalize.test.ts +141 -0
- package/src/ledger-normalize.ts +82 -0
- package/src/ledger.test.ts +92 -0
- package/src/ledger.ts +156 -0
- package/src/pact-file.test.ts +124 -0
- package/src/pact-file.ts +65 -0
- package/src/processor.test.ts +90 -0
- package/src/processor.ts +191 -0
- package/src/tag.test.ts +72 -0
- package/src/tag.ts +21 -0
- package/src/types.ts +169 -0
- package/src/wrapper-http.test.ts +133 -0
- package/src/wrapper-http.ts +194 -0
- package/src/wrapper-provider.test.ts +132 -0
- package/src/wrapper-provider.ts +163 -0
- package/src/wrapper.test.ts +176 -0
- package/src/wrapper.ts +221 -0
package/README.md
ADDED
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
# autotel-pact
|
|
2
|
+
|
|
3
|
+
> Evidence that your Pact contracts are alive. Audit which contracted interactions ran in the audit window, and which are stale confidence.
|
|
4
|
+
|
|
5
|
+
`autotel-pact` is the bridge between [Pact](https://docs.pact.io) and [autotel](https://github.com/jagreehal/autotel). It records, with every contract test run, which interactions were exercised, and produces an audit that answers a question Pact alone cannot:
|
|
6
|
+
|
|
7
|
+
> Of the contracts your test suite claims to verify, which ones were actually exercised in the last N days of test runs?
|
|
8
|
+
|
|
9
|
+
A green Pact suite is evidence of **compatibility**. An observed verified contract is evidence of **relevance**. Most teams have plenty of the former and little of the latter.
|
|
10
|
+
|
|
11
|
+
## Evidence quality
|
|
12
|
+
|
|
13
|
+
**We do not guess. We record evidence.**
|
|
14
|
+
|
|
15
|
+
Every feature states what was observed or verified. We never claim precision the data cannot support.
|
|
16
|
+
|
|
17
|
+
| Evidence | What it means | What you must configure |
|
|
18
|
+
|----------|---------------|-------------------------|
|
|
19
|
+
| **Seen in test** | Consumer exercised the interaction in CI (`source=test`, `role=consumer`) | `withPactInteraction` or `auto-wrap` |
|
|
20
|
+
| **Seen in production** | A span tagged with `pact.*` was recorded at runtime | `tagPactInteraction()` + `PactLedgerSpanProcessor` |
|
|
21
|
+
| **Provider verified** | Provider `verifyProvider()` succeeded and we enumerated interactions from the pact file | `withProviderVerification` |
|
|
22
|
+
| **Broker verified** | Latest broker verification for the consumer–provider **pact pair** succeeded | Broker URL/token at **audit** time |
|
|
23
|
+
|
|
24
|
+
> **Broker limitation:** Broker verification proves the latest pact between a consumer and provider passed. It does **not** prove autotel-pact observed each interaction.
|
|
25
|
+
|
|
26
|
+
## When to use it
|
|
27
|
+
|
|
28
|
+
You should reach for autotel-pact when:
|
|
29
|
+
|
|
30
|
+
- You use **Pact** (HTTP or Message) contracts and want evidence each interaction actually fires in your test suite.
|
|
31
|
+
- Your contract suite is large and you suspect some contracts are obsolete or unexercised, but you can't prove it.
|
|
32
|
+
- You want a CI gate that fails when a "verified" contract has not been exercised recently.
|
|
33
|
+
|
|
34
|
+
v0.2 supports **consumer** wrappers (message + HTTP), **provider** verification, optional **Pact Broker** enrichment at audit time, and **production** observation via a span processor.
|
|
35
|
+
|
|
36
|
+
## What this package does NOT do
|
|
37
|
+
|
|
38
|
+
- **Does not replace Pact.** Pact still owns matching and `can-i-deploy`. We add complementary evidence.
|
|
39
|
+
- **Does not infer interactions from routes.** Production observation requires explicit `pact.*` span tags.
|
|
40
|
+
- **Does not write or modify pact files.** We only record that interactions ran or were verified.
|
|
41
|
+
- **Does not claim per-interaction broker proof.** Broker results are pact-pair level (see warning above).
|
|
42
|
+
- **Does not record request/response bodies** in the ledger. Metadata only (consumer, provider, description, states, trace ids).
|
|
43
|
+
|
|
44
|
+
## Install
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pnpm add -D autotel-pact
|
|
48
|
+
# autotel and @pact-foundation/pact are peer dependencies
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Run the demo
|
|
52
|
+
|
|
53
|
+
A working end-to-end example lives at [`apps/example-contract-testing`](../../apps/example-contract-testing). One `pnpm start` walks through every v0.2 evidence path (TEST_SEEN, STALE, SHADOW, PROVIDER_VERIFIED, PROD_SEEN) and asserts the resulting audit matrix:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pnpm --filter @jagreehal/example-contract-testing start
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## The runtime wrapper
|
|
60
|
+
|
|
61
|
+
### Message Pact
|
|
62
|
+
|
|
63
|
+
Wrap your existing `MessageConsumerPact.verify()` call:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { MessageConsumerPact } from '@pact-foundation/pact';
|
|
67
|
+
import { withPactInteraction } from 'autotel-pact';
|
|
68
|
+
import { orderHandler } from './handlers/order';
|
|
69
|
+
|
|
70
|
+
const pact = new MessageConsumerPact({
|
|
71
|
+
consumer: 'OrderShipper',
|
|
72
|
+
provider: 'OrderService',
|
|
73
|
+
dir: './pacts',
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
pact
|
|
77
|
+
.given('an order has been created')
|
|
78
|
+
.expectsToReceive('an OrderCreated event')
|
|
79
|
+
.withContent({ orderId: 'ord-123', total: 99.5 });
|
|
80
|
+
|
|
81
|
+
await withPactInteraction(pact, (message) => orderHandler(message.contents));
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### HTTP Pact
|
|
85
|
+
|
|
86
|
+
Same idea, mirrored onto `PactV3.executeTest()`:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
import { PactV3 } from '@pact-foundation/pact';
|
|
90
|
+
import { withHttpPactInteraction } from 'autotel-pact';
|
|
91
|
+
import { API } from './api';
|
|
92
|
+
|
|
93
|
+
const provider = new PactV3({
|
|
94
|
+
consumer: 'Web',
|
|
95
|
+
provider: 'Catalog',
|
|
96
|
+
dir: './pacts',
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('gets all products', async () => {
|
|
100
|
+
await withHttpPactInteraction(
|
|
101
|
+
provider,
|
|
102
|
+
{
|
|
103
|
+
states: [{ description: 'products exist' }],
|
|
104
|
+
uponReceiving: 'get all products',
|
|
105
|
+
withRequest: { method: 'GET', path: '/products' },
|
|
106
|
+
willRespondWith: { status: 200, body: [/* ... */] },
|
|
107
|
+
},
|
|
108
|
+
async (mockServer) => {
|
|
109
|
+
const api = new API(mockServer.url);
|
|
110
|
+
expect(await api.getAllProducts()).toEqual(/* ... */);
|
|
111
|
+
},
|
|
112
|
+
);
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Both wrappers:
|
|
117
|
+
|
|
118
|
+
1. Open an autotel span named `pact.interaction` with `pact.consumer`, `pact.provider`, `pact.interaction.description`, `pact.interaction.states`, `pact.kind`, and `pact.outcome` attributes.
|
|
119
|
+
2. Run the underlying Pact verification (`verify()` / `executeTest()`).
|
|
120
|
+
3. Append a ledger entry recording that the interaction was exercised.
|
|
121
|
+
|
|
122
|
+
If the handler / test throws, the span outcome is `failed` and the ledger entry records the error.
|
|
123
|
+
|
|
124
|
+
### Stable interaction IDs (optional but recommended)
|
|
125
|
+
|
|
126
|
+
The audit keys on the `expectsToReceive` / `uponReceiving` text by default. Renaming that text creates a new audit row and leaves the old one STALE. Pass `interactionId` to give the interaction a name that survives prose changes:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
await withPactInteraction(pact, handler, {
|
|
130
|
+
interactionId: 'order.created.v1',
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Convention: `domain.event.vN`. The audit matches on `interactionId` first, description second.
|
|
135
|
+
|
|
136
|
+
### Auto-wrap (zero-touch adoption)
|
|
137
|
+
|
|
138
|
+
If you don't want to edit every contract test, add a single line to your vitest / jest setup:
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
// vitest.config.ts
|
|
142
|
+
import { defineConfig } from 'vitest/config';
|
|
143
|
+
export default defineConfig({
|
|
144
|
+
test: { setupFiles: ['autotel-pact/auto-wrap'] },
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
That import monkey-patches `MessageConsumerPact.prototype.verify` and `PactV3.prototype.executeTest` so every contract test in the project records a ledger entry. No code changes per test. Configure with `AUTOTEL_PACT_RUN_ID` and `AUTOTEL_PACT_LEDGER_DIR` env vars. Idempotent; safe no-op when Pact-JS isn't installed.
|
|
149
|
+
|
|
150
|
+
Trade-off: auto-wrap is global, so all your contract tests must share one ledger directory and one run id. If you need per-test customisation (custom `interactionId`, `contractFile`, `runId`), use the explicit wrappers above.
|
|
151
|
+
|
|
152
|
+
## The ledger
|
|
153
|
+
|
|
154
|
+
Each `withPactInteraction` call appends one line of JSON to a ledger file:
|
|
155
|
+
|
|
156
|
+
```text
|
|
157
|
+
.autotel-pact/ledger-${runId}.jsonl
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Where `runId` comes from `AUTOTEL_PACT_RUN_ID` (set this in CI), falling back to a local timestamp. One file per run avoids append races when tests run in parallel. Upload the directory as a CI artifact for cross-run audits.
|
|
161
|
+
|
|
162
|
+
A ledger entry looks like:
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"type": "interaction",
|
|
167
|
+
"spec": "autotel-pact-ledger-entry/v0.2.0",
|
|
168
|
+
"consumer": "OrderShipper",
|
|
169
|
+
"provider": "OrderService",
|
|
170
|
+
"interaction": "an OrderCreated event",
|
|
171
|
+
"interaction_id": "order.created.v1",
|
|
172
|
+
"states": ["an order has been created"],
|
|
173
|
+
"kind": "message",
|
|
174
|
+
"outcome": "passed",
|
|
175
|
+
"source": "test",
|
|
176
|
+
"role": "consumer",
|
|
177
|
+
"duration_ms": 3.3,
|
|
178
|
+
"observed_at": "2026-05-28T18:09:13.728Z",
|
|
179
|
+
"trace_id": "abc...",
|
|
180
|
+
"span_id": "def...",
|
|
181
|
+
"run_id": "ci-build-1234",
|
|
182
|
+
"git_sha": "..."
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
The full ledger and audit-matrix shapes are published as JSON Schema files under [`schemas/`](./schemas/). Pin to a specific schema version if you build dashboards or downstream tooling against the output. The `spec` field on every artifact is your gate against unknown major versions.
|
|
187
|
+
|
|
188
|
+
## Provider verification (v0.2)
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
import { Verifier } from '@pact-foundation/pact';
|
|
192
|
+
import { withProviderVerification } from 'autotel-pact/provider';
|
|
193
|
+
|
|
194
|
+
await withProviderVerification({
|
|
195
|
+
provider: 'OrderService',
|
|
196
|
+
providerBaseUrl: 'http://localhost:8080',
|
|
197
|
+
pactUrls: ['./pacts/OrderShipper-OrderService.json'],
|
|
198
|
+
});
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
On **success**, one ledger row per interaction in the verified pact file (`role=provider`). On **failure**, a single `provider_verification_run` row, with no per-interaction `provider_verified` flags (we cannot prove which interaction failed).
|
|
202
|
+
|
|
203
|
+
### Skipping the Verifier (demos, smoke tests)
|
|
204
|
+
|
|
205
|
+
Pass `skipVerifier: true` to emit the same per-interaction rows without loading or calling the Pact Verifier:
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
await withProviderVerification(
|
|
209
|
+
{ provider: 'OrderService', providerBaseUrl: 'unused', pactUrls: [pactPath] },
|
|
210
|
+
{ skipVerifier: true },
|
|
211
|
+
);
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
The wrapper parses the pact files, fans out one ledger row per interaction with `role: 'provider'` and `outcome: 'passed'`, and skips the dynamic import of `@pact-foundation/pact`. Use for demos, audit-pipeline smoke tests, and example apps. Not for production CI: it marks every interaction in the pact file as verified without checking anything.
|
|
215
|
+
|
|
216
|
+
## Production observation (v0.2)
|
|
217
|
+
|
|
218
|
+
Tag business spans, then register the processor:
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
import { init } from 'autotel';
|
|
222
|
+
import { trace } from 'autotel';
|
|
223
|
+
import { createPactLedgerProcessor } from 'autotel-pact/processor';
|
|
224
|
+
import { tagPactInteraction } from 'autotel-pact/tag';
|
|
225
|
+
|
|
226
|
+
init({
|
|
227
|
+
service: 'order-service',
|
|
228
|
+
spanProcessors: [
|
|
229
|
+
createPactLedgerProcessor({ dir: '.autotel-pact-prod' }),
|
|
230
|
+
],
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
trace('handleOrderCreated', (ctx) => {
|
|
234
|
+
tagPactInteraction({
|
|
235
|
+
consumer: 'OrderShipper',
|
|
236
|
+
provider: 'OrderService',
|
|
237
|
+
description: 'an OrderCreated event',
|
|
238
|
+
states: [],
|
|
239
|
+
kind: 'message',
|
|
240
|
+
interactionId: 'order.created.v1',
|
|
241
|
+
});
|
|
242
|
+
// ...
|
|
243
|
+
});
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
**Safeguards:**
|
|
247
|
+
|
|
248
|
+
- Bounded queue (default 1024). When full, the processor drops the oldest queued entry so newest evidence survives. A throttled warning fires on each drop wave.
|
|
249
|
+
- Producer backpressure on direct `appendLedgerEntryAsync` callers. The internal write chain is capped at 4096 pending writes. Past that cap, callers await drainage before queueing, so memory cannot grow unbounded.
|
|
250
|
+
- Fail-open writes. A ledger I/O error never breaks the app: the error is reported via `onWriteError` and the span continues.
|
|
251
|
+
- Sampling caveat: if OTel sampling drops a span, the corresponding production observation is missed.
|
|
252
|
+
|
|
253
|
+
Use a **separate ledger directory** for production vs CI.
|
|
254
|
+
|
|
255
|
+
## Pact Broker at audit time (v0.2)
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
npx autotel-pact audit \
|
|
259
|
+
--broker-url https://your-broker.example \
|
|
260
|
+
--broker-token "$PACT_BROKER_TOKEN"
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Works with self-hosted Pact Broker and PactFlow (same HTTP API; bearer token from PactFlow settings). Sets **Broker verified** on all contracted rows for a consumer–provider pair when the latest broker verification succeeded.
|
|
264
|
+
|
|
265
|
+
## The audit CLI
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
npx autotel-pact audit
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Columns (human labels):
|
|
272
|
+
|
|
273
|
+
```text
|
|
274
|
+
STATUS CONTRACTED TEST_SEEN PROD_SEEN PROVIDER_VERIFIED BROKER_VERIFIED CONSUMER → PROVIDER …
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
- **OK**: contracted and **seen in test**
|
|
278
|
+
- **STALE**: contracted but not **seen in test**
|
|
279
|
+
- **SHADOW**: seen (test or production) but not contracted
|
|
280
|
+
|
|
281
|
+
### Status meanings
|
|
282
|
+
|
|
283
|
+
| Status | Contracted in `pacts/` | In ledger window | Meaning |
|
|
284
|
+
|--------|------------------------|------------------|-----------------------------------------------|
|
|
285
|
+
| OK | yes | yes | Trusted path, exercised this window |
|
|
286
|
+
| STALE | yes | no | Pact exists but no test exercised it |
|
|
287
|
+
| SHADOW | no | yes | A wrapped call ran with no matching contract |
|
|
288
|
+
|
|
289
|
+
### Flags
|
|
290
|
+
|
|
291
|
+
| Flag | Default | Description |
|
|
292
|
+
|---------------------|----------------|----------------------------------------------------------------------------|
|
|
293
|
+
| `--pacts <dir>` | `./pacts` | Directory containing pact files |
|
|
294
|
+
| `--ledger <dir>` | `.autotel-pact`| Directory containing ledger files |
|
|
295
|
+
| `--window <days>` | `14` | Ledger lookback window |
|
|
296
|
+
| `--gate` | off | Exit 1 if any contracted interaction was not **seen in test** |
|
|
297
|
+
| `--gate=strict` | off | Also exit 1 on observations with no matching contract |
|
|
298
|
+
| `--gate=broker` | off | Exit 1 if broker configured and any contracted row lacks broker proof |
|
|
299
|
+
| `--broker-url` | env | Pact Broker base URL (`PACT_BROKER_BASE_URL`) |
|
|
300
|
+
| `--broker-token` | env | Bearer token (`PACT_BROKER_TOKEN`) |
|
|
301
|
+
| `--json` | off | Machine-readable JSON (`test_seen`, `prod_seen`, …) |
|
|
302
|
+
| `--help` | | Show help |
|
|
303
|
+
|
|
304
|
+
### CI gating
|
|
305
|
+
|
|
306
|
+
```yaml
|
|
307
|
+
# .github/workflows/test.yml
|
|
308
|
+
- run: pnpm test:pact
|
|
309
|
+
env:
|
|
310
|
+
AUTOTEL_PACT_RUN_ID: ${{ github.run_id }}
|
|
311
|
+
- uses: actions/upload-artifact@v4
|
|
312
|
+
with:
|
|
313
|
+
name: pact-ledger
|
|
314
|
+
path: .autotel-pact/
|
|
315
|
+
- run: npx autotel-pact audit --gate
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## Operating guide
|
|
319
|
+
|
|
320
|
+
A few things to know before you turn `--gate` on for the whole org.
|
|
321
|
+
|
|
322
|
+
### STALE means "not seen in test"
|
|
323
|
+
|
|
324
|
+
A STALE row does **not** mean "never fires in production". It means no consumer test exercised this interaction in the window. Check **PROD_SEEN** for production evidence (requires explicit span tags + processor).
|
|
325
|
+
|
|
326
|
+
Honest one-liner: *"verified by Pact, exercised by our tests, observed in production only when we tag spans."*
|
|
327
|
+
|
|
328
|
+
### Coverage equals wrapped tests
|
|
329
|
+
|
|
330
|
+
The audit only sees interactions that went through `withPactInteraction`. If half your Pact-Message suite still calls `pact.verify()`, those interactions won't reach the ledger and you'll get **false STALE** rows.
|
|
331
|
+
|
|
332
|
+
Migration checklist for an existing Pact suite:
|
|
333
|
+
|
|
334
|
+
1. Roll the wrapper out in one team first; don't `--gate` until coverage is high.
|
|
335
|
+
2. Add a lint rule or grep-based CI check (`! grep -r 'pact.verify(' --include='*.ts' src/`) to prevent regressions.
|
|
336
|
+
3. When `pnpm autotel-pact audit` reports 0 SHADOW rows from your real tests (only from injected demo rows), coverage is complete enough to turn the gate on.
|
|
337
|
+
|
|
338
|
+
### Pick the audit window to match your test cadence
|
|
339
|
+
|
|
340
|
+
The `--window` flag defaults to 14 days. If important interactions only run in nightly or weekly jobs, set the window wide enough to cover them, or `--gate` will flake. A safe rule: `--window` should be at least 2× the slowest test cadence that contributes to coverage.
|
|
341
|
+
|
|
342
|
+
### Interaction renames create new audit rows
|
|
343
|
+
|
|
344
|
+
Interactions are keyed by `(consumer, provider, kind, description)`. The `description` is the literal string passed to `expectsToReceive()`. If you rename `"an OrderCreated event"` to `"OrderCreated"`, the audit sees a **new** row (and the old description goes STALE until the pact file is regenerated).
|
|
345
|
+
|
|
346
|
+
This is intentional: descriptions are the only stable identity Pact-JS exposes. Plan renames as two-step migrations: rename in code and delete the old pact entry, in one PR if possible. A future version may offer stable interaction IDs via metadata.
|
|
347
|
+
|
|
348
|
+
### STALE vs intentionally-disabled
|
|
349
|
+
|
|
350
|
+
A contract that should not run anymore is a contract you should delete, not gate around. STALE is a prompt to **investigate**, not a permanent state to tolerate.
|
|
351
|
+
|
|
352
|
+
## Attribute schema
|
|
353
|
+
|
|
354
|
+
| Attribute | Type | Description |
|
|
355
|
+
|---------------------------------|----------|---------------------------------------------------|
|
|
356
|
+
| `pact.consumer` | string | Consumer name from Pact config |
|
|
357
|
+
| `pact.provider` | string | Provider name from Pact config |
|
|
358
|
+
| `pact.kind` | string | `"message"` (v1) or `"http"` (v2) |
|
|
359
|
+
| `pact.interaction.description` | string | The `expectsToReceive` or `uponReceiving` text |
|
|
360
|
+
| `pact.interaction.states` | string[] | Provider state names from `.given()` |
|
|
361
|
+
| `pact.contract.file` | string? | Path to the pact file (when supplied) |
|
|
362
|
+
| `pact.outcome` | string | `"passed"` or `"failed"` |
|
|
363
|
+
|
|
364
|
+
The `pact.*` namespace is currently unclaimed in OTel semantic conventions; we plan to propose it upstream once the package has traction. Pin to a specific `autotel-pact` version if you build dashboards against these attributes.
|
|
365
|
+
|
|
366
|
+
## How this is different from…
|
|
367
|
+
|
|
368
|
+
- **Pact's own `--enable-otel`** flags emit telemetry about the *Pact tooling itself* (broker calls, verifier execution). `autotel-pact` operates at the **interaction wrapper layer in your code**, capturing whether the contract interactions ran.
|
|
369
|
+
- **Tracetest** lets you write tests that assert against traces. `autotel-pact` does not change how you write tests. It audits which of your existing Pact contracts have evidence of being exercised.
|
|
370
|
+
- **Pact Broker / PactFlow `can-i-deploy`** verifies version compatibility. `autotel-pact` adds the orthogonal question: have these verified contracts run recently?
|
|
371
|
+
|
|
372
|
+
## Related packages
|
|
373
|
+
|
|
374
|
+
- [`autotel`](../autotel): OpenTelemetry instrumentation for Node.js.
|
|
375
|
+
The substrate every span and ledger entry is built on top of.
|
|
376
|
+
|
|
377
|
+
## Roadmap
|
|
378
|
+
|
|
379
|
+
- **v0.1**: consumer wrappers, ledger, audit CLI, auto-wrap, interaction IDs, JSON schemas.
|
|
380
|
+
- **v0.2** (current): provider verification, broker enrichment at audit, production span processor, evidence columns (TEST_SEEN / PROD_SEEN / PROVIDER_VERIFIED / BROKER_VERIFIED).
|
|
381
|
+
- **v0.3+**: PactV4 interaction comments, per-interaction broker hooks if the API allows, publishing verification results to broker from autotel-pact.
|
|
382
|
+
|
|
383
|
+
## License
|
|
384
|
+
|
|
385
|
+
MIT
|