125 lines
4.8 KiB
Python
125 lines
4.8 KiB
Python
from unittest import mock
|
|
|
|
import pytest
|
|
|
|
from infrasynth.features.models import FeatureFlag
|
|
from infrasynth.scheduler.models import ScheduledTask, TaskExecution
|
|
|
|
pytestmark = pytest.mark.django_db
|
|
|
|
TASKS_URL = "/api/scheduler/tasks/"
|
|
EXECUTIONS_URL = "/api/scheduler/executions/"
|
|
STATUS_URL = "/api/scheduler/status/"
|
|
|
|
|
|
@pytest.fixture
|
|
def task():
|
|
return ScheduledTask.objects.create(
|
|
name="cleanup",
|
|
task_path="os.getpid",
|
|
schedule_type=ScheduledTask.ScheduleType.CRON,
|
|
schedule_config={"cron": "0 2 * * *"},
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def execution(task):
|
|
return TaskExecution.objects.create(
|
|
task=task,
|
|
status=TaskExecution.Status.SUCCESS,
|
|
started_at="2026-01-01T00:00:00Z",
|
|
completed_at="2026-01-01T00:00:01Z",
|
|
)
|
|
|
|
|
|
class TestScheduledTaskViewSet:
|
|
def test_list_tasks(self, authenticated_client, task):
|
|
response = authenticated_client.get(TASKS_URL)
|
|
assert response.status_code == 200
|
|
assert response.data["count"] == 1
|
|
assert response.data["results"][0]["name"] == "cleanup"
|
|
|
|
def test_requires_auth(self, api_client, task):
|
|
assert api_client.get(TASKS_URL).status_code == 401
|
|
|
|
def test_create_task(self, authenticated_client):
|
|
response = authenticated_client.post(
|
|
TASKS_URL,
|
|
{
|
|
"name": "backup",
|
|
"task_path": "os.getpid",
|
|
"schedule_type": "interval",
|
|
"schedule_config": {"interval": 3600},
|
|
},
|
|
format="json",
|
|
)
|
|
assert response.status_code == 201
|
|
assert ScheduledTask.objects.filter(name="backup").exists()
|
|
|
|
def test_retrieve_update_delete(self, authenticated_client, task):
|
|
response = authenticated_client.get(f"{TASKS_URL}{task.id}/")
|
|
assert response.status_code == 200
|
|
response = authenticated_client.patch(f"{TASKS_URL}{task.id}/", {"name": "renamed"}, format="json")
|
|
assert response.status_code == 200
|
|
task.refresh_from_db()
|
|
assert task.name == "renamed"
|
|
response = authenticated_client.delete(f"{TASKS_URL}{task.id}/")
|
|
assert response.status_code == 204
|
|
assert not ScheduledTask.objects.filter(pk=task.pk).exists()
|
|
|
|
def test_run_action(self, authenticated_client, task):
|
|
response = authenticated_client.post(f"{TASKS_URL}{task.id}/run/")
|
|
assert response.status_code == 200
|
|
assert response.data["status"] == "triggered"
|
|
assert TaskExecution.objects.filter(task=task).exists()
|
|
|
|
def test_toggle_action(self, authenticated_client, task):
|
|
response = authenticated_client.post(f"{TASKS_URL}{task.id}/toggle/")
|
|
assert response.status_code == 200
|
|
assert response.data["is_active"] is False
|
|
response = authenticated_client.post(f"{TASKS_URL}{task.id}/toggle/")
|
|
assert response.data["is_active"] is True
|
|
|
|
def test_disabled_feature_returns_404(self, authenticated_client):
|
|
FeatureFlag.objects.create(slug="scheduler", name="Scheduler", is_active=False)
|
|
assert authenticated_client.get(TASKS_URL).status_code == 404
|
|
|
|
|
|
class TestTaskExecutionViewSet:
|
|
def test_list_executions(self, authenticated_client, execution):
|
|
response = authenticated_client.get(EXECUTIONS_URL)
|
|
assert response.status_code == 200
|
|
assert response.data["count"] == 1
|
|
assert response.data["results"][0]["task_info"]["name"] == "cleanup"
|
|
|
|
def test_retrieve_execution(self, authenticated_client, execution):
|
|
response = authenticated_client.get(f"{EXECUTIONS_URL}{execution.id}/")
|
|
assert response.status_code == 200
|
|
assert response.data["status"] == "success"
|
|
|
|
def test_requires_auth(self, api_client, execution):
|
|
assert api_client.get(EXECUTIONS_URL).status_code == 401
|
|
|
|
|
|
class TestSchedulerStatusViewSet:
|
|
def test_queue_status(self, authenticated_client):
|
|
with mock.patch("celery.current_app") as app:
|
|
inspect = app.control.inspect()
|
|
inspect.active.return_value = {}
|
|
inspect.scheduled.return_value = {}
|
|
inspect.reserved.return_value = {}
|
|
response = authenticated_client.get(f"{STATUS_URL}queue_status/")
|
|
assert response.status_code == 200
|
|
assert response.data["total_active"] == 0
|
|
|
|
def test_workers(self, authenticated_client):
|
|
with mock.patch("celery.current_app") as app:
|
|
inspect = app.control.inspect()
|
|
inspect.stats.return_value = {}
|
|
inspect.active.return_value = {}
|
|
response = authenticated_client.get(f"{STATUS_URL}workers/")
|
|
assert response.status_code == 200
|
|
assert response.data == []
|
|
|
|
def test_requires_auth(self, api_client):
|
|
assert api_client.get(f"{STATUS_URL}queue_status/").status_code == 401
|