Files
shelly-ui/tests/test_device_manager.py
jonas 71803418e5 Initial commit: Shelly Manager with Textual CLI, Streamlit UI, and comprehensive .gitignore
Shelly device management app with mDNS/subnet discovery, inventory,
configuration, and mass operations for Gen1/Gen2+ devices.

Includes .gitignore excluding runtime data (device DB, user config),
AI conversation history, build artifacts, and common Python/OS patterns.
2026-03-23 21:51:59 +01:00

148 lines
4.5 KiB
Python

"""DeviceManager with discovery mocked."""
from __future__ import annotations
from datetime import UTC, datetime
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from shelly_manager.core.device_manager import DeviceManager, shelly_from_probe
from shelly_manager.core.models import DiscoveredEndpoint, ShellyDevice
@pytest.mark.asyncio
async def test_shelly_from_probe_gen2() -> None:
d = shelly_from_probe(
"10.0.0.2",
2,
{"gen": 2, "mac": "AABBCCDDEEFF", "model": "Plus1", "fw_id": "x", "id": "shellyplus1-abc"},
)
assert d.generation == 2
assert d.ip == "10.0.0.2"
@pytest.mark.asyncio
async def test_discover_all_persists_after_mdns_probe_and_enrich(
app_config_sqlite,
) -> None:
dm = DeviceManager(app_config_sqlite)
enriched = ShellyDevice(
id="AABBCCDDEEFF",
name="enriched",
mac="AA:BB:CC:DD:EE:FF",
ip="10.0.0.1",
generation=2,
model="Plus1",
firmware="1",
online=True,
last_seen=datetime.now(UTC),
capabilities=["switch"],
status={"ok": True},
settings={"sys": {}},
)
ep = DiscoveredEndpoint(ip="10.0.0.1", port=80, source="mdns_http", txt={})
probe_payload = {
"gen": 2,
"mac": "AA:BB:CC:DD:EE:FF",
"model": "Plus1",
"fw_id": "1",
"id": "shellyplus1-abc",
}
fake_rt = MagicMock()
fake_rt.__aenter__ = AsyncMock(return_value=fake_rt)
fake_rt.__aexit__ = AsyncMock(return_value=None)
with (
patch("shelly_manager.core.device_manager.browse_mdns", new_callable=AsyncMock) as mdns,
patch("shelly_manager.core.device_manager.probe_ip", new_callable=AsyncMock) as probe,
patch(
"shelly_manager.core.device_manager.fetch_device_snapshot",
new_callable=AsyncMock,
) as enrich,
patch("shelly_manager.core.device_manager.ShellyRuntime", return_value=fake_rt),
):
mdns.return_value = [ep]
probe.return_value = (2, probe_payload)
enrich.return_value = enriched
sess = MagicMock()
await dm.discover_all(session=sess)
stored = await dm.storage.get_device("AABBCCDDEEFF")
assert stored is not None
assert stored.name == "enriched"
assert stored.status.get("ok") is True
@pytest.mark.asyncio
async def test_add_device_by_ip_invalid(app_config_sqlite) -> None:
dm = DeviceManager(app_config_sqlite)
dev, err = await dm.add_device_by_ip("not-an-ip")
assert dev is None
assert err is not None
assert "Invalid" in err
@pytest.mark.asyncio
async def test_add_device_by_ip_no_shelly_json(app_config_sqlite) -> None:
dm = DeviceManager(app_config_sqlite)
with patch("shelly_manager.core.device_manager.fetch_shelly_json", new_callable=AsyncMock) as fj:
fj.return_value = None
sess = MagicMock()
dev, err = await dm.add_device_by_ip("192.168.1.1", session=sess)
assert dev is None
assert err is not None
@pytest.mark.asyncio
async def test_add_device_by_ip_persists_after_enrich(app_config_sqlite) -> None:
dm = DeviceManager(app_config_sqlite)
enriched = ShellyDevice(
id="AABBCCDDEEFF",
name="enriched",
mac="AA:BB:CC:DD:EE:FF",
ip="10.0.0.5",
generation=2,
model="Plus1",
firmware="1",
online=True,
last_seen=datetime.now(UTC),
capabilities=["switch"],
status={"ok": True},
settings={"sys": {}},
)
probe_payload = {
"gen": 2,
"mac": "AA:BB:CC:DD:EE:FF",
"model": "Plus1",
"fw_id": "1",
"id": "shellyplus1-abc",
}
fake_rt = MagicMock()
fake_rt.__aenter__ = AsyncMock(return_value=fake_rt)
fake_rt.__aexit__ = AsyncMock(return_value=None)
with (
patch("shelly_manager.core.device_manager.fetch_shelly_json", new_callable=AsyncMock) as fj,
patch(
"shelly_manager.core.device_manager.fetch_device_snapshot",
new_callable=AsyncMock,
) as enrich,
patch("shelly_manager.core.device_manager.ShellyRuntime", return_value=fake_rt),
):
fj.return_value = probe_payload
enrich.return_value = enriched
sess = MagicMock()
dev, err = await dm.add_device_by_ip("10.0.0.5", session=sess)
assert err is None
assert dev is not None
assert dev.name == "enriched"
stored = await dm.storage.get_device("AABBCCDDEEFF")
assert stored is not None
assert stored.status.get("ok") is True