"""Phase 4-B v1 환각 가드 정규식 단위 테스트 — ship gate. worker 와 같은 모듈을 import 해 검증. plan 의 검증 D 케이스 (허용 5 + 차단 7). """ from __future__ import annotations import sys from pathlib import Path # 프로젝트 루트의 app/ 를 import path 에 추가 (Document Server 패턴) ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT / "app")) from services.study.session_summary_guard import ( # noqa: E402 GUARD_PATTERN, calibrate_confidence, normalize_confidence, ) # ─── GUARD_PATTERN 허용 케이스 (search() == None 이어야 함) ─── ALLOWED_CASES = [ "오답 5건이 있었습니다.", "모르겠음 83건이 남았습니다.", "배관 영역에서 흔들린 것으로 보입니다.", "같은 영역 문제를 더 풀어보세요.", "정답을 다시 한 번 확인해보세요.", ] # ─── GUARD_PATTERN 차단 케이스 (search() != None 이어야 함) ─── BLOCKED_CASES = [ "정답률 16%였습니다.", "최근 5일 동안 약했습니다.", "지난 7일간 비슷한 실수가 반복됐습니다.", "5~10문항을 더 풀어보세요.", "2026-05-02 기준으로 보면...", "5월 2일 이후 흐름은...", "지난 7회차에서 반복됐습니다.", ] def test_guard_pattern_allows_normal_summary(): for case in ALLOWED_CASES: match = GUARD_PATTERN.search(case) assert match is None, f"false positive: {case!r} matched {match!r}" def test_guard_pattern_blocks_numeric_hallucination(): for case in BLOCKED_CASES: match = GUARD_PATTERN.search(case) assert match is not None, f"false negative: {case!r} not matched" def test_normalize_confidence_standard_values(): for v in ("high", "medium", "low"): assert normalize_confidence(v) == v assert normalize_confidence(v.upper()) == v assert normalize_confidence(f" {v} ") == v def test_normalize_confidence_nonstandard_values(): for v in ("unknown", "mid", "maybe", "", "true", None, 123, [], {}): assert normalize_confidence(v) == "low" # ─── Phase 4-D calibrate_confidence — 자료 부족 토픽 cap ─── def test_calibrate_confidence_no_evidence_caps_to_low(): # 문서 0 + ready ai_explanation 0 → cap 'low' assert calibrate_confidence("high", ctx_docs_count=0, ready_explanation_count=0) == "low" assert calibrate_confidence("medium", ctx_docs_count=0, ready_explanation_count=0) == "low" assert calibrate_confidence("low", ctx_docs_count=0, ready_explanation_count=0) == "low" def test_calibrate_confidence_only_explanations_caps_to_medium(): # 문서 0 + ready ai_explanation 있음 → cap 'medium' assert calibrate_confidence("high", ctx_docs_count=0, ready_explanation_count=3) == "medium" assert calibrate_confidence("medium", ctx_docs_count=0, ready_explanation_count=3) == "medium" assert calibrate_confidence("low", ctx_docs_count=0, ready_explanation_count=3) == "low" def test_calibrate_confidence_with_documents_passthrough(): # 문서 1건 이상 → 모델 값 그대로 통과 assert calibrate_confidence("high", ctx_docs_count=2, ready_explanation_count=0) == "high" assert calibrate_confidence("medium", ctx_docs_count=1, ready_explanation_count=0) == "medium" assert calibrate_confidence("low", ctx_docs_count=5, ready_explanation_count=10) == "low" def test_calibrate_confidence_normalizes_invalid_first(): # 비표준 값은 normalize_confidence 가 'low' 로 박은 후 cap 적용 — 결과는 'low' assert calibrate_confidence("unknown", ctx_docs_count=0, ready_explanation_count=0) == "low" assert calibrate_confidence(None, ctx_docs_count=5, ready_explanation_count=5) == "low" if __name__ == "__main__": # 직접 실행 시 모든 케이스 빠른 점검 test_guard_pattern_allows_normal_summary() test_guard_pattern_blocks_numeric_hallucination() test_normalize_confidence_standard_values() test_normalize_confidence_nonstandard_values() test_calibrate_confidence_no_evidence_caps_to_low() test_calibrate_confidence_only_explanations_caps_to_medium() test_calibrate_confidence_with_documents_passthrough() test_calibrate_confidence_normalizes_invalid_first() print("OK")