feat: usecast test
This commit is contained in:
parent
39841cc168
commit
52905207c6
1
go.mod
1
go.mod
|
@ -11,6 +11,7 @@ require (
|
|||
github.com/testcontainers/testcontainers-go v0.34.0
|
||||
github.com/zeromicro/go-zero v1.8.1
|
||||
go.mongodb.org/mongo-driver v1.17.3
|
||||
go.uber.org/mock v0.5.0
|
||||
google.golang.org/grpc v1.71.0
|
||||
google.golang.org/protobuf v1.36.5
|
||||
)
|
||||
|
|
6
go.sum
6
go.sum
|
@ -278,6 +278,8 @@ go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
|||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||
|
@ -348,8 +350,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
|
|||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
|
||||
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
package usecase
|
||||
|
||||
import (
|
||||
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/entity"
|
||||
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/repository"
|
||||
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/usecase"
|
||||
"code.30cm.net/digimon/library-go/errs"
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/stores/mon"
|
||||
)
|
||||
|
||||
type CategoryUseCaseParam struct {
|
||||
CategoryRepo repository.CategoryRepository
|
||||
}
|
||||
|
||||
type CategoryUseCase struct {
|
||||
CategoryUseCaseParam
|
||||
}
|
||||
|
||||
func MustCategoryUseCase(param CategoryUseCaseParam) usecase.CategoryUseCase {
|
||||
return &CategoryUseCase{
|
||||
param,
|
||||
}
|
||||
}
|
||||
|
||||
func (use *CategoryUseCase) Insert(ctx context.Context, data *entity.Category) error {
|
||||
err := use.CategoryRepo.Insert(ctx, &entity.Category{
|
||||
Name: data.Name,
|
||||
})
|
||||
if err != nil {
|
||||
return errs.DBErrorL(
|
||||
logx.WithContext(ctx),
|
||||
[]logx.LogField{
|
||||
{Key: "req", Value: data},
|
||||
{Key: "func", Value: "CategoryRepo.Insert"},
|
||||
{Key: "err", Value: err.Error()},
|
||||
},
|
||||
"failed to create category").Wrap(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (use *CategoryUseCase) FindOneByID(ctx context.Context, id string) (*entity.Category, error) {
|
||||
res, err := use.CategoryRepo.FindOneByID(ctx, id)
|
||||
if err != nil {
|
||||
if errors.Is(err, mon.ErrNotFound) {
|
||||
e := errs.ResourceNotFound(
|
||||
"failed to get category",
|
||||
)
|
||||
|
||||
return nil, e
|
||||
}
|
||||
|
||||
e := errs.DBErrorL(
|
||||
logx.WithContext(ctx),
|
||||
[]logx.LogField{
|
||||
{Key: "req", Value: id},
|
||||
{Key: "func", Value: "CategoryRepo.FindOneByID"},
|
||||
{Key: "err", Value: err.Error()},
|
||||
},
|
||||
"failed to get category").Wrap(err)
|
||||
|
||||
return nil, e
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (use *CategoryUseCase) Update(ctx context.Context, id string, data *entity.Category) error {
|
||||
_, err := use.CategoryRepo.Update(ctx, id, &entity.Category{
|
||||
Name: data.Name,
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, mon.ErrNotFound) {
|
||||
e := errs.ResourceNotFound(
|
||||
"failed to update category",
|
||||
)
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
e := errs.DBErrorL(
|
||||
logx.WithContext(ctx),
|
||||
[]logx.LogField{
|
||||
{Key: "req", Value: id},
|
||||
{Key: "func", Value: "CategoryRepo.Update"},
|
||||
{Key: "err", Value: err.Error()},
|
||||
},
|
||||
"failed to update category").Wrap(err)
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (use *CategoryUseCase) Delete(ctx context.Context, id string) error {
|
||||
_, err := use.CategoryRepo.Delete(ctx, id)
|
||||
if err != nil {
|
||||
e := errs.DBErrorL(
|
||||
logx.WithContext(ctx),
|
||||
[]logx.LogField{
|
||||
{Key: "req", Value: id},
|
||||
{Key: "func", Value: "CategoryRepo.Delete"},
|
||||
{Key: "err", Value: err.Error()},
|
||||
},
|
||||
"failed to delete category").Wrap(err)
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (use *CategoryUseCase) ListCategory(ctx context.Context, params usecase.CategoryQueryParams) ([]*entity.Category, int64, error) {
|
||||
category, i, err := use.CategoryRepo.ListCategory(ctx, &repository.CategoryQueryParams{
|
||||
ID: params.ID,
|
||||
PageIndex: params.PageIndex,
|
||||
PageSize: params.PageSize,
|
||||
})
|
||||
if err != nil {
|
||||
e := errs.DBErrorL(
|
||||
logx.WithContext(ctx),
|
||||
[]logx.LogField{
|
||||
{Key: "req", Value: params},
|
||||
{Key: "func", Value: "CategoryRepo.ListCategory"},
|
||||
{Key: "err", Value: err.Error()},
|
||||
},
|
||||
"failed to list category").Wrap(err)
|
||||
|
||||
return nil, 0, e
|
||||
}
|
||||
|
||||
return category, i, nil
|
||||
}
|
|
@ -0,0 +1,414 @@
|
|||
package usecase
|
||||
|
||||
import (
|
||||
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/entity"
|
||||
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/repository"
|
||||
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/usecase"
|
||||
"code.30cm.net/digimon/library-go/errs"
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/stores/mon"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.uber.org/mock/gomock"
|
||||
"testing"
|
||||
|
||||
mockRepository "code.30cm.net/digimon/app-cloudep-product-service/pkg/mock/repository"
|
||||
)
|
||||
|
||||
func TestCategoryUseCase_FindOneByID(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
|
||||
mockCategoryRepo := mockRepository.NewMockCategoryRepository(mockCtrl)
|
||||
|
||||
useCase := MustCategoryUseCase(CategoryUseCaseParam{
|
||||
CategoryRepo: mockCategoryRepo,
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
id := primitive.NewObjectID()
|
||||
tests := []struct {
|
||||
name string
|
||||
id primitive.ObjectID
|
||||
mockSetup func()
|
||||
expectedError error
|
||||
expectedResult *entity.Category
|
||||
}{
|
||||
{
|
||||
name: "成功找到 Category",
|
||||
id: id,
|
||||
mockSetup: func() {
|
||||
mockCategoryRepo.EXPECT().FindOneByID(ctx, id.Hex()).Return(&entity.Category{
|
||||
ID: id,
|
||||
Name: "Test Category",
|
||||
}, nil)
|
||||
},
|
||||
expectedError: nil,
|
||||
expectedResult: &entity.Category{
|
||||
ID: id,
|
||||
Name: "Test Category",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Category 不存在",
|
||||
id: primitive.NewObjectID(),
|
||||
mockSetup: func() {
|
||||
mockCategoryRepo.EXPECT().FindOneByID(ctx, gomock.Any()).Return(nil, mon.ErrNotFound)
|
||||
},
|
||||
expectedError: errs.ResourceNotFound("failed to get category"),
|
||||
expectedResult: nil,
|
||||
},
|
||||
{
|
||||
name: "資料庫錯誤",
|
||||
id: primitive.NewObjectID(),
|
||||
mockSetup: func() {
|
||||
mockCategoryRepo.EXPECT().FindOneByID(ctx, gomock.Any()).Return(nil, errors.New("database error"))
|
||||
},
|
||||
expectedError: errs.DBErrorL(
|
||||
logx.WithContext(ctx),
|
||||
[]logx.LogField{
|
||||
{Key: "req", Value: "db_error_id"},
|
||||
{Key: "func", Value: "CategoryRepo.FindOneByID"},
|
||||
{Key: "err", Value: "database error"},
|
||||
},
|
||||
"failed to get category").Wrap(errors.New("database error")),
|
||||
expectedResult: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.mockSetup()
|
||||
|
||||
result, err := useCase.FindOneByID(ctx, tt.id.Hex())
|
||||
|
||||
if tt.expectedError == nil {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.expectedResult, result)
|
||||
} else {
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, tt.expectedError.Error(), err.Error())
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCategoryUseCase_Update(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
|
||||
mockCategoryRepo := mockRepository.NewMockCategoryRepository(mockCtrl)
|
||||
|
||||
useCase := MustCategoryUseCase(CategoryUseCaseParam{
|
||||
CategoryRepo: mockCategoryRepo,
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
id string
|
||||
data entity.Category
|
||||
mockSetup func()
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "成功更新 Category",
|
||||
id: "valid_id",
|
||||
data: entity.Category{
|
||||
Name: "Updated Category",
|
||||
},
|
||||
mockSetup: func() {
|
||||
mockCategoryRepo.EXPECT().Update(ctx, "valid_id", &entity.Category{
|
||||
Name: "Updated Category",
|
||||
}).Return(nil, nil)
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Category 不存在",
|
||||
id: "non_existing_id",
|
||||
data: entity.Category{
|
||||
Name: "Non-existing Category",
|
||||
},
|
||||
mockSetup: func() {
|
||||
mockCategoryRepo.EXPECT().Update(ctx, "non_existing_id", &entity.Category{
|
||||
Name: "Non-existing Category",
|
||||
}).Return(nil, mon.ErrNotFound)
|
||||
},
|
||||
expectedError: errs.ResourceNotFound(
|
||||
"failed to update category",
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "資料庫錯誤",
|
||||
id: "db_error_id",
|
||||
data: entity.Category{
|
||||
Name: "DB Error Category",
|
||||
},
|
||||
mockSetup: func() {
|
||||
mockCategoryRepo.EXPECT().Update(ctx, "db_error_id", &entity.Category{
|
||||
Name: "DB Error Category",
|
||||
}).Return(nil, errors.New("database error"))
|
||||
},
|
||||
expectedError: errs.DBErrorL(
|
||||
logx.WithContext(ctx),
|
||||
[]logx.LogField{
|
||||
{Key: "req", Value: "db_error_id"},
|
||||
{Key: "func", Value: "CategoryRepo.Update"},
|
||||
{Key: "err", Value: "database error"},
|
||||
},
|
||||
"failed to update category").Wrap(errors.New("database error")),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.mockSetup()
|
||||
|
||||
// 執行測試方法
|
||||
err := useCase.Update(ctx, tt.id, &tt.data)
|
||||
|
||||
// 驗證結果
|
||||
if tt.expectedError == nil {
|
||||
assert.NoError(t, err)
|
||||
} else {
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, tt.expectedError.Error(), err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCategoryUseCase_Insert(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
|
||||
mockCategoryRepo := mockRepository.NewMockCategoryRepository(mockCtrl)
|
||||
|
||||
useCase := MustCategoryUseCase(CategoryUseCaseParam{
|
||||
CategoryRepo: mockCategoryRepo,
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input entity.Category
|
||||
mockSetup func()
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "成功插入 Category",
|
||||
input: entity.Category{
|
||||
Name: "Test Category",
|
||||
},
|
||||
mockSetup: func() {
|
||||
mockCategoryRepo.EXPECT().Insert(ctx, &entity.Category{
|
||||
Name: "Test Category",
|
||||
}).Return(nil)
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "插入失敗 - Database Error",
|
||||
input: entity.Category{
|
||||
Name: "Invalid Category",
|
||||
},
|
||||
mockSetup: func() {
|
||||
mockCategoryRepo.EXPECT().Insert(ctx, &entity.Category{
|
||||
Name: "Invalid Category",
|
||||
}).Return(errors.New("database connection error"))
|
||||
},
|
||||
expectedError: errs.DBErrorL(
|
||||
logx.WithContext(ctx),
|
||||
[]logx.LogField{
|
||||
{Key: "req", Value: entity.Category{Name: "Invalid Category"}},
|
||||
{Key: "func", Value: "CategoryRepo.Insert"},
|
||||
{Key: "err", Value: "database connection error"},
|
||||
},
|
||||
"failed to create category",
|
||||
).Wrap(errors.New("database connection error")),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.mockSetup()
|
||||
|
||||
// 執行測試方法
|
||||
err := useCase.Insert(ctx, &tt.input)
|
||||
|
||||
// 驗證結果
|
||||
if tt.expectedError == nil {
|
||||
assert.NoError(t, err)
|
||||
} else {
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, tt.expectedError.Error(), err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCategoryUseCase_Delete(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
|
||||
mockCategoryRepo := mockRepository.NewMockCategoryRepository(mockCtrl)
|
||||
|
||||
useCase := MustCategoryUseCase(CategoryUseCaseParam{
|
||||
CategoryRepo: mockCategoryRepo,
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
id string
|
||||
mockSetup func()
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "成功刪除 Category",
|
||||
id: "valid_id",
|
||||
mockSetup: func() {
|
||||
mockCategoryRepo.EXPECT().Delete(ctx, gomock.Any()).Return(int64(1), nil)
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "資料庫錯誤",
|
||||
id: "db_error_id",
|
||||
mockSetup: func() {
|
||||
mockCategoryRepo.EXPECT().Delete(ctx, gomock.Any()).Return(int64(0), errors.New("database error"))
|
||||
},
|
||||
expectedError: errs.DBErrorL(
|
||||
logx.WithContext(ctx),
|
||||
[]logx.LogField{
|
||||
{Key: "req", Value: "db_error_id"},
|
||||
{Key: "func", Value: "CategoryRepo.Delete"},
|
||||
{Key: "err", Value: "database error"},
|
||||
},
|
||||
"failed to delete category").Wrap(errors.New("database error")),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.mockSetup()
|
||||
|
||||
// 執行測試方法
|
||||
err := useCase.Delete(ctx, tt.id)
|
||||
|
||||
// 驗證結果
|
||||
if tt.expectedError == nil {
|
||||
assert.NoError(t, err)
|
||||
} else {
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, tt.expectedError.Error(), err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCategoryUseCase_ListCategory(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
|
||||
mockCategoryRepo := mockRepository.NewMockCategoryRepository(mockCtrl)
|
||||
|
||||
useCase := MustCategoryUseCase(CategoryUseCaseParam{
|
||||
CategoryRepo: mockCategoryRepo,
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
id := primitive.NewObjectID()
|
||||
tests := []struct {
|
||||
name string
|
||||
params usecase.CategoryQueryParams
|
||||
mockSetup func()
|
||||
expectedResult []*entity.Category
|
||||
expectedCount int64
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "成功列出 Category",
|
||||
params: usecase.CategoryQueryParams{
|
||||
ID: []string{id.Hex()},
|
||||
PageIndex: 1,
|
||||
PageSize: 10,
|
||||
},
|
||||
mockSetup: func() {
|
||||
mockCategoryRepo.EXPECT().ListCategory(ctx, &repository.CategoryQueryParams{
|
||||
ID: []string{id.Hex()},
|
||||
PageIndex: 1,
|
||||
PageSize: 10,
|
||||
}).Return([]*entity.Category{
|
||||
{
|
||||
ID: id,
|
||||
Name: "Test Category",
|
||||
},
|
||||
}, int64(1), nil)
|
||||
},
|
||||
expectedResult: []*entity.Category{
|
||||
{
|
||||
ID: id,
|
||||
Name: "Test Category",
|
||||
},
|
||||
},
|
||||
expectedCount: int64(1),
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "資料庫錯誤",
|
||||
params: usecase.CategoryQueryParams{
|
||||
ID: []string{"db_error_id"},
|
||||
PageIndex: 1,
|
||||
PageSize: 10,
|
||||
},
|
||||
mockSetup: func() {
|
||||
mockCategoryRepo.EXPECT().ListCategory(ctx, &repository.CategoryQueryParams{
|
||||
ID: []string{"db_error_id"},
|
||||
PageIndex: 1,
|
||||
PageSize: 10,
|
||||
}).Return(nil, int64(0), errors.New("database error"))
|
||||
},
|
||||
expectedResult: nil,
|
||||
expectedCount: int64(0),
|
||||
expectedError: errs.DBErrorL(
|
||||
logx.WithContext(ctx),
|
||||
[]logx.LogField{
|
||||
{Key: "req", Value: usecase.CategoryQueryParams{
|
||||
ID: []string{"db_error_id"},
|
||||
PageIndex: 1,
|
||||
PageSize: 10,
|
||||
}},
|
||||
{Key: "func", Value: "CategoryRepo.ListCategory"},
|
||||
{Key: "err", Value: "database error"},
|
||||
},
|
||||
"failed to list category").Wrap(errors.New("database error")),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.mockSetup()
|
||||
|
||||
// 執行測試方法
|
||||
result, count, err := useCase.ListCategory(ctx, tt.params)
|
||||
|
||||
// 驗證結果
|
||||
assert.Equal(t, tt.expectedResult, result)
|
||||
assert.Equal(t, tt.expectedCount, count)
|
||||
if tt.expectedError == nil {
|
||||
assert.NoError(t, err)
|
||||
} else {
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, tt.expectedError.Error(), err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,552 @@
|
|||
package usecase
|
||||
|
||||
import (
|
||||
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/entity"
|
||||
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/kyc"
|
||||
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/repository"
|
||||
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/usecase"
|
||||
mockRepository "code.30cm.net/digimon/app-cloudep-product-service/pkg/mock/repository"
|
||||
repo "code.30cm.net/digimon/app-cloudep-product-service/pkg/repository"
|
||||
"code.30cm.net/digimon/library-go/errs"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.uber.org/mock/gomock"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestKYCUseCase_Create(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
|
||||
mockKYCRepo := mockRepository.NewMockKYCRepository(mockCtrl)
|
||||
|
||||
useCase := MustKYCUseCase(KYCUseCaseParam{
|
||||
KYCRepo: mockKYCRepo,
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
uid := "test-user"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input *entity.KYC
|
||||
mockSetup func()
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "查不到資料可以建立",
|
||||
input: &entity.KYC{
|
||||
UID: uid,
|
||||
},
|
||||
mockSetup: func() {
|
||||
mockKYCRepo.EXPECT().FindLatestByUID(ctx, uid).Return(nil, repo.ErrNotFound)
|
||||
mockKYCRepo.EXPECT().Create(ctx, gomock.Any()).Return(nil)
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "前一筆為 REJECTED 可建立",
|
||||
input: &entity.KYC{
|
||||
UID: uid,
|
||||
},
|
||||
mockSetup: func() {
|
||||
mockKYCRepo.EXPECT().FindLatestByUID(ctx, uid).Return(&entity.KYC{
|
||||
UID: uid,
|
||||
Status: kyc.StatusREJECTED,
|
||||
}, nil)
|
||||
mockKYCRepo.EXPECT().Create(ctx, gomock.Any()).Return(nil)
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "已存在未駁回資料,禁止建立",
|
||||
input: &entity.KYC{
|
||||
UID: uid,
|
||||
},
|
||||
mockSetup: func() {
|
||||
mockKYCRepo.EXPECT().FindLatestByUID(ctx, uid).Return(&entity.KYC{
|
||||
UID: uid,
|
||||
Status: kyc.StatusPending,
|
||||
}, nil)
|
||||
},
|
||||
expectedError: errs.ForbiddenL(
|
||||
logx.WithContext(ctx),
|
||||
[]logx.LogField{
|
||||
{Key: "param", Value: &entity.KYC{UID: uid}},
|
||||
{Key: "func", Value: "KYCRepo.FindLatestByUID"},
|
||||
{Key: "reason", Value: "KYC already in progress or approved"},
|
||||
},
|
||||
"不能重複送出 KYC 資料",
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "查詢資料庫錯誤",
|
||||
input: &entity.KYC{
|
||||
UID: uid,
|
||||
},
|
||||
mockSetup: func() {
|
||||
mockKYCRepo.EXPECT().FindLatestByUID(ctx, uid).Return(nil, errors.New("database error"))
|
||||
},
|
||||
expectedError: errs.DBErrorL(
|
||||
logx.WithContext(ctx),
|
||||
[]logx.LogField{
|
||||
{Key: "param", Value: &entity.KYC{UID: uid}},
|
||||
{Key: "func", Value: "KYCRepo.FindLatestByUID"},
|
||||
{Key: "err", Value: "database error"},
|
||||
},
|
||||
"failed to get latest kyc",
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "建立時資料庫錯誤",
|
||||
input: &entity.KYC{
|
||||
UID: uid,
|
||||
},
|
||||
mockSetup: func() {
|
||||
mockKYCRepo.EXPECT().FindLatestByUID(ctx, uid).Return(nil, repo.ErrNotFound)
|
||||
mockKYCRepo.EXPECT().Create(ctx, gomock.Any()).Return(errors.New("insert failed"))
|
||||
},
|
||||
expectedError: errs.DBErrorL(
|
||||
logx.WithContext(ctx),
|
||||
[]logx.LogField{
|
||||
{Key: "param", Value: &entity.KYC{UID: uid, Status: kyc.StatusPending}},
|
||||
{Key: "func", Value: "KYCRepo.Create"},
|
||||
{Key: "err", Value: "insert failed"},
|
||||
},
|
||||
"failed to create kyc review",
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.mockSetup()
|
||||
|
||||
err := useCase.Create(ctx, tt.input)
|
||||
|
||||
if tt.expectedError == nil {
|
||||
assert.NoError(t, err)
|
||||
} else {
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, tt.expectedError.Error(), err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestKYCUseCase_FindLatestByUID(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
|
||||
mockKYCRepo := mockRepository.NewMockKYCRepository(mockCtrl)
|
||||
|
||||
useCase := MustKYCUseCase(KYCUseCaseParam{
|
||||
KYCRepo: mockKYCRepo,
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
uid := "test-user"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
inputUID string
|
||||
mockSetup func()
|
||||
expectedResult *entity.KYC
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "查詢成功",
|
||||
inputUID: uid,
|
||||
mockSetup: func() {
|
||||
mockKYCRepo.EXPECT().FindLatestByUID(ctx, uid).Return(&entity.KYC{
|
||||
UID: uid,
|
||||
Status: kyc.StatusPending,
|
||||
}, nil)
|
||||
},
|
||||
expectedResult: &entity.KYC{
|
||||
UID: uid,
|
||||
Status: kyc.StatusPending,
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "資料庫錯誤",
|
||||
inputUID: uid,
|
||||
mockSetup: func() {
|
||||
mockKYCRepo.EXPECT().FindLatestByUID(ctx, uid).Return(nil, errors.New("database error"))
|
||||
},
|
||||
expectedResult: nil,
|
||||
expectedError: errs.DBErrorL(
|
||||
logx.WithContext(ctx),
|
||||
[]logx.LogField{
|
||||
{Key: "uid", Value: uid},
|
||||
{Key: "func", Value: "KYCRepo.FindLatestByUID"},
|
||||
{Key: "err", Value: "database error"},
|
||||
},
|
||||
"failed to get latest kyc",
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.mockSetup()
|
||||
|
||||
result, err := useCase.FindLatestByUID(ctx, tt.inputUID)
|
||||
|
||||
if tt.expectedError == nil {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.expectedResult, result)
|
||||
} else {
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, tt.expectedError.Error(), err.Error())
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestKYCUseCase_FindByID(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
|
||||
mockKYCRepo := mockRepository.NewMockKYCRepository(mockCtrl)
|
||||
|
||||
useCase := MustKYCUseCase(KYCUseCaseParam{
|
||||
KYCRepo: mockKYCRepo,
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
objID := primitive.NewObjectID()
|
||||
idStr := objID.Hex()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
inputID string
|
||||
mockSetup func()
|
||||
expectedResult *entity.KYC
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "查詢成功",
|
||||
inputID: idStr,
|
||||
mockSetup: func() {
|
||||
mockKYCRepo.EXPECT().FindByID(ctx, idStr).Return(&entity.KYC{
|
||||
ID: objID,
|
||||
UID: "user-1",
|
||||
Status: kyc.StatusAPPROVED,
|
||||
}, nil)
|
||||
},
|
||||
expectedResult: &entity.KYC{
|
||||
ID: objID,
|
||||
UID: "user-1",
|
||||
Status: kyc.StatusAPPROVED,
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "資料庫錯誤",
|
||||
inputID: idStr,
|
||||
mockSetup: func() {
|
||||
mockKYCRepo.EXPECT().FindByID(ctx, idStr).Return(nil, errors.New("database error"))
|
||||
},
|
||||
expectedResult: nil,
|
||||
expectedError: errs.DBErrorL(
|
||||
logx.WithContext(ctx),
|
||||
[]logx.LogField{
|
||||
{Key: "id", Value: idStr},
|
||||
{Key: "func", Value: "KYCRepo.FindByID"},
|
||||
{Key: "err", Value: "database error"},
|
||||
},
|
||||
"failed to get kyc",
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.mockSetup()
|
||||
|
||||
result, err := useCase.FindByID(ctx, tt.inputID)
|
||||
|
||||
if tt.expectedError == nil {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.expectedResult, result)
|
||||
} else {
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, tt.expectedError.Error(), err.Error())
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestKYCUseCase_List(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
|
||||
mockKYCRepo := mockRepository.NewMockKYCRepository(mockCtrl)
|
||||
|
||||
useCase := MustKYCUseCase(KYCUseCaseParam{
|
||||
KYCRepo: mockKYCRepo,
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
uid := "user-1"
|
||||
country := "TW"
|
||||
status := kyc.StatusAPPROVED
|
||||
ss := status.ToString()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
inputParams usecase.KYCQueryParams
|
||||
mockSetup func()
|
||||
expectedList []*entity.KYC
|
||||
expectedCount int64
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "查詢成功",
|
||||
inputParams: usecase.KYCQueryParams{
|
||||
PageIndex: 1,
|
||||
PageSize: 10,
|
||||
UID: &uid,
|
||||
Country: &country,
|
||||
Status: &ss,
|
||||
},
|
||||
mockSetup: func() {
|
||||
mockKYCRepo.EXPECT().List(ctx, repository.KYCQueryParams{
|
||||
PageIndex: 1,
|
||||
PageSize: 10,
|
||||
UID: &uid,
|
||||
Country: &country,
|
||||
Status: &ss,
|
||||
SortByDate: true,
|
||||
}).Return([]*entity.KYC{
|
||||
{UID: uid, CountryRegion: country, Status: status},
|
||||
}, int64(1), nil)
|
||||
},
|
||||
expectedList: []*entity.KYC{
|
||||
{UID: uid, CountryRegion: country, Status: status},
|
||||
},
|
||||
expectedCount: 1,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "查詢失敗 - 資料庫錯誤",
|
||||
inputParams: usecase.KYCQueryParams{
|
||||
PageIndex: 2,
|
||||
PageSize: 20,
|
||||
},
|
||||
mockSetup: func() {
|
||||
mockKYCRepo.EXPECT().List(ctx, repository.KYCQueryParams{
|
||||
PageIndex: 2,
|
||||
PageSize: 20,
|
||||
SortByDate: true,
|
||||
}).Return(nil, int64(0), errors.New("database error"))
|
||||
},
|
||||
expectedList: nil,
|
||||
expectedCount: 0,
|
||||
expectedError: errs.DBErrorL(logx.WithContext(ctx),
|
||||
[]logx.LogField{
|
||||
{Key: "params", Value: usecase.KYCQueryParams{
|
||||
PageIndex: 2,
|
||||
PageSize: 20,
|
||||
}},
|
||||
{Key: "func", Value: "KYCRepo.List"},
|
||||
{Key: "err", Value: "database error"},
|
||||
}, "failed to list kyc"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.mockSetup()
|
||||
|
||||
list, count, err := useCase.List(ctx, tt.inputParams)
|
||||
|
||||
assert.Equal(t, tt.expectedList, list)
|
||||
assert.Equal(t, tt.expectedCount, count)
|
||||
|
||||
if tt.expectedError == nil {
|
||||
assert.NoError(t, err)
|
||||
} else {
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, tt.expectedError.Error(), err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestKYCUseCase_UpdateStatus(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
|
||||
mockKYCRepo := mockRepository.NewMockKYCRepository(mockCtrl)
|
||||
|
||||
useCase := MustKYCUseCase(KYCUseCaseParam{
|
||||
KYCRepo: mockKYCRepo,
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
id := primitive.NewObjectID().Hex()
|
||||
status := kyc.StatusAPPROVED
|
||||
reason := "all good"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
id string
|
||||
status kyc.Status
|
||||
reason string
|
||||
mockSetup func()
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "更新成功",
|
||||
id: id,
|
||||
status: status,
|
||||
reason: reason,
|
||||
mockSetup: func() {
|
||||
mockKYCRepo.EXPECT().UpdateStatus(ctx, id, status.ToString(), reason).Return(nil)
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "更新失敗 - DB 錯誤",
|
||||
id: id,
|
||||
status: status,
|
||||
reason: reason,
|
||||
mockSetup: func() {
|
||||
mockKYCRepo.EXPECT().UpdateStatus(ctx, id, status.ToString(), reason).Return(errors.New("db error"))
|
||||
},
|
||||
expectedError: errs.DBErrorL(
|
||||
logx.WithContext(ctx),
|
||||
[]logx.LogField{
|
||||
{Key: "params", Value: fmt.Sprintf("id:%s, status:%s, reason: %s", id, status, reason)},
|
||||
{Key: "func", Value: "KYCRepo.UpdateStatus"},
|
||||
{Key: "err", Value: "db error"},
|
||||
},
|
||||
"failed to update kyc status",
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.mockSetup()
|
||||
|
||||
err := useCase.UpdateStatus(ctx, tt.id, tt.status.ToString(), tt.reason)
|
||||
|
||||
if tt.expectedError == nil {
|
||||
assert.NoError(t, err)
|
||||
} else {
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, tt.expectedError.Error(), err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestKYCUseCase_UpdateKYCInfo(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
|
||||
mockKYCRepo := mockRepository.NewMockKYCRepository(mockCtrl)
|
||||
|
||||
useCase := MustKYCUseCase(KYCUseCaseParam{
|
||||
KYCRepo: mockKYCRepo,
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
id := primitive.NewObjectID().Hex()
|
||||
|
||||
updateParams := &usecase.KYCUpdateParams{
|
||||
Name: proto.String("Daniel Wang"),
|
||||
Identification: proto.String("A123456789"),
|
||||
IdentificationType: proto.String("passport"),
|
||||
Address: proto.String("Taipei City"),
|
||||
PostalCode: proto.String("100"),
|
||||
DateOfBirth: proto.String("1993-04-17"),
|
||||
Gender: proto.String("M"),
|
||||
IDFrontImage: proto.String("https://example.com/front.jpg"),
|
||||
IDBackImage: proto.String("https://example.com/back.jpg"),
|
||||
BankStatementImg: proto.String("https://example.com/bank.jpg"),
|
||||
BankCode: proto.String("123"),
|
||||
BankName: proto.String("ABC Bank"),
|
||||
BranchCode: proto.String("001"),
|
||||
BranchName: proto.String("Taipei Branch"),
|
||||
BankAccount: proto.String("1234567890"),
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
id string
|
||||
input *usecase.KYCUpdateParams
|
||||
mockSetup func()
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "更新成功",
|
||||
id: id,
|
||||
input: updateParams,
|
||||
mockSetup: func() {
|
||||
mockKYCRepo.EXPECT().UpdateKYCInfo(ctx, id, &repository.KYCUpdateParams{
|
||||
Name: updateParams.Name,
|
||||
Identification: updateParams.Identification,
|
||||
IdentificationType: updateParams.IdentificationType,
|
||||
Address: updateParams.Address,
|
||||
PostalCode: updateParams.PostalCode,
|
||||
DateOfBirth: updateParams.DateOfBirth,
|
||||
Gender: updateParams.Gender,
|
||||
IDFrontImage: updateParams.IDFrontImage,
|
||||
IDBackImage: updateParams.IDBackImage,
|
||||
BankStatementImg: updateParams.BankStatementImg,
|
||||
BankCode: updateParams.BankCode,
|
||||
BankName: updateParams.BankName,
|
||||
BranchCode: updateParams.BranchCode,
|
||||
BranchName: updateParams.BranchName,
|
||||
BankAccount: updateParams.BankAccount,
|
||||
}).Return(nil)
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "更新失敗 - DB 錯誤",
|
||||
id: id,
|
||||
input: updateParams,
|
||||
mockSetup: func() {
|
||||
mockKYCRepo.EXPECT().UpdateKYCInfo(ctx, id, gomock.Any()).Return(errors.New("db error"))
|
||||
},
|
||||
expectedError: errs.DBErrorL(logx.WithContext(ctx),
|
||||
[]logx.LogField{
|
||||
{Key: "id", Value: id},
|
||||
{Key: "params", Value: updateParams},
|
||||
{Key: "func", Value: "KYCRepo.UpdateKYCInfo"},
|
||||
{Key: "err", Value: "db error"},
|
||||
},
|
||||
"failed to update kyc",
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.mockSetup()
|
||||
|
||||
err := useCase.UpdateKYCInfo(ctx, tt.id, tt.input)
|
||||
|
||||
if tt.expectedError == nil {
|
||||
assert.NoError(t, err)
|
||||
} else {
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, tt.expectedError.Error(), err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,640 @@
|
|||
package usecase
|
||||
|
||||
import (
|
||||
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/entity"
|
||||
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/repository"
|
||||
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/usecase"
|
||||
mockRepository "code.30cm.net/digimon/app-cloudep-product-service/pkg/mock/repository"
|
||||
repo "code.30cm.net/digimon/app-cloudep-product-service/pkg/repository"
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"go.uber.org/mock/gomock"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestProductUseCase_Create(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
|
||||
mockProductRepo := mockRepository.NewMockProductRepository(mockCtrl)
|
||||
mockStatsRepo := mockRepository.NewMockProductStatisticsRepo(mockCtrl)
|
||||
mockTagBinding := mockRepository.NewMockTagBindingRepo(mockCtrl)
|
||||
|
||||
useCase := MustProductUseCase(ProductUseCaseParam{
|
||||
ProductRepo: mockProductRepo,
|
||||
ProductStatisticsRepo: mockStatsRepo,
|
||||
TagBinding: mockTagBinding,
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
input := &usecase.Product{
|
||||
UID: ptr("user-1"),
|
||||
Title: ptr("Test Product"),
|
||||
ShortTitle: ptr("Short"),
|
||||
Details: ptr("Details here"),
|
||||
ShortDescription: ptr("Desc"),
|
||||
Media: []usecase.Media{{Sort: 1, Type: "image", URL: "http://image"}},
|
||||
Slug: ptr("test-product"),
|
||||
IsPublished: ptr(true),
|
||||
Amount: 1000,
|
||||
StartTime: ptr("2025-04-04T00:00:00Z"),
|
||||
EndTime: ptr("2025-04-10T00:00:00Z"),
|
||||
Category: ptr("education"),
|
||||
CustomFields: []usecase.CustomFields{{Key: "Level", Value: "Beginner"}},
|
||||
Tags: []string{"t1", "t2"},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
mockSetup func()
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "建立成功",
|
||||
mockSetup: func() {
|
||||
mockProductRepo.EXPECT().Transaction(gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
DoAndReturn(func(ctx context.Context, fn func(sessCtx mongo.SessionContext) (any, error), opts ...*options.TransactionOptions) error {
|
||||
// ✅ 這邊用 gomock.Any() 模擬 sessCtx
|
||||
mockProductRepo.EXPECT().Insert(gomock.Any(), gomock.Any()).Return(nil)
|
||||
mockStatsRepo.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil)
|
||||
mockTagBinding.EXPECT().BindTags(gomock.Any(), gomock.Any()).Return(nil)
|
||||
_, err := fn(nil)
|
||||
return err
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "插入失敗",
|
||||
mockSetup: func() {
|
||||
mockProductRepo.EXPECT().Transaction(gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
DoAndReturn(func(ctx context.Context, fn func(sessCtx mongo.SessionContext) (any, error), opts ...*options.TransactionOptions) error {
|
||||
mockProductRepo.EXPECT().Insert(gomock.Any(), gomock.Any()).Return(errors.New("failed to create product"))
|
||||
_, err := fn(nil)
|
||||
return err
|
||||
})
|
||||
},
|
||||
expectedError: "failed to create product",
|
||||
},
|
||||
{
|
||||
name: "統計建立失敗",
|
||||
mockSetup: func() {
|
||||
mockProductRepo.EXPECT().Transaction(gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
DoAndReturn(func(ctx context.Context, fn func(sessCtx mongo.SessionContext) (any, error), opts ...*options.TransactionOptions) error {
|
||||
mockProductRepo.EXPECT().Insert(gomock.Any(), gomock.Any()).Return(nil)
|
||||
mockStatsRepo.EXPECT().Create(gomock.Any(), gomock.Any()).Return(errors.New("failed to create product statistics"))
|
||||
_, err := fn(nil)
|
||||
return err
|
||||
})
|
||||
},
|
||||
expectedError: "failed to create product statistics",
|
||||
},
|
||||
{
|
||||
name: "綁定標籤失敗",
|
||||
mockSetup: func() {
|
||||
mockProductRepo.EXPECT().Transaction(gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
DoAndReturn(func(ctx context.Context, fn func(sessCtx mongo.SessionContext) (any, error), opts ...*options.TransactionOptions) error {
|
||||
mockProductRepo.EXPECT().Insert(gomock.Any(), gomock.Any()).Return(nil)
|
||||
mockStatsRepo.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil)
|
||||
mockTagBinding.EXPECT().BindTags(gomock.Any(), gomock.Any()).Return(errors.New("failed to bind product tags"))
|
||||
_, err := fn(nil)
|
||||
return err
|
||||
})
|
||||
},
|
||||
expectedError: "failed to bind product tags",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.mockSetup()
|
||||
err := useCase.Create(ctx, input)
|
||||
if tt.expectedError == "" {
|
||||
assert.NoError(t, err)
|
||||
} else {
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), tt.expectedError)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProductUseCase_IncOrders(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
|
||||
mockStatsRepo := mockRepository.NewMockProductStatisticsRepo(mockCtrl)
|
||||
useCase := MustProductUseCase(ProductUseCaseParam{
|
||||
ProductStatisticsRepo: mockStatsRepo,
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
productID := primitive.NewObjectID().Hex()
|
||||
|
||||
t.Run("成功增加訂單", func(t *testing.T) {
|
||||
mockStatsRepo.EXPECT().IncOrders(ctx, productID, int64(2)).Return(nil)
|
||||
err := useCase.IncOrders(ctx, productID, 2)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("資料庫錯誤", func(t *testing.T) {
|
||||
mockStatsRepo.EXPECT().IncOrders(ctx, productID, int64(1)).Return(errors.New("db error"))
|
||||
err := useCase.IncOrders(ctx, productID, 1)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to inc order")
|
||||
})
|
||||
}
|
||||
|
||||
func TestProductUseCase_DecOrders(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
|
||||
mockStatsRepo := mockRepository.NewMockProductStatisticsRepo(mockCtrl)
|
||||
useCase := MustProductUseCase(ProductUseCaseParam{
|
||||
ProductStatisticsRepo: mockStatsRepo,
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
productID := primitive.NewObjectID().Hex()
|
||||
|
||||
t.Run("成功減少訂單", func(t *testing.T) {
|
||||
mockStatsRepo.EXPECT().DecOrders(ctx, productID, int64(3)).Return(nil)
|
||||
err := useCase.DecOrders(ctx, productID, 3)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("資料庫錯誤", func(t *testing.T) {
|
||||
mockStatsRepo.EXPECT().DecOrders(ctx, productID, int64(1)).Return(errors.New("db error"))
|
||||
err := useCase.DecOrders(ctx, productID, 1)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to dec order")
|
||||
})
|
||||
}
|
||||
|
||||
func TestProductUseCase_UpdateAverageRating(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
|
||||
mockStatsRepo := mockRepository.NewMockProductStatisticsRepo(mockCtrl)
|
||||
useCase := MustProductUseCase(ProductUseCaseParam{
|
||||
ProductStatisticsRepo: mockStatsRepo,
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
productID := primitive.NewObjectID().Hex()
|
||||
|
||||
t.Run("成功更新評價", func(t *testing.T) {
|
||||
mockStatsRepo.EXPECT().UpdateAverageRating(ctx, productID, 4.5).Return(nil)
|
||||
err := useCase.UpdateAverageRating(ctx, productID, 4.5)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("資料庫錯誤", func(t *testing.T) {
|
||||
mockStatsRepo.EXPECT().UpdateAverageRating(ctx, productID, 3.0).Return(errors.New("db error"))
|
||||
err := useCase.UpdateAverageRating(ctx, productID, 3.0)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to update average rating")
|
||||
})
|
||||
}
|
||||
|
||||
func TestProductUseCase_IncFansCount(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
|
||||
mockStatsRepo := mockRepository.NewMockProductStatisticsRepo(mockCtrl)
|
||||
useCase := MustProductUseCase(ProductUseCaseParam{
|
||||
ProductStatisticsRepo: mockStatsRepo,
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
productID := primitive.NewObjectID().Hex()
|
||||
|
||||
t.Run("成功增加追蹤人數", func(t *testing.T) {
|
||||
mockStatsRepo.EXPECT().IncFansCount(ctx, productID, uint64(10)).Return(nil)
|
||||
err := useCase.IncFansCount(ctx, productID, 10)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("資料庫錯誤", func(t *testing.T) {
|
||||
mockStatsRepo.EXPECT().IncFansCount(ctx, productID, uint64(5)).Return(errors.New("db error"))
|
||||
err := useCase.IncFansCount(ctx, productID, 5)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to inc fans count")
|
||||
})
|
||||
}
|
||||
|
||||
func TestProductUseCase_DecFansCount(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
|
||||
mockStatsRepo := mockRepository.NewMockProductStatisticsRepo(mockCtrl)
|
||||
useCase := MustProductUseCase(ProductUseCaseParam{
|
||||
ProductStatisticsRepo: mockStatsRepo,
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
productID := primitive.NewObjectID().Hex()
|
||||
|
||||
t.Run("成功減少追蹤人數", func(t *testing.T) {
|
||||
mockStatsRepo.EXPECT().DecFansCount(ctx, productID, uint64(7)).Return(nil)
|
||||
err := useCase.DecFansCount(ctx, productID, 7)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("資料庫錯誤", func(t *testing.T) {
|
||||
mockStatsRepo.EXPECT().DecFansCount(ctx, productID, uint64(2)).Return(errors.New("db error"))
|
||||
err := useCase.DecFansCount(ctx, productID, 2)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to dec fans count")
|
||||
})
|
||||
}
|
||||
|
||||
func TestProductUseCase_Update(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
|
||||
mockProductRepo := mockRepository.NewMockProductRepository(mockCtrl)
|
||||
mockTagBinding := mockRepository.NewMockTagBindingRepo(mockCtrl)
|
||||
|
||||
useCase := MustProductUseCase(ProductUseCaseParam{
|
||||
ProductRepo: mockProductRepo,
|
||||
TagBinding: mockTagBinding,
|
||||
})
|
||||
ctx := context.Background()
|
||||
id := primitive.NewObjectID().Hex()
|
||||
product := &usecase.Product{
|
||||
Title: ptr("Updated Product"),
|
||||
Tags: []string{"t1", "t2"},
|
||||
}
|
||||
t.Run("更新成功", func(t *testing.T) {
|
||||
mockProductRepo.EXPECT().Transaction(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(
|
||||
func(ctx context.Context, fn func(sessCtx mongo.SessionContext) (any, error), opts ...*options.TransactionOptions) error {
|
||||
mockProductRepo.EXPECT().Update(gomock.Any(), id, gomock.Any()).Return(nil, nil)
|
||||
mockTagBinding.EXPECT().UnbindTagByReferenceID(gomock.Any(), id).Return(nil)
|
||||
mockTagBinding.EXPECT().BindTags(gomock.Any(), gomock.Any()).Return(nil)
|
||||
_, err := fn(nil)
|
||||
return err
|
||||
},
|
||||
)
|
||||
err := useCase.Update(ctx, id, product)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("更新失敗 - ProductRepo", func(t *testing.T) {
|
||||
mockProductRepo.EXPECT().Transaction(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(
|
||||
func(ctx context.Context, fn func(sessCtx mongo.SessionContext) (any, error), opts ...*options.TransactionOptions) error {
|
||||
mockProductRepo.EXPECT().Update(gomock.Any(), id, gomock.Any()).Return(nil, errors.New("update failed"))
|
||||
_, err := fn(nil)
|
||||
return err
|
||||
},
|
||||
)
|
||||
err := useCase.Update(ctx, id, product)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to update product")
|
||||
})
|
||||
|
||||
t.Run("解除綁定失敗", func(t *testing.T) {
|
||||
mockProductRepo.EXPECT().Transaction(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(
|
||||
func(ctx context.Context, fn func(sessCtx mongo.SessionContext) (any, error), opts ...*options.TransactionOptions) error {
|
||||
mockProductRepo.EXPECT().Update(gomock.Any(), id, gomock.Any()).Return(nil, nil)
|
||||
mockTagBinding.EXPECT().UnbindTagByReferenceID(gomock.Any(), id).Return(errors.New("unbind error"))
|
||||
_, err := fn(nil)
|
||||
return err
|
||||
},
|
||||
)
|
||||
err := useCase.Update(ctx, id, product)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to unbind tags")
|
||||
})
|
||||
|
||||
t.Run("綁定失敗", func(t *testing.T) {
|
||||
mockProductRepo.EXPECT().Transaction(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(
|
||||
func(ctx context.Context, fn func(sessCtx mongo.SessionContext) (any, error), opts ...*options.TransactionOptions) error {
|
||||
mockProductRepo.EXPECT().Update(gomock.Any(), id, gomock.Any()).Return(nil, nil)
|
||||
mockTagBinding.EXPECT().UnbindTagByReferenceID(gomock.Any(), id).Return(nil)
|
||||
mockTagBinding.EXPECT().BindTags(gomock.Any(), gomock.Any()).Return(errors.New("bind error"))
|
||||
_, err := fn(nil)
|
||||
return err
|
||||
},
|
||||
)
|
||||
err := useCase.Update(ctx, id, product)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to bind tags")
|
||||
})
|
||||
}
|
||||
|
||||
// ========================= Delete =========================
|
||||
|
||||
func TestProductUseCase_Delete(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
mockProductRepo := mockRepository.NewMockProductRepository(mockCtrl)
|
||||
mockTagRepo := mockRepository.NewMockTagRepo(mockCtrl)
|
||||
useCase := MustProductUseCase(ProductUseCaseParam{
|
||||
ProductRepo: mockProductRepo,
|
||||
TagRepo: mockTagRepo,
|
||||
})
|
||||
ctx := context.Background()
|
||||
id := primitive.NewObjectID().Hex()
|
||||
t.Run("刪除成功", func(t *testing.T) {
|
||||
mockProductRepo.EXPECT().Transaction(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(
|
||||
func(ctx context.Context, fn func(sessCtx mongo.SessionContext) (any, error), opts ...*options.TransactionOptions) error {
|
||||
mockProductRepo.EXPECT().Delete(gomock.Any(), id).Return(nil)
|
||||
mockTagRepo.EXPECT().UnbindTagByReferenceID(gomock.Any(), id).Return(nil)
|
||||
_, err := fn(nil)
|
||||
return err
|
||||
},
|
||||
)
|
||||
err := useCase.Delete(ctx, id)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("刪除失敗", func(t *testing.T) {
|
||||
mockProductRepo.EXPECT().Transaction(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(
|
||||
func(ctx context.Context, fn func(sessCtx mongo.SessionContext) (any, error), opts ...*options.TransactionOptions) error {
|
||||
mockProductRepo.EXPECT().Delete(gomock.Any(), id).Return(errors.New("delete error"))
|
||||
_, err := fn(nil)
|
||||
return err
|
||||
},
|
||||
)
|
||||
err := useCase.Delete(ctx, id)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to delete product")
|
||||
})
|
||||
|
||||
t.Run("解除綁定失敗", func(t *testing.T) {
|
||||
mockProductRepo.EXPECT().Transaction(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(
|
||||
func(ctx context.Context, fn func(sessCtx mongo.SessionContext) (any, error), opts ...*options.TransactionOptions) error {
|
||||
mockProductRepo.EXPECT().Delete(gomock.Any(), id).Return(nil)
|
||||
mockTagRepo.EXPECT().UnbindTagByReferenceID(gomock.Any(), id).Return(errors.New("unbind tag error"))
|
||||
_, err := fn(nil)
|
||||
return err
|
||||
},
|
||||
)
|
||||
err := useCase.Delete(ctx, id)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to unbind tags")
|
||||
})
|
||||
}
|
||||
|
||||
// ========================= Get =========================
|
||||
|
||||
func TestProductUseCase_Get(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
mockProductRepo := mockRepository.NewMockProductRepository(mockCtrl)
|
||||
mockStatsRepo := mockRepository.NewMockProductStatisticsRepo(mockCtrl)
|
||||
mockTagRepo := mockRepository.NewMockTagRepo(mockCtrl)
|
||||
|
||||
useCase := MustProductUseCase(ProductUseCaseParam{
|
||||
ProductRepo: mockProductRepo,
|
||||
ProductStatisticsRepo: mockStatsRepo,
|
||||
TagRepo: mockTagRepo,
|
||||
})
|
||||
ctx := context.Background()
|
||||
id := primitive.NewObjectID()
|
||||
t.Run("查詢成功", func(t *testing.T) {
|
||||
mockProductRepo.EXPECT().FindOneByID(ctx, id.Hex()).Return(&entity.Product{
|
||||
ID: id,
|
||||
UID: "u1",
|
||||
Title: "test",
|
||||
IsPublished: true,
|
||||
Media: []entity.Media{},
|
||||
CustomFields: []entity.CustomFields{},
|
||||
CreatedAt: time.Now().Unix(),
|
||||
UpdatedAt: time.Now().Unix(),
|
||||
}, nil)
|
||||
mockStatsRepo.EXPECT().GetByID(ctx, id.Hex()).Return(&entity.ProductStatistics{}, nil)
|
||||
mockTagRepo.EXPECT().GetBindingsByReference(ctx, id.Hex()).Return([]*entity.TagsBindingTable{}, nil)
|
||||
mockTagRepo.EXPECT().GetByIDs(ctx, gomock.Any()).Return([]*entity.Tags{}, nil)
|
||||
resp, err := useCase.Get(ctx, id.Hex())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "u1", resp.UID)
|
||||
})
|
||||
|
||||
t.Run("查詢失敗 - ProductRepo", func(t *testing.T) {
|
||||
mockProductRepo.EXPECT().FindOneByID(ctx, id.Hex()).Return(nil, errors.New("db error"))
|
||||
resp, err := useCase.Get(ctx, id.Hex())
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, resp)
|
||||
})
|
||||
}
|
||||
|
||||
// ========================= Tag Binding =========================
|
||||
|
||||
func TestProductUseCase_BindTag(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
mockProductRepo := mockRepository.NewMockProductRepository(mockCtrl)
|
||||
mockTagRepo := mockRepository.NewMockTagRepo(mockCtrl)
|
||||
mockTagBinding := mockRepository.NewMockTagBindingRepo(mockCtrl)
|
||||
|
||||
useCase := MustProductUseCase(ProductUseCaseParam{
|
||||
ProductRepo: mockProductRepo,
|
||||
TagRepo: mockTagRepo,
|
||||
TagBinding: mockTagBinding,
|
||||
})
|
||||
ctx := context.Background()
|
||||
binding := usecase.TagsBindingTable{
|
||||
ReferenceID: "pid-123",
|
||||
TagID: "tag-123",
|
||||
}
|
||||
|
||||
t.Run("成功綁定", func(t *testing.T) {
|
||||
mockTagRepo.EXPECT().GetByID(ctx, binding.TagID).Return(&entity.Tags{}, nil)
|
||||
mockProductRepo.EXPECT().FindOneByID(ctx, binding.ReferenceID).Return(&entity.Product{}, nil)
|
||||
mockTagBinding.EXPECT().BindTags(ctx, gomock.Any()).Return(nil)
|
||||
err := useCase.BindTag(ctx, binding)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("Tag 不存在", func(t *testing.T) {
|
||||
mockTagRepo.EXPECT().GetByID(ctx, binding.TagID).Return(nil, repo.ErrNotFound)
|
||||
err := useCase.BindTag(ctx, binding)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to get tags")
|
||||
})
|
||||
|
||||
t.Run("Product 不存在", func(t *testing.T) {
|
||||
mockTagRepo.EXPECT().GetByID(ctx, binding.TagID).Return(&entity.Tags{}, nil)
|
||||
mockProductRepo.EXPECT().FindOneByID(ctx, binding.ReferenceID).Return(nil, repo.ErrNotFound)
|
||||
err := useCase.BindTag(ctx, binding)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to get tags")
|
||||
})
|
||||
|
||||
t.Run("綁定錯誤", func(t *testing.T) {
|
||||
mockTagRepo.EXPECT().GetByID(ctx, binding.TagID).Return(&entity.Tags{}, nil)
|
||||
mockProductRepo.EXPECT().FindOneByID(ctx, binding.ReferenceID).Return(&entity.Product{}, nil)
|
||||
mockTagBinding.EXPECT().BindTags(ctx, gomock.Any()).Return(errors.New("db error"))
|
||||
err := useCase.BindTag(ctx, binding)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to bind tags")
|
||||
})
|
||||
}
|
||||
|
||||
func TestProductUseCase_UnbindTag(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
mockProductRepo := mockRepository.NewMockProductRepository(mockCtrl)
|
||||
mockTagRepo := mockRepository.NewMockTagRepo(mockCtrl)
|
||||
mockTagBinding := mockRepository.NewMockTagBindingRepo(mockCtrl)
|
||||
|
||||
useCase := MustProductUseCase(ProductUseCaseParam{
|
||||
ProductRepo: mockProductRepo,
|
||||
TagRepo: mockTagRepo,
|
||||
TagBinding: mockTagBinding,
|
||||
})
|
||||
ctx := context.Background()
|
||||
binding := usecase.TagsBindingTable{
|
||||
ReferenceID: "pid-123",
|
||||
TagID: "tag-123",
|
||||
}
|
||||
|
||||
t.Run("成功解除綁定", func(t *testing.T) {
|
||||
mockTagRepo.EXPECT().GetByID(ctx, binding.TagID).Return(&entity.Tags{}, nil)
|
||||
mockProductRepo.EXPECT().FindOneByID(ctx, binding.ReferenceID).Return(&entity.Product{}, nil)
|
||||
mockTagBinding.EXPECT().UnbindTag(ctx, binding.TagID, binding.ReferenceID).Return(nil)
|
||||
err := useCase.UnbindTag(ctx, binding)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("解除失敗", func(t *testing.T) {
|
||||
mockTagRepo.EXPECT().GetByID(ctx, binding.TagID).Return(&entity.Tags{}, nil)
|
||||
mockProductRepo.EXPECT().FindOneByID(ctx, binding.ReferenceID).Return(&entity.Product{}, nil)
|
||||
mockTagBinding.EXPECT().UnbindTag(ctx, binding.TagID, binding.ReferenceID).Return(errors.New("unbind error"))
|
||||
err := useCase.UnbindTag(ctx, binding)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed to unbind tags")
|
||||
})
|
||||
}
|
||||
|
||||
func TestProductUseCase_GetBindingsByReference(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
mockTagBinding := mockRepository.NewMockTagBindingRepo(mockCtrl)
|
||||
useCase := MustProductUseCase(ProductUseCaseParam{
|
||||
TagBinding: mockTagBinding,
|
||||
})
|
||||
ctx := context.Background()
|
||||
refID := "ref-123"
|
||||
|
||||
t.Run("查詢成功", func(t *testing.T) {
|
||||
now := time.Now().Unix()
|
||||
mockTagBinding.EXPECT().GetBindingsByReference(ctx, refID).Return([]*entity.TagsBindingTable{
|
||||
{
|
||||
ID: primitive.NewObjectID(),
|
||||
ReferenceID: refID,
|
||||
TagID: "tag-1",
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
},
|
||||
}, nil)
|
||||
resp, err := useCase.GetBindingsByReference(ctx, refID)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, resp, 1)
|
||||
})
|
||||
|
||||
t.Run("查詢失敗", func(t *testing.T) {
|
||||
mockTagBinding.EXPECT().GetBindingsByReference(ctx, refID).Return(nil, errors.New("db error"))
|
||||
resp, err := useCase.GetBindingsByReference(ctx, refID)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, resp)
|
||||
})
|
||||
}
|
||||
|
||||
func TestProductUseCase_ListTagBinding(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
mockTagBinding := mockRepository.NewMockTagBindingRepo(mockCtrl)
|
||||
useCase := MustProductUseCase(ProductUseCaseParam{
|
||||
TagBinding: mockTagBinding,
|
||||
})
|
||||
ctx := context.Background()
|
||||
params := usecase.TagBindingQueryParams{
|
||||
ReferenceID: ptr("r1"),
|
||||
TagID: ptr("t1"),
|
||||
PageSize: 10,
|
||||
PageIndex: 1,
|
||||
}
|
||||
|
||||
t.Run("查詢成功", func(t *testing.T) {
|
||||
now := time.Now().Unix()
|
||||
mockTagBinding.EXPECT().ListTagBinding(ctx, repository.TagBindingQueryParams{
|
||||
ReferenceID: params.ReferenceID,
|
||||
TagID: params.TagID,
|
||||
PageSize: params.PageSize,
|
||||
PageIndex: params.PageIndex,
|
||||
}).Return([]*entity.TagsBindingTable{
|
||||
{
|
||||
ID: primitive.NewObjectID(),
|
||||
ReferenceID: *params.ReferenceID,
|
||||
TagID: *params.TagID,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
},
|
||||
}, int64(1), nil)
|
||||
|
||||
resp, total, err := useCase.ListTagBinding(ctx, params)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, resp, 1)
|
||||
assert.Equal(t, int64(1), total)
|
||||
})
|
||||
|
||||
t.Run("查詢失敗", func(t *testing.T) {
|
||||
mockTagBinding.EXPECT().ListTagBinding(ctx, repository.TagBindingQueryParams{
|
||||
ReferenceID: params.ReferenceID,
|
||||
TagID: params.TagID,
|
||||
PageSize: params.PageSize,
|
||||
PageIndex: params.PageIndex,
|
||||
}).Return(nil, int64(0), errors.New("db error"))
|
||||
|
||||
resp, total, err := useCase.ListTagBinding(ctx, params)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, resp)
|
||||
assert.Equal(t, int64(0), total)
|
||||
})
|
||||
}
|
||||
|
||||
// ========================= List =========================
|
||||
|
||||
func TestProductUseCase_List(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
mockProductRepo := mockRepository.NewMockProductRepository(mockCtrl)
|
||||
mockStatsRepo := mockRepository.NewMockProductStatisticsRepo(mockCtrl)
|
||||
mockTagRepo := mockRepository.NewMockTagRepo(mockCtrl)
|
||||
|
||||
useCase := MustProductUseCase(ProductUseCaseParam{
|
||||
ProductRepo: mockProductRepo,
|
||||
ProductStatisticsRepo: mockStatsRepo,
|
||||
TagRepo: mockTagRepo,
|
||||
})
|
||||
ctx := context.Background()
|
||||
id := primitive.NewObjectID()
|
||||
mockProductRepo.EXPECT().ListProduct(ctx, &repository.ProductQueryParams{
|
||||
PageIndex: 1,
|
||||
PageSize: 10,
|
||||
}).Return([]*entity.Product{{ID: id, UID: "u1", Title: "T", CreatedAt: time.Now().Unix(), UpdatedAt: time.Now().Unix()}}, int64(1), nil)
|
||||
mockStatsRepo.EXPECT().GetByID(ctx, id.Hex()).Return(&entity.ProductStatistics{}, nil)
|
||||
mockTagRepo.EXPECT().GetBindingsByReference(ctx, id.Hex()).Return([]*entity.TagsBindingTable{}, nil)
|
||||
mockTagRepo.EXPECT().GetByIDs(ctx, gomock.Any()).Return([]*entity.Tags{}, nil)
|
||||
|
||||
list, count, err := useCase.List(ctx, usecase.ProductQueryParams{
|
||||
PageIndex: 1,
|
||||
PageSize: 10,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(1), count)
|
||||
assert.Len(t, list, 1)
|
||||
}
|
||||
|
||||
// 工具:指標轉換
|
||||
func ptr[T any](v T) *T {
|
||||
return &v
|
||||
}
|
Loading…
Reference in New Issue