Skip to main content
Glama

Gemini MCP Server

test_tools.py19.1 kB
""" Tests for individual tool implementations """ import json import shutil import tempfile import pytest from tools import AnalyzeTool, ChatTool, CodeReviewTool, ThinkDeepTool class TestThinkDeepTool: """Test the thinkdeep tool""" @pytest.fixture def tool(self): return ThinkDeepTool() def test_tool_metadata(self, tool): """Test tool metadata""" assert tool.get_name() == "thinkdeep" assert "investigation and reasoning" in tool.get_description() assert tool.get_default_temperature() == 0.7 schema = tool.get_input_schema() # ThinkDeep is now a workflow tool with step-based fields assert "step" in schema["properties"] assert "step_number" in schema["properties"] assert "total_steps" in schema["properties"] assert "next_step_required" in schema["properties"] assert "findings" in schema["properties"] # Required fields for workflow expected_required = {"step", "step_number", "total_steps", "next_step_required", "findings"} assert expected_required.issubset(set(schema["required"])) @pytest.mark.asyncio async def test_execute_success(self, tool): """Test successful execution using real integration testing""" import importlib import os # Save original environment original_env = { "OPENAI_API_KEY": os.environ.get("OPENAI_API_KEY"), "DEFAULT_MODEL": os.environ.get("DEFAULT_MODEL"), } try: # Set up environment for real provider resolution os.environ["OPENAI_API_KEY"] = "sk-test-key-thinkdeep-success-test-not-real" os.environ["DEFAULT_MODEL"] = "o3-mini" # Clear other provider keys to isolate to OpenAI for key in ["GEMINI_API_KEY", "XAI_API_KEY", "OPENROUTER_API_KEY"]: os.environ.pop(key, None) # Reload config and clear registry import config importlib.reload(config) from providers.registry import ModelProviderRegistry ModelProviderRegistry._instance = None # Test with real provider resolution try: result = await tool.execute( { "step": "Initial analysis", "step_number": 1, "total_steps": 1, "next_step_required": False, "findings": "Initial thinking about building a cache", "problem_context": "Building a cache", "focus_areas": ["performance", "scalability"], "model": "o3-mini", } ) # If we get here, check the response format assert len(result) == 1 # Should be a valid JSON response output = json.loads(result[0].text) assert "status" in output except Exception as e: # Expected: API call will fail with fake key error_msg = str(e) # Should NOT be a mock-related error assert "MagicMock" not in error_msg assert "'<' not supported between instances" not in error_msg # Should be a real provider error assert any( phrase in error_msg for phrase in ["API", "key", "authentication", "provider", "network", "connection"] ) finally: # Restore environment for key, value in original_env.items(): if value is not None: os.environ[key] = value else: os.environ.pop(key, None) # Reload config and clear registry importlib.reload(config) ModelProviderRegistry._instance = None class TestCodeReviewTool: """Test the codereview tool""" @pytest.fixture def tool(self): return CodeReviewTool() def test_tool_metadata(self, tool): """Test tool metadata""" assert tool.get_name() == "codereview" assert "code review" in tool.get_description() assert tool.get_default_temperature() == 0.2 schema = tool.get_input_schema() assert "relevant_files" in schema["properties"] assert "step" in schema["properties"] assert "step_number" in schema["required"] @pytest.mark.asyncio async def test_execute_with_review_type(self, tool, tmp_path): """Test execution with specific review type using real provider resolution""" import importlib import os # Create test file test_file = tmp_path / "test.py" test_file.write_text("def insecure(): pass", encoding="utf-8") # Save original environment original_env = { "OPENAI_API_KEY": os.environ.get("OPENAI_API_KEY"), "DEFAULT_MODEL": os.environ.get("DEFAULT_MODEL"), } try: # Set up environment for testing os.environ["OPENAI_API_KEY"] = "sk-test-key-codereview-test-not-real" os.environ["DEFAULT_MODEL"] = "o3-mini" # Clear other provider keys for key in ["GEMINI_API_KEY", "XAI_API_KEY", "OPENROUTER_API_KEY"]: os.environ.pop(key, None) # Reload config and clear registry import config importlib.reload(config) from providers.registry import ModelProviderRegistry ModelProviderRegistry._instance = None # Test with real provider resolution - expect it to fail at API level try: result = await tool.execute( { "step": "Review for security issues", "step_number": 1, "total_steps": 1, "next_step_required": False, "findings": "Initial security review", "relevant_files": [str(test_file)], "model": "o3-mini", } ) # If we somehow get here, that's fine too assert result is not None except Exception as e: # Expected: API call will fail with fake key error_msg = str(e) # Should NOT be a mock-related error assert "MagicMock" not in error_msg assert "'<' not supported between instances" not in error_msg # Should be a real provider error assert any( phrase in error_msg for phrase in ["API", "key", "authentication", "provider", "network", "connection"] ) finally: # Restore environment for key, value in original_env.items(): if value is not None: os.environ[key] = value else: os.environ.pop(key, None) # Reload config and clear registry importlib.reload(config) ModelProviderRegistry._instance = None class TestAnalyzeTool: """Test the analyze tool""" @pytest.fixture def tool(self): return AnalyzeTool() def test_tool_metadata(self, tool): """Test tool metadata""" assert tool.get_name() == "analyze" assert "code analysis" in tool.get_description() assert tool.get_default_temperature() == 0.2 schema = tool.get_input_schema() # New workflow tool requires step-based fields assert "step" in schema["properties"] assert "step_number" in schema["properties"] assert "total_steps" in schema["properties"] assert "next_step_required" in schema["properties"] assert "findings" in schema["properties"] # Workflow tools use relevant_files instead of files assert "relevant_files" in schema["properties"] # Required fields for workflow expected_required = {"step", "step_number", "total_steps", "next_step_required", "findings"} assert expected_required.issubset(set(schema["required"])) @pytest.mark.asyncio async def test_execute_with_analysis_type(self, tool, tmp_path): """Test execution with specific analysis type using real provider resolution""" import importlib import os # Create test file test_file = tmp_path / "module.py" test_file.write_text("class Service: pass", encoding="utf-8") # Save original environment original_env = { "OPENAI_API_KEY": os.environ.get("OPENAI_API_KEY"), "DEFAULT_MODEL": os.environ.get("DEFAULT_MODEL"), } try: # Set up environment for testing os.environ["OPENAI_API_KEY"] = "sk-test-key-analyze-test-not-real" os.environ["DEFAULT_MODEL"] = "o3-mini" # Clear other provider keys for key in ["GEMINI_API_KEY", "XAI_API_KEY", "OPENROUTER_API_KEY"]: os.environ.pop(key, None) # Reload config and clear registry import config importlib.reload(config) from providers.registry import ModelProviderRegistry ModelProviderRegistry._instance = None # Test with real provider resolution - expect it to fail at API level try: result = await tool.execute( { "step": "Analyze the structure of this code", "step_number": 1, "total_steps": 1, "next_step_required": False, "findings": "Initial analysis of code structure", "relevant_files": [str(test_file)], "analysis_type": "architecture", "output_format": "summary", "model": "o3-mini", } ) # If we somehow get here, that's fine too assert result is not None except Exception as e: # Expected: API call will fail with fake key error_msg = str(e) # Should NOT be a mock-related error assert "MagicMock" not in error_msg assert "'<' not supported between instances" not in error_msg # Should be a real provider error assert any( phrase in error_msg for phrase in ["API", "key", "authentication", "provider", "network", "connection"] ) finally: # Restore environment for key, value in original_env.items(): if value is not None: os.environ[key] = value else: os.environ.pop(key, None) # Reload config and clear registry importlib.reload(config) ModelProviderRegistry._instance = None class TestAbsolutePathValidation: """Test absolute path validation across all tools""" # Removed: test_analyze_tool_relative_path_rejected - workflow tool handles validation differently # NOTE: CodeReview tool test has been commented out because the codereview tool has been # refactored to use a workflow-based pattern. The workflow tools handle path validation # differently and may accept relative paths in step 1 since validation happens at the # file reading stage. See simulator_tests/test_codereview_validation.py for comprehensive # workflow testing of the new codereview tool. @pytest.mark.asyncio async def test_thinkdeep_tool_relative_path_rejected(self): """Test that thinkdeep tool rejects relative paths""" tool = ThinkDeepTool() result = await tool.execute( { "step": "My analysis", "step_number": 1, "total_steps": 1, "next_step_required": False, "findings": "Initial analysis", "files_checked": ["./local/file.py"], } ) assert len(result) == 1 response = json.loads(result[0].text) assert response["status"] == "error" assert "must be FULL absolute paths" in response["content"] assert "./local/file.py" in response["content"] @pytest.mark.asyncio async def test_chat_tool_relative_path_rejected(self): """Test that chat tool rejects relative paths""" tool = ChatTool() temp_dir = tempfile.mkdtemp() try: result = await tool.execute( { "prompt": "Explain this code", "files": ["code.py"], # relative path without ./ "working_directory": temp_dir, } ) finally: shutil.rmtree(temp_dir, ignore_errors=True) assert len(result) == 1 response = json.loads(result[0].text) assert response["status"] == "error" assert "must be FULL absolute paths" in response["content"] assert "code.py" in response["content"] @pytest.mark.asyncio async def test_analyze_tool_accepts_absolute_paths(self): """Test that analyze tool accepts absolute paths using real provider resolution""" import importlib import os tool = AnalyzeTool() # Save original environment original_env = { "OPENAI_API_KEY": os.environ.get("OPENAI_API_KEY"), "DEFAULT_MODEL": os.environ.get("DEFAULT_MODEL"), } try: # Set up environment for testing os.environ["OPENAI_API_KEY"] = "sk-test-key-absolute-path-test-not-real" os.environ["DEFAULT_MODEL"] = "o3-mini" # Clear other provider keys for key in ["GEMINI_API_KEY", "XAI_API_KEY", "OPENROUTER_API_KEY"]: os.environ.pop(key, None) # Reload config and clear registry import config importlib.reload(config) from providers.registry import ModelProviderRegistry ModelProviderRegistry._instance = None # Test with real provider resolution - expect it to fail at API level try: result = await tool.execute( { "step": "Analyze this code file", "step_number": 1, "total_steps": 1, "next_step_required": False, "findings": "Initial code analysis", "relevant_files": ["/absolute/path/file.py"], "model": "o3-mini", } ) # If we somehow get here, that's fine too assert result is not None except Exception as e: # Expected: API call will fail with fake key error_msg = str(e) # Should NOT be a mock-related error assert "MagicMock" not in error_msg assert "'<' not supported between instances" not in error_msg # Should be a real provider error assert any( phrase in error_msg for phrase in ["API", "key", "authentication", "provider", "network", "connection"] ) finally: # Restore environment for key, value in original_env.items(): if value is not None: os.environ[key] = value else: os.environ.pop(key, None) # Reload config and clear registry importlib.reload(config) ModelProviderRegistry._instance = None class TestSpecialStatusModels: """Test SPECIAL_STATUS_MODELS registry and structured response handling""" def test_trace_complete_status_in_registry(self): """Test that trace_complete status is properly registered""" from tools.models import SPECIAL_STATUS_MODELS, TraceComplete assert "trace_complete" in SPECIAL_STATUS_MODELS assert SPECIAL_STATUS_MODELS["trace_complete"] == TraceComplete def test_trace_complete_model_validation(self): """Test TraceComplete model validation""" from tools.models import TraceComplete # Test precision mode precision_data = { "status": "trace_complete", "trace_type": "precision", "entry_point": { "file": "/path/to/file.py", "class_or_struct": "MyClass", "method": "myMethod", "signature": "def myMethod(self, param1: str) -> bool", "parameters": {"param1": "test"}, }, "call_path": [ { "from": {"file": "/path/to/file.py", "class": "MyClass", "method": "myMethod", "line": 10}, "to": {"file": "/path/to/other.py", "class": "OtherClass", "method": "otherMethod", "line": 20}, "reason": "direct call", "condition": None, "ambiguous": False, } ], } model = TraceComplete(**precision_data) assert model.status == "trace_complete" assert model.trace_type == "precision" assert model.entry_point.file == "/path/to/file.py" assert len(model.call_path) == 1 # Test dependencies mode dependencies_data = { "status": "trace_complete", "trace_type": "dependencies", "target": { "file": "/path/to/file.py", "class_or_struct": "MyClass", "method": "myMethod", "signature": "def myMethod(self, param1: str) -> bool", }, "incoming_dependencies": [ { "from_file": "/path/to/caller.py", "from_class": "CallerClass", "from_method": "callerMethod", "line": 15, "type": "direct_call", } ], "outgoing_dependencies": [ { "to_file": "/path/to/dependency.py", "to_class": "DepClass", "to_method": "depMethod", "line": 25, "type": "method_call", } ], } model = TraceComplete(**dependencies_data) assert model.status == "trace_complete" assert model.trace_type == "dependencies" assert model.target.file == "/path/to/file.py" assert len(model.incoming_dependencies) == 1 assert len(model.outgoing_dependencies) == 1

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/BeehiveInnovations/gemini-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server