generated from john/python-template
UI update complete?
This commit is contained in:
@@ -22,10 +22,12 @@ from transcription.db import create_all
|
||||
from transcription.db import initialize_database_runtime
|
||||
from transcription.db import session_scope
|
||||
from transcription.db.models import Document
|
||||
from transcription.db.models import DocumentPerson
|
||||
from transcription.db.models import Job
|
||||
from transcription.db.models import JobSource
|
||||
from transcription.db.models import JobSourceStatus
|
||||
from transcription.db.models import JobStatus
|
||||
from transcription.db.models import Person
|
||||
from transcription.db.models import Source
|
||||
|
||||
RevisionSeed = str
|
||||
@@ -58,9 +60,12 @@ def clear_ui_database(app_client: tuple[FastAPI, TestClient]) -> None:
|
||||
|
||||
async def _clear() -> None:
|
||||
async with session_scope() as session:
|
||||
await session.exec(delete(JobSource))
|
||||
await session.exec(delete(DocumentPerson))
|
||||
await session.exec(delete(Source))
|
||||
await session.exec(delete(Job))
|
||||
await session.exec(delete(Document))
|
||||
await session.exec(delete(Person))
|
||||
await session.commit()
|
||||
|
||||
asyncio.run(_clear())
|
||||
|
||||
@@ -0,0 +1,331 @@
|
||||
"""Tests for the documents page routes."""
|
||||
|
||||
import asyncio
|
||||
from datetime import UTC
|
||||
from datetime import date
|
||||
from datetime import datetime
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
|
||||
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 Job
|
||||
from transcription.db.models import Person
|
||||
from transcription.db.models import Source
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
class TestDocumentsPageRendering:
|
||||
"""Verify document list/detail routes render expected read states."""
|
||||
|
||||
def test_documents_page_renders_empty_state(self, app_client):
|
||||
"""GET /ui/documents renders empty-state text when no records exist."""
|
||||
_, client = app_client
|
||||
|
||||
response = client.get("/ui/documents")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Documents" in response.text
|
||||
assert "No documents yet." in response.text
|
||||
|
||||
def test_documents_page_lists_seeded_documents(self, app_client):
|
||||
"""GET /ui/documents lists seeded document cards."""
|
||||
_, client = app_client
|
||||
|
||||
async def _seed_document() -> None:
|
||||
async with session_scope() as session:
|
||||
session.add(Document(name="Seeded Document", document_type="letter"))
|
||||
await session.commit()
|
||||
|
||||
asyncio.run(_seed_document())
|
||||
|
||||
response = client.get("/ui/documents")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Seeded Document" in response.text
|
||||
assert "Type: letter" in response.text
|
||||
|
||||
def test_document_detail_page_renders_metadata_and_empty_related_sections(self, app_client):
|
||||
"""GET /ui/documents/{document_id} shows metadata and related empty states."""
|
||||
_, client = app_client
|
||||
|
||||
async def _seed_document() -> str:
|
||||
async with session_scope() as session:
|
||||
document = Document(
|
||||
name="Zenna Letter",
|
||||
document_type="letter",
|
||||
document_date=date(1885, 7, 13),
|
||||
document_date_raw="c. 1885",
|
||||
location_created="Ohio",
|
||||
notes="Family archive",
|
||||
archive_identifier="BOX-1-FOLDER-2",
|
||||
)
|
||||
session.add(document)
|
||||
await session.commit()
|
||||
await session.refresh(document)
|
||||
return str(document.id)
|
||||
|
||||
document_id = asyncio.run(_seed_document())
|
||||
|
||||
response = client.get(f"/ui/documents/{document_id}")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Zenna Letter" in response.text
|
||||
assert "Document type: letter" in response.text
|
||||
assert "Exact date: 1885-07-13" in response.text
|
||||
assert "Approximate date: c. 1885" in response.text
|
||||
assert "Location created: Ohio" in response.text
|
||||
assert "Archive identifier: BOX-1-FOLDER-2" in response.text
|
||||
assert "Notes: Family archive" in response.text
|
||||
assert "Created at (read-only):" in response.text
|
||||
assert "Updated at (read-only):" in response.text
|
||||
assert "No linked people yet." in response.text
|
||||
assert "No sources added yet." in response.text
|
||||
assert "No jobs created yet." in response.text
|
||||
assert "Add sources" in response.text
|
||||
assert "Create job" in response.text
|
||||
assert "View sources" in response.text
|
||||
assert "View jobs" in response.text
|
||||
assert "Edit document" in response.text
|
||||
assert "Delete document" in response.text
|
||||
|
||||
def test_document_detail_page_renders_related_people_sources_and_jobs(self, app_client):
|
||||
"""GET /ui/documents/{document_id} shows related records when present."""
|
||||
_, client = app_client
|
||||
|
||||
async def _seed_related() -> str:
|
||||
async with session_scope() as session:
|
||||
document = Document(name="Roster", document_type="record")
|
||||
person = Person(full_name="Jane Doe")
|
||||
session.add(document)
|
||||
session.add(person)
|
||||
await session.flush()
|
||||
|
||||
session.add(
|
||||
DocumentPerson(
|
||||
document_id=document.id,
|
||||
person_id=person.id,
|
||||
role=DocumentPersonRole.AUTHOR,
|
||||
)
|
||||
)
|
||||
session.add(
|
||||
Source(
|
||||
document_id=document.id,
|
||||
page_number=1,
|
||||
upload_name="001_page.png",
|
||||
filename="stored_001_page.png",
|
||||
file_path="/tmp/stored_001_page.png",
|
||||
)
|
||||
)
|
||||
session.add(
|
||||
Job(
|
||||
document_id=document.id,
|
||||
)
|
||||
)
|
||||
await session.commit()
|
||||
await session.refresh(document)
|
||||
return str(document.id)
|
||||
|
||||
document_id = asyncio.run(_seed_related())
|
||||
|
||||
response = client.get(f"/ui/documents/{document_id}")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Jane Doe (author)" in response.text
|
||||
assert "Page 1: 001_page.png" in response.text
|
||||
assert "queued -" in response.text
|
||||
|
||||
def test_document_jobs_page_filters_to_document_context(self, app_client):
|
||||
_, client = app_client
|
||||
|
||||
async def _seed() -> str:
|
||||
async with session_scope() as session:
|
||||
target = Document(name="Target", document_type="letter")
|
||||
other = Document(name="Other", document_type="record")
|
||||
session.add(target)
|
||||
session.add(other)
|
||||
await session.flush()
|
||||
session.add(Job(document_id=target.id))
|
||||
session.add(Job(document_id=other.id))
|
||||
await session.commit()
|
||||
await session.refresh(target)
|
||||
return str(target.id)
|
||||
|
||||
document_id = asyncio.run(_seed())
|
||||
response = client.get(f"/ui/documents/{document_id}/jobs")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Jobs for Target" in response.text
|
||||
assert "Jobs for Other" not in response.text
|
||||
|
||||
def test_document_sources_page_filters_to_document_context(self, app_client):
|
||||
_, client = app_client
|
||||
|
||||
async def _seed() -> str:
|
||||
async with session_scope() as session:
|
||||
target = Document(name="Target", document_type="letter")
|
||||
other = Document(name="Other", document_type="record")
|
||||
session.add(target)
|
||||
session.add(other)
|
||||
await session.flush()
|
||||
|
||||
session.add(
|
||||
Source(
|
||||
document_id=target.id,
|
||||
page_number=1,
|
||||
upload_name="target_page.png",
|
||||
filename="target_stored.png",
|
||||
file_path="/tmp/target_stored.png",
|
||||
)
|
||||
)
|
||||
session.add(
|
||||
Source(
|
||||
document_id=other.id,
|
||||
page_number=1,
|
||||
upload_name="other_page.png",
|
||||
filename="other_stored.png",
|
||||
file_path="/tmp/other_stored.png",
|
||||
)
|
||||
)
|
||||
await session.commit()
|
||||
await session.refresh(target)
|
||||
return str(target.id)
|
||||
|
||||
document_id = asyncio.run(_seed())
|
||||
response = client.get(f"/ui/documents/{document_id}/sources")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Sources for Target" in response.text
|
||||
assert "target_page.png" in response.text
|
||||
assert "other_page.png" not in response.text
|
||||
|
||||
def test_document_detail_page_rejects_invalid_id(self, app_client):
|
||||
"""GET /ui/documents/{document_id} shows validation feedback for malformed IDs."""
|
||||
_, client = app_client
|
||||
|
||||
response = client.get("/ui/documents/not-a-uuid")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Invalid document id" in response.text
|
||||
|
||||
def test_document_detail_page_handles_missing_document(self, app_client):
|
||||
"""GET /ui/documents/{document_id} shows not-found state for unknown IDs."""
|
||||
_, client = app_client
|
||||
|
||||
response = client.get(f"/ui/documents/{uuid4()}")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Document not found" in response.text
|
||||
|
||||
def test_document_edit_page_renders_expected_fields(self, app_client):
|
||||
"""GET /ui/documents/{document_id}/edit renders editable fields and save controls."""
|
||||
_, client = app_client
|
||||
|
||||
async def _seed_document() -> str:
|
||||
async with session_scope() as session:
|
||||
document = Document(
|
||||
name="Editable Document",
|
||||
document_type="memo",
|
||||
document_date_raw="c. 1900",
|
||||
)
|
||||
session.add(document)
|
||||
await session.commit()
|
||||
await session.refresh(document)
|
||||
return str(document.id)
|
||||
|
||||
document_id = asyncio.run(_seed_document())
|
||||
|
||||
response = client.get(f"/ui/documents/{document_id}/edit")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Edit document" in response.text
|
||||
assert "Document name and document type are required." in response.text
|
||||
assert "Document name" in response.text
|
||||
assert "Document type" in response.text
|
||||
assert "Exact date (YYYY-MM-DD)" in response.text
|
||||
assert "Approximate date" in response.text
|
||||
assert "Document location" in response.text
|
||||
assert "Archive identifier" in response.text
|
||||
assert "Notes" in response.text
|
||||
assert "Save changes" in response.text
|
||||
|
||||
def test_document_delete_page_shows_confirmation_when_unlinked(self, app_client):
|
||||
"""GET /ui/documents/{document_id}/delete renders permanent-action confirmation if unlinked."""
|
||||
_, client = app_client
|
||||
|
||||
async def _seed_document() -> str:
|
||||
async with session_scope() as session:
|
||||
document = Document(name="Safe Delete", document_type="letter")
|
||||
session.add(document)
|
||||
await session.commit()
|
||||
await session.refresh(document)
|
||||
return str(document.id)
|
||||
|
||||
document_id = asyncio.run(_seed_document())
|
||||
|
||||
response = client.get(f"/ui/documents/{document_id}/delete")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Delete document" in response.text
|
||||
assert "This action permanently deletes the document." in response.text
|
||||
assert "Delete document permanently" in response.text
|
||||
|
||||
def test_document_delete_page_shows_blocked_state_when_dependencies_exist(self, app_client):
|
||||
"""GET /ui/documents/{document_id}/delete explains blocked deletion with dependency categories."""
|
||||
_, client = app_client
|
||||
|
||||
async def _seed_related() -> str:
|
||||
async with session_scope() as session:
|
||||
document = Document(name="Blocked Delete", document_type="record")
|
||||
session.add(document)
|
||||
await session.flush()
|
||||
|
||||
session.add(
|
||||
Source(
|
||||
document_id=document.id,
|
||||
page_number=1,
|
||||
upload_name="001_page.png",
|
||||
filename="stored_001_page.png",
|
||||
file_path="/tmp/stored_001_page.png",
|
||||
)
|
||||
)
|
||||
session.add(Job(document_id=document.id))
|
||||
await session.commit()
|
||||
await session.refresh(document)
|
||||
return str(document.id)
|
||||
|
||||
document_id = asyncio.run(_seed_related())
|
||||
|
||||
response = client.get(f"/ui/documents/{document_id}/delete")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Delete is blocked because related records exist." in response.text
|
||||
assert "Dependencies present: Sources, Jobs" in response.text
|
||||
assert "Go to Jobs" in response.text
|
||||
|
||||
def test_job_create_page_preselects_document_query_param(self, app_client):
|
||||
"""GET /ui/jobs/new?document_id=... includes the selected document in rendered state."""
|
||||
_, client = app_client
|
||||
|
||||
async def _seed_document() -> str:
|
||||
async with session_scope() as session:
|
||||
document = Document(
|
||||
name="Preselected Document",
|
||||
document_type="letter",
|
||||
created_at=datetime.now(UTC),
|
||||
updated_at=datetime.now(UTC),
|
||||
)
|
||||
session.add(document)
|
||||
await session.commit()
|
||||
await session.refresh(document)
|
||||
return str(document.id)
|
||||
|
||||
document_id = asyncio.run(_seed_document())
|
||||
|
||||
response = client.get(f"/ui/jobs/new?document_id={document_id}")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Preselected Document" in response.text
|
||||
@@ -1,10 +1,13 @@
|
||||
"""Tests for the jobs page route."""
|
||||
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
|
||||
from transcription.db import session_scope
|
||||
from transcription.db.models import Document
|
||||
from transcription.db.models import JobStatus
|
||||
|
||||
|
||||
@@ -18,8 +21,39 @@ class TestPageRendering:
|
||||
response = client.get("/ui/jobs")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Create job" in response.text
|
||||
assert "No jobs yet." in response.text
|
||||
|
||||
def test_job_create_page_requires_existing_documents(self, app_client):
|
||||
"""GET /ui/jobs/new shows guidance when no Documents exist."""
|
||||
_, client = app_client
|
||||
|
||||
response = client.get("/ui/jobs/new")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Create job" in response.text
|
||||
assert "No documents available. Create a Document before creating a Job." in response.text
|
||||
|
||||
def test_job_create_page_lists_available_documents(self, app_client):
|
||||
"""GET /ui/jobs/new renders document choices when Documents exist."""
|
||||
_, client = app_client
|
||||
|
||||
async def _seed_document() -> None:
|
||||
async with session_scope() as session:
|
||||
session.add(Document(name="Seeded Document"))
|
||||
await session.commit()
|
||||
|
||||
asyncio.run(_seed_document())
|
||||
|
||||
response = client.get("/ui/jobs/new")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Create job" in response.text
|
||||
assert "Seeded Document" in response.text
|
||||
assert "Files are processed alphabetically by original filename." in response.text
|
||||
assert "No files uploaded yet." in response.text
|
||||
assert "Upload folder" in response.text
|
||||
|
||||
def test_jobs_page_lists_seeded_jobs(self, app_client, seed_job):
|
||||
"""GET /ui/jobs lists seeded jobs from the in-memory database."""
|
||||
_, client = app_client
|
||||
@@ -54,6 +88,14 @@ class TestPageRendering:
|
||||
assert "original text" in response.text
|
||||
assert "Document preview" in response.text
|
||||
assert "/uploads/detail.pdf" in response.text
|
||||
assert "Provider:" in response.text
|
||||
assert "Model:" in response.text
|
||||
assert "Prompt:" in response.text
|
||||
assert "Retry count:" in response.text
|
||||
assert "Last updated:" in response.text
|
||||
assert "Delete job" in response.text
|
||||
assert "Delete source" in response.text
|
||||
assert "This permanently deletes the source from this job context." in response.text
|
||||
|
||||
def test_job_detail_page_rejects_invalid_id(self, app_client):
|
||||
"""GET /ui/jobs/{job_id} shows validation feedback for malformed IDs."""
|
||||
@@ -105,3 +147,24 @@ class TestPageRendering:
|
||||
assert "Revision Editor" in response.text
|
||||
assert "Update revision" in response.text
|
||||
assert "hello" in response.text
|
||||
|
||||
def test_job_delete_page_shows_confirmation_when_not_processing(self, app_client, seed_job):
|
||||
_, client = app_client
|
||||
job_id = seed_job(filename="delete-ready.pdf", status=JobStatus.TRANSCRIBED)
|
||||
|
||||
response = client.get(f"/ui/jobs/{job_id}/delete")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Delete job" in response.text
|
||||
assert "This action permanently deletes the job." in response.text
|
||||
assert "Delete job permanently" in response.text
|
||||
|
||||
def test_job_delete_page_shows_blocked_state_when_processing(self, app_client, seed_job):
|
||||
_, client = app_client
|
||||
job_id = seed_job(filename="delete-blocked.pdf", status=JobStatus.PROCESSING)
|
||||
|
||||
response = client.get(f"/ui/jobs/{job_id}/delete")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Delete is blocked while the job is processing." in response.text
|
||||
assert "Wait for processing to complete, then retry delete." in response.text
|
||||
|
||||
@@ -11,8 +11,12 @@ class TestPageRegistration:
|
||||
"""Mounted UI routes respond successfully when the full app is created."""
|
||||
_, client = app_client
|
||||
|
||||
upload_response = client.get("/ui/upload")
|
||||
upload_response = client.get("/ui/upload", follow_redirects=False)
|
||||
documents_response = client.get("/ui/documents")
|
||||
people_response = client.get("/ui/people")
|
||||
jobs_response = client.get("/ui/jobs")
|
||||
|
||||
assert upload_response.status_code == 200
|
||||
assert upload_response.status_code == 307
|
||||
assert documents_response.status_code == 200
|
||||
assert people_response.status_code == 200
|
||||
assert jobs_response.status_code == 200
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
"""Tests for the people page routes."""
|
||||
|
||||
import asyncio
|
||||
from datetime import date
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
|
||||
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
|
||||
|
||||
|
||||
@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 people yet." in response.text
|
||||
|
||||
def test_people_page_lists_seeded_people(self, app_client):
|
||||
_, client = app_client
|
||||
|
||||
async def _seed_person() -> None:
|
||||
async with session_scope() as session:
|
||||
session.add(Person(full_name="Ada Lovelace", display_name="Ada"))
|
||||
await session.commit()
|
||||
|
||||
asyncio.run(_seed_person())
|
||||
|
||||
response = client.get("/ui/people")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Ada Lovelace" in response.text
|
||||
assert "Display name: 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" in response.text
|
||||
assert "Full name is required." in response.text
|
||||
assert "Birth date (YYYY-MM-DD)" in response.text
|
||||
assert "Death date (YYYY-MM-DD)" in response.text
|
||||
assert "Biography" in response.text
|
||||
assert "Save person" in response.text
|
||||
|
||||
def test_person_detail_page_renders_metadata_and_empty_links(self, app_client):
|
||||
_, client = app_client
|
||||
|
||||
async def _seed_person() -> str:
|
||||
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",
|
||||
)
|
||||
session.add(person)
|
||||
await session.commit()
|
||||
await session.refresh(person)
|
||||
return str(person.id)
|
||||
|
||||
person_id = asyncio.run(_seed_person())
|
||||
|
||||
response = client.get(f"/ui/people/{person_id}")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Grace Hopper" in response.text
|
||||
assert "Display name: Grace" in response.text
|
||||
assert "Maiden name: Murray" in response.text
|
||||
assert "Birth date: 1906-12-09" in response.text
|
||||
assert "Death date: 1992-01-01" in response.text
|
||||
assert "Biography: Computer pioneer" in response.text
|
||||
assert "Portrait path: /images/grace.jpg" in response.text
|
||||
assert "Created at (read-only):" in response.text
|
||||
assert "Updated at (read-only):" in response.text
|
||||
assert "No linked documents yet." in response.text
|
||||
assert "Link this person from a Document workflow." in response.text
|
||||
|
||||
def test_person_detail_page_renders_linked_documents(self, app_client):
|
||||
_, client = app_client
|
||||
|
||||
async def _seed_links() -> str:
|
||||
async with session_scope() as session:
|
||||
person = Person(full_name="Linked Person")
|
||||
document = Document(name="Linked Document", document_type="letter")
|
||||
session.add(person)
|
||||
session.add(document)
|
||||
await session.flush()
|
||||
|
||||
session.add(
|
||||
DocumentPerson(
|
||||
document_id=document.id,
|
||||
person_id=person.id,
|
||||
role=DocumentPersonRole.AUTHOR,
|
||||
)
|
||||
)
|
||||
await session.commit()
|
||||
await session.refresh(person)
|
||||
return str(person.id)
|
||||
|
||||
person_id = asyncio.run(_seed_links())
|
||||
|
||||
response = client.get(f"/ui/people/{person_id}")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Linked Document (author)" 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
|
||||
|
||||
def test_person_edit_page_renders_expected_fields(self, app_client):
|
||||
_, client = app_client
|
||||
|
||||
async def _seed_person() -> str:
|
||||
async with session_scope() as session:
|
||||
person = Person(full_name="Editable Person", display_name="EP")
|
||||
session.add(person)
|
||||
await session.commit()
|
||||
await session.refresh(person)
|
||||
return str(person.id)
|
||||
|
||||
person_id = asyncio.run(_seed_person())
|
||||
|
||||
response = client.get(f"/ui/people/{person_id}/edit")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Edit person" in response.text
|
||||
assert "Full name is required." in response.text
|
||||
assert "Full name" in response.text
|
||||
assert "Save changes" in response.text
|
||||
|
||||
def test_person_delete_page_shows_confirmation_when_unlinked(self, app_client):
|
||||
_, client = app_client
|
||||
|
||||
async def _seed_person() -> str:
|
||||
async with session_scope() as session:
|
||||
person = Person(full_name="Safe Delete")
|
||||
session.add(person)
|
||||
await session.commit()
|
||||
await session.refresh(person)
|
||||
return str(person.id)
|
||||
|
||||
person_id = asyncio.run(_seed_person())
|
||||
|
||||
response = client.get(f"/ui/people/{person_id}/delete")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Delete person" in response.text
|
||||
assert "This action permanently deletes the person." in response.text
|
||||
assert "Delete person permanently" in response.text
|
||||
|
||||
def test_person_delete_page_shows_blocked_state_when_linked_documents_exist(self, app_client):
|
||||
_, client = app_client
|
||||
|
||||
async def _seed_links() -> str:
|
||||
async with session_scope() as session:
|
||||
person = Person(full_name="Blocked Delete")
|
||||
document = Document(name="Linked Document", document_type="record")
|
||||
session.add(person)
|
||||
session.add(document)
|
||||
await session.flush()
|
||||
|
||||
session.add(
|
||||
DocumentPerson(
|
||||
document_id=document.id,
|
||||
person_id=person.id,
|
||||
role=DocumentPersonRole.AUTHOR,
|
||||
)
|
||||
)
|
||||
await session.commit()
|
||||
await session.refresh(person)
|
||||
return str(person.id)
|
||||
|
||||
person_id = asyncio.run(_seed_links())
|
||||
|
||||
response = client.get(f"/ui/people/{person_id}/delete")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Delete is blocked because linked documents exist." in response.text
|
||||
assert "Linked documents: 1" in response.text
|
||||
assert "Go to Documents" in response.text
|
||||
@@ -16,21 +16,17 @@ class TestPageRendering:
|
||||
assert response.headers["location"] == "/ui"
|
||||
|
||||
def test_ui_redirects_to_upload(self, app_client):
|
||||
"""GET /ui redirects to the upload page."""
|
||||
"""GET /ui redirects to the jobs page."""
|
||||
_, client = app_client
|
||||
response = client.get("/ui", follow_redirects=False)
|
||||
|
||||
assert response.status_code == 307
|
||||
assert response.headers["location"] == "/ui/upload"
|
||||
assert response.headers["location"] == "/ui/jobs"
|
||||
|
||||
def test_upload_page_renders_expected_controls(self, app_client):
|
||||
"""GET /ui/upload returns the page shell and upload controls."""
|
||||
"""GET /ui/upload redirects to the job-create flow."""
|
||||
_, client = app_client
|
||||
response = client.get("/ui/upload")
|
||||
response = client.get("/ui/upload", follow_redirects=False)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "VibeScribe" in response.text
|
||||
assert "Upload Document" in response.text
|
||||
assert "Select document file" in response.text
|
||||
assert "Upload" in response.text
|
||||
assert "Jobs" in response.text
|
||||
assert response.status_code == 307
|
||||
assert response.headers["location"] == "/ui/jobs/new"
|
||||
|
||||
Reference in New Issue
Block a user