Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
245 changes: 120 additions & 125 deletions tests/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

from tests._ha_stubs import (
clear_integration_modules,
force_module,
stub_config_entry_class,
stub_custom_components_packages,
stub_exceptions,
Expand All @@ -24,11 +23,9 @@
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))

clear_integration_modules()
stub_custom_components_packages(root=ROOT)

# Provide the additional stubs required by __init__.
homeassistant_mod = stub_homeassistant_package()
integration = None
const = None
_BaseDataUpdateCoordinator = None


class _StubConfigEntry:
Expand All @@ -37,11 +34,6 @@ def __class_getitem__(cls, _item):
return cls


stub_config_entry_class(_StubConfigEntry)

core_mod = types.ModuleType("homeassistant.core")


class _StubHomeAssistant: # pragma: no cover - structure only
pass

Expand All @@ -50,16 +42,6 @@ class _StubServiceCall: # pragma: no cover - structure only
pass


core_mod.HomeAssistant = _StubHomeAssistant
core_mod.ServiceCall = _StubServiceCall
force_module("homeassistant.core", core_mod)

ha_components_mod = types.ModuleType("homeassistant.components")
force_module("homeassistant.components", ha_components_mod)

sensor_mod = types.ModuleType("homeassistant.components.sensor")


class _StubSensorEntity: # pragma: no cover - structure only
def __init__(self, *args, **kwargs):
self._attr_unique_id = None
Expand All @@ -83,22 +65,6 @@ class _StubSensorStateClass: # pragma: no cover - structure only
MEASUREMENT = "measurement"


sensor_mod.SensorEntity = _StubSensorEntity
sensor_mod.SensorDeviceClass = _StubSensorDeviceClass
sensor_mod.SensorStateClass = _StubSensorStateClass
force_module("homeassistant.components.sensor", sensor_mod)

const_mod = types.ModuleType("homeassistant.const")
const_mod.ATTR_ATTRIBUTION = "Attribution"
force_module("homeassistant.const", const_mod)

aiohttp_client_mod = types.ModuleType("homeassistant.helpers.aiohttp_client")
aiohttp_client_mod.async_get_clientsession = lambda _hass: None
force_module("homeassistant.helpers.aiohttp_client", aiohttp_client_mod)

aiohttp_mod = types.ModuleType("aiohttp")


class _StubClientError(Exception):
pass

Expand All @@ -112,51 +78,45 @@ def __init__(self, total: float | None = None):
self.total = total


aiohttp_mod.ClientError = _StubClientError
aiohttp_mod.ClientSession = _StubClientSession
aiohttp_mod.ClientTimeout = _StubClientTimeout
aiohttp_mod.ContentTypeError = ValueError
force_module("aiohttp", aiohttp_mod)

cv_mod = types.ModuleType("homeassistant.helpers.config_validation")
cv_mod.config_entry_only_config_schema = lambda _domain: lambda config: config
force_module("homeassistant.helpers.config_validation", cv_mod)

vol_mod = types.ModuleType("voluptuous")
force_module("voluptuous", vol_mod)
if not hasattr(vol_mod, "Schema"):
vol_mod.Schema = lambda *args, **kwargs: None
class _StubEntityCategory:
DIAGNOSTIC = "diagnostic"

helpers_mod = types.ModuleType("homeassistant.helpers")
force_module("homeassistant.helpers", helpers_mod)

entity_registry_mod = types.ModuleType("homeassistant.helpers.entity_registry")
class _StubConfigEntryNotReady(Exception):
pass


def _stub_async_get(_hass): # pragma: no cover - structure only
class _Registry:
@staticmethod
def async_entries_for_config_entry(_registry, _entry_id):
return []
class _StubConfigEntryAuthFailed(Exception):
pass

return _Registry()

class _StubUpdateFailed(Exception):
pass

entity_registry_mod.async_get = _stub_async_get
entity_registry_mod.async_entries_for_config_entry = lambda *args, **kwargs: []
force_module("homeassistant.helpers.entity_registry", entity_registry_mod)

entity_mod = types.ModuleType("homeassistant.helpers.entity")
class _StubCoordinatorEntity:
def __init__(self, coordinator):
self.coordinator = coordinator


class _StubEntityCategory:
DIAGNOSTIC = "diagnostic"
class _StubDataUpdateCoordinator:
def __init__(self, hass, logger, *, name: str, update_interval):
self.hass = hass
self.logger = logger
self.name = name
self.update_interval = update_interval
self.data = {"date": {}, "region": {}}
self.last_updated = None

async def async_config_entry_first_refresh(self):
self.last_updated = "now"
return None

entity_mod.EntityCategory = _StubEntityCategory
force_module("homeassistant.helpers.entity", entity_mod)
async def async_refresh(self):
return None

dt_mod = types.ModuleType("homeassistant.util.dt")
def async_request_refresh(self): # pragma: no cover - scheduling helper
return asyncio.create_task(self.async_refresh())


