@fluid-experimental/odsp-end-to-end-tests 2.0.0-dev.7.4.0.221926
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.cjs +14 -0
- package/CHANGELOG.md +3 -0
- package/LICENSE +21 -0
- package/README.md +5 -0
- package/dist/test/OdspClientFactory.js +48 -0
- package/dist/test/OdspClientFactory.js.map +1 -0
- package/dist/test/OdspTokenFactory.js +53 -0
- package/dist/test/OdspTokenFactory.js.map +1 -0
- package/dist/test/TestDataObject.js +46 -0
- package/dist/test/TestDataObject.js.map +1 -0
- package/dist/test/audience.spec.js +142 -0
- package/dist/test/audience.spec.js.map +1 -0
- package/dist/test/containerCreate.spec.js +115 -0
- package/dist/test/containerCreate.spec.js.map +1 -0
- package/dist/test/ddsTests.spec.js +198 -0
- package/dist/test/ddsTests.spec.js.map +1 -0
- package/dist/test/utils.js +44 -0
- package/dist/test/utils.js.map +1 -0
- package/package.json +97 -0
- package/src/test/.mocharc.js +11 -0
- package/src/test/OdspClientFactory.ts +72 -0
- package/src/test/OdspTokenFactory.ts +76 -0
- package/src/test/TestDataObject.ts +63 -0
- package/src/test/audience.spec.ts +186 -0
- package/src/test/containerCreate.spec.ts +156 -0
- package/src/test/ddsTests.spec.ts +267 -0
- package/src/test/tsconfig.json +9 -0
- package/src/test/utils.ts +48 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
import { strict as assert } from "node:assert";
|
|
6
|
+
|
|
7
|
+
import { OdspClient } from "@fluid-experimental/odsp-client";
|
|
8
|
+
import { AttachState } from "@fluidframework/container-definitions";
|
|
9
|
+
import { ContainerSchema } from "@fluidframework/fluid-static";
|
|
10
|
+
import { SharedMap } from "@fluidframework/map";
|
|
11
|
+
import { timeoutPromise } from "@fluidframework/test-utils";
|
|
12
|
+
|
|
13
|
+
import { ConnectionState } from "@fluidframework/container-loader";
|
|
14
|
+
import { ConfigTypes, IConfigProviderBase } from "@fluidframework/core-interfaces";
|
|
15
|
+
import { createOdspClient, OdspTestCredentials } from "./OdspClientFactory";
|
|
16
|
+
import { waitForMember } from "./utils";
|
|
17
|
+
|
|
18
|
+
const configProvider = (settings: Record<string, ConfigTypes>): IConfigProviderBase => ({
|
|
19
|
+
getRawConfig: (name: string): ConfigTypes => settings[name],
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Default test credentials for odsp-client.
|
|
24
|
+
*/
|
|
25
|
+
const client1Creds: OdspTestCredentials = {
|
|
26
|
+
clientId: "process.env.odsp__client__client__id",
|
|
27
|
+
clientSecret: "process.env.odsp__client__client__secret",
|
|
28
|
+
username: "process.env.odsp__client__login__username",
|
|
29
|
+
password: "process.env.odsp__client__login__password",
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const client2Creds: OdspTestCredentials = {
|
|
33
|
+
clientId: "process.env.odsp__client2__client__id",
|
|
34
|
+
clientSecret: "process.env.odsp__client2__client__secret",
|
|
35
|
+
username: "process.env.odsp__client2__login__username",
|
|
36
|
+
password: "process.env.odsp__client2__login__password",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
describe("Fluid audience", () => {
|
|
40
|
+
const connectTimeoutMs = 10_000;
|
|
41
|
+
let client: OdspClient;
|
|
42
|
+
let schema: ContainerSchema;
|
|
43
|
+
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
client = createOdspClient(client1Creds);
|
|
46
|
+
schema = {
|
|
47
|
+
initialObjects: {
|
|
48
|
+
map1: SharedMap,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Scenario: Find original member/self
|
|
55
|
+
*
|
|
56
|
+
* Expected behavior: container should have a single member upon creation.
|
|
57
|
+
*/
|
|
58
|
+
it("can find original member", async () => {
|
|
59
|
+
const { container, services } = await client.createContainer(schema);
|
|
60
|
+
const itemId = await container.attach();
|
|
61
|
+
|
|
62
|
+
if (container.connectionState !== ConnectionState.Connected) {
|
|
63
|
+
await timeoutPromise((resolve) => container.once("connected", () => resolve()), {
|
|
64
|
+
durationMs: connectTimeoutMs,
|
|
65
|
+
errorMsg: "container connect() timeout",
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
assert.strictEqual(typeof itemId, "string", "Attach did not return a string ID");
|
|
70
|
+
assert.strictEqual(
|
|
71
|
+
container.attachState,
|
|
72
|
+
AttachState.Attached,
|
|
73
|
+
"Container is not attached after attach is called",
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
/* This is a workaround for a known bug, we should have one member (self) upon container connection */
|
|
77
|
+
const myself = await waitForMember(services.audience, client1Creds.username);
|
|
78
|
+
assert.notStrictEqual(myself, undefined, "We should have myself at this point.");
|
|
79
|
+
|
|
80
|
+
const members = services.audience.getMembers();
|
|
81
|
+
assert.strictEqual(members.size, 1, "We should have only one member at this point.");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Scenario: Find partner member
|
|
86
|
+
*
|
|
87
|
+
* Expected behavior: upon resolving container, the partner member should be able
|
|
88
|
+
* to resolve original member.
|
|
89
|
+
*/
|
|
90
|
+
it("can find partner member", async () => {
|
|
91
|
+
const { container, services } = await client.createContainer(schema);
|
|
92
|
+
const itemId = await container.attach();
|
|
93
|
+
|
|
94
|
+
if (container.connectionState !== ConnectionState.Connected) {
|
|
95
|
+
await timeoutPromise((resolve) => container.once("connected", () => resolve()), {
|
|
96
|
+
durationMs: connectTimeoutMs,
|
|
97
|
+
errorMsg: "container connect() timeout",
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
assert.strictEqual(typeof itemId, "string", "Attach did not return a string ID");
|
|
102
|
+
assert.strictEqual(
|
|
103
|
+
container.attachState,
|
|
104
|
+
AttachState.Attached,
|
|
105
|
+
"Container is not attached after attach is called",
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
/* This is a workaround for a known bug, we should have one member (self) upon container connection */
|
|
109
|
+
const originalSelf = await waitForMember(services.audience, client1Creds.username);
|
|
110
|
+
assert.notStrictEqual(originalSelf, undefined, "We should have myself at this point.");
|
|
111
|
+
|
|
112
|
+
// pass client2 credentials
|
|
113
|
+
const client2 = createOdspClient(
|
|
114
|
+
client2Creds,
|
|
115
|
+
undefined,
|
|
116
|
+
configProvider({
|
|
117
|
+
"Fluid.Container.ForceWriteConnection": true,
|
|
118
|
+
}),
|
|
119
|
+
);
|
|
120
|
+
const { services: servicesGet } = await client2.getContainer(itemId, schema);
|
|
121
|
+
|
|
122
|
+
/* This is a workaround for a known bug, we should have one member (self) upon container connection */
|
|
123
|
+
const partner = await waitForMember(servicesGet.audience, client2Creds.username);
|
|
124
|
+
assert.notStrictEqual(partner, undefined, "We should have partner at this point.");
|
|
125
|
+
|
|
126
|
+
const members = servicesGet.audience.getMembers();
|
|
127
|
+
// TODO: uncomment this. One more test creds are required
|
|
128
|
+
// assert.strictEqual(members.size, 2, "We should have two members at this point.");
|
|
129
|
+
assert.strictEqual(members.size, 1, "We should have two members at this point.");
|
|
130
|
+
|
|
131
|
+
// TODO: login using a different M365 account
|
|
132
|
+
// assert.notStrictEqual(
|
|
133
|
+
// partner?.userId,
|
|
134
|
+
// originalSelf?.userId,
|
|
135
|
+
// "Self and partner should have different IDs",
|
|
136
|
+
// );
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Scenario: Partner should be able to observe change in audience
|
|
141
|
+
*
|
|
142
|
+
* Expected behavior: upon 1 partner leaving, other parther should observe
|
|
143
|
+
* memberRemoved event and have correct partner count.
|
|
144
|
+
*/
|
|
145
|
+
it("can observe member leaving", async () => {
|
|
146
|
+
const { container } = await client.createContainer(schema);
|
|
147
|
+
const itemId = await container.attach();
|
|
148
|
+
|
|
149
|
+
if (container.connectionState !== ConnectionState.Connected) {
|
|
150
|
+
await timeoutPromise((resolve) => container.once("connected", () => resolve()), {
|
|
151
|
+
durationMs: connectTimeoutMs,
|
|
152
|
+
errorMsg: "container connect() timeout",
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// pass client2 siteUrl and driveId
|
|
157
|
+
const client2 = createOdspClient(
|
|
158
|
+
client2Creds,
|
|
159
|
+
undefined,
|
|
160
|
+
configProvider({
|
|
161
|
+
"Fluid.Container.ForceWriteConnection": true,
|
|
162
|
+
}),
|
|
163
|
+
);
|
|
164
|
+
const { services: servicesGet } = await client2.getContainer(itemId, schema);
|
|
165
|
+
|
|
166
|
+
/* This is a workaround for a known bug, we should have one member (self) upon container connection */
|
|
167
|
+
const partner = await waitForMember(servicesGet.audience, client2Creds.username);
|
|
168
|
+
assert.notStrictEqual(partner, undefined, "We should have partner at this point.");
|
|
169
|
+
|
|
170
|
+
let members = servicesGet.audience.getMembers();
|
|
171
|
+
// TODO: uncomment this. One more test creds are required
|
|
172
|
+
// assert.strictEqual(members.size, 2, "We should have two members at this point.");
|
|
173
|
+
assert.strictEqual(members.size, 1, "We should have two members at this point.");
|
|
174
|
+
|
|
175
|
+
container.disconnect();
|
|
176
|
+
|
|
177
|
+
await new Promise<void>((resolve) => {
|
|
178
|
+
servicesGet.audience.on("memberRemoved", () => {
|
|
179
|
+
resolve();
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
members = servicesGet.audience.getMembers();
|
|
184
|
+
assert.strictEqual(members.size, 1, "We should have one member left at this point.");
|
|
185
|
+
});
|
|
186
|
+
});
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
import { strict as assert } from "node:assert";
|
|
6
|
+
|
|
7
|
+
import { OdspClient } from "@fluid-experimental/odsp-client";
|
|
8
|
+
import { AttachState } from "@fluidframework/container-definitions";
|
|
9
|
+
import { ContainerSchema } from "@fluidframework/fluid-static";
|
|
10
|
+
import { SharedMap } from "@fluidframework/map";
|
|
11
|
+
import { timeoutPromise } from "@fluidframework/test-utils";
|
|
12
|
+
|
|
13
|
+
import { ConnectionState } from "@fluidframework/container-loader";
|
|
14
|
+
import { OdspTestCredentials, createOdspClient } from "./OdspClientFactory";
|
|
15
|
+
|
|
16
|
+
const clientCreds: OdspTestCredentials = {
|
|
17
|
+
clientId: "process.env.odsp__client__client__id",
|
|
18
|
+
clientSecret: "process.env.odsp__client__client__secret",
|
|
19
|
+
username: "process.env.odsp__client__login__username",
|
|
20
|
+
password: "process.env.odsp__client__login__password",
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
describe("Container create scenarios", () => {
|
|
24
|
+
const connectTimeoutMs = 10_000;
|
|
25
|
+
let client: OdspClient;
|
|
26
|
+
let schema: ContainerSchema;
|
|
27
|
+
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
client = createOdspClient(clientCreds);
|
|
30
|
+
schema = {
|
|
31
|
+
initialObjects: {
|
|
32
|
+
map1: SharedMap,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Scenario: test when an Odsp Client container is created,
|
|
39
|
+
* it is initially detached.
|
|
40
|
+
*
|
|
41
|
+
* Expected behavior: an error should not be thrown nor should a rejected promise
|
|
42
|
+
* be returned.
|
|
43
|
+
*/
|
|
44
|
+
it("Created container is detached", async () => {
|
|
45
|
+
const { container } = await client.createContainer(schema);
|
|
46
|
+
assert.strictEqual(
|
|
47
|
+
container.attachState,
|
|
48
|
+
AttachState.Detached,
|
|
49
|
+
"Container should be detached",
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// Make sure we can attach.
|
|
53
|
+
const itemId = await container.attach();
|
|
54
|
+
assert.strictEqual(typeof itemId, "string", "Attach did not return a string ID");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Scenario: Test attaching a container.
|
|
59
|
+
*
|
|
60
|
+
* Expected behavior: an error should not be thrown nor should a rejected promise
|
|
61
|
+
* be returned.
|
|
62
|
+
*/
|
|
63
|
+
it("can attach a container", async () => {
|
|
64
|
+
const { container } = await client.createContainer(schema);
|
|
65
|
+
const itemId = await container.attach();
|
|
66
|
+
|
|
67
|
+
if (container.connectionState !== ConnectionState.Connected) {
|
|
68
|
+
await timeoutPromise((resolve) => container.once("connected", () => resolve()), {
|
|
69
|
+
durationMs: connectTimeoutMs,
|
|
70
|
+
errorMsg: "container connect() timeout",
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
assert.strictEqual(typeof itemId, "string", "Attach did not return a string ID");
|
|
75
|
+
assert.strictEqual(
|
|
76
|
+
container.attachState,
|
|
77
|
+
AttachState.Attached,
|
|
78
|
+
"Container is not attached after attach is called",
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Scenario: Test if attaching a container twice fails.
|
|
84
|
+
*
|
|
85
|
+
* Expected behavior: an error should not be thrown nor should a rejected promise
|
|
86
|
+
* be returned.
|
|
87
|
+
*/
|
|
88
|
+
it("cannot attach a container twice", async () => {
|
|
89
|
+
const { container } = await client.createContainer(schema);
|
|
90
|
+
const itemId = await container.attach();
|
|
91
|
+
|
|
92
|
+
if (container.connectionState !== ConnectionState.Connected) {
|
|
93
|
+
await timeoutPromise((resolve) => container.once("connected", () => resolve()), {
|
|
94
|
+
durationMs: connectTimeoutMs,
|
|
95
|
+
errorMsg: "container connect() timeout",
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
assert.strictEqual(typeof itemId, "string", "Attach did not return a string ID");
|
|
100
|
+
assert.strictEqual(
|
|
101
|
+
container.attachState,
|
|
102
|
+
AttachState.Attached,
|
|
103
|
+
"Container is attached after attach is called",
|
|
104
|
+
);
|
|
105
|
+
await assert.rejects(container.attach(), () => true, "Container should not attach twice");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Scenario: test if Odsp Client can get an existing container.
|
|
110
|
+
*
|
|
111
|
+
* Expected behavior: an error should not be thrown nor should a rejected promise
|
|
112
|
+
* be returned.
|
|
113
|
+
*/
|
|
114
|
+
it("can retrieve existing ODSP container successfully", async () => {
|
|
115
|
+
const { container: newContainer } = await client.createContainer(schema);
|
|
116
|
+
const itemId = await newContainer.attach();
|
|
117
|
+
|
|
118
|
+
if (newContainer.connectionState !== ConnectionState.Connected) {
|
|
119
|
+
await timeoutPromise((resolve) => newContainer.once("connected", () => resolve()), {
|
|
120
|
+
durationMs: connectTimeoutMs,
|
|
121
|
+
errorMsg: "container connect() timeout",
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const resources = client.getContainer(itemId, schema);
|
|
126
|
+
await assert.doesNotReject(
|
|
127
|
+
resources,
|
|
128
|
+
() => true,
|
|
129
|
+
"container cannot be retrieved from ODSP",
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Scenario: test if Odsp Client can get a non-exiting container.
|
|
135
|
+
*
|
|
136
|
+
* Expected behavior: an error should be thrown when trying to get a non-existent container.
|
|
137
|
+
*/
|
|
138
|
+
it("cannot load improperly created container (cannot load a non-existent container)", async () => {
|
|
139
|
+
const containerAndServicesP = client.getContainer("containerConfig", schema);
|
|
140
|
+
|
|
141
|
+
const errorFn = (error: Error): boolean => {
|
|
142
|
+
assert.notStrictEqual(error.message, undefined, "Odsp Client error is undefined");
|
|
143
|
+
assert.strict(
|
|
144
|
+
error.message.startsWith("ODSP fetch error [400]"),
|
|
145
|
+
`Unexpected error: ${error.message}`,
|
|
146
|
+
);
|
|
147
|
+
return true;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
await assert.rejects(
|
|
151
|
+
containerAndServicesP,
|
|
152
|
+
errorFn,
|
|
153
|
+
"Odsp Client can load a non-existent container",
|
|
154
|
+
);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
import { strict as assert } from "node:assert";
|
|
6
|
+
|
|
7
|
+
import { OdspClient } from "@fluid-experimental/odsp-client";
|
|
8
|
+
import { IFluidHandle } from "@fluidframework/core-interfaces";
|
|
9
|
+
import { ContainerSchema } from "@fluidframework/fluid-static";
|
|
10
|
+
import { SharedMap } from "@fluidframework/map";
|
|
11
|
+
import { timeoutPromise } from "@fluidframework/test-utils";
|
|
12
|
+
|
|
13
|
+
import { ConnectionState } from "@fluidframework/container-loader";
|
|
14
|
+
import { OdspTestCredentials, createOdspClient } from "./OdspClientFactory";
|
|
15
|
+
import { CounterTestDataObject, TestDataObject } from "./TestDataObject";
|
|
16
|
+
import { mapWait } from "./utils";
|
|
17
|
+
|
|
18
|
+
const clientCreds: OdspTestCredentials = {
|
|
19
|
+
clientId: "process.env.odsp__client__client__id",
|
|
20
|
+
clientSecret: "process.env.odsp__client__client__secret",
|
|
21
|
+
username: "process.env.odsp__client__login__username",
|
|
22
|
+
password: "process.env.odsp__client__login__password",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
describe("Fluid data updates", () => {
|
|
26
|
+
const connectTimeoutMs = 10_000;
|
|
27
|
+
let client: OdspClient;
|
|
28
|
+
let schema: ContainerSchema;
|
|
29
|
+
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
client = createOdspClient(clientCreds);
|
|
32
|
+
schema = {
|
|
33
|
+
initialObjects: {
|
|
34
|
+
map1: SharedMap,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Scenario: test when an Odsp Client container is created,
|
|
41
|
+
* it can set the initial objects.
|
|
42
|
+
*
|
|
43
|
+
* Expected behavior: an error should not be thrown nor should a rejected promise
|
|
44
|
+
* be returned.
|
|
45
|
+
*/
|
|
46
|
+
it("can set DDSes as initial objects for a container", async () => {
|
|
47
|
+
const { container: newContainer } = await client.createContainer(schema);
|
|
48
|
+
const itemId = await newContainer.attach();
|
|
49
|
+
|
|
50
|
+
if (newContainer.connectionState !== ConnectionState.Connected) {
|
|
51
|
+
await timeoutPromise((resolve) => newContainer.once("connected", () => resolve()), {
|
|
52
|
+
durationMs: connectTimeoutMs,
|
|
53
|
+
errorMsg: "container connect() timeout",
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const resources = client.getContainer(itemId, schema);
|
|
58
|
+
await assert.doesNotReject(
|
|
59
|
+
resources,
|
|
60
|
+
() => true,
|
|
61
|
+
"container cannot be retrieved from ODSP",
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const { container } = await resources;
|
|
65
|
+
assert.deepStrictEqual(
|
|
66
|
+
Object.keys(container.initialObjects),
|
|
67
|
+
Object.keys(schema.initialObjects),
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Scenario: test if initialObjects passed into the container functions correctly.
|
|
73
|
+
*
|
|
74
|
+
* Expected behavior: initialObjects value loaded in two different containers should mirror
|
|
75
|
+
* each other after value is changed.
|
|
76
|
+
*/
|
|
77
|
+
it("can change DDSes within initialObjects value", async () => {
|
|
78
|
+
const { container } = await client.createContainer(schema);
|
|
79
|
+
const itemId = await container.attach();
|
|
80
|
+
|
|
81
|
+
if (container.connectionState !== ConnectionState.Connected) {
|
|
82
|
+
await timeoutPromise((resolve) => container.once("connected", () => resolve()), {
|
|
83
|
+
durationMs: connectTimeoutMs,
|
|
84
|
+
errorMsg: "container connect() timeout",
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const initialObjectsCreate = container.initialObjects;
|
|
89
|
+
const map1Create = initialObjectsCreate.map1 as SharedMap;
|
|
90
|
+
map1Create.set("new-key", "new-value");
|
|
91
|
+
const valueCreate: string | undefined = map1Create.get("new-key");
|
|
92
|
+
|
|
93
|
+
const { container: containerGet } = await client.getContainer(itemId, schema);
|
|
94
|
+
const map1Get = containerGet.initialObjects.map1 as SharedMap;
|
|
95
|
+
const valueGet: string | undefined = await mapWait(map1Get, "new-key");
|
|
96
|
+
assert.strictEqual(valueGet, valueCreate, "container can't change initial objects");
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Scenario: test if we can create DataObjects through initialObjects schema.
|
|
101
|
+
*
|
|
102
|
+
* Expected behavior: DataObjects can be retrieved from the original and loaded container.
|
|
103
|
+
*/
|
|
104
|
+
it("can set DataObjects as initial objects for a container", async () => {
|
|
105
|
+
const doSchema: ContainerSchema = {
|
|
106
|
+
initialObjects: {
|
|
107
|
+
mdo1: TestDataObject,
|
|
108
|
+
mdo2: CounterTestDataObject,
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
const { container } = await client.createContainer(doSchema);
|
|
112
|
+
const itemId = await container.attach();
|
|
113
|
+
|
|
114
|
+
if (container.connectionState !== ConnectionState.Connected) {
|
|
115
|
+
await timeoutPromise((resolve) => container.once("connected", () => resolve()), {
|
|
116
|
+
durationMs: connectTimeoutMs,
|
|
117
|
+
errorMsg: "container connect() timeout",
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const initialObjectsCreate = container.initialObjects;
|
|
122
|
+
assert(
|
|
123
|
+
initialObjectsCreate.mdo1 instanceof TestDataObject,
|
|
124
|
+
"container returns the wrong type for mdo1",
|
|
125
|
+
);
|
|
126
|
+
assert(
|
|
127
|
+
initialObjectsCreate.mdo2 instanceof CounterTestDataObject,
|
|
128
|
+
"container returns the wrong type for mdo2",
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const { container: containerGet } = await client.getContainer(itemId, doSchema);
|
|
132
|
+
const initialObjectsGet = containerGet.initialObjects;
|
|
133
|
+
assert(
|
|
134
|
+
initialObjectsGet.mdo1 instanceof TestDataObject,
|
|
135
|
+
"container returns the wrong type for mdo1",
|
|
136
|
+
);
|
|
137
|
+
assert(
|
|
138
|
+
initialObjectsCreate.mdo2 instanceof CounterTestDataObject,
|
|
139
|
+
"container returns the wrong type for mdo2",
|
|
140
|
+
);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Scenario: test if we can create multiple DataObjects of the same type
|
|
145
|
+
*
|
|
146
|
+
* Expected behavior: DataObjects of the same type can be retrieved from the
|
|
147
|
+
* original and loaded container.
|
|
148
|
+
* TODO: Known bug that needs to be re-tested once fixed.
|
|
149
|
+
*/
|
|
150
|
+
it("can use multiple DataObjects of the same type", async () => {
|
|
151
|
+
const doSchema: ContainerSchema = {
|
|
152
|
+
initialObjects: {
|
|
153
|
+
mdo1: TestDataObject,
|
|
154
|
+
mdo2: CounterTestDataObject,
|
|
155
|
+
mdo3: CounterTestDataObject,
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
const { container } = await client.createContainer(doSchema);
|
|
159
|
+
const itemId = await container.attach();
|
|
160
|
+
|
|
161
|
+
if (container.connectionState !== ConnectionState.Connected) {
|
|
162
|
+
await timeoutPromise((resolve) => container.once("connected", () => resolve()), {
|
|
163
|
+
durationMs: connectTimeoutMs,
|
|
164
|
+
errorMsg: "container connect() timeout",
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const initialObjectsCreate = container.initialObjects;
|
|
169
|
+
assert(
|
|
170
|
+
initialObjectsCreate.mdo1 instanceof TestDataObject,
|
|
171
|
+
"container returns the wrong type for mdo1",
|
|
172
|
+
);
|
|
173
|
+
assert(
|
|
174
|
+
initialObjectsCreate.mdo2 instanceof CounterTestDataObject,
|
|
175
|
+
"container returns the wrong type for mdo2",
|
|
176
|
+
);
|
|
177
|
+
assert(
|
|
178
|
+
initialObjectsCreate.mdo3 instanceof CounterTestDataObject,
|
|
179
|
+
"container returns the wrong type for mdo3",
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
const { container: containerGet } = await client.getContainer(itemId, doSchema);
|
|
183
|
+
const initialObjectsGet = containerGet.initialObjects;
|
|
184
|
+
assert(
|
|
185
|
+
initialObjectsGet.mdo1 instanceof TestDataObject,
|
|
186
|
+
"container returns the wrong type for mdo1",
|
|
187
|
+
);
|
|
188
|
+
assert(
|
|
189
|
+
initialObjectsCreate.mdo2 instanceof CounterTestDataObject,
|
|
190
|
+
"container returns the wrong type for mdo2",
|
|
191
|
+
);
|
|
192
|
+
assert(
|
|
193
|
+
initialObjectsCreate.mdo3 instanceof CounterTestDataObject,
|
|
194
|
+
"container returns the wrong type for mdo3",
|
|
195
|
+
);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Scenario: test if we can change DataObject value contained within initialObjects
|
|
200
|
+
*
|
|
201
|
+
* Expected behavior: DataObject changes are correctly reflected on original and loaded containers
|
|
202
|
+
*/
|
|
203
|
+
it("can change DataObjects within initialObjects value", async () => {
|
|
204
|
+
const doSchema: ContainerSchema = {
|
|
205
|
+
initialObjects: {
|
|
206
|
+
mdo1: TestDataObject,
|
|
207
|
+
mdo2: CounterTestDataObject,
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
const { container } = await client.createContainer(doSchema);
|
|
211
|
+
const initialObjectsCreate = container.initialObjects;
|
|
212
|
+
const mdo2 = initialObjectsCreate.mdo2 as CounterTestDataObject;
|
|
213
|
+
mdo2.increment();
|
|
214
|
+
mdo2.increment();
|
|
215
|
+
mdo2.increment();
|
|
216
|
+
|
|
217
|
+
assert.strictEqual(mdo2.value, 3);
|
|
218
|
+
|
|
219
|
+
const itemId = await container.attach();
|
|
220
|
+
|
|
221
|
+
if (container.connectionState !== ConnectionState.Connected) {
|
|
222
|
+
await timeoutPromise((resolve) => container.once("connected", () => resolve()), {
|
|
223
|
+
durationMs: connectTimeoutMs,
|
|
224
|
+
errorMsg: "container connect() timeout",
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const { container: containerGet } = await client.getContainer(itemId, doSchema);
|
|
229
|
+
const initialObjectsGet = containerGet.initialObjects;
|
|
230
|
+
const mdo2get = initialObjectsGet.mdo2 as CounterTestDataObject;
|
|
231
|
+
|
|
232
|
+
assert.strictEqual(mdo2get.value, 3);
|
|
233
|
+
|
|
234
|
+
mdo2get.increment();
|
|
235
|
+
mdo2get.increment();
|
|
236
|
+
assert.strictEqual(mdo2get.value, 5);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Scenario: test if the optional schema parameter, dynamicObjectTypes (custom data objects),
|
|
241
|
+
* can be added during runtime and be returned by the container.
|
|
242
|
+
*
|
|
243
|
+
* Expected behavior: added loadable object can be retrieved from the container. Loadable
|
|
244
|
+
* object's id and container config ID should be identical since it's now attached to
|
|
245
|
+
* the container.
|
|
246
|
+
*/
|
|
247
|
+
it("can create/add loadable objects (custom data object) dynamically during runtime", async () => {
|
|
248
|
+
const dynamicSchema: ContainerSchema = {
|
|
249
|
+
initialObjects: {
|
|
250
|
+
map1: SharedMap,
|
|
251
|
+
},
|
|
252
|
+
dynamicObjectTypes: [TestDataObject],
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const { container } = await client.createContainer(dynamicSchema);
|
|
256
|
+
await container.attach();
|
|
257
|
+
|
|
258
|
+
const newDo = await container.create(TestDataObject);
|
|
259
|
+
assert.ok(newDo?.handle);
|
|
260
|
+
|
|
261
|
+
const map1 = container.initialObjects.map1 as SharedMap;
|
|
262
|
+
map1.set("new-pair-id", newDo.handle);
|
|
263
|
+
const handle: IFluidHandle | undefined = await map1.get("new-pair-id");
|
|
264
|
+
const obj: unknown = await handle?.get();
|
|
265
|
+
assert.ok(obj, "container added dynamic objects incorrectly");
|
|
266
|
+
});
|
|
267
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { OdspMember, IOdspAudience } from "@fluid-experimental/odsp-client";
|
|
7
|
+
import { IMember } from "@fluidframework/fluid-static";
|
|
8
|
+
import { ISharedMap, IValueChanged } from "@fluidframework/map";
|
|
9
|
+
|
|
10
|
+
export const waitForMember = async (
|
|
11
|
+
audience: IOdspAudience,
|
|
12
|
+
username: string,
|
|
13
|
+
): Promise<OdspMember> => {
|
|
14
|
+
const allMembers = audience.getMembers();
|
|
15
|
+
const member = allMembers.get(username);
|
|
16
|
+
if (member !== undefined) {
|
|
17
|
+
return member;
|
|
18
|
+
}
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
const handler = (clientId: string, newMember: IMember): void => {
|
|
21
|
+
if (newMember.userId === username) {
|
|
22
|
+
resolve(newMember as OdspMember);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
audience.on("memberAdded", handler);
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const mapWait = async <T>(map: ISharedMap, key: string): Promise<T> => {
|
|
30
|
+
const maybeValue = map.get<T>(key);
|
|
31
|
+
if (maybeValue !== undefined) {
|
|
32
|
+
return maybeValue;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return new Promise((resolve) => {
|
|
36
|
+
const handler = (changed: IValueChanged): void => {
|
|
37
|
+
if (changed.key === key) {
|
|
38
|
+
map.off("valueChanged", handler);
|
|
39
|
+
const value = map.get<T>(changed.key);
|
|
40
|
+
if (value === undefined) {
|
|
41
|
+
throw new Error("Unexpected valueChanged result");
|
|
42
|
+
}
|
|
43
|
+
resolve(value);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
map.on("valueChanged", handler);
|
|
47
|
+
});
|
|
48
|
+
};
|