refactor(chain): 处理链功能域 mixin 化,清理未使用导入并根治兼容层循环导入

- ChainBase 拆分为 RecognitionMixin/MessageProcessingMixin/NotificationMixin
- TransferChain 拆分为 7 个功能 mixin(_mixins.py),SubscribeChain 音乐订阅域拆出 _music.py
- 斜杠命令交互四件套收敛为 InteractionChainMixin 委托,会话管理器移至 application 层,chain 层不再 re-export
- 模块基础类收敛到 app/modules/_base(notification/mediaserver 语义重命名)
- 清理 app/chain/__init__.py 24 个未使用导入,修正 49 处测试 patch 目标到实际命名空间
- 兼容层 legacy 符号不再并入 __all__,根治 schemas 初始化反向拉起 application.transfer 的循环导入
- 修复 bangumi 集数为字符串时 set_bangumi_info 抛 TypeError
- 新增重复代码等架构门禁测试;capability 清单校验排除下划线内部目录
This commit is contained in:
jxxghp
2026-08-16 16:30:16 +08:00
parent 24671f8f18
commit 7e851dbfa7
102 changed files with 6041 additions and 5888 deletions
+33 -33
View File
@@ -48,10 +48,10 @@ def test_report_shared_result_after_local_recognize_success():
mediainfo = _tmdb_media("测试电影", 100, MediaType.MOVIE, year="2024")
with patch.object(chain, "run_module", return_value=mediainfo) as run_module, patch(
"app.chain.MoviePilotServerHelper.report_recognize_share",
"app.chain._recognition.MoviePilotServerHelper.report_recognize_share",
return_value=True,
) as report_mock, patch(
"app.chain.MoviePilotServerHelper.query_recognize_share"
"app.chain._recognition.MoviePilotServerHelper.query_recognize_share"
) as query_mock:
result = chain.recognize_media(meta=meta, cache=False)
@@ -72,7 +72,7 @@ def test_query_shared_result_when_local_recognize_failed():
"run_module",
side_effect=[None, shared_media],
) as run_module, patch(
"app.chain.MoviePilotServerHelper.query_recognize_share",
"app.chain._recognition.MoviePilotServerHelper.query_recognize_share",
return_value={
"type": "tv",
"media_source": "themoviedb",
@@ -80,7 +80,7 @@ def test_query_shared_result_when_local_recognize_failed():
"season": 1,
},
) as query_mock, patch(
"app.chain.MoviePilotServerHelper.to_recognize_params",
"app.chain._recognition.MoviePilotServerHelper.to_recognize_params",
return_value={
"mtype": MediaType.TV,
"media_source": MediaSource.TMDB,
@@ -88,7 +88,7 @@ def test_query_shared_result_when_local_recognize_failed():
"season": 1,
},
), patch(
"app.chain.MoviePilotServerHelper.report_recognize_share",
"app.chain._recognition.MoviePilotServerHelper.report_recognize_share",
return_value=False,
), patch.object(
chain,
@@ -119,7 +119,7 @@ def test_async_query_shared_result_when_local_recognize_failed():
"async_run_module",
async_run_module,
), patch(
"app.chain.MoviePilotServerHelper.async_query_recognize_share",
"app.chain._recognition.MoviePilotServerHelper.async_query_recognize_share",
AsyncMock(return_value={
"type": "tv",
"media_source": "themoviedb",
@@ -127,7 +127,7 @@ def test_async_query_shared_result_when_local_recognize_failed():
"season": 2,
}),
) as query_mock, patch(
"app.chain.MoviePilotServerHelper.to_recognize_params",
"app.chain._recognition.MoviePilotServerHelper.to_recognize_params",
return_value={
"mtype": MediaType.TV,
"media_source": MediaSource.TMDB,
@@ -135,7 +135,7 @@ def test_async_query_shared_result_when_local_recognize_failed():
"season": 2,
},
), patch(
"app.chain.MoviePilotServerHelper.async_report_recognize_share",
"app.chain._recognition.MoviePilotServerHelper.async_report_recognize_share",
AsyncMock(return_value=False),
), patch.object(
chain,
@@ -173,14 +173,14 @@ def test_backfill_local_cache_after_shared_recognize_success():
"run_module",
side_effect=[None, shared_media, None],
) as run_module_mock, patch(
"app.chain.MoviePilotServerHelper.query_recognize_share",
"app.chain._recognition.MoviePilotServerHelper.query_recognize_share",
return_value={
"type": "movie",
"media_source": "themoviedb",
"media_id": "700",
},
), patch(
"app.chain.MoviePilotServerHelper.to_recognize_params",
"app.chain._recognition.MoviePilotServerHelper.to_recognize_params",
return_value={
"mtype": MediaType.MOVIE,
"media_source": MediaSource.TMDB,
@@ -188,7 +188,7 @@ def test_backfill_local_cache_after_shared_recognize_success():
"season": None,
},
), patch(
"app.chain.MoviePilotServerHelper.report_recognize_share",
"app.chain._recognition.MoviePilotServerHelper.report_recognize_share",
return_value=False,
):
result = chain.recognize_media(meta=meta, cache=False)
@@ -288,7 +288,7 @@ def test_report_shared_result_with_distinct_keyword_meta():
mediainfo = _tmdb_media("测试剧集", 402, MediaType.TV, year="2024")
with patch.object(chain, "run_module", return_value=mediainfo), patch(
"app.chain.MoviePilotServerHelper.report_recognize_share",
"app.chain._recognition.MoviePilotServerHelper.report_recognize_share",
return_value=True,
) as report_mock:
result = chain.recognize_media(meta=meta, share_meta=share_meta, cache=False)
@@ -315,7 +315,7 @@ def test_query_shared_result_with_distinct_keyword_meta():
"run_module",
side_effect=[None, shared_media],
), patch(
"app.chain.MoviePilotServerHelper.query_recognize_share",
"app.chain._recognition.MoviePilotServerHelper.query_recognize_share",
return_value={
"type": "tv",
"media_source": "themoviedb",
@@ -323,7 +323,7 @@ def test_query_shared_result_with_distinct_keyword_meta():
"season": 1,
},
) as query_mock, patch(
"app.chain.MoviePilotServerHelper.to_recognize_params",
"app.chain._recognition.MoviePilotServerHelper.to_recognize_params",
return_value={
"mtype": MediaType.TV,
"media_source": MediaSource.TMDB,
@@ -331,7 +331,7 @@ def test_query_shared_result_with_distinct_keyword_meta():
"season": 1,
},
), patch(
"app.chain.MoviePilotServerHelper.report_recognize_share",
"app.chain._recognition.MoviePilotServerHelper.report_recognize_share",
return_value=False,
), patch.object(
chain,
@@ -359,10 +359,10 @@ def test_skip_report_when_local_recognize_hits_cache():
mediainfo.recognize_cache_hit = True
with patch.object(chain, "run_module", return_value=mediainfo) as run_module, patch(
"app.chain.MoviePilotServerHelper.report_recognize_share",
"app.chain._recognition.MoviePilotServerHelper.report_recognize_share",
return_value=True,
) as report_mock, patch(
"app.chain.MoviePilotServerHelper.query_recognize_share"
"app.chain._recognition.MoviePilotServerHelper.query_recognize_share"
) as query_mock:
result = chain.recognize_media(meta=meta)
@@ -385,10 +385,10 @@ def test_async_skip_report_when_local_recognize_hits_cache():
"async_run_module",
AsyncMock(return_value=mediainfo),
) as async_run_module, patch(
"app.chain.MoviePilotServerHelper.async_report_recognize_share",
"app.chain._recognition.MoviePilotServerHelper.async_report_recognize_share",
AsyncMock(return_value=True),
) as report_mock, patch(
"app.chain.MoviePilotServerHelper.async_query_recognize_share",
"app.chain._recognition.MoviePilotServerHelper.async_query_recognize_share",
AsyncMock(),
) as query_mock:
result = await chain.async_recognize_media(meta=meta)
@@ -579,10 +579,10 @@ def test_chain_recognize_media_reports_music_share_result():
music = _music_info()
with patch.object(chain, "recognize_music_from_source", return_value=music), patch(
"app.chain.MoviePilotServerHelper.report_recognize_share",
"app.chain._recognition.MoviePilotServerHelper.report_recognize_share",
return_value=True,
) as report_mock, patch(
"app.chain.MoviePilotServerHelper.query_recognize_share"
"app.chain._recognition.MoviePilotServerHelper.query_recognize_share"
) as query_mock:
result = chain.recognize_media(meta=meta, cache=False)
@@ -602,7 +602,7 @@ def test_chain_recognize_media_queries_music_share_when_local_failed():
"recognize_music_from_source",
side_effect=[None, music],
) as recognize_source, patch(
"app.chain.MoviePilotServerHelper.query_recognize_share",
"app.chain._recognition.MoviePilotServerHelper.query_recognize_share",
return_value={
"type": "music",
"media_source": "musicbrainz",
@@ -610,7 +610,7 @@ def test_chain_recognize_media_queries_music_share_when_local_failed():
"music_type": "recording",
},
), patch(
"app.chain.MoviePilotServerHelper.to_recognize_params",
"app.chain._recognition.MoviePilotServerHelper.to_recognize_params",
return_value={
"mtype": MediaType.MUSIC,
"media_source": MediaSource.MusicBrainz,
@@ -619,7 +619,7 @@ def test_chain_recognize_media_queries_music_share_when_local_failed():
"season": None,
},
), patch(
"app.chain.MoviePilotServerHelper.report_recognize_share",
"app.chain._recognition.MoviePilotServerHelper.report_recognize_share",
return_value=False,
), patch.object(
chain,
@@ -650,7 +650,7 @@ def test_chain_recognize_media_queries_music_share_after_local_fallback():
"recognize_music_from_source",
side_effect=[fallback, music],
) as recognize_source, patch(
"app.chain.MoviePilotServerHelper.query_recognize_share",
"app.chain._recognition.MoviePilotServerHelper.query_recognize_share",
return_value={
"type": "music",
"media_source": "musicbrainz",
@@ -658,7 +658,7 @@ def test_chain_recognize_media_queries_music_share_after_local_fallback():
"music_type": "recording",
},
) as query_share, patch(
"app.chain.MoviePilotServerHelper.to_recognize_params",
"app.chain._recognition.MoviePilotServerHelper.to_recognize_params",
return_value={
"mtype": MediaType.MUSIC,
"media_source": MediaSource.MusicBrainz,
@@ -670,7 +670,7 @@ def test_chain_recognize_media_queries_music_share_after_local_fallback():
chain,
"_update_local_recognize_cache",
), patch(
"app.chain.settings.MEDIA_RECOGNIZE_SHARE",
"app.chain._recognition.settings.MEDIA_RECOGNIZE_SHARE",
True,
):
result = chain.recognize_media(meta=meta, cache=False)
@@ -697,7 +697,7 @@ def test_chain_async_recognize_media_queries_music_share_after_local_fallback():
"async_recognize_music_from_source",
new=AsyncMock(side_effect=[fallback, music]),
) as recognize_source, patch(
"app.chain.MoviePilotServerHelper.async_query_recognize_share",
"app.chain._recognition.MoviePilotServerHelper.async_query_recognize_share",
new=AsyncMock(return_value={
"type": "music",
"media_source": "musicbrainz",
@@ -705,7 +705,7 @@ def test_chain_async_recognize_media_queries_music_share_after_local_fallback():
"music_type": "recording",
}),
) as query_share, patch(
"app.chain.MoviePilotServerHelper.to_recognize_params",
"app.chain._recognition.MoviePilotServerHelper.to_recognize_params",
return_value={
"mtype": MediaType.MUSIC,
"media_source": MediaSource.MusicBrainz,
@@ -718,7 +718,7 @@ def test_chain_async_recognize_media_queries_music_share_after_local_fallback():
"_async_update_local_recognize_cache",
new=AsyncMock(),
), patch(
"app.chain.settings.MEDIA_RECOGNIZE_SHARE",
"app.chain._recognition.settings.MEDIA_RECOGNIZE_SHARE",
True,
):
result = await chain.async_recognize_media(meta=meta, cache=False)
@@ -749,12 +749,12 @@ def test_chain_recognize_media_skips_music_report_for_fallback_result():
fallback = MusicInfo(title="未知曲目", artists=["未知艺术家"])
with patch.object(chain, "recognize_music_from_source", return_value=fallback), patch(
"app.chain.MoviePilotServerHelper.query_recognize_share",
"app.chain._recognition.MoviePilotServerHelper.query_recognize_share",
return_value=None,
) as query_mock, patch(
"app.chain.MoviePilotServerHelper.report_recognize_share"
"app.chain._recognition.MoviePilotServerHelper.report_recognize_share"
) as report_mock, patch(
"app.chain.settings.MEDIA_RECOGNIZE_SHARE", True
"app.chain._recognition.settings.MEDIA_RECOGNIZE_SHARE", True
):
result = chain.recognize_media(meta=meta, cache=False)