Some checks failed
Deploy Development / deploy (push) Successful in 43s
Test Suite / pytest-backend (push) Failing after 0s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 14s
Test Suite / k6 /health Baseline (push) Failing after 4m0s
Test Suite / playwright-tests (push) Failing after 3m41s
- Introduced `email_verified` and `account_state` attributes in the `TenantContext` to improve user state management. - Updated the `resolve_tenant_context` function to dynamically fetch `email_verified` status from the database and determine `account_state` based on user roles and memberships. - Implemented `assert_min_account_state` checks across various endpoints to enforce access control based on user account status. - Incremented version to 1.1.0 in version.py to reflect these enhancements in tenant context management and access control.
168 lines
4.8 KiB
Python
168 lines
4.8 KiB
Python
"""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,
|
|
email_verified=True,
|
|
)
|
|
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,
|
|
email_verified=True,
|
|
)
|
|
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,
|
|
email_verified=True,
|
|
)
|
|
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",
|
|
email_verified=True,
|
|
)
|
|
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",
|
|
email_verified=True,
|
|
)
|
|
assert ctx.club_ids == frozenset()
|
|
assert ctx.effective_club_id is None
|