"""Modèle de données du mini-jeu Association
(document_engine/labels/association_config.py) — sans Flask, teste
directement sanitize_association_config, et le rendu du plateau."""
from typing import Any
import document_engine as doc_engine
def test_sanitize_association_config_on_missing_input_returns_full_defaults() -> None:
assert doc_engine.sanitize_association_config(None) == {"theme_color": "#ff5f2e", "pairs": []}
def test_sanitize_association_config_keeps_valid_pairs() -> None:
raw = {"theme_color": "#123456", "pairs": [{"left": "Chat", "right": "Un félin"}]}
config = doc_engine.sanitize_association_config(raw)
assert config["theme_color"] == "#123456"
assert config["pairs"] == [{"left": "Chat", "right": "Un félin"}]
def test_sanitize_association_config_drops_a_pair_missing_either_side() -> None:
raw = {"pairs": [{"left": "Chat", "right": " "}, {"left": " ", "right": "Un félin"}]}
assert doc_engine.sanitize_association_config(raw)["pairs"] == []
def test_sanitize_association_config_caps_pairs_at_max() -> None:
raw = {"pairs": [{"left": f"L{i}", "right": f"R{i}"} for i in range(doc_engine.MAX_PAIRS + 3)]}
config = doc_engine.sanitize_association_config(raw)
assert len(config["pairs"]) == doc_engine.MAX_PAIRS
def test_sanitize_association_config_ignores_garbage_top_level_input() -> None:
assert doc_engine.sanitize_association_config("n'importe quoi") == doc_engine.DEFAULT_ASSOCIATION_CONFIG
def _association_element(attributes: dict[str, Any]) -> dict[str, Any]:
return {"id": 1, "kind": "association", "parent_id": None, "order_index": 0, "attributes": attributes}
def test_render_association_without_pairs_shows_only_the_badge() -> None:
html = doc_engine.render_document_element(_association_element(doc_engine.DEFAULT_ASSOCIATION_CONFIG), {})
assert "0 paire" in html
assert "docAssocPlayer" not in html
def test_render_association_with_pairs_includes_the_interactive_board() -> None:
config = doc_engine.sanitize_association_config(
{"pairs": [{"left": "Chat", "right": "Un félin"}, {"left": "Chien", "right": "Un canidé"}]}
)
html = doc_engine.render_document_element(_association_element(config), {})
assert "docAssocPlayer" in html
assert "data-assoc-config=" in html
assert "2 paires" in html
# Les deux colonnes sont mélangées indépendamment côté rendu (voir
# _render_association_player) : impossible d'attendre un ORDRE précis,
# seulement que le contenu des deux côtés est bien présent quelque part.
assert "Chat" in html
assert "Un f\\u00e9lin" in html or "Un félin" in html
def test_render_association_escapes_pair_text_in_embedded_json() -> None:
config = doc_engine.sanitize_association_config(
{"pairs": [{"left": '">', "right": "def"}, {"left": "abc", "right": "def2"}]}
)
html = doc_engine.render_document_element(_association_element(config), {})
assert "" not in html
assert "<script>" in html