async def test_hassio_discovery(hass): """Test supervisor add-on discovery.""" result = await hass.config_entries.flow.async_init( DOMAIN, data=HassioServiceInfo(config={ "addon": "RTSPtoWebRTC", "host": "fake-server", "port": 8083, }), context={"source": config_entries.SOURCE_HASSIO}, ) assert result.get("type") == "form" assert result.get("step_id") == "hassio_confirm" assert result.get("description_placeholders") == {"addon": "RTSPtoWebRTC"} with patch("rtsp_to_webrtc.client.Client.heartbeat"), patch( "homeassistant.components.rtsp_to_webrtc.async_setup_entry", return_value=True, ) as mock_setup: result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={}) await hass.async_block_till_done() assert result.get("type") == "create_entry" assert result.get("title") == "RTSPtoWebRTC" assert "result" in result assert result["result"].data == { "server_url": "http://fake-server:8083" } assert len(mock_setup.mock_calls) == 1
async def test_hassio_discovery_update_configuration(hass, aioclient_mock): """Test we can update an existing config entry.""" config_entry = await setup_deconz_integration(hass, aioclient_mock) with patch( "homeassistant.components.deconz.async_setup_entry", return_value=True, ) as mock_setup_entry: result = await hass.config_entries.flow.async_init( DECONZ_DOMAIN, data=HassioServiceInfo( config={ CONF_HOST: "2.3.4.5", CONF_PORT: 8080, CONF_API_KEY: "updated", CONF_SERIAL: BRIDGEID, } ), context={"source": SOURCE_HASSIO}, ) await hass.async_block_till_done() assert result["type"] == RESULT_TYPE_ABORT assert result["reason"] == "already_configured" assert config_entry.data[CONF_HOST] == "2.3.4.5" assert config_entry.data[CONF_PORT] == 8080 assert config_entry.data[CONF_API_KEY] == "updated" assert len(mock_setup_entry.mock_calls) == 1
async def test_hassio_discovery_server_failure(hass: HomeAssistant) -> None: """Test server failure during supvervisor add-on discovery shows an error.""" result = await hass.config_entries.flow.async_init( DOMAIN, data=HassioServiceInfo(config={ "addon": "RTSPtoWebRTC", "host": "fake-server", "port": 8083, }), context={"source": config_entries.SOURCE_HASSIO}, ) assert result.get("type") == "form" assert result.get("step_id") == "hassio_confirm" assert not result.get("errors") assert "flow_id" in result with patch( "rtsp_to_webrtc.client.Client.heartbeat", side_effect=rtsp_to_webrtc.exceptions.ResponseError(), ): result = await hass.config_entries.flow.async_configure( result["flow_id"], {}) assert result.get("type") == "abort" assert result.get("reason") == "server_failure"
async def test_hassio(hass): """Test that Hass.io can discover this integration.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_HASSIO}, data=HassioServiceInfo(config={ "addon": "Almond add-on", "host": "almond-addon", "port": "1234" }), ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["step_id"] == "hassio_confirm" with patch("homeassistant.components.almond.async_setup_entry", return_value=True) as mock_setup: result2 = await hass.config_entries.flow.async_configure( result["flow_id"], {}) assert len(mock_setup.mock_calls) == 1 assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert len(hass.config_entries.async_entries(DOMAIN)) == 1 entry = hass.config_entries.async_entries(DOMAIN)[0] assert entry.data["type"] == "local" assert entry.data["host"] == "http://almond-addon:1234"
async def test_flow_hassio_discovery(hass): """Test hassio discovery flow works.""" result = await hass.config_entries.flow.async_init( DECONZ_DOMAIN, data=HassioServiceInfo( config={ "addon": "Mock Addon", CONF_HOST: "mock-deconz", CONF_PORT: 80, CONF_SERIAL: BRIDGEID, CONF_API_KEY: API_KEY, }), context={"source": SOURCE_HASSIO}, ) assert result["type"] == RESULT_TYPE_FORM assert result["step_id"] == "hassio_confirm" assert result["description_placeholders"] == {"addon": "Mock Addon"} with patch( "homeassistant.components.deconz.async_setup_entry", return_value=True, ) as mock_setup_entry: result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={}) await hass.async_block_till_done() assert result["type"] == RESULT_TYPE_CREATE_ENTRY assert result["result"].data == { CONF_HOST: "mock-deconz", CONF_PORT: 80, CONF_API_KEY: API_KEY, } assert len(mock_setup_entry.mock_calls) == 1
async def test_hassio_discovery_webhook(hass, aioclient_mock, hassio_client): """Test discovery webhook.""" aioclient_mock.get( "http://127.0.0.1/discovery/testuuid", json={ "result": "ok", "data": { "service": "mqtt", "uuid": "test", "addon": "mosquitto", "config": { "broker": "mock-broker", "port": 1883, "username": "******", "password": "******", "protocol": "3.1.1", }, }, }, ) aioclient_mock.get( "http://127.0.0.1/addons/mosquitto/info", json={ "result": "ok", "data": { "name": "Mosquitto Test" } }, ) with patch( "homeassistant.components.mqtt.config_flow.FlowHandler.async_step_hassio", return_value={"type": "abort"}, ) as mock_mqtt: resp = await hassio_client.post( "/api/hassio_push/discovery/testuuid", json={ "addon": "mosquitto", "service": "mqtt", "uuid": "testuuid" }, ) await hass.async_block_till_done() assert resp.status == HTTPStatus.OK assert aioclient_mock.call_count == 2 assert mock_mqtt.called mock_mqtt.assert_called_with( HassioServiceInfo( config={ "broker": "mock-broker", "port": 1883, "username": "******", "password": "******", "protocol": "3.1.1", "addon": "Mosquitto Test", }))
async def test_hassio_discovery_webhook(hass, aioclient_mock, hassio_client, mock_mqtt): """Test discovery webhook.""" aioclient_mock.get( "http://127.0.0.1/discovery/testuuid", json={ "result": "ok", "data": { "service": "mqtt", "uuid": "test", "addon": "mosquitto", "config": { "broker": "mock-broker", "port": 1883, "username": "******", "password": "******", "protocol": "3.1.1", }, }, }, ) aioclient_mock.get( "http://127.0.0.1/addons/mosquitto/info", json={ "result": "ok", "data": { "name": "Mosquitto Test" } }, ) resp = await hassio_client.post( "/api/hassio_push/discovery/testuuid", json={ "addon": "mosquitto", "service": "mqtt", "uuid": "testuuid" }, ) await hass.async_block_till_done() hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) await hass.async_block_till_done() assert resp.status == HTTPStatus.OK assert aioclient_mock.call_count == 2 assert mock_mqtt.async_step_hassio.called mock_mqtt.async_step_hassio.assert_called_with( HassioServiceInfo( config={ "broker": "mock-broker", "port": 1883, "username": "******", "password": "******", "protocol": "3.1.1", "addon": "Mosquitto Test", }))
async def test_hassio_success(hass: HomeAssistant) -> None: """Test successful Supervisor flow.""" result = await hass.config_entries.flow.async_init( DOMAIN, data=HassioServiceInfo(config={ "addon": "motionEye", "url": TEST_URL }), context={"source": config_entries.SOURCE_HASSIO}, ) assert result.get("type") == data_entry_flow.RESULT_TYPE_FORM assert result.get("step_id") == "hassio_confirm" assert result.get("description_placeholders") == {"addon": "motionEye"} assert "flow_id" in result result2 = await hass.config_entries.flow.async_configure( result["flow_id"], {}) assert result2.get("type") == data_entry_flow.RESULT_TYPE_FORM assert result2.get("step_id") == "user" assert "flow_id" in result2 mock_client = create_mock_motioneye_client() with patch( "homeassistant.components.motioneye.MotionEyeClient", return_value=mock_client, ), patch( "homeassistant.components.motioneye.async_setup_entry", return_value=True, ) as mock_setup_entry: result3 = await hass.config_entries.flow.async_configure( result2["flow_id"], { CONF_ADMIN_USERNAME: "******", CONF_ADMIN_PASSWORD: "******", CONF_SURVEILLANCE_USERNAME: "******", CONF_SURVEILLANCE_PASSWORD: "******", }, ) await hass.async_block_till_done() assert result3.get("type") == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result3.get("title") == "Add-on" assert result3.get("data") == { CONF_URL: TEST_URL, CONF_ADMIN_USERNAME: "******", CONF_ADMIN_PASSWORD: "******", CONF_SURVEILLANCE_USERNAME: "******", CONF_SURVEILLANCE_PASSWORD: "******", } assert len(mock_setup_entry.mock_calls) == 1 assert mock_client.async_client_close.called
async def test_hassio_discovery_startup(hass, aioclient_mock, hassio_client): """Test startup and discovery after event.""" aioclient_mock.get( "http://127.0.0.1/discovery", json={ "result": "ok", "data": { "discovery": [{ "service": "mqtt", "uuid": "test", "addon": "mosquitto", "config": { "broker": "mock-broker", "port": 1883, "username": "******", "password": "******", "protocol": "3.1.1", }, }] }, }, ) aioclient_mock.get( "http://127.0.0.1/addons/mosquitto/info", json={ "result": "ok", "data": { "name": "Mosquitto Test" } }, ) assert aioclient_mock.call_count == 0 with patch( "homeassistant.components.mqtt.config_flow.FlowHandler.async_step_hassio", return_value={"type": "abort"}, ) as mock_mqtt: hass.bus.async_fire(EVENT_HOMEASSISTANT_START) await hass.async_block_till_done() assert aioclient_mock.call_count == 2 assert mock_mqtt.called mock_mqtt.assert_called_with( HassioServiceInfo( config={ "broker": "mock-broker", "port": 1883, "username": "******", "password": "******", "protocol": "3.1.1", "addon": "Mosquitto Test", }))
async def test_hassio_ignored(hass: HomeAssistant) -> None: """Test Supervisor discovered instance can be ignored.""" MockConfigEntry(domain=DOMAIN, source=config_entries.SOURCE_IGNORE).add_to_hass(hass) result = await hass.config_entries.flow.async_init( DOMAIN, data=HassioServiceInfo(config={ "addon": "motionEye", "url": TEST_URL }), context={"source": config_entries.SOURCE_HASSIO}, ) assert result.get("type") == data_entry_flow.RESULT_TYPE_ABORT assert result.get("reason") == "already_configured"
async def test_hassio_single_config_entry(hass: HomeAssistant) -> None: """Test supervisor add-on discovery only allows a single entry.""" old_entry = MockConfigEntry(domain=DOMAIN, data={"example": True}) old_entry.add_to_hass(hass) result = await hass.config_entries.flow.async_init( DOMAIN, data=HassioServiceInfo(config={ "addon": "RTSPtoWebRTC", "host": "fake-server", "port": 8083, }), context={"source": config_entries.SOURCE_HASSIO}, ) assert result.get("type") == "abort" assert result.get("reason") == "single_instance_allowed"
async def test_hassio_ignored(hass: HomeAssistant) -> None: """Test we supervisor discovered instance can be ignored.""" MockConfigEntry(domain=mqtt.DOMAIN, source=config_entries.SOURCE_IGNORE).add_to_hass(hass) result = await hass.config_entries.flow.async_init( mqtt.DOMAIN, data=HassioServiceInfo(config={ "addon": "Mosquitto", "host": "mock-mosquitto", "port": "1883" }), context={"source": config_entries.SOURCE_HASSIO}, ) assert result assert result.get("type") == data_entry_flow.RESULT_TYPE_ABORT assert result.get("reason") == "already_configured"
async def test_hassio_abort_if_already_in_progress( hass: HomeAssistant) -> None: """Test Supervisor discovered flow aborts if user flow in progress.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER}) assert result.get("type") == data_entry_flow.RESULT_TYPE_FORM result2 = await hass.config_entries.flow.async_init( DOMAIN, data=HassioServiceInfo(config={ "addon": "motionEye", "url": TEST_URL }), context={"source": config_entries.SOURCE_HASSIO}, ) assert result2.get("type") == data_entry_flow.RESULT_TYPE_ABORT assert result2.get("reason") == "already_in_progress"
async def test_abort_if_existing_entry(hass): """Check flow abort when an entry already exist.""" MockConfigEntry(domain=DOMAIN).add_to_hass(hass) flow = config_flow.AlmondFlowHandler() flow.hass = hass result = await flow.async_step_user() assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["reason"] == "single_instance_allowed" result = await flow.async_step_import({}) assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["reason"] == "single_instance_allowed" result = await flow.async_step_hassio(HassioServiceInfo(config={})) assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["reason"] == "single_instance_allowed"
async def test_hassio_ignored(hass: HomeAssistant) -> None: """Test we supervisor discovered instance can be ignored.""" MockConfigEntry(domain=DOMAIN, source=config_entries.SOURCE_IGNORE).add_to_hass(hass) result = await hass.config_entries.flow.async_init( DOMAIN, data=HassioServiceInfo( config={ "addon": "AdGuard Home Addon", "host": "mock-adguard", "port": "3000", }), context={"source": config_entries.SOURCE_HASSIO}, ) assert result assert result.get("type") == data_entry_flow.FlowResultType.ABORT assert result.get("reason") == "already_configured"
async def test_hassio_clean_up_on_user_flow(hass: HomeAssistant) -> None: """Test Supervisor discovered flow is clean up when doing user flow.""" result = await hass.config_entries.flow.async_init( DOMAIN, data=HassioServiceInfo(config={ "addon": "motionEye", "url": TEST_URL }), context={"source": config_entries.SOURCE_HASSIO}, ) assert result.get("type") == data_entry_flow.RESULT_TYPE_FORM result2 = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER}) assert result2.get("type") == data_entry_flow.RESULT_TYPE_FORM assert "flow_id" in result2 mock_client = create_mock_motioneye_client() with patch( "homeassistant.components.motioneye.MotionEyeClient", return_value=mock_client, ), patch( "homeassistant.components.motioneye.async_setup_entry", return_value=True, ) as mock_setup_entry: result3 = await hass.config_entries.flow.async_configure( result2["flow_id"], { CONF_URL: TEST_URL, CONF_ADMIN_USERNAME: "******", CONF_ADMIN_PASSWORD: "******", CONF_SURVEILLANCE_USERNAME: "******", CONF_SURVEILLANCE_PASSWORD: "******", }, ) await hass.async_block_till_done() assert result3.get("type") == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert len(mock_setup_entry.mock_calls) == 1 flows = hass.config_entries.flow.async_progress() assert len(flows) == 0
async def test_hassio_already_configured(hass: HomeAssistant) -> None: """Test we don't discover when already configured.""" MockConfigEntry( domain=DOMAIN, data={ CONF_URL: TEST_URL }, ).add_to_hass(hass) result = await hass.config_entries.flow.async_init( DOMAIN, data=HassioServiceInfo(config={ "addon": "motionEye", "url": TEST_URL }), context={"source": config_entries.SOURCE_HASSIO}, ) assert result.get("type") == data_entry_flow.FlowResultType.ABORT assert result.get("reason") == "already_configured"
async def test_hassio_discovery_dont_update_configuration(hass, aioclient_mock): """Test we can update an existing config entry.""" await setup_deconz_integration(hass, aioclient_mock) result = await hass.config_entries.flow.async_init( DECONZ_DOMAIN, data=HassioServiceInfo( config={ CONF_HOST: "1.2.3.4", CONF_PORT: 80, CONF_API_KEY: API_KEY, CONF_SERIAL: BRIDGEID, } ), context={"source": SOURCE_HASSIO}, ) assert result["type"] == RESULT_TYPE_ABORT assert result["reason"] == "already_configured"
async def test_hassio_already_configured(hass: HomeAssistant) -> None: """Test we only allow a single config flow.""" MockConfigEntry(domain=DOMAIN, data={ "host": "mock-adguard", "port": "3000" }).add_to_hass(hass) result = await hass.config_entries.flow.async_init( DOMAIN, data=HassioServiceInfo( config={ "addon": "AdGuard Home Addon", "host": "mock-adguard", "port": "3000", }), context={"source": config_entries.SOURCE_HASSIO}, ) assert result assert result.get("type") == data_entry_flow.RESULT_TYPE_ABORT assert result.get("reason") == "already_configured"
async def test_hassio_confirm(hass: HomeAssistant, aioclient_mock: AiohttpClientMocker) -> None: """Test we can finish a config flow.""" aioclient_mock.get( "http://mock-adguard:3000/control/status", json={"version": "v0.99.0"}, headers={"Content-Type": CONTENT_TYPE_JSON}, ) result = await hass.config_entries.flow.async_init( DOMAIN, data=HassioServiceInfo(config={ "addon": "AdGuard Home Addon", "host": "mock-adguard", "port": 3000 }), context={"source": config_entries.SOURCE_HASSIO}, ) assert result assert result.get("type") == data_entry_flow.FlowResultType.FORM assert result.get("step_id") == "hassio_confirm" assert result.get("description_placeholders") == { "addon": "AdGuard Home Addon" } result2 = await hass.config_entries.flow.async_configure( result["flow_id"], {}) assert result2 assert result2.get("type") == data_entry_flow.FlowResultType.CREATE_ENTRY assert result2.get("title") == "AdGuard Home Addon" data = result2.get("data") assert data assert data[CONF_HOST] == "mock-adguard" assert data[CONF_PASSWORD] is None assert data[CONF_PORT] == 3000 assert data[CONF_SSL] is False assert data[CONF_USERNAME] is None assert data[CONF_VERIFY_SSL]
async def test_hassio_confirm(hass, mock_try_connection_success, mock_finish_setup): """Test we can finish a config flow.""" mock_try_connection.return_value = True result = await hass.config_entries.flow.async_init( "mqtt", data=HassioServiceInfo( config={ "addon": "Mock Addon", "host": "mock-broker", "port": 1883, "username": "******", "password": "******", "protocol": "3.1.1", # Set by the addon's discovery, ignored by HA "ssl": False, # Set by the addon's discovery, ignored by HA }), context={"source": config_entries.SOURCE_HASSIO}, ) assert result["type"] == "form" assert result["step_id"] == "hassio_confirm" assert result["description_placeholders"] == {"addon": "Mock Addon"} mock_try_connection_success.reset_mock() result = await hass.config_entries.flow.async_configure( result["flow_id"], {"discovery": True}) assert result["type"] == "create_entry" assert result["result"].data == { "broker": "mock-broker", "port": 1883, "username": "******", "password": "******", "discovery": True, } # Check we tried the connection assert len(mock_try_connection_success.mock_calls) # Check config entry got setup assert len(mock_finish_setup.mock_calls) == 1
async def test_hassio_already_configured(hass: HomeAssistant) -> None: """Test successful hassio flow.""" entry_data = { "password": "******", "host": "1.1.1.1", "port": 8888, "name": "custom name", "addon": "vlc", } entry = MockConfigEntry(domain=DOMAIN, data=entry_data, unique_id="hassio") entry.add_to_hass(hass) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_HASSIO}, data=HassioServiceInfo(config=entry_data), ) await hass.async_block_till_done() assert result["type"] == FlowResultType.ABORT
async def test_hassio_connection_error( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker) -> None: """Test we show Hass.io confirm form on AdGuard Home connection error.""" aioclient_mock.get("http://mock-adguard:3000/control/status", exc=aiohttp.ClientError) result = await hass.config_entries.flow.async_init( DOMAIN, data=HassioServiceInfo(config={ "addon": "AdGuard Home Addon", "host": "mock-adguard", "port": 3000 }), context={"source": config_entries.SOURCE_HASSIO}, ) result = await hass.config_entries.flow.async_configure( result["flow_id"], {}) assert result assert result.get("type") == data_entry_flow.FlowResultType.FORM assert result.get("step_id") == "hassio_confirm" assert result.get("errors") == {"base": "cannot_connect"}
async def test_hassio_flow(hass: HomeAssistant) -> None: """Test successful hassio flow.""" with patch( "homeassistant.components.vlc_telnet.config_flow.Client.connect" ), patch( "homeassistant.components.vlc_telnet.config_flow.Client.login" ), patch( "homeassistant.components.vlc_telnet.config_flow.Client.disconnect" ), patch( "homeassistant.components.vlc_telnet.async_setup_entry", return_value=True, ) as mock_setup_entry: test_data = HassioServiceInfo( config={ "password": "******", "host": "1.1.1.1", "port": 8888, "name": "custom name", "addon": "vlc", }) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_HASSIO}, data=test_data, ) await hass.async_block_till_done() assert result["type"] == FlowResultType.FORM result2 = await hass.config_entries.flow.async_configure( result["flow_id"], {}) assert result2["type"] == FlowResultType.CREATE_ENTRY assert result2["title"] == test_data.config["name"] assert result2["data"] == test_data.config assert len(mock_setup_entry.mock_calls) == 1
async def test_hassio_errors( hass: HomeAssistant, error: str, connect_side_effect: Exception | None, login_side_effect: Exception | None, ) -> None: """Test we handle hassio errors.""" with patch( "homeassistant.components.vlc_telnet.config_flow.Client.connect", side_effect=connect_side_effect, ), patch( "homeassistant.components.vlc_telnet.config_flow.Client.login", side_effect=login_side_effect, ), patch( "homeassistant.components.vlc_telnet.config_flow.Client.disconnect" ): result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_HASSIO}, data=HassioServiceInfo( config={ "password": "******", "host": "1.1.1.1", "port": 8888, "name": "custom name", "addon": "vlc", }), ) await hass.async_block_till_done() assert result["type"] == FlowResultType.FORM result2 = await hass.config_entries.flow.async_configure( result["flow_id"], {}) assert result2["type"] == FlowResultType.ABORT assert result2["reason"] == error
async def test_hassio_discovery_startup_done(hass, aioclient_mock, hassio_client, mock_mqtt): """Test startup and discovery with hass discovery.""" aioclient_mock.post( "http://127.0.0.1/supervisor/options", json={ "result": "ok", "data": {} }, ) aioclient_mock.get( "http://127.0.0.1/discovery", json={ "result": "ok", "data": { "discovery": [{ "service": "mqtt", "uuid": "test", "addon": "mosquitto", "config": { "broker": "mock-broker", "port": 1883, "username": "******", "password": "******", "protocol": "3.1.1", }, }] }, }, ) aioclient_mock.get( "http://127.0.0.1/addons/mosquitto/info", json={ "result": "ok", "data": { "name": "Mosquitto Test" } }, ) with patch( "homeassistant.components.hassio.HassIO.update_hass_api", return_value={"result": "ok"}, ), patch( "homeassistant.components.hassio.HassIO.get_info", Mock(side_effect=HassioAPIError()), ): await hass.async_start() await async_setup_component(hass, "hassio", {}) await hass.async_block_till_done() assert aioclient_mock.call_count == 2 assert mock_mqtt.async_step_hassio.called mock_mqtt.async_step_hassio.assert_called_with( HassioServiceInfo( config={ "broker": "mock-broker", "port": 1883, "username": "******", "password": "******", "protocol": "3.1.1", "addon": "Mosquitto Test", }))
import pytest from homeassistant import config_entries from homeassistant.components.hassio import HassioServiceInfo from homeassistant.components.hassio.handler import HassioAPIError from homeassistant.components.ozw.config_flow import TITLE from homeassistant.components.ozw.const import DOMAIN from tests.common import MockConfigEntry ADDON_DISCOVERY_INFO = HassioServiceInfo( config={ "addon": "OpenZWave", "host": "host1", "port": 1234, "username": "******", "password": "******", } ) @pytest.fixture(name="supervisor") def mock_supervisor_fixture(): """Mock Supervisor.""" with patch("homeassistant.components.hassio.is_hassio", return_value=True): yield @pytest.fixture(name="addon_info") def mock_addon_info():