cai/tests/commands/test_command_config.py

381 lines
15 KiB
Python

#!/usr/bin/env python3
"""
Test suite for the config command functionality.
Tests all handle methods and input possibilities for the config command.
"""
import os
import sys
import pytest
from unittest.mock import patch, Mock, MagicMock
# Add src to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__),
'..', '..', 'src'))
from cai.repl.commands.config import (
ConfigCommand, ENV_VARS, get_env_var_value, set_env_var
)
from cai.repl.commands.base import Command
class TestConfigCommand:
"""Test cases for ConfigCommand."""
@pytest.fixture(autouse=True)
def setup_and_cleanup(self):
"""Setup and cleanup for each test."""
# Set up test environment
os.environ['CAI_TELEMETRY'] = 'false'
os.environ['CAI_TRACING'] = 'false'
# Store original values of environment variables we'll modify
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
# Restore original environment variables
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 config_command(self):
"""Create a ConfigCommand instance for testing."""
return ConfigCommand()
def test_command_initialization(self, config_command):
"""Test that ConfigCommand initializes correctly."""
assert config_command.name == "/config"
assert config_command.description == "Display and configure environment variables"
assert config_command.aliases == ["/cfg"]
# Check subcommands are registered
expected_subcommands = ["list", "set", "get"]
assert set(config_command.get_subcommands()) == set(expected_subcommands)
def test_env_vars_structure(self):
"""Test that ENV_VARS has the expected structure."""
assert isinstance(ENV_VARS, dict)
assert len(ENV_VARS) > 0
# Check each env var has required fields
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
# Check name is a string
assert isinstance(var_info["name"], str)
assert isinstance(var_info["description"], str)
# default can be None or string
assert var_info["default"] is None or isinstance(var_info["default"], str)
def test_get_env_var_value_with_set_value(self):
"""Test getting environment variable value when it's set."""
# Set a test value
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):
"""Test getting environment variable value when it's not set (returns default)."""
# Make sure the variable is not set
if "CAI_MODEL" in os.environ:
del os.environ["CAI_MODEL"]
result = get_env_var_value("CAI_MODEL")
# Should return the default value from ENV_VARS
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):
"""Test getting value for unknown environment variable."""
result = get_env_var_value("UNKNOWN_VARIABLE")
assert result == "Unknown variable"
def test_set_env_var(self):
"""Test setting an environment variable."""
result = set_env_var("TEST_VAR", "test_value")
assert result is True
assert os.environ.get("TEST_VAR") == "test_value"
# Cleanup
if "TEST_VAR" in os.environ:
del os.environ["TEST_VAR"]
def test_handle_list(self, config_command):
"""Test listing all environment variables."""
result = config_command.handle_list([])
assert result is True
def test_handle_no_args(self, config_command):
"""Test handling when no arguments provided (should default to list)."""
result = config_command.handle_no_args()
assert result is True
def test_handle_get_valid_number(self, config_command):
"""Test getting a variable by valid number."""
# Test with variable number 6 (CAI_MODEL)
result = config_command.handle_get(["6"])
assert result is True
def test_handle_get_invalid_number(self, config_command):
"""Test getting a variable by invalid number."""
result = config_command.handle_get(["999"])
assert result is False
def test_handle_get_non_integer(self, config_command):
"""Test getting a variable with non-integer number."""
result = config_command.handle_get(["not_a_number"])
assert result is False
def test_handle_get_no_args(self, config_command):
"""Test get command with no arguments."""
result = config_command.handle_get([])
assert result is False
def test_handle_set_valid_number_and_value(self, config_command):
"""Test setting a variable by valid number and value."""
# Test with variable number 6 (CAI_MODEL)
result = config_command.handle_set(["6", "new-test-model"])
assert result is True
assert os.environ.get("CAI_MODEL") == "new-test-model"
def test_handle_set_invalid_number(self, config_command):
"""Test setting a variable by invalid number."""
result = config_command.handle_set(["999", "some_value"])
assert result is False
def test_handle_set_non_integer(self, config_command):
"""Test setting a variable with non-integer number."""
result = config_command.handle_set(["not_a_number", "some_value"])
assert result is False
def test_handle_set_no_args(self, config_command):
"""Test set command with no arguments."""
result = config_command.handle_set([])
assert result is False
def test_handle_set_insufficient_args(self, config_command):
"""Test set command with insufficient arguments."""
result = config_command.handle_set(["6"]) # Missing value
assert result is False
def test_handle_set_with_spaces_in_value(self, config_command):
"""Test setting a variable with value containing spaces."""
result = config_command.handle_set(["6", "model with spaces"])
assert result is True
assert os.environ.get("CAI_MODEL") == "model with spaces"
def test_handle_set_empty_value(self, config_command):
"""Test setting a variable with empty value."""
result = config_command.handle_set(["6", ""])
assert result is True
assert os.environ.get("CAI_MODEL") == ""
def test_command_base_functionality(self, config_command):
"""Test that the command inherits from base Command properly."""
assert isinstance(config_command, Command)
assert config_command.name == "/config"
assert "/cfg" in config_command.aliases
def test_handle_main_command_routing(self, config_command):
"""Test that main handle method routes to correct subcommands."""
# Test routing to list (no args defaults to list)
result1 = config_command.handle([])
assert result1 is True
# Test routing to list explicitly
result2 = config_command.handle(["list"])
assert result2 is True
# Test routing to get
result3 = config_command.handle(["get", "6"])
assert result3 is True
# Test routing to set
result4 = config_command.handle(["set", "6", "test-value"])
assert result4 is True
assert os.environ.get("CAI_MODEL") == "test-value"
def test_handle_unknown_subcommand(self, config_command):
"""Test handling of unknown subcommands."""
result = config_command.handle(["unknown_subcommand"])
assert result is False
def test_specific_env_vars_exist(self):
"""Test that specific important environment variables are defined."""
important_vars = [
"CAI_MODEL", "CAI_DEBUG", "CAI_BRIEF", "CAI_MAX_TURNS",
"CAI_TRACING", "CAI_AGENT_TYPE", "CTF_NAME", "CTF_CHALLENGE"
]
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):
"""Test that environment variable defaults are reasonable."""
# Check that important variables have reasonable defaults
for var_info in ENV_VARS.values():
var_name = var_info["name"]
default = var_info["default"]
# Boolean-like variables should have "true"/"false" defaults or numeric values
# Exclude interval variables as they have numeric values
boolean_keywords = ["debug", "brief", "tracing", "memory", "online", "offline", "inside"]
if (any(keyword in var_name.lower() for keyword in boolean_keywords) and
"interval" not in var_name.lower()):
if default is not None:
# Accept boolean strings or numeric values (for debug levels)
valid_values = ["true", "false", "0", "1", "2"]
assert default.lower() in valid_values, f"{var_name} should have boolean-like or numeric default"
# Numeric variables should have numeric defaults
if any(keyword in var_name.lower() for keyword in ["turns", "limit", "interval"]):
if default is not None:
# Should be numeric or "inf"
try:
float(default)
except ValueError:
assert default == "inf", f"{var_name} should have numeric default or 'inf'"
@pytest.mark.integration
class TestConfigCommandIntegration:
"""Integration tests for config command functionality."""
@pytest.fixture(autouse=True)
def setup_integration(self):
"""Setup for integration tests."""
# Store original values
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
# Restore original values
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_config_workflow(self):
"""Test a complete workflow of listing, getting, and setting variables."""
cmd = ConfigCommand()
# List all variables
result1 = cmd.handle(["list"])
assert result1 is True
# Get a specific variable (CAI_MODEL - number 6)
result2 = cmd.handle(["get", "6"])
assert result2 is True
# Set the variable to a new value
result3 = cmd.handle(["set", "6", "integration-test-model"])
assert result3 is True
assert os.environ.get("CAI_MODEL") == "integration-test-model"
# Get the variable again to verify it changed
result4 = cmd.handle(["get", "6"])
assert result4 is True
# Set it back to default (if it had a default)
for var_info in ENV_VARS.values():
if var_info["name"] == "CAI_MODEL" and var_info["default"]:
result5 = cmd.handle(["set", "6", var_info["default"]])
assert result5 is True
break
def test_multiple_variable_modifications(self):
"""Test modifying multiple variables in sequence."""
cmd = ConfigCommand()
# Modify several variables
modifications = [
("6", "test-model"), # CAI_MODEL
("7", "2"), # CAI_DEBUG
("8", "true"), # CAI_BRIEF
]
for var_num, value in modifications:
result = cmd.handle(["set", var_num, value])
assert result is True
# Verify the change
get_result = cmd.handle(["get", var_num])
assert get_result is True
# Verify all changes are still present
for var_num, expected_value in modifications:
# Find the variable name
var_info = ENV_VARS[int(var_num)]
var_name = var_info["name"]
assert os.environ.get(var_name) == expected_value
def test_edge_case_values(self):
"""Test setting variables with edge case values."""
cmd = ConfigCommand()
edge_cases = [
("6", ""), # Empty string
("6", "value with spaces"), # Spaces
("6", "special!@#$%chars"), # Special characters
("6", "very_long_value_that_exceeds_normal_length_expectations"), # Long value
("7", "0"), # Zero
("7", "999"), # Large number
]
for var_num, value in edge_cases:
result = cmd.handle(["set", var_num, value])
assert result is True
# Find the variable name and verify
var_info = ENV_VARS[int(var_num)]
var_name = var_info["name"]
assert os.environ.get(var_name) == value
def test_all_env_vars_can_be_set_and_retrieved(self):
"""Test that all defined environment variables can be set and retrieved."""
cmd = ConfigCommand()
for var_num, var_info in ENV_VARS.items():
var_name = var_info["name"]
test_value = f"test_value_for_{var_name}"
# Set the variable
set_result = cmd.handle(["set", str(var_num), test_value])
assert set_result is True, f"Failed to set {var_name}"
# Get the variable
get_result = cmd.handle(["get", str(var_num)])
assert get_result is True, f"Failed to get {var_name}"
# Verify the value was set correctly
assert os.environ.get(var_name) == test_value
if __name__ == '__main__':
pytest.main([__file__, "-v"])