diff --git a/repository/role/role_repository.go b/repository/role/role_repository.go new file mode 100644 index 0000000..485f505 --- /dev/null +++ b/repository/role/role_repository.go @@ -0,0 +1,136 @@ +package repository + +import ( + "context" + "sync" + + "github.com/Lukmanern/gost/database/connector" + "github.com/Lukmanern/gost/domain/entity" + "github.com/Lukmanern/gost/domain/model" + "gorm.io/gorm" +) + +type RoleRepository interface { + // Create adds a new role to the repository. + Create(ctx context.Context, role entity.Role) (id int, err error) + + // GetByID retrieves a role by its unique identifier. + GetByID(ctx context.Context, id int) (role *entity.Role, err error) + + // GetByName retrieves a role by its name. + GetByName(ctx context.Context, name string) (role *entity.Role, err error) + + // GetAll retrieves all roles based on a filter for pagination. + GetAll(ctx context.Context, filter model.RequestGetAll) (roles []entity.Role, total int, err error) + + // Update modifies role information in the repository. + Update(ctx context.Context, role entity.Role) (err error) + + // Delete removes a role from the repository by its ID. + Delete(ctx context.Context, id int) (err error) +} + +type RoleRepositoryImpl struct { + db *gorm.DB +} + +var ( + roleRepositoryImpl *RoleRepositoryImpl + roleRepositoryImplOnce sync.Once +) + +func NewRoleRepository() RoleRepository { + roleRepositoryImplOnce.Do(func() { + roleRepositoryImpl = &RoleRepositoryImpl{ + db: connector.LoadDatabase(), + } + }) + return roleRepositoryImpl +} + +func (repo *RoleRepositoryImpl) Create(ctx context.Context, role entity.Role) (id int, err error) { + err = repo.db.Transaction(func(tx *gorm.DB) error { + res := tx.Create(&role) + if res.Error != nil { + tx.Rollback() + return res.Error + } + id = role.ID + return nil + }) + if err != nil { + return 0, err + } + + return id, nil +} + +func (repo *RoleRepositoryImpl) GetByID(ctx context.Context, id int) (role *entity.Role, err error) { + role = &entity.Role{} + result := repo.db.Where("id = ?", id).First(&role) + if result.Error != nil { + return nil, result.Error + } + return role, nil +} + +func (repo *RoleRepositoryImpl) GetByName(ctx context.Context, name string) (role *entity.Role, err error) { + role = &entity.Role{} + result := repo.db.Where("name = ?", name).First(&role) + if result.Error != nil { + return nil, result.Error + } + return role, nil +} + +func (repo *RoleRepositoryImpl) GetAll(ctx context.Context, filter model.RequestGetAll) (roles []entity.Role, total int, err error) { + var count int64 + args := []interface{}{"%" + filter.Keyword + "%"} + cond := "name LIKE ?" + result := repo.db.Where(cond, args...).Find(&roles) + count = result.RowsAffected + if result.Error != nil { + return nil, 0, result.Error + } + roles = []entity.Role{} + skip := int64(filter.Limit * (filter.Page - 1)) + limit := int64(filter.Limit) + result = repo.db.Where(cond, args...).Limit(int(limit)).Offset(int(skip)).Find(&roles) + if result.Error != nil { + return nil, 0, result.Error + } + total = int(count) + return roles, total, nil +} + +func (repo *RoleRepositoryImpl) Update(ctx context.Context, role entity.Role) (err error) { + err = repo.db.Transaction(func(tx *gorm.DB) error { + var oldData entity.Role + result := tx.Where("id = ?", role.ID).First(&oldData) + if result.Error != nil { + tx.Rollback() + return result.Error + } + + oldData.Name = role.Name + oldData.Description = role.Description + oldData.UpdatedAt = role.UpdatedAt + result = tx.Save(&oldData) + if result.Error != nil { + tx.Rollback() + return result.Error + } + return nil + }) + + return err +} + +func (repo *RoleRepositoryImpl) Delete(ctx context.Context, id int) (err error) { + deleted := entity.Role{} + result := repo.db.Where("id = ?", id).Delete(&deleted) + if result.Error != nil { + return result.Error + } + return nil +} diff --git a/repository/role/role_repository_test.go b/repository/role/role_repository_test.go new file mode 100644 index 0000000..20cc90f --- /dev/null +++ b/repository/role/role_repository_test.go @@ -0,0 +1,392 @@ +package repository + +import ( + "context" + "strconv" + "testing" + "time" + + "github.com/Lukmanern/gost/database/connector" + "github.com/Lukmanern/gost/domain/entity" + "github.com/Lukmanern/gost/domain/model" + "github.com/Lukmanern/gost/internal/env" +) + +var ( + roleRepoImpl RoleRepositoryImpl + timeNow time.Time + ctx context.Context +) + +func init() { + filePath := "./../../.env" + env.ReadConfig(filePath) + + timeNow = time.Now() + ctx = context.Background() + + roleRepoImpl = RoleRepositoryImpl{ + db: connector.LoadDatabase(), + } +} + +func createOneRole(t *testing.T, namePrefix string) *entity.Role { + role := entity.Role{ + Name: "valid-role-name-" + namePrefix, + Description: "valid-role-description-" + namePrefix, + TimeFields: entity.TimeFields{ + CreatedAt: &timeNow, + UpdatedAt: &timeNow, + }, + } + id, createErr := roleRepoImpl.Create(ctx, role) + if createErr != nil { + t.Error("error while creating role : ", createErr.Error()) + } + role.ID = id + return &role +} + +func TestNewRoleRepository(t *testing.T) { + roleRepo := NewRoleRepository() + if roleRepo == nil { + t.Error("should not nil") + } +} + +func TestCreate(t *testing.T) { + role := createOneRole(t, "create-same-name") + if role == nil { + t.Error("failed creating role : role is nil") + } + defer func() { + roleRepoImpl.Delete(ctx, role.ID) + }() + + type args struct { + ctx context.Context + role entity.Role + } + tests := []struct { + name string + repo RoleRepositoryImpl + args args + wantErr bool + wantPanic bool + }{ + { + name: "error while creating with the same name", + wantErr: true, + args: args{ + ctx: ctx, + role: entity.Role{ + Name: role.Name, + Description: "", + TimeFields: entity.TimeFields{ + CreatedAt: &timeNow, + UpdatedAt: &timeNow, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer func() { + if r := recover(); r == nil && tt.wantPanic { + t.Errorf("create() do not panic") + } + }() + gotID, err := tt.repo.Create(tt.args.ctx, tt.args.role) + if (err != nil) != tt.wantErr { + t.Errorf("RoleRepositoryImpl.Create() error = %v, wantErr %v", err, tt.wantErr) + return + } + if gotID <= 0 { + t.Errorf("ID should be positive") + } + }) + } +} + +func TestGetByID(t *testing.T) { + role := createOneRole(t, "TestGetByID") + if role == nil { + t.Error("failed creating role : role is nil") + } + defer func() { + roleRepoImpl.Delete(ctx, role.ID) + }() + + type args struct { + ctx context.Context + id int + } + tests := []struct { + name string + repo RoleRepositoryImpl + args args + wantErr bool + }{ + { + name: "success get role", + repo: roleRepoImpl, + args: args{ + ctx: ctx, + id: role.ID, + }, + wantErr: false, + }, + { + name: "failed get role: invalid id", + repo: roleRepoImpl, + args: args{ + ctx: ctx, + id: -10, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotRole, err := tt.repo.GetByID(tt.args.ctx, tt.args.id) + if (err != nil) != tt.wantErr { + t.Errorf("RoleRepositoryImpl.GetByID() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !tt.wantErr && gotRole == nil { + t.Error("role should not nil") + } + }) + } +} + +func TestGetByName(t *testing.T) { + role := createOneRole(t, "TestGetByName") + if role == nil { + t.Error("failed creating role : role is nil") + } + defer func() { + roleRepoImpl.Delete(ctx, role.ID) + }() + + type args struct { + ctx context.Context + name string + } + tests := []struct { + name string + repo RoleRepositoryImpl + args args + wantErr bool + }{ + { + name: "success get role by valid id", + repo: roleRepoImpl, + args: args{ + ctx: ctx, + name: role.Name, + }, + wantErr: false, + }, + { + name: "failed get role by invalid id", + repo: roleRepoImpl, + args: args{ + ctx: ctx, + name: "unknown name", + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotRole, err := tt.repo.GetByName(tt.args.ctx, tt.args.name) + if (err != nil) != tt.wantErr { + t.Errorf("RoleRepositoryImpl.GetByName() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !tt.wantErr && gotRole == nil { + t.Error("role should not nil") + } + }) + } +} + +func TestGetAll(t *testing.T) { + roles := make([]entity.Role, 0) + for i := 0; i < 10; i++ { + role := createOneRole(t, "TestGetAll"+strconv.Itoa(i)) + if role == nil { + continue + } + defer func() { + roleRepoImpl.Delete(ctx, role.ID) + }() + + roles = append(roles, *role) + } + lenRoles := len(roles) + type args struct { + ctx context.Context + filter model.RequestGetAll + } + tests := []struct { + name string + repo RoleRepositoryImpl + args args + wantErr bool + }{ + { + name: "success get all", + repo: roleRepoImpl, + args: args{ + ctx: ctx, + filter: model.RequestGetAll{ + Limit: 1000, + Page: 1, + }, + }, + wantErr: false, + }, + { + name: "success get all", + repo: roleRepoImpl, + args: args{ + ctx: ctx, + filter: model.RequestGetAll{ + Limit: 1, + Page: 1, + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotRoles, gotTotal, err := tt.repo.GetAll(tt.args.ctx, tt.args.filter) + if (err != nil) != tt.wantErr { + t.Errorf("RoleRepositoryImpl.GetAll() error = %v, wantErr %v", err, tt.wantErr) + return + } + if tt.args.filter.Limit > lenRoles && len(gotRoles) < lenRoles { + t.Error("role should be $lenRoles or more") + } + if tt.args.filter.Limit > lenRoles && gotTotal < lenRoles { + t.Error("total role should be $lenRoles or more") + } + if tt.args.filter.Limit < lenRoles && len(gotRoles) > lenRoles { + t.Error("role should be less than $lenRoles") + } + }) + } +} + +func TestUpdate(t *testing.T) { + role := createOneRole(t, "TestUpdateByID") + if role == nil { + t.Error("failed creating role : role is nil") + } + defer func() { + roleRepoImpl.Delete(ctx, role.ID) + }() + + type args struct { + ctx context.Context + role entity.Role + } + tests := []struct { + name string + repo RoleRepositoryImpl + args args + wantErr bool + }{ + { + name: "success update name and desc", + repo: roleRepoImpl, + wantErr: false, + args: args{ + ctx: ctx, + role: entity.Role{ + ID: role.ID, + Name: "updated name", + Description: "updated description", + }, + }, + }, + { + name: "failed update name and desc with invalid id", + repo: roleRepoImpl, + wantErr: true, + args: args{ + ctx: ctx, + role: entity.Role{ + ID: -10, + Name: "updated name", + Description: "updated description", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.repo.Update(tt.args.ctx, tt.args.role); (err != nil) != tt.wantErr { + t.Errorf("RoleRepositoryImpl.Update() error = %v, wantErr %v", err, tt.wantErr) + } + + p, err := tt.repo.GetByID(tt.args.ctx, role.ID) + if err != nil { + t.Error("error while getting role") + } + if p.Name != tt.args.role.Name || p.Description != tt.args.role.Description { + t.Error("name and description failed to update") + } + }) + } +} + +func TestDelete(t *testing.T) { + role := createOneRole(t, "TestDeleteByID") + if role == nil { + t.Error("failed creating role : role is nil") + } + defer func() { + roleRepoImpl.Delete(ctx, role.ID) + }() + + type args struct { + ctx context.Context + id int + } + tests := []struct { + name string + repo RoleRepositoryImpl + args args + wantErr bool + }{ + { + name: "success update role", + repo: roleRepoImpl, + wantErr: false, + args: args{ + ctx: ctx, + id: role.ID, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.repo.Delete(tt.args.ctx, tt.args.id); (err != nil) != tt.wantErr { + t.Errorf("RoleRepositoryImpl.Delete() error = %v, wantErr %v", err, tt.wantErr) + } + + role, err := tt.repo.GetByID(tt.args.ctx, tt.args.id) + if !tt.wantErr && err == nil { + t.Error("should error") + } + if !tt.wantErr && role != nil { + t.Error("role should nil") + } + }) + } +} diff --git a/repository/user/user_repository.go b/repository/user/user_repository.go new file mode 100644 index 0000000..bdfa812 --- /dev/null +++ b/repository/user/user_repository.go @@ -0,0 +1,182 @@ +// used by user auth service + +package repository + +import ( + "context" + "sync" + + "gorm.io/gorm" + + "github.com/Lukmanern/gost/database/connector" + "github.com/Lukmanern/gost/domain/entity" + "github.com/Lukmanern/gost/domain/model" +) + +type UserRepository interface { + // Create adds a new user to the repository with a specified role. + Create(ctx context.Context, user entity.User, roleID int) (id int, err error) + + // GetByID retrieves a user by their unique identifier. + GetByID(ctx context.Context, id int) (user *entity.User, err error) + + // GetByEmail retrieves a user by their email address. + GetByEmail(ctx context.Context, email string) (user *entity.User, err error) + + // GetByConditions retrieves a user based on specified conditions. + GetByConditions(ctx context.Context, conds map[string]any) (user *entity.User, err error) + + // GetAll retrieves all users based on a filter for pagination. + GetAll(ctx context.Context, filter model.RequestGetAll) (users []entity.User, total int, err error) + + // Update modifies user information in the repository. + Update(ctx context.Context, user entity.User) (err error) + + // Delete removes a user from the repository by their ID. + Delete(ctx context.Context, id int) (err error) + + // UpdatePassword updates a user's password in the repository. + UpdatePassword(ctx context.Context, id int, passwordHashed string) (err error) +} + +type UserRepositoryImpl struct { + db *gorm.DB +} + +var ( + userRepositoryImpl *UserRepositoryImpl + userRepositoryImplOnce sync.Once +) + +func NewUserRepository() UserRepository { + userRepositoryImplOnce.Do(func() { + userRepositoryImpl = &UserRepositoryImpl{ + db: connector.LoadDatabase(), + } + }) + return userRepositoryImpl +} + +func (repo *UserRepositoryImpl) Create(ctx context.Context, user entity.User, roleID int) (id int, err error) { + err = repo.db.Transaction(func(tx *gorm.DB) error { + if res := tx.Create(&user); res.Error != nil { + tx.Rollback() + return res.Error + } + id = user.ID + + if res := tx.Create(&entity.UserHasRoles{ + UserID: id, + RoleID: roleID, + }); res.Error != nil { + tx.Rollback() + return res.Error + } + return nil + }) + if err != nil { + return 0, err + } + return id, nil +} + +func (repo *UserRepositoryImpl) GetByID(ctx context.Context, id int) (user *entity.User, err error) { + user = &entity.User{} + result := repo.db.Where("id = ?", id).Preload("Roles").First(&user) + if result.Error != nil { + return nil, result.Error + } + return user, nil +} + +func (repo *UserRepositoryImpl) GetByEmail(ctx context.Context, email string) (user *entity.User, err error) { + user = &entity.User{} + result := repo.db.Where("email = ?", email).Preload("Roles").First(&user) + if result.Error != nil { + return nil, result.Error + } + return user, nil +} + +func (repo *UserRepositoryImpl) GetByConditions(ctx context.Context, conds map[string]any) (user *entity.User, err error) { + // this func is easy-contain-vunarable by default + user = &entity.User{} + query := repo.db + for con, val := range conds { + query = query.Where(con+" ?", val) + } + result := query.Preload("Roles").First(&user) + if result.Error != nil { + return nil, result.Error + } + return user, nil +} + +func (repo *UserRepositoryImpl) GetAll(ctx context.Context, filter model.RequestGetAll) (users []entity.User, total int, err error) { + var count int64 + args := []interface{}{"%" + filter.Keyword + "%"} + cond := "name LIKE ?" + result := repo.db.Where(cond, args...).Find(&users) + count = result.RowsAffected + if result.Error != nil { + return nil, 0, result.Error + } + users = []entity.User{} + skip := int64(filter.Limit * (filter.Page - 1)) + limit := int64(filter.Limit) + result = repo.db.Where(cond, args...).Limit(int(limit)).Offset(int(skip)).Find(&users) + if result.Error != nil { + return nil, 0, result.Error + } + total = int(count) + return users, total, nil +} + +func (repo *UserRepositoryImpl) Update(ctx context.Context, user entity.User) (err error) { + err = repo.db.Transaction(func(tx *gorm.DB) error { + var oldData entity.User + result := tx.Where("id = ?", user.ID).First(&oldData) + if result.Error != nil { + return result.Error + } + + oldData.Name = user.Name + oldData.ActivatedAt = user.ActivatedAt + oldData.UpdatedAt = user.UpdatedAt + result = tx.Save(&oldData) + if result.Error != nil { + return result.Error + } + return nil + }) + + return err +} + +func (repo *UserRepositoryImpl) Delete(ctx context.Context, id int) (err error) { + deleted := entity.User{} + result := repo.db.Where("id = ?", id).Delete(&deleted) + if result.Error != nil { + return result.Error + } + return nil +} + +func (repo *UserRepositoryImpl) UpdatePassword(ctx context.Context, id int, passwordHashed string) (err error) { + err = repo.db.Transaction(func(tx *gorm.DB) error { + var user entity.User + result := tx.Where("id = ?", id).First(&user) + if result.Error != nil { + return result.Error + } + user.Password = passwordHashed + user.SetUpdateTime() + result = tx.Save(&user) + if result.Error != nil { + return result.Error + } + return nil + }) + + return err +} diff --git a/repository/user/user_repository_test.go b/repository/user/user_repository_test.go new file mode 100644 index 0000000..730e7b6 --- /dev/null +++ b/repository/user/user_repository_test.go @@ -0,0 +1,550 @@ +package repository + +import ( + "context" + "testing" + "time" + + "github.com/Lukmanern/gost/database/connector" + "github.com/Lukmanern/gost/domain/entity" + "github.com/Lukmanern/gost/domain/model" + "github.com/Lukmanern/gost/internal/env" + "github.com/Lukmanern/gost/internal/helper" +) + +var ( + timeNow time.Time + ctx context.Context +) + +func init() { + filePath := "./../../.env" + env.ReadConfig(filePath) + timeNow = time.Now() + ctx = context.Background() +} + +func TestNewUserRepository(t *testing.T) { + userRepository := NewUserRepository() + if userRepository == nil { + t.Error("should not nil") + } +} + +func TestUserRepositoryImplCreate(t *testing.T) { + userRepositoryImpl := UserRepositoryImpl{ + db: connector.LoadDatabase(), + } + + type args struct { + ctx context.Context + user entity.User + } + tests := []struct { + name string + repo UserRepositoryImpl + wantErr bool + wantPanic bool + args args + }{ + { + name: "success create new user", + repo: userRepositoryImpl, + wantErr: false, + wantPanic: false, + args: args{ + ctx: context.Background(), + user: entity.User{ + Name: "validname", + Email: "valid1@email.com", + Password: "example-password", + TimeFields: entity.TimeFields{ + CreatedAt: &timeNow, + UpdatedAt: &timeNow, + }, + }, + }, + }, + { + name: "success create new user with void data", + repo: userRepositoryImpl, + wantErr: false, + wantPanic: false, + }, + { + name: "failed create new user with void data and nil repository", + repo: UserRepositoryImpl{ + db: nil, + }, + wantErr: true, + wantPanic: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if !tt.wantPanic { + gotID, err := tt.repo.Create(tt.args.ctx, tt.args.user, 1) + if (err != nil) != tt.wantErr { + t.Errorf("UserRepositoryImpl.Create() error = %v, wantErr %v", err, tt.wantErr) + return + } + gotID2, err2 := tt.repo.Create(tt.args.ctx, tt.args.user, 1) + if err2 == nil || gotID2 != 0 { + t.Error("should be error, couse email is already used") + } + tt.repo.Delete(tt.args.ctx, gotID) + + return + } + // want panic + defer func() { + if r := recover(); r == nil { + t.Errorf("create() do not panic") + } + }() + userID, err := tt.repo.Create(tt.args.ctx, tt.args.user, 1) + if (err != nil) != tt.wantErr { + t.Errorf("UserRepositoryImpl.Create() error = %v, wantErr %v", err, tt.wantErr) + return + } + userRepositoryImpl.Delete(ctx, userID) + }) + } +} + +func TestUserRepositoryImplGetByID(t *testing.T) { + // create user + user := entity.User{ + Name: "validname", + Email: "valid2@email.com", + Password: "example-password", + TimeFields: entity.TimeFields{ + CreatedAt: &timeNow, + UpdatedAt: &timeNow, + }, + } + userRepositoryImpl := UserRepositoryImpl{ + db: connector.LoadDatabase(), + } + id, createErr := userRepositoryImpl.Create(ctx, user, 1) + if createErr != nil { + t.Errorf("error while creating user") + } + defer func() { + userRepositoryImpl.Delete(ctx, id) + }() + + type args struct { + ctx context.Context + id int + } + tests := []struct { + name string + repo UserRepositoryImpl + wantErr bool + wantUser bool + args args + }{ + { + name: "Success get user by id", + repo: userRepositoryImpl, + wantErr: false, + wantUser: true, + args: args{ + ctx: ctx, + id: id, + }, + }, + { + name: "Failed get user by negative id", + repo: userRepositoryImpl, + wantErr: true, + wantUser: false, + args: args{ + ctx: ctx, + id: -10, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotUser, err := tt.repo.GetByID(tt.args.ctx, tt.args.id) + if (err != nil) != tt.wantErr { + t.Errorf("UserRepositoryImpl.GetByID() error = %v, wantErr %v", err, tt.wantErr) + return + } + if tt.wantUser { + if gotUser == nil { + t.Error("error user shouldn't nil") + } + } + }) + } +} + +func TestUserRepositoryImplGetByEmail(t *testing.T) { + // create user + user := entity.User{ + Name: "validname", + Email: "valid3@email.com", + Password: "example-password", + TimeFields: entity.TimeFields{ + CreatedAt: &timeNow, + UpdatedAt: &timeNow, + }, + } + userRepositoryImpl := UserRepositoryImpl{ + db: connector.LoadDatabase(), + } + id, createErr := userRepositoryImpl.Create(ctx, user, 1) + if createErr != nil { + t.Errorf("error while creating user") + } + defer func() { + userRepositoryImpl.Delete(ctx, id) + }() + + type args struct { + ctx context.Context + email string + } + tests := []struct { + name string + repo UserRepositoryImpl + wantUser bool + wantErr bool + args args + }{ + { + name: "Success get user by valid email", + repo: userRepositoryImpl, + wantErr: false, + wantUser: true, + args: args{ + ctx: ctx, + email: user.Email, + }, + }, + { + name: "Failed get user by invalid-email", + repo: userRepositoryImpl, + wantErr: true, + wantUser: false, + args: args{ + ctx: ctx, + email: "invalid-email", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotUser, err := tt.repo.GetByEmail(tt.args.ctx, tt.args.email) + if (err != nil) != tt.wantErr { + t.Errorf("UserRepositoryImpl.GetByID() error = %v, wantErr %v", err, tt.wantErr) + return + } + if tt.wantUser { + if gotUser == nil { + t.Error("error user shouldn't nil") + } + } + }) + } +} + +func TestUserRepositoryImplGetAll(t *testing.T) { + // create user + allUsersID := make([]int, 0) + userRepositoryImpl := UserRepositoryImpl{ + db: connector.LoadDatabase(), + } + for _, id := range []string{"4", "5", "6", "7", "8"} { + user := entity.User{ + Name: "validname", + Email: "valid" + id + "@email.com", // email is unique + Password: "example-password", + TimeFields: entity.TimeFields{ + CreatedAt: &timeNow, + UpdatedAt: &timeNow, + }, + } + newUserID, createErr := userRepositoryImpl.Create(ctx, user, 1) + if createErr != nil { + t.Errorf("error while creating user :" + id) + } + allUsersID = append(allUsersID, newUserID) + } + defer func() { + for _, userID := range allUsersID { + userRepositoryImpl.Delete(ctx, userID) + } + }() + + type args struct { + ctx context.Context + filter model.RequestGetAll + } + tests := []struct { + name string + repo UserRepositoryImpl + wantErr bool + args args + }{ + { + name: "success get 5 or more users", + repo: userRepositoryImpl, + wantErr: false, + args: args{ + ctx: ctx, + filter: model.RequestGetAll{ + Page: 1, + Limit: 1000, + Keyword: "", + }, + }, + }, + { + name: "success get less than 5", + repo: userRepositoryImpl, + wantErr: false, + args: args{ + ctx: ctx, + filter: model.RequestGetAll{ + Page: 1, + Limit: 1, + Keyword: "", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotUsers, gotTotal, err := tt.repo.GetAll(tt.args.ctx, tt.args.filter) + if (err != nil) != tt.wantErr { + t.Errorf("UserRepositoryImpl.GetAll() error = %v, wantErr %v", err, tt.wantErr) + return + } + if tt.args.filter.Limit > 5 && len(gotUsers) < 5 { + t.Error("users should be 5 or more") + } + if tt.args.filter.Limit > 5 && gotTotal < 5 { + t.Error("total users should be 5 or more") + } + if tt.args.filter.Limit < 5 && len(gotUsers) > 5 { + t.Error("users should be less than 5") + } + }) + } +} + +func TestUserRepositoryImplUpdate(t *testing.T) { + // create user + user := entity.User{ + Name: "validname", + Email: "valid9@email.com", + Password: "example-password", + TimeFields: entity.TimeFields{ + CreatedAt: &timeNow, + UpdatedAt: &timeNow, + }, + } + userRepositoryImpl := UserRepositoryImpl{ + db: connector.LoadDatabase(), + } + id, createErr := userRepositoryImpl.Create(ctx, user, 1) + if createErr != nil { + t.Errorf("error while creating user") + } + // add id to user + user.ID = id + defer func() { + userRepositoryImpl.Delete(ctx, id) + }() + + type args struct { + ctx context.Context + user entity.User + } + tests := []struct { + name string + repo UserRepositoryImpl + wantErr bool + newUserName string + args args + }{ + { + name: "success update user's name", + repo: userRepositoryImpl, + wantErr: false, + newUserName: "test-update-001", + args: args{ + ctx: ctx, + user: user, + }, + }, + } + for _, tt := range tests { + tt.args.user.Name = tt.newUserName + t.Run(tt.name, func(t *testing.T) { + if err := tt.repo.Update(tt.args.ctx, tt.args.user); (err != nil) != tt.wantErr { + t.Errorf("UserRepositoryImpl.Update() error = %v, wantErr %v", err, tt.wantErr) + } + getUser, getErr := tt.repo.GetByID(tt.args.ctx, id) + if getErr != nil { + t.Error("error while getting user") + } + if getUser.Name != tt.newUserName { + t.Error("update name failed") + } + }) + } +} + +func TestUserRepositoryImplDelete(t *testing.T) { + userRepository := NewUserRepository() + if userRepository == nil { + t.Error("shouldn't nil") + } + + ctx := context.Background() + err := userRepository.Delete(ctx, -2) + if err != nil { + t.Error("delete shouldn't error") + } + if ctx.Err() != nil { + t.Error("delete shouldn't error") + } +} + +func TestUserRepositoryImplUpdatePassword(t *testing.T) { + // create user + user := entity.User{ + Name: "validname", + Email: helper.RandomEmail(), + Password: "example-password", + TimeFields: entity.TimeFields{ + CreatedAt: &timeNow, + UpdatedAt: &timeNow, + }, + } + userRepositoryImpl := UserRepositoryImpl{ + db: connector.LoadDatabase(), + } + id, createErr := userRepositoryImpl.Create(ctx, user, 1) + if createErr != nil { + t.Errorf("error while creating user") + } + // add id to user + user.ID = id + defer func() { + userRepositoryImpl.Delete(ctx, id) + }() + + type args struct { + ctx context.Context + id int + passwordHashed string + } + tests := []struct { + name string + repo UserRepositoryImpl + args args + wantErr bool + }{ + { + name: "success update user's password", + repo: userRepositoryImpl, + wantErr: false, + args: args{ + ctx: ctx, + id: id, + passwordHashed: "new-password-hashed", + }, + }, + { + name: "failed getting user with negative id", + repo: userRepositoryImpl, + wantErr: true, + args: args{ + ctx: ctx, + id: -100, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.repo.UpdatePassword(tt.args.ctx, tt.args.id, tt.args.passwordHashed); (err != nil) != tt.wantErr { + t.Errorf("UserRepositoryImpl.UpdatePassword() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !tt.wantErr { + getUser, getErr := tt.repo.GetByID(tt.args.ctx, id) + if getErr != nil { + t.Error("error while getting user") + } + if getUser.Password != tt.args.passwordHashed { + t.Error("failed to update user's password") + } + } + }) + } +} + +func TestUserRepositoryImplGetByConditions(t *testing.T) { + user := entity.User{ + Name: "validname", + Email: helper.RandomEmail(), + Password: "example-password", + TimeFields: entity.TimeFields{ + CreatedAt: &timeNow, + UpdatedAt: &timeNow, + }, + } + userRepositoryImpl := UserRepositoryImpl{ + db: connector.LoadDatabase(), + } + id, createErr := userRepositoryImpl.Create(ctx, user, 1) + if createErr != nil { + t.Errorf("error while creating user") + } + // add id to user + user.ID = id + defer func() { + userRepositoryImpl.Delete(ctx, id) + }() + + type args struct { + ctx context.Context + conds map[string]any + } + tests := []struct { + name string + repo UserRepositoryImpl + args args + wantErr bool + }{ + { + name: "success get data", + repo: userRepositoryImpl, + args: args{ + ctx: ctx, + conds: map[string]any{ + "name =": user.Name, + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotUser, err := tt.repo.GetByConditions(tt.args.ctx, tt.args.conds) + if (err != nil) != tt.wantErr { + t.Errorf("UserRepositoryImpl.GetByConditions() error = %v, wantErr %v", err, tt.wantErr) + return + } + if gotUser.ID != user.ID || gotUser.Email != user.Email || gotUser.Password != user.Password { + t.Error("should got same ID/ Email/ Password") + } + }) + } +}