fix(api): settings PUT accepts booleans/numbers from the panel (#465)

* fix(api): settings PUT accepts the JSON scalars the panel sends

The feature-flags card sends booleans and numeric settings send numbers;
SettingUpdate.value was typed str, so pydantic 422'd on type before the
per-key validators ever ran (live: PUT /settings/notifications_enabled).
Scalars now coerce to the stored text form — bools to the 'true'/'false'
the validators parse.

* chore(docs): reflow hard-wrapped prose from the #401 merge

* chore(foundation): regenerate lifecycle artifacts; reflow inherited prose

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
This commit is contained in:
Renzo F
2026-07-11 09:21:21 +02:00
committed by GitHub
co-authored by Renn F
parent 53a028ec04
commit 50ec283533
2 changed files with 44 additions and 1 deletions
+27
View File
@@ -0,0 +1,27 @@
"""SettingUpdate accepts the JSON scalars the panel naturally sends.
The feature-flags card sends booleans (a live PUT
/settings/notifications_enabled 422'd on type alone) and numeric settings
send numbers; all values persist as text. Constructed via model_validate —
the wire shape the route actually receives.
"""
from __future__ import annotations
from roboco.api.schemas.settings import SettingUpdate
def test_bool_true_coerces_to_stored_form() -> None:
assert SettingUpdate.model_validate({"value": True}).value == "true"
def test_bool_false_coerces_to_stored_form() -> None:
assert SettingUpdate.model_validate({"value": False}).value == "false"
def test_int_coerces_to_text() -> None:
assert SettingUpdate.model_validate({"value": 30}).value == "30"
def test_plain_string_unchanged() -> None:
assert SettingUpdate.model_validate({"value": "qwen3"}).value == "qwen3"