cai/tests/commands/test_command_config.py

386 lines
14 KiB
Python

#!/usr/bin/env python3
"""
Tests for ``env_catalog`` (used by ``/env``) and deprecated ``ConfigCommand`` (``/config``).
Catalog mutations use EnvCommand for list/get/set via ``/env set``.
"""
import os
import sys
import pytest
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "src"))
from cai.repl.commands.base import Command
from cai.repl.commands.config import ConfigCommand
from cai.repl.commands.env_catalog import (
ENV_VARS,
get_env_var_value,
handle_env_catalog_default,
handle_env_catalog_get,
handle_env_catalog_list,
handle_env_catalog_set,
set_env_var,
)
from cai.repl.commands.env_catalog_validate import sample_valid_test_value
from cai.repl.commands.env_info_catalog import is_restart_required
from cai.repl.commands.env import EnvCommand
def get_var_number(var_name: str) -> str:
"""Helper to find the ENV_VARS index for a given variable name."""
for num, var_info in ENV_VARS.items():
if var_info["name"] == var_name:
return str(num)
raise ValueError(f"Variable {var_name} not found in ENV_VARS")
class TestConfigCommandDeprecated:
"""/config is a deprecated no-op stub."""
@pytest.fixture
def config_command(self):
return ConfigCommand()
def test_command_initialization(self, config_command):
assert config_command.name == "/config"
assert "Deprecated" in config_command.description
assert config_command.aliases == ["/cfg"]
assert config_command.get_subcommands() == []
def test_handle_always_deprecation(self, config_command):
assert config_command.handle([]) is True
assert config_command.handle(["list"]) is True
# Args ignored; avoid a catalog index that could be mistaken for a live /env row.
assert config_command.handle(["set", "99", "x"]) is True
class TestEnvCatalogHandlers:
"""Catalog list/get/set via module functions (used by /env)."""
@pytest.fixture(autouse=True)
def setup_and_cleanup(self):
os.environ["CAI_TELEMETRY"] = "false"
os.environ["CAI_TRACING"] = "false"
self.original_env_vars = {}
for var_info in ENV_VARS.values():
var_name = var_info["name"]
if var_name in os.environ:
self.original_env_vars[var_name] = os.environ[var_name]
yield
for var_info in ENV_VARS.values():
var_name = var_info["name"]
if var_name in self.original_env_vars:
os.environ[var_name] = self.original_env_vars[var_name]
elif var_name in os.environ:
del os.environ[var_name]
def test_env_vars_structure(self):
assert isinstance(ENV_VARS, dict)
assert len(ENV_VARS) > 0
for num, var_info in ENV_VARS.items():
assert isinstance(num, int)
assert "name" in var_info
assert "description" in var_info
assert "default" in var_info
assert isinstance(var_info["name"], str)
assert isinstance(var_info["description"], str)
assert var_info["default"] is None or isinstance(var_info["default"], str)
def test_get_env_var_value_with_set_value(self):
os.environ["CAI_MODEL"] = "test-model"
result = get_env_var_value("CAI_MODEL")
assert result == "test-model"
def test_get_env_var_value_with_default(self):
if "CAI_MODEL" in os.environ:
del os.environ["CAI_MODEL"]
result = get_env_var_value("CAI_MODEL")
expected_default = None
for var_info in ENV_VARS.values():
if var_info["name"] == "CAI_MODEL":
expected_default = var_info["default"]
break
assert result == (expected_default or "Not set")
def test_get_env_var_value_unknown_variable(self):
result = get_env_var_value("UNKNOWN_VARIABLE")
assert result == "Unknown variable"
def test_set_env_var(self):
result = set_env_var("TEST_VAR", "test_value")
assert result is True
assert os.environ.get("TEST_VAR") == "test_value"
if "TEST_VAR" in os.environ:
del os.environ["TEST_VAR"]
def test_handle_list(self):
assert handle_env_catalog_list([]) is True
def test_handle_get_valid_number(self):
var_num = get_var_number("CAI_MODEL")
assert handle_env_catalog_get([var_num]) is True
def test_handle_get_valid_name(self):
assert handle_env_catalog_get(["CAI_MODEL"]) is True
def test_handle_get_invalid_number(self):
assert handle_env_catalog_get(["999"]) is False
def test_handle_get_unknown_name(self):
assert handle_env_catalog_get(["not_a_number"]) is False
def test_handle_get_no_args(self):
assert handle_env_catalog_get([]) is False
def test_handle_set_valid_number_and_value(self):
var_num = get_var_number("CAI_MODEL")
assert handle_env_catalog_set([var_num, "alias1"]) is True
assert os.environ.get("CAI_MODEL") == "alias1"
def test_handle_set_invalid_number(self):
assert handle_env_catalog_set(["999", "some_value"]) is False
def test_handle_set_unknown_spec(self):
assert handle_env_catalog_set(["not_a_number", "some_value"]) is False
def test_handle_set_no_args(self):
assert handle_env_catalog_set([]) is False
def test_handle_set_insufficient_args(self):
var_num = get_var_number("CAI_MODEL")
assert handle_env_catalog_set([var_num]) is False
def test_handle_set_with_spaces_in_value(self):
var_num = get_var_number("CAI_PATTERN_DESCRIPTION")
assert handle_env_catalog_set([var_num, "hello", "world"]) is True
assert os.environ.get("CAI_PATTERN_DESCRIPTION") == "hello world"
def test_handle_set_empty_value(self):
var_num = get_var_number("CAI_MODEL")
assert handle_env_catalog_set([var_num, ""]) is False
def test_handle_set_ctf_ip_rejects_invalid(self):
ip_num = get_var_number("CTF_IP")
assert handle_env_catalog_set([ip_num, "not-an-ip"]) is False
def test_handle_set_ctf_name_rejects_unknown_when_caibench_has_list(self):
from cai.repl.commands.env_catalog_validate import _load_caibench_ctf_names_lowercase
if not _load_caibench_ctf_names_lowercase():
pytest.skip("CAIBench CTF config not available")
var_num = get_var_number("CTF_NAME")
prev = os.environ.get("CTF_NAME")
try:
assert handle_env_catalog_set([var_num, "___not_a_real_ctf_name_xyz___"]) is False
finally:
if prev is not None:
os.environ["CTF_NAME"] = prev
else:
os.environ.pop("CTF_NAME", None)
def test_handle_env_catalog_default(self):
os.environ["CAI_DEBUG"] = "2"
assert handle_env_catalog_default([]) is True
assert os.environ.get("CAI_DEBUG") == "1"
def test_specific_env_vars_exist(self):
important_vars = [
"CAI_MODEL",
"CAI_DEBUG",
"CAI_BRIEF",
"CAI_MAX_TURNS",
"CAI_TRACING",
"CAI_AGENT_TYPE",
"CTF_NAME",
"CTF_CHALLENGE",
"CAI_MERGE_SUMMARIZE_PER_WORKER",
"ALIAS_API_KEY",
]
defined_var_names = [var_info["name"] for var_info in ENV_VARS.values()]
for var_name in important_vars:
assert var_name in defined_var_names, f"{var_name} should be defined in ENV_VARS"
def test_env_var_defaults_are_reasonable(self):
for var_info in ENV_VARS.values():
var_name = var_info["name"]
default = var_info["default"]
boolean_keywords = [
"debug",
"brief",
"tracing",
"online",
"offline",
"inside",
]
non_boolean_patterns = ["interval", "collection", "model"]
if (
any(keyword in var_name.lower() for keyword in boolean_keywords)
and not any(pattern in var_name.lower() for pattern in non_boolean_patterns)
):
if default is not None:
valid_values = ["true", "false", "0", "1", "2"]
assert (
default.lower() in valid_values
), f"{var_name} should have boolean-like or numeric default"
if any(keyword in var_name.lower() for keyword in ["turns", "limit", "interval"]):
if default is not None:
try:
float(default)
except ValueError:
assert default == "inf", f"{var_name} should have numeric default or 'inf'"
class TestEnvCommandRouting:
"""Integration-style routing on EnvCommand."""
@pytest.fixture(autouse=True)
def setup_and_cleanup(self):
os.environ["CAI_TELEMETRY"] = "false"
os.environ["CAI_TRACING"] = "false"
self.original_env_vars = {}
for var_info in ENV_VARS.values():
var_name = var_info["name"]
if var_name in os.environ:
self.original_env_vars[var_name] = os.environ[var_name]
yield
for var_info in ENV_VARS.values():
var_name = var_info["name"]
if var_name in self.original_env_vars:
os.environ[var_name] = self.original_env_vars[var_name]
elif var_name in os.environ:
del os.environ[var_name]
@pytest.fixture
def env_command(self):
return EnvCommand()
def test_command_base_functionality(self, env_command):
assert isinstance(env_command, Command)
assert env_command.name == "/env"
assert "/e" in env_command.aliases
def test_handle_main_command_routing(self, env_command):
var_num = get_var_number("CAI_MODEL")
assert env_command.handle(["list"]) is True
assert env_command.handle(["get", var_num]) is True
assert env_command.handle(["set", var_num, "alias1"]) is True
assert os.environ.get("CAI_MODEL") == "alias1"
def test_handle_unknown_subcommand(self, env_command):
assert env_command.handle(["unknown_subcommand"]) is False
@pytest.mark.integration
class TestEnvCommandIntegration:
"""Integration tests for /env catalog workflow."""
@pytest.fixture(autouse=True)
def setup_integration(self):
self.original_env_vars = {}
for var_info in ENV_VARS.values():
var_name = var_info["name"]
if var_name in os.environ:
self.original_env_vars[var_name] = os.environ[var_name]
yield
for var_info in ENV_VARS.values():
var_name = var_info["name"]
if var_name in self.original_env_vars:
os.environ[var_name] = self.original_env_vars[var_name]
elif var_name in os.environ:
del os.environ[var_name]
def test_full_env_workflow(self):
cmd = EnvCommand()
var_num = get_var_number("CAI_MODEL")
assert cmd.handle(["list"]) is True
assert cmd.handle(["get", var_num]) is True
assert cmd.handle(["set", var_num, "alias2-mini"]) is True
assert os.environ.get("CAI_MODEL") == "alias2-mini"
assert cmd.handle(["get", var_num]) is True
for var_info in ENV_VARS.values():
if var_info["name"] == "CAI_MODEL" and var_info["default"]:
assert cmd.handle(["set", var_num, str(var_info["default"])]) is True
break
def test_multiple_variable_modifications(self):
cmd = EnvCommand()
modifications = [
(get_var_number("CAI_MODEL"), "alias1", "CAI_MODEL"),
(get_var_number("CAI_DEBUG"), "2", "CAI_DEBUG"),
(get_var_number("CAI_BRIEF"), "true", "CAI_BRIEF"),
]
for var_num, value, var_name in modifications:
assert cmd.handle(["set", var_num, value]) is True
assert cmd.handle(["get", var_num]) is True
for _, expected_value, var_name in modifications:
assert os.environ.get(var_name) == expected_value
def test_edge_case_values(self):
cmd = EnvCommand()
model_num = get_var_number("CAI_MODEL")
debug_num = get_var_number("CAI_DEBUG")
pat_num = get_var_number("CAI_PATTERN_DESCRIPTION")
assert cmd.handle(["set", pat_num, "x", "y", "z"]) is True
assert os.environ.get("CAI_PATTERN_DESCRIPTION") == "x y z"
assert cmd.handle(["set", model_num, "alias1"]) is True
assert cmd.handle(["set", model_num, "not-a-real-model-xyz-999"]) is False
assert cmd.handle(["set", debug_num, "0"]) is True
assert os.environ.get("CAI_DEBUG") == "0"
assert cmd.handle(["set", debug_num, "999"]) is False
def test_all_env_vars_can_be_set_and_retrieved(self):
cmd = EnvCommand()
for var_num, var_info in ENV_VARS.items():
var_name = str(var_info["name"])
test_value = sample_valid_test_value(var_name, var_info)
if is_restart_required(var_name):
assert cmd.handle(["set", str(var_num), test_value]) is False, (
f"/env set should refuse locked-at-startup {var_name}"
)
assert cmd.handle(["get", str(var_num)]) is True, f"Failed to get {var_name}"
continue
assert cmd.handle(["set", str(var_num), test_value]) is True, f"Failed to set {var_name}"
assert cmd.handle(["get", str(var_num)]) is True, f"Failed to get {var_name}"
assert os.environ.get(var_name) == test_value
if __name__ == "__main__":
pytest.main([__file__, "-v"])