refactor(api): shared serializer + ownership + write-scope helpers (R6 S1a)
Establish the R6 API-contract infra before the S1b payload:dict migration: - iso(dt) in utils.py replaces the ~16 hand-rolled `x.isoformat() if x else None` serializer idioms across the routes. - get_or_404 / owned_or_404 in routes/_common.py — one 404 + existence-leak policy; plex's _own_playlist_or_404 and saved_views' _own_view now delegate to it. - require_write_scope dependency in auth.py collapses the inline has_write_scope→403 checks (channels subscribe/unsubscribe, playlists push/push-plan) that had drifted into three different messages into one; the conditional delete_playlist guard stays inline (its message was already canonical). No response-shape change (iso == isoformat for a present value). Gate: ruff clean, pytest 163 green (+ test_utils for iso).
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
"""iso(): the single datetime→JSON serializer the routes share (R6 S1a)."""
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from app.utils import iso
|
||||
|
||||
|
||||
def test_none_maps_to_none():
|
||||
assert iso(None) is None
|
||||
|
||||
|
||||
def test_aware_datetime_keeps_offset():
|
||||
dt = datetime(2026, 7, 26, 12, 30, 0, tzinfo=timezone.utc)
|
||||
assert iso(dt) == "2026-07-26T12:30:00+00:00"
|
||||
|
||||
|
||||
def test_naive_datetime_serializes_without_offset():
|
||||
dt = datetime(2026, 7, 26, 12, 30, 0)
|
||||
assert iso(dt) == "2026-07-26T12:30:00"
|
||||
|
||||
|
||||
def test_matches_datetime_isoformat_contract():
|
||||
# iso is exactly `dt.isoformat()` for a present value — guards the routes that used to
|
||||
# hand-roll `x.isoformat() if x else None`.
|
||||
dt = datetime(2020, 1, 2, 3, 4, 5, 678901, tzinfo=timezone.utc)
|
||||
assert iso(dt) == dt.isoformat()
|
||||
Reference in New Issue
Block a user