Mirror: Best-effort discovery of the machine's local network using just Node.js dgram sockets
at main 5.2 kB view raw
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 it('deprioritizes bridge interfaces', () => { 121 networkInterfaces.mockReturnValueOnce({ 122 bridge0: [ 123 { 124 address: '100.0.0.11', 125 netmask: '255.255.255.0', 126 family: 'IPv4', 127 mac: '10:00:00:00:00:00', 128 internal: false, 129 cidr: '', 130 }, 131 ], 132 en1: [ 133 { 134 address: '10.0.0.10', 135 netmask: '255.255.255.0', 136 family: 'IPv4', 137 mac: '10:00:00:00:00:00', 138 internal: false, 139 cidr: '', 140 }, 141 ], 142 }); 143 expect(interfaceAssignments()).toMatchInlineSnapshot(` 144 [ 145 { 146 "address": "10.0.0.10", 147 "cidr": "", 148 "family": "IPv4", 149 "iname": "en1", 150 "internal": false, 151 "mac": "10:00:00:00:00:00", 152 "netmask": "255.255.255.0", 153 }, 154 { 155 "address": "100.0.0.11", 156 "cidr": "", 157 "family": "IPv4", 158 "iname": "bridge0", 159 "internal": false, 160 "mac": "10:00:00:00:00:00", 161 "netmask": "255.255.255.0", 162 }, 163 ] 164 `); 165 }); 166}); 167 168describe(matchAssignment, () => { 169 it('returns matching assignment by address', () => { 170 const assignment = { 171 iname: 'en0', 172 address: '100.0.0.11', 173 netmask: '255.255.255.0', 174 family: 'IPv4', 175 mac: '00:00:00:00:00:00', 176 internal: true, 177 cidr: '', 178 } as const; 179 expect(matchAssignment([assignment], '100.0.0.11')).toEqual({ 180 ...assignment, 181 gateway: null, 182 }); 183 }); 184 185 it('returns matching assignment by gateway', () => { 186 const assignment = { 187 iname: 'en0', 188 address: '100.0.0.11', 189 netmask: '255.255.255.0', 190 family: 'IPv4', 191 mac: '00:00:00:00:00:00', 192 internal: true, 193 cidr: '', 194 } as const; 195 expect(matchAssignment([assignment], '100.0.0.1')).toEqual({ 196 ...assignment, 197 gateway: '100.0.0.1', 198 }); 199 }); 200 201 it('returns null otherwise', () => { 202 const assignment = { 203 iname: 'en0', 204 address: '10.0.0.1', 205 netmask: '255.255.255.0', 206 family: 'IPv4', 207 mac: '00:00:00:00:00:00', 208 internal: true, 209 cidr: '', 210 } as const; 211 expect(matchAssignment([assignment], '100.0.0.1')).toBe(null); 212 }); 213});