import sys, os; sys.path.insert(0, "src") os.environ["BUDDY_FORCE_FAKE_RUNTIME"] = "1" from buddy_fusion.fusion import Lineage from buddy_fusion.fallback import fallback_genome, suggest_tweaks_fallback from buddy_fusion.runtime import FakeBuddyRuntime from buddy_fusion.fusion_ui import _parse_chip, _bench_side_html, _pool_sample, TWEAK_POOL def _lin_with_two(): lin = Lineage() a = lin.add(fallback_genome(1, "chick")) b = lin.add(fallback_genome(2, "frog")) return lin, a.id, b.id def test_bench_side_bubbles_carry_side_and_full_sentence(): # Hint bubbles float in the scene; each shows a full sentence verbatim AND # carries that same sentence in data-prompt (what the tweak receives). lin, a, _b = _lin_with_two() hints = ["Add two curved horns", "Give it a pair of wings", "Add a long swishy tail", "Add big round eyes"] html = _bench_side_html(lin, a, "A", hints) assert html.count("buddy-bubble") == len(hints) assert 'data-side="A"' in html for s in hints: assert f'data-prompt="{s}"' in html assert f">{s}" in html def test_bench_side_drops_swatches_and_keeps_scene_and_name(): lin, a, _b = _lin_with_two() html = _bench_side_html(lin, a, "A", ["Add a tail"] * 4) assert "buddy-bench-scene" in html assert "buddy-voxel-root" in html assert lin.get(a).genome.name in html assert "buddy-swatch" not in html # palette swatches removed def test_bench_side_html_empty_slot_is_from_scratch_panel(): lin, _a, _b = _lin_with_two() html = _bench_side_html(lin, None, "A") assert "buddy-bench-cell empty" in html assert "Build from scratch" in html # invites a from-scratch description assert "buddy-bubble" not in html # no AI hint bubbles on an empty slot def test_bench_side_html_empty_slot_tolerates_no_lineage(): # FreeLand opens the bench with both slots empty; the panel must not need a # lineage (cid is None short-circuits before lin.get is touched). html = _bench_side_html(None, None, "B") assert "buddy-bench-cell empty" in html assert "Build from scratch" in html def test_pool_sample_returns_four_distinct(): s = _pool_sample() assert len(s) == 4 and len(set(s)) == 4 assert all(x in TWEAK_POOL for x in s) def test_suggest_tweaks_fallback_returns_four_nonempty(): out = suggest_tweaks_fallback([{"x": 0, "y": 0, "z": 0, "w": 2, "h": 2, "d": 2}]) assert len(out) == 4 and all(isinstance(x, str) and x.strip() for x in out) def test_fake_runtime_suggest_tweaks_returns_four(): out = FakeBuddyRuntime().suggest_tweaks([{}, {}, {}]) assert len(out) == 4 and all(isinstance(x, str) and x.strip() for x in out) def test_parse_chip_valid_trims_prompt(): side, prompt = _parse_chip('{"side": "B", "prompt": " add wings "}') assert side == "B" assert prompt == "add wings" def test_parse_chip_rejects_bad_side_and_garbage(): assert _parse_chip('{"side": "Z", "prompt": "x"}') == (None, "") assert _parse_chip("not json") == (None, "")