"""Tests for transcription.models — Document, Job, Transcript persistence and relationships.""" from uuid import UUID import pytest from sqlalchemy.exc import IntegrityError 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 def _make_document(**overrides) -> Document: """Create a Document with sensible defaults.""" defaults = {"filename": "letter.jpg", "file_path": "/uploads/letter.jpg"} defaults.update(overrides) return Document(**defaults) def _persist_document(session) -> Document: """Create, persist, and return a Document.""" doc = _make_document() session.add(doc) session.commit() session.refresh(doc) return doc def _persist_job(session, document: Document) -> Job: """Create, persist, and return a Job linked to a Document.""" job = Job(document_id=document.id) session.add(job) session.commit() session.refresh(job) return job class TestDocumentModel: """Verify Document creation and default field population.""" def test_can_be_persisted(self, session): """A Document round-trips through the database with correct fields.""" doc = _persist_document(session) fetched = session.get(Document, doc.id) assert fetched is not None assert fetched.filename == "letter.jpg" assert fetched.file_path == "/uploads/letter.jpg" def test_defaults_are_populated(self, session): """id is a UUID and uploaded_at is populated on creation.""" doc = _persist_document(session) assert isinstance(doc.id, UUID) assert doc.uploaded_at is not None class TestJobModel: """Verify Job creation, defaults, and status transitions.""" def test_can_be_created_for_document(self, session): """A Job linked to a Document via FK persists correctly.""" doc = _persist_document(session) job = _persist_job(session, doc) fetched = session.get(Job, job.id) assert fetched is not None assert fetched.document_id == doc.id def test_defaults_are_populated(self, session): """Default status is queued; created_at and updated_at are populated.""" doc = _persist_document(session) job = _persist_job(session, doc) assert job.status == JobStatus.QUEUED assert job.retry_count == 0 assert job.created_at is not None assert job.updated_at is not None def test_transitions_to_transcribed(self, session): """Status updates from queued to processing to transcribed.""" doc = _persist_document(session) job = _persist_job(session, doc) assert job.status == JobStatus.QUEUED job.status = JobStatus.PROCESSING session.add(job) session.commit() session.refresh(job) assert job.status == JobStatus.PROCESSING job.status = JobStatus.TRANSCRIBED session.add(job) session.commit() session.refresh(job) assert job.status == JobStatus.TRANSCRIBED job.status = JobStatus.COMPLETED session.add(job) session.commit() session.refresh(job) assert job.status == JobStatus.COMPLETED def test_transitions_to_failed(self, session): """Status updates from processing to failed.""" doc = _persist_document(session) job = _persist_job(session, doc) job.status = JobStatus.PROCESSING session.add(job) session.commit() session.refresh(job) job.status = JobStatus.FAILED session.add(job) session.commit() session.refresh(job) assert job.status == JobStatus.FAILED class TestTranscriptModel: """Verify Transcript persistence for success and failure cases.""" def test_success_record_persists(self, session): """A Transcript with text set and error_detail None persists correctly.""" doc = _persist_document(session) job = _persist_job(session, doc) transcript = Transcript(job_id=job.id, text="Dear Sir, ...") session.add(transcript) session.commit() session.refresh(transcript) fetched = session.get(Transcript, transcript.id) assert fetched is not None assert fetched.text == "Dear Sir, ..." assert fetched.error_detail is None def test_failure_record_persists(self, session): """A Transcript with text None and error_detail set persists correctly.""" doc = _persist_document(session) job = _persist_job(session, doc) transcript = Transcript(job_id=job.id, error_detail="Provider timeout") session.add(transcript) session.commit() session.refresh(transcript) fetched = session.get(Transcript, transcript.id) assert fetched is not None assert fetched.text is None assert fetched.error_detail == "Provider timeout" def test_job_id_is_unique(self, session): """Inserting two transcripts with the same job_id raises an integrity error.""" doc = _persist_document(session) job = _persist_job(session, doc) t1 = Transcript(job_id=job.id, text="First") session.add(t1) session.commit() t2 = Transcript(job_id=job.id, text="Duplicate") session.add(t2) with pytest.raises(IntegrityError): session.commit() class TestTranscriptRevisionModel: """Verify transcript revision persistence and defaults.""" def test_revision_defaults_and_persistence(self, session): """Revision records persist with revision metadata and defaults.""" doc = _persist_document(session) job = _persist_job(session, doc) revision = TranscriptRevision(job_id=job.id, revision_number=1, text="Rev text") session.add(revision) session.commit() session.refresh(revision) fetched = session.get(TranscriptRevision, revision.id) assert fetched is not None assert fetched.revision_number == 1 assert fetched.text == "Rev text" assert fetched.source == "worker" assert fetched.accepted is False class TestRelationships: """Verify SQLModel relationship navigation between models.""" def test_document_exposes_jobs(self, session): """document.jobs returns the linked Job list.""" doc = _persist_document(session) _persist_job(session, doc) _persist_job(session, doc) session.refresh(doc) assert len(doc.jobs) == 2 assert all(isinstance(j, Job) for j in doc.jobs) def test_job_exposes_transcript(self, session): """job.transcript returns the linked Transcript.""" doc = _persist_document(session) job = _persist_job(session, doc) transcript = Transcript(job_id=job.id, text="Transcribed text") session.add(transcript) session.commit() session.refresh(job) assert job.transcript is not None assert isinstance(job.transcript, Transcript) assert job.transcript.text == "Transcribed text" def test_job_exposes_revisions(self, session): """job.revisions returns revision history linked to the Job.""" doc = _persist_document(session) job = _persist_job(session, doc) session.add(TranscriptRevision(job_id=job.id, revision_number=1, text="v1")) session.add(TranscriptRevision(job_id=job.id, revision_number=2, text="v2", accepted=True)) session.commit() session.refresh(job) assert len(job.revisions) == 2 assert all(isinstance(revision, TranscriptRevision) for revision in job.revisions)