Skip to main content
Glama
test_config.py8.61 kB
""" Property-based tests for configuration loading. **Feature: service-layer-refactoring** **Validates: Requirements 9.3** This module tests: - Property 13: Configuration Loading """ import os from unittest.mock import patch from hypothesis import HealthCheck, given, settings from hypothesis import strategies as st import pytest from banana_image_mcp.config.settings import ( BaseModelConfig, FlashImageConfig, ModelSelectionConfig, ModelTier, ProImageConfig, ServerConfig, ) # ============================================================================= # Property 13: Configuration Loading # ============================================================================= class TestConfigurationLoading: """ **Feature: service-layer-refactoring, Property 13: Configuration Loading** *For any* environment state, `ServerConfig.from_env()` SHALL either: - Return a valid ServerConfig when GEMINI_API_KEY or GOOGLE_API_KEY is set - Raise ValueError when neither API key is set """ def test_from_env_with_gemini_api_key(self, tmp_path): """ **Feature: service-layer-refactoring, Property 13: Configuration Loading** Should return valid config when GEMINI_API_KEY is set. """ with patch.dict( os.environ, { "GEMINI_API_KEY": "test-api-key-12345", "IMAGE_OUTPUT_DIR": str(tmp_path), }, clear=False, ): config = ServerConfig.from_env() assert config.gemini_api_key == "test-api-key-12345" assert config.server_name == "banana-image-mcp" def test_from_env_with_google_api_key(self, tmp_path): """ **Feature: service-layer-refactoring, Property 13: Configuration Loading** Should return valid config when GOOGLE_API_KEY is set. """ with patch.dict( os.environ, { "GOOGLE_API_KEY": "google-api-key-67890", "IMAGE_OUTPUT_DIR": str(tmp_path), }, clear=False, ): # Remove GEMINI_API_KEY if present env = os.environ.copy() env.pop("GEMINI_API_KEY", None) with patch.dict(os.environ, env, clear=True): os.environ["GOOGLE_API_KEY"] = "google-api-key-67890" os.environ["IMAGE_OUTPUT_DIR"] = str(tmp_path) config = ServerConfig.from_env() assert config.gemini_api_key == "google-api-key-67890" def test_from_env_without_api_key_raises_error(self, tmp_path): """ **Feature: service-layer-refactoring, Property 13: Configuration Loading** Should raise ValueError when neither API key is set. """ with patch.dict( os.environ, { "IMAGE_OUTPUT_DIR": str(tmp_path), }, clear=True, ): with pytest.raises(ValueError) as exc_info: ServerConfig.from_env() assert "GEMINI_API_KEY" in str(exc_info.value) assert "GOOGLE_API_KEY" in str(exc_info.value) def test_from_env_creates_output_directory(self, tmp_path): """ **Feature: service-layer-refactoring, Property 13: Configuration Loading** Should create output directory if it doesn't exist. """ output_dir = tmp_path / "new_output_dir" assert not output_dir.exists() with patch.dict( os.environ, { "GEMINI_API_KEY": "test-key", "IMAGE_OUTPUT_DIR": str(output_dir), }, clear=False, ): config = ServerConfig.from_env() assert output_dir.exists() assert config.image_output_dir == str(output_dir) @given( transport=st.sampled_from(["stdio", "http"]), port=st.integers(min_value=1024, max_value=65535), ) @settings( max_examples=20, suppress_health_check=[HealthCheck.function_scoped_fixture], ) def test_from_env_respects_transport_settings(self, tmp_path, transport: str, port: int): """ **Feature: service-layer-refactoring, Property 13: Configuration Loading** Should respect transport and port settings from environment. """ with patch.dict( os.environ, { "GEMINI_API_KEY": "test-key", "IMAGE_OUTPUT_DIR": str(tmp_path), "FASTMCP_TRANSPORT": transport, "FASTMCP_PORT": str(port), }, clear=False, ): config = ServerConfig.from_env() assert config.transport == transport assert config.port == port class TestBaseModelConfigInheritance: """Test that model configs properly inherit from BaseModelConfig.""" def test_flash_config_inherits_base_fields(self): """ **Feature: service-layer-refactoring, Property 13: Configuration Loading** FlashImageConfig should inherit BaseModelConfig fields. """ config = FlashImageConfig() # Base fields assert config.max_images_per_request == 4 assert config.max_inline_image_size == 20 * 1024 * 1024 assert config.default_image_format == "png" assert config.request_timeout == 60 # Flash-specific fields assert config.model_name == "gemini-2.5-flash-image" assert config.max_resolution == 1024 assert config.supports_thinking is False def test_pro_config_inherits_base_fields(self): """ **Feature: service-layer-refactoring, Property 13: Configuration Loading** ProImageConfig should inherit BaseModelConfig fields. """ config = ProImageConfig() # Base fields (with Pro override for timeout) assert config.max_images_per_request == 4 assert config.max_inline_image_size == 20 * 1024 * 1024 assert config.default_image_format == "png" assert config.request_timeout == 90 # Pro has longer timeout # Pro-specific fields assert config.model_name == "gemini-3-pro-image-preview" assert config.max_resolution == 3840 assert config.supports_thinking is True def test_flash_config_is_instance_of_base(self): """FlashImageConfig should be instance of BaseModelConfig.""" config = FlashImageConfig() assert isinstance(config, BaseModelConfig) def test_pro_config_is_instance_of_base(self): """ProImageConfig should be instance of BaseModelConfig.""" config = ProImageConfig() assert isinstance(config, BaseModelConfig) class TestModelSelectionConfigLoading: """Test ModelSelectionConfig loading from environment.""" def test_from_env_default_tier(self): """ **Feature: service-layer-refactoring, Property 13: Configuration Loading** Should use AUTO tier by default. """ with patch.dict(os.environ, {}, clear=True): config = ModelSelectionConfig.from_env() assert config.default_tier == ModelTier.AUTO @given(tier=st.sampled_from(["flash", "pro", "auto"])) @settings(max_examples=10) def test_from_env_respects_model_tier(self, tier: str): """ **Feature: service-layer-refactoring, Property 13: Configuration Loading** Should respect NANOBANANA_MODEL environment variable. """ with patch.dict(os.environ, {"NANOBANANA_MODEL": tier}, clear=True): config = ModelSelectionConfig.from_env() assert config.default_tier == ModelTier(tier) def test_from_env_invalid_tier_defaults_to_auto(self): """ **Feature: service-layer-refactoring, Property 13: Configuration Loading** Should default to AUTO for invalid tier values. """ with patch.dict(os.environ, {"NANOBANANA_MODEL": "invalid"}, clear=True): config = ModelSelectionConfig.from_env() assert config.default_tier == ModelTier.AUTO def test_default_keywords_present(self): """ **Feature: service-layer-refactoring, Property 13: Configuration Loading** Should have default quality and speed keywords. """ config = ModelSelectionConfig() assert len(config.auto_quality_keywords) > 0 assert len(config.auto_speed_keywords) > 0 assert "4k" in config.auto_quality_keywords assert "quick" in config.auto_speed_keywords

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/zengwenliang416/banana-image-mcp'

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