mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-10-29 03:18:14 +08:00
88 lines
3.4 KiB
Python
88 lines
3.4 KiB
Python
"""API 文档运行时开关的路由契约测试。"""
|
||
|
||
import pytest
|
||
from fastapi.testclient import TestClient
|
||
|
||
from app.application.plugin import routes as plugin_routes
|
||
from app.application.settings.contract import build_setting_specs
|
||
from app.factory import create_app
|
||
from app.runtime.config import settings
|
||
|
||
DOC_PATHS = ("/docs", "/docs/oauth2-redirect", "/redoc", "/api/v1/openapi.json")
|
||
|
||
|
||
def test_api_docs_are_closed_by_default() -> None:
|
||
"""默认不开放文档,也不会因访问而生成文档。"""
|
||
app = create_app()
|
||
client = TestClient(app)
|
||
|
||
for path in DOC_PATHS:
|
||
assert client.get(path).status_code == 404, path
|
||
assert app.openapi_schema is None
|
||
|
||
|
||
def test_api_docs_follow_runtime_toggle_without_restart(monkeypatch: pytest.MonkeyPatch) -> None:
|
||
"""同一个应用实例上切换开关即时生效,关闭后丢弃已缓存的文档。"""
|
||
app = create_app()
|
||
client = TestClient(app)
|
||
monkeypatch.setattr(settings, "API_DOCS_ENABLE", True)
|
||
|
||
openapi = client.get("/api/v1/openapi.json")
|
||
assert openapi.status_code == 200
|
||
assert openapi.json()["openapi"].startswith("3.")
|
||
assert app.openapi_schema is not None
|
||
swagger = client.get("/docs")
|
||
assert swagger.status_code == 200
|
||
assert "/api/v1/openapi.json" in swagger.text
|
||
assert "/docs/oauth2-redirect" in swagger.text
|
||
assert client.get("/docs/oauth2-redirect").status_code == 200
|
||
redoc = client.get("/redoc")
|
||
assert redoc.status_code == 200
|
||
assert "/api/v1/openapi.json" in redoc.text
|
||
|
||
monkeypatch.setattr(settings, "API_DOCS_ENABLE", False)
|
||
assert client.get("/api/v1/openapi.json").status_code == 404
|
||
assert app.openapi_schema is None
|
||
|
||
|
||
def test_api_docs_survive_plugin_route_updates(monkeypatch: pytest.MonkeyPatch) -> None:
|
||
"""插件路由增删后文档路由仍在,FastAPI.setup() 不再补注册它们。"""
|
||
app = create_app()
|
||
registry = plugin_routes._get_route_registry()
|
||
# 测试进程没有运行中的主 loop,改为当前线程同步更新路由表。
|
||
monkeypatch.setattr(registry, "_event_loop", None)
|
||
monkeypatch.setattr(registry, "_plugin_apis", lambda _plugin_id: [{
|
||
"path": "/DemoPlugin/health",
|
||
"endpoint": lambda: {"ok": True},
|
||
"methods": ["GET"],
|
||
"allow_anonymous": True,
|
||
}])
|
||
monkeypatch.setattr(settings, "API_DOCS_ENABLE", True)
|
||
client = TestClient(app)
|
||
|
||
registry.update("DemoPlugin", "add")
|
||
assert client.get("/api/v1/plugin/DemoPlugin/health").json() == {"ok": True}
|
||
registry.update("DemoPlugin", "remove")
|
||
|
||
for path in DOC_PATHS:
|
||
assert client.get(path).status_code == 200, path
|
||
|
||
|
||
def test_api_docs_pages_keep_reverse_proxy_prefix(monkeypatch: pytest.MonkeyPatch) -> None:
|
||
"""文档页引用的 OpenAPI 地址带上 root_path,与 FastAPI 自带文档一致。"""
|
||
monkeypatch.setattr(settings, "API_DOCS_ENABLE", True)
|
||
client = TestClient(create_app(), root_path="/mp")
|
||
|
||
assert "/mp/api/v1/openapi.json" in client.get("/docs").text
|
||
assert "/mp/api/v1/openapi.json" in client.get("/redoc").text
|
||
|
||
|
||
def test_api_docs_toggle_applies_immediately() -> None:
|
||
"""开关位于需重启的安全分组,但逐请求读取,设置契约应标记为立即生效。"""
|
||
core_specs, _ = build_setting_specs()
|
||
spec = core_specs["API_DOCS_ENABLE"]
|
||
|
||
assert spec.group == "security"
|
||
assert spec.apply_mode == "immediate"
|
||
assert core_specs["PASSKEY_REQUIRE_UV"].apply_mode == "restart_required"
|