Skip to content

Commit

Permalink
Somre reorg
Browse files Browse the repository at this point in the history
  • Loading branch information
pnbruckner committed Mar 16, 2024
1 parent cf0f3e5 commit f803c54
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 115 deletions.
8 changes: 8 additions & 0 deletions tests/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""Sun2 test common functions, etc."""
from __future__ import annotations

from collections.abc import Callable
from datetime import datetime, tzinfo
from unittest.mock import MagicMock

DtNowMock = tuple[Callable[[tzinfo | None], datetime], MagicMock]
13 changes: 12 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
"""Sun2 test configuration."""
from __future__ import annotations

from collections.abc import AsyncGenerator
from collections.abc import AsyncGenerator, Generator
import logging
from unittest.mock import patch

from custom_components.sun2.const import DOMAIN
import pytest
from pytest import FixtureRequest, LogCaptureFixture

from homeassistant.const import MAJOR_VERSION, MINOR_VERSION
from homeassistant.core import HomeAssistant
from homeassistant.util import dt as dt_util
from tests.common import DtNowMock

pytest_plugins = ["pytest_homeassistant_custom_component"]

Expand Down Expand Up @@ -71,6 +74,14 @@ async def test_abc() -> None:
await hass.config_entries.async_unload(entry.entry_id)


@pytest.fixture
def dt_now() -> Generator[DtNowMock, None, None]:
"""Mock util.dt.now."""
real = dt_util.now
with patch("homeassistant.util.dt.now") as mock:
yield real, mock


# @pytest.fixture
# def mock_setup_and_unload() -> Generator[tuple[AsyncMock, AsyncMock], None, None]:
# """Mock async_setup_entry & async_unload_entry."""
Expand Down
58 changes: 36 additions & 22 deletions tests/test_binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from __future__ import annotations

from datetime import date, datetime, time, timedelta
from unittest.mock import patch

from astral import LocationInfo
from astral.location import Location
Expand All @@ -19,9 +18,15 @@
from homeassistant.helpers.entity_registry import EntityRegistry
from homeassistant.setup import async_setup_component
from homeassistant.util import dt as dt_util, slugify
from tests.common import DtNowMock

from .const import NY_CONFIG

# ========== Fixtures ==================================================================


# ========== Tests =====================================================================


@pytest.mark.parametrize(
"elevation,name,slug",
Expand All @@ -35,11 +40,14 @@
async def test_yaml_binary_sensor(
hass: HomeAssistant,
entity_registry: EntityRegistry,
dt_now: DtNowMock,
elevation: str | float,
name: str | None,
slug: str,
) -> None:
"""Test YAML configured elevation binary sensor."""
dt_now_real, dt_now_mock = dt_now

config = NY_CONFIG | {
"binary_sensors": [
{
Expand All @@ -52,15 +60,15 @@ async def test_yaml_binary_sensor(
config["binary_sensors"][0]["name"] = name

tz = dt_util.get_time_zone(NY_CONFIG["time_zone"])
base_time = dt_util.now(tz)
base_time = dt_now_real(tz)

# Set time to 00:00:00 tommorow.
now = datetime.combine((base_time + timedelta(1)).date(), time()).replace(tzinfo=tz)

with patch("homeassistant.util.dt.now", return_value=now):
with assert_setup_component(1, DOMAIN):
await async_setup_component(hass, DOMAIN, {DOMAIN: [config]})
await hass.async_block_till_done()
dt_now_mock.return_value = now
with assert_setup_component(1, DOMAIN):
await async_setup_component(hass, DOMAIN, {DOMAIN: [config]})
await hass.async_block_till_done()

config_entry = hass.config_entries.async_entries(DOMAIN)[0]

Expand All @@ -83,9 +91,9 @@ async def test_yaml_binary_sensor(
assert now < next_change < noon

# Move time to next_change and make sure state has changed.
with patch("homeassistant.util.dt.now", return_value=next_change):
async_fire_time_changed(hass, next_change)
await hass.async_block_till_done()
dt_now_mock.return_value = next_change
async_fire_time_changed(hass, next_change)
await hass.async_block_till_done()
state = hass.states.get(entity_id)
assert state
assert state.state == STATE_ON
Expand All @@ -105,11 +113,14 @@ async def test_yaml_binary_sensor(
async def test_always_on_or_off(
hass: HomeAssistant,
entity_registry: EntityRegistry,
dt_now: DtNowMock,
caplog: LogCaptureFixture,
elevation: float,
expected_state: str,
) -> None:
"""Test a binary sensor that is always on or off."""
dt_now_real, dt_now_mock = dt_now

config = NY_CONFIG | {
"binary_sensors": [
{
Expand All @@ -120,15 +131,15 @@ async def test_always_on_or_off(
}

tz = dt_util.get_time_zone(NY_CONFIG["time_zone"])
base_time = dt_util.now(tz)
base_time = dt_now_real(tz)

# Set time to 00:00:00 tommorow.
now = datetime.combine((base_time + timedelta(1)).date(), time()).replace(tzinfo=tz)

with patch("homeassistant.util.dt.now", return_value=now):
with assert_setup_component(1, DOMAIN):
await async_setup_component(hass, DOMAIN, {DOMAIN: [config]})
await hass.async_block_till_done()
dt_now_mock.return_value = now
with assert_setup_component(1, DOMAIN):
await async_setup_component(hass, DOMAIN, {DOMAIN: [config]})
await hass.async_block_till_done()

config_entry = hass.config_entries.async_entries(DOMAIN)[0]
entity_id = entity_registry.async_get_entity_id(
Expand All @@ -150,9 +161,9 @@ async def test_always_on_or_off(

# Move time to noon.
noon = now.replace(hour=12)
with patch("homeassistant.util.dt.now", return_value=noon):
async_fire_time_changed(hass, noon)
await hass.async_block_till_done()
dt_now_mock.return_value = noon
async_fire_time_changed(hass, noon)
await hass.async_block_till_done()

# Check that state is still the same.
state = hass.states.get(entity_id)
Expand All @@ -167,12 +178,15 @@ async def test_always_on_or_off(
async def test_next_change_greater_than_one_day(
hass: HomeAssistant,
entity_registry: EntityRegistry,
dt_now: DtNowMock,
caplog: LogCaptureFixture,
func: str,
offset: int,
expected_state: str,
) -> None:
"""Test when binary sensor won't change for more than one day."""
dt_now_real, dt_now_mock = dt_now

