"""Tests for Step 3 library services (revisions, search, export).""" import pytest from sqlmodel import select from transcription.models import Document from transcription.models import Job from transcription.models import JobStatus from transcription.models import Transcript from transcription.models import TranscriptRevision from transcription.services.library import accept_revision from transcription.services.library import add_revision from transcription.services.library import export_transcripts from transcription.services.library import list_revisions from transcription.services.library import search_accepted_transcripts def _create_job(session) -> Job: document = Document(filename="letter.jpg", file_path="uploads/letter.jpg") session.add(document) session.commit() session.refresh(document) job = Job(document_id=document.id, status=JobStatus.TRANSCRIBED) session.add(job) session.commit() session.refresh(job) return job @pytest.mark.integration class TestRevisionHistoryBehavior: """Verify revision append/accept behavior.""" def test_add_revision_appends_incrementing_revision_numbers(self, session): """add_revision creates immutable incrementing revisions per job.""" job = _create_job(session) r1 = add_revision(job_id=job.id, text="first", session=session) r2 = add_revision(job_id=job.id, text="second", session=session) revisions = list_revisions(job_id=job.id, session=session) assert r1.revision_number == 1 assert r2.revision_number == 2 assert [revision.revision_number for revision in revisions] == [1, 2] def test_accept_revision_marks_selected_revision_and_sets_job_completed(self, session): """accept_revision marks one revision accepted and transitions job to completed.""" job = _create_job(session) r1 = add_revision(job_id=job.id, text="v1", session=session) r2 = add_revision(job_id=job.id, text="v2", session=session) accepted = accept_revision(revision_id=r2.id, session=session) session.refresh(job) all_revisions = list(session.exec(select(TranscriptRevision).where(TranscriptRevision.job_id == job.id)).all()) accepted_flags = {revision.id: revision.accepted for revision in all_revisions} transcript = session.exec(select(Transcript).where(Transcript.job_id == job.id)).first() assert accepted.id == r2.id assert accepted_flags[r1.id] is False assert accepted_flags[r2.id] is True assert job.status == JobStatus.COMPLETED assert transcript is not None assert transcript.text == "v2" @pytest.mark.integration class TestSearchAndExportBehavior: """Verify accepted-only search and export semantics.""" def test_search_returns_only_accepted_revisions(self, session): """search_accepted_transcripts filters out non-accepted revisions.""" job = _create_job(session) draft = add_revision(job_id=job.id, text="family archive draft", session=session) accepted = add_revision(job_id=job.id, text="family archive final", accepted=True, session=session) results = search_accepted_transcripts(query="archive", session=session) assert results result_ids = {result.id for result in results} assert accepted.id in result_ids assert draft.id not in result_ids def test_export_returns_serializable_records_for_accepted_revisions(self, session): """export_transcripts returns expected fields for accepted-only export.""" job = _create_job(session) accepted = add_revision(job_id=job.id, text="export me", accepted=True, session=session) records = export_transcripts(accepted_only=True, session=session) assert len(records) == 1 record = records[0] assert record["job_id"] == str(job.id) assert record["revision_id"] == str(accepted.id) assert record["accepted"] is True assert record["text"] == "export me"