Reject non-integer weekday values in presence pattern requests
The create and edit pattern models typed weekday values as lax int, so JSON true, "1" and 1.0 were coerced to weekday 1. Type them StrictInt so each is a 422 naming the pattern field, and add create and edit regression cases for bool, string, float and out-of-range weekdays. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+5
-4
@@ -623,15 +623,16 @@ class AdminPresencePatternCreateRequest(BaseModel):
|
|||||||
covers that week (Monday=0); every week of the cycle must be
|
covers that week (Monday=0); every week of the cycle must be
|
||||||
covered, which the PRESENCE MODEL enforces at construction, not
|
covered, which the PRESENCE MODEL enforces at construction, not
|
||||||
this shape — as it does the non-blank ``name`` and the
|
this shape — as it does the non-blank ``name`` and the
|
||||||
``home``/``away`` ``kind``. ``cycle_length_weeks`` is a strict
|
``home``/``away`` ``kind``. ``cycle_length_weeks`` and the weekday
|
||||||
JSON integer: ``2.0`` or ``"2"`` is a 422, not a silent coercion.
|
values are strict JSON integers: ``2.0``, ``"2"`` or ``true`` is a
|
||||||
|
422, not a silent coercion.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
kind: str
|
kind: str
|
||||||
cycle_length_weeks: StrictInt
|
cycle_length_weeks: StrictInt
|
||||||
anchor_date: str
|
anchor_date: str
|
||||||
pattern: dict[int, list[int]]
|
pattern: dict[int, list[StrictInt]]
|
||||||
|
|
||||||
|
|
||||||
class AdminPresencePatternEditRequest(BaseModel):
|
class AdminPresencePatternEditRequest(BaseModel):
|
||||||
@@ -646,7 +647,7 @@ class AdminPresencePatternEditRequest(BaseModel):
|
|||||||
kind: str | None = None
|
kind: str | None = None
|
||||||
cycle_length_weeks: StrictInt | None = None
|
cycle_length_weeks: StrictInt | None = None
|
||||||
anchor_date: str | None = None
|
anchor_date: str | None = None
|
||||||
pattern: dict[int, list[int]] | None = None
|
pattern: dict[int, list[StrictInt]] | None = None
|
||||||
|
|
||||||
|
|
||||||
class AdminPresencePatternResponse(BaseModel):
|
class AdminPresencePatternResponse(BaseModel):
|
||||||
|
|||||||
@@ -690,6 +690,12 @@ async def test_pattern_patch_unknown_id_is_404(
|
|||||||
({"anchor_date": None}, "anchor_date"),
|
({"anchor_date": None}, "anchor_date"),
|
||||||
({"pattern": {}}, "pattern"),
|
({"pattern": {}}, "pattern"),
|
||||||
({"pattern": None}, "pattern"),
|
({"pattern": None}, "pattern"),
|
||||||
|
({"pattern": {"0": [7]}}, "pattern"), # weekday out of 0..6
|
||||||
|
# Strict JSON integer weekdays: no bool, string or float coercion.
|
||||||
|
({"pattern": {"0": [True]}}, "pattern"),
|
||||||
|
({"pattern": {"0": [False]}}, "pattern"),
|
||||||
|
({"pattern": {"0": ["1"]}}, "pattern"),
|
||||||
|
({"pattern": {"0": [1.0]}}, "pattern"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_rejected_pattern_patch_is_422_and_changes_nothing(
|
async def test_rejected_pattern_patch_is_422_and_changes_nothing(
|
||||||
@@ -882,6 +888,11 @@ async def test_pattern_and_override_for_unknown_child_are_404(
|
|||||||
(_pattern_body(anchor_date="2026-13-40"), "anchor_date"),
|
(_pattern_body(anchor_date="2026-13-40"), "anchor_date"),
|
||||||
(_pattern_body(anchor_date="05/01/2026"), "anchor_date"),
|
(_pattern_body(anchor_date="05/01/2026"), "anchor_date"),
|
||||||
(_pattern_body(pattern={"0": [7]}), "pattern"), # weekday out of 0..6
|
(_pattern_body(pattern={"0": [7]}), "pattern"), # weekday out of 0..6
|
||||||
|
# Strict JSON integer weekdays: no bool, string or float coercion.
|
||||||
|
(_pattern_body(pattern={"0": [True]}), "pattern"),
|
||||||
|
(_pattern_body(pattern={"0": [False]}), "pattern"),
|
||||||
|
(_pattern_body(pattern={"0": ["1"]}), "pattern"),
|
||||||
|
(_pattern_body(pattern={"0": [1.0]}), "pattern"),
|
||||||
(_pattern_body(pattern={"nope": [0]}), "pattern"), # non-int week
|
(_pattern_body(pattern={"nope": [0]}), "pattern"), # non-int week
|
||||||
(_pattern_body(pattern={"0": "0,2,4"}), "pattern"), # not a list
|
(_pattern_body(pattern={"0": "0,2,4"}), "pattern"), # not a list
|
||||||
(_pattern_body(kind="sometimes"), "kind"),
|
(_pattern_body(kind="sometimes"), "kind"),
|
||||||
|
|||||||
Reference in New Issue
Block a user