diff --git a/custom_components/candy/strings.json b/custom_components/candy/strings.json index 0b73efc..ec530e4 100644 --- a/custom_components/candy/strings.json +++ b/custom_components/candy/strings.json @@ -1,5 +1,4 @@ { - "title": "Candy", "config": { "step": { "user": { diff --git a/hacs.json b/hacs.json index 8c1cd6d..f7d85f8 100644 --- a/hacs.json +++ b/hacs.json @@ -1,6 +1,4 @@ { "name": "Candy Simply-Fi", - "render_readme": true, - "domains": ["sensor"], - "iot_class": ["Local Polling"] + "render_readme": true } \ No newline at end of file diff --git a/requirements_test.txt b/requirements_test.txt index 2102a49..4a8d372 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,6 +1,5 @@ -pytest==6.2.4 -pytest-homeassistant-custom-component==0.4.3 -aresponses==2.1.5 +pytest==7.1.1 +pytest-homeassistant-custom-component==0.9.17 pytest-aiohttp==0.3.0 # Component dependencies diff --git a/tests/common.py b/tests/common.py index e288ca1..e03322c 100644 --- a/tests/common.py +++ b/tests/common.py @@ -1,4 +1,3 @@ -import aresponses from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD from homeassistant.core import HomeAssistant from pytest_homeassistant_custom_component.common import load_fixture, MockConfigEntry @@ -16,13 +15,6 @@ """ -def status_response(filename): - return aresponses.Response( - text=load_fixture(filename), - content_type="text/html" # Shame on Candy, but this is how the real API responds - ) - - async def init_integration(hass: HomeAssistant, aioclient_mock, status_response: str): entry = MockConfigEntry( domain=DOMAIN, diff --git a/tests/fixtures/dishwasher/drying_optional_params.json b/tests/components/dishwasher/fixtures/drying_optional_params.json similarity index 100% rename from tests/fixtures/dishwasher/drying_optional_params.json rename to tests/components/dishwasher/fixtures/drying_optional_params.json diff --git a/tests/fixtures/dishwasher/idle.json b/tests/components/dishwasher/fixtures/idle.json similarity index 100% rename from tests/fixtures/dishwasher/idle.json rename to tests/components/dishwasher/fixtures/idle.json diff --git a/tests/fixtures/dishwasher/wash.json b/tests/components/dishwasher/fixtures/wash.json similarity index 100% rename from tests/fixtures/dishwasher/wash.json rename to tests/components/dishwasher/fixtures/wash.json diff --git a/tests/fixtures/dishwasher/wash_no_opzprog.json b/tests/components/dishwasher/fixtures/wash_no_opzprog.json similarity index 100% rename from tests/fixtures/dishwasher/wash_no_opzprog.json rename to tests/components/dishwasher/fixtures/wash_no_opzprog.json diff --git a/tests/fixtures/oven/heating.json b/tests/components/oven/fixtures/heating.json similarity index 100% rename from tests/fixtures/oven/heating.json rename to tests/components/oven/fixtures/heating.json diff --git a/tests/fixtures/oven/idle.json b/tests/components/oven/fixtures/idle.json similarity index 100% rename from tests/fixtures/oven/idle.json rename to tests/components/oven/fixtures/idle.json diff --git a/tests/fixtures/oven/no_timeprogr.json b/tests/components/oven/fixtures/no_timeprogr.json similarity index 100% rename from tests/fixtures/oven/no_timeprogr.json rename to tests/components/oven/fixtures/no_timeprogr.json diff --git a/tests/fixtures/tumble_dryer/idle.json b/tests/components/tumble_dryer/fixtures/idle.json similarity index 100% rename from tests/fixtures/tumble_dryer/idle.json rename to tests/components/tumble_dryer/fixtures/idle.json diff --git a/tests/fixtures/tumble_dryer/running.json b/tests/components/tumble_dryer/fixtures/running.json similarity index 100% rename from tests/fixtures/tumble_dryer/running.json rename to tests/components/tumble_dryer/fixtures/running.json diff --git a/tests/fixtures/washing_machine/delayed_start_wait.json b/tests/components/washing_machine/fixtures/delayed_start_wait.json similarity index 100% rename from tests/fixtures/washing_machine/delayed_start_wait.json rename to tests/components/washing_machine/fixtures/delayed_start_wait.json diff --git a/tests/fixtures/washing_machine/idle.json b/tests/components/washing_machine/fixtures/idle.json similarity index 100% rename from tests/fixtures/washing_machine/idle.json rename to tests/components/washing_machine/fixtures/idle.json diff --git a/tests/fixtures/washing_machine/no_fillr.json b/tests/components/washing_machine/fixtures/no_fillr.json similarity index 100% rename from tests/fixtures/washing_machine/no_fillr.json rename to tests/components/washing_machine/fixtures/no_fillr.json diff --git a/tests/fixtures/washing_machine/no_pr.json b/tests/components/washing_machine/fixtures/no_pr.json similarity index 100% rename from tests/fixtures/washing_machine/no_pr.json rename to tests/components/washing_machine/fixtures/no_pr.json diff --git a/tests/fixtures/washing_machine/running_wash.json b/tests/components/washing_machine/fixtures/running_wash.json similarity index 100% rename from tests/fixtures/washing_machine/running_wash.json rename to tests/components/washing_machine/fixtures/running_wash.json diff --git a/tests/test_client.py b/tests/test_client.py index 0f1d9ce..d7f8bff 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,168 +1,142 @@ -import aiohttp import pytest -from aresponses import ResponsesMockServer from custom_components.candy.client import CandyClient, detect_encryption, Encryption from custom_components.candy.client.model import MachineState, WashProgramState, WashingMachineStatus, DishwasherStatus from .common import * +from homeassistant.helpers.aiohttp_client import async_get_clientsession + @pytest.mark.asyncio -async def test_idle(aresponses: ResponsesMockServer): +async def test_idle(hass, aioclient_mock): """Test parsing the status when turning on the machine and selecting WiFi mode""" - aresponses.add( - TEST_IP, - "/http-read.json", - response=status_response("washing_machine/idle.json") - ) - async with aiohttp.ClientSession() as session: - client = CandyClient(session, device_ip=TEST_IP, encryption_key=TEST_ENCRYPTION_KEY_EMPTY, use_encryption=False) - status = await client.status() + aioclient_mock.get( + f"http://{TEST_IP}/http-read.json", + text=load_fixture("washing_machine/idle.json") + ) - assert type(status) is WashingMachineStatus - assert status.machine_state is MachineState.IDLE - assert status.program_state is WashProgramState.STOPPED - assert status.spin_speed == 800 - assert status.temp == 40 + client = CandyClient( + async_get_clientsession(hass), device_ip=TEST_IP, encryption_key=TEST_ENCRYPTION_KEY_EMPTY, use_encryption=False + ) + status = await client.status() - aresponses.assert_plan_strictly_followed() + assert type(status) is WashingMachineStatus + assert status.machine_state is MachineState.IDLE + assert status.program_state is WashProgramState.STOPPED + assert status.spin_speed == 800 + assert status.temp == 40 @pytest.mark.asyncio -async def test_delayed_start_wait(aresponses: ResponsesMockServer): +async def test_delayed_start_wait(hass, aioclient_mock): """Test parsing the status when machine is waiting for a delayed start wash cycle""" - aresponses.add( - TEST_IP, - "/http-read.json", - response=status_response("washing_machine/delayed_start_wait.json") + aioclient_mock.get( + f"http://{TEST_IP}/http-read.json", + text=load_fixture("washing_machine/delayed_start_wait.json") ) - async with aiohttp.ClientSession() as session: - client = CandyClient(session, device_ip=TEST_IP, encryption_key=TEST_ENCRYPTION_KEY_EMPTY, use_encryption=False) - status = await client.status() + client = CandyClient( + async_get_clientsession(hass), device_ip=TEST_IP, encryption_key=TEST_ENCRYPTION_KEY_EMPTY, use_encryption=False + ) + status = await client.status() - assert type(status) is WashingMachineStatus - assert status.machine_state is MachineState.DELAYED_START_PROGRAMMED - assert status.program_state is WashProgramState.STOPPED - assert status.remaining_minutes == 50 + assert type(status) is WashingMachineStatus + assert status.machine_state is MachineState.DELAYED_START_PROGRAMMED + assert status.program_state is WashProgramState.STOPPED + assert status.remaining_minutes == 50 @pytest.mark.asyncio -async def test_no_fillr_property(aresponses: ResponsesMockServer): +async def test_no_fillr_property(hass, aioclient_mock): """Test parsing the status when response doesn't contain the FillR property""" - aresponses.add( - TEST_IP, - "/http-read.json", - response=status_response("washing_machine/no_fillr.json") + aioclient_mock.get( + f"http://{TEST_IP}/http-read.json", + text=load_fixture("washing_machine/no_fillr.json") ) - async with aiohttp.ClientSession() as session: - client = CandyClient(session, device_ip=TEST_IP, encryption_key=TEST_ENCRYPTION_KEY_EMPTY, use_encryption=False) - status = await client.status() + client = CandyClient( + async_get_clientsession(hass), device_ip=TEST_IP, encryption_key=TEST_ENCRYPTION_KEY_EMPTY, use_encryption=False + ) + status = await client.status() - assert type(status) is WashingMachineStatus - assert status.machine_state is MachineState.IDLE - assert status.fill_percent is None + assert type(status) is WashingMachineStatus + assert status.machine_state is MachineState.IDLE + assert status.fill_percent is None @pytest.mark.asyncio -async def test_detect_no_encryption(aresponses: ResponsesMockServer): - aresponses.add( - TEST_IP, - path_pattern="/http-read.json?encrypted=0", - response=status_response("washing_machine/idle.json"), - match_querystring=True +async def test_detect_no_encryption(hass, aioclient_mock): + aioclient_mock.get( + f"http://{TEST_IP}/http-read.json?encrypted=0", + text=load_fixture("washing_machine/idle.json") ) - async with aiohttp.ClientSession() as session: - encryption_type, key = await detect_encryption(session, TEST_IP) + encryption_type, key = await detect_encryption(async_get_clientsession(hass), TEST_IP) - assert encryption_type is Encryption.NO_ENCRYPTION - assert key is None - - aresponses.assert_plan_strictly_followed() + assert encryption_type is Encryption.NO_ENCRYPTION + assert key is None @pytest.mark.asyncio -async def test_detect_encryption_key(aresponses: ResponsesMockServer): - aresponses.add( - TEST_IP, - path_pattern="/http-read.json?encrypted=0", - response={"response": "BAD REQUEST"}, - match_querystring=True +async def test_detect_encryption_key(hass, aioclient_mock): + aioclient_mock.get( + f"http://{TEST_IP}/http-read.json?encrypted=0", + json={"response": "BAD REQUEST"} ) - aresponses.add( - TEST_IP, - path_pattern="/http-read.json?encrypted=1", - response=TEST_ENCRYPTED_HEX_RESPONSE, - match_querystring=True + aioclient_mock.get( + f"http://{TEST_IP}/http-read.json?encrypted=1", + text=TEST_ENCRYPTED_HEX_RESPONSE ) - async with aiohttp.ClientSession() as session: - encryption_type, key = await detect_encryption(session, TEST_IP) - - assert encryption_type is Encryption.ENCRYPTION - assert key == TEST_ENCRYPTION_KEY + encryption_type, key = await detect_encryption(async_get_clientsession(hass), TEST_IP) - aresponses.assert_plan_strictly_followed() + assert encryption_type is Encryption.ENCRYPTION + assert key == TEST_ENCRYPTION_KEY @pytest.mark.asyncio -async def test_detect_encryption_without_key(aresponses: ResponsesMockServer): - aresponses.add( - TEST_IP, - path_pattern="/http-read.json?encrypted=0", - response={"response": "BAD REQUEST"}, - match_querystring=True +async def test_detect_encryption_without_key(hass, aioclient_mock): + aioclient_mock.get( + f"http://{TEST_IP}/http-read.json?encrypted=0", + json={"response": "BAD REQUEST"} ) - aresponses.add( - TEST_IP, - path_pattern="/http-read.json?encrypted=1", - response=TEST_UNENCRYPTED_HEX_RESPONSE, - match_querystring=True + aioclient_mock.get( + f"http://{TEST_IP}/http-read.json?encrypted=1", + text=TEST_UNENCRYPTED_HEX_RESPONSE ) - async with aiohttp.ClientSession() as session: - encryption_type, key = await detect_encryption(session, TEST_IP) - - assert encryption_type is Encryption.ENCRYPTION_WITHOUT_KEY - assert key is None + encryption_type, key = await detect_encryption(async_get_clientsession(hass), TEST_IP) - aresponses.assert_plan_strictly_followed() + assert encryption_type is Encryption.ENCRYPTION_WITHOUT_KEY + assert key is None @pytest.mark.asyncio -async def test_status_encryption_with_key(aresponses: ResponsesMockServer): - aresponses.add( - TEST_IP, - "/http-read.json", - response="2F7C6B441B390C094C3C42093A023429764B1A403343714A6B3D503902342E073D535B6F086854653240386F2E0C23283714243F4B250A0D1A7313085D416B4C5E78686F6A3E191C570D662C1E0B657B76434361344071611A0454390C2026333D120E6F0368484A14443B44644114353503151E4D25084A026B016F416E4D485D53353F5C23163D562613774F53656D597B68441B0F1B071A73137D4F4F4A4B5D78431D4B251F1A592413774F337263787C6B4430683D104C3B50091F1A657B76414361344071611A0641280327282E263E11391B705A581A653C47646A6505311D00346A3E191A4C6B0B6F5D416B4C5E78686F6B2F153C5124546F5741767364534D403343714A7520423E3E022B35764B437C1B66756231401300041034133D1F12281B705A581A653C47646A650E24140F0956250A4A026B016F416E4D485D5333284A2F0C4A026B016F416E4D485D5322255C29133D486B0B6F5D416B4C5E78686F4B7B5A521A7B136160694E487603536F0368484A14443B4464413572764B437F1B6675623140133F59417D6365534D403343714A4A7C13774F53656D597B68441B384E4A026B016F416E4D485D53137A1B705A5B1A653C47646A65336C535B6F086854653240386F1F5A657B763F3401756854653240386F1F5272636E53506F3440711535434C" +async def test_status_encryption_with_key(hass, aioclient_mock): + aioclient_mock.get( + f"http://{TEST_IP}/http-read.json", + text="2F7C6B441B390C094C3C42093A023429764B1A403343714A6B3D503902342E073D535B6F086854653240386F2E0C23283714243F4B250A0D1A7313085D416B4C5E78686F6A3E191C570D662C1E0B657B76434361344071611A0454390C2026333D120E6F0368484A14443B44644114353503151E4D25084A026B016F416E4D485D53353F5C23163D562613774F53656D597B68441B0F1B071A73137D4F4F4A4B5D78431D4B251F1A592413774F337263787C6B4430683D104C3B50091F1A657B76414361344071611A0641280327282E263E11391B705A581A653C47646A6505311D00346A3E191A4C6B0B6F5D416B4C5E78686F6B2F153C5124546F5741767364534D403343714A7520423E3E022B35764B437C1B66756231401300041034133D1F12281B705A581A653C47646A650E24140F0956250A4A026B016F416E4D485D5333284A2F0C4A026B016F416E4D485D5322255C29133D486B0B6F5D416B4C5E78686F4B7B5A521A7B136160694E487603536F0368484A14443B4464413572764B437F1B6675623140133F59417D6365534D403343714A4A7C13774F53656D597B68441B384E4A026B016F416E4D485D53137A1B705A5B1A653C47646A65336C535B6F086854653240386F1F5A657B763F3401756854653240386F1F5272636E53506F3440711535434C" ) - async with aiohttp.ClientSession() as session: - client = CandyClient(session, device_ip=TEST_IP, encryption_key="TqaM9Jxh8I1MmcGA", use_encryption=True) - status = await client.status() - - assert type(status) is DishwasherStatus + client = CandyClient( + async_get_clientsession(hass), device_ip=TEST_IP, encryption_key="TqaM9Jxh8I1MmcGA", use_encryption=True + ) + status = await client.status() - aresponses.assert_plan_strictly_followed() + assert type(status) is DishwasherStatus @pytest.mark.asyncio -async def test_status_encryption_without_key(aresponses: ResponsesMockServer): - aresponses.add( - TEST_IP, - "/http-read.json", - response="7B0D0A20202020227374617475734C6176617472696365223A7B0D0A2020202020202020202020202257694669537461747573223A2230222C0D0A20202020202020202020202022457272223A22323535222C0D0A202020202020202020202020224D6163684D64223A2232222C0D0A202020202020202020202020225072223A223133222C0D0A2020202020202020202020202250725068223A2235222C0D0A20202020202020202020202022534C6576656C223A22323535222C0D0A2020202020202020202020202254656D70223A2230222C0D0A202020202020202020202020225370696E5370223A2230222C0D0A202020202020202020202020224F707431223A2230222C0D0A202020202020202020202020224F707432223A2230222C0D0A202020202020202020202020224F707433223A2230222C0D0A202020202020202020202020224F707434223A2230222C0D0A202020202020202020202020224F707435223A2230222C0D0A202020202020202020202020224F707436223A2230222C0D0A202020202020202020202020224F707437223A2230222C0D0A202020202020202020202020224F707438223A2230222C0D0A20202020202020202020202022537465616D223A2230222C0D0A2020202020202020202020202244727954223A2230222C0D0A2020202020202020202020202244656C56616C223A22323535222C0D0A2020202020202020202020202252656D54696D65223A223130222C0D0A202020202020202020202020225265636970654964223A2230222C0D0A20202020202020202020202022436865636B55705374617465223A2230220D0A202020207D0D0A7D" +async def test_status_encryption_without_key(hass, aioclient_mock): + aioclient_mock.get( + f"http://{TEST_IP}/http-read.json", + text="7B0D0A20202020227374617475734C6176617472696365223A7B0D0A2020202020202020202020202257694669537461747573223A2230222C0D0A20202020202020202020202022457272223A22323535222C0D0A202020202020202020202020224D6163684D64223A2232222C0D0A202020202020202020202020225072223A223133222C0D0A2020202020202020202020202250725068223A2235222C0D0A20202020202020202020202022534C6576656C223A22323535222C0D0A2020202020202020202020202254656D70223A2230222C0D0A202020202020202020202020225370696E5370223A2230222C0D0A202020202020202020202020224F707431223A2230222C0D0A202020202020202020202020224F707432223A2230222C0D0A202020202020202020202020224F707433223A2230222C0D0A202020202020202020202020224F707434223A2230222C0D0A202020202020202020202020224F707435223A2230222C0D0A202020202020202020202020224F707436223A2230222C0D0A202020202020202020202020224F707437223A2230222C0D0A202020202020202020202020224F707438223A2230222C0D0A20202020202020202020202022537465616D223A2230222C0D0A2020202020202020202020202244727954223A2230222C0D0A2020202020202020202020202244656C56616C223A22323535222C0D0A2020202020202020202020202252656D54696D65223A223130222C0D0A202020202020202020202020225265636970654964223A2230222C0D0A20202020202020202020202022436865636B55705374617465223A2230220D0A202020207D0D0A7D" ) - async with aiohttp.ClientSession() as session: - client = CandyClient(session, device_ip=TEST_IP, encryption_key="", use_encryption=True) - status = await client.status() - - assert type(status) is WashingMachineStatus + client = CandyClient(async_get_clientsession(hass), device_ip=TEST_IP, encryption_key="", use_encryption=True) + status = await client.status() - aresponses.assert_plan_strictly_followed() + assert type(status) is WashingMachineStatus