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.
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
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
|
||||
Reference in New Issue
Block a user