@fluidframework/map 2.0.0-rc.1.0.6 → 2.0.0-rc.2.0.1
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.js → .eslintrc.cjs} +10 -1
- package/{.mocharc.js → .mocharc.cjs} +1 -1
- package/CHANGELOG.md +11 -0
- package/{api-extractor-esm.json → api-extractor-cjs.json} +5 -1
- package/api-extractor-lint.json +1 -1
- package/api-extractor.json +1 -1
- package/api-report/map.api.md +14 -57
- package/dist/directory.d.ts +10 -50
- package/dist/directory.d.ts.map +1 -1
- package/dist/directory.js +76 -164
- package/dist/directory.js.map +1 -1
- package/dist/index.d.ts +45 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +43 -8
- package/dist/index.js.map +1 -1
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js.map +1 -1
- package/dist/internalInterfaces.d.ts +2 -2
- package/dist/internalInterfaces.d.ts.map +1 -1
- package/dist/internalInterfaces.js.map +1 -1
- package/dist/localValues.d.ts +3 -5
- package/dist/localValues.d.ts.map +1 -1
- package/dist/localValues.js +9 -8
- package/dist/localValues.js.map +1 -1
- package/dist/map-alpha.d.ts +31 -116
- package/dist/map-beta.d.ts +24 -105
- package/dist/map-public.d.ts +24 -105
- package/dist/map-untrimmed.d.ts +31 -116
- package/dist/map.d.ts +4 -23
- package/dist/map.d.ts.map +1 -1
- package/dist/map.js +6 -29
- package/dist/map.js.map +1 -1
- package/dist/mapKernel.d.ts +3 -4
- package/dist/mapKernel.d.ts.map +1 -1
- package/dist/mapKernel.js +30 -35
- package/dist/mapKernel.js.map +1 -1
- package/dist/package.json +3 -0
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/tsdoc-metadata.json +1 -1
- package/lib/{directory.d.mts → directory.d.ts} +11 -51
- package/lib/directory.d.ts.map +1 -0
- package/lib/{directory.mjs → directory.js} +77 -165
- package/lib/directory.js.map +1 -0
- package/lib/index.d.ts +61 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +55 -0
- package/lib/index.js.map +1 -0
- package/lib/{interfaces.d.mts → interfaces.d.ts} +1 -1
- package/lib/interfaces.d.ts.map +1 -0
- package/lib/{interfaces.mjs → interfaces.js} +1 -1
- package/lib/interfaces.js.map +1 -0
- package/lib/{internalInterfaces.d.mts → internalInterfaces.d.ts} +3 -3
- package/lib/internalInterfaces.d.ts.map +1 -0
- package/lib/{internalInterfaces.mjs → internalInterfaces.js} +1 -1
- package/lib/internalInterfaces.js.map +1 -0
- package/lib/{localValues.d.mts → localValues.d.ts} +4 -6
- package/lib/localValues.d.ts.map +1 -0
- package/lib/{localValues.mjs → localValues.js} +10 -9
- package/lib/localValues.js.map +1 -0
- package/lib/{map-alpha.d.mts → map-alpha.d.ts} +43 -116
- package/lib/{map-beta.d.mts → map-beta.d.ts} +36 -105
- package/lib/{map-public.d.mts → map-public.d.ts} +36 -105
- package/lib/{map-untrimmed.d.mts → map-untrimmed.d.ts} +43 -116
- package/lib/{map.d.mts → map.d.ts} +5 -24
- package/lib/map.d.ts.map +1 -0
- package/lib/{map.mjs → map.js} +5 -28
- package/lib/map.js.map +1 -0
- package/lib/{mapKernel.d.mts → mapKernel.d.ts} +4 -5
- package/lib/mapKernel.d.ts.map +1 -0
- package/lib/{mapKernel.mjs → mapKernel.js} +32 -37
- package/lib/mapKernel.js.map +1 -0
- package/lib/{packageVersion.d.mts → packageVersion.d.ts} +2 -2
- package/lib/packageVersion.d.ts.map +1 -0
- package/lib/{packageVersion.mjs → packageVersion.js} +2 -2
- package/lib/packageVersion.js.map +1 -0
- package/lib/test/memory/directory.spec.js +71 -0
- package/lib/test/memory/directory.spec.js.map +1 -0
- package/lib/test/memory/map.spec.js +71 -0
- package/lib/test/memory/map.spec.js.map +1 -0
- package/lib/test/mocha/directory.order.spec.js +422 -0
- package/lib/test/mocha/directory.order.spec.js.map +1 -0
- package/lib/test/mocha/directory.snapshot.spec.js +111 -0
- package/lib/test/mocha/directory.snapshot.spec.js.map +1 -0
- package/lib/test/mocha/directory.spec.js +1406 -0
- package/lib/test/mocha/directory.spec.js.map +1 -0
- package/lib/test/mocha/directoryEquivalenceUtils.js +36 -0
- package/lib/test/mocha/directoryEquivalenceUtils.js.map +1 -0
- package/lib/test/mocha/directoryFuzzTests.spec.js +337 -0
- package/lib/test/mocha/directoryFuzzTests.spec.js.map +1 -0
- package/lib/test/mocha/dirname.cjs +16 -0
- package/lib/test/mocha/dirname.cjs.map +1 -0
- package/lib/test/mocha/map.fuzz.spec.js +114 -0
- package/lib/test/mocha/map.fuzz.spec.js.map +1 -0
- package/lib/test/mocha/map.spec.js +685 -0
- package/lib/test/mocha/map.spec.js.map +1 -0
- package/lib/test/mocha/rebasing.spec.js +158 -0
- package/lib/test/mocha/rebasing.spec.js.map +1 -0
- package/lib/test/mocha/reconnection.spec.js +327 -0
- package/lib/test/mocha/reconnection.spec.js.map +1 -0
- package/lib/test/types/validateMapPrevious.generated.js +66 -0
- package/lib/test/types/validateMapPrevious.generated.js.map +1 -0
- package/package.json +55 -52
- package/src/directory.ts +122 -217
- package/src/index.ts +57 -4
- package/src/interfaces.ts +2 -2
- package/src/internalInterfaces.ts +2 -2
- package/src/localValues.ts +14 -9
- package/src/map.ts +7 -32
- package/src/mapKernel.ts +40 -42
- package/src/packageVersion.ts +1 -1
- package/tsconfig.cjs.json +7 -0
- package/tsconfig.json +2 -5
- package/lib/directory.d.mts.map +0 -1
- package/lib/directory.mjs.map +0 -1
- package/lib/index.d.mts +0 -9
- package/lib/index.d.mts.map +0 -1
- package/lib/index.mjs +0 -8
- package/lib/index.mjs.map +0 -1
- package/lib/interfaces.d.mts.map +0 -1
- package/lib/interfaces.mjs.map +0 -1
- package/lib/internalInterfaces.d.mts.map +0 -1
- package/lib/internalInterfaces.mjs.map +0 -1
- package/lib/localValues.d.mts.map +0 -1
- package/lib/localValues.mjs.map +0 -1
- package/lib/map.d.mts.map +0 -1
- package/lib/map.mjs.map +0 -1
- package/lib/mapKernel.d.mts.map +0 -1
- package/lib/mapKernel.mjs.map +0 -1
- package/lib/packageVersion.d.mts.map +0 -1
- package/lib/packageVersion.mjs.map +0 -1
|
@@ -0,0 +1,685 @@
|
|
|
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
|
+
import { runGCTests } from "@fluid-private/test-dds-utils";
|
|
7
|
+
import { MockFluidDataStoreRuntime, MockContainerRuntimeFactory, MockSharedObjectServices, MockStorage, } from "@fluidframework/test-runtime-utils";
|
|
8
|
+
import { AttachState } from "@fluidframework/container-definitions";
|
|
9
|
+
import { MapFactory, SharedMap } from "../../map.js";
|
|
10
|
+
function createConnectedMap(id, runtimeFactory) {
|
|
11
|
+
const dataStoreRuntime = new MockFluidDataStoreRuntime();
|
|
12
|
+
const containerRuntime = runtimeFactory.createContainerRuntime(dataStoreRuntime);
|
|
13
|
+
const services = {
|
|
14
|
+
deltaConnection: dataStoreRuntime.createDeltaConnection(),
|
|
15
|
+
objectStorage: new MockStorage(),
|
|
16
|
+
};
|
|
17
|
+
const map = new SharedMap(id, dataStoreRuntime, MapFactory.Attributes);
|
|
18
|
+
map.connect(services);
|
|
19
|
+
return map;
|
|
20
|
+
}
|
|
21
|
+
function createLocalMap(id) {
|
|
22
|
+
const map = new SharedMap(id, new MockFluidDataStoreRuntime(), MapFactory.Attributes);
|
|
23
|
+
return map;
|
|
24
|
+
}
|
|
25
|
+
class TestSharedMap extends SharedMap {
|
|
26
|
+
testApplyStashedOp(content) {
|
|
27
|
+
this.lastMetadata = undefined;
|
|
28
|
+
this.applyStashedOp(content);
|
|
29
|
+
return this.lastMetadata;
|
|
30
|
+
}
|
|
31
|
+
submitLocalMessage(op, localOpMetadata) {
|
|
32
|
+
this.lastMetadata = localOpMetadata;
|
|
33
|
+
super.submitLocalMessage(op, localOpMetadata);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
describe("Map", () => {
|
|
37
|
+
describe("Local state", () => {
|
|
38
|
+
let map;
|
|
39
|
+
beforeEach("createLocalMap", async () => {
|
|
40
|
+
map = createLocalMap("testMap");
|
|
41
|
+
});
|
|
42
|
+
describe("API", () => {
|
|
43
|
+
it("Can create a new map", () => {
|
|
44
|
+
assert.ok(map, "could not create a new map");
|
|
45
|
+
});
|
|
46
|
+
it("Can set and get map data", async () => {
|
|
47
|
+
map.set("testKey", "testValue");
|
|
48
|
+
map.set("testKey2", "testValue2");
|
|
49
|
+
assert.equal(map.get("testKey"), "testValue", "could not retrieve set key 1");
|
|
50
|
+
assert.equal(map.get("testKey2"), "testValue2", "could not retrieve set key 2");
|
|
51
|
+
});
|
|
52
|
+
it("should fire correct map events", async () => {
|
|
53
|
+
const dummyMap = map;
|
|
54
|
+
let valueChangedExpected = true;
|
|
55
|
+
let clearExpected = false;
|
|
56
|
+
let previousValue;
|
|
57
|
+
dummyMap.on("op", (arg1, arg2, arg3) => {
|
|
58
|
+
assert.fail("shouldn't receive an op event");
|
|
59
|
+
});
|
|
60
|
+
dummyMap.on("valueChanged", (changed, local, target) => {
|
|
61
|
+
assert.equal(valueChangedExpected, true, "valueChange event not expected");
|
|
62
|
+
valueChangedExpected = false;
|
|
63
|
+
assert.equal(changed.key, "marco");
|
|
64
|
+
assert.equal(changed.previousValue, previousValue);
|
|
65
|
+
assert.equal(local, true, "local should be true for local action for valueChanged event");
|
|
66
|
+
assert.equal(target, dummyMap, "target should be the map for valueChanged event");
|
|
67
|
+
});
|
|
68
|
+
dummyMap.on("clear", (local, target) => {
|
|
69
|
+
assert.equal(clearExpected, true, "clear event not expected");
|
|
70
|
+
clearExpected = false;
|
|
71
|
+
assert.equal(local, true, "local should be true for local action for clear event");
|
|
72
|
+
assert.equal(target, dummyMap, "target should be the map for clear event");
|
|
73
|
+
});
|
|
74
|
+
dummyMap.on("error", (error) => {
|
|
75
|
+
// propagate error in the event handlers
|
|
76
|
+
throw error;
|
|
77
|
+
});
|
|
78
|
+
// Test set
|
|
79
|
+
previousValue = undefined;
|
|
80
|
+
dummyMap.set("marco", "polo");
|
|
81
|
+
assert.equal(valueChangedExpected, false, "missing valueChanged event");
|
|
82
|
+
// Test delete
|
|
83
|
+
previousValue = "polo";
|
|
84
|
+
valueChangedExpected = true;
|
|
85
|
+
dummyMap.delete("marco");
|
|
86
|
+
assert.equal(valueChangedExpected, false, "missing valueChanged event");
|
|
87
|
+
// Test clear
|
|
88
|
+
clearExpected = true;
|
|
89
|
+
dummyMap.clear();
|
|
90
|
+
assert.equal(clearExpected, false, "missing clear event");
|
|
91
|
+
});
|
|
92
|
+
it("Should return undefined when a key does not exist in the map", () => {
|
|
93
|
+
assert.equal(map.get("missing"), undefined, "get() did not return undefined for missing key");
|
|
94
|
+
});
|
|
95
|
+
it("Should reject undefined and null key sets", () => {
|
|
96
|
+
assert.throws(() => {
|
|
97
|
+
map.set(undefined, "one");
|
|
98
|
+
}, "Should throw for key of undefined");
|
|
99
|
+
assert.throws(() => {
|
|
100
|
+
// eslint-disable-next-line unicorn/no-null
|
|
101
|
+
map.set(null, "two");
|
|
102
|
+
}, "Should throw for key of null");
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
describe("Serialize", () => {
|
|
106
|
+
it("Should serialize the map as a JSON object", () => {
|
|
107
|
+
map.set("first", "second");
|
|
108
|
+
map.set("third", "fourth");
|
|
109
|
+
map.set("fifth", "sixth");
|
|
110
|
+
const subMap = createLocalMap("subMap");
|
|
111
|
+
map.set("object", subMap.handle);
|
|
112
|
+
const summaryContent = map.getAttachSummary().summary.tree.header
|
|
113
|
+
.content;
|
|
114
|
+
const subMapHandleUrl = subMap.handle.absolutePath;
|
|
115
|
+
assert.equal(summaryContent, `{"blobs":[],"content":{"first":{"type":"Plain","value":"second"},"third":{"type":"Plain","value":"fourth"},"fifth":{"type":"Plain","value":"sixth"},"object":{"type":"Plain","value":{"type":"__fluid_handle__","url":"${subMapHandleUrl}"}}}}`);
|
|
116
|
+
});
|
|
117
|
+
it("Should serialize an undefined value", () => {
|
|
118
|
+
map.set("first", "second");
|
|
119
|
+
map.set("third", "fourth");
|
|
120
|
+
map.set("fifth", undefined);
|
|
121
|
+
assert.ok(map.has("fifth"));
|
|
122
|
+
const subMap = createLocalMap("subMap");
|
|
123
|
+
map.set("object", subMap.handle);
|
|
124
|
+
const summaryContent = map.getAttachSummary().summary.tree.header
|
|
125
|
+
.content;
|
|
126
|
+
const subMapHandleUrl = subMap.handle.absolutePath;
|
|
127
|
+
assert.equal(summaryContent, `{"blobs":[],"content":{"first":{"type":"Plain","value":"second"},"third":{"type":"Plain","value":"fourth"},"fifth":{"type":"Plain"},"object":{"type":"Plain","value":{"type":"__fluid_handle__","url":"${subMapHandleUrl}"}}}}`);
|
|
128
|
+
});
|
|
129
|
+
it("Should serialize an object with nested handles", async () => {
|
|
130
|
+
const subMap = createLocalMap("subMap");
|
|
131
|
+
const subMap2 = createLocalMap("subMap2");
|
|
132
|
+
const containingObject = {
|
|
133
|
+
subMapHandle: subMap.handle,
|
|
134
|
+
nestedObj: {
|
|
135
|
+
subMap2Handle: subMap2.handle,
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
map.set("object", containingObject);
|
|
139
|
+
const subMapHandleUrl = subMap.handle.absolutePath;
|
|
140
|
+
const subMap2HandleUrl = subMap2.handle.absolutePath;
|
|
141
|
+
const summaryContent = map.getAttachSummary().summary.tree.header
|
|
142
|
+
.content;
|
|
143
|
+
assert.equal(summaryContent, `{"blobs":[],"content":{"object":{"type":"Plain","value":{"subMapHandle":{"type":"__fluid_handle__","url":"${subMapHandleUrl}"},"nestedObj":{"subMap2Handle":{"type":"__fluid_handle__","url":"${subMap2HandleUrl}"}}}}}}`);
|
|
144
|
+
});
|
|
145
|
+
it("can load old serialization format", async () => {
|
|
146
|
+
map.set("key", "value");
|
|
147
|
+
const content = JSON.stringify({
|
|
148
|
+
key: {
|
|
149
|
+
type: "Plain",
|
|
150
|
+
value: "value",
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
const services = new MockSharedObjectServices({ header: content });
|
|
154
|
+
const factory = new MapFactory();
|
|
155
|
+
const loadedMap = await factory.load(new MockFluidDataStoreRuntime(), "mapId", services, factory.attributes);
|
|
156
|
+
assert(loadedMap.get("key") === "value");
|
|
157
|
+
});
|
|
158
|
+
it("new serialization format for small maps", async () => {
|
|
159
|
+
map.set("key", "value");
|
|
160
|
+
const summaryTree = map.getAttachSummary().summary;
|
|
161
|
+
assert.strictEqual(Object.keys(summaryTree.tree).length, 1, "summary tree should only have one blob");
|
|
162
|
+
const summaryContent = summaryTree.tree.header?.content;
|
|
163
|
+
const expectedContent = JSON.stringify({
|
|
164
|
+
blobs: [],
|
|
165
|
+
content: {
|
|
166
|
+
key: {
|
|
167
|
+
type: "Plain",
|
|
168
|
+
value: "value",
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
assert.strictEqual(summaryContent, expectedContent, "The summary content is not as expected");
|
|
173
|
+
const services = new MockSharedObjectServices({ header: summaryContent });
|
|
174
|
+
const factory = new MapFactory();
|
|
175
|
+
const loadedMap = await factory.load(new MockFluidDataStoreRuntime(), "mapId", services, factory.attributes);
|
|
176
|
+
assert(loadedMap.get("key") === "value");
|
|
177
|
+
});
|
|
178
|
+
it("new serialization format for big maps", async () => {
|
|
179
|
+
map.set("key", "value");
|
|
180
|
+
// 40K char string
|
|
181
|
+
let longString = "01234567890";
|
|
182
|
+
for (let i = 0; i < 12; i++) {
|
|
183
|
+
longString = longString + longString;
|
|
184
|
+
}
|
|
185
|
+
map.set("longValue", longString);
|
|
186
|
+
map.set("zzz", "the end");
|
|
187
|
+
const summaryTree = map.getAttachSummary().summary;
|
|
188
|
+
assert.strictEqual(Object.keys(summaryTree.tree).length, 2, "There should be 2 entries in the summary tree");
|
|
189
|
+
const expectedContent1 = JSON.stringify({
|
|
190
|
+
blobs: ["blob0"],
|
|
191
|
+
content: {
|
|
192
|
+
key: {
|
|
193
|
+
type: "Plain",
|
|
194
|
+
value: "value",
|
|
195
|
+
},
|
|
196
|
+
zzz: {
|
|
197
|
+
type: "Plain",
|
|
198
|
+
value: "the end",
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
const expectedContent2 = JSON.stringify({
|
|
203
|
+
longValue: {
|
|
204
|
+
type: "Plain",
|
|
205
|
+
value: longString,
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
const header = summaryTree.tree.header;
|
|
209
|
+
const blob0 = summaryTree.tree.blob0;
|
|
210
|
+
assert.strictEqual(header?.content, expectedContent1, "header content is not as expected");
|
|
211
|
+
assert.strictEqual(blob0?.content, expectedContent2, "blob0 content is not as expected");
|
|
212
|
+
const services = new MockSharedObjectServices({
|
|
213
|
+
header: header.content,
|
|
214
|
+
blob0: blob0.content,
|
|
215
|
+
});
|
|
216
|
+
const factory = new MapFactory();
|
|
217
|
+
const loadedMap = await factory.load(new MockFluidDataStoreRuntime(), "mapId", services, factory.attributes);
|
|
218
|
+
assert(loadedMap.get("key") === "value");
|
|
219
|
+
assert(loadedMap.get("longValue") === longString);
|
|
220
|
+
assert(loadedMap.get("zzz") === "the end");
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
describe("Op processing", () => {
|
|
224
|
+
/**
|
|
225
|
+
* These tests test the scenario found in the following bug:
|
|
226
|
+
* {@link https://github.com/microsoft/FluidFramework/issues/2400}
|
|
227
|
+
*
|
|
228
|
+
* - A SharedMap in local state set a key.
|
|
229
|
+
*
|
|
230
|
+
* - A second SharedMap is then created from the snapshot of the first one.
|
|
231
|
+
*
|
|
232
|
+
* - The second SharedMap sets a new value to the same key.
|
|
233
|
+
*
|
|
234
|
+
* - The expected behavior is that the first SharedMap updates the key with the new value. But in the bug
|
|
235
|
+
* the first SharedMap stores the key in its pending state even though it does not send out an op. So,
|
|
236
|
+
* when it gets a remote op with the same key, it ignores it as it has a pending set with the same key.
|
|
237
|
+
*/
|
|
238
|
+
it("should correctly process a set operation sent in local state", async () => {
|
|
239
|
+
const dataStoreRuntime1 = new MockFluidDataStoreRuntime();
|
|
240
|
+
const map1 = new SharedMap("testMap1", dataStoreRuntime1, MapFactory.Attributes);
|
|
241
|
+
// Set a key in local state.
|
|
242
|
+
const key = "testKey";
|
|
243
|
+
const value = "testValue";
|
|
244
|
+
map1.set(key, value);
|
|
245
|
+
// Load a new SharedMap in connected state from the snapshot of the first one.
|
|
246
|
+
const containerRuntimeFactory = new MockContainerRuntimeFactory();
|
|
247
|
+
const dataStoreRuntime2 = new MockFluidDataStoreRuntime();
|
|
248
|
+
const containerRuntime2 = containerRuntimeFactory.createContainerRuntime(dataStoreRuntime2);
|
|
249
|
+
const services2 = MockSharedObjectServices.createFromSummary(map1.getAttachSummary().summary);
|
|
250
|
+
services2.deltaConnection = dataStoreRuntime2.createDeltaConnection();
|
|
251
|
+
const map2 = new SharedMap("testMap2", dataStoreRuntime2, MapFactory.Attributes);
|
|
252
|
+
await map2.load(services2);
|
|
253
|
+
// Now connect the first SharedMap
|
|
254
|
+
dataStoreRuntime1.setAttachState(AttachState.Attached);
|
|
255
|
+
const containerRuntime1 = containerRuntimeFactory.createContainerRuntime(dataStoreRuntime1);
|
|
256
|
+
const services1 = {
|
|
257
|
+
deltaConnection: dataStoreRuntime1.createDeltaConnection(),
|
|
258
|
+
objectStorage: new MockStorage(undefined),
|
|
259
|
+
};
|
|
260
|
+
map1.connect(services1);
|
|
261
|
+
// Verify that both the maps have the key.
|
|
262
|
+
assert.equal(map1.get(key), value, "The first map does not have the key");
|
|
263
|
+
assert.equal(map2.get(key), value, "The second map does not have the key");
|
|
264
|
+
// Set a new value for the same key in the second SharedMap.
|
|
265
|
+
const newValue = "newValue";
|
|
266
|
+
map2.set(key, newValue);
|
|
267
|
+
// Process the message.
|
|
268
|
+
containerRuntimeFactory.processAllMessages();
|
|
269
|
+
// Verify that both the maps have the new value.
|
|
270
|
+
assert.equal(map1.get(key), newValue, "The first map did not get the new value");
|
|
271
|
+
assert.equal(map2.get(key), newValue, "The second map did not get the new value");
|
|
272
|
+
});
|
|
273
|
+
it("metadata op", async () => {
|
|
274
|
+
const serializable = { type: "Plain", value: "value" };
|
|
275
|
+
const dataStoreRuntime1 = new MockFluidDataStoreRuntime();
|
|
276
|
+
const op = { type: "set", key: "key", value: serializable };
|
|
277
|
+
const map1 = new TestSharedMap("testMap1", dataStoreRuntime1, MapFactory.Attributes);
|
|
278
|
+
const containerRuntimeFactory = new MockContainerRuntimeFactory();
|
|
279
|
+
containerRuntimeFactory.createContainerRuntime(dataStoreRuntime1);
|
|
280
|
+
map1.connect({
|
|
281
|
+
deltaConnection: dataStoreRuntime1.createDeltaConnection(),
|
|
282
|
+
objectStorage: new MockStorage(undefined),
|
|
283
|
+
});
|
|
284
|
+
let metadata = map1.testApplyStashedOp(op);
|
|
285
|
+
assert.equal(metadata?.type, "add");
|
|
286
|
+
assert.equal(metadata.pendingMessageId, 0);
|
|
287
|
+
const editMetadata = map1.testApplyStashedOp(op);
|
|
288
|
+
assert.equal(editMetadata.type, "edit");
|
|
289
|
+
assert.equal(editMetadata.pendingMessageId, 1);
|
|
290
|
+
assert.equal(editMetadata.previousValue.value, "value");
|
|
291
|
+
const serializable2 = { type: "Plain", value: "value2" };
|
|
292
|
+
const op2 = { type: "set", key: "key2", value: serializable2 };
|
|
293
|
+
metadata = map1.testApplyStashedOp(op2);
|
|
294
|
+
assert.equal(metadata?.type, "add");
|
|
295
|
+
assert.equal(metadata.pendingMessageId, 2);
|
|
296
|
+
const op3 = { type: "delete", key: "key2" };
|
|
297
|
+
metadata = map1.testApplyStashedOp(op3);
|
|
298
|
+
assert.equal(metadata.type, "edit");
|
|
299
|
+
assert.equal(metadata.pendingMessageId, 3);
|
|
300
|
+
assert.equal(metadata.previousValue.value, "value2");
|
|
301
|
+
const op4 = { type: "clear" };
|
|
302
|
+
metadata = map1.testApplyStashedOp(op4);
|
|
303
|
+
assert.equal(metadata.pendingMessageId, 4);
|
|
304
|
+
assert.equal(metadata.type, "clear");
|
|
305
|
+
assert.equal(metadata.previousMap?.get("key")?.value, "value");
|
|
306
|
+
assert.equal(metadata.previousMap?.has("key2"), false);
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
describe("Connected state", () => {
|
|
311
|
+
let containerRuntimeFactory;
|
|
312
|
+
let map1;
|
|
313
|
+
let map2;
|
|
314
|
+
beforeEach("createConnectedMaps", async () => {
|
|
315
|
+
containerRuntimeFactory = new MockContainerRuntimeFactory();
|
|
316
|
+
// Create the first map
|
|
317
|
+
map1 = createConnectedMap("map1", containerRuntimeFactory);
|
|
318
|
+
// Create and connect a second map
|
|
319
|
+
map2 = createConnectedMap("map2", containerRuntimeFactory);
|
|
320
|
+
});
|
|
321
|
+
describe("API", () => {
|
|
322
|
+
describe(".get()", () => {
|
|
323
|
+
it("Should be able to retrieve a key", () => {
|
|
324
|
+
const value = "value";
|
|
325
|
+
map1.set("test", value);
|
|
326
|
+
containerRuntimeFactory.processAllMessages();
|
|
327
|
+
// Verify the local SharedMap
|
|
328
|
+
assert.equal(map1.get("test"), value, "could not retrieve key");
|
|
329
|
+
// Verify the remote SharedMap
|
|
330
|
+
assert.equal(map2.get("test"), value, "could not retrieve key from the remote map");
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
describe(".has()", () => {
|
|
334
|
+
it("Should return false when a key is not in the map", () => {
|
|
335
|
+
assert.equal(map1.has("notInSet"), false, "has() did not return false for missing key");
|
|
336
|
+
});
|
|
337
|
+
it("Should return true when a key is in the map", () => {
|
|
338
|
+
map1.set("inSet", "value");
|
|
339
|
+
containerRuntimeFactory.processAllMessages();
|
|
340
|
+
// Verify the local SharedMap
|
|
341
|
+
assert.equal(map1.has("inSet"), true, "could not find the key");
|
|
342
|
+
// Verify the remote SharedMap
|
|
343
|
+
assert.equal(map2.has("inSet"), true, "could not find the key in the remote map");
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
describe(".set()", () => {
|
|
347
|
+
it("Should set a key to a value", () => {
|
|
348
|
+
const value = "value";
|
|
349
|
+
map1.set("test", value);
|
|
350
|
+
containerRuntimeFactory.processAllMessages();
|
|
351
|
+
// Verify the local SharedMap
|
|
352
|
+
assert.equal(map1.has("test"), true, "could not find the set key");
|
|
353
|
+
assert.equal(map1.get("test"), value, "could not get the set key");
|
|
354
|
+
// Verify the remote SharedMap
|
|
355
|
+
assert.equal(map2.has("test"), true, "could not find the set key in remote map");
|
|
356
|
+
assert.equal(map2.get("test"), value, "could not get the set key from remote map");
|
|
357
|
+
});
|
|
358
|
+
it("Should be able to set a shared object handle as a key", () => {
|
|
359
|
+
const subMap = createLocalMap("subMap");
|
|
360
|
+
map1.set("test", subMap.handle);
|
|
361
|
+
containerRuntimeFactory.processAllMessages();
|
|
362
|
+
// Verify the local SharedMap
|
|
363
|
+
const localSubMap = map1.get("test");
|
|
364
|
+
assert(localSubMap);
|
|
365
|
+
assert.equal(localSubMap.absolutePath, subMap.handle.absolutePath, "could not get the handle's path");
|
|
366
|
+
// Verify the remote SharedMap
|
|
367
|
+
const remoteSubMap = map2.get("test");
|
|
368
|
+
assert(remoteSubMap);
|
|
369
|
+
assert.equal(remoteSubMap.absolutePath, subMap.handle.absolutePath, "could not get the handle's path in remote map");
|
|
370
|
+
});
|
|
371
|
+
it("Should be able to set and retrieve a plain object with nested handles", async () => {
|
|
372
|
+
const subMap = createLocalMap("subMap");
|
|
373
|
+
const subMap2 = createLocalMap("subMap2");
|
|
374
|
+
const containingObject = {
|
|
375
|
+
subMapHandle: subMap.handle,
|
|
376
|
+
nestedObj: {
|
|
377
|
+
subMap2Handle: subMap2.handle,
|
|
378
|
+
},
|
|
379
|
+
};
|
|
380
|
+
map1.set("object", containingObject);
|
|
381
|
+
containerRuntimeFactory.processAllMessages();
|
|
382
|
+
const retrieved = map1.get("object");
|
|
383
|
+
const retrievedSubMap = await retrieved.subMapHandle.get();
|
|
384
|
+
assert.equal(retrievedSubMap, subMap, "could not get nested map 1");
|
|
385
|
+
const retrievedSubMap2 = await retrieved.nestedObj.subMap2Handle.get();
|
|
386
|
+
assert.equal(retrievedSubMap2, subMap2, "could not get nested map 2");
|
|
387
|
+
});
|
|
388
|
+
it("Shouldn't clear value remotely if there is pending set", () => {
|
|
389
|
+
const valuesChanged = [];
|
|
390
|
+
let clearCount = 0;
|
|
391
|
+
map1.on("valueChanged", (changed, local, target) => {
|
|
392
|
+
valuesChanged.push(changed);
|
|
393
|
+
});
|
|
394
|
+
map1.on("clear", (local, target) => {
|
|
395
|
+
clearCount++;
|
|
396
|
+
});
|
|
397
|
+
map2.set("map2key", "value2");
|
|
398
|
+
map2.clear();
|
|
399
|
+
map1.set("map1Key", "value1");
|
|
400
|
+
map2.clear();
|
|
401
|
+
containerRuntimeFactory.processSomeMessages(2);
|
|
402
|
+
assert.equal(valuesChanged.length, 3);
|
|
403
|
+
assert.equal(valuesChanged[0].key, "map1Key");
|
|
404
|
+
assert.equal(valuesChanged[0].previousValue, undefined);
|
|
405
|
+
assert.equal(valuesChanged[1].key, "map2key");
|
|
406
|
+
assert.equal(valuesChanged[1].previousValue, undefined);
|
|
407
|
+
assert.equal(valuesChanged[2].key, "map1Key");
|
|
408
|
+
assert.equal(valuesChanged[2].previousValue, undefined);
|
|
409
|
+
assert.equal(clearCount, 1);
|
|
410
|
+
assert.equal(map1.size, 1);
|
|
411
|
+
assert.equal(map1.get("map1Key"), "value1");
|
|
412
|
+
containerRuntimeFactory.processSomeMessages(2);
|
|
413
|
+
assert.equal(valuesChanged.length, 3);
|
|
414
|
+
assert.equal(clearCount, 2);
|
|
415
|
+
assert.equal(map1.size, 0);
|
|
416
|
+
});
|
|
417
|
+
it("Shouldn't keep the old pending set after a local clear", () => {
|
|
418
|
+
map1.set("1", 1);
|
|
419
|
+
map1.set("2", 2);
|
|
420
|
+
map1.set("3", 3);
|
|
421
|
+
map1.clear();
|
|
422
|
+
map1.set("1", 2);
|
|
423
|
+
containerRuntimeFactory.processAllMessages();
|
|
424
|
+
assert.equal(map1.get("1"), 2);
|
|
425
|
+
assert.equal(map1.get("2"), undefined);
|
|
426
|
+
assert.equal(map1.get("3"), undefined);
|
|
427
|
+
assert.equal(map2.get("1"), 2);
|
|
428
|
+
assert.equal(map2.get("2"), undefined);
|
|
429
|
+
assert.equal(map2.get("3"), undefined);
|
|
430
|
+
});
|
|
431
|
+
it("Shouldn't overwrite value if there is pending set", () => {
|
|
432
|
+
const value1 = "value1";
|
|
433
|
+
const pending1 = "pending1";
|
|
434
|
+
const pending2 = "pending2";
|
|
435
|
+
map1.set("test", value1);
|
|
436
|
+
map2.set("test", pending1);
|
|
437
|
+
map2.set("test", pending2);
|
|
438
|
+
containerRuntimeFactory.processSomeMessages(1);
|
|
439
|
+
// Verify the SharedMap with processed message
|
|
440
|
+
assert.equal(map1.has("test"), true, "could not find the set key");
|
|
441
|
+
assert.equal(map1.get("test"), value1, "could not get the set key");
|
|
442
|
+
// Verify the SharedMap with 2 pending messages
|
|
443
|
+
assert.equal(map2.has("test"), true, "could not find the set key in pending map");
|
|
444
|
+
assert.equal(map2.get("test"), pending2, "could not get the set key from pending map");
|
|
445
|
+
containerRuntimeFactory.processSomeMessages(1);
|
|
446
|
+
// Verify the SharedMap gets updated from remote
|
|
447
|
+
assert.equal(map1.has("test"), true, "could not find the set key");
|
|
448
|
+
assert.equal(map1.get("test"), pending1, "could not get the set key");
|
|
449
|
+
// Verify the SharedMap with 1 pending message
|
|
450
|
+
assert.equal(map2.has("test"), true, "could not find the set key in pending map");
|
|
451
|
+
assert.equal(map2.get("test"), pending2, "could not get the set key from pending map");
|
|
452
|
+
});
|
|
453
|
+
it("Shouldn't set values when pending clear", () => {
|
|
454
|
+
const key = "test";
|
|
455
|
+
map1.set(key, "map1value1");
|
|
456
|
+
map2.set(key, "map2value2");
|
|
457
|
+
map2.clear();
|
|
458
|
+
map2.set(key, "map2value3");
|
|
459
|
+
map2.clear();
|
|
460
|
+
// map1.set(key, "map1value1");
|
|
461
|
+
containerRuntimeFactory.processSomeMessages(1);
|
|
462
|
+
// Verify the SharedMap with processed message
|
|
463
|
+
assert.equal(map1.has("test"), true, "could not find the set key");
|
|
464
|
+
assert.equal(map1.get("test"), "map1value1", "could not get the set key");
|
|
465
|
+
// Verify the SharedMap with 2 pending clears
|
|
466
|
+
assert.equal(map2.has("test"), false, "found the set key in pending map");
|
|
467
|
+
// map2.set(key, "map2value2");
|
|
468
|
+
containerRuntimeFactory.processSomeMessages(1);
|
|
469
|
+
// Verify the SharedMap gets updated from remote
|
|
470
|
+
assert.equal(map1.has("test"), true, "could not find the set key");
|
|
471
|
+
assert.equal(map1.get("test"), "map2value2", "could not get the set key");
|
|
472
|
+
// Verify the SharedMap with 2 pending clears
|
|
473
|
+
assert.equal(map2.has("test"), false, "found the set key in pending map");
|
|
474
|
+
// map2.clear();
|
|
475
|
+
containerRuntimeFactory.processSomeMessages(1);
|
|
476
|
+
// Verify the SharedMap gets updated from remote clear
|
|
477
|
+
assert.equal(map1.has("test"), false, "found the set key");
|
|
478
|
+
// Verify the SharedMap with 1 pending clear
|
|
479
|
+
assert.equal(map2.has("test"), false, "found the set key in pending map");
|
|
480
|
+
// map2.set(key, "map2value3");
|
|
481
|
+
containerRuntimeFactory.processSomeMessages(1);
|
|
482
|
+
// Verify the SharedMap gets updated from remote
|
|
483
|
+
assert.equal(map1.has("test"), true, "could not find the set key");
|
|
484
|
+
assert.equal(map1.get("test"), "map2value3", "could not get the set key");
|
|
485
|
+
// Verify the SharedMap with 1 pending clear
|
|
486
|
+
assert.equal(map2.has("test"), false, "found the set key in pending map");
|
|
487
|
+
// map2.clear();
|
|
488
|
+
containerRuntimeFactory.processSomeMessages(1);
|
|
489
|
+
// Verify the SharedMap gets updated from remote clear
|
|
490
|
+
assert.equal(map1.has("test"), false, "found the set key");
|
|
491
|
+
// Verify the SharedMap with no more pending clear
|
|
492
|
+
assert.equal(map2.has("test"), false, "found the set key in pending map");
|
|
493
|
+
map1.set(key, "map1value4");
|
|
494
|
+
containerRuntimeFactory.processSomeMessages(1);
|
|
495
|
+
// Verify the SharedMap gets updated from local
|
|
496
|
+
assert.equal(map1.has("test"), true, "could not find the set key");
|
|
497
|
+
assert.equal(map1.get("test"), "map1value4", "could not get the set key");
|
|
498
|
+
// Verify the SharedMap gets updated from remote
|
|
499
|
+
assert.equal(map1.has("test"), true, "could not find the set key");
|
|
500
|
+
assert.equal(map1.get("test"), "map1value4", "could not get the set key");
|
|
501
|
+
});
|
|
502
|
+
});
|
|
503
|
+
describe(".delete()", () => {
|
|
504
|
+
it("Can set and delete map key", async () => {
|
|
505
|
+
map1.set("testKey", "testValue");
|
|
506
|
+
map1.set("testKey2", "testValue2");
|
|
507
|
+
map1.delete("testKey");
|
|
508
|
+
map1.delete("testKey2");
|
|
509
|
+
assert.equal(map1.has("testKey"), false, "could not delete key 1");
|
|
510
|
+
assert.equal(map1.has("testKey2"), false, "could not delete key 2");
|
|
511
|
+
map1.set("testKey", "testValue");
|
|
512
|
+
map1.set("testKey2", "testValue2");
|
|
513
|
+
assert.equal(map1.get("testKey"), "testValue", "could not retrieve set key 1 after delete");
|
|
514
|
+
assert.equal(map1.get("testKey2"), "testValue2", "could not retrieve set key 2 after delete");
|
|
515
|
+
});
|
|
516
|
+
/**
|
|
517
|
+
* It is an unusual scenario, the client of map1 executes an invalid delete (since "foo" does not exist in its keys),
|
|
518
|
+
* but it can remotely delete the "foo" which is locally inserted in map2 but not ack'd yet.
|
|
519
|
+
*
|
|
520
|
+
* This merge outcome might be undesirable: this test case is mostly here to document Map's behavior.
|
|
521
|
+
* Please communicate any concerns about the merge outcome to the DDS team.
|
|
522
|
+
*/
|
|
523
|
+
it("Can remotely delete a key which should be unknown to the local client", () => {
|
|
524
|
+
map1.set("foo", 1);
|
|
525
|
+
containerRuntimeFactory.processAllMessages();
|
|
526
|
+
map1.delete("foo");
|
|
527
|
+
map2.set("foo", 2);
|
|
528
|
+
map1.delete("foo");
|
|
529
|
+
containerRuntimeFactory.processAllMessages();
|
|
530
|
+
assert.equal(map1.get("foo"), undefined);
|
|
531
|
+
assert.equal(map2.get("foo"), undefined);
|
|
532
|
+
});
|
|
533
|
+
});
|
|
534
|
+
describe(".forEach()", () => {
|
|
535
|
+
it("Should iterate over all keys in the map", () => {
|
|
536
|
+
// We use a set to mark the values we want to insert. When we iterate we will remove from the set
|
|
537
|
+
// and then check it's empty at the end
|
|
538
|
+
const set = new Set();
|
|
539
|
+
set.add("first");
|
|
540
|
+
set.add("second");
|
|
541
|
+
set.add("third");
|
|
542
|
+
for (const value of set) {
|
|
543
|
+
map1.set(value, value);
|
|
544
|
+
}
|
|
545
|
+
containerRuntimeFactory.processAllMessages();
|
|
546
|
+
// Verify the local SharedMap
|
|
547
|
+
for (const [key, value] of map1.entries()) {
|
|
548
|
+
assert.ok(set.has(key), "the key should be present in the set");
|
|
549
|
+
assert.equal(key, value, "the value should match the set value");
|
|
550
|
+
assert.equal(map1.get(key), value, "could not get key");
|
|
551
|
+
}
|
|
552
|
+
// Verify the remote SharedMap
|
|
553
|
+
for (const [key, value] of map2.entries()) {
|
|
554
|
+
assert.ok(set.has(key), "the key in remote map should be present in the set");
|
|
555
|
+
assert.equal(key, value, "the value should match the set value in the remote map");
|
|
556
|
+
assert.equal(map2.get(key), value, "could not get key in the remote map");
|
|
557
|
+
set.delete(key);
|
|
558
|
+
}
|
|
559
|
+
assert.equal(set.size, 0);
|
|
560
|
+
});
|
|
561
|
+
});
|
|
562
|
+
describe(".size", () => {
|
|
563
|
+
it("shouldn't count keys deleted concurrent to a clear op", () => {
|
|
564
|
+
map1.clear();
|
|
565
|
+
map2.delete("dummy");
|
|
566
|
+
containerRuntimeFactory.processAllMessages();
|
|
567
|
+
assert.equal(map1.size, 0);
|
|
568
|
+
assert.equal(map2.size, 0);
|
|
569
|
+
});
|
|
570
|
+
it("should count the key with undefined value concurrent to a clear op", () => {
|
|
571
|
+
map1.clear();
|
|
572
|
+
map2.set("1", undefined);
|
|
573
|
+
containerRuntimeFactory.processSomeMessages(1);
|
|
574
|
+
assert.equal(map1.size, 0);
|
|
575
|
+
assert.equal(map2.size, 1);
|
|
576
|
+
containerRuntimeFactory.processSomeMessages(1);
|
|
577
|
+
assert.equal(map1.size, 1);
|
|
578
|
+
assert.equal(map2.size, 1);
|
|
579
|
+
});
|
|
580
|
+
it("should count keys correctly after local operations", () => {
|
|
581
|
+
map1.set("1", 1);
|
|
582
|
+
map1.set("2", 1);
|
|
583
|
+
map2.set("3", 1);
|
|
584
|
+
assert.equal(map1.size, 2);
|
|
585
|
+
assert.equal(map2.size, 1);
|
|
586
|
+
map1.set("2", 2);
|
|
587
|
+
map1.delete("1");
|
|
588
|
+
map2.set("2", 1);
|
|
589
|
+
assert.equal(map1.size, 1);
|
|
590
|
+
assert.equal(map2.size, 2);
|
|
591
|
+
map1.delete("1");
|
|
592
|
+
map2.clear();
|
|
593
|
+
assert.equal(map1.size, 1);
|
|
594
|
+
assert.equal(map2.size, 0);
|
|
595
|
+
});
|
|
596
|
+
it("should count keys correctly after remote operations", () => {
|
|
597
|
+
map1.set("1", 1);
|
|
598
|
+
map1.set("2", 1);
|
|
599
|
+
map2.set("3", 1);
|
|
600
|
+
containerRuntimeFactory.processSomeMessages(2);
|
|
601
|
+
assert.equal(map1.size, 2);
|
|
602
|
+
assert.equal(map2.size, 3);
|
|
603
|
+
containerRuntimeFactory.processSomeMessages(1);
|
|
604
|
+
assert.equal(map1.size, 3);
|
|
605
|
+
assert.equal(map2.size, 3);
|
|
606
|
+
map1.delete("3");
|
|
607
|
+
map2.clear();
|
|
608
|
+
containerRuntimeFactory.processSomeMessages(1);
|
|
609
|
+
assert.equal(map1.size, 2);
|
|
610
|
+
assert.equal(map2.size, 0);
|
|
611
|
+
containerRuntimeFactory.processSomeMessages(1);
|
|
612
|
+
assert.equal(map1.size, 0);
|
|
613
|
+
assert.equal(map2.size, 0);
|
|
614
|
+
});
|
|
615
|
+
});
|
|
616
|
+
});
|
|
617
|
+
});
|
|
618
|
+
describe("Garbage Collection", () => {
|
|
619
|
+
class GCSharedMapProvider {
|
|
620
|
+
constructor() {
|
|
621
|
+
this.subMapCount = 0;
|
|
622
|
+
this._expectedRoutes = [];
|
|
623
|
+
this.containerRuntimeFactory = new MockContainerRuntimeFactory();
|
|
624
|
+
this.map1 = createConnectedMap("map1", this.containerRuntimeFactory);
|
|
625
|
+
this.map2 = createConnectedMap("map2", this.containerRuntimeFactory);
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* {@inheritDoc @fluid-private/test-dds-utils#IGCTestProvider.sharedObject}
|
|
629
|
+
*/
|
|
630
|
+
get sharedObject() {
|
|
631
|
+
// Return the remote SharedMap because we want to verify its summary data.
|
|
632
|
+
return this.map2;
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* {@inheritDoc @fluid-private/test-dds-utils#IGCTestProvider.expectedOutboundRoutes}
|
|
636
|
+
*/
|
|
637
|
+
get expectedOutboundRoutes() {
|
|
638
|
+
return this._expectedRoutes;
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* {@inheritDoc @fluid-private/test-dds-utils#IGCTestProvider.addOutboundRoutes}
|
|
642
|
+
*/
|
|
643
|
+
async addOutboundRoutes() {
|
|
644
|
+
const newSubMapId = `subMap-${++this.subMapCount}`;
|
|
645
|
+
const subMap = createLocalMap(newSubMapId);
|
|
646
|
+
this.map1.set(newSubMapId, subMap.handle);
|
|
647
|
+
this._expectedRoutes.push(subMap.handle.absolutePath);
|
|
648
|
+
this.containerRuntimeFactory.processAllMessages();
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* {@inheritDoc @fluid-private/test-dds-utils#IGCTestProvider.deleteOutboundRoutes}
|
|
652
|
+
*/
|
|
653
|
+
async deleteOutboundRoutes() {
|
|
654
|
+
// Delete the last handle that was added.
|
|
655
|
+
const subMapId = `subMap-${this.subMapCount}`;
|
|
656
|
+
const deletedHandle = this.map1.get(subMapId);
|
|
657
|
+
assert(deletedHandle, "Route must be added before deleting");
|
|
658
|
+
this.map1.delete(subMapId);
|
|
659
|
+
// Remove deleted handle's route from expected routes.
|
|
660
|
+
this._expectedRoutes = this._expectedRoutes.filter((route) => route !== deletedHandle.absolutePath);
|
|
661
|
+
this.containerRuntimeFactory.processAllMessages();
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* {@inheritDoc @fluid-private/test-dds-utils#IGCTestProvider.addNestedHandles}
|
|
665
|
+
*/
|
|
666
|
+
async addNestedHandles() {
|
|
667
|
+
const subMapId1 = `subMap-${++this.subMapCount}`;
|
|
668
|
+
const subMapId2 = `subMap-${++this.subMapCount}`;
|
|
669
|
+
const subMap = createLocalMap(subMapId1);
|
|
670
|
+
const subMap2 = createLocalMap(subMapId2);
|
|
671
|
+
const containingObject = {
|
|
672
|
+
subMapHandle: subMap.handle,
|
|
673
|
+
nestedObj: {
|
|
674
|
+
subMap2Handle: subMap2.handle,
|
|
675
|
+
},
|
|
676
|
+
};
|
|
677
|
+
this.map1.set(subMapId2, containingObject);
|
|
678
|
+
this._expectedRoutes.push(subMap.handle.absolutePath, subMap2.handle.absolutePath);
|
|
679
|
+
this.containerRuntimeFactory.processAllMessages();
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
runGCTests(GCSharedMapProvider);
|
|
683
|
+
});
|
|
684
|
+
});
|
|
685
|
+
//# sourceMappingURL=map.spec.js.map
|