core/tests/components/todoist/test_calendar.py

262 lines
7.6 KiB
Python
Raw Normal View History

"""Unit tests for the Todoist calendar platform."""
from http import HTTPStatus
from typing import Any
from unittest.mock import AsyncMock, patch
import urllib
import pytest
from todoist_api_python.models import Collaborator, Due, Label, Project, Task
from homeassistant import setup
from homeassistant.components.todoist.const import (
ASSIGNEE,
CONTENT,
DOMAIN,
LABELS,
PROJECT_NAME,
SERVICE_NEW_TASK,
)
from homeassistant.const import CONF_TOKEN
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_component import async_update_entity
from homeassistant.util import dt
from tests.typing import ClientSessionGenerator
SUMMARY = "A task"
@pytest.fixture(autouse=True)
def set_time_zone(hass: HomeAssistant):
"""Set the time zone for the tests."""
# Set our timezone to CST/Regina so we can check calculations
# This keeps UTC-6 all year round
hass.config.set_time_zone("America/Regina")
@pytest.fixture(name="due")
def mock_due() -> Due:
"""Mock a todoist Task Due date/time."""
return Due(is_recurring=False, date=dt.now().strftime("%Y-%m-%d"), string="today")
@pytest.fixture(name="task")
def mock_task(due: Due) -> Task:
"""Mock a todoist Task instance."""
return Task(
assignee_id="1",
assigner_id="1",
comment_count=0,
is_completed=False,
content=SUMMARY,
created_at="2021-10-01T00:00:00",
creator_id="1",
description="A task",
due=due,
id="1",
labels=["Label1"],
order=1,
parent_id=None,
priority=1,
project_id="12345",
section_id=None,
url="https://todoist.com",
sync_id=None,
)
@pytest.fixture(name="api")
def mock_api(task) -> AsyncMock:
"""Mock the api state."""
api = AsyncMock()
api.get_projects.return_value = [
Project(
id="12345",
color="blue",
comment_count=0,
is_favorite=False,
name="Name",
is_shared=False,
url="",
is_inbox_project=False,
is_team_inbox=False,
order=1,
parent_id=None,
view_style="list",
)
]
api.get_labels.return_value = [
Label(id="1", name="Label1", color="1", order=1, is_favorite=False)
]
api.get_collaborators.return_value = [
Collaborator(email="user@gmail.com", id="1", name="user")
]
api.get_tasks.return_value = [task]
return api
def get_events_url(entity: str, start: str, end: str) -> str:
"""Create a url to get events during the specified time range."""
return f"/api/calendars/{entity}?start={urllib.parse.quote(start)}&end={urllib.parse.quote(end)}"
def get_events_response(start: dict[str, str], end: dict[str, str]) -> dict[str, Any]:
"""Return an event response with a single task."""
return {
"start": start,
"end": end,
"summary": SUMMARY,
"description": None,
"location": None,
"uid": None,
"recurrence_id": None,
"rrule": None,
}
@pytest.fixture(name="todoist_config")
def mock_todoist_config() -> dict[str, Any]:
"""Mock todoist configuration."""
return {}
@pytest.fixture(name="setup_integration", autouse=True)
async def mock_setup_integration(
hass: HomeAssistant,
api: AsyncMock,
todoist_config: dict[str, Any],
) -> None:
"""Mock setup of the todoist integration."""
with patch(
"homeassistant.components.todoist.calendar.TodoistAPIAsync"
) as todoist_api:
todoist_api.return_value = api
assert await setup.async_setup_component(
hass,
"calendar",
{
"calendar": {
"platform": DOMAIN,
CONF_TOKEN: "token",
**todoist_config,
}
},
)
await hass.async_block_till_done()
await async_update_entity(hass, "calendar.name")
yield
async def test_calendar_entity_unique_id(
hass: HomeAssistant, api: AsyncMock, entity_registry: er.EntityRegistry
) -> None:
"""Test unique id is set to project id."""
entity = entity_registry.async_get("calendar.name")
2023-01-30 10:03:23 +00:00
assert entity.unique_id == "12345"
@pytest.mark.parametrize(
"todoist_config",
[{"custom_projects": [{"name": "All projects", "labels": ["Label1"]}]}],
)
2023-03-10 11:06:50 +00:00
async def test_update_entity_for_custom_project_with_labels_on(
hass: HomeAssistant,
api: AsyncMock,
2023-03-10 11:06:50 +00:00
) -> None:
"""Test that the calendar's state is on for a custom project using labels."""
await async_update_entity(hass, "calendar.all_projects")
state = hass.states.get("calendar.all_projects")
assert state.attributes["labels"] == ["Label1"]
assert state.state == "on"
@pytest.mark.parametrize("due", [None])
async def test_update_entity_for_custom_project_no_due_date_on(
hass: HomeAssistant,
api: AsyncMock,
) -> None:
"""Test that a task without an explicit due date is considered to be in an on state."""
await async_update_entity(hass, "calendar.name")
state = hass.states.get("calendar.name")
assert state.state == "on"
@pytest.mark.parametrize("setup_integration", [None])
async def test_failed_coordinator_update(hass: HomeAssistant, api: AsyncMock) -> None:
"""Test a failed data coordinator update is handled correctly."""
api.get_tasks.side_effect = Exception("API error")
assert await setup.async_setup_component(
hass,
"calendar",
{
"calendar": {
"platform": DOMAIN,
CONF_TOKEN: "token",
"custom_projects": [{"name": "All projects", "labels": ["Label1"]}],
}
},
)
await hass.async_block_till_done()
await async_update_entity(hass, "calendar.all_projects")
state = hass.states.get("calendar.all_projects")
assert state is None
@pytest.mark.parametrize(
"todoist_config",
[{"custom_projects": [{"name": "All projects"}]}],
)
async def test_calendar_custom_project_unique_id(
hass: HomeAssistant, entity_registry: er.EntityRegistry
) -> None:
"""Test unique id is None for any custom projects."""
entity = entity_registry.async_get("calendar.all_projects")
assert entity is None
state = hass.states.get("calendar.all_projects")
assert state.state == "off"
@pytest.mark.parametrize(
("due", "start", "end", "expected_response"),
[
(
Due(date="2023-03-30", is_recurring=False, string="Mar 30"),
"2023-03-28T00:00:00.000Z",
"2023-04-01T00:00:00.000Z",
[get_events_response({"date": "2023-03-30"}, {"date": "2023-03-31"})],
)
],
)
async def test_all_day_event(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
start: str,
end: str,
expected_response: dict[str, Any],
) -> None:
"""Test for an all day calendar event."""
client = await hass_client()
response = await client.get(
get_events_url("calendar.name", start, end),
)
assert response.status == HTTPStatus.OK
assert await response.json() == expected_response
async def test_create_task_service_call(hass: HomeAssistant, api: AsyncMock) -> None:
"""Test api is called correctly after a new task service call."""
await hass.services.async_call(
DOMAIN,
SERVICE_NEW_TASK,
{ASSIGNEE: "user", CONTENT: "task", LABELS: ["Label1"], PROJECT_NAME: "Name"},
)
await hass.async_block_till_done()
api.add_task.assert_called_with(
"task", project_id="12345", labels=["Label1"], assignee_id="1"
)