mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
Clean up Tibber service tests (#127334)
* Tibber: cleanup tests * Tibber: cleanup tests
This commit is contained in:
parent
54c4fb5f56
commit
9a0cb59830
@ -5,6 +5,7 @@ from unittest.mock import AsyncMock, MagicMock, PropertyMock, patch
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components.recorder import Recorder
|
||||||
from homeassistant.components.tibber.const import DOMAIN
|
from homeassistant.components.tibber.const import DOMAIN
|
||||||
from homeassistant.const import CONF_ACCESS_TOKEN
|
from homeassistant.const import CONF_ACCESS_TOKEN
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
@ -26,7 +27,7 @@ def config_entry(hass: HomeAssistant) -> MockConfigEntry:
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
async def mock_tibber_setup(
|
async def mock_tibber_setup(
|
||||||
config_entry: MockConfigEntry, hass: HomeAssistant
|
recorder_mock: Recorder, config_entry: MockConfigEntry, hass: HomeAssistant
|
||||||
) -> AsyncGenerator[MagicMock]:
|
) -> AsyncGenerator[MagicMock]:
|
||||||
"""Mock tibber entry setup."""
|
"""Mock tibber entry setup."""
|
||||||
unique_user_id = "unique_user_id"
|
unique_user_id = "unique_user_id"
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
"""Test service for Tibber integration."""
|
"""Test service for Tibber integration."""
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
@ -8,19 +7,16 @@ from freezegun.api import FrozenDateTimeFactory
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.tibber.const import DOMAIN
|
from homeassistant.components.tibber.const import DOMAIN
|
||||||
from homeassistant.components.tibber.services import PRICE_SERVICE_NAME, __get_prices
|
from homeassistant.components.tibber.services import PRICE_SERVICE_NAME
|
||||||
from homeassistant.core import ServiceCall
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ServiceValidationError
|
from homeassistant.exceptions import ServiceValidationError
|
||||||
from homeassistant.util import dt as dt_util
|
|
||||||
|
|
||||||
STARTTIME = dt.datetime.fromtimestamp(1615766400).replace(
|
START_TIME = dt.datetime.fromtimestamp(1615766400).replace(tzinfo=dt.UTC)
|
||||||
tzinfo=dt_util.get_default_time_zone()
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def generate_mock_home_data():
|
def generate_mock_home_data():
|
||||||
"""Create mock data from the tibber connection."""
|
"""Create mock data from the tibber connection."""
|
||||||
tomorrow = STARTTIME + dt.timedelta(days=1)
|
tomorrow = START_TIME + dt.timedelta(days=1)
|
||||||
mock_homes = [
|
mock_homes = [
|
||||||
MagicMock(
|
MagicMock(
|
||||||
name="first_home",
|
name="first_home",
|
||||||
@ -31,15 +27,15 @@ def generate_mock_home_data():
|
|||||||
"priceInfo": {
|
"priceInfo": {
|
||||||
"today": [
|
"today": [
|
||||||
{
|
{
|
||||||
"startsAt": STARTTIME.isoformat(),
|
"startsAt": START_TIME.isoformat(),
|
||||||
"total": 0.46914,
|
"total": 0.36914,
|
||||||
"level": "VERY_EXPENSIVE",
|
"level": "VERY_EXPENSIVE",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"startsAt": (
|
"startsAt": (
|
||||||
STARTTIME + dt.timedelta(hours=1)
|
START_TIME + dt.timedelta(hours=1)
|
||||||
).isoformat(),
|
).isoformat(),
|
||||||
"total": 0.46914,
|
"total": 0.36914,
|
||||||
"level": "VERY_EXPENSIVE",
|
"level": "VERY_EXPENSIVE",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -72,15 +68,15 @@ def generate_mock_home_data():
|
|||||||
"priceInfo": {
|
"priceInfo": {
|
||||||
"today": [
|
"today": [
|
||||||
{
|
{
|
||||||
"startsAt": STARTTIME.isoformat(),
|
"startsAt": START_TIME.isoformat(),
|
||||||
"total": 0.46914,
|
"total": 0.36914,
|
||||||
"level": "VERY_EXPENSIVE",
|
"level": "VERY_EXPENSIVE",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"startsAt": (
|
"startsAt": (
|
||||||
STARTTIME + dt.timedelta(hours=1)
|
START_TIME + dt.timedelta(hours=1)
|
||||||
).isoformat(),
|
).isoformat(),
|
||||||
"total": 0.46914,
|
"total": 0.36914,
|
||||||
"level": "VERY_EXPENSIVE",
|
"level": "VERY_EXPENSIVE",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -105,101 +101,67 @@ def generate_mock_home_data():
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
# 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[0].name = "first_home"
|
||||||
mock_homes[1].name = "second_home"
|
mock_homes[1].name = "second_home"
|
||||||
return mock_homes
|
return mock_homes
|
||||||
|
|
||||||
|
|
||||||
def create_mock_tibber_connection():
|
@pytest.mark.parametrize(
|
||||||
"""Create a mock tibber connection."""
|
"data",
|
||||||
tibber_connection = MagicMock()
|
[
|
||||||
tibber_connection.get_homes.return_value = generate_mock_home_data()
|
{},
|
||||||
return tibber_connection
|
{"start": START_TIME.isoformat()},
|
||||||
|
{
|
||||||
|
"start": START_TIME.isoformat(),
|
||||||
def create_mock_hass():
|
"end": (START_TIME + dt.timedelta(days=1)).isoformat(),
|
||||||
"""Create a mock hass object."""
|
},
|
||||||
mock_hass = MagicMock
|
],
|
||||||
mock_hass.data = {"tibber": create_mock_tibber_connection()}
|
)
|
||||||
return mock_hass
|
|
||||||
|
|
||||||
|
|
||||||
async def test_get_prices(
|
async def test_get_prices(
|
||||||
|
mock_tibber_setup: MagicMock,
|
||||||
|
hass: HomeAssistant,
|
||||||
freezer: FrozenDateTimeFactory,
|
freezer: FrozenDateTimeFactory,
|
||||||
|
data,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test __get_prices with mock data."""
|
"""Test get_prices with mock data."""
|
||||||
freezer.move_to(STARTTIME)
|
freezer.move_to(START_TIME)
|
||||||
tomorrow = STARTTIME + dt.timedelta(days=1)
|
mock_tibber_setup.get_homes.return_value = generate_mock_home_data()
|
||||||
call = ServiceCall(
|
|
||||||
DOMAIN,
|
result = await hass.services.async_call(
|
||||||
PRICE_SERVICE_NAME,
|
DOMAIN, PRICE_SERVICE_NAME, data, blocking=True, return_response=True
|
||||||
{"start": STARTTIME.date().isoformat(), "end": tomorrow.date().isoformat()},
|
|
||||||
)
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
result = await __get_prices(call, hass=create_mock_hass())
|
|
||||||
|
|
||||||
assert result == {
|
assert result == {
|
||||||
"prices": {
|
"prices": {
|
||||||
"first_home": [
|
"first_home": [
|
||||||
{
|
{
|
||||||
"start_time": STARTTIME,
|
"start_time": dt.datetime.fromisoformat(START_TIME.isoformat()),
|
||||||
"price": 0.46914,
|
# back and forth conversion to deal with HAFakeDatetime vs real datetime being different types
|
||||||
|
"price": 0.36914,
|
||||||
"level": "VERY_EXPENSIVE",
|
"level": "VERY_EXPENSIVE",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"start_time": STARTTIME + dt.timedelta(hours=1),
|
"start_time": dt.datetime.fromisoformat(
|
||||||
"price": 0.46914,
|
(START_TIME + dt.timedelta(hours=1)).isoformat()
|
||||||
|
),
|
||||||
|
"price": 0.36914,
|
||||||
"level": "VERY_EXPENSIVE",
|
"level": "VERY_EXPENSIVE",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"second_home": [
|
"second_home": [
|
||||||
{
|
{
|
||||||
"start_time": STARTTIME,
|
"start_time": dt.datetime.fromisoformat(START_TIME.isoformat()),
|
||||||
"price": 0.46914,
|
"price": 0.36914,
|
||||||
"level": "VERY_EXPENSIVE",
|
"level": "VERY_EXPENSIVE",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"start_time": STARTTIME + dt.timedelta(hours=1),
|
"start_time": dt.datetime.fromisoformat(
|
||||||
"price": 0.46914,
|
(START_TIME + dt.timedelta(hours=1)).isoformat()
|
||||||
"level": "VERY_EXPENSIVE",
|
),
|
||||||
},
|
"price": 0.36914,
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_get_prices_no_input(
|
|
||||||
freezer: FrozenDateTimeFactory,
|
|
||||||
) -> None:
|
|
||||||
"""Test __get_prices with no input."""
|
|
||||||
freezer.move_to(STARTTIME)
|
|
||||||
call = ServiceCall(DOMAIN, PRICE_SERVICE_NAME, {})
|
|
||||||
|
|
||||||
result = await __get_prices(call, hass=create_mock_hass())
|
|
||||||
|
|
||||||
assert result == {
|
|
||||||
"prices": {
|
|
||||||
"first_home": [
|
|
||||||
{
|
|
||||||
"start_time": STARTTIME,
|
|
||||||
"price": 0.46914,
|
|
||||||
"level": "VERY_EXPENSIVE",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"start_time": STARTTIME + dt.timedelta(hours=1),
|
|
||||||
"price": 0.46914,
|
|
||||||
"level": "VERY_EXPENSIVE",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"second_home": [
|
|
||||||
{
|
|
||||||
"start_time": STARTTIME,
|
|
||||||
"price": 0.46914,
|
|
||||||
"level": "VERY_EXPENSIVE",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"start_time": STARTTIME + dt.timedelta(hours=1),
|
|
||||||
"price": 0.46914,
|
|
||||||
"level": "VERY_EXPENSIVE",
|
"level": "VERY_EXPENSIVE",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -208,16 +170,24 @@ async def test_get_prices_no_input(
|
|||||||
|
|
||||||
|
|
||||||
async def test_get_prices_start_tomorrow(
|
async def test_get_prices_start_tomorrow(
|
||||||
|
mock_tibber_setup: MagicMock,
|
||||||
|
hass: HomeAssistant,
|
||||||
freezer: FrozenDateTimeFactory,
|
freezer: FrozenDateTimeFactory,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test __get_prices with start date tomorrow."""
|
"""Test get_prices with start date tomorrow."""
|
||||||
freezer.move_to(STARTTIME)
|
freezer.move_to(START_TIME)
|
||||||
tomorrow = STARTTIME + dt.timedelta(days=1)
|
tomorrow = START_TIME + dt.timedelta(days=1)
|
||||||
call = ServiceCall(
|
|
||||||
DOMAIN, PRICE_SERVICE_NAME, {"start": tomorrow.date().isoformat()}
|
|
||||||
)
|
|
||||||
|
|
||||||
result = await __get_prices(call, hass=create_mock_hass())
|
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 == {
|
assert result == {
|
||||||
"prices": {
|
"prices": {
|
||||||
@ -228,7 +198,7 @@ async def test_get_prices_start_tomorrow(
|
|||||||
"level": "VERY_EXPENSIVE",
|
"level": "VERY_EXPENSIVE",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"start_time": tomorrow + dt.timedelta(hours=1),
|
"start_time": (tomorrow + dt.timedelta(hours=1)),
|
||||||
"price": 0.46914,
|
"price": 0.46914,
|
||||||
"level": "VERY_EXPENSIVE",
|
"level": "VERY_EXPENSIVE",
|
||||||
},
|
},
|
||||||
@ -240,7 +210,7 @@ async def test_get_prices_start_tomorrow(
|
|||||||
"level": "VERY_EXPENSIVE",
|
"level": "VERY_EXPENSIVE",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"start_time": tomorrow + dt.timedelta(hours=1),
|
"start_time": (tomorrow + dt.timedelta(hours=1)),
|
||||||
"price": 0.46914,
|
"price": 0.46914,
|
||||||
"level": "VERY_EXPENSIVE",
|
"level": "VERY_EXPENSIVE",
|
||||||
},
|
},
|
||||||
@ -252,46 +222,55 @@ async def test_get_prices_start_tomorrow(
|
|||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"start_time",
|
"start_time",
|
||||||
[
|
[
|
||||||
STARTTIME.isoformat(),
|
START_TIME.isoformat(),
|
||||||
STARTTIME.replace(tzinfo=None).isoformat(),
|
(START_TIME + dt.timedelta(hours=4))
|
||||||
(STARTTIME + dt.timedelta(hours=4))
|
|
||||||
.replace(tzinfo=dt.timezone(dt.timedelta(hours=4)))
|
.replace(tzinfo=dt.timezone(dt.timedelta(hours=4)))
|
||||||
.isoformat(),
|
.isoformat(),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_get_prices_with_timezones(
|
async def test_get_prices_with_timezones(
|
||||||
|
mock_tibber_setup: MagicMock,
|
||||||
|
hass: HomeAssistant,
|
||||||
freezer: FrozenDateTimeFactory,
|
freezer: FrozenDateTimeFactory,
|
||||||
start_time: str,
|
start_time: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test __get_prices with timezone and without."""
|
"""Test get_prices with timezone and without."""
|
||||||
freezer.move_to(STARTTIME)
|
freezer.move_to(START_TIME)
|
||||||
call = ServiceCall(DOMAIN, PRICE_SERVICE_NAME, {"start": start_time})
|
|
||||||
|
|
||||||
result = await __get_prices(call, hass=create_mock_hass())
|
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 == {
|
assert result == {
|
||||||
"prices": {
|
"prices": {
|
||||||
"first_home": [
|
"first_home": [
|
||||||
{
|
{
|
||||||
"start_time": STARTTIME,
|
"start_time": START_TIME,
|
||||||
"price": 0.46914,
|
"price": 0.36914,
|
||||||
"level": "VERY_EXPENSIVE",
|
"level": "VERY_EXPENSIVE",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"start_time": STARTTIME + dt.timedelta(hours=1),
|
"start_time": START_TIME + dt.timedelta(hours=1),
|
||||||
"price": 0.46914,
|
"price": 0.36914,
|
||||||
"level": "VERY_EXPENSIVE",
|
"level": "VERY_EXPENSIVE",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"second_home": [
|
"second_home": [
|
||||||
{
|
{
|
||||||
"start_time": STARTTIME,
|
"start_time": START_TIME,
|
||||||
"price": 0.46914,
|
"price": 0.36914,
|
||||||
"level": "VERY_EXPENSIVE",
|
"level": "VERY_EXPENSIVE",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"start_time": STARTTIME + dt.timedelta(hours=1),
|
"start_time": START_TIME + dt.timedelta(hours=1),
|
||||||
"price": 0.46914,
|
"price": 0.36914,
|
||||||
"level": "VERY_EXPENSIVE",
|
"level": "VERY_EXPENSIVE",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -302,29 +281,53 @@ async def test_get_prices_with_timezones(
|
|||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"start_time",
|
"start_time",
|
||||||
[
|
[
|
||||||
(STARTTIME + dt.timedelta(hours=4)).isoformat(),
|
(START_TIME + dt.timedelta(hours=2)).isoformat(),
|
||||||
(STARTTIME + dt.timedelta(hours=4)).replace(tzinfo=None).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(
|
async def test_get_prices_with_wrong_timezones(
|
||||||
|
mock_tibber_setup: MagicMock,
|
||||||
|
hass: HomeAssistant,
|
||||||
freezer: FrozenDateTimeFactory,
|
freezer: FrozenDateTimeFactory,
|
||||||
start_time: str,
|
start_time: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test __get_prices with timezone and without, while expecting it to fail."""
|
"""Test get_prices with incorrect time and/or timezone. We expect an empty list."""
|
||||||
freezer.move_to(STARTTIME)
|
freezer.move_to(START_TIME)
|
||||||
call = ServiceCall(DOMAIN, PRICE_SERVICE_NAME, {"start": 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()
|
||||||
|
|
||||||
result = await __get_prices(call, hass=create_mock_hass())
|
|
||||||
assert result == {"prices": {"first_home": [], "second_home": []}}
|
assert result == {"prices": {"first_home": [], "second_home": []}}
|
||||||
|
|
||||||
|
|
||||||
async def test_get_prices_invalid_input() -> None:
|
async def test_get_prices_invalid_input(
|
||||||
"""Test __get_prices with invalid input."""
|
mock_tibber_setup: MagicMock,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
) -> None:
|
||||||
|
"""Test get_prices with invalid input."""
|
||||||
|
|
||||||
call = ServiceCall(DOMAIN, PRICE_SERVICE_NAME, {"start": "test"})
|
with pytest.raises(ServiceValidationError):
|
||||||
task = asyncio.create_task(__get_prices(call, hass=create_mock_hass()))
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
with pytest.raises(ServiceValidationError) as excinfo:
|
PRICE_SERVICE_NAME,
|
||||||
await task
|
{"start": "test"},
|
||||||
|
blocking=True,
|
||||||
assert "Invalid datetime provided." in str(excinfo.value)
|
return_response=True,
|
||||||
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user