Improve canary tests (#39956)

This commit is contained in:
Chris Talkington 2020-09-13 11:32:41 -05:00 committed by GitHub
parent da19854520
commit 17efa1bda5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 261 additions and 227 deletions

View File

@ -76,6 +76,10 @@ class CanaryData:
@Throttle(MIN_TIME_BETWEEN_UPDATES) @Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self, **kwargs): def update(self, **kwargs):
"""Get the latest data from py-canary with a throttle."""
self._update(**kwargs)
def _update(self, **kwargs):
"""Get the latest data from py-canary.""" """Get the latest data from py-canary."""
for location in self._api.get_locations(): for location in self._api.get_locations():
location_id = location.location_id location_id = location.location_id

View File

@ -1 +1,61 @@
"""Tests for the canary component.""" """Tests for the canary component."""
from unittest.mock import MagicMock, PropertyMock
from canary.api import SensorType
from homeassistant.components.homeassistant import (
DOMAIN as HA_DOMAIN,
SERVICE_UPDATE_ENTITY,
)
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import HomeAssistant
async def update_entity(hass: HomeAssistant, entity_id: str) -> None:
"""Run an update action for an entity."""
await hass.services.async_call(
HA_DOMAIN,
SERVICE_UPDATE_ENTITY,
{ATTR_ENTITY_ID: entity_id},
blocking=True,
)
await hass.async_block_till_done()
def mock_device(device_id, name, is_online=True, device_type_name=None):
"""Mock Canary Device class."""
device = MagicMock()
type(device).device_id = PropertyMock(return_value=device_id)
type(device).name = PropertyMock(return_value=name)
type(device).is_online = PropertyMock(return_value=is_online)
type(device).device_type = PropertyMock(
return_value={"id": 1, "name": device_type_name}
)
return device
def mock_location(location_id, name, is_celsius=True, devices=None):
"""Mock Canary Location class."""
location = MagicMock()
type(location).location_id = PropertyMock(return_value=location_id)
type(location).name = PropertyMock(return_value=name)
type(location).is_celsius = PropertyMock(return_value=is_celsius)
type(location).devices = PropertyMock(return_value=devices or [])
return location
def mock_mode(mode_id, name):
"""Mock Canary Mode class."""
mode = MagicMock()
type(mode).mode_id = PropertyMock(return_value=mode_id)
type(mode).name = PropertyMock(return_value=name)
type(mode).resource_url = PropertyMock(return_value=f"/v1/modes/{mode_id}")
return mode
def mock_reading(sensor_type, sensor_value):
"""Mock Canary Reading class."""
reading = MagicMock()
type(reading).sensor_type = SensorType(sensor_type)
type(reading).value = PropertyMock(return_value=sensor_value)
return reading

View File

@ -0,0 +1,34 @@
"""Define fixtures available for all tests."""
from canary.api import Api
from pytest import fixture
from tests.async_mock import MagicMock, patch
def mock_canary_update(self, **kwargs):
"""Get the latest data from py-canary."""
self._update(**kwargs)
@fixture
def canary(hass):
"""Mock the CanaryApi for easier testing."""
with patch.object(Api, "login", return_value=True), patch(
"homeassistant.components.canary.CanaryData.update", mock_canary_update
), patch("homeassistant.components.canary.Api") as mock_canary:
instance = mock_canary.return_value = Api(
"test-username",
"test-password",
1,
)
instance.login = MagicMock(return_value=True)
instance.get_entries = MagicMock(return_value=[])
instance.get_locations = MagicMock(return_value=[])
instance.get_location = MagicMock(return_value=None)
instance.get_modes = MagicMock(return_value=[])
instance.get_readings = MagicMock(return_value=[])
instance.get_latest_readings = MagicMock(return_value=[])
instance.set_location_mode = MagicMock(return_value=None)
yield mock_canary

View File

