Rebranding, refactoring, renaming, cleanup, updated docs
This commit is contained in:
@@ -1 +1 @@
|
||||
# Tests for Gemini MCP Server
|
||||
# Tests for Zen MCP Server
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Pytest configuration for Gemini MCP Server tests
|
||||
Pytest configuration for Zen MCP Server tests
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
@@ -27,13 +27,15 @@ os.environ["DEFAULT_MODEL"] = "gemini-2.0-flash-exp"
|
||||
|
||||
# Force reload of config module to pick up the env var
|
||||
import importlib
|
||||
|
||||
import config
|
||||
|
||||
importlib.reload(config)
|
||||
|
||||
# Set MCP_PROJECT_ROOT to a temporary directory for tests
|
||||
# This provides a safe sandbox for file operations during testing
|
||||
# Create a temporary directory that will be used as the project root for all tests
|
||||
test_root = tempfile.mkdtemp(prefix="gemini_mcp_test_")
|
||||
test_root = tempfile.mkdtemp(prefix="zen_mcp_test_")
|
||||
os.environ["MCP_PROJECT_ROOT"] = test_root
|
||||
|
||||
# Configure asyncio for Windows compatibility
|
||||
@@ -42,9 +44,9 @@ if sys.platform == "win32":
|
||||
|
||||
# Register providers for all tests
|
||||
from providers import ModelProviderRegistry
|
||||
from providers.base import ProviderType
|
||||
from providers.gemini import GeminiModelProvider
|
||||
from providers.openai import OpenAIModelProvider
|
||||
from providers.base import ProviderType
|
||||
|
||||
# Register providers at test startup
|
||||
ModelProviderRegistry.register_provider(ProviderType.GOOGLE, GeminiModelProvider)
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
"""Helper functions for test mocking."""
|
||||
|
||||
from unittest.mock import Mock
|
||||
from providers.base import ModelCapabilities, ProviderType
|
||||
|
||||
from providers.base import ModelCapabilities, ProviderType, RangeTemperatureConstraint
|
||||
|
||||
|
||||
def create_mock_provider(model_name="gemini-2.0-flash-exp", max_tokens=1_048_576):
|
||||
"""Create a properly configured mock provider."""
|
||||
mock_provider = Mock()
|
||||
|
||||
|
||||
# Set up capabilities
|
||||
mock_capabilities = ModelCapabilities(
|
||||
provider=ProviderType.GOOGLE,
|
||||
@@ -17,14 +19,14 @@ def create_mock_provider(model_name="gemini-2.0-flash-exp", max_tokens=1_048_576
|
||||
supports_system_prompts=True,
|
||||
supports_streaming=True,
|
||||
supports_function_calling=True,
|
||||
temperature_range=(0.0, 2.0),
|
||||
temperature_constraint=RangeTemperatureConstraint(0.0, 2.0, 0.7),
|
||||
)
|
||||
|
||||
|
||||
mock_provider.get_capabilities.return_value = mock_capabilities
|
||||
mock_provider.get_provider_type.return_value = ProviderType.GOOGLE
|
||||
mock_provider.supports_thinking_mode.return_value = False
|
||||
mock_provider.validate_model_name.return_value = True
|
||||
|
||||
|
||||
# Set up generate_content response
|
||||
mock_response = Mock()
|
||||
mock_response.content = "Test response"
|
||||
@@ -33,7 +35,7 @@ def create_mock_provider(model_name="gemini-2.0-flash-exp", max_tokens=1_048_576
|
||||
mock_response.friendly_name = "Gemini"
|
||||
mock_response.provider = ProviderType.GOOGLE
|
||||
mock_response.metadata = {"finish_reason": "STOP"}
|
||||
|
||||
|
||||
mock_provider.generate_content.return_value = mock_response
|
||||
|
||||
|
||||
return mock_provider
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
"""Tests for auto mode functionality"""
|
||||
|
||||
import os
|
||||
import pytest
|
||||
from unittest.mock import patch, Mock
|
||||
import importlib
|
||||
import os
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from mcp.types import TextContent
|
||||
from tools.analyze import AnalyzeTool
|
||||
|
||||
|
||||
@@ -16,23 +16,24 @@ class TestAutoMode:
|
||||
"""Test that auto mode is detected correctly"""
|
||||
# Save original
|
||||
original = os.environ.get("DEFAULT_MODEL", "")
|
||||
|
||||
|
||||
try:
|
||||
# Test auto mode
|
||||
os.environ["DEFAULT_MODEL"] = "auto"
|
||||
import config
|
||||
|
||||
importlib.reload(config)
|
||||
|
||||
|
||||
assert config.DEFAULT_MODEL == "auto"
|
||||
assert config.IS_AUTO_MODE is True
|
||||
|
||||
|
||||
# Test non-auto mode
|
||||
os.environ["DEFAULT_MODEL"] = "pro"
|
||||
importlib.reload(config)
|
||||
|
||||
|
||||
assert config.DEFAULT_MODEL == "pro"
|
||||
assert config.IS_AUTO_MODE is False
|
||||
|
||||
|
||||
finally:
|
||||
# Restore
|
||||
if original:
|
||||
@@ -44,7 +45,7 @@ class TestAutoMode:
|
||||
def test_model_capabilities_descriptions(self):
|
||||
"""Test that model capabilities are properly defined"""
|
||||
from config import MODEL_CAPABILITIES_DESC
|
||||
|
||||
|
||||
# Check all expected models are present
|
||||
expected_models = ["flash", "pro", "o3", "o3-mini"]
|
||||
for model in expected_models:
|
||||
@@ -56,25 +57,26 @@ class TestAutoMode:
|
||||
"""Test that tool schemas require model in auto mode"""
|
||||
# Save original
|
||||
original = os.environ.get("DEFAULT_MODEL", "")
|
||||
|
||||
|
||||
try:
|
||||
# Enable auto mode
|
||||
os.environ["DEFAULT_MODEL"] = "auto"
|
||||
import config
|
||||
|
||||
importlib.reload(config)
|
||||
|
||||
|
||||
tool = AnalyzeTool()
|
||||
schema = tool.get_input_schema()
|
||||
|
||||
|
||||
# Model should be required
|
||||
assert "model" in schema["required"]
|
||||
|
||||
|
||||
# Model field should have detailed descriptions
|
||||
model_schema = schema["properties"]["model"]
|
||||
assert "enum" in model_schema
|
||||
assert "flash" in model_schema["enum"]
|
||||
assert "Choose the best model" in model_schema["description"]
|
||||
|
||||
|
||||
finally:
|
||||
# Restore
|
||||
if original:
|
||||
@@ -88,10 +90,10 @@ class TestAutoMode:
|
||||
# This test uses the default from conftest.py which sets non-auto mode
|
||||
tool = AnalyzeTool()
|
||||
schema = tool.get_input_schema()
|
||||
|
||||
|
||||
# Model should not be required
|
||||
assert "model" not in schema["required"]
|
||||
|
||||
|
||||
# Model field should have simpler description
|
||||
model_schema = schema["properties"]["model"]
|
||||
assert "enum" not in model_schema
|
||||
@@ -102,29 +104,27 @@ class TestAutoMode:
|
||||
"""Test that auto mode enforces model parameter"""
|
||||
# Save original
|
||||
original = os.environ.get("DEFAULT_MODEL", "")
|
||||
|
||||
|
||||
try:
|
||||
# Enable auto mode
|
||||
os.environ["DEFAULT_MODEL"] = "auto"
|
||||
import config
|
||||
|
||||
importlib.reload(config)
|
||||
|
||||
|
||||
tool = AnalyzeTool()
|
||||
|
||||
|
||||
# Mock the provider to avoid real API calls
|
||||
with patch.object(tool, 'get_model_provider') as mock_provider:
|
||||
with patch.object(tool, "get_model_provider"):
|
||||
# Execute without model parameter
|
||||
result = await tool.execute({
|
||||
"files": ["/tmp/test.py"],
|
||||
"prompt": "Analyze this"
|
||||
})
|
||||
|
||||
result = await tool.execute({"files": ["/tmp/test.py"], "prompt": "Analyze this"})
|
||||
|
||||
# Should get error
|
||||
assert len(result) == 1
|
||||
response = result[0].text
|
||||
assert "error" in response
|
||||
assert "Model parameter is required" in response
|
||||
|
||||
|
||||
finally:
|
||||
# Restore
|
||||
if original:
|
||||
@@ -136,45 +136,57 @@ class TestAutoMode:
|
||||
def test_model_field_schema_generation(self):
|
||||
"""Test the get_model_field_schema method"""
|
||||
from tools.base import BaseTool
|
||||
|
||||
|
||||
# Create a minimal concrete tool for testing
|
||||
class TestTool(BaseTool):
|
||||
def get_name(self): return "test"
|
||||
def get_description(self): return "test"
|
||||
def get_input_schema(self): return {}
|
||||
def get_system_prompt(self): return ""
|
||||
def get_request_model(self): return None
|
||||
async def prepare_prompt(self, request): return ""
|
||||
|
||||
def get_name(self):
|
||||
return "test"
|
||||
|
||||
def get_description(self):
|
||||
return "test"
|
||||
|
||||
def get_input_schema(self):
|
||||
return {}
|
||||
|
||||
def get_system_prompt(self):
|
||||
return ""
|
||||
|
||||
def get_request_model(self):
|
||||
return None
|
||||
|
||||
async def prepare_prompt(self, request):
|
||||
return ""
|
||||
|
||||
tool = TestTool()
|
||||
|
||||
|
||||
# Save original
|
||||
original = os.environ.get("DEFAULT_MODEL", "")
|
||||
|
||||
|
||||
try:
|
||||
# Test auto mode
|
||||
os.environ["DEFAULT_MODEL"] = "auto"
|
||||
os.environ["DEFAULT_MODEL"] = "auto"
|
||||
import config
|
||||
|
||||
importlib.reload(config)
|
||||
|
||||
|
||||
schema = tool.get_model_field_schema()
|
||||
assert "enum" in schema
|
||||
assert all(model in schema["enum"] for model in ["flash", "pro", "o3"])
|
||||
assert "Choose the best model" in schema["description"]
|
||||
|
||||
|
||||
# Test normal mode
|
||||
os.environ["DEFAULT_MODEL"] = "pro"
|
||||
importlib.reload(config)
|
||||
|
||||
|
||||
schema = tool.get_model_field_schema()
|
||||
assert "enum" not in schema
|
||||
assert "Available:" in schema["description"]
|
||||
assert "'pro'" in schema["description"]
|
||||
|
||||
|
||||
finally:
|
||||
# Restore
|
||||
if original:
|
||||
os.environ["DEFAULT_MODEL"] = original
|
||||
else:
|
||||
os.environ.pop("DEFAULT_MODEL", None)
|
||||
importlib.reload(config)
|
||||
importlib.reload(config)
|
||||
|
||||
@@ -7,11 +7,11 @@ when Gemini doesn't explicitly ask a follow-up question.
|
||||
|
||||
import json
|
||||
from unittest.mock import Mock, patch
|
||||
from tests.mock_helpers import create_mock_provider
|
||||
|
||||
import pytest
|
||||
from pydantic import Field
|
||||
|
||||
from tests.mock_helpers import create_mock_provider
|
||||
from tools.base import BaseTool, ToolRequest
|
||||
from tools.models import ContinuationOffer, ToolOutput
|
||||
from utils.conversation_memory import MAX_CONVERSATION_TURNS
|
||||
@@ -125,7 +125,7 @@ class TestClaudeContinuationOffers:
|
||||
content="Analysis complete. The code looks good.",
|
||||
usage={"input_tokens": 10, "output_tokens": 20, "total_tokens": 30},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={"finish_reason": "STOP"}
|
||||
metadata={"finish_reason": "STOP"},
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
@@ -176,7 +176,7 @@ class TestClaudeContinuationOffers:
|
||||
content=content_with_followup,
|
||||
usage={"input_tokens": 10, "output_tokens": 20, "total_tokens": 30},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={"finish_reason": "STOP"}
|
||||
metadata={"finish_reason": "STOP"},
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
@@ -220,7 +220,7 @@ class TestClaudeContinuationOffers:
|
||||
content="Continued analysis complete.",
|
||||
usage={"input_tokens": 10, "output_tokens": 20, "total_tokens": 30},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={"finish_reason": "STOP"}
|
||||
metadata={"finish_reason": "STOP"},
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@ Tests for dynamic context request and collaboration features
|
||||
|
||||
import json
|
||||
from unittest.mock import Mock, patch
|
||||
from tests.mock_helpers import create_mock_provider
|
||||
|
||||
import pytest
|
||||
|
||||
from tests.mock_helpers import create_mock_provider
|
||||
from tools.analyze import AnalyzeTool
|
||||
from tools.debug import DebugIssueTool
|
||||
from tools.models import ClarificationRequest, ToolOutput
|
||||
@@ -41,10 +41,7 @@ class TestDynamicContextRequests:
|
||||
mock_provider.get_provider_type.return_value = Mock(value="google")
|
||||
mock_provider.supports_thinking_mode.return_value = False
|
||||
mock_provider.generate_content.return_value = Mock(
|
||||
content=clarification_json,
|
||||
usage={},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={}
|
||||
content=clarification_json, usage={}, model_name="gemini-2.0-flash-exp", metadata={}
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
@@ -85,10 +82,7 @@ class TestDynamicContextRequests:
|
||||
mock_provider.get_provider_type.return_value = Mock(value="google")
|
||||
mock_provider.supports_thinking_mode.return_value = False
|
||||
mock_provider.generate_content.return_value = Mock(
|
||||
content=normal_response,
|
||||
usage={},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={}
|
||||
content=normal_response, usage={}, model_name="gemini-2.0-flash-exp", metadata={}
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
@@ -112,10 +106,7 @@ class TestDynamicContextRequests:
|
||||
mock_provider.get_provider_type.return_value = Mock(value="google")
|
||||
mock_provider.supports_thinking_mode.return_value = False
|
||||
mock_provider.generate_content.return_value = Mock(
|
||||
content=malformed_json,
|
||||
usage={},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={}
|
||||
content=malformed_json, usage={}, model_name="gemini-2.0-flash-exp", metadata={}
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
@@ -155,10 +146,7 @@ class TestDynamicContextRequests:
|
||||
mock_provider.get_provider_type.return_value = Mock(value="google")
|
||||
mock_provider.supports_thinking_mode.return_value = False
|
||||
mock_provider.generate_content.return_value = Mock(
|
||||
content=clarification_json,
|
||||
usage={},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={}
|
||||
content=clarification_json, usage={}, model_name="gemini-2.0-flash-exp", metadata={}
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
@@ -245,10 +233,7 @@ class TestCollaborationWorkflow:
|
||||
mock_provider.get_provider_type.return_value = Mock(value="google")
|
||||
mock_provider.supports_thinking_mode.return_value = False
|
||||
mock_provider.generate_content.return_value = Mock(
|
||||
content=clarification_json,
|
||||
usage={},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={}
|
||||
content=clarification_json, usage={}, model_name="gemini-2.0-flash-exp", metadata={}
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
@@ -287,10 +272,7 @@ class TestCollaborationWorkflow:
|
||||
mock_provider.get_provider_type.return_value = Mock(value="google")
|
||||
mock_provider.supports_thinking_mode.return_value = False
|
||||
mock_provider.generate_content.return_value = Mock(
|
||||
content=clarification_json,
|
||||
usage={},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={}
|
||||
content=clarification_json, usage={}, model_name="gemini-2.0-flash-exp", metadata={}
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
@@ -317,10 +299,7 @@ class TestCollaborationWorkflow:
|
||||
"""
|
||||
|
||||
mock_provider.generate_content.return_value = Mock(
|
||||
content=final_response,
|
||||
usage={},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={}
|
||||
content=final_response, usage={}, model_name="gemini-2.0-flash-exp", metadata={}
|
||||
)
|
||||
|
||||
result2 = await tool.execute(
|
||||
|
||||
@@ -2,21 +2,20 @@
|
||||
Test that conversation history is correctly mapped to tool-specific fields
|
||||
"""
|
||||
|
||||
import json
|
||||
import pytest
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
from tests.mock_helpers import create_mock_provider
|
||||
from datetime import datetime
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from providers.base import ProviderType
|
||||
from server import reconstruct_thread_context
|
||||
from utils.conversation_memory import ConversationTurn, ThreadContext
|
||||
from providers.base import ProviderType
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_conversation_history_field_mapping():
|
||||
"""Test that enhanced prompts are mapped to prompt field for all tools"""
|
||||
|
||||
|
||||
# Test data for different tools - all use 'prompt' now
|
||||
test_cases = [
|
||||
{
|
||||
@@ -40,7 +39,7 @@ async def test_conversation_history_field_mapping():
|
||||
"original_value": "My analysis so far",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
for test_case in test_cases:
|
||||
# Create mock conversation context
|
||||
mock_context = ThreadContext(
|
||||
@@ -63,7 +62,7 @@ async def test_conversation_history_field_mapping():
|
||||
],
|
||||
initial_context={},
|
||||
)
|
||||
|
||||
|
||||
# Mock get_thread to return our test context
|
||||
with patch("utils.conversation_memory.get_thread", return_value=mock_context):
|
||||
with patch("utils.conversation_memory.add_turn", return_value=True):
|
||||
@@ -71,43 +70,44 @@ async def test_conversation_history_field_mapping():
|
||||
# Mock provider registry to avoid model lookup errors
|
||||
with patch("providers.registry.ModelProviderRegistry.get_provider_for_model") as mock_get_provider:
|
||||
from providers.base import ModelCapabilities
|
||||
|
||||
mock_provider = MagicMock()
|
||||
mock_provider.get_capabilities.return_value = ModelCapabilities(
|
||||
provider=ProviderType.GOOGLE,
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
friendly_name="Gemini",
|
||||
max_tokens=200000,
|
||||
supports_extended_thinking=True
|
||||
supports_extended_thinking=True,
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
# Mock conversation history building
|
||||
mock_build.return_value = (
|
||||
"=== CONVERSATION HISTORY ===\nPrevious conversation content\n=== END HISTORY ===",
|
||||
1000 # mock token count
|
||||
1000, # mock token count
|
||||
)
|
||||
|
||||
|
||||
# Create arguments with continuation_id
|
||||
arguments = {
|
||||
"continuation_id": "test-thread-123",
|
||||
"prompt": test_case["original_value"],
|
||||
"files": ["/test/file2.py"],
|
||||
}
|
||||
|
||||
|
||||
# Call reconstruct_thread_context
|
||||
enhanced_args = await reconstruct_thread_context(arguments)
|
||||
|
||||
|
||||
# Verify the enhanced prompt is in the prompt field
|
||||
assert "prompt" in enhanced_args
|
||||
enhanced_value = enhanced_args["prompt"]
|
||||
|
||||
|
||||
# Should contain conversation history
|
||||
assert "=== CONVERSATION HISTORY ===" in enhanced_value
|
||||
assert "Previous conversation content" in enhanced_value
|
||||
|
||||
|
||||
# Should contain the new user input
|
||||
assert "=== NEW USER INPUT ===" in enhanced_value
|
||||
assert test_case["original_value"] in enhanced_value
|
||||
|
||||
|
||||
# Should have token budget
|
||||
assert "_remaining_tokens" in enhanced_args
|
||||
assert enhanced_args["_remaining_tokens"] > 0
|
||||
@@ -116,7 +116,7 @@ async def test_conversation_history_field_mapping():
|
||||
@pytest.mark.asyncio
|
||||
async def test_unknown_tool_defaults_to_prompt():
|
||||
"""Test that unknown tools default to using 'prompt' field"""
|
||||
|
||||
|
||||
mock_context = ThreadContext(
|
||||
thread_id="test-thread-456",
|
||||
tool_name="unknown_tool",
|
||||
@@ -125,7 +125,7 @@ async def test_unknown_tool_defaults_to_prompt():
|
||||
turns=[],
|
||||
initial_context={},
|
||||
)
|
||||
|
||||
|
||||
with patch("utils.conversation_memory.get_thread", return_value=mock_context):
|
||||
with patch("utils.conversation_memory.add_turn", return_value=True):
|
||||
with patch("utils.conversation_memory.build_conversation_history", return_value=("History", 500)):
|
||||
@@ -133,9 +133,9 @@ async def test_unknown_tool_defaults_to_prompt():
|
||||
"continuation_id": "test-thread-456",
|
||||
"prompt": "User input",
|
||||
}
|
||||
|
||||
|
||||
enhanced_args = await reconstruct_thread_context(arguments)
|
||||
|
||||
|
||||
# Should default to 'prompt' field
|
||||
assert "prompt" in enhanced_args
|
||||
assert "History" in enhanced_args["prompt"]
|
||||
@@ -145,27 +145,27 @@ async def test_unknown_tool_defaults_to_prompt():
|
||||
async def test_tool_parameter_standardization():
|
||||
"""Test that all tools use standardized 'prompt' parameter"""
|
||||
from tools.analyze import AnalyzeRequest
|
||||
from tools.debug import DebugIssueRequest
|
||||
from tools.codereview import CodeReviewRequest
|
||||
from tools.thinkdeep import ThinkDeepRequest
|
||||
from tools.debug import DebugIssueRequest
|
||||
from tools.precommit import PrecommitRequest
|
||||
|
||||
from tools.thinkdeep import ThinkDeepRequest
|
||||
|
||||
# Test analyze tool uses prompt
|
||||
analyze = AnalyzeRequest(files=["/test.py"], prompt="What does this do?")
|
||||
assert analyze.prompt == "What does this do?"
|
||||
|
||||
|
||||
# Test debug tool uses prompt
|
||||
debug = DebugIssueRequest(prompt="Error occurred")
|
||||
assert debug.prompt == "Error occurred"
|
||||
|
||||
|
||||
# Test codereview tool uses prompt
|
||||
review = CodeReviewRequest(files=["/test.py"], prompt="Review this")
|
||||
assert review.prompt == "Review this"
|
||||
|
||||
|
||||
# Test thinkdeep tool uses prompt
|
||||
think = ThinkDeepRequest(prompt="My analysis")
|
||||
assert think.prompt == "My analysis"
|
||||
|
||||
|
||||
# Test precommit tool uses prompt (optional)
|
||||
precommit = PrecommitRequest(path="/repo", prompt="Fix bug")
|
||||
assert precommit.prompt == "Fix bug"
|
||||
assert precommit.prompt == "Fix bug"
|
||||
|
||||
@@ -12,11 +12,11 @@ Claude had shared in earlier turns.
|
||||
|
||||
import json
|
||||
from unittest.mock import Mock, patch
|
||||
from tests.mock_helpers import create_mock_provider
|
||||
|
||||
import pytest
|
||||
from pydantic import Field
|
||||
|
||||
from tests.mock_helpers import create_mock_provider
|
||||
from tools.base import BaseTool, ToolRequest
|
||||
from utils.conversation_memory import ConversationTurn, ThreadContext
|
||||
|
||||
@@ -116,7 +116,7 @@ class TestConversationHistoryBugFix:
|
||||
content="Response with conversation context",
|
||||
usage={"input_tokens": 10, "output_tokens": 20, "total_tokens": 30},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={"finish_reason": "STOP"}
|
||||
metadata={"finish_reason": "STOP"},
|
||||
)
|
||||
|
||||
mock_provider.generate_content.side_effect = capture_prompt
|
||||
@@ -176,7 +176,7 @@ class TestConversationHistoryBugFix:
|
||||
content="Response without history",
|
||||
usage={"input_tokens": 10, "output_tokens": 20, "total_tokens": 30},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={"finish_reason": "STOP"}
|
||||
metadata={"finish_reason": "STOP"},
|
||||
)
|
||||
|
||||
mock_provider.generate_content.side_effect = capture_prompt
|
||||
@@ -214,7 +214,7 @@ class TestConversationHistoryBugFix:
|
||||
content="New conversation response",
|
||||
usage={"input_tokens": 10, "output_tokens": 20, "total_tokens": 30},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={"finish_reason": "STOP"}
|
||||
metadata={"finish_reason": "STOP"},
|
||||
)
|
||||
|
||||
mock_provider.generate_content.side_effect = capture_prompt
|
||||
@@ -298,7 +298,7 @@ class TestConversationHistoryBugFix:
|
||||
content="Analysis of new files complete",
|
||||
usage={"input_tokens": 10, "output_tokens": 20, "total_tokens": 30},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={"finish_reason": "STOP"}
|
||||
metadata={"finish_reason": "STOP"},
|
||||
)
|
||||
|
||||
mock_provider.generate_content.side_effect = capture_prompt
|
||||
|
||||
@@ -7,11 +7,11 @@ allowing multi-turn conversations to span multiple tool types.
|
||||
|
||||
import json
|
||||
from unittest.mock import Mock, patch
|
||||
from tests.mock_helpers import create_mock_provider
|
||||
|
||||
import pytest
|
||||
from pydantic import Field
|
||||
|
||||
from tests.mock_helpers import create_mock_provider
|
||||
from tools.base import BaseTool, ToolRequest
|
||||
from utils.conversation_memory import ConversationTurn, ThreadContext
|
||||
|
||||
@@ -117,7 +117,7 @@ class TestCrossToolContinuation:
|
||||
content=content_with_followup,
|
||||
usage={"input_tokens": 10, "output_tokens": 20, "total_tokens": 30},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={"finish_reason": "STOP"}
|
||||
metadata={"finish_reason": "STOP"},
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
@@ -165,7 +165,7 @@ class TestCrossToolContinuation:
|
||||
content="Critical security vulnerability confirmed. The authentication function always returns true, bypassing all security checks.",
|
||||
usage={"input_tokens": 10, "output_tokens": 20, "total_tokens": 30},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={"finish_reason": "STOP"}
|
||||
metadata={"finish_reason": "STOP"},
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
@@ -285,7 +285,7 @@ class TestCrossToolContinuation:
|
||||
content="Security review of auth.py shows vulnerabilities",
|
||||
usage={"input_tokens": 10, "output_tokens": 20, "total_tokens": 30},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={"finish_reason": "STOP"}
|
||||
metadata={"finish_reason": "STOP"},
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import os
|
||||
import shutil
|
||||
import tempfile
|
||||
from unittest.mock import MagicMock, patch
|
||||
from tests.mock_helpers import create_mock_provider
|
||||
|
||||
import pytest
|
||||
from mcp.types import TextContent
|
||||
@@ -77,7 +76,7 @@ class TestLargePromptHandling:
|
||||
content="This is a test response",
|
||||
usage={"input_tokens": 10, "output_tokens": 20, "total_tokens": 30},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={"finish_reason": "STOP"}
|
||||
metadata={"finish_reason": "STOP"},
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
@@ -102,7 +101,7 @@ class TestLargePromptHandling:
|
||||
content="Processed large prompt",
|
||||
usage={"input_tokens": 10, "output_tokens": 20, "total_tokens": 30},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={"finish_reason": "STOP"}
|
||||
metadata={"finish_reason": "STOP"},
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
@@ -214,7 +213,7 @@ class TestLargePromptHandling:
|
||||
content="Success",
|
||||
usage={"input_tokens": 10, "output_tokens": 20, "total_tokens": 30},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={"finish_reason": "STOP"}
|
||||
metadata={"finish_reason": "STOP"},
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
@@ -247,7 +246,7 @@ class TestLargePromptHandling:
|
||||
content="Success",
|
||||
usage={"input_tokens": 10, "output_tokens": 20, "total_tokens": 30},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={"finish_reason": "STOP"}
|
||||
metadata={"finish_reason": "STOP"},
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
@@ -278,7 +277,7 @@ class TestLargePromptHandling:
|
||||
content="Success",
|
||||
usage={"input_tokens": 10, "output_tokens": 20, "total_tokens": 30},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={"finish_reason": "STOP"}
|
||||
metadata={"finish_reason": "STOP"},
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
@@ -300,7 +299,7 @@ class TestLargePromptHandling:
|
||||
content="Success",
|
||||
usage={"input_tokens": 10, "output_tokens": 20, "total_tokens": 30},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={"finish_reason": "STOP"}
|
||||
metadata={"finish_reason": "STOP"},
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
"""
|
||||
Live integration tests for google-genai library
|
||||
These tests require GEMINI_API_KEY to be set and will make real API calls
|
||||
|
||||
To run these tests manually:
|
||||
python tests/test_live_integration.py
|
||||
|
||||
Note: These tests are excluded from regular pytest runs to avoid API rate limits.
|
||||
They confirm that the google-genai library integration works correctly with live data.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
# Add parent directory to path to allow imports
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
import json
|
||||
|
||||
from tools.analyze import AnalyzeTool
|
||||
from tools.thinkdeep import ThinkDeepTool
|
||||
|
||||
|
||||
async def run_manual_live_tests():
|
||||
"""Run live tests manually without pytest"""
|
||||
print("🚀 Running manual live integration tests...")
|
||||
|
||||
# Check API key
|
||||
if not os.environ.get("GEMINI_API_KEY"):
|
||||
print("❌ GEMINI_API_KEY not found. Set it to run live tests.")
|
||||
return False
|
||||
|
||||
try:
|
||||
# Test google-genai import
|
||||
|
||||
print("✅ google-genai library import successful")
|
||||
|
||||
# Test tool integration
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
|
||||
f.write("def hello(): return 'world'")
|
||||
temp_path = f.name
|
||||
|
||||
try:
|
||||
# Test AnalyzeTool
|
||||
tool = AnalyzeTool()
|
||||
result = await tool.execute(
|
||||
{
|
||||
"files": [temp_path],
|
||||
"prompt": "What does this code do?",
|
||||
"thinking_mode": "low",
|
||||
}
|
||||
)
|
||||
|
||||
if result and result[0].text:
|
||||
print("✅ AnalyzeTool live test successful")
|
||||
else:
|
||||
print("❌ AnalyzeTool live test failed")
|
||||
return False
|
||||
|
||||
# Test ThinkDeepTool
|
||||
think_tool = ThinkDeepTool()
|
||||
result = await think_tool.execute(
|
||||
{
|
||||
"prompt": "Testing live integration",
|
||||
"thinking_mode": "minimal", # Fast test
|
||||
}
|
||||
)
|
||||
|
||||
if result and result[0].text and "Extended Analysis" in result[0].text:
|
||||
print("✅ ThinkDeepTool live test successful")
|
||||
else:
|
||||
print("❌ ThinkDeepTool live test failed")
|
||||
return False
|
||||
|
||||
# Test collaboration/clarification request
|
||||
print("\n🔄 Testing dynamic context request (collaboration)...")
|
||||
|
||||
# Create a specific test case designed to trigger clarification
|
||||
# We'll use analyze tool with a question that requires seeing files
|
||||
analyze_tool = AnalyzeTool()
|
||||
|
||||
# Ask about dependencies without providing package files
|
||||
result = await analyze_tool.execute(
|
||||
{
|
||||
"files": [temp_path], # Only Python file, no package.json
|
||||
"prompt": "What npm packages and their versions does this project depend on? List all dependencies.",
|
||||
"thinking_mode": "minimal", # Fast test
|
||||
}
|
||||
)
|
||||
|
||||
if result and result[0].text:
|
||||
response_data = json.loads(result[0].text)
|
||||
print(f" Response status: {response_data['status']}")
|
||||
|
||||
if response_data["status"] == "requires_clarification":
|
||||
print("✅ Dynamic context request successfully triggered!")
|
||||
clarification = json.loads(response_data["content"])
|
||||
print(f" Gemini asks: {clarification.get('question', 'N/A')}")
|
||||
if "files_needed" in clarification:
|
||||
print(f" Files requested: {clarification['files_needed']}")
|
||||
# Verify it's asking for package-related files
|
||||
expected_files = [
|
||||
"package.json",
|
||||
"package-lock.json",
|
||||
"yarn.lock",
|
||||
]
|
||||
if any(f in str(clarification["files_needed"]) for f in expected_files):
|
||||
print(" ✅ Correctly identified missing package files!")
|
||||
else:
|
||||
print(" ⚠️ Unexpected files requested")
|
||||
else:
|
||||
# This is a failure - we specifically designed this to need clarification
|
||||
print("❌ Expected clarification request but got direct response")
|
||||
print(" This suggests the dynamic context feature may not be working")
|
||||
print(" Response:", response_data.get("content", "")[:200])
|
||||
return False
|
||||
else:
|
||||
print("❌ Collaboration test failed - no response")
|
||||
return False
|
||||
|
||||
finally:
|
||||
Path(temp_path).unlink(missing_ok=True)
|
||||
|
||||
print("\n🎉 All manual live tests passed!")
|
||||
print("✅ google-genai library working correctly")
|
||||
print("✅ All tools can make live API calls")
|
||||
print("✅ Thinking modes functioning properly")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Live test failed: {e}")
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Run live tests when script is executed directly
|
||||
success = asyncio.run(run_manual_live_tests())
|
||||
exit(0 if success else 1)
|
||||
@@ -167,9 +167,7 @@ TEMPERATURE_ANALYTICAL = 0.2 # For code review, debugging
|
||||
add_turn(thread_id, "assistant", "First response", files=[config_path], tool_name="precommit")
|
||||
|
||||
# Second request with continuation - should skip already embedded files
|
||||
PrecommitRequest(
|
||||
path=temp_dir, files=[config_path], continuation_id=thread_id, prompt="Follow-up review"
|
||||
)
|
||||
PrecommitRequest(path=temp_dir, files=[config_path], continuation_id=thread_id, prompt="Follow-up review")
|
||||
|
||||
files_to_embed_2 = tool.filter_new_files([config_path], thread_id)
|
||||
assert len(files_to_embed_2) == 0, "Continuation should skip already embedded files"
|
||||
|
||||
@@ -7,7 +7,6 @@ normal-sized prompts after implementing the large prompt handling feature.
|
||||
|
||||
import json
|
||||
from unittest.mock import MagicMock, patch
|
||||
from tests.mock_helpers import create_mock_provider
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -33,7 +32,7 @@ class TestPromptRegression:
|
||||
content=text,
|
||||
usage={"input_tokens": 10, "output_tokens": 20, "total_tokens": 30},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={"finish_reason": "STOP"}
|
||||
metadata={"finish_reason": "STOP"},
|
||||
)
|
||||
|
||||
return _create_response
|
||||
@@ -47,7 +46,9 @@ class TestPromptRegression:
|
||||
mock_provider = MagicMock()
|
||||
mock_provider.get_provider_type.return_value = MagicMock(value="google")
|
||||
mock_provider.supports_thinking_mode.return_value = False
|
||||
mock_provider.generate_content.return_value = mock_model_response("This is a helpful response about Python.")
|
||||
mock_provider.generate_content.return_value = mock_model_response(
|
||||
"This is a helpful response about Python."
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
result = await tool.execute({"prompt": "Explain Python decorators"})
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
"""Tests for the model provider abstraction system"""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock, patch
|
||||
import os
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from providers import ModelProviderRegistry, ModelProvider, ModelResponse, ModelCapabilities
|
||||
from providers import ModelProviderRegistry, ModelResponse
|
||||
from providers.base import ProviderType
|
||||
from providers.gemini import GeminiModelProvider
|
||||
from providers.openai import OpenAIModelProvider
|
||||
@@ -12,56 +11,56 @@ from providers.openai import OpenAIModelProvider
|
||||
|
||||
class TestModelProviderRegistry:
|
||||
"""Test the model provider registry"""
|
||||
|
||||
|
||||
def setup_method(self):
|
||||
"""Clear registry before each test"""
|
||||
ModelProviderRegistry._providers.clear()
|
||||
ModelProviderRegistry._initialized_providers.clear()
|
||||
|
||||
|
||||
def test_register_provider(self):
|
||||
"""Test registering a provider"""
|
||||
ModelProviderRegistry.register_provider(ProviderType.GOOGLE, GeminiModelProvider)
|
||||
|
||||
|
||||
assert ProviderType.GOOGLE in ModelProviderRegistry._providers
|
||||
assert ModelProviderRegistry._providers[ProviderType.GOOGLE] == GeminiModelProvider
|
||||
|
||||
|
||||
@patch.dict(os.environ, {"GEMINI_API_KEY": "test-key"})
|
||||
def test_get_provider(self):
|
||||
"""Test getting a provider instance"""
|
||||
ModelProviderRegistry.register_provider(ProviderType.GOOGLE, GeminiModelProvider)
|
||||
|
||||
|
||||
provider = ModelProviderRegistry.get_provider(ProviderType.GOOGLE)
|
||||
|
||||
|
||||
assert provider is not None
|
||||
assert isinstance(provider, GeminiModelProvider)
|
||||
assert provider.api_key == "test-key"
|
||||
|
||||
|
||||
@patch.dict(os.environ, {}, clear=True)
|
||||
def test_get_provider_no_api_key(self):
|
||||
"""Test getting provider without API key returns None"""
|
||||
ModelProviderRegistry.register_provider(ProviderType.GOOGLE, GeminiModelProvider)
|
||||
|
||||
|
||||
provider = ModelProviderRegistry.get_provider(ProviderType.GOOGLE)
|
||||
|
||||
|
||||
assert provider is None
|
||||
|
||||
|
||||
@patch.dict(os.environ, {"GEMINI_API_KEY": "test-key"})
|
||||
def test_get_provider_for_model(self):
|
||||
"""Test getting provider for a specific model"""
|
||||
ModelProviderRegistry.register_provider(ProviderType.GOOGLE, GeminiModelProvider)
|
||||
|
||||
|
||||
provider = ModelProviderRegistry.get_provider_for_model("gemini-2.0-flash-exp")
|
||||
|
||||
|
||||
assert provider is not None
|
||||
assert isinstance(provider, GeminiModelProvider)
|
||||
|
||||
|
||||
def test_get_available_providers(self):
|
||||
"""Test getting list of available providers"""
|
||||
ModelProviderRegistry.register_provider(ProviderType.GOOGLE, GeminiModelProvider)
|
||||
ModelProviderRegistry.register_provider(ProviderType.OPENAI, OpenAIModelProvider)
|
||||
|
||||
|
||||
providers = ModelProviderRegistry.get_available_providers()
|
||||
|
||||
|
||||
assert len(providers) == 2
|
||||
assert ProviderType.GOOGLE in providers
|
||||
assert ProviderType.OPENAI in providers
|
||||
@@ -69,50 +68,50 @@ class TestModelProviderRegistry:
|
||||
|
||||
class TestGeminiProvider:
|
||||
"""Test Gemini model provider"""
|
||||
|
||||
|
||||
def test_provider_initialization(self):
|
||||
"""Test provider initialization"""
|
||||
provider = GeminiModelProvider(api_key="test-key")
|
||||
|
||||
|
||||
assert provider.api_key == "test-key"
|
||||
assert provider.get_provider_type() == ProviderType.GOOGLE
|
||||
|
||||
|
||||
def test_get_capabilities(self):
|
||||
"""Test getting model capabilities"""
|
||||
provider = GeminiModelProvider(api_key="test-key")
|
||||
|
||||
|
||||
capabilities = provider.get_capabilities("gemini-2.0-flash-exp")
|
||||
|
||||
|
||||
assert capabilities.provider == ProviderType.GOOGLE
|
||||
assert capabilities.model_name == "gemini-2.0-flash-exp"
|
||||
assert capabilities.max_tokens == 1_048_576
|
||||
assert not capabilities.supports_extended_thinking
|
||||
|
||||
|
||||
def test_get_capabilities_pro_model(self):
|
||||
"""Test getting capabilities for Pro model with thinking support"""
|
||||
provider = GeminiModelProvider(api_key="test-key")
|
||||
|
||||
|
||||
capabilities = provider.get_capabilities("gemini-2.5-pro-preview-06-05")
|
||||
|
||||
|
||||
assert capabilities.supports_extended_thinking
|
||||
|
||||
|
||||
def test_model_shorthand_resolution(self):
|
||||
"""Test model shorthand resolution"""
|
||||
provider = GeminiModelProvider(api_key="test-key")
|
||||
|
||||
|
||||
assert provider.validate_model_name("flash")
|
||||
assert provider.validate_model_name("pro")
|
||||
|
||||
|
||||
capabilities = provider.get_capabilities("flash")
|
||||
assert capabilities.model_name == "gemini-2.0-flash-exp"
|
||||
|
||||
|
||||
def test_supports_thinking_mode(self):
|
||||
"""Test thinking mode support detection"""
|
||||
provider = GeminiModelProvider(api_key="test-key")
|
||||
|
||||
|
||||
assert not provider.supports_thinking_mode("gemini-2.0-flash-exp")
|
||||
assert provider.supports_thinking_mode("gemini-2.5-pro-preview-06-05")
|
||||
|
||||
|
||||
@patch("google.genai.Client")
|
||||
def test_generate_content(self, mock_client_class):
|
||||
"""Test content generation"""
|
||||
@@ -131,15 +130,11 @@ class TestGeminiProvider:
|
||||
mock_response.usage_metadata = mock_usage
|
||||
mock_client.models.generate_content.return_value = mock_response
|
||||
mock_client_class.return_value = mock_client
|
||||
|
||||
|
||||
provider = GeminiModelProvider(api_key="test-key")
|
||||
|
||||
response = provider.generate_content(
|
||||
prompt="Test prompt",
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
temperature=0.7
|
||||
)
|
||||
|
||||
|
||||
response = provider.generate_content(prompt="Test prompt", model_name="gemini-2.0-flash-exp", temperature=0.7)
|
||||
|
||||
assert isinstance(response, ModelResponse)
|
||||
assert response.content == "Generated content"
|
||||
assert response.model_name == "gemini-2.0-flash-exp"
|
||||
@@ -151,38 +146,38 @@ class TestGeminiProvider:
|
||||
|
||||
class TestOpenAIProvider:
|
||||
"""Test OpenAI model provider"""
|
||||
|
||||
|
||||
def test_provider_initialization(self):
|
||||
"""Test provider initialization"""
|
||||
provider = OpenAIModelProvider(api_key="test-key", organization="test-org")
|
||||
|
||||
|
||||
assert provider.api_key == "test-key"
|
||||
assert provider.organization == "test-org"
|
||||
assert provider.get_provider_type() == ProviderType.OPENAI
|
||||
|
||||
|
||||
def test_get_capabilities_o3(self):
|
||||
"""Test getting O3 model capabilities"""
|
||||
provider = OpenAIModelProvider(api_key="test-key")
|
||||
|
||||
|
||||
capabilities = provider.get_capabilities("o3-mini")
|
||||
|
||||
|
||||
assert capabilities.provider == ProviderType.OPENAI
|
||||
assert capabilities.model_name == "o3-mini"
|
||||
assert capabilities.max_tokens == 200_000
|
||||
assert not capabilities.supports_extended_thinking
|
||||
|
||||
|
||||
def test_validate_model_names(self):
|
||||
"""Test model name validation"""
|
||||
provider = OpenAIModelProvider(api_key="test-key")
|
||||
|
||||
|
||||
assert provider.validate_model_name("o3")
|
||||
assert provider.validate_model_name("o3-mini")
|
||||
assert not provider.validate_model_name("gpt-4o")
|
||||
assert not provider.validate_model_name("invalid-model")
|
||||
|
||||
|
||||
def test_no_thinking_mode_support(self):
|
||||
"""Test that no OpenAI models support thinking mode"""
|
||||
provider = OpenAIModelProvider(api_key="test-key")
|
||||
|
||||
|
||||
assert not provider.supports_thinking_mode("o3")
|
||||
assert not provider.supports_thinking_mode("o3-mini")
|
||||
assert not provider.supports_thinking_mode("o3-mini")
|
||||
|
||||
@@ -3,11 +3,11 @@ Tests for the main server functionality
|
||||
"""
|
||||
|
||||
from unittest.mock import Mock, patch
|
||||
from tests.mock_helpers import create_mock_provider
|
||||
|
||||
import pytest
|
||||
|
||||
from server import handle_call_tool, handle_list_tools
|
||||
from tests.mock_helpers import create_mock_provider
|
||||
|
||||
|
||||
class TestServerTools:
|
||||
@@ -56,10 +56,7 @@ class TestServerTools:
|
||||
mock_provider.get_provider_type.return_value = Mock(value="google")
|
||||
mock_provider.supports_thinking_mode.return_value = False
|
||||
mock_provider.generate_content.return_value = Mock(
|
||||
content="Chat response",
|
||||
usage={},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={}
|
||||
content="Chat response", usage={}, model_name="gemini-2.0-flash-exp", metadata={}
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
@@ -81,6 +78,6 @@ class TestServerTools:
|
||||
assert len(result) == 1
|
||||
|
||||
response = result[0].text
|
||||
assert "Gemini MCP Server v" in response # Version agnostic check
|
||||
assert "Zen MCP Server v" in response # Version agnostic check
|
||||
assert "Available Tools:" in response
|
||||
assert "thinkdeep" in response
|
||||
|
||||
@@ -3,10 +3,10 @@ Tests for thinking_mode functionality across all tools
|
||||
"""
|
||||
|
||||
from unittest.mock import Mock, patch
|
||||
from tests.mock_helpers import create_mock_provider
|
||||
|
||||
import pytest
|
||||
|
||||
from tests.mock_helpers import create_mock_provider
|
||||
from tools.analyze import AnalyzeTool
|
||||
from tools.codereview import CodeReviewTool
|
||||
from tools.debug import DebugIssueTool
|
||||
@@ -45,10 +45,7 @@ class TestThinkingModes:
|
||||
mock_provider.get_provider_type.return_value = Mock(value="google")
|
||||
mock_provider.supports_thinking_mode.return_value = True
|
||||
mock_provider.generate_content.return_value = Mock(
|
||||
content="Minimal thinking response",
|
||||
usage={},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={}
|
||||
content="Minimal thinking response", usage={}, model_name="gemini-2.0-flash-exp", metadata={}
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
@@ -66,7 +63,9 @@ class TestThinkingModes:
|
||||
# Verify generate_content was called with thinking_mode
|
||||
mock_provider.generate_content.assert_called_once()
|
||||
call_kwargs = mock_provider.generate_content.call_args[1]
|
||||
assert call_kwargs.get("thinking_mode") == "minimal" or (not mock_provider.supports_thinking_mode.return_value and call_kwargs.get("thinking_mode") is None) # thinking_mode parameter
|
||||
assert call_kwargs.get("thinking_mode") == "minimal" or (
|
||||
not mock_provider.supports_thinking_mode.return_value and call_kwargs.get("thinking_mode") is None
|
||||
) # thinking_mode parameter
|
||||
|
||||
# Parse JSON response
|
||||
import json
|
||||
@@ -83,10 +82,7 @@ class TestThinkingModes:
|
||||
mock_provider.get_provider_type.return_value = Mock(value="google")
|
||||
mock_provider.supports_thinking_mode.return_value = True
|
||||
mock_provider.generate_content.return_value = Mock(
|
||||
content="Low thinking response",
|
||||
usage={},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={}
|
||||
content="Low thinking response", usage={}, model_name="gemini-2.0-flash-exp", metadata={}
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
@@ -104,7 +100,9 @@ class TestThinkingModes:
|
||||
# Verify generate_content was called with thinking_mode
|
||||
mock_provider.generate_content.assert_called_once()
|
||||
call_kwargs = mock_provider.generate_content.call_args[1]
|
||||
assert call_kwargs.get("thinking_mode") == "low" or (not mock_provider.supports_thinking_mode.return_value and call_kwargs.get("thinking_mode") is None)
|
||||
assert call_kwargs.get("thinking_mode") == "low" or (
|
||||
not mock_provider.supports_thinking_mode.return_value and call_kwargs.get("thinking_mode") is None
|
||||
)
|
||||
|
||||
assert "Code Review" in result[0].text
|
||||
|
||||
@@ -116,10 +114,7 @@ class TestThinkingModes:
|
||||
mock_provider.get_provider_type.return_value = Mock(value="google")
|
||||
mock_provider.supports_thinking_mode.return_value = True
|
||||
mock_provider.generate_content.return_value = Mock(
|
||||
content="Medium thinking response",
|
||||
usage={},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={}
|
||||
content="Medium thinking response", usage={}, model_name="gemini-2.0-flash-exp", metadata={}
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
@@ -136,7 +131,9 @@ class TestThinkingModes:
|
||||
# Verify generate_content was called with thinking_mode
|
||||
mock_provider.generate_content.assert_called_once()
|
||||
call_kwargs = mock_provider.generate_content.call_args[1]
|
||||
assert call_kwargs.get("thinking_mode") == "medium" or (not mock_provider.supports_thinking_mode.return_value and call_kwargs.get("thinking_mode") is None)
|
||||
assert call_kwargs.get("thinking_mode") == "medium" or (
|
||||
not mock_provider.supports_thinking_mode.return_value and call_kwargs.get("thinking_mode") is None
|
||||
)
|
||||
|
||||
assert "Debug Analysis" in result[0].text
|
||||
|
||||
@@ -148,10 +145,7 @@ class TestThinkingModes:
|
||||
mock_provider.get_provider_type.return_value = Mock(value="google")
|
||||
mock_provider.supports_thinking_mode.return_value = True
|
||||
mock_provider.generate_content.return_value = Mock(
|
||||
content="High thinking response",
|
||||
usage={},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={}
|
||||
content="High thinking response", usage={}, model_name="gemini-2.0-flash-exp", metadata={}
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
@@ -169,7 +163,9 @@ class TestThinkingModes:
|
||||
# Verify generate_content was called with thinking_mode
|
||||
mock_provider.generate_content.assert_called_once()
|
||||
call_kwargs = mock_provider.generate_content.call_args[1]
|
||||
assert call_kwargs.get("thinking_mode") == "high" or (not mock_provider.supports_thinking_mode.return_value and call_kwargs.get("thinking_mode") is None)
|
||||
assert call_kwargs.get("thinking_mode") == "high" or (
|
||||
not mock_provider.supports_thinking_mode.return_value and call_kwargs.get("thinking_mode") is None
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch("tools.base.BaseTool.get_model_provider")
|
||||
@@ -179,10 +175,7 @@ class TestThinkingModes:
|
||||
mock_provider.get_provider_type.return_value = Mock(value="google")
|
||||
mock_provider.supports_thinking_mode.return_value = True
|
||||
mock_provider.generate_content.return_value = Mock(
|
||||
content="Max thinking response",
|
||||
usage={},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={}
|
||||
content="Max thinking response", usage={}, model_name="gemini-2.0-flash-exp", metadata={}
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
@@ -199,7 +192,9 @@ class TestThinkingModes:
|
||||
# Verify generate_content was called with thinking_mode
|
||||
mock_provider.generate_content.assert_called_once()
|
||||
call_kwargs = mock_provider.generate_content.call_args[1]
|
||||
assert call_kwargs.get("thinking_mode") == "high" or (not mock_provider.supports_thinking_mode.return_value and call_kwargs.get("thinking_mode") is None)
|
||||
assert call_kwargs.get("thinking_mode") == "high" or (
|
||||
not mock_provider.supports_thinking_mode.return_value and call_kwargs.get("thinking_mode") is None
|
||||
)
|
||||
|
||||
assert "Extended Analysis by Gemini" in result[0].text
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@ Tests for individual tool implementations
|
||||
|
||||
import json
|
||||
from unittest.mock import Mock, patch
|
||||
from tests.mock_helpers import create_mock_provider
|
||||
|
||||
import pytest
|
||||
|
||||
from tests.mock_helpers import create_mock_provider
|
||||
from tools import AnalyzeTool, ChatTool, CodeReviewTool, DebugIssueTool, ThinkDeepTool
|
||||
|
||||
|
||||
@@ -37,10 +37,7 @@ class TestThinkDeepTool:
|
||||
mock_provider.get_provider_type.return_value = Mock(value="google")
|
||||
mock_provider.supports_thinking_mode.return_value = True
|
||||
mock_provider.generate_content.return_value = Mock(
|
||||
content="Extended analysis",
|
||||
usage={},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={}
|
||||
content="Extended analysis", usage={}, model_name="gemini-2.0-flash-exp", metadata={}
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
@@ -91,10 +88,7 @@ class TestCodeReviewTool:
|
||||
mock_provider.get_provider_type.return_value = Mock(value="google")
|
||||
mock_provider.supports_thinking_mode.return_value = False
|
||||
mock_provider.generate_content.return_value = Mock(
|
||||
content="Security issues found",
|
||||
usage={},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={}
|
||||
content="Security issues found", usage={}, model_name="gemini-2.0-flash-exp", metadata={}
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
@@ -139,10 +133,7 @@ class TestDebugIssueTool:
|
||||
mock_provider.get_provider_type.return_value = Mock(value="google")
|
||||
mock_provider.supports_thinking_mode.return_value = False
|
||||
mock_provider.generate_content.return_value = Mock(
|
||||
content="Root cause: race condition",
|
||||
usage={},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={}
|
||||
content="Root cause: race condition", usage={}, model_name="gemini-2.0-flash-exp", metadata={}
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
@@ -190,10 +181,7 @@ class TestAnalyzeTool:
|
||||
mock_provider.get_provider_type.return_value = Mock(value="google")
|
||||
mock_provider.supports_thinking_mode.return_value = False
|
||||
mock_provider.generate_content.return_value = Mock(
|
||||
content="Architecture analysis",
|
||||
usage={},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={}
|
||||
content="Architecture analysis", usage={}, model_name="gemini-2.0-flash-exp", metadata={}
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
@@ -307,10 +295,7 @@ class TestAbsolutePathValidation:
|
||||
mock_provider.get_provider_type.return_value = Mock(value="google")
|
||||
mock_provider.supports_thinking_mode.return_value = False
|
||||
mock_provider.generate_content.return_value = Mock(
|
||||
content="Analysis complete",
|
||||
usage={},
|
||||
model_name="gemini-2.0-flash-exp",
|
||||
metadata={}
|
||||
content="Analysis complete", usage={}, model_name="gemini-2.0-flash-exp", metadata={}
|
||||
)
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
|
||||
Reference in New Issue
Block a user