- Remove unused imports across all generator files - Remove unused variables (spec, url_params, query_params, test_name) - Fix f-strings without placeholders in auth.py and go.py - Fix duplicate BASIC auth handling with wrong indentation - Add missing pytest fixtures (sample_openapi_spec, temp_spec_file, temp_json_spec_file) - Add missing TemplateRenderError import to generator files
274 lines
7.6 KiB
Python
274 lines
7.6 KiB
Python
"""CLI interface for API TestGen."""
|
|
|
|
import click
|
|
|
|
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)
|
|
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()
|