mirror of
https://github.com/imartinez/privateGPT.git
synced 2026-07-17 01:48:03 +00:00
206 lines
7.1 KiB
Python
206 lines
7.1 KiB
Python
from typing import Any
|
|
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
|
|
from private_gpt.settings.settings import settings
|
|
|
|
|
|
def _get_openapi_spec(test_client: TestClient) -> dict[str, Any]:
|
|
prefix_path = settings().server.root_path
|
|
response = test_client.get(f"{prefix_path}/openapi.json")
|
|
assert (
|
|
response.status_code == 200
|
|
), f"Failed to fetch OpenAPI schema: {response.status_code}"
|
|
spec: dict[str, Any] = response.json()
|
|
return spec
|
|
|
|
|
|
def test_all_non_hidden_endpoints_have_basic_properties(
|
|
test_client: TestClient,
|
|
) -> None:
|
|
errors: list[str] = []
|
|
|
|
def check_doc(check: bool, message: str) -> None:
|
|
if not check:
|
|
errors.append(message)
|
|
|
|
spec = _get_openapi_spec(test_client)
|
|
paths: dict[str, Any] | None = spec.get("paths")
|
|
|
|
if paths is None:
|
|
pytest.fail("OpenAPI spec is missing 'paths' section")
|
|
|
|
for path, path_item in paths.items():
|
|
if not isinstance(path_item, dict):
|
|
continue
|
|
|
|
for method, operation in path_item.items():
|
|
if not isinstance(operation, dict):
|
|
continue
|
|
|
|
hidden_config: dict[str, Any] | None = operation.get("hidden")
|
|
if hidden_config and hidden_config.get("value") is True:
|
|
continue
|
|
|
|
check_doc(
|
|
"tags" in operation, f"Path {path}, method {method} is missing tags"
|
|
)
|
|
check_doc(
|
|
"summary" in operation,
|
|
f"Path {path}, method {method} is missing summary",
|
|
)
|
|
check_doc(
|
|
"description" in operation,
|
|
f"Path {path}, method {method} is missing description",
|
|
)
|
|
|
|
responses: dict[str, Any] | None = operation.get("responses")
|
|
check_doc(
|
|
responses is not None,
|
|
f"Path {path}, method {method} is missing responses",
|
|
)
|
|
|
|
if responses:
|
|
for status_code, response in responses.items():
|
|
if not isinstance(response, dict):
|
|
continue
|
|
|
|
check_doc(
|
|
"description" in response,
|
|
f"Path {path}, method {method} is missing response description for status code {status_code}",
|
|
)
|
|
|
|
response_desc: str | None = response.get("description")
|
|
check_doc(
|
|
response_desc is not None and response_desc.strip() != "",
|
|
f"Path {path}, method {method} has empty response description for status code {status_code}",
|
|
)
|
|
|
|
request_body: dict[str, Any] | None = operation.get("requestBody")
|
|
mutating_methods: list[str] = ["post", "put", "patch"]
|
|
|
|
if method.lower() in mutating_methods:
|
|
check_doc(
|
|
request_body is not None,
|
|
f"Path {path}, method {method} is missing requestBody",
|
|
)
|
|
|
|
if request_body:
|
|
check_doc(
|
|
"description" in request_body,
|
|
f"Path {path}, method {method} is missing requestBody description",
|
|
)
|
|
|
|
request_body_desc: str | None = request_body.get("description")
|
|
check_doc(
|
|
request_body_desc is not None and request_body_desc.strip() != "",
|
|
f"Path {path}, method {method} has empty requestBody description",
|
|
)
|
|
|
|
if errors:
|
|
pytest.fail("OpenAPI spec validation failed:\n" + "\n".join(errors))
|
|
|
|
|
|
def test_all_schemas_have_required_properties(test_client: TestClient) -> None:
|
|
errors: list[str] = []
|
|
|
|
def check_schema(check: bool, message: str) -> None:
|
|
if not check:
|
|
errors.append(message)
|
|
|
|
def is_multipart_form_schema(schema_name: str) -> bool:
|
|
return schema_name.startswith("Body_") and (
|
|
"_post" in schema_name.lower()
|
|
or "_put" in schema_name.lower()
|
|
or "_patch" in schema_name.lower()
|
|
)
|
|
|
|
response = test_client.get("/openapi.json")
|
|
assert (
|
|
response.status_code == 200
|
|
), f"Failed to fetch OpenAPI schema: {response.status_code}"
|
|
|
|
spec: dict[str, Any] = response.json()
|
|
components: dict[str, Any] | None = spec.get("components")
|
|
|
|
if components is None:
|
|
pytest.fail("OpenAPI spec is missing 'components' section")
|
|
|
|
schemas: dict[str, Any] | None = components.get("schemas")
|
|
|
|
if schemas is None:
|
|
pytest.fail("OpenAPI spec is missing 'schemas' section")
|
|
|
|
for schema_name, schema_def in schemas.items():
|
|
if not isinstance(schema_def, dict):
|
|
continue
|
|
|
|
if "error" in schema_name.lower():
|
|
continue
|
|
|
|
if is_multipart_form_schema(schema_name):
|
|
# Skip multipart form body schemas - they can't have descriptions
|
|
continue
|
|
|
|
check_schema(
|
|
"description" in schema_def, f"Schema {schema_name} is missing description"
|
|
)
|
|
|
|
description: str | None = schema_def.get("description")
|
|
check_schema(
|
|
description is not None and description.strip() != "",
|
|
f"Schema {schema_name} has empty description",
|
|
)
|
|
|
|
check_schema(
|
|
"type" in schema_def
|
|
or "$ref" in schema_def
|
|
or "allOf" in schema_def
|
|
or "oneOf" in schema_def
|
|
or "anyOf" in schema_def,
|
|
f"Schema {schema_name} is missing type definition",
|
|
)
|
|
|
|
properties: dict[str, Any] | None = schema_def.get("properties")
|
|
if properties:
|
|
for prop_name, prop_def in properties.items():
|
|
if not isinstance(prop_def, dict):
|
|
continue
|
|
|
|
check_schema(
|
|
"type" in prop_def
|
|
or "$ref" in prop_def
|
|
or "allOf" in prop_def
|
|
or "oneOf" in prop_def
|
|
or "anyOf" in prop_def,
|
|
f"Schema {schema_name}, property {prop_name} is missing type definition",
|
|
)
|
|
|
|
check_schema(
|
|
"description" in prop_def,
|
|
f"Schema {schema_name}, property {prop_name} is missing description",
|
|
)
|
|
|
|
prop_description: str | None = prop_def.get("description")
|
|
check_schema(
|
|
prop_description is not None and prop_description.strip() != "",
|
|
f"Schema {schema_name}, property {prop_name} has empty description",
|
|
)
|
|
|
|
if schema_def.get("type") == "array":
|
|
items: dict[str, Any] | None = schema_def.get("items")
|
|
check_schema(
|
|
items is not None,
|
|
f"Schema {schema_name} is array type but missing 'items' definition",
|
|
)
|
|
|
|
if items:
|
|
check_schema(
|
|
"type" in items or "$ref" in items,
|
|
f"Schema {schema_name} array items missing type definition",
|
|
)
|
|
|
|
if errors:
|
|
pytest.fail("Schema validation failed:\n" + "\n".join(errors))
|