5bbda89386
- Fix bare excepts (replace with except Exception:) - Remove wildcard star imports from test step files - Add explicit imports for ConfigurationError, AgentCreationError - Add 'from err' to raise statements in except blocks - Fix loop variable binding with default arguments (B023) - Organize imports, fix trailing whitespace and blank line whitespace - Bump Python to 3.13 and update tool configurations
347 lines
11 KiB
Python
347 lines
11 KiB
Python
"""Step definitions for complex template processing features."""
|
|
|
|
import json
|
|
|
|
import yaml
|
|
from behave import given, then, when
|
|
|
|
from cleveractors.templates.base import TemplateType
|
|
from cleveractors.templates.enhanced_registry import EnhancedTemplateRegistry
|
|
from cleveractors.templates.yaml_preprocessor import YAMLTemplateProcessor
|
|
|
|
|
|
@given("the template processing system is initialized")
|
|
def step_init_template_system(context):
|
|
"""Initialize template processing system."""
|
|
context.processor = YAMLTemplateProcessor()
|
|
context.registry = EnhancedTemplateRegistry()
|
|
|
|
|
|
@given("I have a YAML configuration with templates")
|
|
def step_yaml_templates(context):
|
|
"""Store YAML with templates."""
|
|
context.yaml_content = context.text
|
|
|
|
|
|
@given("I have a YAML configuration with complex templates")
|
|
def step_yaml_complex_templates(context):
|
|
"""Store YAML with complex templates."""
|
|
context.yaml_content = context.text
|
|
|
|
|
|
@given("I have a configuration with template definitions")
|
|
def step_config_template_defs(context):
|
|
"""Store configuration with template definitions."""
|
|
context.yaml_content = context.text
|
|
|
|
|
|
@given("I have registered a complex template")
|
|
def step_register_template(context):
|
|
"""Register a complex template."""
|
|
template_yaml = context.text
|
|
context.registry.register_template_string(
|
|
TemplateType.AGENT, "pipeline_template", template_yaml
|
|
)
|
|
|
|
|
|
@given("I have a YAML with Jinja2 filters")
|
|
def step_yaml_with_filters(context):
|
|
"""Store YAML with Jinja2 filters."""
|
|
context.yaml_content = context.text
|
|
|
|
|
|
@given("I have YAML with edge case scenarios")
|
|
def step_yaml_edge_cases(context):
|
|
"""Store YAML with edge cases."""
|
|
context.yaml_content = context.text
|
|
|
|
|
|
@given("I have a template context")
|
|
def step_create_context(context):
|
|
"""Create template context from table."""
|
|
context.template_context = {}
|
|
for row in context.table:
|
|
key = row["key"]
|
|
value = row["value"]
|
|
|
|
# Parse JSON values
|
|
if value.startswith("[") or value.startswith("{"):
|
|
value = json.loads(value)
|
|
else:
|
|
# Try numeric conversion
|
|
try:
|
|
value = float(value)
|
|
if value.is_integer():
|
|
value = int(value)
|
|
except ValueError:
|
|
# Keep as string
|
|
pass
|
|
|
|
context.template_context[key] = value
|
|
|
|
|
|
@given("I have a context with services and features")
|
|
def step_context_services_features(context):
|
|
"""Create context with services and features."""
|
|
context.template_context = {
|
|
"project_name": "MyApp",
|
|
"version": "2.0.0",
|
|
"debug_mode": True,
|
|
"services": [
|
|
{"name": "api", "port": 8080, "replicas": 3},
|
|
{"name": "web", "port": 3000, "enabled": True},
|
|
{"name": "worker", "port": 9000, "replicas": 2},
|
|
],
|
|
"enable_advanced": True,
|
|
"analytics_level": "detailed",
|
|
}
|
|
|
|
|
|
@given("I have a context with Unicode messages and nested data")
|
|
def step_context_unicode_nested(context):
|
|
"""Create context with Unicode and nested data."""
|
|
context.template_context = {
|
|
"messages": [
|
|
{"text": "Hello", "emoji": "👋"},
|
|
{"text": "Ça va?", "emoji": "🇫🇷"},
|
|
{"text": "你好", "emoji": "🇨🇳"},
|
|
],
|
|
"items": [
|
|
{"name": "service1", "config": {"port": 8080, "enabled": True}},
|
|
{"name": "service2", "config": {"port": 9090, "workers": 4}},
|
|
{"name": "service3", "config": None},
|
|
],
|
|
}
|
|
|
|
|
|
@when("I process the YAML with the template processor")
|
|
def step_process_yaml_processor(context):
|
|
"""Process YAML with template processor."""
|
|
context.result = context.processor.process_string(
|
|
context.yaml_content, context.template_context
|
|
)
|
|
|
|
|
|
@when("I parse the template configuration")
|
|
def step_parse_config(context):
|
|
"""Parse configuration."""
|
|
# For template definitions, just parse as YAML
|
|
context.result = yaml.safe_load(context.yaml_content)
|
|
|
|
|
|
@when("I instantiate the template with parameters")
|
|
def step_instantiate_template(context):
|
|
"""Instantiate template with parameters."""
|
|
params = {}
|
|
for row in context.table:
|
|
key = row["key"]
|
|
value = row["value"]
|
|
|
|
# Parse JSON values
|
|
if value.startswith("[") or value.startswith("{"):
|
|
value = json.loads(value)
|
|
elif value.lower() == "true":
|
|
value = True
|
|
elif value.lower() == "false":
|
|
value = False
|
|
|
|
params[key] = value
|
|
|
|
context.result = context.registry.instantiate(
|
|
TemplateType.AGENT, "pipeline_template", params
|
|
)
|
|
|
|
|
|
@when("I process the configuration")
|
|
def step_process_config(context):
|
|
"""Process configuration."""
|
|
context.result = context.processor.process_string(
|
|
context.yaml_content, context.template_context
|
|
)
|
|
|
|
|
|
@when("I process the edge cases")
|
|
def step_process_edge_cases(context):
|
|
"""Process edge case YAML."""
|
|
context.result = context.processor.process_string(
|
|
context.yaml_content, context.template_context
|
|
)
|
|
|
|
|
|
@then("the result should have {count:d} agents")
|
|
def step_check_agent_count_simple(context, count):
|
|
"""Check agent count."""
|
|
assert "agents" in context.result
|
|
assert len(context.result["agents"]) == count
|
|
|
|
|
|
@then("the agents section should contain {agent_list}")
|
|
def step_check_agents_list(context, agent_list):
|
|
"""Check agents section contains specific agents."""
|
|
# Parse agent list - handle both quoted and unquoted names
|
|
agents = []
|
|
for part in agent_list.split(","):
|
|
agent = part.strip().strip('"').strip("'")
|
|
agents.append(agent)
|
|
|
|
assert "agents" in context.result
|
|
for agent in agents:
|
|
assert agent in context.result["agents"], f"Agent '{agent}' not found"
|
|
|
|
|
|
@then('each agent should have model "{model}" and temperature {temp:f}')
|
|
def step_check_agent_config(context, model, temp):
|
|
"""Check agent configurations."""
|
|
for agent_name, agent_config in context.result["agents"].items():
|
|
if agent_name.endswith("_agent"):
|
|
assert agent_config["config"]["model"] == model
|
|
assert agent_config["config"]["temperature"] == temp
|
|
|
|
|
|
@then("the graph should have {nodes:d} nodes and {edges:d} edges")
|
|
def step_check_graph_structure(context, nodes, edges):
|
|
"""Check graph node and edge count."""
|
|
assert "graph" in context.result
|
|
assert len(context.result["graph"]["nodes"]) == nodes
|
|
assert len(context.result["graph"]["edges"]) == edges
|
|
|
|
|
|
@then('templates should contain "{template_name}"')
|
|
def step_check_template_exists(context, template_name):
|
|
"""Check template exists."""
|
|
assert "templates" in context.result
|
|
assert "agents" in context.result["templates"]
|
|
assert template_name in context.result["templates"]["agents"]
|
|
|
|
|
|
@then('template_strings should contain "{template_name}"')
|
|
def step_check_template_string_exists(context, template_name):
|
|
"""Check template string exists."""
|
|
assert "template_strings" in context.result
|
|
assert "agents" in context.result["template_strings"]
|
|
assert template_name in context.result["template_strings"]["agents"]
|
|
|
|
|
|
@then("agents should reference the dynamic_team template")
|
|
def step_check_agent_reference(context):
|
|
"""Check agents reference templates."""
|
|
assert "agents" in context.result
|
|
assert "my_team" in context.result["agents"]
|
|
assert context.result["agents"]["my_team"]["template"] == "dynamic_team"
|
|
|
|
|
|
@then("the result should contain {count:d} agents: {agent_names}")
|
|
def step_check_result_agents(context, count, agent_names):
|
|
"""Check result contains specific agents."""
|
|
agents = [name.strip(' "') for name in agent_names.split(",")]
|
|
assert len(agents) == count
|
|
|
|
components = context.result["components"]["agents"]
|
|
for agent in agents:
|
|
assert agent in components
|
|
|
|
|
|
@then("the workflow should have parallel execution paths")
|
|
def step_check_parallel_paths(context):
|
|
"""Check workflow has parallel paths."""
|
|
workflow = context.result["components"]["graphs"]["workflow"]
|
|
edges = workflow["edges"]
|
|
|
|
# Count edges from start
|
|
start_edges = [e for e in edges if e["source"] == "start"]
|
|
assert len(start_edges) > 1 # Multiple paths from start = parallel
|
|
|
|
|
|
@then("there should be edges from start to all stages")
|
|
def step_check_all_start_edges(context):
|
|
"""Check edges from start to all stages."""
|
|
workflow = context.result["components"]["graphs"]["workflow"]
|
|
edges = workflow["edges"]
|
|
|
|
# Get all stage names
|
|
stage_nodes = [n for n in workflow["nodes"] if n not in ["start", "end"]]
|
|
|
|
# Check each stage has edge from start
|
|
for stage in stage_nodes:
|
|
has_edge = any(e["source"] == "start" and e["target"] == stage for e in edges)
|
|
assert has_edge, f"No edge from start to {stage}"
|
|
|
|
|
|
@then("config debug should be {value}")
|
|
def step_check_debug_value(context, value):
|
|
"""Check config debug value."""
|
|
expected = value.lower() == "true"
|
|
assert context.result["config"]["debug"] == expected
|
|
|
|
|
|
@then("services should be properly configured with defaults")
|
|
def step_check_services_defaults(context):
|
|
"""Check services have proper defaults."""
|
|
services = context.result["services"]
|
|
|
|
# Check each service
|
|
for service_name, service_config in services.items():
|
|
assert "port" in service_config
|
|
assert "enabled" in service_config
|
|
assert "replicas" in service_config
|
|
|
|
# Check defaults were applied
|
|
if service_name == "web":
|
|
assert service_config["enabled"] is True
|
|
assert service_config["replicas"] == 1 # default
|
|
|
|
|
|
@then("metrics should calculate correct totals")
|
|
def step_check_metrics_totals(context):
|
|
"""Check metrics calculations."""
|
|
metrics = context.result["metrics"]
|
|
|
|
# Verify calculations
|
|
assert metrics["total_services"] == 3
|
|
assert metrics["total_ports"] == 8080 + 3000 + 9000
|
|
assert len(metrics["service_names"]) == 3
|
|
assert set(metrics["service_names"]) == {"api", "web", "worker"}
|
|
|
|
|
|
@then('empty_list should only contain "{content}"')
|
|
def step_check_empty_list(context, content):
|
|
"""Check empty list content."""
|
|
assert "empty_list" in context.result
|
|
assert "static" in context.result["empty_list"]
|
|
assert context.result["empty_list"]["static"] == "value"
|
|
|
|
# Should not have any list items from the empty loop
|
|
list_items = [k for k in context.result["empty_list"] if k != "static"]
|
|
assert len(list_items) == 0
|
|
|
|
|
|
@then("messages should preserve Unicode characters")
|
|
def step_check_unicode_preserved(context):
|
|
"""Check Unicode preservation."""
|
|
messages = context.result["messages"]
|
|
|
|
# Check specific Unicode content
|
|
assert messages[0]["emoji"] == "👋"
|
|
assert messages[1]["text"] == "Ça va?"
|
|
assert messages[2]["text"] == "你好"
|
|
|
|
|
|
@then("complex nested structures should render correctly")
|
|
def step_check_complex_nested(context):
|
|
"""Check complex nested rendering."""
|
|
assert "complex" in context.result
|
|
|
|
# Should have rendered the items
|
|
assert "service1" in context.result["complex"]
|
|
assert "service2" in context.result["complex"]
|
|
|
|
# service1 and service2 should have config
|
|
assert "config" in context.result["complex"]["service1"]
|
|
assert context.result["complex"]["service1"]["config"]["port"] == 8080
|
|
|
|
# service3 should not have config section (it was None)
|
|
if "service3" in context.result.get("complex", {}):
|
|
service3 = context.result["complex"].get("service3")
|
|
if service3 is not None:
|
|
assert "config" not in service3
|