fix: resolve CI linting issues
Some checks failed
CI/CD / test (push) Has been cancelled

This commit is contained in:
2026-02-01 17:30:07 +00:00
parent c1f1349931
commit d44dde5f7a

View File

@@ -1,565 +1,94 @@
import json
import os
import tempfile
from pathlib import Path
from unittest.mock import MagicMock, patch
import pytest import pytest
from click.testing import CliRunner from click.testing import CliRunner
from src.cli import main, serve, generate, validate, search from src.cli import generate, main, search, serve, validate
class TestCLIMain:
"""Test cases for the main CLI entry point."""
def test_main_help(self):
"""Test that main command shows help."""
runner = CliRunner() runner = CliRunner()
result = runner.invoke(main, ["--help"])
@pytest.fixture
def sample_spec_path(tmp_path):
spec = {
"openapi": "3.0.3",
"info": {"title": "Test API", "version": "1.0.0"},
"paths": {
"/users": {
"get": {
"summary": "List users",
"description": "Get all users",
"tags": ["Users"],
"responses": {"200": {"description": "Success"}}
}
},
"/users/{id}": {
"get": {
"summary": "Get user",
"description": "Get a user by ID",
"tags": ["Users"],
"parameters": [
{"name": "id", "in": "path", "required": True, "schema": {"type": "string"}}
],
"responses": {"200": {"description": "Success"}}
}
}
}
}
path = tmp_path / "openapi.json"
import json
path.write_text(json.dumps(spec))
return str(path)
class TestCLI:
def test_main_help(self):
result = runner.invoke(main, ["--help"])
assert result.exit_code == 0 assert result.exit_code == 0
assert "LocalAPI Docs" in result.output assert "LocalAPI Docs" in result.output
assert "serve" in result.output
assert "generate" in result.output
assert "validate" in result.output
assert "search" in result.output
def test_main_verbose_flag(self):
"""Test verbose flag parsing."""
runner = CliRunner()
result = runner.invoke(main, ["--help"])
def test_serve_help(self):
result = runner.invoke(serve, ["--help"])
assert result.exit_code == 0 assert result.exit_code == 0
assert "serve" in result.output
def test_generate_help(self):
result = runner.invoke(generate, ["--help"])
assert result.exit_code == 0
assert "generate" in result.output
def test_validate_help(self):
result = runner.invoke(validate, ["--help"])
assert result.exit_code == 0
assert "validate" in result.output
def test_search_help(self):
result = runner.invoke(search, ["--help"])
assert result.exit_code == 0
assert "search" in result.output
class TestValidateCommand: class TestValidateCommand:
"""Test cases for the validate command.""" def test_validate_valid_spec(self, sample_spec_path):
result = runner.invoke(validate, [sample_spec_path])
def test_validate_valid_spec(self):
"""Test validating a valid OpenAPI spec."""
spec = {
"openapi": "3.0.0",
"info": {
"title": "Test API",
"version": "1.0.0"
},
"paths": {}
}
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
json.dump(spec, f)
f.flush()
runner = CliRunner()
result = runner.invoke(validate, [f.name])
assert result.exit_code == 0 assert result.exit_code == 0
assert "valid" in result.output.lower() assert "Valid OpenAPI spec" in result.output
assert "Test API" in result.output
os.unlink(f.name)
def test_validate_invalid_spec(self):
"""Test validating an invalid OpenAPI spec."""
spec = {
"openapi": "3.0.0",
"info": {
"title": "Test API"
}
}
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
json.dump(spec, f)
f.flush()
runner = CliRunner()
result = runner.invoke(validate, [f.name])
assert result.exit_code == 1
assert "invalid" in result.output.lower() or "error" in result.output.lower()
os.unlink(f.name)
def test_validate_missing_file(self):
"""Test validating a non-existent file."""
runner = CliRunner()
result = runner.invoke(validate, ["nonexistent.json"])
def test_validate_nonexistent_file(self):
result = runner.invoke(validate, ["/nonexistent/path.json"])
assert result.exit_code != 0 assert result.exit_code != 0
assert "not found" in result.output.lower() or "error" in result.output.lower()
def test_validate_yaml_spec(self):
"""Test validating a YAML spec file."""
spec_content = """
openapi: 3.0.0
info:
title: Test API
version: 1.0.0
paths: {}
"""
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
f.write(spec_content)
f.flush()
runner = CliRunner()
result = runner.invoke(validate, [f.name])
assert result.exit_code == 0
os.unlink(f.name)
def test_validate_json_output(self):
"""Test validate command with JSON output."""
spec = {
"openapi": "3.0.0",
"info": {
"title": "Test API",
"version": "1.0.0"
},
"paths": {}
}
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
json.dump(spec, f)
f.flush()
runner = CliRunner()
result = runner.invoke(validate, [f.name, "--json"])
assert result.exit_code == 0
assert "valid" in result.output.lower()
os.unlink(f.name)
class TestGenerateCommand:
"""Test cases for the generate command."""
def test_generate_html(self):
"""Test generating HTML documentation."""
spec = {
"openapi": "3.0.0",
"info": {
"title": "Test API",
"version": "1.0.0",
"description": "A test API"
},
"paths": {
"/users": {
"get": {
"summary": "List users",
"description": "Get a list of users",
"responses": {
"200": {
"description": "Success"
}
}
}
}
}
}
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
json.dump(spec, f)
f.flush()
runner = CliRunner()
with runner.isolated_filesystem():
result = runner.invoke(generate, [f.name, "--format", "html"])
assert result.exit_code == 0
assert "api-docs.html" in result.output.lower() or "generated" in result.output.lower()
os.unlink(f.name)
def test_generate_markdown(self):
"""Test generating Markdown documentation."""
spec = {
"openapi": "3.0.0",
"info": {
"title": "Test API",
"version": "1.0.0"
},
"paths": {}
}
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
json.dump(spec, f)
f.flush()
runner = CliRunner()
with runner.isolated_filesystem():
result = runner.invoke(generate, [f.name, "--format", "markdown"])
assert result.exit_code == 0
os.unlink(f.name)
def test_generate_json(self):
"""Test generating JSON documentation."""
spec = {
"openapi": "3.0.0",
"info": {
"title": "Test API",
"version": "1.0.0"
},
"paths": {}
}
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
json.dump(spec, f)
f.flush()
runner = CliRunner()
with runner.isolated_filesystem():
result = runner.invoke(generate, [f.name, "--format", "json"])
assert result.exit_code == 0
os.unlink(f.name)
def test_generate_with_output_path(self):
"""Test generating with custom output path."""
spec = {
"openapi": "3.0.0",
"info": {
"title": "Test API",
"version": "1.0.0"
},
"paths": {}
}
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
json.dump(spec, f)
f.flush()
runner = CliRunner()
with runner.isolated_filesystem():
result = runner.invoke(generate, [f.name, "--format", "html", "--output", "custom.html"])
assert result.exit_code == 0
assert os.path.exists("custom.html")
os.unlink(f.name)
def test_generate_with_all_formats(self):
"""Test generating all output formats."""
spec = {
"openapi": "3.0.0",
"info": {
"title": "Test API",
"version": "1.0.0"
},
"paths": {}
}
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
json.dump(spec, f)
f.flush()
runner = CliRunner()
with runner.isolated_filesystem():
result = runner.invoke(generate, [f.name, "--format", "all"])
assert result.exit_code == 0
os.unlink(f.name)
class TestSearchCommand: class TestSearchCommand:
"""Test cases for the search command.""" def test_search_query(self, sample_spec_path):
result = runner.invoke(search, [sample_spec_path, "users"])
def test_search_basic(self):
"""Test basic search functionality."""
spec = {
"openapi": "3.0.0",
"info": {
"title": "Test API",
"version": "1.0.0"
},
"paths": {
"/users": {
"get": {
"summary": "List all users",
"description": "Get a list of all users in the system",
"responses": {
"200": {"description": "Success"}
}
}
}
}
}
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
json.dump(spec, f)
f.flush()
runner = CliRunner()
result = runner.invoke(search, [f.name, "users"])
assert result.exit_code == 0 assert result.exit_code == 0
assert "users" in result.output.lower() or "found" in result.output.lower()
os.unlink(f.name) def test_search_no_query(self, sample_spec_path):
result = runner.invoke(search, [sample_spec_path])
def test_search_no_results(self):
"""Test search with no results."""
spec = {
"openapi": "3.0.0",
"info": {
"title": "Test API",
"version": "1.0.0"
},
"paths": {
"/users": {
"get": {
"summary": "List users",
"responses": {"200": {"description": "Success"}}
}
}
}
}
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
json.dump(spec, f)
f.flush()
runner = CliRunner()
result = runner.invoke(search, [f.name, "nonexistentendpoint12345"])
assert result.exit_code == 0 assert result.exit_code == 0
assert "no results" in result.output.lower() assert "query" in result.output.lower()
os.unlink(f.name)
def test_search_json_output(self):
"""Test search with JSON output."""
spec = {
"openapi": "3.0.0",
"info": {
"title": "Test API",
"version": "1.0.0"
},
"paths": {
"/users": {
"get": {
"summary": "List users",
"responses": {"200": {"description": "Success"}}
}
}
}
}
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
json.dump(spec, f)
f.flush()
runner = CliRunner()
result = runner.invoke(search, [f.name, "users", "--json"])
assert result.exit_code == 0
assert "query" in result.output.lower() or "results" in result.output.lower()
os.unlink(f.name)
def test_search_with_limit(self):
"""Test search with result limit."""
spec = {
"openapi": "3.0.0",
"info": {
"title": "Test API",
"version": "1.0.0"
},
"paths": {
"users": {
"get": {
"summary": "List users",
"responses": {"200": {"description": "Success"}}
}
}
}
}
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
json.dump(spec, f)
f.flush()
runner = CliRunner()
result = runner.invoke(search, [f.name, "users", "--limit", "5"])
assert result.exit_code == 0
os.unlink(f.name)
class TestServeCommand: if __name__ == "__main__":
"""Test cases for the serve command.""" pytest.main([__file__, "-v"])
def test_serve_invalid_spec(self):
"""Test serve command with invalid spec."""
spec = {"invalid": "spec"}
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
json.dump(spec, f)
f.flush()
runner = CliRunner()
result = runner.invoke(serve, [f.name, "--port", "9999", "--no-browser"])
assert result.exit_code == 1
assert "error" in result.output.lower() or "invalid" in result.output.lower()
os.unlink(f.name)
def test_serve_missing_file(self):
"""Test serve command with missing file."""
runner = CliRunner()
result = runner.invoke(serve, ["nonexistent.json"])
assert result.exit_code == 1
def test_serve_with_custom_host_port(self):
"""Test serve command with custom host and port."""
spec = {
"openapi": "3.0.0",
"info": {
"title": "Test API",
"version": "1.0.0"
},
"paths": {}
}
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
json.dump(spec, f)
f.flush()
runner = CliRunner()
with runner.isolated_filesystem():
result = runner.invoke(serve, [f.name, "--host", "0.0.0.0", "--port", "9998", "--no-browser"])
assert result.exit_code != 0 or "9998" in result.output or "running" in result.output.lower()
os.unlink(f.name)
class TestCLIIntegration:
"""Integration tests for CLI commands."""
def test_full_parse_and_generate(self):
"""Test complete parse and generate workflow."""
spec = {
"openapi": "3.0.0",
"info": {
"title": "Pet Store API",
"version": "1.0.0",
"description": "A sample pet store API"
},
"paths": {
"/pets": {
"get": {
"summary": "List all pets",
"description": "Returns a list of all pets",
"tags": ["pets"],
"parameters": [
{
"name": "limit",
"in": "query",
"schema": {"type": "integer"},
"description": "Maximum number of pets to return"
}
],
"responses": {
"200": {
"description": "A list of pets",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {"type": "object"}
}
}
}
}
}
},
"post": {
"summary": "Create a pet",
"tags": ["pets"],
"requestBody": {
"required": True,
"content": {
"application/json": {
"schema": {"type": "object"}
}
}
},
"responses": {
"201": {"description": "Pet created"}
}
}
},
"/pets/{petId}": {
"get": {
"summary": "Get a pet",
"tags": ["pets"],
"parameters": [
{
"name": "petId",
"in": "path",
"required": True,
"schema": {"type": "string"}
}
],
"responses": {
"200": {"description": "Pet found"},
"404": {"description": "Pet not found"}
}
},
"delete": {
"summary": "Delete a pet",
"tags": ["pets"],
"parameters": [
{
"name": "petId",
"in": "path",
"required": True,
"schema": {"type": "string"}
}
],
"responses": {
"204": {"description": "Pet deleted"}
}
}
}
},
"components": {
"schemas": {
"Pet": {
"type": "object",
"properties": {
"id": {"type": "string"},
"name": {"type": "string"},
"type": {"type": "string"}
}
}
}
}
}
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
json.dump(spec, f)
f.flush()
runner = CliRunner()
validate_result = runner.invoke(validate, [f.name])
assert validate_result.exit_code == 0
with runner.isolated_filesystem():
generate_result = runner.invoke(generate, [f.name, "--format", "html"])
assert generate_result.exit_code == 0
search_result = runner.invoke(search, [f.name, "pet"])
assert search_result.exit_code == 0
os.unlink(f.name)