@@ -14,6 +14,7 @@ import (
14
14
"context"
15
15
"encoding/json"
16
16
"log"
17
+ "reflect"
17
18
"testing"
18
19
"time"
19
20
@@ -1675,3 +1676,194 @@ func TestAssignSequenceReleaseLoop(t *testing.T) {
1675
1676
releasedSequenceCount := db .DbStats .Database ().SequenceReleasedCount .Value () - startReleasedSequenceCount
1676
1677
assert .Equal (t , int64 (expectedReleasedSequenceCount ), releasedSequenceCount )
1677
1678
}
1679
+
1680
+ // TestPutExistingCurrentVersion:
1681
+ // - Put a document in a db
1682
+ // - Assert on the update to HLV after that PUT
1683
+ // - Construct a HLV to represent the doc created locally being updated on a client
1684
+ // - Call PutExistingCurrentVersion simulating doc update arriving over replicator
1685
+ // - Assert that the doc's HLV in the bucket has been updated correctly with the CV, PV and cvCAS
1686
+ func TestPutExistingCurrentVersion (t * testing.T ) {
1687
+ db , ctx := setupTestDB (t )
1688
+ defer db .Close (ctx )
1689
+
1690
+ bucketUUID := db .BucketUUID
1691
+ collection := GetSingleDatabaseCollectionWithUser (t , db )
1692
+
1693
+ // create a new doc
1694
+ key := "doc1"
1695
+ body := Body {"key1" : "value1" }
1696
+
1697
+ rev , _ , err := collection .Put (ctx , key , body )
1698
+ require .NoError (t , err )
1699
+
1700
+ // assert on HLV on that above PUT
1701
+ syncData , err := collection .GetDocSyncData (ctx , "doc1" )
1702
+ assert .NoError (t , err )
1703
+ uintCAS := base .HexCasToUint64 (syncData .Cas )
1704
+ assert .Equal (t , bucketUUID , syncData .HLV .SourceID )
1705
+ assert .Equal (t , uintCAS , syncData .HLV .Version )
1706
+ assert .Equal (t , uintCAS , syncData .HLV .CurrentVersionCAS )
1707
+
1708
+ // store the cas version allocated to the above doc creation for creation of incoming HLV later in test
1709
+ originalDocVersion := syncData .HLV .Version
1710
+
1711
+ // PUT an update to the above doc
1712
+ body = Body {"key1" : "value11" }
1713
+ body [BodyRev ] = rev
1714
+ _ , _ , err = collection .Put (ctx , key , body )
1715
+ require .NoError (t , err )
1716
+
1717
+ // grab the new version for the above update to assert against later in test
1718
+ syncData , err = collection .GetDocSyncData (ctx , "doc1" )
1719
+ assert .NoError (t , err )
1720
+ docUpdateVersion := syncData .HLV .Version
1721
+
1722
+ // construct a mock doc update coming over a replicator
1723
+ body = Body {"key1" : "value2" }
1724
+ newDoc := createTestDocument (key , "" , body , false , 0 )
1725
+
1726
+ // construct a HLV that simulates a doc update happening on a client
1727
+ // this means moving the current source version pair to PV and adding new sourceID and version pair to CV
1728
+ pv := make (map [string ]uint64 )
1729
+ pv [bucketUUID ] = originalDocVersion
1730
+ // create a version larger than the allocated version above
1731
+ incomingVersion := docUpdateVersion + 10
1732
+ incomingHLV := HybridLogicalVector {
1733
+ SourceID : "test" ,
1734
+ Version : incomingVersion ,
1735
+ PreviousVersions : pv ,
1736
+ }
1737
+
1738
+ // grab the raw doc from the bucket to pass into the PutExistingCurrentVersion function for the above simulation of
1739
+ // doc update arriving over replicator
1740
+ _ , rawDoc , err := collection .GetDocumentWithRaw (ctx , key , DocUnmarshalSync )
1741
+ require .NoError (t , err )
1742
+
1743
+ doc , cv , _ , err := collection .PutExistingCurrentVersion (ctx , newDoc , incomingHLV , rawDoc )
1744
+ require .NoError (t , err )
1745
+ // assert on returned CV
1746
+ assert .Equal (t , "test" , cv .SourceID )
1747
+ assert .Equal (t , incomingVersion , cv .Version )
1748
+ assert .Equal (t , []byte (`{"key1":"value2"}` ), doc ._rawBody )
1749
+
1750
+ // assert on the sync data from the above update to the doc
1751
+ // CV should be equal to CV of update on client but the cvCAS should be updated with the new update and
1752
+ // PV should contain the old CV pair
1753
+ syncData , err = collection .GetDocSyncData (ctx , "doc1" )
1754
+ assert .NoError (t , err )
1755
+ uintCAS = base .HexCasToUint64 (syncData .Cas )
1756
+
1757
+ assert .Equal (t , "test" , syncData .HLV .SourceID )
1758
+ assert .Equal (t , incomingVersion , syncData .HLV .Version )
1759
+ assert .Equal (t , uintCAS , syncData .HLV .CurrentVersionCAS )
1760
+ // update the pv map so we can assert we have correct pv map in HLV
1761
+ pv [bucketUUID ] = docUpdateVersion
1762
+ assert .True (t , reflect .DeepEqual (syncData .HLV .PreviousVersions , pv ))
1763
+ assert .Equal (t , "3-60b024c44c283b369116c2c2570e8088" , syncData .CurrentRev )
1764
+ }
1765
+
1766
+ // TestPutExistingCurrentVersionWithConflict:
1767
+ // - Put a document in a db
1768
+ // - Assert on the update to HLV after that PUT
1769
+ // - Construct a HLV to represent the doc created locally being updated on a client
1770
+ // - Call PutExistingCurrentVersion simulating doc update arriving over replicator
1771
+ // - Assert conflict between the local HLV for the doc and the incoming mutation is correctly identified
1772
+ // - Assert that the doc's HLV in the bucket hasn't been updated
1773
+ func TestPutExistingCurrentVersionWithConflict (t * testing.T ) {
1774
+ db , ctx := setupTestDB (t )
1775
+ defer db .Close (ctx )
1776
+
1777
+ bucketUUID := db .BucketUUID
1778
+ collection := GetSingleDatabaseCollectionWithUser (t , db )
1779
+
1780
+ // create a new doc
1781
+ key := "doc1"
1782
+ body := Body {"key1" : "value1" }
1783
+
1784
+ _ , _ , err := collection .Put (ctx , key , body )
1785
+ require .NoError (t , err )
1786
+
1787
+ // assert on the HLV values after the above creation of the doc
1788
+ syncData , err := collection .GetDocSyncData (ctx , "doc1" )
1789
+ assert .NoError (t , err )
1790
+ uintCAS := base .HexCasToUint64 (syncData .Cas )
1791
+ assert .Equal (t , bucketUUID , syncData .HLV .SourceID )
1792
+ assert .Equal (t , uintCAS , syncData .HLV .Version )
1793
+ assert .Equal (t , uintCAS , syncData .HLV .CurrentVersionCAS )
1794
+
1795
+ // create a new doc update to simulate a doc update arriving over replicator from, client
1796
+ body = Body {"key1" : "value2" }
1797
+ newDoc := createTestDocument (key , "" , body , false , 0 )
1798
+ incomingHLV := HybridLogicalVector {
1799
+ SourceID : "test" ,
1800
+ Version : 1234 ,
1801
+ }
1802
+
1803
+ // grab the raw doc from the bucket to pass into the PutExistingCurrentVersion function
1804
+ _ , rawDoc , err := collection .GetDocumentWithRaw (ctx , key , DocUnmarshalSync )
1805
+ require .NoError (t , err )
1806
+
1807
+ // assert that a conflict is correctly identified and the resulting doc and cv are nil
1808
+ doc , cv , _ , err := collection .PutExistingCurrentVersion (ctx , newDoc , incomingHLV , rawDoc )
1809
+ require .Error (t , err )
1810
+ assert .ErrorContains (t , err , "Document revision conflict" )
1811
+ assert .Nil (t , cv )
1812
+ assert .Nil (t , doc )
1813
+
1814
+ // assert persisted doc hlv hasn't been updated
1815
+ syncData , err = collection .GetDocSyncData (ctx , "doc1" )
1816
+ assert .NoError (t , err )
1817
+ assert .Equal (t , bucketUUID , syncData .HLV .SourceID )
1818
+ assert .Equal (t , uintCAS , syncData .HLV .Version )
1819
+ assert .Equal (t , uintCAS , syncData .HLV .CurrentVersionCAS )
1820
+ }
1821
+
1822
+ // TestPutExistingCurrentVersionWithNoExistingDoc:
1823
+ // - Purpose of this test is to test PutExistingRevWithBody code pathway where an
1824
+ // existing doc is not provided from the bucket into the function simulating a new, not seen
1825
+ // before doc entering this code path
1826
+ func TestPutExistingCurrentVersionWithNoExistingDoc (t * testing.T ) {
1827
+ db , ctx := setupTestDB (t )
1828
+ defer db .Close (ctx )
1829
+
1830
+ bucketUUID := db .BucketUUID
1831
+ collection := GetSingleDatabaseCollectionWithUser (t , db )
1832
+
1833
+ // construct a mock doc update coming over a replicator
1834
+ body := Body {"key1" : "value2" }
1835
+ newDoc := createTestDocument ("doc2" , "" , body , false , 0 )
1836
+
1837
+ // construct a HLV that simulates a doc update happening on a client
1838
+ // this means moving the current source version pair to PV and adding new sourceID and version pair to CV
1839
+ pv := make (map [string ]uint64 )
1840
+ pv [bucketUUID ] = 2
1841
+ // create a version larger than the allocated version above
1842
+ incomingVersion := uint64 (2 + 10 )
1843
+ incomingHLV := HybridLogicalVector {
1844
+ SourceID : "test" ,
1845
+ Version : incomingVersion ,
1846
+ PreviousVersions : pv ,
1847
+ }
1848
+ // call PutExistingCurrentVersion with empty existing doc
1849
+ doc , cv , _ , err := collection .PutExistingCurrentVersion (ctx , newDoc , incomingHLV , & sgbucket.BucketDocument {})
1850
+ require .NoError (t , err )
1851
+ assert .NotNil (t , doc )
1852
+ // assert on returned CV value
1853
+ assert .Equal (t , "test" , cv .SourceID )
1854
+ assert .Equal (t , incomingVersion , cv .Version )
1855
+ assert .Equal (t , []byte (`{"key1":"value2"}` ), doc ._rawBody )
1856
+
1857
+ // assert on the sync data from the above update to the doc
1858
+ // CV should be equal to CV of update on client but the cvCAS should be updated with the new update and
1859
+ // PV should contain the old CV pair
1860
+ syncData , err := collection .GetDocSyncData (ctx , "doc2" )
1861
+ assert .NoError (t , err )
1862
+ uintCAS := base .HexCasToUint64 (syncData .Cas )
1863
+ assert .Equal (t , "test" , syncData .HLV .SourceID )
1864
+ assert .Equal (t , incomingVersion , syncData .HLV .Version )
1865
+ assert .Equal (t , uintCAS , syncData .HLV .CurrentVersionCAS )
1866
+ // update the pv map so we can assert we have correct pv map in HLV
1867
+ assert .True (t , reflect .DeepEqual (syncData .HLV .PreviousVersions , pv ))
1868
+ assert .Equal (t , "1-3a208ea66e84121b528f05b5457d1134" , syncData .CurrentRev )
1869
+ }
0 commit comments