"""Unit tests for models and helpers.""" from __future__ import annotations from datetime import UTC, datetime from shelly_manager.core.discovery import classify_generation, normalize_mac from shelly_manager.core.models import ( DeviceFilter, ShellyDevice, infer_capabilities, preserve_firmware_check_metadata, ) def test_classify_generation_gen1() -> None: assert classify_generation({"type": "SHSW-21", "mac": "A"}) == 1 def test_classify_generation_gen2() -> None: assert classify_generation({"gen": 2, "model": "x"}) == 2 assert classify_generation({"gen": 3, "model": "x"}) == 3 def test_normalize_mac() -> None: assert normalize_mac("aa:bb:cc:dd:ee:ff") == "AABBCCDDEEFF" def test_device_filter_matches() -> None: d = ShellyDevice( id="AABB", name="Kitchen", mac="AA:BB", ip="192.168.1.10", generation=2, model="Plus1", firmware="1.0", online=True, last_seen=datetime.now(UTC), capabilities=["switch"], status={}, settings={}, tags=["t1"], ) assert DeviceFilter(generations=[2]).matches(d) assert not DeviceFilter(generations=[1]).matches(d) assert DeviceFilter(tags=["t1"]).matches(d) assert not DeviceFilter(tags=["missing"]).matches(d) assert DeviceFilter(name_contains="kit").matches(d) assert DeviceFilter(ip_prefix="192.168.1").matches(d) assert DeviceFilter(only_device_ids=["AABB"]).matches(d) assert not DeviceFilter(only_device_ids=["OTHER"]).matches(d) def test_device_filter_settings_truthy() -> None: d = ShellyDevice( id="AABB", name="Kitchen", mac="AA:BB", ip="192.168.1.10", generation=2, model="Plus1", firmware="1.0", online=True, last_seen=datetime.now(UTC), capabilities=[], status={}, settings={"ble": {"enable": True}}, tags=[], ) f = DeviceFilter(settings_path="ble.enable", settings_match="truthy") assert f.matches(d) d2 = d.model_copy(update={"settings": {"ble": {"enable": False}}}) assert not f.matches(d2) def test_shelly_device_http_url() -> None: d = ShellyDevice( id="AABB", name="Kitchen", mac="AA:BB", ip="192.168.1.10", generation=2, model="Plus1", firmware="1.0", online=True, last_seen=datetime.now(UTC), capabilities=[], status={}, settings={}, tags=[], http_port=8080, ) assert d.http_url == "http://192.168.1.10:8080/" def test_device_filter_capabilities_and_status() -> None: d = ShellyDevice( id="AABB", name="Kitchen", mac="AA:BB", ip="192.168.1.10", generation=2, model="Plus1", firmware="1.0", online=True, last_seen=datetime.now(UTC), capabilities=["switch"], status={"switch:0": {"output": True}}, settings={}, tags=[], ) assert DeviceFilter(capabilities_any=["switch"]).matches(d) assert not DeviceFilter(capabilities_any=["relay"]).matches(d) assert DeviceFilter(status_path="switch:0|output", status_match="truthy").matches(d) def test_device_filter_settings_falsy() -> None: d = ShellyDevice( id="AABB", name="Kitchen", mac="AA:BB", ip="192.168.1.10", generation=2, model="Plus1", firmware="1.0", online=True, last_seen=datetime.now(UTC), capabilities=[], status={}, settings={}, tags=[], ) f = DeviceFilter(settings_path="ble.enable", settings_match="falsy") assert f.matches(d) def test_infer_capabilities_gen1_relay() -> None: caps = infer_capabilities(1, {"relays": [{"ison": True}]}, {}) assert "relay" in caps def test_infer_capabilities_gen2_switch() -> None: caps = infer_capabilities(2, {"switch:0": {"output": True}}, {}) assert "switch" in caps def test_preserve_firmware_check_metadata() -> None: ts = datetime.now(UTC) prev = ShellyDevice( id="AABBCCDDEEFF", name="A", mac="AA:BB:CC:DD:EE:FF", ip="192.168.1.1", generation=2, # type: ignore[arg-type] model="Plus1", firmware="1", online=True, last_seen=ts, capabilities=[], status={}, settings={}, firmware_check_result={"stable": {"version": "1.7.4"}}, firmware_check_at=ts, ) fresh = ShellyDevice( id="AABBCCDDEEFF", name="A", mac="AA:BB:CC:DD:EE:FF", ip="192.168.1.1", generation=2, # type: ignore[arg-type] model="Plus1", firmware="1", online=True, last_seen=ts, capabilities=[], status={"sys": {}}, settings={}, firmware_check_result=None, firmware_check_at=None, ) preserve_firmware_check_metadata(fresh, prev) assert fresh.firmware_check_result == prev.firmware_check_result assert fresh.firmware_check_at == prev.firmware_check_at