def _stub_utcnow():
Expand All @@ -165,9 +125,6 @@ def _stub_utcnow():
return datetime.now(UTC)


dt_mod.utcnow = _stub_utcnow


def _stub_parse_http_date(value: str | None): # pragma: no cover - stub only
from datetime import UTC, datetime
from email.utils import parsedate_to_datetime
Expand All @@ -189,66 +146,104 @@ def _stub_parse_http_date(value: str | None): # pragma: no cover - stub only
return None


dt_mod.parse_http_date = _stub_parse_http_date
force_module("homeassistant.util.dt", dt_mod)

util_mod = types.ModuleType("homeassistant.util")
util_mod.dt = dt_mod
force_module("homeassistant.util", util_mod)


class _StubConfigEntryNotReady(Exception):
pass


class _StubConfigEntryAuthFailed(Exception):
pass


class _StubUpdateFailed(Exception):
pass


class _StubCoordinatorEntity:
def __init__(self, coordinator):
self.coordinator = coordinator

def _stub_async_get(_hass): # pragma: no cover - structure only
class _Registry:
@staticmethod
def async_entries_for_config_entry(_registry, _entry_id):
return []

class _StubDataUpdateCoordinator:
def __init__(self, hass, logger, *, name: str, update_interval):
self.hass = hass
self.logger = logger
self.name = name
self.update_interval = update_interval
self.data = {"date": {}, "region": {}}
self.last_updated = None
return _Registry()

async def async_config_entry_first_refresh(self):
self.last_updated = "now"
return None

async def async_refresh(self):
return None
@pytest.fixture
def stub_init_ha_modules(monkeypatch: pytest.MonkeyPatch) -> None:
"""Install only the Home Assistant stubs needed by ``__init__`` imports."""
clear_integration_modules(monkeypatch=monkeypatch)
stub_custom_components_packages(root=ROOT, monkeypatch=monkeypatch)
Comment thread
eXPerience83 marked this conversation as resolved.
stub_homeassistant_package(monkeypatch=monkeypatch)
stub_config_entry_class(_StubConfigEntry, monkeypatch=monkeypatch)
core_mod = types.ModuleType("homeassistant.core")
core_mod.HomeAssistant = _StubHomeAssistant
core_mod.ServiceCall = _StubServiceCall
monkeypatch.setitem(sys.modules, "homeassistant.core", core_mod)

ha_components_mod = types.ModuleType("homeassistant.components")
monkeypatch.setitem(sys.modules, "homeassistant.components", ha_components_mod)

sensor_mod = types.ModuleType("homeassistant.components.sensor")
sensor_mod.SensorEntity = _StubSensorEntity
sensor_mod.SensorDeviceClass = _StubSensorDeviceClass
sensor_mod.SensorStateClass = _StubSensorStateClass
monkeypatch.setitem(sys.modules, "homeassistant.components.sensor", sensor_mod)

const_mod = types.ModuleType("homeassistant.const")
const_mod.ATTR_ATTRIBUTION = "Attribution"
monkeypatch.setitem(sys.modules, "homeassistant.const", const_mod)

aiohttp_client_mod = types.ModuleType("homeassistant.helpers.aiohttp_client")
aiohttp_client_mod.async_get_clientsession = lambda _hass: None
monkeypatch.setitem(
sys.modules, "homeassistant.helpers.aiohttp_client", aiohttp_client_mod
)
aiohttp_mod = types.ModuleType("aiohttp")
aiohttp_mod.ClientError = _StubClientError
aiohttp_mod.ClientSession = _StubClientSession
aiohttp_mod.ClientTimeout = _StubClientTimeout
aiohttp_mod.ContentTypeError = ValueError
monkeypatch.setitem(sys.modules, "aiohttp", aiohttp_mod)

cv_mod = types.ModuleType("homeassistant.helpers.config_validation")
cv_mod.config_entry_only_config_schema = lambda _domain: lambda config: config
monkeypatch.setitem(sys.modules, "homeassistant.helpers.config_validation", cv_mod)

vol_mod = types.ModuleType("voluptuous")
monkeypatch.setitem(sys.modules, "voluptuous", vol_mod)
vol_mod.Schema = lambda *args, **kwargs: None

def async_request_refresh(self): # pragma: no cover - scheduling helper
return asyncio.create_task(self.async_refresh())
helpers_mod = types.ModuleType("homeassistant.helpers")
monkeypatch.setitem(sys.modules, "homeassistant.helpers", helpers_mod)
entity_registry_mod = types.ModuleType("homeassistant.helpers.entity_registry")
entity_registry_mod.async_get = _stub_async_get
entity_registry_mod.async_entries_for_config_entry = lambda *args, **kwargs: []
monkeypatch.setitem(
sys.modules, "homeassistant.helpers.entity_registry", entity_registry_mod
)

