import { beforeEach, describe, expect, it, vi } from "vitest"; vi.mock("@/lib/server/modules/network/helper-client", () => ({ connectWifiFromHelper: vi.fn(), disconnectWifiFromHelper: vi.fn(), getNetworkStatusFromHelper: vi.fn(), scanWifiNetworksFromHelper: vi.fn(), isNetworkHelperUnavailableError: vi.fn(), })); vi.mock("@/lib/server/modules/system/service", () => ({ getSystemMetricsSnapshot: vi.fn(), })); import { connectNetwork, disconnectNetwork, getNetworkStatus, getWifiNetworks, } from "@/lib/server/modules/network/service"; import type { NetworkServiceError } from "@/lib/server/modules/network/service"; import { connectWifiFromHelper, disconnectWifiFromHelper, getNetworkStatusFromHelper, isNetworkHelperUnavailableError, scanWifiNetworksFromHelper, } from "@/lib/server/modules/network/helper-client"; import { getSystemMetricsSnapshot } from "@/lib/server/modules/system/service"; describe("network service", () => { beforeEach(() => { vi.clearAllMocks(); }); it("returns helper network status when helper is available", async () => { vi.mocked(getNetworkStatusFromHelper).mockResolvedValueOnce({ connected: true, iface: "wlan0", ssid: "HomeNet", ipv4: "192.168.1.44", signalPercent: 78, }); const result = await getNetworkStatus({ requestId: "req-1", }); expect(result.source).toBe("helper"); expect(result.data.connected).toBe(true); expect(getSystemMetricsSnapshot).not.toHaveBeenCalled(); }); it("falls back to metrics when helper is unavailable", async () => { vi.mocked(getNetworkStatusFromHelper).mockRejectedValueOnce( new Error("helper down"), ); vi.mocked(scanWifiNetworksFromHelper).mockRejectedValueOnce( new Error("helper down"), ); vi.mocked(isNetworkHelperUnavailableError).mockReturnValue(true); vi.mocked(getSystemMetricsSnapshot).mockResolvedValue({ timestamp: "2026-02-23T00:00:00.000Z", hostname: "homeio", platform: "linux", architecture: "x64", uptimeSeconds: 1_000, cpu: { oneMinute: 0, fiveMinute: 0, fifteenMinute: 0, normalizedPercent: 0, }, memory: { totalBytes: 1, freeBytes: 1, usedBytes: 0, usedPercent: 0, }, temperature: { mainCelsius: null, maxCelsius: null, coresCelsius: [], }, battery: { hasBattery: false, isCharging: false, percent: null, timeRemainingMinutes: null, acConnected: null, manufacturer: null, cycleCount: null, designedCapacityWh: null, maxCapacityWh: null, designToMaxCapacityPercent: null, }, wifi: { connected: true, iface: "wlan0", ssid: "FallbackNet", bssid: null, signalPercent: 55, txRateMbps: null, downloadMbps: null, uploadMbps: null, ipv4: "192.168.1.12", ipv6: null, availableNetworks: [ { ssid: "FallbackNet", channel: 11, qualityPercent: 55, security: "WPA2", }, ], }, process: { pid: 1, uptimeSeconds: 1, nodeVersion: "v22", }, }); const statusResult = await getNetworkStatus(); const networksResult = await getWifiNetworks(); expect(statusResult.source).toBe("fallback"); expect(statusResult.data.ssid).toBe("FallbackNet"); expect(networksResult.source).toBe("fallback"); expect(networksResult.data[0]?.ssid).toBe("FallbackNet"); }); it("reuses fallback cache between network status requests", async () => { vi.useFakeTimers(); vi.setSystemTime(new Date("2026-02-24T19:00:00.000Z")); vi.mocked(getNetworkStatusFromHelper).mockRejectedValue(new Error("helper down")); vi.mocked(isNetworkHelperUnavailableError).mockReturnValue(true); vi.mocked(getSystemMetricsSnapshot).mockResolvedValue({ timestamp: "2026-02-23T00:00:00.000Z", hostname: "homeio", platform: "linux", architecture: "x64", uptimeSeconds: 1_000, cpu: { oneMinute: 0, fiveMinute: 0, fifteenMinute: 0, normalizedPercent: 0, }, memory: { totalBytes: 1, freeBytes: 1, usedBytes: 0, usedPercent: 0, }, temperature: { mainCelsius: null, maxCelsius: null, coresCelsius: [], }, battery: { hasBattery: false, isCharging: false, percent: null, timeRemainingMinutes: null, acConnected: null, manufacturer: null, cycleCount: null, designedCapacityWh: null, maxCapacityWh: null, designToMaxCapacityPercent: null, }, wifi: { connected: true, iface: "wlan0", ssid: "FallbackNet", bssid: null, signalPercent: 55, txRateMbps: null, downloadMbps: null, uploadMbps: null, ipv4: "192.168.1.12", ipv6: null, availableNetworks: [], }, process: { pid: 1, uptimeSeconds: 1, nodeVersion: "v22", }, }); try { const first = await getNetworkStatus(); const second = await getNetworkStatus(); expect(first.source).toBe("fallback"); expect(second.source).toBe("fallback"); expect(vi.mocked(getSystemMetricsSnapshot).mock.calls.length).toBeLessThanOrEqual(1); } finally { vi.useRealTimers(); } }); it("validates connect payload and maps helper errors", async () => { await expect(connectNetwork({})).rejects.toMatchObject({ code: "invalid_request", statusCode: 400, } satisfies Partial); const helperError = Object.assign(new Error("auth failed"), { name: "NetworkHelperError", code: "auth_failed", statusCode: 401, }); vi.mocked(connectWifiFromHelper).mockRejectedValueOnce(helperError); await expect(connectNetwork({ ssid: "HomeNet", password: "bad" })).rejects.toMatchObject( { code: "auth_failed", statusCode: 401, } satisfies Partial, ); }); it("validates disconnect payload and forwards helper result", async () => { await expect(disconnectNetwork({ iface: "" })).rejects.toMatchObject({ code: "invalid_request", statusCode: 400, } satisfies Partial); vi.mocked(disconnectWifiFromHelper).mockResolvedValueOnce({ connected: false, iface: "wlan0", ssid: null, ipv4: null, signalPercent: null, }); const result = await disconnectNetwork({ iface: "wlan0", }); expect(result.connected).toBe(false); }); });