Compare commits

...

2 Commits

Author SHA1 Message Date
mib1185
653a126791 use autospec 2025-11-25 18:45:09 +00:00
mib1185
05917a9fcd add tests for the switch platform 2025-11-23 13:51:58 +00:00
3 changed files with 472 additions and 8 deletions

View File

@@ -2,7 +2,14 @@
from unittest.mock import AsyncMock
from adguardhome.update import AdGuardHomeAvailableUpdate
from adguardhome import AdGuardHome
from adguardhome.filtering import AdGuardHomeFiltering
from adguardhome.parental import AdGuardHomeParental
from adguardhome.querylog import AdGuardHomeQueryLog
from adguardhome.safebrowsing import AdGuardHomeSafeBrowsing
from adguardhome.safesearch import AdGuardHomeSafeSearch
from adguardhome.stats import AdGuardHomeStats
from adguardhome.update import AdGuardHomeAvailableUpdate, AdGuardHomeUpdate
import pytest
from homeassistant.components.adguard import DOMAIN
@@ -38,7 +45,14 @@ def mock_config_entry() -> MockConfigEntry:
@pytest.fixture
async def mock_adguard() -> AsyncMock:
"""Fixture for setting up the component."""
adguard_mock = AsyncMock()
adguard_mock = AsyncMock(spec=AdGuardHome)
adguard_mock.filtering = AsyncMock(spec=AdGuardHomeFiltering)
adguard_mock.parental = AsyncMock(spec=AdGuardHomeParental)
adguard_mock.querylog = AsyncMock(spec=AdGuardHomeQueryLog)
adguard_mock.safebrowsing = AsyncMock(spec=AdGuardHomeSafeBrowsing)
adguard_mock.safesearch = AsyncMock(spec=AdGuardHomeSafeSearch)
adguard_mock.stats = AsyncMock(spec=AdGuardHomeStats)
adguard_mock.update = AsyncMock(spec=AdGuardHomeUpdate)
# static properties
adguard_mock.host = "127.0.0.1"
@@ -48,6 +62,10 @@ async def mock_adguard() -> AsyncMock:
# async method mocks
adguard_mock.version = AsyncMock(return_value="v0.107.50")
adguard_mock.protection_enabled = AsyncMock(return_value=True)
adguard_mock.parental.enabled = AsyncMock(return_value=True)
adguard_mock.safesearch.enabled = AsyncMock(return_value=True)
adguard_mock.safebrowsing.enabled = AsyncMock(return_value=True)
adguard_mock.stats.dns_queries = AsyncMock(return_value=666)
adguard_mock.stats.blocked_filtering = AsyncMock(return_value=1337)
adguard_mock.stats.blocked_percentage = AsyncMock(return_value=200.75)
@@ -56,11 +74,8 @@ async def mock_adguard() -> AsyncMock:
adguard_mock.stats.replaced_safesearch = AsyncMock(return_value=18)
adguard_mock.stats.avg_processing_time = AsyncMock(return_value=31.41)
adguard_mock.filtering.rules_count = AsyncMock(return_value=100)
adguard_mock.filtering.add_url = AsyncMock()
adguard_mock.filtering.remove_url = AsyncMock()
adguard_mock.filtering.enable_url = AsyncMock()
adguard_mock.filtering.disable_url = AsyncMock()
adguard_mock.filtering.refresh = AsyncMock()
adguard_mock.filtering.enabled = AsyncMock(return_value=True)
adguard_mock.querylog.enabled = AsyncMock(return_value=True)
adguard_mock.update.update_available = AsyncMock(
return_value=AdGuardHomeAvailableUpdate(
new_version="v0.107.59",
@@ -70,6 +85,5 @@ async def mock_adguard() -> AsyncMock:
disabled=False,
)
)
adguard_mock.update.begin_update = AsyncMock()
return adguard_mock

View File

@@ -0,0 +1,289 @@
# serializer version: 1
# name: test_switch[switch.adguard_home_filtering-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.adguard_home_filtering',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Filtering',
'platform': 'adguard',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'filtering',
'unique_id': 'adguard_127.0.0.1_3000_switch_filtering',
'unit_of_measurement': None,
})
# ---
# name: test_switch[switch.adguard_home_filtering-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'AdGuard Home Filtering',
}),
'context': <ANY>,
'entity_id': 'switch.adguard_home_filtering',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_switch[switch.adguard_home_parental_control-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.adguard_home_parental_control',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Parental control',
'platform': 'adguard',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'parental',
'unique_id': 'adguard_127.0.0.1_3000_switch_parental',
'unit_of_measurement': None,
})
# ---
# name: test_switch[switch.adguard_home_parental_control-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'AdGuard Home Parental control',
}),
'context': <ANY>,
'entity_id': 'switch.adguard_home_parental_control',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_switch[switch.adguard_home_protection-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.adguard_home_protection',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Protection',
'platform': 'adguard',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'protection',
'unique_id': 'adguard_127.0.0.1_3000_switch_protection',
'unit_of_measurement': None,
})
# ---
# name: test_switch[switch.adguard_home_protection-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'AdGuard Home Protection',
}),
'context': <ANY>,
'entity_id': 'switch.adguard_home_protection',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_switch[switch.adguard_home_query_log-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.adguard_home_query_log',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Query log',
'platform': 'adguard',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'query_log',
'unique_id': 'adguard_127.0.0.1_3000_switch_querylog',
'unit_of_measurement': None,
})
# ---
# name: test_switch[switch.adguard_home_query_log-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'AdGuard Home Query log',
}),
'context': <ANY>,
'entity_id': 'switch.adguard_home_query_log',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_switch[switch.adguard_home_safe_browsing-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.adguard_home_safe_browsing',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Safe browsing',
'platform': 'adguard',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'safe_browsing',
'unique_id': 'adguard_127.0.0.1_3000_switch_safebrowsing',
'unit_of_measurement': None,
})
# ---
# name: test_switch[switch.adguard_home_safe_browsing-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'AdGuard Home Safe browsing',
}),
'context': <ANY>,
'entity_id': 'switch.adguard_home_safe_browsing',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_switch[switch.adguard_home_safe_search-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.adguard_home_safe_search',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Safe search',
'platform': 'adguard',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'safe_search',
'unique_id': 'adguard_127.0.0.1_3000_switch_safesearch',
'unit_of_measurement': None,
})
# ---
# name: test_switch[switch.adguard_home_safe_search-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'AdGuard Home Safe search',
}),
'context': <ANY>,
'entity_id': 'switch.adguard_home_safe_search',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---

