一键导入
api-contract-testing
Tools and patterns for API contract testing using OpenAPI, JSON Schema, and contract-first development
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Tools and patterns for API contract testing using OpenAPI, JSON Schema, and contract-first development
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Structured divergence engine. Generates a wide, deliberately non-anchored set of approaches to a consequential, open-ended problem by spawning isolated parallel sub-agents under different cognitive frames, then a separate critic pass scores, declusters, and deepens the best. The divergent complement to /deep-analysis. Use for high-stakes architecture, strategy, design-space, and "what are our options" decisions. Auto-activates on diverge, brainstorm options, explore alternatives, what are our options, de-anchor, divergent thinking, option space, consider alternatives, multiple approaches.
Orchestrated task execution engine. Decomposes any goal into small atomic tasks, plans dependencies, selects the right agent/tool/MCP server for each, executes in optimally parallel batches, and tracks everything. Use when given a complex, multi-step goal that benefits from structured decomposition and full tool utilization.
Advanced multi-agent patterns using Claude Code's built-in orchestration. Teams, background agents, coordinator pattern, worker restrictions, SendMessage protocol, fork subagents. Auto-activates on multi-agent, orchestrate, team, coordinator, parallel agents, worker agents, background agent, agent swarm.
Structured multi-step reasoning for complex problems using the sequential-thinking MCP server. Use when facing architectural decisions, performance bottlenecks, complex debugging, design trade-offs, technology selection, or any problem requiring rigorous step-by-step analysis with hypothesis testing.
Deep root cause analysis engine. Systematically investigates bugs, crashes, unexpected behavior, and performance issues through an 8-phase diagnostic protocol. Uses structured reasoning (sequential-thinking MCP), multi-pass codebase analysis, git forensics, evidence-based hypothesis testing, and the 5 Whys method. Never jumps to a fix — always proves the root cause first.
Comprehensive codebase reading engine. Systematically reads actual source code line by line through a 6-phase protocol — scoping, structural mapping, execution tracing, deep reading, pattern synthesis, and structured reporting. Source code is the source of truth. Use when needing to truly understand how code works, not just what documentation claims.
| name | api-contract-testing |
| description | Tools and patterns for API contract testing using OpenAPI, JSON Schema, and contract-first development |
Provides tools and patterns for API contract testing using OpenAPI, JSON Schema, and contract-first development.
This skill provides:
openapi: 3.0.0
info:
title: User API
version: 1.0.0
description: User management API
servers:
- url: https://api.example.com/v1
description: Production server
- url: https://staging-api.example.com/v1
description: Staging server
paths:
/users:
get:
summary: List all users
operationId: listUsers
parameters:
- name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 20
responses:
'200':
description: Successful response
content:
application/json:
schema:
type: object
properties:
users:
type: array
items:
$ref: '#/components/schemas/User'
total:
type: integer
post:
summary: Create a new user
operationId: createUser
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UserCreate'
responses:
'201':
description: User created
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'400':
description: Bad request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/users/{userId}:
get:
summary: Get user by ID
operationId: getUserById
parameters:
- name: userId
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'404':
description: User not found
components:
schemas:
User:
type: object
required:
- id
- email
- name
properties:
id:
type: string
format: uuid
readOnly: true
email:
type: string
format: email
name:
type: string
minLength: 1
maxLength: 100
createdAt:
type: string
format: date-time
readOnly: true
UserCreate:
type: object
required:
- email
- name
properties:
email:
type: string
format: email
name:
type: string
minLength: 1
maxLength: 100
password:
type: string
format: password
minLength: 8
Error:
type: object
required:
- message
properties:
message:
type: string
errors:
type: array
items:
type: object
properties:
field:
type: string
message:
type: string
// tests/api-contract.test.ts
import { describe, it, expect } from 'vitest'
import OpenAPIValidator from 'express-openapi-validator'
import SwaggerParser from '@apidevtools/swagger-parser'
const SPEC_PATH = './openapi.yaml'
describe('API Contract Tests', () => {
it('should have valid OpenAPI specification', async () => {
const api = await SwaggerParser.validate(SPEC_PATH)
expect(api).toBeDefined()
expect(api.openapi).toBe('3.0.0')
})
it('should validate request/response against spec', async () => {
const validator = await OpenAPIValidator.middleware({
apiSpec: SPEC_PATH,
validateRequests: true,
validateResponses: true,
})
expect(validator).toBeDefined()
})
})
// tests/pact-provider.test.ts
import { Verifier } from '@pact-foundation/pact'
import path from 'path'
describe('Pact Provider Verification', () => {
it('should validate against consumer contracts', async () => {
const opts = {
provider: 'UserAPI',
providerBaseUrl: 'http://localhost:3000',
pactUrls: [
path.resolve(__dirname, '../pacts/consumer-userapi.json'),
],
stateHandlers: {
'user exists': async () => {
// Setup test data
await db.users.create({
id: 'test-user-id',
email: 'test@example.com',
name: 'Test User',
})
},
},
}
await new Verifier(opts).verifyProvider()
})
})
// tests/pact-consumer.test.ts
import { PactV3, MatchersV3 } from '@pact-foundation/pact'
import { getUserById } from '../api/users'
const { like, iso8601DateTime } = MatchersV3
describe('User API Consumer', () => {
const provider = new PactV3({
consumer: 'WebApp',
provider: 'UserAPI',
})
it('should get user by ID', async () => {
await provider
.given('user exists')
.uponReceiving('a request for user by ID')
.withRequest({
method: 'GET',
path: '/users/test-user-id',
})
.willRespondWith({
status: 200,
headers: { 'Content-Type': 'application/json' },
body: {
id: 'test-user-id',
email: like('test@example.com'),
name: like('Test User'),
createdAt: iso8601DateTime(),
},
})
.executeTest(async (mockServer) => {
const user = await getUserById('test-user-id', mockServer.url)
expect(user.id).toBe('test-user-id')
expect(user.email).toMatch(/^.+@.+\..+$/)
})
})
})
# Install Prism
npm install -g @stoplight/prism-cli
# Start mock server from OpenAPI spec
prism mock openapi.yaml --port 4010
# Mock server with dynamic examples
prism mock openapi.yaml --dynamic
# Validate requests only (proxy to real API)
prism proxy openapi.yaml https://api.example.com
// scripts/generate-postman.ts
import { convert } from 'openapi-to-postmanv2'
import fs from 'fs'
const openapiSpec = JSON.parse(fs.readFileSync('./openapi.json', 'utf8'))
convert(
{ type: 'json', data: openapiSpec },
{},
(err, conversionResult) => {
if (!conversionResult.result) {
console.error('Conversion failed:', conversionResult.reason)
return
}
const collection = conversionResult.output[0].data
fs.writeFileSync(
'./postman-collection.json',
JSON.stringify(collection, null, 2)
)
}
)
// v1/routes.ts
export const v1Routes = {
'/users': getUsersV1,
'/users/:id': getUserByIdV1,
}
// v2/routes.ts (breaking change)
export const v2Routes = {
'/users': getUsersV2, // Returns different schema
'/users/:id': getUserByIdV2,
}
// app.ts
app.use('/v1', v1Routes)
app.use('/v2', v2Routes)
// middleware/version.ts
export function versionMiddleware(req, res, next) {
const version = req.headers['api-version'] || '1.0'
if (version === '1.0') {
req.apiVersion = 'v1'
} else if (version === '2.0') {
req.apiVersion = 'v2'
} else {
return res.status(400).json({ error: 'Unsupported API version' })
}
next()
}
// tests/contract-regression.test.ts
import { describe, it, expect } from 'vitest'
import SwaggerParser from '@apidevtools/swagger-parser'
describe('API Contract Regression', () => {
it('should not introduce breaking changes', async () => {
const previousSpec = await SwaggerParser.validate('./previous-openapi.yaml')
const currentSpec = await SwaggerParser.validate('./openapi.yaml')
// Check that all previous endpoints still exist
for (const path in previousSpec.paths) {
expect(currentSpec.paths[path]).toBeDefined()
for (const method in previousSpec.paths[path]) {
expect(currentSpec.paths[path][method]).toBeDefined()
// Verify response schemas are compatible
const prevResponses = previousSpec.paths[path][method].responses
const currResponses = currentSpec.paths[path][method].responses
for (const statusCode in prevResponses) {
expect(currResponses[statusCode]).toBeDefined()
}
}
}
})
it('should maintain backward compatibility for required fields', async () => {
const previousSpec = await SwaggerParser.validate('./previous-openapi.yaml')
const currentSpec = await SwaggerParser.validate('./openapi.yaml')
for (const schemaName in previousSpec.components?.schemas) {
const prevSchema = previousSpec.components.schemas[schemaName]
const currSchema = currentSpec.components.schemas[schemaName]
if (prevSchema.required && currSchema?.required) {
// New spec must include all previously required fields
for (const field of prevSchema.required) {
expect(currSchema.required).toContain(field)
}
}
}
})
})
Contract-First Development
Versioning
Testing
Documentation
Works best with:
express-openapi-validator, swagger-parser@pact-foundation/pact@stoplight/prism-cli, json-serveropenapi-generator, swagger-codegen