diff --git a/tests/ui/conftest.py b/tests/ui/conftest.py index 7256c9a..4966504 100644 --- a/tests/ui/conftest.py +++ b/tests/ui/conftest.py @@ -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" diff --git a/tests/ui/test_documents_actions.py b/tests/ui/test_documents_actions.py index 998e641..372694f 100644 --- a/tests/ui/test_documents_actions.py +++ b/tests/ui/test_documents_actions.py @@ -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 diff --git a/tests/ui/test_jobs_actions.py b/tests/ui/test_jobs_actions.py index 342fe2f..61e0d76 100644 --- a/tests/ui/test_jobs_actions.py +++ b/tests/ui/test_jobs_actions.py @@ -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 diff --git a/tests/ui/test_jobs_page.py b/tests/ui/test_jobs_page.py index 72a32b4..3e59f75 100644 --- a/tests/ui/test_jobs_page.py +++ b/tests/ui/test_jobs_page.py @@ -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, diff --git a/tests/ui/test_people_actions.py b/tests/ui/test_people_actions.py index 20bc59c..2e0e30c 100644 --- a/tests/ui/test_people_actions.py +++ b/tests/ui/test_people_actions.py @@ -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: diff --git a/tests/ui/test_sources_actions.py b/tests/ui/test_sources_actions.py index 3cef0dc..80dac3f 100644 --- a/tests/ui/test_sources_actions.py +++ b/tests/ui/test_sources_actions.py @@ -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() diff --git a/tests/ui/test_sources_page.py b/tests/ui/test_sources_page.py index 6665195..14508a6 100644 --- a/tests/ui/test_sources_page.py +++ b/tests/ui/test_sources_page.py @@ -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)