Files
Orchestrator/bahn/teamlandkarte-mcp/tests/test_matching_refinements.py
T
ankn a5f8fb49ab Migrate all repos into monorepo context folders
Bahn: aisupport, Analyse-O2C-C2S, awesome-bahn-mcp-servers, beam-mcp,
      Confluence_Bot, db-planet-mcp-server, O2C-Harness, project-audit,
      Projekt-KIQ-HP, teamlandkarte-mcp
Dhive: Jury-Voting
Privat: CV, NoteGraph (NOTE: NoteGraph needs complete redo after consolidation)
Shared: AI-Orchestrator, OrgMyLife, power_skills_and_more
Shared/references: symphony (read-only)

Bahn repos remain available as independent remotes - this monorepo
pulls them in via subtree, the originals are untouched.
2026-06-30 20:39:52 +02:00

115 lines
3.0 KiB
Python

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