mirror of
https://github.com/home-assistant/core.git
synced 2026-09-09 15:11:40 +01:00
720 lines
22 KiB
Python
720 lines
22 KiB
Python
"""Test Alexa Devices todo entities."""
|
|
|
|
import asyncio
|
|
from dataclasses import replace
|
|
from typing import Any
|
|
from unittest.mock import AsyncMock, patch
|
|
|
|
from aioamazondevices import CannotAuthenticate, CannotConnect
|
|
from aioamazondevices.exceptions import CannotRetrieveData
|
|
from aioamazondevices.structures import (
|
|
AmazonListEvent,
|
|
AmazonListEventType,
|
|
AmazonListInfo,
|
|
AmazonListItem,
|
|
AmazonListItemStatus,
|
|
AmazonListType,
|
|
)
|
|
from freezegun.api import FrozenDateTimeFactory
|
|
import pytest
|
|
from syrupy.assertion import SnapshotAssertion
|
|
|
|
from homeassistant.components.alexa_devices.coordinator import SCAN_INTERVAL
|
|
from homeassistant.components.todo import (
|
|
DOMAIN as TODO_DOMAIN,
|
|
TodoItemStatus,
|
|
TodoServices,
|
|
)
|
|
from homeassistant.config_entries import ConfigEntryState
|
|
from homeassistant.const import ATTR_ENTITY_ID, Platform
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.exceptions import HomeAssistantError
|
|
from homeassistant.helpers import entity_registry as er
|
|
from homeassistant.util import slugify
|
|
|
|
from . import setup_integration
|
|
from .const import TEST_USERNAME
|
|
|
|
from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform
|
|
|
|
LIST_ENTITY_ID_PREFIX = f"{TODO_DOMAIN.lower()}.{slugify(TEST_USERNAME)}_"
|
|
MOCK_SHOPPING_LIST_ENTITY_ID = f"{LIST_ENTITY_ID_PREFIX}shopping_list"
|
|
MOCK_TODO_LIST_ENTITY_ID = f"{LIST_ENTITY_ID_PREFIX}to_do_list"
|
|
MOCK_CUSTOM_LIST_ENTITY_ID = f"{LIST_ENTITY_ID_PREFIX}concerts"
|
|
|
|
MOCK_SHOPPING_LIST = AmazonListInfo(
|
|
id="shopping_list_id", name=None, list_type=AmazonListType.SHOP
|
|
)
|
|
MOCK_TODO_LIST = AmazonListInfo(
|
|
id="todo_list_id", name=None, list_type=AmazonListType.TODO
|
|
)
|
|
MOCK_CUSTOM_LIST = AmazonListInfo(
|
|
id="custom_list_id", name="Concerts", list_type=AmazonListType.CUSTOM
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_todo_lists():
|
|
"""Mock todo lists."""
|
|
return [
|
|
MOCK_SHOPPING_LIST,
|
|
MOCK_TODO_LIST,
|
|
MOCK_CUSTOM_LIST,
|
|
]
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_todo_items():
|
|
"""Mock todo items."""
|
|
return {
|
|
"shopping_list_id": {
|
|
"item_1": AmazonListItem(
|
|
id="item_1",
|
|
name="Bubble tea",
|
|
status=AmazonListItemStatus.ACTIVE,
|
|
version=1,
|
|
),
|
|
},
|
|
"todo_list_id": {
|
|
"item_2": AmazonListItem(
|
|
id="item_2",
|
|
name="Task 1",
|
|
status=AmazonListItemStatus.ACTIVE,
|
|
version=1,
|
|
),
|
|
"item_3": AmazonListItem(
|
|
id="item_3",
|
|
name="Task 2",
|
|
status=AmazonListItemStatus.COMPLETE,
|
|
version=2,
|
|
),
|
|
},
|
|
"custom_list_id": {
|
|
"item_4": AmazonListItem(
|
|
id="item_4",
|
|
name="TWICE",
|
|
status=AmazonListItemStatus.ACTIVE,
|
|
version=1,
|
|
),
|
|
"item_5": AmazonListItem(
|
|
id="item_5",
|
|
name="BTS",
|
|
status=AmazonListItemStatus.COMPLETE,
|
|
version=2,
|
|
),
|
|
},
|
|
}
|
|
|
|
|
|
async def test_all_entities(
|
|
hass: HomeAssistant,
|
|
snapshot: SnapshotAssertion,
|
|
mock_amazon_devices_client: AsyncMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
entity_registry: er.EntityRegistry,
|
|
mock_todo_lists: list[AmazonListInfo],
|
|
mock_todo_items: dict[str, Any],
|
|
) -> None:
|
|
"""Test all entities."""
|
|
mock_amazon_devices_client.todo_lists = mock_todo_lists
|
|
mock_amazon_devices_client.get_todo_list_items = AsyncMock(
|
|
side_effect=lambda list_id: dict(mock_todo_items.get(list_id, {}))
|
|
)
|
|
|
|
with patch("homeassistant.components.alexa_devices.PLATFORMS", [Platform.TODO]):
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
|
|
|
|
|
async def test_add_todo_item(
|
|
hass: HomeAssistant,
|
|
mock_amazon_devices_client: AsyncMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_todo_lists: list[AmazonListInfo],
|
|
) -> None:
|
|
"""Test adding a todo item."""
|
|
mock_amazon_devices_client.todo_lists = mock_todo_lists
|
|
list_items: dict[str, AmazonListItem] = {}
|
|
mock_amazon_devices_client.get_todo_list_items = AsyncMock(
|
|
side_effect=lambda list_id: dict(list_items)
|
|
)
|
|
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
entity_id = MOCK_TODO_LIST_ENTITY_ID
|
|
|
|
assert hass.states.get(entity_id).state == "0"
|
|
|
|
# Amazon has the item from the moment the call returns
|
|
mock_amazon_devices_client.add_todo_list_item = AsyncMock(
|
|
side_effect=lambda list_id, name: list_items.update(
|
|
{
|
|
"item_6": AmazonListItem(
|
|
id="item_6",
|
|
name=name,
|
|
status=AmazonListItemStatus.ACTIVE,
|
|
version=1,
|
|
)
|
|
}
|
|
)
|
|
)
|
|
|
|
await hass.services.async_call(
|
|
TODO_DOMAIN,
|
|
TodoServices.ADD_ITEM,
|
|
{ATTR_ENTITY_ID: entity_id, "item": "New Task"},
|
|
blocking=True,
|
|
)
|
|
|
|
mock_amazon_devices_client.add_todo_list_item.assert_called_once_with(
|
|
"todo_list_id", "New Task"
|
|
)
|
|
assert hass.states.get(entity_id).state == "1"
|
|
|
|
|
|
async def test_concurrent_writes_keep_the_newest_answer(
|
|
hass: HomeAssistant,
|
|
mock_amazon_devices_client: AsyncMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_todo_lists: list[AmazonListInfo],
|
|
) -> None:
|
|
"""Test a slow read of an older list does not overwrite a newer one."""
|
|
mock_amazon_devices_client.todo_lists = mock_todo_lists
|
|
list_items: dict[str, AmazonListItem] = {}
|
|
mock_amazon_devices_client.get_todo_list_items = AsyncMock(
|
|
side_effect=lambda list_id: dict(list_items)
|
|
)
|
|
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
entity_id = MOCK_TODO_LIST_ENTITY_ID
|
|
|
|
def add_item(list_id: str, name: str) -> None:
|
|
list_items[name] = AmazonListItem(
|
|
id=name, name=name, status=AmazonListItemStatus.ACTIVE, version=1
|
|
)
|
|
|
|
mock_amazon_devices_client.add_todo_list_item = AsyncMock(side_effect=add_item)
|
|
|
|
# Hold the first read until both items have been written
|
|
released = asyncio.Event()
|
|
reads = 0
|
|
|
|
async def read_items(list_id: str) -> dict[str, AmazonListItem]:
|
|
nonlocal reads
|
|
reads += 1
|
|
items = dict(list_items)
|
|
if reads == 1:
|
|
await released.wait()
|
|
return items
|
|
|
|
mock_amazon_devices_client.get_todo_list_items = AsyncMock(side_effect=read_items)
|
|
|
|
writes = asyncio.gather(
|
|
*[
|
|
hass.services.async_call(
|
|
TODO_DOMAIN,
|
|
TodoServices.ADD_ITEM,
|
|
{ATTR_ENTITY_ID: entity_id, "item": item},
|
|
blocking=True,
|
|
)
|
|
for item in ("First task", "Second task")
|
|
]
|
|
)
|
|
await asyncio.sleep(0)
|
|
released.set()
|
|
await writes
|
|
|
|
assert hass.states.get(entity_id).state == "2"
|
|
|
|
|
|
async def test_pushed_event_survives_a_refresh(
|
|
hass: HomeAssistant,
|
|
mock_amazon_devices_client: AsyncMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_todo_lists: list[AmazonListInfo],
|
|
) -> None:
|
|
"""Test an event arriving during a read back is not lost by that read."""
|
|
mock_amazon_devices_client.todo_lists = mock_todo_lists
|
|
list_items: dict[str, AmazonListItem] = {}
|
|
mock_amazon_devices_client.get_todo_list_items = AsyncMock(
|
|
side_effect=lambda list_id: dict(list_items)
|
|
)
|
|
|
|
await setup_integration(hass, mock_config_entry)
|
|
coordinator = mock_config_entry.runtime_data
|
|
|
|
entity_id = MOCK_TODO_LIST_ENTITY_ID
|
|
|
|
def add_item(list_id: str, name: str) -> None:
|
|
list_items[name] = AmazonListItem(
|
|
id=name, name=name, status=AmazonListItemStatus.ACTIVE, version=1
|
|
)
|
|
|
|
mock_amazon_devices_client.add_todo_list_item = AsyncMock(side_effect=add_item)
|
|
|
|
# Hold the read back until the event has been handled
|
|
released = asyncio.Event()
|
|
|
|
async def read_items(list_id: str) -> dict[str, AmazonListItem]:
|
|
items = dict(list_items)
|
|
await released.wait()
|
|
return items
|
|
|
|
mock_amazon_devices_client.get_todo_list_items = AsyncMock(side_effect=read_items)
|
|
|
|
write = asyncio.create_task(
|
|
hass.services.async_call(
|
|
TODO_DOMAIN,
|
|
TodoServices.ADD_ITEM,
|
|
{ATTR_ENTITY_ID: entity_id, "item": "Written task"},
|
|
blocking=True,
|
|
)
|
|
)
|
|
await asyncio.sleep(0)
|
|
|
|
# Alexa reports an item of its own while the read back is in flight
|
|
pushed = asyncio.create_task(
|
|
coordinator.todo_event_handler(
|
|
AmazonListEvent(
|
|
list_id="todo_list_id",
|
|
item_id="item_6",
|
|
type=AmazonListEventType.CREATED,
|
|
items=AmazonListItem(
|
|
id="item_6",
|
|
name="Spoken task",
|
|
status=AmazonListItemStatus.ACTIVE,
|
|
version=1,
|
|
),
|
|
)
|
|
)
|
|
)
|
|
await asyncio.sleep(0)
|
|
released.set()
|
|
await write
|
|
await pushed
|
|
|
|
assert hass.states.get(entity_id).state == "2"
|
|
|
|
|
|
async def test_delete_todo_item(
|
|
hass: HomeAssistant,
|
|
mock_amazon_devices_client: AsyncMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_todo_lists: list[AmazonListInfo],
|
|
mock_todo_items: dict[str, Any],
|
|
) -> None:
|
|
"""Test deleting a todo item."""
|
|
mock_amazon_devices_client.todo_lists = mock_todo_lists
|
|
mock_amazon_devices_client.get_todo_list_items = AsyncMock(
|
|
side_effect=lambda list_id: dict(mock_todo_items.get(list_id, {}))
|
|
)
|
|
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
entity_id = MOCK_TODO_LIST_ENTITY_ID
|
|
|
|
assert hass.states.get(entity_id).state == "1"
|
|
|
|
# Amazon has dropped the item from the moment the call returns
|
|
mock_amazon_devices_client.delete_todo_list_item = AsyncMock(
|
|
side_effect=lambda list_id, item_id, version: mock_todo_items[list_id].pop(
|
|
item_id
|
|
)
|
|
)
|
|
|
|
# Delete item_2
|
|
await hass.services.async_call(
|
|
TODO_DOMAIN,
|
|
TodoServices.REMOVE_ITEM,
|
|
{ATTR_ENTITY_ID: entity_id, "item": ["item_2"]},
|
|
blocking=True,
|
|
)
|
|
|
|
mock_amazon_devices_client.delete_todo_list_item.assert_called_once_with(
|
|
"todo_list_id", "item_2", 1
|
|
)
|
|
assert hass.states.get(entity_id).state == "0"
|
|
|
|
|
|
async def test_delete_todo_items_partial_failure(
|
|
hass: HomeAssistant,
|
|
mock_amazon_devices_client: AsyncMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_todo_lists: list[AmazonListInfo],
|
|
mock_todo_items: dict[str, Any],
|
|
) -> None:
|
|
"""Test a delete that went through is not left in the cache by a later failure."""
|
|
mock_amazon_devices_client.todo_lists = mock_todo_lists
|
|
mock_amazon_devices_client.get_todo_list_items = AsyncMock(
|
|
side_effect=lambda list_id: dict(mock_todo_items.get(list_id, {}))
|
|
)
|
|
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
entity_id = MOCK_TODO_LIST_ENTITY_ID
|
|
|
|
assert hass.states.get(entity_id).state == "1"
|
|
|
|
# Amazon drops item_2 and then stops answering
|
|
def delete_item(list_id: str, item_id: str, version: int) -> None:
|
|
if item_id == "item_3":
|
|
raise CannotConnect
|
|
mock_todo_items[list_id].pop(item_id)
|
|
|
|
mock_amazon_devices_client.delete_todo_list_item = AsyncMock(
|
|
side_effect=delete_item
|
|
)
|
|
|
|
with pytest.raises(HomeAssistantError):
|
|
await hass.services.async_call(
|
|
TODO_DOMAIN,
|
|
TodoServices.REMOVE_ITEM,
|
|
{ATTR_ENTITY_ID: entity_id, "item": ["item_2", "item_3"]},
|
|
blocking=True,
|
|
)
|
|
|
|
# Reading the list back worked, so the entity stays usable
|
|
assert hass.states.get(entity_id).state == "0"
|
|
|
|
|
|
async def test_update_todo_item(
|
|
hass: HomeAssistant,
|
|
mock_amazon_devices_client: AsyncMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_todo_lists: list[AmazonListInfo],
|
|
mock_todo_items: dict[str, Any],
|
|
) -> None:
|
|
"""Test updating a todo item."""
|
|
mock_amazon_devices_client.todo_lists = mock_todo_lists
|
|
mock_amazon_devices_client.get_todo_list_items = AsyncMock(
|
|
side_effect=lambda list_id: dict(mock_todo_items.get(list_id, {}))
|
|
)
|
|
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
entity_id = MOCK_TODO_LIST_ENTITY_ID
|
|
|
|
# Update item_2 (ACTIVE -> COMPLETE)
|
|
await hass.services.async_call(
|
|
TODO_DOMAIN,
|
|
TodoServices.UPDATE_ITEM,
|
|
{
|
|
ATTR_ENTITY_ID: entity_id,
|
|
"item": "item_2",
|
|
"status": TodoItemStatus.COMPLETED,
|
|
},
|
|
blocking=True,
|
|
)
|
|
|
|
mock_amazon_devices_client.set_todo_list_item_checked_status.assert_called_once_with(
|
|
"todo_list_id", "item_2", True, 1
|
|
)
|
|
mock_amazon_devices_client.rename_todo_list_item.assert_not_called()
|
|
|
|
# Rename item_2
|
|
mock_amazon_devices_client.set_todo_list_item_checked_status.reset_mock()
|
|
await hass.services.async_call(
|
|
TODO_DOMAIN,
|
|
TodoServices.UPDATE_ITEM,
|
|
{
|
|
ATTR_ENTITY_ID: entity_id,
|
|
"item": "item_2",
|
|
"rename": "Renamed Task",
|
|
},
|
|
blocking=True,
|
|
)
|
|
|
|
mock_amazon_devices_client.rename_todo_list_item.assert_called_once_with(
|
|
"todo_list_id", "item_2", "Renamed Task", 1
|
|
)
|
|
mock_amazon_devices_client.set_todo_list_item_checked_status.assert_not_called()
|
|
|
|
# Both status and rename changed
|
|
mock_amazon_devices_client.set_todo_list_item_checked_status.reset_mock()
|
|
mock_amazon_devices_client.rename_todo_list_item.reset_mock()
|
|
await hass.services.async_call(
|
|
TODO_DOMAIN,
|
|
TodoServices.UPDATE_ITEM,
|
|
{
|
|
ATTR_ENTITY_ID: entity_id,
|
|
"item": "item_2",
|
|
"rename": "Both Changed",
|
|
"status": TodoItemStatus.COMPLETED,
|
|
},
|
|
blocking=True,
|
|
)
|
|
mock_amazon_devices_client.set_todo_list_item_checked_status.assert_called_once_with(
|
|
"todo_list_id", "item_2", True, 1
|
|
)
|
|
mock_amazon_devices_client.rename_todo_list_item.assert_called_once_with(
|
|
"todo_list_id", "item_2", "Both Changed", 2
|
|
)
|
|
|
|
# Neither status nor name changed -> no API calls
|
|
mock_amazon_devices_client.set_todo_list_item_checked_status.reset_mock()
|
|
mock_amazon_devices_client.rename_todo_list_item.reset_mock()
|
|
await hass.services.async_call(
|
|
TODO_DOMAIN,
|
|
TodoServices.UPDATE_ITEM,
|
|
{
|
|
ATTR_ENTITY_ID: entity_id,
|
|
"item": "item_2",
|
|
"rename": "Task 1",
|
|
"status": TodoItemStatus.NEEDS_ACTION,
|
|
},
|
|
blocking=True,
|
|
)
|
|
mock_amazon_devices_client.set_todo_list_item_checked_status.assert_not_called()
|
|
mock_amazon_devices_client.rename_todo_list_item.assert_not_called()
|
|
|
|
|
|
async def test_update_todo_item_refreshes_state(
|
|
hass: HomeAssistant,
|
|
mock_amazon_devices_client: AsyncMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_todo_lists: list[AmazonListInfo],
|
|
mock_todo_items: dict[str, Any],
|
|
) -> None:
|
|
"""Test the entity reflects an updated item once the call returns."""
|
|
mock_amazon_devices_client.todo_lists = mock_todo_lists
|
|
mock_amazon_devices_client.get_todo_list_items = AsyncMock(
|
|
side_effect=lambda list_id: dict(mock_todo_items.get(list_id, {}))
|
|
)
|
|
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
entity_id = MOCK_TODO_LIST_ENTITY_ID
|
|
|
|
assert hass.states.get(entity_id).state == "1"
|
|
|
|
# Amazon has the item checked from the moment the call returns
|
|
def check_item(list_id: str, item_id: str, checked: bool, version: int) -> None:
|
|
mock_todo_items[list_id][item_id] = replace(
|
|
mock_todo_items[list_id][item_id], status=AmazonListItemStatus.COMPLETE
|
|
)
|
|
|
|
mock_amazon_devices_client.set_todo_list_item_checked_status = AsyncMock(
|
|
side_effect=check_item
|
|
)
|
|
|
|
await hass.services.async_call(
|
|
TODO_DOMAIN,
|
|
TodoServices.UPDATE_ITEM,
|
|
{
|
|
ATTR_ENTITY_ID: entity_id,
|
|
"item": "item_2",
|
|
"status": TodoItemStatus.COMPLETED,
|
|
},
|
|
blocking=True,
|
|
)
|
|
|
|
assert hass.states.get(entity_id).state == "0"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("initial_lists", "updated_lists"),
|
|
[
|
|
([MOCK_TODO_LIST], [MOCK_TODO_LIST, MOCK_CUSTOM_LIST]), # Add a list
|
|
([MOCK_TODO_LIST, MOCK_CUSTOM_LIST], [MOCK_TODO_LIST]), # Remove a list
|
|
(
|
|
[MOCK_TODO_LIST, MOCK_SHOPPING_LIST, MOCK_CUSTOM_LIST],
|
|
[],
|
|
), # Remove all lists
|
|
],
|
|
)
|
|
async def test_dynamic_entities(
|
|
hass: HomeAssistant,
|
|
freezer: FrozenDateTimeFactory,
|
|
mock_amazon_devices_client: AsyncMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
initial_lists: list[AmazonListInfo],
|
|
updated_lists: list[AmazonListInfo],
|
|
) -> None:
|
|
"""Test entities are dynamically created and deleted."""
|
|
|
|
def get_entity_id(alexa_list: AmazonListInfo) -> str:
|
|
if alexa_list.list_type == AmazonListType.SHOP:
|
|
return MOCK_SHOPPING_LIST_ENTITY_ID
|
|
if alexa_list.list_type == AmazonListType.TODO:
|
|
return MOCK_TODO_LIST_ENTITY_ID
|
|
return MOCK_CUSTOM_LIST_ENTITY_ID
|
|
|
|
# Start with the initial set of lists from the Amazon client.
|
|
mock_amazon_devices_client.todo_lists = list(initial_lists)
|
|
mock_amazon_devices_client.get_todo_list_items = AsyncMock(return_value={})
|
|
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
initial_entity_ids = [get_entity_id(alexa_list) for alexa_list in initial_lists]
|
|
updated_entity_ids = [get_entity_id(alexa_list) for alexa_list in updated_lists]
|
|
|
|
# Confirm the initially expected entities exist.
|
|
for entity_id in initial_entity_ids:
|
|
assert hass.states.get(entity_id) is not None
|
|
|
|
# Update the Amazon client to return the new list set.
|
|
mock_amazon_devices_client.todo_lists = list(updated_lists)
|
|
|
|
freezer.tick(SCAN_INTERVAL)
|
|
async_fire_time_changed(hass)
|
|
await hass.async_block_till_done()
|
|
|
|
# Confirm the entities in the updated set exist.
|
|
for entity_id in updated_entity_ids:
|
|
assert hass.states.get(entity_id) is not None
|
|
|
|
# Confirm removed entities are no longer present.
|
|
for entity_id in set(initial_entity_ids) - set(updated_entity_ids):
|
|
assert hass.states.get(entity_id) is None
|
|
|
|
|
|
async def test_dynamic_add_list_and_add_item(
|
|
hass: HomeAssistant,
|
|
freezer: FrozenDateTimeFactory,
|
|
mock_amazon_devices_client: AsyncMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""Test adding a new list and then adding an item to it."""
|
|
mock_amazon_devices_client.todo_lists = []
|
|
mock_amazon_devices_client.get_todo_list_items = AsyncMock(return_value={})
|
|
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
assert hass.states.get(MOCK_TODO_LIST_ENTITY_ID) is None
|
|
|
|
mock_amazon_devices_client.todo_lists = [MOCK_TODO_LIST]
|
|
|
|
freezer.tick(SCAN_INTERVAL)
|
|
async_fire_time_changed(hass)
|
|
await hass.async_block_till_done()
|
|
|
|
# Send CREATED event for new item in the newly created list (imitate Amazon server)
|
|
new_item_id = "item_1"
|
|
new_item = AmazonListItem(
|
|
id=new_item_id,
|
|
name="New Task",
|
|
status=AmazonListItemStatus.ACTIVE,
|
|
version=1,
|
|
)
|
|
created_event = AmazonListEvent(
|
|
list_id=MOCK_TODO_LIST.id,
|
|
item_id=new_item_id,
|
|
type=AmazonListEventType.CREATED,
|
|
items=new_item,
|
|
)
|
|
|
|
coordinator = mock_config_entry.runtime_data
|
|
await coordinator.todo_event_handler(created_event)
|
|
|
|
assert hass.states.get(MOCK_TODO_LIST_ENTITY_ID) is not None
|
|
|
|
|
|
async def test_todo_event_handler(
|
|
hass: HomeAssistant,
|
|
mock_amazon_devices_client: AsyncMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_todo_lists: list[AmazonListInfo],
|
|
) -> None:
|
|
"""Test todo event handler."""
|
|
mock_amazon_devices_client.todo_lists = mock_todo_lists
|
|
mock_amazon_devices_client.get_todo_list_items = AsyncMock(return_value={})
|
|
|
|
await setup_integration(hass, mock_config_entry)
|
|
coordinator = mock_config_entry.runtime_data
|
|
|
|
# Seed _todo_list_items
|
|
list_id = "todo_list_id"
|
|
item_id = "item_1"
|
|
item = AmazonListItem(
|
|
id=item_id,
|
|
name="Original Task",
|
|
status=AmazonListItemStatus.ACTIVE,
|
|
version=1,
|
|
)
|
|
coordinator.todo_list_items[list_id] = {item_id: item}
|
|
|
|
# Test CREATED
|
|
new_item_id = "item_2"
|
|
new_item = AmazonListItem(
|
|
id=new_item_id,
|
|
name="New Task",
|
|
status=AmazonListItemStatus.ACTIVE,
|
|
version=1,
|
|
)
|
|
created_event = AmazonListEvent(
|
|
list_id=list_id,
|
|
item_id=new_item_id,
|
|
type=AmazonListEventType.CREATED,
|
|
items=new_item,
|
|
)
|
|
await coordinator.todo_event_handler(created_event)
|
|
assert coordinator.todo_list_items[list_id][new_item_id] == new_item
|
|
|
|
# Test UPDATED
|
|
updated_item = AmazonListItem(
|
|
id=item_id,
|
|
name="Updated Task",
|
|
status=AmazonListItemStatus.COMPLETE,
|
|
version=2,
|
|
)
|
|
updated_event = AmazonListEvent(
|
|
list_id=list_id,
|
|
item_id=item_id,
|
|
type=AmazonListEventType.UPDATED,
|
|
items=updated_item,
|
|
)
|
|
await coordinator.todo_event_handler(updated_event)
|
|
assert coordinator.todo_list_items[list_id][item_id] == updated_item
|
|
|
|
# Test DELETED
|
|
deleted_event = AmazonListEvent(
|
|
list_id=list_id,
|
|
item_id=item_id,
|
|
type=AmazonListEventType.DELETED,
|
|
items=None,
|
|
)
|
|
await coordinator.todo_event_handler(deleted_event)
|
|
assert item_id not in coordinator.todo_list_items[list_id]
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("side_effect", "expected_state"),
|
|
[
|
|
pytest.param(
|
|
CannotAuthenticate,
|
|
ConfigEntryState.SETUP_ERROR,
|
|
id="cannot_authenticate",
|
|
),
|
|
pytest.param(
|
|
CannotConnect,
|
|
ConfigEntryState.LOADED,
|
|
id="cannot_connect",
|
|
),
|
|
pytest.param(
|
|
CannotRetrieveData,
|
|
ConfigEntryState.LOADED,
|
|
id="cannot_retrieve_data",
|
|
),
|
|
],
|
|
)
|
|
async def test_sync_todo_list_items_error(
|
|
hass: HomeAssistant,
|
|
mock_amazon_devices_client: AsyncMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
side_effect: type[Exception],
|
|
expected_state: ConfigEntryState,
|
|
) -> None:
|
|
"""Test syncing todo list items handles errors without blocking setup."""
|
|
mock_amazon_devices_client.get_todo_list_items.side_effect = side_effect
|
|
mock_amazon_devices_client.todo_lists = [
|
|
AmazonListInfo(id="shopping_list_id", name=None, list_type=AmazonListType.SHOP)
|
|
]
|
|
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
assert mock_config_entry.state is expected_state
|