"""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