Skip to content

Commit 11aec83

Browse files
kingpanther13claude
andcommitted
test(e2e): pin the #2254 partial-options wipe against a real options flow
The existing options round-trip could not catch it: group's LIGHT options are all vol.Required, and required fields were already backfilled from the step's suggestion. group_type=sensor is the reachable repro -- its schema adds vol.Optional(ignore_non_numeric, default=False), the optional + static-default shape that voluptuous silently refills when the key is omitted. Complements the unit suite rather than repeating it: those pin the payload against a hand-written copy of HA's serialization and would keep passing if HA changed how it emits suggested_value. Verified to fail pre-fix (field omitted, persisted False) and pass post-fix (True submitted and kept). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012d29UJTiH4Uy2Pm37SBPtr
1 parent 8c46e76 commit 11aec83

1 file changed

Lines changed: 86 additions & 0 deletions

File tree

tests/src/e2e/workflows/integrations/test_integration_management.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,92 @@ async def test_add_integration_and_update_options_cycle(self, mcp_client):
220220
{"target": entry_id, "confirm": True},
221221
)
222222

223+
async def test_partial_options_submit_keeps_unnamed_fields(self, mcp_client):
224+
"""A one-field options patch must not reset the fields it omits (#2254).
225+
226+
The bug: the walker submitted ONLY caller-named keys, so every field
227+
the caller left out went back to whatever voluptuous substitutes for an
228+
absent key — its static schema default — while the tool still reported
229+
success. The sibling test above never caught it because ``group``'s
230+
light options are all ``vol.Required``, and required fields were
231+
already backfilled from the step's own suggestion.
232+
233+
``group`` with ``group_type="sensor"`` is the reachable repro: its
234+
options schema extends the basic one with
235+
``vol.Optional(CONF_IGNORE_NON_NUMERIC, default=False)``, an OPTIONAL
236+
field carrying a static default. Set it true, patch a different field,
237+
and pre-fix it silently fell back to false. Deliberately an e2e rather
238+
than a unit test: the unit suite pins the payload against a
239+
hand-written copy of HA's schema serialization, so it would keep
240+
passing if HA changed how it serializes ``suggested_value``. Only a
241+
real options flow proves the values actually survive the round trip.
242+
"""
243+
create_result = await mcp_client.call_tool(
244+
"ha_set_integration",
245+
{
246+
"domain": "group",
247+
"config": {
248+
"group_type": "sensor",
249+
"name": "test_partial_options_2254_e2e",
250+
"entities": [],
251+
"hide_members": False,
252+
"type": "max",
253+
"ignore_non_numeric": True,
254+
},
255+
},
256+
)
257+
data = assert_mcp_success(create_result, "Add sensor group for #2254")
258+
entry_id = data["entry_id"]
259+
260+
try:
261+
await wait_for_tool_result(
262+
mcp_client,
263+
tool_name="ha_get_integration",
264+
arguments={"entry_id": entry_id},
265+
predicate=lambda d: (
266+
d.get("entry", {}).get("options", {}).get("ignore_non_numeric")
267+
is True
268+
),
269+
description="baseline option is set before the partial patch",
270+
)
271+
272+
# The patch under test: name ONLY 'type'. Every other field in the
273+
# step is left out, which is what used to reset them.
274+
update_data = assert_mcp_success(
275+
await mcp_client.call_tool(
276+
"ha_set_integration",
277+
{"entry_id": entry_id, "config": {"type": "min"}},
278+
),
279+
"Partial options patch",
280+
)
281+
assert update_data.get("updated") is True
282+
283+
verify_data = await wait_for_tool_result(
284+
mcp_client,
285+
tool_name="ha_get_integration",
286+
arguments={"entry_id": entry_id},
287+
predicate=lambda d: (
288+
d.get("entry", {}).get("options", {}).get("type") == "min"
289+
),
290+
description="the patched field took effect",
291+
)
292+
options = verify_data["entry"]["options"]
293+
294+
# The regression assert: pre-#2254 this was False, silently reset
295+
# from the static schema default because the key was omitted.
296+
assert options.get("ignore_non_numeric") is True, (
297+
"Partial options submit reset 'ignore_non_numeric' to its "
298+
"schema default — the #2254 wipe is back. Fields the caller "
299+
f"never named must survive the patch. Got options: {options}"
300+
)
301+
assert options.get("type") == "min"
302+
finally:
303+
await safe_call_tool(
304+
mcp_client,
305+
"ha_remove_helpers_integrations",
306+
{"target": entry_id, "confirm": True},
307+
)
308+
223309
async def test_add_integration_unknown_domain_fails(self, mcp_client):
224310
"""Add mode surfaces a structured error for an unknown domain."""
225311
data = await safe_call_tool(

0 commit comments

Comments
 (0)