diff --git a/.gitignore b/.gitignore index 709659d8..86feab15 100644 --- a/.gitignore +++ b/.gitignore @@ -106,3 +106,6 @@ venv.bak/ # macOS files ._* .DS_Store + +# VS Code +.vscode/ diff --git a/Makefile b/Makefile index 7f823c05..5ea994c6 100644 --- a/Makefile +++ b/Makefile @@ -3,8 +3,7 @@ black: pipenv run black teslajsonpy coverage: - #Not implemented yet - #pipenv run py.test -s --verbose --cov-report term-missing --cov-report xml --cov=teslajsonpy tests + pipenv run pytest -s --verbose --cov-report term-missing --cov-report xml --cov=teslajsonpy tests clean: rm -rf dist/ build/ .egg teslajsonpy.egg-info/ init: @@ -23,7 +22,6 @@ publish: pipenv run twine upload dist/* rm -rf dist/ build/ .egg teslajsonpy.egg-info/ test: - #Not implemented yet - #pipenv run py.test + pipenv run pytest typing: pipenv run mypy --ignore-missing-imports teslajsonpy diff --git a/Pipfile b/Pipfile index fab00cfd..1e25035d 100644 --- a/Pipfile +++ b/Pipfile @@ -8,6 +8,8 @@ detox = "*" mypy = "*" pydocstyle = "*" pylint = "*" +pytest = "*" +pytest-asyncio = "*" pytest-cov = "*" tox = "*" twine = "*" diff --git a/teslajsonpy/sentry_mode.py b/teslajsonpy/sentry_mode.py index 0997e3ff..54d46e34 100644 --- a/teslajsonpy/sentry_mode.py +++ b/teslajsonpy/sentry_mode.py @@ -31,12 +31,16 @@ def __init__(self, data, controller): """ super().__init__(data, controller) self.__manual_update_time = 0 - self.__sentry_mode_available = False - self.__sentry_mode = False self.type = "sentry mode switch" self.hass_type = "switch" self.name = self._name() self.uniq_name = self._uniq_name() + self.__sentry_mode = ( + self.sentry_mode_available + and "vehicle_state" in data + and "sentry_mode" in data["vehicle_state"] + and data["vehicle_state"]["sentry_mode"] + ) async def async_update(self, wake_if_asleep=False): """Update the sentry mode of the vehicle.""" @@ -44,21 +48,18 @@ async def async_update(self, wake_if_asleep=False): last_update = self._controller.get_last_update_time(self._id) if last_update >= self.__manual_update_time: data = self._controller.get_state_params(self._id) - if data and "sentry_mode_available" in data: - self.__sentry_mode_available = data["sentry_mode_available"] - if self.__sentry_mode_available and "sentry_mode" in data: - self.__sentry_mode = data["sentry_mode"] + if self.sentry_mode_available and "sentry_mode" in data: + self.__sentry_mode = data["sentry_mode"] else: - self.__sentry_mode_available = False self.__sentry_mode = False def available(self): """Return whether the sentry mode is available.""" - return self.__sentry_mode_available + return self.sentry_mode_available def is_on(self): """Return whether the sentry mode is enabled, always False if sentry mode is not available.""" - return self.__sentry_mode_available and self.__sentry_mode + return self.sentry_mode_available and self.__sentry_mode @staticmethod def has_battery(): @@ -67,7 +68,7 @@ def has_battery(): async def enable_sentry_mode(self): """Enable the sentry mode.""" - if self.__sentry_mode_available and not self.__sentry_mode: + if self.sentry_mode_available and not self.__sentry_mode: data = await self._controller.command( self._id, "set_sentry_mode", {"on": True}, wake_if_asleep=True ) @@ -77,7 +78,7 @@ async def enable_sentry_mode(self): async def disable_sentry_mode(self): """Disable the sentry mode.""" - if self.__sentry_mode_available and self.__sentry_mode: + if self.sentry_mode_available and self.__sentry_mode: data = await self._controller.command( self._id, "set_sentry_mode", {"on": False}, wake_if_asleep=True ) diff --git a/teslajsonpy/vehicle.py b/teslajsonpy/vehicle.py index a593bf76..f7cf6d13 100644 --- a/teslajsonpy/vehicle.py +++ b/teslajsonpy/vehicle.py @@ -40,7 +40,11 @@ def __init__(self, data, controller): self._state = data["state"] self._car_type = f"Model {str(self._vin[3]).upper()}" self._car_version = "" - self._sentry_mode_available = False + self._sentry_mode_available = ( + "vehicle_state" in data + and "sentry_mode_available" in data["vehicle_state"] + and data["vehicle_state"]["sentry_mode_available"] + ) self._controller = controller self.should_poll = True self.type = "device" diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..2a978c16 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: Apache-2.0 +""" +Python Package for controlling Tesla API. + +For more details about this api, please refer to the documentation at +https://github.com/zabuldon/teslajsonpy +""" diff --git a/tests/tesla_mock.py b/tests/tesla_mock.py new file mode 100644 index 00000000..c4b02aeb --- /dev/null +++ b/tests/tesla_mock.py @@ -0,0 +1,415 @@ +""" +Tesla mock. +""" + +from teslajsonpy.connection import Connection +from teslajsonpy.controller import Controller + + +class TeslaMock: + """ + Tesla mock. + """ + + def __init__(self, monkeypatch) -> None: + """ + Initialize mock. + + Args: + monkeypatch (pytest.Monkeypatch): Monkeypatch. + """ + self._monkeypatch = monkeypatch + self._monkeypatch.setattr(Controller, "connect", self.mock_connect) + self._monkeypatch.setattr(Controller, "command", self.mock_command) + self._monkeypatch.setattr( + Controller, "get_charging_params", self.mock_get_charging_params + ) + self._monkeypatch.setattr( + Controller, "get_climate_params", self.mock_get_climate_params + ) + self._monkeypatch.setattr( + Controller, "get_drive_params", self.mock_get_drive_params + ) + self._monkeypatch.setattr( + Controller, "get_gui_params", self.mock_get_gui_params + ) + self._monkeypatch.setattr( + Controller, "get_state_params", self.mock_get_state_params + ) + self._monkeypatch.setattr(Controller, "get_vehicles", self.mock_get_vehicles) + self._monkeypatch.setattr( + Controller, "get_last_update_time", self.mock_get_last_update_time + ) + self._monkeypatch.setattr(Controller, "update", self.mock_update) + self._monkeypatch.setattr( + Connection, "generate_oauth", self.mock_generate_oauth + ) + + def mock_connect(self, *args, **kwargs): + # pylint: disable=unused-argument + """ Mock controller's connect method.""" + return self.controller_connect() + + def mock_command(self, *args, **kwargs): + # pylint: disable=unused-argument + """ Mock controller's command method.""" + return self.controller_command() + + def mock_get_charging_params(self, *args, **kwargs): + # pylint: disable=unused-argument + """ Mock controller's get_charging_params method.""" + return self.controller_get_charging_params() + + def mock_get_climate_params(self, *args, **kwargs): + # pylint: disable=unused-argument + """ Mock controller's get_climate_params method.""" + return self.controller_get_climate_params() + + def mock_get_drive_params(self, *args, **kwargs): + # pylint: disable=unused-argument + """ Mock controller's get_drive_params method.""" + return self.controller_get_drive_params() + + def mock_get_gui_params(self, *args, **kwargs): + # pylint: disable=unused-argument + """ Mock controller's get_gui_params method.""" + return self.controller_get_gui_params() + + def mock_get_state_params(self, *args, **kwargs): + # pylint: disable=unused-argument + """ Mock controller's get_state_params method.""" + return self.controller_get_state_params() + + def mock_get_vehicles(self, *args, **kwargs): + # pylint: disable=unused-argument + """ Mock controller's get_vehicles method.""" + return self.controller_get_vehicles() + + def mock_get_last_update_time(self, *args, **kwargs): + # pylint: disable=unused-argument + """ Mock controller's get_last_update_time method.""" + return 123 + + def mock_update(self, *args, **kwargs): + # pylint: disable=unused-argument + """ Mock controller's update method.""" + return self.controller_update() + + def mock_generate_oauth(self, *args, **kwargs): + # pylint: disable=unused-argument + """ Mock connection's generate_oauth method.""" + return self.connection_generate_oauth() + + @staticmethod + def controller_connect(): + """ Monkeypatch for controller.connect().""" + return ("abc123", "cba321") + + @staticmethod + async def controller_command(): + """ Monkeypatch for controller.command().""" + return RESULT_OK + + @staticmethod + def controller_get_charging_params(): + """ Monkeypatch for controller.get_charging_params().""" + return CHARGE_STATE + + @staticmethod + def controller_get_climate_params(): + """ Monkeypatch for controller.get_climate_params().""" + return CLIMATE_STATE + + @staticmethod + def controller_get_drive_params(): + """ Monkeypatch for controller.get_drive_params().""" + return DRIVE_STATE + + @staticmethod + def controller_get_gui_params(): + """ Monkeypatch for controller.get_gui_params().""" + return GUI_SETTINGS + + @staticmethod + def controller_get_state_params(): + """ Monkeypatch for controller.get_state_params().""" + return VEHICLE_STATE + + @staticmethod + def controller_get_vehicles(): + """ Monkeypatch for controller.get_vehicles().""" + return {SAMPLE_VEHICLE} + + @staticmethod + async def controller_update(): + """ Monkeypatch for controller.update().""" + return 123 + + @staticmethod + def connection_generate_oauth(): + """ Monkeypatch for connection.generate_oauth().""" + return + + @staticmethod + def data_request_vehicle(): + """ Simulates the result of vehicle data request. """ + return VEHICLE + + @staticmethod + def data_request_charge_state(): + """ Simulates the result of charge state data request. """ + return CHARGE_STATE + + @staticmethod + def data_request_climate_state(): + """ Simulates the result of climate state data request. """ + return CLIMATE_STATE + + @staticmethod + def data_request_vehicle_state(): + """ Simulates the result of vehicle state data request. """ + return VEHICLE_STATE + + @staticmethod + def command_ok(): + """ Simulates an OK result for a command. """ + return RESULT_OK + + +RESULT_OK = {"response": {"reason": "", "result": True}} + +# 408 - Request Timeout +RESULT_VEHICLE_UNAVAILABLE = { + "response": None, + "error": 'vehicle unavailable: {:error=>"vehicle unavailable:"}', + "error_description": "", +} + +SAMPLE_VEHICLE = { + "id": 12345678901234567, + "vehicle_id": 1234567890, + "vin": "5YJSA11111111111", + "display_name": "Nikola 2.0", + "option_codes": "MDLS,RENA,AF02,APF1,APH2,APPB,AU01,BC0R,BP00,BR00,BS00,CDM0,CH05,PBCW,CW00,DCF0,DRLH,DSH7,DV4W,FG02,FR04,HP00,IDBA,IX01,LP01,ME02,MI01,PF01,PI01,PK00,PS01,PX00,PX4D,QTVB,RFP2,SC01,SP00,SR01,SU01,TM00,TP03,TR00,UTAB,WTAS,X001,X003,X007,X011,X013,X021,X024,X027,X028,X031,X037,X040,X044,YFFC,COUS", + "color": None, + "tokens": ["abcdef1234567890", "1234567890abcdef"], + "state": "online", + "in_service": False, + "id_s": "12345678901234567", + "calendar_enabled": True, + "api_version": 7, + "backseat_token": None, + "backseat_token_updated_at": None, +} + +DRIVE_STATE = { + "gps_as_of": 1538363883, + "heading": 5, + "latitude": 33.111111, + "longitude": -88.111111, + "native_latitude": 33.111111, + "native_location_supported": 1, + "native_longitude": -88.111111, + "native_type": "wgs", + "power": 0, + "shift_state": None, + "speed": None, + "timestamp": 1538364666096, +} + +CLIMATE_STATE = { + "battery_heater": False, + "battery_heater_no_power": False, + "climate_keeper_mode": "dog", + "defrost_mode": 0, + "driver_temp_setting": 21.6, + "fan_status": 0, + "inside_temp": None, + "is_auto_conditioning_on": None, + "is_climate_on": False, + "is_front_defroster_on": False, + "is_preconditioning": False, + "is_rear_defroster_on": False, + "left_temp_direction": None, + "max_avail_temp": 28.0, + "min_avail_temp": 15.0, + "outside_temp": None, + "passenger_temp_setting": 21.6, + "remote_heater_control_enabled": True, + "right_temp_direction": None, + "seat_heater_left": 3, + "seat_heater_rear_center": 0, + "seat_heater_rear_left": 1, + "seat_heater_rear_left_back": 0, + "seat_heater_rear_right": 1, + "seat_heater_rear_right_back": 0, + "seat_heater_right": 2, + "side_mirror_heaters": False, + "steering_wheel_heater": False, + "timestamp": 1543186971731, + "wiper_blade_heater": False, +} + +CHARGE_STATE = { + "battery_heater_on": False, + "battery_level": 64, + "battery_range": 167.96, + "charge_current_request": 48, + "charge_current_request_max": 48, + "charge_enable_request": True, + "charge_energy_added": 12.41, + "charge_limit_soc": 90, + "charge_limit_soc_max": 100, + "charge_limit_soc_min": 50, + "charge_limit_soc_std": 90, + "charge_miles_added_ideal": 50.0, + "charge_miles_added_rated": 40.0, + "charge_port_cold_weather_mode": False, + "charge_port_door_open": False, + "charge_port_latch": "Engaged", + "charge_rate": 0.0, + "charge_to_max_range": False, + "charger_actual_current": 0, + "charger_phases": None, + "charger_pilot_current": 48, + "charger_power": 0, + "charger_voltage": 0, + "charging_state": "Disconnected", + "conn_charge_cable": "", + "est_battery_range": 118.38, + "fast_charger_brand": "", + "fast_charger_present": False, + "fast_charger_type": "", + "ideal_battery_range": 209.95, + "managed_charging_active": False, + "managed_charging_start_time": None, + "managed_charging_user_canceled": False, + "max_range_charge_counter": 0, + "minutes_to_full_charge": 0, + "not_enough_power_to_heat": False, + "scheduled_charging_pending": False, + "scheduled_charging_start_time": None, + "time_to_full_charge": 0.0, + "timestamp": 1543186971727, + "trip_charging": False, + "usable_battery_level": 64, + "user_charge_enable_request": None, +} + +GUI_SETTINGS = { + "gui_24_hour_time": False, + "gui_charge_rate_units": "mi/hr", + "gui_distance_units": "mi/hr", + "gui_range_display": "Rated", + "gui_temperature_units": "F", + "show_range_units": True, + "timestamp": 1543186971728, +} + +VEHICLE_STATE = { + "api_version": 7, + "autopark_state_v2": "standby", + "autopark_style": "standard", + "calendar_supported": True, + "car_version": "2019.40.2.1 38f55d9f9205", + "center_display_state": 0, + "df": 0, + "dr": 0, + "fd_window": 0, + "fp_window": 0, + "ft": 0, + "homelink_device_count": 0, + "homelink_nearby": True, + "is_user_present": False, + "last_autopark_error": "no_error", + "locked": True, + "media_state": {"remote_control_enabled": True}, + "notifications_supported": True, + "odometer": 33561.422505, + "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, + "sentry_mode": True, + "sentry_mode_available": True, + "smart_summon_available": True, + "software_update": { + "download_perc": 100, + "expected_duration_sec": 2700, + "install_perc": 10, + "scheduled_time_ms": 1575689678432, + "status": "scheduled", + "version": "2019.40.2.1", + }, + "speed_limit_mode": { + "active": False, + "current_limit_mph": 75.0, + "max_limit_mph": 90, + "min_limit_mph": 50, + "pin_code_set": False, + }, + "summon_standby_mode_enabled": True, + "sun_roof_percent_open": 0, + "sun_roof_state": "unknown", + "timestamp": 1538364666096, + "valet_mode": False, + "valet_pin_needed": True, + "vehicle_name": "Nikola 2.0", +} + +VEHICLE_CONFIG = { + "can_accept_navigation_requests": True, + "can_actuate_trunks": True, + "car_special_type": "base", + "car_type": "models2", + "charge_port_type": "US", + "eu_vehicle": False, + "exterior_color": "White", + "has_air_suspension": True, + "has_ludicrous_mode": False, + "key_version": 1, + "motorized_charge_port": True, + "perf_config": "P2", + "plg": True, + "rear_seat_heaters": 0, + "rear_seat_type": 0, + "rhd": False, + "roof_color": "None", + "seat_type": 2, + "spoiler_type": "None", + "sun_roof_installed": 2, + "third_row_seats": "None", + "timestamp": 1538364666096, + "trim_badging": "p90d", + "use_range_badging": False, + "wheel_type": "AeroTurbine19", +} + +VEHICLE = { + "id": 12345678901234567, + "user_id": 123, + "vehicle_id": 1234567890, + "vin": "5YJSA11111111111", + "display_name": "Nikola 2.0", + "option_codes": "MDLS,RENA,AF02,APF1,APH2,APPB,AU01,BC0R,BP00,BR00,BS00,CDM0,CH05,PBCW,CW00,DCF0,DRLH,DSH7,DV4W,FG02,FR04,HP00,IDBA,IX01,LP01,ME02,MI01,PF01,PI01,PK00,PS01,PX00,PX4D,QTVB,RFP2,SC01,SP00,SR01,SU01,TM00,TP03,TR00,UTAB,WTAS,X001,X003,X007,X011,X013,X021,X024,X027,X028,X031,X037,X040,X044,YFFC,COUS", + "color": None, + "tokens": ["abcdef1234567890", "1234567890abcdef"], + "state": "online", + "in_service": False, + "id_s": "12345678901234567", + "calendar_enabled": True, + "api_version": 7, + "backseat_token": None, + "backseat_token_updated_at": None, + "drive_state": DRIVE_STATE, + "climate_state": CLIMATE_STATE, + "charge_state": CHARGE_STATE, + "gui_settings": GUI_SETTINGS, + "vehicle_state": VEHICLE_STATE, + "vehicle_config": VEHICLE_CONFIG, +} diff --git a/tests/unit_tests/__init__.py b/tests/unit_tests/__init__.py new file mode 100644 index 00000000..2a978c16 --- /dev/null +++ b/tests/unit_tests/__init__.py @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: Apache-2.0 +""" +Python Package for controlling Tesla API. + +For more details about this api, please refer to the documentation at +https://github.com/zabuldon/teslajsonpy +""" diff --git a/tests/unit_tests/test_battery_sensor.py b/tests/unit_tests/test_battery_sensor.py new file mode 100644 index 00000000..4b515454 --- /dev/null +++ b/tests/unit_tests/test_battery_sensor.py @@ -0,0 +1,131 @@ +"""Test battery sensor.""" + +import pytest + +from tests.tesla_mock import TeslaMock + +from teslajsonpy.controller import Controller +from teslajsonpy.battery_sensor import Battery + + +def test_has_battery(monkeypatch): + """Test has_battery().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _sensor = Battery(_data, _controller) + + assert _sensor.has_battery() + + +def test_device_class(monkeypatch): + """Test device_class().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _sensor = Battery(_data, _controller) + + assert _sensor.device_class == "battery" + + +def test_get_value_on_init(monkeypatch): + """Test get_value() after initialization.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _sensor = Battery(_data, _controller) + + assert not _sensor is None + assert _sensor.get_value() is None + + +@pytest.mark.asyncio +async def test_get_value_after_update(monkeypatch): + """Test get_value() after an update.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _sensor = Battery(_data, _controller) + + await _sensor.async_update() + + assert not _sensor is None + assert not _sensor.get_value() is None + assert _sensor.get_value() == 64 + + +@pytest.mark.asyncio +async def test_battery_level(monkeypatch): + """Test battery_level().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _sensor = Battery(_data, _controller) + + await _sensor.async_update() + + assert not _sensor is None + assert not _sensor.get_value() is None + assert _sensor.battery_level() == 64 + + +@pytest.mark.asyncio +async def test_battery_charging_off(monkeypatch): + """Test battery_charging() when not charging.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["charge_state"]["charging_state"] = "Disconnected" + _sensor = Battery(_data, _controller) + + await _sensor.async_update() + + assert not _sensor is None + assert not _sensor.battery_charging() + + +@pytest.mark.asyncio +async def test_battery_charging_on(monkeypatch): + """Test battery_charging() when charging.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["charge_state"]["charging_state"] = "Charging" + _sensor = Battery(_data, _controller) + + await _sensor.async_update() + + assert not _sensor is None + assert _sensor.battery_charging() + + +@pytest.mark.asyncio +async def test_async_update(monkeypatch): + """Test async_update().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["charge_state"]["battery_level"] = 12.3 + _sensor = Battery(_data, _controller) + + await _sensor.async_update() + + assert not _sensor is None + assert not _sensor.get_value() is None + assert _sensor.get_value() == 12.3 diff --git a/tests/unit_tests/test_charger_connection_sensor.py b/tests/unit_tests/test_charger_connection_sensor.py new file mode 100644 index 00000000..7845060f --- /dev/null +++ b/tests/unit_tests/test_charger_connection_sensor.py @@ -0,0 +1,87 @@ +"""Test charger connection sensor.""" + +import pytest + +from tests.tesla_mock import TeslaMock + +from teslajsonpy.controller import Controller +from teslajsonpy.binary_sensor import ChargerConnectionSensor + + +def test_has_battery(monkeypatch): + """Test has_battery().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _sensor = ChargerConnectionSensor(_data, _controller) + + assert not _sensor.has_battery() + + +def test_get_value_on_init(monkeypatch): + """Test get_value() after initialization.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _sensor = ChargerConnectionSensor(_data, _controller) + + assert not _sensor is None + assert _sensor.get_value() is None + + +@pytest.mark.asyncio +async def test_get_value_after_update(monkeypatch): + """Test get_value() after an update.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["charge_state"]["charging_state"] = "Charging" + _sensor = ChargerConnectionSensor(_data, _controller) + + await _sensor.async_update() + + assert not _sensor is None + assert not _sensor.get_value() is None + assert _sensor.get_value() + + +@pytest.mark.asyncio +async def test_get_value_on(monkeypatch): + """Test get_value() for charging state ON.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _sensor = ChargerConnectionSensor(_data, _controller) + _data["charge_state"]["charging_state"] = "Charging" + + await _sensor.async_update() + + assert not _sensor is None + assert not _sensor.get_value() is None + assert _sensor.get_value() + + +@pytest.mark.asyncio +async def test_get_value_off(monkeypatch): + """Test get_value() for charging state OFF.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _sensor = ChargerConnectionSensor(_data, _controller) + _data["charge_state"]["charging_state"] = "Disconnected" + + await _sensor.async_update() + + assert not _sensor is None + assert not _sensor.get_value() is None + assert not _sensor.get_value() diff --git a/tests/unit_tests/test_charger_lock.py b/tests/unit_tests/test_charger_lock.py new file mode 100644 index 00000000..4c3545af --- /dev/null +++ b/tests/unit_tests/test_charger_lock.py @@ -0,0 +1,122 @@ +"""Test charger lock.""" + +import pytest + +from tests.tesla_mock import TeslaMock + +from teslajsonpy.controller import Controller +from teslajsonpy.lock import ChargerLock + + +def test_has_battery(monkeypatch): + """Test has_battery().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _lock = ChargerLock(_data, _controller) + + assert not _lock.has_battery() + + +def test_is_locked_on_init(monkeypatch): + """Test is_locked() after initialization.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _lock = ChargerLock(_data, _controller) + + assert not _lock is None + assert not _lock.is_locked() + + +@pytest.mark.asyncio +async def test_is_locked_after_update(monkeypatch): + """Test is_locked() after an update.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["charge_state"]["charge_port_door_open"] = True + _lock = ChargerLock(_data, _controller) + + await _lock.async_update() + + assert not _lock is None + assert _lock.is_locked() + + +@pytest.mark.asyncio +async def test_lock(monkeypatch): + """Test lock().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["charge_state"]["charge_port_door_open"] = False + _lock = ChargerLock(_data, _controller) + + await _lock.async_update() + await _lock.lock() + + assert not _lock is None + assert _lock.is_locked() + + +@pytest.mark.asyncio +async def test_lock_already_locked(monkeypatch): + """Test lock() when already locked.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["charge_state"]["charge_port_door_open"] = True + _lock = ChargerLock(_data, _controller) + + await _lock.async_update() + await _lock.lock() + + assert not _lock is None + assert _lock.is_locked() + + +@pytest.mark.asyncio +async def test_unlock(monkeypatch): + """Test unlock().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["charge_state"]["charge_port_door_open"] = True + _lock = ChargerLock(_data, _controller) + + await _lock.async_update() + await _lock.unlock() + + assert not _lock is None + assert not _lock.is_locked() + + +@pytest.mark.asyncio +async def test_unlock_already_unlocked(monkeypatch): + """Test unlock() when already unlocked.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["charge_state"]["charge_port_door_open"] = False + _lock = ChargerLock(_data, _controller) + + await _lock.async_update() + await _lock.unlock() + + assert not _lock is None + assert not _lock.is_locked() diff --git a/tests/unit_tests/test_charger_switch.py b/tests/unit_tests/test_charger_switch.py new file mode 100644 index 00000000..63905420 --- /dev/null +++ b/tests/unit_tests/test_charger_switch.py @@ -0,0 +1,125 @@ +"""Test charger switch.""" + +import pytest + +from tests.tesla_mock import TeslaMock + +from teslajsonpy.controller import Controller +from teslajsonpy.charger import ChargerSwitch + + +def test_has_battery(monkeypatch): + """Test has_battery().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _switch = ChargerSwitch(_data, _controller) + + assert not _switch.has_battery() + + +def test_is_charging_on_init(monkeypatch): + """Test is_charging() when not charging.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _switch = ChargerSwitch(_data, _controller) + + assert not _switch.is_charging() + + +@pytest.mark.asyncio +async def test_is_charging_on(monkeypatch): + """Test is_charging() with charging state charging.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["charge_state"]["charging_state"] = "Charging" + _switch = ChargerSwitch(_data, _controller) + + await _switch.async_update() + assert _switch.is_charging() + + +@pytest.mark.asyncio +async def test_is_charging_off(monkeypatch): + """Test is_charging() with charging state disconnected.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["charge_state"]["charging_state"] = "Disconnected" + _switch = ChargerSwitch(_data, _controller) + + await _switch.async_update() + assert not _switch.is_charging() + + +@pytest.mark.asyncio +async def test_start_charge(monkeypatch): + """Test start_charge().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["charge_state"]["charging_state"] = "Disconnected" + _switch = ChargerSwitch(_data, _controller) + await _switch.async_update() + + await _switch.start_charge() + assert _switch.is_charging() + + +@pytest.mark.asyncio +async def test_stop_charge(monkeypatch): + """Test stop_charge().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["charge_state"]["charging_state"] = "Charging" + _switch = ChargerSwitch(_data, _controller) + await _switch.async_update() + + await _switch.stop_charge() + assert not _switch.is_charging() + + +@pytest.mark.asyncio +async def test_async_update(monkeypatch): + """Test async_update().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["charge_state"]["charging_state"] = "Charging" + _switch = ChargerSwitch(_data, _controller) + + await _switch.async_update() + assert _switch.is_charging() + + +@pytest.mark.asyncio +async def test_async_update_with_change(monkeypatch): + """Test async_update().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["charge_state"]["charging_state"] = "Charging" + _switch = ChargerSwitch(_data, _controller) + + _data["charge_state"]["charging_state"] = "Disconnected" + await _switch.async_update() + assert not _switch.is_charging() diff --git a/tests/unit_tests/test_charging_sensor.py b/tests/unit_tests/test_charging_sensor.py new file mode 100644 index 00000000..1eba1da5 --- /dev/null +++ b/tests/unit_tests/test_charging_sensor.py @@ -0,0 +1,137 @@ +"""Test charging sensor.""" + +import pytest + +from tests.tesla_mock import TeslaMock + +from teslajsonpy.controller import Controller +from teslajsonpy.charger import ChargingSensor + + +def test_has_battery(monkeypatch): + """Test has_battery().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _sensor = ChargingSensor(_data, _controller) + + assert not _sensor.has_battery() + + +def test_device_class(monkeypatch): + """Test device_class().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _sensor = ChargingSensor(_data, _controller) + + assert _sensor.device_class is None + + +def test_get_value_on_init(monkeypatch): + """Test get_value() after initialization.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _sensor = ChargingSensor(_data, _controller) + + assert not _sensor is None + assert _sensor.charging_rate is None + assert _sensor.time_left is None + assert _sensor.added_range is None + assert _sensor.charge_current_request is None + assert _sensor.charger_actual_current is None + assert _sensor.charger_voltage is None + assert _sensor.charge_energy_added is None + + +@pytest.mark.asyncio +async def test_get_value_after_update(monkeypatch): + """Test get_value() after an update.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _sensor = ChargingSensor(_data, _controller) + + await _sensor.async_update() + + assert not _sensor is None + assert _sensor.charging_rate == 0 + assert _sensor.time_left == 0 + assert _sensor.added_range == 40 + assert _sensor.charge_current_request == 48 + assert _sensor.charger_actual_current == 0 + assert _sensor.charger_voltage == 0 + assert _sensor.charge_energy_added == 12.41 + + +@pytest.mark.asyncio +async def test_async_update(monkeypatch): + """Test async_update().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _sensor = ChargingSensor(_data, _controller) + + await _sensor.async_update() + + assert not _sensor is None + assert _sensor.charging_rate == 0 + assert _sensor.time_left == 0 + assert _sensor.added_range == 40 + assert _sensor.charge_current_request == 48 + assert _sensor.charger_actual_current == 0 + assert _sensor.charger_voltage == 0 + assert _sensor.charge_energy_added == 12.41 + + +@pytest.mark.asyncio +async def test_async_update_in_kmh(monkeypatch): + """Test async_update() for units in km/h.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["gui_settings"]["gui_distance_units"] = "km/hr" + _data["gui_settings"]["gui_range_display"] = "Rated" + _data["charge_state"]["charge_rate"] = 22 + _data["charge_state"]["charge_miles_added_rated"] = 44 + _sensor = ChargingSensor(_data, _controller) + + await _sensor.async_update() + + assert not _sensor is None + assert _sensor.charging_rate == 35.41 + assert _sensor.added_range == 70.81 + + +@pytest.mark.asyncio +async def test_async_update_in_mph(monkeypatch): + """Test async_update() for units in mph.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["gui_settings"]["gui_distance_units"] = "mi/hr" + _data["gui_settings"]["gui_range_display"] = "Rated" + _data["charge_state"]["charge_rate"] = 22 + _data["charge_state"]["charge_miles_added_rated"] = 44 + _sensor = ChargingSensor(_data, _controller) + + await _sensor.async_update() + + assert not _sensor is None + assert _sensor.charging_rate == 22 + assert _sensor.added_range == 44 diff --git a/tests/unit_tests/test_climate.py b/tests/unit_tests/test_climate.py new file mode 100644 index 00000000..9c60e9ff --- /dev/null +++ b/tests/unit_tests/test_climate.py @@ -0,0 +1,196 @@ +"""Test climate.""" + +import pytest + +from tests.tesla_mock import TeslaMock + +from teslajsonpy.controller import Controller +from teslajsonpy.climate import Climate + + +def test_has_battery(monkeypatch): + """Test has_battery().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _climate = Climate(_data, _controller) + + assert not _climate.has_battery() + + +def test_get_values_on_init(monkeypatch): + """Test values after initialization.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _climate = Climate(_data, _controller) + + assert not _climate is None + assert _climate.get_current_temp() is None + assert _climate.get_fan_status() is None + assert _climate.get_goal_temp() is None + assert _climate.is_hvac_enabled() is None + + +@pytest.mark.asyncio +async def test_get_values_after_update(monkeypatch): + """Test values after an update.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _climate = Climate(_data, _controller) + + await _climate.async_update() + + assert not _climate is None + + assert _climate.get_current_temp() is None + assert not _climate.get_fan_status() is None + assert _climate.get_fan_status() == 0 + assert not _climate.get_goal_temp() is None + assert _climate.get_goal_temp() == 21.6 + assert not _climate.is_hvac_enabled() is None + assert not _climate.is_hvac_enabled() + + +@pytest.mark.asyncio +async def test_get_current_temp(monkeypatch): + """Test get_current_temp().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["climate_state"]["inside_temp"] = 18.8 + _climate = Climate(_data, _controller) + + await _climate.async_update() + + assert not _climate.get_current_temp() is None + assert _climate.get_current_temp() == 18.8 + + +@pytest.mark.asyncio +async def test_get_fan_status(monkeypatch): + """Test get_fan_status().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["climate_state"]["fan_status"] = 1 + _climate = Climate(_data, _controller) + + await _climate.async_update() + + assert not _climate.get_fan_status() is None + assert _climate.get_fan_status() == 1 + + +@pytest.mark.asyncio +async def test_get_goal_temp(monkeypatch): + """Test get_goal_temp().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["climate_state"]["driver_temp_setting"] = 23.4 + _climate = Climate(_data, _controller) + + await _climate.async_update() + + assert not _climate.get_goal_temp() is None + assert _climate.get_goal_temp() == 23.4 + + +@pytest.mark.asyncio +async def test_is_hvac_enabled_on(monkeypatch): + """Test is_hvac_enabled() when is_climate_on is True.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["climate_state"]["is_climate_on"] = True + _climate = Climate(_data, _controller) + + await _climate.async_update() + + assert not _climate.is_hvac_enabled() is None + assert _climate.is_hvac_enabled() + + +@pytest.mark.asyncio +async def test_is_hvac_enabled_off(monkeypatch): + """Test is_hvac_enabled() when is_climate_on is False.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["climate_state"]["is_climate_on"] = False + _climate = Climate(_data, _controller) + + await _climate.async_update() + + assert not _climate.is_hvac_enabled() is None + assert not _climate.is_hvac_enabled() + + +@pytest.mark.asyncio +async def test_set_status_on(monkeypatch): + """Test set_status() to enabled.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _climate = Climate(_data, _controller) + + await _climate.async_update() + await _climate.set_status(True) + + assert not _climate.is_hvac_enabled() is None + assert _climate.is_hvac_enabled() + + +@pytest.mark.asyncio +async def test_set_status_off(monkeypatch): + """Test set_status() to disabled.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _climate = Climate(_data, _controller) + + await _climate.async_update() + await _climate.set_status(False) + + assert not _climate.is_hvac_enabled() is None + assert not _climate.is_hvac_enabled() + + +@pytest.mark.asyncio +async def test_set_temperature(monkeypatch): + """Test set_temperature().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _climate = Climate(_data, _controller) + + await _climate.async_update() + + await _climate.set_temperature(12.3) + + assert not _climate.get_goal_temp() is None + assert _climate.get_goal_temp() == 12.3 diff --git a/tests/unit_tests/test_gps_tracker.py b/tests/unit_tests/test_gps_tracker.py new file mode 100644 index 00000000..17e28260 --- /dev/null +++ b/tests/unit_tests/test_gps_tracker.py @@ -0,0 +1,142 @@ +"""Test GPS.""" + +import pytest + +from tests.tesla_mock import TeslaMock + +from teslajsonpy.controller import Controller +from teslajsonpy.gps import GPS + + +def test_has_battery(monkeypatch): + """Test has_battery().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _gps = GPS(_data, _controller) + + assert not _gps.has_battery() + + +def test_get_location_on_init(monkeypatch): + """Test get_location() after initialization.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _gps = GPS(_data, _controller) + + _location = _gps.get_location() + assert not _location is None + assert not "longitude" in _location + assert not "latitude" in _location + assert not "heading" in _location + assert not "speed" in _location + + +@pytest.mark.asyncio +async def test_get_location_after_update(monkeypatch): + """Test get_location() after an update.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _gps = GPS(_data, _controller) + + await _gps.async_update() + _location = _gps.get_location() + + assert not _location is None + assert _location["longitude"] == -88.111111 + assert _location["latitude"] == 33.111111 + assert _location["heading"] == 5 + assert _location["speed"] == 0 + + +@pytest.mark.asyncio +async def test_get_location_native_location(monkeypatch): + """Test get_location() with native location support.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["drive_state"]["native_location_supported"] = True + _data["drive_state"]["longitude"] = 12.345 + _data["drive_state"]["native_longitude"] = 23.456 + _data["drive_state"]["latitude"] = 34.567 + _data["drive_state"]["native_latitude"] = 45.678 + _data["drive_state"]["heading"] = 12 + _data["drive_state"]["native_heading"] = 23 + _data["drive_state"]["speed"] = 23.4 + + _gps = GPS(_data, _controller) + + await _gps.async_update() + _location = _gps.get_location() + + assert not _location is None + assert _location["longitude"] == 23.456 + assert _location["latitude"] == 45.678 + assert _location["heading"] == 23 + assert _location["speed"] == 23.4 + + +@pytest.mark.asyncio +async def test_get_location_no_native_location(monkeypatch): + """Test get_location() without native location support.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["drive_state"]["native_location_supported"] = False + _data["drive_state"]["longitude"] = 12.345 + _data["drive_state"]["native_longitude"] = 23.456 + _data["drive_state"]["latitude"] = 34.567 + _data["drive_state"]["native_latitude"] = 45.678 + _data["drive_state"]["heading"] = 12 + _data["drive_state"]["native_heading"] = 21 + _data["drive_state"]["speed"] = 23.4 + + _gps = GPS(_data, _controller) + + await _gps.async_update() + _location = _gps.get_location() + + assert not _location is None + assert _location["longitude"] == 12.345 + assert _location["latitude"] == 34.567 + assert _location["heading"] == 12 + assert _location["speed"] == 23.4 + + +@pytest.mark.asyncio +async def test_async_update(monkeypatch): + """Test async_update().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["drive_state"]["longitude"] = 12.345 + _data["drive_state"]["native_longitude"] = 12.345 + _data["drive_state"]["latitude"] = 34.567 + _data["drive_state"]["native_latitude"] = 34.567 + _data["drive_state"]["heading"] = 12 + _data["drive_state"]["native_heading"] = 12 + _data["drive_state"]["speed"] = 23.4 + _gps = GPS(_data, _controller) + + await _gps.async_update() + _location = _gps.get_location() + + assert not _location is None + assert _location["longitude"] == 12.345 + assert _location["latitude"] == 34.567 + assert _location["heading"] == 12 + assert _location["speed"] == 23.4 diff --git a/tests/unit_tests/test_lock.py b/tests/unit_tests/test_lock.py new file mode 100644 index 00000000..e20f2ce8 --- /dev/null +++ b/tests/unit_tests/test_lock.py @@ -0,0 +1,122 @@ +"""Test door lock.""" + +import pytest + +from tests.tesla_mock import TeslaMock + +from teslajsonpy.controller import Controller +from teslajsonpy.lock import Lock + + +def test_has_battery(monkeypatch): + """Test has_battery().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _lock = Lock(_data, _controller) + + assert not _lock.has_battery() + + +def test_is_locked_on_init(monkeypatch): + """Test is_locked() after initialization.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _lock = Lock(_data, _controller) + + assert not _lock is None + assert not _lock.is_locked() + + +@pytest.mark.asyncio +async def test_is_locked_after_update(monkeypatch): + """Test is_locked() after an update.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["vehicle_state"]["locked"] = True + _lock = Lock(_data, _controller) + + await _lock.async_update() + + assert not _lock is None + assert _lock.is_locked() + + +@pytest.mark.asyncio +async def test_lock(monkeypatch): + """Test lock().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["vehicle_state"]["locked"] = False + _lock = Lock(_data, _controller) + + await _lock.async_update() + await _lock.lock() + + assert not _lock is None + assert _lock.is_locked() + + +@pytest.mark.asyncio +async def test_lock_already_locked(monkeypatch): + """Test lock() when already locked.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["vehicle_state"]["locked"] = True + _lock = Lock(_data, _controller) + + await _lock.async_update() + await _lock.lock() + + assert not _lock is None + assert _lock.is_locked() + + +@pytest.mark.asyncio +async def test_unlock(monkeypatch): + """Test unlock().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["vehicle_state"]["locked"] = True + _lock = Lock(_data, _controller) + + await _lock.async_update() + await _lock.unlock() + + assert not _lock is None + assert not _lock.is_locked() + + +@pytest.mark.asyncio +async def test_unlock_already_unlocked(monkeypatch): + """Test unlock() when already unlocked.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["vehicle_state"]["locked"] = False + _lock = Lock(_data, _controller) + + await _lock.async_update() + await _lock.unlock() + + assert not _lock is None + assert not _lock.is_locked() diff --git a/tests/unit_tests/test_odometer_sensor.py b/tests/unit_tests/test_odometer_sensor.py new file mode 100644 index 00000000..637071b3 --- /dev/null +++ b/tests/unit_tests/test_odometer_sensor.py @@ -0,0 +1,117 @@ +"""Test odometer sensor.""" + +import pytest + +from tests.tesla_mock import TeslaMock + +from teslajsonpy.controller import Controller +from teslajsonpy.gps import Odometer + + +def test_has_battery(monkeypatch): + """Test has_battery().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _odometer = Odometer(_data, _controller) + + assert not _odometer.has_battery() + + +def test_device_class(monkeypatch): + """Test device_class().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _odometer = Odometer(_data, _controller) + + assert _odometer.device_class is None + + +def test_get_value_on_init(monkeypatch): + """Test get_value() after initialization.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _odometer = Odometer(_data, _controller) + + assert not _odometer is None + assert _odometer.get_value() is None + + +@pytest.mark.asyncio +async def test_get_value_after_update(monkeypatch): + """Test get_value() after an update.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _odometer = Odometer(_data, _controller) + + await _odometer.async_update() + + assert not _odometer is None + assert _odometer.get_value() == 33561.4 + + +@pytest.mark.asyncio +async def test_async_update(monkeypatch): + """Test async_update().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["vehicle_state"]["odometer"] = 12345.6789 + _odometer = Odometer(_data, _controller) + + await _odometer.async_update() + + assert not _odometer is None + assert not _odometer.get_value() is None + assert _odometer.get_value() == 12345.7 + + +@pytest.mark.asyncio +async def test_async_update_in_kmh(monkeypatch): + """Test async_update() for units in km/h.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["gui_settings"]["gui_distance_units"] = "km/hr" + _data["vehicle_state"]["odometer"] = 12345.6789 + _odometer = Odometer(_data, _controller) + + await _odometer.async_update() + + assert not _odometer is None + assert not _odometer.get_value() is None + assert _odometer.get_value() == 12345.7 + + +@pytest.mark.asyncio +async def test_async_update_in_mph(monkeypatch): + """Test async_update() for units in mph.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["gui_settings"]["gui_distance_units"] = "mi/hr" + _data["vehicle_state"]["odometer"] = 12345.6789 + _odometer = Odometer(_data, _controller) + + await _odometer.async_update() + + assert not _odometer is None + assert not _odometer.get_value() is None + assert _odometer.get_value() == 12345.7 diff --git a/tests/unit_tests/test_online_sensor.py b/tests/unit_tests/test_online_sensor.py new file mode 100644 index 00000000..47ab795f --- /dev/null +++ b/tests/unit_tests/test_online_sensor.py @@ -0,0 +1,98 @@ +"""Test online sensor.""" + +import pytest + +from tests.tesla_mock import TeslaMock + +from teslajsonpy.controller import Controller +from teslajsonpy.binary_sensor import OnlineSensor + + +def test_has_battery(monkeypatch): + """Test has_battery().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _sensor = OnlineSensor(_data, _controller) + + assert not _sensor.has_battery() + + +def test_get_value_on_init(monkeypatch): + """Test get_value() after initialization.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _sensor = OnlineSensor(_data, _controller) + + assert not _sensor is None + assert _sensor.get_value() is None + + +@pytest.mark.asyncio +async def test_get_value_after_update(monkeypatch): + """Test get_value() after an update.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + monkeypatch.setitem(_controller.car_online, "5YJSA11111111111", True) + monkeypatch.setitem( + _controller.car_state, "5YJSA11111111111", TeslaMock.data_request_vehicle() + ) + + _data = _mock.data_request_vehicle() + _sensor = OnlineSensor(_data, _controller) + + await _sensor.async_update() + + assert not _sensor is None + assert not _sensor.get_value() is None + assert _sensor.get_value() + + +@pytest.mark.asyncio +async def test_get_value_on(monkeypatch): + """Test get_value() for online mode.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + monkeypatch.setitem(_controller.car_online, "5YJSA11111111111", True) + monkeypatch.setitem( + _controller.car_state, "5YJSA11111111111", TeslaMock.data_request_vehicle() + ) + + _data = _mock.data_request_vehicle() + _sensor = OnlineSensor(_data, _controller) + _data["state"] = "online" + + await _sensor.async_update() + + assert not _sensor is None + assert not _sensor.get_value() is None + assert _sensor.get_value() + + +@pytest.mark.asyncio +async def test_get_value_off(monkeypatch): + """Test get_value() for offline mode.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + monkeypatch.setitem(_controller.car_online, "5YJSA11111111111", False) + monkeypatch.setitem( + _controller.car_state, "5YJSA11111111111", TeslaMock.data_request_vehicle() + ) + + _data = _mock.data_request_vehicle() + _sensor = OnlineSensor(_data, _controller) + _data["state"] = "asleep" + + await _sensor.async_update() + + assert not _sensor is None + assert not _sensor.get_value() is None + assert not _sensor.get_value() diff --git a/tests/unit_tests/test_parking_sensor.py b/tests/unit_tests/test_parking_sensor.py new file mode 100644 index 00000000..bcd45c63 --- /dev/null +++ b/tests/unit_tests/test_parking_sensor.py @@ -0,0 +1,86 @@ +"""Test parking sensor.""" + +import pytest + +from tests.tesla_mock import TeslaMock + +from teslajsonpy.controller import Controller +from teslajsonpy.binary_sensor import ParkingSensor + + +def test_has_battery(monkeypatch): + """Test has_battery().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _sensor = ParkingSensor(_data, _controller) + + assert not _sensor.has_battery() + + +def test_get_value_on_init(monkeypatch): + """Test get_value() after initialization.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _sensor = ParkingSensor(_data, _controller) + + assert not _sensor is None + assert _sensor.get_value() is None + + +@pytest.mark.asyncio +async def test_get_value_after_update(monkeypatch): + """Test get_value() after an update.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _sensor = ParkingSensor(_data, _controller) + + await _sensor.async_update() + + assert not _sensor is None + assert not _sensor.get_value() is None + assert _sensor.get_value() + + +@pytest.mark.asyncio +async def test_get_value_on(monkeypatch): + """Test get_value() for parking mode ON.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _sensor = ParkingSensor(_data, _controller) + _data["drive_state"]["shift_state"] = "P" + + await _sensor.async_update() + + assert not _sensor is None + assert not _sensor.get_value() is None + assert _sensor.get_value() + + +@pytest.mark.asyncio +async def test_get_value_off(monkeypatch): + """Test get_value() for parking mode OFF.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _sensor = ParkingSensor(_data, _controller) + _data["drive_state"]["shift_state"] = "N" + + await _sensor.async_update() + + assert not _sensor is None + assert not _sensor.get_value() is None + assert not _sensor.get_value() diff --git a/tests/unit_tests/test_range_sensor.py b/tests/unit_tests/test_range_sensor.py new file mode 100644 index 00000000..2f12130f --- /dev/null +++ b/tests/unit_tests/test_range_sensor.py @@ -0,0 +1,165 @@ +"""Test range sensor.""" + +import pytest + +from tests.tesla_mock import TeslaMock + +from teslajsonpy.controller import Controller +from teslajsonpy.battery_sensor import Range + + +def test_has_battery(monkeypatch): + """Test has_battery().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _range = Range(_data, _controller) + + assert not _range.has_battery() + + +def test_device_class(monkeypatch): + """Test device_class().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _range = Range(_data, _controller) + + assert _range.device_class is None + + +def test_get_value_on_init(monkeypatch): + """Test get_value() after initialization.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _range = Range(_data, _controller) + + assert not _range is None + assert _range.get_value() is None + + +@pytest.mark.asyncio +async def test_get_value_after_update(monkeypatch): + """Test get_value() after an update.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _range = Range(_data, _controller) + + await _range.async_update() + + assert not _range is None + assert not _range.get_value() is None + assert _range.get_value() == 167.96 + + +@pytest.mark.asyncio +async def test_get_value_rated_on(monkeypatch): + """Test get_value() for range display 'Rated'.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _range = Range(_data, _controller) + _data["gui_settings"]["gui_range_display"] = "Rated" + _data["charge_state"]["battery_range"] = 123.45 + _data["charge_state"]["est_battery_range"] = 234.56 + _data["charge_state"]["ideal_battery_range"] = 345.67 + await _range.async_update() + + assert not _range is None + assert not _range.get_value() is None + assert _range.get_value() == 123.45 + + +@pytest.mark.asyncio +async def test_get_value_rated_off(monkeypatch): + """Test get_value() for range display not 'Rated'.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _range = Range(_data, _controller) + _data["gui_settings"]["gui_range_display"] = "Other" + _data["charge_state"]["battery_range"] = 123.45 + _data["charge_state"]["est_battery_range"] = 234.56 + _data["charge_state"]["ideal_battery_range"] = 345.67 + await _range.async_update() + + assert not _range is None + assert not _range.get_value() is None + assert _range.get_value() == 345.67 + + +@pytest.mark.asyncio +async def test_get_value_in_kmh(monkeypatch): + """Test get_value() for units in km/h'.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _range = Range(_data, _controller) + _data["gui_settings"]["gui_distance_units"] = "km/hr" + _data["gui_settings"]["gui_range_display"] = "Rated" + _data["charge_state"]["battery_range"] = 123.45 + _data["charge_state"]["est_battery_range"] = 234.56 + _data["charge_state"]["ideal_battery_range"] = 345.67 + await _range.async_update() + + assert not _range is None + assert not _range.get_value() is None + assert _range.get_value() == 123.45 + + +@pytest.mark.asyncio +async def test_get_value_in_mph(monkeypatch): + """Test get_value() for units in mph'.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _range = Range(_data, _controller) + _data["gui_settings"]["gui_distance_units"] = "mi/hr" + _data["gui_settings"]["gui_range_display"] = "Rated" + _data["charge_state"]["battery_range"] = 123.45 + _data["charge_state"]["est_battery_range"] = 234.56 + _data["charge_state"]["ideal_battery_range"] = 345.67 + await _range.async_update() + + assert not _range is None + assert not _range.get_value() is None + assert _range.get_value() == 123.45 + + +@pytest.mark.asyncio +async def test_async_update(monkeypatch): + """Test async_update().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["gui_settings"]["gui_range_display"] = "Rated" + _data["charge_state"]["battery_range"] = 123.45 + _data["charge_state"]["est_battery_range"] = 234.56 + _data["charge_state"]["ideal_battery_range"] = 345.67 + _range = Range(_data, _controller) + + await _range.async_update() + + assert not _range is None + assert not _range.get_value() is None + assert _range.get_value() == 123.45 diff --git a/tests/unit_tests/test_range_switch.py b/tests/unit_tests/test_range_switch.py new file mode 100644 index 00000000..e8370880 --- /dev/null +++ b/tests/unit_tests/test_range_switch.py @@ -0,0 +1,125 @@ +"""Test range switch.""" + +import pytest + +from tests.tesla_mock import TeslaMock + +from teslajsonpy.controller import Controller +from teslajsonpy.charger import RangeSwitch + + +def test_has_battery(monkeypatch): + """Test has_battery().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _switch = RangeSwitch(_data, _controller) + + assert not _switch.has_battery() + + +def test_is_maxrange_on_init(monkeypatch): + """Test is_maxrange() when not charging.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _switch = RangeSwitch(_data, _controller) + + assert not _switch.is_maxrange() + + +@pytest.mark.asyncio +async def test_is_maxrange_on(monkeypatch): + """Test is_maxrange() with charging state charging.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["charge_state"]["charge_to_max_range"] = True + _switch = RangeSwitch(_data, _controller) + + await _switch.async_update() + assert _switch.is_maxrange() + + +@pytest.mark.asyncio +async def test_is_maxrange_off(monkeypatch): + """Test is_maxrange() with charging state disconnected.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["charge_state"]["charge_to_max_range"] = False + _switch = RangeSwitch(_data, _controller) + + await _switch.async_update() + assert not _switch.is_maxrange() + + +@pytest.mark.asyncio +async def test_set_max(monkeypatch): + """Test set_max().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["charge_state"]["charge_to_max_range"] = False + _switch = RangeSwitch(_data, _controller) + await _switch.async_update() + + await _switch.set_max() + assert _switch.is_maxrange() + + +@pytest.mark.asyncio +async def test_set_standard(monkeypatch): + """Test set_standard().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["charge_state"]["charge_to_max_range"] = True + _switch = RangeSwitch(_data, _controller) + await _switch.async_update() + + await _switch.set_standard() + assert not _switch.is_maxrange() + + +@pytest.mark.asyncio +async def test_async_update(monkeypatch): + """Test async_update().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["charge_state"]["charge_to_max_range"] = True + _switch = RangeSwitch(_data, _controller) + + await _switch.async_update() + assert _switch.is_maxrange() + + +@pytest.mark.asyncio +async def test_async_update_with_change(monkeypatch): + """Test async_update() after an update.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["charge_state"]["charge_to_max_range"] = True + _switch = RangeSwitch(_data, _controller) + + _data["charge_state"]["charge_to_max_range"] = False + await _switch.async_update() + assert not _switch.is_maxrange() diff --git a/tests/unit_tests/test_sentry_mode_switch.py b/tests/unit_tests/test_sentry_mode_switch.py new file mode 100644 index 00000000..0025c04b --- /dev/null +++ b/tests/unit_tests/test_sentry_mode_switch.py @@ -0,0 +1,257 @@ +"""Test sentry mode switch.""" + +import pytest + +from tests.tesla_mock import TeslaMock + +from teslajsonpy.controller import Controller +from teslajsonpy.sentry_mode import SentryModeSwitch + + +def test_has_battery(monkeypatch): + """Test has_battery().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _switch = SentryModeSwitch(_data, _controller) + + assert not _switch.has_battery() + + +def test_available_true(monkeypatch): + """Test available() when flag sentry_mode_available is false.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["vehicle_state"]["sentry_mode_available"] = True + _switch = SentryModeSwitch(_data, _controller) + + assert _switch.available() + + +def test_available_false(monkeypatch): + """Test available() when flag sentry_mode_available is false.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["vehicle_state"]["sentry_mode_available"] = False + _switch = SentryModeSwitch(_data, _controller) + + assert not _switch.available() + + +def test_is_on_false(monkeypatch): + """Test is_on() when flag sentry_mode is false.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["vehicle_state"]["sentry_mode_available"] = True + _data["vehicle_state"]["sentry_mode"] = False + _switch = SentryModeSwitch(_data, _controller) + + assert not _switch.is_on() + + +def test_is_on_true(monkeypatch): + """Test is_on() when flag sentry_mode is true.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["vehicle_state"]["sentry_mode_available"] = True + _data["vehicle_state"]["sentry_mode"] = True + _switch = SentryModeSwitch(_data, _controller) + + assert _switch.is_on() + + +def test_is_on_unavailable(monkeypatch): + """Test is_on() when flag sentry_mode_available is false.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["vehicle_state"]["sentry_mode_available"] = False + _data["vehicle_state"]["sentry_mode"] = True + _switch = SentryModeSwitch(_data, _controller) + + assert not _switch.is_on() + + +@pytest.mark.asyncio +async def test_enable_sentry_mode(monkeypatch): + """Test enable_sentry_mode().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["vehicle_state"]["sentry_mode_available"] = True + _data["vehicle_state"]["sentry_mode"] = False + _switch = SentryModeSwitch(_data, _controller) + + await _switch.enable_sentry_mode() + assert _switch.is_on() + + +@pytest.mark.asyncio +async def test_enable_sentry_mode_already_enabled(monkeypatch): + """Test enable_sentry_mode() when already enabled.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["vehicle_state"]["sentry_mode_available"] = True + _data["vehicle_state"]["sentry_mode"] = True + _switch = SentryModeSwitch(_data, _controller) + + await _switch.enable_sentry_mode() + assert _switch.is_on() + + +@pytest.mark.asyncio +async def test_enable_sentry_mode_not_available(monkeypatch): + """Test enable_sentry_mode() when sentry mode is not available.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["vehicle_state"]["sentry_mode_available"] = False + _data["vehicle_state"]["sentry_mode"] = False + _switch = SentryModeSwitch(_data, _controller) + + await _switch.enable_sentry_mode() + assert not _switch.is_on() + + +@pytest.mark.asyncio +async def test_disable_sentry_mode(monkeypatch): + """Test disable_sentry_mode().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["vehicle_state"]["sentry_mode_available"] = True + _data["vehicle_state"]["sentry_mode"] = True + _switch = SentryModeSwitch(_data, _controller) + + await _switch.disable_sentry_mode() + assert not _switch.is_on() + + +@pytest.mark.asyncio +async def test_disable_sentry_mode_already_disabled(monkeypatch): + """Test disable_sentry_mode() when already disabled.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["vehicle_state"]["sentry_mode_available"] = True + _data["vehicle_state"]["sentry_mode"] = False + _switch = SentryModeSwitch(_data, _controller) + + await _switch.disable_sentry_mode() + assert not _switch.is_on() + + +@pytest.mark.asyncio +async def test_disable_sentry_mode_not_available(monkeypatch): + """Test disable_sentry_mode() when sentry mode is not available.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["vehicle_state"]["sentry_mode_available"] = False + _data["vehicle_state"]["sentry_mode"] = False + _switch = SentryModeSwitch(_data, _controller) + + await _switch.enable_sentry_mode() + assert not _switch.is_on() + + +@pytest.mark.asyncio +async def test_async_update(monkeypatch): + """Test async_update().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["vehicle_state"]["sentry_mode_available"] = True + _data["vehicle_state"]["sentry_mode"] = False + _switch = SentryModeSwitch(_data, _controller) + + await _switch.async_update() + assert not _switch.is_on() + + +@pytest.mark.asyncio +async def test_async_update_with_change(monkeypatch): + """Test async_update() with a state change.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["vehicle_state"]["sentry_mode_available"] = True + _data["vehicle_state"]["sentry_mode"] = False + _switch = SentryModeSwitch(_data, _controller) + + # Change state value + _data["vehicle_state"]["sentry_mode"] = True + + await _switch.async_update() + assert _switch.is_on() + + +@pytest.mark.asyncio +async def test_async_update_with_change_but_not_available(monkeypatch): + """Test async_update() with a state change, but sentry mode is not available.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["vehicle_state"]["sentry_mode_available"] = False + _data["vehicle_state"]["sentry_mode"] = False + _switch = SentryModeSwitch(_data, _controller) + + # Change state value + _data["vehicle_state"]["sentry_mode"] = True + + await _switch.async_update() + assert not _switch.is_on() + + +@pytest.mark.asyncio +async def test_async_update_with_change_same_value(monkeypatch): + """Test async_update() with a state change, using same value.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["vehicle_state"]["sentry_mode_available"] = True + _data["vehicle_state"]["sentry_mode"] = True + _switch = SentryModeSwitch(_data, _controller) + + # Change state value + _data["vehicle_state"]["sentry_mode"] = True + + await _switch.async_update() + assert _switch.is_on() diff --git a/tests/unit_tests/test_temp_sensor.py b/tests/unit_tests/test_temp_sensor.py new file mode 100644 index 00000000..4de9bc25 --- /dev/null +++ b/tests/unit_tests/test_temp_sensor.py @@ -0,0 +1,67 @@ +"""Test temp sensor.""" + +import pytest + +from tests.tesla_mock import TeslaMock + +from teslajsonpy.controller import Controller +from teslajsonpy.climate import TempSensor + + +def test_has_battery(monkeypatch): + """Test has_battery().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _sensor = TempSensor(_data, _controller) + + assert not _sensor.has_battery() + + +def test_device_class(monkeypatch): + """Test device_class().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _sensor = TempSensor(_data, _controller) + + assert _sensor.device_class == "temperature" + + +def test_get_temp_on_init(monkeypatch): + """Test get_inside_temp() and get_outside_temp() after initialization.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _sensor = TempSensor(_data, _controller) + + assert not _sensor is None + assert _sensor.get_inside_temp() is None + assert _sensor.get_outside_temp() is None + + +@pytest.mark.asyncio +async def test_get_temp_after_update(monkeypatch): + """Test get_inside_temp() and get_outside_temp() after an update.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _data["climate_state"]["inside_temp"] = 18.8 + _data["climate_state"]["outside_temp"] = 22.2 + _sensor = TempSensor(_data, _controller) + + await _sensor.async_update() + + assert not _sensor is None + assert not _sensor.get_inside_temp() is None + assert not _sensor.get_outside_temp() is None + assert _sensor.get_inside_temp() == 18.8 + assert _sensor.get_outside_temp() == 22.2 diff --git a/tests/unit_tests/test_tesla_exception.py b/tests/unit_tests/test_tesla_exception.py new file mode 100644 index 00000000..bed9224b --- /dev/null +++ b/tests/unit_tests/test_tesla_exception.py @@ -0,0 +1,153 @@ +"""Test Tesla exception.""" + +from teslajsonpy.exceptions import TeslaException + + +def test_code_as_string(): + """Test error with a string code.""" + + _err = TeslaException("dummy") + + assert not _err is None + assert _err.code == "dummy" + assert _err.message == "dummy" + + +def test_code_lt_300(): + """Test error with a code less than 300.""" + + _err = TeslaException(299) + + assert not _err is None + assert _err.code == 299 + assert _err.message == "" + + +def test_code_400(): + """Test error 400.""" + + _err = TeslaException(400) + + assert not _err is None + assert _err.code == 400 + assert _err.message == "UNKNOWN_ERROR_400" + + +def test_code_401(): + """Test error 401.""" + + _err = TeslaException(401) + + assert not _err is None + assert _err.code == 401 + assert _err.message == "UNAUTHORIZED" + + +def test_code_402(): + """Test error 402.""" + + _err = TeslaException(402) + + assert not _err is None + assert _err.code == 402 + assert _err.message == "UNKNOWN_ERROR_402" + + +def test_code_403(): + """Test error 403.""" + + _err = TeslaException(403) + + assert not _err is None + assert _err.code == 403 + assert _err.message == "UNKNOWN_ERROR_403" + + +def test_code_404(): + """Test error 404.""" + + _err = TeslaException(404) + + assert not _err is None + assert _err.code == 404 + assert _err.message == "NOT_FOUND" + + +def test_code_405(): + """Test error 405.""" + + _err = TeslaException(405) + + assert not _err is None + assert _err.code == 405 + assert _err.message == "MOBILE_ACCESS_DISABLED" + + +def test_code_408(): + """Test error 408.""" + + _err = TeslaException(408) + + assert not _err is None + assert _err.code == 408 + assert _err.message == "VEHICLE_UNAVAILABLE" + + +def test_code_423(): + """Test error 423.""" + + _err = TeslaException(423) + + assert not _err is None + assert _err.code == 423 + assert _err.message == "ACCOUNT_LOCKED" + + +def test_code_429(): + """Test error 429.""" + + _err = TeslaException(429) + + assert not _err is None + assert _err.code == 429 + assert _err.message == "TOO_MANY_REQUESTS" + + +def test_code_500(): + """Test error 500.""" + + _err = TeslaException(500) + + assert not _err is None + assert _err.code == 500 + assert _err.message == "SERVER_ERROR" + + +def test_code_503(): + """Test error 503.""" + + _err = TeslaException(503) + + assert not _err is None + assert _err.code == 503 + assert _err.message == "SERVICE_MAINTENANCE" + + +def test_code_504(): + """Test error 504.""" + + _err = TeslaException(504) + + assert not _err is None + assert _err.code == 504 + assert _err.message == "UPSTREAM_TIMEOUT" + + +def test_code_505(): + """Test error 505.""" + + _err = TeslaException(505) + + assert not _err is None + assert _err.code == 505 + assert _err.message == "UNKNOWN_ERROR_505" diff --git a/tests/unit_tests/test_vehicle_device.py b/tests/unit_tests/test_vehicle_device.py new file mode 100644 index 00000000..f51a0500 --- /dev/null +++ b/tests/unit_tests/test_vehicle_device.py @@ -0,0 +1,141 @@ +"""Test vehicle device.""" + +import pytest + +from tests.tesla_mock import TeslaMock + +from teslajsonpy.controller import Controller +from teslajsonpy.vehicle import VehicleDevice + + +def test_is_armable(monkeypatch): + """Test is_armable().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _device = VehicleDevice(_data, _controller) + + assert not _device.is_armable() + + +def test_is_armed(monkeypatch): + """Test is_armed().""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _device = VehicleDevice(_data, _controller) + + assert not _device.is_armed() + + +def test_values_on_init(monkeypatch): + """Test values after initialization.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _device = VehicleDevice(_data, _controller) + + assert not _device is None + + assert not _device.car_name() is None + assert _device.car_name() == "Nikola 2.0" + + assert not _device.car_type is None + assert _device.car_type == "Model S" + + assert not _device.car_version is None + assert _device.car_version == "" + + assert not _device.id() is None + assert _device.id() == 12345678901234567 + + assert not _device.sentry_mode_available is None + assert _device.sentry_mode_available + + assert not _device.vehicle_id is None + assert _device.vehicle_id() == 1234567890 + + +@pytest.mark.asyncio +async def test_values_after_update(monkeypatch): + """Test values after update.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + _data = _mock.data_request_vehicle() + _device = VehicleDevice(_data, _controller) + + await _device.async_update() + + assert not _device is None + + assert not _device.car_name() is None + assert _device.car_name() == "Nikola 2.0" + + assert not _device.car_type is None + assert _device.car_type == "Model S" + + assert not _device.car_version is None + assert _device.car_version == "2019.40.2.1 38f55d9f9205" + + assert not _device.id() is None + assert _device.id() == 12345678901234567 + + assert not _device.sentry_mode_available is None + assert _device.sentry_mode_available + + assert not _device.vehicle_id is None + assert _device.vehicle_id() == 1234567890 + + +@pytest.mark.asyncio +async def test_assumed_state_online(monkeypatch): + # pylint: disable=protected-access + """Test assumed_state() with online vehicle.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + # TODO: Should not have protected access here (see vehicle.py) + monkeypatch.setitem(_controller.car_online, 12345678901234567, True) + monkeypatch.setitem(_controller._last_update_time, 12345678901234567, 100) + monkeypatch.setitem(_controller._last_wake_up_time, 12345678901234567, 0) + + _data = _mock.data_request_vehicle() + _device = VehicleDevice(_data, _controller) + + await _device.async_update() + + assert not _device is None + assert not _device.assumed_state() is None + assert not _device.assumed_state() + + +@pytest.mark.asyncio +async def test_assumed_state_offline(monkeypatch): + # pylint: disable=protected-access + """Test assumed_state() with offline vehicle.""" + + _mock = TeslaMock(monkeypatch) + _controller = Controller(None) + + # TODO: Should not have protected access here (see vehicle.py) + monkeypatch.setitem(_controller.car_online, 12345678901234567, False) + monkeypatch.setitem(_controller._last_update_time, 12345678901234567, 1000) + monkeypatch.setitem(_controller._last_wake_up_time, 12345678901234567, 0) + + _data = _mock.data_request_vehicle() + _device = VehicleDevice(_data, _controller) + + await _device.async_update() + + assert not _device is None + assert not _device.assumed_state() is None + assert _device.assumed_state() diff --git a/tox.ini b/tox.ini index 829e62d2..63d0b363 100644 --- a/tox.ini +++ b/tox.ini @@ -29,3 +29,6 @@ deps = pipenv commands= make init make typing + +[pytest] +junit_family=xunit1