"""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}")