From 3a2f9fe6f5bc39611fb47a12f9b7ae14c0f0621c Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Tue, 6 Jun 2023 20:10:44 +0200 Subject: [PATCH] WIP synctree tests rewrite --- .../synctree/mock_synctree/mock_synctree.go | 234 +++++++++++++++++- ...ol_test.go => protocolintegration_test.go} | 0 commonspace/object/tree/synctree/synctree.go | 2 +- .../object/tree/synctree/synctree_test.go | 10 +- .../object/tree/synctree/synctreehandler.go | 2 + .../tree/synctree/synctreehandler_test.go | 6 +- .../{syncprotocol.go => treesyncprotocol.go} | 0 .../tree/synctree/treesyncprotocol_test.go | 83 +++++++ .../object/tree/synctree/utils_test.go | 201 ++++++++------- .../mock_objectsync/mock_objectsync.go | 15 -- 10 files changed, 432 insertions(+), 121 deletions(-) rename commonspace/object/tree/synctree/{syncprotocol_test.go => protocolintegration_test.go} (100%) rename commonspace/object/tree/synctree/{syncprotocol.go => treesyncprotocol.go} (100%) create mode 100644 commonspace/object/tree/synctree/treesyncprotocol_test.go diff --git a/commonspace/object/tree/synctree/mock_synctree/mock_synctree.go b/commonspace/object/tree/synctree/mock_synctree/mock_synctree.go index 0f9d40ba..ca4789f1 100644 --- a/commonspace/object/tree/synctree/mock_synctree/mock_synctree.go +++ b/commonspace/object/tree/synctree/mock_synctree/mock_synctree.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/anyproto/any-sync/commonspace/object/tree/synctree (interfaces: SyncTree,ReceiveQueue,HeadNotifiable) +// Source: github.com/anyproto/any-sync/commonspace/object/tree/synctree (interfaces: SyncTree,ReceiveQueue,HeadNotifiable,SyncClient,RequestFactory) // Package mock_synctree is a generated GoMock package. package mock_synctree @@ -186,6 +186,21 @@ func (mr *MockSyncTreeMockRecorder) HandleMessage(arg0, arg1, arg2 interface{}) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleMessage", reflect.TypeOf((*MockSyncTree)(nil).HandleMessage), arg0, arg1, arg2) } +// HandleRequest mocks base method. +func (m *MockSyncTree) HandleRequest(arg0 context.Context, arg1 string, arg2 *spacesyncproto.ObjectSyncMessage) (*spacesyncproto.ObjectSyncMessage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HandleRequest", arg0, arg1, arg2) + ret0, _ := ret[0].(*spacesyncproto.ObjectSyncMessage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// HandleRequest indicates an expected call of HandleRequest. +func (mr *MockSyncTreeMockRecorder) HandleRequest(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleRequest", reflect.TypeOf((*MockSyncTree)(nil).HandleRequest), arg0, arg1, arg2) +} + // HasChanges mocks base method. func (m *MockSyncTree) HasChanges(arg0 ...string) bool { m.ctrl.T.Helper() @@ -590,3 +605,220 @@ func (mr *MockHeadNotifiableMockRecorder) UpdateHeads(arg0, arg1 interface{}) *g mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateHeads", reflect.TypeOf((*MockHeadNotifiable)(nil).UpdateHeads), arg0, arg1) } + +// MockSyncClient is a mock of SyncClient interface. +type MockSyncClient struct { + ctrl *gomock.Controller + recorder *MockSyncClientMockRecorder +} + +// MockSyncClientMockRecorder is the mock recorder for MockSyncClient. +type MockSyncClientMockRecorder struct { + mock *MockSyncClient +} + +// NewMockSyncClient creates a new mock instance. +func NewMockSyncClient(ctrl *gomock.Controller) *MockSyncClient { + mock := &MockSyncClient{ctrl: ctrl} + mock.recorder = &MockSyncClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSyncClient) EXPECT() *MockSyncClientMockRecorder { + return m.recorder +} + +// Broadcast mocks base method. +func (m *MockSyncClient) Broadcast(arg0 *treechangeproto.TreeSyncMessage) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Broadcast", arg0) +} + +// Broadcast indicates an expected call of Broadcast. +func (mr *MockSyncClientMockRecorder) Broadcast(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Broadcast", reflect.TypeOf((*MockSyncClient)(nil).Broadcast), arg0) +} + +// CreateFullSyncRequest mocks base method. +func (m *MockSyncClient) CreateFullSyncRequest(arg0 objecttree.ObjectTree, arg1, arg2 []string) (*treechangeproto.TreeSyncMessage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateFullSyncRequest", arg0, arg1, arg2) + ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateFullSyncRequest indicates an expected call of CreateFullSyncRequest. +func (mr *MockSyncClientMockRecorder) CreateFullSyncRequest(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFullSyncRequest", reflect.TypeOf((*MockSyncClient)(nil).CreateFullSyncRequest), arg0, arg1, arg2) +} + +// CreateFullSyncResponse mocks base method. +func (m *MockSyncClient) CreateFullSyncResponse(arg0 objecttree.ObjectTree, arg1, arg2 []string) (*treechangeproto.TreeSyncMessage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateFullSyncResponse", arg0, arg1, arg2) + ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateFullSyncResponse indicates an expected call of CreateFullSyncResponse. +func (mr *MockSyncClientMockRecorder) CreateFullSyncResponse(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFullSyncResponse", reflect.TypeOf((*MockSyncClient)(nil).CreateFullSyncResponse), arg0, arg1, arg2) +} + +// CreateHeadUpdate mocks base method. +func (m *MockSyncClient) CreateHeadUpdate(arg0 objecttree.ObjectTree, arg1 []*treechangeproto.RawTreeChangeWithId) *treechangeproto.TreeSyncMessage { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateHeadUpdate", arg0, arg1) + ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage) + return ret0 +} + +// CreateHeadUpdate indicates an expected call of CreateHeadUpdate. +func (mr *MockSyncClientMockRecorder) CreateHeadUpdate(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateHeadUpdate", reflect.TypeOf((*MockSyncClient)(nil).CreateHeadUpdate), arg0, arg1) +} + +// CreateNewTreeRequest mocks base method. +func (m *MockSyncClient) CreateNewTreeRequest() *treechangeproto.TreeSyncMessage { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateNewTreeRequest") + ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage) + return ret0 +} + +// CreateNewTreeRequest indicates an expected call of CreateNewTreeRequest. +func (mr *MockSyncClientMockRecorder) CreateNewTreeRequest() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNewTreeRequest", reflect.TypeOf((*MockSyncClient)(nil).CreateNewTreeRequest)) +} + +// QueueRequest mocks base method. +func (m *MockSyncClient) QueueRequest(arg0, arg1 string, arg2 *treechangeproto.TreeSyncMessage) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "QueueRequest", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// QueueRequest indicates an expected call of QueueRequest. +func (mr *MockSyncClientMockRecorder) QueueRequest(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueueRequest", reflect.TypeOf((*MockSyncClient)(nil).QueueRequest), arg0, arg1, arg2) +} + +// SendRequest mocks base method. +func (m *MockSyncClient) SendRequest(arg0 context.Context, arg1, arg2 string, arg3 *treechangeproto.TreeSyncMessage) (*spacesyncproto.ObjectSyncMessage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendRequest", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(*spacesyncproto.ObjectSyncMessage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SendRequest indicates an expected call of SendRequest. +func (mr *MockSyncClientMockRecorder) SendRequest(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendRequest", reflect.TypeOf((*MockSyncClient)(nil).SendRequest), arg0, arg1, arg2, arg3) +} + +// SendUpdate mocks base method. +func (m *MockSyncClient) SendUpdate(arg0, arg1 string, arg2 *treechangeproto.TreeSyncMessage) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendUpdate", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendUpdate indicates an expected call of SendUpdate. +func (mr *MockSyncClientMockRecorder) SendUpdate(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendUpdate", reflect.TypeOf((*MockSyncClient)(nil).SendUpdate), arg0, arg1, arg2) +} + +// MockRequestFactory is a mock of RequestFactory interface. +type MockRequestFactory struct { + ctrl *gomock.Controller + recorder *MockRequestFactoryMockRecorder +} + +// MockRequestFactoryMockRecorder is the mock recorder for MockRequestFactory. +type MockRequestFactoryMockRecorder struct { + mock *MockRequestFactory +} + +// NewMockRequestFactory creates a new mock instance. +func NewMockRequestFactory(ctrl *gomock.Controller) *MockRequestFactory { + mock := &MockRequestFactory{ctrl: ctrl} + mock.recorder = &MockRequestFactoryMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockRequestFactory) EXPECT() *MockRequestFactoryMockRecorder { + return m.recorder +} + +// CreateFullSyncRequest mocks base method. +func (m *MockRequestFactory) CreateFullSyncRequest(arg0 objecttree.ObjectTree, arg1, arg2 []string) (*treechangeproto.TreeSyncMessage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateFullSyncRequest", arg0, arg1, arg2) + ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateFullSyncRequest indicates an expected call of CreateFullSyncRequest. +func (mr *MockRequestFactoryMockRecorder) CreateFullSyncRequest(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFullSyncRequest", reflect.TypeOf((*MockRequestFactory)(nil).CreateFullSyncRequest), arg0, arg1, arg2) +} + +// CreateFullSyncResponse mocks base method. +func (m *MockRequestFactory) CreateFullSyncResponse(arg0 objecttree.ObjectTree, arg1, arg2 []string) (*treechangeproto.TreeSyncMessage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateFullSyncResponse", arg0, arg1, arg2) + ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateFullSyncResponse indicates an expected call of CreateFullSyncResponse. +func (mr *MockRequestFactoryMockRecorder) CreateFullSyncResponse(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFullSyncResponse", reflect.TypeOf((*MockRequestFactory)(nil).CreateFullSyncResponse), arg0, arg1, arg2) +} + +// CreateHeadUpdate mocks base method. +func (m *MockRequestFactory) CreateHeadUpdate(arg0 objecttree.ObjectTree, arg1 []*treechangeproto.RawTreeChangeWithId) *treechangeproto.TreeSyncMessage { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateHeadUpdate", arg0, arg1) + ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage) + return ret0 +} + +// CreateHeadUpdate indicates an expected call of CreateHeadUpdate. +func (mr *MockRequestFactoryMockRecorder) CreateHeadUpdate(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateHeadUpdate", reflect.TypeOf((*MockRequestFactory)(nil).CreateHeadUpdate), arg0, arg1) +} + +// CreateNewTreeRequest mocks base method. +func (m *MockRequestFactory) CreateNewTreeRequest() *treechangeproto.TreeSyncMessage { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateNewTreeRequest") + ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage) + return ret0 +} + +// CreateNewTreeRequest indicates an expected call of CreateNewTreeRequest. +func (mr *MockRequestFactoryMockRecorder) CreateNewTreeRequest() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNewTreeRequest", reflect.TypeOf((*MockRequestFactory)(nil).CreateNewTreeRequest)) +} diff --git a/commonspace/object/tree/synctree/syncprotocol_test.go b/commonspace/object/tree/synctree/protocolintegration_test.go similarity index 100% rename from commonspace/object/tree/synctree/syncprotocol_test.go rename to commonspace/object/tree/synctree/protocolintegration_test.go diff --git a/commonspace/object/tree/synctree/synctree.go b/commonspace/object/tree/synctree/synctree.go index 4c694880..f8558992 100644 --- a/commonspace/object/tree/synctree/synctree.go +++ b/commonspace/object/tree/synctree/synctree.go @@ -1,4 +1,4 @@ -//go:generate mockgen -destination mock_synctree/mock_synctree.go github.com/anyproto/any-sync/commonspace/object/tree/synctree SyncTree,ReceiveQueue,HeadNotifiable +//go:generate mockgen -destination mock_synctree/mock_synctree.go github.com/anyproto/any-sync/commonspace/object/tree/synctree SyncTree,ReceiveQueue,HeadNotifiable,SyncClient,RequestFactory package synctree import ( diff --git a/commonspace/object/tree/synctree/synctree_test.go b/commonspace/object/tree/synctree/synctree_test.go index 0c12bd34..76791c59 100644 --- a/commonspace/object/tree/synctree/synctree_test.go +++ b/commonspace/object/tree/synctree/synctree_test.go @@ -4,11 +4,11 @@ import ( "context" "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" "github.com/anyproto/any-sync/commonspace/object/tree/objecttree/mock_objecttree" + "github.com/anyproto/any-sync/commonspace/object/tree/synctree/mock_synctree" "github.com/anyproto/any-sync/commonspace/object/tree/synctree/updatelistener" "github.com/anyproto/any-sync/commonspace/object/tree/synctree/updatelistener/mock_updatelistener" "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" "github.com/anyproto/any-sync/commonspace/objectsync" - "github.com/anyproto/any-sync/commonspace/objectsync/mock_objectsync" "github.com/anyproto/any-sync/commonspace/syncstatus" "github.com/anyproto/any-sync/nodeconf" "github.com/golang/mock/gomock" @@ -46,7 +46,7 @@ func Test_BuildSyncTree(t *testing.T) { defer ctrl.Finish() updateListenerMock := mock_updatelistener.NewMockUpdateListener(ctrl) - syncClientMock := mock_objectsync.NewMockSyncClient(ctrl) + syncClientMock := mock_synctree.NewMockSyncClient(ctrl) objTreeMock := newTestObjMock(mock_objecttree.NewMockObjectTree(ctrl)) tr := &syncTree{ ObjectTree: objTreeMock, @@ -73,7 +73,7 @@ func Test_BuildSyncTree(t *testing.T) { updateListenerMock.EXPECT().Update(tr) syncClientMock.EXPECT().CreateHeadUpdate(gomock.Eq(tr), gomock.Eq(changes)).Return(headUpdate) - syncClientMock.EXPECT().Broadcast(gomock.Any(), gomock.Eq(headUpdate)) + syncClientMock.EXPECT().Broadcast(gomock.Eq(headUpdate)) res, err := tr.AddRawChanges(ctx, payload) require.NoError(t, err) require.Equal(t, expectedRes, res) @@ -95,7 +95,7 @@ func Test_BuildSyncTree(t *testing.T) { updateListenerMock.EXPECT().Rebuild(tr) syncClientMock.EXPECT().CreateHeadUpdate(gomock.Eq(tr), gomock.Eq(changes)).Return(headUpdate) - syncClientMock.EXPECT().Broadcast(gomock.Any(), gomock.Eq(headUpdate)) + syncClientMock.EXPECT().Broadcast(gomock.Eq(headUpdate)) res, err := tr.AddRawChanges(ctx, payload) require.NoError(t, err) require.Equal(t, expectedRes, res) @@ -133,7 +133,7 @@ func Test_BuildSyncTree(t *testing.T) { Return(expectedRes, nil) syncClientMock.EXPECT().CreateHeadUpdate(gomock.Eq(tr), gomock.Eq(changes)).Return(headUpdate) - syncClientMock.EXPECT().Broadcast(gomock.Any(), gomock.Eq(headUpdate)) + syncClientMock.EXPECT().Broadcast(gomock.Eq(headUpdate)) res, err := tr.AddContent(ctx, content) require.NoError(t, err) require.Equal(t, expectedRes, res) diff --git a/commonspace/object/tree/synctree/synctreehandler.go b/commonspace/object/tree/synctree/synctreehandler.go index 43e764cc..154330f3 100644 --- a/commonspace/object/tree/synctree/synctreehandler.go +++ b/commonspace/object/tree/synctree/synctreehandler.go @@ -52,6 +52,8 @@ func (s *syncTreeHandler) HandleRequest(ctx context.Context, senderId string, re return } s.syncStatus.HeadsReceive(senderId, request.ObjectId, treechangeproto.GetHeads(unmarshalled)) + s.objTree.Lock() + defer s.objTree.Unlock() treeResp, err := s.syncProtocol.FullSyncRequest(ctx, senderId, fullSyncRequest) if err != nil { return diff --git a/commonspace/object/tree/synctree/synctreehandler_test.go b/commonspace/object/tree/synctree/synctreehandler_test.go index 4a65aad5..f029b092 100644 --- a/commonspace/object/tree/synctree/synctreehandler_test.go +++ b/commonspace/object/tree/synctree/synctreehandler_test.go @@ -63,19 +63,17 @@ type syncHandlerFixture struct { func newSyncHandlerFixture(t *testing.T) *syncHandlerFixture { ctrl := gomock.NewController(t) - syncClientMock := mock_objectsync.NewMockSyncClient(ctrl) objectTreeMock := newTestObjMock(mock_objecttree.NewMockObjectTree(ctrl)) receiveQueue := newReceiveQueue(5) syncHandler := &syncTreeHandler{ - objTree: objectTreeMock, - syncClient: syncClientMock, + objTree: objectTreeMock, + //syncClient: syncClientMock, queue: receiveQueue, syncStatus: syncstatus.NewNoOpSyncStatus(), } return &syncHandlerFixture{ ctrl: ctrl, - syncClientMock: syncClientMock, objectTreeMock: objectTreeMock, receiveQueueMock: receiveQueue, syncHandler: syncHandler, diff --git a/commonspace/object/tree/synctree/syncprotocol.go b/commonspace/object/tree/synctree/treesyncprotocol.go similarity index 100% rename from commonspace/object/tree/synctree/syncprotocol.go rename to commonspace/object/tree/synctree/treesyncprotocol.go diff --git a/commonspace/object/tree/synctree/treesyncprotocol_test.go b/commonspace/object/tree/synctree/treesyncprotocol_test.go new file mode 100644 index 00000000..19080908 --- /dev/null +++ b/commonspace/object/tree/synctree/treesyncprotocol_test.go @@ -0,0 +1,83 @@ +package synctree + +import ( + "context" + "github.com/anyproto/any-sync/app/logger" + "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" + "github.com/anyproto/any-sync/commonspace/object/tree/objecttree/mock_objecttree" + "github.com/anyproto/any-sync/commonspace/object/tree/synctree/mock_synctree" + "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + "testing" +) + +type treeSyncProtocolFixture struct { + log logger.CtxLogger + spaceId string + senderId string + treeId string + objectTreeMock *testObjTreeMock + reqFactory *mock_synctree.MockRequestFactory + ctrl *gomock.Controller + syncProtocol TreeSyncProtocol +} + +func newSyncProtocolFixture(t *testing.T) *treeSyncProtocolFixture { + ctrl := gomock.NewController(t) + objTree := &testObjTreeMock{ + MockObjectTree: mock_objecttree.NewMockObjectTree(ctrl), + } + spaceId := "spaceId" + reqFactory := mock_synctree.NewMockRequestFactory(ctrl) + objTree.EXPECT().Id().Return("treeId") + syncProtocol := newTreeSyncProtocol(spaceId, objTree, reqFactory) + return &treeSyncProtocolFixture{ + log: log, + spaceId: spaceId, + senderId: "senderId", + treeId: "treeId", + objectTreeMock: objTree, + reqFactory: reqFactory, + ctrl: ctrl, + syncProtocol: syncProtocol, + } +} + +func (fx *treeSyncProtocolFixture) finish() { + fx.ctrl.Finish() +} + +func TestTreeSyncProtocol_HeadUpdate(t *testing.T) { + ctx := context.Background() + //fullRequest := &treechangeproto.TreeSyncMessage{ + // Content: &treechangeproto.TreeSyncContentValue{ + // Value: &treechangeproto.TreeSyncContentValue_FullSyncRequest{ + // FullSyncRequest: &treechangeproto.TreeFullSyncRequest{}, + // }, + // }, + //} + t.Run("head update non empty all heads added", func(t *testing.T) { + fx := newSyncProtocolFixture(t) + chWithId := &treechangeproto.RawTreeChangeWithId{} + headUpdate := &treechangeproto.TreeHeadUpdate{ + Heads: []string{"h1"}, + Changes: []*treechangeproto.RawTreeChangeWithId{chWithId}, + SnapshotPath: []string{"h1"}, + } + fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId) + fx.objectTreeMock.EXPECT().Heads().Return([]string{"h2"}).Times(2) + fx.objectTreeMock.EXPECT().HasChanges(gomock.Eq([]string{"h1"})).Return(false) + fx.objectTreeMock.EXPECT(). + AddRawChanges(gomock.Any(), gomock.Eq(objecttree.RawChangesPayload{ + NewHeads: []string{"h1"}, + RawChanges: []*treechangeproto.RawTreeChangeWithId{chWithId}, + })). + Return(objecttree.AddResult{}, nil) + fx.objectTreeMock.EXPECT().HasChanges(gomock.Eq([]string{"h1"})).Return(true) + + res, err := fx.syncProtocol.HeadUpdate(ctx, fx.senderId, headUpdate) + require.NoError(t, err) + require.Nil(t, res) + }) +} diff --git a/commonspace/object/tree/synctree/utils_test.go b/commonspace/object/tree/synctree/utils_test.go index 6c295f9c..3ac8152a 100644 --- a/commonspace/object/tree/synctree/utils_test.go +++ b/commonspace/object/tree/synctree/utils_test.go @@ -3,6 +3,7 @@ package synctree import ( "context" "fmt" + "github.com/anyproto/any-sync/app" "github.com/anyproto/any-sync/commonspace/object/acl/list" "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" @@ -81,51 +82,124 @@ func (m *messageLog) addMessage(msg protocolMsg) { m.batcher.Add(context.Background(), msg) } +type requestPeerManager struct { + peerId string + handlers map[string]*testSyncHandler + log *messageLog +} + +func newRequestPeerManager(peerId string, log *messageLog) *requestPeerManager { + return &requestPeerManager{ + peerId: peerId, + handlers: map[string]*testSyncHandler{}, + log: log, + } +} + +func (r *requestPeerManager) addHandler(peerId string, handler *testSyncHandler) { + r.handlers[peerId] = handler +} + +func (r *requestPeerManager) Run(ctx context.Context) (err error) { + return nil +} + +func (r *requestPeerManager) Close(ctx context.Context) (err error) { + return nil +} + +func (r *requestPeerManager) SendRequest(ctx context.Context, peerId string, msg *spacesyncproto.ObjectSyncMessage) (reply *spacesyncproto.ObjectSyncMessage, err error) { + panic("should not be called") +} + +func (r *requestPeerManager) QueueRequest(peerId string, msg *spacesyncproto.ObjectSyncMessage) (err error) { + pMsg := protocolMsg{ + msg: msg, + senderId: r.peerId, + receiverId: peerId, + } + r.log.addMessage(pMsg) + return r.handlers[peerId].send(context.Background(), pMsg) +} + +func (r *requestPeerManager) Init(a *app.App) (err error) { + return +} + +func (r *requestPeerManager) Name() (name string) { + return +} + +func (r *requestPeerManager) SendPeer(ctx context.Context, peerId string, msg *spacesyncproto.ObjectSyncMessage) (err error) { + pMsg := protocolMsg{ + msg: msg, + senderId: r.peerId, + receiverId: peerId, + } + r.log.addMessage(pMsg) + return r.handlers[peerId].send(context.Background(), pMsg) +} + +func (r *requestPeerManager) Broadcast(ctx context.Context, msg *spacesyncproto.ObjectSyncMessage) (err error) { + for _, handler := range r.handlers { + pMsg := protocolMsg{ + msg: msg, + senderId: r.peerId, + receiverId: handler.peerId, + } + r.log.addMessage(pMsg) + handler.send(context.Background(), pMsg) + } + return +} + +func (r *requestPeerManager) GetResponsiblePeers(ctx context.Context) (peers []peer.Peer, err error) { + return nil, nil +} + // testSyncHandler is the wrapper around individual tree to test sync protocol type testSyncHandler struct { synchandler.SyncHandler - batcher *mb.MB[protocolMsg] - peerId string - aclList list.AclList - log *messageLog - syncClient SyncClient - builder objecttree.BuildObjectTreeFunc + batcher *mb.MB[protocolMsg] + peerId string + aclList list.AclList + log *messageLog + syncClient SyncClient + builder objecttree.BuildObjectTreeFunc + peerManager *requestPeerManager } // createSyncHandler creates a sync handler when a tree is already created func createSyncHandler(peerId, spaceId string, objTree objecttree.ObjectTree, log *messageLog) *testSyncHandler { - factory := NewRequestFactory() - syncClient := NewSyncClient(spaceId, newTestMessagePool(peerId, log), factory) + peerManager := newRequestPeerManager(peerId, log) + syncClient := NewSyncClient(spaceId, peerManager, peerManager) netTree := &broadcastTree{ ObjectTree: objTree, SyncClient: syncClient, } handler := newSyncTreeHandler(spaceId, netTree, syncClient, syncstatus.NewNoOpSyncStatus()) - return newTestSyncHandler(peerId, handler) + return &testSyncHandler{ + SyncHandler: handler, + batcher: mb.New[protocolMsg](0), + peerId: peerId, + peerManager: peerManager, + } } // createEmptySyncHandler creates a sync handler when the tree will be provided later (this emulates the situation when we have no tree) func createEmptySyncHandler(peerId, spaceId string, builder objecttree.BuildObjectTreeFunc, aclList list.AclList, log *messageLog) *testSyncHandler { - factory := NewRequestFactory() - syncClient := NewSyncClient(spaceId, newTestMessagePool(peerId, log), factory) + peerManager := newRequestPeerManager(peerId, log) + syncClient := NewSyncClient(spaceId, peerManager, peerManager) batcher := mb.New[protocolMsg](0) return &testSyncHandler{ - batcher: batcher, - peerId: peerId, - aclList: aclList, - log: log, - syncClient: syncClient, - builder: builder, - } -} - -func newTestSyncHandler(peerId string, syncHandler synchandler.SyncHandler) *testSyncHandler { - batcher := mb.New[protocolMsg](0) - return &testSyncHandler{ - SyncHandler: syncHandler, batcher: batcher, peerId: peerId, + aclList: aclList, + log: log, + syncClient: syncClient, + builder: builder, + peerManager: peerManager, } } @@ -140,12 +214,7 @@ func (h *testSyncHandler) HandleMessage(ctx context.Context, senderId string, re } if unmarshalled.Content.GetFullSyncResponse() == nil { newTreeRequest := NewRequestFactory().CreateNewTreeRequest() - var objMsg *spacesyncproto.ObjectSyncMessage - objMsg, err = MarshallTreeMessage(newTreeRequest, request.SpaceId, request.ObjectId, "") - if err != nil { - return - } - return h.manager().SendPeer(context.Background(), senderId, objMsg) + return h.syncClient.QueueRequest(senderId, request.ObjectId, newTreeRequest) } fullSyncResponse := unmarshalled.Content.GetFullSyncResponse() treeStorage, _ := treestorage.NewInMemoryTreeStorage(unmarshalled.RootChange, []string{unmarshalled.RootChange.Id}, nil) @@ -165,20 +234,16 @@ func (h *testSyncHandler) HandleMessage(ctx context.Context, senderId string, re return } h.SyncHandler = newSyncTreeHandler(request.SpaceId, netTree, h.syncClient, syncstatus.NewNoOpSyncStatus()) - var objMsg *spacesyncproto.ObjectSyncMessage - newTreeRequest := NewRequestFactory().CreateHeadUpdate(netTree, res.Added) - objMsg, err = MarshallTreeMessage(newTreeRequest, request.SpaceId, request.ObjectId, "") - if err != nil { - return - } - return h.manager().Broadcast(context.Background(), objMsg) + headUpdate := NewRequestFactory().CreateHeadUpdate(netTree, res.Added) + h.syncClient.Broadcast(headUpdate) + return nil } -func (h *testSyncHandler) manager() *testMessagePool { +func (h *testSyncHandler) manager() *requestPeerManager { if h.SyncHandler != nil { - return h.SyncHandler.(*syncTreeHandler).syncClient.MessagePool().(*testMessagePool) + return h.manager() } - return h.syncClient.MessagePool().(*testMessagePool) + return h.peerManager } func (h *testSyncHandler) tree() *broadcastTree { @@ -219,60 +284,6 @@ func (h *testSyncHandler) run(ctx context.Context, t *testing.T, wg *sync.WaitGr }() } -// testMessagePool captures all other handlers and sends messages to them -type testMessagePool struct { - peerId string - handlers map[string]*testSyncHandler - log *messageLog -} - -func newTestMessagePool(peerId string, log *messageLog) *testMessagePool { - return &testMessagePool{handlers: map[string]*testSyncHandler{}, peerId: peerId, log: log} -} - -func (m *testMessagePool) addHandler(peerId string, handler *testSyncHandler) { - m.handlers[peerId] = handler -} - -func (m *testMessagePool) SendPeer(ctx context.Context, peerId string, msg *spacesyncproto.ObjectSyncMessage) (err error) { - pMsg := protocolMsg{ - msg: msg, - senderId: m.peerId, - receiverId: peerId, - } - m.log.addMessage(pMsg) - return m.handlers[peerId].send(context.Background(), pMsg) -} - -func (m *testMessagePool) Broadcast(ctx context.Context, msg *spacesyncproto.ObjectSyncMessage) (err error) { - for _, handler := range m.handlers { - pMsg := protocolMsg{ - msg: msg, - senderId: m.peerId, - receiverId: handler.peerId, - } - m.log.addMessage(pMsg) - handler.send(context.Background(), pMsg) - } - return -} - -func (m *testMessagePool) GetResponsiblePeers(ctx context.Context) (peers []peer.Peer, err error) { - panic("should not be called") -} - -func (m *testMessagePool) LastUsage() time.Time { - panic("should not be called") -} - -func (m *testMessagePool) HandleMessage(ctx context.Context, senderId string, request *spacesyncproto.ObjectSyncMessage) (err error) { - panic("should not be called") -} - -func (m *testMessagePool) SendSync(ctx context.Context, peerId string, message *spacesyncproto.ObjectSyncMessage) (reply *spacesyncproto.ObjectSyncMessage, err error) { - panic("should not be called") -} - // broadcastTree is the tree that broadcasts changes to everyone when changes are added // it is a simplified version of SyncTree which is easier to use in the test environment type broadcastTree struct { @@ -286,7 +297,7 @@ func (b *broadcastTree) AddRawChanges(ctx context.Context, changes objecttree.Ra return objecttree.AddResult{}, err } upd := b.SyncClient.CreateHeadUpdate(b.ObjectTree, res.Added) - b.SyncClient.Broadcast(ctx, upd) + b.SyncClient.Broadcast(upd) return res, nil } diff --git a/commonspace/objectsync/mock_objectsync/mock_objectsync.go b/commonspace/objectsync/mock_objectsync/mock_objectsync.go index 5aee48e8..e11f04a9 100644 --- a/commonspace/objectsync/mock_objectsync/mock_objectsync.go +++ b/commonspace/objectsync/mock_objectsync/mock_objectsync.go @@ -10,7 +10,6 @@ import ( objecttree "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" treechangeproto "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" - objectsync "github.com/anyproto/any-sync/commonspace/objectsync" spacesyncproto "github.com/anyproto/any-sync/commonspace/spacesyncproto" gomock "github.com/golang/mock/gomock" ) @@ -108,20 +107,6 @@ func (mr *MockSyncClientMockRecorder) CreateNewTreeRequest() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNewTreeRequest", reflect.TypeOf((*MockSyncClient)(nil).CreateNewTreeRequest)) } -// MessagePool mocks base method. -func (m *MockSyncClient) MessagePool() objectsync.MessagePool { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MessagePool") - ret0, _ := ret[0].(objectsync.MessagePool) - return ret0 -} - -// MessagePool indicates an expected call of MessagePool. -func (mr *MockSyncClientMockRecorder) MessagePool() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MessagePool", reflect.TypeOf((*MockSyncClient)(nil).MessagePool)) -} - // SendSync mocks base method. func (m *MockSyncClient) SendSync(arg0 context.Context, arg1, arg2 string, arg3 *treechangeproto.TreeSyncMessage) (*spacesyncproto.ObjectSyncMessage, error) { m.ctrl.T.Helper()