Skip to content

Commit

Permalink
Update dependencies, refactor client tests (#138)
Browse files Browse the repository at this point in the history
* Update dependencies, refactor client tests

* Fix merge conflicts

* Fix HACS validation

* Remove title from translations (fix hassfest validation)
  • Loading branch information
ofalvai authored Jun 26, 2022
1 parent e12c893 commit 7bd117f
Show file tree
Hide file tree
Showing 19 changed files with 85 additions and 123 deletions.
1 change: 0 additions & 1 deletion custom_components/candy/strings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"title": "Candy",
"config": {
"step": {
"user": {
Expand Down
4 changes: 1 addition & 3 deletions hacs.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
{
"name": "Candy Simply-Fi",
"render_readme": true,
"domains": ["sensor"],
"iot_class": ["Local Polling"]
"render_readme": true
}
5 changes: 2 additions & 3 deletions requirements_test.txt
Original file line number Diff line number Diff line change
@@ -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
Expand Down
8 changes: 0 additions & 8 deletions tests/common.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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,
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
190 changes: 82 additions & 108 deletions tests/test_client.py
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 7bd117f

Please sign in to comment.