"""Tests for similarity helpers.""" from __future__ import annotations from matching.similarity import ( normalize_employer, title_score, employer_match, desc_score, ) class TestNormalizeEmployer: def test_simple_lowercase(self): assert normalize_employer("Acme") == "acme" def test_strips_swedish_ab(self): assert normalize_employer("Acme AB") == "acme" def test_strips_aktiebolag(self): assert normalize_employer("Acme Aktiebolag") == "acme" def test_strips_consulting_suffix(self): assert normalize_employer("Nordic IT Consulting AB") == "nordic it" def test_strips_corp_suffix(self): assert normalize_employer("Globex Corp.") == "globex" def test_strips_ltd_suffix(self): assert normalize_employer("Foo Ltd") == "foo" def test_strips_recruitment_suffix(self): assert normalize_employer("Acme Recruitment Group") == "acme" def test_strips_multiple_suffixes(self): # "Consulting AB" should strip both "AB" then "Consulting" assert normalize_employer("Nordic Consulting AB") == "nordic" def test_removes_punctuation(self): assert normalize_employer("Acme, Inc.") == "acme" def test_empty_string(self): assert normalize_employer("") == "" def test_whitespace_only(self): assert normalize_employer(" ") == "" def test_preserves_core_name_with_special_chars(self): result = normalize_employer("Café Nu AB") assert "café" in result or "cafe" in result def test_dots_in_name_preserved(self): # Punctuation (except & which gets stripped) is removed; H&M -> h m result = normalize_employer("H&M AB") assert result == "h m" class TestTitleScore: def test_identical_titles(self): assert title_score("Senior Python Developer", "Senior Python Developer") == 100.0 def test_similar_titles_high_score(self): score = title_score("Python Developer", "Senior Python Developer") assert score >= 85.0 def test_different_titles_low_score(self): score = title_score("Python Developer", "Frontend Designer") assert score < 50.0 def test_empty_title(self): assert title_score("", "Something") == 0.0 def test_both_empty(self): assert title_score("", "") == 0.0 def test_order_independent(self): # token_set_ratio is order-independent a = "Senior Python Developer" b = "Developer Python Senior" assert title_score(a, b) == 100.0 class TestEmployerMatch: def test_same_name_matches(self): assert employer_match("Acme AB", "Acme AB") is True def test_suffix_variation_matches(self): assert employer_match("Acme AB", "Acme") is True def test_different_employers_no_match(self): assert employer_match("Acme AB", "Globex AB") is False def test_consulting_variations_match(self): assert employer_match("Nordic IT Consulting AB", "Nordic IT") is True def test_empty_no_match(self): assert employer_match("", "") is False def test_one_empty_no_match(self): assert employer_match("Acme", "") is False class TestDescScore: def test_identical_descriptions(self): desc = "We are looking for a Python developer with 5 years experience." assert desc_score(desc, desc) == 100.0 def test_similar_descriptions_high(self): a = "We are looking for a Python developer with 5 years experience." b = "We are looking for a Python developer with 5 years experience in web." assert desc_score(a, b) >= 80.0 def test_different_descriptions_low(self): a = "We need a frontend developer skilled in React and CSS." b = "Looking for a data scientist with Python and SQL expertise." assert desc_score(a, b) < 50.0 def test_empty_desc(self): assert desc_score("", "something") == 0.0 def test_truncation(self): # Test that truncation to max_chars works. long_a = "Python " * 1000 long_b = "Python " * 1000 score = desc_score(long_a, long_b, max_chars=100) assert score == 100.0