generated from john/python-template
@@ -16,6 +16,7 @@ from transcription.db.migration import export_bundle
|
||||
from transcription.db.migration import import_bundle
|
||||
from transcription.db.migration import migrate_via_bundle
|
||||
from transcription.db.migration import sqlite_url_from_path
|
||||
from transcription.db.migration import verify_migration
|
||||
|
||||
|
||||
def test_export_import_migration_round_trips_db_and_uploads(tmp_path):
|
||||
@@ -339,3 +340,143 @@ def test_import_bundle_does_not_leave_upload_copy_db_as_final_database(tmp_path)
|
||||
assert count == 1
|
||||
finally:
|
||||
target_engine.dispose()
|
||||
|
||||
|
||||
def test_verify_migration_succeeds_for_matching_databases(tmp_path):
|
||||
source_db_url = sqlite_url_from_path(tmp_path / "source.db")
|
||||
target_db_url = sqlite_url_from_path(tmp_path / "target.db")
|
||||
|
||||
source_engine = create_engine(source_db_url)
|
||||
target_engine = create_engine(target_db_url)
|
||||
try:
|
||||
SQLModel.metadata.create_all(source_engine)
|
||||
SQLModel.metadata.create_all(target_engine)
|
||||
finally:
|
||||
source_engine.dispose()
|
||||
target_engine.dispose()
|
||||
|
||||
report = verify_migration(source_db_url=source_db_url, target_db_url=target_db_url)
|
||||
|
||||
assert report.success is True
|
||||
assert report.mismatched_tables == {}
|
||||
assert all(count == 0 for count in report.integrity_violations.values())
|
||||
|
||||
|
||||
def test_verify_migration_detects_count_mismatches(tmp_path):
|
||||
source_db_url = sqlite_url_from_path(tmp_path / "source.db")
|
||||
target_db_url = sqlite_url_from_path(tmp_path / "target.db")
|
||||
document_id = uuid4()
|
||||
|
||||
source_engine = create_engine(source_db_url)
|
||||
target_engine = create_engine(target_db_url)
|
||||
try:
|
||||
SQLModel.metadata.create_all(source_engine)
|
||||
SQLModel.metadata.create_all(target_engine)
|
||||
with source_engine.begin() as connection:
|
||||
connection.execute(
|
||||
SQLModel.metadata.tables["document"].insert(),
|
||||
[{"id": document_id, "name": "Source-only row"}],
|
||||
)
|
||||
finally:
|
||||
source_engine.dispose()
|
||||
target_engine.dispose()
|
||||
|
||||
report = verify_migration(source_db_url=source_db_url, target_db_url=target_db_url)
|
||||
|
||||
assert report.success is False
|
||||
assert report.mismatched_tables["document"] == {"source": 1, "target": 0}
|
||||
|
||||
|
||||
def test_export_import_preserves_source_preferred_attempt_reference(tmp_path):
|
||||
source_db_path = tmp_path / "source-preferred.db"
|
||||
target_db_path = tmp_path / "target-preferred.db"
|
||||
source_upload_dir = tmp_path / "source_uploads"
|
||||
target_upload_dir = tmp_path / "target_uploads"
|
||||
bundle_dir = tmp_path / "bundle-preferred"
|
||||
|
||||
source_db_url = sqlite_url_from_path(source_db_path)
|
||||
target_db_url = sqlite_url_from_path(target_db_path)
|
||||
|
||||
document_id = uuid4()
|
||||
job_id = uuid4()
|
||||
source_id = uuid4()
|
||||
job_source_id = uuid4()
|
||||
attempt_id = uuid4()
|
||||
filename = f"{source_id}.jpg"
|
||||
media_path = source_upload_dir / "documents" / str(document_id) / filename
|
||||
media_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
media_path.write_bytes(b"sample-image")
|
||||
|
||||
source_engine = create_engine(source_db_url)
|
||||
try:
|
||||
SQLModel.metadata.create_all(source_engine)
|
||||
with source_engine.begin() as connection:
|
||||
connection.execute(
|
||||
SQLModel.metadata.tables["document"].insert(),
|
||||
[{"id": document_id, "name": "Preferred attempt doc"}],
|
||||
)
|
||||
connection.execute(
|
||||
SQLModel.metadata.tables["job"].insert(),
|
||||
[{"id": job_id, "document_id": document_id, "status": "queued"}],
|
||||
)
|
||||
connection.execute(
|
||||
SQLModel.metadata.tables["source"].insert(),
|
||||
[
|
||||
{
|
||||
"id": source_id,
|
||||
"document_id": document_id,
|
||||
"page_number": 1,
|
||||
"upload_name": "upload.jpg",
|
||||
"filename": filename,
|
||||
"file_path": str(media_path),
|
||||
"file_hash": "b" * 64,
|
||||
"file_size_bytes": len(b"sample-image"),
|
||||
}
|
||||
],
|
||||
)
|
||||
connection.execute(
|
||||
SQLModel.metadata.tables["job_source"].insert(),
|
||||
[{"id": job_source_id, "job_id": job_id, "source_id": source_id, "status": "pending"}],
|
||||
)
|
||||
connection.execute(
|
||||
SQLModel.metadata.tables["execution_attempt"].insert(),
|
||||
[
|
||||
{
|
||||
"id": attempt_id,
|
||||
"job_source_id": job_source_id,
|
||||
"job_id": job_id,
|
||||
"source_id": source_id,
|
||||
"attempt_number": 1,
|
||||
"status": "transcribed",
|
||||
"provider": "fixture",
|
||||
"model": "fixture-model",
|
||||
"raw_transcription": "hello",
|
||||
"started_at": datetime.now(UTC),
|
||||
"finished_at": datetime.now(UTC),
|
||||
"duration_ms": 10,
|
||||
}
|
||||
],
|
||||
)
|
||||
connection.execute(
|
||||
SQLModel.metadata.tables["source"]
|
||||
.update()
|
||||
.where(SQLModel.metadata.tables["source"].c.id == source_id)
|
||||
.values(preferred_execution_attempt_id=attempt_id),
|
||||
)
|
||||
finally:
|
||||
source_engine.dispose()
|
||||
|
||||
export_bundle(source_db_url=source_db_url, source_upload_dir=source_upload_dir, bundle_dir=bundle_dir)
|
||||
import_bundle(target_db_url=target_db_url, target_upload_dir=target_upload_dir, bundle_dir=bundle_dir)
|
||||
|
||||
target_engine = create_engine(target_db_url)
|
||||
try:
|
||||
with target_engine.connect() as connection:
|
||||
preferred_attempt = connection.execute(
|
||||
select(SQLModel.metadata.tables["source"].c.preferred_execution_attempt_id).where(
|
||||
SQLModel.metadata.tables["source"].c.id == source_id
|
||||
)
|
||||
).scalar_one()
|
||||
assert preferred_attempt == attempt_id
|
||||
finally:
|
||||
target_engine.dispose()
|
||||
|
||||
Reference in New Issue
Block a user