@fluid-experimental/tree 2.0.0-rc.2.0.1 → 2.0.0-rc.3.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 (571) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/api-report/experimental-tree.api.md +2 -2
  3. package/dist/ChangeCompression.d.ts +2 -2
  4. package/dist/ChangeCompression.d.ts.map +1 -1
  5. package/dist/ChangeCompression.js +1 -1
  6. package/dist/ChangeCompression.js.map +1 -1
  7. package/dist/ChangeTypes.d.ts +1 -1
  8. package/dist/ChangeTypes.d.ts.map +1 -1
  9. package/dist/ChangeTypes.js +4 -4
  10. package/dist/ChangeTypes.js.map +1 -1
  11. package/dist/Checkout.d.ts +3 -3
  12. package/dist/Checkout.d.ts.map +1 -1
  13. package/dist/Checkout.js +17 -17
  14. package/dist/Checkout.js.map +1 -1
  15. package/dist/EditLog.d.ts +1 -1
  16. package/dist/EditLog.d.ts.map +1 -1
  17. package/dist/EditLog.js +9 -9
  18. package/dist/EditLog.js.map +1 -1
  19. package/dist/EditUtilities.d.ts +3 -3
  20. package/dist/EditUtilities.d.ts.map +1 -1
  21. package/dist/EditUtilities.js +6 -6
  22. package/dist/EditUtilities.js.map +1 -1
  23. package/dist/Forest.d.ts.map +1 -1
  24. package/dist/Forest.js +23 -23
  25. package/dist/Forest.js.map +1 -1
  26. package/dist/HistoryEditFactory.d.ts +1 -1
  27. package/dist/HistoryEditFactory.d.ts.map +1 -1
  28. package/dist/HistoryEditFactory.js +7 -7
  29. package/dist/HistoryEditFactory.js.map +1 -1
  30. package/dist/IdConversion.d.ts.map +1 -1
  31. package/dist/IdConversion.js.map +1 -1
  32. package/dist/LogViewer.d.ts +2 -2
  33. package/dist/LogViewer.d.ts.map +1 -1
  34. package/dist/LogViewer.js +7 -7
  35. package/dist/LogViewer.js.map +1 -1
  36. package/dist/MergeHealth.d.ts.map +1 -1
  37. package/dist/MergeHealth.js +1 -1
  38. package/dist/MergeHealth.js.map +1 -1
  39. package/dist/NodeIdUtilities.d.ts +1 -1
  40. package/dist/NodeIdUtilities.d.ts.map +1 -1
  41. package/dist/NodeIdUtilities.js +1 -1
  42. package/dist/NodeIdUtilities.js.map +1 -1
  43. package/dist/PayloadUtilities.d.ts.map +1 -1
  44. package/dist/PayloadUtilities.js +2 -2
  45. package/dist/PayloadUtilities.js.map +1 -1
  46. package/dist/ReconciliationPath.d.ts +1 -1
  47. package/dist/ReconciliationPath.d.ts.map +1 -1
  48. package/dist/ReconciliationPath.js.map +1 -1
  49. package/dist/RevisionValueCache.d.ts.map +1 -1
  50. package/dist/RevisionValueCache.js +2 -2
  51. package/dist/RevisionValueCache.js.map +1 -1
  52. package/dist/RevisionView.d.ts +2 -2
  53. package/dist/RevisionView.d.ts.map +1 -1
  54. package/dist/RevisionView.js.map +1 -1
  55. package/dist/SerializationUtilities.d.ts +1 -1
  56. package/dist/SerializationUtilities.d.ts.map +1 -1
  57. package/dist/SerializationUtilities.js.map +1 -1
  58. package/dist/SharedTree.d.ts +8 -7
  59. package/dist/SharedTree.d.ts.map +1 -1
  60. package/dist/SharedTree.js +78 -78
  61. package/dist/SharedTree.js.map +1 -1
  62. package/dist/SharedTreeEncoder.d.ts +1 -1
  63. package/dist/SharedTreeEncoder.d.ts.map +1 -1
  64. package/dist/SharedTreeEncoder.js +31 -31
  65. package/dist/SharedTreeEncoder.js.map +1 -1
  66. package/dist/Summary.d.ts +2 -2
  67. package/dist/Summary.d.ts.map +1 -1
  68. package/dist/Summary.js +2 -2
  69. package/dist/Summary.js.map +1 -1
  70. package/dist/SummaryBackCompatibility.d.ts.map +1 -1
  71. package/dist/SummaryBackCompatibility.js.map +1 -1
  72. package/dist/SummaryTestUtilities.d.ts +1 -1
  73. package/dist/SummaryTestUtilities.d.ts.map +1 -1
  74. package/dist/SummaryTestUtilities.js.map +1 -1
  75. package/dist/Transaction.d.ts +3 -3
  76. package/dist/Transaction.d.ts.map +1 -1
  77. package/dist/Transaction.js +3 -3
  78. package/dist/Transaction.js.map +1 -1
  79. package/dist/TransactionInternal.d.ts +3 -3
  80. package/dist/TransactionInternal.d.ts.map +1 -1
  81. package/dist/TransactionInternal.js +6 -6
  82. package/dist/TransactionInternal.js.map +1 -1
  83. package/dist/TreeCompressor.d.ts +5 -1
  84. package/dist/TreeCompressor.d.ts.map +1 -1
  85. package/dist/TreeCompressor.js +5 -5
  86. package/dist/TreeCompressor.js.map +1 -1
  87. package/dist/TreeNodeHandle.d.ts +1 -1
  88. package/dist/TreeNodeHandle.d.ts.map +1 -1
  89. package/dist/TreeNodeHandle.js.map +1 -1
  90. package/dist/TreeView.d.ts +1 -1
  91. package/dist/TreeView.d.ts.map +1 -1
  92. package/dist/TreeView.js +2 -3
  93. package/dist/TreeView.js.map +1 -1
  94. package/dist/UuidUtilities.d.ts.map +1 -1
  95. package/dist/UuidUtilities.js.map +1 -1
  96. package/dist/id-compressor/AppendOnlySortedMap.d.ts.map +1 -1
  97. package/dist/id-compressor/AppendOnlySortedMap.js +3 -3
  98. package/dist/id-compressor/AppendOnlySortedMap.js.map +1 -1
  99. package/dist/id-compressor/IdCompressor.d.ts +1 -1
  100. package/dist/id-compressor/IdCompressor.d.ts.map +1 -1
  101. package/dist/id-compressor/IdCompressor.js +25 -26
  102. package/dist/id-compressor/IdCompressor.js.map +1 -1
  103. package/dist/id-compressor/IdRange.d.ts.map +1 -1
  104. package/dist/id-compressor/IdRange.js +2 -2
  105. package/dist/id-compressor/IdRange.js.map +1 -1
  106. package/dist/id-compressor/NumericUuid.d.ts.map +1 -1
  107. package/dist/id-compressor/NumericUuid.js +2 -1
  108. package/dist/id-compressor/NumericUuid.js.map +1 -1
  109. package/dist/id-compressor/SessionIdNormalizer.d.ts.map +1 -1
  110. package/dist/id-compressor/SessionIdNormalizer.js +8 -9
  111. package/dist/id-compressor/SessionIdNormalizer.js.map +1 -1
  112. package/dist/migration-shim/migrationDeltaHandler.d.ts +1 -1
  113. package/dist/migration-shim/migrationDeltaHandler.d.ts.map +1 -1
  114. package/dist/migration-shim/migrationDeltaHandler.js +11 -11
  115. package/dist/migration-shim/migrationDeltaHandler.js.map +1 -1
  116. package/dist/migration-shim/migrationShim.d.ts +8 -4
  117. package/dist/migration-shim/migrationShim.d.ts.map +1 -1
  118. package/dist/migration-shim/migrationShim.js +13 -13
  119. package/dist/migration-shim/migrationShim.js.map +1 -1
  120. package/dist/migration-shim/migrationShimFactory.d.ts +2 -2
  121. package/dist/migration-shim/migrationShimFactory.d.ts.map +1 -1
  122. package/dist/migration-shim/migrationShimFactory.js +2 -2
  123. package/dist/migration-shim/migrationShimFactory.js.map +1 -1
  124. package/dist/migration-shim/packageVersion.d.ts +0 -2
  125. package/dist/migration-shim/packageVersion.d.ts.map +1 -1
  126. package/dist/migration-shim/packageVersion.js +0 -2
  127. package/dist/migration-shim/packageVersion.js.map +1 -1
  128. package/dist/migration-shim/sharedTreeDeltaHandler.d.ts.map +1 -1
  129. package/dist/migration-shim/sharedTreeDeltaHandler.js +8 -8
  130. package/dist/migration-shim/sharedTreeDeltaHandler.js.map +1 -1
  131. package/dist/migration-shim/sharedTreeShim.d.ts +2 -2
  132. package/dist/migration-shim/sharedTreeShim.d.ts.map +1 -1
  133. package/dist/migration-shim/sharedTreeShim.js +8 -4
  134. package/dist/migration-shim/sharedTreeShim.js.map +1 -1
  135. package/dist/migration-shim/sharedTreeShimFactory.d.ts +1 -1
  136. package/dist/migration-shim/sharedTreeShimFactory.d.ts.map +1 -1
  137. package/dist/migration-shim/sharedTreeShimFactory.js +2 -2
  138. package/dist/migration-shim/sharedTreeShimFactory.js.map +1 -1
  139. package/dist/migration-shim/shimChannelServices.d.ts +1 -1
  140. package/dist/migration-shim/shimChannelServices.d.ts.map +1 -1
  141. package/dist/migration-shim/shimChannelServices.js.map +1 -1
  142. package/dist/migration-shim/shimDeltaConnection.d.ts +1 -1
  143. package/dist/migration-shim/shimDeltaConnection.d.ts.map +1 -1
  144. package/dist/migration-shim/shimDeltaConnection.js +2 -2
  145. package/dist/migration-shim/shimDeltaConnection.js.map +1 -1
  146. package/dist/migration-shim/shimHandle.d.ts.map +1 -1
  147. package/dist/migration-shim/shimHandle.js.map +1 -1
  148. package/dist/migration-shim/types.d.ts +1 -1
  149. package/dist/migration-shim/types.d.ts.map +1 -1
  150. package/dist/migration-shim/types.js.map +1 -1
  151. package/dist/migration-shim/utils.d.ts +1 -1
  152. package/dist/migration-shim/utils.d.ts.map +1 -1
  153. package/dist/migration-shim/utils.js.map +1 -1
  154. package/dist/persisted-types/0.0.2.d.ts +1 -1
  155. package/dist/persisted-types/0.0.2.d.ts.map +1 -1
  156. package/dist/persisted-types/0.0.2.js.map +1 -1
  157. package/dist/persisted-types/0.1.1.d.ts +1 -1
  158. package/dist/persisted-types/0.1.1.d.ts.map +1 -1
  159. package/dist/persisted-types/0.1.1.js +3 -3
  160. package/dist/persisted-types/0.1.1.js.map +1 -1
  161. package/lib/ChangeCompression.d.ts +2 -2
  162. package/lib/ChangeCompression.d.ts.map +1 -1
  163. package/lib/ChangeCompression.js +1 -1
  164. package/lib/ChangeCompression.js.map +1 -1
  165. package/lib/ChangeTypes.d.ts +1 -1
  166. package/lib/ChangeTypes.d.ts.map +1 -1
  167. package/lib/ChangeTypes.js +2 -2
  168. package/lib/ChangeTypes.js.map +1 -1
  169. package/lib/Checkout.d.ts +3 -3
  170. package/lib/Checkout.d.ts.map +1 -1
  171. package/lib/Checkout.js +4 -4
  172. package/lib/Checkout.js.map +1 -1
  173. package/lib/EditLog.d.ts +1 -1
  174. package/lib/EditLog.d.ts.map +1 -1
  175. package/lib/EditLog.js +2 -2
  176. package/lib/EditLog.js.map +1 -1
  177. package/lib/EditUtilities.d.ts +3 -3
  178. package/lib/EditUtilities.d.ts.map +1 -1
  179. package/lib/EditUtilities.js +5 -5
  180. package/lib/EditUtilities.js.map +1 -1
  181. package/lib/Forest.d.ts.map +1 -1
  182. package/lib/Forest.js +2 -2
  183. package/lib/Forest.js.map +1 -1
  184. package/lib/HistoryEditFactory.d.ts +1 -1
  185. package/lib/HistoryEditFactory.d.ts.map +1 -1
  186. package/lib/HistoryEditFactory.js +5 -5
  187. package/lib/HistoryEditFactory.js.map +1 -1
  188. package/lib/IdConversion.d.ts.map +1 -1
  189. package/lib/IdConversion.js.map +1 -1
  190. package/lib/LogViewer.d.ts +2 -2
  191. package/lib/LogViewer.d.ts.map +1 -1
  192. package/lib/LogViewer.js +3 -3
  193. package/lib/LogViewer.js.map +1 -1
  194. package/lib/MergeHealth.d.ts.map +1 -1
  195. package/lib/MergeHealth.js +1 -1
  196. package/lib/MergeHealth.js.map +1 -1
  197. package/lib/NodeIdUtilities.d.ts +1 -1
  198. package/lib/NodeIdUtilities.d.ts.map +1 -1
  199. package/lib/NodeIdUtilities.js +1 -1
  200. package/lib/NodeIdUtilities.js.map +1 -1
  201. package/lib/PayloadUtilities.d.ts.map +1 -1
  202. package/lib/PayloadUtilities.js +1 -1
  203. package/lib/PayloadUtilities.js.map +1 -1
  204. package/lib/ReconciliationPath.d.ts +1 -1
  205. package/lib/ReconciliationPath.d.ts.map +1 -1
  206. package/lib/ReconciliationPath.js.map +1 -1
  207. package/lib/RevisionValueCache.d.ts.map +1 -1
  208. package/lib/RevisionValueCache.js +2 -2
  209. package/lib/RevisionValueCache.js.map +1 -1
  210. package/lib/RevisionView.d.ts +2 -2
  211. package/lib/RevisionView.d.ts.map +1 -1
  212. package/lib/RevisionView.js.map +1 -1
  213. package/lib/SerializationUtilities.d.ts +1 -1
  214. package/lib/SerializationUtilities.d.ts.map +1 -1
  215. package/lib/SerializationUtilities.js.map +1 -1
  216. package/lib/SharedTree.d.ts +8 -7
  217. package/lib/SharedTree.d.ts.map +1 -1
  218. package/lib/SharedTree.js +13 -13
  219. package/lib/SharedTree.js.map +1 -1
  220. package/lib/SharedTreeEncoder.d.ts +1 -1
  221. package/lib/SharedTreeEncoder.d.ts.map +1 -1
  222. package/lib/SharedTreeEncoder.js +5 -5
  223. package/lib/SharedTreeEncoder.js.map +1 -1
  224. package/lib/Summary.d.ts +2 -2
  225. package/lib/Summary.d.ts.map +1 -1
  226. package/lib/Summary.js +1 -1
  227. package/lib/Summary.js.map +1 -1
  228. package/lib/SummaryBackCompatibility.d.ts.map +1 -1
  229. package/lib/SummaryBackCompatibility.js.map +1 -1
  230. package/lib/SummaryTestUtilities.d.ts +1 -1
  231. package/lib/SummaryTestUtilities.d.ts.map +1 -1
  232. package/lib/SummaryTestUtilities.js.map +1 -1
  233. package/lib/Transaction.d.ts +3 -3
  234. package/lib/Transaction.d.ts.map +1 -1
  235. package/lib/Transaction.js +3 -3
  236. package/lib/Transaction.js.map +1 -1
  237. package/lib/TransactionInternal.d.ts +3 -3
  238. package/lib/TransactionInternal.d.ts.map +1 -1
  239. package/lib/TransactionInternal.js +3 -3
  240. package/lib/TransactionInternal.js.map +1 -1
  241. package/lib/TreeCompressor.d.ts +5 -1
  242. package/lib/TreeCompressor.d.ts.map +1 -1
  243. package/lib/TreeCompressor.js +2 -2
  244. package/lib/TreeCompressor.js.map +1 -1
  245. package/lib/TreeNodeHandle.d.ts +1 -1
  246. package/lib/TreeNodeHandle.d.ts.map +1 -1
  247. package/lib/TreeNodeHandle.js.map +1 -1
  248. package/lib/TreeView.d.ts +1 -1
  249. package/lib/TreeView.d.ts.map +1 -1
  250. package/lib/TreeView.js +1 -2
  251. package/lib/TreeView.js.map +1 -1
  252. package/lib/UuidUtilities.d.ts.map +1 -1
  253. package/lib/UuidUtilities.js +1 -1
  254. package/lib/UuidUtilities.js.map +1 -1
  255. package/lib/id-compressor/AppendOnlySortedMap.d.ts.map +1 -1
  256. package/lib/id-compressor/AppendOnlySortedMap.js +1 -1
  257. package/lib/id-compressor/AppendOnlySortedMap.js.map +1 -1
  258. package/lib/id-compressor/IdCompressor.d.ts +1 -1
  259. package/lib/id-compressor/IdCompressor.d.ts.map +1 -1
  260. package/lib/id-compressor/IdCompressor.js +4 -5
  261. package/lib/id-compressor/IdCompressor.js.map +1 -1
  262. package/lib/id-compressor/IdRange.d.ts.map +1 -1
  263. package/lib/id-compressor/IdRange.js +1 -1
  264. package/lib/id-compressor/IdRange.js.map +1 -1
  265. package/lib/id-compressor/NumericUuid.d.ts.map +1 -1
  266. package/lib/id-compressor/NumericUuid.js +2 -1
  267. package/lib/id-compressor/NumericUuid.js.map +1 -1
  268. package/lib/id-compressor/SessionIdNormalizer.d.ts.map +1 -1
  269. package/lib/id-compressor/SessionIdNormalizer.js +1 -2
  270. package/lib/id-compressor/SessionIdNormalizer.js.map +1 -1
  271. package/lib/migration-shim/migrationDeltaHandler.d.ts +1 -1
  272. package/lib/migration-shim/migrationDeltaHandler.d.ts.map +1 -1
  273. package/lib/migration-shim/migrationDeltaHandler.js +1 -1
  274. package/lib/migration-shim/migrationDeltaHandler.js.map +1 -1
  275. package/lib/migration-shim/migrationShim.d.ts +8 -4
  276. package/lib/migration-shim/migrationShim.d.ts.map +1 -1
  277. package/lib/migration-shim/migrationShim.js +3 -3
  278. package/lib/migration-shim/migrationShim.js.map +1 -1
  279. package/lib/migration-shim/migrationShimFactory.d.ts +2 -2
  280. package/lib/migration-shim/migrationShimFactory.d.ts.map +1 -1
  281. package/lib/migration-shim/migrationShimFactory.js +1 -1
  282. package/lib/migration-shim/migrationShimFactory.js.map +1 -1
  283. package/lib/migration-shim/packageVersion.d.ts +0 -2
  284. package/lib/migration-shim/packageVersion.d.ts.map +1 -1
  285. package/lib/migration-shim/packageVersion.js +0 -2
  286. package/lib/migration-shim/packageVersion.js.map +1 -1
  287. package/lib/migration-shim/sharedTreeDeltaHandler.d.ts.map +1 -1
  288. package/lib/migration-shim/sharedTreeDeltaHandler.js +1 -1
  289. package/lib/migration-shim/sharedTreeDeltaHandler.js.map +1 -1
  290. package/lib/migration-shim/sharedTreeShim.d.ts +2 -2
  291. package/lib/migration-shim/sharedTreeShim.d.ts.map +1 -1
  292. package/lib/migration-shim/sharedTreeShim.js +6 -2
  293. package/lib/migration-shim/sharedTreeShim.js.map +1 -1
  294. package/lib/migration-shim/sharedTreeShimFactory.d.ts +1 -1
  295. package/lib/migration-shim/sharedTreeShimFactory.d.ts.map +1 -1
  296. package/lib/migration-shim/sharedTreeShimFactory.js +1 -1
  297. package/lib/migration-shim/sharedTreeShimFactory.js.map +1 -1
  298. package/lib/migration-shim/shimChannelServices.d.ts +1 -1
  299. package/lib/migration-shim/shimChannelServices.d.ts.map +1 -1
  300. package/lib/migration-shim/shimChannelServices.js.map +1 -1
  301. package/lib/migration-shim/shimDeltaConnection.d.ts +1 -1
  302. package/lib/migration-shim/shimDeltaConnection.d.ts.map +1 -1
  303. package/lib/migration-shim/shimDeltaConnection.js +1 -1
  304. package/lib/migration-shim/shimDeltaConnection.js.map +1 -1
  305. package/lib/migration-shim/shimHandle.d.ts.map +1 -1
  306. package/lib/migration-shim/shimHandle.js.map +1 -1
  307. package/lib/migration-shim/types.d.ts +1 -1
  308. package/lib/migration-shim/types.d.ts.map +1 -1
  309. package/lib/migration-shim/types.js.map +1 -1
  310. package/lib/migration-shim/utils.d.ts +1 -1
  311. package/lib/migration-shim/utils.d.ts.map +1 -1
  312. package/lib/migration-shim/utils.js.map +1 -1
  313. package/lib/persisted-types/0.0.2.d.ts +1 -1
  314. package/lib/persisted-types/0.0.2.d.ts.map +1 -1
  315. package/lib/persisted-types/0.0.2.js.map +1 -1
  316. package/lib/persisted-types/0.1.1.d.ts +1 -1
  317. package/lib/persisted-types/0.1.1.d.ts.map +1 -1
  318. package/lib/persisted-types/0.1.1.js +1 -1
  319. package/lib/persisted-types/0.1.1.js.map +1 -1
  320. package/package.json +33 -32
  321. package/src/ChangeCompression.ts +8 -8
  322. package/src/ChangeTypes.ts +5 -4
  323. package/src/Checkout.ts +9 -7
  324. package/src/EditLog.ts +4 -3
  325. package/src/EditUtilities.ts +9 -8
  326. package/src/Forest.ts +3 -2
  327. package/src/HistoryEditFactory.ts +12 -11
  328. package/src/IdConversion.ts +2 -2
  329. package/src/LogViewer.ts +5 -4
  330. package/src/MergeHealth.ts +2 -1
  331. package/src/NodeIdUtilities.ts +2 -2
  332. package/src/PayloadUtilities.ts +2 -1
  333. package/src/ReconciliationPath.ts +1 -1
  334. package/src/RevisionValueCache.ts +3 -2
  335. package/src/RevisionView.ts +3 -3
  336. package/src/SerializationUtilities.ts +1 -1
  337. package/src/SharedTree.ts +46 -49
  338. package/src/SharedTreeEncoder.ts +30 -29
  339. package/src/Summary.ts +5 -3
  340. package/src/SummaryBackCompatibility.ts +1 -0
  341. package/src/SummaryTestUtilities.ts +2 -1
  342. package/src/Transaction.ts +7 -6
  343. package/src/TransactionInternal.ts +17 -16
  344. package/src/TreeCompressor.ts +6 -4
  345. package/src/TreeNodeHandle.ts +2 -2
  346. package/src/TreeView.ts +3 -3
  347. package/src/UuidUtilities.ts +2 -1
  348. package/src/id-compressor/AppendOnlySortedMap.ts +2 -1
  349. package/src/id-compressor/IdCompressor.ts +18 -17
  350. package/src/id-compressor/IdRange.ts +2 -1
  351. package/src/id-compressor/NumericUuid.ts +1 -1
  352. package/src/id-compressor/SessionIdNormalizer.ts +3 -3
  353. package/src/migration-shim/migrationDeltaHandler.ts +3 -2
  354. package/src/migration-shim/migrationShim.ts +14 -10
  355. package/src/migration-shim/migrationShimFactory.ts +6 -4
  356. package/src/migration-shim/packageVersion.ts +0 -2
  357. package/src/migration-shim/sharedTreeDeltaHandler.ts +3 -2
  358. package/src/migration-shim/sharedTreeShim.ts +7 -5
  359. package/src/migration-shim/sharedTreeShimFactory.ts +3 -3
  360. package/src/migration-shim/shimChannelServices.ts +1 -1
  361. package/src/migration-shim/shimDeltaConnection.ts +3 -2
  362. package/src/migration-shim/shimHandle.ts +1 -0
  363. package/src/migration-shim/types.ts +3 -1
  364. package/src/migration-shim/utils.ts +2 -1
  365. package/src/persisted-types/0.0.2.ts +2 -2
  366. package/src/persisted-types/0.1.1.ts +10 -8
  367. package/dist/tree-alpha.d.ts +0 -2901
  368. package/dist/tree-beta.d.ts +0 -348
  369. package/dist/tree-public.d.ts +0 -348
  370. package/dist/tree-untrimmed.d.ts +0 -3820
  371. package/lib/test/AppendOnlySortedMap.perf.tests.d.ts +0 -6
  372. package/lib/test/AppendOnlySortedMap.perf.tests.d.ts.map +0 -1
  373. package/lib/test/AppendOnlySortedMap.perf.tests.js +0 -49
  374. package/lib/test/AppendOnlySortedMap.perf.tests.js.map +0 -1
  375. package/lib/test/AppendOnlySortedMap.tests.d.ts +0 -6
  376. package/lib/test/AppendOnlySortedMap.tests.d.ts.map +0 -1
  377. package/lib/test/AppendOnlySortedMap.tests.js +0 -213
  378. package/lib/test/AppendOnlySortedMap.tests.js.map +0 -1
  379. package/lib/test/ChangeCompression.tests.d.ts +0 -6
  380. package/lib/test/ChangeCompression.tests.d.ts.map +0 -1
  381. package/lib/test/ChangeCompression.tests.js +0 -154
  382. package/lib/test/ChangeCompression.tests.js.map +0 -1
  383. package/lib/test/Checkout.tests.d.ts +0 -10
  384. package/lib/test/Checkout.tests.d.ts.map +0 -1
  385. package/lib/test/Checkout.tests.js +0 -460
  386. package/lib/test/Checkout.tests.js.map +0 -1
  387. package/lib/test/Common.tests.d.ts +0 -6
  388. package/lib/test/Common.tests.d.ts.map +0 -1
  389. package/lib/test/Common.tests.js +0 -102
  390. package/lib/test/Common.tests.js.map +0 -1
  391. package/lib/test/EagerCheckout.tests.d.ts +0 -6
  392. package/lib/test/EagerCheckout.tests.d.ts.map +0 -1
  393. package/lib/test/EagerCheckout.tests.js +0 -20
  394. package/lib/test/EagerCheckout.tests.js.map +0 -1
  395. package/lib/test/Edit.tests.d.ts +0 -6
  396. package/lib/test/Edit.tests.d.ts.map +0 -1
  397. package/lib/test/Edit.tests.js +0 -60
  398. package/lib/test/Edit.tests.js.map +0 -1
  399. package/lib/test/EditLog.perf.tests.d.ts +0 -6
  400. package/lib/test/EditLog.perf.tests.d.ts.map +0 -1
  401. package/lib/test/EditLog.perf.tests.js +0 -41
  402. package/lib/test/EditLog.perf.tests.js.map +0 -1
  403. package/lib/test/EditLog.tests.d.ts +0 -6
  404. package/lib/test/EditLog.tests.d.ts.map +0 -1
  405. package/lib/test/EditLog.tests.js +0 -355
  406. package/lib/test/EditLog.tests.js.map +0 -1
  407. package/lib/test/EditUtilities.tests.d.ts +0 -6
  408. package/lib/test/EditUtilities.tests.d.ts.map +0 -1
  409. package/lib/test/EditUtilities.tests.js +0 -512
  410. package/lib/test/EditUtilities.tests.js.map +0 -1
  411. package/lib/test/Forest.perf.tests.d.ts +0 -6
  412. package/lib/test/Forest.perf.tests.d.ts.map +0 -1
  413. package/lib/test/Forest.perf.tests.js +0 -135
  414. package/lib/test/Forest.perf.tests.js.map +0 -1
  415. package/lib/test/Forest.tests.d.ts +0 -6
  416. package/lib/test/Forest.tests.d.ts.map +0 -1
  417. package/lib/test/Forest.tests.js +0 -213
  418. package/lib/test/Forest.tests.js.map +0 -1
  419. package/lib/test/GenericTransaction.tests.d.ts +0 -6
  420. package/lib/test/GenericTransaction.tests.d.ts.map +0 -1
  421. package/lib/test/GenericTransaction.tests.js +0 -31
  422. package/lib/test/GenericTransaction.tests.js.map +0 -1
  423. package/lib/test/HistoryEditFactory.tests.d.ts +0 -6
  424. package/lib/test/HistoryEditFactory.tests.d.ts.map +0 -1
  425. package/lib/test/HistoryEditFactory.tests.js +0 -170
  426. package/lib/test/HistoryEditFactory.tests.js.map +0 -1
  427. package/lib/test/IdCompressor.perf.tests.d.ts +0 -6
  428. package/lib/test/IdCompressor.perf.tests.d.ts.map +0 -1
  429. package/lib/test/IdCompressor.perf.tests.js +0 -290
  430. package/lib/test/IdCompressor.perf.tests.js.map +0 -1
  431. package/lib/test/IdCompressor.tests.d.ts +0 -6
  432. package/lib/test/IdCompressor.tests.d.ts.map +0 -1
  433. package/lib/test/IdCompressor.tests.js +0 -1542
  434. package/lib/test/IdCompressor.tests.js.map +0 -1
  435. package/lib/test/IdConversion.tests.d.ts +0 -6
  436. package/lib/test/IdConversion.tests.d.ts.map +0 -1
  437. package/lib/test/IdConversion.tests.js +0 -36
  438. package/lib/test/IdConversion.tests.js.map +0 -1
  439. package/lib/test/LazyCheckout.tests.d.ts +0 -6
  440. package/lib/test/LazyCheckout.tests.d.ts.map +0 -1
  441. package/lib/test/LazyCheckout.tests.js +0 -22
  442. package/lib/test/LazyCheckout.tests.js.map +0 -1
  443. package/lib/test/LogViewer.tests.d.ts +0 -6
  444. package/lib/test/LogViewer.tests.d.ts.map +0 -1
  445. package/lib/test/LogViewer.tests.js +0 -588
  446. package/lib/test/LogViewer.tests.js.map +0 -1
  447. package/lib/test/MergeHealthTelemetryHeartbeat.tests.d.ts +0 -6
  448. package/lib/test/MergeHealthTelemetryHeartbeat.tests.d.ts.map +0 -1
  449. package/lib/test/MergeHealthTelemetryHeartbeat.tests.js +0 -351
  450. package/lib/test/MergeHealthTelemetryHeartbeat.tests.js.map +0 -1
  451. package/lib/test/NumericUuid.perf.tests.d.ts +0 -6
  452. package/lib/test/NumericUuid.perf.tests.d.ts.map +0 -1
  453. package/lib/test/NumericUuid.perf.tests.js +0 -68
  454. package/lib/test/NumericUuid.perf.tests.js.map +0 -1
  455. package/lib/test/NumericUuid.tests.d.ts +0 -6
  456. package/lib/test/NumericUuid.tests.d.ts.map +0 -1
  457. package/lib/test/NumericUuid.tests.js +0 -192
  458. package/lib/test/NumericUuid.tests.js.map +0 -1
  459. package/lib/test/RevisionValueCache.tests.d.ts +0 -6
  460. package/lib/test/RevisionValueCache.tests.d.ts.map +0 -1
  461. package/lib/test/RevisionValueCache.tests.js +0 -106
  462. package/lib/test/RevisionValueCache.tests.js.map +0 -1
  463. package/lib/test/RevisionView.tests.d.ts +0 -6
  464. package/lib/test/RevisionView.tests.d.ts.map +0 -1
  465. package/lib/test/RevisionView.tests.js +0 -131
  466. package/lib/test/RevisionView.tests.js.map +0 -1
  467. package/lib/test/SessionIdNormalizer.tests.d.ts +0 -6
  468. package/lib/test/SessionIdNormalizer.tests.d.ts.map +0 -1
  469. package/lib/test/SessionIdNormalizer.tests.js +0 -377
  470. package/lib/test/SessionIdNormalizer.tests.js.map +0 -1
  471. package/lib/test/SharedTree.fuzz.tests.d.ts +0 -6
  472. package/lib/test/SharedTree.fuzz.tests.d.ts.map +0 -1
  473. package/lib/test/SharedTree.fuzz.tests.js +0 -9
  474. package/lib/test/SharedTree.fuzz.tests.js.map +0 -1
  475. package/lib/test/SharedTree.perf.tests.d.ts +0 -6
  476. package/lib/test/SharedTree.perf.tests.d.ts.map +0 -1
  477. package/lib/test/SharedTree.perf.tests.js +0 -39
  478. package/lib/test/SharedTree.perf.tests.js.map +0 -1
  479. package/lib/test/SharedTree.tests.d.ts +0 -6
  480. package/lib/test/SharedTree.tests.d.ts.map +0 -1
  481. package/lib/test/SharedTree.tests.js +0 -22
  482. package/lib/test/SharedTree.tests.js.map +0 -1
  483. package/lib/test/StringInterner.tests.d.ts +0 -6
  484. package/lib/test/StringInterner.tests.d.ts.map +0 -1
  485. package/lib/test/StringInterner.tests.js +0 -73
  486. package/lib/test/StringInterner.tests.js.map +0 -1
  487. package/lib/test/Summary.tests.d.ts +0 -7
  488. package/lib/test/Summary.tests.d.ts.map +0 -1
  489. package/lib/test/Summary.tests.js +0 -386
  490. package/lib/test/Summary.tests.js.map +0 -1
  491. package/lib/test/Transaction.tests.d.ts +0 -6
  492. package/lib/test/Transaction.tests.d.ts.map +0 -1
  493. package/lib/test/Transaction.tests.js +0 -124
  494. package/lib/test/Transaction.tests.js.map +0 -1
  495. package/lib/test/TransactionInternal.tests.d.ts +0 -6
  496. package/lib/test/TransactionInternal.tests.d.ts.map +0 -1
  497. package/lib/test/TransactionInternal.tests.js +0 -576
  498. package/lib/test/TransactionInternal.tests.js.map +0 -1
  499. package/lib/test/TreeCompression.tests.d.ts +0 -6
  500. package/lib/test/TreeCompression.tests.d.ts.map +0 -1
  501. package/lib/test/TreeCompression.tests.js +0 -291
  502. package/lib/test/TreeCompression.tests.js.map +0 -1
  503. package/lib/test/TreeView.tests.d.ts +0 -6
  504. package/lib/test/TreeView.tests.d.ts.map +0 -1
  505. package/lib/test/TreeView.tests.js +0 -178
  506. package/lib/test/TreeView.tests.js.map +0 -1
  507. package/lib/test/UndoRedoHandler.tests.d.ts +0 -6
  508. package/lib/test/UndoRedoHandler.tests.d.ts.map +0 -1
  509. package/lib/test/UndoRedoHandler.tests.js +0 -37
  510. package/lib/test/UndoRedoHandler.tests.js.map +0 -1
  511. package/lib/test/fuzz/Generators.d.ts +0 -8
  512. package/lib/test/fuzz/Generators.d.ts.map +0 -1
  513. package/lib/test/fuzz/Generators.js +0 -345
  514. package/lib/test/fuzz/Generators.js.map +0 -1
  515. package/lib/test/fuzz/SharedTreeFuzzTests.d.ts +0 -23
  516. package/lib/test/fuzz/SharedTreeFuzzTests.d.ts.map +0 -1
  517. package/lib/test/fuzz/SharedTreeFuzzTests.js +0 -241
  518. package/lib/test/fuzz/SharedTreeFuzzTests.js.map +0 -1
  519. package/lib/test/fuzz/Types.d.ts +0 -136
  520. package/lib/test/fuzz/Types.d.ts.map +0 -1
  521. package/lib/test/fuzz/Types.js +0 -6
  522. package/lib/test/fuzz/Types.js.map +0 -1
  523. package/lib/test/utilities/IdCompressorTestUtilities.d.ts +0 -246
  524. package/lib/test/utilities/IdCompressorTestUtilities.d.ts.map +0 -1
  525. package/lib/test/utilities/IdCompressorTestUtilities.js +0 -608
  526. package/lib/test/utilities/IdCompressorTestUtilities.js.map +0 -1
  527. package/lib/test/utilities/MockTransaction.d.ts +0 -35
  528. package/lib/test/utilities/MockTransaction.d.ts.map +0 -1
  529. package/lib/test/utilities/MockTransaction.js +0 -51
  530. package/lib/test/utilities/MockTransaction.js.map +0 -1
  531. package/lib/test/utilities/PendingLocalStateTests.d.ts +0 -12
  532. package/lib/test/utilities/PendingLocalStateTests.d.ts.map +0 -1
  533. package/lib/test/utilities/PendingLocalStateTests.js +0 -223
  534. package/lib/test/utilities/PendingLocalStateTests.js.map +0 -1
  535. package/lib/test/utilities/SharedTreeTests.d.ts +0 -12
  536. package/lib/test/utilities/SharedTreeTests.d.ts.map +0 -1
  537. package/lib/test/utilities/SharedTreeTests.js +0 -949
  538. package/lib/test/utilities/SharedTreeTests.js.map +0 -1
  539. package/lib/test/utilities/SharedTreeVersioningTests.d.ts +0 -11
  540. package/lib/test/utilities/SharedTreeVersioningTests.d.ts.map +0 -1
  541. package/lib/test/utilities/SharedTreeVersioningTests.js +0 -439
  542. package/lib/test/utilities/SharedTreeVersioningTests.js.map +0 -1
  543. package/lib/test/utilities/SummaryLoadPerfTests.d.ts +0 -10
  544. package/lib/test/utilities/SummaryLoadPerfTests.d.ts.map +0 -1
  545. package/lib/test/utilities/SummaryLoadPerfTests.js +0 -105
  546. package/lib/test/utilities/SummaryLoadPerfTests.js.map +0 -1
  547. package/lib/test/utilities/SummarySizeTests.d.ts +0 -11
  548. package/lib/test/utilities/SummarySizeTests.d.ts.map +0 -1
  549. package/lib/test/utilities/SummarySizeTests.js +0 -160
  550. package/lib/test/utilities/SummarySizeTests.js.map +0 -1
  551. package/lib/test/utilities/TestCommon.d.ts +0 -13
  552. package/lib/test/utilities/TestCommon.d.ts.map +0 -1
  553. package/lib/test/utilities/TestCommon.js +0 -19
  554. package/lib/test/utilities/TestCommon.js.map +0 -1
  555. package/lib/test/utilities/TestNode.d.ts +0 -140
  556. package/lib/test/utilities/TestNode.d.ts.map +0 -1
  557. package/lib/test/utilities/TestNode.js +0 -282
  558. package/lib/test/utilities/TestNode.js.map +0 -1
  559. package/lib/test/utilities/TestSerializer.d.ts +0 -24
  560. package/lib/test/utilities/TestSerializer.d.ts.map +0 -1
  561. package/lib/test/utilities/TestSerializer.js +0 -40
  562. package/lib/test/utilities/TestSerializer.js.map +0 -1
  563. package/lib/test/utilities/TestUtilities.d.ts +0 -212
  564. package/lib/test/utilities/TestUtilities.d.ts.map +0 -1
  565. package/lib/test/utilities/TestUtilities.js +0 -413
  566. package/lib/test/utilities/TestUtilities.js.map +0 -1
  567. package/lib/test/utilities/UndoRedoTests.d.ts +0 -32
  568. package/lib/test/utilities/UndoRedoTests.d.ts.map +0 -1
  569. package/lib/test/utilities/UndoRedoTests.js +0 -317
  570. package/lib/test/utilities/UndoRedoTests.js.map +0 -1
  571. /package/{dist → lib}/tsdoc-metadata.json +0 -0
