import datetime as dt import pytest from teamlandkarte_mcp.matching.matcher import Matcher from teamlandkarte_mcp.matching.scorer import categorize from teamlandkarte_mcp.mcp_server import _calculate_overlap_percentage, _task_role_text from teamlandkarte_mcp.models import Capacity, Requirements def test_task_role_text_title_first_fallback_to_description() -> None: assert _task_role_text("DevOps Engineer", "foo") == "DevOps Engineer" assert _task_role_text(" ", "Backend Developer") == "Backend Developer" assert _task_role_text(None, " Backend Developer ") == "Backend Developer" def test_overlap_percentage_basic() -> None: ref_start = dt.date(2025, 3, 1) ref_end = dt.date(2025, 6, 30) other_start = dt.date(2025, 3, 15) other_end = dt.date(2025, 6, 30) # 2025-03-01..2025-06-30 inclusive is 122 days; overlap is 108 days. assert ( _calculate_overlap_percentage( ref_start=ref_start, ref_end=ref_end, other_start=other_start, other_end=other_end, ) == "89%" ) def test_overlap_percentage_open_ended_other_end() -> None: ref_start = dt.date(2025, 3, 1) ref_end = dt.date(2025, 3, 31) other_start = dt.date(2025, 3, 15) other_end = None assert ( _calculate_overlap_percentage( ref_start=ref_start, ref_end=ref_end, other_start=other_start, other_end=other_end, ) == "55%" ) def test_categorize_includes_irrelevant() -> None: class _T: top = 0.8 good = 0.6 partial = 0.4 low = 0.2 class _Cfg: thresholds = _T() assert categorize(0.19, _Cfg()) == "Irrelevant" assert categorize(0.20, _Cfg()) == "Low" @pytest.mark.asyncio async def test_matcher_includes_irrelevant_bucket(monkeypatch) -> None: # Arrange: Build a matcher with a fake similarity engine that returns 0 scores. class _FakeSim: use_bm25_search = False # tells Matcher not to build a global index async def compute_competence_similarity(self, required, present, global_index=None): return {r: {"score": 0.0} for r in required} async def compute_role_similarity(self, required_role, cap_role): return 0.0 class _Thresh: top = 0.8 good = 0.6 partial = 0.4 low = 0.2 class _Cfg: competence_weight = 0.8 role_weight = 0.2 thresholds = _Thresh() matcher = Matcher(_FakeSim(), _Cfg()) cap = Capacity( id=1, owner_name="A", role_name="X", role_level=None, begin_date=dt.date(2025, 1, 1), end_date=dt.date(2025, 12, 31), competences=["a"], ) req = Requirements( role_name="Y", competences=["b"], date_start=dt.date(2025, 1, 1), date_end=dt.date(2025, 1, 2), ) # Act res = await matcher.match([cap], req) # Assert assert "Irrelevant" in res.by_category assert len(res.by_category["Irrelevant"]) == 1