mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
35e4f73ae8
Signed-off-by: phernandez <paul@basicmachines.co> Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
174 lines
7.3 KiB
Python
174 lines
7.3 KiB
Python
"""Integration test for project state synchronization between MCP session and CLI config.
|
|
|
|
This test validates the fix for GitHub issue #148 where MCP session and CLI commands
|
|
had inconsistent project state, causing "Project not found" errors and edit failures.
|
|
|
|
The test simulates the exact workflow reported in the issue:
|
|
1. MCP server starts with a default project
|
|
2. Default project is changed via CLI/API
|
|
3. MCP tools should immediately use the new project (no restart needed)
|
|
4. All operations should work consistently in the new project context
|
|
"""
|
|
|
|
import pytest
|
|
from fastmcp import Client
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_project_state_sync_after_default_change(mcp_server, app, config_manager):
|
|
"""Test that MCP session stays in sync when default project is changed."""
|
|
|
|
async with Client(mcp_server) as client:
|
|
# Step 1: Verify initial state - MCP should show test-project as current
|
|
initial_result = await client.call_tool("get_current_project", {})
|
|
assert len(initial_result) == 1
|
|
assert "Current project: test-project" in initial_result[0].text
|
|
|
|
# Step 2: Create a second project that we can switch to
|
|
create_result = await client.call_tool(
|
|
"create_memory_project",
|
|
{
|
|
"project_name": "minerva",
|
|
"project_path": "/tmp/minerva-test-project",
|
|
"set_default": False, # Don't set as default yet
|
|
},
|
|
)
|
|
assert len(create_result) == 1
|
|
assert "✓" in create_result[0].text
|
|
assert "minerva" in create_result[0].text
|
|
|
|
# Step 3: Change default project to minerva via set_default_project tool
|
|
# This simulates the CLI command `basic-memory project default minerva`
|
|
set_default_result = await client.call_tool(
|
|
"set_default_project", {"project_name": "minerva"}
|
|
)
|
|
assert len(set_default_result) == 1
|
|
assert "✓" in set_default_result[0].text
|
|
assert "minerva" in set_default_result[0].text
|
|
|
|
# Step 4: Verify MCP session immediately reflects the change (no restart needed)
|
|
# This tests the fix - session.refresh_from_config() should have been called
|
|
updated_result = await client.call_tool("get_current_project", {})
|
|
assert len(updated_result) == 1
|
|
|
|
# The fix should ensure these are consistent now:
|
|
updated_text = updated_result[0].text
|
|
assert "Current project: minerva" in updated_text
|
|
|
|
# Step 5: Verify config manager also shows the new default
|
|
assert config_manager.default_project == "minerva"
|
|
|
|
# Step 6: Test that note operations work in the new project context
|
|
# This validates that the identifier resolution works correctly
|
|
write_result = await client.call_tool(
|
|
"write_note",
|
|
{
|
|
"title": "Test Consistency Note",
|
|
"folder": "test",
|
|
"content": "# Test Note\n\nThis note tests project state consistency.\n\n- [test] Project state sync working",
|
|
"tags": "test,consistency",
|
|
},
|
|
)
|
|
assert len(write_result) == 1
|
|
assert "Test Consistency Note" in write_result[0].text
|
|
|
|
# Step 7: Test that we can read the note we just created
|
|
read_result = await client.call_tool("read_note", {"identifier": "Test Consistency Note"})
|
|
assert len(read_result) == 1
|
|
assert "Test Consistency Note" in read_result[0].text
|
|
assert "project state sync working" in read_result[0].text.lower()
|
|
|
|
# Step 8: Test that edit operations work (this was failing in the original issue)
|
|
edit_result = await client.call_tool(
|
|
"edit_note",
|
|
{
|
|
"identifier": "Test Consistency Note",
|
|
"operation": "append",
|
|
"content": "\n\n## Update\n\nEdit operation successful after project switch!",
|
|
},
|
|
)
|
|
assert len(edit_result) == 1
|
|
assert "added" in edit_result[0].text.lower() and "lines" in edit_result[0].text.lower()
|
|
|
|
# Step 9: Verify the edit was applied
|
|
final_read_result = await client.call_tool(
|
|
"read_note", {"identifier": "Test Consistency Note"}
|
|
)
|
|
assert len(final_read_result) == 1
|
|
final_content = final_read_result[0].text
|
|
assert "Edit operation successful" in final_content
|
|
|
|
# Clean up - switch back to test-project
|
|
await client.call_tool("switch_project", {"project_name": "test-project"})
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_multiple_project_switches_maintain_consistency(mcp_server, app, config_manager):
|
|
"""Test that multiple project switches maintain consistent state."""
|
|
|
|
async with Client(mcp_server) as client:
|
|
# Create multiple test projects
|
|
for project_name in ["project-a", "project-b", "project-c"]:
|
|
await client.call_tool(
|
|
"create_memory_project",
|
|
{
|
|
"project_name": project_name,
|
|
"project_path": f"/tmp/{project_name}",
|
|
"set_default": False,
|
|
},
|
|
)
|
|
|
|
# Test switching between projects multiple times
|
|
for project_name in ["project-a", "project-b", "project-c", "test-project"]:
|
|
# Set as default
|
|
set_result = await client.call_tool(
|
|
"set_default_project", {"project_name": project_name}
|
|
)
|
|
assert "✓" in set_result[0].text
|
|
|
|
# Verify MCP session immediately reflects the change
|
|
current_result = await client.call_tool("get_current_project", {})
|
|
assert f"Current project: {project_name}" in current_result[0].text
|
|
|
|
# Verify config is also updated
|
|
assert config_manager.default_project == project_name
|
|
|
|
# Test that operations work in this project
|
|
note_title = f"Note in {project_name}"
|
|
write_result = await client.call_tool(
|
|
"write_note",
|
|
{
|
|
"title": note_title,
|
|
"folder": "test",
|
|
"content": f"# {note_title}\n\nTesting operations in {project_name}.",
|
|
"tags": "test",
|
|
},
|
|
)
|
|
assert note_title in write_result[0].text
|
|
|
|
# Clean up - switch back to test-project
|
|
await client.call_tool("set_default_project", {"project_name": "test-project"})
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_session_handles_nonexistent_project_gracefully(mcp_server, app):
|
|
"""Test that session handles attempts to switch to nonexistent projects gracefully."""
|
|
|
|
async with Client(mcp_server) as client:
|
|
# Try to switch to a project that doesn't exist
|
|
switch_result = await client.call_tool(
|
|
"switch_project", {"project_name": "nonexistent-project"}
|
|
)
|
|
assert len(switch_result) == 1
|
|
result_text = switch_result[0].text
|
|
|
|
# Should show an error message
|
|
assert "Error:" in result_text
|
|
assert "not found" in result_text.lower()
|
|
assert "Available projects:" in result_text
|
|
assert "test-project" in result_text # Should list available projects
|
|
|
|
# Verify the session stays on the original project
|
|
current_result = await client.call_tool("get_current_project", {})
|
|
assert "Current project: test-project" in current_result[0].text
|