"""Behave steps for context manager coverage.""" from __future__ import annotations import json import tempfile from pathlib import Path from unittest.mock import patch from behave import given, then, when from cleveragents.reactive.context_manager import ContextManager def _write_json(path: Path, data: object) -> None: path.write_text(json.dumps(data), encoding="utf-8") @given("a context manager storage directory with valid JSON files") def step_valid_storage_directory(context): temp_dir = Path(tempfile.mkdtemp()) context.context_name = "valid_context" context.temp_dir = temp_dir context.context_root = temp_dir / context.context_name context.context_root.mkdir(parents=True, exist_ok=True) context.expected_messages = [ { "role": "user", "content": "hello", "timestamp": "2024-01-01T00:00:00", "metadata": {}, }, { "role": "assistant", "content": "hi", "timestamp": "2024-01-01T00:00:01", "metadata": {"tone": "friendly"}, }, ] context.expected_metadata = { "created_at": "2024-01-01T00:00:00", "last_updated": "2024-01-02T00:00:00", "context_name": context.context_name, } context.expected_state = {"stage": "draft"} context.expected_global = {"topic": "coverage"} _write_json(context.context_root / "messages.json", context.expected_messages) _write_json(context.context_root / "metadata.json", context.expected_metadata) _write_json(context.context_root / "state.json", context.expected_state) _write_json(context.context_root / "global_context.json", context.expected_global) @given("a context manager storage directory with invalid JSON files") def step_invalid_storage_directory(context): temp_dir = Path(tempfile.mkdtemp()) context.context_name = "invalid_context" context.temp_dir = temp_dir context.context_root = temp_dir / context.context_name context.context_root.mkdir(parents=True, exist_ok=True) for filename in ( "messages.json", "metadata.json", "state.json", "global_context.json", ): (context.context_root / filename).write_text("{", encoding="utf-8") @when("I initialize the context manager from that directory") def step_initialize_context_manager(context): context.manager = ContextManager(context.context_name, context_dir=context.temp_dir) @then("the stored context data should be loaded") def step_verify_loaded_context(context): assert context.manager.messages == context.expected_messages assert context.manager.metadata == context.expected_metadata assert context.manager.state == context.expected_state assert context.manager.global_context == context.expected_global @then("invalid context files should load as empty defaults") def step_verify_invalid_defaults(context): assert context.manager.messages == [] assert context.manager.metadata == {} assert context.manager.state == {} assert context.manager.global_context == {} @given("a temporary home directory for the context manager") def step_temporary_home(context): context.temp_home = Path(tempfile.mkdtemp()) context.context_name = "home_context" @when("I initialize the context manager without a context directory") def step_initialize_default_context(context): with patch("pathlib.Path.home", return_value=context.temp_home): context.manager = ContextManager(context.context_name) @then("the context manager should use the default home context path") def step_verify_default_path(context): expected = context.temp_home / ".cleveragents" / "context" / context.context_name assert context.manager.context_dir == expected assert context.manager.context_dir.exists() @then("deleting the context should remove the directory") def step_verify_delete(context): context.manager.delete() assert not context.manager.context_dir.exists() @given("a context manager for state and metadata updates") def step_state_metadata_manager(context): context.temp_dir = Path(tempfile.mkdtemp()) context.context_name = "state_meta_context" context.manager = ContextManager(context.context_name, context_dir=context.temp_dir) @when("I update state and metadata in the context manager") def step_update_state_metadata(context): context.manager.update_state("mode", "active") context.manager.update_metadata({"owner": "qa"}) context.state_value = context.manager.get_state("mode") context.default_state = context.manager.get_state("missing", "default") context.exists_value = context.manager.exists() @then("the context manager should return the updated values") def step_verify_updated_values(context): assert context.state_value == "active" assert context.default_state == "default" assert context.manager.metadata["owner"] == "qa" @then("the context should report it exists on disk") def step_verify_exists(context): assert context.exists_value is True @given("a context manager with stored messages") def step_context_with_messages(context): context.temp_dir = Path(tempfile.mkdtemp()) context.context_name = "message_context" context.manager = ContextManager(context.context_name, context_dir=context.temp_dir) context.manager.add_message("user", "Hello") context.manager.add_message("assistant", "Hi") context.manager.update_state("counter", 2) context.manager.global_context = {"mode": "chat"} @when("I request the last message and clear the context") def step_last_message_and_clear(context): context.last_messages = context.manager.get_last_n_messages(1) context.manager.clear() @then("the last message should be returned and the context reset") def step_verify_last_and_clear(context): assert len(context.last_messages) == 1 assert context.last_messages[0]["role"] == "assistant" assert context.manager.messages == [] assert context.manager.state == {} assert context.manager.global_context == {} assert context.manager.metadata["context_name"] == context.context_name assert "created_at" in context.manager.metadata assert "last_updated" in context.manager.metadata @given("a context manager with exportable data") def step_exportable_context(context): context.temp_dir = Path(tempfile.mkdtemp()) context.context_name = "export_context" context.manager = ContextManager(context.context_name, context_dir=context.temp_dir) context.manager.add_message("user", "Export this") context.manager.update_state("flag", True) context.manager.global_context = {"mode": "full"} context.manager.metadata["tag"] = "export" context.export_file = context.temp_dir / "exports" / "context.json" @when("I export and import the context") def step_export_import_context(context): context.manager.export_context(context.export_file) context.import_manager = ContextManager( "import_context", context_dir=context.temp_dir ) context.import_manager.import_context(context.export_file) @then("the imported context should match the exported data") def step_verify_imported_data(context): assert context.export_file.exists() assert context.import_manager.messages == context.manager.messages assert context.import_manager.state == context.manager.state assert context.import_manager.global_context == context.manager.global_context assert ( context.import_manager.metadata["context_name"] == context.manager.metadata["context_name"] ) assert context.import_manager.metadata.get("tag") == "export"