From f7654938abd9f081e1ae56da0b238d766df01530 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Sat, 16 Aug 2025 13:52:03 +0000 Subject: [PATCH] Improve sql config flow --- homeassistant/components/sql/config_flow.py | 187 +++-- homeassistant/components/sql/const.py | 1 + tests/components/sql/__init__.py | 100 ++- tests/components/sql/conftest.py | 41 + tests/components/sql/test_config_flow.py | 847 ++++++++++---------- 5 files changed, 634 insertions(+), 542 deletions(-) create mode 100644 tests/components/sql/conftest.py diff --git a/homeassistant/components/sql/config_flow.py b/homeassistant/components/sql/config_flow.py index 37a6f9ef104..434cccf021f 100644 --- a/homeassistant/components/sql/config_flow.py +++ b/homeassistant/components/sql/config_flow.py @@ -6,7 +6,7 @@ import logging from typing import Any import sqlalchemy -from sqlalchemy.engine import Result +from sqlalchemy.engine import Engine, Result from sqlalchemy.exc import MultipleResultsFound, NoSuchColumnError, SQLAlchemyError from sqlalchemy.orm import Session, scoped_session, sessionmaker import sqlparse @@ -32,9 +32,10 @@ from homeassistant.const import ( CONF_VALUE_TEMPLATE, ) from homeassistant.core import callback +from homeassistant.data_entry_flow import section from homeassistant.helpers import selector -from .const import CONF_COLUMN_NAME, CONF_QUERY, DOMAIN +from .const import CONF_ADVANCED_OPTIONS, CONF_COLUMN_NAME, CONF_QUERY, DOMAIN from .util import resolve_db_url _LOGGER = logging.getLogger(__name__) @@ -42,40 +43,38 @@ _LOGGER = logging.getLogger(__name__) OPTIONS_SCHEMA: vol.Schema = vol.Schema( { - vol.Optional( - CONF_DB_URL, - ): selector.TextSelector(), - vol.Required( - CONF_COLUMN_NAME, - ): selector.TextSelector(), - vol.Required( - CONF_QUERY, - ): selector.TextSelector(selector.TextSelectorConfig(multiline=True)), - vol.Optional( - CONF_UNIT_OF_MEASUREMENT, - ): selector.TextSelector(), - vol.Optional( - CONF_VALUE_TEMPLATE, - ): selector.TemplateSelector(), - vol.Optional(CONF_DEVICE_CLASS): selector.SelectSelector( - selector.SelectSelectorConfig( - options=[ - cls.value - for cls in SensorDeviceClass - if cls != SensorDeviceClass.ENUM - ], - mode=selector.SelectSelectorMode.DROPDOWN, - translation_key="device_class", - sort=True, - ) + vol.Required(CONF_COLUMN_NAME): selector.TextSelector(), + vol.Required(CONF_QUERY): selector.TextSelector( + selector.TextSelectorConfig(multiline=True) ), - vol.Optional(CONF_STATE_CLASS): selector.SelectSelector( - selector.SelectSelectorConfig( - options=[cls.value for cls in SensorStateClass], - mode=selector.SelectSelectorMode.DROPDOWN, - translation_key="state_class", - sort=True, - ) + vol.Required(CONF_ADVANCED_OPTIONS): section( + vol.Schema( + { + vol.Optional(CONF_VALUE_TEMPLATE): selector.TemplateSelector(), + vol.Optional(CONF_UNIT_OF_MEASUREMENT): selector.TextSelector(), + vol.Optional(CONF_DEVICE_CLASS): selector.SelectSelector( + selector.SelectSelectorConfig( + options=[ + cls.value + for cls in SensorDeviceClass + if cls != SensorDeviceClass.ENUM + ], + mode=selector.SelectSelectorMode.DROPDOWN, + translation_key="device_class", + sort=True, + ) + ), + vol.Optional(CONF_STATE_CLASS): selector.SelectSelector( + selector.SelectSelectorConfig( + options=[cls.value for cls in SensorStateClass], + mode=selector.SelectSelectorMode.DROPDOWN, + translation_key="state_class", + sort=True, + ) + ), + } + ), + {"collapsed": True}, ), } ) @@ -83,8 +82,9 @@ OPTIONS_SCHEMA: vol.Schema = vol.Schema( CONFIG_SCHEMA: vol.Schema = vol.Schema( { vol.Required(CONF_NAME, default="Select SQL Query"): selector.TextSelector(), + vol.Optional(CONF_DB_URL): selector.TextSelector(), } -).extend(OPTIONS_SCHEMA.schema) +) def validate_sql_select(value: str) -> str: @@ -99,6 +99,31 @@ def validate_sql_select(value: str) -> str: return str(query[0]) +def validate_db_connection(db_url: str) -> bool: + """Validate db connection.""" + + engine: Engine | None = None + sess: Session | None = None + try: + engine = sqlalchemy.create_engine(db_url, future=True) + sessmaker = scoped_session(sessionmaker(bind=engine, future=True)) + sess = sessmaker() + sess.execute(sqlalchemy.text("select 1 as value")) + except SQLAlchemyError as error: + _LOGGER.debug("Execution error %s", error) + if sess: + sess.close() + if engine: + engine.dispose() + raise + + if sess: + sess.close() + engine.dispose() + + return True + + def validate_query(db_url: str, query: str, column: str) -> bool: """Validate SQL query.""" @@ -138,6 +163,8 @@ class SQLConfigFlow(ConfigFlow, domain=DOMAIN): VERSION = 1 + data: dict[str, Any] + @staticmethod @callback def async_get_options_flow( @@ -151,17 +178,46 @@ class SQLConfigFlow(ConfigFlow, domain=DOMAIN): ) -> ConfigFlowResult: """Handle the user step.""" errors = {} - description_placeholders = {} if user_input is not None: db_url = user_input.get(CONF_DB_URL) + + try: + db_url_for_validation = resolve_db_url(self.hass, db_url) + await self.hass.async_add_executor_job( + validate_db_connection, db_url_for_validation + ) + except SQLAlchemyError: + errors["db_url"] = "db_url_invalid" + + if not errors: + self.data = {CONF_NAME: user_input[CONF_NAME]} + if db_url and db_url_for_validation != get_instance(self.hass).db_url: + self.data[CONF_DB_URL] = db_url + return await self.async_step_options() + + return self.async_show_form( + step_id="user", + data_schema=self.add_suggested_values_to_schema(CONFIG_SCHEMA, user_input), + errors=errors, + ) + + async def async_step_options( + self, user_input: dict[str, Any] | None = None + ) -> ConfigFlowResult: + """Handle the user step.""" + errors = {} + description_placeholders = {} + + if user_input is not None: query = user_input[CONF_QUERY] column = user_input[CONF_COLUMN_NAME] - db_url_for_validation = None try: query = validate_sql_select(query) - db_url_for_validation = resolve_db_url(self.hass, db_url) + db_url_for_validation = resolve_db_url( + self.hass, self.data.get(CONF_DB_URL) + ) await self.hass.async_add_executor_job( validate_query, db_url_for_validation, query, column ) @@ -178,32 +234,22 @@ class SQLConfigFlow(ConfigFlow, domain=DOMAIN): _LOGGER.debug("Invalid query: %s", err) errors["query"] = "query_invalid" - options = { - CONF_QUERY: query, - CONF_COLUMN_NAME: column, - CONF_NAME: user_input[CONF_NAME], - } - if uom := user_input.get(CONF_UNIT_OF_MEASUREMENT): - options[CONF_UNIT_OF_MEASUREMENT] = uom - if value_template := user_input.get(CONF_VALUE_TEMPLATE): - options[CONF_VALUE_TEMPLATE] = value_template - if device_class := user_input.get(CONF_DEVICE_CLASS): - options[CONF_DEVICE_CLASS] = device_class - if state_class := user_input.get(CONF_STATE_CLASS): - options[CONF_STATE_CLASS] = state_class - if db_url_for_validation != get_instance(self.hass).db_url: - options[CONF_DB_URL] = db_url_for_validation + for k, v in user_input[CONF_ADVANCED_OPTIONS].items(): + if not v: + user_input[CONF_ADVANCED_OPTIONS].pop(k) if not errors: + name = self.data[CONF_NAME] + self.data.pop(CONF_NAME) return self.async_create_entry( - title=user_input[CONF_NAME], - data={}, - options=options, + title=name, + data=self.data, + options=user_input, ) return self.async_show_form( - step_id="user", - data_schema=self.add_suggested_values_to_schema(CONFIG_SCHEMA, user_input), + step_id="options", + data_schema=self.add_suggested_values_to_schema(OPTIONS_SCHEMA, user_input), errors=errors, description_placeholders=description_placeholders, ) @@ -220,10 +266,9 @@ class SQLOptionsFlowHandler(OptionsFlowWithReload): description_placeholders = {} if user_input is not None: - db_url = user_input.get(CONF_DB_URL) + db_url = self.config_entry.data.get(CONF_DB_URL) query = user_input[CONF_QUERY] column = user_input[CONF_COLUMN_NAME] - name = self.config_entry.options.get(CONF_NAME, self.config_entry.title) try: query = validate_sql_select(query) @@ -252,24 +297,12 @@ class SQLOptionsFlowHandler(OptionsFlowWithReload): recorder_db, ) - options = { - CONF_QUERY: query, - CONF_COLUMN_NAME: column, - CONF_NAME: name, - } - if uom := user_input.get(CONF_UNIT_OF_MEASUREMENT): - options[CONF_UNIT_OF_MEASUREMENT] = uom - if value_template := user_input.get(CONF_VALUE_TEMPLATE): - options[CONF_VALUE_TEMPLATE] = value_template - if device_class := user_input.get(CONF_DEVICE_CLASS): - options[CONF_DEVICE_CLASS] = device_class - if state_class := user_input.get(CONF_STATE_CLASS): - options[CONF_STATE_CLASS] = state_class - if db_url_for_validation != get_instance(self.hass).db_url: - options[CONF_DB_URL] = db_url_for_validation + for k, v in user_input[CONF_ADVANCED_OPTIONS].items(): + if not v: + user_input[CONF_ADVANCED_OPTIONS].pop(k) return self.async_create_entry( - data=options, + data=user_input, ) return self.async_show_form( diff --git a/homeassistant/components/sql/const.py b/homeassistant/components/sql/const.py index d8d13ab1699..20e54c52abf 100644 --- a/homeassistant/components/sql/const.py +++ b/homeassistant/components/sql/const.py @@ -9,4 +9,5 @@ PLATFORMS = [Platform.SENSOR] CONF_COLUMN_NAME = "column" CONF_QUERY = "query" +CONF_ADVANCED_OPTIONS = "advanced_options" DB_URL_RE = re.compile("//.*:.*@") diff --git a/tests/components/sql/__init__.py b/tests/components/sql/__init__.py index 5f91cba1d94..f7ef563f7f4 100644 --- a/tests/components/sql/__init__.py +++ b/tests/components/sql/__init__.py @@ -10,7 +10,12 @@ from homeassistant.components.sensor import ( SensorDeviceClass, SensorStateClass, ) -from homeassistant.components.sql.const import CONF_COLUMN_NAME, CONF_QUERY, DOMAIN +from homeassistant.components.sql.const import ( + CONF_ADVANCED_OPTIONS, + CONF_COLUMN_NAME, + CONF_QUERY, + DOMAIN, +) from homeassistant.config_entries import SOURCE_USER from homeassistant.const import ( CONF_DEVICE_CLASS, @@ -30,140 +35,167 @@ from homeassistant.helpers.trigger_template_entity import ( from tests.common import MockConfigEntry ENTRY_CONFIG = { - CONF_NAME: "Get Value", CONF_QUERY: "SELECT 5 as value", CONF_COLUMN_NAME: "value", - CONF_UNIT_OF_MEASUREMENT: "MiB", - CONF_DEVICE_CLASS: SensorDeviceClass.DATA_SIZE, - CONF_STATE_CLASS: SensorStateClass.TOTAL, + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_DEVICE_CLASS: SensorDeviceClass.DATA_SIZE, + CONF_STATE_CLASS: SensorStateClass.TOTAL, + }, } ENTRY_CONFIG_WITH_VALUE_TEMPLATE = { - CONF_NAME: "Get Value", CONF_QUERY: "SELECT 5 as value", CONF_COLUMN_NAME: "value", - CONF_UNIT_OF_MEASUREMENT: "MiB", - CONF_VALUE_TEMPLATE: "{{ value }}", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_VALUE_TEMPLATE: "{{ value }}", + }, } ENTRY_CONFIG_INVALID_QUERY = { - CONF_NAME: "Get Value", CONF_QUERY: "SELECT 5 FROM as value", CONF_COLUMN_NAME: "size", - CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, } ENTRY_CONFIG_INVALID_QUERY_2 = { - CONF_NAME: "Get Value", CONF_QUERY: "SELECT5 FROM as value", CONF_COLUMN_NAME: "size", - CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, } ENTRY_CONFIG_INVALID_QUERY_3 = { - CONF_NAME: "Get Value", CONF_QUERY: ";;", CONF_COLUMN_NAME: "size", - CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, } ENTRY_CONFIG_INVALID_QUERY_OPT = { CONF_QUERY: "SELECT 5 FROM as value", CONF_COLUMN_NAME: "size", - CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, } ENTRY_CONFIG_INVALID_QUERY_2_OPT = { CONF_QUERY: "SELECT5 FROM as value", CONF_COLUMN_NAME: "size", - CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, } ENTRY_CONFIG_INVALID_QUERY_3_OPT = { CONF_QUERY: ";;", CONF_COLUMN_NAME: "size", - CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, } ENTRY_CONFIG_QUERY_READ_ONLY_CTE = { - CONF_NAME: "Get Value", CONF_QUERY: "WITH test AS (SELECT 1 AS row_num, 10 AS state) SELECT state FROM test WHERE row_num = 1 LIMIT 1;", CONF_COLUMN_NAME: "state", - CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, } ENTRY_CONFIG_QUERY_NO_READ_ONLY = { - CONF_NAME: "Get Value", CONF_QUERY: "UPDATE states SET state = 999999 WHERE state_id = 11125", CONF_COLUMN_NAME: "state", - CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, } ENTRY_CONFIG_QUERY_NO_READ_ONLY_CTE = { - CONF_NAME: "Get Value", CONF_QUERY: "WITH test AS (SELECT state FROM states) UPDATE states SET states.state = test.state;", CONF_COLUMN_NAME: "size", - CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, } ENTRY_CONFIG_QUERY_READ_ONLY_CTE_OPT = { CONF_QUERY: "WITH test AS (SELECT 1 AS row_num, 10 AS state) SELECT state FROM test WHERE row_num = 1 LIMIT 1;", CONF_COLUMN_NAME: "state", - CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, } ENTRY_CONFIG_QUERY_NO_READ_ONLY_OPT = { CONF_QUERY: "UPDATE 5 as value", CONF_COLUMN_NAME: "size", - CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, } ENTRY_CONFIG_QUERY_NO_READ_ONLY_CTE_OPT = { CONF_QUERY: "WITH test AS (SELECT state FROM states) UPDATE states SET states.state = test.state;", CONF_COLUMN_NAME: "size", - CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, } ENTRY_CONFIG_MULTIPLE_QUERIES = { - CONF_NAME: "Get Value", CONF_QUERY: "SELECT 5 as state; UPDATE states SET state = 10;", CONF_COLUMN_NAME: "state", - CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, } ENTRY_CONFIG_MULTIPLE_QUERIES_OPT = { CONF_QUERY: "SELECT 5 as state; UPDATE states SET state = 10;", CONF_COLUMN_NAME: "state", - CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, } ENTRY_CONFIG_INVALID_COLUMN_NAME = { - CONF_NAME: "Get Value", CONF_QUERY: "SELECT 5 as value", CONF_COLUMN_NAME: "size", - CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, } ENTRY_CONFIG_INVALID_COLUMN_NAME_OPT = { CONF_QUERY: "SELECT 5 as value", CONF_COLUMN_NAME: "size", - CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, } ENTRY_CONFIG_NO_RESULTS = { - CONF_NAME: "Get Value", CONF_QUERY: "SELECT kalle as value from no_table;", CONF_COLUMN_NAME: "value", - CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, } YAML_CONFIG = { diff --git a/tests/components/sql/conftest.py b/tests/components/sql/conftest.py new file mode 100644 index 00000000000..3dc78b7a171 --- /dev/null +++ b/tests/components/sql/conftest.py @@ -0,0 +1,41 @@ +"""Fixtures for the SQL integration.""" + +from __future__ import annotations + +from collections.abc import Generator +from pathlib import Path +import sqlite3 +from unittest.mock import AsyncMock, patch + +import pytest + +from homeassistant.core import HomeAssistant + + +@pytest.fixture +def mock_setup_entry() -> Generator[AsyncMock]: + """Override async_setup_entry.""" + with patch( + "homeassistant.components.sql.async_setup_entry", return_value=True + ) as mock_setup_entry: + yield mock_setup_entry + + +@pytest.fixture +async def create_db( + hass: HomeAssistant, + tmp_path: Path, +) -> str: + """Test the SQL sensor with a query that returns no value.""" + db_path = tmp_path / "test.db" + db_path_str = f"sqlite:///{db_path}" + + def make_test_db(): + """Create a test database.""" + conn = sqlite3.connect(db_path) + conn.execute("CREATE TABLE users (value INTEGER)") + conn.commit() + conn.close() + + await hass.async_add_executor_job(make_test_db) + return db_path_str diff --git a/tests/components/sql/test_config_flow.py b/tests/components/sql/test_config_flow.py index 3f2400c0a32..6007abd13d0 100644 --- a/tests/components/sql/test_config_flow.py +++ b/tests/components/sql/test_config_flow.py @@ -3,14 +3,31 @@ from __future__ import annotations from pathlib import Path +from typing import Any from unittest.mock import patch +import pytest from sqlalchemy.exc import SQLAlchemyError from homeassistant import config_entries -from homeassistant.components.recorder import Recorder -from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass -from homeassistant.components.sql.const import DOMAIN +from homeassistant.components.recorder import CONF_DB_URL +from homeassistant.components.sensor import ( + CONF_STATE_CLASS, + SensorDeviceClass, + SensorStateClass, +) +from homeassistant.components.sql.const import ( + CONF_ADVANCED_OPTIONS, + CONF_COLUMN_NAME, + CONF_QUERY, + DOMAIN, +) +from homeassistant.const import ( + CONF_DEVICE_CLASS, + CONF_NAME, + CONF_UNIT_OF_MEASUREMENT, + CONF_VALUE_TEMPLATE, +) from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType @@ -36,8 +53,25 @@ from . import ( from tests.common import MockConfigEntry +pytestmark = pytest.mark.usefixtures("mock_setup_entry", "recorder_mock") -async def test_form(recorder_mock: Recorder, hass: HomeAssistant) -> None: +DATA_CONFIG = {CONF_NAME: "Get Value"} +DATA_CONFIG_DB = {CONF_NAME: "Get Value", CONF_DB_URL: "sqlite://"} +OPTIONS_DATA_CONFIG = {} + + +@pytest.mark.parametrize( + ("data_config", "result_config"), + [ + (DATA_CONFIG, OPTIONS_DATA_CONFIG), + (DATA_CONFIG_DB, OPTIONS_DATA_CONFIG), + ], +) +async def test_form_simple( + hass: HomeAssistant, + data_config: dict[str, Any], + result_config: dict[str, Any], +) -> None: """Test we get the form.""" result = await hass.config_entries.flow.async_init( @@ -46,32 +80,33 @@ async def test_form(recorder_mock: Recorder, hass: HomeAssistant) -> None: assert result["type"] is FlowResultType.FORM assert result["errors"] == {} - with patch( - "homeassistant.components.sql.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - ENTRY_CONFIG, - ) - await hass.async_block_till_done() + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + data_config, + ) + await hass.async_block_till_done() - assert result2["type"] is FlowResultType.CREATE_ENTRY - assert result2["title"] == "Get Value" - assert result2["options"] == { - "name": "Get Value", - "query": "SELECT 5 as value", - "column": "value", - "unit_of_measurement": "MiB", - "device_class": SensorDeviceClass.DATA_SIZE, - "state_class": SensorStateClass.TOTAL, + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + ENTRY_CONFIG, + ) + await hass.async_block_till_done() + + assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["title"] == "Get Value" + assert result["data"] == result_config + assert result["options"] == { + CONF_QUERY: "SELECT 5 as value", + CONF_COLUMN_NAME: "value", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_DEVICE_CLASS: SensorDeviceClass.DATA_SIZE, + CONF_STATE_CLASS: SensorStateClass.TOTAL, + }, } - assert len(mock_setup_entry.mock_calls) == 1 -async def test_form_with_value_template( - recorder_mock: Recorder, hass: HomeAssistant -) -> None: +async def test_form_with_value_template(hass: HomeAssistant) -> None: """Test for with value template.""" result = await hass.config_entries.flow.async_init( @@ -80,208 +115,217 @@ async def test_form_with_value_template( assert result["type"] is FlowResultType.FORM assert result["errors"] == {} - with patch( - "homeassistant.components.sql.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - ENTRY_CONFIG_WITH_VALUE_TEMPLATE, - ) - await hass.async_block_till_done() + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + DATA_CONFIG, + ) + await hass.async_block_till_done() - assert result2["type"] is FlowResultType.CREATE_ENTRY - assert result2["title"] == "Get Value" - assert result2["options"] == { - "name": "Get Value", - "query": "SELECT 5 as value", - "column": "value", - "unit_of_measurement": "MiB", - "value_template": "{{ value }}", + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + ENTRY_CONFIG_WITH_VALUE_TEMPLATE, + ) + await hass.async_block_till_done() + + assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["title"] == "Get Value" + assert result["data"] == {} + assert result["options"] == { + CONF_QUERY: "SELECT 5 as value", + CONF_COLUMN_NAME: "value", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_VALUE_TEMPLATE: "{{ value }}", + }, } - assert len(mock_setup_entry.mock_calls) == 1 -async def test_flow_fails_db_url(recorder_mock: Recorder, hass: HomeAssistant) -> None: +async def test_flow_fails_db_url(hass: HomeAssistant) -> None: """Test config flow fails incorrect db url.""" - result4 = await hass.config_entries.flow.async_init( + result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) - assert result4["type"] is FlowResultType.FORM - assert result4["step_id"] == config_entries.SOURCE_USER + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == config_entries.SOURCE_USER with patch( "homeassistant.components.sql.config_flow.sqlalchemy.create_engine", side_effect=SQLAlchemyError("error_message"), ): - result4 = await hass.config_entries.flow.async_configure( - result4["flow_id"], - user_input=ENTRY_CONFIG, + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input=DATA_CONFIG, ) - assert result4["errors"] == {"db_url": "db_url_invalid"} + assert result["errors"] == {CONF_DB_URL: "db_url_invalid"} -async def test_flow_fails_invalid_query( - recorder_mock: Recorder, hass: HomeAssistant -) -> None: +async def test_flow_fails_invalid_query(hass: HomeAssistant) -> None: """Test config flow fails incorrect db url.""" - result4 = await hass.config_entries.flow.async_init( + result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) - assert result4["type"] is FlowResultType.FORM - assert result4["step_id"] == config_entries.SOURCE_USER + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == config_entries.SOURCE_USER - result5 = await hass.config_entries.flow.async_configure( - result4["flow_id"], + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input=DATA_CONFIG, + ) + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input=ENTRY_CONFIG_INVALID_QUERY, ) - assert result5["type"] is FlowResultType.FORM - assert result5["errors"] == { - "query": "query_invalid", + assert result["type"] is FlowResultType.FORM + assert result["errors"] == { + CONF_QUERY: "query_invalid", } - result6 = await hass.config_entries.flow.async_configure( - result4["flow_id"], + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input=ENTRY_CONFIG_INVALID_QUERY_2, ) - assert result6["type"] is FlowResultType.FORM - assert result6["errors"] == { - "query": "query_invalid", + assert result["type"] is FlowResultType.FORM + assert result["errors"] == { + CONF_QUERY: "query_invalid", } - result6 = await hass.config_entries.flow.async_configure( - result4["flow_id"], + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input=ENTRY_CONFIG_INVALID_QUERY_3, ) - assert result6["type"] is FlowResultType.FORM - assert result6["errors"] == { - "query": "query_invalid", + assert result["type"] is FlowResultType.FORM + assert result["errors"] == { + CONF_QUERY: "query_invalid", } - result5 = await hass.config_entries.flow.async_configure( - result4["flow_id"], + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input=ENTRY_CONFIG_QUERY_NO_READ_ONLY, ) - assert result5["type"] is FlowResultType.FORM - assert result5["errors"] == { - "query": "query_no_read_only", + assert result["type"] is FlowResultType.FORM + assert result["errors"] == { + CONF_QUERY: "query_no_read_only", } - result6 = await hass.config_entries.flow.async_configure( - result4["flow_id"], + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input=ENTRY_CONFIG_QUERY_NO_READ_ONLY_CTE, ) - assert result6["type"] is FlowResultType.FORM - assert result6["errors"] == { - "query": "query_no_read_only", + assert result["type"] is FlowResultType.FORM + assert result["errors"] == { + CONF_QUERY: "query_no_read_only", } - result6 = await hass.config_entries.flow.async_configure( - result4["flow_id"], + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input=ENTRY_CONFIG_MULTIPLE_QUERIES, ) - assert result6["type"] is FlowResultType.FORM - assert result6["errors"] == { - "query": "multiple_queries", + assert result["type"] is FlowResultType.FORM + assert result["errors"] == { + CONF_QUERY: "multiple_queries", } - result5 = await hass.config_entries.flow.async_configure( - result4["flow_id"], + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input=ENTRY_CONFIG_NO_RESULTS, ) - assert result5["type"] is FlowResultType.FORM - assert result5["errors"] == { - "query": "query_invalid", + assert result["type"] is FlowResultType.FORM + assert result["errors"] == { + CONF_QUERY: "query_invalid", } - result5 = await hass.config_entries.flow.async_configure( - result4["flow_id"], + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input=ENTRY_CONFIG, ) - assert result5["type"] is FlowResultType.CREATE_ENTRY - assert result5["title"] == "Get Value" - assert result5["options"] == { - "name": "Get Value", - "query": "SELECT 5 as value", - "column": "value", - "unit_of_measurement": "MiB", - "device_class": SensorDeviceClass.DATA_SIZE, - "state_class": SensorStateClass.TOTAL, + assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["title"] == "Get Value" + assert result["data"] == {} + assert result["options"] == { + CONF_QUERY: "SELECT 5 as value", + CONF_COLUMN_NAME: "value", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_DEVICE_CLASS: SensorDeviceClass.DATA_SIZE, + CONF_STATE_CLASS: SensorStateClass.TOTAL, + }, } -async def test_flow_fails_invalid_column_name( - recorder_mock: Recorder, hass: HomeAssistant -) -> None: +async def test_flow_fails_invalid_column_name(hass: HomeAssistant) -> None: """Test config flow fails invalid column name.""" - result4 = await hass.config_entries.flow.async_init( + result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) - assert result4["type"] is FlowResultType.FORM - assert result4["step_id"] == "user" + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "user" - result5 = await hass.config_entries.flow.async_configure( - result4["flow_id"], + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input=DATA_CONFIG, + ) + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input=ENTRY_CONFIG_INVALID_COLUMN_NAME, ) - assert result5["type"] is FlowResultType.FORM - assert result5["errors"] == { - "column": "column_invalid", + assert result["type"] is FlowResultType.FORM + assert result["errors"] == { + CONF_COLUMN_NAME: "column_invalid", } - result5 = await hass.config_entries.flow.async_configure( - result4["flow_id"], + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input=ENTRY_CONFIG, ) - assert result5["type"] is FlowResultType.CREATE_ENTRY - assert result5["title"] == "Get Value" - assert result5["options"] == { - "name": "Get Value", - "query": "SELECT 5 as value", - "column": "value", - "unit_of_measurement": "MiB", - "device_class": SensorDeviceClass.DATA_SIZE, - "state_class": SensorStateClass.TOTAL, + assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["title"] == "Get Value" + assert result["data"] == {} + assert result["options"] == { + CONF_QUERY: "SELECT 5 as value", + CONF_COLUMN_NAME: "value", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_DEVICE_CLASS: SensorDeviceClass.DATA_SIZE, + CONF_STATE_CLASS: SensorStateClass.TOTAL, + }, } -async def test_options_flow(recorder_mock: Recorder, hass: HomeAssistant) -> None: +async def test_options_flow(hass: HomeAssistant) -> None: """Test options config flow.""" entry = MockConfigEntry( domain=DOMAIN, - data={}, + data=OPTIONS_DATA_CONFIG, options={ - "db_url": "sqlite://", - "name": "Get Value", - "query": "SELECT 5 as value", - "column": "value", - "unit_of_measurement": "MiB", - "device_class": SensorDeviceClass.DATA_SIZE, - "state_class": SensorStateClass.TOTAL, + CONF_QUERY: "SELECT 5 as value", + CONF_COLUMN_NAME: "value", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_DEVICE_CLASS: SensorDeviceClass.DATA_SIZE, + CONF_STATE_CLASS: SensorStateClass.TOTAL, + }, }, ) entry.add_to_hass(hass) - with patch( - "homeassistant.components.sql.async_setup_entry", - return_value=True, - ): - assert await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() + 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) @@ -291,40 +335,41 @@ async def test_options_flow(recorder_mock: Recorder, hass: HomeAssistant) -> Non result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={ - "db_url": "sqlite://", - "query": "SELECT 5 as size", - "column": "size", - "unit_of_measurement": "MiB", - "value_template": "{{ value }}", - "device_class": SensorDeviceClass.DATA_SIZE, - "state_class": SensorStateClass.TOTAL, + CONF_QUERY: "SELECT 5 as size", + CONF_COLUMN_NAME: "size", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_VALUE_TEMPLATE: "{{ value }}", + CONF_DEVICE_CLASS: SensorDeviceClass.DATA_SIZE, + CONF_STATE_CLASS: SensorStateClass.TOTAL, + }, }, ) assert result["type"] is FlowResultType.CREATE_ENTRY assert result["data"] == { - "name": "Get Value", - "query": "SELECT 5 as size", - "column": "size", - "unit_of_measurement": "MiB", - "value_template": "{{ value }}", - "device_class": SensorDeviceClass.DATA_SIZE, - "state_class": SensorStateClass.TOTAL, + CONF_QUERY: "SELECT 5 as size", + CONF_COLUMN_NAME: "size", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_VALUE_TEMPLATE: "{{ value }}", + CONF_DEVICE_CLASS: SensorDeviceClass.DATA_SIZE, + CONF_STATE_CLASS: SensorStateClass.TOTAL, + }, } -async def test_options_flow_name_previously_removed( - recorder_mock: Recorder, hass: HomeAssistant -) -> None: +async def test_options_flow_name_previously_removed(hass: HomeAssistant) -> None: """Test options config flow where the name was missing.""" entry = MockConfigEntry( domain=DOMAIN, - data={}, + data=OPTIONS_DATA_CONFIG, options={ - "db_url": "sqlite://", - "query": "SELECT 5 as value", - "column": "value", - "unit_of_measurement": "MiB", + CONF_QUERY: "SELECT 5 as value", + CONF_COLUMN_NAME: "value", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, }, title="Get Value Title", ) @@ -338,54 +383,45 @@ async def test_options_flow_name_previously_removed( assert result["type"] is FlowResultType.FORM assert result["step_id"] == "init" - with patch( - "homeassistant.components.sql.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - result = await hass.config_entries.options.async_configure( - result["flow_id"], - user_input={ - "db_url": "sqlite://", - "query": "SELECT 5 as size", - "column": "size", - "unit_of_measurement": "MiB", + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + CONF_QUERY: "SELECT 5 as size", + CONF_COLUMN_NAME: "size", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", }, - ) - await hass.async_block_till_done() + }, + ) + await hass.async_block_till_done() - assert len(mock_setup_entry.mock_calls) == 1 assert result["type"] is FlowResultType.CREATE_ENTRY assert result["data"] == { - "name": "Get Value Title", - "query": "SELECT 5 as size", - "column": "size", - "unit_of_measurement": "MiB", + CONF_QUERY: "SELECT 5 as size", + CONF_COLUMN_NAME: "size", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, } -async def test_options_flow_fails_db_url( - recorder_mock: Recorder, hass: HomeAssistant -) -> None: +async def test_options_flow_fails_db_url(hass: HomeAssistant) -> None: """Test options flow fails incorrect db url.""" entry = MockConfigEntry( domain=DOMAIN, - data={}, + data=OPTIONS_DATA_CONFIG, options={ - "db_url": "sqlite://", - "name": "Get Value", - "query": "SELECT 5 as value", - "column": "value", - "unit_of_measurement": "MiB", + CONF_QUERY: "SELECT 5 as value", + CONF_COLUMN_NAME: "value", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, }, ) entry.add_to_hass(hass) - with patch( - "homeassistant.components.sql.async_setup_entry", - return_value=True, - ): - assert await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() + 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) @@ -393,233 +429,218 @@ async def test_options_flow_fails_db_url( "homeassistant.components.sql.config_flow.sqlalchemy.create_engine", side_effect=SQLAlchemyError("error_message"), ): - result2 = await hass.config_entries.options.async_configure( + result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={ - "db_url": "sqlite://", - "query": "SELECT 5 as size", - "column": "size", - "unit_of_measurement": "MiB", + CONF_QUERY: "SELECT 5 as size", + CONF_COLUMN_NAME: "size", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, }, ) - assert result2["errors"] == {"db_url": "db_url_invalid"} + assert result["errors"] == {CONF_DB_URL: "db_url_invalid"} -async def test_options_flow_fails_invalid_query( - recorder_mock: Recorder, hass: HomeAssistant -) -> None: +async def test_options_flow_fails_invalid_query(hass: HomeAssistant) -> None: """Test options flow fails incorrect query and template.""" entry = MockConfigEntry( domain=DOMAIN, - data={}, + data=OPTIONS_DATA_CONFIG, options={ - "db_url": "sqlite://", - "name": "Get Value", - "query": "SELECT 5 as value", - "column": "value", - "unit_of_measurement": "MiB", + CONF_QUERY: "SELECT 5 as value", + CONF_COLUMN_NAME: "value", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, }, ) entry.add_to_hass(hass) - with patch( - "homeassistant.components.sql.async_setup_entry", - return_value=True, - ): - assert await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() + 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) - result2 = await hass.config_entries.options.async_configure( + result = await hass.config_entries.options.async_configure( result["flow_id"], user_input=ENTRY_CONFIG_INVALID_QUERY_OPT, ) - assert result2["type"] is FlowResultType.FORM - assert result2["errors"] == { - "query": "query_invalid", + assert result["type"] is FlowResultType.FORM + assert result["errors"] == { + CONF_QUERY: "query_invalid", } - result3 = await hass.config_entries.options.async_configure( + result = await hass.config_entries.options.async_configure( result["flow_id"], user_input=ENTRY_CONFIG_INVALID_QUERY_2_OPT, ) - assert result3["type"] is FlowResultType.FORM - assert result3["errors"] == { - "query": "query_invalid", + assert result["type"] is FlowResultType.FORM + assert result["errors"] == { + CONF_QUERY: "query_invalid", } - result3 = await hass.config_entries.options.async_configure( + result = await hass.config_entries.options.async_configure( result["flow_id"], user_input=ENTRY_CONFIG_INVALID_QUERY_3_OPT, ) - assert result3["type"] is FlowResultType.FORM - assert result3["errors"] == { - "query": "query_invalid", + assert result["type"] is FlowResultType.FORM + assert result["errors"] == { + CONF_QUERY: "query_invalid", } - result2 = await hass.config_entries.options.async_configure( + result = await hass.config_entries.options.async_configure( result["flow_id"], user_input=ENTRY_CONFIG_QUERY_NO_READ_ONLY_OPT, ) - assert result2["type"] is FlowResultType.FORM - assert result2["errors"] == { - "query": "query_no_read_only", + assert result["type"] is FlowResultType.FORM + assert result["errors"] == { + CONF_QUERY: "query_no_read_only", } - result3 = await hass.config_entries.options.async_configure( + result = await hass.config_entries.options.async_configure( result["flow_id"], user_input=ENTRY_CONFIG_QUERY_NO_READ_ONLY_CTE_OPT, ) - assert result3["type"] is FlowResultType.FORM - assert result3["errors"] == { - "query": "query_no_read_only", + assert result["type"] is FlowResultType.FORM + assert result["errors"] == { + CONF_QUERY: "query_no_read_only", } - result3 = await hass.config_entries.options.async_configure( + result = await hass.config_entries.options.async_configure( result["flow_id"], user_input=ENTRY_CONFIG_MULTIPLE_QUERIES_OPT, ) - assert result3["type"] is FlowResultType.FORM - assert result3["errors"] == { - "query": "multiple_queries", + assert result["type"] is FlowResultType.FORM + assert result["errors"] == { + CONF_QUERY: "multiple_queries", } - result4 = await hass.config_entries.options.async_configure( + result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={ - "db_url": "sqlite://", - "query": "SELECT 5 as size", - "column": "size", - "unit_of_measurement": "MiB", + CONF_QUERY: "SELECT 5 as size", + CONF_COLUMN_NAME: "size", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, }, ) - assert result4["type"] is FlowResultType.CREATE_ENTRY - assert result4["data"] == { - "name": "Get Value", - "query": "SELECT 5 as size", - "column": "size", - "unit_of_measurement": "MiB", + assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["data"] == { + CONF_QUERY: "SELECT 5 as size", + CONF_COLUMN_NAME: "size", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, } -async def test_options_flow_fails_invalid_column_name( - recorder_mock: Recorder, hass: HomeAssistant -) -> None: +async def test_options_flow_fails_invalid_column_name(hass: HomeAssistant) -> None: """Test options flow fails invalid column name.""" entry = MockConfigEntry( domain=DOMAIN, - data={}, + data=OPTIONS_DATA_CONFIG, options={ - "name": "Get Value", - "query": "SELECT 5 as value", - "column": "value", - "unit_of_measurement": "MiB", + CONF_QUERY: "SELECT 5 as value", + CONF_COLUMN_NAME: "value", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, }, ) entry.add_to_hass(hass) - with patch( - "homeassistant.components.sql.async_setup_entry", - return_value=True, - ): - assert await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() + 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) - result2 = await hass.config_entries.options.async_configure( + result = await hass.config_entries.options.async_configure( result["flow_id"], user_input=ENTRY_CONFIG_INVALID_COLUMN_NAME_OPT, ) - assert result2["type"] is FlowResultType.FORM - assert result2["errors"] == { - "column": "column_invalid", + assert result["type"] is FlowResultType.FORM + assert result["errors"] == { + CONF_COLUMN_NAME: "column_invalid", } - result4 = await hass.config_entries.options.async_configure( + result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={ - "query": "SELECT 5 as value", - "column": "value", - "unit_of_measurement": "MiB", + CONF_QUERY: "SELECT 5 as value", + CONF_COLUMN_NAME: "value", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, }, ) - assert result4["type"] is FlowResultType.CREATE_ENTRY - assert result4["data"] == { - "name": "Get Value", - "query": "SELECT 5 as value", - "column": "value", - "unit_of_measurement": "MiB", + assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["data"] == { + CONF_QUERY: "SELECT 5 as value", + CONF_COLUMN_NAME: "value", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, } -async def test_options_flow_db_url_empty( - recorder_mock: Recorder, hass: HomeAssistant -) -> None: +async def test_options_flow_db_url_empty(hass: HomeAssistant) -> None: """Test options config flow with leaving db_url empty.""" entry = MockConfigEntry( domain=DOMAIN, - data={}, + data=OPTIONS_DATA_CONFIG, options={ - "db_url": "sqlite://", - "name": "Get Value", - "query": "SELECT 5 as value", - "column": "value", - "unit_of_measurement": "MiB", + CONF_QUERY: "SELECT 5 as value", + CONF_COLUMN_NAME: "value", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, }, ) entry.add_to_hass(hass) - with patch( - "homeassistant.components.sql.async_setup_entry", - return_value=True, - ): - assert await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() + 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) assert result["type"] is FlowResultType.FORM assert result["step_id"] == "init" - with ( - patch( - "homeassistant.components.sql.async_setup_entry", - return_value=True, - ), - ): - result = await hass.config_entries.options.async_configure( - result["flow_id"], - user_input={ - "query": "SELECT 5 as size", - "column": "size", - "unit_of_measurement": "MiB", + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + CONF_QUERY: "SELECT 5 as size", + CONF_COLUMN_NAME: "size", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", }, - ) - await hass.async_block_till_done() + }, + ) + await hass.async_block_till_done() assert result["type"] is FlowResultType.CREATE_ENTRY assert result["data"] == { - "name": "Get Value", - "query": "SELECT 5 as size", - "column": "size", - "unit_of_measurement": "MiB", + CONF_QUERY: "SELECT 5 as size", + CONF_COLUMN_NAME: "size", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, } async def test_full_flow_not_recorder_db( - recorder_mock: Recorder, hass: HomeAssistant, tmp_path: Path, ) -> None: @@ -632,30 +653,31 @@ async def test_full_flow_not_recorder_db( db_path = tmp_path / "db.db" db_path_str = f"sqlite:///{db_path}" - with ( - patch( - "homeassistant.components.sql.async_setup_entry", - return_value=True, - ), - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "db_url": db_path_str, - "name": "Get Value", - "query": "SELECT 5 as value", - "column": "value", - }, - ) - await hass.async_block_till_done() + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_DB_URL: db_path_str, + CONF_NAME: "Get Value", + }, + ) - assert result2["type"] is FlowResultType.CREATE_ENTRY - assert result2["title"] == "Get Value" - assert result2["options"] == { - "name": "Get Value", - "db_url": db_path_str, - "query": "SELECT 5 as value", - "column": "value", + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_QUERY: "SELECT 5 as value", + CONF_COLUMN_NAME: "value", + CONF_ADVANCED_OPTIONS: {}, + }, + ) + await hass.async_block_till_done() + + assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["title"] == "Get Value" + assert result["data"] == {CONF_DB_URL: db_path_str} + assert result["options"] == { + CONF_QUERY: "SELECT 5 as value", + CONF_COLUMN_NAME: "value", + CONF_ADVANCED_OPTIONS: {}, } entry = hass.config_entries.async_entries(DOMAIN)[0] @@ -665,75 +687,40 @@ async def test_full_flow_not_recorder_db( assert result["type"] is FlowResultType.FORM assert result["step_id"] == "init" - with ( - patch( - "homeassistant.components.sql.async_setup_entry", - return_value=True, - ), - ): - result = await hass.config_entries.options.async_configure( - result["flow_id"], - user_input={ - "query": "SELECT 5 as value", - "db_url": db_path_str, - "column": "value", - "unit_of_measurement": "MiB", - }, - ) - await hass.async_block_till_done() - - assert result["type"] is FlowResultType.CREATE_ENTRY - assert result["data"] == { - "name": "Get Value", - "db_url": db_path_str, - "query": "SELECT 5 as value", - "column": "value", - "unit_of_measurement": "MiB", - } - - # Need to test same again to mitigate issue with db_url removal - result = await hass.config_entries.options.async_init(entry.entry_id) - result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={ - "query": "SELECT 5 as value", - "db_url": db_path_str, - "column": "value", - "unit_of_measurement": "MB", + CONF_QUERY: "SELECT 5 as value", + CONF_COLUMN_NAME: "value", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, }, ) await hass.async_block_till_done() assert result["type"] is FlowResultType.CREATE_ENTRY assert result["data"] == { - "name": "Get Value", - "db_url": db_path_str, - "query": "SELECT 5 as value", - "column": "value", - "unit_of_measurement": "MB", - } - - assert entry.options == { - "name": "Get Value", - "db_url": db_path_str, - "query": "SELECT 5 as value", - "column": "value", - "unit_of_measurement": "MB", + CONF_QUERY: "SELECT 5 as value", + CONF_COLUMN_NAME: "value", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, } -async def test_device_state_class(recorder_mock: Recorder, hass: HomeAssistant) -> None: +async def test_device_state_class(hass: HomeAssistant) -> None: """Test we get the form.""" entry = MockConfigEntry( domain=DOMAIN, - data={}, + data=OPTIONS_DATA_CONFIG, options={ - "name": "Get Value", - "query": "SELECT 5 as value", - "column": "value", - "unit_of_measurement": "MiB", + CONF_QUERY: "SELECT 5 as value", + CONF_COLUMN_NAME: "value", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, }, ) entry.add_to_hass(hass) @@ -742,56 +729,54 @@ async def test_device_state_class(recorder_mock: Recorder, hass: HomeAssistant) assert result["type"] is FlowResultType.FORM assert result["step_id"] == "init" - with patch( - "homeassistant.components.sql.async_setup_entry", - return_value=True, - ): - result2 = await hass.config_entries.options.async_configure( - result["flow_id"], - user_input={ - "query": "SELECT 5 as value", - "column": "value", - "unit_of_measurement": "MiB", - "device_class": SensorDeviceClass.DATA_SIZE, - "state_class": SensorStateClass.TOTAL, + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + CONF_QUERY: "SELECT 5 as value", + CONF_COLUMN_NAME: "value", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_DEVICE_CLASS: SensorDeviceClass.DATA_SIZE, + CONF_STATE_CLASS: SensorStateClass.TOTAL, }, - ) - await hass.async_block_till_done() + }, + ) + await hass.async_block_till_done() - assert result2["type"] is FlowResultType.CREATE_ENTRY - assert result2["data"] == { - "name": "Get Value", - "query": "SELECT 5 as value", - "column": "value", - "unit_of_measurement": "MiB", - "device_class": SensorDeviceClass.DATA_SIZE, - "state_class": SensorStateClass.TOTAL, + assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["data"] == { + CONF_QUERY: "SELECT 5 as value", + CONF_COLUMN_NAME: "value", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + CONF_DEVICE_CLASS: SensorDeviceClass.DATA_SIZE, + CONF_STATE_CLASS: SensorStateClass.TOTAL, + }, } result = await hass.config_entries.options.async_init(entry.entry_id) assert result["type"] is FlowResultType.FORM assert result["step_id"] == "init" - with patch( - "homeassistant.components.sql.async_setup_entry", - return_value=True, - ): - result3 = await hass.config_entries.options.async_configure( - result["flow_id"], - user_input={ - "query": "SELECT 5 as value", - "column": "value", - "unit_of_measurement": "MiB", + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + CONF_QUERY: "SELECT 5 as value", + CONF_COLUMN_NAME: "value", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", }, - ) - await hass.async_block_till_done() + }, + ) + await hass.async_block_till_done() - assert result3["type"] is FlowResultType.CREATE_ENTRY - assert "device_class" not in result3["data"] - assert "state_class" not in result3["data"] - assert result3["data"] == { - "name": "Get Value", - "query": "SELECT 5 as value", - "column": "value", - "unit_of_measurement": "MiB", + assert result["type"] is FlowResultType.CREATE_ENTRY + assert CONF_DEVICE_CLASS not in result["data"] + assert CONF_STATE_CLASS not in result["data"] + assert result["data"] == { + CONF_QUERY: "SELECT 5 as value", + CONF_COLUMN_NAME: "value", + CONF_ADVANCED_OPTIONS: { + CONF_UNIT_OF_MEASUREMENT: "MiB", + }, }