core/tests/components/influxdb/test_init.py

1114 lines
34 KiB
Python

"""The tests for the InfluxDB component."""
import datetime
import pytest
import homeassistant.components.influxdb as influxdb
from homeassistant.const import (
EVENT_STATE_CHANGED,
STATE_OFF,
STATE_ON,
STATE_STANDBY,
UNIT_PERCENTAGE,
)
from homeassistant.setup import async_setup_component
from tests.async_mock import MagicMock, Mock, call, patch
BASE_V1_CONFIG = {}
BASE_V2_CONFIG = {
"api_version": influxdb.API_VERSION_2,
"organization": "org",
"token": "token",
}
@pytest.fixture(autouse=True)
def mock_batch_timeout(hass, monkeypatch):
"""Mock the event bus listener and the batch timeout for tests."""
hass.bus.listen = MagicMock()
monkeypatch.setattr(
"homeassistant.components.influxdb.InfluxThread.batch_timeout",
Mock(return_value=0),
)
@pytest.fixture(name="mock_client")
def mock_client_fixture(request):
"""Patch the InfluxDBClient object with mock for version under test."""
if request.param == influxdb.API_VERSION_2:
client_target = "homeassistant.components.influxdb.InfluxDBClientV2"
else:
client_target = "homeassistant.components.influxdb.InfluxDBClient"
with patch(client_target) as client:
yield client
@pytest.fixture(name="get_mock_call")
def get_mock_call_fixture(request):
"""Get version specific lambda to make write API call mock."""
if request.param == influxdb.API_VERSION_2:
return lambda body: call(bucket=influxdb.DEFAULT_BUCKET, record=body)
# pylint: disable=unnecessary-lambda
return lambda body: call(body)
def _get_write_api_mock_v1(mock_influx_client):
"""Return the write api mock for the V1 client."""
return mock_influx_client.return_value.write_points
def _get_write_api_mock_v2(mock_influx_client):
"""Return the write api mock for the V2 client."""
return mock_influx_client.return_value.write_api.return_value.write
@pytest.mark.parametrize(
"mock_client, config_ext, get_write_api",
[
(
influxdb.DEFAULT_API_VERSION,
{
"api_version": influxdb.DEFAULT_API_VERSION,
"username": "user",
"password": "password",
"verify_ssl": "False",
},
_get_write_api_mock_v1,
),
(
influxdb.API_VERSION_2,
{
"api_version": influxdb.API_VERSION_2,
"token": "token",
"organization": "organization",
"bucket": "bucket",
},
_get_write_api_mock_v2,
),
],
indirect=["mock_client"],
)
async def test_setup_config_full(hass, mock_client, config_ext, get_write_api):
"""Test the setup with full configuration."""
config = {
"influxdb": {
"host": "host",
"port": 123,
"database": "db",
"max_retries": 4,
"ssl": "False",
}
}
config["influxdb"].update(config_ext)
assert await async_setup_component(hass, influxdb.DOMAIN, config)
await hass.async_block_till_done()
assert hass.bus.listen.called
assert EVENT_STATE_CHANGED == hass.bus.listen.call_args_list[0][0][0]
assert get_write_api(mock_client).call_count == 1
@pytest.mark.parametrize(
"mock_client, config_ext, get_write_api",
[
(influxdb.DEFAULT_API_VERSION, BASE_V1_CONFIG, _get_write_api_mock_v1),
(influxdb.API_VERSION_2, BASE_V2_CONFIG, _get_write_api_mock_v2),
],
indirect=["mock_client"],
)
async def test_setup_minimal_config(hass, mock_client, config_ext, get_write_api):
"""Test the setup with minimal configuration and defaults."""
config = {"influxdb": {}}
config["influxdb"].update(config_ext)
assert await async_setup_component(hass, influxdb.DOMAIN, config)
await hass.async_block_till_done()
assert hass.bus.listen.called
assert EVENT_STATE_CHANGED == hass.bus.listen.call_args_list[0][0][0]
assert get_write_api(mock_client).call_count == 1
@pytest.mark.parametrize(
"mock_client, config_ext, get_write_api",
[
(influxdb.DEFAULT_API_VERSION, {"username": "user"}, _get_write_api_mock_v1),
(influxdb.DEFAULT_API_VERSION, {"token": "token"}, _get_write_api_mock_v1),
(
influxdb.API_VERSION_2,
{"api_version": influxdb.API_VERSION_2, "organization": "organization"},
_get_write_api_mock_v2,
),
(
influxdb.API_VERSION_2,
{
"api_version": influxdb.API_VERSION_2,
"token": "token",
"organization": "organization",
"username": "user",
"password": "pass",
},
_get_write_api_mock_v2,
),
],
indirect=["mock_client"],
)
async def test_invalid_config(hass, mock_client, config_ext, get_write_api):
"""Test the setup with invalid config or config options specified for wrong version."""
config = {"influxdb": {}}
config["influxdb"].update(config_ext)
assert not await async_setup_component(hass, influxdb.DOMAIN, config)
async def _setup(hass, mock_influx_client, config_ext, get_write_api):
"""Prepare client for next test and return event handler method."""
config = {
"influxdb": {
"host": "host",
"exclude": {"entities": ["fake.blacklisted"], "domains": ["another_fake"]},
}
}
config["influxdb"].update(config_ext)
assert await async_setup_component(hass, influxdb.DOMAIN, config)
await hass.async_block_till_done()
# A call is made to the write API during setup to test the connection.
# Therefore we reset the write API mock here before the test begins.
get_write_api(mock_influx_client).reset_mock()
return hass.bus.listen.call_args_list[0][0][1]
@pytest.mark.parametrize(
"mock_client, config_ext, get_write_api, get_mock_call",
[
(
influxdb.DEFAULT_API_VERSION,
BASE_V1_CONFIG,
_get_write_api_mock_v1,
influxdb.DEFAULT_API_VERSION,
),
(
influxdb.API_VERSION_2,
BASE_V2_CONFIG,
_get_write_api_mock_v2,
influxdb.API_VERSION_2,
),
],
indirect=["mock_client", "get_mock_call"],
)
async def test_event_listener(
hass, mock_client, config_ext, get_write_api, get_mock_call
):
"""Test the event listener."""
handler_method = await _setup(hass, mock_client, config_ext, get_write_api)
# map of HA State to valid influxdb [state, value] fields
valid = {
"1": [None, 1],
"1.0": [None, 1.0],
STATE_ON: [STATE_ON, 1],
STATE_OFF: [STATE_OFF, 0],
STATE_STANDBY: [STATE_STANDBY, None],
"foo": ["foo", None],
}
for in_, out in valid.items():
attrs = {
"unit_of_measurement": "foobars",
"longitude": "1.1",
"latitude": "2.2",
"battery_level": f"99{UNIT_PERCENTAGE}",
"temperature": "20c",
"last_seen": "Last seen 23 minutes ago",
"updated_at": datetime.datetime(2017, 1, 1, 0, 0),
"multi_periods": "0.120.240.2023873",
}
state = MagicMock(
state=in_,
domain="fake",
entity_id="fake.entity-id",
object_id="entity",
attributes=attrs,
)
event = MagicMock(data={"new_state": state}, time_fired=12345)
body = [
{
"measurement": "foobars",
"tags": {"domain": "fake", "entity_id": "entity"},
"time": 12345,
"fields": {
"longitude": 1.1,
"latitude": 2.2,
"battery_level_str": f"99{UNIT_PERCENTAGE}",
"battery_level": 99.0,
"temperature_str": "20c",
"temperature": 20.0,
"last_seen_str": "Last seen 23 minutes ago",
"last_seen": 23.0,
"updated_at_str": "2017-01-01 00:00:00",
"updated_at": 20170101000000,
"multi_periods_str": "0.120.240.2023873",
},
}
]
if out[0] is not None:
body[0]["fields"]["state"] = out[0]
if out[1] is not None:
body[0]["fields"]["value"] = out[1]
handler_method(event)
hass.data[influxdb.DOMAIN].block_till_done()
write_api = get_write_api(mock_client)
assert write_api.call_count == 1
assert write_api.call_args == get_mock_call(body)
write_api.reset_mock()
@pytest.mark.parametrize(
"mock_client, config_ext, get_write_api, get_mock_call",
[
(
influxdb.DEFAULT_API_VERSION,
BASE_V1_CONFIG,
_get_write_api_mock_v1,
influxdb.DEFAULT_API_VERSION,
),
(
influxdb.API_VERSION_2,
BASE_V2_CONFIG,
_get_write_api_mock_v2,
influxdb.API_VERSION_2,
),
],
indirect=["mock_client", "get_mock_call"],
)
async def test_event_listener_no_units(
hass, mock_client, config_ext, get_write_api, get_mock_call
):
"""Test the event listener for missing units."""
handler_method = await _setup(hass, mock_client, config_ext, get_write_api)
for unit in (None, ""):
if unit:
attrs = {"unit_of_measurement": unit}
else:
attrs = {}
state = MagicMock(
state=1,
domain="fake",
entity_id="fake.entity-id",
object_id="entity",
attributes=attrs,
)
event = MagicMock(data={"new_state": state}, time_fired=12345)
body = [
{
"measurement": "fake.entity-id",
"tags": {"domain": "fake", "entity_id": "entity"},
"time": 12345,
"fields": {"value": 1},
}
]
handler_method(event)
hass.data[influxdb.DOMAIN].block_till_done()
write_api = get_write_api(mock_client)
assert write_api.call_count == 1
assert write_api.call_args == get_mock_call(body)
write_api.reset_mock()
@pytest.mark.parametrize(
"mock_client, config_ext, get_write_api, get_mock_call",
[
(
influxdb.DEFAULT_API_VERSION,
BASE_V1_CONFIG,
_get_write_api_mock_v1,
influxdb.DEFAULT_API_VERSION,
),
(
influxdb.API_VERSION_2,
BASE_V2_CONFIG,
_get_write_api_mock_v2,
influxdb.API_VERSION_2,
),
],
indirect=["mock_client", "get_mock_call"],
)
async def test_event_listener_inf(
hass, mock_client, config_ext, get_write_api, get_mock_call
):
"""Test the event listener with large or invalid numbers."""
handler_method = await _setup(hass, mock_client, config_ext, get_write_api)
attrs = {"bignumstring": "9" * 999, "nonumstring": "nan"}
state = MagicMock(
state=8,
domain="fake",
entity_id="fake.entity-id",
object_id="entity",
attributes=attrs,
)
event = MagicMock(data={"new_state": state}, time_fired=12345)
body = [
{
"measurement": "fake.entity-id",
"tags": {"domain": "fake", "entity_id": "entity"},
"time": 12345,
"fields": {"value": 8},
}
]
handler_method(event)
hass.data[influxdb.DOMAIN].block_till_done()
write_api = get_write_api(mock_client)
assert write_api.call_count == 1
assert write_api.call_args == get_mock_call(body)
@pytest.mark.parametrize(
"mock_client, config_ext, get_write_api, get_mock_call",
[
(
influxdb.DEFAULT_API_VERSION,
BASE_V1_CONFIG,
_get_write_api_mock_v1,
influxdb.DEFAULT_API_VERSION,
),
(
influxdb.API_VERSION_2,
BASE_V2_CONFIG,
_get_write_api_mock_v2,
influxdb.API_VERSION_2,
),
],
indirect=["mock_client", "get_mock_call"],
)
async def test_event_listener_states(
hass, mock_client, config_ext, get_write_api, get_mock_call
):
"""Test the event listener against ignored states."""
handler_method = await _setup(hass, mock_client, config_ext, get_write_api)
for state_state in (1, "unknown", "", "unavailable"):
state = MagicMock(
state=state_state,
domain="fake",
entity_id="fake.entity-id",
object_id="entity",
attributes={},
)
event = MagicMock(data={"new_state": state}, time_fired=12345)
body = [
{
"measurement": "fake.entity-id",
"tags": {"domain": "fake", "entity_id": "entity"},
"time": 12345,
"fields": {"value": 1},
}
]
handler_method(event)
hass.data[influxdb.DOMAIN].block_till_done()
write_api = get_write_api(mock_client)
if state_state == 1:
assert write_api.call_count == 1
assert write_api.call_args == get_mock_call(body)
else:
assert not write_api.called
write_api.reset_mock()
@pytest.mark.parametrize(
"mock_client, config_ext, get_write_api, get_mock_call",
[
(
influxdb.DEFAULT_API_VERSION,
BASE_V1_CONFIG,
_get_write_api_mock_v1,
influxdb.DEFAULT_API_VERSION,
),
(
influxdb.API_VERSION_2,
BASE_V2_CONFIG,
_get_write_api_mock_v2,
influxdb.API_VERSION_2,
),
],
indirect=["mock_client", "get_mock_call"],
)
async def test_event_listener_blacklist(
hass, mock_client, config_ext, get_write_api, get_mock_call
):
"""Test the event listener against a blacklist."""
handler_method = await _setup(hass, mock_client, config_ext, get_write_api)
for entity_id in ("ok", "blacklisted"):
state = MagicMock(
state=1,
domain="fake",
entity_id=f"fake.{entity_id}",
object_id=entity_id,
attributes={},
)
event = MagicMock(data={"new_state": state}, time_fired=12345)
body = [
{
"measurement": f"fake.{entity_id}",
"tags": {"domain": "fake", "entity_id": entity_id},
"time": 12345,
"fields": {"value": 1},
}
]
handler_method(event)
hass.data[influxdb.DOMAIN].block_till_done()
write_api = get_write_api(mock_client)
if entity_id == "ok":
assert write_api.call_count == 1
assert write_api.call_args == get_mock_call(body)
else:
assert not write_api.called
write_api.reset_mock()
@pytest.mark.parametrize(
"mock_client, config_ext, get_write_api, get_mock_call",
[
(
influxdb.DEFAULT_API_VERSION,
BASE_V1_CONFIG,
_get_write_api_mock_v1,
influxdb.DEFAULT_API_VERSION,
),
(
influxdb.API_VERSION_2,
BASE_V2_CONFIG,
_get_write_api_mock_v2,
influxdb.API_VERSION_2,
),
],
indirect=["mock_client", "get_mock_call"],
)
async def test_event_listener_blacklist_domain(
hass, mock_client, config_ext, get_write_api, get_mock_call
):
"""Test the event listener against a domain blacklist."""
handler_method = await _setup(hass, mock_client, config_ext, get_write_api)
for domain in ("ok", "another_fake"):
state = MagicMock(
state=1,
domain=domain,
entity_id=f"{domain}.something",
object_id="something",
attributes={},
)
event = MagicMock(data={"new_state": state}, time_fired=12345)
body = [
{
"measurement": f"{domain}.something",
"tags": {"domain": domain, "entity_id": "something"},
"time": 12345,
"fields": {"value": 1},
}
]
handler_method(event)
hass.data[influxdb.DOMAIN].block_till_done()
write_api = get_write_api(mock_client)
if domain == "ok":
assert write_api.call_count == 1
assert write_api.call_args == get_mock_call(body)
else:
assert not write_api.called
write_api.reset_mock()
@pytest.mark.parametrize(
"mock_client, config_ext, get_write_api, get_mock_call",
[
(
influxdb.DEFAULT_API_VERSION,
BASE_V1_CONFIG,
_get_write_api_mock_v1,
influxdb.DEFAULT_API_VERSION,
),
(
influxdb.API_VERSION_2,
BASE_V2_CONFIG,
_get_write_api_mock_v2,
influxdb.API_VERSION_2,
),
],
indirect=["mock_client", "get_mock_call"],
)
async def test_event_listener_whitelist(
hass, mock_client, config_ext, get_write_api, get_mock_call
):
"""Test the event listener against a whitelist."""
config = {"include": {"entities": ["fake.included"]}}
config.update(config_ext)
handler_method = await _setup(hass, mock_client, config, get_write_api)
for entity_id in ("included", "default"):
state = MagicMock(
state=1,
domain="fake",
entity_id=f"fake.{entity_id}",
object_id=entity_id,
attributes={},
)
event = MagicMock(data={"new_state": state}, time_fired=12345)
body = [
{
"measurement": f"fake.{entity_id}",
"tags": {"domain": "fake", "entity_id": entity_id},
"time": 12345,
"fields": {"value": 1},
}
]
handler_method(event)
hass.data[influxdb.DOMAIN].block_till_done()
write_api = get_write_api(mock_client)
if entity_id == "included":
assert write_api.call_count == 1
assert write_api.call_args == get_mock_call(body)
else:
assert not write_api.called
write_api.reset_mock()
@pytest.mark.parametrize(
"mock_client, config_ext, get_write_api, get_mock_call",
[
(
influxdb.DEFAULT_API_VERSION,
BASE_V1_CONFIG,
_get_write_api_mock_v1,
influxdb.DEFAULT_API_VERSION,
),
(
influxdb.API_VERSION_2,
BASE_V2_CONFIG,
_get_write_api_mock_v2,
influxdb.API_VERSION_2,
),
],
indirect=["mock_client", "get_mock_call"],
)
async def test_event_listener_whitelist_domain(
hass, mock_client, config_ext, get_write_api, get_mock_call
):
"""Test the event listener against a domain whitelist."""
config = {"include": {"domains": ["fake"]}}
config.update(config_ext)
handler_method = await _setup(hass, mock_client, config, get_write_api)
for domain in ("fake", "another_fake"):
state = MagicMock(
state=1,
domain=domain,
entity_id=f"{domain}.something",
object_id="something",
attributes={},
)
event = MagicMock(data={"new_state": state}, time_fired=12345)
body = [
{
"measurement": f"{domain}.something",
"tags": {"domain": domain, "entity_id": "something"},
"time": 12345,
"fields": {"value": 1},
}
]
handler_method(event)
hass.data[influxdb.DOMAIN].block_till_done()
write_api = get_write_api(mock_client)
if domain == "fake":
assert write_api.call_count == 1
assert write_api.call_args == get_mock_call(body)
else:
assert not write_api.called
write_api.reset_mock()
@pytest.mark.parametrize(
"mock_client, config_ext, get_write_api, get_mock_call",
[
(
influxdb.DEFAULT_API_VERSION,
BASE_V1_CONFIG,
_get_write_api_mock_v1,
influxdb.DEFAULT_API_VERSION,
),
(
influxdb.API_VERSION_2,
BASE_V2_CONFIG,
_get_write_api_mock_v2,
influxdb.API_VERSION_2,
),
],
indirect=["mock_client", "get_mock_call"],
)
async def test_event_listener_whitelist_domain_and_entities(
hass, mock_client, config_ext, get_write_api, get_mock_call
):
"""Test the event listener against a domain and entity whitelist."""
config = {"include": {"domains": ["fake"], "entities": ["other.one"]}}
config.update(config_ext)
handler_method = await _setup(hass, mock_client, config, get_write_api)
for domain in ("fake", "another_fake"):
state = MagicMock(
state=1,
domain=domain,
entity_id=f"{domain}.something",
object_id="something",
attributes={},
)
event = MagicMock(data={"new_state": state}, time_fired=12345)
body = [
{
"measurement": f"{domain}.something",
"tags": {"domain": domain, "entity_id": "something"},
"time": 12345,
"fields": {"value": 1},
}
]
handler_method(event)
hass.data[influxdb.DOMAIN].block_till_done()
write_api = get_write_api(mock_client)
if domain == "fake":
assert write_api.call_count == 1
assert write_api.call_args == get_mock_call(body)
else:
assert not write_api.called
write_api.reset_mock()
for entity_id in ("one", "two"):
state = MagicMock(
state=1,
domain="other",
entity_id=f"other.{entity_id}",
object_id=entity_id,
attributes={},
)
event = MagicMock(data={"new_state": state}, time_fired=12345)
body = [
{
"measurement": f"other.{entity_id}",
"tags": {"domain": "other", "entity_id": entity_id},
"time": 12345,
"fields": {"value": 1},
}
]
handler_method(event)
hass.data[influxdb.DOMAIN].block_till_done()
write_api = get_write_api(mock_client)
if entity_id == "one":
assert write_api.call_count == 1
assert write_api.call_args == get_mock_call(body)
else:
assert not write_api.called
write_api.reset_mock()
@pytest.mark.parametrize(
"mock_client, config_ext, get_write_api, get_mock_call",
[
(
influxdb.DEFAULT_API_VERSION,
BASE_V1_CONFIG,
_get_write_api_mock_v1,
influxdb.DEFAULT_API_VERSION,
),
(
influxdb.API_VERSION_2,
BASE_V2_CONFIG,
_get_write_api_mock_v2,
influxdb.API_VERSION_2,
),
],
indirect=["mock_client", "get_mock_call"],
)
async def test_event_listener_invalid_type(
hass, mock_client, config_ext, get_write_api, get_mock_call
):
"""Test the event listener when an attribute has an invalid type."""
handler_method = await _setup(hass, mock_client, config_ext, get_write_api)
# map of HA State to valid influxdb [state, value] fields
valid = {
"1": [None, 1],
"1.0": [None, 1.0],
STATE_ON: [STATE_ON, 1],
STATE_OFF: [STATE_OFF, 0],
STATE_STANDBY: [STATE_STANDBY, None],
"foo": ["foo", None],
}
for in_, out in valid.items():
attrs = {
"unit_of_measurement": "foobars",
"longitude": "1.1",
"latitude": "2.2",
"invalid_attribute": ["value1", "value2"],
}
state = MagicMock(
state=in_,
domain="fake",
entity_id="fake.entity-id",
object_id="entity",
attributes=attrs,
)
event = MagicMock(data={"new_state": state}, time_fired=12345)
body = [
{
"measurement": "foobars",
"tags": {"domain": "fake", "entity_id": "entity"},
"time": 12345,
"fields": {
"longitude": 1.1,
"latitude": 2.2,
"invalid_attribute_str": "['value1', 'value2']",
},
}
]
if out[0] is not None:
body[0]["fields"]["state"] = out[0]
if out[1] is not None:
body[0]["fields"]["value"] = out[1]
handler_method(event)
hass.data[influxdb.DOMAIN].block_till_done()
write_api = get_write_api(mock_client)
assert write_api.call_count == 1
assert write_api.call_args == get_mock_call(body)
write_api.reset_mock()
@pytest.mark.parametrize(
"mock_client, config_ext, get_write_api, get_mock_call",
[
(
influxdb.DEFAULT_API_VERSION,
BASE_V1_CONFIG,
_get_write_api_mock_v1,
influxdb.DEFAULT_API_VERSION,
),
(
influxdb.API_VERSION_2,
BASE_V2_CONFIG,
_get_write_api_mock_v2,
influxdb.API_VERSION_2,
),
],
indirect=["mock_client", "get_mock_call"],
)
async def test_event_listener_default_measurement(
hass, mock_client, config_ext, get_write_api, get_mock_call
):
"""Test the event listener with a default measurement."""
config = {"default_measurement": "state"}
config.update(config_ext)
handler_method = await _setup(hass, mock_client, config, get_write_api)
state = MagicMock(
state=1, domain="fake", entity_id="fake.ok", object_id="ok", attributes={},
)
event = MagicMock(data={"new_state": state}, time_fired=12345)
body = [
{
"measurement": "state",
"tags": {"domain": "fake", "entity_id": "ok"},
"time": 12345,
"fields": {"value": 1},
}
]
handler_method(event)
hass.data[influxdb.DOMAIN].block_till_done()
write_api = get_write_api(mock_client)
assert write_api.call_count == 1
assert write_api.call_args == get_mock_call(body)
@pytest.mark.parametrize(
"mock_client, config_ext, get_write_api, get_mock_call",
[
(
influxdb.DEFAULT_API_VERSION,
BASE_V1_CONFIG,
_get_write_api_mock_v1,
influxdb.DEFAULT_API_VERSION,
),
(
influxdb.API_VERSION_2,
BASE_V2_CONFIG,
_get_write_api_mock_v2,
influxdb.API_VERSION_2,
),
],
indirect=["mock_client", "get_mock_call"],
)
async def test_event_listener_unit_of_measurement_field(
hass, mock_client, config_ext, get_write_api, get_mock_call
):
"""Test the event listener for unit of measurement field."""
config = {"override_measurement": "state"}
config.update(config_ext)
handler_method = await _setup(hass, mock_client, config, get_write_api)
attrs = {"unit_of_measurement": "foobars"}
state = MagicMock(
state="foo",
domain="fake",
entity_id="fake.entity-id",
object_id="entity",
attributes=attrs,
)
event = MagicMock(data={"new_state": state}, time_fired=12345)
body = [
{
"measurement": "state",
"tags": {"domain": "fake", "entity_id": "entity"},
"time": 12345,
"fields": {"state": "foo", "unit_of_measurement_str": "foobars"},
}
]
handler_method(event)
hass.data[influxdb.DOMAIN].block_till_done()
write_api = get_write_api(mock_client)
assert write_api.call_count == 1
assert write_api.call_args == get_mock_call(body)
@pytest.mark.parametrize(
"mock_client, config_ext, get_write_api, get_mock_call",
[
(
influxdb.DEFAULT_API_VERSION,
BASE_V1_CONFIG,
_get_write_api_mock_v1,
influxdb.DEFAULT_API_VERSION,
),
(
influxdb.API_VERSION_2,
BASE_V2_CONFIG,
_get_write_api_mock_v2,
influxdb.API_VERSION_2,
),
],
indirect=["mock_client", "get_mock_call"],
)
async def test_event_listener_tags_attributes(
hass, mock_client, config_ext, get_write_api, get_mock_call
):
"""Test the event listener when some attributes should be tags."""
config = {"tags_attributes": ["friendly_fake"]}
config.update(config_ext)
handler_method = await _setup(hass, mock_client, config, get_write_api)
attrs = {"friendly_fake": "tag_str", "field_fake": "field_str"}
state = MagicMock(
state=1,
domain="fake",
entity_id="fake.something",
object_id="something",
attributes=attrs,
)
event = MagicMock(data={"new_state": state}, time_fired=12345)
body = [
{
"measurement": "fake.something",
"tags": {
"domain": "fake",
"entity_id": "something",
"friendly_fake": "tag_str",
},
"time": 12345,
"fields": {"value": 1, "field_fake_str": "field_str"},
}
]
handler_method(event)
hass.data[influxdb.DOMAIN].block_till_done()
write_api = get_write_api(mock_client)
assert write_api.call_count == 1
assert write_api.call_args == get_mock_call(body)
@pytest.mark.parametrize(
"mock_client, config_ext, get_write_api, get_mock_call",
[
(
influxdb.DEFAULT_API_VERSION,
BASE_V1_CONFIG,
_get_write_api_mock_v1,
influxdb.DEFAULT_API_VERSION,
),
(
influxdb.API_VERSION_2,
BASE_V2_CONFIG,
_get_write_api_mock_v2,
influxdb.API_VERSION_2,
),
],
indirect=["mock_client", "get_mock_call"],
)
async def test_event_listener_component_override_measurement(
hass, mock_client, config_ext, get_write_api, get_mock_call
):
"""Test the event listener with overridden measurements."""
config = {
"component_config": {
"sensor.fake_humidity": {"override_measurement": "humidity"}
},
"component_config_glob": {
"binary_sensor.*motion": {"override_measurement": "motion"}
},
"component_config_domain": {"climate": {"override_measurement": "hvac"}},
}
config.update(config_ext)
handler_method = await _setup(hass, mock_client, config, get_write_api)
test_components = [
{"domain": "sensor", "id": "fake_humidity", "res": "humidity"},
{"domain": "binary_sensor", "id": "fake_motion", "res": "motion"},
{"domain": "climate", "id": "fake_thermostat", "res": "hvac"},
{"domain": "other", "id": "just_fake", "res": "other.just_fake"},
]
for comp in test_components:
state = MagicMock(
state=1,
domain=comp["domain"],
entity_id=f"{comp['domain']}.{comp['id']}",
object_id=comp["id"],
attributes={},
)
event = MagicMock(data={"new_state": state}, time_fired=12345)
body = [
{
"measurement": comp["res"],
"tags": {"domain": comp["domain"], "entity_id": comp["id"]},
"time": 12345,
"fields": {"value": 1},
}
]
handler_method(event)
hass.data[influxdb.DOMAIN].block_till_done()
write_api = get_write_api(mock_client)
assert write_api.call_count == 1
assert write_api.call_args == get_mock_call(body)
write_api.reset_mock()
@pytest.mark.parametrize(
"mock_client, config_ext, get_write_api, get_mock_call",
[
(
influxdb.DEFAULT_API_VERSION,
BASE_V1_CONFIG,
_get_write_api_mock_v1,
influxdb.DEFAULT_API_VERSION,
),
(
influxdb.API_VERSION_2,
BASE_V2_CONFIG,
_get_write_api_mock_v2,
influxdb.API_VERSION_2,
),
],
indirect=["mock_client", "get_mock_call"],
)
async def test_event_listener_scheduled_write(
hass, mock_client, config_ext, get_write_api, get_mock_call
):
"""Test the event listener retries after a write failure."""
config = {"max_retries": 1}
config.update(config_ext)
handler_method = await _setup(hass, mock_client, config, get_write_api)
state = MagicMock(
state=1,
domain="fake",
entity_id="entity.id",
object_id="entity",
attributes={},
)
event = MagicMock(data={"new_state": state}, time_fired=12345)
write_api = get_write_api(mock_client)
write_api.side_effect = IOError("foo")
# Write fails
with patch.object(influxdb.time, "sleep") as mock_sleep:
handler_method(event)
hass.data[influxdb.DOMAIN].block_till_done()
assert mock_sleep.called
assert write_api.call_count == 2
# Write works again
write_api.side_effect = None
with patch.object(influxdb.time, "sleep") as mock_sleep:
handler_method(event)
hass.data[influxdb.DOMAIN].block_till_done()
assert not mock_sleep.called
assert write_api.call_count == 3
@pytest.mark.parametrize(
"mock_client, config_ext, get_write_api, get_mock_call",
[
(
influxdb.DEFAULT_API_VERSION,
BASE_V1_CONFIG,
_get_write_api_mock_v1,
influxdb.DEFAULT_API_VERSION,
),
(
influxdb.API_VERSION_2,
BASE_V2_CONFIG,
_get_write_api_mock_v2,
influxdb.API_VERSION_2,
),
],
indirect=["mock_client", "get_mock_call"],
)
async def test_event_listener_backlog_full(
hass, mock_client, config_ext, get_write_api, get_mock_call
):
"""Test the event listener drops old events when backlog gets full."""
handler_method = await _setup(hass, mock_client, config_ext, get_write_api)
state = MagicMock(
state=1,
domain="fake",
entity_id="entity.id",
object_id="entity",
attributes={},
)
event = MagicMock(data={"new_state": state}, time_fired=12345)
monotonic_time = 0
def fast_monotonic():
"""Monotonic time that ticks fast enough to cause a timeout."""
nonlocal monotonic_time
monotonic_time += 60
return monotonic_time
with patch("homeassistant.components.influxdb.time.monotonic", new=fast_monotonic):
handler_method(event)
hass.data[influxdb.DOMAIN].block_till_done()
assert get_write_api(mock_client).call_count == 0