"""Tests for evaluation module.""" import pytest from contractlens.evaluation.evaluator import Evaluator from contractlens.evaluation.error_taxonomy import ErrorTaxonomy, classify_error from contractlens.evaluation.metrics import ( compute_char_overlap, compute_span_f1, is_partial_match, ) from contractlens.models import ClauseCategory, ErrorCategory, Span class TestMetrics: """Tests for metrics computation.""" def test_compute_char_overlap(self): """Test character overlap computation.""" overlap, pred_cov, gt_cov = compute_char_overlap(0, 10, 5, 15) assert overlap == 5 assert pred_cov == 0.5 assert gt_cov == 0.5 def test_compute_char_overlap_no_overlap(self): """Test with no overlap.""" overlap, pred_cov, gt_cov = compute_char_overlap(0, 10, 20, 30) assert overlap == 0 assert pred_cov == 0.0 assert gt_cov == 0.0 def test_is_partial_match(self): """Test partial match detection.""" pred = Span( start_char=0, end_char=10, text="Test clause", category=ClauseCategory.TERMINATION, confidence=0.9, ) gt = Span( start_char=0, end_char=20, text="Test clause with more text", category=ClauseCategory.TERMINATION, confidence=0.9, ) assert is_partial_match(pred, gt) is True def test_compute_span_f1_perfect_match(self): """Test F1 with perfect match.""" pred = [ Span( start_char=0, end_char=10, text="Termination", category=ClauseCategory.TERMINATION, confidence=0.9, ) ] gt = [ Span( start_char=0, end_char=10, text="Termination", category=ClauseCategory.TERMINATION, confidence=0.9, ) ] tp, fp, fn, precision, recall, f1 = compute_span_f1( pred, gt, ClauseCategory.TERMINATION ) assert tp == 1 assert fp == 0 assert fn == 0 assert f1 == 1.0 def test_compute_span_f1_no_predictions(self): """Test F1 with no predictions.""" pred: list[Span] = [] gt = [ Span( start_char=0, end_char=10, text="Termination", category=ClauseCategory.TERMINATION, confidence=0.9, ) ] tp, fp, fn, precision, recall, f1 = compute_span_f1( pred, gt, ClauseCategory.TERMINATION ) assert tp == 0 assert fp == 0 assert fn == 1 assert f1 == 0.0 class TestErrorTaxonomy: """Tests for error taxonomy.""" def test_record_error(self): """Test recording an error.""" taxonomy = ErrorTaxonomy() taxonomy.record_error( category=ErrorCategory.OFFSET_ERROR, contract_id="test_1", message="Offset mismatch", clause_category=ClauseCategory.TERMINATION, ) assert len(taxonomy) == 1 def test_get_error_distribution(self): """Test getting error distribution.""" taxonomy = ErrorTaxonomy() taxonomy.record_error(ErrorCategory.OFFSET_ERROR, "c1", "error 1") taxonomy.record_error(ErrorCategory.OFFSET_ERROR, "c2", "error 2") taxonomy.record_error(ErrorCategory.VERIFICATION_FAILED, "c3", "error 3") dist = taxonomy.get_error_distribution() assert dist["offset_error"] == 2 assert dist["verification_failed"] == 1 def test_get_top_errors(self): """Test getting top errors.""" taxonomy = ErrorTaxonomy() taxonomy.record_error(ErrorCategory.OFFSET_ERROR, "c1", "e1") taxonomy.record_error(ErrorCategory.OFFSET_ERROR, "c2", "e2") taxonomy.record_error(ErrorCategory.VERIFICATION_FAILED, "c3", "e3") top = taxonomy.get_top_errors(2) assert len(top) == 2 assert top[0][0] == ErrorCategory.OFFSET_ERROR def test_classify_error(self): """Test error classification.""" assert classify_error("offset mismatch") == ErrorCategory.OFFSET_ERROR assert classify_error("verification rejected") == ErrorCategory.VERIFICATION_FAILED assert classify_error("unknown error") == ErrorCategory.MODEL_ERROR class TestEvaluator: """Tests for Evaluator.""" def test_evaluator_initialization(self): """Test evaluator initialization.""" evaluator = Evaluator(model="gpt-4o-mini") assert evaluator.model == "gpt-4o-mini" assert len(evaluator.categories) == 41 def test_evaluate_contract(self): """Test evaluating a single contract.""" evaluator = Evaluator(model="gpt-4o-mini") contract_text = "This is a test contract with a termination clause." ground_truth = [ Span( start_char=32, end_char=47, text="termination clause", category=ClauseCategory.TERMINATION, confidence=1.0, ) ] results = evaluator.evaluate_contract(contract_text, ground_truth, "test_1") assert len(results) == 41 # All categories