"""Modèle de données du mini-jeu Quiz (document_engine/labels/quiz_config.py) — sans Flask, teste directement sanitize_quiz_config/quiz_total_points.""" import document_engine as doc_engine def test_sanitize_quiz_config_on_missing_input_returns_full_defaults() -> None: config = doc_engine.sanitize_quiz_config(None) assert config == { "theme_color": "#ff5f2e", "timer_enabled": False, "timer_seconds": 30, "questions": [], } def test_sanitize_quiz_config_keeps_a_valid_question_as_is() -> None: raw = { "theme_color": "#123456", "timer_enabled": True, "timer_seconds": 45, "questions": [ {"text": "2 + 2 ?", "choices": ["3", "4", "5"], "correct_index": 1, "points": 10}, ], } config = doc_engine.sanitize_quiz_config(raw) assert config["theme_color"] == "#123456" assert config["timer_enabled"] is True assert config["timer_seconds"] == 45 assert config["questions"] == [{"text": "2 + 2 ?", "choices": ["3", "4", "5"], "correct_index": 1, "points": 10}] def test_sanitize_quiz_config_drops_a_question_with_empty_text() -> None: raw = {"questions": [{"text": " ", "choices": ["A", "B"], "correct_index": 0, "points": 5}]} assert doc_engine.sanitize_quiz_config(raw)["questions"] == [] def test_sanitize_quiz_config_drops_a_question_with_fewer_than_two_choices() -> None: raw = {"questions": [{"text": "Une question", "choices": ["Seul choix"], "correct_index": 0, "points": 5}]} assert doc_engine.sanitize_quiz_config(raw)["questions"] == [] def test_sanitize_quiz_config_drops_blank_choices_before_counting() -> None: raw = {"questions": [{"text": "Une question", "choices": ["A", " ", ""], "correct_index": 0, "points": 5}]} assert doc_engine.sanitize_quiz_config(raw)["questions"] == [] def test_sanitize_quiz_config_caps_choices_at_four() -> None: raw = { "questions": [ {"text": "Une question", "choices": ["A", "B", "C", "D", "E"], "correct_index": 0, "points": 5}, ], } config = doc_engine.sanitize_quiz_config(raw) assert config["questions"][0]["choices"] == ["A", "B", "C", "D"] def test_sanitize_quiz_config_resets_out_of_range_correct_index() -> None: raw = {"questions": [{"text": "Une question", "choices": ["A", "B"], "correct_index": 7, "points": 5}]} config = doc_engine.sanitize_quiz_config(raw) assert config["questions"][0]["correct_index"] == 0 def test_sanitize_quiz_config_rejects_a_boolean_as_correct_index() -> None: """True/False sont des int en Python (bool hérite de int) — un JSON malveillant/malformé pourrait glisser `true` là où un index est attendu, ce qui vaudrait 1 après coercition implicite sans ce garde-fou explicite.""" raw = {"questions": [{"text": "Une question", "choices": ["A", "B"], "correct_index": True, "points": 5}]} config = doc_engine.sanitize_quiz_config(raw) assert config["questions"][0]["correct_index"] == 0 def test_sanitize_quiz_config_clamps_negative_points_to_zero() -> None: raw = {"questions": [{"text": "Une question", "choices": ["A", "B"], "correct_index": 0, "points": -5}]} assert doc_engine.sanitize_quiz_config(raw)["questions"][0]["points"] == 0 def test_sanitize_quiz_config_clamps_timer_seconds_into_bounds() -> None: assert doc_engine.sanitize_quiz_config({"timer_seconds": 1})["timer_seconds"] == doc_engine.MIN_TIMER_SECONDS assert doc_engine.sanitize_quiz_config({"timer_seconds": 9999})["timer_seconds"] == doc_engine.MAX_TIMER_SECONDS def test_sanitize_quiz_config_ignores_garbage_top_level_input() -> None: assert doc_engine.sanitize_quiz_config("n'importe quoi") == doc_engine.DEFAULT_QUIZ_CONFIG def test_quiz_total_points_sums_every_question() -> None: config = doc_engine.sanitize_quiz_config( { "questions": [ {"text": "Q1", "choices": ["A", "B"], "correct_index": 0, "points": 10}, {"text": "Q2", "choices": ["A", "B"], "correct_index": 1, "points": 5}, ] } ) assert doc_engine.quiz_total_points(config) == 15 def test_quiz_total_points_on_empty_quiz_is_zero() -> None: assert doc_engine.quiz_total_points(doc_engine.DEFAULT_QUIZ_CONFIG) == 0