import inspect from app.chain.download import DownloadChain from app.chain.search import SearchChain from app.chain.subscribe import SubscribeChain from app.chain.transfer import TransferChain LEGACY_MEDIA_ID_PARAMETERS = { "tmdbid", "doubanid", "bangumiid", "anilistid", "imdbid", "tvdbid", "mediaid", } def _assert_unified_media_identity(method, *, required: bool = True) -> None: """断言通用媒体方法仅暴露成对的统一身份参数。""" parameters = inspect.signature(method).parameters assert "media_source" in parameters assert "media_id" in parameters assert not LEGACY_MEDIA_ID_PARAMETERS.intersection(parameters) if required: assert parameters["media_source"].default is inspect.Parameter.empty assert parameters["media_id"].default is inspect.Parameter.empty def test_search_chain_uses_unified_media_identity() -> None: """精确资源与字幕搜索必须只接收统一媒体身份。""" for method in ( SearchChain.search_by_id, SearchChain.async_search_by_id, SearchChain.async_search_by_id_stream, SearchChain.async_search_subtitles_by_id, SearchChain.async_search_subtitles_by_id_stream, ): _assert_unified_media_identity(method) def test_download_chain_uses_unified_media_identity() -> None: """字幕下载必须只接收统一媒体身份。""" _assert_unified_media_identity(DownloadChain.download_subtitle) def test_transfer_chain_uses_unified_media_identity() -> None: """手动整理可选身份也必须使用成对的统一字段。""" _assert_unified_media_identity(TransferChain.manual_transfer, required=False) def test_subscribe_chain_uses_unified_media_identity() -> None: """订阅入口可选身份也必须使用成对的统一字段。""" _assert_unified_media_identity(SubscribeChain.add, required=False) _assert_unified_media_identity(SubscribeChain.async_add, required=False)