@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.
Files changed (132) hide show
  1. package/{.eslintrc.js → .eslintrc.cjs} +10 -1
  2. package/{.mocharc.js → .mocharc.cjs} +1 -1
  3. package/CHANGELOG.md +11 -0
  4. package/{api-extractor-esm.json → api-extractor-cjs.json} +5 -1
  5. package/api-extractor-lint.json +1 -1
  6. package/api-extractor.json +1 -1
  7. package/api-report/map.api.md +14 -57
  8. package/dist/directory.d.ts +10 -50
  9. package/dist/directory.d.ts.map +1 -1
  10. package/dist/directory.js +76 -164
  11. package/dist/directory.js.map +1 -1
  12. package/dist/index.d.ts +45 -4
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +43 -8
  15. package/dist/index.js.map +1 -1
  16. package/dist/interfaces.d.ts.map +1 -1
  17. package/dist/interfaces.js.map +1 -1
  18. package/dist/internalInterfaces.d.ts +2 -2
  19. package/dist/internalInterfaces.d.ts.map +1 -1
  20. package/dist/internalInterfaces.js.map +1 -1
  21. package/dist/localValues.d.ts +3 -5
  22. package/dist/localValues.d.ts.map +1 -1
  23. package/dist/localValues.js +9 -8
  24. package/dist/localValues.js.map +1 -1
  25. package/dist/map-alpha.d.ts +31 -116
  26. package/dist/map-beta.d.ts +24 -105
  27. package/dist/map-public.d.ts +24 -105
  28. package/dist/map-untrimmed.d.ts +31 -116
  29. package/dist/map.d.ts +4 -23
  30. package/dist/map.d.ts.map +1 -1
  31. package/dist/map.js +6 -29
  32. package/dist/map.js.map +1 -1
  33. package/dist/mapKernel.d.ts +3 -4
  34. package/dist/mapKernel.d.ts.map +1 -1
  35. package/dist/mapKernel.js +30 -35
  36. package/dist/mapKernel.js.map +1 -1
  37. package/dist/package.json +3 -0
  38. package/dist/packageVersion.d.ts +1 -1
  39. package/dist/packageVersion.js +1 -1
  40. package/dist/packageVersion.js.map +1 -1
  41. package/dist/tsdoc-metadata.json +1 -1
  42. package/lib/{directory.d.mts → directory.d.ts} +11 -51
  43. package/lib/directory.d.ts.map +1 -0
  44. package/lib/{directory.mjs → directory.js} +77 -165
  45. package/lib/directory.js.map +1 -0
  46. package/lib/index.d.ts +61 -0
  47. package/lib/index.d.ts.map +1 -0
  48. package/lib/index.js +55 -0
  49. package/lib/index.js.map +1 -0
  50. package/lib/{interfaces.d.mts → interfaces.d.ts} +1 -1
  51. package/lib/interfaces.d.ts.map +1 -0
  52. package/lib/{interfaces.mjs → interfaces.js} +1 -1
  53. package/lib/interfaces.js.map +1 -0
  54. package/lib/{internalInterfaces.d.mts → internalInterfaces.d.ts} +3 -3
  55. package/lib/internalInterfaces.d.ts.map +1 -0
  56. package/lib/{internalInterfaces.mjs → internalInterfaces.js} +1 -1
  57. package/lib/internalInterfaces.js.map +1 -0
  58. package/lib/{localValues.d.mts → localValues.d.ts} +4 -6
  59. package/lib/localValues.d.ts.map +1 -0
  60. package/lib/{localValues.mjs → localValues.js} +10 -9
  61. package/lib/localValues.js.map +1 -0
  62. package/lib/{map-alpha.d.mts → map-alpha.d.ts} +43 -116
  63. package/lib/{map-beta.d.mts → map-beta.d.ts} +36 -105
  64. package/lib/{map-public.d.mts → map-public.d.ts} +36 -105
  65. package/lib/{map-untrimmed.d.mts → map-untrimmed.d.ts} +43 -116
  66. package/lib/{map.d.mts → map.d.ts} +5 -24
  67. package/lib/map.d.ts.map +1 -0
  68. package/lib/{map.mjs → map.js} +5 -28
  69. package/lib/map.js.map +1 -0
  70. package/lib/{mapKernel.d.mts → mapKernel.d.ts} +4 -5
  71. package/lib/mapKernel.d.ts.map +1 -0
  72. package/lib/{mapKernel.mjs → mapKernel.js} +32 -37
  73. package/lib/mapKernel.js.map +1 -0
  74. package/lib/{packageVersion.d.mts → packageVersion.d.ts} +2 -2
  75. package/lib/packageVersion.d.ts.map +1 -0
  76. package/lib/{packageVersion.mjs → packageVersion.js} +2 -2
  77. package/lib/packageVersion.js.map +1 -0
  78. package/lib/test/memory/directory.spec.js +71 -0
  79. package/lib/test/memory/directory.spec.js.map +1 -0
  80. package/lib/test/memory/map.spec.js +71 -0
  81. package/lib/test/memory/map.spec.js.map +1 -0
  82. package/lib/test/mocha/directory.order.spec.js +422 -0
  83. package/lib/test/mocha/directory.order.spec.js.map +1 -0
  84. package/lib/test/mocha/directory.snapshot.spec.js +111 -0
  85. package/lib/test/mocha/directory.snapshot.spec.js.map +1 -0
  86. package/lib/test/mocha/directory.spec.js +1406 -0
  87. package/lib/test/mocha/directory.spec.js.map +1 -0
  88. package/lib/test/mocha/directoryEquivalenceUtils.js +36 -0
  89. package/lib/test/mocha/directoryEquivalenceUtils.js.map +1 -0
  90. package/lib/test/mocha/directoryFuzzTests.spec.js +337 -0
  91. package/lib/test/mocha/directoryFuzzTests.spec.js.map +1 -0
  92. package/lib/test/mocha/dirname.cjs +16 -0
  93. package/lib/test/mocha/dirname.cjs.map +1 -0
  94. package/lib/test/mocha/map.fuzz.spec.js +114 -0
  95. package/lib/test/mocha/map.fuzz.spec.js.map +1 -0
  96. package/lib/test/mocha/map.spec.js +685 -0
  97. package/lib/test/mocha/map.spec.js.map +1 -0
  98. package/lib/test/mocha/rebasing.spec.js +158 -0
  99. package/lib/test/mocha/rebasing.spec.js.map +1 -0
  100. package/lib/test/mocha/reconnection.spec.js +327 -0
  101. package/lib/test/mocha/reconnection.spec.js.map +1 -0
  102. package/lib/test/types/validateMapPrevious.generated.js +66 -0
  103. package/lib/test/types/validateMapPrevious.generated.js.map +1 -0
  104. package/package.json +55 -52
  105. package/src/directory.ts +122 -217
  106. package/src/index.ts +57 -4
  107. package/src/interfaces.ts +2 -2
  108. package/src/internalInterfaces.ts +2 -2
  109. package/src/localValues.ts +14 -9
  110. package/src/map.ts +7 -32
  111. package/src/mapKernel.ts +40 -42
  112. package/src/packageVersion.ts +1 -1
  113. package/tsconfig.cjs.json +7 -0
  114. package/tsconfig.json +2 -5
  115. package/lib/directory.d.mts.map +0 -1
  116. package/lib/directory.mjs.map +0 -1
  117. package/lib/index.d.mts +0 -9
  118. package/lib/index.d.mts.map +0 -1
  119. package/lib/index.mjs +0 -8
  120. package/lib/index.mjs.map +0 -1
  121. package/lib/interfaces.d.mts.map +0 -1
  122. package/lib/interfaces.mjs.map +0 -1
  123. package/lib/internalInterfaces.d.mts.map +0 -1
  124. package/lib/internalInterfaces.mjs.map +0 -1
  125. package/lib/localValues.d.mts.map +0 -1
  126. package/lib/localValues.mjs.map +0 -1
  127. package/lib/map.d.mts.map +0 -1
  128. package/lib/map.mjs.map +0 -1
  129. package/lib/mapKernel.d.mts.map +0 -1
  130. package/lib/mapKernel.mjs.map +0 -1
  131. package/lib/packageVersion.d.mts.map +0 -1
  132. package/lib/packageVersion.mjs.map +0 -1
