Add generators and mock server modules
This commit is contained in:
278
api_testgen/mocks/generator.py
Normal file
278
api_testgen/mocks/generator.py
Normal file
@@ -0,0 +1,278 @@
|
||||
"""Mock server generator for Prism/OpenAPI mock support."""
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from ..core import SpecParser
|
||||
|
||||
|
||||
class MockServerGenerator:
|
||||
"""Generate Prism/OpenAPI mock server configurations."""
|
||||
|
||||
DEFAULT_PORT = 4010
|
||||
DEFAULT_HOST = "0.0.0.0"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
spec_parser: SpecParser,
|
||||
output_dir: str = ".",
|
||||
):
|
||||
"""Initialize the mock server generator.
|
||||
|
||||
Args:
|
||||
spec_parser: The OpenAPI specification parser.
|
||||
output_dir: Directory for generated configuration files.
|
||||
"""
|
||||
self.spec_parser = spec_parser
|
||||
self.output_dir = Path(output_dir)
|
||||
|
||||
def generate(
|
||||
self,
|
||||
prism_config: bool = True,
|
||||
docker_compose: bool = True,
|
||||
dockerfile: bool = True,
|
||||
) -> List[Path]:
|
||||
"""Generate mock server configuration files.
|
||||
|
||||
Args:
|
||||
prism_config: Whether to generate prism-config.json.
|
||||
docker_compose: Whether to generate docker-compose.yml.
|
||||
dockerfile: Whether to generate Dockerfile.
|
||||
|
||||
Returns:
|
||||
List of generated file paths.
|
||||
"""
|
||||
self.output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
generated_files = []
|
||||
|
||||
if prism_config:
|
||||
generated_files.append(self.generate_prism_config())
|
||||
|
||||
if docker_compose:
|
||||
generated_files.append(self.generate_docker_compose())
|
||||
|
||||
if dockerfile:
|
||||
generated_files.append(self.generate_dockerfile())
|
||||
|
||||
return generated_files
|
||||
|
||||
def generate_prism_config(self, output_file: Optional[str] = None) -> Path:
|
||||
"""Generate Prism mock server configuration.
|
||||
|
||||
Args:
|
||||
output_file: Optional output file path.
|
||||
|
||||
Returns:
|
||||
Path to the generated file.
|
||||
"""
|
||||
if output_file:
|
||||
output_path = Path(output_file)
|
||||
else:
|
||||
output_path = self.output_dir / "prism-config.json"
|
||||
|
||||
config = {
|
||||
"mock": {
|
||||
"host": self.DEFAULT_HOST,
|
||||
"port": self.DEFAULT_PORT,
|
||||
"cors": {
|
||||
"enabled": True,
|
||||
"allowOrigin": "*",
|
||||
"allowMethods": ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"],
|
||||
"allowHeaders": ["Content-Type", "Authorization", "X-API-Key"],
|
||||
},
|
||||
"Validation": {
|
||||
"request": True,
|
||||
"response": True,
|
||||
},
|
||||
},
|
||||
"logging": {
|
||||
"level": "info",
|
||||
"format": "json",
|
||||
},
|
||||
}
|
||||
|
||||
output_path.write_text(json.dumps(config, indent=2))
|
||||
return output_path
|
||||
|
||||
def generate_docker_compose(self, output_file: Optional[str] = None) -> Path:
|
||||
"""Generate Docker Compose configuration for mock server.
|
||||
|
||||
Args:
|
||||
output_file: Optional output file path.
|
||||
|
||||
Returns:
|
||||
Path to the generated file.
|
||||
"""
|
||||
if output_file:
|
||||
output_path = Path(output_file)
|
||||
else:
|
||||
output_path = self.output_dir / "docker-compose.yml"
|
||||
|
||||
spec_info = self.spec_parser.get_info()
|
||||
|
||||
compose_content = f'''version: '3.8'
|
||||
|
||||
services:
|
||||
mock-server:
|
||||
image: stoplight/prism:latest
|
||||
container_name: "{spec_info['title']}-mock-server"
|
||||
command: >
|
||||
mock
|
||||
--spec /app/openapi.yaml
|
||||
--port {self.DEFAULT_PORT}
|
||||
--host {self.DEFAULT_HOST}
|
||||
ports:
|
||||
- "{self.DEFAULT_PORT}:{self.DEFAULT_PORT}"
|
||||
volumes:
|
||||
- ./:/app
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-q", "--spider", f"http://localhost:{self.DEFAULT_PORT}/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
mock-server-https:
|
||||
image: stoplight/prism:latest
|
||||
container_name: "{spec_info['title']}-mock-server-https"
|
||||
command: >
|
||||
mock
|
||||
--spec /app/openapi.yaml
|
||||
--port {self.DEFAULT_PORT + 1}
|
||||
--host {self.DEFAULT_HOST}
|
||||
ports:
|
||||
- "{self.DEFAULT_PORT + 1}:{self.DEFAULT_PORT + 1}"
|
||||
volumes:
|
||||
- ./:/app
|
||||
restart: unless-stopped
|
||||
'''
|
||||
|
||||
output_path.write_text(compose_content)
|
||||
return output_path
|
||||
|
||||
def generate_dockerfile(self, output_file: Optional[str] = None) -> Path:
|
||||
"""Generate Dockerfile for mock server.
|
||||
|
||||
Args:
|
||||
output_file: Optional output file path.
|
||||
|
||||
Returns:
|
||||
Path to the generated file.
|
||||
"""
|
||||
if output_file:
|
||||
output_path = Path(output_file)
|
||||
else:
|
||||
output_path = self.output_dir / "Dockerfile"
|
||||
|
||||
spec_info = self.spec_parser.get_info()
|
||||
|
||||
dockerfile_content = f'''FROM stoplight/prism:latest
|
||||
|
||||
LABEL maintainer="developer@example.com"
|
||||
LABEL description="Mock server for {spec_info['title']} API"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY openapi.yaml .
|
||||
|
||||
EXPOSE {self.DEFAULT_PORT}
|
||||
|
||||
CMD ["mock", "--spec", "openapi.yaml", "--port", "{self.DEFAULT_PORT}", "--host", "0.0.0.0"]
|
||||
'''
|
||||
|
||||
output_path.write_text(dockerfile_content)
|
||||
return output_path
|
||||
|
||||
def generate_start_script(self, output_file: Optional[str] = None) -> Path:
|
||||
"""Generate shell script to start mock server.
|
||||
|
||||
Args:
|
||||
output_file: Optional output file path.
|
||||
|
||||
Returns:
|
||||
Path to the generated file.
|
||||
"""
|
||||
if output_file:
|
||||
output_path = Path(output_file)
|
||||
else:
|
||||
output_path = self.output_dir / "start-mock-server.sh"
|
||||
|
||||
script_content = f'''#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${{BASH_SOURCE[0]}}")" && pwd)"
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
PORT={self.DEFAULT_PORT}
|
||||
HOST="0.0.0.0"
|
||||
|
||||
echo "Starting mock server for API..."
|
||||
echo "Mock server will be available at: http://localhost:$PORT"
|
||||
|
||||
docker compose up -d mock-server
|
||||
|
||||
echo "Mock server started successfully!"
|
||||
echo "To stop: docker compose down"
|
||||
'''
|
||||
|
||||
output_path.write_text(script_content)
|
||||
output_path.chmod(0o755)
|
||||
return output_path
|
||||
|
||||
def generate_health_check(self) -> Path:
|
||||
"""Generate health check endpoint configuration.
|
||||
|
||||
Returns:
|
||||
Path to the generated file.
|
||||
"""
|
||||
output_path = self.output_dir / "health-check.json"
|
||||
|
||||
spec_info = self.spec_parser.get_info()
|
||||
|
||||
health_check = {
|
||||
"openapi": self.spec_parser.version,
|
||||
"info": {
|
||||
"title": f"{spec_info['title']} - Health Check",
|
||||
"version": spec_info["version"],
|
||||
"description": "Health check endpoint for the mock server",
|
||||
},
|
||||
"paths": {
|
||||
"/health": {
|
||||
"get": {
|
||||
"summary": "Health check endpoint",
|
||||
"description": "Returns the health status of the mock server",
|
||||
"operationId": "healthCheck",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Mock server is healthy",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["healthy"],
|
||||
},
|
||||
"api": {
|
||||
"type": "string",
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
output_path.write_text(json.dumps(health_check, indent=2))
|
||||
return output_path
|
||||
Reference in New Issue
Block a user