@fluidframework/azure-end-to-end-tests 2.60.0 → 2.61.0-355516
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/lib/test/multiprocess/{childClient.js → childClient.tool.js} +24 -7
- package/lib/test/multiprocess/childClient.tool.js.map +1 -0
- package/lib/test/multiprocess/orchestratorUtils.js +35 -16
- package/lib/test/multiprocess/orchestratorUtils.js.map +1 -1
- package/lib/test/multiprocess/presenceTest.spec.js +214 -177
- package/lib/test/multiprocess/presenceTest.spec.js.map +1 -1
- package/package.json +26 -26
- package/src/test/.mocharc.cjs +0 -1
- package/src/test/multiprocess/{childClient.ts → childClient.tool.ts} +27 -6
- package/src/test/multiprocess/orchestratorUtils.ts +102 -31
- package/src/test/multiprocess/presenceTest.spec.ts +313 -265
- package/lib/test/multiprocess/childClient.js.map +0 -1
|
@@ -8,13 +8,14 @@ import type { ChildProcess } from "node:child_process";
|
|
|
8
8
|
import inspector from "node:inspector";
|
|
9
9
|
|
|
10
10
|
import type { AttendeeId } from "@fluidframework/presence/beta";
|
|
11
|
-
import { timeoutPromise } from "@fluidframework/test-utils/internal";
|
|
11
|
+
import { timeoutAwait, timeoutPromise } from "@fluidframework/test-utils/internal";
|
|
12
12
|
|
|
13
13
|
import type { MessageFromChild } from "./messageTypes.js";
|
|
14
14
|
import {
|
|
15
|
-
|
|
16
|
-
connectChildProcesses,
|
|
15
|
+
connectAndListenForAttendees,
|
|
17
16
|
connectAndWaitForAttendees,
|
|
17
|
+
connectChildProcesses,
|
|
18
|
+
forkChildProcesses,
|
|
18
19
|
getLatestMapValueResponses,
|
|
19
20
|
getLatestValueResponses,
|
|
20
21
|
registerWorkspaceOnChildren,
|
|
@@ -23,12 +24,17 @@ import {
|
|
|
23
24
|
waitForLatestValueUpdates,
|
|
24
25
|
} from "./orchestratorUtils.js";
|
|
25
26
|
|
|
27
|
+
const useAzure = process.env.FLUID_CLIENT === "azure";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Detects if the debugger is attached (when code loaded).
|
|
31
|
+
*/
|
|
26
32
|
const debuggerAttached = inspector.url() !== undefined;
|
|
27
33
|
|
|
28
34
|
/**
|
|
29
35
|
* Set this to a high number when debugging to avoid timeouts from debugging time.
|
|
30
36
|
*/
|
|
31
|
-
const timeoutMultiplier = debuggerAttached ? 1000 : 1;
|
|
37
|
+
const timeoutMultiplier = debuggerAttached ? 1000 : useAzure ? 3 : 1;
|
|
32
38
|
|
|
33
39
|
/**
|
|
34
40
|
* Sets the timeout for the given test context.
|
|
@@ -71,7 +77,7 @@ function setTimeout(context: Mocha.Context, duration: number): void {
|
|
|
71
77
|
* - Receive response messages from child clients to verify expected behavior.
|
|
72
78
|
* - Clean up child processes after each test.
|
|
73
79
|
*
|
|
74
|
-
* The child processes are located in the `childClient.ts` file. Each child process simulates a Fluid client.
|
|
80
|
+
* The child processes are located in the `childClient.tool.ts` file. Each child process simulates a Fluid client.
|
|
75
81
|
*
|
|
76
82
|
* The child client's job includes:
|
|
77
83
|
* - Create/Get + connect to Fluid container.
|
|
@@ -108,23 +114,11 @@ describe(`Presence with AzureClient`, () => {
|
|
|
108
114
|
/**
|
|
109
115
|
* Timeout for presence attendees to connect {@link AttendeeConnectedEvent}
|
|
110
116
|
*/
|
|
111
|
-
const
|
|
112
|
-
/**
|
|
113
|
-
* Timeout for workspace registration {@link WorkspaceRegisteredEvent}
|
|
114
|
-
*/
|
|
115
|
-
const workspaceRegisterTimeoutMs = 5000;
|
|
116
|
-
/**
|
|
117
|
-
* Timeout for presence update events {@link LatestMapValueUpdatedEvent} and {@link LatestValueUpdatedEvent}
|
|
118
|
-
*/
|
|
119
|
-
const stateUpdateTimeoutMs = 5000;
|
|
120
|
-
/**
|
|
121
|
-
* Timeout for {@link LatestMapValueGetResponseEvent} and {@link LatestValueGetResponseEvent}
|
|
122
|
-
*/
|
|
123
|
-
const getStateTimeoutMs = 5000;
|
|
117
|
+
const allAttendeesJoinedTimeoutMs = (1000 + 200 * numClients) * timeoutMultiplier;
|
|
124
118
|
|
|
125
119
|
for (const writeClients of [numClients, 1]) {
|
|
126
120
|
it(`announces 'attendeeConnected' when remote client joins session [${numClients} clients, ${writeClients} writers]`, async function () {
|
|
127
|
-
setTimeout(this, childConnectTimeoutMs +
|
|
121
|
+
setTimeout(this, childConnectTimeoutMs + allAttendeesJoinedTimeoutMs + 1000);
|
|
128
122
|
|
|
129
123
|
// Setup
|
|
130
124
|
const { children, childErrorPromise } = await forkChildProcesses(
|
|
@@ -139,7 +133,7 @@ describe(`Presence with AzureClient`, () => {
|
|
|
139
133
|
writeClients,
|
|
140
134
|
attendeeCountRequired: numClients - 1,
|
|
141
135
|
childConnectTimeoutMs,
|
|
142
|
-
|
|
136
|
+
allAttendeesJoinedTimeoutMs,
|
|
143
137
|
},
|
|
144
138
|
childErrorPromise,
|
|
145
139
|
);
|
|
@@ -157,7 +151,10 @@ describe(`Presence with AzureClient`, () => {
|
|
|
157
151
|
|
|
158
152
|
setTimeout(
|
|
159
153
|
this,
|
|
160
|
-
childConnectTimeoutMs +
|
|
154
|
+
childConnectTimeoutMs +
|
|
155
|
+
allAttendeesJoinedTimeoutMs +
|
|
156
|
+
childDisconnectTimeoutMs +
|
|
157
|
+
1000,
|
|
161
158
|
);
|
|
162
159
|
|
|
163
160
|
// Setup
|
|
@@ -166,16 +163,30 @@ describe(`Presence with AzureClient`, () => {
|
|
|
166
163
|
afterCleanUp,
|
|
167
164
|
);
|
|
168
165
|
|
|
169
|
-
const connectResult = await
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
166
|
+
const connectResult = await connectAndListenForAttendees(children, {
|
|
167
|
+
writeClients,
|
|
168
|
+
attendeeCountRequired: numClients - 1,
|
|
169
|
+
childConnectTimeoutMs,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Wait for all attendees to be fully joined
|
|
173
|
+
// Keep a tally for debuggability
|
|
174
|
+
let childrenFullyJoined = 0;
|
|
175
|
+
const allAttendeesFullyJoined = Promise.all(
|
|
176
|
+
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
|
177
|
+
connectResult.attendeeCountRequiredPromises.map((attendeeFullyJoinedPromise) =>
|
|
178
|
+
attendeeFullyJoinedPromise.then(() => childrenFullyJoined++),
|
|
179
|
+
),
|
|
178
180
|
);
|
|
181
|
+
await timeoutAwait(allAttendeesFullyJoined, {
|
|
182
|
+
durationMs: allAttendeesJoinedTimeoutMs,
|
|
183
|
+
errorMsg: "Not all attendees fully joined",
|
|
184
|
+
}).catch((error) => {
|
|
185
|
+
// Ideally this information would just be in the timeout error message, but that
|
|
186
|
+
// must be a resolved string (not dynamic). So, just log it separately.
|
|
187
|
+
testConsole.log(`${childrenFullyJoined} attendees fully joined before error...`);
|
|
188
|
+
throw error;
|
|
189
|
+
});
|
|
179
190
|
|
|
180
191
|
const waitForDisconnected = children.map(async (child, index) =>
|
|
181
192
|
index === 0
|
|
@@ -206,266 +217,303 @@ describe(`Presence with AzureClient`, () => {
|
|
|
206
217
|
await Promise.race([Promise.all(waitForDisconnected), childErrorPromise]);
|
|
207
218
|
});
|
|
208
219
|
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
{
|
|
223
|
+
/**
|
|
224
|
+
* Timeout for workspace registration {@link WorkspaceRegisteredEvent}
|
|
225
|
+
*/
|
|
226
|
+
const workspaceRegisterTimeoutMs = 5000;
|
|
227
|
+
/**
|
|
228
|
+
* Timeout for presence update events {@link LatestMapValueUpdatedEvent} and {@link LatestValueUpdatedEvent}
|
|
229
|
+
*/
|
|
230
|
+
const stateUpdateTimeoutMs = 5000;
|
|
231
|
+
/**
|
|
232
|
+
* Timeout for {@link LatestMapValueGetResponseEvent} and {@link LatestValueGetResponseEvent}
|
|
233
|
+
*/
|
|
234
|
+
const getStateTimeoutMs = 5000;
|
|
209
235
|
|
|
210
236
|
// This test suite focuses on the synchronization of Latest state between clients.
|
|
211
237
|
// NOTE: For testing purposes child clients will expect a Latest value of type string.
|
|
212
|
-
describe(`using Latest state object
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
workspaceId,
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
);
|
|
245
|
-
|
|
246
|
-
// Act - Trigger the update
|
|
247
|
-
children[0].send({
|
|
248
|
-
command: "setLatestValue",
|
|
249
|
-
workspaceId,
|
|
250
|
-
value: testValue,
|
|
238
|
+
describe(`using Latest state object`, () => {
|
|
239
|
+
for (const numClients of [5, 20]) {
|
|
240
|
+
assert(numClients > 1, "Must have at least two clients");
|
|
241
|
+
/**
|
|
242
|
+
* Timeout for child processes to connect to container ({@link ConnectedEvent})
|
|
243
|
+
*/
|
|
244
|
+
const childConnectTimeoutMs = 1000 * numClients * timeoutMultiplier;
|
|
245
|
+
|
|
246
|
+
let children: ChildProcess[];
|
|
247
|
+
let childErrorPromise: Promise<never>;
|
|
248
|
+
let containerCreatorAttendeeId: AttendeeId;
|
|
249
|
+
let attendeeIdPromises: Promise<AttendeeId>[];
|
|
250
|
+
let remoteClients: ChildProcess[];
|
|
251
|
+
const testValue = "testValue";
|
|
252
|
+
const workspaceId = "presenceTestWorkspace";
|
|
253
|
+
|
|
254
|
+
beforeEach(async () => {
|
|
255
|
+
({ children, childErrorPromise } = await forkChildProcesses(
|
|
256
|
+
numClients,
|
|
257
|
+
afterCleanUp,
|
|
258
|
+
));
|
|
259
|
+
({ containerCreatorAttendeeId, attendeeIdPromises } = await connectChildProcesses(
|
|
260
|
+
children,
|
|
261
|
+
{ writeClients: numClients, readyTimeoutMs: childConnectTimeoutMs },
|
|
262
|
+
));
|
|
263
|
+
await Promise.all(attendeeIdPromises);
|
|
264
|
+
remoteClients = children.filter((_, index) => index !== 0);
|
|
265
|
+
// NOTE: For testing purposes child clients will expect a Latest value of type string (StateFactory.latest<{ value: string }>).
|
|
266
|
+
await registerWorkspaceOnChildren(children, workspaceId, {
|
|
267
|
+
latest: true,
|
|
268
|
+
timeoutMs: workspaceRegisterTimeoutMs,
|
|
269
|
+
});
|
|
251
270
|
});
|
|
252
|
-
const updateEvents = await updateEventsPromise;
|
|
253
|
-
|
|
254
|
-
// Verify all events are from the expected attendee
|
|
255
|
-
for (const updateEvent of updateEvents) {
|
|
256
|
-
assert.strictEqual(updateEvent.attendeeId, containerCreatorAttendeeId);
|
|
257
|
-
assert.deepStrictEqual(updateEvent.value, testValue);
|
|
258
|
-
}
|
|
259
271
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
272
|
+
it(`allows clients to read Latest state from other clients [${numClients} clients]`, async () => {
|
|
273
|
+
// Setup
|
|
274
|
+
const updateEventsPromise = waitForLatestValueUpdates(
|
|
275
|
+
remoteClients,
|
|
276
|
+
workspaceId,
|
|
277
|
+
childErrorPromise,
|
|
278
|
+
stateUpdateTimeoutMs,
|
|
279
|
+
{ fromAttendeeId: containerCreatorAttendeeId, expectedValue: testValue },
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
// Act - Trigger the update
|
|
283
|
+
children[0].send({
|
|
284
|
+
command: "setLatestValue",
|
|
264
285
|
workspaceId,
|
|
265
|
-
|
|
286
|
+
value: testValue,
|
|
266
287
|
});
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
288
|
+
const updateEvents = await updateEventsPromise;
|
|
289
|
+
|
|
290
|
+
// Verify all events are from the expected attendee
|
|
291
|
+
for (const updateEvent of updateEvents) {
|
|
292
|
+
assert.strictEqual(updateEvent.attendeeId, containerCreatorAttendeeId);
|
|
293
|
+
assert.deepStrictEqual(updateEvent.value, testValue);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Act - Request each remote client to read latest state from container creator
|
|
297
|
+
for (const child of remoteClients) {
|
|
298
|
+
child.send({
|
|
299
|
+
command: "getLatestValue",
|
|
300
|
+
workspaceId,
|
|
301
|
+
attendeeId: containerCreatorAttendeeId,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const getResponses = await getLatestValueResponses(
|
|
306
|
+
remoteClients,
|
|
307
|
+
workspaceId,
|
|
308
|
+
childErrorPromise,
|
|
309
|
+
getStateTimeoutMs,
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
// Verify - all responses should contain the expected value
|
|
313
|
+
for (const getResponse of getResponses) {
|
|
314
|
+
assert.deepStrictEqual(getResponse.value, testValue);
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
}
|
|
281
318
|
});
|
|
282
319
|
|
|
283
320
|
// This test suite focuses on the synchronization of LatestMap state between clients.
|
|
284
321
|
// NOTE: For testing purposes child clients will expect a LatestMap value of type Record<string, string | number>.
|
|
285
|
-
describe(`using LatestMap state object
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
childErrorPromise,
|
|
321
|
-
stateUpdateTimeoutMs,
|
|
322
|
-
{ fromAttendeeId: containerCreatorAttendeeId, expectedValue: testValue },
|
|
323
|
-
);
|
|
324
|
-
|
|
325
|
-
// Act
|
|
326
|
-
children[0].send({
|
|
327
|
-
command: "setLatestMapValue",
|
|
328
|
-
workspaceId,
|
|
329
|
-
key: testKey,
|
|
330
|
-
value: testValue,
|
|
322
|
+
describe(`using LatestMap state object`, () => {
|
|
323
|
+
for (const numClients of [5, 20]) {
|
|
324
|
+
assert(numClients > 1, "Must have at least two clients");
|
|
325
|
+
/**
|
|
326
|
+
* Timeout for child processes to connect to container ({@link ConnectedEvent})
|
|
327
|
+
*/
|
|
328
|
+
const childConnectTimeoutMs = 1000 * numClients * timeoutMultiplier;
|
|
329
|
+
|
|
330
|
+
let children: ChildProcess[];
|
|
331
|
+
let childErrorPromise: Promise<never>;
|
|
332
|
+
let containerCreatorAttendeeId: AttendeeId;
|
|
333
|
+
let attendeeIdPromises: Promise<AttendeeId>[];
|
|
334
|
+
let remoteClients: ChildProcess[];
|
|
335
|
+
const workspaceId = "presenceTestWorkspace";
|
|
336
|
+
const key1 = "player1";
|
|
337
|
+
const key2 = "player2";
|
|
338
|
+
const value1 = { name: "Alice", score: 100 };
|
|
339
|
+
const value2 = { name: "Bob", score: 200 };
|
|
340
|
+
|
|
341
|
+
beforeEach(async () => {
|
|
342
|
+
({ children, childErrorPromise } = await forkChildProcesses(
|
|
343
|
+
numClients,
|
|
344
|
+
afterCleanUp,
|
|
345
|
+
));
|
|
346
|
+
({ containerCreatorAttendeeId, attendeeIdPromises } = await connectChildProcesses(
|
|
347
|
+
children,
|
|
348
|
+
{ writeClients: numClients, readyTimeoutMs: childConnectTimeoutMs },
|
|
349
|
+
));
|
|
350
|
+
await Promise.all(attendeeIdPromises);
|
|
351
|
+
remoteClients = children.filter((_, index) => index !== 0);
|
|
352
|
+
// NOTE: For testing purposes child clients will expect a LatestMap value of type Record<string, string | number> (StateFactory.latestMap<{ value: Record<string, string | number> }, string>).
|
|
353
|
+
await registerWorkspaceOnChildren(children, workspaceId, {
|
|
354
|
+
latestMap: true,
|
|
355
|
+
timeoutMs: workspaceRegisterTimeoutMs,
|
|
356
|
+
});
|
|
331
357
|
});
|
|
332
|
-
const updateEvents = await updateEventsPromise;
|
|
333
358
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
359
|
+
it(`allows clients to read LatestMap values from other clients [${numClients} clients]`, async () => {
|
|
360
|
+
// Setup
|
|
361
|
+
const testKey = "cursor";
|
|
362
|
+
const testValue = { x: 150, y: 300 };
|
|
363
|
+
const updateEventsPromise = waitForLatestMapValueUpdates(
|
|
364
|
+
remoteClients,
|
|
365
|
+
workspaceId,
|
|
366
|
+
testKey,
|
|
367
|
+
childErrorPromise,
|
|
368
|
+
stateUpdateTimeoutMs,
|
|
369
|
+
{ fromAttendeeId: containerCreatorAttendeeId, expectedValue: testValue },
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
// Act
|
|
373
|
+
children[0].send({
|
|
374
|
+
command: "setLatestMapValue",
|
|
344
375
|
workspaceId,
|
|
345
376
|
key: testKey,
|
|
346
|
-
|
|
377
|
+
value: testValue,
|
|
347
378
|
});
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
{ fromAttendeeId: attendee0Id, expectedValue: value1 },
|
|
378
|
-
);
|
|
379
|
-
const key2UpdateEventsPromise = waitForLatestMapValueUpdates(
|
|
380
|
-
key2Recipients,
|
|
381
|
-
workspaceId,
|
|
382
|
-
key2,
|
|
383
|
-
childErrorPromise,
|
|
384
|
-
stateUpdateTimeoutMs,
|
|
385
|
-
{ fromAttendeeId: attendee1Id, expectedValue: value2 },
|
|
386
|
-
);
|
|
387
|
-
|
|
388
|
-
// Act
|
|
389
|
-
children[0].send({
|
|
390
|
-
command: "setLatestMapValue",
|
|
391
|
-
workspaceId,
|
|
392
|
-
key: key1,
|
|
393
|
-
value: value1,
|
|
394
|
-
});
|
|
395
|
-
const key1UpdateEvents = await key1UpdateEventsPromise;
|
|
396
|
-
children[1].send({
|
|
397
|
-
command: "setLatestMapValue",
|
|
398
|
-
workspaceId,
|
|
399
|
-
key: key2,
|
|
400
|
-
value: value2,
|
|
379
|
+
const updateEvents = await updateEventsPromise;
|
|
380
|
+
|
|
381
|
+
// Check all events are from the expected attendee
|
|
382
|
+
for (const updateEvent of updateEvents) {
|
|
383
|
+
assert.strictEqual(updateEvent.attendeeId, containerCreatorAttendeeId);
|
|
384
|
+
assert.strictEqual(updateEvent.key, testKey);
|
|
385
|
+
assert.deepStrictEqual(updateEvent.value, testValue);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
for (const child of remoteClients) {
|
|
389
|
+
child.send({
|
|
390
|
+
command: "getLatestMapValue",
|
|
391
|
+
workspaceId,
|
|
392
|
+
key: testKey,
|
|
393
|
+
attendeeId: containerCreatorAttendeeId,
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
const getResponses = await getLatestMapValueResponses(
|
|
397
|
+
remoteClients,
|
|
398
|
+
workspaceId,
|
|
399
|
+
testKey,
|
|
400
|
+
childErrorPromise,
|
|
401
|
+
getStateTimeoutMs,
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
// Verify
|
|
405
|
+
for (const getResponse of getResponses) {
|
|
406
|
+
assert.deepStrictEqual(getResponse.value, testValue);
|
|
407
|
+
}
|
|
401
408
|
});
|
|
402
|
-
const key2UpdateEvents = await key2UpdateEventsPromise;
|
|
403
409
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
}
|
|
410
|
-
for (const updateEvent of key2UpdateEvents) {
|
|
411
|
-
assert.strictEqual(updateEvent.attendeeId, attendee1Id);
|
|
412
|
-
assert.strictEqual(updateEvent.key, key2);
|
|
413
|
-
assert.deepStrictEqual(updateEvent.value, value2);
|
|
414
|
-
}
|
|
410
|
+
it(`returns per-key values on read [${numClients} clients]`, async () => {
|
|
411
|
+
// Setup
|
|
412
|
+
const allAttendeeIds = await Promise.all(attendeeIdPromises);
|
|
413
|
+
const attendee0Id = containerCreatorAttendeeId;
|
|
414
|
+
const attendee1Id = allAttendeeIds[1];
|
|
415
415
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
416
|
+
const key1Recipients = children.filter((_, index) => index !== 0);
|
|
417
|
+
const key2Recipients = children.filter((_, index) => index !== 1);
|
|
418
|
+
const key1UpdateEventsPromise = waitForLatestMapValueUpdates(
|
|
419
|
+
key1Recipients,
|
|
420
|
+
workspaceId,
|
|
421
|
+
key1,
|
|
422
|
+
childErrorPromise,
|
|
423
|
+
stateUpdateTimeoutMs,
|
|
424
|
+
{ fromAttendeeId: attendee0Id, expectedValue: value1 },
|
|
425
|
+
);
|
|
426
|
+
const key2UpdateEventsPromise = waitForLatestMapValueUpdates(
|
|
427
|
+
key2Recipients,
|
|
428
|
+
workspaceId,
|
|
429
|
+
key2,
|
|
430
|
+
childErrorPromise,
|
|
431
|
+
stateUpdateTimeoutMs,
|
|
432
|
+
{ fromAttendeeId: attendee1Id, expectedValue: value2 },
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
// Act
|
|
436
|
+
children[0].send({
|
|
437
|
+
command: "setLatestMapValue",
|
|
420
438
|
workspaceId,
|
|
421
439
|
key: key1,
|
|
422
|
-
|
|
440
|
+
value: value1,
|
|
423
441
|
});
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
workspaceId,
|
|
428
|
-
key1,
|
|
429
|
-
childErrorPromise,
|
|
430
|
-
getStateTimeoutMs,
|
|
431
|
-
);
|
|
432
|
-
|
|
433
|
-
// Read key2 of attendee1 from all children
|
|
434
|
-
for (const child of children) {
|
|
435
|
-
child.send({
|
|
436
|
-
command: "getLatestMapValue",
|
|
442
|
+
const key1UpdateEvents = await key1UpdateEventsPromise;
|
|
443
|
+
children[1].send({
|
|
444
|
+
command: "setLatestMapValue",
|
|
437
445
|
workspaceId,
|
|
438
446
|
key: key2,
|
|
439
|
-
|
|
447
|
+
value: value2,
|
|
440
448
|
});
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
449
|
+
const key2UpdateEvents = await key2UpdateEventsPromise;
|
|
450
|
+
|
|
451
|
+
// Verify all events are from the expected attendees
|
|
452
|
+
for (const updateEvent of key1UpdateEvents) {
|
|
453
|
+
assert.strictEqual(updateEvent.attendeeId, attendee0Id);
|
|
454
|
+
assert.strictEqual(updateEvent.key, key1);
|
|
455
|
+
assert.deepStrictEqual(updateEvent.value, value1);
|
|
456
|
+
}
|
|
457
|
+
for (const updateEvent of key2UpdateEvents) {
|
|
458
|
+
assert.strictEqual(updateEvent.attendeeId, attendee1Id);
|
|
459
|
+
assert.strictEqual(updateEvent.key, key2);
|
|
460
|
+
assert.deepStrictEqual(updateEvent.value, value2);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Read key1 of attendee0 from all children
|
|
464
|
+
for (const child of children) {
|
|
465
|
+
child.send({
|
|
466
|
+
command: "getLatestMapValue",
|
|
467
|
+
workspaceId,
|
|
468
|
+
key: key1,
|
|
469
|
+
attendeeId: attendee0Id,
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
const key1Responses = await getLatestMapValueResponses(
|
|
473
|
+
children,
|
|
474
|
+
workspaceId,
|
|
475
|
+
key1,
|
|
476
|
+
childErrorPromise,
|
|
477
|
+
getStateTimeoutMs,
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
// Read key2 of attendee1 from all children
|
|
481
|
+
for (const child of children) {
|
|
482
|
+
child.send({
|
|
483
|
+
command: "getLatestMapValue",
|
|
484
|
+
workspaceId,
|
|
485
|
+
key: key2,
|
|
486
|
+
attendeeId: attendee1Id,
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
const key2Responses = await getLatestMapValueResponses(
|
|
490
|
+
children,
|
|
491
|
+
workspaceId,
|
|
492
|
+
key2,
|
|
493
|
+
childErrorPromise,
|
|
494
|
+
getStateTimeoutMs,
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
// Verify
|
|
498
|
+
assert.strictEqual(
|
|
499
|
+
key1Responses.length,
|
|
500
|
+
numClients,
|
|
501
|
+
"Expected responses from all clients for key1",
|
|
502
|
+
);
|
|
503
|
+
assert.strictEqual(
|
|
504
|
+
key2Responses.length,
|
|
505
|
+
numClients,
|
|
506
|
+
"Expected responses from all clients for key2",
|
|
507
|
+
);
|
|
508
|
+
|
|
509
|
+
for (const response of key1Responses) {
|
|
510
|
+
assert.deepStrictEqual(response.value, value1, "Key1 value should match");
|
|
511
|
+
}
|
|
512
|
+
for (const response of key2Responses) {
|
|
513
|
+
assert.deepStrictEqual(response.value, value2, "Key2 value should match");
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
}
|
|
469
517
|
});
|
|
470
518
|
}
|
|
471
519
|
});
|