"""Tests for Tools/facade_detail_concepts.py — the pure parts. No network calls: the OpenRouter request and the image bytes are represented as the small structures the API uses. Generation itself is manual and paid. """ import os import sys _TOOLS = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, _TOOLS) for _sub in ("review", "gen", "unreal", "blender", "input"): sys.path.insert(0, os.path.join(_TOOLS, _sub)) import base64 import json import os import sys import unittest ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) sys.path.insert(0, os.path.join(ROOT, "Tools")) import facade_detail_concepts as fc # noqa: E402 class StudyTableTest(unittest.TestCase): def test_every_study_has_a_reference_and_a_prompt(self): self.assertTrue(fc.STUDIES) for name, (ref, prompt) in fc.STUDIES.items(): self.assertTrue(ref, f"{name} has no reference") self.assertTrue(prompt.strip(), f"{name} has no prompt") # References are relative to Art/reference and never escape it. self.assertFalse(os.path.isabs(ref), f"{name} reference is absolute") self.assertNotIn("..", ref, f"{name} reference escapes the directory") def test_prompt_always_carries_the_anchor_suffix(self): for name in fc.STUDIES: prompt = fc.build_prompt(name) self.assertIn(fc.ANCHOR_SUFFIX, prompt) self.assertIn(fc.STUDIES[name][1], prompt) def test_unknown_study_raises(self): with self.assertRaises(KeyError): fc.build_prompt("no-such-study") class NextFreePathTest(unittest.TestCase): def test_uses_the_plain_name_when_free(self): import tempfile with tempfile.TemporaryDirectory() as tmp: self.assertEqual(os.path.basename(fc.next_free_path("x", tmp)), "x.png") def test_never_overwrites(self): import tempfile with tempfile.TemporaryDirectory() as tmp: open(os.path.join(tmp, "x.png"), "w").close() self.assertEqual(os.path.basename(fc.next_free_path("x", tmp)), "x-v2.png") open(os.path.join(tmp, "x-v2.png"), "w").close() self.assertEqual(os.path.basename(fc.next_free_path("x", tmp)), "x-v3.png") class DecodeImageTest(unittest.TestCase): PAYLOAD_BYTES = b"\x89PNG\r\n\x1a\nfake" def _data_url(self): return "data:image/png;base64," + base64.b64encode(self.PAYLOAD_BYTES).decode() def test_reads_message_images(self): payload = {"choices": [{"message": { "images": [{"image_url": {"url": self._data_url()}}]}}]} self.assertEqual(fc.decode_image(payload), self.PAYLOAD_BYTES) def test_reads_a_data_url_embedded_in_content(self): payload = {"choices": [{"message": {"content": f"here: {self._data_url()}"}}]} self.assertEqual(fc.decode_image(payload), self.PAYLOAD_BYTES) def test_refusal_raises_with_the_text(self): payload = {"choices": [{"message": {"content": "I cannot do that."}}]} with self.assertRaises(SystemExit): fc.decode_image(payload) class DataUrlTest(unittest.TestCase): def test_encodes_a_jpeg(self): import tempfile with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as handle: handle.write(b"jpegbytes") path = handle.name try: url = fc.data_url(path) self.assertTrue(url.startswith("data:image/jpeg;base64,")) finally: os.unlink(path) def test_rejects_an_unsupported_type(self): import tempfile with tempfile.NamedTemporaryFile(suffix=".gif", delete=False) as handle: path = handle.name try: with self.assertRaises(SystemExit): fc.data_url(path) finally: os.unlink(path) class SidecarShapeTest(unittest.TestCase): def test_studies_and_outputs_are_local_only(self): # The output directory lives under the gitignored Art/reference tree, so # neither the images nor the sidecars can be committed. self.assertIn(os.path.join("Art", "reference"), fc.OUT_DIR) self.assertIn("gitignored", fc.__doc__) if __name__ == "__main__": unittest.main()