Skip to main content
Glama
test_tools_promote.pyβ€’15.3 kB
"""Tests for promote_memory tool.""" import time from unittest.mock import MagicMock, patch import pytest from mnemex.storage.models import Memory, MemoryStatus from mnemex.tools.promote import promote_memory from tests.conftest import make_test_uuid class TestPromoteMemory: """Test suite for promote_memory tool.""" def test_promote_requires_memory_id_or_auto_detect(self): """Test that either memory_id or auto_detect must be provided.""" result = promote_memory() assert result["success"] is False assert "must specify" in result["message"].lower() def test_promote_memory_not_found(self, temp_storage): """Test promoting non-existent memory.""" result = promote_memory(memory_id="00000000-0000-0000-0000-000000000000") assert result["success"] is False assert "not found" in result["message"].lower() def test_promote_already_promoted_memory(self, temp_storage): """Test that already promoted memory returns error.""" test_id = make_test_uuid("mem-promoted") mem = Memory( id=test_id, content="Already promoted", status=MemoryStatus.PROMOTED, promoted_to="/vault/memory.md", ) temp_storage.save_memory(mem) result = promote_memory(memory_id=test_id) assert result["success"] is False assert "already promoted" in result["message"].lower() assert "promoted_to" in result def test_promote_memory_not_meeting_criteria(self, temp_storage): """Test that low-scoring memory cannot be promoted without force.""" now = int(time.time()) test_id = make_test_uuid("mem-low") low_score_mem = Memory( id=test_id, content="Low score memory", use_count=0, last_used=now, created_at=now, strength=1.0, ) temp_storage.save_memory(low_score_mem) result = promote_memory(memory_id=test_id) assert result["success"] is False assert "does not meet" in result["message"].lower() assert "score" in result @patch("mnemex.tools.promote.BasicMemoryIntegration") def test_promote_with_force_flag(self, mock_integration, temp_storage): """Test that force flag bypasses criteria check.""" now = int(time.time()) mock_integration_instance = MagicMock() mock_integration_instance.promote_to_obsidian.return_value = { "success": True, "path": "/vault/forced.md", } mock_integration.return_value = mock_integration_instance test_id = make_test_uuid("mem-force") low_score_mem = Memory( id=test_id, content="Forced promotion", use_count=0, last_used=now, created_at=now ) temp_storage.save_memory(low_score_mem) result = promote_memory(memory_id=test_id, force=True, dry_run=False) assert result["success"] is True assert result["promoted_count"] >= 1 @patch("mnemex.tools.promote.BasicMemoryIntegration") def test_promote_high_scoring_memory(self, mock_integration, temp_storage): """Test promoting a high-scoring memory.""" now = int(time.time()) mock_integration_instance = MagicMock() mock_integration_instance.promote_to_obsidian.return_value = { "success": True, "path": "/vault/high.md", } mock_integration.return_value = mock_integration_instance test_id = make_test_uuid("mem-high") high_score_mem = Memory( id=test_id, content="High value memory", use_count=10, last_used=now, created_at=now - (14 * 86400), # 14 days old strength=1.5, ) temp_storage.save_memory(high_score_mem) result = promote_memory(memory_id=test_id, dry_run=False) assert result["success"] is True assert result["promoted_count"] == 1 assert result["promoted_ids"] == [test_id] def test_promote_dry_run_mode(self, temp_storage): """Test dry run mode doesn't actually promote.""" now = int(time.time()) test_id = make_test_uuid("mem-dry") high_mem = Memory( id=test_id, content="Test dry run", use_count=10, last_used=now, created_at=now - (14 * 86400), strength=1.5, ) temp_storage.save_memory(high_mem) result = promote_memory(memory_id=test_id, dry_run=True, force=True) assert result["success"] is True assert result["dry_run"] is True assert result["promoted_count"] == 0 # Memory should still be active mem = temp_storage.get_memory(test_id) assert mem.status == MemoryStatus.ACTIVE def test_promote_auto_detect_no_candidates(self, temp_storage): """Test auto-detect when no memories meet criteria.""" now = int(time.time()) # Create only low-scoring memories for i in range(3): test_id = make_test_uuid(f"mem-{i}") mem = Memory( id=test_id, content=f"Low score {i}", use_count=0, last_used=now, created_at=now ) temp_storage.save_memory(mem) result = promote_memory(auto_detect=True, dry_run=True) assert result["success"] is True assert result["candidates_found"] == 0 @patch("mnemex.tools.promote.BasicMemoryIntegration") def test_promote_auto_detect_finds_candidates(self, mock_integration, temp_storage): """Test auto-detect finds high-value memories.""" now = int(time.time()) mock_integration_instance = MagicMock() mock_integration_instance.promote_to_obsidian.return_value = { "success": True, "path": "/vault/auto.md", } mock_integration.return_value = mock_integration_instance # Create high-value memory test_id = make_test_uuid("mem-auto") high_mem = Memory( id=test_id, content="High value", use_count=10, last_used=now, created_at=now - (14 * 86400), strength=1.5, ) temp_storage.save_memory(high_mem) result = promote_memory(auto_detect=True, dry_run=True) assert result["success"] is True # May find candidates if criteria met assert "candidates_found" in result def test_promote_result_format(self, temp_storage): """Test that promote result has correct format.""" result = promote_memory(auto_detect=True, dry_run=True) assert result["success"] is True assert "dry_run" in result assert "candidates_found" in result assert "promoted_count" in result assert "promoted_ids" in result assert "candidates" in result assert "message" in result def test_promote_candidate_preview_format(self, temp_storage): """Test that candidate previews have correct format.""" now = int(time.time()) test_id = make_test_uuid("mem-preview") high_mem = Memory( id=test_id, content="Test preview format" * 10, # Long content use_count=10, last_used=now, created_at=now - (14 * 86400), strength=1.5, ) temp_storage.save_memory(high_mem) result = promote_memory(auto_detect=True, dry_run=True) assert result["success"] is True if result["candidates_found"] > 0: candidate = result["candidates"][0] assert "id" in candidate assert "content_preview" in candidate assert "reason" in candidate assert "score" in candidate assert "use_count" in candidate assert "age_days" in candidate # Content should be truncated to 100 chars assert len(candidate["content_preview"]) <= 100 def test_promote_candidates_limited_to_10(self, temp_storage): """Test that candidate list is limited to 10.""" now = int(time.time()) # Create many high-value memories for i in range(15): test_id = make_test_uuid(f"mem-{i:02d}") mem = Memory( id=test_id, content=f"High value {i}", use_count=10, last_used=now, created_at=now - (14 * 86400), strength=1.5, ) temp_storage.save_memory(mem) result = promote_memory(auto_detect=True, dry_run=True) assert result["success"] is True # Candidate preview list should be limited to 10 assert len(result["candidates"]) <= 10 @patch("mnemex.tools.promote.BasicMemoryIntegration") def test_promote_updates_memory_status(self, mock_integration, temp_storage): """Test that promotion updates memory status.""" now = int(time.time()) mock_integration_instance = MagicMock() mock_integration_instance.promote_to_obsidian.return_value = { "success": True, "path": "/vault/promoted.md", } mock_integration.return_value = mock_integration_instance test_id = make_test_uuid("mem-status") mem = Memory( id=test_id, content="Test status update", use_count=10, last_used=now, created_at=now - (14 * 86400), strength=1.5, ) temp_storage.save_memory(mem) result = promote_memory(memory_id=test_id, dry_run=False, force=True) if result["success"] and result["promoted_count"] > 0: updated = temp_storage.get_memory(test_id) assert updated.status == MemoryStatus.PROMOTED assert updated.promoted_at is not None assert updated.promoted_to is not None # Validation tests def test_promote_invalid_uuid_fails(self): """Test that invalid UUID fails.""" with pytest.raises(ValueError, match="memory_id.*valid UUID"): promote_memory(memory_id="not-a-uuid") def test_promote_invalid_target_fails(self): """Test that invalid target fails.""" with pytest.raises(ValueError, match="target"): promote_memory(auto_detect=True, target="invalid-target") def test_promote_empty_string_uuid_fails(self): """Test that empty string UUID fails.""" with pytest.raises(ValueError, match="memory_id"): promote_memory(memory_id="") # Edge cases def test_promote_with_default_parameters(self, temp_storage): """Test default parameter values.""" now = int(time.time()) test_id = make_test_uuid("mem-defaults") mem = Memory( id=test_id, content="Test defaults", use_count=10, last_used=now, created_at=now - (14 * 86400), strength=1.5, ) temp_storage.save_memory(mem) # Should fail because neither memory_id nor auto_detect provided result = promote_memory() assert result["success"] is False def test_promote_obsidian_target(self, temp_storage): """Test that obsidian is valid target.""" result = promote_memory(auto_detect=True, dry_run=True, target="obsidian") assert result["success"] is True def test_promote_message_dry_run(self, temp_storage): """Test that dry run message says 'Would promote'.""" result = promote_memory(auto_detect=True, dry_run=True) assert result["success"] is True assert "would promote" in result["message"].lower() @patch("mnemex.tools.promote.BasicMemoryIntegration") def test_promote_message_actual_run(self, mock_integration, temp_storage): """Test that actual run message says 'Promoted'.""" mock_integration_instance = MagicMock() mock_integration_instance.promote_to_obsidian.return_value = { "success": True, "path": "/vault/test.md", } mock_integration.return_value = mock_integration_instance result = promote_memory(auto_detect=True, dry_run=False) assert result["success"] is True assert result["message"].startswith("Promoted") def test_promote_empty_database(self, temp_storage): """Test promotion on empty database.""" result = promote_memory(auto_detect=True, dry_run=True) assert result["success"] is True assert result["candidates_found"] == 0 assert result["promoted_count"] == 0 @patch("mnemex.tools.promote.BasicMemoryIntegration") def test_promote_integration_failure(self, mock_integration, temp_storage): """Test handling of integration failure.""" now = int(time.time()) mock_integration_instance = MagicMock() mock_integration_instance.promote_to_obsidian.return_value = { "success": False, "error": "Write failed", } mock_integration.return_value = mock_integration_instance test_id = make_test_uuid("mem-fail") mem = Memory( id=test_id, content="Test failure", use_count=10, last_used=now, created_at=now - (14 * 86400), strength=1.5, ) temp_storage.save_memory(mem) result = promote_memory(memory_id=test_id, dry_run=False, force=True) # Should still succeed but with 0 promoted assert result["success"] is True assert result["promoted_count"] == 0 def test_promote_candidates_sorted_by_score(self, temp_storage): """Test that candidates are sorted by score descending.""" now = int(time.time()) # Create memories with different scores for i in range(3): test_id = make_test_uuid(f"mem-{i}") mem = Memory( id=test_id, content=f"Memory {i}", use_count=i * 5, last_used=now, created_at=now - (14 * 86400), strength=1.0 + (i * 0.2), ) temp_storage.save_memory(mem) result = promote_memory(auto_detect=True, dry_run=True) assert result["success"] is True if result["candidates_found"] > 1: scores = [c["score"] for c in result["candidates"]] assert scores == sorted(scores, reverse=True) def test_promote_preserves_memory_content(self, temp_storage): """Test that promotion doesn't modify content.""" now = int(time.time()) original_content = "This content should not change" test_id = make_test_uuid("mem-content") mem = Memory( id=test_id, content=original_content, use_count=10, last_used=now, created_at=now - (14 * 86400), strength=1.5, ) temp_storage.save_memory(mem) promote_memory(memory_id=test_id, dry_run=True, force=True) updated = temp_storage.get_memory(test_id) assert updated.content == original_content

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mnemexai/mnemex'

If you have feedback or need assistance with the MCP directory API, please join our Discord server