#!/usr/bin/env python3 from __future__ import annotations import importlib.util import json from pathlib import Path import tempfile import unittest from unittest import mock MODULE_PATH = Path(__file__).with_name("ui_gallery.py") SPEC = importlib.util.spec_from_file_location("ui_gallery", MODULE_PATH) assert SPEC is not None and SPEC.loader is not None UI_GALLERY = importlib.util.module_from_spec(SPEC) SPEC.loader.exec_module(UI_GALLERY) class UiGalleryWorkflowTests(unittest.TestCase): def test_compiler_artifact_reports_identity_target_and_freshness(self) -> None: message = { "reason": "compiler-artifact", "package_id": "path+file:///workspace#editor_ui@0.1.0", "target": {"name": "editor_ui"}, "fresh": True, } self.assertEqual( UI_GALLERY.compiler_artifact(message), ( "path+file:///workspace#editor_ui@0.1.0", "editor_ui", True, ), ) def test_compiler_artifact_rejects_non_artifact_messages(self) -> None: self.assertIsNone(UI_GALLERY.compiler_artifact({"reason": "build-finished"})) def test_bounded_output_rejects_oversized_machine_documents(self) -> None: with self.assertRaises(RuntimeError): UI_GALLERY.bounded_command_output( [ "python3", "-c", "import sys; sys.stdout.write('x' * 4097)", ], 4096, ) def test_bounded_output_returns_small_json(self) -> None: document = {"packages": [], "resolve": {"nodes": []}} output = UI_GALLERY.bounded_command_output( ["python3", "-c", f"print({json.dumps(json.dumps(document))})"], 4096, ) self.assertEqual(json.loads(output), document) def test_source_snapshot_tracks_only_rust_and_manifests(self) -> None: with tempfile.TemporaryDirectory() as temporary: root = Path(temporary) source = root / "src" source.mkdir() (source / "panel.rs").write_text("fn panel() {}\n", encoding="utf-8") (source / "theme.json").write_text("{}\n", encoding="utf-8") original_roots = UI_GALLERY.WATCH_ROOTS original_files = UI_GALLERY.WATCH_FILES try: UI_GALLERY.WATCH_ROOTS = (source,) UI_GALLERY.WATCH_FILES = () snapshot = UI_GALLERY.source_snapshot() finally: UI_GALLERY.WATCH_ROOTS = original_roots UI_GALLERY.WATCH_FILES = original_files self.assertEqual(list(snapshot), [source / "panel.rs"]) def test_build_graph_cache_requires_matching_manifest_key(self) -> None: with tempfile.TemporaryDirectory() as temporary: cache = Path(temporary) / "graph.json" original = UI_GALLERY.BUILD_GRAPH_CACHE UI_GALLERY.BUILD_GRAPH_CACHE = cache try: UI_GALLERY.save_build_graph("current", {"package-a", "package-b"}) self.assertEqual( UI_GALLERY.cached_build_graph("current"), {"package-a", "package-b"}, ) self.assertIsNone(UI_GALLERY.cached_build_graph("stale")) finally: UI_GALLERY.BUILD_GRAPH_CACHE = original def test_failed_rebuild_preserves_the_last_good_gallery(self) -> None: current = mock.Mock() with ( mock.patch.object(UI_GALLERY, "build_gallery", return_value=1), mock.patch.object(UI_GALLERY, "launch_gallery") as launch, mock.patch.object(UI_GALLERY, "stop_gallery") as stop, ): replacement = UI_GALLERY.rebuild_and_replace_gallery(current) self.assertIs(replacement, current) launch.assert_not_called() stop.assert_not_called() def test_successful_rebuild_launches_before_retiring_the_last_good_gallery(self) -> None: current = mock.Mock() next_gallery = mock.Mock() order: list[str] = [] with ( mock.patch.object(UI_GALLERY, "build_gallery", return_value=0), mock.patch.object( UI_GALLERY, "launch_gallery", side_effect=lambda: order.append("launch") or next_gallery, ), mock.patch.object( UI_GALLERY, "stop_gallery", side_effect=lambda process: order.append("stop"), ) as stop, ): replacement = UI_GALLERY.rebuild_and_replace_gallery(current) self.assertIs(replacement, next_gallery) self.assertEqual(order, ["launch", "stop"]) stop.assert_called_once_with(current) if __name__ == "__main__": unittest.main()