2023-10-25 04:30:29 +00:00
|
|
|
"""Tests for Google Tasks todo platform."""
|
|
|
|
|
|
|
|
|
|
|
|
from collections.abc import Awaitable, Callable
|
|
|
|
import json
|
2023-10-25 08:51:21 +00:00
|
|
|
from typing import Any
|
|
|
|
from unittest.mock import Mock, patch
|
2023-10-25 04:30:29 +00:00
|
|
|
|
|
|
|
from httplib2 import Response
|
|
|
|
import pytest
|
2023-10-25 08:51:21 +00:00
|
|
|
from syrupy.assertion import SnapshotAssertion
|
2023-10-25 04:30:29 +00:00
|
|
|
|
2023-10-25 08:51:21 +00:00
|
|
|
from homeassistant.components.todo import DOMAIN as TODO_DOMAIN
|
2023-10-25 04:30:29 +00:00
|
|
|
from homeassistant.const import Platform
|
|
|
|
from homeassistant.core import HomeAssistant
|
|
|
|
|
|
|
|
from tests.typing import WebSocketGenerator
|
|
|
|
|
|
|
|
ENTITY_ID = "todo.my_tasks"
|
|
|
|
LIST_TASK_LIST_RESPONSE = {
|
|
|
|
"items": [
|
|
|
|
{
|
|
|
|
"id": "task-list-id-1",
|
|
|
|
"title": "My tasks",
|
|
|
|
},
|
|
|
|
]
|
|
|
|
}
|
2023-10-25 08:51:21 +00:00
|
|
|
EMPTY_RESPONSE = {}
|
|
|
|
LIST_TASKS_RESPONSE = {
|
|
|
|
"items": [],
|
|
|
|
}
|
2023-10-25 04:30:29 +00:00
|
|
|
|
2023-10-31 01:03:34 +00:00
|
|
|
LIST_TASKS_RESPONSE_WATER = {
|
|
|
|
"items": [
|
|
|
|
{"id": "some-task-id", "title": "Water", "status": "needsAction"},
|
|
|
|
],
|
|
|
|
}
|
|
|
|
|
2023-10-25 04:30:29 +00:00
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def platforms() -> list[str]:
|
|
|
|
"""Fixture to specify platforms to test."""
|
|
|
|
return [Platform.TODO]
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def ws_req_id() -> Callable[[], int]:
|
|
|
|
"""Fixture for incremental websocket requests."""
|
|
|
|
|
|
|
|
id = 0
|
|
|
|
|
|
|
|
def next_id() -> int:
|
|
|
|
nonlocal id
|
|
|
|
id += 1
|
|
|
|
return id
|
|
|
|
|
|
|
|
return next_id
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
async def ws_get_items(
|
|
|
|
hass_ws_client: WebSocketGenerator, ws_req_id: Callable[[], int]
|
|
|
|
) -> Callable[[], Awaitable[dict[str, str]]]:
|
|
|
|
"""Fixture to fetch items from the todo websocket."""
|
|
|
|
|
|
|
|
async def get() -> list[dict[str, str]]:
|
|
|
|
# Fetch items using To-do platform
|
|
|
|
client = await hass_ws_client()
|
|
|
|
id = ws_req_id()
|
|
|
|
await client.send_json(
|
|
|
|
{
|
|
|
|
"id": id,
|
|
|
|
"type": "todo/item/list",
|
|
|
|
"entity_id": ENTITY_ID,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
resp = await client.receive_json()
|
|
|
|
assert resp.get("id") == id
|
|
|
|
assert resp.get("success")
|
|
|
|
return resp.get("result", {}).get("items", [])
|
|
|
|
|
|
|
|
return get
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(name="api_responses")
|
|
|
|
def mock_api_responses() -> list[dict | list]:
|
|
|
|
"""Fixture for API responses to return during test."""
|
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
2023-10-25 08:51:21 +00:00
|
|
|
def mock_http_response(api_responses: list[dict | list]) -> Mock:
|
2023-10-25 04:30:29 +00:00
|
|
|
"""Fixture to fake out http2lib responses."""
|
|
|
|
responses = [
|
|
|
|
(Response({}), bytes(json.dumps(api_response), encoding="utf-8"))
|
|
|
|
for api_response in api_responses
|
|
|
|
]
|
2023-10-25 08:51:21 +00:00
|
|
|
with patch("httplib2.Http.request", side_effect=responses) as mock_response:
|
|
|
|
yield mock_response
|
2023-10-25 04:30:29 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"api_responses",
|
|
|
|
[
|
|
|
|
[
|
|
|
|
LIST_TASK_LIST_RESPONSE,
|
|
|
|
{
|
|
|
|
"items": [
|
|
|
|
{"id": "task-1", "title": "Task 1", "status": "needsAction"},
|
|
|
|
{"id": "task-2", "title": "Task 2", "status": "completed"},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
]
|
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_get_items(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
setup_credentials: None,
|
|
|
|
integration_setup: Callable[[], Awaitable[bool]],
|
|
|
|
hass_ws_client: WebSocketGenerator,
|
|
|
|
ws_get_items: Callable[[], Awaitable[dict[str, str]]],
|
|
|
|
) -> None:
|
|
|
|
"""Test getting todo list items."""
|
|
|
|
|
|
|
|
assert await integration_setup()
|
|
|
|
|
|
|
|
await hass_ws_client(hass)
|
|
|
|
|
|
|
|
items = await ws_get_items()
|
|
|
|
assert items == [
|
|
|
|
{
|
|
|
|
"uid": "task-1",
|
|
|
|
"summary": "Task 1",
|
|
|
|
"status": "needs_action",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"uid": "task-2",
|
|
|
|
"summary": "Task 2",
|
|
|
|
"status": "completed",
|
|
|
|
},
|
|
|
|
]
|
|
|
|
|
|
|
|
# State reflect that one task needs action
|
|
|
|
state = hass.states.get("todo.my_tasks")
|
|
|
|
assert state
|
|
|
|
assert state.state == "1"
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"api_responses",
|
|
|
|
[
|
|
|
|
[
|
|
|
|
LIST_TASK_LIST_RESPONSE,
|
2023-10-25 08:51:21 +00:00
|
|
|
LIST_TASKS_RESPONSE,
|
2023-10-25 04:30:29 +00:00
|
|
|
]
|
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_empty_todo_list(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
setup_credentials: None,
|
|
|
|
integration_setup: Callable[[], Awaitable[bool]],
|
|
|
|
hass_ws_client: WebSocketGenerator,
|
|
|
|
ws_get_items: Callable[[], Awaitable[dict[str, str]]],
|
|
|
|
) -> None:
|
|
|
|
"""Test getting todo list items."""
|
|
|
|
|
|
|
|
assert await integration_setup()
|
|
|
|
|
|
|
|
await hass_ws_client(hass)
|
|
|
|
|
|
|
|
items = await ws_get_items()
|
|
|
|
assert items == []
|
|
|
|
|
|
|
|
state = hass.states.get("todo.my_tasks")
|
|
|
|
assert state
|
|
|
|
assert state.state == "0"
|
2023-10-25 08:51:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"api_responses",
|
|
|
|
[
|
|
|
|
[
|
|
|
|
LIST_TASK_LIST_RESPONSE,
|
|
|
|
LIST_TASKS_RESPONSE,
|
|
|
|
EMPTY_RESPONSE, # create
|
|
|
|
LIST_TASKS_RESPONSE, # refresh after create
|
|
|
|
]
|
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_create_todo_list_item(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
setup_credentials: None,
|
|
|
|
integration_setup: Callable[[], Awaitable[bool]],
|
|
|
|
mock_http_response: Mock,
|
|
|
|
snapshot: SnapshotAssertion,
|
|
|
|
) -> None:
|
|
|
|
"""Test for creating a To-do Item."""
|
|
|
|
|
|
|
|
assert await integration_setup()
|
|
|
|
|
|
|
|
state = hass.states.get("todo.my_tasks")
|
|
|
|
assert state
|
|
|
|
assert state.state == "0"
|
|
|
|
|
|
|
|
await hass.services.async_call(
|
|
|
|
TODO_DOMAIN,
|
2023-10-31 01:03:34 +00:00
|
|
|
"add_item",
|
|
|
|
{"item": "Soda"},
|
2023-10-25 08:51:21 +00:00
|
|
|
target={"entity_id": "todo.my_tasks"},
|
|
|
|
blocking=True,
|
|
|
|
)
|
|
|
|
assert len(mock_http_response.call_args_list) == 4
|
|
|
|
call = mock_http_response.call_args_list[2]
|
|
|
|
assert call
|
|
|
|
assert call.args == snapshot
|
|
|
|
assert call.kwargs.get("body") == snapshot
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"api_responses",
|
|
|
|
[
|
|
|
|
[
|
|
|
|
LIST_TASK_LIST_RESPONSE,
|
2023-10-31 01:03:34 +00:00
|
|
|
LIST_TASKS_RESPONSE_WATER,
|
2023-10-25 08:51:21 +00:00
|
|
|
EMPTY_RESPONSE, # update
|
|
|
|
LIST_TASKS_RESPONSE, # refresh after update
|
|
|
|
]
|
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_update_todo_list_item(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
setup_credentials: None,
|
|
|
|
integration_setup: Callable[[], Awaitable[bool]],
|
|
|
|
mock_http_response: Any,
|
|
|
|
snapshot: SnapshotAssertion,
|
|
|
|
) -> None:
|
|
|
|
"""Test for updating a To-do Item."""
|
|
|
|
|
|
|
|
assert await integration_setup()
|
|
|
|
|
|
|
|
state = hass.states.get("todo.my_tasks")
|
|
|
|
assert state
|
2023-10-31 01:03:34 +00:00
|
|
|
assert state.state == "1"
|
2023-10-25 08:51:21 +00:00
|
|
|
|
|
|
|
await hass.services.async_call(
|
|
|
|
TODO_DOMAIN,
|
|
|
|
"update_item",
|
2023-10-31 01:03:34 +00:00
|
|
|
{"item": "some-task-id", "rename": "Soda", "status": "completed"},
|
2023-10-25 08:51:21 +00:00
|
|
|
target={"entity_id": "todo.my_tasks"},
|
|
|
|
blocking=True,
|
|
|
|
)
|
|
|
|
assert len(mock_http_response.call_args_list) == 4
|
|
|
|
call = mock_http_response.call_args_list[2]
|
|
|
|
assert call
|
|
|
|
assert call.args == snapshot
|
|
|
|
assert call.kwargs.get("body") == snapshot
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"api_responses",
|
|
|
|
[
|
|
|
|
[
|
|
|
|
LIST_TASK_LIST_RESPONSE,
|
2023-10-31 01:03:34 +00:00
|
|
|
LIST_TASKS_RESPONSE_WATER,
|
2023-10-25 08:51:21 +00:00
|
|
|
EMPTY_RESPONSE, # update
|
|
|
|
LIST_TASKS_RESPONSE, # refresh after update
|
|
|
|
]
|
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_partial_update_title(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
setup_credentials: None,
|
|
|
|
integration_setup: Callable[[], Awaitable[bool]],
|
|
|
|
mock_http_response: Any,
|
|
|
|
snapshot: SnapshotAssertion,
|
|
|
|
) -> None:
|
|
|
|
"""Test for partial update with title only."""
|
|
|
|
|
|
|
|
assert await integration_setup()
|
|
|
|
|
|
|
|
state = hass.states.get("todo.my_tasks")
|
|
|
|
assert state
|
2023-10-31 01:03:34 +00:00
|
|
|
assert state.state == "1"
|
2023-10-25 08:51:21 +00:00
|
|
|
|
|
|
|
await hass.services.async_call(
|
|
|
|
TODO_DOMAIN,
|
|
|
|
"update_item",
|
2023-10-31 01:03:34 +00:00
|
|
|
{"item": "some-task-id", "rename": "Soda"},
|
2023-10-25 08:51:21 +00:00
|
|
|
target={"entity_id": "todo.my_tasks"},
|
|
|
|
blocking=True,
|
|
|
|
)
|
|
|
|
assert len(mock_http_response.call_args_list) == 4
|
|
|
|
call = mock_http_response.call_args_list[2]
|
|
|
|
assert call
|
|
|
|
assert call.args == snapshot
|
|
|
|
assert call.kwargs.get("body") == snapshot
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"api_responses",
|
|
|
|
[
|
|
|
|
[
|
|
|
|
LIST_TASK_LIST_RESPONSE,
|
2023-10-31 01:03:34 +00:00
|
|
|
LIST_TASKS_RESPONSE_WATER,
|
2023-10-25 08:51:21 +00:00
|
|
|
EMPTY_RESPONSE, # update
|
|
|
|
LIST_TASKS_RESPONSE, # refresh after update
|
|
|
|
]
|
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_partial_update_status(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
setup_credentials: None,
|
|
|
|
integration_setup: Callable[[], Awaitable[bool]],
|
|
|
|
mock_http_response: Any,
|
|
|
|
snapshot: SnapshotAssertion,
|
|
|
|
) -> None:
|
|
|
|
"""Test for partial update with status only."""
|
|
|
|
|
|
|
|
assert await integration_setup()
|
|
|
|
|
|
|
|
state = hass.states.get("todo.my_tasks")
|
|
|
|
assert state
|
2023-10-31 01:03:34 +00:00
|
|
|
assert state.state == "1"
|
2023-10-25 08:51:21 +00:00
|
|
|
|
|
|
|
await hass.services.async_call(
|
|
|
|
TODO_DOMAIN,
|
|
|
|
"update_item",
|
2023-10-31 01:03:34 +00:00
|
|
|
{"item": "some-task-id", "status": "needs_action"},
|
2023-10-25 08:51:21 +00:00
|
|
|
target={"entity_id": "todo.my_tasks"},
|
|
|
|
blocking=True,
|
|
|
|
)
|
|
|
|
assert len(mock_http_response.call_args_list) == 4
|
|
|
|
call = mock_http_response.call_args_list[2]
|
|
|
|
assert call
|
|
|
|
assert call.args == snapshot
|
|
|
|
assert call.kwargs.get("body") == snapshot
|