@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.
- package/CHANGELOG.md +23 -0
- package/api-report/experimental-tree.api.md +2 -2
- package/dist/ChangeCompression.d.ts +2 -2
- package/dist/ChangeCompression.d.ts.map +1 -1
- package/dist/ChangeCompression.js +1 -1
- package/dist/ChangeCompression.js.map +1 -1
- package/dist/ChangeTypes.d.ts +1 -1
- package/dist/ChangeTypes.d.ts.map +1 -1
- package/dist/ChangeTypes.js +4 -4
- package/dist/ChangeTypes.js.map +1 -1
- package/dist/Checkout.d.ts +3 -3
- package/dist/Checkout.d.ts.map +1 -1
- package/dist/Checkout.js +17 -17
- package/dist/Checkout.js.map +1 -1
- package/dist/EditLog.d.ts +1 -1
- package/dist/EditLog.d.ts.map +1 -1
- package/dist/EditLog.js +9 -9
- package/dist/EditLog.js.map +1 -1
- package/dist/EditUtilities.d.ts +3 -3
- package/dist/EditUtilities.d.ts.map +1 -1
- package/dist/EditUtilities.js +6 -6
- package/dist/EditUtilities.js.map +1 -1
- package/dist/Forest.d.ts.map +1 -1
- package/dist/Forest.js +23 -23
- package/dist/Forest.js.map +1 -1
- package/dist/HistoryEditFactory.d.ts +1 -1
- package/dist/HistoryEditFactory.d.ts.map +1 -1
- package/dist/HistoryEditFactory.js +7 -7
- package/dist/HistoryEditFactory.js.map +1 -1
- package/dist/IdConversion.d.ts.map +1 -1
- package/dist/IdConversion.js.map +1 -1
- package/dist/LogViewer.d.ts +2 -2
- package/dist/LogViewer.d.ts.map +1 -1
- package/dist/LogViewer.js +7 -7
- package/dist/LogViewer.js.map +1 -1
- package/dist/MergeHealth.d.ts.map +1 -1
- package/dist/MergeHealth.js +1 -1
- package/dist/MergeHealth.js.map +1 -1
- package/dist/NodeIdUtilities.d.ts +1 -1
- package/dist/NodeIdUtilities.d.ts.map +1 -1
- package/dist/NodeIdUtilities.js +1 -1
- package/dist/NodeIdUtilities.js.map +1 -1
- package/dist/PayloadUtilities.d.ts.map +1 -1
- package/dist/PayloadUtilities.js +2 -2
- package/dist/PayloadUtilities.js.map +1 -1
- package/dist/ReconciliationPath.d.ts +1 -1
- package/dist/ReconciliationPath.d.ts.map +1 -1
- package/dist/ReconciliationPath.js.map +1 -1
- package/dist/RevisionValueCache.d.ts.map +1 -1
- package/dist/RevisionValueCache.js +2 -2
- package/dist/RevisionValueCache.js.map +1 -1
- package/dist/RevisionView.d.ts +2 -2
- package/dist/RevisionView.d.ts.map +1 -1
- package/dist/RevisionView.js.map +1 -1
- package/dist/SerializationUtilities.d.ts +1 -1
- package/dist/SerializationUtilities.d.ts.map +1 -1
- package/dist/SerializationUtilities.js.map +1 -1
- package/dist/SharedTree.d.ts +8 -7
- package/dist/SharedTree.d.ts.map +1 -1
- package/dist/SharedTree.js +78 -78
- package/dist/SharedTree.js.map +1 -1
- package/dist/SharedTreeEncoder.d.ts +1 -1
- package/dist/SharedTreeEncoder.d.ts.map +1 -1
- package/dist/SharedTreeEncoder.js +31 -31
- package/dist/SharedTreeEncoder.js.map +1 -1
- package/dist/Summary.d.ts +2 -2
- package/dist/Summary.d.ts.map +1 -1
- package/dist/Summary.js +2 -2
- package/dist/Summary.js.map +1 -1
- package/dist/SummaryBackCompatibility.d.ts.map +1 -1
- package/dist/SummaryBackCompatibility.js.map +1 -1
- package/dist/SummaryTestUtilities.d.ts +1 -1
- package/dist/SummaryTestUtilities.d.ts.map +1 -1
- package/dist/SummaryTestUtilities.js.map +1 -1
- package/dist/Transaction.d.ts +3 -3
- package/dist/Transaction.d.ts.map +1 -1
- package/dist/Transaction.js +3 -3
- package/dist/Transaction.js.map +1 -1
- package/dist/TransactionInternal.d.ts +3 -3
- package/dist/TransactionInternal.d.ts.map +1 -1
- package/dist/TransactionInternal.js +6 -6
- package/dist/TransactionInternal.js.map +1 -1
- package/dist/TreeCompressor.d.ts +5 -1
- package/dist/TreeCompressor.d.ts.map +1 -1
- package/dist/TreeCompressor.js +5 -5
- package/dist/TreeCompressor.js.map +1 -1
- package/dist/TreeNodeHandle.d.ts +1 -1
- package/dist/TreeNodeHandle.d.ts.map +1 -1
- package/dist/TreeNodeHandle.js.map +1 -1
- package/dist/TreeView.d.ts +1 -1
- package/dist/TreeView.d.ts.map +1 -1
- package/dist/TreeView.js +2 -3
- package/dist/TreeView.js.map +1 -1
- package/dist/UuidUtilities.d.ts.map +1 -1
- package/dist/UuidUtilities.js.map +1 -1
- package/dist/id-compressor/AppendOnlySortedMap.d.ts.map +1 -1
- package/dist/id-compressor/AppendOnlySortedMap.js +3 -3
- package/dist/id-compressor/AppendOnlySortedMap.js.map +1 -1
- package/dist/id-compressor/IdCompressor.d.ts +1 -1
- package/dist/id-compressor/IdCompressor.d.ts.map +1 -1
- package/dist/id-compressor/IdCompressor.js +25 -26
- package/dist/id-compressor/IdCompressor.js.map +1 -1
- package/dist/id-compressor/IdRange.d.ts.map +1 -1
- package/dist/id-compressor/IdRange.js +2 -2
- package/dist/id-compressor/IdRange.js.map +1 -1
- package/dist/id-compressor/NumericUuid.d.ts.map +1 -1
- package/dist/id-compressor/NumericUuid.js +2 -1
- package/dist/id-compressor/NumericUuid.js.map +1 -1
- package/dist/id-compressor/SessionIdNormalizer.d.ts.map +1 -1
- package/dist/id-compressor/SessionIdNormalizer.js +8 -9
- package/dist/id-compressor/SessionIdNormalizer.js.map +1 -1
- package/dist/migration-shim/migrationDeltaHandler.d.ts +1 -1
- package/dist/migration-shim/migrationDeltaHandler.d.ts.map +1 -1
- package/dist/migration-shim/migrationDeltaHandler.js +11 -11
- package/dist/migration-shim/migrationDeltaHandler.js.map +1 -1
- package/dist/migration-shim/migrationShim.d.ts +8 -4
- package/dist/migration-shim/migrationShim.d.ts.map +1 -1
- package/dist/migration-shim/migrationShim.js +13 -13
- package/dist/migration-shim/migrationShim.js.map +1 -1
- package/dist/migration-shim/migrationShimFactory.d.ts +2 -2
- package/dist/migration-shim/migrationShimFactory.d.ts.map +1 -1
- package/dist/migration-shim/migrationShimFactory.js +2 -2
- package/dist/migration-shim/migrationShimFactory.js.map +1 -1
- package/dist/migration-shim/packageVersion.d.ts +0 -2
- package/dist/migration-shim/packageVersion.d.ts.map +1 -1
- package/dist/migration-shim/packageVersion.js +0 -2
- package/dist/migration-shim/packageVersion.js.map +1 -1
- package/dist/migration-shim/sharedTreeDeltaHandler.d.ts.map +1 -1
- package/dist/migration-shim/sharedTreeDeltaHandler.js +8 -8
- package/dist/migration-shim/sharedTreeDeltaHandler.js.map +1 -1
- package/dist/migration-shim/sharedTreeShim.d.ts +2 -2
- package/dist/migration-shim/sharedTreeShim.d.ts.map +1 -1
- package/dist/migration-shim/sharedTreeShim.js +8 -4
- package/dist/migration-shim/sharedTreeShim.js.map +1 -1
- package/dist/migration-shim/sharedTreeShimFactory.d.ts +1 -1
- package/dist/migration-shim/sharedTreeShimFactory.d.ts.map +1 -1
- package/dist/migration-shim/sharedTreeShimFactory.js +2 -2
- package/dist/migration-shim/sharedTreeShimFactory.js.map +1 -1
- package/dist/migration-shim/shimChannelServices.d.ts +1 -1
- package/dist/migration-shim/shimChannelServices.d.ts.map +1 -1
- package/dist/migration-shim/shimChannelServices.js.map +1 -1
- package/dist/migration-shim/shimDeltaConnection.d.ts +1 -1
- package/dist/migration-shim/shimDeltaConnection.d.ts.map +1 -1
- package/dist/migration-shim/shimDeltaConnection.js +2 -2
- package/dist/migration-shim/shimDeltaConnection.js.map +1 -1
- package/dist/migration-shim/shimHandle.d.ts.map +1 -1
- package/dist/migration-shim/shimHandle.js.map +1 -1
- package/dist/migration-shim/types.d.ts +1 -1
- package/dist/migration-shim/types.d.ts.map +1 -1
- package/dist/migration-shim/types.js.map +1 -1
- package/dist/migration-shim/utils.d.ts +1 -1
- package/dist/migration-shim/utils.d.ts.map +1 -1
- package/dist/migration-shim/utils.js.map +1 -1
- package/dist/persisted-types/0.0.2.d.ts +1 -1
- package/dist/persisted-types/0.0.2.d.ts.map +1 -1
- package/dist/persisted-types/0.0.2.js.map +1 -1
- package/dist/persisted-types/0.1.1.d.ts +1 -1
- package/dist/persisted-types/0.1.1.d.ts.map +1 -1
- package/dist/persisted-types/0.1.1.js +3 -3
- package/dist/persisted-types/0.1.1.js.map +1 -1
- package/lib/ChangeCompression.d.ts +2 -2
- package/lib/ChangeCompression.d.ts.map +1 -1
- package/lib/ChangeCompression.js +1 -1
- package/lib/ChangeCompression.js.map +1 -1
- package/lib/ChangeTypes.d.ts +1 -1
- package/lib/ChangeTypes.d.ts.map +1 -1
- package/lib/ChangeTypes.js +2 -2
- package/lib/ChangeTypes.js.map +1 -1
- package/lib/Checkout.d.ts +3 -3
- package/lib/Checkout.d.ts.map +1 -1
- package/lib/Checkout.js +4 -4
- package/lib/Checkout.js.map +1 -1
- package/lib/EditLog.d.ts +1 -1
- package/lib/EditLog.d.ts.map +1 -1
- package/lib/EditLog.js +2 -2
- package/lib/EditLog.js.map +1 -1
- package/lib/EditUtilities.d.ts +3 -3
- package/lib/EditUtilities.d.ts.map +1 -1
- package/lib/EditUtilities.js +5 -5
- package/lib/EditUtilities.js.map +1 -1
- package/lib/Forest.d.ts.map +1 -1
- package/lib/Forest.js +2 -2
- package/lib/Forest.js.map +1 -1
- package/lib/HistoryEditFactory.d.ts +1 -1
- package/lib/HistoryEditFactory.d.ts.map +1 -1
- package/lib/HistoryEditFactory.js +5 -5
- package/lib/HistoryEditFactory.js.map +1 -1
- package/lib/IdConversion.d.ts.map +1 -1
- package/lib/IdConversion.js.map +1 -1
- package/lib/LogViewer.d.ts +2 -2
- package/lib/LogViewer.d.ts.map +1 -1
- package/lib/LogViewer.js +3 -3
- package/lib/LogViewer.js.map +1 -1
- package/lib/MergeHealth.d.ts.map +1 -1
- package/lib/MergeHealth.js +1 -1
- package/lib/MergeHealth.js.map +1 -1
- package/lib/NodeIdUtilities.d.ts +1 -1
- package/lib/NodeIdUtilities.d.ts.map +1 -1
- package/lib/NodeIdUtilities.js +1 -1
- package/lib/NodeIdUtilities.js.map +1 -1
- package/lib/PayloadUtilities.d.ts.map +1 -1
- package/lib/PayloadUtilities.js +1 -1
- package/lib/PayloadUtilities.js.map +1 -1
- package/lib/ReconciliationPath.d.ts +1 -1
- package/lib/ReconciliationPath.d.ts.map +1 -1
- package/lib/ReconciliationPath.js.map +1 -1
- package/lib/RevisionValueCache.d.ts.map +1 -1
- package/lib/RevisionValueCache.js +2 -2
- package/lib/RevisionValueCache.js.map +1 -1
- package/lib/RevisionView.d.ts +2 -2
- package/lib/RevisionView.d.ts.map +1 -1
- package/lib/RevisionView.js.map +1 -1
- package/lib/SerializationUtilities.d.ts +1 -1
- package/lib/SerializationUtilities.d.ts.map +1 -1
- package/lib/SerializationUtilities.js.map +1 -1
- package/lib/SharedTree.d.ts +8 -7
- package/lib/SharedTree.d.ts.map +1 -1
- package/lib/SharedTree.js +13 -13
- package/lib/SharedTree.js.map +1 -1
- package/lib/SharedTreeEncoder.d.ts +1 -1
- package/lib/SharedTreeEncoder.d.ts.map +1 -1
- package/lib/SharedTreeEncoder.js +5 -5
- package/lib/SharedTreeEncoder.js.map +1 -1
- package/lib/Summary.d.ts +2 -2
- package/lib/Summary.d.ts.map +1 -1
- package/lib/Summary.js +1 -1
- package/lib/Summary.js.map +1 -1
- package/lib/SummaryBackCompatibility.d.ts.map +1 -1
- package/lib/SummaryBackCompatibility.js.map +1 -1
- package/lib/SummaryTestUtilities.d.ts +1 -1
- package/lib/SummaryTestUtilities.d.ts.map +1 -1
- package/lib/SummaryTestUtilities.js.map +1 -1
- package/lib/Transaction.d.ts +3 -3
- package/lib/Transaction.d.ts.map +1 -1
- package/lib/Transaction.js +3 -3
- package/lib/Transaction.js.map +1 -1
- package/lib/TransactionInternal.d.ts +3 -3
- package/lib/TransactionInternal.d.ts.map +1 -1
- package/lib/TransactionInternal.js +3 -3
- package/lib/TransactionInternal.js.map +1 -1
- package/lib/TreeCompressor.d.ts +5 -1
- package/lib/TreeCompressor.d.ts.map +1 -1
- package/lib/TreeCompressor.js +2 -2
- package/lib/TreeCompressor.js.map +1 -1
- package/lib/TreeNodeHandle.d.ts +1 -1
- package/lib/TreeNodeHandle.d.ts.map +1 -1
- package/lib/TreeNodeHandle.js.map +1 -1
- package/lib/TreeView.d.ts +1 -1
- package/lib/TreeView.d.ts.map +1 -1
- package/lib/TreeView.js +1 -2
- package/lib/TreeView.js.map +1 -1
- package/lib/UuidUtilities.d.ts.map +1 -1
- package/lib/UuidUtilities.js +1 -1
- package/lib/UuidUtilities.js.map +1 -1
- package/lib/id-compressor/AppendOnlySortedMap.d.ts.map +1 -1
- package/lib/id-compressor/AppendOnlySortedMap.js +1 -1
- package/lib/id-compressor/AppendOnlySortedMap.js.map +1 -1
- package/lib/id-compressor/IdCompressor.d.ts +1 -1
- package/lib/id-compressor/IdCompressor.d.ts.map +1 -1
- package/lib/id-compressor/IdCompressor.js +4 -5
- package/lib/id-compressor/IdCompressor.js.map +1 -1
- package/lib/id-compressor/IdRange.d.ts.map +1 -1
- package/lib/id-compressor/IdRange.js +1 -1
- package/lib/id-compressor/IdRange.js.map +1 -1
- package/lib/id-compressor/NumericUuid.d.ts.map +1 -1
- package/lib/id-compressor/NumericUuid.js +2 -1
- package/lib/id-compressor/NumericUuid.js.map +1 -1
- package/lib/id-compressor/SessionIdNormalizer.d.ts.map +1 -1
- package/lib/id-compressor/SessionIdNormalizer.js +1 -2
- package/lib/id-compressor/SessionIdNormalizer.js.map +1 -1
- package/lib/migration-shim/migrationDeltaHandler.d.ts +1 -1
- package/lib/migration-shim/migrationDeltaHandler.d.ts.map +1 -1
- package/lib/migration-shim/migrationDeltaHandler.js +1 -1
- package/lib/migration-shim/migrationDeltaHandler.js.map +1 -1
- package/lib/migration-shim/migrationShim.d.ts +8 -4
- package/lib/migration-shim/migrationShim.d.ts.map +1 -1
- package/lib/migration-shim/migrationShim.js +3 -3
- package/lib/migration-shim/migrationShim.js.map +1 -1
- package/lib/migration-shim/migrationShimFactory.d.ts +2 -2
- package/lib/migration-shim/migrationShimFactory.d.ts.map +1 -1
- package/lib/migration-shim/migrationShimFactory.js +1 -1
- package/lib/migration-shim/migrationShimFactory.js.map +1 -1
- package/lib/migration-shim/packageVersion.d.ts +0 -2
- package/lib/migration-shim/packageVersion.d.ts.map +1 -1
- package/lib/migration-shim/packageVersion.js +0 -2
- package/lib/migration-shim/packageVersion.js.map +1 -1
- package/lib/migration-shim/sharedTreeDeltaHandler.d.ts.map +1 -1
- package/lib/migration-shim/sharedTreeDeltaHandler.js +1 -1
- package/lib/migration-shim/sharedTreeDeltaHandler.js.map +1 -1
- package/lib/migration-shim/sharedTreeShim.d.ts +2 -2
- package/lib/migration-shim/sharedTreeShim.d.ts.map +1 -1
- package/lib/migration-shim/sharedTreeShim.js +6 -2
- package/lib/migration-shim/sharedTreeShim.js.map +1 -1
- package/lib/migration-shim/sharedTreeShimFactory.d.ts +1 -1
- package/lib/migration-shim/sharedTreeShimFactory.d.ts.map +1 -1
- package/lib/migration-shim/sharedTreeShimFactory.js +1 -1
- package/lib/migration-shim/sharedTreeShimFactory.js.map +1 -1
- package/lib/migration-shim/shimChannelServices.d.ts +1 -1
- package/lib/migration-shim/shimChannelServices.d.ts.map +1 -1
- package/lib/migration-shim/shimChannelServices.js.map +1 -1
- package/lib/migration-shim/shimDeltaConnection.d.ts +1 -1
- package/lib/migration-shim/shimDeltaConnection.d.ts.map +1 -1
- package/lib/migration-shim/shimDeltaConnection.js +1 -1
- package/lib/migration-shim/shimDeltaConnection.js.map +1 -1
- package/lib/migration-shim/shimHandle.d.ts.map +1 -1
- package/lib/migration-shim/shimHandle.js.map +1 -1
- package/lib/migration-shim/types.d.ts +1 -1
- package/lib/migration-shim/types.d.ts.map +1 -1
- package/lib/migration-shim/types.js.map +1 -1
- package/lib/migration-shim/utils.d.ts +1 -1
- package/lib/migration-shim/utils.d.ts.map +1 -1
- package/lib/migration-shim/utils.js.map +1 -1
- package/lib/persisted-types/0.0.2.d.ts +1 -1
- package/lib/persisted-types/0.0.2.d.ts.map +1 -1
- package/lib/persisted-types/0.0.2.js.map +1 -1
- package/lib/persisted-types/0.1.1.d.ts +1 -1
- package/lib/persisted-types/0.1.1.d.ts.map +1 -1
- package/lib/persisted-types/0.1.1.js +1 -1
- package/lib/persisted-types/0.1.1.js.map +1 -1
- package/package.json +33 -32
- package/src/ChangeCompression.ts +8 -8
- package/src/ChangeTypes.ts +5 -4
- package/src/Checkout.ts +9 -7
- package/src/EditLog.ts +4 -3
- package/src/EditUtilities.ts +9 -8
- package/src/Forest.ts +3 -2
- package/src/HistoryEditFactory.ts +12 -11
- package/src/IdConversion.ts +2 -2
- package/src/LogViewer.ts +5 -4
- package/src/MergeHealth.ts +2 -1
- package/src/NodeIdUtilities.ts +2 -2
- package/src/PayloadUtilities.ts +2 -1
- package/src/ReconciliationPath.ts +1 -1
- package/src/RevisionValueCache.ts +3 -2
- package/src/RevisionView.ts +3 -3
- package/src/SerializationUtilities.ts +1 -1
- package/src/SharedTree.ts +46 -49
- package/src/SharedTreeEncoder.ts +30 -29
- package/src/Summary.ts +5 -3
- package/src/SummaryBackCompatibility.ts +1 -0
- package/src/SummaryTestUtilities.ts +2 -1
- package/src/Transaction.ts +7 -6
- package/src/TransactionInternal.ts +17 -16
- package/src/TreeCompressor.ts +6 -4
- package/src/TreeNodeHandle.ts +2 -2
- package/src/TreeView.ts +3 -3
- package/src/UuidUtilities.ts +2 -1
- package/src/id-compressor/AppendOnlySortedMap.ts +2 -1
- package/src/id-compressor/IdCompressor.ts +18 -17
- package/src/id-compressor/IdRange.ts +2 -1
- package/src/id-compressor/NumericUuid.ts +1 -1
- package/src/id-compressor/SessionIdNormalizer.ts +3 -3
- package/src/migration-shim/migrationDeltaHandler.ts +3 -2
- package/src/migration-shim/migrationShim.ts +14 -10
- package/src/migration-shim/migrationShimFactory.ts +6 -4
- package/src/migration-shim/packageVersion.ts +0 -2
- package/src/migration-shim/sharedTreeDeltaHandler.ts +3 -2
- package/src/migration-shim/sharedTreeShim.ts +7 -5
- package/src/migration-shim/sharedTreeShimFactory.ts +3 -3
- package/src/migration-shim/shimChannelServices.ts +1 -1
- package/src/migration-shim/shimDeltaConnection.ts +3 -2
- package/src/migration-shim/shimHandle.ts +1 -0
- package/src/migration-shim/types.ts +3 -1
- package/src/migration-shim/utils.ts +2 -1
- package/src/persisted-types/0.0.2.ts +2 -2
- package/src/persisted-types/0.1.1.ts +10 -8
- package/dist/tree-alpha.d.ts +0 -2901
- package/dist/tree-beta.d.ts +0 -348
- package/dist/tree-public.d.ts +0 -348
- package/dist/tree-untrimmed.d.ts +0 -3820
- package/lib/test/AppendOnlySortedMap.perf.tests.d.ts +0 -6
- package/lib/test/AppendOnlySortedMap.perf.tests.d.ts.map +0 -1
- package/lib/test/AppendOnlySortedMap.perf.tests.js +0 -49
- package/lib/test/AppendOnlySortedMap.perf.tests.js.map +0 -1
- package/lib/test/AppendOnlySortedMap.tests.d.ts +0 -6
- package/lib/test/AppendOnlySortedMap.tests.d.ts.map +0 -1
- package/lib/test/AppendOnlySortedMap.tests.js +0 -213
- package/lib/test/AppendOnlySortedMap.tests.js.map +0 -1
- package/lib/test/ChangeCompression.tests.d.ts +0 -6
- package/lib/test/ChangeCompression.tests.d.ts.map +0 -1
- package/lib/test/ChangeCompression.tests.js +0 -154
- package/lib/test/ChangeCompression.tests.js.map +0 -1
- package/lib/test/Checkout.tests.d.ts +0 -10
- package/lib/test/Checkout.tests.d.ts.map +0 -1
- package/lib/test/Checkout.tests.js +0 -460
- package/lib/test/Checkout.tests.js.map +0 -1
- package/lib/test/Common.tests.d.ts +0 -6
- package/lib/test/Common.tests.d.ts.map +0 -1
- package/lib/test/Common.tests.js +0 -102
- package/lib/test/Common.tests.js.map +0 -1
- package/lib/test/EagerCheckout.tests.d.ts +0 -6
- package/lib/test/EagerCheckout.tests.d.ts.map +0 -1
- package/lib/test/EagerCheckout.tests.js +0 -20
- package/lib/test/EagerCheckout.tests.js.map +0 -1
- package/lib/test/Edit.tests.d.ts +0 -6
- package/lib/test/Edit.tests.d.ts.map +0 -1
- package/lib/test/Edit.tests.js +0 -60
- package/lib/test/Edit.tests.js.map +0 -1
- package/lib/test/EditLog.perf.tests.d.ts +0 -6
- package/lib/test/EditLog.perf.tests.d.ts.map +0 -1
- package/lib/test/EditLog.perf.tests.js +0 -41
- package/lib/test/EditLog.perf.tests.js.map +0 -1
- package/lib/test/EditLog.tests.d.ts +0 -6
- package/lib/test/EditLog.tests.d.ts.map +0 -1
- package/lib/test/EditLog.tests.js +0 -355
- package/lib/test/EditLog.tests.js.map +0 -1
- package/lib/test/EditUtilities.tests.d.ts +0 -6
- package/lib/test/EditUtilities.tests.d.ts.map +0 -1
- package/lib/test/EditUtilities.tests.js +0 -512
- package/lib/test/EditUtilities.tests.js.map +0 -1
- package/lib/test/Forest.perf.tests.d.ts +0 -6
- package/lib/test/Forest.perf.tests.d.ts.map +0 -1
- package/lib/test/Forest.perf.tests.js +0 -135
- package/lib/test/Forest.perf.tests.js.map +0 -1
- package/lib/test/Forest.tests.d.ts +0 -6
- package/lib/test/Forest.tests.d.ts.map +0 -1
- package/lib/test/Forest.tests.js +0 -213
- package/lib/test/Forest.tests.js.map +0 -1
- package/lib/test/GenericTransaction.tests.d.ts +0 -6
- package/lib/test/GenericTransaction.tests.d.ts.map +0 -1
- package/lib/test/GenericTransaction.tests.js +0 -31
- package/lib/test/GenericTransaction.tests.js.map +0 -1
- package/lib/test/HistoryEditFactory.tests.d.ts +0 -6
- package/lib/test/HistoryEditFactory.tests.d.ts.map +0 -1
- package/lib/test/HistoryEditFactory.tests.js +0 -170
- package/lib/test/HistoryEditFactory.tests.js.map +0 -1
- package/lib/test/IdCompressor.perf.tests.d.ts +0 -6
- package/lib/test/IdCompressor.perf.tests.d.ts.map +0 -1
- package/lib/test/IdCompressor.perf.tests.js +0 -290
- package/lib/test/IdCompressor.perf.tests.js.map +0 -1
- package/lib/test/IdCompressor.tests.d.ts +0 -6
- package/lib/test/IdCompressor.tests.d.ts.map +0 -1
- package/lib/test/IdCompressor.tests.js +0 -1542
- package/lib/test/IdCompressor.tests.js.map +0 -1
- package/lib/test/IdConversion.tests.d.ts +0 -6
- package/lib/test/IdConversion.tests.d.ts.map +0 -1
- package/lib/test/IdConversion.tests.js +0 -36
- package/lib/test/IdConversion.tests.js.map +0 -1
- package/lib/test/LazyCheckout.tests.d.ts +0 -6
- package/lib/test/LazyCheckout.tests.d.ts.map +0 -1
- package/lib/test/LazyCheckout.tests.js +0 -22
- package/lib/test/LazyCheckout.tests.js.map +0 -1
- package/lib/test/LogViewer.tests.d.ts +0 -6
- package/lib/test/LogViewer.tests.d.ts.map +0 -1
- package/lib/test/LogViewer.tests.js +0 -588
- package/lib/test/LogViewer.tests.js.map +0 -1
- package/lib/test/MergeHealthTelemetryHeartbeat.tests.d.ts +0 -6
- package/lib/test/MergeHealthTelemetryHeartbeat.tests.d.ts.map +0 -1
- package/lib/test/MergeHealthTelemetryHeartbeat.tests.js +0 -351
- package/lib/test/MergeHealthTelemetryHeartbeat.tests.js.map +0 -1
- package/lib/test/NumericUuid.perf.tests.d.ts +0 -6
- package/lib/test/NumericUuid.perf.tests.d.ts.map +0 -1
- package/lib/test/NumericUuid.perf.tests.js +0 -68
- package/lib/test/NumericUuid.perf.tests.js.map +0 -1
- package/lib/test/NumericUuid.tests.d.ts +0 -6
- package/lib/test/NumericUuid.tests.d.ts.map +0 -1
- package/lib/test/NumericUuid.tests.js +0 -192
- package/lib/test/NumericUuid.tests.js.map +0 -1
- package/lib/test/RevisionValueCache.tests.d.ts +0 -6
- package/lib/test/RevisionValueCache.tests.d.ts.map +0 -1
- package/lib/test/RevisionValueCache.tests.js +0 -106
- package/lib/test/RevisionValueCache.tests.js.map +0 -1
- package/lib/test/RevisionView.tests.d.ts +0 -6
- package/lib/test/RevisionView.tests.d.ts.map +0 -1
- package/lib/test/RevisionView.tests.js +0 -131
- package/lib/test/RevisionView.tests.js.map +0 -1
- package/lib/test/SessionIdNormalizer.tests.d.ts +0 -6
- package/lib/test/SessionIdNormalizer.tests.d.ts.map +0 -1
- package/lib/test/SessionIdNormalizer.tests.js +0 -377
- package/lib/test/SessionIdNormalizer.tests.js.map +0 -1
- package/lib/test/SharedTree.fuzz.tests.d.ts +0 -6
- package/lib/test/SharedTree.fuzz.tests.d.ts.map +0 -1
- package/lib/test/SharedTree.fuzz.tests.js +0 -9
- package/lib/test/SharedTree.fuzz.tests.js.map +0 -1
- package/lib/test/SharedTree.perf.tests.d.ts +0 -6
- package/lib/test/SharedTree.perf.tests.d.ts.map +0 -1
- package/lib/test/SharedTree.perf.tests.js +0 -39
- package/lib/test/SharedTree.perf.tests.js.map +0 -1
- package/lib/test/SharedTree.tests.d.ts +0 -6
- package/lib/test/SharedTree.tests.d.ts.map +0 -1
- package/lib/test/SharedTree.tests.js +0 -22
- package/lib/test/SharedTree.tests.js.map +0 -1
- package/lib/test/StringInterner.tests.d.ts +0 -6
- package/lib/test/StringInterner.tests.d.ts.map +0 -1
- package/lib/test/StringInterner.tests.js +0 -73
- package/lib/test/StringInterner.tests.js.map +0 -1
- package/lib/test/Summary.tests.d.ts +0 -7
- package/lib/test/Summary.tests.d.ts.map +0 -1
- package/lib/test/Summary.tests.js +0 -386
- package/lib/test/Summary.tests.js.map +0 -1
- package/lib/test/Transaction.tests.d.ts +0 -6
- package/lib/test/Transaction.tests.d.ts.map +0 -1
- package/lib/test/Transaction.tests.js +0 -124
- package/lib/test/Transaction.tests.js.map +0 -1
- package/lib/test/TransactionInternal.tests.d.ts +0 -6
- package/lib/test/TransactionInternal.tests.d.ts.map +0 -1
- package/lib/test/TransactionInternal.tests.js +0 -576
- package/lib/test/TransactionInternal.tests.js.map +0 -1
- package/lib/test/TreeCompression.tests.d.ts +0 -6
- package/lib/test/TreeCompression.tests.d.ts.map +0 -1
- package/lib/test/TreeCompression.tests.js +0 -291
- package/lib/test/TreeCompression.tests.js.map +0 -1
- package/lib/test/TreeView.tests.d.ts +0 -6
- package/lib/test/TreeView.tests.d.ts.map +0 -1
- package/lib/test/TreeView.tests.js +0 -178
- package/lib/test/TreeView.tests.js.map +0 -1
- package/lib/test/UndoRedoHandler.tests.d.ts +0 -6
- package/lib/test/UndoRedoHandler.tests.d.ts.map +0 -1
- package/lib/test/UndoRedoHandler.tests.js +0 -37
- package/lib/test/UndoRedoHandler.tests.js.map +0 -1
- package/lib/test/fuzz/Generators.d.ts +0 -8
- package/lib/test/fuzz/Generators.d.ts.map +0 -1
- package/lib/test/fuzz/Generators.js +0 -345
- package/lib/test/fuzz/Generators.js.map +0 -1
- package/lib/test/fuzz/SharedTreeFuzzTests.d.ts +0 -23
- package/lib/test/fuzz/SharedTreeFuzzTests.d.ts.map +0 -1
- package/lib/test/fuzz/SharedTreeFuzzTests.js +0 -241
- package/lib/test/fuzz/SharedTreeFuzzTests.js.map +0 -1
- package/lib/test/fuzz/Types.d.ts +0 -136
- package/lib/test/fuzz/Types.d.ts.map +0 -1
- package/lib/test/fuzz/Types.js +0 -6
- package/lib/test/fuzz/Types.js.map +0 -1
- package/lib/test/utilities/IdCompressorTestUtilities.d.ts +0 -246
- package/lib/test/utilities/IdCompressorTestUtilities.d.ts.map +0 -1
- package/lib/test/utilities/IdCompressorTestUtilities.js +0 -608
- package/lib/test/utilities/IdCompressorTestUtilities.js.map +0 -1
- package/lib/test/utilities/MockTransaction.d.ts +0 -35
- package/lib/test/utilities/MockTransaction.d.ts.map +0 -1
- package/lib/test/utilities/MockTransaction.js +0 -51
- package/lib/test/utilities/MockTransaction.js.map +0 -1
- package/lib/test/utilities/PendingLocalStateTests.d.ts +0 -12
- package/lib/test/utilities/PendingLocalStateTests.d.ts.map +0 -1
- package/lib/test/utilities/PendingLocalStateTests.js +0 -223
- package/lib/test/utilities/PendingLocalStateTests.js.map +0 -1
- package/lib/test/utilities/SharedTreeTests.d.ts +0 -12
- package/lib/test/utilities/SharedTreeTests.d.ts.map +0 -1
- package/lib/test/utilities/SharedTreeTests.js +0 -949
- package/lib/test/utilities/SharedTreeTests.js.map +0 -1
- package/lib/test/utilities/SharedTreeVersioningTests.d.ts +0 -11
- package/lib/test/utilities/SharedTreeVersioningTests.d.ts.map +0 -1
- package/lib/test/utilities/SharedTreeVersioningTests.js +0 -439
- package/lib/test/utilities/SharedTreeVersioningTests.js.map +0 -1
- package/lib/test/utilities/SummaryLoadPerfTests.d.ts +0 -10
- package/lib/test/utilities/SummaryLoadPerfTests.d.ts.map +0 -1
- package/lib/test/utilities/SummaryLoadPerfTests.js +0 -105
- package/lib/test/utilities/SummaryLoadPerfTests.js.map +0 -1
- package/lib/test/utilities/SummarySizeTests.d.ts +0 -11
- package/lib/test/utilities/SummarySizeTests.d.ts.map +0 -1
- package/lib/test/utilities/SummarySizeTests.js +0 -160
- package/lib/test/utilities/SummarySizeTests.js.map +0 -1
- package/lib/test/utilities/TestCommon.d.ts +0 -13
- package/lib/test/utilities/TestCommon.d.ts.map +0 -1
- package/lib/test/utilities/TestCommon.js +0 -19
- package/lib/test/utilities/TestCommon.js.map +0 -1
- package/lib/test/utilities/TestNode.d.ts +0 -140
- package/lib/test/utilities/TestNode.d.ts.map +0 -1
- package/lib/test/utilities/TestNode.js +0 -282
- package/lib/test/utilities/TestNode.js.map +0 -1
- package/lib/test/utilities/TestSerializer.d.ts +0 -24
- package/lib/test/utilities/TestSerializer.d.ts.map +0 -1
- package/lib/test/utilities/TestSerializer.js +0 -40
- package/lib/test/utilities/TestSerializer.js.map +0 -1
- package/lib/test/utilities/TestUtilities.d.ts +0 -212
- package/lib/test/utilities/TestUtilities.d.ts.map +0 -1
- package/lib/test/utilities/TestUtilities.js +0 -413
- package/lib/test/utilities/TestUtilities.js.map +0 -1
- package/lib/test/utilities/UndoRedoTests.d.ts +0 -32
- package/lib/test/utilities/UndoRedoTests.d.ts.map +0 -1
- package/lib/test/utilities/UndoRedoTests.js +0 -317
- package/lib/test/utilities/UndoRedoTests.js.map +0 -1
- /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
|