config = NY_CONFIG | {
"binary_sensors": [
{
Expand All @@ -190,7 +204,7 @@ async def test_next_change_greater_than_one_day(

# Get next year's September 20, since on this date neither the min nor max elevation
# is near their extremes in New York, NY. Then get the min or max sun elevations.
now_date = date(dt_util.now().year + 1, 9, 20)
now_date = date(dt_now_real(tz).year + 1, 9, 20)
elv = loc.solar_elevation(getattr(loc, func)(now_date), obs_elv)

# Configure sensor with an elevation threshold just below or above.
Expand All @@ -199,10 +213,10 @@ async def test_next_change_greater_than_one_day(
# Set time to midnight on that date.
now = datetime.combine(now_date, time()).replace(tzinfo=tz)

with patch("homeassistant.util.dt.now", return_value=now):
with assert_setup_component(1, DOMAIN):
await async_setup_component(hass, DOMAIN, {DOMAIN: [config]})
await hass.async_block_till_done()
dt_now_mock.return_value = now
with assert_setup_component(1, DOMAIN):
await async_setup_component(hass, DOMAIN, {DOMAIN: [config]})
await hass.async_block_till_done()

config_entry = hass.config_entries.async_entries(DOMAIN)[0]
entity_id = entity_registry.async_get_entity_id(
Expand Down
28 changes: 20 additions & 8 deletions tests/test_config_flow.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
"""Test config flow module."""
from __future__ import annotations

from collections.abc import Generator
import logging
from typing import Any
from unittest.mock import patch
from unittest.mock import AsyncMock, patch

from custom_components.sun2.const import DOMAIN
import pytest
Expand All @@ -17,18 +18,25 @@

_LOGGER = logging.getLogger(__name__)

# ========== Fixtures ==================================================================


@pytest.fixture
def reload_mock(hass: HomeAssistant) -> Generator[AsyncMock, None, None]:
"""Mock config_entries.async_reload."""
with patch.object(hass.config_entries, "async_reload") as mock:
yield mock


# ========== Import Flow Tests =========================================================


async def test_import_min_new(hass: HomeAssistant):
async def test_import_min_new(hass: HomeAssistant, reload_mock: AsyncMock):
"""Test minimum YAML config with new location."""

with patch.object(hass.config_entries, "async_reload") as reload_mock:
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_IMPORT}, data=HOME_CONFIG.copy()
)
await hass.async_block_till_done()
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_IMPORT}, data=HOME_CONFIG.copy()
)
await hass.async_block_till_done()

assert result["type"] == FlowResultType.CREATE_ENTRY
config_entry: ConfigEntry = result["result"]
Expand All @@ -37,6 +45,7 @@ async def test_import_min_new(hass: HomeAssistant):
assert config_entry.data == {}
assert config_entry.options == {}
assert config_entry.unique_id == HOME_CONFIG["unique_id"]
reload_mock.assert_not_called
reload_mock.assert_not_awaited


Expand Down Expand Up @@ -71,4 +80,7 @@ async def test_import_min_old(
assert changed ^ (config_entry.as_dict() == old_values)
# When config changes, entry_updated will take care of reloading it. But when config
# does not change, config flow will make sure it gets reloaded.
assert changed ^ (reload_mock.call_count == 1)
assert changed ^ (reload_mock.await_count == 1)
if reload_mock.call_count == 1:
reload_mock.assert_called_with(config_entry.entry_id)
Loading

0 comments on commit f803c54

Please sign in to comment.