2026-05-05 22:11:05 +02:00
|
|
|
"""Unit tests ohne Datenbank für die Zugriffsschicht (Visibility-SQL, Header-Parsing)."""
|
|
|
|
|
import pytest
|
|
|
|
|
from fastapi import HTTPException
|
|
|
|
|
|
2026-05-08 10:45:58 +02:00
|
|
|
from tenant_context import library_content_visibility_sql, parse_active_club_header, resolve_tenant_context
|
2026-05-05 22:11:05 +02:00
|
|
|
|
|
|
|
|
|
2026-05-09 10:55:58 +02:00
|
|
|
def test_library_visibility_sql_platform_admin_restricts_club_by_membership():
|
2026-05-05 22:11:05 +02:00
|
|
|
sql, params = library_content_visibility_sql(
|
|
|
|
|
alias="e",
|
|
|
|
|
profile_id=1,
|
|
|
|
|
role="admin",
|
|
|
|
|
effective_club_id=None,
|
|
|
|
|
)
|
2026-05-09 10:55:58 +02:00
|
|
|
assert "e.visibility = 'official'" in sql
|
|
|
|
|
assert "club_members" in sql
|
|
|
|
|
assert params == [1, 1]
|
2026-05-05 22:11:05 +02:00
|
|
|
|
|
|
|
|
|
2026-05-09 10:55:58 +02:00
|
|
|
def test_library_visibility_sql_superadmin_uses_membership_clause_for_club_visibility():
|
2026-05-05 22:11:05 +02:00
|
|
|
sql, params = library_content_visibility_sql(
|
|
|
|
|
alias="fp",
|
|
|
|
|
profile_id=2,
|
|
|
|
|
role="superadmin",
|
|
|
|
|
effective_club_id=100,
|
|
|
|
|
)
|
2026-05-09 10:55:58 +02:00
|
|
|
assert "club_members" in sql
|
|
|
|
|
assert params == [2, 2]
|
2026-05-05 22:11:05 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_library_visibility_sql_trainer_without_active_club_no_shared_club_branch():
|
|
|
|
|
sql, params = library_content_visibility_sql(
|
|
|
|
|
alias="g",
|
|
|
|
|
profile_id=42,
|
|
|
|
|
role="trainer",
|
|
|
|
|
effective_club_id=None,
|
|
|
|
|
)
|
|
|
|
|
assert "official" in sql
|
|
|
|
|
assert "private" in sql
|
|
|
|
|
assert "visibility = 'club'" not in sql
|
|
|
|
|
assert params == [42]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_library_visibility_sql_user_with_active_club_includes_club_branch():
|
|
|
|
|
sql, params = library_content_visibility_sql(
|
|
|
|
|
alias="t",
|
|
|
|
|
profile_id=7,
|
|
|
|
|
role="user",
|
|
|
|
|
effective_club_id=99,
|
|
|
|
|
)
|
|
|
|
|
assert "visibility = 'club'" in sql
|
|
|
|
|
assert "club_members" in sql
|
|
|
|
|
assert params[0] == 7 # private branch created_by
|
|
|
|
|
assert 99 in params
|
|
|
|
|
assert params.count(7) >= 2 # private + EXISTS membership
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_parse_active_club_header_none_and_empty():
|
|
|
|
|
assert parse_active_club_header(None) is None
|
|
|
|
|
assert parse_active_club_header("") is None
|
|
|
|
|
assert parse_active_club_header(" ") is None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_parse_active_club_header_valid():
|
|
|
|
|
assert parse_active_club_header("12") == 12
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_parse_active_club_header_invalid():
|
|
|
|
|
with pytest.raises(HTTPException) as exc:
|
|
|
|
|
parse_active_club_header("not-int")
|
|
|
|
|
assert exc.value.status_code == 400
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_parse_active_club_header_non_positive():
|
|
|
|
|
with pytest.raises(HTTPException) as exc:
|
|
|
|
|
parse_active_club_header("0")
|
|
|
|
|
assert exc.value.status_code == 400
|
2026-05-08 10:45:58 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_resolve_platform_admin_uses_stored_club_without_header(monkeypatch):
|
|
|
|
|
"""Ohne X-Active-Club-Id: effective wie gespeichertes Profil (Sync mit Dropdown/DB)."""
|
|
|
|
|
cur = object()
|
|
|
|
|
|
|
|
|
|
def fake_exists(c, cid):
|
|
|
|
|
return cid == 99
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr("tenant_context._club_exists", fake_exists)
|
|
|
|
|
ctx = resolve_tenant_context(
|
|
|
|
|
cur,
|
|
|
|
|
profile_id=1,
|
|
|
|
|
global_role="admin",
|
|
|
|
|
header_raw=None,
|
|
|
|
|
memberships=[{"id": 10}],
|
|
|
|
|
stored_active_club_id=99,
|
2026-06-06 21:10:52 +02:00
|
|
|
email_verified=True,
|
2026-05-08 10:45:58 +02:00
|
|
|
)
|
|
|
|
|
assert ctx.effective_club_id == 99
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_resolve_platform_admin_header_overrides_stored(monkeypatch):
|
|
|
|
|
cur = object()
|
|
|
|
|
|
|
|
|
|
def fake_exists(c, cid):
|
|
|
|
|
return cid in (5, 99)
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr("tenant_context._club_exists", fake_exists)
|
|
|
|
|
ctx = resolve_tenant_context(
|
|
|
|
|
cur,
|
|
|
|
|
profile_id=1,
|
|
|
|
|
global_role="superadmin",
|
|
|
|
|
header_raw="5",
|
|
|
|
|
memberships=[{"id": 10}],
|
|
|
|
|
stored_active_club_id=99,
|
2026-06-06 21:10:52 +02:00
|
|
|
email_verified=True,
|
2026-05-08 10:45:58 +02:00
|
|
|
)
|
|
|
|
|
assert ctx.effective_club_id == 5
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_resolve_platform_admin_no_header_stored_invalid(monkeypatch):
|
|
|
|
|
cur = object()
|
|
|
|
|
monkeypatch.setattr("tenant_context._club_exists", lambda c, cid: False)
|
|
|
|
|
ctx = resolve_tenant_context(
|
|
|
|
|
cur,
|
|
|
|
|
profile_id=1,
|
|
|
|
|
global_role="admin",
|
|
|
|
|
header_raw=None,
|
|
|
|
|
memberships=[{"id": 1}],
|
|
|
|
|
stored_active_club_id=123,
|
2026-06-06 21:10:52 +02:00
|
|
|
email_verified=True,
|
2026-05-08 10:45:58 +02:00
|
|
|
)
|
|
|
|
|
assert ctx.effective_club_id is None
|
2026-05-09 10:42:56 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_resolve_trainer_club_ids_excludes_inactive_memberships():
|
|
|
|
|
"""Nur aktive Vereinszugänge zählen für Mandant / Header-Validierung."""
|
|
|
|
|
cur = object()
|
|
|
|
|
ctx = resolve_tenant_context(
|
|
|
|
|
cur,
|
|
|
|
|
profile_id=9,
|
|
|
|
|
global_role="user",
|
|
|
|
|
header_raw=None,
|
|
|
|
|
memberships=[
|
|
|
|
|
{"id": 10, "membership_status": "inactive"},
|
|
|
|
|
{"id": 20, "membership_status": "active"},
|
|
|
|
|
],
|
|
|
|
|
stored_active_club_id=None,
|
|
|
|
|
invalid_header_policy="ignore",
|
2026-06-06 21:10:52 +02:00
|
|
|
email_verified=True,
|
2026-05-09 10:42:56 +02:00
|
|
|
)
|
|
|
|
|
assert ctx.club_ids == frozenset({20})
|
|
|
|
|
assert ctx.effective_club_id == 20
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_resolve_all_memberships_inactive_no_effective_club():
|
|
|
|
|
cur = object()
|
|
|
|
|
ctx = resolve_tenant_context(
|
|
|
|
|
cur,
|
|
|
|
|
profile_id=9,
|
|
|
|
|
global_role="user",
|
|
|
|
|
header_raw=None,
|
|
|
|
|
memberships=[{"id": 10, "membership_status": "inactive"}],
|
|
|
|
|
stored_active_club_id=10,
|
|
|
|
|
invalid_header_policy="ignore",
|
2026-06-06 21:10:52 +02:00
|
|
|
email_verified=True,
|
2026-05-09 10:42:56 +02:00
|
|
|
)
|
|
|
|
|
assert ctx.club_ids == frozenset()
|
|
|
|
|
assert ctx.effective_club_id is None
|