Files
transcription/tests/ui/test_jobs_page.py
T
Jim Lancaster 9a7970c533
Quality Gate / gate (push) Failing after 49s
UI refinement: Back buttons
2026-08-24 12:44:02 -05:00

214 lines
7.7 KiB
Python

"""Tests for the jobs page routes and action handlers."""
import pytest
import pytest_asyncio
from transcription.db import session_scope
from transcription.db.models import Document
from transcription.db.models import Job
from transcription.db.models import JobStatus
# --- Helper Fixtures ---
@pytest_asyncio.fixture
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")
session.add(document)
await session.flush()
job = Job(
document_id=document.id,
status=JobStatus.QUEUED,
provider="openai",
model="gpt-4o",
)
session.add(job)
await session.commit()
return str(document.id), str(job.id)
# --- Integration Tests for Jobs Route Handlers ---
@pytest.mark.integration
class TestJobsPageRendering:
"""Verify jobs list, creation, detail, and lifecycle action routes."""
def test_jobs_page_renders_empty_state(self, app_client):
_, client = app_client
response = client.get("/ui/jobs")
assert response.status_code == 200
assert "Transcription Pipeline Jobs" in response.text
assert "No job records found in repository." in response.text
@pytest.mark.asyncio
async def test_jobs_page_lists_seeded_jobs(self, app_client, seed_job):
_, client = app_client
job_id = await seed_job(filename="seeded-document-page.png")
response = client.get("/ui/jobs")
assert response.status_code == 200
assert "Document Name" in response.text
assert "# Sources" in response.text
assert "Source Filename" not in response.text
assert "Updated" in response.text
assert "Created" not in response.text
assert str(job_id) in response.text
@pytest.mark.asyncio
async def test_jobs_page_filters_for_document_context(self, app_client):
_, client = app_client
async with session_scope() as session:
first = Document(name="First Doc")
second = Document(name="Second Doc")
session.add_all([first, second])
await session.flush()
first_job = Job(document_id=first.id, status=JobStatus.QUEUED, provider="openai", model="gpt-4o")
second_job = Job(document_id=second.id, status=JobStatus.QUEUED, provider="openai", model="gpt-4o")
session.add_all([first_job, second_job])
await session.commit()
first_id = str(first.id)
first_job_id = str(first_job.id)
second_job_id = str(second_job.id)
response = client.get(f"/ui/jobs?document_id={first_id}")
assert response.status_code == 200
assert "Jobs for Document" in response.text
assert "Create job" not in response.text
assert "Refresh" not in response.text
assert "Back to Document" in response.text
assert first_job_id in response.text
assert second_job_id not in response.text
@pytest.mark.asyncio
async def test_job_create_page_shows_empty_document_warning_when_no_docs(self, app_client):
_, client = app_client
response = client.get("/ui/jobs/new")
assert response.status_code == 200
assert "Create Processing Job" in response.text
assert "No documents available. Create a Document before creating a Job." in response.text
@pytest.mark.asyncio
async def test_job_create_page_preselects_document_from_query_param(self, app_client):
_, client = app_client
async with session_scope() as session:
doc = Document(name="Preselected Journal Entry")
session.add(doc)
await session.commit()
doc_id = str(doc.id)
response = client.get(f"/ui/jobs/new?document_id={doc_id}")
assert response.status_code == 200
assert "Create Processing Job" in response.text
assert "Preselected Journal Entry" in response.text
assert "Provider" in response.text
assert "Model" in response.text
@pytest.mark.asyncio
async def test_job_detail_page_renders_logistics_and_links(self, app_client, seed_document_with_unlinked_job):
_, client = app_client
_, job_id = seed_document_with_unlinked_job
response = client.get(f"/ui/jobs/{job_id}")
assert response.status_code == 200
assert f"Job Record: {job_id}" in response.text
assert "Execution Logistics".upper() in response.text.upper()
assert "openai" in response.text
assert "gpt-4o" in response.text
assert "Document Name:" in response.text
assert "Sources:" in response.text
assert "View Sources" in response.text
assert "Back to Jobs" in response.text
assert "View Linked Document" not in response.text
assert "View Linked Sources" not in response.text
assert "updates automatically while the job is active" in response.text
@pytest.mark.asyncio
async def test_job_detail_page_renders_document_context_back_button(
self,
app_client,
seed_document_with_unlinked_job,
):
_, client = app_client
document_id, job_id = seed_document_with_unlinked_job
response = client.get(f"/ui/jobs/{job_id}?from=document&document_id={document_id}")
assert response.status_code == 200
assert "Back to Document" in response.text
@pytest.mark.asyncio
async def test_job_cancel_page_renders_confirmation(self, app_client, seed_document_with_unlinked_job):
_, client = app_client
_, job_id = seed_document_with_unlinked_job
response = client.get(f"/ui/jobs/{job_id}/cancel")
assert response.status_code == 200
assert "Cancel Processing Job" in response.text
assert "Cancel stops processing" in response.text
assert "Cancel job" in response.text
@pytest.mark.asyncio
async def test_job_resubmit_page_renders_counts(self, app_client, seed_job):
_, client = app_client
job_id = await seed_job(
filename="failed-resubmit.png",
status=JobStatus.FAILED,
transcription_text=None,
error_detail="Timeout",
)
response = client.get(f"/ui/jobs/{job_id}/resubmit")
assert response.status_code == 200
assert "Resubmit Job" in response.text
assert "Resubmittable Sources:" in response.text
assert "Resubmit now" in response.text
@pytest.mark.asyncio
async def test_job_delete_page_blocks_deletion_when_processing(self, app_client):
_, client = app_client
async with session_scope() as session:
doc = Document(name="Processing Doc")
session.add(doc)
await session.flush()
job = Job(document_id=doc.id, status=JobStatus.PROCESSING)
session.add(job)
await session.commit()
job_id = str(job.id)
response = client.get(f"/ui/jobs/{job_id}/delete")
assert response.status_code == 200
assert "Delete Processing Job" in response.text
assert "Delete is blocked while the job is processing." in response.text
@pytest.mark.asyncio
async def test_job_delete_page_allows_deletion_for_queued_job(self, app_client, seed_document_with_unlinked_job):
_, client = app_client
_, job_id = seed_document_with_unlinked_job
response = client.get(f"/ui/jobs/{job_id}/delete")
assert response.status_code == 200
assert "Delete Processing Job" in response.text
assert "Delete job and evidence" in response.text
assert "immutable execution evidence" in response.text
assert "Delete is blocked" not in response.text