@@ -1,1542 +0,0 @@
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 { expect } from 'chai';
7
- import { v4, v5 } from 'uuid';
8
- import { MockLogger } from '@fluidframework/telemetry-utils';
9
- import { take } from '@fluid-private/stochastic-test-utils';
10
- import { validateAssertionError } from '@fluidframework/test-runtime-utils';
11
- import { IdCompressor, isFinalId, isLocalId, hasOngoingSession, legacySharedTreeInitialTreeId, } from '../id-compressor/IdCompressor.js';
12
- import { assertNotUndefined, fail } from '../Common.js';
13
- import { createSessionId, incrementUuid, numericUuidFromStableId, stableIdFromNumericUuid, } from '../id-compressor/NumericUuid.js';
14
- import { getIds } from '../id-compressor/IdRange.js';
15
- import { assertIsStableId, generateStableId, isStableId } from '../UuidUtilities.js';
16
- import { createCompressor, performFuzzActions, sessionIds, IdCompressorTestNetwork, Client, DestinationClient, MetaClient, expectSerializes, roundtrip, sessionNumericUuids, makeOpGenerator, attributionIds, generateCompressedIds, } from './utilities/IdCompressorTestUtilities.js';
17
- import { expectDefined, expectAssert } from './utilities/TestCommon.js';
18
- describe('IdCompressor', () => {
19
- it('detects invalid cluster sizes', () => {
20
- const compressor = createCompressor(Client.Client1, 1);
21
- assert.throws(() => (compressor.clusterCapacity = -1), (e) => validateAssertionError(e, 'Clusters must have a positive capacity'));
22
- assert.throws(() => (compressor.clusterCapacity = 0), (e) => validateAssertionError(e, 'Clusters must have a positive capacity'));
23
- assert.throws(() => (compressor.clusterCapacity = IdCompressor.maxClusterSize + 1), (e) => validateAssertionError(e, 'Clusters must not exceed max cluster size'));
24
- });
25
- it('reports the proper session ID', () => {
26
- const sessionId = createSessionId();
27
- const compressor = new IdCompressor(sessionId, 0);
28
- expect(compressor.localSessionId).to.equal(sessionId);
29
- });
30
- it('accepts different numbers of reserved IDs', () => {
31
- for (const reservedIdCount of [0, 1, 5]) {
32
- const compressor = new IdCompressor(createSessionId(), reservedIdCount);
33
- if (reservedIdCount > 0) {
34
- expect(compressor.decompress(compressor.getReservedId(0))).to.equal(legacySharedTreeInitialTreeId);
35
- }
36
- }
37
- });
38
- describe('ID Generation', () => {
39
- it('can create a compressed ID with an override', () => {
40
- const compressor = createCompressor(Client.Client1);
41
- const override = 'override';
42
- const id = compressor.generateCompressedId(override);
43
- expect(compressor.decompress(id)).to.equal(override);
44
- });
45
- it('can create compressed IDs with v5 overrides', () => {
46
- const compressor = createCompressor(Client.Client1);
47
- const uuidA = v5('foo', '7834b437-6e8c-4936-a1a3-0130b1178f17');
48
- const uuidB = uuidA.slice(0, uuidA.length - 1) + (uuidA.endsWith('a') ? 'b' : 'a');
49
- const idA = compressor.generateCompressedId(uuidA);
50
- const idB = compressor.generateCompressedId(uuidB);
51
- expect(compressor.decompress(idA)).to.equal(uuidA);
52
- expect(compressor.decompress(idB)).to.equal(uuidB);
53
- });
54
- it('can manually create a compressed ID', () => {
55
- const compressor = createCompressor(Client.Client1);
56
- const id = compressor.generateCompressedId();
57
- const uuid = compressor.decompress(id);
58
- expect(id).to.equal(compressor.recompress(uuid));
59
- });
60
- it('will not decompress IDs it did not compress', () => {
61
- const errorMessage = 'Compressed ID was not generated by this compressor';
62
- const compressor = createCompressor(Client.Client1);
63
- assert.throws(() => compressor.decompress(-1), (e) => validateAssertionError(e, errorMessage));
64
- assert.throws(() => compressor.decompress(compressor.reservedIdCount), (e) => validateAssertionError(e, errorMessage));
65
- });
66
- it('will not re-compress uuids it did not originally compress', () => {
67
- const compressor = createCompressor(Client.Client1);
68
- expect(compressor.tryRecompress('5fff846a-efd4-42fb-8b78-b32ce2672f99')).to.be.undefined;
69
- });
70
- it('unifies duplicate overrides originating from the same compressor', () => {
71
- const override = 'override';
72
- const compressor = createCompressor(Client.Client1, 3);
73
- // Client1 compresses a uuid
74
- const localId1 = compressor.generateCompressedId(override);
75
- const localId2 = compressor.generateCompressedId(override);
76
- expect(localId1).to.equal(localId2, 'only one local ID should be allocated for the same override');
77
- expect(compressor.decompress(localId1)).to.equal(override, 'override incorrectly associated with local ID');
78
- });
79
- it('unifies overrides with sequential local IDs', () => {
80
- const compressor = createCompressor(Client.Client1, 3);
81
- // Client1 compresses a uuid
82
- compressor.generateCompressedId();
83
- const localId2 = compressor.generateCompressedId();
84
- const stableId2 = assertIsStableId(compressor.decompress(localId2));
85
- const localId3 = compressor.generateCompressedId(stableId2);
86
- expect(localId3).to.equal(localId2, 'only one local ID should be allocated for the same sequential uuid');
87
- });
88
- it('unifies overrides with sequential local IDs that sort before the reserved session UUID', () => {
89
- // This is a regression test for an issue where passing a sequential UUID that sorted before the reserved UUID
90
- // as an override created duplicate overrides in the compressor.
91
- const newSession = `0${legacySharedTreeInitialTreeId.slice(1)}`;
92
- const compressor = new IdCompressor(newSession, 1 /* just needs to be > 0 */);
93
- // Client1 compresses a uuid
94
- compressor.generateCompressedId();
95
- const localId2 = compressor.generateCompressedId();
96
- const stableId2 = assertIsStableId(compressor.decompress(localId2));
97
- const localId3 = compressor.generateCompressedId(stableId2);
98
- expect(localId3).to.equal(localId2, 'only one local ID should be allocated for the same sequential uuid');
99
- });
100
- it('unifies overrides with sequential local IDs that sort after an existing override', () => {
101
- // This is a regression test for an issue where passing a sequential UUID that sorted after an existing override
102
- // as an override created duplicate overrides in the compressor.
103
- const newSession = `b${v4().slice(1)}`;
104
- const compressor = new IdCompressor(newSession, 0);
105
- // Client1 compresses a uuid with some override that will sort before the session uuid
106
- compressor.generateCompressedId(`a${v4().slice(1)}`);
107
- const localId2 = compressor.generateCompressedId();
108
- const stableId2 = assertIsStableId(compressor.decompress(localId2));
109
- const localId3 = compressor.generateCompressedId(stableId2);
110
- expect(localId3).to.equal(localId2, 'only one local ID should be allocated for the same sequential uuid');
111
- });
112
- it('unifies overrides with sequential local IDs that sort after an existing cluster', () => {
113
- // This is a regression test for an issue where passing a sequential UUID that sorted after an existing cluster
114
- // as an override created duplicate overrides in the compressor.
115
- const newSession1 = `c${v4().slice(1)}`;
116
- const newSession2 = `b${v4().slice(1)}`;
117
- const compressor1 = new IdCompressor(newSession1, 0);
118
- const compressor2 = new IdCompressor(newSession2, 0);
119
- compressor1.clusterCapacity = 5;
120
- compressor2.clusterCapacity = 5;
121
- compressor2.generateCompressedId(); // one ID, enough to make a cluster
122
- compressor1.finalizeCreationRange(compressor2.takeNextCreationRange());
123
- const localId = compressor1.generateCompressedId();
124
- const stableId2 = assertIsStableId(compressor1.decompress(localId));
125
- const localId3 = compressor1.generateCompressedId(stableId2);
126
- expect(localId3).to.equal(localId, 'only one local ID should be allocated for the same sequential uuid');
127
- });
128
- it('unifies unfinalized local overrides with final IDs from a remote session', () => {
129
- const compressor1 = createCompressor(Client.Client1, 3);
130
- const compressor2 = createCompressor(Client.Client2, 3);
131
- const override = 'override';
132
- const local1 = compressor1.generateCompressedId(override);
133
- const local2 = compressor2.generateCompressedId(override);
134
- const creationRange = compressor2.takeNextCreationRange();
135
- compressor1.finalizeCreationRange(creationRange);
136
- compressor2.finalizeCreationRange(creationRange);
137
- expect(compressor1.decompress(local1)).to.equal(override);
138
- const final1 = compressor1.normalizeToOpSpace(local1);
139
- const final2 = compressor2.normalizeToOpSpace(local2);
140
- expect(isFinalId(final1)).to.be.true;
141
- expect(final1).to.equal(final2);
142
- });
143
- it('unifies overrides with sequential local IDs that have been finalized', () => {
144
- const compressor = createCompressor(Client.Client1);
145
- const id = compressor.generateCompressedId();
146
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
147
- const stableId = assertIsStableId(compressor.decompress(id));
148
- const localId2 = compressor.generateCompressedId(stableId);
149
- expect(localId2).to.equal(id, 'only one local ID should be allocated for the same sequential uuid');
150
- });
151
- });
152
- describe('can enumerate all locally created IDs', () => {
153
- const idCount = 10;
154
- it('created without finalization', () => {
155
- const compressor = createCompressor(Client.Client1, idCount);
156
- const ids = [];
157
- for (let i = 0; i < idCount; i++) {
158
- ids.push(compressor.generateCompressedId());
159
- }
160
- const returnedIds = [...compressor.getAllIdsFromLocalSession()];
161
- expect(returnedIds).to.deep.equal(ids);
162
- });
163
- it('created with finalization', () => {
164
- const compressor = createCompressor(Client.Client1, idCount);
165
- const ids = [];
166
- for (let i = 0; i < idCount; i++) {
167
- if (i === Math.floor(idCount / 2)) {
168
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
169
- }
170
- ids.push(compressor.generateCompressedId());
171
- }
172
- const returnedIds = [...compressor.getAllIdsFromLocalSession()];
173
- expect(returnedIds).to.deep.equal(ids);
174
- });
175
- it('created with expanded finalization', () => {
176
- const compressor = createCompressor(Client.Client1, idCount);
177
- const ids = [];
178
- for (let i = 0; i < idCount * 4; i++) {
179
- if (i !== 0 && i % Math.floor(idCount / 3) === 0) {
180
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
181
- }
182
- ids.push(compressor.generateCompressedId());
183
- }
184
- const returnedIds = [...compressor.getAllIdsFromLocalSession()];
185
- expect(returnedIds).to.deep.equal(ids);
186
- });
187
- it('created with overrides', () => {
188
- const capacity = 100;
189
- const compressor = createCompressor(Client.Client1, capacity);
190
- const ids = [];
191
- for (let i = 0; i < capacity; i++) {
192
- if (i === 1) {
193
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
194
- }
195
- ids.push(compressor.generateCompressedId(i % 3 === 0 ? undefined : `override${i}`));
196
- }
197
- const returnedIds = [...compressor.getAllIdsFromLocalSession()];
198
- expect(returnedIds).to.deep.equal(ids);
199
- });
200
- });
201
- it('has default attribution ID', () => {
202
- const compressor = createCompressor(Client.Client1);
203
- expectDefined(compressor.attributionId);
204
- });
205
- it('correctly uses explicit attribution ID', () => {
206
- const attributionId = generateStableId();
207
- const compressor = createCompressor(Client.Client1, 5, attributionId);
208
- expect(compressor.attributionId).to.equal(attributionId);
209
- const range1 = compressor.takeNextCreationRange();
210
- expect(compressor.attributionId).to.equal(attributionId);
211
- expect(range1.attributionId).to.equal(attributionId);
212
- });
213
- it('only sends attribution ID until the first ID is allocated', () => {
214
- const compressor = createCompressor(Client.Client1, 5, generateStableId());
215
- let range = compressor.takeNextCreationRange();
216
- expectDefined(range.attributionId);
217
- range = compressor.takeNextCreationRange();
218
- expectDefined(range.attributionId);
219
- compressor.generateCompressedId();
220
- range = compressor.takeNextCreationRange();
221
- expectDefined(range.attributionId);
222
- range = compressor.takeNextCreationRange();
223
- expect(range.attributionId).to.be.undefined;
224
- });
225
- it('does not send default attribution ID', () => {
226
- const compressor = createCompressor(Client.Client1);
227
- const range = compressor.takeNextCreationRange();
228
- expect(range.attributionId).to.be.undefined;
229
- });
230
- it('attributes correctly after unification', () => {
231
- const compressor1 = createCompressor(Client.Client1, undefined, attributionIds.get(Client.Client1));
232
- const compressor2 = createCompressor(Client.Client2, undefined, attributionIds.get(Client.Client2));
233
- const id1 = compressor1.generateCompressedId('override');
234
- const id2 = compressor2.generateCompressedId('override');
235
- const range1 = compressor1.takeNextCreationRange();
236
- const range2 = compressor2.takeNextCreationRange();
237
- compressor1.finalizeCreationRange(range2); // 2 gets sequenced first
238
- compressor2.finalizeCreationRange(range2);
239
- compressor1.finalizeCreationRange(range1);
240
- compressor2.finalizeCreationRange(range1);
241
- expect(compressor1.normalizeToOpSpace(id1)).to.equal(compressor2.normalizeToOpSpace(id2));
242
- expect(compressor1.attributeId(id1)).to.equal(attributionIds.get(Client.Client2));
243
- expect(compressor1.attributeId(compressor1.recompress(compressor2.decompress(id2)))).to.equal(attributionIds.get(Client.Client2));
244
- expect(compressor2.attributeId(compressor2.recompress(compressor1.decompress(id1)))).to.equal(attributionIds.get(Client.Client2));
245
- expect(compressor2.attributeId(id2)).to.equal(attributionIds.get(Client.Client2));
246
- });
247
- describe('can produce a creation range', () => {
248
- const tests = [
249
- { title: 'that is empty', overrideIndices: [], idCount: 0 },
250
- { title: 'with only sequential IDs', overrideIndices: [], idCount: 3 },
251
- {
252
- title: 'with an overriding ID',
253
- overrideIndices: [0],
254
- idCount: 1,
255
- },
256
- {
257
- title: 'with a sequential ID before an overriding ID',
258
- overrideIndices: [1],
259
- idCount: 2,
260
- },
261
- {
262
- title: 'with a sequential ID after an overriding ID',
263
- overrideIndices: [0],
264
- idCount: 2,
265
- },
266
- {
267
- title: 'with an overriding ID between sequential IDs',
268
- overrideIndices: [1],
269
- idCount: 3,
270
- },
271
- {
272
- title: 'with a sequential ID between override IDs',
273
- overrideIndices: [0, 2],
274
- idCount: 3,
275
- },
276
- ];
277
- tests.forEach(({ title, overrideIndices, idCount }) => {
278
- it(title, () => {
279
- const compressor = createCompressor(Client.Client1);
280
- validateIdCreationRange(compressor, idCount, new Set(overrideIndices));
281
- });
282
- tests.forEach(({ title: title2, overrideIndices: overrideIndices2, idCount: idCount2 }) => {
283
- it(`${title2} after a range ${title}`, () => {
284
- const compressor = createCompressor(Client.Client1);
285
- const lastTaken = validateIdCreationRange(compressor, idCount, new Set(overrideIndices));
286
- validateIdCreationRange(compressor, idCount2, new Set(overrideIndices2), lastTaken);
287
- });
288
- });
289
- });
290
- function validateIdCreationRange(compressor, idCount, overrideIndices, lastTakenId = 0) {
291
- const overrides = [];
292
- for (let i = 0; i < idCount; i++) {
293
- const override = overrideIndices.has(i) ? v4() : undefined;
294
- const id = compressor.generateCompressedId(override);
295
- overrides.push([id, override]);
296
- }
297
- const range = compressor.takeNextCreationRange();
298
- let newLastTakenId = lastTakenId;
299
- let idsActual = getIds(range);
300
- if (overrides.length === 0) {
301
- expect(idsActual).to.be.undefined;
302
- }
303
- else {
304
- idsActual = expectDefined(idsActual);
305
- expect(overrides[0][0]).to.equal(idsActual.first);
306
- expect(overrides[overrides.length - 1][0]).to.equal(idsActual.last);
307
- for (const [id, uuid] of Object.entries(overrideIndices)) {
308
- expect(overrides[id][1]).to.equal(uuid);
309
- }
310
- newLastTakenId = idsActual.last;
311
- }
312
- return newLastTakenId;
313
- }
314
- });
315
- describe('Finalizing', () => {
316
- it('can finalize multiple overrides into the same cluster using different ranges', () => {
317
- const compressor = createCompressor(Client.Client1);
318
- const override1 = 'override1';
319
- const override2 = 'override2';
320
- const id1 = compressor.generateCompressedId(override1);
321
- compressor.generateCompressedId();
322
- const range1 = compressor.takeNextCreationRange();
323
- const id2 = compressor.generateCompressedId(override2);
324
- const range2 = compressor.takeNextCreationRange();
325
- compressor.finalizeCreationRange(range1);
326
- compressor.finalizeCreationRange(range2);
327
- const finalId1 = compressor.normalizeToOpSpace(id1);
328
- const finalId2 = compressor.normalizeToOpSpace(id2);
329
- expect(isFinalId(finalId1)).to.be.true;
330
- expect(isFinalId(finalId2)).to.be.true;
331
- expect(compressor.decompress(finalId1)).to.equal(override1);
332
- expect(compressor.decompress(finalId2)).to.equal(override2);
333
- });
334
- it('prevents attempts to finalize ranges twice', () => {
335
- const rangeCompressor = createCompressor(Client.Client1);
336
- generateCompressedIds(rangeCompressor, 3);
337
- const batchRange = rangeCompressor.takeNextCreationRange();
338
- rangeCompressor.finalizeCreationRange(batchRange);
339
- assert.throws(() => rangeCompressor.finalizeCreationRange(batchRange), (e) => validateAssertionError(e, 'Ranges finalized out of order.'));
340
- // Make a new compressor, as the first one will be left in a bad state
341
- const explicitCompressor = createCompressor(Client.Client1);
342
- explicitCompressor.generateCompressedId();
343
- explicitCompressor.generateCompressedId('override');
344
- const explicitRange = explicitCompressor.takeNextCreationRange();
345
- explicitCompressor.finalizeCreationRange(explicitRange);
346
- assert.throws(() => explicitCompressor.finalizeCreationRange(explicitRange), (e) => validateAssertionError(e, 'Ranges finalized out of order.'));
347
- });
348
- it('prevents attempts to finalize ranges out of order', () => {
349
- const compressor = createCompressor(Client.Client1);
350
- compressor.generateCompressedId();
351
- compressor.takeNextCreationRange();
352
- compressor.generateCompressedId();
353
- const secondRange = compressor.takeNextCreationRange();
354
- assert.throws(() => compressor.finalizeCreationRange(secondRange), (e) => validateAssertionError(e, 'Ranges finalized out of order.'));
355
- });
356
- it('can finalize ranges into clusters of varying sizes', () => {
357
- for (let i = 1; i < 5; i++) {
358
- for (let j = 0; j <= i; j++) {
359
- const compressor = createCompressor(Client.Client1, i);
360
- const ids = new Set();
361
- for (let k = 0; k <= j; k++) {
362
- ids.add(compressor.generateCompressedId());
363
- }
364
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
365
- const opIds = new Set();
366
- ids.forEach((id) => opIds.add(compressor.normalizeToOpSpace(id)));
367
- expect(ids.size).to.equal(opIds.size);
368
- opIds.forEach((id) => expect(isFinalId(id)).to.be.true);
369
- }
370
- }
371
- });
372
- it('prevents finalizing unacceptably enormous amounts of ID allocation', () => {
373
- const compressor1 = createCompressor(Client.Client1);
374
- const integerLargerThanHalfMax = Math.round((Number.MAX_SAFE_INTEGER / 3) * 2);
375
- const midPoint = -integerLargerThanHalfMax;
376
- const largeRange1 = {
377
- sessionId: sessionIds.get(Client.Client2),
378
- ids: { first: -1, last: midPoint },
379
- };
380
- compressor1.finalizeCreationRange(largeRange1);
381
- const largeRange2 = {
382
- sessionId: sessionIds.get(Client.Client2),
383
- ids: {
384
- first: (midPoint - 1),
385
- last: (-Number.MAX_SAFE_INTEGER - 2),
386
- },
387
- };
388
- assert.throws(() => compressor1.finalizeCreationRange(largeRange2), (e) => validateAssertionError(e, 'The number of allocated final IDs must not exceed the JS maximum safe integer.'));
389
- });
390
- });
391
- describe('Compression', () => {
392
- it('can re-compress a sequential uuid it generated', () => {
393
- const compressor = createCompressor(Client.Client1);
394
- const id = compressor.generateCompressedId();
395
- const uuid = compressor.decompress(id);
396
- expect(compressor.recompress(uuid)).to.equal(id);
397
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
398
- expect(compressor.recompress(uuid)).to.equal(id);
399
- });
400
- it('can re-compress an override', () => {
401
- const compressor = createCompressor(Client.Client1);
402
- const override = 'override';
403
- const id = compressor.generateCompressedId(override);
404
- expect(compressor.recompress(override)).to.equal(id);
405
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
406
- expect(compressor.recompress(override)).to.equal(id);
407
- });
408
- it('can re-compress overrides from a remote client it has finalized', () => {
409
- const compressor = createCompressor(Client.Client1);
410
- const id = compressor.generateCompressedId();
411
- const override = 'override';
412
- compressor.generateCompressedId(override);
413
- const uuid = compressor.decompress(id);
414
- const compressor2 = createCompressor(Client.Client2);
415
- compressor2.finalizeCreationRange(compressor.takeNextCreationRange());
416
- const finalId1 = compressor2.recompress(uuid);
417
- const finalId2 = compressor2.recompress(override);
418
- if (finalId1 === undefined || finalId2 === undefined) {
419
- expect.fail();
420
- }
421
- expect(isFinalId(finalId1)).to.be.true;
422
- expect(isFinalId(finalId2)).to.be.true;
423
- });
424
- it('will not compress an override it never compressed or finalized', () => {
425
- const compressor = createCompressor(Client.Client1, 5);
426
- // Leading zeroes to exploit calls to getOrNextLower on uuid maps, as it will be before test session uuids
427
- const override = 'override';
428
- expect(compressor.tryRecompress(override)).to.be.undefined;
429
- expect(compressor.tryRecompress(stableIdFromNumericUuid(sessionNumericUuids.get(Client.Client1), 1))).to.be
430
- .undefined;
431
- compressor.generateCompressedId(override);
432
- generateCompressedIds(compressor, 2);
433
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
434
- expect(compressor.tryRecompress(stableIdFromNumericUuid(sessionNumericUuids.get(Client.Client1), 4))).to.be
435
- .undefined;
436
- });
437
- it('can re-compress an eagerly generated final ID that is not finalized', () => {
438
- const compressor = createCompressor(Client.Client1, 5);
439
- compressor.generateCompressedId();
440
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
441
- const finalId = compressor.generateCompressedId();
442
- expect(isFinalId(finalId)).to.be.true;
443
- const stableId = stableIdFromNumericUuid(numericUuidFromStableId(sessionIds.get(Client.Client1)), 1);
444
- expect(compressor.recompress(stableId)).to.equal(finalId);
445
- });
446
- });
447
- describe('Decompression', () => {
448
- it('can decompress a local ID before and after finalizing', () => {
449
- const compressor = createCompressor(Client.Client1);
450
- const id = compressor.generateCompressedId();
451
- const uuid = compressor.decompress(id);
452
- expect(isStableId(uuid)).to.be.true;
453
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
454
- expect(compressor.decompress(id)).to.equal(uuid);
455
- });
456
- it('can decompress reserved IDs', () => {
457
- // This is a glass box test in that it increments UUIDs
458
- const compressor = createCompressor(Client.Client1);
459
- expect(compressor.decompress(compressor.getReservedId(0))).to.equal(legacySharedTreeInitialTreeId);
460
- const reservedSessionUuid = numericUuidFromStableId(assertIsStableId(compressor.decompress(compressor.getReservedId(1))));
461
- for (let i = 1; i < compressor.reservedIdCount; i++) {
462
- const reservedId = compressor.getReservedId(i);
463
- const stable = compressor.decompress(reservedId);
464
- expect(stable).to.equal(stableIdFromNumericUuid(incrementUuid(reservedSessionUuid, i - 1)));
465
- const finalIdForReserved = compressor.recompress(stable);
466
- expect(isLocalId(finalIdForReserved)).to.be.false;
467
- expect(finalIdForReserved).to.equal(reservedId);
468
- }
469
- const outOfBoundsError = 'Reserved Id index out of bounds';
470
- assert.throws(() => compressor.getReservedId(-1), (e) => validateAssertionError(e, outOfBoundsError));
471
- assert.throws(() => compressor.getReservedId(compressor.reservedIdCount), (e) => validateAssertionError(e, outOfBoundsError));
472
- });
473
- it('can decompress a final ID', () => {
474
- const compressor = createCompressor(Client.Client1);
475
- const id = compressor.generateCompressedId();
476
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
477
- const finalId = compressor.normalizeToOpSpace(id);
478
- if (isLocalId(finalId)) {
479
- expect.fail('Op space ID was finalized but is local');
480
- }
481
- const uuid = compressor.decompress(finalId);
482
- expect(isStableId(uuid)).to.be.true;
483
- });
484
- it('can decompress a final ID with an override', () => {
485
- const compressor = createCompressor(Client.Client1);
486
- const override = 'override';
487
- const id = compressor.generateCompressedId(override);
488
- const range = compressor.takeNextCreationRange();
489
- compressor.finalizeCreationRange(range);
490
- const finalId = compressor.normalizeToOpSpace(id);
491
- if (isLocalId(finalId)) {
492
- expect.fail('Op space ID was finalized but is local');
493
- }
494
- const uuid = compressor.decompress(finalId);
495
- expect(uuid).to.equal(override);
496
- });
497
- it('can decompress an override that is an UUID', () => {
498
- const compressor = createCompressor(Client.Client1, 5);
499
- const uuid = 'd1302ab1-3c08-4e79-a49a-4c39ac369c16';
500
- const id = compressor.generateCompressedId(uuid);
501
- expect(compressor.decompress(id)).to.equal(uuid);
502
- });
503
- it('properly sorts UUID-like overrides separately from true UUIDs', () => {
504
- // This is a glass box test that ensures overrides which would sort in between a cluster base UUID and an UUID higher up in the
505
- // same cluster are not accidentally retrieved during cluster ID lookup.
506
- const compressor = createCompressor(Client.Client1, 5);
507
- const override = `${compressor.localSessionId}6`;
508
- const id = compressor.generateCompressedId();
509
- compressor.generateCompressedId(override);
510
- const decompressedId = compressor.decompress(id);
511
- expect(compressor.recompress(decompressedId)).to.equal(id);
512
- });
513
- it('can decompress an override that starts with the reserved prefix character', () => {
514
- const compressor = createCompressor(Client.Client1, 5);
515
- const override = `\ue15e${compressor.localSessionId}`;
516
- const id = compressor.generateCompressedId(override);
517
- expect(compressor.decompress(id)).to.equal(override);
518
- });
519
- it('can decompress an eagerly generated final ID that is not finalized', () => {
520
- const compressor = createCompressor(Client.Client1, 5);
521
- compressor.generateCompressedId();
522
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
523
- const finalId = compressor.generateCompressedId();
524
- expect(isFinalId(finalId)).to.be.true;
525
- expect(compressor.decompress(finalId)).to.equal(stableIdFromNumericUuid(numericUuidFromStableId(sessionIds.get(Client.Client1)), 1));
526
- });
527
- });
528
- describe('Normalization', () => {
529
- it('can normalize a local ID to op space before finalizing', () => {
530
- const compressor = createCompressor(Client.Client1);
531
- const id = compressor.generateCompressedId();
532
- const normalized = compressor.normalizeToOpSpace(id);
533
- expect(isLocalId(id)).to.be.true;
534
- expect(id).to.equal(normalized);
535
- });
536
- it('can normalize a local ID to op space after finalizing', () => {
537
- const compressor = createCompressor(Client.Client1);
538
- const id = compressor.generateCompressedId();
539
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
540
- const normalized = compressor.normalizeToOpSpace(id);
541
- expect(isFinalId(normalized)).to.be.true;
542
- expect(id).to.not.equal(normalized);
543
- });
544
- it('can normalize an eagerly generated final ID', () => {
545
- const compressor = createCompressor(Client.Client1, 5);
546
- compressor.generateCompressedId();
547
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
548
- const eagerFinalId = compressor.generateCompressedId();
549
- expect(isFinalId(eagerFinalId)).to.be.true;
550
- const opNormalized = compressor.normalizeToOpSpace(eagerFinalId);
551
- expect(eagerFinalId).to.equal(opNormalized);
552
- const sessionNormalized = compressor.normalizeToSessionSpace(opNormalized, compressor.localSessionId);
553
- expect(sessionNormalized).to.equal(opNormalized);
554
- });
555
- it('cannot normalize a remote ID to session space if it has not been finalized', () => {
556
- const compressor1 = createCompressor(Client.Client1);
557
- const compressor2 = createCompressor(Client.Client2);
558
- const normalized = compressor1.normalizeToOpSpace(compressor1.generateCompressedId());
559
- assert.throws(() => compressor2.normalizeToSessionSpace(normalized, compressor1.localSessionId), (e) => validateAssertionError(e, 'No IDs have ever been finalized by the supplied session.'));
560
- });
561
- it('can normalize local and final IDs from a remote session to session space', () => {
562
- const compressor1 = createCompressor(Client.Client1);
563
- const compressor2 = createCompressor(Client.Client2);
564
- const id = compressor1.generateCompressedId();
565
- const normalizedLocal = compressor1.normalizeToOpSpace(id);
566
- const range = compressor1.takeNextCreationRange();
567
- compressor1.finalizeCreationRange(range);
568
- const normalizedFinal = compressor1.normalizeToOpSpace(id);
569
- compressor2.finalizeCreationRange(range);
570
- expect(isLocalId(normalizedLocal)).to.be.true;
571
- expect(isFinalId(normalizedFinal)).to.be.true;
572
- expect(compressor2.normalizeToSessionSpace(normalizedFinal, compressor1.localSessionId)).to.equal(normalizedFinal);
573
- expect(compressor2.normalizeToSessionSpace(normalizedLocal, compressor1.localSessionId)).to.equal(normalizedFinal);
574
- });
575
- it("can normalize a final ID created by the local session but sent in another client's op space", () => {
576
- // Regression test for the situation in which a client creates a final ID and another client references
577
- // that final ID in a message back to the creating client. The creating client will normalize it and
578
- // pass the session ID of the remote (non-creating) client. This should be handled correctly.
579
- const compressor = createCompressor(Client.Client1, 5);
580
- const compressor2 = createCompressor(Client.Client2, 5);
581
- const id = compressor.generateCompressedId();
582
- const creationRange = compressor.takeNextCreationRange();
583
- compressor.finalizeCreationRange(creationRange);
584
- compressor2.finalizeCreationRange(creationRange);
585
- const idInClient2OpSpace = compressor2.normalizeToOpSpace(compressor2.normalizeToSessionSpace(compressor.normalizeToOpSpace(id), compressor.localSessionId));
586
- const normalizedToClient1SessionSpace = compressor.normalizeToSessionSpace(idInClient2OpSpace, compressor2.localSessionId);
587
- expect(normalizedToClient1SessionSpace).to.equal(id);
588
- });
589
- });
590
- describe('Telemetry', () => {
591
- it('emits first cluster and new cluster telemetry events', () => {
592
- const mockLogger = new MockLogger();
593
- const compressor = createCompressor(Client.Client1, 5, undefined, mockLogger);
594
- const localId1 = compressor.generateCompressedId();
595
- expect(isLocalId(localId1)).to.be.true;
596
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
597
- mockLogger.assertMatch([
598
- {
599
- eventName: 'SharedTreeIdCompressor:FirstCluster',
600
- sessionId: '88888888-8888-4888-b088-888888888888',
601
- },
602
- {
603
- eventName: 'SharedTreeIdCompressor:NewCluster',
604
- sessionId: '88888888-8888-4888-b088-888888888888',
605
- clusterCapacity: 5,
606
- clusterCount: 1,
607
- },
608
- {
609
- eventName: 'SharedTreeIdCompressor:IdCompressorStatus',
610
- sessionId: '88888888-8888-4888-b088-888888888888',
611
- eagerFinalIdCount: 0,
612
- overridesCount: 0,
613
- localIdCount: 1,
614
- },
615
- ]);
616
- });
617
- it('emits new cluster event on second cluster', () => {
618
- // Fill the first cluster
619
- const mockLogger = new MockLogger();
620
- const compressor = createCompressor(Client.Client1, 5, undefined, mockLogger);
621
- for (let i = 0; i < 5; i++) {
622
- compressor.generateCompressedId();
623
- }
624
- const range = compressor.takeNextCreationRange();
625
- compressor.finalizeCreationRange(range);
626
- // Create another cluster with a different client so that expansion doesn't happen
627
- const mockLogger2 = new MockLogger();
628
- const compressor2 = createCompressor(Client.Client2, 5, undefined, mockLogger2);
629
- compressor2.finalizeCreationRange(range);
630
- compressor2.generateCompressedId();
631
- const range2 = compressor2.takeNextCreationRange();
632
- compressor2.finalizeCreationRange(range2);
633
- compressor.finalizeCreationRange(range2);
634
- // Make sure we emitted the FirstCluster event
635
- mockLogger.assertMatchAny([
636
- {
637
- eventName: 'SharedTreeIdCompressor:FirstCluster',
638
- },
639
- ]);
640
- mockLogger.clear();
641
- // Trigger a new cluster creation and make sure FirstCluster isn't emitted
642
- compressor.generateCompressedId();
643
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
644
- mockLogger.assertMatchAny([
645
- {
646
- eventName: 'SharedTreeIdCompressor:NewCluster',
647
- },
648
- ]);
649
- mockLogger.assertMatchNone([
650
- {
651
- eventName: 'SharedTreeIdCompressor:FirstCluster',
652
- },
653
- ]);
654
- });
655
- it('correctly logs telemetry events for eager final id allocations', () => {
656
- const mockLogger = new MockLogger();
657
- const compressor = createCompressor(Client.Client1, 5, undefined, mockLogger);
658
- const localId1 = compressor.generateCompressedId();
659
- expect(isLocalId(localId1)).to.be.true;
660
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
661
- mockLogger.assertMatchAny([
662
- {
663
- eventName: 'SharedTreeIdCompressor:IdCompressorStatus',
664
- eagerFinalIdCount: 0,
665
- localIdCount: 1,
666
- overridesCount: 0,
667
- sessionId: '88888888-8888-4888-b088-888888888888',
668
- },
669
- ]);
670
- mockLogger.clear();
671
- const finalId1 = compressor.generateCompressedId();
672
- const finalId2 = compressor.generateCompressedId();
673
- expect(isFinalId(finalId1)).to.be.true;
674
- expect(isFinalId(finalId2)).to.be.true;
675
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
676
- mockLogger.assertMatchAny([
677
- {
678
- eventName: 'SharedTreeIdCompressor:IdCompressorStatus',
679
- eagerFinalIdCount: 2,
680
- localIdCount: 0,
681
- overridesCount: 0,
682
- sessionId: '88888888-8888-4888-b088-888888888888',
683
- },
684
- ]);
685
- });
686
- it('correctly logs telemetry events for expansion case', () => {
687
- const mockLogger = new MockLogger();
688
- const compressor = createCompressor(Client.Client1, 5, undefined, mockLogger);
689
- const localId1 = compressor.generateCompressedId();
690
- expect(isLocalId(localId1)).to.be.true;
691
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
692
- mockLogger.assertMatchAny([
693
- {
694
- eventName: 'SharedTreeIdCompressor:IdCompressorStatus',
695
- eagerFinalIdCount: 0,
696
- localIdCount: 1,
697
- overridesCount: 0,
698
- sessionId: '88888888-8888-4888-b088-888888888888',
699
- },
700
- ]);
701
- mockLogger.clear();
702
- for (let i = 0; i < 4; i++) {
703
- const id = compressor.generateCompressedId();
704
- expect(isFinalId(id)).to.be.true;
705
- }
706
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
707
- mockLogger.assertMatchAny([
708
- {
709
- eventName: 'SharedTreeIdCompressor:IdCompressorStatus',
710
- eagerFinalIdCount: 4,
711
- localIdCount: 0,
712
- overridesCount: 0,
713
- sessionId: '88888888-8888-4888-b088-888888888888',
714
- },
715
- ]);
716
- mockLogger.clear();
717
- const expansionId1 = compressor.generateCompressedId();
718
- const expansionId2 = compressor.generateCompressedId();
719
- expect(isLocalId(expansionId1)).to.be.true;
720
- expect(isLocalId(expansionId2)).to.be.true;
721
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
722
- mockLogger.assertMatch([
723
- {
724
- eventName: 'SharedTreeIdCompressor:ClusterExpansion',
725
- sessionId: '88888888-8888-4888-b088-888888888888',
726
- previousCapacity: 5,
727
- newCapacity: 12,
728
- overflow: 2,
729
- },
730
- {
731
- eventName: 'SharedTreeIdCompressor:IdCompressorStatus',
732
- eagerFinalIdCount: 2,
733
- localIdCount: 0,
734
- overridesCount: 0,
735
- sessionId: '88888888-8888-4888-b088-888888888888',
736
- },
737
- ]);
738
- });
739
- it('emits correct telemetry status with overrides', () => {
740
- const mockLogger = new MockLogger();
741
- const compressor = createCompressor(Client.Client1, 5, undefined, mockLogger);
742
- const localId1 = compressor.generateCompressedId();
743
- expect(isLocalId(localId1)).to.be.true;
744
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
745
- mockLogger.assertMatchAny([
746
- {
747
- eventName: 'SharedTreeIdCompressor:IdCompressorStatus',
748
- eagerFinalIdCount: 0,
749
- localIdCount: 1,
750
- overridesCount: 0,
751
- sessionId: '88888888-8888-4888-b088-888888888888',
752
- },
753
- ]);
754
- mockLogger.clear();
755
- const finalId2 = compressor.generateCompressedId();
756
- const overrideId3 = compressor.generateCompressedId('override');
757
- expect(isFinalId(finalId2)).to.be.true;
758
- expect(isLocalId(overrideId3)).to.be.true;
759
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
760
- mockLogger.assertMatchAny([
761
- {
762
- eventName: 'SharedTreeIdCompressor:IdCompressorStatus',
763
- eagerFinalIdCount: 1,
764
- localIdCount: 1,
765
- overridesCount: 1,
766
- sessionId: '88888888-8888-4888-b088-888888888888',
767
- },
768
- ]);
769
- });
770
- it('emits telemetry when serialized', () => {
771
- const mockLogger = new MockLogger();
772
- const compressor = createCompressor(Client.Client1, 5, undefined, mockLogger);
773
- const localId1 = compressor.generateCompressedId();
774
- expect(isLocalId(localId1)).to.be.true;
775
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
776
- compressor.serialize(false);
777
- mockLogger.assertMatchAny([
778
- {
779
- eventName: 'SharedTreeIdCompressor:SerializedIdCompressorSize',
780
- size: 137,
781
- clusterCount: 1,
782
- sessionCount: 1,
783
- },
784
- ]);
785
- });
786
- });
787
- describe('Eager final ID allocation', () => {
788
- it('eagerly allocates final IDs when cluster creation has been finalized', () => {
789
- const compressor = createCompressor(Client.Client1, 5);
790
- const localId1 = compressor.generateCompressedId();
791
- expect(isLocalId(localId1)).to.be.true;
792
- const localId2 = compressor.generateCompressedId();
793
- expect(isLocalId(localId2)).to.be.true;
794
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
795
- const finalId3 = compressor.generateCompressedId();
796
- expect(isFinalId(finalId3)).to.be.true;
797
- const finalId4 = compressor.generateCompressedId();
798
- expect(isFinalId(finalId4)).to.be.true;
799
- const finalId5 = compressor.generateCompressedId();
800
- expect(isFinalId(finalId5)).to.be.true;
801
- const localId6 = compressor.generateCompressedId();
802
- expect(isLocalId(localId6)).to.be.true;
803
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
804
- const opSpaceId1 = compressor.normalizeToOpSpace(localId1);
805
- const opSpaceId2 = compressor.normalizeToOpSpace(localId2);
806
- const opSpaceId3 = compressor.normalizeToOpSpace(finalId3);
807
- const opSpaceId4 = compressor.normalizeToOpSpace(finalId4);
808
- const opSpaceId5 = compressor.normalizeToOpSpace(finalId5);
809
- const opSpaceId6 = compressor.normalizeToOpSpace(localId6);
810
- expectAssert(isFinalId(opSpaceId1));
811
- expectAssert(isFinalId(opSpaceId2));
812
- expectAssert(isFinalId(opSpaceId3) && opSpaceId3 === finalId3);
813
- expectAssert(isFinalId(opSpaceId4) && opSpaceId4 === finalId4);
814
- expectAssert(isFinalId(opSpaceId5) && opSpaceId5 === finalId5);
815
- expectAssert(isFinalId(opSpaceId6));
816
- expect(compressor.normalizeToSessionSpace(opSpaceId1)).to.equal(localId1);
817
- expect(compressor.normalizeToSessionSpace(opSpaceId2)).to.equal(localId2);
818
- expect(compressor.normalizeToSessionSpace(opSpaceId3)).to.equal(finalId3);
819
- expect(compressor.normalizeToSessionSpace(opSpaceId4)).to.equal(finalId4);
820
- expect(compressor.normalizeToSessionSpace(opSpaceId5)).to.equal(finalId5);
821
- expect(compressor.normalizeToSessionSpace(opSpaceId6)).to.equal(localId6);
822
- });
823
- it('does not eagerly allocate final IDs for IDs with overrides', () => {
824
- const compressor = createCompressor(Client.Client1, 5);
825
- const localId1 = compressor.generateCompressedId();
826
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
827
- const override1 = compressor.generateCompressedId('override1');
828
- expect(isLocalId(override1)).to.be.true;
829
- const finalId1 = compressor.generateCompressedId();
830
- expect(isFinalId(finalId1)).to.be.true;
831
- generateCompressedIds(compressor, 5);
832
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
833
- const override2 = compressor.generateCompressedId('override2');
834
- expect(isLocalId(override2)).to.be.true;
835
- const finalId2 = compressor.generateCompressedId();
836
- expect(isFinalId(finalId2)).to.be.true;
837
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
838
- const opSpaceId1 = compressor.normalizeToOpSpace(localId1);
839
- const opSpaceId2 = compressor.normalizeToOpSpace(override1);
840
- const opSpaceId3 = compressor.normalizeToOpSpace(finalId1);
841
- const opSpaceId4 = compressor.normalizeToOpSpace(override2);
842
- const opSpaceId5 = compressor.normalizeToOpSpace(finalId2);
843
- expectAssert(isFinalId(opSpaceId1));
844
- expectAssert(isFinalId(opSpaceId2));
845
- expectAssert(isFinalId(opSpaceId3) && opSpaceId3 === finalId1);
846
- expectAssert(isFinalId(opSpaceId4));
847
- expectAssert(isFinalId(opSpaceId5) && opSpaceId5 === finalId2);
848
- expect(compressor.normalizeToSessionSpace(opSpaceId1)).to.equal(localId1);
849
- expect(compressor.normalizeToSessionSpace(opSpaceId2)).to.equal(override1);
850
- expect(compressor.normalizeToSessionSpace(opSpaceId3)).to.equal(finalId1);
851
- expect(compressor.normalizeToSessionSpace(opSpaceId4)).to.equal(override2);
852
- expect(compressor.normalizeToSessionSpace(opSpaceId5)).to.equal(finalId2);
853
- });
854
- it('correctly normalizes eagerly allocated final IDs', () => {
855
- const compressor = createCompressor(Client.Client1, 5);
856
- const localId1 = compressor.generateCompressedId();
857
- const range1 = compressor.takeNextCreationRange();
858
- const localId2 = compressor.generateCompressedId();
859
- const range2 = compressor.takeNextCreationRange();
860
- expect(isLocalId(localId1)).to.be.true;
861
- expect(isLocalId(localId2)).to.be.true;
862
- compressor.finalizeCreationRange(range1);
863
- compressor.finalizeCreationRange(range2);
864
- const opSpaceId1 = compressor.normalizeToOpSpace(localId1);
865
- const opSpaceId2 = compressor.normalizeToOpSpace(localId2);
866
- expectAssert(isFinalId(opSpaceId1));
867
- expectAssert(isFinalId(opSpaceId2));
868
- expect(compressor.normalizeToSessionSpace(opSpaceId1)).to.equal(localId1);
869
- expect(compressor.normalizeToSessionSpace(opSpaceId2)).to.equal(localId2);
870
- });
871
- it('generates correct eager finals when there are outstanding locals after cluster expansion', () => {
872
- const compressor = createCompressor(Client.Client1, 2);
873
- // Before cluster expansion
874
- expect(isLocalId(compressor.generateCompressedId())).to.be.true;
875
- const rangeA = compressor.takeNextCreationRange();
876
- compressor.finalizeCreationRange(rangeA);
877
- expect(isFinalId(compressor.generateCompressedId())).to.be.true;
878
- // After cluster expansion
879
- expect(isLocalId(compressor.generateCompressedId())).to.be.true;
880
- const rangeB = compressor.takeNextCreationRange();
881
- const localId = compressor.generateCompressedId();
882
- expect(isLocalId(localId)).to.be.true;
883
- // Take a range that won't be finalized in this test; the finalizing of range B should associate this range with finals
884
- const rangeC = compressor.takeNextCreationRange();
885
- compressor.finalizeCreationRange(rangeB);
886
- const eagerId = compressor.generateCompressedId();
887
- expect(isFinalId(eagerId)).to.be.true;
888
- expect(compressor.recompress(compressor.decompress(localId))).to.equal(localId);
889
- expect(compressor.recompress(compressor.decompress(eagerId))).to.equal(eagerId);
890
- compressor.finalizeCreationRange(rangeC);
891
- expect(compressor.recompress(compressor.decompress(localId))).to.equal(localId);
892
- expect(compressor.recompress(compressor.decompress(eagerId))).to.equal(eagerId);
893
- });
894
- it('generates unique eager finals when multiple outstanding creation ranges during finalizing', () => {
895
- const compressor = createCompressor(Client.Client1, 10 /* must be 10 for the test to make sense */);
896
- // Make a first outstanding range
897
- const id1_1 = compressor.generateCompressedId();
898
- const id1_2 = compressor.generateCompressedId();
899
- expect(isLocalId(id1_1)).to.be.true;
900
- expect(isLocalId(id1_2)).to.be.true;
901
- const range1 = compressor.takeNextCreationRange();
902
- // Make a second outstanding range
903
- const id2_1 = compressor.generateCompressedId();
904
- const id2_2 = compressor.generateCompressedId();
905
- expect(isLocalId(id2_1)).to.be.true;
906
- expect(isLocalId(id2_2)).to.be.true;
907
- const range2 = compressor.takeNextCreationRange();
908
- // Finalize just the first one, which should create finals that align with both outstanding ranges
909
- compressor.finalizeCreationRange(range1);
910
- // Make a third range. This one should be composed of eager finals that align after the two ranges above.
911
- const id3_1 = compressor.generateCompressedId();
912
- const id3_2 = compressor.generateCompressedId();
913
- expect(isFinalId(id3_1)).to.be.true;
914
- expect(isFinalId(id3_2)).to.be.true;
915
- const range3 = compressor.takeNextCreationRange();
916
- // Finalize both initial ranges.
917
- compressor.finalizeCreationRange(range2);
918
- compressor.finalizeCreationRange(range3);
919
- // Make some more eager finals that should be aligned correctly.
920
- const id4_1 = compressor.generateCompressedId();
921
- const id4_2 = compressor.generateCompressedId();
922
- expect(isFinalId(id4_1)).to.be.true;
923
- expect(isFinalId(id4_2)).to.be.true;
924
- // Assert everything is unique and consistent.
925
- const ids = new Set();
926
- const uuids = new Set();
927
- [id1_1, id1_2, id2_1, id2_2, id3_1, id3_2, id4_1, id4_2].forEach((id) => {
928
- ids.add(id);
929
- uuids.add(compressor.decompress(id));
930
- });
931
- expect(ids.size).to.equal(8);
932
- expect(uuids.size).to.equal(8);
933
- });
934
- it('generates unique eager finals when there are still outstanding locals after a cluster is expanded', () => {
935
- // const compressor = createCompressor(Client.Client1, 4 /* must be 4 for the test to make sense */);
936
- const compressor = new IdCompressor(sessionIds.get(Client.Client1), 0);
937
- compressor.clusterCapacity = 4;
938
- // Make locals to fill half the future cluster
939
- const id1_1 = compressor.generateCompressedId();
940
- const id1_2 = compressor.generateCompressedId();
941
- expect(isLocalId(id1_1)).to.be.true;
942
- expect(isLocalId(id1_2)).to.be.true;
943
- const range1 = compressor.takeNextCreationRange();
944
- // Make locals to overflow the future cluster
945
- const id2_1 = compressor.generateCompressedId();
946
- const id2_2 = compressor.generateCompressedId();
947
- const id2_3 = compressor.generateCompressedId();
948
- expect(isLocalId(id2_1)).to.be.true;
949
- expect(isLocalId(id2_2)).to.be.true;
950
- expect(isLocalId(id2_3)).to.be.true;
951
- const range2 = compressor.takeNextCreationRange();
952
- // Finalize the first range. This should align the first four locals (i.e. all of range1, and 2/3 of range2)
953
- compressor.finalizeCreationRange(range1);
954
- // Make a single range that should still be overflowing the initial cluster (i.e. be local)
955
- const id3_1 = compressor.generateCompressedId();
956
- expect(isLocalId(id3_1)).to.be.true;
957
- const range3 = compressor.takeNextCreationRange();
958
- // First finalize should expand the cluster and align all outstanding ranges.
959
- compressor.finalizeCreationRange(range2);
960
- // All generated IDs should have aligned finals (even though range3 has not been finalized)
961
- const allIds = [id1_1, id1_2, id2_1, id2_2, id2_3, id3_1];
962
- allIds.forEach((id) => expect(isFinalId(compressor.normalizeToOpSpace(id))).to.be.true);
963
- compressor.finalizeCreationRange(range3);
964
- // Make one eager final
965
- const id4_1 = compressor.generateCompressedId();
966
- allIds.push(id4_1);
967
- expect(isFinalId(id4_1)).to.be.true;
968
- // Assert everything is unique and consistent.
969
- const ids = new Set();
970
- const uuids = new Set();
971
- allIds.forEach((id) => {
972
- ids.add(id);
973
- uuids.add(compressor.decompress(id));
974
- });
975
- expect(ids.size).to.equal(7);
976
- expect(uuids.size).to.equal(7);
977
- });
978
- });
979
- describe('Serialization', () => {
980
- it('can serialize an empty compressor', () => {
981
- const compressor = createCompressor(Client.Client1);
982
- const [serializedNoSession, serializedWithSession] = expectSerializes(compressor);
983
- expect(serializedWithSession.clusters.length).to.equal(0, 'reserved cluster should not be serialized');
984
- expect(serializedNoSession.clusters.length).to.equal(0, 'reserved cluster should not be serialized');
985
- });
986
- it('correctly deserializes and resumes a session', () => {
987
- const compressor1 = createCompressor(Client.Client1, undefined, attributionIds.get(Client.Client1));
988
- const compressor2 = createCompressor(Client.Client2, undefined, attributionIds.get(Client.Client2));
989
- compressor1.generateCompressedId();
990
- const creationRange = compressor1.takeNextCreationRange();
991
- compressor1.finalizeCreationRange(creationRange);
992
- compressor2.finalizeCreationRange(creationRange);
993
- const [_, serializedWithSession] = expectSerializes(compressor1);
994
- const compressorResumed = IdCompressor.deserialize(serializedWithSession);
995
- compressorResumed.generateCompressedId();
996
- const range2 = compressorResumed.takeNextCreationRange();
997
- compressor1.finalizeCreationRange(range2);
998
- compressor2.finalizeCreationRange(range2);
999
- expect(IdCompressor.deserialize(compressor1.serialize(false), createSessionId()).equals(IdCompressor.deserialize(compressor2.serialize(false), createSessionId()), false // don't compare local state
1000
- )).to.be.true;
1001
- });
1002
- it('can serialize local state with attribution but no IDs', () => {
1003
- // This is a regression test for the scenario in which an ID compressor sends its first range, which
1004
- // includes its attribution ID, but has made no IDs. An incorrect optimization when serializing had
1005
- // omitted local state if no IDs had been allocated, but then also dropped the `sentAttribution` flag
1006
- const compressor = createCompressor(Client.Client1, undefined, attributionIds.get(Client.Client1));
1007
- const range = compressor.takeNextCreationRange();
1008
- expect(range.ids).to.be.undefined;
1009
- expect(range.attributionId).to.equal(attributionIds.get(Client.Client1));
1010
- expectSerializes(compressor);
1011
- });
1012
- });
1013
- // No validation, as these leave the network in a broken state
1014
- describeNetworkNoValidation('detects UUID collision', (itNetwork) => {
1015
- itNetwork('when a client requests an override that is an UUID reserved for later allocation by a cluster', 2, (network) => {
1016
- network.allocateAndSendIds(Client.Client2, 1);
1017
- network.deliverOperations(Client.Client1);
1018
- const compressor2 = network.getCompressor(Client.Client2);
1019
- const id = network.getIdLog(Client.Client2)[0].id;
1020
- const uuid = assertIsStableId(compressor2.decompress(id));
1021
- const nextUuid = stableIdFromNumericUuid(numericUuidFromStableId(uuid), 1);
1022
- // TODO:#283: Re-assess test when full unification is implemented
1023
- network.allocateAndSendIds(Client.Client1, 1, { 0: nextUuid });
1024
- });
1025
- itNetwork('when a new cluster is allocated whose base UUID collides with an existing override', 2, (network) => {
1026
- network.allocateAndSendIds(Client.Client1, 1);
1027
- network.deliverOperations(DestinationClient.All);
1028
- const compressor1 = network.getCompressor(Client.Client1);
1029
- const id = network.getIdLog(Client.Client1)[0].id;
1030
- const uuid = assertIsStableId(compressor1.decompress(id));
1031
- const nextUuid = stableIdFromNumericUuid(numericUuidFromStableId(uuid), 2);
1032
- network.allocateAndSendIds(Client.Client1, 1, { 0: nextUuid });
1033
- network.allocateAndSendIds(Client.Client2, 1);
1034
- network.deliverOperations(DestinationClient.All);
1035
- network.allocateAndSendIds(Client.Client1, 1); // new cluster
1036
- assert.throws(() => network.deliverOperations(Client.Client1), (e) => validateAssertionError(e, `Override '${nextUuid}' collides with another allocated UUID.`));
1037
- });
1038
- itNetwork('detects colliding override UUIDs when expanding a cluster', 1, (network) => {
1039
- // This is a glass box test in that it is testing cluster expansion
1040
- network.allocateAndSendIds(Client.Client1, 1);
1041
- network.deliverOperations(DestinationClient.All);
1042
- const compressor1 = network.getCompressor(Client.Client1);
1043
- const id = network.getIdLog(Client.Client1)[0].id;
1044
- const uuid = assertIsStableId(compressor1.decompress(id));
1045
- const expansion = 3;
1046
- const nextUuid = stableIdFromNumericUuid(numericUuidFromStableId(uuid), expansion);
1047
- network.allocateAndSendIds(Client.Client1, expansion, { 0: nextUuid });
1048
- assert.throws(() => network.deliverOperations(DestinationClient.All), (e) => validateAssertionError(e, `Override '${nextUuid}' collides with another allocated UUID.`));
1049
- });
1050
- });
1051
- describeNetwork('Networked', (itNetwork) => {
1052
- describe('can attribute', () => {
1053
- itNetwork('local IDs before and after being finalized', (network) => {
1054
- const compressor = network.getCompressor(Client.Client1);
1055
- network.allocateAndSendIds(Client.Client1, 1);
1056
- const id = network.getIdLog(Client.Client1)[0].id;
1057
- expect(compressor.attributeId(id)).to.equal(attributionIds.get(Client.Client1));
1058
- network.deliverOperations(Client.Client1);
1059
- expect(compressor.attributeId(id)).to.equal(attributionIds.get(Client.Client1));
1060
- });
1061
- itNetwork('final IDs from a remote session', (network) => {
1062
- const compressor = network.getCompressor(Client.Client1);
1063
- network.allocateAndSendIds(Client.Client2, 1);
1064
- network.deliverOperations(DestinationClient.All);
1065
- const id = network.getSequencedIdLog(Client.Client1)[0].id;
1066
- expect(compressor.attributeId(id)).to.equal(attributionIds.get(Client.Client2));
1067
- });
1068
- itNetwork('final IDs from multiple remote sessions', 1, (network) => {
1069
- const compressor = network.getCompressor(Client.Client1);
1070
- // Ensure multiple clusters are made by each client. Cluster size === 1.
1071
- network.allocateAndSendIds(Client.Client1, compressor.clusterCapacity);
1072
- network.allocateAndSendIds(Client.Client2, compressor.clusterCapacity);
1073
- network.allocateAndSendIds(Client.Client3, compressor.clusterCapacity);
1074
- network.allocateAndSendIds(Client.Client1, compressor.clusterCapacity);
1075
- network.allocateAndSendIds(Client.Client2, compressor.clusterCapacity);
1076
- network.allocateAndSendIds(Client.Client3, compressor.clusterCapacity);
1077
- network.deliverOperations(DestinationClient.All);
1078
- const log = network.getSequencedIdLog(Client.Client1);
1079
- expect(compressor.attributeId(log[0].id)).to.equal(attributionIds.get(Client.Client1));
1080
- expect(compressor.attributeId(log[1].id)).to.equal(attributionIds.get(Client.Client2));
1081
- expect(compressor.attributeId(log[2].id)).to.equal(attributionIds.get(Client.Client3));
1082
- expect(compressor.attributeId(log[3].id)).to.equal(attributionIds.get(Client.Client1));
1083
- expect(compressor.attributeId(log[4].id)).to.equal(attributionIds.get(Client.Client2));
1084
- expect(compressor.attributeId(log[5].id)).to.equal(attributionIds.get(Client.Client3));
1085
- });
1086
- itNetwork('unified IDs', (network) => {
1087
- const override = 'override';
1088
- const allTargets = network.getTargetCompressors(DestinationClient.All);
1089
- for (const [client, compressor] of allTargets) {
1090
- network.allocateAndSendIds(client, 1, { 0: override });
1091
- for (const { id } of network.getIdLog(client)) {
1092
- expect(compressor.attributeId(id)).to.equal(attributionIds.get(client));
1093
- }
1094
- }
1095
- network.deliverOperations(DestinationClient.All);
1096
- const firstTarget = allTargets[0][0];
1097
- for (const [client, compressor] of allTargets) {
1098
- for (const { id } of network.getIdLog(client)) {
1099
- expect(compressor.attributeId(id)).to.equal(attributionIds.get(firstTarget));
1100
- }
1101
- }
1102
- });
1103
- itNetwork('eagerly generated final IDs that are not finalized', (network) => {
1104
- const compressor = network.getCompressor(Client.Client1);
1105
- network.allocateAndSendIds(Client.Client1, 1);
1106
- network.deliverOperations(DestinationClient.All);
1107
- const ids = network.allocateAndSendIds(Client.Client1, 1);
1108
- const id = compressor.normalizeToSessionSpace(ids[0], compressor.localSessionId);
1109
- expect(compressor.attributeId(id)).to.equal(attributionIds.get(Client.Client1));
1110
- });
1111
- });
1112
- itNetwork('upholds the invariant that IDs always decompress to the same UUID', 2, (network) => {
1113
- network.allocateAndSendIds(Client.Client1, 5, {
1114
- 1: 'override1',
1115
- });
1116
- network.allocateAndSendIds(Client.Client2, 5, {
1117
- 2: 'override2',
1118
- });
1119
- network.allocateAndSendIds(Client.Client3, 5, {
1120
- 3: 'override3',
1121
- });
1122
- const preAckLocals = new Map();
1123
- for (const [client, compressor] of network.getTargetCompressors(MetaClient.All)) {
1124
- const locals = [];
1125
- for (const idData of network.getIdLog(client)) {
1126
- locals.push([idData.id, compressor.decompress(idData.id)]);
1127
- }
1128
- preAckLocals.set(client, locals);
1129
- }
1130
- // Ack all IDs
1131
- network.deliverOperations(DestinationClient.All);
1132
- for (const [client, compressor] of network.getTargetCompressors(MetaClient.All)) {
1133
- const preAckLocalIds = preAckLocals.get(client) ?? fail();
1134
- let i = 0;
1135
- for (const idData of network.getIdLog(client)) {
1136
- if (idData.originatingClient === client) {
1137
- expect(isFinalId(idData.id)).to.be.false;
1138
- const currentUuid = compressor.decompress(idData.id);
1139
- expect(currentUuid).to.equal(preAckLocalIds[i % preAckLocalIds.length][1]);
1140
- i++;
1141
- }
1142
- }
1143
- }
1144
- });
1145
- itNetwork('can normalize session space IDs to op space', 5, (network) => {
1146
- const clusterCapacity = 5;
1147
- const idCount = clusterCapacity * 2;
1148
- for (let i = 0; i < idCount; i++) {
1149
- network.allocateAndSendIds(Client.Client1, 1);
1150
- network.allocateAndSendIds(Client.Client2, 1);
1151
- network.allocateAndSendIds(Client.Client3, 1);
1152
- }
1153
- for (const [client, compressor] of network.getTargetCompressors(MetaClient.All)) {
1154
- for (const idData of network.getIdLog(client)) {
1155
- expect(idData.originatingClient).to.equal(client);
1156
- expect(isLocalId(compressor.normalizeToOpSpace(idData.id))).to.be.true;
1157
- }
1158
- }
1159
- network.deliverOperations(DestinationClient.All);
1160
- for (const [client, compressor] of network.getTargetCompressors(MetaClient.All)) {
1161
- for (const idData of network.getIdLog(client)) {
1162
- expect(isFinalId(compressor.normalizeToOpSpace(idData.id))).to.be.true;
1163
- }
1164
- }
1165
- });
1166
- itNetwork('can normalize local op space IDs from a local session to session space IDs', (network) => {
1167
- const compressor = network.getCompressor(Client.Client1);
1168
- network.allocateAndSendIds(Client.Client1, 1);
1169
- network.deliverOperations(Client.Client1);
1170
- const sessionSpaceIds = network.getIdLog(Client.Client1);
1171
- const opSpaceId = compressor.normalizeToOpSpace(sessionSpaceIds[0].id);
1172
- const sessionSpaceId = compressor.normalizeToSessionSpace(opSpaceId, compressor.localSessionId);
1173
- expect(isFinalId(opSpaceId)).to.be.true;
1174
- expect(isLocalId(sessionSpaceId)).to.be.true;
1175
- });
1176
- itNetwork('can normalize local op space IDs from a remote session to session space IDs', (network) => {
1177
- const compressor1 = network.getCompressor(Client.Client1);
1178
- const compressor2 = network.getCompressor(Client.Client2);
1179
- const opSpaceIds = network.allocateAndSendIds(Client.Client1, 1);
1180
- // Mimic sending a reference to an ID that hasn't been acked yet, such as in a slow network
1181
- const id = opSpaceIds[0];
1182
- const getSessionNormalizedId = () => compressor2.normalizeToSessionSpace(id, compressor1.localSessionId);
1183
- assert.throws(getSessionNormalizedId, (e) => validateAssertionError(e, 'No IDs have ever been finalized by the supplied session.'));
1184
- network.deliverOperations(Client.Client2);
1185
- expect(isFinalId(getSessionNormalizedId())).to.be.true;
1186
- });
1187
- itNetwork('unifies duplicate overrides', 3, (network) => {
1188
- const override = 'override';
1189
- const compressor1 = network.getCompressor(Client.Client1);
1190
- const compressor2 = network.getCompressor(Client.Client2);
1191
- const compressor3 = network.getCompressor(Client.Client3);
1192
- const clusterCapacity = compressor1.clusterCapacity;
1193
- // Ensure some clusters exist to avoid simple case of empty clusters
1194
- network.allocateAndSendIds(Client.Client1, clusterCapacity);
1195
- network.allocateAndSendIds(Client.Client2, clusterCapacity);
1196
- network.allocateAndSendIds(Client.Client3, clusterCapacity);
1197
- network.deliverOperations(DestinationClient.All);
1198
- const range1 = network.allocateAndSendIds(Client.Client1, 1, { 0: override });
1199
- const overrides1 = expectDefined(getIds(range1)?.overrides);
1200
- const id1 = compressor1.normalizeToSessionSpace(overrides1[0][0], compressor1.localSessionId);
1201
- const opNormalizedLocal1 = compressor1.normalizeToOpSpace(id1);
1202
- expect(isLocalId(opNormalizedLocal1)).to.be.true;
1203
- expect(isFinalId(id1)).to.be.false;
1204
- network.deliverOperations(DestinationClient.Client1);
1205
- const finalId1 = compressor1.normalizeToOpSpace(id1);
1206
- expect(isFinalId(finalId1)).to.be.true;
1207
- const range2 = network.allocateAndSendIds(Client.Client2, 2, { 1: override });
1208
- const overrides2 = expectDefined(getIds(range2)?.overrides);
1209
- const id2 = compressor2.normalizeToSessionSpace(overrides2[0][0], compressor2.localSessionId);
1210
- const opNormalizedLocal2 = compressor2.normalizeToOpSpace(id2);
1211
- expect(isLocalId(opNormalizedLocal2)).to.be.true;
1212
- expect(isFinalId(id2)).to.be.false;
1213
- network.allocateAndSendIds(Client.Client3, 1);
1214
- network.deliverOperations(DestinationClient.All);
1215
- const finalId2 = compressor2.normalizeToOpSpace(id2);
1216
- expect(isFinalId(finalId2)).to.be.true;
1217
- expect(finalId1).to.equal(finalId2);
1218
- expect(compressor1.normalizeToOpSpace(id1)).to.equal(finalId1);
1219
- expect(compressor1.normalizeToSessionSpace(finalId1, compressor1.localSessionId)).to.equal(id1);
1220
- expect(compressor1.normalizeToSessionSpace(opNormalizedLocal2, compressor2.localSessionId)).to.equal(id1);
1221
- expect(compressor1.decompress(id1)).to.equal(override);
1222
- expect(compressor1.decompress(finalId1)).to.equal(override);
1223
- expect(compressor1.recompress(override)).to.equal(id1);
1224
- expect(compressor2.normalizeToOpSpace(id2)).to.equal(finalId2);
1225
- expect(compressor2.normalizeToSessionSpace(finalId1, compressor1.localSessionId)).to.equal(id2);
1226
- expect(compressor2.normalizeToSessionSpace(opNormalizedLocal1, compressor1.localSessionId)).to.equal(id2);
1227
- expect(compressor2.decompress(id2)).to.equal(override);
1228
- expect(compressor2.decompress(finalId2)).to.equal(override);
1229
- expect(compressor2.tryRecompress(override)).to.equal(id2);
1230
- expect(compressor3.normalizeToSessionSpace(finalId1, compressor1.localSessionId)).to.equal(finalId1);
1231
- expect(compressor3.normalizeToSessionSpace(opNormalizedLocal1, compressor1.localSessionId)).to.equal(finalId1);
1232
- expect(compressor3.normalizeToSessionSpace(opNormalizedLocal2, compressor2.localSessionId)).to.equal(finalId1);
1233
- expect(compressor3.decompress(finalId1)).to.equal(override);
1234
- expect(compressor3.recompress(override)).to.equal(finalId1);
1235
- });
1236
- itNetwork('maintains alignment after unifying duplicate overrides', 3, (network) => {
1237
- const override = 'override';
1238
- network.allocateAndSendIds(Client.Client1, 1, { 0: override });
1239
- network.allocateAndSendIds(Client.Client2, 2, { 1: override });
1240
- network.allocateAndSendIds(Client.Client1, 5);
1241
- network.allocateAndSendIds(Client.Client2, 5);
1242
- expectSequencedLogsAlign(network, Client.Client1, Client.Client2, 1);
1243
- });
1244
- function expectSequencedLogsAlign(network, client1, client2, numUnifications = 0) {
1245
- network.deliverOperations(DestinationClient.All);
1246
- assert(client1 !== client2);
1247
- const log1 = network.getSequencedIdLog(client1);
1248
- const log2 = network.getSequencedIdLog(client2);
1249
- expect(log1.length).to.equal(log2.length);
1250
- const compressor1 = network.getCompressor(client1);
1251
- const compressor2 = network.getCompressor(client2);
1252
- const ids = new Set();
1253
- const uuidsOrOverrides = new Set();
1254
- for (let i = 0; i < log1.length; i++) {
1255
- const data1 = log1[i];
1256
- const id1 = compressor1.normalizeToOpSpace(data1.id);
1257
- const id2 = compressor2.normalizeToOpSpace(log2[i].id);
1258
- expect(isFinalId(id1)).to.be.true;
1259
- ids.add(id1);
1260
- expect(id1).to.equal(id2);
1261
- const uuidOrOverride1 = compressor1.decompress(id1);
1262
- uuidsOrOverrides.add(uuidOrOverride1);
1263
- if (data1.expectedOverride === undefined) {
1264
- expect(isStableId(uuidOrOverride1)).to.be.true;
1265
- }
1266
- expect(uuidOrOverride1).to.equal(compressor2.decompress(id2));
1267
- }
1268
- const expectedSize = log1.length - numUnifications;
1269
- expect(ids.size).to.equal(expectedSize);
1270
- expect(uuidsOrOverrides.size).to.equal(expectedSize);
1271
- }
1272
- itNetwork('produces ID spaces correctly', (network) => {
1273
- // This test asserts that IDs returned from IDCompressor APIs are correctly encoded as either local or final.
1274
- // This is a glass box test in that it assumes the negative/positive encoding of CompressedIds (negative = local, positive = final).
1275
- const compressor1 = network.getCompressor(Client.Client1);
1276
- // Client 1 makes two IDs, two explicit (one with an override) and one sequential
1277
- network.allocateAndSendIds(Client.Client1, 3, {
1278
- 1: 'override1',
1279
- });
1280
- network.getIdLog(Client.Client1).forEach((id) => expect(id.id).to.be.lessThan(0));
1281
- // Client 1's IDs have not been acked so have no op space equivalent
1282
- network
1283
- .getIdLog(Client.Client1)
1284
- .forEach((idData) => expect(compressor1.normalizeToOpSpace(idData.id)).to.be.lessThan(0));
1285
- // Client 1's IDs are acked
1286
- network.deliverOperations(Client.Client1);
1287
- network.getIdLog(Client.Client1).forEach((id) => expect(id.id).to.be.lessThan(0));
1288
- // Client 3 makes two IDs, two explicit (one with an override) and one sequential
1289
- network.allocateAndSendIds(Client.Client2, 3, {
1290
- 1: 'override2',
1291
- });
1292
- network.getIdLog(Client.Client2).forEach((id) => expect(id.id).to.be.lessThan(0));
1293
- // Client 1 receives Client 2's IDs
1294
- network.deliverOperations(Client.Client1);
1295
- network
1296
- .getIdLog(Client.Client1)
1297
- .slice(-3)
1298
- .forEach((id) => expect(id.id).to.be.greaterThan(0));
1299
- // All IDs have been acked or are from another client, and therefore have a final form in op space
1300
- network
1301
- .getIdLog(Client.Client1)
1302
- .forEach((idData) => expect(compressor1.normalizeToOpSpace(idData.id)).to.be.greaterThan(0));
1303
- // Compression should preserve ID space correctness
1304
- network.getIdLog(Client.Client1).forEach((idData) => {
1305
- const roundtripped = compressor1.recompress(compressor1.decompress(idData.id));
1306
- expect(Math.sign(roundtripped)).to.equal(Math.sign(idData.id));
1307
- });
1308
- network.getIdLog(Client.Client1).forEach((idData) => {
1309
- const opNormalized = compressor1.normalizeToOpSpace(idData.id);
1310
- expect(Math.sign(compressor1.normalizeToSessionSpace(opNormalized, idData.sessionId))).to.equal(Math.sign(idData.id));
1311
- });
1312
- });
1313
- itNetwork('produces consistent IDs with large fuzz input', (network) => {
1314
- const generator = take(2000, makeOpGenerator({ includeOverrides: true }));
1315
- performFuzzActions(generator, network, 1984, undefined, true, (network) => network.assertNetworkState());
1316
- network.deliverOperations(DestinationClient.All);
1317
- });
1318
- itNetwork('can set the cluster size via constructor', 2, (network) => {
1319
- const compressor = network.getCompressor(Client.Client1);
1320
- const compressor2 = network.getCompressor(Client.Client2);
1321
- network.allocateAndSendIds(Client.Client1, 1);
1322
- const opSpaceIds = network.allocateAndSendIds(Client.Client2, 2);
1323
- network.deliverOperations(DestinationClient.All);
1324
- // Glass box test, as it knows the order of final IDs
1325
- expect(compressor.normalizeToSessionSpace(opSpaceIds[0], compressor2.localSessionId)).to.equal(compressor.reservedIdCount + compressor.clusterCapacity);
1326
- });
1327
- itNetwork('can set the cluster size via API', 2, (network) => {
1328
- const compressor = network.getCompressor(Client.Client1);
1329
- const compressor2 = network.getCompressor(Client.Client2);
1330
- const initialClusterCapacity = compressor.clusterCapacity;
1331
- network.allocateAndSendIds(Client.Client1, initialClusterCapacity);
1332
- network.allocateAndSendIds(Client.Client2, initialClusterCapacity);
1333
- network.enqueueCapacityChange(5);
1334
- network.allocateAndSendIds(Client.Client1, 1);
1335
- const opSpaceIds = network.allocateAndSendIds(Client.Client2, 1);
1336
- network.deliverOperations(DestinationClient.All);
1337
- // Glass box test, as it knows the order of final IDs
1338
- expect(compressor.normalizeToSessionSpace(opSpaceIds[0], compressor2.localSessionId)).to.equal(compressor.reservedIdCount + initialClusterCapacity * 2 + compressor.clusterCapacity);
1339
- });
1340
- itNetwork('does not decompress ids for empty parts of clusters', 2, (network) => {
1341
- // This is a glass box test in that it creates a final ID outside of the ID compressor
1342
- network.allocateAndSendIds(Client.Client1, 1);
1343
- network.deliverOperations(DestinationClient.All);
1344
- const id = network.getSequencedIdLog(Client.Client2)[0].id;
1345
- expect(isFinalId(id)).to.be.true;
1346
- // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
1347
- const emptyId = (id + 1);
1348
- assert.throws(() => network.getCompressor(Client.Client2).decompress(emptyId), (e) => validateAssertionError(e, 'Compressed ID was not generated by this compressor'));
1349
- });
1350
- describe('Finalizing', () => {
1351
- itNetwork('can finalize IDs from multiple clients', (network) => {
1352
- network.allocateAndSendIds(Client.Client1, 3, {
1353
- 1: 'override1',
1354
- });
1355
- network.allocateAndSendIds(Client.Client2, 3, {
1356
- 1: 'override2',
1357
- });
1358
- expectSequencedLogsAlign(network, Client.Client1, Client.Client2);
1359
- });
1360
- itNetwork('can finalize a range when the current cluster is full', 5, (network) => {
1361
- const clusterCapacity = network.getCompressor(Client.Client1).clusterCapacity;
1362
- network.allocateAndSendIds(Client.Client1, clusterCapacity);
1363
- network.allocateAndSendIds(Client.Client2, clusterCapacity);
1364
- network.allocateAndSendIds(Client.Client1, clusterCapacity, {
1365
- 0: 'override1',
1366
- 1: 'override2',
1367
- 2: 'override3',
1368
- });
1369
- expectSequencedLogsAlign(network, Client.Client1, Client.Client2);
1370
- });
1371
- itNetwork('can finalize a range that spans multiple clusters', 5, (network) => {
1372
- const clusterCapacity = network.getCompressor(Client.Client1).clusterCapacity;
1373
- network.allocateAndSendIds(Client.Client1, clusterCapacity - 2, {
1374
- 0: 'override1',
1375
- 1: 'override2',
1376
- });
1377
- network.allocateAndSendIds(Client.Client2, 1);
1378
- network.allocateAndSendIds(Client.Client1, clusterCapacity, {
1379
- 0: 'override3',
1380
- 1: 'override4',
1381
- 2: 'override5',
1382
- });
1383
- expectSequencedLogsAlign(network, Client.Client1, Client.Client2);
1384
- });
1385
- });
1386
- describe('Serialization', () => {
1387
- itNetwork('prevents attempts to resume a session from a serialized compressor with no session', (network) => {
1388
- const compressor = network.getCompressor(Client.Client1);
1389
- network.allocateAndSendIds(Client.Client2, 1);
1390
- network.allocateAndSendIds(Client.Client3, 1);
1391
- network.deliverOperations(Client.Client1);
1392
- const serializedWithoutLocalState = compressor.serialize(false);
1393
- assert.throws(() => IdCompressor.deserialize(serializedWithoutLocalState, sessionIds.get(Client.Client2)), (e) => validateAssertionError(e, 'Cannot resume existing session.'));
1394
- });
1395
- itNetwork('round-trips local state', 3, (network) => {
1396
- network.allocateAndSendIds(Client.Client1, 2);
1397
- network.allocateAndSendIds(Client.Client2, 3);
1398
- network.allocateAndSendIds(Client.Client1, 5);
1399
- network.allocateAndSendIds(Client.Client1, 5);
1400
- network.allocateAndSendIds(Client.Client3, 3);
1401
- network.allocateAndSendIds(Client.Client2, 3);
1402
- network.deliverOperations(Client.Client1);
1403
- // Some un-acked locals at the end
1404
- network.allocateAndSendIds(Client.Client1, 4);
1405
- const [serializedNoSession, serializedWithSession] = expectSerializes(network.getCompressor(Client.Client1));
1406
- expect(hasOngoingSession(serializedWithSession)).to.be.true;
1407
- expect(hasOngoingSession(serializedNoSession)).to.be.false;
1408
- });
1409
- itNetwork('can serialize a partially empty cluster', 5, (network) => {
1410
- network.allocateAndSendIds(Client.Client1, 2);
1411
- network.deliverOperations(DestinationClient.All);
1412
- expectSerializes(network.getCompressor(Client.Client1));
1413
- expectSerializes(network.getCompressor(Client.Client3));
1414
- });
1415
- itNetwork('can serialize a full cluster', 2, (network) => {
1416
- network.allocateAndSendIds(Client.Client1, 2);
1417
- network.deliverOperations(DestinationClient.All);
1418
- expectSerializes(network.getCompressor(Client.Client1));
1419
- expectSerializes(network.getCompressor(Client.Client3));
1420
- });
1421
- itNetwork('can serialize full clusters from different clients', 2, (network) => {
1422
- network.allocateAndSendIds(Client.Client1, 2);
1423
- network.allocateAndSendIds(Client.Client2, 2);
1424
- network.deliverOperations(DestinationClient.All);
1425
- expectSerializes(network.getCompressor(Client.Client1));
1426
- expectSerializes(network.getCompressor(Client.Client3));
1427
- });
1428
- itNetwork('can serialize clusters of different sizes and clients', 3, (network) => {
1429
- network.allocateAndSendIds(Client.Client1, 2);
1430
- network.allocateAndSendIds(Client.Client2, 3);
1431
- network.allocateAndSendIds(Client.Client1, 5);
1432
- network.allocateAndSendIds(Client.Client1, 5);
1433
- network.allocateAndSendIds(Client.Client2, 3);
1434
- network.deliverOperations(DestinationClient.All);
1435
- expectSerializes(network.getCompressor(Client.Client1));
1436
- expectSerializes(network.getCompressor(Client.Client3));
1437
- });
1438
- itNetwork('can serialize clusters with overrides', 3, (network) => {
1439
- network.allocateAndSendIds(Client.Client1, 2, {
1440
- 1: 'override',
1441
- });
1442
- network.allocateAndSendIds(Client.Client2, 3, {
1443
- 0: 'override1',
1444
- 2: 'override2',
1445
- });
1446
- network.deliverOperations(DestinationClient.All);
1447
- expectSerializes(network.getCompressor(Client.Client1));
1448
- expectSerializes(network.getCompressor(Client.Client3));
1449
- });
1450
- itNetwork('packs IDs into a single cluster when a single client generates non-overridden ids', 3, (network) => {
1451
- network.allocateAndSendIds(Client.Client1, 20);
1452
- network.deliverOperations(DestinationClient.All);
1453
- const [serialized1WithNoSession, serialized1WithSession] = expectSerializes(network.getCompressor(Client.Client1));
1454
- expect(serialized1WithNoSession.clusters.length).to.equal(1);
1455
- expect(serialized1WithSession.clusters.length).to.equal(1);
1456
- const [serialized3WithNoSession, serialized3WithSession] = expectSerializes(network.getCompressor(Client.Client3));
1457
- expect(serialized3WithNoSession.clusters.length).to.equal(1);
1458
- expect(serialized3WithSession.clusters.length).to.equal(1);
1459
- });
1460
- itNetwork('serializes correctly after unifying duplicate overrides', 3, (network) => {
1461
- const override = 'override';
1462
- network.allocateAndSendIds(Client.Client1, 1, { 0: override });
1463
- network.allocateAndSendIds(Client.Client2, 2, { 1: override });
1464
- network.allocateAndSendIds(Client.Client1, 5);
1465
- network.allocateAndSendIds(Client.Client2, 5);
1466
- network.deliverOperations(DestinationClient.All);
1467
- expectSerializes(network.getCompressor(Client.Client1));
1468
- expectSerializes(network.getCompressor(Client.Client2));
1469
- expectSerializes(network.getCompressor(Client.Client3));
1470
- });
1471
- itNetwork('can resume a session and interact with multiple other clients', 3, (network) => {
1472
- const clusterSize = network.getCompressor(Client.Client1).clusterCapacity;
1473
- network.allocateAndSendIds(Client.Client1, clusterSize);
1474
- network.allocateAndSendIds(Client.Client2, clusterSize);
1475
- network.allocateAndSendIds(Client.Client3, clusterSize);
1476
- network.allocateAndSendIds(Client.Client1, clusterSize);
1477
- network.allocateAndSendIds(Client.Client2, clusterSize);
1478
- network.allocateAndSendIds(Client.Client3, clusterSize);
1479
- network.deliverOperations(DestinationClient.All);
1480
- network.goOfflineThenResume(Client.Client1);
1481
- network.allocateAndSendIds(Client.Client1, 2);
1482
- network.allocateAndSendIds(Client.Client2, 2);
1483
- network.allocateAndSendIds(Client.Client3, 2);
1484
- expectSequencedLogsAlign(network, Client.Client1, Client.Client2);
1485
- });
1486
- itNetwork('can serialize after a large fuzz input', 3, (network) => {
1487
- const generator = take(2000, makeOpGenerator({ includeOverrides: true }));
1488
- performFuzzActions(generator, network, Math.PI, undefined, true, (network) => {
1489
- // Periodically check that everyone in the network has the same serialized state
1490
- network.deliverOperations(DestinationClient.All);
1491
- const compressors = network.getTargetCompressors(DestinationClient.All);
1492
- let deserializedPrev = roundtrip(compressors[0][1], false)[1];
1493
- for (let i = 1; i < compressors.length; i++) {
1494
- const deserializedCur = roundtrip(compressors[i][1], false)[1];
1495
- expect(deserializedPrev.equals(deserializedCur, false)).to.be.true;
1496
- deserializedPrev = deserializedCur;
1497
- }
1498
- });
1499
- expectSerializes(network.getCompressor(Client.Client1));
1500
- expectSerializes(network.getCompressor(Client.Client2));
1501
- expectSerializes(network.getCompressor(Client.Client3));
1502
- });
1503
- itNetwork('stores override indices relative to their clusters', 3, (network) => {
1504
- network.allocateAndSendIds(Client.Client1, 3, { 0: 'cluster1' });
1505
- network.allocateAndSendIds(Client.Client2, 3, { 0: 'cluster2' });
1506
- network.deliverOperations(Client.Client1);
1507
- const serialized = network.getCompressor(Client.Client1).serialize(false);
1508
- expect(serialized.clusters.length).to.equal(2);
1509
- expect(serialized.clusters[0][2]?.[0][0]).to.equal(0);
1510
- expect(serialized.clusters[1][2]?.[0][0]).to.equal(0);
1511
- });
1512
- });
1513
- });
1514
- });
1515
- function createNetworkTestFunction(validateAfter) {
1516
- return (title, testOrCapacity, test) => {
1517
- it(title, () => {
1518
- const hasCapacity = typeof testOrCapacity === 'number';
1519
- const capacity = hasCapacity ? testOrCapacity : undefined;
1520
- const network = new IdCompressorTestNetwork(capacity);
1521
- (hasCapacity ? assertNotUndefined(test) : testOrCapacity)(network);
1522
- if (validateAfter) {
1523
- network.deliverOperations(DestinationClient.All);
1524
- network.assertNetworkState();
1525
- }
1526
- }).timeout(10000);
1527
- };
1528
- }
1529
- function describeNetwork(title, its) {
1530
- describe(title, () => {
1531
- its(createNetworkTestFunction(false));
1532
- });
1533
- describe(`${title} (with validation)`, () => {
1534
- its(createNetworkTestFunction(true));
1535
- });
1536
- }
1537
- function describeNetworkNoValidation(title, its) {
1538
- describe(title, () => {
1539
- its(createNetworkTestFunction(false));
1540
- });
1541
- }
1542
- //# sourceMappingURL=IdCompressor.tests.js.map