Skip to main content
Glama
test_core.py13.5 kB
"""Unit tests for core image generation utilities.""" # pylint: disable=missing-function-docstring import base64 import os import tempfile import unittest from datetime import datetime, timezone from pathlib import Path from unittest.mock import patch from image_generator import core class ModelFallbackTests(unittest.TestCase): """Validate model selection fallback order.""" def setUp(self): # Clear env for each test and reset model state self.env_patch = patch.dict(os.environ, {}, clear=True) self.env_patch.start() core._state._model_id = None # type: ignore[attr-defined] # pylint: disable=protected-access def tearDown(self): core._state._model_id = None # type: ignore[attr-defined] # pylint: disable=protected-access self.env_patch.stop() def test_default_model_used_when_none_configured(self): self.assertEqual(core.get_current_model(), core.DEFAULT_MODEL_ID) def test_env_overrides_default(self): os.environ["IMAGEN_MODEL_ID"] = "env-model" self.assertEqual(core.get_current_model(), "env-model") def test_runtime_overrides_env(self): os.environ["IMAGEN_MODEL_ID"] = "env-model" core.set_current_model("runtime-model") self.assertEqual(core.get_current_model(), "runtime-model") class ImageGenerationTests(unittest.TestCase): """Unit tests for generation and editing helpers.""" def setUp(self): self.env_patch = patch.dict(os.environ, {core.API_KEY_ENV: "test-key"}, clear=True) self.env_patch.start() core._state._model_id = None # type: ignore[attr-defined] # pylint: disable=protected-access def tearDown(self): core._state._model_id = None # type: ignore[attr-defined] # pylint: disable=protected-access self.env_patch.stop() def _inline_response(self, mime_type: str, data_b64: str): return { "candidates": [ { "content": { "parts": [ { "inlineData": { "mimeType": mime_type, "data": data_b64, } } ] } } ] } @patch("image_generator.core._http_post_json") def test_generate_image_uses_explicit_model(self, mock_post): data_b64 = base64.b64encode(b"pngdata").decode("utf-8") mock_post.return_value = self._inline_response("image/png", data_b64) result = core.generate_image(prompt="hello", model_id="explicit-model") called_url = mock_post.call_args[0][0] self.assertTrue(called_url.endswith("explicit-model:generateContent")) self.assertEqual(result.mime_type, "image/png") self.assertEqual(result.buffer, base64.b64decode(data_b64)) @patch("image_generator.core._http_post_json") def test_edit_image_sends_prompt_and_image(self, mock_post): data_b64 = base64.b64encode(b"pngdata").decode("utf-8") mock_post.return_value = self._inline_response("image/png", data_b64) image_b64 = base64.b64encode(b"imgbytes").decode("utf-8") result = core.edit_image( prompt="make it green", image_data=image_b64, image_mime_type="image/png", model_id="edit-model", ) called_url, payload, _ = mock_post.call_args[0] self.assertTrue(called_url.endswith("edit-model:generateContent")) # Ensure payload contains prompt then inline image in order parts = payload["contents"][0]["parts"] self.assertEqual(parts[0]["text"], "make it green") self.assertIn("inlineData", parts[1]) self.assertEqual(parts[1]["inlineData"]["data"], image_b64) self.assertEqual(result.buffer, base64.b64decode(data_b64)) def test_read_image_file_validates_extension(self): with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp: tmp.write(b"data") path = tmp.name try: data_b64, mime = core.read_image_file(path) self.assertEqual(mime, "image/png") self.assertEqual(base64.b64decode(data_b64), b"data") finally: os.remove(path) def test_read_image_file_unsupported_extension(self): with tempfile.NamedTemporaryFile(suffix=".txt", delete=False) as tmp: tmp.write(b"data") path = tmp.name try: with self.assertRaises(ValueError): core.read_image_file(path) finally: os.remove(path) def test_read_image_file_missing(self): with self.assertRaises(FileNotFoundError): core.read_image_file("/nonexistent/image.png") @unittest.skipUnless(os.getenv(core.API_KEY_ENV), "GOOGLE_AI_API_KEY not set; integration test skipped") class ImageEditingIntegrationTests(unittest.TestCase): """Integration tests that hit the live Gemini image APIs.""" red_apple_path: Path | None = None green_apple_path: Path | None = None def _out_dir(self) -> Path: out_dir = Path("test_output") out_dir.mkdir(exist_ok=True) return out_dir def _ts(self) -> str: # Include microseconds to avoid collisions across fast test runs. return datetime.now(timezone.utc).strftime("%Y%m%d%H%M%S%f") def _png_size(self, buf: bytes): sig = b"\x89PNG\r\n\x1a\n" if not buf.startswith(sig) or len(buf) < 24: return None width = int.from_bytes(buf[16:20], "big") height = int.from_bytes(buf[20:24], "big") return width, height def _choose_model(self): models = core.list_available_models(image_only=True) target = next((m.name for m in models if m.name == "gemini-3-pro-image-preview"), None) return target or (models[0].name if models else None) def test_01_generate_and_edit_apple_color_only(self): model = self._choose_model() self.assertIsNotNone(model, "No image model available for integration test") core.set_current_model(model) out_dir = self._out_dir() ts = self._ts() # Generate red apple gen = core.generate_image( prompt="A photorealistic red apple on a white background, simple studio lighting" ) self.assertGreater(len(gen.buffer), 0) red_png_size = self._png_size(gen.buffer) red_path = out_dir / f"red_apple_{ts}.png" core.write_image_to_file(gen.buffer, red_path) self.assertTrue(red_path.exists()) self.__class__.red_apple_path = red_path # Edit apple to green without altering other attributes image_b64 = base64.b64encode(gen.buffer).decode("utf-8") edited = core.edit_image( prompt=("Change the apple to a green apple. Keep shape, size, texture, position, lighting, and background" " identical; only change the color of the apple."), image_data=image_b64, image_mime_type=gen.mime_type, ) self.assertGreater(len(edited.buffer), 0) self.assertEqual(edited.mime_type, gen.mime_type) self.assertNotEqual(edited.buffer, gen.buffer) green_path = out_dir / f"green_apple_{ts}.png" core.write_image_to_file(edited.buffer, green_path) self.assertTrue(green_path.exists()) self.__class__.green_apple_path = green_path green_png_size = self._png_size(edited.buffer) if red_png_size and green_png_size: self.assertEqual(red_png_size, green_png_size) def test_02_generate_with_reference_apple_held_by_man(self): model = self._choose_model() self.assertIsNotNone(model, "No image model available for integration test") core.set_current_model(model) out_dir = self._out_dir() ts = self._ts() ref_path = self.__class__.red_apple_path if not ref_path or not ref_path.exists(): gen = core.generate_image( prompt="A photorealistic red apple on a white background, simple studio lighting" ) ref_path = out_dir / f"red_apple_{ts}.png" core.write_image_to_file(gen.buffer, ref_path) self.assertTrue(ref_path.exists()) self.__class__.red_apple_path = ref_path ref_b64, ref_mime = core.read_image_file(ref_path) prompt = ( "Generate a photorealistic image of a man holding the exact same red apple from the reference image. " "The apple must be identical in shape, size, texture, stem, lighting, highlights, shadows, and color; " "do not alter any details of the apple. Keep the apple exactly as-is; only add the man and his hand " "holding it naturally. Use a clean, simple studio background." ) out = core.generate_image_with_references( prompt=prompt, reference_images=[(ref_b64, ref_mime)], ) self.assertGreater(len(out.buffer), 0) out_path = out_dir / f"man_holding_same_apple_{ts}.png" core.write_image_to_file(out.buffer, out_path) self.assertTrue(out_path.exists()) def test_03_generate_with_multiple_references_two_apples_held_by_man(self): model = self._choose_model() self.assertIsNotNone(model, "No image model available for integration test") core.set_current_model(model) out_dir = self._out_dir() ts = self._ts() red_path = self.__class__.red_apple_path green_path = self.__class__.green_apple_path if not red_path or not red_path.exists(): gen = core.generate_image( prompt="A photorealistic red apple on a white background, simple studio lighting" ) red_path = out_dir / f"red_apple_{ts}.png" core.write_image_to_file(gen.buffer, red_path) self.assertTrue(red_path.exists()) self.__class__.red_apple_path = red_path if not green_path or not green_path.exists(): red_b64, red_mime = core.read_image_file(red_path) edited = core.edit_image( prompt=( "Change the apple to a green apple. Keep shape, size, texture, position, lighting, and background " "identical; only change the color of the apple." ), image_data=red_b64, image_mime_type=red_mime, ) green_path = out_dir / f"green_apple_{ts}.png" core.write_image_to_file(edited.buffer, green_path) self.assertTrue(green_path.exists()) self.__class__.green_apple_path = green_path red_b64, red_mime = core.read_image_file(red_path) green_b64, green_mime = core.read_image_file(green_path) prompt = ( "Generate a photorealistic image of a man holding TWO apples: the exact red apple from reference image 1 " "and the exact green apple from reference image 2. Each apple must be identical to its reference in " "shape, size, texture, stem, lighting, highlights, shadows, and color; do not alter any details of either " "apple. Only add the man and his hands holding them naturally. Use a clean, simple studio background." ) out = core.generate_image_with_references( prompt=prompt, reference_images=[(red_b64, red_mime), (green_b64, green_mime)], ) self.assertGreater(len(out.buffer), 0) out_path = out_dir / f"man_holding_red_and_green_apples_{ts}.png" core.write_image_to_file(out.buffer, out_path) self.assertTrue(out_path.exists()) def test_04_generate_with_reference_style_only_no_apple_present(self): model = self._choose_model() self.assertIsNotNone(model, "No image model available for integration test") core.set_current_model(model) out_dir = self._out_dir() ts = self._ts() red_path = self.__class__.red_apple_path if not red_path or not red_path.exists(): gen = core.generate_image( prompt="A photorealistic red apple on a white background, simple studio lighting" ) red_path = out_dir / f"red_apple_{ts}.png" core.write_image_to_file(gen.buffer, red_path) self.assertTrue(red_path.exists()) self.__class__.red_apple_path = red_path red_b64, red_mime = core.read_image_file(red_path) prompt = ( "Use the reference image only to match the photography style (lighting, contrast, background cleanliness). " "Generate a photorealistic portrait of a man smiling, with a clean studio background and similar lighting. " "Do NOT include any apples or apple-like objects anywhere in the image." ) out = core.generate_image_with_references( prompt=prompt, reference_images=[(red_b64, red_mime)], ) self.assertGreater(len(out.buffer), 0) out_path = out_dir / f"style_reference_no_apple_{ts}.png" core.write_image_to_file(out.buffer, out_path) self.assertTrue(out_path.exists()) if __name__ == "__main__": unittest.main()

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/vipincr/imagen-mcp'

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