"""Tests for the Growatt Server integration.""" from datetime import timedelta import json from unittest.mock import patch from freezegun.api import FrozenDateTimeFactory import growattServer import pytest from syrupy.assertion import SnapshotAssertion from homeassistant.components.growatt_server.const import DOMAIN from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr from . import setup_integration from tests.common import MockConfigEntry, async_fire_time_changed @pytest.mark.usefixtures("init_integration") async def test_load_unload_config_entry( hass: HomeAssistant, mock_config_entry: MockConfigEntry, ) -> None: """Test loading and unloading the integration.""" assert mock_config_entry.state is ConfigEntryState.LOADED await hass.config_entries.async_unload(mock_config_entry.entry_id) await hass.async_block_till_done() assert mock_config_entry.state is ConfigEntryState.NOT_LOADED @pytest.mark.usefixtures("init_integration") async def test_device_info( snapshot: SnapshotAssertion, device_registry: dr.DeviceRegistry, ) -> None: """Test device registry integration.""" device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "MIN123456")}) assert device_entry is not None assert device_entry == snapshot @pytest.mark.parametrize( ("exception", "expected_state"), [ (growattServer.GrowattV1ApiError("API Error"), ConfigEntryState.SETUP_ERROR), ( json.decoder.JSONDecodeError("Invalid JSON", "", 0), ConfigEntryState.SETUP_ERROR, ), ], ) async def test_setup_error_on_api_failure( hass: HomeAssistant, mock_growatt_v1_api, mock_config_entry: MockConfigEntry, exception: Exception, expected_state: ConfigEntryState, ) -> None: """Test setup error on API failures during device list.""" mock_growatt_v1_api.device_list.side_effect = exception await setup_integration(hass, mock_config_entry) assert mock_config_entry.state is expected_state @pytest.mark.usefixtures("init_integration") async def test_coordinator_update_failed( hass: HomeAssistant, mock_growatt_v1_api, mock_config_entry: MockConfigEntry, freezer: FrozenDateTimeFactory, ) -> None: """Test coordinator handles update failures gracefully.""" # Integration should be loaded assert mock_config_entry.state is ConfigEntryState.LOADED # Cause coordinator update to fail mock_growatt_v1_api.min_detail.side_effect = growattServer.GrowattV1ApiError( "Connection timeout" ) # Trigger coordinator refresh freezer.tick(timedelta(minutes=5)) async_fire_time_changed(hass) await hass.async_block_till_done(wait_background_tasks=True) # Integration should remain loaded despite coordinator error assert mock_config_entry.state is ConfigEntryState.LOADED async def test_classic_api_setup( hass: HomeAssistant, snapshot: SnapshotAssertion, mock_growatt_classic_api, mock_config_entry_classic: MockConfigEntry, device_registry: dr.DeviceRegistry, ) -> None: """Test integration setup with Classic API (password auth).""" # Classic API doesn't support MIN devices - use TLX device instead mock_growatt_classic_api.device_list.return_value = [ {"deviceSn": "TLX123456", "deviceType": "tlx"} ] await setup_integration(hass, mock_config_entry_classic) assert mock_config_entry_classic.state is ConfigEntryState.LOADED # Verify Classic API login was called mock_growatt_classic_api.login.assert_called() # Verify device was created device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "TLX123456")}) assert device_entry is not None assert device_entry == snapshot @pytest.mark.usefixtures("init_integration") async def test_unload_removes_listeners( hass: HomeAssistant, mock_config_entry: MockConfigEntry, ) -> None: """Test that unloading removes all listeners.""" # Get initial listener count initial_listeners = len(hass.bus.async_listeners()) # Unload the integration await hass.config_entries.async_unload(mock_config_entry.entry_id) await hass.async_block_till_done() # Verify listeners were removed (should be same or less) final_listeners = len(hass.bus.async_listeners()) assert final_listeners <= initial_listeners async def test_multiple_devices_discovered( hass: HomeAssistant, snapshot: SnapshotAssertion, mock_growatt_v1_api, mock_config_entry: MockConfigEntry, device_registry: dr.DeviceRegistry, ) -> None: """Test handling multiple devices from device_list.""" # Reset and add multiple devices mock_config_entry_new = MockConfigEntry( domain=DOMAIN, data=mock_config_entry.data, unique_id="plant_456", ) mock_growatt_v1_api.device_list.return_value = { "devices": [ {"device_sn": "MIN123456", "type": 7}, {"device_sn": "MIN789012", "type": 7}, ] } with patch( "homeassistant.components.growatt_server.coordinator.SCAN_INTERVAL", timedelta(minutes=5), ): await setup_integration(hass, mock_config_entry_new) # Verify both devices were created device1 = device_registry.async_get_device(identifiers={(DOMAIN, "MIN123456")}) device2 = device_registry.async_get_device(identifiers={(DOMAIN, "MIN789012")}) assert device1 is not None assert device1 == snapshot(name="device_min123456") assert device2 is not None assert device2 == snapshot(name="device_min789012")