fix(ocr): round-4 review — defensive poll + drop dead tracking columns

Round 4 review (PR #910), no blockers:
- poll(): a 2xx body with no `status` now fails fast (logged) instead of being
  treated as perpetually pending until the deadline; defensive page index
  (`p.get("index", i)`) so a malformed page degrades rather than KeyError-ing.
- Document on poll() that job_id is namespaced (embeds "/") so the gateway route
  must be a path-capture param (GET /v1/ocr/batch/{job_id:path}).
- Drop the vestigial `status` + `updated_at` columns from batch_ocr_jobs: a row
  only ever exists while pending (terminal jobs are deleted) and the live status
  comes from a fresh poll, so a stored mirror was permanently "pending" /
  redundant with submitted_at. Simplifies the migration, store, and dataclass.
- Tests: submit() ValueError on missing job_id; poll() missing-status → failed.

1653 unit tests pass; ruff + ty green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Chris Coutinho
2026-06-15 11:04:03 +02:00
co-authored by Claude Opus 4.8
parent 55630ba25c
commit 232684e881
5 changed files with 50 additions and 16 deletions
-1
View File
@@ -35,7 +35,6 @@ async def test_insert_then_get(store):
job = await store.get(**_DOC)
assert job is not None
assert job.job_id == "mistral/j1"
assert job.status == "pending"
assert job.submitted_at > 0
+16
View File
@@ -79,6 +79,22 @@ async def test_submit_sends_bearer_when_token_provider(monkeypatch):
assert seen[0].headers["Authorization"] == "Bearer tok-abc"
async def test_submit_raises_on_missing_job_id(monkeypatch):
# A 2xx with no job_id is a gateway contract violation -> actionable error.
_patch_transport(monkeypatch, lambda r: httpx.Response(202, json={}))
with pytest.raises(ValueError, match="no job_id"):
await gbc.GatewayBatchOcrClient("https://gw", "m").submit(
b"x", "application/pdf", custom_id="d"
)
async def test_poll_missing_status_is_failed(monkeypatch):
# A 2xx body without a status field must fail fast, not poll forever.
_patch_transport(monkeypatch, lambda r: httpx.Response(200, json={"total": 1}))
result = await gbc.GatewayBatchOcrClient("https://gw", "m").poll("mistral/j")
assert result.is_failed
async def test_poll_pending(monkeypatch):
_patch_transport(
monkeypatch,