@ -1,73 +1,37 @@
"""The tests for the Canary component.""" """The tests for the Canary component."""
import unittest from requests import HTTPError
from homeassistant import setup from homeassistant.components.canary import DOMAIN
import homeassistant.components.canary as canary from homeassistant.setup import async_setup_component
from tests.async_mock import MagicMock, PropertyMock, patch from tests.async_mock import patch
from tests.common import get_test_home_assistant
def mock_device(device_id, name, is_online=True, device_type_name=None): async def test_setup_with_valid_config(hass, canary) -> None:
"""Mock Canary Device class.""" """Test setup with valid YAML."""
device = MagicMock() await async_setup_component(hass, "persistent_notification", {})
type(device).device_id = PropertyMock(return_value=device_id) config = {DOMAIN: {"username": "test-username", "password": "test-password"}}
type(device).name = PropertyMock(return_value=name)
type(device).is_online = PropertyMock(return_value=is_online) with patch(
type(device).device_type = PropertyMock( "homeassistant.components.canary.alarm_control_panel.setup_platform",
return_value={"id": 1, "name": device_type_name} return_value=True,
) ), patch(
return device "homeassistant.components.canary.camera.setup_platform",
return_value=True,
), patch(
"homeassistant.components.canary.sensor.setup_platform",
return_value=True,
):
assert await async_setup_component(hass, DOMAIN, config)
await hass.async_block_till_done()
def mock_location(name, is_celsius=True, devices=None): async def test_setup_with_http_error(hass, canary) -> None:
"""Mock Canary Location class.""" """Test setup with HTTP error."""
location = MagicMock() await async_setup_component(hass, "persistent_notification", {})
type(location).name = PropertyMock(return_value=name) config = {DOMAIN: {"username": "test-username", "password": "test-password"}}
type(location).is_celsius = PropertyMock(return_value=is_celsius)
type(location).devices = PropertyMock(return_value=devices or [])
return location
canary.side_effect = HTTPError()
def mock_reading(sensor_type, sensor_value): assert not await async_setup_component(hass, DOMAIN, config)
"""Mock Canary Reading class.""" await hass.async_block_till_done()
reading = MagicMock()
type(reading).sensor_type = PropertyMock(return_value=sensor_type)
type(reading).value = PropertyMock(return_value=sensor_value)
return reading
class TestCanary(unittest.TestCase):
"""Tests the Canary component."""
def setUp(self):
"""Initialize values for this test case class."""
self.hass = get_test_home_assistant()
self.addCleanup(self.tear_down_cleanup)
def tear_down_cleanup(self):
"""Stop everything that was started."""
self.hass.stop()
@patch("homeassistant.components.canary.CanaryData.update")
@patch("canary.api.Api.login")
def test_setup_with_valid_config(self, mock_login, mock_update):
"""Test setup component."""
config = {"canary": {"username": "foo@bar.org", "password": "bar"}}
assert setup.setup_component(self.hass, canary.DOMAIN, config)
mock_update.assert_called_once_with()
mock_login.assert_called_once_with()
def test_setup_with_missing_password(self):
"""Test setup component."""
config = {"canary": {"username": "foo@bar.org"}}
assert not setup.setup_component(self.hass, canary.DOMAIN, config)
def test_setup_with_missing_username(self):
"""Test setup component."""
config = {"canary": {"password": "bar"}}
assert not setup.setup_component(self.hass, canary.DOMAIN, config)

View File

