Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:23:04 +08:00
commit 28231b02c2
12 changed files with 834 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
# Assets
Bundled resources for contract-test-validator skill
- [ ] pact_contract_template.json: A template for creating Pact consumer contracts.
- [ ] openapi_example.yaml: An example OpenAPI specification file.
- [ ] report_template.html: A template for generating HTML reports of contract validation results.
- [ ] config_template.yaml: A configuration template for setting up the contract testing environment.

View File

@@ -0,0 +1,80 @@
# Configuration for the Contract Test Validator plugin.
# General settings
general:
# Enable or disable the plugin
enabled: true
# Log level (debug, info, warning, error, critical)
log_level: info
# Directory to store temporary files and reports.
temp_dir: ./tmp/contract_tests # REPLACE_ME: Choose a suitable temporary directory
# Fail fast. Stop on first error?
fail_fast: false
# Pact settings
pact:
# Enable Pact contract testing
enabled: true
# Directory to store Pact files
pact_dir: ./pacts # REPLACE_ME: Choose a directory for pact files
# URL of the Pact Broker (optional)
broker_url: YOUR_VALUE_HERE # e.g., https://pact-broker.example.com
# Username for Pact Broker authentication (optional)
broker_username: YOUR_VALUE_HERE
# Password for Pact Broker authentication (optional)
broker_password: YOUR_VALUE_HERE
# Publish verification results to the Pact Broker
publish_verification_results: false
# Consumer version tag to use when publishing to the broker
consumer_version_tag: main # REPLACE_ME: Specify the consumer version tag
# Provider version tag to use when verifying from the broker
provider_version_tag: main # REPLACE_ME: Specify the provider version tag
# OpenAPI validation settings
openapi:
# Enable OpenAPI validation
enabled: true
# Directory containing OpenAPI specifications
spec_dir: ./openapi_specs # REPLACE_ME: Choose a directory for OpenAPI specs
# Base URL to use for validation
base_url: http://localhost:8080 # REPLACE_ME: The base URL of your API
# Breaking change detection settings
breaking_change:
# Enable breaking change detection
enabled: true
# Previous API definition file (e.g., OpenAPI spec)
previous_api_definition: ./openapi_specs/previous.yaml # REPLACE_ME: Path to previous API definition
# Current API definition file (e.g., OpenAPI spec)
current_api_definition: ./openapi_specs/current.yaml # REPLACE_ME: Path to current API definition
# Report format (text, json)
report_format: text
# Output file for breaking change report
report_file: ./reports/breaking_changes.txt # REPLACE_ME: Choose a location for report
# Custom scripts (e.g., for setup and teardown)
scripts:
# Script to run before contract tests
before_all: ./scripts/before_all.sh # REPLACE_ME: Path to setup script (optional)
# Script to run after contract tests
after_all: ./scripts/after_all.sh # REPLACE_ME: Path to teardown script (optional)
# Notification settings (e.g., Slack, email)
notifications:
# Enable notifications
enabled: false
# Type of notification (slack, email)
type: slack # or email
# Slack webhook URL (if using Slack notifications)
slack_webhook_url: YOUR_VALUE_HERE # REPLACE_ME: Your Slack webhook URL
# Email configuration (if using email notifications)
email:
sender: YOUR_VALUE_HERE # REPLACE_ME: Sender email address
recipients: # REPLACE_ME: List of recipient email addresses
- recipient1@example.com
- recipient2@example.com
smtp_server: YOUR_VALUE_HERE # REPLACE_ME: SMTP server address
smtp_port: 587
smtp_username: YOUR_VALUE_HERE # REPLACE_ME: SMTP username
smtp_password: YOUR_VALUE_HERE # REPLACE_ME: SMTP password
use_tls: true

View File

@@ -0,0 +1,237 @@
# OpenAPI Specification Example for Contract Test Validator Plugin
# This file defines the structure of an example API for testing purposes.
openapi: 3.0.0
info:
title: Example API for Contract Testing
version: 1.0.0
description: A simple API to demonstrate contract testing with Pact and OpenAPI validation.
contact:
name: REPLACE_ME - Your Name
email: REPLACE_ME - your.email@example.com
servers:
- url: https://api.example.com/v1
description: Production server
paths:
/users:
get:
summary: Get all users
description: Retrieves a list of all users.
operationId: getUsers
responses:
'200':
description: Successful operation
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
'500':
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
post:
summary: Create a new user
description: Creates a new user with the provided information.
operationId: createUser
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUserRequest'
responses:
'201':
description: User created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'400':
description: Bad request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/users/{userId}:
get:
summary: Get user by ID
description: Retrieves a specific user by their ID.
operationId: getUserById
parameters:
- in: path
name: userId
schema:
type: integer
required: true
description: The ID of the user to retrieve
responses:
'200':
description: Successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'404':
description: User not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
put:
summary: Update user by ID
description: Updates a specific user by their ID.
operationId: updateUserById
parameters:
- in: path
name: userId
schema:
type: integer
required: true
description: The ID of the user to update
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateUserRequest'
responses:
'200':
description: Successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'400':
description: Bad request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: User not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
delete:
summary: Delete user by ID
description: Deletes a specific user by their ID.
operationId: deleteUserById
parameters:
- in: path
name: userId
schema:
type: integer
required: true
description: The ID of the user to delete
responses:
'204':
description: User deleted successfully
'404':
description: User not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
components:
schemas:
User:
type: object
properties:
id:
type: integer
description: The user ID.
name:
type: string
description: The user's name.
email:
type: string
format: email
description: The user's email address.
created_at:
type: string
format: date-time
description: The date and time the user was created.
required:
- id
- name
- email
CreateUserRequest:
type: object
properties:
name:
type: string
description: The user's name.
email:
type: string
format: email
description: The user's email address.
required:
- name
- email
UpdateUserRequest:
type: object
properties:
name:
type: string
description: The user's name.
email:
type: string
format: email
description: The user's email address.
Error:
type: object
properties:
code:
type: integer
description: The error code.
message:
type: string
description: The error message.
required:
- code
- message
securitySchemes:
bearerAuth: # arbitrary name for the security scheme
type: http
scheme: bearer
bearerFormat: JWT # optional, for documentation purposes only
security:
- bearerAuth: [] # applies the "bearerAuth" security scheme to all operations

