Refactor repository

This commit is contained in:
Anton Zadvorny 2024-01-12 12:20:15 +03:00
parent f1c6820d66
commit 2716580834
2 changed files with 200 additions and 5 deletions

View File

@ -7,7 +7,7 @@ import (
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
driver "go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
@ -28,11 +28,11 @@ type RepositoryInterface[T Document] interface {
}
type Repository[T Document] struct {
collection *driver.Collection
indexes []driver.IndexModel
collection *mongo.Collection
indexes []mongo.IndexModel
}
func NewRepository[T Document](collection *driver.Collection, indexes []driver.IndexModel) *Repository[T] {
func NewRepository[T Document](collection *mongo.Collection, indexes []mongo.IndexModel) *Repository[T] {
return &Repository[T]{collection: collection, indexes: withTimestampIndexes(indexes)}
}
@ -112,6 +112,6 @@ func (s *Repository[T]) EnsureIndexes(ctx context.Context, createIndexes bool) (
return ensureIndexes(ctx, s.collection, s.indexes, createIndexes)
}
func repositoryError(collection *driver.Collection, op string, err error) error {
func repositoryError(collection *mongo.Collection, op string, err error) error {
return fmt.Errorf("%s.%s: %v", collection.Name(), op, err)
}

195
repository_test.go Normal file
View File

@ -0,0 +1,195 @@
package leaf
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/assert"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/integration/mtest"
)
type tModel struct {
Name string `bson:"name"`
Base `bson:",inline"`
}
func tModelToBSON(model *tModel) bson.D {
raw, _ := bson.Marshal(model)
var doc bson.D
bson.Unmarshal(raw, &doc)
return doc
}
func tModelsToBSON(models []*tModel) []bson.D {
docs := make([]bson.D, len(models))
for i := range models {
docs[i] = tModelToBSON(models[i])
}
return docs
}
func TestRepository_Find(t *testing.T) {
mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock))
ctx := context.Background()
mt.Run("returns data on success", func(mt *mtest.T) {
data := []*tModel{{Name: "test1"}, {Name: "test2"}}
repo := NewRepository[*tModel](mt.Coll, []mongo.IndexModel{})
mt.AddMockResponses(mtest.CreateCursorResponse(0, "test.foo", mtest.FirstBatch, tModelsToBSON(data)...))
result, err := repo.Find(ctx, bson.M{})
assert.Nil(mt, err)
assert.Equal(mt, data, result)
})
}
func TestRepository_FindOne(t *testing.T) {
mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock))
ctx := context.Background()
mt.Run("returns data on success", func(mt *mtest.T) {
data := &tModel{Name: "test"}
repo := NewRepository[*tModel](mt.Coll, []mongo.IndexModel{})
mt.AddMockResponses((mtest.CreateCursorResponse(0, "test.foo", mtest.FirstBatch, tModelToBSON(data))))
result, err := repo.FindOne(ctx, bson.M{"name": "test"})
assert.Nil(t, err)
assert.Equal(t, data, result)
})
mt.Run("returns error on failure", func(mt *mtest.T) {
repo := NewRepository[*tModel](mt.Coll, []mongo.IndexModel{})
mt.AddMockResponses((mtest.CreateCursorResponse(0, "test.foo", mtest.FirstBatch)))
_, err := repo.FindOne(ctx, bson.M{"name": "test"})
assert.NotNil(t, err)
})
}
func TestRepository_FindByID(t *testing.T) {
mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock))
ctx := context.Background()
mt.Run("returns data on success", func(mt *mtest.T) {
data := &tModel{Base: Base{ObjectID: primitive.NewObjectID()}, Name: "test"}
repo := NewRepository[*tModel](mt.Coll, []mongo.IndexModel{})
mt.AddMockResponses(mtest.CreateCursorResponse(0, "test.foo", mtest.FirstBatch, tModelToBSON(data)))
result, err := repo.FindByID(ctx, data.ID().Hex())
assert.Nil(t, err)
assert.Equal(t, data, result)
})
mt.Run("returns error on failure", func(mt *mtest.T) {
repo := NewRepository[*tModel](mt.Coll, []mongo.IndexModel{})
mt.AddMockResponses(mtest.CreateCursorResponse(0, "test.foo", mtest.FirstBatch))
_, err := repo.FindByID(ctx, "invalid")
assert.NotNil(t, err)
})
}
func TestRepository_Create(t *testing.T) {
mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock))
ctx := context.Background()
mt.Run("creates a new document successfully", func(mt *mtest.T) {
data := &tModel{Name: "test"}
repo := NewRepository[*tModel](mt.Coll, []mongo.IndexModel{})
mt.AddMockResponses(mtest.CreateSuccessResponse())
result, err := repo.Create(ctx, data)
assert.Nil(t, err)
assert.NotEmpty(t, result.ObjectID)
assert.WithinDuration(t, time.Now(), result.CreatedAt, time.Second)
assert.WithinDuration(t, time.Now(), result.UpdatedAt, time.Second)
})
mt.Run("returns error on failure", func(mt *mtest.T) {
data := &tModel{Name: "test"}
repo := NewRepository[*tModel](mt.Coll, []mongo.IndexModel{})
errResponse := mtest.CreateWriteErrorsResponse(mtest.WriteError{Index: 0, Code: 11000, Message: "duplicate key error"})
mt.AddMockResponses(errResponse)
_, err := repo.Create(ctx, data)
assert.NotNil(t, err)
})
}
func TestRepository_UpdateOne(t *testing.T) {
mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock))
ctx := context.Background()
mt.Run("updates a document successfully", func(mt *mtest.T) {
data := &tModel{Name: "test"}
repo := NewRepository[*tModel](mt.Coll, []mongo.IndexModel{})
successResponse := mtest.CreateSuccessResponse(
bson.E{Key: "n", Value: 1},
bson.E{Key: "nModified", Value: 1},
)
mt.AddMockResponses(successResponse)
result, err := repo.UpdateOne(ctx, bson.M{"name": "test"}, data)
assert.Nil(t, err)
assert.Equal(t, data, result)
assert.WithinDuration(t, time.Now(), result.UpdatedAt, time.Second)
})
mt.Run("returns error on failure", func(mt *mtest.T) {
data := &tModel{Name: "test"}
repo := NewRepository[*tModel](mt.Coll, []mongo.IndexModel{})
errResponse := mtest.CreateWriteErrorsResponse(mtest.WriteError{Index: 0, Code: 11000, Message: "duplicate key error"})
mt.AddMockResponses(errResponse)
_, err := repo.UpdateOne(ctx, bson.M{"name": "test"}, data)
assert.NotNil(t, err)
})
}
func TestRepository_DeleteOne(t *testing.T) {
mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock))
ctx := context.Background()
mt.Run("deletes a document successfully", func(mt *mtest.T) {
repo := NewRepository[*tModel](mt.Coll, []mongo.IndexModel{})
successResponse := mtest.CreateSuccessResponse(
bson.E{Key: "n", Value: 1},
)
mt.AddMockResponses(successResponse)
err := repo.DeleteOne(ctx, bson.M{"name": "test"})
assert.Nil(t, err)
})
mt.Run("returns error on failure", func(mt *mtest.T) {
repo := NewRepository[*tModel](mt.Coll, []mongo.IndexModel{})
errResponse := mtest.CreateWriteErrorsResponse(mtest.WriteError{Index: 0, Code: 11000, Message: "delete error"})
mt.AddMockResponses(errResponse)
err := repo.DeleteOne(ctx, bson.M{"name": "test"})
assert.NotNil(t, err)
})
}
func TestRepository_CountDocuments(t *testing.T) {
mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock))
ctx := context.Background()
mt.Run("returns count on success", func(mt *mtest.T) {
repo := NewRepository[*tModel](mt.Coll, []mongo.IndexModel{})
mt.AddMockResponses(mtest.CreateCursorResponse(1, "test.foo", mtest.FirstBatch, bson.D{bson.E{Key: "n", Value: 2}}))
count, err := repo.CountDocuments(ctx, bson.M{"name": "test"})
assert.Nil(t, err)
assert.Equal(t, 2, count)
})
}