from datetime import timedelta import pytest from django.utils import timezone from apps.payments.models import Payment from apps.payments.tasks import sweep_stale_payment_verifications pytestmark = [pytest.mark.integration, pytest.mark.django_db] def test_sweep_schedules_only_old_unresolved_payments_and_deduplicates_dispatch( user_factory, event_factory, mocker, settings ): settings.PAYMENT_VERIFICATION_GRACE_SECONDS = 1800 user = user_factory() event = event_factory(price=25000) old_payment = Payment.objects.create( user=user, event=event, base_amount=25000, amount=25000, authority="AUTH-RECOVERY-OLD", status=Payment.OrderStatusChoices.PENDING, ) recent_payment = Payment.objects.create( user=user, event=event, base_amount=25000, amount=25000, authority="AUTH-RECOVERY-NEW", status=Payment.OrderStatusChoices.PENDING, ) Payment.objects.filter(pk=old_payment.pk).update( created_at=timezone.now() - timedelta(seconds=1801) ) enqueue = mocker.patch("apps.payments.tasks.reconcile_payment.apply_async") scheduled = sweep_stale_payment_verifications.run() old_payment.refresh_from_db() recent_payment.refresh_from_db() assert scheduled == 1 enqueue.assert_called_once_with(args=(old_payment.pk,)) assert old_payment.verification_dispatch_expires_at is not None assert old_payment.verification_dispatch_expires_at > timezone.now() assert recent_payment.verification_next_attempt_at is None assert sweep_stale_payment_verifications.run() == 0 enqueue.assert_called_once() def test_sweep_does_not_restart_exhausted_or_terminal_attempts( user_factory, event_factory, mocker, settings ): settings.PAYMENT_VERIFICATION_GRACE_SECONDS = 60 settings.PAYMENT_VERIFICATION_MAX_ATTEMPTS = 3 user = user_factory() event = event_factory(price=25000) payments = [ Payment.objects.create( user=user, event=event, base_amount=25000, amount=25000, authority=f"AUTH-RECOVERY-{status}-{index}", status=status, verification_attempts=3, ) for index, status in enumerate( [Payment.OrderStatusChoices.PENDING, Payment.OrderStatusChoices.FAILED] ) ] Payment.objects.filter(pk__in=[payment.pk for payment in payments]).update( created_at=timezone.now() - timedelta(minutes=5) ) enqueue = mocker.patch("apps.payments.tasks.reconcile_payment.apply_async") scheduled = sweep_stale_payment_verifications.run() assert scheduled == 0 enqueue.assert_not_called()