"""Unit tests for the maintenance tasks. Eager-mode Celery so the task function runs synchronously inside the same DB transaction as the test. """ from datetime import UTC, datetime, timedelta import pytest from backend.app.celery_app import celery from backend.app.models import ImportBatch, ImportTask @pytest.fixture(autouse=True) def eager(): celery.conf.task_always_eager = True yield celery.conf.task_always_eager = False def _make_batch(session) -> int: batch = ImportBatch(triggered_by="manual", source_path="/import", scan_mode="quick") session.add(batch) session.flush() return batch.id def test_recover_interrupted_only_old(db_sync): batch_id = _make_batch(db_sync) now = datetime.now(UTC) fresh = ImportTask( batch_id=batch_id, source_path="/import/a.jpg", task_type="media", status="processing", started_at=now - timedelta(minutes=5), ) stale = ImportTask( batch_id=batch_id, source_path="/import/b.jpg", task_type="media", status="processing", started_at=now - timedelta(hours=2), ) db_sync.add_all([fresh, stale]) db_sync.commit() from backend.app.tasks.maintenance import recover_interrupted_tasks recovered = recover_interrupted_tasks.apply().get() assert recovered == 1 db_sync.refresh(fresh) db_sync.refresh(stale) assert fresh.status == "processing" assert stale.status == "queued" assert stale.started_at is None def test_cleanup_old_deletes_finished_old(db_sync): batch_id = _make_batch(db_sync) now = datetime.now(UTC) old_complete = ImportTask( batch_id=batch_id, source_path="/import/a.jpg", task_type="media", status="complete", finished_at=now - timedelta(days=10), ) recent_complete = ImportTask( batch_id=batch_id, source_path="/import/b.jpg", task_type="media", status="complete", finished_at=now - timedelta(days=2), ) old_pending = ImportTask( batch_id=batch_id, source_path="/import/c.jpg", task_type="media", status="pending", ) db_sync.add_all([old_complete, recent_complete, old_pending]) db_sync.commit() from backend.app.tasks.maintenance import cleanup_old_tasks deleted = cleanup_old_tasks.apply().get() assert deleted == 1 remaining = {t.source_path for t in db_sync.query(ImportTask).all()} assert remaining == {"/import/b.jpg", "/import/c.jpg"}