mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-15 19:14:01 +08:00
66 lines
3.1 KiB
Python
66 lines
3.1 KiB
Python
"""``app.db`` 对外契约的边界。
|
||
|
||
``__all__`` 是这个包唯一一份机器可读的对外承诺,而当前的写法**看起来像个疏漏**:
|
||
``SessionFactory`` / ``AsyncSessionFactory`` / ``ScopedSession`` 三个名字在
|
||
``app/db/__init__.py`` 里明明 import 了,却不出现在 ``__all__`` 中。下一个读到这段代码
|
||
的人很容易顺手把它们补回去——那会在无人察觉的情况下把契约重新放宽。
|
||
|
||
这三个名字建出来的是**绕过事务装饰器**的裸会话:不提交、不回滚、不释放,全靠调用方
|
||
自己兜底。它们之所以还留在模块里,只是因为包内的 scheduler、postgresql 模块与 Alembic
|
||
迁移脚本用直接导入的方式在用(直接导入不受 ``__all__`` 约束)。
|
||
|
||
所以这里同时钉两头:契约里没有它们,但包内的既有导入不能被这个决定误伤。
|
||
"""
|
||
import app.db as db_package
|
||
|
||
|
||
# 降级为内部实现细节的三个会话工厂
|
||
INTERNAL_FACTORY_NAMES = ("SessionFactory", "AsyncSessionFactory", "ScopedSession")
|
||
|
||
|
||
def test_session_factories_are_not_part_of_the_public_contract():
|
||
"""
|
||
三个会话工厂不得出现在 ``__all__`` 里。
|
||
|
||
插件要访问数据库应走 ``DbOper`` 子类或 ``db_query`` / ``async_db_query`` 装饰器,
|
||
由装饰器收口会话的提交、回滚与释放。
|
||
"""
|
||
leaked = [name for name in INTERNAL_FACTORY_NAMES if name in db_package.__all__]
|
||
assert not leaked, f"会话工厂被重新放进了对外契约:{leaked}"
|
||
|
||
|
||
def test_session_factories_remain_importable_for_in_repo_callers():
|
||
"""
|
||
契约收窄不等于删除:包内既有的直接导入必须照常可用。
|
||
|
||
``app/scheduler.py``、``app/modules/postgresql/__init__.py`` 与
|
||
``database/versions/*.py`` 都在用 ``from app.db import SessionFactory``,
|
||
这类直接导入本就不受 ``__all__`` 影响,此处显式钉住以免连带删除。
|
||
"""
|
||
for name in INTERNAL_FACTORY_NAMES:
|
||
assert callable(getattr(db_package, name, None)), f"{name} 不再可从 app.db 导入"
|
||
|
||
|
||
def test_engines_stay_in_the_public_contract():
|
||
"""
|
||
``Engine`` / ``AsyncEngine`` 留在契约内。
|
||
|
||
建表、Alembic 迁移与连接诊断确实需要引擎**对象**本身,事务装饰器覆盖不到这些用途,
|
||
仓库外拿它是正当的。
|
||
|
||
只断言名字在不在 ``__all__``,不去真的取它:这两个名字由模块级 ``__getattr__`` 解析,
|
||
取属性即创建引擎,而本用例并不想在测试进程里凭空建一个没人释放的异步引擎。
|
||
"""
|
||
for name in ("Engine", "AsyncEngine"):
|
||
assert name in db_package.__all__, f"{name} 被移出了对外契约"
|
||
|
||
|
||
def test_documented_plugin_entrypoints_are_exported():
|
||
"""
|
||
契约里必须留有插件真正该走的那条路:``DbOper`` 基类与四个事务装饰器。
|
||
|
||
否则「工厂降级为内部细节」就成了一句没有出口的话。
|
||
"""
|
||
for name in ("DbOper", "db_query", "db_update", "async_db_query", "async_db_update"):
|
||
assert name in db_package.__all__, f"插件数据访问入口 {name} 不在 __all__ 中"
|