86 lines
2.8 KiB
Python
86 lines
2.8 KiB
Python
from datetime import timedelta
|
|
|
|
import pytest
|
|
from django.utils import timezone
|
|
|
|
from apps.sources.models import Source, SourceLease
|
|
from apps.sources.services.scheduling import (
|
|
acquire_source_lease,
|
|
calculate_failure_backoff_seconds,
|
|
release_source_lease,
|
|
)
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_source_lease_blocks_concurrent_workers_and_recovers_when_expired(source):
|
|
now = timezone.now()
|
|
lease_a = acquire_source_lease(source_id=source.pk, worker_token="worker-a", now=now)
|
|
assert lease_a is not None
|
|
assert acquire_source_lease(source_id=source.pk, worker_token="worker-b", now=now) is None
|
|
|
|
assert release_source_lease(source_id=source.pk, worker_token="worker-a", now=now)
|
|
lease_b = acquire_source_lease(
|
|
source_id=source.pk,
|
|
worker_token="worker-b",
|
|
now=now + timedelta(seconds=source.minimum_interval_seconds),
|
|
)
|
|
assert lease_b is not None
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_source_lease_reuses_expired_lease(db, source):
|
|
now = timezone.now()
|
|
SourceLease.objects.create(
|
|
source=source,
|
|
token="expired-worker",
|
|
worker_id="expired-worker",
|
|
expires_at=now - timedelta(minutes=1),
|
|
)
|
|
lease = acquire_source_lease(source_id=source.pk, worker_token="recovery-worker", now=now)
|
|
assert lease is not None
|
|
assert lease.worker_id == "recovery-worker"
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_source_lease_allows_origin_concurrency(db):
|
|
shared_domain = "shared.example.org"
|
|
source_a = Source.objects.create(
|
|
name="Source A",
|
|
source_type=Source.Type.EMPLOYER,
|
|
base_url="https://shared.example.org/jobs/",
|
|
domain=shared_domain,
|
|
status=Source.Status.ACTIVE,
|
|
policy=Source.Policy.ALLOW,
|
|
max_concurrency=2,
|
|
)
|
|
source_b = Source.objects.create(
|
|
name="Source B",
|
|
source_type=Source.Type.RSS,
|
|
base_url="https://shared.example.org/careers/",
|
|
domain=shared_domain,
|
|
status=Source.Status.ACTIVE,
|
|
policy=Source.Policy.ALLOW,
|
|
max_concurrency=2,
|
|
)
|
|
now = timezone.now()
|
|
lease_a = acquire_source_lease(source_id=source_a.pk, worker_token="worker-a", now=now)
|
|
lease_b = acquire_source_lease(source_id=source_b.pk, worker_token="worker-b", now=now)
|
|
assert lease_a is not None
|
|
assert lease_b is not None
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_failure_backoff_uses_retry_after_and_bounds():
|
|
source = Source(
|
|
name="Temp",
|
|
source_type=Source.Type.EMPLOYER,
|
|
base_url="https://retry.example.org/",
|
|
domain="retry.example.org",
|
|
status=Source.Status.ACTIVE,
|
|
policy=Source.Policy.ALLOW,
|
|
)
|
|
source.save()
|
|
backoff = calculate_failure_backoff_seconds(source, failure_count=2, retry_after_seconds=120)
|
|
assert backoff >= 120
|
|
assert backoff <= 3600 + 45
|