"""Tests for transcription.worker.""" from threading import Event import pytest from sqlmodel import select from transcription.models import Document, Job, JobStatus, Transcript from transcription.providers.base import TranscriptionResult from transcription.worker import process_next_queued_job, run_worker_loop def _create_queued_job(session, *, filename: str = "doc.jpg", file_path: str = "uploads/doc.jpg") -> Job: document = Document(filename=filename, file_path=file_path) session.add(document) session.commit() session.refresh(document) job = Job(document_id=document.id, status=JobStatus.QUEUED) session.add(job) session.commit() session.refresh(job) return job @pytest.mark.integration class TestWorkerQueueBehavior: """Verify worker behavior when selecting queued jobs.""" def test_returns_false_when_queue_empty(self, session): """process_next_queued_job returns False when there are no queued jobs.""" processed = process_next_queued_job(session=session) assert processed is False @pytest.mark.integration class TestWorkerSuccessPath: """Verify worker success-path lifecycle transitions and transcript persistence.""" def test_transitions_processing_to_transcribed(self, session, monkeypatch): """process_next_queued_job transitions queued jobs to transcribed on success.""" job = _create_queued_job(session) def _fake_transcribe(_path): return TranscriptionResult(text="ok", provider="openrouter", model="test-model") monkeypatch.setattr("transcription.worker.transcribe_document_image", _fake_transcribe) processed = process_next_queued_job(session=session) session.refresh(job) assert processed is True assert job.status == JobStatus.TRANSCRIBED def test_persists_transcript_text_on_success(self, session, monkeypatch): """process_next_queued_job stores transcript text for successful jobs.""" job = _create_queued_job(session) def _fake_transcribe(_path): return TranscriptionResult(text="Transcript body", provider="openrouter", model="test-model") monkeypatch.setattr("transcription.worker.transcribe_document_image", _fake_transcribe) process_next_queued_job(session=session) transcript = session.exec( select(Transcript).where(Transcript.job_id == job.id) ).first() assert transcript is not None assert transcript.text == "Transcript body" assert transcript.error_detail is None @pytest.mark.integration class TestWorkerFailurePath: """Verify worker failure-path lifecycle transitions and error persistence.""" def test_sets_failed_and_error_detail_on_failure(self, session, monkeypatch): """process_next_queued_job marks failed and stores error detail on exception.""" job = _create_queued_job(session) def _fake_transcribe(_path): raise RuntimeError("provider failure") monkeypatch.setattr("transcription.worker.transcribe_document_image", _fake_transcribe) processed = process_next_queued_job(session=session) session.refresh(job) transcript = session.exec( select(Transcript).where(Transcript.job_id == job.id) ).first() assert processed is True assert job.status == JobStatus.FAILED assert transcript is not None assert transcript.text is None assert "provider failure" in transcript.error_detail assert "[internal_unexpected_error]" in transcript.error_detail assert "error_id=" in transcript.error_detail assert "suggestion=" in transcript.error_detail def test_updates_existing_transcript_if_present(self, session, monkeypatch): """process_next_queued_job updates existing transcript instead of duplicating.""" job = _create_queued_job(session) existing = Transcript(job_id=job.id, text="old", error_detail=None) session.add(existing) session.commit() session.refresh(existing) def _fake_transcribe(_path): raise RuntimeError("provider failure") monkeypatch.setattr("transcription.worker.transcribe_document_image", _fake_transcribe) process_next_queued_job(session=session) transcripts = session.exec( select(Transcript).where(Transcript.job_id == job.id) ).all() assert len(transcripts) == 1 assert transcripts[0].id == existing.id assert transcripts[0].text is None assert "provider failure" in transcripts[0].error_detail assert "[internal_unexpected_error]" in transcripts[0].error_detail assert "error_id=" in transcripts[0].error_detail @pytest.mark.unit class TestWorkerLoopControl: """Verify worker loop start/stop behavior.""" def test_stops_when_stop_event_is_set(self, monkeypatch): """run_worker_loop exits when a stop event is set.""" stop_event = Event() stop_event.set() called = {"value": False} def _fake_process_next_queued_job(): called["value"] = True return False monkeypatch.setattr("transcription.worker.process_next_queued_job", _fake_process_next_queued_job) run_worker_loop(stop_event=stop_event, poll_interval_seconds=0.01) assert called["value"] is False