From 598a480a0b694047e84207f43fbde14a1cc50f58 Mon Sep 17 00:00:00 2001 From: BoulderBadgeDad Date: Mon, 29 Jun 2026 17:07:03 -0700 Subject: [PATCH] fix: Deezer dropped from hybrid + no green light despite working as primary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Deezer download is_configured()/is_available() returned the raw _authenticated flag, but the ARL auth is LAZY — only is_authenticated() triggers it (post-boot). The primary-source download path calls is_authenticated() so Deezer auths and works; the hybrid-mode source gate (web_server _resolve sources) and the green-light status (orchestrator.get_source_status) both read is_configured(), which never fired the auth — so Deezer looked unauthenticated, got pruned from the hybrid chain, and showed no green dot. Route is_configured()/is_available() through is_authenticated() so all three agree (and the auth still defers cleanly during boot). 4 seam tests: lazy-auth fires post-boot, defers during boot, stays True when already authed, False with no ARL. --- core/deezer_download_client.py | 8 ++- tests/test_deezer_download_is_configured.py | 66 +++++++++++++++++++++ 2 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 tests/test_deezer_download_is_configured.py diff --git a/core/deezer_download_client.py b/core/deezer_download_client.py index 99fe4753..4b35b762 100644 --- a/core/deezer_download_client.py +++ b/core/deezer_download_client.py @@ -227,10 +227,14 @@ class DeezerDownloadClient(DownloadSourcePlugin): self.shutdown_check = check_callable def is_configured(self) -> bool: - return self._authenticated + # Go through is_authenticated() so the lazy ARL auth fires (post-boot) — otherwise the raw + # _authenticated flag is still False until something else triggers it, and the hybrid-mode + # gate + the green-light status (both read is_configured) silently drop Deezer even though it + # downloads fine as a primary source (that path calls is_authenticated). Keeps the three in sync. + return self.is_authenticated() def is_available(self) -> bool: - return self._authenticated + return self.is_authenticated() def is_authenticated(self) -> bool: if self._pending_arl and not self._authenticated: diff --git a/tests/test_deezer_download_is_configured.py b/tests/test_deezer_download_is_configured.py new file mode 100644 index 00000000..a74003d3 --- /dev/null +++ b/tests/test_deezer_download_is_configured.py @@ -0,0 +1,66 @@ +"""Regression: Deezer download client `is_configured()` must fire the lazy ARL auth. + +The hybrid-mode source gate + the green-light status both read `is_configured()`. It used to return +the raw `_authenticated` flag, which stays False until something calls `is_authenticated()` (the lazy +ARL auth). So Deezer downloaded fine as a *primary* source (that path auths) but was silently dropped +from a hybrid chain and showed no green light. The fix routes is_configured()/is_available() through +is_authenticated() so the three can't drift. +""" + +from __future__ import annotations + +import core.boot_phase as boot +from core.deezer_download_client import DeezerDownloadClient + + +def _bare_client(authenticated=False, pending_arl=None): + """A client without __init__ (no config/network) — just the auth-state seam we're testing.""" + c = DeezerDownloadClient.__new__(DeezerDownloadClient) + c._authenticated = authenticated + c._pending_arl = pending_arl + return c + + +def test_is_configured_fires_lazy_arl_auth_post_boot(monkeypatch): + monkeypatch.setattr(boot, "is_boot_phase", lambda: False) + c = _bare_client(authenticated=False, pending_arl="fake-arl") + calls = [] + + def fake_auth(arl): + calls.append(arl) + c._authenticated = True + + c._authenticate = fake_auth + + # The hybrid gate / green light call this — it must trigger the deferred auth, not report False. + assert c.is_configured() is True + assert c.is_available() is True + assert calls == ["fake-arl"] + assert c._pending_arl is None + + # Idempotent: once authed, no repeat network auth. + assert c.is_configured() is True + assert calls == ["fake-arl"] + + +def test_is_configured_defers_during_boot(monkeypatch): + monkeypatch.setattr(boot, "is_boot_phase", lambda: True) + c = _bare_client(authenticated=False, pending_arl="fake-arl") + + def boom(arl): + raise AssertionError("must not authenticate (network) during boot") + + c._authenticate = boom + assert c.is_configured() is False # deferred — stays unauthenticated, but never auths mid-boot + + +def test_is_configured_true_when_already_authed(): + c = _bare_client(authenticated=True, pending_arl=None) + assert c.is_configured() is True + assert c.is_available() is True + + +def test_is_configured_false_with_no_arl(monkeypatch): + monkeypatch.setattr(boot, "is_boot_phase", lambda: False) + c = _bare_client(authenticated=False, pending_arl=None) # no ARL configured at all + assert c.is_configured() is False