import pytest from contextlib import contextmanager from celery.schedules import crontab import psycopg from django.conf import settings from apps.ingestion.models import IngestionRun from apps.ingestion.services.runs import _build_ingestion_lock_key, release_ingestion_lock, try_acquire_ingestion_lock from apps.ingestion.tasks import scheduled_provider_sync, trigger_incremental_sync from config.celery import app as celery_app, build_periodic_schedule @pytest.mark.django_db def test_periodic_task_registered(): assert "apps.ingestion.tasks.scheduled_provider_sync" in celery_app.tasks @pytest.mark.django_db def test_build_periodic_schedule_enabled(settings): settings.INGESTION_SCHEDULE_ENABLED = True settings.INGESTION_SCHEDULE_CRON = "15 * * * *" schedule = build_periodic_schedule() assert "ingestion.scheduled_provider_sync" in schedule entry = schedule["ingestion.scheduled_provider_sync"] assert entry["task"] == "apps.ingestion.tasks.scheduled_provider_sync" assert isinstance(entry["schedule"], crontab) assert entry["schedule"]._orig_minute == "15" @pytest.mark.django_db def test_build_periodic_schedule_disabled(settings): settings.INGESTION_SCHEDULE_ENABLED = False assert build_periodic_schedule() == {} @pytest.mark.django_db def test_build_periodic_schedule_invalid_cron_disables_task_and_logs(settings, caplog): settings.INGESTION_SCHEDULE_ENABLED = True settings.INGESTION_SCHEDULE_CRON = "invalid-cron" with caplog.at_level("ERROR"): schedule = build_periodic_schedule() assert schedule == {} assert any("Invalid periodic ingestion schedule config. Task disabled." in message for message in caplog.messages) @pytest.mark.django_db def test_trigger_incremental_sync_skips_when_advisory_lock_not_acquired(settings, monkeypatch): settings.INGESTION_PREVENT_OVERLAP = True @contextmanager def fake_lock(**kwargs): yield False monkeypatch.setattr("apps.ingestion.tasks.ingestion_advisory_lock", fake_lock) run_id = trigger_incremental_sync.apply( kwargs={"provider_namespace": "mvp_demo"}, ).get() skipped_run = IngestionRun.objects.get(id=run_id) assert skipped_run.status == IngestionRun.RunStatus.CANCELED assert "advisory lock" in skipped_run.error_summary @pytest.mark.django_db def test_advisory_lock_prevents_concurrent_acquisition(): provider_namespace = "mvp_demo" job_type = IngestionRun.JobType.INCREMENTAL lock_key = _build_ingestion_lock_key(provider_namespace=provider_namespace, job_type=job_type) conninfo = ( f"dbname={settings.DATABASES['default']['NAME']} " f"user={settings.DATABASES['default']['USER']} " f"password={settings.DATABASES['default']['PASSWORD']} " f"host={settings.DATABASES['default']['HOST']} " f"port={settings.DATABASES['default']['PORT']}" ) with psycopg.connect(conninfo) as external_conn: with external_conn.cursor() as cursor: cursor.execute("SELECT pg_advisory_lock(%s);", [lock_key]) acquired, _ = try_acquire_ingestion_lock( provider_namespace=provider_namespace, job_type=job_type, ) assert acquired is False cursor.execute("SELECT pg_advisory_unlock(%s);", [lock_key]) acquired, django_key = try_acquire_ingestion_lock( provider_namespace=provider_namespace, job_type=job_type, ) assert acquired is True release_ingestion_lock(lock_key=django_key) @pytest.mark.django_db def test_scheduled_provider_sync_uses_configured_job_type(settings, monkeypatch): settings.INGESTION_SCHEDULE_JOB_TYPE = IngestionRun.JobType.FULL_SYNC settings.INGESTION_SCHEDULE_PROVIDER_NAMESPACE = "mvp_demo" captured = {} def fake_runner(**kwargs): captured.update(kwargs) return 99 monkeypatch.setattr("apps.ingestion.tasks._run_sync_with_overlap_guard", fake_runner) result = scheduled_provider_sync.apply().get() assert result == 99 assert captured["provider_namespace"] == "mvp_demo" assert captured["job_type"] == IngestionRun.JobType.FULL_SYNC