Phase 4: Shell completion scripts and comprehensive test suite
This commit is contained in:
1 parent
aba2fc11f4
commit
5e70e22780
5 files changed
+641
-7
No files matched your search
@@ -0,0 +1,186 @@
|
||||
"""
|
||||
Test suite for MaxClaw Agent configuration manager
|
||||
"""
|
||||
import os
|
||||
import json
|
||||
import tempfile
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
# Add parent directory to path
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from config_manager import ConfigManager
|
||||
|
||||
|
||||
class TestConfigManager:
|
||||
"""Test cases for ConfigManager"""
|
||||
|
||||
def setup_method(self):
|
||||
"""Set up test environment"""
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
self.config_manager = ConfigManager(self.temp_dir)
|
||||
|
||||
def teardown_method(self):
|
||||
"""Clean up test environment"""
|
||||
import shutil
|
||||
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
||||
|
||||
def test_initialization(self):
|
||||
"""Test ConfigManager initialization"""
|
||||
assert self.config_manager.config_dir.exists()
|
||||
assert self.config_manager.config_file.exists()
|
||||
assert self.config_manager.key_file.exists()
|
||||
assert self.config_manager.config == {"configurations": {}, "active_configuration": None, "version": "1.0"}
|
||||
|
||||
def test_add_configuration(self):
|
||||
"""Test adding a configuration"""
|
||||
config = {
|
||||
"provider": "ollama",
|
||||
"model_name": "gemma2:9b",
|
||||
"base_url": "http://localhost:11434"
|
||||
}
|
||||
|
||||
self.config_manager.add_configuration("test-config", config)
|
||||
|
||||
# Verify configuration was added
|
||||
assert self.config_manager.configuration_exists("test-config")
|
||||
retrieved = self.config_manager.get_configuration("test-config")
|
||||
assert retrieved["provider"] == "ollama"
|
||||
assert retrieved["model_name"] == "gemma2:9b"
|
||||
assert retrieved["base_url"] == "http://localhost:11434"
|
||||
|
||||
def test_remove_configuration(self):
|
||||
"""Test removing a configuration"""
|
||||
config = {
|
||||
"provider": "openai",
|
||||
"model_name": "gpt-4o",
|
||||
"api_key": "sk-test123"
|
||||
}
|
||||
|
||||
self.config_manager.add_configuration("test-openai", config)
|
||||
assert self.config_manager.configuration_exists("test-openai")
|
||||
|
||||
# Remove configuration
|
||||
result = self.config_manager.remove_configuration("test-openai")
|
||||
assert result is True
|
||||
assert not self.config_manager.configuration_exists("test-openai")
|
||||
|
||||
def test_set_active_configuration(self):
|
||||
"""Test setting active configuration"""
|
||||
config = {
|
||||
"provider": "ollama",
|
||||
"model_name": "gemma2:9b",
|
||||
"base_url": "http://localhost:11434"
|
||||
}
|
||||
|
||||
self.config_manager.add_configuration("test-config", config)
|
||||
|
||||
# Set as active
|
||||
result = self.config_manager.set_active_configuration("test-config")
|
||||
assert result is True
|
||||
assert self.config_manager.get_active_configuration() == "test-config"
|
||||
|
||||
def test_validation(self):
|
||||
"""Test configuration validation"""
|
||||
# Valid configuration
|
||||
valid_config = {
|
||||
"provider": "ollama",
|
||||
"model_name": "gemma2:9b",
|
||||
"base_url": "http://localhost:11434"
|
||||
}
|
||||
|
||||
is_valid, errors = self.config_manager.validate_configuration(valid_config)
|
||||
assert is_valid is True
|
||||
assert len(errors) == 0
|
||||
|
||||
# Invalid configuration - missing provider
|
||||
invalid_config = {
|
||||
"model_name": "gemma2:9b",
|
||||
"base_url": "http://localhost:11434"
|
||||
}
|
||||
|
||||
is_valid, errors = self.config_manager.validate_configuration(invalid_config)
|
||||
assert is_valid is False
|
||||
assert "Provider is required" in errors
|
||||
|
||||
# Invalid configuration - OpenAI without API key
|
||||
invalid_openai = {
|
||||
"provider": "openai",
|
||||
"model_name": "gpt-4o"
|
||||
}
|
||||
|
||||
is_valid, errors = self.config_manager.validate_configuration(invalid_openai)
|
||||
assert is_valid is False
|
||||
assert "API key is required for OpenAI provider" in errors
|
||||
|
||||
def test_encryption(self):
|
||||
"""Test that API keys are encrypted"""
|
||||
config = {
|
||||
"provider": "openai",
|
||||
"model_name": "gpt-4o",
|
||||
"api_key": "sk-test123456789"
|
||||
}
|
||||
|
||||
self.config_manager.add_configuration("test-openai", config)
|
||||
|
||||
# Read the raw config file
|
||||
with open(self.config_manager.config_file, 'r') as f:
|
||||
raw_config = json.load(f)
|
||||
|
||||
# Check that API key is encrypted in the file
|
||||
stored_config = raw_config["configurations"]["test-openai"]
|
||||
assert "api_key" in stored_config
|
||||
assert stored_config["api_key"] != "sk-test123456789" # Should be encrypted
|
||||
assert len(stored_config["api_key"]) > 20 # Encrypted data is longer
|
||||
|
||||
# But when retrieved, it should be decrypted
|
||||
retrieved = self.config_manager.get_configuration("test-openai")
|
||||
assert retrieved["api_key"] == "sk-test123456789"
|
||||
|
||||
def test_list_configurations(self):
|
||||
"""Test listing configurations"""
|
||||
# Add multiple configurations
|
||||
configs = [
|
||||
("config1", {"provider": "ollama", "model_name": "gemma2:9b", "base_url": "http://localhost:11434"}),
|
||||
("config2", {"provider": "openai", "model_name": "gpt-4o", "api_key": "sk-test"}),
|
||||
("config3", {"provider": "openrouter", "model_name": "claude-3.5-sonnet", "api_key": "sk-or-test", "base_url": "https://openrouter.ai/api/v1"})
|
||||
]
|
||||
|
||||
for name, config in configs:
|
||||
self.config_manager.add_configuration(name, config)
|
||||
|
||||
# List configurations
|
||||
config_names = self.config_manager.list_configuration_names()
|
||||
assert len(config_names) == 3
|
||||
assert "config1" in config_names
|
||||
assert "config2" in config_names
|
||||
assert "config3" in config_names
|
||||
|
||||
def test_backup_creation(self):
|
||||
"""Test that backups are created"""
|
||||
config = {
|
||||
"provider": "ollama",
|
||||
"model_name": "gemma2:9b",
|
||||
"base_url": "http://localhost:11434"
|
||||
}
|
||||
|
||||
# Add first configuration
|
||||
self.config_manager.add_configuration("config1", config)
|
||||
|
||||
# Add second configuration (should create backup)
|
||||
config2 = {
|
||||
"provider": "openai",
|
||||
"model_name": "gpt-4o",
|
||||
"api_key": "sk-test"
|
||||
}
|
||||
self.config_manager.add_configuration("config2", config2)
|
||||
|
||||
# Check that backup file exists
|
||||
backup_file = self.config_manager.config_file.with_suffix('.json.bak')
|
||||
assert backup_file.exists()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-v"])
|
||||
@@ -0,0 +1,256 @@
|
||||
"""
|
||||
Test suite for MaxClaw Agent provider tools
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import pytest
|
||||
from unittest.mock import Mock, patch, MagicMock
|
||||
|
||||
# Add parent directory to path
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from tools.provider_tools import (
|
||||
OllamaProviderTools,
|
||||
OpenAIProviderTools,
|
||||
OpenRouterProviderTools,
|
||||
get_provider_tools
|
||||
)
|
||||
|
||||
|
||||
class TestOllamaProviderTools:
|
||||
"""Test cases for OllamaProviderTools"""
|
||||
|
||||
def setup_method(self):
|
||||
"""Set up test environment"""
|
||||
self.tools = OllamaProviderTools(base_url="http://localhost:11434")
|
||||
|
||||
def test_initialization(self):
|
||||
"""Test OllamaProviderTools initialization"""
|
||||
assert self.tools.provider_name == "ollama"
|
||||
assert self.tools.base_url == "http://localhost:11434"
|
||||
assert self.tools.api_url == "http://localhost:11434/api"
|
||||
|
||||
@patch('requests.get')
|
||||
def test_list_models_success(self, mock_get):
|
||||
"""Test successful model listing"""
|
||||
mock_response = Mock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {
|
||||
"models": [
|
||||
{"name": "gemma2:9b", "size": 5000000000},
|
||||
{"name": "llama2:7b", "size": 4000000000}
|
||||
]
|
||||
}
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
result = self.tools.list_models()
|
||||
|
||||
assert "Available Ollama Models" in result
|
||||
assert "gemma2:9b" in result
|
||||
assert "llama2:7b" in result
|
||||
|
||||
@patch('requests.get')
|
||||
def test_list_models_failure(self, mock_get):
|
||||
"""Test failed model listing"""
|
||||
mock_get.side_effect = Exception("Connection failed")
|
||||
|
||||
result = self.tools.list_models()
|
||||
|
||||
assert "Error listing models" in result
|
||||
|
||||
@patch('requests.post')
|
||||
def test_get_model_info_success(self, mock_post):
|
||||
"""Test successful model info retrieval"""
|
||||
mock_response = Mock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {
|
||||
"license": "Apache 2.0",
|
||||
"parameters": "9B",
|
||||
"details": {
|
||||
"parameter_size": "9B",
|
||||
"quantization_level": "Q4_0"
|
||||
}
|
||||
}
|
||||
mock_post.return_value = mock_response
|
||||
|
||||
result = self.tools.get_model_info("gemma2:9b")
|
||||
|
||||
assert "Model Information: gemma2:9b" in result
|
||||
assert "License: Apache 2.0" in result
|
||||
assert "Parameter Size: 9B" in result
|
||||
|
||||
@patch('requests.get')
|
||||
def test_test_connection_success(self, mock_get):
|
||||
"""Test successful connection test"""
|
||||
mock_response = Mock()
|
||||
mock_response.status_code = 200
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
result = self.tools.test_connection()
|
||||
|
||||
assert "✅ Ollama instance at http://localhost:11434 is running" in result
|
||||
|
||||
@patch('requests.get')
|
||||
def test_test_connection_failure(self, mock_get):
|
||||
"""Test failed connection test"""
|
||||
mock_get.side_effect = Exception("Connection failed")
|
||||
|
||||
result = self.tools.test_connection()
|
||||
|
||||
assert "❌ Cannot connect to Ollama instance" in result
|
||||
|
||||
def test_get_tools(self):
|
||||
"""Test tool generation"""
|
||||
tools = self.tools.get_tools()
|
||||
|
||||
assert len(tools) == 6
|
||||
tool_names = [tool.name for tool in tools]
|
||||
assert "ollama_list_models" in tool_names
|
||||
assert "ollama_model_info" in tool_names
|
||||
assert "ollama_test_connection" in tool_names
|
||||
assert "ollama_get_default_model" in tool_names
|
||||
assert "ollama_set_default_model" in tool_names
|
||||
assert "ollama_info" in tool_names
|
||||
|
||||
|
||||
class TestOpenAIProviderTools:
|
||||
"""Test cases for OpenAIProviderTools"""
|
||||
|
||||
def setup_method(self):
|
||||
"""Set up test environment"""
|
||||
self.tools = OpenAIProviderTools(api_key="sk-test123")
|
||||
|
||||
def test_initialization(self):
|
||||
"""Test OpenAIProviderTools initialization"""
|
||||
assert self.tools.provider_name == "openai"
|
||||
assert self.tools.api_key == "sk-test123"
|
||||
assert self.tools.api_url == "https://api.openai.com/v1"
|
||||
|
||||
@patch('requests.get')
|
||||
def test_list_models_success(self, mock_get):
|
||||
"""Test successful model listing"""
|
||||
mock_response = Mock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {
|
||||
"data": [
|
||||
{"id": "gpt-4o"},
|
||||
{"id": "gpt-4o-mini"},
|
||||
{"id": "gpt-3.5-turbo"}
|
||||
]
|
||||
}
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
result = self.tools.list_models()
|
||||
|
||||
assert "Available OpenAI Models" in result
|
||||
assert "gpt-4o" in result
|
||||
assert "gpt-4o-mini" in result
|
||||
|
||||
@patch('requests.get')
|
||||
def test_test_connection_success(self, mock_get):
|
||||
"""Test successful connection test"""
|
||||
mock_response = Mock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {"data": []}
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
result = self.tools.test_connection()
|
||||
|
||||
assert "✅ OpenAI API connection successful" in result
|
||||
|
||||
def test_get_tools(self):
|
||||
"""Test tool generation"""
|
||||
tools = self.tools.get_tools()
|
||||
|
||||
assert len(tools) == 6
|
||||
tool_names = [tool.name for tool in tools]
|
||||
assert "openai_list_models" in tool_names
|
||||
assert "openai_model_info" in tool_names
|
||||
assert "openai_test_connection" in tool_names
|
||||
|
||||
|
||||
class TestOpenRouterProviderTools:
|
||||
"""Test cases for OpenRouterProviderTools"""
|
||||
|
||||
def setup_method(self):
|
||||
"""Set up test environment"""
|
||||
self.tools = OpenRouterProviderTools(
|
||||
api_key="sk-or-test123",
|
||||
base_url="https://openrouter.ai/api/v1"
|
||||
)
|
||||
|
||||
def test_initialization(self):
|
||||
"""Test OpenRouterProviderTools initialization"""
|
||||
assert self.tools.provider_name == "openrouter"
|
||||
assert self.tools.api_key == "sk-or-test123"
|
||||
assert self.tools.base_url == "https://openrouter.ai/api/v1"
|
||||
|
||||
@patch('requests.get')
|
||||
def test_list_models_success(self, mock_get):
|
||||
"""Test successful model listing"""
|
||||
mock_response = Mock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {
|
||||
"data": [
|
||||
{"id": "anthropic/claude-3.5-sonnet", "name": "Claude 3.5 Sonnet"},
|
||||
{"id": "openai/gpt-4o", "name": "GPT-4o"}
|
||||
]
|
||||
}
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
result = self.tools.list_models()
|
||||
|
||||
assert "Available OpenRouter Models" in result
|
||||
assert "anthropic/claude-3.5-sonnet" in result
|
||||
assert "openai/gpt-4o" in result
|
||||
|
||||
def test_get_tools(self):
|
||||
"""Test tool generation"""
|
||||
tools = self.tools.get_tools()
|
||||
|
||||
assert len(tools) == 6
|
||||
tool_names = [tool.name for tool in tools]
|
||||
assert "openrouter_list_models" in tool_names
|
||||
assert "openrouter_model_info" in tool_names
|
||||
assert "openrouter_test_connection" in tool_names
|
||||
|
||||
|
||||
class TestGetProviderTools:
|
||||
"""Test cases for get_provider_tools function"""
|
||||
|
||||
@patch.dict(os.environ, {"OLLAMA_BASE_URL": "http://localhost:11434"})
|
||||
def test_get_ollama_tools(self):
|
||||
"""Test getting Ollama tools"""
|
||||
tools = get_provider_tools("ollama")
|
||||
|
||||
assert len(tools) == 6
|
||||
tool_names = [tool.name for tool in tools]
|
||||
assert "ollama_list_models" in tool_names
|
||||
|
||||
@patch.dict(os.environ, {"OPENAI_API_KEY": "sk-test123"})
|
||||
def test_get_openai_tools(self):
|
||||
"""Test getting OpenAI tools"""
|
||||
tools = get_provider_tools("openai")
|
||||
|
||||
assert len(tools) == 6
|
||||
tool_names = [tool.name for tool in tools]
|
||||
assert "openai_list_models" in tool_names
|
||||
|
||||
@patch.dict(os.environ, {"OPENROUTER_API_KEY": "sk-or-test123"})
|
||||
def test_get_openrouter_tools(self):
|
||||
"""Test getting OpenRouter tools"""
|
||||
tools = get_provider_tools("openrouter")
|
||||
|
||||
assert len(tools) == 6
|
||||
tool_names = [tool.name for tool in tools]
|
||||
assert "openrouter_list_models" in tool_names
|
||||
|
||||
def test_get_unknown_provider_tools(self):
|
||||
"""Test getting tools for unknown provider"""
|
||||
tools = get_provider_tools("unknown")
|
||||
|
||||
assert len(tools) == 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-v"])
|
||||
Reference in new issue
Block a user