fix: scope MCP configuration to Codex and stabilize Windows launchers
This commit is contained in:
1 parent
18d17da966
commit
c96935b47b
13 files changed
+290
-1125
No files matched your search
@@ -120,33 +120,7 @@ command = "leave-alone"
|
||||
assert path.with_name(path.name + ".artemis-update.bak").read_text(encoding="utf-8") == original
|
||||
|
||||
|
||||
def test_json_migrates_existing_standard_mcp_to_current_checkout(tmp_path):
|
||||
path = tmp_path / "mcp.json"
|
||||
root = tmp_path / "project"
|
||||
config = {
|
||||
"cwd": str(root),
|
||||
"command": "old",
|
||||
"args": ["-m", "mcp_server"],
|
||||
"disabled": True,
|
||||
"env": {"CUSTOM": "preserved"},
|
||||
"autoApprove": [],
|
||||
}
|
||||
path.write_text(json.dumps({"mcpServers": {"artemis": config, "other": {"command": "keep"}}}))
|
||||
moved_root = tmp_path / "new release"
|
||||
assert update.refresh_json(path, ("mcpServers",), moved_root, "new", ("mobile_batch",))
|
||||
result = json.loads(path.read_text())["mcpServers"]
|
||||
assert result["artemis"]["env"]["CUSTOM"] == "preserved"
|
||||
assert result["artemis"]["env"]["PYTHONPATH"] == str(moved_root)
|
||||
assert result["artemis"]["cwd"] == str(moved_root)
|
||||
assert result["artemis"]["disabled"] is True
|
||||
assert result["other"] == {"command": "keep"}
|
||||
assert not update.refresh_json(path, ("mcpServers",), moved_root, "new", ("mobile_batch",))
|
||||
missing = tmp_path / "not-installed.json"
|
||||
assert not update.refresh_json(missing, ("mcpServers",), root, "new", ())
|
||||
assert not missing.exists()
|
||||
|
||||
|
||||
def test_mcp_sync_installs_detected_clients_and_migrates_codex(tmp_path, monkeypatch):
|
||||
def test_mcp_sync_updates_only_codex_and_preserves_other_clients(tmp_path, monkeypatch):
|
||||
from artemis.interfaces.cli.commands import mcp as mcp_command
|
||||
|
||||
home = tmp_path / "home"
|
||||
@@ -170,8 +144,11 @@ def test_mcp_sync_installs_detected_clients_and_migrates_codex(tmp_path, monkeyp
|
||||
cursor_home = home / ".cursor"
|
||||
cursor_home.mkdir()
|
||||
(cursor_home / "mcp.json").write_text('{"mcpServers":{"other":{"command":"keep"}}}')
|
||||
vscode_home = home / "AppData/Code/User"
|
||||
vscode_home.mkdir(parents=True)
|
||||
vscode_config = vscode_home / "mcp.json"
|
||||
vscode_config.write_bytes(b"{invalid VS Code JSONC}")
|
||||
monkeypatch.setattr("pathlib.Path.home", lambda: home)
|
||||
monkeypatch.setattr(mcp_command, "_get_vscode_user_dir", lambda: home / "vscode-user")
|
||||
monkeypatch.setenv("CODEX_HOME", str(codex_home))
|
||||
monkeypatch.setenv("APPDATA", str(home / "AppData"))
|
||||
root = tmp_path / "new release"
|
||||
@@ -187,26 +164,31 @@ def test_mcp_sync_installs_detected_clients_and_migrates_codex(tmp_path, monkeyp
|
||||
assert entry["env"]["PYTHONPATH"] == str(root)
|
||||
assert entry["enabled_tools"] == list(mcp_command.ARTEMIS_MCP_TOOLS)
|
||||
cursor = json.loads((cursor_home / "mcp.json").read_text(encoding="utf-8"))
|
||||
assert cursor["mcpServers"]["artemis"]["env"]["PYTHONPATH"] == str(root)
|
||||
assert "artemis" not in cursor["mcpServers"]
|
||||
assert cursor["mcpServers"]["other"] == {"command": "keep"}
|
||||
assert vscode_config.read_bytes() == b"{invalid VS Code JSONC}"
|
||||
assert not vscode_config.with_name("mcp.json.artemis-update.bak").exists()
|
||||
first = codex_config.read_text(encoding="utf-8")
|
||||
assert update.refresh_mcp(root) == 0
|
||||
assert codex_config.read_text(encoding="utf-8") == first
|
||||
|
||||
|
||||
def test_mcp_sync_fails_on_invalid_detected_config_without_overwriting(tmp_path, monkeypatch):
|
||||
from artemis.interfaces.cli.commands import mcp as mcp_command
|
||||
|
||||
def test_mcp_sync_fails_on_invalid_codex_config_without_overwriting(tmp_path, monkeypatch, capsys):
|
||||
home = tmp_path / "home"
|
||||
cursor_home = home / ".cursor"
|
||||
cursor_home.mkdir(parents=True)
|
||||
config = cursor_home / "mcp.json"
|
||||
config.write_text("{invalid json", encoding="utf-8")
|
||||
codex_home = home / ".codex"
|
||||
codex_home.mkdir(parents=True)
|
||||
config = codex_home / "config.toml"
|
||||
original = b'model = "keep-secret"\n[mcp_servers.artemis\n'
|
||||
config.write_bytes(original)
|
||||
monkeypatch.setattr("pathlib.Path.home", lambda: home)
|
||||
monkeypatch.setattr(mcp_command, "_get_vscode_user_dir", lambda: home / "vscode-user")
|
||||
monkeypatch.delenv("CODEX_HOME", raising=False)
|
||||
monkeypatch.setenv("CODEX_HOME", str(codex_home))
|
||||
assert update.refresh_mcp(tmp_path / "project") == 1
|
||||
assert config.read_text(encoding="utf-8") == "{invalid json"
|
||||
assert config.read_bytes() == original
|
||||
assert not config.with_name(config.name + ".artemis-update.bak").exists()
|
||||
output = capsys.readouterr().out
|
||||
assert str(config) in output
|
||||
assert "Invalid TOML" in output
|
||||
assert "keep-secret" not in output
|
||||
|
||||
|
||||
def test_mcp_sync_installs_codex_when_client_directory_exists(tmp_path, monkeypatch):
|
||||
@@ -216,7 +198,6 @@ def test_mcp_sync_installs_codex_when_client_directory_exists(tmp_path, monkeypa
|
||||
codex_home = home / ".codex"
|
||||
codex_home.mkdir(parents=True)
|
||||
monkeypatch.setattr("pathlib.Path.home", lambda: home)
|
||||
monkeypatch.setattr(mcp_command, "_get_vscode_user_dir", lambda: home / "vscode-user")
|
||||
monkeypatch.setenv("CODEX_HOME", str(codex_home))
|
||||
root = tmp_path / "project"
|
||||
assert update.refresh_mcp(root) == 0
|
||||
@@ -225,6 +206,59 @@ def test_mcp_sync_installs_codex_when_client_directory_exists(tmp_path, monkeypa
|
||||
assert config["mcp_servers"]["artemis"]["enabled_tools"] == list(mcp_command.ARTEMIS_MCP_TOOLS)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("codex_home_setting", [None, "", "custom"])
|
||||
def test_codex_sync_reports_actual_path_and_ready_state(
|
||||
tmp_path, monkeypatch, capsys, codex_home_setting
|
||||
):
|
||||
home = tmp_path / "home"
|
||||
codex_home = home / ".codex" if codex_home_setting != "custom" else tmp_path / "custom Codex"
|
||||
codex_home.mkdir(parents=True)
|
||||
monkeypatch.setattr("pathlib.Path.home", lambda: home)
|
||||
monkeypatch.setenv("ARTEMIS_LAUNCH_LANG", "zh-CN")
|
||||
if codex_home_setting is None:
|
||||
monkeypatch.delenv("CODEX_HOME", raising=False)
|
||||
else:
|
||||
monkeypatch.setenv("CODEX_HOME", str(codex_home) if codex_home_setting == "custom" else "")
|
||||
root = tmp_path / "project"
|
||||
assert update.refresh_mcp(root) == 0
|
||||
first = capsys.readouterr().out
|
||||
assert str(codex_home / "config.toml") in first
|
||||
assert "已安装" in first
|
||||
assert update.refresh_mcp(root) == 0
|
||||
second = capsys.readouterr().out
|
||||
assert str(codex_home / "config.toml") in second
|
||||
assert "已是最新" in second
|
||||
if codex_home_setting == "custom":
|
||||
assert not (home / ".codex").exists()
|
||||
|
||||
|
||||
def test_codex_sync_reports_missing_directory_without_touching_other_clients(
|
||||
tmp_path, monkeypatch, capsys
|
||||
):
|
||||
monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path)
|
||||
monkeypatch.delenv("CODEX_HOME", raising=False)
|
||||
monkeypatch.setenv("ARTEMIS_LAUNCH_LANG", "zh-CN")
|
||||
assert update.refresh_mcp(tmp_path / "project") == 0
|
||||
output = capsys.readouterr().out
|
||||
assert str(tmp_path / ".codex/config.toml") in output
|
||||
assert "未发现 Codex 配置目录" in output
|
||||
assert "--install codex" in output
|
||||
assert not (tmp_path / ".codex").exists()
|
||||
|
||||
|
||||
def test_custom_codex_command_is_preserved_and_reported(tmp_path, monkeypatch, capsys):
|
||||
codex_home = tmp_path / ".codex"
|
||||
codex_home.mkdir()
|
||||
config = codex_home / "config.toml"
|
||||
original = '[mcp_servers.artemis]\ncommand = "custom"\nargs = ["wrapper"]\n'
|
||||
config.write_text(original, encoding="utf-8")
|
||||
monkeypatch.setenv("CODEX_HOME", str(codex_home))
|
||||
monkeypatch.setenv("ARTEMIS_LAUNCH_LANG", "zh-CN")
|
||||
assert update.refresh_mcp(tmp_path / "project") == 0
|
||||
assert config.read_text(encoding="utf-8") == original
|
||||
assert "自定义" in capsys.readouterr().out
|
||||
|
||||
|
||||
def test_codex_install_preserves_existing_user_tables(tmp_path):
|
||||
path = tmp_path / "config.toml"
|
||||
path.write_text(
|
||||
@@ -277,25 +311,6 @@ def test_codex_inline_environment_sync_is_idempotent(tmp_path):
|
||||
)
|
||||
|
||||
|
||||
def test_custom_mcp_wrapper_is_left_unchanged(tmp_path):
|
||||
path = tmp_path / "mcp.json"
|
||||
original = '{"mcpServers":{"artemis":{"command":"uv","args":["run","custom"]}}}'
|
||||
path.write_text(original, encoding="utf-8")
|
||||
assert (
|
||||
update.refresh_json(
|
||||
path,
|
||||
("mcpServers",),
|
||||
tmp_path,
|
||||
"new-python",
|
||||
("mobile_run_task",),
|
||||
install_missing=True,
|
||||
template={"command": "new-python", "args": ["-m", "mcp_server"], "env": {}},
|
||||
)
|
||||
is None
|
||||
)
|
||||
assert path.read_text(encoding="utf-8") == original
|
||||
|
||||
|
||||
def test_frontend_fingerprint_detects_lock_source_and_deleted_assets(tmp_path):
|
||||
ui = tmp_path / "apps/showcase_ui"
|
||||
(ui / "src").mkdir(parents=True)
|
||||
|
||||
Reference in new issue
Block a user