import sys; sys.path.insert(0, "src") from buddy_fusion.fusion import Lineage, Creature from buddy_fusion.fallback import fallback_genome def test_add_ancestor_has_no_parents_and_gen_0(): lin = Lineage() c = lin.add(fallback_genome(1)) assert c.generation == 0 and c.parent_ids == () def test_fuse_creates_gen_1_child_with_two_parents(): lin = Lineage() a = lin.add(fallback_genome(1)); b = lin.add(fallback_genome(2)) child = lin.fuse(a.id, b.id, seed=9) # uses deterministic fallback when no runtime assert child.generation == 1 assert set(child.parent_ids) == {a.id, b.id} assert lin.get(child.id) is child def test_generation_increments_from_max_parent(): lin = Lineage() a = lin.add(fallback_genome(1)); b = lin.add(fallback_genome(2)) c1 = lin.fuse(a.id, b.id, seed=1) c2 = lin.fuse(c1.id, a.id, seed=2) assert c2.generation == 2 def test_serialize_roundtrip(): lin = Lineage() a = lin.add(fallback_genome(1)); b = lin.add(fallback_genome(2)) lin.fuse(a.id, b.id, seed=3) restored = Lineage.from_dict(lin.to_dict()) assert len(restored.creatures) == 3 def test_bloodline_of_fresh_hatch_is_parents_then_child(): lin = Lineage() a = lin.add(fallback_genome(1)); b = lin.add(fallback_genome(2)) child = lin.fuse(a.id, b.id, seed=3) chain = lin.bloodline(child.id) assert [c.id for c in chain] == [a.id, b.id, child.id] # oldest-first, both parents assert chain[-1] is child def test_bloodline_includes_descendants_when_present(): lin = Lineage() a = lin.add(fallback_genome(1)); b = lin.add(fallback_genome(2)) c1 = lin.fuse(a.id, b.id, seed=1) # gen 1 grandchild = lin.fuse(c1.id, a.id, seed=2) # gen 2, child of c1 chain = lin.bloodline(c1.id) ids = [c.id for c in chain] assert c1.id in ids and grandchild.id in ids # spine pulls in the descendant assert ids == sorted(ids, key=lambda x: chain[ids.index(x)].generation) # generation-ordered def test_bloodline_unknown_id_is_empty(): lin = Lineage() assert lin.bloodline("nope") == [] def test_bloodline_is_bounded_on_deep_tree(): lin = Lineage() a = lin.add(fallback_genome(1)); b = lin.add(fallback_genome(2)) focus = lin.fuse(a.id, b.id, seed=0) for i in range(8): # long descendant chain focus = lin.fuse(focus.id, a.id, seed=i + 10) # focus is the deepest node; ancestry up is capped, so the chain never explodes assert len(lin.bloodline(a.id)) <= 8