V4.10
Quality Gate / gate (push) Failing after 11s

This commit is contained in:
Jim Lancaster
2026-08-22 10:19:30 -05:00
parent bf2f3ac09c
commit cf49c3c127
35 changed files with 1029 additions and 161 deletions
+5 -4
View File
@@ -131,7 +131,7 @@ class TestDocumentsPageRendering:
assert "Edit Document" in response.text
@pytest.mark.asyncio
async def test_document_jobs_page_renders_job_links(self, app_client):
async def test_document_jobs_page_redirects_to_filtered_jobs(self, app_client):
_, client = app_client
async with session_scope() as session:
@@ -143,13 +143,14 @@ class TestDocumentsPageRendering:
session.add(job)
await session.commit()
doc_id = str(doc.id)
job_id = str(job.id)
_ = str(job.id)
response = client.get(f"/ui/documents/{doc_id}/jobs")
assert response.status_code == 200
assert "Jobs for Doc With Job" in response.text
assert f"Job ID: {job_id}" in response.text
assert "Jobs for Document" in response.text
assert "Create job" not in response.text
assert "Refresh" not in response.text
@pytest.mark.asyncio
async def test_document_edit_page_prefills_existing_values(self, app_client, seed_person_and_document):
+7
View File
@@ -3,6 +3,7 @@ from datetime import date
from transcription.db.models import Person
from transcription.ui.components.formatters import compact_date
from transcription.ui.components.formatters import family_search_url
from transcription.ui.components.formatters import google_maps_search_url
from transcription.ui.components.formatters import person_selector_label
@@ -32,3 +33,9 @@ def test_family_search_url_uses_fixed_person_details_route():
assert family_search_url("G8T4-MDQ") == (
"https://www.familysearch.org/tree/person/details/G8T4-MDQ"
)
def test_google_maps_search_url_encodes_place_query():
assert google_maps_search_url("New York, NY") == (
"https://www.google.com/maps/search/?api=1&query=New+York%2C+NY"
)
+36 -3
View File
@@ -54,8 +54,38 @@ class TestJobsPageRendering:
response = client.get("/ui/jobs")
assert response.status_code == 200
assert "Document Name" 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
assert "seeded-document-page.png" 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 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):
@@ -95,8 +125,11 @@ class TestJobsPageRendering:
assert "Execution Logistics".upper() in response.text.upper()
assert "openai" in response.text
assert "gpt-4o" in response.text
assert "View Linked Document" in response.text
assert "View Linked Sources" in response.text
assert "Document Name:" in response.text
assert "Sources:" in response.text
assert "View Sources" 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
+5
View File
@@ -24,3 +24,8 @@ class TestPageRegistration:
assert sources_response.status_code == 200
assert jobs_response.status_code == 200
assert settings_response.status_code == 200
assert "Document Types" in settings_response.text
assert "Person Roles" in settings_response.text
assert "Prompts" in settings_response.text
assert "Home Page Text" in settings_response.text
assert "README.md" not in settings_response.text
+52 -2
View File
@@ -12,6 +12,7 @@ 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
@@ -55,6 +56,9 @@ class TestPeoplePageRendering:
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
@@ -92,11 +96,16 @@ class TestPeoplePageRendering:
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" 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
@@ -134,6 +143,28 @@ class TestPeoplePageRendering:
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(
@@ -148,8 +179,27 @@ class TestPeoplePageRendering:
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 "Role: Author" 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
+48
View File
@@ -1,5 +1,7 @@
"""Tests for the sources page routes and Source model properties."""
from datetime import UTC
from datetime import datetime
from pathlib import Path
from uuid import uuid4
@@ -10,6 +12,7 @@ from transcription.db import session_scope
from transcription.db.loading import orm_attribute
from transcription.db.loading import selectinload
from transcription.db.models import Document
from transcription.db.models import ExecutionAttempt
from transcription.db.models import Job
from transcription.db.models import JobSource
from transcription.db.models import JobSourceStatus
@@ -71,6 +74,51 @@ class TestSourceModelProperties:
assert source.latest_error_detail == "Timeout during OCR parsing"
assert source.latest_job_source is not None
@pytest.mark.asyncio
async def test_latest_error_detail_ignores_older_failures_when_latest_attempt_succeeds(self):
source = Source(
document_id=uuid4(),
page_number=1,
upload_name="page_one.png",
filename="stored_page_one.png",
file_path="/tmp/stored_page_one.png",
file_hash="b" * 64,
file_size_bytes=1,
)
job_source = JobSource(
job_id=uuid4(),
source_id=source.id,
status=JobSourceStatus.TRANSCRIBED,
)
failed_attempt = ExecutionAttempt(
job_source_id=job_source.id,
job_id=job_source.job_id,
source_id=source.id,
attempt_number=1,
status=JobSourceStatus.FAILED,
provider="openrouter",
error_detail="Provider timeout",
started_at=datetime.now(UTC),
finished_at=datetime.now(UTC),
duration_ms=10,
)
successful_attempt = ExecutionAttempt(
job_source_id=job_source.id,
job_id=job_source.job_id,
source_id=source.id,
attempt_number=2,
status=JobSourceStatus.TRANSCRIBED,
provider="openrouter",
error_detail=None,
started_at=datetime.now(UTC),
finished_at=datetime.now(UTC),
duration_ms=10,
)
job_source.execution_attempts = [failed_attempt, successful_attempt]
source.job_sources = [job_source]
assert source.latest_error_detail is None
# --- Integration Tests for Page Rendering ---