refactor: isolate transfer and download history queries

This commit is contained in:
jxxghp
2026-08-23 13:59:12 +08:00
parent 03c03fa27d
commit 4074fa4e42
10 changed files with 505 additions and 348 deletions
+3 -188
View File
@@ -1,12 +1,12 @@
{
"model_decorators": {
"by_kind": {
"async_db_query": 27,
"async_db_query": 18,
"async_db_update": 0,
"db_query": 48,
"db_query": 20,
"db_update": 0
},
"count": 75,
"count": 38,
"methods": [
{
"decorator": "async_db_query",
@@ -38,86 +38,6 @@
"file": "app/db/models/agenttaskrun.py",
"method": "AgentTaskRun.list_for_task"
},
{
"decorator": "db_query",
"file": "app/db/models/downloadhistory.py",
"method": "DownloadFiles.get_by_fullpath"
},
{
"decorator": "db_query",
"file": "app/db/models/downloadhistory.py",
"method": "DownloadFiles.get_by_hash"
},
{
"decorator": "db_query",
"file": "app/db/models/downloadhistory.py",
"method": "DownloadFiles.get_by_savepath"
},
{
"decorator": "async_db_query",
"file": "app/db/models/downloadhistory.py",
"method": "DownloadHistory.async_count"
},
{
"decorator": "async_db_query",
"file": "app/db/models/downloadhistory.py",
"method": "DownloadHistory.async_count_by_title"
},
{
"decorator": "async_db_query",
"file": "app/db/models/downloadhistory.py",
"method": "DownloadHistory.async_list_by_page"
},
{
"decorator": "async_db_query",
"file": "app/db/models/downloadhistory.py",
"method": "DownloadHistory.async_list_by_title"
},
{
"decorator": "db_query",
"file": "app/db/models/downloadhistory.py",
"method": "DownloadHistory.get_by_hash"
},
{
"decorator": "db_query",
"file": "app/db/models/downloadhistory.py",
"method": "DownloadHistory.get_by_hashes"
},
{
"decorator": "db_query",
"file": "app/db/models/downloadhistory.py",
"method": "DownloadHistory.get_by_media_identity"
},
{
"decorator": "db_query",
"file": "app/db/models/downloadhistory.py",
"method": "DownloadHistory.get_by_path"
},
{
"decorator": "db_query",
"file": "app/db/models/downloadhistory.py",
"method": "DownloadHistory.get_last_by"
},
{
"decorator": "db_query",
"file": "app/db/models/downloadhistory.py",
"method": "DownloadHistory.list_by_date"
},
{
"decorator": "db_query",
"file": "app/db/models/downloadhistory.py",
"method": "DownloadHistory.list_by_page"
},
{
"decorator": "db_query",
"file": "app/db/models/downloadhistory.py",
"method": "DownloadHistory.list_by_type"
},
{
"decorator": "db_query",
"file": "app/db/models/downloadhistory.py",
"method": "DownloadHistory.list_by_user_date"
},
{
"decorator": "async_db_query",
"file": "app/db/models/mediaserver.py",
@@ -233,111 +153,6 @@
"file": "app/db/models/systemconfig.py",
"method": "SystemConfig.get_by_key"
},
{
"decorator": "async_db_query",
"file": "app/db/models/transferhistory.py",
"method": "TransferHistory.async_count"
},
{
"decorator": "async_db_query",
"file": "app/db/models/transferhistory.py",
"method": "TransferHistory.async_count_by_title"
},
{
"decorator": "async_db_query",
"file": "app/db/models/transferhistory.py",
"method": "TransferHistory.async_list_by_page"
},
{
"decorator": "async_db_query",
"file": "app/db/models/transferhistory.py",
"method": "TransferHistory.async_list_by_title"
},
{
"decorator": "async_db_query",
"file": "app/db/models/transferhistory.py",
"method": "TransferHistory.async_statistic"
},
{
"decorator": "db_query",
"file": "app/db/models/transferhistory.py",
"method": "TransferHistory.count"
},
{
"decorator": "db_query",
"file": "app/db/models/transferhistory.py",
"method": "TransferHistory.count_by_title"
},
{
"decorator": "db_query",
"file": "app/db/models/transferhistory.py",
"method": "TransferHistory.get_by_dest"
},
{
"decorator": "db_query",
"file": "app/db/models/transferhistory.py",
"method": "TransferHistory.get_by_hash"
},
{
"decorator": "db_query",
"file": "app/db/models/transferhistory.py",
"method": "TransferHistory.get_by_media_identity"
},
{
"decorator": "db_query",
"file": "app/db/models/transferhistory.py",
"method": "TransferHistory.get_by_src"
},
{
"decorator": "db_query",
"file": "app/db/models/transferhistory.py",
"method": "TransferHistory.get_success_by_src"
},
{
"decorator": "db_query",
"file": "app/db/models/transferhistory.py",
"method": "TransferHistory.list_by"
},
{
"decorator": "db_query",
"file": "app/db/models/transferhistory.py",
"method": "TransferHistory.list_by_date"
},
{
"decorator": "db_query",
"file": "app/db/models/transferhistory.py",
"method": "TransferHistory.list_by_hash"
},
{
"decorator": "db_query",
"file": "app/db/models/transferhistory.py",
"method": "TransferHistory.list_by_page"
},
{
"decorator": "db_query",
"file": "app/db/models/transferhistory.py",
"method": "TransferHistory.list_by_title"
},
{
"decorator": "db_query",
"file": "app/db/models/transferhistory.py",
"method": "TransferHistory.list_success_by_src"
},
{
"decorator": "db_query",
"file": "app/db/models/transferhistory.py",
"method": "TransferHistory.list_success_move_by_dest"
},
{
"decorator": "db_query",
"file": "app/db/models/transferhistory.py",
"method": "TransferHistory.monthly_media_statistics"
},
{
"decorator": "db_query",
"file": "app/db/models/transferhistory.py",
"method": "TransferHistory.statistic"
},
{
"decorator": "db_query",
"file": "app/db/models/transferpending.py",
@@ -0,0 +1,111 @@
"""Transfer/Download History 查询兼容层的会话与旧插件 ABI 验证。"""
import asyncio
from app.db import decorators
from app.db.models.downloadhistory import DownloadHistory
from app.db.models.transferhistory import TransferHistory
from app.db.oper.downloadhistory import DownloadHistoryOper
from app.db.oper.transferhistory import TransferHistoryOper
from app.db.session import SessionFactory, async_session_scope
def test_oper_reuses_explicit_sync_session(db, monkeypatch):
"""显式同步会话绑定到 Oper 后,查询不能再创建兼容会话。"""
row = db.add(TransferHistory(src="/compat/transfer.mkv", src_storage="local"))
monkeypatch.setattr(
decorators,
"ScopedSession",
lambda: (_ for _ in ()).throw(AssertionError("不应创建额外同步会话")),
)
assert TransferHistoryOper(db.session).get_by_src("/compat/transfer.mkv").id == row.id
assert DownloadHistoryOper(db.session).get_by_hash("missing") is None
def test_model_legacy_sync_calls_preserve_business_arguments(db, monkeypatch):
"""旧插件省略 db 时,第一个位置参数仍须作为业务参数传入。"""
row = db.add(TransferHistory(src="/compat/legacy.mkv", src_storage="local"))
created = []
monkeypatch.setattr(
decorators,
"ScopedSession",
lambda: (created.append(True) or SessionFactory()),
)
assert TransferHistory.get_by_src("/compat/legacy.mkv").id == row.id
assert created == [True]
def test_download_model_legacy_sync_call_preserves_keyword_arguments(db, monkeypatch):
"""旧插件使用关键字查询时,兼容层仍须自动补入 db。"""
row = db.add(
DownloadHistory(
path="/compat/download",
type="电视剧",
download_hash="compat-hash",
title="兼容",
)
)
created = []
monkeypatch.setattr(
decorators,
"ScopedSession",
lambda: (created.append(True) or SessionFactory()),
)
assert DownloadHistory.get_by_hash(download_hash="compat-hash").id == row.id
assert created == [True]
def test_oper_reuses_explicit_async_session(db, monkeypatch):
"""显式异步会话绑定到 Oper 后,异步查询不能再创建兼容作用域。"""
db.add(
DownloadHistory(
path="/compat/async-download",
type="电视剧",
title="异步兼容",
download_hash="async-compat",
)
)
async def check() -> None:
async with async_session_scope() as session:
monkeypatch.setattr(
decorators,
"async_session_scope",
lambda: (_ for _ in ()).throw(AssertionError("不应创建额外异步会话")),
)
result = await DownloadHistoryOper(session).async_list_by_page(count=10)
assert any(item.download_hash == "async-compat" for item in result)
asyncio.run(check())
def test_model_legacy_async_calls_support_explicit_and_implicit_sessions(db, monkeypatch):
"""异步 Model 查询同时保留显式会话调用与旧插件无会话调用。"""
db.add(
DownloadHistory(
path="/compat/async-legacy",
type="电视剧",
title="异步旧 ABI",
download_hash="async-legacy",
)
)
original_scope = decorators.async_session_scope
created = []
def tracked_scope():
"""记录兼容层是否创建了异步会话作用域。"""
created.append(True)
return original_scope()
async def check() -> None:
async with original_scope() as session:
assert await DownloadHistory.async_count(session) >= 1
monkeypatch.setattr(decorators, "async_session_scope", tracked_scope)
result = await DownloadHistory.async_list_by_title(title="异步旧 ABI")
assert result[0].download_hash == "async-legacy"
asyncio.run(check())
assert created == [True]