From 332d3e0f191536b3eb8b0823a94986691220b15c Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Sun, 22 Jan 2023 17:33:40 +0100 Subject: [PATCH] Use fixtures to setup Axis integration in tests (#86034) Co-authored-by: Martin Hjelmare --- tests/components/axis/conftest.py | 169 ++++++++-- tests/components/axis/const.py | 141 ++++++++ tests/components/axis/test_binary_sensor.py | 11 +- tests/components/axis/test_camera.py | 15 +- tests/components/axis/test_config_flow.py | 155 +++++---- tests/components/axis/test_device.py | 347 +++----------------- tests/components/axis/test_diagnostics.py | 24 +- tests/components/axis/test_init.py | 12 +- tests/components/axis/test_light.py | 71 ++-- tests/components/axis/test_switch.py | 31 +- 10 files changed, 491 insertions(+), 485 deletions(-) create mode 100644 tests/components/axis/const.py diff --git a/tests/components/axis/conftest.py b/tests/components/axis/conftest.py index a97520bea0e..38f20df7ff0 100644 --- a/tests/components/axis/conftest.py +++ b/tests/components/axis/conftest.py @@ -1,10 +1,12 @@ """Axis conftest.""" from __future__ import annotations +from copy import deepcopy from unittest.mock import patch from axis.rtsp import Signal, State import pytest +import respx from homeassistant.components.axis.const import CONF_EVENTS, DOMAIN as AXIS_DOMAIN from homeassistant.const import ( @@ -16,26 +18,30 @@ from homeassistant.const import ( CONF_USERNAME, ) +from .const import ( + API_DISCOVERY_RESPONSE, + APPLICATIONS_LIST_RESPONSE, + BASIC_DEVICE_INFO_RESPONSE, + BRAND_RESPONSE, + DEFAULT_HOST, + FORMATTED_MAC, + IMAGE_RESPONSE, + MODEL, + MQTT_CLIENT_RESPONSE, + NAME, + PORT_MANAGEMENT_RESPONSE, + PORTS_RESPONSE, + PROPERTIES_RESPONSE, + PTZ_RESPONSE, + STREAM_PROFILES_RESPONSE, + VIEW_AREAS_RESPONSE, + VMD4_RESPONSE, +) + from tests.common import MockConfigEntry from tests.components.light.conftest import mock_light_profiles # noqa: F401 -MAC = "00408C123456" -FORMATTED_MAC = "00:40:8c:12:34:56" -MODEL = "model" -NAME = "name" - -DEFAULT_HOST = "1.2.3.4" - -ENTRY_OPTIONS = {CONF_EVENTS: True} - -ENTRY_CONFIG = { - CONF_HOST: DEFAULT_HOST, - CONF_USERNAME: "root", - CONF_PASSWORD: "pass", - CONF_PORT: 80, - CONF_MODEL: MODEL, - CONF_NAME: NAME, -} +# Config entry fixtures @pytest.fixture(name="config_entry") @@ -61,13 +67,138 @@ def config_entry_version_fixture(request): @pytest.fixture(name="config") def config_fixture(): """Define a config entry data fixture.""" - return ENTRY_CONFIG.copy() + return { + CONF_HOST: DEFAULT_HOST, + CONF_USERNAME: "root", + CONF_PASSWORD: "pass", + CONF_PORT: 80, + CONF_MODEL: MODEL, + CONF_NAME: NAME, + } @pytest.fixture(name="options") def options_fixture(request): """Define a config entry options fixture.""" - return ENTRY_OPTIONS.copy() + return {CONF_EVENTS: True} + + +# Axis API fixtures + + +@pytest.fixture(name="mock_vapix_requests") +def default_request_fixture(respx_mock): + """Mock default Vapix requests responses.""" + + def __mock_default_requests(host): + path = f"http://{host}:80" + + if host != DEFAULT_HOST: + respx.post(f"{path}/axis-cgi/apidiscovery.cgi").respond( + json=API_DISCOVERY_RESPONSE, + ) + respx.post(f"{path}/axis-cgi/basicdeviceinfo.cgi").respond( + json=BASIC_DEVICE_INFO_RESPONSE, + ) + respx.post(f"{path}/axis-cgi/io/portmanagement.cgi").respond( + json=PORT_MANAGEMENT_RESPONSE, + ) + respx.post(f"{path}/axis-cgi/mqtt/client.cgi").respond( + json=MQTT_CLIENT_RESPONSE, + ) + respx.post(f"{path}/axis-cgi/streamprofile.cgi").respond( + json=STREAM_PROFILES_RESPONSE, + ) + respx.post(f"{path}/axis-cgi/viewarea/info.cgi").respond( + json=VIEW_AREAS_RESPONSE + ) + respx.get(f"{path}/axis-cgi/param.cgi?action=list&group=root.Brand").respond( + text=BRAND_RESPONSE, + headers={"Content-Type": "text/plain"}, + ) + respx.get(f"{path}/axis-cgi/param.cgi?action=list&group=root.Image").respond( + text=IMAGE_RESPONSE, + headers={"Content-Type": "text/plain"}, + ) + respx.get(f"{path}/axis-cgi/param.cgi?action=list&group=root.Input").respond( + text=PORTS_RESPONSE, + headers={"Content-Type": "text/plain"}, + ) + respx.get(f"{path}/axis-cgi/param.cgi?action=list&group=root.IOPort").respond( + text=PORTS_RESPONSE, + headers={"Content-Type": "text/plain"}, + ) + respx.get(f"{path}/axis-cgi/param.cgi?action=list&group=root.Output").respond( + text=PORTS_RESPONSE, + headers={"Content-Type": "text/plain"}, + ) + respx.get( + f"{path}/axis-cgi/param.cgi?action=list&group=root.Properties" + ).respond( + text=PROPERTIES_RESPONSE, + headers={"Content-Type": "text/plain"}, + ) + respx.get(f"{path}/axis-cgi/param.cgi?action=list&group=root.PTZ").respond( + text=PTZ_RESPONSE, + headers={"Content-Type": "text/plain"}, + ) + respx.get( + f"{path}/axis-cgi/param.cgi?action=list&group=root.StreamProfile" + ).respond( + text=STREAM_PROFILES_RESPONSE, + headers={"Content-Type": "text/plain"}, + ) + respx.post(f"{path}/axis-cgi/applications/list.cgi").respond( + text=APPLICATIONS_LIST_RESPONSE, + headers={"Content-Type": "text/xml"}, + ) + respx.post(f"{path}/local/vmd/control.cgi").respond(json=VMD4_RESPONSE) + + yield __mock_default_requests + + +@pytest.fixture() +def api_discovery_items(): + """Additional Apidiscovery items.""" + return {} + + +@pytest.fixture(autouse=True) +def api_discovery_fixture(api_discovery_items): + """Apidiscovery mock response.""" + data = deepcopy(API_DISCOVERY_RESPONSE) + if api_discovery_items: + data["data"]["apiList"].append(api_discovery_items) + respx.post(f"http://{DEFAULT_HOST}:80/axis-cgi/apidiscovery.cgi").respond(json=data) + + +@pytest.fixture(name="setup_default_vapix_requests") +def default_vapix_requests_fixture(mock_vapix_requests): + """Mock default Vapix requests responses.""" + mock_vapix_requests(DEFAULT_HOST) + + +@pytest.fixture(name="prepare_config_entry") +async def prep_config_entry_fixture(hass, config_entry, setup_default_vapix_requests): + """Fixture factory to set up Axis network device.""" + + async def __mock_setup_config_entry(): + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + return config_entry + + yield __mock_setup_config_entry + + +@pytest.fixture(name="setup_config_entry") +async def setup_config_entry_fixture(hass, config_entry, setup_default_vapix_requests): + """Define a fixture to set up Axis network device.""" + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + yield config_entry + + +# RTSP fixtures @pytest.fixture(autouse=True) diff --git a/tests/components/axis/const.py b/tests/components/axis/const.py new file mode 100644 index 00000000000..d90a788ae75 --- /dev/null +++ b/tests/components/axis/const.py @@ -0,0 +1,141 @@ +"""Constants for Axis integration tests.""" + + +MAC = "00408C123456" +FORMATTED_MAC = "00:40:8c:12:34:56" +MODEL = "model" +NAME = "name" + +DEFAULT_HOST = "1.2.3.4" + + +API_DISCOVERY_RESPONSE = { + "method": "getApiList", + "apiVersion": "1.0", + "data": { + "apiList": [ + {"id": "api-discovery", "version": "1.0", "name": "API Discovery Service"}, + {"id": "param-cgi", "version": "1.0", "name": "Legacy Parameter Handling"}, + ] + }, +} + +API_DISCOVERY_BASIC_DEVICE_INFO = { + "id": "basic-device-info", + "version": "1.1", + "name": "Basic Device Information", +} +API_DISCOVERY_MQTT = {"id": "mqtt-client", "version": "1.0", "name": "MQTT Client API"} +API_DISCOVERY_PORT_MANAGEMENT = { + "id": "io-port-management", + "version": "1.0", + "name": "IO Port Management", +} + +APPLICATIONS_LIST_RESPONSE = """ + +""" + +BASIC_DEVICE_INFO_RESPONSE = { + "apiVersion": "1.1", + "data": { + "propertyList": { + "ProdNbr": "M1065-LW", + "ProdType": "Network Camera", + "SerialNumber": MAC, + "Version": "9.80.1", + } + }, +} + + +MQTT_CLIENT_RESPONSE = { + "apiVersion": "1.0", + "context": "some context", + "method": "getClientStatus", + "data": {"status": {"state": "active", "connectionStatus": "Connected"}}, +} + +PORT_MANAGEMENT_RESPONSE = { + "apiVersion": "1.0", + "method": "getPorts", + "data": { + "numberOfPorts": 1, + "items": [ + { + "port": "0", + "configurable": False, + "usage": "", + "name": "PIR sensor", + "direction": "input", + "state": "open", + "normalState": "open", + } + ], + }, +} + +VMD4_RESPONSE = { + "apiVersion": "1.4", + "method": "getConfiguration", + "context": "Axis library", + "data": { + "cameras": [{"id": 1, "rotation": 0, "active": True}], + "profiles": [ + {"filters": [], "camera": 1, "triggers": [], "name": "Profile 1", "uid": 1} + ], + }, +} + +BRAND_RESPONSE = """root.Brand.Brand=AXIS +root.Brand.ProdFullName=AXIS M1065-LW Network Camera +root.Brand.ProdNbr=M1065-LW +root.Brand.ProdShortName=AXIS M1065-LW +root.Brand.ProdType=Network Camera +root.Brand.ProdVariant= +root.Brand.WebURL=http://www.axis.com +""" + +IMAGE_RESPONSE = """root.Image.I0.Enabled=yes +root.Image.I0.Name=View Area 1 +root.Image.I0.Source=0 +root.Image.I1.Enabled=no +root.Image.I1.Name=View Area 2 +root.Image.I1.Source=0 +""" + +PORTS_RESPONSE = """root.Input.NbrOfInputs=1 +root.IOPort.I0.Configurable=no +root.IOPort.I0.Direction=input +root.IOPort.I0.Input.Name=PIR sensor +root.IOPort.I0.Input.Trig=closed +root.Output.NbrOfOutputs=0 +""" + +PROPERTIES_RESPONSE = f"""root.Properties.API.HTTP.Version=3 +root.Properties.API.Metadata.Metadata=yes +root.Properties.API.Metadata.Version=1.0 +root.Properties.EmbeddedDevelopment.Version=2.16 +root.Properties.Firmware.BuildDate=Feb 15 2019 09:42 +root.Properties.Firmware.BuildNumber=26 +root.Properties.Firmware.Version=9.10.1 +root.Properties.Image.Format=jpeg,mjpeg,h264 +root.Properties.Image.NbrOfViews=2 +root.Properties.Image.Resolution=1920x1080,1280x960,1280x720,1024x768,1024x576,800x600,640x480,640x360,352x240,320x240 +root.Properties.Image.Rotation=0,180 +root.Properties.System.SerialNumber={MAC} +""" + +PTZ_RESPONSE = "" + + +STREAM_PROFILES_RESPONSE = """root.StreamProfile.MaxGroups=26 +root.StreamProfile.S0.Description=profile_1_description +root.StreamProfile.S0.Name=profile_1 +root.StreamProfile.S0.Parameters=videocodec=h264 +root.StreamProfile.S1.Description=profile_2_description +root.StreamProfile.S1.Name=profile_2 +root.StreamProfile.S1.Parameters=videocodec=h265 +""" + +VIEW_AREAS_RESPONSE = {"apiVersion": "1.0", "method": "list", "data": {"viewAreas": []}} diff --git a/tests/components/axis/test_binary_sensor.py b/tests/components/axis/test_binary_sensor.py index 87dae03c4ff..de4bdd95943 100644 --- a/tests/components/axis/test_binary_sensor.py +++ b/tests/components/axis/test_binary_sensor.py @@ -8,8 +8,7 @@ from homeassistant.components.binary_sensor import ( from homeassistant.const import STATE_OFF, STATE_ON from homeassistant.setup import async_setup_component -from .conftest import NAME -from .test_device import setup_axis_integration +from .const import NAME async def test_platform_manually_configured(hass): @@ -26,17 +25,13 @@ async def test_platform_manually_configured(hass): assert AXIS_DOMAIN not in hass.data -async def test_no_binary_sensors(hass, config_entry): +async def test_no_binary_sensors(hass, setup_config_entry): """Test that no sensors in Axis results in no sensor entities.""" - await setup_axis_integration(hass, config_entry) - assert not hass.states.async_entity_ids(BINARY_SENSOR_DOMAIN) -async def test_binary_sensors(hass, config_entry, mock_rtsp_event): +async def test_binary_sensors(hass, setup_config_entry, mock_rtsp_event): """Test that sensors are loaded properly.""" - await setup_axis_integration(hass, config_entry) - mock_rtsp_event( topic="tns1:Device/tnsaxis:Sensor/PIR", data_type="state", diff --git a/tests/components/axis/test_camera.py b/tests/components/axis/test_camera.py index 5c6e941d540..57d54ba17bd 100644 --- a/tests/components/axis/test_camera.py +++ b/tests/components/axis/test_camera.py @@ -13,8 +13,7 @@ from homeassistant.components.camera import DOMAIN as CAMERA_DOMAIN from homeassistant.const import STATE_IDLE from homeassistant.setup import async_setup_component -from .conftest import NAME -from .test_device import setup_axis_integration +from .const import NAME async def test_platform_manually_configured(hass): @@ -29,10 +28,8 @@ async def test_platform_manually_configured(hass): assert AXIS_DOMAIN not in hass.data -async def test_camera(hass, config_entry): +async def test_camera(hass, setup_config_entry): """Test that Axis camera platform is loaded properly.""" - await setup_axis_integration(hass, config_entry) - assert len(hass.states.async_entity_ids(CAMERA_DOMAIN)) == 1 entity_id = f"{CAMERA_DOMAIN}.{NAME}" @@ -51,10 +48,8 @@ async def test_camera(hass, config_entry): @pytest.mark.parametrize("options", [{CONF_STREAM_PROFILE: "profile_1"}]) -async def test_camera_with_stream_profile(hass, config_entry): +async def test_camera_with_stream_profile(hass, setup_config_entry): """Test that Axis camera entity is using the correct path with stream profike.""" - await setup_axis_integration(hass, config_entry) - assert len(hass.states.async_entity_ids(CAMERA_DOMAIN)) == 1 entity_id = f"{CAMERA_DOMAIN}.{NAME}" @@ -75,9 +70,9 @@ async def test_camera_with_stream_profile(hass, config_entry): ) -async def test_camera_disabled(hass, config_entry): +async def test_camera_disabled(hass, prepare_config_entry): """Test that Axis camera platform is loaded properly but does not create camera entity.""" with patch("axis.vapix.vapix.Params.image_format", new=None): - await setup_axis_integration(hass, config_entry) + await prepare_config_entry() assert len(hass.states.async_entity_ids(CAMERA_DOMAIN)) == 0 diff --git a/tests/components/axis/test_config_flow.py b/tests/components/axis/test_config_flow.py index d875af94efa..d66fb3881cb 100644 --- a/tests/components/axis/test_config_flow.py +++ b/tests/components/axis/test_config_flow.py @@ -2,7 +2,6 @@ from unittest.mock import patch import pytest -import respx from homeassistant.components import dhcp, ssdp, zeroconf from homeassistant.components.axis import config_flow @@ -32,13 +31,12 @@ from homeassistant.const import ( ) from homeassistant.data_entry_flow import FlowResultType -from .conftest import DEFAULT_HOST, MAC, MODEL, NAME -from .test_device import mock_default_vapix_requests, setup_axis_integration +from .const import DEFAULT_HOST, MAC, MODEL, NAME from tests.common import MockConfigEntry -async def test_flow_manual_configuration(hass): +async def test_flow_manual_configuration(hass, setup_default_vapix_requests): """Test that config flow works.""" MockConfigEntry(domain=AXIS_DOMAIN, source=SOURCE_IGNORE).add_to_hass(hass) @@ -49,17 +47,15 @@ async def test_flow_manual_configuration(hass): assert result["type"] == FlowResultType.FORM assert result["step_id"] == SOURCE_USER - with respx.mock: - mock_default_vapix_requests(respx) - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - user_input={ - CONF_HOST: "1.2.3.4", - CONF_USERNAME: "user", - CONF_PASSWORD: "pass", - CONF_PORT: 80, - }, - ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_HOST: "1.2.3.4", + CONF_USERNAME: "user", + CONF_PASSWORD: "pass", + CONF_PORT: 80, + }, + ) assert result["type"] == FlowResultType.CREATE_ENTRY assert result["title"] == f"M1065-LW - {MAC}" @@ -73,10 +69,11 @@ async def test_flow_manual_configuration(hass): } -async def test_manual_configuration_update_configuration(hass, config_entry): +async def test_manual_configuration_update_configuration( + hass, setup_config_entry, mock_vapix_requests +): """Test that config flow fails on already configured device.""" - await setup_axis_integration(hass, config_entry) - device = hass.data[AXIS_DOMAIN][config_entry.entry_id] + device = hass.data[AXIS_DOMAIN][setup_config_entry.entry_id] result = await hass.config_entries.flow.async_init( AXIS_DOMAIN, context={"source": SOURCE_USER} @@ -86,10 +83,9 @@ async def test_manual_configuration_update_configuration(hass, config_entry): assert result["step_id"] == SOURCE_USER with patch( - "homeassistant.components.axis.async_setup_entry", - return_value=True, - ) as mock_setup_entry, respx.mock: - mock_default_vapix_requests(respx, "2.3.4.5") + "homeassistant.components.axis.async_setup_entry", return_value=True + ) as mock_setup_entry: + mock_vapix_requests("2.3.4.5") result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={ @@ -159,7 +155,9 @@ async def test_flow_fails_cannot_connect(hass): assert result["errors"] == {"base": "cannot_connect"} -async def test_flow_create_entry_multiple_existing_entries_of_same_model(hass): +async def test_flow_create_entry_multiple_existing_entries_of_same_model( + hass, setup_default_vapix_requests +): """Test that create entry can generate a name with other entries.""" entry = MockConfigEntry( domain=AXIS_DOMAIN, @@ -179,17 +177,15 @@ async def test_flow_create_entry_multiple_existing_entries_of_same_model(hass): assert result["type"] == FlowResultType.FORM assert result["step_id"] == SOURCE_USER - with respx.mock: - mock_default_vapix_requests(respx) - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - user_input={ - CONF_HOST: "1.2.3.4", - CONF_USERNAME: "user", - CONF_PASSWORD: "pass", - CONF_PORT: 80, - }, - ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_HOST: "1.2.3.4", + CONF_USERNAME: "user", + CONF_PASSWORD: "pass", + CONF_PORT: 80, + }, + ) assert result["type"] == FlowResultType.CREATE_ENTRY assert result["title"] == f"M1065-LW - {MAC}" @@ -205,32 +201,32 @@ async def test_flow_create_entry_multiple_existing_entries_of_same_model(hass): assert result["data"][CONF_NAME] == "M1065-LW 2" -async def test_reauth_flow_update_configuration(hass, config_entry): +async def test_reauth_flow_update_configuration( + hass, setup_config_entry, mock_vapix_requests +): """Test that config flow fails on already configured device.""" - await setup_axis_integration(hass, config_entry) - device = hass.data[AXIS_DOMAIN][config_entry.entry_id] + device = hass.data[AXIS_DOMAIN][setup_config_entry.entry_id] result = await hass.config_entries.flow.async_init( AXIS_DOMAIN, context={"source": SOURCE_REAUTH}, - data=config_entry.data, + data=setup_config_entry.data, ) assert result["type"] == FlowResultType.FORM assert result["step_id"] == SOURCE_USER - with respx.mock: - mock_default_vapix_requests(respx, "2.3.4.5") - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - user_input={ - CONF_HOST: "2.3.4.5", - CONF_USERNAME: "user2", - CONF_PASSWORD: "pass2", - CONF_PORT: 80, - }, - ) - await hass.async_block_till_done() + mock_vapix_requests("2.3.4.5") + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_HOST: "2.3.4.5", + CONF_USERNAME: "user2", + CONF_PASSWORD: "pass2", + CONF_PORT: 80, + }, + ) + await hass.async_block_till_done() assert result["type"] == FlowResultType.ABORT assert result["reason"] == "already_configured" @@ -303,7 +299,9 @@ async def test_reauth_flow_update_configuration(hass, config_entry): ), ], ) -async def test_discovery_flow(hass, source: str, discovery_info: dict): +async def test_discovery_flow( + hass, setup_default_vapix_requests, source: str, discovery_info: dict +): """Test the different discovery flows for new devices work.""" result = await hass.config_entries.flow.async_init( AXIS_DOMAIN, data=discovery_info, context={"source": source} @@ -316,17 +314,15 @@ async def test_discovery_flow(hass, source: str, discovery_info: dict): assert len(flows) == 1 assert flows[0].get("context", {}).get("configuration_url") == "http://1.2.3.4:80" - with respx.mock: - mock_default_vapix_requests(respx) - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - user_input={ - CONF_HOST: "1.2.3.4", - CONF_USERNAME: "user", - CONF_PASSWORD: "pass", - CONF_PORT: 80, - }, - ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_HOST: "1.2.3.4", + CONF_USERNAME: "user", + CONF_PASSWORD: "pass", + CONF_PORT: 80, + }, + ) assert result["type"] == FlowResultType.CREATE_ENTRY assert result["title"] == f"M1065-LW - {MAC}" @@ -380,11 +376,10 @@ async def test_discovery_flow(hass, source: str, discovery_info: dict): ], ) async def test_discovered_device_already_configured( - hass, config_entry, source: str, discovery_info: dict + hass, setup_config_entry, source: str, discovery_info: dict ): """Test that discovery doesn't setup already configured devices.""" - await setup_axis_integration(hass, config_entry) - assert config_entry.data[CONF_HOST] == DEFAULT_HOST + assert setup_config_entry.data[CONF_HOST] == DEFAULT_HOST result = await hass.config_entries.flow.async_init( AXIS_DOMAIN, data=discovery_info, context={"source": source} @@ -392,7 +387,7 @@ async def test_discovered_device_already_configured( assert result["type"] == FlowResultType.ABORT assert result["reason"] == "already_configured" - assert config_entry.data[CONF_HOST] == DEFAULT_HOST + assert setup_config_entry.data[CONF_HOST] == DEFAULT_HOST @pytest.mark.parametrize( @@ -436,11 +431,15 @@ async def test_discovered_device_already_configured( ], ) async def test_discovery_flow_updated_configuration( - hass, config_entry, source: str, discovery_info: dict, expected_port: int + hass, + setup_config_entry, + mock_vapix_requests, + source: str, + discovery_info: dict, + expected_port: int, ): """Test that discovery flow update configuration with new parameters.""" - await setup_axis_integration(hass, config_entry) - assert config_entry.data == { + assert setup_config_entry.data == { CONF_HOST: DEFAULT_HOST, CONF_PORT: 80, CONF_USERNAME: "root", @@ -450,10 +449,9 @@ async def test_discovery_flow_updated_configuration( } with patch( - "homeassistant.components.axis.async_setup_entry", - return_value=True, - ) as mock_setup_entry, respx.mock: - mock_default_vapix_requests(respx, "2.3.4.5") + "homeassistant.components.axis.async_setup_entry", return_value=True + ) as mock_setup_entry: + mock_vapix_requests("2.3.4.5") result = await hass.config_entries.flow.async_init( AXIS_DOMAIN, data=discovery_info, context={"source": source} ) @@ -461,7 +459,7 @@ async def test_discovery_flow_updated_configuration( assert result["type"] == FlowResultType.ABORT assert result["reason"] == "already_configured" - assert config_entry.data == { + assert setup_config_entry.data == { CONF_HOST: "2.3.4.5", CONF_PORT: expected_port, CONF_USERNAME: "root", @@ -570,16 +568,13 @@ async def test_discovery_flow_ignore_link_local_address( assert result["reason"] == "link_local_address" -async def test_option_flow(hass, config_entry): +async def test_option_flow(hass, setup_config_entry): """Test config flow options.""" - await setup_axis_integration(hass, config_entry) - device = hass.data[AXIS_DOMAIN][config_entry.entry_id] + device = hass.data[AXIS_DOMAIN][setup_config_entry.entry_id] assert device.option_stream_profile == DEFAULT_STREAM_PROFILE assert device.option_video_source == DEFAULT_VIDEO_SOURCE - with respx.mock: - mock_default_vapix_requests(respx) - result = await hass.config_entries.options.async_init(config_entry.entry_id) + result = await hass.config_entries.options.async_init(setup_config_entry.entry_id) assert result["type"] == FlowResultType.FORM assert result["step_id"] == "configure_stream" diff --git a/tests/components/axis/test_device.py b/tests/components/axis/test_device.py index 201440379ee..23b32e47cb5 100644 --- a/tests/components/axis/test_device.py +++ b/tests/components/axis/test_device.py @@ -1,11 +1,9 @@ """Test Axis device.""" -from copy import deepcopy from unittest import mock from unittest.mock import Mock, patch import axis as axislib import pytest -import respx from homeassistant.components import axis, zeroconf from homeassistant.components.axis.const import DOMAIN as AXIS_DOMAIN @@ -21,257 +19,27 @@ from homeassistant.const import ( ) from homeassistant.helpers import device_registry as dr -from .conftest import DEFAULT_HOST, ENTRY_CONFIG, FORMATTED_MAC, MAC, NAME +from .const import ( + API_DISCOVERY_BASIC_DEVICE_INFO, + API_DISCOVERY_MQTT, + FORMATTED_MAC, + MAC, + NAME, +) from tests.common import async_fire_mqtt_message -API_DISCOVERY_RESPONSE = { - "method": "getApiList", - "apiVersion": "1.0", - "data": { - "apiList": [ - {"id": "api-discovery", "version": "1.0", "name": "API Discovery Service"}, - {"id": "param-cgi", "version": "1.0", "name": "Legacy Parameter Handling"}, - ] - }, -} -API_DISCOVERY_BASIC_DEVICE_INFO = { - "id": "basic-device-info", - "version": "1.1", - "name": "Basic Device Information", -} -API_DISCOVERY_MQTT = {"id": "mqtt-client", "version": "1.0", "name": "MQTT Client API"} -API_DISCOVERY_PORT_MANAGEMENT = { - "id": "io-port-management", - "version": "1.0", - "name": "IO Port Management", -} - -APPLICATIONS_LIST_RESPONSE = """ - -""" - -BASIC_DEVICE_INFO_RESPONSE = { - "apiVersion": "1.1", - "data": { - "propertyList": { - "ProdNbr": "M1065-LW", - "ProdType": "Network Camera", - "SerialNumber": MAC, - "Version": "9.80.1", - } - }, -} - -LIGHT_CONTROL_RESPONSE = { - "apiVersion": "1.1", - "method": "getLightInformation", - "data": { - "items": [ - { - "lightID": "led0", - "lightType": "IR", - "enabled": True, - "synchronizeDayNightMode": True, - "lightState": False, - "automaticIntensityMode": False, - "automaticAngleOfIlluminationMode": False, - "nrOfLEDs": 1, - "error": False, - "errorInfo": "", - } - ] - }, -} - -MQTT_CLIENT_RESPONSE = { - "apiVersion": "1.0", - "context": "some context", - "method": "getClientStatus", - "data": {"status": {"state": "active", "connectionStatus": "Connected"}}, -} - -PORT_MANAGEMENT_RESPONSE = { - "apiVersion": "1.0", - "method": "getPorts", - "data": { - "numberOfPorts": 1, - "items": [ - { - "port": "0", - "configurable": False, - "usage": "", - "name": "PIR sensor", - "direction": "input", - "state": "open", - "normalState": "open", - } - ], - }, -} - -VMD4_RESPONSE = { - "apiVersion": "1.4", - "method": "getConfiguration", - "context": "Axis library", - "data": { - "cameras": [{"id": 1, "rotation": 0, "active": True}], - "profiles": [ - {"filters": [], "camera": 1, "triggers": [], "name": "Profile 1", "uid": 1} - ], - }, -} - -BRAND_RESPONSE = """root.Brand.Brand=AXIS -root.Brand.ProdFullName=AXIS M1065-LW Network Camera -root.Brand.ProdNbr=M1065-LW -root.Brand.ProdShortName=AXIS M1065-LW -root.Brand.ProdType=Network Camera -root.Brand.ProdVariant= -root.Brand.WebURL=http://www.axis.com -""" - -IMAGE_RESPONSE = """root.Image.I0.Enabled=yes -root.Image.I0.Name=View Area 1 -root.Image.I0.Source=0 -root.Image.I1.Enabled=no -root.Image.I1.Name=View Area 2 -root.Image.I1.Source=0 -""" - -PORTS_RESPONSE = """root.Input.NbrOfInputs=1 -root.IOPort.I0.Configurable=no -root.IOPort.I0.Direction=input -root.IOPort.I0.Input.Name=PIR sensor -root.IOPort.I0.Input.Trig=closed -root.Output.NbrOfOutputs=0 -""" - -PROPERTIES_RESPONSE = f"""root.Properties.API.HTTP.Version=3 -root.Properties.API.Metadata.Metadata=yes -root.Properties.API.Metadata.Version=1.0 -root.Properties.EmbeddedDevelopment.Version=2.16 -root.Properties.Firmware.BuildDate=Feb 15 2019 09:42 -root.Properties.Firmware.BuildNumber=26 -root.Properties.Firmware.Version=9.10.1 -root.Properties.Image.Format=jpeg,mjpeg,h264 -root.Properties.Image.NbrOfViews=2 -root.Properties.Image.Resolution=1920x1080,1280x960,1280x720,1024x768,1024x576,800x600,640x480,640x360,352x240,320x240 -root.Properties.Image.Rotation=0,180 -root.Properties.System.SerialNumber={MAC} -""" - -PTZ_RESPONSE = "" +@pytest.fixture(name="forward_entry_setup") +def hass_mock_forward_entry_setup(hass): + """Mock async_forward_entry_setup.""" + with patch.object(hass.config_entries, "async_forward_entry_setup") as forward_mock: + yield forward_mock -STREAM_PROFILES_RESPONSE = """root.StreamProfile.MaxGroups=26 -root.StreamProfile.S0.Description=profile_1_description -root.StreamProfile.S0.Name=profile_1 -root.StreamProfile.S0.Parameters=videocodec=h264 -root.StreamProfile.S1.Description=profile_2_description -root.StreamProfile.S1.Name=profile_2 -root.StreamProfile.S1.Parameters=videocodec=h265 -""" - -VIEW_AREAS_RESPONSE = {"apiVersion": "1.0", "method": "list", "data": {"viewAreas": []}} - - -def mock_default_vapix_requests(respx: respx, host: str = DEFAULT_HOST) -> None: - """Mock default Vapix requests responses.""" - respx.post(f"http://{host}:80/axis-cgi/apidiscovery.cgi").respond( - json=API_DISCOVERY_RESPONSE, - ) - respx.post(f"http://{host}:80/axis-cgi/basicdeviceinfo.cgi").respond( - json=BASIC_DEVICE_INFO_RESPONSE, - ) - respx.post(f"http://{host}:80/axis-cgi/io/portmanagement.cgi").respond( - json=PORT_MANAGEMENT_RESPONSE, - ) - respx.post(f"http://{host}:80/axis-cgi/lightcontrol.cgi").respond( - json=LIGHT_CONTROL_RESPONSE, - ) - respx.post(f"http://{host}:80/axis-cgi/mqtt/client.cgi").respond( - json=MQTT_CLIENT_RESPONSE, - ) - respx.post(f"http://{host}:80/axis-cgi/streamprofile.cgi").respond( - json=STREAM_PROFILES_RESPONSE, - ) - respx.post(f"http://{host}:80/axis-cgi/viewarea/info.cgi").respond( - json=VIEW_AREAS_RESPONSE - ) - respx.get( - f"http://{host}:80/axis-cgi/param.cgi?action=list&group=root.Brand" - ).respond( - text=BRAND_RESPONSE, - headers={"Content-Type": "text/plain"}, - ) - respx.get( - f"http://{host}:80/axis-cgi/param.cgi?action=list&group=root.Image" - ).respond( - text=IMAGE_RESPONSE, - headers={"Content-Type": "text/plain"}, - ) - respx.get( - f"http://{host}:80/axis-cgi/param.cgi?action=list&group=root.Input" - ).respond( - text=PORTS_RESPONSE, - headers={"Content-Type": "text/plain"}, - ) - respx.get( - f"http://{host}:80/axis-cgi/param.cgi?action=list&group=root.IOPort" - ).respond( - text=PORTS_RESPONSE, - headers={"Content-Type": "text/plain"}, - ) - respx.get( - f"http://{host}:80/axis-cgi/param.cgi?action=list&group=root.Output" - ).respond( - text=PORTS_RESPONSE, - headers={"Content-Type": "text/plain"}, - ) - respx.get( - f"http://{host}:80/axis-cgi/param.cgi?action=list&group=root.Properties" - ).respond( - text=PROPERTIES_RESPONSE, - headers={"Content-Type": "text/plain"}, - ) - respx.get( - f"http://{host}:80/axis-cgi/param.cgi?action=list&group=root.PTZ" - ).respond( - text=PTZ_RESPONSE, - headers={"Content-Type": "text/plain"}, - ) - respx.get( - f"http://{host}:80/axis-cgi/param.cgi?action=list&group=root.StreamProfile" - ).respond( - text=STREAM_PROFILES_RESPONSE, - headers={"Content-Type": "text/plain"}, - ) - respx.post(f"http://{host}:80/axis-cgi/applications/list.cgi").respond( - text=APPLICATIONS_LIST_RESPONSE, - headers={"Content-Type": "text/xml"}, - ) - respx.post(f"http://{host}:80/local/vmd/control.cgi").respond(json=VMD4_RESPONSE) - - -async def setup_axis_integration(hass, config_entry): - """Create the Axis device.""" - - with respx.mock: - mock_default_vapix_requests(respx) - await hass.config_entries.async_setup(config_entry.entry_id) - await hass.async_block_till_done() - - -async def test_device_setup(hass, config_entry): +async def test_device_setup(hass, forward_entry_setup, config, setup_config_entry): """Successful setup.""" - with patch( - "homeassistant.config_entries.ConfigEntries.async_forward_entry_setup", - return_value=True, - ) as forward_entry_setup: - await setup_axis_integration(hass, config_entry) - device = hass.data[AXIS_DOMAIN][config_entry.entry_id] + device = hass.data[AXIS_DOMAIN][setup_config_entry.entry_id] assert device.api.vapix.firmware_version == "9.10.1" assert device.api.vapix.product_number == "M1065-LW" @@ -279,14 +47,14 @@ async def test_device_setup(hass, config_entry): assert device.api.vapix.serial_number == "00408C123456" assert len(forward_entry_setup.mock_calls) == 4 - assert forward_entry_setup.mock_calls[0][1] == (config_entry, "binary_sensor") - assert forward_entry_setup.mock_calls[1][1] == (config_entry, "camera") - assert forward_entry_setup.mock_calls[2][1] == (config_entry, "light") - assert forward_entry_setup.mock_calls[3][1] == (config_entry, "switch") + assert forward_entry_setup.mock_calls[0][1][1] == "binary_sensor" + assert forward_entry_setup.mock_calls[1][1][1] == "camera" + assert forward_entry_setup.mock_calls[2][1][1] == "light" + assert forward_entry_setup.mock_calls[3][1][1] == "switch" - assert device.host == ENTRY_CONFIG[CONF_HOST] - assert device.model == ENTRY_CONFIG[CONF_MODEL] - assert device.name == ENTRY_CONFIG[CONF_NAME] + assert device.host == config[CONF_HOST] + assert device.model == config[CONF_MODEL] + assert device.name == config[CONF_NAME] assert device.unique_id == FORMATTED_MAC device_registry = dr.async_get(hass) @@ -297,14 +65,10 @@ async def test_device_setup(hass, config_entry): assert device_entry.configuration_url == device.api.config.url -async def test_device_info(hass, config_entry): +@pytest.mark.parametrize("api_discovery_items", [API_DISCOVERY_BASIC_DEVICE_INFO]) +async def test_device_info(hass, setup_config_entry): """Verify other path of device information works.""" - api_discovery = deepcopy(API_DISCOVERY_RESPONSE) - api_discovery["data"]["apiList"].append(API_DISCOVERY_BASIC_DEVICE_INFO) - - with patch.dict(API_DISCOVERY_RESPONSE, api_discovery): - await setup_axis_integration(hass, config_entry) - device = hass.data[AXIS_DOMAIN][config_entry.entry_id] + device = hass.data[AXIS_DOMAIN][setup_config_entry.entry_id] assert device.api.vapix.firmware_version == "9.80.1" assert device.api.vapix.product_number == "M1065-LW" @@ -312,14 +76,9 @@ async def test_device_info(hass, config_entry): assert device.api.vapix.serial_number == "00408C123456" -async def test_device_support_mqtt(hass, mqtt_mock, config_entry): +@pytest.mark.parametrize("api_discovery_items", [API_DISCOVERY_MQTT]) +async def test_device_support_mqtt(hass, mqtt_mock, setup_config_entry): """Successful setup.""" - api_discovery = deepcopy(API_DISCOVERY_RESPONSE) - api_discovery["data"]["apiList"].append(API_DISCOVERY_MQTT) - - with patch.dict(API_DISCOVERY_RESPONSE, api_discovery): - await setup_axis_integration(hass, config_entry) - mqtt_mock.async_subscribe.assert_called_with(f"{MAC}/#", mock.ANY, 0, "utf-8") topic = f"{MAC}/event/tns:onvif/Device/tns:axis/Sensor/PIR/$source/sensor/0" @@ -338,17 +97,15 @@ async def test_device_support_mqtt(hass, mqtt_mock, config_entry): assert pir.name == f"{NAME} PIR 0" -async def test_update_address(hass, config_entry): +async def test_update_address(hass, setup_config_entry, mock_vapix_requests): """Test update address works.""" - await setup_axis_integration(hass, config_entry) - device = hass.data[AXIS_DOMAIN][config_entry.entry_id] + device = hass.data[AXIS_DOMAIN][setup_config_entry.entry_id] assert device.api.config.host == "1.2.3.4" with patch( - "homeassistant.components.axis.async_setup_entry", - return_value=True, - ) as mock_setup_entry, respx.mock: - mock_default_vapix_requests(respx, "2.3.4.5") + "homeassistant.components.axis.async_setup_entry", return_value=True + ) as mock_setup_entry: + mock_vapix_requests("2.3.4.5") await hass.config_entries.flow.async_init( AXIS_DOMAIN, data=zeroconf.ZeroconfServiceInfo( @@ -369,11 +126,9 @@ async def test_update_address(hass, config_entry): async def test_device_unavailable( - hass, config_entry, mock_rtsp_event, mock_rtsp_signal_state + hass, setup_config_entry, mock_rtsp_event, mock_rtsp_signal_state ): """Successful setup.""" - await setup_axis_integration(hass, config_entry) - # Provide an entity that can be used to verify connection state on mock_rtsp_event( topic="tns1:AudioSource/tnsaxis:TriggerLevel", @@ -404,43 +159,47 @@ async def test_device_unavailable( assert hass.states.get(f"{BINARY_SENSOR_DOMAIN}.{NAME}_sound_1").state == STATE_OFF -async def test_device_reset(hass, config_entry): +async def test_device_reset(hass, setup_config_entry): """Successfully reset device.""" - await setup_axis_integration(hass, config_entry) - device = hass.data[AXIS_DOMAIN][config_entry.entry_id] + device = hass.data[AXIS_DOMAIN][setup_config_entry.entry_id] result = await device.async_reset() assert result is True -async def test_device_not_accessible(hass, config_entry): +async def test_device_not_accessible(hass, config_entry, setup_default_vapix_requests): """Failed setup schedules a retry of setup.""" with patch.object(axis, "get_axis_device", side_effect=axis.errors.CannotConnect): - await setup_axis_integration(hass, config_entry) + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() assert hass.data[AXIS_DOMAIN] == {} -async def test_device_trigger_reauth_flow(hass, config_entry): +async def test_device_trigger_reauth_flow( + hass, config_entry, setup_default_vapix_requests +): """Failed authentication trigger a reauthentication flow.""" with patch.object( axis, "get_axis_device", side_effect=axis.errors.AuthenticationRequired ), patch.object(hass.config_entries.flow, "async_init") as mock_flow_init: - await setup_axis_integration(hass, config_entry) + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() mock_flow_init.assert_called_once() assert hass.data[AXIS_DOMAIN] == {} -async def test_device_unknown_error(hass, config_entry): +async def test_device_unknown_error(hass, config_entry, setup_default_vapix_requests): """Unknown errors are handled.""" with patch.object(axis, "get_axis_device", side_effect=Exception): - await setup_axis_integration(hass, config_entry) + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() assert hass.data[AXIS_DOMAIN] == {} -async def test_shutdown(): +async def test_shutdown(config): """Successful shutdown.""" hass = Mock() entry = Mock() - entry.data = ENTRY_CONFIG + entry.data = config axis_device = axis.device.AxisNetworkDevice(hass, entry, Mock()) @@ -449,25 +208,25 @@ async def test_shutdown(): assert len(axis_device.api.stream.stop.mock_calls) == 1 -async def test_get_device_fails(hass): +async def test_get_device_fails(hass, config): """Device unauthorized yields authentication required error.""" with patch( "axis.vapix.vapix.Vapix.request", side_effect=axislib.Unauthorized ), pytest.raises(axis.errors.AuthenticationRequired): - await axis.device.get_axis_device(hass, ENTRY_CONFIG) + await axis.device.get_axis_device(hass, config) -async def test_get_device_device_unavailable(hass): +async def test_get_device_device_unavailable(hass, config): """Device unavailable yields cannot connect error.""" with patch( "axis.vapix.vapix.Vapix.request", side_effect=axislib.RequestError ), pytest.raises(axis.errors.CannotConnect): - await axis.device.get_axis_device(hass, ENTRY_CONFIG) + await axis.device.get_axis_device(hass, config) -async def test_get_device_unknown_error(hass): +async def test_get_device_unknown_error(hass, config): """Device yield unknown error.""" with patch( "axis.vapix.vapix.Vapix.request", side_effect=axislib.AxisException ), pytest.raises(axis.errors.AuthenticationRequired): - await axis.device.get_axis_device(hass, ENTRY_CONFIG) + await axis.device.get_axis_device(hass, config) diff --git a/tests/components/axis/test_diagnostics.py b/tests/components/axis/test_diagnostics.py index aad658d1ea4..7529f50bd6c 100644 --- a/tests/components/axis/test_diagnostics.py +++ b/tests/components/axis/test_diagnostics.py @@ -1,30 +1,22 @@ """Test Axis diagnostics.""" -from copy import deepcopy -from unittest.mock import patch +import pytest from homeassistant.components.diagnostics import REDACTED -from .test_device import ( - API_DISCOVERY_BASIC_DEVICE_INFO, - API_DISCOVERY_RESPONSE, - setup_axis_integration, -) +from .const import API_DISCOVERY_BASIC_DEVICE_INFO from tests.components.diagnostics import get_diagnostics_for_config_entry -async def test_entry_diagnostics(hass, hass_client, config_entry): +@pytest.mark.parametrize("api_discovery_items", [API_DISCOVERY_BASIC_DEVICE_INFO]) +async def test_entry_diagnostics(hass, hass_client, setup_config_entry): """Test config entry diagnostics.""" - api_discovery = deepcopy(API_DISCOVERY_RESPONSE) - api_discovery["data"]["apiList"].append(API_DISCOVERY_BASIC_DEVICE_INFO) - - with patch.dict(API_DISCOVERY_RESPONSE, api_discovery): - await setup_axis_integration(hass, config_entry) - - assert await get_diagnostics_for_config_entry(hass, hass_client, config_entry) == { + assert await get_diagnostics_for_config_entry( + hass, hass_client, setup_config_entry + ) == { "config": { - "entry_id": config_entry.entry_id, + "entry_id": setup_config_entry.entry_id, "version": 3, "domain": "axis", "title": "Mock Title", diff --git a/tests/components/axis/test_init.py b/tests/components/axis/test_init.py index 72af4c42f43..fe504f7d15b 100644 --- a/tests/components/axis/test_init.py +++ b/tests/components/axis/test_init.py @@ -7,8 +7,6 @@ from homeassistant.components import axis from homeassistant.components.axis.const import DOMAIN as AXIS_DOMAIN from homeassistant.setup import async_setup_component -from .test_device import setup_axis_integration - async def test_setup_no_config(hass): """Test setup without configuration.""" @@ -16,11 +14,10 @@ async def test_setup_no_config(hass): assert AXIS_DOMAIN not in hass.data -async def test_setup_entry(hass, config_entry): +async def test_setup_entry(hass, setup_config_entry): """Test successful setup of entry.""" - await setup_axis_integration(hass, config_entry) assert len(hass.data[AXIS_DOMAIN]) == 1 - assert config_entry.entry_id in hass.data[AXIS_DOMAIN] + assert setup_config_entry.entry_id in hass.data[AXIS_DOMAIN] async def test_setup_entry_fails(hass, config_entry): @@ -36,12 +33,11 @@ async def test_setup_entry_fails(hass, config_entry): assert not hass.data[AXIS_DOMAIN] -async def test_unload_entry(hass, config_entry): +async def test_unload_entry(hass, setup_config_entry): """Test successful unload of entry.""" - await setup_axis_integration(hass, config_entry) assert hass.data[AXIS_DOMAIN] - assert await hass.config_entries.async_unload(config_entry.entry_id) + assert await hass.config_entries.async_unload(setup_config_entry.entry_id) assert not hass.data[AXIS_DOMAIN] diff --git a/tests/components/axis/test_light.py b/tests/components/axis/test_light.py index 7d990f6ea9c..7550e19e884 100644 --- a/tests/components/axis/test_light.py +++ b/tests/components/axis/test_light.py @@ -1,8 +1,10 @@ """Axis light platform tests.""" -from copy import deepcopy from unittest.mock import patch +import pytest +import respx + from homeassistant.components.axis.const import DOMAIN as AXIS_DOMAIN from homeassistant.components.light import ATTR_BRIGHTNESS, DOMAIN as LIGHT_DOMAIN from homeassistant.const import ( @@ -14,12 +16,7 @@ from homeassistant.const import ( ) from homeassistant.setup import async_setup_component -from .conftest import NAME -from .test_device import ( - API_DISCOVERY_RESPONSE, - LIGHT_CONTROL_RESPONSE, - setup_axis_integration, -) +from .const import DEFAULT_HOST, NAME API_DISCOVERY_LIGHT_CONTROL = { "id": "light-control", @@ -28,6 +25,38 @@ API_DISCOVERY_LIGHT_CONTROL = { } +@pytest.fixture() +def light_control_items(): + """Available lights.""" + return [ + { + "lightID": "led0", + "lightType": "IR", + "enabled": True, + "synchronizeDayNightMode": True, + "lightState": False, + "automaticIntensityMode": False, + "automaticAngleOfIlluminationMode": False, + "nrOfLEDs": 1, + "error": False, + "errorInfo": "", + } + ] + + +@pytest.fixture(autouse=True) +def light_control_fixture(light_control_items): + """Light control mock response.""" + data = { + "apiVersion": "1.1", + "method": "getLightInformation", + "data": {"items": light_control_items}, + } + respx.post(f"http://{DEFAULT_HOST}:80/axis-cgi/lightcontrol.cgi").respond( + json=data, + ) + + async def test_platform_manually_configured(hass): """Test that nothing happens when platform is manually configured.""" assert await async_setup_component( @@ -37,28 +66,17 @@ async def test_platform_manually_configured(hass): assert AXIS_DOMAIN not in hass.data -async def test_no_lights(hass, config_entry): +async def test_no_lights(hass, setup_config_entry): """Test that no light events in Axis results in no light entities.""" - await setup_axis_integration(hass, config_entry) - assert not hass.states.async_entity_ids(LIGHT_DOMAIN) +@pytest.mark.parametrize("api_discovery_items", [API_DISCOVERY_LIGHT_CONTROL]) +@pytest.mark.parametrize("light_control_items", [[]]) async def test_no_light_entity_without_light_control_representation( - hass, config_entry, mock_rtsp_event + hass, setup_config_entry, mock_rtsp_event ): """Verify no lights entities get created without light control representation.""" - api_discovery = deepcopy(API_DISCOVERY_RESPONSE) - api_discovery["data"]["apiList"].append(API_DISCOVERY_LIGHT_CONTROL) - - light_control = deepcopy(LIGHT_CONTROL_RESPONSE) - light_control["data"]["items"] = [] - - with patch.dict(API_DISCOVERY_RESPONSE, api_discovery), patch.dict( - LIGHT_CONTROL_RESPONSE, light_control - ): - await setup_axis_integration(hass, config_entry) - mock_rtsp_event( topic="tns1:Device/tnsaxis:Light/Status", data_type="state", @@ -71,14 +89,9 @@ async def test_no_light_entity_without_light_control_representation( assert not hass.states.async_entity_ids(LIGHT_DOMAIN) -async def test_lights(hass, config_entry, mock_rtsp_event): +@pytest.mark.parametrize("api_discovery_items", [API_DISCOVERY_LIGHT_CONTROL]) +async def test_lights(hass, setup_config_entry, mock_rtsp_event): """Test that lights are loaded properly.""" - api_discovery = deepcopy(API_DISCOVERY_RESPONSE) - api_discovery["data"]["apiList"].append(API_DISCOVERY_LIGHT_CONTROL) - - with patch.dict(API_DISCOVERY_RESPONSE, api_discovery): - await setup_axis_integration(hass, config_entry) - # Add light with patch( "axis.vapix.interfaces.light_control.LightControl.get_current_intensity", diff --git a/tests/components/axis/test_switch.py b/tests/components/axis/test_switch.py index 40ecb02a68f..741aa6f1b1a 100644 --- a/tests/components/axis/test_switch.py +++ b/tests/components/axis/test_switch.py @@ -1,7 +1,8 @@ """Axis switch platform tests.""" -from copy import deepcopy -from unittest.mock import AsyncMock, patch +from unittest.mock import AsyncMock + +import pytest from homeassistant.components.axis.const import DOMAIN as AXIS_DOMAIN from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN @@ -14,12 +15,7 @@ from homeassistant.const import ( ) from homeassistant.setup import async_setup_component -from .conftest import NAME -from .test_device import ( - API_DISCOVERY_PORT_MANAGEMENT, - API_DISCOVERY_RESPONSE, - setup_axis_integration, -) +from .const import API_DISCOVERY_PORT_MANAGEMENT, NAME async def test_platform_manually_configured(hass): @@ -31,17 +27,14 @@ async def test_platform_manually_configured(hass): assert AXIS_DOMAIN not in hass.data -async def test_no_switches(hass, config_entry): +async def test_no_switches(hass, setup_config_entry): """Test that no output events in Axis results in no switch entities.""" - await setup_axis_integration(hass, config_entry) - assert not hass.states.async_entity_ids(SWITCH_DOMAIN) -async def test_switches_with_port_cgi(hass, config_entry, mock_rtsp_event): +async def test_switches_with_port_cgi(hass, setup_config_entry, mock_rtsp_event): """Test that switches are loaded properly using port.cgi.""" - await setup_axis_integration(hass, config_entry) - device = hass.data[AXIS_DOMAIN][config_entry.entry_id] + device = hass.data[AXIS_DOMAIN][setup_config_entry.entry_id] device.api.vapix.ports = {"0": AsyncMock(), "1": AsyncMock()} device.api.vapix.ports["0"].name = "Doorbell" @@ -94,14 +87,10 @@ async def test_switches_with_port_cgi(hass, config_entry, mock_rtsp_event): device.api.vapix.ports["0"].open.assert_called_once() -async def test_switches_with_port_management(hass, config_entry, mock_rtsp_event): +@pytest.mark.parametrize("api_discovery_items", [API_DISCOVERY_PORT_MANAGEMENT]) +async def test_switches_with_port_management(hass, setup_config_entry, mock_rtsp_event): """Test that switches are loaded properly using port management.""" - api_discovery = deepcopy(API_DISCOVERY_RESPONSE) - api_discovery["data"]["apiList"].append(API_DISCOVERY_PORT_MANAGEMENT) - - with patch.dict(API_DISCOVERY_RESPONSE, api_discovery): - await setup_axis_integration(hass, config_entry) - device = hass.data[AXIS_DOMAIN][config_entry.entry_id] + device = hass.data[AXIS_DOMAIN][setup_config_entry.entry_id] device.api.vapix.ports = {"0": AsyncMock(), "1": AsyncMock()} device.api.vapix.ports["0"].name = "Doorbell"