cojson-transport-ws 0.8.12 → 0.8.16
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/CHANGELOG.md +160 -147
- package/dist/BatchedOutgoingMessages.js +3 -3
- package/dist/BatchedOutgoingMessages.js.map +1 -1
- package/dist/index.js +87 -40
- package/dist/index.js.map +1 -1
- package/dist/serialization.js +6 -0
- package/dist/serialization.js.map +1 -1
- package/dist/tests/BatchedOutgoingMessages.test.js +41 -11
- package/dist/tests/BatchedOutgoingMessages.test.js.map +1 -1
- package/dist/tests/createWebSocketPeer.test.js +12 -13
- package/dist/tests/createWebSocketPeer.test.js.map +1 -1
- package/package.json +5 -9
- package/src/BatchedOutgoingMessages.ts +36 -35
- package/src/index.ts +182 -122
- package/src/serialization.ts +26 -17
- package/src/tests/BatchedOutgoingMessages.test.ts +138 -106
- package/src/tests/createWebSocketPeer.test.ts +404 -408
- package/src/types.ts +19 -20
- package/tsconfig.json +2 -2
- package/.eslintrc.cjs +0 -24
- package/.prettierrc.js +0 -9
|
@@ -1,463 +1,459 @@
|
|
|
1
|
-
import { describe, test, expect, vi, Mocked } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
BUFFER_LIMIT,
|
|
4
|
-
BUFFER_LIMIT_POLLING_INTERVAL,
|
|
5
|
-
createWebSocketPeer,
|
|
6
|
-
CreateWebSocketPeerOpts,
|
|
7
|
-
} from "../index.js";
|
|
8
|
-
import { AnyWebSocket } from "../types.js";
|
|
9
1
|
import { SyncMessage } from "cojson";
|
|
10
2
|
import { Channel } from "cojson/src/streamUtils.ts";
|
|
3
|
+
import { Mocked, describe, expect, test, vi } from "vitest";
|
|
11
4
|
import { MAX_OUTGOING_MESSAGES_CHUNK_BYTES } from "../BatchedOutgoingMessages.js";
|
|
5
|
+
import {
|
|
6
|
+
BUFFER_LIMIT,
|
|
7
|
+
BUFFER_LIMIT_POLLING_INTERVAL,
|
|
8
|
+
CreateWebSocketPeerOpts,
|
|
9
|
+
createWebSocketPeer,
|
|
10
|
+
} from "../index.js";
|
|
11
|
+
import { AnyWebSocket } from "../types.js";
|
|
12
12
|
|
|
13
13
|
function setup(opts: Partial<CreateWebSocketPeerOpts> = {}) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
14
|
+
const listeners = new Map<string, (event: MessageEvent) => void>();
|
|
15
|
+
|
|
16
|
+
const mockWebSocket = {
|
|
17
|
+
readyState: 1,
|
|
18
|
+
addEventListener: vi.fn().mockImplementation((type, listener) => {
|
|
19
|
+
listeners.set(type, listener);
|
|
20
|
+
}),
|
|
21
|
+
removeEventListener: vi.fn().mockImplementation((type) => {
|
|
22
|
+
listeners.delete(type);
|
|
23
|
+
}),
|
|
24
|
+
close: vi.fn(),
|
|
25
|
+
send: vi.fn(),
|
|
26
|
+
} as unknown as Mocked<AnyWebSocket>;
|
|
27
|
+
|
|
28
|
+
const peer = createWebSocketPeer({
|
|
29
|
+
id: "test-peer",
|
|
30
|
+
websocket: mockWebSocket,
|
|
31
|
+
role: "client",
|
|
32
|
+
batchingByDefault: true,
|
|
33
|
+
...opts,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return { mockWebSocket, peer, listeners };
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
function serializeMessages(messages: SyncMessage[]) {
|
|
37
|
-
|
|
40
|
+
return messages.map((msg) => JSON.stringify(msg)).join("\n");
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
describe("createWebSocketPeer", () => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
test("should create a peer with correct properties", () => {
|
|
45
|
+
const { peer } = setup();
|
|
46
|
+
|
|
47
|
+
expect(peer).toHaveProperty("id", "test-peer");
|
|
48
|
+
expect(peer).toHaveProperty("incoming");
|
|
49
|
+
expect(peer).toHaveProperty("outgoing");
|
|
50
|
+
expect(peer).toHaveProperty("role", "client");
|
|
51
|
+
expect(peer).toHaveProperty("crashOnClose", false);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("should handle disconnection", async () => {
|
|
55
|
+
expect.assertions(1);
|
|
56
|
+
|
|
57
|
+
const { listeners, peer } = setup();
|
|
58
|
+
|
|
59
|
+
const incoming = peer.incoming as Channel<
|
|
60
|
+
SyncMessage | "Disconnected" | "PingTimeout"
|
|
61
|
+
>;
|
|
62
|
+
const pushSpy = vi.spyOn(incoming, "push");
|
|
63
|
+
|
|
64
|
+
const closeHandler = listeners.get("close");
|
|
65
|
+
|
|
66
|
+
closeHandler?.(new MessageEvent("close"));
|
|
67
|
+
|
|
68
|
+
expect(pushSpy).toHaveBeenCalledWith("Disconnected");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("should handle ping timeout", async () => {
|
|
72
|
+
vi.useFakeTimers();
|
|
73
|
+
const { listeners, peer } = setup();
|
|
74
|
+
|
|
75
|
+
const incoming = peer.incoming as Channel<
|
|
76
|
+
SyncMessage | "Disconnected" | "PingTimeout"
|
|
77
|
+
>;
|
|
78
|
+
const pushSpy = vi.spyOn(incoming, "push");
|
|
79
|
+
|
|
80
|
+
const messageHandler = listeners.get("message");
|
|
81
|
+
|
|
82
|
+
messageHandler?.(new MessageEvent("message", { data: "{}" }));
|
|
83
|
+
|
|
84
|
+
await vi.advanceTimersByTimeAsync(10_000);
|
|
85
|
+
|
|
86
|
+
expect(pushSpy).toHaveBeenCalledWith("PingTimeout");
|
|
87
|
+
|
|
88
|
+
vi.useRealTimers();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("should send outgoing messages", async () => {
|
|
92
|
+
const { peer, mockWebSocket } = setup();
|
|
93
|
+
|
|
94
|
+
const testMessage: SyncMessage = {
|
|
95
|
+
action: "known",
|
|
96
|
+
id: "co_ztest",
|
|
97
|
+
header: false,
|
|
98
|
+
sessions: {},
|
|
99
|
+
};
|
|
100
|
+
const promise = peer.outgoing.push(testMessage);
|
|
101
|
+
|
|
102
|
+
await waitFor(() => {
|
|
103
|
+
expect(mockWebSocket.send).toHaveBeenCalledWith(
|
|
104
|
+
JSON.stringify(testMessage),
|
|
105
|
+
);
|
|
49
106
|
});
|
|
50
107
|
|
|
51
|
-
|
|
52
|
-
|
|
108
|
+
await expect(promise).resolves.toBeUndefined();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("should stop sending messages when the websocket is closed", async () => {
|
|
112
|
+
const { peer, mockWebSocket } = setup();
|
|
53
113
|
|
|
54
|
-
|
|
114
|
+
mockWebSocket.send.mockImplementation(() => {
|
|
115
|
+
mockWebSocket.readyState = 0;
|
|
116
|
+
});
|
|
55
117
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
118
|
+
const message1: SyncMessage = {
|
|
119
|
+
action: "known",
|
|
120
|
+
id: "co_ztest",
|
|
121
|
+
header: false,
|
|
122
|
+
sessions: {},
|
|
123
|
+
};
|
|
60
124
|
|
|
61
|
-
|
|
125
|
+
const message2: SyncMessage = {
|
|
126
|
+
action: "content",
|
|
127
|
+
id: "co_zlow",
|
|
128
|
+
new: {},
|
|
129
|
+
priority: 1,
|
|
130
|
+
};
|
|
62
131
|
|
|
63
|
-
|
|
132
|
+
void peer.outgoing.push(message1);
|
|
64
133
|
|
|
65
|
-
|
|
134
|
+
await waitFor(() => {
|
|
135
|
+
expect(mockWebSocket.send).toHaveBeenCalled();
|
|
66
136
|
});
|
|
67
137
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
138
|
+
expect(mockWebSocket.send).toHaveBeenCalledWith(JSON.stringify(message1));
|
|
139
|
+
|
|
140
|
+
mockWebSocket.send.mockClear();
|
|
141
|
+
void peer.outgoing.push(message2);
|
|
142
|
+
|
|
143
|
+
await new Promise<void>((resolve) => setTimeout(resolve, 100));
|
|
144
|
+
|
|
145
|
+
expect(mockWebSocket.send).not.toHaveBeenCalled();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test("should close the websocket connection", () => {
|
|
149
|
+
const { mockWebSocket, peer } = setup();
|
|
71
150
|
|
|
72
|
-
|
|
73
|
-
SyncMessage | "Disconnected" | "PingTimeout"
|
|
74
|
-
>;
|
|
75
|
-
const pushSpy = vi.spyOn(incoming, "push");
|
|
151
|
+
peer.outgoing.close();
|
|
76
152
|
|
|
77
|
-
|
|
153
|
+
expect(mockWebSocket.close).toHaveBeenCalled();
|
|
154
|
+
});
|
|
78
155
|
|
|
79
|
-
|
|
156
|
+
describe("batchingByDefault = true", () => {
|
|
157
|
+
test("should batch outgoing messages", async () => {
|
|
158
|
+
const { peer, mockWebSocket } = setup();
|
|
80
159
|
|
|
81
|
-
|
|
160
|
+
mockWebSocket.send.mockImplementation(() => {
|
|
161
|
+
mockWebSocket.readyState = 0;
|
|
162
|
+
});
|
|
82
163
|
|
|
83
|
-
|
|
164
|
+
const message1: SyncMessage = {
|
|
165
|
+
action: "known",
|
|
166
|
+
id: "co_ztest",
|
|
167
|
+
header: false,
|
|
168
|
+
sessions: {},
|
|
169
|
+
};
|
|
84
170
|
|
|
85
|
-
|
|
171
|
+
const message2: SyncMessage = {
|
|
172
|
+
action: "content",
|
|
173
|
+
id: "co_zlow",
|
|
174
|
+
new: {},
|
|
175
|
+
priority: 1,
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
void peer.outgoing.push(message1);
|
|
179
|
+
void peer.outgoing.push(message2);
|
|
180
|
+
|
|
181
|
+
await waitFor(() => {
|
|
182
|
+
expect(mockWebSocket.send).toHaveBeenCalled();
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
expect(mockWebSocket.send).toHaveBeenCalledWith(
|
|
186
|
+
[message1, message2].map((msg) => JSON.stringify(msg)).join("\n"),
|
|
187
|
+
);
|
|
86
188
|
});
|
|
87
189
|
|
|
88
|
-
test("should send
|
|
89
|
-
|
|
190
|
+
test("should send all the pending messages when the websocket is closed", async () => {
|
|
191
|
+
const { peer, mockWebSocket } = setup();
|
|
90
192
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const promise = peer.outgoing.push(testMessage);
|
|
193
|
+
const message1: SyncMessage = {
|
|
194
|
+
action: "known",
|
|
195
|
+
id: "co_ztest",
|
|
196
|
+
header: false,
|
|
197
|
+
sessions: {},
|
|
198
|
+
};
|
|
98
199
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
200
|
+
const message2: SyncMessage = {
|
|
201
|
+
action: "content",
|
|
202
|
+
id: "co_zlow",
|
|
203
|
+
new: {},
|
|
204
|
+
priority: 1,
|
|
205
|
+
};
|
|
104
206
|
|
|
105
|
-
|
|
207
|
+
void peer.outgoing.push(message1);
|
|
208
|
+
void peer.outgoing.push(message2);
|
|
209
|
+
|
|
210
|
+
peer.outgoing.close();
|
|
211
|
+
|
|
212
|
+
expect(mockWebSocket.send).toHaveBeenCalledWith(
|
|
213
|
+
[message1, message2].map((msg) => JSON.stringify(msg)).join("\n"),
|
|
214
|
+
);
|
|
106
215
|
});
|
|
107
216
|
|
|
108
|
-
test("should
|
|
109
|
-
|
|
217
|
+
test("should limit the chunk size to MAX_OUTGOING_MESSAGES_CHUNK_SIZE", async () => {
|
|
218
|
+
const { peer, mockWebSocket } = setup();
|
|
219
|
+
|
|
220
|
+
const message1: SyncMessage = {
|
|
221
|
+
action: "known",
|
|
222
|
+
id: "co_ztest",
|
|
223
|
+
header: false,
|
|
224
|
+
sessions: {},
|
|
225
|
+
};
|
|
226
|
+
const message2: SyncMessage = {
|
|
227
|
+
action: "content",
|
|
228
|
+
id: "co_zlow",
|
|
229
|
+
new: {},
|
|
230
|
+
priority: 1,
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const stream: SyncMessage[] = [];
|
|
234
|
+
|
|
235
|
+
while (
|
|
236
|
+
serializeMessages(stream.concat(message1)).length <
|
|
237
|
+
MAX_OUTGOING_MESSAGES_CHUNK_BYTES
|
|
238
|
+
) {
|
|
239
|
+
stream.push(message1);
|
|
240
|
+
void peer.outgoing.push(message1);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
void peer.outgoing.push(message2);
|
|
110
244
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
245
|
+
await waitFor(() => {
|
|
246
|
+
expect(mockWebSocket.send).toHaveBeenCalledTimes(2);
|
|
247
|
+
});
|
|
114
248
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
header: false,
|
|
119
|
-
sessions: {},
|
|
120
|
-
};
|
|
249
|
+
expect(mockWebSocket.send).toHaveBeenCalledWith(
|
|
250
|
+
serializeMessages(stream),
|
|
251
|
+
);
|
|
121
252
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
};
|
|
253
|
+
expect(mockWebSocket.send).toHaveBeenNthCalledWith(
|
|
254
|
+
2,
|
|
255
|
+
JSON.stringify(message2),
|
|
256
|
+
);
|
|
257
|
+
});
|
|
128
258
|
|
|
259
|
+
test("should wait for the buffer to be under BUFFER_LIMIT before sending more messages", async () => {
|
|
260
|
+
vi.useFakeTimers();
|
|
261
|
+
const { peer, mockWebSocket } = setup();
|
|
262
|
+
|
|
263
|
+
mockWebSocket.send.mockImplementation(() => {
|
|
264
|
+
mockWebSocket.bufferedAmount = BUFFER_LIMIT + 1;
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
const message1: SyncMessage = {
|
|
268
|
+
action: "known",
|
|
269
|
+
id: "co_ztest",
|
|
270
|
+
header: false,
|
|
271
|
+
sessions: {},
|
|
272
|
+
};
|
|
273
|
+
const message2: SyncMessage = {
|
|
274
|
+
action: "content",
|
|
275
|
+
id: "co_zlow",
|
|
276
|
+
new: {},
|
|
277
|
+
priority: 1,
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
const stream: SyncMessage[] = [];
|
|
281
|
+
|
|
282
|
+
while (
|
|
283
|
+
serializeMessages(stream.concat(message1)).length <
|
|
284
|
+
MAX_OUTGOING_MESSAGES_CHUNK_BYTES
|
|
285
|
+
) {
|
|
286
|
+
stream.push(message1);
|
|
129
287
|
void peer.outgoing.push(message1);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
void peer.outgoing.push(message2);
|
|
291
|
+
|
|
292
|
+
await vi.advanceTimersByTimeAsync(100);
|
|
130
293
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
294
|
+
expect(mockWebSocket.send).toHaveBeenCalledWith(
|
|
295
|
+
serializeMessages(stream),
|
|
296
|
+
);
|
|
134
297
|
|
|
135
|
-
|
|
298
|
+
mockWebSocket.bufferedAmount = 0;
|
|
136
299
|
|
|
137
|
-
|
|
138
|
-
void peer.outgoing.push(message2);
|
|
300
|
+
await vi.advanceTimersByTimeAsync(BUFFER_LIMIT_POLLING_INTERVAL + 1);
|
|
139
301
|
|
|
140
|
-
|
|
302
|
+
expect(mockWebSocket.send).toHaveBeenNthCalledWith(
|
|
303
|
+
2,
|
|
304
|
+
JSON.stringify(message2),
|
|
305
|
+
);
|
|
141
306
|
|
|
142
|
-
|
|
307
|
+
vi.useRealTimers();
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
describe("batchingByDefault = false", () => {
|
|
312
|
+
test("should not batch outgoing messages", async () => {
|
|
313
|
+
const { peer, mockWebSocket } = setup({ batchingByDefault: false });
|
|
314
|
+
|
|
315
|
+
const message1: SyncMessage = {
|
|
316
|
+
action: "known",
|
|
317
|
+
id: "co_ztest",
|
|
318
|
+
header: false,
|
|
319
|
+
sessions: {},
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const message2: SyncMessage = {
|
|
323
|
+
action: "content",
|
|
324
|
+
id: "co_zlow",
|
|
325
|
+
new: {},
|
|
326
|
+
priority: 1,
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
void peer.outgoing.push(message1);
|
|
330
|
+
void peer.outgoing.push(message2);
|
|
331
|
+
|
|
332
|
+
await waitFor(() => {
|
|
333
|
+
expect(mockWebSocket.send).toHaveBeenCalled();
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
expect(mockWebSocket.send).toHaveBeenNthCalledWith(
|
|
337
|
+
1,
|
|
338
|
+
JSON.stringify(message1),
|
|
339
|
+
);
|
|
340
|
+
expect(mockWebSocket.send).toHaveBeenNthCalledWith(
|
|
341
|
+
2,
|
|
342
|
+
JSON.stringify(message2),
|
|
343
|
+
);
|
|
143
344
|
});
|
|
144
345
|
|
|
145
|
-
test("should
|
|
146
|
-
|
|
346
|
+
test("should start batching outgoing messages when reiceving a batched message", async () => {
|
|
347
|
+
const { peer, mockWebSocket, listeners } = setup({
|
|
348
|
+
batchingByDefault: false,
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
const message1: SyncMessage = {
|
|
352
|
+
action: "known",
|
|
353
|
+
id: "co_ztest",
|
|
354
|
+
header: false,
|
|
355
|
+
sessions: {},
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
const messageHandler = listeners.get("message");
|
|
359
|
+
|
|
360
|
+
messageHandler?.(
|
|
361
|
+
new MessageEvent("message", {
|
|
362
|
+
data: Array.from({ length: 5 }, () => message1)
|
|
363
|
+
.map((msg) => JSON.stringify(msg))
|
|
364
|
+
.join("\n"),
|
|
365
|
+
}),
|
|
366
|
+
);
|
|
147
367
|
|
|
148
|
-
|
|
368
|
+
const message2: SyncMessage = {
|
|
369
|
+
action: "content",
|
|
370
|
+
id: "co_zlow",
|
|
371
|
+
new: {},
|
|
372
|
+
priority: 1,
|
|
373
|
+
};
|
|
149
374
|
|
|
150
|
-
|
|
151
|
-
|
|
375
|
+
void peer.outgoing.push(message1);
|
|
376
|
+
void peer.outgoing.push(message2);
|
|
152
377
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const message1: SyncMessage = {
|
|
162
|
-
action: "known",
|
|
163
|
-
id: "co_ztest",
|
|
164
|
-
header: false,
|
|
165
|
-
sessions: {},
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
const message2: SyncMessage = {
|
|
169
|
-
action: "content",
|
|
170
|
-
id: "co_zlow",
|
|
171
|
-
new: {},
|
|
172
|
-
priority: 1,
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
void peer.outgoing.push(message1);
|
|
176
|
-
void peer.outgoing.push(message2);
|
|
177
|
-
|
|
178
|
-
await waitFor(() => {
|
|
179
|
-
expect(mockWebSocket.send).toHaveBeenCalled();
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
expect(mockWebSocket.send).toHaveBeenCalledWith(
|
|
183
|
-
[message1, message2]
|
|
184
|
-
.map((msg) => JSON.stringify(msg))
|
|
185
|
-
.join("\n"),
|
|
186
|
-
);
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
test("should send all the pending messages when the websocket is closed", async () => {
|
|
190
|
-
const { peer, mockWebSocket } = setup();
|
|
191
|
-
|
|
192
|
-
const message1: SyncMessage = {
|
|
193
|
-
action: "known",
|
|
194
|
-
id: "co_ztest",
|
|
195
|
-
header: false,
|
|
196
|
-
sessions: {},
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
const message2: SyncMessage = {
|
|
200
|
-
action: "content",
|
|
201
|
-
id: "co_zlow",
|
|
202
|
-
new: {},
|
|
203
|
-
priority: 1,
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
void peer.outgoing.push(message1);
|
|
207
|
-
void peer.outgoing.push(message2);
|
|
208
|
-
|
|
209
|
-
peer.outgoing.close();
|
|
210
|
-
|
|
211
|
-
expect(mockWebSocket.send).toHaveBeenCalledWith(
|
|
212
|
-
[message1, message2]
|
|
213
|
-
.map((msg) => JSON.stringify(msg))
|
|
214
|
-
.join("\n"),
|
|
215
|
-
);
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
test("should limit the chunk size to MAX_OUTGOING_MESSAGES_CHUNK_SIZE", async () => {
|
|
219
|
-
const { peer, mockWebSocket } = setup();
|
|
220
|
-
|
|
221
|
-
const message1: SyncMessage = {
|
|
222
|
-
action: "known",
|
|
223
|
-
id: "co_ztest",
|
|
224
|
-
header: false,
|
|
225
|
-
sessions: {},
|
|
226
|
-
};
|
|
227
|
-
const message2: SyncMessage = {
|
|
228
|
-
action: "content",
|
|
229
|
-
id: "co_zlow",
|
|
230
|
-
new: {},
|
|
231
|
-
priority: 1,
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
const stream: SyncMessage[] = [];
|
|
235
|
-
|
|
236
|
-
while (serializeMessages(stream.concat(message1)).length < MAX_OUTGOING_MESSAGES_CHUNK_BYTES) {
|
|
237
|
-
stream.push(message1);
|
|
238
|
-
void peer.outgoing.push(message1);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
void peer.outgoing.push(message2);
|
|
242
|
-
|
|
243
|
-
await waitFor(() => {
|
|
244
|
-
expect(mockWebSocket.send).toHaveBeenCalledTimes(2);
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
expect(mockWebSocket.send).toHaveBeenCalledWith(
|
|
248
|
-
serializeMessages(stream),
|
|
249
|
-
);
|
|
250
|
-
|
|
251
|
-
expect(mockWebSocket.send).toHaveBeenNthCalledWith(
|
|
252
|
-
2,
|
|
253
|
-
JSON.stringify(message2),
|
|
254
|
-
);
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
test("should wait for the buffer to be under BUFFER_LIMIT before sending more messages", async () => {
|
|
258
|
-
vi.useFakeTimers();
|
|
259
|
-
const { peer, mockWebSocket } = setup();
|
|
260
|
-
|
|
261
|
-
mockWebSocket.send.mockImplementation(() => {
|
|
262
|
-
mockWebSocket.bufferedAmount = BUFFER_LIMIT + 1;
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
const message1: SyncMessage = {
|
|
266
|
-
action: "known",
|
|
267
|
-
id: "co_ztest",
|
|
268
|
-
header: false,
|
|
269
|
-
sessions: {},
|
|
270
|
-
};
|
|
271
|
-
const message2: SyncMessage = {
|
|
272
|
-
action: "content",
|
|
273
|
-
id: "co_zlow",
|
|
274
|
-
new: {},
|
|
275
|
-
priority: 1,
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
const stream: SyncMessage[] = [];
|
|
279
|
-
|
|
280
|
-
while (serializeMessages(stream.concat(message1)).length < MAX_OUTGOING_MESSAGES_CHUNK_BYTES) {
|
|
281
|
-
stream.push(message1);
|
|
282
|
-
void peer.outgoing.push(message1);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
void peer.outgoing.push(message2);
|
|
286
|
-
|
|
287
|
-
await vi.advanceTimersByTimeAsync(100);
|
|
288
|
-
|
|
289
|
-
expect(mockWebSocket.send).toHaveBeenCalledWith(
|
|
290
|
-
serializeMessages(stream),
|
|
291
|
-
);
|
|
292
|
-
|
|
293
|
-
mockWebSocket.bufferedAmount = 0;
|
|
294
|
-
|
|
295
|
-
await vi.advanceTimersByTimeAsync(
|
|
296
|
-
BUFFER_LIMIT_POLLING_INTERVAL + 1,
|
|
297
|
-
);
|
|
298
|
-
|
|
299
|
-
expect(mockWebSocket.send).toHaveBeenNthCalledWith(
|
|
300
|
-
2,
|
|
301
|
-
JSON.stringify(message2),
|
|
302
|
-
);
|
|
303
|
-
|
|
304
|
-
vi.useRealTimers();
|
|
305
|
-
});
|
|
378
|
+
await waitFor(() => {
|
|
379
|
+
expect(mockWebSocket.send).toHaveBeenCalled();
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
expect(mockWebSocket.send).toHaveBeenCalledWith(
|
|
383
|
+
[message1, message2].map((msg) => JSON.stringify(msg)).join("\n"),
|
|
384
|
+
);
|
|
306
385
|
});
|
|
307
386
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
id: "co_ztest",
|
|
351
|
-
header: false,
|
|
352
|
-
sessions: {},
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
const messageHandler = listeners.get("message");
|
|
356
|
-
|
|
357
|
-
messageHandler?.(
|
|
358
|
-
new MessageEvent("message", {
|
|
359
|
-
data: Array.from(
|
|
360
|
-
{ length: 5 },
|
|
361
|
-
() => message1,
|
|
362
|
-
)
|
|
363
|
-
.map((msg) => JSON.stringify(msg))
|
|
364
|
-
.join("\n"),
|
|
365
|
-
}),
|
|
366
|
-
);
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
const message2: SyncMessage = {
|
|
370
|
-
action: "content",
|
|
371
|
-
id: "co_zlow",
|
|
372
|
-
new: {},
|
|
373
|
-
priority: 1,
|
|
374
|
-
};
|
|
375
|
-
|
|
376
|
-
void peer.outgoing.push(message1);
|
|
377
|
-
void peer.outgoing.push(message2);
|
|
378
|
-
|
|
379
|
-
await waitFor(() => {
|
|
380
|
-
expect(mockWebSocket.send).toHaveBeenCalled();
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
expect(mockWebSocket.send).toHaveBeenCalledWith(
|
|
384
|
-
[message1, message2]
|
|
385
|
-
.map((msg) => JSON.stringify(msg))
|
|
386
|
-
.join("\n"),
|
|
387
|
-
);
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
test("should not start batching outgoing messages when reiceving non-batched message", async () => {
|
|
391
|
-
const { peer, mockWebSocket, listeners } = setup({
|
|
392
|
-
batchingByDefault: false,
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
const message1: SyncMessage = {
|
|
396
|
-
action: "known",
|
|
397
|
-
id: "co_ztest",
|
|
398
|
-
header: false,
|
|
399
|
-
sessions: {},
|
|
400
|
-
};
|
|
401
|
-
|
|
402
|
-
const messageHandler = listeners.get("message");
|
|
403
|
-
|
|
404
|
-
messageHandler?.(
|
|
405
|
-
new MessageEvent("message", {
|
|
406
|
-
data: JSON.stringify(message1),
|
|
407
|
-
}),
|
|
408
|
-
);
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
const message2: SyncMessage = {
|
|
412
|
-
action: "content",
|
|
413
|
-
id: "co_zlow",
|
|
414
|
-
new: {},
|
|
415
|
-
priority: 1,
|
|
416
|
-
};
|
|
417
|
-
|
|
418
|
-
void peer.outgoing.push(message1);
|
|
419
|
-
void peer.outgoing.push(message2);
|
|
420
|
-
|
|
421
|
-
await waitFor(() => {
|
|
422
|
-
expect(mockWebSocket.send).toHaveBeenCalled();
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
expect(mockWebSocket.send).toHaveBeenNthCalledWith(
|
|
426
|
-
1,
|
|
427
|
-
JSON.stringify(message1),
|
|
428
|
-
);
|
|
429
|
-
expect(mockWebSocket.send).toHaveBeenNthCalledWith(
|
|
430
|
-
2,
|
|
431
|
-
JSON.stringify(message2),
|
|
432
|
-
);
|
|
433
|
-
});
|
|
387
|
+
test("should not start batching outgoing messages when reiceving non-batched message", async () => {
|
|
388
|
+
const { peer, mockWebSocket, listeners } = setup({
|
|
389
|
+
batchingByDefault: false,
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
const message1: SyncMessage = {
|
|
393
|
+
action: "known",
|
|
394
|
+
id: "co_ztest",
|
|
395
|
+
header: false,
|
|
396
|
+
sessions: {},
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
const messageHandler = listeners.get("message");
|
|
400
|
+
|
|
401
|
+
messageHandler?.(
|
|
402
|
+
new MessageEvent("message", {
|
|
403
|
+
data: JSON.stringify(message1),
|
|
404
|
+
}),
|
|
405
|
+
);
|
|
406
|
+
|
|
407
|
+
const message2: SyncMessage = {
|
|
408
|
+
action: "content",
|
|
409
|
+
id: "co_zlow",
|
|
410
|
+
new: {},
|
|
411
|
+
priority: 1,
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
void peer.outgoing.push(message1);
|
|
415
|
+
void peer.outgoing.push(message2);
|
|
416
|
+
|
|
417
|
+
await waitFor(() => {
|
|
418
|
+
expect(mockWebSocket.send).toHaveBeenCalled();
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
expect(mockWebSocket.send).toHaveBeenNthCalledWith(
|
|
422
|
+
1,
|
|
423
|
+
JSON.stringify(message1),
|
|
424
|
+
);
|
|
425
|
+
expect(mockWebSocket.send).toHaveBeenNthCalledWith(
|
|
426
|
+
2,
|
|
427
|
+
JSON.stringify(message2),
|
|
428
|
+
);
|
|
434
429
|
});
|
|
430
|
+
});
|
|
435
431
|
});
|
|
436
432
|
|
|
437
433
|
function waitFor(callback: () => boolean | void) {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
434
|
+
return new Promise<void>((resolve, reject) => {
|
|
435
|
+
const checkPassed = () => {
|
|
436
|
+
try {
|
|
437
|
+
return { ok: callback(), error: null };
|
|
438
|
+
} catch (error) {
|
|
439
|
+
return { ok: false, error };
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
let retries = 0;
|
|
444
|
+
|
|
445
|
+
const interval = setInterval(() => {
|
|
446
|
+
const { ok, error } = checkPassed();
|
|
447
|
+
|
|
448
|
+
if (ok !== false) {
|
|
449
|
+
clearInterval(interval);
|
|
450
|
+
resolve();
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (++retries > 10) {
|
|
454
|
+
clearInterval(interval);
|
|
455
|
+
reject(error);
|
|
456
|
+
}
|
|
457
|
+
}, 100);
|
|
458
|
+
});
|
|
463
459
|
}
|