mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/jxxghp/MoviePilot.git
synced 2026-09-20 08:03:34 +08:00
168 lines
6.3 KiB
Python
168 lines
6.3 KiB
Python
import asyncio
|
|
from unittest.mock import AsyncMock, Mock
|
|
|
|
import pytest
|
|
|
|
from app.domain.context import MusicInfo
|
|
from app.domain.meta.metamusic import MetaMusic
|
|
from app.modules.acoustid import AcoustIdModule
|
|
from app.modules.anilist.anilist import AniListApi
|
|
from app.modules.bangumi.bangumi import BangumiApi
|
|
from app.modules.musicbrainz import MusicBrainzModule
|
|
from app.modules.theaudiodb import TheAudioDbModule
|
|
from app.schemas.types import MediaSource
|
|
|
|
|
|
def test_bangumi_sync_async_share_request_and_projection(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""Bangumi 同步异步搜索与人物投影应使用同一请求和结果规则。"""
|
|
api = BangumiApi()
|
|
sync_invoke = Mock(
|
|
side_effect=[
|
|
{"list": [{"id": 1}]},
|
|
[{"id": 2, "name": "角色", "actors": [{"id": 3}]}],
|
|
]
|
|
)
|
|
async_invoke = AsyncMock(
|
|
side_effect=[
|
|
{"list": [{"id": 1}]},
|
|
[{"id": 2, "name": "角色", "actors": [{"id": 3}]}],
|
|
]
|
|
)
|
|
monkeypatch.setattr(api, "_BangumiApi__invoke", sync_invoke)
|
|
monkeypatch.setattr(api, "_BangumiApi__async_invoke", async_invoke)
|
|
|
|
sync_search = api.search("葬送的芙莉莲")
|
|
async_search = asyncio.run(api.async_search("葬送的芙莉莲"))
|
|
sync_credits = api.credits(154587)
|
|
async_credits = asyncio.run(api.async_credits(154587))
|
|
|
|
assert sync_search == async_search == [{"id": 1}]
|
|
assert sync_credits == async_credits == [
|
|
{"id": 3, "career": ["角色"]}
|
|
]
|
|
assert sync_invoke.call_args_list[0].args == async_invoke.await_args_list[0].args
|
|
assert sync_invoke.call_args_list[1].args == async_invoke.await_args_list[1].args
|
|
assert sync_invoke.call_args_list[1].kwargs.keys() == async_invoke.await_args_list[1].kwargs.keys()
|
|
|
|
|
|
def test_anilist_sync_async_share_queries_and_projection(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""AniList 同步异步搜索与人物入口应共享查询文本、变量和投影。"""
|
|
api = AniListApi()
|
|
search_payload = {"Page": {"media": [{"id": 154587}]}}
|
|
person_payload = {"Staff": {"id": 95075, "name": {"native": "种崎敦美"}}}
|
|
sync_invoke = Mock(side_effect=[search_payload, person_payload])
|
|
async_invoke = AsyncMock(side_effect=[search_payload, person_payload])
|
|
monkeypatch.setattr(api, "_invoke", sync_invoke)
|
|
monkeypatch.setattr(api, "_async_invoke", async_invoke)
|
|
|
|
sync_search = AniListApi.search.__wrapped__(api, "Frieren", 12)
|
|
async_search = asyncio.run(
|
|
AniListApi.async_search.__wrapped__(api, "Frieren", 12)
|
|
)
|
|
sync_person = AniListApi.person_detail.__wrapped__(api, 95075)
|
|
async_person = asyncio.run(
|
|
AniListApi.async_person_detail.__wrapped__(api, 95075)
|
|
)
|
|
|
|
assert sync_search == async_search == [{"id": 154587}]
|
|
assert sync_person == async_person == person_payload["Staff"]
|
|
assert sync_invoke.call_args_list[0] == async_invoke.await_args_list[0]
|
|
assert sync_invoke.call_args_list[1] == async_invoke.await_args_list[1]
|
|
|
|
|
|
def test_musicbrainz_sync_async_share_detail_decision(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""MusicBrainz 显式详情识别应共享来源准入、参数和缓存收尾。"""
|
|
module = MusicBrainzModule()
|
|
module.cache = Mock()
|
|
meta = MetaMusic(
|
|
title="Yellow",
|
|
media_source=MediaSource.MusicBrainz,
|
|
media_id="recording-1",
|
|
)
|
|
expected = MusicInfo(
|
|
media_source=MediaSource.MusicBrainz,
|
|
media_id="recording-1",
|
|
title="Yellow",
|
|
)
|
|
sync_detail = Mock(return_value=expected)
|
|
async_detail = AsyncMock(return_value=expected)
|
|
monkeypatch.setattr(module, "recognize_music", sync_detail)
|
|
monkeypatch.setattr(module, "async_recognize_music", async_detail)
|
|
|
|
sync_result = module.recognize_media(meta=meta, cache=False)
|
|
async_result = asyncio.run(
|
|
module.async_recognize_media(meta=meta, cache=False)
|
|
)
|
|
|
|
assert sync_result == async_result == expected
|
|
assert async_detail.await_args is not None
|
|
assert sync_detail.call_args.args == async_detail.await_args.args
|
|
assert sync_detail.call_args.kwargs == async_detail.await_args.kwargs == {}
|
|
assert module.cache.update.call_count == 2
|
|
|
|
|
|
def test_theaudiodb_sync_async_share_request_plan_and_response(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""TheAudioDB 同步异步详情应共享请求计划、响应解析和实体选择。"""
|
|
module = TheAudioDbModule()
|
|
payload = {
|
|
"track": [
|
|
{
|
|
"idTrack": "32793500",
|
|
"strTrack": "Yellow",
|
|
"strArtist": "Coldplay",
|
|
}
|
|
]
|
|
}
|
|
sync_request = Mock(return_value=payload)
|
|
async_request = AsyncMock(return_value=payload)
|
|
monkeypatch.setattr(module, "_request_json", sync_request)
|
|
monkeypatch.setattr(module, "_async_request_json", async_request)
|
|
|
|
sync_result = module.recognize_music(
|
|
MediaSource.TheAudioDB, "32793500", music_type="recording"
|
|
)
|
|
async_result = asyncio.run(
|
|
module.async_recognize_music(
|
|
MediaSource.TheAudioDB, "32793500", music_type="recording"
|
|
)
|
|
)
|
|
|
|
assert sync_result == async_result
|
|
assert sync_result is not None and sync_result.media_id == "32793500"
|
|
assert sync_request.call_args == async_request.await_args
|
|
plan = module._request_plan("client-key", "track.php", {"h": "32793500"})
|
|
assert plan is not None
|
|
assert plan.url.endswith("/client-key/track.php")
|
|
assert plan.params == {"h": "32793500"}
|
|
|
|
|
|
def test_acoustid_lookup_plan_and_response_projection_are_transport_neutral() -> None:
|
|
"""AcoustID 请求载荷和响应筛选不得依赖同步或异步传输实现。"""
|
|
recording_id = "38035858-f990-4fbb-b3b2-f2f8b958eeba"
|
|
plan = AcoustIdModule._lookup_plan(" client-key ", 243, "AQADtM...")
|
|
payload = {
|
|
"status": "ok",
|
|
"results": [
|
|
{"score": 0.98, "recordings": [{"id": recording_id}]}
|
|
],
|
|
}
|
|
|
|
assert plan is not None
|
|
assert plan.data == {
|
|
"client": "client-key",
|
|
"duration": 243,
|
|
"fingerprint": "AQADtM...",
|
|
"meta": "recordingids",
|
|
"format": "json",
|
|
}
|
|
assert AcoustIdModule._project_lookup_response(200, payload) == recording_id
|
|
assert AcoustIdModule._project_lookup_response(503, payload) is None
|