diff --git a/.mockery.yaml b/.mockery.yaml index 748322769..58956d2c4 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -193,4 +193,7 @@ packages: Updater: github.com/anyproto/anytype-heart/core/syncstatus/detailsupdater: interfaces: - SpaceStatusUpdater: \ No newline at end of file + SpaceStatusUpdater: + github.com/anyproto/anytype-heart/core/syncstatus/spacesyncstatus: + interfaces: + SpaceIdGetter: \ No newline at end of file diff --git a/core/block/object/treesyncer/treesyncer_test.go b/core/block/object/treesyncer/treesyncer_test.go index 90da9cf15..6749765df 100644 --- a/core/block/object/treesyncer/treesyncer_test.go +++ b/core/block/object/treesyncer/treesyncer_test.go @@ -198,7 +198,7 @@ func TestTreeSyncer(t *testing.T) { fx.nodeConf.EXPECT().NodeIds(spaceId).Return([]string{peerId}) fx.checker.EXPECT().IsPeerOffline(peerId).Return(true) fx.syncStatus.EXPECT().RemoveAllExcept(peerId, []string{existingId}).Return() - fx.syncDetailsUpdater.EXPECT().UpdateDetails("existing", domain.Offline, domain.Null, "spaceId").Return() + fx.syncDetailsUpdater.EXPECT().UpdateDetails([]string{"existing"}, domain.Offline, domain.Null, "spaceId").Return() fx.StartSync() err := fx.SyncAll(context.Background(), peerId, []string{existingId}, []string{missingId}) @@ -218,8 +218,8 @@ func TestTreeSyncer(t *testing.T) { fx.nodeConf.EXPECT().NodeIds(spaceId).Return([]string{peerId}) fx.checker.EXPECT().IsPeerOffline(peerId).Return(false) fx.syncStatus.EXPECT().RemoveAllExcept(peerId, []string{existingId}).Return() - fx.syncDetailsUpdater.EXPECT().UpdateDetails("existing", domain.Synced, domain.Null, "spaceId").Return() - fx.syncDetailsUpdater.EXPECT().UpdateDetails("existing", domain.Syncing, domain.Null, "spaceId").Return() + fx.syncDetailsUpdater.EXPECT().UpdateDetails([]string{"existing"}, domain.Synced, domain.Null, "spaceId").Return() + fx.syncDetailsUpdater.EXPECT().UpdateDetails([]string{"existing"}, domain.Syncing, domain.Null, "spaceId").Return() fx.StartSync() err := fx.SyncAll(context.Background(), peerId, []string{existingId}, []string{missingId}) diff --git a/core/payments/cache/mock_cache/mock_CacheService.go b/core/payments/cache/mock_cache/mock_CacheService.go index cedd84e0f..b1a45fd02 100644 --- a/core/payments/cache/mock_cache/mock_CacheService.go +++ b/core/payments/cache/mock_cache/mock_CacheService.go @@ -225,17 +225,17 @@ func (_c *MockCacheService_CacheGet_Call) RunAndReturn(run func() (*pb.RpcMember return _c } -// CacheSet provides a mock function with given fields: status, tiers -func (_m *MockCacheService) CacheSet(status *pb.RpcMembershipGetStatusResponse, tiers *pb.RpcMembershipGetTiersResponse) error { - ret := _m.Called(status, tiers) +// CacheSet provides a mock function with given fields: status, tiers, subscriptionEnds +func (_m *MockCacheService) CacheSet(status *pb.RpcMembershipGetStatusResponse, tiers *pb.RpcMembershipGetTiersResponse, subscriptionEnds time.Time) error { + ret := _m.Called(status, tiers, subscriptionEnds) if len(ret) == 0 { panic("no return value specified for CacheSet") } var r0 error - if rf, ok := ret.Get(0).(func(*pb.RpcMembershipGetStatusResponse, *pb.RpcMembershipGetTiersResponse) error); ok { - r0 = rf(status, tiers) + if rf, ok := ret.Get(0).(func(*pb.RpcMembershipGetStatusResponse, *pb.RpcMembershipGetTiersResponse, time.Time) error); ok { + r0 = rf(status, tiers, subscriptionEnds) } else { r0 = ret.Error(0) } @@ -251,11 +251,12 @@ type MockCacheService_CacheSet_Call struct { // CacheSet is a helper method to define mock.On call // - status *pb.RpcMembershipGetStatusResponse // - tiers *pb.RpcMembershipGetTiersResponse -func (_e *MockCacheService_Expecter) CacheSet(status interface{}, tiers interface{}) *MockCacheService_CacheSet_Call { - return &MockCacheService_CacheSet_Call{Call: _e.mock.On("CacheSet", status, tiers)} +// - subscriptionEnds time.Time +func (_e *MockCacheService_Expecter) CacheSet(status interface{}, tiers interface{}, subscriptionEnds interface{}) *MockCacheService_CacheSet_Call { + return &MockCacheService_CacheSet_Call{Call: _e.mock.On("CacheSet", status, tiers, subscriptionEnds)} } -func (_c *MockCacheService_CacheSet_Call) Run(run func(status *pb.RpcMembershipGetStatusResponse, tiers *pb.RpcMembershipGetTiersResponse)) *MockCacheService_CacheSet_Call { +func (_c *MockCacheService_CacheSet_Call) Run(run func(status *pb.RpcMembershipGetStatusResponse, tiers *pb.RpcMembershipGetTiersResponse, subscriptionEnds time.Time)) *MockCacheService_CacheSet_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(*pb.RpcMembershipGetStatusResponse), args[1].(*pb.RpcMembershipGetTiersResponse)) }) diff --git a/core/syncstatus/detailsupdater/updater.go b/core/syncstatus/detailsupdater/updater.go index fa10bb258..101822c28 100644 --- a/core/syncstatus/detailsupdater/updater.go +++ b/core/syncstatus/detailsupdater/updater.go @@ -152,9 +152,6 @@ func (u *syncStatusUpdater) updateObjectDetails(syncStatusDetails *syncStatusDet if err != nil { return err } - if record == nil { - return nil - } return u.setObjectDetails(syncStatusDetails, record.Details, objectId) } diff --git a/core/syncstatus/detailsupdater/updater_test.go b/core/syncstatus/detailsupdater/updater_test.go index 0c58e05e1..f67f14522 100644 --- a/core/syncstatus/detailsupdater/updater_test.go +++ b/core/syncstatus/detailsupdater/updater_test.go @@ -12,7 +12,7 @@ import ( "github.com/anyproto/anytype-heart/core/block/editor" "github.com/anyproto/anytype-heart/core/block/editor/smartblock/smarttest" - "github.com/anyproto/anytype-heart/core/domain" + domain "github.com/anyproto/anytype-heart/core/domain" "github.com/anyproto/anytype-heart/core/syncstatus/detailsupdater/mock_detailsupdater" "github.com/anyproto/anytype-heart/core/syncstatus/filesyncstatus" "github.com/anyproto/anytype-heart/pkg/lib/bundle" @@ -37,9 +37,10 @@ func TestSyncStatusUpdater_UpdateDetails(t *testing.T) { }) // when - fixture.updater.updateDetails(&syncStatusDetails{[]string{"id"}, domain.Synced, domain.Null, "spaceId"}) + err := fixture.updater.updateObjectDetails(&syncStatusDetails{[]string{"id"}, domain.Synced, domain.Null, "spaceId"}, "id") // then + assert.Nil(t, err) fixture.service.AssertNotCalled(t, "Get") }) t.Run("update sync status and date - details exist in store", func(t *testing.T) { @@ -56,7 +57,10 @@ func TestSyncStatusUpdater_UpdateDetails(t *testing.T) { // when fixture.statusUpdater.EXPECT().SendUpdate(domain.MakeSyncStatus("spaceId", domain.Synced, domain.Null, domain.Objects)) - fixture.updater.updateDetails(&syncStatusDetails{[]string{"id"}, domain.Synced, domain.Null, "spaceId"}) + err := fixture.updater.updateObjectDetails(&syncStatusDetails{[]string{"id"}, domain.Synced, domain.Null, "spaceId"}, "id") + + // then + assert.Nil(t, err) }) t.Run("update sync status and date - object not exist in cache", func(t *testing.T) { // given @@ -74,7 +78,10 @@ func TestSyncStatusUpdater_UpdateDetails(t *testing.T) { // when fixture.statusUpdater.EXPECT().SendUpdate(domain.MakeSyncStatus("spaceId", domain.Synced, domain.Null, domain.Objects)) - fixture.updater.updateDetails(&syncStatusDetails{[]string{"id"}, domain.Synced, domain.Null, "spaceId"}) + err := fixture.updater.updateObjectDetails(&syncStatusDetails{[]string{"id"}, domain.Synced, domain.Null, "spaceId"}, "id") + + // then + assert.Nil(t, err) }) t.Run("update sync status and date - object exist in cache", func(t *testing.T) { // given @@ -86,7 +93,10 @@ func TestSyncStatusUpdater_UpdateDetails(t *testing.T) { // when fixture.statusUpdater.EXPECT().SendUpdate(domain.MakeSyncStatus("spaceId", domain.Synced, domain.Null, domain.Objects)) - fixture.updater.updateDetails(&syncStatusDetails{[]string{"id"}, domain.Synced, domain.Null, "spaceId"}) + err := fixture.updater.updateObjectDetails(&syncStatusDetails{[]string{"id"}, domain.Synced, domain.Null, "spaceId"}, "id") + + // then + assert.Nil(t, err) }) t.Run("update sync status and date - file status", func(t *testing.T) { @@ -104,7 +114,70 @@ func TestSyncStatusUpdater_UpdateDetails(t *testing.T) { // when fixture.statusUpdater.EXPECT().SendUpdate(domain.MakeSyncStatus("spaceId", domain.Syncing, domain.Null, domain.Objects)) - fixture.updater.updateDetails(&syncStatusDetails{[]string{"id"}, domain.Synced, domain.Null, "spaceId"}) + err := fixture.updater.updateObjectDetails(&syncStatusDetails{[]string{"id"}, domain.Synced, domain.Null, "spaceId"}, "id") + + // then + assert.Nil(t, err) + }) + t.Run("update sync status and date - storage limit file status", func(t *testing.T) { + // given + fixture := newFixture(t) + space := mock_clientspace.NewMockSpace(t) + fixture.service.EXPECT().Get(context.Background(), "spaceId").Return(space, nil) + fixture.storeFixture.AddObjects(t, []objectstore.TestObject{ + { + bundle.RelationKeyId: pbtypes.String("id"), + bundle.RelationKeyFileBackupStatus: pbtypes.Int64(int64(filesyncstatus.Limited)), + }, + }) + space.EXPECT().DoLockedIfNotExists("id", mock.Anything).Return(nil) + + // when + fixture.statusUpdater.EXPECT().SendUpdate(domain.MakeSyncStatus("spaceId", domain.Error, domain.StorageLimitExceed, domain.Objects)) + err := fixture.updater.updateObjectDetails(&syncStatusDetails{[]string{"id"}, domain.Synced, domain.Null, "spaceId"}, "id") + + // then + assert.Nil(t, err) + }) + t.Run("update sync status and date - unknown file status", func(t *testing.T) { + // given + fixture := newFixture(t) + space := mock_clientspace.NewMockSpace(t) + fixture.service.EXPECT().Get(context.Background(), "spaceId").Return(space, nil) + fixture.storeFixture.AddObjects(t, []objectstore.TestObject{ + { + bundle.RelationKeyId: pbtypes.String("id"), + bundle.RelationKeyFileBackupStatus: pbtypes.Int64(int64(filesyncstatus.Unknown)), + }, + }) + space.EXPECT().DoLockedIfNotExists("id", mock.Anything).Return(nil) + + // when + fixture.statusUpdater.EXPECT().SendUpdate(domain.MakeSyncStatus("spaceId", domain.Error, domain.NetworkError, domain.Objects)) + err := fixture.updater.updateObjectDetails(&syncStatusDetails{[]string{"id"}, domain.Synced, domain.Null, "spaceId"}, "id") + + // then + assert.Nil(t, err) + }) + t.Run("update sync status and date - synced file status", func(t *testing.T) { + // given + fixture := newFixture(t) + space := mock_clientspace.NewMockSpace(t) + fixture.service.EXPECT().Get(context.Background(), "spaceId").Return(space, nil) + fixture.storeFixture.AddObjects(t, []objectstore.TestObject{ + { + bundle.RelationKeyId: pbtypes.String("id"), + bundle.RelationKeyFileBackupStatus: pbtypes.Int64(int64(filesyncstatus.Synced)), + }, + }) + space.EXPECT().DoLockedIfNotExists("id", mock.Anything).Return(nil) + + // when + fixture.statusUpdater.EXPECT().SendUpdate(domain.MakeSyncStatus("spaceId", domain.Synced, domain.Null, domain.Objects)) + err := fixture.updater.updateObjectDetails(&syncStatusDetails{[]string{"id"}, domain.Synced, domain.Null, "spaceId"}, "id") + + // then + assert.Nil(t, err) }) } @@ -122,6 +195,22 @@ func TestSyncStatusUpdater_Run(t *testing.T) { err = fixture.updater.Close(context.Background()) assert.Nil(t, err) }) + + t.Run("run 2 time for 1 object", func(t *testing.T) { + // given + fixture := newFixture(t) + + // when + err := fixture.updater.Run(context.Background()) + assert.Nil(t, err) + fixture.updater.UpdateDetails([]string{"id"}, domain.Synced, domain.Null, "spaceId") + fixture.updater.UpdateDetails([]string{"id"}, domain.Syncing, domain.Null, "spaceId") + + // then + err = fixture.updater.Close(context.Background()) + assert.Nil(t, err) + assert.Equal(t, &syncStatusDetails{status: domain.Syncing, syncError: domain.Null, spaceId: "spaceId"}, fixture.updater.entries["id"]) + }) } func TestSyncStatusUpdater_setSyncDetails(t *testing.T) { @@ -164,11 +253,35 @@ func TestSyncStatusUpdater_setSyncDetails(t *testing.T) { }) } +func TestSyncStatusUpdater_updateDetails(t *testing.T) { + t.Run("update sync status and date - no changes", func(t *testing.T) { + // given + fixture := newFixture(t) + space := mock_clientspace.NewMockSpace(t) + fixture.service.EXPECT().Get(context.Background(), "spaceId").Return(space, nil) + fixture.storeFixture.AddObjects(t, []objectstore.TestObject{ + { + bundle.RelationKeyId: pbtypes.String("id"), + bundle.RelationKeySpaceId: pbtypes.String("spaceId"), + }, + }) + space.EXPECT().DoLockedIfNotExists("id", mock.Anything).Return(nil) + + // when + fixture.statusUpdater.EXPECT().SendUpdate(domain.MakeSyncStatus("spaceId", domain.Synced, domain.Null, domain.Objects)) + fixture.updater.updateDetails(&syncStatusDetails{nil, domain.Synced, domain.Null, "spaceId"}) + }) +} + func newFixture(t *testing.T) *fixture { smartTest := smarttest.New("id") storeFixture := objectstore.NewStoreFixture(t) service := mock_space.NewMockService(t) - updater := &syncStatusUpdater{batcher: mb.New[*syncStatusDetails](0), finish: make(chan struct{})} + updater := &syncStatusUpdater{ + batcher: mb.New[*syncStatusDetails](0), + finish: make(chan struct{}), + entries: map[string]*syncStatusDetails{}, + } statusUpdater := mock_detailsupdater.NewMockSpaceStatusUpdater(t) a := &app.App{} a.Register(storeFixture). diff --git a/core/syncstatus/objectsyncstatus/syncstatus_test.go b/core/syncstatus/objectsyncstatus/syncstatus_test.go index d1e9f9824..6d49885cb 100644 --- a/core/syncstatus/objectsyncstatus/syncstatus_test.go +++ b/core/syncstatus/objectsyncstatus/syncstatus_test.go @@ -11,6 +11,7 @@ import ( "github.com/anyproto/any-sync/commonspace/object/tree/treestorage" "github.com/anyproto/any-sync/commonspace/spacestate" "github.com/anyproto/any-sync/commonspace/spacestorage/mock_spacestorage" + "github.com/anyproto/any-sync/nodeconf" "github.com/anyproto/any-sync/nodeconf/mock_nodeconf" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" @@ -27,7 +28,8 @@ func Test_HeadsChange(t *testing.T) { t.Run("HeadsChange: new object", func(t *testing.T) { // given s := newFixture(t) - s.detailsUpdater.EXPECT().UpdateDetails("id", domain.Syncing, domain.Null, "spaceId") + s.service.EXPECT().NetworkCompatibilityStatus().Return(nodeconf.NetworkCompatibilityStatusOk) + s.detailsUpdater.EXPECT().UpdateDetails([]string{"id"}, domain.Syncing, domain.Null, "spaceId") // when s.HeadsChange("id", []string{"head1", "head2"}) @@ -40,7 +42,8 @@ func Test_HeadsChange(t *testing.T) { // given s := newFixture(t) s.config.NetworkMode = pb.RpcAccount_LocalOnly - s.detailsUpdater.EXPECT().UpdateDetails("id", domain.Offline, domain.Null, "spaceId") + s.service.EXPECT().NetworkCompatibilityStatus().Return(nodeconf.NetworkCompatibilityStatusOk) + s.detailsUpdater.EXPECT().UpdateDetails([]string{"id"}, domain.Offline, domain.Null, "spaceId") // when s.HeadsChange("id", []string{"head1", "head2"}) @@ -53,7 +56,8 @@ func Test_HeadsChange(t *testing.T) { // given s := newFixture(t) s.config.NetworkMode = pb.RpcAccount_DefaultConfig - s.detailsUpdater.EXPECT().UpdateDetails("id", domain.Syncing, domain.Null, "spaceId") + s.detailsUpdater.EXPECT().UpdateDetails([]string{"id"}, domain.Syncing, domain.Null, "spaceId") + s.service.EXPECT().NetworkCompatibilityStatus().Return(nodeconf.NetworkCompatibilityStatusOk).Times(2) // when s.HeadsChange("id", []string{"head1", "head2"}) @@ -68,12 +72,26 @@ func Test_HeadsChange(t *testing.T) { s := newFixture(t) s.service.EXPECT().NodeIds("spaceId").Return([]string{"peerId"}) s.nodeStatus.SetNodesStatus("spaceId", "peerId", nodestatus.ConnectionError) - s.detailsUpdater.EXPECT().UpdateDetails("id", domain.Error, domain.NetworkError, "spaceId") + s.detailsUpdater.EXPECT().UpdateDetails([]string{"id"}, domain.Error, domain.NetworkError, "spaceId") + s.service.EXPECT().NetworkCompatibilityStatus().Return(nodeconf.NetworkCompatibilityStatusOk).Times(2) // when s.HeadsChange("id", []string{"head1", "head2"}) s.HeadsChange("id", []string{"head3"}) + // then + assert.NotNil(t, s.treeHeads["id"]) + assert.Equal(t, []string{"head3"}, s.treeHeads["id"].heads) + }) + t.Run("HeadsChange: network incompatible", func(t *testing.T) { + // given + s := newFixture(t) + s.detailsUpdater.EXPECT().UpdateDetails([]string{"id"}, domain.Error, domain.IncompatibleVersion, "spaceId") + s.service.EXPECT().NetworkCompatibilityStatus().Return(nodeconf.NetworkCompatibilityStatusIncompatible).Times(1) + + // when + s.HeadsChange("id", []string{"head3"}) + // then assert.NotNil(t, s.treeHeads["id"]) assert.Equal(t, []string{"head3"}, s.treeHeads["id"].heads) @@ -110,7 +128,8 @@ func TestSyncStatusService_HeadsReceive(t *testing.T) { // given s := newFixture(t) s.service.EXPECT().NodeIds(s.spaceId).Return([]string{"peerId2"}) - s.detailsUpdater.EXPECT().UpdateDetails("id", domain.Syncing, domain.Null, "spaceId") + s.detailsUpdater.EXPECT().UpdateDetails([]string{"id"}, domain.Syncing, domain.Null, "spaceId") + s.service.EXPECT().NetworkCompatibilityStatus().Return(nodeconf.NetworkCompatibilityStatusOk) // when s.HeadsChange("id", []string{"head1"}) @@ -124,11 +143,11 @@ func TestSyncStatusService_HeadsReceive(t *testing.T) { // given s := newFixture(t) s.service.EXPECT().NodeIds(s.spaceId).Return([]string{"peerId"}) + s.service.EXPECT().NetworkCompatibilityStatus().Return(nodeconf.NetworkCompatibilityStatusOk) // when - s.detailsUpdater.EXPECT().UpdateDetails("id", domain.Syncing, domain.Null, "spaceId") + s.detailsUpdater.EXPECT().UpdateDetails([]string{"id"}, domain.Syncing, domain.Null, "spaceId") s.HeadsChange("id", []string{"head1"}) - s.detailsUpdater.EXPECT().UpdateDetails("id", domain.Synced, domain.Null, "spaceId") s.HeadsReceive("peerId", "id", []string{"head1"}) // then @@ -143,7 +162,8 @@ func TestSyncStatusService_Watch(t *testing.T) { s := newFixture(t) // when - s.detailsUpdater.EXPECT().UpdateDetails("id", domain.Syncing, domain.Null, "spaceId") + s.detailsUpdater.EXPECT().UpdateDetails([]string{"id"}, domain.Syncing, domain.Null, "spaceId") + s.service.EXPECT().NetworkCompatibilityStatus().Return(nodeconf.NetworkCompatibilityStatusOk) s.HeadsChange("id", []string{"head1"}) err := s.Watch("id") @@ -190,7 +210,8 @@ func TestSyncStatusService_Unwatch(t *testing.T) { s := newFixture(t) // when - s.detailsUpdater.EXPECT().UpdateDetails("id", domain.Syncing, domain.Null, "spaceId") + s.detailsUpdater.EXPECT().UpdateDetails([]string{"id"}, domain.Syncing, domain.Null, "spaceId") + s.service.EXPECT().NetworkCompatibilityStatus().Return(nodeconf.NetworkCompatibilityStatusOk) s.HeadsChange("id", []string{"head1"}) err := s.Watch("id") assert.Nil(t, err) @@ -213,7 +234,8 @@ func TestSyncStatusService_update(t *testing.T) { s.SetUpdateReceiver(updateReceiver) // when - s.detailsUpdater.EXPECT().UpdateDetails("id", domain.Syncing, domain.Null, "spaceId") + s.detailsUpdater.EXPECT().UpdateDetails([]string{"id"}, domain.Syncing, domain.Null, "spaceId") + s.service.EXPECT().NetworkCompatibilityStatus().Return(nodeconf.NetworkCompatibilityStatusOk) s.HeadsChange("id", []string{"head1"}) err := s.Watch("id") assert.Nil(t, err) @@ -230,7 +252,9 @@ func TestSyncStatusService_update(t *testing.T) { s.SetUpdateReceiver(updateReceiver) // when - s.detailsUpdater.EXPECT().UpdateDetails("id", domain.Syncing, domain.Null, "spaceId") + s.detailsUpdater.EXPECT().UpdateDetails([]string{"id"}, domain.Syncing, domain.Null, "spaceId") + s.service.EXPECT().NetworkCompatibilityStatus().Return(nodeconf.NetworkCompatibilityStatusOk) + s.HeadsChange("id", []string{"head1"}) err := s.Watch("id") assert.Nil(t, err) diff --git a/core/syncstatus/spacesyncstatus/filestate_test.go b/core/syncstatus/spacesyncstatus/filestate_test.go index 421fe7b11..2dac7956b 100644 --- a/core/syncstatus/spacesyncstatus/filestate_test.go +++ b/core/syncstatus/spacesyncstatus/filestate_test.go @@ -179,4 +179,29 @@ func TestFileState_SetSyncStatus(t *testing.T) { // then assert.Equal(t, domain.Offline, fileState.GetSyncStatus("spaceId")) }) + t.Run("SetSyncStatusAndErr, storage limit error", func(t *testing.T) { + // given + storeFixture := objectstore.NewStoreFixture(t) + fileState := NewFileState(storeFixture) + storeFixture.AddObjects(t, []objectstore.TestObject{ + { + bundle.RelationKeyId: pbtypes.String("id1"), + bundle.RelationKeyFileBackupStatus: pbtypes.Int64(int64(filesyncstatus.Limited)), + bundle.RelationKeySpaceId: pbtypes.String("spaceId"), + }, + { + bundle.RelationKeyId: pbtypes.String("id2"), + bundle.RelationKeyFileBackupStatus: pbtypes.Int64(int64(filesyncstatus.Limited)), + bundle.RelationKeySpaceId: pbtypes.String("spaceId"), + }, + }) + + // when + syncStatus := domain.MakeSyncStatus("spaceId", domain.Synced, domain.Null, domain.Files) + fileState.SetSyncStatusAndErr(syncStatus) + + // then + assert.Equal(t, domain.Error, fileState.GetSyncStatus("spaceId")) + assert.Equal(t, domain.StorageLimitExceed, fileState.GetSyncErr("spaceId")) + }) } diff --git a/core/syncstatus/spacesyncstatus/mock_spacesyncstatus/mock_SpaceIdGetter.go b/core/syncstatus/spacesyncstatus/mock_spacesyncstatus/mock_SpaceIdGetter.go new file mode 100644 index 000000000..c1479e5f1 --- /dev/null +++ b/core/syncstatus/spacesyncstatus/mock_spacesyncstatus/mock_SpaceIdGetter.go @@ -0,0 +1,216 @@ +// Code generated by mockery. DO NOT EDIT. + +package mock_spacesyncstatus + +import ( + app "github.com/anyproto/any-sync/app" + mock "github.com/stretchr/testify/mock" +) + +// MockSpaceIdGetter is an autogenerated mock type for the SpaceIdGetter type +type MockSpaceIdGetter struct { + mock.Mock +} + +type MockSpaceIdGetter_Expecter struct { + mock *mock.Mock +} + +func (_m *MockSpaceIdGetter) EXPECT() *MockSpaceIdGetter_Expecter { + return &MockSpaceIdGetter_Expecter{mock: &_m.Mock} +} + +// Init provides a mock function with given fields: a +func (_m *MockSpaceIdGetter) Init(a *app.App) error { + ret := _m.Called(a) + + if len(ret) == 0 { + panic("no return value specified for Init") + } + + var r0 error + if rf, ok := ret.Get(0).(func(*app.App) error); ok { + r0 = rf(a) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockSpaceIdGetter_Init_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Init' +type MockSpaceIdGetter_Init_Call struct { + *mock.Call +} + +// Init is a helper method to define mock.On call +// - a *app.App +func (_e *MockSpaceIdGetter_Expecter) Init(a interface{}) *MockSpaceIdGetter_Init_Call { + return &MockSpaceIdGetter_Init_Call{Call: _e.mock.On("Init", a)} +} + +func (_c *MockSpaceIdGetter_Init_Call) Run(run func(a *app.App)) *MockSpaceIdGetter_Init_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*app.App)) + }) + return _c +} + +func (_c *MockSpaceIdGetter_Init_Call) Return(err error) *MockSpaceIdGetter_Init_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockSpaceIdGetter_Init_Call) RunAndReturn(run func(*app.App) error) *MockSpaceIdGetter_Init_Call { + _c.Call.Return(run) + return _c +} + +// Name provides a mock function with given fields: +func (_m *MockSpaceIdGetter) Name() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Name") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// MockSpaceIdGetter_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' +type MockSpaceIdGetter_Name_Call struct { + *mock.Call +} + +// Name is a helper method to define mock.On call +func (_e *MockSpaceIdGetter_Expecter) Name() *MockSpaceIdGetter_Name_Call { + return &MockSpaceIdGetter_Name_Call{Call: _e.mock.On("Name")} +} + +func (_c *MockSpaceIdGetter_Name_Call) Run(run func()) *MockSpaceIdGetter_Name_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockSpaceIdGetter_Name_Call) Return(name string) *MockSpaceIdGetter_Name_Call { + _c.Call.Return(name) + return _c +} + +func (_c *MockSpaceIdGetter_Name_Call) RunAndReturn(run func() string) *MockSpaceIdGetter_Name_Call { + _c.Call.Return(run) + return _c +} + +// PersonalSpaceId provides a mock function with given fields: +func (_m *MockSpaceIdGetter) PersonalSpaceId() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for PersonalSpaceId") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// MockSpaceIdGetter_PersonalSpaceId_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PersonalSpaceId' +type MockSpaceIdGetter_PersonalSpaceId_Call struct { + *mock.Call +} + +// PersonalSpaceId is a helper method to define mock.On call +func (_e *MockSpaceIdGetter_Expecter) PersonalSpaceId() *MockSpaceIdGetter_PersonalSpaceId_Call { + return &MockSpaceIdGetter_PersonalSpaceId_Call{Call: _e.mock.On("PersonalSpaceId")} +} + +func (_c *MockSpaceIdGetter_PersonalSpaceId_Call) Run(run func()) *MockSpaceIdGetter_PersonalSpaceId_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockSpaceIdGetter_PersonalSpaceId_Call) Return(_a0 string) *MockSpaceIdGetter_PersonalSpaceId_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockSpaceIdGetter_PersonalSpaceId_Call) RunAndReturn(run func() string) *MockSpaceIdGetter_PersonalSpaceId_Call { + _c.Call.Return(run) + return _c +} + +// TechSpaceId provides a mock function with given fields: +func (_m *MockSpaceIdGetter) TechSpaceId() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for TechSpaceId") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// MockSpaceIdGetter_TechSpaceId_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TechSpaceId' +type MockSpaceIdGetter_TechSpaceId_Call struct { + *mock.Call +} + +// TechSpaceId is a helper method to define mock.On call +func (_e *MockSpaceIdGetter_Expecter) TechSpaceId() *MockSpaceIdGetter_TechSpaceId_Call { + return &MockSpaceIdGetter_TechSpaceId_Call{Call: _e.mock.On("TechSpaceId")} +} + +func (_c *MockSpaceIdGetter_TechSpaceId_Call) Run(run func()) *MockSpaceIdGetter_TechSpaceId_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockSpaceIdGetter_TechSpaceId_Call) Return(_a0 string) *MockSpaceIdGetter_TechSpaceId_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockSpaceIdGetter_TechSpaceId_Call) RunAndReturn(run func() string) *MockSpaceIdGetter_TechSpaceId_Call { + _c.Call.Return(run) + return _c +} + +// NewMockSpaceIdGetter creates a new instance of MockSpaceIdGetter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockSpaceIdGetter(t interface { + mock.TestingT + Cleanup(func()) +}) *MockSpaceIdGetter { + mock := &MockSpaceIdGetter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/syncstatus/spacesyncstatus/spacestatus.go b/core/syncstatus/spacesyncstatus/spacestatus.go index ce32d7cc2..eda929882 100644 --- a/core/syncstatus/spacesyncstatus/spacestatus.go +++ b/core/syncstatus/spacesyncstatus/spacestatus.go @@ -48,6 +48,7 @@ type Updater interface { } type SpaceIdGetter interface { + app.Component TechSpaceId() string PersonalSpaceId() string } diff --git a/core/syncstatus/spacesyncstatus/spacestatus_test.go b/core/syncstatus/spacesyncstatus/spacestatus_test.go index 64a82d16a..2077f5a16 100644 --- a/core/syncstatus/spacesyncstatus/spacestatus_test.go +++ b/core/syncstatus/spacesyncstatus/spacestatus_test.go @@ -12,10 +12,10 @@ import ( "github.com/anyproto/anytype-heart/core/domain" "github.com/anyproto/anytype-heart/core/event/mock_event" "github.com/anyproto/anytype-heart/core/syncstatus/filesyncstatus" + "github.com/anyproto/anytype-heart/core/syncstatus/spacesyncstatus/mock_spacesyncstatus" "github.com/anyproto/anytype-heart/pb" "github.com/anyproto/anytype-heart/pkg/lib/bundle" "github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore" - "github.com/anyproto/anytype-heart/space/mock_space" "github.com/anyproto/anytype-heart/tests/testutil" "github.com/anyproto/anytype-heart/util/pbtypes" ) @@ -28,7 +28,7 @@ func TestSpaceSyncStatus_Init(t *testing.T) { a := new(app.App) eventSender := mock_event.NewMockSender(t) - space := mock_space.NewMockService(t) + space := mock_spacesyncstatus.NewMockSpaceIdGetter(t) a.Register(testutil.PrepareMock(ctx, a, eventSender)). Register(objectstore.NewStoreFixture(t)). Register(&config.Config{NetworkMode: pb.RpcAccount_DefaultConfig}). @@ -39,6 +39,19 @@ func TestSpaceSyncStatus_Init(t *testing.T) { // then assert.Nil(t, err) + + space.EXPECT().PersonalSpaceId().Return("personalId") + eventSender.EXPECT().Broadcast(&pb.Event{ + Messages: []*pb.EventMessage{{ + Value: &pb.EventMessageValueOfSpaceSyncStatusUpdate{ + SpaceSyncStatusUpdate: &pb.EventSpaceSyncStatusUpdate{ + Id: "personalId", + Status: pb.EventSpace_Synced, + Network: pb.EventSpace_Anytype, + }, + }, + }}, + }) err = status.Run(ctx) assert.Nil(t, err) err = status.Close(ctx) @@ -61,7 +74,7 @@ func TestSpaceSyncStatus_Init(t *testing.T) { }, }}, }) - space := mock_space.NewMockService(t) + space := mock_spacesyncstatus.NewMockSpaceIdGetter(t) a.Register(testutil.PrepareMock(ctx, a, eventSender)). Register(objectstore.NewStoreFixture(t)). @@ -209,7 +222,7 @@ func TestSpaceSyncStatus_updateSpaceSyncStatus(t *testing.T) { Id: "spaceId", Status: pb.EventSpace_Error, Network: pb.EventSpace_Anytype, - Error: pb.EventSpace_Null, + Error: pb.EventSpace_NetworkError, SyncingObjectsCounter: 0, }, }, @@ -222,7 +235,73 @@ func TestSpaceSyncStatus_updateSpaceSyncStatus(t *testing.T) { filesState: NewFileState(objectstore.NewStoreFixture(t)), objectsState: NewObjectState(objectstore.NewStoreFixture(t)), } - syncStatus := domain.MakeSyncStatus("spaceId", domain.Error, domain.Null, domain.Objects) + syncStatus := domain.MakeSyncStatus("spaceId", domain.Error, domain.NetworkError, domain.Objects) + + // then + status.updateSpaceSyncStatus(syncStatus) + + // when + assert.Equal(t, domain.Error, status.objectsState.GetSyncStatus("spaceId")) + assert.Equal(t, 0, status.objectsState.GetSyncObjectCount("spaceId")) + assert.Equal(t, domain.Error, status.getSpaceSyncStatus(syncStatus.SpaceId)) + }) + t.Run("send storage error event", func(t *testing.T) { + // given + eventSender := mock_event.NewMockSender(t) + eventSender.EXPECT().Broadcast(&pb.Event{ + Messages: []*pb.EventMessage{{ + Value: &pb.EventMessageValueOfSpaceSyncStatusUpdate{ + SpaceSyncStatusUpdate: &pb.EventSpaceSyncStatusUpdate{ + Id: "spaceId", + Status: pb.EventSpace_Error, + Network: pb.EventSpace_Anytype, + Error: pb.EventSpace_StorageLimitExceed, + SyncingObjectsCounter: 0, + }, + }, + }}, + }) + status := spaceSyncStatus{ + eventSender: eventSender, + networkConfig: &config.Config{NetworkMode: pb.RpcAccount_DefaultConfig}, + batcher: mb.New[*domain.SpaceSync](0), + filesState: NewFileState(objectstore.NewStoreFixture(t)), + objectsState: NewObjectState(objectstore.NewStoreFixture(t)), + } + syncStatus := domain.MakeSyncStatus("spaceId", domain.Error, domain.StorageLimitExceed, domain.Files) + + // then + status.updateSpaceSyncStatus(syncStatus) + + // when + assert.Equal(t, domain.Error, status.filesState.GetSyncStatus("spaceId")) + assert.Equal(t, 0, status.filesState.GetSyncObjectCount("spaceId")) + assert.Equal(t, domain.Error, status.getSpaceSyncStatus(syncStatus.SpaceId)) + }) + t.Run("send incompatible error event", func(t *testing.T) { + // given + eventSender := mock_event.NewMockSender(t) + eventSender.EXPECT().Broadcast(&pb.Event{ + Messages: []*pb.EventMessage{{ + Value: &pb.EventMessageValueOfSpaceSyncStatusUpdate{ + SpaceSyncStatusUpdate: &pb.EventSpaceSyncStatusUpdate{ + Id: "spaceId", + Status: pb.EventSpace_Error, + Network: pb.EventSpace_Anytype, + Error: pb.EventSpace_IncompatibleVersion, + SyncingObjectsCounter: 0, + }, + }, + }}, + }) + status := spaceSyncStatus{ + eventSender: eventSender, + networkConfig: &config.Config{NetworkMode: pb.RpcAccount_DefaultConfig}, + batcher: mb.New[*domain.SpaceSync](0), + filesState: NewFileState(objectstore.NewStoreFixture(t)), + objectsState: NewObjectState(objectstore.NewStoreFixture(t)), + } + syncStatus := domain.MakeSyncStatus("spaceId", domain.Error, domain.IncompatibleVersion, domain.Objects) // then status.updateSpaceSyncStatus(syncStatus) diff --git a/space/spacecore/peermanager/manager.go b/space/spacecore/peermanager/manager.go index 05b2bdb53..9459e6331 100644 --- a/space/spacecore/peermanager/manager.go +++ b/space/spacecore/peermanager/manager.go @@ -180,7 +180,7 @@ func (n *clientPeerManager) manageResponsiblePeers() { for { n.fetchResponsiblePeers() select { - case <-time.After(time.Minute): + case <-time.After(time.Second * 20): case <-n.rebuildResponsiblePeers: case <-n.ctx.Done(): return diff --git a/util/linkpreview/mock_linkpreview/mock_LinkPreview.go b/util/linkpreview/mock_linkpreview/mock_LinkPreview.go index 1921bec98..f6dbc946e 100644 --- a/util/linkpreview/mock_linkpreview/mock_LinkPreview.go +++ b/util/linkpreview/mock_linkpreview/mock_LinkPreview.go @@ -88,8 +88,8 @@ func (_c *MockLinkPreview_Fetch_Call) Run(run func(ctx context.Context, url stri return _c } -func (_c *MockLinkPreview_Fetch_Call) Return(_a0 model.LinkPreview, _a1 []byte, _a2 bool, _a3 error) *MockLinkPreview_Fetch_Call { - _c.Call.Return(_a0, _a1, _a2, _a3) +func (_c *MockLinkPreview_Fetch_Call) Return(linkPreview model.LinkPreview, responseBody []byte, isFile bool, err error) *MockLinkPreview_Fetch_Call { + _c.Call.Return(linkPreview, responseBody, isFile, err) return _c }