""" Tests for Job Orchestrator Tool """ import pytest import os import sys sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from services.router.tool_manager import ToolManager, ToolResult class TestJobOrchestratorTool: """Test job orchestrator tool functionality""" @pytest.mark.asyncio async def test_list_tasks_returns_allowed_tasks(self): """Test that list_tasks returns only allowed tasks""" tool_mgr = ToolManager({}) result = await tool_mgr._job_orchestrator_tool({ "action": "list_tasks", "params": {}, "agent_id": "sofiia" }) assert result.success is True assert "tasks" in result.result assert result.result["count"] >= 0 @pytest.mark.asyncio async def test_list_tasks_filter_by_tag(self): """Test filtering tasks by tag""" tool_mgr = ToolManager({}) result = await tool_mgr._job_orchestrator_tool({ "action": "list_tasks", "params": { "filter": {"tag": "smoke"} }, "agent_id": "sofiia" }) assert result.success is True for task in result.result["tasks"]: assert "smoke" in task.get("tags", []) @pytest.mark.asyncio async def test_list_tasks_filter_by_service(self): """Test filtering tasks by service""" tool_mgr = ToolManager({}) result = await tool_mgr._job_orchestrator_tool({ "action": "list_tasks", "params": { "filter": {"service": "gateway"} }, "agent_id": "sofiia" }) assert result.success is True for task in result.result["tasks"]: service = task.get("id", "") assert "gateway" in service or "smoke" in service @pytest.mark.asyncio async def test_start_task_requires_task_id(self): """Test that start_task requires task_id""" tool_mgr = ToolManager({}) result = await tool_mgr._job_orchestrator_tool({ "action": "start_task", "params": {}, "agent_id": "sofiia" }) assert result.success is False assert "task_id" in result.error.lower() @pytest.mark.asyncio async def test_start_task_unknown_task(self): """Test starting unknown task returns error""" tool_mgr = ToolManager({}) result = await tool_mgr._job_orchestrator_tool({ "action": "start_task", "params": { "task_id": "nonexistent_task" }, "agent_id": "sofiia" }) assert result.success is False assert "not found" in result.error.lower() @pytest.mark.asyncio async def test_start_task_dry_run(self): """Test dry run returns execution plan without running""" tool_mgr = ToolManager({}) result = await tool_mgr._job_orchestrator_tool({ "action": "start_task", "params": { "task_id": "smoke_gateway", "dry_run": True }, "agent_id": "sofiia" }) assert result.success is True assert "execution_plan" in result.result assert result.result["job"]["status"] == "dry_run" assert result.result["message"] == "Dry run - no execution performed" @pytest.mark.asyncio async def test_start_task_with_inputs_validation(self): """Test input schema validation""" tool_mgr = ToolManager({}) result = await tool_mgr._job_orchestrator_tool({ "action": "start_task", "params": { "task_id": "drift_check_node1", "inputs": { "mode": "quick" } }, "agent_id": "sofiia" }) assert result.success is True @pytest.mark.asyncio async def test_start_task_invalid_input(self): """Test that invalid inputs are rejected""" tool_mgr = ToolManager({}) result = await tool_mgr._job_orchestrator_tool({ "action": "start_task", "params": { "task_id": "drift_check_node1", "inputs": { "mode": "invalid_mode" } }, "agent_id": "sofiia" }) assert result.success is False assert "validation" in result.error.lower() or "invalid" in result.error.lower() @pytest.mark.asyncio async def test_start_task_missing_required_input(self): """Test that missing required inputs are rejected""" tool_mgr = ToolManager({}) result = await tool_mgr._job_orchestrator_tool({ "action": "start_task", "params": { "task_id": "drift_check_node1", "inputs": {} }, "agent_id": "sofiia" }) assert result.success is False assert "missing" in result.error.lower() or "required" in result.error.lower() @pytest.mark.asyncio async def test_get_job_requires_job_id(self): """Test that get_job requires job_id""" tool_mgr = ToolManager({}) result = await tool_mgr._job_orchestrator_tool({ "action": "get_job", "params": {}, "agent_id": "sofiia" }) assert result.success is False assert "job_id" in result.error.lower() @pytest.mark.asyncio async def test_cancel_job_requires_job_id(self): """Test that cancel_job requires job_id""" tool_mgr = ToolManager({}) result = await tool_mgr._job_orchestrator_tool({ "action": "cancel_job", "params": {}, "agent_id": "sofiia" }) assert result.success is False assert "job_id" in result.error.lower() @pytest.mark.asyncio async def test_cancel_job_allowed_for_admin(self): """Test that admin can cancel jobs""" tool_mgr = ToolManager({}) result = await tool_mgr._job_orchestrator_tool({ "action": "cancel_job", "params": { "job_id": "job-abc123", "reason": "Testing cancellation" }, "agent_id": "sofiia" }) assert result.success is True assert result.result["status"] == "canceled" assert result.result["canceled_by"] == "sofiia" @pytest.mark.asyncio async def test_cancel_job_denied_for_non_admin(self): """Test that non-admin cannot cancel jobs""" tool_mgr = ToolManager({}) result = await tool_mgr._job_orchestrator_tool({ "action": "cancel_job", "params": { "job_id": "job-abc123", "reason": "Testing" }, "agent_id": "guest" }) assert result.success is False assert "only" in result.error.lower() or "admin" in result.error.lower() @pytest.mark.asyncio async def test_unknown_action_returns_error(self): """Test that unknown action returns error""" tool_mgr = ToolManager({}) result = await tool_mgr._job_orchestrator_tool({ "action": "unknown_action", "params": {}, "agent_id": "sofiia" }) assert result.success is False assert "unknown action" in result.error.lower() @pytest.mark.asyncio async def test_task_has_required_fields(self): """Test that tasks have required fields""" tool_mgr = ToolManager({}) result = await tool_mgr._job_orchestrator_tool({ "action": "list_tasks", "params": {}, "agent_id": "sofiia" }) assert result.success is True for task in result.result["tasks"]: assert "id" in task assert "title" in task assert "tags" in task @pytest.mark.asyncio async def test_start_task_returns_job_record(self): """Test that start_task returns job record""" tool_mgr = ToolManager({}) result = await tool_mgr._job_orchestrator_tool({ "action": "start_task", "params": { "task_id": "smoke_gateway", "dry_run": True }, "agent_id": "sofiia" }) assert result.success is True assert "job" in result.result job = result.result["job"] assert "id" in job assert "task_id" in job assert "status" in job assert "created_at" in job @pytest.mark.asyncio async def test_idempotency_key_supported(self): """Test that idempotency_key is supported""" tool_mgr = ToolManager({}) result = await tool_mgr._job_orchestrator_tool({ "action": "start_task", "params": { "task_id": "smoke_gateway", "idempotency_key": "unique-key-123" }, "agent_id": "sofiia" }) assert result.success is True assert result.result["job"]["idempotency_key"] == "unique-key-123"