@fluidframework/container-loader 2.0.0-rc.1.0.4 → 2.0.0-rc.2.0.0

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 (298) hide show
  1. package/{.eslintrc.js → .eslintrc.cjs} +5 -6
  2. package/{.mocharc.js → .mocharc.cjs} +1 -1
  3. package/CHANGELOG.md +48 -0
  4. package/README.md +3 -3
  5. package/{api-extractor-esm.json → api-extractor-cjs.json} +5 -1
  6. package/api-extractor-lint.json +1 -1
  7. package/api-extractor.json +1 -1
  8. package/api-report/container-loader.api.md +2 -2
  9. package/dist/attachment.d.ts +115 -0
  10. package/dist/attachment.d.ts.map +1 -0
  11. package/dist/attachment.js +83 -0
  12. package/dist/attachment.js.map +1 -0
  13. package/dist/audience.d.ts +9 -4
  14. package/dist/audience.d.ts.map +1 -1
  15. package/dist/audience.js +10 -4
  16. package/dist/audience.js.map +1 -1
  17. package/dist/connectionManager.d.ts +3 -3
  18. package/dist/connectionManager.d.ts.map +1 -1
  19. package/dist/connectionManager.js +17 -18
  20. package/dist/connectionManager.js.map +1 -1
  21. package/dist/connectionState.d.ts +1 -0
  22. package/dist/connectionState.d.ts.map +1 -1
  23. package/dist/connectionState.js +1 -0
  24. package/dist/connectionState.js.map +1 -1
  25. package/dist/connectionStateHandler.d.ts +7 -7
  26. package/dist/connectionStateHandler.d.ts.map +1 -1
  27. package/dist/connectionStateHandler.js +32 -32
  28. package/dist/connectionStateHandler.js.map +1 -1
  29. package/dist/container-loader-alpha.d.ts +2 -1
  30. package/dist/container-loader-beta.d.ts +3 -0
  31. package/dist/container-loader-public.d.ts +3 -0
  32. package/dist/container-loader-untrimmed.d.ts +5 -5
  33. package/dist/container.d.ts +29 -27
  34. package/dist/container.d.ts.map +1 -1
  35. package/dist/container.js +219 -284
  36. package/dist/container.js.map +1 -1
  37. package/dist/containerContext.d.ts +3 -2
  38. package/dist/containerContext.d.ts.map +1 -1
  39. package/dist/containerContext.js +2 -1
  40. package/dist/containerContext.js.map +1 -1
  41. package/dist/containerStorageAdapter.d.ts +5 -6
  42. package/dist/containerStorageAdapter.d.ts.map +1 -1
  43. package/dist/containerStorageAdapter.js +14 -21
  44. package/dist/containerStorageAdapter.js.map +1 -1
  45. package/dist/contracts.d.ts +3 -3
  46. package/dist/contracts.d.ts.map +1 -1
  47. package/dist/contracts.js.map +1 -1
  48. package/dist/debugLogger.js.map +1 -1
  49. package/dist/deltaManager.d.ts +5 -5
  50. package/dist/deltaManager.d.ts.map +1 -1
  51. package/dist/deltaManager.js +6 -6
  52. package/dist/deltaManager.js.map +1 -1
  53. package/dist/error.d.ts.map +1 -1
  54. package/dist/error.js.map +1 -1
  55. package/dist/index.d.ts +6 -6
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +11 -11
  58. package/dist/index.js.map +1 -1
  59. package/dist/loader.d.ts +3 -3
  60. package/dist/loader.d.ts.map +1 -1
  61. package/dist/loader.js +13 -17
  62. package/dist/loader.js.map +1 -1
  63. package/dist/location-redirection-utilities/index.d.ts +1 -1
  64. package/dist/location-redirection-utilities/index.d.ts.map +1 -1
  65. package/dist/location-redirection-utilities/index.js +3 -3
  66. package/dist/location-redirection-utilities/index.js.map +1 -1
  67. package/dist/package.json +3 -0
  68. package/dist/packageVersion.d.ts +1 -1
  69. package/dist/packageVersion.js +1 -1
  70. package/dist/packageVersion.js.map +1 -1
  71. package/dist/protocolTreeDocumentStorageService.d.ts +1 -1
  72. package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
  73. package/dist/protocolTreeDocumentStorageService.js +1 -3
  74. package/dist/protocolTreeDocumentStorageService.js.map +1 -1
  75. package/dist/retriableDocumentStorageService.d.ts +2 -2
  76. package/dist/retriableDocumentStorageService.d.ts.map +1 -1
  77. package/dist/retriableDocumentStorageService.js +8 -6
  78. package/dist/retriableDocumentStorageService.js.map +1 -1
  79. package/dist/serializedStateManager.d.ts +44 -0
  80. package/dist/serializedStateManager.d.ts.map +1 -0
  81. package/dist/serializedStateManager.js +149 -0
  82. package/dist/serializedStateManager.js.map +1 -0
  83. package/dist/tsdoc-metadata.json +1 -1
  84. package/dist/utils.d.ts +16 -11
  85. package/dist/utils.d.ts.map +1 -1
  86. package/dist/utils.js +107 -32
  87. package/dist/utils.js.map +1 -1
  88. package/lib/attachment.d.ts +115 -0
  89. package/lib/attachment.d.ts.map +1 -0
  90. package/lib/attachment.js +79 -0
  91. package/lib/attachment.js.map +1 -0
  92. package/lib/{audience.d.mts → audience.d.ts} +14 -5
  93. package/lib/audience.d.ts.map +1 -0
  94. package/lib/{audience.mjs → audience.js} +14 -4
  95. package/lib/audience.js.map +1 -0
  96. package/lib/{catchUpMonitor.d.mts → catchUpMonitor.d.ts} +1 -1
  97. package/lib/catchUpMonitor.d.ts.map +1 -0
  98. package/lib/{catchUpMonitor.mjs → catchUpMonitor.js} +1 -1
  99. package/lib/catchUpMonitor.js.map +1 -0
  100. package/lib/{connectionManager.d.mts → connectionManager.d.ts} +4 -4
  101. package/lib/connectionManager.d.ts.map +1 -0
  102. package/lib/{connectionManager.mjs → connectionManager.js} +7 -10
  103. package/lib/connectionManager.js.map +1 -0
  104. package/lib/{connectionState.d.mts → connectionState.d.ts} +2 -1
  105. package/lib/connectionState.d.ts.map +1 -0
  106. package/lib/{connectionState.mjs → connectionState.js} +2 -1
  107. package/lib/connectionState.js.map +1 -0
  108. package/lib/{connectionStateHandler.d.mts → connectionStateHandler.d.ts} +8 -8
  109. package/lib/connectionStateHandler.d.ts.map +1 -0
  110. package/lib/{connectionStateHandler.mjs → connectionStateHandler.js} +3 -3
  111. package/lib/connectionStateHandler.js.map +1 -0
  112. package/lib/{container-loader-alpha.d.mts → container-loader-alpha.d.ts} +2 -1
  113. package/lib/{container-loader-beta.d.mts → container-loader-beta.d.ts} +3 -0
  114. package/lib/{container-loader-public.d.mts → container-loader-public.d.ts} +3 -0
  115. package/lib/{container-loader-untrimmed.d.mts → container-loader-untrimmed.d.ts} +5 -5
  116. package/lib/{container.d.mts → container.d.ts} +30 -28
  117. package/lib/container.d.ts.map +1 -0
  118. package/lib/{container.mjs → container.js} +179 -247
  119. package/lib/container.js.map +1 -0
  120. package/lib/{containerContext.d.mts → containerContext.d.ts} +4 -3
  121. package/lib/containerContext.d.ts.map +1 -0
  122. package/lib/{containerContext.mjs → containerContext.js} +3 -2
  123. package/lib/containerContext.js.map +1 -0
  124. package/lib/{containerStorageAdapter.d.mts → containerStorageAdapter.d.ts} +6 -7
  125. package/lib/containerStorageAdapter.d.ts.map +1 -0
  126. package/lib/{containerStorageAdapter.mjs → containerStorageAdapter.js} +13 -20
  127. package/lib/containerStorageAdapter.js.map +1 -0
  128. package/lib/{contracts.d.mts → contracts.d.ts} +4 -4
  129. package/lib/contracts.d.ts.map +1 -0
  130. package/lib/{contracts.mjs → contracts.js} +1 -1
  131. package/lib/contracts.js.map +1 -0
  132. package/lib/{debugLogger.d.mts → debugLogger.d.ts} +1 -1
  133. package/lib/debugLogger.d.ts.map +1 -0
  134. package/lib/{debugLogger.mjs → debugLogger.js} +2 -1
  135. package/lib/debugLogger.js.map +1 -0
  136. package/lib/{deltaManager.d.mts → deltaManager.d.ts} +6 -6
  137. package/lib/deltaManager.d.ts.map +1 -0
  138. package/lib/{deltaManager.mjs → deltaManager.js} +4 -4
  139. package/lib/deltaManager.js.map +1 -0
  140. package/lib/{deltaQueue.d.mts → deltaQueue.d.ts} +1 -1
  141. package/lib/deltaQueue.d.ts.map +1 -0
  142. package/lib/{deltaQueue.mjs → deltaQueue.js} +1 -1
  143. package/lib/deltaQueue.js.map +1 -0
  144. package/lib/{disposal.d.mts → disposal.d.ts} +1 -1
  145. package/lib/disposal.d.ts.map +1 -0
  146. package/lib/{disposal.mjs → disposal.js} +1 -1
  147. package/lib/disposal.js.map +1 -0
  148. package/lib/{error.d.mts → error.d.ts} +1 -1
  149. package/lib/error.d.ts.map +1 -0
  150. package/lib/{error.mjs → error.js} +1 -1
  151. package/lib/error.js.map +1 -0
  152. package/lib/{index.d.mts → index.d.ts} +7 -7
  153. package/lib/index.d.ts.map +1 -0
  154. package/lib/index.js +10 -0
  155. package/lib/index.js.map +1 -0
  156. package/lib/{loader.d.mts → loader.d.ts} +4 -4
  157. package/lib/loader.d.ts.map +1 -0
  158. package/lib/{loader.mjs → loader.js} +7 -11
  159. package/lib/loader.js.map +1 -0
  160. package/lib/location-redirection-utilities/{index.mjs → index.d.ts} +2 -2
  161. package/lib/location-redirection-utilities/index.d.ts.map +1 -0
  162. package/lib/location-redirection-utilities/{index.d.mts → index.js} +2 -2
  163. package/lib/location-redirection-utilities/index.js.map +1 -0
  164. package/lib/location-redirection-utilities/{resolveWithLocationRedirection.d.mts → resolveWithLocationRedirection.d.ts} +1 -1
  165. package/lib/location-redirection-utilities/resolveWithLocationRedirection.d.ts.map +1 -0
  166. package/lib/location-redirection-utilities/{resolveWithLocationRedirection.mjs → resolveWithLocationRedirection.js} +1 -1
  167. package/lib/location-redirection-utilities/resolveWithLocationRedirection.js.map +1 -0
  168. package/lib/{noopHeuristic.d.mts → noopHeuristic.d.ts} +1 -1
  169. package/lib/noopHeuristic.d.ts.map +1 -0
  170. package/lib/{noopHeuristic.mjs → noopHeuristic.js} +1 -1
  171. package/lib/noopHeuristic.js.map +1 -0
  172. package/lib/{packageVersion.d.mts → packageVersion.d.ts} +2 -2
  173. package/lib/packageVersion.d.ts.map +1 -0
  174. package/lib/{packageVersion.mjs → packageVersion.js} +2 -2
  175. package/lib/packageVersion.js.map +1 -0
  176. package/lib/{protocol.d.mts → protocol.d.ts} +1 -1
  177. package/lib/protocol.d.ts.map +1 -0
  178. package/lib/{protocol.mjs → protocol.js} +1 -1
  179. package/lib/protocol.js.map +1 -0
  180. package/lib/{protocolTreeDocumentStorageService.d.mts → protocolTreeDocumentStorageService.d.ts} +2 -2
  181. package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -0
  182. package/lib/{protocolTreeDocumentStorageService.mjs → protocolTreeDocumentStorageService.js} +2 -4
  183. package/lib/protocolTreeDocumentStorageService.js.map +1 -0
  184. package/lib/{quorum.d.mts → quorum.d.ts} +5 -1
  185. package/lib/quorum.d.ts.map +1 -0
  186. package/lib/{quorum.mjs → quorum.js} +1 -1
  187. package/lib/quorum.js.map +1 -0
  188. package/lib/{retriableDocumentStorageService.d.mts → retriableDocumentStorageService.d.ts} +3 -3
  189. package/lib/retriableDocumentStorageService.d.ts.map +1 -0
  190. package/lib/{retriableDocumentStorageService.mjs → retriableDocumentStorageService.js} +10 -8
  191. package/lib/retriableDocumentStorageService.js.map +1 -0
  192. package/lib/serializedStateManager.d.ts +44 -0
  193. package/lib/serializedStateManager.d.ts.map +1 -0
  194. package/lib/serializedStateManager.js +145 -0
  195. package/lib/serializedStateManager.js.map +1 -0
  196. package/lib/test/attachment.spec.js +380 -0
  197. package/lib/test/attachment.spec.js.map +1 -0
  198. package/lib/test/catchUpMonitor.spec.js +88 -0
  199. package/lib/test/catchUpMonitor.spec.js.map +1 -0
  200. package/lib/test/connectionManager.spec.js +201 -0
  201. package/lib/test/connectionManager.spec.js.map +1 -0
  202. package/lib/test/connectionStateHandler.spec.js +555 -0
  203. package/lib/test/connectionStateHandler.spec.js.map +1 -0
  204. package/lib/test/container.spec.js +64 -0
  205. package/lib/test/container.spec.js.map +1 -0
  206. package/lib/test/deltaManager.spec.js +405 -0
  207. package/lib/test/deltaManager.spec.js.map +1 -0
  208. package/lib/test/loader.spec.js +212 -0
  209. package/lib/test/loader.spec.js.map +1 -0
  210. package/lib/test/locationRedirectionTests.spec.js +44 -0
  211. package/lib/test/locationRedirectionTests.spec.js.map +1 -0
  212. package/lib/test/serializedStateManager.spec.js +148 -0
  213. package/lib/test/serializedStateManager.spec.js.map +1 -0
  214. package/lib/test/snapshotConversionTest.spec.js +79 -0
  215. package/lib/test/snapshotConversionTest.spec.js.map +1 -0
  216. package/lib/test/types/validateContainerLoaderPrevious.generated.js +38 -0
  217. package/lib/test/types/validateContainerLoaderPrevious.generated.js.map +1 -0
  218. package/lib/test/utils.spec.js +31 -0
  219. package/lib/test/utils.spec.js.map +1 -0
  220. package/lib/{utils.d.mts → utils.d.ts} +17 -12
  221. package/lib/utils.d.ts.map +1 -0
  222. package/lib/utils.js +206 -0
  223. package/lib/utils.js.map +1 -0
  224. package/package.json +63 -63
  225. package/src/attachment.ts +222 -0
  226. package/src/audience.ts +9 -3
  227. package/src/connectionManager.ts +9 -11
  228. package/src/connectionState.ts +1 -0
  229. package/src/connectionStateHandler.ts +8 -7
  230. package/src/container.ts +297 -323
  231. package/src/containerContext.ts +2 -1
  232. package/src/containerStorageAdapter.ts +21 -26
  233. package/src/contracts.ts +3 -3
  234. package/src/debugLogger.ts +2 -2
  235. package/src/deltaManager.ts +8 -8
  236. package/src/error.ts +2 -2
  237. package/src/index.ts +6 -6
  238. package/src/loader.ts +9 -13
  239. package/src/location-redirection-utilities/index.ts +1 -1
  240. package/src/packageVersion.ts +1 -1
  241. package/src/protocolTreeDocumentStorageService.ts +1 -3
  242. package/src/retriableDocumentStorageService.ts +18 -8
  243. package/src/serializedStateManager.ts +217 -0
  244. package/src/utils.ts +140 -43
  245. package/tsconfig.cjs.json +7 -0
  246. package/tsconfig.json +2 -5
  247. package/lib/audience.d.mts.map +0 -1
  248. package/lib/audience.mjs.map +0 -1
  249. package/lib/catchUpMonitor.d.mts.map +0 -1
  250. package/lib/catchUpMonitor.mjs.map +0 -1
  251. package/lib/connectionManager.d.mts.map +0 -1
  252. package/lib/connectionManager.mjs.map +0 -1
  253. package/lib/connectionState.d.mts.map +0 -1
  254. package/lib/connectionState.mjs.map +0 -1
  255. package/lib/connectionStateHandler.d.mts.map +0 -1
  256. package/lib/connectionStateHandler.mjs.map +0 -1
  257. package/lib/container.d.mts.map +0 -1
  258. package/lib/container.mjs.map +0 -1
  259. package/lib/containerContext.d.mts.map +0 -1
  260. package/lib/containerContext.mjs.map +0 -1
  261. package/lib/containerStorageAdapter.d.mts.map +0 -1
  262. package/lib/containerStorageAdapter.mjs.map +0 -1
  263. package/lib/contracts.d.mts.map +0 -1
  264. package/lib/contracts.mjs.map +0 -1
  265. package/lib/debugLogger.d.mts.map +0 -1
  266. package/lib/debugLogger.mjs.map +0 -1
  267. package/lib/deltaManager.d.mts.map +0 -1
  268. package/lib/deltaManager.mjs.map +0 -1
  269. package/lib/deltaQueue.d.mts.map +0 -1
  270. package/lib/deltaQueue.mjs.map +0 -1
  271. package/lib/disposal.d.mts.map +0 -1
  272. package/lib/disposal.mjs.map +0 -1
  273. package/lib/error.d.mts.map +0 -1
  274. package/lib/error.mjs.map +0 -1
  275. package/lib/index.d.mts.map +0 -1
  276. package/lib/index.mjs +0 -10
  277. package/lib/index.mjs.map +0 -1
  278. package/lib/loader.d.mts.map +0 -1
  279. package/lib/loader.mjs.map +0 -1
  280. package/lib/location-redirection-utilities/index.d.mts.map +0 -1
  281. package/lib/location-redirection-utilities/index.mjs.map +0 -1
  282. package/lib/location-redirection-utilities/resolveWithLocationRedirection.d.mts.map +0 -1
  283. package/lib/location-redirection-utilities/resolveWithLocationRedirection.mjs.map +0 -1
  284. package/lib/noopHeuristic.d.mts.map +0 -1
  285. package/lib/noopHeuristic.mjs.map +0 -1
  286. package/lib/packageVersion.d.mts.map +0 -1
  287. package/lib/packageVersion.mjs.map +0 -1
  288. package/lib/protocol.d.mts.map +0 -1
  289. package/lib/protocol.mjs.map +0 -1
  290. package/lib/protocolTreeDocumentStorageService.d.mts.map +0 -1
  291. package/lib/protocolTreeDocumentStorageService.mjs.map +0 -1
  292. package/lib/quorum.d.mts.map +0 -1
  293. package/lib/quorum.mjs.map +0 -1
  294. package/lib/retriableDocumentStorageService.d.mts.map +0 -1
  295. package/lib/retriableDocumentStorageService.mjs.map +0 -1
  296. package/lib/utils.d.mts.map +0 -1
  297. package/lib/utils.mjs +0 -133
  298. package/lib/utils.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serializedStateManager.js","sourceRoot":"","sources":["../src/serializedStateManager.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 {\n\tISequencedDocumentMessage,\n\tISnapshotTree,\n\tIVersion,\n} from \"@fluidframework/protocol-definitions\";\nimport { IGetPendingLocalStateProps, IRuntime } from \"@fluidframework/container-definitions\";\nimport {\n\tITelemetryLoggerExt,\n\tMonitoringContext,\n\tPerformanceEvent,\n\tUsageError,\n\tcreateChildMonitoringContext,\n} from \"@fluidframework/telemetry-utils\";\nimport { assert } from \"@fluidframework/core-utils\";\nimport {\n\tIDocumentStorageService,\n\tIResolvedUrl,\n\tISnapshot,\n} from \"@fluidframework/driver-definitions\";\nimport { isInstanceOfISnapshot } from \"@fluidframework/driver-utils\";\nimport { ISerializableBlobContents, getBlobContentsFromTree } from \"./containerStorageAdapter.js\";\nimport { IPendingContainerState } from \"./container.js\";\n\nexport class SerializedStateManager {\n\tprivate readonly processedOps: ISequencedDocumentMessage[] = [];\n\tprivate snapshot:\n\t\t| {\n\t\t\t\ttree: ISnapshotTree;\n\t\t\t\tblobs: ISerializableBlobContents;\n\t\t }\n\t\t| undefined;\n\tprivate readonly mc: MonitoringContext;\n\n\tconstructor(\n\t\tprivate readonly pendingLocalState: IPendingContainerState | undefined,\n\t\tsubLogger: ITelemetryLoggerExt,\n\t\tprivate readonly storageAdapter: Pick<\n\t\t\tIDocumentStorageService,\n\t\t\t\"readBlob\" | \"getSnapshotTree\" | \"getSnapshot\" | \"getVersions\"\n\t\t>,\n\t\tprivate readonly _offlineLoadEnabled: boolean,\n\t) {\n\t\tthis.mc = createChildMonitoringContext({\n\t\t\tlogger: subLogger,\n\t\t\tnamespace: \"serializedStateManager\",\n\t\t});\n\t}\n\n\tpublic get offlineLoadEnabled(): boolean {\n\t\treturn this._offlineLoadEnabled;\n\t}\n\n\tpublic addProcessedOp(message: ISequencedDocumentMessage) {\n\t\tif (this.offlineLoadEnabled) {\n\t\t\tthis.processedOps.push(message);\n\t\t}\n\t}\n\n\tprivate async getVersion(version: string | null): Promise<IVersion | undefined> {\n\t\tconst versions = await this.storageAdapter.getVersions(version, 1);\n\t\treturn versions[0];\n\t}\n\n\tpublic async fetchSnapshot(\n\t\tspecifiedVersion: string | undefined,\n\t\tsupportGetSnapshotApi: boolean | undefined,\n\t) {\n\t\tconst { snapshot, version } =\n\t\t\tthis.pendingLocalState === undefined\n\t\t\t\t? await this.fetchSnapshotCore(specifiedVersion, supportGetSnapshotApi)\n\t\t\t\t: { snapshot: this.pendingLocalState.baseSnapshot, version: undefined };\n\t\tconst snapshotTree: ISnapshotTree | undefined = isInstanceOfISnapshot(snapshot)\n\t\t\t? snapshot.snapshotTree\n\t\t\t: snapshot;\n\t\tif (this.pendingLocalState) {\n\t\t\tthis.snapshot = {\n\t\t\t\ttree: this.pendingLocalState.baseSnapshot,\n\t\t\t\tblobs: this.pendingLocalState.snapshotBlobs,\n\t\t\t};\n\t\t} else {\n\t\t\tassert(snapshotTree !== undefined, 0x8e4 /* Snapshot should exist */);\n\t\t\t// non-interactive clients will not have any pending state we want to save\n\t\t\tif (this.offlineLoadEnabled) {\n\t\t\t\tconst blobs = await getBlobContentsFromTree(snapshotTree, this.storageAdapter);\n\t\t\t\tthis.snapshot = { tree: snapshotTree, blobs };\n\t\t\t}\n\t\t}\n\t\treturn { snapshotTree, version };\n\t}\n\n\tprivate async fetchSnapshotCore(\n\t\tspecifiedVersion: string | undefined,\n\t\tsupportGetSnapshotApi: boolean | undefined,\n\t): Promise<{ snapshot?: ISnapshot | ISnapshotTree; version?: IVersion }> {\n\t\tif (\n\t\t\tthis.mc.config.getBoolean(\"Fluid.Container.UseLoadingGroupIdForSnapshotFetch\") ===\n\t\t\t\ttrue &&\n\t\t\tsupportGetSnapshotApi === true\n\t\t) {\n\t\t\tconst snapshot =\n\t\t\t\t(await this.storageAdapter.getSnapshot?.({\n\t\t\t\t\tversionId: specifiedVersion,\n\t\t\t\t})) ?? undefined;\n\t\t\tconst version: IVersion | undefined =\n\t\t\t\tsnapshot?.snapshotTree.id === undefined\n\t\t\t\t\t? undefined\n\t\t\t\t\t: {\n\t\t\t\t\t\t\tid: snapshot.snapshotTree.id,\n\t\t\t\t\t\t\ttreeId: snapshot.snapshotTree.id,\n\t\t\t\t\t };\n\n\t\t\tif (snapshot === undefined && specifiedVersion !== undefined) {\n\t\t\t\tthis.mc.logger.sendErrorEvent({\n\t\t\t\t\teventName: \"getSnapshotTreeFailed\",\n\t\t\t\t\tid: specifiedVersion,\n\t\t\t\t});\n\t\t\t\t// Not sure if this should be here actually\n\t\t\t} else if (snapshot !== undefined && version?.id === undefined) {\n\t\t\t\tthis.mc.logger.sendErrorEvent({\n\t\t\t\t\teventName: \"getSnapshotFetchedTreeWithoutVersionId\",\n\t\t\t\t\thasVersion: version !== undefined, // if hasVersion is true, this means that the contract with the service was broken.\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { snapshot, version };\n\t\t}\n\t\treturn this.fetchSnapshotTree(specifiedVersion);\n\t}\n\n\t/**\n\t * Get the most recent snapshot, or a specific version.\n\t * @param specifiedVersion - The specific version of the snapshot to retrieve\n\t * @returns The snapshot requested, or the latest snapshot if no version was specified, plus version ID\n\t */\n\tprivate async fetchSnapshotTree(\n\t\tspecifiedVersion: string | undefined,\n\t): Promise<{ snapshot?: ISnapshotTree; version?: IVersion | undefined }> {\n\t\tconst version = await this.getVersion(specifiedVersion ?? null);\n\n\t\tif (version === undefined && specifiedVersion !== undefined) {\n\t\t\t// We should have a defined version to load from if specified version requested\n\t\t\tthis.mc.logger.sendErrorEvent({\n\t\t\t\teventName: \"NoVersionFoundWhenSpecified\",\n\t\t\t\tid: specifiedVersion,\n\t\t\t});\n\t\t}\n\t\tconst snapshot = (await this.storageAdapter.getSnapshotTree(version)) ?? undefined;\n\n\t\tif (snapshot === undefined && version !== undefined) {\n\t\t\tthis.mc.logger.sendErrorEvent({ eventName: \"getSnapshotTreeFailed\", id: version.id });\n\t\t} else if (snapshot !== undefined && version?.id === undefined) {\n\t\t\tthis.mc.logger.sendErrorEvent({\n\t\t\t\teventName: \"getSnapshotFetchedTreeWithoutVersionId\",\n\t\t\t\thasVersion: version !== undefined, // if hasVersion is true, this means that the contract with the service was broken.\n\t\t\t});\n\t\t}\n\t\treturn { snapshot, version };\n\t}\n\n\t/**\n\t * This method is only meant to be used by Container.attach() to set the initial\n\t * base snapshot when attaching.\n\t * @param snapshot - snapshot and blobs collected while attaching\n\t */\n\tpublic setSnapshot(\n\t\tsnapshot:\n\t\t\t| {\n\t\t\t\t\ttree: ISnapshotTree;\n\t\t\t\t\tblobs: ISerializableBlobContents;\n\t\t\t }\n\t\t\t| undefined,\n\t) {\n\t\tthis.snapshot = snapshot;\n\t}\n\n\tpublic async getPendingLocalStateCore(\n\t\tprops: IGetPendingLocalStateProps,\n\t\tclientId: string | undefined,\n\t\truntime: Pick<IRuntime, \"getPendingLocalState\">,\n\t\tresolvedUrl: IResolvedUrl,\n\t) {\n\t\treturn PerformanceEvent.timedExecAsync(\n\t\t\tthis.mc.logger,\n\t\t\t{\n\t\t\t\teventName: \"getPendingLocalState\",\n\t\t\t\tnotifyImminentClosure: props.notifyImminentClosure,\n\t\t\t\tprocessedOpsSize: this.processedOps.length,\n\t\t\t\tclientId,\n\t\t\t},\n\t\t\tasync () => {\n\t\t\t\tif (!this.offlineLoadEnabled) {\n\t\t\t\t\tthrow new UsageError(\n\t\t\t\t\t\t\"Can't get pending local state unless offline load is enabled\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tassert(this.snapshot !== undefined, 0x8e5 /* no base data */);\n\t\t\t\tconst pendingRuntimeState = await runtime.getPendingLocalState(props);\n\t\t\t\tconst pendingState: IPendingContainerState = {\n\t\t\t\t\tattached: true,\n\t\t\t\t\tpendingRuntimeState,\n\t\t\t\t\tbaseSnapshot: this.snapshot.tree,\n\t\t\t\t\tsnapshotBlobs: this.snapshot.blobs,\n\t\t\t\t\tsavedOps: this.processedOps,\n\t\t\t\t\turl: resolvedUrl.url,\n\t\t\t\t\t// no need to save this if there is no pending runtime state\n\t\t\t\t\tclientId: pendingRuntimeState !== undefined ? clientId : undefined,\n\t\t\t\t};\n\n\t\t\t\treturn JSON.stringify(pendingState);\n\t\t\t},\n\t\t);\n\t}\n}\n"]}
