hsync 0.30.1 → 0.31.0
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.
- package/.github/workflows/ci.yml +32 -0
- package/.husky/pre-commit +1 -0
- package/.prettierrc +7 -0
- package/Readme.md +1 -0
- package/cli.js +103 -56
- package/config.js +6 -6
- package/connection.js +44 -48
- package/dist/hsync.js +1082 -831
- package/dist/hsync.min.js +28862 -1
- package/dist/hsync.min.js.LICENSE.txt +11 -1
- package/eslint.config.js +26 -0
- package/hsync-web.js +18 -17
- package/hsync.js +14 -18
- package/index.js +3 -3
- package/lib/fetch.js +2 -4
- package/lib/peers.js +69 -68
- package/lib/rtc-node.js +35 -34
- package/lib/rtc-web.js +60 -58
- package/lib/socket-listeners.js +16 -22
- package/lib/socket-map.js +5 -5
- package/lib/socket-relays.js +12 -17
- package/lib/web-handler.js +8 -14
- package/package.json +61 -18
- package/shell.js +4 -8
- package/test/mocks/mqtt.mock.js +25 -0
- package/test/mocks/net.mock.js +34 -0
- package/test/mocks/rtc.mock.js +9 -0
- package/test/setup.js +10 -0
- package/test/unit/config.test.js +36 -0
- package/test/unit/fetch.test.js +189 -0
- package/test/unit/peers.test.js +275 -0
- package/test/unit/socket-listeners.test.js +319 -0
- package/test/unit/socket-map.test.js +64 -0
- package/test/unit/socket-relays.test.js +315 -0
- package/test/unit/web-handler.test.js +223 -0
- package/todo.md +324 -0
- package/vitest.config.js +15 -0
- package/webpack.config.js +24 -8
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { initRelays, setNet } from '../../lib/socket-relays.js';
|
|
3
|
+
import { sockets } from '../../lib/socket-map.js';
|
|
4
|
+
|
|
5
|
+
describe('socket-relays', () => {
|
|
6
|
+
let mockNet;
|
|
7
|
+
let mockSocket;
|
|
8
|
+
let mockHsyncClient;
|
|
9
|
+
let mockPeer;
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
mockSocket = {
|
|
13
|
+
socketId: null,
|
|
14
|
+
write: vi.fn(),
|
|
15
|
+
end: vi.fn(),
|
|
16
|
+
on: vi.fn(),
|
|
17
|
+
connect: vi.fn((port, host, cb) => cb && cb()),
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// Create mock net module with class-based constructor
|
|
21
|
+
mockNet = {
|
|
22
|
+
Socket: class MockSocket {
|
|
23
|
+
constructor() {
|
|
24
|
+
Object.assign(this, mockSocket);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
mockPeer = {
|
|
30
|
+
hostName: 'remote.example.com',
|
|
31
|
+
notifications: {
|
|
32
|
+
oncloseRelaySocket: vi.fn(),
|
|
33
|
+
},
|
|
34
|
+
notifiers: {
|
|
35
|
+
closeListenerSocket: vi.fn(),
|
|
36
|
+
},
|
|
37
|
+
packAndSend: vi.fn(),
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
mockHsyncClient = {
|
|
41
|
+
myHostName: 'local.example.com',
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
setNet(mockNet);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('setNet', () => {
|
|
48
|
+
it('should set the net implementation', () => {
|
|
49
|
+
const customNet = { Socket: vi.fn() };
|
|
50
|
+
setNet(customNet);
|
|
51
|
+
// No error means success
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('initRelays', () => {
|
|
56
|
+
it('should return object with required methods', () => {
|
|
57
|
+
const relays = initRelays(mockHsyncClient);
|
|
58
|
+
|
|
59
|
+
expect(relays.addSocketRelay).toBeTypeOf('function');
|
|
60
|
+
expect(relays.getSocketRelays).toBeTypeOf('function');
|
|
61
|
+
expect(relays.connectSocket).toBeTypeOf('function');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should attach methods to hsyncClient', () => {
|
|
65
|
+
initRelays(mockHsyncClient);
|
|
66
|
+
|
|
67
|
+
expect(mockHsyncClient.cachedRelays).toBeTypeOf('object');
|
|
68
|
+
expect(mockHsyncClient.addSocketRelay).toBeTypeOf('function');
|
|
69
|
+
expect(mockHsyncClient.getSocketRelays).toBeTypeOf('function');
|
|
70
|
+
expect(mockHsyncClient.connectSocket).toBeTypeOf('function');
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('addSocketRelay', () => {
|
|
75
|
+
let relays;
|
|
76
|
+
|
|
77
|
+
beforeEach(() => {
|
|
78
|
+
relays = initRelays(mockHsyncClient);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should add relay with provided options', () => {
|
|
82
|
+
const relay = relays.addSocketRelay({
|
|
83
|
+
port: 3000,
|
|
84
|
+
targetPort: 4000,
|
|
85
|
+
targetHost: 'myserver.local',
|
|
86
|
+
whitelist: 'allowed.com',
|
|
87
|
+
blacklist: 'blocked.com',
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
expect(relay.port).toBe(3000);
|
|
91
|
+
expect(relay.targetPort).toBe(4000);
|
|
92
|
+
expect(relay.targetHost).toBe('myserver.local');
|
|
93
|
+
expect(relay.whitelist).toBe('allowed.com');
|
|
94
|
+
expect(relay.blacklist).toBe('blocked.com');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should use port as targetPort if not specified', () => {
|
|
98
|
+
const relay = relays.addSocketRelay({
|
|
99
|
+
port: 3000,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
expect(relay.targetPort).toBe(3000);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should use localhost as targetHost if not specified', () => {
|
|
106
|
+
const relay = relays.addSocketRelay({
|
|
107
|
+
port: 3000,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
expect(relay.targetHost).toBe('localhost');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should store relay by port key', () => {
|
|
114
|
+
relays.addSocketRelay({
|
|
115
|
+
port: 3000,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
expect(mockHsyncClient.cachedRelays['p3000']).toBeDefined();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should set hostName same as targetHost', () => {
|
|
122
|
+
const relay = relays.addSocketRelay({
|
|
123
|
+
port: 3000,
|
|
124
|
+
targetHost: 'myserver.local',
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
expect(relay.hostName).toBe('myserver.local');
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe('getSocketRelays', () => {
|
|
132
|
+
let relays;
|
|
133
|
+
|
|
134
|
+
beforeEach(() => {
|
|
135
|
+
relays = initRelays(mockHsyncClient);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should return empty array when no relays', () => {
|
|
139
|
+
const result = relays.getSocketRelays();
|
|
140
|
+
|
|
141
|
+
expect(result).toEqual([]);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should return relay info', () => {
|
|
145
|
+
relays.addSocketRelay({
|
|
146
|
+
port: 3000,
|
|
147
|
+
targetPort: 4000,
|
|
148
|
+
targetHost: 'myserver.local',
|
|
149
|
+
whitelist: 'allowed.com',
|
|
150
|
+
blacklist: 'blocked.com',
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const result = relays.getSocketRelays();
|
|
154
|
+
|
|
155
|
+
expect(result).toHaveLength(1);
|
|
156
|
+
expect(result[0]).toEqual({
|
|
157
|
+
port: 3000,
|
|
158
|
+
targetHost: 'myserver.local',
|
|
159
|
+
targetPort: 4000,
|
|
160
|
+
whitelist: 'allowed.com',
|
|
161
|
+
blacklist: 'blocked.com',
|
|
162
|
+
hostName: 'myserver.local',
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('should return multiple relays', () => {
|
|
167
|
+
relays.addSocketRelay({ port: 3000 });
|
|
168
|
+
relays.addSocketRelay({ port: 4000 });
|
|
169
|
+
|
|
170
|
+
const result = relays.getSocketRelays();
|
|
171
|
+
|
|
172
|
+
expect(result).toHaveLength(2);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should return empty strings for undefined whitelist/blacklist', () => {
|
|
176
|
+
relays.addSocketRelay({
|
|
177
|
+
port: 3000,
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const result = relays.getSocketRelays();
|
|
181
|
+
|
|
182
|
+
expect(result[0].whitelist).toBe('');
|
|
183
|
+
expect(result[0].blacklist).toBe('');
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe('connectSocket', () => {
|
|
188
|
+
let relays;
|
|
189
|
+
|
|
190
|
+
beforeEach(() => {
|
|
191
|
+
relays = initRelays(mockHsyncClient);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('should throw if no relay found for port', () => {
|
|
195
|
+
expect(() =>
|
|
196
|
+
relays.connectSocket(mockPeer, {
|
|
197
|
+
port: 9999,
|
|
198
|
+
socketId: 'test-socket',
|
|
199
|
+
hostName: 'remote.example.com',
|
|
200
|
+
})
|
|
201
|
+
).toThrow('no relay found for port: 9999');
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('should create socket and connect to relay target', async () => {
|
|
205
|
+
relays.addSocketRelay({
|
|
206
|
+
port: 3000,
|
|
207
|
+
targetPort: 4000,
|
|
208
|
+
targetHost: 'myserver.local',
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
const result = await relays.connectSocket(mockPeer, {
|
|
212
|
+
port: 3000,
|
|
213
|
+
socketId: 'test-socket',
|
|
214
|
+
hostName: 'remote.example.com',
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// Verify socket was connected to correct target
|
|
218
|
+
expect(mockSocket.connect).toHaveBeenCalledWith(4000, 'myserver.local', expect.any(Function));
|
|
219
|
+
expect(result.socketId).toBe('test-socket');
|
|
220
|
+
expect(result.targetHost).toBe('myserver.local');
|
|
221
|
+
expect(result.targetPort).toBe(4000);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('should assign socketId to created socket', async () => {
|
|
225
|
+
relays.addSocketRelay({
|
|
226
|
+
port: 3000,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
await relays.connectSocket(mockPeer, {
|
|
230
|
+
port: 3000,
|
|
231
|
+
socketId: 'my-socket-id',
|
|
232
|
+
hostName: 'remote.example.com',
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// Socket should be stored in sockets map with the socketId
|
|
236
|
+
expect(sockets['my-socket-id']).toBeDefined();
|
|
237
|
+
expect(sockets['my-socket-id'].socketId).toBe('my-socket-id');
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('should register oncloseRelaySocket notification', async () => {
|
|
241
|
+
relays.addSocketRelay({
|
|
242
|
+
port: 3000,
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
await relays.connectSocket(mockPeer, {
|
|
246
|
+
port: 3000,
|
|
247
|
+
socketId: 'test-socket',
|
|
248
|
+
hostName: 'remote.example.com',
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
expect(mockPeer.notifications.oncloseRelaySocket).toHaveBeenCalled();
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('should register socket event handlers', async () => {
|
|
255
|
+
relays.addSocketRelay({
|
|
256
|
+
port: 3000,
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
await relays.connectSocket(mockPeer, {
|
|
260
|
+
port: 3000,
|
|
261
|
+
socketId: 'test-socket',
|
|
262
|
+
hostName: 'remote.example.com',
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
expect(mockSocket.on).toHaveBeenCalledWith('data', expect.any(Function));
|
|
266
|
+
expect(mockSocket.on).toHaveBeenCalledWith('close', expect.any(Function));
|
|
267
|
+
expect(mockSocket.on).toHaveBeenCalledWith('error', expect.any(Function));
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('should send data via RTC when packAndSend available', async () => {
|
|
271
|
+
relays.addSocketRelay({
|
|
272
|
+
port: 3000,
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
await relays.connectSocket(mockPeer, {
|
|
276
|
+
port: 3000,
|
|
277
|
+
socketId: 'test-socket',
|
|
278
|
+
hostName: 'remote.example.com',
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// Get the data handler
|
|
282
|
+
const dataHandler = mockSocket.on.mock.calls.find((call) => call[0] === 'data')[1];
|
|
283
|
+
const testData = Buffer.from('test data');
|
|
284
|
+
dataHandler(testData);
|
|
285
|
+
|
|
286
|
+
expect(mockPeer.packAndSend).toHaveBeenCalledWith(
|
|
287
|
+
'socketData/test-socket',
|
|
288
|
+
expect.any(Buffer)
|
|
289
|
+
);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('should reject on socket error', async () => {
|
|
293
|
+
relays.addSocketRelay({
|
|
294
|
+
port: 3000,
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// Make connect call the error handler instead
|
|
298
|
+
mockSocket.connect = vi.fn((_port, _host, _cb) => {
|
|
299
|
+
// Don't call success callback
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
const connectPromise = relays.connectSocket(mockPeer, {
|
|
303
|
+
port: 3000,
|
|
304
|
+
socketId: 'test-socket',
|
|
305
|
+
hostName: 'remote.example.com',
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// Get error handler and call it
|
|
309
|
+
const errorHandler = mockSocket.on.mock.calls.find((call) => call[0] === 'error')[1];
|
|
310
|
+
errorHandler(new Error('Connection failed'));
|
|
311
|
+
|
|
312
|
+
await expect(connectPromise).rejects.toThrow('Connection failed');
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
});
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { createWebHandler, setNet } from '../../lib/web-handler.js';
|
|
3
|
+
|
|
4
|
+
describe('web-handler', () => {
|
|
5
|
+
let mockNet;
|
|
6
|
+
let mockSocket;
|
|
7
|
+
let mockMqConn;
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
// Create mock socket
|
|
11
|
+
mockSocket = {
|
|
12
|
+
socketId: null,
|
|
13
|
+
write: vi.fn(),
|
|
14
|
+
end: vi.fn(),
|
|
15
|
+
on: vi.fn(),
|
|
16
|
+
connect: vi.fn((port, host, cb) => cb()),
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// Create mock net module with class-based constructor
|
|
20
|
+
mockNet = {
|
|
21
|
+
Socket: class MockSocket {
|
|
22
|
+
constructor() {
|
|
23
|
+
Object.assign(this, mockSocket);
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Create mock MQTT connection
|
|
29
|
+
mockMqConn = {
|
|
30
|
+
publish: vi.fn(),
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
setNet(mockNet);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('setNet', () => {
|
|
37
|
+
it('should set the net implementation', () => {
|
|
38
|
+
const customNet = { Socket: vi.fn() };
|
|
39
|
+
setNet(customNet);
|
|
40
|
+
// No error means success - net is used internally
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('createWebHandler', () => {
|
|
45
|
+
it('should return handler with required methods', () => {
|
|
46
|
+
const handler = createWebHandler({
|
|
47
|
+
myHostName: 'test.example.com',
|
|
48
|
+
localHost: 'localhost',
|
|
49
|
+
port: 3000,
|
|
50
|
+
mqConn: mockMqConn,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
expect(handler.handleWebRequest).toBeTypeOf('function');
|
|
54
|
+
expect(handler.sockets).toBeTypeOf('object');
|
|
55
|
+
expect(handler.end).toBeTypeOf('function');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should start with empty sockets', () => {
|
|
59
|
+
const handler = createWebHandler({
|
|
60
|
+
myHostName: 'test.example.com',
|
|
61
|
+
localHost: 'localhost',
|
|
62
|
+
port: 3000,
|
|
63
|
+
mqConn: mockMqConn,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
expect(Object.keys(handler.sockets)).toHaveLength(0);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('handleWebRequest', () => {
|
|
71
|
+
let handler;
|
|
72
|
+
|
|
73
|
+
beforeEach(() => {
|
|
74
|
+
handler = createWebHandler({
|
|
75
|
+
myHostName: 'test.example.com',
|
|
76
|
+
localHost: 'localhost',
|
|
77
|
+
port: 3000,
|
|
78
|
+
mqConn: mockMqConn,
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should ignore requests for different hostnames', () => {
|
|
83
|
+
handler.handleWebRequest('other.example.com', 'socket-123', null, Buffer.from('test'));
|
|
84
|
+
|
|
85
|
+
// Socket should not be created for different hostname
|
|
86
|
+
expect(handler.sockets['socket-123']).toBeUndefined();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should create new socket for new socketId', () => {
|
|
90
|
+
const message = Buffer.from('GET / HTTP/1.1\r\n\r\n');
|
|
91
|
+
|
|
92
|
+
handler.handleWebRequest('test.example.com', 'socket-123', null, message);
|
|
93
|
+
|
|
94
|
+
// Verify socket was created and connected
|
|
95
|
+
expect(handler.sockets['socket-123']).toBeDefined();
|
|
96
|
+
expect(mockSocket.connect).toHaveBeenCalledWith(3000, 'localhost', expect.any(Function));
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should store socket in sockets map', () => {
|
|
100
|
+
const message = Buffer.from('GET / HTTP/1.1\r\n\r\n');
|
|
101
|
+
|
|
102
|
+
handler.handleWebRequest('test.example.com', 'socket-123', null, message);
|
|
103
|
+
|
|
104
|
+
expect(handler.sockets['socket-123']).toBeDefined();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should write message to socket after connect', () => {
|
|
108
|
+
const message = Buffer.from('GET / HTTP/1.1\r\n\r\n');
|
|
109
|
+
|
|
110
|
+
handler.handleWebRequest('test.example.com', 'socket-123', null, message);
|
|
111
|
+
|
|
112
|
+
expect(mockSocket.write).toHaveBeenCalledWith(message);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should handle close action', () => {
|
|
116
|
+
const message = Buffer.from('GET / HTTP/1.1\r\n\r\n');
|
|
117
|
+
|
|
118
|
+
// First create a socket
|
|
119
|
+
handler.handleWebRequest('test.example.com', 'socket-123', null, message);
|
|
120
|
+
expect(handler.sockets['socket-123']).toBeDefined();
|
|
121
|
+
|
|
122
|
+
// Now close it
|
|
123
|
+
handler.handleWebRequest('test.example.com', 'socket-123', 'close', Buffer.from(''));
|
|
124
|
+
|
|
125
|
+
expect(mockSocket.end).toHaveBeenCalled();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should ignore close for non-existent socket', () => {
|
|
129
|
+
handler.handleWebRequest('test.example.com', 'nonexistent', 'close', Buffer.from(''));
|
|
130
|
+
|
|
131
|
+
// Should not throw
|
|
132
|
+
expect(mockSocket.end).not.toHaveBeenCalled();
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should reuse existing socket for same socketId', () => {
|
|
136
|
+
const message1 = Buffer.from('first message');
|
|
137
|
+
const message2 = Buffer.from('second message');
|
|
138
|
+
|
|
139
|
+
handler.handleWebRequest('test.example.com', 'socket-123', null, message1);
|
|
140
|
+
handler.handleWebRequest('test.example.com', 'socket-123', null, message2);
|
|
141
|
+
|
|
142
|
+
// Socket should only be connected once (reused for second message)
|
|
143
|
+
expect(mockSocket.connect).toHaveBeenCalledTimes(1);
|
|
144
|
+
// But write should be called twice (once on connect, once for second message)
|
|
145
|
+
expect(mockSocket.write).toHaveBeenCalledTimes(2);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should register socket event handlers', () => {
|
|
149
|
+
const message = Buffer.from('test');
|
|
150
|
+
|
|
151
|
+
handler.handleWebRequest('test.example.com', 'socket-123', null, message);
|
|
152
|
+
|
|
153
|
+
expect(mockSocket.on).toHaveBeenCalledWith('data', expect.any(Function));
|
|
154
|
+
expect(mockSocket.on).toHaveBeenCalledWith('close', expect.any(Function));
|
|
155
|
+
expect(mockSocket.on).toHaveBeenCalledWith('error', expect.any(Function));
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('should publish reply on socket data', () => {
|
|
159
|
+
const message = Buffer.from('request');
|
|
160
|
+
const responseData = Buffer.from('HTTP/1.1 200 OK\r\n\r\n');
|
|
161
|
+
|
|
162
|
+
handler.handleWebRequest('test.example.com', 'socket-123', null, message);
|
|
163
|
+
|
|
164
|
+
// Get the data handler and call it
|
|
165
|
+
const dataHandler = mockSocket.on.mock.calls.find((call) => call[0] === 'data')[1];
|
|
166
|
+
dataHandler(responseData);
|
|
167
|
+
|
|
168
|
+
expect(mockMqConn.publish).toHaveBeenCalledWith(
|
|
169
|
+
'reply/test.example.com/socket-123',
|
|
170
|
+
responseData
|
|
171
|
+
);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('should publish close on socket close', () => {
|
|
175
|
+
const message = Buffer.from('request');
|
|
176
|
+
|
|
177
|
+
handler.handleWebRequest('test.example.com', 'socket-123', null, message);
|
|
178
|
+
|
|
179
|
+
// Get the close handler and call it
|
|
180
|
+
const closeHandler = mockSocket.on.mock.calls.find((call) => call[0] === 'close')[1];
|
|
181
|
+
closeHandler();
|
|
182
|
+
|
|
183
|
+
expect(mockMqConn.publish).toHaveBeenCalledWith('close/test.example.com/socket-123', '');
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe('end', () => {
|
|
188
|
+
it('should close all sockets', () => {
|
|
189
|
+
const handler = createWebHandler({
|
|
190
|
+
myHostName: 'test.example.com',
|
|
191
|
+
localHost: 'localhost',
|
|
192
|
+
port: 3000,
|
|
193
|
+
mqConn: mockMqConn,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Create some sockets
|
|
197
|
+
handler.handleWebRequest('test.example.com', 'socket-1', null, Buffer.from('test1'));
|
|
198
|
+
handler.handleWebRequest('test.example.com', 'socket-2', null, Buffer.from('test2'));
|
|
199
|
+
|
|
200
|
+
handler.end();
|
|
201
|
+
|
|
202
|
+
expect(mockSocket.end).toHaveBeenCalled();
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('should handle errors when closing sockets', () => {
|
|
206
|
+
const handler = createWebHandler({
|
|
207
|
+
myHostName: 'test.example.com',
|
|
208
|
+
localHost: 'localhost',
|
|
209
|
+
port: 3000,
|
|
210
|
+
mqConn: mockMqConn,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
mockSocket.end = vi.fn(() => {
|
|
214
|
+
throw new Error('socket error');
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
handler.handleWebRequest('test.example.com', 'socket-1', null, Buffer.from('test'));
|
|
218
|
+
|
|
219
|
+
// Should not throw
|
|
220
|
+
expect(() => handler.end()).not.toThrow();
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
});
|