Phase 5.14: API documentation - OpenAPI tags, response models, examples, docs

This commit is contained in:
Agent Zero
2026-07-23 22:44:54 +02:00
parent 3c1b2f227b
commit 66b6c32ed8
11 changed files with 716 additions and 28 deletions
+129
View File
@@ -0,0 +1,129 @@
"""Tests for OpenAPI documentation completeness (Task 5.14)."""
from __future__ import annotations
import os
import sys
import pytest
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
def test_openapi_tags_configured():
"""FastAPI app should have openapi_tags configured with descriptions."""
from app.main import app
assert app.openapi_tags is not None
assert len(app.openapi_tags) > 0
tag_names = {t["name"] for t in app.openapi_tags}
# Core tags should be present
assert "health" in tag_names
assert "auth" in tag_names
assert "users" in tag_names
assert "contacts" in tag_names
assert "system-settings" in tag_names
def test_openapi_tags_have_descriptions():
"""Each openapi_tag should have a description."""
from app.main import app
for tag in app.openapi_tags:
assert "description" in tag
assert len(tag["description"]) > 0
def test_app_has_description():
"""FastAPI app should have a description for OpenAPI docs."""
from app.main import app
assert app.description is not None
assert len(app.description) > 0
assert "LeoCRM" in app.description or "CRM" in app.description
def test_all_api_routes_have_tags():
"""All API routes should have at least one tag for OpenAPI grouping."""
from app.main import app
untagged = []
for route in app.routes:
if not hasattr(route, "methods") or not hasattr(route, "path"):
continue
if not route.path.startswith("/api/"):
continue
methods = route.methods - {"HEAD", "OPTIONS"} if route.methods else set()
if not methods:
continue
tags = getattr(route, "tags", []) or []
if not tags:
untagged.append(f"{route.path} [{','.join(sorted(methods))}]")
assert len(untagged) == 0, f"Routes without tags: {untagged}"
def test_key_routes_have_response_model():
"""Key routes should have response_model set for OpenAPI schema generation."""
from app.main import app
# Check that at least some routes have response_model
routes_with_response_model = 0
for route in app.routes:
if not hasattr(route, "methods") or not hasattr(route, "path"):
continue
if not route.path.startswith("/api/"):
continue
response_model = getattr(route, "response_model", None)
if response_model is not None:
routes_with_response_model += 1
assert routes_with_response_model >= 5, (
f"Expected at least 5 routes with response_model, got {routes_with_response_model}"
)
def test_auth_login_has_auth_response_model():
"""POST /api/v1/auth/login should have AuthResponse as response_model."""
from app.main import app
from app.schemas.auth import AuthResponse
for route in app.routes:
if (
hasattr(route, "path")
and route.path == "/api/v1/auth/login"
and hasattr(route, "response_model")
):
assert route.response_model == AuthResponse
return
pytest.fail("Route /api/v1/auth/login not found")
def test_health_has_health_response_model():
"""GET /api/v1/health should have HealthResponse as response_model."""
from app.main import app
from app.schemas.common import HealthResponse
for route in app.routes:
if (
hasattr(route, "path")
and route.path == "/api/v1/health"
and hasattr(route, "response_model")
):
assert route.response_model == HealthResponse
return
pytest.fail("Route /api/v1/health not found")
def test_api_documentation_md_exists():
"""docs/api-documentation.md should exist and have content."""
doc_path = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"docs",
"api-documentation.md",
)
assert os.path.isfile(doc_path), "docs/api-documentation.md not found"
with open(doc_path) as f:
content = f.read()
assert "LeoCRM API Documentation" in content
assert "auth" in content.lower()
assert "contacts" in content.lower()
assert "plugins" in content.lower()