22
33from __future__ import annotations
44
5- import math
65from pathlib import Path
76from unittest .mock import AsyncMock , MagicMock , patch
87
@@ -140,31 +139,26 @@ async def test_boundary_11ccr_adjacent_passes(self):
140139 state = _make_state (completion = "Sample." , metadata = {"target_grade" : "11-CCR" })
141140 assert (await scorer_fn (state , _make_target ())).value == CORRECT
142141
143- async def test_missing_target_grade_returns_unscored (self ):
142+ async def test_missing_target_grade_returns_none (self ):
144143 scorer_fn , _ = _make_scorer ()
145144 state = _make_state (completion = "Sample." , metadata = {})
146- score = await scorer_fn (state , _make_target ())
147- assert math .isnan (float (score .value ))
145+ assert await scorer_fn (state , _make_target ()) is None
148146
149- async def test_invalid_target_grade_returns_unscored (self ):
147+ async def test_invalid_target_grade_returns_none (self ):
150148 scorer_fn , _ = _make_scorer ()
151149 state = _make_state (completion = "Sample." , metadata = {"target_grade" : "Grade 5" })
152- score = await scorer_fn (state , _make_target ())
153- assert math .isnan (float (score .value ))
150+ assert await scorer_fn (state , _make_target ()) is None
154151
155- async def test_empty_completion_returns_unscored (self ):
152+ async def test_empty_completion_returns_none (self ):
156153 scorer_fn , mock_evaluator = _make_scorer ()
157154 state = _make_state (completion = "" , metadata = {"target_grade" : "6-8" })
158- score = await scorer_fn (state , _make_target ())
159- assert math .isnan (float (score .value ))
155+ assert await scorer_fn (state , _make_target ()) is None
160156 mock_evaluator .evaluate .assert_not_called ()
161157
162- async def test_api_error_returns_unscored (self ):
158+ async def test_api_error_returns_none (self ):
163159 scorer_fn , _ = _make_scorer (side_effect = APIError ("rate limit" ))
164160 state = _make_state (completion = "Sample." , metadata = {"target_grade" : "6-8" })
165- score = await scorer_fn (state , _make_target ())
166- assert math .isnan (float (score .value ))
167- assert "rate limit" in score .explanation
161+ assert await scorer_fn (state , _make_target ()) is None
168162
169163 async def test_configuration_error_propagates (self ):
170164 scorer_fn , _ = _make_scorer (side_effect = ConfigurationError ("no key" ))
@@ -186,11 +180,10 @@ async def test_custom_target_grade_key(self):
186180 state = _make_state (completion = "Sample." , metadata = {"expected_grade" : "6-8" })
187181 assert (await scorer_fn (state , _make_target ())).value == CORRECT
188182
189- async def test_custom_target_grade_key_absent_returns_unscored (self ):
183+ async def test_custom_target_grade_key_absent_returns_none (self ):
190184 scorer_fn , _ = _make_scorer (target_grade_key = "expected_grade" )
191185 state = _make_state (completion = "Sample." , metadata = {"target_grade" : "6-8" })
192- score = await scorer_fn (state , _make_target ())
193- assert math .isnan (float (score .value ))
186+ assert await scorer_fn (state , _make_target ()) is None
194187
195188 async def test_custom_grader_model_passed_to_adapter (self ):
196189 with (
@@ -221,16 +214,15 @@ async def test_reads_artifact_files(self, tmp_path: Path):
221214 called_text = mock_evaluator .evaluate .call_args [0 ][0 ].text .value
222215 assert "mitochondria" in called_text
223216
224- async def test_unreadable_artifact_returns_unscored (self ):
217+ async def test_unreadable_artifact_returns_none (self ):
225218 scorer_fn , mock_evaluator = _make_scorer (text_source = "artifacts" )
226219 state = _make_state (
227220 metadata = {
228221 "target_grade" : "6-8" ,
229222 "artifacts" : [{"path" : "/nonexistent/path.md" , "filename" : "missing.md" }],
230223 }
231224 )
232- score = await scorer_fn (state , _make_target ())
233- assert math .isnan (float (score .value ))
225+ assert await scorer_fn (state , _make_target ()) is None
234226 mock_evaluator .evaluate .assert_not_called ()
235227
236228 async def test_multiple_artifacts_joined_with_separator (self , tmp_path : Path ):
@@ -307,7 +299,7 @@ def test_scorer_wires_through_eval(self):
307299 assert sample .score is not None
308300 assert sample .score .value == CORRECT
309301
310- def test_unscored_sample_does_not_count_as_failure (self ):
302+ def test_skipped_sample_is_omitted_from_scores (self ):
311303 from unittest .mock import AsyncMock , patch
312304
313305 from inspect_ai import Task , eval
@@ -323,14 +315,15 @@ def test_unscored_sample_does_not_count_as_failure(self):
323315 ):
324316 scorer = gla_scorer ()
325317
326- # Sample has no target_grade — should be unscored, not INCORRECT
318+ # Sample has no target_grade — gla_scorer returns None, so it must NOT
319+ # contribute a score (no CORRECT/INCORRECT, no NaN to poison the mean).
327320 task = Task (
328321 dataset = [Sample (input = "Write something." , metadata = {})],
329322 solver = [generate ()],
330323 scorer = scorer ,
331324 )
332325
333326 log = eval (task , model = "mockllm/model" )[0 ]
327+ assert log .status == "success"
334328 sample = log .samples [0 ]
335- assert sample .score is not None
336- assert math .isnan (float (sample .score .value ))
329+ assert (sample .scores or {}).get ("gla_scorer" ) is None
0 commit comments