feat(downloads): platform cooldown honors server Retry-After — B1 (plan #708)
Owning the native client means we see the 429 Retry-After header — previously discarded. PatreonAPIError now carries `retry_after`; on a PERSISTENT page-fetch 429 the client attaches the server's raw Retry-After seconds. New DownloadResult.retry_after_seconds; patreon_ingester._failure_result sets it on RATE_LIMITED. download_service._update_source_health passes it to set_platform_cooldown as `seconds=`, clamped to [60, 3600] (a tiny hint can't leave the platform effectively un-cooled; a huge one can't strand it for hours); no hint → the flat 900s default. So a rate-limited platform cools for as long as the server actually asks, not a fixed guess. Tests: terminal 429 surfaces retry_after (test_patreon_client); cooldown honors + clamps the hint, falls back to default when absent (test_download_service). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -327,6 +327,18 @@ def test_fetch_persistent_429_raises_after_retries(monkeypatch):
|
||||
assert not isinstance(ei.value, PatreonAuthError)
|
||||
|
||||
|
||||
def test_fetch_persistent_429_surfaces_retry_after(monkeypatch):
|
||||
"""plan #708 B1: a terminal 429 carries the server's raw Retry-After seconds
|
||||
(uncapped here — the cooldown clamps) so the platform cooldown matches it."""
|
||||
client, _slept = _client_with_sequence(
|
||||
monkeypatch, [_FakeResp(429, headers={"Retry-After": "120"})] * 10,
|
||||
)
|
||||
with pytest.raises(PatreonAPIError) as ei:
|
||||
client._fetch("5555", None)
|
||||
assert ei.value.status_code == 429
|
||||
assert ei.value.retry_after == 120.0
|
||||
|
||||
|
||||
def test_request_sleep_paces_before_fetch(monkeypatch):
|
||||
client = PatreonClient(cookies_path=None, request_sleep=1.5)
|
||||
monkeypatch.setattr(
|
||||
|
||||
Reference in New Issue
Block a user