forked from HAL9000/cleveragents-core
408 lines
15 KiB
Python
408 lines
15 KiB
Python
"""
|
|
BDD step definitions for specific missing coverage lines in route.py.
|
|
"""
|
|
|
|
from pathlib import Path
|
|
|
|
from behave import given, then, when
|
|
from behave.runner import Context
|
|
|
|
from cleveragents.langgraph.graph import GraphConfig
|
|
from cleveragents.langgraph.nodes import Edge, NodeConfig, NodeType
|
|
from cleveragents.reactive.route import RouteComplexityAnalyzer, RouteConfig, RouteType
|
|
from cleveragents.reactive.stream_router import StreamConfig, StreamType
|
|
|
|
|
|
@given("I have a StreamConfig with all optional properties set")
|
|
def step_create_stream_config_all_properties(context: Context):
|
|
"""Create a StreamConfig with all optional properties."""
|
|
context.full_stream_config = StreamConfig(
|
|
name="full_stream",
|
|
type=StreamType.HOT,
|
|
operators=[
|
|
{"type": "map", "params": {"transform": "data"}},
|
|
{"type": "filter", "params": {"condition": "valid"}},
|
|
],
|
|
subscriptions=["input1", "input2"],
|
|
publications=["output1", "output2"],
|
|
agents=["agent1", "agent2"],
|
|
initial_value={"start": "value"},
|
|
buffer_size=20,
|
|
template_config={"template": "advanced", "vars": {"key": "value"}},
|
|
)
|
|
|
|
|
|
@when("I call RouteConfig.from_stream_config directly")
|
|
def step_call_from_stream_config_directly(context: Context):
|
|
"""Call RouteConfig.from_stream_config directly."""
|
|
context.result_route = RouteConfig.from_stream_config(context.full_stream_config)
|
|
|
|
|
|
@then("line 170 should be executed")
|
|
def step_line_170_executed(context: Context):
|
|
"""Verify line 170 is executed (return cls(...))."""
|
|
# This is verified by the successful creation
|
|
assert context.result_route is not None
|
|
assert isinstance(context.result_route, RouteConfig)
|
|
|
|
|
|
@then("all stream properties should be copied correctly")
|
|
def step_all_stream_properties_copied(context: Context):
|
|
"""Verify all stream properties are copied correctly."""
|
|
route = context.result_route
|
|
source = context.full_stream_config
|
|
|
|
assert route.name == source.name
|
|
assert route.type == RouteType.STREAM
|
|
assert route.stream_type == source.type
|
|
assert route.operators == source.operators
|
|
assert route.subscriptions == source.subscriptions
|
|
assert route.publications == source.publications
|
|
assert route.agents == source.agents
|
|
assert route.initial_value == source.initial_value
|
|
assert route.buffer_size == source.buffer_size
|
|
assert route.template_config == source.template_config
|
|
|
|
|
|
@given("I have a GraphConfig with nodes having all optional properties")
|
|
def step_create_graph_config_all_properties(context: Context):
|
|
"""Create a GraphConfig with nodes having all optional properties."""
|
|
# Create nodes with all possible properties
|
|
agent_node = NodeConfig(
|
|
name="agent_node",
|
|
type=NodeType.AGENT,
|
|
agent="test_agent",
|
|
tools=["tool1", "tool2", "tool3"],
|
|
retry_policy={"max_retries": 5, "backoff": "exponential"},
|
|
timeout=60,
|
|
parallel=True,
|
|
metadata={"category": "processing"},
|
|
)
|
|
|
|
function_node = NodeConfig(
|
|
name="function_node",
|
|
type=NodeType.FUNCTION,
|
|
function="process_data",
|
|
tools=["utility_tool"],
|
|
retry_policy={"max_retries": 3},
|
|
timeout=30,
|
|
parallel=False,
|
|
condition="data_valid",
|
|
subgraph="sub_process",
|
|
)
|
|
|
|
start_node = NodeConfig(name="start_node", type=NodeType.START, parallel=False)
|
|
|
|
# Create edges with conditions
|
|
edge_with_condition = Edge(
|
|
source="start_node",
|
|
target="agent_node",
|
|
condition="input_ready",
|
|
metadata={"priority": "high"},
|
|
)
|
|
|
|
edge_without_condition = Edge(source="agent_node", target="function_node")
|
|
|
|
context.full_graph_config = GraphConfig(
|
|
name="full_graph",
|
|
nodes={
|
|
"agent_node": agent_node,
|
|
"function_node": function_node,
|
|
"start_node": start_node,
|
|
},
|
|
edges=[edge_with_condition, edge_without_condition],
|
|
entry_point="start_node",
|
|
state_class=None,
|
|
checkpointing=True,
|
|
checkpoint_dir=Path("/tmp/test_checkpoints"),
|
|
enable_time_travel=True,
|
|
parallel_execution=True,
|
|
metadata={"version": "1.0", "author": "test"},
|
|
)
|
|
|
|
|
|
@when("I call RouteConfig.from_graph_config directly")
|
|
def step_call_from_graph_config_directly(context: Context):
|
|
"""Call RouteConfig.from_graph_config directly."""
|
|
context.result_route = RouteConfig.from_graph_config(context.full_graph_config)
|
|
|
|
|
|
@then("lines 187-216 should be executed")
|
|
def step_lines_187_216_executed(context: Context):
|
|
"""Verify lines 187-216 are executed."""
|
|
# This is verified by successful conversion with all properties
|
|
assert context.result_route is not None
|
|
assert context.result_route.type == RouteType.GRAPH
|
|
assert len(context.result_route.nodes) == 3
|
|
assert len(context.result_route.edges) == 2
|
|
|
|
|
|
@then("nodes with tools should be converted")
|
|
def step_nodes_with_tools_converted(context: Context):
|
|
"""Verify nodes with tools are converted (lines 197-198)."""
|
|
route = context.result_route
|
|
|
|
# Check agent_node has tools
|
|
agent_node = route.nodes["agent_node"]
|
|
assert "tools" in agent_node
|
|
assert agent_node["tools"] == ["tool1", "tool2", "tool3"]
|
|
|
|
# Check function_node has tools
|
|
function_node = route.nodes["function_node"]
|
|
assert "tools" in function_node
|
|
assert function_node["tools"] == ["utility_tool"]
|
|
|
|
|
|
@then("nodes with retry_policy should be converted")
|
|
def step_nodes_with_retry_policy_converted(context: Context):
|
|
"""Verify nodes with retry_policy are converted (lines 199-200)."""
|
|
route = context.result_route
|
|
|
|
# Check agent_node has retry_policy
|
|
agent_node = route.nodes["agent_node"]
|
|
assert "retry_policy" in agent_node
|
|
assert agent_node["retry_policy"] == {"max_retries": 5, "backoff": "exponential"}
|
|
|
|
# Check function_node has retry_policy
|
|
function_node = route.nodes["function_node"]
|
|
assert "retry_policy" in function_node
|
|
assert function_node["retry_policy"] == {"max_retries": 3}
|
|
|
|
|
|
@then("nodes with timeout should be converted")
|
|
def step_nodes_with_timeout_converted(context: Context):
|
|
"""Verify nodes with timeout are converted (lines 201-202)."""
|
|
route = context.result_route
|
|
|
|
# Check agent_node has timeout
|
|
agent_node = route.nodes["agent_node"]
|
|
assert "timeout" in agent_node
|
|
assert agent_node["timeout"] == 60
|
|
|
|
# Check function_node has timeout
|
|
function_node = route.nodes["function_node"]
|
|
assert "timeout" in function_node
|
|
assert function_node["timeout"] == 30
|
|
|
|
|
|
@then("edges with conditions should be converted")
|
|
def step_edges_with_conditions_converted(context: Context):
|
|
"""Verify edges with conditions are converted (lines 212-213)."""
|
|
route = context.result_route
|
|
|
|
# Find edge with condition
|
|
edge_with_condition = None
|
|
edge_without_condition = None
|
|
|
|
for edge in route.edges:
|
|
if "condition" in edge:
|
|
edge_with_condition = edge
|
|
else:
|
|
edge_without_condition = edge
|
|
|
|
# Verify edge with condition
|
|
assert edge_with_condition is not None
|
|
assert edge_with_condition["condition"] == "input_ready"
|
|
|
|
# Verify edge without condition doesn't have condition key
|
|
assert edge_without_condition is not None
|
|
assert "condition" not in edge_without_condition
|
|
|
|
|
|
@given("I have a hot stream RouteConfig")
|
|
def step_create_hot_stream_route_config(context: Context):
|
|
"""Create a hot stream RouteConfig."""
|
|
context.hot_stream_route = RouteConfig(
|
|
name="hot_stream",
|
|
type=RouteType.STREAM,
|
|
stream_type=StreamType.HOT,
|
|
operators=[{"type": "map"}],
|
|
)
|
|
|
|
|
|
@when("I analyze stream complexity")
|
|
def step_analyze_stream_complexity(context: Context):
|
|
"""Analyze stream complexity."""
|
|
context.stream_analysis = RouteComplexityAnalyzer.analyze_route(context.hot_stream_route)
|
|
|
|
|
|
@then("line 273-274 should be executed for hot stream detection")
|
|
def step_lines_273_274_executed(context: Context):
|
|
"""Verify lines 273-274 are executed for hot stream detection."""
|
|
analysis = context.stream_analysis
|
|
|
|
# Check that hot stream feature is detected
|
|
features = analysis["features"]
|
|
has_hot_feature = any("hot" in str(feature).lower() for feature in features)
|
|
assert has_hot_feature, "Hot stream feature should be detected"
|
|
|
|
|
|
@given("I have a graph with multiple conditional edges")
|
|
def step_create_graph_with_conditional_edges(context: Context):
|
|
"""Create a graph with multiple conditional edges."""
|
|
context.conditional_graph = RouteConfig(
|
|
name="conditional_graph",
|
|
type=RouteType.GRAPH,
|
|
nodes={
|
|
"node1": {"type": "agent"},
|
|
"node2": {"type": "agent"},
|
|
"node3": {"type": "agent"},
|
|
},
|
|
edges=[
|
|
{"source": "start", "target": "node1"},
|
|
{"source": "node1", "target": "node2", "condition": "path_a"},
|
|
{"source": "node1", "target": "node3", "condition": "path_b"},
|
|
{"source": "node2", "target": "end", "condition": "final_check"},
|
|
],
|
|
)
|
|
|
|
|
|
@when("I analyze graph complexity")
|
|
def step_analyze_graph_complexity(context: Context):
|
|
"""Analyze graph complexity."""
|
|
context.graph_analysis = RouteComplexityAnalyzer.analyze_route(context.conditional_graph)
|
|
|
|
|
|
@then("lines 303-304 should be executed for conditional edge counting")
|
|
def step_lines_303_304_executed(context: Context):
|
|
"""Verify lines 303-304 are executed for conditional edge counting."""
|
|
analysis = context.graph_analysis
|
|
|
|
# Should detect 3 conditional edges
|
|
features = analysis["features"]
|
|
has_conditional_feature = any("conditional" in str(feature).lower() for feature in features)
|
|
assert has_conditional_feature, "Conditional edges feature should be detected"
|
|
|
|
|
|
@given("I have a stream with complexity score of 8")
|
|
def step_create_stream_score_8(context: Context):
|
|
"""Create a stream that will have complexity score of 8."""
|
|
# Score calculation: 1 (base) + 6 (operators) + 2 (routing) = 9, close to 8
|
|
context.complex_stream_score = 8
|
|
|
|
|
|
@when("I get stream recommendation")
|
|
def step_get_stream_recommendation(context: Context):
|
|
"""Get stream recommendation."""
|
|
context.stream_recommendation = RouteComplexityAnalyzer._get_stream_recommendation(context.complex_stream_score)
|
|
|
|
|
|
@then("line 328 should be executed")
|
|
def step_line_328_executed(context: Context):
|
|
"""Verify line 328 is executed (complex stream recommendation)."""
|
|
recommendation = context.stream_recommendation
|
|
assert "graph" in recommendation.lower(), "Should recommend considering graph for complex streams"
|
|
|
|
|
|
@given("I have a graph with complexity score of 18")
|
|
def step_create_graph_score_18(context: Context):
|
|
"""Create a graph that will have complexity score of 18."""
|
|
context.advanced_graph_score = 18
|
|
|
|
|
|
@when("I get graph recommendation")
|
|
def step_get_graph_recommendation(context: Context):
|
|
"""Get graph recommendation."""
|
|
context.graph_recommendation = RouteComplexityAnalyzer._get_graph_recommendation(context.advanced_graph_score)
|
|
|
|
|
|
@then("line 340 should be executed")
|
|
def step_line_340_executed(context: Context):
|
|
"""Verify line 340 is executed (advanced graph recommendation)."""
|
|
recommendation = context.graph_recommendation
|
|
assert "features" in recommendation.lower(), "Should recommend feature evaluation for advanced graphs"
|
|
|
|
|
|
@given("I have requirements with needs_state and needs_conditionals true")
|
|
def step_create_state_conditionals_requirements(context: Context):
|
|
"""Create requirements with needs_state and needs_conditionals true."""
|
|
context.state_conditionals_reqs = {
|
|
"needs_state": True,
|
|
"needs_conditionals": True,
|
|
"needs_persistence": False,
|
|
}
|
|
|
|
|
|
@when("I get route type suggestion")
|
|
def step_get_route_type_suggestion(context: Context):
|
|
"""Get route type suggestion."""
|
|
if hasattr(context, "state_conditionals_reqs"):
|
|
context.route_suggestion = RouteComplexityAnalyzer.suggest_route_type(context.state_conditionals_reqs)
|
|
elif hasattr(context, "conditionals_not_continuous_reqs"):
|
|
context.route_suggestion = RouteComplexityAnalyzer.suggest_route_type(context.conditionals_not_continuous_reqs)
|
|
elif hasattr(context, "stateless_continuous_reqs"):
|
|
context.route_suggestion = RouteComplexityAnalyzer.suggest_route_type(context.stateless_continuous_reqs)
|
|
elif hasattr(context, "simple_reqs"):
|
|
context.route_suggestion = RouteComplexityAnalyzer.suggest_route_type(context.simple_reqs)
|
|
|
|
|
|
@then("line 351 should be executed")
|
|
def step_line_351_executed(context: Context):
|
|
"""Verify line 351 is executed."""
|
|
assert context.route_suggestion == RouteType.GRAPH
|
|
|
|
|
|
@then("it should return GRAPH")
|
|
def step_should_return_graph(context: Context):
|
|
"""Verify it returns GRAPH."""
|
|
assert context.route_suggestion == RouteType.GRAPH
|
|
|
|
|
|
@given("I have requirements with conditionals true but continuous false")
|
|
def step_create_conditionals_not_continuous_requirements(context: Context):
|
|
"""Create requirements with conditionals true but continuous false."""
|
|
context.conditionals_not_continuous_reqs = {
|
|
"needs_conditionals": True,
|
|
"is_continuous": False,
|
|
"needs_persistence": False,
|
|
"needs_state": False,
|
|
}
|
|
|
|
|
|
@then("line 353 should be executed")
|
|
def step_line_353_executed(context: Context):
|
|
"""Verify line 353 is executed."""
|
|
assert context.route_suggestion == RouteType.GRAPH
|
|
|
|
|
|
@given("I have requirements with stateless and continuous true")
|
|
def step_create_stateless_continuous_requirements(context: Context):
|
|
"""Create requirements with stateless and continuous true."""
|
|
context.stateless_continuous_reqs = {
|
|
"is_stateless": True,
|
|
"is_continuous": True,
|
|
"needs_persistence": False,
|
|
"needs_state": False,
|
|
"needs_conditionals": False,
|
|
}
|
|
|
|
|
|
@then("line 355 should be executed")
|
|
def step_line_355_executed(context: Context):
|
|
"""Verify line 355 is executed."""
|
|
assert context.route_suggestion == RouteType.STREAM
|
|
|
|
|
|
@then("it should return STREAM")
|
|
def step_should_return_stream(context: Context):
|
|
"""Verify it returns STREAM."""
|
|
assert context.route_suggestion == RouteType.STREAM
|
|
|
|
|
|
@given("I have simple requirements")
|
|
def step_create_simple_requirements(context: Context):
|
|
"""Create simple requirements (default case)."""
|
|
context.simple_reqs = {
|
|
"needs_persistence": False,
|
|
"needs_state": False,
|
|
"needs_conditionals": False,
|
|
"is_continuous": False,
|
|
"is_stateless": False,
|
|
}
|
|
|
|
|
|
@then("line 358 should be executed as default")
|
|
def step_line_358_executed_default(context: Context):
|
|
"""Verify line 358 is executed as default."""
|
|
assert context.route_suggestion == RouteType.STREAM
|