"""Tests for CLI commands.""" import pytest import json import tempfile from pathlib import Path from click.testing import CliRunner from src.cli.commands import cli @pytest.fixture def runner(): """Create a Click CLI runner.""" return CliRunner() @pytest.fixture def sample_openapi_yaml(): """Sample OpenAPI specification in YAML format.""" return """ openapi: \"3.0.0\" info: title: Sample API version: \"1.0.0\" paths: /users: get: summary: Get all users parameters: - name: page in: query schema: type: integer - name: limit in: query schema: type: integer responses: '200': description: Successful response content: application/json: schema: type: array items: type: object properties: id: type: integer name: type: string email: type: string format: email createdAt: type: string format: date-time /users/{userId}: get: summary: Get user by ID parameters: - name: userId in: path required: true schema: type: integer responses: '200': description: Successful response content: application/json: schema: type: object properties: id: type: integer name: type: string email: type: string format: email '404': description: User not found content: application/json: schema: type: object properties: error: type: string message: type: string put: summary: Update user parameters: - name: userId in: path required: true schema: type: integer requestBody: required: true content: application/json: schema: type: object properties: name: type: string email: type: string format: email required: - name responses: '200': description: User updated content: application/json: schema: type: object properties: id: type: integer name: type: string /posts: post: summary: Create a post requestBody: required: true content: application/json: schema: type: object properties: title: type: string content: type: string authorId: type: integer responses: '201': description: Post created content: application/json: schema: type: object properties: id: type: integer title: type: string content: type: string authorId: type: integer publishedAt: type: string format: date-time components: schemas: User: type: object properties: id: type: integer name: type: string email: type: string format: email createdAt: type: string format: date-time Post: type: object properties: id: type: integer title: type: string content: type: string authorId: type: integer publishedAt: type: string format: date-time Error: type: object properties: error: type: string message: type: string """ @pytest.fixture def temp_spec_file(sample_openapi_yaml, tmp_path): """Create a temporary OpenAPI spec file.""" spec_file = tmp_path / "openapi.yaml" spec_file.write_text(sample_openapi_yaml) return str(spec_file) class TestCLIServe: """Tests for serve command.""" def test_serve_validate_only(self, temp_spec_file, runner): """Test serve command with validate-only option.""" result = runner.invoke(cli, ["--spec-file", temp_spec_file, "serve", "--validate-only"]) assert result.exit_code == 0 assert "valid" in result.output.lower() or "success" in result.output.lower() def test_serve_missing_spec_file(self, tmp_path, runner): """Test serve command with missing spec file.""" result = runner.invoke(cli, ["--spec-file", str(tmp_path / "nonexistent.yaml"), "serve", "--validate-only"]) assert result.exit_code != 0 assert "not found" in result.output.lower() def test_serve_with_port(self, temp_spec_file, runner): """Test serve command with custom port.""" result = runner.invoke(cli, ["--spec-file", temp_spec_file, "serve", "--validate-only", "--port", "3000"]) assert result.exit_code == 0 def test_serve_with_delay(self, temp_spec_file, runner): """Test serve command with delay.""" result = runner.invoke(cli, ["--spec-file", temp_spec_file, "serve", "--validate-only", "--delay", "100"]) assert result.exit_code == 0 class TestCLIGenerate: """Tests for generate command.""" def test_generate_lists_endpoints(self, temp_spec_file, runner): """Test generate command without path lists endpoints.""" result = runner.invoke(cli, ["--spec-file", temp_spec_file, "generate"]) assert result.exit_code == 0 assert "/users" in result.output def test_generate_specific_endpoint(self, temp_spec_file, runner): """Test generate command for specific endpoint.""" result = runner.invoke(cli, ["--spec-file", temp_spec_file, "generate", "/users", "get"]) assert result.exit_code == 0 assert "status_code" in result.output or "200" in result.output def test_generate_with_format_json(self, temp_spec_file, runner): """Test generate command with JSON output format.""" result = runner.invoke(cli, ["--spec-file", temp_spec_file, "generate", "/users", "get", "--format", "json"]) assert result.exit_code == 0 assert "{" in result.output def test_generate_with_format_yaml(self, temp_spec_file, runner): """Test generate command with YAML output format.""" result = runner.invoke(cli, ["--spec-file", temp_spec_file, "generate", "/users", "get", "--format", "yaml"]) assert result.exit_code == 0 def test_generate_path_not_found(self, temp_spec_file, runner): """Test generate command with invalid path.""" result = runner.invoke(cli, ["--spec-file", temp_spec_file, "generate", "/nonexistent", "get"]) assert result.exit_code != 0 assert "not found" in result.output.lower() class TestCLIValidate: """Tests for validate command.""" def test_validate_spec(self, temp_spec_file, runner): """Test validate command for valid spec.""" result = runner.invoke(cli, ["--spec-file", temp_spec_file, "validate"]) assert result.exit_code == 0 assert "valid" in result.output.lower() def test_validate_missing_file(self, tmp_path, runner): """Test validate command with missing file.""" result = runner.invoke(cli, ["--spec-file", str(tmp_path / "nonexistent.yaml"), "validate"]) assert result.exit_code != 0 class TestCLIRoutes: """Tests for routes command.""" def test_routes_lists_all(self, temp_spec_file, runner): """Test routes command lists all routes.""" result = runner.invoke(cli, ["--spec-file", temp_spec_file, "routes"]) assert result.exit_code == 0 assert "/users" in result.output assert "/posts" in result.output class TestCLIInfo: """Tests for info command.""" def test_info_displays_spec_info(self, temp_spec_file, runner): """Test info command displays spec information.""" result = runner.invoke(cli, ["--spec-file", temp_spec_file, "info"]) assert result.exit_code == 0 assert "Sample API" in result.output assert "1.0.0" in result.output assert "Paths" in result.output class TestCLITest: """Tests for test command.""" def test_test_endpoint(self, temp_spec_file, runner): """Test test command for specific endpoint.""" result = runner.invoke(cli, ["--spec-file", temp_spec_file, "test", "/users", "--method", "get"]) assert result.exit_code == 0 assert "Status" in result.output or "200" in result.output def test_test_with_query_params(self, temp_spec_file, runner): """Test test command with query parameters.""" result = runner.invoke(cli, ["--spec-file", temp_spec_file, "test", "/users", "--method", "get", "--query", "page=1"]) assert result.exit_code == 0 def test_test_with_body(self, temp_spec_file, runner): """Test test command with request body.""" body = json.dumps({"title": "Test Post", "content": "Content"}) result = runner.invoke(cli, [ "--spec-file", temp_spec_file, "test", "/posts", "--method", "post", "--body", body ]) assert result.exit_code == 0 def test_test_missing_path(self, temp_spec_file, runner): """Test test command without path.""" result = runner.invoke(cli, ["--spec-file", temp_spec_file, "test"]) assert result.exit_code != 0 assert "path" in result.output.lower() class TestCLIDocker: """Tests for docker command.""" def test_docker_builds_image(self, runner): """Test docker command builds image.""" result = runner.invoke(cli, ["docker", "--no-build"]) assert result.exit_code == 0 assert "docker run" in result.output def test_docker_with_custom_image_name(self, runner): """Test docker command with custom image name.""" result = runner.invoke(cli, ["docker", "--no-build", "--image-name", "my-mock-server"]) assert result.exit_code == 0 assert "my-mock-server" in result.output class TestCLIVersion: """Tests for version option.""" def test_version_flag(self, runner): """Test --version flag displays version.""" result = runner.invoke(cli, ["--version"]) assert result.exit_code == 0 assert "0.1.0" in result.output or "openapi-mock-generator" in result.output class TestCLIHelp: """Tests for help option.""" def test_help_displays_commands(self, runner): """Test --help displays available commands.""" result = runner.invoke(cli, ["--help"]) assert result.exit_code == 0 assert "serve" in result.output assert "generate" in result.output assert "validate" in result.output assert "routes" in result.output assert "info" in result.output