Add initial tuya snapshot tests (#148034)

Co-authored-by: Franck Nijhof <git@frenck.dev>
This commit is contained in:
epenet 2025-07-05 09:54:54 +02:00 committed by GitHub
parent 0d54e75940
commit 7898e3f0fb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 618 additions and 7 deletions

View File

@ -1 +1,33 @@
"""Tests for the Tuya component."""
from __future__ import annotations
from unittest.mock import patch
from tuya_sharing import CustomerDevice
from homeassistant.components.tuya import ManagerCompat
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
async def initialize_entry(
hass: HomeAssistant,
mock_manager: ManagerCompat,
mock_config_entry: MockConfigEntry,
mock_device: CustomerDevice,
) -> None:
"""Initialize the Tuya component with a mock manager and config entry."""
# Setup
mock_manager.device_map = {
mock_device.id: mock_device,
}
mock_config_entry.add_to_hass(hass)
# Initialize the component
with patch(
"homeassistant.components.tuya.ManagerCompat", return_value=mock_manager
):
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()

View File

@ -6,10 +6,20 @@ from collections.abc import Generator
from unittest.mock import MagicMock, patch
import pytest
from tuya_sharing import CustomerDevice
from homeassistant.components.tuya.const import CONF_APP_TYPE, CONF_USER_CODE, DOMAIN
from homeassistant.components.tuya import ManagerCompat
from homeassistant.components.tuya.const import (
CONF_APP_TYPE,
CONF_ENDPOINT,
CONF_TERMINAL_ID,
CONF_TOKEN_INFO,
CONF_USER_CODE,
DOMAIN,
)
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
from tests.common import MockConfigEntry, async_load_json_object_fixture
@pytest.fixture
@ -25,15 +35,44 @@ def mock_old_config_entry() -> MockConfigEntry:
@pytest.fixture
def mock_config_entry() -> MockConfigEntry:
"""Mock an config entry."""
"""Mock a config entry."""
return MockConfigEntry(
title="12345",
title="Test Tuya entry",
domain=DOMAIN,
data={CONF_USER_CODE: "12345"},
data={
CONF_ENDPOINT: "test_endpoint",
CONF_TERMINAL_ID: "test_terminal",
CONF_TOKEN_INFO: "test_token",
CONF_USER_CODE: "test_user_code",
},
unique_id="12345",
)
@pytest.fixture
async def mock_loaded_entry(
hass: HomeAssistant,
mock_manager: ManagerCompat,
mock_config_entry: MockConfigEntry,
mock_device: CustomerDevice,
) -> MockConfigEntry:
"""Mock a config entry."""
# Setup
mock_manager.device_map = {
mock_device.id: mock_device,
}
mock_config_entry.add_to_hass(hass)
# Initialize the component
with (
patch("homeassistant.components.tuya.ManagerCompat", return_value=mock_manager),
):
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
return mock_config_entry
@pytest.fixture
def mock_setup_entry() -> Generator[None]:
"""Mock setting up a config entry."""
@ -68,3 +107,47 @@ def mock_tuya_login_control() -> Generator[MagicMock]:
},
)
yield login_control
@pytest.fixture
def mock_manager() -> ManagerCompat:
"""Mock Tuya Manager."""
manager = MagicMock(spec=ManagerCompat)
manager.device_map = {}
manager.mq = MagicMock()
return manager
@pytest.fixture
def mock_device_code() -> str:
"""Fixture to parametrize the type of the mock device.
To set a configuration, tests can be marked with:
@pytest.mark.parametrize("mock_device_code", ["device_code_1", "device_code_2"])
"""
return None
@pytest.fixture
async def mock_device(hass: HomeAssistant, mock_device_code: str) -> CustomerDevice:
"""Mock a Tuya CustomerDevice."""
details = await async_load_json_object_fixture(
hass, f"{mock_device_code}.json", DOMAIN
)
device = MagicMock(spec=CustomerDevice)
device.id = details["id"]
device.name = details["name"]
device.category = details["category"]
device.product_id = details["product_id"]
device.product_name = details["product_name"]
device.online = details["online"]
device.function = {
key: MagicMock(type=value["type"], values=value["values"])
for key, value in details["function"].items()
}
device.status_range = {
key: MagicMock(type=value["type"], values=value["values"])
for key, value in details["status_range"].items()
}
device.status = details["status"]
return device

View File

