generated from john/python-template
V4.4 Complete
This commit is contained in:
@@ -9,10 +9,10 @@ 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 DocumentPersonRole
|
||||
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
|
||||
|
||||
# --- Helper Fixtures ---
|
||||
@@ -23,6 +23,7 @@ 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()
|
||||
@@ -38,7 +39,7 @@ async def seed_person_and_document():
|
||||
link = DocumentPerson(
|
||||
document_id=doc.id,
|
||||
person_id=person.id,
|
||||
role=DocumentPersonRole.AUTHOR,
|
||||
role_id=author_role.id,
|
||||
)
|
||||
session.add(link)
|
||||
await session.commit()
|
||||
@@ -92,7 +93,7 @@ class TestDocumentsPageRendering:
|
||||
assert response.status_code == 200
|
||||
assert "Create Document" in response.text
|
||||
assert "Document name" in response.text
|
||||
assert "Linked People by Role" in response.text
|
||||
assert "Linked People" in response.text
|
||||
assert "Document type" in response.text
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -132,7 +133,7 @@ class TestDocumentsPageRendering:
|
||||
_, client = app_client
|
||||
|
||||
async with session_scope() as session:
|
||||
doc = Document(name="Doc With Job", document_type="letter")
|
||||
doc = Document(name="Doc With Job")
|
||||
session.add(doc)
|
||||
await session.flush()
|
||||
|
||||
@@ -165,7 +166,7 @@ class TestDocumentsPageRendering:
|
||||
_, client = app_client
|
||||
|
||||
async with session_scope() as session:
|
||||
doc = Document(name="Doc With Source", document_type="letter")
|
||||
doc = Document(name="Doc With Source")
|
||||
session.add(doc)
|
||||
await session.flush()
|
||||
|
||||
@@ -194,7 +195,7 @@ class TestDocumentsPageRendering:
|
||||
_, client = app_client
|
||||
|
||||
async with session_scope() as session:
|
||||
doc = Document(name="Orphan Document", document_type="note")
|
||||
doc = Document(name="Orphan Document")
|
||||
session.add(doc)
|
||||
await session.commit()
|
||||
doc_id = str(doc.id)
|
||||
|
||||
@@ -15,7 +15,7 @@ from transcription.db.models import JobStatus
|
||||
async def seed_document_with_unlinked_job():
|
||||
"""Seed a document and a queued job for testing route actions."""
|
||||
async with session_scope() as session:
|
||||
document = Document(name="Test Archival Letter", document_type="letter")
|
||||
document = Document(name="Test Archival Letter")
|
||||
session.add(document)
|
||||
await session.flush()
|
||||
|
||||
@@ -72,7 +72,7 @@ class TestJobsPageRendering:
|
||||
_, client = app_client
|
||||
|
||||
async with session_scope() as session:
|
||||
doc = Document(name="Preselected Journal Entry", document_type="journal")
|
||||
doc = Document(name="Preselected Journal Entry")
|
||||
session.add(doc)
|
||||
await session.commit()
|
||||
doc_id = str(doc.id)
|
||||
@@ -133,7 +133,7 @@ class TestJobsPageRendering:
|
||||
_, client = app_client
|
||||
|
||||
async with session_scope() as session:
|
||||
doc = Document(name="Processing Doc", document_type="letter")
|
||||
doc = Document(name="Processing Doc")
|
||||
session.add(doc)
|
||||
await session.flush()
|
||||
job = Job(document_id=doc.id, status=JobStatus.PROCESSING)
|
||||
|
||||
@@ -4,12 +4,13 @@ from datetime import date
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
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 DocumentPersonRole
|
||||
from transcription.db.models import Person
|
||||
from transcription.db.models import PersonRole
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@@ -122,8 +123,9 @@ class TestPeoplePageRendering:
|
||||
_, client = app_client
|
||||
|
||||
async with session_scope() as session:
|
||||
author_role = (await session.exec(select(PersonRole).where(PersonRole.semantic_key == "author"))).one()
|
||||
person = Person(full_name="Linked Person")
|
||||
document = Document(name="Linked Document", document_type="letter")
|
||||
document = Document(name="Linked Document")
|
||||
session.add_all([person, document])
|
||||
await session.flush()
|
||||
|
||||
@@ -131,7 +133,7 @@ class TestPeoplePageRendering:
|
||||
DocumentPerson(
|
||||
document_id=document.id,
|
||||
person_id=person.id,
|
||||
role=DocumentPersonRole.AUTHOR,
|
||||
role_id=author_role.id,
|
||||
)
|
||||
)
|
||||
await session.commit()
|
||||
@@ -141,7 +143,7 @@ class TestPeoplePageRendering:
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Linked Document" in response.text
|
||||
assert "Role: author" in response.text
|
||||
assert "Role: Author" in response.text
|
||||
|
||||
def test_person_detail_page_handles_invalid_id(self, app_client):
|
||||
_, client = app_client
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from transcription.db import session_scope
|
||||
from transcription.db.models import Document
|
||||
from transcription.db.models import Source
|
||||
from transcription.ui.pages.print_preview_page import reflow_transcription
|
||||
|
||||
|
||||
def test_reflow_transcription_preserves_paragraph_boundaries():
|
||||
assert reflow_transcription("first line\nsecond line\n\nnext paragraph") == [
|
||||
"first line second line",
|
||||
"next paragraph",
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.mark.asyncio
|
||||
async def test_document_print_preview_and_safe_media_route(app_client):
|
||||
app, client = app_client
|
||||
media_path = app.state.settings.upload_dir / "documents" / "print-page.png"
|
||||
pdf_path = app.state.settings.upload_dir / "documents" / "print-page.pdf"
|
||||
media_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
media_path.write_bytes(b"\x89PNG\r\n\x1a\n")
|
||||
pdf_path.write_bytes(b"%PDF-1.4\n%%EOF")
|
||||
|
||||
async with session_scope() as session:
|
||||
document = Document(name="<Print & Preserve>", notes="<script>unsafe()</script>")
|
||||
session.add(document)
|
||||
await session.flush()
|
||||
source = Source(
|
||||
document_id=document.id,
|
||||
page_number=1,
|
||||
upload_name="print-page.png",
|
||||
filename="print-page.png",
|
||||
file_path=str(media_path),
|
||||
file_hash="a" * 64,
|
||||
file_size_bytes=media_path.stat().st_size,
|
||||
raw_transcription="line one\nline two",
|
||||
)
|
||||
session.add(source)
|
||||
session.add(
|
||||
Source(
|
||||
document_id=document.id,
|
||||
page_number=2,
|
||||
upload_name="print-page.pdf",
|
||||
filename="print-page.pdf",
|
||||
file_path=str(pdf_path),
|
||||
file_hash="c" * 64,
|
||||
file_size_bytes=pdf_path.stat().st_size,
|
||||
raw_transcription="PDF source",
|
||||
)
|
||||
)
|
||||
await session.commit()
|
||||
document_id = document.id
|
||||
source_id = source.id
|
||||
|
||||
response = client.get(f"/ui/documents/{document_id}/print")
|
||||
assert response.status_code == 200
|
||||
assert "Print & Preserve" in response.text
|
||||
assert "<script>unsafe()</script>" in response.text
|
||||
assert "Facsimile" in response.text
|
||||
assert "Text only" in response.text
|
||||
assert "print-page-break" in response.text
|
||||
assert "print-source-pdf" in response.text
|
||||
assert str(media_path) not in response.text
|
||||
assert str(pdf_path) not in response.text
|
||||
|
||||
media_response = client.get(f"/api/v4/documents/{document_id}/sources/{source_id}/media")
|
||||
assert media_response.status_code == 200
|
||||
assert media_response.headers["content-type"] == "image/png"
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.mark.asyncio
|
||||
async def test_document_source_media_rejects_cross_document_access(app_client):
|
||||
app, client = app_client
|
||||
media_path = Path(app.state.settings.upload_dir) / "documents" / "other.png"
|
||||
media_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
media_path.write_bytes(b"\x89PNG\r\n\x1a\n")
|
||||
|
||||
async with session_scope() as session:
|
||||
owner = Document(name="Owner")
|
||||
other = Document(name="Other")
|
||||
session.add_all([owner, other])
|
||||
await session.flush()
|
||||
source = Source(
|
||||
document_id=owner.id,
|
||||
page_number=1,
|
||||
upload_name="other.png",
|
||||
filename="other.png",
|
||||
file_path=str(media_path),
|
||||
file_hash="b" * 64,
|
||||
file_size_bytes=media_path.stat().st_size,
|
||||
)
|
||||
session.add(source)
|
||||
await session.commit()
|
||||
other_id = other.id
|
||||
source_id = source.id
|
||||
|
||||
response = client.get(f"/api/v4/documents/{other_id}/sources/{source_id}/media")
|
||||
assert response.status_code == 404
|
||||
@@ -80,7 +80,7 @@ class TestSourcesPageRendering:
|
||||
_, client = app_client
|
||||
|
||||
async with session_scope() as session:
|
||||
document = Document(name="Source Document", document_type="letter")
|
||||
document = Document(name="Source Document")
|
||||
session.add(document)
|
||||
await session.flush()
|
||||
session.add(
|
||||
@@ -108,8 +108,8 @@ class TestSourcesPageRendering:
|
||||
_, client = app_client
|
||||
|
||||
async with session_scope() as session:
|
||||
target = Document(name="Target", document_type="letter")
|
||||
other = Document(name="Other", document_type="record")
|
||||
target = Document(name="Target")
|
||||
other = Document(name="Other")
|
||||
session.add_all([target, other])
|
||||
await session.flush()
|
||||
|
||||
@@ -274,7 +274,7 @@ class TestSourcesPageRendering:
|
||||
_, client = app_client
|
||||
|
||||
async with session_scope() as session:
|
||||
document = Document(name="Unlinked Source Doc", document_type="memo")
|
||||
document = Document(name="Unlinked Source Doc")
|
||||
session.add(document)
|
||||
await session.flush()
|
||||
source = Source(
|
||||
|
||||
Reference in New Issue
Block a user