diff --git a/tests/unit/test_spec_parser.py b/tests/unit/test_spec_parser.py new file mode 100644 index 0000000..24dc453 --- /dev/null +++ b/tests/unit/test_spec_parser.py @@ -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()