generated from john/python-template
Revamped the Documents, People, & Jobs too.
This commit is contained in:
+78
-87
@@ -2,39 +2,35 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Callable
|
||||
from collections.abc import Generator
|
||||
from datetime import UTC
|
||||
from datetime import datetime
|
||||
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
|
||||
from transcription.config import SqliteSettings
|
||||
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
|
||||
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) -> Generator[tuple[FastAPI, TestClient]]:
|
||||
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(
|
||||
@@ -48,94 +44,89 @@ def app_client(tmp_path_factory: pytest.TempPathFactory) -> Generator[tuple[Fast
|
||||
|
||||
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.fixture(autouse=True)
|
||||
def clear_ui_database(app_client: tuple[FastAPI, TestClient]) -> None:
|
||||
"""Reset UI-facing tables before each test for isolation."""
|
||||
app, _ = app_client
|
||||
|
||||
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())
|
||||
@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.fixture
|
||||
def seed_job(app_client: tuple[FastAPI, TestClient]) -> Callable[..., UUID]:
|
||||
"""Return a helper for inserting a document/job/source/(optional revision) tuple."""
|
||||
@pytest_asyncio.fixture
|
||||
async def seed_job(app_client: tuple[FastAPI, TestClient]) -> Callable[..., AsyncGenerator[UUID, None]]:
|
||||
"""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"
|
||||
|
||||
def _seed(
|
||||
async def _seed(
|
||||
*,
|
||||
filename: str = "sample.pdf",
|
||||
status: JobStatus = JobStatus.TRANSCRIBED,
|
||||
transcription_text: str | None = "Sample transcript text",
|
||||
error_detail: str | None = None,
|
||||
revision_text: RevisionSeed | None = None,
|
||||
revision_text: str | None = None,
|
||||
source_file: Path | None = None,
|
||||
) -> UUID:
|
||||
async def _insert() -> UUID:
|
||||
async with session_scope() as session:
|
||||
stored_path = app.state.settings.upload_dir / filename
|
||||
stored_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
source_path = source_file or fixtures_dir / "small_png.png"
|
||||
stored_path.write_bytes(source_path.read_bytes())
|
||||
async with session_scope() as session:
|
||||
stored_path = app.state.settings.upload_dir / filename
|
||||
stored_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
source_path = source_file or fixtures_dir / "small_png.png"
|
||||
stored_path.write_bytes(source_path.read_bytes())
|
||||
|
||||
document = Document(name=filename)
|
||||
session.add(document)
|
||||
await session.flush()
|
||||
document = Document(name=filename)
|
||||
session.add(document)
|
||||
await session.flush()
|
||||
|
||||
job = Job(
|
||||
document_id=document.id,
|
||||
status=status,
|
||||
retry_count=0,
|
||||
provider="openrouter",
|
||||
model="google/gemini-2.5-flash",
|
||||
prompt_name="transcribe_document.md",
|
||||
)
|
||||
session.add(job)
|
||||
await session.flush()
|
||||
job = Job(
|
||||
document_id=document.id,
|
||||
status=status,
|
||||
retry_count=0,
|
||||
provider="openrouter",
|
||||
model="google/gemini-2.5-flash",
|
||||
prompt_name="transcribe_document.md",
|
||||
)
|
||||
session.add(job)
|
||||
await session.flush()
|
||||
|
||||
source = Source(
|
||||
document_id=document.id,
|
||||
upload_name=filename,
|
||||
filename=filename,
|
||||
file_path=str(stored_path),
|
||||
)
|
||||
session.add(source)
|
||||
await session.flush()
|
||||
source = Source(
|
||||
document_id=document.id,
|
||||
upload_name=filename,
|
||||
filename=filename,
|
||||
file_path=str(stored_path),
|
||||
)
|
||||
session.add(source)
|
||||
await session.flush()
|
||||
|
||||
if transcription_text is not None or error_detail is not None:
|
||||
session.add(
|
||||
JobSource(
|
||||
job_id=job.id,
|
||||
source_id=source.id,
|
||||
status=JobSourceStatus.TRANSCRIBED if transcription_text is not None else JobSourceStatus.FAILED,
|
||||
raw_transcription=transcription_text,
|
||||
error_detail=error_detail,
|
||||
)
|
||||
if transcription_text is not None or error_detail is not None:
|
||||
session.add(
|
||||
JobSource(
|
||||
job_id=job.id,
|
||||
source_id=source.id,
|
||||
status=JobSourceStatus.TRANSCRIBED if transcription_text is not None else JobSourceStatus.FAILED,
|
||||
raw_transcription=transcription_text,
|
||||
error_detail=error_detail,
|
||||
)
|
||||
)
|
||||
|
||||
if revision_text is not None:
|
||||
source.revised_text = revision_text
|
||||
source.date_revised = datetime.now(UTC)
|
||||
session.add(source)
|
||||
if revision_text is not None:
|
||||
source.revised_text = revision_text
|
||||
source.date_revised = datetime.now(UTC)
|
||||
session.add(source)
|
||||
|
||||
await session.commit()
|
||||
return job.id
|
||||
await session.commit()
|
||||
return job.id
|
||||
|
||||
return asyncio.run(_insert())
|
||||
|
||||
return _seed
|
||||
return _seed
|
||||
Reference in New Issue
Block a user