Skip to content

Commit

Permalink
Merge pull request #4 from isd-sgcu/feature/user-service
Browse files Browse the repository at this point in the history
Feature/user service
  • Loading branch information
bookpanda authored Dec 31, 2023
2 parents fd556fa + 23c69ce commit 606d179
Show file tree
Hide file tree
Showing 11 changed files with 304 additions and 21 deletions.
11 changes: 10 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,13 @@ server:

mock-gen:
mockgen -source ./src/pkg/repository/cache/cache.repository.go -destination ./src/mocks/repository/cache/cache.mock.go
mockgen -source ./src/pkg/repository/auth/auth.repository.go -destination ./src/mocks/repository/auth/auth.mock.go
mockgen -source ./src/pkg/repository/auth/auth.repository.go -destination ./src/mocks/repository/auth/auth.mock.go

test:
go vet ./...
go test -v -coverpkg ./src/internal/... -coverprofile coverage.out -covermode count ./src/internal/...
go tool cover -func=coverage.out
go tool cover -html=coverage.out -o coverage.html

proto:
go get github.com/isd-sgcu/johnjud-go-proto@latest
4 changes: 2 additions & 2 deletions config/config.example.yaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
app:
port: 8081
port: 3002
debug: true
secret: <secret>

database:
host: localhost
port: 5432
name: johnjud-auth-db
name: johnjud_db
username: root
password: root

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ require (
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/isd-sgcu/johnjud-go-proto v0.1.2 // indirect
github.com/isd-sgcu/johnjud-go-proto v0.1.5 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgx/v5 v5.4.3 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ github.com/isd-sgcu/johnjud-go-proto v0.0.8 h1:nIQBZgK2OFVrLjVtpeDgwows8poA7LhsI
github.com/isd-sgcu/johnjud-go-proto v0.0.8/go.mod h1:HP0w9gC30b5WNnqeFBM9JJZud+pvyikz0+pGFSI/Wjw=
github.com/isd-sgcu/johnjud-go-proto v0.1.2 h1:gp1MGHCnJdeuUEI4yCbvpQJ1BfLzR9Tr9Q1kKSdssME=
github.com/isd-sgcu/johnjud-go-proto v0.1.2/go.mod h1:1OK6aiCgtXQiLhxp0r6iLEejYIRpckWQZDrCZ9Trbo4=
github.com/isd-sgcu/johnjud-go-proto v0.1.5 h1:ZhMb73xXOSM2OlsfQ8wv6rmbfSIrXwbBumIx/vtdgHc=
github.com/isd-sgcu/johnjud-go-proto v0.1.5/go.mod h1:1OK6aiCgtXQiLhxp0r6iLEejYIRpckWQZDrCZ9Trbo4=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
Expand Down
3 changes: 2 additions & 1 deletion src/internal/service/auth/auth.service.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package auth

import (
"context"

"github.com/google/uuid"
"github.com/isd-sgcu/johnjud-auth/src/internal/constant"
"github.com/isd-sgcu/johnjud-auth/src/internal/domain/model"
Expand Down Expand Up @@ -45,7 +46,7 @@ func (s *serviceImpl) RefreshToken(_ context.Context, request *authProto.Refresh
return nil, nil
}

func (s *serviceImpl) Signup(_ context.Context, request *authProto.SignUpRequest) (*authProto.SignUpResponse, error) {
func (s *serviceImpl) SignUp(_ context.Context, request *authProto.SignUpRequest) (*authProto.SignUpResponse, error) {
hashPassword, err := s.bcryptUtil.GenerateHashedPassword(request.Password)
if err != nil {
return nil, status.Error(codes.Internal, constant.InternalServerErrorMessage)
Expand Down
13 changes: 7 additions & 6 deletions src/internal/service/auth/auth.service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package auth

import (
"context"
"testing"
"time"

"github.com/go-faker/faker/v4"
"github.com/golang/mock/gomock"
"github.com/google/uuid"
Expand All @@ -19,8 +22,6 @@ import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"gorm.io/gorm"
"testing"
"time"
)

type AuthServiceTest struct {
Expand Down Expand Up @@ -97,7 +98,7 @@ func (t *AuthServiceTest) TestSignupSuccess() {
userRepo.On("Create", newUser).Return(createdUser, nil)

authSvc := NewService(authRepo, &userRepo, &tokenService, &bcryptUtil)
actual, err := authSvc.Signup(t.ctx, t.signupRequest)
actual, err := authSvc.SignUp(t.ctx, t.signupRequest)

assert.Nil(t.T(), err)
assert.Equal(t.T(), expected.Id, actual.Id)
Expand All @@ -118,7 +119,7 @@ func (t *AuthServiceTest) TestSignupHashPasswordFailed() {
bcryptUtil.On("GenerateHashedPassword", t.signupRequest.Password).Return("", hashPasswordErr)

authSvc := NewService(authRepo, &userRepo, &tokenService, &bcryptUtil)
actual, err := authSvc.Signup(t.ctx, t.signupRequest)
actual, err := authSvc.SignUp(t.ctx, t.signupRequest)

status, ok := status.FromError(err)

Expand Down Expand Up @@ -152,7 +153,7 @@ func (t *AuthServiceTest) TestSignupCreateUserDuplicateConstraint() {
userRepo.On("Create", newUser).Return(nil, createUserErr)

authSvc := NewService(authRepo, &userRepo, &tokenService, &bcryptUtil)
actual, err := authSvc.Signup(t.ctx, t.signupRequest)
actual, err := authSvc.SignUp(t.ctx, t.signupRequest)

status, ok := status.FromError(err)

Expand Down Expand Up @@ -186,7 +187,7 @@ func (t *AuthServiceTest) TestSignupCreateUserInternalFailed() {
userRepo.On("Create", newUser).Return(nil, createUserErr)

authSvc := NewService(authRepo, &userRepo, &tokenService, &bcryptUtil)
actual, err := authSvc.Signup(t.ctx, t.signupRequest)
actual, err := authSvc.SignUp(t.ctx, t.signupRequest)

status, ok := status.FromError(err)

Expand Down
79 changes: 79 additions & 0 deletions src/internal/service/user/user.service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package user

import (
"context"
"errors"

"github.com/isd-sgcu/johnjud-auth/src/internal/constant"
"github.com/isd-sgcu/johnjud-auth/src/internal/domain/model"
"github.com/isd-sgcu/johnjud-auth/src/internal/utils"
userRepo "github.com/isd-sgcu/johnjud-auth/src/pkg/repository/user"
proto "github.com/isd-sgcu/johnjud-go-proto/johnjud/auth/user/v1"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"gorm.io/gorm"
)

type serviceImpl struct {
proto.UnimplementedUserServiceServer
repo userRepo.Repository
bcryptUtil utils.IBcryptUtil
}

func NewService(repo userRepo.Repository, bcryptUtil utils.IBcryptUtil) *serviceImpl {
return &serviceImpl{repo: repo, bcryptUtil: bcryptUtil}
}

func (s *serviceImpl) FindOne(_ context.Context, request *proto.FindOneUserRequest) (*proto.FindOneUserResponse, error) {
raw := model.User{}

err := s.repo.FindById(request.Id, &raw)
if err != nil {
return nil, status.Error(codes.Internal, "Find one user failed")
}

return &proto.FindOneUserResponse{User: RawToDto(&raw)}, nil
}

func (s *serviceImpl) Update(_ context.Context, request *proto.UpdateUserRequest) (*proto.UpdateUserResponse, error) {
hashPassword, err := s.bcryptUtil.GenerateHashedPassword(request.Password)
if err != nil {
return nil, status.Error(codes.Internal, constant.InternalServerErrorMessage)
}

updateUser := &model.User{
Email: request.Email,
Password: hashPassword,
Firstname: request.Firstname,
Lastname: request.Lastname,
}

err = s.repo.Update(request.Id, updateUser)
if err != nil {
if errors.Is(err, gorm.ErrDuplicatedKey) {
return nil, status.Error(codes.AlreadyExists, constant.DuplicateEmailErrorMessage)
}
return nil, status.Error(codes.Internal, "Update user failed")
}

return &proto.UpdateUserResponse{User: RawToDto(updateUser)}, nil
}

func (s *serviceImpl) Delete(_ context.Context, request *proto.DeleteUserRequest) (*proto.DeleteUserResponse, error) {
err := s.repo.Delete(request.Id)
if err != nil {
return nil, status.Error(codes.Internal, "Delete user failed")
}

return &proto.DeleteUserResponse{Success: true}, nil
}

func RawToDto(in *model.User) *proto.User {
return &proto.User{
Id: in.ID.String(),
Email: in.Email,
Firstname: in.Firstname,
Lastname: in.Lastname,
Role: string(in.Role),
}
}
176 changes: 176 additions & 0 deletions src/internal/service/user/user.service_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package user

import (
"context"
"errors"
"testing"
"time"

"github.com/go-faker/faker/v4"
"github.com/google/uuid"
"github.com/isd-sgcu/johnjud-auth/src/config"
"github.com/isd-sgcu/johnjud-auth/src/internal/domain/model"
mock "github.com/isd-sgcu/johnjud-auth/src/mocks/repository/user"
"github.com/isd-sgcu/johnjud-auth/src/mocks/utils"
proto "github.com/isd-sgcu/johnjud-go-proto/johnjud/auth/user/v1"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"gorm.io/gorm"
)

type UserServiceTest struct {
suite.Suite
config *config.App
User *model.User
UpdateUser *model.User
UserDto *proto.User
UserDtoNoPassword *proto.User
HashedPassword string
UpdateUserReqMock *proto.UpdateUserRequest
}

func TestUserService(t *testing.T) {
suite.Run(t, new(UserServiceTest))
}

func (t *UserServiceTest) SetupTest() {
t.User = &model.User{
Base: model.Base{
ID: uuid.New(),
CreatedAt: time.Time{},
UpdatedAt: time.Time{},
DeletedAt: gorm.DeletedAt{},
},
Email: faker.Email(),
Password: faker.Password(),
Firstname: faker.Username(),
Lastname: faker.Username(),
Role: "user",
}

t.UserDto = &proto.User{
Id: t.User.ID.String(),
Email: t.User.Email,
Password: t.User.Password,
Firstname: t.User.Firstname,
Lastname: t.User.Lastname,
Role: string(t.User.Role),
}

t.UserDtoNoPassword = &proto.User{
Id: t.User.ID.String(),
Email: t.User.Email,
Firstname: t.User.Firstname,
Lastname: t.User.Lastname,
Role: string(t.User.Role),
}

t.UpdateUserReqMock = &proto.UpdateUserRequest{
Id: t.User.ID.String(),
Email: t.User.Email,
Password: t.User.Password,
Firstname: t.User.Firstname,
Lastname: t.User.Lastname,
}

t.HashedPassword = faker.Password()

t.UpdateUser = &model.User{
Email: t.User.Email,
Password: t.HashedPassword,
Firstname: t.User.Firstname,
Lastname: t.User.Lastname,
}
}

func (t *UserServiceTest) TestFindOneSuccess() {
want := &proto.FindOneUserResponse{User: t.UserDtoNoPassword}

repo := &mock.UserRepositoryMock{}
repo.On("FindById", t.User.ID.String(), &model.User{}).Return(t.User, nil)

brcyptUtil := &utils.BcryptUtilMock{}
srv := NewService(repo, brcyptUtil)
actual, err := srv.FindOne(context.Background(), &proto.FindOneUserRequest{Id: t.User.ID.String()})

assert.Nil(t.T(), err)
assert.Equal(t.T(), want, actual)
}

func (t *UserServiceTest) TestFindOneInternalErr() {
repo := &mock.UserRepositoryMock{}
repo.On("FindById", t.User.ID.String(), &model.User{}).Return(nil, errors.New("Not found user"))

brcyptUtil := &utils.BcryptUtilMock{}
srv := NewService(repo, brcyptUtil)
actual, err := srv.FindOne(context.Background(), &proto.FindOneUserRequest{Id: t.User.ID.String()})

st, ok := status.FromError(err)

assert.True(t.T(), ok)
assert.Nil(t.T(), actual)
assert.Equal(t.T(), codes.Internal, st.Code())
}

func (t *UserServiceTest) TestUpdateSuccess() {
want := &proto.UpdateUserResponse{User: t.UserDtoNoPassword}

repo := &mock.UserRepositoryMock{}
repo.On("Update", t.User.ID.String(), t.UpdateUser).Return(t.User, nil)

brcyptUtil := &utils.BcryptUtilMock{}
brcyptUtil.On("GenerateHashedPassword", t.User.Password).Return(t.HashedPassword, nil)

srv := NewService(repo, brcyptUtil)
actual, err := srv.Update(context.Background(), t.UpdateUserReqMock)

assert.Nil(t.T(), err)
assert.Equal(t.T(), want, actual)
}

func (t *UserServiceTest) TestUpdateInternalErr() {
repo := &mock.UserRepositoryMock{}
repo.On("Update", t.User.ID.String(), t.UpdateUser).Return(nil, errors.New("Not found user"))

brcyptUtil := &utils.BcryptUtilMock{}
brcyptUtil.On("GenerateHashedPassword", t.User.Password).Return(t.HashedPassword, nil)

srv := NewService(repo, brcyptUtil)
actual, err := srv.Update(context.Background(), t.UpdateUserReqMock)

st, ok := status.FromError(err)

assert.True(t.T(), ok)
assert.Nil(t.T(), actual)
assert.Equal(t.T(), codes.Internal, st.Code())
}

func (t *UserServiceTest) TestDeleteSuccess() {
want := &proto.DeleteUserResponse{Success: true}

repo := &mock.UserRepositoryMock{}
repo.On("Delete", t.User.ID.String()).Return(nil)

brcyptUtil := &utils.BcryptUtilMock{}
srv := NewService(repo, brcyptUtil)
actual, err := srv.Delete(context.Background(), &proto.DeleteUserRequest{Id: t.UserDto.Id})

assert.Nil(t.T(), err)
assert.Equal(t.T(), want, actual)
}

func (t *UserServiceTest) TestDeleteInternalErr() {
repo := &mock.UserRepositoryMock{}
repo.On("Delete", t.User.ID.String()).Return(errors.New("Not found user"))

brcyptUtil := &utils.BcryptUtilMock{}
srv := NewService(repo, brcyptUtil)
actual, err := srv.Delete(context.Background(), &proto.DeleteUserRequest{Id: t.UserDto.Id})

st, ok := status.FromError(err)
assert.True(t.T(), ok)
assert.Nil(t.T(), actual)
assert.Equal(t.T(), codes.Internal, st.Code())
}
Loading

0 comments on commit 606d179

Please sign in to comment.