@ -0,0 +1,53 @@
{
"id": "bf3fce6af592f12df3gbgq",
"name": "Dehumidifier",
"category": "cs",
"product_id": "zibqa9dutqyaxym2",
"product_name": "Arete\u00ae Two 12L Dehumidifier/Air Purifier",
"online": true,
"function": {
"switch": { "type": "Boolean", "values": "{}" },
"dehumidify_set_value": {
"type": "Integer",
"values": "{\"unit\": \"%\", \"min\": 35, \"max\": 70, \"scale\": 0, \"step\": 5}"
},
"child_lock": { "type": "Boolean", "values": "{}" },
"countdown_set": {
"type": "Enum",
"values": "{\"range\": [\"cancel\", \"1h\", \"2h\", \"3h\"]}"
}
},
"status_range": {
"switch": { "type": "Boolean", "values": "{}" },
"dehumidify_set_value": {
"type": "Integer",
"values": "{\"unit\": \"%\", \"min\": 35, \"max\": 70, \"scale\": 0, \"step\": 5}"
},
"child_lock": { "type": "Boolean", "values": "{}" },
"humidity_indoor": {
"type": "Integer",
"values": "{\"unit\": \"%\", \"min\": 0, \"max\": 100, \"scale\": 0, \"step\": 1}"
},
"countdown_set": {
"type": "Enum",
"values": "{\"range\": [\"cancel\", \"1h\", \"2h\", \"3h\"]}"
},
"countdown_left": {
"type": "Integer",
"values": "{\"unit\": \"h\", \"min\": 0, \"max\": 24, \"scale\": 0, \"step\": 1}"
},
"fault": {
"type": "Bitmap",
"values": "{\"label\": [\"tankfull\", \"defrost\", \"E1\", \"E2\", \"L2\", \"L3\", \"L4\", \"wet\"]}"
}
},
"status": {
"switch": true,
"dehumidify_set_value": 50,
"child_lock": false,
"humidity_indoor": 47,
"countdown_set": "cancel",
"countdown_left": 0,
"fault": 0
}
}

View File

@ -0,0 +1,20 @@
{
"id": "bf5cccf9027080e2dbb9w3",
"name": "Door Sensor",
"category": "mcs",
"product_id": "7jIGJAymiH8OsFFb",
"product_name": "Door Sensor",
"online": true,
"function": {},
"status_range": {
"switch": { "type": "Boolean", "values": "{}" },
"battery": {
"type": "Integer",
"values": "{\"unit\": \"\", \"min\": 0, \"max\": 500, \"scale\": 0, \"step\": 1}"
}
},
"status": {
"switch": false,
"battery": 100
}
}

View File

@ -11,7 +11,7 @@
't': 'mocked_t',
'uid': 'mocked_uid',
}),
'user_code': '12345',
'user_code': 'test_user_code',
}),
'disabled_by': None,
'discovery_keys': dict({
@ -26,7 +26,7 @@
'source': 'user',
'subentries': list([
]),
'title': '12345',
'title': 'Test Tuya entry',
'unique_id': '12345',
'version': 1,
})

View File

@ -0,0 +1,51 @@
# serializer version: 1
# name: test_platform_setup_and_discovery[cs_arete_two_12l_dehumidifier_air_purifier][fan.dehumidifier-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'fan',
'entity_category': None,
'entity_id': 'fan.dehumidifier',
'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': None,
'platform': 'tuya',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': <FanEntityFeature: 48>,
'translation_key': None,
'unique_id': 'tuya.bf3fce6af592f12df3gbgq',
'unit_of_measurement': None,
})
# ---
# name: test_platform_setup_and_discovery[cs_arete_two_12l_dehumidifier_air_purifier][fan.dehumidifier-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dehumidifier',
'supported_features': <FanEntityFeature: 48>,
}),
'context': <ANY>,
'entity_id': 'fan.dehumidifier',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---

View File

@ -0,0 +1,58 @@
# serializer version: 1
# name: test_platform_setup_and_discovery[cs_arete_two_12l_dehumidifier_air_purifier][humidifier.dehumidifier-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'max_humidity': 70,
'min_humidity': 35,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'humidifier',
'entity_category': None,
'entity_id': 'humidifier.dehumidifier',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <HumidifierDeviceClass.DEHUMIDIFIER: 'dehumidifier'>,
'original_icon': None,
'original_name': None,
'platform': 'tuya',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'tuya.bf3fce6af592f12df3gbgqswitch',
'unit_of_measurement': None,
})
# ---
# name: test_platform_setup_and_discovery[cs_arete_two_12l_dehumidifier_air_purifier][humidifier.dehumidifier-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'current_humidity': 47,
'device_class': 'dehumidifier',
'friendly_name': 'Dehumidifier',
'humidity': 50,
'max_humidity': 70,
'min_humidity': 35,
'supported_features': <HumidifierEntityFeature: 0>,
}),
'context': <ANY>,
'entity_id': 'humidifier.dehumidifier',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---

View File

@ -0,0 +1,62 @@
# serializer version: 1
# name: test_platform_setup_and_discovery[cs_arete_two_12l_dehumidifier_air_purifier][select.dehumidifier_countdown-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'options': list([
'cancel',
'1h',
'2h',
'3h',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'select',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'select.dehumidifier_countdown',
'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': 'Countdown',
'platform': 'tuya',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'countdown',
'unique_id': 'tuya.bf3fce6af592f12df3gbgqcountdown_set',
'unit_of_measurement': None,
})
# ---
# name: test_platform_setup_and_discovery[cs_arete_two_12l_dehumidifier_air_purifier][select.dehumidifier_countdown-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dehumidifier Countdown',
'options': list([
'cancel',
'1h',
'2h',
'3h',
]),
}),
'context': <ANY>,
'entity_id': 'select.dehumidifier_countdown',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'cancel',
})
# ---

View File

