mirror of https://github.com/home-assistant/core
277 lines
8.5 KiB
Python
277 lines
8.5 KiB
Python
"""Test service for Tibber integration."""
|
|
|
|
import datetime as dt
|
|
from unittest.mock import MagicMock
|
|
|
|
from freezegun.api import FrozenDateTimeFactory
|
|
import pytest
|
|
|
|
from homeassistant.components.tibber.const import DOMAIN
|
|
from homeassistant.components.tibber.services import PRICE_SERVICE_NAME
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.exceptions import ServiceValidationError
|
|
|
|
START_TIME = dt.datetime.fromtimestamp(1615766400).replace(tzinfo=dt.UTC)
|
|
|
|
|
|
def generate_mock_home_data():
|
|
"""Create mock data from the tibber connection."""
|
|
tomorrow = START_TIME + dt.timedelta(days=1)
|
|
mock_homes = [
|
|
MagicMock(
|
|
name="first_home",
|
|
price_total={
|
|
START_TIME.isoformat(): 0.36914,
|
|
(START_TIME + dt.timedelta(hours=1)).isoformat(): 0.36914,
|
|
tomorrow.isoformat(): 0.46914,
|
|
(tomorrow + dt.timedelta(hours=1)).isoformat(): 0.46914,
|
|
},
|
|
price_level={
|
|
START_TIME.isoformat(): "VERY_EXPENSIVE",
|
|
(START_TIME + dt.timedelta(hours=1)).isoformat(): "VERY_EXPENSIVE",
|
|
tomorrow.isoformat(): "VERY_EXPENSIVE",
|
|
(tomorrow + dt.timedelta(hours=1)).isoformat(): "VERY_EXPENSIVE",
|
|
},
|
|
),
|
|
MagicMock(
|
|
name="second_home",
|
|
price_total={
|
|
START_TIME.isoformat(): 0.36914,
|
|
(START_TIME + dt.timedelta(hours=1)).isoformat(): 0.36914,
|
|
tomorrow.isoformat(): 0.46914,
|
|
(tomorrow + dt.timedelta(hours=1)).isoformat(): 0.46914,
|
|
},
|
|
price_level={
|
|
START_TIME.isoformat(): "VERY_EXPENSIVE",
|
|
(START_TIME + dt.timedelta(hours=1)).isoformat(): "VERY_EXPENSIVE",
|
|
tomorrow.isoformat(): "VERY_EXPENSIVE",
|
|
(tomorrow + dt.timedelta(hours=1)).isoformat(): "VERY_EXPENSIVE",
|
|
},
|
|
),
|
|
]
|
|
# set name again, as the name is special in mock objects
|
|
# see documentation: https://docs.python.org/3/library/unittest.mock.html#mock-names-and-the-name-attribute
|
|
mock_homes[0].name = "first_home"
|
|
mock_homes[1].name = "second_home"
|
|
return mock_homes
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"data",
|
|
[
|
|
{},
|
|
{"start": START_TIME.isoformat()},
|
|
{
|
|
"start": START_TIME.isoformat(),
|
|
"end": (START_TIME + dt.timedelta(days=1)).isoformat(),
|
|
},
|
|
],
|
|
)
|
|
async def test_get_prices(
|
|
mock_tibber_setup: MagicMock,
|
|
hass: HomeAssistant,
|
|
freezer: FrozenDateTimeFactory,
|
|
data,
|
|
) -> None:
|
|
"""Test get_prices with mock data."""
|
|
freezer.move_to(START_TIME)
|
|
mock_tibber_setup.get_homes.return_value = generate_mock_home_data()
|
|
|
|
result = await hass.services.async_call(
|
|
DOMAIN, PRICE_SERVICE_NAME, data, blocking=True, return_response=True
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
assert result == {
|
|
"prices": {
|
|
"first_home": [
|
|
{
|
|
"start_time": START_TIME.isoformat(),
|
|
"price": 0.36914,
|
|
"level": "VERY_EXPENSIVE",
|
|
},
|
|
{
|
|
"start_time": (START_TIME + dt.timedelta(hours=1)).isoformat(),
|
|
"price": 0.36914,
|
|
"level": "VERY_EXPENSIVE",
|
|
},
|
|
],
|
|
"second_home": [
|
|
{
|
|
"start_time": START_TIME.isoformat(),
|
|
"price": 0.36914,
|
|
"level": "VERY_EXPENSIVE",
|
|
},
|
|
{
|
|
"start_time": (START_TIME + dt.timedelta(hours=1)).isoformat(),
|
|
"price": 0.36914,
|
|
"level": "VERY_EXPENSIVE",
|
|
},
|
|
],
|
|
}
|
|
}
|
|
|
|
|
|
async def test_get_prices_start_tomorrow(
|
|
mock_tibber_setup: MagicMock,
|
|
hass: HomeAssistant,
|
|
freezer: FrozenDateTimeFactory,
|
|
) -> None:
|
|
"""Test get_prices with start date tomorrow."""
|
|
freezer.move_to(START_TIME)
|
|
tomorrow = START_TIME + dt.timedelta(days=1)
|
|
|
|
mock_tibber_setup.get_homes.return_value = generate_mock_home_data()
|
|
|
|
result = await hass.services.async_call(
|
|
DOMAIN,
|
|
PRICE_SERVICE_NAME,
|
|
{"start": tomorrow.isoformat()},
|
|
blocking=True,
|
|
return_response=True,
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
assert result == {
|
|
"prices": {
|
|
"first_home": [
|
|
{
|
|
"start_time": tomorrow.isoformat(),
|
|
"price": 0.46914,
|
|
"level": "VERY_EXPENSIVE",
|
|
},
|
|
{
|
|
"start_time": (tomorrow + dt.timedelta(hours=1)).isoformat(),
|
|
"price": 0.46914,
|
|
"level": "VERY_EXPENSIVE",
|
|
},
|
|
],
|
|
"second_home": [
|
|
{
|
|
"start_time": tomorrow.isoformat(),
|
|
"price": 0.46914,
|
|
"level": "VERY_EXPENSIVE",
|
|
},
|
|
{
|
|
"start_time": (tomorrow + dt.timedelta(hours=1)).isoformat(),
|
|
"price": 0.46914,
|
|
"level": "VERY_EXPENSIVE",
|
|
},
|
|
],
|
|
}
|
|
}
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"start_time",
|
|
[
|
|
START_TIME.isoformat(),
|
|
(START_TIME + dt.timedelta(hours=4))
|
|
.replace(tzinfo=dt.timezone(dt.timedelta(hours=4)))
|
|
.isoformat(),
|
|
],
|
|
)
|
|
async def test_get_prices_with_timezones(
|
|
mock_tibber_setup: MagicMock,
|
|
hass: HomeAssistant,
|
|
freezer: FrozenDateTimeFactory,
|
|
start_time: str,
|
|
) -> None:
|
|
"""Test get_prices with timezone and without."""
|
|
freezer.move_to(START_TIME)
|
|
|
|
mock_tibber_setup.get_homes.return_value = generate_mock_home_data()
|
|
|
|
result = await hass.services.async_call(
|
|
DOMAIN,
|
|
PRICE_SERVICE_NAME,
|
|
{"start": start_time},
|
|
blocking=True,
|
|
return_response=True,
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
assert result == {
|
|
"prices": {
|
|
"first_home": [
|
|
{
|
|
"start_time": START_TIME.isoformat(),
|
|
"price": 0.36914,
|
|
"level": "VERY_EXPENSIVE",
|
|
},
|
|
{
|
|
"start_time": (START_TIME + dt.timedelta(hours=1)).isoformat(),
|
|
"price": 0.36914,
|
|
"level": "VERY_EXPENSIVE",
|
|
},
|
|
],
|
|
"second_home": [
|
|
{
|
|
"start_time": START_TIME.isoformat(),
|
|
"price": 0.36914,
|
|
"level": "VERY_EXPENSIVE",
|
|
},
|
|
{
|
|
"start_time": (START_TIME + dt.timedelta(hours=1)).isoformat(),
|
|
"price": 0.36914,
|
|
"level": "VERY_EXPENSIVE",
|
|
},
|
|
],
|
|
}
|
|
}
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"start_time",
|
|
[
|
|
(START_TIME + dt.timedelta(hours=2)).isoformat(),
|
|
(START_TIME + dt.timedelta(hours=2))
|
|
.astimezone(tz=dt.timezone(dt.timedelta(hours=5)))
|
|
.isoformat(),
|
|
(START_TIME + dt.timedelta(hours=2))
|
|
.astimezone(tz=dt.timezone(dt.timedelta(hours=8)))
|
|
.isoformat(),
|
|
(START_TIME + dt.timedelta(hours=2))
|
|
.astimezone(tz=dt.timezone(dt.timedelta(hours=-8)))
|
|
.isoformat(),
|
|
],
|
|
)
|
|
async def test_get_prices_with_wrong_timezones(
|
|
mock_tibber_setup: MagicMock,
|
|
hass: HomeAssistant,
|
|
freezer: FrozenDateTimeFactory,
|
|
start_time: str,
|
|
) -> None:
|
|
"""Test get_prices with incorrect time and/or timezone. We expect an empty list."""
|
|
freezer.move_to(START_TIME)
|
|
tomorrow = START_TIME + dt.timedelta(days=1)
|
|
|
|
mock_tibber_setup.get_homes.return_value = generate_mock_home_data()
|
|
|
|
result = await hass.services.async_call(
|
|
DOMAIN,
|
|
PRICE_SERVICE_NAME,
|
|
{"start": start_time, "end": tomorrow.isoformat()},
|
|
blocking=True,
|
|
return_response=True,
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
assert result == {"prices": {"first_home": [], "second_home": []}}
|
|
|
|
|
|
async def test_get_prices_invalid_input(
|
|
mock_tibber_setup: MagicMock,
|
|
hass: HomeAssistant,
|
|
) -> None:
|
|
"""Test get_prices with invalid input."""
|
|
|
|
with pytest.raises(ServiceValidationError):
|
|
await hass.services.async_call(
|
|
DOMAIN,
|
|
PRICE_SERVICE_NAME,
|
|
{"start": "test"},
|
|
blocking=True,
|
|
return_response=True,
|
|
)
|