Fix unit test errors

This commit is contained in:
Jim Lancaster
2026-08-05 13:27:30 -05:00
parent fd3ca60008
commit 9219adaf0c
7 changed files with 15 additions and 116 deletions
+1 -68
View File
@@ -1,72 +1,5 @@
"""Shared fixtures for UI integration tests."""
from __future__ import annotations
from collections.abc import AsyncGenerator, Callable
from datetime import UTC, datetime
from pathlib import Path
from typing import Any
from uuid import UUID
import pytest
import pytest_asyncio
from fastapi import FastAPI
from fastapi.testclient import TestClient
from sqlmodel import delete
from transcription.app import create_app
from transcription.config import Settings, SqliteSettings
from transcription.db import create_all, initialize_database_runtime, session_scope
from transcription.db.models import (
Document,
DocumentPerson,
Job,
JobSource,
JobSourceStatus,
JobStatus,
Person,
Source,
)
@pytest.fixture(scope="session")
def app_client(tmp_path_factory: pytest.TempPathFactory) -> AsyncGenerator[tuple[FastAPI, TestClient], None]:
"""Provide a real application and test client backed by in-memory SQLite."""
tmp_path = tmp_path_factory.mktemp("ui")
settings = Settings(
openrouter_api_key="test-key",
database=SqliteSettings(path=":memory:"),
environment="test",
bootstrap_schema_on_startup=True,
upload_dir=tmp_path / "uploads",
prompt_dir=tmp_path / "prompts",
)
app = create_app()
app.state.runtime = initialize_database_runtime(settings=settings)
import asyncio
asyncio.run(create_all(engine=app.state.runtime.engine))
with TestClient(app) as client:
yield app, client
@pytest_asyncio.fixture(autouse=True)
async def clear_ui_database(app_client: tuple[FastAPI, TestClient]) -> None:
"""Reset UI-facing tables asynchronously before each test for isolation."""
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()
@pytest_asyncio.fixture
async def seed_job(app_client: tuple[FastAPI, TestClient]) -> Callable[..., AsyncGenerator[UUID, None]]:
async def seed_job(app_client: tuple[FastAPI, TestClient]):
"""Return an async factory helper for seeding a Document -> Job -> Source tuple."""
app, _ = app_client
fixtures_dir = Path(__file__).resolve().parents[1] / "fixtures" / "images" / "valid"
-10
View File
@@ -28,8 +28,6 @@ class TestDocumentActionHandlers:
"notes": "Belonged to Hig.",
}
response = client.post("/ui/documents/new", data=payload, follow_redirects=True)
assert response.status_code == 200
assert "New Historical Journal" in response.text
@@ -58,8 +56,6 @@ class TestDocumentActionHandlers:
"author_id": person_id,
}
response = client.post("/ui/documents/new", data=payload, follow_redirects=True)
assert response.status_code == 200
assert "Isbill Letter" in response.text
@@ -108,8 +104,6 @@ class TestDocumentActionHandlers:
"author_id": new_author_id,
}
response = client.post(f"/ui/documents/{doc_id}/edit", data=update_payload, follow_redirects=True)
assert response.status_code == 200
assert "Updated Title" in response.text
@@ -140,8 +134,6 @@ class TestDocumentActionHandlers:
await session.commit()
doc_id = str(doc.id)
response = client.post(f"/ui/documents/{doc_id}/delete", follow_redirects=True)
assert response.status_code == 200
assert "Document deleted" in response.text or "Archival Documents" in response.text
@@ -169,8 +161,6 @@ class TestDocumentActionHandlers:
await session.commit()
doc_id = str(doc.id)
response = client.post(f"/ui/documents/{doc_id}/delete", follow_redirects=True)
assert response.status_code == 200
assert "Delete is blocked because related records exist." in response.text
+3 -13
View File
@@ -40,8 +40,6 @@ class TestJobsActionHandlers:
"prompt_name": "default_transcription",
}
response = client.post("/ui/jobs/new", data=data, files=files, follow_redirects=True)
assert response.status_code == 200
assert "Job Record:" in response.text or "Execution Logistics" in response.text
@@ -57,9 +55,7 @@ class TestJobsActionHandlers:
@pytest.mark.asyncio
async def test_cancel_queued_job_success(self, app_client, seed_job):
_, client = app_client
job_id = seed_job(status=JobStatus.QUEUED, filename="queued-job.png")
response = client.post(f"/ui/jobs/{job_id}/cancel", follow_redirects=True)
job_id = await seed_job(status=JobStatus.QUEUED, filename="queued-job.png")
assert response.status_code == 200
assert "Job cancelled" in response.text or "CANCELLED" in response.text or "FAILED" in response.text
@@ -72,15 +68,13 @@ class TestJobsActionHandlers:
@pytest.mark.asyncio
async def test_resubmit_failed_sources_success(self, app_client, seed_job):
_, client = app_client
job_id = seed_job(
job_id = await seed_job(
filename="failed-page.png",
status=JobStatus.FAILED,
transcription_text=None,
error_detail="Provider API timeout",
)
response = client.post(f"/ui/jobs/{job_id}/resubmit", follow_redirects=True)
assert response.status_code == 200
assert "Resubmitted" in response.text or "QUEUED" in response.text
@@ -98,9 +92,7 @@ class TestJobsActionHandlers:
@pytest.mark.asyncio
async def test_delete_queued_or_completed_job_success(self, app_client, seed_job):
_, client = app_client
job_id = seed_job(status=JobStatus.COMPLETED, filename="completed-job.png")
response = client.post(f"/ui/jobs/{job_id}/delete", follow_redirects=True)
job_id = await seed_job(status=JobStatus.COMPLETED, filename="completed-job.png")
assert response.status_code == 200
assert "Job deleted" in response.text or "Transcription Pipeline Jobs" in response.text
@@ -123,8 +115,6 @@ class TestJobsActionHandlers:
await session.commit()
job_id = str(job.id)
response = client.post(f"/ui/jobs/{job_id}/delete", follow_redirects=True)
assert response.status_code == 200
assert "Delete is blocked while the job is processing." in response.text
+2 -2
View File
@@ -47,7 +47,7 @@ class TestJobsPageRendering:
def test_jobs_page_lists_seeded_jobs(self, app_client, seed_job):
_, client = app_client
job_id = seed_job(filename="seeded-document-page.png")
job_id = await seed_job(filename="seeded-document-page.png")
response = client.get("/ui/jobs")
@@ -117,7 +117,7 @@ class TestJobsPageRendering:
self, app_client, seed_job
):
_, client = app_client
job_id = seed_job(
job_id = await seed_job(
filename="failed-resubmit.png",
status=JobStatus.FAILED,
transcription_text=None,
-10
View File
@@ -28,8 +28,6 @@ class TestPeopleActionHandlers:
"biography": "Editor and scholar in documentary editing.",
}
response = client.post("/ui/people/new", data=payload, follow_redirects=True)
assert response.status_code == 200
assert "Mary-Jo Kline" in response.text
@@ -51,8 +49,6 @@ class TestPeopleActionHandlers:
"display_name": "Anonymous",
}
response = client.post("/ui/people/new", data=payload, follow_redirects=True)
assert response.status_code == 200
assert "Full name is required." in response.text
@@ -74,8 +70,6 @@ class TestPeopleActionHandlers:
"biography": "Updated archival biographical information.",
}
response = client.post(f"/ui/people/{person_id}/edit", data=update_payload, follow_redirects=True)
assert response.status_code == 200
assert "Updated Person Name" in response.text
@@ -97,8 +91,6 @@ class TestPeopleActionHandlers:
await session.commit()
person_id = str(person.id)
response = client.post(f"/ui/people/{person_id}/delete", follow_redirects=True)
assert response.status_code == 200
assert "Person deleted" in response.text or "Archival Entities: People" in response.text
@@ -126,8 +118,6 @@ class TestPeopleActionHandlers:
person_id = str(person.id)
doc_id = str(doc.id)
response = client.post(f"/ui/people/{person_id}/delete", follow_redirects=True)
assert response.status_code == 200
async with session_scope() as session:
+2 -8
View File
@@ -16,7 +16,7 @@ class TestSourcesActionHandlers:
@pytest.mark.asyncio
async def test_upsert_revision_for_source_success(self, app_client, seed_job):
_, client = app_client
job_id = seed_job(
job_id = await seed_job(
filename="revision-source.png",
transcription_text="automated raw transcription text",
)
@@ -34,8 +34,6 @@ class TestSourcesActionHandlers:
"revised_text": "Curated human transcription text by editor.",
}
response = client.post(f"/ui/sources/{source_id}", data=payload, follow_redirects=True)
assert response.status_code == 200
assert "Revision saved" in response.text or "Curated human transcription text by editor." in response.text
@@ -65,8 +63,6 @@ class TestSourcesActionHandlers:
await session.commit()
source_id = str(source.id)
response = client.post(f"/ui/sources/{source_id}/delete", follow_redirects=True)
assert response.status_code == 200
assert "Source deleted" in response.text or "Archival Source Media" in response.text
@@ -77,7 +73,7 @@ class TestSourcesActionHandlers:
@pytest.mark.asyncio
async def test_delete_source_blocked_when_job_linked(self, app_client, seed_job):
_, client = app_client
job_id = seed_job(filename="job-linked-source.png", transcription_text="job text")
job_id = await seed_job(filename="job-linked-source.png", transcription_text="job text")
async with session_scope() as session:
job = await session.get(Job, job_id)
@@ -88,8 +84,6 @@ class TestSourcesActionHandlers:
assert source is not None
source_id = str(source.id)
response = client.post(f"/ui/sources/{source_id}/delete", follow_redirects=True)
assert response.status_code == 200
assert "Delete is only available for unlinked sources." in response.text or "linked" in response.text.lower()
+7 -5
View File
@@ -32,7 +32,7 @@ class TestSourceModelProperties:
@pytest.mark.asyncio
async def test_source_properties_with_document_and_job_sources(self, seed_job):
job_id = seed_job(
job_id = await seed_job(
filename="source_prop_test.png",
status=JobStatus.FAILED,
transcription_text=None,
@@ -134,9 +134,10 @@ class TestSourcesPageRendering:
assert "target_page.png" in response.text
assert "other_page.png" not in response.text
@pytest.mark.asyncio
def test_sources_page_filters_to_job_context(self, app_client, seed_job):
_, client = app_client
job_id = seed_job(filename="job-page.png", transcription_text="job text")
job_id = await seed_job(filename="job-page.png", transcription_text="job text")
response = client.get(f"/ui/sources?job_id={job_id}")
@@ -145,11 +146,12 @@ class TestSourcesPageRendering:
assert "Back to Job" in response.text
assert "job-page.png" in response.text
@pytest.mark.asyncio
def test_sources_page_job_context_shows_job_source_status_and_error_detail(
self, app_client, seed_job
):
_, client = app_client
job_id = seed_job(
job_id = await seed_job(
filename="job-failed-page.png",
status=JobStatus.FAILED,
transcription_text=None,
@@ -175,7 +177,7 @@ class TestSourcesPageRendering:
/ "valid"
/ "small_png.png"
)
job_id = seed_job(
job_id = await seed_job(
filename="detail-source.png",
transcription_text="original transcription text",
revision_text="human revision text",
@@ -205,7 +207,7 @@ class TestSourcesPageRendering:
self, app_client, seed_job
):
_, client = app_client
job_id = seed_job(filename="linked-source.png", transcription_text="linked text")
job_id = await seed_job(filename="linked-source.png", transcription_text="linked text")
async with session_scope() as session:
job = await session.get(Job, job_id)