@ -1,203 +1,175 @@
"""The tests for the Canary sensor platform.""" """The tests for the Canary sensor platform."""
import copy from homeassistant.components.canary import DOMAIN
import unittest
from homeassistant.components.canary import DATA_CANARY, sensor as canary
from homeassistant.components.canary.sensor import ( from homeassistant.components.canary.sensor import (
ATTR_AIR_QUALITY, ATTR_AIR_QUALITY,
SENSOR_TYPES,
STATE_AIR_QUALITY_ABNORMAL, STATE_AIR_QUALITY_ABNORMAL,
STATE_AIR_QUALITY_NORMAL, STATE_AIR_QUALITY_NORMAL,
STATE_AIR_QUALITY_VERY_ABNORMAL, STATE_AIR_QUALITY_VERY_ABNORMAL,
CanarySensor,
) )
from homeassistant.const import PERCENTAGE, TEMP_CELSIUS from homeassistant.const import ATTR_UNIT_OF_MEASUREMENT, PERCENTAGE, TEMP_CELSIUS
from homeassistant.setup import async_setup_component
from tests.async_mock import Mock from . import mock_device, mock_location, mock_reading
from tests.common import get_test_home_assistant
from tests.components.canary.test_init import mock_device, mock_location
VALID_CONFIG = {"canary": {"username": "foo@bar.org", "password": "bar"}} from tests.async_mock import patch
from tests.common import mock_registry
class TestCanarySensorSetup(unittest.TestCase): async def test_sensors_pro(hass, canary) -> None:
"""Test the Canary platform.""" """Test the creation and values of the sensors for Canary Pro."""
await async_setup_component(hass, "persistent_notification", {})
DEVICES = [] registry = mock_registry(hass)
online_device_at_home = mock_device(20, "Dining Room", True, "Canary Pro")
def add_entities(self, devices, action): instance = canary.return_value
"""Mock add devices.""" instance.get_locations.return_value = [
for device in devices: mock_location(100, "Home", True, devices=[online_device_at_home]),
self.DEVICES.append(device) ]
def setUp(self): instance.get_latest_readings.return_value = [
"""Initialize values for this testcase class.""" mock_reading("temperature", "21.12"),
self.hass = get_test_home_assistant() mock_reading("humidity", "50.46"),
self.config = copy.deepcopy(VALID_CONFIG) mock_reading("air_quality", "0.59"),
self.addCleanup(self.hass.stop) ]
def test_setup_sensors(self): config = {DOMAIN: {"username": "test-username", "password": "test-password"}}
"""Test the sensor setup.""" with patch("homeassistant.components.canary.CANARY_COMPONENTS", ["sensor"]):
online_device_at_home = mock_device(20, "Dining Room", True, "Canary Pro") assert await async_setup_component(hass, DOMAIN, config)
offline_device_at_home = mock_device(21, "Front Yard", False, "Canary Pro") await hass.async_block_till_done()
online_device_at_work = mock_device(22, "Office", True, "Canary Pro")
self.hass.data[DATA_CANARY] = Mock() sensors = {
self.hass.data[DATA_CANARY].locations = [ "home_dining_room_temperature": (
mock_location( "20_temperature",
"Home", True, devices=[online_device_at_home, offline_device_at_home] "21.12",
), TEMP_CELSIUS,
mock_location("Work", True, devices=[online_device_at_work]), None,
] "mdi:thermometer",
),
"home_dining_room_humidity": (
"20_humidity",
"50.46",
PERCENTAGE,
None,
"mdi:water-percent",
),
"home_dining_room_air_quality": (
"20_air_quality",
"0.59",
None,
None,
"mdi:weather-windy",
),
}
canary.setup_platform(self.hass, self.config, self.add_entities, None) for (sensor_id, data) in sensors.items():
entity_entry = registry.async_get(f"sensor.{sensor_id}")
assert entity_entry
assert entity_entry.device_class == data[3]
assert entity_entry.unique_id == data[0]
assert entity_entry.original_icon == data[4]
assert len(self.DEVICES) == 6 state = hass.states.get(f"sensor.{sensor_id}")
assert state
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == data[2]
assert state.state == data[1]
def test_temperature_sensor(self):
"""Test temperature sensor with fahrenheit."""
device = mock_device(10, "Family Room", "Canary Pro")
location = mock_location("Home", False)
data = Mock() async def test_sensors_attributes_pro(hass, canary) -> None:
data.get_reading.return_value = 21.1234 """Test the creation and values of the sensors attributes for Canary Pro."""
await async_setup_component(hass, "persistent_notification", {})
sensor = CanarySensor(data, SENSOR_TYPES[0], location, device) online_device_at_home = mock_device(20, "Dining Room", True, "Canary Pro")
sensor.update()
assert sensor.name == "Home Family Room Temperature" instance = canary.return_value
assert sensor.unit_of_measurement == TEMP_CELSIUS instance.get_locations.return_value = [
assert sensor.state == 21.12 mock_location(100, "Home", True, devices=[online_device_at_home]),
assert sensor.icon == "mdi:thermometer" ]
def test_temperature_sensor_with_none_sensor_value(self): instance.get_latest_readings.return_value = [
"""Test temperature sensor with fahrenheit.""" mock_reading("temperature", "21.12"),
device = mock_device(10, "Family Room", "Canary Pro") mock_reading("humidity", "50.46"),
location = mock_location("Home", False) mock_reading("air_quality", "0.59"),
]
data = Mock() config = {DOMAIN: {"username": "test-username", "password": "test-password"}}
data.get_reading.return_value = None with patch("homeassistant.components.canary.CANARY_COMPONENTS", ["sensor"]):
assert await async_setup_component(hass, DOMAIN, config)
await hass.async_block_till_done()
sensor = CanarySensor(data, SENSOR_TYPES[0], location, device) entity_id = "sensor.home_dining_room_air_quality"
sensor.update() state = hass.states.get(entity_id)
assert state
assert state.attributes[ATTR_AIR_QUALITY] == STATE_AIR_QUALITY_ABNORMAL
assert sensor.state is None instance.get_latest_readings.return_value = [
mock_reading("temperature", "21.12"),
mock_reading("humidity", "50.46"),
mock_reading("air_quality", "0.4"),
]
def test_humidity_sensor(self): await hass.helpers.entity_component.async_update_entity(entity_id)
"""Test humidity sensor.""" await hass.async_block_till_done()
device = mock_device(10, "Family Room", "Canary Pro")
location = mock_location("Home")
data = Mock() state = hass.states.get(entity_id)
data.get_reading.return_value = 50.4567 assert state
assert state.attributes[ATTR_AIR_QUALITY] == STATE_AIR_QUALITY_VERY_ABNORMAL
sensor = CanarySensor(data, SENSOR_TYPES[1], location, device) instance.get_latest_readings.return_value = [
sensor.update() mock_reading("temperature", "21.12"),
mock_reading("humidity", "50.46"),
mock_reading("air_quality", "1.0"),
]
assert sensor.name == "Home Family Room Humidity" await hass.helpers.entity_component.async_update_entity(entity_id)
assert sensor.unit_of_measurement == PERCENTAGE await hass.async_block_till_done()
assert sensor.state == 50.46
assert sensor.icon == "mdi:water-percent"
def test_air_quality_sensor_with_very_abnormal_reading(self): state = hass.states.get(entity_id)
"""Test air quality sensor.""" assert state
device = mock_device(10, "Family Room", "Canary Pro") assert state.attributes[ATTR_AIR_QUALITY] == STATE_AIR_QUALITY_NORMAL
location = mock_location("Home")
data = Mock()
data.get_reading.return_value = 0.4
sensor = CanarySensor(data, SENSOR_TYPES[2], location, device) async def test_sensors_flex(hass, canary) -> None:
sensor.update() """Test the creation and values of the sensors for Canary Flex."""
await async_setup_component(hass, "persistent_notification", {})
assert sensor.name == "Home Family Room Air Quality" registry = mock_registry(hass)
assert sensor.unit_of_measurement is None online_device_at_home = mock_device(20, "Dining Room", True, "Canary Flex")
assert sensor.state == 0.4
assert sensor.icon == "mdi:weather-windy"
air_quality = sensor.device_state_attributes[ATTR_AIR_QUALITY] instance = canary.return_value
assert air_quality == STATE_AIR_QUALITY_VERY_ABNORMAL instance.get_locations.return_value = [
mock_location(100, "Home", True, devices=[online_device_at_home]),
]
def test_air_quality_sensor_with_abnormal_reading(self): instance.get_latest_readings.return_value = [
"""Test air quality sensor.""" mock_reading("battery", "70.4567"),
device = mock_device(10, "Family Room", "Canary Pro") mock_reading("wifi", "-57"),
location = mock_location("Home") ]
data = Mock() config = {DOMAIN: {"username": "test-username", "password": "test-password"}}
data.get_reading.return_value = 0.59 with patch("homeassistant.components.canary.CANARY_COMPONENTS", ["sensor"]):
assert await async_setup_component(hass, DOMAIN, config)
await hass.async_block_till_done()
sensor = CanarySensor(data, SENSOR_TYPES[2], location, device) sensors = {
sensor.update() "home_dining_room_battery": (
"20_battery",
"70.46",
PERCENTAGE,
None,
"mdi:battery-70",
),
"home_dining_room_wifi": ("20_wifi", "-57.0", "dBm", None, "mdi:wifi"),
}
assert sensor.name == "Home Family Room Air Quality" for (sensor_id, data) in sensors.items():
assert sensor.unit_of_measurement is None entity_entry = registry.async_get(f"sensor.{sensor_id}")
assert sensor.state == 0.59 assert entity_entry
assert sensor.icon == "mdi:weather-windy" assert entity_entry.device_class == data[3]
assert entity_entry.unique_id == data[0]
assert entity_entry.original_icon == data[4]
air_quality = sensor.device_state_attributes[ATTR_AIR_QUALITY] state = hass.states.get(f"sensor.{sensor_id}")
assert air_quality == STATE_AIR_QUALITY_ABNORMAL assert state
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == data[2]
def test_air_quality_sensor_with_normal_reading(self): assert state.state == data[1]
"""Test air quality sensor."""
device = mock_device(10, "Family Room", "Canary Pro")
location = mock_location("Home")
data = Mock()
data.get_reading.return_value = 1.0
sensor = CanarySensor(data, SENSOR_TYPES[2], location, device)
sensor.update()
assert sensor.name == "Home Family Room Air Quality"
assert sensor.unit_of_measurement is None
assert sensor.state == 1.0
assert sensor.icon == "mdi:weather-windy"
air_quality = sensor.device_state_attributes[ATTR_AIR_QUALITY]
assert air_quality == STATE_AIR_QUALITY_NORMAL
def test_air_quality_sensor_with_none_sensor_value(self):
"""Test air quality sensor."""
device = mock_device(10, "Family Room", "Canary Pro")
location = mock_location("Home")
data = Mock()
data.get_reading.return_value = None
sensor = CanarySensor(data, SENSOR_TYPES[2], location, device)
sensor.update()
assert sensor.state is None
assert sensor.device_state_attributes is None
def test_battery_sensor(self):
"""Test battery sensor."""
device = mock_device(10, "Family Room", "Canary Flex")
location = mock_location("Home")
data = Mock()
data.get_reading.return_value = 70.4567
sensor = CanarySensor(data, SENSOR_TYPES[4], location, device)
sensor.update()
assert sensor.name == "Home Family Room Battery"
assert sensor.unit_of_measurement == PERCENTAGE
assert sensor.state == 70.46
assert sensor.icon == "mdi:battery-70"
def test_wifi_sensor(self):
"""Test battery sensor."""
device = mock_device(10, "Family Room", "Canary Flex")
location = mock_location("Home")
data = Mock()
data.get_reading.return_value = -57
sensor = CanarySensor(data, SENSOR_TYPES[3], location, device)
sensor.update()
assert sensor.name == "Home Family Room Wifi"
assert sensor.unit_of_measurement == "dBm"
assert sensor.state == -57
assert sensor.icon == "mdi:wifi"