642 lines
22 KiB
Go
642 lines
22 KiB
Go
package usecase
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"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
|
|
}
|