Initial commit: QMDSearch 分层信息检索服务
- FastAPI + Qdrant + Redis + Ollama 技术栈 - L1→L2→L3→chunk 四层分层检索(dense + sparse RRF 融合) - 文档三级总结与 2.5 级回退 - query 解析路由与分类 - /admin 管理页面
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
"""管理页面挂载测试(GET /admin)
|
||||
|
||||
覆盖:
|
||||
1. 路由存在性:200 + content-type 为 text/html
|
||||
2. 五区块可识别标记(文案与 section id)
|
||||
3. 零外部依赖:无 http(s) 外链资源、无 CDN 引用
|
||||
4. fetch 调用路径与后端 API 契约一致
|
||||
5. 删除操作的 confirm() 二次确认逻辑
|
||||
"""
|
||||
|
||||
import re
|
||||
from collections.abc import Iterator
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.main import app
|
||||
from app.services.qdrant import QdrantService
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client(monkeypatch: pytest.MonkeyPatch) -> Iterator[TestClient]:
|
||||
"""TestClient,lifespan 中的 Qdrant 集合初始化替换为空操作"""
|
||||
|
||||
async def _noop_ensure_collections(self: QdrantService) -> None:
|
||||
return None
|
||||
|
||||
monkeypatch.setattr(QdrantService, "ensure_collections", _noop_ensure_collections)
|
||||
with TestClient(app) as test_client:
|
||||
yield test_client
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def admin_html(client: TestClient) -> str:
|
||||
"""请求 /admin 并返回 HTML 文本(前置断言 200)"""
|
||||
resp = client.get("/admin")
|
||||
assert resp.status_code == 200
|
||||
return resp.text
|
||||
|
||||
|
||||
def test_admin_page_ok(client: TestClient) -> None:
|
||||
"""GET /admin → 200,content-type 含 text/html"""
|
||||
resp = client.get("/admin")
|
||||
|
||||
assert resp.status_code == 200
|
||||
assert "text/html" in resp.headers["content-type"]
|
||||
|
||||
|
||||
def test_admin_page_has_five_sections(admin_html: str) -> None:
|
||||
"""HTML 含五个区块的文案与对应 section id"""
|
||||
for section_id in (
|
||||
"section-overview",
|
||||
"section-docs",
|
||||
"section-ingest",
|
||||
"section-search",
|
||||
"section-categories",
|
||||
):
|
||||
assert section_id in admin_html
|
||||
for label in ("概览", "文档管理", "文档入库", "检索测试台", "类目列表"):
|
||||
assert label in admin_html
|
||||
|
||||
|
||||
def test_admin_page_no_external_resources(admin_html: str) -> None:
|
||||
"""零外部依赖:无 src/href http(s) 外链,无 CDN 引用"""
|
||||
assert re.search(r'src=["\']https?://', admin_html) is None
|
||||
assert re.search(r'href=["\']https?://', admin_html) is None
|
||||
assert re.search(r"//cdn", admin_html) is None
|
||||
|
||||
|
||||
def test_admin_page_fetch_paths(admin_html: str) -> None:
|
||||
"""fetch 调用路径与后端 API 契约一致"""
|
||||
for path in (
|
||||
"/api/v1/search",
|
||||
"/api/v1/documents",
|
||||
"/api/v1/knowledge/stats",
|
||||
"/api/v1/knowledge/categories",
|
||||
):
|
||||
assert path in admin_html
|
||||
|
||||
|
||||
def _collect_route_paths(routes: list) -> set[str]:
|
||||
"""递归收集路由路径(FastAPI 0.140+ include_router 包装为 _IncludedRouter)"""
|
||||
paths: set[str] = set()
|
||||
for route in routes:
|
||||
path = getattr(route, "path", None)
|
||||
if path:
|
||||
paths.add(path)
|
||||
sub_router = getattr(route, "original_router", None)
|
||||
if sub_router is not None:
|
||||
paths |= _collect_route_paths(sub_router.routes)
|
||||
return paths
|
||||
|
||||
|
||||
def test_admin_page_fetch_paths_in_real_routes(admin_html: str) -> None:
|
||||
"""页面 api() 调用路径均在真实后端路由集合内(含新增的 tasks 路径)"""
|
||||
route_paths = _collect_route_paths(app.routes)
|
||||
assert "/api/v1/documents/tasks/{task_id}" in route_paths
|
||||
|
||||
prefixes = set(re.findall(r'api\("([^"?]+)', admin_html))
|
||||
assert prefixes, "页面应包含 api() 调用"
|
||||
for prefix in prefixes:
|
||||
assert any(path == prefix or path.startswith(prefix) for path in route_paths), (
|
||||
f"页面 fetch 路径 {prefix} 不在后端路由集合内"
|
||||
)
|
||||
|
||||
|
||||
def test_admin_page_ingest_polling(admin_html: str) -> None:
|
||||
"""入库区块:异步任务轮询逻辑标记"""
|
||||
# 轮询任务状态端点
|
||||
assert "/api/v1/documents/tasks/" in admin_html
|
||||
# 状态中文映射
|
||||
for text in ("排队中", "总结中", "分类中", "向量化中", "写入中", "完成", "失败"):
|
||||
assert text in admin_html
|
||||
# 提交确认与超时提示
|
||||
assert "任务已提交" in admin_html
|
||||
assert "任务仍在进行,可稍后凭 task_id 查询" in admin_html
|
||||
# 5 分钟超时:150 次 × 2s
|
||||
assert "150" in admin_html
|
||||
# 防重复提交:轮询中禁用提交按钮
|
||||
assert "disabled" in admin_html
|
||||
|
||||
|
||||
def test_admin_page_has_confirm(admin_html: str) -> None:
|
||||
"""删除操作包含 confirm() 二次确认逻辑"""
|
||||
assert "confirm(" in admin_html
|
||||
Reference in New Issue
Block a user