import tempfile import unittest from pathlib import Path from unittest.mock import patch from PIL import Image import asset_pipeline class AssetPipelineTests(unittest.TestCase): def setUp(self): self.temp_dir = tempfile.TemporaryDirectory() self.root = Path(self.temp_dir.name) def tearDown(self): self.temp_dir.cleanup() def test_crop_uses_center_and_exact_dimensions(self): source = self.root / "source.png" output = self.root / "crop.png" Image.new("RGB", (80, 60), (20, 40, 60)).save(source) asset_pipeline.crop_image(source, output, 32, 24) with Image.open(output) as image: self.assertEqual(image.size, (32, 24)) def test_resize_contain_preserves_aspect_and_exact_canvas(self): source = self.root / "source.png" output = self.root / "resized.png" Image.new("RGB", (80, 40), (20, 40, 60)).save(source) asset_pipeline.resize_image(source, output, 64, 64, "contain") with Image.open(output) as image: self.assertEqual(image.size, (64, 64)) self.assertEqual(image.getpixel((32, 0)), (0, 0, 0, 0)) def test_texture_audit_accepts_valid_tile_and_rejects_bad_seams(self): good = self.root / "good.png" bad = self.root / "bad.png" Image.new("RGB", (512, 512), (80, 80, 80)).save(good) mismatch = Image.new("RGB", (512, 512), (80, 80, 80)) for y in range(512): mismatch.putpixel((0, y), (255, 0, 0)) mismatch.save(bad) self.assertEqual(asset_pipeline.audit_image( good, "tile")["quality"], "good") self.assertEqual(asset_pipeline.audit_image( bad, "tile")["quality"], "bad") def test_background_audit_rejects_small_image(self): image_path = self.root / "small.png" Image.new("RGB", (640, 360)).save(image_path) result = asset_pipeline.audit_image(image_path, "background") self.assertEqual(result["quality"], "bad") self.assertTrue( any("too small" in error for error in result["errors"])) def test_grayscale_vignette_is_valid_without_alpha(self): image_path = self.root / "vignette.png" Image.new("L", (512, 512), 0).save(image_path) self.assertEqual(asset_pipeline.audit_image( image_path, "overlay")["quality"], "good") def test_icon_audit_rejects_opaque_rgba_images(self): image_path = self.root / "opaque_icon.png" Image.new("RGBA", (64, 64), (255, 255, 255, 255)).save(image_path) result = asset_pipeline.audit_image(image_path, "icon") self.assertEqual(result["quality"], "bad") self.assertTrue(any("transparency required" in error for error in result["errors"])) def test_generation_reuses_existing_backend_and_targets_role_dimensions(self): output = self.root / "background.png" args = type("Args", (), { "prompt": "Hollywood at night", "role": "background", "output": str(output), "width": 1920, "height": 1080, "transparent": None, "model": "test-model", "request_size": 2048, })() generated = Image.new("RGB", (256, 256), (20, 40, 60)) with patch.object(asset_pipeline.generate_textures, "generate_image", return_value=generated) as backend: asset_pipeline._generate(args) self.assertIn("Hollywood at night", backend.call_args.args[0]) self.assertIn("wide 16:9", backend.call_args.args[0]) with Image.open(output) as image: self.assertEqual(image.size, (1920, 1080)) def test_role_inference_uses_game_asset_paths(self): self.assertEqual(asset_pipeline.infer_role( Path("assets/textures/icons/wand.png")), "icon") self.assertEqual(asset_pipeline.infer_role( Path("assets/textures/cards/ticket.png")), "card") self.assertEqual(asset_pipeline.infer_role( Path("assets/textures/skyline.png")), "backdrop") if __name__ == "__main__": unittest.main()