forked from ebhomengo/niki
1
0
Fork 0

feat(niki): add test for add benefactor kind_box_req

This commit is contained in:
mohammad mahdi rezaei 2024-01-26 15:41:50 +03:30 committed by mehdi
parent 748dee60e2
commit 58c48dfccc
22 changed files with 916 additions and 69 deletions

7
config/test.yml Normal file
View File

@ -0,0 +1,7 @@
debug: false
multi_word_var: "I'm complex in config.yml"
db:
host: "localhost"
username: "ali"
password: "passwd"
multi_word_nested_var: "WHAT??"

View File

@ -1,28 +1,36 @@
package benefactorkindboxreqhandler package benefactorkindboxreqhandler
import ( import (
"fmt"
"net/http" "net/http"
param "git.gocasts.ir/ebhomengo/niki/param/benefactor/kind_box_req" param "git.gocasts.ir/ebhomengo/niki/param/benefactor/kind_box_req"
"git.gocasts.ir/ebhomengo/niki/pkg/claim" "git.gocasts.ir/ebhomengo/niki/pkg/claim"
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
httpmsg "git.gocasts.ir/ebhomengo/niki/pkg/http_msg" httpmsg "git.gocasts.ir/ebhomengo/niki/pkg/http_msg"
echo "github.com/labstack/echo/v4" echo "github.com/labstack/echo/v4"
) )
func (h Handler) Add(c echo.Context) error { func (h Handler) Add(c echo.Context) error {
req := param.KindBoxReqAddRequest{} req := param.KindBoxReqAddRequest{}
if bErr := c.Bind(&req); bErr != nil { if err := c.Bind(&req); err != nil {
return echo.NewHTTPError(http.StatusBadRequest) fmt.Println("err", err, req)
return c.JSON(http.StatusBadRequest, echo.Map{
"message": errmsg.ErrBadRequest,
})
// TODO: return echo.NewHTTPError(http.StatusBadRequest, errmsg.ErrBadRequest) ؟؟؟
} }
claims := claim.GetClaimsFromEchoContext(c) claims := claim.GetClaimsFromEchoContext(c)
req.BenefactorID = claims.UserID req.BenefactorID = claims.UserID
if fieldErrors, err := h.benefactorKindBoxReqVld.ValidateAddRequest(req); err != nil { result := h.benefactorKindBoxReqVld.ValidateAddRequest(req)
msg, code := httpmsg.Error(err) if result != nil {
msg, code := httpmsg.Error(result.Err)
return c.JSON(code, echo.Map{ return c.JSON(code, echo.Map{
"message": msg, "message": msg,
"errors": fieldErrors, "errors": result.Fields,
}) })
} }
resp, sErr := h.benefactorKindBoxReqSvc.Add(c.Request().Context(), req) resp, sErr := h.benefactorKindBoxReqSvc.Add(c.Request().Context(), req)

View File

@ -0,0 +1,153 @@
package benefactorkindboxreqhandler_test
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"time"
"git.gocasts.ir/ebhomengo/niki/delivery/http_server/middleware"
"git.gocasts.ir/ebhomengo/niki/entity"
"git.gocasts.ir/ebhomengo/niki/param/benefactor/address"
benefactoreparam "git.gocasts.ir/ebhomengo/niki/param/benefactor/benefactore"
benefactorkindboxreqparam "git.gocasts.ir/ebhomengo/niki/param/benefactor/kind_box_req"
testutils "git.gocasts.ir/ebhomengo/niki/test"
"git.gocasts.ir/ebhomengo/niki/test/seed"
"github.com/brianvoe/gofakeit/v6"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
)
func TestAdd(t *testing.T) {
testutils.SetupEnd2EndTest(t)
respSendOTP := testutils.SendOTP(t)
loginOrRegisterRequest := benefactoreparam.LoginOrRegisterRequest{
PhoneNumber: respSendOTP.PhoneNumber,
VerificationCode: respSendOTP.Code,
}
benefactor, cleanupBenefactor := testutils.CreateBenefactorWithSvc(t, loginOrRegisterRequest)
defer cleanupBenefactor()
benefactorAddAddressRequest := addressparam.BenefactorAddAddressRequest{
PostalCode: gofakeit.Address().Zip,
Address: gofakeit.Address().Address,
Lat: float32(gofakeit.Address().Latitude),
Lon: float32(gofakeit.Address().Longitude),
CityID: gofakeit.UintRange(1, 100),
ProvinceID: gofakeit.UintRange(1, 31),
BenefactorID: benefactor.BenefactorInfo.ID,
}
address, cleanupAddress := testutils.CreateAddressWithSvc(t, benefactorAddAddressRequest)
defer cleanupAddress()
kindboxreqResponse, _ := json.Marshal(entity.KindBoxReq{
ID: 1,
KindBoxType: entity.KindBoxCylindrical,
CountRequested: gofakeit.UintRange(1, 100),
CountAccepted: 0,
BenefactorID: benefactor.BenefactorInfo.ID,
Status: entity.KindBoxReqPendingStatus,
Description: "",
ReferDate: time.Time{},
AddressID: address.Address.ID,
})
type testCase struct {
name string
requestBody interface{}
expectedStatus int
expectedBody string
err bool
token string
}
testCases := []testCase{
{
name: "invalid payload",
requestBody: `invalid payload`,
expectedStatus: http.StatusBadRequest,
expectedBody: `{"message": "Bad request"}`,
err: true,
token: "Bearer " + benefactor.Tokens.AccessToken,
},
{
name: "invalid or expired jwt",
requestBody: benefactorkindboxreqparam.KindBoxReqAddRequest{
TypeID: 1,
AddressID: address.Address.ID,
ReferDate: time.Now(),
CountRequested: 1,
},
token: "Bearer 12" + benefactor.Tokens.AccessToken,
expectedStatus: http.StatusUnauthorized,
err: true,
expectedBody: `{"message":"invalid or expired jwt"}`,
},
{
name: "Validation Failed",
requestBody: benefactorkindboxreqparam.KindBoxReqAddRequest{
AddressID: address.Address.ID,
ReferDate: time.Now(),
CountRequested: 2,
},
err: true,
token: "Bearer " + benefactor.Tokens.AccessToken,
expectedStatus: http.StatusUnprocessableEntity,
expectedBody: `{
"errors":{
"type_id":"cannot be blank"
},
"message":"invalid input"
}`,
},
{
name: "Added successfully",
requestBody: benefactorkindboxreqparam.KindBoxReqAddRequest{
TypeID: 2,
AddressID: address.Address.ID,
ReferDate: time.Now(),
CountRequested: 2,
},
token: "Bearer " + benefactor.Tokens.AccessToken,
expectedStatus: http.StatusCreated,
expectedBody: string(kindboxreqResponse),
},
}
e := echo.New()
r := e.Group("/benefactor/kindboxreqs")
r.POST("/", testutils.BenefactorkindBoxReqHandler.Add, middleware.Auth(testutils.AuthSvc, testutils.AuthConfig),
middleware.BenefactorAuthorization(entity.UserBenefactorRole))
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
requestBody, _ := json.Marshal(tc.requestBody)
req := httptest.NewRequest(http.MethodPost, "/benefactor/kindboxreqs/", bytes.NewBuffer(requestBody))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
req.Header.Set(echo.HeaderAuthorization, tc.token)
rec := httptest.NewRecorder()
e.ServeHTTP(rec, req)
// Assertion
assert.Equal(t, tc.expectedStatus, rec.Code)
if tc.err {
assert.JSONEq(t, tc.expectedBody, rec.Body.String())
return
}
response := &benefactorkindboxreqparam.KindBoxReqAddResponse{}
err := json.Unmarshal(
rec.Body.Bytes(),
response,
)
assert.Nil(t, err, "error in deserializing the request")
seed.DeleteBenefactor(t, testutils.MysqlRepo, response.KindBoxReq.ID)
assert.NotEmpty(t, response.KindBoxReq)
})
}
}

