import { afterEach, describe, expect, it, vi } from "vitest"; import type * as NetworkActivity from "@/lib/server/modules/system/network-activity"; const { countersMock } = vi.hoisted(() => ({ countersMock: vi.fn() })); vi.mock("node:os", () => ({ default: { loadavg: () => [0.6, 0.5, 0.4], cpus: () => [0, 1, 2].map(() => ({ times: { user: 100, nice: 0, sys: 50, idle: 850, irq: 0 } })), networkInterfaces: () => ({ wlan0: [ { address: "192.168.1.22", family: "IPv4", internal: false }, { address: "fe80::1234", family: "IPv6", internal: false }, ], }), totalmem: () => 1024, freemem: () => 256, hostname: () => "pi", platform: () => "linux", release: () => "6.6", uptime: () => 3600, arch: () => "x64", }, })); vi.mock("@/lib/server/modules/system/network-activity", async (importOriginal) => ({ ...(await importOriginal()), readDefaultRouteInterface: vi.fn(async () => "wlan0"), readInterfaceCounters: countersMock, })); vi.mock("systeminformation", () => ({ default: { // Must stay unused by the snapshot: each call spawns cat/grep/nmcli/ip. currentLoad: vi.fn(), networkInterfaces: vi.fn(), networkStats: vi.fn(), cpuTemperature: vi.fn(async () => ({ main: 49.2, cores: [48.5, 51.1], max: 51.1, })), battery: vi.fn(async () => ({ hasBattery: true, isCharging: false, percent: 78, timeRemaining: 98, acConnected: false, manufacturer: "SONY", cycleCount: 248, designedCapacity: 50, maxCapacity: 45, })), wifiConnections: vi.fn(async () => [ { iface: "wlan0", ssid: "HomeNet", bssid: "11:22:33", quality: 66, txRate: 130, }, ]), wifiNetworks: vi.fn(async () => [ { ssid: "HomeNet", channel: 1, quality: 66, security: ["WPA2"], rsnFlags: [], }, ]), fsSize: vi.fn(async () => [ { fs: "/dev/sda1", type: "ext4", size: 500 * 1024 * 1024 * 1024, used: 120 * 1024 * 1024 * 1024, use: 24, mount: "/", }, ]), diskLayout: vi.fn(async () => [ { device: "/dev/sda", type: "SSD", smartStatus: "Ok", }, ]), }, })); import { getSystemMetricsSnapshot } from "@/lib/server/modules/system/service"; describe("system service", () => { afterEach(() => { vi.restoreAllMocks(); }); it("returns a snapshot with expected shape", async () => { // Network throughput is a rate between two snapshots taken 2s apart. const now = vi.spyOn(Date, "now").mockReturnValue(10_000); countersMock.mockResolvedValueOnce({ rxBytes: 0, txBytes: 0 }); await getSystemMetricsSnapshot({ bypassCache: true }); now.mockReturnValue(12_000); countersMock.mockResolvedValueOnce({ rxBytes: 6_200_000, txBytes: 2_200_000 }); const snapshot = await getSystemMetricsSnapshot({ bypassCache: true }); expect(snapshot.hostname).toBe("pi"); expect(snapshot.timestamp).toBeTypeOf("string"); expect(snapshot.uptimeSeconds).toBe(3600); expect(snapshot.cpu.normalizedPercent).toBeGreaterThanOrEqual(0); expect(snapshot.cpu.normalizedPercent).toBeLessThanOrEqual(100); expect(snapshot.memory.totalBytes).toBe(1024); expect(snapshot.temperature.mainCelsius).toBe(49.2); expect(snapshot.battery.percent).toBe(78); expect(snapshot.battery.manufacturer).toBe("SONY"); expect(snapshot.battery.cycleCount).toBe(248); expect(snapshot.battery.designToMaxCapacityPercent).toBe(90); expect(snapshot.wifi.connected).toBe(true); expect(snapshot.wifi.ssid).toBe("HomeNet"); expect(snapshot.wifi.iface).toBe("wlan0"); expect(snapshot.wifi.ipv4).toBe("192.168.1.22"); expect(snapshot.wifi.ipv6).toBe("fe80::1234"); expect(snapshot.wifi.downloadMbps).toBe(24.8); expect(snapshot.wifi.uploadMbps).toBe(8.8); }); it("reads network and CPU load from the kernel, without spawning commands", async () => { const si = (await import("systeminformation")).default; countersMock.mockResolvedValue({ rxBytes: 0, txBytes: 0 }); await getSystemMetricsSnapshot({ bypassCache: true }); expect(si.currentLoad).not.toHaveBeenCalled(); expect(si.networkInterfaces).not.toHaveBeenCalled(); expect(si.networkStats).not.toHaveBeenCalled(); }); it("serves cached snapshot unless bypassCache is set", async () => { const cachedOne = await getSystemMetricsSnapshot(); const cachedTwo = await getSystemMetricsSnapshot(); const fresh = await getSystemMetricsSnapshot({ bypassCache: true }); expect(cachedTwo).toBe(cachedOne); expect(fresh).not.toBe(cachedOne); }); });