mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 23:47:41 +08:00
fix(acoustid): 测试函数独立检测本地 fpcalc 依赖而非仅校验网络
原先 test() 依赖 init_module() 一次性缓存的 self._fpcalc_path,初始化之后再安装或移除 fpcalc 都不会反映在测试结果中,导致 UI 测试可能错误显示为通过。 - 抽出静态方法 _resolve_fpcalc() 基于 shutil.which 定位可执行 fpcalc,init_module() 复用 - test() 每次自行重新定位 fpcalc,缺失则直接失败且不发起网络请求,并写回 self._fpcalc_path - 新增 3 个回归用例覆盖本地依赖检测与独立于 init 缓存的行为
This commit is contained in:
@@ -34,9 +34,17 @@ class AcoustIdModule(_ModuleBase):
|
|||||||
self._cache: OrderedDict[tuple[str, int, int], Optional[str]] = OrderedDict()
|
self._cache: OrderedDict[tuple[str, int, int], Optional[str]] = OrderedDict()
|
||||||
self._cache_lock = threading.Lock()
|
self._cache_lock = threading.Lock()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _resolve_fpcalc() -> Optional[str]:
|
||||||
|
"""在 PATH 中定位可执行的 fpcalc,返回其绝对路径,缺失则返回 None。
|
||||||
|
|
||||||
|
shutil.which 在 POSIX 上已校验文件可执行,足以判定本地依赖是否就绪。
|
||||||
|
"""
|
||||||
|
return shutil.which("fpcalc") or None
|
||||||
|
|
||||||
def init_module(self) -> None:
|
def init_module(self) -> None:
|
||||||
"""定位 fpcalc 工具并清空可能过期的文件识别缓存。"""
|
"""定位 fpcalc 工具并清空可能过期的文件识别缓存。"""
|
||||||
self._fpcalc_path = shutil.which("fpcalc")
|
self._fpcalc_path = self._resolve_fpcalc()
|
||||||
with self._cache_lock:
|
with self._cache_lock:
|
||||||
self._cache.clear()
|
self._cache.clear()
|
||||||
if not self._fpcalc_path:
|
if not self._fpcalc_path:
|
||||||
@@ -52,11 +60,18 @@ class AcoustIdModule(_ModuleBase):
|
|||||||
self._cache.clear()
|
self._cache.clear()
|
||||||
|
|
||||||
def test(self) -> Tuple[bool, str]:
|
def test(self) -> Tuple[bool, str]:
|
||||||
"""检查 API Key、fpcalc 和 AcoustID API 的基础连通性。"""
|
"""检查 API Key、本地 fpcalc 依赖与 AcoustID API 的基础连通性。
|
||||||
|
|
||||||
|
本地依赖检测独立于 init_module 的缓存快照,重新定位 fpcalc,确保即便
|
||||||
|
模块初始化早于 fpcalc 安装,或运行期依赖被移除,测试也能如实反映本地
|
||||||
|
依赖状态,而不是只校验网络连通性。
|
||||||
|
"""
|
||||||
if not str(settings.ACOUSTID_API_KEY or "").strip():
|
if not str(settings.ACOUSTID_API_KEY or "").strip():
|
||||||
return False, "AcoustID API Key 未配置"
|
return False, "AcoustID API Key 未配置"
|
||||||
if not self._fpcalc_path:
|
fpcalc_path = self._resolve_fpcalc()
|
||||||
|
if not fpcalc_path:
|
||||||
return False, "未找到 fpcalc,请先安装 Chromaprint"
|
return False, "未找到 fpcalc,请先安装 Chromaprint"
|
||||||
|
self._fpcalc_path = fpcalc_path
|
||||||
response = RequestUtils(
|
response = RequestUtils(
|
||||||
ua=settings.USER_AGENT,
|
ua=settings.USER_AGENT,
|
||||||
proxies=settings.PROXY,
|
proxies=settings.PROXY,
|
||||||
|
|||||||
@@ -129,6 +129,56 @@ def test_identify_music_by_fingerprint_skips_missing_fpcalc(tmp_path, monkeypatc
|
|||||||
post_res.assert_not_called()
|
post_res.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_test_fails_without_local_fpcalc_and_skips_network(monkeypatch):
|
||||||
|
"""缺少本地 fpcalc 时,测试应直接失败且不发起任何网络请求。"""
|
||||||
|
monkeypatch.setattr("app.modules.acoustid.shutil.which", lambda _: None)
|
||||||
|
monkeypatch.setattr("app.modules.acoustid.settings.ACOUSTID_API_KEY", "client-key")
|
||||||
|
get_res = Mock()
|
||||||
|
monkeypatch.setattr(RequestUtils, "get_res", get_res)
|
||||||
|
|
||||||
|
module = AcoustIdModule()
|
||||||
|
ok, message = module.test()
|
||||||
|
|
||||||
|
assert ok is False
|
||||||
|
assert "fpcalc" in message
|
||||||
|
get_res.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_test_passes_with_local_fpcalc_and_network(monkeypatch):
|
||||||
|
"""fpcalc 存在且网络可达时,测试应成功并刷新本地依赖快照。"""
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"app.modules.acoustid.shutil.which",
|
||||||
|
lambda _: "/usr/bin/fpcalc",
|
||||||
|
)
|
||||||
|
monkeypatch.setattr("app.modules.acoustid.settings.ACOUSTID_API_KEY", "client-key")
|
||||||
|
monkeypatch.setattr(RequestUtils, "get_res", Mock(return_value=FakeResponse({})))
|
||||||
|
|
||||||
|
module = AcoustIdModule()
|
||||||
|
ok, message = module.test()
|
||||||
|
|
||||||
|
assert ok is True
|
||||||
|
assert message == ""
|
||||||
|
assert module._fpcalc_path == "/usr/bin/fpcalc"
|
||||||
|
|
||||||
|
|
||||||
|
def test_test_resolves_fpcalc_independently_of_init(monkeypatch):
|
||||||
|
"""即便 init_module 早期未定位到 fpcalc,测试也应重新检测本地依赖。"""
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"app.modules.acoustid.shutil.which",
|
||||||
|
lambda _: "/usr/bin/fpcalc",
|
||||||
|
)
|
||||||
|
monkeypatch.setattr("app.modules.acoustid.settings.ACOUSTID_API_KEY", "client-key")
|
||||||
|
monkeypatch.setattr(RequestUtils, "get_res", Mock(return_value=FakeResponse({})))
|
||||||
|
|
||||||
|
module = AcoustIdModule()
|
||||||
|
module._fpcalc_path = None
|
||||||
|
|
||||||
|
ok, _ = module.test()
|
||||||
|
|
||||||
|
assert ok is True
|
||||||
|
assert module._fpcalc_path == "/usr/bin/fpcalc"
|
||||||
|
|
||||||
|
|
||||||
def test_async_identify_music_by_fingerprint_uses_async_process_and_http(
|
def test_async_identify_music_by_fingerprint_uses_async_process_and_http(
|
||||||
tmp_path,
|
tmp_path,
|
||||||
monkeypatch,
|
monkeypatch,
|
||||||
|
|||||||
Reference in New Issue
Block a user