"""Tests for maintenance run persistence and execution lifecycle.""" from __future__ import annotations from pathlib import Path import pytest from sqlalchemy import text from transcription.config import Settings from transcription.db.models import MaintenanceJobType from transcription.db.models import MaintenanceRunStatus from transcription.errors import ErrorCategory from transcription.services.maintenance import MaintenanceError from transcription.services.maintenance import MaintenanceExecution from transcription.services.maintenance import MaintenanceService @pytest.mark.asyncio async def test_enqueue_and_list_runs(default_session_factory, default_settings): service = MaintenanceService(session_factory=default_session_factory, settings=default_settings) first = await service.enqueue_run(job_type=MaintenanceJobType.BACKUP, triggered_by="test") second = await service.enqueue_run(job_type=MaintenanceJobType.STORAGE_RECONCILIATION, triggered_by="test") third = await service.enqueue_run(job_type=MaintenanceJobType.GEDCOM_IMPORT, triggered_by="test") runs = await service.list_runs(limit=10) assert len(runs) == 3 assert runs[0].id == third.id assert runs[1].id == second.id assert runs[2].id == first.id assert runs[0].status == MaintenanceRunStatus.QUEUED @pytest.mark.asyncio async def test_process_next_queued_run_persists_terminal_result( default_session_factory, default_settings, tmp_path, monkeypatch, ): settings = default_settings.model_copy(update={"log_dir": tmp_path / "logs"}) settings = Settings.model_validate(settings.model_dump()) service = MaintenanceService(session_factory=default_session_factory, settings=settings) queued = await service.enqueue_run(job_type=MaintenanceJobType.BACKUP, triggered_by="test") async def _fake_execute(_run): return MaintenanceExecution( status=MaintenanceRunStatus.SUCCEEDED, summary="Synthetic success", output="stdout line\nstderr line", ) monkeypatch.setattr(service, "_execute_run", _fake_execute) processed = await service.process_next_queued_run() assert processed is True runs = await service.list_runs(limit=10) updated = next(run for run in runs if run.id == queued.id) assert updated.status == MaintenanceRunStatus.SUCCEEDED assert updated.summary == "Synthetic success" assert updated.log_path is not None assert (settings.log_dir / Path(updated.log_path)).is_file() @pytest.mark.asyncio async def test_list_runs_raises_when_table_is_missing(default_session_factory, default_settings): service = MaintenanceService(session_factory=default_session_factory, settings=default_settings) async with default_session_factory() as session: await session.exec(text("DROP TABLE maintenance_run")) await session.commit() with pytest.raises(MaintenanceError) as exc: await service.list_runs(limit=10) assert exc.value.category == ErrorCategory.INFRA_PERSISTENT @pytest.mark.asyncio async def test_execute_run_dispatches_gedcom_import(default_session_factory, default_settings, monkeypatch): service = MaintenanceService(session_factory=default_session_factory, settings=default_settings) run = await service.enqueue_run(job_type=MaintenanceJobType.GEDCOM_IMPORT, triggered_by="test") async def _fake_gedcom_import(): return MaintenanceExecution( status=MaintenanceRunStatus.SUCCEEDED, summary="GEDCOM imported", output="ok", ) monkeypatch.setattr(service, "_execute_gedcom_import", _fake_gedcom_import) execution = await service._execute_run(run) assert execution.summary == "GEDCOM imported"