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
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
130 lines
4.7 KiB
Python
130 lines
4.7 KiB
Python
"""Tests de la construction du system prompt du chat (app.application.chat).
|
|
|
|
Assertions par INCLUSION (présence des données/sections clés) plutôt que sur le
|
|
texte exact des consignes : robuste aux retouches de formulation, tout en
|
|
vérifiant que chaque contexte est bien injecté.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from app.application.chat import ChatUseCase
|
|
from app.domain.models import (
|
|
ArcSummary,
|
|
CampaignStructuralContext,
|
|
ChapterSummary,
|
|
CharacterSummary,
|
|
ChatMessage,
|
|
GameSystemContext,
|
|
JournalEntrySummary,
|
|
LoreStructuralContext,
|
|
NarrativeEntityContext,
|
|
NpcSummary,
|
|
PageContext,
|
|
PageSummary,
|
|
SceneSummary,
|
|
SessionContext,
|
|
)
|
|
|
|
|
|
def _build(**kw) -> str:
|
|
return ChatUseCase(None).build_system_prompt(**kw)
|
|
|
|
|
|
def _empty_lore() -> LoreStructuralContext:
|
|
return LoreStructuralContext(lore_name="L", lore_description=None, folders={}, tags=[])
|
|
|
|
|
|
def test_base_prompt_without_context_is_non_empty():
|
|
assert len(_build()) > 0
|
|
|
|
|
|
def test_lore_block_renders_pages_with_values_tags_and_links():
|
|
lore = LoreStructuralContext(
|
|
lore_name="Eldoria", lore_description="un monde sombre",
|
|
folders={"PNJ": [PageSummary(
|
|
title="Aragorn", template_name="Personnage",
|
|
values={"apparence": "grand et noble"}, tags=["héros"],
|
|
related_page_titles=["Gondor"])]},
|
|
tags=["dark-fantasy"])
|
|
p = _build(lore_context=lore)
|
|
assert "Eldoria" in p
|
|
assert "un monde sombre" in p
|
|
assert "Aragorn" in p
|
|
assert "apparence" in p and "grand et noble" in p
|
|
assert "héros" in p
|
|
assert "Gondor" in p
|
|
|
|
|
|
def test_empty_lore_signals_vide():
|
|
assert "Lore vide" in _build(lore_context=_empty_lore())
|
|
|
|
|
|
def test_page_context_block_lists_fields_and_empty_marker():
|
|
page = PageContext(title="Aragorn", template_name="Personnage",
|
|
template_fields=["apparence", "histoire"],
|
|
values={"apparence": "grand"})
|
|
p = _build(page_context=page)
|
|
assert "PAGE EN COURS" in p
|
|
assert "Aragorn" in p
|
|
assert "apparence" in p
|
|
assert "(vide)" in p # 'histoire' sans valeur
|
|
|
|
|
|
def test_campaign_block_with_arc_and_empty_pj_npc_and_no_lore_note():
|
|
camp = CampaignStructuralContext(
|
|
campaign_name="La Malédiction", campaign_description="horreur gothique",
|
|
arcs=[ArcSummary(name="Acte I", description="intro",
|
|
chapters=[ChapterSummary(name="Ch1", description="",
|
|
scenes=[SceneSummary(name="Sc1", description="")])])],
|
|
characters=[], npcs=[])
|
|
p = _build(campaign_context=camp)
|
|
assert "CAMPAGNE COURANTE" in p
|
|
assert "La Malédiction" in p
|
|
assert "Acte I" in p
|
|
assert "aucune fiche" in p # pas de PJ
|
|
assert "aucun univers" in p # pas de lore lié
|
|
|
|
|
|
def test_campaign_with_characters_npcs_and_lore_present_note():
|
|
camp = CampaignStructuralContext(
|
|
campaign_name="C", campaign_description=None, arcs=[],
|
|
characters=[CharacterSummary(name="Tav", snippet="roublarde")],
|
|
npcs=[NpcSummary(name="Strahd", snippet="vampire de Barovia")])
|
|
p = _build(campaign_context=camp, lore_context=_empty_lore())
|
|
assert "Tav" in p and "roublarde" in p
|
|
assert "Strahd" in p and "vampire de Barovia" in p
|
|
assert "liée à l'univers" in p
|
|
|
|
|
|
def test_game_system_narrative_and_session_sections_injected():
|
|
gs = GameSystemContext(system_name="Nimble", system_description=None,
|
|
sections={"Combat": "règles de combat"})
|
|
narr = NarrativeEntityContext(entity_type="scene", title="L'auberge du Portail",
|
|
fields={"ambiance": "tendue"})
|
|
sess = SessionContext(
|
|
session_name="Séance 3", active=True, started_at=None,
|
|
entries=[JournalEntrySummary(type="EVENT", content="Le pont s'effondre", occurred_at=None)],
|
|
previous_events=[])
|
|
p = _build(lore_context=_empty_lore(), game_system_context=gs,
|
|
narrative_entity=narr, session_context=sess)
|
|
assert "Nimble" in p
|
|
assert "L'auberge du Portail" in p
|
|
assert "Séance 3" in p
|
|
assert "Le pont s'effondre" in p
|
|
|
|
|
|
async def test_stream_passes_built_prompt_to_llm():
|
|
class FakeChatLLM:
|
|
def __init__(self) -> None:
|
|
self.system_prompt = None
|
|
|
|
async def stream_chat(self, messages, *, system_prompt=None, temperature=None):
|
|
self.system_prompt = system_prompt
|
|
yield "tok"
|
|
|
|
llm = FakeChatLLM()
|
|
out = [t async for t in ChatUseCase(llm).stream(
|
|
[ChatMessage(role="user", content="salut")],
|
|
lore_context=LoreStructuralContext("Eldoria", None, {}, []))]
|
|
assert out == ["tok"]
|
|
assert "Eldoria" in llm.system_prompt
|