generated from john/python-template
254 lines
9.5 KiB
Python
254 lines
9.5 KiB
Python
"""Tests for the people page routes and action handlers."""
|
|
|
|
from datetime import date
|
|
from uuid import uuid4
|
|
|
|
import pytest
|
|
from sqlmodel import select
|
|
|
|
from transcription.config import get_settings
|
|
from transcription.db import session_scope
|
|
from transcription.db.models import Document
|
|
from transcription.db.models import DocumentPerson
|
|
from transcription.db.models import Person
|
|
from transcription.db.models import PersonRole
|
|
from transcription.db.models import Source
|
|
|
|
|
|
@pytest.mark.integration
|
|
class TestPeoplePageRendering:
|
|
"""Verify people routes render expected CRUD read states."""
|
|
|
|
def test_people_page_renders_empty_state(self, app_client):
|
|
_, client = app_client
|
|
|
|
response = client.get("/ui/people")
|
|
|
|
assert response.status_code == 200
|
|
assert "People" in response.text
|
|
assert "Create new person" in response.text
|
|
assert "No person records found in repository." in response.text
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_people_page_lists_seeded_people(self, app_client):
|
|
_, client = app_client
|
|
|
|
async with session_scope() as session:
|
|
session.add(Person(full_name="Ada Lovelace", display_name="Ada"))
|
|
await session.commit()
|
|
|
|
response = client.get("/ui/people")
|
|
|
|
assert response.status_code == 200
|
|
assert "Ada Lovelace" in response.text
|
|
assert "Ada" in response.text
|
|
|
|
def test_person_create_page_renders_fields(self, app_client):
|
|
_, client = app_client
|
|
|
|
response = client.get("/ui/people/new")
|
|
|
|
assert response.status_code == 200
|
|
assert "Create Person Record" in response.text
|
|
assert "Full name is required." in response.text
|
|
assert "Birth date" in response.text
|
|
assert "Death date" in response.text
|
|
assert "Birth date (YYYY-MM-DD)" not in response.text
|
|
assert "Death date (YYYY-MM-DD)" not in response.text
|
|
assert "FamilySearch ID" in response.text
|
|
assert "Auto-fill from FamilySearch" not in response.text
|
|
assert "Marriage date (FamilySearch)" not in response.text
|
|
assert "Spouse (FamilySearch)" not in response.text
|
|
assert "Biography" in response.text
|
|
assert "Save person" in response.text
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_person_detail_page_renders_metadata_and_empty_links(self, app_client):
|
|
_, client = app_client
|
|
|
|
async with session_scope() as session:
|
|
person = Person(
|
|
full_name="Grace Hopper",
|
|
display_name="Grace",
|
|
maiden_name="Murray",
|
|
birth_date=date(1906, 12, 9),
|
|
birth_date_raw="1906",
|
|
birth_place="New York",
|
|
death_date=date(1992, 1, 1),
|
|
death_date_raw="1992",
|
|
death_place="Arlington",
|
|
biography="Computer pioneer",
|
|
portrait_path="/images/grace.jpg",
|
|
family_search_id="G8T4-MDQ",
|
|
)
|
|
session.add(person)
|
|
await session.commit()
|
|
person_id = str(person.id)
|
|
|
|
response = client.get(f"/ui/people/{person_id}")
|
|
|
|
assert response.status_code == 200
|
|
assert "Grace Hopper" in response.text
|
|
assert "Full Name:" in response.text
|
|
assert "Display Name:" in response.text
|
|
assert "Maiden Name:" in response.text
|
|
assert "Birth Date:" in response.text
|
|
assert "1906-12-09" in response.text
|
|
assert "Death Date:" in response.text
|
|
assert "1992-01-01" in response.text
|
|
assert "Open Birth Place in Google Maps" not in response.text
|
|
assert "Open Death Place in Google Maps" not in response.text
|
|
assert "google.com/maps/search/?api=1&query=New+York" in response.text
|
|
assert "google.com/maps/search/?api=1&query=Arlington" in response.text
|
|
assert "biography" in response.text.lower()
|
|
assert "Computer pioneer" in response.text
|
|
assert "Created:" in response.text
|
|
assert "Updated:" in response.text
|
|
assert "Open in FamilySearch" not in response.text
|
|
assert "FamilySearch ID:" in response.text
|
|
assert "familysearch.org/tree/person/details/G8T4-MDQ" in response.text
|
|
assert "New Document" in response.text
|
|
assert "No linked documents yet." in response.text
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_person_detail_page_resolves_relative_portrait_path(self, app_client):
|
|
app, client = app_client
|
|
upload_dirs = {app.state.settings.upload_dir, get_settings().upload_dir}
|
|
for upload_dir in upload_dirs:
|
|
portrait_file = upload_dir / "persons" / "person" / "seeded.png"
|
|
portrait_file.parent.mkdir(parents=True, exist_ok=True)
|
|
portrait_file.write_bytes(b"portrait")
|
|
|
|
async with session_scope() as session:
|
|
person = Person(
|
|
full_name="Portrait Person",
|
|
portrait_path="persons/person/seeded.png",
|
|
)
|
|
session.add(person)
|
|
await session.commit()
|
|
person_id = str(person.id)
|
|
|
|
response = client.get(f"/ui/people/{person_id}")
|
|
|
|
assert response.status_code == 200
|
|
assert "No source media available for inspection." not in response.text
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_person_detail_page_renders_linked_documents(self, app_client):
|
|
_, 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")
|
|
session.add_all([person, document])
|
|
await session.flush()
|
|
session.add_all(
|
|
[
|
|
Source(
|
|
document_id=document.id,
|
|
page_number=1,
|
|
upload_name="linked-page-1.png",
|
|
filename="linked-page-1.png",
|
|
file_path="/tmp/linked-page-1.png",
|
|
file_hash="1" * 64,
|
|
file_size_bytes=1,
|
|
),
|
|
Source(
|
|
document_id=document.id,
|
|
page_number=2,
|
|
upload_name="linked-page-2.png",
|
|
filename="linked-page-2.png",
|
|
file_path="/tmp/linked-page-2.png",
|
|
file_hash="2" * 64,
|
|
file_size_bytes=1,
|
|
),
|
|
]
|
|
)
|
|
|
|
session.add(
|
|
DocumentPerson(
|
|
document_id=document.id,
|
|
person_id=person.id,
|
|
role_id=author_role.id,
|
|
)
|
|
)
|
|
await session.commit()
|
|
person_id = str(person.id)
|
|
|
|
response = client.get(f"/ui/people/{person_id}")
|
|
|
|
assert response.status_code == 200
|
|
assert "Document Name" in response.text
|
|
assert "Number of Pages" in response.text
|
|
assert "Linked Document" in response.text
|
|
assert "Author" in response.text
|
|
assert '"page_count":2' in response.text
|
|
assert "Open" not in response.text
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_person_detail_page_hides_empty_maiden_name(self, app_client):
|
|
_, client = app_client
|
|
|
|
async with session_scope() as session:
|
|
person = Person(full_name="No Maiden Name")
|
|
session.add(person)
|
|
await session.commit()
|
|
person_id = str(person.id)
|
|
|
|
response = client.get(f"/ui/people/{person_id}")
|
|
|
|
assert response.status_code == 200
|
|
assert "Maiden Name:" not in response.text
|
|
|
|
def test_person_detail_page_handles_invalid_id(self, app_client):
|
|
_, client = app_client
|
|
|
|
response = client.get("/ui/people/not-a-uuid")
|
|
|
|
assert response.status_code == 200
|
|
assert "Invalid person id" in response.text
|
|
|
|
def test_person_detail_page_handles_missing_person(self, app_client):
|
|
_, client = app_client
|
|
|
|
response = client.get(f"/ui/people/{uuid4()}")
|
|
|
|
assert response.status_code == 200
|
|
assert "Person not found" in response.text
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_person_edit_page_renders_expected_fields(self, app_client):
|
|
_, client = app_client
|
|
|
|
async with session_scope() as session:
|
|
person = Person(full_name="Editable Person", display_name="EP")
|
|
session.add(person)
|
|
await session.commit()
|
|
person_id = str(person.id)
|
|
|
|
response = client.get(f"/ui/people/{person_id}/edit")
|
|
|
|
assert response.status_code == 200
|
|
assert "Edit Person Record" in response.text
|
|
assert "Full name is required." in response.text
|
|
assert "Editable Person" in response.text
|
|
assert "Save changes" in response.text
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_person_delete_page_shows_confirmation_when_unlinked(self, app_client):
|
|
_, client = app_client
|
|
|
|
async with session_scope() as session:
|
|
person = Person(full_name="Safe Delete")
|
|
session.add(person)
|
|
await session.commit()
|
|
person_id = str(person.id)
|
|
|
|
response = client.get(f"/ui/people/{person_id}/delete")
|
|
|
|
assert response.status_code == 200
|
|
assert "Delete Person Record" in response.text
|
|
assert "This action permanently deletes the person record." in response.text
|
|
assert "Delete person permanently" in response.text
|