from __future__ import annotations
import subprocess
from pathlib import Path
from unittest.mock import Mock
import pytest
from providers.config import ProviderConfig
from providers.unified_config import OgMemConfig
def _write_yaml(tmp_path: Path, body: str) -> str:
path = tmp_path / "ogmem.yaml"
path.write_text(body, encoding="utf-8")
return str(path)
def _allow_secret_helpers(monkeypatch: pytest.MonkeyPatch, *paths: str) -> None:
monkeypatch.setattr("providers.unified_config.ALLOWED_SECRET_HELPERS", tuple(paths))
def test_load_prefers_yaml_api_key_over_env_and_command(monkeypatch: pytest.MonkeyPatch, tmp_path: Path):
monkeypatch.setenv("OGMEM_API_KEY", "env-key")
monkeypatch.setenv("OGMEM_API_KEY_CMD", '["/abs/tool","ignored"]')
cfg = OgMemConfig.load(
_write_yaml(
tmp_path,
"""
llm:
api_key: yaml-key
""",
)
)
assert cfg.openai_api_key == "yaml-key"
assert cfg.effective_openai_api_key() == "yaml-key"
def test_load_uses_env_api_key_when_yaml_missing(monkeypatch: pytest.MonkeyPatch, tmp_path: Path):
monkeypatch.setenv("OGMEM_API_KEY", "env-key")
cfg = OgMemConfig.load(_write_yaml(tmp_path, "llm: {}\n"))
assert cfg.openai_api_key == "env-key"
assert cfg.effective_openai_api_key() == "env-key"
def test_load_does_not_execute_command_eagerly(monkeypatch: pytest.MonkeyPatch, tmp_path: Path):
_allow_secret_helpers(monkeypatch, "/abs/tool")
run_mock = Mock(return_value=subprocess.CompletedProcess(args=["/abs/tool"], returncode=0, stdout="cmd-key\n", stderr=""))
monkeypatch.setattr("providers.unified_config.subprocess.run", run_mock)
cfg = OgMemConfig.load(
_write_yaml(
tmp_path,
"""
llm:
api_key_command:
command: ["/abs/tool", "--read-key"]
""",
)
)
assert cfg.openai_api_key is None
assert cfg.openai_api_key_command is not None
run_mock.assert_not_called()
def test_effective_openai_api_key_executes_command_lazily(monkeypatch: pytest.MonkeyPatch, tmp_path: Path):
_allow_secret_helpers(monkeypatch, "/abs/tool")
run_mock = Mock(return_value=subprocess.CompletedProcess(args=["/abs/tool"], returncode=0, stdout="cmd-key\n", stderr=""))
monkeypatch.setattr("providers.unified_config.subprocess.run", run_mock)
cfg = OgMemConfig.load(
_write_yaml(
tmp_path,
"""
llm:
api_key_command:
command: ["/abs/tool", "--read-key"]
""",
)
)
assert cfg.effective_openai_api_key() == "cmd-key"
run_mock.assert_called_once()
_, kwargs = run_mock.call_args
assert kwargs["shell"] is False
def test_provider_config_does_not_execute_command_until_resolution(monkeypatch: pytest.MonkeyPatch):
_allow_secret_helpers(monkeypatch, "/abs/tool")
run_mock = Mock(return_value=subprocess.CompletedProcess(args=["/abs/tool"], returncode=0, stdout="cmd-key\n", stderr=""))
monkeypatch.setattr("providers.unified_config.subprocess.run", run_mock)
from providers.unified_config import SecretCommandSpec
cfg = ProviderConfig(
provider="openai",
openai_api_key_command=SecretCommandSpec(argv=("/abs/tool",)),
)
run_mock.assert_not_called()
assert cfg.effective_openai_api_key() == "cmd-key"
run_mock.assert_called_once()
assert cfg.effective_openai_api_key() == "cmd-key"
run_mock.assert_called_once()
def test_provider_config_uses_command_for_embedder(monkeypatch: pytest.MonkeyPatch):
_allow_secret_helpers(monkeypatch, "/abs/tool")
run_mock = Mock(return_value=subprocess.CompletedProcess(args=["/abs/tool"], returncode=0, stdout="emb-key\n", stderr=""))
monkeypatch.setattr("providers.unified_config.subprocess.run", run_mock)
from providers.unified_config import SecretCommandSpec
cfg = ProviderConfig(
provider="openai",
openai_api_key_command=SecretCommandSpec(argv=("/abs/tool",)),
openai_embedding_api_key_command=SecretCommandSpec(argv=("/abs/tool", "--emb")),
)
assert cfg.effective_embedding_api_key() == "emb-key"
run_mock.assert_called_once()
assert cfg.effective_embedding_api_key() == "emb-key"
run_mock.assert_called_once()
def test_embedding_api_key_falls_back_to_llm_command(monkeypatch: pytest.MonkeyPatch):
_allow_secret_helpers(monkeypatch, "/abs/tool")
run_mock = Mock(return_value=subprocess.CompletedProcess(args=["/abs/tool"], returncode=0, stdout="llm-key\n", stderr=""))
monkeypatch.setattr("providers.unified_config.subprocess.run", run_mock)
from providers.unified_config import SecretCommandSpec
cfg = ProviderConfig(
provider="openai",
openai_api_key_command=SecretCommandSpec(argv=("/abs/tool",)),
)
assert cfg.effective_embedding_api_key() == "llm-key"
def test_load_rejects_non_absolute_command_path(tmp_path: Path):
with pytest.raises(ValueError, match="absolute executable path"):
OgMemConfig.load(
_write_yaml(
tmp_path,
"""
llm:
api_key_command: ["python", "-c", "print('bad')"]
""",
)
)
def test_load_rejects_command_when_helper_is_not_allowlisted(tmp_path: Path):
with pytest.raises(ValueError, match="helper allowlist"):
OgMemConfig.load(
_write_yaml(
tmp_path,
"""
llm:
api_key_command: ["/abs/tool", "read"]
""",
)
)
def test_load_accepts_allowlisted_unc_command_path(monkeypatch: pytest.MonkeyPatch, tmp_path: Path):
_allow_secret_helpers(monkeypatch, "\\\\server\\share\\tool.exe")
cfg = OgMemConfig.load(
_write_yaml(
tmp_path,
"""
llm:
api_key_command: ['\\\\server\\share\\tool.exe', 'read']
""",
)
)
assert cfg.openai_api_key_command is not None
assert cfg.openai_api_key_command.argv == ("\\\\server\\share\\tool.exe", "read")
def test_command_failure_raises_only_on_resolution(monkeypatch: pytest.MonkeyPatch, tmp_path: Path):
_allow_secret_helpers(monkeypatch, "/abs/tool")
monkeypatch.setattr(
"providers.unified_config.subprocess.run",
Mock(return_value=subprocess.CompletedProcess(args=["/abs/tool"], returncode=3, stdout="", stderr="boom")),
)
cfg = OgMemConfig.load(
_write_yaml(
tmp_path,
"""
llm:
api_key_command: ["/abs/tool"]
""",
)
)
with pytest.raises(ValueError, match="llm.api_key command failed"):
cfg.effective_openai_api_key()
def test_load_rejects_invalid_timeout_range(tmp_path: Path):
with pytest.MonkeyPatch.context() as monkeypatch:
_allow_secret_helpers(monkeypatch, "/abs/tool")
with pytest.raises(ValueError, match="timeout_ms must be between 100 and 60000"):
OgMemConfig.load(
_write_yaml(
tmp_path,
"""
llm:
api_key_command:
command: ["/abs/tool"]
timeout_ms: 0
""",
)
)
def test_load_rejects_invalid_max_output_range(tmp_path: Path):
with pytest.MonkeyPatch.context() as monkeypatch:
_allow_secret_helpers(monkeypatch, "/abs/tool")
with pytest.raises(ValueError, match="max_output_bytes must be between 1 and 1048576"):
OgMemConfig.load(
_write_yaml(
tmp_path,
"""
llm:
api_key_command:
command: ["/abs/tool"]
max_output_bytes: 2000000
""",
)
)