backend/tests/test_download_rate_limit_wait.py
Rama master · réplica de lectura del repositorio original.
"""Our own rate limiter must not turn a download into a provider failure.
Prod, September 2026: 309 ``download_failed`` attempts for opensubtitles with
zero quota or API errors behind them. The shared 40-per-10s window was full of
searches, ``download_subtitle`` returned None on the limiter's refusal, and the
caller booked that as a failed download and a provider stats failure.
"""
from unittest.mock import MagicMock, patch
import pytest
import providers.download_manager as dm
from providers.download_manager import (
DownloadRateLimitedError,
download_subtitle,
search_and_download_best,
)
@pytest.fixture
def short_wait(monkeypatch):
monkeypatch.setattr(dm, "RATE_LIMIT_MAX_WAIT_S", 0.05)
monkeypatch.setattr(dm, "_RATE_LIMIT_POLL_S", 0.005)
def _result(provider_name="opensubtitles", subtitle_id="s1", score=90):
return MagicMock(provider_name=provider_name, subtitle_id=subtitle_id, score=score)
class TestDownloadWaitsForSlot:
def test_waits_for_a_free_slot_then_downloads(self, monkeypatch):
monkeypatch.setattr(dm, "RATE_LIMIT_MAX_WAIT_S", 5.0)
monkeypatch.setattr(dm, "_RATE_LIMIT_POLL_S", 0.005)
provider = MagicMock()
provider.download.return_value = b"content"
checker = MagicMock(side_effect=[False, False, True])
ret = download_subtitle({"opensubtitles": provider}, {}, checker, _result())
assert ret == b"content"
assert checker.call_count == 3
provider.download.assert_called_once()
def test_still_refused_raises_when_asked(self, short_wait):
provider = MagicMock()
with pytest.raises(DownloadRateLimitedError):
download_subtitle(
{"opensubtitles": provider},
{},
MagicMock(return_value=False),
_result(),
raise_skips=True,
)
provider.download.assert_not_called()
def test_still_refused_returns_none_by_default(self, short_wait):
provider = MagicMock()
breaker = MagicMock()
breaker.allow_request.return_value = True
ret = download_subtitle(
{"opensubtitles": provider},
{"opensubtitles": breaker},
MagicMock(return_value=False),
_result(),
)
assert ret is None
provider.download.assert_not_called()
breaker.record_failure.assert_not_called()
def test_stop_request_ends_the_wait(self, monkeypatch):
monkeypatch.setattr(dm, "RATE_LIMIT_MAX_WAIT_S", 30.0)
monkeypatch.setattr(dm, "_RATE_LIMIT_POLL_S", 0.005)
checker = MagicMock(return_value=False)
with (
patch("services.scheduler.cancellation.abort_requested", return_value=True),
pytest.raises(DownloadRateLimitedError),
):
download_subtitle(
{"opensubtitles": MagicMock()},
{},
checker,
_result(),
raise_skips=True,
)
assert checker.call_count == 1
class TestSearchAndDownloadBestRateLimited:
def test_rate_limited_is_not_a_provider_failure(self):
limited = _result("opensubtitles", "os1", 95)
other = _result("jimaku", "j1", 80)
download_fn = MagicMock(side_effect=[DownloadRateLimitedError("full"), b"content"])
stats_fn = MagicMock()
with (
patch("providers.reranker.apply_auto_reranking"),
patch.object(dm, "decision_log") as dlog,
):
ret = search_and_download_best(
MagicMock(return_value=[limited, other]), download_fn, stats_fn, "query"
)
assert ret is other
stats_fn.assert_called_once_with("jimaku", success=True, score=80)
statuses = [c.args[2] for c in dlog.download_attempt.call_args_list]
assert statuses == ["rate_limited", "selected"]
def test_further_results_of_a_limited_provider_are_not_retried(self):
first = _result("opensubtitles", "os1", 95)
second = _result("opensubtitles", "os2", 90)
download_fn = MagicMock(side_effect=DownloadRateLimitedError("full"))
stats_fn = MagicMock()
with patch.object(dm, "decision_log") as dlog:
ret = search_and_download_best(
MagicMock(return_value=[first, second]), download_fn, stats_fn, "query"
)
assert ret is None
assert download_fn.call_count == 1
stats_fn.assert_not_called()
statuses = [c.args[2] for c in dlog.download_attempt.call_args_list]
assert statuses == ["rate_limited", "rate_limited"]
class TestProviderManagerWiring:
def test_manager_search_and_download_books_rate_limited(self, short_wait):
from providers import ProviderManager
manager = ProviderManager.__new__(ProviderManager)
provider = MagicMock()
manager._providers = {"opensubtitles": provider}
manager._circuit_breakers = {}
manager._check_rate_limit = MagicMock(return_value=False)
manager.search_with_fallback = MagicMock(return_value=[_result()])
with (
patch("db.providers.update_provider_stats") as stats,
patch.object(dm, "decision_log") as dlog,
):
ret = manager.search_and_download_best(MagicMock())
assert ret is None
provider.download.assert_not_called()
stats.assert_not_called()
statuses = [c.args[2] for c in dlog.download_attempt.call_args_list]
assert statuses == ["rate_limited"]