View File

@@ -0,0 +1,161 @@
"""Tests for the AdGuard Home switch entity."""
from collections.abc import Callable
import logging
from typing import Any
from unittest.mock import AsyncMock, patch
from adguardhome import AdGuardHomeError
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.components.switch import SERVICE_TURN_OFF, SERVICE_TURN_ON
from homeassistant.const import ATTR_ENTITY_ID, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import setup_integration
from tests.common import MockConfigEntry, snapshot_platform
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_switch(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
mock_adguard: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test the adguard switch platform."""
with patch("homeassistant.components.adguard.PLATFORMS", [Platform.SWITCH]):
await setup_integration(hass, mock_config_entry, mock_adguard)
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
@pytest.mark.parametrize(
("switch_name", "service", "call_assertion"),
[
(
"protection",
SERVICE_TURN_ON,
lambda mock: mock.enable_protection.assert_called_once(),
),
(
"protection",
SERVICE_TURN_OFF,
lambda mock: mock.disable_protection.assert_called_once(),
),
(
"parental_control",
SERVICE_TURN_ON,
lambda mock: mock.parental.enable.assert_called_once(),
),
(
"parental_control",
SERVICE_TURN_OFF,
lambda mock: mock.parental.disable.assert_called_once(),
),
(
"safe_search",
SERVICE_TURN_ON,
lambda mock: mock.safesearch.enable.assert_called_once(),
),
(
"safe_search",
SERVICE_TURN_OFF,
lambda mock: mock.safesearch.disable.assert_called_once(),
),
(
"safe_browsing",
SERVICE_TURN_ON,
lambda mock: mock.safebrowsing.enable.assert_called_once(),
),
(
"safe_browsing",
SERVICE_TURN_OFF,
lambda mock: mock.safebrowsing.disable.assert_called_once(),
),
(
"filtering",
SERVICE_TURN_ON,
lambda mock: mock.filtering.enable.assert_called_once(),
),
(
"filtering",
SERVICE_TURN_OFF,
lambda mock: mock.filtering.disable.assert_called_once(),
),
(
"query_log",
SERVICE_TURN_ON,
lambda mock: mock.querylog.enable.assert_called_once(),
),
(
"query_log",
SERVICE_TURN_OFF,
lambda mock: mock.querylog.disable.assert_called_once(),
),
],
)
async def test_switch_actions(
hass: HomeAssistant,
mock_adguard: AsyncMock,
mock_config_entry: MockConfigEntry,
switch_name: str,
service: str,
call_assertion: Callable[[AsyncMock], Any],
) -> None:
"""Test the adguard switch actions."""
with patch("homeassistant.components.adguard.PLATFORMS", [Platform.SWITCH]):
await setup_integration(hass, mock_config_entry, mock_adguard)
await hass.services.async_call(
"switch",
service,
{ATTR_ENTITY_ID: f"switch.adguard_home_{switch_name}"},
blocking=True,
)
call_assertion(mock_adguard)
@pytest.mark.parametrize(
("service", "expected_message"),
[
(
SERVICE_TURN_ON,
"An error occurred while turning on AdGuard Home switch",
),
(
SERVICE_TURN_OFF,
"An error occurred while turning off AdGuard Home switch",
),
],
)
async def test_switch_action_failed(
hass: HomeAssistant,
mock_adguard: AsyncMock,
mock_config_entry: MockConfigEntry,
caplog: pytest.LogCaptureFixture,
service: str,
expected_message: str,
) -> None:
"""Test the adguard switch actions."""
caplog.set_level(logging.ERROR)
with patch("homeassistant.components.adguard.PLATFORMS", [Platform.SWITCH]):
await setup_integration(hass, mock_config_entry, mock_adguard)
mock_adguard.enable_protection.side_effect = AdGuardHomeError("Boom")
mock_adguard.disable_protection.side_effect = AdGuardHomeError("Boom")
await hass.services.async_call(
"switch",
service,
{ATTR_ENTITY_ID: "switch.adguard_home_protection"},
blocking=True,
)
assert expected_message in caplog.text