Add generators and mock server modules
This commit is contained in:
277
api_testgen/cli/main.py
Normal file
277
api_testgen/cli/main.py
Normal file
@@ -0,0 +1,277 @@
|
||||
"""CLI interface for API TestGen."""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import click
|
||||
import yaml
|
||||
|
||||
from ..core import SpecParser, AuthConfig
|
||||
from ..core.exceptions import InvalidOpenAPISpecError, UnsupportedVersionError
|
||||
from ..generators import PytestGenerator, JestGenerator, GoGenerator
|
||||
from ..mocks import MockServerGenerator
|
||||
|
||||
|
||||
@click.group()
|
||||
@click.version_option(version="0.1.0")
|
||||
@click.option(
|
||||
"--spec",
|
||||
"-s",
|
||||
type=click.Path(exists=True, file_okay=True, dir_okay=False),
|
||||
help="Path to OpenAPI specification file",
|
||||
)
|
||||
@click.option(
|
||||
"--output",
|
||||
"-o",
|
||||
type=click.Path(file_okay=False, dir_okay=True),
|
||||
help="Output directory for generated files",
|
||||
)
|
||||
@click.option(
|
||||
"--mock-url",
|
||||
default="http://localhost:4010",
|
||||
help="URL of the mock server",
|
||||
)
|
||||
@click.pass_context
|
||||
def main(
|
||||
ctx: click.Context,
|
||||
spec: str,
|
||||
output: str,
|
||||
mock_url: str,
|
||||
):
|
||||
"""API TestGen - Generate integration tests from OpenAPI specifications."""
|
||||
ctx.ensure_object(dict)
|
||||
ctx.obj["spec"] = spec
|
||||
ctx.obj["output"] = output or "./generated"
|
||||
ctx.obj["mock_url"] = mock_url
|
||||
|
||||
|
||||
@main.command("parse")
|
||||
@click.pass_context
|
||||
def parse_spec(ctx: click.Context):
|
||||
"""Parse and validate an OpenAPI specification."""
|
||||
spec_path = ctx.obj["spec"]
|
||||
|
||||
if not spec_path:
|
||||
click.echo("Error: --spec option is required", err=True)
|
||||
raise click.Abort()
|
||||
|
||||
try:
|
||||
parser = SpecParser(spec_path)
|
||||
spec = parser.load()
|
||||
|
||||
info = parser.get_info()
|
||||
endpoints = parser.get_endpoints()
|
||||
security_schemes = parser.get_security_schemes()
|
||||
|
||||
click.echo(f"API: {info['title']} v{info['version']}")
|
||||
click.echo(f"OpenAPI Version: {parser.version}")
|
||||
click.echo(f"Base Path: {parser.base_path}")
|
||||
click.echo(f"Endpoints: {len(endpoints)}")
|
||||
click.echo(f"Security Schemes: {len(security_schemes)}")
|
||||
click.echo()
|
||||
|
||||
for endpoint in endpoints:
|
||||
click.echo(f" {endpoint['method'].upper():6} {endpoint['path']}")
|
||||
|
||||
ctx.obj["parser"] = parser
|
||||
|
||||
except InvalidOpenAPISpecError as e:
|
||||
click.echo(f"Error: {e}", err=True)
|
||||
raise click.Abort()
|
||||
except UnsupportedVersionError as e:
|
||||
click.echo(f"Error: {e}", err=True)
|
||||
raise click.Abort()
|
||||
|
||||
|
||||
@main.command("generate")
|
||||
@click.argument("framework", type=click.Choice(["pytest", "jest", "go"]))
|
||||
@click.option(
|
||||
"--output-file",
|
||||
"-f",
|
||||
type=click.Path(file_okay=True, dir_okay=False),
|
||||
help="Specific output file path",
|
||||
)
|
||||
@click.option(
|
||||
"--package-name",
|
||||
default="apitest",
|
||||
help="Go package name (only for go framework)",
|
||||
)
|
||||
@click.pass_context
|
||||
def generate_tests(
|
||||
ctx: click.Context,
|
||||
framework: str,
|
||||
output_file: str,
|
||||
package_name: str,
|
||||
):
|
||||
"""Generate test files for a framework (pytest, jest, or go)."""
|
||||
spec_path = ctx.obj["spec"]
|
||||
|
||||
if not spec_path:
|
||||
click.echo("Error: --spec option is required", err=True)
|
||||
raise click.Abort()
|
||||
|
||||
output_dir = ctx.obj["output"]
|
||||
mock_url = ctx.obj["mock_url"]
|
||||
|
||||
try:
|
||||
parser = SpecParser(spec_path)
|
||||
parser.load()
|
||||
|
||||
if framework == "pytest":
|
||||
generator = PytestGenerator(parser, output_dir, mock_url)
|
||||
files = generator.generate(output_file)
|
||||
|
||||
elif framework == "jest":
|
||||
generator = JestGenerator(parser, output_dir, mock_url)
|
||||
files = generator.generate(output_file)
|
||||
|
||||
elif framework == "go":
|
||||
generator = GoGenerator(parser, output_dir, mock_url, package_name)
|
||||
files = generator.generate(output_file)
|
||||
|
||||
click.echo(f"Generated {len(files)} test file(s):")
|
||||
for f in files:
|
||||
click.echo(f" - {f}")
|
||||
|
||||
except Exception as e:
|
||||
click.echo(f"Error: {e}", err=True)
|
||||
raise click.Abort()
|
||||
|
||||
|
||||
@main.command("mock")
|
||||
@click.option(
|
||||
"--no-prism-config",
|
||||
is_flag=True,
|
||||
help="Skip generating prism-config.json",
|
||||
)
|
||||
@click.option(
|
||||
"--no-docker-compose",
|
||||
is_flag=True,
|
||||
help="Skip generating docker-compose.yml",
|
||||
)
|
||||
@click.option(
|
||||
"--no-dockerfile",
|
||||
is_flag=True,
|
||||
help="Skip generating Dockerfile",
|
||||
)
|
||||
@click.pass_context
|
||||
def generate_mock(
|
||||
ctx: click.Context,
|
||||
no_prism_config: bool,
|
||||
no_docker_compose: bool,
|
||||
no_dockerfile: bool,
|
||||
):
|
||||
"""Generate mock server configuration files."""
|
||||
spec_path = ctx.obj["spec"]
|
||||
|
||||
if not spec_path:
|
||||
click.echo("Error: --spec option is required", err=True)
|
||||
raise click.Abort()
|
||||
|
||||
output_dir = ctx.obj["output"]
|
||||
|
||||
try:
|
||||
parser = SpecParser(spec_path)
|
||||
parser.load()
|
||||
|
||||
generator = MockServerGenerator(parser, output_dir)
|
||||
|
||||
files = generator.generate(
|
||||
prism_config=not no_prism_config,
|
||||
docker_compose=not no_docker_compose,
|
||||
dockerfile=not no_dockerfile,
|
||||
)
|
||||
|
||||
click.echo(f"Generated {len(files)} mock server file(s):")
|
||||
for f in files:
|
||||
click.echo(f" - {f}")
|
||||
|
||||
except Exception as e:
|
||||
click.echo(f"Error: {e}", err=True)
|
||||
raise click.Abort()
|
||||
|
||||
|
||||
@main.command("all")
|
||||
@click.argument("framework", type=click.Choice(["pytest", "jest", "go"]))
|
||||
@click.pass_context
|
||||
def generate_all(
|
||||
ctx: click.Context,
|
||||
framework: str,
|
||||
):
|
||||
"""Generate test files and mock server configuration."""
|
||||
spec_path = ctx.obj["spec"]
|
||||
|
||||
if not spec_path:
|
||||
click.echo("Error: --spec option is required", err=True)
|
||||
raise click.Abort()
|
||||
|
||||
output_dir = ctx.obj["output"]
|
||||
mock_url = ctx.obj["mock_url"]
|
||||
|
||||
try:
|
||||
parser = SpecParser(spec_path)
|
||||
parser.load()
|
||||
|
||||
click.echo("Generating tests...")
|
||||
if framework == "pytest":
|
||||
generator = PytestGenerator(parser, output_dir, mock_url)
|
||||
files = generator.generate()
|
||||
|
||||
elif framework == "jest":
|
||||
generator = JestGenerator(parser, output_dir, mock_url)
|
||||
files = generator.generate()
|
||||
|
||||
elif framework == "go":
|
||||
generator = GoGenerator(parser, output_dir, mock_url)
|
||||
files = generator.generate()
|
||||
|
||||
click.echo(f"Generated {len(files)} test file(s)")
|
||||
|
||||
click.echo("Generating mock server configuration...")
|
||||
mock_generator = MockServerGenerator(parser, output_dir)
|
||||
mock_files = mock_generator.generate()
|
||||
|
||||
click.echo(f"Generated {len(mock_files)} mock server file(s)")
|
||||
|
||||
click.echo("\nAll files generated successfully!")
|
||||
|
||||
except Exception as e:
|
||||
click.echo(f"Error: {e}", err=True)
|
||||
raise click.Abort()
|
||||
|
||||
|
||||
@main.command("auth")
|
||||
@click.argument("scheme_name")
|
||||
@click.option("--type", "auth_type", type=click.Choice(["apiKey", "bearer", "basic"]), help="Authentication type")
|
||||
@click.option("--header", help="Header name for API key", default="X-API-Key")
|
||||
@click.option("--token", help="Bearer token or API key value")
|
||||
@click.option("--username", help="Username for Basic auth")
|
||||
@click.option("--password", help="Password for Basic auth")
|
||||
@click.pass_context
|
||||
def configure_auth(
|
||||
ctx: click.Context,
|
||||
scheme_name: str,
|
||||
auth_type: str,
|
||||
header: str,
|
||||
token: str,
|
||||
username: str,
|
||||
password: str,
|
||||
):
|
||||
"""Configure authentication for a security scheme."""
|
||||
auth_config = AuthConfig()
|
||||
|
||||
if auth_type == "apiKey":
|
||||
auth_config.add_api_key(scheme_name, header, token or "")
|
||||
elif auth_type == "bearer":
|
||||
auth_config.add_bearer(scheme_name, token or "")
|
||||
elif auth_type == "basic":
|
||||
auth_config.add_basic(scheme_name, username or "", password or "")
|
||||
|
||||
click.echo(f"Authentication scheme '{scheme_name}' configured:")
|
||||
methods = auth_config.get_all_methods()
|
||||
for name, method in methods.items():
|
||||
click.echo(f" - {name}: {method['type'].value}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user