79d84c1d12
CI / push-validation (pull_request) Successful in 29s
CI / helm (pull_request) Successful in 35s
CI / lint (pull_request) Successful in 48s
CI / build (pull_request) Successful in 46s
CI / quality (pull_request) Successful in 58s
CI / typecheck (pull_request) Successful in 1m2s
CI / security (pull_request) Successful in 1m6s
CI / unit_tests (pull_request) Successful in 4m26s
CI / docker (pull_request) Successful in 1m30s
CI / integration_tests (pull_request) Successful in 9m49s
CI / coverage (pull_request) Successful in 9m4s
CI / status-check (pull_request) Successful in 3s
- Remove unused `import ast` and fix `set_function_registry` to store callable values directly instead of calling eval() on a function object - Replace try/except/pass with contextlib.suppress (SIM105) - Add strict=False to zip() call (B905) - Wrap long line in execute_graph (E501) - Use list unpacking in assert_topo_order_equals (RUF005) - Fix keyword name: Remove hyphen from 'Non-Functional' (Python method has no hyphen so Robot generates 'Non Functional') - Rewrite Set Double And Increment Registry to evaluate lambdas directly rather than iterating a list of tuples (which paired whole tuples as loop variables instead of unpacking them) - Pass node/function name lists via Robot list variables instead of space-delimited positional args ISSUES CLOSED: #9531
140 lines
5.5 KiB
Python
140 lines
5.5 KiB
Python
"""Robot Framework library for PureGraph integration testing."""
|
|
|
|
import contextlib
|
|
|
|
|
|
class PureGraphLibrary:
|
|
"""Custom keywords for exercising PureGraph functionality."""
|
|
|
|
ROBOT_AUTO_KEYWORDS = True
|
|
|
|
def __init__(self):
|
|
self._graph = None
|
|
self._fn_registry = {}
|
|
self._topo_order = None
|
|
self._exec_result = None
|
|
|
|
# ------------------------------------------------------------------
|
|
# Graph construction helpers
|
|
# ------------------------------------------------------------------
|
|
|
|
def create_linear_graph_with_function_nodes(self, node_names, function_names):
|
|
"""Create a linear graph with the specified node and function names."""
|
|
from cleveragents.langgraph.nodes import Edge, NodeConfig, NodeType
|
|
from cleveragents.langgraph.pure_graph import PureGraph
|
|
|
|
nodes = {
|
|
"start": NodeConfig(name="start", type=NodeType.START),
|
|
"end": NodeConfig(name="end", type=NodeType.END),
|
|
}
|
|
edges = []
|
|
previous = "start"
|
|
for node_name, function_name in zip(node_names, function_names, strict=False):
|
|
nodes[node_name] = NodeConfig(
|
|
name=node_name, type=NodeType.FUNCTION, function=function_name
|
|
)
|
|
edges.append(Edge(source=previous, target=node_name))
|
|
previous = node_name
|
|
edges.append(Edge(source=previous, target="end"))
|
|
|
|
self._graph = PureGraph(name="robot-test-graph", nodes=nodes, edges=edges)
|
|
|
|
def create_linear_graph_with_non_functional_nodes(self, node_names):
|
|
"""Create a linear graph with non-functional (inert) nodes."""
|
|
from cleveragents.langgraph.nodes import Edge, NodeConfig, NodeType
|
|
from cleveragents.langgraph.pure_graph import PureGraph
|
|
|
|
nodes = {
|
|
"start": NodeConfig(name="start", type=NodeType.START),
|
|
"end": NodeConfig(name="end", type=NodeType.END),
|
|
}
|
|
edges = []
|
|
previous = "start"
|
|
for node_name in node_names:
|
|
nodes[node_name] = NodeConfig(
|
|
name=node_name, type=NodeType.FUNCTION, function=None
|
|
)
|
|
edges.append(Edge(source=previous, target=node_name))
|
|
previous = node_name
|
|
edges.append(Edge(source=previous, target="end"))
|
|
|
|
self._graph = PureGraph(name="robot-test-inert", nodes=nodes, edges=edges)
|
|
|
|
def create_graph_with_missing_function(self, unregistered_fn_name):
|
|
"""Create a graph where a node references a function not in the registry."""
|
|
from cleveragents.langgraph.nodes import Edge, NodeConfig, NodeType
|
|
from cleveragents.langgraph.pure_graph import PureGraph
|
|
|
|
nodes = {
|
|
"start": NodeConfig(name="start", type=NodeType.START),
|
|
"end": NodeConfig(name="end", type=NodeType.END),
|
|
}
|
|
edges = []
|
|
previous = "start"
|
|
node_name = f"skipper_{unregistered_fn_name}"
|
|
nodes[node_name] = NodeConfig(
|
|
name=node_name, type=NodeType.FUNCTION, function=unregistered_fn_name
|
|
)
|
|
edges.append(Edge(source=previous, target=node_name))
|
|
previous = node_name
|
|
edges.append(Edge(source=previous, target="end"))
|
|
|
|
self._graph = PureGraph(name="robot-test-missing-fn", nodes=nodes, edges=edges)
|
|
self._fn_registry = {}
|
|
|
|
def set_function_registry(self, registry_dict):
|
|
"""Set or update the function registry used by execute()."""
|
|
self._fn_registry = {}
|
|
for key, value in registry_dict.items():
|
|
if callable(value):
|
|
self._fn_registry[key] = value
|
|
elif isinstance(value, str):
|
|
with contextlib.suppress(Exception):
|
|
self._fn_registry[key] = eval(value)
|
|
|
|
# ------------------------------------------------------------------
|
|
# Topological order queries
|
|
# ------------------------------------------------------------------
|
|
|
|
def compute_topological_order(self):
|
|
"""Compute and return the topological ordering of the current graph."""
|
|
self._topo_order = self._graph.topological_order()
|
|
|
|
def get_topological_order(self):
|
|
"""Return the last computed topological order as a list."""
|
|
return self._topo_order or []
|
|
|
|
# ------------------------------------------------------------------
|
|
# Execution queries
|
|
# ------------------------------------------------------------------
|
|
|
|
def execute_graph(self, initial_value):
|
|
"""Execute the current graph starting with the given initial value."""
|
|
self._exec_result = self._graph.execute(
|
|
self._fn_registry, initial=initial_value
|
|
)
|
|
return self._exec_result
|
|
|
|
def get_execution_result(self):
|
|
"""Return the last execution result."""
|
|
return self._exec_result
|
|
|
|
# ------------------------------------------------------------------
|
|
# Assertion helpers
|
|
# ------------------------------------------------------------------
|
|
|
|
def assert_topo_order_equals(self, *expected_nodes):
|
|
"""Assert topological order matches expected: [start, nodes..., end]."""
|
|
actual = self._topo_order or []
|
|
expected = ["start", *expected_nodes, "end"]
|
|
if actual != expected:
|
|
raise AssertionError(
|
|
f"Topological order mismatch: expected {expected}, got {actual}"
|
|
)
|
|
|
|
def assert_execution_result_equals(self, expected):
|
|
"""Assert the last execution result equals expected value."""
|
|
actual = self._exec_result
|
|
if actual != expected:
|
|
raise AssertionError(f"Expected {expected}, got {actual}")
|