diff --git a/internal/howmuch/adapter/controller/session.go b/internal/howmuch/adapter/controller/session.go index 5008166..08ddb5d 100644 --- a/internal/howmuch/adapter/controller/session.go +++ b/internal/howmuch/adapter/controller/session.go @@ -64,19 +64,14 @@ type createParams struct { // Since we use JWT method, this token is not stored anywhere. Thus it // stops at the controller level. func (sc *SessionController) Create(ctx *gin.Context) { - var params createParams + var user model.UserExistDTO - if err := ctx.Bind(¶ms); err != nil { + if err := ctx.Bind(&user); err != nil { log.ErrorLog("param error", "err", err) core.WriteResponse(ctx, UserParamsErr, nil) return } - user := model.User{ - Email: params.Email, - Password: params.Password, - } - err := sc.userUsecase.Exist(ctx, &user) if err != nil { core.WriteResponse(ctx, err, nil) diff --git a/internal/howmuch/adapter/controller/usecasemock/testuserusecase.go b/internal/howmuch/adapter/controller/usecasemock/testuserusecase.go index ffa0ddf..268bf89 100644 --- a/internal/howmuch/adapter/controller/usecasemock/testuserusecase.go +++ b/internal/howmuch/adapter/controller/usecasemock/testuserusecase.go @@ -35,11 +35,11 @@ func NewtestUserUsecase() usecase.User { return &testUserUsecase{} } -func (*testUserUsecase) Create(ctx context.Context, u *model.User) (*model.User, error) { +func (*testUserUsecase) Create(ctx context.Context, u *model.UserCreateDTO) (*model.User, error) { return nil, nil } -func (*testUserUsecase) Exist(ctx context.Context, u *model.User) error { +func (*testUserUsecase) Exist(ctx context.Context, u *model.UserExistDTO) error { switch u.Email { case "a@b.c": if u.Password == "strong password" { diff --git a/internal/howmuch/adapter/controller/user.go b/internal/howmuch/adapter/controller/user.go index eda8947..cab830e 100644 --- a/internal/howmuch/adapter/controller/user.go +++ b/internal/howmuch/adapter/controller/user.go @@ -57,26 +57,14 @@ func NewUserController(us usecase.User) User { } func (uc *UserController) Create(ctx core.Context) { - var params struct { - Email string `json:"email" binding:"required,email"` - FirstName string `json:"first_name" binding:"required"` - LastName string `json:"last_name" binding:"required"` - Password string `json:"password" binding:"required"` - } + var userDTO model.UserCreateDTO - if err := ctx.Bind(¶ms); err != nil { + if err := ctx.Bind(&userDTO); err != nil { core.WriteResponse(ctx, UserParamsErr, nil) return } - user := model.User{ - Email: params.Email, - FirstName: params.FirstName, - LastName: params.LastName, - Password: params.Password, - } - - _, err := uc.userUsecase.Create(ctx, &user) + _, err := uc.userUsecase.Create(ctx, &userDTO) if err != nil { core.WriteResponse(ctx, err, nil) return diff --git a/internal/howmuch/model/user.go b/internal/howmuch/model/user.go index ef28092..14f3387 100644 --- a/internal/howmuch/model/user.go +++ b/internal/howmuch/model/user.go @@ -24,6 +24,18 @@ package model import "time" +type UserCreateDTO struct { + Email string `json:"email" binding:"required,email"` + FirstName string `json:"first_name" binding:"required"` + LastName string `json:"last_name" binding:"required"` + Password string `json:"password" binding:"required"` +} + +type UserExistDTO struct { + Email string `json:"email" binding:"required,email"` + Password string `json:"password" binding:"required"` +} + // User model type User struct { ID int diff --git a/internal/howmuch/usecase/usecase/user.go b/internal/howmuch/usecase/usecase/user.go index 9144bcd..53fc549 100644 --- a/internal/howmuch/usecase/usecase/user.go +++ b/internal/howmuch/usecase/usecase/user.go @@ -60,8 +60,8 @@ type userUsecase struct { } type User interface { - Create(ctx context.Context, u *model.User) (*model.User, error) - Exist(ctx context.Context, u *model.User) error + Create(ctx context.Context, u *model.UserCreateDTO) (*model.User, error) + Exist(ctx context.Context, u *model.UserExistDTO) error } func NewUserUsecase(r repo.UserRepository, d repo.DBRepository) User { @@ -71,7 +71,7 @@ func NewUserUsecase(r repo.UserRepository, d repo.DBRepository) User { } } -func (uuc *userUsecase) Create(ctx context.Context, u *model.User) (*model.User, error) { +func (uuc *userUsecase) Create(ctx context.Context, u *model.UserCreateDTO) (*model.User, error) { // Hash the password encrypted, err := bcrypt.GenerateFromPassword([]byte(u.Password), 12) if err != nil { @@ -82,7 +82,12 @@ func (uuc *userUsecase) Create(ctx context.Context, u *model.User) (*model.User, data, err := uuc.dbRepo.Transaction( ctx, func(txCtx context.Context, tx interface{}) (interface{}, error) { - u, err := uuc.userRepo.Create(txCtx, tx, u) + created, err := uuc.userRepo.Create(txCtx, tx, &model.User{ + Email: u.Email, + Password: u.Password, + FirstName: u.FirstName, + LastName: u.LastName, + }) if err != nil { match, _ := regexp.MatchString("SQLSTATE 23505", err.Error()) if match { @@ -100,7 +105,7 @@ func (uuc *userUsecase) Create(ctx context.Context, u *model.User) (*model.User, fmt.Sprintf("%s %s", u.FirstName, u.LastName), ) - return u, err + return created, err }, ) if err != nil { @@ -113,7 +118,7 @@ func (uuc *userUsecase) Create(ctx context.Context, u *model.User) (*model.User, return user, nil } -func (uuc *userUsecase) Exist(ctx context.Context, u *model.User) error { +func (uuc *userUsecase) Exist(ctx context.Context, u *model.UserExistDTO) error { got, err := uuc.userRepo.GetByEmail(ctx, u.Email) // Any query error? if err != nil { diff --git a/internal/howmuch/usecase/usecase/user_test.go b/internal/howmuch/usecase/usecase/user_test.go index 1ae3d5d..b0a22ad 100644 --- a/internal/howmuch/usecase/usecase/user_test.go +++ b/internal/howmuch/usecase/usecase/user_test.go @@ -29,30 +29,42 @@ import ( "git.vinchent.xyz/vinchent/howmuch/internal/howmuch/model" "git.vinchent.xyz/vinchent/howmuch/internal/howmuch/usecase/usecase/repomock" "github.com/stretchr/testify/assert" + "golang.org/x/crypto/bcrypt" ) func TestCreateUser(t *testing.T) { t.Run("normal create", func(t *testing.T) { ctx := context.Background() userUsecase := NewUserUsecase(&repomock.TestUserRepository{}, &repomock.TestDBRepository{}) - input := &model.User{ + input := &model.UserCreateDTO{ Email: "a@b.c", FirstName: "James", LastName: "Bond", Password: "verystrong", } - want := input - want.ID = 123 + want := &model.User{ + ID: 123, + Email: input.Email, + FirstName: input.FirstName, + LastName: input.LastName, + // Password is hashed + Password: "verystrong", + } got, err := userUsecase.Create(ctx, input) assert.NoError(t, err) - assert.Equal(t, want, got) + assert.Equal(t, want.ID, got.ID) + + assert.NoError( + t, + bcrypt.CompareHashAndPassword([]byte(got.Password), []byte(want.Password)), + ) }) t.Run("duplicate create", func(t *testing.T) { ctx := context.Background() userUsecase := NewUserUsecase(&repomock.TestUserRepository{}, &repomock.TestDBRepository{}) - input := &model.User{ + input := &model.UserCreateDTO{ Email: "duplicate@error.com", FirstName: "James", LastName: "Bond", @@ -67,22 +79,22 @@ func TestCreateUser(t *testing.T) { func TestUserExist(t *testing.T) { testCases := []struct { Name string - User *model.User + User *model.UserExistDTO ExpErr error }{ - {"user exists", &model.User{ + {"user exists", &model.UserExistDTO{ Email: "a@b.c", Password: "strongHashed", }, nil}, - {"query error", &model.User{ + {"query error", &model.UserExistDTO{ Email: "query@error.com", Password: "strongHashed", }, repomock.UserTestDummyErr}, - {"user doesn not exist", &model.User{ + {"user doesn not exist", &model.UserExistDTO{ Email: "inexist@error.com", Password: "strongHashed", }, UserNotExist}, - {"wrong password", &model.User{ + {"wrong password", &model.UserExistDTO{ Email: "a@b.c", Password: "wrongHashed", }, UserWrongPassword},