@@ -53,7 +53,7 @@ class TestValidateIdentifierNotEmptyHelper:
5353 "\r " ,
5454 "\v " ,
5555 "\xa0 " , # non-breaking space (U+00A0)
56- " " , # ideographic space (U+3000)
56+ "\u3000 " , # ideographic space (U+3000)
5757 ],
5858 )
5959 def test_rejects_empty_or_whitespace (self , bad ):
@@ -219,9 +219,7 @@ async def test_get_rejects_empty_category_id(self, tools, bad):
219219 @pytest .mark .parametrize ("bad" , ["" , " " ])
220220 async def test_remove_rejects_empty_category_id (self , tools , bad ):
221221 with pytest .raises (ToolError ) as excinfo :
222- await tools .ha_config_remove_category (
223- scope = "automation" , category_id = bad
224- )
222+ await tools .ha_config_remove_category (scope = "automation" , category_id = bad )
225223 _assert_invalid_param (excinfo )
226224 tools ._client .send_websocket_message .assert_not_called ()
227225
@@ -327,9 +325,7 @@ def decorator(fn):
327325 async def test_create_rejects_whitespace_name (self , register_tools , mock_ws_client ):
328326 set_helper = register_tools ["ha_config_set_helper" ]
329327 with pytest .raises (ToolError ) as excinfo :
330- await set_helper (
331- helper_type = "input_boolean" , action = "create" , name = " "
332- )
328+ await set_helper (helper_type = "input_boolean" , action = "create" , name = " " )
333329 _assert_invalid_param (excinfo )
334330 mock_ws_client .send_websocket_message .assert_not_called ()
335331
@@ -364,9 +360,7 @@ async def test_implicit_action_with_empty_helper_id_rejects(
364360 for bad in ("" , " " ):
365361 mock_ws_client .send_websocket_message .reset_mock ()
366362 with pytest .raises (ToolError ) as excinfo :
367- await set_helper (
368- helper_type = helper_type , helper_id = bad , name = "X"
369- )
363+ await set_helper (helper_type = helper_type , helper_id = bad , name = "X" )
370364 _assert_invalid_param (excinfo )
371365 mock_ws_client .send_websocket_message .assert_not_called ()
372366
@@ -378,9 +372,7 @@ async def test_flow_helper_create_rejects_whitespace_name(
378372 # downstream config-flow build proceeds with a name HA cannot use.
379373 set_helper = register_tools ["ha_config_set_helper" ]
380374 with pytest .raises (ToolError ) as excinfo :
381- await set_helper (
382- helper_type = "utility_meter" , action = "create" , name = " "
383- )
375+ await set_helper (helper_type = "utility_meter" , action = "create" , name = " " )
384376 _assert_invalid_param (excinfo )
385377 # The pre-flow gate runs before any flow start — no WS round-trip.
386378 mock_ws_client .send_websocket_message .assert_not_called ()
@@ -439,9 +431,7 @@ async def test_skips_ws_call_on_empty_or_whitespace_name(
439431 from ha_mcp .tools .tools_config_helpers import _check_name_collision
440432
441433 # The early-return runs before any WS message is constructed.
442- result = await _check_name_collision (
443- mock_ws_client , "input_boolean" , bad_name
444- )
434+ result = await _check_name_collision (mock_ws_client , "input_boolean" , bad_name )
445435 assert result is None
446436 mock_ws_client .send_websocket_message .assert_not_called ()
447437
@@ -562,9 +552,7 @@ async def test_get_rejects_empty_identifier(self, tools, bad):
562552 with pytest .raises (ToolError ) as excinfo :
563553 await tools .ha_config_get_automation (identifier = bad )
564554 _assert_invalid_param (excinfo )
565- assert '"parameter": "identifier"' in str (excinfo .value ), str (
566- excinfo .value
567- )
555+ assert '"parameter": "identifier"' in str (excinfo .value ), str (excinfo .value )
568556 tools ._client .send_websocket_message .assert_not_called ()
569557 # Also no REST GET on the automation config.
570558 tools ._client .get_automation_config .assert_not_called ()
@@ -583,9 +571,7 @@ async def test_set_rejects_empty_identifier_on_update(self, tools, bad):
583571 identifier = bad ,
584572 )
585573 _assert_invalid_param (excinfo )
586- assert '"parameter": "identifier"' in str (excinfo .value ), str (
587- excinfo .value
588- )
574+ assert '"parameter": "identifier"' in str (excinfo .value ), str (excinfo .value )
589575 # Guard fires before any upsert / WS round-trip.
590576 tools ._client .upsert_automation_config .assert_not_called ()
591577
@@ -638,9 +624,7 @@ async def test_get_rejects_empty_script_id(self, tools, bad):
638624 with pytest .raises (ToolError ) as excinfo :
639625 await tools .ha_config_get_script (script_id = bad )
640626 _assert_invalid_param (excinfo )
641- assert '"parameter": "script_id"' in str (excinfo .value ), str (
642- excinfo .value
643- )
627+ assert '"parameter": "script_id"' in str (excinfo .value ), str (excinfo .value )
644628 tools ._client .get_script_config .assert_not_called ()
645629
646630 @pytest .mark .parametrize ("bad" , ["" , " " ])
@@ -654,9 +638,7 @@ async def test_set_rejects_empty_script_id(self, tools, bad):
654638 config = {"sequence" : [{"delay" : {"seconds" : 1 }}]},
655639 )
656640 _assert_invalid_param (excinfo )
657- assert '"parameter": "script_id"' in str (excinfo .value ), str (
658- excinfo .value
659- )
641+ assert '"parameter": "script_id"' in str (excinfo .value ), str (excinfo .value )
660642 tools ._client .upsert_script_config .assert_not_called ()
661643
662644
@@ -689,9 +671,7 @@ def decorator(fn):
689671
690672class TestDashboardsIdentifierValidation :
691673 @pytest .mark .parametrize ("bad" , ["" , " " ])
692- async def test_get_dashboard_rejects_empty_url_path (
693- self , mock_ws_client , bad
694- ):
674+ async def test_get_dashboard_rejects_empty_url_path (self , mock_ws_client , bad ):
695675 # ``url_path`` is optional (omit + ``list_only=True`` lists all).
696676 # When provided, empty/whitespace would slip past the list_only
697677 # check and reach the search-mode / get-mode WS dispatch. Guard
@@ -702,9 +682,7 @@ async def test_get_dashboard_rejects_empty_url_path(
702682 with pytest .raises (ToolError ) as excinfo :
703683 await ha_config_get_dashboard (url_path = bad )
704684 _assert_invalid_param (excinfo )
705- assert '"parameter": "url_path"' in str (excinfo .value ), str (
706- excinfo .value
707- )
685+ assert '"parameter": "url_path"' in str (excinfo .value ), str (excinfo .value )
708686 mock_ws_client .send_websocket_message .assert_not_called ()
709687
710688 async def test_get_dashboard_list_only_skips_guard (self , mock_ws_client ):
@@ -764,9 +742,7 @@ async def test_get_dashboard_with_none_url_path_routes_to_default(
764742 )
765743
766744 @pytest .mark .parametrize ("bad" , ["" , " " ])
767- async def test_set_dashboard_rejects_empty_url_path (
768- self , mock_ws_client , bad
769- ):
745+ async def test_set_dashboard_rejects_empty_url_path (self , mock_ws_client , bad ):
770746 # ``url_path`` is required for set_dashboard. Empty/whitespace
771747 # would pass the ``"default"`` alias (False), reach the
772748 # pre-resolver / hyphen-check, and surface as a misleading
@@ -778,15 +754,11 @@ async def test_set_dashboard_rejects_empty_url_path(
778754 with pytest .raises (ToolError ) as excinfo :
779755 await ha_config_set_dashboard (url_path = bad , config = {"views" : []})
780756 _assert_invalid_param (excinfo )
781- assert '"parameter": "url_path"' in str (excinfo .value ), str (
782- excinfo .value
783- )
757+ assert '"parameter": "url_path"' in str (excinfo .value ), str (excinfo .value )
784758 mock_ws_client .send_websocket_message .assert_not_called ()
785759
786760 @pytest .mark .parametrize ("bad" , ["" , " " ])
787- async def test_delete_dashboard_rejects_empty_url_path (
788- self , mock_ws_client , bad
789- ):
761+ async def test_delete_dashboard_rejects_empty_url_path (self , mock_ws_client , bad ):
790762 # ``url_path`` is required for delete_dashboard. Empty/whitespace
791763 # would reach ``_resolve_dashboard`` and surface as a misleading
792764 # "no dashboard found" — the guard names the actual problem
@@ -797,9 +769,7 @@ async def test_delete_dashboard_rejects_empty_url_path(
797769 with pytest .raises (ToolError ) as excinfo :
798770 await ha_config_delete_dashboard (url_path = bad )
799771 _assert_invalid_param (excinfo )
800- assert '"parameter": "url_path"' in str (excinfo .value ), str (
801- excinfo .value
802- )
772+ assert '"parameter": "url_path"' in str (excinfo .value ), str (excinfo .value )
803773 mock_ws_client .send_websocket_message .assert_not_called ()
804774
805775
@@ -835,9 +805,7 @@ async def test_set_rejects_empty_object_id(self, tools, bad):
835805 # misleading HA service-call failure. Symmetric with the
836806 # ``ha_config_remove_group`` pre-flight added in this PR.
837807 with pytest .raises (ToolError ) as excinfo :
838- await tools .ha_config_set_group (
839- object_id = bad , entities = ["light.example" ]
840- )
808+ await tools .ha_config_set_group (object_id = bad , entities = ["light.example" ])
841809 _assert_invalid_param (excinfo )
842810 assert '"parameter": "object_id"' in str (excinfo .value ), str (excinfo .value )
843811 tools ._client .call_service .assert_not_called ()
@@ -958,9 +926,7 @@ async def test_delete_helpers_integrations_rejects_empty_target(
958926 tools ._client .delete_config_entry .assert_not_called ()
959927
960928 @pytest .mark .parametrize ("bad" , ["" , " " ])
961- async def test_set_integration_enabled_rejects_empty_entry_id (
962- self , tools , bad
963- ):
929+ async def test_set_integration_enabled_rejects_empty_entry_id (self , tools , bad ):
964930 # ``entry_id`` is passed straight into ``config_entries/disable``;
965931 # without the guard, ``entry_id=""`` would surface as a misleading
966932 # HA "config entry not found".
@@ -1012,17 +978,13 @@ async def test_remove_item_rejects_empty_item(self, tools, bad):
1012978 # new guard, ``item=""`` would surface as a misleading HA
1013979 # "item not found".
1014980 with pytest .raises (ToolError ) as excinfo :
1015- await tools .ha_remove_todo_item (
1016- entity_id = "todo.shopping_list" , item = bad
1017- )
981+ await tools .ha_remove_todo_item (entity_id = "todo.shopping_list" , item = bad )
1018982 _assert_invalid_param (excinfo )
1019983 assert '"parameter": "item"' in str (excinfo .value ), str (excinfo .value )
1020984 tools ._client .call_service .assert_not_called ()
1021985
1022986 @pytest .mark .parametrize ("bad" , ["" , " " ])
1023- async def test_set_item_rejects_empty_item_on_implicit_update (
1024- self , tools , bad
1025- ):
987+ async def test_set_item_rejects_empty_item_on_implicit_update (self , tools , bad ):
1026988 # ``item`` is the implicit create/update discriminator: ``None``
1027989 # routes to create, non-None to update. Without the new guard,
1028990 # ``item=""`` would route to update (``"" is None`` is False) and
@@ -1069,9 +1031,7 @@ def decorator(fn):
10691031
10701032class TestEntitiesIdentifierValidation :
10711033 @pytest .mark .parametrize ("bad" , ["" , " " ])
1072- async def test_remove_entity_rejects_empty_entity_id (
1073- self , mock_ws_client , bad
1074- ):
1034+ async def test_remove_entity_rejects_empty_entity_id (self , mock_ws_client , bad ):
10751035 # ``entity_id`` is passed straight into the
10761036 # ``config/entity_registry/remove`` WS message; without the new
10771037 # guard, ``entity_id=""`` surfaces as a misleading HA
@@ -1082,15 +1042,11 @@ async def test_remove_entity_rejects_empty_entity_id(
10821042 with pytest .raises (ToolError ) as excinfo :
10831043 await ha_remove_entity (entity_id = bad )
10841044 _assert_invalid_param (excinfo )
1085- assert '"parameter": "entity_id"' in str (excinfo .value ), str (
1086- excinfo .value
1087- )
1045+ assert '"parameter": "entity_id"' in str (excinfo .value ), str (excinfo .value )
10881046 mock_ws_client .send_websocket_message .assert_not_called ()
10891047
10901048 @pytest .mark .parametrize ("bad" , ["" , " " ])
1091- async def test_set_entity_rejects_empty_entity_id_str (
1092- self , mock_ws_client , bad
1093- ):
1049+ async def test_set_entity_rejects_empty_entity_id_str (self , mock_ws_client , bad ):
10941050 # ``ha_set_entity`` accepts ``entity_id: str | list[str]``. The
10951051 # existing list-empty check rejects ``[]`` but lets ``[""]``
10961052 # through; for the string input path, ``""`` was normalised to
@@ -1102,9 +1058,7 @@ async def test_set_entity_rejects_empty_entity_id_str(
11021058 with pytest .raises (ToolError ) as excinfo :
11031059 await ha_set_entity (entity_id = bad , name = "New Name" )
11041060 _assert_invalid_param (excinfo )
1105- assert '"parameter": "entity_id"' in str (excinfo .value ), str (
1106- excinfo .value
1107- )
1061+ assert '"parameter": "entity_id"' in str (excinfo .value ), str (excinfo .value )
11081062 mock_ws_client .send_websocket_message .assert_not_called ()
11091063
11101064 @pytest .mark .parametrize ("bad" , ["" , " " ])
@@ -1123,9 +1077,7 @@ async def test_set_entity_rejects_empty_entity_id_in_list(
11231077 categories = {"automation" : "cat_id" },
11241078 )
11251079 _assert_invalid_param (excinfo )
1126- assert '"parameter": "entity_id"' in str (excinfo .value ), str (
1127- excinfo .value
1128- )
1080+ assert '"parameter": "entity_id"' in str (excinfo .value ), str (excinfo .value )
11291081 mock_ws_client .send_websocket_message .assert_not_called ()
11301082
11311083
@@ -1152,9 +1104,7 @@ def decorator(fn):
11521104
11531105class TestRegistryIdentifierValidation :
11541106 @pytest .mark .parametrize ("bad" , ["" , " " ])
1155- async def test_remove_device_rejects_empty_device_id (
1156- self , mock_ws_client , bad
1157- ):
1107+ async def test_remove_device_rejects_empty_device_id (self , mock_ws_client , bad ):
11581108 # Empty/whitespace ``device_id`` would slip past the local-filter
11591109 # check (``next((d for d in devices if d.get("id") == device_id)...)``)
11601110 # after wasting a ``config/device_registry/list`` round-trip, and
@@ -1166,15 +1116,11 @@ async def test_remove_device_rejects_empty_device_id(
11661116 with pytest .raises (ToolError ) as excinfo :
11671117 await ha_remove_device (device_id = bad )
11681118 _assert_invalid_param (excinfo )
1169- assert '"parameter": "device_id"' in str (excinfo .value ), str (
1170- excinfo .value
1171- )
1119+ assert '"parameter": "device_id"' in str (excinfo .value ), str (excinfo .value )
11721120 mock_ws_client .send_websocket_message .assert_not_called ()
11731121
11741122 @pytest .mark .parametrize ("bad" , ["" , " " ])
1175- async def test_update_device_rejects_empty_device_id (
1176- self , mock_ws_client , bad
1177- ):
1123+ async def test_update_device_rejects_empty_device_id (self , mock_ws_client , bad ):
11781124 # ``device_id`` is passed straight through ``ha_update_device`` to
11791125 # ``_update_device_internal`` which builds a
11801126 # ``config/device_registry/update`` WS message; without the new
@@ -1187,9 +1133,7 @@ async def test_update_device_rejects_empty_device_id(
11871133 with pytest .raises (ToolError ) as excinfo :
11881134 await ha_update_device (device_id = bad , name = "New Name" )
11891135 _assert_invalid_param (excinfo )
1190- assert '"parameter": "device_id"' in str (excinfo .value ), str (
1191- excinfo .value
1192- )
1136+ assert '"parameter": "device_id"' in str (excinfo .value ), str (excinfo .value )
11931137 mock_ws_client .send_websocket_message .assert_not_called ()
11941138
11951139
@@ -1295,7 +1239,5 @@ async def test_hacs_download_rejects_empty_repository_id(self, tools, bad):
12951239 with pytest .raises (ToolError ) as excinfo :
12961240 await tools .ha_hacs_download (repository_id = bad )
12971241 _assert_invalid_param (excinfo )
1298- assert '"parameter": "repository_id"' in str (excinfo .value ), str (
1299- excinfo .value
1300- )
1242+ assert '"parameter": "repository_id"' in str (excinfo .value ), str (excinfo .value )
13011243 tools ._client .send_websocket_message .assert_not_called ()
0 commit comments