40
docker-compose.dev.yaml Normal file
View File

@ -0,0 +1,40 @@
version: '3.9'
services:
mysqltest:
image: mysql:8.0
ports:
- "3305:3306"
container_name: niki-database-test
volumes:
- dbdatatest:/var/lib/mysql
restart: always
command: [ 'mysqld', '--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci' ]
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: test_db
MYSQL_USER: testuser
MYSQL_PASSWORD: test1234
niki-redis-test:
image: bitnami/redis:6.2
container_name: niki-redis-test
restart: always
ports:
- '6381:6379'
# TODO - remove `--save "" --appendonly no` from command to persist data
command: redis-server --loglevel warning --protected-mode no --save "" --appendonly no
environment:
- ALLOW_EMPTY_PASSWORD=yes
volumes:
- niki-redis-data-test:/data
volumes:
dbdatatest:
niki-redis-data-test:
# docker-compose -f docker-compose.dev.yaml up -d

View File

@ -1,37 +0,0 @@
version: '3.9'
services:
mysql:
platform: linux/amd64
image: mysql:8.0
ports:
- 3305:3305
volumes:
- ~/apps/mysql:/var/lib/mysql
restart: always
hostname: mysql
container_name: niki_mysql
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_USER=niki_user
- MYSQL_PASSWORD=NIKI_user@123
- MYSQL_DATABASE=niki_db
niki-redis:
image: bitnami/redis:6.2
container_name: niki-redis
restart: always
ports:
- '6380:6379'
# TODO - remove `--save "" --appendonly no` from command to persist data
command: redis-server --loglevel warning --protected-mode no --save "" --appendonly no
environment:
- ALLOW_EMPTY_PASSWORD=yes
volumes:
- niki-redis-data:/data
volumes:
dbdata:
niki-redis-data:

4
go.mod
View File

