generated from john/python-template
V3 step 1 update models.py and step 2 implement service/worker, and raw API response persistence
This commit is contained in:
@@ -78,6 +78,8 @@ async def test_delete_document_blocks_when_dependencies_exist(default_session_fa
|
||||
upload_name="001_page.png",
|
||||
filename="001_page.png",
|
||||
file_path="uploads/001_page.png",
|
||||
file_hash="a" * 64,
|
||||
file_size_bytes=1,
|
||||
)
|
||||
)
|
||||
session.add(Job(document_id=document.id))
|
||||
|
||||
@@ -80,6 +80,8 @@ class TestJobService:
|
||||
upload_name="letter.jpg",
|
||||
filename="stored-letter.jpg",
|
||||
file_path="/uploads/stored-letter.jpg",
|
||||
file_hash="a" * 64,
|
||||
file_size_bytes=1,
|
||||
)
|
||||
session.add(source)
|
||||
await session.flush()
|
||||
@@ -120,7 +122,7 @@ class TestJobService:
|
||||
assert next_job.id == first.id
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_job_persists_provider_model_prompt(
|
||||
async def test_create_job_persists_provider_and_model(
|
||||
self,
|
||||
job_service: JobService,
|
||||
document_service: DocumentService,
|
||||
@@ -132,14 +134,12 @@ class TestJobService:
|
||||
document_id=document.id,
|
||||
provider="openrouter",
|
||||
model="google/gemini-2.5-flash",
|
||||
prompt_name="transcribe_document.md",
|
||||
)
|
||||
await job_service.create_job(job=job)
|
||||
|
||||
fetched = await job_service.read_job(job_id=job.id)
|
||||
assert fetched.provider == "openrouter"
|
||||
assert fetched.model == "google/gemini-2.5-flash"
|
||||
assert fetched.prompt_name == "transcribe_document.md"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_read_job_resolves_filename_from_linked_source(
|
||||
@@ -160,6 +160,8 @@ class TestJobService:
|
||||
upload_name="page_001.png",
|
||||
filename="stored_page_001.png",
|
||||
file_path="/uploads/stored_page_001.png",
|
||||
file_hash="b" * 64,
|
||||
file_size_bytes=1,
|
||||
)
|
||||
session.add(source)
|
||||
await session.flush()
|
||||
@@ -210,6 +212,8 @@ class TestJobService:
|
||||
upload_name="delete-job-source.jpg",
|
||||
filename="stored-delete-job-source.jpg",
|
||||
file_path="/uploads/stored-delete-job-source.jpg",
|
||||
file_hash="c" * 64,
|
||||
file_size_bytes=1,
|
||||
)
|
||||
session.add(source)
|
||||
await session.flush()
|
||||
@@ -246,6 +250,8 @@ class TestJobService:
|
||||
upload_name="cancel-1.jpg",
|
||||
filename="stored-cancel-1.jpg",
|
||||
file_path="/uploads/stored-cancel-1.jpg",
|
||||
file_hash="d" * 64,
|
||||
file_size_bytes=1,
|
||||
)
|
||||
source_two = Source(
|
||||
document_id=document.id,
|
||||
@@ -253,6 +259,8 @@ class TestJobService:
|
||||
upload_name="cancel-2.jpg",
|
||||
filename="stored-cancel-2.jpg",
|
||||
file_path="/uploads/stored-cancel-2.jpg",
|
||||
file_hash="e" * 64,
|
||||
file_size_bytes=1,
|
||||
)
|
||||
session.add(source_one)
|
||||
session.add(source_two)
|
||||
@@ -304,6 +312,8 @@ class TestJobService:
|
||||
upload_name="resubmit-1.jpg",
|
||||
filename="stored-resubmit-1.jpg",
|
||||
file_path="/uploads/stored-resubmit-1.jpg",
|
||||
file_hash="f" * 64,
|
||||
file_size_bytes=1,
|
||||
raw_transcription="existing text",
|
||||
)
|
||||
source_two = Source(
|
||||
@@ -312,6 +322,8 @@ class TestJobService:
|
||||
upload_name="resubmit-2.jpg",
|
||||
filename="stored-resubmit-2.jpg",
|
||||
file_path="/uploads/stored-resubmit-2.jpg",
|
||||
file_hash="0" * 64,
|
||||
file_size_bytes=1,
|
||||
raw_transcription="done text",
|
||||
)
|
||||
session.add(source_one)
|
||||
|
||||
@@ -48,7 +48,6 @@ async def test_create_job_for_document_sorts_uploads_and_creates_links(async_ses
|
||||
],
|
||||
provider="openrouter",
|
||||
model="test-model",
|
||||
prompt_name="transcribe_document.md",
|
||||
session=async_session,
|
||||
settings=settings,
|
||||
)
|
||||
@@ -57,7 +56,6 @@ async def test_create_job_for_document_sorts_uploads_and_creates_links(async_ses
|
||||
assert created_job is not None
|
||||
assert created_job.provider == "openrouter"
|
||||
assert created_job.model == "test-model"
|
||||
assert created_job.prompt_name == "transcribe_document.md"
|
||||
|
||||
sources = (
|
||||
await async_session.exec(
|
||||
@@ -71,10 +69,16 @@ async def test_create_job_for_document_sorts_uploads_and_creates_links(async_ses
|
||||
assert all("A_page" not in source.filename and "b_page" not in source.filename for source in sources)
|
||||
assert all(Path(source.filename).stem == str(source.id) for source in sources)
|
||||
assert all(Path(source.file_path).parent == (tmp_path / "documents" / str(document.id)) for source in sources)
|
||||
assert [source.file_hash for source in sources] == [
|
||||
"ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb",
|
||||
"3e23e8160039594a33894f6564e1b1348bbd7a0088d42c4acb73eeaed59c009d",
|
||||
]
|
||||
assert [source.file_size_bytes for source in sources] == [1, 1]
|
||||
|
||||
job_sources = (await async_session.exec(select(JobSource).where(JobSource.job_id == result.job_id))).all()
|
||||
assert len(job_sources) == 2
|
||||
assert set(result.source_ids) == {job_source.source_id for job_source in job_sources}
|
||||
assert {job_source.prompt_name for job_source in job_sources} == {None}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -103,6 +107,8 @@ async def test_create_upload_job_stores_source_under_document_id_directory(async
|
||||
assert Path(source.filename).stem == str(source.id)
|
||||
assert result.stored_path.name == source.filename
|
||||
assert Path(source.file_path).parent == expected_parent
|
||||
assert source.file_hash == "2c8648d103e3dd7ad87660da0f126a1443b6d21ac1bd3ec000c5e24e2373a90c"
|
||||
assert source.file_size_bytes == len(b"image-bytes")
|
||||
|
||||
|
||||
def test_store_person_portrait_stores_file_under_person_id_directory(tmp_path):
|
||||
|
||||
@@ -39,6 +39,8 @@ class TestTranscriptionServiceRevisionUpsert:
|
||||
upload_name="source.jpg",
|
||||
filename="source.jpg",
|
||||
file_path="uploads/source.jpg",
|
||||
file_hash="1" * 64,
|
||||
file_size_bytes=1,
|
||||
)
|
||||
async with transcriptions._session_scope() as session:
|
||||
session.add(source)
|
||||
@@ -74,6 +76,8 @@ class TestTranscriptionServiceRevisionUpsert:
|
||||
upload_name="source.jpg",
|
||||
filename="source.jpg",
|
||||
file_path="uploads/source.jpg",
|
||||
file_hash="2" * 64,
|
||||
file_size_bytes=1,
|
||||
)
|
||||
async with transcriptions._session_scope() as session:
|
||||
session.add(source)
|
||||
@@ -115,6 +119,8 @@ class TestTranscriptionServiceRevisionUpsert:
|
||||
upload_name="delete.jpg",
|
||||
filename="delete.jpg",
|
||||
file_path=str(stored_path),
|
||||
file_hash="3" * 64,
|
||||
file_size_bytes=4,
|
||||
)
|
||||
async with transcriptions._session_scope() as session:
|
||||
session.add(source)
|
||||
@@ -149,6 +155,8 @@ class TestTranscriptionServiceRevisionUpsert:
|
||||
upload_name="shared.jpg",
|
||||
filename="shared.jpg",
|
||||
file_path="uploads/shared.jpg",
|
||||
file_hash="4" * 64,
|
||||
file_size_bytes=1,
|
||||
)
|
||||
async with transcriptions._session_scope() as session:
|
||||
session.add(source)
|
||||
@@ -180,6 +188,8 @@ class TestTranscriptionServiceRevisionUpsert:
|
||||
upload_name="orphan.jpg",
|
||||
filename="orphan.jpg",
|
||||
file_path=str(stored_path),
|
||||
file_hash="5" * 64,
|
||||
file_size_bytes=4,
|
||||
)
|
||||
await transcriptions.create_source(source=source)
|
||||
|
||||
@@ -207,6 +217,8 @@ class TestTranscriptionServiceRevisionUpsert:
|
||||
upload_name="linked.jpg",
|
||||
filename="linked.jpg",
|
||||
file_path="uploads/linked.jpg",
|
||||
file_hash="6" * 64,
|
||||
file_size_bytes=1,
|
||||
)
|
||||
async with transcriptions._session_scope() as session:
|
||||
session.add(source)
|
||||
|
||||
@@ -60,6 +60,8 @@ async def test_transcription_service_manages_source_crud(default_session_factory
|
||||
upload_name="page-1.jpg",
|
||||
filename="page-1.jpg",
|
||||
file_path="uploads/page-1.jpg",
|
||||
file_hash="7" * 64,
|
||||
file_size_bytes=1,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -101,6 +103,8 @@ async def test_transcription_service_job_source_crud_uses_caller_session(default
|
||||
upload_name="job-source.jpg",
|
||||
filename="job-source.jpg",
|
||||
file_path="uploads/job-source.jpg",
|
||||
file_hash="8" * 64,
|
||||
file_size_bytes=1,
|
||||
)
|
||||
session.add(source)
|
||||
await session.flush()
|
||||
@@ -165,6 +169,8 @@ async def test_document_delete_is_blocked_with_source_and_job_dependencies(defau
|
||||
upload_name="blocked.jpg",
|
||||
filename="blocked.jpg",
|
||||
file_path="uploads/blocked.jpg",
|
||||
file_hash="9" * 64,
|
||||
file_size_bytes=1,
|
||||
)
|
||||
)
|
||||
await transcriptions.create_job_source(
|
||||
@@ -200,6 +206,8 @@ async def test_source_delete_blocks_when_linked_to_multiple_jobs(default_session
|
||||
upload_name="shared-page.jpg",
|
||||
filename="shared-page.jpg",
|
||||
file_path="uploads/shared-page.jpg",
|
||||
file_hash="a" * 64,
|
||||
file_size_bytes=1,
|
||||
)
|
||||
)
|
||||
await transcriptions.create_job_source(
|
||||
@@ -228,6 +236,8 @@ async def test_update_job_source_transcription_persists_provider_json_payloads(d
|
||||
upload_name="provider.jpg",
|
||||
filename="provider.jpg",
|
||||
file_path="uploads/provider.jpg",
|
||||
file_hash="b" * 64,
|
||||
file_size_bytes=1,
|
||||
)
|
||||
)
|
||||
await transcriptions.create_job_source(
|
||||
@@ -251,5 +261,6 @@ async def test_update_job_source_transcription_persists_provider_json_payloads(d
|
||||
stored_rows = await transcriptions.list_job_sources(job_id=job.id)
|
||||
assert len(stored_rows) == 1
|
||||
assert stored_rows[0].raw_transcription == "provider transcript"
|
||||
assert stored_rows[0].prompt_name == "transcribe_document.md"
|
||||
assert stored_rows[0].ai_metadata == metadata
|
||||
assert stored_rows[0].raw_api_response == raw_payload
|
||||
|
||||
@@ -8,6 +8,8 @@ import pytest
|
||||
from transcription.config import Settings
|
||||
from transcription.db.models import Document
|
||||
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 Source
|
||||
from transcription.services import ServiceBundle
|
||||
@@ -49,12 +51,16 @@ class TestWorkflowReliability:
|
||||
|
||||
source = Source(
|
||||
document_id=document.id,
|
||||
job_id=job.id,
|
||||
page_number=1,
|
||||
upload_name="timeout.jpg",
|
||||
filename="timeout.jpg",
|
||||
file_path=str(Path("tests/fixtures/images/real/Book Two - page 02.jpg")),
|
||||
file_hash="c" * 64,
|
||||
file_size_bytes=1,
|
||||
)
|
||||
session.add(source)
|
||||
await session.flush()
|
||||
session.add(JobSource(job_id=job.id, source_id=source.id, status=JobSourceStatus.PENDING))
|
||||
await session.commit()
|
||||
|
||||
loaded = await services.jobs.read_job(job_id=job.id, session=session)
|
||||
|
||||
Reference in New Issue
Block a user