File size: 6,343 Bytes
7c6ffa6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | from __future__ import annotations
from app.services.answer_correction import (
SOURCE_ANSWER_NOT_FOUND,
UNKNOWN_MARKS_PROMPT,
build_answer_correction_output,
)
def test_physics_derivation_correction_marks_missing_change_in_velocity() -> None:
output = build_answer_correction_output(
question="Derive v = u + at.",
student_answer="Acceleration is velocity by time, so v = u + at.",
subject="Physics",
board="Kerala HSE",
class_level="+2",
marks=5,
)
joined = " ".join(output["marks_lost"]).lower()
assert "acceleration definition is incomplete" in joined
assert "change in velocity" in joined
assert "a = (v - u) / t" in output["corrected_board_answer"]
assert output["score"].startswith("Estimated score:")
def test_physics_formula_with_spaces_detected() -> None:
output = build_answer_correction_output(
question="Derive v = u + at.",
student_answer="We know a = change in velocity / time. So a = (v-u)/t. Therefore v = u + at.",
subject="Physics",
marks=5,
)
corrected = output["corrected_board_answer"].lower()
assert "v = u + at" in corrected
assert "estimated score" in output["score"].lower()
lost_joined = " ".join(output["marks_lost"]).lower()
assert "symbol" not in lost_joined or "define" not in lost_joined
def test_chemistry_numerical_co2_11g_correction() -> None:
output = build_answer_correction_output(
question="Calculate number of moles in 11 g of CO2. Molar mass of CO2 = 44 g/mol.",
student_answer="Moles = mass x molar mass = 11 x 44 = 484 mol.",
subject="Chemistry",
marks=3,
)
corrected = output["corrected_board_answer"].lower()
assert "11" in corrected
assert "44" in corrected
assert "0.25" in corrected
assert "co2" in corrected
assert "mol" in corrected
lost_joined = " ".join(output["marks_lost"]).lower()
assert "wrong formula" in lost_joined or "multiplication" in lost_joined or "division" in lost_joined
score = output["score"].lower()
assert "estimated" in score
assert "/3" in score or "/ 3" in score
def test_chemistry_numerical_water_18g_still_works() -> None:
output = build_answer_correction_output(
question="Calculate the number of moles in 18 g of water.",
student_answer="Water has 1 mole.",
subject="Chemistry",
marks=3,
)
missing = " ".join(output["missing_keywords"]).lower()
corrected = output["corrected_board_answer"].lower()
assert "given" in missing
assert "formula" in corrected
assert "substitution" in corrected
assert "mol" in corrected
assert "18" in corrected
assert "h2o" in corrected or "water" in corrected
def test_maths_proof_correction_without_given_to_prove() -> None:
output = build_answer_correction_output(
question="Prove sin^2 x + cos^2 x = 1.",
student_answer="It is a formula, so sin^2 x + cos^2 x = 1.",
subject="Mathematics",
marks=3,
)
corrected = output["corrected_board_answer"].lower()
assert "given" in corrected
assert "to prove" in corrected
assert "pythagoras theorem" in corrected
lost_joined = " ".join(output["marks_lost"]).lower()
score = output["score"].lower()
assert "estimated" in score
assert "/3" in score or "/ 3" in score
assert len(output["marks_lost"]) >= 1
def test_maths_proof_with_full_steps_gets_fewer_marks_lost() -> None:
output = build_answer_correction_output(
question="Prove sin^2 x + cos^2 x = 1.",
student_answer=(
"Let opposite side = a, adjacent side = b, hypotenuse = c. "
"sin x = a/c, cos x = b/c. sin^2 x + cos^2 x = a^2/c^2 + b^2/c^2 "
"= (a^2 + b^2)/c^2 = c^2/c^2 = 1. Hence proved."
),
subject="Mathematics",
marks=3,
)
lost_joined = " ".join(output["marks_lost"]).lower()
assert "too short" not in lost_joined
def test_source_mismatch_does_not_pretend_boolean_logic_supports_physics(client) -> None:
source = client.post(
"/sources/text",
json={
"title": "Number Systems and Boolean Logic",
"text": (
"Binary number system. Decimal to binary conversion. Boolean logic. "
"AND, OR, NOT gates. Truth table examples. Three simple exam questions."
),
"source_type": "notes",
"subject": "Computer Science",
"chapter": "Boolean Logic",
},
)
assert source.status_code == 201
source_id = source.json()["id"]
response = client.post(
"/generate/answer-correction",
json={
"topic": "Derive v = u + at.",
"subject": "Physics",
"source_id": source_id,
"source_ids": [source_id],
"language_preference": "English",
"options": {
"question": "Derive v = u + at.",
"student_answer": "Acceleration is velocity by time, so v = u + at.",
"marks": "5",
},
},
)
assert response.status_code == 200
output = response.json()["output"]
assert output["source_truth"] == SOURCE_ANSWER_NOT_FOUND
assert output["corrected_board_answer"] == SOURCE_ANSWER_NOT_FOUND
assert output["source_evidence"] == []
def test_unknown_marks_asks_or_states_assumed_value() -> None:
output = build_answer_correction_output(
question="Derive v = u + at.",
student_answer="Acceleration is velocity by time, so v = u + at.",
subject="Physics",
marks="not sure",
)
assert "Assuming this is a 5-mark answer" in output["mark_scheme_assumption"]
assert UNKNOWN_MARKS_PROMPT in output["mark_scheme_assumption"]
assert output["score"].startswith("Estimated score:")
def test_estimated_rubric_label_appears_when_no_official_scheme() -> None:
output = build_answer_correction_output(
question="Derive v = u + at.",
student_answer="Acceleration is velocity by time.",
subject="Physics",
marks=5,
)
assert output["official_scheme_available"] is False
assert "estimated" in output["score"].lower()
assert "Assuming" in output["mark_scheme_assumption"]
|