from types import SimpleNamespace from app.monitoring.storage import DeviceStorageMonitor, directory_bytes def test_directory_bytes_deduplicates_nested_roots_and_counts_template_content(tmp_path): program = tmp_path / "program" data = program / "data" templates = data / "templates" animations = data / "animations" templates.mkdir(parents=True) animations.mkdir() (program / "app.py").write_bytes(b"app") (data / "config.json").write_bytes(b"config") (templates / "one.json").write_bytes(b"template") (animations / "frame.json").write_bytes(b"animation") assert directory_bytes([program, data]) == 3 + 6 + 8 + 9 assert directory_bytes([templates, animations]) == 8 + 9 def test_device_storage_monitor_caches_expires_and_invalidates(tmp_path): program = tmp_path / "program" data = tmp_path / "data" program.mkdir() data.mkdir() now = [10.0] calls = [] def directory_reader(paths): resolved = tuple(str(path) for path in paths) calls.append(resolved) return 100 if len(calls) % 2 else 25 monitor = DeviceStorageMonitor( program, data, cache_seconds=300, clock=lambda: now[0], usage_reader=lambda _path: SimpleNamespace(total=1000, used=400, free=600), directory_reader=directory_reader, ) first = monitor.get_status() second = monitor.get_status() assert first == second assert first["device_total_bytes"] == 1000 assert first["device_free_bytes"] == 600 assert first["software_bytes"] == 100 assert first["templates_bytes"] == 25 assert len(calls) == 2 now[0] += 299 assert monitor.get_status() == first assert len(calls) == 2 now[0] += 1 monitor.get_status() assert len(calls) == 4 monitor.invalidate() monitor.get_status() assert len(calls) == 6