Mirror: Best-effort discovery of the machine's local network using just Node.js dgram sockets
1import os from 'node:os';
2import { vi, describe, it, expect } from 'vitest';
3import {
4 parseMacStr,
5 parseIpStr,
6 toIpStr,
7 interfaceAssignments,
8 matchAssignment,
9 isSameSubnet,
10} from '../network';
11
12describe(parseMacStr, () => {
13 it('parses valid MAC addresses', () => {
14 expect(parseMacStr('11:22:33:44:55:66')).toEqual([17, 34, 51, 68, 85, 102]);
15 });
16});
17
18describe(parseIpStr, () => {
19 it('parses valid IP addresses', () => {
20 expect(parseIpStr('0.0.0.0').toString(16)).toBe('0');
21 expect(parseIpStr('1.1.1.1').toString(16)).toBe('1010101');
22 expect(parseIpStr('255.255.255.255').toString(16)).toBe('-1');
23 expect(parseIpStr('100.1.2.3').toString(16)).toBe('64010203');
24 });
25});
26
27describe(toIpStr, () => {
28 it.each([['0.0.0.0'], ['1.1.1.1'], ['255.255.255.255'], ['100.1.2.3']])(
29 'stringifies parsed IP (%s)',
30 addr => {
31 expect(toIpStr(parseIpStr(addr))).toBe(addr);
32 }
33 );
34});
35
36describe(isSameSubnet, () => {
37 it('returns true for same subnet', () => {
38 expect(isSameSubnet('192.168.1.1', '192.168.1.2', '255.255.255.0')).toBe(
39 true
40 );
41 });
42 it('returns false for different subnet', () => {
43 expect(isSameSubnet('192.168.1.1', '192.168.2.1', '255.255.255.0')).toBe(
44 false
45 );
46 });
47});
48
49describe(interfaceAssignments, () => {
50 const networkInterfaces = vi
51 .spyOn(os, 'networkInterfaces')
52 .mockReturnValue([] as any);
53
54 it('returns sorted list of assignments', () => {
55 networkInterfaces.mockReturnValueOnce({
56 lo0: [
57 {
58 address: '127.0.0.1',
59 netmask: '255.0.0.0',
60 family: 'IPv4',
61 mac: '00:00:00:00:00:00',
62 internal: true,
63 cidr: '',
64 },
65 ],
66 en1: [
67 {
68 address: '10.0.0.10',
69 netmask: '255.255.255.0',
70 family: 'IPv4',
71 mac: '00:00:00:00:00:00',
72 internal: false,
73 cidr: '',
74 },
75 ],
76 tun2: [
77 {
78 address: '100.0.0.11',
79 netmask: '255.255.255.0',
80 family: 'IPv4',
81 mac: '00:00:00:00:00:00',
82 internal: true,
83 cidr: '',
84 },
85 ],
86 });
87 expect(interfaceAssignments()).toMatchInlineSnapshot(`
88 [
89 {
90 "address": "10.0.0.10",
91 "cidr": "",
92 "family": "IPv4",
93 "iname": "en1",
94 "internal": false,
95 "mac": "00:00:00:00:00:00",
96 "netmask": "255.255.255.0",
97 },
98 {
99 "address": "100.0.0.11",
100 "cidr": "",
101 "family": "IPv4",
102 "iname": "tun2",
103 "internal": true,
104 "mac": "00:00:00:00:00:00",
105 "netmask": "255.255.255.0",
106 },
107 {
108 "address": "127.0.0.1",
109 "cidr": "",
110 "family": "IPv4",
111 "iname": "lo0",
112 "internal": true,
113 "mac": "00:00:00:00:00:00",
114 "netmask": "255.0.0.0",
115 },
116 ]
117 `);
118 });
119});
120
121describe(matchAssignment, () => {
122 it('returns matching assignment by address', () => {
123 const assignment = {
124 iname: 'en0',
125 address: '100.0.0.11',
126 netmask: '255.255.255.0',
127 family: 'IPv4',
128 mac: '00:00:00:00:00:00',
129 internal: true,
130 cidr: '',
131 } as const;
132 expect(matchAssignment([assignment], '100.0.0.11')).toEqual({
133 ...assignment,
134 gateway: null,
135 });
136 });
137
138 it('returns matching assignment by gateway', () => {
139 const assignment = {
140 iname: 'en0',
141 address: '100.0.0.11',
142 netmask: '255.255.255.0',
143 family: 'IPv4',
144 mac: '00:00:00:00:00:00',
145 internal: true,
146 cidr: '',
147 } as const;
148 expect(matchAssignment([assignment], '100.0.0.1')).toEqual({
149 ...assignment,
150 gateway: '100.0.0.1',
151 });
152 });
153
154 it('returns null otherwise', () => {
155 const assignment = {
156 iname: 'en0',
157 address: '10.0.0.1',
158 netmask: '255.255.255.0',
159 family: 'IPv4',
160 mac: '00:00:00:00:00:00',
161 internal: true,
162 cidr: '',
163 } as const;
164 expect(matchAssignment([assignment], '100.0.0.1')).toBe(null);
165 });
166});