- claude_settings: 目标文件改为全局 ~/.claude/settings.json,与 cc-switch 行为一致 - claude_settings: 切回 claude 时保留用户已有的 ANTHROPIC_API_KEY - backend: 修正 save_project_settings -> save_settings,提示文本同步 - test_backends: 清理未用 import - test_entrypoints: 新增 backend 命令注册测试 - 新增 test_claude_settings / test_secrets / test_backend_command(共 36 tests)
104 lines
4.1 KiB
Python
104 lines
4.1 KiB
Python
"""Tests for myagents.secrets."""
|
|
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from myagents import secrets as secrets_mod
|
|
|
|
|
|
@pytest.fixture()
|
|
def fake_dirs(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> dict[str, Path]:
|
|
xiaohe_dir = tmp_path / ".xiaohe" / "agent"
|
|
mytoolkit_dir = tmp_path / ".mytoolkit"
|
|
monkeypatch.setattr(secrets_mod, "XIAOHE_CONFIG_DIR", xiaohe_dir)
|
|
monkeypatch.setattr(secrets_mod, "XIAOHE_CONFIG_PATH", xiaohe_dir / "config.json")
|
|
monkeypatch.setattr(secrets_mod, "MYTOOLKIT_CONFIG_PATH", mytoolkit_dir / "config.json")
|
|
return {"xiaohe": xiaohe_dir, "mytoolkit": mytoolkit_dir}
|
|
|
|
|
|
class TestGetKey:
|
|
def test_returns_xiaohe_key_first(self, fake_dirs: dict[str, Path]) -> None:
|
|
xiaohe_path = fake_dirs["xiaohe"] / "config.json"
|
|
mytoolkit_path = fake_dirs["mytoolkit"] / "config.json"
|
|
xiaohe_path.parent.mkdir(parents=True)
|
|
xiaohe_path.write_text(json.dumps({"keys": {"kimi": "xiaohe-key"}}))
|
|
mytoolkit_path.parent.mkdir(parents=True)
|
|
mytoolkit_path.write_text(json.dumps({"keys": {"kimi": "mtk-key"}}))
|
|
|
|
assert secrets_mod.get_key("kimi") == "xiaohe-key"
|
|
|
|
def test_falls_back_to_mytoolkit_key(self, fake_dirs: dict[str, Path]) -> None:
|
|
mytoolkit_path = fake_dirs["mytoolkit"] / "config.json"
|
|
mytoolkit_path.parent.mkdir(parents=True)
|
|
mytoolkit_path.write_text(json.dumps({"keys": {"kimi": "mtk-key"}}))
|
|
|
|
assert secrets_mod.get_key("kimi") == "mtk-key"
|
|
|
|
def test_returns_none_when_missing(self, fake_dirs: dict[str, Path]) -> None:
|
|
assert secrets_mod.get_key("missing") is None
|
|
|
|
def test_skips_empty_strings(self, fake_dirs: dict[str, Path]) -> None:
|
|
xiaohe_path = fake_dirs["xiaohe"] / "config.json"
|
|
xiaohe_path.parent.mkdir(parents=True)
|
|
xiaohe_path.write_text(json.dumps({"keys": {"kimi": ""}}))
|
|
|
|
assert secrets_mod.get_key("kimi") is None
|
|
|
|
|
|
class TestSetKey:
|
|
def test_stores_key_in_xiaohe_config(self, fake_dirs: dict[str, Path]) -> None:
|
|
secrets_mod.set_key("deepseek", "sk-test")
|
|
|
|
data = json.loads((fake_dirs["xiaohe"] / "config.json").read_text())
|
|
assert data["keys"]["deepseek"] == "sk-test"
|
|
|
|
def test_rejects_empty_key(self, fake_dirs: dict[str, Path]) -> None:
|
|
with pytest.raises(ValueError, match="cannot be empty"):
|
|
secrets_mod.set_key("deepseek", "")
|
|
|
|
def test_preserves_other_keys(self, fake_dirs: dict[str, Path]) -> None:
|
|
xiaohe_path = fake_dirs["xiaohe"] / "config.json"
|
|
xiaohe_path.parent.mkdir(parents=True)
|
|
xiaohe_path.write_text(json.dumps({"keys": {"other": "keep"}}))
|
|
|
|
secrets_mod.set_key("deepseek", "sk-test")
|
|
|
|
data = json.loads(xiaohe_path.read_text())
|
|
assert data["keys"]["other"] == "keep"
|
|
assert data["keys"]["deepseek"] == "sk-test"
|
|
|
|
|
|
class TestRemoveKey:
|
|
def test_removes_existing_key(self, fake_dirs: dict[str, Path]) -> None:
|
|
xiaohe_path = fake_dirs["xiaohe"] / "config.json"
|
|
xiaohe_path.parent.mkdir(parents=True)
|
|
xiaohe_path.write_text(json.dumps({"keys": {"deepseek": "sk-test"}}))
|
|
|
|
assert secrets_mod.remove_key("deepseek") is True
|
|
data = json.loads(xiaohe_path.read_text())
|
|
assert "deepseek" not in data.get("keys", {})
|
|
|
|
def test_returns_false_when_missing(self, fake_dirs: dict[str, Path]) -> None:
|
|
assert secrets_mod.remove_key("deepseek") is False
|
|
|
|
|
|
class TestPermissions:
|
|
@pytest.mark.skipif(sys.platform == "win32", reason="Unix permissions only")
|
|
def test_config_file_is_user_readable_only(self, fake_dirs: dict[str, Path]) -> None:
|
|
secrets_mod.set_key("kimi", "sk-test")
|
|
path = fake_dirs["xiaohe"] / "config.json"
|
|
mode = path.stat().st_mode
|
|
assert mode & 0o777 == 0o600
|
|
|
|
|
|
class TestHasKey:
|
|
def test_true_when_key_exists(self, fake_dirs: dict[str, Path]) -> None:
|
|
secrets_mod.set_key("kimi", "sk-test")
|
|
assert secrets_mod.has_key("kimi") is True
|
|
|
|
def test_false_when_missing(self, fake_dirs: dict[str, Path]) -> None:
|
|
assert secrets_mod.has_key("missing") is False
|