@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,1406 @@
|
|
|
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 { SummaryType } from "@fluidframework/protocol-definitions";
|
|
7
|
+
import { runGCTests } from "@fluid-private/test-dds-utils";
|
|
8
|
+
import { MockFluidDataStoreRuntime, MockContainerRuntimeFactory, MockSharedObjectServices, MockStorage, } from "@fluidframework/test-runtime-utils";
|
|
9
|
+
import { AttachState } from "@fluidframework/container-definitions";
|
|
10
|
+
import { MapFactory } from "../../map.js";
|
|
11
|
+
import { DirectoryFactory, SharedDirectory } from "../../directory.js";
|
|
12
|
+
import { assertEquivalentDirectories } from "./directoryEquivalenceUtils.js";
|
|
13
|
+
function createConnectedDirectory(id, runtimeFactory) {
|
|
14
|
+
const dataStoreRuntime = new MockFluidDataStoreRuntime();
|
|
15
|
+
const containerRuntime = runtimeFactory.createContainerRuntime(dataStoreRuntime);
|
|
16
|
+
const services = {
|
|
17
|
+
deltaConnection: dataStoreRuntime.createDeltaConnection(),
|
|
18
|
+
objectStorage: new MockStorage(),
|
|
19
|
+
};
|
|
20
|
+
const directory = new SharedDirectory(id, dataStoreRuntime, DirectoryFactory.Attributes);
|
|
21
|
+
directory.connect(services);
|
|
22
|
+
return directory;
|
|
23
|
+
}
|
|
24
|
+
function createLocalMap(id) {
|
|
25
|
+
const factory = new MapFactory();
|
|
26
|
+
return factory.create(new MockFluidDataStoreRuntime(), id);
|
|
27
|
+
}
|
|
28
|
+
async function populate(directory, content) {
|
|
29
|
+
const storage = new MockSharedObjectServices({
|
|
30
|
+
header: JSON.stringify(content),
|
|
31
|
+
});
|
|
32
|
+
return directory.load(storage);
|
|
33
|
+
}
|
|
34
|
+
function serialize(directory1) {
|
|
35
|
+
const summaryTree = directory1.getAttachSummary().summary;
|
|
36
|
+
const summaryObjectKeys = Object.keys(summaryTree.tree);
|
|
37
|
+
assert.strictEqual(summaryObjectKeys.length, 1, "summary tree should only have one blob");
|
|
38
|
+
assert.strictEqual(summaryObjectKeys[0], "header", "summary should have a header blob");
|
|
39
|
+
assert.strictEqual(summaryTree.tree.header.type, SummaryType.Blob, "header is not of SummaryType.Blob");
|
|
40
|
+
const content = summaryTree.tree.header.content;
|
|
41
|
+
return JSON.stringify(JSON.parse(content).content);
|
|
42
|
+
}
|
|
43
|
+
describe("Directory", () => {
|
|
44
|
+
describe("Local state", () => {
|
|
45
|
+
let directory;
|
|
46
|
+
let dataStoreRuntime;
|
|
47
|
+
beforeEach("createDirectory", async () => {
|
|
48
|
+
dataStoreRuntime = new MockFluidDataStoreRuntime({ attachState: AttachState.Detached });
|
|
49
|
+
directory = new SharedDirectory("directory", dataStoreRuntime, DirectoryFactory.Attributes);
|
|
50
|
+
});
|
|
51
|
+
describe("API", () => {
|
|
52
|
+
it("Can create a new directory", () => {
|
|
53
|
+
assert.ok(directory, "could not create a new directory");
|
|
54
|
+
});
|
|
55
|
+
it("Knows its absolute path", () => {
|
|
56
|
+
assert.equal(directory.absolutePath, "/", "the absolute path is not correct");
|
|
57
|
+
});
|
|
58
|
+
it("Can set and get keys one level deep", () => {
|
|
59
|
+
directory.set("testKey", "testValue");
|
|
60
|
+
directory.set("testKey2", "testValue2");
|
|
61
|
+
assert.equal(directory.get("testKey"), "testValue", "could not retrieve set key 1");
|
|
62
|
+
assert.equal(directory.get("testKey2"), "testValue2", "could not retrieve set key 2");
|
|
63
|
+
});
|
|
64
|
+
it("should fire correct directory events", async () => {
|
|
65
|
+
let valueChangedExpected = true;
|
|
66
|
+
let containedValueChangedExpected = true;
|
|
67
|
+
let clearExpected = false;
|
|
68
|
+
let previousValue;
|
|
69
|
+
let directoryCreationExpected = true;
|
|
70
|
+
let directoryDeletedExpected = true;
|
|
71
|
+
directory.on("op", (arg1, arg2, arg3) => {
|
|
72
|
+
assert.fail("shouldn't receive an op event");
|
|
73
|
+
});
|
|
74
|
+
directory.on("valueChanged", (changed, local, target) => {
|
|
75
|
+
assert.equal(valueChangedExpected, true, "valueChange event not expected");
|
|
76
|
+
valueChangedExpected = false;
|
|
77
|
+
assert.equal(changed.key, "dwayne");
|
|
78
|
+
assert.equal(changed.previousValue, previousValue);
|
|
79
|
+
assert.equal(changed.path, directory.absolutePath);
|
|
80
|
+
assert.equal(local, true, "local should be true for local action for valueChanged event");
|
|
81
|
+
assert.equal(target, directory, "target should be the directory for valueChanged event");
|
|
82
|
+
});
|
|
83
|
+
directory.on("subDirectoryCreated", (path, local, target) => {
|
|
84
|
+
assert.equal(directoryCreationExpected, true, "subDirectoryCreated event not expected");
|
|
85
|
+
directoryCreationExpected = false;
|
|
86
|
+
assert.equal(path, "rock");
|
|
87
|
+
assert.equal(local, true, "local should be true for local action for subDirectoryCreated event");
|
|
88
|
+
assert.equal(target, directory, "target should be the directory for subDirectoryCreated event");
|
|
89
|
+
});
|
|
90
|
+
directory.on("subDirectoryDeleted", (path, local, target) => {
|
|
91
|
+
assert.equal(directoryDeletedExpected, true, "subDirectoryDeleted event not expected");
|
|
92
|
+
directoryDeletedExpected = false;
|
|
93
|
+
assert.equal(path, "rock");
|
|
94
|
+
assert.equal(local, true, "local should be true for local action for subDirectoryDeleted event");
|
|
95
|
+
assert.equal(target, directory, "target should be the directory for subDirectoryDeleted event");
|
|
96
|
+
});
|
|
97
|
+
directory.on("containedValueChanged", (changed, local, target) => {
|
|
98
|
+
assert.equal(containedValueChangedExpected, true, "containedValueChanged event not expected for containedValueChanged event");
|
|
99
|
+
containedValueChangedExpected = false;
|
|
100
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
101
|
+
assert.equal(changed.key, "dwayne");
|
|
102
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
103
|
+
assert.equal(changed.previousValue, previousValue);
|
|
104
|
+
assert.equal(local, true, "local should be true for local action for containedValueChanged event");
|
|
105
|
+
assert.equal(target, directory, "target should be the directory for containedValueChanged event");
|
|
106
|
+
});
|
|
107
|
+
directory.on("clear", (local, target) => {
|
|
108
|
+
assert.equal(clearExpected, true, "clear event not expected");
|
|
109
|
+
clearExpected = false;
|
|
110
|
+
assert.equal(local, true, "local should be true for local action for clear event");
|
|
111
|
+
assert.equal(target, directory, "target should be the directory for clear event");
|
|
112
|
+
});
|
|
113
|
+
directory.on("error", (error) => {
|
|
114
|
+
// propagate error in the event handlers
|
|
115
|
+
throw error;
|
|
116
|
+
});
|
|
117
|
+
// Test set
|
|
118
|
+
previousValue = undefined;
|
|
119
|
+
directory.set("dwayne", "johnson");
|
|
120
|
+
assert.equal(valueChangedExpected, false, "missing valueChangedExpected event");
|
|
121
|
+
assert.equal(containedValueChangedExpected, false, "missing containedValueChanged event");
|
|
122
|
+
// Test delete
|
|
123
|
+
previousValue = "johnson";
|
|
124
|
+
valueChangedExpected = true;
|
|
125
|
+
containedValueChangedExpected = true;
|
|
126
|
+
directory.delete("dwayne");
|
|
127
|
+
assert.equal(valueChangedExpected, false, "missing valueChangedExpected event");
|
|
128
|
+
assert.equal(containedValueChangedExpected, false, "missing containedValueChanged event");
|
|
129
|
+
// Test createSubDirectory
|
|
130
|
+
directory.createSubDirectory("rock");
|
|
131
|
+
assert.equal(directoryCreationExpected, false, "missing subDirectoryCreated event");
|
|
132
|
+
// Test deleteSubDirectory
|
|
133
|
+
previousValue = directory.getSubDirectory("rock");
|
|
134
|
+
directory.deleteSubDirectory("rock");
|
|
135
|
+
assert.equal(valueChangedExpected, false, "missing valueChangedExpected event");
|
|
136
|
+
assert.equal(containedValueChangedExpected, false, "missing containedValueChanged event");
|
|
137
|
+
// Test clear
|
|
138
|
+
clearExpected = true;
|
|
139
|
+
directory.clear();
|
|
140
|
+
assert.equal(clearExpected, false, "missing clearExpected event");
|
|
141
|
+
});
|
|
142
|
+
it("should fire create/delete sub directory events", async () => {
|
|
143
|
+
const subDirectory = directory.createSubDirectory("rock");
|
|
144
|
+
const subDirectory1 = subDirectory.createSubDirectory("rockChild");
|
|
145
|
+
// Check Creation events
|
|
146
|
+
let directoryCreationExpected1 = false;
|
|
147
|
+
let directoryCreationExpected2 = false;
|
|
148
|
+
subDirectory.on("subDirectoryCreated", (relativePath, local, target) => {
|
|
149
|
+
directoryCreationExpected1 = true;
|
|
150
|
+
assert.equal(relativePath, "rockChild/rockChildChild", "Path should match");
|
|
151
|
+
});
|
|
152
|
+
subDirectory1.on("subDirectoryCreated", (relativePath, local, target) => {
|
|
153
|
+
directoryCreationExpected2 = true;
|
|
154
|
+
assert.equal(relativePath, "rockChildChild", "Path should match");
|
|
155
|
+
});
|
|
156
|
+
subDirectory1.createSubDirectory("rockChildChild");
|
|
157
|
+
assert(directoryCreationExpected1, "Create event should fire");
|
|
158
|
+
assert(directoryCreationExpected2, "Create event should fire");
|
|
159
|
+
// Check Deletion Events
|
|
160
|
+
let directoryDeletionExpected = false;
|
|
161
|
+
let directoryDeletionExpected1 = false;
|
|
162
|
+
let directoryDeletionExpected2 = false;
|
|
163
|
+
directory.on("subDirectoryDeleted", (relativePath, local, target) => {
|
|
164
|
+
directoryDeletionExpected = true;
|
|
165
|
+
assert.equal(relativePath, "rock/rockChild/rockChildChild", "Path should match");
|
|
166
|
+
});
|
|
167
|
+
subDirectory.on("subDirectoryDeleted", (relativePath, local, target) => {
|
|
168
|
+
directoryDeletionExpected1 = true;
|
|
169
|
+
assert.equal(relativePath, "rockChild/rockChildChild", "Path should match");
|
|
170
|
+
});
|
|
171
|
+
subDirectory1.on("subDirectoryDeleted", (relativePath, local, target) => {
|
|
172
|
+
directoryDeletionExpected2 = true;
|
|
173
|
+
assert.equal(relativePath, "rockChildChild", "Path should match");
|
|
174
|
+
});
|
|
175
|
+
subDirectory1.deleteSubDirectory("rockChildChild");
|
|
176
|
+
assert(directoryDeletionExpected, "Delete event should fire on root");
|
|
177
|
+
assert(directoryDeletionExpected1, "Delete event should fire on child1");
|
|
178
|
+
assert(directoryDeletionExpected2, "Delete event should fire on child2");
|
|
179
|
+
subDirectory1.deleteSubDirectory("rockChildChild");
|
|
180
|
+
});
|
|
181
|
+
it("Should fire dispose event correctly", () => {
|
|
182
|
+
let valueChangedExpected = true;
|
|
183
|
+
directory.on("valueChanged", (changed, local, target) => {
|
|
184
|
+
assert.equal(valueChangedExpected, true, "valueChange event not expected");
|
|
185
|
+
valueChangedExpected = false;
|
|
186
|
+
assert.equal(changed.key, "dwayne", "key should match");
|
|
187
|
+
assert.equal(changed.previousValue, undefined, "previous value should match");
|
|
188
|
+
assert.equal(changed.path, "/rock", "absolute path should match");
|
|
189
|
+
assert.equal(local, true, "local should be true for local action for valueChanged event");
|
|
190
|
+
assert.equal(target, directory, "target should be the directory for valueChanged event");
|
|
191
|
+
});
|
|
192
|
+
// Test dispose on subDirectory delete
|
|
193
|
+
let subDirectoryDisposed = false;
|
|
194
|
+
const subDirectory = directory.createSubDirectory("rock");
|
|
195
|
+
subDirectory.on("disposed", (value) => {
|
|
196
|
+
subDirectoryDisposed = true;
|
|
197
|
+
assert.equal(value.disposed, true, "sub directory not deleted");
|
|
198
|
+
});
|
|
199
|
+
// Should fire dispose event.
|
|
200
|
+
directory.deleteSubDirectory("rock");
|
|
201
|
+
assert.equal(subDirectoryDisposed, true, "sub directory not disposed!!");
|
|
202
|
+
// Should be able to work on new directory with same name.
|
|
203
|
+
valueChangedExpected = true;
|
|
204
|
+
const newSubDirectory = directory.createSubDirectory("rock");
|
|
205
|
+
newSubDirectory.set("dwayne", "johnson");
|
|
206
|
+
assert.equal(valueChangedExpected, false, "missing valueChangedExpected event");
|
|
207
|
+
// Usage Error on accessing disposed directory.
|
|
208
|
+
try {
|
|
209
|
+
subDirectory.set("throw", "error");
|
|
210
|
+
assert.fail("Should throw usage error");
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
assert.strictEqual(error?.errorType, "usageError", "Should throw usage error");
|
|
214
|
+
}
|
|
215
|
+
// Check recursive dispose event firing
|
|
216
|
+
const subSubDirectory = newSubDirectory.createSubDirectory("rockChild");
|
|
217
|
+
let rockSubDirectoryDisposed = false;
|
|
218
|
+
let subSubDirectoryDisposed = false;
|
|
219
|
+
newSubDirectory.on("disposed", (value) => {
|
|
220
|
+
rockSubDirectoryDisposed = true;
|
|
221
|
+
assert.equal(value.disposed, true, "rock sub directory not deleted");
|
|
222
|
+
});
|
|
223
|
+
subSubDirectory.on("disposed", (value) => {
|
|
224
|
+
subSubDirectoryDisposed = true;
|
|
225
|
+
assert.equal(value.disposed, true, "sub sub directory not deleted");
|
|
226
|
+
});
|
|
227
|
+
directory.deleteSubDirectory("rock");
|
|
228
|
+
assert(rockSubDirectoryDisposed, "Rock sub directory should be disposed");
|
|
229
|
+
assert(subSubDirectoryDisposed, "sub sub directory should be disposed");
|
|
230
|
+
});
|
|
231
|
+
it("Check number of sub directories", () => {
|
|
232
|
+
const subDirectory = directory.createSubDirectory("rock1");
|
|
233
|
+
directory.createSubDirectory("rock2");
|
|
234
|
+
const childSubDirectory = subDirectory.createSubDirectory("rock1Child");
|
|
235
|
+
assert.strictEqual(directory.countSubDirectory(), 2, "Should have 2 sub directories");
|
|
236
|
+
assert(subDirectory.countSubDirectory !== undefined &&
|
|
237
|
+
subDirectory.countSubDirectory() === 1, "Should have 1 sub directory");
|
|
238
|
+
assert(childSubDirectory.countSubDirectory !== undefined &&
|
|
239
|
+
subDirectory.countSubDirectory() === 1, "Should have 0 sub directory");
|
|
240
|
+
});
|
|
241
|
+
it("Rejects a undefined and null key set", () => {
|
|
242
|
+
assert.throws(() => {
|
|
243
|
+
directory.set(undefined, "testValue");
|
|
244
|
+
}, "Should throw for key of undefined");
|
|
245
|
+
assert.throws(() => {
|
|
246
|
+
// eslint-disable-next-line unicorn/no-null
|
|
247
|
+
directory.set(null, "testValue");
|
|
248
|
+
}, "Should throw for key of null");
|
|
249
|
+
});
|
|
250
|
+
it("Rejects subdirectories with undefined and null names", () => {
|
|
251
|
+
assert.throws(() => {
|
|
252
|
+
directory.createSubDirectory(undefined);
|
|
253
|
+
}, "Should throw for undefined subDirectory name");
|
|
254
|
+
assert.throws(() => {
|
|
255
|
+
// eslint-disable-next-line unicorn/no-null
|
|
256
|
+
directory.createSubDirectory(null);
|
|
257
|
+
}, "Should throw for null subDirectory name");
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
describe("Serialize", () => {
|
|
261
|
+
it("Should serialize an empty directory as a JSON object", () => {
|
|
262
|
+
const serialized = serialize(directory);
|
|
263
|
+
assert.equal(serialized, '{"ci":{"csn":0,"ccIds":[]}}');
|
|
264
|
+
});
|
|
265
|
+
it("Should serialize a directory without subdirectories as a JSON object", () => {
|
|
266
|
+
directory.set("first", "second");
|
|
267
|
+
directory.set("third", "fourth");
|
|
268
|
+
directory.set("fifth", "sixth");
|
|
269
|
+
const subMap = createLocalMap("subMap");
|
|
270
|
+
directory.set("object", subMap.handle);
|
|
271
|
+
const subMapHandleUrl = subMap.handle.absolutePath;
|
|
272
|
+
const serialized = serialize(directory);
|
|
273
|
+
const expected = `{"ci":{"csn":0,"ccIds":[]},"storage":{"first":{"type":"Plain","value":"second"},"third":{"type":"Plain","value":"fourth"},"fifth":{"type":"Plain","value":"sixth"},"object":{"type":"Plain","value":{"type":"__fluid_handle__","url":"${subMapHandleUrl}"}}}}`;
|
|
274
|
+
assert.equal(serialized, expected);
|
|
275
|
+
});
|
|
276
|
+
it("Should serialize a directory with subdirectories as a JSON object", () => {
|
|
277
|
+
directory.set("first", "second");
|
|
278
|
+
directory.set("third", "fourth");
|
|
279
|
+
directory.set("fifth", "sixth");
|
|
280
|
+
const subMap = createLocalMap("subMap");
|
|
281
|
+
directory.set("object", subMap.handle);
|
|
282
|
+
const nestedDirectory = directory.createSubDirectory("nested");
|
|
283
|
+
nestedDirectory.set("deepKey1", "deepValue1");
|
|
284
|
+
nestedDirectory
|
|
285
|
+
.createSubDirectory("nested2")
|
|
286
|
+
.createSubDirectory("nested3")
|
|
287
|
+
.set("deepKey2", "deepValue2");
|
|
288
|
+
const subMapHandleUrl = subMap.handle.absolutePath;
|
|
289
|
+
const serialized = serialize(directory);
|
|
290
|
+
const expected = `{"ci":{"csn":0,"ccIds":[]},"storage":{"first":{"type":"Plain","value":"second"},"third":{"type":"Plain","value":"fourth"},"fifth":{"type":"Plain","value":"sixth"},"object":{"type":"Plain","value":{"type":"__fluid_handle__","url":"${subMapHandleUrl}"}}},"subdirectories":{"nested":{"ci":{"csn":0,"ccIds":["${dataStoreRuntime.clientId}"]},"storage":{"deepKey1":{"type":"Plain","value":"deepValue1"}},"subdirectories":{"nested2":{"ci":{"csn":0,"ccIds":["${dataStoreRuntime.clientId}"]},"subdirectories":{"nested3":{"ci":{"csn":0,"ccIds":["${dataStoreRuntime.clientId}"]},"storage":{"deepKey2":{"type":"Plain","value":"deepValue2"}}}}}}}}}`;
|
|
291
|
+
assert.equal(serialized, expected);
|
|
292
|
+
});
|
|
293
|
+
it("Should serialize an undefined value", () => {
|
|
294
|
+
directory.set("first", "second");
|
|
295
|
+
directory.set("third", "fourth");
|
|
296
|
+
directory.set("fifth", undefined);
|
|
297
|
+
assert.ok(directory.has("fifth"));
|
|
298
|
+
const subMap = createLocalMap("subMap");
|
|
299
|
+
directory.set("object", subMap.handle);
|
|
300
|
+
const nestedDirectory = directory.createSubDirectory("nested");
|
|
301
|
+
nestedDirectory.set("deepKey1", "deepValue1");
|
|
302
|
+
nestedDirectory.set("deepKeyUndefined", undefined);
|
|
303
|
+
assert.ok(nestedDirectory.has("deepKeyUndefined"));
|
|
304
|
+
nestedDirectory
|
|
305
|
+
.createSubDirectory("nested2")
|
|
306
|
+
.createSubDirectory("nested3")
|
|
307
|
+
.set("deepKey2", "deepValue2");
|
|
308
|
+
const subMapHandleUrl = subMap.handle.absolutePath;
|
|
309
|
+
const serialized = serialize(directory);
|
|
310
|
+
const expected = `{"ci":{"csn":0,"ccIds":[]},"storage":{"first":{"type":"Plain","value":"second"},"third":{"type":"Plain","value":"fourth"},"fifth":{"type":"Plain"},"object":{"type":"Plain","value":{"type":"__fluid_handle__","url":"${subMapHandleUrl}"}}},"subdirectories":{"nested":{"ci":{"csn":0,"ccIds":["${dataStoreRuntime.clientId}"]},"storage":{"deepKey1":{"type":"Plain","value":"deepValue1"},"deepKeyUndefined":{"type":"Plain"}},"subdirectories":{"nested2":{"ci":{"csn":0,"ccIds":["${dataStoreRuntime.clientId}"]},"subdirectories":{"nested3":{"ci":{"csn":0,"ccIds":["${dataStoreRuntime.clientId}"]},"storage":{"deepKey2":{"type":"Plain","value":"deepValue2"}}}}}}}}}`;
|
|
311
|
+
assert.equal(serialized, expected);
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
describe("Populate", () => {
|
|
315
|
+
it("Should populate the directory from an empty JSON object (old format)", async () => {
|
|
316
|
+
await populate(directory, {});
|
|
317
|
+
assert.equal(directory.size, 0, "Failed to initialize to empty directory storage");
|
|
318
|
+
directory.set("testKey", "testValue");
|
|
319
|
+
assert.equal(directory.get("testKey"), "testValue", "Failed to set testKey");
|
|
320
|
+
directory.createSubDirectory("testSubDir").set("testSubKey", "testSubValue");
|
|
321
|
+
const subDir = directory.getWorkingDirectory("testSubDir");
|
|
322
|
+
assert(subDir);
|
|
323
|
+
assert.equal(subDir.get("testSubKey"), "testSubValue", "Failed to set testSubKey");
|
|
324
|
+
});
|
|
325
|
+
it("Should populate the directory from a basic JSON object (old format)", async () => {
|
|
326
|
+
await populate(directory, {
|
|
327
|
+
storage: {
|
|
328
|
+
testKey: {
|
|
329
|
+
type: "Plain",
|
|
330
|
+
value: "testValue4",
|
|
331
|
+
},
|
|
332
|
+
testKey2: {
|
|
333
|
+
type: "Plain",
|
|
334
|
+
value: "testValue5",
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
subdirectories: {
|
|
338
|
+
foo: {
|
|
339
|
+
storage: {
|
|
340
|
+
testKey: {
|
|
341
|
+
type: "Plain",
|
|
342
|
+
value: "testValue",
|
|
343
|
+
},
|
|
344
|
+
testKey2: {
|
|
345
|
+
type: "Plain",
|
|
346
|
+
value: "testValue2",
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
bar: {
|
|
351
|
+
storage: {
|
|
352
|
+
testKey3: {
|
|
353
|
+
type: "Plain",
|
|
354
|
+
value: "testValue3",
|
|
355
|
+
},
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
});
|
|
360
|
+
assert.equal(directory.size, 2, "Failed to initialize directory storage correctly");
|
|
361
|
+
assert.equal(directory.getWorkingDirectory("/foo")?.get("testKey"), "testValue");
|
|
362
|
+
assert.equal(directory.getWorkingDirectory("foo")?.get("testKey2"), "testValue2");
|
|
363
|
+
assert.equal(directory.getWorkingDirectory("/bar")?.get("testKey3"), "testValue3");
|
|
364
|
+
assert.equal(directory.getWorkingDirectory("")?.get("testKey"), "testValue4");
|
|
365
|
+
assert.equal(directory.getWorkingDirectory("/")?.get("testKey2"), "testValue5");
|
|
366
|
+
directory.set("testKey", "newValue");
|
|
367
|
+
assert.equal(directory.get("testKey"), "newValue", "Failed to set testKey");
|
|
368
|
+
directory.createSubDirectory("testSubDir").set("testSubKey", "newSubValue");
|
|
369
|
+
assert.equal(directory.getWorkingDirectory("testSubDir")?.get("testSubKey"), "newSubValue", "Failed to set testSubKey");
|
|
370
|
+
});
|
|
371
|
+
it("Should populate the directory with undefined values (old format)", async () => {
|
|
372
|
+
await populate(directory, {
|
|
373
|
+
storage: {
|
|
374
|
+
testKey: {
|
|
375
|
+
type: "Plain",
|
|
376
|
+
value: "testValue4",
|
|
377
|
+
},
|
|
378
|
+
testKey2: {
|
|
379
|
+
type: "Plain",
|
|
380
|
+
},
|
|
381
|
+
},
|
|
382
|
+
subdirectories: {
|
|
383
|
+
foo: {
|
|
384
|
+
storage: {
|
|
385
|
+
testKey: {
|
|
386
|
+
type: "Plain",
|
|
387
|
+
value: "testValue",
|
|
388
|
+
},
|
|
389
|
+
testKey2: {
|
|
390
|
+
type: "Plain",
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
},
|
|
394
|
+
bar: {
|
|
395
|
+
storage: {
|
|
396
|
+
testKey3: {
|
|
397
|
+
type: "Plain",
|
|
398
|
+
value: "testValue3",
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
});
|
|
404
|
+
assert.equal(directory.size, 2, "Failed to initialize directory storage correctly");
|
|
405
|
+
assert.equal(directory.getWorkingDirectory("/foo")?.get("testKey"), "testValue");
|
|
406
|
+
assert.equal(directory.getWorkingDirectory("foo")?.get("testKey2"), undefined);
|
|
407
|
+
assert.equal(directory.getWorkingDirectory("/bar")?.get("testKey3"), "testValue3");
|
|
408
|
+
assert.equal(directory.getWorkingDirectory("")?.get("testKey"), "testValue4");
|
|
409
|
+
assert.equal(directory.getWorkingDirectory("/")?.get("testKey2"), undefined);
|
|
410
|
+
assert.ok(directory.has("testKey2"));
|
|
411
|
+
assert.ok(directory.getWorkingDirectory("/foo")?.has("testKey2"));
|
|
412
|
+
directory.set("testKey", "newValue");
|
|
413
|
+
assert.equal(directory.get("testKey"), "newValue", "Failed to set testKey");
|
|
414
|
+
directory.createSubDirectory("testSubDir").set("testSubKey", "newSubValue");
|
|
415
|
+
assert.equal(directory.getWorkingDirectory("testSubDir")?.get("testSubKey"), "newSubValue", "Failed to set testSubKey");
|
|
416
|
+
});
|
|
417
|
+
it("Should populate, serialize and de-serialize directory with long property values", async () => {
|
|
418
|
+
// 40K word
|
|
419
|
+
let longWord = "0123456789";
|
|
420
|
+
for (let i = 0; i < 12; i++) {
|
|
421
|
+
longWord = longWord + longWord;
|
|
422
|
+
}
|
|
423
|
+
const logWord2 = `${longWord}_2`;
|
|
424
|
+
directory.set("first", "second");
|
|
425
|
+
directory.set("long1", longWord);
|
|
426
|
+
const nestedDirectory = directory.createSubDirectory("nested");
|
|
427
|
+
nestedDirectory.set("deepKey1", "deepValue1");
|
|
428
|
+
nestedDirectory.set("long2", logWord2);
|
|
429
|
+
const summarizeResult = directory.getAttachSummary();
|
|
430
|
+
const summaryTree = summarizeResult.summary;
|
|
431
|
+
assert.strictEqual(summaryTree.type, SummaryType.Tree, "summary should be a tree");
|
|
432
|
+
assert.strictEqual(Object.keys(summaryTree.tree).length, 3, "number of blobs in summary is incorrect");
|
|
433
|
+
const blob0 = summaryTree.tree.blob0;
|
|
434
|
+
assert(blob0 !== undefined, "blob0 not present in summary");
|
|
435
|
+
assert.strictEqual(blob0.type, SummaryType.Blob, "blob0 is not of SummaryType.Blob");
|
|
436
|
+
assert(blob0.content.length >= 1024, "blob0's length is incorrect");
|
|
437
|
+
const blob1 = summaryTree.tree.blob1;
|
|
438
|
+
assert(blob1 !== undefined, "blob1 not present in summary");
|
|
439
|
+
assert.strictEqual(blob1.type, SummaryType.Blob, "blob1 is not of SummaryType.Blob");
|
|
440
|
+
assert(blob1.content.length >= 1024, "blob1's length is incorrect");
|
|
441
|
+
const header = summaryTree.tree.header;
|
|
442
|
+
assert(header !== undefined, "header not present in summary");
|
|
443
|
+
assert.strictEqual(header.type, SummaryType.Blob, "header is not of SummaryType.Blob");
|
|
444
|
+
assert(header.content.length >= 200, "header's length is incorrect");
|
|
445
|
+
const directory2 = new SharedDirectory("test", dataStoreRuntime, DirectoryFactory.Attributes);
|
|
446
|
+
const storage = MockSharedObjectServices.createFromSummary(summarizeResult.summary);
|
|
447
|
+
await directory2.load(storage);
|
|
448
|
+
assert.equal(directory2.get("first"), "second");
|
|
449
|
+
assert.equal(directory2.get("long1"), longWord);
|
|
450
|
+
const nestedSubDir = directory2.getWorkingDirectory("/nested");
|
|
451
|
+
assert(nestedSubDir);
|
|
452
|
+
assert.equal(nestedSubDir.get("deepKey1"), "deepValue1");
|
|
453
|
+
assert.equal(nestedSubDir.get("long2"), logWord2);
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
describe("Op processing", () => {
|
|
457
|
+
it("Should lead to eventual consistency 1", async () => {
|
|
458
|
+
// Load a new SharedDirectory in connected state from the summarize of the first one.
|
|
459
|
+
const containerRuntimeFactory = new MockContainerRuntimeFactory();
|
|
460
|
+
const dataStoreRuntime2 = new MockFluidDataStoreRuntime();
|
|
461
|
+
const containerRuntime2 = containerRuntimeFactory.createContainerRuntime(dataStoreRuntime2);
|
|
462
|
+
const services2 = MockSharedObjectServices.createFromSummary(directory.getAttachSummary().summary);
|
|
463
|
+
services2.deltaConnection = dataStoreRuntime2.createDeltaConnection();
|
|
464
|
+
const directory2 = new SharedDirectory("directory2", dataStoreRuntime2, DirectoryFactory.Attributes);
|
|
465
|
+
await directory2.load(services2);
|
|
466
|
+
// Now connect the first SharedDirectory
|
|
467
|
+
dataStoreRuntime.setAttachState(AttachState.Attached);
|
|
468
|
+
containerRuntimeFactory.createContainerRuntime(dataStoreRuntime);
|
|
469
|
+
const services1 = {
|
|
470
|
+
deltaConnection: dataStoreRuntime.createDeltaConnection(),
|
|
471
|
+
objectStorage: new MockStorage(undefined),
|
|
472
|
+
};
|
|
473
|
+
directory.connect(services1);
|
|
474
|
+
// Create a sub directory in a sub directory and queue up keys to be set in it
|
|
475
|
+
const someParentDir1 = directory.createSubDirectory("lists");
|
|
476
|
+
const subDir1 = someParentDir1.createSubDirectory("ListLevels-0");
|
|
477
|
+
subDir1.set("random1", 1);
|
|
478
|
+
subDir1.set("random2", 2);
|
|
479
|
+
// Let everything get stamped and round-trip back to us
|
|
480
|
+
containerRuntimeFactory.processAllMessages();
|
|
481
|
+
// Now, let's be tricky. Let's set one of the keys again...
|
|
482
|
+
const dir1 = directory.getSubDirectory("lists");
|
|
483
|
+
const subDirDir11 = dir1?.getSubDirectory("ListLevels-0");
|
|
484
|
+
subDirDir11?.set("random1", 3);
|
|
485
|
+
// ... then delete the sub directory and its parent, create a new sub directory
|
|
486
|
+
// and parent with the exact same paths. We need to set at least one of the same
|
|
487
|
+
// keys as we had unacked in the old sub directory instance. In this case, we only
|
|
488
|
+
// need to set one of the keys.
|
|
489
|
+
directory.getSubDirectory("lists")?.deleteSubDirectory("ListLevels-0");
|
|
490
|
+
directory.deleteSubDirectory("lists");
|
|
491
|
+
const someParentDir2 = directory.createSubDirectory("lists");
|
|
492
|
+
const subDir2 = someParentDir2.createSubDirectory("ListLevels-0");
|
|
493
|
+
subDir2.set("random1", 4);
|
|
494
|
+
// Let everything get stamped and round-trip back to us
|
|
495
|
+
containerRuntimeFactory.processAllMessages();
|
|
496
|
+
const testSubDir1 = directory
|
|
497
|
+
.getSubDirectory("lists")
|
|
498
|
+
?.getSubDirectory("ListLevels-0");
|
|
499
|
+
const testSubDir2 = directory2
|
|
500
|
+
.getSubDirectory("lists")
|
|
501
|
+
?.getSubDirectory("ListLevels-0");
|
|
502
|
+
assert(testSubDir1 !== undefined, "second level subDir should exists in dir1");
|
|
503
|
+
assert(testSubDir2 !== undefined, "second level subDir should exists in dir2");
|
|
504
|
+
assert(testSubDir1.get("random1") === 4, "value should be correct in dir1");
|
|
505
|
+
assert(testSubDir2.get("random1") === 4, "value should be correct in dir2");
|
|
506
|
+
assert(testSubDir1.get("random2") === undefined, "value should be correct in dir1 for key2");
|
|
507
|
+
assert(testSubDir2.get("random2") === undefined, "value should be correct in dir2 for key2");
|
|
508
|
+
});
|
|
509
|
+
it("Should populate with csn as 0 and then process the create op", async () => {
|
|
510
|
+
directory.createSubDirectory("nested");
|
|
511
|
+
const serialized = serialize(directory);
|
|
512
|
+
// Now populate a new directory with contents of above to simulate processing of attach op
|
|
513
|
+
const containerRuntimeFactory = new MockContainerRuntimeFactory();
|
|
514
|
+
const dataStoreRuntime2 = new MockFluidDataStoreRuntime();
|
|
515
|
+
const containerRuntime2 = containerRuntimeFactory.createContainerRuntime(dataStoreRuntime2);
|
|
516
|
+
const services2 = MockSharedObjectServices.createFromSummary(directory.getAttachSummary().summary);
|
|
517
|
+
services2.deltaConnection = dataStoreRuntime2.createDeltaConnection();
|
|
518
|
+
const directory2 = new SharedDirectory("directory2", dataStoreRuntime2, DirectoryFactory.Attributes);
|
|
519
|
+
await directory2.load(services2);
|
|
520
|
+
// Now load another directory to send op from that.
|
|
521
|
+
const dataStoreRuntime3 = new MockFluidDataStoreRuntime();
|
|
522
|
+
containerRuntimeFactory.createContainerRuntime(dataStoreRuntime3);
|
|
523
|
+
const services3 = MockSharedObjectServices.createFromSummary(directory.getAttachSummary().summary);
|
|
524
|
+
services3.deltaConnection = dataStoreRuntime3.createDeltaConnection();
|
|
525
|
+
const directory3 = new SharedDirectory("directory3", dataStoreRuntime3, DirectoryFactory.Attributes);
|
|
526
|
+
await directory3.load(services3);
|
|
527
|
+
containerRuntimeFactory.processAllMessages();
|
|
528
|
+
// Now send create op
|
|
529
|
+
directory3.getSubDirectory("nested")?.createSubDirectory("nested2");
|
|
530
|
+
containerRuntimeFactory.processAllMessages();
|
|
531
|
+
// Other directory should process the create op.
|
|
532
|
+
assert(directory2.getSubDirectory("nested")?.getSubDirectory("nested2") !== undefined, "/nested/nested2 should be present");
|
|
533
|
+
});
|
|
534
|
+
/**
|
|
535
|
+
* These tests test the scenario found in the following bug:
|
|
536
|
+
* {@link https://github.com/microsoft/FluidFramework/issues/2400}.
|
|
537
|
+
*
|
|
538
|
+
* - A SharedDirectory in local state performs a set or directory operation.
|
|
539
|
+
*
|
|
540
|
+
* - A second SharedDirectory is then created from the summarize of the first one.
|
|
541
|
+
*
|
|
542
|
+
* - The second SharedDirectory performs the same operation as the first one but with a different value.
|
|
543
|
+
*
|
|
544
|
+
* - The expected behavior is that the first SharedDirectory updates the key with the new value. But in the
|
|
545
|
+
* bug, the first SharedDirectory stores the key in its pending state even though it does not send out an
|
|
546
|
+
* an op. So when it gets a remote op with the same key, it ignores it as it has a pending op with the
|
|
547
|
+
* same key.
|
|
548
|
+
*/
|
|
549
|
+
it("should correctly process a set operation sent in local state", async () => {
|
|
550
|
+
// Set a key in local state.
|
|
551
|
+
const key = "testKey";
|
|
552
|
+
const value = "testValue";
|
|
553
|
+
directory.set(key, value);
|
|
554
|
+
// Load a new SharedDirectory in connected state from the summarize of the first one.
|
|
555
|
+
const containerRuntimeFactory = new MockContainerRuntimeFactory();
|
|
556
|
+
const dataStoreRuntime2 = new MockFluidDataStoreRuntime();
|
|
557
|
+
const containerRuntime2 = containerRuntimeFactory.createContainerRuntime(dataStoreRuntime2);
|
|
558
|
+
const services2 = MockSharedObjectServices.createFromSummary(directory.getAttachSummary().summary);
|
|
559
|
+
services2.deltaConnection = dataStoreRuntime2.createDeltaConnection();
|
|
560
|
+
const directory2 = new SharedDirectory("directory2", dataStoreRuntime2, DirectoryFactory.Attributes);
|
|
561
|
+
await directory2.load(services2);
|
|
562
|
+
// Now connect the first SharedDirectory
|
|
563
|
+
dataStoreRuntime.setAttachState(AttachState.Attached);
|
|
564
|
+
containerRuntimeFactory.createContainerRuntime(dataStoreRuntime);
|
|
565
|
+
const services1 = {
|
|
566
|
+
deltaConnection: dataStoreRuntime.createDeltaConnection(),
|
|
567
|
+
objectStorage: new MockStorage(undefined),
|
|
568
|
+
};
|
|
569
|
+
directory.connect(services1);
|
|
570
|
+
// Verify that both the directories have the key.
|
|
571
|
+
assert.equal(directory.get(key), value, "The first directory does not have the key");
|
|
572
|
+
assert.equal(directory2.get(key), value, "The second directory does not have the key");
|
|
573
|
+
// Set a new value for the same key in the second SharedDirectory.
|
|
574
|
+
const newValue = "newValue";
|
|
575
|
+
directory2.set(key, newValue);
|
|
576
|
+
// Process the message.
|
|
577
|
+
containerRuntimeFactory.processAllMessages();
|
|
578
|
+
// Verify that both the directories get the new value.
|
|
579
|
+
assert.equal(directory.get(key), newValue, "The first directory did not get the new value");
|
|
580
|
+
assert.equal(directory2.get(key), newValue, "The second directory did not get the new value");
|
|
581
|
+
});
|
|
582
|
+
it("should correctly process subDirectory operations sent in local state", async () => {
|
|
583
|
+
// Set the data store runtime to local.
|
|
584
|
+
dataStoreRuntime.local = true;
|
|
585
|
+
// Create a sub directory in local state.
|
|
586
|
+
const subDirName = "testSubDir";
|
|
587
|
+
directory.createSubDirectory(subDirName);
|
|
588
|
+
// Load a new SharedDirectory in connected state from the summarize of the first one.
|
|
589
|
+
const containerRuntimeFactory = new MockContainerRuntimeFactory();
|
|
590
|
+
const dataStoreRuntime2 = new MockFluidDataStoreRuntime();
|
|
591
|
+
const containerRuntime2 = containerRuntimeFactory.createContainerRuntime(dataStoreRuntime2);
|
|
592
|
+
const services2 = MockSharedObjectServices.createFromSummary(directory.getAttachSummary().summary);
|
|
593
|
+
services2.deltaConnection = dataStoreRuntime2.createDeltaConnection();
|
|
594
|
+
const directory2 = new SharedDirectory("directory2", dataStoreRuntime2, DirectoryFactory.Attributes);
|
|
595
|
+
await directory2.load(services2);
|
|
596
|
+
// Now connect the first SharedDirectory
|
|
597
|
+
dataStoreRuntime.setAttachState(AttachState.Attached);
|
|
598
|
+
containerRuntimeFactory.createContainerRuntime(dataStoreRuntime);
|
|
599
|
+
const services1 = {
|
|
600
|
+
deltaConnection: dataStoreRuntime.createDeltaConnection(),
|
|
601
|
+
objectStorage: new MockStorage(undefined),
|
|
602
|
+
};
|
|
603
|
+
directory.connect(services1);
|
|
604
|
+
// Verify that both the directories have the key.
|
|
605
|
+
assert.ok(directory.getSubDirectory(subDirName), "The first directory does not have sub directory");
|
|
606
|
+
const subDir2 = directory2.getSubDirectory(subDirName);
|
|
607
|
+
assert.ok(subDir2, "The second directory does not have sub directory");
|
|
608
|
+
subDir2.set("foo", "bar");
|
|
609
|
+
containerRuntimeFactory.processAllMessages();
|
|
610
|
+
assertEquivalentDirectories(directory, directory2);
|
|
611
|
+
// Delete the subDirectory in the second SharedDirectory.
|
|
612
|
+
directory2.deleteSubDirectory(subDirName);
|
|
613
|
+
// Process the message.
|
|
614
|
+
containerRuntimeFactory.processAllMessages();
|
|
615
|
+
// Verify that both the directory have the sub directory deleted.
|
|
616
|
+
assert.equal(directory.getSubDirectory(subDirName), undefined, "The first directory did not process delete");
|
|
617
|
+
assert.equal(directory2.getSubDirectory(subDirName), undefined, "The second directory did not process delete");
|
|
618
|
+
});
|
|
619
|
+
});
|
|
620
|
+
});
|
|
621
|
+
describe("Connected state", () => {
|
|
622
|
+
let containerRuntimeFactory;
|
|
623
|
+
let directory1;
|
|
624
|
+
let directory2;
|
|
625
|
+
beforeEach("createDirectory", async () => {
|
|
626
|
+
containerRuntimeFactory = new MockContainerRuntimeFactory();
|
|
627
|
+
// Create the first directory1.
|
|
628
|
+
directory1 = createConnectedDirectory("directory1", containerRuntimeFactory);
|
|
629
|
+
// Create a second directory1
|
|
630
|
+
directory2 = createConnectedDirectory("directory2", containerRuntimeFactory);
|
|
631
|
+
});
|
|
632
|
+
describe("API", () => {
|
|
633
|
+
it("Can set and get keys one level deep", () => {
|
|
634
|
+
directory1.set("testKey", "testValue");
|
|
635
|
+
containerRuntimeFactory.processAllMessages();
|
|
636
|
+
// Verify the local SharedDirectory
|
|
637
|
+
assert.equal(directory1.get("testKey"), "testValue", "could not retrieve key");
|
|
638
|
+
// Verify the remote SharedDirectory
|
|
639
|
+
assert.equal(directory2.get("testKey"), "testValue", "could not retrieve key from remote directory1");
|
|
640
|
+
});
|
|
641
|
+
it("Can set and get keys two levels deep", () => {
|
|
642
|
+
const fooDirectory = directory1.createSubDirectory("foo");
|
|
643
|
+
const barDirectory = directory1.createSubDirectory("bar");
|
|
644
|
+
fooDirectory.set("testKey", "testValue");
|
|
645
|
+
fooDirectory.set("testKey2", "testValue2");
|
|
646
|
+
barDirectory.set("testKey3", "testValue3");
|
|
647
|
+
containerRuntimeFactory.processAllMessages();
|
|
648
|
+
// Verify the local SharedDirectory
|
|
649
|
+
assert.equal(directory1.getWorkingDirectory("foo")?.get("testKey"), "testValue");
|
|
650
|
+
assert.equal(directory1.getWorkingDirectory("foo/")?.get("testKey2"), "testValue2");
|
|
651
|
+
assert.equal(directory1.getWorkingDirectory("bar")?.get("testKey3"), "testValue3");
|
|
652
|
+
// Verify the remote SharedDirectory
|
|
653
|
+
assert.equal(directory2.getWorkingDirectory("foo")?.get("testKey"), "testValue");
|
|
654
|
+
assert.equal(directory2.getWorkingDirectory("foo/")?.get("testKey2"), "testValue2");
|
|
655
|
+
assert.equal(directory2.getWorkingDirectory("bar")?.get("testKey3"), "testValue3");
|
|
656
|
+
});
|
|
657
|
+
it("Can clear keys stored directly under the root", () => {
|
|
658
|
+
const fooDirectory = directory1.createSubDirectory("foo");
|
|
659
|
+
const barDirectory = directory1.createSubDirectory("bar");
|
|
660
|
+
fooDirectory.set("testKey", "testValue");
|
|
661
|
+
fooDirectory.set("testKey2", "testValue2");
|
|
662
|
+
barDirectory.set("testKey3", "testValue3");
|
|
663
|
+
directory1.set("testKey", "testValue4");
|
|
664
|
+
directory1.set("testKey2", "testValue5");
|
|
665
|
+
directory1.clear();
|
|
666
|
+
containerRuntimeFactory.processAllMessages();
|
|
667
|
+
// Verify the local SharedDirectory
|
|
668
|
+
assert.equal(directory1.getWorkingDirectory("/foo/")?.get("testKey"), "testValue");
|
|
669
|
+
assert.equal(directory1.getWorkingDirectory("./foo")?.get("testKey2"), "testValue2");
|
|
670
|
+
assert.equal(directory1.getWorkingDirectory("bar")?.get("testKey3"), "testValue3");
|
|
671
|
+
assert.equal(directory1.get("testKey"), undefined);
|
|
672
|
+
assert.equal(directory1.get("testKey2"), undefined);
|
|
673
|
+
// Verify the remote SharedDirectory
|
|
674
|
+
assert.equal(directory2.getWorkingDirectory("/foo/")?.get("testKey"), "testValue");
|
|
675
|
+
assert.equal(directory2.getWorkingDirectory("./foo")?.get("testKey2"), "testValue2");
|
|
676
|
+
assert.equal(directory2.getWorkingDirectory("bar")?.get("testKey3"), "testValue3");
|
|
677
|
+
assert.equal(directory2.get("testKey"), undefined);
|
|
678
|
+
assert.equal(directory2.get("testKey2"), undefined);
|
|
679
|
+
});
|
|
680
|
+
it("Can delete keys from the root", () => {
|
|
681
|
+
const fooDirectory = directory1.createSubDirectory("foo");
|
|
682
|
+
const barDirectory = directory1.createSubDirectory("bar");
|
|
683
|
+
fooDirectory.set("testKey", "testValue");
|
|
684
|
+
fooDirectory.set("testKey2", "testValue2");
|
|
685
|
+
barDirectory.set("testKey3", "testValue3");
|
|
686
|
+
directory1.set("testKey", "testValue4");
|
|
687
|
+
directory1.set("testKey2", "testValue5");
|
|
688
|
+
directory1.delete("testKey2");
|
|
689
|
+
containerRuntimeFactory.processAllMessages();
|
|
690
|
+
// Verify the local SharedDirectory
|
|
691
|
+
assert.equal(directory1.getWorkingDirectory("foo")?.get("testKey"), "testValue");
|
|
692
|
+
assert.equal(directory1.getWorkingDirectory("foo")?.get("testKey2"), "testValue2");
|
|
693
|
+
assert.equal(directory1.getWorkingDirectory("bar")?.get("testKey3"), "testValue3");
|
|
694
|
+
assert.equal(directory1.get("testKey"), "testValue4");
|
|
695
|
+
assert.equal(directory1.get("testKey2"), undefined);
|
|
696
|
+
// Verify the remote SharedDirectory
|
|
697
|
+
assert.equal(directory2.getWorkingDirectory("foo")?.get("testKey"), "testValue");
|
|
698
|
+
assert.equal(directory2.getWorkingDirectory("foo")?.get("testKey2"), "testValue2");
|
|
699
|
+
assert.equal(directory2.getWorkingDirectory("bar")?.get("testKey3"), "testValue3");
|
|
700
|
+
assert.equal(directory2.get("testKey"), "testValue4");
|
|
701
|
+
assert.equal(directory2.get("testKey2"), undefined);
|
|
702
|
+
});
|
|
703
|
+
it("Shouldn't clear value if there is pending set", () => {
|
|
704
|
+
const valuesChanged = [];
|
|
705
|
+
let clearCount = 0;
|
|
706
|
+
directory1.on("valueChanged", (changed, local, target) => {
|
|
707
|
+
valuesChanged.push(changed);
|
|
708
|
+
});
|
|
709
|
+
directory1.on("clear", (local, target) => {
|
|
710
|
+
clearCount++;
|
|
711
|
+
});
|
|
712
|
+
directory2.set("directory2key", "value2");
|
|
713
|
+
directory2.clear();
|
|
714
|
+
directory1.set("directory1Key", "value1");
|
|
715
|
+
directory2.clear();
|
|
716
|
+
if (containerRuntimeFactory.processSomeMessages === undefined) {
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
containerRuntimeFactory.processSomeMessages(2);
|
|
720
|
+
assert.equal(valuesChanged.length, 3);
|
|
721
|
+
assert.equal(valuesChanged[0].key, "directory1Key");
|
|
722
|
+
assert.equal(valuesChanged[0].previousValue, undefined);
|
|
723
|
+
assert.equal(valuesChanged[1].key, "directory2key");
|
|
724
|
+
assert.equal(valuesChanged[1].previousValue, undefined);
|
|
725
|
+
assert.equal(valuesChanged[2].key, "directory1Key");
|
|
726
|
+
assert.equal(valuesChanged[2].previousValue, undefined);
|
|
727
|
+
assert.equal(clearCount, 1);
|
|
728
|
+
assert.equal(directory1.size, 1);
|
|
729
|
+
assert.equal(directory1.get("directory1Key"), "value1");
|
|
730
|
+
containerRuntimeFactory.processSomeMessages(2);
|
|
731
|
+
assert.equal(valuesChanged.length, 3);
|
|
732
|
+
assert.equal(clearCount, 2);
|
|
733
|
+
assert.equal(directory1.size, 0);
|
|
734
|
+
});
|
|
735
|
+
it("Shouldn't overwrite value if there is pending set", () => {
|
|
736
|
+
const value1 = "value1";
|
|
737
|
+
const pending1 = "pending1";
|
|
738
|
+
const pending2 = "pending2";
|
|
739
|
+
directory1.set("test", value1);
|
|
740
|
+
directory2.set("test", pending1);
|
|
741
|
+
directory2.set("test", pending2);
|
|
742
|
+
if (containerRuntimeFactory.processSomeMessages === undefined) {
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
containerRuntimeFactory.processSomeMessages(1);
|
|
746
|
+
// Verify the SharedDirectory with processed message
|
|
747
|
+
assert.equal(directory1.has("test"), true, "could not find the set key");
|
|
748
|
+
assert.equal(directory1.get("test"), value1, "could not get the set key");
|
|
749
|
+
// Verify the SharedDirectory with 2 pending messages
|
|
750
|
+
assert.equal(directory2.has("test"), true, "could not find the set key in pending directory");
|
|
751
|
+
assert.equal(directory2.get("test"), pending2, "could not get the set key from pending directory");
|
|
752
|
+
containerRuntimeFactory.processSomeMessages(1);
|
|
753
|
+
// Verify the SharedDirectory gets updated from remote
|
|
754
|
+
assert.equal(directory1.has("test"), true, "could not find the set key");
|
|
755
|
+
assert.equal(directory1.get("test"), pending1, "could not get the set key");
|
|
756
|
+
// Verify the SharedDirectory with 1 pending message
|
|
757
|
+
assert.equal(directory2.has("test"), true, "could not find the set key in pending directory");
|
|
758
|
+
assert.equal(directory2.get("test"), pending2, "could not get the set key from pending directory");
|
|
759
|
+
});
|
|
760
|
+
it("Shouldn't set values when pending clear", () => {
|
|
761
|
+
const key = "test";
|
|
762
|
+
directory1.set(key, "directory1value1");
|
|
763
|
+
directory2.set(key, "directory2value2");
|
|
764
|
+
directory2.clear();
|
|
765
|
+
directory2.set(key, "directory2value3");
|
|
766
|
+
directory2.clear();
|
|
767
|
+
if (containerRuntimeFactory.processSomeMessages === undefined) {
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
// directory1.set(key, "directory1value1");
|
|
771
|
+
containerRuntimeFactory.processSomeMessages(1);
|
|
772
|
+
// Verify the SharedDirectory with processed message
|
|
773
|
+
assert.equal(directory1.has("test"), true, "could not find the set key");
|
|
774
|
+
assert.equal(directory1.get("test"), "directory1value1", "could not get the set key");
|
|
775
|
+
// Verify the SharedDirectory with 2 pending clears
|
|
776
|
+
assert.equal(directory2.has("test"), false, "found the set key in pending directory");
|
|
777
|
+
// directory2.set(key, "directory2value2");
|
|
778
|
+
containerRuntimeFactory.processSomeMessages(1);
|
|
779
|
+
// Verify the SharedDirectory gets updated from remote
|
|
780
|
+
assert.equal(directory1.has("test"), true, "could not find the set key");
|
|
781
|
+
assert.equal(directory1.get("test"), "directory2value2", "could not get the set key");
|
|
782
|
+
// Verify the SharedDirectory with 2 pending clears
|
|
783
|
+
assert.equal(directory2.has("test"), false, "found the set key in pending directory");
|
|
784
|
+
// directory2.clear();
|
|
785
|
+
containerRuntimeFactory.processSomeMessages(1);
|
|
786
|
+
// Verify the SharedDirectory gets updated from remote clear
|
|
787
|
+
assert.equal(directory1.has("test"), false, "found the set key");
|
|
788
|
+
// Verify the SharedDirectory with 1 pending clear
|
|
789
|
+
assert.equal(directory2.has("test"), false, "found the set key in pending directory");
|
|
790
|
+
// directory2.set(key, "directory2value3");
|
|
791
|
+
containerRuntimeFactory.processSomeMessages(1);
|
|
792
|
+
// Verify the SharedDirectory gets updated from remote
|
|
793
|
+
assert.equal(directory1.has("test"), true, "could not find the set key");
|
|
794
|
+
assert.equal(directory1.get("test"), "directory2value3", "could not get the set key");
|
|
795
|
+
// Verify the SharedDirectory with 1 pending clear
|
|
796
|
+
assert.equal(directory2.has("test"), false, "found the set key in pending directory");
|
|
797
|
+
// directory2.clear();
|
|
798
|
+
containerRuntimeFactory.processSomeMessages(1);
|
|
799
|
+
// Verify the SharedDirectory gets updated from remote clear
|
|
800
|
+
assert.equal(directory1.has("test"), false, "found the set key");
|
|
801
|
+
// Verify the SharedDirectory with no more pending clear
|
|
802
|
+
assert.equal(directory2.has("test"), false, "found the set key in pending directory");
|
|
803
|
+
directory1.set(key, "directory1value4");
|
|
804
|
+
containerRuntimeFactory.processSomeMessages(1);
|
|
805
|
+
// Verify the SharedDirectory gets updated from local
|
|
806
|
+
assert.equal(directory1.has("test"), true, "could not find the set key");
|
|
807
|
+
assert.equal(directory1.get("test"), "directory1value4", "could not get the set key");
|
|
808
|
+
// Verify the SharedDirectory gets updated from remote
|
|
809
|
+
assert.equal(directory1.has("test"), true, "could not find the set key");
|
|
810
|
+
assert.equal(directory1.get("test"), "directory1value4", "could not get the set key");
|
|
811
|
+
});
|
|
812
|
+
it("Directories should ensure eventual consistency using LWW approach 1: Test 1", async () => {
|
|
813
|
+
const root1SubDir = directory1.createSubDirectory("testSubDir");
|
|
814
|
+
root1SubDir.set("key1", "testValue1");
|
|
815
|
+
directory1.deleteSubDirectory("testSubDir");
|
|
816
|
+
const root1SubDir2 = directory1.createSubDirectory("testSubDir");
|
|
817
|
+
root1SubDir2.set("key2", "testValue2");
|
|
818
|
+
directory2.createSubDirectory("testSubDir");
|
|
819
|
+
// After the above scenario, the consistent state using LWW would be to have testSubDir with 1 key.
|
|
820
|
+
containerRuntimeFactory.processAllMessages();
|
|
821
|
+
const directory1SubDir = directory1.getSubDirectory("testSubDir");
|
|
822
|
+
const directory2SubDir = directory2.getSubDirectory("testSubDir");
|
|
823
|
+
assert(directory1SubDir !== undefined, "SubDirectory on dir 1 should be present");
|
|
824
|
+
assert(directory2SubDir !== undefined, "SubDirectory on dir 2 should be present");
|
|
825
|
+
assert.strictEqual(directory1SubDir.size, 1, "Dir1 1 key should exist");
|
|
826
|
+
assert.strictEqual(directory2SubDir.size, 1, "Dir2 1 key should exist");
|
|
827
|
+
assert.strictEqual(directory1SubDir.get("key2"), "testValue2", "Dir1 key value should match");
|
|
828
|
+
assert.strictEqual(directory2SubDir.get("key2"), "testValue2", "Dir2 key value should match");
|
|
829
|
+
});
|
|
830
|
+
it("Directories should ensure eventual consistency using LWW approach 1: Test 2", async () => {
|
|
831
|
+
const root1SubDir = directory1.createSubDirectory("testSubDir");
|
|
832
|
+
directory2.createSubDirectory("testSubDir");
|
|
833
|
+
root1SubDir.set("key1", "testValue1");
|
|
834
|
+
directory2.deleteSubDirectory("testSubDir");
|
|
835
|
+
directory2.createSubDirectory("testSubDir");
|
|
836
|
+
// After the above scenario, the consistent state using LWW would be to have testSubDir with 0 keys.
|
|
837
|
+
containerRuntimeFactory.processAllMessages();
|
|
838
|
+
const directory1SubDir = directory1.getSubDirectory("testSubDir");
|
|
839
|
+
const directory2SubDir = directory2.getSubDirectory("testSubDir");
|
|
840
|
+
assert(directory1SubDir !== undefined, "SubDirectory on dir 1 should be present");
|
|
841
|
+
assert(directory2SubDir !== undefined, "SubDirectory on dir 2 should be present");
|
|
842
|
+
assert.strictEqual(directory1SubDir.size, 0, "Dir 1 no key should exist");
|
|
843
|
+
assert.strictEqual(directory2SubDir.size, 0, "Dir 2 no key should exist");
|
|
844
|
+
});
|
|
845
|
+
});
|
|
846
|
+
describe("SubDirectory", () => {
|
|
847
|
+
it("Can iterate over the subdirectories in the root", () => {
|
|
848
|
+
directory1.createSubDirectory("foo");
|
|
849
|
+
directory1.createSubDirectory("bar");
|
|
850
|
+
containerRuntimeFactory.processAllMessages();
|
|
851
|
+
const expectedDirectories = new Set(["foo", "bar"]);
|
|
852
|
+
// Verify the local SharedDirectory
|
|
853
|
+
for (const [subDirName] of directory1.subdirectories()) {
|
|
854
|
+
assert.ok(expectedDirectories.has(subDirName));
|
|
855
|
+
}
|
|
856
|
+
// Verify the remote SharedDirectory
|
|
857
|
+
for (const [subDirName] of directory2.subdirectories()) {
|
|
858
|
+
assert.ok(expectedDirectories.has(subDirName));
|
|
859
|
+
expectedDirectories.delete(subDirName);
|
|
860
|
+
}
|
|
861
|
+
assert.ok(expectedDirectories.size === 0);
|
|
862
|
+
});
|
|
863
|
+
it("Can get a subDirectory", () => {
|
|
864
|
+
const fooDirectory = directory1.createSubDirectory("foo");
|
|
865
|
+
const barDirectory = directory1.createSubDirectory("bar");
|
|
866
|
+
fooDirectory.set("testKey", "testValue");
|
|
867
|
+
fooDirectory.set("testKey2", "testValue2");
|
|
868
|
+
barDirectory.set("testKey3", "testValue3");
|
|
869
|
+
containerRuntimeFactory.processAllMessages();
|
|
870
|
+
// Verify the local SharedDirectory
|
|
871
|
+
assert.ok(directory1.getWorkingDirectory("/foo"));
|
|
872
|
+
assert.ok(directory1.getSubDirectory("foo"));
|
|
873
|
+
// Verify the remote SharedDirectory
|
|
874
|
+
assert.ok(directory2.getWorkingDirectory("/foo"));
|
|
875
|
+
assert.ok(directory2.getSubDirectory("foo"));
|
|
876
|
+
});
|
|
877
|
+
it("Knows its absolute path", () => {
|
|
878
|
+
const fooDirectory = directory1.createSubDirectory("foo");
|
|
879
|
+
const barDirectory = fooDirectory.createSubDirectory("bar");
|
|
880
|
+
containerRuntimeFactory.processAllMessages();
|
|
881
|
+
// Verify the local SharedDirectory
|
|
882
|
+
assert.equal(fooDirectory.absolutePath, "/foo");
|
|
883
|
+
assert.equal(barDirectory.absolutePath, "/foo/bar");
|
|
884
|
+
// Verify the remote SharedDirectory
|
|
885
|
+
const fooDirectory2 = directory2.getSubDirectory("foo");
|
|
886
|
+
assert(fooDirectory2);
|
|
887
|
+
const barDirectory2 = fooDirectory2.getSubDirectory("bar");
|
|
888
|
+
assert(barDirectory2);
|
|
889
|
+
assert.equal(fooDirectory2.absolutePath, "/foo");
|
|
890
|
+
assert.equal(barDirectory2.absolutePath, "/foo/bar");
|
|
891
|
+
});
|
|
892
|
+
it("Can get and set keys from a subDirectory using relative paths", () => {
|
|
893
|
+
const fooDirectory = directory1.createSubDirectory("foo");
|
|
894
|
+
const barDirectory = directory1.createSubDirectory("bar");
|
|
895
|
+
fooDirectory.set("testKey", "testValue");
|
|
896
|
+
fooDirectory.set("testKey2", "testValue2");
|
|
897
|
+
barDirectory.set("testKey3", "testValue3");
|
|
898
|
+
containerRuntimeFactory.processAllMessages();
|
|
899
|
+
// Verify the local SharedDirectory
|
|
900
|
+
const testSubDir = directory1.getWorkingDirectory("/foo");
|
|
901
|
+
assert(testSubDir);
|
|
902
|
+
assert.equal(testSubDir.has("testKey"), true);
|
|
903
|
+
assert.equal(testSubDir.has("garbage"), false);
|
|
904
|
+
assert.equal(testSubDir.get("testKey"), "testValue");
|
|
905
|
+
assert.equal(testSubDir.get("testKey2"), "testValue2");
|
|
906
|
+
assert.equal(testSubDir.get("testKey3"), undefined);
|
|
907
|
+
// Verify the remote SharedDirectory
|
|
908
|
+
const barSubDir = directory2.getWorkingDirectory("/foo");
|
|
909
|
+
assert(barSubDir);
|
|
910
|
+
assert.equal(barSubDir.has("testKey"), true);
|
|
911
|
+
assert.equal(barSubDir.has("garbage"), false);
|
|
912
|
+
assert.equal(barSubDir.get("testKey"), "testValue");
|
|
913
|
+
assert.equal(barSubDir.get("testKey2"), "testValue2");
|
|
914
|
+
assert.equal(barSubDir.get("testKey3"), undefined);
|
|
915
|
+
// Set value in sub directory1.
|
|
916
|
+
testSubDir.set("fromSubDir", "testValue4");
|
|
917
|
+
containerRuntimeFactory.processAllMessages();
|
|
918
|
+
// Verify the local sub directory1
|
|
919
|
+
assert.equal(directory1.getWorkingDirectory("foo")?.get("fromSubDir"), "testValue4");
|
|
920
|
+
// Verify the remote sub directory1
|
|
921
|
+
assert.equal(directory2.getWorkingDirectory("foo")?.get("fromSubDir"), "testValue4");
|
|
922
|
+
});
|
|
923
|
+
it("raises the containedValueChanged event when keys are set and deleted from a subDirectory", () => {
|
|
924
|
+
directory1.createSubDirectory("foo");
|
|
925
|
+
directory1.createSubDirectory("bar");
|
|
926
|
+
containerRuntimeFactory.processAllMessages();
|
|
927
|
+
const foo1 = directory1.getWorkingDirectory("/foo");
|
|
928
|
+
assert(foo1);
|
|
929
|
+
const foo2 = directory2.getWorkingDirectory("/foo");
|
|
930
|
+
assert(foo2);
|
|
931
|
+
const bar1 = directory1.getWorkingDirectory("/bar");
|
|
932
|
+
assert(bar1);
|
|
933
|
+
const bar2 = directory2.getWorkingDirectory("/bar");
|
|
934
|
+
assert(bar2);
|
|
935
|
+
let called1 = 0;
|
|
936
|
+
let called2 = 0;
|
|
937
|
+
let called3 = 0;
|
|
938
|
+
let called4 = 0;
|
|
939
|
+
foo1.on("containedValueChanged", () => called1++);
|
|
940
|
+
foo2.on("containedValueChanged", () => called2++);
|
|
941
|
+
bar1.on("containedValueChanged", () => called3++);
|
|
942
|
+
bar2.on("containedValueChanged", () => called4++);
|
|
943
|
+
foo1.set("testKey", "testValue");
|
|
944
|
+
containerRuntimeFactory.processAllMessages();
|
|
945
|
+
assert.strictEqual(called1, 1, "containedValueChanged on local foo subDirectory after set()");
|
|
946
|
+
assert.strictEqual(called2, 1, "containedValueChanged on remote foo subDirectory after set()");
|
|
947
|
+
assert.strictEqual(called3, 0, "containedValueChanged on local bar subDirectory after set()");
|
|
948
|
+
assert.strictEqual(called4, 0, "containedValueChanged on remote bar subDirectory after set()");
|
|
949
|
+
foo1.delete("testKey");
|
|
950
|
+
containerRuntimeFactory.processAllMessages();
|
|
951
|
+
assert.strictEqual(called1, 2, "containedValueChanged on local subDirectory after delete()");
|
|
952
|
+
assert.strictEqual(called2, 2, "containedValueChanged on remote subDirectory after delete()");
|
|
953
|
+
assert.strictEqual(called3, 0, "containedValueChanged on local bar subDirectory after delete()");
|
|
954
|
+
assert.strictEqual(called4, 0, "containedValueChanged on remote bar subDirectory after delete()");
|
|
955
|
+
});
|
|
956
|
+
it("Can be cleared from the subDirectory", () => {
|
|
957
|
+
const fooDirectory = directory1.createSubDirectory("foo");
|
|
958
|
+
const barDirectory = directory1.createSubDirectory("bar");
|
|
959
|
+
fooDirectory.set("testKey", "testValue");
|
|
960
|
+
fooDirectory.set("testKey2", "testValue2");
|
|
961
|
+
barDirectory.set("testKey3", "testValue3");
|
|
962
|
+
directory1.set("testKey", "testValue4");
|
|
963
|
+
directory1.set("testKey2", "testValue5");
|
|
964
|
+
const testSubDir = directory1.getWorkingDirectory("/foo");
|
|
965
|
+
assert(testSubDir);
|
|
966
|
+
testSubDir.clear();
|
|
967
|
+
containerRuntimeFactory.processAllMessages();
|
|
968
|
+
// Verify the local SharedDirectory
|
|
969
|
+
const fooSubDirectory1 = directory1.getWorkingDirectory("foo");
|
|
970
|
+
assert(fooSubDirectory1);
|
|
971
|
+
assert.equal(fooSubDirectory1.get("testKey"), undefined);
|
|
972
|
+
assert.equal(fooSubDirectory1.get("testKey2"), undefined);
|
|
973
|
+
assert.equal(directory1.getWorkingDirectory("bar")?.get("testKey3"), "testValue3");
|
|
974
|
+
assert.equal(directory1.getWorkingDirectory("..")?.get("testKey"), "testValue4");
|
|
975
|
+
assert.equal(directory1.getWorkingDirectory(".")?.get("testKey2"), "testValue5");
|
|
976
|
+
// Verify the remote SharedDirectory
|
|
977
|
+
const fooSubDirectory2 = directory2.getWorkingDirectory("foo");
|
|
978
|
+
assert(fooSubDirectory2);
|
|
979
|
+
assert.equal(fooSubDirectory2.get("testKey"), undefined);
|
|
980
|
+
assert.equal(fooSubDirectory2.get("testKey2"), undefined);
|
|
981
|
+
assert.equal(directory2.getWorkingDirectory("bar")?.get("testKey3"), "testValue3");
|
|
982
|
+
assert.equal(directory2.getWorkingDirectory("..")?.get("testKey"), "testValue4");
|
|
983
|
+
assert.equal(directory2.getWorkingDirectory(".")?.get("testKey2"), "testValue5");
|
|
984
|
+
});
|
|
985
|
+
it("Can delete keys from the subDirectory", () => {
|
|
986
|
+
const fooDirectory = directory1.createSubDirectory("foo");
|
|
987
|
+
const barDirectory = directory1.createSubDirectory("bar");
|
|
988
|
+
fooDirectory.set("testKey", "testValue");
|
|
989
|
+
fooDirectory.set("testKey2", "testValue2");
|
|
990
|
+
barDirectory.set("testKey3", "testValue3");
|
|
991
|
+
directory1.set("testKey", "testValue4");
|
|
992
|
+
directory1.set("testKey2", "testValue5");
|
|
993
|
+
const testSubDirFoo = directory1.getWorkingDirectory("/foo");
|
|
994
|
+
assert(testSubDirFoo);
|
|
995
|
+
testSubDirFoo.delete("testKey2");
|
|
996
|
+
const testSubDirBar = directory1.getWorkingDirectory("/bar");
|
|
997
|
+
assert(testSubDirBar);
|
|
998
|
+
testSubDirBar.delete("testKey3");
|
|
999
|
+
containerRuntimeFactory.processAllMessages();
|
|
1000
|
+
// Verify the local SharedDirectory
|
|
1001
|
+
const fooSubDirectory1 = directory1.getWorkingDirectory("foo");
|
|
1002
|
+
assert(fooSubDirectory1);
|
|
1003
|
+
const barSubDirectory1 = directory1.getWorkingDirectory("bar");
|
|
1004
|
+
assert(barSubDirectory1);
|
|
1005
|
+
assert.equal(fooSubDirectory1.get("testKey"), "testValue");
|
|
1006
|
+
assert.equal(fooSubDirectory1.get("testKey2"), undefined);
|
|
1007
|
+
assert.equal(barSubDirectory1.get("testKey3"), undefined);
|
|
1008
|
+
assert.equal(directory1.get("testKey"), "testValue4");
|
|
1009
|
+
assert.equal(directory1.get("testKey2"), "testValue5");
|
|
1010
|
+
// Verify the remote SharedDirectory
|
|
1011
|
+
const fooSubDirectory2 = directory2.getWorkingDirectory("foo");
|
|
1012
|
+
assert(fooSubDirectory2);
|
|
1013
|
+
const barSubDirectory2 = directory2.getWorkingDirectory("bar");
|
|
1014
|
+
assert(barSubDirectory2);
|
|
1015
|
+
assert.equal(fooSubDirectory2.get("testKey"), "testValue");
|
|
1016
|
+
assert.equal(fooSubDirectory2.get("testKey2"), undefined);
|
|
1017
|
+
assert.equal(barSubDirectory2.get("testKey3"), undefined);
|
|
1018
|
+
assert.equal(directory2.get("testKey"), "testValue4");
|
|
1019
|
+
assert.equal(directory2.get("testKey2"), "testValue5");
|
|
1020
|
+
});
|
|
1021
|
+
it("Knows the size of the subDirectory", () => {
|
|
1022
|
+
const fooDirectory = directory1.createSubDirectory("foo");
|
|
1023
|
+
const barDirectory = directory1.createSubDirectory("bar");
|
|
1024
|
+
fooDirectory.set("testKey", "testValue");
|
|
1025
|
+
fooDirectory.set("testKey2", "testValue2");
|
|
1026
|
+
barDirectory.set("testKey3", "testValue3");
|
|
1027
|
+
directory1.set("testKey", "testValue4");
|
|
1028
|
+
directory1.set("testKey2", "testValue5");
|
|
1029
|
+
containerRuntimeFactory.processAllMessages();
|
|
1030
|
+
// Verify the local SharedDirectory
|
|
1031
|
+
const testSubDirFoo = directory1.getWorkingDirectory("/foo");
|
|
1032
|
+
assert(testSubDirFoo);
|
|
1033
|
+
assert.equal(testSubDirFoo.size, 2);
|
|
1034
|
+
// Verify the remote SharedDirectory
|
|
1035
|
+
const testSubDirFoo2 = directory2.getWorkingDirectory("/foo");
|
|
1036
|
+
assert(testSubDirFoo2);
|
|
1037
|
+
assert.equal(testSubDirFoo2.size, 2);
|
|
1038
|
+
testSubDirFoo.delete("testKey2");
|
|
1039
|
+
containerRuntimeFactory.processAllMessages();
|
|
1040
|
+
// Verify the local SharedDirectory
|
|
1041
|
+
assert.equal(testSubDirFoo.size, 1);
|
|
1042
|
+
// Verify the remote SharedDirectory
|
|
1043
|
+
assert.equal(testSubDirFoo2.size, 1);
|
|
1044
|
+
directory1.delete("testKey");
|
|
1045
|
+
containerRuntimeFactory.processAllMessages();
|
|
1046
|
+
// Verify the local SharedDirectory
|
|
1047
|
+
assert.equal(testSubDirFoo.size, 1);
|
|
1048
|
+
// Verify the remote SharedDirectory
|
|
1049
|
+
assert.equal(testSubDirFoo2.size, 1);
|
|
1050
|
+
const testSubDirBar = directory1.getWorkingDirectory("/bar");
|
|
1051
|
+
assert(testSubDirBar);
|
|
1052
|
+
testSubDirBar.delete("testKey3");
|
|
1053
|
+
// Verify the local SharedDirectory
|
|
1054
|
+
assert.equal(testSubDirFoo.size, 1);
|
|
1055
|
+
// Verify the remote SharedDirectory
|
|
1056
|
+
assert.equal(testSubDirFoo2.size, 1);
|
|
1057
|
+
directory1.clear();
|
|
1058
|
+
containerRuntimeFactory.processAllMessages();
|
|
1059
|
+
// Verify the local SharedDirectory
|
|
1060
|
+
assert.equal(testSubDirFoo.size, 1);
|
|
1061
|
+
// Verify the remote SharedDirectory
|
|
1062
|
+
assert.equal(testSubDirFoo2.size, 1);
|
|
1063
|
+
testSubDirFoo.clear();
|
|
1064
|
+
containerRuntimeFactory.processAllMessages();
|
|
1065
|
+
// Verify the local SharedDirectory
|
|
1066
|
+
assert.equal(testSubDirFoo.size, 0);
|
|
1067
|
+
// Verify the remote SharedDirectory
|
|
1068
|
+
assert.equal(testSubDirFoo2.size, 0);
|
|
1069
|
+
});
|
|
1070
|
+
it("Can get a subDirectory from a subDirectory", () => {
|
|
1071
|
+
const fooDirectory = directory1.createSubDirectory("foo");
|
|
1072
|
+
const barDirectory = directory1.createSubDirectory("bar");
|
|
1073
|
+
const bazDirectory = barDirectory.createSubDirectory("baz");
|
|
1074
|
+
fooDirectory.set("testKey", "testValue");
|
|
1075
|
+
fooDirectory.set("testKey2", "testValue2");
|
|
1076
|
+
barDirectory.set("testKey3", "testValue3");
|
|
1077
|
+
bazDirectory.set("testKey4", "testValue4");
|
|
1078
|
+
containerRuntimeFactory.processAllMessages();
|
|
1079
|
+
// Verify the local SharedDirectory
|
|
1080
|
+
const barSubDir = directory1.getWorkingDirectory("/bar");
|
|
1081
|
+
assert.ok(barSubDir);
|
|
1082
|
+
const bazSubDir = barSubDir.getWorkingDirectory("./baz");
|
|
1083
|
+
assert.ok(bazSubDir);
|
|
1084
|
+
assert.equal(bazSubDir.get("testKey4"), "testValue4");
|
|
1085
|
+
// Verify the remote SharedDirectory
|
|
1086
|
+
const barSubDir2 = directory2.getWorkingDirectory("/bar");
|
|
1087
|
+
assert.ok(barSubDir2);
|
|
1088
|
+
const bazSubDir2 = barSubDir2.getWorkingDirectory("./baz");
|
|
1089
|
+
assert.ok(bazSubDir2);
|
|
1090
|
+
assert.equal(bazSubDir2.get("testKey4"), "testValue4");
|
|
1091
|
+
});
|
|
1092
|
+
it("Can delete a child subDirectory", () => {
|
|
1093
|
+
const fooDirectory = directory1.createSubDirectory("foo");
|
|
1094
|
+
const barDirectory = directory1.createSubDirectory("bar");
|
|
1095
|
+
const bazDirectory = barDirectory.createSubDirectory("baz");
|
|
1096
|
+
fooDirectory.set("testKey", "testValue");
|
|
1097
|
+
fooDirectory.set("testKey2", "testValue2");
|
|
1098
|
+
barDirectory.set("testKey3", "testValue3");
|
|
1099
|
+
bazDirectory.set("testKey4", "testValue4");
|
|
1100
|
+
barDirectory.deleteSubDirectory("baz");
|
|
1101
|
+
containerRuntimeFactory.processAllMessages();
|
|
1102
|
+
// Verify the local SharedDirectory
|
|
1103
|
+
assert.equal(barDirectory.getWorkingDirectory("baz"), undefined);
|
|
1104
|
+
// Verify the remote SharedDirectory
|
|
1105
|
+
const barDirectory2 = directory2.getSubDirectory("bar");
|
|
1106
|
+
assert(barDirectory2);
|
|
1107
|
+
assert.equal(barDirectory2.getWorkingDirectory("baz"), undefined);
|
|
1108
|
+
});
|
|
1109
|
+
it("Can delete a child subDirectory with children", () => {
|
|
1110
|
+
const fooDirectory = directory1.createSubDirectory("foo");
|
|
1111
|
+
const barDirectory = directory1.createSubDirectory("bar");
|
|
1112
|
+
const bazDirectory = barDirectory.createSubDirectory("baz");
|
|
1113
|
+
fooDirectory.set("testKey", "testValue");
|
|
1114
|
+
fooDirectory.set("testKey2", "testValue2");
|
|
1115
|
+
barDirectory.set("testKey3", "testValue3");
|
|
1116
|
+
bazDirectory.set("testKey4", "testValue4");
|
|
1117
|
+
directory1.deleteSubDirectory("bar");
|
|
1118
|
+
containerRuntimeFactory.processAllMessages();
|
|
1119
|
+
// Verify the local SharedDirectory
|
|
1120
|
+
assert.equal(directory1.getWorkingDirectory("bar"), undefined);
|
|
1121
|
+
// Verify the remote SharedDirectory
|
|
1122
|
+
assert.equal(directory2.getWorkingDirectory("bar"), undefined);
|
|
1123
|
+
});
|
|
1124
|
+
it("Can get and use a keys iterator", () => {
|
|
1125
|
+
const fooDirectory = directory1.createSubDirectory("foo");
|
|
1126
|
+
const barDirectory = directory1.createSubDirectory("bar");
|
|
1127
|
+
const bazDirectory = barDirectory.createSubDirectory("baz");
|
|
1128
|
+
fooDirectory.set("testKey", "testValue");
|
|
1129
|
+
fooDirectory.set("testKey2", "testValue2");
|
|
1130
|
+
barDirectory.set("testKey3", "testValue3");
|
|
1131
|
+
bazDirectory.set("testKey4", "testValue4");
|
|
1132
|
+
containerRuntimeFactory.processAllMessages();
|
|
1133
|
+
// Verify the local SharedDirectory
|
|
1134
|
+
const fooSubDir = directory1.getWorkingDirectory("/foo");
|
|
1135
|
+
assert(fooSubDir);
|
|
1136
|
+
const fooSubDirIterator = fooSubDir.keys();
|
|
1137
|
+
const fooSubDirResult1 = fooSubDirIterator.next();
|
|
1138
|
+
assert.equal(fooSubDirResult1.value, "testKey");
|
|
1139
|
+
assert.equal(fooSubDirResult1.done, false);
|
|
1140
|
+
const fooSubDirResult2 = fooSubDirIterator.next();
|
|
1141
|
+
assert.equal(fooSubDirResult2.value, "testKey2");
|
|
1142
|
+
assert.equal(fooSubDirResult2.done, false);
|
|
1143
|
+
const fooSubDirResult3 = fooSubDirIterator.next();
|
|
1144
|
+
assert.equal(fooSubDirResult3.value, undefined);
|
|
1145
|
+
assert.equal(fooSubDirResult3.done, true);
|
|
1146
|
+
const barSubDir = directory1.getWorkingDirectory("/bar");
|
|
1147
|
+
assert(barSubDir);
|
|
1148
|
+
const barSubDirIterator = barSubDir.keys();
|
|
1149
|
+
const barSubDirResult1 = barSubDirIterator.next();
|
|
1150
|
+
assert.equal(barSubDirResult1.value, "testKey3");
|
|
1151
|
+
assert.equal(barSubDirResult1.done, false);
|
|
1152
|
+
const barSubDirResult2 = barSubDirIterator.next();
|
|
1153
|
+
assert.equal(barSubDirResult2.value, undefined);
|
|
1154
|
+
assert.equal(barSubDirResult2.done, true);
|
|
1155
|
+
// Verify the remote SharedDirectory
|
|
1156
|
+
const fooSubDir2 = directory2.getWorkingDirectory("/foo");
|
|
1157
|
+
assert(fooSubDir2);
|
|
1158
|
+
const fooSubDir2Iterator = fooSubDir2.keys();
|
|
1159
|
+
const fooSubDir2Result1 = fooSubDir2Iterator.next();
|
|
1160
|
+
assert.equal(fooSubDir2Result1.value, "testKey");
|
|
1161
|
+
assert.equal(fooSubDir2Result1.done, false);
|
|
1162
|
+
const fooSubDir2Result2 = fooSubDir2Iterator.next();
|
|
1163
|
+
assert.equal(fooSubDir2Result2.value, "testKey2");
|
|
1164
|
+
assert.equal(fooSubDir2Result2.done, false);
|
|
1165
|
+
const fooSubDir2Result3 = fooSubDir2Iterator.next();
|
|
1166
|
+
assert.equal(fooSubDir2Result3.value, undefined);
|
|
1167
|
+
assert.equal(fooSubDir2Result3.done, true);
|
|
1168
|
+
const barSubDir2 = directory2.getWorkingDirectory("/bar");
|
|
1169
|
+
assert(barSubDir2);
|
|
1170
|
+
const barSubDir2Iterator = barSubDir2.keys();
|
|
1171
|
+
const barSubDir2Result1 = barSubDir2Iterator.next();
|
|
1172
|
+
assert.equal(barSubDir2Result1.value, "testKey3");
|
|
1173
|
+
assert.equal(barSubDir2Result1.done, false);
|
|
1174
|
+
const barSubDir2Result2 = barSubDir2Iterator.next();
|
|
1175
|
+
assert.equal(barSubDir2Result2.value, undefined);
|
|
1176
|
+
assert.equal(barSubDir2Result2.done, true);
|
|
1177
|
+
});
|
|
1178
|
+
it("Can get and use a values iterator", () => {
|
|
1179
|
+
const fooDirectory = directory1.createSubDirectory("foo");
|
|
1180
|
+
const barDirectory = directory1.createSubDirectory("bar");
|
|
1181
|
+
const bazDirectory = barDirectory.createSubDirectory("baz");
|
|
1182
|
+
fooDirectory.set("testKey", "testValue");
|
|
1183
|
+
fooDirectory.set("testKey2", "testValue2");
|
|
1184
|
+
barDirectory.set("testKey3", "testValue3");
|
|
1185
|
+
bazDirectory.set("testKey4", "testValue4");
|
|
1186
|
+
containerRuntimeFactory.processAllMessages();
|
|
1187
|
+
// Verify the local SharedDirectory
|
|
1188
|
+
const fooSubDir = directory1.getWorkingDirectory("/foo");
|
|
1189
|
+
assert(fooSubDir);
|
|
1190
|
+
const fooSubDirIterator = fooSubDir.values();
|
|
1191
|
+
const fooSubDirResult1 = fooSubDirIterator.next();
|
|
1192
|
+
assert.equal(fooSubDirResult1.value, "testValue");
|
|
1193
|
+
assert.equal(fooSubDirResult1.done, false);
|
|
1194
|
+
const fooSubDirResult2 = fooSubDirIterator.next();
|
|
1195
|
+
assert.equal(fooSubDirResult2.value, "testValue2");
|
|
1196
|
+
assert.equal(fooSubDirResult2.done, false);
|
|
1197
|
+
const fooSubDirResult3 = fooSubDirIterator.next();
|
|
1198
|
+
assert.equal(fooSubDirResult3.value, undefined);
|
|
1199
|
+
assert.equal(fooSubDirResult3.done, true);
|
|
1200
|
+
const barSubDir = directory1.getWorkingDirectory("/bar");
|
|
1201
|
+
assert(barSubDir);
|
|
1202
|
+
const barSubDirIterator = barSubDir.values();
|
|
1203
|
+
const barSubDirResult1 = barSubDirIterator.next();
|
|
1204
|
+
assert.equal(barSubDirResult1.value, "testValue3");
|
|
1205
|
+
assert.equal(barSubDirResult1.done, false);
|
|
1206
|
+
const barSubDirResult2 = barSubDirIterator.next();
|
|
1207
|
+
assert.equal(barSubDirResult2.value, undefined);
|
|
1208
|
+
assert.equal(barSubDirResult2.done, true);
|
|
1209
|
+
// Verify the remote SharedDirectory
|
|
1210
|
+
const fooSubDir2 = directory2.getWorkingDirectory("/foo");
|
|
1211
|
+
assert(fooSubDir2);
|
|
1212
|
+
const fooSubDir2Iterator = fooSubDir2.values();
|
|
1213
|
+
const fooSubDir2Result1 = fooSubDir2Iterator.next();
|
|
1214
|
+
assert.equal(fooSubDir2Result1.value, "testValue");
|
|
1215
|
+
assert.equal(fooSubDir2Result1.done, false);
|
|
1216
|
+
const fooSubDir2Result2 = fooSubDir2Iterator.next();
|
|
1217
|
+
assert.equal(fooSubDir2Result2.value, "testValue2");
|
|
1218
|
+
assert.equal(fooSubDir2Result2.done, false);
|
|
1219
|
+
const fooSubDir2Result3 = fooSubDir2Iterator.next();
|
|
1220
|
+
assert.equal(fooSubDir2Result3.value, undefined);
|
|
1221
|
+
assert.equal(fooSubDir2Result3.done, true);
|
|
1222
|
+
const barSubDir2 = directory2.getWorkingDirectory("/bar");
|
|
1223
|
+
assert(barSubDir2);
|
|
1224
|
+
const barSubDir2Iterator = barSubDir2.values();
|
|
1225
|
+
const barSubDir2Result1 = barSubDir2Iterator.next();
|
|
1226
|
+
assert.equal(barSubDir2Result1.value, "testValue3");
|
|
1227
|
+
assert.equal(barSubDir2Result1.done, false);
|
|
1228
|
+
const barSubDir2Result2 = barSubDir2Iterator.next();
|
|
1229
|
+
assert.equal(barSubDir2Result2.value, undefined);
|
|
1230
|
+
assert.equal(barSubDir2Result2.done, true);
|
|
1231
|
+
});
|
|
1232
|
+
it("Can get and use an entries iterator", () => {
|
|
1233
|
+
const fooDirectory = directory1.createSubDirectory("foo");
|
|
1234
|
+
const barDirectory = directory1.createSubDirectory("bar");
|
|
1235
|
+
const bazDirectory = barDirectory.createSubDirectory("baz");
|
|
1236
|
+
fooDirectory.set("testKey", "testValue");
|
|
1237
|
+
fooDirectory.set("testKey2", "testValue2");
|
|
1238
|
+
barDirectory.set("testKey3", "testValue3");
|
|
1239
|
+
bazDirectory.set("testKey4", "testValue4");
|
|
1240
|
+
containerRuntimeFactory.processAllMessages();
|
|
1241
|
+
// Verify the local SharedDirectory
|
|
1242
|
+
const fooSubDir = directory1.getWorkingDirectory("/foo");
|
|
1243
|
+
assert(fooSubDir);
|
|
1244
|
+
const fooSubDirIterator = fooSubDir.entries();
|
|
1245
|
+
const fooSubDirResult1 = fooSubDirIterator.next();
|
|
1246
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
1247
|
+
assert.equal(fooSubDirResult1.value[0], "testKey");
|
|
1248
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
1249
|
+
assert.equal(fooSubDirResult1.value[1], "testValue");
|
|
1250
|
+
assert.equal(fooSubDirResult1.done, false);
|
|
1251
|
+
const fooSubDirResult2 = fooSubDirIterator.next();
|
|
1252
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
1253
|
+
assert.equal(fooSubDirResult2.value[0], "testKey2");
|
|
1254
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
1255
|
+
assert.equal(fooSubDirResult2.value[1], "testValue2");
|
|
1256
|
+
assert.equal(fooSubDirResult2.done, false);
|
|
1257
|
+
const fooSubDirResult3 = fooSubDirIterator.next();
|
|
1258
|
+
assert.equal(fooSubDirResult3.value, undefined);
|
|
1259
|
+
assert.equal(fooSubDirResult3.done, true);
|
|
1260
|
+
const barSubDir = directory1.getWorkingDirectory("/bar");
|
|
1261
|
+
assert(barSubDir);
|
|
1262
|
+
const expectedEntries = new Set(["testKey3"]);
|
|
1263
|
+
for (const entry of barSubDir) {
|
|
1264
|
+
assert.ok(expectedEntries.has(entry[0]));
|
|
1265
|
+
expectedEntries.delete(entry[0]);
|
|
1266
|
+
}
|
|
1267
|
+
assert.ok(expectedEntries.size === 0);
|
|
1268
|
+
// Verify the remote SharedDirectory
|
|
1269
|
+
const fooSubDir2 = directory2.getWorkingDirectory("/foo");
|
|
1270
|
+
assert(fooSubDir2);
|
|
1271
|
+
const fooSubDir2Iterator = fooSubDir2.entries();
|
|
1272
|
+
const fooSubDir2Result1 = fooSubDir2Iterator.next();
|
|
1273
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
1274
|
+
assert.equal(fooSubDir2Result1.value[0], "testKey");
|
|
1275
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
1276
|
+
assert.equal(fooSubDir2Result1.value[1], "testValue");
|
|
1277
|
+
assert.equal(fooSubDir2Result1.done, false);
|
|
1278
|
+
const fooSubDir2Result2 = fooSubDir2Iterator.next();
|
|
1279
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
1280
|
+
assert.equal(fooSubDir2Result2.value[0], "testKey2");
|
|
1281
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
1282
|
+
assert.equal(fooSubDir2Result2.value[1], "testValue2");
|
|
1283
|
+
assert.equal(fooSubDir2Result2.done, false);
|
|
1284
|
+
const fooSubDir2Result3 = fooSubDir2Iterator.next();
|
|
1285
|
+
assert.equal(fooSubDir2Result3.value, undefined);
|
|
1286
|
+
assert.equal(fooSubDir2Result3.done, true);
|
|
1287
|
+
const barSubDir2 = directory2.getWorkingDirectory("/bar");
|
|
1288
|
+
assert(barSubDir2);
|
|
1289
|
+
const expectedEntries2 = new Set(["testKey3"]);
|
|
1290
|
+
for (const entry of barSubDir2) {
|
|
1291
|
+
assert.ok(expectedEntries2.has(entry[0]));
|
|
1292
|
+
expectedEntries2.delete(entry[0]);
|
|
1293
|
+
}
|
|
1294
|
+
assert.ok(expectedEntries2.size === 0);
|
|
1295
|
+
});
|
|
1296
|
+
it("Can iterate over its subdirectories", () => {
|
|
1297
|
+
const fooDirectory = directory1.createSubDirectory("foo");
|
|
1298
|
+
fooDirectory.createSubDirectory("bar");
|
|
1299
|
+
fooDirectory.createSubDirectory("baz");
|
|
1300
|
+
containerRuntimeFactory.processAllMessages();
|
|
1301
|
+
// Verify the local SharedDirectory
|
|
1302
|
+
const expectedDirectories = new Set(["bar", "baz"]);
|
|
1303
|
+
for (const [subDirName] of fooDirectory.subdirectories()) {
|
|
1304
|
+
assert.ok(expectedDirectories.has(subDirName));
|
|
1305
|
+
expectedDirectories.delete(subDirName);
|
|
1306
|
+
}
|
|
1307
|
+
assert.ok(expectedDirectories.size === 0);
|
|
1308
|
+
// Verify the remote SharedDirectory
|
|
1309
|
+
const fooDirectory2 = directory2.getSubDirectory("foo");
|
|
1310
|
+
assert(fooDirectory2);
|
|
1311
|
+
const expectedDirectories2 = new Set(["bar", "baz"]);
|
|
1312
|
+
for (const [subDirName] of fooDirectory2.subdirectories()) {
|
|
1313
|
+
assert.ok(expectedDirectories2.has(subDirName));
|
|
1314
|
+
expectedDirectories2.delete(subDirName);
|
|
1315
|
+
}
|
|
1316
|
+
assert.ok(expectedDirectories2.size === 0);
|
|
1317
|
+
});
|
|
1318
|
+
it("Only creates a subDirectory once", () => {
|
|
1319
|
+
const fooDirectory = directory1.createSubDirectory("foo");
|
|
1320
|
+
fooDirectory.set("testKey", "testValue");
|
|
1321
|
+
const fooDirectory2 = directory1.createSubDirectory("foo");
|
|
1322
|
+
fooDirectory2.set("testKey2", "testValue2");
|
|
1323
|
+
assert.strictEqual(fooDirectory, fooDirectory2, "Created two separate subdirectories");
|
|
1324
|
+
assert.strictEqual(fooDirectory.get("testKey2"), "testValue2", "Value 2 not present");
|
|
1325
|
+
assert.strictEqual(fooDirectory2.get("testKey"), "testValue", "Value 1 not present");
|
|
1326
|
+
});
|
|
1327
|
+
});
|
|
1328
|
+
});
|
|
1329
|
+
describe("Garbage Collection", () => {
|
|
1330
|
+
class GCSharedDirectoryProvider {
|
|
1331
|
+
constructor() {
|
|
1332
|
+
this.subMapCount = 0;
|
|
1333
|
+
this._expectedRoutes = [];
|
|
1334
|
+
this.containerRuntimeFactory = new MockContainerRuntimeFactory();
|
|
1335
|
+
this.directory1 = createConnectedDirectory("directory1", this.containerRuntimeFactory);
|
|
1336
|
+
this.directory2 = createConnectedDirectory("directory2", this.containerRuntimeFactory);
|
|
1337
|
+
}
|
|
1338
|
+
/**
|
|
1339
|
+
* {@inheritDoc @fluid-private/test-dds-utils#IGCTestProvider.sharedObject}
|
|
1340
|
+
*/
|
|
1341
|
+
get sharedObject() {
|
|
1342
|
+
// Return the remote SharedDirectory because we want to verify its summary data.
|
|
1343
|
+
return this.directory2;
|
|
1344
|
+
}
|
|
1345
|
+
/**
|
|
1346
|
+
* {@inheritDoc @fluid-private/test-dds-utils#IGCTestProvider.expectedOutboundRoutes}
|
|
1347
|
+
*/
|
|
1348
|
+
get expectedOutboundRoutes() {
|
|
1349
|
+
return this._expectedRoutes;
|
|
1350
|
+
}
|
|
1351
|
+
/**
|
|
1352
|
+
* {@inheritDoc @fluid-private/test-dds-utils#IGCTestProvider.addOutboundRoutes}
|
|
1353
|
+
*/
|
|
1354
|
+
async addOutboundRoutes() {
|
|
1355
|
+
const subMapId1 = `subMap-${++this.subMapCount}`;
|
|
1356
|
+
const subMap1 = createLocalMap(subMapId1);
|
|
1357
|
+
this.directory1.set(subMapId1, subMap1.handle);
|
|
1358
|
+
this._expectedRoutes.push(subMap1.handle.absolutePath);
|
|
1359
|
+
const fooDirectory = this.directory1.getSubDirectory("foo") ??
|
|
1360
|
+
this.directory1.createSubDirectory("foo");
|
|
1361
|
+
const subMapId2 = `subMap-${++this.subMapCount}`;
|
|
1362
|
+
const subMap2 = createLocalMap(subMapId2);
|
|
1363
|
+
fooDirectory.set(subMapId2, subMap2.handle);
|
|
1364
|
+
this._expectedRoutes.push(subMap2.handle.absolutePath);
|
|
1365
|
+
this.containerRuntimeFactory.processAllMessages();
|
|
1366
|
+
}
|
|
1367
|
+
/**
|
|
1368
|
+
* {@inheritDoc @fluid-private/test-dds-utils#IGCTestProvider.deleteOutboundRoutes}
|
|
1369
|
+
*/
|
|
1370
|
+
async deleteOutboundRoutes() {
|
|
1371
|
+
// Delete the last handle that was added.
|
|
1372
|
+
const fooDirectory = this.directory1.getSubDirectory("foo");
|
|
1373
|
+
assert(fooDirectory, "Route must be added before deleting");
|
|
1374
|
+
const subMapId = `subMap-${this.subMapCount}`;
|
|
1375
|
+
const deletedHandle = fooDirectory.get(subMapId);
|
|
1376
|
+
assert(deletedHandle, "Route must be added before deleting");
|
|
1377
|
+
fooDirectory.delete(subMapId);
|
|
1378
|
+
// Remove deleted handle's route from expected routes.
|
|
1379
|
+
this._expectedRoutes = this._expectedRoutes.filter((route) => route !== deletedHandle.absolutePath);
|
|
1380
|
+
this.containerRuntimeFactory.processAllMessages();
|
|
1381
|
+
}
|
|
1382
|
+
/**
|
|
1383
|
+
* {@inheritDoc @fluid-private/test-dds-utils#IGCTestProvider.addNestedHandles}
|
|
1384
|
+
*/
|
|
1385
|
+
async addNestedHandles() {
|
|
1386
|
+
const fooDirectory = this.directory1.getSubDirectory("foo") ??
|
|
1387
|
+
this.directory1.createSubDirectory("foo");
|
|
1388
|
+
const subMapId1 = `subMap-${++this.subMapCount}`;
|
|
1389
|
+
const subMapId2 = `subMap-${++this.subMapCount}`;
|
|
1390
|
+
const subMap = createLocalMap(subMapId1);
|
|
1391
|
+
const subMap2 = createLocalMap(subMapId2);
|
|
1392
|
+
const containingObject = {
|
|
1393
|
+
subMapHandle: subMap.handle,
|
|
1394
|
+
nestedObj: {
|
|
1395
|
+
subMap2Handle: subMap2.handle,
|
|
1396
|
+
},
|
|
1397
|
+
};
|
|
1398
|
+
fooDirectory.set(subMapId2, containingObject);
|
|
1399
|
+
this.containerRuntimeFactory.processAllMessages();
|
|
1400
|
+
this._expectedRoutes.push(subMap.handle.absolutePath, subMap2.handle.absolutePath);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
runGCTests(GCSharedDirectoryProvider);
|
|
1404
|
+
});
|
|
1405
|
+
});
|
|
1406
|
+
//# sourceMappingURL=directory.spec.js.map
|