mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 07:27:15 +08:00
refactor(db): 修复异步连接池无界增长,并完成 SQLAlchemy 2.0 迁移与分层归位 (#6320)
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
"""``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__ 中"
|
||||
Reference in New Issue
Block a user