Files
basicmachines-co-basic-memory/tests/mcp/test_tool_basic_memory_diagnostics.py
T
claude[bot] b181783cdf feat: implement basic_memory_diagnostics tool for version and system information
Add comprehensive diagnostic tool that provides:
- Basic Memory version and API version information
- System details (Python, OS, architecture)
- Configuration summary with JSON dump
- Project context and status
- Robust error handling with troubleshooting guidance

The tool helps users troubleshoot installations, verify features,
and gather system information for support requests.

Resolves #187

Co-authored-by: Drew Cain <groksrc@users.noreply.github.com>
2025-07-01 19:10:30 +00:00

213 lines
8.3 KiB
Python

"""Tests for basic_memory_diagnostics MCP tool."""
import json
import platform
import sys
from pathlib import Path
from unittest.mock import MagicMock, patch
import pytest
import basic_memory
from basic_memory.mcp.tools.basic_memory_diagnostics import basic_memory_diagnostics
@pytest.mark.asyncio
async def test_basic_memory_diagnostics_basic_functionality():
"""Test basic diagnostic functionality without project parameter."""
result = await basic_memory_diagnostics.fn()
# Check that key sections are present
assert "# Basic Memory Diagnostics" in result
assert "## Version Information" in result
assert "## System Information" in result
assert "## Configuration" in result
assert "## Default Project Context" in result
# Check version information
assert f"**Basic Memory Version**: {basic_memory.__version__}" in result
assert f"**API Version**: {basic_memory.__api_version__}" in result
# Check system information
assert f"**Python Version**: {sys.version.split()[0]}" in result
assert f"**Platform**: {platform.system()} {platform.release()}" in result
assert f"**Architecture**: {platform.machine()}" in result
@pytest.mark.asyncio
async def test_basic_memory_diagnostics_with_project_parameter():
"""Test diagnostic with valid project parameter."""
# Mock project session to return a valid project
mock_project = MagicMock()
mock_project.name = "test-project"
mock_project.home = "/path/to/test-project"
with patch("basic_memory.mcp.tools.basic_memory_diagnostics.get_active_project") as mock_get_project:
mock_get_project.return_value = mock_project
with patch("pathlib.Path.exists") as mock_exists:
mock_exists.return_value = True
result = await basic_memory_diagnostics.fn(project="test-project")
# Check project-specific information
assert "## Project Context" in result
assert "**Requested Project**: test-project" in result
assert "**Active Project Name**: test-project" in result
assert "**Project Path**: /path/to/test-project" in result
assert "**Project Exists**: ✅ Yes" in result
@pytest.mark.asyncio
async def test_basic_memory_diagnostics_with_invalid_project():
"""Test diagnostic with invalid project parameter."""
with patch("basic_memory.mcp.tools.basic_memory_diagnostics.get_active_project") as mock_get_project:
mock_get_project.side_effect = Exception("Project not found")
result = await basic_memory_diagnostics.fn(project="invalid-project")
# Check error handling for invalid project
assert "## Project Context" in result
assert "**Requested Project**: invalid-project" in result
assert "❌ **Project Error**: Project not found" in result
assert "**Troubleshooting:**" in result
@pytest.mark.asyncio
async def test_basic_memory_diagnostics_configuration_success():
"""Test diagnostic with successful configuration access."""
# Mock app config
mock_config = MagicMock()
mock_config.env = "test"
mock_config.projects = {"main": "/path/to/main", "secondary": "/path/to/secondary"}
mock_config.default_project = "main"
mock_config.log_level = "INFO"
mock_config.debug = False
mock_config.database_url = "sqlite:///test.db"
mock_config._config_file_path = "/path/to/config.json"
with patch("basic_memory.mcp.tools.basic_memory_diagnostics.app_config", mock_config):
result = await basic_memory_diagnostics.fn()
# Check configuration information
assert "**Config File Path**: /path/to/config.json" in result
assert "**Configuration JSON**:" in result
assert '"env": "test"' in result
assert '"main": "/path/to/main"' in result
assert '"default_project": "main"' in result
@pytest.mark.asyncio
async def test_basic_memory_diagnostics_configuration_error():
"""Test diagnostic when configuration access fails."""
with patch("basic_memory.mcp.tools.basic_memory_diagnostics.app_config") as mock_config:
# Simulate import error for configuration
mock_config.side_effect = ImportError("Configuration not available")
result = await basic_memory_diagnostics.fn()
# Check error handling for configuration
assert "❌ **Unable to access configuration**:" in result
assert "**Troubleshooting:**" in result
assert "configuration system may not be initialized" in result
@pytest.mark.asyncio
async def test_basic_memory_diagnostics_exception_handling():
"""Test diagnostic handles unexpected exceptions gracefully."""
# Mock basic_memory module to raise an exception when accessing version
with patch.object(basic_memory, '__version__', side_effect=Exception("Unexpected error")):
result = await basic_memory_diagnostics.fn()
# Should return error format but still include minimal info
assert "# Basic Memory Diagnostics - Error" in result
assert "❌ **Unable to generate diagnostic information**:" in result
assert "**Troubleshooting:**" in result
assert "**Minimal Information Available:**" in result
@pytest.mark.asyncio
async def test_basic_memory_diagnostics_default_project_info():
"""Test diagnostic shows default project information correctly."""
# Mock app config with default project
mock_config = MagicMock()
mock_config.default_project = "main"
mock_config.projects = {"main": "/path/to/main"}
with patch("basic_memory.mcp.tools.basic_memory_diagnostics.app_config", mock_config):
with patch("pathlib.Path.exists") as mock_exists:
mock_exists.return_value = True
result = await basic_memory_diagnostics.fn()
# Check default project information
assert "## Default Project Context" in result
assert "**Default Project**: main" in result
assert "**Default Project Path**: /path/to/main" in result
assert "**Project Exists**: ✅ Yes" in result
@pytest.mark.asyncio
async def test_basic_memory_diagnostics_default_project_missing():
"""Test diagnostic handles missing default project gracefully."""
# Mock app config with default project that doesn't exist
mock_config = MagicMock()
mock_config.default_project = "main"
mock_config.projects = {"main": "/path/to/nonexistent"}
with patch("basic_memory.mcp.tools.basic_memory_diagnostics.app_config", mock_config):
with patch("pathlib.Path.exists") as mock_exists:
mock_exists.return_value = False
result = await basic_memory_diagnostics.fn()
# Check missing project indication
assert "**Project Exists**: ❌ No" in result
@pytest.mark.asyncio
async def test_basic_memory_diagnostics_json_serialization():
"""Test that configuration JSON serialization works correctly."""
# Mock app config with complex data types
mock_config = MagicMock()
mock_config.env = "test"
mock_config.projects = {"main": "/path/to/main"}
mock_config.default_project = "main"
mock_config.log_level = "DEBUG"
mock_config.debug = True
mock_config.database_url = "sqlite:///memory.db"
with patch("basic_memory.mcp.tools.basic_memory_diagnostics.app_config", mock_config):
result = await basic_memory_diagnostics.fn()
# Extract and validate JSON content
json_start = result.find("```json\n") + 8
json_end = result.find("\n```", json_start)
json_content = result[json_start:json_end]
# Should be valid JSON
parsed_config = json.loads(json_content)
assert parsed_config["env"] == "test"
assert parsed_config["projects"]["main"] == "/path/to/main"
assert parsed_config["default_project"] == "main"
assert parsed_config["debug"] is True
@pytest.mark.asyncio
async def test_basic_memory_diagnostics_project_nonexistent_path():
"""Test diagnostic with project that has non-existent path."""
# Mock project session to return a project with non-existent path
mock_project = MagicMock()
mock_project.name = "test-project"
mock_project.home = "/nonexistent/path"
with patch("basic_memory.mcp.tools.basic_memory_diagnostics.get_active_project") as mock_get_project:
mock_get_project.return_value = mock_project
with patch("pathlib.Path.exists") as mock_exists:
mock_exists.return_value = False
result = await basic_memory_diagnostics.fn(project="test-project")
# Check that non-existent path is indicated
assert "**Project Exists**: ❌ No" in result