Skip to content

Commit 9043537

Browse files
committed
tests: Add real tests for configured starlette app
1 parent 29b9034 commit 9043537

File tree

3 files changed

+171
-37
lines changed

3 files changed

+171
-37
lines changed

adk/tests/test_a2a_starlette.py

Lines changed: 142 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,156 @@
1+
import uuid
2+
from typing import Any
3+
14
import pytest
25
from agenticlayer.agent_to_a2a import to_a2a
3-
from google.adk.agents.base_agent import BaseAgent
4-
from starlette.applications import Starlette
6+
from agenticlayer.config import parse_sub_agents, parse_tools
7+
from google.adk.agents.llm_agent import LlmAgent
8+
from google.adk.models.lite_llm import LiteLlm
59
from starlette.testclient import TestClient
610

711

8-
class TestA2AStarlette:
9-
"""Test suite for the a2a_starlette module."""
12+
def create_mock_agent_card(
13+
agent_name: str,
14+
base_url: str,
15+
skills: list[dict[str, Any]] | None = None,
16+
) -> dict[str, Any]:
17+
"""Helper function to create a valid agent card response."""
18+
return {
19+
"name": agent_name,
20+
"description": f"Mock agent {agent_name}",
21+
"url": base_url,
22+
"version": "1.0.0",
23+
"capabilities": {},
24+
"skills": skills or [],
25+
"default_input_modes": ["text/plain"],
26+
"default_output_modes": ["text/plain"],
27+
"supports_authenticated_extended_card": False,
28+
}
29+
1030

11-
@pytest.fixture
12-
def test_agent(self) -> BaseAgent:
13-
"""Create a test agent for testing."""
14-
return BaseAgent(name="test_agent")
31+
def create_send_message_request(
32+
message_text: str = "Hello, agent!",
33+
) -> dict[str, Any]:
34+
"""Helper function to create a valid A2A send message request."""
35+
message_id = str(uuid.uuid4())
36+
context_id = str(uuid.uuid4())
37+
return {
38+
"jsonrpc": "2.0",
39+
"id": 1,
40+
"method": "message/send",
41+
"params": {
42+
"message": {
43+
"role": "user",
44+
"parts": [{"kind": "text", "text": message_text}],
45+
"messageId": message_id,
46+
"contextId": context_id,
47+
},
48+
"metadata": {},
49+
},
50+
}
1551

16-
@pytest.fixture
17-
def starlette_app(self, test_agent: BaseAgent) -> Starlette:
18-
"""Create a Starlette app with the test agent."""
19-
return to_a2a(test_agent)
2052

21-
@pytest.fixture
22-
def client(self, starlette_app: Starlette) -> TestClient:
23-
"""Create a test client."""
24-
return TestClient(starlette_app)
53+
def create_agent(
54+
name: str = "test_agent",
55+
sub_agents_config: str = "{}",
56+
tools_config: str = "{}",
57+
) -> LlmAgent:
58+
sub_agents, agent_tools = parse_sub_agents(sub_agents_config)
59+
mcp_tools = parse_tools(tools_config)
60+
tools = [*agent_tools, *mcp_tools]
61+
return LlmAgent(
62+
name=name,
63+
model=LiteLlm(model="gemini/gemini-2.5-flash"),
64+
description="Test agent",
65+
instruction="You are a test agent.",
66+
sub_agents=sub_agents,
67+
tools=tools,
68+
)
2569

26-
def test_agent_card_endpoint(self, starlette_app: Starlette, client: TestClient) -> None:
70+
71+
class TestA2AStarlette:
72+
@pytest.mark.asyncio
73+
async def test_agent_card(self) -> None:
2774
"""Test that the agent card is available at /.well-known/agent-card.json"""
2875

29-
# Try the standard agent card endpoint
76+
# Given:
77+
agent = create_agent()
78+
app = await to_a2a(agent)
79+
client = TestClient(app)
80+
81+
# When: Requesting the agent card endpoint
3082
response = client.get("/.well-known/agent-card.json")
3183

32-
if response.status_code == 200:
33-
# Great! We found the agent card
34-
data = response.json()
35-
assert isinstance(data, dict), "Agent card should return a JSON object"
84+
# Then: Agent card is returned
85+
assert response.status_code == 200
86+
data = response.json()
87+
assert isinstance(data, dict), "Agent card should return a JSON object"
88+
assert data.get("name") == agent.name
89+
assert data.get("description") == agent.description
90+
91+
@pytest.mark.asyncio
92+
async def test_agent_rpc_send_message(self) -> None:
93+
"""Test that the RPC url is working for send message."""
94+
95+
# Given:
96+
agent = create_agent()
97+
app = await to_a2a(agent)
98+
client = TestClient(app)
99+
100+
# When: Sending an A2A RPC request
101+
rpc_response = client.post("", json=create_send_message_request())
102+
103+
# Then: RPC response is returned
104+
assert rpc_response.status_code == 200
105+
rpc_data = rpc_response.json()
106+
assert rpc_data.get("jsonrpc") == "2.0"
107+
assert rpc_data.get("id") == 1
108+
109+
@pytest.mark.asyncio
110+
async def test_sub_agents(self) -> None:
111+
"""Test that sub-agents are parsed and integrated correctly."""
112+
113+
# When: Creating an agent with sub-agents
114+
sub_agents_config = """{
115+
"sub_agent_1": {
116+
"url": "http://sub-agent-1.local/.well-known/agent-card.json",
117+
"interaction_type": "transfer"
118+
},
119+
"sub_agent_2": {
120+
"url": "http://sub-agent-2.local/.well-known/agent-card.json",
121+
"interaction_type": "tool_call"
122+
}
123+
}"""
124+
agent = create_agent(sub_agents_config=sub_agents_config)
125+
126+
# Then: Verify sub-agents and tools are parsed correctly
127+
assert len(agent.sub_agents) == 1, "There should be 1 sub-agent for transfer interaction type"
128+
assert len(agent.tools) == 1, "There should be 1 agent tool for tool_call interaction type"
129+
130+
# When: Requesting the agent card endpoint
131+
app = await to_a2a(agent)
132+
client = TestClient(app)
133+
response = client.get("/.well-known/agent-card.json")
134+
135+
# Then: Agent card is returned
136+
assert response.status_code == 200
137+
138+
@pytest.mark.asyncio
139+
async def test_tools(self) -> None:
140+
"""Test that tools are parsed and integrated correctly."""
141+
142+
# When: Creating an agent with tools
143+
tools_config = """{
144+
"tool_1": {
145+
"url": "http://tool-1.local/mcp"
146+
},
147+
"tool_2": {
148+
"url": "http://tool-2.local/mcp"
149+
}
150+
}"""
151+
tools = parse_tools(tools_config)
152+
153+
# Then: Verify McpToolsets are created correctly
154+
assert len(tools) == 2, "There should be 2 McpToolset tools"
36155

37-
# Verify it contains expected agent card fields
38-
assert len(data) > 0, "Agent card should not be empty"
156+
# Note: Further integration tests would require mocking MCP tool behavior

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ dev = [
1616
"pytest>=9.0.1,<10",
1717
"pytest-cov>=7,<8",
1818
"types-protobuf>=6.30.2.20250822",
19+
"pytest-asyncio>=1.3.0",
1920
]
2021

2122

@@ -38,7 +39,7 @@ build-backend = "hatchling.build"
3839

3940
[tool.pytest.ini_options]
4041
minversion = "7.0"
41-
testpaths = "test"
42+
testpaths = "adk/tests"
4243
pythonpath = ["."]
4344
addopts = """\
4445
--strict-config \

uv.lock

Lines changed: 27 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)