@fluidframework/azure-end-to-end-tests 2.70.0-361788 → 2.70.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/CHANGELOG.md +4 -0
- package/lib/test/multiprocess/childClient.tool.js +8 -0
- package/lib/test/multiprocess/childClient.tool.js.map +1 -1
- package/lib/test/multiprocess/presenceTest.spec.js +140 -145
- package/lib/test/multiprocess/presenceTest.spec.js.map +1 -1
- package/package.json +24 -23
- package/src/test/multiprocess/childClient.tool.ts +9 -0
- package/src/test/multiprocess/presenceTest.spec.ts +197 -200
|
@@ -25,6 +25,14 @@ import {
|
|
|
25
25
|
waitForLatestValueUpdates,
|
|
26
26
|
} from "./orchestratorUtils.js";
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* When true, slower (long running time) tests will be run.
|
|
30
|
+
* Otherwise, those test will not appear. Console output is used to show that
|
|
31
|
+
* they exist. (They could be skipped, though skipped test are often an
|
|
32
|
+
* indication of a problem.)
|
|
33
|
+
*/
|
|
34
|
+
const shouldRunScaleTests = process.env.FLUID_TEST_SCALE !== undefined;
|
|
35
|
+
|
|
28
36
|
const useAzure = process.env.FLUID_CLIENT === "azure";
|
|
29
37
|
|
|
30
38
|
/**
|
|
@@ -98,215 +106,212 @@ describe(`Presence with AzureClient`, () => {
|
|
|
98
106
|
afterCleanUp.length = 0;
|
|
99
107
|
});
|
|
100
108
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Timeout for presence attendees to join per first child perspective {@link AttendeeConnectedEvent}
|
|
112
|
-
*/
|
|
113
|
-
const allAttendeesJoinedTimeoutMs = (1000 + 200 * numClients) * timeoutMultiplier;
|
|
114
|
-
/**
|
|
115
|
-
* Timeout for presence attendees to fully join (everyone knows about everyone) {@link AttendeeConnectedEvent}
|
|
116
|
-
*/
|
|
117
|
-
const allAttendeesFullyJoinedTimeoutMs = (2000 + 300 * numClients) * timeoutMultiplier;
|
|
109
|
+
describe("`attendees` support", () => {
|
|
110
|
+
const numClientsForAttendeeTests = [5, 40, 100, 250];
|
|
111
|
+
for (const numClients of numClientsForAttendeeTests) {
|
|
112
|
+
if (numClients > 50 && !shouldRunScaleTests) {
|
|
113
|
+
testConsole.log(
|
|
114
|
+
`skipping Presence attendee scale tests with ${numClients} clients (set FLUID_TEST_SCALE=true to run)`,
|
|
115
|
+
);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
118
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
119
|
+
assert(numClients > 1, "Must have at least two clients");
|
|
120
|
+
/**
|
|
121
|
+
* Timeout for child processes to connect to container ({@link ConnectedEvent})
|
|
122
|
+
*/
|
|
123
|
+
const childConnectTimeoutMs = 1000 * numClients * timeoutMultiplier;
|
|
124
|
+
/**
|
|
125
|
+
* Timeout for presence attendees to join per first child perspective {@link AttendeeConnectedEvent}
|
|
126
|
+
*/
|
|
127
|
+
const allAttendeesJoinedTimeoutMs = (1000 + 200 * numClients) * timeoutMultiplier;
|
|
128
|
+
/**
|
|
129
|
+
* Timeout for presence attendees to fully join (everyone knows about everyone) {@link AttendeeConnectedEvent}
|
|
130
|
+
*/
|
|
131
|
+
const allAttendeesFullyJoinedTimeoutMs = (2000 + 300 * numClients) * timeoutMultiplier;
|
|
132
|
+
|
|
133
|
+
for (const writeClients of [numClients, 1]) {
|
|
134
|
+
it(`announces 'attendeeConnected' when remote client joins session [${numClients} clients, ${writeClients} writers]`, async function () {
|
|
135
|
+
setTestTimeout(this, childConnectTimeoutMs + allAttendeesJoinedTimeoutMs + 1000);
|
|
125
136
|
|
|
126
|
-
|
|
137
|
+
// Setup
|
|
138
|
+
const { children, childErrorPromise } = await forkChildProcesses(
|
|
139
|
+
numClients,
|
|
140
|
+
afterCleanUp,
|
|
141
|
+
);
|
|
127
142
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
143
|
+
// Further Setup with Act and Verify
|
|
144
|
+
await connectAndWaitForAttendees(
|
|
145
|
+
children,
|
|
146
|
+
{
|
|
147
|
+
writeClients,
|
|
148
|
+
attendeeCountRequired: numClients - 1,
|
|
149
|
+
childConnectTimeoutMs,
|
|
150
|
+
allAttendeesJoinedTimeoutMs,
|
|
151
|
+
},
|
|
152
|
+
childErrorPromise,
|
|
153
|
+
);
|
|
154
|
+
});
|
|
133
155
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
156
|
+
it(`announces 'attendeeDisconnected' when remote client disconnects [${numClients} clients, ${writeClients} writers]`, async function () {
|
|
157
|
+
if (useAzure && numClients > 50) {
|
|
158
|
+
// Even with increased timeouts, more than 50 clients can be too large for AFR.
|
|
159
|
+
// This may be due to slow responses/inactivity from the clients that are
|
|
160
|
+
// creating pressure on ADO agent.
|
|
161
|
+
this.skip();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const childDisconnectTimeoutMs = 10_000 * timeoutMultiplier;
|
|
165
|
+
|
|
166
|
+
setTestTimeout(
|
|
167
|
+
this,
|
|
168
|
+
childConnectTimeoutMs +
|
|
169
|
+
allAttendeesFullyJoinedTimeoutMs +
|
|
170
|
+
childDisconnectTimeoutMs +
|
|
171
|
+
1000,
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
// Setup
|
|
175
|
+
const { children, childErrorPromise } = await forkChildProcesses(
|
|
176
|
+
numClients,
|
|
177
|
+
afterCleanUp,
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
const startConnectAndFullJoin = performance.now();
|
|
181
|
+
const connectResult = await connectAndListenForAttendees(children, {
|
|
138
182
|
writeClients,
|
|
139
183
|
attendeeCountRequired: numClients - 1,
|
|
140
184
|
childConnectTimeoutMs,
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
it.skip(`announces 'attendeeDisconnected' when remote client disconnects [${numClients} clients, ${writeClients} writers]`, async function () {
|
|
149
|
-
// TODO: AB#45620: "Presence: perf: update Join pattern for scale" can handle
|
|
150
|
-
// larger counts of read-only attendees. Without protocol changes tests with
|
|
151
|
-
// 20+ attendees exceed current limits.
|
|
152
|
-
if (numClients >= 20 && writeClients === 1) {
|
|
153
|
-
this.skip();
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (useAzure && numClients > 50) {
|
|
157
|
-
// Even with increased timeouts, more than 50 clients can be too large for AFR.
|
|
158
|
-
// This may be due to slow responses/inactivity from the clients that are
|
|
159
|
-
// creating pressure on ADO agent.
|
|
160
|
-
this.skip();
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const childDisconnectTimeoutMs = 10_000 * timeoutMultiplier;
|
|
164
|
-
|
|
165
|
-
setTestTimeout(
|
|
166
|
-
this,
|
|
167
|
-
childConnectTimeoutMs +
|
|
168
|
-
allAttendeesFullyJoinedTimeoutMs +
|
|
169
|
-
childDisconnectTimeoutMs +
|
|
170
|
-
1000,
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
// Setup
|
|
174
|
-
const { children, childErrorPromise } = await forkChildProcesses(
|
|
175
|
-
numClients,
|
|
176
|
-
afterCleanUp,
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
const startConnectAndFullJoin = performance.now();
|
|
180
|
-
const connectResult = await connectAndListenForAttendees(children, {
|
|
181
|
-
writeClients,
|
|
182
|
-
attendeeCountRequired: numClients - 1,
|
|
183
|
-
childConnectTimeoutMs,
|
|
184
|
-
});
|
|
185
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
186
|
-
connectResult.attendeeCountRequiredPromises[0].then(() =>
|
|
187
|
-
testConsole.log(
|
|
188
|
-
`[${new Date().toISOString()}] All attendees joined per child 0 after ${performance.now() - startConnectAndFullJoin}ms`,
|
|
189
|
-
),
|
|
190
|
-
);
|
|
185
|
+
});
|
|
186
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
187
|
+
connectResult.attendeeCountRequiredPromises[0].then(() =>
|
|
188
|
+
testConsole.log(
|
|
189
|
+
`[${new Date().toISOString()}] All attendees joined per child 0 after ${performance.now() - startConnectAndFullJoin}ms`,
|
|
190
|
+
),
|
|
191
|
+
);
|
|
191
192
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
);
|
|
208
|
-
let timedout = true;
|
|
209
|
-
const allFullyJoinedOrChildError = Promise.race([
|
|
210
|
-
allAttendeesFullyJoined,
|
|
211
|
-
childErrorPromise,
|
|
212
|
-
]).finally(() => (timedout = false));
|
|
213
|
-
await timeoutAwait(allFullyJoinedOrChildError, {
|
|
214
|
-
durationMs: allAttendeesFullyJoinedTimeoutMs,
|
|
215
|
-
errorMsg: "Not all attendees fully joined",
|
|
216
|
-
}).catch(async (error) => {
|
|
217
|
-
// Ideally this information would just be in the timeout error message, but that
|
|
218
|
-
// must be a resolved string (not dynamic). So, just log it separately.
|
|
219
|
-
testConsole.log(
|
|
220
|
-
`[${new Date().toISOString()}] ${childrenFullyJoined} attendees fully joined before error...`,
|
|
193
|
+
// Wait for all attendees to be fully joined
|
|
194
|
+
// Keep a tally for debuggability
|
|
195
|
+
let childrenFullyJoined = 0;
|
|
196
|
+
const setNotFullyJoined = new Set<number>();
|
|
197
|
+
for (let i = 0; i < children.length; i++) {
|
|
198
|
+
setNotFullyJoined.add(i);
|
|
199
|
+
}
|
|
200
|
+
const allAttendeesFullyJoined = Promise.all(
|
|
201
|
+
connectResult.attendeeCountRequiredPromises.map(
|
|
202
|
+
async (attendeeFullyJoinedPromise, index) => {
|
|
203
|
+
await attendeeFullyJoinedPromise;
|
|
204
|
+
childrenFullyJoined++;
|
|
205
|
+
setNotFullyJoined.delete(index);
|
|
206
|
+
},
|
|
207
|
+
),
|
|
221
208
|
);
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
209
|
+
let timedout = true;
|
|
210
|
+
const allFullyJoinedOrChildError = Promise.race([
|
|
211
|
+
allAttendeesFullyJoined,
|
|
212
|
+
childErrorPromise,
|
|
213
|
+
]).finally(() => (timedout = false));
|
|
214
|
+
await timeoutAwait(allFullyJoinedOrChildError, {
|
|
215
|
+
durationMs: allAttendeesFullyJoinedTimeoutMs,
|
|
216
|
+
errorMsg: "Not all attendees fully joined",
|
|
217
|
+
}).catch(async (error) => {
|
|
218
|
+
// Ideally this information would just be in the timeout error message, but that
|
|
219
|
+
// must be a resolved string (not dynamic). So, just log it separately.
|
|
220
|
+
testConsole.log(
|
|
221
|
+
`[${new Date().toISOString()}] ${childrenFullyJoined} attendees fully joined before error...`,
|
|
222
|
+
);
|
|
223
|
+
if (timedout) {
|
|
224
|
+
// Gather additional timing data if timed out to understand what increased
|
|
225
|
+
// timeout could work. Test will still fail if this secondary wait succeeds.
|
|
226
|
+
const startAdditionalWait = performance.now();
|
|
227
|
+
try {
|
|
228
|
+
await timeoutAwait(allFullyJoinedOrChildError, {
|
|
229
|
+
durationMs: allAttendeesFullyJoinedTimeoutMs,
|
|
230
|
+
});
|
|
231
|
+
testConsole.log(
|
|
232
|
+
`[${new Date().toISOString()}] All attendees fully joined after additional wait (${performance.now() - startAdditionalWait}ms)`,
|
|
233
|
+
);
|
|
234
|
+
} catch (secondaryError) {
|
|
235
|
+
testConsole.log(
|
|
236
|
+
`[${new Date().toISOString()}] Secondary await resulted in`,
|
|
237
|
+
secondaryError,
|
|
238
|
+
);
|
|
239
|
+
}
|
|
238
240
|
}
|
|
239
|
-
}
|
|
240
241
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
242
|
+
// Gather and report debug info from children
|
|
243
|
+
// If there are less than 10 children, get all reports.
|
|
244
|
+
// Otherwise, just child 0 and those not fully joined.
|
|
245
|
+
setTestTimeout(this, 0); // Disable test timeout. Will throw within 20s below.
|
|
246
|
+
const childrenRequestedToReport =
|
|
247
|
+
children.length <= 10
|
|
248
|
+
? children
|
|
249
|
+
: // Just those not fully joined
|
|
250
|
+
children.filter((_, index) => index === 0 || setNotFullyJoined.has(index));
|
|
251
|
+
await timeoutAwait(
|
|
252
|
+
Promise.race([
|
|
253
|
+
executeDebugReports(childrenRequestedToReport),
|
|
254
|
+
childErrorPromise,
|
|
255
|
+
]),
|
|
256
|
+
{ durationMs: 20_000, errorMsg: "Debug report timeout" },
|
|
257
|
+
).catch((debugAwaitError) => {
|
|
258
|
+
testConsole.error("Debug report await resulted in", debugAwaitError);
|
|
259
|
+
});
|
|
256
260
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
261
|
+
throw error;
|
|
262
|
+
});
|
|
263
|
+
testConsole.log(
|
|
264
|
+
`[${new Date().toISOString()}] All attendees fully joined after ${performance.now() - startConnectAndFullJoin}ms`,
|
|
265
|
+
);
|
|
262
266
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
267
|
+
let child0ReportRequested = false;
|
|
268
|
+
const waitForDisconnected = children.map(async (child, index) =>
|
|
269
|
+
index === 0
|
|
270
|
+
? Promise.resolve()
|
|
271
|
+
: timeoutPromise(
|
|
272
|
+
(resolve) => {
|
|
273
|
+
child.on("message", (msg: MessageFromChild) => {
|
|
274
|
+
if (
|
|
275
|
+
msg.event === "attendeeDisconnected" &&
|
|
276
|
+
msg.attendeeId === connectResult.containerCreatorAttendeeId
|
|
277
|
+
) {
|
|
278
|
+
console.log(`Child[${index}] saw creator disconnect`);
|
|
279
|
+
resolve();
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
durationMs: childDisconnectTimeoutMs,
|
|
285
|
+
errorMsg: `Attendee[${index}] Disconnected Timeout`,
|
|
286
|
+
},
|
|
287
|
+
).catch(async (error) => {
|
|
288
|
+
const childrenRequestedToReport = [child];
|
|
289
|
+
if (!child0ReportRequested) {
|
|
290
|
+
childrenRequestedToReport.unshift(children[0]);
|
|
291
|
+
child0ReportRequested = true;
|
|
292
|
+
}
|
|
293
|
+
await timeoutAwait(
|
|
294
|
+
Promise.race([
|
|
295
|
+
executeDebugReports(childrenRequestedToReport),
|
|
296
|
+
childErrorPromise,
|
|
297
|
+
]),
|
|
298
|
+
{ durationMs: 20_000, errorMsg: "Debug report timeout" },
|
|
299
|
+
).catch((debugAwaitError) => {
|
|
300
|
+
testConsole.error("Debug report await resulted in", debugAwaitError);
|
|
277
301
|
});
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
errorMsg: `Attendee[${index}] Disconnected Timeout`,
|
|
282
|
-
},
|
|
283
|
-
).catch(async (error) => {
|
|
284
|
-
const childrenRequestedToReport = [child];
|
|
285
|
-
if (!child0ReportRequested) {
|
|
286
|
-
childrenRequestedToReport.unshift(children[0]);
|
|
287
|
-
child0ReportRequested = true;
|
|
288
|
-
}
|
|
289
|
-
await timeoutAwait(
|
|
290
|
-
Promise.race([
|
|
291
|
-
executeDebugReports(childrenRequestedToReport),
|
|
292
|
-
childErrorPromise,
|
|
293
|
-
]),
|
|
294
|
-
{ durationMs: 20_000, errorMsg: "Debug report timeout" },
|
|
295
|
-
).catch((debugAwaitError) => {
|
|
296
|
-
testConsole.error("Debug report await resulted in", debugAwaitError);
|
|
297
|
-
});
|
|
298
|
-
throw error;
|
|
299
|
-
}),
|
|
300
|
-
);
|
|
302
|
+
throw error;
|
|
303
|
+
}),
|
|
304
|
+
);
|
|
301
305
|
|
|
302
|
-
|
|
303
|
-
|
|
306
|
+
// Act - disconnect first child process
|
|
307
|
+
children[0].send({ command: "disconnectSelf" });
|
|
304
308
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
309
|
+
// Verify - wait for all 'attendeeDisconnected' events
|
|
310
|
+
await Promise.race([Promise.all(waitForDisconnected), childErrorPromise]);
|
|
311
|
+
});
|
|
312
|
+
}
|
|
308
313
|
}
|
|
309
|
-
}
|
|
314
|
+
});
|
|
310
315
|
|
|
311
316
|
{
|
|
312
317
|
/**
|
|
@@ -359,10 +364,6 @@ describe(`Presence with AzureClient`, () => {
|
|
|
359
364
|
});
|
|
360
365
|
|
|
361
366
|
it(`allows clients to read Latest state from other clients [${numClients} clients]`, async function () {
|
|
362
|
-
// AB#48866: Fix intermittently failing presence tests
|
|
363
|
-
if (useAzure) {
|
|
364
|
-
this.skip();
|
|
365
|
-
}
|
|
366
367
|
// Setup
|
|
367
368
|
const updateEventsPromise = waitForLatestValueUpdates(
|
|
368
369
|
remoteClients,
|
|
@@ -501,10 +502,6 @@ describe(`Presence with AzureClient`, () => {
|
|
|
501
502
|
});
|
|
502
503
|
|
|
503
504
|
it(`returns per-key values on read [${numClients} clients]`, async function () {
|
|
504
|
-
// AB#48866: Fix intermittently failing presence tests
|
|
505
|
-
if (useAzure) {
|
|
506
|
-
this.skip();
|
|
507
|
-
}
|
|
508
505
|
// Setup
|
|
509
506
|
const allAttendeeIds = await Promise.all(attendeeIdPromises);
|
|
510
507
|
const attendee0Id = containerCreatorAttendeeId;
|