Files
MoviePilot/tests/test_history_query.py
T

114 lines
3.6 KiB
Python

"""历史查询应用服务的分页、筛选和 DTO 边界测试。"""
from types import SimpleNamespace
from unittest.mock import AsyncMock
import pytest
from app.application.history import HistoryQueryService
from app.schemas.history import DownloadHistory, TransferHistory
def _make_service(
*,
downloads: AsyncMock | None = None,
transfers: AsyncMock | None = None,
) -> tuple[HistoryQueryService, AsyncMock, AsyncMock]:
"""构造使用可观察异步仓储的历史查询服务。"""
download_repository = downloads or AsyncMock()
transfer_repository = transfers or AsyncMock()
return (
HistoryQueryService(
download_repository=download_repository,
transfer_repository=transfer_repository,
),
download_repository,
transfer_repository,
)
@pytest.mark.asyncio
async def test_list_download_returns_schema_dtos() -> None:
"""下载历史查询不得把仓储对象原样泄漏给 API。"""
service, download_repository, _ = _make_service()
raw_record = SimpleNamespace(id=7, title="Movie")
download_repository.async_list_by_page.return_value = [raw_record]
records = await service.list_download(page=2, count=10)
assert records == [DownloadHistory(id=7, title="Movie")]
assert records[0] is not raw_record
download_repository.async_list_by_page.assert_awaited_once_with(2, 10)
@pytest.mark.asyncio
async def test_list_transfer_maps_failed_alias_to_status_filter() -> None:
"""“失败”快捷条件应继续映射为无标题的失败状态查询。"""
service, _, transfer_repository = _make_service()
transfer_repository.async_list_by_page.return_value = [
SimpleNamespace(id=3, status=False)
]
transfer_repository.async_count.return_value = 1
page = await service.list_transfer(title="失败", page=3, count=5)
assert page.list == [TransferHistory(id=3, status=False)]
assert page.total == 1
transfer_repository.async_list_by_page.assert_awaited_once_with(
page=3,
count=5,
status=False,
)
transfer_repository.async_count.assert_awaited_once_with(status=False)
@pytest.mark.asyncio
async def test_list_transfer_preserves_glob_escaping() -> None:
"""glob 查询应转义 SQL 通配符并显式启用 wildcard 模式。"""
service, _, transfer_repository = _make_service()
transfer_repository.async_list_by_title.return_value = []
transfer_repository.async_count_by_title.return_value = 0
page = await service.list_transfer(
title=r"show_100%*.mkv",
page=1,
count=30,
status=True,
)
assert page.total == 0
pattern = r"show\_100\%%.mkv"
transfer_repository.async_count_by_title.assert_awaited_once_with(
pattern,
status=True,
wildcard=True,
)
transfer_repository.async_list_by_title.assert_awaited_once_with(
pattern,
page=1,
count=30,
status=True,
wildcard=True,
)
@pytest.mark.asyncio
async def test_get_transfers_preserves_order_and_reports_missing_ids() -> None:
"""批量 AI 重做准备应保持去重后的输入顺序并报告缺失记录。"""
service, _, transfer_repository = _make_service()
transfer_repository.async_get.side_effect = [
SimpleNamespace(id=11),
None,
SimpleNamespace(id=13),
]
records, missing_ids = await service.get_transfers([11, 12, 13])
assert [record.id for record in records] == [11, 13]
assert missing_ids == [12]
assert transfer_repository.async_get.await_args_list == [
((11,), {}),
((12,), {}),
((13,), {}),
]