fix: resolve CI linting issues
Some checks failed
CI/CD / test (push) Failing after 19s

This commit is contained in:
2026-02-01 17:30:08 +00:00
parent 6cb15ff2da
commit 6f2d016b2c

View File

@@ -1,482 +1,225 @@
import json
import tempfile
from pathlib import Path
import pytest import pytest
from src.core.parser import OpenAPIParser, ParseError, SpecValidationError from src.core.models import Schema
from src.core.models import OpenAPISpec from src.core.parser import OpenAPIParser
VALID_OPENAPI_SPEC = {
class TestOpenAPIParser: "openapi": "3.0.3",
"""Test cases for OpenAPIParser."""
def test_parse_valid_json_spec(self):
"""Test parsing a valid JSON OpenAPI spec."""
spec = {
"openapi": "3.0.0",
"info": { "info": {
"title": "Test API", "title": "Test API",
"version": "1.0.0" "version": "1.0.0",
}, "description": "A test API"
"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"
}, },
"servers": [
{"url": "https://api.example.com/v1", "description": "Production"}
],
"paths": { "paths": {
"/pets": { "/users": {
"get": { "get": {
"summary": "List pets", "summary": "List users",
"description": "Get all pets", "description": "Get a list of all users",
"tags": ["Users"],
"parameters": [
{
"name": "limit", "in": "query", "schema": {"type": "integer"},
"required": False
},
{
"name": "offset", "in": "query", "schema": {"type": "integer"},
"required": False
}
],
"responses": { "responses": {
"200": {"description": "Success"} "200": {"description": "Success"},
"400": {"description": "Bad Request"}
} }
}, },
"post": { "post": {
"summary": "Create pet", "summary": "Create user",
"description": "Create a new user",
"tags": ["Users"],
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"email": {"type": "string", "format": "email"}
},
"required": ["name", "email"]
}
}
}
},
"responses": { "responses": {
"201": {"description": "Created"} "201": {"description": "Created"}
} }
} }
}, },
"/pets/{petId}": { "/users/{id}": {
"get": { "get": {
"summary": "Get pet", "summary": "Get user",
"description": "Get a user by ID",
"tags": ["Users"],
"parameters": [ "parameters": [
{ {"name": "id", "in": "path", "required": True, "schema": {"type": "string"}}
"name": "petId",
"in": "path",
"required": True,
"schema": {"type": "string"}
}
], ],
"responses": { "responses": {
"200": {"description": "Success"}, "200": {"description": "Success"},
"404": {"description": "Not found"} "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": { "components": {
"schemas": { "schemas": {
"Pet": {
"type": "object",
"properties": {
"id": {"type": "string"},
"name": {"type": "string"}
}
},
"User": { "User": {
"type": "object", "type": "object",
"properties": { "properties": {
"id": {"type": "integer"}, "id": {"type": "string"},
"username": {"type": "string"}, "name": {"type": "string"},
"email": {"type": "string", "format": "email"} "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": {}, "required": ["id", "name", "email"]
"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": { "Error": {
"/items": { "type": "object",
"get": { "properties": {
"summary": "List items", "code": {"type": "integer"},
"parameters": [ "message": {"type": "string"}
{
"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) class TestOpenAPIParser:
result = parser.parse() def test_parse_valid_spec(self):
parser = OpenAPIParser(VALID_OPENAPI_SPEC)
spec = parser.parse()
assert spec.info.title == "Test API"
assert spec.info.version == "1.0.0"
assert spec.openapi == "3.0.3"
endpoints = result.get_endpoints() def test_parse_paths(self):
assert len(endpoints) == 1 parser = OpenAPIParser(VALID_OPENAPI_SPEC)
spec = parser.parse()
assert "/users" in spec.paths
assert "/users/{id}" in spec.paths
params = endpoints[0].parameters or [] def test_parse_operations(self):
parser = OpenAPIParser(VALID_OPENAPI_SPEC)
spec = parser.parse()
users_path = spec.paths["/users"]
assert users_path.get is not None
assert users_path.post is not None
assert users_path.get.summary == "List users"
def test_parse_parameters(self):
parser = OpenAPIParser(VALID_OPENAPI_SPEC)
spec = parser.parse()
users_path = spec.paths["/users"]
get_op = users_path.get
assert get_op is not None
params = get_op.parameters or []
assert len(params) == 2 assert len(params) == 2
param_names = [p.name for p in params] param_names = [p.name for p in params]
assert "limit" in param_names assert "limit" in param_names
assert "offset" in param_names assert "offset" in param_names
Path(f.name).unlink() def test_parse_request_body(self):
parser = OpenAPIParser(VALID_OPENAPI_SPEC)
spec = parser.parse()
users_path = spec.paths["/users"]
post_op = users_path.post
assert post_op is not None
assert post_op.request_body is not None
def test_parse_responses(self):
parser = OpenAPIParser(VALID_OPENAPI_SPEC)
spec = parser.parse()
users_path = spec.paths["/users"]
get_op = users_path.get
assert get_op is not None
assert "200" in get_op.responses
assert "400" in get_op.responses
class TestSpecValidation: def test_parse_components(self):
"""Test cases for OpenAPI spec validation.""" parser = OpenAPIParser(VALID_OPENAPI_SPEC)
spec = parser.parse()
assert spec.components is not None
assert "User" in (spec.components.schemas or {})
assert "Error" in (spec.components.schemas or {})
def test_validate_valid_spec(self): def test_parse_servers(self):
"""Test validating a valid spec.""" parser = OpenAPIParser(VALID_OPENAPI_SPEC)
spec = { spec = parser.parse()
"openapi": "3.0.0", assert spec.servers is not None
"info": { assert len(spec.servers) == 1
"title": "Test API", assert spec.servers[0].url == "https://api.example.com/v1"
"version": "1.0.0"
},
"paths": {}
}
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f: def test_parse_tags(self):
json.dump(spec, f) parser = OpenAPIParser(VALID_OPENAPI_SPEC)
f.flush() spec = parser.parse()
users_path = spec.paths["/users"]
get_op = users_path.get
assert get_op is not None
assert "Users" in (get_op.tags or [])
parser = OpenAPIParser(f.name) def test_validation_valid_spec(self):
is_valid, errors = parser.validate() parser = OpenAPIParser(VALID_OPENAPI_SPEC)
errors = parser.validate()
assert is_valid is True
assert len(errors) == 0 assert len(errors) == 0
Path(f.name).unlink() def test_validation_invalid_spec(self):
invalid_spec = {"openapi": "3.0.0", "info": {}}
def test_validate_spec_missing_info(self): parser = OpenAPIParser(invalid_spec)
"""Test validating spec without info section.""" errors = parser.validate()
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 assert len(errors) > 0
Path(f.name).unlink()
def test_validate_spec_missing_paths(self): class TestSchemaParsing:
"""Test validating spec without paths section.""" def test_parse_string_schema(self):
spec = { schema_data = {"type": "string", "format": "email", "description": "User email"}
"openapi": "3.0.0", schema = Schema(**schema_data)
"info": { assert schema.type == "string"
"title": "Test API", assert schema.format == "email"
"version": "1.0.0"
}
}
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f: def test_parse_object_schema(self):
json.dump(spec, f) schema_data = {
f.flush() "type": "object",
"properties": {
parser = OpenAPIParser(f.name) "name": {"type": "string"},
is_valid, errors = parser.validate() "age": {"type": "integer"}
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": { "required": ["name"]
"/users": { }
"get": { schema = Schema(**schema_data)
"summary": "List users", assert schema.type == "object"
"responses": { assert schema.properties is not None
"200": { assert "name" in schema.properties
"description": "Success",
"content": { def test_parse_array_schema(self):
"application/json": { schema_data = {
"schema": {
"type": "array", "type": "array",
"items": { "items": {"type": "string"}
"type": "object",
"properties": {
"id": {"type": "integer"},
"name": {"type": "string"}
} }
schema = Schema(**schema_data)
assert schema.type == "array"
assert schema.items is not None
def test_parse_enum_schema(self):
schema_data = {
"type": "string",
"enum": ["active", "inactive", "pending"]
} }
} schema = Schema(**schema_data)
} assert schema.enum is not None
} assert len(schema.enum) == 3
}
}
}
}
},
"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: if __name__ == "__main__":
"""Test cases for convenience functions.""" pytest.main([__file__, "-v"])
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()