From 959c4b8a91ec80028d88ebd177a3eb3739e3efb1 Mon Sep 17 00:00:00 2001 From: Tor Colvin Date: Thu, 14 Nov 2024 15:08:30 -0500 Subject: [PATCH] fixes - update CBG ticket names - remove all changes in rest package, create DocMetadata in topology test to include extended version information - created log key VV for testing --- base/log_keys.go | 2 + db/crud.go | 12 +-- rest/api_test.go | 2 +- rest/blip_api_crud_test.go | 10 ++- rest/utilities_testing.go | 31 ++------ rest/utilities_testing_blip_client.go | 23 ++++-- rest/utilities_testing_resttester.go | 16 ++-- topologytest/couchbase_lite_mock_peer_test.go | 18 ++--- topologytest/couchbase_server_peer_test.go | 76 +++++++++---------- topologytest/hlv_test.go | 21 +++-- topologytest/peer_test.go | 13 ++-- topologytest/sync_gateway_peer_test.go | 22 +++--- topologytest/topologies_test.go | 1 + 13 files changed, 120 insertions(+), 127 deletions(-) diff --git a/base/log_keys.go b/base/log_keys.go index 73cb0c60dc..f575495e99 100644 --- a/base/log_keys.go +++ b/base/log_keys.go @@ -53,6 +53,7 @@ const ( KeyReplicate KeySync KeySyncMsg + KeyVV KeyWebSocket KeyWebSocketFrame KeySGTest @@ -87,6 +88,7 @@ var ( KeyReplicate: "Replicate", KeySync: "Sync", KeySyncMsg: "SyncMsg", + KeyVV: "VV", KeyWebSocket: "WS", KeyWebSocketFrame: "WSFrame", KeySGTest: "TEST", diff --git a/db/crud.go b/db/crud.go index 1fdb383c4d..3d86ce3b1a 100644 --- a/db/crud.go +++ b/db/crud.go @@ -901,9 +901,9 @@ func (db *DatabaseCollectionWithUser) updateHLV(ctx context.Context, d *Document hasHLV := d.HLV != nil if d.HLV == nil { d.HLV = &HybridLogicalVector{} - base.DebugfCtx(ctx, base.KeySGTest, "No existing HLV for doc %s", base.UD(d.ID)) + base.DebugfCtx(ctx, base.KeyVV, "No existing HLV for doc %s", base.UD(d.ID)) } else { - base.DebugfCtx(ctx, base.KeySGTest, "Existing HLV for doc %s before modification %+v", base.UD(d.ID), d.HLV) + base.DebugfCtx(ctx, base.KeyVV, "Existing HLV for doc %s before modification %+v", base.UD(d.ID), d.HLV) } switch docUpdateEvent { case ExistingVersion: @@ -925,9 +925,9 @@ func (db *DatabaseCollectionWithUser) updateHLV(ctx context.Context, d *Document return nil, err } d.HLV.CurrentVersionCAS = d.Cas - base.DebugfCtx(ctx, base.KeySGTest, "Adding new version to HLV due to import for doc %s, updated HLV %+v", base.UD(d.ID), d.HLV) + base.DebugfCtx(ctx, base.KeyVV, "Adding new version to HLV due to import for doc %s, updated HLV %+v", base.UD(d.ID), d.HLV) } else { - base.DebugfCtx(ctx, base.KeySGTest, "Not updating HLV to _mou.cas == doc.cas for doc %s, extant HLV %+v", base.UD(d.ID), d.HLV) + base.DebugfCtx(ctx, base.KeyVV, "Not updating HLV to _mou.cas == doc.cas for doc %s, extant HLV %+v", base.UD(d.ID), d.HLV) } case NewVersion, ExistingVersionWithUpdateToHLV: // add a new entry to the version vector @@ -2098,9 +2098,9 @@ func (col *DatabaseCollectionWithUser) documentUpdateFunc( mouMatch := false if doc.MetadataOnlyUpdate != nil && base.HexCasToUint64(doc.MetadataOnlyUpdate.CAS) == doc.Cas { mouMatch = base.HexCasToUint64(doc.MetadataOnlyUpdate.CAS) == doc.Cas - base.DebugfCtx(ctx, base.KeySGTest, "updateDoc(%q): _mou:%+v Metadata-only update match:%t", base.UD(doc.ID), doc.MetadataOnlyUpdate, mouMatch) + base.DebugfCtx(ctx, base.KeyVV, "updateDoc(%q): _mou:%+v Metadata-only update match:%t", base.UD(doc.ID), doc.MetadataOnlyUpdate, mouMatch) } else { - base.DebugfCtx(ctx, base.KeySGTest, "updateDoc(%q): has no _mou", base.UD(doc.ID)) + base.DebugfCtx(ctx, base.KeyVV, "updateDoc(%q): has no _mou", base.UD(doc.ID)) } // Invoke the callback to update the document and with a new revision body to be used by the Sync Function: newDoc, newAttachments, createNewRevIDSkipped, updatedExpiry, err := callback(doc) diff --git a/rest/api_test.go b/rest/api_test.go index 390600414f..bc554c10fe 100644 --- a/rest/api_test.go +++ b/rest/api_test.go @@ -2834,7 +2834,7 @@ func TestPvDeltaReadAndWrite(t *testing.T) { // assert that we have a prev CV drop to pv and a new CV pair, assert pv values are as expected after delta conversions assert.Equal(t, testSource, newDoc.HLV.SourceID) - assert.Equal(t, version2.HLV.Version, newDoc.HLV.Version) + assert.Equal(t, version2.CV.Value, newDoc.HLV.Version) assert.Len(t, newDoc.HLV.PreviousVersions, 1) assert.Equal(t, casV1, newDoc.HLV.PreviousVersions[encodedSourceV1]) diff --git a/rest/blip_api_crud_test.go b/rest/blip_api_crud_test.go index 6922b38cf1..279ecb3cbd 100644 --- a/rest/blip_api_crud_test.go +++ b/rest/blip_api_crud_test.go @@ -2173,7 +2173,7 @@ func TestPullReplicationUpdateOnOtherHLVAwarePeer(t *testing.T) { otherSource := "otherSource" hlvHelper := db.NewHLVAgent(t, rt.GetSingleDataStore(), otherSource, "_vv") existingHLVKey := "doc1" - _ = hlvHelper.InsertWithHLV(ctx, existingHLVKey) + cas := hlvHelper.InsertWithHLV(ctx, existingHLVKey) // force import of this write _, _ = rt.GetDoc(docID) @@ -2181,7 +2181,13 @@ func TestPullReplicationUpdateOnOtherHLVAwarePeer(t *testing.T) { require.NoError(t, err) // create doc version of the above doc write - version1 := DocVersionFromDocument(bucketDoc) + version1 := DocVersion{ + RevTreeID: bucketDoc.CurrentRev, + CV: db.Version{ + SourceID: hlvHelper.Source, + Value: cas, + }, + } _ = btcRunner.WaitForVersion(client.id, docID, version1) diff --git a/rest/utilities_testing.go b/rest/utilities_testing.go index 2d442fc26e..108ee0d2fa 100644 --- a/rest/utilities_testing.go +++ b/rest/utilities_testing.go @@ -1485,7 +1485,7 @@ func createBlipTesterWithSpec(tb testing.TB, spec BlipTesterSpec, rt *RestTester if err != nil { return nil, err } - log.Printf("db:%s Creating user: %v", bt.restTester.GetDatabase().Name, userDocBody) + log.Printf("Creating user: %v", userDocBody) // Create a user. NOTE: this must come *after* the bt.rt.TestPublicHandler() call, otherwise it will end up getting ignored _ = bt.restTester.SendAdminRequest( @@ -2448,15 +2448,12 @@ func WaitAndAssertBackgroundManagerExpiredHeartbeat(t testing.TB, bm *db.Backgro // DocVersion represents a specific version of a document in an revID/HLV agnostic manner. type DocVersion struct { - RevTreeID string // RevTreeID is the rev treee ID of a document, may be empty not present - HLV *db.HybridLogicalVector // HLV is the hybrid logical vector of the document, may not be present - Mou *db.MetadataOnlyUpdate // Mou is the metadata only update of the document, may not be present - Cas uint64 // Cas is the cas value of the document - HasImplicitCV bool // If true, the HLV was constructed from cas@sourceID instead of from _vv, used for documents written to Couchbase Server without Sync Gateway + RevTreeID string + CV db.Version } -func (v DocVersion) String() string { - return fmt.Sprintf("Cas:%d RevTreeID:%s HLV:%+v Mou:%+v HasImplicitCV:%t", v.Cas, v.RevTreeID, v.HLV, v.Mou, v.HasImplicitCV) +func (v *DocVersion) String() string { + return fmt.Sprintf("RevTreeID: %s", v.RevTreeID) } func (v DocVersion) Equal(o DocVersion) bool { @@ -2468,19 +2465,15 @@ func (v DocVersion) Equal(o DocVersion) bool { func (v DocVersion) GetRev(useHLV bool) string { if useHLV { - if v.HLV == nil { + if v.CV.SourceID == "" { return "" } - return v.HLV.GetCurrentVersionString() + return v.CV.String() } else { return v.RevTreeID } } -func (v DocVersion) CV() string { - return v.GetRev(true) -} - // Digest returns the digest for the current version func (v DocVersion) Digest() string { return strings.Split(v.RevTreeID, "-")[1] @@ -2511,16 +2504,6 @@ func NewDocVersionFromFakeRev(fakeRev string) DocVersion { return DocVersion{RevTreeID: fakeRev} } -// DocVersionFromDocument returns a DocVersion from the given document. -func DocVersionFromDocument(doc *db.Document) DocVersion { - return DocVersion{ - RevTreeID: doc.CurrentRev, - Mou: doc.MetadataOnlyUpdate, - Cas: doc.Cas, - HLV: doc.HLV, - } -} - // DocVersionFromPutResponse returns a DocRevisionID from the given response to PUT /{, or fails the given test if a rev ID was not found. func DocVersionFromPutResponse(t testing.TB, response *TestResponse) DocVersion { var r struct { diff --git a/rest/utilities_testing_blip_client.go b/rest/utilities_testing_blip_client.go index 4d87f6b2b2..d433d96ebc 100644 --- a/rest/utilities_testing_blip_client.go +++ b/rest/utilities_testing_blip_client.go @@ -924,7 +924,7 @@ func (btc *BlipTesterCollectionClient) PushRev(docID string, parentVersion DocVe func (btc *BlipTesterCollectionClient) requireRevID(expected DocVersion, revID string) { if btc.UseHLV() { - require.Equal(btc.parent.rt.TB(), expected.CV(), revID) + require.Equal(btc.parent.rt.TB(), expected.CV.String(), revID) } else { require.Equal(btc.parent.rt.TB(), expected.RevTreeID, revID) } @@ -948,7 +948,7 @@ func (btc *BlipTesterClient) GetDocVersion(docID string) DocVersion { if !btc.UseHLV() || doc.HLV == nil { return DocVersion{RevTreeID: doc.CurrentRev} } - return DocVersion{RevTreeID: doc.CurrentRev, HLV: doc.HLV} + return DocVersion{RevTreeID: doc.CurrentRev, CV: db.Version{SourceID: doc.HLV.SourceID, Value: doc.HLV.Version}} } // PushRevWithHistory creates a revision on the client with history, and immediately sends a changes request for it. @@ -1193,7 +1193,7 @@ func (btc *BlipTesterClient) AssertOnBlipHistory(t *testing.T, msg *blip.Message require.NoError(t, err) if subProtocol >= db.CBMobileReplicationV4 { // history could be empty a lot of the time in HLV messages as updates from the same source won't populate previous versions if msg.Properties[db.RevMessageHistory] != "" { - assert.Equal(t, docVersion.CV(), msg.Properties[db.RevMessageHistory]) + assert.Equal(t, docVersion.CV.String(), msg.Properties[db.RevMessageHistory]) } } else { assert.Equal(t, docVersion.RevTreeID, msg.Properties[db.RevMessageHistory]) @@ -1207,7 +1207,7 @@ func (btc *BlipTesterCollectionClient) GetVersion(docID string, docVersion DocVe if doc, ok := btc.docs[docID]; ok { if doc.revMode == revModeHLV { - if doc.getCurrentRevID() == docVersion.CV() { + if doc.getCurrentRevID() == docVersion.CV.String() { return doc.body, true } } else { @@ -1306,7 +1306,7 @@ func (btr *BlipTesterReplicator) storeMessage(msg *blip.Message) { func (btc *BlipTesterCollectionClient) WaitForBlipRevMessage(docID string, version DocVersion) (msg *blip.Message) { var revID string if btc.UseHLV() { - revID = version.CV() + revID = version.CV.String() } else { revID = version.RevTreeID } @@ -1455,9 +1455,16 @@ func (btc *BlipTesterCollectionClient) sendPushMsg(msg *blip.Message) error { // PutDoc will upsert the document with a given contents. func (btc *BlipTesterClient) PutDoc(docID string, body string) DocVersion { rt := btc.rt - var unmarshalledBody db.Body - require.NoError(rt.TB(), base.JSONUnmarshal([]byte(body), &unmarshalledBody)) - return rt.PutDocDirectly(docID, unmarshalledBody) + version := rt.PutDoc(docID, body) + if btc.UseHLV() { + collection, _ := rt.GetSingleTestDatabaseCollection() + source, value := collection.GetDocumentCurrentVersion(rt.TB(), docID) + version.CV = db.Version{ + SourceID: source, + Value: value, + } + } + return version } // RequireRev checks the current rev for the specified docID on the backend the BTC is replicating diff --git a/rest/utilities_testing_resttester.go b/rest/utilities_testing_resttester.go index f990b74b80..07dc7e56e5 100644 --- a/rest/utilities_testing_resttester.go +++ b/rest/utilities_testing_resttester.go @@ -419,25 +419,25 @@ func (rt *RestTester) RequireDbOnline() { // TEMPORARY HELPER METHODS FOR BLIP TEST CLIENT RUNNER func (rt *RestTester) PutDocDirectly(docID string, body db.Body) DocVersion { collection, ctx := rt.GetSingleTestDatabaseCollectionWithUser() - _, doc, err := collection.Put(ctx, docID, body) + rev, doc, err := collection.Put(ctx, docID, body) require.NoError(rt.TB(), err) - return DocVersionFromDocument(doc) + return DocVersion{RevTreeID: rev, CV: db.Version{SourceID: doc.HLV.SourceID, Value: doc.HLV.Version}} } func (rt *RestTester) UpdateDocDirectly(docID string, version DocVersion, body db.Body) DocVersion { collection, ctx := rt.GetSingleTestDatabaseCollectionWithUser() body[db.BodyId] = docID body[db.BodyRev] = version.RevTreeID - _, doc, err := collection.Put(ctx, docID, body) + rev, doc, err := collection.Put(ctx, docID, body) require.NoError(rt.TB(), err) - return DocVersionFromDocument(doc) + return DocVersion{RevTreeID: rev, CV: db.Version{SourceID: doc.HLV.SourceID, Value: doc.HLV.Version}} } func (rt *RestTester) DeleteDocDirectly(docID string, version DocVersion) DocVersion { collection, ctx := rt.GetSingleTestDatabaseCollectionWithUser() - _, doc, err := collection.DeleteDoc(ctx, docID, version.RevTreeID) + rev, doc, err := collection.DeleteDoc(ctx, docID, version.RevTreeID) require.NoError(rt.TB(), err) - return DocVersionFromDocument(doc) + return DocVersion{RevTreeID: rev, CV: db.Version{SourceID: doc.HLV.SourceID, Value: doc.HLV.Version}} } func (rt *RestTester) PutDocDirectlyInCollection(collection *db.DatabaseCollection, docID string, body db.Body) DocVersion { @@ -445,9 +445,9 @@ func (rt *RestTester) PutDocDirectlyInCollection(collection *db.DatabaseCollecti DatabaseCollection: collection, } ctx := base.UserLogCtx(collection.AddCollectionContext(rt.Context()), "gotest", base.UserDomainBuiltin, nil) - _, doc, err := dbUser.Put(ctx, docID, body) + rev, doc, err := dbUser.Put(ctx, docID, body) require.NoError(rt.TB(), err) - return DocVersionFromDocument(doc) + return DocVersion{RevTreeID: rev, CV: db.Version{SourceID: doc.HLV.SourceID, Value: doc.HLV.Version}} } // PutDocWithAttachment will upsert the document with a given contents and attachments. diff --git a/topologytest/couchbase_lite_mock_peer_test.go b/topologytest/couchbase_lite_mock_peer_test.go index f45aea27ed..3895aeb18d 100644 --- a/topologytest/couchbase_lite_mock_peer_test.go +++ b/topologytest/couchbase_lite_mock_peer_test.go @@ -43,9 +43,9 @@ func (p *CouchbaseLiteMockPeer) String() string { } // GetDocument returns the latest version of a document. The test will fail the document does not exist. -func (p *CouchbaseLiteMockPeer) GetDocument(_ sgbucket.DataStoreName, _ string) (rest.DocVersion, db.Body) { +func (p *CouchbaseLiteMockPeer) GetDocument(_ sgbucket.DataStoreName, _ string) (DocMetadata, db.Body) { // this isn't yet collection aware, using single default collection - return rest.EmptyDocVersion(), nil + return DocMetadata{}, nil } // getSingleBlipClient returns the single blip client for the peer. If there are multiple clients, or not clients it will fail the test. This is temporary to stub support for multiple Sync Gateway peers. @@ -62,28 +62,28 @@ func (p *CouchbaseLiteMockPeer) getSingleBlipClient() *PeerBlipTesterClient { } // CreateDocument creates a document on the peer. The test will fail if the document already exists. -func (p *CouchbaseLiteMockPeer) CreateDocument(dsName sgbucket.DataStoreName, docID string, body []byte) rest.DocVersion { +func (p *CouchbaseLiteMockPeer) CreateDocument(dsName sgbucket.DataStoreName, docID string, body []byte) DocMetadata { p.t.Logf("%s: Creating document %s", p, docID) return p.WriteDocument(dsName, docID, body) } // WriteDocument writes a document to the peer. The test will fail if the write does not succeed. -func (p *CouchbaseLiteMockPeer) WriteDocument(_ sgbucket.DataStoreName, docID string, body []byte) rest.DocVersion { +func (p *CouchbaseLiteMockPeer) WriteDocument(_ sgbucket.DataStoreName, docID string, body []byte) DocMetadata { // this isn't yet collection aware, using single default collection client := p.getSingleBlipClient() // set an HLV here. docVersion, err := client.btcRunner.PushRev(client.ID(), docID, rest.EmptyDocVersion(), body) require.NoError(client.btcRunner.TB(), err) - return docVersion + return DocMetadataFromDocVersion(docID, docVersion) } // DeleteDocument deletes a document on the peer. The test will fail if the document does not exist. -func (p *CouchbaseLiteMockPeer) DeleteDocument(sgbucket.DataStoreName, string) rest.DocVersion { - return rest.EmptyDocVersion() +func (p *CouchbaseLiteMockPeer) DeleteDocument(sgbucket.DataStoreName, string) DocMetadata { + return DocMetadata{} } // WaitForDocVersion waits for a document to reach a specific version. The test will fail if the document does not reach the expected version in 20s. -func (p *CouchbaseLiteMockPeer) WaitForDocVersion(_ sgbucket.DataStoreName, docID string, _ rest.DocVersion) db.Body { +func (p *CouchbaseLiteMockPeer) WaitForDocVersion(_ sgbucket.DataStoreName, docID string, _ DocMetadata) db.Body { // this isn't yet collection aware, using single default collection client := p.getSingleBlipClient() // FIXME: waiting for a specific version isn't working yet. @@ -99,7 +99,7 @@ func (p *CouchbaseLiteMockPeer) WaitForDeletion(_ sgbucket.DataStoreName, _ stri } // WaitForTombstoneVersion waits for a document to reach a specific version, this must be a tombstone. The test will fail if the document does not reach the expected version in 20s. -func (p *CouchbaseLiteMockPeer) WaitForTombstoneVersion(_ sgbucket.DataStoreName, _ string, _ rest.DocVersion) { +func (p *CouchbaseLiteMockPeer) WaitForTombstoneVersion(_ sgbucket.DataStoreName, _ string, _ DocMetadata) { require.Fail(p.TB(), "WaitForTombstoneVersion not yet implemented CBG-4257") } diff --git a/topologytest/couchbase_server_peer_test.go b/topologytest/couchbase_server_peer_test.go index fa1c254ad8..b9880ebcbf 100644 --- a/topologytest/couchbase_server_peer_test.go +++ b/topologytest/couchbase_server_peer_test.go @@ -18,7 +18,6 @@ import ( sgbucket "github.com/couchbase/sg-bucket" "github.com/couchbase/sync_gateway/base" "github.com/couchbase/sync_gateway/db" - "github.com/couchbase/sync_gateway/rest" "github.com/couchbase/sync_gateway/xdcr" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -79,12 +78,12 @@ func (p *CouchbaseServerPeer) getCollection(dsName sgbucket.DataStoreName) sgbuc } // GetDocument returns the latest version of a document. The test will fail the document does not exist. -func (p *CouchbaseServerPeer) GetDocument(dsName sgbucket.DataStoreName, docID string) (rest.DocVersion, db.Body) { +func (p *CouchbaseServerPeer) GetDocument(dsName sgbucket.DataStoreName, docID string) (DocMetadata, db.Body) { return getBodyAndVersion(p, p.getCollection(dsName), docID) } // CreateDocument creates a document on the peer. The test will fail if the document already exists. -func (p *CouchbaseServerPeer) CreateDocument(dsName sgbucket.DataStoreName, docID string, body []byte) rest.DocVersion { +func (p *CouchbaseServerPeer) CreateDocument(dsName sgbucket.DataStoreName, docID string, body []byte) DocMetadata { p.tb.Logf("%s: Creating document %s", p, docID) // create document with xattrs to prevent XDCR from doing a round trip replication in this scenario: // CBS1: write document (cas1, no _vv) @@ -92,19 +91,18 @@ func (p *CouchbaseServerPeer) CreateDocument(dsName sgbucket.DataStoreName, docI // CBS2->CBS1: XDCR replication, creates a new _vv cas, err := p.getCollection(dsName).WriteWithXattrs(p.Context(), docID, 0, 0, body, map[string][]byte{"userxattr": []byte(`{"dummy": "xattr"}`)}, nil, nil) require.NoError(p.tb, err) - return rest.DocVersion{ - HLV: &db.HybridLogicalVector{ - SourceID: p.SourceID(), - Version: cas, - CurrentVersionCAS: cas, + return DocMetadata{ + DocID: docID, + Cas: cas, + ImplicitCV: &db.Version{ + SourceID: p.SourceID(), + Value: cas, }, - Cas: cas, - HasImplicitCV: true, } } // WriteDocument writes a document to the peer. The test will fail if the write does not succeed. -func (p *CouchbaseServerPeer) WriteDocument(dsName sgbucket.DataStoreName, docID string, body []byte) rest.DocVersion { +func (p *CouchbaseServerPeer) WriteDocument(dsName sgbucket.DataStoreName, docID string, body []byte) DocMetadata { p.tb.Logf("%s: Writing document %s", p, docID) // write the document LWW, ignoring any in progress writes callback := func(_ []byte) (updated []byte, expiry *uint32, shouldDelete bool, err error) { @@ -112,38 +110,37 @@ func (p *CouchbaseServerPeer) WriteDocument(dsName sgbucket.DataStoreName, docID } cas, err := p.getCollection(dsName).Update(docID, 0, callback) require.NoError(p.tb, err) - return rest.DocVersion{ + return DocMetadata{ + DocID: docID, // FIXME: this should actually probably show the HLV persisted, and then also the implicit CV - HLV: &db.HybridLogicalVector{ - SourceID: p.SourceID(), - Version: cas, - CurrentVersionCAS: cas, + Cas: cas, + ImplicitCV: &db.Version{ + SourceID: p.SourceID(), + Value: cas, }, - HasImplicitCV: true, - Cas: cas, } } // DeleteDocument deletes a document on the peer. The test will fail if the document does not exist. -func (p *CouchbaseServerPeer) DeleteDocument(dsName sgbucket.DataStoreName, docID string) rest.DocVersion { +func (p *CouchbaseServerPeer) DeleteDocument(dsName sgbucket.DataStoreName, docID string) DocMetadata { // delete the document, ignoring any in progress writes. We are allowed to delete a document that does not exist. callback := func(_ []byte) (updated []byte, expiry *uint32, shouldDelete bool, err error) { return nil, nil, true, nil } cas, err := p.getCollection(dsName).Update(docID, 0, callback) require.NoError(p.tb, err) - return rest.DocVersion{ - HLV: &db.HybridLogicalVector{ - SourceID: p.SourceID(), - Version: cas, - CurrentVersionCAS: cas, + return DocMetadata{ + DocID: docID, + Cas: cas, + ImplicitCV: &db.Version{ + SourceID: p.SourceID(), + Value: cas, }, - HasImplicitCV: true, } } // WaitForDocVersion waits for a document to reach a specific version. The test will fail if the document does not reach the expected version in 20s. -func (p *CouchbaseServerPeer) WaitForDocVersion(dsName sgbucket.DataStoreName, docID string, expected rest.DocVersion) db.Body { +func (p *CouchbaseServerPeer) WaitForDocVersion(dsName sgbucket.DataStoreName, docID string, expected DocMetadata) db.Body { docBytes := p.waitForDocVersion(dsName, docID, expected) var body db.Body require.NoError(p.tb, base.JSONUnmarshal(docBytes, &body), "couldn't unmarshal docID %s: %s", docID, docBytes) @@ -159,15 +156,15 @@ func (p *CouchbaseServerPeer) WaitForDeletion(dsName sgbucket.DataStoreName, doc } // WaitForTombstoneVersion waits for a document to reach a specific version, this must be a tombstone. The test will fail if the document does not reach the expected version in 20s. -func (p *CouchbaseServerPeer) WaitForTombstoneVersion(dsName sgbucket.DataStoreName, docID string, expected rest.DocVersion) { +func (p *CouchbaseServerPeer) WaitForTombstoneVersion(dsName sgbucket.DataStoreName, docID string, expected DocMetadata) { docBytes := p.waitForDocVersion(dsName, docID, expected) require.Nil(p.tb, docBytes, "expected tombstone for docID %s, got %s", docID, docBytes) } // waitForDocVersion waits for a document to reach a specific version and returns the body in bytes. The bytes will be nil if the document is a tombstone. The test will fail if the document does not reach the expected version in 20s. -func (p *CouchbaseServerPeer) waitForDocVersion(dsName sgbucket.DataStoreName, docID string, expected rest.DocVersion) []byte { +func (p *CouchbaseServerPeer) waitForDocVersion(dsName sgbucket.DataStoreName, docID string, expected DocMetadata) []byte { var docBytes []byte - var version rest.DocVersion + var version DocMetadata require.EventuallyWithT(p.tb, func(c *assert.CollectT) { var err error var xattrs map[string][]byte @@ -177,7 +174,7 @@ func (p *CouchbaseServerPeer) waitForDocVersion(dsName sgbucket.DataStoreName, d return } // have to use p.tb instead of c because of the assert.CollectT doesn't implement TB - version = getDocVersion(p, cas, xattrs) + version = getDocVersion(docID, p, cas, xattrs) assert.Equal(c, expected.CV(), version.CV(), "Could not find matching CV on %s for peer %s (sourceID:%s)\nexpected: %+v\nactual: %+v\n body: %+v\n", docID, p, p.SourceID(), expected, version, string(docBytes)) }, 5*time.Second, 100*time.Millisecond) @@ -257,9 +254,10 @@ func (p *CouchbaseServerPeer) TB() testing.TB { } // getDocVersion returns a DocVersion from a cas and xattrs with _vv (hlv) and _sync (RevTreeID). -func getDocVersion(peer Peer, cas uint64, xattrs map[string][]byte) rest.DocVersion { - docVersion := rest.DocVersion{ - Cas: cas, +func getDocVersion(docID string, peer Peer, cas uint64, xattrs map[string][]byte) DocMetadata { + docVersion := DocMetadata{ + DocID: docID, + Cas: cas, } mouBytes, ok := xattrs[base.MouXattrName] if ok { @@ -269,12 +267,10 @@ func getDocVersion(peer Peer, cas uint64, xattrs map[string][]byte) rest.DocVers if ok { require.NoError(peer.TB(), json.Unmarshal(hlvBytes, &docVersion.HLV)) } else { - docVersion.HLV = &db.HybridLogicalVector{ - SourceID: peer.SourceID(), - Version: cas, - CurrentVersionCAS: cas, + docVersion.ImplicitCV = &db.Version{ + SourceID: peer.SourceID(), + Value: cas, } - docVersion.HasImplicitCV = true } sync, ok := xattrs[base.SyncXattrName] if ok { @@ -286,11 +282,11 @@ func getDocVersion(peer Peer, cas uint64, xattrs map[string][]byte) rest.DocVers } // getBodyAndVersion returns the body and version of a document from a sgbucket.DataStore. -func getBodyAndVersion(peer Peer, collection sgbucket.DataStore, docID string) (rest.DocVersion, db.Body) { +func getBodyAndVersion(peer Peer, collection sgbucket.DataStore, docID string) (DocMetadata, db.Body) { docBytes, xattrs, cas, err := collection.GetWithXattrs(peer.Context(), docID, []string{base.VvXattrName}) require.NoError(peer.TB(), err) // get hlv to construct DocVersion var body db.Body require.NoError(peer.TB(), base.JSONUnmarshal(docBytes, &body)) - return getDocVersion(peer, cas, xattrs), body + return getDocVersion(docID, peer, cas, xattrs), body } diff --git a/topologytest/hlv_test.go b/topologytest/hlv_test.go index 5c0ab307f5..1693fbebbf 100644 --- a/topologytest/hlv_test.go +++ b/topologytest/hlv_test.go @@ -15,7 +15,6 @@ import ( "github.com/couchbase/sync_gateway/base" "github.com/couchbase/sync_gateway/db" - "github.com/couchbase/sync_gateway/rest" "golang.org/x/exp/maps" "github.com/stretchr/testify/require" @@ -68,7 +67,7 @@ func getSingleActorTestCase() []singleActorTest { // TestHLVCreateDocumentSingleActor tests creating a document with a single actor in different topologies. func TestHLVCreateDocumentSingleActor(t *testing.T) { - base.SetUpTestLogging(t, base.LevelDebug, base.KeyCRUD, base.KeyImport, base.KeySGTest) + base.SetUpTestLogging(t, base.LevelDebug, base.KeyCRUD, base.KeyImport, base.KeyVV) for _, tc := range getSingleActorTestCase() { t.Run(tc.description(), func(t *testing.T) { peers, _ := setupTests(t, tc.topology, tc.activePeerID) @@ -83,14 +82,14 @@ func TestHLVCreateDocumentSingleActor(t *testing.T) { // TestHLVUpdateDocumentSingleActor tests creating a document with a single actor in different topologies. func TestHLVUpdateDocumentSingleActor(t *testing.T) { - base.SetUpTestLogging(t, base.LevelDebug, base.KeyCRUD, base.KeyImport, base.KeySGTest) + base.SetUpTestLogging(t, base.LevelDebug, base.KeyCRUD, base.KeyImport, base.KeyVV) for _, tc := range getSingleActorTestCase() { t.Run(tc.description(), func(t *testing.T) { if strings.HasPrefix(tc.activePeerID, "cbl") { - t.Skip("Skipping Couchbase Lite test, returns unexpected body in proposeChanges: [304], CBG-4335") + t.Skip("Skipping Couchbase Lite test, returns unexpected body in proposeChanges: [304], CBG-4257") } if base.UnitTestUrlIsWalrus() { - t.Skip("rosmar failure to investigate CBG-4329") + t.Skip("rosmar failure CBG-4365") } peers, _ := setupTests(t, tc.topology, tc.activePeerID) @@ -111,11 +110,11 @@ func TestHLVUpdateDocumentSingleActor(t *testing.T) { // TestHLVDeleteDocumentSingleActor tests creating a document with a single actor in different topologies. func TestHLVDeleteDocumentSingleActor(t *testing.T) { - base.SetUpTestLogging(t, base.LevelDebug, base.KeyImport, base.KeySGTest) + base.SetUpTestLogging(t, base.LevelDebug, base.KeyImport, base.KeyVV) for _, tc := range getSingleActorTestCase() { t.Run(tc.description(), func(t *testing.T) { if strings.HasPrefix(tc.activePeerID, "cbl") { - t.Skip("Skipping Couchbase Lite test, does not know how to push a deletion yet") + t.Skip("Skipping Couchbase Lite test, does not know how to push a deletion yet CBG-4257") } peers, _ := setupTests(t, tc.topology, tc.activePeerID) @@ -135,13 +134,13 @@ func TestHLVDeleteDocumentSingleActor(t *testing.T) { // TestHLVResurrectDocumentSingleActor tests resurrect a document with a single actor in different topologies. func TestHLVResurrectDocumentSingleActor(t *testing.T) { - base.SetUpTestLogging(t, base.LevelDebug, base.KeyImport, base.KeySGTest) + base.SetUpTestLogging(t, base.LevelDebug, base.KeyImport, base.KeyVV) for _, tc := range getSingleActorTestCase() { t.Run(tc.description(), func(t *testing.T) { if strings.HasPrefix(tc.activePeerID, "cbl") { - t.Skip("Skipping Couchbase Lite test, does not know how to push a deletion yet") + t.Skip("Skipping Couchbase Lite test, does not know how to push a deletion yet CBG-4257") } - t.Skip("Skipping Sync Gateway test for rosmar, intermittent failures CBG-4239") + t.Skip("Skipping ressurection tests CBG-4366") peers, _ := setupTests(t, tc.topology, tc.activePeerID) @@ -183,7 +182,7 @@ func stripInternalProperties(body db.Body) { delete(body, "_id") } -func waitForVersionAndBody(t *testing.T, testCase singleActorTest, peers map[string]Peer, expectedVersion rest.DocVersion, expectedBody []byte) { +func waitForVersionAndBody(t *testing.T, testCase singleActorTest, peers map[string]Peer, expectedVersion DocMetadata, expectedBody []byte) { // sort peer names to make tests more deterministic peerNames := maps.Keys(peers) for _, peerName := range peerNames { diff --git a/topologytest/peer_test.go b/topologytest/peer_test.go index 85543ea6d8..21fc811fb5 100644 --- a/topologytest/peer_test.go +++ b/topologytest/peer_test.go @@ -17,7 +17,6 @@ import ( sgbucket "github.com/couchbase/sg-bucket" "github.com/couchbase/sync_gateway/base" "github.com/couchbase/sync_gateway/db" - "github.com/couchbase/sync_gateway/rest" "github.com/couchbase/sync_gateway/xdcr" "github.com/stretchr/testify/require" ) @@ -25,22 +24,22 @@ import ( // Peer represents a peer in an Mobile workflow. The types of Peers are Couchbase Server, Sync Gateway, or Couchbase Lite. type Peer interface { // GetDocument returns the latest version of a document. The test will fail the document does not exist. - GetDocument(dsName sgbucket.DataStoreName, docID string) (rest.DocVersion, db.Body) + GetDocument(dsName sgbucket.DataStoreName, docID string) (DocMetadata, db.Body) // CreateDocument creates a document on the peer. The test will fail if the document already exists. - CreateDocument(dsName sgbucket.DataStoreName, docID string, body []byte) rest.DocVersion + CreateDocument(dsName sgbucket.DataStoreName, docID string, body []byte) DocMetadata // WriteDocument upserts a document to the peer. The test will fail if the write does not succeed. Reasons for failure might be sync function rejections for Sync Gateway rejections. - WriteDocument(dsName sgbucket.DataStoreName, docID string, body []byte) rest.DocVersion + WriteDocument(dsName sgbucket.DataStoreName, docID string, body []byte) DocMetadata // DeleteDocument deletes a document on the peer. The test will fail if the document does not exist. - DeleteDocument(dsName sgbucket.DataStoreName, docID string) rest.DocVersion + DeleteDocument(dsName sgbucket.DataStoreName, docID string) DocMetadata // WaitForDocVersion waits for a document to reach a specific version. Returns the state of the document at that version. The test will fail if the document does not reach the expected version in 20s. - WaitForDocVersion(dsName sgbucket.DataStoreName, docID string, expected rest.DocVersion) db.Body + WaitForDocVersion(dsName sgbucket.DataStoreName, docID string, expected DocMetadata) db.Body // WaitForDeletion waits for a document to be deleted. This document must be a tombstone. The test will fail if the document still exists after 20s. WaitForDeletion(dsName sgbucket.DataStoreName, docID string) // WaitForTombstoneVersion waits for a document to reach a specific version. This document must be a tombstone. The test will fail if the document does not reach the expected version in 20s. - WaitForTombstoneVersion(dsName sgbucket.DataStoreName, docID string, expected rest.DocVersion) + WaitForTombstoneVersion(dsName sgbucket.DataStoreName, docID string, expected DocMetadata) // RequireDocNotFound asserts that a document does not exist on the peer. RequireDocNotFound(dsName sgbucket.DataStoreName, docID string) diff --git a/topologytest/sync_gateway_peer_test.go b/topologytest/sync_gateway_peer_test.go index 11bfef7097..2386e19d0e 100644 --- a/topologytest/sync_gateway_peer_test.go +++ b/topologytest/sync_gateway_peer_test.go @@ -57,21 +57,21 @@ func (p *SyncGatewayPeer) getCollection(dsName sgbucket.DataStoreName) (*db.Data } // GetDocument returns the latest version of a document. The test will fail the document does not exist. -func (p *SyncGatewayPeer) GetDocument(dsName sgbucket.DataStoreName, docID string) (rest.DocVersion, db.Body) { +func (p *SyncGatewayPeer) GetDocument(dsName sgbucket.DataStoreName, docID string) (DocMetadata, db.Body) { collection, ctx := p.getCollection(dsName) doc, err := collection.GetDocument(ctx, docID, db.DocUnmarshalAll) require.NoError(p.TB(), err) - return rest.DocVersionFromDocument(doc), doc.Body(ctx) + return DocMetadataFromDocument(doc), doc.Body(ctx) } // CreateDocument creates a document on the peer. The test will fail if the document already exists. -func (p *SyncGatewayPeer) CreateDocument(dsName sgbucket.DataStoreName, docID string, body []byte) rest.DocVersion { +func (p *SyncGatewayPeer) CreateDocument(dsName sgbucket.DataStoreName, docID string, body []byte) DocMetadata { p.TB().Logf("%s: Creating document %s", p, docID) return p.WriteDocument(dsName, docID, body) } // writeDocument writes a document to the peer. The test will fail if the write does not succeed. -func (p *SyncGatewayPeer) writeDocument(dsName sgbucket.DataStoreName, docID string, body []byte) rest.DocVersion { +func (p *SyncGatewayPeer) writeDocument(dsName sgbucket.DataStoreName, docID string, body []byte) DocMetadata { collection, ctx := p.getCollection(dsName) var doc *db.Document @@ -97,17 +97,17 @@ func (p *SyncGatewayPeer) writeDocument(dsName sgbucket.DataStoreName, docID str return false, nil, nil }, base.CreateSleeperFunc(5, 100)) require.NoError(p.TB(), err) - return rest.DocVersionFromDocument(doc) + return DocMetadataFromDocument(doc) } // WriteDocument writes a document to the peer. The test will fail if the write does not succeed. -func (p *SyncGatewayPeer) WriteDocument(dsName sgbucket.DataStoreName, docID string, body []byte) rest.DocVersion { +func (p *SyncGatewayPeer) WriteDocument(dsName sgbucket.DataStoreName, docID string, body []byte) DocMetadata { p.TB().Logf("%s: Writing document %s", p, docID) return p.writeDocument(dsName, docID, body) } // DeleteDocument deletes a document on the peer. The test will fail if the document does not exist. -func (p *SyncGatewayPeer) DeleteDocument(dsName sgbucket.DataStoreName, docID string) rest.DocVersion { +func (p *SyncGatewayPeer) DeleteDocument(dsName sgbucket.DataStoreName, docID string) DocMetadata { collection, ctx := p.getCollection(dsName) doc, err := collection.GetDocument(ctx, docID, db.DocUnmarshalAll) var revID string @@ -116,11 +116,11 @@ func (p *SyncGatewayPeer) DeleteDocument(dsName sgbucket.DataStoreName, docID st } _, doc, err = collection.DeleteDoc(ctx, docID, revID) require.NoError(p.TB(), err) - return rest.DocVersionFromDocument(doc) + return DocMetadataFromDocument(doc) } // WaitForDocVersion waits for a document to reach a specific version. The test will fail if the document does not reach the expected version in 20s. -func (p *SyncGatewayPeer) WaitForDocVersion(dsName sgbucket.DataStoreName, docID string, expected rest.DocVersion) db.Body { +func (p *SyncGatewayPeer) WaitForDocVersion(dsName sgbucket.DataStoreName, docID string, expected DocMetadata) db.Body { collection, ctx := p.getCollection(dsName) var doc *db.Document require.EventuallyWithT(p.TB(), func(c *assert.CollectT) { @@ -130,7 +130,7 @@ func (p *SyncGatewayPeer) WaitForDocVersion(dsName sgbucket.DataStoreName, docID if doc == nil { return } - version := rest.DocVersionFromDocument(doc) + version := DocMetadataFromDocument(doc) // Only assert on CV since RevTreeID might not be present if this was a Couchbase Server write bodyBytes, err := doc.BodyBytes(ctx) assert.NoError(c, err) @@ -153,7 +153,7 @@ func (p *SyncGatewayPeer) WaitForDeletion(dsName sgbucket.DataStoreName, docID s } // WaitForTombstoneVersion waits for a document to reach a specific version, this must be a tombstone. The test will fail if the document does not reach the expected version in 20s. -func (p *SyncGatewayPeer) WaitForTombstoneVersion(dsName sgbucket.DataStoreName, docID string, expected rest.DocVersion) { +func (p *SyncGatewayPeer) WaitForTombstoneVersion(dsName sgbucket.DataStoreName, docID string, expected DocMetadata) { docBytes := p.WaitForDocVersion(dsName, docID, expected) require.Nil(p.TB(), docBytes, "expected tombstone for docID %s, got %s", docID, docBytes) } diff --git a/topologytest/topologies_test.go b/topologytest/topologies_test.go index 706d016a8f..9a9055c996 100644 --- a/topologytest/topologies_test.go +++ b/topologytest/topologies_test.go @@ -23,6 +23,7 @@ type Topology struct { skipIf func(t *testing.T, activePeerID string, peers map[string]Peer) // allow temporary skips while the code is being ironed out } +// PeerNames returns a sorted list of peers. func (t Topology) PeerNames() []string { peerNames := maps.Keys(t.peers) slices.Sort(peerNames)