app-cloudep-product-service/pkg/usecase/product_test.go

642 lines
22 KiB
Go
Raw Permalink Normal View History

2025-04-04 09:33:48 +00:00
package usecase
import (
2025-04-06 02:08:46 +00:00
"context"
"errors"
"testing"
"time"
2025-04-04 09:33:48 +00:00
"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"
"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"
)
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
}