From db2fda09b9cfdd30d8a96b636b5a3d375d465f42 Mon Sep 17 00:00:00 2001 From: Pavel Pletenev Date: Sun, 27 Jun 2021 01:36:45 +0300 Subject: [PATCH] Fix habitica regression (#52097) --- homeassistant/components/habitica/__init__.py | 10 +- homeassistant/components/habitica/const.py | 4 + tests/components/habitica/test_init.py | 111 ++++++++++++++++-- 3 files changed, 116 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/habitica/__init__.py b/homeassistant/components/habitica/__init__.py index e8846d1f85a..efb82a9f1aa 100644 --- a/homeassistant/components/habitica/__init__.py +++ b/homeassistant/components/habitica/__init__.py @@ -19,6 +19,7 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession from .const import ( ATTR_ARGS, + ATTR_DATA, ATTR_PATH, CONF_API_USER, DEFAULT_URL, @@ -111,7 +112,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def handle_api_call(call): name = call.data[ATTR_NAME] path = call.data[ATTR_PATH] - api = hass.data[DOMAIN].get(name) + entries = hass.config_entries.async_entries(DOMAIN) + api = None + for entry in entries: + if entry.data[CONF_NAME] == name: + api = hass.data[DOMAIN].get(entry.entry_id) + break if api is None: _LOGGER.error("API_CALL: User '%s' not configured", name) return @@ -126,7 +132,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: kwargs = call.data.get(ATTR_ARGS, {}) data = await api(**kwargs) hass.bus.async_fire( - EVENT_API_CALL_SUCCESS, {"name": name, "path": path, "data": data} + EVENT_API_CALL_SUCCESS, {ATTR_NAME: name, ATTR_PATH: path, ATTR_DATA: data} ) data = hass.data.setdefault(DOMAIN, {}) diff --git a/homeassistant/components/habitica/const.py b/homeassistant/components/habitica/const.py index 02a46334c7a..1379f0a6447 100644 --- a/homeassistant/components/habitica/const.py +++ b/homeassistant/components/habitica/const.py @@ -7,7 +7,11 @@ CONF_API_USER = "api_user" DEFAULT_URL = "https://habitica.com" DOMAIN = "habitica" +# service constants SERVICE_API_CALL = "api_call" ATTR_PATH = CONF_PATH ATTR_ARGS = "args" + +# event constants EVENT_API_CALL_SUCCESS = f"{DOMAIN}_{SERVICE_API_CALL}_success" +ATTR_DATA = "data" diff --git a/tests/components/habitica/test_init.py b/tests/components/habitica/test_init.py index 5f7e4b7fbf5..97d4fb092fc 100644 --- a/tests/components/habitica/test_init.py +++ b/tests/components/habitica/test_init.py @@ -1,15 +1,33 @@ -"""Test the habitica init module.""" +"""Test the habitica module.""" +import pytest + from homeassistant.components.habitica.const import ( + ATTR_ARGS, + ATTR_DATA, + ATTR_PATH, DEFAULT_URL, DOMAIN, + EVENT_API_CALL_SUCCESS, SERVICE_API_CALL, ) +from homeassistant.components.habitica.sensor import TASKS_TYPES +from homeassistant.const import ATTR_NAME -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, async_capture_events + +TEST_API_CALL_ARGS = {"text": "Use API from Home Assistant", "type": "todo"} +TEST_USER_NAME = "test_user" -async def test_entry_setup_unload(hass, aioclient_mock): - """Test integration setup and unload.""" +@pytest.fixture +def capture_api_call_success(hass): + """Capture api_call events.""" + return async_capture_events(hass, EVENT_API_CALL_SUCCESS) + + +@pytest.fixture +def habitica_entry(hass): + """Test entry for the following tests.""" entry = MockConfigEntry( domain=DOMAIN, unique_id="test-api-user", @@ -20,17 +38,96 @@ async def test_entry_setup_unload(hass, aioclient_mock): }, ) entry.add_to_hass(hass) + return entry + +@pytest.fixture +def common_requests(aioclient_mock): + """Register requests for the tests.""" aioclient_mock.get( "https://habitica.com/api/v3/user", - json={"data": {"api_user": "test-api-user", "profile": {"name": "test_user"}}}, + json={ + "data": { + "api_user": "test-api-user", + "profile": {"name": TEST_USER_NAME}, + "stats": { + "class": "test-class", + "con": 1, + "exp": 2, + "gp": 3, + "hp": 4, + "int": 5, + "lvl": 6, + "maxHealth": 7, + "maxMP": 8, + "mp": 9, + "per": 10, + "points": 11, + "str": 12, + "toNextLevel": 13, + }, + } + }, + ) + for n_tasks, task_type in enumerate(TASKS_TYPES.keys(), start=1): + aioclient_mock.get( + f"https://habitica.com/api/v3/tasks/user?type={task_type}", + json={ + "data": [ + {"text": f"this is a mock {task_type} #{task}", "id": f"{task}"} + for task in range(n_tasks) + ] + }, + ) + + aioclient_mock.post( + "https://habitica.com/api/v3/tasks/user", + status=201, + json={"data": TEST_API_CALL_ARGS}, ) - assert await hass.config_entries.async_setup(entry.entry_id) + return aioclient_mock + + +async def test_entry_setup_unload(hass, habitica_entry, common_requests): + """Test integration setup and unload.""" + assert await hass.config_entries.async_setup(habitica_entry.entry_id) await hass.async_block_till_done() assert hass.services.has_service(DOMAIN, SERVICE_API_CALL) - assert await hass.config_entries.async_unload(entry.entry_id) + assert await hass.config_entries.async_unload(habitica_entry.entry_id) + + assert not hass.services.has_service(DOMAIN, SERVICE_API_CALL) + + +async def test_service_call( + hass, habitica_entry, common_requests, capture_api_call_success +): + """Test integration setup, service call and unload.""" + + assert await hass.config_entries.async_setup(habitica_entry.entry_id) + await hass.async_block_till_done() + + assert hass.services.has_service(DOMAIN, SERVICE_API_CALL) + + assert len(capture_api_call_success) == 0 + + TEST_SERVICE_DATA = { + ATTR_NAME: "test_user", + ATTR_PATH: ["tasks", "user", "post"], + ATTR_ARGS: TEST_API_CALL_ARGS, + } + assert await hass.services.async_call( + DOMAIN, SERVICE_API_CALL, TEST_SERVICE_DATA, blocking=True + ) + + assert len(capture_api_call_success) == 1 + captured_data = capture_api_call_success[0].data + captured_data[ATTR_ARGS] = captured_data[ATTR_DATA] + del captured_data[ATTR_DATA] + assert captured_data == TEST_SERVICE_DATA + + assert await hass.config_entries.async_unload(habitica_entry.entry_id) assert not hass.services.has_service(DOMAIN, SERVICE_API_CALL)