from personal_agent.database.database import Database from personal_agent.memory.models import MemoryCategory from personal_agent.memory.store import LongTermMemoryStore from personal_agent.tools.memory import ( LongTermMemoryDeleteTool, LongTermMemorySaveTool, LongTermMemorySearchTool, ToolError, ) def create_store(tmp_path): database = Database(tmp_path / "test.db") database.migrate() return LongTermMemoryStore(database) def test_save_tool_persists_memory(tmp_path): store = create_store(tmp_path) tool = LongTermMemorySaveTool(store) result = tool.execute( { "content": "User prefers Rust for systems programming.", "category": "semantic", } ) memories = store.list() assert len(memories) == 1 assert memories[0].content == "User prefers Rust for systems programming." assert memories[0].category is MemoryCategory.SEMANTIC assert str(memories[0].id) in result.content def test_save_tool_rejects_empty_content(tmp_path): store = create_store(tmp_path) tool = LongTermMemorySaveTool(store) try: tool.execute({"content": " ", "category": "semantic"}) except ToolError as error: assert str(error) == "content must not be empty" else: raise AssertionError("Expected ToolError") def test_save_tool_rejects_invalid_category(tmp_path): store = create_store(tmp_path) tool = LongTermMemorySaveTool(store) try: tool.execute( { "content": "Something", "category": "invalid", } ) except ToolError as error: assert str(error) == "category must be 'semantic' or 'episodic'" else: raise AssertionError("Expected ToolError") def test_search_tool_returns_matching_memories(tmp_path): store = create_store(tmp_path) save_tool = LongTermMemorySaveTool(store) search_tool = LongTermMemorySearchTool(store) save_tool.execute( { "content": "User prefers Rust for systems programming.", "category": "semantic", } ) save_tool.execute( { "content": "User visited Tokyo last year.", "category": "episodic", } ) result = search_tool.execute({"query": "Rust"}) assert "User prefers Rust for systems programming." in result.content assert "User visited Tokyo last year." not in result.content def test_search_tool_can_filter_category(tmp_path): store = create_store(tmp_path) save_tool = LongTermMemorySaveTool(store) search_tool = LongTermMemorySearchTool(store) save_tool.execute( { "content": "User likes Rust.", "category": "semantic", } ) save_tool.execute( { "content": "User worked on a Rust project last summer.", "category": "episodic", } ) result = search_tool.execute( { "query": "Rust", "category": "episodic", } ) assert "worked on a Rust project" in result.content assert "User likes Rust." not in result.content def test_search_tool_returns_no_matches(tmp_path): store = create_store(tmp_path) search_tool = LongTermMemorySearchTool(store) result = search_tool.execute({"query": "nonexistent"}) assert result.content == "No matching memories found." def test_search_tool_rejects_empty_query(tmp_path): store = create_store(tmp_path) search_tool = LongTermMemorySearchTool(store) try: search_tool.execute({"query": " "}) except ToolError as error: assert str(error) == "query must not be empty" else: raise AssertionError("Expected ToolError") def test_delete_tool_removes_memory(tmp_path): store = create_store(tmp_path) save_tool = LongTermMemorySaveTool(store) delete_tool = LongTermMemoryDeleteTool(store) result = save_tool.execute( { "content": "User likes Rust.", "category": "semantic", } ) memory_id = result.content.removeprefix("Saved memory: ") delete_result = delete_tool.execute({"id": memory_id}) assert delete_result.content == f"Deleted memory: {memory_id}" assert store.list() == [] def test_delete_tool_rejects_invalid_id(tmp_path): store = create_store(tmp_path) delete_tool = LongTermMemoryDeleteTool(store) try: delete_tool.execute({"id": "not-a-uuid"}) except ToolError as error: assert str(error) == "id must be a valid UUID" else: raise AssertionError("Expected ToolError")