Files
cleveragents-core/features/consolidated_langgraph.feature
freemo b2e923173f perf(tests): consolidate 141 trivially small feature files into 34 domain groups
Consolidate 141 BDD feature files that each complete in under 0.1 seconds
into 25 domain-grouped feature files, reducing subprocess count from 339
to ~223. Each consolidated file groups scenarios from the same domain/module
that share step definitions and fixtures. All scenarios are preserved with
clear comment headers indicating their original source file.

This reduces subprocess overhead by ~116 invocations (141 original files
replaced by 25 consolidated files), targeting the 42% of subprocess count
that contributed only 0.2% of actual test runtime.

ISSUES CLOSED: #485
2026-03-02 14:56:13 +00:00

474 lines
18 KiB
Gherkin

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: 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
# ============================================================
# 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
# ============================================================