Await writes in shopping_list action handlers (#157420)

This commit is contained in:
Erik Montnemery
2026-01-05 16:51:30 +01:00
committed by Bram Kragten
parent 85c2351af2
commit b3f123c715
4 changed files with 901 additions and 40 deletions
@@ -325,8 +325,7 @@ class ShoppingData:
)
return self.items
@callback
def async_reorder(
async def async_reorder(
self, item_ids: list[str], context: Context | None = None
) -> None:
"""Reorder items."""
@@ -351,7 +350,7 @@ class ShoppingData:
)
new_items.append(value)
self.items = new_items
self.hass.async_add_executor_job(self.save)
await self.hass.async_add_executor_job(self.save)
self._async_notify()
self.hass.bus.async_fire(
EVENT_SHOPPING_LIST_UPDATED,
@@ -388,7 +387,7 @@ class ShoppingData:
) -> None:
"""Sort items by name."""
self.items = sorted(self.items, key=lambda item: item["name"], reverse=reverse) # type: ignore[arg-type,return-value]
self.hass.async_add_executor_job(self.save)
await self.hass.async_add_executor_job(self.save)
self._async_notify()
self.hass.bus.async_fire(
EVENT_SHOPPING_LIST_UPDATED,
@@ -591,7 +590,8 @@ async def websocket_handle_clear(
vol.Required("item_ids"): [str],
}
)
def websocket_handle_reorder(
@websocket_api.async_response
async def websocket_handle_reorder(
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
msg: dict[str, Any],
@@ -599,7 +599,9 @@ def websocket_handle_reorder(
"""Handle reordering shopping_list items."""
msg_id = msg.pop("id")
try:
hass.data[DOMAIN].async_reorder(msg.pop("item_ids"), connection.context(msg))
await hass.data[DOMAIN].async_reorder(
msg.pop("item_ids"), connection.context(msg)
)
except NoMatchingShoppingListItem:
connection.send_error(
msg_id,
+10 -8
View File
@@ -1,6 +1,8 @@
"""Shopping list test helpers."""
from unittest.mock import patch
from collections.abc import Generator
from contextlib import suppress
import os
import pytest
@@ -11,13 +13,13 @@ from tests.common import MockConfigEntry
@pytest.fixture(autouse=True)
def mock_shopping_list_io():
"""Stub out the persistence."""
with (
patch("homeassistant.components.shopping_list.ShoppingData.save"),
patch("homeassistant.components.shopping_list.ShoppingData.async_load"),
):
def wipe_shopping_list_store(hass: HomeAssistant) -> Generator[None]:
"""Wipe shopping list store after test."""
try:
yield
finally:
with suppress(FileNotFoundError):
os.remove(hass.config.path(".shopping_list.json"))
@pytest.fixture
@@ -27,7 +29,7 @@ def mock_config_entry() -> MockConfigEntry:
@pytest.fixture
async def sl_setup(hass: HomeAssistant, mock_config_entry: MockConfigEntry):
async def sl_setup(hass: HomeAssistant, mock_config_entry: MockConfigEntry) -> None:
"""Set up the shopping list."""
mock_config_entry.add_to_hass(hass)
@@ -0,0 +1,717 @@
# serializer version: 1
# name: test_add_item
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
])
# ---
# name: test_add_item_service
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
])
# ---
# name: test_api_update_fails
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
])
# ---
# name: test_api_update_fails.1
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
])
# ---
# name: test_api_update_fails.2
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
])
# ---
# name: test_clear_completed_items
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'cheese',
}),
])
# ---
# name: test_clear_completed_items.1
list([
dict({
'complete': True,
'id': '<ANY>',
'name': 'beer',
}),
dict({
'complete': True,
'id': '<ANY>',
'name': 'cheese',
}),
])
# ---
# name: test_clear_completed_items.2
list([
])
# ---
# name: test_clear_completed_items_service
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
])
# ---
# name: test_clear_completed_items_service.1
list([
dict({
'complete': True,
'id': '<ANY>',
'name': 'beer',
}),
])
# ---
# name: test_clear_completed_items_service.2
list([
])
# ---
# name: test_deprecated_api_clear_completed
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'wine',
}),
])
# ---
# name: test_deprecated_api_clear_completed.1
list([
dict({
'complete': True,
'id': '<ANY>',
'name': 'beer',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'wine',
}),
])
# ---
# name: test_deprecated_api_clear_completed.2
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'wine',
}),
])
# ---
# name: test_deprecated_api_create
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'soda',
}),
])
# ---
# name: test_deprecated_api_create_fail
'<file missing>'
# ---
# name: test_deprecated_api_get_all
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'wine',
}),
])
# ---
# name: test_deprecated_api_get_all.1
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'wine',
}),
])
# ---
# name: test_deprecated_api_update
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'wine',
}),
])
# ---
# name: test_deprecated_api_update.1
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'soda',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'wine',
}),
])
# ---
# name: test_deprecated_api_update.2
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'soda',
}),
dict({
'complete': True,
'id': '<ANY>',
'name': 'wine',
}),
])
# ---
# name: test_recent_items_intent
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'wine',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'soda',
}),
])
# ---
# name: test_recent_items_intent.1
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'wine',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'soda',
}),
])
# ---
# name: test_remove_item
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'cheese',
}),
])
# ---
# name: test_remove_item.1
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'cheese',
}),
])
# ---
# name: test_remove_item.2
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'cheese',
}),
])
# ---
# name: test_remove_item_service
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'cheese',
}),
])
# ---
# name: test_remove_item_service.1
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'cheese',
}),
])
# ---
# name: test_sort_list_service
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'zzz',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'ddd',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'aaa',
}),
])
# ---
# name: test_sort_list_service.1
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'aaa',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'ddd',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'zzz',
}),
])
# ---
# name: test_sort_list_service.2
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'zzz',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'ddd',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'aaa',
}),
])
# ---
# name: test_update_list
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'cheese',
}),
])
# ---
# name: test_update_list.1
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'dupe',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'dupe',
}),
])
# ---
# name: test_ws_add_item
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'soda',
}),
])
# ---
# name: test_ws_add_item_fail
'<file missing>'
# ---
# name: test_ws_clear_items
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'wine',
}),
])
# ---
# name: test_ws_clear_items.1
list([
dict({
'complete': True,
'id': '<ANY>',
'name': 'beer',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'wine',
}),
])
# ---
# name: test_ws_clear_items.2
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'wine',
}),
])
# ---
# name: test_ws_get_items
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'wine',
}),
])
# ---
# name: test_ws_get_items.1
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'wine',
}),
])
# ---
# name: test_ws_remove_item
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'soda',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'cheese',
}),
])
# ---
# name: test_ws_remove_item.1
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'cheese',
}),
])
# ---
# name: test_ws_remove_item_fail
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'soda',
}),
])
# ---
# name: test_ws_remove_item_fail.1
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'soda',
}),
])
# ---
# name: test_ws_reorder_items
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'wine',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'apple',
}),
])
# ---
# name: test_ws_reorder_items.1
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'wine',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'apple',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
])
# ---
# name: test_ws_reorder_items.2
list([
dict({
'complete': True,
'id': '<ANY>',
'name': 'wine',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'apple',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
])
# ---
# name: test_ws_reorder_items.3
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'apple',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
dict({
'complete': True,
'id': '<ANY>',
'name': 'wine',
}),
])
# ---
# name: test_ws_reorder_items_failure
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'wine',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'apple',
}),
])
# ---
# name: test_ws_reorder_items_failure.1
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'wine',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'apple',
}),
])
# ---
# name: test_ws_reorder_items_failure.2
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'wine',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'apple',
}),
])
# ---
# name: test_ws_update_item
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'wine',
}),
])
# ---
# name: test_ws_update_item.1
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'soda',
}),
dict({
'complete': False,
'id': '<ANY>',
'name': 'wine',
}),
])
# ---
# name: test_ws_update_item.2
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'soda',
}),
dict({
'complete': True,
'id': '<ANY>',
'name': 'wine',
}),
])
# ---
# name: test_ws_update_item_fail
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
])
# ---
# name: test_ws_update_item_fail.1
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
])
# ---
# name: test_ws_update_item_fail.2
list([
dict({
'complete': False,
'id': '<ANY>',
'name': 'beer',
}),
])
# ---
+166 -26
View File
@@ -1,8 +1,11 @@
"""Test shopping list component."""
from http import HTTPStatus
import json
from pathlib import Path
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.components.shopping_list import NoMatchingShoppingListItem
from homeassistant.components.shopping_list.const import (
@@ -28,7 +31,23 @@ from tests.common import async_capture_events
from tests.typing import ClientSessionGenerator, WebSocketGenerator
async def test_add_item(hass: HomeAssistant, sl_setup) -> None:
def assert_shopping_list_data(hass: HomeAssistant, snapshot: SnapshotAssertion) -> None:
"""Assert shopping list data matches snapshot."""
path = Path(hass.config.path(".shopping_list.json"))
if not path.exists():
assert snapshot == "<file missing>"
else:
shopping_list_data = json.loads(path.read_text(encoding="utf-8"))
for item in shopping_list_data:
if "id" not in item:
continue
item["id"] = "<ANY>"
assert shopping_list_data == snapshot
async def test_add_item(
hass: HomeAssistant, sl_setup: None, snapshot: SnapshotAssertion
) -> None:
"""Test adding an item intent."""
response = await intent.async_handle(
@@ -39,10 +58,13 @@ async def test_add_item(hass: HomeAssistant, sl_setup) -> None:
# Response text is now handled by default conversation agent
assert response.response_type == intent.IntentResponseType.ACTION_DONE
assert_shopping_list_data(hass, snapshot)
async def test_remove_item(hass: HomeAssistant, sl_setup) -> None:
"""Test removiung list items."""
async def test_remove_item(
hass: HomeAssistant, sl_setup: None, snapshot: SnapshotAssertion
) -> None:
"""Test removing list items."""
await intent.async_handle(
hass, "test", "HassShoppingListAddItem", {"item": {"value": "beer"}}
)
@@ -50,12 +72,14 @@ async def test_remove_item(hass: HomeAssistant, sl_setup) -> None:
await intent.async_handle(
hass, "test", "HassShoppingListAddItem", {"item": {"value": "cheese"}}
)
assert_shopping_list_data(hass, snapshot)
assert len(hass.data[DOMAIN].items) == 2
# Remove a single item
item_id = hass.data[DOMAIN].items[0]["id"]
await hass.data[DOMAIN].async_remove(item_id)
assert_shopping_list_data(hass, snapshot)
assert len(hass.data[DOMAIN].items) == 1
@@ -65,9 +89,12 @@ async def test_remove_item(hass: HomeAssistant, sl_setup) -> None:
# Trying to remove the same item twice should fail
with pytest.raises(NoMatchingShoppingListItem):
await hass.data[DOMAIN].async_remove(item_id)
assert_shopping_list_data(hass, snapshot)
async def test_update_list(hass: HomeAssistant, sl_setup) -> None:
async def test_update_list(
hass: HomeAssistant, sl_setup: None, snapshot: SnapshotAssertion
) -> None:
"""Test updating all list items."""
await intent.async_handle(
hass, "test", "HassShoppingListAddItem", {"item": {"value": "beer"}}
@@ -76,6 +103,7 @@ async def test_update_list(hass: HomeAssistant, sl_setup) -> None:
await intent.async_handle(
hass, "test", "HassShoppingListAddItem", {"item": {"value": "cheese"}}
)
assert_shopping_list_data(hass, snapshot)
# Update a single attribute, other attributes shouldn't change
await hass.data[DOMAIN].async_update_list({"complete": True})
@@ -90,6 +118,7 @@ async def test_update_list(hass: HomeAssistant, sl_setup) -> None:
# Update multiple attributes
await hass.data[DOMAIN].async_update_list({"name": "dupe", "complete": False})
assert_shopping_list_data(hass, snapshot)
beer = hass.data[DOMAIN].items[0]
assert beer["name"] == "dupe"
@@ -100,7 +129,9 @@ async def test_update_list(hass: HomeAssistant, sl_setup) -> None:
assert cheese["complete"] is False
async def test_clear_completed_items(hass: HomeAssistant, sl_setup) -> None:
async def test_clear_completed_items(
hass: HomeAssistant, sl_setup: None, snapshot: SnapshotAssertion
) -> None:
"""Test clear completed list items."""
await intent.async_handle(
hass,
@@ -112,18 +143,23 @@ async def test_clear_completed_items(hass: HomeAssistant, sl_setup) -> None:
await intent.async_handle(
hass, "test", "HassShoppingListAddItem", {"item": {"value": "cheese"}}
)
assert_shopping_list_data(hass, snapshot)
assert len(hass.data[DOMAIN].items) == 2
# Update a single attribute, other attributes shouldn't change
await hass.data[DOMAIN].async_update_list({"complete": True})
assert_shopping_list_data(hass, snapshot)
await hass.data[DOMAIN].async_clear_completed()
assert_shopping_list_data(hass, snapshot)
assert len(hass.data[DOMAIN].items) == 0
async def test_recent_items_intent(hass: HomeAssistant, sl_setup) -> None:
async def test_recent_items_intent(
hass: HomeAssistant, sl_setup: None, snapshot: SnapshotAssertion
) -> None:
"""Test recent items."""
await intent.async_handle(
@@ -135,8 +171,10 @@ async def test_recent_items_intent(hass: HomeAssistant, sl_setup) -> None:
await intent.async_handle(
hass, "test", "HassShoppingListAddItem", {"item": {"value": "soda"}}
)
assert_shopping_list_data(hass, snapshot)
response = await intent.async_handle(hass, "test", "HassShoppingListLastItems")
assert_shopping_list_data(hass, snapshot)
assert (
response.speech["plain"]["speech"]
@@ -145,7 +183,10 @@ async def test_recent_items_intent(hass: HomeAssistant, sl_setup) -> None:
async def test_deprecated_api_get_all(
hass: HomeAssistant, hass_client: ClientSessionGenerator, sl_setup
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
sl_setup: None,
snapshot: SnapshotAssertion,
) -> None:
"""Test the API."""
@@ -155,9 +196,11 @@ async def test_deprecated_api_get_all(
await intent.async_handle(
hass, "test", "HassShoppingListAddItem", {"item": {"value": "wine"}}
)
assert_shopping_list_data(hass, snapshot)
client = await hass_client()
resp = await client.get("/api/shopping_list")
assert_shopping_list_data(hass, snapshot)
assert resp.status == HTTPStatus.OK
data = await resp.json()
@@ -169,7 +212,10 @@ async def test_deprecated_api_get_all(
async def test_ws_get_items(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator, sl_setup
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
sl_setup: None,
snapshot: SnapshotAssertion,
) -> None:
"""Test get shopping_list items websocket command."""
@@ -179,6 +225,7 @@ async def test_ws_get_items(
await intent.async_handle(
hass, "test", "HassShoppingListAddItem", {"item": {"value": "wine"}}
)
assert_shopping_list_data(hass, snapshot)
client = await hass_ws_client(hass)
events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED)
@@ -187,6 +234,7 @@ async def test_ws_get_items(
msg = await client.receive_json()
assert msg["success"] is True
assert len(events) == 0
assert_shopping_list_data(hass, snapshot)
assert msg["id"] == 5
assert msg["type"] == TYPE_RESULT
@@ -200,7 +248,10 @@ async def test_ws_get_items(
async def test_deprecated_api_update(
hass: HomeAssistant, hass_client: ClientSessionGenerator, sl_setup
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
sl_setup: None,
snapshot: SnapshotAssertion,
) -> None:
"""Test the API."""
@@ -210,6 +261,7 @@ async def test_deprecated_api_update(
await intent.async_handle(
hass, "test", "HassShoppingListAddItem", {"item": {"value": "wine"}}
)
assert_shopping_list_data(hass, snapshot)
beer_id = hass.data["shopping_list"].items[0]["id"]
wine_id = hass.data["shopping_list"].items[1]["id"]
@@ -219,6 +271,7 @@ async def test_deprecated_api_update(
resp = await client.post(
f"/api/shopping_list/item/{beer_id}", json={"name": "soda"}
)
assert_shopping_list_data(hass, snapshot)
assert resp.status == HTTPStatus.OK
assert len(events) == 1
@@ -228,6 +281,7 @@ async def test_deprecated_api_update(
resp = await client.post(
f"/api/shopping_list/item/{wine_id}", json={"complete": True}
)
assert_shopping_list_data(hass, snapshot)
assert resp.status == HTTPStatus.OK
assert len(events) == 2
@@ -240,7 +294,10 @@ async def test_deprecated_api_update(
async def test_ws_update_item(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator, sl_setup
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
sl_setup: None,
snapshot: SnapshotAssertion,
) -> None:
"""Test update shopping_list item websocket command."""
await intent.async_handle(
@@ -249,6 +306,7 @@ async def test_ws_update_item(
await intent.async_handle(
hass, "test", "HassShoppingListAddItem", {"item": {"value": "wine"}}
)
assert_shopping_list_data(hass, snapshot)
beer_id = hass.data["shopping_list"].items[0]["id"]
wine_id = hass.data["shopping_list"].items[1]["id"]
@@ -267,6 +325,7 @@ async def test_ws_update_item(
data = msg["result"]
assert data == {"id": beer_id, "name": "soda", "complete": False}
assert len(events) == 1
assert_shopping_list_data(hass, snapshot)
await client.send_json(
{
@@ -281,6 +340,7 @@ async def test_ws_update_item(
data = msg["result"]
assert data == {"id": wine_id, "name": "wine", "complete": True}
assert len(events) == 2
assert_shopping_list_data(hass, snapshot)
beer, wine = hass.data["shopping_list"].items
assert beer == {"id": beer_id, "name": "soda", "complete": False}
@@ -288,34 +348,44 @@ async def test_ws_update_item(
async def test_api_update_fails(
hass: HomeAssistant, hass_client: ClientSessionGenerator, sl_setup
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
sl_setup: None,
snapshot: SnapshotAssertion,
) -> None:
"""Test the API."""
await intent.async_handle(
hass, "test", "HassShoppingListAddItem", {"item": {"value": "beer"}}
)
assert_shopping_list_data(hass, snapshot)
client = await hass_client()
events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED)
resp = await client.post("/api/shopping_list/non_existing", json={"name": "soda"})
assert_shopping_list_data(hass, snapshot)
assert resp.status == HTTPStatus.NOT_FOUND
assert len(events) == 0
beer_id = hass.data["shopping_list"].items[0]["id"]
resp = await client.post(f"/api/shopping_list/item/{beer_id}", json={"name": 123})
assert_shopping_list_data(hass, snapshot)
assert resp.status == HTTPStatus.BAD_REQUEST
async def test_ws_update_item_fail(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator, sl_setup
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
sl_setup: None,
snapshot: SnapshotAssertion,
) -> None:
"""Test failure of update shopping_list item websocket command."""
await intent.async_handle(
hass, "test", "HassShoppingListAddItem", {"item": {"value": "beer"}}
)
assert_shopping_list_data(hass, snapshot)
client = await hass_ws_client(hass)
events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED)
await client.send_json(
@@ -331,15 +401,20 @@ async def test_ws_update_item_fail(
data = msg["error"]
assert data == {"code": "item_not_found", "message": "Item not found"}
assert len(events) == 0
assert_shopping_list_data(hass, snapshot)
await client.send_json({"id": 6, "type": "shopping_list/items/update", "name": 123})
msg = await client.receive_json()
assert msg["success"] is False
assert len(events) == 0
assert_shopping_list_data(hass, snapshot)
async def test_deprecated_api_clear_completed(
hass: HomeAssistant, hass_client: ClientSessionGenerator, sl_setup
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
sl_setup: None,
snapshot: SnapshotAssertion,
) -> None:
"""Test the API."""
@@ -349,6 +424,7 @@ async def test_deprecated_api_clear_completed(
await intent.async_handle(
hass, "test", "HassShoppingListAddItem", {"item": {"value": "wine"}}
)
assert_shopping_list_data(hass, snapshot)
beer_id = hass.data["shopping_list"].items[0]["id"]
wine_id = hass.data["shopping_list"].items[1]["id"]
@@ -362,10 +438,12 @@ async def test_deprecated_api_clear_completed(
)
assert resp.status == HTTPStatus.OK
assert len(events) == 1
assert_shopping_list_data(hass, snapshot)
resp = await client.post("/api/shopping_list/clear_completed")
assert resp.status == HTTPStatus.OK
assert len(events) == 2
assert_shopping_list_data(hass, snapshot)
items = hass.data["shopping_list"].items
assert len(items) == 1
@@ -374,7 +452,10 @@ async def test_deprecated_api_clear_completed(
async def test_ws_clear_items(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator, sl_setup
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
sl_setup: None,
snapshot: SnapshotAssertion,
) -> None:
"""Test clearing shopping_list items websocket command."""
await intent.async_handle(
@@ -383,6 +464,7 @@ async def test_ws_clear_items(
await intent.async_handle(
hass, "test", "HassShoppingListAddItem", {"item": {"value": "wine"}}
)
assert_shopping_list_data(hass, snapshot)
beer_id = hass.data["shopping_list"].items[0]["id"]
wine_id = hass.data["shopping_list"].items[1]["id"]
client = await hass_ws_client(hass)
@@ -398,6 +480,7 @@ async def test_ws_clear_items(
msg = await client.receive_json()
assert msg["success"] is True
assert len(events) == 1
assert_shopping_list_data(hass, snapshot)
await client.send_json({"id": 6, "type": "shopping_list/items/clear"})
msg = await client.receive_json()
@@ -406,16 +489,21 @@ async def test_ws_clear_items(
assert len(items) == 1
assert items[0] == {"id": wine_id, "name": "wine", "complete": False}
assert len(events) == 2
assert_shopping_list_data(hass, snapshot)
async def test_deprecated_api_create(
hass: HomeAssistant, hass_client: ClientSessionGenerator, sl_setup
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
sl_setup: None,
snapshot: SnapshotAssertion,
) -> None:
"""Test the API."""
client = await hass_client()
events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED)
resp = await client.post("/api/shopping_list/item", json={"name": "soda"})
assert_shopping_list_data(hass, snapshot)
assert resp.status == HTTPStatus.OK
data = await resp.json()
@@ -430,13 +518,17 @@ async def test_deprecated_api_create(
async def test_deprecated_api_create_fail(
hass: HomeAssistant, hass_client: ClientSessionGenerator, sl_setup
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
sl_setup: None,
snapshot: SnapshotAssertion,
) -> None:
"""Test the API."""
client = await hass_client()
events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED)
resp = await client.post("/api/shopping_list/item", json={"name": 1234})
assert_shopping_list_data(hass, snapshot)
assert resp.status == HTTPStatus.BAD_REQUEST
assert len(hass.data["shopping_list"].items) == 0
@@ -444,7 +536,10 @@ async def test_deprecated_api_create_fail(
async def test_ws_add_item(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator, sl_setup
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
sl_setup: None,
snapshot: SnapshotAssertion,
) -> None:
"""Test adding shopping_list item websocket command."""
client = await hass_ws_client(hass)
@@ -456,6 +551,7 @@ async def test_ws_add_item(
assert data["name"] == "soda"
assert data["complete"] is False
assert len(events) == 1
assert_shopping_list_data(hass, snapshot)
items = hass.data["shopping_list"].items
assert len(items) == 1
@@ -464,7 +560,10 @@ async def test_ws_add_item(
async def test_ws_add_item_fail(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator, sl_setup
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
sl_setup: None,
snapshot: SnapshotAssertion,
) -> None:
"""Test adding shopping_list item failure websocket command."""
client = await hass_ws_client(hass)
@@ -474,10 +573,14 @@ async def test_ws_add_item_fail(
assert msg["success"] is False
assert len(events) == 0
assert len(hass.data["shopping_list"].items) == 0
assert_shopping_list_data(hass, snapshot)
async def test_ws_remove_item(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator, sl_setup
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
sl_setup: None,
snapshot: SnapshotAssertion,
) -> None:
"""Test removing shopping_list item websocket command."""
client = await hass_ws_client(hass)
@@ -490,6 +593,7 @@ async def test_ws_remove_item(
)
msg = await client.receive_json()
assert len(events) == 2
assert_shopping_list_data(hass, snapshot)
items = hass.data["shopping_list"].items
assert len(items) == 2
@@ -500,6 +604,7 @@ async def test_ws_remove_item(
msg = await client.receive_json()
assert len(events) == 3
assert msg["success"] is True
assert_shopping_list_data(hass, snapshot)
items = hass.data["shopping_list"].items
assert len(items) == 1
@@ -507,22 +612,30 @@ async def test_ws_remove_item(
async def test_ws_remove_item_fail(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator, sl_setup
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
sl_setup: None,
snapshot: SnapshotAssertion,
) -> None:
"""Test removing shopping_list item failure websocket command."""
client = await hass_ws_client(hass)
events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED)
await client.send_json({"id": 5, "type": "shopping_list/items/add", "name": "soda"})
msg = await client.receive_json()
assert_shopping_list_data(hass, snapshot)
await client.send_json({"id": 6, "type": "shopping_list/items/remove"})
msg = await client.receive_json()
assert msg["success"] is False
assert len(events) == 1
assert len(hass.data["shopping_list"].items) == 1
assert_shopping_list_data(hass, snapshot)
async def test_ws_reorder_items(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator, sl_setup
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
sl_setup: None,
snapshot: SnapshotAssertion,
) -> None:
"""Test reordering shopping_list items websocket command."""
await intent.async_handle(
@@ -534,6 +647,7 @@ async def test_ws_reorder_items(
await intent.async_handle(
hass, "test", "HassShoppingListAddItem", {"item": {"value": "apple"}}
)
assert_shopping_list_data(hass, snapshot)
beer_id = hass.data["shopping_list"].items[0]["id"]
wine_id = hass.data["shopping_list"].items[1]["id"]
@@ -566,6 +680,7 @@ async def test_ws_reorder_items(
"name": "beer",
"complete": False,
}
assert_shopping_list_data(hass, snapshot)
# Mark wine as completed.
await client.send_json(
@@ -578,6 +693,7 @@ async def test_ws_reorder_items(
)
_ = await client.receive_json()
assert len(events) == 2
assert_shopping_list_data(hass, snapshot)
await client.send_json(
{
@@ -604,10 +720,14 @@ async def test_ws_reorder_items(
"name": "wine",
"complete": True,
}
assert_shopping_list_data(hass, snapshot)
async def test_ws_reorder_items_failure(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator, sl_setup
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
sl_setup: None,
snapshot: SnapshotAssertion,
) -> None:
"""Test reordering shopping_list items websocket command."""
await intent.async_handle(
@@ -619,6 +739,7 @@ async def test_ws_reorder_items_failure(
await intent.async_handle(
hass, "test", "HassShoppingListAddItem", {"item": {"value": "apple"}}
)
assert_shopping_list_data(hass, snapshot)
beer_id = hass.data["shopping_list"].items[0]["id"]
wine_id = hass.data["shopping_list"].items[1]["id"]
@@ -639,6 +760,7 @@ async def test_ws_reorder_items_failure(
assert msg["success"] is False
assert msg["error"]["code"] == ERR_NOT_FOUND
assert len(events) == 0
assert_shopping_list_data(hass, snapshot)
# Testing not sending all unchecked item ids.
await client.send_json(
@@ -652,9 +774,12 @@ async def test_ws_reorder_items_failure(
assert msg["success"] is False
assert msg["error"]["code"] == ERR_INVALID_FORMAT
assert len(events) == 0
assert_shopping_list_data(hass, snapshot)
async def test_add_item_service(hass: HomeAssistant, sl_setup) -> None:
async def test_add_item_service(
hass: HomeAssistant, sl_setup: None, snapshot: SnapshotAssertion
) -> None:
"""Test adding shopping_list item service."""
events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED)
await hass.services.async_call(
@@ -665,9 +790,12 @@ async def test_add_item_service(hass: HomeAssistant, sl_setup) -> None:
)
assert len(hass.data[DOMAIN].items) == 1
assert len(events) == 1
assert_shopping_list_data(hass, snapshot)
async def test_remove_item_service(hass: HomeAssistant, sl_setup) -> None:
async def test_remove_item_service(
hass: HomeAssistant, sl_setup: None, snapshot: SnapshotAssertion
) -> None:
"""Test removing shopping_list item service."""
events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED)
await hass.services.async_call(
@@ -684,6 +812,7 @@ async def test_remove_item_service(hass: HomeAssistant, sl_setup) -> None:
)
assert len(hass.data[DOMAIN].items) == 2
assert len(events) == 2
assert_shopping_list_data(hass, snapshot)
await hass.services.async_call(
DOMAIN,
@@ -694,9 +823,12 @@ async def test_remove_item_service(hass: HomeAssistant, sl_setup) -> None:
assert len(hass.data[DOMAIN].items) == 1
assert hass.data[DOMAIN].items[0]["name"] == "cheese"
assert len(events) == 3
assert_shopping_list_data(hass, snapshot)
async def test_clear_completed_items_service(hass: HomeAssistant, sl_setup) -> None:
async def test_clear_completed_items_service(
hass: HomeAssistant, sl_setup: None, snapshot: SnapshotAssertion
) -> None:
"""Test clearing completed shopping_list items service."""
events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED)
await hass.services.async_call(
@@ -707,6 +839,7 @@ async def test_clear_completed_items_service(hass: HomeAssistant, sl_setup) -> N
)
assert len(hass.data[DOMAIN].items) == 1
assert len(events) == 1
assert_shopping_list_data(hass, snapshot)
events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED)
await hass.services.async_call(
@@ -717,6 +850,7 @@ async def test_clear_completed_items_service(hass: HomeAssistant, sl_setup) -> N
)
assert len(hass.data[DOMAIN].items) == 1
assert len(events) == 1
assert_shopping_list_data(hass, snapshot)
events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED)
await hass.services.async_call(
@@ -727,9 +861,12 @@ async def test_clear_completed_items_service(hass: HomeAssistant, sl_setup) -> N
)
assert len(hass.data[DOMAIN].items) == 0
assert len(events) == 1
assert_shopping_list_data(hass, snapshot)
async def test_sort_list_service(hass: HomeAssistant, sl_setup) -> None:
async def test_sort_list_service(
hass: HomeAssistant, sl_setup: None, snapshot: SnapshotAssertion
) -> None:
"""Test sort_all service."""
for name in ("zzz", "ddd", "aaa"):
@@ -739,6 +876,7 @@ async def test_sort_list_service(hass: HomeAssistant, sl_setup) -> None:
{ATTR_NAME: name},
blocking=True,
)
assert_shopping_list_data(hass, snapshot)
# sort ascending
events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED)
@@ -748,6 +886,7 @@ async def test_sort_list_service(hass: HomeAssistant, sl_setup) -> None:
{ATTR_REVERSE: False},
blocking=True,
)
assert_shopping_list_data(hass, snapshot)
assert hass.data[DOMAIN].items[0][ATTR_NAME] == "aaa"
assert hass.data[DOMAIN].items[1][ATTR_NAME] == "ddd"
@@ -761,6 +900,7 @@ async def test_sort_list_service(hass: HomeAssistant, sl_setup) -> None:
{ATTR_REVERSE: True},
blocking=True,
)
assert_shopping_list_data(hass, snapshot)
assert hass.data[DOMAIN].items[0][ATTR_NAME] == "zzz"
assert hass.data[DOMAIN].items[1][ATTR_NAME] == "ddd"