mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Add update reward action to Habitica integration (#139157)
This commit is contained in:
parent
694a77fe3c
commit
2509353221
@ -35,6 +35,10 @@ ATTR_TYPE = "type"
|
||||
ATTR_PRIORITY = "priority"
|
||||
ATTR_TAG = "tag"
|
||||
ATTR_KEYWORD = "keyword"
|
||||
ATTR_REMOVE_TAG = "remove_tag"
|
||||
ATTR_ALIAS = "alias"
|
||||
ATTR_PRIORITY = "priority"
|
||||
ATTR_COST = "cost"
|
||||
|
||||
SERVICE_CAST_SKILL = "cast_skill"
|
||||
SERVICE_START_QUEST = "start_quest"
|
||||
@ -50,6 +54,7 @@ SERVICE_SCORE_REWARD = "score_reward"
|
||||
|
||||
SERVICE_TRANSFORMATION = "transformation"
|
||||
|
||||
SERVICE_UPDATE_REWARD = "update_reward"
|
||||
|
||||
DEVELOPER_ID = "4c4ca53f-c059-4ffa-966e-9d29dd405daf"
|
||||
X_CLIENT = f"{DEVELOPER_ID} - {APPLICATION_NAME} {__version__}"
|
||||
|
@ -217,6 +217,13 @@
|
||||
"sections": {
|
||||
"filter": "mdi:calendar-filter"
|
||||
}
|
||||
},
|
||||
"update_reward": {
|
||||
"service": "mdi:treasure-chest",
|
||||
"sections": {
|
||||
"tag_options": "mdi:tag",
|
||||
"developer_options": "mdi:test-tube"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,8 @@ from __future__ import annotations
|
||||
|
||||
from dataclasses import asdict
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
from uuid import UUID
|
||||
|
||||
from aiohttp import ClientError
|
||||
from habiticalib import (
|
||||
@ -13,6 +14,7 @@ from habiticalib import (
|
||||
NotAuthorizedError,
|
||||
NotFoundError,
|
||||
Skill,
|
||||
Task,
|
||||
TaskData,
|
||||
TaskPriority,
|
||||
TaskType,
|
||||
@ -20,6 +22,7 @@ from habiticalib import (
|
||||
)
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.todo import ATTR_DESCRIPTION, ATTR_RENAME
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import ATTR_NAME, CONF_NAME
|
||||
from homeassistant.core import (
|
||||
@ -34,14 +37,17 @@ from homeassistant.helpers.issue_registry import IssueSeverity, async_create_iss
|
||||
from homeassistant.helpers.selector import ConfigEntrySelector
|
||||
|
||||
from .const import (
|
||||
ATTR_ALIAS,
|
||||
ATTR_ARGS,
|
||||
ATTR_CONFIG_ENTRY,
|
||||
ATTR_COST,
|
||||
ATTR_DATA,
|
||||
ATTR_DIRECTION,
|
||||
ATTR_ITEM,
|
||||
ATTR_KEYWORD,
|
||||
ATTR_PATH,
|
||||
ATTR_PRIORITY,
|
||||
ATTR_REMOVE_TAG,
|
||||
ATTR_SKILL,
|
||||
ATTR_TAG,
|
||||
ATTR_TARGET,
|
||||
@ -61,6 +67,7 @@ from .const import (
|
||||
SERVICE_SCORE_REWARD,
|
||||
SERVICE_START_QUEST,
|
||||
SERVICE_TRANSFORMATION,
|
||||
SERVICE_UPDATE_REWARD,
|
||||
)
|
||||
from .coordinator import HabiticaConfigEntry
|
||||
|
||||
@ -104,6 +111,21 @@ SERVICE_TRANSFORMATION_SCHEMA = vol.Schema(
|
||||
}
|
||||
)
|
||||
|
||||
SERVICE_UPDATE_TASK_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(ATTR_CONFIG_ENTRY): ConfigEntrySelector(),
|
||||
vol.Required(ATTR_TASK): cv.string,
|
||||
vol.Optional(ATTR_RENAME): cv.string,
|
||||
vol.Optional(ATTR_DESCRIPTION): cv.string,
|
||||
vol.Optional(ATTR_TAG): vol.All(cv.ensure_list, [str]),
|
||||
vol.Optional(ATTR_REMOVE_TAG): vol.All(cv.ensure_list, [str]),
|
||||
vol.Optional(ATTR_ALIAS): vol.All(
|
||||
cv.string, cv.matches_regex("^[a-zA-Z0-9-_]*$")
|
||||
),
|
||||
vol.Optional(ATTR_COST): vol.Coerce(float),
|
||||
}
|
||||
)
|
||||
|
||||
SERVICE_GET_TASKS_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(ATTR_CONFIG_ENTRY): ConfigEntrySelector({"integration": DOMAIN}),
|
||||
@ -516,6 +538,130 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
|
||||
|
||||
return result
|
||||
|
||||
async def update_task(call: ServiceCall) -> ServiceResponse:
|
||||
"""Update task action."""
|
||||
entry = get_config_entry(hass, call.data[ATTR_CONFIG_ENTRY])
|
||||
coordinator = entry.runtime_data
|
||||
await coordinator.async_refresh()
|
||||
|
||||
try:
|
||||
current_task = next(
|
||||
task
|
||||
for task in coordinator.data.tasks
|
||||
if call.data[ATTR_TASK] in (str(task.id), task.alias, task.text)
|
||||
and task.Type is TaskType.REWARD
|
||||
)
|
||||
except StopIteration as e:
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="task_not_found",
|
||||
translation_placeholders={"task": f"'{call.data[ATTR_TASK]}'"},
|
||||
) from e
|
||||
|
||||
task_id = current_task.id
|
||||
if TYPE_CHECKING:
|
||||
assert task_id
|
||||
data = Task()
|
||||
|
||||
if rename := call.data.get(ATTR_RENAME):
|
||||
data["text"] = rename
|
||||
|
||||
if (description := call.data.get(ATTR_DESCRIPTION)) is not None:
|
||||
data["notes"] = description
|
||||
|
||||
tags = cast(list[str], call.data.get(ATTR_TAG))
|
||||
remove_tags = cast(list[str], call.data.get(ATTR_REMOVE_TAG))
|
||||
|
||||
if tags or remove_tags:
|
||||
update_tags = set(current_task.tags)
|
||||
user_tags = {
|
||||
tag.name.lower(): tag.id
|
||||
for tag in coordinator.data.user.tags
|
||||
if tag.id and tag.name
|
||||
}
|
||||
|
||||
if tags:
|
||||
# Creates new tag if it doesn't exist
|
||||
async def create_tag(tag_name: str) -> UUID:
|
||||
tag_id = (await coordinator.habitica.create_tag(tag_name)).data.id
|
||||
if TYPE_CHECKING:
|
||||
assert tag_id
|
||||
return tag_id
|
||||
|
||||
try:
|
||||
update_tags.update(
|
||||
{
|
||||
user_tags.get(tag_name.lower())
|
||||
or (await create_tag(tag_name))
|
||||
for tag_name in tags
|
||||
}
|
||||
)
|
||||
except TooManyRequestsError as e:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="setup_rate_limit_exception",
|
||||
translation_placeholders={"retry_after": str(e.retry_after)},
|
||||
) from e
|
||||
except HabiticaException as e:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="service_call_exception",
|
||||
translation_placeholders={"reason": str(e.error.message)},
|
||||
) from e
|
||||
except ClientError as e:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="service_call_exception",
|
||||
translation_placeholders={"reason": str(e)},
|
||||
) from e
|
||||
|
||||
if remove_tags:
|
||||
update_tags.difference_update(
|
||||
{
|
||||
user_tags[tag_name.lower()]
|
||||
for tag_name in remove_tags
|
||||
if tag_name.lower() in user_tags
|
||||
}
|
||||
)
|
||||
|
||||
data["tags"] = list(update_tags)
|
||||
|
||||
if (alias := call.data.get(ATTR_ALIAS)) is not None:
|
||||
data["alias"] = alias
|
||||
|
||||
if (cost := call.data.get(ATTR_COST)) is not None:
|
||||
data["value"] = cost
|
||||
|
||||
try:
|
||||
response = await coordinator.habitica.update_task(task_id, data)
|
||||
except TooManyRequestsError as e:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="setup_rate_limit_exception",
|
||||
translation_placeholders={"retry_after": str(e.retry_after)},
|
||||
) from e
|
||||
except HabiticaException as e:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="service_call_exception",
|
||||
translation_placeholders={"reason": str(e.error.message)},
|
||||
) from e
|
||||
except ClientError as e:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="service_call_exception",
|
||||
translation_placeholders={"reason": str(e)},
|
||||
) from e
|
||||
else:
|
||||
return response.data.to_dict(omit_none=True)
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN,
|
||||
SERVICE_UPDATE_REWARD,
|
||||
update_task,
|
||||
schema=SERVICE_UPDATE_TASK_SCHEMA,
|
||||
supports_response=SupportsResponse.ONLY,
|
||||
)
|
||||
hass.services.async_register(
|
||||
DOMAIN,
|
||||
SERVICE_API_CALL,
|
||||
|
@ -140,3 +140,43 @@ get_tasks:
|
||||
required: false
|
||||
selector:
|
||||
text:
|
||||
update_reward:
|
||||
fields:
|
||||
config_entry: *config_entry
|
||||
task: *task
|
||||
rename:
|
||||
selector:
|
||||
text:
|
||||
description:
|
||||
required: false
|
||||
selector:
|
||||
text:
|
||||
multiline: true
|
||||
cost:
|
||||
required: false
|
||||
selector:
|
||||
number:
|
||||
min: 0
|
||||
step: 0.01
|
||||
unit_of_measurement: "🪙"
|
||||
mode: box
|
||||
tag_options:
|
||||
collapsed: true
|
||||
fields:
|
||||
tag:
|
||||
required: false
|
||||
selector:
|
||||
text:
|
||||
multiple: true
|
||||
remove_tag:
|
||||
required: false
|
||||
selector:
|
||||
text:
|
||||
multiple: true
|
||||
developer_options:
|
||||
collapsed: true
|
||||
fields:
|
||||
alias:
|
||||
required: false
|
||||
selector:
|
||||
text:
|
||||
|
@ -7,7 +7,23 @@
|
||||
"unit_tasks": "tasks",
|
||||
"unit_health_points": "HP",
|
||||
"unit_mana_points": "MP",
|
||||
"unit_experience_points": "XP"
|
||||
"unit_experience_points": "XP",
|
||||
"config_entry_description": "Select the Habitica account to update a task.",
|
||||
"task_description": "The name (or task ID) of the task you want to update.",
|
||||
"rename_name": "Rename",
|
||||
"rename_description": "The new title for the Habitica task.",
|
||||
"description_name": "Update description",
|
||||
"description_description": "The new description for the Habitica task.",
|
||||
"tag_name": "Add tags",
|
||||
"tag_description": "Add tags to the Habitica task. If a tag does not already exist, a new one will be created.",
|
||||
"remove_tag_name": "Remove tags",
|
||||
"remove_tag_description": "Remove tags from the Habitica task.",
|
||||
"alias_name": "Task alias",
|
||||
"alias_description": "A task alias can be used instead of the name or task ID. Only dashes, underscores, and alphanumeric characters are supported. The task alias must be unique among all your tasks.",
|
||||
"developer_options_name": "Advanced settings",
|
||||
"developer_options_description": "Additional features available in developer mode.",
|
||||
"tag_options_name": "Tags",
|
||||
"tag_options_description": "Add or remove tags from a task."
|
||||
},
|
||||
"config": {
|
||||
"abort": {
|
||||
@ -457,6 +473,12 @@
|
||||
},
|
||||
"authentication_failed": {
|
||||
"message": "Authentication failed. It looks like your API token has been reset. Please re-authenticate using your new token"
|
||||
},
|
||||
"frequency_not_weekly": {
|
||||
"message": "Unable to update task, weekly repeat settings apply only to weekly recurring dailies."
|
||||
},
|
||||
"frequency_not_monthly": {
|
||||
"message": "Unable to update task, monthly repeat settings apply only to monthly recurring dailies."
|
||||
}
|
||||
},
|
||||
"issues": {
|
||||
@ -651,6 +673,54 @@
|
||||
"description": "Use the optional filters to narrow the returned tasks."
|
||||
}
|
||||
}
|
||||
},
|
||||
"update_reward": {
|
||||
"name": "Update a reward",
|
||||
"description": "Updates a specific reward for the selected Habitica character",
|
||||
"fields": {
|
||||
"config_entry": {
|
||||
"name": "[%key:component::habitica::common::config_entry_name%]",
|
||||
"description": "Select the Habitica account to update a reward."
|
||||
},
|
||||
"task": {
|
||||
"name": "[%key:component::habitica::common::task_name%]",
|
||||
"description": "[%key:component::habitica::common::task_description%]"
|
||||
},
|
||||
"rename": {
|
||||
"name": "[%key:component::habitica::common::rename_name%]",
|
||||
"description": "[%key:component::habitica::common::rename_description%]"
|
||||
},
|
||||
"description": {
|
||||
"name": "[%key:component::habitica::common::description_name%]",
|
||||
"description": "[%key:component::habitica::common::description_description%]"
|
||||
},
|
||||
"tag": {
|
||||
"name": "[%key:component::habitica::common::tag_name%]",
|
||||
"description": "[%key:component::habitica::common::tag_description%]"
|
||||
},
|
||||
"remove_tag": {
|
||||
"name": "[%key:component::habitica::common::remove_tag_name%]",
|
||||
"description": "[%key:component::habitica::common::remove_tag_description%]"
|
||||
},
|
||||
"alias": {
|
||||
"name": "[%key:component::habitica::common::alias_name%]",
|
||||
"description": "[%key:component::habitica::common::alias_description%]"
|
||||
},
|
||||
"cost": {
|
||||
"name": "Cost",
|
||||
"description": "Update the cost of a reward."
|
||||
}
|
||||
},
|
||||
"sections": {
|
||||
"tag_options": {
|
||||
"name": "[%key:component::habitica::common::tag_options_name%]",
|
||||
"description": "[%key:component::habitica::common::tag_options_description%]"
|
||||
},
|
||||
"developer_options": {
|
||||
"name": "[%key:component::habitica::common::developer_options_name%]",
|
||||
"description": "[%key:component::habitica::common::developer_options_description%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"selector": {
|
||||
|
@ -14,6 +14,7 @@ from habiticalib import (
|
||||
HabiticaResponse,
|
||||
HabiticaScoreResponse,
|
||||
HabiticaSleepResponse,
|
||||
HabiticaTagResponse,
|
||||
HabiticaTaskOrderResponse,
|
||||
HabiticaTaskResponse,
|
||||
HabiticaTasksResponse,
|
||||
@ -144,6 +145,12 @@ async def mock_habiticalib() -> Generator[AsyncMock]:
|
||||
load_fixture("anonymized.json", DOMAIN)
|
||||
)
|
||||
)
|
||||
client.update_task.return_value = HabiticaTaskResponse.from_json(
|
||||
load_fixture("task.json", DOMAIN)
|
||||
)
|
||||
client.create_tag.return_value = HabiticaTagResponse.from_json(
|
||||
load_fixture("create_tag.json", DOMAIN)
|
||||
)
|
||||
client.habitipy.return_value = {
|
||||
"tasks": {
|
||||
"user": {
|
||||
|
8
tests/components/habitica/fixtures/create_tag.json
Normal file
8
tests/components/habitica/fixtures/create_tag.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"name": "Home Assistant",
|
||||
"id": "8bc0afbf-ab8e-49a4-982d-67a40557ed1a"
|
||||
},
|
||||
"notifications": []
|
||||
}
|
27
tests/components/habitica/fixtures/reward.json
Normal file
27
tests/components/habitica/fixtures/reward.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"_id": "5e2ea1df-f6e6-4ba3-bccb-97c5ec63e99b",
|
||||
"type": "reward",
|
||||
"text": "Belohne Dich selbst",
|
||||
"notes": "Schaue fern, spiele ein Spiel, gönne Dir einen Leckerbissen, es liegt ganz bei Dir!",
|
||||
"tags": [],
|
||||
"value": 10,
|
||||
"priority": 1,
|
||||
"attribute": "str",
|
||||
"challenge": {},
|
||||
"group": {
|
||||
"completedBy": {},
|
||||
"assignedUsers": []
|
||||
},
|
||||
"byHabitica": false,
|
||||
"reminders": [],
|
||||
"createdAt": "2024-07-07T17:51:53.266Z",
|
||||
"updatedAt": "2024-07-07T17:51:53.266Z",
|
||||
"userId": "5f359083-ef78-4af0-985a-0b2c6d05797c",
|
||||
"id": "5e2ea1df-f6e6-4ba3-bccb-97c5ec63e99b"
|
||||
},
|
||||
"notifications": [],
|
||||
"userV": 589,
|
||||
"appVersion": "5.28.6"
|
||||
}
|
@ -533,7 +533,10 @@
|
||||
"type": "reward",
|
||||
"text": "Belohne Dich selbst",
|
||||
"notes": "Schaue fern, spiele ein Spiel, gönne Dir einen Leckerbissen, es liegt ganz bei Dir!",
|
||||
"tags": [],
|
||||
"tags": [
|
||||
"3450351f-1323-4c7e-9fd2-0cdff25b3ce0",
|
||||
"b2780f82-b3b5-49a3-a677-48f2c8c7e3bb"
|
||||
],
|
||||
"value": 10,
|
||||
"priority": 1,
|
||||
"attribute": "str",
|
||||
|
@ -1271,6 +1271,10 @@
|
||||
'th': False,
|
||||
'w': True,
|
||||
}),
|
||||
'tags': list([
|
||||
'3450351f-1323-4c7e-9fd2-0cdff25b3ce0',
|
||||
'b2780f82-b3b5-49a3-a677-48f2c8c7e3bb',
|
||||
]),
|
||||
'text': 'Belohne Dich selbst',
|
||||
'type': 'reward',
|
||||
'value': 10.0,
|
||||
|
@ -1081,6 +1081,8 @@
|
||||
'startDate': None,
|
||||
'streak': None,
|
||||
'tags': list([
|
||||
'3450351f-1323-4c7e-9fd2-0cdff25b3ce0',
|
||||
'b2780f82-b3b5-49a3-a677-48f2c8c7e3bb',
|
||||
]),
|
||||
'text': 'Belohne Dich selbst',
|
||||
'type': 'reward',
|
||||
@ -3321,6 +3323,8 @@
|
||||
'startDate': None,
|
||||
'streak': None,
|
||||
'tags': list([
|
||||
'3450351f-1323-4c7e-9fd2-0cdff25b3ce0',
|
||||
'b2780f82-b3b5-49a3-a677-48f2c8c7e3bb',
|
||||
]),
|
||||
'text': 'Belohne Dich selbst',
|
||||
'type': 'reward',
|
||||
@ -5580,6 +5584,8 @@
|
||||
'startDate': None,
|
||||
'streak': None,
|
||||
'tags': list([
|
||||
'3450351f-1323-4c7e-9fd2-0cdff25b3ce0',
|
||||
'b2780f82-b3b5-49a3-a677-48f2c8c7e3bb',
|
||||
]),
|
||||
'text': 'Belohne Dich selbst',
|
||||
'type': 'reward',
|
||||
@ -5954,6 +5960,8 @@
|
||||
'startDate': None,
|
||||
'streak': None,
|
||||
'tags': list([
|
||||
'3450351f-1323-4c7e-9fd2-0cdff25b3ce0',
|
||||
'b2780f82-b3b5-49a3-a677-48f2c8c7e3bb',
|
||||
]),
|
||||
'text': 'Belohne Dich selbst',
|
||||
'type': 'reward',
|
||||
|
@ -6,16 +6,19 @@ from unittest.mock import AsyncMock, patch
|
||||
from uuid import UUID
|
||||
|
||||
from aiohttp import ClientError
|
||||
from habiticalib import Direction, Skill
|
||||
from habiticalib import Direction, HabiticaTaskResponse, Skill, Task
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.habitica.const import (
|
||||
ATTR_ALIAS,
|
||||
ATTR_CONFIG_ENTRY,
|
||||
ATTR_COST,
|
||||
ATTR_DIRECTION,
|
||||
ATTR_ITEM,
|
||||
ATTR_KEYWORD,
|
||||
ATTR_PRIORITY,
|
||||
ATTR_REMOVE_TAG,
|
||||
ATTR_SKILL,
|
||||
ATTR_TAG,
|
||||
ATTR_TARGET,
|
||||
@ -33,7 +36,9 @@ from homeassistant.components.habitica.const import (
|
||||
SERVICE_SCORE_REWARD,
|
||||
SERVICE_START_QUEST,
|
||||
SERVICE_TRANSFORMATION,
|
||||
SERVICE_UPDATE_REWARD,
|
||||
)
|
||||
from homeassistant.components.todo import ATTR_DESCRIPTION, ATTR_RENAME
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
||||
@ -45,7 +50,7 @@ from .conftest import (
|
||||
ERROR_TOO_MANY_REQUESTS,
|
||||
)
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.common import MockConfigEntry, load_fixture
|
||||
|
||||
REQUEST_EXCEPTION_MSG = "Unable to connect to Habitica: reason"
|
||||
RATE_LIMIT_EXCEPTION_MSG = "Rate limit exceeded, try again in 5 seconds"
|
||||
@ -889,3 +894,261 @@ async def test_get_tasks(
|
||||
)
|
||||
|
||||
assert response == snapshot
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("exception", "expected_exception", "exception_msg"),
|
||||
[
|
||||
(
|
||||
ERROR_TOO_MANY_REQUESTS,
|
||||
HomeAssistantError,
|
||||
RATE_LIMIT_EXCEPTION_MSG,
|
||||
),
|
||||
(
|
||||
ERROR_BAD_REQUEST,
|
||||
HomeAssistantError,
|
||||
REQUEST_EXCEPTION_MSG,
|
||||
),
|
||||
(
|
||||
ClientError,
|
||||
HomeAssistantError,
|
||||
"Unable to connect to Habitica: ",
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.usefixtures("habitica")
|
||||
async def test_update_task_exceptions(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
habitica: AsyncMock,
|
||||
exception: Exception,
|
||||
expected_exception: Exception,
|
||||
exception_msg: str,
|
||||
) -> None:
|
||||
"""Test Habitica task action exceptions."""
|
||||
task_id = "5e2ea1df-f6e6-4ba3-bccb-97c5ec63e99b"
|
||||
|
||||
habitica.update_task.side_effect = exception
|
||||
with pytest.raises(expected_exception, match=exception_msg):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_UPDATE_REWARD,
|
||||
service_data={
|
||||
ATTR_CONFIG_ENTRY: config_entry.entry_id,
|
||||
ATTR_TASK: task_id,
|
||||
},
|
||||
return_response=True,
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("habitica")
|
||||
async def test_task_not_found(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
habitica: AsyncMock,
|
||||
) -> None:
|
||||
"""Test Habitica task not found exceptions."""
|
||||
task_id = "7f902bbc-eb3d-4a8f-82cf-4e2025d69af1"
|
||||
|
||||
with pytest.raises(
|
||||
ServiceValidationError,
|
||||
match="Unable to complete action, could not find the task '7f902bbc-eb3d-4a8f-82cf-4e2025d69af1'",
|
||||
):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_UPDATE_REWARD,
|
||||
service_data={
|
||||
ATTR_CONFIG_ENTRY: config_entry.entry_id,
|
||||
ATTR_TASK: task_id,
|
||||
},
|
||||
return_response=True,
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("service_data", "call_args"),
|
||||
[
|
||||
(
|
||||
{
|
||||
ATTR_COST: 100,
|
||||
},
|
||||
Task(value=100),
|
||||
),
|
||||
(
|
||||
{
|
||||
ATTR_RENAME: "RENAME",
|
||||
},
|
||||
Task(text="RENAME"),
|
||||
),
|
||||
(
|
||||
{
|
||||
ATTR_DESCRIPTION: "DESCRIPTION",
|
||||
},
|
||||
Task(notes="DESCRIPTION"),
|
||||
),
|
||||
(
|
||||
{
|
||||
ATTR_ALIAS: "ALIAS",
|
||||
},
|
||||
Task(alias="ALIAS"),
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_update_reward(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
habitica: AsyncMock,
|
||||
service_data: dict[str, Any],
|
||||
call_args: Task,
|
||||
) -> None:
|
||||
"""Test Habitica update_reward action."""
|
||||
task_id = "5e2ea1df-f6e6-4ba3-bccb-97c5ec63e99b"
|
||||
|
||||
habitica.update_task.return_value = HabiticaTaskResponse.from_json(
|
||||
load_fixture("task.json", DOMAIN)
|
||||
)
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_UPDATE_REWARD,
|
||||
service_data={
|
||||
ATTR_CONFIG_ENTRY: config_entry.entry_id,
|
||||
ATTR_TASK: task_id,
|
||||
**service_data,
|
||||
},
|
||||
return_response=True,
|
||||
blocking=True,
|
||||
)
|
||||
habitica.update_task.assert_awaited_with(UUID(task_id), call_args)
|
||||
|
||||
|
||||
async def test_tags(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
habitica: AsyncMock,
|
||||
) -> None:
|
||||
"""Test adding tags to a task."""
|
||||
task_id = "5e2ea1df-f6e6-4ba3-bccb-97c5ec63e99b"
|
||||
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_UPDATE_REWARD,
|
||||
service_data={
|
||||
ATTR_CONFIG_ENTRY: config_entry.entry_id,
|
||||
ATTR_TASK: task_id,
|
||||
ATTR_TAG: ["Schule"],
|
||||
},
|
||||
return_response=True,
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
call_args = habitica.update_task.call_args[0]
|
||||
assert call_args[0] == UUID(task_id)
|
||||
assert set(call_args[1]["tags"]) == {
|
||||
UUID("2ac458af-0833-4f3f-bf04-98a0c33ef60b"),
|
||||
UUID("3450351f-1323-4c7e-9fd2-0cdff25b3ce0"),
|
||||
UUID("b2780f82-b3b5-49a3-a677-48f2c8c7e3bb"),
|
||||
}
|
||||
|
||||
|
||||
async def test_create_new_tag(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
habitica: AsyncMock,
|
||||
) -> None:
|
||||
"""Test adding a non-existent tag and create it as new."""
|
||||
task_id = "5e2ea1df-f6e6-4ba3-bccb-97c5ec63e99b"
|
||||
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_UPDATE_REWARD,
|
||||
service_data={
|
||||
ATTR_CONFIG_ENTRY: config_entry.entry_id,
|
||||
ATTR_TASK: task_id,
|
||||
ATTR_TAG: ["Home Assistant"],
|
||||
},
|
||||
return_response=True,
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
habitica.create_tag.assert_awaited_with("Home Assistant")
|
||||
|
||||
call_args = habitica.update_task.call_args[0]
|
||||
assert call_args[0] == UUID(task_id)
|
||||
assert set(call_args[1]["tags"]) == {
|
||||
UUID("8bc0afbf-ab8e-49a4-982d-67a40557ed1a"),
|
||||
UUID("3450351f-1323-4c7e-9fd2-0cdff25b3ce0"),
|
||||
UUID("b2780f82-b3b5-49a3-a677-48f2c8c7e3bb"),
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("exception", "expected_exception", "exception_msg"),
|
||||
[
|
||||
(
|
||||
ERROR_TOO_MANY_REQUESTS,
|
||||
HomeAssistantError,
|
||||
RATE_LIMIT_EXCEPTION_MSG,
|
||||
),
|
||||
(
|
||||
ERROR_BAD_REQUEST,
|
||||
HomeAssistantError,
|
||||
REQUEST_EXCEPTION_MSG,
|
||||
),
|
||||
(
|
||||
ClientError,
|
||||
HomeAssistantError,
|
||||
"Unable to connect to Habitica: ",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_create_new_tag_exception(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
habitica: AsyncMock,
|
||||
exception: Exception,
|
||||
expected_exception: Exception,
|
||||
exception_msg: str,
|
||||
) -> None:
|
||||
"""Test create new tag exception."""
|
||||
task_id = "5e2ea1df-f6e6-4ba3-bccb-97c5ec63e99b"
|
||||
|
||||
habitica.create_tag.side_effect = exception
|
||||
with pytest.raises(expected_exception, match=exception_msg):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_UPDATE_REWARD,
|
||||
service_data={
|
||||
ATTR_CONFIG_ENTRY: config_entry.entry_id,
|
||||
ATTR_TASK: task_id,
|
||||
ATTR_TAG: ["Home Assistant"],
|
||||
},
|
||||
return_response=True,
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
||||
async def test_remove_tags(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
habitica: AsyncMock,
|
||||
) -> None:
|
||||
"""Test removing tags from a task."""
|
||||
task_id = "5e2ea1df-f6e6-4ba3-bccb-97c5ec63e99b"
|
||||
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_UPDATE_REWARD,
|
||||
service_data={
|
||||
ATTR_CONFIG_ENTRY: config_entry.entry_id,
|
||||
ATTR_TASK: task_id,
|
||||
ATTR_REMOVE_TAG: ["Kreativität"],
|
||||
},
|
||||
return_response=True,
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
call_args = habitica.update_task.call_args[0]
|
||||
assert call_args[0] == UUID(task_id)
|
||||
assert set(call_args[1]["tags"]) == {UUID("b2780f82-b3b5-49a3-a677-48f2c8c7e3bb")}
|
||||
|
Loading…
x
Reference in New Issue
Block a user