Files
cleveractors-core/features/steps/complex_template_steps.py
CleverThis Engineering 5bbda89386 style: fix all lint errors across source and test files
- 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
2026-05-26 22:21:50 +00:00

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