Files
aventuriers-des-mots/tests/correction/test_answer_validator.gd
T
Denis Defolie 91dcd7e07a Fix sentence correction rejecting valid answers over incidental text differences
AnswerValidator.validate_sentence_correction compared the typed
correction to the expected word with raw string equality, with none
of the tolerance already built for dictations: no case folding, no
typographic-apostrophe or whitespace normalization, no decomposed-
accent handling. Tapping "petit" and typing "petits" could fail for
reasons having nothing to do with the grammar point being tested
(e.g. a stray capital from autocapitalization), which violates the
"an ambiguous answer is never auto-marked wrong" invariant.

Found this while chasing a manual bug report: an automated test
reproducing the user's exact click sequence (tap once, type, submit)
did NOT reproduce the failure, which actually pointed at the real
gap -- the logic worked for byte-identical input, but had zero
tolerance for anything else. Now reuses TextNormalizer (built for
dictation analysis) before comparing, case-insensitively, while still
showing the child's raw typed text in the feedback. Three new tests
cover case, incidental whitespace, and decomposed accents.

Rebuilt both exports so the shipped binaries include the fix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-11 18:12:56 +02:00

116 lines
3.9 KiB
GDScript

extends GutTest
## Tests de AnswerValidator (choix multiple et correction de phrase).
var multiple_choice_exercise: Dictionary = {
"rule_id": "regle_test",
"answers": ["petit", "petits", "petite"],
"correct_answer": 1,
"hint": "indice",
"explanation": "explication",
}
var sentence_correction_exercise: Dictionary = {
"rule_id": "regle_test",
"hint": "indice",
"explanation": "explication",
"tokens":
[
{"text": "Les", "correct": true},
{
"text": "petit",
"correct": false,
"expected": "petits",
"rule_id": "regle_accord_adjectif",
},
{"text": "chiens", "correct": true},
],
}
func test_multiple_choice_correct_answer() -> void:
var result := AnswerValidator.validate_multiple_choice(multiple_choice_exercise, 1)
assert_true(result.is_correct)
assert_eq(result.errors, [])
func test_multiple_choice_wrong_answer_reports_found_and_expected() -> void:
var result := AnswerValidator.validate_multiple_choice(multiple_choice_exercise, 0)
assert_false(result.is_correct)
assert_eq(result.errors.size(), 1)
assert_eq(result.errors[0]["found"], "petit")
assert_eq(result.errors[0]["expected"], "petits")
assert_eq(result.errors[0]["rule_id"], "regle_test")
func test_sentence_correction_fully_correct() -> void:
var result := AnswerValidator.validate_sentence_correction(
sentence_correction_exercise, [1], {1: "petits"}
)
assert_true(result.is_correct)
func test_sentence_correction_missed_faulty_token() -> void:
var result := AnswerValidator.validate_sentence_correction(sentence_correction_exercise, [], {})
assert_false(result.is_correct)
assert_eq(result.errors.size(), 1)
assert_eq(result.errors[0]["rule_id"], "regle_accord_adjectif")
assert_eq(result.errors[0]["found"], "petit")
assert_eq(result.errors[0]["expected"], "petits")
func test_sentence_correction_wrong_correction_text() -> void:
var result := AnswerValidator.validate_sentence_correction(
sentence_correction_exercise, [1], {1: "petite"}
)
assert_false(result.is_correct)
assert_eq(result.errors[0]["found"], "petite")
assert_eq(result.errors[0]["expected"], "petits")
func test_sentence_correction_false_positive_on_correct_token() -> void:
var result := AnswerValidator.validate_sentence_correction(
sentence_correction_exercise, [1, 2], {1: "petits", 2: "chien"}
)
assert_false(result.is_correct)
assert_eq(result.errors.size(), 1)
assert_eq(result.errors[0]["error_type"], "unclassified")
assert_eq(result.errors[0]["position"], 2)
func test_sentence_correction_accepts_correction_with_different_case() -> void:
# Rapporte manuellement : "Petits" (majuscule incidente, ex. autocapitalisation)
# devait etre accepte au meme titre que "petits".
var result := AnswerValidator.validate_sentence_correction(
sentence_correction_exercise, [1], {1: "Petits"}
)
assert_true(result.is_correct)
func test_sentence_correction_accepts_correction_with_surrounding_whitespace() -> void:
var result := AnswerValidator.validate_sentence_correction(
sentence_correction_exercise, [1], {1: " petits "}
)
assert_true(result.is_correct)
func test_sentence_correction_accepts_decomposed_accented_correction() -> void:
var exercise_with_accent: Dictionary = {
"rule_id": "regle_test",
"hint": "indice",
"explanation": "explication",
"tokens":
[
{
"text": "lecole",
"correct": false,
"expected": "école",
"rule_id": "regle_accord_adjectif",
},
],
}
# "e" + accent aigu combinant (U+0301), forme decomposee du meme mot.
var result := AnswerValidator.validate_sentence_correction(
exercise_with_accent, [0], {0: "école"}
)
assert_true(result.is_correct)