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,230 @@
# Feature: llm-fulltext-matching, Property 9: matching_method-Validierung lehnt unbekannte Werte ab
"""Property test for ``matching_method`` validation.
The MCP tools ``find_matching_capacities`` and ``find_matching_tasks``
must reject any ``matching_method`` value whose ``strip().lower()``
representation is neither ``"score"`` nor ``"llm_fulltext"``. The
rejection must:
1. Produce an error message that mentions both allowed values
(``score`` and ``llm_fulltext``).
2. Avoid touching the database (no capacity loading) and the LLM (no
``chat_completion`` call). The validation must short-circuit BEFORE
any side-effect.
The fake database and the fake ``chat_completion`` track call counts;
both counters must remain zero for every Hypothesis-generated invalid
input.
**Validates: Requirements 1.5**
"""
from __future__ import annotations
from typing import Any
import pytest
from hypothesis import HealthCheck, given, settings
from hypothesis import strategies as st
import teamlandkarte_mcp.mcp_server as mcp_mod
from teamlandkarte_mcp.mcp_server import build_server
_CONFIG_TEMPLATE = """
[database]
host='x'
port=1
username='u'
password='p'
backend='trino'
[matching]
competence_weight=0.8
role_weight=0.2
require_confirmation=false
[cache]
db_ttl_hours=1
search_ttl_minutes=60
max_size=100
[azure_openai]
endpoint='https://example.openai.azure.com'
api_version='2024-02-15-preview'
chat_deployment='gpt-4'
""".strip()
class _CountingDB:
"""Fake DB that counts how often ``get_all_capacities_with_competences``
is invoked. Any non-zero counter after a rejected validation is a bug.
"""
def __init__(self) -> None:
self.capacity_calls = 0
self.capacity_by_id_calls = 0
def test_connection(self) -> None:
return
def get_table_columns(self, table: str) -> list[str]:
if table == "teamlandkarte_v_capacity_roles_latest":
return ["name", "active", "staffing_board_relevant"]
if table == "teamlandkarte_v_capacities_latest":
return ["creation_date"]
if table == "teamlandkarte_v_teams_latest":
return [
"team_id",
"ouid",
"about_us",
"offerings",
"interests",
"focus_name",
]
if table == "teamlandkarte_v_teammeter_organizational_units_latest":
return ["id", "name"]
if table == "teamlandkarte_v_teammeter_team_competences_latest":
return ["ouid", "competence_id", "top_competency"]
if table == "teamlandkarte_v_team_references_latest":
return ["ouid", "partner_id", "projects"]
return []
def get_all_role_names(self) -> list[str]:
return ["X"]
def get_all_competence_names(self) -> list[str]:
return ["A"]
def get_open_tasks(self, limit: int = 20):
return []
def get_all_capacities_with_competences(self):
self.capacity_calls += 1
return []
def get_recent_free_capacities(self, limit: int = 20):
return []
def get_capacity_by_id(self, capacity_id): # pragma: no cover
self.capacity_by_id_calls += 1
return None
def _result_to_text(result: Any) -> str:
if isinstance(result, tuple) and len(result) == 2:
content, structured = result
if isinstance(structured, dict) and isinstance(
structured.get("result"), str
):
return structured["result"]
if isinstance(content, list) and content:
text = getattr(content[0], "text", None)
if isinstance(text, str):
return text
return str(result)
async def _call_tool(srv: Any, name: str, args: dict[str, Any]) -> str:
return _result_to_text(await srv.call_tool(name, args))
def _build_test_server(
monkeypatch: pytest.MonkeyPatch,
tmp_path,
) -> tuple[Any, _CountingDB, dict[str, int]]:
"""Construct a server backed by counting DB and LLM doubles."""
cfg = tmp_path / "config.toml"
cfg.write_text(_CONFIG_TEMPLATE, encoding="utf-8")
fake_db = _CountingDB()
monkeypatch.setattr(
mcp_mod, "create_db_client", lambda *_a, **_k: fake_db
)
llm_calls = {"count": 0}
async def _fake_chat_completion(self, system, user): # noqa: ARG001
llm_calls["count"] += 1
return '{"category":"Top","rationale":"r"}'
monkeypatch.setattr(
"teamlandkarte_mcp.azure.openai_client.AzureOpenAIClient.chat_completion",
_fake_chat_completion,
)
monkeypatch.setenv("DATA_LAKE_USERNAME", "u")
monkeypatch.setenv("DATA_LAKE_PASSWORD", "p")
monkeypatch.setenv("AZURE_OPENAI_LLM_API_KEY", "k")
srv = build_server(str(cfg))
return srv, fake_db, llm_calls
# Reject anything that, after trimming and lower-casing, is one of the
# canonical values (those are valid inputs and would not exercise the
# error path) or the empty string. The empty/whitespace-only string is
# treated by the server as "no value provided" (it then falls back to
# the configured default), so it is not part of the invalid-input space
# this property test targets. Hypothesis is otherwise free to generate
# any text.
def _is_invalid_method(value: str) -> bool:
norm = value.strip().lower()
if norm == "":
return False
return norm not in ("score", "llm_fulltext")
_INVALID_METHOD = st.text(max_size=20).filter(_is_invalid_method)
@settings(
max_examples=50,
deadline=None,
suppress_health_check=[HealthCheck.function_scoped_fixture],
)
@given(value=_INVALID_METHOD)
@pytest.mark.asyncio
async def test_invalid_matching_method_is_rejected_without_side_effects(
monkeypatch: pytest.MonkeyPatch,
tmp_path,
value: str,
) -> None:
"""Property 9: invalid matching_method values are rejected verbatim.
The error message must list both allowed values; the database and the
LLM mock must not be touched.
"""
srv, fake_db, llm_calls = _build_test_server(monkeypatch, tmp_path)
res = await _call_tool(
srv,
"find_matching_capacities",
{
"role_name": "X",
"competences": ["A"],
"matching_method": value,
},
)
# The error message references both allowed values.
assert "score" in res, (
f"missing 'score' in error response for {value!r}: {res!r}"
)
assert "llm_fulltext" in res, (
f"missing 'llm_fulltext' in error response for {value!r}: {res!r}"
)
# The response must signal an error (not a successful search).
lowered = res.lower()
assert ("error" in lowered) or ("invalid" in lowered), (
f"response is not an error for {value!r}: {res!r}"
)
# No DB/LLM access for the rejected validation path.
assert fake_db.capacity_calls == 0, (
f"DB capacity loader was invoked for invalid value {value!r}"
)
assert llm_calls["count"] == 0, (
f"LLM was invoked for invalid value {value!r}"
)