import json from laya_router.clients.base import read_json from laya_router.clients.claude import ClaudeClient from laya_router.clients.codex import CodexClient, hooks_feature_enabled from laya_router.config import Settings def _settings(tmp_path) -> Settings: return Settings(home=tmp_path) def test_claude_merge_is_idempotent_and_keeps_existing_hooks(tmp_path): path = tmp_path / "settings.json" path.write_text( json.dumps( { "env": {"ANTHROPIC_AUTH_TOKEN": "secret-token"}, "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [{"type": "command", "command": "rtk hook claude"}], } ] }, } ), encoding="utf-8", ) client = ClaudeClient(path) settings = _settings(tmp_path / "runtime") assert client.install(settings) is True assert client.install(settings) is False saved = read_json(path) assert saved["env"]["ANTHROPIC_AUTH_TOKEN"] == "secret-token" assert saved["hooks"]["PreToolUse"][0]["matcher"] == "Bash" commands = [ item["command"] for group in saved["hooks"]["UserPromptSubmit"] for item in group["hooks"] ] assert commands == [f'"{tmp_path / "runtime" / ".venv" / "bin" / "laya-hook"}" --client claude'] assert list(path.parent.glob("settings.json.bak-*")) assert client.uninstall() is True removed = read_json(path) assert "UserPromptSubmit" not in removed["hooks"] assert removed["hooks"]["PreToolUse"][0]["hooks"][0]["command"] == "rtk hook claude" assert removed["env"]["ANTHROPIC_AUTH_TOKEN"] == "secret-token" def test_codex_uninstall_drops_file_when_it_only_contains_our_hook(tmp_path): codex_dir = tmp_path / ".codex" client = CodexClient(codex_dir) settings = _settings(tmp_path / "runtime") assert client.install(settings) is True assert client.install(settings) is False saved = read_json(client.hooks_path) assert saved["hooks"]["UserPromptSubmit"][0]["hooks"][0]["statusMessage"] == "Laya routing" assert client.uninstall() is True assert not client.hooks_path.exists() def test_codex_uninstall_keeps_foreign_hooks(tmp_path): codex_dir = tmp_path / ".codex" hooks = codex_dir / "hooks.json" hooks.parent.mkdir() hooks.write_text( json.dumps( { "hooks": { "UserPromptSubmit": [ {"hooks": [{"type": "command", "command": "other-hook"}]} ] } } ), encoding="utf-8", ) client = CodexClient(codex_dir) client.install(_settings(tmp_path / "runtime")) client.uninstall() saved = read_json(hooks) commands = [ item["command"] for group in saved["hooks"]["UserPromptSubmit"] for item in group["hooks"] ] assert commands == ["other-hook"] def test_hooks_feature_parser(): listing = "hooks stable true\nplugin_hooks removed false\n" assert hooks_feature_enabled(listing) is True assert hooks_feature_enabled("hooks experimental false") is False assert hooks_feature_enabled("no hooks here") is None