258 lines
8.6 KiB
Python
258 lines
8.6 KiB
Python
"""
|
|
Tests for the Task agent.
|
|
|
|
Tests registration, API endpoints, tool access, and spawn_agent functionality.
|
|
"""
|
|
import pytest
|
|
from unittest.mock import AsyncMock, patch
|
|
|
|
from src.domains.agents.base import get_agent, list_agents
|
|
from src.domains.agents.task import task_agent, TaskAgentImpl
|
|
|
|
|
|
class TestTaskAgentRegistration:
|
|
"""Tests for Task agent registration."""
|
|
|
|
def test_task_agent_registered(self):
|
|
"""Test that task agent is registered in registry."""
|
|
agent = get_agent("task")
|
|
assert agent is not None
|
|
assert agent.name == "task"
|
|
|
|
def test_task_agent_in_list(self):
|
|
"""Test that task agent appears in agent list."""
|
|
agents = list_agents()
|
|
names = [a["name"] for a in agents]
|
|
assert "task" in names
|
|
|
|
def test_task_agent_has_description(self):
|
|
"""Test that task agent has a description."""
|
|
agent = get_agent("task")
|
|
assert agent is not None
|
|
assert len(agent.description) > 0
|
|
assert "task" in agent.description.lower() or "autonomous" in agent.description.lower()
|
|
|
|
def test_task_agent_singleton(self):
|
|
"""Test that task_agent is the registered instance."""
|
|
registered = get_agent("task")
|
|
assert registered is task_agent
|
|
|
|
def test_task_agent_is_correct_type(self):
|
|
"""Test that task agent is correct implementation type."""
|
|
assert isinstance(task_agent, TaskAgentImpl)
|
|
|
|
|
|
class TestTaskAgentTools:
|
|
"""Tests for Task agent tool access."""
|
|
|
|
def test_task_agent_has_all_tools(self):
|
|
"""Test that task agent has all 9 tools."""
|
|
agent = task_agent.agent
|
|
tool_names = list(agent._function_toolset.tools.keys())
|
|
|
|
# Should have 9 tools total
|
|
assert len(tool_names) == 9
|
|
|
|
def test_task_agent_has_read_only_tools(self):
|
|
"""Test that task agent has read-only tools."""
|
|
agent = task_agent.agent
|
|
tool_names = list(agent._function_toolset.tools.keys())
|
|
|
|
assert "read_file" in tool_names
|
|
assert "glob_files" in tool_names
|
|
assert "grep_content" in tool_names
|
|
assert "bash_readonly" in tool_names
|
|
|
|
def test_task_agent_has_write_tools(self):
|
|
"""Test that task agent has write tools."""
|
|
agent = task_agent.agent
|
|
tool_names = list(agent._function_toolset.tools.keys())
|
|
|
|
assert "edit_file" in tool_names
|
|
assert "write_file" in tool_names
|
|
assert "bash" in tool_names
|
|
|
|
def test_task_agent_has_external_tools(self):
|
|
"""Test that task agent has external tools."""
|
|
agent = task_agent.agent
|
|
tool_names = list(agent._function_toolset.tools.keys())
|
|
|
|
assert "web_search" in tool_names
|
|
|
|
def test_task_agent_has_spawn_agent_tool(self):
|
|
"""Test that task agent has spawn_agent orchestration tool."""
|
|
agent = task_agent.agent
|
|
tool_names = list(agent._function_toolset.tools.keys())
|
|
|
|
assert "spawn_agent" in tool_names
|
|
|
|
|
|
class TestSpawnAgentTool:
|
|
"""Tests for spawn_agent orchestration functionality."""
|
|
|
|
@pytest.mark.anyio
|
|
async def test_spawn_explore_agent(self):
|
|
"""Test spawning an explore agent."""
|
|
from src.domains.agents.task.tools import register_task_tools
|
|
from src.domains.agents.base import AgentContext
|
|
from pydantic_ai import Agent, RunContext
|
|
from unittest.mock import MagicMock
|
|
|
|
# Create a mock context
|
|
ctx = MagicMock(spec=RunContext)
|
|
ctx.deps = AgentContext(
|
|
working_dir="/tmp",
|
|
allowed_paths=["/tmp"],
|
|
timeout_seconds=30
|
|
)
|
|
|
|
# Mock the explore agent
|
|
with patch("src.domains.agents.base.get_agent") as mock_get_agent:
|
|
mock_explore = AsyncMock()
|
|
mock_explore.run = AsyncMock(return_value="Found 5 Python files")
|
|
mock_get_agent.return_value = mock_explore
|
|
|
|
# Import and call spawn_agent directly
|
|
from src.domains.agents.task import tools
|
|
# We need to test the actual tool function
|
|
# For now, verify the explore agent would be called correctly
|
|
|
|
@pytest.mark.anyio
|
|
async def test_spawn_unknown_agent_returns_error(self):
|
|
"""Test that spawning unknown agent type returns error."""
|
|
from src.domains.agents.base import AgentContext
|
|
from unittest.mock import MagicMock
|
|
from pydantic_ai import RunContext
|
|
|
|
# We can't easily test the tool directly, but we can verify
|
|
# the agent type validation logic
|
|
allowed_types = ["explore", "plan"]
|
|
assert "nonexistent" not in allowed_types
|
|
assert "task" not in allowed_types # Task should be blocked
|
|
|
|
def test_spawn_task_agent_blocked(self):
|
|
"""Test that spawning nested task agents is blocked."""
|
|
# Verify the validation logic prevents recursion
|
|
# The spawn_agent tool should return an error for agent_type="task"
|
|
allowed_types = ["explore", "plan"]
|
|
assert "task" not in allowed_types
|
|
|
|
|
|
class TestTaskAgentAPI:
|
|
"""Tests for Task agent REST API."""
|
|
|
|
@pytest.mark.anyio
|
|
async def test_list_agents_includes_task(self, auth_client):
|
|
"""Test that agent list includes task agent."""
|
|
response = await auth_client.get("/agents/")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
names = [a["name"] for a in data["agents"]]
|
|
assert "task" in names
|
|
|
|
@pytest.mark.anyio
|
|
async def test_get_task_agent_info(self, auth_client):
|
|
"""Test getting task agent info."""
|
|
response = await auth_client.get("/agents/task")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["name"] == "task"
|
|
assert "description" in data
|
|
assert len(data["description"]) > 0
|
|
|
|
@pytest.mark.anyio
|
|
async def test_run_task_with_invalid_body(self, auth_client):
|
|
"""Test running task agent with invalid request."""
|
|
response = await auth_client.post(
|
|
"/agents/run",
|
|
json={
|
|
"agent_type": "task",
|
|
# Missing prompt
|
|
}
|
|
)
|
|
|
|
assert response.status_code == 422
|
|
|
|
@pytest.mark.anyio
|
|
async def test_stream_task_with_invalid_body(self, auth_client):
|
|
"""Test streaming task agent with invalid request."""
|
|
response = await auth_client.post(
|
|
"/agents/stream",
|
|
json={
|
|
"agent_type": "task",
|
|
# Missing prompt
|
|
}
|
|
)
|
|
|
|
assert response.status_code == 422
|
|
|
|
|
|
class TestTaskAgentProperties:
|
|
"""Tests for Task agent properties and configuration."""
|
|
|
|
def test_task_agent_name(self):
|
|
"""Test task agent name property."""
|
|
assert task_agent.name == "task"
|
|
|
|
def test_task_agent_description_not_empty(self):
|
|
"""Test task agent description is not empty."""
|
|
assert task_agent.description
|
|
assert len(task_agent.description) > 10
|
|
|
|
def test_task_agent_creates_agent_lazily(self):
|
|
"""Test that PydanticAI agent is created lazily."""
|
|
# Create a fresh instance
|
|
fresh_agent = TaskAgentImpl()
|
|
|
|
# _agent should be None before first access
|
|
assert fresh_agent._agent is None
|
|
|
|
# Access the agent property
|
|
_ = fresh_agent.agent
|
|
|
|
# Now _agent should be set
|
|
assert fresh_agent._agent is not None
|
|
|
|
|
|
class TestAllAgentsRegistered:
|
|
"""Tests to verify all three agents are registered."""
|
|
|
|
def test_all_agents_in_registry(self):
|
|
"""Test that explore, plan, and task agents are all registered."""
|
|
agents = list_agents()
|
|
names = [a["name"] for a in agents]
|
|
|
|
assert "explore" in names
|
|
assert "plan" in names
|
|
assert "task" in names
|
|
assert len(names) == 3
|
|
|
|
def test_agent_hierarchy(self):
|
|
"""Test the agent capability hierarchy."""
|
|
explore = get_agent("explore")
|
|
plan = get_agent("plan")
|
|
task = get_agent("task")
|
|
|
|
explore_tools = list(explore.agent._function_toolset.tools.keys())
|
|
plan_tools = list(plan.agent._function_toolset.tools.keys())
|
|
task_tools = list(task.agent._function_toolset.tools.keys())
|
|
|
|
# Explore has all tools (read + write)
|
|
assert "edit_file" in explore_tools
|
|
assert "write_file" in explore_tools
|
|
|
|
# Plan has read-only tools
|
|
assert "edit_file" not in plan_tools
|
|
assert "write_file" not in plan_tools
|
|
|
|
# Task has all tools plus spawn_agent
|
|
assert "edit_file" in task_tools
|
|
assert "write_file" in task_tools
|
|
assert "spawn_agent" in task_tools
|
|
|
|
# Only Task has spawn_agent
|
|
assert "spawn_agent" not in explore_tools
|
|
assert "spawn_agent" not in plan_tools
|