about-system 0.10.15 → 0.10.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,347 @@
1
+ /**
2
+ * @fileoverview Memory and disk probes. Every function branches on the host
3
+ * platform, so `utils/platform` is mocked with live getters and each test
4
+ * picks the OS it wants to impersonate.
5
+ */
6
+ import { beforeEach, describe, expect, it, vi } from "vitest";
7
+
8
+ const flags = vi.hoisted(() => ({ linux: true, mac: false, windows: false }));
9
+ vi.mock("../utils/platform", () => ({
10
+ get IS_LINUX() {
11
+ return flags.linux;
12
+ },
13
+ get IS_MAC() {
14
+ return flags.mac;
15
+ },
16
+ get IS_WINDOWS() {
17
+ return flags.windows;
18
+ },
19
+ }));
20
+
21
+ const execCommand = vi.hoisted(() => vi.fn());
22
+ vi.mock("../utils/command", () => ({ execCommand, commandExists: vi.fn() }));
23
+
24
+ const fsMock = vi.hoisted(() => ({
25
+ readFileSync: vi.fn(),
26
+ existsSync: vi.fn(() => false),
27
+ writeFileSync: vi.fn(),
28
+ }));
29
+ vi.mock("fs", () => ({ ...fsMock, default: fsMock }));
30
+
31
+ const osMock = vi.hoisted(() => ({
32
+ totalmem: vi.fn(() => 16 * 1024 ** 3),
33
+ freemem: vi.fn(() => 4 * 1024 ** 3),
34
+ tmpdir: vi.fn(() => "/tmp"),
35
+ platform: vi.fn(() => "linux"),
36
+ }));
37
+ vi.mock("os", () => ({ ...osMock, default: osMock }));
38
+
39
+ const m = await import("./memory");
40
+ import type { InfoContext } from "../types/internal-types";
41
+
42
+ const ctx = (cache: InfoContext["cache"] = {}): InfoContext => ({ cache });
43
+ const platform = (p: "linux" | "mac" | "windows") => {
44
+ flags.linux = p === "linux";
45
+ flags.mac = p === "mac";
46
+ flags.windows = p === "windows";
47
+ };
48
+
49
+ const MEMINFO = [
50
+ "MemTotal: 16777216 kB",
51
+ "MemFree: 4194304 kB",
52
+ "MemAvailable: 8388608 kB",
53
+ "SwapTotal: 2097152 kB",
54
+ "SwapFree: 1572864 kB",
55
+ ].join("\n");
56
+
57
+ beforeEach(() => {
58
+ platform("linux");
59
+ execCommand.mockReset().mockReturnValue("");
60
+ fsMock.readFileSync.mockReset().mockReturnValue(MEMINFO);
61
+ osMock.totalmem.mockReturnValue(16 * 1024 ** 3);
62
+ osMock.freemem.mockReturnValue(4 * 1024 ** 3);
63
+ });
64
+
65
+ describe("ram_used", () => {
66
+ it("reads /proc/meminfo on Linux", () => {
67
+ expect(m.ram_used(ctx())).toBe("12/16GB");
68
+ });
69
+
70
+ it("falls back to os.totalmem when /proc/meminfo is unreadable", () => {
71
+ fsMock.readFileSync.mockImplementation(() => {
72
+ throw new Error("nope");
73
+ });
74
+ expect(m.ram_used(ctx())).toBe("12/16GB");
75
+ });
76
+
77
+ it("falls back to os.totalmem when meminfo lacks the keys", () => {
78
+ fsMock.readFileSync.mockReturnValue("garbage");
79
+ expect(m.ram_used(ctx())).toBe("12/16GB");
80
+ });
81
+
82
+ it("uses os memory on non-Linux hosts", () => {
83
+ platform("mac");
84
+ osMock.totalmem.mockReturnValue(8 * 1024 ** 3);
85
+ osMock.freemem.mockReturnValue(2 * 1024 ** 3);
86
+ expect(m.ram_used(ctx())).toBe("6/8GB");
87
+ expect(fsMock.readFileSync).not.toHaveBeenCalled();
88
+ });
89
+
90
+ it("serves a cached value and stores a fresh one", () => {
91
+ const cache = {};
92
+ expect(m.ram_used(ctx(cache))).toBe("12/16GB");
93
+ fsMock.readFileSync.mockReturnValue("MemTotal: 1 kB\nMemFree: 1 kB");
94
+ expect(m.ram_used(ctx(cache))).toBe("12/16GB");
95
+ });
96
+ });
97
+
98
+ describe("memory_available", () => {
99
+ it("reads MemAvailable on Linux", () => {
100
+ expect(m.memory_available()).toBe("8GB available");
101
+ });
102
+
103
+ it("returns empty when MemAvailable is missing or unreadable on Linux", () => {
104
+ fsMock.readFileSync.mockReturnValue("MemTotal: 1 kB");
105
+ expect(m.memory_available()).toBe("");
106
+ fsMock.readFileSync.mockImplementation(() => {
107
+ throw new Error("x");
108
+ });
109
+ expect(m.memory_available()).toBe("");
110
+ });
111
+
112
+ it("derives free+inactive pages from vm_stat on macOS", () => {
113
+ platform("mac");
114
+ execCommand.mockReturnValue(
115
+ [
116
+ "Mach Virtual Memory Statistics: (page size of 16384 bytes)",
117
+ "Pages free: 262144.",
118
+ "Pages inactive: 262144.",
119
+ ].join("\n"),
120
+ );
121
+ // (262144 + 262144) * 16384 bytes = 8 GiB
122
+ expect(m.memory_available()).toBe("8GB available");
123
+ expect(execCommand).toHaveBeenCalledWith("vm_stat");
124
+ });
125
+
126
+ it("returns empty on macOS when vm_stat output is unparseable", () => {
127
+ platform("mac");
128
+ execCommand.mockReturnValue("nonsense");
129
+ expect(m.memory_available()).toBe("");
130
+ execCommand.mockImplementation(() => {
131
+ throw new Error("x");
132
+ });
133
+ expect(m.memory_available()).toBe("");
134
+ });
135
+
136
+ it("returns empty on Windows", () => {
137
+ platform("windows");
138
+ expect(m.memory_available()).toBe("");
139
+ });
140
+ });
141
+
142
+ describe("swap_used", () => {
143
+ it("computes percent and MB on Linux", () => {
144
+ // 2 GiB total, 0.5 GiB used
145
+ expect(m.swap_used()).toBe("25% (512MB) swap");
146
+ });
147
+
148
+ it("returns empty when there is no swap or meminfo is bad", () => {
149
+ fsMock.readFileSync.mockReturnValue("SwapTotal: 0 kB\nSwapFree: 0 kB");
150
+ expect(m.swap_used()).toBe("");
151
+ fsMock.readFileSync.mockReturnValue("nothing");
152
+ expect(m.swap_used()).toBe("");
153
+ fsMock.readFileSync.mockImplementation(() => {
154
+ throw new Error("x");
155
+ });
156
+ expect(m.swap_used()).toBe("");
157
+ });
158
+
159
+ it("parses sysctl vm.swapusage on macOS", () => {
160
+ platform("mac");
161
+ execCommand.mockReturnValue("total = 2048.00M used = 512.00M free = 1536.00M (encrypted)");
162
+ expect(m.swap_used()).toBe("25% (512MB) swap");
163
+ });
164
+
165
+ it("reports zero swap on macOS when total is 0", () => {
166
+ platform("mac");
167
+ execCommand.mockReturnValue("total = 0.00M used = 0.00M free = 0.00M");
168
+ expect(m.swap_used()).toBe("0% (0MB) swap");
169
+ });
170
+
171
+ it("returns empty on macOS with unparseable output, and on Windows", () => {
172
+ platform("mac");
173
+ execCommand.mockReturnValue("?");
174
+ expect(m.swap_used()).toBe("");
175
+ platform("windows");
176
+ expect(m.swap_used()).toBe("");
177
+ });
178
+ });
179
+
180
+ const DF_H = [
181
+ "Filesystem Size Used Avail Use% Mounted on",
182
+ "/dev/sda1 100G 45G 50G 47% /",
183
+ "tmpfs 1.0G 0 1.0G 0% /dev/shm",
184
+ "/dev/sdb1 500G 390G 80G 78% /mnt/data",
185
+ ].join("\n");
186
+
187
+ describe("disk_used", () => {
188
+ it("finds the root filesystem percentage", () => {
189
+ execCommand.mockReturnValue(DF_H);
190
+ expect(m.disk_used(ctx())).toBe("47%");
191
+ });
192
+
193
+ it("falls back to a regex when no line ends in ' /' with a % column", () => {
194
+ execCommand.mockReturnValue("Filesystem Use% Mounted\nrootfs 33% /");
195
+ expect(m.disk_used(ctx())).toBe("33%");
196
+ });
197
+
198
+ it("returns empty when nothing matches", () => {
199
+ execCommand.mockReturnValue("Filesystem Size\nnone");
200
+ expect(m.disk_used(ctx())).toBe("");
201
+ });
202
+
203
+ it("reads the emulated storage line on Android", () => {
204
+ execCommand.mockReturnValue("/dev/fuse 100G 40G 60G 40% /storage/emulated");
205
+ expect(m.disk_used(ctx())).toBe("40%");
206
+ execCommand.mockReturnValue("/storage/emulated only");
207
+ expect(m.disk_used(ctx())).toBe("");
208
+ });
209
+
210
+ it("is empty on Windows, and caches the result", () => {
211
+ platform("windows");
212
+ const cache = {};
213
+ expect(m.disk_used(ctx(cache))).toBe("");
214
+ platform("linux");
215
+ execCommand.mockReturnValue(DF_H);
216
+ expect(m.disk_used(ctx(cache))).toBe("");
217
+ });
218
+
219
+ it("returns empty when df throws inside the probe", () => {
220
+ execCommand.mockImplementation(() => {
221
+ throw new Error("x");
222
+ });
223
+ expect(m.disk_used(ctx())).toBe("");
224
+ });
225
+ });
226
+
227
+ const GB = 1024 * 1024;
228
+ const dfK = (rows: string[]) =>
229
+ ["Filesystem 1K-blocks Used Available Use% Mounted on", ...rows].join("\n");
230
+
231
+ describe("disk_size", () => {
232
+ it("reports a single real disk without its mount point", () => {
233
+ execCommand.mockReturnValue(
234
+ dfK([
235
+ `/dev/sda1 ${100 * GB} ${45 * GB} ${50 * GB} 47% /`,
236
+ `tmpfs ${100 * GB} 0 ${100 * GB} 0% /run`,
237
+ `/dev/sda2 ${100 * GB} ${1 * GB} ${99 * GB} 1% /boot`,
238
+ `/dev/small 1000 10 990 1% /small`,
239
+ "short row",
240
+ `/dev/bad abc def 1 1% /bad`,
241
+ ]),
242
+ );
243
+ expect(m.disk_size(ctx())).toBe("45/100GB");
244
+ });
245
+
246
+ it("labels each disk by mount point when several are found", () => {
247
+ execCommand.mockReturnValue(
248
+ dfK([
249
+ `/dev/sda1 ${100 * GB} ${45 * GB} ${50 * GB} 47% /`,
250
+ `/dev/sdb1 ${500 * GB} ${390 * GB} ${80 * GB} 78% /mnt/data`,
251
+ `devtmpfs ${50 * GB} 0 ${50 * GB} 0% /dev`,
252
+ `overlay ${50 * GB} 0 ${50 * GB} 0% /var/lib/docker`,
253
+ `proc ${50 * GB} 0 ${50 * GB} 0% /proc`,
254
+ `sysfs ${50 * GB} 0 ${50 * GB} 0% /sys`,
255
+ ]),
256
+ );
257
+ expect(m.disk_size(ctx())).toBe("/(45/100GB) /mnt/data(390/500GB)");
258
+ });
259
+
260
+ it("returns empty when no disk qualifies", () => {
261
+ execCommand.mockReturnValue(dfK([`tmpfs ${100 * GB} 0 ${100 * GB} 0% /run`]));
262
+ expect(m.disk_size(ctx())).toBe("");
263
+ });
264
+
265
+ it("only counts / and /Volumes/* on macOS", () => {
266
+ platform("mac");
267
+ execCommand.mockReturnValue(
268
+ dfK([
269
+ `/dev/disk3s1 ${500 * GB} ${200 * GB} ${300 * GB} 40% /`,
270
+ `/dev/disk3s5 ${500 * GB} ${200 * GB} ${300 * GB} 40% /System/Volumes/Data`,
271
+ `/dev/disk4s1 ${1000 * GB} ${900 * GB} ${100 * GB} 90% /Volumes/Backup`,
272
+ ]),
273
+ );
274
+ expect(m.disk_size(ctx())).toBe("/(200/500GB) /Volumes/Backup(900/1000GB)");
275
+ });
276
+
277
+ it("is empty on Windows and when df throws", () => {
278
+ platform("windows");
279
+ expect(m.disk_size(ctx())).toBe("");
280
+ platform("linux");
281
+ execCommand.mockImplementation(() => {
282
+ throw new Error("x");
283
+ });
284
+ expect(m.disk_size(ctx())).toBe("");
285
+ });
286
+
287
+ it("serves the cached value", () => {
288
+ const cache = {};
289
+ execCommand.mockReturnValue(dfK([`/dev/sda1 ${100 * GB} ${45 * GB} 1 47% /`]));
290
+ expect(m.disk_size(ctx(cache))).toBe("45/100GB");
291
+ execCommand.mockReturnValue("");
292
+ expect(m.disk_size(ctx(cache))).toBe("45/100GB");
293
+ });
294
+ });
295
+
296
+ describe("mount_points", () => {
297
+ it("lists non-system mounts with usage on Linux (max 3)", () => {
298
+ execCommand.mockReturnValue(
299
+ [
300
+ "Filesystem Size Used Avail Use% Mounted on",
301
+ "/dev/sda1 100G 45G 50G 47% /",
302
+ "tmpfs 1G 0 1G 0% /dev/shm",
303
+ "/dev/sdb1 500G 390G 80G 78% /mnt/data",
304
+ "/dev/sdc1 500G 100G 80G 20% /home",
305
+ "/dev/sdd1 500G 100G 80G 21% /srv",
306
+ "/dev/sde1 500G 100G 80G 22% /opt",
307
+ "weird 1G",
308
+ "nopercent mount",
309
+ "/dev/x 1G 1G 0 1% /System/Volumes/Data",
310
+ ].join("\n"),
311
+ );
312
+ expect(m.mount_points(ctx())).toBe("/mnt/data(78%) /home(20%) /srv(21%)");
313
+ });
314
+
315
+ it("reports only /Volumes mounts on macOS", () => {
316
+ platform("mac");
317
+ execCommand.mockReturnValue(
318
+ [
319
+ "Filesystem Size Used Avail Capacity Mounted on",
320
+ "/dev/disk3s1 500G 200G 300G 40% /",
321
+ "/dev/disk4s1 1T 900G 100G 90% /Volumes/Backup",
322
+ "/dev/disk3s5 500G 200G 300G 40% /System/Volumes/Data",
323
+ ].join("\n"),
324
+ );
325
+ expect(m.mount_points(ctx())).toBe("/Volumes/Backup(90%)");
326
+ });
327
+
328
+ it("is empty on Windows and when df throws", () => {
329
+ platform("windows");
330
+ expect(m.mount_points(ctx())).toBe("");
331
+ platform("linux");
332
+ execCommand.mockImplementation(() => {
333
+ throw new Error("x");
334
+ });
335
+ expect(m.mount_points(ctx())).toBe("");
336
+ });
337
+
338
+ it("serves the cached value", () => {
339
+ const cache = {};
340
+ execCommand.mockReturnValue(
341
+ "h\n/dev/a 1G 1G 0 5% /mnt/a",
342
+ );
343
+ expect(m.mount_points(ctx(cache))).toBe("/mnt/a(5%)");
344
+ execCommand.mockReturnValue("");
345
+ expect(m.mount_points(ctx(cache))).toBe("/mnt/a(5%)");
346
+ });
347
+ });
@@ -0,0 +1,174 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+
3
+ const flags = vi.hoisted(() => ({ linux: true, mac: false, windows: false }));
4
+ vi.mock("../utils/platform", () => ({
5
+ get IS_LINUX() {
6
+ return flags.linux;
7
+ },
8
+ get IS_MAC() {
9
+ return flags.mac;
10
+ },
11
+ get IS_WINDOWS() {
12
+ return flags.windows;
13
+ },
14
+ }));
15
+
16
+ const execCommand = vi.hoisted(() => vi.fn());
17
+ vi.mock("../utils/command", () => ({ execCommand, commandExists: vi.fn() }));
18
+
19
+ const osMock = vi.hoisted(() => ({
20
+ networkInterfaces: vi.fn(),
21
+ tmpdir: vi.fn(() => "/tmp"),
22
+ platform: vi.fn(() => "linux"),
23
+ }));
24
+ vi.mock("os", () => ({ ...osMock, default: osMock }));
25
+
26
+ const n = await import("./network");
27
+ import type { InfoContext } from "../types/internal-types";
28
+
29
+ const ctx = (extra: Partial<InfoContext> = {}): InfoContext => ({ cache: {}, ...extra });
30
+ const platform = (p: "linux" | "mac" | "windows") => {
31
+ flags.linux = p === "linux";
32
+ flags.mac = p === "mac";
33
+ flags.windows = p === "windows";
34
+ };
35
+
36
+ const iface = (address: string, internal = false, family = "IPv4") => ({
37
+ address,
38
+ family,
39
+ internal,
40
+ });
41
+
42
+ beforeEach(() => {
43
+ platform("linux");
44
+ execCommand.mockReset().mockReturnValue("");
45
+ osMock.networkInterfaces.mockReset().mockReturnValue({});
46
+ });
47
+
48
+ describe("ipinfo-derived fields", () => {
49
+ const info = { ip: "1.2.3.4", city: "Paris", hostname: "host.example", org: "AS123 Example Net Inc" };
50
+
51
+ it("ip / city read from the context", () => {
52
+ expect(n.ip(ctx({ ipInfo: info }))).toBe("1.2.3.4");
53
+ expect(n.city(ctx({ ipInfo: info }))).toBe("Paris");
54
+ expect(n.ip(ctx())).toBe("");
55
+ expect(n.city(ctx())).toBe("");
56
+ });
57
+
58
+ it("domain prefixes http:// only when a hostname is known", () => {
59
+ expect(n.domain(ctx({ ipInfo: info }))).toBe("http://host.example");
60
+ expect(n.domain(ctx())).toBe("");
61
+ });
62
+
63
+ it("isp drops the leading AS number", () => {
64
+ expect(n.isp(ctx({ ipInfo: info }))).toBe("Example Net Inc");
65
+ expect(n.isp(ctx())).toBe("");
66
+ });
67
+ });
68
+
69
+ describe("iplocal", () => {
70
+ it("prefers the wlan0 address from ifconfig on Linux", () => {
71
+ execCommand.mockReturnValue("eth0: flags\n inet 10.0.0.2\nwlan0: flags\n inet 192.168.1.50 netmask");
72
+ expect(n.iplocal()).toBe("192.168.1.50");
73
+ });
74
+
75
+ it("falls back to `ip addr`, skipping loopback", () => {
76
+ execCommand.mockImplementation((c: string) =>
77
+ c.startsWith("ifconfig")
78
+ ? ""
79
+ : "inet 127.0.0.1/8 lo\ninet 10.0.0.5/24 eth0\ninet 172.16.0.9/16 eth1",
80
+ );
81
+ expect(n.iplocal()).toBe("10.0.0.5 172.16.0.9");
82
+ });
83
+
84
+ it("falls back to os.networkInterfaces when the shell tools find nothing", () => {
85
+ osMock.networkInterfaces.mockReturnValue({
86
+ lo: [iface("127.0.0.1", true)],
87
+ eth0: [iface("10.1.1.1"), iface("fe80::1", false, "IPv6")],
88
+ wl: undefined,
89
+ });
90
+ expect(n.iplocal()).toBe("10.1.1.1");
91
+ });
92
+
93
+ it("survives the shell helpers throwing", () => {
94
+ execCommand.mockImplementation(() => {
95
+ throw new Error("x");
96
+ });
97
+ osMock.networkInterfaces.mockReturnValue({ en0: [iface("192.168.0.2")] });
98
+ expect(n.iplocal()).toBe("192.168.0.2");
99
+ });
100
+
101
+ it("uses os.networkInterfaces directly on macOS", () => {
102
+ platform("mac");
103
+ osMock.networkInterfaces.mockReturnValue({ en0: [iface("192.168.0.9")] });
104
+ expect(n.iplocal()).toBe("192.168.0.9");
105
+ expect(execCommand).not.toHaveBeenCalled();
106
+ });
107
+ });
108
+
109
+ describe("network_interfaces", () => {
110
+ it("lists active non-loopback IPv4 interfaces", () => {
111
+ osMock.networkInterfaces.mockReturnValue({
112
+ lo: [iface("127.0.0.1", true)],
113
+ eth0: [iface("10.0.0.1")],
114
+ docker0: [iface("172.17.0.1", true)],
115
+ wlan0: [iface("fe80::1", false, "IPv6"), iface("10.0.0.2")],
116
+ empty: undefined,
117
+ });
118
+ expect(n.network_interfaces(ctx())).toBe("eth0 wlan0");
119
+ });
120
+
121
+ it("is empty on Windows", () => {
122
+ platform("windows");
123
+ expect(n.network_interfaces(ctx())).toBe("");
124
+ });
125
+
126
+ it("is empty when os.networkInterfaces throws", () => {
127
+ osMock.networkInterfaces.mockImplementation(() => {
128
+ throw new Error("x");
129
+ });
130
+ expect(n.network_interfaces(ctx())).toBe("");
131
+ });
132
+
133
+ it("serves from the cache", () => {
134
+ const c = ctx();
135
+ osMock.networkInterfaces.mockReturnValue({ eth0: [iface("10.0.0.1")] });
136
+ expect(n.network_interfaces(c)).toBe("eth0");
137
+ osMock.networkInterfaces.mockReturnValue({});
138
+ expect(n.network_interfaces(c)).toBe("eth0");
139
+ });
140
+ });
141
+
142
+ describe("ports", () => {
143
+ const LSOF = [
144
+ "COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME",
145
+ "node 100 me 20u IPv4 0x1 0t0 TCP *:3000 (LISTEN)".replace("*:3000 (LISTEN)", "*:3000"),
146
+ "nginx-wo 101 me 6u IPv6 0x2 0t0 TCP [::1]:8080",
147
+ "node 102 me 21u IPv4 0x3 0t0 TCP *:3000",
148
+ "short line",
149
+ "weird 103 me 5u IPv4 0x4 0t0 TCP noport",
150
+ ].join("\n");
151
+
152
+ it("lists unique port+process pairs", () => {
153
+ execCommand.mockReturnValue(LSOF);
154
+ expect(n.ports(ctx())).toBe("3000node 8080ngin");
155
+ });
156
+
157
+ it("is empty on Windows and when lsof throws", () => {
158
+ platform("windows");
159
+ expect(n.ports(ctx())).toBe("");
160
+ platform("linux");
161
+ execCommand.mockImplementation(() => {
162
+ throw new Error("x");
163
+ });
164
+ expect(n.ports(ctx())).toBe("");
165
+ });
166
+
167
+ it("serves from the cache", () => {
168
+ const c = ctx();
169
+ execCommand.mockReturnValue(LSOF);
170
+ n.ports(c);
171
+ execCommand.mockReturnValue("");
172
+ expect(n.ports(c)).toBe("3000node 8080ngin");
173
+ });
174
+ });
@@ -0,0 +1,121 @@
1
+ /**
2
+ * @fileoverview The Windows and macOS branches of the platform facts, which
3
+ * the Linux-only suite in platform.test.ts cannot reach.
4
+ */
5
+ import { beforeEach, describe, expect, it, vi } from "vitest";
6
+
7
+ const flags = vi.hoisted(() => ({ linux: false, mac: false, windows: false }));
8
+ vi.mock("../utils/platform", () => ({
9
+ get IS_LINUX() {
10
+ return flags.linux;
11
+ },
12
+ get IS_MAC() {
13
+ return flags.mac;
14
+ },
15
+ get IS_WINDOWS() {
16
+ return flags.windows;
17
+ },
18
+ }));
19
+
20
+ const execCommand = vi.hoisted(() => vi.fn());
21
+ const commandExists = vi.hoisted(() => vi.fn());
22
+ vi.mock("../utils/command", () => ({ execCommand, commandExists }));
23
+
24
+ const osMock = vi.hoisted(() => ({
25
+ platform: vi.fn(() => "freebsd"),
26
+ release: vi.fn(() => "14.0"),
27
+ hostname: vi.fn(() => "h"),
28
+ userInfo: vi.fn(() => ({ username: "u" })),
29
+ tmpdir: vi.fn(() => "/tmp"),
30
+ }));
31
+ vi.mock("os", () => ({ ...osMock, default: osMock }));
32
+
33
+ const fsMock = vi.hoisted(() => ({
34
+ existsSync: vi.fn(() => false),
35
+ readFileSync: vi.fn(() => ""),
36
+ }));
37
+ vi.mock("fs", () => ({ ...fsMock, default: fsMock }));
38
+
39
+ const p = await import("./platform");
40
+ import type { InfoContext } from "../types/internal-types";
41
+
42
+ const ctx = (): InfoContext => ({ cache: {} });
43
+ const only = (k: "linux" | "mac" | "windows" | "none") => {
44
+ flags.linux = k === "linux";
45
+ flags.mac = k === "mac";
46
+ flags.windows = k === "windows";
47
+ };
48
+
49
+ beforeEach(() => {
50
+ only("none");
51
+ execCommand.mockReset().mockReturnValue("");
52
+ commandExists.mockReset().mockReturnValue(false);
53
+ });
54
+
55
+ describe("os_info off Linux", () => {
56
+ it("falls back to '<platform> <release>' on an unknown OS", () => {
57
+ expect(p.os_info(ctx())).toBe("freebsd 14.0");
58
+ });
59
+
60
+ it("combines the macOS product name and version from sw_vers", () => {
61
+ only("mac");
62
+ execCommand.mockImplementation((c: string) =>
63
+ c.endsWith("productName") ? "macOS" : "14.4.1",
64
+ );
65
+ expect(p.os_info(ctx())).toBe("macOS 14.4.1");
66
+ });
67
+
68
+ it("falls back to 'macOS <release>' when sw_vers is silent", () => {
69
+ only("mac");
70
+ expect(p.os_info(ctx())).toBe("macOS 14.0");
71
+ });
72
+
73
+ it("parses the Windows version from `ver`", () => {
74
+ only("windows");
75
+ execCommand.mockReturnValue("Microsoft Windows [Version 10.0.22631.3447]");
76
+ expect(p.os_info(ctx())).toBe("Windows 10.0.22631.3447");
77
+ });
78
+
79
+ it("falls back to the kernel release when `ver` is unparseable or throws", () => {
80
+ only("windows");
81
+ execCommand.mockReturnValue("garbage");
82
+ expect(p.os_info(ctx())).toBe("Windows 14.0");
83
+ execCommand.mockImplementation(() => {
84
+ throw new Error("x");
85
+ });
86
+ expect(p.os_info(ctx())).toBe("Windows 14.0");
87
+ });
88
+ });
89
+
90
+ describe("device off Linux", () => {
91
+ it("reads the Windows model from wmic", () => {
92
+ only("windows");
93
+ execCommand.mockReturnValue("Name=Surface Laptop 5\r\n");
94
+ expect(p.device(ctx())).toBe("Surface Laptop 5");
95
+ });
96
+
97
+ it("falls back to PowerShell on Windows when wmic gives nothing", () => {
98
+ only("windows");
99
+ execCommand.mockImplementation((c: string) =>
100
+ c.startsWith("wmic") ? "" : "ThinkPad X1\r\n",
101
+ );
102
+ expect(p.device(ctx())).toBe("ThinkPad X1");
103
+ });
104
+
105
+ it("is empty on Windows when neither tool answers", () => {
106
+ only("windows");
107
+ expect(p.device(ctx())).toBe("");
108
+ });
109
+
110
+ it("reads the macOS model name from system_profiler", () => {
111
+ only("mac");
112
+ execCommand.mockReturnValue("Hardware:\n Model Name: MacBook Pro\n Chip: M3");
113
+ expect(p.device(ctx())).toBe("MacBook Pro");
114
+ });
115
+
116
+ it("is empty on macOS when system_profiler has no model line", () => {
117
+ only("mac");
118
+ execCommand.mockReturnValue("nothing");
119
+ expect(p.device(ctx())).toBe("");
120
+ });
121
+ });