Skip to main content
Glama
test_aspect_ratio.py9.27 kB
""" Tests for aspect ratio functionality. This module tests the aspect ratio feature added in PR #3, including: - Parameter validation - API integration - Metadata tracking - Edge cases """ import pytest from unittest.mock import Mock, patch, MagicMock from google.genai import types as gx from nanobanana_mcp_server.services.gemini_client import GeminiClient from nanobanana_mcp_server.config.settings import ServerConfig, GeminiConfig # Supported aspect ratios according to Gemini API docs SUPPORTED_ASPECT_RATIOS = [ "1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9" ] class TestAspectRatioValidation: """Test aspect ratio parameter validation.""" @pytest.mark.parametrize("ratio", SUPPORTED_ASPECT_RATIOS) def test_valid_aspect_ratios(self, ratio): """Test that all supported aspect ratios are accepted.""" # This tests the Literal type constraint in generate_image.py # The Pydantic validation should accept these values assert ratio in SUPPORTED_ASPECT_RATIOS def test_aspect_ratio_literal_type_constraint(self): """Verify the tool parameter uses Literal type for type safety.""" from nanobanana_mcp_server.tools.generate_image import register_generate_image_tool from fastmcp import FastMCP # This test ensures the Literal constraint is in place # If it's not, the type system won't catch invalid values server = FastMCP("test") register_generate_image_tool(server) # Get the tool function tool_func = server._tools[0] # Check that aspect_ratio parameter exists import inspect sig = inspect.signature(tool_func.fn) assert 'aspect_ratio' in sig.parameters class TestGeminiClientAspectRatio: """Test GeminiClient aspect ratio integration.""" @pytest.fixture def mock_config(self): """Create mock configuration.""" server_config = ServerConfig(gemini_api_key="test-key") gemini_config = GeminiConfig() return server_config, gemini_config @pytest.fixture def gemini_client(self, mock_config): """Create GeminiClient with mocked dependencies.""" server_config, gemini_config = mock_config client = GeminiClient(server_config, gemini_config) # Mock the underlying genai client client._client = Mock() client._client.models = Mock() client._client.models.generate_content = Mock() return client def test_aspect_ratio_creates_image_config(self, gemini_client): """Test that aspect_ratio parameter creates ImageConfig.""" with patch('nanobanana_mcp_server.services.gemini_client.gx') as mock_gx: # Setup mocks mock_image_config = Mock() mock_gx.ImageConfig.return_value = mock_image_config mock_gx.GenerateContentConfig = Mock() # Call generate_content with aspect_ratio gemini_client.generate_content( contents=["test prompt"], aspect_ratio="16:9" ) # Verify ImageConfig was called with aspect_ratio mock_gx.ImageConfig.assert_called_once_with(aspect_ratio="16:9") def test_aspect_ratio_none_skips_image_config(self, gemini_client): """Test that aspect_ratio=None doesn't create ImageConfig.""" with patch('nanobanana_mcp_server.services.gemini_client.gx') as mock_gx: mock_gx.GenerateContentConfig = Mock() # Call without aspect_ratio gemini_client.generate_content(contents=["test prompt"]) # Verify ImageConfig was not called mock_gx.ImageConfig.assert_not_called() def test_config_conflict_warning(self, gemini_client, caplog): """Test warning when both config kwarg and aspect_ratio are provided.""" import logging caplog.set_level(logging.WARNING) # Provide both config kwarg and aspect_ratio custom_config = Mock(spec=gx.GenerateContentConfig) gemini_client.generate_content( contents=["test"], aspect_ratio="16:9", config=custom_config ) # Verify warning was logged assert any("ignoring aspect_ratio" in record.message.lower() for record in caplog.records) def test_response_modalities_forced_to_image(self, gemini_client): """Test that response_modalities is always set to ['Image'].""" with patch('nanobanana_mcp_server.services.gemini_client.gx') as mock_gx: mock_gx.ImageConfig = Mock() mock_gx.GenerateContentConfig = Mock() gemini_client.generate_content( contents=["test"], aspect_ratio="16:9" ) # Check that GenerateContentConfig was called with response_modalities call_kwargs = mock_gx.GenerateContentConfig.call_args[1] assert call_kwargs.get('response_modalities') == ['Image'] class TestAspectRatioMetadata: """Test aspect ratio in metadata tracking.""" def test_aspect_ratio_in_generation_metadata(self): """Test that aspect_ratio appears in generation metadata.""" # This would need integration with actual services # For now, verify the metadata structure metadata = { "prompt": "test image", "negative_prompt": None, "system_instruction": None, "aspect_ratio": "16:9", } assert "aspect_ratio" in metadata assert metadata["aspect_ratio"] == "16:9" def test_aspect_ratio_none_in_metadata(self): """Test that aspect_ratio=None is properly tracked.""" metadata = { "prompt": "test image", "aspect_ratio": None, } assert "aspect_ratio" in metadata assert metadata["aspect_ratio"] is None class TestAspectRatioEdgeCases: """Test edge cases and error conditions.""" def test_aspect_ratio_with_edit_mode(self): """Test aspect ratio behavior in edit mode.""" # Currently aspect_ratio only works in generate mode # This test documents the current behavior # If edit mode support is added, update this test pass # Document-only test for now def test_aspect_ratio_with_multiple_images(self): """Test aspect ratio when generating multiple images.""" # All generated images should have the same aspect ratio # This would require integration testing with real API pass # Integration test placeholder def test_aspect_ratio_with_input_images(self): """Test aspect ratio with image conditioning.""" # Aspect ratio should apply to output, not input pass # Integration test placeholder class TestAspectRatioIntegration: """Integration tests for aspect ratio feature. Note: These tests are marked as 'integration' and require actual API access. They are skipped by default in CI/CD but can be run manually with: pytest -m integration """ @pytest.mark.integration @pytest.mark.skip(reason="Requires Gemini API access and costs money") def test_generate_with_16_9_aspect_ratio(self): """Integration test: Generate image with 16:9 aspect ratio.""" # This would test actual API call # from nanobanana_mcp_server.tools.generate_image import generate_image # result = generate_image(prompt="test", aspect_ratio="16:9") # assert result is not None pass @pytest.mark.integration @pytest.mark.skip(reason="Requires Gemini API access and costs money") @pytest.mark.parametrize("ratio", SUPPORTED_ASPECT_RATIOS) def test_all_aspect_ratios_work(self, ratio): """Integration test: Verify all aspect ratios work with real API.""" pass class TestAspectRatioServicePropagation: """Test that aspect_ratio propagates through service layers.""" def test_enhanced_image_service_accepts_aspect_ratio(self): """Test EnhancedImageService.generate_images accepts aspect_ratio.""" from nanobanana_mcp_server.services.enhanced_image_service import EnhancedImageService import inspect # Check method signature sig = inspect.signature(EnhancedImageService.generate_images) assert 'aspect_ratio' in sig.parameters def test_file_image_service_accepts_aspect_ratio(self): """Test FileImageService.generate_images accepts aspect_ratio.""" from nanobanana_mcp_server.services.file_image_service import FileImageService import inspect sig = inspect.signature(FileImageService.generate_images) assert 'aspect_ratio' in sig.parameters def test_image_service_accepts_aspect_ratio(self): """Test ImageService.generate_images accepts aspect_ratio.""" from nanobanana_mcp_server.services.image_service import ImageService import inspect sig = inspect.signature(ImageService.generate_images) assert 'aspect_ratio' in sig.parameters # Test configuration pytest_plugins = [] # Add any required plugins # Mark integration tests pytestmark = pytest.mark.unit # Default mark for this module

Latest Blog Posts

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/zhongweili/nanobanana-mcp-server'

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