From d78f6ffaa23cae91ae0247cf2961873de3e8a80a Mon Sep 17 00:00:00 2001 From: Sejong Kim Date: Fri, 6 Sep 2024 22:39:09 +0900 Subject: [PATCH 1/7] Add PushPull consistency test --- server/packs/packs.go | 11 ++ server/packs/packs_test.go | 232 +++++++++++++++++++++++++++++ server/rpc/yorkie_server.go | 8 +- test/bench/push_pull_bench_test.go | 12 +- 4 files changed, 253 insertions(+), 10 deletions(-) create mode 100644 server/packs/packs_test.go diff --git a/server/packs/packs.go b/server/packs/packs.go index ce76a3f9c..cbea66007 100644 --- a/server/packs/packs.go +++ b/server/packs/packs.go @@ -20,6 +20,7 @@ package packs import ( "context" + "errors" "fmt" "strconv" gotime "time" @@ -37,6 +38,10 @@ import ( "github.com/yorkie-team/yorkie/server/logging" ) +var ( + ErrCheckpointTest = errors.New("failure for checkpoint testing purpose") +) + // PushPullKey creates a new sync.Key of PushPull for the given document. func PushPullKey(projectID types.ID, docKey key.Key) sync.Key { return sync.NewKey(fmt.Sprintf("pushpull-%s-%s", projectID, docKey)) @@ -69,6 +74,7 @@ func PushPull( docInfo *database.DocInfo, reqPack *change.Pack, opts PushPullOptions, + cpTest bool, ) (*ServerPack, error) { start := gotime.Now() defer func() { @@ -123,6 +129,11 @@ func PushPull( } } + // For consistency testing purposes + if cpTest { + return nil, ErrCheckpointTest + } + if err := be.DB.UpdateClientInfoAfterPushPull(ctx, clientInfo, docInfo); err != nil { return nil, err } diff --git a/server/packs/packs_test.go b/server/packs/packs_test.go new file mode 100644 index 000000000..75403af7c --- /dev/null +++ b/server/packs/packs_test.go @@ -0,0 +1,232 @@ +/* + * Copyright 2024 The Yorkie Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package packs_test + +import ( + "context" + "encoding/hex" + "fmt" + "log" + "net/http" + "os" + "testing" + + "connectrpc.com/connect" + "github.com/stretchr/testify/assert" + + "github.com/yorkie-team/yorkie/api/converter" + "github.com/yorkie-team/yorkie/api/types" + api "github.com/yorkie-team/yorkie/api/yorkie/v1" + "github.com/yorkie-team/yorkie/api/yorkie/v1/v1connect" + "github.com/yorkie-team/yorkie/client" + "github.com/yorkie-team/yorkie/pkg/document" + "github.com/yorkie-team/yorkie/pkg/document/time" + "github.com/yorkie-team/yorkie/server/backend" + "github.com/yorkie-team/yorkie/server/backend/database" + "github.com/yorkie-team/yorkie/server/backend/database/mongo" + "github.com/yorkie-team/yorkie/server/backend/housekeeping" + "github.com/yorkie-team/yorkie/server/clients" + "github.com/yorkie-team/yorkie/server/documents" + "github.com/yorkie-team/yorkie/server/packs" + "github.com/yorkie-team/yorkie/server/profiling/prometheus" + "github.com/yorkie-team/yorkie/server/rpc" + "github.com/yorkie-team/yorkie/test/helper" +) + +var ( + testRPCServer *rpc.Server + testRPCAddr = fmt.Sprintf("localhost:%d", helper.RPCPort) + testClient v1connect.YorkieServiceClient + testBackend *backend.Backend +) + +func TestMain(m *testing.M) { + met, err := prometheus.NewMetrics() + if err != nil { + log.Fatal(err) + } + + testBackend, err = backend.New(&backend.Config{ + AdminUser: helper.AdminUser, + AdminPassword: helper.AdminPassword, + UseDefaultProject: helper.UseDefaultProject, + ClientDeactivateThreshold: helper.ClientDeactivateThreshold, + SnapshotThreshold: helper.SnapshotThreshold, + AuthWebhookCacheSize: helper.AuthWebhookSize, + ProjectInfoCacheSize: helper.ProjectInfoCacheSize, + ProjectInfoCacheTTL: helper.ProjectInfoCacheTTL.String(), + AdminTokenDuration: helper.AdminTokenDuration, + }, &mongo.Config{ + ConnectionURI: helper.MongoConnectionURI, + YorkieDatabase: helper.TestDBName(), + ConnectionTimeout: helper.MongoConnectionTimeout, + PingTimeout: helper.MongoPingTimeout, + }, &housekeeping.Config{ + Interval: helper.HousekeepingInterval.String(), + CandidatesLimitPerProject: helper.HousekeepingCandidatesLimitPerProject, + ProjectFetchSize: helper.HousekeepingProjectFetchSize, + }, met) + if err != nil { + log.Fatal(err) + } + + project, err := testBackend.DB.FindProjectInfoByID( + context.Background(), + database.DefaultProjectID, + ) + if err != nil { + log.Fatal(err) + } + + testRPCServer, err = rpc.NewServer(&rpc.Config{ + Port: helper.RPCPort, + }, testBackend) + if err != nil { + log.Fatal(err) + } + + if err = testRPCServer.Start(); err != nil { + log.Fatalf("failed rpc listen: %s\n", err) + } + if err = helper.WaitForServerToStart(testRPCAddr); err != nil { + log.Fatal(err) + } + + authInterceptor := client.NewAuthInterceptor(project.PublicKey, "") + + conn := http.DefaultClient + testClient = v1connect.NewYorkieServiceClient( + conn, + "http://"+testRPCAddr, + connect.WithInterceptors(authInterceptor), + ) + + code := m.Run() + + if err := testBackend.Shutdown(); err != nil { + log.Fatal(err) + } + testRPCServer.Shutdown(true) + os.Exit(code) +} + +func TestPacks(t *testing.T) { + t.Run("pushpull consistency test", func(t *testing.T) { + ctx := context.Background() + + projectInfo, err := testBackend.DB.FindProjectInfoByID( + ctx, + database.DefaultProjectID, + ) + assert.NoError(t, err) + project := projectInfo.ToProject() + + activateResp, err := testClient.ActivateClient( + context.Background(), + connect.NewRequest(&api.ActivateClientRequest{ClientKey: helper.TestDocKey(t).String()})) + assert.NoError(t, err) + + clientID, _ := hex.DecodeString(activateResp.Msg.ClientId) + resPack, err := testClient.AttachDocument( + context.Background(), + connect.NewRequest(&api.AttachDocumentRequest{ + ClientId: activateResp.Msg.ClientId, + ChangePack: &api.ChangePack{ + DocumentKey: helper.TestDocKey(t).String(), + Checkpoint: &api.Checkpoint{ServerSeq: 0, ClientSeq: 1}, + Changes: []*api.Change{{ + Id: &api.ChangeID{ + ClientSeq: 1, + Lamport: 1, + ActorId: clientID, + }, + }}, + }, + }, + )) + assert.NoError(t, err) + + actorID, err := time.ActorIDFromBytes(clientID) + assert.NoError(t, err) + + docID := types.ID(resPack.Msg.DocumentId) + docRefKey := types.DocRefKey{ + ProjectID: project.ID, + DocID: docID, + } + + // 0. Check docInfo.ServerSeq and clientInfo.Checkpoint + docInfo, err := documents.FindDocInfoByRefKey(ctx, testBackend, docRefKey) + assert.NoError(t, err) + assert.Equal(t, int64(1), docInfo.ServerSeq) + + clientInfo, err := clients.FindActiveClientInfo(ctx, testBackend.DB, types.ClientRefKey{ + ProjectID: project.ID, + ClientID: types.IDFromActorID(actorID), + }) + assert.NoError(t, err) + assert.Equal(t, int64(1), clientInfo.Checkpoint(docID).ServerSeq) + assert.Equal(t, uint32(1), clientInfo.Checkpoint(docID).ClientSeq) + + // 1. Create a ChangePack with a single Change + pack, err := converter.FromChangePack(&api.ChangePack{ + DocumentKey: helper.TestDocKey(t).String(), + Checkpoint: &api.Checkpoint{ServerSeq: 0, ClientSeq: 2}, + Changes: []*api.Change{{ + Id: &api.ChangeID{ + ClientSeq: 2, + Lamport: 2, + ActorId: clientID, + }, + }}, + }) + assert.NoError(t, err) + + // 2-1. An arbitrary failure occurs while updating clientInfo + _, err = packs.PushPull(ctx, testBackend, project, clientInfo, docInfo, pack, packs.PushPullOptions{ + Mode: types.SyncModePushPull, + Status: document.StatusAttached, + }, true) + assert.ErrorIs(t, err, packs.ErrCheckpointTest) + + // 2-2. docInfo.ServerSeq increases from 1 to 2 + docInfo, err = documents.FindDocInfoByRefKey(ctx, testBackend, docRefKey) + assert.NoError(t, err) + assert.Equal(t, int64(2), docInfo.ServerSeq) + + // 2-3. clientInfo.Checkpoint has not been updated + clientInfo, err = clients.FindActiveClientInfo(ctx, testBackend.DB, types.ClientRefKey{ + ProjectID: project.ID, + ClientID: types.IDFromActorID(actorID), + }) + assert.NoError(t, err) + assert.Equal(t, int64(1), clientInfo.Checkpoint(docID).ServerSeq) + assert.Equal(t, uint32(1), clientInfo.Checkpoint(docID).ClientSeq) + + // 3-1. A duplicate request is sent + _, err = packs.PushPull(ctx, testBackend, project, clientInfo, docInfo, pack, packs.PushPullOptions{ + Mode: types.SyncModePushPull, + Status: document.StatusAttached, + }, false) + assert.NoError(t, err) + + // 3-2. The server should detect the duplication and not update docInfo.ServerSeq + docInfo, err = documents.FindDocInfoByRefKey(ctx, testBackend, docRefKey) + assert.NoError(t, err) + assert.Equal(t, int64(2), docInfo.ServerSeq) + }) +} diff --git a/server/rpc/yorkie_server.go b/server/rpc/yorkie_server.go index 86c273080..e9a19ed13 100644 --- a/server/rpc/yorkie_server.go +++ b/server/rpc/yorkie_server.go @@ -164,7 +164,7 @@ func (s *yorkieServer) AttachDocument( pulled, err := packs.PushPull(ctx, s.backend, project, clientInfo, docInfo, pack, packs.PushPullOptions{ Mode: types.SyncModePushPull, Status: document.StatusAttached, - }) + }, false) if err != nil { return nil, err } @@ -258,7 +258,7 @@ func (s *yorkieServer) DetachDocument( pulled, err := packs.PushPull(ctx, s.backend, project, clientInfo, docInfo, pack, packs.PushPullOptions{ Mode: types.SyncModePushPull, Status: status, - }) + }, false) if err != nil { return nil, err } @@ -349,7 +349,7 @@ func (s *yorkieServer) PushPullChanges( pulled, err := packs.PushPull(ctx, s.backend, project, clientInfo, docInfo, pack, packs.PushPullOptions{ Mode: syncMode, Status: document.StatusAttached, - }) + }, false) if err != nil { return nil, err } @@ -546,7 +546,7 @@ func (s *yorkieServer) RemoveDocument( pulled, err := packs.PushPull(ctx, s.backend, project, clientInfo, docInfo, pack, packs.PushPullOptions{ Mode: types.SyncModePushPull, Status: document.StatusRemoved, - }) + }, false) if err != nil { return nil, err } diff --git a/test/bench/push_pull_bench_test.go b/test/bench/push_pull_bench_test.go index ca6fff1bb..be5519ad9 100644 --- a/test/bench/push_pull_bench_test.go +++ b/test/bench/push_pull_bench_test.go @@ -147,7 +147,7 @@ func benchmarkPushChanges( _, err = packs.PushPull(ctx, be, project, clientInfos[0], docInfo, pack, packs.PushPullOptions{ Mode: types.SyncModePushPull, Status: document.StatusAttached, - }) + }, false) assert.NoError(b, err) } } @@ -177,7 +177,7 @@ func benchmarkPullChanges( _, err = packs.PushPull(ctx, be, project, pusherClientInfo, docInfo, pushPack, packs.PushPullOptions{ Mode: types.SyncModePushPull, Status: document.StatusAttached, - }) + }, false) assert.NoError(b, err) docInfo, err = documents.FindDocInfoByRefKey(ctx, be, docRefKey) @@ -187,7 +187,7 @@ func benchmarkPullChanges( _, err = packs.PushPull(ctx, be, project, pullerClientInfo, docInfo, pullPack, packs.PushPullOptions{ Mode: types.SyncModePushPull, Status: document.StatusAttached, - }) + }, false) assert.NoError(b, err) } } @@ -220,7 +220,7 @@ func benchmarkPushSnapshots( pulled, err := packs.PushPull(ctx, be, project, clientInfos[0], docInfo, pushPack, packs.PushPullOptions{ Mode: types.SyncModePushPull, Status: document.StatusAttached, - }) + }, false) assert.NoError(b, err) b.StopTimer() @@ -259,7 +259,7 @@ func benchmarkPullSnapshot( _, err = packs.PushPull(ctx, be, project, pusherClientInfo, docInfo, pushPack, packs.PushPullOptions{ Mode: types.SyncModePushPull, Status: document.StatusAttached, - }) + }, false) assert.NoError(b, err) docInfo, err = documents.FindDocInfoByRefKey(ctx, be, docRefKey) @@ -269,7 +269,7 @@ func benchmarkPullSnapshot( _, err = packs.PushPull(ctx, be, project, pullerClientInfo, docInfo, pullPack, packs.PushPullOptions{ Mode: types.SyncModePushPull, Status: document.StatusAttached, - }) + }, false) assert.NoError(b, err) } } From 402a51c6bc40dcaafb7cddbab8fffd8f3ed99548 Mon Sep 17 00:00:00 2001 From: Sejong Kim Date: Fri, 6 Sep 2024 22:46:39 +0900 Subject: [PATCH 2/7] Lint --- server/packs/packs.go | 3 ++- server/packs/packs_test.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/server/packs/packs.go b/server/packs/packs.go index cbea66007..c2507b125 100644 --- a/server/packs/packs.go +++ b/server/packs/packs.go @@ -39,7 +39,8 @@ import ( ) var ( - ErrCheckpointTest = errors.New("failure for checkpoint testing purpose") + // ErrCheckpointTest is an error for checkpoint test purposes + ErrCheckpointTest = errors.New("error for checkpoint testing purposes") ) // PushPullKey creates a new sync.Key of PushPull for the given document. diff --git a/server/packs/packs_test.go b/server/packs/packs_test.go index 75403af7c..282585723 100644 --- a/server/packs/packs_test.go +++ b/server/packs/packs_test.go @@ -125,7 +125,7 @@ func TestMain(m *testing.M) { } func TestPacks(t *testing.T) { - t.Run("pushpull consistency test", func(t *testing.T) { + t.Run("cannot detect change duplication due to clientInfo update failure", func(t *testing.T) { ctx := context.Background() projectInfo, err := testBackend.DB.FindProjectInfoByID( From 84d2ce0f26b5b78a52e4e40c2482806f5437655d Mon Sep 17 00:00:00 2001 From: kokodak Date: Sat, 7 Sep 2024 18:02:39 +0900 Subject: [PATCH 3/7] Add assertion of Change and clientInfo.Checkpoint --- server/packs/packs_test.go | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/server/packs/packs_test.go b/server/packs/packs_test.go index 282585723..e62e1efa5 100644 --- a/server/packs/packs_test.go +++ b/server/packs/packs_test.go @@ -203,12 +203,17 @@ func TestPacks(t *testing.T) { }, true) assert.ErrorIs(t, err, packs.ErrCheckpointTest) - // 2-2. docInfo.ServerSeq increases from 1 to 2 + // 2-2. pushed change is stored in the database + changes, err := packs.FindChanges(ctx, testBackend, docInfo, 2, 2) + assert.NoError(t, err) + assert.Len(t, changes, 1) + + // 2-3. docInfo.ServerSeq increases from 1 to 2 docInfo, err = documents.FindDocInfoByRefKey(ctx, testBackend, docRefKey) assert.NoError(t, err) assert.Equal(t, int64(2), docInfo.ServerSeq) - // 2-3. clientInfo.Checkpoint has not been updated + // 2-4. clientInfo.Checkpoint has not been updated clientInfo, err = clients.FindActiveClientInfo(ctx, testBackend.DB, types.ClientRefKey{ ProjectID: project.ID, ClientID: types.IDFromActorID(actorID), @@ -224,9 +229,23 @@ func TestPacks(t *testing.T) { }, false) assert.NoError(t, err) - // 3-2. The server should detect the duplication and not update docInfo.ServerSeq + // 3-2. duplicated change is not stored in the database + changes, err = packs.FindChanges(ctx, testBackend, docInfo, 3, 3) + assert.NoError(t, err) + assert.Len(t, changes, 0) + + // 3-3. The server should detect the duplication and not update docInfo.ServerSeq docInfo, err = documents.FindDocInfoByRefKey(ctx, testBackend, docRefKey) assert.NoError(t, err) assert.Equal(t, int64(2), docInfo.ServerSeq) + + // 3-4. clientInfo.Checkpoint has been updated properly + clientInfo, err = clients.FindActiveClientInfo(ctx, testBackend.DB, types.ClientRefKey{ + ProjectID: project.ID, + ClientID: types.IDFromActorID(actorID), + }) + assert.NoError(t, err) + assert.Equal(t, int64(2), clientInfo.Checkpoint(docID).ServerSeq) + assert.Equal(t, uint32(2), clientInfo.Checkpoint(docID).ClientSeq) }) } From b8cf1baf067756673f847126515af6ece0a8f7fb Mon Sep 17 00:00:00 2001 From: Sejong Kim Date: Sat, 7 Sep 2024 23:19:27 +0900 Subject: [PATCH 4/7] apply mocking --- go.mod | 4 +- go.sum | 4 + server/packs/mock_db.go | 238 +++++++++++++++++++++++++++++ server/packs/packs.go | 12 -- server/packs/packs_test.go | 37 ++++- server/rpc/yorkie_server.go | 8 +- test/bench/push_pull_bench_test.go | 12 +- 7 files changed, 289 insertions(+), 26 deletions(-) create mode 100644 server/packs/mock_db.go diff --git a/go.mod b/go.mod index ec2fce562..9abb9dd77 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/spf13/cobra v1.5.0 github.com/spf13/viper v1.17.0 github.com/stretchr/testify v1.9.0 - github.com/undefinedlabs/go-mpatch v1.0.6 + github.com/undefinedlabs/go-mpatch v1.0.7 go.mongodb.org/mongo-driver v1.11.7 go.uber.org/zap v1.23.0 golang.org/x/crypto v0.16.0 @@ -30,6 +30,8 @@ require ( gopkg.in/yaml.v3 v3.0.1 ) +require github.com/stretchr/objx v0.5.2 // indirect + require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect diff --git a/go.sum b/go.sum index 10b9cb034..77e3fa764 100644 --- a/go.sum +++ b/go.sum @@ -318,6 +318,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -336,6 +338,8 @@ github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/undefinedlabs/go-mpatch v1.0.6 h1:h8q5ORH/GaOE1Se1DMhrOyljXZEhRcROO7agMqWXCOY= github.com/undefinedlabs/go-mpatch v1.0.6/go.mod h1:TyJZDQ/5AgyN7FSLiBJ8RO9u2c6wbtRvK827b6AVqY4= +github.com/undefinedlabs/go-mpatch v1.0.7 h1:943FMskd9oqfbZV0qRVKOUsXQhTLXL0bQTVbQSpzmBs= +github.com/undefinedlabs/go-mpatch v1.0.7/go.mod h1:TyJZDQ/5AgyN7FSLiBJ8RO9u2c6wbtRvK827b6AVqY4= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E= diff --git a/server/packs/mock_db.go b/server/packs/mock_db.go new file mode 100644 index 000000000..75e22d603 --- /dev/null +++ b/server/packs/mock_db.go @@ -0,0 +1,238 @@ +/* + * Copyright 2024 The Yorkie Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package packs + +import ( + "context" + + "github.com/stretchr/testify/mock" + "github.com/yorkie-team/yorkie/api/types" + "github.com/yorkie-team/yorkie/pkg/document" + "github.com/yorkie-team/yorkie/pkg/document/change" + "github.com/yorkie-team/yorkie/pkg/document/key" + "github.com/yorkie-team/yorkie/pkg/document/time" + "github.com/yorkie-team/yorkie/server/backend/database" +) + +type MockDB struct { + mock.Mock + realDB database.Database +} + +func NewMockDB(realDB database.Database) *MockDB { + return &MockDB{ + realDB: realDB, + } +} + +// Close all resources of this client. +func (m *MockDB) Close() error { + return m.realDB.Close() +} + +// EnsureDefaultUserAndProject creates the default user and project if they do not exist. +func (m *MockDB) EnsureDefaultUserAndProject(ctx context.Context, username, password string, clientDeactivateThreshold string) (*database.UserInfo, *database.ProjectInfo, error) { + return m.realDB.EnsureDefaultUserAndProject(ctx, username, password, clientDeactivateThreshold) +} + +// CreateProjectInfo creates a new project. +func (m *MockDB) CreateProjectInfo(ctx context.Context, name string, owner types.ID, clientDeactivateThreshold string) (*database.ProjectInfo, error) { + return m.realDB.CreateProjectInfo(ctx, name, owner, clientDeactivateThreshold) +} + +// FindNextNCyclingProjectInfos finds the next N cycling projects from the given projectID. +func (m *MockDB) FindNextNCyclingProjectInfos(ctx context.Context, pageSize int, lastProjectID types.ID) ([]*database.ProjectInfo, error) { + return m.realDB.FindNextNCyclingProjectInfos(ctx, pageSize, lastProjectID) +} + +// ListProjectInfos returns all project infos owned by owner. +func (m *MockDB) ListProjectInfos(ctx context.Context, owner types.ID) ([]*database.ProjectInfo, error) { + return m.realDB.ListProjectInfos(ctx, owner) +} + +// FindProjectInfoByPublicKey returns a project by public key. +func (m *MockDB) FindProjectInfoByPublicKey(ctx context.Context, publicKey string) (*database.ProjectInfo, error) { + return m.realDB.FindProjectInfoByPublicKey(ctx, publicKey) +} + +// FindProjectInfoBySecretKey returns a project by secret key. +func (m *MockDB) FindProjectInfoBySecretKey(ctx context.Context, secretKey string) (*database.ProjectInfo, error) { + return m.realDB.FindProjectInfoBySecretKey(ctx, secretKey) +} + +// FindProjectInfoByName returns a project by name. +func (m *MockDB) FindProjectInfoByName(ctx context.Context, owner types.ID, name string) (*database.ProjectInfo, error) { + return m.realDB.FindProjectInfoByName(ctx, owner, name) +} + +// FindProjectInfoByID returns a project by the given id. +func (m *MockDB) FindProjectInfoByID(ctx context.Context, id types.ID) (*database.ProjectInfo, error) { + return m.realDB.FindProjectInfoByID(ctx, id) +} + +// UpdateProjectInfo updates the project info. +func (m *MockDB) UpdateProjectInfo(ctx context.Context, owner types.ID, id types.ID, fields *types.UpdatableProjectFields) (*database.ProjectInfo, error) { + return m.realDB.UpdateProjectInfo(ctx, owner, id, fields) +} + +// CreateUserInfo creates a new user. +func (m *MockDB) CreateUserInfo(ctx context.Context, username string, hashedPassword string) (*database.UserInfo, error) { + return m.realDB.CreateUserInfo(ctx, username, hashedPassword) +} + +// DeleteUserInfoByName deletes a user by name. +func (m *MockDB) DeleteUserInfoByName(ctx context.Context, username string) error { + return m.realDB.DeleteUserInfoByName(ctx, username) +} + +// ChangeUserPassword changes to new password for user. +func (m *MockDB) ChangeUserPassword(ctx context.Context, username, hashedNewPassword string) error { + return m.realDB.ChangeUserPassword(ctx, username, hashedNewPassword) +} + +// FindUserInfoByID returns a user by ID. +func (m *MockDB) FindUserInfoByID(ctx context.Context, clientID types.ID) (*database.UserInfo, error) { + return m.realDB.FindUserInfoByID(ctx, clientID) +} + +// FindUserInfoByName returns a user by username. +func (m *MockDB) FindUserInfoByName(ctx context.Context, username string) (*database.UserInfo, error) { + return m.realDB.FindUserInfoByName(ctx, username) +} + +// ListUserInfos returns all users. +func (m *MockDB) ListUserInfos(ctx context.Context) ([]*database.UserInfo, error) { + return m.realDB.ListUserInfos(ctx) +} + +// ActivateClient activates the client of the given key. +func (m *MockDB) ActivateClient(ctx context.Context, projectID types.ID, key string) (*database.ClientInfo, error) { + return m.realDB.ActivateClient(ctx, projectID, key) +} + +// DeactivateClient deactivates the client of the given refKey and updates document statuses as detached. +func (m *MockDB) DeactivateClient(ctx context.Context, refKey types.ClientRefKey) (*database.ClientInfo, error) { + return m.realDB.DeactivateClient(ctx, refKey) +} + +// FindClientInfoByRefKey finds the client of the given refKey. +func (m *MockDB) FindClientInfoByRefKey(ctx context.Context, refKey types.ClientRefKey) (*database.ClientInfo, error) { + return m.realDB.FindClientInfoByRefKey(ctx, refKey) +} + +// UpdateClientInfoAfterPushPull updates the client from the given clientInfo after handling PushPull. +func (m *MockDB) UpdateClientInfoAfterPushPull(ctx context.Context, clientInfo *database.ClientInfo, docInfo *database.DocInfo) error { + args := m.Called(ctx, clientInfo, docInfo) + if args.Get(0) != nil { + return args.Error(0) + } + return m.realDB.UpdateClientInfoAfterPushPull(ctx, clientInfo, docInfo) +} + +// FindDeactivateCandidatesPerProject finds the clients that need housekeeping per project. +func (m *MockDB) FindDeactivateCandidatesPerProject(ctx context.Context, project *database.ProjectInfo, candidatesLimit int) ([]*database.ClientInfo, error) { + return m.realDB.FindDeactivateCandidatesPerProject(ctx, project, candidatesLimit) +} + +// FindDocInfoByKeyAndOwner finds the document of the given key. +func (m *MockDB) FindDocInfoByKeyAndOwner(ctx context.Context, clientRefKey types.ClientRefKey, docKey key.Key, createDocIfNotExist bool) (*database.DocInfo, error) { + return m.realDB.FindDocInfoByKeyAndOwner(ctx, clientRefKey, docKey, createDocIfNotExist) +} + +// FindDocInfoByKey finds the document of the given key. +func (m *MockDB) FindDocInfoByKey(ctx context.Context, projectID types.ID, docKey key.Key) (*database.DocInfo, error) { + return m.realDB.FindDocInfoByKey(ctx, projectID, docKey) +} + +// FindDocInfosByKeys finds the documents of the given keys. +func (m *MockDB) FindDocInfosByKeys(ctx context.Context, projectID types.ID, docKeys []key.Key) ([]*database.DocInfo, error) { + return m.realDB.FindDocInfosByKeys(ctx, projectID, docKeys) +} + +// FindDocInfoByRefKey finds a docInfo of the given refKey. +func (m *MockDB) FindDocInfoByRefKey(ctx context.Context, refKey types.DocRefKey) (*database.DocInfo, error) { + return m.realDB.FindDocInfoByRefKey(ctx, refKey) +} + +// UpdateDocInfoStatusToRemoved updates the document status to removed. +func (m *MockDB) UpdateDocInfoStatusToRemoved(ctx context.Context, refKey types.DocRefKey) error { + return m.realDB.UpdateDocInfoStatusToRemoved(ctx, refKey) +} + +// CreateChangeInfos stores the given changes and doc info. +func (m *MockDB) CreateChangeInfos(ctx context.Context, projectID types.ID, docInfo *database.DocInfo, initialServerSeq int64, changes []*change.Change, isRemoved bool) error { + return m.realDB.CreateChangeInfos(ctx, projectID, docInfo, initialServerSeq, changes, isRemoved) +} + +// PurgeStaleChanges deletes changes before the smallest in synced seqs. +func (m *MockDB) PurgeStaleChanges(ctx context.Context, docRefKey types.DocRefKey) error { + return m.realDB.PurgeStaleChanges(ctx, docRefKey) +} + +// FindChangesBetweenServerSeqs returns the changes between two server sequences. +func (m *MockDB) FindChangesBetweenServerSeqs(ctx context.Context, docRefKey types.DocRefKey, from int64, to int64) ([]*change.Change, error) { + return m.realDB.FindChangesBetweenServerSeqs(ctx, docRefKey, from, to) +} + +// FindChangeInfosBetweenServerSeqs returns the changeInfos between two server sequences. +func (m *MockDB) FindChangeInfosBetweenServerSeqs(ctx context.Context, docRefKey types.DocRefKey, from int64, to int64) ([]*database.ChangeInfo, error) { + return m.realDB.FindChangeInfosBetweenServerSeqs(ctx, docRefKey, from, to) +} + +// CreateSnapshotInfo stores the snapshot of the given document. +func (m *MockDB) CreateSnapshotInfo(ctx context.Context, docRefKey types.DocRefKey, doc *document.InternalDocument) error { + return m.realDB.CreateSnapshotInfo(ctx, docRefKey, doc) +} + +// FindSnapshotInfoByRefKey returns the snapshot by the given refKey. +func (m *MockDB) FindSnapshotInfoByRefKey(ctx context.Context, refKey types.SnapshotRefKey) (*database.SnapshotInfo, error) { + return m.realDB.FindSnapshotInfoByRefKey(ctx, refKey) +} + +// FindClosestSnapshotInfo finds the last snapshot of the given document. +func (m *MockDB) FindClosestSnapshotInfo(ctx context.Context, docRefKey types.DocRefKey, serverSeq int64, includeSnapshot bool) (*database.SnapshotInfo, error) { + return m.realDB.FindClosestSnapshotInfo(ctx, docRefKey, serverSeq, includeSnapshot) +} + +// FindMinSyncedSeqInfo finds the minimum synced sequence info. +func (m *MockDB) FindMinSyncedSeqInfo(ctx context.Context, docRefKey types.DocRefKey) (*database.SyncedSeqInfo, error) { + return m.realDB.FindMinSyncedSeqInfo(ctx, docRefKey) +} + +// UpdateAndFindMinSyncedTicket updates the given serverSeq of the given client and returns the min synced ticket. +func (m *MockDB) UpdateAndFindMinSyncedTicket(ctx context.Context, clientInfo *database.ClientInfo, docRefKey types.DocRefKey, serverSeq int64) (*time.Ticket, error) { + return m.realDB.UpdateAndFindMinSyncedTicket(ctx, clientInfo, docRefKey, serverSeq) +} + +// FindDocInfosByPaging returns the docInfos of the given paging. +func (m *MockDB) FindDocInfosByPaging(ctx context.Context, projectID types.ID, paging types.Paging[types.ID]) ([]*database.DocInfo, error) { + return m.realDB.FindDocInfosByPaging(ctx, projectID, paging) +} + +// FindDocInfosByQuery returns the docInfos which match the given query. +func (m *MockDB) FindDocInfosByQuery(ctx context.Context, projectID types.ID, query string, pageSize int) (*types.SearchResult[*database.DocInfo], error) { + return m.realDB.FindDocInfosByQuery(ctx, projectID, query, pageSize) +} + +// UpdateSyncedSeq updates the syncedSeq of the given client. +func (m *MockDB) UpdateSyncedSeq(ctx context.Context, clientInfo *database.ClientInfo, docRefKey types.DocRefKey, serverSeq int64) error { + return m.realDB.UpdateSyncedSeq(ctx, clientInfo, docRefKey, serverSeq) +} + +// IsDocumentAttached returns whether the given document is attached to clients. +func (m *MockDB) IsDocumentAttached(ctx context.Context, docRefKey types.DocRefKey, excludeClientID types.ID) (bool, error) { + return m.realDB.IsDocumentAttached(ctx, docRefKey, excludeClientID) +} diff --git a/server/packs/packs.go b/server/packs/packs.go index c2507b125..ce76a3f9c 100644 --- a/server/packs/packs.go +++ b/server/packs/packs.go @@ -20,7 +20,6 @@ package packs import ( "context" - "errors" "fmt" "strconv" gotime "time" @@ -38,11 +37,6 @@ import ( "github.com/yorkie-team/yorkie/server/logging" ) -var ( - // ErrCheckpointTest is an error for checkpoint test purposes - ErrCheckpointTest = errors.New("error for checkpoint testing purposes") -) - // PushPullKey creates a new sync.Key of PushPull for the given document. func PushPullKey(projectID types.ID, docKey key.Key) sync.Key { return sync.NewKey(fmt.Sprintf("pushpull-%s-%s", projectID, docKey)) @@ -75,7 +69,6 @@ func PushPull( docInfo *database.DocInfo, reqPack *change.Pack, opts PushPullOptions, - cpTest bool, ) (*ServerPack, error) { start := gotime.Now() defer func() { @@ -130,11 +123,6 @@ func PushPull( } } - // For consistency testing purposes - if cpTest { - return nil, ErrCheckpointTest - } - if err := be.DB.UpdateClientInfoAfterPushPull(ctx, clientInfo, docInfo); err != nil { return nil, err } diff --git a/server/packs/packs_test.go b/server/packs/packs_test.go index e62e1efa5..d343b61a2 100644 --- a/server/packs/packs_test.go +++ b/server/packs/packs_test.go @@ -19,6 +19,7 @@ package packs_test import ( "context" "encoding/hex" + "errors" "fmt" "log" "net/http" @@ -27,6 +28,7 @@ import ( "connectrpc.com/connect" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/yorkie-team/yorkie/api/converter" "github.com/yorkie-team/yorkie/api/types" @@ -47,11 +49,18 @@ import ( "github.com/yorkie-team/yorkie/test/helper" ) +var ( + // ErrUpdateClientInfoFailed occurs when updating ClientInfo failed + // for testing purposes. + ErrUpdateClientInfoFailed = errors.New("updating clientinfo failed") +) + var ( testRPCServer *rpc.Server testRPCAddr = fmt.Sprintf("localhost:%d", helper.RPCPort) testClient v1connect.YorkieServiceClient testBackend *backend.Backend + testMockDB *packs.MockDB ) func TestMain(m *testing.M) { @@ -83,6 +92,8 @@ func TestMain(m *testing.M) { if err != nil { log.Fatal(err) } + testMockDB = packs.NewMockDB(testBackend.DB) + testBackend.DB = testMockDB project, err := testBackend.DB.FindProjectInfoByID( context.Background(), @@ -124,6 +135,21 @@ func TestMain(m *testing.M) { os.Exit(code) } +func triggerErrUpdateClientInfo(on bool) { + testMockDB. + On("UpdateClientInfoAfterPushPull", mock.Anything, mock.Anything, mock.Anything). + Unset() + if on { + testMockDB. + On("UpdateClientInfoAfterPushPull", mock.Anything, mock.Anything, mock.Anything). + Return(ErrUpdateClientInfoFailed) + } else { + testMockDB. + On("UpdateClientInfoAfterPushPull", mock.Anything, mock.Anything, mock.Anything). + Return(nil) + } +} + func TestPacks(t *testing.T) { t.Run("cannot detect change duplication due to clientInfo update failure", func(t *testing.T) { ctx := context.Background() @@ -135,6 +161,8 @@ func TestPacks(t *testing.T) { assert.NoError(t, err) project := projectInfo.ToProject() + triggerErrUpdateClientInfo(false) + activateResp, err := testClient.ActivateClient( context.Background(), connect.NewRequest(&api.ActivateClientRequest{ClientKey: helper.TestDocKey(t).String()})) @@ -197,11 +225,14 @@ func TestPacks(t *testing.T) { assert.NoError(t, err) // 2-1. An arbitrary failure occurs while updating clientInfo + triggerErrUpdateClientInfo(true) _, err = packs.PushPull(ctx, testBackend, project, clientInfo, docInfo, pack, packs.PushPullOptions{ Mode: types.SyncModePushPull, Status: document.StatusAttached, - }, true) - assert.ErrorIs(t, err, packs.ErrCheckpointTest) + }) + assert.ErrorIs(t, err, ErrUpdateClientInfoFailed) + + triggerErrUpdateClientInfo(false) // 2-2. pushed change is stored in the database changes, err := packs.FindChanges(ctx, testBackend, docInfo, 2, 2) @@ -226,7 +257,7 @@ func TestPacks(t *testing.T) { _, err = packs.PushPull(ctx, testBackend, project, clientInfo, docInfo, pack, packs.PushPullOptions{ Mode: types.SyncModePushPull, Status: document.StatusAttached, - }, false) + }) assert.NoError(t, err) // 3-2. duplicated change is not stored in the database diff --git a/server/rpc/yorkie_server.go b/server/rpc/yorkie_server.go index e9a19ed13..86c273080 100644 --- a/server/rpc/yorkie_server.go +++ b/server/rpc/yorkie_server.go @@ -164,7 +164,7 @@ func (s *yorkieServer) AttachDocument( pulled, err := packs.PushPull(ctx, s.backend, project, clientInfo, docInfo, pack, packs.PushPullOptions{ Mode: types.SyncModePushPull, Status: document.StatusAttached, - }, false) + }) if err != nil { return nil, err } @@ -258,7 +258,7 @@ func (s *yorkieServer) DetachDocument( pulled, err := packs.PushPull(ctx, s.backend, project, clientInfo, docInfo, pack, packs.PushPullOptions{ Mode: types.SyncModePushPull, Status: status, - }, false) + }) if err != nil { return nil, err } @@ -349,7 +349,7 @@ func (s *yorkieServer) PushPullChanges( pulled, err := packs.PushPull(ctx, s.backend, project, clientInfo, docInfo, pack, packs.PushPullOptions{ Mode: syncMode, Status: document.StatusAttached, - }, false) + }) if err != nil { return nil, err } @@ -546,7 +546,7 @@ func (s *yorkieServer) RemoveDocument( pulled, err := packs.PushPull(ctx, s.backend, project, clientInfo, docInfo, pack, packs.PushPullOptions{ Mode: types.SyncModePushPull, Status: document.StatusRemoved, - }, false) + }) if err != nil { return nil, err } diff --git a/test/bench/push_pull_bench_test.go b/test/bench/push_pull_bench_test.go index be5519ad9..ca6fff1bb 100644 --- a/test/bench/push_pull_bench_test.go +++ b/test/bench/push_pull_bench_test.go @@ -147,7 +147,7 @@ func benchmarkPushChanges( _, err = packs.PushPull(ctx, be, project, clientInfos[0], docInfo, pack, packs.PushPullOptions{ Mode: types.SyncModePushPull, Status: document.StatusAttached, - }, false) + }) assert.NoError(b, err) } } @@ -177,7 +177,7 @@ func benchmarkPullChanges( _, err = packs.PushPull(ctx, be, project, pusherClientInfo, docInfo, pushPack, packs.PushPullOptions{ Mode: types.SyncModePushPull, Status: document.StatusAttached, - }, false) + }) assert.NoError(b, err) docInfo, err = documents.FindDocInfoByRefKey(ctx, be, docRefKey) @@ -187,7 +187,7 @@ func benchmarkPullChanges( _, err = packs.PushPull(ctx, be, project, pullerClientInfo, docInfo, pullPack, packs.PushPullOptions{ Mode: types.SyncModePushPull, Status: document.StatusAttached, - }, false) + }) assert.NoError(b, err) } } @@ -220,7 +220,7 @@ func benchmarkPushSnapshots( pulled, err := packs.PushPull(ctx, be, project, clientInfos[0], docInfo, pushPack, packs.PushPullOptions{ Mode: types.SyncModePushPull, Status: document.StatusAttached, - }, false) + }) assert.NoError(b, err) b.StopTimer() @@ -259,7 +259,7 @@ func benchmarkPullSnapshot( _, err = packs.PushPull(ctx, be, project, pusherClientInfo, docInfo, pushPack, packs.PushPullOptions{ Mode: types.SyncModePushPull, Status: document.StatusAttached, - }, false) + }) assert.NoError(b, err) docInfo, err = documents.FindDocInfoByRefKey(ctx, be, docRefKey) @@ -269,7 +269,7 @@ func benchmarkPullSnapshot( _, err = packs.PushPull(ctx, be, project, pullerClientInfo, docInfo, pullPack, packs.PushPullOptions{ Mode: types.SyncModePushPull, Status: document.StatusAttached, - }, false) + }) assert.NoError(b, err) } } From b505814882b59a8378eb438ee9e22d4e8406bb76 Mon Sep 17 00:00:00 2001 From: Sejong Kim Date: Sat, 7 Sep 2024 23:44:30 +0900 Subject: [PATCH 5/7] Lint --- server/packs/mock_db.go | 144 ++++++++++++++++++++++++++++++++++------ 1 file changed, 122 insertions(+), 22 deletions(-) diff --git a/server/packs/mock_db.go b/server/packs/mock_db.go index 75e22d603..429caa8fc 100644 --- a/server/packs/mock_db.go +++ b/server/packs/mock_db.go @@ -13,12 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package packs import ( "context" + "fmt" "github.com/stretchr/testify/mock" + "github.com/yorkie-team/yorkie/api/types" "github.com/yorkie-team/yorkie/pkg/document" "github.com/yorkie-team/yorkie/pkg/document/change" @@ -27,11 +30,13 @@ import ( "github.com/yorkie-team/yorkie/server/backend/database" ) +// MockDB represents a mock database for testing purposes type MockDB struct { mock.Mock realDB database.Database } +// NewMockDB returns a mock database with a real database func NewMockDB(realDB database.Database) *MockDB { return &MockDB{ realDB: realDB, @@ -44,17 +49,30 @@ func (m *MockDB) Close() error { } // EnsureDefaultUserAndProject creates the default user and project if they do not exist. -func (m *MockDB) EnsureDefaultUserAndProject(ctx context.Context, username, password string, clientDeactivateThreshold string) (*database.UserInfo, *database.ProjectInfo, error) { +func (m *MockDB) EnsureDefaultUserAndProject( + ctx context.Context, + username, password string, + clientDeactivateThreshold string, +) (*database.UserInfo, *database.ProjectInfo, error) { return m.realDB.EnsureDefaultUserAndProject(ctx, username, password, clientDeactivateThreshold) } // CreateProjectInfo creates a new project. -func (m *MockDB) CreateProjectInfo(ctx context.Context, name string, owner types.ID, clientDeactivateThreshold string) (*database.ProjectInfo, error) { +func (m *MockDB) CreateProjectInfo( + ctx context.Context, + name string, + owner types.ID, + clientDeactivateThreshold string, +) (*database.ProjectInfo, error) { return m.realDB.CreateProjectInfo(ctx, name, owner, clientDeactivateThreshold) } // FindNextNCyclingProjectInfos finds the next N cycling projects from the given projectID. -func (m *MockDB) FindNextNCyclingProjectInfos(ctx context.Context, pageSize int, lastProjectID types.ID) ([]*database.ProjectInfo, error) { +func (m *MockDB) FindNextNCyclingProjectInfos( + ctx context.Context, + pageSize int, + lastProjectID types.ID, +) ([]*database.ProjectInfo, error) { return m.realDB.FindNextNCyclingProjectInfos(ctx, pageSize, lastProjectID) } @@ -74,7 +92,11 @@ func (m *MockDB) FindProjectInfoBySecretKey(ctx context.Context, secretKey strin } // FindProjectInfoByName returns a project by name. -func (m *MockDB) FindProjectInfoByName(ctx context.Context, owner types.ID, name string) (*database.ProjectInfo, error) { +func (m *MockDB) FindProjectInfoByName( + ctx context.Context, + owner types.ID, + name string, +) (*database.ProjectInfo, error) { return m.realDB.FindProjectInfoByName(ctx, owner, name) } @@ -84,12 +106,21 @@ func (m *MockDB) FindProjectInfoByID(ctx context.Context, id types.ID) (*databas } // UpdateProjectInfo updates the project info. -func (m *MockDB) UpdateProjectInfo(ctx context.Context, owner types.ID, id types.ID, fields *types.UpdatableProjectFields) (*database.ProjectInfo, error) { +func (m *MockDB) UpdateProjectInfo( + ctx context.Context, + owner types.ID, + id types.ID, + fields *types.UpdatableProjectFields, +) (*database.ProjectInfo, error) { return m.realDB.UpdateProjectInfo(ctx, owner, id, fields) } // CreateUserInfo creates a new user. -func (m *MockDB) CreateUserInfo(ctx context.Context, username string, hashedPassword string) (*database.UserInfo, error) { +func (m *MockDB) CreateUserInfo( + ctx context.Context, + username string, + hashedPassword string, +) (*database.UserInfo, error) { return m.realDB.CreateUserInfo(ctx, username, hashedPassword) } @@ -134,21 +165,34 @@ func (m *MockDB) FindClientInfoByRefKey(ctx context.Context, refKey types.Client } // UpdateClientInfoAfterPushPull updates the client from the given clientInfo after handling PushPull. -func (m *MockDB) UpdateClientInfoAfterPushPull(ctx context.Context, clientInfo *database.ClientInfo, docInfo *database.DocInfo) error { +func (m *MockDB) UpdateClientInfoAfterPushPull( + ctx context.Context, + clientInfo *database.ClientInfo, + docInfo *database.DocInfo, +) error { args := m.Called(ctx, clientInfo, docInfo) if args.Get(0) != nil { - return args.Error(0) + return fmt.Errorf("%w", args.Error(0)) } return m.realDB.UpdateClientInfoAfterPushPull(ctx, clientInfo, docInfo) } // FindDeactivateCandidatesPerProject finds the clients that need housekeeping per project. -func (m *MockDB) FindDeactivateCandidatesPerProject(ctx context.Context, project *database.ProjectInfo, candidatesLimit int) ([]*database.ClientInfo, error) { +func (m *MockDB) FindDeactivateCandidatesPerProject( + ctx context.Context, + project *database.ProjectInfo, + candidatesLimit int, +) ([]*database.ClientInfo, error) { return m.realDB.FindDeactivateCandidatesPerProject(ctx, project, candidatesLimit) } // FindDocInfoByKeyAndOwner finds the document of the given key. -func (m *MockDB) FindDocInfoByKeyAndOwner(ctx context.Context, clientRefKey types.ClientRefKey, docKey key.Key, createDocIfNotExist bool) (*database.DocInfo, error) { +func (m *MockDB) FindDocInfoByKeyAndOwner( + ctx context.Context, + clientRefKey types.ClientRefKey, + docKey key.Key, + createDocIfNotExist bool, +) (*database.DocInfo, error) { return m.realDB.FindDocInfoByKeyAndOwner(ctx, clientRefKey, docKey, createDocIfNotExist) } @@ -158,7 +202,11 @@ func (m *MockDB) FindDocInfoByKey(ctx context.Context, projectID types.ID, docKe } // FindDocInfosByKeys finds the documents of the given keys. -func (m *MockDB) FindDocInfosByKeys(ctx context.Context, projectID types.ID, docKeys []key.Key) ([]*database.DocInfo, error) { +func (m *MockDB) FindDocInfosByKeys( + ctx context.Context, + projectID types.ID, + docKeys []key.Key, +) ([]*database.DocInfo, error) { return m.realDB.FindDocInfosByKeys(ctx, projectID, docKeys) } @@ -173,7 +221,14 @@ func (m *MockDB) UpdateDocInfoStatusToRemoved(ctx context.Context, refKey types. } // CreateChangeInfos stores the given changes and doc info. -func (m *MockDB) CreateChangeInfos(ctx context.Context, projectID types.ID, docInfo *database.DocInfo, initialServerSeq int64, changes []*change.Change, isRemoved bool) error { +func (m *MockDB) CreateChangeInfos( + ctx context.Context, + projectID types.ID, + docInfo *database.DocInfo, + initialServerSeq int64, + changes []*change.Change, + isRemoved bool, +) error { return m.realDB.CreateChangeInfos(ctx, projectID, docInfo, initialServerSeq, changes, isRemoved) } @@ -183,27 +238,49 @@ func (m *MockDB) PurgeStaleChanges(ctx context.Context, docRefKey types.DocRefKe } // FindChangesBetweenServerSeqs returns the changes between two server sequences. -func (m *MockDB) FindChangesBetweenServerSeqs(ctx context.Context, docRefKey types.DocRefKey, from int64, to int64) ([]*change.Change, error) { +func (m *MockDB) FindChangesBetweenServerSeqs( + ctx context.Context, + docRefKey types.DocRefKey, + from int64, + to int64, +) ([]*change.Change, error) { return m.realDB.FindChangesBetweenServerSeqs(ctx, docRefKey, from, to) } // FindChangeInfosBetweenServerSeqs returns the changeInfos between two server sequences. -func (m *MockDB) FindChangeInfosBetweenServerSeqs(ctx context.Context, docRefKey types.DocRefKey, from int64, to int64) ([]*database.ChangeInfo, error) { +func (m *MockDB) FindChangeInfosBetweenServerSeqs( + ctx context.Context, + docRefKey types.DocRefKey, + from int64, + to int64, +) ([]*database.ChangeInfo, error) { return m.realDB.FindChangeInfosBetweenServerSeqs(ctx, docRefKey, from, to) } // CreateSnapshotInfo stores the snapshot of the given document. -func (m *MockDB) CreateSnapshotInfo(ctx context.Context, docRefKey types.DocRefKey, doc *document.InternalDocument) error { +func (m *MockDB) CreateSnapshotInfo( + ctx context.Context, + docRefKey types.DocRefKey, + doc *document.InternalDocument, +) error { return m.realDB.CreateSnapshotInfo(ctx, docRefKey, doc) } // FindSnapshotInfoByRefKey returns the snapshot by the given refKey. -func (m *MockDB) FindSnapshotInfoByRefKey(ctx context.Context, refKey types.SnapshotRefKey) (*database.SnapshotInfo, error) { +func (m *MockDB) FindSnapshotInfoByRefKey( + ctx context.Context, + refKey types.SnapshotRefKey, +) (*database.SnapshotInfo, error) { return m.realDB.FindSnapshotInfoByRefKey(ctx, refKey) } // FindClosestSnapshotInfo finds the last snapshot of the given document. -func (m *MockDB) FindClosestSnapshotInfo(ctx context.Context, docRefKey types.DocRefKey, serverSeq int64, includeSnapshot bool) (*database.SnapshotInfo, error) { +func (m *MockDB) FindClosestSnapshotInfo( + ctx context.Context, + docRefKey types.DocRefKey, + serverSeq int64, + includeSnapshot bool, +) (*database.SnapshotInfo, error) { return m.realDB.FindClosestSnapshotInfo(ctx, docRefKey, serverSeq, includeSnapshot) } @@ -213,26 +290,49 @@ func (m *MockDB) FindMinSyncedSeqInfo(ctx context.Context, docRefKey types.DocRe } // UpdateAndFindMinSyncedTicket updates the given serverSeq of the given client and returns the min synced ticket. -func (m *MockDB) UpdateAndFindMinSyncedTicket(ctx context.Context, clientInfo *database.ClientInfo, docRefKey types.DocRefKey, serverSeq int64) (*time.Ticket, error) { +func (m *MockDB) UpdateAndFindMinSyncedTicket( + ctx context.Context, + clientInfo *database.ClientInfo, + docRefKey types.DocRefKey, + serverSeq int64, +) (*time.Ticket, error) { return m.realDB.UpdateAndFindMinSyncedTicket(ctx, clientInfo, docRefKey, serverSeq) } // FindDocInfosByPaging returns the docInfos of the given paging. -func (m *MockDB) FindDocInfosByPaging(ctx context.Context, projectID types.ID, paging types.Paging[types.ID]) ([]*database.DocInfo, error) { +func (m *MockDB) FindDocInfosByPaging( + ctx context.Context, + projectID types.ID, + paging types.Paging[types.ID], +) ([]*database.DocInfo, error) { return m.realDB.FindDocInfosByPaging(ctx, projectID, paging) } // FindDocInfosByQuery returns the docInfos which match the given query. -func (m *MockDB) FindDocInfosByQuery(ctx context.Context, projectID types.ID, query string, pageSize int) (*types.SearchResult[*database.DocInfo], error) { +func (m *MockDB) FindDocInfosByQuery( + ctx context.Context, + projectID types.ID, + query string, + pageSize int, +) (*types.SearchResult[*database.DocInfo], error) { return m.realDB.FindDocInfosByQuery(ctx, projectID, query, pageSize) } // UpdateSyncedSeq updates the syncedSeq of the given client. -func (m *MockDB) UpdateSyncedSeq(ctx context.Context, clientInfo *database.ClientInfo, docRefKey types.DocRefKey, serverSeq int64) error { +func (m *MockDB) UpdateSyncedSeq( + ctx context.Context, + clientInfo *database.ClientInfo, + docRefKey types.DocRefKey, + serverSeq int64, +) error { return m.realDB.UpdateSyncedSeq(ctx, clientInfo, docRefKey, serverSeq) } // IsDocumentAttached returns whether the given document is attached to clients. -func (m *MockDB) IsDocumentAttached(ctx context.Context, docRefKey types.DocRefKey, excludeClientID types.ID) (bool, error) { +func (m *MockDB) IsDocumentAttached( + ctx context.Context, + docRefKey types.DocRefKey, + excludeClientID types.ID, +) (bool, error) { return m.realDB.IsDocumentAttached(ctx, docRefKey, excludeClientID) } From 46832c10bfb07837328b37067e36990ee6f6c8f6 Mon Sep 17 00:00:00 2001 From: Sejong Kim Date: Sat, 7 Sep 2024 23:56:01 +0900 Subject: [PATCH 6/7] Skip test --- server/packs/packs_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/packs/packs_test.go b/server/packs/packs_test.go index d343b61a2..026e935e6 100644 --- a/server/packs/packs_test.go +++ b/server/packs/packs_test.go @@ -152,6 +152,7 @@ func triggerErrUpdateClientInfo(on bool) { func TestPacks(t *testing.T) { t.Run("cannot detect change duplication due to clientInfo update failure", func(t *testing.T) { + t.Skip("remove this after resolving pushpull consistency problem") ctx := context.Background() projectInfo, err := testBackend.DB.FindProjectInfoByID( From dfc28438723928f18e350d97bd6ba28bff23ef46 Mon Sep 17 00:00:00 2001 From: kokodak Date: Thu, 12 Sep 2024 16:07:41 +0900 Subject: [PATCH 7/7] Change MockDB structure and remove boilerplate code --- server/packs/mock_db.go | 338 ------------------------------------- server/packs/packs_test.go | 125 ++++++++------ 2 files changed, 78 insertions(+), 385 deletions(-) delete mode 100644 server/packs/mock_db.go diff --git a/server/packs/mock_db.go b/server/packs/mock_db.go deleted file mode 100644 index 429caa8fc..000000000 --- a/server/packs/mock_db.go +++ /dev/null @@ -1,338 +0,0 @@ -/* - * Copyright 2024 The Yorkie Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package packs - -import ( - "context" - "fmt" - - "github.com/stretchr/testify/mock" - - "github.com/yorkie-team/yorkie/api/types" - "github.com/yorkie-team/yorkie/pkg/document" - "github.com/yorkie-team/yorkie/pkg/document/change" - "github.com/yorkie-team/yorkie/pkg/document/key" - "github.com/yorkie-team/yorkie/pkg/document/time" - "github.com/yorkie-team/yorkie/server/backend/database" -) - -// MockDB represents a mock database for testing purposes -type MockDB struct { - mock.Mock - realDB database.Database -} - -// NewMockDB returns a mock database with a real database -func NewMockDB(realDB database.Database) *MockDB { - return &MockDB{ - realDB: realDB, - } -} - -// Close all resources of this client. -func (m *MockDB) Close() error { - return m.realDB.Close() -} - -// EnsureDefaultUserAndProject creates the default user and project if they do not exist. -func (m *MockDB) EnsureDefaultUserAndProject( - ctx context.Context, - username, password string, - clientDeactivateThreshold string, -) (*database.UserInfo, *database.ProjectInfo, error) { - return m.realDB.EnsureDefaultUserAndProject(ctx, username, password, clientDeactivateThreshold) -} - -// CreateProjectInfo creates a new project. -func (m *MockDB) CreateProjectInfo( - ctx context.Context, - name string, - owner types.ID, - clientDeactivateThreshold string, -) (*database.ProjectInfo, error) { - return m.realDB.CreateProjectInfo(ctx, name, owner, clientDeactivateThreshold) -} - -// FindNextNCyclingProjectInfos finds the next N cycling projects from the given projectID. -func (m *MockDB) FindNextNCyclingProjectInfos( - ctx context.Context, - pageSize int, - lastProjectID types.ID, -) ([]*database.ProjectInfo, error) { - return m.realDB.FindNextNCyclingProjectInfos(ctx, pageSize, lastProjectID) -} - -// ListProjectInfos returns all project infos owned by owner. -func (m *MockDB) ListProjectInfos(ctx context.Context, owner types.ID) ([]*database.ProjectInfo, error) { - return m.realDB.ListProjectInfos(ctx, owner) -} - -// FindProjectInfoByPublicKey returns a project by public key. -func (m *MockDB) FindProjectInfoByPublicKey(ctx context.Context, publicKey string) (*database.ProjectInfo, error) { - return m.realDB.FindProjectInfoByPublicKey(ctx, publicKey) -} - -// FindProjectInfoBySecretKey returns a project by secret key. -func (m *MockDB) FindProjectInfoBySecretKey(ctx context.Context, secretKey string) (*database.ProjectInfo, error) { - return m.realDB.FindProjectInfoBySecretKey(ctx, secretKey) -} - -// FindProjectInfoByName returns a project by name. -func (m *MockDB) FindProjectInfoByName( - ctx context.Context, - owner types.ID, - name string, -) (*database.ProjectInfo, error) { - return m.realDB.FindProjectInfoByName(ctx, owner, name) -} - -// FindProjectInfoByID returns a project by the given id. -func (m *MockDB) FindProjectInfoByID(ctx context.Context, id types.ID) (*database.ProjectInfo, error) { - return m.realDB.FindProjectInfoByID(ctx, id) -} - -// UpdateProjectInfo updates the project info. -func (m *MockDB) UpdateProjectInfo( - ctx context.Context, - owner types.ID, - id types.ID, - fields *types.UpdatableProjectFields, -) (*database.ProjectInfo, error) { - return m.realDB.UpdateProjectInfo(ctx, owner, id, fields) -} - -// CreateUserInfo creates a new user. -func (m *MockDB) CreateUserInfo( - ctx context.Context, - username string, - hashedPassword string, -) (*database.UserInfo, error) { - return m.realDB.CreateUserInfo(ctx, username, hashedPassword) -} - -// DeleteUserInfoByName deletes a user by name. -func (m *MockDB) DeleteUserInfoByName(ctx context.Context, username string) error { - return m.realDB.DeleteUserInfoByName(ctx, username) -} - -// ChangeUserPassword changes to new password for user. -func (m *MockDB) ChangeUserPassword(ctx context.Context, username, hashedNewPassword string) error { - return m.realDB.ChangeUserPassword(ctx, username, hashedNewPassword) -} - -// FindUserInfoByID returns a user by ID. -func (m *MockDB) FindUserInfoByID(ctx context.Context, clientID types.ID) (*database.UserInfo, error) { - return m.realDB.FindUserInfoByID(ctx, clientID) -} - -// FindUserInfoByName returns a user by username. -func (m *MockDB) FindUserInfoByName(ctx context.Context, username string) (*database.UserInfo, error) { - return m.realDB.FindUserInfoByName(ctx, username) -} - -// ListUserInfos returns all users. -func (m *MockDB) ListUserInfos(ctx context.Context) ([]*database.UserInfo, error) { - return m.realDB.ListUserInfos(ctx) -} - -// ActivateClient activates the client of the given key. -func (m *MockDB) ActivateClient(ctx context.Context, projectID types.ID, key string) (*database.ClientInfo, error) { - return m.realDB.ActivateClient(ctx, projectID, key) -} - -// DeactivateClient deactivates the client of the given refKey and updates document statuses as detached. -func (m *MockDB) DeactivateClient(ctx context.Context, refKey types.ClientRefKey) (*database.ClientInfo, error) { - return m.realDB.DeactivateClient(ctx, refKey) -} - -// FindClientInfoByRefKey finds the client of the given refKey. -func (m *MockDB) FindClientInfoByRefKey(ctx context.Context, refKey types.ClientRefKey) (*database.ClientInfo, error) { - return m.realDB.FindClientInfoByRefKey(ctx, refKey) -} - -// UpdateClientInfoAfterPushPull updates the client from the given clientInfo after handling PushPull. -func (m *MockDB) UpdateClientInfoAfterPushPull( - ctx context.Context, - clientInfo *database.ClientInfo, - docInfo *database.DocInfo, -) error { - args := m.Called(ctx, clientInfo, docInfo) - if args.Get(0) != nil { - return fmt.Errorf("%w", args.Error(0)) - } - return m.realDB.UpdateClientInfoAfterPushPull(ctx, clientInfo, docInfo) -} - -// FindDeactivateCandidatesPerProject finds the clients that need housekeeping per project. -func (m *MockDB) FindDeactivateCandidatesPerProject( - ctx context.Context, - project *database.ProjectInfo, - candidatesLimit int, -) ([]*database.ClientInfo, error) { - return m.realDB.FindDeactivateCandidatesPerProject(ctx, project, candidatesLimit) -} - -// FindDocInfoByKeyAndOwner finds the document of the given key. -func (m *MockDB) FindDocInfoByKeyAndOwner( - ctx context.Context, - clientRefKey types.ClientRefKey, - docKey key.Key, - createDocIfNotExist bool, -) (*database.DocInfo, error) { - return m.realDB.FindDocInfoByKeyAndOwner(ctx, clientRefKey, docKey, createDocIfNotExist) -} - -// FindDocInfoByKey finds the document of the given key. -func (m *MockDB) FindDocInfoByKey(ctx context.Context, projectID types.ID, docKey key.Key) (*database.DocInfo, error) { - return m.realDB.FindDocInfoByKey(ctx, projectID, docKey) -} - -// FindDocInfosByKeys finds the documents of the given keys. -func (m *MockDB) FindDocInfosByKeys( - ctx context.Context, - projectID types.ID, - docKeys []key.Key, -) ([]*database.DocInfo, error) { - return m.realDB.FindDocInfosByKeys(ctx, projectID, docKeys) -} - -// FindDocInfoByRefKey finds a docInfo of the given refKey. -func (m *MockDB) FindDocInfoByRefKey(ctx context.Context, refKey types.DocRefKey) (*database.DocInfo, error) { - return m.realDB.FindDocInfoByRefKey(ctx, refKey) -} - -// UpdateDocInfoStatusToRemoved updates the document status to removed. -func (m *MockDB) UpdateDocInfoStatusToRemoved(ctx context.Context, refKey types.DocRefKey) error { - return m.realDB.UpdateDocInfoStatusToRemoved(ctx, refKey) -} - -// CreateChangeInfos stores the given changes and doc info. -func (m *MockDB) CreateChangeInfos( - ctx context.Context, - projectID types.ID, - docInfo *database.DocInfo, - initialServerSeq int64, - changes []*change.Change, - isRemoved bool, -) error { - return m.realDB.CreateChangeInfos(ctx, projectID, docInfo, initialServerSeq, changes, isRemoved) -} - -// PurgeStaleChanges deletes changes before the smallest in synced seqs. -func (m *MockDB) PurgeStaleChanges(ctx context.Context, docRefKey types.DocRefKey) error { - return m.realDB.PurgeStaleChanges(ctx, docRefKey) -} - -// FindChangesBetweenServerSeqs returns the changes between two server sequences. -func (m *MockDB) FindChangesBetweenServerSeqs( - ctx context.Context, - docRefKey types.DocRefKey, - from int64, - to int64, -) ([]*change.Change, error) { - return m.realDB.FindChangesBetweenServerSeqs(ctx, docRefKey, from, to) -} - -// FindChangeInfosBetweenServerSeqs returns the changeInfos between two server sequences. -func (m *MockDB) FindChangeInfosBetweenServerSeqs( - ctx context.Context, - docRefKey types.DocRefKey, - from int64, - to int64, -) ([]*database.ChangeInfo, error) { - return m.realDB.FindChangeInfosBetweenServerSeqs(ctx, docRefKey, from, to) -} - -// CreateSnapshotInfo stores the snapshot of the given document. -func (m *MockDB) CreateSnapshotInfo( - ctx context.Context, - docRefKey types.DocRefKey, - doc *document.InternalDocument, -) error { - return m.realDB.CreateSnapshotInfo(ctx, docRefKey, doc) -} - -// FindSnapshotInfoByRefKey returns the snapshot by the given refKey. -func (m *MockDB) FindSnapshotInfoByRefKey( - ctx context.Context, - refKey types.SnapshotRefKey, -) (*database.SnapshotInfo, error) { - return m.realDB.FindSnapshotInfoByRefKey(ctx, refKey) -} - -// FindClosestSnapshotInfo finds the last snapshot of the given document. -func (m *MockDB) FindClosestSnapshotInfo( - ctx context.Context, - docRefKey types.DocRefKey, - serverSeq int64, - includeSnapshot bool, -) (*database.SnapshotInfo, error) { - return m.realDB.FindClosestSnapshotInfo(ctx, docRefKey, serverSeq, includeSnapshot) -} - -// FindMinSyncedSeqInfo finds the minimum synced sequence info. -func (m *MockDB) FindMinSyncedSeqInfo(ctx context.Context, docRefKey types.DocRefKey) (*database.SyncedSeqInfo, error) { - return m.realDB.FindMinSyncedSeqInfo(ctx, docRefKey) -} - -// UpdateAndFindMinSyncedTicket updates the given serverSeq of the given client and returns the min synced ticket. -func (m *MockDB) UpdateAndFindMinSyncedTicket( - ctx context.Context, - clientInfo *database.ClientInfo, - docRefKey types.DocRefKey, - serverSeq int64, -) (*time.Ticket, error) { - return m.realDB.UpdateAndFindMinSyncedTicket(ctx, clientInfo, docRefKey, serverSeq) -} - -// FindDocInfosByPaging returns the docInfos of the given paging. -func (m *MockDB) FindDocInfosByPaging( - ctx context.Context, - projectID types.ID, - paging types.Paging[types.ID], -) ([]*database.DocInfo, error) { - return m.realDB.FindDocInfosByPaging(ctx, projectID, paging) -} - -// FindDocInfosByQuery returns the docInfos which match the given query. -func (m *MockDB) FindDocInfosByQuery( - ctx context.Context, - projectID types.ID, - query string, - pageSize int, -) (*types.SearchResult[*database.DocInfo], error) { - return m.realDB.FindDocInfosByQuery(ctx, projectID, query, pageSize) -} - -// UpdateSyncedSeq updates the syncedSeq of the given client. -func (m *MockDB) UpdateSyncedSeq( - ctx context.Context, - clientInfo *database.ClientInfo, - docRefKey types.DocRefKey, - serverSeq int64, -) error { - return m.realDB.UpdateSyncedSeq(ctx, clientInfo, docRefKey, serverSeq) -} - -// IsDocumentAttached returns whether the given document is attached to clients. -func (m *MockDB) IsDocumentAttached( - ctx context.Context, - docRefKey types.DocRefKey, - excludeClientID types.ID, -) (bool, error) { - return m.realDB.IsDocumentAttached(ctx, docRefKey, excludeClientID) -} diff --git a/server/packs/packs_test.go b/server/packs/packs_test.go index 026e935e6..42a85b642 100644 --- a/server/packs/packs_test.go +++ b/server/packs/packs_test.go @@ -28,7 +28,6 @@ import ( "connectrpc.com/connect" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/yorkie-team/yorkie/api/converter" "github.com/yorkie-team/yorkie/api/types" @@ -60,39 +59,65 @@ var ( testRPCAddr = fmt.Sprintf("localhost:%d", helper.RPCPort) testClient v1connect.YorkieServiceClient testBackend *backend.Backend - testMockDB *packs.MockDB + testMockDB *MockDB ) +// MockDB represents a mock database for testing purposes +type MockDB struct { + database.Database + mockUpdateClientInfoAfterPushPull func(context.Context, *database.ClientInfo, *database.DocInfo) error +} + +// NewMockDB returns a mock database with a real database +func NewMockDB(database database.Database) *MockDB { + return &MockDB{ + Database: database, + } +} + +func (m *MockDB) UpdateClientInfoAfterPushPull( + ctx context.Context, + clientInfo *database.ClientInfo, + docInfo *database.DocInfo, +) error { + if m.mockUpdateClientInfoAfterPushPull != nil { + return m.mockUpdateClientInfoAfterPushPull(ctx, clientInfo, docInfo) + } + return m.Database.UpdateClientInfoAfterPushPull(ctx, clientInfo, docInfo) +} + func TestMain(m *testing.M) { met, err := prometheus.NewMetrics() if err != nil { log.Fatal(err) } - testBackend, err = backend.New(&backend.Config{ - AdminUser: helper.AdminUser, - AdminPassword: helper.AdminPassword, - UseDefaultProject: helper.UseDefaultProject, - ClientDeactivateThreshold: helper.ClientDeactivateThreshold, - SnapshotThreshold: helper.SnapshotThreshold, - AuthWebhookCacheSize: helper.AuthWebhookSize, - ProjectInfoCacheSize: helper.ProjectInfoCacheSize, - ProjectInfoCacheTTL: helper.ProjectInfoCacheTTL.String(), - AdminTokenDuration: helper.AdminTokenDuration, - }, &mongo.Config{ - ConnectionURI: helper.MongoConnectionURI, - YorkieDatabase: helper.TestDBName(), - ConnectionTimeout: helper.MongoConnectionTimeout, - PingTimeout: helper.MongoPingTimeout, - }, &housekeeping.Config{ - Interval: helper.HousekeepingInterval.String(), - CandidatesLimitPerProject: helper.HousekeepingCandidatesLimitPerProject, - ProjectFetchSize: helper.HousekeepingProjectFetchSize, - }, met) + testBackend, err = backend.New( + &backend.Config{ + AdminUser: helper.AdminUser, + AdminPassword: helper.AdminPassword, + UseDefaultProject: helper.UseDefaultProject, + ClientDeactivateThreshold: helper.ClientDeactivateThreshold, + SnapshotThreshold: helper.SnapshotThreshold, + AuthWebhookCacheSize: helper.AuthWebhookSize, + ProjectInfoCacheSize: helper.ProjectInfoCacheSize, + ProjectInfoCacheTTL: helper.ProjectInfoCacheTTL.String(), + AdminTokenDuration: helper.AdminTokenDuration, + }, &mongo.Config{ + ConnectionURI: helper.MongoConnectionURI, + YorkieDatabase: helper.TestDBName(), + ConnectionTimeout: helper.MongoConnectionTimeout, + PingTimeout: helper.MongoPingTimeout, + }, &housekeeping.Config{ + Interval: helper.HousekeepingInterval.String(), + CandidatesLimitPerProject: helper.HousekeepingCandidatesLimitPerProject, + ProjectFetchSize: helper.HousekeepingProjectFetchSize, + }, met, + ) if err != nil { log.Fatal(err) } - testMockDB = packs.NewMockDB(testBackend.DB) + testMockDB = NewMockDB(testBackend.DB) testBackend.DB = testMockDB project, err := testBackend.DB.FindProjectInfoByID( @@ -103,9 +128,11 @@ func TestMain(m *testing.M) { log.Fatal(err) } - testRPCServer, err = rpc.NewServer(&rpc.Config{ - Port: helper.RPCPort, - }, testBackend) + testRPCServer, err = rpc.NewServer( + &rpc.Config{ + Port: helper.RPCPort, + }, testBackend, + ) if err != nil { log.Fatal(err) } @@ -136,17 +163,16 @@ func TestMain(m *testing.M) { } func triggerErrUpdateClientInfo(on bool) { - testMockDB. - On("UpdateClientInfoAfterPushPull", mock.Anything, mock.Anything, mock.Anything). - Unset() if on { - testMockDB. - On("UpdateClientInfoAfterPushPull", mock.Anything, mock.Anything, mock.Anything). - Return(ErrUpdateClientInfoFailed) + testMockDB.mockUpdateClientInfoAfterPushPull = func( + context.Context, + *database.ClientInfo, + *database.DocInfo, + ) error { + return ErrUpdateClientInfoFailed + } } else { - testMockDB. - On("UpdateClientInfoAfterPushPull", mock.Anything, mock.Anything, mock.Anything). - Return(nil) + testMockDB.mockUpdateClientInfoAfterPushPull = nil } } @@ -177,13 +203,15 @@ func TestPacks(t *testing.T) { ChangePack: &api.ChangePack{ DocumentKey: helper.TestDocKey(t).String(), Checkpoint: &api.Checkpoint{ServerSeq: 0, ClientSeq: 1}, - Changes: []*api.Change{{ - Id: &api.ChangeID{ - ClientSeq: 1, - Lamport: 1, - ActorId: clientID, + Changes: []*api.Change{ + { + Id: &api.ChangeID{ + ClientSeq: 1, + Lamport: 1, + ActorId: clientID, + }, }, - }}, + }, }, }, )) @@ -215,18 +243,21 @@ func TestPacks(t *testing.T) { pack, err := converter.FromChangePack(&api.ChangePack{ DocumentKey: helper.TestDocKey(t).String(), Checkpoint: &api.Checkpoint{ServerSeq: 0, ClientSeq: 2}, - Changes: []*api.Change{{ - Id: &api.ChangeID{ - ClientSeq: 2, - Lamport: 2, - ActorId: clientID, + Changes: []*api.Change{ + { + Id: &api.ChangeID{ + ClientSeq: 2, + Lamport: 2, + ActorId: clientID, + }, }, - }}, + }, }) assert.NoError(t, err) // 2-1. An arbitrary failure occurs while updating clientInfo triggerErrUpdateClientInfo(true) + _, err = packs.PushPull(ctx, testBackend, project, clientInfo, docInfo, pack, packs.PushPullOptions{ Mode: types.SyncModePushPull, Status: document.StatusAttached,