Add settingsdocument tests

This commit is contained in:
mcrakhman 2022-11-29 18:53:04 +01:00 committed by Mikhail Iudin
parent 694973a4e3
commit df804c198d
No known key found for this signature in database
GPG Key ID: FAAAA8BAABDFF1C0
8 changed files with 290 additions and 75 deletions

View File

@ -3,7 +3,7 @@ package diffservice
import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app/logger"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/diffservice/mock_diffservice"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument/deletionstate"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument/deletionstate/mock_deletionstate"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage/mock_storage"
mock_storage2 "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage/mock_storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/ldiff"
@ -24,7 +24,7 @@ func TestDiffService(t *testing.T) {
treeStorageMock := mock_storage2.NewMockTreeStorage(ctrl)
diffMock := mock_ldiff.NewMockDiff(ctrl)
syncer := mock_diffservice.NewMockDiffSyncer(ctrl)
delState := deletionstate.NewDeletionState(storageMock)
delState := mock_deletionstate.NewMockDeletionState(ctrl)
syncPeriod := 1
initId := "initId"

View File

@ -5,10 +5,9 @@ import (
"fmt"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app/logger"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/remotediff"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument/deletionstate"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument/deletionstate/mock_deletionstate"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto/mock_spacesyncproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage/mock_storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/treegetter/mock_treegetter"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/net/peer"
@ -107,27 +106,22 @@ func TestDiffSyncer_Sync(t *testing.T) {
factory := spacesyncproto.ClientFactoryFunc(func(cc drpc.Conn) spacesyncproto.DRPCSpaceClient {
return clientMock
})
delState := deletionstate.NewDeletionState(stMock)
delState := mock_deletionstate.NewMockDeletionState(ctrl)
spaceId := "spaceId"
aclRootId := "aclRootId"
l := logger.NewNamed(spaceId)
diffSyncer := newDiffSyncer(spaceId, diffMock, connectorMock, cacheMock, stMock, factory, l)
delState.EXPECT().AddObserver(gomock.Any())
diffSyncer.Init(delState)
delStateAdd := func(deletedId string) {
stMock.EXPECT().TreeDeletedStatus(deletedId).Return("", nil)
stMock.EXPECT().SetTreeDeletedStatus(deletedId, storage.TreeDeletedStatusQueued)
diffMock.EXPECT().RemoveId(deletedId)
require.NoError(t, delState.Add([]string{deletedId}))
}
t.Run("diff syncer sync simple", func(t *testing.T) {
t.Run("diff syncer sync", func(t *testing.T) {
connectorMock.EXPECT().
GetResponsiblePeers(gomock.Any(), spaceId).
Return([]peer.Peer{mockPeer{}}, nil)
diffMock.EXPECT().
Diff(gomock.Any(), gomock.Eq(remotediff.NewRemoteDiff(spaceId, clientMock))).
Return([]string{"new"}, []string{"changed"}, nil, nil)
delState.EXPECT().FilterJoin(gomock.Any()).Return([]string{"new", "changed"})
for _, arg := range []string{"new", "changed"} {
cacheMock.EXPECT().
GetTree(gomock.Any(), spaceId, arg).
@ -136,22 +130,6 @@ func TestDiffSyncer_Sync(t *testing.T) {
require.NoError(t, diffSyncer.Sync(ctx))
})
t.Run("diff syncer sync filtered", func(t *testing.T) {
delStateAdd("changed")
connectorMock.EXPECT().
GetResponsiblePeers(gomock.Any(), spaceId).
Return([]peer.Peer{mockPeer{}}, nil)
diffMock.EXPECT().
Diff(gomock.Any(), gomock.Eq(remotediff.NewRemoteDiff(spaceId, clientMock))).
Return([]string{"new1", "new2"}, []string{"changed"}, nil, nil)
for _, arg := range []string{"new1", "new2"} {
cacheMock.EXPECT().
GetTree(gomock.Any(), spaceId, arg).
Return(nil, nil)
}
require.NoError(t, diffSyncer.Sync(ctx))
})
t.Run("diff syncer sync conf error", func(t *testing.T) {
connectorMock.EXPECT().
GetResponsiblePeers(gomock.Any(), spaceId).
@ -162,7 +140,7 @@ func TestDiffSyncer_Sync(t *testing.T) {
t.Run("deletion state remove objects", func(t *testing.T) {
deletedId := "id"
delStateAdd(deletedId)
delState.EXPECT().Exists(deletedId).Return(true)
// this should not result in any mock being called
diffSyncer.UpdateHeads(deletedId, []string{"someHead"})
@ -175,6 +153,7 @@ func TestDiffSyncer_Sync(t *testing.T) {
Id: newId,
Head: concatStrings(newHeads),
})
delState.EXPECT().Exists(newId).Return(false)
diffSyncer.UpdateHeads(newId, newHeads)
})

View File

@ -36,7 +36,7 @@ func (m *MockDiffSyncer) EXPECT() *MockDiffSyncerMockRecorder {
}
// Init mocks base method.
func (m *MockDiffSyncer) Init(arg0 *deletionstate.DeletionState) {
func (m *MockDiffSyncer) Init(arg0 deletionstate.DeletionState) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "Init", arg0)
}

View File

@ -1,6 +1,8 @@
//go:generate mockgen -destination mock_deletionstate/mock_deletionstate.go github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument/deletionstate DeletionState
package deletionstate
import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
"sync"
)
@ -14,6 +16,7 @@ type DeletionState interface {
Delete(id string) (err error)
Exists(id string) bool
FilterJoin(ids ...[]string) (filtered []string)
CreateDeleteChange(id string, isSnapshot bool) (res []byte, err error)
}
type deletionState struct {
@ -124,6 +127,21 @@ func (st *deletionState) FilterJoin(ids ...[]string) (filtered []string) {
return
}
func (st *deletionState) CreateDeleteChange(id string, isSnapshot bool) (res []byte, err error) {
content := &spacesyncproto.SpaceSettingsContent_ObjectDelete{
ObjectDelete: &spacesyncproto.ObjectDelete{Id: id},
}
change := &spacesyncproto.SettingsData{
Content: []*spacesyncproto.SpaceSettingsContent{
{content},
},
Snapshot: nil,
}
// TODO: add snapshot logic
res, err = change.Marshal()
return
}
func (st *deletionState) exists(id string) bool {
if _, exists := st.deleted[id]; exists {
return true

View File

@ -0,0 +1,136 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument/deletionstate (interfaces: DeletionState)
// Package mock_deletionstate is a generated GoMock package.
package mock_deletionstate
import (
reflect "reflect"
deletionstate "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument/deletionstate"
gomock "github.com/golang/mock/gomock"
)
// MockDeletionState is a mock of DeletionState interface.
type MockDeletionState struct {
ctrl *gomock.Controller
recorder *MockDeletionStateMockRecorder
}
// MockDeletionStateMockRecorder is the mock recorder for MockDeletionState.
type MockDeletionStateMockRecorder struct {
mock *MockDeletionState
}
// NewMockDeletionState creates a new mock instance.
func NewMockDeletionState(ctrl *gomock.Controller) *MockDeletionState {
mock := &MockDeletionState{ctrl: ctrl}
mock.recorder = &MockDeletionStateMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockDeletionState) EXPECT() *MockDeletionStateMockRecorder {
return m.recorder
}
// Add mocks base method.
func (m *MockDeletionState) Add(arg0 []string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Add", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// Add indicates an expected call of Add.
func (mr *MockDeletionStateMockRecorder) Add(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockDeletionState)(nil).Add), arg0)
}
// AddObserver mocks base method.
func (m *MockDeletionState) AddObserver(arg0 deletionstate.StateUpdateObserver) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "AddObserver", arg0)
}
// AddObserver indicates an expected call of AddObserver.
func (mr *MockDeletionStateMockRecorder) AddObserver(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddObserver", reflect.TypeOf((*MockDeletionState)(nil).AddObserver), arg0)
}
// CreateDeleteChange mocks base method.
func (m *MockDeletionState) CreateDeleteChange(arg0 string, arg1 bool) ([]byte, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateDeleteChange", arg0, arg1)
ret0, _ := ret[0].([]byte)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CreateDeleteChange indicates an expected call of CreateDeleteChange.
func (mr *MockDeletionStateMockRecorder) CreateDeleteChange(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDeleteChange", reflect.TypeOf((*MockDeletionState)(nil).CreateDeleteChange), arg0, arg1)
}
// Delete mocks base method.
func (m *MockDeletionState) Delete(arg0 string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Delete", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// Delete indicates an expected call of Delete.
func (mr *MockDeletionStateMockRecorder) Delete(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockDeletionState)(nil).Delete), arg0)
}
// Exists mocks base method.
func (m *MockDeletionState) Exists(arg0 string) bool {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Exists", arg0)
ret0, _ := ret[0].(bool)
return ret0
}
// Exists indicates an expected call of Exists.
func (mr *MockDeletionStateMockRecorder) Exists(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exists", reflect.TypeOf((*MockDeletionState)(nil).Exists), arg0)
}
// FilterJoin mocks base method.
func (m *MockDeletionState) FilterJoin(arg0 ...[]string) []string {
m.ctrl.T.Helper()
varargs := []interface{}{}
for _, a := range arg0 {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "FilterJoin", varargs...)
ret0, _ := ret[0].([]string)
return ret0
}
// FilterJoin indicates an expected call of FilterJoin.
func (mr *MockDeletionStateMockRecorder) FilterJoin(arg0 ...interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FilterJoin", reflect.TypeOf((*MockDeletionState)(nil).FilterJoin), arg0...)
}
// GetQueued mocks base method.
func (m *MockDeletionState) GetQueued() []string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetQueued")
ret0, _ := ret[0].([]string)
return ret0
}
// GetQueued indicates an expected call of GetQueued.
func (mr *MockDeletionStateMockRecorder) GetQueued() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQueued", reflect.TypeOf((*MockDeletionState)(nil).GetQueued))
}

View File

@ -21,33 +21,38 @@ func (p *provider) convert(decrypted []byte) (res any, err error) {
return deleteChange, nil
}
func (p *provider) processChange(change *tree.Change, tr tree.ObjectTree, startId string, ids []string) []string {
// ignoring root change which has empty model or startId change
if change.Model == nil || (change.Id == startId && startId != "") {
return ids
}
deleteChange := change.Model.(*spacesyncproto.SettingsData)
// getting data from snapshot if we start from it
if change.Id == tr.Root().Id {
ids = deleteChange.Snapshot.DeletedIds
return ids
}
// otherwise getting data from content
for _, cnt := range deleteChange.Content {
if cnt.GetObjectDelete() != nil {
ids = append(ids, cnt.GetObjectDelete().GetId())
}
}
return ids
}
func (p *provider) ProvideIds(tr tree.ObjectTree, startId string) (ids []string, lastId string, err error) {
processChange := func(change *tree.Change) bool {
// ignoring root change which has empty model or startId change
process := func(change *tree.Change) bool {
lastId = change.Id
if change.Model == nil || (change.Id == startId && startId != "") {
return true
}
deleteChange := change.Model.(*spacesyncproto.SettingsData)
// getting data from snapshot if we start from it
if change.Id == tr.Root().Id {
ids = deleteChange.Snapshot.DeletedIds
return true
}
// otherwise getting data from content
for _, cnt := range deleteChange.Content {
if cnt.GetObjectDelete() != nil {
ids = append(ids, cnt.GetObjectDelete().GetId())
}
}
ids = p.processChange(change, tr, startId, ids)
return true
}
if startId == "" {
err = tr.IterateFrom(tr.ID(), p.convert, processChange)
err = tr.IterateFrom(tr.ID(), p.convert, process)
} else {
err = tr.IterateFrom(startId, p.convert, processChange)
err = tr.IterateFrom(startId, p.convert, process)
}
return
}

View File

@ -6,7 +6,6 @@ import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/account"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app/logger"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument/deletionstate"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
spacestorage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/synctree"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/synctree/updatelistener"
@ -78,6 +77,8 @@ func NewSettingsDocument(deps Deps, spaceId string) (doc SettingsDocument) {
// this is needed mainly for testing
if deps.prov == nil {
s.prov = &provider{}
} else {
s.prov = deps.prov
}
doc = s
@ -113,6 +114,7 @@ func (s *settingsDocument) Init(ctx context.Context) (err error) {
if err != nil {
return
}
s.loop.Run()
return
}
@ -129,29 +131,24 @@ func (s *settingsDocument) DeleteObject(id string) (err error) {
return nil
}
content := &spacesyncproto.SpaceSettingsContent_ObjectDelete{
ObjectDelete: &spacesyncproto.ObjectDelete{Id: id},
}
change := &spacesyncproto.SettingsData{
Content: []*spacesyncproto.SpaceSettingsContent{
{content},
},
Snapshot: nil,
}
// TODO: add snapshot logic
res, err := change.Marshal()
res, err := s.deletionState.CreateDeleteChange(id, false)
if err != nil {
return
}
accountData := s.account.Account()
_, err = s.AddContent(context.Background(), tree.SignableChangeContent{
Data: res,
Key: s.account.Account().SignKey,
Identity: s.account.Account().Identity,
Key: accountData.SignKey,
Identity: accountData.Identity,
IsSnapshot: false,
IsEncrypted: false,
})
if err == nil {
s.Update(s)
if err != nil {
return
}
s.Update(s)
return
}

View File

@ -3,30 +3,53 @@ package settingsdocument
import (
"context"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/account/mock_account"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument/deletionstate"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument/deletionstate/mock_deletionstate"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument/mock_settingsdocument"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage/mock_storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/synctree"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/synctree/mock_synctree"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/synctree/updatelistener"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/treegetter/mock_treegetter"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/account"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/tree"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/keys/asymmetric/signingkey"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"
"sync"
"testing"
"time"
)
type testSyncTreeMock struct {
*mock_synctree.MockSyncTree
m sync.Mutex
}
func newTestObjMock(mockTree *mock_synctree.MockSyncTree) *testSyncTreeMock {
return &testSyncTreeMock{
MockSyncTree: mockTree,
}
}
func (t *testSyncTreeMock) Lock() {
t.m.Lock()
}
func (t *testSyncTreeMock) Unlock() {
t.m.Unlock()
}
type settingsFixture struct {
spaceId string
docId string
doc SettingsDocument
doc *settingsDocument
ctrl *gomock.Controller
treeGetter *mock_treegetter.MockTreeGetter
spaceStorage *mock_storage.MockSpaceStorage
provider *mock_settingsdocument.MockDeletedIdsProvider
deleter *mock_settingsdocument.MockDeleter
syncTree *mock_synctree.MockSyncTree
delState *deletionstate.DeletionState
delState *mock_deletionstate.MockDeletionState
account *mock_account.MockService
}
@ -38,14 +61,16 @@ func newSettingsFixture(t *testing.T) *settingsFixture {
acc := mock_account.NewMockService(ctrl)
treeGetter := mock_treegetter.NewMockTreeGetter(ctrl)
st := mock_storage.NewMockSpaceStorage(ctrl)
delState := deletionstate.NewDeletionState(st)
delState := mock_deletionstate.NewMockDeletionState(ctrl)
prov := mock_settingsdocument.NewMockDeletedIdsProvider(ctrl)
syncTree := mock_synctree.NewMockSyncTree(ctrl)
del := mock_settingsdocument.NewMockDeleter(ctrl)
delState.EXPECT().AddObserver(gomock.Any())
buildFunc := BuildTreeFunc(func(ctx context.Context, id string, listener updatelistener.UpdateListener) (synctree.SyncTree, error) {
require.Equal(t, docId, id)
return syncTree, nil
return newTestObjMock(syncTree), nil
})
deps := Deps{
@ -57,7 +82,7 @@ func newSettingsFixture(t *testing.T) *settingsFixture {
prov: prov,
del: del,
}
doc := NewSettingsDocument(deps, spaceId)
doc := NewSettingsDocument(deps, spaceId).(*settingsDocument)
return &settingsFixture{
spaceId: spaceId,
docId: docId,
@ -97,12 +122,67 @@ func TestSettingsDocument_DeleteObject(t *testing.T) {
fx.spaceStorage.EXPECT().SpaceSettingsId().Return(fx.docId)
fx.deleter.EXPECT().Delete()
fx.syncTree.EXPECT().Close().Return(nil)
err := fx.doc.Init(context.Background())
require.NoError(t, err)
time.Sleep(10 * time.Millisecond)
time.Sleep(100 * time.Millisecond)
delId := "delId"
fx.delState.EXPECT().Exists(delId).Return(false)
res := []byte("settingsData")
fx.delState.EXPECT().CreateDeleteChange(delId, false).Return(res, nil)
accountData := &account.AccountData{
Identity: []byte("id"),
PeerKey: nil,
SignKey: &signingkey.Ed25519PrivateKey{},
EncKey: nil,
}
fx.account.EXPECT().Account().Return(accountData)
fx.syncTree.EXPECT().AddContent(gomock.Any(), tree.SignableChangeContent{
Data: res,
Key: accountData.SignKey,
Identity: accountData.Identity,
IsSnapshot: false,
IsEncrypted: false,
}).Return(tree.AddResult{}, nil)
lastChangeId := "someId"
retIds := []string{"id1", "id2"}
fx.doc.lastChangeId = lastChangeId
fx.provider.EXPECT().ProvideIds(gomock.Not(nil), lastChangeId).Return(retIds, retIds[len(retIds)-1], nil)
fx.delState.EXPECT().Add(retIds).Return(nil)
err = fx.doc.DeleteObject(delId)
require.NoError(t, err)
require.Equal(t, retIds[len(retIds)-1], fx.doc.lastChangeId)
fx.syncTree.EXPECT().Close().Return(nil)
err = fx.doc.Close()
require.NoError(t, err)
}
func TestSettingsDocument_Rebuild(t *testing.T) {
fx := newSettingsFixture(t)
defer fx.stop()
fx.spaceStorage.EXPECT().SpaceSettingsId().Return(fx.docId)
fx.deleter.EXPECT().Delete()
err := fx.doc.Init(context.Background())
require.NoError(t, err)
time.Sleep(100 * time.Millisecond)
lastChangeId := "someId"
retIds := []string{"id1", "id2"}
fx.doc.lastChangeId = lastChangeId
fx.provider.EXPECT().ProvideIds(gomock.Not(nil), "").Return(retIds, retIds[len(retIds)-1], nil)
fx.delState.EXPECT().Add(retIds).Return(nil)
fx.doc.Rebuild(fx.doc)
require.Equal(t, retIds[len(retIds)-1], fx.doc.lastChangeId)
fx.syncTree.EXPECT().Close().Return(nil)
err = fx.doc.Close()
require.NoError(t, err)
}