"""Tests for the documents page routes and action handlers.""" import re from datetime import date import pytest import pytest_asyncio from sqlmodel import select from transcription.db import session_scope from transcription.db.models import Document from transcription.db.models import DocumentPerson from transcription.db.models import DocumentTag from transcription.db.models import DocumentType from transcription.db.models import Job from transcription.db.models import Person from transcription.db.models import PersonRole from transcription.db.models import Source from transcription.db.models import Tag from transcription.ui.pages.documents_page import _resolve_selected_tag_labels # --- Helper Fixtures --- @pytest_asyncio.fixture async def seed_person_and_document(): """Seed a Person and Document linked by DocumentPerson role.""" async with session_scope() as session: letter_type = (await session.exec(select(DocumentType).where(DocumentType.label == "Letter"))).one() author_role = (await session.exec(select(PersonRole).where(PersonRole.semantic_key == "author"))).one() person = Person(given_names="Zenna", last_name="Cochran") session.add(person) await session.flush() doc = Document( name="Letter from Hig", document_type_id=letter_type.id, archive_identifier="ZC-1924-001", document_date=date(1924, 7, 4), ) session.add(doc) await session.flush() link = DocumentPerson( document_id=doc.id, person_id=person.id, role_id=author_role.id, ) session.add(link) await session.commit() return str(doc.id), str(person.id) # --- Integration Tests for Documents Route Handlers --- @pytest.mark.integration class TestDocumentsPageRendering: """Verify document list, detail, edit, and deletion route behaviors.""" def test_documents_page_renders_empty_state(self, app_client): _, client = app_client response = client.get("/ui/documents") assert response.status_code == 200 assert "Archival Documents" in response.text assert "No documents found in repository." in response.text @pytest.mark.asyncio async def test_documents_page_lists_seeded_documents(self, app_client): _, client = app_client async with session_scope() as session: postcard_type = (await session.exec(select(DocumentType).where(DocumentType.label == "Postcard"))).one() family_tag = Tag(label="Family", normalized_label="family") doc = Document( name="1924 Postcard", document_type_id=postcard_type.id, archive_identifier="PC-001", ) session.add_all([doc, family_tag]) await session.flush() session.add(DocumentTag(document_id=doc.id, tag_id=family_tag.id)) await session.commit() response = client.get("/ui/documents") assert response.status_code == 200 assert "1924 Postcard" in response.text assert "Postcard" in response.text assert "Document Date" in response.text assert "Author" in response.text assert "Tags" in response.text assert "Family" in response.text assert "# Sources" in response.text assert "Archive Ref" not in response.text assert re.search( r'"name":"name","label":"Document Title".*' r'"name":"authors","label":"Author".*' r'"name":"tags","label":"Tags".*' r'"name":"document_date","label":"Document Date".*' r'"name":"document_type","label":"Type".*' r'"name":"source_count","label":"# Sources"', response.text, re.DOTALL, ) def test_document_create_page_renders_form(self, app_client): _, client = app_client response = client.get("/ui/documents/new") assert response.status_code == 200 assert "Create Document" in response.text assert "Document name" in response.text assert "Linked People" in response.text assert "Document type" in response.text assert "Tags" in response.text @pytest.mark.asyncio async def test_document_create_page_preselects_person_with_disambiguating_label(self, app_client): _, client = app_client async with session_scope() as session: person = Person( given_names="Albert Edward", last_name="Higgins", birth_date=date(1885, 1, 2), ) session.add(person) await session.commit() person_id = str(person.id) response = client.get(f"/ui/documents/new?person_id={person_id}") assert response.status_code == 200 assert "Albert Edward Higgins (1885)" in response.text @pytest.mark.asyncio async def test_document_detail_page_renders_bento_grid_and_metadata(self, app_client, seed_person_and_document): _, client = app_client doc_id, _ = seed_person_and_document response = client.get(f"/ui/documents/{doc_id}") assert response.status_code == 200 assert "Letter from Hig" in response.text assert "ZC-1924-001" in response.text assert "Zenna Cochran" in response.text assert "Document Type:" in response.text assert "Letter" in response.text assert "07-04-1924" in response.text assert "1924-07-04" not in response.text assert "PIPELINE JOBS" in response.text.upper() assert "Edit Document" in response.text assert "Back to Documents" in response.text @pytest.mark.asyncio async def test_document_detail_page_renders_person_context_back_button(self, app_client, seed_person_and_document): _, client = app_client doc_id, person_id = seed_person_and_document response = client.get(f"/ui/documents/{doc_id}?from=person&person_id={person_id}") assert response.status_code == 200 assert "Back to Person" in response.text @pytest.mark.asyncio async def test_document_detail_page_renders_job_context_back_button(self, app_client): _, client = app_client async with session_scope() as session: doc = Document(name="Job-linked Document") session.add(doc) await session.flush() job = Job(document_id=doc.id) session.add(job) await session.commit() doc_id = str(doc.id) job_id = str(job.id) response = client.get(f"/ui/documents/{doc_id}?from=job&job_id={job_id}") assert response.status_code == 200 assert "Back to Job" in response.text @pytest.mark.asyncio async def test_document_jobs_page_redirects_to_filtered_jobs(self, app_client): _, client = app_client async with session_scope() as session: doc = Document(name="Doc With Job") session.add(doc) await session.flush() job = Job(document_id=doc.id) session.add(job) await session.commit() doc_id = str(doc.id) _ = str(job.id) response = client.get(f"/ui/documents/{doc_id}/jobs") assert response.status_code == 200 assert "Jobs for Document" in response.text assert "Create job" not in response.text assert "Refresh" not in response.text @pytest.mark.asyncio async def test_document_edit_page_prefills_existing_values(self, app_client, seed_person_and_document): _, client = app_client doc_id, _ = seed_person_and_document response = client.get(f"/ui/documents/{doc_id}/edit") assert response.status_code == 200 assert "Edit Document Record" in response.text assert "Letter from Hig" in response.text assert "Document date" in response.text assert "Exact date (YYYY-MM-DD)" not in response.text assert "ZC-1924-001" in response.text @pytest.mark.asyncio async def test_document_delete_page_blocks_deletion_when_dependencies_exist(self, app_client): _, client = app_client async with session_scope() as session: doc = Document(name="Doc With Source") session.add(doc) await session.flush() source = Source( document_id=doc.id, page_number=1, upload_name="page_1.png", filename="page_1.png", file_path="/tmp/page_1.png", file_hash="0" * 64, file_size_bytes=1, ) session.add(source) await session.commit() doc_id = str(doc.id) response = client.get(f"/ui/documents/{doc_id}/delete") assert response.status_code == 200 assert "Delete Document" in response.text assert "Delete is blocked because related records exist." in response.text assert "Dependencies present: Sources" in response.text @pytest.mark.asyncio async def test_document_delete_page_allows_unlinked_document_deletion(self, app_client): _, client = app_client async with session_scope() as session: doc = Document(name="Orphan Document") session.add(doc) await session.commit() doc_id = str(doc.id) response = client.get(f"/ui/documents/{doc_id}/delete") assert response.status_code == 200 assert "Delete Document" in response.text assert "Delete document permanently" in response.text assert "Delete is blocked" not in response.text def test_resolve_selected_tag_labels_handles_multiple_payload_shapes(): assert _resolve_selected_tag_labels("Family") == ["Family"] assert _resolve_selected_tag_labels(["Family", "Research"]) == ["Family", "Research"] assert _resolve_selected_tag_labels([{"label": "Family"}, {"value": "Research"}]) == ["Family", "Research"] assert set(_resolve_selected_tag_labels({"value": {"Family", "Research"}})) == {"Family", "Research"}