"""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