"""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 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()