mirror of https://github.com/home-assistant/core
288 lines
8.2 KiB
Python
288 lines
8.2 KiB
Python
"""Unit tests for the OurGroceries todo platform."""
|
|
|
|
from unittest.mock import AsyncMock
|
|
|
|
from aiohttp import ClientError
|
|
from freezegun.api import FrozenDateTimeFactory
|
|
import pytest
|
|
|
|
from homeassistant.components.ourgroceries.coordinator import SCAN_INTERVAL
|
|
from homeassistant.components.todo import (
|
|
ATTR_ITEM,
|
|
ATTR_RENAME,
|
|
ATTR_STATUS,
|
|
DOMAIN as TODO_DOMAIN,
|
|
TodoServices,
|
|
)
|
|
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers.entity_component import async_update_entity
|
|
|
|
from . import items_to_shopping_list
|
|
|
|
from tests.common import async_fire_time_changed
|
|
|
|
|
|
def _mock_version_id(og: AsyncMock, version: int) -> None:
|
|
og.get_my_lists.return_value["shoppingLists"][0]["versionId"] = str(version)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("items", "expected_state"),
|
|
[
|
|
([], "0"),
|
|
([{"id": "12345", "name": "Soda"}], "1"),
|
|
([{"id": "12345", "name": "Soda", "crossedOffAt": 1699107501}], "0"),
|
|
(
|
|
[
|
|
{"id": "12345", "name": "Soda"},
|
|
{"id": "54321", "name": "Milk"},
|
|
],
|
|
"2",
|
|
),
|
|
],
|
|
)
|
|
async def test_todo_item_state(
|
|
hass: HomeAssistant,
|
|
setup_integration: None,
|
|
expected_state: str,
|
|
) -> None:
|
|
"""Test for a shopping list entity state."""
|
|
|
|
state = hass.states.get("todo.test_list")
|
|
assert state
|
|
assert state.state == expected_state
|
|
|
|
|
|
async def test_add_todo_list_item(
|
|
hass: HomeAssistant,
|
|
setup_integration: None,
|
|
ourgroceries: AsyncMock,
|
|
) -> None:
|
|
"""Test for adding an item."""
|
|
|
|
state = hass.states.get("todo.test_list")
|
|
assert state
|
|
assert state.state == "0"
|
|
|
|
ourgroceries.add_item_to_list = AsyncMock()
|
|
# Fake API response when state is refreshed after create
|
|
_mock_version_id(ourgroceries, 2)
|
|
ourgroceries.get_list_items.return_value = items_to_shopping_list(
|
|
[{"id": "12345", "name": "Soda"}],
|
|
version_id="2",
|
|
)
|
|
|
|
await hass.services.async_call(
|
|
TODO_DOMAIN,
|
|
TodoServices.ADD_ITEM,
|
|
{ATTR_ITEM: "Soda"},
|
|
target={ATTR_ENTITY_ID: "todo.test_list"},
|
|
blocking=True,
|
|
)
|
|
|
|
args = ourgroceries.add_item_to_list.call_args
|
|
assert args
|
|
assert args.args == ("test_list", "Soda")
|
|
assert args.kwargs.get("auto_category") is True
|
|
|
|
# Verify state is refreshed
|
|
state = hass.states.get("todo.test_list")
|
|
assert state
|
|
assert state.state == "1"
|
|
|
|
|
|
@pytest.mark.parametrize(("items"), [[{"id": "12345", "name": "Soda"}]])
|
|
async def test_update_todo_item_status(
|
|
hass: HomeAssistant,
|
|
setup_integration: None,
|
|
ourgroceries: AsyncMock,
|
|
) -> None:
|
|
"""Test for updating the completion status of an item."""
|
|
|
|
state = hass.states.get("todo.test_list")
|
|
assert state
|
|
assert state.state == "1"
|
|
|
|
ourgroceries.toggle_item_crossed_off = AsyncMock()
|
|
|
|
# Fake API response when state is refreshed after crossing off
|
|
_mock_version_id(ourgroceries, 2)
|
|
ourgroceries.get_list_items.return_value = items_to_shopping_list(
|
|
[{"id": "12345", "name": "Soda", "crossedOffAt": 1699107501}]
|
|
)
|
|
|
|
await hass.services.async_call(
|
|
TODO_DOMAIN,
|
|
TodoServices.UPDATE_ITEM,
|
|
{ATTR_ITEM: "12345", ATTR_STATUS: "completed"},
|
|
target={ATTR_ENTITY_ID: "todo.test_list"},
|
|
blocking=True,
|
|
)
|
|
assert ourgroceries.toggle_item_crossed_off.called
|
|
args = ourgroceries.toggle_item_crossed_off.call_args
|
|
assert args
|
|
assert args.args == ("test_list", "12345")
|
|
assert args.kwargs.get("cross_off") is True
|
|
|
|
# Verify state is refreshed
|
|
state = hass.states.get("todo.test_list")
|
|
assert state
|
|
assert state.state == "0"
|
|
|
|
# Fake API response when state is refreshed after reopen
|
|
_mock_version_id(ourgroceries, 2)
|
|
ourgroceries.get_list_items.return_value = items_to_shopping_list(
|
|
[{"id": "12345", "name": "Soda"}]
|
|
)
|
|
|
|
await hass.services.async_call(
|
|
TODO_DOMAIN,
|
|
TodoServices.UPDATE_ITEM,
|
|
{ATTR_ITEM: "12345", ATTR_STATUS: "needs_action"},
|
|
target={ATTR_ENTITY_ID: "todo.test_list"},
|
|
blocking=True,
|
|
)
|
|
assert ourgroceries.toggle_item_crossed_off.called
|
|
args = ourgroceries.toggle_item_crossed_off.call_args
|
|
assert args
|
|
assert args.args == ("test_list", "12345")
|
|
assert args.kwargs.get("cross_off") is False
|
|
|
|
# Verify state is refreshed
|
|
state = hass.states.get("todo.test_list")
|
|
assert state
|
|
assert state.state == "1"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("items", "category"),
|
|
[
|
|
(
|
|
[{"id": "12345", "name": "Soda", "categoryId": "test_category"}],
|
|
"test_category",
|
|
),
|
|
([{"id": "12345", "name": "Uncategorized"}], None),
|
|
],
|
|
)
|
|
async def test_update_todo_item_summary(
|
|
hass: HomeAssistant,
|
|
setup_integration: None,
|
|
ourgroceries: AsyncMock,
|
|
category: str | None,
|
|
) -> None:
|
|
"""Test for updating an item summary."""
|
|
|
|
state = hass.states.get("todo.test_list")
|
|
assert state
|
|
assert state.state == "1"
|
|
|
|
ourgroceries.change_item_on_list = AsyncMock()
|
|
|
|
# Fake API response when state is refreshed update
|
|
_mock_version_id(ourgroceries, 2)
|
|
ourgroceries.get_list_items.return_value = items_to_shopping_list(
|
|
[{"id": "12345", "name": "Milk"}]
|
|
)
|
|
|
|
await hass.services.async_call(
|
|
TODO_DOMAIN,
|
|
TodoServices.UPDATE_ITEM,
|
|
{ATTR_ITEM: "12345", ATTR_RENAME: "Milk"},
|
|
target={ATTR_ENTITY_ID: "todo.test_list"},
|
|
blocking=True,
|
|
)
|
|
assert ourgroceries.change_item_on_list
|
|
args = ourgroceries.change_item_on_list.call_args
|
|
assert args.args == ("test_list", "12345", category, "Milk")
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("items"),
|
|
[
|
|
[
|
|
{"id": "12345", "name": "Soda"},
|
|
{"id": "54321", "name": "Milk"},
|
|
]
|
|
],
|
|
)
|
|
async def test_remove_todo_item(
|
|
hass: HomeAssistant,
|
|
setup_integration: None,
|
|
ourgroceries: AsyncMock,
|
|
) -> None:
|
|
"""Test for removing an item."""
|
|
|
|
state = hass.states.get("todo.test_list")
|
|
assert state
|
|
assert state.state == "2"
|
|
|
|
ourgroceries.remove_item_from_list = AsyncMock()
|
|
# Fake API response when state is refreshed after remove
|
|
_mock_version_id(ourgroceries, 2)
|
|
ourgroceries.get_list_items.return_value = items_to_shopping_list([])
|
|
|
|
await hass.services.async_call(
|
|
TODO_DOMAIN,
|
|
TodoServices.REMOVE_ITEM,
|
|
{ATTR_ITEM: ["12345", "54321"]},
|
|
target={ATTR_ENTITY_ID: "todo.test_list"},
|
|
blocking=True,
|
|
)
|
|
assert ourgroceries.remove_item_from_list.call_count == 2
|
|
args = ourgroceries.remove_item_from_list.call_args_list
|
|
assert args[0].args == ("test_list", "12345")
|
|
assert args[1].args == ("test_list", "54321")
|
|
|
|
await async_update_entity(hass, "todo.test_list")
|
|
state = hass.states.get("todo.test_list")
|
|
assert state
|
|
assert state.state == "0"
|
|
|
|
|
|
async def test_version_id_optimization(
|
|
hass: HomeAssistant,
|
|
freezer: FrozenDateTimeFactory,
|
|
setup_integration: None,
|
|
ourgroceries: AsyncMock,
|
|
) -> None:
|
|
"""Test that list items aren't being retrieved if version id stays the same."""
|
|
state = hass.states.get("todo.test_list")
|
|
assert state.state == "0"
|
|
assert ourgroceries.get_list_items.call_count == 1
|
|
freezer.tick(SCAN_INTERVAL)
|
|
async_fire_time_changed(hass)
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get("todo.test_list")
|
|
assert state.state == "0"
|
|
assert ourgroceries.get_list_items.call_count == 1
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("exception"),
|
|
[
|
|
(ClientError),
|
|
(TimeoutError),
|
|
],
|
|
)
|
|
async def test_coordinator_error(
|
|
hass: HomeAssistant,
|
|
freezer: FrozenDateTimeFactory,
|
|
setup_integration: None,
|
|
ourgroceries: AsyncMock,
|
|
exception: Exception,
|
|
) -> None:
|
|
"""Test error on coordinator update."""
|
|
state = hass.states.get("todo.test_list")
|
|
assert state.state == "0"
|
|
|
|
_mock_version_id(ourgroceries, 2)
|
|
ourgroceries.get_list_items.side_effect = exception
|
|
freezer.tick(SCAN_INTERVAL)
|
|
async_fire_time_changed(hass)
|
|
await hass.async_block_till_done(wait_background_tasks=True)
|
|
|
|
state = hass.states.get("todo.test_list")
|
|
assert state.state == STATE_UNAVAILABLE
|