"""Unit tests for student/teacher comparison. No database.""" from __future__ import annotations import os import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT)) os.environ.setdefault("KANSHO_PROVIDER_KEY", "") os.environ.setdefault("KANSHO_DETECT_PROVIDER_KEY", "") from unittest.mock import patch from detect_learning import canonicalize_detect_mode, decision_for_mention from detect_pipeline import ( MentionSpan, compact_diff_counters, map_entity_type, protects, same_mention, types_equivalent, ) def expect(ok: bool, message: str) -> None: if not ok: raise SystemExit(f"FAIL: {message}") print(f"OK {message}") def main() -> None: expect(canonicalize_detect_mode("learning") == "learning_full", "learning aliases to learning_full") expect(canonicalize_detect_mode("semantic") == "bypass", "semantic aliases to bypass") expect(canonicalize_detect_mode("wordlist") is None, "unknown mode is rejected") expect(map_entity_type("person") == "PERSON", "person maps to PERSON") expect(map_entity_type("place") == "PLACE", "place maps to PLACE") expect(protects("PERSON") and not protects("FOOD"), "PERSON protects, FOOD does not") expect(types_equivalent("PERSON", "PLACE"), "similar protectable types are equivalent") expect(not types_equivalent("FOOD", "PERSON"), "FOOD versus PERSON is never equivalent") inner = MentionSpan("Sushi", 20, 25, "PERSON", "identity", "cue", True, "frau") outer = MentionSpan("Frau Sushi", 15, 25, "PERSON", "identity", "teacher", False, "frau") expect(same_mention(inner, outer), "contained span is the same mention") food = MentionSpan("Sushi", 20, 25, "FOOD", "not_identity", "teacher", False, "aß") expect(same_mention(inner, food), "same offsets stay one mention even when types differ") compact = compact_diff_counters({"diff_agree": 2, "diff_type_conflict": 1, "label": "Sushi"}) expect(compact["diff_agree"] == 2 and compact["diff_type_conflict"] == 1, "known counters are kept") expect("label" not in compact and "Sushi" not in str(compact), "compact counters drop plaintext") with patch("detect_learning.get_cue_decision", return_value=None), patch( "detect_learning.get_sense", return_value={"identity_hits": 3, "non_identity_hits": 0, "ambiguous": False}, ), patch("detect_learning.cue_for_mention", return_value="aß"): expect( decision_for_mention("p", "Sushi", "Ich aß Sushi.", 7, 12) == "ask", "Tag-21: identity-only sense does not lock a new cue", ) with patch("detect_learning.get_cue_decision", return_value="identity"), patch( "detect_learning.cue_for_mention", return_value="frau" ): expect( decision_for_mention("p", "Sushi", "meine Frau Sushi", 11, 16) == "identity", "stored cue remains binding", ) print("detect pipeline unit ok") if __name__ == "__main__": main()