feat: add AniList rankings and discovery

This commit is contained in:
jxxghp
2026-07-22 12:53:58 +08:00
parent a3c90c64ca
commit fb32c59713
17 changed files with 1904 additions and 66 deletions
+122
View File
@@ -0,0 +1,122 @@
import asyncio
from unittest.mock import AsyncMock, Mock, patch
import pytest
from app.api.endpoints.media import search
from app.chain import ChainBase
from app.modules.douban import DoubanModule
from app.modules.themoviedb import TheMovieDbModule
@pytest.mark.parametrize(
("search_type", "method_name", "source"),
[
("collection", "async_search_collections", "themoviedb"),
("person", "async_search_persons", "douban"),
],
)
def test_media_search_endpoint_forwards_source(
search_type: str, method_name: str, source: str
) -> None:
"""媒体搜索接口应将合集和人物的数据源下传到处理链。"""
chain = Mock()
search_method = AsyncMock(return_value=[])
setattr(chain, method_name, search_method)
with patch("app.api.endpoints.media.MediaChain", return_value=chain):
result = asyncio.run(
search(
title="测试",
type=search_type,
source=source,
_=Mock(),
)
)
assert result == []
search_method.assert_awaited_once_with(name="测试", source=source)
@pytest.mark.parametrize(
("method_name", "module_method_name"),
[
("async_search_persons", "async_search_persons"),
("async_search_collections", "async_search_collections"),
],
)
def test_chain_forwards_source_to_modules(
method_name: str, module_method_name: str
) -> None:
"""处理链应将人物和合集的请求级数据源传递给媒体模块。"""
chain = Mock(spec=ChainBase)
chain.async_run_module = AsyncMock(return_value=[])
result = asyncio.run(
getattr(ChainBase, method_name)(
chain,
name="测试",
source="themoviedb",
)
)
assert result == []
chain.async_run_module.assert_awaited_once_with(
module_method_name,
name="测试",
source="themoviedb",
)
def test_tmdb_person_search_respects_explicit_source(monkeypatch) -> None:
"""TMDB人物搜索应拒绝其他来源,并允许显式选择覆盖系统默认来源。"""
monkeypatch.setattr("app.modules.themoviedb.settings.SEARCH_SOURCE", "douban")
module = TheMovieDbModule()
module.tmdb = Mock()
module.tmdb.async_search_persons = AsyncMock(return_value=[])
skipped = asyncio.run(
module.async_search_persons(name="测试", source="douban")
)
result = asyncio.run(
module.async_search_persons(name="测试", source="themoviedb")
)
assert skipped is None
assert result == []
module.tmdb.async_search_persons.assert_awaited_once_with("测试")
def test_douban_person_search_respects_explicit_source(monkeypatch) -> None:
"""豆瓣人物搜索应拒绝其他来源,并允许显式选择覆盖系统默认来源。"""
monkeypatch.setattr(
"app.modules.douban.settings.SEARCH_SOURCE", "themoviedb"
)
module = DoubanModule()
module.doubanapi = Mock()
module.doubanapi.async_person_search = AsyncMock(return_value={})
skipped = asyncio.run(
module.async_search_persons(name="测试", source="themoviedb")
)
result = asyncio.run(
module.async_search_persons(name="测试", source="douban")
)
assert skipped is None
assert result == []
module.doubanapi.async_person_search.assert_awaited_once_with(keyword="测试")
def test_tmdb_collection_search_rejects_unsupported_source() -> None:
"""TMDB合集搜索不应处理非TMDB来源请求。"""
module = TheMovieDbModule()
module.tmdb = Mock()
module.tmdb.async_search_collections = AsyncMock(return_value=[])
result = asyncio.run(
module.async_search_collections(name="测试", source="douban")
)
assert result is None
module.tmdb.async_search_collections.assert_not_awaited()