forked from ebhomengo/niki
feat(validator): implement unit test for admin login validator (#109)
This commit is contained in:
parent
705adda09b
commit
fb4bd97c5c
|
@ -0,0 +1,3 @@
|
|||
inpackage: True
|
||||
with-expecter: True
|
||||
testonly: True
|
|
@ -0,0 +1,17 @@
|
|||
## Mocking interfaces in unit tests
|
||||
1- add a //go:generate directive above the interface:
|
||||
```go
|
||||
//go:generate mockery --name Repository
|
||||
type Repository interface {
|
||||
AdminExistByPhoneNumber(ctx context.Context, phoneNumber string) (bool, error)
|
||||
AdminExistByEmail(ctx context.Context, email string) (bool, error)
|
||||
}
|
||||
```
|
||||
2- run go generate to create the mock files:
|
||||
```bash
|
||||
go generate ./...
|
||||
```
|
||||
3- use the generated mock types in your tests.
|
||||
|
||||
for more information visit:
|
||||
https://vektra.github.io/mockery/latest/
|
4
go.mod
4
go.mod
|
@ -13,6 +13,7 @@ require (
|
|||
github.com/labstack/echo/v4 v4.12.0
|
||||
github.com/redis/go-redis/v9 v9.4.0
|
||||
github.com/rubenv/sql-migrate v1.6.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/swaggo/echo-swagger v1.4.1
|
||||
github.com/swaggo/swag v1.16.3
|
||||
golang.org/x/crypto v0.23.0
|
||||
|
@ -23,6 +24,7 @@ require (
|
|||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/fatih/structs v1.1.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
|
@ -42,6 +44,8 @@ require (
|
|||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/swaggo/files/v2 v2.0.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
|
|
2
go.sum
2
go.sum
|
@ -292,6 +292,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
|||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
package adminvalidator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
adminserviceparam "git.gocasts.ir/ebhomengo/niki/param/admin/admin"
|
||||
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestValidateLoginWithPhoneNumberRequest(t *testing.T) {
|
||||
mockRepo := NewMockRepository(t)
|
||||
validator := New(mockRepo)
|
||||
ctx := context.Background()
|
||||
validPhoneNumber := "09123456789"
|
||||
validPassword := "validpassword"
|
||||
|
||||
t.Run("Valid request", func(t *testing.T) {
|
||||
|
||||
req := adminserviceparam.LoginWithPhoneNumberRequest{
|
||||
PhoneNumber: validPhoneNumber,
|
||||
Password: validPassword,
|
||||
}
|
||||
|
||||
mockRepo.EXPECT().AdminExistByPhoneNumber(ctx, validPhoneNumber).Return(true, nil).Once()
|
||||
|
||||
fieldErrors, err := validator.ValidateLoginWithPhoneNumberRequest(ctx, req)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, fieldErrors)
|
||||
})
|
||||
|
||||
t.Run("Empty phone number", func(t *testing.T) {
|
||||
|
||||
req := adminserviceparam.LoginWithPhoneNumberRequest{
|
||||
PhoneNumber: "",
|
||||
Password: validPassword,
|
||||
}
|
||||
|
||||
fieldErrors, err := validator.ValidateLoginWithPhoneNumberRequest(ctx, req)
|
||||
assert.Error(t, err)
|
||||
assert.NotNil(t, fieldErrors)
|
||||
assert.Contains(t, fieldErrors, "phone_number")
|
||||
})
|
||||
|
||||
t.Run("Empty password", func(t *testing.T) {
|
||||
|
||||
req := adminserviceparam.LoginWithPhoneNumberRequest{
|
||||
PhoneNumber: validPhoneNumber,
|
||||
Password: "",
|
||||
}
|
||||
|
||||
mockRepo.EXPECT().AdminExistByPhoneNumber(ctx, validPhoneNumber).Return(true, nil).Once()
|
||||
|
||||
fieldErrors, err := validator.ValidateLoginWithPhoneNumberRequest(ctx, req)
|
||||
assert.Error(t, err)
|
||||
assert.NotNil(t, fieldErrors)
|
||||
assert.Contains(t, fieldErrors, "password")
|
||||
})
|
||||
|
||||
t.Run("Invalid phone number format", func(t *testing.T) {
|
||||
req := adminserviceparam.LoginWithPhoneNumberRequest{
|
||||
PhoneNumber: "12345",
|
||||
Password: validPassword,
|
||||
}
|
||||
|
||||
fieldErrors, err := validator.ValidateLoginWithPhoneNumberRequest(ctx, req)
|
||||
assert.Error(t, err)
|
||||
assert.NotNil(t, fieldErrors)
|
||||
assert.Equal(t, errmsg.ErrorMsgPhoneNumberIsNotValid, fieldErrors["phone_number"])
|
||||
})
|
||||
|
||||
t.Run("Invalid password length", func(t *testing.T) {
|
||||
req := adminserviceparam.LoginWithPhoneNumberRequest{
|
||||
PhoneNumber: validPhoneNumber,
|
||||
Password: "short",
|
||||
}
|
||||
|
||||
mockRepo.EXPECT().AdminExistByPhoneNumber(ctx, validPhoneNumber).Return(true, nil).Once()
|
||||
|
||||
fieldErrors, err := validator.ValidateLoginWithPhoneNumberRequest(ctx, req)
|
||||
assert.Error(t, err)
|
||||
assert.NotNil(t, fieldErrors)
|
||||
assert.Contains(t, fieldErrors, "password")
|
||||
})
|
||||
|
||||
t.Run("Phone number does not exist", func(t *testing.T) {
|
||||
req := adminserviceparam.LoginWithPhoneNumberRequest{
|
||||
PhoneNumber: validPhoneNumber,
|
||||
Password: validPassword,
|
||||
}
|
||||
|
||||
mockRepo.EXPECT().AdminExistByPhoneNumber(ctx, validPhoneNumber).Return(false, nil).Once()
|
||||
|
||||
fieldErrors, err := validator.ValidateLoginWithPhoneNumberRequest(ctx, req)
|
||||
assert.Error(t, err)
|
||||
assert.NotNil(t, fieldErrors)
|
||||
assert.Equal(t, errmsg.ErrorMsgPhoneNumberOrPassIsIncorrect, fieldErrors["phone_number"])
|
||||
})
|
||||
|
||||
t.Run("Repository error", func(t *testing.T) {
|
||||
req := adminserviceparam.LoginWithPhoneNumberRequest{
|
||||
PhoneNumber: validPhoneNumber,
|
||||
Password: validPassword,
|
||||
}
|
||||
|
||||
mockRepo.EXPECT().AdminExistByPhoneNumber(ctx, validPhoneNumber).Return(false, errors.New("repo error")).Once()
|
||||
|
||||
fieldErrors, err := validator.ValidateLoginWithPhoneNumberRequest(ctx, req)
|
||||
assert.Error(t, err)
|
||||
assert.NotNil(t, fieldErrors)
|
||||
assert.Equal(t, errmsg.ErrorMsgSomethingWentWrong, fieldErrors["phone_number"])
|
||||
})
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
// Code generated by mockery v2.41.0. DO NOT EDIT.
|
||||
|
||||
package adminvalidator
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// MockRepository is an autogenerated mock type for the Repository type
|
||||
type MockRepository struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type MockRepository_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *MockRepository) EXPECT() *MockRepository_Expecter {
|
||||
return &MockRepository_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// AdminExistByEmail provides a mock function with given fields: ctx, email
|
||||
func (_m *MockRepository) AdminExistByEmail(ctx context.Context, email string) (bool, error) {
|
||||
ret := _m.Called(ctx, email)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for AdminExistByEmail")
|
||||
}
|
||||
|
||||
var r0 bool
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) (bool, error)); ok {
|
||||
return rf(ctx, email)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) bool); ok {
|
||||
r0 = rf(ctx, email)
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = rf(ctx, email)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// MockRepository_AdminExistByEmail_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AdminExistByEmail'
|
||||
type MockRepository_AdminExistByEmail_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// AdminExistByEmail is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - email string
|
||||
func (_e *MockRepository_Expecter) AdminExistByEmail(ctx interface{}, email interface{}) *MockRepository_AdminExistByEmail_Call {
|
||||
return &MockRepository_AdminExistByEmail_Call{Call: _e.mock.On("AdminExistByEmail", ctx, email)}
|
||||
}
|
||||
|
||||
func (_c *MockRepository_AdminExistByEmail_Call) Run(run func(ctx context.Context, email string)) *MockRepository_AdminExistByEmail_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRepository_AdminExistByEmail_Call) Return(_a0 bool, _a1 error) *MockRepository_AdminExistByEmail_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRepository_AdminExistByEmail_Call) RunAndReturn(run func(context.Context, string) (bool, error)) *MockRepository_AdminExistByEmail_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// AdminExistByPhoneNumber provides a mock function with given fields: ctx, phoneNumber
|
||||
func (_m *MockRepository) AdminExistByPhoneNumber(ctx context.Context, phoneNumber string) (bool, error) {
|
||||
ret := _m.Called(ctx, phoneNumber)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for AdminExistByPhoneNumber")
|
||||
}
|
||||
|
||||
var r0 bool
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) (bool, error)); ok {
|
||||
return rf(ctx, phoneNumber)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) bool); ok {
|
||||
r0 = rf(ctx, phoneNumber)
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = rf(ctx, phoneNumber)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// MockRepository_AdminExistByPhoneNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AdminExistByPhoneNumber'
|
||||
type MockRepository_AdminExistByPhoneNumber_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// AdminExistByPhoneNumber is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - phoneNumber string
|
||||
func (_e *MockRepository_Expecter) AdminExistByPhoneNumber(ctx interface{}, phoneNumber interface{}) *MockRepository_AdminExistByPhoneNumber_Call {
|
||||
return &MockRepository_AdminExistByPhoneNumber_Call{Call: _e.mock.On("AdminExistByPhoneNumber", ctx, phoneNumber)}
|
||||
}
|
||||
|
||||
func (_c *MockRepository_AdminExistByPhoneNumber_Call) Run(run func(ctx context.Context, phoneNumber string)) *MockRepository_AdminExistByPhoneNumber_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRepository_AdminExistByPhoneNumber_Call) Return(_a0 bool, _a1 error) *MockRepository_AdminExistByPhoneNumber_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRepository_AdminExistByPhoneNumber_Call) RunAndReturn(run func(context.Context, string) (bool, error)) *MockRepository_AdminExistByPhoneNumber_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// NewMockRepository creates a new instance of MockRepository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewMockRepository(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *MockRepository {
|
||||
mock := &MockRepository{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
|
@ -19,10 +19,12 @@ const (
|
|||
maxLengthPassword = 32
|
||||
)
|
||||
|
||||
//go:generate mockery --name Repository
|
||||
type Repository interface {
|
||||
AdminExistByPhoneNumber(ctx context.Context, phoneNumber string) (bool, error)
|
||||
AdminExistByEmail(ctx context.Context, email string) (bool, error)
|
||||
}
|
||||
|
||||
type Validator struct {
|
||||
repo Repository
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue