# -*- coding: utf-8 -*- """识别测试类请求的数据源选择回归测试。 识别测试(名称测试)前端只传请求级数据源 media_source,不携带 media_id; 该路径必须按名称在指定数据源内识别,而不是被"显式身份必须成对"规则拦截。 """ import asyncio from unittest.mock import AsyncMock, Mock, patch from app.chain import ChainBase from app.domain.context import MediaInfo from app.domain.metainfo import MetaInfo from app.schemas.types import MediaSource, MediaType # 用户反馈的识别测试失败样例 FAILING_TITLE = "[ANI] 關於我轉生變成史萊姆這檔事 第四季 - 90 [1080P][Baha][WEB-DL][AAC AVC][CHT].mp4" def _tmdb_media() -> MediaInfo: """构造带 TMDB 身份的识别结果。""" return MediaInfo( media_source=MediaSource.TMDB, media_id="120089", tmdb_id=120089, title="关于我转生变成史莱姆这档事", type=MediaType.TV, ) def test_recognize_media_with_source_only_uses_name_search(): """仅传 media_source 时应按名称在指定数据源识别,不得直接拒绝。""" chain = ChainBase() meta = MetaInfo(FAILING_TITLE) captured = {} def fake_native(module_kwargs, cache): captured.update(module_kwargs) return _tmdb_media() with patch.object(chain, "_run_native_media_recognize", side_effect=fake_native), \ patch.object(chain, "_supplement_media_recognize", side_effect=lambda **kw: kw["mediainfo"]), \ patch("app.chain._recognition.MoviePilotServerHelper"): result = chain.recognize_media(meta=meta, media_source=MediaSource.TMDB, cache=False) assert result is not None assert result.tmdb_id == 120089 # 数据源约束透传到模块层,且无显式 ID assert captured["media_source"] == MediaSource.TMDB assert captured["media_id"] is None # 名称识别应沿用 meta 推断的类型 assert captured["mtype"] == MediaType.TV def test_async_recognize_media_with_source_only_uses_name_search(): """异步入口与同步入口保持同一语义。""" chain = ChainBase() meta = MetaInfo(FAILING_TITLE) captured = {} async def fake_native(module_kwargs, cache): captured.update(module_kwargs) return _tmdb_media() async def fake_supplement(**kwargs): return kwargs["mediainfo"] # 异步路径上报共享识别为协程,需要可 await 的桩 helper = Mock() helper.async_report_recognize_share = AsyncMock() with patch.object(chain, "_async_run_native_media_recognize", side_effect=fake_native), \ patch.object(chain, "_async_supplement_media_recognize", side_effect=fake_supplement), \ patch("app.chain._recognition.MoviePilotServerHelper", helper): result = asyncio.run( chain.async_recognize_media(meta=meta, media_source=MediaSource.TMDB, cache=False) ) assert result is not None assert result.tmdb_id == 120089 assert captured["media_source"] == MediaSource.TMDB assert captured["media_id"] is None def test_recognize_media_accepts_string_source_only(): """第三方客户端可能传字符串数据源,应规范为枚举后按名称识别。""" chain = ChainBase() meta = MetaInfo(FAILING_TITLE) captured = {} def fake_native(module_kwargs, cache): captured.update(module_kwargs) return _tmdb_media() with patch.object(chain, "_run_native_media_recognize", side_effect=fake_native), \ patch.object(chain, "_supplement_media_recognize", side_effect=lambda **kw: kw["mediainfo"]), \ patch("app.chain._recognition.MoviePilotServerHelper"): result = chain.recognize_media(meta=meta, media_source="themoviedb", cache=False) assert result is not None assert captured["media_source"] == MediaSource.TMDB def test_recognize_media_meta_identity_same_source_uses_id(): """仅传数据源且 meta 自带同源身份(如 {tmdbid=})时应直接按身份识别。""" chain = ChainBase() meta = MetaInfo("空之境界 第五章 矛盾螺旋 (2008) {tmdbid=23155}") captured = {} def fake_native(module_kwargs, cache): captured.update(module_kwargs) return _tmdb_media() with patch.object(chain, "_run_native_media_recognize", side_effect=fake_native), \ patch.object(chain, "_supplement_media_recognize", side_effect=lambda **kw: kw["mediainfo"]), \ patch("app.chain._recognition.MoviePilotServerHelper"): result = chain.recognize_media(meta=meta, media_source=MediaSource.TMDB, cache=False) assert result is not None assert captured["media_source"] == MediaSource.TMDB assert captured["media_id"] == "23155" def test_recognize_media_media_id_without_source_still_rejected(): """显式 media_id 缺少有效来源时仍应拒绝,成对约束不放宽。""" chain = ChainBase() native = Mock() with patch.object(chain, "_run_native_media_recognize", native): result = chain.recognize_media(meta=MetaInfo("任意标题"), media_id="12345") assert result is None native.assert_not_called() def test_recognize_media_invalid_pair_still_rejected(): """media_id 为 0 等无效值与来源组合仍应拒绝。""" chain = ChainBase() native = Mock() with patch.object(chain, "_run_native_media_recognize", native): result = chain.recognize_media( meta=MetaInfo("任意标题"), media_source=MediaSource.TMDB, media_id="0" ) assert result is None native.assert_not_called()