"""Unit tests ohne Datenbank für die Zugriffsschicht (Visibility-SQL, Header-Parsing).""" import pytest from fastapi import HTTPException from tenant_context import library_content_visibility_sql, parse_active_club_header, resolve_tenant_context def test_library_visibility_sql_platform_admin_restricts_club_by_membership(): sql, params = library_content_visibility_sql( alias="e", profile_id=1, role="admin", effective_club_id=None, ) assert "e.visibility = 'official'" in sql assert "club_members" in sql assert params == [1, 1] def test_library_visibility_sql_superadmin_uses_membership_clause_for_club_visibility(): sql, params = library_content_visibility_sql( alias="fp", profile_id=2, role="superadmin", effective_club_id=100, ) assert "club_members" in sql assert params == [2, 2] 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 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, ) 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, ) 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, ) assert ctx.effective_club_id is None 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", ) 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", ) assert ctx.club_ids == frozenset() assert ctx.effective_club_id is None