# Copyright 2024 Heinrich Krupp
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Tests for Memory CRUD API endpoints.
Tests the store_memory endpoint for:
- X-Agent-ID header auto-tagging (agent:<id> appended to tags)
"""
import pytest
import pytest_asyncio
import tempfile
import os
from fastapi.testclient import TestClient
from mcp_memory_service.web.dependencies import set_storage
from mcp_memory_service.storage.sqlite_vec import SqliteVecMemoryStorage
@pytest.fixture
def temp_db():
"""Create a temporary database for testing."""
with tempfile.TemporaryDirectory() as tmpdir:
db_path = os.path.join(tmpdir, "test_memories.db")
yield db_path
@pytest_asyncio.fixture
async def initialized_storage(temp_db, monkeypatch):
"""Create and initialize a real SQLite storage backend."""
monkeypatch.setenv('MCP_SEMANTIC_DEDUP_ENABLED', 'false')
storage = SqliteVecMemoryStorage(temp_db)
await storage.initialize()
yield storage
await storage.close()
@pytest.fixture
def test_app(initialized_storage, monkeypatch):
"""Create a FastAPI test application with initialized storage."""
monkeypatch.setenv('MCP_API_KEY', '')
monkeypatch.setenv('MCP_OAUTH_ENABLED', 'false')
monkeypatch.setenv('MCP_ALLOW_ANONYMOUS_ACCESS', 'true')
monkeypatch.setenv('INCLUDE_HOSTNAME', 'false')
import sys
import importlib
try:
if 'mcp_memory_service.config' in sys.modules:
importlib.reload(sys.modules['mcp_memory_service.config'])
if 'mcp_memory_service.web.oauth.middleware' in sys.modules:
importlib.reload(sys.modules['mcp_memory_service.web.oauth.middleware'])
except (AttributeError, ImportError):
pass
from mcp_memory_service.web.app import app
from mcp_memory_service.web.oauth.middleware import (
get_current_user, require_write_access, require_read_access,
AuthenticationResult
)
set_storage(initialized_storage)
async def mock_get_current_user():
return AuthenticationResult(
authenticated=True,
client_id="test_client",
scope="read write admin",
auth_method="test"
)
app.dependency_overrides[get_current_user] = mock_get_current_user
app.dependency_overrides[require_write_access] = mock_get_current_user
app.dependency_overrides[require_read_access] = mock_get_current_user
client = TestClient(app)
yield client
app.dependency_overrides.clear()
@pytest.mark.integration
def test_store_memory_with_agent_id_header_appends_agent_tag(test_app):
"""X-Agent-ID header auto-appends agent:<id> tag to stored memory."""
response = test_app.post(
"/api/memories",
json={
"content": "Researcher found that the API rate limit is 100 req/min",
"tags": ["api", "rate-limit"],
},
headers={"X-Agent-ID": "researcher"},
)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
stored_tags = data["memory"]["tags"]
assert "agent:researcher" in stored_tags
assert "api" in stored_tags
assert "rate-limit" in stored_tags
@pytest.mark.integration
def test_store_memory_without_agent_id_header_no_agent_tag(test_app):
"""Without X-Agent-ID header, no agent: tag is added."""
response = test_app.post(
"/api/memories",
json={
"content": "Regular memory without agent context",
"tags": ["general"],
},
)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
stored_tags = data["memory"]["tags"]
assert not any(tag.startswith("agent:") for tag in stored_tags)
@pytest.mark.integration
def test_store_memory_agent_id_not_duplicated_if_already_in_tags(test_app):
"""X-Agent-ID header does not duplicate an agent: tag already in tags."""
response = test_app.post(
"/api/memories",
json={
"content": "Memory with pre-existing agent tag",
"tags": ["agent:researcher", "other"],
},
headers={"X-Agent-ID": "researcher"},
)
assert response.status_code == 200
data = response.json()
stored_tags = data["memory"]["tags"]
assert stored_tags.count("agent:researcher") == 1