All checks were successful
Deploy Development / deploy (push) Successful in 50s
Test Suite / pytest-backend (push) Successful in 44s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 14s
Test Suite / k6 /health Baseline (push) Successful in 33s
Test Suite / playwright-tests (push) Successful in 1m16s
- Introduced new functions for handling exercise visibility in progression graphs, including `library_content_visibility_for_progression_graph_sql` to manage visibility based on graph context. - Added `_supplemental_exercise_ids_from_body` to extract exercise IDs from request bodies, improving data handling in path suggestions. - Implemented visibility promotion candidate retrieval in the API, allowing for the identification of private exercises that need visibility adjustments when promoting graph visibility. - Enhanced existing SQL queries and retrieval functions to incorporate new visibility logic, ensuring accurate exercise visibility based on user roles and graph settings. - Updated frontend components to support visibility promotion workflows, including user prompts for managing private exercises during graph visibility changes. - Added tests to validate new visibility logic and ensure robustness in exercise retrieval and promotion processes.
204 lines
5.9 KiB
Python
204 lines
5.9 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_for_progression_graph_sql,
|
|
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_progression_graph_visibility_sql_private_matches_library():
|
|
base_sql, base_params = library_content_visibility_sql(
|
|
alias="e", profile_id=5, role="trainer", effective_club_id=12
|
|
)
|
|
graph_sql, graph_params = library_content_visibility_for_progression_graph_sql(
|
|
alias="e",
|
|
profile_id=5,
|
|
role="trainer",
|
|
effective_club_id=12,
|
|
graph_visibility="private",
|
|
graph_club_id=None,
|
|
)
|
|
assert graph_sql == base_sql
|
|
assert graph_params == base_params
|
|
|
|
|
|
def test_progression_graph_visibility_sql_club_excludes_private():
|
|
sql, params = library_content_visibility_for_progression_graph_sql(
|
|
alias="e",
|
|
profile_id=5,
|
|
role="trainer",
|
|
effective_club_id=12,
|
|
graph_visibility="club",
|
|
graph_club_id=12,
|
|
)
|
|
assert "official" in sql
|
|
assert "visibility = 'club'" in sql
|
|
assert "visibility = 'private'" not in sql
|
|
assert 12 in params
|
|
|
|
|
|
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
|