@ -3,6 +3,7 @@ module git.gocasts.ir/ebhomengo/niki
go 1.21.3 go 1.21.3
require ( require (
github.com/brianvoe/gofakeit/v6 v6.28.0
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible github.com/go-ozzo/ozzo-validation v3.6.0+incompatible
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 github.com/go-ozzo/ozzo-validation/v4 v4.3.0
github.com/go-sql-driver/mysql v1.6.0 github.com/go-sql-driver/mysql v1.6.0
@ -14,6 +15,7 @@ require (
github.com/oklog/ulid/v2 v2.1.0 github.com/oklog/ulid/v2 v2.1.0
github.com/redis/go-redis/v9 v9.4.0 github.com/redis/go-redis/v9 v9.4.0
github.com/rubenv/sql-migrate v1.6.0 github.com/rubenv/sql-migrate v1.6.0
github.com/stretchr/testify v1.8.4
golang.org/x/crypto v0.17.0 golang.org/x/crypto v0.17.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/natefinch/lumberjack.v2 v2.2.1
) )
@ -21,6 +23,7 @@ require (
require ( require (
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 // indirect github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // 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/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/fatih/structs v1.1.0 // indirect github.com/fatih/structs v1.1.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect
@ -33,6 +36,7 @@ require (
github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect
golang.org/x/net v0.19.0 // indirect golang.org/x/net v0.19.0 // indirect

2
go.sum
View File

@ -27,6 +27,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/brianvoe/gofakeit/v6 v6.28.0 h1:Xib46XXuQfmlLS2EXRuJpqcw8St6qSZz75OUo0tgAW4=
github.com/brianvoe/gofakeit/v6 v6.28.0/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=

View File

@ -4,7 +4,6 @@ import (
"git.gocasts.ir/ebhomengo/niki/adapter/redis" "git.gocasts.ir/ebhomengo/niki/adapter/redis"
"git.gocasts.ir/ebhomengo/niki/config" "git.gocasts.ir/ebhomengo/niki/config"
"git.gocasts.ir/ebhomengo/niki/repository/mysql" "git.gocasts.ir/ebhomengo/niki/repository/mysql"
mysqlkindboxreq "git.gocasts.ir/ebhomengo/niki/repository/mysql/kind_box_req"
adminvalidator "git.gocasts.ir/ebhomengo/niki/validator/admin/admin" adminvalidator "git.gocasts.ir/ebhomengo/niki/validator/admin/admin"
adminkindboxreqvalidator "git.gocasts.ir/ebhomengo/niki/validator/admin/kind_box_req" adminkindboxreqvalidator "git.gocasts.ir/ebhomengo/niki/validator/admin/kind_box_req"
benefactoraddressvalidator "git.gocasts.ir/ebhomengo/niki/validator/benefactor/address" benefactoraddressvalidator "git.gocasts.ir/ebhomengo/niki/validator/benefactor/address"
@ -34,7 +33,6 @@ func InitBenefactorValidator() benefactorvalidator.Validator {
func InitBenefactorKindBoxReqValidator(cfg config.Config, redisAdapter redis.Adapter, db *mysql.DB) benefactorkindboxreqvalidator.Validator { func InitBenefactorKindBoxReqValidator(cfg config.Config, redisAdapter redis.Adapter, db *mysql.DB) benefactorkindboxreqvalidator.Validator {
return benefactorkindboxreqvalidator.New( return benefactorkindboxreqvalidator.New(
mysqlkindboxreq.New(db),
InitBenefactorService(cfg, redisAdapter, db), InitBenefactorService(cfg, redisAdapter, db),
InitBenefactorAddressService(db), InitBenefactorAddressService(db),
) )

View File

@ -1,6 +1,8 @@
package benefactorkindboxreqparam package benefactorkindboxreqparam
import ( import (
"time"
entity "git.gocasts.ir/ebhomengo/niki/entity" entity "git.gocasts.ir/ebhomengo/niki/entity"
) )
@ -8,7 +10,7 @@ type KindBoxReqAddRequest struct {
BenefactorID uint `json:"benefactor_id"` BenefactorID uint `json:"benefactor_id"`
TypeID entity.KindBoxType `json:"type_id"` TypeID entity.KindBoxType `json:"type_id"`
AddressID uint `json:"address_id"` AddressID uint `json:"address_id"`
ReferDate string `json:"refer_date"` ReferDate time.Time `json:"refer_date"`
CountRequested uint `json:"count_requested"` CountRequested uint `json:"count_requested"`
} }

View File

@ -14,6 +14,7 @@ const (
ErrorMsgOtpCodeIsNotValid = "verification code is not valid" ErrorMsgOtpCodeIsNotValid = "verification code is not valid"
ErrorMsgCantScanQueryResult = "can't scan query result" ErrorMsgCantScanQueryResult = "can't scan query result"
ErrorMsgPhoneNumberOrPassIsIncorrect = "phone number or password is incorrect" ErrorMsgPhoneNumberOrPassIsIncorrect = "phone number or password is incorrect"
ErrBadRequest = "Bad request"
ErrorMsgAcceptKindBoxReqStatus = "only pending requests will have the ability to be confirmed" ErrorMsgAcceptKindBoxReqStatus = "only pending requests will have the ability to be confirmed"
ErrorMsgRejectKindBoxReqStatus = "only pending requests will have the ability to be rejected" ErrorMsgRejectKindBoxReqStatus = "only pending requests will have the ability to be rejected"
) )

View File

@ -0,0 +1,72 @@
package mysqlkindboxreq_test
import (
"context"
"testing"
"git.gocasts.ir/ebhomengo/niki/entity"
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
mysqlkindboxreq "git.gocasts.ir/ebhomengo/niki/repository/mysql/kind_box_req"
testutils "git.gocasts.ir/ebhomengo/niki/test"
"git.gocasts.ir/ebhomengo/niki/test/seed"
"github.com/brianvoe/gofakeit/v6"
"github.com/stretchr/testify/assert"
)
func TestAddKindBoxReq(t *testing.T) {
mysqlRepo := testutils.Setup(t)
mysqlKindboxReq := mysqlkindboxreq.New(mysqlRepo)
benefactor, cleanupBenefactor := seed.CreateBenefactor(t, mysqlRepo)
defer cleanupBenefactor()
address, cleanupAddress := seed.CreateAddress(t, mysqlRepo, benefactor.ID)
defer cleanupAddress()
testCases := []struct {
name string
repoErr bool
expectedErr error
kindBoxReq entity.KindBoxReq
}{
{
name: "repo fails",
repoErr: true,
expectedErr: richerror.New("mysqlkindboxreq.AddKindBoxReq").WithMessage(errmsg.ErrorMsgNotFound).WithKind(richerror.KindUnexpected),
kindBoxReq: entity.KindBoxReq{
KindBoxType: entity.KindBoxStandUp,
AddressID: address.ID,
CountRequested: gofakeit.UintRange(1, 100),
ReferDate: gofakeit.Date(),
Status: entity.KindBoxReqPendingStatus,
},
},
{
name: "ordinary",
kindBoxReq: entity.KindBoxReq{
BenefactorID: benefactor.ID,
KindBoxType: entity.KindBoxStandUp,
AddressID: address.ID,
CountRequested: gofakeit.UintRange(1, 100),
ReferDate: gofakeit.Date(),
Status: entity.KindBoxReqPendingStatus,
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
ctx := context.Background()
kindBoxReq, err := mysqlKindboxReq.AddKindBoxReq(ctx, tc.kindBoxReq)
if tc.expectedErr != nil {
assert.Equal(t, tc.expectedErr.Error(), err.Error())
assert.Empty(t, kindBoxReq)
return
}
assert.NoError(t, err)
assert.NotEmpty(t, kindBoxReq)
seed.DeleteBenefactor(t, mysqlRepo, kindBoxReq.ID)
})
}
}

View File

View File

@ -2,7 +2,6 @@ package benefactorkindboxreqservice
import ( import (
"context" "context"
"time"
entity "git.gocasts.ir/ebhomengo/niki/entity" entity "git.gocasts.ir/ebhomengo/niki/entity"
param "git.gocasts.ir/ebhomengo/niki/param/benefactor/kind_box_req" param "git.gocasts.ir/ebhomengo/niki/param/benefactor/kind_box_req"
@ -11,15 +10,11 @@ import (
func (s Service) Add(ctx context.Context, req param.KindBoxReqAddRequest) (param.KindBoxReqAddResponse, error) { func (s Service) Add(ctx context.Context, req param.KindBoxReqAddRequest) (param.KindBoxReqAddResponse, error) {
const op = "userkindboxreqservice.Add" const op = "userkindboxreqservice.Add"
t, tErr := time.Parse(time.DateTime, req.ReferDate)
if tErr != nil {
return param.KindBoxReqAddResponse{}, richerror.New(op).WithErr(tErr).WithKind(richerror.KindInvalid)
}
kindBoxReq, err := s.repo.AddKindBoxReq(ctx, entity.KindBoxReq{ kindBoxReq, err := s.repo.AddKindBoxReq(ctx, entity.KindBoxReq{
BenefactorID: req.BenefactorID, BenefactorID: req.BenefactorID,
KindBoxType: req.TypeID, KindBoxType: req.TypeID,
AddressID: req.AddressID, AddressID: req.AddressID,
ReferDate: t, ReferDate: req.ReferDate,
CountRequested: req.CountRequested, CountRequested: req.CountRequested,
Status: entity.KindBoxReqPendingStatus, Status: entity.KindBoxReqPendingStatus,
}) })

View File

@ -0,0 +1,64 @@
package benefactorkindboxreqservice_test
import (
"context"
"fmt"
"testing"
"time"
benefactorkindboxreqparam "git.gocasts.ir/ebhomengo/niki/param/benefactor/kind_box_req"
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
benefactorkindboxreqservice "git.gocasts.ir/ebhomengo/niki/service/benefactor/kind_box_req"
"git.gocasts.ir/ebhomengo/niki/test/mock"
"github.com/stretchr/testify/assert"
)
func TestAdd(t *testing.T) {
testCases := []struct {
name string
repoErr bool
expectedErr error
req benefactorkindboxreqparam.KindBoxReqAddRequest
}{
{
name: "repo fails",
repoErr: true,
expectedErr: richerror.New("userkindboxreqservice.Add").WithErr(fmt.Errorf(benefactorkindboxreqmock.RepoErr)).WithKind(richerror.KindUnexpected),
req: benefactorkindboxreqparam.KindBoxReqAddRequest{
BenefactorID: 1,
AddressID: 1,
ReferDate: time.Now(),
CountRequested: 1,
TypeID: 1,
},
},
{
name: "ordinary",
req: benefactorkindboxreqparam.KindBoxReqAddRequest{
BenefactorID: 1,
AddressID: 1,
ReferDate: time.Now(),
CountRequested: 1,
TypeID: 1,
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
repo := benefactorkindboxreqmock.NewMockRepository(tc.repoErr)
svc := benefactorkindboxreqservice.New(repo)
ctx := context.Background()
kindBoxreq, err := svc.Add(ctx, tc.req)
if tc.expectedErr != nil {
assert.Equal(t, tc.expectedErr.Error(), err.Error())
assert.Empty(t, kindBoxreq)
return
}
assert.NoError(t, err)
assert.NotEmpty(t, kindBoxreq)
})
}
}

39
test/db.go Normal file
View File

@ -0,0 +1,39 @@
package testutils
import (
"sync"
"testing"
"git.gocasts.ir/ebhomengo/niki/repository/migrator"
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
//nolint
_ "github.com/go-sql-driver/mysql"
)
var once = sync.Once{}
const port = 3305
func MySQLTestConfig() mysql.Config {
return mysql.Config{
Username: "testuser",
Password: "test1234",
Port: port,
Host: "localhost",
DBName: "test_db",
}
}
func Setup(t *testing.T) *mysql.DB {
t.Helper()
// connect to mysql database
config := MySQLTestConfig()
once.Do(func() {
mgr := migrator.New(config)
mgr.Up()
})
mysqlRepo := mysql.New(config)
return mysqlRepo
}

105
test/end2end.go Normal file
View File

@ -0,0 +1,105 @@
package testutils
import (
"context"
"testing"
smsprovider "git.gocasts.ir/ebhomengo/niki/adapter/sms_provider/kavenegar"
kavenegarotp "git.gocasts.ir/ebhomengo/niki/adapter/sms_provider/kavenegar/otp"
"git.gocasts.ir/ebhomengo/niki/config"
benefactorkindboxreqhandler "git.gocasts.ir/ebhomengo/niki/delivery/http_server/benefactor/kind_box_req"
"git.gocasts.ir/ebhomengo/niki/internal/initial"
addressparam "git.gocasts.ir/ebhomengo/niki/param/benefactor/address"
benefactoreparam "git.gocasts.ir/ebhomengo/niki/param/benefactor/benefactore"
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
mysqladdress "git.gocasts.ir/ebhomengo/niki/repository/mysql/address"
mysqlbenefactor "git.gocasts.ir/ebhomengo/niki/repository/mysql/benefactor"
mysqlkindboxreq "git.gocasts.ir/ebhomengo/niki/repository/mysql/kind_box_req"
redisotp "git.gocasts.ir/ebhomengo/niki/repository/redis/redis_otp"
authservice "git.gocasts.ir/ebhomengo/niki/service/auth"
benefactoraddressservice "git.gocasts.ir/ebhomengo/niki/service/benefactor/address"
benefactorservice "git.gocasts.ir/ebhomengo/niki/service/benefactor/benefactor"
benefactorkindboxreqservice "git.gocasts.ir/ebhomengo/niki/service/benefactor/kind_box_req"
benefactorkindboxreqvalidator "git.gocasts.ir/ebhomengo/niki/validator/benefactor/kind_box_req"
"github.com/brianvoe/gofakeit/v6"
"github.com/stretchr/testify/assert"
)
var (
benefactorSvc benefactorservice.Service
benefactorAddressSvc benefactoraddressservice.Service
BenefactorkindBoxReqHandler benefactorkindboxreqhandler.Handler
AuthSvc authservice.Service
MysqlRepo *mysql.DB
AuthConfig authservice.Config
)
func SetupEnd2EndTest(t *testing.T) {
t.Helper()
cfg := config.C()
MysqlRepo = Setup(t)
redisAdapter := SetupRedis(t)
AuthSvc = initial.InitBenefactorAuthService(cfg)
RedisOtp := redisotp.New(redisAdapter)
benefactorMysql := mysqlbenefactor.New(MysqlRepo)
kavenegarSmsProvider := smsprovider.New(cfg.KavenegarSmsProvider)
otpSmsProvider := kavenegarotp.New(kavenegarSmsProvider)
benefactorSvc = benefactorservice.New(cfg.BenefactorSvc, RedisOtp, otpSmsProvider, AuthSvc, benefactorMysql)
benefactorAddressMysql := mysqladdress.New(MysqlRepo)
benefactorAddressSvc = benefactoraddressservice.New(benefactorAddressMysql)
benefactorKindBoxReqMysql := mysqlkindboxreq.New(MysqlRepo)
benefactorKindBoxReqSvc := benefactorkindboxreqservice.New(benefactorKindBoxReqMysql)
benefactorKindBoxReqVld := benefactorkindboxreqvalidator.New(benefactorSvc, benefactorAddressSvc)
BenefactorkindBoxReqHandler = benefactorkindboxreqhandler.New(cfg.Auth, AuthSvc, benefactorKindBoxReqSvc, benefactorKindBoxReqVld)
//nolint
return
}
func SendOTP(t *testing.T) benefactoreparam.SendOtpResponse {
t.Helper()
req := benefactoreparam.SendOtpRequest{PhoneNumber: gofakeit.Phone()}
ctx := context.Background()
resp, err := benefactorSvc.SendOtp(ctx, req)
if err != nil {
t.Logf(err.Error())
}
return resp
}
//nolint
func CreateBenefactorWithSvc(t *testing.T, req benefactoreparam.LoginOrRegisterRequest) (benefactoreparam.LoginOrRegisterResponse, func()) {
t.Helper()
ctx := context.Background()
resp, err := benefactorSvc.LoginOrRegister(ctx, req)
if err != nil {
t.Logf(err.Error())
}
return resp, func() {
_, err := MysqlRepo.Conn().ExecContext(ctx, `delete from benefactors where id=?`,
resp.BenefactorInfo.ID)
assert.Nil(t, err)
}
}
//nolint
func CreateAddressWithSvc(t *testing.T, req addressparam.BenefactorAddAddressRequest) (addressparam.BenefactorAddAddressResponse, func()) {
t.Helper()
ctx := context.Background()
resp, err := benefactorAddressSvc.Add(ctx, req)
if err != nil {
t.Logf(err.Error())
}
return resp, func() {
_, err := MysqlRepo.Conn().ExecContext(ctx, `delete from addresses where id=?`,
resp.Address.ID)
assert.Nil(t, err)
}
}

View File

@ -0,0 +1,65 @@
package benefactorkindboxreqmock
import (
"context"
"fmt"
"time"
"git.gocasts.ir/ebhomengo/niki/entity"
)
const RepoErr = "repository error"
type DefaultKindBoxReqTest struct {
BenefactorID uint
TypeID entity.KindBoxType
AddressID uint
ReferDate time.Time
CountRequested uint
}
func DefaultKindBoxReq() DefaultKindBoxReqTest {
return DefaultKindBoxReqTest{
BenefactorID: 1,
TypeID: 1,
AddressID: 1,
ReferDate: time.Now(),
CountRequested: 1,
}
}
type MockRepository struct {
kindBoxReqs []entity.KindBoxReq
hasErr bool
}
func NewMockRepository(hasErr bool) *MockRepository {
var kindBoxReqs []entity.KindBoxReq
DefaultKindBoxReq := DefaultKindBoxReq()
kindBoxReqs = append(kindBoxReqs, entity.KindBoxReq{
BenefactorID: DefaultKindBoxReq.BenefactorID,
AddressID: DefaultKindBoxReq.AddressID,
KindBoxType: DefaultKindBoxReq.TypeID,
ReferDate: DefaultKindBoxReq.ReferDate,
CountRequested: DefaultKindBoxReq.CountRequested,
Status: entity.KindBoxReqPendingStatus,
})
return &MockRepository{
kindBoxReqs: kindBoxReqs,
hasErr: hasErr,
}
}
//nolint
func (m *MockRepository) AddKindBoxReq(ctx context.Context, kindBoxReq entity.KindBoxReq) (entity.KindBoxReq, error) {
if m.hasErr {
return entity.KindBoxReq{}, fmt.Errorf(RepoErr)
}
kindBoxReq.ID = 1
m.kindBoxReqs = append(m.kindBoxReqs, kindBoxReq)
return kindBoxReq, nil
}

26
test/redisadapter.go Normal file
View File

@ -0,0 +1,26 @@
package testutils
import (
"testing"
"git.gocasts.ir/ebhomengo/niki/adapter/redis"
)
const portRedis = 6381
func RedisTestConfig() redis.Config {
return redis.Config{
Host: "localhost",
Port: portRedis,
Password: "",
DB: 0,
}
}
func SetupRedis(t *testing.T) redis.Adapter {
t.Helper()
config := RedisTestConfig()
redisAdapter := redis.New(config)
return redisAdapter
}

79
test/seed/kind_box_req.go Normal file
View File

@ -0,0 +1,79 @@
package seed
import (
"context"
"testing"
"time"
"git.gocasts.ir/ebhomengo/niki/entity"
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
"github.com/brianvoe/gofakeit/v6"
"github.com/stretchr/testify/assert"
)
//nolint
func CreateBenefactor(t *testing.T, db *mysql.DB) (*entity.Benefactor, func()) {
t.Helper()
benefactor := &entity.Benefactor{
FirstName: gofakeit.FirstName(),
LastName: gofakeit.LastName(),
PhoneNumber: gofakeit.Phone(),
Address: gofakeit.Address().Address,
Description: "",
Email: gofakeit.Email(),
City: gofakeit.City(),
Gender: 0,
Status: entity.BenefactorActiveStatus,
Birthdate: time.Time{},
Role: entity.UserBenefactorRole,
}
ctx := context.Background()
res, err := db.Conn().ExecContext(ctx, `insert into benefactors(phone_number, status, role) values(?, ?, ?)`,
benefactor.PhoneNumber, benefactor.Status.String(), benefactor.Role.String())
assert.Nil(t, err)
//nolint
id, _ := res.LastInsertId()
benefactor.ID = uint(id)
return benefactor, func() {
_, err := db.Conn().ExecContext(ctx, `delete from benefactors where id=?`,
id)
assert.Nil(t, err)
}
}
//nolint
func CreateAddress(t *testing.T, db *mysql.DB, benfactorID uint) (*entity.Address, func()) {
t.Helper()
address := &entity.Address{
PostalCode: gofakeit.Address().Zip,
Address: gofakeit.Address().Address,
Lat: float32(gofakeit.Address().Latitude),
Lon: float32(gofakeit.Address().Longitude),
//nolint
CityID: 1,
//nolint
ProvinceID: 15,
BenefactorID: benfactorID,
}
ctx := context.Background()
res, err := db.Conn().ExecContext(ctx, `insert into addresses(postal_code, address, lat, lon,province_id,city_id,benefactor_id) values(?, ?, ?,?,?,?,?)`,
address.PostalCode, address.Address, address.Lat, address.Lon, address.ProvinceID, address.CityID, address.BenefactorID)
assert.Nil(t, err)
//nolint
// error is always nil
id, _ := res.LastInsertId()
address.ID = uint(id)
return address, func() {
_, err := db.Conn().ExecContext(ctx, `delete from addresses where id=?`,
id)
assert.Nil(t, err)
}
}
func DeleteBenefactor(t *testing.T, db *mysql.DB, kindBoxReqID uint) {
t.Helper()
_, mErr := db.Conn().Exec(`delete from kind_box_reqs where id=?`, kindBoxReqID)
assert.Nil(t, mErr)
}

View File

@ -2,7 +2,6 @@ package benefactorkindboxreqvalidator
import ( import (
"errors" "errors"
"time"
param "git.gocasts.ir/ebhomengo/niki/param/benefactor/kind_box_req" param "git.gocasts.ir/ebhomengo/niki/param/benefactor/kind_box_req"
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg" errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
@ -10,7 +9,7 @@ import (
validation "github.com/go-ozzo/ozzo-validation/v4" validation "github.com/go-ozzo/ozzo-validation/v4"
) )
func (v Validator) ValidateAddRequest(req param.KindBoxReqAddRequest) (map[string]string, error) { func (v Validator) ValidateAddRequest(req param.KindBoxReqAddRequest) *ValidatorError {
const op = "userkindboxreqvalidator.ValidateAddRequest" const op = "userkindboxreqvalidator.ValidateAddRequest"
if err := validation.ValidateStruct(&req, if err := validation.ValidateStruct(&req,
@ -31,7 +30,6 @@ func (v Validator) ValidateAddRequest(req param.KindBoxReqAddRequest) (map[strin
validation.Field(&req.ReferDate, validation.Field(&req.ReferDate,
validation.Required, validation.Required,
validation.Date(time.DateTime),
), ),
); err != nil { ); err != nil {
@ -46,12 +44,16 @@ func (v Validator) ValidateAddRequest(req param.KindBoxReqAddRequest) (map[strin
} }
} }
return fieldErrors, richerror.New(op). return &ValidatorError{
Fields: fieldErrors,
Err: richerror.New(op).
WithMessage(errmsg.ErrorMsgInvalidInput). WithMessage(errmsg.ErrorMsgInvalidInput).
WithKind(richerror.KindInvalid). WithKind(richerror.KindInvalid).
WithMeta(map[string]interface{}{"req": req}). WithMeta(map[string]interface{}{"req": req}).
WithErr(err) WithErr(err),
} }
return map[string]string{}, nil }
return nil
} }

View File

@ -0,0 +1,214 @@
package benefactorkindboxreqvalidator_test
import (
"context"
"fmt"
"testing"
"time"
"git.gocasts.ir/ebhomengo/niki/entity"
addressparam "git.gocasts.ir/ebhomengo/niki/param/benefactor/address"
param "git.gocasts.ir/ebhomengo/niki/param/benefactor/benefactore"
benefactorkindboxreqparam "git.gocasts.ir/ebhomengo/niki/param/benefactor/kind_box_req"
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
benefactorkindboxreqvalidator "git.gocasts.ir/ebhomengo/niki/validator/benefactor/kind_box_req"
"github.com/stretchr/testify/assert"
)
const RepoErr = "repository error"
type StubService struct {
addresses []entity.Address
benefactors []entity.Benefactor
haveError bool
}
func NewMock(haveError bool) *StubService {
var addresses []entity.Address
var benefactors []entity.Benefactor
addresses = append(addresses, entity.Address{
ID: 1,
PostalCode: "123456789",
Address: "tehran",
Lat: 25.25,
Lon: 25.26,
CityID: 1,
ProvinceID: 1,
BenefactorID: 1,
})
benefactors = append(benefactors, entity.Benefactor{
ID: 1,
FirstName: "mehdi",
LastName: "rez",
PhoneNumber: "09191234556",
Address: "tehran",
Description: "",
Email: "example@gmail.com",
City: "teran",
Gender: 0,
Status: 0,
Birthdate: time.Time{},
Role: 1,
})
return &StubService{
addresses: addresses,
benefactors: benefactors,
haveError: haveError,
}
}
func (s StubService) BenefactorExistByID(ctx context.Context, request param.BenefactorExistByIDRequest) (param.BenefactorExistByIDResponse, error) {
if s.haveError {
// error response
return param.BenefactorExistByIDResponse{Existed: false}, fmt.Errorf(RepoErr)
}
for _, benefactor := range s.benefactors {
if benefactor.ID == request.ID {
return param.BenefactorExistByIDResponse{Existed: true}, nil
}
}
return param.BenefactorExistByIDResponse{Existed: false}, nil
}
func (s StubService) AddressExistByID(ctx context.Context, request addressparam.GetAddressByIDRequest) (addressparam.GetAddressByIDResponse, error) {
if s.haveError {
// error response
return addressparam.GetAddressByIDResponse{Address: nil}, fmt.Errorf(RepoErr)
}
for _, address := range s.addresses {
if address.ID == request.ID {
return addressparam.GetAddressByIDResponse{Address: &address}, nil
}
}
return addressparam.GetAddressByIDResponse{Address: nil}, nil
}
func TestValidateAddRequest(t *testing.T) {
testCases := []struct {
name string
params benefactorkindboxreqparam.KindBoxReqAddRequest
repoErr bool
error error
}{
{
name: "ordinary add",
params: benefactorkindboxreqparam.KindBoxReqAddRequest{
AddressID: 1,
BenefactorID: 1,
TypeID: 1,
CountRequested: 2,
ReferDate: time.Now(),
},
},
{
name: "repo error",
repoErr: true,
error: fmt.Errorf("benefactor_id: record not found\naddress_id: something went wrong\n"),
params: benefactorkindboxreqparam.KindBoxReqAddRequest{
AddressID: 1,
BenefactorID: 1,
TypeID: 1,
CountRequested: 1,
ReferDate: time.Now(),
},
},
{
name: "Count Requested cannot be empty",
error: fmt.Errorf(fmt.Sprintf("count_requested: cannot be blank\n")),
params: benefactorkindboxreqparam.KindBoxReqAddRequest{
AddressID: 1,
BenefactorID: 1,
TypeID: 1,
CountRequested: 0,
ReferDate: time.Now(),
},
},
{
name: "TypeID cannot be empty",
error: fmt.Errorf(fmt.Sprintf("type_id: cannot be blank\n")),
params: benefactorkindboxreqparam.KindBoxReqAddRequest{
AddressID: 1,
BenefactorID: 1,
TypeID: 0,
CountRequested: 1,
ReferDate: time.Now(),
},
},
{
name: "type with ID does exists",
error: fmt.Errorf(fmt.Sprintf("type_id: %s\n", errmsg.ErrorMsgNotFound)),
params: benefactorkindboxreqparam.KindBoxReqAddRequest{
AddressID: 1,
BenefactorID: 1,
TypeID: 5,
CountRequested: 1,
ReferDate: time.Now(),
},
},
{
name: "AddressID cannot be empty",
error: fmt.Errorf(fmt.Sprintf("address_id: cannot be blank\n")),
params: benefactorkindboxreqparam.KindBoxReqAddRequest{
AddressID: 0,
TypeID: 1,
BenefactorID: 1,
CountRequested: 1,
ReferDate: time.Now(),
},
},
{
name: "address with ID does exists",
error: fmt.Errorf(fmt.Sprintf("address_id: %s\n", errmsg.ErrorMsgNotFound)),
params: benefactorkindboxreqparam.KindBoxReqAddRequest{
AddressID: 5000,
TypeID: 1,
CountRequested: 1,
BenefactorID: 1,
ReferDate: time.Now(),
},
},
{
name: "ReferDate should not be empty",
error: fmt.Errorf(fmt.Sprintf("refer_date: cannot be blank\n")),
params: benefactorkindboxreqparam.KindBoxReqAddRequest{
AddressID: 1,
TypeID: 1,
BenefactorID: 1,
CountRequested: 1,
},
},
{
name: "This address does not belong to this benefactor",
error: fmt.Errorf(fmt.Sprintf("address_id: %s\n", errmsg.ErrorMsgNotFound)),
params: benefactorkindboxreqparam.KindBoxReqAddRequest{
AddressID: 1,
BenefactorID: 100,
TypeID: 1,
CountRequested: 1,
ReferDate: time.Now(),
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// 1. setup
repo := NewMock(tc.repoErr)
vld := benefactorkindboxreqvalidator.New(repo, repo)
// 2. execution
res := vld.ValidateAddRequest(tc.params)
// 3. assertion
if tc.error == nil {
assert.Nil(t, res)
return
}
assert.Equal(t, tc.error.Error(), res.Error())
})
}
}

View File

@ -16,12 +16,6 @@ const (
MaxKindBoxReq uint = 100 MaxKindBoxReq uint = 100
) )
type Repository interface {
// KindBoxReqExist(id uint) (bool, error)
// KindBoxBelongToBenefactor(bID uint, kbID uint) (bool, error)
// PendingStatus(id uint) (bool, error)
}
type BenefactorSvc interface { type BenefactorSvc interface {
BenefactorExistByID(ctx context.Context, request param.BenefactorExistByIDRequest) (param.BenefactorExistByIDResponse, error) BenefactorExistByID(ctx context.Context, request param.BenefactorExistByIDRequest) (param.BenefactorExistByIDResponse, error)
} }
@ -31,13 +25,27 @@ type AddressSvc interface {
} }
type Validator struct { type Validator struct {
repo Repository
benefactorSvc BenefactorSvc benefactorSvc BenefactorSvc
addressSvc AddressSvc addressSvc AddressSvc
} }
func New(repo Repository, benefactorSvc BenefactorSvc, addressSvc AddressSvc) Validator { type ValidatorError struct {
return Validator{repo: repo, benefactorSvc: benefactorSvc, addressSvc: addressSvc} Fields map[string]string `json:"error"`
Err error `json:"message"`
}
func (v ValidatorError) Error() string {
var err string
for key, value := range v.Fields {
err += fmt.Sprintf("%s: %s\n", key, value)
}
return err
}
func New(benefactorSvc BenefactorSvc, addressSvc AddressSvc) Validator {
return Validator{benefactorSvc: benefactorSvc, addressSvc: addressSvc}
} }
func (v Validator) doesBenefactorExist(value interface{}) error { func (v Validator) doesBenefactorExist(value interface{}) error {