Skip to content

Commit 63d0134

Browse files
committed
update tests to use blip tester client runner
1 parent 9412167 commit 63d0134

2 files changed

Lines changed: 159 additions & 164 deletions

File tree

rest/blip_legacy_revid_test.go

Lines changed: 154 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -660,118 +660,76 @@ func TestChangesResponseLegacyRev(t *testing.T) {
660660
// Setup:
661661
// - Create doc rev1 on SGW
662662
// - Update doc to create rev2 on SGW
663-
// - Client connects with V4 subprotocol, delta sync enabled
664-
// - Custom changes handler responds with [rev1ID] as knownRevs (simulating a client that only has a legacy rev revision 1)
665-
// - Server computes delta from rev1 → rev2 and sends via sendDelta with remoteIsLegacyRev=true
663+
// - Client pre-populated with rev1 as a legacy revtree revision (no HLV)
664+
// - Client pulls with delta sync enabled
665+
// - Server computes delta from rev1 → rev2 and sends via sendDelta
666666
//
667667
// Expected rev message (buildRevHistory scenario 3):
668668
//
669-
// history property: [hlvHistory, rev2RevTreeID, rev1RevTreeID] (hlv history here is empty since doc has only been updated by one hlv aware peer)
669+
// history property: [hlvHistory, rev2RevTreeID, rev1RevTreeID] (no hlv pv here since doc has only been updated by one hlv aware peer)
670670
// deltaSrc: rev1RevTreeID
671671
// body: the delta (not full body)
672-
func TestDeltaSyncSendHistoryWithLegacyClient(t *testing.T) {
672+
func TestDeltaSyncSendRevTreeHistoryWithClientHavingLegacyRev(t *testing.T) {
673673
if !base.IsEnterpriseEdition() {
674674
t.Skip("Delta sync requires EE")
675675
}
676-
base.SetUpTestLogging(t, base.LevelDebug, base.KeyHTTP, base.KeySync, base.KeySyncMsg, base.KeyChanges)
676+
677+
btcRunner := NewBlipTesterClientRunner(t)
678+
btcRunner.SkipSubtest[RevtreeSubtestName] = true // V4-specific: tests legacy rev in HLV-aware client
677679

678680
sgUseDeltas := true
679-
rt := NewRestTester(t, &RestTesterConfig{
680-
GuestEnabled: true,
681-
DatabaseConfig: &DatabaseConfig{DbConfig: DbConfig{
682-
DeltaSync: &DeltaSyncConfig{
683-
Enabled: &sgUseDeltas,
684-
},
685-
}},
686-
})
687-
defer rt.Close()
681+
btcRunner.Run(func(t *testing.T) {
682+
rt := NewRestTester(t, &RestTesterConfig{
683+
GuestEnabled: true,
684+
DatabaseConfig: &DatabaseConfig{DbConfig: DbConfig{
685+
DeltaSync: &DeltaSyncConfig{
686+
Enabled: &sgUseDeltas,
687+
},
688+
}},
689+
})
690+
defer rt.Close()
688691

689-
bt := NewBlipTesterFromSpecWithRT(rt, &BlipTesterSpec{
690-
blipProtocols: []string{db.CBMobileReplicationV4.SubprotocolString()},
691-
})
692-
defer bt.Close()
692+
docID := SafeDocumentName(t, t.Name())
693693

694-
// Create rev1 and rev2 on SGW
695-
docVersion1 := rt.PutDoc("doc1", `{"key": "val1"}`)
696-
rev1ID := docVersion1.RevTreeID
697-
docVersion2 := rt.UpdateDoc("doc1", docVersion1, `{"key": "val2"}`)
698-
rt.WaitForPendingChanges()
694+
client := btcRunner.NewBlipTesterClientOptsWithRT(rt, &BlipTesterClientOpts{
695+
ClientDeltas: true,
696+
})
697+
defer client.Close()
699698

700-
receivedChangesRequestWg := sync.WaitGroup{}
701-
receivedChangesRequestWg.Add(2) // 1 for doc changes + 1 for empty "caught up" changes
699+
// Create rev1 and rev2 on SGW
700+
docVersion1 := rt.PutDoc(docID, `{"key": "val1"}`)
701+
rev1ID := docVersion1.RevTreeID
702+
docVersion2 := rt.UpdateDoc(docID, docVersion1, `{"key": "val2"}`)
703+
rt.WaitForPendingChanges()
702704

703-
revsFinishedWg := sync.WaitGroup{}
704-
revsFinishedWg.Add(1) // expect 1 rev/delta message
705+
// Pre-populate the client with rev1 as a legacy revtree revision so that when SGW sends changes
706+
// for rev2, the client reports rev1ID as its known rev (triggering delta from rev1).
707+
btcRunner.AddRevTreeRev(client.id, docID, rev1ID, EmptyDocVersion(), []byte(`{"key": "val1"}`))
705708

706-
bt.blipContext.HandlerForProfile["rev"] = func(request *blip.Message) {
707-
defer revsFinishedWg.Done()
709+
btcRunner.StartOneshotPull(client.id)
710+
msg := btcRunner.WaitForPullRevMessage(client.id, docID, docVersion2)
708711

709712
// Should be sent as delta with rev1 as the delta source
710-
deltaSrc := request.Properties[db.RevMessageDeltaSrc]
711-
assert.Equal(t, rev1ID, deltaSrc, "delta source should be the legacy revID the client reported")
713+
assert.Equal(t, rev1ID, msg.Properties[db.RevMessageDeltaSrc], "delta source should be the legacy revID the client reported")
712714

713715
// The rev property should be the CV (HLV-aware identifier)
714-
rev := request.Properties["rev"]
715-
assert.Equal(t, docVersion2.CV.String(), rev)
716+
assert.Equal(t, docVersion2.CV.String(), msg.Properties[db.RevMessageRev])
716717

717-
// History should contain: HLV history, then rev2's revTreeID, then rev1's revTreeID
718-
// (scenario 3 in buildRevHistory: local HLV-aware, remote legacy)
719-
history := request.Properties[db.RevMessageHistory]
718+
// History should contain rev2's revTreeID then rev1's revTreeID
719+
// (scenario 3 in buildRevHistory: local HLV-aware, remote legacy; no HLV pv since same source)
720+
history := msg.Properties[db.RevMessageHistory]
720721
require.NotEmpty(t, history, "history must not be empty — rev tree history is required for legacy client conflict detection")
721722
historyList := strings.Split(history, ",")
722-
// The history should be the rev tree: current revID then parent revID
723-
require.Len(t, historyList, 2, "history should rev tree entries only since hlv history is empty for this doc")
724-
assert.Equal(t, docVersion2.RevTreeID, historyList[len(historyList)-2], "second to last history entry should be current revTreeID")
725-
assert.Equal(t, docVersion1.RevTreeID, historyList[len(historyList)-1], "last history entry should be parent revTreeID")
723+
require.Len(t, historyList, 2, "history should have rev tree entries only since hlv history is empty for this doc")
724+
assert.Equal(t, docVersion2.RevTreeID, historyList[0], "first history entry should be current revTreeID")
725+
assert.Equal(t, docVersion1.RevTreeID, historyList[1], "second history entry should be parent revTreeID")
726726

727727
// Should NOT have a separate revTreeHistory property (that's only for SGR2 peers)
728-
assert.Empty(t, request.Properties[db.RevMessageTreeHistory], "revTreeHistory property should not be set for non-SGR2 clients")
729-
}
730-
731-
bt.blipContext.HandlerForProfile["changes"] = func(request *blip.Message) {
732-
defer receivedChangesRequestWg.Done()
733-
body, err := request.Body()
734-
require.NoError(t, err)
735-
736-
knownRevs := []any{}
737-
if string(body) != "null" {
738-
var changesReqs [][]any
739-
err = base.JSONUnmarshal(body, &changesReqs)
740-
require.NoError(t, err)
741-
742-
knownRevs = make([]any, len(changesReqs))
743-
for i := range changesReqs {
744-
// Respond with the legacy revID as the known rev, simulating a client that has rev1
745-
// but predates HLV. This triggers remoteIsLegacyRev=true and sets deltaSrc=rev1ID
746-
// on the server side.
747-
knownRevs[i] = []string{rev1ID}
748-
}
749-
}
750-
751-
if !request.NoReply() {
752-
response := request.Response()
753-
// Enable deltas so the server sends a delta rather than full body
754-
response.Properties[db.ChangesResponseDeltas] = "true"
755-
emptyResponseValBytes, err := base.JSONMarshal(knownRevs)
756-
require.NoError(t, err)
757-
response.SetBody(emptyResponseValBytes)
758-
}
759-
}
760-
761-
subChangesRequest := bt.newRequest()
762-
subChangesRequest.SetProfile("subChanges")
763-
subChangesRequest.Properties["continuous"] = "false"
764-
sent := bt.sender.Send(subChangesRequest)
765-
assert.True(t, sent)
766-
767-
subChangesResponse := subChangesRequest.Response()
768-
assert.Equal(t, subChangesRequest.SerialNumber(), subChangesResponse.SerialNumber())
769-
770-
base.WaitWithTimeout(t, &receivedChangesRequestWg, time.Second*10)
771-
base.WaitWithTimeout(t, &revsFinishedWg, time.Second*10)
728+
assert.Empty(t, msg.Properties[db.RevMessageTreeHistory], "revTreeHistory property should not be set for non-SGR2 clients")
729+
})
772730
}
773731

774-
// TestDeltaSyncSendHistoryWithLegacyClientAndHLVHistory verifies that when a delta is sent to a client that has a
732+
// TestDeltaSyncSendRevTreeHistoryWithHLVHistoryClientHavingLegacyRev verifies that when a delta is sent to a client that has a
775733
// legacy revision and the document has multi-source HLV history, the history property contains the HLV previous
776734
// versions followed by the rev tree.
777735
//
@@ -781,120 +739,155 @@ func TestDeltaSyncSendHistoryWithLegacyClient(t *testing.T) {
781739
// Setup:
782740
// - Create doc rev1 on SGW (gets revTreeID + HLV cv)
783741
// - Update doc via HLV agent to simulate a different peer creating rev2 (gets a new HLV cv with rev1's cv as pv)
784-
// - Client connects with V4 subprotocol, delta sync enabled
785-
// - Custom changes handler responds with [rev1RevTreeID] as knownRevs (legacy client)
742+
// - Client pre-populated with rev1 as a legacy revtree revision (no HLV)
743+
// - Client pulls with delta sync enabled
786744
//
787745
// Expected rev message (buildRevHistory scenario 3):
788746
//
789747
// history property: [pv (rev1's cv), rev2RevTreeID, rev1RevTreeID]
790748
// deltaSrc: rev1RevTreeID
791-
func TestDeltaSyncSendHistoryWithLegacyClientAndHLVHistory(t *testing.T) {
749+
func TestDeltaSyncSendRevTreeHistoryWithHLVHistoryClientHavingLegacyRev(t *testing.T) {
792750
if !base.IsEnterpriseEdition() {
793751
t.Skip("Delta sync requires EE")
794752
}
795-
base.SetUpTestLogging(t, base.LevelDebug, base.KeyHTTP, base.KeySync, base.KeySyncMsg, base.KeyChanges)
753+
754+
btcRunner := NewBlipTesterClientRunner(t)
755+
btcRunner.SkipSubtest[RevtreeSubtestName] = true // V4-specific: tests legacy rev in HLV-aware client
796756

797757
sgUseDeltas := true
798-
rt := NewRestTester(t, &RestTesterConfig{
799-
GuestEnabled: true,
800-
DatabaseConfig: &DatabaseConfig{DbConfig: DbConfig{
801-
DeltaSync: &DeltaSyncConfig{
802-
Enabled: &sgUseDeltas,
803-
},
804-
}},
805-
})
806-
defer rt.Close()
758+
btcRunner.Run(func(t *testing.T) {
759+
rt := NewRestTester(t, &RestTesterConfig{
760+
GuestEnabled: true,
761+
DatabaseConfig: &DatabaseConfig{DbConfig: DbConfig{
762+
DeltaSync: &DeltaSyncConfig{
763+
Enabled: &sgUseDeltas,
764+
},
765+
}},
766+
})
767+
defer rt.Close()
807768

808-
bt := NewBlipTesterFromSpecWithRT(rt, &BlipTesterSpec{
809-
blipProtocols: []string{db.CBMobileReplicationV4.SubprotocolString()},
810-
})
811-
defer bt.Close()
769+
docID := SafeDocumentName(t, t.Name())
812770

813-
collection, ctx := rt.GetSingleTestDatabaseCollection()
771+
client := btcRunner.NewBlipTesterClientOptsWithRT(rt, &BlipTesterClientOpts{
772+
ClientDeltas: true,
773+
})
774+
defer client.Close()
814775

815-
// Create rev1 on SGW — gets revTreeID + HLV
816-
docVersion1 := rt.PutDoc("doc1", `{"key": "val1"}`)
817-
rev1ID := docVersion1.RevTreeID
776+
collection, ctx := rt.GetSingleTestDatabaseCollection()
818777

819-
// Update doc via HLV agent to simulate a different peer creating rev2.
820-
// This gives the doc a new CV from "peerSource" with rev1's CV as a previous version in the HLV.
821-
newDoc, _, err := collection.GetDocWithXattrs(ctx, "doc1", db.DocUnmarshalAll)
822-
require.NoError(t, err)
823-
agent := db.NewHLVAgent(t, rt.GetSingleDataStore(), "peerSource", base.VvXattrName)
824-
_ = agent.UpdateWithHLV(ctx, "doc1", newDoc.Cas, newDoc.HLV)
778+
// Create rev1 on SGW — gets revTreeID + HLV
779+
docVersion1 := rt.PutDoc(docID, `{"key": "val1"}`)
780+
rev1ID := docVersion1.RevTreeID
825781

826-
// Force import so SGW picks up the HLV agent's update
827-
newDoc, err = collection.GetDocument(ctx, "doc1", db.DocUnmarshalAll)
828-
require.NoError(t, err)
829-
rt.WaitForPendingChanges()
782+
// Pre-populate the client with rev1 as a legacy revtree revision so that when SGW sends changes
783+
// for the updated doc, the client reports rev1ID as its known rev (triggering delta from rev1).
784+
btcRunner.AddRevTreeRev(client.id, docID, rev1ID, EmptyDocVersion(), []byte(`{"key": "val1"}`))
830785

831-
receivedChangesRequestWg := sync.WaitGroup{}
832-
receivedChangesRequestWg.Add(2)
833-
revsFinishedWg := sync.WaitGroup{}
834-
revsFinishedWg.Add(1)
786+
// Update doc via HLV agent to simulate a different peer creating rev2.
787+
// This gives the doc a new CV from "peerSource" with rev1's CV as a previous version in the HLV.
788+
newDoc, _, err := collection.GetDocWithXattrs(ctx, docID, db.DocUnmarshalAll)
789+
require.NoError(t, err)
790+
agent := db.NewHLVAgent(t, rt.GetSingleDataStore(), "peerSource", base.VvXattrName)
791+
_ = agent.UpdateWithHLV(ctx, docID, newDoc.Cas, newDoc.HLV)
835792

836-
bt.blipContext.HandlerForProfile["rev"] = func(request *blip.Message) {
837-
defer revsFinishedWg.Done()
793+
// Force import so SGW picks up the HLV agent's update
794+
newDoc, err = collection.GetDocument(ctx, docID, db.DocUnmarshalAll)
795+
require.NoError(t, err)
796+
rt.WaitForPendingChanges()
797+
798+
docVersion2 := newDoc.ExtractDocVersion()
799+
btcRunner.StartOneshotPull(client.id)
800+
msg := btcRunner.WaitForPullRevMessage(client.id, docID, docVersion2)
838801

839802
// Should be sent as delta with rev1 as the delta source
840-
deltaSrc := request.Properties[db.RevMessageDeltaSrc]
841-
assert.Equal(t, rev1ID, deltaSrc, "delta source should be the legacy revID the client reported")
803+
assert.Equal(t, rev1ID, msg.Properties[db.RevMessageDeltaSrc], "delta source should be the legacy revID the client reported")
842804

843805
// Rev property should be the new CV from the HLV agent update
844-
rev := request.Properties["rev"]
845-
assert.Equal(t, newDoc.HLV.GetCurrentVersionString(), rev)
806+
assert.Equal(t, docVersion2.CV.String(), msg.Properties[db.RevMessageRev])
846807

847808
// History should be: [pv (rev1's original cv), rev2RevTreeID, rev1RevTreeID]
848809
// - First entry is HLV previous version (rev1's cv becomes pv after the HLV agent update)
849810
// - Last two entries are the rev tree (scenario 3: local HLV-aware, remote legacy)
850-
history := request.Properties[db.RevMessageHistory]
811+
history := msg.Properties[db.RevMessageHistory]
851812
require.NotEmpty(t, history, "history must not be empty")
852813
historyList := strings.Split(history, ",")
853814
require.Len(t, historyList, 3, "history should have HLV pv + 2 rev tree entries")
854815
assert.Equal(t, docVersion1.CV.String(), historyList[0], "first history entry should be HLV previous version (rev1's cv)")
855-
assert.Equal(t, newDoc.GetRevTreeID(), historyList[1], "second history entry should be current revTreeID")
816+
assert.Equal(t, docVersion2.RevTreeID, historyList[1], "second history entry should be current revTreeID")
856817
assert.Equal(t, docVersion1.RevTreeID, historyList[2], "third history entry should be parent revTreeID")
857818

858-
assert.Empty(t, request.Properties[db.RevMessageTreeHistory], "revTreeHistory property should not be set for non-SGR2 clients")
819+
assert.Empty(t, msg.Properties[db.RevMessageTreeHistory], "revTreeHistory property should not be set for non-SGR2 clients")
820+
})
821+
}
822+
823+
// TestBothSidesLegacyRevDeltaSync verifies that when a delta is sent to a client that has a legacy revision
824+
// and the document on SGW is also a legacy revision (no HLV on either side), the history property contains
825+
// only the parent revTreeID and the rev property is the current revTreeID (not a CV).
826+
//
827+
// Setup:
828+
// - Create doc rev1 and rev2 on SGW with no HLV (legacy revs)
829+
// - Client pre-populated with rev1 as a legacy revtree revision (no HLV)
830+
// - Client pulls with delta sync enabled
831+
//
832+
// Expected rev message (buildRevHistory scenario 1):
833+
//
834+
// history property: [rev1RevTreeID] (no hlv pv since neither side is HLV-aware)
835+
// rev property: rev2RevTreeID (not a CV, since both sides are legacy)
836+
// deltaSrc: rev1RevTreeID
837+
func TestBothSidesLegacyRevDeltaSync(t *testing.T) {
838+
if !base.IsEnterpriseEdition() {
839+
t.Skip("Delta sync requires EE")
859840
}
860841

861-
bt.blipContext.HandlerForProfile["changes"] = func(request *blip.Message) {
862-
defer receivedChangesRequestWg.Done()
863-
body, err := request.Body()
864-
require.NoError(t, err)
842+
btcRunner := NewBlipTesterClientRunner(t)
843+
btcRunner.SkipSubtest[RevtreeSubtestName] = true // V4-specific: tests legacy rev in HLV-aware client
865844

866-
knownRevs := []any{}
867-
if string(body) != "null" {
868-
var changesReqs [][]any
869-
err = base.JSONUnmarshal(body, &changesReqs)
870-
require.NoError(t, err)
845+
sgUseDeltas := true
846+
btcRunner.Run(func(t *testing.T) {
847+
rt := NewRestTester(t, &RestTesterConfig{
848+
GuestEnabled: true,
849+
DatabaseConfig: &DatabaseConfig{DbConfig: DbConfig{
850+
DeltaSync: &DeltaSyncConfig{
851+
Enabled: &sgUseDeltas,
852+
},
853+
}},
854+
})
855+
defer rt.Close()
871856

872-
knownRevs = make([]any, len(changesReqs))
873-
for i := range changesReqs {
874-
knownRevs[i] = []string{rev1ID}
875-
}
876-
}
857+
docID := SafeDocumentName(t, t.Name())
877858

878-
if !request.NoReply() {
879-
response := request.Response()
880-
response.Properties[db.ChangesResponseDeltas] = "true"
881-
emptyResponseValBytes, err := base.JSONMarshal(knownRevs)
882-
require.NoError(t, err)
883-
response.SetBody(emptyResponseValBytes)
884-
}
885-
}
859+
client := btcRunner.NewBlipTesterClientOptsWithRT(rt, &BlipTesterClientOpts{
860+
ClientDeltas: true,
861+
})
862+
defer client.Close()
886863

887-
subChangesRequest := bt.newRequest()
888-
subChangesRequest.SetProfile("subChanges")
889-
subChangesRequest.Properties["continuous"] = "false"
890-
sent := bt.sender.Send(subChangesRequest)
891-
assert.True(t, sent)
864+
// create rev1, rev 2 on SGW with no HLV (legacy revs)
865+
docRev1 := rt.CreateDocNoHLV(docID, db.Body{"test": "doc"})
866+
docRev2 := rt.CreateDocNoHLV(docID, db.Body{"test": "update", db.BodyRev: docRev1.GetRevTreeID()})
867+
rt.WaitForPendingChanges()
892868

893-
subChangesResponse := subChangesRequest.Response()
894-
assert.Equal(t, subChangesRequest.SerialNumber(), subChangesResponse.SerialNumber())
869+
// Pre-populate the client with rev1 as a legacy revtree revision so that when SGW sends changes
870+
// for rev2, the client reports rev1ID as its known rev (triggering delta from rev1).
871+
btcRunner.AddRevTreeRev(client.id, docID, docRev1.GetRevTreeID(), EmptyDocVersion(), []byte(`{"test": "doc"}`))
895872

896-
base.WaitWithTimeout(t, &receivedChangesRequestWg, time.Second*10)
897-
base.WaitWithTimeout(t, &revsFinishedWg, time.Second*10)
873+
btcRunner.StartOneshotPull(client.id)
874+
msg := btcRunner.WaitForPullRevMessage(client.id, docID, docRev2.ExtractDocVersion())
875+
876+
// Should be sent as delta with rev1 as the delta source
877+
assert.Equal(t, docRev1.GetRevTreeID(), msg.Properties[db.RevMessageDeltaSrc], "delta source should be the legacy revID the client reported")
878+
879+
// Rev property should be rev2's rev tree ID since both sides are legacy
880+
assert.Equal(t, docRev2.GetRevTreeID(), msg.Properties[db.RevMessageRev])
881+
882+
// History should contain rev1's revTreeID
883+
history := msg.Properties[db.RevMessageHistory]
884+
require.NotEmpty(t, history, "history must not be empty — rev tree history is required for legacy client conflict detection")
885+
historyList := strings.Split(history, ",")
886+
require.Len(t, historyList, 1, "history should have rev tree entries only since hlv history is empty for this doc")
887+
assert.Equal(t, docRev1.GetRevTreeID(), historyList[0], "only history entry should be parent revTreeID since both sides are legacy")
888+
889+
assert.Empty(t, msg.Properties[db.RevMessageTreeHistory], "revTreeHistory property should not be set for non-SGR2 clients")
890+
})
898891
}
899892

900893
// TestChangesResponseWithHLVInHistory:

0 commit comments

Comments
 (0)