Skip to content

Commit 0bc2d92

Browse files
forward port of testing framework
1 parent 11d0dfe commit 0bc2d92

File tree

9 files changed

+564
-317
lines changed

9 files changed

+564
-317
lines changed

src/agentex/lib/cli/commands/init.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ def create_project_structure(path: Path, context: Dict[str, Any], template_type:
4545
# Create __init__.py
4646
(code_dir / "__init__.py").touch()
4747

48+
# Create tests directory
49+
tests_dir: Path = project_dir / "tests"
50+
tests_dir.mkdir(parents=True, exist_ok=True)
51+
52+
# Create tests/__init__.py
53+
(tests_dir / "__init__.py").touch()
54+
4855
# Define project files based on template type
4956
project_files = {
5057
TemplateType.TEMPORAL: ["acp.py", "workflow.py", "run_worker.py"],
@@ -77,13 +84,15 @@ def create_project_structure(path: Path, context: Dict[str, Any], template_type:
7784
# Add development notebook for agents
7885
root_templates["dev.ipynb.j2"] = "dev.ipynb"
7986

80-
# Add test file
81-
root_templates["test_agent.py.j2"] = "test_agent.py"
82-
8387
for template, output in root_templates.items():
8488
output_path = project_dir / output
8589
output_path.write_text(render_template(template, context, template_type))
8690

91+
# Create test file in tests/ directory
92+
test_template_path = "test_agent.py.j2"
93+
test_output_path = tests_dir / "test_agent.py"
94+
test_output_path.write_text(render_template(test_template_path, context, template_type))
95+
8796
console.print(f"\n[green]✓[/green] Created project structure at: {project_dir}")
8897

8998

@@ -219,7 +228,7 @@ def validate_agent_name(text: str) -> bool | str:
219228
console.print(" agentex agents run --manifest manifest.yaml")
220229

221230
console.print("5. Test your agent:")
222-
console.print(" pytest test_agent.py -v")
231+
console.print(" pytest tests/test_agent.py -v")
223232

224233
console.print("6. Deploy your agent:")
225234
console.print(" agentex agents deploy --cluster your-cluster --namespace your-namespace")
Lines changed: 98 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -1,147 +1,112 @@
11
"""
2-
Sample tests for AgentEx ACP agent.
2+
Tests for {{ agent_name }}
33

4-
This test suite demonstrates how to test the main AgentEx API functions:
5-
- Non-streaming event sending and polling
6-
- Streaming event sending
4+
This test suite demonstrates testing your agentic agent with the AgentEx testing framework.
75

8-
To run these tests:
9-
1. Make sure the agent is running (via docker-compose or `agentex agents run`)
10-
2. Set the AGENTEX_API_BASE_URL environment variable if not using default
11-
3. Run: pytest test_agent.py -v
6+
Test coverage:
7+
- Basic event sending and polling
8+
- Streaming responses
9+
- Multi-turn conversations
1210

13-
Configuration:
14-
- AGENTEX_API_BASE_URL: Base URL for the AgentEx server (default: http://localhost:5003)
15-
- AGENT_NAME: Name of the agent to test (default: {{ agent_name }})
11+
Prerequisites:
12+
- AgentEx services running (make dev)
13+
- Agent running: agentex agents run --manifest manifest.yaml
14+
15+
Run tests:
16+
pytest tests/test_agent.py -v
1617
"""
1718

18-
import os
19-
import uuid
20-
import asyncio
2119
import pytest
22-
import pytest_asyncio
23-
from agentex import AsyncAgentex
24-
from agentex.types import TaskMessage
25-
from agentex.types.agent_rpc_params import ParamsCreateTaskRequest
26-
from agentex.types.text_content_param import TextContentParam
27-
from test_utils.agentic import (
28-
poll_for_agent_response,
29-
send_event_and_poll_yielding,
20+
21+
from agentex.lib.testing import (
22+
test_agentic_agent,
23+
assert_valid_agent_response,
24+
assert_agent_response_contains,
3025
stream_agent_response,
31-
validate_text_in_response,
32-
poll_messages,
26+
stream_task_messages,
3327
)
3428

29+
AGENT_NAME = "{{ agent_name }}"
30+
31+
32+
@pytest.mark.asyncio
33+
async def test_agent_basic_response():
34+
"""Test that agent responds to basic events."""
35+
async with test_agentic_agent(agent_name=AGENT_NAME) as test:
36+
response = await test.send_event(
37+
"Hello! Please respond briefly.",
38+
timeout_seconds=30.0
39+
)
40+
41+
assert_valid_agent_response(response)
42+
assert len(response.content) > 0
43+
print(f"✓ Agent responded: {response.content[:80]}...")
44+
45+
46+
@pytest.mark.asyncio
47+
async def test_agent_multi_turn():
48+
"""Test multi-turn conversation."""
49+
async with test_agentic_agent(agent_name=AGENT_NAME) as test:
50+
# Turn 1
51+
response1 = await test.send_event("Hello!", timeout_seconds=30.0)
52+
assert_valid_agent_response(response1)
53+
54+
# Turn 2
55+
response2 = await test.send_event("How are you?", timeout_seconds=30.0)
56+
assert_valid_agent_response(response2)
57+
58+
# Turn 3
59+
response3 = await test.send_event("Thank you!", timeout_seconds=30.0)
60+
assert_valid_agent_response(response3)
61+
62+
# Verify history
63+
history = await test.get_conversation_history()
64+
assert len(history) >= 6, f"Expected >= 6 messages, got {len(history)}"
65+
print(f"✓ Conversation: {len(history)} messages")
66+
67+
68+
@pytest.mark.asyncio
69+
async def test_agent_streaming():
70+
"""Test streaming responses from agent."""
71+
async with test_agentic_agent(agent_name=AGENT_NAME) as test:
72+
# Send event first
73+
await test.send_event("Start streaming task", timeout_seconds=10.0)
74+
75+
# Now stream subsequent events
76+
events_received = []
77+
async for event in test.send_event_and_stream("Stream this response", timeout_seconds=30.0):
78+
events_received.append(event)
79+
event_type = event.get('type')
80+
81+
if event_type == 'done':
82+
print(f"✓ Stream complete ({len(events_received)} events)")
83+
break
84+
85+
assert len(events_received) > 0, "Should receive at least one event"
86+
print(f"✓ Streaming works ({len(events_received)} events received)")
87+
88+
89+
@pytest.mark.asyncio
90+
async def test_agent_custom_scenario():
91+
"""
92+
Add your custom test scenarios here.
93+
94+
Customize this test for your agent's specific behavior and requirements.
95+
"""
96+
async with test_agentic_agent(agent_name=AGENT_NAME) as test:
97+
# Example: Test specific functionality
98+
response = await test.send_event(
99+
"Your custom test message here",
100+
timeout_seconds=30.0
101+
)
102+
103+
assert_valid_agent_response(response)
35104

36-
# Configuration from environment variables
37-
AGENTEX_API_BASE_URL = os.environ.get("AGENTEX_API_BASE_URL", "http://localhost:5003")
38-
AGENT_NAME = os.environ.get("AGENT_NAME", "{{ agent_name }}")
39-
40-
41-
@pytest_asyncio.fixture
42-
async def client():
43-
"""Create an AsyncAgentex client instance for testing."""
44-
client = AsyncAgentex(base_url=AGENTEX_API_BASE_URL)
45-
yield client
46-
await client.close()
47-
48-
49-
@pytest.fixture
50-
def agent_name():
51-
"""Return the agent name for testing."""
52-
return AGENT_NAME
53-
54-
55-
@pytest_asyncio.fixture
56-
async def agent_id(client, agent_name):
57-
"""Retrieve the agent ID based on the agent name."""
58-
agents = await client.agents.list()
59-
for agent in agents:
60-
if agent.name == agent_name:
61-
return agent.id
62-
raise ValueError(f"Agent with name {agent_name} not found.")
63-
64-
65-
class TestNonStreamingEvents:
66-
"""Test non-streaming event sending and polling."""
67-
68-
@pytest.mark.asyncio
69-
async def test_send_event_and_poll(self, client: AsyncAgentex, _agent_name: str, agent_id: str):
70-
"""Test sending an event and polling for the response."""
71-
# TODO: Create a task for this conversation
72-
# task_response = await client.agents.create_task(agent_id, params=ParamsCreateTaskRequest(name=uuid.uuid1().hex))
73-
# task = task_response.result
74-
# assert task is not None
75-
76-
# TODO: Poll for the initial task creation message (if your agent sends one)
77-
# async for message in poll_messages(
78-
# client=client,
79-
# task_id=task.id,
80-
# timeout=30,
81-
# sleep_interval=1.0,
82-
# ):
83-
# assert isinstance(message, TaskMessage)
84-
# if message.content and message.content.type == "text" and message.content.author == "agent":
85-
# # Check for your expected initial message
86-
# assert "expected initial text" in message.content.content
87-
# break
88-
89-
# TODO: Send an event and poll for response using the yielding helper function
90-
# user_message = "Your test message here"
91-
# async for message in send_event_and_poll_yielding(
92-
# client=client,
93-
# agent_id=agent_id,
94-
# task_id=task.id,
95-
# user_message=user_message,
96-
# timeout=30,
97-
# sleep_interval=1.0,
98-
# ):
99-
# assert isinstance(message, TaskMessage)
100-
# if message.content and message.content.type == "text" and message.content.author == "agent":
101-
# # Check for your expected response
102-
# assert "expected response text" in message.content.content
103-
# break
104-
pass
105-
106-
107-
class TestStreamingEvents:
108-
"""Test streaming event sending."""
109-
110-
@pytest.mark.asyncio
111-
async def test_send_event_and_stream(self, client: AsyncAgentex, _agent_name: str, agent_id: str):
112-
"""Test sending an event and streaming the response."""
113-
# TODO: Create a task for this conversation
114-
# task_response = await client.agents.create_task(agent_id, params=ParamsCreateTaskRequest(name=uuid.uuid1().hex))
115-
# task = task_response.result
116-
# assert task is not None
117-
118-
# user_message = "Your test message here"
119-
120-
# # Collect events from stream
121-
# all_events = []
122-
123-
# async def collect_stream_events():
124-
# async for event in stream_agent_response(
125-
# client=client,
126-
# task_id=task.id,
127-
# timeout=30,
128-
# ):
129-
# all_events.append(event)
130-
131-
# # Start streaming task
132-
# stream_task = asyncio.create_task(collect_stream_events())
133-
134-
# # Send the event
135-
# event_content = TextContentParam(type="text", author="user", content=user_message)
136-
# await client.agents.send_event(agent_id=agent_id, params={"task_id": task.id, "content": event_content})
137-
138-
# # Wait for streaming to complete
139-
# await stream_task
140-
141-
# # TODO: Add your validation here
142-
# assert len(all_events) > 0, "No events received in streaming response"
143-
pass
105+
# Add assertions specific to your agent's expected behavior
106+
# assert_agent_response_contains(response, "expected text")
107+
# assert len(response.content) > 100, "Response should be detailed"
144108

145109

146110
if __name__ == "__main__":
147-
pytest.main([__file__, "-v"])
111+
print(f"Run with: pytest tests/test_agent.py -v")
112+
print(f"Testing agent: {AGENT_NAME}")

0 commit comments

Comments
 (0)