diff --git a/tests/iot/modules/test_ambientlight.py b/tests/iot/modules/test_ambientlight.py index ff2bd92c2..9abae4054 100644 --- a/tests/iot/modules/test_ambientlight.py +++ b/tests/iot/modules/test_ambientlight.py @@ -8,7 +8,7 @@ @dimmer_iot -def test_ambientlight_getters(dev: IotDimmer): +def test_ambientlight_getters(dev: IotDimmer) -> None: assert Module.IotAmbientLight in dev.modules ambientlight: AmbientLight = dev.modules[Module.IotAmbientLight] @@ -22,7 +22,7 @@ def test_ambientlight_getters(dev: IotDimmer): @dimmer_iot -async def test_ambientlight_setters(dev: IotDimmer, mocker: MockerFixture): +async def test_ambientlight_setters(dev: IotDimmer, mocker: MockerFixture) -> None: ambientlight: AmbientLight = dev.modules[Module.IotAmbientLight] query_helper = mocker.patch("kasa.iot.IotDimmer._query_helper") @@ -36,7 +36,7 @@ async def test_ambientlight_setters(dev: IotDimmer, mocker: MockerFixture): @dimmer_iot -def test_ambientlight_feature(dev: IotDimmer): +def test_ambientlight_feature(dev: IotDimmer) -> None: assert Module.IotAmbientLight in dev.modules ambientlight: AmbientLight = dev.modules[Module.IotAmbientLight] diff --git a/tests/iot/modules/test_cloud.py b/tests/iot/modules/test_cloud.py index ec7f8f834..9c4aca98c 100644 --- a/tests/iot/modules/test_cloud.py +++ b/tests/iot/modules/test_cloud.py @@ -4,7 +4,7 @@ @device_iot -def test_cloud(dev: Device): +def test_cloud(dev: Device) -> None: cloud = dev.modules.get(Module.IotCloud) assert cloud info = cloud.info diff --git a/tests/iot/modules/test_dimmer.py b/tests/iot/modules/test_dimmer.py index e4b267610..a5a009045 100644 --- a/tests/iot/modules/test_dimmer.py +++ b/tests/iot/modules/test_dimmer.py @@ -14,7 +14,7 @@ @dimmer_iot -def test_dimmer_getters(dev: IotDimmer): +def test_dimmer_getters(dev: IotDimmer) -> None: assert Module.IotDimmer in dev.modules dimmer: Dimmer = dev.modules[Module.IotDimmer] @@ -27,7 +27,7 @@ def test_dimmer_getters(dev: IotDimmer): @dimmer_iot -async def test_dimmer_setters(dev: IotDimmer, mocker: MockerFixture): +async def test_dimmer_setters(dev: IotDimmer, mocker: MockerFixture) -> None: dimmer: Dimmer = dev.modules[Module.IotDimmer] query_helper = mocker.patch("kasa.iot.IotDimmer._query_helper") @@ -65,7 +65,7 @@ async def test_dimmer_setters(dev: IotDimmer, mocker: MockerFixture): @dimmer_iot -async def test_dimmer_setter_min(dev: IotDimmer, mocker: MockerFixture): +async def test_dimmer_setter_min(dev: IotDimmer, mocker: MockerFixture) -> None: dimmer: Dimmer = dev.modules[Module.IotDimmer] query_helper = mocker.patch("kasa.iot.IotDimmer._query_helper") @@ -103,7 +103,7 @@ async def test_dimmer_setter_min(dev: IotDimmer, mocker: MockerFixture): @dimmer_iot -async def test_dimmer_setter_max(dev: IotDimmer, mocker: MockerFixture): +async def test_dimmer_setter_max(dev: IotDimmer, mocker: MockerFixture) -> None: dimmer: Dimmer = dev.modules[Module.IotDimmer] query_helper = mocker.patch("kasa.iot.IotDimmer._query_helper") @@ -141,7 +141,7 @@ async def test_dimmer_setter_max(dev: IotDimmer, mocker: MockerFixture): @dimmer_iot -async def test_dimmer_setters_min_oob(dev: IotDimmer, mocker: MockerFixture): +async def test_dimmer_setters_min_oob(dev: IotDimmer, mocker: MockerFixture) -> None: dimmer: Dimmer = dev.modules[Module.IotDimmer] query_helper = mocker.patch("kasa.iot.IotDimmer._query_helper") @@ -173,7 +173,7 @@ async def test_dimmer_setters_min_oob(dev: IotDimmer, mocker: MockerFixture): @dimmer_iot -async def test_dimmer_setters_max_oob(dev: IotDimmer, mocker: MockerFixture): +async def test_dimmer_setters_max_oob(dev: IotDimmer, mocker: MockerFixture) -> None: dimmer: Dimmer = dev.modules[Module.IotDimmer] query_helper = mocker.patch("kasa.iot.IotDimmer._query_helper") diff --git a/tests/iot/modules/test_emeter.py b/tests/iot/modules/test_emeter.py index 54fd02b2e..160cd5a5d 100644 --- a/tests/iot/modules/test_emeter.py +++ b/tests/iot/modules/test_emeter.py @@ -12,7 +12,7 @@ from kasa import Device, DeviceType, EmeterStatus, Module from kasa.interfaces.energy import Energy -from kasa.iot import IotStrip +from kasa.iot import IotDevice, IotStrip from kasa.iot.modules.emeter import Emeter from tests.conftest import has_emeter_iot, no_emeter_iot @@ -37,22 +37,22 @@ @no_emeter_iot -async def test_no_emeter(dev): +async def test_no_emeter(dev: IotDevice) -> None: assert not dev.has_emeter with pytest.raises(AttributeError): - await dev.get_emeter_realtime() + await dev.get_emeter_realtime() # type: ignore[attr-defined] with pytest.raises(AttributeError): - await dev.get_emeter_daily() + await dev.get_emeter_daily() # type: ignore[attr-defined] with pytest.raises(AttributeError): - await dev.get_emeter_monthly() + await dev.get_emeter_monthly() # type: ignore[attr-defined] with pytest.raises(AttributeError): - await dev.erase_emeter_stats() + await dev.erase_emeter_stats() # type: ignore[attr-defined] @has_emeter_iot -async def test_get_emeter_realtime(dev): +async def test_get_emeter_realtime(dev: IotDevice) -> None: emeter = dev.modules[Module.Energy] current_emeter = await emeter.get_status() @@ -65,7 +65,7 @@ async def test_get_emeter_realtime(dev): @has_emeter_iot @pytest.mark.requires_dummy -async def test_get_emeter_daily(dev): +async def test_get_emeter_daily(dev: IotDevice) -> None: emeter = dev.modules[Module.Energy] assert await emeter.get_daily_stats(year=1900, month=1) == {} @@ -85,7 +85,7 @@ async def test_get_emeter_daily(dev): @has_emeter_iot @pytest.mark.requires_dummy -async def test_get_emeter_monthly(dev): +async def test_get_emeter_monthly(dev: IotDevice) -> None: emeter = dev.modules[Module.Energy] assert await emeter.get_monthly_stats(year=1900) == {} @@ -104,7 +104,7 @@ async def test_get_emeter_monthly(dev): @has_emeter_iot -async def test_emeter_status(dev): +async def test_emeter_status(dev: IotDevice) -> None: emeter = dev.modules[Module.Energy] d = await emeter.get_status() @@ -126,21 +126,21 @@ async def test_emeter_status(dev): @pytest.mark.skip("not clearing your stats..") @has_emeter_iot -async def test_erase_emeter_stats(dev): +async def test_erase_emeter_stats(dev: IotDevice) -> None: emeter = dev.modules[Module.Energy] await emeter.erase_emeter() @has_emeter_iot -async def test_current_consumption(dev): +async def test_current_consumption(dev: IotDevice) -> None: emeter = dev.modules[Module.Energy] x = emeter.current_consumption assert isinstance(x, float) assert x >= 0.0 -async def test_emeterstatus_missing_current(): +async def test_emeterstatus_missing_current() -> None: """KL125 does not report 'current' for emeter.""" regular = EmeterStatus( {"err_code": 0, "power_mw": 0, "total_wh": 13, "current_ma": 123} @@ -154,13 +154,13 @@ async def test_emeterstatus_missing_current(): assert missing_current["current"] is None -async def test_emeter_daily(): +async def test_emeter_daily() -> None: """Test fetching the emeter for today. This test uses inline data since the fixtures will not have data for the current day. """ - emeter_data = { + emeter_data: dict = { "get_daystat": { "day_list": [{"day": 1, "energy_wh": 8, "month": 1, "year": 2023}], "err_code": 0, @@ -181,7 +181,7 @@ def data(self): @has_emeter_iot -async def test_supported(dev: Device): +async def test_supported(dev: Device) -> None: energy_module = dev.modules.get(Module.Energy) assert energy_module diff --git a/tests/iot/modules/test_homekit.py b/tests/iot/modules/test_homekit.py index 29436785f..c542df4e0 100644 --- a/tests/iot/modules/test_homekit.py +++ b/tests/iot/modules/test_homekit.py @@ -10,7 +10,7 @@ @device_iot -def test_homekit_getters(dev: IotDevice): +def test_homekit_getters(dev: IotDevice) -> None: # HomeKit can be present on any IOT device if Module.IotHomeKit not in dev.modules: pytest.skip("HomeKit module not present on this device") @@ -31,7 +31,7 @@ def test_homekit_getters(dev: IotDevice): @device_iot -def test_homekit_feature(dev: IotDevice): +def test_homekit_feature(dev: IotDevice) -> None: if Module.IotHomeKit not in dev.modules: pytest.skip("HomeKit module not present on this device") homekit: HomeKit = dev.modules[Module.IotHomeKit] @@ -45,7 +45,7 @@ def test_homekit_feature(dev: IotDevice): @device_iot -def test_initialize_features_skips_when_no_data(dev: IotDevice): +def test_initialize_features_skips_when_no_data(dev: IotDevice) -> None: if Module.IotHomeKit not in dev.modules: pytest.skip("HomeKit module not present on this device") homekit: HomeKit = dev.modules[Module.IotHomeKit] diff --git a/tests/iot/modules/test_motion.py b/tests/iot/modules/test_motion.py index 2d1ccbcc7..e0ed8c2e1 100644 --- a/tests/iot/modules/test_motion.py +++ b/tests/iot/modules/test_motion.py @@ -9,7 +9,7 @@ @dimmer_iot -def test_motion_getters(dev: IotDimmer): +def test_motion_getters(dev: IotDimmer) -> None: assert Module.IotMotion in dev.modules motion: Motion = dev.modules[Module.IotMotion] @@ -19,7 +19,7 @@ def test_motion_getters(dev: IotDimmer): @dimmer_iot -async def test_motion_setters(dev: IotDimmer, mocker: MockerFixture): +async def test_motion_setters(dev: IotDimmer, mocker: MockerFixture) -> None: motion: Motion = dev.modules[Module.IotMotion] query_helper = mocker.patch("kasa.iot.IotDimmer._query_helper") @@ -33,7 +33,7 @@ async def test_motion_setters(dev: IotDimmer, mocker: MockerFixture): @dimmer_iot -async def test_motion_range(dev: IotDimmer, mocker: MockerFixture): +async def test_motion_range(dev: IotDimmer, mocker: MockerFixture) -> None: motion: Motion = dev.modules[Module.IotMotion] query_helper = mocker.patch("kasa.iot.IotDimmer._query_helper") @@ -47,7 +47,7 @@ async def test_motion_range(dev: IotDimmer, mocker: MockerFixture): @dimmer_iot -async def test_motion_range_from_string(dev: IotDimmer, mocker: MockerFixture): +async def test_motion_range_from_string(dev: IotDimmer, mocker: MockerFixture) -> None: motion: Motion = dev.modules[Module.IotMotion] query_helper = mocker.patch("kasa.iot.IotDimmer._query_helper") @@ -74,7 +74,7 @@ async def test_motion_range_from_string(dev: IotDimmer, mocker: MockerFixture): @dimmer_iot -async def test_motion_threshold(dev: IotDimmer, mocker: MockerFixture): +async def test_motion_threshold(dev: IotDimmer, mocker: MockerFixture) -> None: motion: Motion = dev.modules[Module.IotMotion] query_helper = mocker.patch("kasa.iot.IotDimmer._query_helper") @@ -97,7 +97,7 @@ async def test_motion_threshold(dev: IotDimmer, mocker: MockerFixture): @dimmer_iot -async def test_motion_realtime(dev: IotDimmer, mocker: MockerFixture): +async def test_motion_realtime(dev: IotDimmer, mocker: MockerFixture) -> None: motion: Motion = dev.modules[Module.IotMotion] query_helper = mocker.patch("kasa.iot.IotDimmer._query_helper") @@ -106,7 +106,7 @@ async def test_motion_realtime(dev: IotDimmer, mocker: MockerFixture): @dimmer_iot -def test_motion_feature(dev: IotDimmer): +def test_motion_feature(dev: IotDimmer) -> None: assert Module.IotMotion in dev.modules motion: Motion = dev.modules[Module.IotMotion] diff --git a/tests/iot/modules/test_schedule.py b/tests/iot/modules/test_schedule.py index 4a4ffdee6..c65cb36f3 100644 --- a/tests/iot/modules/test_schedule.py +++ b/tests/iot/modules/test_schedule.py @@ -8,7 +8,7 @@ @device_iot @pytest.mark.xdist_group(name="caplog") -def test_schedule(dev: Device, caplog: pytest.LogCaptureFixture): +def test_schedule(dev: Device, caplog: pytest.LogCaptureFixture) -> None: schedule = dev.modules.get(Module.IotSchedule) assert schedule if rules := schedule.rules: diff --git a/tests/iot/modules/test_usage.py b/tests/iot/modules/test_usage.py index 7b2c0eed6..e180b01b7 100644 --- a/tests/iot/modules/test_usage.py +++ b/tests/iot/modules/test_usage.py @@ -4,10 +4,10 @@ from kasa.iot.modules import Usage -def test_usage_convert_stat_data(): - usage = Usage(None, module="usage") +def test_usage_convert_stat_data() -> None: + usage = Usage(Mock(), module="usage") - test_data = [] + test_data: list[dict[str, int]] = [] assert usage._convert_stat_data(test_data, "day") == {} test_data = [ @@ -24,13 +24,13 @@ def test_usage_convert_stat_data(): assert v == 30 -def test_usage_today(): +def test_usage_today() -> None: """Test fetching the usage for today. This test uses inline data since the fixtures will not have data for the current day. """ - emeter_data = { + emeter_data: dict = { "get_daystat": { "day_list": [], "err_code": 0, @@ -55,13 +55,13 @@ def data(self): assert usage.usage_today == 500 -def test_usage_this_month(): +def test_usage_this_month() -> None: """Test fetching the usage for this month. This test uses inline data since the fixtures will not have data for the current month. """ - emeter_data = { + emeter_data: dict = { "get_monthstat": { "month_list": [], "err_code": 0, diff --git a/tests/iot/test_iotbulb.py b/tests/iot/test_iotbulb.py index 4d40dff67..870efe20f 100644 --- a/tests/iot/test_iotbulb.py +++ b/tests/iot/test_iotbulb.py @@ -3,6 +3,7 @@ import re import pytest +from pytest_mock import MockerFixture from voluptuous import ( All, Boolean, @@ -27,7 +28,7 @@ @bulb_iot -async def test_bulb_sysinfo(dev: Device): +async def test_bulb_sysinfo(dev: Device) -> None: assert dev.sys_info is not None SYSINFO_SCHEMA_BULB(dev.sys_info) @@ -35,19 +36,21 @@ async def test_bulb_sysinfo(dev: Device): @bulb_iot -async def test_light_state_without_update(dev: IotBulb, monkeypatch): +async def test_light_state_without_update( + dev: IotBulb, monkeypatch: pytest.MonkeyPatch +) -> None: monkeypatch.setitem(dev._last_update["system"]["get_sysinfo"], "light_state", None) with pytest.raises(KasaException): print(dev.light_state) @bulb_iot -async def test_get_light_state(dev: IotBulb): +async def test_get_light_state(dev: IotBulb) -> None: LIGHT_STATE_SCHEMA(await dev.get_light_state()) @color_bulb_iot -async def test_set_hsv_transition(dev: IotBulb, mocker): +async def test_set_hsv_transition(dev: IotBulb, mocker: MockerFixture) -> None: set_light_state = mocker.patch("kasa.iot.IotBulb._set_light_state") light = dev.modules.get(Module.Light) assert light @@ -60,7 +63,7 @@ async def test_set_hsv_transition(dev: IotBulb, mocker): @bulb_iot -async def test_light_set_state(dev: IotBulb, mocker): +async def test_light_set_state(dev: IotBulb, mocker: MockerFixture) -> None: """Testing setting LightState on the light module.""" light = dev.modules.get(Module.Light) assert light @@ -76,7 +79,7 @@ async def test_light_set_state(dev: IotBulb, mocker): @variable_temp_iot -async def test_set_color_temp_transition(dev: IotBulb, mocker): +async def test_set_color_temp_transition(dev: IotBulb, mocker: MockerFixture) -> None: set_light_state = mocker.patch("kasa.iot.IotBulb._set_light_state") light = dev.modules.get(Module.Light) assert light @@ -87,7 +90,9 @@ async def test_set_color_temp_transition(dev: IotBulb, mocker): @variable_temp_iot @pytest.mark.xdist_group(name="caplog") -async def test_unknown_temp_range(dev: IotBulb, monkeypatch, caplog): +async def test_unknown_temp_range( + dev: IotBulb, monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture +) -> None: monkeypatch.setitem(dev._sys_info, "model", "unknown bulb") light = dev.modules.get(Module.Light) assert light @@ -99,7 +104,7 @@ async def test_unknown_temp_range(dev: IotBulb, monkeypatch, caplog): @dimmable_iot @turn_on -async def test_dimmable_brightness(dev: IotBulb, turn_on): +async def test_dimmable_brightness(dev: IotBulb, turn_on: bool) -> None: assert isinstance(dev, IotBulb | IotDimmer) light = dev.modules.get(Module.Light) assert light @@ -119,7 +124,7 @@ async def test_dimmable_brightness(dev: IotBulb, turn_on): @bulb_iot -async def test_turn_on_transition(dev: IotBulb, mocker): +async def test_turn_on_transition(dev: IotBulb, mocker: MockerFixture) -> None: set_light_state = mocker.patch("kasa.iot.IotBulb._set_light_state") await dev.turn_on(transition=1000) @@ -131,7 +136,9 @@ async def test_turn_on_transition(dev: IotBulb, mocker): @bulb_iot -async def test_dimmable_brightness_transition(dev: IotBulb, mocker): +async def test_dimmable_brightness_transition( + dev: IotBulb, mocker: MockerFixture +) -> None: set_light_state = mocker.patch("kasa.iot.IotBulb._set_light_state") light = dev.modules.get(Module.Light) assert light @@ -141,7 +148,7 @@ async def test_dimmable_brightness_transition(dev: IotBulb, mocker): @dimmable_iot -async def test_invalid_brightness(dev: IotBulb): +async def test_invalid_brightness(dev: IotBulb) -> None: assert dev._is_dimmable light = dev.modules.get(Module.Light) assert light @@ -159,7 +166,7 @@ async def test_invalid_brightness(dev: IotBulb): @non_dimmable_iot -async def test_non_dimmable(dev: IotBulb): +async def test_non_dimmable(dev: IotBulb) -> None: assert not dev._is_dimmable light = dev.modules.get(Module.Light) assert light @@ -171,8 +178,8 @@ async def test_non_dimmable(dev: IotBulb): @bulb_iot async def test_ignore_default_not_set_without_color_mode_change_turn_on( - dev: IotBulb, mocker -): + dev: IotBulb, mocker: MockerFixture +) -> None: query_helper = mocker.patch("kasa.iot.IotBulb._query_helper") # When turning back without settings, ignore default to restore the state await dev.turn_on() @@ -185,7 +192,7 @@ async def test_ignore_default_not_set_without_color_mode_change_turn_on( @bulb_iot -async def test_list_presets(dev: IotBulb): +async def test_list_presets(dev: IotBulb) -> None: light_preset = dev.modules.get(Module.LightPreset) assert light_preset assert isinstance(light_preset, IotLightPresetModule) @@ -206,7 +213,7 @@ async def test_list_presets(dev: IotBulb): @bulb_iot -async def test_modify_preset(dev: IotBulb, mocker): +async def test_modify_preset(dev: IotBulb, mocker: MockerFixture) -> None: """Verify that modifying preset calls the and exceptions are raised properly.""" if ( not (light_preset := dev.modules.get(Module.LightPreset)) @@ -254,7 +261,9 @@ async def test_modify_preset(dev: IotBulb, mocker): ), ], ) -async def test_modify_preset_payloads(dev: IotBulb, preset, payload, mocker): +async def test_modify_preset_payloads( + dev: IotBulb, preset, payload, mocker: MockerFixture +) -> None: """Test that modify preset payloads ignore none values.""" if ( not (light_preset := dev.modules.get(Module.LightPreset)) @@ -318,6 +327,6 @@ async def test_modify_preset_payloads(dev: IotBulb, preset, payload, mocker): @bulb_iot -async def test_turn_on_behaviours(dev: IotBulb): +async def test_turn_on_behaviours(dev: IotBulb) -> None: behavior = await dev.get_turn_on_behavior() assert behavior diff --git a/tests/iot/test_iotdevice.py b/tests/iot/test_iotdevice.py index 16dac35ff..19625a641 100644 --- a/tests/iot/test_iotdevice.py +++ b/tests/iot/test_iotdevice.py @@ -4,6 +4,7 @@ from datetime import datetime import pytest +from pytest_mock import MockerFixture from voluptuous import ( REMOVE_EXTRA, All, @@ -84,20 +85,20 @@ def check_mac(x): @device_iot -async def test_state_info(dev): +async def test_state_info(dev: IotDevice) -> None: assert isinstance(dev.state_information, dict) @pytest.mark.requires_dummy @device_iot -async def test_invalid_connection(mocker, dev): +async def test_invalid_connection(mocker: MockerFixture, dev: IotDevice) -> None: mocker.patch.object(FakeIotProtocol, "query", side_effect=KasaException) with pytest.raises(KasaException): await dev.update() @has_emeter_iot -async def test_initial_update_emeter(dev, mocker): +async def test_initial_update_emeter(dev: IotDevice, mocker: MockerFixture) -> None: """Test that the initial update performs second query if emeter is available.""" dev._last_update = {} dev._legacy_features = set() @@ -109,7 +110,7 @@ async def test_initial_update_emeter(dev, mocker): @no_emeter_iot -async def test_initial_update_no_emeter(dev, mocker): +async def test_initial_update_no_emeter(dev: IotDevice, mocker: MockerFixture) -> None: """Test that the initial update performs second query if emeter is available.""" dev._last_update = {} dev._legacy_features = set() @@ -128,7 +129,7 @@ async def test_initial_update_no_emeter(dev, mocker): @device_iot -async def test_query_helper(dev): +async def test_query_helper(dev: IotDevice) -> None: with pytest.raises(KasaException): await dev._query_helper("test", "testcmd", {}) # TODO check for unwrapping? @@ -136,7 +137,7 @@ async def test_query_helper(dev): @device_iot @turn_on -async def test_state(dev, turn_on): +async def test_state(dev: IotDevice, turn_on: bool) -> None: orig_state = dev.is_on await handle_turn_on(dev, turn_on) await dev.update() @@ -164,7 +165,7 @@ async def test_state(dev, turn_on): @device_iot @turn_on -async def test_on_since(dev, turn_on): +async def test_on_since(dev: IotDevice, turn_on: bool) -> None: await handle_turn_on(dev, turn_on) orig_state = dev.is_on if "on_time" not in dev.sys_info and dev.device_type is not DeviceType.Strip: @@ -176,43 +177,43 @@ async def test_on_since(dev, turn_on): @device_iot -async def test_time(dev): +async def test_time(dev: IotDevice) -> None: assert isinstance(dev.modules[Module.Time].time, datetime) @device_iot -async def test_timezone(dev): +async def test_timezone(dev: IotDevice) -> None: TZ_SCHEMA(await dev.modules[Module.Time].get_timezone()) @device_iot -async def test_hw_info(dev): +async def test_hw_info(dev: IotDevice) -> None: SYSINFO_SCHEMA(dev.hw_info) @device_iot -async def test_location(dev): +async def test_location(dev: IotDevice) -> None: SYSINFO_SCHEMA(dev.location) @device_iot -async def test_rssi(dev): +async def test_rssi(dev: IotDevice) -> None: SYSINFO_SCHEMA({"rssi": dev.rssi}) # wrapping for vol @device_iot -async def test_mac(dev): +async def test_mac(dev: IotDevice) -> None: SYSINFO_SCHEMA({"mac": dev.mac}) # wrapping for val @device_iot -async def test_representation(dev): +async def test_representation(dev: IotDevice) -> None: pattern = re.compile(r"") assert pattern.match(str(dev)) @device_iot -async def test_children(dev): +async def test_children(dev: IotDevice) -> None: """Make sure that children property is exposed by every device.""" if dev.device_type is DeviceType.Strip: assert len(dev.children) > 0 @@ -221,7 +222,7 @@ async def test_children(dev): @device_iot -async def test_modules_preserved(dev: IotDevice): +async def test_modules_preserved(dev: IotDevice) -> None: """Make modules that are not being updated are preserved between updates.""" dev._last_update["some_module_not_being_updated"] = "should_be_kept" await dev.update() @@ -229,13 +230,13 @@ async def test_modules_preserved(dev: IotDevice): @device_iot -async def test_internal_state(dev): +async def test_internal_state(dev: IotDevice) -> None: """Make sure the internal state returns the last update results.""" assert dev.internal_state == dev._last_update @device_iot -async def test_features(dev): +async def test_features(dev: IotDevice) -> None: """Make sure features is always accessible.""" sysinfo = dev._last_update["system"]["get_sysinfo"] if "feature" in sysinfo: @@ -245,20 +246,20 @@ async def test_features(dev): @device_iot -async def test_max_device_response_size(dev): +async def test_max_device_response_size(dev: IotDevice) -> None: """Make sure every device return has a set max response size.""" assert dev.max_device_response_size > 0 @device_iot -async def test_estimated_response_sizes(dev): +async def test_estimated_response_sizes(dev: IotDevice) -> None: """Make sure every module has an estimated response size set.""" for mod in dev.modules.values(): assert mod.estimated_query_response_size > 0 @device_iot -async def test_modules_not_supported(dev: IotDevice): +async def test_modules_not_supported(dev: IotDevice) -> None: """Test that unsupported modules do not break the device.""" for module in dev.modules.values(): assert module.is_supported is not None @@ -267,7 +268,7 @@ async def test_modules_not_supported(dev: IotDevice): assert module.is_supported is not None -async def test_get_modules(): +async def test_get_modules() -> None: """Test getting modules for child and parent modules.""" dummy_device = await get_device_for_fixture_protocol( "HS100(US)_2.0_1.5.6.json", "IOT" @@ -293,10 +294,10 @@ async def test_get_modules(): assert module is None -def test_merge_dict(): +def test_merge_dict() -> None: """Test the recursive dict merge.""" - dest = {"a": 1, "b": {"c": 2, "d": 3}} - source = {"b": {"c": 4, "e": 5}} + dest: dict = {"a": 1, "b": {"c": 2, "d": 3}} + source: dict = {"b": {"c": 4, "e": 5}} assert _merge_dict(dest, source) == {"a": 1, "b": {"c": 4, "d": 3, "e": 5}} dest = {"smartlife.iot.common.emeter": {"get_realtime": None}} diff --git a/tests/iot/test_iotdimmer.py b/tests/iot/test_iotdimmer.py index 38f440e70..561796d4a 100644 --- a/tests/iot/test_iotdimmer.py +++ b/tests/iot/test_iotdimmer.py @@ -1,4 +1,5 @@ import pytest +from pytest_mock import MockerFixture from kasa import DeviceType, Module from kasa.iot import IotDimmer @@ -6,7 +7,7 @@ @dimmer_iot -async def test_set_brightness(dev): +async def test_set_brightness(dev: IotDimmer) -> None: light = dev.modules.get(Module.Light) assert light await handle_turn_on(dev, False) @@ -26,7 +27,9 @@ async def test_set_brightness(dev): @dimmer_iot @turn_on -async def test_set_brightness_transition(dev, turn_on, mocker): +async def test_set_brightness_transition( + dev: IotDimmer, turn_on: bool, mocker: MockerFixture +) -> None: light = dev.modules.get(Module.Light) assert light await handle_turn_on(dev, turn_on) @@ -49,7 +52,7 @@ async def test_set_brightness_transition(dev, turn_on, mocker): @dimmer_iot -async def test_set_brightness_invalid(dev): +async def test_set_brightness_invalid(dev: IotDimmer) -> None: light = dev.modules.get(Module.Light) assert light for invalid_brightness in [-1, 101]: @@ -62,7 +65,7 @@ async def test_set_brightness_invalid(dev): @dimmer_iot -async def test_set_brightness_invalid_transition(dev): +async def test_set_brightness_invalid_transition(dev: IotDimmer) -> None: light = dev.modules.get(Module.Light) assert light for invalid_transition in [-1]: @@ -74,7 +77,7 @@ async def test_set_brightness_invalid_transition(dev): @dimmer_iot -async def test_turn_on_transition(dev, mocker): +async def test_turn_on_transition(dev: IotDimmer, mocker: MockerFixture) -> None: light = dev.modules.get(Module.Light) assert light query_helper = mocker.spy(IotDimmer, "_query_helper") @@ -93,7 +96,7 @@ async def test_turn_on_transition(dev, mocker): @dimmer_iot -async def test_turn_off_transition(dev, mocker): +async def test_turn_off_transition(dev: IotDimmer, mocker: MockerFixture) -> None: light = dev.modules.get(Module.Light) assert light await handle_turn_on(dev, True) @@ -115,7 +118,9 @@ async def test_turn_off_transition(dev, mocker): @dimmer_iot @turn_on -async def test_set_dimmer_transition(dev, turn_on, mocker): +async def test_set_dimmer_transition( + dev: IotDimmer, turn_on: bool, mocker: MockerFixture +) -> None: light = dev.modules.get(Module.Light) assert light await handle_turn_on(dev, turn_on) @@ -135,7 +140,9 @@ async def test_set_dimmer_transition(dev, turn_on, mocker): @dimmer_iot @turn_on -async def test_set_dimmer_transition_to_off(dev, turn_on, mocker): +async def test_set_dimmer_transition_to_off( + dev: IotDimmer, turn_on: bool, mocker: MockerFixture +) -> None: light = dev.modules.get(Module.Light) assert light await handle_turn_on(dev, turn_on) @@ -156,26 +163,26 @@ async def test_set_dimmer_transition_to_off(dev, turn_on, mocker): @dimmer_iot -async def test_set_dimmer_transition_invalid_brightness(dev): +async def test_set_dimmer_transition_invalid_brightness(dev: IotDimmer) -> None: for invalid_brightness in [-1, 101]: with pytest.raises(ValueError, match="Invalid brightness value: "): await dev.set_dimmer_transition(invalid_brightness, 1000) for invalid_type in [0.5, "foo"]: with pytest.raises(TypeError, match="Transition must be integer"): - await dev.set_dimmer_transition(1, invalid_type) + await dev.set_dimmer_transition(1, invalid_type) # type: ignore[arg-type] @dimmer_iot -async def test_set_dimmer_transition_invalid_transition(dev): +async def test_set_dimmer_transition_invalid_transition(dev: IotDimmer) -> None: for invalid_transition in [-1]: with pytest.raises(ValueError, match="Transition value .+? is not valid."): await dev.set_dimmer_transition(1, transition=invalid_transition) for invalid_type in [0.5, "foo"]: with pytest.raises(TypeError, match="Transition must be integer"): - await dev.set_dimmer_transition(1, transition=invalid_type) + await dev.set_dimmer_transition(1, transition=invalid_type) # type: ignore[arg-type] @dimmer_iot -def test_device_type_dimmer(dev): +def test_device_type_dimmer(dev: IotDimmer) -> None: assert dev.device_type == DeviceType.Dimmer diff --git a/tests/iot/test_iotlightstrip.py b/tests/iot/test_iotlightstrip.py index 23eb61dc9..c78780f82 100644 --- a/tests/iot/test_iotlightstrip.py +++ b/tests/iot/test_iotlightstrip.py @@ -1,4 +1,5 @@ import pytest +from pytest_mock import MockerFixture from kasa import DeviceType, Module from kasa.iot import IotLightStrip @@ -7,13 +8,13 @@ @lightstrip_iot -async def test_lightstrip_length(dev: IotLightStrip): +async def test_lightstrip_length(dev: IotLightStrip) -> None: assert dev.device_type == DeviceType.LightStrip assert dev.length == dev.sys_info["length"] @lightstrip_iot -async def test_lightstrip_effect(dev: IotLightStrip): +async def test_lightstrip_effect(dev: IotLightStrip) -> None: le: LightEffect = dev.modules[Module.LightEffect] assert isinstance(le._deprecated_effect, dict) for k in ["brightness", "custom", "enable", "id", "name"]: @@ -21,7 +22,7 @@ async def test_lightstrip_effect(dev: IotLightStrip): @lightstrip_iot -async def test_effects_lightstrip_set_effect(dev: IotLightStrip): +async def test_effects_lightstrip_set_effect(dev: IotLightStrip) -> None: le: LightEffect = dev.modules[Module.LightEffect] with pytest.raises( ValueError, match="The effect Not real is not a built in effect" @@ -36,8 +37,8 @@ async def test_effects_lightstrip_set_effect(dev: IotLightStrip): @lightstrip_iot @pytest.mark.parametrize("brightness", [100, 50]) async def test_effects_lightstrip_set_effect_brightness( - dev: IotLightStrip, brightness, mocker -): + dev: IotLightStrip, brightness: int, mocker: MockerFixture +) -> None: query_helper = mocker.patch("kasa.iot.IotLightStrip._query_helper") le: LightEffect = dev.modules[Module.LightEffect] @@ -55,8 +56,8 @@ async def test_effects_lightstrip_set_effect_brightness( @lightstrip_iot @pytest.mark.parametrize("transition", [500, 1000]) async def test_effects_lightstrip_set_effect_transition( - dev: IotLightStrip, transition, mocker -): + dev: IotLightStrip, transition: int, mocker: MockerFixture +) -> None: query_helper = mocker.patch("kasa.iot.IotLightStrip._query_helper") le: LightEffect = dev.modules[Module.LightEffect] @@ -72,12 +73,12 @@ async def test_effects_lightstrip_set_effect_transition( @lightstrip_iot -async def test_effects_lightstrip_has_effects(dev: IotLightStrip): +async def test_effects_lightstrip_has_effects(dev: IotLightStrip) -> None: le: LightEffect = dev.modules[Module.LightEffect] assert le is not None assert le.effect_list @lightstrip_iot -def test_device_type_lightstrip(dev): +def test_device_type_lightstrip(dev: IotLightStrip) -> None: assert dev.device_type == DeviceType.LightStrip diff --git a/tests/iot/test_iotstrip.py b/tests/iot/test_iotstrip.py index 50c1df618..1ef32c29a 100644 --- a/tests/iot/test_iotstrip.py +++ b/tests/iot/test_iotstrip.py @@ -1,11 +1,15 @@ from unittest.mock import AsyncMock +from pytest_mock import MockerFixture + from kasa import Module +from kasa.iot import IotStrip +from kasa.iot.iotstrip import IotStripPlug from tests.conftest import strip_emeter_iot, strip_iot @strip_iot -async def test_strip_update_and_child_update_behaviors(dev): +async def test_strip_update_and_child_update_behaviors(dev: IotStrip) -> None: await dev.update() await dev.update(update_children=False) @@ -18,9 +22,10 @@ async def test_strip_update_and_child_update_behaviors(dev): @strip_iot -async def test_strip_child_delegated_properties(dev): +async def test_strip_child_delegated_properties(dev: IotStrip) -> None: await dev.update() child = dev.children[0] + assert isinstance(child, IotStripPlug) assert child.led is False assert child.time == dev.time @@ -32,7 +37,7 @@ async def test_strip_child_delegated_properties(dev): @strip_emeter_iot -async def test_strip_emeter_erase_stats(dev, mocker): +async def test_strip_emeter_erase_stats(dev: IotStrip, mocker: MockerFixture) -> None: await dev.update() for child in dev.children: diff --git a/tests/iot/test_iottimezone.py b/tests/iot/test_iottimezone.py index 640603b16..ae2f24f0d 100644 --- a/tests/iot/test_iottimezone.py +++ b/tests/iot/test_iottimezone.py @@ -5,7 +5,7 @@ from pytest_mock import MockerFixture -def test_expected_dst_behavior_for_index_cases(): +def test_expected_dst_behavior_for_index_cases() -> None: """Exercise _expected_dst_behavior_for_index for several representative indices.""" from kasa.iot.iottimezone import _expected_dst_behavior_for_index @@ -20,7 +20,7 @@ def test_expected_dst_behavior_for_index_cases(): _expected_dst_behavior_for_index(999) -async def test_guess_timezone_by_offset_fixed_fallback_unit(): +async def test_guess_timezone_by_offset_fixed_fallback_unit() -> None: """When no ZoneInfo matches, return a fixed-offset tzinfo.""" import kasa.iot.iottimezone as tzmod @@ -31,7 +31,7 @@ async def test_guess_timezone_by_offset_fixed_fallback_unit(): assert tz.utcoffset(when) == offset -async def test_guess_timezone_by_offset_candidates_unit(): +async def test_guess_timezone_by_offset_candidates_unit() -> None: """Cover naive when_utc branch and candidate selection path (non-empty candidates).""" import kasa.iot.iottimezone as tzmod @@ -47,7 +47,7 @@ async def test_guess_timezone_by_offset_candidates_unit(): async def test_guess_timezone_by_offset_dst_expected_true_filters( mocker: MockerFixture, -): +) -> None: """dst_expected=True should prefer a DST-observing zone when possible.""" import kasa.iot.iottimezone as tzmod @@ -62,7 +62,7 @@ async def test_guess_timezone_by_offset_dst_expected_true_filters( assert jan != jul # observes DST -async def test_guess_timezone_by_offset_dst_expected_false_prefers_non_dst(): +async def test_guess_timezone_by_offset_dst_expected_false_prefers_non_dst() -> None: """dst_expected=False should prefer a non-DST zone and skip DST candidates (covers False branch).""" import kasa.iot.iottimezone as tzmod @@ -79,7 +79,7 @@ async def test_guess_timezone_by_offset_dst_expected_false_prefers_non_dst(): async def test_guess_timezone_by_offset_handles_missing_zoneinfo_unit( mocker: MockerFixture, -): +) -> None: """Cover the ZoneInfoNotFoundError continue path within guess_timezone_by_offset.""" from zoneinfo import ZoneInfoNotFoundError as ZNF @@ -101,7 +101,7 @@ async def flaky_get(name: str): assert tz.utcoffset(when) == timedelta(0) -async def test_get_timezone_index_direct_match(): +async def test_get_timezone_index_direct_match() -> None: """If ZoneInfo key is in TIMEZONE_INDEX, return index directly.""" import kasa.iot.iottimezone as tzmod @@ -109,7 +109,7 @@ async def test_get_timezone_index_direct_match(): assert idx == 39 # "GB" is mapped to index 39 -async def test_get_timezone_index_non_zoneinfo_unit(): +async def test_get_timezone_index_non_zoneinfo_unit() -> None: """Exercise get_timezone_index path when input tzinfo is not a ZoneInfo instance.""" import kasa.iot.iottimezone as tzmod @@ -119,7 +119,7 @@ async def test_get_timezone_index_non_zoneinfo_unit(): assert 0 <= idx <= 109 -async def test_get_timezone_index_skips_missing_unit(mocker: MockerFixture): +async def test_get_timezone_index_skips_missing_unit(mocker: MockerFixture) -> None: """Cover ZoneInfoNotFoundError path in get_timezone_index loop and successful match.""" from zoneinfo import ZoneInfoNotFoundError as ZNF @@ -141,7 +141,7 @@ async def side_effect(i: int): assert idx >= 5 -async def test_get_timezone_index_raises_for_unmatched_unit(): +async def test_get_timezone_index_raises_for_unmatched_unit() -> None: """Ensure get_timezone_index completes loop and raises when no match exists (covers raise branch).""" import kasa.iot.iottimezone as tzmod @@ -150,7 +150,7 @@ async def test_get_timezone_index_raises_for_unmatched_unit(): await tzmod.get_timezone_index(timezone(timedelta(minutes=2))) -async def test_get_matching_timezones_branches_unit(mocker: MockerFixture): +async def test_get_matching_timezones_branches_unit(mocker: MockerFixture) -> None: """Cover initial append, except path, and duplicate suppression in get_matching_timezones.""" from zoneinfo import ZoneInfoNotFoundError as ZNF @@ -172,7 +172,7 @@ async def side_effect(i: int): # Loop should find GB again but not duplicate it -async def test_get_matching_timezones_non_zoneinfo_unit(): +async def test_get_matching_timezones_non_zoneinfo_unit() -> None: """Exercise get_matching_timezones when input tzinfo is not a ZoneInfo (skips initial append).""" import kasa.iot.iottimezone as tzmod @@ -181,7 +181,7 @@ async def test_get_matching_timezones_non_zoneinfo_unit(): assert len(matches) > 0 -async def test_get_timezone_out_of_range_defaults_to_utc(): +async def test_get_timezone_out_of_range_defaults_to_utc() -> None: """Out-of-range index should log and default to UTC.""" import kasa.iot.iottimezone as tzmod diff --git a/tests/iot/test_wallswitch.py b/tests/iot/test_wallswitch.py index b6fd2a673..3e2eac428 100644 --- a/tests/iot/test_wallswitch.py +++ b/tests/iot/test_wallswitch.py @@ -1,8 +1,10 @@ +from kasa.iot import IotWallSwitch + from ..device_fixtures import wallswitch_iot @wallswitch_iot -def test_wallswitch_motion(dev): +def test_wallswitch_motion(dev: IotWallSwitch) -> None: """Check that wallswitches with motion sensor get modules enabled.""" has_motion = "PIR" in dev.sys_info["dev_name"] assert "motion" in dev.modules if has_motion else True