mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
35884ef3a7
FastMCP library changes now require calling decorated functions via the .fn attribute: - Tools: @mcp.tool() functions return FunctionTool, call with tool.fn() - Prompts: @mcp.prompt() functions return FunctionPrompt, call with prompt.fn() - Resources: @mcp.resource() functions return FunctionResource, call with resource.fn() Updated core files: - view_note.py: read_note() → read_note.fn() - read_note.py: search_notes() → search_notes.fn() (2 locations) - tool.py: 6 MCP tool calls updated to use .fn - recent_activity.py: recent_activity() → recent_activity.fn() - project.py: project_info() → project_info.fn() with type ignore Updated 100+ test files systematically to use .fn attribute and fixed mock targets. All 869 tests now pass. Fixes view_note tool error in Claude Desktop. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
145 lines
4.8 KiB
Python
145 lines
4.8 KiB
Python
"""Tests for the project_info MCP tool."""
|
|
|
|
from unittest.mock import patch, MagicMock
|
|
|
|
import pytest
|
|
from httpx import Response
|
|
|
|
from basic_memory.mcp.resources.project_info import project_info
|
|
from basic_memory.schemas import (
|
|
ProjectInfoResponse,
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_project_info_tool():
|
|
"""Test that the project_info tool calls the API and returns structured data."""
|
|
# Create a mock response
|
|
mock_response = MagicMock(spec=Response)
|
|
mock_response.status_code = 200
|
|
|
|
# Create sample data that matches the schema
|
|
sample_data = {
|
|
"project_name": "test",
|
|
"project_path": "/path/to/test",
|
|
"available_projects": {
|
|
"test": {
|
|
"path": "/path/to/test",
|
|
"active": True,
|
|
"id": 1,
|
|
"is_default": True,
|
|
"permalink": "test",
|
|
},
|
|
"other": {
|
|
"path": "/path/to/other",
|
|
"active": False,
|
|
"id": 2,
|
|
"is_default": False,
|
|
"permalink": "other",
|
|
},
|
|
},
|
|
"default_project": "test",
|
|
"statistics": {
|
|
"total_entities": 42,
|
|
"total_observations": 24,
|
|
"total_relations": 18,
|
|
"total_unresolved_relations": 3,
|
|
"entity_types": {"note": 30, "conversation": 12},
|
|
"observation_categories": {"tech": 15, "note": 9},
|
|
"relation_types": {"relates_to": 10, "implements": 8},
|
|
"most_connected_entities": [
|
|
{"id": 1, "title": "Test Entity", "permalink": "test/entity", "relation_count": 5}
|
|
],
|
|
"isolated_entities": 2,
|
|
},
|
|
"activity": {
|
|
"recently_created": [
|
|
{
|
|
"id": 1,
|
|
"title": "Test Entity",
|
|
"permalink": "test/entity",
|
|
"entity_type": "note",
|
|
"created_at": "2025-03-05T12:00:00",
|
|
}
|
|
],
|
|
"recently_updated": [
|
|
{
|
|
"id": 1,
|
|
"title": "Test Entity",
|
|
"permalink": "test/entity",
|
|
"entity_type": "note",
|
|
"updated_at": "2025-03-05T12:00:00",
|
|
}
|
|
],
|
|
"monthly_growth": {
|
|
"2025-03": {"entities": 10, "observations": 5, "relations": 8, "total": 23}
|
|
},
|
|
},
|
|
"system": {
|
|
"version": "0.1.0",
|
|
"database_path": "/path/to/db.sqlite",
|
|
"database_size": "2.50 MB",
|
|
"watch_status": {
|
|
"running": True,
|
|
"start_time": "2025-03-05T12:00:00",
|
|
"pid": 1234,
|
|
"error_count": 0,
|
|
"synced_files": 42,
|
|
},
|
|
"timestamp": "2025-03-05T12:00:00",
|
|
},
|
|
}
|
|
|
|
mock_response.json.return_value = sample_data
|
|
|
|
# Mock the call_get function
|
|
with patch(
|
|
"basic_memory.mcp.resources.project_info.call_get", return_value=mock_response
|
|
) as mock_call_get:
|
|
# Call the function
|
|
result = await project_info.fn()
|
|
|
|
# Verify that call_get was called with the correct URL
|
|
mock_call_get.assert_called_once()
|
|
args, kwargs = mock_call_get.call_args
|
|
assert args[1] == "/test-project/project/info"
|
|
|
|
# Verify the result is a ProjectInfoResponse
|
|
assert isinstance(result, ProjectInfoResponse)
|
|
|
|
# Verify the content
|
|
assert result.project_name == "test"
|
|
assert result.project_path == "/path/to/test"
|
|
assert "test" in result.available_projects
|
|
assert result.default_project == "test"
|
|
|
|
# Check statistics
|
|
assert result.statistics.total_entities == 42
|
|
assert result.statistics.total_observations == 24
|
|
assert result.statistics.total_relations == 18
|
|
|
|
# Check activity
|
|
assert len(result.activity.recently_created) == 1
|
|
assert result.activity.recently_created[0]["title"] == "Test Entity"
|
|
|
|
# Check system
|
|
assert result.system.version == "0.1.0"
|
|
assert result.system.database_size == "2.50 MB"
|
|
assert result.system.watch_status is not None
|
|
assert result.system.watch_status["running"] is True
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_project_info_error_handling():
|
|
"""Test that the project_info tool handles errors gracefully."""
|
|
# Mock call_get to raise an exception
|
|
with patch(
|
|
"basic_memory.mcp.resources.project_info.call_get", side_effect=Exception("Test error")
|
|
):
|
|
# Verify that the exception propagates
|
|
with pytest.raises(Exception) as excinfo:
|
|
await project_info.fn()
|
|
|
|
# Verify error message
|
|
assert "Test error" in str(excinfo.value)
|