diff --git a/src/infra/os-summary.test.ts b/src/infra/os-summary.test.ts new file mode 100644 index 00000000000..533321f8dba --- /dev/null +++ b/src/infra/os-summary.test.ts @@ -0,0 +1,64 @@ +import os from "node:os"; +import { afterEach, describe, expect, it, vi } from "vitest"; + +const spawnSyncMock = vi.hoisted(() => vi.fn()); + +vi.mock("node:child_process", () => ({ + spawnSync: (...args: unknown[]) => spawnSyncMock(...args), +})); + +import { resolveOsSummary } from "./os-summary.js"; + +describe("resolveOsSummary", () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + it("formats darwin labels from sw_vers output", () => { + vi.spyOn(os, "platform").mockReturnValue("darwin"); + vi.spyOn(os, "release").mockReturnValue("24.0.0"); + vi.spyOn(os, "arch").mockReturnValue("arm64"); + spawnSyncMock.mockReturnValue({ + stdout: " 15.4 \n", + stderr: "", + pid: 1, + output: [], + status: 0, + signal: null, + }); + + expect(resolveOsSummary()).toEqual({ + platform: "darwin", + arch: "arm64", + release: "24.0.0", + label: "macos 15.4 (arm64)", + }); + }); + + it("falls back to os.release when sw_vers output is blank", () => { + vi.spyOn(os, "platform").mockReturnValue("darwin"); + vi.spyOn(os, "release").mockReturnValue("24.1.0"); + vi.spyOn(os, "arch").mockReturnValue("x64"); + spawnSyncMock.mockReturnValue({ + stdout: " ", + stderr: "", + pid: 1, + output: [], + status: 0, + signal: null, + }); + + expect(resolveOsSummary().label).toBe("macos 24.1.0 (x64)"); + }); + + it("formats windows and non-darwin labels from os metadata", () => { + vi.spyOn(os, "release").mockReturnValue("10.0.26100"); + vi.spyOn(os, "arch").mockReturnValue("x64"); + + vi.spyOn(os, "platform").mockReturnValue("win32"); + expect(resolveOsSummary().label).toBe("windows 10.0.26100 (x64)"); + + vi.spyOn(os, "platform").mockReturnValue("linux"); + expect(resolveOsSummary().label).toBe("linux 10.0.26100 (x64)"); + }); +}); diff --git a/src/infra/tailnet.test.ts b/src/infra/tailnet.test.ts new file mode 100644 index 00000000000..eeb259cbeb4 --- /dev/null +++ b/src/infra/tailnet.test.ts @@ -0,0 +1,54 @@ +import os from "node:os"; +import { afterEach, describe, expect, it, vi } from "vitest"; +import { + isTailnetIPv4, + listTailnetAddresses, + pickPrimaryTailnetIPv4, + pickPrimaryTailnetIPv6, +} from "./tailnet.js"; + +describe("tailnet helpers", () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + it("detects tailscale ipv4 ranges", () => { + expect(isTailnetIPv4("100.64.0.1")).toBe(true); + expect(isTailnetIPv4("100.127.255.254")).toBe(true); + expect(isTailnetIPv4("100.63.255.255")).toBe(false); + expect(isTailnetIPv4("192.168.1.10")).toBe(false); + }); + + it("lists unique non-internal tailnet addresses only", () => { + vi.spyOn(os, "networkInterfaces").mockReturnValue({ + lo0: [{ address: "127.0.0.1", family: "IPv4", internal: true, netmask: "" }], + en0: [ + { address: " 100.88.1.5 ", family: "IPv4", internal: false, netmask: "" }, + { address: "100.88.1.5", family: "IPv4", internal: false, netmask: "" }, + { address: "fd7a:115c:a1e0::1", family: "IPv6", internal: false, netmask: "" }, + { address: " ", family: "IPv6", internal: false, netmask: "" }, + { address: "fe80::1", family: "IPv6", internal: false, netmask: "" }, + ], + // oxlint-disable-next-line typescript/no-explicit-any + } as any); + + expect(listTailnetAddresses()).toEqual({ + ipv4: ["100.88.1.5"], + ipv6: ["fd7a:115c:a1e0::1"], + }); + }); + + it("picks the first available tailnet addresses", () => { + vi.spyOn(os, "networkInterfaces").mockReturnValue({ + utun1: [ + { address: "100.99.1.1", family: "IPv4", internal: false, netmask: "" }, + { address: "100.99.1.2", family: "IPv4", internal: false, netmask: "" }, + { address: "fd7a:115c:a1e0::9", family: "IPv6", internal: false, netmask: "" }, + ], + // oxlint-disable-next-line typescript/no-explicit-any + } as any); + + expect(pickPrimaryTailnetIPv4()).toBe("100.99.1.1"); + expect(pickPrimaryTailnetIPv6()).toBe("fd7a:115c:a1e0::9"); + }); +});