"""Terminal ``TaskExecution`` signals fire exactly once (Part B2).""" import pytest from infrasynth.scheduler.models import ScheduledTask from infrasynth.scheduler.services import TaskService from infrasynth.scheduler.signals import task_completed, task_failed pytestmark = pytest.mark.django_db def _capture(signal): received = [] receiver = lambda **kwargs: received.append(kwargs) # noqa: E731 signal.connect(receiver, weak=False) return received, receiver @pytest.fixture def plain_task(): return ScheduledTask.objects.create( name="plain", task_path="os.getpid", schedule_type=ScheduledTask.ScheduleType.MANUAL, ) class TestTaskCompleted: def test_completed_fires_once(self, plain_task): received, receiver = _capture(task_completed) try: TaskService().run_now(plain_task.id) finally: task_completed.disconnect(receiver) assert len(received) == 1 assert received[0]["task_name"] == "plain" assert received[0]["tenant_id"] == str(plain_task.tenant_id) assert received[0]["duration_ms"] is not None def test_completed_does_not_emit_failed(self, plain_task): received, receiver = _capture(task_failed) try: TaskService().run_now(plain_task.id) finally: task_failed.disconnect(receiver) assert received == [] class TestTaskFailed: def test_unimportable_fires_once(self): task = ScheduledTask.objects.create( name="broken", task_path="does.not.exist", schedule_type=ScheduledTask.ScheduleType.MANUAL, ) received, receiver = _capture(task_failed) try: TaskService().run_now(task.id) finally: task_failed.disconnect(receiver) assert len(received) == 1 assert received[0]["task_name"] == "broken" assert "Could not import" in received[0]["error"] def test_exception_fires_once(self): task = ScheduledTask.objects.create( name="boom", task_path="math.sqrt", schedule_type=ScheduledTask.ScheduleType.MANUAL, args=["not-a-number"], ) received, receiver = _capture(task_failed) try: TaskService().run_now(task.id) finally: task_failed.disconnect(receiver) assert len(received) == 1 assert received[0]["task_name"] == "boom"