mirror of https://github.com/home-assistant/core
247 lines
8.0 KiB
Python
247 lines
8.0 KiB
Python
"""The tests for the TCP sensor platform."""
|
|
|
|
from copy import copy
|
|
from unittest.mock import call, patch
|
|
|
|
import pytest
|
|
|
|
import homeassistant.components.tcp.common as tcp
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.setup import async_setup_component
|
|
|
|
from tests.common import assert_setup_component
|
|
|
|
TEST_CONFIG = {
|
|
"sensor": {
|
|
"platform": "tcp",
|
|
tcp.CONF_NAME: "test_name",
|
|
tcp.CONF_HOST: "test_host",
|
|
tcp.CONF_PORT: 12345,
|
|
tcp.CONF_TIMEOUT: tcp.DEFAULT_TIMEOUT + 1,
|
|
tcp.CONF_PAYLOAD: "test_payload",
|
|
tcp.CONF_UNIT_OF_MEASUREMENT: "test_unit",
|
|
tcp.CONF_VALUE_TEMPLATE: "{{ '7.' + value }}",
|
|
tcp.CONF_VALUE_ON: "7.on",
|
|
tcp.CONF_BUFFER_SIZE: tcp.DEFAULT_BUFFER_SIZE + 1,
|
|
}
|
|
}
|
|
SENSOR_TEST_CONFIG = TEST_CONFIG["sensor"]
|
|
TEST_ENTITY = "sensor.test_name"
|
|
|
|
KEYS_AND_DEFAULTS = {
|
|
tcp.CONF_NAME: tcp.DEFAULT_NAME,
|
|
tcp.CONF_TIMEOUT: tcp.DEFAULT_TIMEOUT,
|
|
tcp.CONF_UNIT_OF_MEASUREMENT: None,
|
|
tcp.CONF_VALUE_TEMPLATE: None,
|
|
tcp.CONF_VALUE_ON: None,
|
|
tcp.CONF_BUFFER_SIZE: tcp.DEFAULT_BUFFER_SIZE,
|
|
}
|
|
|
|
socket_test_value = "123"
|
|
|
|
|
|
@pytest.fixture(name="mock_socket")
|
|
def mock_socket_fixture(mock_select):
|
|
"""Mock socket."""
|
|
with patch("homeassistant.components.tcp.entity.socket.socket") as mock_socket:
|
|
socket_instance = mock_socket.return_value.__enter__.return_value
|
|
socket_instance.recv.return_value = socket_test_value.encode()
|
|
yield socket_instance
|
|
|
|
|
|
@pytest.fixture(name="mock_select")
|
|
def mock_select_fixture():
|
|
"""Mock select."""
|
|
with patch(
|
|
"homeassistant.components.tcp.entity.select.select",
|
|
return_value=(True, False, False),
|
|
) as mock_select:
|
|
yield mock_select
|
|
|
|
|
|
@pytest.fixture(name="mock_ssl_context")
|
|
def mock_ssl_context_fixture():
|
|
"""Mock select."""
|
|
with patch(
|
|
"homeassistant.components.tcp.entity.ssl.create_default_context",
|
|
) as mock_ssl_context:
|
|
mock_ssl_context.return_value.wrap_socket.return_value.recv.return_value = (
|
|
socket_test_value + "567"
|
|
).encode()
|
|
yield mock_ssl_context
|
|
|
|
|
|
async def test_setup_platform_valid_config(hass: HomeAssistant, mock_socket) -> None:
|
|
"""Check a valid configuration and call add_entities with sensor."""
|
|
with assert_setup_component(1, "sensor"):
|
|
assert await async_setup_component(hass, "sensor", TEST_CONFIG)
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
async def test_setup_platform_invalid_config(hass: HomeAssistant, mock_socket) -> None:
|
|
"""Check an invalid configuration."""
|
|
with assert_setup_component(0):
|
|
assert await async_setup_component(
|
|
hass, "sensor", {"sensor": {"platform": "tcp", "porrt": 1234}}
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
async def test_state(hass: HomeAssistant, mock_socket, mock_select) -> None:
|
|
"""Return the contents of _state."""
|
|
assert await async_setup_component(hass, "sensor", TEST_CONFIG)
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get(TEST_ENTITY)
|
|
|
|
assert state
|
|
assert state.state == "7.123"
|
|
assert (
|
|
state.attributes["unit_of_measurement"]
|
|
== SENSOR_TEST_CONFIG[tcp.CONF_UNIT_OF_MEASUREMENT]
|
|
)
|
|
assert mock_socket.connect.called
|
|
assert mock_socket.connect.call_args == call(
|
|
(SENSOR_TEST_CONFIG["host"], SENSOR_TEST_CONFIG["port"])
|
|
)
|
|
assert mock_socket.send.called
|
|
assert mock_socket.send.call_args == call(SENSOR_TEST_CONFIG["payload"].encode())
|
|
assert mock_select.call_args == call(
|
|
[mock_socket], [], [], SENSOR_TEST_CONFIG[tcp.CONF_TIMEOUT]
|
|
)
|
|
assert mock_socket.recv.called
|
|
assert mock_socket.recv.call_args == call(SENSOR_TEST_CONFIG["buffer_size"])
|
|
|
|
|
|
async def test_config_uses_defaults(hass: HomeAssistant, mock_socket) -> None:
|
|
"""Check if defaults were set."""
|
|
config = copy(SENSOR_TEST_CONFIG)
|
|
|
|
for key in KEYS_AND_DEFAULTS:
|
|
del config[key]
|
|
|
|
with assert_setup_component(1) as result_config:
|
|
assert await async_setup_component(hass, "sensor", {"sensor": config})
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get("sensor.tcp_sensor")
|
|
|
|
assert state
|
|
assert state.state == "123"
|
|
|
|
for key, default in KEYS_AND_DEFAULTS.items():
|
|
assert result_config["sensor"][0].get(key) == default
|
|
|
|
|
|
@pytest.mark.parametrize("sock_attr", ["connect", "send"])
|
|
async def test_update_socket_error(hass: HomeAssistant, mock_socket, sock_attr) -> None:
|
|
"""Test socket errors during update."""
|
|
socket_method = getattr(mock_socket, sock_attr)
|
|
socket_method.side_effect = OSError("Boom")
|
|
|
|
assert await async_setup_component(hass, "sensor", TEST_CONFIG)
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get(TEST_ENTITY)
|
|
|
|
assert state
|
|
assert state.state == "unknown"
|
|
|
|
|
|
async def test_update_select_fails(
|
|
hass: HomeAssistant, mock_socket, mock_select
|
|
) -> None:
|
|
"""Test select fails to return a socket for reading."""
|
|
mock_select.return_value = (False, False, False)
|
|
|
|
assert await async_setup_component(hass, "sensor", TEST_CONFIG)
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get(TEST_ENTITY)
|
|
|
|
assert state
|
|
assert state.state == "unknown"
|
|
|
|
|
|
async def test_update_returns_if_template_render_fails(
|
|
hass: HomeAssistant, mock_socket
|
|
) -> None:
|
|
"""Return None if rendering the template fails."""
|
|
config = copy(SENSOR_TEST_CONFIG)
|
|
config[tcp.CONF_VALUE_TEMPLATE] = "{{ value / 0 }}"
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": config})
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get(TEST_ENTITY)
|
|
|
|
assert state
|
|
assert state.state == "unknown"
|
|
|
|
|
|
async def test_ssl_state(
|
|
hass: HomeAssistant, mock_socket, mock_select, mock_ssl_context
|
|
) -> None:
|
|
"""Return the contents of _state, updated over SSL."""
|
|
config = copy(SENSOR_TEST_CONFIG)
|
|
config[tcp.CONF_SSL] = "on"
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": config})
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get(TEST_ENTITY)
|
|
|
|
assert state
|
|
assert state.state == "7.123567"
|
|
assert mock_socket.connect.called
|
|
assert mock_socket.connect.call_args == call(
|
|
(SENSOR_TEST_CONFIG["host"], SENSOR_TEST_CONFIG["port"])
|
|
)
|
|
assert not mock_socket.send.called
|
|
assert mock_ssl_context.called
|
|
assert mock_ssl_context.return_value.check_hostname
|
|
mock_ssl_socket = mock_ssl_context.return_value.wrap_socket.return_value
|
|
assert mock_ssl_socket.send.called
|
|
assert mock_ssl_socket.send.call_args == call(
|
|
SENSOR_TEST_CONFIG["payload"].encode()
|
|
)
|
|
assert mock_select.call_args == call(
|
|
[mock_ssl_socket], [], [], SENSOR_TEST_CONFIG[tcp.CONF_TIMEOUT]
|
|
)
|
|
assert mock_ssl_socket.recv.called
|
|
assert mock_ssl_socket.recv.call_args == call(SENSOR_TEST_CONFIG["buffer_size"])
|
|
|
|
|
|
async def test_ssl_state_verify_off(
|
|
hass: HomeAssistant, mock_socket, mock_select, mock_ssl_context
|
|
) -> None:
|
|
"""Return the contents of _state, updated over SSL (verify_ssl disabled)."""
|
|
config = copy(SENSOR_TEST_CONFIG)
|
|
config[tcp.CONF_SSL] = "on"
|
|
config[tcp.CONF_VERIFY_SSL] = "off"
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": config})
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get(TEST_ENTITY)
|
|
|
|
assert state
|
|
assert state.state == "7.123567"
|
|
assert mock_socket.connect.called
|
|
assert mock_socket.connect.call_args == call(
|
|
(SENSOR_TEST_CONFIG["host"], SENSOR_TEST_CONFIG["port"])
|
|
)
|
|
assert not mock_socket.send.called
|
|
assert mock_ssl_context.called
|
|
assert not mock_ssl_context.return_value.check_hostname
|
|
mock_ssl_socket = mock_ssl_context.return_value.wrap_socket.return_value
|
|
assert mock_ssl_socket.send.called
|
|
assert mock_ssl_socket.send.call_args == call(
|
|
SENSOR_TEST_CONFIG["payload"].encode()
|
|
)
|
|
assert mock_select.call_args == call(
|
|
[mock_ssl_socket], [], [], SENSOR_TEST_CONFIG[tcp.CONF_TIMEOUT]
|
|
)
|
|
assert mock_ssl_socket.recv.called
|
|
assert mock_ssl_socket.recv.call_args == call(SENSOR_TEST_CONFIG["buffer_size"])
|