"""Tests for keyword extraction and coverage.""" from __future__ import annotations from matching.keywords import extract_keywords, coverage class TestExtractKeywords: def test_basic_extraction(self): text = "Python developer with Fast API experience and PostgreSQL database skills." kws = extract_keywords(text) assert "python" in kws assert "fastapi" in kws assert "postgresql" in kws def test_stopwords_removed(self): text = "We are looking for a developer with experience in Python." kws = extract_keywords(text) assert "we" not in kws assert "are" not in kws assert "for" not in kws assert "a" not in kws assert "in" not in kws assert "python" in kws assert "developer" in kws def test_swedish_stopwords_removed(self): text = "Vi letar efter en Python utvecklare med erfarenhet av Docker." kws = extract_keywords(text) assert "vi" not in kws assert "en" not in kws assert "av" not in kws assert "python" in kws assert "docker" in kws def test_top_n_limit(self): text = "python python python docker docker docker kubernetes kubernetes kubernetes react react react" kws = extract_keywords(text, top_n=2) assert len(kws) == 2 def test_empty_text(self): assert extract_keywords("") == [] def test_whitespace_only(self): assert extract_keywords(" ") == [] def test_multiword_fastapi(self): text = "Experience with fast api framework for building REST APIs." kws = extract_keywords(text) assert "fastapi" in kws def test_multiword_machine_learning(self): text = "machine learning models for predictive analytics." kws = extract_keywords(text) assert "machine-learning" in kws def test_frequency_ordering(self): text = "python python python docker docker kubernetes" kws = extract_keywords(text, top_n=3) assert kws[0] == "python" assert kws[1] == "docker" assert kws[2] == "kubernetes" def test_deterministic_tie_breaking(self): """Ties in frequency should be broken alphabetically.""" text = "docker kubernetes" kws = extract_keywords(text) # Both have frequency 1, so alphabetical: docker < kubernetes assert kws[0] == "docker" assert kws[1] == "kubernetes" def test_min_token_length(self): text = "x y z aa bb cc developer" kws = extract_keywords(text) assert "x" not in kws assert "y" not in kws assert "z" not in kws assert "developer" in kws def test_tech_terms_preserved(self): text = "Node.js and React Native for mobile development." kws = extract_keywords(text) assert "nodejs" in kws assert "react-native" in kws class TestCoverage: def test_full_coverage(self): cv = "Python developer with Fast API PostgreSQL Docker AWS Kubernetes" posting = "Python developer with Fast API PostgreSQL Docker AWS Kubernetes" result = coverage(cv, posting) assert result["ratio"] == 1.0 assert len(result["missing"]) == 0 def test_partial_coverage(self): cv = "Python developer with PostgreSQL and Docker experience" posting = "Python developer with Fast API PostgreSQL Docker AWS Kubernetes React" result = coverage(cv, posting) assert 0.0 < result["ratio"] < 1.0 assert "python" in result["matched"] assert "postgresql" in result["matched"] assert "docker" in result["matched"] assert "fastapi" in result["missing"] assert "kubernetes" in result["missing"] def test_zero_coverage(self): cv = "Chef with experience in French cuisine and menu planning" posting = "Python developer with Fast API PostgreSQL Docker" result = coverage(cv, posting) assert result["ratio"] == 0.0 assert len(result["matched"]) == 0 assert len(result["missing"]) > 0 def test_empty_posting(self): result = coverage("Python developer", "") assert result == {"matched": [], "missing": [], "ratio": 0.0} def test_empty_cv(self): result = coverage("", "Python developer with Docker") assert result["ratio"] == 0.0 assert len(result["matched"]) == 0 assert len(result["missing"]) > 0 def test_both_empty(self): result = coverage("", "") assert result == {"matched": [], "missing": [], "ratio": 0.0} def test_ratio_calculation(self): cv = "python docker postgresql" posting = "python docker postgresql kubernetes" result = coverage(cv, posting) # 3 of 4 matched (approx, depends on stopword filtering). assert result["ratio"] > 0.5 assert result["ratio"] <= 1.0 def test_matched_and_missing_lists(self): cv = "python docker" posting = "python docker kubernetes react" result = coverage(cv, posting) assert "python" in result["matched"] assert "docker" in result["matched"] assert "kubernetes" in result["missing"] assert "react" in result["missing"] def test_coverage_returns_dict_keys(self): result = coverage("python", "python docker") assert "matched" in result assert "missing" in result assert "ratio" in result assert isinstance(result["matched"], list) assert isinstance(result["missing"], list) assert isinstance(result["ratio"], float)