import httpx import pytest from opengoods.adapters.openfoodfacts import OpenFoodFactsAdapter def _adapter(handler, **kwargs): client = httpx.Client(transport=httpx.MockTransport(handler)) return OpenFoodFactsAdapter(client=client, min_interval=0, backoff_base=0, **kwargs) def test_retries_transient_5xx_then_succeeds(): calls = {"n": 0} def handler(request: httpx.Request) -> httpx.Response: calls["n"] += 1 if calls["n"] < 3: return httpx.Response(503) return httpx.Response(200, json={"status": 1, "product": {"code": "x"}}) adapter = _adapter(handler, max_retries=4) assert adapter.fetch_barcode("x") == {"code": "x"} assert calls["n"] == 3 # two 503s retried, third succeeds def test_retries_exhausted_raises(): def handler(request: httpx.Request) -> httpx.Response: return httpx.Response(503) adapter = _adapter(handler, max_retries=2) with pytest.raises(httpx.HTTPStatusError): adapter.fetch_barcode("x") def test_non_retryable_4xx_not_retried(): calls = {"n": 0} def handler(request: httpx.Request) -> httpx.Response: calls["n"] += 1 return httpx.Response(404) adapter = _adapter(handler, max_retries=4) with pytest.raises(httpx.HTTPStatusError): adapter.fetch_barcode("x") assert calls["n"] == 1 # 404 is not retried def test_retries_connection_error_then_succeeds(): calls = {"n": 0} def handler(request: httpx.Request) -> httpx.Response: calls["n"] += 1 if calls["n"] == 1: raise httpx.ConnectError("boom") return httpx.Response(200, json={"status": 1, "product": {"code": "y"}}) adapter = _adapter(handler, max_retries=4) assert adapter.fetch_barcode("y") == {"code": "y"} assert calls["n"] == 2