Files
LoreMind/brain/tests/test_tree_merger.py
IETM_FIXE\ietm6 4d049274f9
Some checks failed
E2E Tests / e2e (push) Waiting to run
Tests unitaires / Web (Angular · vitest + couverture) (push) Successful in 26s
Build & Push Images / tests (push) Failing after 13s
Build & Push Images / build (brain) (push) Has been skipped
Build & Push Images / build (core) (push) Has been skipped
Build & Push Images / build (web) (push) Has been skipped
Build & Push Images / build-switcher (push) Has been skipped
Tests unitaires / Brain (Python · pytest + couverture) (push) Successful in 1m12s
Tests unitaires / Core (Java · mvn test + JaCoCo) (push) Failing after 1m28s
Refacto du code coté Python, java et coté angular afin de mieux séparer les responsabilité et d'avoir moins de répétitivité dans le code.
Mise en place de tests unitaires coté Python et Angular
Mise en place de la couverture de test directement dans le workflow : le programme ne build pas si jamais un test échoue
Passage en v0.16.2 en conséquence
2026-06-18 15:59:10 +02:00

150 lines
4.9 KiB
Python

"""Tests du _TreeMerger de l'import de campagne (app.application.import_campaign).
Cœur du REDUCE : fusion par nom (insensible à la casse) des sous-arbres
arc→chapitre→scène→pièce produits morceau par morceau, + accumulation des PNJ.
"""
from __future__ import annotations
from app.application.import_campaign import _TreeMerger
def test_single_chunk_builds_full_tree():
m = _TreeMerger()
m.add([{
"name": "Acte I", "description": "intro",
"chapters": [{
"name": "Ch1", "description": "d",
"scenes": [{
"name": "Sc1", "description": "s",
"player_narration": "PN", "gm_notes": "GM",
"rooms": [{"name": "R1", "description": "rd", "enemies": "gob", "loot": "or"}],
}],
}],
}])
arcs = m.result()
assert len(arcs) == 1
arc = arcs[0]
assert arc.name == "Acte I"
assert arc.arc_type == "LINEAR"
sc = arc.chapters[0].scenes[0]
assert sc.player_narration == "PN"
assert sc.gm_notes == "GM"
room = sc.rooms[0]
assert (room.name, room.enemies, room.loot) == ("R1", "gob", "or")
def test_case_insensitive_arc_and_chapter_merge():
m = _TreeMerger()
m.add([{"name": "Acte I", "chapters": [{"name": "Ch1", "scenes": []}]}])
m.add([{"name": "acte i", "chapters": [{"name": "ch1", "scenes": []},
{"name": "Ch2", "scenes": []}]}])
arcs = m.result()
assert len(arcs) == 1
assert {c.name for c in arcs[0].chapters} == {"Ch1", "Ch2"}
def test_description_first_non_empty_wins():
m = _TreeMerger()
m.add([{"name": "A", "description": "", "chapters": []}])
m.add([{"name": "A", "description": "vraie", "chapters": []}])
m.add([{"name": "A", "description": "autre", "chapters": []}])
assert m.result()[0].description == "vraie"
def test_hub_type_wins_if_any_chunk_signals_it():
m = _TreeMerger()
m.add([{"name": "A", "type": "LINEAR", "chapters": []}])
m.add([{"name": "A", "type": "HUB", "chapters": []}])
assert m.result()[0].arc_type == "HUB"
def _scene(narr=None, gm=None):
s = {"name": "S"}
if narr is not None:
s["player_narration"] = narr
if gm is not None:
s["gm_notes"] = gm
return {"name": "A", "chapters": [{"name": "C", "scenes": [s]}]}
def test_scene_narration_concatenated_across_chunks():
m = _TreeMerger()
m.add([_scene(narr="début")])
m.add([_scene(narr="suite")])
sc = m.result()[0].chapters[0].scenes[0]
assert sc.player_narration == "début\n\nsuite"
def test_scene_field_dedups_exact_overlap():
m = _TreeMerger()
m.add([_scene(gm="texte identique")])
m.add([_scene(gm="texte identique")])
assert m.result()[0].chapters[0].scenes[0].gm_notes == "texte identique"
def test_scene_field_takes_superset_version():
m = _TreeMerger()
m.add([_scene(gm="court")])
m.add([_scene(gm="court et bien plus long")])
assert m.result()[0].chapters[0].scenes[0].gm_notes == "court et bien plus long"
def test_npcs_longest_description_wins():
m = _TreeMerger()
m.add_npcs([{"name": "Thorin", "description": "court"}])
m.add_npcs([{"name": "thorin", "description": "une description bien plus complète"}])
npcs = m.npcs()
assert len(npcs) == 1
assert npcs[0].name == "Thorin"
assert npcs[0].description == "une description bien plus complète"
def test_counts():
m = _TreeMerger()
m.add([{"name": "A", "chapters": [
{"name": "C1", "scenes": [{"name": "S1"}, {"name": "S2"}]},
{"name": "C2", "scenes": []},
]}])
assert m.counts() == (1, 2, 2)
def test_blank_names_are_skipped():
m = _TreeMerger()
m.add([{"name": "", "chapters": []},
{"name": " ", "chapters": []},
{"name": "OK", "chapters": [{"name": "", "scenes": []}]}])
arcs = m.result()
assert len(arcs) == 1
assert arcs[0].name == "OK"
assert arcs[0].chapters == []
def test_merge_chapters_consolidation():
m = _TreeMerger()
m.add([{"name": "A", "chapters": [
{"name": "Intro", "scenes": [{"name": "S1"}]},
{"name": "Introduction", "scenes": [{"name": "S2"}]},
]}])
assert m.merge_chapters("Intro", ["Introduction"]) is True
chapters = m.result()[0].chapters
assert len(chapters) == 1
assert {s.name for s in chapters[0].scenes} == {"S1", "S2"}
def test_merge_chapters_unknown_target_returns_false():
m = _TreeMerger()
m.add([{"name": "A", "chapters": [{"name": "Intro", "scenes": []}]}])
assert m.merge_chapters("Inexistant", ["Intro"]) is False
def test_merge_scenes_consolidation():
m = _TreeMerger()
m.add([{"name": "A", "chapters": [{"name": "C", "scenes": [
{"name": "Combat", "gm_notes": "x"},
{"name": "Le combat", "gm_notes": "y"},
]}]}])
assert m.merge_scenes("C", "Combat", ["Le combat"]) is True
scenes = m.result()[0].chapters[0].scenes
assert len(scenes) == 1
assert scenes[0].name == "Combat"