fix(remote): report each handled remote error once, from the code that catches it

A caught RemoteError now logs at debug under quiet(); a recovered licence prints one warning, only when the track's own key is held.
This commit is contained in:
imSp4rky
2026-11-12 17:15:40 -07:00
parent dc18b23b91
commit 16fbf5d17a
6 changed files with 156 additions and 34 deletions
+26
View File
@@ -169,6 +169,32 @@ def test_remote_error_carries_its_reason_and_exits_a_cli_run():
assert not remote_service.RemoteError("Server error [NOT_FOUND]: no title", "NOT_FOUND").session_broke
def test_quiet_mutes_a_remote_error_the_caller_reports(caplog):
with caplog.at_level("DEBUG", logger="remote_service"), remote_service.quiet():
with remote_service.quiet():
remote_service.RemoteError("Server error [SERVICE_ERROR]: refused", "SERVICE_ERROR")
remote_service.RemoteError("Remote auth failed: bad key", "AUTH")
remote_service.RemoteError("Server error [NOT_FOUND]: outside", "NOT_FOUND")
assert [(r.levelname, r.getMessage()) for r in caplog.records] == [
("DEBUG", "Server error [SERVICE_ERROR]: refused"),
("DEBUG", "Remote auth failed: bad key"),
("ERROR", "Server error [NOT_FOUND]: outside"),
]
def test_an_unreachable_server_list_logs_one_warning(monkeypatch, caplog):
class Down:
def get(self, endpoint):
raise remote_service.RemoteError("Could not connect to remote server at http://srv", "CONNECT")
monkeypatch.setattr(remote_service, "remote_client", lambda name: Down())
monkeypatch.setattr(remote_service, "_remote_lists", {})
assert remote_service.remote_service_list("down") is None
assert [r.levelname for r in caplog.records if r.levelname in ("WARNING", "ERROR")] == ["WARNING"]
def test_a_geofenced_remote_service_gets_its_first_allowed_region_as_proxy(monkeypatch):
monkeypatch.setattr(handlers, "server_proxy_allowed", lambda request=None: True)
fenced = {"tag": "EXAMPLE", "geofence": ["US", "ca"], "geoblock": ["us"]}
@@ -98,7 +98,7 @@ def test_proxy_mode_keeps_the_callers_type():
assert svc.client.posts[0]["pssh"] == "PR"
def test_a_refused_relayed_licence_raises_a_click_exception_with_the_server_message():
def test_a_refused_relayed_licence_raises_a_click_exception_with_the_server_message(caplog):
import click
from unshackle.core.remote_service import RemoteError
@@ -115,3 +115,20 @@ def test_a_refused_relayed_licence_raises_a_click_exception_with_the_server_mess
with pytest.raises(click.ClickException, match="^Licence request failed: refused$"):
svc.proxy_license(b"chal", track, "playready")
assert not [r for r in caplog.records if r.levelname == "ERROR"]
def test_a_refused_server_cdm_licence_logs_one_warning(caplog):
from unshackle.core.remote_service import RemoteError
svc = _service("widevine", {})
def refuse(endpoint, payload, expect=()):
raise RemoteError("Server error [SERVICE_ERROR]: refused", "SERVICE_ERROR")
svc.client.post = refuse
track = SimpleNamespace(id="vid", drm=[_Widevine()])
assert svc.proxy_license(b"chal", track, "widevine") == b"chal"
assert [r.levelname for r in caplog.records if r.levelname in ("WARNING", "ERROR")] == ["WARNING"]
+29 -1
View File
@@ -11,7 +11,7 @@ import requests
import responses
from langcodes import Language
from unshackle.core.remote_service import RemoteClient, RemoteError, RemoteService, build_title
from unshackle.core.remote_service import RemoteClient, RemoteError, RemoteService, build_title, quiet
from unshackle.core.tracks import Audio
from unshackle.core.tracks.track import Track
@@ -124,3 +124,31 @@ def test_a_gateway_error_page_counts_as_an_unreachable_server():
client.get("/api/session/s1/titles")
assert exc.value.kind == "CONNECT"
def fail_reopen(session_id, kind):
raise RemoteError("Server error [NOT_FOUND]: no title", "NOT_FOUND")
@pytest.mark.parametrize("recover", [lambda session_id, kind: False, fail_reopen], ids=["lost", "reopen-failed"])
@responses.activate
def test_a_session_lost_under_quiet_is_left_to_the_caller(monkeypatch, caplog, recover):
service = make_service()
monkeypatch.setattr(service, "recover", recover)
responses.add(responses.POST, f"{SERVER}/api/session/s1/license", json=GONE, status=404)
with quiet(), pytest.raises(RemoteError):
license_call(service, audio("a-old"))
assert not [r for r in caplog.records if r.levelname == "ERROR"]
@responses.activate
def test_closing_a_session_the_server_lost_logs_no_error(caplog):
service = make_service()
responses.add(responses.DELETE, f"{SERVER}/api/session/s1", json=GONE, status=404)
service.close()
assert service._session_id is None
assert not [r for r in caplog.records if r.levelname in ("WARNING", "ERROR")]