1+ """Tests for project context utilities."""
2+
3+ import os
4+ from unittest .mock import patch , MagicMock
5+
6+ import pytest
7+
8+
9+ class TestResolveProjectParameter :
10+ """Tests for resolve_project_parameter function."""
11+
12+ @pytest .mark .asyncio
13+ async def test_cloud_mode_requires_project_by_default (self ):
14+ """In cloud mode, project is required when allow_discovery=False."""
15+ from basic_memory .mcp .project_context import resolve_project_parameter
16+
17+ mock_config = MagicMock ()
18+ mock_config .cloud_mode = True
19+
20+ with patch (
21+ "basic_memory.mcp.project_context.ConfigManager"
22+ ) as mock_config_manager :
23+ mock_config_manager .return_value .config = mock_config
24+
25+ with pytest .raises (ValueError ) as exc_info :
26+ await resolve_project_parameter (project = None , allow_discovery = False )
27+
28+ assert "No project specified" in str (exc_info .value )
29+ assert "Project is required for cloud mode" in str (exc_info .value )
30+
31+ @pytest .mark .asyncio
32+ async def test_cloud_mode_allows_discovery_when_enabled (self ):
33+ """In cloud mode with allow_discovery=True, returns None instead of error."""
34+ from basic_memory .mcp .project_context import resolve_project_parameter
35+
36+ mock_config = MagicMock ()
37+ mock_config .cloud_mode = True
38+
39+ with patch (
40+ "basic_memory.mcp.project_context.ConfigManager"
41+ ) as mock_config_manager :
42+ mock_config_manager .return_value .config = mock_config
43+
44+ result = await resolve_project_parameter (project = None , allow_discovery = True )
45+
46+ assert result is None
47+
48+ @pytest .mark .asyncio
49+ async def test_cloud_mode_returns_project_when_specified (self ):
50+ """In cloud mode, returns the specified project."""
51+ from basic_memory .mcp .project_context import resolve_project_parameter
52+
53+ mock_config = MagicMock ()
54+ mock_config .cloud_mode = True
55+
56+ with patch (
57+ "basic_memory.mcp.project_context.ConfigManager"
58+ ) as mock_config_manager :
59+ mock_config_manager .return_value .config = mock_config
60+
61+ result = await resolve_project_parameter (project = "my-project" )
62+
63+ assert result == "my-project"
64+
65+ @pytest .mark .asyncio
66+ async def test_local_mode_uses_env_var_priority (self ):
67+ """In local mode, BASIC_MEMORY_MCP_PROJECT env var takes priority."""
68+ from basic_memory .mcp .project_context import resolve_project_parameter
69+
70+ mock_config = MagicMock ()
71+ mock_config .cloud_mode = False
72+
73+ with patch (
74+ "basic_memory.mcp.project_context.ConfigManager"
75+ ) as mock_config_manager :
76+ mock_config_manager .return_value .config = mock_config
77+
78+ with patch .dict (os .environ , {"BASIC_MEMORY_MCP_PROJECT" : "env-project" }):
79+ result = await resolve_project_parameter (project = "explicit-project" )
80+
81+ # Env var should take priority over explicit project
82+ assert result == "env-project"
83+
84+ @pytest .mark .asyncio
85+ async def test_local_mode_uses_explicit_project (self ):
86+ """In local mode without env var, uses explicit project parameter."""
87+ from basic_memory .mcp .project_context import resolve_project_parameter
88+
89+ mock_config = MagicMock ()
90+ mock_config .cloud_mode = False
91+ mock_config .default_project_mode = False
92+
93+ with patch (
94+ "basic_memory.mcp.project_context.ConfigManager"
95+ ) as mock_config_manager :
96+ mock_config_manager .return_value .config = mock_config
97+
98+ with patch .dict (os .environ , {}, clear = True ):
99+ # Remove the env var if it exists
100+ os .environ .pop ("BASIC_MEMORY_MCP_PROJECT" , None )
101+ result = await resolve_project_parameter (project = "explicit-project" )
102+
103+ assert result == "explicit-project"
104+
105+ @pytest .mark .asyncio
106+ async def test_local_mode_uses_default_project (self ):
107+ """In local mode with default_project_mode, uses default project."""
108+ from basic_memory .mcp .project_context import resolve_project_parameter
109+
110+ mock_config = MagicMock ()
111+ mock_config .cloud_mode = False
112+ mock_config .default_project_mode = True
113+ mock_config .default_project = "default-project"
114+
115+ with patch (
116+ "basic_memory.mcp.project_context.ConfigManager"
117+ ) as mock_config_manager :
118+ mock_config_manager .return_value .config = mock_config
119+
120+ with patch .dict (os .environ , {}, clear = True ):
121+ os .environ .pop ("BASIC_MEMORY_MCP_PROJECT" , None )
122+ result = await resolve_project_parameter (project = None )
123+
124+ assert result == "default-project"
125+
126+ @pytest .mark .asyncio
127+ async def test_local_mode_returns_none_when_no_resolution (self ):
128+ """In local mode without any project source, returns None."""
129+ from basic_memory .mcp .project_context import resolve_project_parameter
130+
131+ mock_config = MagicMock ()
132+ mock_config .cloud_mode = False
133+ mock_config .default_project_mode = False
134+
135+ with patch (
136+ "basic_memory.mcp.project_context.ConfigManager"
137+ ) as mock_config_manager :
138+ mock_config_manager .return_value .config = mock_config
139+
140+ with patch .dict (os .environ , {}, clear = True ):
141+ os .environ .pop ("BASIC_MEMORY_MCP_PROJECT" , None )
142+ result = await resolve_project_parameter (project = None )
143+
144+ assert result is None
0 commit comments