cojson-transport-ws 0.20.0 → 0.20.2
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +14 -0
- package/dist/BatchedOutgoingMessages.d.ts +2 -0
- package/dist/BatchedOutgoingMessages.d.ts.map +1 -1
- package/dist/BatchedOutgoingMessages.js +18 -15
- package/dist/BatchedOutgoingMessages.js.map +1 -1
- package/dist/tests/createWebSocketPeer.test.js +158 -77
- package/dist/tests/createWebSocketPeer.test.js.map +1 -1
- package/package.json +2 -2
- package/src/BatchedOutgoingMessages.ts +23 -17
- package/src/tests/createWebSocketPeer.test.ts +213 -108
|
@@ -9,45 +9,91 @@ import type { AnyWebSocket } from "../types.js";
|
|
|
9
9
|
import { BUFFER_LIMIT, BUFFER_LIMIT_POLLING_INTERVAL } from "../utils.js";
|
|
10
10
|
import { createTestMetricReader, tearDownTestMetricReader } from "./utils.js";
|
|
11
11
|
|
|
12
|
-
const { CO_VALUE_PRIORITY, WEBSOCKET_CONFIG
|
|
13
|
-
cojsonInternals;
|
|
12
|
+
const { CO_VALUE_PRIORITY, WEBSOCKET_CONFIG } = cojsonInternals;
|
|
14
13
|
|
|
15
14
|
const { MAX_OUTGOING_MESSAGES_CHUNK_BYTES } = WEBSOCKET_CONFIG;
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
interface SetupOptions extends Partial<CreateWebSocketPeerOpts> {
|
|
17
|
+
initialReadyState?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function setup(opts: SetupOptions = {}) {
|
|
21
|
+
const { initialReadyState = 1, ...peerOpts } = opts;
|
|
22
|
+
const listeners = new Map<
|
|
23
|
+
string,
|
|
24
|
+
Set<{ callback: (event: MessageEvent) => void; once?: boolean }>
|
|
25
|
+
>();
|
|
19
26
|
|
|
20
27
|
const mockWebSocket = {
|
|
21
|
-
readyState:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
readyState: initialReadyState,
|
|
29
|
+
bufferedAmount: 0,
|
|
30
|
+
addEventListener: vi
|
|
31
|
+
.fn()
|
|
32
|
+
.mockImplementation(
|
|
33
|
+
(
|
|
34
|
+
type: string,
|
|
35
|
+
callback: (event: MessageEvent) => void,
|
|
36
|
+
options?: { once?: boolean },
|
|
37
|
+
) => {
|
|
38
|
+
if (!listeners.has(type)) {
|
|
39
|
+
listeners.set(type, new Set());
|
|
40
|
+
}
|
|
41
|
+
listeners.get(type)!.add({ callback, once: options?.once });
|
|
42
|
+
},
|
|
43
|
+
),
|
|
44
|
+
removeEventListener: vi
|
|
45
|
+
.fn()
|
|
46
|
+
.mockImplementation(
|
|
47
|
+
(type: string, callback: (event: MessageEvent) => void) => {
|
|
48
|
+
const set = listeners.get(type);
|
|
49
|
+
if (set) {
|
|
50
|
+
for (const entry of set) {
|
|
51
|
+
if (entry.callback === callback) {
|
|
52
|
+
set.delete(entry);
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
),
|
|
28
59
|
close: vi.fn(),
|
|
29
60
|
send: vi.fn(),
|
|
30
61
|
} as unknown as Mocked<AnyWebSocket>;
|
|
31
62
|
|
|
63
|
+
const triggerEvent = (type: string, event?: MessageEvent) => {
|
|
64
|
+
const set = listeners.get(type);
|
|
65
|
+
if (set) {
|
|
66
|
+
const toRemove: {
|
|
67
|
+
callback: (event: MessageEvent) => void;
|
|
68
|
+
once?: boolean;
|
|
69
|
+
}[] = [];
|
|
70
|
+
for (const entry of set) {
|
|
71
|
+
entry.callback(event ?? new MessageEvent(type));
|
|
72
|
+
if (entry.once) {
|
|
73
|
+
toRemove.push(entry);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
for (const entry of toRemove) {
|
|
77
|
+
set.delete(entry);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
32
82
|
const peer = createWebSocketPeer({
|
|
33
83
|
id: "test-peer",
|
|
34
84
|
websocket: mockWebSocket,
|
|
35
85
|
role: "client",
|
|
36
86
|
batchingByDefault: true,
|
|
37
|
-
...
|
|
87
|
+
...peerOpts,
|
|
38
88
|
});
|
|
39
89
|
|
|
40
|
-
return { mockWebSocket, peer, listeners };
|
|
90
|
+
return { mockWebSocket, peer, listeners, triggerEvent };
|
|
41
91
|
}
|
|
42
92
|
|
|
43
93
|
function serializeMessages(messages: SyncMessage[]) {
|
|
44
94
|
return messages.map((msg) => JSON.stringify(msg)).join("\n");
|
|
45
95
|
}
|
|
46
96
|
|
|
47
|
-
afterEach(() => {
|
|
48
|
-
setOutgoingMessagesChunkDelay(5);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
97
|
describe("createWebSocketPeer", () => {
|
|
52
98
|
test("should create a peer with correct properties", () => {
|
|
53
99
|
const { peer } = setup();
|
|
@@ -59,29 +105,25 @@ describe("createWebSocketPeer", () => {
|
|
|
59
105
|
});
|
|
60
106
|
|
|
61
107
|
test("should handle disconnection", async () => {
|
|
62
|
-
const {
|
|
108
|
+
const { triggerEvent, peer } = setup();
|
|
63
109
|
|
|
64
110
|
const onMessageSpy = vi.fn();
|
|
65
111
|
peer.incoming.onMessage(onMessageSpy);
|
|
66
112
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
closeHandler?.(new MessageEvent("close"));
|
|
113
|
+
triggerEvent("close");
|
|
70
114
|
|
|
71
115
|
expect(onMessageSpy).toHaveBeenCalledWith("Disconnected");
|
|
72
116
|
});
|
|
73
117
|
|
|
74
118
|
test("should handle ping timeout", async () => {
|
|
75
119
|
vi.useFakeTimers();
|
|
76
|
-
const {
|
|
120
|
+
const { triggerEvent, peer } = setup();
|
|
77
121
|
|
|
78
122
|
const onMessageSpy = vi.fn();
|
|
79
123
|
|
|
80
124
|
peer.incoming.onMessage(onMessageSpy);
|
|
81
125
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
messageHandler?.(new MessageEvent("message", { data: "{}" }));
|
|
126
|
+
triggerEvent("message", new MessageEvent("message", { data: "{}" }));
|
|
85
127
|
|
|
86
128
|
await vi.advanceTimersByTimeAsync(10_000);
|
|
87
129
|
|
|
@@ -156,9 +198,8 @@ describe("createWebSocketPeer", () => {
|
|
|
156
198
|
|
|
157
199
|
test("should call onSuccess handler after receiving first message", () => {
|
|
158
200
|
const onSuccess = vi.fn();
|
|
159
|
-
const {
|
|
201
|
+
const { triggerEvent } = setup({ onSuccess });
|
|
160
202
|
|
|
161
|
-
const messageHandler = listeners.get("message");
|
|
162
203
|
const message: SyncMessage = {
|
|
163
204
|
action: "known",
|
|
164
205
|
id: "co_ztest",
|
|
@@ -167,24 +208,24 @@ describe("createWebSocketPeer", () => {
|
|
|
167
208
|
};
|
|
168
209
|
|
|
169
210
|
// First message should trigger onSuccess
|
|
170
|
-
|
|
211
|
+
triggerEvent(
|
|
212
|
+
"message",
|
|
171
213
|
new MessageEvent("message", { data: JSON.stringify(message) }),
|
|
172
214
|
);
|
|
173
215
|
expect(onSuccess).toHaveBeenCalledTimes(1);
|
|
174
216
|
|
|
175
217
|
// Subsequent messages should not trigger onSuccess again
|
|
176
|
-
|
|
218
|
+
triggerEvent(
|
|
219
|
+
"message",
|
|
177
220
|
new MessageEvent("message", { data: JSON.stringify(message) }),
|
|
178
221
|
);
|
|
179
222
|
expect(onSuccess).toHaveBeenCalledTimes(1);
|
|
180
223
|
});
|
|
181
224
|
|
|
182
225
|
describe("batchingByDefault = true", () => {
|
|
183
|
-
test("should batch outgoing messages", async () => {
|
|
184
|
-
const { peer, mockWebSocket } = setup(
|
|
185
|
-
|
|
186
|
-
mockWebSocket.send.mockImplementation(() => {
|
|
187
|
-
mockWebSocket.readyState = 0;
|
|
226
|
+
test("should batch outgoing messages when socket is not ready", async () => {
|
|
227
|
+
const { peer, mockWebSocket, triggerEvent } = setup({
|
|
228
|
+
initialReadyState: 0,
|
|
188
229
|
});
|
|
189
230
|
|
|
190
231
|
const message1: SyncMessage = {
|
|
@@ -204,6 +245,10 @@ describe("createWebSocketPeer", () => {
|
|
|
204
245
|
void peer.outgoing.push(message1);
|
|
205
246
|
void peer.outgoing.push(message2);
|
|
206
247
|
|
|
248
|
+
// Simulate socket becoming ready
|
|
249
|
+
mockWebSocket.readyState = 1;
|
|
250
|
+
triggerEvent("open");
|
|
251
|
+
|
|
207
252
|
await waitFor(() => {
|
|
208
253
|
expect(mockWebSocket.send).toHaveBeenCalled();
|
|
209
254
|
});
|
|
@@ -213,44 +258,88 @@ describe("createWebSocketPeer", () => {
|
|
|
213
258
|
);
|
|
214
259
|
});
|
|
215
260
|
|
|
216
|
-
test("should
|
|
261
|
+
test("should send messages immediately when socket is ready", async () => {
|
|
217
262
|
const { peer, mockWebSocket } = setup();
|
|
218
263
|
|
|
219
|
-
|
|
220
|
-
|
|
264
|
+
const message1: SyncMessage = {
|
|
265
|
+
action: "known",
|
|
266
|
+
id: "co_ztest",
|
|
267
|
+
header: false,
|
|
268
|
+
sessions: {},
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const message2: SyncMessage = {
|
|
272
|
+
action: "content",
|
|
273
|
+
id: "co_zlow",
|
|
274
|
+
new: {},
|
|
275
|
+
priority: 6,
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
void peer.outgoing.push(message1);
|
|
279
|
+
|
|
280
|
+
await waitFor(() => {
|
|
281
|
+
expect(mockWebSocket.send).toHaveBeenCalledTimes(1);
|
|
221
282
|
});
|
|
222
283
|
|
|
223
|
-
|
|
284
|
+
expect(mockWebSocket.send).toHaveBeenCalledWith(JSON.stringify(message1));
|
|
285
|
+
|
|
286
|
+
void peer.outgoing.push(message2);
|
|
287
|
+
|
|
288
|
+
await waitFor(() => {
|
|
289
|
+
expect(mockWebSocket.send).toHaveBeenCalledTimes(2);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
expect(mockWebSocket.send).toHaveBeenNthCalledWith(
|
|
293
|
+
2,
|
|
294
|
+
JSON.stringify(message2),
|
|
295
|
+
);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
test("should sort remaining queued messages by priority after first message", async () => {
|
|
299
|
+
const { peer, mockWebSocket, triggerEvent } = setup({
|
|
300
|
+
initialReadyState: 0,
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
const lowPriority: SyncMessage = {
|
|
224
304
|
action: "content",
|
|
225
305
|
id: "co_zlow",
|
|
226
306
|
new: {},
|
|
227
307
|
priority: CO_VALUE_PRIORITY.LOW,
|
|
228
308
|
};
|
|
229
309
|
|
|
230
|
-
const
|
|
310
|
+
const highPriority: SyncMessage = {
|
|
231
311
|
action: "content",
|
|
232
312
|
id: "co_zhigh",
|
|
233
313
|
new: {},
|
|
234
314
|
priority: CO_VALUE_PRIORITY.HIGH,
|
|
235
315
|
};
|
|
236
316
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
void peer.outgoing.push(
|
|
317
|
+
// First message is pulled immediately before socket check,
|
|
318
|
+
// so it will be first regardless of priority
|
|
319
|
+
void peer.outgoing.push(lowPriority);
|
|
320
|
+
// Subsequent messages are queued and sorted by priority
|
|
321
|
+
void peer.outgoing.push(lowPriority);
|
|
322
|
+
void peer.outgoing.push(highPriority);
|
|
323
|
+
|
|
324
|
+
// Simulate socket becoming ready
|
|
325
|
+
mockWebSocket.readyState = 1;
|
|
326
|
+
triggerEvent("open");
|
|
240
327
|
|
|
241
328
|
await waitFor(() => {
|
|
242
329
|
expect(mockWebSocket.send).toHaveBeenCalled();
|
|
243
330
|
});
|
|
244
331
|
|
|
332
|
+
// First message (lowPriority) comes first as it was pulled before waiting,
|
|
333
|
+
// then remaining messages are sorted: highPriority before lowPriority
|
|
245
334
|
expect(mockWebSocket.send).toHaveBeenCalledWith(
|
|
246
|
-
[
|
|
335
|
+
[lowPriority, highPriority, lowPriority]
|
|
247
336
|
.map((msg) => JSON.stringify(msg))
|
|
248
337
|
.join("\n"),
|
|
249
338
|
);
|
|
250
339
|
});
|
|
251
340
|
|
|
252
|
-
test("should send
|
|
253
|
-
const { peer, mockWebSocket } = setup();
|
|
341
|
+
test("should send remaining queued messages when close is called", async () => {
|
|
342
|
+
const { peer, mockWebSocket } = setup({ initialReadyState: 0 });
|
|
254
343
|
|
|
255
344
|
const message1: SyncMessage = {
|
|
256
345
|
action: "known",
|
|
@@ -266,19 +355,36 @@ describe("createWebSocketPeer", () => {
|
|
|
266
355
|
priority: 6,
|
|
267
356
|
};
|
|
268
357
|
|
|
358
|
+
const message3: SyncMessage = {
|
|
359
|
+
action: "content",
|
|
360
|
+
id: "co_zmedium",
|
|
361
|
+
new: {},
|
|
362
|
+
priority: 3,
|
|
363
|
+
};
|
|
364
|
+
|
|
269
365
|
void peer.outgoing.push(message1);
|
|
270
366
|
void peer.outgoing.push(message2);
|
|
367
|
+
void peer.outgoing.push(message3);
|
|
271
368
|
|
|
369
|
+
// Set socket to open before close to allow sending
|
|
370
|
+
mockWebSocket.readyState = 1;
|
|
272
371
|
peer.outgoing.close();
|
|
273
372
|
|
|
373
|
+
// First message was already pulled by processQueue (waiting for socket),
|
|
374
|
+
// close() processes and sends remaining messages from queue sorted by priority
|
|
274
375
|
expect(mockWebSocket.send).toHaveBeenCalledWith(
|
|
275
|
-
[
|
|
376
|
+
[message3, message2].map((msg) => JSON.stringify(msg)).join("\n"),
|
|
276
377
|
);
|
|
277
378
|
});
|
|
278
379
|
|
|
279
380
|
test("should limit the chunk size to MAX_OUTGOING_MESSAGES_CHUNK_SIZE", async () => {
|
|
381
|
+
// This test verifies chunking works when socket is already ready
|
|
280
382
|
const { peer, mockWebSocket } = setup();
|
|
281
383
|
|
|
384
|
+
mockWebSocket.send.mockImplementation((value: string) => {
|
|
385
|
+
mockWebSocket.bufferedAmount += value.length;
|
|
386
|
+
});
|
|
387
|
+
|
|
282
388
|
const message1: SyncMessage = {
|
|
283
389
|
action: "known",
|
|
284
390
|
id: "co_ztest",
|
|
@@ -292,35 +398,30 @@ describe("createWebSocketPeer", () => {
|
|
|
292
398
|
priority: 6,
|
|
293
399
|
};
|
|
294
400
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
serializeMessages(stream.concat(message1)).length <
|
|
299
|
-
MAX_OUTGOING_MESSAGES_CHUNK_BYTES
|
|
300
|
-
) {
|
|
301
|
-
stream.push(message1);
|
|
302
|
-
void peer.outgoing.push(message1);
|
|
401
|
+
// Fill up the buffer
|
|
402
|
+
while (mockWebSocket.bufferedAmount < BUFFER_LIMIT) {
|
|
403
|
+
peer.outgoing.push(message1);
|
|
303
404
|
}
|
|
304
405
|
|
|
406
|
+
mockWebSocket.send.mockClear();
|
|
407
|
+
|
|
305
408
|
void peer.outgoing.push(message2);
|
|
306
409
|
|
|
307
|
-
|
|
308
|
-
expect(mockWebSocket.send).toHaveBeenCalledTimes(2);
|
|
309
|
-
});
|
|
410
|
+
expect(mockWebSocket.send).not.toHaveBeenCalled();
|
|
310
411
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
);
|
|
412
|
+
// Reset the buffer, make it look like we have sent the messages
|
|
413
|
+
mockWebSocket.bufferedAmount = 0;
|
|
314
414
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
);
|
|
415
|
+
await waitFor(() => {
|
|
416
|
+
expect(mockWebSocket.send).toHaveBeenCalled();
|
|
417
|
+
});
|
|
319
418
|
});
|
|
320
419
|
|
|
321
420
|
test("should send accumulated messages before a large message", async () => {
|
|
322
421
|
const { peer, mockWebSocket } = setup();
|
|
323
422
|
|
|
423
|
+
mockWebSocket.bufferedAmount = BUFFER_LIMIT + 1;
|
|
424
|
+
|
|
324
425
|
const smallMessage: SyncMessage = {
|
|
325
426
|
action: "known",
|
|
326
427
|
id: "co_z_small",
|
|
@@ -340,6 +441,8 @@ describe("createWebSocketPeer", () => {
|
|
|
340
441
|
void peer.outgoing.push(smallMessage);
|
|
341
442
|
void peer.outgoing.push(largeMessage);
|
|
342
443
|
|
|
444
|
+
mockWebSocket.bufferedAmount = 0;
|
|
445
|
+
|
|
343
446
|
await waitFor(() => {
|
|
344
447
|
expect(mockWebSocket.send).toHaveBeenCalledTimes(2);
|
|
345
448
|
});
|
|
@@ -359,10 +462,6 @@ describe("createWebSocketPeer", () => {
|
|
|
359
462
|
vi.useFakeTimers();
|
|
360
463
|
const { peer, mockWebSocket } = setup();
|
|
361
464
|
|
|
362
|
-
mockWebSocket.send.mockImplementation(() => {
|
|
363
|
-
mockWebSocket.bufferedAmount = BUFFER_LIMIT + 1;
|
|
364
|
-
});
|
|
365
|
-
|
|
366
465
|
const message1: SyncMessage = {
|
|
367
466
|
action: "known",
|
|
368
467
|
id: "co_ztest",
|
|
@@ -376,31 +475,25 @@ describe("createWebSocketPeer", () => {
|
|
|
376
475
|
priority: 6,
|
|
377
476
|
};
|
|
378
477
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
while (
|
|
382
|
-
serializeMessages(stream.concat(message1)).length <
|
|
383
|
-
MAX_OUTGOING_MESSAGES_CHUNK_BYTES
|
|
384
|
-
) {
|
|
385
|
-
stream.push(message1);
|
|
386
|
-
void peer.outgoing.push(message1);
|
|
387
|
-
}
|
|
478
|
+
// Start with buffer full so messages go through the queue
|
|
479
|
+
mockWebSocket.bufferedAmount = BUFFER_LIMIT + 1;
|
|
388
480
|
|
|
481
|
+
void peer.outgoing.push(message1);
|
|
389
482
|
void peer.outgoing.push(message2);
|
|
390
483
|
|
|
391
|
-
await vi.advanceTimersByTimeAsync(
|
|
484
|
+
await vi.advanceTimersByTimeAsync(0);
|
|
392
485
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
);
|
|
486
|
+
// No messages sent yet because buffer is full
|
|
487
|
+
expect(mockWebSocket.send).not.toHaveBeenCalled();
|
|
396
488
|
|
|
489
|
+
// Clear the buffer
|
|
397
490
|
mockWebSocket.bufferedAmount = 0;
|
|
398
491
|
|
|
399
492
|
await vi.advanceTimersByTimeAsync(BUFFER_LIMIT_POLLING_INTERVAL + 1);
|
|
400
493
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
JSON.stringify(
|
|
494
|
+
// Both messages are batched together
|
|
495
|
+
expect(mockWebSocket.send).toHaveBeenCalledWith(
|
|
496
|
+
[message1, message2].map((msg) => JSON.stringify(msg)).join("\n"),
|
|
404
497
|
);
|
|
405
498
|
|
|
406
499
|
vi.useRealTimers();
|
|
@@ -426,10 +519,15 @@ describe("createWebSocketPeer", () => {
|
|
|
426
519
|
};
|
|
427
520
|
|
|
428
521
|
void peer.outgoing.push(message1);
|
|
522
|
+
|
|
523
|
+
await waitFor(() => {
|
|
524
|
+
expect(mockWebSocket.send).toHaveBeenCalledTimes(1);
|
|
525
|
+
});
|
|
526
|
+
|
|
429
527
|
void peer.outgoing.push(message2);
|
|
430
528
|
|
|
431
529
|
await waitFor(() => {
|
|
432
|
-
expect(mockWebSocket.send).
|
|
530
|
+
expect(mockWebSocket.send).toHaveBeenCalledTimes(2);
|
|
433
531
|
});
|
|
434
532
|
|
|
435
533
|
expect(mockWebSocket.send).toHaveBeenNthCalledWith(
|
|
@@ -442,9 +540,10 @@ describe("createWebSocketPeer", () => {
|
|
|
442
540
|
);
|
|
443
541
|
});
|
|
444
542
|
|
|
445
|
-
test("should start batching outgoing messages when
|
|
446
|
-
const { peer, mockWebSocket,
|
|
543
|
+
test("should start batching outgoing messages when receiving a batched message", async () => {
|
|
544
|
+
const { peer, mockWebSocket, triggerEvent } = setup({
|
|
447
545
|
batchingByDefault: false,
|
|
546
|
+
initialReadyState: 0,
|
|
448
547
|
});
|
|
449
548
|
|
|
450
549
|
const message1: SyncMessage = {
|
|
@@ -454,9 +553,8 @@ describe("createWebSocketPeer", () => {
|
|
|
454
553
|
sessions: {},
|
|
455
554
|
};
|
|
456
555
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
messageHandler?.(
|
|
556
|
+
triggerEvent(
|
|
557
|
+
"message",
|
|
460
558
|
new MessageEvent("message", {
|
|
461
559
|
data: Array.from({ length: 5 }, () => message1)
|
|
462
560
|
.map((msg) => JSON.stringify(msg))
|
|
@@ -474,6 +572,10 @@ describe("createWebSocketPeer", () => {
|
|
|
474
572
|
void peer.outgoing.push(message1);
|
|
475
573
|
void peer.outgoing.push(message2);
|
|
476
574
|
|
|
575
|
+
// Simulate socket becoming ready
|
|
576
|
+
mockWebSocket.readyState = 1;
|
|
577
|
+
triggerEvent("open");
|
|
578
|
+
|
|
477
579
|
await waitFor(() => {
|
|
478
580
|
expect(mockWebSocket.send).toHaveBeenCalled();
|
|
479
581
|
});
|
|
@@ -483,8 +585,8 @@ describe("createWebSocketPeer", () => {
|
|
|
483
585
|
);
|
|
484
586
|
});
|
|
485
587
|
|
|
486
|
-
test("should not start batching outgoing messages when
|
|
487
|
-
const { peer, mockWebSocket,
|
|
588
|
+
test("should not start batching outgoing messages when receiving non-batched message", async () => {
|
|
589
|
+
const { peer, mockWebSocket, triggerEvent } = setup({
|
|
488
590
|
batchingByDefault: false,
|
|
489
591
|
});
|
|
490
592
|
|
|
@@ -495,9 +597,8 @@ describe("createWebSocketPeer", () => {
|
|
|
495
597
|
sessions: {},
|
|
496
598
|
};
|
|
497
599
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
messageHandler?.(
|
|
600
|
+
triggerEvent(
|
|
601
|
+
"message",
|
|
501
602
|
new MessageEvent("message", {
|
|
502
603
|
data: JSON.stringify(message1),
|
|
503
604
|
}),
|
|
@@ -511,10 +612,15 @@ describe("createWebSocketPeer", () => {
|
|
|
511
612
|
};
|
|
512
613
|
|
|
513
614
|
void peer.outgoing.push(message1);
|
|
615
|
+
|
|
616
|
+
await waitFor(() => {
|
|
617
|
+
expect(mockWebSocket.send).toHaveBeenCalledTimes(1);
|
|
618
|
+
});
|
|
619
|
+
|
|
514
620
|
void peer.outgoing.push(message2);
|
|
515
621
|
|
|
516
622
|
await waitFor(() => {
|
|
517
|
-
expect(mockWebSocket.send).
|
|
623
|
+
expect(mockWebSocket.send).toHaveBeenCalledTimes(2);
|
|
518
624
|
});
|
|
519
625
|
|
|
520
626
|
expect(mockWebSocket.send).toHaveBeenNthCalledWith(
|
|
@@ -550,14 +656,13 @@ describe("createWebSocketPeer", () => {
|
|
|
550
656
|
|
|
551
657
|
test("should correctly measure incoming ingress", async () => {
|
|
552
658
|
const metricReader = createTestMetricReader();
|
|
553
|
-
const {
|
|
659
|
+
const { triggerEvent } = setup({
|
|
554
660
|
meta: { label: "value" },
|
|
555
661
|
});
|
|
556
662
|
|
|
557
|
-
const messageHandler = listeners.get("message");
|
|
558
|
-
|
|
559
663
|
const encryptedChanges = "Hello, world!";
|
|
560
|
-
|
|
664
|
+
triggerEvent(
|
|
665
|
+
"message",
|
|
561
666
|
new MessageEvent("message", {
|
|
562
667
|
data: JSON.stringify({
|
|
563
668
|
action: "content",
|
|
@@ -585,7 +690,8 @@ describe("createWebSocketPeer", () => {
|
|
|
585
690
|
).toBe(encryptedChanges.length);
|
|
586
691
|
|
|
587
692
|
const trustingChanges = "Jazz is great!";
|
|
588
|
-
|
|
693
|
+
triggerEvent(
|
|
694
|
+
"message",
|
|
589
695
|
new MessageEvent("message", {
|
|
590
696
|
data: JSON.stringify({
|
|
591
697
|
action: "content",
|
|
@@ -611,9 +717,8 @@ describe("createWebSocketPeer", () => {
|
|
|
611
717
|
});
|
|
612
718
|
|
|
613
719
|
test("should drain the outgoing queue on websocket close so pulled equals pushed", async () => {
|
|
614
|
-
setOutgoingMessagesChunkDelay(500);
|
|
615
720
|
const metricReader = createTestMetricReader();
|
|
616
|
-
const { peer,
|
|
721
|
+
const { peer, triggerEvent } = setup({ initialReadyState: 0 });
|
|
617
722
|
|
|
618
723
|
const high: SyncMessage = {
|
|
619
724
|
action: "content",
|
|
@@ -657,12 +762,14 @@ describe("createWebSocketPeer", () => {
|
|
|
657
762
|
}),
|
|
658
763
|
).toBe(1);
|
|
659
764
|
|
|
765
|
+
// First message is already pulled by processQueue (waiting for socket open),
|
|
766
|
+
// so pulled count for that priority is already 1
|
|
660
767
|
expect(
|
|
661
768
|
await metricReader.getMetricValue("jazz.messagequeue.outgoing.pulled", {
|
|
662
769
|
priority: CO_VALUE_PRIORITY.HIGH,
|
|
663
770
|
peerRole: "client",
|
|
664
771
|
}),
|
|
665
|
-
).toBe(
|
|
772
|
+
).toBe(1);
|
|
666
773
|
expect(
|
|
667
774
|
await metricReader.getMetricValue("jazz.messagequeue.outgoing.pulled", {
|
|
668
775
|
priority: CO_VALUE_PRIORITY.MEDIUM,
|
|
@@ -676,9 +783,9 @@ describe("createWebSocketPeer", () => {
|
|
|
676
783
|
}),
|
|
677
784
|
).toBe(0);
|
|
678
785
|
|
|
679
|
-
|
|
680
|
-
closeHandler?.(new MessageEvent("close"));
|
|
786
|
+
triggerEvent("close");
|
|
681
787
|
|
|
788
|
+
// After close, drain() is called which pulls all remaining messages
|
|
682
789
|
expect(
|
|
683
790
|
await metricReader.getMetricValue("jazz.messagequeue.outgoing.pulled", {
|
|
684
791
|
priority: CO_VALUE_PRIORITY.HIGH,
|
|
@@ -697,8 +804,6 @@ describe("createWebSocketPeer", () => {
|
|
|
697
804
|
peerRole: "client",
|
|
698
805
|
}),
|
|
699
806
|
).toBe(1);
|
|
700
|
-
|
|
701
|
-
vi.useRealTimers();
|
|
702
807
|
});
|
|
703
808
|
});
|
|
704
809
|
});
|