181 lines
6.3 KiB
Python
181 lines
6.3 KiB
Python
"""Tests for the ClickUp chat tools (API-backed, no KV store)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from cheddahbot.tools.clickup_tool import (
|
|
clickup_list_tasks,
|
|
clickup_query_tasks,
|
|
clickup_reset_task,
|
|
clickup_task_status,
|
|
get_active_tasks,
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class FakeTask:
|
|
id: str = "t1"
|
|
name: str = "Test Task"
|
|
status: str = "to do"
|
|
task_type: str = "Press Release"
|
|
url: str = "https://app.clickup.com/t/t1"
|
|
due_date: str = ""
|
|
date_updated: str = ""
|
|
tags: list = field(default_factory=list)
|
|
custom_fields: dict = field(default_factory=dict)
|
|
|
|
|
|
def _make_ctx():
|
|
config = MagicMock()
|
|
config.clickup.api_token = "test-token"
|
|
config.clickup.workspace_id = "ws1"
|
|
config.clickup.space_id = "sp1"
|
|
config.clickup.task_type_field_name = "Work Category"
|
|
config.clickup.automation_status = "automation underway"
|
|
config.clickup.review_status = "internal review"
|
|
config.clickup.error_status = "error"
|
|
config.clickup.poll_statuses = ["to do"]
|
|
return {"config": config, "db": MagicMock()}
|
|
|
|
|
|
class TestClickupQueryTasks:
|
|
@patch("cheddahbot.tools.clickup_tool._get_clickup_client")
|
|
def test_returns_tasks(self, mock_client_fn):
|
|
mock_client = MagicMock()
|
|
mock_client.get_tasks_from_space.return_value = [
|
|
FakeTask(id="t1", name="PR Task", task_type="Press Release"),
|
|
]
|
|
mock_client_fn.return_value = mock_client
|
|
|
|
result = clickup_query_tasks(ctx=_make_ctx())
|
|
assert "PR Task" in result
|
|
assert "t1" in result
|
|
|
|
@patch("cheddahbot.tools.clickup_tool._get_clickup_client")
|
|
def test_no_tasks_found(self, mock_client_fn):
|
|
mock_client = MagicMock()
|
|
mock_client.get_tasks_from_space.return_value = []
|
|
mock_client_fn.return_value = mock_client
|
|
|
|
result = clickup_query_tasks(ctx=_make_ctx())
|
|
assert "No tasks found" in result
|
|
|
|
|
|
class TestClickupListTasks:
|
|
@patch("cheddahbot.tools.clickup_tool._get_clickup_client")
|
|
def test_lists_automation_tasks(self, mock_client_fn):
|
|
mock_client = MagicMock()
|
|
mock_client.get_tasks_from_space.return_value = [
|
|
FakeTask(id="t1", name="Active Task", status="automation underway"),
|
|
]
|
|
mock_client_fn.return_value = mock_client
|
|
|
|
result = clickup_list_tasks(ctx=_make_ctx())
|
|
assert "Active Task" in result
|
|
assert "t1" in result
|
|
|
|
@patch("cheddahbot.tools.clickup_tool._get_clickup_client")
|
|
def test_no_automation_tasks(self, mock_client_fn):
|
|
mock_client = MagicMock()
|
|
mock_client.get_tasks_from_space.return_value = []
|
|
mock_client_fn.return_value = mock_client
|
|
|
|
result = clickup_list_tasks(ctx=_make_ctx())
|
|
assert "No tasks found" in result
|
|
|
|
@patch("cheddahbot.tools.clickup_tool._get_clickup_client")
|
|
def test_filter_by_status(self, mock_client_fn):
|
|
mock_client = MagicMock()
|
|
mock_client.get_tasks_from_space.return_value = [
|
|
FakeTask(id="t1", name="Error Task", status="error"),
|
|
]
|
|
mock_client_fn.return_value = mock_client
|
|
|
|
result = clickup_list_tasks(status="error", ctx=_make_ctx())
|
|
assert "Error Task" in result
|
|
|
|
|
|
class TestClickupTaskStatus:
|
|
@patch("cheddahbot.tools.clickup_tool._get_clickup_client")
|
|
def test_shows_details(self, mock_client_fn):
|
|
mock_client = MagicMock()
|
|
mock_client.get_task.return_value = FakeTask(
|
|
id="t1",
|
|
name="My Task",
|
|
status="automation underway",
|
|
task_type="Press Release",
|
|
)
|
|
mock_client_fn.return_value = mock_client
|
|
|
|
result = clickup_task_status(task_id="t1", ctx=_make_ctx())
|
|
assert "My Task" in result
|
|
assert "automation underway" in result
|
|
assert "Press Release" in result
|
|
|
|
@patch("cheddahbot.tools.clickup_tool._get_clickup_client")
|
|
def test_api_error(self, mock_client_fn):
|
|
mock_client = MagicMock()
|
|
mock_client.get_task.side_effect = Exception("Not found")
|
|
mock_client_fn.return_value = mock_client
|
|
|
|
result = clickup_task_status(task_id="bad", ctx=_make_ctx())
|
|
assert "Error" in result
|
|
|
|
|
|
class TestClickupResetTask:
|
|
@patch("cheddahbot.tools.clickup_tool._get_clickup_client")
|
|
def test_resets_task(self, mock_client_fn):
|
|
mock_client = MagicMock()
|
|
mock_client_fn.return_value = mock_client
|
|
|
|
result = clickup_reset_task(task_id="t1", ctx=_make_ctx())
|
|
assert "reset" in result.lower()
|
|
mock_client.update_task_status.assert_called_once_with("t1", "to do")
|
|
mock_client.add_comment.assert_called_once()
|
|
|
|
@patch("cheddahbot.tools.clickup_tool._get_clickup_client")
|
|
def test_api_error(self, mock_client_fn):
|
|
mock_client = MagicMock()
|
|
mock_client.update_task_status.side_effect = Exception("API error")
|
|
mock_client_fn.return_value = mock_client
|
|
|
|
result = clickup_reset_task(task_id="t1", ctx=_make_ctx())
|
|
assert "Error" in result
|
|
|
|
|
|
class TestGetActiveTasks:
|
|
def test_no_scheduler(self):
|
|
result = get_active_tasks(ctx={"config": MagicMock()})
|
|
assert "not available" in result.lower()
|
|
|
|
def test_nothing_running(self):
|
|
scheduler = MagicMock()
|
|
scheduler.get_active_executions.return_value = {}
|
|
scheduler.get_loop_timestamps.return_value = {"clickup": None, "folder_watch": None}
|
|
|
|
result = get_active_tasks(ctx={"scheduler": scheduler})
|
|
assert "No tasks actively executing" in result
|
|
assert "Safe to restart: Yes" in result
|
|
|
|
def test_tasks_running(self):
|
|
from datetime import UTC, datetime, timedelta
|
|
|
|
scheduler = MagicMock()
|
|
scheduler.get_active_executions.return_value = {
|
|
"t1": {
|
|
"name": "Press Release for Acme",
|
|
"tool": "write_press_releases",
|
|
"started_at": datetime.now(UTC) - timedelta(minutes=5),
|
|
"thread": "clickup_thread",
|
|
}
|
|
}
|
|
scheduler.get_loop_timestamps.return_value = {"clickup": datetime.now(UTC).isoformat()}
|
|
|
|
result = get_active_tasks(ctx={"scheduler": scheduler})
|
|
assert "Active Executions (1)" in result
|
|
assert "Press Release for Acme" in result
|
|
assert "write_press_releases" in result
|
|
assert "Safe to restart: No" in result
|