Files
goods/ingestion/tests/test_off_retry.py
T
novaalphastrikeomegaz663 044c870df7
CI / Go (api) (pull_request) Failing after 22s
CI / Python (ingestion) (pull_request) Successful in 14s
CI / Migrations (postgres) (pull_request) Failing after 18s
feat(ingestion): harden OFF ingestion for bulk seeding
- Add retry/backoff (429 + 5xx, Retry-After aware) to the OFF adapter so
  transient API errors no longer abort a run.
- Clamp bounded text fields (serving_size, net_content_unit,
  country_of_origin) to their column widths in transform; long OFF values
  previously raised StringDataRightTruncation and rolled back the batch.
- Load each record inside a savepoint (load_record_safe) so one malformed
  source record is skipped instead of aborting the whole import; jobs now
  report an errored count.
- Tests for retry behaviour, serving_size clamping, and per-record isolation.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-06-20 06:55:56 +00:00

60 lines
1.8 KiB
Python

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