Files
temp/tests/features/steps/chain_agent_comprehensive_steps.py

439 lines
16 KiB
Python

"""Step definitions for comprehensive chain agent testing."""
import asyncio
from typing import Any
from typing import Dict
from unittest.mock import MagicMock
from unittest.mock import Mock
from behave import given
from behave import then
from behave import when
from behave.runner import Context
from cleveragents.agents.base import Agent
from cleveragents.agents.chain import ChainAgent
from cleveragents.core.exceptions import ConfigurationError
from cleveragents.templates.renderer import TemplateRenderer
@given("the chain agent test environment is initialized")
def step_init_chain_test_environment(context: Context):
"""Initialize the chain agent test environment."""
context.template_renderer = Mock(spec=TemplateRenderer)
context.configs = {}
context.agents = {}
context.errors = []
context.results = {}
context.template_store = {}
@given('I have a minimal chain agent configuration with name "{name}"')
def step_minimal_chain_config(context: Context, name: str):
"""Create a minimal chain agent configuration."""
context.configs["current"] = {"name": name, "config": {}}
@given("I have a chain agent configuration with steps:")
def step_chain_config_with_steps(context: Context):
"""Create a chain agent configuration with steps from table."""
steps = [row["step_name"] for row in context.table]
context.configs["current"] = {
"name": "test_chain_with_steps",
"config": {"steps": steps},
}
@given('I have a chain agent configuration with prompt "{prompt}"')
def step_chain_config_with_prompt(context: Context, prompt: str):
"""Create a chain agent configuration with inline prompt."""
context.configs["current"] = {
"name": "test_chain_with_prompt",
"config": {"prompt": prompt},
}
@given('I have a template store with a template "{template_name}"')
def step_create_template_in_store(context: Context, template_name: str):
"""Create a template in the template store."""
context.template_store[template_name] = "Template content for " + template_name
# Configure the mock template renderer
def get_template_side_effect(name):
if name in context.template_store:
return context.template_store[name]
raise ValueError(f"Template '{name}' not found")
context.template_renderer.get_template.side_effect = get_template_side_effect
@given('I have a template store with template "{name}" containing "{content}"')
def step_create_template_with_content(context: Context, name: str, content: str):
"""Create a template with specific content."""
context.template_store[name] = content
def get_template_side_effect(template_name):
if template_name in context.template_store:
return context.template_store[template_name]
raise ValueError(f"Template '{template_name}' not found")
context.template_renderer.get_template.side_effect = get_template_side_effect
@given('I have a chain agent configuration with prompt_reference "{ref}"')
def step_chain_config_with_prompt_ref(context: Context, ref: str):
"""Create a chain agent configuration with prompt reference."""
context.configs["current"] = {
"name": "test_chain_with_ref",
"config": {"prompt_reference": ref},
}
@given("I have a chain agent configuration with both prompt and prompt_reference")
def step_chain_config_with_both_prompts(context: Context):
"""Create a chain agent configuration with both prompt types."""
context.configs["current"] = {
"name": "test_chain_invalid",
"config": {"prompt": "Inline prompt", "prompt_reference": "some_ref"},
}
@given("I have a chain agent with no prompt and no steps")
def step_create_basic_chain_agent(context: Context):
"""Create a basic chain agent with no configuration."""
config = {}
context.agents["current"] = ChainAgent(
name="basic_chain", config=config, template_renderer=context.template_renderer
)
@given("I have a chain agent with steps {steps}")
def step_create_chain_agent_with_steps(context: Context, steps: str):
"""Create a chain agent with specified steps."""
import json
steps_list = json.loads(steps)
config = {"steps": steps_list}
context.agents["current"] = ChainAgent(
name="chain_with_steps",
config=config,
template_renderer=context.template_renderer,
)
@given('I have a chain agent with prompt "{prompt}"')
def step_create_chain_agent_with_prompt(context: Context, prompt: str):
"""Create a chain agent with inline prompt."""
config = {"prompt": prompt}
# Mock the render_string method
def render_string_side_effect(template, ctx, source_description=""):
# Simple template rendering simulation
result = template
for key, value in ctx.items():
result = result.replace(f"{{{key}}}", str(value))
return result
context.template_renderer.render_string.side_effect = render_string_side_effect
context.agents["current"] = ChainAgent(
name="chain_with_prompt",
config=config,
template_renderer=context.template_renderer,
)
@given('I have a chain agent with prompt "{prompt}" and steps {steps}')
def step_create_chain_agent_with_prompt_and_steps(
context: Context, prompt: str, steps: str
):
"""Create a chain agent with prompt and steps."""
import json
steps_list = json.loads(steps)
config = {"prompt": prompt, "steps": steps_list}
# Mock the render_string method
def render_string_side_effect(template, ctx, source_description=""):
result = template
for key, value in ctx.items():
result = result.replace(f"{{{key}}}", str(value))
return result
context.template_renderer.render_string.side_effect = render_string_side_effect
context.agents["current"] = ChainAgent(
name="chain_with_both",
config=config,
template_renderer=context.template_renderer,
)
@given('I have a chain agent using prompt_reference "{ref}"')
def step_create_chain_agent_with_ref(context: Context, ref: str):
"""Create a chain agent using prompt reference."""
config = {"prompt_reference": ref}
context.agents["current"] = ChainAgent(
name="chain_with_ref",
config=config,
template_renderer=context.template_renderer,
)
# Mock the render_string to handle the referenced template
def render_string_side_effect(template, ctx, source_description=""):
result = template
for key, value in ctx.items():
result = result.replace(f"{{{key}}}", str(value))
return result
context.template_renderer.render_string.side_effect = render_string_side_effect
@given("I have any chain agent")
def step_create_any_chain_agent(context: Context):
"""Create any chain agent for testing."""
context.agents["current"] = ChainAgent(
name="any_chain", config={}, template_renderer=context.template_renderer
)
@given("I have a chain agent instance")
def step_create_chain_instance(context: Context):
"""Create a chain agent instance for inheritance testing."""
context.agents["current"] = ChainAgent(
name="inheritance_test",
config={"steps": ["test"]},
template_renderer=context.template_renderer,
)
@when("I create a chain agent from the configuration")
def step_create_chain_from_config(context: Context):
"""Create a chain agent from the current configuration."""
try:
config_data = context.configs["current"]
context.agents["created"] = ChainAgent(
name=config_data["name"],
config=config_data["config"],
template_renderer=context.template_renderer,
)
except Exception as e:
context.errors.append(e)
@when("I try to create a chain agent from the configuration")
def step_try_create_chain_from_config(context: Context):
"""Try to create a chain agent, expecting possible errors."""
try:
config_data = context.configs["current"]
context.agents["created"] = ChainAgent(
name=config_data["name"],
config=config_data["config"],
template_renderer=context.template_renderer,
)
except ConfigurationError as e:
context.errors.append(e)
except Exception as e:
context.errors.append(e)
@when('I process the message "{message}"')
def step_process_message(context: Context, message: str):
"""Process a message through the current agent."""
agent = context.agents["current"]
# Run async method in sync context
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
context.results["process"] = loop.run_until_complete(
agent.process_message(message)
)
finally:
loop.close()
@when('I process the message "{message}" with context {context_json}')
def step_process_message_with_context(
context: Context, message: str, context_json: str
):
"""Process a message with additional context."""
import json
additional_context = json.loads(context_json)
context.processing_context = additional_context.copy()
agent = context.agents["current"]
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
context.results["process"] = loop.run_until_complete(
agent.process_message(message, additional_context)
)
finally:
loop.close()
@when("I get the agent capabilities")
def step_get_capabilities(context: Context):
"""Get the capabilities of the current agent."""
agent = context.agents["current"]
context.results["capabilities"] = agent.get_capabilities()
@when('I call the process method directly with "{message}"')
def step_call_process_method(context: Context, message: str):
"""Call the process method directly."""
agent = context.agents["current"]
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
context.results["process"] = loop.run_until_complete(agent.process(message))
finally:
loop.close()
@when("I check the agent inheritance")
def step_check_inheritance(context: Context):
"""Check the inheritance of the current agent."""
agent = context.agents["current"]
context.results["inheritance"] = {
"is_agent": isinstance(agent, Agent),
"has_name": hasattr(agent, "name"),
"has_config": hasattr(agent, "config"),
"has_template_renderer": hasattr(agent, "template_renderer"),
"name_value": getattr(agent, "name", None),
"config_value": getattr(agent, "config", None),
}
@then("the chain agent should be created successfully")
def step_verify_agent_created(context: Context):
"""Verify the agent was created successfully."""
assert len(context.errors) == 0, f"Unexpected errors: {context.errors}"
assert "created" in context.agents
assert isinstance(context.agents["created"], ChainAgent)
@then('the chain agent name should be "{expected_name}"')
def step_verify_agent_name(context: Context, expected_name: str):
"""Verify the agent name."""
agent = context.agents["created"]
assert agent.name == expected_name
@then("the chain agent steps should be empty")
def step_verify_empty_steps(context: Context):
"""Verify the agent has no steps."""
agent = context.agents["created"]
assert agent.steps == []
@then("the chain agent should have {count:d} steps")
def step_verify_step_count(context: Context, count: int):
"""Verify the agent step count."""
agent = context.agents["created"]
assert len(agent.steps) == count
@then('the chain agent prompt template should be "{expected}"')
def step_verify_prompt_template(context: Context, expected: str):
"""Verify the agent prompt template."""
agent = context.agents["created"]
assert agent.prompt_template == expected
@then("the chain agent should use the referenced template")
def step_verify_referenced_template(context: Context):
"""Verify the agent uses a referenced template."""
agent = context.agents["created"]
# The prompt_template should be set to the content from get_template
assert agent.prompt_template is not None
assert context.template_renderer.get_template.called
@then('a ConfigurationError should be raised with message containing "{text}"')
def step_verify_config_error(context: Context, text: str):
"""Verify a ConfigurationError was raised with specific message."""
assert len(context.errors) > 0, "Expected an error but none was raised"
error = context.errors[-1]
assert isinstance(error, ConfigurationError)
assert text in str(error)
@then('the chain result should be "{expected}"')
def step_verify_chain_result(context: Context, expected: str):
"""Verify the chain processing result."""
assert context.results["process"] == expected
@then('the chain result should contain "{expected}"')
def step_verify_chain_result_contains(context: Context, expected: str):
"""Verify the chain result contains expected text."""
assert expected in context.results["process"]
@then('the prompt should be rendered with message "{message}"')
def step_verify_prompt_rendered(context: Context, message: str):
"""Verify the prompt was rendered with the message."""
# Check that render_string was called with the message in context
assert context.template_renderer.render_string.called
call_args = context.template_renderer.render_string.call_args
assert call_args[0][1].get("message") == message
@then("the prompt should be rendered with context")
def step_verify_prompt_rendered_with_context(context: Context):
"""Verify the prompt was rendered with context."""
assert context.template_renderer.render_string.called
@then('the context message should be set to "{expected}"')
def step_verify_context_message(context: Context, expected: str):
"""Verify the context message value."""
# The message parameter should override context
if context.template_renderer.render_string.called:
call_args = context.template_renderer.render_string.call_args
if call_args and len(call_args[0]) > 1:
# The render_context should have the message set to the expected value
render_context = call_args[0][1]
assert (
render_context.get("message") == expected
), f"Expected message '{expected}', got '{render_context.get('message')}'"
else:
assert False, "render_string was called but without expected arguments"
else:
assert False, "render_string was not called"
@then("the capabilities should be {expected}")
def step_verify_capabilities(context: Context, expected: str):
"""Verify the agent capabilities."""
import json
expected_list = json.loads(expected)
assert context.results["capabilities"] == expected_list
@then("the agent should be an instance of Agent base class")
def step_verify_agent_inheritance(context: Context):
"""Verify the agent inherits from Agent."""
assert context.results["inheritance"]["is_agent"] is True
@then("the agent should have name attribute")
def step_verify_has_name_attr(context: Context):
"""Verify the agent has name attribute."""
assert context.results["inheritance"]["has_name"] is True
@then("the agent should have config attribute")
def step_verify_has_config_attr(context: Context):
"""Verify the agent has config attribute."""
assert context.results["inheritance"]["has_config"] is True
@then("the agent should have template_renderer attribute")
def step_verify_has_renderer_attr(context: Context):
"""Verify the agent has template_renderer attribute."""
assert context.results["inheritance"]["has_template_renderer"] is True