diff --git a/api/types/resource_ref_key.go b/api/types/resource_ref_key.go index a4410a95e..6f44fba0d 100644 --- a/api/types/resource_ref_key.go +++ b/api/types/resource_ref_key.go @@ -24,6 +24,9 @@ import ( "github.com/yorkie-team/yorkie/pkg/document/key" ) +// ErrInvalidDocRefKeySetInput is returned when the input of DocRefKey Set is invalid. +var ErrInvalidDocRefKeySetInput = errors.New("use the format 'docKey,docID' for the input") + // DocRefKey represents an identifier used to reference a document. type DocRefKey struct { Key key.Key @@ -40,7 +43,7 @@ func (r *DocRefKey) String() string { func (r *DocRefKey) Set(v string) error { parsed := strings.Split(v, ",") if len(parsed) != 2 { - return errors.New("use the format 'docKey,docID' for the input") + return ErrInvalidDocRefKeySetInput } r.Key = key.Key(parsed[0]) r.ID = ID(parsed[1]) diff --git a/api/types/resource_ref_key_test.go b/api/types/resource_ref_key_test.go new file mode 100644 index 000000000..6ae555f6d --- /dev/null +++ b/api/types/resource_ref_key_test.go @@ -0,0 +1,45 @@ +/* + * Copyright 2023 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 types_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/yorkie-team/yorkie/api/types" + "github.com/yorkie-team/yorkie/pkg/document/key" +) + +func TestResourceRefKey(t *testing.T) { + t.Run("DocRefKey Set test", func(t *testing.T) { + docKey := key.Key("docKey") + docID := types.ID("docID") + docRef := types.DocRefKey{} + + // 01. Give an invalid input to Set. + err := docRef.Set("abc") + assert.ErrorIs(t, err, types.ErrInvalidDocRefKeySetInput) + + // 02. Give a valid input to Set. + err = docRef.Set(fmt.Sprintf("%s,%s", docKey, docID)) + assert.NoError(t, err) + assert.Equal(t, docRef.Key, docKey) + assert.Equal(t, docRef.ID, docID) + }) +} diff --git a/server/backend/database/mongo/registry.go b/server/backend/database/mongo/registry.go index f49e326b8..ebcc54c74 100644 --- a/server/backend/database/mongo/registry.go +++ b/server/backend/database/mongo/registry.go @@ -48,56 +48,59 @@ func NewRegistryBuilder() *bsoncodec.RegistryBuilder { // containing a number of `doc_key`.`doc_id`.{`client_seq`, `server_seq`, `status`}s. rb.RegisterTypeDecoder( reflect.TypeOf(make(database.ClientDocInfoMap)), - bsoncodec.ValueDecoderFunc(func(_ bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { - docs, err := vr.ReadDocument() - if err != nil { - return fmt.Errorf("read documents: %w", err) - } - if val.IsNil() { - val.Set(reflect.MakeMap(val.Type())) - } + bsoncodec.ValueDecoderFunc(clientDocumentsDecoder), + ) - for { - docKey, docInfoByDocIDMapReader, err := docs.ReadElement() - if err != nil { - if err == bsonrw.ErrEOD { - break - } - return fmt.Errorf("read the element in documents: %w", err) - } - docInfoByDocIDMap, err := docInfoByDocIDMapReader.ReadDocument() - if err != nil { - return fmt.Errorf("read docInfoByDocID: %w", err) - } - for { - docID, docInfoReader, err := docInfoByDocIDMap.ReadElement() - if err != nil { - if err == bsonrw.ErrEOD { - break - } - return fmt.Errorf("read the element in docInfoByDocID: %w", err) - } + return rb +} - docInfo := &database.ClientDocInfo{} - docInfoDecoder, err := bson.NewDecoder(docInfoReader) - if err != nil { - return fmt.Errorf("create docInfoDecoder: %w", err) - } - err = docInfoDecoder.Decode(docInfo) - if err != nil { - return fmt.Errorf("decode docInfo: %w", err) - } +func clientDocumentsDecoder(_ bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + docs, err := vr.ReadDocument() + if err != nil { + return fmt.Errorf("read documents: %w", err) + } + if val.IsNil() { + val.Set(reflect.MakeMap(val.Type())) + } - docRef := reflect.ValueOf(types.DocRefKey{ - Key: key.Key(docKey), - ID: types.ID(docID), - }) - val.SetMapIndex(docRef, reflect.ValueOf(docInfo)) + for { + docKey, docInfoByDocIDMapReader, err := docs.ReadElement() + if err != nil { + if err == bsonrw.ErrEOD { + break + } + return fmt.Errorf("read the element in documents: %w", err) + } + docInfoByDocIDMap, err := docInfoByDocIDMapReader.ReadDocument() + if err != nil { + return fmt.Errorf("read docInfoByDocID: %w", err) + } + for { + docID, docInfoReader, err := docInfoByDocIDMap.ReadElement() + if err != nil { + if err == bsonrw.ErrEOD { + break } + return fmt.Errorf("read the element in docInfoByDocID: %w", err) + } + + docInfo := &database.ClientDocInfo{} + docInfoDecoder, err := bson.NewDecoder(docInfoReader) + if err != nil { + return fmt.Errorf("create docInfoDecoder: %w", err) + } + err = docInfoDecoder.Decode(docInfo) + if err != nil { + return fmt.Errorf("decode docInfo: %w", err) } - return nil - })) + docRef := reflect.ValueOf(types.DocRefKey{ + Key: key.Key(docKey), + ID: types.ID(docID), + }) + val.SetMapIndex(docRef, reflect.ValueOf(docInfo)) + } + } - return rb + return nil } diff --git a/server/backend/database/mongo/registry_test.go b/server/backend/database/mongo/registry_test.go index 83b7cf35c..ee67aac6d 100644 --- a/server/backend/database/mongo/registry_test.go +++ b/server/backend/database/mongo/registry_test.go @@ -17,13 +17,17 @@ package mongo import ( + "reflect" "testing" "github.com/stretchr/testify/assert" "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/bsoncodec" + "go.mongodb.org/mongo-driver/bson/bsonrw" "go.mongodb.org/mongo-driver/bson/primitive" "github.com/yorkie-team/yorkie/api/types" + "github.com/yorkie-team/yorkie/pkg/document/key" "github.com/yorkie-team/yorkie/server/backend/database" ) @@ -41,3 +45,63 @@ func TestRegistry(t *testing.T) { assert.Equal(t, id, info.ID) } + +func TestDecoder(t *testing.T) { + t.Run("clientDocumentsDecoder test", func(t *testing.T) { + docs := []struct { + docRefKey types.DocRefKey + docInfo database.ClientDocInfo + }{ + { + docRefKey: types.DocRefKey{ + Key: key.Key("test-doc-key1"), + ID: types.ID("test-doc-id1"), + }, + docInfo: database.ClientDocInfo{ + ClientSeq: 0, + ServerSeq: 0, + Status: database.DocumentAttached, + }, + }, + { + docRefKey: types.DocRefKey{ + Key: key.Key("test-doc-key2"), + ID: types.ID("test-doc-id2"), + }, + docInfo: database.ClientDocInfo{ + ClientSeq: 0, + ServerSeq: 0, + Status: database.DocumentDetached, + }, + }, + } + + bsonDocs := make(bson.M) + for _, doc := range docs { + bsonDocs[doc.docRefKey.Key.String()] = bson.M{ + doc.docRefKey.ID.String(): bson.M{ + "client_seq": doc.docInfo.ClientSeq, + "server_seq": doc.docInfo.ServerSeq, + "status": doc.docInfo.Status, + }, + } + } + + marshaledDocs, err := bson.Marshal(bsonDocs) + assert.NoError(t, err) + + clientDocInfoMap := make(database.ClientDocInfoMap) + err = clientDocumentsDecoder( + bsoncodec.DecodeContext{}, + bsonrw.NewBSONDocumentReader(marshaledDocs), + reflect.ValueOf(clientDocInfoMap), + ) + assert.NoError(t, err) + assert.Len(t, clientDocInfoMap, len(docs)) + for _, doc := range docs { + assert.Equal(t, doc.docInfo.ClientSeq, clientDocInfoMap[doc.docRefKey].ClientSeq) + assert.Equal(t, doc.docInfo.ServerSeq, clientDocInfoMap[doc.docRefKey].ServerSeq) + assert.Equal(t, doc.docInfo.Status, clientDocInfoMap[doc.docRefKey].Status) + } + }) +}