@patricio0312rev/skillset 0.1.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/CHANGELOG.md +29 -0
- package/LICENSE +21 -0
- package/README.md +176 -0
- package/bin/cli.js +37 -0
- package/package.json +55 -0
- package/src/commands/init.js +301 -0
- package/src/index.js +168 -0
- package/src/lib/config.js +200 -0
- package/src/lib/generator.js +166 -0
- package/src/utils/display.js +95 -0
- package/src/utils/readme.js +196 -0
- package/src/utils/tool-specific.js +233 -0
- package/templates/ai-engineering/agent-orchestration-planner/ SKILL.md +266 -0
- package/templates/ai-engineering/cost-latency-optimizer/ SKILL.md +270 -0
- package/templates/ai-engineering/doc-to-vector-dataset-generator/ SKILL.md +239 -0
- package/templates/ai-engineering/evaluation-harness/ SKILL.md +219 -0
- package/templates/ai-engineering/guardrails-safety-filter-builder/ SKILL.md +226 -0
- package/templates/ai-engineering/llm-debugger/ SKILL.md +283 -0
- package/templates/ai-engineering/prompt-regression-tester/ SKILL.md +216 -0
- package/templates/ai-engineering/prompt-template-builder/ SKILL.md +393 -0
- package/templates/ai-engineering/rag-pipeline-builder/ SKILL.md +244 -0
- package/templates/ai-engineering/tool-function-schema-designer/ SKILL.md +219 -0
- package/templates/architecture/adr-writer/ SKILL.md +250 -0
- package/templates/architecture/api-versioning-deprecation-planner/ SKILL.md +331 -0
- package/templates/architecture/domain-model-boundaries-mapper/ SKILL.md +300 -0
- package/templates/architecture/migration-planner/ SKILL.md +376 -0
- package/templates/architecture/performance-budget-setter/ SKILL.md +318 -0
- package/templates/architecture/reliability-strategy-builder/ SKILL.md +286 -0
- package/templates/architecture/rfc-generator/ SKILL.md +362 -0
- package/templates/architecture/scalability-playbook/ SKILL.md +279 -0
- package/templates/architecture/system-design-generator/ SKILL.md +339 -0
- package/templates/architecture/tech-debt-prioritizer/ SKILL.md +329 -0
- package/templates/backend/api-contract-normalizer/ SKILL.md +487 -0
- package/templates/backend/api-endpoint-generator/ SKILL.md +415 -0
- package/templates/backend/auth-module-builder/ SKILL.md +99 -0
- package/templates/backend/background-jobs-designer/ SKILL.md +166 -0
- package/templates/backend/caching-strategist/ SKILL.md +190 -0
- package/templates/backend/error-handling-standardizer/ SKILL.md +174 -0
- package/templates/backend/rate-limiting-abuse-protection/ SKILL.md +147 -0
- package/templates/backend/rbac-permissions-builder/ SKILL.md +158 -0
- package/templates/backend/service-layer-extractor/ SKILL.md +269 -0
- package/templates/backend/webhook-receiver-hardener/ SKILL.md +211 -0
- package/templates/ci-cd/artifact-sbom-publisher/ SKILL.md +236 -0
- package/templates/ci-cd/caching-strategy-optimizer/ SKILL.md +195 -0
- package/templates/ci-cd/deployment-checklist-generator/ SKILL.md +381 -0
- package/templates/ci-cd/github-actions-pipeline-creator/ SKILL.md +348 -0
- package/templates/ci-cd/monorepo-ci-optimizer/ SKILL.md +298 -0
- package/templates/ci-cd/preview-environments-builder/ SKILL.md +187 -0
- package/templates/ci-cd/quality-gates-enforcer/ SKILL.md +342 -0
- package/templates/ci-cd/release-automation-builder/ SKILL.md +281 -0
- package/templates/ci-cd/rollback-workflow-builder/ SKILL.md +372 -0
- package/templates/ci-cd/secrets-env-manager/ SKILL.md +242 -0
- package/templates/db-management/backup-restore-runbook-generator/ SKILL.md +505 -0
- package/templates/db-management/data-integrity-auditor/ SKILL.md +505 -0
- package/templates/db-management/data-retention-archiving-planner/ SKILL.md +430 -0
- package/templates/db-management/data-seeding-fixtures-builder/ SKILL.md +375 -0
- package/templates/db-management/db-performance-watchlist/ SKILL.md +425 -0
- package/templates/db-management/etl-sync-job-builder/ SKILL.md +457 -0
- package/templates/db-management/multi-tenant-safety-checker/ SKILL.md +398 -0
- package/templates/db-management/prisma-migration-assistant/ SKILL.md +379 -0
- package/templates/db-management/schema-consistency-checker/ SKILL.md +440 -0
- package/templates/db-management/sql-query-optimizer/ SKILL.md +324 -0
- package/templates/foundation/changelog-writer/ SKILL.md +431 -0
- package/templates/foundation/code-formatter-installer/ SKILL.md +320 -0
- package/templates/foundation/codebase-summarizer/ SKILL.md +360 -0
- package/templates/foundation/dependency-doctor/ SKILL.md +163 -0
- package/templates/foundation/dev-environment-bootstrapper/ SKILL.md +259 -0
- package/templates/foundation/dev-onboarding-builder/ SKILL.md +556 -0
- package/templates/foundation/docs-starter-kit/ SKILL.md +574 -0
- package/templates/foundation/explaining-code/SKILL.md +13 -0
- package/templates/foundation/git-hygiene-enforcer/ SKILL.md +455 -0
- package/templates/foundation/project-scaffolder/ SKILL.md +65 -0
- package/templates/foundation/project-scaffolder/references/templates.md +126 -0
- package/templates/foundation/repo-structure-linter/ SKILL.md +0 -0
- package/templates/foundation/repo-structure-linter/references/conventions.md +98 -0
- package/templates/frontend/animation-micro-interaction-pack/ SKILL.md +41 -0
- package/templates/frontend/component-scaffold-generator/ SKILL.md +562 -0
- package/templates/frontend/design-to-component-translator/ SKILL.md +547 -0
- package/templates/frontend/form-wizard-builder/ SKILL.md +553 -0
- package/templates/frontend/frontend-refactor-planner/ SKILL.md +37 -0
- package/templates/frontend/i18n-frontend-implementer/ SKILL.md +44 -0
- package/templates/frontend/modal-drawer-system/ SKILL.md +377 -0
- package/templates/frontend/page-layout-builder/ SKILL.md +630 -0
- package/templates/frontend/state-ux-flow-builder/ SKILL.md +23 -0
- package/templates/frontend/table-builder/ SKILL.md +350 -0
- package/templates/performance/alerting-dashboard-builder/ SKILL.md +162 -0
- package/templates/performance/backend-latency-profiler-helper/ SKILL.md +108 -0
- package/templates/performance/caching-cdn-strategy-planner/ SKILL.md +150 -0
- package/templates/performance/capacity-planning-helper/ SKILL.md +242 -0
- package/templates/performance/core-web-vitals-tuner/ SKILL.md +126 -0
- package/templates/performance/incident-runbook-generator/ SKILL.md +162 -0
- package/templates/performance/load-test-scenario-builder/ SKILL.md +256 -0
- package/templates/performance/observability-setup/ SKILL.md +232 -0
- package/templates/performance/postmortem-writer/ SKILL.md +203 -0
- package/templates/performance/structured-logging-standardizer/ SKILL.md +122 -0
- package/templates/security/auth-security-reviewer/ SKILL.md +428 -0
- package/templates/security/dependency-vulnerability-triage/ SKILL.md +495 -0
- package/templates/security/input-validation-sanitization-auditor/ SKILL.md +76 -0
- package/templates/security/pii-redaction-logging-policy-builder/ SKILL.md +65 -0
- package/templates/security/rbac-policy-tester/ SKILL.md +80 -0
- package/templates/security/secrets-scanner/ SKILL.md +462 -0
- package/templates/security/secure-headers-csp-builder/ SKILL.md +404 -0
- package/templates/security/security-incident-playbook-generator/ SKILL.md +76 -0
- package/templates/security/security-pr-checklist-skill/ SKILL.md +62 -0
- package/templates/security/threat-model-generator/ SKILL.md +394 -0
- package/templates/testing/contract-testing-builder/ SKILL.md +492 -0
- package/templates/testing/coverage-strategist/ SKILL.md +436 -0
- package/templates/testing/e2e-test-builder/ SKILL.md +382 -0
- package/templates/testing/flaky-test-detective/ SKILL.md +416 -0
- package/templates/testing/integration-test-builder/ SKILL.md +525 -0
- package/templates/testing/mocking-assistant/ SKILL.md +383 -0
- package/templates/testing/snapshot-test-refactorer/ SKILL.md +375 -0
- package/templates/testing/test-data-factory-builder/ SKILL.md +449 -0
- package/templates/testing/test-reporting-triage-skill/ SKILL.md +469 -0
- package/templates/testing/unit-test-generator/ SKILL.md +548 -0
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: contract-testing-builder
|
|
3
|
+
description: Implements API contract testing to ensure provider-consumer compatibility using Pact or similar tools. Prevents breaking changes with contract specifications and bi-directional verification. Use for "contract testing", "API contracts", "Pact", or "consumer-driven contracts".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Contract Testing Builder
|
|
7
|
+
|
|
8
|
+
Ensure API contracts don't break consumers.
|
|
9
|
+
|
|
10
|
+
## Contract Testing Concepts
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
Consumer → Defines expected contract → Provider must satisfy
|
|
14
|
+
|
|
15
|
+
Benefits:
|
|
16
|
+
- Catch breaking changes early
|
|
17
|
+
- Independent development
|
|
18
|
+
- Fast feedback (no integration env needed)
|
|
19
|
+
- Documentation as code
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Pact Setup (Consumer Side)
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
// consumer/tests/pacts/user-api.pact.test.ts
|
|
26
|
+
import { PactV3 } from "@pact-foundation/pact";
|
|
27
|
+
import { userApi } from "../api/userApi";
|
|
28
|
+
|
|
29
|
+
const provider = new PactV3({
|
|
30
|
+
consumer: "UserWebApp",
|
|
31
|
+
provider: "UserAPI",
|
|
32
|
+
dir: path.resolve(__dirname, "../../pacts"),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe("User API Contract", () => {
|
|
36
|
+
it("should get user by ID", async () => {
|
|
37
|
+
// Define expected interaction
|
|
38
|
+
await provider
|
|
39
|
+
.given("user 123 exists")
|
|
40
|
+
.uponReceiving("a request for user 123")
|
|
41
|
+
.withRequest({
|
|
42
|
+
method: "GET",
|
|
43
|
+
path: "/api/users/123",
|
|
44
|
+
headers: {
|
|
45
|
+
Authorization: "Bearer token123",
|
|
46
|
+
},
|
|
47
|
+
})
|
|
48
|
+
.willRespondWith({
|
|
49
|
+
status: 200,
|
|
50
|
+
headers: {
|
|
51
|
+
"Content-Type": "application/json",
|
|
52
|
+
},
|
|
53
|
+
body: {
|
|
54
|
+
id: "123",
|
|
55
|
+
email: "john@example.com",
|
|
56
|
+
name: "John Doe",
|
|
57
|
+
role: "USER",
|
|
58
|
+
createdAt: like("2024-01-01T00:00:00Z"),
|
|
59
|
+
},
|
|
60
|
+
})
|
|
61
|
+
.executeTest(async (mockServer) => {
|
|
62
|
+
// Make actual API call against mock server
|
|
63
|
+
const user = await userApi.getUser("123", mockServer.url);
|
|
64
|
+
|
|
65
|
+
// Verify consumer can handle response
|
|
66
|
+
expect(user.id).toBe("123");
|
|
67
|
+
expect(user.email).toBe("john@example.com");
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("should return 404 when user not found", async () => {
|
|
72
|
+
await provider
|
|
73
|
+
.given("user 999 does not exist")
|
|
74
|
+
.uponReceiving("a request for non-existent user")
|
|
75
|
+
.withRequest({
|
|
76
|
+
method: "GET",
|
|
77
|
+
path: "/api/users/999",
|
|
78
|
+
})
|
|
79
|
+
.willRespondWith({
|
|
80
|
+
status: 404,
|
|
81
|
+
headers: {
|
|
82
|
+
"Content-Type": "application/json",
|
|
83
|
+
},
|
|
84
|
+
body: {
|
|
85
|
+
error: "User not found",
|
|
86
|
+
},
|
|
87
|
+
})
|
|
88
|
+
.executeTest(async (mockServer) => {
|
|
89
|
+
await expect(userApi.getUser("999", mockServer.url)).rejects.toThrow(
|
|
90
|
+
"User not found"
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Pact Verification (Provider Side)
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
// provider/tests/pacts/verify.test.ts
|
|
101
|
+
import { Verifier } from "@pact-foundation/pact";
|
|
102
|
+
import { app } from "../src/app";
|
|
103
|
+
|
|
104
|
+
describe("Pact Verification", () => {
|
|
105
|
+
let server: Server;
|
|
106
|
+
|
|
107
|
+
beforeAll(async () => {
|
|
108
|
+
server = app.listen(3000);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
afterAll(() => {
|
|
112
|
+
server.close();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("should validate consumer contracts", async () => {
|
|
116
|
+
const verifier = new Verifier({
|
|
117
|
+
provider: "UserAPI",
|
|
118
|
+
providerBaseUrl: "http://localhost:3000",
|
|
119
|
+
|
|
120
|
+
// Fetch pacts from broker or local files
|
|
121
|
+
pactUrls: [
|
|
122
|
+
path.resolve(__dirname, "../../pacts/UserWebApp-UserAPI.json"),
|
|
123
|
+
],
|
|
124
|
+
|
|
125
|
+
// Provider states setup
|
|
126
|
+
stateHandlers: {
|
|
127
|
+
"user 123 exists": async () => {
|
|
128
|
+
// Seed database with user 123
|
|
129
|
+
await db.user.create({
|
|
130
|
+
id: "123",
|
|
131
|
+
email: "john@example.com",
|
|
132
|
+
name: "John Doe",
|
|
133
|
+
role: "USER",
|
|
134
|
+
});
|
|
135
|
+
},
|
|
136
|
+
"user 999 does not exist": async () => {
|
|
137
|
+
// Ensure user 999 doesn't exist
|
|
138
|
+
await db.user.deleteMany({ where: { id: "999" } });
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
// Teardown after each test
|
|
143
|
+
afterEach: async () => {
|
|
144
|
+
await db.$executeRaw`TRUNCATE TABLE users CASCADE`;
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
await verifier.verifyProvider();
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## OpenAPI Contract Testing
|
|
154
|
+
|
|
155
|
+
```yaml
|
|
156
|
+
# contracts/user-api.yaml
|
|
157
|
+
openapi: 3.0.0
|
|
158
|
+
info:
|
|
159
|
+
title: User API
|
|
160
|
+
version: 1.0.0
|
|
161
|
+
|
|
162
|
+
paths:
|
|
163
|
+
/api/users/{id}:
|
|
164
|
+
get:
|
|
165
|
+
parameters:
|
|
166
|
+
- name: id
|
|
167
|
+
in: path
|
|
168
|
+
required: true
|
|
169
|
+
schema:
|
|
170
|
+
type: string
|
|
171
|
+
responses:
|
|
172
|
+
"200":
|
|
173
|
+
description: User found
|
|
174
|
+
content:
|
|
175
|
+
application/json:
|
|
176
|
+
schema:
|
|
177
|
+
$ref: "#/components/schemas/User"
|
|
178
|
+
"404":
|
|
179
|
+
description: User not found
|
|
180
|
+
content:
|
|
181
|
+
application/json:
|
|
182
|
+
schema:
|
|
183
|
+
$ref: "#/components/schemas/Error"
|
|
184
|
+
|
|
185
|
+
components:
|
|
186
|
+
schemas:
|
|
187
|
+
User:
|
|
188
|
+
type: object
|
|
189
|
+
required:
|
|
190
|
+
- id
|
|
191
|
+
- email
|
|
192
|
+
- name
|
|
193
|
+
- role
|
|
194
|
+
properties:
|
|
195
|
+
id:
|
|
196
|
+
type: string
|
|
197
|
+
email:
|
|
198
|
+
type: string
|
|
199
|
+
format: email
|
|
200
|
+
name:
|
|
201
|
+
type: string
|
|
202
|
+
role:
|
|
203
|
+
type: string
|
|
204
|
+
enum: [USER, ADMIN]
|
|
205
|
+
createdAt:
|
|
206
|
+
type: string
|
|
207
|
+
format: date-time
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Contract Validation (OpenAPI)
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
// tests/contract-validation.test.ts
|
|
214
|
+
import * as OpenAPIValidator from "express-openapi-validator";
|
|
215
|
+
import * as fs from "fs";
|
|
216
|
+
import * as yaml from "js-yaml";
|
|
217
|
+
|
|
218
|
+
describe("API Contract Validation", () => {
|
|
219
|
+
it("should match OpenAPI spec", async () => {
|
|
220
|
+
const spec = yaml.load(
|
|
221
|
+
fs.readFileSync("./contracts/user-api.yaml", "utf8")
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
app.use(
|
|
225
|
+
OpenAPIValidator.middleware({
|
|
226
|
+
apiSpec: spec,
|
|
227
|
+
validateRequests: true,
|
|
228
|
+
validateResponses: true,
|
|
229
|
+
})
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
// Valid request - should pass
|
|
233
|
+
await request(app)
|
|
234
|
+
.get("/api/users/123")
|
|
235
|
+
.expect(200)
|
|
236
|
+
.expect((res) => {
|
|
237
|
+
expect(res.body).toHaveProperty("id");
|
|
238
|
+
expect(res.body).toHaveProperty("email");
|
|
239
|
+
expect(res.body).toHaveProperty("name");
|
|
240
|
+
expect(res.body).toHaveProperty("role");
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it("should reject invalid responses", async () => {
|
|
245
|
+
// Mock endpoint that returns invalid data
|
|
246
|
+
app.get("/api/invalid", (req, res) => {
|
|
247
|
+
res.json({
|
|
248
|
+
id: "123",
|
|
249
|
+
// Missing required fields!
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// Should fail validation
|
|
254
|
+
await request(app).get("/api/invalid").expect(500);
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## JSON Schema Validation
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
// schemas/user.schema.ts
|
|
263
|
+
export const userSchema = {
|
|
264
|
+
type: "object",
|
|
265
|
+
required: ["id", "email", "name", "role"],
|
|
266
|
+
properties: {
|
|
267
|
+
id: { type: "string" },
|
|
268
|
+
email: { type: "string", format: "email" },
|
|
269
|
+
name: { type: "string", minLength: 1 },
|
|
270
|
+
role: { type: "string", enum: ["USER", "ADMIN"] },
|
|
271
|
+
createdAt: { type: "string", format: "date-time" },
|
|
272
|
+
},
|
|
273
|
+
additionalProperties: false,
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
// tests/schema-validation.test.ts
|
|
277
|
+
import Ajv from "ajv";
|
|
278
|
+
import addFormats from "ajv-formats";
|
|
279
|
+
|
|
280
|
+
const ajv = new Ajv();
|
|
281
|
+
addFormats(ajv);
|
|
282
|
+
|
|
283
|
+
describe("User Schema Validation", () => {
|
|
284
|
+
const validate = ajv.compile(userSchema);
|
|
285
|
+
|
|
286
|
+
it("should validate correct user object", () => {
|
|
287
|
+
const user = {
|
|
288
|
+
id: "123",
|
|
289
|
+
email: "john@example.com",
|
|
290
|
+
name: "John Doe",
|
|
291
|
+
role: "USER",
|
|
292
|
+
createdAt: "2024-01-01T00:00:00Z",
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
expect(validate(user)).toBe(true);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it("should reject missing required fields", () => {
|
|
299
|
+
const user = {
|
|
300
|
+
id: "123",
|
|
301
|
+
email: "john@example.com",
|
|
302
|
+
// Missing name and role
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
expect(validate(user)).toBe(false);
|
|
306
|
+
expect(validate.errors).toContainEqual(
|
|
307
|
+
expect.objectContaining({
|
|
308
|
+
message: "must have required property 'name'",
|
|
309
|
+
})
|
|
310
|
+
);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
it("should reject invalid email format", () => {
|
|
314
|
+
const user = {
|
|
315
|
+
id: "123",
|
|
316
|
+
email: "invalid-email",
|
|
317
|
+
name: "John Doe",
|
|
318
|
+
role: "USER",
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
expect(validate(user)).toBe(false);
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## CI Integration
|
|
327
|
+
|
|
328
|
+
```yaml
|
|
329
|
+
# .github/workflows/contract-tests.yml
|
|
330
|
+
name: Contract Tests
|
|
331
|
+
|
|
332
|
+
on: [push, pull_request]
|
|
333
|
+
|
|
334
|
+
jobs:
|
|
335
|
+
consumer-tests:
|
|
336
|
+
runs-on: ubuntu-latest
|
|
337
|
+
steps:
|
|
338
|
+
- uses: actions/checkout@v4
|
|
339
|
+
- uses: actions/setup-node@v4
|
|
340
|
+
|
|
341
|
+
- name: Run consumer tests
|
|
342
|
+
run: npm run test:pact
|
|
343
|
+
|
|
344
|
+
- name: Publish pacts
|
|
345
|
+
run: |
|
|
346
|
+
npx pact-broker publish \
|
|
347
|
+
./pacts \
|
|
348
|
+
--consumer-app-version=${{ github.sha }} \
|
|
349
|
+
--broker-base-url=${{ secrets.PACT_BROKER_URL }} \
|
|
350
|
+
--broker-token=${{ secrets.PACT_BROKER_TOKEN }}
|
|
351
|
+
|
|
352
|
+
provider-tests:
|
|
353
|
+
runs-on: ubuntu-latest
|
|
354
|
+
needs: consumer-tests
|
|
355
|
+
steps:
|
|
356
|
+
- uses: actions/checkout@v4
|
|
357
|
+
- uses: actions/setup-node@v4
|
|
358
|
+
|
|
359
|
+
- name: Verify provider
|
|
360
|
+
run: npm run test:pact:verify
|
|
361
|
+
env:
|
|
362
|
+
PACT_BROKER_URL: ${{ secrets.PACT_BROKER_URL }}
|
|
363
|
+
PACT_BROKER_TOKEN: ${{ secrets.PACT_BROKER_TOKEN }}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
## Breaking Change Detection
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
// tests/breaking-changes.test.ts
|
|
370
|
+
describe("Breaking Change Detection", () => {
|
|
371
|
+
it("should not remove required fields", async () => {
|
|
372
|
+
const v1Response = {
|
|
373
|
+
id: "123",
|
|
374
|
+
email: "john@example.com",
|
|
375
|
+
name: "John Doe",
|
|
376
|
+
role: "USER",
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
const v2Response = {
|
|
380
|
+
id: "123",
|
|
381
|
+
email: "john@example.com",
|
|
382
|
+
// Missing 'name' - BREAKING CHANGE!
|
|
383
|
+
role: "USER",
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
// Validate v2 still has all v1 required fields
|
|
387
|
+
const v1Keys = Object.keys(v1Response);
|
|
388
|
+
const v2Keys = Object.keys(v2Response);
|
|
389
|
+
|
|
390
|
+
const missingFields = v1Keys.filter((key) => !v2Keys.includes(key));
|
|
391
|
+
|
|
392
|
+
expect(missingFields).toHaveLength(0);
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it("should not change field types", async () => {
|
|
396
|
+
const v1Response = {
|
|
397
|
+
id: "123", // string
|
|
398
|
+
age: 25, // number
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
const v2Response = {
|
|
402
|
+
id: 123, // number - BREAKING CHANGE!
|
|
403
|
+
age: "25", // string - BREAKING CHANGE!
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
expect(typeof v2Response.id).toBe(typeof v1Response.id);
|
|
407
|
+
expect(typeof v2Response.age).toBe(typeof v1Response.age);
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
## Contract Documentation
|
|
413
|
+
|
|
414
|
+
````markdown
|
|
415
|
+
# API Contract Documentation
|
|
416
|
+
|
|
417
|
+
## User API Contract
|
|
418
|
+
|
|
419
|
+
### Consumer: UserWebApp
|
|
420
|
+
|
|
421
|
+
### Provider: UserAPI
|
|
422
|
+
|
|
423
|
+
### Interactions
|
|
424
|
+
|
|
425
|
+
#### Get User by ID
|
|
426
|
+
|
|
427
|
+
**Request:**
|
|
428
|
+
|
|
429
|
+
```http
|
|
430
|
+
GET /api/users/{id}
|
|
431
|
+
Authorization: Bearer {token}
|
|
432
|
+
```
|
|
433
|
+
````
|
|
434
|
+
|
|
435
|
+
**Response (200):**
|
|
436
|
+
|
|
437
|
+
```json
|
|
438
|
+
{
|
|
439
|
+
"id": "string",
|
|
440
|
+
"email": "string (email format)",
|
|
441
|
+
"name": "string",
|
|
442
|
+
"role": "USER | ADMIN",
|
|
443
|
+
"createdAt": "string (ISO 8601)"
|
|
444
|
+
}
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
**Response (404):**
|
|
448
|
+
|
|
449
|
+
```json
|
|
450
|
+
{
|
|
451
|
+
"error": "User not found"
|
|
452
|
+
}
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### Provider States
|
|
456
|
+
|
|
457
|
+
- **user {id} exists**: User with given ID exists in database
|
|
458
|
+
- **user {id} does not exist**: User with given ID does not exist
|
|
459
|
+
|
|
460
|
+
### Breaking Change Policy
|
|
461
|
+
|
|
462
|
+
1. Cannot remove required fields
|
|
463
|
+
2. Cannot change field types
|
|
464
|
+
3. Cannot remove enum values
|
|
465
|
+
4. Can add optional fields
|
|
466
|
+
5. Can deprecate with 6-month notice
|
|
467
|
+
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
## Best Practices
|
|
471
|
+
|
|
472
|
+
1. **Consumer-driven**: Consumers define expectations
|
|
473
|
+
2. **Test early**: Run in CI on every commit
|
|
474
|
+
3. **Use Pact Broker**: Central contract repository
|
|
475
|
+
4. **Provider states**: Setup test data properly
|
|
476
|
+
5. **Version contracts**: Track API versions
|
|
477
|
+
6. **Document changes**: Clear migration guides
|
|
478
|
+
7. **Monitor compliance**: Track contract violations
|
|
479
|
+
|
|
480
|
+
## Output Checklist
|
|
481
|
+
|
|
482
|
+
- [ ] Contract test framework chosen (Pact/OpenAPI)
|
|
483
|
+
- [ ] Consumer tests written
|
|
484
|
+
- [ ] Provider verification configured
|
|
485
|
+
- [ ] Provider states implemented
|
|
486
|
+
- [ ] Schema validation added
|
|
487
|
+
- [ ] Breaking change detection
|
|
488
|
+
- [ ] CI integration configured
|
|
489
|
+
- [ ] Contract documentation
|
|
490
|
+
- [ ] Pact Broker setup (if using Pact)
|
|
491
|
+
- [ ] Versioning strategy defined
|
|
492
|
+
```
|