generated from john/python-template
fix: retry execution attempt number conflicts
Co-authored-by: Copilot App <[email protected]>
This commit is contained in:
co-authored by
Copilot App
parent
f9261a1af3
commit
2093eb6fb3
@@ -2,9 +2,12 @@ from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
from pydantic import JsonValue
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||
|
||||
from transcription.db.models import Document
|
||||
from transcription.db.models import DocumentPerson
|
||||
from transcription.db.models import ExecutionAttempt
|
||||
from transcription.db.models import Job
|
||||
from transcription.db.models import JobSource
|
||||
from transcription.db.models import JobSourceStatus
|
||||
@@ -369,3 +372,104 @@ async def test_update_job_source_transcription_persists_provider_json_payloads(d
|
||||
assert attempt.attempt.raw_transcription == "provider transcript"
|
||||
assert attempt.attempt.normalized_metadata == metadata
|
||||
assert attempt.attempt.sdk_response_snapshot == raw_payload
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_job_source_transcription_retries_on_execution_attempt_integrity_conflict(
|
||||
default_session_factory,
|
||||
monkeypatch,
|
||||
):
|
||||
documents = DocumentService(session_factory=default_session_factory)
|
||||
jobs = JobService(session_factory=default_session_factory)
|
||||
transcriptions = SourceService(session_factory=default_session_factory)
|
||||
|
||||
document = await documents.create_document(Document(id=uuid4(), name="attempt-retry-doc"))
|
||||
job = await jobs.create_job(Job(document_id=document.id))
|
||||
source = await transcriptions.create_source(
|
||||
Source(
|
||||
document_id=document.id,
|
||||
page_number=1,
|
||||
upload_name="attempt-retry.jpg",
|
||||
filename="attempt-retry.jpg",
|
||||
file_path="uploads/attempt-retry.jpg",
|
||||
file_hash="e" * 64,
|
||||
file_size_bytes=1,
|
||||
)
|
||||
)
|
||||
await transcriptions.create_job_source(
|
||||
JobSource(job_id=job.id, source_id=source.id, status=JobSourceStatus.PENDING)
|
||||
)
|
||||
|
||||
original_flush = AsyncSession.flush
|
||||
execution_attempt_flushes = 0
|
||||
|
||||
async def _flaky_flush(self, *args, **kwargs):
|
||||
nonlocal execution_attempt_flushes
|
||||
if any(isinstance(item, ExecutionAttempt) for item in self.new):
|
||||
execution_attempt_flushes += 1
|
||||
if execution_attempt_flushes == 1:
|
||||
raise IntegrityError("insert execution_attempt", {}, Exception("duplicate attempt number"))
|
||||
return await original_flush(self, *args, **kwargs)
|
||||
|
||||
monkeypatch.setattr(AsyncSession, "flush", _flaky_flush)
|
||||
|
||||
await transcriptions.update_job_source_transcription(
|
||||
job_id=job.id,
|
||||
source_id=source.id,
|
||||
text="retry succeeds",
|
||||
provider="openrouter",
|
||||
model="test-model",
|
||||
)
|
||||
|
||||
assert execution_attempt_flushes == 2
|
||||
rows = await transcriptions.list_job_sources(job_id=job.id)
|
||||
assert len(rows) == 1
|
||||
assert rows[0].status == JobSourceStatus.TRANSCRIBED
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_job_source_transcription_raises_domain_error_after_attempt_retry_exhaustion(
|
||||
default_session_factory,
|
||||
monkeypatch,
|
||||
):
|
||||
documents = DocumentService(session_factory=default_session_factory)
|
||||
jobs = JobService(session_factory=default_session_factory)
|
||||
transcriptions = SourceService(session_factory=default_session_factory)
|
||||
|
||||
document = await documents.create_document(Document(id=uuid4(), name="attempt-exhaustion-doc"))
|
||||
job = await jobs.create_job(Job(document_id=document.id))
|
||||
source = await transcriptions.create_source(
|
||||
Source(
|
||||
document_id=document.id,
|
||||
page_number=1,
|
||||
upload_name="attempt-exhaustion.jpg",
|
||||
filename="attempt-exhaustion.jpg",
|
||||
file_path="uploads/attempt-exhaustion.jpg",
|
||||
file_hash="f" * 64,
|
||||
file_size_bytes=1,
|
||||
)
|
||||
)
|
||||
await transcriptions.create_job_source(
|
||||
JobSource(job_id=job.id, source_id=source.id, status=JobSourceStatus.PENDING)
|
||||
)
|
||||
|
||||
original_flush = AsyncSession.flush
|
||||
|
||||
async def _always_conflict_flush(self, *args, **kwargs):
|
||||
if any(isinstance(item, ExecutionAttempt) for item in self.new):
|
||||
raise IntegrityError("insert execution_attempt", {}, Exception("duplicate attempt number"))
|
||||
return await original_flush(self, *args, **kwargs)
|
||||
|
||||
monkeypatch.setattr(AsyncSession, "flush", _always_conflict_flush)
|
||||
|
||||
with pytest.raises(TranscriptionError) as exc_info:
|
||||
await transcriptions.update_job_source_transcription(
|
||||
job_id=job.id,
|
||||
source_id=source.id,
|
||||
text="will not persist",
|
||||
provider="openrouter",
|
||||
model="test-model",
|
||||
)
|
||||
|
||||
assert exc_info.value.category == ErrorCategory.CONFLICT
|
||||
assert "attempt number" in exc_info.value.message.lower()
|
||||
|
||||
Reference in New Issue
Block a user