fix: add Gitea Actions CI workflow for automated testing
This commit is contained in:
1
tests/unit/__init__.py
Normal file
1
tests/unit/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Unit tests for API TestGen."""
|
||||
182
tests/unit/test_auth.py
Normal file
182
tests/unit/test_auth.py
Normal file
@@ -0,0 +1,182 @@
|
||||
"""Unit tests for the auth configuration module."""
|
||||
import pytest
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from api_testgen.core.auth import AuthConfig, AuthType
|
||||
from api_testgen.core.exceptions import AuthConfigError, MissingSecuritySchemeError
|
||||
|
||||
|
||||
class TestAuthConfig:
|
||||
"""Tests for AuthConfig class."""
|
||||
|
||||
def test_add_api_key(self):
|
||||
"""Test adding API key authentication."""
|
||||
auth = AuthConfig()
|
||||
auth.add_api_key("test_api_key", header_name="X-API-Key", api_key="test123")
|
||||
|
||||
method = auth.get_auth_method("test_api_key")
|
||||
|
||||
assert method is not None
|
||||
assert method["type"] == AuthType.API_KEY
|
||||
assert method["header_name"] == "X-API-Key"
|
||||
assert method["api_key"] == "test123"
|
||||
|
||||
def test_add_bearer_token(self):
|
||||
"""Test adding Bearer token authentication."""
|
||||
auth = AuthConfig()
|
||||
auth.add_bearer("test_bearer", token="abc123", token_prefix="Bearer")
|
||||
|
||||
method = auth.get_auth_method("test_bearer")
|
||||
|
||||
assert method is not None
|
||||
assert method["type"] == AuthType.BEARER
|
||||
assert method["token"] == "abc123"
|
||||
assert method["token_prefix"] == "Bearer"
|
||||
|
||||
def test_add_basic_auth(self):
|
||||
"""Test adding Basic authentication."""
|
||||
auth = AuthConfig()
|
||||
auth.add_basic("test_basic", username="user", password="pass")
|
||||
|
||||
method = auth.get_auth_method("test_basic")
|
||||
|
||||
assert method is not None
|
||||
assert method["type"] == AuthType.BASIC
|
||||
assert method["username"] == "user"
|
||||
assert method["password"] == "pass"
|
||||
|
||||
def test_method_chaining(self):
|
||||
"""Test that add methods return self for chaining."""
|
||||
auth = AuthConfig()
|
||||
|
||||
result = auth.add_api_key("key1")
|
||||
assert result is auth
|
||||
|
||||
result = auth.add_bearer("key2")
|
||||
assert result is auth
|
||||
|
||||
result = auth.add_basic("key3")
|
||||
assert result is auth
|
||||
|
||||
def test_get_all_methods(self):
|
||||
"""Test getting all configured auth methods."""
|
||||
auth = AuthConfig()
|
||||
auth.add_api_key("api_key")
|
||||
auth.add_bearer("bearer")
|
||||
|
||||
methods = auth.get_all_methods()
|
||||
|
||||
assert len(methods) == 2
|
||||
assert "api_key" in methods
|
||||
assert "bearer" in methods
|
||||
|
||||
def test_get_headers_api_key(self):
|
||||
"""Test getting headers for API key auth."""
|
||||
auth = AuthConfig()
|
||||
auth.add_api_key("test", header_name="X-Custom-Key", api_key="mykey")
|
||||
|
||||
headers = auth.get_headers("test")
|
||||
|
||||
assert headers["X-Custom-Key"] == "mykey"
|
||||
|
||||
def test_get_headers_bearer(self):
|
||||
"""Test getting headers for Bearer auth."""
|
||||
auth = AuthConfig()
|
||||
auth.add_bearer("test", token="mytoken", token_prefix="Bearer")
|
||||
|
||||
headers = auth.get_headers("test")
|
||||
|
||||
assert headers["Authorization"] == "Bearer mytoken"
|
||||
|
||||
def test_get_headers_basic(self):
|
||||
"""Test getting headers for Basic auth."""
|
||||
import base64
|
||||
|
||||
auth = AuthConfig()
|
||||
auth.add_basic("test", username="user", password="pass")
|
||||
|
||||
headers = auth.get_headers("test")
|
||||
|
||||
expected = base64.b64encode(b"user:pass").decode()
|
||||
assert headers["Authorization"] == f"Basic {expected}"
|
||||
|
||||
def test_get_headers_unconfigured_scheme_raises_error(self):
|
||||
"""Test that getting headers for unconfigured scheme raises error."""
|
||||
auth = AuthConfig()
|
||||
|
||||
with pytest.raises(AuthConfigError):
|
||||
auth.get_headers("nonexistent")
|
||||
|
||||
def test_build_from_spec(self):
|
||||
"""Test building auth config from OpenAPI security schemes."""
|
||||
auth = AuthConfig()
|
||||
|
||||
security_schemes = {
|
||||
"ApiKeyAuth": {"type": "apiKey", "name": "X-API-Key", "in": "header"},
|
||||
"BearerAuth": {"type": "http", "scheme": "bearer"},
|
||||
"BasicAuth": {"type": "http", "scheme": "basic"},
|
||||
}
|
||||
|
||||
security_requirements = [
|
||||
{"ApiKeyAuth": []},
|
||||
{"BearerAuth": []},
|
||||
]
|
||||
|
||||
auth.build_from_spec(security_schemes, security_requirements)
|
||||
|
||||
assert auth.get_auth_method("ApiKeyAuth") is not None
|
||||
assert auth.get_auth_method("BearerAuth") is not None
|
||||
assert auth.get_auth_method("BasicAuth") is None
|
||||
|
||||
def test_build_from_spec_missing_scheme_raises_error(self):
|
||||
"""Test that missing security scheme raises error."""
|
||||
auth = AuthConfig()
|
||||
|
||||
security_schemes = {
|
||||
"ApiKeyAuth": {"type": "apiKey", "name": "X-API-Key", "in": "header"},
|
||||
}
|
||||
|
||||
security_requirements = [
|
||||
{"MissingScheme": []},
|
||||
]
|
||||
|
||||
with pytest.raises(MissingSecuritySchemeError):
|
||||
auth.build_from_spec(security_schemes, security_requirements)
|
||||
|
||||
def test_generate_pytest_auth_code(self):
|
||||
"""Test generating pytest authentication code."""
|
||||
auth = AuthConfig()
|
||||
auth.add_api_key("test_key", header_name="X-Api-Key", api_key="key123")
|
||||
|
||||
code = auth.generate_auth_code("test_key", "pytest")
|
||||
|
||||
assert "X-Api-Key" in code
|
||||
assert "key123" in code
|
||||
|
||||
def test_generate_jest_auth_code(self):
|
||||
"""Test generating Jest authentication code."""
|
||||
auth = AuthConfig()
|
||||
auth.add_bearer("test_bearer", token="token123")
|
||||
|
||||
code = auth.generate_auth_code("test_bearer", "jest")
|
||||
|
||||
assert "Authorization" in code
|
||||
assert "token123" in code
|
||||
|
||||
def test_generate_go_auth_code(self):
|
||||
"""Test generating Go authentication code."""
|
||||
auth = AuthConfig()
|
||||
auth.add_api_key("test_key", header_name="X-Api-Key")
|
||||
|
||||
code = auth.generate_auth_code("test_key", "go")
|
||||
|
||||
assert "X-Api-Key" in code
|
||||
|
||||
def test_generate_auth_code_unconfigured_scheme(self):
|
||||
"""Test generating auth code for unconfigured scheme returns empty."""
|
||||
auth = AuthConfig()
|
||||
|
||||
code = auth.generate_auth_code("nonexistent", "pytest")
|
||||
|
||||
assert code == ""
|
||||
223
tests/unit/test_generators.py
Normal file
223
tests/unit/test_generators.py
Normal file
@@ -0,0 +1,223 @@
|
||||
"""Unit tests for the generator modules."""
|
||||
import pytest
|
||||
import tempfile
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from api_testgen.core import SpecParser
|
||||
from api_testgen.generators import PytestGenerator, JestGenerator, GoGenerator
|
||||
from api_testgen.mocks import MockServerGenerator
|
||||
|
||||
|
||||
class TestPytestGenerator:
|
||||
"""Tests for PytestGenerator class."""
|
||||
|
||||
@pytest.fixture
|
||||
def parser(self, sample_openapi_spec):
|
||||
"""Create a spec parser with sample spec."""
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
|
||||
import yaml
|
||||
yaml.dump(sample_openapi_spec, f)
|
||||
parser = SpecParser(Path(f.name))
|
||||
parser.load()
|
||||
return parser
|
||||
|
||||
def test_generate_creates_file(self, parser, tmp_path):
|
||||
"""Test that generate creates a test file."""
|
||||
generator = PytestGenerator(parser, output_dir=str(tmp_path))
|
||||
files = generator.generate()
|
||||
|
||||
assert len(files) == 1
|
||||
assert files[0].exists()
|
||||
assert files[0].suffix == ".py"
|
||||
|
||||
def test_generate_content_contains_tests(self, parser, tmp_path):
|
||||
"""Test that generated file contains test functions."""
|
||||
generator = PytestGenerator(parser, output_dir=str(tmp_path))
|
||||
files = generator.generate()
|
||||
|
||||
content = files[0].read_text()
|
||||
|
||||
assert "test_" in content
|
||||
assert "BASE_URL" in content
|
||||
assert "def test_" in content
|
||||
|
||||
def test_generate_with_custom_output_file(self, parser, tmp_path):
|
||||
"""Test generating to a specific file path."""
|
||||
generator = PytestGenerator(parser, output_dir=str(tmp_path))
|
||||
files = generator.generate(output_file=str(tmp_path / "custom_test.py"))
|
||||
|
||||
assert len(files) == 1
|
||||
assert files[0].name == "custom_test.py"
|
||||
|
||||
def test_generate_endpoint_test(self, parser):
|
||||
"""Test generating a single endpoint test."""
|
||||
generator = PytestGenerator(parser)
|
||||
|
||||
test_code = generator.generate_endpoint_tests("/pets", "get")
|
||||
|
||||
assert "test_" in test_code
|
||||
assert "requests.get" in test_code
|
||||
|
||||
def test_generate_test_name(self, parser):
|
||||
"""Test test name generation."""
|
||||
generator = PytestGenerator(parser)
|
||||
|
||||
name = generator._generate_test_name({
|
||||
"path": "/pets",
|
||||
"method": "get",
|
||||
"summary": "List all pets",
|
||||
})
|
||||
|
||||
assert name == "get_pets" or "pets" in name
|
||||
|
||||
|
||||
class TestJestGenerator:
|
||||
"""Tests for JestGenerator class."""
|
||||
|
||||
@pytest.fixture
|
||||
def parser(self, sample_openapi_spec):
|
||||
"""Create a spec parser with sample spec."""
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
|
||||
import yaml
|
||||
yaml.dump(sample_openapi_spec, f)
|
||||
parser = SpecParser(Path(f.name))
|
||||
parser.load()
|
||||
return parser
|
||||
|
||||
def test_generate_creates_file(self, parser, tmp_path):
|
||||
"""Test that generate creates a test file."""
|
||||
generator = JestGenerator(parser, output_dir=str(tmp_path))
|
||||
files = generator.generate()
|
||||
|
||||
assert len(files) == 1
|
||||
assert files[0].exists()
|
||||
assert files[0].suffix == ".js"
|
||||
|
||||
def test_generate_content_contains_describe(self, parser, tmp_path):
|
||||
"""Test that generated file contains describe blocks."""
|
||||
generator = JestGenerator(parser, output_dir=str(tmp_path))
|
||||
files = generator.generate()
|
||||
|
||||
content = files[0].read_text()
|
||||
|
||||
assert "describe" in content
|
||||
expect_in = "expect" in content or "toContain" in content or "toBe" in content
|
||||
assert expect_in
|
||||
|
||||
def test_generate_endpoint_test(self, parser):
|
||||
"""Test generating a single endpoint test."""
|
||||
generator = JestGenerator(parser)
|
||||
|
||||
test_code = generator.generate_endpoint_tests("/pets", "get")
|
||||
|
||||
assert "describe" in test_code or "it(" in test_code
|
||||
|
||||
|
||||
class TestGoGenerator:
|
||||
"""Tests for GoGenerator class."""
|
||||
|
||||
@pytest.fixture
|
||||
def parser(self, sample_openapi_spec):
|
||||
"""Create a spec parser with sample spec."""
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
|
||||
import yaml
|
||||
yaml.dump(sample_openapi_spec, f)
|
||||
parser = SpecParser(Path(f.name))
|
||||
parser.load()
|
||||
return parser
|
||||
|
||||
def test_generate_creates_file(self, parser, tmp_path):
|
||||
"""Test that generate creates a test file."""
|
||||
generator = GoGenerator(parser, output_dir=str(tmp_path))
|
||||
files = generator.generate()
|
||||
|
||||
assert len(files) == 1
|
||||
assert files[0].exists()
|
||||
assert files[0].name.endswith("_test.go")
|
||||
|
||||
def test_generate_content_contains_tests(self, parser, tmp_path):
|
||||
"""Test that generated file contains test functions."""
|
||||
generator = GoGenerator(parser, output_dir=str(tmp_path))
|
||||
files = generator.generate()
|
||||
|
||||
content = files[0].read_text()
|
||||
|
||||
assert "func Test" in content
|
||||
assert "http.Client" in content or "http.NewRequest" in content
|
||||
|
||||
def test_generate_with_custom_package(self, parser, tmp_path):
|
||||
"""Test generating with custom package name."""
|
||||
generator = GoGenerator(parser, output_dir=str(tmp_path), package_name="custompkg")
|
||||
files = generator.generate()
|
||||
|
||||
content = files[0].read_text()
|
||||
|
||||
assert "package custompkg" in content
|
||||
|
||||
|
||||
class TestMockServerGenerator:
|
||||
"""Tests for MockServerGenerator class."""
|
||||
|
||||
@pytest.fixture
|
||||
def parser(self, sample_openapi_spec):
|
||||
"""Create a spec parser with sample spec."""
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
|
||||
import yaml
|
||||
yaml.dump(sample_openapi_spec, f)
|
||||
parser = SpecParser(Path(f.name))
|
||||
parser.load()
|
||||
return parser
|
||||
|
||||
def test_generate_prism_config(self, parser, tmp_path):
|
||||
"""Test generating Prism configuration."""
|
||||
generator = MockServerGenerator(parser, output_dir=str(tmp_path))
|
||||
file = generator.generate_prism_config()
|
||||
|
||||
assert file.exists()
|
||||
assert file.name == "prism-config.json"
|
||||
|
||||
content = file.read_text()
|
||||
import json
|
||||
config = json.loads(content)
|
||||
|
||||
assert "mock" in config
|
||||
assert "port" in config["mock"]
|
||||
|
||||
def test_generate_docker_compose(self, parser, tmp_path):
|
||||
"""Test generating Docker Compose configuration."""
|
||||
generator = MockServerGenerator(parser, output_dir=str(tmp_path))
|
||||
file = generator.generate_docker_compose()
|
||||
|
||||
assert file.exists()
|
||||
assert file.name == "docker-compose.yml"
|
||||
|
||||
content = file.read_text()
|
||||
|
||||
assert "services" in content
|
||||
assert "mock-server" in content
|
||||
|
||||
def test_generate_dockerfile(self, parser, tmp_path):
|
||||
"""Test generating Dockerfile."""
|
||||
generator = MockServerGenerator(parser, output_dir=str(tmp_path))
|
||||
file = generator.generate_dockerfile()
|
||||
|
||||
assert file.exists()
|
||||
assert file.name == "Dockerfile"
|
||||
|
||||
content = file.read_text()
|
||||
|
||||
assert "FROM" in content
|
||||
assert "prism" in content.lower()
|
||||
|
||||
def test_generate_all(self, parser, tmp_path):
|
||||
"""Test generating all mock server files."""
|
||||
generator = MockServerGenerator(parser, output_dir=str(tmp_path))
|
||||
files = generator.generate()
|
||||
|
||||
assert len(files) == 3
|
||||
|
||||
file_names = [f.name for f in files]
|
||||
assert "prism-config.json" in file_names
|
||||
assert "docker-compose.yml" in file_names
|
||||
assert "Dockerfile" in file_names
|
||||
173
tests/unit/test_spec_parser.py
Normal file
173
tests/unit/test_spec_parser.py
Normal file
@@ -0,0 +1,173 @@
|
||||
"""Unit tests for the spec parser module."""
|
||||
import pytest
|
||||
import tempfile
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from api_testgen.core import SpecParser
|
||||
from api_testgen.core.exceptions import InvalidOpenAPISpecError, UnsupportedVersionError
|
||||
|
||||
|
||||
class TestSpecParser:
|
||||
"""Tests for SpecParser class."""
|
||||
|
||||
def test_load_valid_openapi_30_spec(self, temp_spec_file):
|
||||
"""Test loading a valid OpenAPI 3.0 specification."""
|
||||
parser = SpecParser(temp_spec_file)
|
||||
spec = parser.load()
|
||||
|
||||
assert spec is not None
|
||||
assert parser.version == "3.0.0"
|
||||
assert parser.base_path == ""
|
||||
assert len(parser.servers) == 1
|
||||
|
||||
def test_load_valid_json_spec(self, temp_json_spec_file):
|
||||
"""Test loading a valid JSON OpenAPI specification."""
|
||||
parser = SpecParser(temp_json_spec_file)
|
||||
spec = parser.load()
|
||||
|
||||
assert spec is not None
|
||||
assert parser.version == "3.0.0"
|
||||
|
||||
def test_get_info(self, temp_spec_file):
|
||||
"""Test extracting API info from spec."""
|
||||
parser = SpecParser(temp_spec_file)
|
||||
parser.load()
|
||||
|
||||
info = parser.get_info()
|
||||
|
||||
assert info["title"] == "Test Pet Store API"
|
||||
assert info["version"] == "1.0.0"
|
||||
assert "sample" in info["description"].lower()
|
||||
|
||||
def test_get_paths(self, temp_spec_file):
|
||||
"""Test extracting paths from spec."""
|
||||
parser = SpecParser(temp_spec_file)
|
||||
parser.load()
|
||||
|
||||
paths = parser.get_paths()
|
||||
|
||||
assert "/pets" in paths
|
||||
assert "/pets/{petId}" in paths
|
||||
|
||||
def test_get_endpoints(self, temp_spec_file):
|
||||
"""Test extracting endpoints from spec."""
|
||||
parser = SpecParser(temp_spec_file)
|
||||
parser.load()
|
||||
|
||||
endpoints = parser.get_endpoints()
|
||||
|
||||
assert len(endpoints) == 4
|
||||
assert any(e["method"] == "get" and e["path"] == "/pets" for e in endpoints)
|
||||
assert any(e["method"] == "post" and e["path"] == "/pets" for e in endpoints)
|
||||
assert any(e["method"] == "get" and e["path"] == "/pets/{petId}" for e in endpoints)
|
||||
assert any(e["method"] == "delete" and e["path"] == "/pets/{petId}" for e in endpoints)
|
||||
|
||||
def test_get_security_schemes(self, temp_spec_file):
|
||||
"""Test extracting security schemes from spec."""
|
||||
parser = SpecParser(temp_spec_file)
|
||||
parser.load()
|
||||
|
||||
schemes = parser.get_security_schemes()
|
||||
|
||||
assert "ApiKeyAuth" in schemes
|
||||
assert "BearerAuth" in schemes
|
||||
assert schemes["ApiKeyAuth"]["type"] == "apiKey"
|
||||
assert schemes["BearerAuth"]["type"] == "http"
|
||||
assert schemes["BearerAuth"]["scheme"] == "bearer"
|
||||
|
||||
def test_get_definitions(self, temp_spec_file):
|
||||
"""Test extracting schema definitions from spec."""
|
||||
parser = SpecParser(temp_spec_file)
|
||||
parser.load()
|
||||
|
||||
definitions = parser.get_definitions()
|
||||
|
||||
assert "Pet" in definitions
|
||||
assert definitions["Pet"]["type"] == "object"
|
||||
|
||||
def test_endpoint_with_parameters(self, temp_spec_file):
|
||||
"""Test endpoint parameter extraction."""
|
||||
parser = SpecParser(temp_spec_file)
|
||||
parser.load()
|
||||
|
||||
endpoints = parser.get_endpoints()
|
||||
|
||||
pets_endpoint = next(e for e in endpoints if e["path"] == "/pets" and e["method"] == "get")
|
||||
|
||||
assert len(pets_endpoint["parameters"]) == 2
|
||||
assert any(p["name"] == "limit" and p["in"] == "query" for p in pets_endpoint["parameters"])
|
||||
assert any(p["name"] == "status" and p["in"] == "query" for p in pets_endpoint["parameters"])
|
||||
|
||||
def test_endpoint_with_path_parameters(self, temp_spec_file):
|
||||
"""Test path parameter extraction."""
|
||||
parser = SpecParser(temp_spec_file)
|
||||
parser.load()
|
||||
|
||||
endpoints = parser.get_endpoints()
|
||||
|
||||
pet_endpoint = next(e for e in endpoints if e["path"] == "/pets/{petId}" and e["method"] == "get")
|
||||
|
||||
assert len(pet_endpoint["parameters"]) == 1
|
||||
param = pet_endpoint["parameters"][0]
|
||||
assert param["name"] == "petId"
|
||||
assert param["in"] == "path"
|
||||
assert param["required"] is True
|
||||
|
||||
def test_endpoint_with_request_body(self, temp_spec_file):
|
||||
"""Test request body extraction for OpenAPI 3.0."""
|
||||
parser = SpecParser(temp_spec_file)
|
||||
parser.load()
|
||||
|
||||
endpoints = parser.get_endpoints()
|
||||
|
||||
create_endpoint = next(e for e in endpoints if e["path"] == "/pets" and e["method"] == "post")
|
||||
|
||||
assert create_endpoint["request_body"] is not None
|
||||
assert create_endpoint["request_body"]["required"] is True
|
||||
|
||||
def test_endpoint_with_responses(self, temp_spec_file):
|
||||
"""Test response extraction."""
|
||||
parser = SpecParser(temp_spec_file)
|
||||
parser.load()
|
||||
|
||||
endpoints = parser.get_endpoints()
|
||||
|
||||
pets_endpoint = next(e for e in endpoints if e["path"] == "/pets" and e["method"] == "get")
|
||||
|
||||
assert "200" in pets_endpoint["responses"]
|
||||
assert pets_endpoint["responses"]["200"]["description"] == "A list of pets"
|
||||
|
||||
def test_to_dict(self, temp_spec_file):
|
||||
"""Test dictionary representation of parsed spec."""
|
||||
parser = SpecParser(temp_spec_file)
|
||||
parser.load()
|
||||
|
||||
spec_dict = parser.to_dict()
|
||||
|
||||
assert "version" in spec_dict
|
||||
assert "info" in spec_dict
|
||||
assert "paths" in spec_dict
|
||||
assert "endpoints" in spec_dict
|
||||
assert "security_schemes" in spec_dict
|
||||
assert "definitions" in spec_dict
|
||||
|
||||
def test_nonexistent_file_raises_error(self):
|
||||
"""Test that nonexistent file raises InvalidOpenAPISpecError."""
|
||||
parser = SpecParser("/nonexistent/path/spec.yaml")
|
||||
|
||||
with pytest.raises(InvalidOpenAPISpecError):
|
||||
parser.load()
|
||||
|
||||
def test_invalid_yaml_raises_error(self):
|
||||
"""Test that invalid YAML raises InvalidOpenAPISpecError."""
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
|
||||
f.write("invalid: yaml: content: [[[")
|
||||
f.flush()
|
||||
|
||||
parser = SpecParser(Path(f.name))
|
||||
|
||||
with pytest.raises(InvalidOpenAPISpecError):
|
||||
parser.load()
|
||||
|
||||
Path(f.name).unlink()
|
||||
Reference in New Issue
Block a user