diff --git a/commonspace/headsync/diffsyncer_test.go b/commonspace/headsync/diffsyncer_test.go index 4c6fcdc9..b49c2178 100644 --- a/commonspace/headsync/diffsyncer_test.go +++ b/commonspace/headsync/diffsyncer_test.go @@ -1,7 +1,14 @@ package headsync import ( + "bytes" "context" + "fmt" + "github.com/anyproto/any-sync/app/ldiff" + "github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto" + "github.com/anyproto/any-sync/commonspace/object/acl/liststorage/mock_liststorage" + "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" + "github.com/anyproto/any-sync/commonspace/object/tree/treestorage/mock_treestorage" "github.com/anyproto/any-sync/commonspace/spacesyncproto" "github.com/anyproto/any-sync/net/peer" "github.com/golang/mock/gomock" @@ -11,6 +18,42 @@ import ( "time" ) +type pushSpaceRequestMatcher struct { + spaceId string + aclRootId string + settingsId string + credential []byte + spaceHeader *spacesyncproto.RawSpaceHeaderWithId +} + +func newPushSpaceRequestMatcher( + spaceId string, + aclRootId string, + settingsId string, + credential []byte, + spaceHeader *spacesyncproto.RawSpaceHeaderWithId) *pushSpaceRequestMatcher { + return &pushSpaceRequestMatcher{ + spaceId: spaceId, + aclRootId: aclRootId, + settingsId: settingsId, + credential: credential, + spaceHeader: spaceHeader, + } +} + +func (p pushSpaceRequestMatcher) Matches(x interface{}) bool { + res, ok := x.(*spacesyncproto.SpacePushRequest) + if !ok { + return false + } + + return res.Payload.AclPayloadId == p.aclRootId && res.Payload.SpaceHeader == p.spaceHeader && res.Payload.SpaceSettingsPayloadId == p.settingsId && bytes.Equal(p.credential, res.Credential) +} + +func (p pushSpaceRequestMatcher) String() string { + return "" +} + type mockPeer struct { } @@ -58,12 +101,12 @@ func (fx *headSyncFixture) initDiffSyncer(t *testing.T) { } func TestDiffSyncer(t *testing.T) { - fx := newHeadSyncFixture(t) - fx.initDiffSyncer(t) - defer fx.stop() ctx := context.Background() t.Run("diff syncer sync", func(t *testing.T) { + fx := newHeadSyncFixture(t) + fx.initDiffSyncer(t) + defer fx.stop() mPeer := mockPeer{} fx.peerManagerMock.EXPECT(). GetResponsiblePeers(gomock.Any()). @@ -77,6 +120,122 @@ func TestDiffSyncer(t *testing.T) { fx.treeSyncerMock.EXPECT().SyncAll(gomock.Any(), mPeer.Id(), []string{"changed"}, []string{"new"}).Return(nil) require.NoError(t, fx.diffSyncer.Sync(ctx)) }) + + t.Run("diff syncer sync conf error", func(t *testing.T) { + fx := newHeadSyncFixture(t) + fx.initDiffSyncer(t) + defer fx.stop() + ctx := context.Background() + fx.peerManagerMock.EXPECT(). + GetResponsiblePeers(gomock.Any()). + Return(nil, fmt.Errorf("some error")) + + require.Error(t, fx.diffSyncer.Sync(ctx)) + }) + + t.Run("deletion state remove objects", func(t *testing.T) { + fx := newHeadSyncFixture(t) + fx.initDiffSyncer(t) + defer fx.stop() + deletedId := "id" + fx.deletionStateMock.EXPECT().Exists(deletedId).Return(true) + + // this should not result in any mock being called + fx.diffSyncer.UpdateHeads(deletedId, []string{"someHead"}) + }) + + t.Run("update heads updates diff", func(t *testing.T) { + fx := newHeadSyncFixture(t) + fx.initDiffSyncer(t) + defer fx.stop() + newId := "newId" + newHeads := []string{"h1", "h2"} + hash := "hash" + fx.diffMock.EXPECT().Set(ldiff.Element{ + Id: newId, + Head: concatStrings(newHeads), + }) + fx.diffMock.EXPECT().Hash().Return(hash) + fx.deletionStateMock.EXPECT().Exists(newId).Return(false) + fx.storageMock.EXPECT().WriteSpaceHash(hash) + fx.diffSyncer.UpdateHeads(newId, newHeads) + }) + + t.Run("diff syncer sync space missing", func(t *testing.T) { + fx := newHeadSyncFixture(t) + fx.initDiffSyncer(t) + defer fx.stop() + aclStorageMock := mock_liststorage.NewMockListStorage(fx.ctrl) + settingsStorage := mock_treestorage.NewMockTreeStorage(fx.ctrl) + settingsId := "settingsId" + aclRootId := "aclRootId" + aclRoot := &aclrecordproto.RawAclRecordWithId{ + Id: aclRootId, + } + settingsRoot := &treechangeproto.RawTreeChangeWithId{ + Id: settingsId, + } + spaceHeader := &spacesyncproto.RawSpaceHeaderWithId{} + spaceSettingsId := "spaceSettingsId" + credential := []byte("credential") + + fx.peerManagerMock.EXPECT(). + GetResponsiblePeers(gomock.Any()). + Return([]peer.Peer{mockPeer{}}, nil) + fx.diffMock.EXPECT(). + Diff(gomock.Any(), gomock.Eq(NewRemoteDiff(fx.spaceState.SpaceId, fx.clientMock))). + Return(nil, nil, nil, spacesyncproto.ErrSpaceMissing) + + fx.storageMock.EXPECT().AclStorage().Return(aclStorageMock, nil) + fx.storageMock.EXPECT().SpaceHeader().Return(spaceHeader, nil) + fx.storageMock.EXPECT().SpaceSettingsId().Return(spaceSettingsId) + fx.storageMock.EXPECT().TreeStorage(spaceSettingsId).Return(settingsStorage, nil) + + settingsStorage.EXPECT().Root().Return(settingsRoot, nil) + aclStorageMock.EXPECT(). + Root(). + Return(aclRoot, nil) + fx.credentialProviderMock.EXPECT(). + GetCredential(gomock.Any(), spaceHeader). + Return(credential, nil) + fx.clientMock.EXPECT(). + SpacePush(gomock.Any(), newPushSpaceRequestMatcher(fx.spaceState.SpaceId, aclRootId, settingsId, credential, spaceHeader)). + Return(nil, nil) + fx.peerManagerMock.EXPECT().SendPeer(gomock.Any(), "peerId", gomock.Any()) + + require.NoError(t, fx.diffSyncer.Sync(ctx)) + }) + + t.Run("diff syncer sync unexpected", func(t *testing.T) { + fx := newHeadSyncFixture(t) + fx.initDiffSyncer(t) + defer fx.stop() + fx.peerManagerMock.EXPECT(). + GetResponsiblePeers(gomock.Any()). + Return([]peer.Peer{mockPeer{}}, nil) + fx.diffMock.EXPECT(). + Diff(gomock.Any(), gomock.Eq(NewRemoteDiff(fx.spaceState.SpaceId, fx.clientMock))). + Return(nil, nil, nil, spacesyncproto.ErrUnexpected) + + require.NoError(t, fx.diffSyncer.Sync(ctx)) + }) + + t.Run("diff syncer sync space is deleted error", func(t *testing.T) { + fx := newHeadSyncFixture(t) + fx.initDiffSyncer(t) + defer fx.stop() + mPeer := mockPeer{} + fx.peerManagerMock.EXPECT(). + GetResponsiblePeers(gomock.Any()). + Return([]peer.Peer{mPeer}, nil) + fx.diffMock.EXPECT(). + Diff(gomock.Any(), gomock.Eq(NewRemoteDiff(fx.spaceState.SpaceId, fx.clientMock))). + Return(nil, nil, nil, spacesyncproto.ErrSpaceIsDeleted) + fx.storageMock.EXPECT().SpaceSettingsId().Return("settingsId") + fx.treeSyncerMock.EXPECT().SyncAll(gomock.Any(), mPeer.Id(), []string{"settingsId"}, nil).Return(nil) + + require.NoError(t, fx.diffSyncer.Sync(ctx)) + }) } //