Skip to content

Commit f61336f

Browse files
committed
feat: enhance tests for socket functionality and add ICMP checksum tests
1 parent 06a7211 commit f61336f

File tree

6 files changed

+597
-3
lines changed

6 files changed

+597
-3
lines changed

.github/workflows/test.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,21 @@ jobs:
2323
with:
2424
node-version: ${{ matrix.node-version }}
2525

26+
# unfortunately, raw sockets require elevated privileges to run tests, and neither capabilities nor sudo are supported on Linux runners
27+
- name: Set raw socket capability (Linux)
28+
if: runner.os == 'Linux'
29+
run: sudo setcap cap_net_raw,cap_net_admin,cap_net_bind_service+eip $(which node)
30+
2631
- name: Install dependencies
2732
run: npm ci
2833

2934
- name: Build
3035
run: npm run build
3136

37+
- name: Run tests (Mac)
38+
if: runner.os == 'macOS'
39+
run: sudo npm test
40+
3241
- name: Run tests
42+
if: runner.os != 'macOS'
3343
run: npm test
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import { describe, it } from 'node:test';
2+
import assert from 'node:assert';
3+
import { htonl, htons, ntohl, ntohs } from '../../index.mjs';
4+
5+
describe('byte order conversions', () => {
6+
it('should convert 0x1234 with htons', () => {
7+
// arrange
8+
const value = 0x1234;
9+
10+
// act
11+
const result = htons(value);
12+
13+
// assert
14+
assert.strictEqual(typeof result, 'number');
15+
});
16+
17+
it('should convert 0x1234 back with ntohs after htons', () => {
18+
// arrange
19+
const original = 0x1234;
20+
21+
// act
22+
const result = ntohs(htons(original));
23+
24+
// assert
25+
assert.strictEqual(result, original);
26+
});
27+
28+
it('should handle port number conversion (80)', () => {
29+
// arrange
30+
const httpPort = 80;
31+
32+
// act
33+
const networkOrder = htons(httpPort);
34+
const hostOrder = ntohs(networkOrder);
35+
36+
// assert
37+
assert.strictEqual(hostOrder, httpPort);
38+
});
39+
40+
it('should handle port number conversion (443)', () => {
41+
// arrange
42+
const httpsPort = 443;
43+
44+
// act
45+
const networkOrder = htons(httpsPort);
46+
const hostOrder = ntohs(networkOrder);
47+
48+
// assert
49+
assert.strictEqual(hostOrder, httpsPort);
50+
});
51+
52+
it('should handle max 16-bit value', () => {
53+
// arrange
54+
const maxValue = 0xffff;
55+
56+
// act
57+
const result = ntohs(htons(maxValue));
58+
59+
// assert
60+
assert.strictEqual(result, maxValue);
61+
});
62+
63+
it('should handle zero', () => {
64+
// arrange
65+
const zero = 0;
66+
67+
// act
68+
const result = ntohs(htons(zero));
69+
70+
// assert
71+
assert.strictEqual(result, zero);
72+
});
73+
74+
it('should convert 0x12345678 with htonl', () => {
75+
// arrange
76+
const value = 0x12345678;
77+
78+
// act
79+
const result = htonl(value);
80+
81+
// assert
82+
assert.strictEqual(typeof result, 'number');
83+
});
84+
85+
it('should convert 0x12345678 back with ntohl after htonl', () => {
86+
// arrange
87+
const original = 0x12345678;
88+
89+
// act
90+
const result = ntohl(htonl(original));
91+
92+
// assert
93+
assert.strictEqual(result, original);
94+
});
95+
96+
it('should handle IPv4 address conversion (192.168.1.1)', () => {
97+
// arrange
98+
const ipAddress = ((192 << 24) | (168 << 16) | (1 << 8) | 1) >>> 0;
99+
100+
// act
101+
const networkOrder = htonl(ipAddress);
102+
const hostOrder = ntohl(networkOrder);
103+
104+
// assert
105+
assert.strictEqual(hostOrder >>> 0, ipAddress >>> 0);
106+
});
107+
108+
it('should handle IPv4 address conversion (127.0.0.1)', () => {
109+
// arrange
110+
const localhost = (127 << 24) | (0 << 16) | (0 << 8) | 1;
111+
112+
// act
113+
const networkOrder = htonl(localhost);
114+
const hostOrder = ntohl(networkOrder);
115+
116+
// assert
117+
assert.strictEqual(hostOrder, localhost);
118+
});
119+
120+
it('should handle max 32-bit value', () => {
121+
// arrange
122+
const maxValue = 0xffffffff;
123+
124+
// act
125+
const result = ntohl(htonl(maxValue));
126+
127+
// assert
128+
assert.strictEqual(result >>> 0, maxValue >>> 0);
129+
});
130+
131+
it('should handle zero', () => {
132+
// arrange
133+
const zero = 0;
134+
135+
// act
136+
const result = ntohl(htonl(zero));
137+
138+
// assert
139+
assert.strictEqual(result, zero);
140+
});
141+
142+
it('should be idempotent for double conversion (htons)', () => {
143+
// arrange
144+
const original = 0x1234;
145+
146+
// act
147+
const once = htons(original);
148+
const twice = htons(htons(original));
149+
const back = ntohs(ntohs(twice));
150+
151+
// assert
152+
assert.strictEqual(back, original);
153+
});
154+
155+
it('should be idempotent for double conversion (htonl)', () => {
156+
// arrange
157+
const original = 0x12345678;
158+
159+
// act
160+
const once = htonl(original);
161+
const twice = htonl(htonl(original));
162+
const back = ntohl(ntohl(twice));
163+
164+
// assert
165+
assert.strictEqual(back, original);
166+
});
167+
});

