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,153 @@
|
||||
import re
|
||||
|
||||
import pytest
|
||||
|
||||
from teamlandkarte_mcp.database.trino_client import TrinoClient
|
||||
|
||||
|
||||
class _FakeCursor:
|
||||
def __init__(self) -> None:
|
||||
self.executed: list[tuple[str, tuple[object, ...] | None]] = []
|
||||
|
||||
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:
|
||||
# Trino DB-API accepts sequences; tests only need tuple form.
|
||||
params_tuple = tuple(params)
|
||||
self.executed.append((sql, params_tuple))
|
||||
|
||||
def fetchone(self): # pragma: no cover
|
||||
return (1,)
|
||||
|
||||
def fetchall(self): # pragma: no cover
|
||||
return []
|
||||
|
||||
|
||||
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())
|
||||
|
||||
# Avoid connecting in tests; ensure all methods use our fake cursor.
|
||||
client._cursor = lambda: _FakeCursorContext(cur) # type: ignore[method-assign]
|
||||
return client
|
||||
|
||||
|
||||
def _norm_sql(sql: str) -> str:
|
||||
sql = re.sub(r"\s+", " ", sql).strip()
|
||||
return sql
|
||||
|
||||
|
||||
def test_get_recent_free_capacities_query_ordering_and_limit() -> None:
|
||||
cur = _FakeCursor()
|
||||
c = _mk_client(cur)
|
||||
|
||||
c.get_recent_free_capacities(limit=7)
|
||||
|
||||
assert cur.executed, "Expected query to execute"
|
||||
sql, params = cur.executed[-1]
|
||||
sql_n = _norm_sql(sql)
|
||||
|
||||
assert "FROM teamlandkarte_v_capacities_latest" in sql_n
|
||||
assert "WHERE cap.deletion_reason IS NULL" in sql_n
|
||||
assert "ORDER BY cap.creation_date DESC" in sql_n
|
||||
assert "LIMIT ?" in sql_n
|
||||
assert params == (7,)
|
||||
|
||||
|
||||
def test_get_capacity_by_id_query_filters_and_ordering() -> None:
|
||||
cur = _FakeCursor()
|
||||
c = _mk_client(cur)
|
||||
|
||||
c.get_capacity_by_id(123)
|
||||
|
||||
assert cur.executed
|
||||
sql, params = cur.executed[-1]
|
||||
sql_n = _norm_sql(sql)
|
||||
|
||||
assert "FROM teamlandkarte_v_capacities_latest" in sql_n
|
||||
assert "LEFT JOIN teamlandkarte_v_capacity_competences_latest" in sql_n
|
||||
assert "LEFT JOIN teamlandkarte_v_competences_latest" in sql_n
|
||||
assert "cap.deletion_reason IS NULL" in sql_n
|
||||
assert "cap.id = ?" in sql_n
|
||||
assert "ORDER BY comp.name" in sql_n
|
||||
assert params == (123,)
|
||||
|
||||
|
||||
def test_get_all_role_names_query_filters_and_ordering() -> None:
|
||||
cur = _FakeCursor()
|
||||
c = _mk_client(cur)
|
||||
|
||||
c.get_all_role_names()
|
||||
|
||||
assert cur.executed
|
||||
sql, params = cur.executed[-1]
|
||||
sql_n = _norm_sql(sql)
|
||||
|
||||
assert "FROM teamlandkarte_v_capacity_roles_latest" in sql_n
|
||||
assert "WHERE active = true" in sql_n
|
||||
assert "AND staffing_board_relevant = true" in sql_n
|
||||
assert "ORDER BY name" in sql_n
|
||||
assert params is None
|
||||
|
||||
|
||||
def test_get_all_competence_names_query_filters_and_ordering() -> None:
|
||||
cur = _FakeCursor()
|
||||
c = _mk_client(cur)
|
||||
|
||||
c.get_all_competence_names()
|
||||
|
||||
assert cur.executed
|
||||
sql, params = cur.executed[-1]
|
||||
sql_n = _norm_sql(sql)
|
||||
|
||||
assert "FROM beschaffungstool_kmp_skill_latest" in sql_n
|
||||
assert "WHERE skillname__c IS NOT NULL" in sql_n
|
||||
assert "ORDER BY skillname__c" in sql_n
|
||||
assert params is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"method,args",
|
||||
[
|
||||
("get_recent_free_capacities", (1,)),
|
||||
("get_capacity_by_id", ("1",)),
|
||||
("get_all_role_names", ()),
|
||||
("get_all_competence_names", ()),
|
||||
],
|
||||
)
|
||||
def test_phase2_queries_are_select_only(method: str, args: tuple[object, ...]) -> None:
|
||||
cur = _FakeCursor()
|
||||
c = _mk_client(cur)
|
||||
|
||||
getattr(c, method)(*args)
|
||||
|
||||
assert cur.executed
|
||||
sql, _params = cur.executed[-1]
|
||||
assert sql.strip().upper().startswith("SELECT") or sql.strip().upper().startswith(
|
||||
"WITH"
|
||||
)
|
||||
Reference in New Issue
Block a user