feat: add Plan Agent for implementation planning
- 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>
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
"""
|
||||
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
|
||||
Reference in New Issue
Block a user