Gateprep / backend /tests /test_scoring.py
banu4prasad's picture
add: tests and code cleanup
05b4327
Raw
History Blame Contribute Delete
3.2 kB
import pytest
from app.models.models import Question
from app.services.scoring import evaluate_answer
def make_question(q_type, correct, marks=2.0, negative_marks=0.66) -> Question:
return Question(
question_type=q_type,
correct_answer=correct,
marks=marks,
negative_marks=negative_marks,
question_text="Test",
)
def test_evaluate_answer_skipped():
q = make_question("mcq", "A")
# Skipped answer should return None for is_correct, and 0.0 marks
assert evaluate_answer(q, None) == (None, 0.0)
assert evaluate_answer(q, "") == (None, 0.0)
assert evaluate_answer(q, " ") == (None, 0.0)
def test_evaluate_answer_mcq():
q = make_question("mcq", "A", marks=2.0, negative_marks=0.66)
# Correct
assert evaluate_answer(q, "A") == (True, 2.0)
# Case insensitivity
assert evaluate_answer(q, "a") == (True, 2.0)
# Incorrect
assert evaluate_answer(q, "B") == (False, -0.66)
assert evaluate_answer(q, "C") == (False, -0.66)
def test_evaluate_answer_msq():
# MSQ gives marks only for fully correct, no negative marking.
q = make_question("msq", "A,B", marks=2.0, negative_marks=0.0)
# Exact match
assert evaluate_answer(q, "A,B") == (True, 2.0)
# Order independence & case insensitivity & spacing
assert evaluate_answer(q, "b, a") == (True, 2.0)
assert evaluate_answer(q, "B; A") == (True, 2.0)
# Partial correct
assert evaluate_answer(q, "A") == (False, 0.0)
# Extra wrong option
assert evaluate_answer(q, "A,B,C") == (False, 0.0)
# Completely wrong
assert evaluate_answer(q, "C,D") == (False, 0.0)
def test_evaluate_answer_nat_range():
# NAT gives marks only for correct, no negative marking.
q = make_question("nat", "10.5:11.5", marks=2.0, negative_marks=0.0)
# Within range
assert evaluate_answer(q, "11.0") == (True, 2.0)
# Exact boundaries
assert evaluate_answer(q, "10.5") == (True, 2.0)
assert evaluate_answer(q, "11.5") == (True, 2.0)
# Outside range
assert evaluate_answer(q, "10.49") == (False, 0.0)
assert evaluate_answer(q, "11.51") == (False, 0.0)
# Invalid given format
assert evaluate_answer(q, "abc") == (False, 0.0)
def test_evaluate_answer_nat_exact():
q = make_question("nat", "42.5", marks=1.0, negative_marks=0.0)
# Exact match
assert evaluate_answer(q, "42.5") == (True, 1.0)
assert evaluate_answer(q, "42.50") == (True, 1.0)
# Outside
assert evaluate_answer(q, "42.6") == (False, 0.0)
def test_evaluate_answer_nat_multiple_options():
# NAT with multiple correct ranges/options separated by comma
q = make_question("nat", "1.0:2.0, 4.0:5.0", marks=2.0, negative_marks=0.0)
# In first range
assert evaluate_answer(q, "1.5") == (True, 2.0)
# In second range
assert evaluate_answer(q, "4.5") == (True, 2.0)
# Outside both
assert evaluate_answer(q, "3.0") == (False, 0.0)
def test_evaluate_answer_invalid_question_type():
q = make_question("unknown_type", "A")
# Should safely return False, 0.0
assert evaluate_answer(q, "A") == (False, 0.0)