@fluidframework/azure-end-to-end-tests 2.92.0 → 2.100.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 +12 -0
- package/README.md +1 -1
- package/eslint.config.mts +1 -1
- package/lib/test/multiprocess/childClient.tool.js +3 -3
- 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.map +1 -1
- package/lib/test/multiprocess/presenceTest.spec.js.map +1 -1
- package/lib/test/signals.spec.js +2 -2
- package/lib/test/signals.spec.js.map +1 -1
- package/package.json +25 -25
- package/src/test/multiprocess/childClient.tool.ts +8 -6
- package/src/test/multiprocess/messageTypes.ts +1 -1
- package/src/test/multiprocess/orchestratorUtils.ts +1 -1
- package/src/test/multiprocess/presenceTest.spec.ts +1 -1
- package/src/test/signals.spec.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @fluidframework/azure-end-to-end-tests
|
|
2
2
|
|
|
3
|
+
## 2.100.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Node 22 is now the minimum supported Node.js version ([#27116](https://github.com/microsoft/FluidFramework/pull/27116)) [e8214d29663](https://github.com/microsoft/FluidFramework/commit/e8214d29663f5ee98d737daed82506a25d8de8d0)
|
|
8
|
+
|
|
9
|
+
All Fluid Framework client packages now require Node.js 22 or later. This aligns with the standing Node upgrade policy as Node 20 reaches end-of-life on April 30, 2026.
|
|
10
|
+
|
|
11
|
+
## 2.93.0
|
|
12
|
+
|
|
13
|
+
Dependency updates only.
|
|
14
|
+
|
|
3
15
|
## 2.92.0
|
|
4
16
|
|
|
5
17
|
Dependency updates only.
|
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ When making such a request please include if the configuration already works (an
|
|
|
24
24
|
|
|
25
25
|
### Supported Runtimes
|
|
26
26
|
|
|
27
|
-
- NodeJs ^
|
|
27
|
+
- NodeJs ^22.22.2 except that we will drop support for it [when NodeJs 22 loses its upstream support on 2027-04-30](https://github.com/nodejs/release#release-schedule), and will support a newer LTS version of NodeJS at least 1 year before 22 is end-of-life.
|
|
28
28
|
- Running Fluid in a Node.js environment with the `--no-experimental-fetch` flag is not supported.
|
|
29
29
|
- Modern browsers supporting the es2022 standard library: in response to asks we can add explicit support for using babel to polyfill to target specific standards or runtimes (meaning we can avoid/remove use of things that don't polyfill robustly, but otherwise target modern standards).
|
|
30
30
|
|
package/eslint.config.mts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { Linter } from "eslint";
|
|
7
|
-
import { recommended } from "
|
|
7
|
+
import { recommended } from "@fluidframework/eslint-config-fluid/flat.mts";
|
|
8
8
|
|
|
9
9
|
const config: Linter.Config[] = [
|
|
10
10
|
...recommended,
|
|
@@ -6,8 +6,8 @@ import { strict as assert } from "node:assert";
|
|
|
6
6
|
import { AzureClient, } from "@fluidframework/azure-client";
|
|
7
7
|
import { AttachState } from "@fluidframework/container-definitions";
|
|
8
8
|
import { ConnectionState } from "@fluidframework/container-loader";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { getPresence } from "@fluidframework/fluid-static";
|
|
10
|
+
import { StateFactory, } from "@fluidframework/presence";
|
|
11
11
|
import { InsecureTokenProvider } from "@fluidframework/test-runtime-utils/internal";
|
|
12
12
|
import { timeoutPromise } from "@fluidframework/test-utils/internal";
|
|
13
13
|
import { createAzureTokenProvider } from "../AzureTokenFactory.js";
|
|
@@ -54,7 +54,7 @@ function selectiveVerboseLog(event, logLevel) {
|
|
|
54
54
|
if (interest === "details") {
|
|
55
55
|
content.details = event.details;
|
|
56
56
|
}
|
|
57
|
-
log(`[${logLevel ??
|
|
57
|
+
log(`[${logLevel ?? "unspecified"}]`, content);
|
|
58
58
|
}
|
|
59
59
|
/**
|
|
60
60
|
* Get or create a Fluid container.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"childClient.tool.js","sourceRoot":"","sources":["../../../src/test/multiprocess/childClient.tool.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { strict as assert } from \"node:assert\";\n\nimport {\n\tAzureClient,\n\ttype AzureContainerServices,\n\ttype AzureLocalConnectionConfig,\n\ttype AzureRemoteConnectionConfig,\n\ttype ITelemetryBaseEvent,\n} from \"@fluidframework/azure-client\";\nimport { AttachState } from \"@fluidframework/container-definitions\";\nimport { ConnectionState } from \"@fluidframework/container-loader\";\nimport type { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { LogLevel } from \"@fluidframework/core-interfaces\";\nimport type { ScopeType } from \"@fluidframework/driver-definitions/legacy\";\nimport type { ContainerSchema, IFluidContainer } from \"@fluidframework/fluid-static\";\nimport {\n\tgetPresence,\n\ttype Attendee,\n\ttype Presence,\n\tStateFactory,\n\ttype LatestRaw,\n\ttype LatestMapRaw,\n\ttype StatesWorkspace,\n} from \"@fluidframework/presence/beta\";\nimport { InsecureTokenProvider } from \"@fluidframework/test-runtime-utils/internal\";\nimport { timeoutPromise } from \"@fluidframework/test-utils/internal\";\n\nimport { createAzureTokenProvider } from \"../AzureTokenFactory.js\";\nimport { TestDataObject } from \"../TestDataObject.js\";\n\nimport type {\n\tMessageFromChild as MessageToParent,\n\tMessageToChild as MessageFromParent,\n\tUserIdAndName,\n\tEventEntry,\n} from \"./messageTypes.js\";\n\nconst testLabel = process.argv[2];\n// Identifier given to child process\nconst process_id = process.argv[3];\nconst verbosity = process.argv[4] ?? \"\";\n\nconst useAzure = process.env.FLUID_CLIENT === \"azure\";\nconst tenantId = useAzure\n\t? (process.env.azure__fluid__relay__service__tenantId as string)\n\t: \"frs-client-tenant\";\nconst endPoint = process.env.azure__fluid__relay__service__endpoint as string;\nif (useAzure && endPoint === undefined) {\n\tthrow new Error(\"Azure Fluid Relay service endpoint is missing\");\n}\n\nconst containerSchema = {\n\tinitialObjects: {\n\t\t// A DataObject is added as otherwise fluid-static complains \"Container cannot be initialized without any DataTypes\"\n\t\t_unused: TestDataObject,\n\t},\n} as const satisfies ContainerSchema;\n\nfunction log(...data: unknown[]): void {\n\tconsole.log(`[${testLabel}] [${new Date().toISOString()}] [${process_id}]`, ...data);\n}\n\nfunction telemetryEventInterestLevel(eventName: string): \"none\" | \"basic\" | \"details\" {\n\tif (eventName.includes(\":Signal\") || eventName.includes(\":Join\")) {\n\t\treturn \"details\";\n\t} else if (eventName.includes(\":Container:\") || eventName.includes(\":Presence:\")) {\n\t\treturn \"basic\";\n\t}\n\treturn \"none\";\n}\n\nfunction selectiveVerboseLog(event: ITelemetryBaseEvent, logLevel?: LogLevel): void {\n\tconst interest = telemetryEventInterestLevel(event.eventName);\n\tif (interest === \"none\") {\n\t\treturn;\n\t}\n\tconst content: Record<string, unknown> = {\n\t\teventName: event.eventName,\n\t\tcontainerConnectionState: event.containerConnectionState,\n\t};\n\tif (interest === \"details\") {\n\t\tcontent.details = event.details;\n\t}\n\tlog(`[${logLevel ?? LogLevel.default}]`, content);\n}\n\n/**\n * Get or create a Fluid container.\n */\nconst getOrCreateContainer = async (params: {\n\tlogger: ITelemetryBaseLogger;\n\tonDisconnected: () => void;\n\tcontainerId?: string;\n\tuser: UserIdAndName;\n\tscopes?: ScopeType[];\n\tcreateScopes?: ScopeType[];\n\tconnectTimeoutMs: number;\n}): Promise<{\n\tcontainer: IFluidContainer<typeof containerSchema>;\n\tservices: AzureContainerServices;\n\tclient: AzureClient;\n\tcontainerId: string;\n\tconnected: Promise<void>;\n}> => {\n\tlet container: IFluidContainer<typeof containerSchema>;\n\tlet { containerId } = params;\n\tconst { logger, onDisconnected, user, scopes, createScopes, connectTimeoutMs } = params;\n\tconst connectionProps: AzureRemoteConnectionConfig | AzureLocalConnectionConfig = useAzure\n\t\t? {\n\t\t\t\ttenantId,\n\t\t\t\ttokenProvider: createAzureTokenProvider(\n\t\t\t\t\tuser.id ?? \"foo\",\n\t\t\t\t\tuser.name ?? \"bar\",\n\t\t\t\t\tscopes,\n\t\t\t\t\tcreateScopes,\n\t\t\t\t),\n\t\t\t\tendpoint: endPoint,\n\t\t\t\ttype: \"remote\",\n\t\t\t}\n\t\t: {\n\t\t\t\ttokenProvider: new InsecureTokenProvider(\"fooBar\", user, scopes, createScopes),\n\t\t\t\tendpoint: \"http://localhost:7071\",\n\t\t\t\ttype: \"local\",\n\t\t\t};\n\tconst client = new AzureClient({\n\t\tconnection: connectionProps,\n\t\tlogger,\n\t\tconfigProvider: {\n\t\t\tgetRawConfig: (v: string) => {\n\t\t\t\t// At higher client counts, summarizer will get invoked, taking up resources\n\t\t\t\t// and spewing telemetry. None of current tests need summarization, so disable it.\n\t\t\t\tif (v === \"Fluid.ContainerRuntime.Test.DisableSummaries\") {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn undefined;\n\t\t\t},\n\t\t},\n\t});\n\tlet services: AzureContainerServices;\n\tif (containerId === undefined) {\n\t\t({ container, services } = await client.createContainer(containerSchema, \"2\"));\n\t\tcontainerId = await container.attach();\n\t} else {\n\t\t({ container, services } = await client.getContainer(containerId, containerSchema, \"2\"));\n\t}\n\tcontainer.on(\"disconnected\", onDisconnected);\n\n\tconst connected =\n\t\tcontainer.connectionState === ConnectionState.Connected\n\t\t\t? Promise.resolve()\n\t\t\t: timeoutPromise((resolve) => container.once(\"connected\", () => resolve()), {\n\t\t\t\t\tdurationMs: connectTimeoutMs,\n\t\t\t\t\terrorMsg: \"container connect() timeout\",\n\t\t\t\t});\n\n\tassert.strictEqual(\n\t\tcontainer.attachState,\n\t\tAttachState.Attached,\n\t\t\"Container is not attached after attach is called\",\n\t);\n\n\treturn {\n\t\tclient,\n\t\tcontainer,\n\t\tservices,\n\t\tcontainerId,\n\t\tconnected,\n\t};\n};\n\nfunction createSendFunction(): (msg: MessageToParent) => void {\n\tif (process.send) {\n\t\tconst sendFn = process.send.bind(process);\n\t\tif (verbosity.includes(\"msgs\")) {\n\t\t\treturn (msg: MessageToParent) => {\n\t\t\t\tlog(`Sending`, msg);\n\t\t\t\tsendFn(msg);\n\t\t\t};\n\t\t}\n\t\treturn sendFn;\n\t}\n\tthrow new Error(\"process.send is not defined\");\n}\n\nconst send = createSendFunction();\n\nfunction isStringOrNumberRecord(value: unknown): value is Record<string, string | number> {\n\tif (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n\t\treturn false;\n\t}\n\n\tconst stringKeys = Object.keys(value);\n\tconst allKeys = Reflect.ownKeys(value);\n\n\tif (stringKeys.length !== allKeys.length) {\n\t\t// If there are non-string/symbol keys, return false\n\t\treturn false;\n\t}\n\tfor (const key of stringKeys) {\n\t\tif (!(typeof value[key] === \"string\" || typeof value[key] === \"number\")) {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\n// NOTE:\n// - This schema intentionally uses optional keys (latest?, latestMap?) so tests can register\n// states conditionally at runtime.\n// - Optional keys are not explicitly supported in StatesWorkspace typing today, which means\n// workspace.states.<key> is typed as any. As a result, usages below require casts\n// (e.g., to LatestRaw / LatestMapRaw) to recover concrete types.\n// - Track adding proper optional-key support to Presence state workspace typing here:\n// Work item: AB#47518\n// - Fallout: Until the above is addressed, keep the casts in place and document new usages accordingly.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\ntype WorkspaceSchema = {\n\tlatest?: ReturnType<typeof StateFactory.latest<{ value: string }, \"latest\">>;\n\tlatestMap?: ReturnType<\n\t\ttypeof StateFactory.latestMap<\n\t\t\t{ value: Record<string, string | number> },\n\t\t\tstring,\n\t\t\t\"latestMap\"\n\t\t>\n\t>;\n};\nconst WorkspaceSchema: WorkspaceSchema = {};\n\nclass MessageHandler {\n\tprivate readonly log: EventEntry[] = [];\n\tprivate msgQueue: undefined | Exclude<MessageFromParent, { command: \"ping\" | \"connect\" }>[];\n\tprivate container: IFluidContainer | undefined;\n\tprivate presence: Presence | undefined;\n\tprivate readonly workspaces = new Map<string, StatesWorkspace<WorkspaceSchema>>();\n\n\tprivate send(msg: MessageToParent): void {\n\t\tthis.log.push({\n\t\t\ttimestamp: Date.now(),\n\t\t\tagentId: process_id,\n\t\t\teventCategory: \"messageSent\",\n\t\t\teventName: msg.event,\n\t\t\tdetails:\n\t\t\t\tmsg.event === \"debugReportComplete\" && msg.log\n\t\t\t\t\t? JSON.stringify({ logLength: msg.log.length })\n\t\t\t\t\t: JSON.stringify(msg),\n\t\t});\n\t\tsend(msg);\n\t}\n\n\tprivate readonly sendAttendeeConnected = (attendee: Attendee): void => {\n\t\tthis.send({\n\t\t\tevent: \"attendeeConnected\",\n\t\t\tattendeeId: attendee.attendeeId,\n\t\t});\n\t};\n\tprivate readonly sendAttendeeDisconnected = (attendee: Attendee): void => {\n\t\tthis.send({\n\t\t\tevent: \"attendeeDisconnected\",\n\t\t\tattendeeId: attendee.attendeeId,\n\t\t});\n\t};\n\n\tprivate readonly logger: ITelemetryBaseLogger = {\n\t\tsend: (event: ITelemetryBaseEvent, logLevel?: LogLevel) => {\n\t\t\t// Filter out non-interactive client telemetry\n\t\t\tconst clientType = event.clientType;\n\t\t\tif (typeof clientType === \"string\" && clientType.startsWith(\"noninteractive\")) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Special case unexpected telemetry event\n\t\t\tif (event.eventName.endsWith(\":JoinResponseWhenAlone\")) {\n\t\t\t\tthis.send({\n\t\t\t\t\tevent: \"error\",\n\t\t\t\t\terror: `Unexpected ClientJoin response. Details: ${JSON.stringify(event.details)}\\nLog: ${JSON.stringify(this.log)}`,\n\t\t\t\t});\n\t\t\t\t// Keep going\n\t\t\t}\n\n\t\t\tconst interest = telemetryEventInterestLevel(event.eventName);\n\t\t\tif (interest === \"none\") {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.log.push({\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t\tagentId: process_id,\n\t\t\t\teventCategory: \"telemetry\",\n\t\t\t\teventName: event.eventName,\n\t\t\t\tdetails:\n\t\t\t\t\ttypeof event.details === \"string\" ? event.details : JSON.stringify(event.details),\n\t\t\t});\n\t\t\tif (verbosity.includes(\"telem\")) {\n\t\t\t\tselectiveVerboseLog(event, logLevel);\n\t\t\t}\n\t\t},\n\t};\n\n\tprivate readonly onDisconnected = (): void => {\n\t\t// Test state is a bit fragile and does not account for reconnections.\n\t\tthis.send({ event: \"error\", error: `${process_id}: Container disconnected` });\n\t};\n\n\tprivate registerWorkspace(\n\t\tworkspaceId: string,\n\t\toptions: { latest?: boolean; latestMap?: boolean },\n\t): void {\n\t\tif (!this.presence) {\n\t\t\tthis.send({ event: \"error\", error: `${process_id} is not connected to presence` });\n\t\t\treturn;\n\t\t}\n\t\tconst { latest, latestMap } = options;\n\t\tconst workspace: StatesWorkspace<WorkspaceSchema> = this.presence.states.getWorkspace(\n\t\t\t`test:${workspaceId}`,\n\t\t\tWorkspaceSchema,\n\t\t);\n\n\t\tif (latest && !workspace.states.latest) {\n\t\t\tworkspace.add(\"latest\", StateFactory.latest({ local: { value: \"initial\" } }));\n\t\t\t// Cast required due to optional keys in WorkspaceSchema\n\t\t\t// TODO: AB#47518\n\t\t\tconst latestState = workspace.states.latest as LatestRaw<{ value: string }>;\n\t\t\tlatestState.events.on(\"remoteUpdated\", (update) => {\n\t\t\t\tthis.send({\n\t\t\t\t\tevent: \"latestValueUpdated\",\n\t\t\t\t\tworkspaceId,\n\t\t\t\t\tattendeeId: update.attendee.attendeeId,\n\t\t\t\t\tvalue: update.value.value,\n\t\t\t\t});\n\t\t\t});\n\t\t\tfor (const remote of latestState.getRemotes()) {\n\t\t\t\tthis.send({\n\t\t\t\t\tevent: \"latestValueUpdated\",\n\t\t\t\t\tworkspaceId,\n\t\t\t\t\tattendeeId: remote.attendee.attendeeId,\n\t\t\t\t\tvalue: remote.value.value,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tif (latestMap && !workspace.states.latestMap) {\n\t\t\tworkspace.add(\n\t\t\t\t\"latestMap\",\n\t\t\t\tStateFactory.latestMap<{ value: Record<string, string | number> }>({\n\t\t\t\t\tlocal: {},\n\t\t\t\t}),\n\t\t\t);\n\t\t\t// Cast required due to optional keys in WorkspaceSchema\n\t\t\t// TODO: AB#47518\n\t\t\tconst latestMapState = workspace.states.latestMap as LatestMapRaw<{\n\t\t\t\tvalue: Record<string, string | number>;\n\t\t\t}>;\n\t\t\tlatestMapState.events.on(\"remoteUpdated\", (update) => {\n\t\t\t\tfor (const [key, valueWithMetadata] of update.items) {\n\t\t\t\t\tthis.send({\n\t\t\t\t\t\tevent: \"latestMapValueUpdated\",\n\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\tattendeeId: update.attendee.attendeeId,\n\t\t\t\t\t\tkey: String(key),\n\t\t\t\t\t\tvalue: valueWithMetadata.value.value,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\t\t\tfor (const remote of latestMapState.getRemotes()) {\n\t\t\t\tfor (const [key, valueWithMetadata] of remote.items) {\n\t\t\t\t\tthis.send({\n\t\t\t\t\t\tevent: \"latestMapValueUpdated\",\n\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\tattendeeId: remote.attendee.attendeeId,\n\t\t\t\t\t\tkey: String(key),\n\t\t\t\t\t\tvalue: valueWithMetadata.value.value,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis.workspaces.set(workspaceId, workspace);\n\t\tthis.send({\n\t\t\tevent: \"workspaceRegistered\",\n\t\t\tworkspaceId,\n\t\t\tlatest: latest ?? false,\n\t\t\tlatestMap: latestMap ?? false,\n\t\t});\n\t}\n\n\tpublic async onMessage(msg: MessageFromParent): Promise<void> {\n\t\tif (verbosity.includes(\"msgs\")) {\n\t\t\tthis.log.push({\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t\tagentId: process_id,\n\t\t\t\teventCategory: \"messageReceived\",\n\t\t\t\teventName: msg.command,\n\t\t\t});\n\t\t\tlog(`Received`, msg);\n\t\t}\n\n\t\tif (msg.command === \"ping\") {\n\t\t\tthis.handlePing();\n\t\t\treturn;\n\t\t}\n\n\t\tif (msg.command === \"connect\") {\n\t\t\tawait this.handleConnect(msg);\n\t\t\treturn;\n\t\t}\n\n\t\t// All other message must wait if connect is in progress\n\t\tif (this.msgQueue !== undefined) {\n\t\t\tthis.msgQueue.push(msg);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.processMessage(msg);\n\t}\n\n\tprivate processMessage(\n\t\tmsg: Exclude<MessageFromParent, { command: \"ping\" | \"connect\" }>,\n\t): void {\n\t\tswitch (msg.command) {\n\t\t\tcase \"debugReport\": {\n\t\t\t\tthis.handleDebugReport(msg);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"disconnectSelf\": {\n\t\t\t\tthis.handleDisconnectSelf();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"setLatestValue\": {\n\t\t\t\tthis.handleSetLatestValue(msg);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"setLatestMapValue\": {\n\t\t\t\tthis.handleSetLatestMapValue(msg);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"getLatestValue\": {\n\t\t\t\tthis.handleGetLatestValue(msg);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"getLatestMapValue\": {\n\t\t\t\tthis.handleGetLatestMapValue(msg);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"registerWorkspace\": {\n\t\t\t\tthis.registerWorkspace(msg.workspaceId, {\n\t\t\t\t\tlatest: msg.latest,\n\t\t\t\t\tlatestMap: msg.latestMap,\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tconsole.error(`${process_id}: Unknown command:`, msg);\n\t\t\t\tthis.send({\n\t\t\t\t\tevent: \"error\",\n\t\t\t\t\terror: `${process_id} Unknown command: ${JSON.stringify(msg)}`,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate handlePing(): void {\n\t\tthis.send({ event: \"ack\" });\n\t}\n\n\tprivate async handleConnect(\n\t\tmsg: Extract<MessageFromParent, { command: \"connect\" }>,\n\t): Promise<void> {\n\t\tif (!msg.user) {\n\t\t\tthis.send({ event: \"error\", error: `${process_id}: No azure user information given` });\n\t\t\treturn;\n\t\t}\n\t\tif (this.container) {\n\t\t\tthis.send({ event: \"error\", error: `${process_id}: Container already loaded` });\n\t\t\treturn;\n\t\t}\n\n\t\t// Prevent reentrance. Queue messages until after connect is fully processed.\n\t\tthis.msgQueue = [];\n\n\t\ttry {\n\t\t\tconst { container, containerId, connected } = await getOrCreateContainer({\n\t\t\t\t...msg,\n\t\t\t\tlogger: this.logger,\n\t\t\t\tonDisconnected: this.onDisconnected,\n\t\t\t});\n\t\t\tthis.container = container;\n\t\t\tconst presence = getPresence(container);\n\t\t\tthis.presence = presence;\n\n\t\t\t// wait for 'ConnectionState.Connected'\n\t\t\tawait connected.catch((error) => {\n\t\t\t\t(error as Error).message += `\\nLog: ${JSON.stringify(this.log)}`;\n\t\t\t\tthrow error;\n\t\t\t});\n\n\t\t\t// Acknowledge connection before sending current attendee information\n\t\t\tthis.send({\n\t\t\t\tevent: \"connected\",\n\t\t\t\tcontainerId,\n\t\t\t\tattendeeId: presence.attendees.getMyself().attendeeId,\n\t\t\t});\n\n\t\t\t// Send existing attendees excluding self to parent/orchestrator\n\t\t\tconst self = presence.attendees.getMyself();\n\t\t\tfor (const attendee of presence.attendees.getAttendees()) {\n\t\t\t\tif (attendee !== self && attendee.getConnectionStatus() === \"Connected\") {\n\t\t\t\t\tthis.sendAttendeeConnected(attendee);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Listen for presence events to notify parent/orchestrator when a new attendee joins or leaves the session.\n\t\t\tpresence.attendees.events.on(\"attendeeConnected\", this.sendAttendeeConnected);\n\t\t\tpresence.attendees.events.on(\"attendeeDisconnected\", this.sendAttendeeDisconnected);\n\t\t} finally {\n\t\t\t// Process any queued messages received while connecting\n\t\t\tfor (const queuedMsg of this.msgQueue) {\n\t\t\t\tthis.processMessage(queuedMsg);\n\t\t\t}\n\t\t\tthis.msgQueue = undefined;\n\t\t}\n\t}\n\n\tprivate handleDebugReport(\n\t\tmsg: Extract<MessageFromParent, { command: \"debugReport\" }>,\n\t): void {\n\t\tif (msg.reportAttendees) {\n\t\t\tif (this.presence) {\n\t\t\t\tconst attendees = this.presence.attendees.getAttendees();\n\t\t\t\tlet connectedCount = 0;\n\t\t\t\tfor (const attendee of attendees) {\n\t\t\t\t\tif (attendee.getConnectionStatus() === \"Connected\") {\n\t\t\t\t\t\tconnectedCount++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tlog(`Report: ${attendees.size} attendees, ${connectedCount} connected`);\n\t\t\t} else {\n\t\t\t\tthis.send({ event: \"error\", error: `${process_id} is not connected to presence` });\n\t\t\t}\n\t\t}\n\n\t\tconst debugReport: Extract<MessageToParent, { event: \"debugReportComplete\" }> = {\n\t\t\tevent: \"debugReportComplete\",\n\t\t};\n\t\tif (msg.sendEventLog) {\n\t\t\tdebugReport.log = this.log;\n\t\t}\n\t\tthis.send(debugReport);\n\t}\n\n\tprivate handleDisconnectSelf(): void {\n\t\tif (!this.container) {\n\t\t\tthis.send({ event: \"error\", error: `${process_id} is not connected to container` });\n\t\t\treturn;\n\t\t}\n\t\t// There are no current scenarios where disconnect without presence is expected.\n\t\tif (!this.presence) {\n\t\t\tthis.send({ event: \"error\", error: `${process_id} is not connected to presence` });\n\t\t\treturn;\n\t\t}\n\t\t// Disconnect event is treated as an error in normal handling.\n\t\t// Remove listener as this disconnect is intentional.\n\t\tthis.container.off(\"disconnected\", this.onDisconnected);\n\t\tthis.container.disconnect();\n\t\tthis.send({\n\t\t\tevent: \"disconnectedSelf\",\n\t\t\tattendeeId: this.presence.attendees.getMyself().attendeeId,\n\t\t});\n\t}\n\n\tprivate handleSetLatestValue(\n\t\tmsg: Extract<MessageFromParent, { command: \"setLatestValue\" }>,\n\t): void {\n\t\tif (!this.presence) {\n\t\t\tthis.send({ event: \"error\", error: `${process_id} is not connected to presence` });\n\t\t\treturn;\n\t\t}\n\t\tconst workspace = this.workspaces.get(msg.workspaceId);\n\t\tif (!workspace) {\n\t\t\tthis.send({\n\t\t\t\tevent: \"error\",\n\t\t\t\terror: `${process_id} workspace ${msg.workspaceId} not found`,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\t// Cast required due to optional keys in WorkspaceSchema\n\t\t// TODO: AB#47518\n\t\tconst latestState = workspace.states.latest as LatestRaw<{ value: string }> | undefined;\n\t\tif (!latestState) {\n\t\t\tthis.send({\n\t\t\t\tevent: \"error\",\n\t\t\t\terror: `${process_id} latest state not registered for workspace ${msg.workspaceId}`,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tif (typeof msg.value !== \"string\") {\n\t\t\treturn;\n\t\t}\n\t\tlatestState.local = { value: msg.value };\n\t}\n\n\tprivate handleSetLatestMapValue(\n\t\tmsg: Extract<MessageFromParent, { command: \"setLatestMapValue\" }>,\n\t): void {\n\t\tif (!this.presence) {\n\t\t\tthis.send({ event: \"error\", error: `${process_id} is not connected to presence` });\n\t\t\treturn;\n\t\t}\n\t\tif (typeof msg.key !== \"string\") {\n\t\t\tthis.send({ event: \"error\", error: `${process_id} invalid key type` });\n\t\t\treturn;\n\t\t}\n\t\tconst workspace = this.workspaces.get(msg.workspaceId);\n\t\tif (!workspace) {\n\t\t\tthis.send({\n\t\t\t\tevent: \"error\",\n\t\t\t\terror: `${process_id} workspace ${msg.workspaceId} not found`,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\t// Cast required due to optional keys in WorkspaceSchema\n\t\t// TODO: AB#47518\n\t\tconst latestMapState = workspace.states.latestMap as\n\t\t\t| LatestMapRaw<{ value: Record<string, string | number> }>\n\t\t\t| undefined;\n\t\tif (!latestMapState) {\n\t\t\tthis.send({\n\t\t\t\tevent: \"error\",\n\t\t\t\terror: `${process_id} latestMap state not registered for workspace ${msg.workspaceId}`,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tif (!isStringOrNumberRecord(msg.value)) {\n\t\t\treturn;\n\t\t}\n\t\tlatestMapState.local.set(msg.key, { value: msg.value });\n\t}\n\n\tprivate handleGetLatestValue(\n\t\tmsg: Extract<MessageFromParent, { command: \"getLatestValue\" }>,\n\t): void {\n\t\tif (!this.presence) {\n\t\t\tthis.send({ event: \"error\", error: `${process_id} is not connected to presence` });\n\t\t\treturn;\n\t\t}\n\t\tconst workspace = this.workspaces.get(msg.workspaceId);\n\t\tif (!workspace) {\n\t\t\tthis.send({\n\t\t\t\tevent: \"error\",\n\t\t\t\terror: `${process_id} workspace ${msg.workspaceId} not found`,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\t// Cast required due to optional keys in WorkspaceSchema\n\t\t// TODO: AB#47518\n\t\tconst latestState = workspace.states.latest as LatestRaw<{ value: string }> | undefined;\n\t\tif (!latestState) {\n\t\t\tthis.send({\n\t\t\t\tevent: \"error\",\n\t\t\t\terror: `${process_id} latest state not registered for workspace ${msg.workspaceId}`,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tlet value: { value: string };\n\t\tif (msg.attendeeId) {\n\t\t\tconst attendee = this.presence.attendees.getAttendee(msg.attendeeId);\n\t\t\tconst remoteData = latestState.getRemote(attendee);\n\t\t\tvalue = remoteData.value;\n\t\t} else {\n\t\t\tvalue = latestState.local;\n\t\t}\n\t\tthis.send({\n\t\t\tevent: \"latestValueGetResponse\",\n\t\t\tworkspaceId: msg.workspaceId,\n\t\t\tattendeeId: msg.attendeeId,\n\t\t\tvalue: value.value,\n\t\t});\n\t}\n\n\tprivate handleGetLatestMapValue(\n\t\tmsg: Extract<MessageFromParent, { command: \"getLatestMapValue\" }>,\n\t): void {\n\t\tif (!this.presence) {\n\t\t\tthis.send({ event: \"error\", error: `${process_id} is not connected to presence` });\n\t\t\treturn;\n\t\t}\n\t\tif (typeof msg.key !== \"string\") {\n\t\t\tthis.send({ event: \"error\", error: `${process_id} invalid key type` });\n\t\t\treturn;\n\t\t}\n\t\tconst workspace = this.workspaces.get(msg.workspaceId);\n\t\tif (!workspace) {\n\t\t\tthis.send({\n\t\t\t\tevent: \"error\",\n\t\t\t\terror: `${process_id} workspace ${msg.workspaceId} not found`,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\t// Cast required due to optional keys in WorkspaceSchema\n\t\t// TODO: AB#47518\n\t\tconst latestMapState = workspace.states.latestMap as\n\t\t\t| LatestMapRaw<{ value: Record<string, string | number> }>\n\t\t\t| undefined;\n\t\tif (!latestMapState) {\n\t\t\tthis.send({\n\t\t\t\tevent: \"error\",\n\t\t\t\terror: `${process_id} latestMap state not registered for workspace ${msg.workspaceId}`,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tlet value: { value: Record<string, string | number> } | undefined;\n\t\tif (msg.attendeeId) {\n\t\t\tconst attendee = this.presence.attendees.getAttendee(msg.attendeeId);\n\t\t\tconst remoteData = latestMapState.getRemote(attendee);\n\t\t\tconst keyData = remoteData.get(msg.key);\n\t\t\tvalue = keyData?.value;\n\t\t} else {\n\t\t\tvalue = latestMapState.local.get(msg.key);\n\t\t}\n\t\tthis.send({\n\t\t\tevent: \"latestMapValueGetResponse\",\n\t\t\tworkspaceId: msg.workspaceId,\n\t\t\tattendeeId: msg.attendeeId,\n\t\t\tkey: msg.key,\n\t\t\tvalue: value?.value,\n\t\t});\n\t}\n}\n\nfunction setupMessageHandler(): void {\n\tconst messageHandler = new MessageHandler();\n\tprocess.on(\"message\", (msg: MessageFromParent) => {\n\t\tmessageHandler.onMessage(msg).catch((error: Error) => {\n\t\t\tconsole.error(`[${testLabel}] Error in client ${process_id}`, error);\n\t\t\tsend({ event: \"error\", error: `${process_id}: ${error.message}` });\n\t\t});\n\t});\n}\n\nsetupMessageHandler();\n"]}
|
|
1
|
+
{"version":3,"file":"childClient.tool.js","sourceRoot":"","sources":["../../../src/test/multiprocess/childClient.tool.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { strict as assert } from \"node:assert\";\n\nimport {\n\tAzureClient,\n\ttype AzureContainerServices,\n\ttype AzureLocalConnectionConfig,\n\ttype AzureRemoteConnectionConfig,\n\ttype ITelemetryBaseEvent,\n} from \"@fluidframework/azure-client\";\nimport { AttachState } from \"@fluidframework/container-definitions\";\nimport { ConnectionState } from \"@fluidframework/container-loader\";\nimport type { ITelemetryBaseLogger, LogLevel } from \"@fluidframework/core-interfaces\";\nimport type { ScopeType } from \"@fluidframework/driver-definitions/legacy\";\nimport type { ContainerSchema, IFluidContainer } from \"@fluidframework/fluid-static\";\nimport { getPresence } from \"@fluidframework/fluid-static\";\nimport {\n\ttype Attendee,\n\ttype Presence,\n\tStateFactory,\n\ttype LatestRaw,\n\ttype LatestMapRaw,\n\ttype StatesWorkspace,\n} from \"@fluidframework/presence\";\nimport { InsecureTokenProvider } from \"@fluidframework/test-runtime-utils/internal\";\nimport { timeoutPromise } from \"@fluidframework/test-utils/internal\";\n\nimport { createAzureTokenProvider } from \"../AzureTokenFactory.js\";\nimport { TestDataObject } from \"../TestDataObject.js\";\n\nimport type {\n\tMessageFromChild as MessageToParent,\n\tMessageToChild as MessageFromParent,\n\tUserIdAndName,\n\tEventEntry,\n} from \"./messageTypes.js\";\n\nconst testLabel = process.argv[2];\n// Identifier given to child process\nconst process_id = process.argv[3];\nconst verbosity = process.argv[4] ?? \"\";\n\nconst useAzure = process.env.FLUID_CLIENT === \"azure\";\nconst tenantId = useAzure\n\t? (process.env.azure__fluid__relay__service__tenantId as string)\n\t: \"frs-client-tenant\";\nconst endPoint = process.env.azure__fluid__relay__service__endpoint as string;\nif (useAzure && endPoint === undefined) {\n\tthrow new Error(\"Azure Fluid Relay service endpoint is missing\");\n}\n\nconst containerSchema = {\n\tinitialObjects: {\n\t\t// A DataObject is added as otherwise fluid-static complains \"Container cannot be initialized without any DataTypes\"\n\t\t_unused: TestDataObject,\n\t},\n} as const satisfies ContainerSchema;\n\nfunction log(...data: unknown[]): void {\n\tconsole.log(`[${testLabel}] [${new Date().toISOString()}] [${process_id}]`, ...data);\n}\n\nfunction telemetryEventInterestLevel(eventName: string): \"none\" | \"basic\" | \"details\" {\n\tif (eventName.includes(\":Signal\") || eventName.includes(\":Join\")) {\n\t\treturn \"details\";\n\t} else if (eventName.includes(\":Container:\") || eventName.includes(\":Presence:\")) {\n\t\treturn \"basic\";\n\t}\n\treturn \"none\";\n}\n\nfunction selectiveVerboseLog(\n\tevent: ITelemetryBaseEvent,\n\tlogLevel: LogLevel | undefined,\n): void {\n\tconst interest = telemetryEventInterestLevel(event.eventName);\n\tif (interest === \"none\") {\n\t\treturn;\n\t}\n\tconst content: Record<string, unknown> = {\n\t\teventName: event.eventName,\n\t\tcontainerConnectionState: event.containerConnectionState,\n\t};\n\tif (interest === \"details\") {\n\t\tcontent.details = event.details;\n\t}\n\tlog(`[${logLevel ?? \"unspecified\"}]`, content);\n}\n\n/**\n * Get or create a Fluid container.\n */\nconst getOrCreateContainer = async (params: {\n\tlogger: ITelemetryBaseLogger;\n\tonDisconnected: () => void;\n\tcontainerId?: string;\n\tuser: UserIdAndName;\n\tscopes?: ScopeType[];\n\tcreateScopes?: ScopeType[];\n\tconnectTimeoutMs: number;\n}): Promise<{\n\tcontainer: IFluidContainer<typeof containerSchema>;\n\tservices: AzureContainerServices;\n\tclient: AzureClient;\n\tcontainerId: string;\n\tconnected: Promise<void>;\n}> => {\n\tlet container: IFluidContainer<typeof containerSchema>;\n\tlet { containerId } = params;\n\tconst { logger, onDisconnected, user, scopes, createScopes, connectTimeoutMs } = params;\n\tconst connectionProps: AzureRemoteConnectionConfig | AzureLocalConnectionConfig = useAzure\n\t\t? {\n\t\t\t\ttenantId,\n\t\t\t\ttokenProvider: createAzureTokenProvider(\n\t\t\t\t\tuser.id ?? \"foo\",\n\t\t\t\t\tuser.name ?? \"bar\",\n\t\t\t\t\tscopes,\n\t\t\t\t\tcreateScopes,\n\t\t\t\t),\n\t\t\t\tendpoint: endPoint,\n\t\t\t\ttype: \"remote\",\n\t\t\t}\n\t\t: {\n\t\t\t\ttokenProvider: new InsecureTokenProvider(\"fooBar\", user, scopes, createScopes),\n\t\t\t\tendpoint: \"http://localhost:7071\",\n\t\t\t\ttype: \"local\",\n\t\t\t};\n\tconst client = new AzureClient({\n\t\tconnection: connectionProps,\n\t\tlogger,\n\t\tconfigProvider: {\n\t\t\tgetRawConfig: (v: string) => {\n\t\t\t\t// At higher client counts, summarizer will get invoked, taking up resources\n\t\t\t\t// and spewing telemetry. None of current tests need summarization, so disable it.\n\t\t\t\tif (v === \"Fluid.ContainerRuntime.Test.DisableSummaries\") {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn undefined;\n\t\t\t},\n\t\t},\n\t});\n\tlet services: AzureContainerServices;\n\tif (containerId === undefined) {\n\t\t({ container, services } = await client.createContainer(containerSchema, \"2\"));\n\t\tcontainerId = await container.attach();\n\t} else {\n\t\t({ container, services } = await client.getContainer(containerId, containerSchema, \"2\"));\n\t}\n\tcontainer.on(\"disconnected\", onDisconnected);\n\n\tconst connected =\n\t\tcontainer.connectionState === ConnectionState.Connected\n\t\t\t? Promise.resolve()\n\t\t\t: timeoutPromise((resolve) => container.once(\"connected\", () => resolve()), {\n\t\t\t\t\tdurationMs: connectTimeoutMs,\n\t\t\t\t\terrorMsg: \"container connect() timeout\",\n\t\t\t\t});\n\n\tassert.strictEqual(\n\t\tcontainer.attachState,\n\t\tAttachState.Attached,\n\t\t\"Container is not attached after attach is called\",\n\t);\n\n\treturn {\n\t\tclient,\n\t\tcontainer,\n\t\tservices,\n\t\tcontainerId,\n\t\tconnected,\n\t};\n};\n\nfunction createSendFunction(): (msg: MessageToParent) => void {\n\tif (process.send) {\n\t\tconst sendFn = process.send.bind(process);\n\t\tif (verbosity.includes(\"msgs\")) {\n\t\t\treturn (msg: MessageToParent) => {\n\t\t\t\tlog(`Sending`, msg);\n\t\t\t\tsendFn(msg);\n\t\t\t};\n\t\t}\n\t\treturn sendFn;\n\t}\n\tthrow new Error(\"process.send is not defined\");\n}\n\nconst send = createSendFunction();\n\nfunction isStringOrNumberRecord(value: unknown): value is Record<string, string | number> {\n\tif (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n\t\treturn false;\n\t}\n\n\tconst stringKeys = Object.keys(value);\n\tconst allKeys = Reflect.ownKeys(value);\n\n\tif (stringKeys.length !== allKeys.length) {\n\t\t// If there are non-string/symbol keys, return false\n\t\treturn false;\n\t}\n\tfor (const key of stringKeys) {\n\t\tif (!(typeof value[key] === \"string\" || typeof value[key] === \"number\")) {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\n// NOTE:\n// - This schema intentionally uses optional keys (latest?, latestMap?) so tests can register\n// states conditionally at runtime.\n// - Optional keys are not explicitly supported in StatesWorkspace typing today, which means\n// workspace.states.<key> is typed as any. As a result, usages below require casts\n// (e.g., to LatestRaw / LatestMapRaw) to recover concrete types.\n// - Track adding proper optional-key support to Presence state workspace typing here:\n// Work item: AB#47518\n// - Fallout: Until the above is addressed, keep the casts in place and document new usages accordingly.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\ntype WorkspaceSchema = {\n\tlatest?: ReturnType<typeof StateFactory.latest<{ value: string }, \"latest\">>;\n\tlatestMap?: ReturnType<\n\t\ttypeof StateFactory.latestMap<\n\t\t\t{ value: Record<string, string | number> },\n\t\t\tstring,\n\t\t\t\"latestMap\"\n\t\t>\n\t>;\n};\nconst WorkspaceSchema: WorkspaceSchema = {};\n\nclass MessageHandler {\n\tprivate readonly log: EventEntry[] = [];\n\tprivate msgQueue: undefined | Exclude<MessageFromParent, { command: \"ping\" | \"connect\" }>[];\n\tprivate container: IFluidContainer | undefined;\n\tprivate presence: Presence | undefined;\n\tprivate readonly workspaces = new Map<string, StatesWorkspace<WorkspaceSchema>>();\n\n\tprivate send(msg: MessageToParent): void {\n\t\tthis.log.push({\n\t\t\ttimestamp: Date.now(),\n\t\t\tagentId: process_id,\n\t\t\teventCategory: \"messageSent\",\n\t\t\teventName: msg.event,\n\t\t\tdetails:\n\t\t\t\tmsg.event === \"debugReportComplete\" && msg.log\n\t\t\t\t\t? JSON.stringify({ logLength: msg.log.length })\n\t\t\t\t\t: JSON.stringify(msg),\n\t\t});\n\t\tsend(msg);\n\t}\n\n\tprivate readonly sendAttendeeConnected = (attendee: Attendee): void => {\n\t\tthis.send({\n\t\t\tevent: \"attendeeConnected\",\n\t\t\tattendeeId: attendee.attendeeId,\n\t\t});\n\t};\n\tprivate readonly sendAttendeeDisconnected = (attendee: Attendee): void => {\n\t\tthis.send({\n\t\t\tevent: \"attendeeDisconnected\",\n\t\t\tattendeeId: attendee.attendeeId,\n\t\t});\n\t};\n\n\tprivate readonly logger: ITelemetryBaseLogger = {\n\t\tsend: (event: ITelemetryBaseEvent, logLevel?: LogLevel) => {\n\t\t\t// Filter out non-interactive client telemetry\n\t\t\tconst clientType = event.clientType;\n\t\t\tif (typeof clientType === \"string\" && clientType.startsWith(\"noninteractive\")) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Special case unexpected telemetry event\n\t\t\tif (event.eventName.endsWith(\":JoinResponseWhenAlone\")) {\n\t\t\t\tthis.send({\n\t\t\t\t\tevent: \"error\",\n\t\t\t\t\terror: `Unexpected ClientJoin response. Details: ${JSON.stringify(event.details)}\\nLog: ${JSON.stringify(this.log)}`,\n\t\t\t\t});\n\t\t\t\t// Keep going\n\t\t\t}\n\n\t\t\tconst interest = telemetryEventInterestLevel(event.eventName);\n\t\t\tif (interest === \"none\") {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.log.push({\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t\tagentId: process_id,\n\t\t\t\teventCategory: \"telemetry\",\n\t\t\t\teventName: event.eventName,\n\t\t\t\tdetails:\n\t\t\t\t\ttypeof event.details === \"string\" ? event.details : JSON.stringify(event.details),\n\t\t\t});\n\t\t\tif (verbosity.includes(\"telem\")) {\n\t\t\t\tselectiveVerboseLog(event, logLevel);\n\t\t\t}\n\t\t},\n\t};\n\n\tprivate readonly onDisconnected = (): void => {\n\t\t// Test state is a bit fragile and does not account for reconnections.\n\t\tthis.send({ event: \"error\", error: `${process_id}: Container disconnected` });\n\t};\n\n\tprivate registerWorkspace(\n\t\tworkspaceId: string,\n\t\toptions: { latest?: boolean; latestMap?: boolean },\n\t): void {\n\t\tif (!this.presence) {\n\t\t\tthis.send({ event: \"error\", error: `${process_id} is not connected to presence` });\n\t\t\treturn;\n\t\t}\n\t\tconst { latest, latestMap } = options;\n\t\tconst workspace: StatesWorkspace<WorkspaceSchema> = this.presence.states.getWorkspace(\n\t\t\t`test:${workspaceId}`,\n\t\t\tWorkspaceSchema,\n\t\t);\n\n\t\tif (latest && !workspace.states.latest) {\n\t\t\tworkspace.add(\"latest\", StateFactory.latest({ local: { value: \"initial\" } }));\n\t\t\t// Cast required due to optional keys in WorkspaceSchema\n\t\t\t// TODO: AB#47518\n\t\t\tconst latestState = workspace.states.latest as LatestRaw<{ value: string }>;\n\t\t\tlatestState.events.on(\"remoteUpdated\", (update) => {\n\t\t\t\tthis.send({\n\t\t\t\t\tevent: \"latestValueUpdated\",\n\t\t\t\t\tworkspaceId,\n\t\t\t\t\tattendeeId: update.attendee.attendeeId,\n\t\t\t\t\tvalue: update.value.value,\n\t\t\t\t});\n\t\t\t});\n\t\t\tfor (const remote of latestState.getRemotes()) {\n\t\t\t\tthis.send({\n\t\t\t\t\tevent: \"latestValueUpdated\",\n\t\t\t\t\tworkspaceId,\n\t\t\t\t\tattendeeId: remote.attendee.attendeeId,\n\t\t\t\t\tvalue: remote.value.value,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tif (latestMap && !workspace.states.latestMap) {\n\t\t\tworkspace.add(\n\t\t\t\t\"latestMap\",\n\t\t\t\tStateFactory.latestMap<{ value: Record<string, string | number> }>({\n\t\t\t\t\tlocal: {},\n\t\t\t\t}),\n\t\t\t);\n\t\t\t// Cast required due to optional keys in WorkspaceSchema\n\t\t\t// TODO: AB#47518\n\t\t\tconst latestMapState = workspace.states.latestMap as LatestMapRaw<{\n\t\t\t\tvalue: Record<string, string | number>;\n\t\t\t}>;\n\t\t\tlatestMapState.events.on(\"remoteUpdated\", (update) => {\n\t\t\t\tfor (const [key, valueWithMetadata] of update.items) {\n\t\t\t\t\tthis.send({\n\t\t\t\t\t\tevent: \"latestMapValueUpdated\",\n\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\tattendeeId: update.attendee.attendeeId,\n\t\t\t\t\t\tkey: String(key),\n\t\t\t\t\t\tvalue: valueWithMetadata.value.value,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\t\t\tfor (const remote of latestMapState.getRemotes()) {\n\t\t\t\tfor (const [key, valueWithMetadata] of remote.items) {\n\t\t\t\t\tthis.send({\n\t\t\t\t\t\tevent: \"latestMapValueUpdated\",\n\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\tattendeeId: remote.attendee.attendeeId,\n\t\t\t\t\t\tkey: String(key),\n\t\t\t\t\t\tvalue: valueWithMetadata.value.value,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis.workspaces.set(workspaceId, workspace);\n\t\tthis.send({\n\t\t\tevent: \"workspaceRegistered\",\n\t\t\tworkspaceId,\n\t\t\tlatest: latest ?? false,\n\t\t\tlatestMap: latestMap ?? false,\n\t\t});\n\t}\n\n\tpublic async onMessage(msg: MessageFromParent): Promise<void> {\n\t\tif (verbosity.includes(\"msgs\")) {\n\t\t\tthis.log.push({\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t\tagentId: process_id,\n\t\t\t\teventCategory: \"messageReceived\",\n\t\t\t\teventName: msg.command,\n\t\t\t});\n\t\t\tlog(`Received`, msg);\n\t\t}\n\n\t\tif (msg.command === \"ping\") {\n\t\t\tthis.handlePing();\n\t\t\treturn;\n\t\t}\n\n\t\tif (msg.command === \"connect\") {\n\t\t\tawait this.handleConnect(msg);\n\t\t\treturn;\n\t\t}\n\n\t\t// All other message must wait if connect is in progress\n\t\tif (this.msgQueue !== undefined) {\n\t\t\tthis.msgQueue.push(msg);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.processMessage(msg);\n\t}\n\n\tprivate processMessage(\n\t\tmsg: Exclude<MessageFromParent, { command: \"ping\" | \"connect\" }>,\n\t): void {\n\t\tswitch (msg.command) {\n\t\t\tcase \"debugReport\": {\n\t\t\t\tthis.handleDebugReport(msg);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"disconnectSelf\": {\n\t\t\t\tthis.handleDisconnectSelf();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"setLatestValue\": {\n\t\t\t\tthis.handleSetLatestValue(msg);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"setLatestMapValue\": {\n\t\t\t\tthis.handleSetLatestMapValue(msg);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"getLatestValue\": {\n\t\t\t\tthis.handleGetLatestValue(msg);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"getLatestMapValue\": {\n\t\t\t\tthis.handleGetLatestMapValue(msg);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"registerWorkspace\": {\n\t\t\t\tthis.registerWorkspace(msg.workspaceId, {\n\t\t\t\t\tlatest: msg.latest,\n\t\t\t\t\tlatestMap: msg.latestMap,\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tconsole.error(`${process_id}: Unknown command:`, msg);\n\t\t\t\tthis.send({\n\t\t\t\t\tevent: \"error\",\n\t\t\t\t\terror: `${process_id} Unknown command: ${JSON.stringify(msg)}`,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate handlePing(): void {\n\t\tthis.send({ event: \"ack\" });\n\t}\n\n\tprivate async handleConnect(\n\t\tmsg: Extract<MessageFromParent, { command: \"connect\" }>,\n\t): Promise<void> {\n\t\tif (!msg.user) {\n\t\t\tthis.send({ event: \"error\", error: `${process_id}: No azure user information given` });\n\t\t\treturn;\n\t\t}\n\t\tif (this.container) {\n\t\t\tthis.send({ event: \"error\", error: `${process_id}: Container already loaded` });\n\t\t\treturn;\n\t\t}\n\n\t\t// Prevent reentrance. Queue messages until after connect is fully processed.\n\t\tthis.msgQueue = [];\n\n\t\ttry {\n\t\t\tconst { container, containerId, connected } = await getOrCreateContainer({\n\t\t\t\t...msg,\n\t\t\t\tlogger: this.logger,\n\t\t\t\tonDisconnected: this.onDisconnected,\n\t\t\t});\n\t\t\tthis.container = container;\n\t\t\tconst presence = getPresence(container);\n\t\t\tthis.presence = presence;\n\n\t\t\t// wait for 'ConnectionState.Connected'\n\t\t\tawait connected.catch((error) => {\n\t\t\t\t(error as Error).message += `\\nLog: ${JSON.stringify(this.log)}`;\n\t\t\t\tthrow error;\n\t\t\t});\n\n\t\t\t// Acknowledge connection before sending current attendee information\n\t\t\tthis.send({\n\t\t\t\tevent: \"connected\",\n\t\t\t\tcontainerId,\n\t\t\t\tattendeeId: presence.attendees.getMyself().attendeeId,\n\t\t\t});\n\n\t\t\t// Send existing attendees excluding self to parent/orchestrator\n\t\t\tconst self = presence.attendees.getMyself();\n\t\t\tfor (const attendee of presence.attendees.getAttendees()) {\n\t\t\t\tif (attendee !== self && attendee.getConnectionStatus() === \"Connected\") {\n\t\t\t\t\tthis.sendAttendeeConnected(attendee);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Listen for presence events to notify parent/orchestrator when a new attendee joins or leaves the session.\n\t\t\tpresence.attendees.events.on(\"attendeeConnected\", this.sendAttendeeConnected);\n\t\t\tpresence.attendees.events.on(\"attendeeDisconnected\", this.sendAttendeeDisconnected);\n\t\t} finally {\n\t\t\t// Process any queued messages received while connecting\n\t\t\tfor (const queuedMsg of this.msgQueue) {\n\t\t\t\tthis.processMessage(queuedMsg);\n\t\t\t}\n\t\t\tthis.msgQueue = undefined;\n\t\t}\n\t}\n\n\tprivate handleDebugReport(\n\t\tmsg: Extract<MessageFromParent, { command: \"debugReport\" }>,\n\t): void {\n\t\tif (msg.reportAttendees) {\n\t\t\tif (this.presence) {\n\t\t\t\tconst attendees = this.presence.attendees.getAttendees();\n\t\t\t\tlet connectedCount = 0;\n\t\t\t\tfor (const attendee of attendees) {\n\t\t\t\t\tif (attendee.getConnectionStatus() === \"Connected\") {\n\t\t\t\t\t\tconnectedCount++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tlog(`Report: ${attendees.size} attendees, ${connectedCount} connected`);\n\t\t\t} else {\n\t\t\t\tthis.send({ event: \"error\", error: `${process_id} is not connected to presence` });\n\t\t\t}\n\t\t}\n\n\t\tconst debugReport: Extract<MessageToParent, { event: \"debugReportComplete\" }> = {\n\t\t\tevent: \"debugReportComplete\",\n\t\t};\n\t\tif (msg.sendEventLog) {\n\t\t\tdebugReport.log = this.log;\n\t\t}\n\t\tthis.send(debugReport);\n\t}\n\n\tprivate handleDisconnectSelf(): void {\n\t\tif (!this.container) {\n\t\t\tthis.send({ event: \"error\", error: `${process_id} is not connected to container` });\n\t\t\treturn;\n\t\t}\n\t\t// There are no current scenarios where disconnect without presence is expected.\n\t\tif (!this.presence) {\n\t\t\tthis.send({ event: \"error\", error: `${process_id} is not connected to presence` });\n\t\t\treturn;\n\t\t}\n\t\t// Disconnect event is treated as an error in normal handling.\n\t\t// Remove listener as this disconnect is intentional.\n\t\tthis.container.off(\"disconnected\", this.onDisconnected);\n\t\tthis.container.disconnect();\n\t\tthis.send({\n\t\t\tevent: \"disconnectedSelf\",\n\t\t\tattendeeId: this.presence.attendees.getMyself().attendeeId,\n\t\t});\n\t}\n\n\tprivate handleSetLatestValue(\n\t\tmsg: Extract<MessageFromParent, { command: \"setLatestValue\" }>,\n\t): void {\n\t\tif (!this.presence) {\n\t\t\tthis.send({ event: \"error\", error: `${process_id} is not connected to presence` });\n\t\t\treturn;\n\t\t}\n\t\tconst workspace = this.workspaces.get(msg.workspaceId);\n\t\tif (!workspace) {\n\t\t\tthis.send({\n\t\t\t\tevent: \"error\",\n\t\t\t\terror: `${process_id} workspace ${msg.workspaceId} not found`,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\t// Cast required due to optional keys in WorkspaceSchema\n\t\t// TODO: AB#47518\n\t\tconst latestState = workspace.states.latest as LatestRaw<{ value: string }> | undefined;\n\t\tif (!latestState) {\n\t\t\tthis.send({\n\t\t\t\tevent: \"error\",\n\t\t\t\terror: `${process_id} latest state not registered for workspace ${msg.workspaceId}`,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tif (typeof msg.value !== \"string\") {\n\t\t\treturn;\n\t\t}\n\t\tlatestState.local = { value: msg.value };\n\t}\n\n\tprivate handleSetLatestMapValue(\n\t\tmsg: Extract<MessageFromParent, { command: \"setLatestMapValue\" }>,\n\t): void {\n\t\tif (!this.presence) {\n\t\t\tthis.send({ event: \"error\", error: `${process_id} is not connected to presence` });\n\t\t\treturn;\n\t\t}\n\t\tif (typeof msg.key !== \"string\") {\n\t\t\tthis.send({ event: \"error\", error: `${process_id} invalid key type` });\n\t\t\treturn;\n\t\t}\n\t\tconst workspace = this.workspaces.get(msg.workspaceId);\n\t\tif (!workspace) {\n\t\t\tthis.send({\n\t\t\t\tevent: \"error\",\n\t\t\t\terror: `${process_id} workspace ${msg.workspaceId} not found`,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\t// Cast required due to optional keys in WorkspaceSchema\n\t\t// TODO: AB#47518\n\t\tconst latestMapState = workspace.states.latestMap as\n\t\t\t| LatestMapRaw<{ value: Record<string, string | number> }>\n\t\t\t| undefined;\n\t\tif (!latestMapState) {\n\t\t\tthis.send({\n\t\t\t\tevent: \"error\",\n\t\t\t\terror: `${process_id} latestMap state not registered for workspace ${msg.workspaceId}`,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tif (!isStringOrNumberRecord(msg.value)) {\n\t\t\treturn;\n\t\t}\n\t\tlatestMapState.local.set(msg.key, { value: msg.value });\n\t}\n\n\tprivate handleGetLatestValue(\n\t\tmsg: Extract<MessageFromParent, { command: \"getLatestValue\" }>,\n\t): void {\n\t\tif (!this.presence) {\n\t\t\tthis.send({ event: \"error\", error: `${process_id} is not connected to presence` });\n\t\t\treturn;\n\t\t}\n\t\tconst workspace = this.workspaces.get(msg.workspaceId);\n\t\tif (!workspace) {\n\t\t\tthis.send({\n\t\t\t\tevent: \"error\",\n\t\t\t\terror: `${process_id} workspace ${msg.workspaceId} not found`,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\t// Cast required due to optional keys in WorkspaceSchema\n\t\t// TODO: AB#47518\n\t\tconst latestState = workspace.states.latest as LatestRaw<{ value: string }> | undefined;\n\t\tif (!latestState) {\n\t\t\tthis.send({\n\t\t\t\tevent: \"error\",\n\t\t\t\terror: `${process_id} latest state not registered for workspace ${msg.workspaceId}`,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tlet value: { value: string };\n\t\tif (msg.attendeeId) {\n\t\t\tconst attendee = this.presence.attendees.getAttendee(msg.attendeeId);\n\t\t\tconst remoteData = latestState.getRemote(attendee);\n\t\t\tvalue = remoteData.value;\n\t\t} else {\n\t\t\tvalue = latestState.local;\n\t\t}\n\t\tthis.send({\n\t\t\tevent: \"latestValueGetResponse\",\n\t\t\tworkspaceId: msg.workspaceId,\n\t\t\tattendeeId: msg.attendeeId,\n\t\t\tvalue: value.value,\n\t\t});\n\t}\n\n\tprivate handleGetLatestMapValue(\n\t\tmsg: Extract<MessageFromParent, { command: \"getLatestMapValue\" }>,\n\t): void {\n\t\tif (!this.presence) {\n\t\t\tthis.send({ event: \"error\", error: `${process_id} is not connected to presence` });\n\t\t\treturn;\n\t\t}\n\t\tif (typeof msg.key !== \"string\") {\n\t\t\tthis.send({ event: \"error\", error: `${process_id} invalid key type` });\n\t\t\treturn;\n\t\t}\n\t\tconst workspace = this.workspaces.get(msg.workspaceId);\n\t\tif (!workspace) {\n\t\t\tthis.send({\n\t\t\t\tevent: \"error\",\n\t\t\t\terror: `${process_id} workspace ${msg.workspaceId} not found`,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\t// Cast required due to optional keys in WorkspaceSchema\n\t\t// TODO: AB#47518\n\t\tconst latestMapState = workspace.states.latestMap as\n\t\t\t| LatestMapRaw<{ value: Record<string, string | number> }>\n\t\t\t| undefined;\n\t\tif (!latestMapState) {\n\t\t\tthis.send({\n\t\t\t\tevent: \"error\",\n\t\t\t\terror: `${process_id} latestMap state not registered for workspace ${msg.workspaceId}`,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tlet value: { value: Record<string, string | number> } | undefined;\n\t\tif (msg.attendeeId) {\n\t\t\tconst attendee = this.presence.attendees.getAttendee(msg.attendeeId);\n\t\t\tconst remoteData = latestMapState.getRemote(attendee);\n\t\t\tconst keyData = remoteData.get(msg.key);\n\t\t\tvalue = keyData?.value;\n\t\t} else {\n\t\t\tvalue = latestMapState.local.get(msg.key);\n\t\t}\n\t\tthis.send({\n\t\t\tevent: \"latestMapValueGetResponse\",\n\t\t\tworkspaceId: msg.workspaceId,\n\t\t\tattendeeId: msg.attendeeId,\n\t\t\tkey: msg.key,\n\t\t\tvalue: value?.value,\n\t\t});\n\t}\n}\n\nfunction setupMessageHandler(): void {\n\tconst messageHandler = new MessageHandler();\n\tprocess.on(\"message\", (msg: MessageFromParent) => {\n\t\tmessageHandler.onMessage(msg).catch((error: Error) => {\n\t\t\tconsole.error(`[${testLabel}] Error in client ${process_id}`, error);\n\t\t\tsend({ event: \"error\", error: `${process_id}: ${error.message}` });\n\t\t});\n\t});\n}\n\nsetupMessageHandler();\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messageTypes.js","sourceRoot":"","sources":["../../../src/test/multiprocess/messageTypes.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n// eslint-disable-next-line import-x/no-internal-modules\nimport type { JsonSerializable } from \"@fluidframework/core-interfaces/internal\";\nimport type { ScopeType } from \"@fluidframework/driver-definitions/legacy\";\nimport type { AttendeeId } from \"@fluidframework/presence
|
|
1
|
+
{"version":3,"file":"messageTypes.js","sourceRoot":"","sources":["../../../src/test/multiprocess/messageTypes.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n// eslint-disable-next-line import-x/no-internal-modules\nimport type { JsonSerializable } from \"@fluidframework/core-interfaces/internal\";\nimport type { ScopeType } from \"@fluidframework/driver-definitions/legacy\";\nimport type { AttendeeId } from \"@fluidframework/presence\";\n\nexport interface UserIdAndName {\n\tid: string;\n\tname: string;\n}\n\nexport interface EventEntry {\n\ttimestamp: number;\n\tagentId: string;\n\teventCategory: string;\n\teventName: string;\n\tdetails?: string;\n}\n\n/**\n * Message types sent from the orchestrator to the child processes\n */\nexport type MessageToChild =\n\t| ConnectCommand\n\t| DebugReportCommand\n\t| DisconnectSelfCommand\n\t| RegisterWorkspaceCommand\n\t| GetLatestValueCommand\n\t| GetLatestMapValueCommand\n\t| SetLatestValueCommand\n\t| SetLatestMapValueCommand\n\t| PingCommand;\n\n/**\n * Can be sent to check child responsiveness.\n * An {@link AcknowledgeEvent} should be expected in response.\n */\ninterface PingCommand {\n\tcommand: \"ping\";\n}\n\n/**\n * Instructs a child process to connect to a Fluid container.\n * A {@link ConnectedEvent} should be expected in response.\n */\nexport interface ConnectCommand {\n\tcommand: \"connect\";\n\tuser: UserIdAndName;\n\tscopes: ScopeType[];\n\tcreateScopes?: ScopeType[];\n\t/**\n\t * The ID of the Fluid container to connect to.\n\t * If not provided, a new Fluid container will be created.\n\t */\n\tcontainerId?: string;\n\tconnectTimeoutMs: number;\n}\n\n/**\n * Instructs a child process to report debug information.\n *\n * @privateRemarks\n * This can be expanded over time to include more options.\n */\ninterface DebugReportCommand {\n\tcommand: \"debugReport\";\n\t/**\n\t * Send event log entries.\n\t */\n\tsendEventLog?: true;\n\t/**\n\t * Send basic attendee statistics (like count of connected).\n\t */\n\treportAttendees?: true;\n}\n\n/**\n * Instructs a child process to disconnect from a Fluid container.\n * A {@link DisconnectedSelfEvent} should be expected in response.\n */\ninterface DisconnectSelfCommand {\n\tcommand: \"disconnectSelf\";\n}\n\n/**\n * Instructs a child process to register for state objects in a workspace given a workspaceId\n * A {@link WorkspaceRegisteredEvent} should be expected in response.\n */\ninterface RegisterWorkspaceCommand {\n\tcommand: \"registerWorkspace\";\n\tworkspaceId: string;\n\t/**\n\t * Register a Latest state for this workspace.\n\t */\n\tlatest?: true;\n\t/**\n\t * Register a LatestMap state for this workspace.\n\t */\n\tlatestMap?: true;\n}\n\n/**\n * Instructs a child process to set the latest value.\n * We then can wait for {@link LatestValueUpdatedEvent} from other clients to know when an update occurs that represents this change.\n * Note: The client doesn't guarantee that the update message is directly related to this set command.\n */\ninterface SetLatestValueCommand {\n\tcommand: \"setLatestValue\";\n\tworkspaceId: string;\n\tvalue: JsonSerializable<unknown>;\n}\n\n/**\n * Instructs a child process to set the latest map value.\n * We then can wait for {@link LatestMapValueUpdatedEvent} from other clients to know when an update occurs that represents this change.\n * Note: The client doesn't guarantee that the update message is directly related to this set command.\n */\ninterface SetLatestMapValueCommand {\n\tcommand: \"setLatestMapValue\";\n\tworkspaceId: string;\n\tkey: string;\n\tvalue: JsonSerializable<unknown>;\n}\n\n/**\n * Instructs a child process to get the latest value.\n * A {@link LatestValueGetResponseEvent} should be expected in response.\n */\ninterface GetLatestValueCommand {\n\tcommand: \"getLatestValue\";\n\tworkspaceId: string;\n\tattendeeId?: AttendeeId;\n}\n\n/**\n * Instructs a child process to get the latest map value.\n * A {@link LatestMapValueGetResponseEvent} should be expected in response.\n */\ninterface GetLatestMapValueCommand {\n\tcommand: \"getLatestMapValue\";\n\tworkspaceId: string;\n\tkey: string;\n\tattendeeId?: AttendeeId;\n}\n\n/**\n * Message types sent from the child processes to the orchestrator\n */\nexport type MessageFromChild =\n\t| AcknowledgeEvent\n\t| AttendeeConnectedEvent\n\t| AttendeeDisconnectedEvent\n\t| ConnectedEvent\n\t| DebugReportCompleteEvent\n\t| DisconnectedSelfEvent\n\t| ErrorEvent\n\t| LatestMapValueGetResponseEvent\n\t| LatestMapValueUpdatedEvent\n\t| LatestValueGetResponseEvent\n\t| LatestValueUpdatedEvent\n\t| WorkspaceRegisteredEvent;\n\n/**\n * Sent from the child processes to the orchestrator in response to a {@link PingCommand}.\n */\ninterface AcknowledgeEvent {\n\tevent: \"ack\";\n}\n\n/**\n * Sent arbitrarily to indicate a new attendee has connected.\n */\ninterface AttendeeConnectedEvent {\n\tevent: \"attendeeConnected\";\n\tattendeeId: AttendeeId;\n}\n\n/**\n * Sent arbitrarily to indicate an attendee has disconnected.\n */\ninterface AttendeeDisconnectedEvent {\n\tevent: \"attendeeDisconnected\";\n\tattendeeId: AttendeeId;\n}\n\n/**\n * Sent from the child processes to the orchestrator in response to a {@link ConnectCommand}.\n */\ninterface ConnectedEvent {\n\tevent: \"connected\";\n\tcontainerId: string;\n\tattendeeId: AttendeeId;\n}\n\n/**\n * Sent from the child processes to the orchestrator in response to a {@link DebugReportCommand}.\n */\ninterface DebugReportCompleteEvent {\n\tevent: \"debugReportComplete\";\n\tlog?: EventEntry[];\n}\n\n/**\n * Sent from the child processes to the orchestrator in response to a {@link DisconnectSelfCommand}.\n */\ninterface DisconnectedSelfEvent {\n\tevent: \"disconnectedSelf\";\n\tattendeeId: AttendeeId;\n}\n\n/**\n * Sent from the child processes to the orchestrator in response to latest value update.\n */\nexport interface LatestValueUpdatedEvent {\n\tevent: \"latestValueUpdated\";\n\tworkspaceId: string;\n\tattendeeId: AttendeeId;\n\tvalue: unknown;\n}\n\n/**\n * Sent from the child processes to the orchestrator in response to latest map value update.\n */\nexport interface LatestMapValueUpdatedEvent {\n\tevent: \"latestMapValueUpdated\";\n\tworkspaceId: string;\n\tattendeeId: AttendeeId;\n\tkey: string;\n\tvalue: unknown;\n}\n\n/**\n * Sent from the child processes to the orchestrator in response to a {@link GetLatestValueCommand}.\n */\nexport interface LatestValueGetResponseEvent {\n\tevent: \"latestValueGetResponse\";\n\tworkspaceId: string;\n\tattendeeId: AttendeeId | undefined;\n\tvalue: unknown;\n}\n\n/**\n * Sent from the child processes to the orchestrator in response to a {@link GetLatestMapValueCommand}.\n */\nexport interface LatestMapValueGetResponseEvent {\n\tevent: \"latestMapValueGetResponse\";\n\tworkspaceId: string;\n\tattendeeId: AttendeeId | undefined;\n\tkey: string;\n\tvalue: unknown;\n}\n\n/**\n * Sent from the child process to acknowledge workspace registration.\n */\ninterface WorkspaceRegisteredEvent {\n\tevent: \"workspaceRegistered\";\n\tworkspaceId: string;\n\tlatest?: boolean;\n\tlatestMap?: boolean;\n}\n\n/**\n * Sent at any time to indicate an error.\n */\ninterface ErrorEvent {\n\tevent: \"error\";\n\terror: string;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestratorUtils.js","sourceRoot":"","sources":["../../../src/test/multiprocess/orchestratorUtils.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { fork } from \"node:child_process\";\nimport type { ChildProcess as AnyChildProcess } from \"node:child_process\";\n\nimport { ScopeType } from \"@fluidframework/driver-definitions/legacy\";\nimport type { AttendeeId } from \"@fluidframework/presence/beta\";\nimport { timeoutAwait, timeoutPromise } from \"@fluidframework/test-utils/internal\";\n\nimport type {\n\tConnectCommand,\n\tMessageFromChild,\n\tLatestValueUpdatedEvent,\n\tLatestMapValueUpdatedEvent,\n\tLatestValueGetResponseEvent,\n\tLatestMapValueGetResponseEvent,\n\tMessageToChild,\n\tEventEntry,\n} from \"./messageTypes.js\";\n\n/**\n * Child process to console logging verbosity\n *\n * @remarks\n * Meaningful substrings:\n * - \"msgs\"\n * - \"telem\"\n *\n * @example \"msgs+telem\"\n */\nconst childLoggingVerbosity = process.env.FLUID_TEST_VERBOSE ?? \"none\";\n\n/**\n * Capture console./warn/error before test infrastructure alters it.\n */\nexport const testConsole = {\n\tlog: console.log,\n\twarn: console.warn,\n\terror: console.error,\n};\n\ninterface ChildProcess extends AnyChildProcess {\n\tsend(message: MessageToChild): boolean;\n}\n\n/**\n * Fork child processes to simulate multiple Fluid clients.\n *\n * @remarks\n * Individual child processes may be scheduled concurrently on a multi-core CPU\n * and separate processes will never share a port when connected to a service.\n *\n * @param numProcesses - The number of child processes to fork.\n * @param cleanUpAccumulator - An array to accumulate cleanup functions for each child.\n * @returns A collection of child processes and a promise that rejects on the first child error.\n */\nexport async function forkChildProcesses(\n\ttestLabel: string,\n\tnumProcesses: number,\n\tcleanUpAccumulator: (() => void)[],\n): Promise<{\n\tchildren: ChildProcess[];\n\t/**\n\t * Will never resolve successfully, it is only used to reject on child process error.\n\t */\n\tchildErrorPromise: Promise<never>;\n}> {\n\tconst children: ChildProcess[] = [];\n\tconst childReadyPromises: Promise<void>[] = [];\n\tconst childErrorPromises: Promise<never>[] = [];\n\tfor (let i = 0; i < numProcesses; i++) {\n\t\tconst child = fork(\"./lib/test/multiprocess/childClient.tool.js\", [\n\t\t\ttestLabel,\n\t\t\t`child ${i}` /* identifier passed to child process */,\n\t\t\tchildLoggingVerbosity /* console logging verbosity */,\n\t\t]);\n\t\tcleanUpAccumulator.push(() => {\n\t\t\tchild.kill();\n\t\t\tchild.removeAllListeners();\n\t\t});\n\t\tconst readyPromise = new Promise<void>((resolve, reject) => {\n\t\t\tchild.once(\"message\", (msg: MessageFromChild) => {\n\t\t\t\tif (msg.event === \"ack\") {\n\t\t\t\t\tresolve();\n\t\t\t\t} else {\n\t\t\t\t\treject(\n\t\t\t\t\t\tnew Error(`Unexpected (non-\"ack\") message from child${i}: ${JSON.stringify(msg)}`),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t\tchildReadyPromises.push(readyPromise);\n\t\tconst errorPromise = new Promise<never>((_resolve, reject) => {\n\t\t\tchild.once(\"error\", (error) => {\n\t\t\t\treject(new Error(`Child${i} process errored: ${error.message}`));\n\t\t\t});\n\t\t\tchild.once(\"exit\", (code, signal) => {\n\t\t\t\treject(new Error(`Child${i} process exited: code ${code}, signal ${signal}`));\n\t\t\t});\n\t\t});\n\t\tchildErrorPromises.push(errorPromise);\n\t\tchild.send({ command: \"ping\" });\n\t\tchildren.push(child);\n\t}\n\tconst childErrorPromise = Promise.race(childErrorPromises);\n\tawait Promise.race([Promise.all(childReadyPromises), childErrorPromise]);\n\treturn { children, childErrorPromise };\n}\n\n/**\n * Instructs all listed child processes to send debug reports and then the\n * collection is output sorted by timestamp. Report content is up to the child\n * processes, but typically includes messages sent and some telemetry events.\n */\nexport async function executeDebugReports(\n\tchildrenRequestedToReport: ChildProcess[],\n): Promise<void> {\n\tconst debugReportPromises: Promise<EventEntry[]>[] = [];\n\tfor (const child of childrenRequestedToReport) {\n\t\tconst debugReportPromise = new Promise<EventEntry[]>((resolve) => {\n\t\t\tconst handler = (msg: MessageFromChild): void => {\n\t\t\t\tif (msg.event === \"debugReportComplete\") {\n\t\t\t\t\tresolve(msg.log ?? []);\n\t\t\t\t\tchild.off(\"message\", handler);\n\t\t\t\t}\n\t\t\t};\n\t\t\tchild.on(\"message\", handler);\n\t\t});\n\t\tdebugReportPromises.push(debugReportPromise);\n\t\tchild.send({ command: \"debugReport\", sendEventLog: true, reportAttendees: true });\n\t}\n\n\tconst logs = await Promise.all(debugReportPromises);\n\tconst combinedLogs = logs.flat().sort((a, b) => a.timestamp - b.timestamp);\n\tfor (const entry of combinedLogs) {\n\t\ttestConsole.log(\n\t\t\t`[${new Date(entry.timestamp).toISOString()}] [${entry.agentId}] [${entry.eventCategory}] ${entry.eventName}${\n\t\t\t\tentry.details\n\t\t\t\t\t? ` - ${typeof entry.details === \"string\" ? entry.details : JSON.stringify(entry.details)}`\n\t\t\t\t\t: \"\"\n\t\t\t}`,\n\t\t);\n\t}\n}\n\n/**\n * Creates a {@link ConnectCommand} for a test user with a deterministic id and name.\n *\n * @param id - Suffix used to construct stable test user identity.\n */\nfunction composeConnectMessage(\n\tid: string | number,\n\tscopes: ScopeType[] = [ScopeType.DocRead],\n\tconnectTimeoutMs: number,\n): ConnectCommand {\n\treturn {\n\t\tcommand: \"connect\",\n\t\tuser: {\n\t\t\tid: `test-user-id-${id}`,\n\t\t\tname: `test-user-name-${id}`,\n\t\t},\n\t\tscopes,\n\t\tcreateScopes: [ScopeType.DocWrite, ScopeType.DocRead],\n\t\tconnectTimeoutMs,\n\t};\n}\n\n/**\n * Listens for a \"connected\" response from a child process\n * allowing/handling subset of other expected messages.\n */\nfunction listenForConnectedResponse({\n\tchild,\n\tchildId,\n\tonConnected,\n\treject,\n}: {\n\tchild: ChildProcess;\n\tchildId: number | string;\n\t/**\n\t * Will be called up to once when a \"connected\" message is received.\n\t */\n\tonConnected: (msg: Extract<MessageFromChild, { event: \"connected\" }>) => void;\n\t/**\n\t * Callback to reject for unexpected messages or child errors.\n\t */\n\treject: (reason?: unknown) => void;\n}): void {\n\tconst listener = (msg: MessageFromChild): void => {\n\t\tif (msg.event === \"connected\") {\n\t\t\tchild.off(\"message\", listener);\n\t\t\tonConnected(msg);\n\t\t} else if (msg.event === \"error\") {\n\t\t\tchild.off(\"message\", listener);\n\t\t\treject(new Error(`Child ${childId} process error: ${msg.error}`));\n\t\t} else if (msg.event !== \"ack\") {\n\t\t\tchild.off(\"message\", listener);\n\t\t\t// This is not strictly required, but is current expectation.\n\t\t\treject(\n\t\t\t\tnew Error(\n\t\t\t\t\t`Unexpected message from child ${childId} while connecting: ${JSON.stringify(msg)}`,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t};\n\tchild.on(\"message\", listener);\n}\n\ninterface CreatorAttendeeIdAndAttendeePromises {\n\tcontainerCreatorAttendeeId: AttendeeId;\n\tattendeeIdPromises: Promise<AttendeeId>[];\n}\n\n/**\n * Sends connect commands to the provided child processes.\n *\n * The first child will create the container unless a containerId is pre-specified; subsequent\n * children are sent the discovered containerId.\n */\nexport async function connectChildProcesses(\n\tchildProcesses: ChildProcess[],\n\t{ writeClients, readyTimeoutMs }: { writeClients: number; readyTimeoutMs: number },\n): Promise<CreatorAttendeeIdAndAttendeePromises> {\n\tif (childProcesses.length === 0) {\n\t\tthrow new Error(\"No child processes provided for connection.\");\n\t}\n\tconst firstChild = childProcesses[0];\n\tconst containerReadyPromise = new Promise<{\n\t\tcontainerCreatorAttendeeId: AttendeeId;\n\t\tcontainerId: string;\n\t}>((resolve, reject) =>\n\t\tlistenForConnectedResponse({\n\t\t\tchild: firstChild,\n\t\t\tchildId: 0,\n\t\t\tonConnected: (msg) => {\n\t\t\t\tif (msg.containerId) {\n\t\t\t\t\tresolve({\n\t\t\t\t\t\tcontainerCreatorAttendeeId: msg.attendeeId,\n\t\t\t\t\t\tcontainerId: msg.containerId,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\treject(\n\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t`Child 0 (creator) connected without containerId: ${JSON.stringify(msg)}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t},\n\t\t\treject,\n\t\t}),\n\t);\n\t{\n\t\t// Note that DocWrite is used to have this attendee be the \"leader\".\n\t\t// DocRead would also be valid as DocWrite is specified for attach when there\n\t\t// is no document id (container id).\n\t\tconst connectContainerCreator = composeConnectMessage(\n\t\t\t0,\n\t\t\twriteClients > 0 ? [ScopeType.DocWrite, ScopeType.DocRead] : [ScopeType.DocRead],\n\t\t\t/* connectTimeoutMs */ readyTimeoutMs,\n\t\t);\n\t\tfirstChild.send(connectContainerCreator);\n\t}\n\tconst { containerCreatorAttendeeId, containerId } = await timeoutAwait(\n\t\tcontainerReadyPromise,\n\t\t{\n\t\t\tdurationMs: readyTimeoutMs,\n\t\t\terrorMsg: \"did not receive 'connected' from child process\",\n\t\t},\n\t).catch(async (error) => {\n\t\tawait executeDebugReports([firstChild]);\n\t\tthrow error;\n\t});\n\n\tconst attendeeIdPromises: Promise<AttendeeId>[] = [];\n\tfor (const [index, child] of childProcesses.entries()) {\n\t\tif (index === 0) {\n\t\t\tattendeeIdPromises.push(Promise.resolve(containerCreatorAttendeeId));\n\t\t\tcontinue;\n\t\t}\n\t\tconst message = composeConnectMessage(\n\t\t\tindex,\n\t\t\tindex < writeClients ? [ScopeType.DocWrite, ScopeType.DocRead] : [ScopeType.DocRead],\n\t\t\t/* connectTimeoutMs */ readyTimeoutMs,\n\t\t);\n\t\tmessage.containerId = containerId;\n\t\tattendeeIdPromises.push(\n\t\t\tnew Promise<AttendeeId>((resolve, reject) =>\n\t\t\t\tlistenForConnectedResponse({\n\t\t\t\t\tchild,\n\t\t\t\t\tchildId: index,\n\t\t\t\t\tonConnected: (msg) => {\n\t\t\t\t\t\tresolve(msg.attendeeId);\n\t\t\t\t\t},\n\t\t\t\t\treject,\n\t\t\t\t}),\n\t\t\t),\n\t\t);\n\t\tchild.send(message);\n\t}\n\treturn { containerCreatorAttendeeId, attendeeIdPromises };\n}\n\ninterface ConnectAndListenForAttendees extends CreatorAttendeeIdAndAttendeePromises {\n\tattendeeCountRequiredPromises: Promise<void>[];\n}\n\n/**\n * Connects the child processes and creates promises for the specified number of\n * attendees to connect.\n */\nexport async function connectAndListenForAttendees(\n\tchildren: ChildProcess[],\n\t{\n\t\twriteClients,\n\t\tattendeeCountRequired,\n\t\tchildConnectTimeoutMs,\n\t}: {\n\t\t/**\n\t\t * The number of clients that should have write access.\n\t\t */\n\t\twriteClients: number;\n\t\t/**\n\t\t * The number of attendees that must connect.\n\t\t */\n\t\tattendeeCountRequired: number;\n\t\t/**\n\t\t * Timeout duration for child process connections.\n\t\t */\n\t\tchildConnectTimeoutMs: number;\n\t},\n): Promise<ConnectAndListenForAttendees> {\n\t// Setup\n\tconst attendeeCountRequiredPromises = children.map(\n\t\t// eslint-disable-next-line @typescript-eslint/promise-function-async\n\t\t(child) =>\n\t\t\tnew Promise<void>((resolve) => {\n\t\t\t\tlet attendeesJoinedEvents = 0;\n\t\t\t\tconst listenForAttendees = (msg: MessageFromChild): void => {\n\t\t\t\t\tif (msg.event === \"attendeeConnected\") {\n\t\t\t\t\t\tattendeesJoinedEvents++;\n\t\t\t\t\t\tif (attendeesJoinedEvents >= attendeeCountRequired) {\n\t\t\t\t\t\t\tchild.off(\"message\", listenForAttendees);\n\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tchild.on(\"message\", listenForAttendees);\n\t\t\t}),\n\t);\n\n\t// Act - connect all child processes\n\tconst connectResult = await connectChildProcesses(children, {\n\t\twriteClients,\n\t\treadyTimeoutMs: childConnectTimeoutMs,\n\t});\n\n\t// These actions are not awaited. They are here to provide additional logging.\n\t// It is up to the caller to await attendeeIdPromises if desired. This can mean\n\t// An \"error\" message is output, but the caller does not care about attendee\n\t// ids and proceeds.\n\tPromise.all(connectResult.attendeeIdPromises)\n\t\t.then(() => console.log(\"All attendees connected.\"))\n\t\t.catch((error) => {\n\t\t\ttestConsole.error(\"Error connecting children:\", error);\n\t\t});\n\n\treturn { ...connectResult, attendeeCountRequiredPromises };\n}\n\n/**\n * Connects the child processes and waits for the specified number of attendees to connect.\n *\n * @remarks\n * This function can be used directly as a test. Comments in the functionality describe the\n * breakdown of test blocks.\n */\nexport async function connectAndWaitForAttendees(\n\tchildren: ChildProcess[],\n\t{\n\t\twriteClients,\n\t\tattendeeCountRequired,\n\t\tchildConnectTimeoutMs,\n\t\tallAttendeesJoinedTimeoutMs,\n\t}: {\n\t\t/**\n\t\t * The number of clients that should have write access.\n\t\t */\n\t\twriteClients: number;\n\t\t/**\n\t\t * The number of attendees that must connect.\n\t\t */\n\t\tattendeeCountRequired: number;\n\t\t/**\n\t\t * Timeout duration for child process connections.\n\t\t */\n\t\tchildConnectTimeoutMs: number;\n\t\t/**\n\t\t * Timeout duration for all required attendees to join.\n\t\t */\n\t\tallAttendeesJoinedTimeoutMs: number;\n\t},\n\tearlyExitPromise: Promise<never>,\n): Promise<{ containerCreatorAttendeeId: AttendeeId }> {\n\t// Setup and Act - connect all child processes\n\tconst connectAndListenResult = await connectAndListenForAttendees(children, {\n\t\twriteClients,\n\t\tattendeeCountRequired,\n\t\tchildConnectTimeoutMs,\n\t});\n\n\tconst attendeeConnectedPromise = connectAndListenResult.attendeeCountRequiredPromises[0];\n\n\tawait timeoutAwait(Promise.race([attendeeConnectedPromise, earlyExitPromise]), {\n\t\tdurationMs: allAttendeesJoinedTimeoutMs,\n\t\terrorMsg: \"child 0 did not receive all 'attendeeConnected' events\",\n\t});\n\treturn connectAndListenResult;\n}\n\n/**\n * Registers a workspace (latest and/or latestMap) on all provided child processes and waits for acknowledgement.\n *\n * @remarks\n * The listener for the acknowledgement event is attached before sending the command to avoid a race where the\n * child responds faster than the parent attaches the handler.\n *\n * @param children - Child processes representing Fluid clients.\n * @param workspaceId - Logical (unprefixed) workspace id used in tests.\n * @param options - Which state types to register plus optional timeout.\n */\nexport async function registerWorkspaceOnChildren(\n\tchildren: ChildProcess[],\n\tworkspaceId: string,\n\toptions: { latest?: true; latestMap?: true; timeoutMs: number },\n): Promise<void> {\n\tconst { latest, latestMap, timeoutMs } = options;\n\tconst promises = children.map(async (child, index) => {\n\t\tconst ackPromise = waitForEvent(\n\t\t\tchild,\n\t\t\t\"workspaceRegistered\",\n\t\t\t{\n\t\t\t\ttimeoutMs,\n\t\t\t\terrorMsg: `Child ${index} did not acknowledge workspace registration ${workspaceId}`,\n\t\t\t},\n\t\t\t(msg: MessageFromChild) =>\n\t\t\t\tmsg.event === \"workspaceRegistered\" && msg.workspaceId === workspaceId,\n\t\t);\n\t\tchild.send({\n\t\t\tcommand: \"registerWorkspace\",\n\t\t\tworkspaceId,\n\t\t\tlatest,\n\t\t\tlatestMap,\n\t\t});\n\t\tawait ackPromise;\n\t});\n\tawait Promise.all(promises);\n}\n\n// Basic command type guards\nfunction isLatestValueGetResponse(msg: MessageFromChild): msg is LatestValueGetResponseEvent {\n\treturn msg.event === \"latestValueGetResponse\";\n}\nfunction isLatestMapValueGetResponse(\n\tmsg: MessageFromChild,\n): msg is LatestMapValueGetResponseEvent {\n\treturn msg.event === \"latestMapValueGetResponse\";\n}\nfunction isLatestValueUpdated(msg: MessageFromChild): msg is LatestValueUpdatedEvent {\n\treturn msg.event === \"latestValueUpdated\";\n}\nfunction isLatestMapValueUpdated(msg: MessageFromChild): msg is LatestMapValueUpdatedEvent {\n\treturn msg.event === \"latestMapValueUpdated\";\n}\n\n/**\n * Waits for a single message of the specified event type from a child process.\n */\nexport async function waitForEvent(\n\tchild: ChildProcess,\n\teventType: MessageFromChild[\"event\"],\n\toptions: { timeoutMs: number; errorMsg?: string },\n\tpredicate?: (msg: MessageFromChild) => boolean,\n): Promise<MessageFromChild> {\n\tconst { timeoutMs, errorMsg = `did not receive '${eventType}' event` } = options;\n\n\tlet handler: ((msg: MessageFromChild) => void) | undefined;\n\n\tconst cleanup = (): void => {\n\t\tif (handler) {\n\t\t\tchild.off(\"message\", handler);\n\t\t\thandler = undefined;\n\t\t}\n\t};\n\n\treturn timeoutPromise<MessageFromChild>(\n\t\t(resolve) => {\n\t\t\thandler = (msg: MessageFromChild): void => {\n\t\t\t\tif (msg.event === eventType && (!predicate || predicate(msg))) {\n\t\t\t\t\tcleanup();\n\t\t\t\t\tresolve(msg);\n\t\t\t\t}\n\t\t\t};\n\t\t\tchild.on(\"message\", handler);\n\t\t},\n\t\t{ durationMs: timeoutMs, errorMsg },\n\t).finally(cleanup);\n}\n\n/**\n * Waits for latest value updates for the provided workspace from all clients.\n *\n * @param clients - Child processes to wait for updates from\n * @param workspaceId - Workspace ID to filter updates\n * @param earlyExitPromise - Promise that rejects early on error\n * @param timeoutMs - Timeout in milliseconds\n * @param options - Optional filtering criteria with fromAttendeeId and expectedValue properties\n */\nexport async function waitForLatestValueUpdates(\n\tclients: ChildProcess[],\n\tworkspaceId: string,\n\tearlyExitPromise: Promise<never>,\n\ttimeoutMs: number,\n\toptions: { fromAttendeeId?: AttendeeId; expectedValue?: unknown } = {},\n): Promise<LatestValueUpdatedEvent[]> {\n\tconst { fromAttendeeId, expectedValue } = options;\n\n\tconst filterMsg = (msg: MessageFromChild): boolean => {\n\t\tif (!isLatestValueUpdated(msg) || msg.workspaceId !== workspaceId) {\n\t\t\treturn false;\n\t\t}\n\t\tif (fromAttendeeId !== undefined && msg.attendeeId !== fromAttendeeId) {\n\t\t\treturn false;\n\t\t}\n\t\tif (expectedValue !== undefined) {\n\t\t\treturn JSON.stringify(msg.value) === JSON.stringify(expectedValue);\n\t\t}\n\t\treturn true;\n\t};\n\tlet filterDescription = \"update\";\n\tif (fromAttendeeId) filterDescription += ` from attendee ${fromAttendeeId}`;\n\tif (expectedValue !== undefined)\n\t\tfilterDescription += ` with specific value ${JSON.stringify(expectedValue)}`;\n\tif (filterDescription === \"update\") filterDescription = \"any update\";\n\n\tconst updatePromises = clients.map(async (child, index) =>\n\t\twaitForEvent(\n\t\t\tchild,\n\t\t\t\"latestValueUpdated\",\n\t\t\t{\n\t\t\t\ttimeoutMs,\n\t\t\t\terrorMsg: `Client ${index} did not receive latest value ${filterDescription}`,\n\t\t\t},\n\t\t\tfilterMsg,\n\t\t),\n\t);\n\tconst responses = await Promise.race([Promise.all(updatePromises), earlyExitPromise]);\n\tconst latestValueUpdatedEvents: LatestValueUpdatedEvent[] = [];\n\tfor (const response of responses) {\n\t\tif (isLatestValueUpdated(response)) {\n\t\t\tlatestValueUpdatedEvents.push(response);\n\t\t} else {\n\t\t\tthrow new TypeError(`Expected LatestValueUpdated but got ${response.event}`);\n\t\t}\n\t}\n\treturn latestValueUpdatedEvents;\n}\n\n/**\n * Waits for latest map value updates (specific key) from all clients.\n *\n * @param clients - Child processes to wait for updates from\n * @param workspaceId - Workspace ID to filter updates\n * @param key - Map key to filter updates\n * @param earlyExitPromise - Promise that rejects early on error\n * @param timeoutMs - Timeout in milliseconds\n * @param options - Optional filtering criteria with fromAttendeeId and expectedValue properties\n */\nexport async function waitForLatestMapValueUpdates(\n\tclients: ChildProcess[],\n\tworkspaceId: string,\n\tkey: string,\n\tearlyExitPromise: Promise<never>,\n\ttimeoutMs: number,\n\toptions: { fromAttendeeId?: AttendeeId; expectedValue?: unknown } = {},\n): Promise<LatestMapValueUpdatedEvent[]> {\n\tconst { fromAttendeeId, expectedValue } = options;\n\n\tconst filterMsg = (msg: MessageFromChild): boolean => {\n\t\tif (!isLatestMapValueUpdated(msg) || msg.workspaceId !== workspaceId || msg.key !== key) {\n\t\t\treturn false;\n\t\t}\n\t\tif (fromAttendeeId !== undefined && msg.attendeeId !== fromAttendeeId) {\n\t\t\treturn false;\n\t\t}\n\t\tif (expectedValue !== undefined) {\n\t\t\treturn JSON.stringify(msg.value) === JSON.stringify(expectedValue);\n\t\t}\n\t\treturn true;\n\t};\n\tlet filterDescription = `update for key \"${key}\"`;\n\tif (fromAttendeeId) filterDescription += ` from attendee ${fromAttendeeId}`;\n\tif (expectedValue !== undefined)\n\t\tfilterDescription += ` with specific value ${JSON.stringify(expectedValue)}`;\n\n\tconst updatePromises = clients.map(async (child, index) =>\n\t\twaitForEvent(\n\t\t\tchild,\n\t\t\t\"latestMapValueUpdated\",\n\t\t\t{\n\t\t\t\ttimeoutMs,\n\t\t\t\terrorMsg: `Client ${index} did not receive latest map value ${filterDescription}`,\n\t\t\t},\n\t\t\tfilterMsg,\n\t\t),\n\t);\n\tconst responses = await Promise.race([Promise.all(updatePromises), earlyExitPromise]);\n\tconst latestMapValueUpdatedEvents: LatestMapValueUpdatedEvent[] = [];\n\tfor (const response of responses) {\n\t\tif (isLatestMapValueUpdated(response)) {\n\t\t\tlatestMapValueUpdatedEvents.push(response);\n\t\t} else {\n\t\t\tthrow new TypeError(`Expected LatestMapValueUpdated but got ${response.event}`);\n\t\t}\n\t}\n\treturn latestMapValueUpdatedEvents;\n}\n\n/**\n * Collects latest value get response events from all clients.\n */\nexport async function getLatestValueResponses(\n\tclients: ChildProcess[],\n\tworkspaceId: string,\n\tearlyExitPromise: Promise<never>,\n\ttimeoutMs: number,\n): Promise<LatestValueGetResponseEvent[]> {\n\tconst responsePromises = clients.map(async (child, index) =>\n\t\twaitForEvent(\n\t\t\tchild,\n\t\t\t\"latestValueGetResponse\",\n\t\t\t{ timeoutMs, errorMsg: `Client ${index} did not respond with latest value` },\n\t\t\t(msg: MessageFromChild) =>\n\t\t\t\tisLatestValueGetResponse(msg) && msg.workspaceId === workspaceId,\n\t\t),\n\t);\n\tconst responses = await Promise.race([Promise.all(responsePromises), earlyExitPromise]);\n\treturn responses.map((response) => {\n\t\tif (!isLatestValueGetResponse(response)) {\n\t\t\tthrow new TypeError(`Expected LatestValueGetResponse but got ${response.event}`);\n\t\t}\n\t\treturn response;\n\t});\n}\n\n/**\n * Collects latest map value get response events from all clients.\n */\nexport async function getLatestMapValueResponses(\n\tclients: ChildProcess[],\n\tworkspaceId: string,\n\tkey: string,\n\tearlyExitPromise: Promise<never>,\n\ttimeoutMs: number,\n): Promise<LatestMapValueGetResponseEvent[]> {\n\tconst responsePromises = clients.map(async (child, index) =>\n\t\twaitForEvent(\n\t\t\tchild,\n\t\t\t\"latestMapValueGetResponse\",\n\t\t\t{ timeoutMs, errorMsg: `Client ${index} did not respond with latest map value` },\n\t\t\t(msg: MessageFromChild) =>\n\t\t\t\tisLatestMapValueGetResponse(msg) && msg.workspaceId === workspaceId && msg.key === key,\n\t\t),\n\t);\n\tconst responses = await Promise.race([Promise.all(responsePromises), earlyExitPromise]);\n\treturn responses.map((response) => {\n\t\tif (!isLatestMapValueGetResponse(response)) {\n\t\t\tthrow new TypeError(`Expected LatestMapValueGetResponse but got ${response.event}`);\n\t\t}\n\t\treturn response;\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"orchestratorUtils.js","sourceRoot":"","sources":["../../../src/test/multiprocess/orchestratorUtils.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { fork } from \"node:child_process\";\nimport type { ChildProcess as AnyChildProcess } from \"node:child_process\";\n\nimport { ScopeType } from \"@fluidframework/driver-definitions/legacy\";\nimport type { AttendeeId } from \"@fluidframework/presence\";\nimport { timeoutAwait, timeoutPromise } from \"@fluidframework/test-utils/internal\";\n\nimport type {\n\tConnectCommand,\n\tMessageFromChild,\n\tLatestValueUpdatedEvent,\n\tLatestMapValueUpdatedEvent,\n\tLatestValueGetResponseEvent,\n\tLatestMapValueGetResponseEvent,\n\tMessageToChild,\n\tEventEntry,\n} from \"./messageTypes.js\";\n\n/**\n * Child process to console logging verbosity\n *\n * @remarks\n * Meaningful substrings:\n * - \"msgs\"\n * - \"telem\"\n *\n * @example \"msgs+telem\"\n */\nconst childLoggingVerbosity = process.env.FLUID_TEST_VERBOSE ?? \"none\";\n\n/**\n * Capture console./warn/error before test infrastructure alters it.\n */\nexport const testConsole = {\n\tlog: console.log,\n\twarn: console.warn,\n\terror: console.error,\n};\n\ninterface ChildProcess extends AnyChildProcess {\n\tsend(message: MessageToChild): boolean;\n}\n\n/**\n * Fork child processes to simulate multiple Fluid clients.\n *\n * @remarks\n * Individual child processes may be scheduled concurrently on a multi-core CPU\n * and separate processes will never share a port when connected to a service.\n *\n * @param numProcesses - The number of child processes to fork.\n * @param cleanUpAccumulator - An array to accumulate cleanup functions for each child.\n * @returns A collection of child processes and a promise that rejects on the first child error.\n */\nexport async function forkChildProcesses(\n\ttestLabel: string,\n\tnumProcesses: number,\n\tcleanUpAccumulator: (() => void)[],\n): Promise<{\n\tchildren: ChildProcess[];\n\t/**\n\t * Will never resolve successfully, it is only used to reject on child process error.\n\t */\n\tchildErrorPromise: Promise<never>;\n}> {\n\tconst children: ChildProcess[] = [];\n\tconst childReadyPromises: Promise<void>[] = [];\n\tconst childErrorPromises: Promise<never>[] = [];\n\tfor (let i = 0; i < numProcesses; i++) {\n\t\tconst child = fork(\"./lib/test/multiprocess/childClient.tool.js\", [\n\t\t\ttestLabel,\n\t\t\t`child ${i}` /* identifier passed to child process */,\n\t\t\tchildLoggingVerbosity /* console logging verbosity */,\n\t\t]);\n\t\tcleanUpAccumulator.push(() => {\n\t\t\tchild.kill();\n\t\t\tchild.removeAllListeners();\n\t\t});\n\t\tconst readyPromise = new Promise<void>((resolve, reject) => {\n\t\t\tchild.once(\"message\", (msg: MessageFromChild) => {\n\t\t\t\tif (msg.event === \"ack\") {\n\t\t\t\t\tresolve();\n\t\t\t\t} else {\n\t\t\t\t\treject(\n\t\t\t\t\t\tnew Error(`Unexpected (non-\"ack\") message from child${i}: ${JSON.stringify(msg)}`),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t\tchildReadyPromises.push(readyPromise);\n\t\tconst errorPromise = new Promise<never>((_resolve, reject) => {\n\t\t\tchild.once(\"error\", (error) => {\n\t\t\t\treject(new Error(`Child${i} process errored: ${error.message}`));\n\t\t\t});\n\t\t\tchild.once(\"exit\", (code, signal) => {\n\t\t\t\treject(new Error(`Child${i} process exited: code ${code}, signal ${signal}`));\n\t\t\t});\n\t\t});\n\t\tchildErrorPromises.push(errorPromise);\n\t\tchild.send({ command: \"ping\" });\n\t\tchildren.push(child);\n\t}\n\tconst childErrorPromise = Promise.race(childErrorPromises);\n\tawait Promise.race([Promise.all(childReadyPromises), childErrorPromise]);\n\treturn { children, childErrorPromise };\n}\n\n/**\n * Instructs all listed child processes to send debug reports and then the\n * collection is output sorted by timestamp. Report content is up to the child\n * processes, but typically includes messages sent and some telemetry events.\n */\nexport async function executeDebugReports(\n\tchildrenRequestedToReport: ChildProcess[],\n): Promise<void> {\n\tconst debugReportPromises: Promise<EventEntry[]>[] = [];\n\tfor (const child of childrenRequestedToReport) {\n\t\tconst debugReportPromise = new Promise<EventEntry[]>((resolve) => {\n\t\t\tconst handler = (msg: MessageFromChild): void => {\n\t\t\t\tif (msg.event === \"debugReportComplete\") {\n\t\t\t\t\tresolve(msg.log ?? []);\n\t\t\t\t\tchild.off(\"message\", handler);\n\t\t\t\t}\n\t\t\t};\n\t\t\tchild.on(\"message\", handler);\n\t\t});\n\t\tdebugReportPromises.push(debugReportPromise);\n\t\tchild.send({ command: \"debugReport\", sendEventLog: true, reportAttendees: true });\n\t}\n\n\tconst logs = await Promise.all(debugReportPromises);\n\tconst combinedLogs = logs.flat().sort((a, b) => a.timestamp - b.timestamp);\n\tfor (const entry of combinedLogs) {\n\t\ttestConsole.log(\n\t\t\t`[${new Date(entry.timestamp).toISOString()}] [${entry.agentId}] [${entry.eventCategory}] ${entry.eventName}${\n\t\t\t\tentry.details\n\t\t\t\t\t? ` - ${typeof entry.details === \"string\" ? entry.details : JSON.stringify(entry.details)}`\n\t\t\t\t\t: \"\"\n\t\t\t}`,\n\t\t);\n\t}\n}\n\n/**\n * Creates a {@link ConnectCommand} for a test user with a deterministic id and name.\n *\n * @param id - Suffix used to construct stable test user identity.\n */\nfunction composeConnectMessage(\n\tid: string | number,\n\tscopes: ScopeType[] = [ScopeType.DocRead],\n\tconnectTimeoutMs: number,\n): ConnectCommand {\n\treturn {\n\t\tcommand: \"connect\",\n\t\tuser: {\n\t\t\tid: `test-user-id-${id}`,\n\t\t\tname: `test-user-name-${id}`,\n\t\t},\n\t\tscopes,\n\t\tcreateScopes: [ScopeType.DocWrite, ScopeType.DocRead],\n\t\tconnectTimeoutMs,\n\t};\n}\n\n/**\n * Listens for a \"connected\" response from a child process\n * allowing/handling subset of other expected messages.\n */\nfunction listenForConnectedResponse({\n\tchild,\n\tchildId,\n\tonConnected,\n\treject,\n}: {\n\tchild: ChildProcess;\n\tchildId: number | string;\n\t/**\n\t * Will be called up to once when a \"connected\" message is received.\n\t */\n\tonConnected: (msg: Extract<MessageFromChild, { event: \"connected\" }>) => void;\n\t/**\n\t * Callback to reject for unexpected messages or child errors.\n\t */\n\treject: (reason?: unknown) => void;\n}): void {\n\tconst listener = (msg: MessageFromChild): void => {\n\t\tif (msg.event === \"connected\") {\n\t\t\tchild.off(\"message\", listener);\n\t\t\tonConnected(msg);\n\t\t} else if (msg.event === \"error\") {\n\t\t\tchild.off(\"message\", listener);\n\t\t\treject(new Error(`Child ${childId} process error: ${msg.error}`));\n\t\t} else if (msg.event !== \"ack\") {\n\t\t\tchild.off(\"message\", listener);\n\t\t\t// This is not strictly required, but is current expectation.\n\t\t\treject(\n\t\t\t\tnew Error(\n\t\t\t\t\t`Unexpected message from child ${childId} while connecting: ${JSON.stringify(msg)}`,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t};\n\tchild.on(\"message\", listener);\n}\n\ninterface CreatorAttendeeIdAndAttendeePromises {\n\tcontainerCreatorAttendeeId: AttendeeId;\n\tattendeeIdPromises: Promise<AttendeeId>[];\n}\n\n/**\n * Sends connect commands to the provided child processes.\n *\n * The first child will create the container unless a containerId is pre-specified; subsequent\n * children are sent the discovered containerId.\n */\nexport async function connectChildProcesses(\n\tchildProcesses: ChildProcess[],\n\t{ writeClients, readyTimeoutMs }: { writeClients: number; readyTimeoutMs: number },\n): Promise<CreatorAttendeeIdAndAttendeePromises> {\n\tif (childProcesses.length === 0) {\n\t\tthrow new Error(\"No child processes provided for connection.\");\n\t}\n\tconst firstChild = childProcesses[0];\n\tconst containerReadyPromise = new Promise<{\n\t\tcontainerCreatorAttendeeId: AttendeeId;\n\t\tcontainerId: string;\n\t}>((resolve, reject) =>\n\t\tlistenForConnectedResponse({\n\t\t\tchild: firstChild,\n\t\t\tchildId: 0,\n\t\t\tonConnected: (msg) => {\n\t\t\t\tif (msg.containerId) {\n\t\t\t\t\tresolve({\n\t\t\t\t\t\tcontainerCreatorAttendeeId: msg.attendeeId,\n\t\t\t\t\t\tcontainerId: msg.containerId,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\treject(\n\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t`Child 0 (creator) connected without containerId: ${JSON.stringify(msg)}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t},\n\t\t\treject,\n\t\t}),\n\t);\n\t{\n\t\t// Note that DocWrite is used to have this attendee be the \"leader\".\n\t\t// DocRead would also be valid as DocWrite is specified for attach when there\n\t\t// is no document id (container id).\n\t\tconst connectContainerCreator = composeConnectMessage(\n\t\t\t0,\n\t\t\twriteClients > 0 ? [ScopeType.DocWrite, ScopeType.DocRead] : [ScopeType.DocRead],\n\t\t\t/* connectTimeoutMs */ readyTimeoutMs,\n\t\t);\n\t\tfirstChild.send(connectContainerCreator);\n\t}\n\tconst { containerCreatorAttendeeId, containerId } = await timeoutAwait(\n\t\tcontainerReadyPromise,\n\t\t{\n\t\t\tdurationMs: readyTimeoutMs,\n\t\t\terrorMsg: \"did not receive 'connected' from child process\",\n\t\t},\n\t).catch(async (error) => {\n\t\tawait executeDebugReports([firstChild]);\n\t\tthrow error;\n\t});\n\n\tconst attendeeIdPromises: Promise<AttendeeId>[] = [];\n\tfor (const [index, child] of childProcesses.entries()) {\n\t\tif (index === 0) {\n\t\t\tattendeeIdPromises.push(Promise.resolve(containerCreatorAttendeeId));\n\t\t\tcontinue;\n\t\t}\n\t\tconst message = composeConnectMessage(\n\t\t\tindex,\n\t\t\tindex < writeClients ? [ScopeType.DocWrite, ScopeType.DocRead] : [ScopeType.DocRead],\n\t\t\t/* connectTimeoutMs */ readyTimeoutMs,\n\t\t);\n\t\tmessage.containerId = containerId;\n\t\tattendeeIdPromises.push(\n\t\t\tnew Promise<AttendeeId>((resolve, reject) =>\n\t\t\t\tlistenForConnectedResponse({\n\t\t\t\t\tchild,\n\t\t\t\t\tchildId: index,\n\t\t\t\t\tonConnected: (msg) => {\n\t\t\t\t\t\tresolve(msg.attendeeId);\n\t\t\t\t\t},\n\t\t\t\t\treject,\n\t\t\t\t}),\n\t\t\t),\n\t\t);\n\t\tchild.send(message);\n\t}\n\treturn { containerCreatorAttendeeId, attendeeIdPromises };\n}\n\ninterface ConnectAndListenForAttendees extends CreatorAttendeeIdAndAttendeePromises {\n\tattendeeCountRequiredPromises: Promise<void>[];\n}\n\n/**\n * Connects the child processes and creates promises for the specified number of\n * attendees to connect.\n */\nexport async function connectAndListenForAttendees(\n\tchildren: ChildProcess[],\n\t{\n\t\twriteClients,\n\t\tattendeeCountRequired,\n\t\tchildConnectTimeoutMs,\n\t}: {\n\t\t/**\n\t\t * The number of clients that should have write access.\n\t\t */\n\t\twriteClients: number;\n\t\t/**\n\t\t * The number of attendees that must connect.\n\t\t */\n\t\tattendeeCountRequired: number;\n\t\t/**\n\t\t * Timeout duration for child process connections.\n\t\t */\n\t\tchildConnectTimeoutMs: number;\n\t},\n): Promise<ConnectAndListenForAttendees> {\n\t// Setup\n\tconst attendeeCountRequiredPromises = children.map(\n\t\t// eslint-disable-next-line @typescript-eslint/promise-function-async\n\t\t(child) =>\n\t\t\tnew Promise<void>((resolve) => {\n\t\t\t\tlet attendeesJoinedEvents = 0;\n\t\t\t\tconst listenForAttendees = (msg: MessageFromChild): void => {\n\t\t\t\t\tif (msg.event === \"attendeeConnected\") {\n\t\t\t\t\t\tattendeesJoinedEvents++;\n\t\t\t\t\t\tif (attendeesJoinedEvents >= attendeeCountRequired) {\n\t\t\t\t\t\t\tchild.off(\"message\", listenForAttendees);\n\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tchild.on(\"message\", listenForAttendees);\n\t\t\t}),\n\t);\n\n\t// Act - connect all child processes\n\tconst connectResult = await connectChildProcesses(children, {\n\t\twriteClients,\n\t\treadyTimeoutMs: childConnectTimeoutMs,\n\t});\n\n\t// These actions are not awaited. They are here to provide additional logging.\n\t// It is up to the caller to await attendeeIdPromises if desired. This can mean\n\t// An \"error\" message is output, but the caller does not care about attendee\n\t// ids and proceeds.\n\tPromise.all(connectResult.attendeeIdPromises)\n\t\t.then(() => console.log(\"All attendees connected.\"))\n\t\t.catch((error) => {\n\t\t\ttestConsole.error(\"Error connecting children:\", error);\n\t\t});\n\n\treturn { ...connectResult, attendeeCountRequiredPromises };\n}\n\n/**\n * Connects the child processes and waits for the specified number of attendees to connect.\n *\n * @remarks\n * This function can be used directly as a test. Comments in the functionality describe the\n * breakdown of test blocks.\n */\nexport async function connectAndWaitForAttendees(\n\tchildren: ChildProcess[],\n\t{\n\t\twriteClients,\n\t\tattendeeCountRequired,\n\t\tchildConnectTimeoutMs,\n\t\tallAttendeesJoinedTimeoutMs,\n\t}: {\n\t\t/**\n\t\t * The number of clients that should have write access.\n\t\t */\n\t\twriteClients: number;\n\t\t/**\n\t\t * The number of attendees that must connect.\n\t\t */\n\t\tattendeeCountRequired: number;\n\t\t/**\n\t\t * Timeout duration for child process connections.\n\t\t */\n\t\tchildConnectTimeoutMs: number;\n\t\t/**\n\t\t * Timeout duration for all required attendees to join.\n\t\t */\n\t\tallAttendeesJoinedTimeoutMs: number;\n\t},\n\tearlyExitPromise: Promise<never>,\n): Promise<{ containerCreatorAttendeeId: AttendeeId }> {\n\t// Setup and Act - connect all child processes\n\tconst connectAndListenResult = await connectAndListenForAttendees(children, {\n\t\twriteClients,\n\t\tattendeeCountRequired,\n\t\tchildConnectTimeoutMs,\n\t});\n\n\tconst attendeeConnectedPromise = connectAndListenResult.attendeeCountRequiredPromises[0];\n\n\tawait timeoutAwait(Promise.race([attendeeConnectedPromise, earlyExitPromise]), {\n\t\tdurationMs: allAttendeesJoinedTimeoutMs,\n\t\terrorMsg: \"child 0 did not receive all 'attendeeConnected' events\",\n\t});\n\treturn connectAndListenResult;\n}\n\n/**\n * Registers a workspace (latest and/or latestMap) on all provided child processes and waits for acknowledgement.\n *\n * @remarks\n * The listener for the acknowledgement event is attached before sending the command to avoid a race where the\n * child responds faster than the parent attaches the handler.\n *\n * @param children - Child processes representing Fluid clients.\n * @param workspaceId - Logical (unprefixed) workspace id used in tests.\n * @param options - Which state types to register plus optional timeout.\n */\nexport async function registerWorkspaceOnChildren(\n\tchildren: ChildProcess[],\n\tworkspaceId: string,\n\toptions: { latest?: true; latestMap?: true; timeoutMs: number },\n): Promise<void> {\n\tconst { latest, latestMap, timeoutMs } = options;\n\tconst promises = children.map(async (child, index) => {\n\t\tconst ackPromise = waitForEvent(\n\t\t\tchild,\n\t\t\t\"workspaceRegistered\",\n\t\t\t{\n\t\t\t\ttimeoutMs,\n\t\t\t\terrorMsg: `Child ${index} did not acknowledge workspace registration ${workspaceId}`,\n\t\t\t},\n\t\t\t(msg: MessageFromChild) =>\n\t\t\t\tmsg.event === \"workspaceRegistered\" && msg.workspaceId === workspaceId,\n\t\t);\n\t\tchild.send({\n\t\t\tcommand: \"registerWorkspace\",\n\t\t\tworkspaceId,\n\t\t\tlatest,\n\t\t\tlatestMap,\n\t\t});\n\t\tawait ackPromise;\n\t});\n\tawait Promise.all(promises);\n}\n\n// Basic command type guards\nfunction isLatestValueGetResponse(msg: MessageFromChild): msg is LatestValueGetResponseEvent {\n\treturn msg.event === \"latestValueGetResponse\";\n}\nfunction isLatestMapValueGetResponse(\n\tmsg: MessageFromChild,\n): msg is LatestMapValueGetResponseEvent {\n\treturn msg.event === \"latestMapValueGetResponse\";\n}\nfunction isLatestValueUpdated(msg: MessageFromChild): msg is LatestValueUpdatedEvent {\n\treturn msg.event === \"latestValueUpdated\";\n}\nfunction isLatestMapValueUpdated(msg: MessageFromChild): msg is LatestMapValueUpdatedEvent {\n\treturn msg.event === \"latestMapValueUpdated\";\n}\n\n/**\n * Waits for a single message of the specified event type from a child process.\n */\nexport async function waitForEvent(\n\tchild: ChildProcess,\n\teventType: MessageFromChild[\"event\"],\n\toptions: { timeoutMs: number; errorMsg?: string },\n\tpredicate?: (msg: MessageFromChild) => boolean,\n): Promise<MessageFromChild> {\n\tconst { timeoutMs, errorMsg = `did not receive '${eventType}' event` } = options;\n\n\tlet handler: ((msg: MessageFromChild) => void) | undefined;\n\n\tconst cleanup = (): void => {\n\t\tif (handler) {\n\t\t\tchild.off(\"message\", handler);\n\t\t\thandler = undefined;\n\t\t}\n\t};\n\n\treturn timeoutPromise<MessageFromChild>(\n\t\t(resolve) => {\n\t\t\thandler = (msg: MessageFromChild): void => {\n\t\t\t\tif (msg.event === eventType && (!predicate || predicate(msg))) {\n\t\t\t\t\tcleanup();\n\t\t\t\t\tresolve(msg);\n\t\t\t\t}\n\t\t\t};\n\t\t\tchild.on(\"message\", handler);\n\t\t},\n\t\t{ durationMs: timeoutMs, errorMsg },\n\t).finally(cleanup);\n}\n\n/**\n * Waits for latest value updates for the provided workspace from all clients.\n *\n * @param clients - Child processes to wait for updates from\n * @param workspaceId - Workspace ID to filter updates\n * @param earlyExitPromise - Promise that rejects early on error\n * @param timeoutMs - Timeout in milliseconds\n * @param options - Optional filtering criteria with fromAttendeeId and expectedValue properties\n */\nexport async function waitForLatestValueUpdates(\n\tclients: ChildProcess[],\n\tworkspaceId: string,\n\tearlyExitPromise: Promise<never>,\n\ttimeoutMs: number,\n\toptions: { fromAttendeeId?: AttendeeId; expectedValue?: unknown } = {},\n): Promise<LatestValueUpdatedEvent[]> {\n\tconst { fromAttendeeId, expectedValue } = options;\n\n\tconst filterMsg = (msg: MessageFromChild): boolean => {\n\t\tif (!isLatestValueUpdated(msg) || msg.workspaceId !== workspaceId) {\n\t\t\treturn false;\n\t\t}\n\t\tif (fromAttendeeId !== undefined && msg.attendeeId !== fromAttendeeId) {\n\t\t\treturn false;\n\t\t}\n\t\tif (expectedValue !== undefined) {\n\t\t\treturn JSON.stringify(msg.value) === JSON.stringify(expectedValue);\n\t\t}\n\t\treturn true;\n\t};\n\tlet filterDescription = \"update\";\n\tif (fromAttendeeId) filterDescription += ` from attendee ${fromAttendeeId}`;\n\tif (expectedValue !== undefined)\n\t\tfilterDescription += ` with specific value ${JSON.stringify(expectedValue)}`;\n\tif (filterDescription === \"update\") filterDescription = \"any update\";\n\n\tconst updatePromises = clients.map(async (child, index) =>\n\t\twaitForEvent(\n\t\t\tchild,\n\t\t\t\"latestValueUpdated\",\n\t\t\t{\n\t\t\t\ttimeoutMs,\n\t\t\t\terrorMsg: `Client ${index} did not receive latest value ${filterDescription}`,\n\t\t\t},\n\t\t\tfilterMsg,\n\t\t),\n\t);\n\tconst responses = await Promise.race([Promise.all(updatePromises), earlyExitPromise]);\n\tconst latestValueUpdatedEvents: LatestValueUpdatedEvent[] = [];\n\tfor (const response of responses) {\n\t\tif (isLatestValueUpdated(response)) {\n\t\t\tlatestValueUpdatedEvents.push(response);\n\t\t} else {\n\t\t\tthrow new TypeError(`Expected LatestValueUpdated but got ${response.event}`);\n\t\t}\n\t}\n\treturn latestValueUpdatedEvents;\n}\n\n/**\n * Waits for latest map value updates (specific key) from all clients.\n *\n * @param clients - Child processes to wait for updates from\n * @param workspaceId - Workspace ID to filter updates\n * @param key - Map key to filter updates\n * @param earlyExitPromise - Promise that rejects early on error\n * @param timeoutMs - Timeout in milliseconds\n * @param options - Optional filtering criteria with fromAttendeeId and expectedValue properties\n */\nexport async function waitForLatestMapValueUpdates(\n\tclients: ChildProcess[],\n\tworkspaceId: string,\n\tkey: string,\n\tearlyExitPromise: Promise<never>,\n\ttimeoutMs: number,\n\toptions: { fromAttendeeId?: AttendeeId; expectedValue?: unknown } = {},\n): Promise<LatestMapValueUpdatedEvent[]> {\n\tconst { fromAttendeeId, expectedValue } = options;\n\n\tconst filterMsg = (msg: MessageFromChild): boolean => {\n\t\tif (!isLatestMapValueUpdated(msg) || msg.workspaceId !== workspaceId || msg.key !== key) {\n\t\t\treturn false;\n\t\t}\n\t\tif (fromAttendeeId !== undefined && msg.attendeeId !== fromAttendeeId) {\n\t\t\treturn false;\n\t\t}\n\t\tif (expectedValue !== undefined) {\n\t\t\treturn JSON.stringify(msg.value) === JSON.stringify(expectedValue);\n\t\t}\n\t\treturn true;\n\t};\n\tlet filterDescription = `update for key \"${key}\"`;\n\tif (fromAttendeeId) filterDescription += ` from attendee ${fromAttendeeId}`;\n\tif (expectedValue !== undefined)\n\t\tfilterDescription += ` with specific value ${JSON.stringify(expectedValue)}`;\n\n\tconst updatePromises = clients.map(async (child, index) =>\n\t\twaitForEvent(\n\t\t\tchild,\n\t\t\t\"latestMapValueUpdated\",\n\t\t\t{\n\t\t\t\ttimeoutMs,\n\t\t\t\terrorMsg: `Client ${index} did not receive latest map value ${filterDescription}`,\n\t\t\t},\n\t\t\tfilterMsg,\n\t\t),\n\t);\n\tconst responses = await Promise.race([Promise.all(updatePromises), earlyExitPromise]);\n\tconst latestMapValueUpdatedEvents: LatestMapValueUpdatedEvent[] = [];\n\tfor (const response of responses) {\n\t\tif (isLatestMapValueUpdated(response)) {\n\t\t\tlatestMapValueUpdatedEvents.push(response);\n\t\t} else {\n\t\t\tthrow new TypeError(`Expected LatestMapValueUpdated but got ${response.event}`);\n\t\t}\n\t}\n\treturn latestMapValueUpdatedEvents;\n}\n\n/**\n * Collects latest value get response events from all clients.\n */\nexport async function getLatestValueResponses(\n\tclients: ChildProcess[],\n\tworkspaceId: string,\n\tearlyExitPromise: Promise<never>,\n\ttimeoutMs: number,\n): Promise<LatestValueGetResponseEvent[]> {\n\tconst responsePromises = clients.map(async (child, index) =>\n\t\twaitForEvent(\n\t\t\tchild,\n\t\t\t\"latestValueGetResponse\",\n\t\t\t{ timeoutMs, errorMsg: `Client ${index} did not respond with latest value` },\n\t\t\t(msg: MessageFromChild) =>\n\t\t\t\tisLatestValueGetResponse(msg) && msg.workspaceId === workspaceId,\n\t\t),\n\t);\n\tconst responses = await Promise.race([Promise.all(responsePromises), earlyExitPromise]);\n\treturn responses.map((response) => {\n\t\tif (!isLatestValueGetResponse(response)) {\n\t\t\tthrow new TypeError(`Expected LatestValueGetResponse but got ${response.event}`);\n\t\t}\n\t\treturn response;\n\t});\n}\n\n/**\n * Collects latest map value get response events from all clients.\n */\nexport async function getLatestMapValueResponses(\n\tclients: ChildProcess[],\n\tworkspaceId: string,\n\tkey: string,\n\tearlyExitPromise: Promise<never>,\n\ttimeoutMs: number,\n): Promise<LatestMapValueGetResponseEvent[]> {\n\tconst responsePromises = clients.map(async (child, index) =>\n\t\twaitForEvent(\n\t\t\tchild,\n\t\t\t\"latestMapValueGetResponse\",\n\t\t\t{ timeoutMs, errorMsg: `Client ${index} did not respond with latest map value` },\n\t\t\t(msg: MessageFromChild) =>\n\t\t\t\tisLatestMapValueGetResponse(msg) && msg.workspaceId === workspaceId && msg.key === key,\n\t\t),\n\t);\n\tconst responses = await Promise.race([Promise.all(responsePromises), earlyExitPromise]);\n\treturn responses.map((response) => {\n\t\tif (!isLatestMapValueGetResponse(response)) {\n\t\t\tthrow new TypeError(`Expected LatestMapValueGetResponse but got ${response.event}`);\n\t\t}\n\t\treturn response;\n\t});\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presenceTest.spec.js","sourceRoot":"","sources":["../../../src/test/multiprocess/presenceTest.spec.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { strict as assert } from \"node:assert\";\nimport type { ChildProcess } from \"node:child_process\";\nimport inspector from \"node:inspector\";\n\nimport type { AttendeeId } from \"@fluidframework/presence/beta\";\nimport { timeoutAwait, timeoutPromise } from \"@fluidframework/test-utils/internal\";\n\nimport type { MessageFromChild } from \"./messageTypes.js\";\nimport {\n\tconnectAndListenForAttendees,\n\tconnectAndWaitForAttendees,\n\tconnectChildProcesses,\n\texecuteDebugReports,\n\tforkChildProcesses,\n\tgetLatestMapValueResponses,\n\tgetLatestValueResponses,\n\tregisterWorkspaceOnChildren,\n\ttestConsole,\n\twaitForLatestMapValueUpdates,\n\twaitForLatestValueUpdates,\n} from \"./orchestratorUtils.js\";\n\n/**\n * When true, slower (long running time) tests will be run.\n * Otherwise, those test will not appear. Console output is used to show that\n * they exist. (They could be skipped, though skipped test are often an\n * indication of a problem.)\n */\nconst shouldRunScaleTests = process.env.FLUID_TEST_SCALE !== undefined;\n\nconst useAzure = process.env.FLUID_CLIENT === \"azure\";\n\n/**\n * Detects if the debugger is attached (when code loaded).\n */\nconst debuggerAttached = inspector.url() !== undefined;\n\n/**\n * Set this to a high number when debugging to avoid timeouts from debugging time.\n */\nconst timeoutMultiplier = debuggerAttached ? 1000 : useAzure ? 5 : 1;\n\n/**\n * Sets the timeout for the given test context.\n *\n * @remarks\n * If a debugger is attached, the timeout is set to 0 to prevent timeouts during debugging.\n * Otherwise, it sets the timeout to the maximum of the current timeout and the specified duration.\n *\n * @param context - The Mocha test context.\n * @param duration - The duration in milliseconds to set the timeout to. Zero disables the timeout.\n */\nfunction setTestTimeout(context: Mocha.Context, duration: number): void {\n\tconst currentTimeout = context.timeout();\n\tconst newTimeout =\n\t\tdebuggerAttached || currentTimeout === 0 || duration === 0\n\t\t\t? 0\n\t\t\t: Math.max(currentTimeout, duration);\n\tif (newTimeout !== currentTimeout) {\n\t\ttestConsole.log(\n\t\t\t`${context.test?.title}: setting timeout to ${newTimeout}ms (was ${currentTimeout}ms)`,\n\t\t);\n\t\tcontext.timeout(newTimeout);\n\t}\n}\n\n/**\n * This test suite is a prototype for a multi-process end to end test for Fluid using the new Presence API on AzureClient.\n * In the future we hope to expand and generalize this pattern to broadly test more Fluid features.\n * Other E2E tests are limited to running multiple clients on a single process which does not effectively\n * simulate real-world production scenarios where clients are usually running on different machines. Since\n * the Fluid Framework client is designed to carry most of the work burden, multi-process testing from a\n * single machine is also not representative but does at least work past some limitations of a single\n * Node.js process handling multiple clients.\n *\n * The pattern demonstrated in this test suite is as follows:\n *\n * This main test file acts as the 'Orchestrator'. The orchestrator's job includes:\n * - Fork child processes to simulate multiple Fluid clients\n * - Send command messages to child clients to perform specific Fluid actions.\n * - Receive response messages from child clients to verify expected behavior.\n * - Clean up child processes after each test.\n *\n * The child processes are located in the `childClient.tool.ts` file. Each child process simulates a Fluid client.\n *\n * The child client's job includes:\n * - Create/Get + connect to Fluid container.\n * - Listen for command messages from the orchestrator.\n * - Perform the requested action.\n * - Send response messages including any relevant data back to the orchestrator to verify expected behavior.\n */\n\ndescribe(`Presence with AzureClient`, () => {\n\tconst afterCleanUp: (() => void)[] = [];\n\n\t// After each test, call any cleanup functions that were registered (kill each child process)\n\tafterEach(async () => {\n\t\tfor (const cleanUp of afterCleanUp) {\n\t\t\tcleanUp();\n\t\t}\n\t\tafterCleanUp.length = 0;\n\t});\n\n\tdescribe(\"`attendees` support\", () => {\n\t\tconst numClientsForAttendeeTests = [5, 40, 100, 250];\n\t\tfor (const numClients of numClientsForAttendeeTests) {\n\t\t\tif (numClients > 50 && !shouldRunScaleTests) {\n\t\t\t\ttestConsole.log(\n\t\t\t\t\t`skipping Presence attendee scale tests with ${numClients} clients (set FLUID_TEST_SCALE=true to run)`,\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tassert(numClients > 1, \"Must have at least two clients\");\n\t\t\t/**\n\t\t\t * Timeout for child processes to connect to container ({@link ConnectedEvent})\n\t\t\t */\n\t\t\tconst childConnectTimeoutMs = 1000 * numClients * timeoutMultiplier;\n\t\t\t/**\n\t\t\t * Timeout for presence attendees to join per first child perspective {@link AttendeeConnectedEvent}\n\t\t\t */\n\t\t\tconst allAttendeesJoinedTimeoutMs = (1000 + 200 * numClients) * timeoutMultiplier;\n\t\t\t/**\n\t\t\t * Timeout for presence attendees to fully join (everyone knows about everyone) {@link AttendeeConnectedEvent}\n\t\t\t */\n\t\t\tconst allAttendeesFullyJoinedTimeoutMs = (2000 + 300 * numClients) * timeoutMultiplier;\n\n\t\t\tfor (const writeClients of [numClients, 1]) {\n\t\t\t\tit(`announces 'attendeeConnected' when remote client joins session [${numClients} clients, ${writeClients} writers]`, async function testAnnouncesAttendeeConnected() {\n\t\t\t\t\t/**\n\t\t\t\t\t * Note: This test is currently skipped in the AFR driver due to General Network Errors in the Service Clients End to End tests pipelines.\n\t\t\t\t\t * For more context, see AB#59980.\n\t\t\t\t\t */\n\t\t\t\t\tif (useAzure && numClients > 50) {\n\t\t\t\t\t\tthis.skip();\n\t\t\t\t\t}\n\t\t\t\t\tsetTestTimeout(this, childConnectTimeoutMs + allAttendeesJoinedTimeoutMs + 1000);\n\n\t\t\t\t\t// Setup\n\t\t\t\t\tconst { children, childErrorPromise } = await forkChildProcesses(\n\t\t\t\t\t\tthis.test?.title ?? \"\",\n\t\t\t\t\t\tnumClients,\n\t\t\t\t\t\tafterCleanUp,\n\t\t\t\t\t);\n\n\t\t\t\t\t// Further Setup with Act and Verify\n\t\t\t\t\tawait connectAndWaitForAttendees(\n\t\t\t\t\t\tchildren,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\twriteClients,\n\t\t\t\t\t\t\tattendeeCountRequired: numClients - 1,\n\t\t\t\t\t\t\tchildConnectTimeoutMs,\n\t\t\t\t\t\t\tallAttendeesJoinedTimeoutMs,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tchildErrorPromise,\n\t\t\t\t\t);\n\t\t\t\t});\n\n\t\t\t\tit(`announces 'attendeeDisconnected' when remote client disconnects [${numClients} clients, ${writeClients} writers]`, async function testAnnouncesAttendeeDisconnected() {\n\t\t\t\t\tif (useAzure && numClients > 50) {\n\t\t\t\t\t\t// Even with increased timeouts, more than 50 clients can be too large for AFR.\n\t\t\t\t\t\t// This may be due to slow responses/inactivity from the clients that are\n\t\t\t\t\t\t// creating pressure on ADO agent.\n\t\t\t\t\t\tthis.skip();\n\t\t\t\t\t}\n\n\t\t\t\t\tconst childDisconnectTimeoutMs = 10_000 * timeoutMultiplier;\n\n\t\t\t\t\tsetTestTimeout(\n\t\t\t\t\t\tthis,\n\t\t\t\t\t\tchildConnectTimeoutMs +\n\t\t\t\t\t\t\tallAttendeesFullyJoinedTimeoutMs +\n\t\t\t\t\t\t\tchildDisconnectTimeoutMs +\n\t\t\t\t\t\t\t1000,\n\t\t\t\t\t);\n\n\t\t\t\t\t// Setup\n\t\t\t\t\tconst { children, childErrorPromise } = await forkChildProcesses(\n\t\t\t\t\t\tthis.test?.title ?? \"\",\n\t\t\t\t\t\tnumClients,\n\t\t\t\t\t\tafterCleanUp,\n\t\t\t\t\t);\n\n\t\t\t\t\tconst startConnectAndFullJoin = performance.now();\n\t\t\t\t\tconst connectResult = await connectAndListenForAttendees(children, {\n\t\t\t\t\t\twriteClients,\n\t\t\t\t\t\tattendeeCountRequired: numClients - 1,\n\t\t\t\t\t\tchildConnectTimeoutMs,\n\t\t\t\t\t});\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-floating-promises\n\t\t\t\t\tconnectResult.attendeeCountRequiredPromises[0].then(() =>\n\t\t\t\t\t\ttestConsole.log(\n\t\t\t\t\t\t\t`[${new Date().toISOString()}] All attendees joined per child 0 after ${performance.now() - startConnectAndFullJoin}ms`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\n\t\t\t\t\t// Wait for all attendees to be fully joined\n\t\t\t\t\t// Keep a tally for debuggability\n\t\t\t\t\tlet childrenFullyJoined = 0;\n\t\t\t\t\tconst setNotFullyJoined = new Set<number>();\n\t\t\t\t\tfor (let i = 0; i < children.length; i++) {\n\t\t\t\t\t\tsetNotFullyJoined.add(i);\n\t\t\t\t\t}\n\t\t\t\t\tconst allAttendeesFullyJoined = Promise.all(\n\t\t\t\t\t\tconnectResult.attendeeCountRequiredPromises.map(\n\t\t\t\t\t\t\tasync (attendeeFullyJoinedPromise, index) => {\n\t\t\t\t\t\t\t\tawait attendeeFullyJoinedPromise;\n\t\t\t\t\t\t\t\tchildrenFullyJoined++;\n\t\t\t\t\t\t\t\tsetNotFullyJoined.delete(index);\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\tlet timedout = true;\n\t\t\t\t\tconst allFullyJoinedOrChildError = Promise.race([\n\t\t\t\t\t\tallAttendeesFullyJoined,\n\t\t\t\t\t\tchildErrorPromise,\n\t\t\t\t\t]).finally(() => (timedout = false));\n\t\t\t\t\tawait timeoutAwait(allFullyJoinedOrChildError, {\n\t\t\t\t\t\tdurationMs: allAttendeesFullyJoinedTimeoutMs,\n\t\t\t\t\t\terrorMsg: \"Not all attendees fully joined\",\n\t\t\t\t\t}).catch(async (error) => {\n\t\t\t\t\t\t// Ideally this information would just be in the timeout error message, but that\n\t\t\t\t\t\t// must be a resolved string (not dynamic). So, just log it separately.\n\t\t\t\t\t\ttestConsole.log(\n\t\t\t\t\t\t\t`[${new Date().toISOString()}] ${childrenFullyJoined} attendees fully joined before error...`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (timedout) {\n\t\t\t\t\t\t\t// Gather additional timing data if timed out to understand what increased\n\t\t\t\t\t\t\t// timeout could work. Test will still fail if this secondary wait succeeds.\n\t\t\t\t\t\t\tconst startAdditionalWait = performance.now();\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tawait timeoutAwait(allFullyJoinedOrChildError, {\n\t\t\t\t\t\t\t\t\tdurationMs: allAttendeesFullyJoinedTimeoutMs,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\ttestConsole.log(\n\t\t\t\t\t\t\t\t\t`[${new Date().toISOString()}] All attendees fully joined after additional wait (${performance.now() - startAdditionalWait}ms)`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t} catch (secondaryError) {\n\t\t\t\t\t\t\t\ttestConsole.log(\n\t\t\t\t\t\t\t\t\t`[${new Date().toISOString()}] Secondary await resulted in`,\n\t\t\t\t\t\t\t\t\tsecondaryError,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Gather and report debug info from children\n\t\t\t\t\t\t// If there are less than 10 children, get all reports.\n\t\t\t\t\t\t// Otherwise, just child 0 and those not fully joined.\n\t\t\t\t\t\tsetTestTimeout(this, 0); // Disable test timeout. Will throw within 20s below.\n\t\t\t\t\t\tconst childrenRequestedToReport =\n\t\t\t\t\t\t\tchildren.length <= 10\n\t\t\t\t\t\t\t\t? children\n\t\t\t\t\t\t\t\t: // Just those not fully joined\n\t\t\t\t\t\t\t\t\tchildren.filter((_, index) => index === 0 || setNotFullyJoined.has(index));\n\t\t\t\t\t\tawait timeoutAwait(\n\t\t\t\t\t\t\tPromise.race([\n\t\t\t\t\t\t\t\texecuteDebugReports(childrenRequestedToReport),\n\t\t\t\t\t\t\t\tchildErrorPromise,\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t{ durationMs: 20_000, errorMsg: \"Debug report timeout\" },\n\t\t\t\t\t\t).catch((debugAwaitError) => {\n\t\t\t\t\t\t\ttestConsole.error(\"Debug report await resulted in\", debugAwaitError);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t});\n\t\t\t\t\ttestConsole.log(\n\t\t\t\t\t\t`[${new Date().toISOString()}] All attendees fully joined after ${performance.now() - startConnectAndFullJoin}ms`,\n\t\t\t\t\t);\n\n\t\t\t\t\tlet child0ReportRequested = false;\n\t\t\t\t\tconst waitForDisconnected = children.map(async (child, index) =>\n\t\t\t\t\t\tindex === 0\n\t\t\t\t\t\t\t? Promise.resolve()\n\t\t\t\t\t\t\t: timeoutPromise(\n\t\t\t\t\t\t\t\t\t(resolve) => {\n\t\t\t\t\t\t\t\t\t\tchild.on(\"message\", (msg: MessageFromChild) => {\n\t\t\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\t\t\tmsg.event === \"attendeeDisconnected\" &&\n\t\t\t\t\t\t\t\t\t\t\t\tmsg.attendeeId === connectResult.containerCreatorAttendeeId\n\t\t\t\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\t\t\t\tconsole.log(`Child[${index}] saw creator disconnect`);\n\t\t\t\t\t\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tdurationMs: childDisconnectTimeoutMs,\n\t\t\t\t\t\t\t\t\t\terrorMsg: `Attendee[${index}] Disconnected Timeout`,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t).catch(async (error) => {\n\t\t\t\t\t\t\t\t\tconst childrenRequestedToReport = [child];\n\t\t\t\t\t\t\t\t\tif (!child0ReportRequested) {\n\t\t\t\t\t\t\t\t\t\tchildrenRequestedToReport.unshift(children[0]);\n\t\t\t\t\t\t\t\t\t\tchild0ReportRequested = true;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tawait timeoutAwait(\n\t\t\t\t\t\t\t\t\t\tPromise.race([\n\t\t\t\t\t\t\t\t\t\t\texecuteDebugReports(childrenRequestedToReport),\n\t\t\t\t\t\t\t\t\t\t\tchildErrorPromise,\n\t\t\t\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t\t\t\t{ durationMs: 20_000, errorMsg: \"Debug report timeout\" },\n\t\t\t\t\t\t\t\t\t).catch((debugAwaitError) => {\n\t\t\t\t\t\t\t\t\t\ttestConsole.error(\"Debug report await resulted in\", debugAwaitError);\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\n\t\t\t\t\t// Act - disconnect first child process\n\t\t\t\t\tchildren[0].send({ command: \"disconnectSelf\" });\n\n\t\t\t\t\t// Verify - wait for all 'attendeeDisconnected' events\n\t\t\t\t\tawait Promise.race([Promise.all(waitForDisconnected), childErrorPromise]);\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t});\n\n\t{\n\t\t/**\n\t\t * Timeout for workspace registration {@link WorkspaceRegisteredEvent}\n\t\t */\n\t\tconst workspaceRegisterTimeoutMs = 5000;\n\t\t/**\n\t\t * Timeout for presence update events {@link LatestMapValueUpdatedEvent} and {@link LatestValueUpdatedEvent}\n\t\t */\n\t\tconst stateUpdateTimeoutMs = 5000;\n\t\t/**\n\t\t * Timeout for {@link LatestMapValueGetResponseEvent} and {@link LatestValueGetResponseEvent}\n\t\t */\n\t\tconst getStateTimeoutMs = 5000;\n\n\t\t// This test suite focuses on the synchronization of Latest state between clients.\n\t\t// NOTE: For testing purposes child clients will expect a Latest value of type string.\n\t\tdescribe(`using Latest state object`, () => {\n\t\t\tfor (const numClients of [5, 20]) {\n\t\t\t\tassert(numClients > 1, \"Must have at least two clients\");\n\t\t\t\t/**\n\t\t\t\t * Timeout for child processes to connect to container ({@link ConnectedEvent})\n\t\t\t\t */\n\t\t\t\tconst childConnectTimeoutMs = (4000 + 1000 * numClients) * timeoutMultiplier;\n\t\t\t\tconst testCaseTimeoutMs = 1000;\n\t\t\t\tconst testSetupAndActTimeoutMs = childConnectTimeoutMs + testCaseTimeoutMs;\n\n\t\t\t\t// These tests use beforeEach to setup complex state that takes a lot of time\n\t\t\t\t// and is dependent on number of clients. Keeping the work in beforeEach\n\t\t\t\t// allows time reporting to report the tested scenario apart from the setup time.\n\t\t\t\t// So this describe block isolates those beforeEach setups from each distinct\n\t\t\t\t// client count. Test cases descriptions also have the client count for clarity.\n\t\t\t\tdescribe(`with ${numClients} clients`, () => {\n\t\t\t\t\tlet children: ChildProcess[];\n\t\t\t\t\tlet childErrorPromise: Promise<never>;\n\t\t\t\t\tlet containerCreatorAttendeeId: AttendeeId;\n\t\t\t\t\tlet attendeeIdPromises: Promise<AttendeeId>[];\n\t\t\t\t\tlet remoteClients: ChildProcess[];\n\t\t\t\t\tconst testValue = \"testValue\";\n\t\t\t\t\tconst workspaceId = \"presenceTestWorkspace\";\n\n\t\t\t\t\tbeforeEach(async function usingLatestStateObject_beforeEach(): Promise<void> {\n\t\t\t\t\t\tconst startTime = performance.now();\n\t\t\t\t\t\tsetTestTimeout(this, testSetupAndActTimeoutMs);\n\n\t\t\t\t\t\t({ children, childErrorPromise } = await forkChildProcesses(\n\t\t\t\t\t\t\tthis.currentTest?.title ?? \"\",\n\t\t\t\t\t\t\tnumClients,\n\t\t\t\t\t\t\tafterCleanUp,\n\t\t\t\t\t\t));\n\t\t\t\t\t\t({ containerCreatorAttendeeId, attendeeIdPromises } = await connectChildProcesses(\n\t\t\t\t\t\t\tchildren,\n\t\t\t\t\t\t\t{ writeClients: numClients, readyTimeoutMs: childConnectTimeoutMs },\n\t\t\t\t\t\t));\n\t\t\t\t\t\tawait Promise.all(attendeeIdPromises);\n\t\t\t\t\t\tremoteClients = children.filter((_, index) => index !== 0);\n\t\t\t\t\t\t// NOTE: For testing purposes child clients will expect a Latest value of type string (StateFactory.latest<{ value: string }>).\n\t\t\t\t\t\tawait registerWorkspaceOnChildren(children, workspaceId, {\n\t\t\t\t\t\t\tlatest: true,\n\t\t\t\t\t\t\ttimeoutMs: workspaceRegisterTimeoutMs,\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\ttestConsole.log(\n\t\t\t\t\t\t\t` Setup for \"${this.currentTest?.title}\" completed in ${performance.now() - startTime}ms`,\n\t\t\t\t\t\t);\n\t\t\t\t\t});\n\n\t\t\t\t\tit(`allows clients to read Latest state from other clients [${numClients} clients]`, async function () {\n\t\t\t\t\t\t// Setup\n\t\t\t\t\t\tconst updateEventsPromise = waitForLatestValueUpdates(\n\t\t\t\t\t\t\tremoteClients,\n\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\tchildErrorPromise,\n\t\t\t\t\t\t\tstateUpdateTimeoutMs,\n\t\t\t\t\t\t\t{ fromAttendeeId: containerCreatorAttendeeId, expectedValue: testValue },\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Act - Trigger the update\n\t\t\t\t\t\tchildren[0].send({\n\t\t\t\t\t\t\tcommand: \"setLatestValue\",\n\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\tvalue: testValue,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tconst updateEvents = await updateEventsPromise;\n\n\t\t\t\t\t\t// Verify all events are from the expected attendee\n\t\t\t\t\t\tfor (const updateEvent of updateEvents) {\n\t\t\t\t\t\t\tassert.strictEqual(updateEvent.attendeeId, containerCreatorAttendeeId);\n\t\t\t\t\t\t\tassert.deepStrictEqual(updateEvent.value, testValue);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Act - Request each remote client to read latest state from container creator\n\t\t\t\t\t\tfor (const child of remoteClients) {\n\t\t\t\t\t\t\tchild.send({\n\t\t\t\t\t\t\t\tcommand: \"getLatestValue\",\n\t\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\t\tattendeeId: containerCreatorAttendeeId,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst getResponses = await getLatestValueResponses(\n\t\t\t\t\t\t\tremoteClients,\n\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\tchildErrorPromise,\n\t\t\t\t\t\t\tgetStateTimeoutMs,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Verify - all responses should contain the expected value\n\t\t\t\t\t\tfor (const getResponse of getResponses) {\n\t\t\t\t\t\t\tassert.deepStrictEqual(getResponse.value, testValue);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\n\t\t// This test suite focuses on the synchronization of LatestMap state between clients.\n\t\t// NOTE: For testing purposes child clients will expect a LatestMap value of type Record<string, string | number>.\n\t\tdescribe(`using LatestMap state object`, () => {\n\t\t\tfor (const numClients of [5, 20]) {\n\t\t\t\tassert(numClients > 1, \"Must have at least two clients\");\n\t\t\t\t/**\n\t\t\t\t * Timeout for child processes to connect to container ({@link ConnectedEvent})\n\t\t\t\t */\n\t\t\t\tconst childConnectTimeoutMs = (4000 + 1000 * numClients) * timeoutMultiplier;\n\t\t\t\tconst testCaseTimeoutMs = 1000;\n\t\t\t\tconst testSetupAndActTimeoutMs = childConnectTimeoutMs + testCaseTimeoutMs;\n\n\t\t\t\t// These tests use beforeEach to setup complex state that takes a lot of time\n\t\t\t\t// and is dependent on number of clients. Keeping the work in beforeEach\n\t\t\t\t// allows time reporting to report the tested scenario apart from the setup time.\n\t\t\t\t// So this describe block isolates those beforeEach setups from each distinct\n\t\t\t\t// client count. Test cases descriptions also have the client count for clarity.\n\t\t\t\tdescribe(`with ${numClients} clients`, () => {\n\t\t\t\t\tlet children: ChildProcess[];\n\t\t\t\t\tlet childErrorPromise: Promise<never>;\n\t\t\t\t\tlet containerCreatorAttendeeId: AttendeeId;\n\t\t\t\t\tlet attendeeIdPromises: Promise<AttendeeId>[];\n\t\t\t\t\tlet remoteClients: ChildProcess[];\n\t\t\t\t\tconst workspaceId = \"presenceTestWorkspace\";\n\t\t\t\t\tconst key1 = \"player1\";\n\t\t\t\t\tconst key2 = \"player2\";\n\t\t\t\t\tconst value1 = { name: \"Alice\", score: 100 };\n\t\t\t\t\tconst value2 = { name: \"Bob\", score: 200 };\n\n\t\t\t\t\tbeforeEach(async function usingLatestMapStateObject_beforeEach(): Promise<void> {\n\t\t\t\t\t\tconst startTime = performance.now();\n\n\t\t\t\t\t\tsetTestTimeout(this, testSetupAndActTimeoutMs);\n\n\t\t\t\t\t\t({ children, childErrorPromise } = await forkChildProcesses(\n\t\t\t\t\t\t\tthis.currentTest?.title ?? \"\",\n\t\t\t\t\t\t\tnumClients,\n\t\t\t\t\t\t\tafterCleanUp,\n\t\t\t\t\t\t));\n\t\t\t\t\t\t({ containerCreatorAttendeeId, attendeeIdPromises } = await connectChildProcesses(\n\t\t\t\t\t\t\tchildren,\n\t\t\t\t\t\t\t{ writeClients: numClients, readyTimeoutMs: childConnectTimeoutMs },\n\t\t\t\t\t\t));\n\t\t\t\t\t\tawait Promise.all(attendeeIdPromises);\n\t\t\t\t\t\tremoteClients = children.filter((_, index) => index !== 0);\n\t\t\t\t\t\t// 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>).\n\t\t\t\t\t\tawait registerWorkspaceOnChildren(children, workspaceId, {\n\t\t\t\t\t\t\tlatestMap: true,\n\t\t\t\t\t\t\ttimeoutMs: workspaceRegisterTimeoutMs,\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\ttestConsole.log(\n\t\t\t\t\t\t\t` Setup for \"${this.currentTest?.title}\" completed in ${performance.now() - startTime}ms`,\n\t\t\t\t\t\t);\n\t\t\t\t\t});\n\n\t\t\t\t\tit(`allows clients to read LatestMap values from other clients [${numClients} clients]`, async () => {\n\t\t\t\t\t\t// Setup\n\t\t\t\t\t\tconst testKey = \"cursor\";\n\t\t\t\t\t\tconst testValue = { x: 150, y: 300 };\n\t\t\t\t\t\tconst updateEventsPromise = waitForLatestMapValueUpdates(\n\t\t\t\t\t\t\tremoteClients,\n\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\ttestKey,\n\t\t\t\t\t\t\tchildErrorPromise,\n\t\t\t\t\t\t\tstateUpdateTimeoutMs,\n\t\t\t\t\t\t\t{ fromAttendeeId: containerCreatorAttendeeId, expectedValue: testValue },\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Act\n\t\t\t\t\t\tchildren[0].send({\n\t\t\t\t\t\t\tcommand: \"setLatestMapValue\",\n\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\tkey: testKey,\n\t\t\t\t\t\t\tvalue: testValue,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tconst updateEvents = await updateEventsPromise;\n\n\t\t\t\t\t\t// Check all events are from the expected attendee\n\t\t\t\t\t\tfor (const updateEvent of updateEvents) {\n\t\t\t\t\t\t\tassert.strictEqual(updateEvent.attendeeId, containerCreatorAttendeeId);\n\t\t\t\t\t\t\tassert.strictEqual(updateEvent.key, testKey);\n\t\t\t\t\t\t\tassert.deepStrictEqual(updateEvent.value, testValue);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor (const child of remoteClients) {\n\t\t\t\t\t\t\tchild.send({\n\t\t\t\t\t\t\t\tcommand: \"getLatestMapValue\",\n\t\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\t\tkey: testKey,\n\t\t\t\t\t\t\t\tattendeeId: containerCreatorAttendeeId,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst getResponses = await getLatestMapValueResponses(\n\t\t\t\t\t\t\tremoteClients,\n\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\ttestKey,\n\t\t\t\t\t\t\tchildErrorPromise,\n\t\t\t\t\t\t\tgetStateTimeoutMs,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Verify\n\t\t\t\t\t\tfor (const getResponse of getResponses) {\n\t\t\t\t\t\t\tassert.deepStrictEqual(getResponse.value, testValue);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\n\t\t\t\t\tit(`returns per-key values on read [${numClients} clients]`, async function () {\n\t\t\t\t\t\t// Setup\n\t\t\t\t\t\tconst allAttendeeIds = await Promise.all(attendeeIdPromises);\n\t\t\t\t\t\tconst attendee0Id = containerCreatorAttendeeId;\n\t\t\t\t\t\tconst attendee1Id = allAttendeeIds[1];\n\n\t\t\t\t\t\tconst key1Recipients = children.filter((_, index) => index !== 0);\n\t\t\t\t\t\tconst key2Recipients = children.filter((_, index) => index !== 1);\n\t\t\t\t\t\tconst key1UpdateEventsPromise = waitForLatestMapValueUpdates(\n\t\t\t\t\t\t\tkey1Recipients,\n\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\tkey1,\n\t\t\t\t\t\t\tchildErrorPromise,\n\t\t\t\t\t\t\tstateUpdateTimeoutMs,\n\t\t\t\t\t\t\t{ fromAttendeeId: attendee0Id, expectedValue: value1 },\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst key2UpdateEventsPromise = waitForLatestMapValueUpdates(\n\t\t\t\t\t\t\tkey2Recipients,\n\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\tkey2,\n\t\t\t\t\t\t\tchildErrorPromise,\n\t\t\t\t\t\t\tstateUpdateTimeoutMs,\n\t\t\t\t\t\t\t{ fromAttendeeId: attendee1Id, expectedValue: value2 },\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Act\n\t\t\t\t\t\tchildren[0].send({\n\t\t\t\t\t\t\tcommand: \"setLatestMapValue\",\n\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\tkey: key1,\n\t\t\t\t\t\t\tvalue: value1,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tconst key1UpdateEvents = await key1UpdateEventsPromise;\n\t\t\t\t\t\tchildren[1].send({\n\t\t\t\t\t\t\tcommand: \"setLatestMapValue\",\n\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\tkey: key2,\n\t\t\t\t\t\t\tvalue: value2,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tconst key2UpdateEvents = await key2UpdateEventsPromise;\n\n\t\t\t\t\t\t// Verify all events are from the expected attendees\n\t\t\t\t\t\tfor (const updateEvent of key1UpdateEvents) {\n\t\t\t\t\t\t\tassert.strictEqual(updateEvent.attendeeId, attendee0Id);\n\t\t\t\t\t\t\tassert.strictEqual(updateEvent.key, key1);\n\t\t\t\t\t\t\tassert.deepStrictEqual(updateEvent.value, value1);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor (const updateEvent of key2UpdateEvents) {\n\t\t\t\t\t\t\tassert.strictEqual(updateEvent.attendeeId, attendee1Id);\n\t\t\t\t\t\t\tassert.strictEqual(updateEvent.key, key2);\n\t\t\t\t\t\t\tassert.deepStrictEqual(updateEvent.value, value2);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Read key1 of attendee0 from all children\n\t\t\t\t\t\tfor (const child of children) {\n\t\t\t\t\t\t\tchild.send({\n\t\t\t\t\t\t\t\tcommand: \"getLatestMapValue\",\n\t\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\t\tkey: key1,\n\t\t\t\t\t\t\t\tattendeeId: attendee0Id,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst key1Responses = await getLatestMapValueResponses(\n\t\t\t\t\t\t\tchildren,\n\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\tkey1,\n\t\t\t\t\t\t\tchildErrorPromise,\n\t\t\t\t\t\t\tgetStateTimeoutMs,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Read key2 of attendee1 from all children\n\t\t\t\t\t\tfor (const child of children) {\n\t\t\t\t\t\t\tchild.send({\n\t\t\t\t\t\t\t\tcommand: \"getLatestMapValue\",\n\t\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\t\tkey: key2,\n\t\t\t\t\t\t\t\tattendeeId: attendee1Id,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst key2Responses = await getLatestMapValueResponses(\n\t\t\t\t\t\t\tchildren,\n\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\tkey2,\n\t\t\t\t\t\t\tchildErrorPromise,\n\t\t\t\t\t\t\tgetStateTimeoutMs,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Verify\n\t\t\t\t\t\tassert.strictEqual(\n\t\t\t\t\t\t\tkey1Responses.length,\n\t\t\t\t\t\t\tnumClients,\n\t\t\t\t\t\t\t\"Expected responses from all clients for key1\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tassert.strictEqual(\n\t\t\t\t\t\t\tkey2Responses.length,\n\t\t\t\t\t\t\tnumClients,\n\t\t\t\t\t\t\t\"Expected responses from all clients for key2\",\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tfor (const response of key1Responses) {\n\t\t\t\t\t\t\tassert.deepStrictEqual(response.value, value1, \"Key1 value should match\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor (const response of key2Responses) {\n\t\t\t\t\t\t\tassert.deepStrictEqual(response.value, value2, \"Key2 value should match\");\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t}\n});\n"]}
|
|
1
|
+
{"version":3,"file":"presenceTest.spec.js","sourceRoot":"","sources":["../../../src/test/multiprocess/presenceTest.spec.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { strict as assert } from \"node:assert\";\nimport type { ChildProcess } from \"node:child_process\";\nimport inspector from \"node:inspector\";\n\nimport type { AttendeeId } from \"@fluidframework/presence\";\nimport { timeoutAwait, timeoutPromise } from \"@fluidframework/test-utils/internal\";\n\nimport type { MessageFromChild } from \"./messageTypes.js\";\nimport {\n\tconnectAndListenForAttendees,\n\tconnectAndWaitForAttendees,\n\tconnectChildProcesses,\n\texecuteDebugReports,\n\tforkChildProcesses,\n\tgetLatestMapValueResponses,\n\tgetLatestValueResponses,\n\tregisterWorkspaceOnChildren,\n\ttestConsole,\n\twaitForLatestMapValueUpdates,\n\twaitForLatestValueUpdates,\n} from \"./orchestratorUtils.js\";\n\n/**\n * When true, slower (long running time) tests will be run.\n * Otherwise, those test will not appear. Console output is used to show that\n * they exist. (They could be skipped, though skipped test are often an\n * indication of a problem.)\n */\nconst shouldRunScaleTests = process.env.FLUID_TEST_SCALE !== undefined;\n\nconst useAzure = process.env.FLUID_CLIENT === \"azure\";\n\n/**\n * Detects if the debugger is attached (when code loaded).\n */\nconst debuggerAttached = inspector.url() !== undefined;\n\n/**\n * Set this to a high number when debugging to avoid timeouts from debugging time.\n */\nconst timeoutMultiplier = debuggerAttached ? 1000 : useAzure ? 5 : 1;\n\n/**\n * Sets the timeout for the given test context.\n *\n * @remarks\n * If a debugger is attached, the timeout is set to 0 to prevent timeouts during debugging.\n * Otherwise, it sets the timeout to the maximum of the current timeout and the specified duration.\n *\n * @param context - The Mocha test context.\n * @param duration - The duration in milliseconds to set the timeout to. Zero disables the timeout.\n */\nfunction setTestTimeout(context: Mocha.Context, duration: number): void {\n\tconst currentTimeout = context.timeout();\n\tconst newTimeout =\n\t\tdebuggerAttached || currentTimeout === 0 || duration === 0\n\t\t\t? 0\n\t\t\t: Math.max(currentTimeout, duration);\n\tif (newTimeout !== currentTimeout) {\n\t\ttestConsole.log(\n\t\t\t`${context.test?.title}: setting timeout to ${newTimeout}ms (was ${currentTimeout}ms)`,\n\t\t);\n\t\tcontext.timeout(newTimeout);\n\t}\n}\n\n/**\n * This test suite is a prototype for a multi-process end to end test for Fluid using the new Presence API on AzureClient.\n * In the future we hope to expand and generalize this pattern to broadly test more Fluid features.\n * Other E2E tests are limited to running multiple clients on a single process which does not effectively\n * simulate real-world production scenarios where clients are usually running on different machines. Since\n * the Fluid Framework client is designed to carry most of the work burden, multi-process testing from a\n * single machine is also not representative but does at least work past some limitations of a single\n * Node.js process handling multiple clients.\n *\n * The pattern demonstrated in this test suite is as follows:\n *\n * This main test file acts as the 'Orchestrator'. The orchestrator's job includes:\n * - Fork child processes to simulate multiple Fluid clients\n * - Send command messages to child clients to perform specific Fluid actions.\n * - Receive response messages from child clients to verify expected behavior.\n * - Clean up child processes after each test.\n *\n * The child processes are located in the `childClient.tool.ts` file. Each child process simulates a Fluid client.\n *\n * The child client's job includes:\n * - Create/Get + connect to Fluid container.\n * - Listen for command messages from the orchestrator.\n * - Perform the requested action.\n * - Send response messages including any relevant data back to the orchestrator to verify expected behavior.\n */\n\ndescribe(`Presence with AzureClient`, () => {\n\tconst afterCleanUp: (() => void)[] = [];\n\n\t// After each test, call any cleanup functions that were registered (kill each child process)\n\tafterEach(async () => {\n\t\tfor (const cleanUp of afterCleanUp) {\n\t\t\tcleanUp();\n\t\t}\n\t\tafterCleanUp.length = 0;\n\t});\n\n\tdescribe(\"`attendees` support\", () => {\n\t\tconst numClientsForAttendeeTests = [5, 40, 100, 250];\n\t\tfor (const numClients of numClientsForAttendeeTests) {\n\t\t\tif (numClients > 50 && !shouldRunScaleTests) {\n\t\t\t\ttestConsole.log(\n\t\t\t\t\t`skipping Presence attendee scale tests with ${numClients} clients (set FLUID_TEST_SCALE=true to run)`,\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tassert(numClients > 1, \"Must have at least two clients\");\n\t\t\t/**\n\t\t\t * Timeout for child processes to connect to container ({@link ConnectedEvent})\n\t\t\t */\n\t\t\tconst childConnectTimeoutMs = 1000 * numClients * timeoutMultiplier;\n\t\t\t/**\n\t\t\t * Timeout for presence attendees to join per first child perspective {@link AttendeeConnectedEvent}\n\t\t\t */\n\t\t\tconst allAttendeesJoinedTimeoutMs = (1000 + 200 * numClients) * timeoutMultiplier;\n\t\t\t/**\n\t\t\t * Timeout for presence attendees to fully join (everyone knows about everyone) {@link AttendeeConnectedEvent}\n\t\t\t */\n\t\t\tconst allAttendeesFullyJoinedTimeoutMs = (2000 + 300 * numClients) * timeoutMultiplier;\n\n\t\t\tfor (const writeClients of [numClients, 1]) {\n\t\t\t\tit(`announces 'attendeeConnected' when remote client joins session [${numClients} clients, ${writeClients} writers]`, async function testAnnouncesAttendeeConnected() {\n\t\t\t\t\t/**\n\t\t\t\t\t * Note: This test is currently skipped in the AFR driver due to General Network Errors in the Service Clients End to End tests pipelines.\n\t\t\t\t\t * For more context, see AB#59980.\n\t\t\t\t\t */\n\t\t\t\t\tif (useAzure && numClients > 50) {\n\t\t\t\t\t\tthis.skip();\n\t\t\t\t\t}\n\t\t\t\t\tsetTestTimeout(this, childConnectTimeoutMs + allAttendeesJoinedTimeoutMs + 1000);\n\n\t\t\t\t\t// Setup\n\t\t\t\t\tconst { children, childErrorPromise } = await forkChildProcesses(\n\t\t\t\t\t\tthis.test?.title ?? \"\",\n\t\t\t\t\t\tnumClients,\n\t\t\t\t\t\tafterCleanUp,\n\t\t\t\t\t);\n\n\t\t\t\t\t// Further Setup with Act and Verify\n\t\t\t\t\tawait connectAndWaitForAttendees(\n\t\t\t\t\t\tchildren,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\twriteClients,\n\t\t\t\t\t\t\tattendeeCountRequired: numClients - 1,\n\t\t\t\t\t\t\tchildConnectTimeoutMs,\n\t\t\t\t\t\t\tallAttendeesJoinedTimeoutMs,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tchildErrorPromise,\n\t\t\t\t\t);\n\t\t\t\t});\n\n\t\t\t\tit(`announces 'attendeeDisconnected' when remote client disconnects [${numClients} clients, ${writeClients} writers]`, async function testAnnouncesAttendeeDisconnected() {\n\t\t\t\t\tif (useAzure && numClients > 50) {\n\t\t\t\t\t\t// Even with increased timeouts, more than 50 clients can be too large for AFR.\n\t\t\t\t\t\t// This may be due to slow responses/inactivity from the clients that are\n\t\t\t\t\t\t// creating pressure on ADO agent.\n\t\t\t\t\t\tthis.skip();\n\t\t\t\t\t}\n\n\t\t\t\t\tconst childDisconnectTimeoutMs = 10_000 * timeoutMultiplier;\n\n\t\t\t\t\tsetTestTimeout(\n\t\t\t\t\t\tthis,\n\t\t\t\t\t\tchildConnectTimeoutMs +\n\t\t\t\t\t\t\tallAttendeesFullyJoinedTimeoutMs +\n\t\t\t\t\t\t\tchildDisconnectTimeoutMs +\n\t\t\t\t\t\t\t1000,\n\t\t\t\t\t);\n\n\t\t\t\t\t// Setup\n\t\t\t\t\tconst { children, childErrorPromise } = await forkChildProcesses(\n\t\t\t\t\t\tthis.test?.title ?? \"\",\n\t\t\t\t\t\tnumClients,\n\t\t\t\t\t\tafterCleanUp,\n\t\t\t\t\t);\n\n\t\t\t\t\tconst startConnectAndFullJoin = performance.now();\n\t\t\t\t\tconst connectResult = await connectAndListenForAttendees(children, {\n\t\t\t\t\t\twriteClients,\n\t\t\t\t\t\tattendeeCountRequired: numClients - 1,\n\t\t\t\t\t\tchildConnectTimeoutMs,\n\t\t\t\t\t});\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-floating-promises\n\t\t\t\t\tconnectResult.attendeeCountRequiredPromises[0].then(() =>\n\t\t\t\t\t\ttestConsole.log(\n\t\t\t\t\t\t\t`[${new Date().toISOString()}] All attendees joined per child 0 after ${performance.now() - startConnectAndFullJoin}ms`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\n\t\t\t\t\t// Wait for all attendees to be fully joined\n\t\t\t\t\t// Keep a tally for debuggability\n\t\t\t\t\tlet childrenFullyJoined = 0;\n\t\t\t\t\tconst setNotFullyJoined = new Set<number>();\n\t\t\t\t\tfor (let i = 0; i < children.length; i++) {\n\t\t\t\t\t\tsetNotFullyJoined.add(i);\n\t\t\t\t\t}\n\t\t\t\t\tconst allAttendeesFullyJoined = Promise.all(\n\t\t\t\t\t\tconnectResult.attendeeCountRequiredPromises.map(\n\t\t\t\t\t\t\tasync (attendeeFullyJoinedPromise, index) => {\n\t\t\t\t\t\t\t\tawait attendeeFullyJoinedPromise;\n\t\t\t\t\t\t\t\tchildrenFullyJoined++;\n\t\t\t\t\t\t\t\tsetNotFullyJoined.delete(index);\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\tlet timedout = true;\n\t\t\t\t\tconst allFullyJoinedOrChildError = Promise.race([\n\t\t\t\t\t\tallAttendeesFullyJoined,\n\t\t\t\t\t\tchildErrorPromise,\n\t\t\t\t\t]).finally(() => (timedout = false));\n\t\t\t\t\tawait timeoutAwait(allFullyJoinedOrChildError, {\n\t\t\t\t\t\tdurationMs: allAttendeesFullyJoinedTimeoutMs,\n\t\t\t\t\t\terrorMsg: \"Not all attendees fully joined\",\n\t\t\t\t\t}).catch(async (error) => {\n\t\t\t\t\t\t// Ideally this information would just be in the timeout error message, but that\n\t\t\t\t\t\t// must be a resolved string (not dynamic). So, just log it separately.\n\t\t\t\t\t\ttestConsole.log(\n\t\t\t\t\t\t\t`[${new Date().toISOString()}] ${childrenFullyJoined} attendees fully joined before error...`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (timedout) {\n\t\t\t\t\t\t\t// Gather additional timing data if timed out to understand what increased\n\t\t\t\t\t\t\t// timeout could work. Test will still fail if this secondary wait succeeds.\n\t\t\t\t\t\t\tconst startAdditionalWait = performance.now();\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tawait timeoutAwait(allFullyJoinedOrChildError, {\n\t\t\t\t\t\t\t\t\tdurationMs: allAttendeesFullyJoinedTimeoutMs,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\ttestConsole.log(\n\t\t\t\t\t\t\t\t\t`[${new Date().toISOString()}] All attendees fully joined after additional wait (${performance.now() - startAdditionalWait}ms)`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t} catch (secondaryError) {\n\t\t\t\t\t\t\t\ttestConsole.log(\n\t\t\t\t\t\t\t\t\t`[${new Date().toISOString()}] Secondary await resulted in`,\n\t\t\t\t\t\t\t\t\tsecondaryError,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Gather and report debug info from children\n\t\t\t\t\t\t// If there are less than 10 children, get all reports.\n\t\t\t\t\t\t// Otherwise, just child 0 and those not fully joined.\n\t\t\t\t\t\tsetTestTimeout(this, 0); // Disable test timeout. Will throw within 20s below.\n\t\t\t\t\t\tconst childrenRequestedToReport =\n\t\t\t\t\t\t\tchildren.length <= 10\n\t\t\t\t\t\t\t\t? children\n\t\t\t\t\t\t\t\t: // Just those not fully joined\n\t\t\t\t\t\t\t\t\tchildren.filter((_, index) => index === 0 || setNotFullyJoined.has(index));\n\t\t\t\t\t\tawait timeoutAwait(\n\t\t\t\t\t\t\tPromise.race([\n\t\t\t\t\t\t\t\texecuteDebugReports(childrenRequestedToReport),\n\t\t\t\t\t\t\t\tchildErrorPromise,\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t{ durationMs: 20_000, errorMsg: \"Debug report timeout\" },\n\t\t\t\t\t\t).catch((debugAwaitError) => {\n\t\t\t\t\t\t\ttestConsole.error(\"Debug report await resulted in\", debugAwaitError);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t});\n\t\t\t\t\ttestConsole.log(\n\t\t\t\t\t\t`[${new Date().toISOString()}] All attendees fully joined after ${performance.now() - startConnectAndFullJoin}ms`,\n\t\t\t\t\t);\n\n\t\t\t\t\tlet child0ReportRequested = false;\n\t\t\t\t\tconst waitForDisconnected = children.map(async (child, index) =>\n\t\t\t\t\t\tindex === 0\n\t\t\t\t\t\t\t? Promise.resolve()\n\t\t\t\t\t\t\t: timeoutPromise(\n\t\t\t\t\t\t\t\t\t(resolve) => {\n\t\t\t\t\t\t\t\t\t\tchild.on(\"message\", (msg: MessageFromChild) => {\n\t\t\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\t\t\tmsg.event === \"attendeeDisconnected\" &&\n\t\t\t\t\t\t\t\t\t\t\t\tmsg.attendeeId === connectResult.containerCreatorAttendeeId\n\t\t\t\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\t\t\t\tconsole.log(`Child[${index}] saw creator disconnect`);\n\t\t\t\t\t\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tdurationMs: childDisconnectTimeoutMs,\n\t\t\t\t\t\t\t\t\t\terrorMsg: `Attendee[${index}] Disconnected Timeout`,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t).catch(async (error) => {\n\t\t\t\t\t\t\t\t\tconst childrenRequestedToReport = [child];\n\t\t\t\t\t\t\t\t\tif (!child0ReportRequested) {\n\t\t\t\t\t\t\t\t\t\tchildrenRequestedToReport.unshift(children[0]);\n\t\t\t\t\t\t\t\t\t\tchild0ReportRequested = true;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tawait timeoutAwait(\n\t\t\t\t\t\t\t\t\t\tPromise.race([\n\t\t\t\t\t\t\t\t\t\t\texecuteDebugReports(childrenRequestedToReport),\n\t\t\t\t\t\t\t\t\t\t\tchildErrorPromise,\n\t\t\t\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t\t\t\t{ durationMs: 20_000, errorMsg: \"Debug report timeout\" },\n\t\t\t\t\t\t\t\t\t).catch((debugAwaitError) => {\n\t\t\t\t\t\t\t\t\t\ttestConsole.error(\"Debug report await resulted in\", debugAwaitError);\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\n\t\t\t\t\t// Act - disconnect first child process\n\t\t\t\t\tchildren[0].send({ command: \"disconnectSelf\" });\n\n\t\t\t\t\t// Verify - wait for all 'attendeeDisconnected' events\n\t\t\t\t\tawait Promise.race([Promise.all(waitForDisconnected), childErrorPromise]);\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t});\n\n\t{\n\t\t/**\n\t\t * Timeout for workspace registration {@link WorkspaceRegisteredEvent}\n\t\t */\n\t\tconst workspaceRegisterTimeoutMs = 5000;\n\t\t/**\n\t\t * Timeout for presence update events {@link LatestMapValueUpdatedEvent} and {@link LatestValueUpdatedEvent}\n\t\t */\n\t\tconst stateUpdateTimeoutMs = 5000;\n\t\t/**\n\t\t * Timeout for {@link LatestMapValueGetResponseEvent} and {@link LatestValueGetResponseEvent}\n\t\t */\n\t\tconst getStateTimeoutMs = 5000;\n\n\t\t// This test suite focuses on the synchronization of Latest state between clients.\n\t\t// NOTE: For testing purposes child clients will expect a Latest value of type string.\n\t\tdescribe(`using Latest state object`, () => {\n\t\t\tfor (const numClients of [5, 20]) {\n\t\t\t\tassert(numClients > 1, \"Must have at least two clients\");\n\t\t\t\t/**\n\t\t\t\t * Timeout for child processes to connect to container ({@link ConnectedEvent})\n\t\t\t\t */\n\t\t\t\tconst childConnectTimeoutMs = (4000 + 1000 * numClients) * timeoutMultiplier;\n\t\t\t\tconst testCaseTimeoutMs = 1000;\n\t\t\t\tconst testSetupAndActTimeoutMs = childConnectTimeoutMs + testCaseTimeoutMs;\n\n\t\t\t\t// These tests use beforeEach to setup complex state that takes a lot of time\n\t\t\t\t// and is dependent on number of clients. Keeping the work in beforeEach\n\t\t\t\t// allows time reporting to report the tested scenario apart from the setup time.\n\t\t\t\t// So this describe block isolates those beforeEach setups from each distinct\n\t\t\t\t// client count. Test cases descriptions also have the client count for clarity.\n\t\t\t\tdescribe(`with ${numClients} clients`, () => {\n\t\t\t\t\tlet children: ChildProcess[];\n\t\t\t\t\tlet childErrorPromise: Promise<never>;\n\t\t\t\t\tlet containerCreatorAttendeeId: AttendeeId;\n\t\t\t\t\tlet attendeeIdPromises: Promise<AttendeeId>[];\n\t\t\t\t\tlet remoteClients: ChildProcess[];\n\t\t\t\t\tconst testValue = \"testValue\";\n\t\t\t\t\tconst workspaceId = \"presenceTestWorkspace\";\n\n\t\t\t\t\tbeforeEach(async function usingLatestStateObject_beforeEach(): Promise<void> {\n\t\t\t\t\t\tconst startTime = performance.now();\n\t\t\t\t\t\tsetTestTimeout(this, testSetupAndActTimeoutMs);\n\n\t\t\t\t\t\t({ children, childErrorPromise } = await forkChildProcesses(\n\t\t\t\t\t\t\tthis.currentTest?.title ?? \"\",\n\t\t\t\t\t\t\tnumClients,\n\t\t\t\t\t\t\tafterCleanUp,\n\t\t\t\t\t\t));\n\t\t\t\t\t\t({ containerCreatorAttendeeId, attendeeIdPromises } = await connectChildProcesses(\n\t\t\t\t\t\t\tchildren,\n\t\t\t\t\t\t\t{ writeClients: numClients, readyTimeoutMs: childConnectTimeoutMs },\n\t\t\t\t\t\t));\n\t\t\t\t\t\tawait Promise.all(attendeeIdPromises);\n\t\t\t\t\t\tremoteClients = children.filter((_, index) => index !== 0);\n\t\t\t\t\t\t// NOTE: For testing purposes child clients will expect a Latest value of type string (StateFactory.latest<{ value: string }>).\n\t\t\t\t\t\tawait registerWorkspaceOnChildren(children, workspaceId, {\n\t\t\t\t\t\t\tlatest: true,\n\t\t\t\t\t\t\ttimeoutMs: workspaceRegisterTimeoutMs,\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\ttestConsole.log(\n\t\t\t\t\t\t\t` Setup for \"${this.currentTest?.title}\" completed in ${performance.now() - startTime}ms`,\n\t\t\t\t\t\t);\n\t\t\t\t\t});\n\n\t\t\t\t\tit(`allows clients to read Latest state from other clients [${numClients} clients]`, async function () {\n\t\t\t\t\t\t// Setup\n\t\t\t\t\t\tconst updateEventsPromise = waitForLatestValueUpdates(\n\t\t\t\t\t\t\tremoteClients,\n\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\tchildErrorPromise,\n\t\t\t\t\t\t\tstateUpdateTimeoutMs,\n\t\t\t\t\t\t\t{ fromAttendeeId: containerCreatorAttendeeId, expectedValue: testValue },\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Act - Trigger the update\n\t\t\t\t\t\tchildren[0].send({\n\t\t\t\t\t\t\tcommand: \"setLatestValue\",\n\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\tvalue: testValue,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tconst updateEvents = await updateEventsPromise;\n\n\t\t\t\t\t\t// Verify all events are from the expected attendee\n\t\t\t\t\t\tfor (const updateEvent of updateEvents) {\n\t\t\t\t\t\t\tassert.strictEqual(updateEvent.attendeeId, containerCreatorAttendeeId);\n\t\t\t\t\t\t\tassert.deepStrictEqual(updateEvent.value, testValue);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Act - Request each remote client to read latest state from container creator\n\t\t\t\t\t\tfor (const child of remoteClients) {\n\t\t\t\t\t\t\tchild.send({\n\t\t\t\t\t\t\t\tcommand: \"getLatestValue\",\n\t\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\t\tattendeeId: containerCreatorAttendeeId,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst getResponses = await getLatestValueResponses(\n\t\t\t\t\t\t\tremoteClients,\n\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\tchildErrorPromise,\n\t\t\t\t\t\t\tgetStateTimeoutMs,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Verify - all responses should contain the expected value\n\t\t\t\t\t\tfor (const getResponse of getResponses) {\n\t\t\t\t\t\t\tassert.deepStrictEqual(getResponse.value, testValue);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\n\t\t// This test suite focuses on the synchronization of LatestMap state between clients.\n\t\t// NOTE: For testing purposes child clients will expect a LatestMap value of type Record<string, string | number>.\n\t\tdescribe(`using LatestMap state object`, () => {\n\t\t\tfor (const numClients of [5, 20]) {\n\t\t\t\tassert(numClients > 1, \"Must have at least two clients\");\n\t\t\t\t/**\n\t\t\t\t * Timeout for child processes to connect to container ({@link ConnectedEvent})\n\t\t\t\t */\n\t\t\t\tconst childConnectTimeoutMs = (4000 + 1000 * numClients) * timeoutMultiplier;\n\t\t\t\tconst testCaseTimeoutMs = 1000;\n\t\t\t\tconst testSetupAndActTimeoutMs = childConnectTimeoutMs + testCaseTimeoutMs;\n\n\t\t\t\t// These tests use beforeEach to setup complex state that takes a lot of time\n\t\t\t\t// and is dependent on number of clients. Keeping the work in beforeEach\n\t\t\t\t// allows time reporting to report the tested scenario apart from the setup time.\n\t\t\t\t// So this describe block isolates those beforeEach setups from each distinct\n\t\t\t\t// client count. Test cases descriptions also have the client count for clarity.\n\t\t\t\tdescribe(`with ${numClients} clients`, () => {\n\t\t\t\t\tlet children: ChildProcess[];\n\t\t\t\t\tlet childErrorPromise: Promise<never>;\n\t\t\t\t\tlet containerCreatorAttendeeId: AttendeeId;\n\t\t\t\t\tlet attendeeIdPromises: Promise<AttendeeId>[];\n\t\t\t\t\tlet remoteClients: ChildProcess[];\n\t\t\t\t\tconst workspaceId = \"presenceTestWorkspace\";\n\t\t\t\t\tconst key1 = \"player1\";\n\t\t\t\t\tconst key2 = \"player2\";\n\t\t\t\t\tconst value1 = { name: \"Alice\", score: 100 };\n\t\t\t\t\tconst value2 = { name: \"Bob\", score: 200 };\n\n\t\t\t\t\tbeforeEach(async function usingLatestMapStateObject_beforeEach(): Promise<void> {\n\t\t\t\t\t\tconst startTime = performance.now();\n\n\t\t\t\t\t\tsetTestTimeout(this, testSetupAndActTimeoutMs);\n\n\t\t\t\t\t\t({ children, childErrorPromise } = await forkChildProcesses(\n\t\t\t\t\t\t\tthis.currentTest?.title ?? \"\",\n\t\t\t\t\t\t\tnumClients,\n\t\t\t\t\t\t\tafterCleanUp,\n\t\t\t\t\t\t));\n\t\t\t\t\t\t({ containerCreatorAttendeeId, attendeeIdPromises } = await connectChildProcesses(\n\t\t\t\t\t\t\tchildren,\n\t\t\t\t\t\t\t{ writeClients: numClients, readyTimeoutMs: childConnectTimeoutMs },\n\t\t\t\t\t\t));\n\t\t\t\t\t\tawait Promise.all(attendeeIdPromises);\n\t\t\t\t\t\tremoteClients = children.filter((_, index) => index !== 0);\n\t\t\t\t\t\t// 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>).\n\t\t\t\t\t\tawait registerWorkspaceOnChildren(children, workspaceId, {\n\t\t\t\t\t\t\tlatestMap: true,\n\t\t\t\t\t\t\ttimeoutMs: workspaceRegisterTimeoutMs,\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\ttestConsole.log(\n\t\t\t\t\t\t\t` Setup for \"${this.currentTest?.title}\" completed in ${performance.now() - startTime}ms`,\n\t\t\t\t\t\t);\n\t\t\t\t\t});\n\n\t\t\t\t\tit(`allows clients to read LatestMap values from other clients [${numClients} clients]`, async () => {\n\t\t\t\t\t\t// Setup\n\t\t\t\t\t\tconst testKey = \"cursor\";\n\t\t\t\t\t\tconst testValue = { x: 150, y: 300 };\n\t\t\t\t\t\tconst updateEventsPromise = waitForLatestMapValueUpdates(\n\t\t\t\t\t\t\tremoteClients,\n\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\ttestKey,\n\t\t\t\t\t\t\tchildErrorPromise,\n\t\t\t\t\t\t\tstateUpdateTimeoutMs,\n\t\t\t\t\t\t\t{ fromAttendeeId: containerCreatorAttendeeId, expectedValue: testValue },\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Act\n\t\t\t\t\t\tchildren[0].send({\n\t\t\t\t\t\t\tcommand: \"setLatestMapValue\",\n\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\tkey: testKey,\n\t\t\t\t\t\t\tvalue: testValue,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tconst updateEvents = await updateEventsPromise;\n\n\t\t\t\t\t\t// Check all events are from the expected attendee\n\t\t\t\t\t\tfor (const updateEvent of updateEvents) {\n\t\t\t\t\t\t\tassert.strictEqual(updateEvent.attendeeId, containerCreatorAttendeeId);\n\t\t\t\t\t\t\tassert.strictEqual(updateEvent.key, testKey);\n\t\t\t\t\t\t\tassert.deepStrictEqual(updateEvent.value, testValue);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor (const child of remoteClients) {\n\t\t\t\t\t\t\tchild.send({\n\t\t\t\t\t\t\t\tcommand: \"getLatestMapValue\",\n\t\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\t\tkey: testKey,\n\t\t\t\t\t\t\t\tattendeeId: containerCreatorAttendeeId,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst getResponses = await getLatestMapValueResponses(\n\t\t\t\t\t\t\tremoteClients,\n\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\ttestKey,\n\t\t\t\t\t\t\tchildErrorPromise,\n\t\t\t\t\t\t\tgetStateTimeoutMs,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Verify\n\t\t\t\t\t\tfor (const getResponse of getResponses) {\n\t\t\t\t\t\t\tassert.deepStrictEqual(getResponse.value, testValue);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\n\t\t\t\t\tit(`returns per-key values on read [${numClients} clients]`, async function () {\n\t\t\t\t\t\t// Setup\n\t\t\t\t\t\tconst allAttendeeIds = await Promise.all(attendeeIdPromises);\n\t\t\t\t\t\tconst attendee0Id = containerCreatorAttendeeId;\n\t\t\t\t\t\tconst attendee1Id = allAttendeeIds[1];\n\n\t\t\t\t\t\tconst key1Recipients = children.filter((_, index) => index !== 0);\n\t\t\t\t\t\tconst key2Recipients = children.filter((_, index) => index !== 1);\n\t\t\t\t\t\tconst key1UpdateEventsPromise = waitForLatestMapValueUpdates(\n\t\t\t\t\t\t\tkey1Recipients,\n\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\tkey1,\n\t\t\t\t\t\t\tchildErrorPromise,\n\t\t\t\t\t\t\tstateUpdateTimeoutMs,\n\t\t\t\t\t\t\t{ fromAttendeeId: attendee0Id, expectedValue: value1 },\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst key2UpdateEventsPromise = waitForLatestMapValueUpdates(\n\t\t\t\t\t\t\tkey2Recipients,\n\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\tkey2,\n\t\t\t\t\t\t\tchildErrorPromise,\n\t\t\t\t\t\t\tstateUpdateTimeoutMs,\n\t\t\t\t\t\t\t{ fromAttendeeId: attendee1Id, expectedValue: value2 },\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Act\n\t\t\t\t\t\tchildren[0].send({\n\t\t\t\t\t\t\tcommand: \"setLatestMapValue\",\n\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\tkey: key1,\n\t\t\t\t\t\t\tvalue: value1,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tconst key1UpdateEvents = await key1UpdateEventsPromise;\n\t\t\t\t\t\tchildren[1].send({\n\t\t\t\t\t\t\tcommand: \"setLatestMapValue\",\n\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\tkey: key2,\n\t\t\t\t\t\t\tvalue: value2,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tconst key2UpdateEvents = await key2UpdateEventsPromise;\n\n\t\t\t\t\t\t// Verify all events are from the expected attendees\n\t\t\t\t\t\tfor (const updateEvent of key1UpdateEvents) {\n\t\t\t\t\t\t\tassert.strictEqual(updateEvent.attendeeId, attendee0Id);\n\t\t\t\t\t\t\tassert.strictEqual(updateEvent.key, key1);\n\t\t\t\t\t\t\tassert.deepStrictEqual(updateEvent.value, value1);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor (const updateEvent of key2UpdateEvents) {\n\t\t\t\t\t\t\tassert.strictEqual(updateEvent.attendeeId, attendee1Id);\n\t\t\t\t\t\t\tassert.strictEqual(updateEvent.key, key2);\n\t\t\t\t\t\t\tassert.deepStrictEqual(updateEvent.value, value2);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Read key1 of attendee0 from all children\n\t\t\t\t\t\tfor (const child of children) {\n\t\t\t\t\t\t\tchild.send({\n\t\t\t\t\t\t\t\tcommand: \"getLatestMapValue\",\n\t\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\t\tkey: key1,\n\t\t\t\t\t\t\t\tattendeeId: attendee0Id,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst key1Responses = await getLatestMapValueResponses(\n\t\t\t\t\t\t\tchildren,\n\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\tkey1,\n\t\t\t\t\t\t\tchildErrorPromise,\n\t\t\t\t\t\t\tgetStateTimeoutMs,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Read key2 of attendee1 from all children\n\t\t\t\t\t\tfor (const child of children) {\n\t\t\t\t\t\t\tchild.send({\n\t\t\t\t\t\t\t\tcommand: \"getLatestMapValue\",\n\t\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\t\tkey: key2,\n\t\t\t\t\t\t\t\tattendeeId: attendee1Id,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst key2Responses = await getLatestMapValueResponses(\n\t\t\t\t\t\t\tchildren,\n\t\t\t\t\t\t\tworkspaceId,\n\t\t\t\t\t\t\tkey2,\n\t\t\t\t\t\t\tchildErrorPromise,\n\t\t\t\t\t\t\tgetStateTimeoutMs,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Verify\n\t\t\t\t\t\tassert.strictEqual(\n\t\t\t\t\t\t\tkey1Responses.length,\n\t\t\t\t\t\t\tnumClients,\n\t\t\t\t\t\t\t\"Expected responses from all clients for key1\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tassert.strictEqual(\n\t\t\t\t\t\t\tkey2Responses.length,\n\t\t\t\t\t\t\tnumClients,\n\t\t\t\t\t\t\t\"Expected responses from all clients for key2\",\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tfor (const response of key1Responses) {\n\t\t\t\t\t\t\tassert.deepStrictEqual(response.value, value1, \"Key1 value should match\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor (const response of key2Responses) {\n\t\t\t\t\t\t\tassert.deepStrictEqual(response.value, value2, \"Key2 value should match\");\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t}\n});\n"]}
|
package/lib/test/signals.spec.js
CHANGED
|
@@ -95,7 +95,7 @@ for (const testOpts of testMatrix) {
|
|
|
95
95
|
* Scenario: Client sends a signal and connected clients receive it.
|
|
96
96
|
*
|
|
97
97
|
* Expected behavior: While 2 clients are connected to a container,
|
|
98
|
-
* a signal sent by 1 client should be
|
|
98
|
+
* a signal sent by 1 client should be received by both clients.
|
|
99
99
|
*/
|
|
100
100
|
it("can send and receive signals", async () => {
|
|
101
101
|
const { signaler, containerId } = await getOrCreateSignalerContainer(undefined, user1);
|
|
@@ -115,7 +115,7 @@ for (const testOpts of testMatrix) {
|
|
|
115
115
|
* Scenario: Read and Write clients send signals and connected clients receive them.
|
|
116
116
|
*
|
|
117
117
|
* Expected behavior: While 2 clients are connected (1 writer, 2 readers) to a container,
|
|
118
|
-
* a signal sent by any 1 client should be
|
|
118
|
+
* a signal sent by any 1 client should be received by all 3 clients, regardless of read/write permissions.
|
|
119
119
|
*/
|
|
120
120
|
it("can send and receive read-only client signals", async function () {
|
|
121
121
|
const { signaler: writeSignaler, containerId } = await getOrCreateSignalerContainer(undefined, user1);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signals.spec.js","sourceRoot":"","sources":["../../src/test/signals.spec.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { strict as assert } from \"node:assert\";\n\nimport type { AzureClient, AzureContainerServices } from \"@fluidframework/azure-client\";\nimport { AttachState } from \"@fluidframework/container-definitions\";\nimport { ConnectionState } from \"@fluidframework/container-loader\";\nimport type { ContainerSchema, IFluidContainer } from \"@fluidframework/fluid-static\";\nimport { timeoutPromise } from \"@fluidframework/test-utils/internal\";\n\nimport {\n\tcreateAzureClient,\n\tcreateContainerFromPayload,\n\tgetContainerIdFromPayloadResponse,\n\tScopeType,\n} from \"./AzureClientFactory.js\";\nimport { SignalerTestDataObject } from \"./TestDataObject.js\";\nimport * as ephemeralSummaryTrees from \"./ephemeralSummaryTrees.js\";\nimport { configProvider, getTestMatrix } from \"./utils.js\";\n\ninterface UserIdAndName {\n\treadonly id: string;\n\treadonly name: string;\n}\n\nasync function createSignalListenerPromise<T>(\n\tsignaler: SignalerTestDataObject,\n\tsignalType: string,\n\texpectedPayload: T,\n\tname: string = \"Signal\",\n\ttimeoutMs: number = 10_000,\n): Promise<T> {\n\treturn timeoutPromise(\n\t\t(resolve, reject) => {\n\t\t\tsignaler.onSignal<T>(signalType, (clientId, local, receivedPayload) => {\n\t\t\t\ttry {\n\t\t\t\t\tassert.deepStrictEqual(\n\t\t\t\t\t\treceivedPayload,\n\t\t\t\t\t\texpectedPayload,\n\t\t\t\t\t\t\"Received payload does not match expected payload\",\n\t\t\t\t\t);\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn reject(error);\n\t\t\t\t}\n\t\t\t\tresolve(receivedPayload);\n\t\t\t});\n\t\t},\n\t\t{ durationMs: timeoutMs, errorMsg: `${name}: listener timeout` },\n\t);\n}\n\nconst testMatrix = getTestMatrix();\nfor (const testOpts of testMatrix) {\n\tdescribe(`Fluid Signals (${testOpts.variant})`, () => {\n\t\tconst connectedContainers: IFluidContainer[] = [];\n\t\tconst connectTimeoutMs = 10_000;\n\t\tconst isEphemeral: boolean = testOpts.options.isEphemeral;\n\t\tconst user1 = {\n\t\t\tid: \"test-user-id-1\",\n\t\t\tname: \"test-user-name-2\",\n\t\t} as const satisfies UserIdAndName;\n\t\tconst user2 = {\n\t\t\tid: \"test-user-id-1\",\n\t\t\tname: \"test-user-name-2\",\n\t\t} as const satisfies UserIdAndName;\n\t\tconst user3 = {\n\t\t\tid: \"test-user-id-1\",\n\t\t\tname: \"test-user-name-2\",\n\t\t} as const satisfies UserIdAndName;\n\n\t\tafterEach(async () => {\n\t\t\tfor (const container of connectedContainers) {\n\t\t\t\tcontainer.disconnect();\n\t\t\t\tcontainer.dispose();\n\t\t\t}\n\t\t\tconnectedContainers.splice(0, connectedContainers.length);\n\t\t});\n\n\t\tconst getOrCreateSignalerContainer = async (\n\t\t\tid: string | undefined,\n\t\t\tuser: UserIdAndName,\n\t\t\tconfig?: ReturnType<typeof configProvider>,\n\t\t\tscopes?: ScopeType[],\n\t\t): Promise<{\n\t\t\tcontainer: IFluidContainer;\n\t\t\tsignaler: SignalerTestDataObject;\n\t\t\tservices: AzureContainerServices;\n\t\t\tclient: AzureClient;\n\t\t\tcontainerId: string;\n\t\t}> => {\n\t\t\tconst client = createAzureClient(user.id, user.name, undefined, config, scopes);\n\t\t\tconst schema: ContainerSchema = {\n\t\t\t\tinitialObjects: {\n\t\t\t\t\tsignaler: SignalerTestDataObject,\n\t\t\t\t},\n\t\t\t};\n\t\t\tlet container: IFluidContainer;\n\t\t\tlet services: AzureContainerServices;\n\t\t\tlet containerId: string;\n\t\t\tif (id === undefined) {\n\t\t\t\tif (isEphemeral) {\n\t\t\t\t\tconst containerResponse = await createContainerFromPayload(\n\t\t\t\t\t\tephemeralSummaryTrees.sendAndRecieveSignals,\n\t\t\t\t\t\t\"test-user-id-1\",\n\t\t\t\t\t\t\"test-user-name-1\",\n\t\t\t\t\t);\n\t\t\t\t\tcontainerId = getContainerIdFromPayloadResponse(containerResponse);\n\t\t\t\t\t({ container, services } = await client.getContainer(containerId, schema, \"2\"));\n\t\t\t\t} else {\n\t\t\t\t\t({ container, services } = await client.createContainer(schema, \"2\"));\n\t\t\t\t\tcontainerId = await container.attach();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcontainerId = id;\n\t\t\t\t({ container, services } = await client.getContainer(containerId, schema, \"2\"));\n\t\t\t}\n\n\t\t\tif (container.connectionState !== ConnectionState.Connected) {\n\t\t\t\tawait timeoutPromise((resolve) => container.once(\"connected\", () => resolve()), {\n\t\t\t\t\tdurationMs: connectTimeoutMs,\n\t\t\t\t\terrorMsg: \"container connect() timeout\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tconnectedContainers.push(container);\n\n\t\t\tassert.strictEqual(typeof containerId, \"string\", \"Attach did not return a string ID\");\n\t\t\tassert.strictEqual(\n\t\t\t\tcontainer.attachState,\n\t\t\t\tAttachState.Attached,\n\t\t\t\t\"Container is not attached after attach is called\",\n\t\t\t);\n\n\t\t\tconst signaler = container.initialObjects.signaler as SignalerTestDataObject;\n\t\t\treturn {\n\t\t\t\tclient,\n\t\t\t\tcontainer,\n\t\t\t\tsignaler,\n\t\t\t\tservices,\n\t\t\t\tcontainerId,\n\t\t\t};\n\t\t};\n\n\t\t/**\n\t\t * Scenario: Client sends a signal and connected clients receive it.\n\t\t *\n\t\t * Expected behavior: While 2 clients are connected to a container,\n\t\t * a signal sent by 1 client should be recieved by both clients.\n\t\t */\n\t\tit(\"can send and receive signals\", async () => {\n\t\t\tconst { signaler, containerId } = await getOrCreateSignalerContainer(undefined, user1);\n\t\t\tconst { signaler: signaler2 } = await getOrCreateSignalerContainer(\n\t\t\t\tcontainerId,\n\t\t\t\tuser2,\n\t\t\t\tconfigProvider({\n\t\t\t\t\t\"Fluid.Container.ForceWriteConnection\": true,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tconst signalName = \"test-signal\";\n\t\t\tconst signalPayload = { test: \"payload\" };\n\n\t\t\tconst listenerPromises = [\n\t\t\t\tcreateSignalListenerPromise(\n\t\t\t\t\tsignaler2,\n\t\t\t\t\tsignalName,\n\t\t\t\t\tsignalPayload,\n\t\t\t\t\t\"Write client listening for write client signal\",\n\t\t\t\t),\n\t\t\t\tcreateSignalListenerPromise(\n\t\t\t\t\tsignaler,\n\t\t\t\t\tsignalName,\n\t\t\t\t\tsignalPayload,\n\t\t\t\t\t\"Write client listening for its own signal\",\n\t\t\t\t),\n\t\t\t];\n\n\t\t\tsignaler.submitSignal(signalName, signalPayload);\n\n\t\t\tawait assert.doesNotReject(\n\t\t\t\tPromise.all(listenerPromises),\n\t\t\t\t\"Listening clients should receive signals.\",\n\t\t\t);\n\t\t});\n\n\t\t/**\n\t\t * Scenario: Read and Write clients send signals and connected clients receive them.\n\t\t *\n\t\t * Expected behavior: While 2 clients are connected (1 writer, 2 readers) to a container,\n\t\t * a signal sent by any 1 client should be recieved by all 3 clients, regardless of read/write permissions.\n\t\t */\n\t\tit(\"can send and receive read-only client signals\", async function () {\n\t\t\tconst { signaler: writeSignaler, containerId } = await getOrCreateSignalerContainer(\n\t\t\t\tundefined,\n\t\t\t\tuser1,\n\t\t\t);\n\t\t\tconst { signaler: readSignaler } = await getOrCreateSignalerContainer(\n\t\t\t\tcontainerId,\n\t\t\t\tuser2,\n\t\t\t\tundefined,\n\t\t\t\t[ScopeType.DocRead],\n\t\t\t);\n\t\t\tconst { signaler: readSignaler2 } = await getOrCreateSignalerContainer(\n\t\t\t\tcontainerId,\n\t\t\t\tuser3,\n\t\t\t\tundefined,\n\t\t\t\t[ScopeType.DocRead],\n\t\t\t);\n\n\t\t\tconst signalName = \"test-signal\";\n\n\t\t\tconst signalPayload1 = { test: \"payload\" };\n\t\t\tconst listenerPromises1 = [\n\t\t\t\tcreateSignalListenerPromise(\n\t\t\t\t\twriteSignaler,\n\t\t\t\t\tsignalName,\n\t\t\t\t\tsignalPayload1,\n\t\t\t\t\t\"Write client listening for read client signal\",\n\t\t\t\t),\n\t\t\t\tcreateSignalListenerPromise(\n\t\t\t\t\treadSignaler2,\n\t\t\t\t\tsignalName,\n\t\t\t\t\tsignalPayload1,\n\t\t\t\t\t\"Read client 2 listening for read client signal\",\n\t\t\t\t),\n\t\t\t\tcreateSignalListenerPromise(\n\t\t\t\t\treadSignaler,\n\t\t\t\t\tsignalName,\n\t\t\t\t\tsignalPayload1,\n\t\t\t\t\t\"Read client listening for its own signal\",\n\t\t\t\t),\n\t\t\t];\n\t\t\treadSignaler.submitSignal(signalName, signalPayload1);\n\t\t\tawait assert.doesNotReject(\n\t\t\t\tPromise.all(listenerPromises1),\n\t\t\t\t\"Listening clients should receive signals from read clients.\",\n\t\t\t);\n\n\t\t\tconst signalPayload2 = { test: \"payload2\" };\n\t\t\tconst listenerPromises2 = [\n\t\t\t\tcreateSignalListenerPromise(\n\t\t\t\t\treadSignaler,\n\t\t\t\t\tsignalName,\n\t\t\t\t\tsignalPayload2,\n\t\t\t\t\t\"Read client listening for write client signal\",\n\t\t\t\t),\n\t\t\t\tcreateSignalListenerPromise(\n\t\t\t\t\treadSignaler2,\n\t\t\t\t\tsignalName,\n\t\t\t\t\tsignalPayload2,\n\t\t\t\t\t\"Read client 2 listening for write client signal\",\n\t\t\t\t),\n\t\t\t\tcreateSignalListenerPromise(\n\t\t\t\t\twriteSignaler,\n\t\t\t\t\tsignalName,\n\t\t\t\t\tsignalPayload2,\n\t\t\t\t\t\"Write client listening for its own signal\",\n\t\t\t\t),\n\t\t\t];\n\t\t\twriteSignaler.submitSignal(signalName, signalPayload2);\n\t\t\tawait assert.doesNotReject(\n\t\t\t\tPromise.all(listenerPromises2),\n\t\t\t\t\"Listening clients should receive signals from write clients.\",\n\t\t\t);\n\t\t});\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"signals.spec.js","sourceRoot":"","sources":["../../src/test/signals.spec.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { strict as assert } from \"node:assert\";\n\nimport type { AzureClient, AzureContainerServices } from \"@fluidframework/azure-client\";\nimport { AttachState } from \"@fluidframework/container-definitions\";\nimport { ConnectionState } from \"@fluidframework/container-loader\";\nimport type { ContainerSchema, IFluidContainer } from \"@fluidframework/fluid-static\";\nimport { timeoutPromise } from \"@fluidframework/test-utils/internal\";\n\nimport {\n\tcreateAzureClient,\n\tcreateContainerFromPayload,\n\tgetContainerIdFromPayloadResponse,\n\tScopeType,\n} from \"./AzureClientFactory.js\";\nimport { SignalerTestDataObject } from \"./TestDataObject.js\";\nimport * as ephemeralSummaryTrees from \"./ephemeralSummaryTrees.js\";\nimport { configProvider, getTestMatrix } from \"./utils.js\";\n\ninterface UserIdAndName {\n\treadonly id: string;\n\treadonly name: string;\n}\n\nasync function createSignalListenerPromise<T>(\n\tsignaler: SignalerTestDataObject,\n\tsignalType: string,\n\texpectedPayload: T,\n\tname: string = \"Signal\",\n\ttimeoutMs: number = 10_000,\n): Promise<T> {\n\treturn timeoutPromise(\n\t\t(resolve, reject) => {\n\t\t\tsignaler.onSignal<T>(signalType, (clientId, local, receivedPayload) => {\n\t\t\t\ttry {\n\t\t\t\t\tassert.deepStrictEqual(\n\t\t\t\t\t\treceivedPayload,\n\t\t\t\t\t\texpectedPayload,\n\t\t\t\t\t\t\"Received payload does not match expected payload\",\n\t\t\t\t\t);\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn reject(error);\n\t\t\t\t}\n\t\t\t\tresolve(receivedPayload);\n\t\t\t});\n\t\t},\n\t\t{ durationMs: timeoutMs, errorMsg: `${name}: listener timeout` },\n\t);\n}\n\nconst testMatrix = getTestMatrix();\nfor (const testOpts of testMatrix) {\n\tdescribe(`Fluid Signals (${testOpts.variant})`, () => {\n\t\tconst connectedContainers: IFluidContainer[] = [];\n\t\tconst connectTimeoutMs = 10_000;\n\t\tconst isEphemeral: boolean = testOpts.options.isEphemeral;\n\t\tconst user1 = {\n\t\t\tid: \"test-user-id-1\",\n\t\t\tname: \"test-user-name-2\",\n\t\t} as const satisfies UserIdAndName;\n\t\tconst user2 = {\n\t\t\tid: \"test-user-id-1\",\n\t\t\tname: \"test-user-name-2\",\n\t\t} as const satisfies UserIdAndName;\n\t\tconst user3 = {\n\t\t\tid: \"test-user-id-1\",\n\t\t\tname: \"test-user-name-2\",\n\t\t} as const satisfies UserIdAndName;\n\n\t\tafterEach(async () => {\n\t\t\tfor (const container of connectedContainers) {\n\t\t\t\tcontainer.disconnect();\n\t\t\t\tcontainer.dispose();\n\t\t\t}\n\t\t\tconnectedContainers.splice(0, connectedContainers.length);\n\t\t});\n\n\t\tconst getOrCreateSignalerContainer = async (\n\t\t\tid: string | undefined,\n\t\t\tuser: UserIdAndName,\n\t\t\tconfig?: ReturnType<typeof configProvider>,\n\t\t\tscopes?: ScopeType[],\n\t\t): Promise<{\n\t\t\tcontainer: IFluidContainer;\n\t\t\tsignaler: SignalerTestDataObject;\n\t\t\tservices: AzureContainerServices;\n\t\t\tclient: AzureClient;\n\t\t\tcontainerId: string;\n\t\t}> => {\n\t\t\tconst client = createAzureClient(user.id, user.name, undefined, config, scopes);\n\t\t\tconst schema: ContainerSchema = {\n\t\t\t\tinitialObjects: {\n\t\t\t\t\tsignaler: SignalerTestDataObject,\n\t\t\t\t},\n\t\t\t};\n\t\t\tlet container: IFluidContainer;\n\t\t\tlet services: AzureContainerServices;\n\t\t\tlet containerId: string;\n\t\t\tif (id === undefined) {\n\t\t\t\tif (isEphemeral) {\n\t\t\t\t\tconst containerResponse = await createContainerFromPayload(\n\t\t\t\t\t\tephemeralSummaryTrees.sendAndRecieveSignals,\n\t\t\t\t\t\t\"test-user-id-1\",\n\t\t\t\t\t\t\"test-user-name-1\",\n\t\t\t\t\t);\n\t\t\t\t\tcontainerId = getContainerIdFromPayloadResponse(containerResponse);\n\t\t\t\t\t({ container, services } = await client.getContainer(containerId, schema, \"2\"));\n\t\t\t\t} else {\n\t\t\t\t\t({ container, services } = await client.createContainer(schema, \"2\"));\n\t\t\t\t\tcontainerId = await container.attach();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcontainerId = id;\n\t\t\t\t({ container, services } = await client.getContainer(containerId, schema, \"2\"));\n\t\t\t}\n\n\t\t\tif (container.connectionState !== ConnectionState.Connected) {\n\t\t\t\tawait timeoutPromise((resolve) => container.once(\"connected\", () => resolve()), {\n\t\t\t\t\tdurationMs: connectTimeoutMs,\n\t\t\t\t\terrorMsg: \"container connect() timeout\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tconnectedContainers.push(container);\n\n\t\t\tassert.strictEqual(typeof containerId, \"string\", \"Attach did not return a string ID\");\n\t\t\tassert.strictEqual(\n\t\t\t\tcontainer.attachState,\n\t\t\t\tAttachState.Attached,\n\t\t\t\t\"Container is not attached after attach is called\",\n\t\t\t);\n\n\t\t\tconst signaler = container.initialObjects.signaler as SignalerTestDataObject;\n\t\t\treturn {\n\t\t\t\tclient,\n\t\t\t\tcontainer,\n\t\t\t\tsignaler,\n\t\t\t\tservices,\n\t\t\t\tcontainerId,\n\t\t\t};\n\t\t};\n\n\t\t/**\n\t\t * Scenario: Client sends a signal and connected clients receive it.\n\t\t *\n\t\t * Expected behavior: While 2 clients are connected to a container,\n\t\t * a signal sent by 1 client should be received by both clients.\n\t\t */\n\t\tit(\"can send and receive signals\", async () => {\n\t\t\tconst { signaler, containerId } = await getOrCreateSignalerContainer(undefined, user1);\n\t\t\tconst { signaler: signaler2 } = await getOrCreateSignalerContainer(\n\t\t\t\tcontainerId,\n\t\t\t\tuser2,\n\t\t\t\tconfigProvider({\n\t\t\t\t\t\"Fluid.Container.ForceWriteConnection\": true,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tconst signalName = \"test-signal\";\n\t\t\tconst signalPayload = { test: \"payload\" };\n\n\t\t\tconst listenerPromises = [\n\t\t\t\tcreateSignalListenerPromise(\n\t\t\t\t\tsignaler2,\n\t\t\t\t\tsignalName,\n\t\t\t\t\tsignalPayload,\n\t\t\t\t\t\"Write client listening for write client signal\",\n\t\t\t\t),\n\t\t\t\tcreateSignalListenerPromise(\n\t\t\t\t\tsignaler,\n\t\t\t\t\tsignalName,\n\t\t\t\t\tsignalPayload,\n\t\t\t\t\t\"Write client listening for its own signal\",\n\t\t\t\t),\n\t\t\t];\n\n\t\t\tsignaler.submitSignal(signalName, signalPayload);\n\n\t\t\tawait assert.doesNotReject(\n\t\t\t\tPromise.all(listenerPromises),\n\t\t\t\t\"Listening clients should receive signals.\",\n\t\t\t);\n\t\t});\n\n\t\t/**\n\t\t * Scenario: Read and Write clients send signals and connected clients receive them.\n\t\t *\n\t\t * Expected behavior: While 2 clients are connected (1 writer, 2 readers) to a container,\n\t\t * a signal sent by any 1 client should be received by all 3 clients, regardless of read/write permissions.\n\t\t */\n\t\tit(\"can send and receive read-only client signals\", async function () {\n\t\t\tconst { signaler: writeSignaler, containerId } = await getOrCreateSignalerContainer(\n\t\t\t\tundefined,\n\t\t\t\tuser1,\n\t\t\t);\n\t\t\tconst { signaler: readSignaler } = await getOrCreateSignalerContainer(\n\t\t\t\tcontainerId,\n\t\t\t\tuser2,\n\t\t\t\tundefined,\n\t\t\t\t[ScopeType.DocRead],\n\t\t\t);\n\t\t\tconst { signaler: readSignaler2 } = await getOrCreateSignalerContainer(\n\t\t\t\tcontainerId,\n\t\t\t\tuser3,\n\t\t\t\tundefined,\n\t\t\t\t[ScopeType.DocRead],\n\t\t\t);\n\n\t\t\tconst signalName = \"test-signal\";\n\n\t\t\tconst signalPayload1 = { test: \"payload\" };\n\t\t\tconst listenerPromises1 = [\n\t\t\t\tcreateSignalListenerPromise(\n\t\t\t\t\twriteSignaler,\n\t\t\t\t\tsignalName,\n\t\t\t\t\tsignalPayload1,\n\t\t\t\t\t\"Write client listening for read client signal\",\n\t\t\t\t),\n\t\t\t\tcreateSignalListenerPromise(\n\t\t\t\t\treadSignaler2,\n\t\t\t\t\tsignalName,\n\t\t\t\t\tsignalPayload1,\n\t\t\t\t\t\"Read client 2 listening for read client signal\",\n\t\t\t\t),\n\t\t\t\tcreateSignalListenerPromise(\n\t\t\t\t\treadSignaler,\n\t\t\t\t\tsignalName,\n\t\t\t\t\tsignalPayload1,\n\t\t\t\t\t\"Read client listening for its own signal\",\n\t\t\t\t),\n\t\t\t];\n\t\t\treadSignaler.submitSignal(signalName, signalPayload1);\n\t\t\tawait assert.doesNotReject(\n\t\t\t\tPromise.all(listenerPromises1),\n\t\t\t\t\"Listening clients should receive signals from read clients.\",\n\t\t\t);\n\n\t\t\tconst signalPayload2 = { test: \"payload2\" };\n\t\t\tconst listenerPromises2 = [\n\t\t\t\tcreateSignalListenerPromise(\n\t\t\t\t\treadSignaler,\n\t\t\t\t\tsignalName,\n\t\t\t\t\tsignalPayload2,\n\t\t\t\t\t\"Read client listening for write client signal\",\n\t\t\t\t),\n\t\t\t\tcreateSignalListenerPromise(\n\t\t\t\t\treadSignaler2,\n\t\t\t\t\tsignalName,\n\t\t\t\t\tsignalPayload2,\n\t\t\t\t\t\"Read client 2 listening for write client signal\",\n\t\t\t\t),\n\t\t\t\tcreateSignalListenerPromise(\n\t\t\t\t\twriteSignaler,\n\t\t\t\t\tsignalName,\n\t\t\t\t\tsignalPayload2,\n\t\t\t\t\t\"Write client listening for its own signal\",\n\t\t\t\t),\n\t\t\t];\n\t\t\twriteSignaler.submitSignal(signalName, signalPayload2);\n\t\t\tawait assert.doesNotReject(\n\t\t\t\tPromise.all(listenerPromises2),\n\t\t\t\t\"Listening clients should receive signals from write clients.\",\n\t\t\t);\n\t\t});\n\t});\n}\n"]}
|
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.100.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.100.0",
|
|
37
|
+
"@fluid-internal/client-utils": "~2.100.0",
|
|
38
|
+
"@fluid-internal/mocha-test-setup": "~2.100.0",
|
|
39
|
+
"@fluid-private/test-version-utils": "~2.100.0",
|
|
40
|
+
"@fluidframework/aqueduct": "~2.100.0",
|
|
41
|
+
"@fluidframework/azure-client": "~2.100.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.100.0",
|
|
44
|
+
"@fluidframework/container-loader": "~2.100.0",
|
|
45
|
+
"@fluidframework/core-interfaces": "~2.100.0",
|
|
46
|
+
"@fluidframework/counter": "~2.100.0",
|
|
47
|
+
"@fluidframework/datastore-definitions": "~2.100.0",
|
|
48
|
+
"@fluidframework/fluid-static": "~2.100.0",
|
|
49
|
+
"@fluidframework/map": "~2.100.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.100.0",
|
|
52
|
+
"@fluidframework/presence": "~2.100.0",
|
|
53
|
+
"@fluidframework/runtime-definitions": "~2.100.0",
|
|
54
|
+
"@fluidframework/sequence": "~2.100.0",
|
|
55
|
+
"@fluidframework/telemetry-utils": "~2.100.0",
|
|
56
|
+
"@fluidframework/test-runtime-utils": "~2.100.0",
|
|
57
|
+
"@fluidframework/test-utils": "~2.100.0",
|
|
58
|
+
"@fluidframework/tree": "~2.100.0",
|
|
59
59
|
"cross-env": "^10.1.0",
|
|
60
60
|
"mocha": "^11.7.5",
|
|
61
61
|
"mocha-multi-reporters": "^1.5.1",
|
|
@@ -67,12 +67,12 @@
|
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"@biomejs/biome": "~2.4.5",
|
|
69
69
|
"@fluidframework/build-common": "^2.0.3",
|
|
70
|
-
"@fluidframework/build-tools": "^0.
|
|
71
|
-
"@fluidframework/driver-definitions": "~2.
|
|
70
|
+
"@fluidframework/build-tools": "^0.65.0",
|
|
71
|
+
"@fluidframework/driver-definitions": "~2.100.0",
|
|
72
72
|
"@fluidframework/eslint-config-fluid": "^9.0.0",
|
|
73
73
|
"@types/mocha": "^10.0.10",
|
|
74
74
|
"@types/nock": "^9.3.0",
|
|
75
|
-
"@types/node": "~
|
|
75
|
+
"@types/node": "~22.19.17",
|
|
76
76
|
"@types/sinon": "^17.0.3",
|
|
77
77
|
"c8": "^10.1.3",
|
|
78
78
|
"eslint": "~9.39.1",
|
|
@@ -14,19 +14,18 @@ import {
|
|
|
14
14
|
} from "@fluidframework/azure-client";
|
|
15
15
|
import { AttachState } from "@fluidframework/container-definitions";
|
|
16
16
|
import { ConnectionState } from "@fluidframework/container-loader";
|
|
17
|
-
import type { ITelemetryBaseLogger } from "@fluidframework/core-interfaces";
|
|
18
|
-
import { LogLevel } from "@fluidframework/core-interfaces";
|
|
17
|
+
import type { ITelemetryBaseLogger, LogLevel } from "@fluidframework/core-interfaces";
|
|
19
18
|
import type { ScopeType } from "@fluidframework/driver-definitions/legacy";
|
|
20
19
|
import type { ContainerSchema, IFluidContainer } from "@fluidframework/fluid-static";
|
|
20
|
+
import { getPresence } from "@fluidframework/fluid-static";
|
|
21
21
|
import {
|
|
22
|
-
getPresence,
|
|
23
22
|
type Attendee,
|
|
24
23
|
type Presence,
|
|
25
24
|
StateFactory,
|
|
26
25
|
type LatestRaw,
|
|
27
26
|
type LatestMapRaw,
|
|
28
27
|
type StatesWorkspace,
|
|
29
|
-
} from "@fluidframework/presence
|
|
28
|
+
} from "@fluidframework/presence";
|
|
30
29
|
import { InsecureTokenProvider } from "@fluidframework/test-runtime-utils/internal";
|
|
31
30
|
import { timeoutPromise } from "@fluidframework/test-utils/internal";
|
|
32
31
|
|
|
@@ -74,7 +73,10 @@ function telemetryEventInterestLevel(eventName: string): "none" | "basic" | "det
|
|
|
74
73
|
return "none";
|
|
75
74
|
}
|
|
76
75
|
|
|
77
|
-
function selectiveVerboseLog(
|
|
76
|
+
function selectiveVerboseLog(
|
|
77
|
+
event: ITelemetryBaseEvent,
|
|
78
|
+
logLevel: LogLevel | undefined,
|
|
79
|
+
): void {
|
|
78
80
|
const interest = telemetryEventInterestLevel(event.eventName);
|
|
79
81
|
if (interest === "none") {
|
|
80
82
|
return;
|
|
@@ -86,7 +88,7 @@ function selectiveVerboseLog(event: ITelemetryBaseEvent, logLevel?: LogLevel): v
|
|
|
86
88
|
if (interest === "details") {
|
|
87
89
|
content.details = event.details;
|
|
88
90
|
}
|
|
89
|
-
log(`[${logLevel ??
|
|
91
|
+
log(`[${logLevel ?? "unspecified"}]`, content);
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
/**
|
|
@@ -6,7 +6,7 @@
|
|
|
6
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
|
-
import type { AttendeeId } from "@fluidframework/presence
|
|
9
|
+
import type { AttendeeId } from "@fluidframework/presence";
|
|
10
10
|
|
|
11
11
|
export interface UserIdAndName {
|
|
12
12
|
id: string;
|
|
@@ -7,7 +7,7 @@ import { fork } from "node:child_process";
|
|
|
7
7
|
import type { ChildProcess as AnyChildProcess } from "node:child_process";
|
|
8
8
|
|
|
9
9
|
import { ScopeType } from "@fluidframework/driver-definitions/legacy";
|
|
10
|
-
import type { AttendeeId } from "@fluidframework/presence
|
|
10
|
+
import type { AttendeeId } from "@fluidframework/presence";
|
|
11
11
|
import { timeoutAwait, timeoutPromise } from "@fluidframework/test-utils/internal";
|
|
12
12
|
|
|
13
13
|
import type {
|
|
@@ -7,7 +7,7 @@ import { strict as assert } from "node:assert";
|
|
|
7
7
|
import type { ChildProcess } from "node:child_process";
|
|
8
8
|
import inspector from "node:inspector";
|
|
9
9
|
|
|
10
|
-
import type { AttendeeId } from "@fluidframework/presence
|
|
10
|
+
import type { AttendeeId } from "@fluidframework/presence";
|
|
11
11
|
import { timeoutAwait, timeoutPromise } from "@fluidframework/test-utils/internal";
|
|
12
12
|
|
|
13
13
|
import type { MessageFromChild } from "./messageTypes.js";
|
package/src/test/signals.spec.ts
CHANGED
|
@@ -147,7 +147,7 @@ for (const testOpts of testMatrix) {
|
|
|
147
147
|
* Scenario: Client sends a signal and connected clients receive it.
|
|
148
148
|
*
|
|
149
149
|
* Expected behavior: While 2 clients are connected to a container,
|
|
150
|
-
* a signal sent by 1 client should be
|
|
150
|
+
* a signal sent by 1 client should be received by both clients.
|
|
151
151
|
*/
|
|
152
152
|
it("can send and receive signals", async () => {
|
|
153
153
|
const { signaler, containerId } = await getOrCreateSignalerContainer(undefined, user1);
|
|
@@ -189,7 +189,7 @@ for (const testOpts of testMatrix) {
|
|
|
189
189
|
* Scenario: Read and Write clients send signals and connected clients receive them.
|
|
190
190
|
*
|
|
191
191
|
* Expected behavior: While 2 clients are connected (1 writer, 2 readers) to a container,
|
|
192
|
-
* a signal sent by any 1 client should be
|
|
192
|
+
* a signal sent by any 1 client should be received by all 3 clients, regardless of read/write permissions.
|
|
193
193
|
*/
|
|
194
194
|
it("can send and receive read-only client signals", async function () {
|
|
195
195
|
const { signaler: writeSignaler, containerId } = await getOrCreateSignalerContainer(
|