Feature: Consolidated Langgraph Combined scenarios from: langgraph_bridge_coverage, langgraph_dynamic_router_coverage, langgraph_graph_coverage, langgraph_nodes_additional_coverage, langgraph_nodes_coverage, langgraph_nodes_uncovered_lines, langgraph_state_additional_coverage, langgraph_state_branch_coverage, pure_graph_coverage, agent_langgraph_port # ============================================================ # Originally from: langgraph_bridge_coverage.feature # Feature: LangGraph bridge coverage # ============================================================ Scenario: Cover bridge lifecycle and operators Given I set up a langgraph bridge test harness When I create a bridge with mocked graph configuration And I exercise bridge graph and stream creation And I exercise graph executor branches And I exercise state utilities and node operator And I exercise conditional routing helpers And I exercise router node helpers and cleanup Then bridge coverage steps should complete successfully # ============================================================ # Originally from: langgraph_dynamic_router_coverage.feature # Feature: Dynamic Router Coverage # ============================================================ Scenario: Default route when no rules are provided Given a dynamic router with no rules When I route the message """ {"text": "hello"} """ Then the router should return the default end edge Scenario: Route to target when no condition is required Given a dynamic router with a rule targeting "next" and no condition When I route the message """ {"text": "any"} """ Then the router should return a single edge to "next" Scenario: Route to target when condition matches Given a dynamic router with a rule targeting "match_target" when message type is "match" When I route the message """ {"type": "match", "text": "payload"} """ Then the router should return a single edge to "match_target" Scenario: Fall back to end when condition fails Given a dynamic router with a rule targeting "conditional" when message type is "expected" When I route the message """ {"type": "other", "text": "payload"} """ Then the router should return the default end edge # ============================================================ # Originally from: langgraph_graph_coverage.feature # Feature: LangGraph uncovered branches # ============================================================ Scenario: Scheduler falls back when no running loop exists Given a langgraph config with only start and end nodes And asyncio has no running loop When I construct a LangGraph without providing a scheduler Then it should create an AsyncIOScheduler and initialize streams Scenario: Execute dispatches to start stream and returns state Given a langgraph instance with a fresh state manager When I execute the graph with input data Then the start stream should receive the state and execution returns a GraphState Scenario: Start raises when start stream is missing Given a langgraph instance with a removed start stream When I call start on the graph Then a start stream error should be raised Scenario: Parallel groups disabled and cycles detected Given a langgraph config with parallel execution disabled and a cycle When I construct the LangGraph Then the graph should report cycles and empty parallel groups Scenario: Node executor updates state and history Given a langgraph instance with a patched state manager When I invoke the builtin node executor Then it should update state and record execution history Scenario: Topological levels enumerate layered nodes Given a langgraph config with layered nodes When I compute the topological levels Then the levels should list each layer Scenario: Execution history getter returns copy Given a langgraph instance with recorded history When I request execution history Then it should return a copy of the recorded history Scenario: Initialization respects explicit guard nodes Given a langgraph config with explicit start and end nodes When I construct the LangGraph with that config Then initialization should reuse the provided start and end nodes Scenario: Node subscriptions invoke handlers on stream events Given a langgraph instance prepared to observe node streams When the node stream emits next error and completion events Then the subscription handlers should be exercised Scenario: Node stream messages are processed by the node executor Given a langgraph instance with a mocked node executor When a message is emitted on the worker node stream Then the registered executor should be called with the message Scenario: Raw stream values are wrapped before node processing Given a langgraph instance with a mocked node executor When a raw value is emitted on the worker node stream Then the executor should receive a StreamMessage wrapping the raw value Scenario: Missing executor triggers a diagnostic warning Given a langgraph instance with a removed node executor When a message is emitted on the executorless worker stream Then a warning about missing executor should be logged Scenario: Each node stream dispatches to its own executor Given a langgraph instance with multiple mocked node executors When messages are emitted on both alpha and beta node streams Then each executor should receive its own message independently Scenario: Node stream on_next handles executor exceptions gracefully Given a langgraph instance with a failing node executor When a message is emitted on the failing worker stream Then the exception should be caught and logged without propagating Scenario: Subscription is skipped when observable is missing Given a langgraph instance with a removed observable When node stream subscriptions are set up Then no executor should be called and no error should occur Scenario: Execute raises when start stream is missing Given a langgraph instance with a removed start stream for execute When I execute the graph expecting a start stream error Then a start stream missing error should be raised from execute Scenario: Parallel groups computed for converging branches Given a langgraph config with converging branches When I construct the LangGraph for parallel groups Then parallel groups should reflect staged execution order Scenario: Validation fails when guard nodes missing Given a langgraph instance missing required guard nodes When I validate the graph explicitly Then a guard validation error should be raised Scenario: Start uses default state and stop resets running flag Given a langgraph instance ready to start When I start without input then stop and fetch state Then it should use default state and update running flags Scenario: Executor completes via run_coroutine_threadsafe on running loop Given a langgraph instance with a scheduler whose loop is running in a background thread When a message is emitted on the worker stream via the running loop path Then the executor should complete successfully via run_coroutine_threadsafe Scenario: Execution history is capped at maximum size Given a langgraph instance with execution history at capacity When one more executor invocation is recorded Then the history length should remain at the maximum and the oldest entry should be dropped Scenario: Concurrent state updates are serialized by the lock Given a state manager configured for concurrent access When multiple threads perform state updates simultaneously Then the final execution count should equal the total number of updates Scenario: Executor times out via run_coroutine_threadsafe path Given a langgraph instance with a slow executor on a background loop and a short timeout When the slow executor is invoked via the running loop path Then a TimeoutError should be raised with the node name and timeout duration Scenario: Executor times out via thread pool path Given a langgraph instance with a slow executor and a short timeout When the slow executor is invoked via the thread pool path Then a TimeoutError should be raised from the thread pool path with the node name Scenario: Graph can be restarted after stop Given a langgraph instance that has been started and stopped When I restart the graph and invoke an executor Then the executor should complete successfully after restart Scenario: Executor raises when graph is not running Given a langgraph instance that is not running When I invoke the node executor directly Then a RuntimeError should be raised indicating the graph is not running Scenario: StateManager replace_state atomically replaces and emits deep copy Given a state manager with an initial state When I call replace_state with a new state Then get_state should return the new state And the state stream should have received a notification And the emitted state should be a deep copy not the same object And the emitted state should be distinct from the internal state Scenario: CancelledError in thread pool path raises descriptive RuntimeError Given a langgraph instance with a cancellable executor task When the executor future is cancelled before completion Then a RuntimeError should be raised indicating graph stopping Scenario: LangGraph __del__ shuts down executor pool without error Given a langgraph instance that was never stopped When __del__ is called on the graph Then no exception should propagate from __del__ Scenario: Execute raises when graph is not running via execute method Given a langgraph instance that is not running for execute When I call execute on the non-running graph Then a RuntimeError should be raised indicating the graph is not running for execute Scenario: TimeoutError through stream path logs warning not exception Given a langgraph instance with an executor that raises TimeoutError When a message is emitted on the timeout worker stream Then the timeout should be logged at warning level And logger exception should not be called for timeout Scenario: CancelledError in run_coroutine_threadsafe path raises descriptive RuntimeError Given a langgraph instance with a scheduler loop and a cancellable coroutine future When the coroutine future is cancelled before completion Then a RuntimeError should be raised indicating graph stopping from coroutine path # ============================================================ # Originally from: langgraph_nodes_additional_coverage.feature # Feature: Additional LangGraph nodes coverage # ============================================================ Scenario: Conversation history handles empty messages Given a node with default history limits When I prepare the conversation history for empty input Then it should return empty history without truncation Scenario: Conversation history falls back on invalid limits Given a node with invalid history metadata and two messages When I prepare the conversation history with invalid limits Then it should return full history without truncation Scenario: Agent node without configured agent returns failure metadata Given an agent node without an agent configured When I execute the node expecting an agent configuration error Then the result should include failed_node and error text Scenario: Agent receives empty input when no messages exist Given an agent node with no prior messages When I execute the agent node with empty history Then the agent should receive empty input and track node metadata Scenario: Agent merges nested metadata context and flags truncation Given an agent node with nested metadata context and long history When I execute the agent node with nested context Then the agent context should merge nested values and mark truncation Scenario: Agent error path captures processing failures Given an agent node whose handler raises an exception When I execute the failing agent node Then the agent response should contain the error string Scenario: Function node retries and reports missing function Given a function node missing a target with retry policy When I execute the function node with retries Then it should report failure after retries # ============================================================ # Originally from: langgraph_nodes_coverage.feature # Feature: LangGraph nodes coverage # ============================================================ Scenario: Conversation history truncates by message count Given a node with history limit 2 messages And a conversation history of 3 short messages When I prepare the conversation history Then it should return 2 messages and indicate truncation Scenario: Agent node falls back when agent instance is missing Given an agent node referencing a missing agent When I execute the agent node Then it should return a not found assistant message Scenario: Tool agent uses current_message metadata for input Given a tool agent node with a current_message "override" and long history When I execute the tool agent node Then the tool agent should receive input "override" And the response metadata should include last_agent_node Scenario: Function node runs async function Given a function node with an async function When I execute the function node Then the function result message should be "async-result" Scenario: Function node awaits coroutine returned from sync function Given a function node with a sync function that returns a coroutine When I execute the function node Then the function result message should be "sync-inner" Scenario: Conditional node detects assistant field match Given a conditional node looking for field "status" equals "OK" When I execute the conditional node with matching assistant message Then the condition_result should be true and metadata flagged Scenario: Message router selects rule based on field condition Given a message router node with a rule matching field "topic" value "urgent" When I execute the message router with current_message containing topic "urgent" Then it should route to "urgent_node" Scenario: Edge condition checks equals against latest message Given an edge with equals condition on field "content" value "done" When I evaluate the edge condition with matching last message Then the edge condition should be true And evaluating with no messages should also default to true Scenario: Eval condition supports equals and field comparisons Given I evaluate equals condition on message "abc" Then the equals evaluation should return true When I evaluate field condition on message dict with foo value "bar" Then the field evaluation should return true When I evaluate field condition on non-dict message Then it should return false Scenario: Node parallel execution flag reflects configuration Given a node configured for parallel execution Then can_execute_parallel should return true # ============================================================ # Originally from: langgraph_nodes_uncovered_lines.feature # Feature: LangGraph nodes uncovered lines # ============================================================ Scenario: Tool node marks execution Given a tool node configured When I execute the tool node Then it should mark tool executed Scenario: ToolAgent branch uses current_message for input Given a tool agent node using ToolAgent with current_message "override" When I execute the tool agent node with ToolAgent branch Then the ToolAgent should receive input "override" Scenario: Agent falls back to last assistant message when no user entries exist Given an agent node with only assistant history When I execute the agent node for assistant fallback Then the agent should receive the assistant fallback content Scenario: Agent clearing context skips metadata merge Given an agent that clears context during processing When I execute the clearing agent node Then only last_agent_node metadata should remain Scenario: Agent mutation adds metadata updates Given an agent that mutates context to add metadata When I execute the mutating agent node Then the metadata should include the mutated key Scenario: Subgraph node reports invocation Given a subgraph node named "child_subgraph" When I execute the subgraph node Then it should report the invoked subgraph "child_subgraph" Scenario: Start node indicates start Given a start node When I execute the start node Then it should indicate graph start Scenario: End node indicates completion Given an end node When I execute the end node Then it should indicate graph completion Scenario: Non-dict tool result leaves base state updates Given a tool node patched to return non dict When I execute the patched tool node Then the execute result should only include current_node # ============================================================ # Originally from: langgraph_state_additional_coverage.feature # Feature: LangGraph state additional coverage # ============================================================ Scenario: Merge mode trims messages beyond retention limit Given the state management system is available Given I have a GraphState starting with 49 messages And I have merge updates containing 5 new messages When I merge the updates into the state Then the message list should be trimmed to 50 messages And the newest message should be retained after trimming Scenario: Append mode trims messages beyond retention limit Given the state management system is available Given the state management system is available Given I have a GraphState starting with 50 messages And I have append updates containing 5 new messages When I append the updates into the state Then the message list should be trimmed to 50 messages after append And the newest appended message should be retained after trimming Scenario: Time travel history is recorded and trimmed Given the state management system is available Given I have a StateManager with time travel enabled and history size 2 When I perform 4 sequential state updates with time travel Then only the two most recent history snapshots should remain And the earliest snapshots should be discarded Scenario: Automatic checkpoint is saved at interval Given the state management system is available Given I have a StateManager with checkpointing enabled and interval 1 When I perform 2 sequential state updates with checkpointing Then a checkpoint file should exist in the directory And checkpoint saving should have been triggered Scenario: Time travel clamps when steps exceed history Given the state management system is available Given I have a StateManager with time travel history of 2 snapshots When I request time travel 5 steps back Then time travel should return the earliest available snapshot And the returned state should reflect the earliest snapshot # ============================================================ # Originally from: langgraph_state_branch_coverage.feature # Feature: LangGraph state branch coverage # ============================================================ Scenario: Replace mode overwrites existing fields Given the state management system is available Given I have a GraphState with metadata {"original": 1} and current node "old" When I replace the state with updates containing metadata {"new": 2} and current node "new" Then the state's metadata should equal {"new": 2} And the state's current node should equal "new" Scenario: Merge mode merges metadata dictionaries Given the state management system is available Given I have a GraphState with metadata {"alpha": 1} And I have merge metadata updates {"beta": 2} When I merge metadata updates into the state Then the state's metadata should contain keys "alpha" and "beta" Scenario: Append mode appends a single message item Given the state management system is available Given I have a GraphState with 1 message When I append a single message with id 99 into the state Then the state's messages should end with id 99 And the message list length should be 2 Scenario: Save checkpoint without directory is a no-op Given the state management system is available Given I have a StateManager without checkpointing When I invoke checkpoint saving manually Then the update count should remain at 0 Scenario: Load checkpoint restores state and counters Given the state management system is available Given I have a checkpoint file with execution count 7 and metadata {"restored": true} When I load the checkpoint into a StateManager Then the manager state should include metadata {"restored": true} And the manager update count should be 7 Scenario: Latest checkpoint returns None when missing Given the state management system is available Given I have a StateManager without checkpointing When I request the latest checkpoint Then no checkpoint path should be returned Scenario: Latest checkpoint selects the most recent file Given the state management system is available Given I have a StateManager with two checkpoints written at different times When I request the latest checkpoint Then the newest checkpoint path should be returned Scenario: Time travel without history returns None Given the state management system is available Given I have a StateManager with time travel disabled When I request time travel Then the returned time travel state should be None Scenario: Clear history and reset restore initial state Given the state management system is available Given I have a StateManager with time travel history entries When I clear the history and then reset with metadata {"reset": true} Then the history should be empty after reset And the execution count should be zero after reset And the state metadata should include {"reset": true} # ============================================================ # Originally from: pure_graph_coverage.feature # Feature: PureGraph coverage # ============================================================ Scenario: Topological order keeps start and end boundaries Given a pure graph with nodes "alpha" and "beta" When I list its topological order Then the order should be start alpha beta end Scenario: Execute applies registered node functions sequentially Given a pure graph with function nodes "double" and "increment" And registered functions that double then increment When I execute the pure graph starting with 1 Then the final result should be 3 And the functions should run in declaration order Scenario: Execute skips unregistered functions Given a pure graph with a missing function node When I execute the pure graph starting with 5 Then the result should remain 5 Scenario: Nodes without functions do not change value Given a pure graph with non-functional nodes "alpha" and "beta" When I execute the pure graph starting with 7 Then the result should remain 7 # ============================================================ # Originally from: agent_langgraph_port.feature # Feature: Actor-first port of v2 agent and LangGraph suites # ============================================================