@@ -0,0 +1,380 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { strict as assert } from "assert";
6
+ import { AttachState } from "@fluidframework/container-definitions";
7
+ import { v4 as uuid } from "uuid";
8
+ import { SummaryType } from "@fluidframework/protocol-definitions";
9
+ import { stringToBuffer } from "@fluid-internal/client-utils";
10
+ import { runRetriableAttachProcess, } from "../attachment.js";
11
+ import { combineAppAndProtocolSummary } from "../utils.js";
12
+ const emptySummary = combineAppAndProtocolSummary({ tree: {}, type: SummaryType.Tree }, { tree: {}, type: SummaryType.Tree });
13
+ const addCallCounts = (obj) => {
14
+ const calls = Object.keys(obj).reduce((pv, cv) => {
15
+ pv[cv] = 0;
16
+ return pv;
17
+ }, {});
18
+ return new Proxy(obj, {
19
+ get: (t, p, r) => {
20
+ if (p === "calls") {
21
+ return calls;
22
+ }
23
+ else {
24
+ calls[p]++;
25
+ return Reflect.get(t, p, r);
26
+ }
27
+ },
28
+ });
29
+ };
30
+ const createDetachStorage = (blobCount) => {
31
+ const blobs = new Map(Array.from({ length: blobCount }).map((_, i) => [
32
+ i.toString(),
33
+ stringToBuffer(`${i}-content`, "utf-8"),
34
+ ]));
35
+ return addCallCounts({
36
+ get size() {
37
+ return blobs.size;
38
+ },
39
+ getBlobIds() {
40
+ return [...blobs.keys()];
41
+ },
42
+ async readBlob(id) {
43
+ const content = blobs.get(id);
44
+ assert(content !== undefined, `no blob content for [${id}]`);
45
+ return content;
46
+ },
47
+ });
48
+ };
49
+ const createProxyWithFailDefault = (partial = {}) => {
50
+ return new Proxy(partial, {
51
+ get: (t, p, r) => {
52
+ if (p in t) {
53
+ return Reflect.get(t, p, r);
54
+ }
55
+ return new Proxy({}, {
56
+ get: () => assert.fail(`unexpected call too ${p.toString()}`),
57
+ });
58
+ },
59
+ });
60
+ };
61
+ describe("runRetriableAttachProcess", () => {
62
+ describe("end to end process", () => {
63
+ it("From DetachedDefaultData without blobs or offline", async () => {
64
+ const initial = {
65
+ state: AttachState.Detached,
66
+ };
67
+ let attachmentData;
68
+ await runRetriableAttachProcess({
69
+ initialAttachmentData: initial,
70
+ offlineLoadEnabled: false,
71
+ setAttachmentData: (data) => (attachmentData = data),
72
+ createAttachmentSummary: (redirectTable) => {
73
+ assert.strictEqual(redirectTable, undefined, "redirectTable");
74
+ return emptySummary;
75
+ },
76
+ createOrGetStorageService: async () => createProxyWithFailDefault(),
77
+ });
78
+ assert.strictEqual(attachmentData?.state, AttachState.Attached, "should be attached");
79
+ });
80
+ it("From DetachedDefaultData with offline and without blobs", async () => {
81
+ const initial = {
82
+ state: AttachState.Detached,
83
+ };
84
+ let attachmentData;
85
+ const snapshot = await runRetriableAttachProcess({
86
+ initialAttachmentData: initial,
87
+ offlineLoadEnabled: true,
88
+ setAttachmentData: (data) => (attachmentData = data),
89
+ createAttachmentSummary: (redirectTable) => {
90
+ assert.strictEqual(redirectTable, undefined, "redirectTable");
91
+ return emptySummary;
92
+ },
93
+ createOrGetStorageService: async () => ({
94
+ createBlob: async () => assert.fail("no blobs should be created"),
95
+ uploadSummaryWithContext: async () => assert.fail("no summary should be uploaded outside of create"),
96
+ }),
97
+ });
98
+ assert.strictEqual(attachmentData?.state, AttachState.Attached, "should be attached");
99
+ assert.notStrictEqual(snapshot, undefined, "should have snapshot");
100
+ });
101
+ it("From DetachedDefaultData with blobs and without offline", async () => {
102
+ const initial = {
103
+ state: AttachState.Detached,
104
+ };
105
+ let attachmentData;
106
+ const blobCount = 10;
107
+ const detachedBlobStorage = createDetachStorage(blobCount);
108
+ const storageAdapter = addCallCounts({
109
+ createBlob: async () => Promise.resolve({ id: uuid() }),
110
+ uploadSummaryWithContext: async () => Promise.resolve(uuid()),
111
+ });
112
+ await runRetriableAttachProcess({
113
+ initialAttachmentData: initial,
114
+ offlineLoadEnabled: false,
115
+ setAttachmentData: (data) => (attachmentData = data),
116
+ createAttachmentSummary: (redirectTable) => {
117
+ assert.strictEqual(redirectTable?.size, blobCount, "redirectTable?.size");
118
+ return emptySummary;
119
+ },
120
+ createOrGetStorageService: async () => storageAdapter,
121
+ detachedBlobStorage,
122
+ });
123
+ // expect every blob to read, and uploaded
124
+ assert.strictEqual(detachedBlobStorage.calls.readBlob, blobCount, "detachedBlobStorage.calls.readBlob");
125
+ assert.strictEqual(storageAdapter.calls.createBlob, blobCount, "storageAdapter.calls.createBlob");
126
+ // after blobs are uploaded summary should be
127
+ assert.strictEqual(storageAdapter.calls.uploadSummaryWithContext, 1, "storageAdapter.calls.uploadSummaryWithContext");
128
+ assert.strictEqual(attachmentData?.state, AttachState.Attached, "should be attached");
129
+ });
130
+ it("From DetachedDefaultData with zero blobs and without offline", async () => {
131
+ const initial = {
132
+ state: AttachState.Detached,
133
+ };
134
+ let attachmentData;
135
+ await runRetriableAttachProcess({
136
+ initialAttachmentData: initial,
137
+ offlineLoadEnabled: false,
138
+ setAttachmentData: (data) => (attachmentData = data),
139
+ createAttachmentSummary: (redirectTable) => {
140
+ assert.strictEqual(redirectTable, undefined, "redirectTable");
141
+ return emptySummary;
142
+ },
143
+ createOrGetStorageService: async () => createProxyWithFailDefault(),
144
+ // we have blobs storage, but it is empty,
145
+ // so it should be treat like there are no blobs
146
+ detachedBlobStorage: createProxyWithFailDefault({ size: 0 }),
147
+ });
148
+ assert.strictEqual(attachmentData?.state, AttachState.Attached, "should be attached");
149
+ });
150
+ });
151
+ describe("ends in intermediate state due to failure", () => {
152
+ it("From DetachedDefaultData without blobs with createAttachmentSummary failure", async () => {
153
+ const initial = {
154
+ state: AttachState.Detached,
155
+ };
156
+ let attachmentData;
157
+ const error = new Error("createAttachmentSummary failure");
158
+ try {
159
+ await runRetriableAttachProcess(createProxyWithFailDefault({
160
+ initialAttachmentData: initial,
161
+ detachedBlobStorage: undefined,
162
+ createAttachmentSummary: () => {
163
+ throw error;
164
+ },
165
+ }));
166
+ assert.fail("failure expected");
167
+ }
168
+ catch (e) {
169
+ assert.deepStrictEqual(e, error);
170
+ }
171
+ assert.deepStrictEqual(attachmentData, undefined, "attachment data shouldn't have been set");
172
+ });
173
+ it("From DetachedDefaultData without blobs getStorageService failure", async () => {
174
+ const initial = {
175
+ state: AttachState.Detached,
176
+ };
177
+ let attachmentData;
178
+ const error = new Error("getStorageService failure");
179
+ try {
180
+ await runRetriableAttachProcess(createProxyWithFailDefault({
181
+ initialAttachmentData: initial,
182
+ setAttachmentData: (data) => (attachmentData = data),
183
+ createAttachmentSummary: () => emptySummary,
184
+ detachedBlobStorage: undefined,
185
+ createOrGetStorageService: () => {
186
+ throw error;
187
+ },
188
+ }));
189
+ assert.fail("failure expected");
190
+ }
191
+ catch (e) {
192
+ assert.deepStrictEqual(e, error);
193
+ }
194
+ assert.deepStrictEqual(attachmentData, {
195
+ state: AttachState.Attaching,
196
+ blobs: "none",
197
+ summary: emptySummary,
198
+ }, "should have made it to attaching state");
199
+ });
200
+ it("From DetachedDefaultData with blobs with createBlob failure", async () => {
201
+ const initial = {
202
+ state: AttachState.Detached,
203
+ };
204
+ let attachmentData;
205
+ const blobCount = 10;
206
+ const detachedBlobStorage = createDetachStorage(blobCount);
207
+ const error = new Error("createBlob failure");
208
+ try {
209
+ await runRetriableAttachProcess({
210
+ initialAttachmentData: initial,
211
+ offlineLoadEnabled: false,
212
+ setAttachmentData: (data) => (attachmentData = data),
213
+ createAttachmentSummary: () => emptySummary,
214
+ createOrGetStorageService: async () => createProxyWithFailDefault({
215
+ createBlob: () => {
216
+ throw error;
217
+ },
218
+ }),
219
+ detachedBlobStorage,
220
+ });
221
+ assert.fail("failure expected");
222
+ }
223
+ catch (e) {
224
+ assert.deepStrictEqual(e, error);
225
+ }
226
+ assert.deepStrictEqual(attachmentData, {
227
+ state: AttachState.Detached,
228
+ blobs: "outstanding",
229
+ redirectTable: new Map(),
230
+ }, "should have made it to attaching state");
231
+ });
232
+ it("From DetachedDefaultData with blobs with createAttachmentSummary failure", async () => {
233
+ const initial = {
234
+ state: AttachState.Detached,
235
+ };
236
+ let attachmentData;
237
+ const blobCount = 10;
238
+ const detachedBlobStorage = createDetachStorage(blobCount);
239
+ const error = new Error("createAttachmentSummary failure");
240
+ try {
241
+ await runRetriableAttachProcess({
242
+ initialAttachmentData: initial,
243
+ offlineLoadEnabled: false,
244
+ setAttachmentData: (data) => (attachmentData = data),
245
+ createAttachmentSummary: () => {
246
+ throw error;
247
+ },
248
+ createOrGetStorageService: async () => createProxyWithFailDefault({
249
+ createBlob: async () => Promise.resolve({ id: uuid() }),
250
+ }),
251
+ detachedBlobStorage,
252
+ });
253
+ assert.fail("failure expected");
254
+ }
255
+ catch (e) {
256
+ assert.deepStrictEqual(e, error);
257
+ }
258
+ assert.deepStrictEqual(
259
+ // override redirectTable as it makes validation pain, and we have good coverage in other tests
260
+ {
261
+ ...attachmentData,
262
+ redirectTable: attachmentData && "redirectTable" in attachmentData ? new Map() : undefined,
263
+ }, {
264
+ state: AttachState.Detached,
265
+ blobs: "outstanding",
266
+ redirectTable: new Map(),
267
+ }, "should have made it to attaching state");
268
+ });
269
+ it("From DetachedDefaultData with blobs with uploadSummaryWithContext failure", async () => {
270
+ const initial = {
271
+ state: AttachState.Detached,
272
+ };
273
+ let attachmentData;
274
+ const blobCount = 10;
275
+ const detachedBlobStorage = createDetachStorage(blobCount);
276
+ const error = new Error("uploadSummaryWithContext failure");
277
+ try {
278
+ await runRetriableAttachProcess({
279
+ initialAttachmentData: initial,
280
+ offlineLoadEnabled: false,
281
+ setAttachmentData: (data) => (attachmentData = data),
282
+ createAttachmentSummary: (redirectTable) => {
283
+ assert.strictEqual(redirectTable?.size, 10, "redirectTable?.size");
284
+ return emptySummary;
285
+ },
286
+ createOrGetStorageService: async () => ({
287
+ createBlob: async () => Promise.resolve({ id: uuid() }),
288
+ uploadSummaryWithContext: () => {
289
+ throw error;
290
+ },
291
+ }),
292
+ detachedBlobStorage,
293
+ });
294
+ assert.fail("failure expected");
295
+ }
296
+ catch (e) {
297
+ assert.deepStrictEqual(e, error);
298
+ }
299
+ assert.deepStrictEqual(attachmentData, {
300
+ state: AttachState.Attaching,
301
+ blobs: "done",
302
+ summary: emptySummary,
303
+ }, "should have made it to attaching state");
304
+ });
305
+ });
306
+ describe("from intermediate state", () => {
307
+ it("From DetachedDataWithOutstandingBlobs", async () => {
308
+ const initial = {
309
+ state: AttachState.Detached,
310
+ blobs: "outstanding",
311
+ redirectTable: new Map(),
312
+ };
313
+ let attachmentData;
314
+ const blobCount = 10;
315
+ const detachedBlobStorage = createDetachStorage(blobCount);
316
+ const storageAdapter = addCallCounts({
317
+ createBlob: async () => Promise.resolve({ id: uuid() }),
318
+ uploadSummaryWithContext: async () => Promise.resolve(uuid()),
319
+ });
320
+ await runRetriableAttachProcess({
321
+ initialAttachmentData: initial,
322
+ offlineLoadEnabled: false,
323
+ setAttachmentData: (data) => (attachmentData = data),
324
+ createAttachmentSummary: (redirectTable) => {
325
+ assert.strictEqual(redirectTable?.size, blobCount, "redirectTable?.size");
326
+ return emptySummary;
327
+ },
328
+ createOrGetStorageService: async () => storageAdapter,
329
+ detachedBlobStorage,
330
+ });
331
+ // expect every blob to read, and uploaded
332
+ assert.strictEqual(initial.redirectTable.size, blobCount, "initial.redirectTable.size");
333
+ assert.strictEqual(detachedBlobStorage.calls.readBlob, blobCount, "detachedBlobStorage.calls.readBlob");
334
+ assert.strictEqual(storageAdapter.calls.createBlob, blobCount, "storageAdapter.calls.createBlob");
335
+ // after blobs are uploaded summary should be
336
+ assert.strictEqual(storageAdapter.calls.uploadSummaryWithContext, 1, "storageAdapter.calls.uploadSummaryWithContext");
337
+ assert.strictEqual(attachmentData?.state, AttachState.Attached, "should be attached");
338
+ });
339
+ it("From AttachingDataWithBlobs", async () => {
340
+ const initial = {
341
+ state: AttachState.Attaching,
342
+ blobs: "done",
343
+ summary: emptySummary,
344
+ };
345
+ let attachmentData;
346
+ const snapshot = await runRetriableAttachProcess(createProxyWithFailDefault({
347
+ initialAttachmentData: initial,
348
+ offlineLoadEnabled: true,
349
+ setAttachmentData: (data) => (attachmentData = data),
350
+ createOrGetStorageService: async () =>
351
+ // only the summary should be left to upload
352
+ createProxyWithFailDefault({
353
+ uploadSummaryWithContext: async () => Promise.resolve(uuid()),
354
+ }),
355
+ }));
356
+ assert.strictEqual(attachmentData?.state, AttachState.Attached, "should be attached");
357
+ assert.notStrictEqual(snapshot, undefined, "should have snapshot");
358
+ });
359
+ it("From AttachingDataWithoutBlobs", async () => {
360
+ const initial = {
361
+ state: AttachState.Attaching,
362
+ blobs: "none",
363
+ summary: emptySummary,
364
+ };
365
+ let attachmentData;
366
+ const snapshot = await runRetriableAttachProcess(createProxyWithFailDefault({
367
+ initialAttachmentData: initial,
368
+ offlineLoadEnabled: true,
369
+ setAttachmentData: (data) => (attachmentData = data),
370
+ createOrGetStorageService: async (summary) => {
371
+ assert.notStrictEqual(summary, undefined, "data.summary");
372
+ return createProxyWithFailDefault();
373
+ },
374
+ }));
375
+ assert.strictEqual(attachmentData?.state, AttachState.Attached, "should be attached");
376
+ assert.notStrictEqual(snapshot, undefined, "should have snapshot");
377
+ });
378
+ });
379
+ });
380
+ //# sourceMappingURL=attachment.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachment.spec.js","sourceRoot":"","sources":["../../src/test/attachment.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 { strict as assert } from \"assert\";\nimport { AttachState } from \"@fluidframework/container-definitions\";\nimport { v4 as uuid } from \"uuid\";\nimport { SummaryType } from \"@fluidframework/protocol-definitions\";\nimport { stringToBuffer } from \"@fluid-internal/client-utils\";\nimport { IDocumentStorageService } from \"@fluidframework/driver-definitions\";\nimport {\n\tAttachProcessProps,\n\tAttachingDataWithBlobs,\n\tDetachedDataWithOutstandingBlobs,\n\trunRetriableAttachProcess,\n\tDetachedDefaultData,\n\tAttachmentData,\n\tAttachingDataWithoutBlobs,\n} from \"../attachment.js\";\nimport { combineAppAndProtocolSummary } from \"../utils.js\";\n\nconst emptySummary = combineAppAndProtocolSummary(\n\t{ tree: {}, type: SummaryType.Tree },\n\t{ tree: {}, type: SummaryType.Tree },\n);\n\ntype ObjectWithCallCounts<T extends Record<string, any>> = T &\n\tRecord<\"calls\", Record<keyof T, number>>;\n\nconst addCallCounts = <T extends Record<string, any>>(obj: T): ObjectWithCallCounts<T> => {\n\tconst calls = Object.keys(obj).reduce((pv, cv) => {\n\t\tpv[cv] = 0;\n\t\treturn pv;\n\t}, {}) as Record<keyof T, number>;\n\n\treturn new Proxy<ObjectWithCallCounts<T>>(obj as ObjectWithCallCounts<T>, {\n\t\tget: (t, p, r): any => {\n\t\t\tif (p === \"calls\") {\n\t\t\t\treturn calls;\n\t\t\t} else {\n\t\t\t\tcalls[p as keyof T]++;\n\t\t\t\treturn Reflect.get(t, p, r);\n\t\t\t}\n\t\t},\n\t});\n};\n\nconst createDetachStorage = (\n\tblobCount: number,\n): ObjectWithCallCounts<Exclude<AttachProcessProps[\"detachedBlobStorage\"], undefined>> => {\n\tconst blobs = new Map<string, ArrayBufferLike>(\n\t\tArray.from({ length: blobCount }).map((_, i) => [\n\t\t\ti.toString(),\n\t\t\tstringToBuffer(`${i}-content`, \"utf-8\"),\n\t\t]),\n\t);\n\n\treturn addCallCounts({\n\t\tget size() {\n\t\t\treturn blobs.size;\n\t\t},\n\t\tgetBlobIds() {\n\t\t\treturn [...blobs.keys()];\n\t\t},\n\t\tasync readBlob(id) {\n\t\t\tconst content = blobs.get(id);\n\t\t\tassert(content !== undefined, `no blob content for [${id}]`);\n\t\t\treturn content;\n\t\t},\n\t});\n};\n\nconst createProxyWithFailDefault = <T extends Record<string, any> | undefined>(\n\tpartial: Partial<T> = {},\n): T => {\n\treturn new Proxy(partial, {\n\t\tget: (t, p, r): any => {\n\t\t\tif (p in t) {\n\t\t\t\treturn Reflect.get(t, p, r);\n\t\t\t}\n\n\t\t\treturn new Proxy(\n\t\t\t\t{},\n\t\t\t\t{\n\t\t\t\t\tget: () => assert.fail(`unexpected call too ${p.toString()}`),\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t}) as T;\n};\n\ndescribe(\"runRetriableAttachProcess\", () => {\n\tdescribe(\"end to end process\", () => {\n\t\tit(\"From DetachedDefaultData without blobs or offline\", async () => {\n\t\t\tconst initial: DetachedDefaultData = {\n\t\t\t\tstate: AttachState.Detached,\n\t\t\t};\n\t\t\tlet attachmentData: AttachmentData | undefined;\n\t\t\tawait runRetriableAttachProcess({\n\t\t\t\tinitialAttachmentData: initial,\n\t\t\t\tofflineLoadEnabled: false,\n\t\t\t\tsetAttachmentData: (data) => (attachmentData = data),\n\t\t\t\tcreateAttachmentSummary: (redirectTable) => {\n\t\t\t\t\tassert.strictEqual(redirectTable, undefined, \"redirectTable\");\n\t\t\t\t\treturn emptySummary;\n\t\t\t\t},\n\t\t\t\tcreateOrGetStorageService: async () =>\n\t\t\t\t\tcreateProxyWithFailDefault<IDocumentStorageService>(),\n\t\t\t});\n\n\t\t\tassert.strictEqual(attachmentData?.state, AttachState.Attached, \"should be attached\");\n\t\t});\n\n\t\tit(\"From DetachedDefaultData with offline and without blobs\", async () => {\n\t\t\tconst initial: DetachedDefaultData = {\n\t\t\t\tstate: AttachState.Detached,\n\t\t\t};\n\t\t\tlet attachmentData: AttachmentData | undefined;\n\t\t\tconst snapshot = await runRetriableAttachProcess({\n\t\t\t\tinitialAttachmentData: initial,\n\t\t\t\tofflineLoadEnabled: true,\n\t\t\t\tsetAttachmentData: (data) => (attachmentData = data),\n\t\t\t\tcreateAttachmentSummary: (redirectTable) => {\n\t\t\t\t\tassert.strictEqual(redirectTable, undefined, \"redirectTable\");\n\t\t\t\t\treturn emptySummary;\n\t\t\t\t},\n\t\t\t\tcreateOrGetStorageService: async () => ({\n\t\t\t\t\tcreateBlob: async () => assert.fail(\"no blobs should be created\"),\n\t\t\t\t\tuploadSummaryWithContext: async () =>\n\t\t\t\t\t\tassert.fail(\"no summary should be uploaded outside of create\"),\n\t\t\t\t}),\n\t\t\t});\n\n\t\t\tassert.strictEqual(attachmentData?.state, AttachState.Attached, \"should be attached\");\n\t\t\tassert.notStrictEqual(snapshot, undefined, \"should have snapshot\");\n\t\t});\n\n\t\tit(\"From DetachedDefaultData with blobs and without offline\", async () => {\n\t\t\tconst initial: DetachedDefaultData = {\n\t\t\t\tstate: AttachState.Detached,\n\t\t\t};\n\t\t\tlet attachmentData: AttachmentData | undefined;\n\t\t\tconst blobCount = 10;\n\t\t\tconst detachedBlobStorage = createDetachStorage(blobCount);\n\t\t\tconst storageAdapter = addCallCounts({\n\t\t\t\tcreateBlob: async () => Promise.resolve({ id: uuid() }),\n\t\t\t\tuploadSummaryWithContext: async () => Promise.resolve(uuid()),\n\t\t\t});\n\t\t\tawait runRetriableAttachProcess({\n\t\t\t\tinitialAttachmentData: initial,\n\t\t\t\tofflineLoadEnabled: false,\n\t\t\t\tsetAttachmentData: (data) => (attachmentData = data),\n\t\t\t\tcreateAttachmentSummary: (redirectTable) => {\n\t\t\t\t\tassert.strictEqual(redirectTable?.size, blobCount, \"redirectTable?.size\");\n\t\t\t\t\treturn emptySummary;\n\t\t\t\t},\n\t\t\t\tcreateOrGetStorageService: async () => storageAdapter,\n\t\t\t\tdetachedBlobStorage,\n\t\t\t});\n\n\t\t\t// expect every blob to read, and uploaded\n\t\t\tassert.strictEqual(\n\t\t\t\tdetachedBlobStorage.calls.readBlob,\n\t\t\t\tblobCount,\n\t\t\t\t\"detachedBlobStorage.calls.readBlob\",\n\t\t\t);\n\t\t\tassert.strictEqual(\n\t\t\t\tstorageAdapter.calls.createBlob,\n\t\t\t\tblobCount,\n\t\t\t\t\"storageAdapter.calls.createBlob\",\n\t\t\t);\n\n\t\t\t// after blobs are uploaded summary should be\n\t\t\tassert.strictEqual(\n\t\t\t\tstorageAdapter.calls.uploadSummaryWithContext,\n\t\t\t\t1,\n\t\t\t\t\"storageAdapter.calls.uploadSummaryWithContext\",\n\t\t\t);\n\n\t\t\tassert.strictEqual(attachmentData?.state, AttachState.Attached, \"should be attached\");\n\t\t});\n\n\t\tit(\"From DetachedDefaultData with zero blobs and without offline\", async () => {\n\t\t\tconst initial: DetachedDefaultData = {\n\t\t\t\tstate: AttachState.Detached,\n\t\t\t};\n\t\t\tlet attachmentData: AttachmentData | undefined;\n\t\t\tawait runRetriableAttachProcess({\n\t\t\t\tinitialAttachmentData: initial,\n\t\t\t\tofflineLoadEnabled: false,\n\t\t\t\tsetAttachmentData: (data) => (attachmentData = data),\n\t\t\t\tcreateAttachmentSummary: (redirectTable) => {\n\t\t\t\t\tassert.strictEqual(redirectTable, undefined, \"redirectTable\");\n\t\t\t\t\treturn emptySummary;\n\t\t\t\t},\n\t\t\t\tcreateOrGetStorageService: async () => createProxyWithFailDefault(),\n\t\t\t\t// we have blobs storage, but it is empty,\n\t\t\t\t// so it should be treat like there are no blobs\n\t\t\t\tdetachedBlobStorage: createProxyWithFailDefault<\n\t\t\t\t\tAttachProcessProps[\"detachedBlobStorage\"]\n\t\t\t\t>({ size: 0 }),\n\t\t\t});\n\n\t\t\tassert.strictEqual(attachmentData?.state, AttachState.Attached, \"should be attached\");\n\t\t});\n\t});\n\n\tdescribe(\"ends in intermediate state due to failure\", () => {\n\t\tit(\"From DetachedDefaultData without blobs with createAttachmentSummary failure\", async () => {\n\t\t\tconst initial: DetachedDefaultData = {\n\t\t\t\tstate: AttachState.Detached,\n\t\t\t};\n\t\t\tlet attachmentData: AttachmentData | undefined;\n\t\t\tconst error = new Error(\"createAttachmentSummary failure\");\n\t\t\ttry {\n\t\t\t\tawait runRetriableAttachProcess(\n\t\t\t\t\tcreateProxyWithFailDefault<AttachProcessProps>({\n\t\t\t\t\t\tinitialAttachmentData: initial,\n\t\t\t\t\t\tdetachedBlobStorage: undefined,\n\t\t\t\t\t\tcreateAttachmentSummary: () => {\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tassert.fail(\"failure expected\");\n\t\t\t} catch (e) {\n\t\t\t\tassert.deepStrictEqual(e, error);\n\t\t\t}\n\n\t\t\tassert.deepStrictEqual(\n\t\t\t\tattachmentData,\n\t\t\t\tundefined,\n\t\t\t\t\"attachment data shouldn't have been set\",\n\t\t\t);\n\t\t});\n\n\t\tit(\"From DetachedDefaultData without blobs getStorageService failure\", async () => {\n\t\t\tconst initial: DetachedDefaultData = {\n\t\t\t\tstate: AttachState.Detached,\n\t\t\t};\n\t\t\tlet attachmentData: AttachmentData | undefined;\n\t\t\tconst error = new Error(\"getStorageService failure\");\n\t\t\ttry {\n\t\t\t\tawait runRetriableAttachProcess(\n\t\t\t\t\tcreateProxyWithFailDefault<AttachProcessProps>({\n\t\t\t\t\t\tinitialAttachmentData: initial,\n\t\t\t\t\t\tsetAttachmentData: (data) => (attachmentData = data),\n\t\t\t\t\t\tcreateAttachmentSummary: () => emptySummary,\n\t\t\t\t\t\tdetachedBlobStorage: undefined,\n\t\t\t\t\t\tcreateOrGetStorageService: () => {\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tassert.fail(\"failure expected\");\n\t\t\t} catch (e) {\n\t\t\t\tassert.deepStrictEqual(e, error);\n\t\t\t}\n\n\t\t\tassert.deepStrictEqual<AttachingDataWithoutBlobs>(\n\t\t\t\tattachmentData,\n\t\t\t\t{\n\t\t\t\t\tstate: AttachState.Attaching,\n\t\t\t\t\tblobs: \"none\",\n\t\t\t\t\tsummary: emptySummary,\n\t\t\t\t},\n\t\t\t\t\"should have made it to attaching state\",\n\t\t\t);\n\t\t});\n\n\t\tit(\"From DetachedDefaultData with blobs with createBlob failure\", async () => {\n\t\t\tconst initial: DetachedDefaultData = {\n\t\t\t\tstate: AttachState.Detached,\n\t\t\t};\n\t\t\tlet attachmentData: AttachmentData | undefined;\n\t\t\tconst blobCount = 10;\n\t\t\tconst detachedBlobStorage = createDetachStorage(blobCount);\n\t\t\tconst error = new Error(\"createBlob failure\");\n\t\t\ttry {\n\t\t\t\tawait runRetriableAttachProcess({\n\t\t\t\t\tinitialAttachmentData: initial,\n\t\t\t\t\tofflineLoadEnabled: false,\n\t\t\t\t\tsetAttachmentData: (data) => (attachmentData = data),\n\t\t\t\t\tcreateAttachmentSummary: () => emptySummary,\n\t\t\t\t\tcreateOrGetStorageService: async () =>\n\t\t\t\t\t\tcreateProxyWithFailDefault<IDocumentStorageService>({\n\t\t\t\t\t\t\tcreateBlob: () => {\n\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}),\n\t\t\t\t\tdetachedBlobStorage,\n\t\t\t\t});\n\t\t\t\tassert.fail(\"failure expected\");\n\t\t\t} catch (e) {\n\t\t\t\tassert.deepStrictEqual(e, error);\n\t\t\t}\n\n\t\t\tassert.deepStrictEqual<DetachedDataWithOutstandingBlobs>(\n\t\t\t\tattachmentData,\n\t\t\t\t{\n\t\t\t\t\tstate: AttachState.Detached,\n\t\t\t\t\tblobs: \"outstanding\",\n\t\t\t\t\tredirectTable: new Map(),\n\t\t\t\t},\n\t\t\t\t\"should have made it to attaching state\",\n\t\t\t);\n\t\t});\n\n\t\tit(\"From DetachedDefaultData with blobs with createAttachmentSummary failure\", async () => {\n\t\t\tconst initial: DetachedDefaultData = {\n\t\t\t\tstate: AttachState.Detached,\n\t\t\t};\n\t\t\tlet attachmentData: AttachmentData | undefined;\n\t\t\tconst blobCount = 10;\n\t\t\tconst detachedBlobStorage = createDetachStorage(blobCount);\n\t\t\tconst error = new Error(\"createAttachmentSummary failure\");\n\t\t\ttry {\n\t\t\t\tawait runRetriableAttachProcess({\n\t\t\t\t\tinitialAttachmentData: initial,\n\t\t\t\t\tofflineLoadEnabled: false,\n\t\t\t\t\tsetAttachmentData: (data) => (attachmentData = data),\n\t\t\t\t\tcreateAttachmentSummary: () => {\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t},\n\t\t\t\t\tcreateOrGetStorageService: async () =>\n\t\t\t\t\t\tcreateProxyWithFailDefault<IDocumentStorageService>({\n\t\t\t\t\t\t\tcreateBlob: async () => Promise.resolve({ id: uuid() }),\n\t\t\t\t\t\t}),\n\t\t\t\t\tdetachedBlobStorage,\n\t\t\t\t});\n\t\t\t\tassert.fail(\"failure expected\");\n\t\t\t} catch (e) {\n\t\t\t\tassert.deepStrictEqual(e, error);\n\t\t\t}\n\n\t\t\tassert.deepStrictEqual<DetachedDataWithOutstandingBlobs>(\n\t\t\t\t// override redirectTable as it makes validation pain, and we have good coverage in other tests\n\t\t\t\t{\n\t\t\t\t\t...attachmentData,\n\t\t\t\t\tredirectTable:\n\t\t\t\t\t\tattachmentData && \"redirectTable\" in attachmentData ? new Map() : undefined,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tstate: AttachState.Detached,\n\t\t\t\t\tblobs: \"outstanding\",\n\t\t\t\t\tredirectTable: new Map(),\n\t\t\t\t},\n\t\t\t\t\"should have made it to attaching state\",\n\t\t\t);\n\t\t});\n\n\t\tit(\"From DetachedDefaultData with blobs with uploadSummaryWithContext failure\", async () => {\n\t\t\tconst initial: DetachedDefaultData = {\n\t\t\t\tstate: AttachState.Detached,\n\t\t\t};\n\t\t\tlet attachmentData: AttachmentData | undefined;\n\t\t\tconst blobCount = 10;\n\t\t\tconst detachedBlobStorage = createDetachStorage(blobCount);\n\t\t\tconst error = new Error(\"uploadSummaryWithContext failure\");\n\t\t\ttry {\n\t\t\t\tawait runRetriableAttachProcess({\n\t\t\t\t\tinitialAttachmentData: initial,\n\t\t\t\t\tofflineLoadEnabled: false,\n\t\t\t\t\tsetAttachmentData: (data) => (attachmentData = data),\n\t\t\t\t\tcreateAttachmentSummary: (redirectTable) => {\n\t\t\t\t\t\tassert.strictEqual(redirectTable?.size, 10, \"redirectTable?.size\");\n\t\t\t\t\t\treturn emptySummary;\n\t\t\t\t\t},\n\t\t\t\t\tcreateOrGetStorageService: async () => ({\n\t\t\t\t\t\tcreateBlob: async () => Promise.resolve({ id: uuid() }),\n\t\t\t\t\t\tuploadSummaryWithContext: () => {\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t\tdetachedBlobStorage,\n\t\t\t\t});\n\t\t\t\tassert.fail(\"failure expected\");\n\t\t\t} catch (e) {\n\t\t\t\tassert.deepStrictEqual(e, error);\n\t\t\t}\n\n\t\t\tassert.deepStrictEqual<AttachingDataWithBlobs>(\n\t\t\t\tattachmentData,\n\t\t\t\t{\n\t\t\t\t\tstate: AttachState.Attaching,\n\t\t\t\t\tblobs: \"done\",\n\t\t\t\t\tsummary: emptySummary,\n\t\t\t\t},\n\t\t\t\t\"should have made it to attaching state\",\n\t\t\t);\n\t\t});\n\t});\n\n\tdescribe(\"from intermediate state\", () => {\n\t\tit(\"From DetachedDataWithOutstandingBlobs\", async () => {\n\t\t\tconst initial: DetachedDataWithOutstandingBlobs = {\n\t\t\t\tstate: AttachState.Detached,\n\t\t\t\tblobs: \"outstanding\",\n\t\t\t\tredirectTable: new Map(),\n\t\t\t};\n\t\t\tlet attachmentData: AttachmentData | undefined;\n\t\t\tconst blobCount = 10;\n\t\t\tconst detachedBlobStorage = createDetachStorage(blobCount);\n\t\t\tconst storageAdapter = addCallCounts({\n\t\t\t\tcreateBlob: async () => Promise.resolve({ id: uuid() }),\n\t\t\t\tuploadSummaryWithContext: async () => Promise.resolve(uuid()),\n\t\t\t});\n\t\t\tawait runRetriableAttachProcess({\n\t\t\t\tinitialAttachmentData: initial,\n\t\t\t\tofflineLoadEnabled: false,\n\t\t\t\tsetAttachmentData: (data) => (attachmentData = data),\n\t\t\t\tcreateAttachmentSummary: (redirectTable) => {\n\t\t\t\t\tassert.strictEqual(redirectTable?.size, blobCount, \"redirectTable?.size\");\n\t\t\t\t\treturn emptySummary;\n\t\t\t\t},\n\t\t\t\tcreateOrGetStorageService: async () => storageAdapter,\n\t\t\t\tdetachedBlobStorage,\n\t\t\t});\n\n\t\t\t// expect every blob to read, and uploaded\n\t\t\tassert.strictEqual(initial.redirectTable.size, blobCount, \"initial.redirectTable.size\");\n\n\t\t\tassert.strictEqual(\n\t\t\t\tdetachedBlobStorage.calls.readBlob,\n\t\t\t\tblobCount,\n\t\t\t\t\"detachedBlobStorage.calls.readBlob\",\n\t\t\t);\n\t\t\tassert.strictEqual(\n\t\t\t\tstorageAdapter.calls.createBlob,\n\t\t\t\tblobCount,\n\t\t\t\t\"storageAdapter.calls.createBlob\",\n\t\t\t);\n\n\t\t\t// after blobs are uploaded summary should be\n\t\t\tassert.strictEqual(\n\t\t\t\tstorageAdapter.calls.uploadSummaryWithContext,\n\t\t\t\t1,\n\t\t\t\t\"storageAdapter.calls.uploadSummaryWithContext\",\n\t\t\t);\n\n\t\t\tassert.strictEqual(attachmentData?.state, AttachState.Attached, \"should be attached\");\n\t\t});\n\n\t\tit(\"From AttachingDataWithBlobs\", async () => {\n\t\t\tconst initial: AttachingDataWithBlobs = {\n\t\t\t\tstate: AttachState.Attaching,\n\t\t\t\tblobs: \"done\",\n\t\t\t\tsummary: emptySummary,\n\t\t\t};\n\t\t\tlet attachmentData: AttachmentData | undefined;\n\t\t\tconst snapshot = await runRetriableAttachProcess(\n\t\t\t\tcreateProxyWithFailDefault<AttachProcessProps>({\n\t\t\t\t\tinitialAttachmentData: initial,\n\t\t\t\t\tofflineLoadEnabled: true,\n\t\t\t\t\tsetAttachmentData: (data) => (attachmentData = data),\n\t\t\t\t\tcreateOrGetStorageService: async () =>\n\t\t\t\t\t\t// only the summary should be left to upload\n\t\t\t\t\t\tcreateProxyWithFailDefault<IDocumentStorageService>({\n\t\t\t\t\t\t\tuploadSummaryWithContext: async () => Promise.resolve(uuid()),\n\t\t\t\t\t\t}),\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tassert.strictEqual(attachmentData?.state, AttachState.Attached, \"should be attached\");\n\t\t\tassert.notStrictEqual(snapshot, undefined, \"should have snapshot\");\n\t\t});\n\n\t\tit(\"From AttachingDataWithoutBlobs\", async () => {\n\t\t\tconst initial: AttachingDataWithoutBlobs = {\n\t\t\t\tstate: AttachState.Attaching,\n\t\t\t\tblobs: \"none\",\n\t\t\t\tsummary: emptySummary,\n\t\t\t};\n\t\t\tlet attachmentData: AttachmentData | undefined;\n\t\t\tconst snapshot = await runRetriableAttachProcess(\n\t\t\t\tcreateProxyWithFailDefault<AttachProcessProps>({\n\t\t\t\t\tinitialAttachmentData: initial,\n\t\t\t\t\tofflineLoadEnabled: true,\n\t\t\t\t\tsetAttachmentData: (data) => (attachmentData = data),\n\t\t\t\t\tcreateOrGetStorageService: async (summary) => {\n\t\t\t\t\t\tassert.notStrictEqual(summary, undefined, \"data.summary\");\n\t\t\t\t\t\treturn createProxyWithFailDefault();\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tassert.strictEqual(attachmentData?.state, AttachState.Attached, \"should be attached\");\n\t\t\tassert.notStrictEqual(snapshot, undefined, \"should have snapshot\");\n\t\t});\n\t});\n});\n"]}
@@ -0,0 +1,88 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { strict as assert } from "assert";
6
+ import { TypedEventEmitter } from "@fluid-internal/client-utils";
7
+ import { CatchUpMonitor } from "../catchUpMonitor.js";
8
+ class MockDeltaManagerForCatchingUp extends TypedEventEmitter {
9
+ constructor(lastSequenceNumber = 5, lastKnownSeqNumber = 10) {
10
+ super();
11
+ this.lastSequenceNumber = lastSequenceNumber;
12
+ this.lastKnownSeqNumber = lastKnownSeqNumber;
13
+ }
14
+ /** Simulate processing op with the given sequence number, to trigger CatchUpMonitor */
15
+ emitOpWithSequenceNumber(sequenceNumber) {
16
+ this.emit("op", { sequenceNumber });
17
+ }
18
+ /** Trigger the CatchUpMonitor by emitting op with the target sequence number */
19
+ emitOpToCatchUp() {
20
+ this.emitOpWithSequenceNumber(this.lastKnownSeqNumber);
21
+ }
22
+ static create(sequenceNumbers = {}) {
23
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
24
+ return new MockDeltaManagerForCatchingUp(sequenceNumbers.lastSequenceNumber, sequenceNumbers.lastKnownSeqNumber);
25
+ }
26
+ }
27
+ describe("CatchUpMonitor", () => {
28
+ let monitor;
29
+ afterEach(() => {
30
+ monitor?.dispose();
31
+ });
32
+ it("constructor validates DeltaManager sequence number coherency", async () => {
33
+ const mockDeltaManager = MockDeltaManagerForCatchingUp.create({
34
+ lastSequenceNumber: 20,
35
+ lastKnownSeqNumber: 15, // Should be impossible in real world
36
+ });
37
+ assert.throws(() => new CatchUpMonitor(mockDeltaManager, () => { }), "Expect assert when DeltaManager in invalid state");
38
+ });
39
+ it("Emits caughtUp event when caught up to the point it was created", () => {
40
+ const mockDeltaManager = MockDeltaManagerForCatchingUp.create({
41
+ lastSequenceNumber: 10,
42
+ lastKnownSeqNumber: 15,
43
+ });
44
+ let caughtUp = false;
45
+ mockDeltaManager.lastKnownSeqNumber = 20;
46
+ monitor = new CatchUpMonitor(mockDeltaManager, () => {
47
+ caughtUp = true;
48
+ });
49
+ mockDeltaManager.lastKnownSeqNumber = 25; // Shouldn't change anything about the monitor
50
+ mockDeltaManager.emitOpWithSequenceNumber(19); // Less than 20
51
+ assert(!caughtUp, "Shouldn't be considered caught up yet");
52
+ mockDeltaManager.emitOpWithSequenceNumber(21); // Greater than 20
53
+ assert(caughtUp, "Should be considered caught up now");
54
+ });
55
+ it("Emits caught up immediately if last known/processed sequence numbers match", () => {
56
+ const mockDeltaManager = MockDeltaManagerForCatchingUp.create({
57
+ lastSequenceNumber: 10,
58
+ lastKnownSeqNumber: 10,
59
+ });
60
+ let caughtUp = false;
61
+ monitor = new CatchUpMonitor(mockDeltaManager, () => {
62
+ caughtUp = true;
63
+ });
64
+ assert(caughtUp, "caughtUp should have fired immediately");
65
+ });
66
+ it("Only emits caughtUp once", () => {
67
+ const mockDeltaManager = MockDeltaManagerForCatchingUp.create({
68
+ lastSequenceNumber: 10,
69
+ lastKnownSeqNumber: 15,
70
+ });
71
+ let caughtUpCount = 0;
72
+ monitor = new CatchUpMonitor(mockDeltaManager, () => {
73
+ ++caughtUpCount;
74
+ });
75
+ mockDeltaManager.emitOpWithSequenceNumber(15);
76
+ assert.equal(caughtUpCount, 1, "caughtUp should have fired once");
77
+ mockDeltaManager.emitOpWithSequenceNumber(16);
78
+ assert.equal(caughtUpCount, 1, "caughtUp should have fired only once");
79
+ });
80
+ it("Dispose removes all listeners", () => {
81
+ const mockDeltaManager = MockDeltaManagerForCatchingUp.create();
82
+ monitor = new CatchUpMonitor(mockDeltaManager, () => { });
83
+ monitor.dispose();
84
+ assert(monitor.disposed, "dispose() should set disposed");
85
+ assert.equal(mockDeltaManager.listenerCount("op"), 0, "CatchUpMonitor.dispose should remove listener on DeltaManager");
86
+ });
87
+ });
88
+ //# sourceMappingURL=catchUpMonitor.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catchUpMonitor.spec.js","sourceRoot":"","sources":["../../src/test/catchUpMonitor.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 { strict as assert } from \"assert\";\nimport { IDeltaManager, IDeltaManagerEvents } from \"@fluidframework/container-definitions\";\nimport { TypedEventEmitter } from \"@fluid-internal/client-utils\";\nimport { CatchUpMonitor } from \"../catchUpMonitor.js\";\n\nclass MockDeltaManagerForCatchingUp\n\textends TypedEventEmitter<IDeltaManagerEvents>\n\timplements Pick<IDeltaManager<any, any>, \"lastSequenceNumber\" | \"lastKnownSeqNumber\">\n{\n\tconstructor(\n\t\tpublic lastSequenceNumber: number = 5,\n\t\tpublic lastKnownSeqNumber: number = 10,\n\t) {\n\t\tsuper();\n\t}\n\n\t/** Simulate processing op with the given sequence number, to trigger CatchUpMonitor */\n\temitOpWithSequenceNumber(sequenceNumber: number) {\n\t\tthis.emit(\"op\", { sequenceNumber });\n\t}\n\n\t/** Trigger the CatchUpMonitor by emitting op with the target sequence number */\n\temitOpToCatchUp() {\n\t\tthis.emitOpWithSequenceNumber(this.lastKnownSeqNumber);\n\t}\n\n\tstatic create(\n\t\tsequenceNumbers: {\n\t\t\tlastSequenceNumber?: number;\n\t\t\tlastKnownSeqNumber?: number;\n\t\t} = {},\n\t): MockDeltaManagerForCatchingUp & IDeltaManager<any, any> {\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-return\n\t\treturn new MockDeltaManagerForCatchingUp(\n\t\t\tsequenceNumbers.lastSequenceNumber,\n\t\t\tsequenceNumbers.lastKnownSeqNumber,\n\t\t) as any;\n\t}\n}\n\ndescribe(\"CatchUpMonitor\", () => {\n\tlet monitor: CatchUpMonitor;\n\n\tafterEach(() => {\n\t\tmonitor?.dispose();\n\t});\n\n\tit(\"constructor validates DeltaManager sequence number coherency\", async () => {\n\t\tconst mockDeltaManager = MockDeltaManagerForCatchingUp.create({\n\t\t\tlastSequenceNumber: 20,\n\t\t\tlastKnownSeqNumber: 15, // Should be impossible in real world\n\t\t});\n\n\t\tassert.throws(\n\t\t\t() => new CatchUpMonitor(mockDeltaManager, () => {}),\n\t\t\t\"Expect assert when DeltaManager in invalid state\",\n\t\t);\n\t});\n\n\tit(\"Emits caughtUp event when caught up to the point it was created\", () => {\n\t\tconst mockDeltaManager = MockDeltaManagerForCatchingUp.create({\n\t\t\tlastSequenceNumber: 10,\n\t\t\tlastKnownSeqNumber: 15,\n\t\t});\n\t\tlet caughtUp = false;\n\n\t\tmockDeltaManager.lastKnownSeqNumber = 20;\n\t\tmonitor = new CatchUpMonitor(mockDeltaManager, () => {\n\t\t\tcaughtUp = true;\n\t\t});\n\t\tmockDeltaManager.lastKnownSeqNumber = 25; // Shouldn't change anything about the monitor\n\n\t\tmockDeltaManager.emitOpWithSequenceNumber(19); // Less than 20\n\t\tassert(!caughtUp, \"Shouldn't be considered caught up yet\");\n\t\tmockDeltaManager.emitOpWithSequenceNumber(21); // Greater than 20\n\t\tassert(caughtUp, \"Should be considered caught up now\");\n\t});\n\n\tit(\"Emits caught up immediately if last known/processed sequence numbers match\", () => {\n\t\tconst mockDeltaManager = MockDeltaManagerForCatchingUp.create({\n\t\t\tlastSequenceNumber: 10,\n\t\t\tlastKnownSeqNumber: 10,\n\t\t});\n\t\tlet caughtUp = false;\n\n\t\tmonitor = new CatchUpMonitor(mockDeltaManager, () => {\n\t\t\tcaughtUp = true;\n\t\t});\n\n\t\tassert(caughtUp, \"caughtUp should have fired immediately\");\n\t});\n\n\tit(\"Only emits caughtUp once\", () => {\n\t\tconst mockDeltaManager = MockDeltaManagerForCatchingUp.create({\n\t\t\tlastSequenceNumber: 10,\n\t\t\tlastKnownSeqNumber: 15,\n\t\t});\n\t\tlet caughtUpCount = 0;\n\n\t\tmonitor = new CatchUpMonitor(mockDeltaManager, () => {\n\t\t\t++caughtUpCount;\n\t\t});\n\n\t\tmockDeltaManager.emitOpWithSequenceNumber(15);\n\t\tassert.equal(caughtUpCount, 1, \"caughtUp should have fired once\");\n\t\tmockDeltaManager.emitOpWithSequenceNumber(16);\n\t\tassert.equal(caughtUpCount, 1, \"caughtUp should have fired only once\");\n\t});\n\n\tit(\"Dispose removes all listeners\", () => {\n\t\tconst mockDeltaManager = MockDeltaManagerForCatchingUp.create();\n\t\tmonitor = new CatchUpMonitor(mockDeltaManager, () => {});\n\n\t\tmonitor.dispose();\n\n\t\tassert(monitor.disposed, \"dispose() should set disposed\");\n\t\tassert.equal(\n\t\t\tmockDeltaManager.listenerCount(\"op\"),\n\t\t\t0,\n\t\t\t\"CatchUpMonitor.dispose should remove listener on DeltaManager\",\n\t\t);\n\t});\n});\n"]}