View File

@@ -0,0 +1,93 @@
{
"_comment": "Pact Consumer Contract Template",
"consumer": {
"name": "MyConsumer"
},
"provider": {
"name": "MyProvider"
},
"interactions": [
{
"_comment": "Example Interaction - GET request",
"description": "a request to get a product",
"request": {
"method": "GET",
"path": "/products/123",
"headers": {
"Accept": "application/json"
},
"_comment": "Optional: Query parameters"
"query": ""
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json; charset=utf-8"
},
"body": {
"id": 123,
"name": "Example Product",
"description": "A sample product for testing purposes",
"price": 25.99,
"inStock": true,
"_comment": "Optional: Matchers for flexible validation"
"_matchers": {
"$.id": {
"match": "type"
},
"$.price": {
"match": "decimal"
}
}
}
},
"providerStates": [
{
"name": "Product 123 exists",
"_comment": "Optional: Define state setup for provider"
"params": {
"productId": 123
}
}
]
},
{
"_comment": "Example Interaction - POST request",
"description": "a request to create a new product",
"request": {
"method": "POST",
"path": "/products",
"headers": {
"Content-Type": "application/json"
},
"body": {
"name": "New Product",
"description": "A new product to be created",
"price": 10.00
}
},
"response": {
"status": 201,
"headers": {
"Content-Type": "application/json; charset=utf-8"
},
"body": {
"id": 456,
"name": "New Product",
"description": "A new product to be created",
"price": 10.00
}
},
"providerStates": [
{
"name": "The system is in a valid state to create products"
}
]
}
],
"metadata": {
"pactSpecification": {
"version": "3.0.0"
}
}
}

View File

@@ -0,0 +1,161 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Contract Validation Report</title>
<style>
/* Inline CSS for styling */
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f4f4f4;
color: #333;
}
h1 {
text-align: center;
margin-bottom: 30px;
color: #007bff;
}
.report-container {
max-width: 900px;
margin: 0 auto;
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.section {
margin-bottom: 20px;
border-bottom: 1px solid #eee;
padding-bottom: 15px;
}
.section h2 {
color: #007bff;
margin-bottom: 10px;
}
.section p {
line-height: 1.6;
}
.status-indicator {
display: inline-block;
padding: 5px 10px;
border-radius: 5px;
font-weight: bold;
color: white;
}
.status-success {
background-color: #28a745; /* Green */
}
.status-failure {
background-color: #dc3545; /* Red */
}
.status-warning {
background-color: #ffc107; /* Yellow */
color: #212529;
}
.status-unknown {
background-color: #6c757d; /* Gray */
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
font-weight: bold;
}
pre {
background-color: #f8f8f8;
padding: 10px;
border: 1px solid #ddd;
overflow-x: auto;
white-space: pre-wrap;
}
/* Responsive Design */
@media (max-width: 600px) {
.report-container {
padding: 10px;
}
table {
width: 100%;
}
th, td {
padding: 6px;
font-size: 0.9em;
}
}
</style>
</head>
<body>
<div class="report-container">
<h1>Contract Validation Report</h1>
<!-- Summary Section -->
<div class="section">
<h2>Summary</h2>
<p>Report generated on: {{report_date}}</p>
<p>Total tests: {{total_tests}}</p>
<p>Passed: <span class="status-indicator status-success">{{passed_tests}}</span></p>
<p>Failed: <span class="status-indicator status-failure">{{failed_tests}}</span></p>
<p>Warnings: <span class="status-indicator status-warning">{{warning_tests}}</span></p>
</div>
<!-- Pact Verification Results -->
<div class="section">
<h2>Pact Verification Results</h2>
{{pact_results}}
</div>
<!-- OpenAPI Validation Results -->
<div class="section">
<h2>OpenAPI Validation Results</h2>
{{openapi_results}}
</div>
<!-- Breaking Change Detection Results -->
<div class="section">
<h2>Breaking Change Detection Results</h2>
{{breaking_change_results}}
</div>
<!-- Raw Output Section -->
<div class="section">
<h2>Raw Output</h2>
<pre>{{raw_output}}</pre>
</div>
<!-- Additional Notes Section -->
<div class="section">
<h2>Additional Notes</h2>
<p>{{additional_notes}}</p>
</div>
</div>
</body>
</html>