We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/doobidoo/mcp-memory-service'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
"""
Integration tests for Memory dataclass with ontology validation
Tests the complete ontology validation workflow in Memory creation.
"""
import pytest
import hashlib
from pathlib import Path
import importlib.util
import sys
# Load ontology module first
ontology_path = Path(__file__).parent.parent / "src" / "mcp_memory_service" / "models" / "ontology.py"
spec_ont = importlib.util.spec_from_file_location("ontology_test", ontology_path)
ontology_module = importlib.util.module_from_spec(spec_ont)
spec_ont.loader.exec_module(ontology_module)
# Load tag_taxonomy module
tag_tax_path = Path(__file__).parent.parent / "src" / "mcp_memory_service" / "models" / "tag_taxonomy.py"
spec_tag = importlib.util.spec_from_file_location("tag_taxonomy_test", tag_tax_path)
tag_taxonomy_module = importlib.util.module_from_spec(spec_tag)
spec_tag.loader.exec_module(tag_taxonomy_module)
# Create a fake package for the absolute imports
fake_package = type(sys)('mcp_memory_service')
fake_models = type(sys)('models')
fake_package.models = fake_models
fake_models.ontology = ontology_module
fake_models.tag_taxonomy = tag_taxonomy_module
sys.modules['mcp_memory_service'] = fake_package
sys.modules['mcp_memory_service.models'] = fake_models
sys.modules['mcp_memory_service.models.ontology'] = ontology_module
sys.modules['mcp_memory_service.models.tag_taxonomy'] = tag_taxonomy_module
# Now load memory module
memory_path = Path(__file__).parent.parent / "src" / "mcp_memory_service" / "models" / "memory.py"
spec = importlib.util.spec_from_file_location("memory_test", memory_path)
memory_module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(memory_module)
Memory = memory_module.Memory
class TestBurstI1MemoryOntologyValidation:
"""Tests for Burst I.1: Memory Dataclass Ontology Validation"""
def test_valid_base_type_passes(self):
"""Valid base types should pass validation"""
content = "Test observation"
content_hash = hashlib.sha256(content.encode()).hexdigest()
memory = Memory(
content=content,
content_hash=content_hash,
memory_type="observation"
)
assert memory.memory_type == "observation"
def test_valid_subtype_passes(self):
"""Valid subtypes should pass validation"""
content = "Edited config.py"
content_hash = hashlib.sha256(content.encode()).hexdigest()
memory = Memory(
content=content,
content_hash=content_hash,
memory_type="code_edit"
)
assert memory.memory_type == "code_edit"
def test_invalid_type_defaults_to_observation(self, caplog):
"""Invalid types should default to 'observation' with warning"""
content = "Test content"
content_hash = hashlib.sha256(content.encode()).hexdigest()
memory = Memory(
content=content,
content_hash=content_hash,
memory_type="invalid_type"
)
# Should default to observation
assert memory.memory_type == "observation"
# Should log warning
assert "Invalid memory_type" in caplog.text
assert "invalid_type" in caplog.text
def test_none_memory_type_allowed(self):
"""None memory_type should be preserved (backward compatibility)"""
content = "Test content"
content_hash = hashlib.sha256(content.encode()).hexdigest()
memory = Memory(
content=content,
content_hash=content_hash,
memory_type=None
)
assert memory.memory_type is None
class TestBurstI2TagValidation:
"""Tests for Burst I.2: Tag Validation in Memory"""
def test_valid_namespaced_tags_pass(self):
"""Valid namespaced tags should pass without warnings"""
content = "Test content"
content_hash = hashlib.sha256(content.encode()).hexdigest()
memory = Memory(
content=content,
content_hash=content_hash,
tags=["sys:core", "q:high", "proj:auth", "topic:security"]
)
assert memory.tags == ["sys:core", "q:high", "proj:auth", "topic:security"]
def test_legacy_tags_pass(self, caplog):
"""Legacy tags (no namespace) should pass without warnings"""
content = "Test content"
content_hash = hashlib.sha256(content.encode()).hexdigest()
memory = Memory(
content=content,
content_hash=content_hash,
tags=["python", "refactoring", "bug-fix"]
)
assert memory.tags == ["python", "refactoring", "bug-fix"]
# Should not log anything for legacy tags
assert "invalid namespaces" not in caplog.text.lower()
def test_invalid_namespaced_tags_logged(self, caplog):
"""Invalid namespaced tags should be logged but preserved"""
import logging
caplog.set_level(logging.INFO)
content = "Test content"
content_hash = hashlib.sha256(content.encode()).hexdigest()
memory = Memory(
content=content,
content_hash=content_hash,
tags=["invalid:tag", "bad:namespace"]
)
# Tags should still be preserved
assert memory.tags == ["invalid:tag", "bad:namespace"]
# Should log info message
assert "invalid namespaces" in caplog.text.lower()
assert "invalid:tag" in caplog.text
assert "bad:namespace" in caplog.text
def test_mixed_tags_only_invalid_logged(self, caplog):
"""Mix of valid, invalid, and legacy tags"""
import logging
caplog.set_level(logging.INFO)
content = "Test content"
content_hash = hashlib.sha256(content.encode()).hexdigest()
memory = Memory(
content=content,
content_hash=content_hash,
tags=["sys:core", "python", "bad:namespace", "q:high", "legacy-tag"]
)
# All tags preserved
assert len(memory.tags) == 5
# Only invalid namespaced tag logged
assert "bad:namespace" in caplog.text
assert "sys:core" not in caplog.text # Valid, not logged
assert "python" not in caplog.text # Legacy, not logged
def test_empty_tags_gets_untagged_default(self):
"""Empty tags list should get 'untagged' default tag"""
content = "Test content"
content_hash = hashlib.sha256(content.encode()).hexdigest()
memory = Memory(
content=content,
content_hash=content_hash,
tags=[]
)
assert memory.tags == ["untagged"]
def test_explicit_tags_not_adds_untagged(self):
"""Memory with explicit tags should NOT get 'untagged' added"""
content = "Test content with tags"
content_hash = hashlib.sha256(content.encode()).hexdigest()
memory = Memory(
content=content,
content_hash=content_hash,
tags=["python", "testing"]
)
assert memory.tags == ["python", "testing"]
assert "untagged" not in memory.tags
def test_none_tags_gets_untagged_default(self):
"""Memory created without tags param should get 'untagged' default"""
content = "Test content no tags"
content_hash = hashlib.sha256(content.encode()).hexdigest()
memory = Memory(
content=content,
content_hash=content_hash,
)
assert memory.tags == ["untagged"]