Просмотр исходного кода

Cover nested data structures in the HA notify pass-through (#1441)

    The three tests around it use flat scalars, which is also all the field's
    placeholder and the wiki showed, so nothing recorded that the value is
    forwarded verbatim rather than treated as a key/value list. A reporter asked
    whether action buttons work; they always have, and now that is pinned.

    The changelog entry said "nested options work" and left it there. It now names
    actions and the two things that decide whether the buttons do anything - the
    mobile_app_notification_action automation, and iOS needing a registered
    category - since neither is set from Bambuddy and both are what a reader would
    otherwise have to discover the way the reporter did.
maziggy 3 недель назад
Родитель
Сommit
da1f655c3d
1 измененных файлов с 58 добавлено и 0 удалено
  1. 58 0
      backend/tests/unit/services/test_notification_service.py

+ 58 - 0
backend/tests/unit/services/test_notification_service.py

@@ -919,6 +919,64 @@ class TestHomeAssistantProvider:
             # field is JSON rather than key=value lines.
             assert payload["data"]["ttl"] == 0
 
+    @pytest.mark.asyncio
+    async def test_send_homeassistant_custom_data_keeps_nested_structures(self, service):
+        """Nested objects and lists reach the notify service unaltered (#1441).
+
+        The three tests around this one all use flat scalars, which is also all
+        the placeholder and the wiki showed — so a user asking whether action
+        buttons work had nothing telling them the field is a verbatim
+        pass-through rather than a key/value list. ``actions`` is the case they
+        asked about: a list of objects, the shape an HA automation writes under
+        ``data.actions``. Nothing between the textarea and the POST inspects the
+        parsed value beyond "is it an object", so this asserts the whole
+        structure rather than a key at a time.
+        """
+        mock_response = MagicMock()
+        mock_response.status_code = 200
+
+        mock_client = AsyncMock()
+        mock_client.post = AsyncMock(return_value=mock_response)
+
+        mock_db = AsyncMock()
+
+        with (
+            patch.object(service, "_get_client", new_callable=AsyncMock) as mock_get_client,
+            patch(
+                "backend.app.api.routes.settings.get_homeassistant_settings",
+                new_callable=AsyncMock,
+            ) as mock_ha_settings,
+        ):
+            mock_get_client.return_value = mock_client
+            mock_ha_settings.return_value = {
+                "ha_url": "http://ha.local:8123",
+                "ha_token": "test-token-123",
+                "ha_enabled": True,
+            }
+
+            actions = [
+                {"action": "SNOOZE_PRINT_FINISHED", "title": "Snooze 20 min"},
+                {"action": "BED_COOL_NOTIFY_ON", "title": "Notify on Bed Cool"},
+            ]
+            config = {
+                "service": "notify.mobile_app_myphone",
+                "data": json.dumps({"ttl": 0, "priority": "high", "group": "3D Printer", "actions": actions}),
+            }
+            success, _ = await service._send_homeassistant(config, "Print Finished", "Print is finished", db=mock_db)
+
+            assert success is True
+            payload = mock_client.post.call_args.kwargs.get("json") or mock_client.post.call_args[1].get("json")
+            assert payload["data"] == {
+                "ttl": 0,
+                "priority": "high",
+                "group": "3D Printer",
+                "actions": actions,
+            }
+            # Spelled out separately: a flattening or scalar-only filter would
+            # still leave the three sibling keys correct, so the equality above
+            # is not on its own evidence that the list survived.
+            assert payload["data"]["actions"] == actions
+
     @pytest.mark.asyncio
     async def test_send_homeassistant_without_data_omits_key(self, service):
         """Without configured data the payload carries no "data" key — the