127 lines
4.6 KiB
Python
127 lines
4.6 KiB
Python
"""规则校验器测试(第一道防线)."""
|
||
|
||
import json
|
||
|
||
from src.ai.services.evaluation.rule_validator import (
|
||
MAX_QUESTION_LENGTH,
|
||
RuleValidator,
|
||
ValidationResult,
|
||
)
|
||
|
||
|
||
class TestRuleValidator:
|
||
"""RuleValidator 测试."""
|
||
|
||
def setup_method(self) -> None:
|
||
self.validator = RuleValidator()
|
||
|
||
def test_valid_json_passes(self) -> None:
|
||
"""合法 JSON 通过."""
|
||
output = json.dumps({
|
||
"question": "什么是勾股定理?",
|
||
"answer": "a² + b² = c²",
|
||
"explanation": "直角三角形两直角边的平方和等于斜边的平方",
|
||
})
|
||
result = self.validator.validate(output)
|
||
assert result.passed is True
|
||
assert result.score == 1.0
|
||
assert result.parsed is not None
|
||
|
||
def test_invalid_json_fails(self) -> None:
|
||
"""非 JSON 失败."""
|
||
result = self.validator.validate("这不是 JSON")
|
||
assert result.passed is False
|
||
assert "不是有效的 JSON" in result.errors[0]
|
||
|
||
def test_markdown_wrapped_json(self) -> None:
|
||
"""markdown 代码块包裹的 JSON 能解析."""
|
||
output = '```json\n{"question": "题", "answer": "答"}\n```'
|
||
result = self.validator.validate(output)
|
||
assert result.passed is True
|
||
|
||
def test_missing_question_fails(self) -> None:
|
||
"""缺少 question 字段失败."""
|
||
output = json.dumps({"answer": "答"})
|
||
result = self.validator.validate(output)
|
||
assert result.passed is False
|
||
assert any("question" in e for e in result.errors)
|
||
|
||
def test_missing_answer_fails(self) -> None:
|
||
"""缺少 answer 字段失败."""
|
||
output = json.dumps({"question": "题"})
|
||
result = self.validator.validate(output)
|
||
assert result.passed is False
|
||
assert any("answer" in e for e in result.errors)
|
||
|
||
def test_short_question_warning(self) -> None:
|
||
"""过短 question 产生 warning."""
|
||
output = json.dumps({"question": "ab", "answer": "答"})
|
||
result = self.validator.validate(output)
|
||
assert result.passed is True
|
||
assert any("过短" in w for w in result.warnings)
|
||
assert result.score == 0.8 # 有 warning
|
||
|
||
def test_difficulty_mismatch_warning(self) -> None:
|
||
"""难度不匹配产生 warning."""
|
||
output = json.dumps({
|
||
"question": "这是一道题目",
|
||
"answer": "这是答案",
|
||
"difficulty": "easy",
|
||
})
|
||
result = self.validator.validate(output, expected_difficulty="hard")
|
||
assert result.passed is True
|
||
assert any("difficulty 不匹配" in w for w in result.warnings)
|
||
|
||
def test_invalid_difficulty_warning(self) -> None:
|
||
"""无效难度值产生 warning."""
|
||
output = json.dumps({
|
||
"question": "这是一道题目",
|
||
"answer": "这是答案",
|
||
"difficulty": "impossible",
|
||
})
|
||
result = self.validator.validate(output, expected_difficulty="easy")
|
||
assert result.passed is True
|
||
assert any("无效" in w for w in result.warnings)
|
||
|
||
def test_question_type_mismatch_warning(self) -> None:
|
||
"""题型不匹配产生 warning."""
|
||
output = json.dumps({
|
||
"question": "这是一道题目",
|
||
"answer": "这是答案",
|
||
"question_type": "single_choice",
|
||
})
|
||
result = self.validator.validate(
|
||
output, expected_question_type="essay",
|
||
)
|
||
assert result.passed is True
|
||
assert any("question_type 不匹配" in w for w in result.warnings)
|
||
|
||
def test_json_array_fails(self) -> None:
|
||
"""JSON 数组(非 dict)失败."""
|
||
result = self.validator.validate('[1, 2, 3]')
|
||
assert result.passed is False
|
||
|
||
def test_long_question_warning(self) -> None:
|
||
"""过长 question 产生 warning."""
|
||
output = json.dumps({
|
||
"question": "题" * (MAX_QUESTION_LENGTH + 1),
|
||
"answer": "答",
|
||
})
|
||
result = self.validator.validate(output)
|
||
assert result.passed is True
|
||
assert any("过长" in w for w in result.warnings)
|
||
|
||
def test_score_no_warnings(self) -> None:
|
||
"""无 warning 时 score=1.0."""
|
||
output = json.dumps({
|
||
"question": "这是一道完整的题目",
|
||
"answer": "这是完整的答案",
|
||
})
|
||
result = self.validator.validate(output)
|
||
assert result.score == 1.0
|
||
|
||
def test_validation_result_score_failed(self) -> None:
|
||
"""失败时 score=0.0."""
|
||
result = ValidationResult(passed=False, errors=["err"])
|
||
assert result.score == 0.0
|