@@ -0,0 +1,337 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import * as dirPath from "node:path";
6
+ import { strict as assert } from "node:assert";
7
+ import { combineReducersAsync, createWeightedAsyncGenerator, takeAsync, } from "@fluid-private/stochastic-test-utils";
8
+ import { createDDSFuzzSuite, } from "@fluid-private/test-dds-utils";
9
+ import { FlushMode } from "@fluidframework/runtime-definitions";
10
+ import { DirectoryFactory } from "../../directory.js";
11
+ import { assertEquivalentDirectories } from "./directoryEquivalenceUtils.js";
12
+ import { _dirname } from "./dirname.cjs";
13
+ const defaultOptions = {
14
+ validateInterval: 10,
15
+ maxSubDirectoryChild: 3,
16
+ subDirectoryNamePool: ["dir1", "dir2", "dir3"],
17
+ keyNamePool: ["prop1", "prop2", "prop3"],
18
+ setKeyWeight: 5,
19
+ deleteKeyWeight: 2,
20
+ clearKeysWeight: 1,
21
+ createSubDirWeight: 2,
22
+ deleteSubDirWeight: 1,
23
+ };
24
+ function makeOperationGenerator(optionsParam) {
25
+ const options = { ...defaultOptions, ...optionsParam };
26
+ // All subsequent helper functions are generators; note that they don't actually apply any operations.
27
+ function pickAbsolutePathForCreateDirectoryOp(state) {
28
+ const { random, client } = state;
29
+ let dir = client.channel;
30
+ for (;;) {
31
+ assert(dir !== undefined, "Directory should be defined");
32
+ const subDirectories = [];
33
+ for (const [_, b] of dir.subdirectories()) {
34
+ subDirectories.push(b);
35
+ }
36
+ // If this dir already has max number of child, then choose one and continue.
37
+ if (dir.countSubDirectory !== undefined &&
38
+ dir.countSubDirectory() === options.maxSubDirectoryChild) {
39
+ dir = random.pick(subDirectories);
40
+ continue;
41
+ }
42
+ const subDir = random.pick([undefined, ...subDirectories]);
43
+ if (subDir === undefined) {
44
+ break;
45
+ }
46
+ else {
47
+ dir = subDir;
48
+ }
49
+ }
50
+ return dir.absolutePath;
51
+ }
52
+ function pickAbsolutePathForDeleteDirectoryOp(state) {
53
+ const { random, client } = state;
54
+ let parentDir = client.channel;
55
+ const subDirectories = [];
56
+ for (const [_, b] of client.channel.subdirectories()) {
57
+ subDirectories.push(b);
58
+ }
59
+ let dirToDelete = random.pick(subDirectories);
60
+ for (;;) {
61
+ assert(dirToDelete !== undefined, "Directory should be defined");
62
+ const subDirs = [];
63
+ for (const [_, b] of dirToDelete.subdirectories()) {
64
+ subDirs.push(b);
65
+ }
66
+ const subDir = random.pick([undefined, ...subDirs]);
67
+ if (subDir === undefined) {
68
+ break;
69
+ }
70
+ else {
71
+ parentDir = dirToDelete;
72
+ dirToDelete = subDir;
73
+ }
74
+ }
75
+ return parentDir.absolutePath;
76
+ }
77
+ function pickAbsolutePathForKeyOps(state, shouldHaveKey) {
78
+ const { random, client } = state;
79
+ let parentDir = client.channel;
80
+ for (;;) {
81
+ assert(parentDir !== undefined, "Directory should be defined");
82
+ const subDirs = [];
83
+ for (const [_, b] of parentDir.subdirectories()) {
84
+ subDirs.push(b);
85
+ }
86
+ const subDir = random.pick([undefined, ...subDirs]);
87
+ if (subDir !== undefined && (!shouldHaveKey || subDir.size > 0)) {
88
+ parentDir = subDir;
89
+ }
90
+ else {
91
+ break;
92
+ }
93
+ }
94
+ return parentDir.absolutePath;
95
+ }
96
+ async function createSubDirectory(state) {
97
+ return {
98
+ type: "createSubDirectory",
99
+ name: state.random.pick(options.subDirectoryNamePool),
100
+ path: pickAbsolutePathForCreateDirectoryOp(state),
101
+ };
102
+ }
103
+ async function deleteSubDirectory(state) {
104
+ const { random, client } = state;
105
+ const path = pickAbsolutePathForDeleteDirectoryOp(state);
106
+ const parentDir = client.channel.getWorkingDirectory(path);
107
+ assert(parentDir !== undefined, "parent dir should be defined");
108
+ assert(parentDir.countSubDirectory && parentDir.countSubDirectory() > 0, "Atleast 1 subdir should be there");
109
+ const subDirName = [];
110
+ for (const [a, _] of parentDir.subdirectories()) {
111
+ subDirName.push(a);
112
+ }
113
+ return {
114
+ type: "deleteSubDirectory",
115
+ name: random.pick(subDirName),
116
+ path,
117
+ };
118
+ }
119
+ async function setKey(state) {
120
+ const { random } = state;
121
+ return {
122
+ type: "set",
123
+ key: random.pick(options.keyNamePool),
124
+ path: pickAbsolutePathForKeyOps(state, false),
125
+ value: random.string(random.integer(0, 4)),
126
+ };
127
+ }
128
+ async function clearKeys(state) {
129
+ return {
130
+ type: "clear",
131
+ path: pickAbsolutePathForKeyOps(state, true),
132
+ };
133
+ }
134
+ async function deleteKey(state) {
135
+ const { random, client } = state;
136
+ const path = pickAbsolutePathForKeyOps(state, true);
137
+ const dir = client.channel.getWorkingDirectory(path);
138
+ assert(dir, "dir should exist");
139
+ return {
140
+ type: "delete",
141
+ key: random.pick([...dir.keys()]),
142
+ path,
143
+ };
144
+ }
145
+ return createWeightedAsyncGenerator([
146
+ [createSubDirectory, options.createSubDirWeight],
147
+ [
148
+ deleteSubDirectory,
149
+ options.deleteSubDirWeight,
150
+ (state) => (state.client.channel.countSubDirectory?.() ?? 0) > 0,
151
+ ],
152
+ [setKey, options.setKeyWeight],
153
+ [
154
+ deleteKey,
155
+ options.deleteKeyWeight,
156
+ (state) => state.client.channel.size > 0,
157
+ ],
158
+ [
159
+ clearKeys,
160
+ options.clearKeysWeight,
161
+ (state) => state.client.channel.size > 0,
162
+ ],
163
+ ]);
164
+ }
165
+ function logCurrentState(clients, loggingInfo) {
166
+ for (const id of loggingInfo.clientIds) {
167
+ const { channel: sharedDirectory } = clients.find((s) => s.containerRuntime.clientId === id) ?? {};
168
+ if (sharedDirectory !== undefined) {
169
+ console.log(`Client ${id}:`);
170
+ console.log(JSON.stringify(sharedDirectory.getAttachSummary(true).summary, undefined, 4));
171
+ console.log("\n");
172
+ }
173
+ }
174
+ }
175
+ function makeReducer(loggingInfo) {
176
+ const withLogging = (baseReducer) => async (state, operation) => {
177
+ if (loggingInfo !== undefined && loggingInfo.printConsoleLogs) {
178
+ logCurrentState(state.clients, loggingInfo);
179
+ console.log("-".repeat(20));
180
+ console.log("Next operation:", JSON.stringify(operation, undefined, 4));
181
+ }
182
+ try {
183
+ await baseReducer(state, operation);
184
+ }
185
+ catch (error) {
186
+ if (loggingInfo !== undefined) {
187
+ logCurrentState(state.clients, loggingInfo);
188
+ }
189
+ throw error;
190
+ }
191
+ return state;
192
+ };
193
+ const reducer = combineReducersAsync({
194
+ createSubDirectory: async ({ client }, { path, name }) => {
195
+ const dir = client.channel.getWorkingDirectory(path);
196
+ assert(dir);
197
+ dir.createSubDirectory(name);
198
+ },
199
+ deleteSubDirectory: async ({ client }, { path, name }) => {
200
+ const dir = client.channel.getWorkingDirectory(path);
201
+ assert(dir);
202
+ dir.deleteSubDirectory(name);
203
+ },
204
+ set: async ({ client }, { path, key, value }) => {
205
+ const dir = client.channel.getWorkingDirectory(path);
206
+ assert(dir);
207
+ dir.set(key, value);
208
+ },
209
+ clear: async ({ client }, { path }) => {
210
+ const dir = client.channel.getWorkingDirectory(path);
211
+ assert(dir);
212
+ dir.clear();
213
+ },
214
+ delete: async ({ client }, { path, key }) => {
215
+ const dir = client.channel.getWorkingDirectory(path);
216
+ assert(dir);
217
+ dir.delete(key);
218
+ },
219
+ });
220
+ return withLogging(reducer);
221
+ }
222
+ describe("SharedDirectory fuzz Create/Delete concentrated", () => {
223
+ const options = {
224
+ setKeyWeight: 0,
225
+ clearKeysWeight: 0,
226
+ deleteKeyWeight: 0,
227
+ createSubDirWeight: 2,
228
+ deleteSubDirWeight: 2,
229
+ maxSubDirectoryChild: 2,
230
+ subDirectoryNamePool: ["dir1", "dir2"],
231
+ validateInterval: defaultOptions.validateInterval,
232
+ };
233
+ const model = {
234
+ workloadName: "default directory 1",
235
+ generatorFactory: () => takeAsync(100, makeOperationGenerator(options)),
236
+ reducer: makeReducer({ clientIds: ["A", "B", "C"], printConsoleLogs: false }),
237
+ validateConsistency: assertEquivalentDirectories,
238
+ factory: new DirectoryFactory(),
239
+ };
240
+ createDDSFuzzSuite(model, {
241
+ validationStrategy: { type: "fixedInterval", interval: defaultOptions.validateInterval },
242
+ reconnectProbability: 0.15,
243
+ numberOfClients: 3,
244
+ clientJoinOptions: {
245
+ maxNumberOfClients: 3,
246
+ clientAddProbability: 0.08,
247
+ stashableClientProbability: 0.2,
248
+ },
249
+ defaultTestCount: 25,
250
+ // Uncomment this line to replay a specific seed from its failure file:
251
+ // replay: 21,
252
+ saveFailures: { directory: dirPath.join(_dirname, "../../../src/test/mocha/results/1") },
253
+ });
254
+ createDDSFuzzSuite({ ...model, workloadName: "default directory 1 with rebasing" }, {
255
+ validationStrategy: {
256
+ type: "random",
257
+ probability: 0.4,
258
+ },
259
+ rebaseProbability: 0.2,
260
+ reconnectProbability: 0.5,
261
+ containerRuntimeOptions: {
262
+ flushMode: FlushMode.TurnBased,
263
+ enableGroupedBatching: true,
264
+ },
265
+ numberOfClients: 3,
266
+ clientJoinOptions: {
267
+ maxNumberOfClients: 3,
268
+ clientAddProbability: 0.08,
269
+ stashableClientProbability: undefined,
270
+ },
271
+ defaultTestCount: 200,
272
+ // The seeds below fail only when rebaseProbability is non-zero ADO:6044
273
+ skip: [
274
+ 13, 40, 43, 55, 66, 93, 94, 107, 110, 123, 136, 148, 160, 163, 168, 172, 177, 191,
275
+ 196,
276
+ ],
277
+ // Uncomment this line to replay a specific seed from its failure file:
278
+ // replay: 21,
279
+ saveFailures: {
280
+ directory: dirPath.join(_dirname, "../../../src/test/mocha/results/1"),
281
+ },
282
+ });
283
+ });
284
+ describe("SharedDirectory fuzz", () => {
285
+ const model = {
286
+ workloadName: "default directory 2",
287
+ generatorFactory: () => takeAsync(100, makeOperationGenerator()),
288
+ reducer: makeReducer({ clientIds: ["A", "B", "C"], printConsoleLogs: false }),
289
+ validateConsistency: assertEquivalentDirectories,
290
+ factory: new DirectoryFactory(),
291
+ };
292
+ createDDSFuzzSuite(model, {
293
+ validationStrategy: { type: "fixedInterval", interval: defaultOptions.validateInterval },
294
+ reconnectProbability: 0.15,
295
+ numberOfClients: 3,
296
+ clientJoinOptions: {
297
+ // Note: if tests are slow, we may want to tune this down. This mimics behavior before this suite
298
+ // was refactored to use the DDS fuzz harness.
299
+ maxNumberOfClients: Number.MAX_SAFE_INTEGER,
300
+ clientAddProbability: 0.08,
301
+ stashableClientProbability: 0.2,
302
+ },
303
+ defaultTestCount: 25,
304
+ // Uncomment this line to replay a specific seed from its failure file:
305
+ // replay: 0,
306
+ saveFailures: { directory: dirPath.join(_dirname, "../../../src/test/mocha/results/2") },
307
+ });
308
+ createDDSFuzzSuite({ ...model, workloadName: "default directory 2 with rebasing" }, {
309
+ validationStrategy: {
310
+ type: "random",
311
+ probability: 0.4,
312
+ },
313
+ rebaseProbability: 0.2,
314
+ reconnectProbability: 0.5,
315
+ containerRuntimeOptions: {
316
+ flushMode: FlushMode.TurnBased,
317
+ enableGroupedBatching: true,
318
+ },
319
+ numberOfClients: 3,
320
+ clientJoinOptions: {
321
+ // Note: if tests are slow, we may want to tune this down. This mimics behavior before this suite
322
+ // was refactored to use the DDS fuzz harness.
323
+ maxNumberOfClients: Number.MAX_SAFE_INTEGER,
324
+ clientAddProbability: 0.08,
325
+ stashableClientProbability: undefined,
326
+ },
327
+ defaultTestCount: 200,
328
+ // The seeds below fail only when rebaseProbability is non-zero ADO:6044
329
+ skip: [73],
330
+ // Uncomment this line to replay a specific seed from its failure file:
331
+ // replay: 0,
332
+ saveFailures: {
333
+ directory: dirPath.join(_dirname, "../../../src/test/mocha/results/2"),
334
+ },
335
+ });
336
+ });
337
+ //# sourceMappingURL=directoryFuzzTests.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"directoryFuzzTests.spec.js","sourceRoot":"","sources":["../../../src/test/mocha/directoryFuzzTests.spec.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,OAAO,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,MAAM,IAAI,MAAM,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAGN,oBAAoB,EACpB,4BAA4B,EAC5B,SAAS,GACT,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAEN,kBAAkB,GAGlB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAoDzC,MAAM,cAAc,GAAwC;IAC3D,gBAAgB,EAAE,EAAE;IACpB,oBAAoB,EAAE,CAAC;IACvB,oBAAoB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;IAC9C,WAAW,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACxC,YAAY,EAAE,CAAC;IACf,eAAe,EAAE,CAAC;IAClB,eAAe,EAAE,CAAC;IAClB,kBAAkB,EAAE,CAAC;IACrB,kBAAkB,EAAE,CAAC;CACrB,CAAC;AAEF,SAAS,sBAAsB,CAC9B,YAAwC;IAExC,MAAM,OAAO,GAAG,EAAE,GAAG,cAAc,EAAE,GAAG,YAAY,EAAE,CAAC;IAEvD,sGAAsG;IACtG,SAAS,oCAAoC,CAAC,KAAoB;QACjE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;QACjC,IAAI,GAAG,GAAe,MAAM,CAAC,OAAO,CAAC;QACrC,SAAS;YACR,MAAM,CAAC,GAAG,KAAK,SAAS,EAAE,6BAA6B,CAAC,CAAC;YACzD,MAAM,cAAc,GAAiB,EAAE,CAAC;YACxC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC,cAAc,EAAE,EAAE;gBAC1C,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;aACvB;YACD,6EAA6E;YAC7E,IACC,GAAG,CAAC,iBAAiB,KAAK,SAAS;gBACnC,GAAG,CAAC,iBAAiB,EAAE,KAAK,OAAO,CAAC,oBAAoB,EACvD;gBACD,GAAG,GAAG,MAAM,CAAC,IAAI,CAAa,cAAc,CAAC,CAAC;gBAC9C,SAAS;aACT;YACD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAyB,CAAC,SAAS,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC;YACnF,IAAI,MAAM,KAAK,SAAS,EAAE;gBACzB,MAAM;aACN;iBAAM;gBACN,GAAG,GAAG,MAAM,CAAC;aACb;SACD;QACD,OAAO,GAAG,CAAC,YAAY,CAAC;IACzB,CAAC;IAED,SAAS,oCAAoC,CAAC,KAAoB;QACjE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;QACjC,IAAI,SAAS,GAAe,MAAM,CAAC,OAAO,CAAC;QAC3C,MAAM,cAAc,GAAiB,EAAE,CAAC;QACxC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE;YACrD,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SACvB;QACD,IAAI,WAAW,GAAG,MAAM,CAAC,IAAI,CAAa,cAAc,CAAC,CAAC;QAC1D,SAAS;YACR,MAAM,CAAC,WAAW,KAAK,SAAS,EAAE,6BAA6B,CAAC,CAAC;YACjE,MAAM,OAAO,GAAiB,EAAE,CAAC;YACjC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,WAAW,CAAC,cAAc,EAAE,EAAE;gBAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;aAChB;YACD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAyB,CAAC,SAAS,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC;YAC5E,IAAI,MAAM,KAAK,SAAS,EAAE;gBACzB,MAAM;aACN;iBAAM;gBACN,SAAS,GAAG,WAAW,CAAC;gBACxB,WAAW,GAAG,MAAM,CAAC;aACrB;SACD;QACD,OAAO,SAAS,CAAC,YAAY,CAAC;IAC/B,CAAC;IAED,SAAS,yBAAyB,CAAC,KAAoB,EAAE,aAAsB;QAC9E,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;QACjC,IAAI,SAAS,GAAe,MAAM,CAAC,OAAO,CAAC;QAC3C,SAAS;YACR,MAAM,CAAC,SAAS,KAAK,SAAS,EAAE,6BAA6B,CAAC,CAAC;YAC/D,MAAM,OAAO,GAAiB,EAAE,CAAC;YACjC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,SAAS,CAAC,cAAc,EAAE,EAAE;gBAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;aAChB;YACD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAyB,CAAC,SAAS,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC;YAC5E,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,aAAa,IAAI,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,EAAE;gBAChE,SAAS,GAAG,MAAM,CAAC;aACnB;iBAAM;gBACN,MAAM;aACN;SACD;QACD,OAAO,SAAS,CAAC,YAAY,CAAC;IAC/B,CAAC;IAED,KAAK,UAAU,kBAAkB,CAAC,KAAoB;QACrD,OAAO;YACN,IAAI,EAAE,oBAAoB;YAC1B,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAAC;YACrD,IAAI,EAAE,oCAAoC,CAAC,KAAK,CAAC;SACjD,CAAC;IACH,CAAC;IAED,KAAK,UAAU,kBAAkB,CAAC,KAAoB;QACrD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;QACjC,MAAM,IAAI,GAAG,oCAAoC,CAAC,KAAK,CAAC,CAAC;QACzD,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAC3D,MAAM,CAAC,SAAS,KAAK,SAAS,EAAE,8BAA8B,CAAC,CAAC;QAChE,MAAM,CACL,SAAS,CAAC,iBAAiB,IAAI,SAAS,CAAC,iBAAiB,EAAE,GAAG,CAAC,EAChE,kCAAkC,CAClC,CAAC;QACF,MAAM,UAAU,GAAa,EAAE,CAAC;QAChC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,SAAS,CAAC,cAAc,EAAE,EAAE;YAChD,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SACnB;QACD,OAAO;YACN,IAAI,EAAE,oBAAoB;YAC1B,IAAI,EAAE,MAAM,CAAC,IAAI,CAAS,UAAU,CAAC;YACrC,IAAI;SACJ,CAAC;IACH,CAAC;IAED,KAAK,UAAU,MAAM,CAAC,KAAoB;QACzC,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;QACzB,OAAO;YACN,IAAI,EAAE,KAAK;YACX,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC;YACrC,IAAI,EAAE,yBAAyB,CAAC,KAAK,EAAE,KAAK,CAAC;YAC7C,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;SAC1C,CAAC;IACH,CAAC;IAED,KAAK,UAAU,SAAS,CAAC,KAAoB;QAC5C,OAAO;YACN,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,yBAAyB,CAAC,KAAK,EAAE,IAAI,CAAC;SAC5C,CAAC;IACH,CAAC;IAED,KAAK,UAAU,SAAS,CAAC,KAAoB;QAC5C,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;QACjC,MAAM,IAAI,GAAG,yBAAyB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACpD,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QACrD,MAAM,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC;QAChC,OAAO;YACN,IAAI,EAAE,QAAQ;YACd,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;YACjC,IAAI;SACJ,CAAC;IACH,CAAC;IAED,OAAO,4BAA4B,CAA2B;QAC7D,CAAC,kBAAkB,EAAE,OAAO,CAAC,kBAAkB,CAAC;QAChD;YACC,kBAAkB;YAClB,OAAO,CAAC,kBAAkB;YAC1B,CAAC,KAAoB,EAAW,EAAE,CACjC,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC;SACtD;QACD,CAAC,MAAM,EAAE,OAAO,CAAC,YAAY,CAAC;QAC9B;YACC,SAAS;YACT,OAAO,CAAC,eAAe;YACvB,CAAC,KAAoB,EAAW,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC;SAChE;QACD;YACC,SAAS;YACT,OAAO,CAAC,eAAe;YACvB,CAAC,KAAoB,EAAW,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC;SAChE;KACD,CAAC,CAAC;AACJ,CAAC;AASD,SAAS,eAAe,CAAC,OAAmC,EAAE,WAAwB;IACrF,KAAK,MAAM,EAAE,IAAI,WAAW,CAAC,SAAS,EAAE;QACvC,MAAM,EAAE,OAAO,EAAE,eAAe,EAAE,GACjC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,gBAAgB,CAAC,QAAQ,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC;QAC/D,IAAI,eAAe,KAAK,SAAS,EAAE;YAClC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;YAC7B,OAAO,CAAC,GAAG,CACV,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC,CAC5E,CAAC;YACF,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;SAClB;KACD;AACF,CAAC;AAED,SAAS,WAAW,CAAC,WAAyB;IAC7C,MAAM,WAAW,GAChB,CAAI,WAA2C,EAAkC,EAAE,CACnF,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE;QAC1B,IAAI,WAAW,KAAK,SAAS,IAAI,WAAW,CAAC,gBAAgB,EAAE;YAC9D,eAAe,CAAC,KAAK,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;YAC5C,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;YAC5B,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC;SACxE;QACD,IAAI;YACH,MAAM,WAAW,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;SACpC;QAAC,OAAO,KAAK,EAAE;YACf,IAAI,WAAW,KAAK,SAAS,EAAE;gBAC9B,eAAe,CAAC,KAAK,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;aAC5C;YACD,MAAM,KAAK,CAAC;SACZ;QACD,OAAO,KAAK,CAAC;IACd,CAAC,CAAC;IAEH,MAAM,OAAO,GAA2C,oBAAoB,CAAC;QAC5E,kBAAkB,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE;YACxD,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;YACrD,MAAM,CAAC,GAAG,CAAC,CAAC;YACZ,GAAG,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;QAC9B,CAAC;QACD,kBAAkB,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE;YACxD,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;YACrD,MAAM,CAAC,GAAG,CAAC,CAAC;YACZ,GAAG,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;QAC9B,CAAC;QACD,GAAG,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE;YAC/C,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;YACrD,MAAM,CAAC,GAAG,CAAC,CAAC;YACZ,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACrB,CAAC;QACD,KAAK,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YACrC,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;YACrD,MAAM,CAAC,GAAG,CAAC,CAAC;YACZ,GAAG,CAAC,KAAK,EAAE,CAAC;QACb,CAAC;QACD,MAAM,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE;YAC3C,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;YACrD,MAAM,CAAC,GAAG,CAAC,CAAC;YACZ,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjB,CAAC;KACD,CAAC,CAAC;IAEH,OAAO,WAAW,CAAC,OAAO,CAAC,CAAC;AAC7B,CAAC;AAED,QAAQ,CAAC,iDAAiD,EAAE,GAAG,EAAE;IAChE,MAAM,OAAO,GAA8B;QAC1C,YAAY,EAAE,CAAC;QACf,eAAe,EAAE,CAAC;QAClB,eAAe,EAAE,CAAC;QAClB,kBAAkB,EAAE,CAAC;QACrB,kBAAkB,EAAE,CAAC;QACrB,oBAAoB,EAAE,CAAC;QACvB,oBAAoB,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;QACtC,gBAAgB,EAAE,cAAc,CAAC,gBAAgB;KACjD,CAAC;IACF,MAAM,KAAK,GAA8C;QACxD,YAAY,EAAE,qBAAqB;QACnC,gBAAgB,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACvE,OAAO,EAAE,WAAW,CAAC,EAAE,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,gBAAgB,EAAE,KAAK,EAAE,CAAC;QAC7E,mBAAmB,EAAE,2BAA2B;QAChD,OAAO,EAAE,IAAI,gBAAgB,EAAE;KAC/B,CAAC;IAEF,kBAAkB,CAAC,KAAK,EAAE;QACzB,kBAAkB,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,QAAQ,EAAE,cAAc,CAAC,gBAAgB,EAAE;QACxF,oBAAoB,EAAE,IAAI;QAC1B,eAAe,EAAE,CAAC;QAClB,iBAAiB,EAAE;YAClB,kBAAkB,EAAE,CAAC;YACrB,oBAAoB,EAAE,IAAI;YAC1B,0BAA0B,EAAE,GAAG;SAC/B;QACD,gBAAgB,EAAE,EAAE;QACpB,uEAAuE;QACvE,cAAc;QACd,YAAY,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,mCAAmC,CAAC,EAAE;KACxF,CAAC,CAAC;IAEH,kBAAkB,CACjB,EAAE,GAAG,KAAK,EAAE,YAAY,EAAE,mCAAmC,EAAE,EAC/D;QACC,kBAAkB,EAAE;YACnB,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,GAAG;SAChB;QACD,iBAAiB,EAAE,GAAG;QACtB,oBAAoB,EAAE,GAAG;QACzB,uBAAuB,EAAE;YACxB,SAAS,EAAE,SAAS,CAAC,SAAS;YAC9B,qBAAqB,EAAE,IAAI;SAC3B;QACD,eAAe,EAAE,CAAC;QAClB,iBAAiB,EAAE;YAClB,kBAAkB,EAAE,CAAC;YACrB,oBAAoB,EAAE,IAAI;YAC1B,0BAA0B,EAAE,SAAS;SACrC;QACD,gBAAgB,EAAE,GAAG;QACrB,wEAAwE;QACxE,IAAI,EAAE;YACL,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG;YACjF,GAAG;SACH;QACD,uEAAuE;QACvE,cAAc;QACd,YAAY,EAAE;YACb,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,mCAAmC,CAAC;SACtE;KACD,CACD,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACrC,MAAM,KAAK,GAA8C;QACxD,YAAY,EAAE,qBAAqB;QACnC,gBAAgB,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,sBAAsB,EAAE,CAAC;QAChE,OAAO,EAAE,WAAW,CAAC,EAAE,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,gBAAgB,EAAE,KAAK,EAAE,CAAC;QAC7E,mBAAmB,EAAE,2BAA2B;QAChD,OAAO,EAAE,IAAI,gBAAgB,EAAE;KAC/B,CAAC;IAEF,kBAAkB,CAAC,KAAK,EAAE;QACzB,kBAAkB,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,QAAQ,EAAE,cAAc,CAAC,gBAAgB,EAAE;QACxF,oBAAoB,EAAE,IAAI;QAC1B,eAAe,EAAE,CAAC;QAClB,iBAAiB,EAAE;YAClB,iGAAiG;YACjG,8CAA8C;YAC9C,kBAAkB,EAAE,MAAM,CAAC,gBAAgB;YAC3C,oBAAoB,EAAE,IAAI;YAC1B,0BAA0B,EAAE,GAAG;SAC/B;QACD,gBAAgB,EAAE,EAAE;QACpB,uEAAuE;QACvE,aAAa;QACb,YAAY,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,mCAAmC,CAAC,EAAE;KACxF,CAAC,CAAC;IAEH,kBAAkB,CACjB,EAAE,GAAG,KAAK,EAAE,YAAY,EAAE,mCAAmC,EAAE,EAC/D;QACC,kBAAkB,EAAE;YACnB,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,GAAG;SAChB;QACD,iBAAiB,EAAE,GAAG;QACtB,oBAAoB,EAAE,GAAG;QACzB,uBAAuB,EAAE;YACxB,SAAS,EAAE,SAAS,CAAC,SAAS;YAC9B,qBAAqB,EAAE,IAAI;SAC3B;QACD,eAAe,EAAE,CAAC;QAClB,iBAAiB,EAAE;YAClB,iGAAiG;YACjG,8CAA8C;YAC9C,kBAAkB,EAAE,MAAM,CAAC,gBAAgB;YAC3C,oBAAoB,EAAE,IAAI;YAC1B,0BAA0B,EAAE,SAAS;SACrC;QACD,gBAAgB,EAAE,GAAG;QACrB,wEAAwE;QACxE,IAAI,EAAE,CAAC,EAAE,CAAC;QACV,uEAAuE;QACvE,aAAa;QACb,YAAY,EAAE;YACb,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,mCAAmC,CAAC;SACtE;KACD,CACD,CAAC;AACH,CAAC,CAAC,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport * as dirPath from \"node:path\";\nimport { strict as assert } from \"node:assert\";\nimport {\n\tAsyncGenerator,\n\tAsyncReducer,\n\tcombineReducersAsync,\n\tcreateWeightedAsyncGenerator,\n\ttakeAsync,\n} from \"@fluid-private/stochastic-test-utils\";\nimport {\n\tClient,\n\tcreateDDSFuzzSuite,\n\tDDSFuzzModel,\n\tDDSFuzzTestState,\n} from \"@fluid-private/test-dds-utils\";\nimport { FlushMode } from \"@fluidframework/runtime-definitions\";\nimport { DirectoryFactory } from \"../../directory.js\";\nimport { IDirectory } from \"../../interfaces.js\";\nimport { assertEquivalentDirectories } from \"./directoryEquivalenceUtils.js\";\nimport { _dirname } from \"./dirname.cjs\";\n\ntype FuzzTestState = DDSFuzzTestState<DirectoryFactory>;\n\ninterface SetKey {\n\ttype: \"set\";\n\tpath: string;\n\tkey: string;\n\tvalue: string;\n}\n\ninterface ClearKeys {\n\ttype: \"clear\";\n\tpath: string;\n}\n\ninterface DeleteKey {\n\ttype: \"delete\";\n\tpath: string;\n\tkey: string;\n}\n\ninterface CreateSubDirectory {\n\ttype: \"createSubDirectory\";\n\tpath: string;\n\tname: string;\n}\n\ninterface DeleteSubDirectory {\n\ttype: \"deleteSubDirectory\";\n\tpath: string;\n\tname: string;\n}\n\ntype KeyOperation = SetKey | DeleteKey | ClearKeys;\n\ntype SubDirectoryOperation = CreateSubDirectory | DeleteSubDirectory;\n\ntype Operation = KeyOperation | SubDirectoryOperation;\n\ninterface OperationGenerationConfig {\n\tvalidateInterval: number;\n\tmaxSubDirectoryChild?: number;\n\tsubDirectoryNamePool?: string[];\n\tkeyNamePool?: string[];\n\tsetKeyWeight?: number;\n\tdeleteKeyWeight?: number;\n\tclearKeysWeight?: number;\n\tcreateSubDirWeight?: number;\n\tdeleteSubDirWeight?: number;\n}\n\nconst defaultOptions: Required<OperationGenerationConfig> = {\n\tvalidateInterval: 10,\n\tmaxSubDirectoryChild: 3,\n\tsubDirectoryNamePool: [\"dir1\", \"dir2\", \"dir3\"],\n\tkeyNamePool: [\"prop1\", \"prop2\", \"prop3\"],\n\tsetKeyWeight: 5,\n\tdeleteKeyWeight: 2,\n\tclearKeysWeight: 1,\n\tcreateSubDirWeight: 2,\n\tdeleteSubDirWeight: 1,\n};\n\nfunction makeOperationGenerator(\n\toptionsParam?: OperationGenerationConfig,\n): AsyncGenerator<Operation, FuzzTestState> {\n\tconst options = { ...defaultOptions, ...optionsParam };\n\n\t// All subsequent helper functions are generators; note that they don't actually apply any operations.\n\tfunction pickAbsolutePathForCreateDirectoryOp(state: FuzzTestState): string {\n\t\tconst { random, client } = state;\n\t\tlet dir: IDirectory = client.channel;\n\t\tfor (;;) {\n\t\t\tassert(dir !== undefined, \"Directory should be defined\");\n\t\t\tconst subDirectories: IDirectory[] = [];\n\t\t\tfor (const [_, b] of dir.subdirectories()) {\n\t\t\t\tsubDirectories.push(b);\n\t\t\t}\n\t\t\t// If this dir already has max number of child, then choose one and continue.\n\t\t\tif (\n\t\t\t\tdir.countSubDirectory !== undefined &&\n\t\t\t\tdir.countSubDirectory() === options.maxSubDirectoryChild\n\t\t\t) {\n\t\t\t\tdir = random.pick<IDirectory>(subDirectories);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst subDir = random.pick<IDirectory | undefined>([undefined, ...subDirectories]);\n\t\t\tif (subDir === undefined) {\n\t\t\t\tbreak;\n\t\t\t} else {\n\t\t\t\tdir = subDir;\n\t\t\t}\n\t\t}\n\t\treturn dir.absolutePath;\n\t}\n\n\tfunction pickAbsolutePathForDeleteDirectoryOp(state: FuzzTestState): string {\n\t\tconst { random, client } = state;\n\t\tlet parentDir: IDirectory = client.channel;\n\t\tconst subDirectories: IDirectory[] = [];\n\t\tfor (const [_, b] of client.channel.subdirectories()) {\n\t\t\tsubDirectories.push(b);\n\t\t}\n\t\tlet dirToDelete = random.pick<IDirectory>(subDirectories);\n\t\tfor (;;) {\n\t\t\tassert(dirToDelete !== undefined, \"Directory should be defined\");\n\t\t\tconst subDirs: IDirectory[] = [];\n\t\t\tfor (const [_, b] of dirToDelete.subdirectories()) {\n\t\t\t\tsubDirs.push(b);\n\t\t\t}\n\t\t\tconst subDir = random.pick<IDirectory | undefined>([undefined, ...subDirs]);\n\t\t\tif (subDir === undefined) {\n\t\t\t\tbreak;\n\t\t\t} else {\n\t\t\t\tparentDir = dirToDelete;\n\t\t\t\tdirToDelete = subDir;\n\t\t\t}\n\t\t}\n\t\treturn parentDir.absolutePath;\n\t}\n\n\tfunction pickAbsolutePathForKeyOps(state: FuzzTestState, shouldHaveKey: boolean): string {\n\t\tconst { random, client } = state;\n\t\tlet parentDir: IDirectory = client.channel;\n\t\tfor (;;) {\n\t\t\tassert(parentDir !== undefined, \"Directory should be defined\");\n\t\t\tconst subDirs: IDirectory[] = [];\n\t\t\tfor (const [_, b] of parentDir.subdirectories()) {\n\t\t\t\tsubDirs.push(b);\n\t\t\t}\n\t\t\tconst subDir = random.pick<IDirectory | undefined>([undefined, ...subDirs]);\n\t\t\tif (subDir !== undefined && (!shouldHaveKey || subDir.size > 0)) {\n\t\t\t\tparentDir = subDir;\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn parentDir.absolutePath;\n\t}\n\n\tasync function createSubDirectory(state: FuzzTestState): Promise<CreateSubDirectory> {\n\t\treturn {\n\t\t\ttype: \"createSubDirectory\",\n\t\t\tname: state.random.pick(options.subDirectoryNamePool),\n\t\t\tpath: pickAbsolutePathForCreateDirectoryOp(state),\n\t\t};\n\t}\n\n\tasync function deleteSubDirectory(state: FuzzTestState): Promise<DeleteSubDirectory> {\n\t\tconst { random, client } = state;\n\t\tconst path = pickAbsolutePathForDeleteDirectoryOp(state);\n\t\tconst parentDir = client.channel.getWorkingDirectory(path);\n\t\tassert(parentDir !== undefined, \"parent dir should be defined\");\n\t\tassert(\n\t\t\tparentDir.countSubDirectory && parentDir.countSubDirectory() > 0,\n\t\t\t\"Atleast 1 subdir should be there\",\n\t\t);\n\t\tconst subDirName: string[] = [];\n\t\tfor (const [a, _] of parentDir.subdirectories()) {\n\t\t\tsubDirName.push(a);\n\t\t}\n\t\treturn {\n\t\t\ttype: \"deleteSubDirectory\",\n\t\t\tname: random.pick<string>(subDirName),\n\t\t\tpath,\n\t\t};\n\t}\n\n\tasync function setKey(state: FuzzTestState): Promise<SetKey> {\n\t\tconst { random } = state;\n\t\treturn {\n\t\t\ttype: \"set\",\n\t\t\tkey: random.pick(options.keyNamePool),\n\t\t\tpath: pickAbsolutePathForKeyOps(state, false),\n\t\t\tvalue: random.string(random.integer(0, 4)),\n\t\t};\n\t}\n\n\tasync function clearKeys(state: FuzzTestState): Promise<ClearKeys> {\n\t\treturn {\n\t\t\ttype: \"clear\",\n\t\t\tpath: pickAbsolutePathForKeyOps(state, true),\n\t\t};\n\t}\n\n\tasync function deleteKey(state: FuzzTestState): Promise<DeleteKey> {\n\t\tconst { random, client } = state;\n\t\tconst path = pickAbsolutePathForKeyOps(state, true);\n\t\tconst dir = client.channel.getWorkingDirectory(path);\n\t\tassert(dir, \"dir should exist\");\n\t\treturn {\n\t\t\ttype: \"delete\",\n\t\t\tkey: random.pick([...dir.keys()]),\n\t\t\tpath,\n\t\t};\n\t}\n\n\treturn createWeightedAsyncGenerator<Operation, FuzzTestState>([\n\t\t[createSubDirectory, options.createSubDirWeight],\n\t\t[\n\t\t\tdeleteSubDirectory,\n\t\t\toptions.deleteSubDirWeight,\n\t\t\t(state: FuzzTestState): boolean =>\n\t\t\t\t(state.client.channel.countSubDirectory?.() ?? 0) > 0,\n\t\t],\n\t\t[setKey, options.setKeyWeight],\n\t\t[\n\t\t\tdeleteKey,\n\t\t\toptions.deleteKeyWeight,\n\t\t\t(state: FuzzTestState): boolean => state.client.channel.size > 0,\n\t\t],\n\t\t[\n\t\t\tclearKeys,\n\t\t\toptions.clearKeysWeight,\n\t\t\t(state: FuzzTestState): boolean => state.client.channel.size > 0,\n\t\t],\n\t]);\n}\n\ninterface LoggingInfo {\n\t// Clients to print\n\tclientIds: string[];\n\t// Set this to true in case you want to debug and print client states and ops.\n\tprintConsoleLogs?: boolean;\n}\n\nfunction logCurrentState(clients: Client<DirectoryFactory>[], loggingInfo: LoggingInfo): void {\n\tfor (const id of loggingInfo.clientIds) {\n\t\tconst { channel: sharedDirectory } =\n\t\t\tclients.find((s) => s.containerRuntime.clientId === id) ?? {};\n\t\tif (sharedDirectory !== undefined) {\n\t\t\tconsole.log(`Client ${id}:`);\n\t\t\tconsole.log(\n\t\t\t\tJSON.stringify(sharedDirectory.getAttachSummary(true).summary, undefined, 4),\n\t\t\t);\n\t\t\tconsole.log(\"\\n\");\n\t\t}\n\t}\n}\n\nfunction makeReducer(loggingInfo?: LoggingInfo): AsyncReducer<Operation, FuzzTestState> {\n\tconst withLogging =\n\t\t<T>(baseReducer: AsyncReducer<T, FuzzTestState>): AsyncReducer<T, FuzzTestState> =>\n\t\tasync (state, operation) => {\n\t\t\tif (loggingInfo !== undefined && loggingInfo.printConsoleLogs) {\n\t\t\t\tlogCurrentState(state.clients, loggingInfo);\n\t\t\t\tconsole.log(\"-\".repeat(20));\n\t\t\t\tconsole.log(\"Next operation:\", JSON.stringify(operation, undefined, 4));\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait baseReducer(state, operation);\n\t\t\t} catch (error) {\n\t\t\t\tif (loggingInfo !== undefined) {\n\t\t\t\t\tlogCurrentState(state.clients, loggingInfo);\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\treturn state;\n\t\t};\n\n\tconst reducer: AsyncReducer<Operation, FuzzTestState> = combineReducersAsync({\n\t\tcreateSubDirectory: async ({ client }, { path, name }) => {\n\t\t\tconst dir = client.channel.getWorkingDirectory(path);\n\t\t\tassert(dir);\n\t\t\tdir.createSubDirectory(name);\n\t\t},\n\t\tdeleteSubDirectory: async ({ client }, { path, name }) => {\n\t\t\tconst dir = client.channel.getWorkingDirectory(path);\n\t\t\tassert(dir);\n\t\t\tdir.deleteSubDirectory(name);\n\t\t},\n\t\tset: async ({ client }, { path, key, value }) => {\n\t\t\tconst dir = client.channel.getWorkingDirectory(path);\n\t\t\tassert(dir);\n\t\t\tdir.set(key, value);\n\t\t},\n\t\tclear: async ({ client }, { path }) => {\n\t\t\tconst dir = client.channel.getWorkingDirectory(path);\n\t\t\tassert(dir);\n\t\t\tdir.clear();\n\t\t},\n\t\tdelete: async ({ client }, { path, key }) => {\n\t\t\tconst dir = client.channel.getWorkingDirectory(path);\n\t\t\tassert(dir);\n\t\t\tdir.delete(key);\n\t\t},\n\t});\n\n\treturn withLogging(reducer);\n}\n\ndescribe(\"SharedDirectory fuzz Create/Delete concentrated\", () => {\n\tconst options: OperationGenerationConfig = {\n\t\tsetKeyWeight: 0,\n\t\tclearKeysWeight: 0,\n\t\tdeleteKeyWeight: 0,\n\t\tcreateSubDirWeight: 2,\n\t\tdeleteSubDirWeight: 2,\n\t\tmaxSubDirectoryChild: 2,\n\t\tsubDirectoryNamePool: [\"dir1\", \"dir2\"],\n\t\tvalidateInterval: defaultOptions.validateInterval,\n\t};\n\tconst model: DDSFuzzModel<DirectoryFactory, Operation> = {\n\t\tworkloadName: \"default directory 1\",\n\t\tgeneratorFactory: () => takeAsync(100, makeOperationGenerator(options)),\n\t\treducer: makeReducer({ clientIds: [\"A\", \"B\", \"C\"], printConsoleLogs: false }),\n\t\tvalidateConsistency: assertEquivalentDirectories,\n\t\tfactory: new DirectoryFactory(),\n\t};\n\n\tcreateDDSFuzzSuite(model, {\n\t\tvalidationStrategy: { type: \"fixedInterval\", interval: defaultOptions.validateInterval },\n\t\treconnectProbability: 0.15,\n\t\tnumberOfClients: 3,\n\t\tclientJoinOptions: {\n\t\t\tmaxNumberOfClients: 3,\n\t\t\tclientAddProbability: 0.08,\n\t\t\tstashableClientProbability: 0.2,\n\t\t},\n\t\tdefaultTestCount: 25,\n\t\t// Uncomment this line to replay a specific seed from its failure file:\n\t\t// replay: 21,\n\t\tsaveFailures: { directory: dirPath.join(_dirname, \"../../../src/test/mocha/results/1\") },\n\t});\n\n\tcreateDDSFuzzSuite(\n\t\t{ ...model, workloadName: \"default directory 1 with rebasing\" },\n\t\t{\n\t\t\tvalidationStrategy: {\n\t\t\t\ttype: \"random\",\n\t\t\t\tprobability: 0.4,\n\t\t\t},\n\t\t\trebaseProbability: 0.2,\n\t\t\treconnectProbability: 0.5,\n\t\t\tcontainerRuntimeOptions: {\n\t\t\t\tflushMode: FlushMode.TurnBased,\n\t\t\t\tenableGroupedBatching: true,\n\t\t\t},\n\t\t\tnumberOfClients: 3,\n\t\t\tclientJoinOptions: {\n\t\t\t\tmaxNumberOfClients: 3,\n\t\t\t\tclientAddProbability: 0.08,\n\t\t\t\tstashableClientProbability: undefined,\n\t\t\t},\n\t\t\tdefaultTestCount: 200,\n\t\t\t// The seeds below fail only when rebaseProbability is non-zero ADO:6044\n\t\t\tskip: [\n\t\t\t\t13, 40, 43, 55, 66, 93, 94, 107, 110, 123, 136, 148, 160, 163, 168, 172, 177, 191,\n\t\t\t\t196,\n\t\t\t],\n\t\t\t// Uncomment this line to replay a specific seed from its failure file:\n\t\t\t// replay: 21,\n\t\t\tsaveFailures: {\n\t\t\t\tdirectory: dirPath.join(_dirname, \"../../../src/test/mocha/results/1\"),\n\t\t\t},\n\t\t},\n\t);\n});\n\ndescribe(\"SharedDirectory fuzz\", () => {\n\tconst model: DDSFuzzModel<DirectoryFactory, Operation> = {\n\t\tworkloadName: \"default directory 2\",\n\t\tgeneratorFactory: () => takeAsync(100, makeOperationGenerator()),\n\t\treducer: makeReducer({ clientIds: [\"A\", \"B\", \"C\"], printConsoleLogs: false }),\n\t\tvalidateConsistency: assertEquivalentDirectories,\n\t\tfactory: new DirectoryFactory(),\n\t};\n\n\tcreateDDSFuzzSuite(model, {\n\t\tvalidationStrategy: { type: \"fixedInterval\", interval: defaultOptions.validateInterval },\n\t\treconnectProbability: 0.15,\n\t\tnumberOfClients: 3,\n\t\tclientJoinOptions: {\n\t\t\t// Note: if tests are slow, we may want to tune this down. This mimics behavior before this suite\n\t\t\t// was refactored to use the DDS fuzz harness.\n\t\t\tmaxNumberOfClients: Number.MAX_SAFE_INTEGER,\n\t\t\tclientAddProbability: 0.08,\n\t\t\tstashableClientProbability: 0.2,\n\t\t},\n\t\tdefaultTestCount: 25,\n\t\t// Uncomment this line to replay a specific seed from its failure file:\n\t\t// replay: 0,\n\t\tsaveFailures: { directory: dirPath.join(_dirname, \"../../../src/test/mocha/results/2\") },\n\t});\n\n\tcreateDDSFuzzSuite(\n\t\t{ ...model, workloadName: \"default directory 2 with rebasing\" },\n\t\t{\n\t\t\tvalidationStrategy: {\n\t\t\t\ttype: \"random\",\n\t\t\t\tprobability: 0.4,\n\t\t\t},\n\t\t\trebaseProbability: 0.2,\n\t\t\treconnectProbability: 0.5,\n\t\t\tcontainerRuntimeOptions: {\n\t\t\t\tflushMode: FlushMode.TurnBased,\n\t\t\t\tenableGroupedBatching: true,\n\t\t\t},\n\t\t\tnumberOfClients: 3,\n\t\t\tclientJoinOptions: {\n\t\t\t\t// Note: if tests are slow, we may want to tune this down. This mimics behavior before this suite\n\t\t\t\t// was refactored to use the DDS fuzz harness.\n\t\t\t\tmaxNumberOfClients: Number.MAX_SAFE_INTEGER,\n\t\t\t\tclientAddProbability: 0.08,\n\t\t\t\tstashableClientProbability: undefined,\n\t\t\t},\n\t\t\tdefaultTestCount: 200,\n\t\t\t// The seeds below fail only when rebaseProbability is non-zero ADO:6044\n\t\t\tskip: [73],\n\t\t\t// Uncomment this line to replay a specific seed from its failure file:\n\t\t\t// replay: 0,\n\t\t\tsaveFailures: {\n\t\t\t\tdirectory: dirPath.join(_dirname, \"../../../src/test/mocha/results/2\"),\n\t\t\t},\n\t\t},\n\t);\n});\n"]}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ /*!
3
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
4
+ * Licensed under the MIT License.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports._dirname = void 0;
8
+ // Problem:
9
+ // - `__dirname` is not defined in ESM
10
+ // - `import.meta.url` is not defined in CJS
11
+ // Solution:
12
+ // - Export '__dirname' from a .cjs file in the same directory.
13
+ //
14
+ // Note that *.cjs files are always CommonJS, but can be imported from ESM.
15
+ exports._dirname = __dirname;
16
+ //# sourceMappingURL=dirname.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dirname.cjs","sourceRoot":"","sources":["../../../src/test/mocha/dirname.cts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,WAAW;AACX,wCAAwC;AACxC,8CAA8C;AAC9C,YAAY;AACZ,iEAAiE;AACjE,EAAE;AACF,2EAA2E;AAC9D,QAAA,QAAQ,GAAG,SAAS,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n// Problem:\n// - `__dirname` is not defined in ESM\n// - `import.meta.url` is not defined in CJS\n// Solution:\n// - Export '__dirname' from a .cjs file in the same directory.\n//\n// Note that *.cjs files are always CommonJS, but can be imported from ESM.\nexport const _dirname = __dirname;\n"]}
@@ -0,0 +1,114 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import * as path from "node:path";
6
+ import { strict as assert } from "node:assert";
7
+ import { createDDSFuzzSuite } from "@fluid-private/test-dds-utils";
8
+ import { combineReducers, createWeightedGenerator, takeAsync, } from "@fluid-private/stochastic-test-utils";
9
+ import { FlushMode } from "@fluidframework/runtime-definitions";
10
+ import { MapFactory } from "../../map.js";
11
+ import { _dirname } from "./dirname.cjs";
12
+ function assertMapsAreEquivalent(a, b) {
13
+ assert.equal(a.size, b.size, `${a.id} and ${b.id} have different number of keys.`);
14
+ for (const key of a.keys()) {
15
+ const aVal = a.get(key);
16
+ const bVal = b.get(key);
17
+ assert.equal(aVal, bVal, `${a.id} and ${b.id} differ at ${key}: ${aVal} vs ${bVal}`);
18
+ }
19
+ }
20
+ const reducer = combineReducers({
21
+ clear: ({ client }) => client.channel.clear(),
22
+ setKey: ({ client }, { key, value }) => {
23
+ client.channel.set(key, value);
24
+ },
25
+ deleteKey: ({ client }, { key }) => {
26
+ client.channel.delete(key);
27
+ },
28
+ });
29
+ const defaultOptions = {
30
+ setWeight: 20,
31
+ deleteWeight: 20,
32
+ clearWeight: 1,
33
+ keyPoolSize: 20,
34
+ };
35
+ function makeGenerator(optionsParam) {
36
+ const { setWeight, deleteWeight, clearWeight, keyPoolSize } = {
37
+ ...defaultOptions,
38
+ ...optionsParam,
39
+ };
40
+ // Use numbers as the key names.
41
+ const keyNames = Array.from({ length: keyPoolSize }, (_, i) => `${i}`);
42
+ const setKey = ({ random }) => ({
43
+ type: "setKey",
44
+ key: random.pick(keyNames),
45
+ value: random.bool() ? random.integer(1, 50) : random.string(random.integer(3, 7)),
46
+ });
47
+ const deleteKey = ({ random }) => ({
48
+ type: "deleteKey",
49
+ key: random.pick(keyNames),
50
+ });
51
+ const syncGenerator = createWeightedGenerator([
52
+ [setKey, setWeight],
53
+ [deleteKey, deleteWeight],
54
+ [{ type: "clear" }, clearWeight],
55
+ ]);
56
+ return async (state) => syncGenerator(state);
57
+ }
58
+ describe("Map fuzz tests", () => {
59
+ const model = {
60
+ workloadName: "default",
61
+ factory: new MapFactory(),
62
+ generatorFactory: () => takeAsync(100, makeGenerator()),
63
+ reducer: async (state, operation) => reducer(state, operation),
64
+ validateConsistency: assertMapsAreEquivalent,
65
+ };
66
+ createDDSFuzzSuite(model, {
67
+ defaultTestCount: 100,
68
+ numberOfClients: 3,
69
+ clientJoinOptions: {
70
+ maxNumberOfClients: 6,
71
+ clientAddProbability: 0.1,
72
+ stashableClientProbability: 0.2,
73
+ },
74
+ reconnectProbability: 0,
75
+ // Uncomment to replay a particular seed.
76
+ // replay: 0,
77
+ saveFailures: { directory: path.join(_dirname, "../../../src/test/mocha/results/map") },
78
+ });
79
+ createDDSFuzzSuite({ ...model, workloadName: "with reconnect" }, {
80
+ defaultTestCount: 100,
81
+ numberOfClients: 3,
82
+ clientJoinOptions: {
83
+ maxNumberOfClients: 6,
84
+ clientAddProbability: 0.1,
85
+ stashableClientProbability: 0.2,
86
+ },
87
+ reconnectProbability: 0.1,
88
+ // Uncomment to replay a particular seed.
89
+ // replay: 0,
90
+ saveFailures: {
91
+ directory: path.join(_dirname, "../../../src/test/mocha/results/map-reconnect"),
92
+ },
93
+ });
94
+ createDDSFuzzSuite({ ...model, workloadName: "with batches and rebasing" }, {
95
+ defaultTestCount: 100,
96
+ numberOfClients: 3,
97
+ clientJoinOptions: {
98
+ maxNumberOfClients: 6,
99
+ clientAddProbability: 0.1,
100
+ stashableClientProbability: 0.2,
101
+ },
102
+ rebaseProbability: 0.2,
103
+ containerRuntimeOptions: {
104
+ flushMode: FlushMode.TurnBased,
105
+ enableGroupedBatching: true,
106
+ },
107
+ // Uncomment to replay a particular seed.
108
+ // replay: 0,
109
+ saveFailures: {
110
+ directory: path.join(_dirname, "../../../src/test/mocha/results/map-rebase"),
111
+ },
112
+ });
113
+ });
114
+ //# sourceMappingURL=map.fuzz.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map.fuzz.spec.js","sourceRoot":"","sources":["../../../src/test/mocha/map.fuzz.spec.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport * as path from \"node:path\";\nimport { strict as assert } from \"node:assert\";\nimport { DDSFuzzModel, DDSFuzzTestState, createDDSFuzzSuite } from \"@fluid-private/test-dds-utils\";\nimport { Jsonable } from \"@fluidframework/datastore-definitions\";\nimport {\n\tcombineReducers,\n\tcreateWeightedGenerator,\n\tAsyncGenerator,\n\tGenerator,\n\ttakeAsync,\n} from \"@fluid-private/stochastic-test-utils\";\nimport { FlushMode } from \"@fluidframework/runtime-definitions\";\nimport { MapFactory } from \"../../map.js\";\nimport { ISharedMap } from \"../../interfaces.js\";\nimport { _dirname } from \"./dirname.cjs\";\n\ninterface Clear {\n\ttype: \"clear\";\n}\n\ninterface SetKey {\n\ttype: \"setKey\";\n\tkey: string;\n\tvalue: Jsonable<unknown>;\n}\n\ninterface DeleteKey {\n\ttype: \"deleteKey\";\n\tkey: string;\n}\n\ntype Operation = SetKey | DeleteKey | Clear;\n\n// This type gets used a lot as the state object of the suite; shorthand it here.\ntype State = DDSFuzzTestState<MapFactory>;\n\nfunction assertMapsAreEquivalent(a: ISharedMap, b: ISharedMap): void {\n\tassert.equal(a.size, b.size, `${a.id} and ${b.id} have different number of keys.`);\n\tfor (const key of a.keys()) {\n\t\tconst aVal: unknown = a.get(key);\n\t\tconst bVal: unknown = b.get(key);\n\t\tassert.equal(aVal, bVal, `${a.id} and ${b.id} differ at ${key}: ${aVal} vs ${bVal}`);\n\t}\n}\n\nconst reducer = combineReducers<Operation, State>({\n\tclear: ({ client }) => client.channel.clear(),\n\tsetKey: ({ client }, { key, value }) => {\n\t\tclient.channel.set(key, value);\n\t},\n\tdeleteKey: ({ client }, { key }) => {\n\t\tclient.channel.delete(key);\n\t},\n});\n\ninterface GeneratorOptions {\n\tsetWeight: number;\n\tdeleteWeight: number;\n\tclearWeight: number;\n\tkeyPoolSize: number;\n}\n\nconst defaultOptions: GeneratorOptions = {\n\tsetWeight: 20,\n\tdeleteWeight: 20,\n\tclearWeight: 1,\n\tkeyPoolSize: 20,\n};\n\nfunction makeGenerator(optionsParam?: Partial<GeneratorOptions>): AsyncGenerator<Operation, State> {\n\tconst { setWeight, deleteWeight, clearWeight, keyPoolSize } = {\n\t\t...defaultOptions,\n\t\t...optionsParam,\n\t};\n\t// Use numbers as the key names.\n\tconst keyNames = Array.from({ length: keyPoolSize }, (_, i) => `${i}`);\n\n\tconst setKey: Generator<SetKey, State> = ({ random }) => ({\n\t\ttype: \"setKey\",\n\t\tkey: random.pick(keyNames),\n\t\tvalue: random.bool() ? random.integer(1, 50) : random.string(random.integer(3, 7)),\n\t});\n\tconst deleteKey: Generator<DeleteKey, State> = ({ random }) => ({\n\t\ttype: \"deleteKey\",\n\t\tkey: random.pick(keyNames),\n\t});\n\n\tconst syncGenerator = createWeightedGenerator<Operation, State>([\n\t\t[setKey, setWeight],\n\t\t[deleteKey, deleteWeight],\n\t\t[{ type: \"clear\" }, clearWeight],\n\t]);\n\n\treturn async (state) => syncGenerator(state);\n}\n\ndescribe(\"Map fuzz tests\", () => {\n\tconst model: DDSFuzzModel<MapFactory, Operation> = {\n\t\tworkloadName: \"default\",\n\t\tfactory: new MapFactory(),\n\t\tgeneratorFactory: () => takeAsync(100, makeGenerator()),\n\t\treducer: async (state, operation) => reducer(state, operation),\n\t\tvalidateConsistency: assertMapsAreEquivalent,\n\t};\n\n\tcreateDDSFuzzSuite(model, {\n\t\tdefaultTestCount: 100,\n\t\tnumberOfClients: 3,\n\t\tclientJoinOptions: {\n\t\t\tmaxNumberOfClients: 6,\n\t\t\tclientAddProbability: 0.1,\n\t\t\tstashableClientProbability: 0.2,\n\t\t},\n\t\treconnectProbability: 0,\n\t\t// Uncomment to replay a particular seed.\n\t\t// replay: 0,\n\t\tsaveFailures: { directory: path.join(_dirname, \"../../../src/test/mocha/results/map\") },\n\t});\n\n\tcreateDDSFuzzSuite(\n\t\t{ ...model, workloadName: \"with reconnect\" },\n\t\t{\n\t\t\tdefaultTestCount: 100,\n\t\t\tnumberOfClients: 3,\n\t\t\tclientJoinOptions: {\n\t\t\t\tmaxNumberOfClients: 6,\n\t\t\t\tclientAddProbability: 0.1,\n\t\t\t\tstashableClientProbability: 0.2,\n\t\t\t},\n\t\t\treconnectProbability: 0.1,\n\t\t\t// Uncomment to replay a particular seed.\n\t\t\t// replay: 0,\n\t\t\tsaveFailures: {\n\t\t\t\tdirectory: path.join(_dirname, \"../../../src/test/mocha/results/map-reconnect\"),\n\t\t\t},\n\t\t},\n\t);\n\n\tcreateDDSFuzzSuite(\n\t\t{ ...model, workloadName: \"with batches and rebasing\" },\n\t\t{\n\t\t\tdefaultTestCount: 100,\n\t\t\tnumberOfClients: 3,\n\t\t\tclientJoinOptions: {\n\t\t\t\tmaxNumberOfClients: 6,\n\t\t\t\tclientAddProbability: 0.1,\n\t\t\t\tstashableClientProbability: 0.2,\n\t\t\t},\n\t\t\trebaseProbability: 0.2,\n\t\t\tcontainerRuntimeOptions: {\n\t\t\t\tflushMode: FlushMode.TurnBased,\n\t\t\t\tenableGroupedBatching: true,\n\t\t\t},\n\t\t\t// Uncomment to replay a particular seed.\n\t\t\t// replay: 0,\n\t\t\tsaveFailures: {\n\t\t\t\tdirectory: path.join(_dirname, \"../../../src/test/mocha/results/map-rebase\"),\n\t\t\t},\n\t\t},\n\t);\n});\n"]}