diff --git a/common/commonspace/synctree/synctree.go b/common/commonspace/synctree/synctree.go index 99c6014d..14e05a3e 100644 --- a/common/commonspace/synctree/synctree.go +++ b/common/commonspace/synctree/synctree.go @@ -17,6 +17,10 @@ type SyncTree struct { listener updatelistener.UpdateListener } +var createDerivedObjectTree = tree.CreateDerivedObjectTree +var createObjectTree = tree.CreateObjectTree +var buildObjectTree = tree.BuildObjectTree + func DeriveSyncTree( ctx context.Context, payload tree.ObjectTreeCreatePayload, @@ -24,7 +28,7 @@ func DeriveSyncTree( listener updatelistener.UpdateListener, aclList list.ACLList, createStorage storage.TreeStorageCreatorFunc) (t tree.ObjectTree, err error) { - t, err = tree.CreateDerivedObjectTree(payload, aclList, createStorage) + t, err = createDerivedObjectTree(payload, aclList, createStorage) if err != nil { return } @@ -46,7 +50,7 @@ func CreateSyncTree( listener updatelistener.UpdateListener, aclList list.ACLList, createStorage storage.TreeStorageCreatorFunc) (t tree.ObjectTree, err error) { - t, err = tree.CreateObjectTree(payload, aclList, createStorage) + t, err = createObjectTree(payload, aclList, createStorage) if err != nil { return } @@ -76,7 +80,7 @@ func buildSyncTree( treeStorage storage.TreeStorage, listener updatelistener.UpdateListener, aclList list.ACLList) (t tree.ObjectTree, err error) { - t, err = tree.BuildObjectTree(treeStorage, aclList) + t, err = buildObjectTree(treeStorage, aclList) if err != nil { return } @@ -93,7 +97,7 @@ func buildSyncTree( } func (s *SyncTree) AddContent(ctx context.Context, content tree.SignableChangeContent) (res tree.AddResult, err error) { - res, err = s.AddContent(ctx, content) + res, err = s.ObjectTree.AddContent(ctx, content) if err != nil { return } @@ -103,7 +107,7 @@ func (s *SyncTree) AddContent(ctx context.Context, content tree.SignableChangeCo } func (s *SyncTree) AddRawChanges(ctx context.Context, changes ...*treechangeproto.RawTreeChangeWithId) (res tree.AddResult, err error) { - res, err = s.AddRawChanges(ctx, changes...) + res, err = s.ObjectTree.AddRawChanges(ctx, changes...) if err != nil { return } diff --git a/common/commonspace/synctree/synctree_test.go b/common/commonspace/synctree/synctree_test.go new file mode 100644 index 00000000..ef1bd99f --- /dev/null +++ b/common/commonspace/synctree/synctree_test.go @@ -0,0 +1,176 @@ +package synctree + +import ( + "context" + "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto" + "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/syncservice" + "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/syncservice/mock_syncservice" + "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/synctree/updatelistener" + "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/synctree/updatelistener/mock_updatelistener" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/list" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/list/mock_list" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage/mock_storage" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/tree" + mock_tree "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/tree/mock_objecttree" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treechangeproto" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + "testing" +) + +type syncTreeMatcher struct { + objTree tree.ObjectTree + client syncservice.SyncClient + listener updatelistener.UpdateListener +} + +func (s syncTreeMatcher) Matches(x interface{}) bool { + t, ok := x.(*SyncTree) + if !ok { + return false + } + return s.objTree == t.ObjectTree && t.syncClient == s.client && t.listener == s.listener +} + +func (s syncTreeMatcher) String() string { + return "" +} + +func Test_DeriveSyncTree(t *testing.T) { + ctx := context.Background() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + updateListenerMock := mock_updatelistener.NewMockUpdateListener(ctrl) + syncClientMock := mock_syncservice.NewMockSyncClient(ctrl) + aclListMock := mock_list.NewMockACLList(ctrl) + createStorage := storage.TreeStorageCreatorFunc(func(payload storage.TreeStorageCreatePayload) (storage.TreeStorage, error) { + return nil, nil + }) + objTreeMock := mock_tree.NewMockObjectTree(ctrl) + createDerivedObjectTree = func(payload tree.ObjectTreeCreatePayload, l list.ACLList, create storage.TreeStorageCreatorFunc) (objTree tree.ObjectTree, err error) { + require.Equal(t, l, aclListMock) + return objTreeMock, nil + } + headUpdate := &spacesyncproto.ObjectSyncMessage{} + syncClientMock.EXPECT().CreateHeadUpdate(syncTreeMatcher{objTreeMock, syncClientMock, updateListenerMock}, gomock.Nil()).Return(headUpdate) + syncClientMock.EXPECT().BroadcastAsync(gomock.Eq(headUpdate)).Return(nil) + + _, err := DeriveSyncTree(ctx, tree.ObjectTreeCreatePayload{}, syncClientMock, updateListenerMock, aclListMock, createStorage) + require.NoError(t, err) +} + +func Test_CreateSyncTree(t *testing.T) { + ctx := context.Background() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + updateListenerMock := mock_updatelistener.NewMockUpdateListener(ctrl) + syncClientMock := mock_syncservice.NewMockSyncClient(ctrl) + aclListMock := mock_list.NewMockACLList(ctrl) + createStorage := storage.TreeStorageCreatorFunc(func(payload storage.TreeStorageCreatePayload) (storage.TreeStorage, error) { + return nil, nil + }) + objTreeMock := mock_tree.NewMockObjectTree(ctrl) + createObjectTree = func(payload tree.ObjectTreeCreatePayload, l list.ACLList, create storage.TreeStorageCreatorFunc) (objTree tree.ObjectTree, err error) { + require.Equal(t, l, aclListMock) + return objTreeMock, nil + } + headUpdate := &spacesyncproto.ObjectSyncMessage{} + syncClientMock.EXPECT().CreateHeadUpdate(syncTreeMatcher{objTreeMock, syncClientMock, updateListenerMock}, gomock.Nil()).Return(headUpdate) + syncClientMock.EXPECT().BroadcastAsync(gomock.Eq(headUpdate)).Return(nil) + + _, err := CreateSyncTree(ctx, tree.ObjectTreeCreatePayload{}, syncClientMock, updateListenerMock, aclListMock, createStorage) + require.NoError(t, err) +} + +func Test_BuildSyncTree(t *testing.T) { + ctx := context.Background() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + updateListenerMock := mock_updatelistener.NewMockUpdateListener(ctrl) + syncClientMock := mock_syncservice.NewMockSyncClient(ctrl) + aclListMock := mock_list.NewMockACLList(ctrl) + storageMock := mock_storage.NewMockTreeStorage(ctrl) + objTreeMock := mock_tree.NewMockObjectTree(ctrl) + buildObjectTree = func(store storage.TreeStorage, l list.ACLList) (objTree tree.ObjectTree, err error) { + require.Equal(t, aclListMock, l) + require.Equal(t, store, storageMock) + return objTreeMock, nil + } + headUpdate := &spacesyncproto.ObjectSyncMessage{} + syncClientMock.EXPECT().CreateHeadUpdate(syncTreeMatcher{objTreeMock, syncClientMock, updateListenerMock}, gomock.Nil()).Return(headUpdate) + syncClientMock.EXPECT().BroadcastAsyncOrSendResponsible(gomock.Eq(headUpdate)).Return(nil) + + tr, err := BuildSyncTree(ctx, syncClientMock, storageMock, updateListenerMock, aclListMock) + require.NoError(t, err) + + t.Run("AddRawChanges update", func(t *testing.T) { + changes := []*treechangeproto.RawTreeChangeWithId{{Id: "some"}} + expectedRes := tree.AddResult{ + Added: changes, + Mode: tree.Append, + } + objTreeMock.EXPECT().AddRawChanges(gomock.Any(), gomock.Eq(changes)). + Return(expectedRes, nil) + updateListenerMock.EXPECT().Update(tr) + + syncClientMock.EXPECT().CreateHeadUpdate(gomock.Eq(tr), gomock.Eq(changes)).Return(headUpdate) + syncClientMock.EXPECT().BroadcastAsync(gomock.Eq(headUpdate)).Return(nil) + res, err := tr.AddRawChanges(ctx, changes...) + require.NoError(t, err) + require.Equal(t, expectedRes, res) + }) + + t.Run("AddRawChanges rebuild", func(t *testing.T) { + changes := []*treechangeproto.RawTreeChangeWithId{{Id: "some"}} + expectedRes := tree.AddResult{ + Added: changes, + Mode: tree.Rebuild, + } + objTreeMock.EXPECT().AddRawChanges(gomock.Any(), gomock.Eq(changes)). + Return(expectedRes, nil) + updateListenerMock.EXPECT().Rebuild(tr) + + syncClientMock.EXPECT().CreateHeadUpdate(gomock.Eq(tr), gomock.Eq(changes)).Return(headUpdate) + syncClientMock.EXPECT().BroadcastAsync(gomock.Eq(headUpdate)).Return(nil) + res, err := tr.AddRawChanges(ctx, changes...) + require.NoError(t, err) + require.Equal(t, expectedRes, res) + }) + + t.Run("AddRawChanges nothing", func(t *testing.T) { + changes := []*treechangeproto.RawTreeChangeWithId{{Id: "some"}} + expectedRes := tree.AddResult{ + Added: changes, + Mode: tree.Nothing, + } + objTreeMock.EXPECT().AddRawChanges(gomock.Any(), gomock.Eq(changes)). + Return(expectedRes, nil) + + res, err := tr.AddRawChanges(ctx, changes...) + require.NoError(t, err) + require.Equal(t, expectedRes, res) + }) + + t.Run("AddContent", func(t *testing.T) { + changes := []*treechangeproto.RawTreeChangeWithId{{Id: "some"}} + content := tree.SignableChangeContent{ + Data: []byte("abcde"), + } + expectedRes := tree.AddResult{ + Mode: tree.Append, + Added: changes, + } + objTreeMock.EXPECT().AddContent(gomock.Any(), gomock.Eq(content)). + Return(expectedRes, nil) + + syncClientMock.EXPECT().CreateHeadUpdate(gomock.Eq(tr), gomock.Eq(changes)).Return(headUpdate) + syncClientMock.EXPECT().BroadcastAsync(gomock.Eq(headUpdate)).Return(nil) + res, err := tr.AddContent(ctx, content) + require.NoError(t, err) + require.Equal(t, expectedRes, res) + }) +} diff --git a/common/commonspace/synctree/updatelistener/mock_updatelistener/mock_updatelistener.go b/common/commonspace/synctree/updatelistener/mock_updatelistener/mock_updatelistener.go new file mode 100644 index 00000000..cb88dd6a --- /dev/null +++ b/common/commonspace/synctree/updatelistener/mock_updatelistener/mock_updatelistener.go @@ -0,0 +1,59 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/synctree/updatelistener (interfaces: UpdateListener) + +// Package mock_updatelistener is a generated GoMock package. +package mock_updatelistener + +import ( + reflect "reflect" + + tree "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/tree" + gomock "github.com/golang/mock/gomock" +) + +// MockUpdateListener is a mock of UpdateListener interface. +type MockUpdateListener struct { + ctrl *gomock.Controller + recorder *MockUpdateListenerMockRecorder +} + +// MockUpdateListenerMockRecorder is the mock recorder for MockUpdateListener. +type MockUpdateListenerMockRecorder struct { + mock *MockUpdateListener +} + +// NewMockUpdateListener creates a new mock instance. +func NewMockUpdateListener(ctrl *gomock.Controller) *MockUpdateListener { + mock := &MockUpdateListener{ctrl: ctrl} + mock.recorder = &MockUpdateListenerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockUpdateListener) EXPECT() *MockUpdateListenerMockRecorder { + return m.recorder +} + +// Rebuild mocks base method. +func (m *MockUpdateListener) Rebuild(arg0 tree.ObjectTree) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Rebuild", arg0) +} + +// Rebuild indicates an expected call of Rebuild. +func (mr *MockUpdateListenerMockRecorder) Rebuild(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Rebuild", reflect.TypeOf((*MockUpdateListener)(nil).Rebuild), arg0) +} + +// Update mocks base method. +func (m *MockUpdateListener) Update(arg0 tree.ObjectTree) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Update", arg0) +} + +// Update indicates an expected call of Update. +func (mr *MockUpdateListenerMockRecorder) Update(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockUpdateListener)(nil).Update), arg0) +} diff --git a/common/commonspace/synctree/updatelistener/updatelistener.go b/common/commonspace/synctree/updatelistener/updatelistener.go index 0a1a6659..49f6e0d5 100644 --- a/common/commonspace/synctree/updatelistener/updatelistener.go +++ b/common/commonspace/synctree/updatelistener/updatelistener.go @@ -1,3 +1,4 @@ +//go:generate mockgen -destination mock_updatelistener/mock_updatelistener.go github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/synctree/updatelistener UpdateListener package updatelistener import "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/tree"