aps-agent/tests/golden/test_openapi_schema.py

92 lines
3.1 KiB
Python
Raw Normal View History

# ============================================================
# OpenAPI schema 生成门禁(round-52 回归守护)
#
# 根因:`server/gateway/app.py` 的 create_app() 函数体内定义的局部
# Pydantic 请求模型类,会让 FastAPI 生成 /openapi.json 时抛
# PydanticUserError(ForwardRef 无法按模块全局解析),线上表现为
# GET /openapi.json 500。
#
# 本测试守护两件事:
# 1) openapi() 必须能完整生成(所有路由请求模型可解析);
# 2) 曾出问题的 12 个请求模型必须是模块级定义(可被
# `from server.gateway.app import ...` 导入,且 __qualname__
# 不含 create_app)。
# ============================================================
from __future__ import annotations
import pytest
from tests.auth_provider import install_test_auth
@pytest.fixture()
def gateway_app(tmp_path, monkeypatch):
monkeypatch.setenv("APS_DB_PATH", str(tmp_path / "openapi.db"))
monkeypatch.setenv("APS_WORLD_PATH", str(tmp_path / "world.json"))
from server.db.database import reset_engine
from server.state import store as world_store
install_test_auth(monkeypatch, "tenant-a-000000000000000000000000001")
world_store._stores.clear()
reset_engine()
from server.gateway.app import create_app
app = create_app()
yield app
reset_engine()
world_store._stores.clear()
# round-52 曾定义在 create_app() 局部、导致 OpenAPI 生成崩溃的 12 个模型
MODULE_SCOPE_MODELS = (
"ImportCommitRequest",
"TemplateApplyRequest",
"RagQueryRequest",
"RebuildStartRequest",
"RebuildRollbackRequest",
"KnowledgeImportRequest",
"SkillRegisterRequest",
"SkillEnableRequest",
"McpPluginRegisterRequest",
"McpPluginEnableRequest",
"McpPermissionRequest",
"JobSubmitRequest",
)
# 上述模型服务的受影响路由
AFFECTED_PATHS = (
"/api/import/commit",
"/api/templates/apply",
"/api/rag/query",
"/api/rebuild/{module_id}",
"/api/rebuild/{module_id}/rollback",
"/api/knowledge/import",
"/api/skills/register",
"/api/skills/{skill_id}/enable",
"/api/mcp/plugins/register",
"/api/mcp/plugins/{plugin_id}/enable",
"/api/mcp/plugins/{plugin_id}/permissions",
"/api/jobs",
)
def test_openapi_schema_generates_without_forward_ref_error(gateway_app):
"""round-52 回归:openapi() 必须能完整生成,不再抛 PydanticUserError。"""
spec = gateway_app.openapi()
assert isinstance(spec, dict)
paths = spec.get("paths") or {}
for path in AFFECTED_PATHS:
assert path in paths, f"openapi schema 缺少受影响路由 {path}"
def test_affected_request_models_are_module_scope(gateway_app):
"""round-52 守护:请求模型必须是模块级定义,不能是 create_app 局部类。"""
import server.gateway.app as gateway_module
for name in MODULE_SCOPE_MODELS:
cls = getattr(gateway_module, name)
assert "create_app" not in cls.__qualname__, (
f"{name} 仍是函数局部定义: {cls.__qualname__}"
)
assert cls.__module__ == "server.gateway.app", cls.__module__