Files
basicmachines-co-basic-memory/tests/mcp/test_project_context.py
T
phernandez 0bcda4a14a fix: allow recent_activity discovery mode in cloud mode
Add `allow_discovery` parameter to `resolve_project_parameter()` that
enables tools like `recent_activity` to work across all projects in
cloud mode without requiring a project parameter.

- Add `allow_discovery: bool = False` param to resolve_project_parameter
- In cloud mode with allow_discovery=True, return None instead of error
- Update recent_activity to use allow_discovery=True
- Fix circular import by deferring call_get import inside functions
- Add comprehensive tests for resolve_project_parameter

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
2025-12-29 16:22:48 -06:00

144 lines
5.4 KiB
Python

"""Tests for project context utilities."""
import os
from unittest.mock import patch, MagicMock
import pytest
class TestResolveProjectParameter:
"""Tests for resolve_project_parameter function."""
@pytest.mark.asyncio
async def test_cloud_mode_requires_project_by_default(self):
"""In cloud mode, project is required when allow_discovery=False."""
from basic_memory.mcp.project_context import resolve_project_parameter
mock_config = MagicMock()
mock_config.cloud_mode = True
with patch(
"basic_memory.mcp.project_context.ConfigManager"
) as mock_config_manager:
mock_config_manager.return_value.config = mock_config
with pytest.raises(ValueError) as exc_info:
await resolve_project_parameter(project=None, allow_discovery=False)
assert "No project specified" in str(exc_info.value)
assert "Project is required for cloud mode" in str(exc_info.value)
@pytest.mark.asyncio
async def test_cloud_mode_allows_discovery_when_enabled(self):
"""In cloud mode with allow_discovery=True, returns None instead of error."""
from basic_memory.mcp.project_context import resolve_project_parameter
mock_config = MagicMock()
mock_config.cloud_mode = True
with patch(
"basic_memory.mcp.project_context.ConfigManager"
) as mock_config_manager:
mock_config_manager.return_value.config = mock_config
result = await resolve_project_parameter(project=None, allow_discovery=True)
assert result is None
@pytest.mark.asyncio
async def test_cloud_mode_returns_project_when_specified(self):
"""In cloud mode, returns the specified project."""
from basic_memory.mcp.project_context import resolve_project_parameter
mock_config = MagicMock()
mock_config.cloud_mode = True
with patch(
"basic_memory.mcp.project_context.ConfigManager"
) as mock_config_manager:
mock_config_manager.return_value.config = mock_config
result = await resolve_project_parameter(project="my-project")
assert result == "my-project"
@pytest.mark.asyncio
async def test_local_mode_uses_env_var_priority(self):
"""In local mode, BASIC_MEMORY_MCP_PROJECT env var takes priority."""
from basic_memory.mcp.project_context import resolve_project_parameter
mock_config = MagicMock()
mock_config.cloud_mode = False
with patch(
"basic_memory.mcp.project_context.ConfigManager"
) as mock_config_manager:
mock_config_manager.return_value.config = mock_config
with patch.dict(os.environ, {"BASIC_MEMORY_MCP_PROJECT": "env-project"}):
result = await resolve_project_parameter(project="explicit-project")
# Env var should take priority over explicit project
assert result == "env-project"
@pytest.mark.asyncio
async def test_local_mode_uses_explicit_project(self):
"""In local mode without env var, uses explicit project parameter."""
from basic_memory.mcp.project_context import resolve_project_parameter
mock_config = MagicMock()
mock_config.cloud_mode = False
mock_config.default_project_mode = False
with patch(
"basic_memory.mcp.project_context.ConfigManager"
) as mock_config_manager:
mock_config_manager.return_value.config = mock_config
with patch.dict(os.environ, {}, clear=True):
# Remove the env var if it exists
os.environ.pop("BASIC_MEMORY_MCP_PROJECT", None)
result = await resolve_project_parameter(project="explicit-project")
assert result == "explicit-project"
@pytest.mark.asyncio
async def test_local_mode_uses_default_project(self):
"""In local mode with default_project_mode, uses default project."""
from basic_memory.mcp.project_context import resolve_project_parameter
mock_config = MagicMock()
mock_config.cloud_mode = False
mock_config.default_project_mode = True
mock_config.default_project = "default-project"
with patch(
"basic_memory.mcp.project_context.ConfigManager"
) as mock_config_manager:
mock_config_manager.return_value.config = mock_config
with patch.dict(os.environ, {}, clear=True):
os.environ.pop("BASIC_MEMORY_MCP_PROJECT", None)
result = await resolve_project_parameter(project=None)
assert result == "default-project"
@pytest.mark.asyncio
async def test_local_mode_returns_none_when_no_resolution(self):
"""In local mode without any project source, returns None."""
from basic_memory.mcp.project_context import resolve_project_parameter
mock_config = MagicMock()
mock_config.cloud_mode = False
mock_config.default_project_mode = False
with patch(
"basic_memory.mcp.project_context.ConfigManager"
) as mock_config_manager:
mock_config_manager.return_value.config = mock_config
with patch.dict(os.environ, {}, clear=True):
os.environ.pop("BASIC_MEMORY_MCP_PROJECT", None)
result = await resolve_project_parameter(project=None)
assert result is None