@fluidframework/azure-end-to-end-tests 2.21.0 → 2.23.0-323641
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/README.md +1 -0
- package/lib/test/audience.spec.js +1 -1
- package/lib/test/audience.spec.js.map +1 -1
- package/lib/test/containerCreate.spec.js +4 -4
- package/lib/test/containerCreate.spec.js.map +1 -1
- package/lib/test/ddsTests.spec.js +1 -1
- package/lib/test/ddsTests.spec.js.map +1 -1
- package/lib/test/multiprocess/childClient.js +158 -0
- package/lib/test/multiprocess/childClient.js.map +1 -0
- package/lib/test/multiprocess/messageTypes.js +6 -0
- package/lib/test/multiprocess/messageTypes.js.map +1 -0
- package/lib/test/multiprocess/presenceTest.spec.js +146 -0
- package/lib/test/multiprocess/presenceTest.spec.js.map +1 -0
- package/lib/test/signals.spec.js +2 -2
- package/lib/test/signals.spec.js.map +1 -1
- package/lib/test/tree.spec.js +1 -1
- package/lib/test/tree.spec.js.map +1 -1
- package/lib/test/viewContainerVersion.spec.js +1 -1
- package/lib/test/viewContainerVersion.spec.js.map +1 -1
- package/package.json +24 -23
- package/src/test/multiprocess/childClient.ts +219 -0
- package/src/test/multiprocess/messageTypes.ts +50 -0
- package/src/test/multiprocess/presenceTest.spec.ts +173 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"viewContainerVersion.spec.js","sourceRoot":"","sources":["../../src/test/viewContainerVersion.spec.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,IAAI,MAAM,EAAE,MAAM,aAAa,CAAC;AAG/C,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAEnE,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AAGrE,OAAO,EACN,iBAAiB,EACjB,0BAA0B,EAC1B,iCAAiC,GACjC,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,qBAAqB,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,UAAU,GAAG,aAAa,EAAE,CAAC;AACnC,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;IACnC,QAAQ,CAAC,mCAAmC,QAAQ,CAAC,OAAO,GAAG,EAAE,GAAG,EAAE;QACrE,MAAM,WAAW,GAAY,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC;QAC1D,MAAM,gBAAgB,GAAG,KAAM,CAAC;QAChC,IAAI,MAAmB,CAAC;QACxB,MAAM,MAAM,GAAG;YACd,cAAc,EAAE;gBACf,IAAI,EAAE,SAAS;aACf;SACyB,CAAC;QAE5B,UAAU,CAAC,mBAAmB,EAAE,GAAG,EAAE;YACpC,MAAM,GAAG,iBAAiB,EAAE,CAAC;QAC9B,CAAC,CAAC,CAAC;QAEH,UAAU,CAAC,iBAAiB,EAAE,KAAK;YAClC,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,KAAK,OAAO,EAAE,CAAC;gBAC1C,IAAI,CAAC,IAAI,EAAE,CAAC;YACb,CAAC;QACF,CAAC,CAAC,CAAC;QAEH,UAAU,CAAC,kBAAkB,EAAE,KAAK;YACnC,IAAI,WAAW,EAAE,CAAC;gBACjB,IAAI,CAAC,IAAI,EAAE,CAAC;YACb,CAAC;QACF,CAAC,CAAC,CAAC;QAEH;;;;;WAKG;QACH,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;YACrD,IAAI,WAAmB,CAAC;YACxB,IAAI,SAA0B,CAAC;YAC/B,IAAI,WAAW,EAAE,CAAC;gBACjB,MAAM,iBAAiB,GAA8B,MAAM,0BAA0B,CACpF,qBAAqB,CAAC,4BAA4B,EAClD,gBAAgB,EAChB,kBAAkB,CAClB,CAAC;gBACF,WAAW,GAAG,iCAAiC,CAAC,iBAAiB,CAAC,CAAC;gBACnE,CAAC,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;YACvE,CAAC;iBAAM,CAAC;gBACP,CAAC,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;gBAC5D,WAAW,GAAG,MAAM,SAAS,CAAC,MAAM,EAAE,CAAC;YACxC,CAAC;YAED,IAAI,SAAS,CAAC,eAAe,KAAK,eAAe,CAAC,SAAS,EAAE,CAAC;gBAC7D,MAAM,cAAc,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,EAAE;oBAC/E,UAAU,EAAE,gBAAgB;oBAC5B,QAAQ,EAAE,6BAA6B;iBACvC,CAAC,CAAC;YACJ,CAAC;YACD,MAAM,SAAS,GAAG,MAAM,CAAC,oBAAoB,CAAC,WAAW,CAAC,CAAC;YAC3D,MAAM,MAAM,CAAC,aAAa,CACzB,SAAS,EACT,GAAG,EAAE,CAAC,IAAI,EACV,yCAAyC,CACzC,CAAC;YAEF,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC;YACjC,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,4CAA4C,CAAC,CAAC;QACtF,CAAC,CAAC,CAAC;QAEH;;;;WAIG;QACH,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;YACpE,MAAM,SAAS,GAAG,MAAM,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC;YACvD,MAAM,OAAO,GAAG,CAAC,KAAY,EAAW,EAAE;gBACzC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,EAAE,iCAAiC,CAAC,CAAC;gBACnF,MAAM,CAAC,WAAW,CACjB,KAAK,CAAC,OAAO,EACb,+DAA+D,EAC/D,qBAAqB,KAAK,CAAC,OAAO,EAAE,CACpC,CAAC;gBACF,OAAO,IAAI,CAAC;YACb,CAAC,CAAC;YACF,MAAM,MAAM,CAAC,OAAO,CACnB,SAAS,EACT,OAAO,EACP,kDAAkD,CAClD,CAAC;QACH,CAAC,CAAC,CAAC;QAEH;;;;;WAKG;QACH,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;YACxD,IAAI,WAAmB,CAAC;YACxB,IAAI,SAA0B,CAAC;YAC/B,IAAI,WAAW,EAAE,CAAC;gBACjB,MAAM,iBAAiB,GAA8B,MAAM,0BAA0B,CACpF,qBAAqB,CAAC,wBAAwB,EAC9C,gBAAgB,EAChB,kBAAkB,CAClB,CAAC;gBACF,WAAW,GAAG,iCAAiC,CAAC,iBAAiB,CAAC,CAAC;YACpE,CAAC;iBAAM,CAAC;gBACP,CAAC,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;gBAC5D,WAAW,GAAG,MAAM,SAAS,CAAC,MAAM,EAAE,CAAC;gBAEvC,IAAI,SAAS,CAAC,eAAe,KAAK,eAAe,CAAC,SAAS,EAAE,CAAC;oBAC7D,MAAM,cAAc,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,EAAE;wBAC/E,UAAU,EAAE,gBAAgB;wBAC5B,QAAQ,EAAE,6BAA6B;qBACvC,CAAC,CAAC;gBACJ,CAAC;YACF,CAAC;YAED,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,WAAW,CAAC,CAAC;YAChE,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,sCAAsC,CAAC,CAAC;YAClF,MAAM,2BAA2B,GAAG,MAAM,CAAC,oBAAoB,CAC9D,WAAW,EACX,MAAM,EACN,QAAQ,CAAC,CAAC,CAAC,EACX,GAAG,CACH,CAAC;YACF,MAAM,MAAM,CAAC,aAAa,CAAC,2BAA2B,CAAC,CAAC;YACxD,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,GAAG,MAAM,2BAA2B,CAAC;YACvE,MAAM,CAAC,cAAc,CAAC,aAAa,CAAC,cAAc,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACrE,CAAC,CAAC,CAAC;QAEH;;;;WAIG;QACH,EAAE,CAAC,uDAAuD,EAAE,KAAK,IAAI,EAAE;YACtE,IAAI,WAAmB,CAAC;YACxB,IAAI,SAA0B,CAAC;YAC/B,IAAI,aAAa,GAAuB,gBAAgB,CAAC;YACzD,IAAI,IAAe,CAAC;YACpB,MAAM,OAAO,GAAG,SAAS,CAAC;YAC1B,MAAM,aAAa,GAAG,gBAAgB,CAAC;YACvC,IAAI,WAAW,EAAE,CAAC;gBACjB,MAAM,iBAAiB,GAA8B,MAAM,0BAA0B,CACpF,qBAAqB,CAAC,iCAAiC,EACvD,gBAAgB,EAChB,kBAAkB,CAClB,CAAC;gBACF,WAAW,GAAG,iCAAiC,CAAC,iBAAiB,CAAC,CAAC;gBACnE,CAAC,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;gBACtE,IAAI,GAAG,SAAS,CAAC,cAAc,CAAC,IAAiB,CAAC;YACnD,CAAC;iBAAM,CAAC;gBACP,CAAC,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;gBAE5D,IAAI,GAAG,SAAS,CAAC,cAAc,CAAC,IAAiB,CAAC;gBAClD,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;gBACjC,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBAElC,WAAW,GAAG,MAAM,SAAS,CAAC,MAAM,EAAE,CAAC;YACxC,CAAC;YAED,IAAI,SAAS,CAAC,eAAe,KAAK,eAAe,CAAC,SAAS,EAAE,CAAC;gBAC7D,MAAM,cAAc,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,EAAE;oBAC/E,UAAU,EAAE,gBAAgB;oBAC5B,QAAQ,EAAE,6BAA6B;iBACvC,CAAC,CAAC;YACJ,CAAC;YACD,2FAA2F;YAC3F,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;YAEtC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,WAAW,CAAC,CAAC;YAChE,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,sCAAsC,CAAC,CAAC;YAClF,0GAA0G;YAC1G,MAAM,2BAA2B,GAAG,MAAM,CAAC,oBAAoB,CAC9D,WAAW,EACX,MAAM,EACN,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,EAC7B,GAAG,CACH,CAAC;YACF,MAAM,MAAM,CAAC,aAAa,CAAC,2BAA2B,CAAC,CAAC;YACxD,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,GAAG,MAAM,2BAA2B,CAAC;YACvE,MAAM,CAAC,WAAW,CAAC,aAAa,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC,CAAC;QACnF,CAAC,CAAC,CAAC;QAEH;;;;WAIG;QACH,EAAE,CAAC,mCAAmC,EAAE,KAAK,IAAI,EAAE;YAClD,MAAM,SAAS,GAAG,MAAM,CAAC,oBAAoB,CAC5C,oBAAoB,EACpB,MAAM,EACN;gBACC,EAAE,EAAE,UAAU;aACd,EACD,GAAG,CACH,CAAC;YACF,MAAM,OAAO,GAAG,CAAC,KAAY,EAAW,EAAE;gBACzC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,EAAE,iCAAiC,CAAC,CAAC;gBACnF,MAAM,CAAC,WAAW,CACjB,KAAK,CAAC,OAAO,EACb,+DAA+D,EAC/D,qBAAqB,KAAK,CAAC,OAAO,EAAE,CACpC,CAAC;gBACF,OAAO,IAAI,CAAC;YACb,CAAC,CAAC;YAEF,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,8CAA8C,CAAC,CAAC;QAC1F,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC","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 { AzureClient } from \"@fluidframework/azure-client\";\nimport { ConnectionState } from \"@fluidframework/container-loader\";\nimport { ContainerSchema, type IFluidContainer } from \"@fluidframework/fluid-static\";\nimport { SharedMap } from \"@fluidframework/map/internal\";\nimport { timeoutPromise } from \"@fluidframework/test-utils/internal\";\nimport { AxiosResponse } from \"axios\";\n\nimport {\n\tcreateAzureClient,\n\tcreateContainerFromPayload,\n\tgetContainerIdFromPayloadResponse,\n} from \"./AzureClientFactory.js\";\nimport * as ephemeralSummaryTrees from \"./ephemeralSummaryTrees.js\";\nimport { getTestMatrix } from \"./utils.js\";\n\nconst testMatrix = getTestMatrix();\nfor (const testOpts of testMatrix) {\n\tdescribe(`viewContainerVersion scenarios (${testOpts.variant})`, () => {\n\t\tconst isEphemeral: boolean = testOpts.options.isEphemeral;\n\t\tconst connectTimeoutMs = 10_000;\n\t\tlet client: AzureClient;\n\t\tconst schema = {\n\t\t\tinitialObjects: {\n\t\t\t\tmap1: SharedMap,\n\t\t\t},\n\t\t} satisfies ContainerSchema;\n\n\t\tbeforeEach(\"createAzureClient\", () => {\n\t\t\tclient = createAzureClient();\n\t\t});\n\n\t\tbeforeEach(\"skipForNonAzure\", async function () {\n\t\t\tif (process.env.FLUID_CLIENT !== \"azure\") {\n\t\t\t\tthis.skip();\n\t\t\t}\n\t\t});\n\n\t\tbeforeEach(\"skipForEphemeral\", async function () {\n\t\t\tif (isEphemeral) {\n\t\t\t\tthis.skip();\n\t\t\t}\n\t\t});\n\n\t\t/**\n\t\t * Scenario: test if Azure Client can provide versions of the container.\n\t\t *\n\t\t * Expected behavior: an error should not be thrown nor should a rejected promise\n\t\t * be returned. Upon creation, we should recieve back 1 version of the container.\n\t\t */\n\t\tit(\"can get versions of current document\", async () => {\n\t\t\tlet containerId: string;\n\t\t\tlet container: IFluidContainer;\n\t\t\tif (isEphemeral) {\n\t\t\t\tconst containerResponse: AxiosResponse | undefined = await createContainerFromPayload(\n\t\t\t\t\tephemeralSummaryTrees.getVersionsOfCurrentDocument,\n\t\t\t\t\t\"test-user-id-1\",\n\t\t\t\t\t\"test-user-name-1\",\n\t\t\t\t);\n\t\t\t\tcontainerId = getContainerIdFromPayloadResponse(containerResponse);\n\t\t\t\t({ container } = await client.getContainer(containerId, schema, \"2\"));\n\t\t\t} else {\n\t\t\t\t({ container } = await client.createContainer(schema, \"2\"));\n\t\t\t\tcontainerId = await container.attach();\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\tconst resources = client.getContainerVersions(containerId);\n\t\t\tawait assert.doesNotReject(\n\t\t\t\tresources,\n\t\t\t\t() => true,\n\t\t\t\t\"could not get versions of the container\",\n\t\t\t);\n\n\t\t\tconst versions = await resources;\n\t\t\tassert.strictEqual(versions.length, 1, \"Container should have exactly one version.\");\n\t\t});\n\n\t\t/**\n\t\t * Scenario: test if Azure Client can handle bad document ID when versions are requested.\n\t\t *\n\t\t * Expected behavior: Client should throw an error.\n\t\t */\n\t\tit(\"can handle bad document id when requesting versions\", async () => {\n\t\t\tconst resources = client.getContainerVersions(\"badid\");\n\t\t\tconst errorFn = (error: Error): boolean => {\n\t\t\t\tassert.notStrictEqual(error.message, undefined, \"Azure Client error is undefined\");\n\t\t\t\tassert.strictEqual(\n\t\t\t\t\terror.message,\n\t\t\t\t\t\"R11s fetch error: Document is deleted and cannot be accessed.\",\n\t\t\t\t\t`Unexpected error: ${error.message}`,\n\t\t\t\t);\n\t\t\t\treturn true;\n\t\t\t};\n\t\t\tawait assert.rejects(\n\t\t\t\tresources,\n\t\t\t\terrorFn,\n\t\t\t\t\"We should not be able to get container versions.\",\n\t\t\t);\n\t\t});\n\n\t\t/**\n\t\t * Scenario: test if Azure Client can retrieve a specified version of a container.\n\t\t *\n\t\t * Expected behavior: an error should not be thrown nor should a rejected promise\n\t\t * be returned.\n\t\t */\n\t\tit(\"can view container version successfully\", async () => {\n\t\t\tlet containerId: string;\n\t\t\tlet container: IFluidContainer;\n\t\t\tif (isEphemeral) {\n\t\t\t\tconst containerResponse: AxiosResponse | undefined = await createContainerFromPayload(\n\t\t\t\t\tephemeralSummaryTrees.copyDocumentSuccessfully,\n\t\t\t\t\t\"test-user-id-1\",\n\t\t\t\t\t\"test-user-name-1\",\n\t\t\t\t);\n\t\t\t\tcontainerId = getContainerIdFromPayloadResponse(containerResponse);\n\t\t\t} else {\n\t\t\t\t({ container } = await client.createContainer(schema, \"2\"));\n\t\t\t\tcontainerId = await container.attach();\n\n\t\t\t\tif (container.connectionState !== ConnectionState.Connected) {\n\t\t\t\t\tawait timeoutPromise((resolve) => container.once(\"connected\", () => resolve()), {\n\t\t\t\t\t\tdurationMs: connectTimeoutMs,\n\t\t\t\t\t\terrorMsg: \"container connect() timeout\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst versions = await client.getContainerVersions(containerId);\n\t\t\tassert.notStrictEqual(versions.length, 0, \"There should be at least one version\");\n\t\t\tconst viewContainerVersionAttempt = client.viewContainerVersion(\n\t\t\t\tcontainerId,\n\t\t\t\tschema,\n\t\t\t\tversions[0],\n\t\t\t\t\"2\",\n\t\t\t);\n\t\t\tawait assert.doesNotReject(viewContainerVersionAttempt);\n\t\t\tconst { container: containerView } = await viewContainerVersionAttempt;\n\t\t\tassert.notStrictEqual(containerView.initialObjects.map1, undefined);\n\t\t});\n\n\t\t/**\n\t\t * Scenario: test if Azure Client observes correct DDS values when viewing version.\n\t\t *\n\t\t * Expected behavior: DDS values should reflect their values from the version.\n\t\t */\n\t\tit(\"has correct DDS values when viewing container version\", async () => {\n\t\t\tlet containerId: string;\n\t\t\tlet container: IFluidContainer;\n\t\t\tlet valueAtCreate: string | undefined = \"expected-value\";\n\t\t\tlet map1: SharedMap;\n\t\t\tconst testKey = \"new-key\";\n\t\t\tconst expectedValue = \"expected-value\";\n\t\t\tif (isEphemeral) {\n\t\t\t\tconst containerResponse: AxiosResponse | undefined = await createContainerFromPayload(\n\t\t\t\t\tephemeralSummaryTrees.copyDDSValuesWhenCopyingContainer,\n\t\t\t\t\t\"test-user-id-1\",\n\t\t\t\t\t\"test-user-name-1\",\n\t\t\t\t);\n\t\t\t\tcontainerId = getContainerIdFromPayloadResponse(containerResponse);\n\t\t\t\t({ container } = await client.getContainer(containerId, schema, \"2\"));\n\t\t\t\tmap1 = container.initialObjects.map1 as SharedMap;\n\t\t\t} else {\n\t\t\t\t({ container } = await client.createContainer(schema, \"2\"));\n\n\t\t\t\tmap1 = container.initialObjects.map1 as SharedMap;\n\t\t\t\tmap1.set(testKey, expectedValue);\n\t\t\t\tvalueAtCreate = map1.get(testKey);\n\n\t\t\t\tcontainerId = await container.attach();\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\t// Set a new value to the map - we do not expect to see this when loading the older version\n\t\t\tmap1.set(testKey, \"some-newer-value\");\n\n\t\t\tconst versions = await client.getContainerVersions(containerId);\n\t\t\tassert.notStrictEqual(versions.length, 0, \"There should be at least one version\");\n\t\t\t// Get the oldest version, which we expect is the version from attach and should still have the old value.\n\t\t\tconst viewContainerVersionAttempt = client.viewContainerVersion(\n\t\t\t\tcontainerId,\n\t\t\t\tschema,\n\t\t\t\tversions[versions.length - 1],\n\t\t\t\t\"2\",\n\t\t\t);\n\t\t\tawait assert.doesNotReject(viewContainerVersionAttempt);\n\t\t\tconst { container: containerView } = await viewContainerVersionAttempt;\n\t\t\tassert.strictEqual(containerView.initialObjects.map1.get(testKey), valueAtCreate);\n\t\t});\n\n\t\t/**\n\t\t * Scenario: test if Azure Client can handle non-existing container when trying to view a version\n\t\t *\n\t\t * Expected behavior: client should throw an error.\n\t\t */\n\t\tit(\"can handle non-existing container\", async () => {\n\t\t\tconst resources = client.viewContainerVersion(\n\t\t\t\t\"badidonviewversion\",\n\t\t\t\tschema,\n\t\t\t\t{\n\t\t\t\t\tid: \"whatever\",\n\t\t\t\t},\n\t\t\t\t\"2\",\n\t\t\t);\n\t\t\tconst errorFn = (error: Error): boolean => {\n\t\t\t\tassert.notStrictEqual(error.message, undefined, \"Azure Client error is undefined\");\n\t\t\t\tassert.strictEqual(\n\t\t\t\t\terror.message,\n\t\t\t\t\t\"R11s fetch error: Document is deleted and cannot be accessed.\",\n\t\t\t\t\t`Unexpected error: ${error.message}`,\n\t\t\t\t);\n\t\t\t\treturn true;\n\t\t\t};\n\n\t\t\tawait assert.rejects(resources, errorFn, \"We should not be able to view the container.\");\n\t\t});\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"viewContainerVersion.spec.js","sourceRoot":"","sources":["../../src/test/viewContainerVersion.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 { AzureClient } from \"@fluidframework/azure-client\";\nimport { ConnectionState } from \"@fluidframework/container-loader\";\nimport { ContainerSchema, type IFluidContainer } from \"@fluidframework/fluid-static\";\nimport { SharedMap } from \"@fluidframework/map/internal\";\nimport { timeoutPromise } from \"@fluidframework/test-utils/internal\";\nimport { AxiosResponse } from \"axios\";\n\nimport {\n\tcreateAzureClient,\n\tcreateContainerFromPayload,\n\tgetContainerIdFromPayloadResponse,\n} from \"./AzureClientFactory.js\";\nimport * as ephemeralSummaryTrees from \"./ephemeralSummaryTrees.js\";\nimport { getTestMatrix } from \"./utils.js\";\n\nconst testMatrix = getTestMatrix();\nfor (const testOpts of testMatrix) {\n\tdescribe(`viewContainerVersion scenarios (${testOpts.variant})`, () => {\n\t\tconst isEphemeral: boolean = testOpts.options.isEphemeral;\n\t\tconst connectTimeoutMs = 10_000;\n\t\tlet client: AzureClient;\n\t\tconst schema = {\n\t\t\tinitialObjects: {\n\t\t\t\tmap1: SharedMap,\n\t\t\t},\n\t\t} satisfies ContainerSchema;\n\n\t\tbeforeEach(\"createAzureClient\", () => {\n\t\t\tclient = createAzureClient();\n\t\t});\n\n\t\tbeforeEach(\"skipForNonAzure\", async function () {\n\t\t\tif (process.env.FLUID_CLIENT !== \"azure\") {\n\t\t\t\tthis.skip();\n\t\t\t}\n\t\t});\n\n\t\tbeforeEach(\"skipForEphemeral\", async function () {\n\t\t\tif (isEphemeral) {\n\t\t\t\tthis.skip();\n\t\t\t}\n\t\t});\n\n\t\t/**\n\t\t * Scenario: test if Azure Client can provide versions of the container.\n\t\t *\n\t\t * Expected behavior: an error should not be thrown nor should a rejected promise\n\t\t * be returned. Upon creation, we should recieve back 1 version of the container.\n\t\t */\n\t\tit(\"can get versions of current document\", async () => {\n\t\t\tlet containerId: string;\n\t\t\tlet container: IFluidContainer;\n\t\t\tif (isEphemeral) {\n\t\t\t\tconst containerResponse: AxiosResponse | undefined = await createContainerFromPayload(\n\t\t\t\t\tephemeralSummaryTrees.getVersionsOfCurrentDocument,\n\t\t\t\t\t\"test-user-id-1\",\n\t\t\t\t\t\"test-user-name-1\",\n\t\t\t\t);\n\t\t\t\tcontainerId = getContainerIdFromPayloadResponse(containerResponse);\n\t\t\t\t({ container } = await client.getContainer(containerId, schema, \"2\"));\n\t\t\t} else {\n\t\t\t\t({ container } = await client.createContainer(schema, \"2\"));\n\t\t\t\tcontainerId = await container.attach();\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\tconst resources = client.getContainerVersions(containerId);\n\t\t\tawait assert.doesNotReject(\n\t\t\t\tresources,\n\t\t\t\t() => true,\n\t\t\t\t\"could not get versions of the container\",\n\t\t\t);\n\n\t\t\tconst versions = await resources;\n\t\t\tassert.strictEqual(versions.length, 1, \"Container should have exactly one version.\");\n\t\t});\n\n\t\t/**\n\t\t * Scenario: test if Azure Client can handle bad document ID when versions are requested.\n\t\t *\n\t\t * Expected behavior: Client should throw an error.\n\t\t */\n\t\tit(\"can handle bad document id when requesting versions\", async () => {\n\t\t\tconst resources = client.getContainerVersions(\"badid\");\n\t\t\tconst errorFn = (error: Error): boolean => {\n\t\t\t\tassert.notStrictEqual(error.message, undefined, \"Azure Client error is undefined\");\n\t\t\t\tassert.strictEqual(\n\t\t\t\t\terror.message,\n\t\t\t\t\t\"R11s fetch error: Document is deleted and cannot be accessed.\",\n\t\t\t\t\t`Unexpected error: ${error.message}`,\n\t\t\t\t);\n\t\t\t\treturn true;\n\t\t\t};\n\t\t\tawait assert.rejects(\n\t\t\t\tresources,\n\t\t\t\terrorFn,\n\t\t\t\t\"We should not be able to get container versions.\",\n\t\t\t);\n\t\t});\n\n\t\t/**\n\t\t * Scenario: test if Azure Client can retrieve a specified version of a container.\n\t\t *\n\t\t * Expected behavior: an error should not be thrown nor should a rejected promise\n\t\t * be returned.\n\t\t */\n\t\tit(\"can view container version successfully\", async () => {\n\t\t\tlet containerId: string;\n\t\t\tlet container: IFluidContainer;\n\t\t\tif (isEphemeral) {\n\t\t\t\tconst containerResponse: AxiosResponse | undefined = await createContainerFromPayload(\n\t\t\t\t\tephemeralSummaryTrees.copyDocumentSuccessfully,\n\t\t\t\t\t\"test-user-id-1\",\n\t\t\t\t\t\"test-user-name-1\",\n\t\t\t\t);\n\t\t\t\tcontainerId = getContainerIdFromPayloadResponse(containerResponse);\n\t\t\t} else {\n\t\t\t\t({ container } = await client.createContainer(schema, \"2\"));\n\t\t\t\tcontainerId = await container.attach();\n\n\t\t\t\tif (container.connectionState !== ConnectionState.Connected) {\n\t\t\t\t\tawait timeoutPromise((resolve) => container.once(\"connected\", () => resolve()), {\n\t\t\t\t\t\tdurationMs: connectTimeoutMs,\n\t\t\t\t\t\terrorMsg: \"container connect() timeout\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst versions = await client.getContainerVersions(containerId);\n\t\t\tassert.notStrictEqual(versions.length, 0, \"There should be at least one version\");\n\t\t\tconst viewContainerVersionAttempt = client.viewContainerVersion(\n\t\t\t\tcontainerId,\n\t\t\t\tschema,\n\t\t\t\tversions[0],\n\t\t\t\t\"2\",\n\t\t\t);\n\t\t\tawait assert.doesNotReject(viewContainerVersionAttempt);\n\t\t\tconst { container: containerView } = await viewContainerVersionAttempt;\n\t\t\tassert.notStrictEqual(containerView.initialObjects.map1, undefined);\n\t\t});\n\n\t\t/**\n\t\t * Scenario: test if Azure Client observes correct DDS values when viewing version.\n\t\t *\n\t\t * Expected behavior: DDS values should reflect their values from the version.\n\t\t */\n\t\tit(\"has correct DDS values when viewing container version\", async () => {\n\t\t\tlet containerId: string;\n\t\t\tlet container: IFluidContainer;\n\t\t\tlet valueAtCreate: string | undefined = \"expected-value\";\n\t\t\tlet map1: SharedMap;\n\t\t\tconst testKey = \"new-key\";\n\t\t\tconst expectedValue = \"expected-value\";\n\t\t\tif (isEphemeral) {\n\t\t\t\tconst containerResponse: AxiosResponse | undefined = await createContainerFromPayload(\n\t\t\t\t\tephemeralSummaryTrees.copyDDSValuesWhenCopyingContainer,\n\t\t\t\t\t\"test-user-id-1\",\n\t\t\t\t\t\"test-user-name-1\",\n\t\t\t\t);\n\t\t\t\tcontainerId = getContainerIdFromPayloadResponse(containerResponse);\n\t\t\t\t({ container } = await client.getContainer(containerId, schema, \"2\"));\n\t\t\t\tmap1 = container.initialObjects.map1 as SharedMap;\n\t\t\t} else {\n\t\t\t\t({ container } = await client.createContainer(schema, \"2\"));\n\n\t\t\t\tmap1 = container.initialObjects.map1 as SharedMap;\n\t\t\t\tmap1.set(testKey, expectedValue);\n\t\t\t\tvalueAtCreate = map1.get(testKey);\n\n\t\t\t\tcontainerId = await container.attach();\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\t// Set a new value to the map - we do not expect to see this when loading the older version\n\t\t\tmap1.set(testKey, \"some-newer-value\");\n\n\t\t\tconst versions = await client.getContainerVersions(containerId);\n\t\t\tassert.notStrictEqual(versions.length, 0, \"There should be at least one version\");\n\t\t\t// Get the oldest version, which we expect is the version from attach and should still have the old value.\n\t\t\tconst viewContainerVersionAttempt = client.viewContainerVersion(\n\t\t\t\tcontainerId,\n\t\t\t\tschema,\n\t\t\t\tversions[versions.length - 1],\n\t\t\t\t\"2\",\n\t\t\t);\n\t\t\tawait assert.doesNotReject(viewContainerVersionAttempt);\n\t\t\tconst { container: containerView } = await viewContainerVersionAttempt;\n\t\t\tassert.strictEqual(containerView.initialObjects.map1.get(testKey), valueAtCreate);\n\t\t});\n\n\t\t/**\n\t\t * Scenario: test if Azure Client can handle non-existing container when trying to view a version\n\t\t *\n\t\t * Expected behavior: client should throw an error.\n\t\t */\n\t\tit(\"can handle non-existing container\", async () => {\n\t\t\tconst resources = client.viewContainerVersion(\n\t\t\t\t\"badidonviewversion\",\n\t\t\t\tschema,\n\t\t\t\t{\n\t\t\t\t\tid: \"whatever\",\n\t\t\t\t},\n\t\t\t\t\"2\",\n\t\t\t);\n\t\t\tconst errorFn = (error: Error): boolean => {\n\t\t\t\tassert.notStrictEqual(error.message, undefined, \"Azure Client error is undefined\");\n\t\t\t\tassert.strictEqual(\n\t\t\t\t\terror.message,\n\t\t\t\t\t\"R11s fetch error: Document is deleted and cannot be accessed.\",\n\t\t\t\t\t`Unexpected error: ${error.message}`,\n\t\t\t\t);\n\t\t\t\treturn true;\n\t\t\t};\n\n\t\t\tawait assert.rejects(resources, errorFn, \"We should not be able to view the container.\");\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.23.0-323641",
|
|
4
4
|
"description": "Azure client end to end tests",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -33,28 +33,29 @@
|
|
|
33
33
|
"temp-directory": "nyc/.nyc_output"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@fluid-experimental/data-objects": "
|
|
37
|
-
"@fluid-internal/client-utils": "
|
|
38
|
-
"@fluid-internal/mocha-test-setup": "
|
|
39
|
-
"@fluid-private/test-version-utils": "
|
|
40
|
-
"@fluidframework/aqueduct": "
|
|
41
|
-
"@fluidframework/azure-client": "
|
|
36
|
+
"@fluid-experimental/data-objects": "2.23.0-323641",
|
|
37
|
+
"@fluid-internal/client-utils": "2.23.0-323641",
|
|
38
|
+
"@fluid-internal/mocha-test-setup": "2.23.0-323641",
|
|
39
|
+
"@fluid-private/test-version-utils": "2.23.0-323641",
|
|
40
|
+
"@fluidframework/aqueduct": "2.23.0-323641",
|
|
41
|
+
"@fluidframework/azure-client": "2.23.0-323641",
|
|
42
42
|
"@fluidframework/azure-client-legacy": "npm:@fluidframework/azure-client@^1.2.0",
|
|
43
|
-
"@fluidframework/container-definitions": "
|
|
44
|
-
"@fluidframework/container-loader": "
|
|
45
|
-
"@fluidframework/core-interfaces": "
|
|
46
|
-
"@fluidframework/counter": "
|
|
47
|
-
"@fluidframework/datastore-definitions": "
|
|
48
|
-
"@fluidframework/fluid-static": "
|
|
49
|
-
"@fluidframework/map": "
|
|
43
|
+
"@fluidframework/container-definitions": "2.23.0-323641",
|
|
44
|
+
"@fluidframework/container-loader": "2.23.0-323641",
|
|
45
|
+
"@fluidframework/core-interfaces": "2.23.0-323641",
|
|
46
|
+
"@fluidframework/counter": "2.23.0-323641",
|
|
47
|
+
"@fluidframework/datastore-definitions": "2.23.0-323641",
|
|
48
|
+
"@fluidframework/fluid-static": "2.23.0-323641",
|
|
49
|
+
"@fluidframework/map": "2.23.0-323641",
|
|
50
50
|
"@fluidframework/map-legacy": "npm:@fluidframework/map@^1.4.0",
|
|
51
|
-
"@fluidframework/matrix": "
|
|
52
|
-
"@fluidframework/
|
|
53
|
-
"@fluidframework/
|
|
54
|
-
"@fluidframework/
|
|
55
|
-
"@fluidframework/
|
|
56
|
-
"@fluidframework/test-utils": "
|
|
57
|
-
"@fluidframework/
|
|
51
|
+
"@fluidframework/matrix": "2.23.0-323641",
|
|
52
|
+
"@fluidframework/presence": "2.23.0-323641",
|
|
53
|
+
"@fluidframework/runtime-definitions": "2.23.0-323641",
|
|
54
|
+
"@fluidframework/sequence": "2.23.0-323641",
|
|
55
|
+
"@fluidframework/telemetry-utils": "2.23.0-323641",
|
|
56
|
+
"@fluidframework/test-runtime-utils": "2.23.0-323641",
|
|
57
|
+
"@fluidframework/test-utils": "2.23.0-323641",
|
|
58
|
+
"@fluidframework/tree": "2.23.0-323641",
|
|
58
59
|
"axios": "^1.7.7",
|
|
59
60
|
"cross-env": "^7.0.3",
|
|
60
61
|
"mocha": "^10.2.0",
|
|
@@ -68,8 +69,8 @@
|
|
|
68
69
|
"devDependencies": {
|
|
69
70
|
"@biomejs/biome": "~1.9.3",
|
|
70
71
|
"@fluidframework/build-common": "^2.0.3",
|
|
71
|
-
"@fluidframework/build-tools": "^0.
|
|
72
|
-
"@fluidframework/driver-definitions": "
|
|
72
|
+
"@fluidframework/build-tools": "^0.53.0",
|
|
73
|
+
"@fluidframework/driver-definitions": "2.23.0-323641",
|
|
73
74
|
"@fluidframework/eslint-config-fluid": "^5.7.3",
|
|
74
75
|
"@types/mocha": "^10.0.10",
|
|
75
76
|
"@types/nock": "^9.3.0",
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { strict as assert } from "node:assert";
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
AzureClient,
|
|
10
|
+
type AzureContainerServices,
|
|
11
|
+
AzureLocalConnectionConfig,
|
|
12
|
+
AzureRemoteConnectionConfig,
|
|
13
|
+
} from "@fluidframework/azure-client";
|
|
14
|
+
import { AttachState } from "@fluidframework/container-definitions";
|
|
15
|
+
import { ConnectionState } from "@fluidframework/container-loader";
|
|
16
|
+
import { ContainerSchema, type IFluidContainer } from "@fluidframework/fluid-static";
|
|
17
|
+
import {
|
|
18
|
+
acquirePresenceViaDataObject,
|
|
19
|
+
ExperimentalPresenceManager,
|
|
20
|
+
type ExperimentalPresenceDO,
|
|
21
|
+
type IPresence,
|
|
22
|
+
type ISessionClient,
|
|
23
|
+
// eslint-disable-next-line import/no-internal-modules
|
|
24
|
+
} from "@fluidframework/presence/alpha";
|
|
25
|
+
import { InsecureTokenProvider } from "@fluidframework/test-runtime-utils/internal";
|
|
26
|
+
import { timeoutPromise } from "@fluidframework/test-utils/internal";
|
|
27
|
+
|
|
28
|
+
import { ScopeType } from "../AzureClientFactory.js";
|
|
29
|
+
import { createAzureTokenProvider } from "../AzureTokenFactory.js";
|
|
30
|
+
import { configProvider } from "../utils.js";
|
|
31
|
+
|
|
32
|
+
import { MessageFromChild, MessageToChild } from "./messageTypes.js";
|
|
33
|
+
|
|
34
|
+
type MessageFromParent = MessageToChild;
|
|
35
|
+
type MessageToParent = Required<MessageFromChild>;
|
|
36
|
+
interface UserIdAndName {
|
|
37
|
+
id: string;
|
|
38
|
+
name: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const connectTimeoutMs = 10_000;
|
|
42
|
+
// Identifier given to child process
|
|
43
|
+
const process_id = process.argv[2];
|
|
44
|
+
|
|
45
|
+
const useAzure = process.env.FLUID_CLIENT === "azure";
|
|
46
|
+
const tenantId = useAzure
|
|
47
|
+
? (process.env.azure__fluid__relay__service__tenantId as string)
|
|
48
|
+
: "frs-client-tenant";
|
|
49
|
+
const endPoint = process.env.azure__fluid__relay__service__endpoint as string;
|
|
50
|
+
if (useAzure && endPoint === undefined) {
|
|
51
|
+
throw new Error("Azure Fluid Relay service endpoint is missing");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Get or create a Fluid container with Presence in initialObjects.
|
|
56
|
+
*/
|
|
57
|
+
const getOrCreatePresenceContainer = async (
|
|
58
|
+
id: string | undefined,
|
|
59
|
+
user: UserIdAndName,
|
|
60
|
+
config?: ReturnType<typeof configProvider>,
|
|
61
|
+
scopes?: ScopeType[],
|
|
62
|
+
): Promise<{
|
|
63
|
+
container: IFluidContainer;
|
|
64
|
+
presence: IPresence;
|
|
65
|
+
services: AzureContainerServices;
|
|
66
|
+
client: AzureClient;
|
|
67
|
+
containerId: string;
|
|
68
|
+
}> => {
|
|
69
|
+
let container: IFluidContainer;
|
|
70
|
+
let containerId: string;
|
|
71
|
+
const connectionProps: AzureRemoteConnectionConfig | AzureLocalConnectionConfig = useAzure
|
|
72
|
+
? {
|
|
73
|
+
tenantId,
|
|
74
|
+
tokenProvider: createAzureTokenProvider(user.id ?? "foo", user.name ?? "bar", scopes),
|
|
75
|
+
endpoint: endPoint,
|
|
76
|
+
type: "remote",
|
|
77
|
+
}
|
|
78
|
+
: {
|
|
79
|
+
tokenProvider: new InsecureTokenProvider("fooBar", user, scopes),
|
|
80
|
+
endpoint: "http://localhost:7071",
|
|
81
|
+
type: "local",
|
|
82
|
+
};
|
|
83
|
+
const client = new AzureClient({ connection: connectionProps });
|
|
84
|
+
const schema: ContainerSchema = {
|
|
85
|
+
initialObjects: {
|
|
86
|
+
presence: ExperimentalPresenceManager,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
let services: AzureContainerServices;
|
|
90
|
+
if (id === undefined) {
|
|
91
|
+
({ container, services } = await client.createContainer(schema, "2"));
|
|
92
|
+
containerId = await container.attach();
|
|
93
|
+
} else {
|
|
94
|
+
containerId = id;
|
|
95
|
+
({ container, services } = await client.getContainer(containerId, schema, "2"));
|
|
96
|
+
}
|
|
97
|
+
// wait for 'ConnectionState.Connected' so we return with client connected to container
|
|
98
|
+
if (container.connectionState !== ConnectionState.Connected) {
|
|
99
|
+
await timeoutPromise((resolve) => container.once("connected", () => resolve()), {
|
|
100
|
+
durationMs: connectTimeoutMs,
|
|
101
|
+
errorMsg: "container connect() timeout",
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
assert.strictEqual(
|
|
105
|
+
container.attachState,
|
|
106
|
+
AttachState.Attached,
|
|
107
|
+
"Container is not attached after attach is called",
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const presence = acquirePresenceViaDataObject(
|
|
111
|
+
container.initialObjects.presence as ExperimentalPresenceDO,
|
|
112
|
+
);
|
|
113
|
+
return {
|
|
114
|
+
client,
|
|
115
|
+
container,
|
|
116
|
+
presence,
|
|
117
|
+
services,
|
|
118
|
+
containerId,
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
function createSendFunction(): (msg: MessageToParent) => void {
|
|
122
|
+
if (process.send) {
|
|
123
|
+
return process.send.bind(process);
|
|
124
|
+
}
|
|
125
|
+
throw new Error("process.send is not defined");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const send = createSendFunction();
|
|
129
|
+
|
|
130
|
+
function isConnected(container: IFluidContainer | undefined): boolean {
|
|
131
|
+
return container !== undefined && container.connectionState === ConnectionState.Connected;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
class MessageHandler {
|
|
135
|
+
public presence: IPresence | undefined;
|
|
136
|
+
public container: IFluidContainer | undefined;
|
|
137
|
+
public containerId: string | undefined;
|
|
138
|
+
|
|
139
|
+
public async onMessage(msg: MessageFromParent): Promise<void> {
|
|
140
|
+
switch (msg.command) {
|
|
141
|
+
// Respond to connect command by connecting to Fluid container with the provided user information.
|
|
142
|
+
case "connect": {
|
|
143
|
+
// Check if valid user information has been provided by parent/orchestrator
|
|
144
|
+
if (!msg.user) {
|
|
145
|
+
send({ event: "error", error: `${process_id}: No azure user information given` });
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
// Check if already connected to container
|
|
149
|
+
if (isConnected(this.container)) {
|
|
150
|
+
send({ event: "error", error: `${process_id}: Already connected to container` });
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
const { container, presence, containerId } = await getOrCreatePresenceContainer(
|
|
154
|
+
msg.containerId,
|
|
155
|
+
msg.user,
|
|
156
|
+
);
|
|
157
|
+
this.container = container;
|
|
158
|
+
this.presence = presence;
|
|
159
|
+
this.containerId = containerId;
|
|
160
|
+
|
|
161
|
+
// Listen for presence events to notify parent/orchestrator when a new attendee joins or leaves the session.
|
|
162
|
+
presence.events.on("attendeeJoined", (attendee: ISessionClient) => {
|
|
163
|
+
const m: MessageToParent = {
|
|
164
|
+
event: "attendeeJoined",
|
|
165
|
+
sessionId: attendee.sessionId,
|
|
166
|
+
};
|
|
167
|
+
send(m);
|
|
168
|
+
});
|
|
169
|
+
presence.events.on("attendeeDisconnected", (attendee: ISessionClient) => {
|
|
170
|
+
const m: MessageToParent = {
|
|
171
|
+
event: "attendeeDisconnected",
|
|
172
|
+
sessionId: attendee.sessionId,
|
|
173
|
+
};
|
|
174
|
+
send(m);
|
|
175
|
+
});
|
|
176
|
+
send({ event: "ready", containerId, sessionId: presence.getMyself().sessionId });
|
|
177
|
+
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Respond to disconnect command by disconnecting self from Fluid container.
|
|
182
|
+
case "disconnectSelf": {
|
|
183
|
+
if (!this.container) {
|
|
184
|
+
send({ event: "error", error: `${process_id} is not connected to container` });
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
if (!this.presence) {
|
|
188
|
+
send({ event: "error", error: `${process_id} is not connected to presence` });
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
this.container.disconnect();
|
|
193
|
+
send({
|
|
194
|
+
event: "disconnectedSelf",
|
|
195
|
+
sessionId: this.presence.getMyself().sessionId,
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
default: {
|
|
201
|
+
console.error(`${process_id}: Unknown command`);
|
|
202
|
+
send({ event: "error", error: `${process_id} Unknown command` });
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function setupMessageHandler(): void {
|
|
210
|
+
const messageHandler = new MessageHandler();
|
|
211
|
+
process.on("message", (msg: MessageFromParent) => {
|
|
212
|
+
messageHandler.onMessage(msg).catch((error: Error) => {
|
|
213
|
+
console.error(`Error in client ${process_id}`, error);
|
|
214
|
+
send({ event: "error", error: `${process_id}: ${error.message}` });
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
setupMessageHandler();
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { AzureUser } from "@fluidframework/azure-client/internal";
|
|
7
|
+
// eslint-disable-next-line import/no-internal-modules
|
|
8
|
+
import type { ClientSessionId } from "@fluidframework/presence/alpha";
|
|
9
|
+
|
|
10
|
+
export type MessageToChild = ConnectCommand | DisconnectSelfCommand;
|
|
11
|
+
interface ConnectCommand {
|
|
12
|
+
command: "connect";
|
|
13
|
+
user: AzureUser;
|
|
14
|
+
containerId?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface DisconnectSelfCommand {
|
|
18
|
+
command: "disconnectSelf";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type MessageFromChild =
|
|
22
|
+
| AttendeeDisconnectedEvent
|
|
23
|
+
| AttendeeJoinedEvent
|
|
24
|
+
| ReadyEvent
|
|
25
|
+
| DisconnectedSelfEvent
|
|
26
|
+
| ErrorEvent;
|
|
27
|
+
interface AttendeeDisconnectedEvent {
|
|
28
|
+
event: "attendeeDisconnected";
|
|
29
|
+
sessionId: ClientSessionId;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface AttendeeJoinedEvent {
|
|
33
|
+
event: "attendeeJoined";
|
|
34
|
+
sessionId: ClientSessionId;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface ReadyEvent {
|
|
38
|
+
event: "ready";
|
|
39
|
+
containerId: string;
|
|
40
|
+
sessionId: ClientSessionId;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface DisconnectedSelfEvent {
|
|
44
|
+
event: "disconnectedSelf";
|
|
45
|
+
sessionId: ClientSessionId;
|
|
46
|
+
}
|
|
47
|
+
interface ErrorEvent {
|
|
48
|
+
event: "error";
|
|
49
|
+
error: string;
|
|
50
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { strict as assert } from "node:assert";
|
|
7
|
+
import { fork, ChildProcess } from "node:child_process";
|
|
8
|
+
|
|
9
|
+
import { timeoutPromise } from "@fluidframework/test-utils/internal";
|
|
10
|
+
|
|
11
|
+
import type { MessageFromChild, MessageToChild } from "./messageTypes.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* This test suite is a prototype for a multi-process end to end test for Fluid using the new Presence API on AzureClient.
|
|
15
|
+
* In the future we hope to expand and generalize this pattern to broadly test more Fluid features.
|
|
16
|
+
* Currently our E2E tests are limited to running multiple clients on a single process which does not effectively
|
|
17
|
+
* simulate real-world production scenarios where clients are usually running on different machines.
|
|
18
|
+
*
|
|
19
|
+
* The pattern demonstrated in this test suite is as follows:
|
|
20
|
+
*
|
|
21
|
+
* This main test file acts as the 'Orchestrator'. The orchestrator's job includes:
|
|
22
|
+
* - Fork child processes to simulate multiple Fluid clients
|
|
23
|
+
* - Send command messages to child clients to perform specific Fluid actions.
|
|
24
|
+
* - Receive response messages from child clients to verify expected behavior.
|
|
25
|
+
* - Clean up child processes after each test.
|
|
26
|
+
*
|
|
27
|
+
* The child processes are located in the `childClient.ts` file. Each child process simulates a Fluid client.
|
|
28
|
+
*
|
|
29
|
+
* The child client's job includes:
|
|
30
|
+
* - Create/Get + connect to Fluid container.
|
|
31
|
+
* - Listen for command messages from the orchestrator.
|
|
32
|
+
* - Perform the requested action.
|
|
33
|
+
* - Send response messages including any relevant data back to the orchestrator to verify expected behavior.
|
|
34
|
+
*
|
|
35
|
+
* This particular test suite tests the following E2E functionality for Presence:
|
|
36
|
+
* - Announce 'attendeeJoined' when remote client joins session.
|
|
37
|
+
* - Announce 'attendeeDisconnected' when remote client disconnects.
|
|
38
|
+
*/
|
|
39
|
+
describe(`Presence with AzureClient`, () => {
|
|
40
|
+
const numClients = 5; // Set the total number of Fluid clients to create
|
|
41
|
+
assert(numClients > 1, "Must have at least two clients");
|
|
42
|
+
let children: ChildProcess[] = [];
|
|
43
|
+
// This promise is used to capture all errors that occur in the child processes.
|
|
44
|
+
// It will never resolve successfully, it is only used to reject on child process error.
|
|
45
|
+
let childErrorPromise: Promise<void>;
|
|
46
|
+
// Timeout duration used when waiting for response messages from child processes.
|
|
47
|
+
const durationMs = 10_000;
|
|
48
|
+
|
|
49
|
+
const afterCleanUp: (() => void)[] = [];
|
|
50
|
+
|
|
51
|
+
async function connectChildProcesses(
|
|
52
|
+
childProcesses: ChildProcess[],
|
|
53
|
+
): Promise<string | undefined> {
|
|
54
|
+
let containerIdPromise: Promise<string> | undefined;
|
|
55
|
+
let containerCreatorSessionId: string | undefined;
|
|
56
|
+
for (const [index, child] of childProcesses.entries()) {
|
|
57
|
+
const user = { id: `test-user-id-${index}`, name: `test-user-name-${index}` };
|
|
58
|
+
const message: MessageToChild = { command: "connect", user };
|
|
59
|
+
|
|
60
|
+
if (containerIdPromise === undefined) {
|
|
61
|
+
// Create a promise that resolves with the containerId from the created container.
|
|
62
|
+
containerIdPromise = timeoutPromise<string>(
|
|
63
|
+
(resolve, reject) => {
|
|
64
|
+
child.once("message", (msg: MessageFromChild) => {
|
|
65
|
+
if (msg.event === "ready" && msg.containerId) {
|
|
66
|
+
containerCreatorSessionId = msg.sessionId;
|
|
67
|
+
resolve(msg.containerId);
|
|
68
|
+
} else {
|
|
69
|
+
reject(new Error(`Non-ready message from child0: ${JSON.stringify(msg)}`));
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
durationMs,
|
|
75
|
+
errorMsg: "did not receive 'ready' from child process",
|
|
76
|
+
},
|
|
77
|
+
);
|
|
78
|
+
} else {
|
|
79
|
+
// For subsequent children, wait for containerId from the promise only when needed.
|
|
80
|
+
message.containerId = await containerIdPromise;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
child.send(message);
|
|
84
|
+
}
|
|
85
|
+
return containerCreatorSessionId;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
beforeEach("setup", async () => {
|
|
89
|
+
// Collect all child process error promises into this array
|
|
90
|
+
const childErrorPromises: Promise<void>[] = [];
|
|
91
|
+
// Fork child processes
|
|
92
|
+
for (let i = 0; i < numClients; i++) {
|
|
93
|
+
const child = fork("./lib/test/multiprocess/childClient.js", [
|
|
94
|
+
`child${i}` /* identifier passed to child process */,
|
|
95
|
+
]);
|
|
96
|
+
const errorPromise = new Promise<void>((_, reject) => {
|
|
97
|
+
child.on("error", (error) => {
|
|
98
|
+
reject(new Error(`Child${i} process errored: ${error.message}`));
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
childErrorPromises.push(errorPromise);
|
|
102
|
+
children.push(child);
|
|
103
|
+
// Register cleanup for the child process listeners.
|
|
104
|
+
afterCleanUp.push(() => child.removeAllListeners());
|
|
105
|
+
}
|
|
106
|
+
// This race will be used to reject any of the following tests on any child process errors
|
|
107
|
+
childErrorPromise = Promise.race(childErrorPromises);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// After each test, kill each child process and call any cleanup functions that were registered
|
|
111
|
+
afterEach(async () => {
|
|
112
|
+
for (const child of children) {
|
|
113
|
+
child.kill();
|
|
114
|
+
}
|
|
115
|
+
children = [];
|
|
116
|
+
for (const cleanUp of afterCleanUp) {
|
|
117
|
+
cleanUp();
|
|
118
|
+
}
|
|
119
|
+
afterCleanUp.length = 0;
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("announces 'attendeeJoined' when remote client joins session and 'attendeeDisconnected' when remote client disconnects", async () => {
|
|
123
|
+
// Setup
|
|
124
|
+
const attendeeJoinedPromise = timeoutPromise(
|
|
125
|
+
(resolve) => {
|
|
126
|
+
let attendeesJoinedEvents = 0;
|
|
127
|
+
children[0].on("message", (msg: MessageFromChild) => {
|
|
128
|
+
if (msg.event === "attendeeJoined") {
|
|
129
|
+
attendeesJoinedEvents++;
|
|
130
|
+
if (attendeesJoinedEvents === numClients - 1) {
|
|
131
|
+
resolve();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
durationMs,
|
|
138
|
+
errorMsg: "did not receive all 'attendeeJoined' events",
|
|
139
|
+
},
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
// Act - connect all child processes
|
|
143
|
+
const creatorSessionId = await connectChildProcesses(children);
|
|
144
|
+
|
|
145
|
+
// Verify - wait for all 'attendeeJoined' events
|
|
146
|
+
await Promise.race([attendeeJoinedPromise, childErrorPromise]);
|
|
147
|
+
|
|
148
|
+
// Setup
|
|
149
|
+
const waitForDisconnected = children
|
|
150
|
+
.filter((_, index) => index !== 0)
|
|
151
|
+
.map(async (child, index) =>
|
|
152
|
+
timeoutPromise(
|
|
153
|
+
(resolve) => {
|
|
154
|
+
child.on("message", (msg: MessageFromChild) => {
|
|
155
|
+
if (msg.event === "attendeeDisconnected" && msg.sessionId === creatorSessionId) {
|
|
156
|
+
resolve();
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
durationMs,
|
|
162
|
+
errorMsg: `Attendee[${index}] Disconnected Timeout`,
|
|
163
|
+
},
|
|
164
|
+
),
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
// Act - disconnect first child process
|
|
168
|
+
children[0].send({ command: "disconnectSelf" });
|
|
169
|
+
|
|
170
|
+
// Verify - wait for all 'attendeeDisconnected' events
|
|
171
|
+
await Promise.race([Promise.all(waitForDisconnected), childErrorPromise]);
|
|
172
|
+
});
|
|
173
|
+
});
|