55
66import pytest
77
8+ from src .metrics .base import MetricNotApplicableError
89from src .models .config import MetricConfig
910from src .runners .benchmark import BenchmarkRunner
1011
@@ -353,8 +354,7 @@ def test_evaluator_init_failure_raises(monkeypatch, sample_config):
353354 """A declared evaluator that cannot be created must abort the run, not be skipped.
354355
355356 Silently dropping one leaves a sweep with fewer judges than planned, visible only in
356- metadata.json after the calls have been spent -- and two of the four models reported in
357- the paper are locally served.
357+ metadata.json after the calls have been spent.
358358 """
359359 from src .evaluators .factory import LLMProviderFactory
360360 from src .runners .benchmark import BenchmarkRunner
@@ -519,7 +519,7 @@ def fail_first(*args, **kwargs):
519519def test_skipped_error_does_not_fail_completeness (
520520 registered_metrics , mock_llm_provider , sample_config , sample_quiz
521521):
522- """A ValueError (e.g. distractor on true/false) is a skip, not a failure."""
522+ """MetricNotApplicableError ( distractor on true/false) is a skip, not a failure."""
523523 from dataclasses import replace
524524 from unittest .mock import patch
525525
@@ -544,7 +544,7 @@ def test_skipped_error_does_not_fail_completeness(
544544 def skip_first (* args , ** kwargs ):
545545 call_count [0 ] += 1
546546 if call_count [0 ] == 1 :
547- raise ValueError ("Not applicable for true/false" )
547+ raise MetricNotApplicableError ("Not applicable for true/false" )
548548 return original_evaluate (* args , ** kwargs )
549549
550550 with patch .object (runner .metrics ["clarity" ], "evaluate" , side_effect = skip_first ):
@@ -554,3 +554,48 @@ def skip_first(*args, **kwargs):
554554 assert report ["complete" ]
555555 assert report ["skipped" ] == 1
556556 assert len (report ["skipped_cells" ]) == 1
557+
558+
559+ def test_truncation_error_fails_completeness (
560+ registered_metrics , mock_llm_provider , sample_config , sample_quiz
561+ ):
562+ """max_tokens truncation is data loss, not a skip: it must flip `complete`.
563+
564+ Regression guard: 36 cells were lost to 'length limit was reached' truncation
565+ yet the run reported complete and exited 0.
566+ """
567+ from dataclasses import replace
568+ from unittest .mock import patch
569+
570+ config = replace (
571+ sample_config ,
572+ runs = 1 ,
573+ metrics = [
574+ MetricConfig (
575+ name = "clarity" ,
576+ version = "2.0" ,
577+ evaluators = ["mock_eval" ],
578+ parameters = {},
579+ enabled = True ,
580+ )
581+ ],
582+ )
583+ runner = BenchmarkRunner (config )
584+
585+ original_evaluate = runner .metrics ["clarity" ].evaluate
586+ call_count = [0 ]
587+
588+ def truncate_first (* args , ** kwargs ):
589+ call_count [0 ] += 1
590+ if call_count [0 ] == 1 :
591+ raise ValueError ("Could not parse response content as the length limit was reached" )
592+ return original_evaluate (* args , ** kwargs )
593+
594+ with patch .object (runner .metrics ["clarity" ], "evaluate" , side_effect = truncate_first ):
595+ runner .run (quizzes = [sample_quiz ], source_texts = {"quiz_1" : "text" })
596+
597+ report = runner .get_completeness_report ()
598+ assert not report ["complete" ], "truncation must not be absorbed as a skip"
599+ assert report ["failed" ] == 1
600+ assert report ["skipped" ] == 0
601+ assert report ["failed_cells" ][0 ]["category" ] == "failed"
0 commit comments