adaptive-lighting/tests/test_init.py
Adam DeMuri 623dd65aef
Register service actions in async_setup for Bronze tier compliance (#1403)
* Register service actions in async_setup for Bronze tier compliance

- Move 'apply' and 'set_manual_control' service registration from async_setup_entry to async_setup.
- Move service handlers to module-level functions in switch.py.
- Update apply_service_schema to support dynamic defaults for transition duration.
- Clean up related unused imports and fix Python 3.10 syntax compatibility.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Clean up and add tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix lint errors

* Automated update of generated docs

* Re-add types

* Make transition not required again

* fix: validate global service targets

* fix: document optional apply transition

* fix: derive service docs from schema markers

* docs: clarify service target options

* fix: preserve entity service target handling

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Bas Nijholt <bas@nijho.lt>
2026-09-06 13:04:18 +02:00

187 lines
6.2 KiB
Python

"""Tests for Adaptive Lighting integration."""
import pytest
import voluptuous.error
from homeassistant.components import adaptive_lighting
from homeassistant.components.adaptive_lighting.const import (
CONF_LIGHTS,
DEFAULT_NAME,
SERVICE_APPLY,
SERVICE_CHANGE_SWITCH_SETTINGS,
SERVICE_SET_MANUAL_CONTROL,
UNDO_UPDATE_LISTENER,
)
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import ATTR_ENTITY_ID, CONF_NAME
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import service
from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry
async def test_setup_with_config(hass):
"""Test that we import the config and setup the integration."""
config = {
adaptive_lighting.DOMAIN: {
adaptive_lighting.CONF_NAME: DEFAULT_NAME,
},
}
assert await async_setup_component(hass, adaptive_lighting.DOMAIN, config)
assert adaptive_lighting.DOMAIN in hass.data
async def test_successful_config_entry(hass):
"""Test that Adaptive Lighting is configured successfully."""
entry = MockConfigEntry(
domain=adaptive_lighting.DOMAIN,
data={CONF_NAME: DEFAULT_NAME},
)
entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(entry.entry_id)
assert entry.state == ConfigEntryState.LOADED
assert UNDO_UPDATE_LISTENER in hass.data[adaptive_lighting.DOMAIN][entry.entry_id]
async def test_unload_entry(hass):
"""Test removing Adaptive Lighting."""
entry = MockConfigEntry(
domain=adaptive_lighting.DOMAIN,
data={CONF_NAME: DEFAULT_NAME},
)
entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(entry.entry_id)
assert await hass.config_entries.async_unload(entry.entry_id)
await hass.async_block_till_done()
assert entry.state == ConfigEntryState.NOT_LOADED
assert adaptive_lighting.DOMAIN not in hass.data
async def test_services_survive_entry_unload_and_reload(hass):
"""Test integration services remain registered across entry lifecycle."""
assert await async_setup_component(hass, adaptive_lighting.DOMAIN, {})
service_names = (
SERVICE_APPLY,
SERVICE_CHANGE_SWITCH_SETTINGS,
SERVICE_SET_MANUAL_CONTROL,
)
services = hass.services.async_services()[adaptive_lighting.DOMAIN]
assert SERVICE_APPLY in services
assert SERVICE_SET_MANUAL_CONTROL in services
if hasattr(service, "async_register_platform_entity_service"):
assert SERVICE_CHANGE_SWITCH_SETTINGS in services
else:
assert SERVICE_CHANGE_SWITCH_SETTINGS not in services
entry = MockConfigEntry(
domain=adaptive_lighting.DOMAIN,
data={CONF_NAME: DEFAULT_NAME},
)
entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(entry.entry_id)
registered = {
name: hass.services.async_services()[adaptive_lighting.DOMAIN][name]
for name in service_names
}
switch = hass.data[adaptive_lighting.DOMAIN][entry.entry_id][SWITCH_DOMAIN]
assert await hass.config_entries.async_unload(entry.entry_id)
for name in service_names:
assert (
hass.services.async_services()[adaptive_lighting.DOMAIN][name]
is registered[name]
)
with pytest.raises(ServiceValidationError, match="No Adaptive Lighting"):
await hass.services.async_call(
adaptive_lighting.DOMAIN,
SERVICE_APPLY,
{ATTR_ENTITY_ID: switch.entity_id},
blocking=True,
)
assert await hass.config_entries.async_setup(entry.entry_id)
for name in service_names:
assert (
hass.services.async_services()[adaptive_lighting.DOMAIN][name]
is registered[name]
)
await hass.services.async_call(
adaptive_lighting.DOMAIN,
SERVICE_CHANGE_SWITCH_SETTINGS,
{ATTR_ENTITY_ID: switch.entity_id},
blocking=True,
)
async def test_service_call_without_loaded_entry(hass):
"""Test global services reject calls when no profile is loaded."""
assert await async_setup_component(hass, adaptive_lighting.DOMAIN, {})
with pytest.raises(ServiceValidationError, match="No Adaptive Lighting"):
await hass.services.async_call(
adaptive_lighting.DOMAIN,
SERVICE_APPLY,
{CONF_LIGHTS: ["light.test"]},
blocking=True,
)
pending_entry = MockConfigEntry(
domain=adaptive_lighting.DOMAIN,
data={CONF_NAME: "pending"},
)
pending_entry.add_to_hass(hass)
hass.data[adaptive_lighting.DOMAIN] = {pending_entry.entry_id: {}}
with pytest.raises(ServiceValidationError, match="not found in any switch"):
await hass.services.async_call(
adaptive_lighting.DOMAIN,
SERVICE_APPLY,
{CONF_LIGHTS: ["light.test"]},
blocking=True,
)
async def test_apply_rejects_unknown_light(hass):
"""Test the apply service rejects an unknown light target."""
entry = MockConfigEntry(
domain=adaptive_lighting.DOMAIN,
data={CONF_NAME: DEFAULT_NAME},
)
entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(entry.entry_id)
with pytest.raises(ServiceValidationError, match="not found in any switch"):
await hass.services.async_call(
adaptive_lighting.DOMAIN,
SERVICE_APPLY,
{CONF_LIGHTS: ["light.does_not_exist"]},
blocking=True,
)
async def test_change_switch_settings_requires_entity_target(hass):
"""Test change_switch_settings rejects a missing entity target."""
entry = MockConfigEntry(
domain=adaptive_lighting.DOMAIN,
data={CONF_NAME: DEFAULT_NAME},
)
entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(entry.entry_id)
with pytest.raises(
voluptuous.error.MultipleInvalid,
match=r"must contain at least one of entity_id.*area_id",
):
await hass.services.async_call(
adaptive_lighting.DOMAIN,
SERVICE_CHANGE_SWITCH_SETTINGS,
{},
blocking=True,
)