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
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kind of contract interaction observed.
|
|
3
|
+
*/
|
|
4
|
+
type PactKind = 'message' | 'http';
|
|
5
|
+
type PactOutcome = 'passed' | 'failed';
|
|
6
|
+
type LedgerSource = 'test' | 'production';
|
|
7
|
+
type LedgerRole = 'consumer' | 'provider';
|
|
8
|
+
declare const LEDGER_ENTRY_SPEC = "autotel-pact-ledger-entry/v0.2.0";
|
|
9
|
+
declare const AUDIT_MATRIX_SPEC = "autotel-pact-audit-matrix/v0.2.0";
|
|
10
|
+
/**
|
|
11
|
+
* Metadata about a single Pact interaction, derived from the reified message
|
|
12
|
+
* plus the consumer/provider config. Stamped onto the span and the ledger entry.
|
|
13
|
+
*/
|
|
14
|
+
interface PactInteractionMeta {
|
|
15
|
+
consumer: string;
|
|
16
|
+
provider: string;
|
|
17
|
+
description: string;
|
|
18
|
+
states: string[];
|
|
19
|
+
kind: PactKind;
|
|
20
|
+
interactionId?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Per-interaction ledger evidence (consumer exercise, provider verify, or production tag).
|
|
24
|
+
*/
|
|
25
|
+
interface InteractionLedgerEntry {
|
|
26
|
+
type?: 'interaction';
|
|
27
|
+
spec: typeof LEDGER_ENTRY_SPEC;
|
|
28
|
+
consumer: string;
|
|
29
|
+
provider: string;
|
|
30
|
+
interaction: string;
|
|
31
|
+
interaction_id?: string;
|
|
32
|
+
states: string[];
|
|
33
|
+
kind: PactKind;
|
|
34
|
+
outcome: PactOutcome;
|
|
35
|
+
source: LedgerSource;
|
|
36
|
+
role: LedgerRole;
|
|
37
|
+
duration_ms: number;
|
|
38
|
+
observed_at: string;
|
|
39
|
+
trace_id?: string;
|
|
40
|
+
span_id?: string;
|
|
41
|
+
run_id?: string;
|
|
42
|
+
git_sha?: string;
|
|
43
|
+
error?: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Run-level provider verification failure — does not imply per-interaction outcomes.
|
|
47
|
+
*/
|
|
48
|
+
interface ProviderVerificationRunEntry {
|
|
49
|
+
type: 'provider_verification_run';
|
|
50
|
+
spec: typeof LEDGER_ENTRY_SPEC;
|
|
51
|
+
consumer: string;
|
|
52
|
+
provider: string;
|
|
53
|
+
outcome: 'failed';
|
|
54
|
+
source: LedgerSource;
|
|
55
|
+
role: 'provider';
|
|
56
|
+
observed_at: string;
|
|
57
|
+
error: string;
|
|
58
|
+
run_id?: string;
|
|
59
|
+
git_sha?: string;
|
|
60
|
+
trace_id?: string;
|
|
61
|
+
span_id?: string;
|
|
62
|
+
}
|
|
63
|
+
type LedgerRecord = InteractionLedgerEntry | ProviderVerificationRunEntry;
|
|
64
|
+
declare function isInteractionLedgerEntry(entry: LedgerRecord): entry is InteractionLedgerEntry;
|
|
65
|
+
declare function isProviderVerificationRun(entry: LedgerRecord): entry is ProviderVerificationRunEntry;
|
|
66
|
+
/**
|
|
67
|
+
* Shape of a Pact contract file on disk (subset we read).
|
|
68
|
+
*/
|
|
69
|
+
interface PactFile {
|
|
70
|
+
consumer: {
|
|
71
|
+
name: string;
|
|
72
|
+
};
|
|
73
|
+
provider: {
|
|
74
|
+
name: string;
|
|
75
|
+
};
|
|
76
|
+
messages?: Array<{
|
|
77
|
+
description: string;
|
|
78
|
+
providerStates?: Array<{
|
|
79
|
+
name: string;
|
|
80
|
+
}>;
|
|
81
|
+
metadata?: Record<string, unknown>;
|
|
82
|
+
}>;
|
|
83
|
+
interactions?: Array<{
|
|
84
|
+
description: string;
|
|
85
|
+
providerStates?: Array<{
|
|
86
|
+
name: string;
|
|
87
|
+
}>;
|
|
88
|
+
metadata?: Record<string, unknown>;
|
|
89
|
+
}>;
|
|
90
|
+
}
|
|
91
|
+
interface BrokerVerification {
|
|
92
|
+
consumer: string;
|
|
93
|
+
provider: string;
|
|
94
|
+
success: boolean;
|
|
95
|
+
verifiedAt?: string;
|
|
96
|
+
/**
|
|
97
|
+
* Populated when the broker could not be reached or returned a non-2xx
|
|
98
|
+
* response. Distinguishes "broker said the pact is not verified" (no error)
|
|
99
|
+
* from "we could not determine verification status" (error set).
|
|
100
|
+
*/
|
|
101
|
+
error?: string;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* One row in the audit matrix.
|
|
105
|
+
*/
|
|
106
|
+
interface AuditRow {
|
|
107
|
+
consumer: string;
|
|
108
|
+
provider: string;
|
|
109
|
+
interaction: string;
|
|
110
|
+
interaction_id?: string;
|
|
111
|
+
kind: PactKind;
|
|
112
|
+
contracted: boolean;
|
|
113
|
+
/** Any interaction-level ledger hit in the window (test or production). */
|
|
114
|
+
observed: boolean;
|
|
115
|
+
test_seen: boolean;
|
|
116
|
+
prod_seen: boolean;
|
|
117
|
+
provider_verified: boolean;
|
|
118
|
+
broker_verified: boolean;
|
|
119
|
+
broker_verified_at?: string;
|
|
120
|
+
/** Set when the broker check failed (network error, non-2xx, parse error). */
|
|
121
|
+
broker_error?: string;
|
|
122
|
+
last_observed_at?: string;
|
|
123
|
+
last_outcome?: PactOutcome;
|
|
124
|
+
}
|
|
125
|
+
interface AuditMatrix {
|
|
126
|
+
spec: typeof AUDIT_MATRIX_SPEC;
|
|
127
|
+
rows: AuditRow[];
|
|
128
|
+
counts: {
|
|
129
|
+
total: number;
|
|
130
|
+
/** Any contracted row. */
|
|
131
|
+
contracted: number;
|
|
132
|
+
/** Any row with test_seen OR prod_seen. */
|
|
133
|
+
observed: number;
|
|
134
|
+
/** Contracted AND seen in a consumer test. */
|
|
135
|
+
contracted_and_test_seen: number;
|
|
136
|
+
/** Contracted but not seen in a consumer test (stale confidence). */
|
|
137
|
+
contracted_not_test_seen: number;
|
|
138
|
+
/** Seen (test or production) without a matching contract (ungoverned flow). */
|
|
139
|
+
test_or_prod_seen_not_contracted: number;
|
|
140
|
+
test_seen: number;
|
|
141
|
+
prod_seen: number;
|
|
142
|
+
provider_verified: number;
|
|
143
|
+
broker_verified: number;
|
|
144
|
+
};
|
|
145
|
+
window_days: number;
|
|
146
|
+
generated_at: string;
|
|
147
|
+
verification_failures?: ProviderVerificationRunEntry[];
|
|
148
|
+
}
|
|
149
|
+
interface PactInteractionKey {
|
|
150
|
+
consumer: string;
|
|
151
|
+
provider: string;
|
|
152
|
+
interaction: string;
|
|
153
|
+
kind: PactKind;
|
|
154
|
+
interactionId?: string;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export { AUDIT_MATRIX_SPEC as A, type BrokerVerification as B, type InteractionLedgerEntry as I, LEDGER_ENTRY_SPEC as L, type PactInteractionMeta as P, type PactOutcome as a, type PactFile as b, type PactInteractionKey as c, type AuditMatrix as d, type AuditRow as e, type LedgerRecord as f, type LedgerRole as g, type LedgerSource as h, type PactKind as i, type ProviderVerificationRunEntry as j, isInteractionLedgerEntry as k, isProviderVerificationRun as l };
|
package/package.json
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "autotel-pact",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Runtime evidence for Pact contracts — autotel bridge that records which contract interactions were actually exercised, and audits 'contracted but never observed'.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"sideEffects": [
|
|
9
|
+
"./dist/auto-wrap.js",
|
|
10
|
+
"./dist/auto-wrap.cjs"
|
|
11
|
+
],
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"require": "./dist/index.cjs"
|
|
17
|
+
},
|
|
18
|
+
"./audit": {
|
|
19
|
+
"types": "./dist/audit.d.ts",
|
|
20
|
+
"import": "./dist/audit.js",
|
|
21
|
+
"require": "./dist/audit.cjs"
|
|
22
|
+
},
|
|
23
|
+
"./auto-wrap": {
|
|
24
|
+
"types": "./dist/auto-wrap.d.ts",
|
|
25
|
+
"import": "./dist/auto-wrap.js",
|
|
26
|
+
"require": "./dist/auto-wrap.cjs"
|
|
27
|
+
},
|
|
28
|
+
"./provider": {
|
|
29
|
+
"types": "./dist/provider.d.ts",
|
|
30
|
+
"import": "./dist/provider.js",
|
|
31
|
+
"require": "./dist/provider.cjs"
|
|
32
|
+
},
|
|
33
|
+
"./broker": {
|
|
34
|
+
"types": "./dist/broker.d.ts",
|
|
35
|
+
"import": "./dist/broker.js",
|
|
36
|
+
"require": "./dist/broker.cjs"
|
|
37
|
+
},
|
|
38
|
+
"./processor": {
|
|
39
|
+
"types": "./dist/processor.d.ts",
|
|
40
|
+
"import": "./dist/processor.js",
|
|
41
|
+
"require": "./dist/processor.cjs"
|
|
42
|
+
},
|
|
43
|
+
"./tag": {
|
|
44
|
+
"types": "./dist/tag.d.ts",
|
|
45
|
+
"import": "./dist/tag.js",
|
|
46
|
+
"require": "./dist/tag.cjs"
|
|
47
|
+
},
|
|
48
|
+
"./schemas/*.json": "./schemas/*.json"
|
|
49
|
+
},
|
|
50
|
+
"bin": {
|
|
51
|
+
"autotel-pact": "./dist/cli.js"
|
|
52
|
+
},
|
|
53
|
+
"files": [
|
|
54
|
+
"dist",
|
|
55
|
+
"src",
|
|
56
|
+
"schemas",
|
|
57
|
+
"README.md"
|
|
58
|
+
],
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "tsup",
|
|
61
|
+
"dev": "tsup --watch",
|
|
62
|
+
"lint": "eslint src/**/*.ts",
|
|
63
|
+
"lint:fix": "eslint src/**/*.ts --fix",
|
|
64
|
+
"type-check": "tsc --noEmit",
|
|
65
|
+
"test": "vitest run",
|
|
66
|
+
"test:watch": "vitest",
|
|
67
|
+
"clean": "rimraf dist"
|
|
68
|
+
},
|
|
69
|
+
"keywords": [
|
|
70
|
+
"autotel",
|
|
71
|
+
"pact",
|
|
72
|
+
"contract-testing",
|
|
73
|
+
"opentelemetry",
|
|
74
|
+
"observability",
|
|
75
|
+
"runtime-evidence",
|
|
76
|
+
"pact-message",
|
|
77
|
+
"async"
|
|
78
|
+
],
|
|
79
|
+
"author": "Jag Reehal <jag@jagreehal.com> (https://jagreehal.com)",
|
|
80
|
+
"license": "MIT",
|
|
81
|
+
"peerDependencies": {
|
|
82
|
+
"@pact-foundation/pact": ">=16",
|
|
83
|
+
"autotel": "workspace:*"
|
|
84
|
+
},
|
|
85
|
+
"peerDependenciesMeta": {
|
|
86
|
+
"@pact-foundation/pact": {
|
|
87
|
+
"optional": false
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"devDependencies": {
|
|
91
|
+
"@pact-foundation/pact": "^16.5.0",
|
|
92
|
+
"@types/node": "^25.9.1",
|
|
93
|
+
"autotel": "workspace:*",
|
|
94
|
+
"rimraf": "^6.1.3",
|
|
95
|
+
"tsup": "^8.5.1",
|
|
96
|
+
"typescript": "^6.0.3",
|
|
97
|
+
"vitest": "^4.1.7"
|
|
98
|
+
},
|
|
99
|
+
"repository": {
|
|
100
|
+
"type": "git",
|
|
101
|
+
"url": "https://github.com/jagreehal/autotel",
|
|
102
|
+
"directory": "packages/autotel-pact"
|
|
103
|
+
},
|
|
104
|
+
"bugs": {
|
|
105
|
+
"url": "https://github.com/jagreehal/autotel/issues"
|
|
106
|
+
},
|
|
107
|
+
"homepage": "https://github.com/jagreehal/autotel/tree/main/packages/autotel-pact#readme"
|
|
108
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# autotel-pact JSON Schemas
|
|
2
|
+
|
|
3
|
+
The **public contract** for `autotel-pact`'s persisted artifacts.
|
|
4
|
+
|
|
5
|
+
| File | Emitted by | `spec:` value |
|
|
6
|
+
|------|------------|---------------|
|
|
7
|
+
| [`ledger-entry-v0.2.0.json`](./ledger-entry-v0.2.0.json) | Wrappers, processor, provider verify | `autotel-pact-ledger-entry/v0.2.0` |
|
|
8
|
+
| [`audit-matrix-v0.2.0.json`](./audit-matrix-v0.2.0.json) | `autotel-pact audit --json` | `autotel-pact-audit-matrix/v0.2.0` |
|
|
9
|
+
|
|
10
|
+
v0.2 covers:
|
|
11
|
+
|
|
12
|
+
- `source` (`test` | `production`) and `role` (`consumer` | `provider`) on every interaction row.
|
|
13
|
+
- A discriminated `type: 'provider_verification_run'` record for run-level provider failures that cannot be attributed to a single interaction.
|
|
14
|
+
- Renamed audit-matrix count fields: `contracted_and_test_seen`, `contracted_not_test_seen`, `test_or_prod_seen_not_contracted`.
|
|
15
|
+
|
|
16
|
+
## Version policy
|
|
17
|
+
|
|
18
|
+
Minor bumps add optional fields; major bumps break shape. The `spec` field is the gate against unknown majors.
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { LEDGER_ENTRY_SPEC, AUDIT_MATRIX_SPEC } from 'autotel-pact';
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Readers reject unknown `spec` values. There is no legacy migration path; v0.1 was never released.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://autotel.dev/schemas/autotel-pact/audit-matrix-v0.2.0.json",
|
|
4
|
+
"title": "autotel-pact audit matrix",
|
|
5
|
+
"description": "Output of `autotel-pact audit --json`.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["spec", "rows", "counts", "window_days", "generated_at"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"spec": { "const": "autotel-pact-audit-matrix/v0.2.0" },
|
|
10
|
+
"window_days": { "type": "integer", "minimum": 1 },
|
|
11
|
+
"generated_at": { "type": "string", "format": "date-time" },
|
|
12
|
+
"verification_failures": {
|
|
13
|
+
"type": "array",
|
|
14
|
+
"items": { "$ref": "ledger-entry-v0.2.0.json#/$defs/provider_verification_run" }
|
|
15
|
+
},
|
|
16
|
+
"counts": {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"required": [
|
|
19
|
+
"total",
|
|
20
|
+
"contracted",
|
|
21
|
+
"observed",
|
|
22
|
+
"contracted_and_test_seen",
|
|
23
|
+
"contracted_not_test_seen",
|
|
24
|
+
"test_or_prod_seen_not_contracted",
|
|
25
|
+
"test_seen",
|
|
26
|
+
"prod_seen",
|
|
27
|
+
"provider_verified",
|
|
28
|
+
"broker_verified"
|
|
29
|
+
],
|
|
30
|
+
"properties": {
|
|
31
|
+
"total": { "type": "integer" },
|
|
32
|
+
"contracted": { "type": "integer" },
|
|
33
|
+
"observed": { "type": "integer" },
|
|
34
|
+
"contracted_and_test_seen": { "type": "integer" },
|
|
35
|
+
"contracted_not_test_seen": { "type": "integer" },
|
|
36
|
+
"test_or_prod_seen_not_contracted": { "type": "integer" },
|
|
37
|
+
"test_seen": { "type": "integer" },
|
|
38
|
+
"prod_seen": { "type": "integer" },
|
|
39
|
+
"provider_verified": { "type": "integer" },
|
|
40
|
+
"broker_verified": { "type": "integer" }
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"rows": {
|
|
44
|
+
"type": "array",
|
|
45
|
+
"items": {
|
|
46
|
+
"type": "object",
|
|
47
|
+
"required": [
|
|
48
|
+
"consumer",
|
|
49
|
+
"provider",
|
|
50
|
+
"interaction",
|
|
51
|
+
"kind",
|
|
52
|
+
"contracted",
|
|
53
|
+
"observed",
|
|
54
|
+
"test_seen",
|
|
55
|
+
"prod_seen",
|
|
56
|
+
"provider_verified",
|
|
57
|
+
"broker_verified"
|
|
58
|
+
],
|
|
59
|
+
"properties": {
|
|
60
|
+
"consumer": { "type": "string" },
|
|
61
|
+
"provider": { "type": "string" },
|
|
62
|
+
"interaction": { "type": "string" },
|
|
63
|
+
"interaction_id": { "type": "string" },
|
|
64
|
+
"kind": { "enum": ["message", "http"] },
|
|
65
|
+
"contracted": { "type": "boolean" },
|
|
66
|
+
"observed": { "type": "boolean" },
|
|
67
|
+
"test_seen": { "type": "boolean" },
|
|
68
|
+
"prod_seen": { "type": "boolean" },
|
|
69
|
+
"provider_verified": { "type": "boolean" },
|
|
70
|
+
"broker_verified": { "type": "boolean" },
|
|
71
|
+
"broker_verified_at": { "type": "string" },
|
|
72
|
+
"last_observed_at": { "type": "string" },
|
|
73
|
+
"last_outcome": { "enum": ["passed", "failed"] }
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://autotel.dev/schemas/autotel-pact/ledger-entry-v0.2.0.json",
|
|
4
|
+
"title": "autotel-pact ledger entry",
|
|
5
|
+
"description": "JSONL ledger row: interaction observation or provider verification run failure.",
|
|
6
|
+
"oneOf": [
|
|
7
|
+
{ "$ref": "#/$defs/interaction" },
|
|
8
|
+
{ "$ref": "#/$defs/provider_verification_run" }
|
|
9
|
+
],
|
|
10
|
+
"$defs": {
|
|
11
|
+
"interaction": {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"required": [
|
|
14
|
+
"spec",
|
|
15
|
+
"consumer",
|
|
16
|
+
"provider",
|
|
17
|
+
"interaction",
|
|
18
|
+
"states",
|
|
19
|
+
"kind",
|
|
20
|
+
"source",
|
|
21
|
+
"role",
|
|
22
|
+
"outcome",
|
|
23
|
+
"duration_ms",
|
|
24
|
+
"observed_at"
|
|
25
|
+
],
|
|
26
|
+
"properties": {
|
|
27
|
+
"type": { "const": "interaction" },
|
|
28
|
+
"spec": { "const": "autotel-pact-ledger-entry/v0.2.0" },
|
|
29
|
+
"consumer": { "type": "string" },
|
|
30
|
+
"provider": { "type": "string" },
|
|
31
|
+
"interaction": { "type": "string" },
|
|
32
|
+
"interaction_id": { "type": "string", "minLength": 1 },
|
|
33
|
+
"states": { "type": "array", "items": { "type": "string" } },
|
|
34
|
+
"kind": { "enum": ["message", "http"] },
|
|
35
|
+
"source": { "enum": ["test", "production"] },
|
|
36
|
+
"role": { "enum": ["consumer", "provider"] },
|
|
37
|
+
"outcome": { "enum": ["passed", "failed"] },
|
|
38
|
+
"duration_ms": { "type": "number", "minimum": 0 },
|
|
39
|
+
"observed_at": { "type": "string", "format": "date-time" },
|
|
40
|
+
"trace_id": { "type": "string" },
|
|
41
|
+
"span_id": { "type": "string" },
|
|
42
|
+
"run_id": { "type": "string" },
|
|
43
|
+
"git_sha": { "type": "string" },
|
|
44
|
+
"error": { "type": "string" }
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"provider_verification_run": {
|
|
48
|
+
"type": "object",
|
|
49
|
+
"required": [
|
|
50
|
+
"type",
|
|
51
|
+
"spec",
|
|
52
|
+
"consumer",
|
|
53
|
+
"provider",
|
|
54
|
+
"source",
|
|
55
|
+
"role",
|
|
56
|
+
"outcome",
|
|
57
|
+
"observed_at",
|
|
58
|
+
"error"
|
|
59
|
+
],
|
|
60
|
+
"properties": {
|
|
61
|
+
"type": { "const": "provider_verification_run" },
|
|
62
|
+
"spec": { "const": "autotel-pact-ledger-entry/v0.2.0" },
|
|
63
|
+
"consumer": { "type": "string" },
|
|
64
|
+
"provider": { "type": "string" },
|
|
65
|
+
"source": { "enum": ["test", "production"] },
|
|
66
|
+
"role": { "const": "provider" },
|
|
67
|
+
"outcome": { "const": "failed" },
|
|
68
|
+
"observed_at": { "type": "string", "format": "date-time" },
|
|
69
|
+
"error": { "type": "string" },
|
|
70
|
+
"run_id": { "type": "string" },
|
|
71
|
+
"git_sha": { "type": "string" },
|
|
72
|
+
"trace_id": { "type": "string" },
|
|
73
|
+
"span_id": { "type": "string" }
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { buildPactAttributes, outcomeAttribute, PACT_ATTRS } from './attrs.js';
|
|
3
|
+
import type { PactInteractionMeta } from './types.js';
|
|
4
|
+
|
|
5
|
+
const meta: PactInteractionMeta = {
|
|
6
|
+
consumer: 'OrderShipper',
|
|
7
|
+
provider: 'OrderService',
|
|
8
|
+
description: 'an OrderCreated event',
|
|
9
|
+
states: ['an order exists'],
|
|
10
|
+
kind: 'message',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
describe('buildPactAttributes', () => {
|
|
14
|
+
it('emits the canonical pact.* attribute set', () => {
|
|
15
|
+
const attrs = buildPactAttributes(meta);
|
|
16
|
+
expect(attrs[PACT_ATTRS.CONSUMER]).toBe('OrderShipper');
|
|
17
|
+
expect(attrs[PACT_ATTRS.PROVIDER]).toBe('OrderService');
|
|
18
|
+
expect(attrs[PACT_ATTRS.KIND]).toBe('message');
|
|
19
|
+
expect(attrs[PACT_ATTRS.INTERACTION_DESCRIPTION]).toBe('an OrderCreated event');
|
|
20
|
+
expect(attrs[PACT_ATTRS.INTERACTION_STATES]).toEqual(['an order exists']);
|
|
21
|
+
expect(attrs[PACT_ATTRS.CONTRACT_FILE]).toBeUndefined();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('includes pact.contract.file when supplied', () => {
|
|
25
|
+
const attrs = buildPactAttributes(meta, { contractFile: 'pacts/OrderShipper-OrderService.json' });
|
|
26
|
+
expect(attrs[PACT_ATTRS.CONTRACT_FILE]).toBe('pacts/OrderShipper-OrderService.json');
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('outcomeAttribute', () => {
|
|
31
|
+
it('returns a single-key record for the outcome', () => {
|
|
32
|
+
expect(outcomeAttribute('passed')).toEqual({ [PACT_ATTRS.OUTCOME]: 'passed' });
|
|
33
|
+
expect(outcomeAttribute('failed')).toEqual({ [PACT_ATTRS.OUTCOME]: 'failed' });
|
|
34
|
+
});
|
|
35
|
+
});
|
package/src/attrs.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { PactInteractionMeta, PactOutcome } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Attribute keys for Pact interactions. Centralised so the namespace is
|
|
5
|
+
* a single source of truth and is forward-compatible with eventual OTel
|
|
6
|
+
* semantic conventions.
|
|
7
|
+
*/
|
|
8
|
+
export const PACT_ATTRS = {
|
|
9
|
+
CONSUMER: 'pact.consumer',
|
|
10
|
+
PROVIDER: 'pact.provider',
|
|
11
|
+
KIND: 'pact.kind',
|
|
12
|
+
INTERACTION_DESCRIPTION: 'pact.interaction.description',
|
|
13
|
+
INTERACTION_ID: 'pact.interaction.id',
|
|
14
|
+
INTERACTION_STATES: 'pact.interaction.states',
|
|
15
|
+
CONTRACT_FILE: 'pact.contract.file',
|
|
16
|
+
OUTCOME: 'pact.outcome',
|
|
17
|
+
} as const;
|
|
18
|
+
|
|
19
|
+
export type PactAttributeKey = (typeof PACT_ATTRS)[keyof typeof PACT_ATTRS];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Build the set of attributes to stamp on a span when an interaction is
|
|
23
|
+
* about to be exercised. `outcome` is added later by the wrapper.
|
|
24
|
+
*/
|
|
25
|
+
export function buildPactAttributes(
|
|
26
|
+
meta: PactInteractionMeta,
|
|
27
|
+
opts: { contractFile?: string } = {},
|
|
28
|
+
): Record<string, string | string[]> {
|
|
29
|
+
const attrs: Record<string, string | string[]> = {
|
|
30
|
+
[PACT_ATTRS.CONSUMER]: meta.consumer,
|
|
31
|
+
[PACT_ATTRS.PROVIDER]: meta.provider,
|
|
32
|
+
[PACT_ATTRS.KIND]: meta.kind,
|
|
33
|
+
[PACT_ATTRS.INTERACTION_DESCRIPTION]: meta.description,
|
|
34
|
+
[PACT_ATTRS.INTERACTION_STATES]: meta.states,
|
|
35
|
+
};
|
|
36
|
+
if (opts.contractFile) {
|
|
37
|
+
attrs[PACT_ATTRS.CONTRACT_FILE] = opts.contractFile;
|
|
38
|
+
}
|
|
39
|
+
if (meta.interactionId) {
|
|
40
|
+
attrs[PACT_ATTRS.INTERACTION_ID] = meta.interactionId;
|
|
41
|
+
}
|
|
42
|
+
return attrs;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Helper that returns just the outcome attribute — stamped after the
|
|
47
|
+
* handler resolves or rejects.
|
|
48
|
+
*/
|
|
49
|
+
export function outcomeAttribute(
|
|
50
|
+
outcome: PactOutcome,
|
|
51
|
+
): Record<string, string> {
|
|
52
|
+
return { [PACT_ATTRS.OUTCOME]: outcome };
|
|
53
|
+
}
|