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,294 @@
"""Unit tests for `TrinoClient.get_team_competences` and
`batch_get_team_competences`.
Covers SQL shape (INNER JOIN on the competences view, COALESCE for
`top_competency`, deterministic `ORDER BY`), grouping by `ouid` in
the batch variant, NULL-name filtering and empty-input handling.
"""
from __future__ import annotations
import re
import pytest
from teamlandkarte_mcp.database.trino_client import TrinoClient
class _FakeCursor:
"""Minimal cursor double recording executes and returning canned rows."""
def __init__(self, rows: list[tuple] | None = None) -> None:
self.executed: list[tuple[str, tuple[object, ...] | None]] = []
self._rows: list[tuple] = rows or []
def execute(self, sql: str, params=None) -> None: # noqa: ANN001
if params is None:
params_tuple: tuple[object, ...] | None = None
elif isinstance(params, tuple):
params_tuple = params
else:
params_tuple = tuple(params)
self.executed.append((sql, params_tuple))
def fetchone(self):
return self._rows[0] if self._rows else None
def fetchall(self):
return list(self._rows)
class _FakeCursorContext:
def __init__(self, cursor: _FakeCursor):
self._cursor = cursor
def __enter__(self) -> _FakeCursor:
return self._cursor
def __exit__(self, exc_type, exc, tb) -> None: # noqa: ANN001
return None
class _FakeConfig:
host = "x"
port = 1
username = "u"
password = "p"
http_scheme = "http"
verify_ssl = False
catalog = "c"
schema = "s"
pool_size = 1
def _mk_client(cur: _FakeCursor) -> TrinoClient:
client = TrinoClient(_FakeConfig())
client._cursor = lambda: _FakeCursorContext(cur) # type: ignore[method-assign]
return client
def _norm_sql(sql: str) -> str:
return re.sub(r"\s+", " ", sql).strip()
def _assert_select_only(sql: str) -> None:
upper = sql.strip().upper()
assert upper.startswith("SELECT"), f"expected SELECT, got: {sql!r}"
for kw in ("INSERT", "UPDATE", "DELETE", "DROP", "ALTER", "TRUNCATE"):
assert kw not in upper, f"unexpected write keyword {kw} in SQL: {sql!r}"
# ---------------------------------------------------------------------------
# Single-row variant
# ---------------------------------------------------------------------------
def test_get_team_competences_returns_empty_for_blank_input() -> None:
cur = _FakeCursor(rows=[])
c = _mk_client(cur)
assert c.get_team_competences("") == []
assert c.get_team_competences(" ") == []
assert cur.executed == []
def test_get_team_competences_one_query_select_only_and_join_shape() -> None:
cur = _FakeCursor(
rows=[
("Python", True),
("Cloud", False),
]
)
c = _mk_client(cur)
out = c.get_team_competences("OU1")
assert len(cur.executed) == 1
sql, params = cur.executed[0]
sql_n = _norm_sql(sql)
_assert_select_only(sql)
assert (
"FROM teamlandkarte_v_teammeter_team_competences_latest tc"
in sql_n
)
assert "JOIN teamlandkarte_v_competences_latest c" in sql_n
assert "CAST(tc.competence_id AS VARCHAR) = CAST(c.id AS VARCHAR)" in sql_n
assert "WHERE CAST(tc.ouid AS VARCHAR) = ?" in sql_n
assert "COALESCE(tc.top_competency, FALSE)" in sql_n
# Order: top first, then name asc
assert "ORDER BY" in sql_n
assert "CASE WHEN COALESCE(tc.top_competency, FALSE) THEN 0 ELSE 1 END" in sql_n
assert "c.name ASC" in sql_n
assert params == ("OU1",)
assert out == [
{"name": "Python", "top_competency": True},
{"name": "Cloud", "top_competency": False},
]
def test_get_team_competences_normalises_null_top_competency() -> None:
# SQL-side COALESCE turns NULL into FALSE, but the Python layer also
# defends against `None` defensively via bool(...).
cur = _FakeCursor(rows=[("Python", None), ("Cloud", 1)])
c = _mk_client(cur)
out = c.get_team_competences("OU1")
assert out == [
{"name": "Python", "top_competency": False},
{"name": "Cloud", "top_competency": True},
]
def test_get_team_competences_filters_rows_without_resolvable_name() -> None:
cur = _FakeCursor(
rows=[
(None, True), # join produced no name → drop
("", False), # empty name → drop
("Python", True),
]
)
c = _mk_client(cur)
assert c.get_team_competences("OU1") == [
{"name": "Python", "top_competency": True},
]
def test_get_team_competences_no_rows_returns_empty_list() -> None:
cur = _FakeCursor(rows=[])
c = _mk_client(cur)
assert c.get_team_competences("OU1") == []
assert len(cur.executed) == 1
# ---------------------------------------------------------------------------
# Batch variant
# ---------------------------------------------------------------------------
def test_batch_get_team_competences_empty_input_returns_empty_no_sql() -> None:
cur = _FakeCursor(rows=[])
c = _mk_client(cur)
out = c.batch_get_team_competences([])
assert out == {}
assert cur.executed == []
def test_batch_get_team_competences_groups_and_defaults_to_empty_list() -> None:
cur = _FakeCursor(
rows=[
("OU1", "Python", True),
("OU1", "Cloud", False),
("OU2", "Architecture", True),
]
)
c = _mk_client(cur)
out = c.batch_get_team_competences(["OU1", "OU2", "OU3"])
assert len(cur.executed) == 1, "batch must use exactly one SELECT"
sql, params = cur.executed[0]
sql_n = _norm_sql(sql)
_assert_select_only(sql)
assert (
"FROM teamlandkarte_v_teammeter_team_competences_latest tc"
in sql_n
)
assert "JOIN teamlandkarte_v_competences_latest c" in sql_n
assert "CAST(tc.competence_id AS VARCHAR) = CAST(c.id AS VARCHAR)" in sql_n
assert "WHERE CAST(tc.ouid AS VARCHAR) IN (?, ?, ?)" in sql_n
assert "ORDER BY tc.ouid ASC" in sql_n
assert "CASE WHEN COALESCE(tc.top_competency, FALSE) THEN 0 ELSE 1 END" in sql_n
assert "c.name ASC" in sql_n
assert params == ("OU1", "OU2", "OU3")
assert out == {
"OU1": [
{"name": "Python", "top_competency": True},
{"name": "Cloud", "top_competency": False},
],
"OU2": [
{"name": "Architecture", "top_competency": True},
],
"OU3": [],
}
def test_batch_get_team_competences_filters_unresolved_competence_names() -> None:
cur = _FakeCursor(
rows=[
("OU1", None, True), # no resolvable name → drop
("OU1", "Python", True),
("OU2", "", False), # empty name → drop
]
)
c = _mk_client(cur)
out = c.batch_get_team_competences(["OU1", "OU2"])
assert out == {
"OU1": [{"name": "Python", "top_competency": True}],
"OU2": [],
}
def test_batch_get_team_competences_normalises_null_top_competency() -> None:
cur = _FakeCursor(
rows=[
("OU1", "Python", None),
("OU1", "Cloud", 1),
]
)
c = _mk_client(cur)
out = c.batch_get_team_competences(["OU1"])
assert out == {
"OU1": [
{"name": "Python", "top_competency": False},
{"name": "Cloud", "top_competency": True},
],
}
def test_batch_get_team_competences_ignores_blank_ouid_inputs() -> None:
cur = _FakeCursor(rows=[])
c = _mk_client(cur)
out = c.batch_get_team_competences(["", " "])
assert out == {"": [], " ": []}
assert cur.executed == []
# ---------------------------------------------------------------------------
# SELECT-only guard parametric coverage
# ---------------------------------------------------------------------------
@pytest.mark.parametrize(
"method,args,rows",
[
("get_team_competences", ("OU1",), [("Python", True)]),
(
"batch_get_team_competences",
(["OU1", "OU2"],),
[("OU1", "Python", True)],
),
],
)
def test_team_competence_methods_emit_select_only(
method: str, args: tuple, rows: list[tuple]
) -> None:
cur = _FakeCursor(rows=rows)
c = _mk_client(cur)
getattr(c, method)(*args)
assert cur.executed, f"{method} should have executed a query"
assert len(cur.executed) == 1, f"{method} should issue exactly one query"
sql, _params = cur.executed[0]
_assert_select_only(sql)