docs: add project description and server setup instructions to README
This commit is contained in:
108
server/tests/test_device_store.py
Normal file
108
server/tests/test_device_store.py
Normal file
@@ -0,0 +1,108 @@
|
||||
"""
|
||||
Tests for the in-memory DeviceStore.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
|
||||
import pytest
|
||||
|
||||
from app.device_store import DeviceStore, SensorReading
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_all_devices(store: DeviceStore):
|
||||
"""All registered devices are returned."""
|
||||
devices = await store.get_all_devices()
|
||||
assert len(devices) == 3
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_enabled_devices(store: DeviceStore):
|
||||
"""Only enabled devices are returned."""
|
||||
devices = await store.get_enabled_devices()
|
||||
assert len(devices) == 2
|
||||
ids = {d.config.id for d in devices}
|
||||
assert "test-disabled" not in ids
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_device_by_id(store: DeviceStore):
|
||||
"""Lookup by device ID."""
|
||||
device = await store.get_device("test-plant-01")
|
||||
assert device is not None
|
||||
assert device.config.name == "Test Plant"
|
||||
|
||||
missing = await store.get_device("nonexistent")
|
||||
assert missing is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_device_by_ip(store: DeviceStore):
|
||||
"""Lookup by IP address."""
|
||||
device = await store.get_device_by_ip("192.168.1.100")
|
||||
assert device is not None
|
||||
assert device.config.id == "test-plant-01"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_reading(store: DeviceStore):
|
||||
"""Recording a reading updates value and marks device online."""
|
||||
await store.update_reading("test-plant-01", "sensors/soil_moisture", 42.5, "percent")
|
||||
|
||||
device = await store.get_device("test-plant-01")
|
||||
assert device.online is True
|
||||
assert "sensors/soil_moisture" in device.readings
|
||||
assert device.readings["sensors/soil_moisture"].value == 42.5
|
||||
assert device.readings["sensors/soil_moisture"].unit == "percent"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_reading_unknown_device(store: DeviceStore):
|
||||
"""Readings for unknown devices are silently ignored."""
|
||||
await store.update_reading("unknown-device", "sensors/x", 99, "unit")
|
||||
device = await store.get_device("unknown-device")
|
||||
assert device is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mark_offline(store: DeviceStore):
|
||||
"""Marking offline sets flag and marks readings stale."""
|
||||
await store.update_reading("test-plant-01", "sensors/temperature", 24.0, "celsius")
|
||||
await store.mark_offline("test-plant-01")
|
||||
|
||||
device = await store.get_device("test-plant-01")
|
||||
assert device.online is False
|
||||
assert device.readings["sensors/temperature"].stale is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mark_online(store: DeviceStore):
|
||||
"""Marking online resets the flag."""
|
||||
await store.mark_offline("test-plant-01")
|
||||
await store.mark_online("test-plant-01")
|
||||
|
||||
device = await store.get_device("test-plant-01")
|
||||
assert device.online is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_snapshot(store: DeviceStore):
|
||||
"""Snapshot returns a serializable dict of all devices."""
|
||||
await store.update_reading("test-plant-01", "sensors/temperature", 23.5, "celsius")
|
||||
snap = await store.snapshot()
|
||||
|
||||
assert "test-plant-01" in snap
|
||||
assert snap["test-plant-01"]["readings"]["sensors/temperature"]["value"] == 23.5
|
||||
|
||||
|
||||
def test_sensor_reading_age():
|
||||
"""SensorReading.age_seconds returns meaningful values."""
|
||||
import time
|
||||
|
||||
reading = SensorReading(value=42, unit="percent", timestamp=time.time() - 10)
|
||||
assert 9.5 < reading.age_seconds() < 11.0
|
||||
|
||||
stale = SensorReading(value=0, timestamp=0)
|
||||
assert stale.age_seconds() == float("inf")
|
||||
Reference in New Issue
Block a user