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:
2026-06-30 20:39:52 +02:00
parent 2f2b295531
commit a5f8fb49ab
1717 changed files with 447332 additions and 0 deletions
@@ -0,0 +1,198 @@
# Unit tests for VocabularyCache.infer_competences with mocked LLM client.
#
# **Validates: Requirements 2.1, 2.2, 2.3, 2.4, 2.5**
from __future__ import annotations
import json
import logging
import pytest
from teamlandkarte_mcp.matching.vocabulary import VocabularyCache
# ---------------------------------------------------------------------------
# Fakes / Stubs
# ---------------------------------------------------------------------------
class FakeDB:
"""Stub DBClient returning a configurable competence list."""
def __init__(self, competences: list[str] | None = None) -> None:
self._competences = competences if competences is not None else []
def get_all_competence_names(self) -> list[str]:
return self._competences
def get_all_role_names(self) -> list[str]:
return ["Software Engineer"]
class FakeLLMClient:
"""Stub AzureOpenAIClient that returns a pre-configured response."""
def __init__(self, response: str | Exception) -> None:
self._response = response
async def chat_completion(self, system: str, user: str) -> str:
if isinstance(self._response, Exception):
raise self._response
return self._response
# ---------------------------------------------------------------------------
# Tests
# ---------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_valid_json_response_returns_correct_tuples() -> None:
"""Valid JSON response from LLM → correct list of (name, confidence) tuples."""
competences = ["Python", "TypeScript", "React", "Docker"]
llm_response = json.dumps(
{
"competences": [
{"name": "Python", "confidence": 0.95},
{"name": "React", "confidence": 0.8},
]
}
)
db = FakeDB(competences=competences)
client = FakeLLMClient(response=llm_response)
cache = VocabularyCache(db=db, client=client) # type: ignore[arg-type]
result = await cache.infer_competences(task_text="Build a Python backend")
assert result == [("Python", 0.95), ("React", 0.8)]
@pytest.mark.asyncio
async def test_empty_task_text_returns_empty_list() -> None:
"""Empty task text → []."""
competences = ["Python", "TypeScript"]
llm_response = json.dumps({"competences": [{"name": "Python", "confidence": 0.9}]})
db = FakeDB(competences=competences)
client = FakeLLMClient(response=llm_response)
cache = VocabularyCache(db=db, client=client) # type: ignore[arg-type]
assert await cache.infer_competences(task_text="") == []
assert await cache.infer_competences(task_text=" ") == []
@pytest.mark.asyncio
async def test_empty_competence_list_in_db_returns_empty_list() -> None:
"""Empty competence list in DB → []."""
llm_response = json.dumps({"competences": [{"name": "Python", "confidence": 0.9}]})
db = FakeDB(competences=[])
client = FakeLLMClient(response=llm_response)
cache = VocabularyCache(db=db, client=client) # type: ignore[arg-type]
result = await cache.infer_competences(task_text="Build a Python backend")
assert result == []
@pytest.mark.asyncio
async def test_llm_error_returns_empty_list_and_logs_warning(
caplog: pytest.LogCaptureFixture,
) -> None:
"""LLM error (Exception) → [] + warning logged."""
competences = ["Python", "TypeScript"]
db = FakeDB(competences=competences)
client = FakeLLMClient(response=RuntimeError("API timeout"))
cache = VocabularyCache(db=db, client=client) # type: ignore[arg-type]
with caplog.at_level(logging.WARNING):
result = await cache.infer_competences(task_text="Build a Python backend")
assert result == []
assert any("LLM call failed" in record.message for record in caplog.records)
@pytest.mark.asyncio
async def test_invalid_json_response_returns_empty_list(
caplog: pytest.LogCaptureFixture,
) -> None:
"""Invalid JSON response → [] (json.loads raises, caught by except)."""
competences = ["Python", "TypeScript"]
db = FakeDB(competences=competences)
client = FakeLLMClient(response="this is not valid json {{{")
cache = VocabularyCache(db=db, client=client) # type: ignore[arg-type]
with caplog.at_level(logging.WARNING):
result = await cache.infer_competences(task_text="Build a Python backend")
assert result == []
assert any("LLM call failed" in record.message for record in caplog.records)
@pytest.mark.asyncio
async def test_unknown_competence_names_are_filtered_out() -> None:
"""Competence names not in DB → filtered out."""
competences = ["Python", "TypeScript", "React"]
llm_response = json.dumps(
{
"competences": [
{"name": "Python", "confidence": 0.9},
{"name": "Rust", "confidence": 0.85}, # not in DB
{"name": "Go", "confidence": 0.7}, # not in DB
{"name": "React", "confidence": 0.6},
]
}
)
db = FakeDB(competences=competences)
client = FakeLLMClient(response=llm_response)
cache = VocabularyCache(db=db, client=client) # type: ignore[arg-type]
result = await cache.infer_competences(task_text="Build a backend service")
assert result == [("Python", 0.9), ("React", 0.6)]
@pytest.mark.asyncio
async def test_max_competences_limited_to_10() -> None:
"""Result is limited to max_competences (default 10)."""
# Create 15 competences and have the LLM return all of them
competences = [f"Competence_{i}" for i in range(15)]
llm_response = json.dumps(
{
"competences": [
{"name": name, "confidence": round(0.5 + i * 0.03, 2)}
for i, name in enumerate(competences)
]
}
)
db = FakeDB(competences=competences)
client = FakeLLMClient(response=llm_response)
cache = VocabularyCache(db=db, client=client) # type: ignore[arg-type]
result = await cache.infer_competences(task_text="A complex task")
assert len(result) == 10
# All returned names must be in the DB list
for name, confidence in result:
assert name in competences
assert 0.0 <= confidence <= 1.0
@pytest.mark.asyncio
async def test_confidence_clamped_to_valid_range() -> None:
"""Confidence values outside [0.0, 1.0] are clamped."""
competences = ["Python", "TypeScript"]
llm_response = json.dumps(
{
"competences": [
{"name": "Python", "confidence": 1.5}, # above 1.0
{"name": "TypeScript", "confidence": -0.3}, # below 0.0
]
}
)
db = FakeDB(competences=competences)
client = FakeLLMClient(response=llm_response)
cache = VocabularyCache(db=db, client=client) # type: ignore[arg-type]
result = await cache.infer_competences(task_text="Build something")
assert result == [("Python", 1.0), ("TypeScript", 0.0)]