Files
transcription/tests/ui/test_documents_page.py
T
Jim Lancaster 141ee1fa85
Quality Gate / gate (push) Failing after 11s
V5.0 Minor change to UI
2026-08-23 10:49:40 -05:00

240 lines
8.6 KiB
Python

"""Tests for the documents page routes and action handlers."""
from datetime import date
import re
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 DocumentType
from transcription.db.models import Job
from transcription.db.models import Person
from transcription.db.models import PersonRole
from transcription.db.models import Tag
from transcription.db.models import DocumentTag
from transcription.db.models import Source
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(full_name="Zenna Cochran")
session.add(person)
await session.flush()
doc = Document(
name="Letter from Hig",
document_type_id=letter_type.id,
archive_identifier="ZC-1924-001",
)
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(
full_name="Albert Edward Higgins",
display_name="Hig",
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 "Hig - 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 "PIPELINE JOBS" in response.text.upper()
assert "Edit Document" 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 "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"}