from __future__ import annotations import threading import time import pytest from iti.tasks.registry import TaskRegistry from iti.tasks.runner import TaskRunner, _parse_interval, _parse_simple_cron def test_task_registry_triggers_success_and_failure_runs(): registry = TaskRegistry() registry.register(name="success", handler=lambda: {"ok": True}) registry.register(name="failure", handler=lambda: 1 / 0) success = registry.trigger("success") failure = registry.trigger("failure") assert success.status == "success" assert success.result == {"ok": True} assert success.finished_at is not None assert failure.status == "failed" assert "ZeroDivisionError" in failure.error def test_task_registry_rejects_duplicate_names(): registry = TaskRegistry() registry.register(name="sync", handler=lambda: None) with pytest.raises(ValueError, match="task already registered"): registry.register(name="sync", handler=lambda: None) def test_task_registry_skips_when_same_task_is_running(): started = threading.Event() release = threading.Event() registry = TaskRegistry() def blocking_handler(): started.set() release.wait(timeout=2) registry.register(name="sync", handler=blocking_handler) worker = threading.Thread(target=registry.trigger, args=("sync",)) worker.start() assert started.wait(timeout=1) skipped = registry.trigger("sync") release.set() worker.join(timeout=2) assert skipped.status == "skipped" assert skipped.error == "task already running" def test_task_schedule_parsers_and_due_check(): runner = TaskRunner(TaskRegistry()) now = time.time() assert _parse_interval("interval:60") == 60 assert _parse_interval("interval:0") == 1 assert _parse_interval("bad") is None assert _parse_simple_cron("cron:*/5 * * * *") == 300 assert _parse_simple_cron("* * * * *") == 60 assert _parse_simple_cron("0 * * * *") is None assert runner._due("interval:10", "sync", now) is True runner._last_run["sync"] = now assert runner._due("interval:10", "sync", now + 5) is False assert runner._due("interval:10", "sync", now + 10) is True