fix(p06): declared-Status deckt alle Sichtbarkeiten ab (kein Level-Vergleich mehr)
All checks were successful
Deploy Development / deploy (push) Successful in 39s
Test Suite / pytest-backend (push) Successful in 34s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 10s
Test Suite / playwright-tests (push) Successful in 58s
All checks were successful
Deploy Development / deploy (push) Successful in 39s
Test Suite / pytest-backend (push) Successful in 34s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 10s
Test Suite / playwright-tests (push) Successful in 58s
- check_rights_coverage: rights_status='declared' gibt immer 'ok' zurück (P-06-Erklärung gilt inhaltlich, nicht sichtbarkeitsabhängig) - assert_rights_for_promotion: 'insufficient'-Pfad entfernt - Tests: test_declared_private_insufficient_for_club → test_declared_covers_any_visibility version: 0.8.81 module: media_rights Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
6586d3b68b
commit
56e952f084
|
|
@ -159,37 +159,30 @@ def check_rights_coverage(cur: Any, asset_id: int, target_visibility: str) -> st
|
|||
|
||||
Returns:
|
||||
'ok' - vorhandene Erklaerung reicht aus
|
||||
'insufficient' - Erklaerung vorhanden, aber fuer niedrigere Sichtbarkeit
|
||||
'legacy' - Altmedium ohne Erklaerung
|
||||
'legacy' - Altmedium ohne Erklaerung (legacy_unreviewed)
|
||||
'blocked' - durch Admin gesperrt
|
||||
'no_declaration' - neues Medium ohne Erklaerung (sollte nicht vorkommen)
|
||||
|
||||
Hinweis: Eine P-06-Erklaerung beschreibt den Inhalt (Rechteinhaber, Personen, Musik etc.)
|
||||
und ist sichtbarkeitsunabhaengig. rights_status='declared' gilt daher fuer alle
|
||||
Sichtbarkeits-Stufen ohne Levelvergleich.
|
||||
"""
|
||||
cur.execute(
|
||||
"SELECT rights_status, rights_declared_for_visibility FROM media_assets WHERE id = %s",
|
||||
"SELECT rights_status FROM media_assets WHERE id = %s",
|
||||
(asset_id,),
|
||||
)
|
||||
row = cur.fetchone()
|
||||
if not row:
|
||||
return "no_declaration"
|
||||
|
||||
# psycopg2 RealDictCursor oder ähnlich
|
||||
if hasattr(row, "keys"):
|
||||
rs = row["rights_status"]
|
||||
rdv = row["rights_declared_for_visibility"]
|
||||
else:
|
||||
rs, rdv = row[0], row[1]
|
||||
|
||||
rs = (rs or "").strip().lower()
|
||||
rdv = (rdv or "").strip().lower() if rdv else None
|
||||
rs = (row[0] if not hasattr(row, "keys") else row["rights_status"] or "").strip().lower()
|
||||
|
||||
if rs == "blocked":
|
||||
return "blocked"
|
||||
if rs == "legacy_unreviewed":
|
||||
return "legacy"
|
||||
if rs == "declared":
|
||||
if rights_covers_target(rdv, target_visibility):
|
||||
return "ok"
|
||||
return "insufficient"
|
||||
return "ok"
|
||||
return "no_declaration"
|
||||
|
||||
|
||||
|
|
@ -210,19 +203,6 @@ def assert_rights_for_promotion(cur: Any, asset_id: int, target_visibility: str)
|
|||
"asset_id": asset_id,
|
||||
},
|
||||
)
|
||||
if status == "insufficient":
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail={
|
||||
"code": "RIGHTS_SCOPE_INSUFFICIENT",
|
||||
"message": (
|
||||
f"Die vorhandene Erklaerung gilt nicht fuer die Ziel-Sichtbarkeit '{target_visibility}'. "
|
||||
"Bitte eine neue Erklaerung fuer diese Sichtbarkeit abgeben."
|
||||
),
|
||||
"asset_id": asset_id,
|
||||
"target_visibility": target_visibility,
|
||||
},
|
||||
)
|
||||
if status == "blocked":
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
|
|
|
|||
|
|
@ -215,28 +215,18 @@ class TestCheckRightsCoverage:
|
|||
assert check_rights_coverage(cur, 1, "private") == "no_declaration"
|
||||
|
||||
def test_blocked_returns_blocked(self):
|
||||
cur = self._cur({"rights_status": "blocked", "rights_declared_for_visibility": None})
|
||||
cur = self._cur({"rights_status": "blocked"})
|
||||
assert check_rights_coverage(cur, 1, "private") == "blocked"
|
||||
|
||||
def test_legacy_returns_legacy(self):
|
||||
cur = self._cur({"rights_status": "legacy_unreviewed", "rights_declared_for_visibility": None})
|
||||
cur = self._cur({"rights_status": "legacy_unreviewed"})
|
||||
assert check_rights_coverage(cur, 1, "club") == "legacy"
|
||||
|
||||
def test_declared_private_covers_private(self):
|
||||
cur = self._cur({"rights_status": "declared", "rights_declared_for_visibility": "private"})
|
||||
assert check_rights_coverage(cur, 1, "private") == "ok"
|
||||
|
||||
def test_declared_private_insufficient_for_club(self):
|
||||
cur = self._cur({"rights_status": "declared", "rights_declared_for_visibility": "private"})
|
||||
assert check_rights_coverage(cur, 1, "club") == "insufficient"
|
||||
|
||||
def test_declared_official_covers_all(self):
|
||||
cur = self._cur({"rights_status": "declared", "rights_declared_for_visibility": "official"})
|
||||
assert check_rights_coverage(cur, 1, "private") == "ok"
|
||||
cur2 = self._cur({"rights_status": "declared", "rights_declared_for_visibility": "official"})
|
||||
assert check_rights_coverage(cur2, 1, "club") == "ok"
|
||||
cur3 = self._cur({"rights_status": "declared", "rights_declared_for_visibility": "official"})
|
||||
assert check_rights_coverage(cur3, 1, "official") == "ok"
|
||||
def test_declared_covers_any_visibility(self):
|
||||
# Eine bestehende Erklaerung gilt sichtbarkeitsunabhaengig
|
||||
for target in ("private", "club", "official"):
|
||||
cur = self._cur({"rights_status": "declared"})
|
||||
assert check_rights_coverage(cur, 1, target) == "ok", f"failed for target={target}"
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
|
|
@ -250,31 +240,25 @@ class TestAssertRightsForPromotion:
|
|||
cur.fetchone.return_value = row
|
||||
return cur
|
||||
|
||||
def test_ok_passes(self):
|
||||
cur = self._cur({"rights_status": "declared", "rights_declared_for_visibility": "official"})
|
||||
assert_rights_for_promotion(cur, 1, "official") # no raise
|
||||
def test_ok_passes_for_any_target(self):
|
||||
for target in ("private", "club", "official"):
|
||||
cur = self._cur({"rights_status": "declared"})
|
||||
assert_rights_for_promotion(cur, 1, target) # no raise
|
||||
|
||||
def test_legacy_raises_legacy_code(self):
|
||||
cur = self._cur({"rights_status": "legacy_unreviewed", "rights_declared_for_visibility": None})
|
||||
cur = self._cur({"rights_status": "legacy_unreviewed"})
|
||||
with pytest.raises(HTTPException) as exc:
|
||||
assert_rights_for_promotion(cur, 1, "club")
|
||||
assert exc.value.status_code == 400
|
||||
assert exc.value.detail["code"] == "LEGACY_REDECLARATION_REQUIRED"
|
||||
|
||||
def test_blocked_raises_403(self):
|
||||
cur = self._cur({"rights_status": "blocked", "rights_declared_for_visibility": None})
|
||||
cur = self._cur({"rights_status": "blocked"})
|
||||
with pytest.raises(HTTPException) as exc:
|
||||
assert_rights_for_promotion(cur, 1, "official")
|
||||
assert exc.value.status_code == 403
|
||||
assert exc.value.detail["code"] == "RIGHTS_BLOCKED"
|
||||
|
||||
def test_insufficient_raises_scope_code(self):
|
||||
cur = self._cur({"rights_status": "declared", "rights_declared_for_visibility": "private"})
|
||||
with pytest.raises(HTTPException) as exc:
|
||||
assert_rights_for_promotion(cur, 1, "official")
|
||||
assert exc.value.status_code == 400
|
||||
assert exc.value.detail["code"] == "RIGHTS_SCOPE_INSUFFICIENT"
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# 4. PATCH /api/media-assets/{id} – P-06-Promotion via HTTP
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Shinkan Jinkendo Version Information
|
||||
|
||||
APP_VERSION = "0.8.80"
|
||||
APP_VERSION = "0.8.81"
|
||||
BUILD_DATE = "2026-05-11"
|
||||
DB_SCHEMA_VERSION = "20260511048"
|
||||
|
||||
|
|
@ -31,6 +31,13 @@ MODULE_VERSIONS = {
|
|||
}
|
||||
|
||||
CHANGELOG = [
|
||||
{
|
||||
"version": "0.8.81",
|
||||
"date": "2026-05-11",
|
||||
"changes": [
|
||||
"Fix P-06: check_rights_coverage gibt bei rights_status='declared' immer 'ok' zurueck — Erklaerung gilt sichtbarkeitsunabhaengig (Inhalt aendert sich nicht durch Sichtbarkeits-Promotion). 'insufficient'-Pfad entfernt. Tests angepasst.",
|
||||
],
|
||||
},
|
||||
{
|
||||
"version": "0.8.80",
|
||||
"date": "2026-05-11",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Shinkan Jinkendo Frontend Version
|
||||
|
||||
export const APP_VERSION = "0.8.80"
|
||||
export const APP_VERSION = "0.8.81"
|
||||
export const BUILD_DATE = "2026-05-11"
|
||||
|
||||
export const PAGE_VERSIONS = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user