Tests and refactoring

This commit is contained in:
mcrakhman 2022-11-29 16:54:04 +01:00 committed by Mikhail Iudin
parent b24f79645d
commit 694973a4e3
No known key found for this signature in database
GPG Key ID: FAAAA8BAABDFF1C0
9 changed files with 187 additions and 31 deletions

View File

@ -21,7 +21,7 @@ type DiffService interface {
RemoveObjects(ids []string) RemoveObjects(ids []string)
AllIds() []string AllIds() []string
Init(objectIds []string, deletionState *deletionstate.DeletionState) Init(objectIds []string, deletionState deletionstate.DeletionState)
Close() (err error) Close() (err error)
} }
@ -61,7 +61,7 @@ func NewDiffService(
} }
} }
func (d *diffService) Init(objectIds []string, deletionState *deletionstate.DeletionState) { func (d *diffService) Init(objectIds []string, deletionState deletionstate.DeletionState) {
d.fillDiff(objectIds) d.fillDiff(objectIds)
d.syncer.Init(deletionState) d.syncer.Init(deletionState)
d.periodicSync.Run() d.periodicSync.Run()

View File

@ -19,7 +19,7 @@ type DiffSyncer interface {
Sync(ctx context.Context) error Sync(ctx context.Context) error
RemoveObjects(ids []string) RemoveObjects(ids []string)
UpdateHeads(id string, heads []string) UpdateHeads(id string, heads []string)
Init(deletionState *deletionstate.DeletionState) Init(deletionState deletionstate.DeletionState)
} }
func newDiffSyncer( func newDiffSyncer(
@ -49,10 +49,10 @@ type diffSyncer struct {
storage storage.SpaceStorage storage storage.SpaceStorage
clientFactory spacesyncproto.ClientFactory clientFactory spacesyncproto.ClientFactory
log *zap.Logger log *zap.Logger
deletionState *deletionstate.DeletionState deletionState deletionstate.DeletionState
} }
func (d *diffSyncer) Init(deletionState *deletionstate.DeletionState) { func (d *diffSyncer) Init(deletionState deletionstate.DeletionState) {
d.deletionState = deletionState d.deletionState = deletionState
d.deletionState.AddObserver(d.RemoveObjects) d.deletionState.AddObserver(d.RemoveObjects)
} }

View File

@ -8,17 +8,21 @@ import (
"go.uber.org/zap" "go.uber.org/zap"
) )
type Deleter interface {
Delete()
}
type deleter struct { type deleter struct {
st storage.SpaceStorage st storage.SpaceStorage
state *deletionstate.DeletionState state deletionstate.DeletionState
getter treegetter.TreeGetter getter treegetter.TreeGetter
} }
func newDeleter(st storage.SpaceStorage, state *deletionstate.DeletionState, getter treegetter.TreeGetter) *deleter { func newDeleter(st storage.SpaceStorage, state deletionstate.DeletionState, getter treegetter.TreeGetter) Deleter {
return &deleter{st, state, getter} return &deleter{st, state, getter}
} }
func (d *deleter) delete() { func (d *deleter) Delete() {
allQueued := d.state.GetQueued() allQueued := d.state.GetQueued()
for _, id := range allQueued { for _, id := range allQueued {
err := d.getter.DeleteTree(context.Background(), d.st.Id(), id) err := d.getter.DeleteTree(context.Background(), d.st.Id(), id)

View File

@ -7,7 +7,16 @@ import (
type StateUpdateObserver func(ids []string) type StateUpdateObserver func(ids []string)
type DeletionState struct { type DeletionState interface {
AddObserver(observer StateUpdateObserver)
Add(ids []string) (err error)
GetQueued() (ids []string)
Delete(id string) (err error)
Exists(id string) bool
FilterJoin(ids ...[]string) (filtered []string)
}
type deletionState struct {
sync.RWMutex sync.RWMutex
queued map[string]struct{} queued map[string]struct{}
deleted map[string]struct{} deleted map[string]struct{}
@ -15,21 +24,21 @@ type DeletionState struct {
storage storage.SpaceStorage storage storage.SpaceStorage
} }
func NewDeletionState(storage storage.SpaceStorage) *DeletionState { func NewDeletionState(storage storage.SpaceStorage) DeletionState {
return &DeletionState{ return &deletionState{
queued: map[string]struct{}{}, queued: map[string]struct{}{},
deleted: map[string]struct{}{}, deleted: map[string]struct{}{},
storage: storage, storage: storage,
} }
} }
func (st *DeletionState) AddObserver(observer StateUpdateObserver) { func (st *deletionState) AddObserver(observer StateUpdateObserver) {
st.Lock() st.Lock()
defer st.Unlock() defer st.Unlock()
st.stateUpdateObservers = append(st.stateUpdateObservers, observer) st.stateUpdateObservers = append(st.stateUpdateObservers, observer)
} }
func (st *DeletionState) Add(ids []string) (err error) { func (st *deletionState) Add(ids []string) (err error) {
st.Lock() st.Lock()
defer func() { defer func() {
st.Unlock() st.Unlock()
@ -71,7 +80,7 @@ func (st *DeletionState) Add(ids []string) (err error) {
return return
} }
func (st *DeletionState) GetQueued() (ids []string) { func (st *deletionState) GetQueued() (ids []string) {
st.RLock() st.RLock()
defer st.RUnlock() defer st.RUnlock()
ids = make([]string, 0, len(st.queued)) ids = make([]string, 0, len(st.queued))
@ -81,7 +90,7 @@ func (st *DeletionState) GetQueued() (ids []string) {
return return
} }
func (st *DeletionState) Delete(id string) (err error) { func (st *deletionState) Delete(id string) (err error) {
st.Lock() st.Lock()
defer st.Unlock() defer st.Unlock()
delete(st.queued, id) delete(st.queued, id)
@ -93,13 +102,13 @@ func (st *DeletionState) Delete(id string) (err error) {
return return
} }
func (st *DeletionState) Exists(id string) bool { func (st *deletionState) Exists(id string) bool {
st.RLock() st.RLock()
defer st.RUnlock() defer st.RUnlock()
return st.exists(id) return st.exists(id)
} }
func (st *DeletionState) FilterJoin(ids ...[]string) (filtered []string) { func (st *deletionState) FilterJoin(ids ...[]string) (filtered []string) {
st.RLock() st.RLock()
defer st.RUnlock() defer st.RUnlock()
filter := func(ids []string) { filter := func(ids []string) {
@ -115,7 +124,7 @@ func (st *DeletionState) FilterJoin(ids ...[]string) (filtered []string) {
return return
} }
func (st *DeletionState) exists(id string) bool { func (st *deletionState) exists(id string) bool {
if _, exists := st.deleted[id]; exists { if _, exists := st.deleted[id]; exists {
return true return true
} }

View File

@ -1,4 +1,3 @@
//go:generate mockgen -destination mock_settingsdocument/mock_settingsdocument.go github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument DeletedIdsProvider
package settingsdocument package settingsdocument
import ( import (

View File

@ -1,5 +1,5 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument (interfaces: DeletedIdsProvider) // Source: github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument (interfaces: DeletedIdsProvider,Deleter)
// Package mock_settingsdocument is a generated GoMock package. // Package mock_settingsdocument is a generated GoMock package.
package mock_settingsdocument package mock_settingsdocument
@ -49,3 +49,38 @@ func (mr *MockDeletedIdsProviderMockRecorder) ProvideIds(arg0, arg1 interface{})
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProvideIds", reflect.TypeOf((*MockDeletedIdsProvider)(nil).ProvideIds), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProvideIds", reflect.TypeOf((*MockDeletedIdsProvider)(nil).ProvideIds), arg0, arg1)
} }
// MockDeleter is a mock of Deleter interface.
type MockDeleter struct {
ctrl *gomock.Controller
recorder *MockDeleterMockRecorder
}
// MockDeleterMockRecorder is the mock recorder for MockDeleter.
type MockDeleterMockRecorder struct {
mock *MockDeleter
}
// NewMockDeleter creates a new mock instance.
func NewMockDeleter(ctrl *gomock.Controller) *MockDeleter {
mock := &MockDeleter{ctrl: ctrl}
mock.recorder = &MockDeleterMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockDeleter) EXPECT() *MockDeleterMockRecorder {
return m.recorder
}
// Delete mocks base method.
func (m *MockDeleter) Delete() {
m.ctrl.T.Helper()
m.ctrl.Call(m, "Delete")
}
// Delete indicates an expected call of Delete.
func (mr *MockDeleterMockRecorder) Delete() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockDeleter)(nil).Delete))
}

View File

@ -1,3 +1,4 @@
//go:generate mockgen -destination mock_settingsdocument/mock_settingsdocument.go github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument DeletedIdsProvider,Deleter
package settingsdocument package settingsdocument
import ( import (
@ -29,9 +30,10 @@ type Deps struct {
Account account.Service Account account.Service
TreeGetter treegetter.TreeGetter TreeGetter treegetter.TreeGetter
Store spacestorage.SpaceStorage Store spacestorage.SpaceStorage
DeletionState *deletionstate.DeletionState DeletionState deletionstate.DeletionState
// prov exists mainly for the ease of testing // testing dependencies
prov DeletedIdsProvider prov DeletedIdsProvider
del Deleter
} }
type settingsDocument struct { type settingsDocument struct {
@ -44,14 +46,20 @@ type settingsDocument struct {
buildFunc BuildTreeFunc buildFunc BuildTreeFunc
loop *deleteLoop loop *deleteLoop
deletionState *deletionstate.DeletionState deletionState deletionstate.DeletionState
lastChangeId string lastChangeId string
} }
func NewSettingsDocument(deps Deps, spaceId string) (doc SettingsDocument, err error) { func NewSettingsDocument(deps Deps, spaceId string) (doc SettingsDocument) {
deleter := newDeleter(deps.Store, deps.DeletionState, deps.TreeGetter) var deleter Deleter
if deps.del == nil {
deleter = newDeleter(deps.Store, deps.DeletionState, deps.TreeGetter)
} else {
deleter = deps.del
}
loop := newDeleteLoop(func() { loop := newDeleteLoop(func() {
deleter.delete() deleter.Delete()
}) })
deps.DeletionState.AddObserver(func(ids []string) { deps.DeletionState.AddObserver(func(ids []string) {
loop.notify() loop.notify()
@ -89,10 +97,12 @@ func (s *settingsDocument) updateIds(tr tree.ObjectTree, lastChangeId string) {
} }
} }
// Update is called as part of UpdateListener interface
func (s *settingsDocument) Update(tr tree.ObjectTree) { func (s *settingsDocument) Update(tr tree.ObjectTree) {
s.updateIds(tr, s.lastChangeId) s.updateIds(tr, s.lastChangeId)
} }
// Rebuild is called as part of UpdateListener interface (including when the object is built for the first time, e.g. on Init call)
func (s *settingsDocument) Rebuild(tr tree.ObjectTree) { func (s *settingsDocument) Rebuild(tr tree.ObjectTree) {
// at initial build "s" may not contain the object tree, so it is safer to provide it from the function parameter // at initial build "s" may not contain the object tree, so it is safer to provide it from the function parameter
s.updateIds(tr, "") s.updateIds(tr, "")
@ -128,6 +138,7 @@ func (s *settingsDocument) DeleteObject(id string) (err error) {
}, },
Snapshot: nil, Snapshot: nil,
} }
// TODO: add snapshot logic
res, err := change.Marshal() res, err := change.Marshal()
if err != nil { if err != nil {
return return

View File

@ -1,7 +1,108 @@
package settingsdocument package settingsdocument
import "testing" 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/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/golang/mock/gomock"
"github.com/stretchr/testify/require"
"testing"
"time"
)
type settingsFixture struct {
spaceId string
docId string
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
account *mock_account.MockService
}
func newSettingsFixture(t *testing.T) *settingsFixture {
spaceId := "spaceId"
docId := "documentId"
ctrl := gomock.NewController(t)
acc := mock_account.NewMockService(ctrl)
treeGetter := mock_treegetter.NewMockTreeGetter(ctrl)
st := mock_storage.NewMockSpaceStorage(ctrl)
delState := deletionstate.NewDeletionState(st)
prov := mock_settingsdocument.NewMockDeletedIdsProvider(ctrl)
syncTree := mock_synctree.NewMockSyncTree(ctrl)
del := mock_settingsdocument.NewMockDeleter(ctrl)
buildFunc := BuildTreeFunc(func(ctx context.Context, id string, listener updatelistener.UpdateListener) (synctree.SyncTree, error) {
require.Equal(t, docId, id)
return syncTree, nil
})
deps := Deps{
BuildFunc: buildFunc,
Account: acc,
TreeGetter: treeGetter,
Store: st,
DeletionState: delState,
prov: prov,
del: del,
}
doc := NewSettingsDocument(deps, spaceId)
return &settingsFixture{
spaceId: spaceId,
docId: docId,
doc: doc,
ctrl: ctrl,
treeGetter: treeGetter,
spaceStorage: st,
provider: prov,
deleter: del,
syncTree: syncTree,
account: acc,
delState: delState,
}
}
func (fx *settingsFixture) stop() {
fx.ctrl.Finish()
}
func TestSettingsDocument_Init(t *testing.T) { func TestSettingsDocument_Init(t *testing.T) {
fx := newSettingsFixture(t)
defer fx.stop()
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)
err = fx.doc.Close()
require.NoError(t, err)
}
func TestSettingsDocument_DeleteObject(t *testing.T) {
fx := newSettingsFixture(t)
defer fx.stop()
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)
err = fx.doc.Close()
require.NoError(t, err)
} }

View File

@ -167,10 +167,7 @@ func (s *space) Init(ctx context.Context) (err error) {
Store: s.storage, Store: s.storage,
DeletionState: deletionState, DeletionState: deletionState,
} }
s.settingsDocument, err = settingsdocument.NewSettingsDocument(deps, s.id) s.settingsDocument = settingsdocument.NewSettingsDocument(deps, s.id)
if err != nil {
return
}
objectGetter := newCommonSpaceGetter(s.id, s.aclList, s.cache, s.settingsDocument) objectGetter := newCommonSpaceGetter(s.id, s.aclList, s.cache, s.settingsDocument)
s.syncService.Init(objectGetter) s.syncService.Init(objectGetter)