@ -0,0 +1,107 @@
# serializer version: 1
# name: test_platform_setup_and_discovery[cs_arete_two_12l_dehumidifier_air_purifier][sensor.dehumidifier_humidity-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.dehumidifier_humidity',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.HUMIDITY: 'humidity'>,
'original_icon': None,
'original_name': 'Humidity',
'platform': 'tuya',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'humidity',
'unique_id': 'tuya.bf3fce6af592f12df3gbgqhumidity_indoor',
'unit_of_measurement': '%',
})
# ---
# name: test_platform_setup_and_discovery[cs_arete_two_12l_dehumidifier_air_purifier][sensor.dehumidifier_humidity-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'humidity',
'friendly_name': 'Dehumidifier Humidity',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.dehumidifier_humidity',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '47.0',
})
# ---
# name: test_platform_setup_and_discovery[mcs_door_sensor][sensor.door_sensor_battery-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.door_sensor_battery',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.BATTERY: 'battery'>,
'original_icon': None,
'original_name': 'Battery',
'platform': 'tuya',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'battery',
'unique_id': 'tuya.bf5cccf9027080e2dbb9w3battery',
'unit_of_measurement': '%',
})
# ---
# name: test_platform_setup_and_discovery[mcs_door_sensor][sensor.door_sensor_battery-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'battery',
'friendly_name': 'Door Sensor Battery',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.door_sensor_battery',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '100.0',
})
# ---

View File

@ -0,0 +1,36 @@
"""Test Tuya fan platform."""
from __future__ import annotations
from unittest.mock import patch
import pytest
from syrupy.assertion import SnapshotAssertion
from tuya_sharing import CustomerDevice
from homeassistant.components.tuya import ManagerCompat
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import initialize_entry
from tests.common import MockConfigEntry, snapshot_platform
@pytest.mark.parametrize(
"mock_device_code", ["cs_arete_two_12l_dehumidifier_air_purifier"]
)
@patch("homeassistant.components.tuya.PLATFORMS", [Platform.FAN])
async def test_platform_setup_and_discovery(
hass: HomeAssistant,
mock_manager: ManagerCompat,
mock_config_entry: MockConfigEntry,
mock_device: CustomerDevice,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
) -> None:
"""Test platform setup and discovery."""
await initialize_entry(hass, mock_manager, mock_config_entry, mock_device)
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)

View File

@ -0,0 +1,36 @@
"""Test Tuya humidifier platform."""
from __future__ import annotations
from unittest.mock import patch
import pytest
from syrupy.assertion import SnapshotAssertion
from tuya_sharing import CustomerDevice
from homeassistant.components.tuya import ManagerCompat
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import initialize_entry
from tests.common import MockConfigEntry, snapshot_platform
@pytest.mark.parametrize(
"mock_device_code", ["cs_arete_two_12l_dehumidifier_air_purifier"]
)
@patch("homeassistant.components.tuya.PLATFORMS", [Platform.HUMIDIFIER])
async def test_platform_setup_and_discovery(
hass: HomeAssistant,
mock_manager: ManagerCompat,
mock_config_entry: MockConfigEntry,
mock_device: CustomerDevice,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
) -> None:
"""Test platform setup and discovery."""
await initialize_entry(hass, mock_manager, mock_config_entry, mock_device)
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)

View File

@ -0,0 +1,36 @@
"""Test Tuya select platform."""
from __future__ import annotations
from unittest.mock import patch
import pytest
from syrupy.assertion import SnapshotAssertion
from tuya_sharing import CustomerDevice
from homeassistant.components.tuya import ManagerCompat
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import initialize_entry
from tests.common import MockConfigEntry, snapshot_platform
@pytest.mark.parametrize(
"mock_device_code", ["cs_arete_two_12l_dehumidifier_air_purifier"]
)
@patch("homeassistant.components.tuya.PLATFORMS", [Platform.SELECT])
async def test_platform_setup_and_discovery(
hass: HomeAssistant,
mock_manager: ManagerCompat,
mock_config_entry: MockConfigEntry,
mock_device: CustomerDevice,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
) -> None:
"""Test platform setup and discovery."""
await initialize_entry(hass, mock_manager, mock_config_entry, mock_device)
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)

View File

@ -0,0 +1,37 @@
"""Test Tuya sensor platform."""
from __future__ import annotations
from unittest.mock import patch
import pytest
from syrupy.assertion import SnapshotAssertion
from tuya_sharing import CustomerDevice
from homeassistant.components.tuya import ManagerCompat
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import initialize_entry
from tests.common import MockConfigEntry, snapshot_platform
@pytest.mark.parametrize(
"mock_device_code",
["cs_arete_two_12l_dehumidifier_air_purifier", "mcs_door_sensor"],
)
@patch("homeassistant.components.tuya.PLATFORMS", [Platform.SENSOR])
async def test_platform_setup_and_discovery(
hass: HomeAssistant,
mock_manager: ManagerCompat,
mock_config_entry: MockConfigEntry,
mock_device: CustomerDevice,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
) -> None:
"""Test platform setup and discovery."""
await initialize_entry(hass, mock_manager, mock_config_entry, mock_device)
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)