import json import tempfile from pathlib import Path import pytest from src.core.parser import OpenAPIParser, ParseError, SpecValidationError from src.core.models import OpenAPISpec class TestOpenAPIParser: """Test cases for OpenAPIParser.""" def test_parse_valid_json_spec(self): """Test parsing a valid JSON 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() parser = OpenAPIParser(f.name) result = parser.parse() assert isinstance(result, OpenAPISpec) assert result.openapi == "3.0.0" assert result.info.title == "Test API" assert result.info.version == "1.0.0" Path(f.name).unlink() def test_parse_valid_yaml_spec(self): """Test parsing a valid YAML OpenAPI spec.""" 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() parser = OpenAPIParser(f.name) result = parser.parse() assert isinstance(result, OpenAPISpec) assert result.openapi == "3.0.0" assert result.info.title == "Test API" Path(f.name).unlink() def test_parse_missing_file(self): """Test parsing a non-existent file.""" parser = OpenAPIParser("nonexistent.json") with pytest.raises(ParseError) as exc_info: parser.parse() assert "not found" in str(exc_info.value).lower() def test_parse_invalid_json(self): """Test parsing invalid JSON file.""" with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f: f.write("{ invalid json }") f.flush() parser = OpenAPIParser(f.name) with pytest.raises(ParseError): parser.parse() Path(f.name).unlink() def test_parse_invalid_yaml(self): """Test parsing invalid YAML file.""" with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: f.write(" - invalid: yaml: content:") f.flush() parser = OpenAPIParser(f.name) with pytest.raises(ParseError): parser.parse() Path(f.name).unlink() def test_parse_unsupported_format(self): """Test parsing unsupported file format.""" with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f: f.write("test content") f.flush() parser = OpenAPIParser(f.name) with pytest.raises(ParseError) as exc_info: parser.parse() assert "unsupported" in str(exc_info.value).lower() Path(f.name).unlink() def test_parse_spec_with_endpoints(self): """Test parsing spec with endpoints.""" spec = { "openapi": "3.0.0", "info": { "title": "Pet Store API", "version": "1.0.0" }, "paths": { "/pets": { "get": { "summary": "List pets", "description": "Get all pets", "responses": { "200": {"description": "Success"} } }, "post": { "summary": "Create pet", "responses": { "201": {"description": "Created"} } } }, "/pets/{petId}": { "get": { "summary": "Get pet", "parameters": [ { "name": "petId", "in": "path", "required": True, "schema": {"type": "string"} } ], "responses": { "200": {"description": "Success"}, "404": {"description": "Not found"} } } } } } with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f: json.dump(spec, f) f.flush() parser = OpenAPIParser(f.name) result = parser.parse() endpoints = result.get_endpoints() assert len(endpoints) == 3 endpoint_paths = [e.path for e in endpoints] assert "/pets" in endpoint_paths assert "/pets/{petId}" in endpoint_paths endpoint_methods = [e.method for e in endpoints] assert "GET" in endpoint_methods assert "POST" in endpoint_methods Path(f.name).unlink() def test_parse_spec_with_schemas(self): """Test parsing spec with schema definitions.""" spec = { "openapi": "3.0.0", "info": { "title": "Test API", "version": "1.0.0" }, "paths": {}, "components": { "schemas": { "Pet": { "type": "object", "properties": { "id": {"type": "string"}, "name": {"type": "string"} } }, "User": { "type": "object", "properties": { "id": {"type": "integer"}, "username": {"type": "string"}, "email": {"type": "string", "format": "email"} } } } } } with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f: json.dump(spec, f) f.flush() parser = OpenAPIParser(f.name) result = parser.parse() schemas = result.get_schemas() assert "Pet" in schemas assert "User" in schemas Path(f.name).unlink() def test_parse_spec_with_tags(self): """Test parsing spec with tag definitions.""" spec = { "openapi": "3.0.0", "info": { "title": "Test API", "version": "1.0.0" }, "paths": {}, "tags": [ {"name": "pets", "description": "Pet operations"}, {"name": "users", "description": "User operations"} ] } with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f: json.dump(spec, f) f.flush() parser = OpenAPIParser(f.name) result = parser.parse() tags = result.get_tags() assert len(tags) == 2 tag_names = [t.name for t in tags] assert "pets" in tag_names assert "users" in tag_names Path(f.name).unlink() def test_parse_spec_with_parameters(self): """Test parsing spec with endpoint parameters.""" spec = { "openapi": "3.0.0", "info": { "title": "Test API", "version": "1.0.0" }, "paths": { "/items": { "get": { "summary": "List items", "parameters": [ { "name": "limit", "in": "query", "schema": {"type": "integer"}, "description": "Max items to return" }, { "name": "offset", "in": "query", "schema": {"type": "integer"}, "description": "Items to skip" } ], "responses": {"200": {"description": "Success"}} } } } } with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f: json.dump(spec, f) f.flush() parser = OpenAPIParser(f.name) result = parser.parse() endpoints = result.get_endpoints() assert len(endpoints) == 1 params = endpoints[0].parameters or [] assert len(params) == 2 param_names = [p.name for p in params] assert "limit" in param_names assert "offset" in param_names Path(f.name).unlink() class TestSpecValidation: """Test cases for OpenAPI spec validation.""" def test_validate_valid_spec(self): """Test validating a valid 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() parser = OpenAPIParser(f.name) is_valid, errors = parser.validate() assert is_valid is True assert len(errors) == 0 Path(f.name).unlink() def test_validate_spec_missing_info(self): """Test validating spec without info section.""" spec = { "openapi": "3.0.0", "paths": {} } with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f: json.dump(spec, f) f.flush() parser = OpenAPIParser(f.name) is_valid, errors = parser.validate() assert is_valid is False assert len(errors) > 0 Path(f.name).unlink() def test_validate_spec_missing_paths(self): """Test validating spec without paths section.""" spec = { "openapi": "3.0.0", "info": { "title": "Test API", "version": "1.0.0" } } with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f: json.dump(spec, f) f.flush() parser = OpenAPIParser(f.name) is_valid, errors = parser.validate() assert is_valid is False Path(f.name).unlink() class TestParseWithExamples: """Test cases for parsing with generated examples.""" def test_parse_with_examples(self): """Test parsing spec and generating examples.""" spec = { "openapi": "3.0.0", "info": { "title": "Test API", "version": "1.0.0" }, "paths": { "/users": { "get": { "summary": "List users", "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "object", "properties": { "id": {"type": "integer"}, "name": {"type": "string"} } } } } } } } } } }, "components": { "schemas": { "User": { "type": "object", "properties": { "id": {"type": "integer"}, "name": {"type": "string", "format": "email"} } } } } } with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f: json.dump(spec, f) f.flush() parser = OpenAPIParser(f.name) result = parser.parse_with_examples() assert "spec" in result assert "endpoints" in result assert "schemas" in result assert len(result["endpoints"]) == 1 assert len(result["schemas"]) == 1 Path(f.name).unlink() class TestConvenienceFunctions: """Test cases for convenience functions.""" def test_parse_spec_file(self): """Test parse_spec_file convenience function.""" from src.core.parser import parse_spec_file 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() result = parse_spec_file(f.name) assert isinstance(result, OpenAPISpec) assert result.info.title == "Test API" Path(f.name).unlink() def test_validate_spec_file(self): """Test validate_spec_file convenience function.""" from src.core.parser import validate_spec_file 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() is_valid, errors = validate_spec_file(f.name) assert is_valid is True Path(f.name).unlink()