| """ |
| Unit tests for eval/grounded.py — shared matching primitives + check_no_invention. |
| |
| These tests use the same sys.path bootstrap as test_eval_matching.py. |
| No app import needed — grounded.py is stdlib-only. |
| |
| Test coverage: |
| - check_no_invention: returns [] when all deadline values appear verbatim in transcription |
| - check_no_invention: returns non-empty list when a deadline value is NOT in transcription |
| - normalize, value_found, extract_values_from_section: return identical results to known |
| cases from test_eval_matching (regression against moved functions) |
| - import run_eval still works after refactor (eval-compat regression) |
| """ |
|
|
| import os |
| import sys |
|
|
| |
| _REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
| _EVAL_DIR = os.path.join(_REPO_ROOT, "eval") |
| if _REPO_ROOT not in sys.path: |
| sys.path.insert(0, _REPO_ROOT) |
| if _EVAL_DIR not in sys.path: |
| sys.path.insert(0, _EVAL_DIR) |
|
|
| import grounded |
| from types import SimpleNamespace as S |
|
|
|
|
| |
| |
| |
|
|
| def test_check_no_invention_returns_empty_when_all_values_in_transcription(): |
| """Returns [] when every deadline value appears verbatim in the transcription.""" |
| result = S( |
| transcription="betala 1 234 kr senast 2026-06-15", |
| deadlines="- 2026-06-15 — sista dag\n- 1 234 kr — belopp", |
| ) |
| invented = grounded.check_no_invention(result) |
| assert invented == [] |
|
|
|
|
| def test_check_no_invention_returns_nonempty_for_invented_value(): |
| """Returns a non-empty list when a deadline value is NOT in transcription.""" |
| result = S( |
| transcription="betala senast 2026-06-15", |
| deadlines="- 2026-07-99 — invented date\n- 2026-06-15 — real date", |
| ) |
| invented = grounded.check_no_invention(result) |
| assert len(invented) >= 1 |
| assert "2026-07-99" in invented |
|
|
|
|
| def test_check_no_invention_empty_deadlines_returns_empty(): |
| """Empty deadlines section → no values extracted → returns [].""" |
| result = S( |
| transcription="betala senast 2026-06-15", |
| deadlines="", |
| ) |
| invented = grounded.check_no_invention(result) |
| assert invented == [] |
|
|
|
|
| def test_check_no_invention_none_found_returns_empty(): |
| """'None found.' sentinel in deadlines → no values extracted → returns [].""" |
| result = S( |
| transcription="informational letter", |
| deadlines="None found.", |
| ) |
| invented = grounded.check_no_invention(result) |
| assert invented == [] |
|
|
|
|
| def test_check_no_invention_all_invented_returns_all(): |
| """All values invented → all returned in invented list.""" |
| result = S( |
| transcription="hej det här är ett informationsbrev", |
| deadlines="- 2099-01-01 — fake\n- 99 999 kr — fake amount", |
| ) |
| invented = grounded.check_no_invention(result) |
| assert len(invented) == 2 |
|
|
|
|
| |
| |
| |
|
|
| def test_normalize_case_insensitive(): |
| """normalize lowercases input (regression from test_eval_matching).""" |
| assert grounded.normalize("1 234,56 KR") == grounded.normalize("1 234,56 kr") |
|
|
|
|
| def test_normalize_collapses_multiple_spaces(): |
| """Multiple spaces collapse to single space.""" |
| assert grounded.normalize("a b") == "a b" |
|
|
|
|
| def test_value_found_long_date_in_transcription(): |
| """An ISO date is found when present.""" |
| haystack = "Du ska betala senast 2026-06-15. Glöm inte." |
| assert grounded.value_found("2026-06-15", haystack) is True |
|
|
|
|
| def test_value_found_short_boundary_inside_longer(): |
| """A 3-digit ref is NOT found inside a longer run of digits.""" |
| haystack = "ref 1234567 i systemet" |
| assert grounded.value_found("123", haystack) is False |
|
|
|
|
| def test_value_found_short_boundary_standalone(): |
| """A 3-digit ref IS found when standing alone.""" |
| haystack = "OCR-nummer 123 gäller" |
| assert grounded.value_found("123", haystack) is True |
|
|
|
|
| def test_extract_em_dash_separator(): |
| """Em-dash separator: verbatim value before ' — '.""" |
| section = "- 15 juni 2026 — last day to file\n- 1 234 kr — amount owed" |
| vals = grounded.extract_values_from_section(section) |
| assert vals == ["15 juni 2026", "1 234 kr"] |
|
|
|
|
| def test_extract_hyphen_with_spaces_separator(): |
| """Hyphen-with-spaces separator.""" |
| section = "- 2026-06-15 - sista betalningsdag\n- 1 234 kr - fakturabelopp" |
| vals = grounded.extract_values_from_section(section) |
| assert vals == ["2026-06-15", "1 234 kr"] |
|
|
|
|
| def test_extract_none_found_yields_empty(): |
| """'None found.' lines yield no values.""" |
| assert grounded.extract_values_from_section("None found.") == [] |
|
|
|
|
| def test_extract_date_with_internal_hyphens_preserved(): |
| """ISO date is not split by internal hyphens.""" |
| section = "- 2026-06-15 — sista dag" |
| vals = grounded.extract_values_from_section(section) |
| assert vals == ["2026-06-15"] |
|
|
|
|
| |
| |
| |
|
|
| def test_import_run_eval_succeeds(): |
| """import run_eval still works after the grounded.py refactor.""" |
| import run_eval |
| assert hasattr(run_eval, "normalize"), "run_eval.normalize must be re-bound" |
| assert hasattr(run_eval, "value_found"), "run_eval.value_found must be re-bound" |
| assert hasattr(run_eval, "extract_values_from_section"), ( |
| "run_eval.extract_values_from_section must be re-bound" |
| ) |
|
|
|
|
| def test_run_eval_normalize_still_accessible_via_namespace(): |
| """import run_eval as e; e.normalize(...) still resolves after refactor.""" |
| import run_eval as e |
| |
| assert e.normalize("A b") == "a b" |
|
|
|
|
| if __name__ == "__main__": |
| import pytest |
| pytest.main([__file__, "-v"]) |
|
|