test/e2e/cjs-compat.test.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import assert from 'node:assert';
44
describe('cjs import', () => {
55
it('should be importable via require', async () => {
66
// act
7-
const module = await import('../../index.js');
7+
const module = await import('../../index.cjs');
88

99
// assert
1010
assert.ok(module);
@@ -20,7 +20,7 @@ describe('cjs import', () => {
2020
it('should have same exports as esm', async () => {
2121
// arrange
2222
const esm = await import('../../index.mjs');
23-
const cjs = await import('../../index.js');
23+
const cjs = await import('../../index.cjs');
2424
const keysToIgnore = ['default', 'module.exports'];
2525

2626
// act

test/e2e/constants.test.mjs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import { describe, it } from 'node:test';
2+
import assert from 'node:assert';
3+
import { AddressFamily, Protocol, SocketLevel, SocketOption } from '../../index.mjs';
4+
5+
describe('AddressFamily', () => {
6+
it('should have IPv4 mapped to 1', () => {
7+
// assert
8+
assert.strictEqual(AddressFamily.IPv4, 1);
9+
});
10+
11+
it('should have IPv6 mapped to 2', () => {
12+
// assert
13+
assert.strictEqual(AddressFamily.IPv6, 2);
14+
});
15+
16+
it('should have reverse mapping for IPv4', () => {
17+
// assert
18+
assert.strictEqual(AddressFamily[1], 'IPv4');
19+
});
20+
21+
it('should have reverse mapping for IPv6', () => {
22+
// assert
23+
assert.strictEqual(AddressFamily[2], 'IPv6');
24+
});
25+
});
26+
27+
describe('Protocol', () => {
28+
it('should have None mapped to 0', () => {
29+
// assert
30+
assert.strictEqual(Protocol.None, 0);
31+
});
32+
33+
it('should have ICMP mapped to 1', () => {
34+
// assert
35+
assert.strictEqual(Protocol.ICMP, 1);
36+
});
37+
38+
it('should have TCP mapped to 6', () => {
39+
// assert
40+
assert.strictEqual(Protocol.TCP, 6);
41+
});
42+
43+
it('should have UDP mapped to 17', () => {
44+
// assert
45+
assert.strictEqual(Protocol.UDP, 17);
46+
});
47+
48+
it('should have ICMPv6 mapped to 58', () => {
49+
// assert
50+
assert.strictEqual(Protocol.ICMPv6, 58);
51+
});
52+
53+
it('should have reverse mappings', () => {
54+
// assert
55+
assert.strictEqual(Protocol[0], 'None');
56+
assert.strictEqual(Protocol[1], 'ICMP');
57+
assert.strictEqual(Protocol[6], 'TCP');
58+
assert.strictEqual(Protocol[17], 'UDP');
59+
assert.strictEqual(Protocol[58], 'ICMPv6');
60+
});
61+
});
62+
63+
describe('SocketLevel', () => {
64+
it('should have IPPROTO_IP defined', () => {
65+
// assert
66+
assert.strictEqual(typeof SocketLevel.IPPROTO_IP, 'number');
67+
});
68+
69+
it('should have IPPROTO_IPV6 defined', () => {
70+
// assert
71+
assert.strictEqual(typeof SocketLevel.IPPROTO_IPV6, 'number');
72+
});
73+
74+
it('should have SOL_SOCKET defined', () => {
75+
// assert
76+
assert.strictEqual(typeof SocketLevel.SOL_SOCKET, 'number');
77+
});
78+
});
79+
80+
describe('SocketOption', () => {
81+
it('should have IP_TTL defined', () => {
82+
// assert
83+
assert.strictEqual(typeof SocketOption.IP_TTL, 'number');
84+
});
85+
86+
it('should have IP_HDRINCL defined', () => {
87+
// assert
88+
assert.strictEqual(typeof SocketOption.IP_HDRINCL, 'number');
89+
});
90+
91+
it('should have IP_TOS defined', () => {
92+
// assert
93+
assert.strictEqual(typeof SocketOption.IP_TOS, 'number');
94+
});
95+
96+
it('should have SO_BROADCAST defined', () => {
97+
// assert
98+
assert.strictEqual(typeof SocketOption.SO_BROADCAST, 'number');
99+
});
100+
101+
it('should have SO_RCVBUF defined', () => {
102+
// assert
103+
assert.strictEqual(typeof SocketOption.SO_RCVBUF, 'number');
104+
});
105+
106+
it('should have SO_SNDBUF defined', () => {
107+
// assert
108+
assert.strictEqual(typeof SocketOption.SO_SNDBUF, 'number');
109+
});
110+
111+
it('should have IPV6_TTL defined', () => {
112+
// assert
113+
assert.strictEqual(typeof SocketOption.IPV6_TTL, 'number');
114+
});
115+
116+
it('should have IPV6_UNICAST_HOPS defined', () => {
117+
// assert
118+
assert.strictEqual(typeof SocketOption.IPV6_UNICAST_HOPS, 'number');
119+
});
120+
121+
it('should have IPV6_V6ONLY defined', () => {
122+
// assert
123+
assert.strictEqual(typeof SocketOption.IPV6_V6ONLY, 'number');
124+
});
125+
});

0 commit comments

Comments
 (0)