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) }) }