113 lines
4.1 KiB
Python
113 lines
4.1 KiB
Python
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
|