Add tests to Teslemetry (#108884)

* Add tests

* Add partial sleep test

* Remove useless AsyncMock

* Review feedback

* Patch imports

* Fix mock_test
This commit is contained in:
Brett Adams 2024-01-29 06:04:44 +10:00 committed by GitHub
parent a01e73a575
commit b28e8a3cf0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 808 additions and 19 deletions

View File

@ -1371,11 +1371,6 @@ omit =
homeassistant/components/telnet/switch.py homeassistant/components/telnet/switch.py
homeassistant/components/temper/sensor.py homeassistant/components/temper/sensor.py
homeassistant/components/tensorflow/image_processing.py homeassistant/components/tensorflow/image_processing.py
homeassistant/components/teslemetry/__init__.py
homeassistant/components/teslemetry/climate.py
homeassistant/components/teslemetry/coordinator.py
homeassistant/components/teslemetry/entity.py
homeassistant/components/teslemetry/context.py
homeassistant/components/tfiac/climate.py homeassistant/components/tfiac/climate.py
homeassistant/components/thermoworks_smoke/sensor.py homeassistant/components/thermoworks_smoke/sensor.py
homeassistant/components/thethingsnetwork/* homeassistant/components/thethingsnetwork/*

View File

@ -2,7 +2,7 @@
import asyncio import asyncio
from typing import Final from typing import Final
from tesla_fleet_api import Teslemetry from tesla_fleet_api import Teslemetry, VehicleSpecific
from tesla_fleet_api.exceptions import InvalidToken, PaymentRequired, TeslaFleetError from tesla_fleet_api.exceptions import InvalidToken, PaymentRequired, TeslaFleetError
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
@ -48,7 +48,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
continue continue
vin = product["vin"] vin = product["vin"]
api = teslemetry.vehicle.specific(vin) api = VehicleSpecific(teslemetry.vehicle, vin)
coordinator = TeslemetryVehicleDataCoordinator(hass, api) coordinator = TeslemetryVehicleDataCoordinator(hass, api)
data.append( data.append(
TeslemetryVehicleData( TeslemetryVehicleData(

View File

@ -13,4 +13,4 @@ def handle_command():
try: try:
yield yield
except TeslaFleetError as e: except TeslaFleetError as e:
raise HomeAssistantError from e raise HomeAssistantError("Teslemetry command failed") from e

View File

@ -1 +1,50 @@
"""Tests for the Teslemetry integration.""" """Tests for the Teslemetry integration."""
from unittest.mock import patch
from syrupy import SnapshotAssertion
from homeassistant.components.teslemetry.const import DOMAIN
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from .const import CONFIG
from tests.common import MockConfigEntry
async def setup_platform(hass: HomeAssistant, platforms: list[Platform] | None = None):
"""Set up the Teslemetry platform."""
mock_entry = MockConfigEntry(
domain=DOMAIN,
data=CONFIG,
)
mock_entry.add_to_hass(hass)
if platforms is None:
await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done()
else:
with patch("homeassistant.components.teslemetry.PLATFORMS", platforms):
await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done()
return mock_entry
def assert_entities(
hass: HomeAssistant,
entry_id: str,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
) -> None:
"""Test that all entities match their snapshot."""
entity_entries = er.async_entries_for_config_entry(entity_registry, entry_id)
assert entity_entries
for entity_entry in entity_entries:
assert entity_entry == snapshot(name=f"{entity_entry.entity_id}-entry")
assert (state := hass.states.get(entity_entry.entity_id))
assert state == snapshot(name=f"{entity_entry.entity_id}-state")

View File

@ -0,0 +1,47 @@
"""Fixtures for Tessie."""
from __future__ import annotations
from unittest.mock import patch
import pytest
from .const import PRODUCTS, RESPONSE_OK, VEHICLE_DATA, WAKE_UP_ONLINE
@pytest.fixture(autouse=True)
def mock_products():
"""Mock Tesla Fleet Api products method."""
with patch(
"homeassistant.components.teslemetry.Teslemetry.products", return_value=PRODUCTS
) as mock_products:
yield mock_products
@pytest.fixture(autouse=True)
def mock_vehicle_data():
"""Mock Tesla Fleet API Vehicle Specific vehicle_data method."""
with patch(
"homeassistant.components.teslemetry.VehicleSpecific.vehicle_data",
return_value=VEHICLE_DATA,
) as mock_vehicle_data:
yield mock_vehicle_data
@pytest.fixture(autouse=True)
def mock_wake_up():
"""Mock Tesla Fleet API Vehicle Specific wake_up method."""
with patch(
"homeassistant.components.teslemetry.VehicleSpecific.wake_up",
return_value=WAKE_UP_ONLINE,
) as mock_wake_up:
yield mock_wake_up
@pytest.fixture(autouse=True)
def mock_request():
"""Mock Tesla Fleet API Vehicle Specific class."""
with patch(
"homeassistant.components.teslemetry.Teslemetry._request",
return_value=RESPONSE_OK,
) as mock_request:
yield mock_request

View File

@ -1,5 +1,16 @@
"""Constants for the teslemetry tests.""" """Constants for the teslemetry tests."""
from homeassistant.components.teslemetry.const import DOMAIN, TeslemetryState
from homeassistant.const import CONF_ACCESS_TOKEN from homeassistant.const import CONF_ACCESS_TOKEN
from tests.common import load_json_object_fixture
CONFIG = {CONF_ACCESS_TOKEN: "1234567890"} CONFIG = {CONF_ACCESS_TOKEN: "1234567890"}
WAKE_UP_ONLINE = {"response": {"state": TeslemetryState.ONLINE}, "error": None}
WAKE_UP_ASLEEP = {"response": {"state": TeslemetryState.ASLEEP}, "error": None}
PRODUCTS = load_json_object_fixture("products.json", DOMAIN)
VEHICLE_DATA = load_json_object_fixture("vehicle_data.json", DOMAIN)
RESPONSE_OK = {"response": {}, "error": None}

View File

@ -0,0 +1,99 @@
{
"response": [
{
"id": 1234,
"user_id": 1234,
"vehicle_id": 1234,
"vin": "VINVINVIN",
"color": null,
"access_type": "OWNER",
"display_name": "Test",
"option_codes": null,
"cached_data": null,
"granular_access": { "hide_private": false },
"tokens": ["abc", "def"],
"state": "asleep",
"in_service": false,
"id_s": "1234",
"calendar_enabled": true,
"api_version": 71,
"backseat_token": null,
"backseat_token_updated_at": null,
"ble_autopair_enrolled": false,
"vehicle_config": {
"aux_park_lamps": "Eu",
"badge_version": 1,
"can_accept_navigation_requests": true,
"can_actuate_trunks": true,
"car_special_type": "base",
"car_type": "model3",
"charge_port_type": "CCS",
"cop_user_set_temp_supported": false,
"dashcam_clip_save_supported": true,
"default_charge_to_max": false,
"driver_assist": "TeslaAP3",
"ece_restrictions": false,
"efficiency_package": "M32021",
"eu_vehicle": true,
"exterior_color": "DeepBlue",
"exterior_trim": "Black",
"exterior_trim_override": "",
"has_air_suspension": false,
"has_ludicrous_mode": false,
"has_seat_cooling": false,
"headlamp_type": "Global",
"interior_trim_type": "White2",
"key_version": 2,
"motorized_charge_port": true,
"paint_color_override": "0,9,25,0.7,0.04",
"performance_package": "Base",
"plg": true,
"pws": true,
"rear_drive_unit": "PM216MOSFET",
"rear_seat_heaters": 1,
"rear_seat_type": 0,
"rhd": true,
"roof_color": "RoofColorGlass",
"seat_type": null,
"spoiler_type": "None",
"sun_roof_installed": null,
"supports_qr_pairing": false,
"third_row_seats": "None",
"timestamp": 1705701487912,
"trim_badging": "74d",
"use_range_badging": true,
"utc_offset": 36000,
"webcam_selfie_supported": true,
"webcam_supported": true,
"wheel_type": "Pinwheel18CapKit"
},
"command_signing": "allowed",
"release_notes_supported": true
},
{
"energy_site_id": 2345,
"resource_type": "wall_connector",
"id": "ID1234",
"asset_site_id": "abcdef",
"warp_site_number": "ID1234",
"go_off_grid_test_banner_enabled": null,
"storm_mode_enabled": null,
"powerwall_onboarding_settings_set": null,
"powerwall_tesla_electric_interested_in": null,
"vpp_tour_enabled": null,
"sync_grid_alert_enabled": false,
"breaker_alert_enabled": false,
"components": {
"battery": false,
"solar": false,
"grid": false,
"load_meter": false,
"wall_connectors": [
{ "device_id": "abcdef", "din": "12345", "is_active": true }
]
},
"features": {}
}
],
"count": 2
}

View File

@ -0,0 +1,269 @@
{
"response": {
"id": 1234,
"user_id": 1234,
"vehicle_id": 1234,
"vin": "VINVINVIN",
"color": null,
"access_type": "OWNER",
"granular_access": {
"hide_private": false
},
"tokens": ["abc", "def"],
"state": "online",
"in_service": false,
"id_s": "1234",
"calendar_enabled": true,
"api_version": 71,
"backseat_token": null,
"backseat_token_updated_at": null,
"ble_autopair_enrolled": false,
"charge_state": {
"battery_heater_on": false,
"battery_level": 77,
"battery_range": 266.87,
"charge_amps": 16,
"charge_current_request": 16,
"charge_current_request_max": 16,
"charge_enable_request": true,
"charge_energy_added": 0,
"charge_limit_soc": 80,
"charge_limit_soc_max": 100,
"charge_limit_soc_min": 50,
"charge_limit_soc_std": 80,
"charge_miles_added_ideal": 0,
"charge_miles_added_rated": 0,
"charge_port_cold_weather_mode": false,
"charge_port_color": "<invalid>",
"charge_port_door_open": true,
"charge_port_latch": "Engaged",
"charge_rate": 0,
"charger_actual_current": 0,
"charger_phases": null,
"charger_pilot_current": 16,
"charger_power": 0,
"charger_voltage": 2,
"charging_state": "Stopped",
"conn_charge_cable": "IEC",
"est_battery_range": 275.04,
"fast_charger_brand": "<invalid>",
"fast_charger_present": false,
"fast_charger_type": "ACSingleWireCAN",
"ideal_battery_range": 266.87,
"max_range_charge_counter": 0,
"minutes_to_full_charge": 0,
"not_enough_power_to_heat": null,
"off_peak_charging_enabled": false,
"off_peak_charging_times": "all_week",
"off_peak_hours_end_time": 900,
"preconditioning_enabled": false,
"preconditioning_times": "all_week",
"scheduled_charging_mode": "Off",
"scheduled_charging_pending": false,
"scheduled_charging_start_time": null,
"scheduled_charging_start_time_app": 600,
"scheduled_departure_time": 1704837600,
"scheduled_departure_time_minutes": 480,
"supercharger_session_trip_planner": false,
"time_to_full_charge": 0,
"timestamp": 1705707520649,
"trip_charging": false,
"usable_battery_level": 77,
"user_charge_enable_request": null
},
"climate_state": {
"allow_cabin_overheat_protection": true,
"auto_seat_climate_left": false,
"auto_seat_climate_right": true,
"auto_steering_wheel_heat": false,
"battery_heater": false,
"battery_heater_no_power": null,
"cabin_overheat_protection": "On",
"cabin_overheat_protection_actively_cooling": false,
"climate_keeper_mode": "off",
"cop_activation_temperature": "High",
"defrost_mode": 0,
"driver_temp_setting": 22,
"fan_status": 0,
"hvac_auto_request": "On",
"inside_temp": 29.8,
"is_auto_conditioning_on": false,
"is_climate_on": false,
"is_front_defroster_on": false,
"is_preconditioning": false,
"is_rear_defroster_on": false,
"left_temp_direction": 251,
"max_avail_temp": 28,
"min_avail_temp": 15,
"outside_temp": 30,
"passenger_temp_setting": 22,
"remote_heater_control_enabled": false,
"right_temp_direction": 251,
"seat_heater_left": 0,
"seat_heater_rear_center": 0,
"seat_heater_rear_left": 0,
"seat_heater_rear_right": 0,
"seat_heater_right": 0,
"side_mirror_heaters": false,
"steering_wheel_heat_level": 0,
"steering_wheel_heater": false,
"supports_fan_only_cabin_overheat_protection": true,
"timestamp": 1705707520649,
"wiper_blade_heater": false
},
"drive_state": {
"active_route_latitude": -27.855946,
"active_route_longitude": 153.345056,
"active_route_traffic_minutes_delay": 0,
"power": 0,
"shift_state": null,
"speed": null,
"timestamp": 1705707520649
},
"gui_settings": {
"gui_24_hour_time": false,
"gui_charge_rate_units": "kW",
"gui_distance_units": "km/hr",
"gui_range_display": "Rated",
"gui_temperature_units": "C",
"gui_tirepressure_units": "Psi",
"show_range_units": false,
"timestamp": 1705707520649
},
"vehicle_config": {
"aux_park_lamps": "Eu",
"badge_version": 1,
"can_accept_navigation_requests": true,
"can_actuate_trunks": true,
"car_special_type": "base",
"car_type": "model3",
"charge_port_type": "CCS",
"cop_user_set_temp_supported": false,
"dashcam_clip_save_supported": true,
"default_charge_to_max": false,
"driver_assist": "TeslaAP3",
"ece_restrictions": false,
"efficiency_package": "M32021",
"eu_vehicle": true,
"exterior_color": "DeepBlue",
"exterior_trim": "Black",
"exterior_trim_override": "",
"has_air_suspension": false,
"has_ludicrous_mode": false,
"has_seat_cooling": false,
"headlamp_type": "Global",
"interior_trim_type": "White2",
"key_version": 2,
"motorized_charge_port": true,
"paint_color_override": "0,9,25,0.7,0.04",
"performance_package": "Base",
"plg": true,
"pws": true,
"rear_drive_unit": "PM216MOSFET",
"rear_seat_heaters": 1,
"rear_seat_type": 0,
"rhd": true,
"roof_color": "RoofColorGlass",
"seat_type": null,
"spoiler_type": "None",
"sun_roof_installed": null,
"supports_qr_pairing": false,
"third_row_seats": "None",
"timestamp": 1705707520649,
"trim_badging": "74d",
"use_range_badging": true,
"utc_offset": 36000,
"webcam_selfie_supported": true,
"webcam_supported": true,
"wheel_type": "Pinwheel18CapKit"
},
"vehicle_state": {
"api_version": 71,
"autopark_state_v2": "unavailable",
"calendar_supported": true,
"car_version": "2023.44.30.8 06f534d46010",
"center_display_state": 0,
"dashcam_clip_save_available": true,
"dashcam_state": "Recording",
"df": 0,
"dr": 0,
"fd_window": 0,
"feature_bitmask": "fbdffbff,187f",
"fp_window": 0,
"ft": 0,
"is_user_present": false,
"locked": false,
"media_info": {
"audio_volume": 2.6667,
"audio_volume_increment": 0.333333,
"audio_volume_max": 10.333333,
"media_playback_status": "Stopped",
"now_playing_album": "",
"now_playing_artist": "",
"now_playing_duration": 0,
"now_playing_elapsed": 0,
"now_playing_source": "Spotify",
"now_playing_station": "",
"now_playing_title": ""
},
"media_state": {
"remote_control_enabled": true
},
"notifications_supported": true,
"odometer": 6481.019282,
"parsed_calendar_supported": true,
"pf": 0,
"pr": 0,
"rd_window": 0,
"remote_start": false,
"remote_start_enabled": true,
"remote_start_supported": true,
"rp_window": 0,
"rt": 0,
"santa_mode": 0,
"sentry_mode": false,
"sentry_mode_available": true,
"service_mode": false,
"service_mode_plus": false,
"software_update": {
"download_perc": 0,
"expected_duration_sec": 2700,
"install_perc": 1,
"status": "",
"version": " "
},
"speed_limit_mode": {
"active": false,
"current_limit_mph": 69,
"max_limit_mph": 120,
"min_limit_mph": 50,
"pin_code_set": true
},
"timestamp": 1705707520649,
"tpms_hard_warning_fl": false,
"tpms_hard_warning_fr": false,
"tpms_hard_warning_rl": false,
"tpms_hard_warning_rr": false,
"tpms_last_seen_pressure_time_fl": 1705700812,
"tpms_last_seen_pressure_time_fr": 1705700793,
"tpms_last_seen_pressure_time_rl": 1705700794,
"tpms_last_seen_pressure_time_rr": 1705700823,
"tpms_pressure_fl": 2.775,
"tpms_pressure_fr": 2.8,
"tpms_pressure_rl": 2.775,
"tpms_pressure_rr": 2.775,
"tpms_rcp_front_value": 2.9,
"tpms_rcp_rear_value": 2.9,
"tpms_soft_warning_fl": false,
"tpms_soft_warning_fr": false,
"tpms_soft_warning_rl": false,
"tpms_soft_warning_rr": false,
"valet_mode": false,
"valet_pin_needed": false,
"vehicle_name": "Test",
"vehicle_self_test_progress": 0,
"vehicle_self_test_requested": false,
"webcam_available": true
}
}
}

View File

@ -0,0 +1,73 @@
# serializer version: 1
# name: test_climate[climate.test_climate-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'hvac_modes': list([
<HVACMode.HEAT_COOL: 'heat_cool'>,
<HVACMode.OFF: 'off'>,
]),
'max_temp': 28.0,
'min_temp': 15.0,
'preset_modes': list([
'off',
'keep',
'dog',
'camp',
]),
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'climate',
'entity_category': None,
'entity_id': 'climate.test_climate',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Climate',
'platform': 'teslemetry',
'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 17>,
'translation_key': <TeslemetryClimateSide.DRIVER: 'driver_temp'>,
'unique_id': 'VINVINVIN-driver_temp',
'unit_of_measurement': None,
})
# ---
# name: test_climate[climate.test_climate-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'current_temperature': 30.0,
'friendly_name': 'Test Climate',
'hvac_modes': list([
<HVACMode.HEAT_COOL: 'heat_cool'>,
<HVACMode.OFF: 'off'>,
]),
'max_temp': 28.0,
'min_temp': 15.0,
'preset_mode': 'off',
'preset_modes': list([
'off',
'keep',
'dog',
'camp',
]),
'supported_features': <ClimateEntityFeature: 17>,
'temperature': 22.0,
}),
'context': <ANY>,
'entity_id': 'climate.test_climate',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---

View File

@ -0,0 +1,131 @@
"""Test the Teslemetry climate platform."""
from datetime import timedelta
from unittest.mock import patch
from freezegun.api import FrozenDateTimeFactory
import pytest
from syrupy import SnapshotAssertion
from tesla_fleet_api.exceptions import InvalidCommand, VehicleOffline
from homeassistant.components.climate import (
ATTR_HVAC_MODE,
ATTR_PRESET_MODE,
ATTR_TEMPERATURE,
DOMAIN as CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
SERVICE_SET_PRESET_MODE,
SERVICE_SET_TEMPERATURE,
SERVICE_TURN_ON,
HVACMode,
)
from homeassistant.components.teslemetry.coordinator import SYNC_INTERVAL
from homeassistant.const import ATTR_ENTITY_ID, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er
from . import assert_entities, setup_platform
from tests.common import async_fire_time_changed
async def test_climate(
hass: HomeAssistant,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
) -> None:
"""Tests that the climate entity is correct."""
entry = await setup_platform(hass, [Platform.CLIMATE])
assert_entities(hass, entry.entry_id, entity_registry, snapshot)
entity_id = "climate.test_climate"
state = hass.states.get(entity_id)
# Turn On
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
{ATTR_ENTITY_ID: [entity_id], ATTR_HVAC_MODE: HVACMode.HEAT_COOL},
blocking=True,
)
state = hass.states.get(entity_id)
assert state.state == HVACMode.HEAT_COOL
# Set Temp
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{ATTR_ENTITY_ID: [entity_id], ATTR_TEMPERATURE: 20},
blocking=True,
)
state = hass.states.get(entity_id)
assert state.attributes[ATTR_TEMPERATURE] == 20
# Set Preset
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_PRESET_MODE,
{ATTR_ENTITY_ID: [entity_id], ATTR_PRESET_MODE: "keep"},
blocking=True,
)
state = hass.states.get(entity_id)
assert state.attributes[ATTR_PRESET_MODE] == "keep"
# Turn Off
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
{ATTR_ENTITY_ID: [entity_id], ATTR_HVAC_MODE: HVACMode.OFF},
blocking=True,
)
state = hass.states.get(entity_id)
assert state.state == HVACMode.OFF
async def test_errors(
hass: HomeAssistant,
) -> None:
"""Tests service error is handled."""
await setup_platform(hass, platforms=[Platform.CLIMATE])
entity_id = "climate.test_climate"
with patch(
"homeassistant.components.teslemetry.VehicleSpecific.auto_conditioning_start",
side_effect=InvalidCommand,
) as mock_on, pytest.raises(HomeAssistantError) as error:
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: [entity_id]},
blocking=True,
)
mock_on.assert_called_once()
assert error.from_exception == InvalidCommand
async def test_asleep_or_offline(
hass: HomeAssistant, mock_vehicle_data, freezer: FrozenDateTimeFactory
) -> None:
"""Tests asleep is handled."""
await setup_platform(hass, [Platform.CLIMATE])
entity_id = "climate.test_climate"
mock_vehicle_data.assert_called_once()
# Put the vehicle alseep
mock_vehicle_data.reset_mock()
mock_vehicle_data.side_effect = VehicleOffline
freezer.tick(timedelta(seconds=SYNC_INTERVAL))
async_fire_time_changed(hass)
await hass.async_block_till_done()
mock_vehicle_data.assert_called_once()
# Run a command that will wake up the vehicle, but not immediately
await hass.services.async_call(
CLIMATE_DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: [entity_id]}, blocking=True
)
await hass.async_block_till_done()

View File

@ -1,6 +1,6 @@
"""Test the Teslemetry config flow.""" """Test the Teslemetry config flow."""
from unittest.mock import AsyncMock, patch from unittest.mock import patch
from aiohttp import ClientConnectionError from aiohttp import ClientConnectionError
import pytest import pytest
@ -16,13 +16,12 @@ from .const import CONFIG
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def teslemetry_config_entry_mock(): def mock_test():
"""Mock Teslemetry api class.""" """Mock Teslemetry api class."""
with patch( with patch(
"homeassistant.components.teslemetry.config_flow.Teslemetry", "homeassistant.components.teslemetry.Teslemetry.test", return_value=True
) as teslemetry_config_entry_mock: ) as mock_test:
teslemetry_config_entry_mock.return_value.test = AsyncMock() yield mock_test
yield teslemetry_config_entry_mock
async def test_form( async def test_form(
@ -60,16 +59,14 @@ async def test_form(
(TeslaFleetError, {"base": "unknown"}), (TeslaFleetError, {"base": "unknown"}),
], ],
) )
async def test_form_errors( async def test_form_errors(hass: HomeAssistant, side_effect, error, mock_test) -> None:
hass: HomeAssistant, side_effect, error, teslemetry_config_entry_mock
) -> None:
"""Test errors are handled.""" """Test errors are handled."""
result1 = await hass.config_entries.flow.async_init( result1 = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
teslemetry_config_entry_mock.return_value.test.side_effect = side_effect mock_test.side_effect = side_effect
result2 = await hass.config_entries.flow.async_configure( result2 = await hass.config_entries.flow.async_configure(
result1["flow_id"], result1["flow_id"],
CONFIG, CONFIG,
@ -79,7 +76,7 @@ async def test_form_errors(
assert result2["errors"] == error assert result2["errors"] == error
# Complete the flow # Complete the flow
teslemetry_config_entry_mock.return_value.test.side_effect = None mock_test.side_effect = None
result3 = await hass.config_entries.flow.async_configure( result3 = await hass.config_entries.flow.async_configure(
result2["flow_id"], result2["flow_id"],
CONFIG, CONFIG,

View File

@ -0,0 +1,118 @@
"""Test the Tessie init."""
from datetime import timedelta
from freezegun.api import FrozenDateTimeFactory
from tesla_fleet_api.exceptions import (
InvalidToken,
PaymentRequired,
TeslaFleetError,
VehicleOffline,
)
from homeassistant.components.teslemetry.coordinator import SYNC_INTERVAL
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from . import setup_platform
from .const import WAKE_UP_ASLEEP, WAKE_UP_ONLINE
from tests.common import async_fire_time_changed
async def test_load_unload(hass: HomeAssistant) -> None:
"""Test load and unload."""
entry = await setup_platform(hass)
assert entry.state is ConfigEntryState.LOADED
assert await hass.config_entries.async_unload(entry.entry_id)
await hass.async_block_till_done()
assert entry.state is ConfigEntryState.NOT_LOADED
async def test_auth_failure(hass: HomeAssistant, mock_products) -> None:
"""Test init with an authentication error."""
mock_products.side_effect = InvalidToken
entry = await setup_platform(hass)
assert entry.state is ConfigEntryState.SETUP_ERROR
async def test_subscription_failure(hass: HomeAssistant, mock_products) -> None:
"""Test init with an client response error."""
mock_products.side_effect = PaymentRequired
entry = await setup_platform(hass)
assert entry.state is ConfigEntryState.SETUP_ERROR
async def test_other_failure(hass: HomeAssistant, mock_products) -> None:
"""Test init with an client response error."""
mock_products.side_effect = TeslaFleetError
entry = await setup_platform(hass)
assert entry.state is ConfigEntryState.SETUP_RETRY
# Coordinator
async def test_first_refresh(
hass: HomeAssistant,
mock_wake_up,
mock_vehicle_data,
mock_products,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test first coordinator refresh but vehicle is asleep."""
# Mock vehicle is asleep
mock_wake_up.return_value = WAKE_UP_ASLEEP
entry = await setup_platform(hass)
assert entry.state is ConfigEntryState.SETUP_RETRY
mock_wake_up.assert_called_once()
# Reset mock and set vehicle to online
mock_wake_up.reset_mock()
mock_wake_up.return_value = WAKE_UP_ONLINE
# Wait for the retry
freezer.tick(timedelta(seconds=60))
async_fire_time_changed(hass)
await hass.async_block_till_done()
# Verify we have loaded
assert entry.state is ConfigEntryState.LOADED
mock_wake_up.assert_called_once()
mock_vehicle_data.assert_called_once()
async def test_first_refresh_error(hass: HomeAssistant, mock_wake_up) -> None:
"""Test first coordinator refresh with an error."""
mock_wake_up.side_effect = TeslaFleetError
entry = await setup_platform(hass)
assert entry.state is ConfigEntryState.SETUP_RETRY
async def test_refresh_offline(
hass: HomeAssistant, mock_vehicle_data, freezer: FrozenDateTimeFactory
) -> None:
"""Test coordinator refresh with an error."""
entry = await setup_platform(hass, [Platform.CLIMATE])
assert entry.state is ConfigEntryState.LOADED
mock_vehicle_data.assert_called_once()
mock_vehicle_data.reset_mock()
mock_vehicle_data.side_effect = VehicleOffline
freezer.tick(timedelta(seconds=SYNC_INTERVAL))
async_fire_time_changed(hass)
await hass.async_block_till_done()
mock_vehicle_data.assert_called_once()
async def test_refresh_error(hass: HomeAssistant, mock_vehicle_data) -> None:
"""Test coordinator refresh with an error."""
mock_vehicle_data.side_effect = TeslaFleetError
entry = await setup_platform(hass)
assert entry.state is ConfigEntryState.SETUP_RETRY