entity_mod = types.ModuleType("homeassistant.helpers.entity")
entity_mod.EntityCategory = _StubEntityCategory
monkeypatch.setitem(sys.modules, "homeassistant.helpers.entity", entity_mod)
dt_mod = types.ModuleType("homeassistant.util.dt")
dt_mod.utcnow = _stub_utcnow
dt_mod.parse_http_date = _stub_parse_http_date
monkeypatch.setitem(sys.modules, "homeassistant.util.dt", dt_mod)

util_mod = types.ModuleType("homeassistant.util")
util_mod.dt = dt_mod
monkeypatch.setitem(sys.modules, "homeassistant.util", util_mod)
stub_exceptions(
ConfigEntryNotReady=_StubConfigEntryNotReady,
ConfigEntryAuthFailed=_StubConfigEntryAuthFailed,
monkeypatch=monkeypatch,
)
stub_update_coordinator_module(
update_failed=_StubUpdateFailed,
data_update_coordinator=_StubDataUpdateCoordinator,
coordinator_entity=_StubCoordinatorEntity,
monkeypatch=monkeypatch,
)

stub_exceptions(
ConfigEntryNotReady=_StubConfigEntryNotReady,
ConfigEntryAuthFailed=_StubConfigEntryAuthFailed,
)
stub_update_coordinator_module(
update_failed=_StubUpdateFailed,
data_update_coordinator=_StubDataUpdateCoordinator,
coordinator_entity=_StubCoordinatorEntity,
)
_BaseDataUpdateCoordinator = _StubDataUpdateCoordinator

integration = importlib.import_module(
"custom_components.pollenlevels.__init__"
) # noqa: E402
const = importlib.import_module("custom_components.pollenlevels.const") # noqa: E402
@pytest.fixture(autouse=True)
def integration_modules(monkeypatch: pytest.MonkeyPatch, stub_init_ha_modules: None):
"""Import integration modules only after stubs are installed."""
global integration, const, _BaseDataUpdateCoordinator
# Remove the package stub installed by stub_custom_components_packages so
# importing custom_components.pollenlevels executes the real __init__.py.
clear_integration_modules(monkeypatch=monkeypatch)
const = importlib.import_module("custom_components.pollenlevels.const")
Comment thread
eXPerience83 marked this conversation as resolved.
integration = importlib.import_module("custom_components.pollenlevels")
_BaseDataUpdateCoordinator = _StubDataUpdateCoordinator
return integration, const
Comment thread
eXPerience83 marked this conversation as resolved.


class _FakeConfigEntries:
Expand Down
31 changes: 16 additions & 15 deletions tests/test_options_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,24 @@

import pytest

from custom_components.pollenlevels.const import (
CONF_API_KEY,
CONF_CREATE_FORECAST_SENSORS,
CONF_FORECAST_DAYS,
CONF_LANGUAGE_CODE,
CONF_LATITUDE,
CONF_LONGITUDE,
CONF_UPDATE_INTERVAL,
DEFAULT_FORECAST_DAYS,
DEFAULT_UPDATE_INTERVAL,
FORECAST_SENSORS_CHOICES,
MAX_FORECAST_DAYS,
MAX_UPDATE_INTERVAL_HOURS,
MIN_FORECAST_DAYS,
)
# Importing test_config_flow first installs the Home Assistant stubs needed for
# importing integration modules during collection.
from tests import test_config_flow as base
Comment thread
eXPerience83 marked this conversation as resolved.

CONF_API_KEY = base.cf.CONF_API_KEY
CONF_CREATE_FORECAST_SENSORS = base.cf.CONF_CREATE_FORECAST_SENSORS
CONF_FORECAST_DAYS = base.cf.CONF_FORECAST_DAYS
CONF_LANGUAGE_CODE = base.cf.CONF_LANGUAGE_CODE
CONF_LATITUDE = base.cf.CONF_LATITUDE
CONF_LONGITUDE = base.cf.CONF_LONGITUDE
CONF_UPDATE_INTERVAL = base.cf.CONF_UPDATE_INTERVAL
DEFAULT_FORECAST_DAYS = base.cf.DEFAULT_FORECAST_DAYS
DEFAULT_UPDATE_INTERVAL = base.cf.DEFAULT_UPDATE_INTERVAL
FORECAST_SENSORS_CHOICES = base.cf.FORECAST_SENSORS_CHOICES
MAX_FORECAST_DAYS = base.cf.MAX_FORECAST_DAYS
MAX_UPDATE_INTERVAL_HOURS = base.cf.MAX_UPDATE_INTERVAL_HOURS
MIN_FORECAST_DAYS = base.cf.MIN_FORECAST_DAYS

PollenLevelsOptionsFlow = base.cf.PollenLevelsOptionsFlow
_StubConfigEntry = base._StubConfigEntry

Expand Down
Loading