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
|
||||
covered, which the PRESENCE MODEL enforces at construction, not
|
||||
this shape — as it does the non-blank ``name`` and the
|
||||
``home``/``away`` ``kind``. ``cycle_length_weeks`` is a strict
|
||||
JSON integer: ``2.0`` or ``"2"`` is a 422, not a silent coercion.
|
||||
``home``/``away`` ``kind``. ``cycle_length_weeks`` and the weekday
|
||||
values are strict JSON integers: ``2.0``, ``"2"`` or ``true`` is a
|
||||
422, not a silent coercion.
|
||||
"""
|
||||
|
||||
name: str
|
||||
kind: str
|
||||
cycle_length_weeks: StrictInt
|
||||
anchor_date: str
|
||||
pattern: dict[int, list[int]]
|
||||
pattern: dict[int, list[StrictInt]]
|
||||
|
||||
|
||||
class AdminPresencePatternEditRequest(BaseModel):
|
||||
@@ -646,7 +647,7 @@ class AdminPresencePatternEditRequest(BaseModel):
|
||||
kind: str | None = None
|
||||
cycle_length_weeks: StrictInt | None = None
|
||||
anchor_date: str | None = None
|
||||
pattern: dict[int, list[int]] | None = None
|
||||
pattern: dict[int, list[StrictInt]] | None = None
|
||||
|
||||
|
||||
class AdminPresencePatternResponse(BaseModel):
|
||||
|
||||
@@ -690,6 +690,12 @@ async def test_pattern_patch_unknown_id_is_404(
|
||||
({"anchor_date": None}, "anchor_date"),
|
||||
({"pattern": {}}, "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(
|
||||
@@ -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="05/01/2026"), "anchor_date"),
|
||||
(_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={"0": "0,2,4"}), "pattern"), # not a list
|
||||
(_pattern_body(kind="sometimes"), "kind"),
|
||||
|
||||
Reference in New Issue
Block a user