from uuid import uuid4 import pytest from transcription.db.models import Document from transcription.db.models import DocumentPerson from transcription.db.models import DocumentPersonRole from transcription.db.models import Job from transcription.db.models import JobSource from transcription.db.models import JobSourceStatus from transcription.db.models import Person from transcription.db.models import Source from transcription.services.documents import DocumentDeleteBlockedError from transcription.services.documents import DocumentService from transcription.services.jobs import JobService from transcription.services.transcription import SourceDeleteBlockedError from transcription.services.transcription import TranscriptionService @pytest.mark.asyncio async def test_document_service_handles_person_and_document_person_crud(default_session_factory): documents = DocumentService(session_factory=default_session_factory) document = await documents.create_document(Document(id=uuid4(), name="person-doc")) person = await documents.create_person(Person(full_name="Ada Lovelace")) link = await documents.create_document_person( DocumentPerson(document_id=document.id, person_id=person.id, role=DocumentPersonRole.AUTHOR) ) fetched = await documents.read_document_person(link.id) assert fetched.id == link.id assert fetched.role == DocumentPersonRole.AUTHOR updated_link = await documents.update_document_person( DocumentPerson(id=link.id, document_id=document.id, person_id=person.id, role=DocumentPersonRole.RECIPIENT) ) assert updated_link.role == DocumentPersonRole.RECIPIENT listed = await documents.list_document_people(document_id=document.id) assert len(listed) == 1 people = await documents.list_people() assert len(people) == 1 await documents.delete_document_person(updated_link) assert len(await documents.list_document_people(document_id=document.id)) == 0 @pytest.mark.asyncio async def test_transcription_service_manages_source_crud(default_session_factory): documents = DocumentService(session_factory=default_session_factory) transcriptions = TranscriptionService(session_factory=default_session_factory) document = await documents.create_document(Document(id=uuid4(), name="source-doc")) source = await transcriptions.create_source( Source( document_id=document.id, page_number=1, upload_name="page-1.jpg", filename="page-1.jpg", file_path="uploads/page-1.jpg", ) ) fetched = await transcriptions.read_source(source.id) assert fetched.id == source.id source.page_number = 2 updated = await transcriptions.update_source(source) assert updated.page_number == 2 listed = await transcriptions.list_sources(document_id=document.id) assert len(listed) == 1 filtered = await transcriptions.query_sources(document_id=document.id, page_number=2) assert len(filtered) == 1 await transcriptions.delete_source(updated) assert len(await transcriptions.list_sources(document_id=document.id)) == 0 @pytest.mark.asyncio async def test_transcription_service_job_source_crud_uses_caller_session(default_session_factory): documents = DocumentService(session_factory=default_session_factory) jobs = JobService(session_factory=default_session_factory) transcriptions = TranscriptionService(session_factory=default_session_factory) async with transcriptions._session_scope() as session: document = Document(id=uuid4(), name="job-source-doc") session.add(document) await session.flush() job = Job(document_id=document.id) session.add(job) await session.flush() source = Source( document_id=document.id, page_number=1, upload_name="job-source.jpg", filename="job-source.jpg", file_path="uploads/job-source.jpg", ) session.add(source) await session.flush() job_source = await transcriptions.create_job_source( JobSource(job_id=job.id, source_id=source.id, status=JobSourceStatus.PENDING), session=session, ) assert job_source.status == JobSourceStatus.PENDING job_source.status = JobSourceStatus.TRANSCRIBED updated = await transcriptions.update_job_source(job_source, session=session) assert updated.status == JobSourceStatus.TRANSCRIBED fetched = await transcriptions.read_job_source(job_source.id, session=session) assert fetched.id == job_source.id listed = await transcriptions.list_job_sources(job_id=job.id, session=session) assert len(listed) == 1 await transcriptions.delete_job_source(updated, session=session) await session.commit() assert len(await transcriptions.list_job_sources(job_id=job.id)) == 0 @pytest.mark.asyncio async def test_document_detail_loads_linked_person_relationship(default_session_factory): documents = DocumentService(session_factory=default_session_factory) document = await documents.create_document(Document(id=uuid4(), name="detail-person-doc")) person = await documents.create_person(Person(full_name="Grace Hopper")) await documents.create_document_person( DocumentPerson( document_id=document.id, person_id=person.id, role=DocumentPersonRole.AUTHOR, ) ) detail = await documents.read_document_detail(document.id) assert len(detail.document_people) == 1 link = detail.document_people[0] assert link.person is not None assert link.person.full_name == "Grace Hopper" assert link.role == DocumentPersonRole.AUTHOR @pytest.mark.asyncio async def test_document_delete_is_blocked_with_source_and_job_dependencies(default_session_factory): documents = DocumentService(session_factory=default_session_factory) jobs = JobService(session_factory=default_session_factory) transcriptions = TranscriptionService(session_factory=default_session_factory) document = await documents.create_document(Document(id=uuid4(), name="blocked-by-deps")) 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="blocked.jpg", filename="blocked.jpg", file_path="uploads/blocked.jpg", ) ) await transcriptions.create_job_source( JobSource( job_id=job.id, source_id=source.id, status=JobSourceStatus.PENDING, ) ) with pytest.raises(DocumentDeleteBlockedError) as exc_info: await documents.delete_document(document) message = exc_info.value.message assert "Sources" in message assert "Jobs" in message @pytest.mark.asyncio async def test_source_delete_blocks_when_linked_to_multiple_jobs(default_session_factory): documents = DocumentService(session_factory=default_session_factory) jobs = JobService(session_factory=default_session_factory) transcriptions = TranscriptionService(session_factory=default_session_factory) document = await documents.create_document(Document(id=uuid4(), name="multi-job-source-doc")) job_one = await jobs.create_job(Job(document_id=document.id)) job_two = await jobs.create_job(Job(document_id=document.id)) source = await transcriptions.create_source( Source( document_id=document.id, page_number=1, upload_name="shared-page.jpg", filename="shared-page.jpg", file_path="uploads/shared-page.jpg", ) ) await transcriptions.create_job_source( JobSource(job_id=job_one.id, source_id=source.id, status=JobSourceStatus.PENDING) ) await transcriptions.create_job_source( JobSource(job_id=job_two.id, source_id=source.id, status=JobSourceStatus.PENDING) ) with pytest.raises(SourceDeleteBlockedError): await transcriptions.delete_source_from_job_context(job_id=job_one.id, source_id=source.id)