test: confine api data compatibility locator

This commit is contained in:
jxxghp
2026-08-24 01:25:12 +08:00
parent 04253b4cf7
commit 050768925f
2 changed files with 13 additions and 18 deletions
@@ -165,7 +165,7 @@ MoviePilot V3 当前不是“目录混乱、必须推倒重来”的状态。第
1. **架构基线工具的事实源分离已完成。**宿主、官方插件和启动性能使用独立 check/write;稳定语义与源码位置诊断分开,配置/事务低水位采用单向 ratchet,不能再用一次全量 `--write` 掩盖跨域变化。
2. **V3 部署拓扑边界已完成。**全功能模式在 startup、launcher 和 Doctor 共同拒绝 `API_WORKERS > 1`,生产入口固定单 worker;开发 reload/监督模式使用 `app.factory:create_app` import-string factory,不再把 app 实例交给多进程 supervisor。旧配置键继续可解析,未来只有拆出 control role 后才重新评估全功能多 worker。
3. **事务所有权已完成装饰器层收口,但 ORM 对象跨层流转仍需治理。**正式 Model 查询/写装饰器均已清零,宿主 Oper 查询统一接收显式 Session;调用方仍需继续明确 ORM 对象生命周期、懒加载和业务提交后副作用边界。
4. **组合根之后仍存在全局服务定位,但配置直连债务已清零。**canonical 未批准 Settings 导入与非组合根 `SystemConfigOper()` 构造均为 `0`;数据库基础设施 3 处和 startup 唯一构造点作为不可扩张边界登记。Singleton模块级 provider 和 API 数据端口注册表仍需按消费者切片收口
4. **组合根之后仍存在全局服务定位,但配置和 API 数据主路径已收口。**canonical 未批准 Settings 导入与非组合根 `SystemConfigOper()` 构造均为 `0`;数据库基础设施 3 处和 startup 唯一构造点作为不可扩张边界登记。正式 FastAPI 依赖只读取 AppState `HostRuntime` 的命名领域,字符串 API 数据注册表仅允许 startup 注入和旧 Facade 转发;后续对象是 Singleton模块级 `configure/get` provider,不应再迁移已类型化 API 依赖
5. **模块契约仍在推进,Event Registry 已完成全量登记。**当前 212 个模块 spec 中 168 个仍使用 legacy aggregation44 个已使用可执行聚合/接力语义;53 个事件全部绑定 typed payload,可见性、投递等级、错误行为和敏感字段均有基线,legacy event payload 为 `0`。后续重点是模块能力逐族收口和 6 个 durable-required 事件的真实持久投递,不是重复创建事件 DTO。
6. **后台副作用缺少统一可靠性定义。**事件队列、APScheduler、FastAPI BackgroundTasks 和线程池任务的丢失、重试、幂等、关停语义各不相同;数据库提交与事件/上报之间仍有进程崩溃窗口。
7. **核心关联与健康边界已落地,指标导出仍未收口。**HTTP/SSE correlation ID 已传播到线程池、事件、工作流、子进程、外部请求和日志;`/health/live``/health/ready` 已由部署入口消费,事件/数据库队列深度及模块/事件耗时使用低基数指标登记。当前缺口是稳定 exporter、运维查询面和跨进程聚合,而不是重新实现 request ID 或健康路由。
+12 -17
View File
@@ -207,28 +207,23 @@ def test_fastapi_dependencies_use_fake_runtime_without_real_services() -> None:
assert response.json() == {"same_session": True, "has_persistence": True}
def test_official_api_dependencies_do_not_use_string_data_locator() -> None:
"""正式业务依赖只能读取 HostRuntime 命名领域,禁止回退字符串注册表"""
dependency_root = PROJECT_ROOT / "app" / "api" / "dependencies"
official_modules = {
"agent.py",
"auth.py",
"history.py",
"site.py",
"subscription.py",
"workflow.py",
}
for filename in official_modules:
tree = ast.parse(
(dependency_root / filename).read_text(encoding="utf-8")
)
def test_string_api_data_locator_is_confined_to_compatibility_boundary() -> None:
"""字符串数据注册表只能由 startup 注入并经旧 Facade 转发"""
importers = set()
for path in (PROJECT_ROOT / "app").rglob("*.py"):
tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
imported_modules = {
node.module
for node in ast.walk(tree)
if isinstance(node, ast.ImportFrom) and node.module
}
assert "app.api.data" not in imported_modules
assert "app.api.dependencies.data" not in imported_modules
if imported_modules & {"app.api.data", "app.api.dependencies.data"}:
importers.add(path.relative_to(PROJECT_ROOT).as_posix())
assert importers == {
"app/api/dependencies/data.py",
"app/startup/initializers/modules.py",
}
@pytest.mark.asyncio