"""配置:0600 权限、环境变量覆盖优先级、密钥不落日志。""" import stat import pytest from quercus_core.config import ( ENV_ANTHROPIC_API_KEY, ENV_OPENAI_API_KEY, ENV_OPENAI_BASE_URL, Config, config_home, config_summary, default_config_path, default_sessions_dir, load_config, save_config, ) class TestSaveLoad: def test_save_creates_0600(self, tmp_path): path = tmp_path / "cfg" / "config.toml" save_config(Config(anthropic_api_key="sk-ant-secret"), path) assert path.exists() assert stat.S_IMODE(path.stat().st_mode) == 0o600 def test_roundtrip(self, tmp_path): path = tmp_path / "config.toml" save_config( Config( anthropic_api_key="sk-a", openai_api_key="sk-o", openai_base_url="https://gw.example/v1", ), path, ) cfg = load_config(path) assert cfg.anthropic_api_key == "sk-a" assert cfg.openai_api_key == "sk-o" assert cfg.openai_base_url == "https://gw.example/v1" assert cfg.has_anthropic_key and cfg.has_openai_key def test_env_overrides_file(self, tmp_path, monkeypatch): path = tmp_path / "config.toml" save_config(Config(anthropic_api_key="file-key", openai_api_key="file-o"), path) monkeypatch.setenv(ENV_ANTHROPIC_API_KEY, "env-key") cfg = load_config(path) assert cfg.anthropic_api_key == "env-key" # 环境变量 > 配置文件 assert cfg.openai_api_key == "file-o" # 未覆盖的保持文件值 def test_env_without_file(self, tmp_path, monkeypatch): monkeypatch.setenv(ENV_OPENAI_API_KEY, "env-o") monkeypatch.setenv(ENV_OPENAI_BASE_URL, "http://localhost:8080/v1") monkeypatch.delenv(ENV_ANTHROPIC_API_KEY, raising=False) cfg = load_config(tmp_path / "missing.toml") assert cfg.openai_api_key == "env-o" assert cfg.openai_base_url == "http://localhost:8080/v1" assert cfg.anthropic_api_key is None def test_no_config_no_env(self, tmp_path, monkeypatch): monkeypatch.delenv(ENV_ANTHROPIC_API_KEY, raising=False) monkeypatch.delenv(ENV_OPENAI_API_KEY, raising=False) monkeypatch.delenv(ENV_OPENAI_BASE_URL, raising=False) cfg = load_config(tmp_path / "missing.toml") assert cfg.anthropic_api_key is None assert cfg.openai_api_key is None assert cfg.openai_base_url is None assert not cfg.has_anthropic_key and not cfg.has_openai_key class TestHome: def test_home_override(self, tmp_home): assert config_home() == tmp_home / ".quercus" assert default_config_path() == tmp_home / ".quercus" / "config.toml" assert default_sessions_dir() == tmp_home / ".quercus" / "sessions" def test_config_summary_hides_secrets(): cfg = Config(anthropic_api_key="top-secret-value", openai_api_key="second-secret") summary = config_summary(cfg) assert summary["anthropic_configured"] is True assert summary["openai_configured"] is True assert "secret" not in str(summary)