mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-07 00:16:57 +08:00
refactor: isolate pending transfer and config queries
This commit is contained in:
+3
-18
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"model_decorators": {
|
||||
"by_kind": {
|
||||
"async_db_query": 7,
|
||||
"async_db_query": 6,
|
||||
"async_db_update": 0,
|
||||
"db_query": 5,
|
||||
"db_query": 3,
|
||||
"db_update": 0
|
||||
},
|
||||
"count": 12,
|
||||
"count": 9,
|
||||
"methods": [
|
||||
{
|
||||
"decorator": "async_db_query",
|
||||
@@ -52,21 +52,6 @@
|
||||
"decorator": "db_query",
|
||||
"file": "app/db/models/subscribehistory.py",
|
||||
"method": "SubscribeHistory.list_by_type"
|
||||
},
|
||||
{
|
||||
"decorator": "async_db_query",
|
||||
"file": "app/db/models/systemconfig.py",
|
||||
"method": "SystemConfig.async_get_by_key"
|
||||
},
|
||||
{
|
||||
"decorator": "db_query",
|
||||
"file": "app/db/models/systemconfig.py",
|
||||
"method": "SystemConfig.get_by_key"
|
||||
},
|
||||
{
|
||||
"decorator": "db_query",
|
||||
"file": "app/db/models/transferpending.py",
|
||||
"method": "TransferPending.list_all"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -126,8 +126,8 @@ def test_transaction_debt_baseline_is_a_model_and_oper_ratchet() -> None:
|
||||
baseline = json.loads(baseline_path.read_text(encoding="utf-8"))
|
||||
|
||||
assert baseline["schema_version"] == 1
|
||||
assert baseline["model_decorators"]["count"] == 12
|
||||
assert sum(baseline["model_decorators"]["by_kind"].values()) == 12
|
||||
assert baseline["model_decorators"]["count"] == 9
|
||||
assert sum(baseline["model_decorators"]["by_kind"].values()) == 9
|
||||
assert baseline["model_decorators"]["by_kind"]["db_update"] == 0
|
||||
assert baseline["model_decorators"]["by_kind"]["async_db_update"] == 0
|
||||
assert baseline["model_transaction_calls"] == {"count": 0, "calls": []}
|
||||
|
||||
@@ -9,12 +9,14 @@ import asyncio
|
||||
|
||||
import pytest
|
||||
|
||||
from app.db import decorators
|
||||
from app.db.models.passkey import PassKey
|
||||
from app.db.models.systemconfig import SystemConfig
|
||||
from app.db.models.user import User
|
||||
from app.db.models.userconfig import UserConfig
|
||||
from app.db.oper.passkey import PassKeyOper
|
||||
from app.db.oper.user import UserOper
|
||||
from app.db.session import SessionFactory, async_session_scope
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
@@ -48,6 +50,46 @@ def test_systemconfig_get_by_key_returns_none_when_absent(db):
|
||||
assert SystemConfig.get_by_key(db.session, "mp-test-missing") is None
|
||||
|
||||
|
||||
def test_systemconfig_queries_reuse_explicit_sessions(db, monkeypatch):
|
||||
"""SystemConfig 显式同步与异步会话不得触发兼容会话。"""
|
||||
db.add(SystemConfig(key="mp-explicit-config", value=True))
|
||||
monkeypatch.setattr(
|
||||
decorators,
|
||||
"ScopedSession",
|
||||
lambda: (_ for _ in ()).throw(AssertionError("不应创建额外同步会话")),
|
||||
)
|
||||
assert SystemConfig.get_by_key(db.session, "mp-explicit-config") is not None
|
||||
|
||||
async def check() -> None:
|
||||
"""验证异步配置查询复用显式 AsyncSession。"""
|
||||
async with async_session_scope() as session:
|
||||
monkeypatch.setattr(
|
||||
decorators,
|
||||
"async_session_scope",
|
||||
lambda: (_ for _ in ()).throw(AssertionError("不应创建额外异步会话")),
|
||||
)
|
||||
assert await SystemConfig.async_get_by_key(
|
||||
session,
|
||||
"mp-explicit-config",
|
||||
) is not None
|
||||
|
||||
asyncio.run(check())
|
||||
|
||||
|
||||
def test_systemconfig_model_legacy_query_keeps_keyword_abi(db, monkeypatch):
|
||||
"""旧插件以关键字直调 SystemConfig 时仍自动补入短会话。"""
|
||||
db.add(SystemConfig(key="mp-legacy-config", value=True))
|
||||
opened = []
|
||||
monkeypatch.setattr(
|
||||
decorators,
|
||||
"ScopedSession",
|
||||
lambda: (opened.append(True) or SessionFactory()),
|
||||
)
|
||||
|
||||
assert SystemConfig.get_by_key(key="mp-legacy-config") is not None
|
||||
assert opened == [True]
|
||||
|
||||
|
||||
def test_systemconfig_delete_by_key_removes_only_that_key(db):
|
||||
"""
|
||||
按键删除只能删掉那一个键,误删会静默丢失其他配置。
|
||||
|
||||
@@ -7,8 +7,10 @@
|
||||
"""
|
||||
import pytest
|
||||
|
||||
from app.db import decorators
|
||||
from app.db.models.transferpending import TransferPending
|
||||
from app.db.oper.transferpending import TransferPendingOper
|
||||
from app.db.session import SessionFactory
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
@@ -150,6 +152,40 @@ def test_oper_returns_plain_tuples_not_orm_instances(db):
|
||||
assert all(isinstance(item, tuple) for item in listed)
|
||||
|
||||
|
||||
def test_oper_reuses_explicit_query_session(db, monkeypatch):
|
||||
"""TransferPendingOper 查询必须复用调用方会话。"""
|
||||
db.add(TransferPending(
|
||||
storage="local",
|
||||
src_path="/mnt/explicit.mkv",
|
||||
created_at="2026-08-13 10:00:00",
|
||||
))
|
||||
monkeypatch.setattr(
|
||||
decorators,
|
||||
"ScopedSession",
|
||||
lambda: (_ for _ in ()).throw(AssertionError("不应创建额外同步会话")),
|
||||
)
|
||||
|
||||
assert ("local", "/mnt/explicit.mkv") in TransferPendingOper(db.session).list_all()
|
||||
|
||||
|
||||
def test_model_legacy_query_keeps_keyword_abi(db, monkeypatch):
|
||||
"""旧插件以关键字直调 TransferPending 时仍自动补入短会话。"""
|
||||
db.add(TransferPending(
|
||||
storage="local",
|
||||
src_path="/mnt/legacy.mkv",
|
||||
created_at="2026-08-13 10:00:00",
|
||||
))
|
||||
opened = []
|
||||
monkeypatch.setattr(
|
||||
decorators,
|
||||
"ScopedSession",
|
||||
lambda: (opened.append(True) or SessionFactory()),
|
||||
)
|
||||
|
||||
assert TransferPending.list_all(limit=1)
|
||||
assert opened == [True]
|
||||
|
||||
|
||||
def test_oper_drops_rows_with_missing_fields(db):
|
||||
"""
|
||||
回放时必须跳过字段残缺的历史遗留行,不能把空存储送进整理链。
|
||||
|
||||
Reference in New Issue
Block a user