@fluidframework/test-runtime-utils 2.70.0-361248 → 2.70.0-361788

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/src/mocks.ts CHANGED
@@ -45,6 +45,7 @@ import {
45
45
  ITreeEntry,
46
46
  MessageType,
47
47
  ISequencedDocumentMessage,
48
+ type ISnapshotTree,
48
49
  } from "@fluidframework/driver-definitions/internal";
49
50
  import type { IIdCompressor } from "@fluidframework/id-compressor";
50
51
  import type {
@@ -708,7 +709,10 @@ export class MockQuorumClients implements IQuorumClients, EventEmitter {
708
709
  }
709
710
 
710
711
  getMembers(): Map<string, ISequencedClient> {
711
- return this.members;
712
+ // Implementation always generates a new Map.
713
+ // Mock should as well in case any callers rely on being able to modify
714
+ // the returned Map.
715
+ return new Map(this.members);
712
716
  }
713
717
  getMember(clientId: string): ISequencedClient | undefined {
714
718
  return this.getMembers().get(clientId);
@@ -1040,14 +1044,6 @@ export class MockFluidDataStoreRuntime
1040
1044
  return null;
1041
1045
  }
1042
1046
 
1043
- /**
1044
- * @deprecated Use `IFluidDataStoreContext.submitMessage` instead.
1045
- * @see https://github.com/microsoft/FluidFramework/issues/24406
1046
- */
1047
- public submitMessage(type: MessageType, content: any) {
1048
- return null;
1049
- }
1050
-
1051
1047
  private submitMessageInternal(messageContent: any, localOpMetadata: unknown): number {
1052
1048
  assert(
1053
1049
  this.containerRuntime !== undefined,
@@ -1232,22 +1228,36 @@ export class MockEmptyDeltaConnection implements IDeltaConnection {
1232
1228
  * @legacy @beta
1233
1229
  */
1234
1230
  export class MockObjectStorageService implements IChannelStorageService {
1235
- public constructor(private readonly contents: { [key: string]: string }) {}
1231
+ private readonly snapshotTree: ISnapshotTree;
1232
+
1233
+ /**
1234
+ * @param contents - Key value pairs that represent a snapshot.
1235
+ * The keys are the path to the contents of a blob in the snapshot tree. The corresponding values are its contents.
1236
+ *
1237
+ * @remarks
1238
+ * The snapshot contents must not change after it has been passed here as the changes will not be reflected
1239
+ * in the snapshot tree retrieved via `getSnapshotTree`.
1240
+ */
1241
+ public constructor(private readonly contents: { [key: string]: string }) {
1242
+ this.snapshotTree = createSnapshotTreeFromContents(contents);
1243
+ }
1236
1244
 
1237
1245
  public async readBlob(path: string): Promise<ArrayBufferLike> {
1238
1246
  return stringToBuffer(this.contents[path], "utf8");
1239
1247
  }
1240
-
1241
1248
  public async contains(path: string): Promise<boolean> {
1242
1249
  return this.contents[path] !== undefined;
1243
1250
  }
1244
-
1245
1251
  public async list(path: string): Promise<string[]> {
1246
1252
  const pathPartsLength = getNormalizedObjectStoragePathParts(path).length;
1247
1253
  return Object.keys(this.contents).filter(
1248
1254
  (key) => key.startsWith(path) && key.split("/").length === pathPartsLength + 1,
1249
1255
  );
1250
1256
  }
1257
+
1258
+ public getSnapshotTree(): ISnapshotTree {
1259
+ return this.snapshotTree;
1260
+ }
1251
1261
  }
1252
1262
 
1253
1263
  /**
@@ -1294,3 +1304,43 @@ function setContentsFromSummaryTree(
1294
1304
  }
1295
1305
  }
1296
1306
  }
1307
+
1308
+ /**
1309
+ * Create an ISnapshotTree from contents object (reverse of setContentsFromSummaryTree)
1310
+ * @param contents - Object with path/value pairs
1311
+ * @returns ISnapshotTree representing the hierarchical structure
1312
+ */
1313
+ export function createSnapshotTreeFromContents(contents: {
1314
+ [key: string]: string;
1315
+ }): ISnapshotTree {
1316
+ const tree: ISnapshotTree = {
1317
+ trees: {},
1318
+ blobs: {},
1319
+ };
1320
+
1321
+ for (const [path, content] of Object.entries(contents)) {
1322
+ // Remove empty strings to handle leading, trailing, or consecutive slashes in the path.
1323
+ const pathParts = path.split("/").filter((part) => part !== "");
1324
+ let currentTree = tree;
1325
+
1326
+ // Navigate/create the tree structure for all but the last part
1327
+ for (let i = 0; i < pathParts.length - 1; i++) {
1328
+ const part = pathParts[i];
1329
+ if (!currentTree.trees[part]) {
1330
+ currentTree.trees[part] = {
1331
+ trees: {},
1332
+ blobs: {},
1333
+ };
1334
+ }
1335
+ currentTree = currentTree.trees[part];
1336
+ }
1337
+
1338
+ // Add the blob at the final location
1339
+ const blobName = pathParts[pathParts.length - 1];
1340
+ if (blobName !== undefined) {
1341
+ currentTree.blobs[blobName] = content;
1342
+ }
1343
+ }
1344
+
1345
+ return tree;
1346
+ }