""" BDD step definitions for specific missing coverage lines in state.py. """ import json import tempfile from pathlib import Path from behave import given, then, when from behave.runner import Context from cleveragents.langgraph.state import ( GraphState, StateManager, StateUpdateMode, ) @given("I have updates with non-existent attributes only") def step_create_nonexistent_updates(context: Context): """Create updates with non-existent attributes only.""" context.test_data["nonexistent_updates"] = { "fake_attribute": "should_not_be_set", "another_fake": "also_ignored", "invalid_field": 123, } @when("I update the state with REPLACE mode for nonexistent attributes") def step_update_state_replace_mode_missing(context: Context): """Update state using REPLACE mode (missing coverage version).""" state = context.state_instances["main"] updates = context.test_data["nonexistent_updates"] state.update(updates, StateUpdateMode.REPLACE) @then("non-existent attributes should be ignored") def step_verify_nonexistent_ignored(context: Context): """Verify non-existent attributes are ignored.""" state = context.state_instances["main"] # None of the fake attributes should exist assert not hasattr(state, "fake_attribute") assert not hasattr(state, "another_fake") assert not hasattr(state, "invalid_field") @then("hasattr check should be performed for each attribute") def step_verify_hasattr_check(context: Context): """Verify hasattr check logic (lines 64-66).""" # This is verified by the fact that no fake attributes were set # The test reaching this point means hasattr checks worked correctly assert True @given("I have a GraphState with string metadata") def step_create_state_string_metadata(context: Context): """Create GraphState with string metadata.""" context.state_instances["string_meta"] = GraphState(current_node="test_string") @given("I have string updates for merge mode") def step_create_string_updates_merge(context: Context): """Create string updates for merge mode.""" context.test_data["string_updates"] = {"current_node": "new_string_value"} @when("I update the state with MERGE mode for string values") def step_update_state_merge_mode_missing(context: Context): """Update state using MERGE mode (missing coverage version).""" state = context.state_instances["string_meta"] updates = context.test_data["string_updates"] state.update(updates, StateUpdateMode.MERGE) @then("the simple value should replace the existing value") def step_verify_simple_value_replace(context: Context): """Verify simple value replacement.""" state = context.state_instances["string_meta"] assert state.current_node == "new_string_value" @then("line 76 should be executed") def step_verify_line_76_executed(context: Context): """Verify line 76 is executed.""" # Line 76 is the else case for non-dict/non-list values in MERGE mode # This is verified by the successful replacement above assert True @given("I have a GraphState with list messages") def step_create_state_list_messages(context: Context): """Create GraphState with list messages.""" context.state_instances["list_messages"] = GraphState(messages=[{"id": 1}, {"id": 2}]) @given("I have single item to append to list field") def step_create_single_item_append(context: Context): """Create single item to append to list field.""" context.test_data["single_append_list"] = {"messages": {"id": 3, "content": "single_item"}} @when("I update the state with APPEND mode for string field") def step_update_state_append_mode_missing(context: Context): """Update state using APPEND mode (missing coverage version).""" state = context.state_instances["list_messages"] updates = context.test_data["single_append_list"] state.update(updates, StateUpdateMode.APPEND) @then("the single item should be set as new value") def step_verify_single_item_set(context: Context): """Verify single item is appended to list.""" state = context.state_instances["list_messages"] assert len(state.messages) == 3 assert state.messages[2] == {"id": 3, "content": "single_item"} @then("line 85 should be executed for non-list field") def step_verify_line_85_executed(context: Context): """Verify line 85 is executed for single item append to list.""" # Line 85 is the append operation for single item to list # This is verified by the successful append above assert True @given("I have a complete state dictionary") def step_create_complete_state_dict(context: Context): """Create a complete state dictionary.""" context.test_data["complete_state_dict"] = { "messages": [{"role": "user", "content": "test"}], "metadata": {"key": "value"}, "current_node": "test_node", "execution_count": 42, "error": "test_error", } @when("I call GraphState.from_dict class method directly") def step_call_from_dict_directly(context: Context): """Call GraphState.from_dict class method directly.""" state_dict = context.test_data["complete_state_dict"] context.state_instances["from_dict_direct"] = GraphState.from_dict(state_dict) @then("a new GraphState instance should be created") def step_verify_new_instance_created(context: Context): """Verify new GraphState instance is created.""" state = context.state_instances["from_dict_direct"] assert isinstance(state, GraphState) @then("line 100 should be executed") def step_verify_line_100_executed(context: Context): """Verify line 100 is executed.""" # Line 100 is the return cls(**data) in from_dict # This is verified by successful instance creation assert True @then("all attributes should be properly set") def step_verify_all_attributes_set(context: Context): """Verify all attributes are properly set.""" state = context.state_instances["from_dict_direct"] assert state.messages == [{"role": "user", "content": "test"}] assert state.metadata == {"key": "value"} assert state.current_node == "test_node" assert state.execution_count == 42 assert state.error == "test_error" @given("I set max history to exactly 2") def step_set_max_history_2(context: Context): """Set max history to exactly 2.""" manager = context.state_managers["time_travel"] manager.max_history_size = 2 @when("I make exactly 3 state updates") def step_make_exactly_3_updates(context: Context): """Make exactly 3 state updates.""" manager = context.state_managers["time_travel"] for i in range(3): manager.update_state({"execution_count": i}, node_id=f"node_{i}") @then("history should be trimmed to 2 entries") def step_verify_history_trimmed_2(context: Context): """Verify history is trimmed to 2 entries.""" manager = context.state_managers["time_travel"] assert len(manager.history) == 2 @then("line 159 should be executed for trimming") def step_verify_line_159_executed(context: Context): """Verify line 159 is executed for trimming.""" # Line 159 is the history trimming operation # This is verified by the trimmed history above assert True @given("I have a StateManager with checkpointing and small interval") def step_create_state_manager_checkpoint_small_interval(context: Context): """Create StateManager with checkpointing and small interval.""" checkpoint_dir = Path(tempfile.mkdtemp()) context.test_data["checkpoint_dir"] = checkpoint_dir context.state_managers["checkpoint_small"] = StateManager(checkpoint_dir=checkpoint_dir) @given("I set checkpoint interval to 2") def step_set_checkpoint_interval_2(context: Context): """Set checkpoint interval to 2.""" manager = context.state_managers["checkpoint_small"] manager.checkpoint_interval = 2 @when("I make exactly 2 state updates") def step_make_exactly_2_updates(context: Context): """Make exactly 2 state updates.""" manager = context.state_managers["checkpoint_small"] for i in range(2): manager.update_state({"execution_count": i}) @then("checkpoint should be saved automatically") def step_verify_checkpoint_saved_automatically(context: Context): """Verify checkpoint is saved automatically.""" checkpoint_dir = context.test_data["checkpoint_dir"] checkpoint_files = list(checkpoint_dir.glob("checkpoint_*.json")) assert len(checkpoint_files) >= 1 @then("line 171 should be executed for checkpoint trigger") def step_verify_line_171_executed(context: Context): """Verify line 171 is executed for checkpoint trigger.""" # Line 171 is the _save_checkpoint() call # This is verified by the checkpoint file creation above assert True @given("I have a StateManager without checkpoint directory") def step_create_state_manager_no_checkpoint_dir(context: Context): """Create StateManager without checkpoint directory.""" context.state_managers["no_checkpoint"] = StateManager(checkpoint_dir=None) @when("I trigger checkpoint saving") def step_trigger_checkpoint_saving(context: Context): """Trigger checkpoint saving.""" manager = context.state_managers["no_checkpoint"] # Call _save_checkpoint directly to hit lines 177-178 manager._save_checkpoint() @then("_save_checkpoint should return early") def step_verify_save_checkpoint_returns_early(context: Context): """Verify _save_checkpoint returns early.""" # If we reach this point without error, the early return worked assert True @then("lines 177-178 should be executed") def step_verify_lines_177_178_executed(context: Context): """Verify lines 177-178 are executed.""" # Lines 177-178 are the early return when no checkpoint_dir # This is verified by successful early return above assert True @given("I have a StateManager with checkpoint directory") def step_create_state_manager_with_checkpoint_dir(context: Context): """Create StateManager with checkpoint directory.""" checkpoint_dir = Path(tempfile.mkdtemp()) context.test_data["checkpoint_dir"] = checkpoint_dir context.state_managers["with_checkpoint_dir"] = StateManager(checkpoint_dir=checkpoint_dir) @given("I have a valid checkpoint file") def step_create_valid_checkpoint_file(context: Context): """Create a valid checkpoint file.""" checkpoint_dir = context.test_data["checkpoint_dir"] checkpoint_data = { "state": { "messages": [{"role": "system", "content": "loaded"}], "metadata": {"loaded": True}, "current_node": "loaded_node", "execution_count": 888, "error": None, }, "timestamp": "20240101_150000", "update_count": 55, } checkpoint_file = checkpoint_dir / "checkpoint_test_load.json" with open(checkpoint_file, "w") as f: json.dump(checkpoint_data, f) context.test_data["checkpoint_file"] = checkpoint_file @when("I load checkpoint from the file") def step_load_checkpoint_from_file(context: Context): """Load checkpoint from the file.""" manager = context.state_managers["with_checkpoint_dir"] checkpoint_file = context.test_data["checkpoint_file"] manager.load_checkpoint(checkpoint_file) @then("state should be restored from checkpoint data") def step_verify_state_restored_from_checkpoint(context: Context): """Verify state is restored from checkpoint data.""" manager = context.state_managers["with_checkpoint_dir"] state = manager.get_state() assert state.messages == [{"role": "system", "content": "loaded"}] assert state.metadata == {"loaded": True} assert state.current_node == "loaded_node" assert state.execution_count == 888 assert manager.update_count == 55 @then("lines 195-204 should be executed") def step_verify_lines_195_204_executed(context: Context): """Verify lines 195-204 are executed.""" # Lines 195-204 are the checkpoint loading logic # This is verified by successful state restoration above assert True @then("GraphState.from_dict should be called") def step_verify_from_dict_called(context: Context): """Verify GraphState.from_dict is called.""" # This is verified by successful state creation from checkpoint data assert True @given("I have checkpoint files in the directory") def step_create_checkpoint_files_in_directory(context: Context): """Create checkpoint files in the directory.""" checkpoint_dir = context.test_data["checkpoint_dir"] # Create multiple checkpoint files with different timestamps import time for i, timestamp in enumerate(["20240101_100000", "20240101_110000", "20240101_120000"]): checkpoint_file = checkpoint_dir / f"checkpoint_{timestamp}.json" checkpoint_data = { "state": {"execution_count": i}, "timestamp": timestamp, "update_count": i, } with open(checkpoint_file, "w") as f: json.dump(checkpoint_data, f) # Ensure different modification times time.sleep(0.01) @when("I get the latest checkpoint from directory") def step_get_latest_checkpoint_missing(context: Context): """Get the latest checkpoint.""" manager = context.state_managers["with_checkpoint_dir"] context.results["latest_checkpoint"] = manager.get_latest_checkpoint() @then("the most recent file should be returned") def step_verify_most_recent_file_returned(context: Context): """Verify the most recent file is returned.""" latest_checkpoint = context.results["latest_checkpoint"] assert latest_checkpoint is not None assert "120000" in str(latest_checkpoint) @then("lines 208-215 should be executed") def step_verify_lines_208_215_executed(context: Context): """Verify lines 208-215 are executed.""" # Lines 208-215 are the get_latest_checkpoint logic # This is verified by successful latest file return above assert True @given("I have a StateManager without checkpoint directory for latest check") def step_create_state_manager_no_checkpoint_dir_latest(context: Context): """Create StateManager without checkpoint directory.""" context.state_managers["no_checkpoint_latest"] = StateManager(checkpoint_dir=None) @when("I get the latest checkpoint with no directory") def step_get_latest_checkpoint_no_dir(context: Context): """Get the latest checkpoint with no directory.""" manager = context.state_managers["no_checkpoint_latest"] context.results["latest_no_dir"] = manager.get_latest_checkpoint() @then("None should be returned immediately") def step_verify_none_returned_immediately(context: Context): """Verify None is returned immediately.""" assert context.results["latest_no_dir"] is None @then("line 209 should be executed") def step_verify_line_209_executed(context: Context): """Verify line 209 is executed.""" # Line 209 is the early return None when no checkpoint_dir # This is verified by None return above assert True @given("I have a StateManager with time travel and history") def step_create_state_manager_time_travel_history(context: Context): """Create StateManager with time travel and history.""" context.state_managers["time_travel_history"] = StateManager(enable_time_travel=True) @given("I have made multiple state updates with history") def step_make_multiple_updates_with_history(context: Context): """Make multiple state updates with history.""" manager = context.state_managers["time_travel_history"] for i in range(3): manager.update_state({"execution_count": i, "current_node": f"node_{i}"}) @when("I perform time travel operation") def step_perform_time_travel_operation(context: Context): """Perform time travel operation.""" manager = context.state_managers["time_travel_history"] context.results["time_travel_state"] = manager.time_travel(1) @then("state should revert to historical snapshot") def step_verify_state_reverts_to_snapshot(context: Context): """Verify state reverts to historical snapshot.""" reverted_state = context.results["time_travel_state"] assert reverted_state is not None assert reverted_state.execution_count == 1 # Second-to-last state @then("lines 219-231 should be executed") def step_verify_lines_219_231_executed(context: Context): """Verify lines 219-231 are executed.""" # Lines 219-231 are the time_travel method logic # This is verified by successful state reversion above assert True @then("GraphState.from_dict should be called for restoration") def step_verify_from_dict_called_restoration(context: Context): """Verify GraphState.from_dict is called for restoration.""" # This is verified by successful state restoration from snapshot assert True @given("I have a StateManager with limited time travel history") def step_create_state_manager_limited_time_travel(context: Context): """Create StateManager with limited time travel history.""" context.state_managers["limited_time_travel"] = StateManager(enable_time_travel=True) # Add limited history manager = context.state_managers["limited_time_travel"] manager.update_state({"execution_count": 1}) @when("I try to time travel beyond available history") def step_try_time_travel_beyond_history(context: Context): """Try to time travel beyond available history.""" manager = context.state_managers["limited_time_travel"] context.results["limited_time_travel"] = manager.time_travel(10) # Way more than available @then("it should travel to earliest available state") def step_verify_earliest_available_state(context: Context): """Verify it travels to earliest available state.""" result = context.results["limited_time_travel"] assert result is not None # Should not crash @then("steps should be clamped to history length") def step_verify_steps_clamped(context: Context): """Verify steps are clamped to history length.""" # This is verified by successful operation without crash assert True @when("I call get_state_observable method") def step_call_get_state_observable(context: Context): """Call get_state_observable method.""" manager = context.state_managers["basic"] context.results["state_observable"] = manager.get_state_observable() @then("the behavior subject should be returned") def step_verify_behavior_subject_returned(context: Context): """Verify the behavior subject is returned.""" observable = context.results["state_observable"] assert hasattr(observable, "subscribe") @then("line 235 should be executed") def step_verify_line_235_executed(context: Context): """Verify line 235 is executed.""" # Line 235 is the return self.state_stream # This is verified by successful observable return above assert True @given("I have a StateManager with existing history") def step_create_state_manager_existing_history(context: Context): """Create StateManager with existing history.""" context.state_managers["existing_history"] = StateManager(enable_time_travel=True) manager = context.state_managers["existing_history"] # Add some history for i in range(3): manager.update_state({"execution_count": i}) @when("I call clear_history method") def step_call_clear_history_method(context: Context): """Call clear_history method.""" manager = context.state_managers["existing_history"] manager.clear_history() @then("history list should be emptied") def step_verify_history_list_emptied(context: Context): """Verify history list is emptied.""" manager = context.state_managers["existing_history"] assert len(manager.history) == 0 @then("line 239 should be executed") def step_verify_line_239_executed(context: Context): """Verify line 239 is executed.""" # Line 239 is the self.history.clear() # This is verified by empty history above assert True @when("I call reset with no parameters") def step_call_reset_no_parameters(context: Context): """Call reset with no parameters.""" manager = context.state_managers["modified"] manager.reset() @then("state should reset to default GraphState") def step_verify_state_reset_to_default(context: Context): """Verify state resets to default GraphState.""" manager = context.state_managers["modified"] state = manager.get_state() assert state.execution_count == 0 assert state.current_node is None assert state.metadata == {} assert state.messages == [] assert state.error is None @then("lines 243-248 should be executed") def step_verify_lines_243_248_executed(context: Context): """Verify lines 243-248 are executed.""" # Lines 243-248 are the reset method logic # This is verified by successful reset above assert True @then("state stream should emit reset state") def step_verify_state_stream_emits_reset(context: Context): """Verify state stream emits reset state.""" manager = context.state_managers["modified"] current_state = manager.get_state() assert current_state.execution_count == 0 @given("I have a custom GraphState instance") def step_create_custom_graphstate_instance(context: Context): """Create a custom GraphState instance.""" context.test_data["custom_graphstate"] = GraphState( messages=[{"role": "system", "content": "custom"}], metadata={"custom": True}, current_node="custom_start", execution_count=99, ) @when("I call reset with the custom initial state") def step_call_reset_with_custom_initial(context: Context): """Call reset with the custom initial state.""" manager = context.state_managers["basic"] custom_initial = context.test_data["custom_graphstate"] manager.reset(custom_initial) @then("state should be set to the custom instance") def step_verify_state_set_to_custom(context: Context): """Verify state is set to the custom instance.""" manager = context.state_managers["basic"] state = manager.get_state() assert state.messages == [{"role": "system", "content": "custom"}] assert state.metadata == {"custom": True} assert state.current_node == "custom_start" assert state.execution_count == 99 @then("reset operations should be performed") def step_verify_reset_operations_performed(context: Context): """Verify reset operations are performed.""" manager = context.state_managers["basic"] assert manager.update_count == 0 assert len(manager.history) == 0