- Add PlanAgentImpl with read-only tools only - System prompts optimized for architecture planning - Outputs step-by-step implementation plans with critical files - 15 unit tests for registration, tools, and API - Update COVERAGE.md to ~70% complete Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
153 lines
4.9 KiB
Python
153 lines
4.9 KiB
Python
"""
|
|
Tests for the Plan agent.
|
|
|
|
Tests registration, API endpoints, and tool restrictions.
|
|
"""
|
|
import pytest
|
|
|
|
from src.domains.agents.base import get_agent, list_agents
|
|
from src.domains.agents.plan import plan_agent, PlanAgentImpl
|
|
|
|
|
|
class TestPlanAgentRegistration:
|
|
"""Tests for Plan agent registration."""
|
|
|
|
def test_plan_agent_registered(self):
|
|
"""Test that plan agent is registered in registry."""
|
|
agent = get_agent("plan")
|
|
assert agent is not None
|
|
assert agent.name == "plan"
|
|
|
|
def test_plan_agent_in_list(self):
|
|
"""Test that plan agent appears in agent list."""
|
|
agents = list_agents()
|
|
names = [a["name"] for a in agents]
|
|
assert "plan" in names
|
|
|
|
def test_plan_agent_has_description(self):
|
|
"""Test that plan agent has a description."""
|
|
agent = get_agent("plan")
|
|
assert agent is not None
|
|
assert len(agent.description) > 0
|
|
assert "plan" in agent.description.lower() or "architect" in agent.description.lower()
|
|
|
|
def test_plan_agent_singleton(self):
|
|
"""Test that plan_agent is the registered instance."""
|
|
registered = get_agent("plan")
|
|
assert registered is plan_agent
|
|
|
|
def test_plan_agent_is_correct_type(self):
|
|
"""Test that plan agent is correct implementation type."""
|
|
assert isinstance(plan_agent, PlanAgentImpl)
|
|
|
|
|
|
class TestPlanAgentTools:
|
|
"""Tests for Plan agent tool restrictions."""
|
|
|
|
def test_plan_agent_has_read_only_tools(self):
|
|
"""Test that plan agent has read-only tools."""
|
|
# Access the underlying PydanticAI agent to check tools
|
|
agent = plan_agent.agent
|
|
tool_names = list(agent._function_toolset.tools.keys())
|
|
|
|
# Should have read-only tools
|
|
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_plan_agent_no_write_tools(self):
|
|
"""Test that plan agent does NOT have write tools."""
|
|
agent = plan_agent.agent
|
|
tool_names = list(agent._function_toolset.tools.keys())
|
|
|
|
# Should NOT have write tools
|
|
assert "edit_file" not in tool_names
|
|
assert "write_file" not in tool_names
|
|
assert "bash" not in tool_names
|
|
assert "web_search" not in tool_names
|
|
|
|
def test_plan_agent_tool_count(self):
|
|
"""Test that plan agent has exactly 4 tools."""
|
|
agent = plan_agent.agent
|
|
tool_count = len(agent._function_toolset.tools)
|
|
assert tool_count == 4
|
|
|
|
|
|
class TestPlanAgentAPI:
|
|
"""Tests for Plan agent REST API."""
|
|
|
|
@pytest.mark.anyio
|
|
async def test_list_agents_includes_plan(self, auth_client):
|
|
"""Test that agent list includes plan agent."""
|
|
response = await auth_client.get("/agents/")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
names = [a["name"] for a in data["agents"]]
|
|
assert "plan" in names
|
|
|
|
@pytest.mark.anyio
|
|
async def test_get_plan_agent_info(self, auth_client):
|
|
"""Test getting plan agent info."""
|
|
response = await auth_client.get("/agents/plan")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["name"] == "plan"
|
|
assert "description" in data
|
|
assert len(data["description"]) > 0
|
|
|
|
@pytest.mark.anyio
|
|
async def test_run_plan_with_invalid_body(self, auth_client):
|
|
"""Test running plan agent with invalid request."""
|
|
response = await auth_client.post(
|
|
"/agents/run",
|
|
json={
|
|
"agent_type": "plan",
|
|
# Missing prompt
|
|
}
|
|
)
|
|
|
|
assert response.status_code == 422
|
|
|
|
@pytest.mark.anyio
|
|
async def test_stream_plan_with_invalid_body(self, auth_client):
|
|
"""Test streaming plan agent with invalid request."""
|
|
response = await auth_client.post(
|
|
"/agents/stream",
|
|
json={
|
|
"agent_type": "plan",
|
|
# Missing prompt
|
|
}
|
|
)
|
|
|
|
assert response.status_code == 422
|
|
|
|
|
|
class TestPlanAgentProperties:
|
|
"""Tests for Plan agent properties and configuration."""
|
|
|
|
def test_plan_agent_name(self):
|
|
"""Test plan agent name property."""
|
|
assert plan_agent.name == "plan"
|
|
|
|
def test_plan_agent_description_not_empty(self):
|
|
"""Test plan agent description is not empty."""
|
|
assert plan_agent.description
|
|
assert len(plan_agent.description) > 10
|
|
|
|
def test_plan_agent_creates_agent_lazily(self):
|
|
"""Test that PydanticAI agent is created lazily."""
|
|
# Create a fresh instance
|
|
fresh_agent = PlanAgentImpl()
|
|
|
|
# _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
|