Optional weather source fields used the current selection as the form default. A cleared field that the frontend omits was refilled with the old sources by voluptuous, and an options key missing from the submission fell back to the original setup data. Optional providers could therefore not be removed in the UI. Pre-fill with suggested values instead, and save omitted optional lists as empty so they override older entry data. No UI strings changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
92 lines
5.3 KiB
Python
92 lines
5.3 KiB
Python
"""Validate actual HA form schemas, options and entry migration."""
|
|
from homeassistant.config_entries import SOURCE_USER
|
|
from homeassistant.data_entry_flow import FlowResultType
|
|
from pytest_homeassistant_custom_component.common import MockConfigEntry
|
|
|
|
from custom_components.heating_mpc.const import DOMAIN
|
|
from custom_components.heating_mpc.weather_sources import sources_for
|
|
|
|
|
|
async def test_config_flow(hass, inputs):
|
|
result = await hass.config_entries.flow.async_init(DOMAIN, context={"source": SOURCE_USER})
|
|
assert result["type"] is FlowResultType.FORM
|
|
main = {"climate_entity": "climate.reference", "heater_entity": "switch.boiler",
|
|
"room_entities": ["sensor.room"]}
|
|
validated = result["data_schema"](main)
|
|
assert validated["room_max_age_minutes"] == 30
|
|
assert validated["weather_max_age_minutes"] == 120
|
|
result = await hass.config_entries.flow.async_configure(result["flow_id"], validated)
|
|
assert result["step_id"] == "weather"
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"], result["data_schema"]({"outside_temperature_sources": ["sensor.outside"]}))
|
|
assert result["step_id"] == "weather_extra"
|
|
result = await hass.config_entries.flow.async_configure(result["flow_id"], result["data_schema"]({}))
|
|
assert result["step_id"] == "weights"
|
|
result = await hass.config_entries.flow.async_configure(result["flow_id"], {"weight__sensor.room": 2})
|
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
|
assert result["data"]["weights"] == {"sensor.room": 2}
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
async def test_options_flow_keeps_settings(hass, entry, inputs):
|
|
entry.add_to_hass(hass)
|
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
result = await hass.config_entries.options.async_init(entry.entry_id)
|
|
main = result["data_schema"]({"climate_entity": "climate.reference",
|
|
"heater_entity": "switch.boiler", "room_entities": ["sensor.room"],
|
|
"room_max_age_minutes": 45})
|
|
result = await hass.config_entries.options.async_configure(result["flow_id"], main)
|
|
result = await hass.config_entries.options.async_configure(
|
|
result["flow_id"], result["data_schema"]({"outside_temperature_sources": ["sensor.outside"]}))
|
|
result = await hass.config_entries.options.async_configure(result["flow_id"], result["data_schema"]({}))
|
|
result = await hass.config_entries.options.async_configure(result["flow_id"], {"weight__sensor.room": 3})
|
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
|
await hass.async_block_till_done()
|
|
assert entry.options["room_max_age_minutes"] == 45
|
|
assert hass.data[DOMAIN][entry.entry_id].config["room_max_age_minutes"] == 45
|
|
|
|
|
|
async def test_v1_entry_migrates_on_setup(hass, inputs):
|
|
entry = MockConfigEntry(domain=DOMAIN, version=1, data={
|
|
"climate_entity": "climate.reference", "heater_entity": "switch.boiler",
|
|
"room_entities": ["sensor.room"], "outside_entity": "sensor.outside"})
|
|
entry.add_to_hass(hass)
|
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
assert entry.version == 2
|
|
assert entry.data["outside_temperature_sources"] == ["sensor.outside"]
|
|
assert "outside_entity" not in entry.data
|
|
|
|
|
|
async def test_options_flow_clears_optional_weather_sources(hass, inputs):
|
|
"""Swapping providers in the UI must allow removing an optional source list."""
|
|
entry = MockConfigEntry(domain=DOMAIN, version=2, unique_id="home_heating", data={
|
|
"climate_entity": "climate.reference", "heater_entity": "switch.boiler",
|
|
"room_entities": ["sensor.room"], "outside_temperature_sources": ["sensor.outside"],
|
|
"outside_wind_sources": ["sensor.wind"], "outside_pressure_sources": ["sensor.pressure"],
|
|
"weights": {"sensor.room": 1.0}})
|
|
entry.add_to_hass(hass)
|
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
flow = hass.config_entries.options
|
|
result = await flow.async_init(entry.entry_id)
|
|
result = await flow.async_configure(result["flow_id"], result["data_schema"]({
|
|
"climate_entity": "climate.reference", "heater_entity": "switch.boiler",
|
|
"room_entities": ["sensor.room"]}))
|
|
markers = {str(key): key for key in result["data_schema"].schema}
|
|
assert markers["outside_wind_sources"].description == {"suggested_value": ["sensor.wind"]}
|
|
# The user removes the wind provider; a cleared field may be omitted on submit.
|
|
result = await flow.async_configure(result["flow_id"], result["data_schema"](
|
|
{"outside_temperature_sources": ["sensor.outside"]}))
|
|
result = await flow.async_configure(result["flow_id"], result["data_schema"](
|
|
{"outside_pressure_sources": ["sensor.pressure"]}))
|
|
result = await flow.async_configure(result["flow_id"], {"weight__sensor.room": 1})
|
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
|
await hass.async_block_till_done()
|
|
assert entry.options["outside_wind_sources"] == []
|
|
assert entry.options["outside_pressure_sources"] == ["sensor.pressure"]
|
|
config = hass.data[DOMAIN][entry.entry_id].config
|
|
assert sources_for(config, "wind") == []
|
|
assert sources_for(config, "pressure") == ["sensor.pressure"]
|