@fluidframework/azure-end-to-end-tests 2.70.0 → 2.72.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/.eslintrc.cjs +2 -2
- package/CHANGELOG.md +8 -4
- package/lib/test/AzureClientFactory.js.map +1 -1
- package/lib/test/TestDataObject.js +1 -1
- package/lib/test/TestDataObject.js.map +1 -1
- package/lib/test/containerCreate.spec.js +1 -1
- package/lib/test/containerCreate.spec.js.map +1 -1
- package/lib/test/multiprocess/childClient.tool.js +22 -11
- package/lib/test/multiprocess/childClient.tool.js.map +1 -1
- package/lib/test/multiprocess/messageTypes.js.map +1 -1
- package/lib/test/multiprocess/orchestratorUtils.js +15 -4
- package/lib/test/multiprocess/orchestratorUtils.js.map +1 -1
- package/lib/test/multiprocess/presenceTest.spec.js +181 -157
- package/lib/test/multiprocess/presenceTest.spec.js.map +1 -1
- package/package.json +27 -27
- package/src/test/AzureClientFactory.ts +1 -1
- package/src/test/TestDataObject.ts +1 -1
- package/src/test/containerCreate.spec.ts +1 -1
- package/src/test/multiprocess/childClient.tool.ts +25 -13
- package/src/test/multiprocess/messageTypes.ts +2 -1
- package/src/test/multiprocess/orchestratorUtils.ts +14 -1
- package/src/test/multiprocess/presenceTest.spec.ts +270 -233
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/azure-end-to-end-tests",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.72.0",
|
|
4
4
|
"description": "Azure client end to end tests",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -33,29 +33,29 @@
|
|
|
33
33
|
"temp-directory": "nyc/.nyc_output"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@fluid-experimental/data-objects": "~2.
|
|
37
|
-
"@fluid-internal/client-utils": "~2.
|
|
38
|
-
"@fluid-internal/mocha-test-setup": "~2.
|
|
39
|
-
"@fluid-private/test-version-utils": "~2.
|
|
40
|
-
"@fluidframework/aqueduct": "~2.
|
|
41
|
-
"@fluidframework/azure-client": "~2.
|
|
36
|
+
"@fluid-experimental/data-objects": "~2.72.0",
|
|
37
|
+
"@fluid-internal/client-utils": "~2.72.0",
|
|
38
|
+
"@fluid-internal/mocha-test-setup": "~2.72.0",
|
|
39
|
+
"@fluid-private/test-version-utils": "~2.72.0",
|
|
40
|
+
"@fluidframework/aqueduct": "~2.72.0",
|
|
41
|
+
"@fluidframework/azure-client": "~2.72.0",
|
|
42
42
|
"@fluidframework/azure-client-legacy": "npm:@fluidframework/azure-client@^1.2.0",
|
|
43
|
-
"@fluidframework/container-definitions": "~2.
|
|
44
|
-
"@fluidframework/container-loader": "~2.
|
|
45
|
-
"@fluidframework/core-interfaces": "~2.
|
|
46
|
-
"@fluidframework/counter": "~2.
|
|
47
|
-
"@fluidframework/datastore-definitions": "~2.
|
|
48
|
-
"@fluidframework/fluid-static": "~2.
|
|
49
|
-
"@fluidframework/map": "~2.
|
|
43
|
+
"@fluidframework/container-definitions": "~2.72.0",
|
|
44
|
+
"@fluidframework/container-loader": "~2.72.0",
|
|
45
|
+
"@fluidframework/core-interfaces": "~2.72.0",
|
|
46
|
+
"@fluidframework/counter": "~2.72.0",
|
|
47
|
+
"@fluidframework/datastore-definitions": "~2.72.0",
|
|
48
|
+
"@fluidframework/fluid-static": "~2.72.0",
|
|
49
|
+
"@fluidframework/map": "~2.72.0",
|
|
50
50
|
"@fluidframework/map-legacy": "npm:@fluidframework/map@^1.4.0",
|
|
51
|
-
"@fluidframework/matrix": "~2.
|
|
52
|
-
"@fluidframework/presence": "~2.
|
|
53
|
-
"@fluidframework/runtime-definitions": "~2.
|
|
54
|
-
"@fluidframework/sequence": "~2.
|
|
55
|
-
"@fluidframework/telemetry-utils": "~2.
|
|
56
|
-
"@fluidframework/test-runtime-utils": "~2.
|
|
57
|
-
"@fluidframework/test-utils": "~2.
|
|
58
|
-
"@fluidframework/tree": "~2.
|
|
51
|
+
"@fluidframework/matrix": "~2.72.0",
|
|
52
|
+
"@fluidframework/presence": "~2.72.0",
|
|
53
|
+
"@fluidframework/runtime-definitions": "~2.72.0",
|
|
54
|
+
"@fluidframework/sequence": "~2.72.0",
|
|
55
|
+
"@fluidframework/telemetry-utils": "~2.72.0",
|
|
56
|
+
"@fluidframework/test-runtime-utils": "~2.72.0",
|
|
57
|
+
"@fluidframework/test-utils": "~2.72.0",
|
|
58
|
+
"@fluidframework/tree": "~2.72.0",
|
|
59
59
|
"axios": "^1.8.4",
|
|
60
60
|
"cross-env": "^7.0.3",
|
|
61
61
|
"mocha": "^10.8.2",
|
|
@@ -68,9 +68,9 @@
|
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@biomejs/biome": "~1.9.3",
|
|
70
70
|
"@fluidframework/build-common": "^2.0.3",
|
|
71
|
-
"@fluidframework/build-tools": "^0.
|
|
72
|
-
"@fluidframework/driver-definitions": "~2.
|
|
73
|
-
"@fluidframework/eslint-config-fluid": "
|
|
71
|
+
"@fluidframework/build-tools": "^0.60.0",
|
|
72
|
+
"@fluidframework/driver-definitions": "~2.72.0",
|
|
73
|
+
"@fluidframework/eslint-config-fluid": "~2.72.0",
|
|
74
74
|
"@types/mocha": "^10.0.10",
|
|
75
75
|
"@types/nock": "^9.3.0",
|
|
76
76
|
"@types/node": "^18.19.0",
|
|
@@ -100,8 +100,8 @@
|
|
|
100
100
|
"check:biome": "biome check .",
|
|
101
101
|
"check:format": "npm run check:biome",
|
|
102
102
|
"clean": "rimraf --glob dist lib \"**/*.tsbuildinfo\" \"**/*.build.log\" nyc",
|
|
103
|
-
"eslint": "eslint --format stylish src",
|
|
104
|
-
"eslint:fix": "eslint --format stylish src --fix --fix-type problem,suggestion,layout",
|
|
103
|
+
"eslint": "eslint --quiet --format stylish src",
|
|
104
|
+
"eslint:fix": "eslint --quiet --format stylish src --fix --fix-type problem,suggestion,layout",
|
|
105
105
|
"format": "npm run format:biome",
|
|
106
106
|
"format:biome": "biome check . --write",
|
|
107
107
|
"lint": "fluid-build . --task lint",
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
type AzureRemoteConnectionConfig,
|
|
10
10
|
type ITelemetryBaseLogger,
|
|
11
11
|
} from "@fluidframework/azure-client";
|
|
12
|
-
// eslint-disable-next-line import/no-internal-modules -- TODO consider a test exposure to avoid /internal
|
|
12
|
+
// eslint-disable-next-line import-x/no-internal-modules -- TODO consider a test exposure to avoid /internal
|
|
13
13
|
import type { AzureClientPropsInternal } from "@fluidframework/azure-client/internal";
|
|
14
14
|
import {
|
|
15
15
|
AzureClient as AzureClientLegacy,
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import type { SignalListener } from "@fluid-experimental/data-objects";
|
|
7
7
|
import { EventEmitter } from "@fluid-internal/client-utils";
|
|
8
|
-
// eslint-disable-next-line import/no-internal-modules -- TODO consider a test exposure to avoid /internal
|
|
8
|
+
// eslint-disable-next-line import-x/no-internal-modules -- TODO consider a test exposure to avoid /internal
|
|
9
9
|
import { createDataObjectKind } from "@fluidframework/aqueduct/internal";
|
|
10
10
|
import {
|
|
11
11
|
DataObject,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
MessageType,
|
|
15
15
|
type ISequencedDocumentMessage,
|
|
16
16
|
} from "@fluidframework/driver-definitions/legacy";
|
|
17
|
-
// eslint-disable-next-line import/no-internal-modules -- TODO consider a test exposure to avoid /internal
|
|
17
|
+
// eslint-disable-next-line import-x/no-internal-modules -- TODO consider a test exposure to avoid /internal
|
|
18
18
|
import { isTreeContainerSchema } from "@fluidframework/fluid-static/internal";
|
|
19
19
|
import {
|
|
20
20
|
type ContainerSchema,
|
|
@@ -40,10 +40,10 @@ import type {
|
|
|
40
40
|
EventEntry,
|
|
41
41
|
} from "./messageTypes.js";
|
|
42
42
|
|
|
43
|
-
const
|
|
43
|
+
const testLabel = process.argv[2];
|
|
44
44
|
// Identifier given to child process
|
|
45
|
-
const process_id = process.argv[
|
|
46
|
-
const verbosity = process.argv[
|
|
45
|
+
const process_id = process.argv[3];
|
|
46
|
+
const verbosity = process.argv[4] ?? "";
|
|
47
47
|
|
|
48
48
|
const useAzure = process.env.FLUID_CLIENT === "azure";
|
|
49
49
|
const tenantId = useAzure
|
|
@@ -61,6 +61,10 @@ const containerSchema = {
|
|
|
61
61
|
},
|
|
62
62
|
} as const satisfies ContainerSchema;
|
|
63
63
|
|
|
64
|
+
function log(...data: unknown[]): void {
|
|
65
|
+
console.log(`[${testLabel}] [${new Date().toISOString()}] [${process_id}]`, ...data);
|
|
66
|
+
}
|
|
67
|
+
|
|
64
68
|
function telemetryEventInterestLevel(eventName: string): "none" | "basic" | "details" {
|
|
65
69
|
if (eventName.includes(":Signal") || eventName.includes(":Join")) {
|
|
66
70
|
return "details";
|
|
@@ -82,7 +86,7 @@ function selectiveVerboseLog(event: ITelemetryBaseEvent, logLevel?: LogLevel): v
|
|
|
82
86
|
if (interest === "details") {
|
|
83
87
|
content.details = event.details;
|
|
84
88
|
}
|
|
85
|
-
|
|
89
|
+
log(`[${logLevel ?? LogLevel.default}]`, content);
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
/**
|
|
@@ -95,6 +99,7 @@ const getOrCreateContainer = async (params: {
|
|
|
95
99
|
user: UserIdAndName;
|
|
96
100
|
scopes?: ScopeType[];
|
|
97
101
|
createScopes?: ScopeType[];
|
|
102
|
+
connectTimeoutMs: number;
|
|
98
103
|
}): Promise<{
|
|
99
104
|
container: IFluidContainer<typeof containerSchema>;
|
|
100
105
|
services: AzureContainerServices;
|
|
@@ -104,7 +109,7 @@ const getOrCreateContainer = async (params: {
|
|
|
104
109
|
}> => {
|
|
105
110
|
let container: IFluidContainer<typeof containerSchema>;
|
|
106
111
|
let { containerId } = params;
|
|
107
|
-
const { logger, onDisconnected, user, scopes, createScopes } = params;
|
|
112
|
+
const { logger, onDisconnected, user, scopes, createScopes, connectTimeoutMs } = params;
|
|
108
113
|
const connectionProps: AzureRemoteConnectionConfig | AzureLocalConnectionConfig = useAzure
|
|
109
114
|
? {
|
|
110
115
|
tenantId,
|
|
@@ -163,7 +168,7 @@ function createSendFunction(): (msg: MessageToParent) => void {
|
|
|
163
168
|
const sendFn = process.send.bind(process);
|
|
164
169
|
if (verbosity.includes("msgs")) {
|
|
165
170
|
return (msg: MessageToParent) => {
|
|
166
|
-
|
|
171
|
+
log(`Sending`, msg);
|
|
167
172
|
sendFn(msg);
|
|
168
173
|
};
|
|
169
174
|
}
|
|
@@ -248,11 +253,17 @@ class MessageHandler {
|
|
|
248
253
|
|
|
249
254
|
private readonly logger: ITelemetryBaseLogger = {
|
|
250
255
|
send: (event: ITelemetryBaseEvent, logLevel?: LogLevel) => {
|
|
256
|
+
// Filter out non-interactive client telemetry
|
|
257
|
+
const clientType = event.clientType;
|
|
258
|
+
if (typeof clientType === "string" && clientType.startsWith("noninteractive")) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
251
262
|
// Special case unexpected telemetry event
|
|
252
263
|
if (event.eventName.endsWith(":JoinResponseWhenAlone")) {
|
|
253
264
|
this.send({
|
|
254
265
|
event: "error",
|
|
255
|
-
error: `Unexpected ClientJoin response. Details: ${JSON.stringify(event.details)}`,
|
|
266
|
+
error: `Unexpected ClientJoin response. Details: ${JSON.stringify(event.details)}\nLog: ${JSON.stringify(this.log)}`,
|
|
256
267
|
});
|
|
257
268
|
// Keep going
|
|
258
269
|
}
|
|
@@ -374,7 +385,7 @@ class MessageHandler {
|
|
|
374
385
|
eventCategory: "messageReceived",
|
|
375
386
|
eventName: msg.command,
|
|
376
387
|
});
|
|
377
|
-
|
|
388
|
+
log(`Received`, msg);
|
|
378
389
|
}
|
|
379
390
|
|
|
380
391
|
if (msg.command === "ping") {
|
|
@@ -471,7 +482,10 @@ class MessageHandler {
|
|
|
471
482
|
this.presence = presence;
|
|
472
483
|
|
|
473
484
|
// wait for 'ConnectionState.Connected'
|
|
474
|
-
await connected
|
|
485
|
+
await connected.catch((error) => {
|
|
486
|
+
(error as Error).message += `\nLog: ${JSON.stringify(this.log)}`;
|
|
487
|
+
throw error;
|
|
488
|
+
});
|
|
475
489
|
|
|
476
490
|
// Acknowledge connection before sending current attendee information
|
|
477
491
|
this.send({
|
|
@@ -512,9 +526,7 @@ class MessageHandler {
|
|
|
512
526
|
connectedCount++;
|
|
513
527
|
}
|
|
514
528
|
}
|
|
515
|
-
|
|
516
|
-
`[${process_id}] Report: ${attendees.size} attendees, ${connectedCount} connected`,
|
|
517
|
-
);
|
|
529
|
+
log(`Report: ${attendees.size} attendees, ${connectedCount} connected`);
|
|
518
530
|
} else {
|
|
519
531
|
this.send({ event: "error", error: `${process_id} is not connected to presence` });
|
|
520
532
|
}
|
|
@@ -712,7 +724,7 @@ function setupMessageHandler(): void {
|
|
|
712
724
|
const messageHandler = new MessageHandler();
|
|
713
725
|
process.on("message", (msg: MessageFromParent) => {
|
|
714
726
|
messageHandler.onMessage(msg).catch((error: Error) => {
|
|
715
|
-
console.error(`Error in client ${process_id}`, error);
|
|
727
|
+
console.error(`[${testLabel}] Error in client ${process_id}`, error);
|
|
716
728
|
send({ event: "error", error: `${process_id}: ${error.message}` });
|
|
717
729
|
});
|
|
718
730
|
});
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
// eslint-disable-next-line import/no-internal-modules
|
|
6
|
+
// eslint-disable-next-line import-x/no-internal-modules
|
|
7
7
|
import type { JsonSerializable } from "@fluidframework/core-interfaces/internal";
|
|
8
8
|
import type { ScopeType } from "@fluidframework/driver-definitions/legacy";
|
|
9
9
|
import type { AttendeeId } from "@fluidframework/presence/beta";
|
|
@@ -57,6 +57,7 @@ export interface ConnectCommand {
|
|
|
57
57
|
* If not provided, a new Fluid container will be created.
|
|
58
58
|
*/
|
|
59
59
|
containerId?: string;
|
|
60
|
+
connectTimeoutMs: number;
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
/**
|
|
@@ -58,6 +58,7 @@ interface ChildProcess extends AnyChildProcess {
|
|
|
58
58
|
* @returns A collection of child processes and a promise that rejects on the first child error.
|
|
59
59
|
*/
|
|
60
60
|
export async function forkChildProcesses(
|
|
61
|
+
testLabel: string,
|
|
61
62
|
numProcesses: number,
|
|
62
63
|
cleanUpAccumulator: (() => void)[],
|
|
63
64
|
): Promise<{
|
|
@@ -72,6 +73,7 @@ export async function forkChildProcesses(
|
|
|
72
73
|
const childErrorPromises: Promise<never>[] = [];
|
|
73
74
|
for (let i = 0; i < numProcesses; i++) {
|
|
74
75
|
const child = fork("./lib/test/multiprocess/childClient.tool.js", [
|
|
76
|
+
testLabel,
|
|
75
77
|
`child ${i}` /* identifier passed to child process */,
|
|
76
78
|
childLoggingVerbosity /* console logging verbosity */,
|
|
77
79
|
]);
|
|
@@ -152,6 +154,7 @@ export async function executeDebugReports(
|
|
|
152
154
|
function composeConnectMessage(
|
|
153
155
|
id: string | number,
|
|
154
156
|
scopes: ScopeType[] = [ScopeType.DocRead],
|
|
157
|
+
connectTimeoutMs: number,
|
|
155
158
|
): ConnectCommand {
|
|
156
159
|
return {
|
|
157
160
|
command: "connect",
|
|
@@ -161,6 +164,7 @@ function composeConnectMessage(
|
|
|
161
164
|
},
|
|
162
165
|
scopes,
|
|
163
166
|
createScopes: [ScopeType.DocWrite, ScopeType.DocRead],
|
|
167
|
+
connectTimeoutMs,
|
|
164
168
|
};
|
|
165
169
|
}
|
|
166
170
|
|
|
@@ -255,6 +259,7 @@ export async function connectChildProcesses(
|
|
|
255
259
|
const connectContainerCreator = composeConnectMessage(
|
|
256
260
|
0,
|
|
257
261
|
writeClients > 0 ? [ScopeType.DocWrite, ScopeType.DocRead] : [ScopeType.DocRead],
|
|
262
|
+
/* connectTimeoutMs */ readyTimeoutMs,
|
|
258
263
|
);
|
|
259
264
|
firstChild.send(connectContainerCreator);
|
|
260
265
|
}
|
|
@@ -264,7 +269,10 @@ export async function connectChildProcesses(
|
|
|
264
269
|
durationMs: readyTimeoutMs,
|
|
265
270
|
errorMsg: "did not receive 'connected' from child process",
|
|
266
271
|
},
|
|
267
|
-
)
|
|
272
|
+
).catch(async (error) => {
|
|
273
|
+
await executeDebugReports([firstChild]);
|
|
274
|
+
throw error;
|
|
275
|
+
});
|
|
268
276
|
|
|
269
277
|
const attendeeIdPromises: Promise<AttendeeId>[] = [];
|
|
270
278
|
for (const [index, child] of childProcesses.entries()) {
|
|
@@ -275,6 +283,7 @@ export async function connectChildProcesses(
|
|
|
275
283
|
const message = composeConnectMessage(
|
|
276
284
|
index,
|
|
277
285
|
index < writeClients ? [ScopeType.DocWrite, ScopeType.DocRead] : [ScopeType.DocRead],
|
|
286
|
+
/* connectTimeoutMs */ readyTimeoutMs,
|
|
278
287
|
);
|
|
279
288
|
message.containerId = containerId;
|
|
280
289
|
attendeeIdPromises.push(
|
|
@@ -348,6 +357,10 @@ export async function connectAndListenForAttendees(
|
|
|
348
357
|
readyTimeoutMs: childConnectTimeoutMs,
|
|
349
358
|
});
|
|
350
359
|
|
|
360
|
+
// These actions are not awaited. They are here to provide additional logging.
|
|
361
|
+
// It is up to the caller to await attendeeIdPromises if desired. This can mean
|
|
362
|
+
// An "error" message is output, but the caller does not care about attendee
|
|
363
|
+
// ids and proceeds.
|
|
351
364
|
Promise.all(connectResult.attendeeIdPromises)
|
|
352
365
|
.then(() => console.log("All attendees connected."))
|
|
353
366
|
.catch((error) => {
|