From 025fe51322824d34fc3f69b3c6bab63d5a183b7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Fri, 2 Feb 2024 09:36:26 +0100 Subject: [PATCH] Use a mocked API client in Traccar Server tests (#109358) --- tests/components/traccar_server/conftest.py | 77 ++++++++++- .../traccar_server/fixtures/devices.json | 17 +++ .../traccar_server/fixtures/geofences.json | 10 ++ .../traccar_server/fixtures/positions.json | 24 ++++ .../fixtures/reports_events.json | 12 ++ .../traccar_server/fixtures/server.json | 21 +++ .../traccar_server/test_config_flow.py | 124 ++++++++---------- 7 files changed, 210 insertions(+), 75 deletions(-) create mode 100644 tests/components/traccar_server/fixtures/devices.json create mode 100644 tests/components/traccar_server/fixtures/geofences.json create mode 100644 tests/components/traccar_server/fixtures/positions.json create mode 100644 tests/components/traccar_server/fixtures/reports_events.json create mode 100644 tests/components/traccar_server/fixtures/server.json diff --git a/tests/components/traccar_server/conftest.py b/tests/components/traccar_server/conftest.py index 4141b28849c..10cc6192d38 100644 --- a/tests/components/traccar_server/conftest.py +++ b/tests/components/traccar_server/conftest.py @@ -3,12 +3,79 @@ from collections.abc import Generator from unittest.mock import AsyncMock, patch import pytest +from pytraccar import ApiClient + +from homeassistant.components.traccar_server.const import ( + CONF_CUSTOM_ATTRIBUTES, + CONF_EVENTS, + CONF_MAX_ACCURACY, + CONF_SKIP_ACCURACY_FILTER_FOR, + DOMAIN, +) +from homeassistant.const import ( + CONF_HOST, + CONF_PASSWORD, + CONF_PORT, + CONF_SSL, + CONF_USERNAME, + CONF_VERIFY_SSL, +) + +from tests.common import ( + MockConfigEntry, + load_json_array_fixture, + load_json_object_fixture, +) @pytest.fixture -def mock_setup_entry() -> Generator[AsyncMock, None, None]: - """Override async_setup_entry.""" +def mock_traccar_api_client() -> Generator[AsyncMock, None, None]: + """Mock a Traccar ApiClient client.""" with patch( - "homeassistant.components.traccar_server.async_setup_entry", return_value=True - ) as mock_setup_entry: - yield mock_setup_entry + "homeassistant.components.traccar_server.ApiClient", + autospec=True, + ) as mock_client, patch( + "homeassistant.components.traccar_server.config_flow.ApiClient", + new=mock_client, + ): + client: ApiClient = mock_client.return_value + client.get_devices.return_value = load_json_array_fixture( + "traccar_server/devices.json" + ) + client.get_geofences.return_value = load_json_array_fixture( + "traccar_server/geofences.json" + ) + client.get_positions.return_value = load_json_array_fixture( + "traccar_server/positions.json" + ) + client.get_server.return_value = load_json_object_fixture( + "traccar_server/server.json" + ) + client.get_reports_events.return_value = load_json_array_fixture( + "traccar_server/reports_events.json" + ) + + yield client + + +@pytest.fixture +def mock_config_entry() -> MockConfigEntry: + """Mock a Traccar Server config entry.""" + return MockConfigEntry( + domain=DOMAIN, + title="1.1.1.1:8082", + data={ + CONF_HOST: "1.1.1.1", + CONF_PORT: "8082", + CONF_USERNAME: "test@example.org", + CONF_PASSWORD: "ThisIsNotThePasswordYouAreL00kingFor", + CONF_SSL: False, + CONF_VERIFY_SSL: True, + }, + options={ + CONF_CUSTOM_ATTRIBUTES: ["custom_attr_1"], + CONF_EVENTS: ["device_moving"], + CONF_MAX_ACCURACY: 5.0, + CONF_SKIP_ACCURACY_FILTER_FOR: [], + }, + ) diff --git a/tests/components/traccar_server/fixtures/devices.json b/tests/components/traccar_server/fixtures/devices.json new file mode 100644 index 00000000000..b04d53d9fdf --- /dev/null +++ b/tests/components/traccar_server/fixtures/devices.json @@ -0,0 +1,17 @@ +[ + { + "id": 0, + "name": "X-Wing", + "uniqueId": "abc123", + "status": "unknown", + "disabled": false, + "lastUpdate": "1970-01-01T00:00:00Z", + "positionId": 0, + "groupId": 0, + "phone": null, + "model": "1337", + "contact": null, + "category": "starfighter", + "attributes": {} + } +] diff --git a/tests/components/traccar_server/fixtures/geofences.json b/tests/components/traccar_server/fixtures/geofences.json new file mode 100644 index 00000000000..5452c0485de --- /dev/null +++ b/tests/components/traccar_server/fixtures/geofences.json @@ -0,0 +1,10 @@ +[ + { + "id": 0, + "name": "Tatooine", + "description": "A harsh desert world orbiting twin suns in the galaxy's Outer Rim", + "area": "string", + "calendarId": 0, + "attributes": {} + } +] diff --git a/tests/components/traccar_server/fixtures/positions.json b/tests/components/traccar_server/fixtures/positions.json new file mode 100644 index 00000000000..6b65116e804 --- /dev/null +++ b/tests/components/traccar_server/fixtures/positions.json @@ -0,0 +1,24 @@ +[ + { + "id": 0, + "deviceId": 0, + "protocol": "C-3PO", + "deviceTime": "1970-01-01T00:00:00Z", + "fixTime": "1970-01-01T00:00:00Z", + "serverTime": "1970-01-01T00:00:00Z", + "outdated": true, + "valid": true, + "latitude": 52.0, + "longitude": 25.0, + "altitude": 546841384638, + "speed": 4568795, + "course": 360, + "address": "Mos Espa", + "accuracy": 3.5, + "network": {}, + "geofenceIds": [0], + "attributes": { + "custom_attr_1": "custom_attr_1_value" + } + } +] diff --git a/tests/components/traccar_server/fixtures/reports_events.json b/tests/components/traccar_server/fixtures/reports_events.json new file mode 100644 index 00000000000..e8280471d96 --- /dev/null +++ b/tests/components/traccar_server/fixtures/reports_events.json @@ -0,0 +1,12 @@ +[ + { + "id": 0, + "type": "deviceMoving", + "eventTime": "2019-08-24T14:15:22Z", + "deviceId": 0, + "positionId": 0, + "geofenceId": 0, + "maintenanceId": 0, + "attributes": {} + } +] diff --git a/tests/components/traccar_server/fixtures/server.json b/tests/components/traccar_server/fixtures/server.json new file mode 100644 index 00000000000..039b6bfa1f4 --- /dev/null +++ b/tests/components/traccar_server/fixtures/server.json @@ -0,0 +1,21 @@ +{ + "id": 0, + "registration": true, + "readonly": true, + "deviceReadonly": true, + "limitCommands": true, + "map": null, + "bingKey": null, + "mapUrl": null, + "poiLayer": null, + "latitude": 0, + "longitude": 0, + "zoom": 0, + "twelveHourFormat": true, + "version": "99.99", + "forceSettings": true, + "coordinateFormat": null, + "openIdEnabled": true, + "openIdForce": true, + "attributes": {} +} diff --git a/tests/components/traccar_server/test_config_flow.py b/tests/components/traccar_server/test_config_flow.py index 028bc99cec5..00a987a4711 100644 --- a/tests/components/traccar_server/test_config_flow.py +++ b/tests/components/traccar_server/test_config_flow.py @@ -1,6 +1,7 @@ """Test the Traccar Server config flow.""" +from collections.abc import Generator from typing import Any -from unittest.mock import AsyncMock, patch +from unittest.mock import AsyncMock import pytest from pytraccar import TraccarException @@ -29,7 +30,10 @@ from homeassistant.data_entry_flow import FlowResultType from tests.common import MockConfigEntry -async def test_form(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> None: +async def test_form( + hass: HomeAssistant, + mock_traccar_api_client: Generator[AsyncMock, None, None], +) -> None: """Test we get the form.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -37,19 +41,15 @@ async def test_form(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> None: assert result["type"] == FlowResultType.FORM assert result["errors"] == {} - with patch( - "homeassistant.components.traccar_server.config_flow.ApiClient.get_server", - return_value={"id": "1234"}, - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - CONF_HOST: "1.1.1.1", - CONF_USERNAME: "test-username", - CONF_PASSWORD: "test-password", - }, - ) - await hass.async_block_till_done() + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_HOST: "1.1.1.1", + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password", + }, + ) + await hass.async_block_till_done() assert result["type"] == FlowResultType.CREATE_ENTRY assert result["title"] == "1.1.1.1:8082" @@ -61,7 +61,7 @@ async def test_form(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> None: CONF_SSL: False, CONF_VERIFY_SSL: True, } - assert len(mock_setup_entry.mock_calls) == 1 + assert result["result"].state == config_entries.ConfigEntryState.LOADED @pytest.mark.parametrize( @@ -73,44 +73,40 @@ async def test_form(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> None: ) async def test_form_cannot_connect( hass: HomeAssistant, - mock_setup_entry: AsyncMock, side_effect: Exception, error: str, + mock_traccar_api_client: Generator[AsyncMock, None, None], ) -> None: """Test we handle cannot connect error.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) - with patch( - "homeassistant.components.traccar_server.config_flow.ApiClient.get_server", - side_effect=side_effect, - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - CONF_HOST: "1.1.1.1", - CONF_USERNAME: "test-username", - CONF_PASSWORD: "test-password", - }, - ) + mock_traccar_api_client.get_server.side_effect = side_effect + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_HOST: "1.1.1.1", + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password", + }, + ) assert result["type"] == FlowResultType.FORM assert result["errors"] == {"base": error} - with patch( - "homeassistant.components.traccar_server.config_flow.ApiClient.get_server", - return_value={"id": "1234"}, - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - CONF_HOST: "1.1.1.1", - CONF_USERNAME: "test-username", - CONF_PASSWORD: "test-password", - }, - ) - await hass.async_block_till_done() + mock_traccar_api_client.get_server.side_effect = None + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_HOST: "1.1.1.1", + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password", + }, + ) + await hass.async_block_till_done() assert result["type"] == FlowResultType.CREATE_ENTRY assert result["title"] == "1.1.1.1:8082" @@ -122,27 +118,23 @@ async def test_form_cannot_connect( CONF_SSL: False, CONF_VERIFY_SSL: True, } - assert len(mock_setup_entry.mock_calls) == 1 + + assert result["result"].state == config_entries.ConfigEntryState.LOADED async def test_options( hass: HomeAssistant, - mock_setup_entry: AsyncMock, + mock_config_entry: MockConfigEntry, + mock_traccar_api_client: Generator[AsyncMock, None, None], ) -> None: """Test options flow.""" + mock_config_entry.add_to_hass(hass) - config_entry = MockConfigEntry( - domain=DOMAIN, - data={}, - ) + assert await hass.config_entries.async_setup(mock_config_entry.entry_id) - config_entry.add_to_hass(hass) + assert mock_config_entry.options.get(CONF_MAX_ACCURACY) == 5.0 - assert await hass.config_entries.async_setup(config_entry.entry_id) - - assert CONF_MAX_ACCURACY not in config_entry.options - - result = await hass.config_entries.options.async_init(config_entry.entry_id) + result = await hass.config_entries.options.async_init(mock_config_entry.entry_id) result = await hass.config_entries.options.async_configure( result["flow_id"], @@ -151,7 +143,7 @@ async def test_options( await hass.async_block_till_done() assert result["type"] == FlowResultType.CREATE_ENTRY - assert config_entry.options == { + assert mock_config_entry.options == { CONF_MAX_ACCURACY: 2.0, CONF_EVENTS: [], CONF_CUSTOM_ATTRIBUTES: [], @@ -234,10 +226,10 @@ async def test_options( ) async def test_import_from_yaml( hass: HomeAssistant, - mock_setup_entry: AsyncMock, imported: dict[str, Any], data: dict[str, Any], options: dict[str, Any], + mock_traccar_api_client: Generator[AsyncMock, None, None], ) -> None: """Test importing configuration from YAML.""" result = await hass.config_entries.flow.async_init( @@ -249,12 +241,10 @@ async def test_import_from_yaml( assert result["title"] == f"{data[CONF_HOST]}:{data[CONF_PORT]}" assert result["data"] == data assert result["options"] == options + assert result["result"].state == config_entries.ConfigEntryState.LOADED -async def test_abort_import_already_configured( - hass: HomeAssistant, - mock_setup_entry: AsyncMock, -) -> None: +async def test_abort_import_already_configured(hass: HomeAssistant) -> None: """Test abort for existing server while importing.""" config_entry = MockConfigEntry( @@ -284,18 +274,12 @@ async def test_abort_import_already_configured( async def test_abort_already_configured( hass: HomeAssistant, - mock_setup_entry: AsyncMock, + mock_config_entry: MockConfigEntry, + mock_traccar_api_client: Generator[AsyncMock, None, None], ) -> None: """Test abort for existing server.""" - - config_entry = MockConfigEntry( - domain=DOMAIN, - data={CONF_HOST: "1.1.1.1", CONF_PORT: "8082"}, - ) - - config_entry.add_to_hass(hass) - - assert await hass.config_entries.async_setup(config_entry.entry_id) + mock_config_entry.add_to_hass(hass) + assert await hass.config_entries.async_setup(mock_config_entry.entry_id) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER}