|
1 | 1 | """Integration tests for CLI routing flags (--local/--cloud). |
2 | 2 |
|
3 | 3 | These tests verify that the --local and --cloud flags work correctly |
4 | | -across CLI commands, and that the MCP command forces local routing. |
| 4 | +across CLI commands, and that MCP routing varies by transport. |
5 | 5 |
|
6 | 6 | Note: Environment variable behavior during command execution is tested |
7 | 7 | in unit tests (tests/cli/test_routing.py) which can properly monkeypatch |
@@ -89,33 +89,93 @@ def test_tool_edit_note_both_flags_error(self): |
89 | 89 | assert "Cannot specify both --local and --cloud" in result.output |
90 | 90 |
|
91 | 91 |
|
92 | | -class TestMcpCommandForcesLocal: |
93 | | - """Tests that the MCP command forces local routing.""" |
| 92 | +class TestMcpCommandRouting: |
| 93 | + """Tests that MCP routing varies by transport.""" |
94 | 94 |
|
95 | | - def test_mcp_sets_force_local_env(self, monkeypatch): |
96 | | - """MCP command should set BASIC_MEMORY_FORCE_LOCAL before server starts.""" |
97 | | - # Track what environment variable was set |
98 | | - env_set_value = [] |
| 95 | + def test_mcp_stdio_does_not_force_local(self, monkeypatch): |
| 96 | + """Stdio transport should not inject explicit local routing env vars.""" |
| 97 | + # Ensure env is clean before test |
| 98 | + monkeypatch.delenv("BASIC_MEMORY_FORCE_LOCAL", raising=False) |
| 99 | + monkeypatch.delenv("BASIC_MEMORY_EXPLICIT_ROUTING", raising=False) |
| 100 | + |
| 101 | + env_at_run = {} |
99 | 102 |
|
100 | | - # Mock the MCP server run to capture env state without actually starting server |
101 | 103 | import basic_memory.cli.commands.mcp as mcp_mod |
102 | 104 |
|
103 | 105 | def mock_run(*args, **kwargs): |
104 | | - env_set_value.append(os.environ.get("BASIC_MEMORY_FORCE_LOCAL")) |
105 | | - # Don't actually start the server |
| 106 | + env_at_run["FORCE_LOCAL"] = os.environ.get("BASIC_MEMORY_FORCE_LOCAL") |
| 107 | + env_at_run["EXPLICIT"] = os.environ.get("BASIC_MEMORY_EXPLICIT_ROUTING") |
106 | 108 | raise SystemExit(0) |
107 | 109 |
|
108 | | - # Get the actual mcp_server from the module |
109 | 110 | monkeypatch.setattr(mcp_mod.mcp_server, "run", mock_run) |
| 111 | + monkeypatch.setattr(mcp_mod, "init_mcp_logging", lambda: None) |
| 112 | + |
| 113 | + runner.invoke(cli_app, ["mcp"]) # default transport is stdio |
| 114 | + |
| 115 | + # Command should not have set these vars |
| 116 | + assert env_at_run["FORCE_LOCAL"] is None |
| 117 | + assert env_at_run["EXPLICIT"] is None |
| 118 | + |
| 119 | + def test_mcp_stdio_honors_external_env_override(self, monkeypatch): |
| 120 | + """Stdio transport should pass through externally-set routing env vars.""" |
| 121 | + monkeypatch.setenv("BASIC_MEMORY_FORCE_CLOUD", "true") |
| 122 | + monkeypatch.setenv("BASIC_MEMORY_EXPLICIT_ROUTING", "true") |
110 | 123 |
|
111 | | - # Also mock init_mcp_logging to avoid file operations |
| 124 | + env_at_run = {} |
| 125 | + |
| 126 | + import basic_memory.cli.commands.mcp as mcp_mod |
| 127 | + |
| 128 | + def mock_run(*args, **kwargs): |
| 129 | + env_at_run["FORCE_CLOUD"] = os.environ.get("BASIC_MEMORY_FORCE_CLOUD") |
| 130 | + env_at_run["EXPLICIT"] = os.environ.get("BASIC_MEMORY_EXPLICIT_ROUTING") |
| 131 | + raise SystemExit(0) |
| 132 | + |
| 133 | + monkeypatch.setattr(mcp_mod.mcp_server, "run", mock_run) |
112 | 134 | monkeypatch.setattr(mcp_mod, "init_mcp_logging", lambda: None) |
113 | 135 |
|
114 | 136 | runner.invoke(cli_app, ["mcp"]) |
115 | 137 |
|
116 | | - # Environment variable should have been set to "true" |
117 | | - assert len(env_set_value) == 1 |
118 | | - assert env_set_value[0] == "true" |
| 138 | + # Externally-set vars should be preserved |
| 139 | + assert env_at_run["FORCE_CLOUD"] == "true" |
| 140 | + assert env_at_run["EXPLICIT"] == "true" |
| 141 | + |
| 142 | + def test_mcp_streamable_http_forces_local(self, monkeypatch): |
| 143 | + """Streamable-HTTP transport should force local routing.""" |
| 144 | + env_at_run = {} |
| 145 | + |
| 146 | + import basic_memory.cli.commands.mcp as mcp_mod |
| 147 | + |
| 148 | + def mock_run(*args, **kwargs): |
| 149 | + env_at_run["FORCE_LOCAL"] = os.environ.get("BASIC_MEMORY_FORCE_LOCAL") |
| 150 | + env_at_run["EXPLICIT"] = os.environ.get("BASIC_MEMORY_EXPLICIT_ROUTING") |
| 151 | + raise SystemExit(0) |
| 152 | + |
| 153 | + monkeypatch.setattr(mcp_mod.mcp_server, "run", mock_run) |
| 154 | + monkeypatch.setattr(mcp_mod, "init_mcp_logging", lambda: None) |
| 155 | + |
| 156 | + runner.invoke(cli_app, ["mcp", "--transport", "streamable-http"]) |
| 157 | + |
| 158 | + assert env_at_run["FORCE_LOCAL"] == "true" |
| 159 | + assert env_at_run["EXPLICIT"] == "true" |
| 160 | + |
| 161 | + def test_mcp_sse_forces_local(self, monkeypatch): |
| 162 | + """SSE transport should force local routing.""" |
| 163 | + env_at_run = {} |
| 164 | + |
| 165 | + import basic_memory.cli.commands.mcp as mcp_mod |
| 166 | + |
| 167 | + def mock_run(*args, **kwargs): |
| 168 | + env_at_run["FORCE_LOCAL"] = os.environ.get("BASIC_MEMORY_FORCE_LOCAL") |
| 169 | + env_at_run["EXPLICIT"] = os.environ.get("BASIC_MEMORY_EXPLICIT_ROUTING") |
| 170 | + raise SystemExit(0) |
| 171 | + |
| 172 | + monkeypatch.setattr(mcp_mod.mcp_server, "run", mock_run) |
| 173 | + monkeypatch.setattr(mcp_mod, "init_mcp_logging", lambda: None) |
| 174 | + |
| 175 | + runner.invoke(cli_app, ["mcp", "--transport", "sse"]) |
| 176 | + |
| 177 | + assert env_at_run["FORCE_LOCAL"] == "true" |
| 178 | + assert env_at_run["EXPLICIT"] == "true" |
119 | 179 |
|
120 | 180 |
|
121 | 181 | class TestToolCommandsAcceptFlags: |
|
0 commit comments