shinkan-jinkendo/backend/tests/test_official_exercise_media_rules.py
Lars 95f5b0b2d7
All checks were successful
Deploy Development / deploy (push) Successful in 35s
Test Suite / pytest-backend (push) Successful in 24s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 6s
Test Suite / playwright-tests (push) Successful in 22s
feat: implement official exercise media management and copyright validation
- Enhanced exercise update functionality to support the promotion of attached media assets to 'official' status, requiring active visibility and copyright validation.
- Updated backend API to handle new fields for promoting media and setting default copyright notices during exercise updates.
- Improved frontend error handling to prompt users for confirmation when promoting media assets, including checks for copyright compliance.
- Incremented version to 0.8.47, reflecting the latest changes in media management and governance.
2026-05-07 13:34:22 +02:00

108 lines
3.1 KiB
Python

"""§4.2: apply_official_exercise_media_rules — Lifecycle, Sichtbarkeit, Copyright."""
from __future__ import annotations
import os
from unittest.mock import MagicMock
import pytest
from fastapi import HTTPException
os.environ.setdefault("SKIP_DB_MIGRATE", "1")
from routers.exercises import apply_official_exercise_media_rules
def _row(
aid: int,
*,
vis: str = "private",
lifecycle: str = "active",
copyright_notice: str | None = "",
name: str = "f.bin",
) -> dict:
return {
"id": aid,
"visibility": vis,
"club_id": 1,
"lifecycle_state": lifecycle,
"copyright_notice": copyright_notice,
"original_filename": name,
}
def test_non_official_visibility_noop() -> None:
cur = MagicMock()
apply_official_exercise_media_rules(
cur,
1,
"private",
promote_attached_media=False,
default_official_media_copyright=None,
)
cur.execute.assert_not_called()
def test_lifecycle_not_active_422() -> None:
cur = MagicMock()
cur.fetchall.return_value = [_row(1, lifecycle="trash_soft", copyright_notice="abc")]
with pytest.raises(HTTPException) as ei:
apply_official_exercise_media_rules(
cur,
1,
"official",
promote_attached_media=False,
default_official_media_copyright=None,
)
assert ei.value.status_code == 422
assert ei.value.detail["code"] == "OFFICIAL_MEDIA_LIFECYCLE"
def test_visibility_promotion_confirm_422() -> None:
cur = MagicMock()
cur.fetchall.return_value = [_row(1, vis="club", copyright_notice="halten")]
with pytest.raises(HTTPException) as ei:
apply_official_exercise_media_rules(
cur,
1,
"official",
promote_attached_media=False,
default_official_media_copyright=None,
)
assert ei.value.status_code == 422
assert ei.value.detail["code"] == "OFFICIAL_MEDIA_CONFIRM_REQUIRED"
assert ei.value.detail["assets_need_visibility_promotion"]
def test_copyright_required_422() -> None:
cur = MagicMock()
cur.fetchall.return_value = [_row(1, vis="official", copyright_notice="")]
with pytest.raises(HTTPException) as ei:
apply_official_exercise_media_rules(
cur,
1,
"official",
promote_attached_media=True,
default_official_media_copyright=None,
)
assert ei.value.status_code == 422
assert ei.value.detail["code"] == "OFFICIAL_MEDIA_CONFIRM_REQUIRED"
assert ei.value.detail["assets_missing_copyright"]
def test_promote_and_fill_copyright_updates() -> None:
cur = MagicMock()
cur.fetchall.return_value = [_row(1, vis="private", copyright_notice=" ")]
apply_official_exercise_media_rules(
cur,
42,
"official",
promote_attached_media=True,
default_official_media_copyright="© Test Holding",
)
assert cur.execute.call_count == 3
sql1 = cur.execute.call_args_list[1][0][0]
sql2 = cur.execute.call_args_list[2][0][0]
assert "visibility = 'official'" in sql1
assert "copyright_notice" in sql2