fix: merge downloader tracker mappings

This commit is contained in:
jxxghp
2026-08-24 01:08:45 +08:00
parent b0c4809d1a
commit f37407d0f3
6 changed files with 104 additions and 8 deletions
+8 -5
View File
@@ -4178,16 +4178,19 @@
"version": 1
},
"get_torrent_trackers": {
"aggregation": "legacy",
"aggregation": "ordered_mapping_merge",
"error_policy": "isolate_provider",
"execution": "sync_or_async",
"family": "downloader",
"input_contract": "DownloaderKeywordArguments",
"input_contract": "TorrentTrackersRequest",
"plugin_short_circuit": true,
"public_to_plugins": true,
"required_parameters": [],
"result_contract": "DownloaderProviderResult",
"result_shape": "any",
"required_parameters": [
"downloader",
"hash_string"
],
"result_contract": "dict[str, list[str]] | None",
"result_shape": "mapping",
"supports_async": true,
"supports_sync": true,
"timeout_policy": "caller_budget",
@@ -242,6 +242,75 @@ def test_ordered_list_contract_bypasses_legacy_signature_relay() -> None:
assert dispatcher.dispatch("search_medias") == ["plugin", "system"]
def test_ordered_mapping_contract_merges_system_downloader_results() -> None:
"""未指定下载器时应按宿主优先级合并各 provider 的 Tracker 映射。"""
class TrackerModule:
"""返回单个下载器 Tracker 映射的测试模块。"""
def __init__(self, name: str, priority: int) -> None:
"""保存下载器名称和 provider 优先级。"""
self._name = name
self._priority = priority
def get_name(self) -> str:
"""返回测试模块名。"""
return self._name
def get_priority(self) -> int:
"""返回测试优先级。"""
return self._priority
def get_torrent_trackers(
self,
hash_string: str,
downloader: str | None = None,
) -> dict[str, list[str]]:
"""返回当前测试下载器的 Tracker 映射。"""
assert hash_string == "hash"
assert downloader is None
return {self._name: [f"https://{self._name}.test/announce"]}
dispatcher, _, _, _ = _dispatcher(
modules=[
TrackerModule("transmission", 20),
TrackerModule("qbittorrent", 10),
]
)
assert dispatcher.dispatch(
"get_torrent_trackers",
hash_string="hash",
downloader=None,
) == {
"qbittorrent": ["https://qbittorrent.test/announce"],
"transmission": ["https://transmission.test/announce"],
}
def test_plugin_mapping_keeps_existing_host_short_circuit() -> None:
"""插件返回 Tracker 映射后仍应保持插件优先,不再调用宿主 provider。"""
system_call = Mock(return_value={"system": ["https://system.test"]})
module = _Module("系统", 10, system_call)
setattr(module, "get_torrent_trackers", module.execute)
dispatcher, _, _, _ = _dispatcher(
plugins={
("P1", "插件一"): {
"get_torrent_trackers": lambda **_kwargs: {
"plugin": ["https://plugin.test"]
}
},
},
modules=[module],
)
assert dispatcher.dispatch(
"get_torrent_trackers",
hash_string="hash",
downloader=None,
) == {"plugin": ["https://plugin.test"]}
system_call.assert_not_called()
def test_module_exception_uses_error_policy_and_continues() -> None:
"""普通异常应交给错误策略,后续空结果模块仍可继续运行。"""
def broken():
+9
View File
@@ -169,6 +169,15 @@ def test_heterogeneous_torrent_files_result_remains_legacy_compatible() -> None:
assert contract.result_shape is ModuleResultShape.ANY
def test_torrent_tracker_contract_merges_downloader_mappings() -> None:
"""Tracker 查询应登记跨下载器有序映射合并,而不是首个字典短路。"""
contract = get_module_method_contract("get_torrent_trackers")
assert contract.required_parameters == ("hash_string", "downloader")
assert contract.aggregation is ModuleResultAggregation.ORDERED_MAPPING_MERGE
assert contract.result_shape is ModuleResultShape.MAPPING
def test_attachment_result_diagnostics_distinguish_bytes_and_strings() -> None:
"""附件契约应区分二进制内容和可展示字符串,偏差仍仅供诊断。"""
assert diagnose_module_result("download_qq_file_bytes", b"content") == ()