2025-04-05 14:29:53 +00:00
|
|
|
package usecase
|
|
|
|
|
|
|
|
import (
|
2025-04-06 02:08:46 +00:00
|
|
|
"context"
|
|
|
|
"errors"
|
|
|
|
"testing"
|
|
|
|
|
2025-04-05 14:29:53 +00:00
|
|
|
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/entity"
|
|
|
|
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/product"
|
|
|
|
"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"
|
|
|
|
"github.com/shopspring/decimal"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
|
|
"go.uber.org/mock/gomock"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestProductItemUseCase_Create(t *testing.T) {
|
|
|
|
mockCtrl := gomock.NewController(t)
|
|
|
|
defer mockCtrl.Finish()
|
|
|
|
|
|
|
|
mockProductItemRepo := mockRepository.NewMockProductItemRepository(mockCtrl)
|
|
|
|
|
|
|
|
useCase := MustProductItemUseCase(ProductItemUseCaseParam{
|
|
|
|
ProductItems: mockProductItemRepo,
|
|
|
|
})
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
input := &usecase.ProductItems{
|
|
|
|
ReferenceID: "p-id",
|
|
|
|
Name: "Item A",
|
|
|
|
Description: "desc",
|
|
|
|
ShortDescription: "short",
|
|
|
|
IsUnLimit: false,
|
|
|
|
IsFree: false,
|
|
|
|
Stock: 100,
|
|
|
|
Price: "123.45",
|
|
|
|
SKU: "SKU001",
|
|
|
|
TimeSeries: product.TimeSeriesTenMinutes,
|
|
|
|
Status: product.StatusUnderReview,
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Run("建立成功", func(t *testing.T) {
|
|
|
|
mockProductItemRepo.EXPECT().Insert(ctx, gomock.Any()).Return(nil)
|
|
|
|
err := useCase.Create(ctx, input)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("轉換失敗 - 價格格式錯誤", func(t *testing.T) {
|
|
|
|
badInput := *input
|
|
|
|
badInput.Price = "invalid-price"
|
|
|
|
err := useCase.Create(ctx, &badInput)
|
|
|
|
assert.Error(t, err)
|
|
|
|
assert.Contains(t, err.Error(), "failed to change use case into entity")
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("建立失敗 - DB 錯誤", func(t *testing.T) {
|
|
|
|
mockProductItemRepo.EXPECT().Insert(ctx, gomock.Any()).Return(errors.New("db error"))
|
|
|
|
err := useCase.Create(ctx, input)
|
|
|
|
assert.Error(t, err)
|
|
|
|
assert.Contains(t, err.Error(), "failed to insert product item")
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestProductItemUseCase_Get(t *testing.T) {
|
|
|
|
mockCtrl := gomock.NewController(t)
|
|
|
|
defer mockCtrl.Finish()
|
|
|
|
|
|
|
|
mockProductItemRepo := mockRepository.NewMockProductItemRepository(mockCtrl)
|
|
|
|
|
|
|
|
useCase := MustProductItemUseCase(ProductItemUseCaseParam{
|
|
|
|
ProductItems: mockProductItemRepo,
|
|
|
|
})
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
id := primitive.NewObjectID().Hex()
|
|
|
|
|
|
|
|
ent := &entity.ProductItems{
|
|
|
|
ID: primitive.NewObjectID(),
|
|
|
|
ReferenceID: "ref-id",
|
|
|
|
Name: "Item A",
|
|
|
|
Description: "desc",
|
|
|
|
ShortDescription: "short",
|
|
|
|
IsUnLimit: true,
|
|
|
|
IsFree: false,
|
|
|
|
Stock: 50,
|
|
|
|
Price: decimal.NewFromInt(999),
|
|
|
|
SKU: "sku-01",
|
|
|
|
TimeSeries: product.TimeSeriesHalfHour,
|
|
|
|
Status: product.StatusUnderReview,
|
|
|
|
SalesCount: 10,
|
|
|
|
UpdatedAt: 1700000000,
|
|
|
|
CreatedAt: 1600000000,
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Run("查詢成功", func(t *testing.T) {
|
|
|
|
mockProductItemRepo.EXPECT().FindByID(ctx, id).Return(ent, nil)
|
|
|
|
resp, err := useCase.Get(ctx, id)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, ent.Name, resp.Name)
|
|
|
|
assert.Equal(t, ent.Price.String(), resp.Price)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("查詢失敗 - DB 錯誤", func(t *testing.T) {
|
|
|
|
mockProductItemRepo.EXPECT().FindByID(ctx, id).Return(nil, errors.New("db error"))
|
|
|
|
resp, err := useCase.Get(ctx, id)
|
|
|
|
assert.Error(t, err)
|
|
|
|
assert.Nil(t, resp)
|
|
|
|
assert.Contains(t, err.Error(), "failed to create product items")
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestProductItemUseCase_Update(t *testing.T) {
|
|
|
|
mockCtrl := gomock.NewController(t)
|
|
|
|
defer mockCtrl.Finish()
|
|
|
|
|
|
|
|
mockRepo := mockRepository.NewMockProductItemRepository(mockCtrl)
|
|
|
|
useCase := MustProductItemUseCase(ProductItemUseCaseParam{
|
|
|
|
ProductItems: mockRepo,
|
|
|
|
})
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
id := primitive.NewObjectID().Hex()
|
|
|
|
|
|
|
|
t.Run("更新成功", func(t *testing.T) {
|
|
|
|
stock := uint64(100)
|
|
|
|
price := "123.45"
|
|
|
|
param := &usecase.UpdateProductItems{
|
|
|
|
Name: ptr("New Name"),
|
|
|
|
Stock: &stock,
|
|
|
|
Price: &price,
|
|
|
|
IsFree: ptr(true),
|
|
|
|
}
|
|
|
|
mockRepo.EXPECT().Update(ctx, id, gomock.Any()).Return(nil)
|
|
|
|
|
|
|
|
err := useCase.Update(ctx, id, param)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("更新失敗 - 價格格式錯誤", func(t *testing.T) {
|
|
|
|
price := "invalid-price"
|
|
|
|
param := &usecase.UpdateProductItems{
|
|
|
|
Price: &price,
|
|
|
|
}
|
|
|
|
err := useCase.Update(ctx, id, param)
|
|
|
|
assert.Error(t, err)
|
|
|
|
assert.Contains(t, err.Error(), "failed to convert price")
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("更新失敗 - DB 錯誤", func(t *testing.T) {
|
|
|
|
price := "456.78"
|
|
|
|
param := &usecase.UpdateProductItems{
|
|
|
|
Price: &price,
|
|
|
|
}
|
|
|
|
mockRepo.EXPECT().Update(ctx, id, gomock.Any()).Return(errors.New("db error"))
|
|
|
|
err := useCase.Update(ctx, id, param)
|
|
|
|
assert.Error(t, err)
|
|
|
|
assert.Contains(t, err.Error(), "failed to update product item")
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestProductItemUseCase_IncSalesCount(t *testing.T) {
|
|
|
|
mockCtrl := gomock.NewController(t)
|
|
|
|
defer mockCtrl.Finish()
|
|
|
|
|
|
|
|
mockItemRepo := mockRepository.NewMockProductItemRepository(mockCtrl)
|
|
|
|
useCase := MustProductItemUseCase(ProductItemUseCaseParam{
|
|
|
|
ProductItems: mockItemRepo,
|
|
|
|
})
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
id := "item-123"
|
|
|
|
saleCount := uint64(10)
|
|
|
|
|
|
|
|
t.Run("成功增加銷售數量", func(t *testing.T) {
|
|
|
|
mockItemRepo.EXPECT().IncSalesCount(ctx, id, int64(saleCount)).Return(nil)
|
|
|
|
err := useCase.IncSalesCount(ctx, id, saleCount)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("資料庫錯誤", func(t *testing.T) {
|
|
|
|
mockItemRepo.EXPECT().IncSalesCount(ctx, id, int64(saleCount)).Return(errors.New("db error"))
|
|
|
|
err := useCase.IncSalesCount(ctx, id, saleCount)
|
|
|
|
assert.Error(t, err)
|
|
|
|
assert.Contains(t, err.Error(), "failed to insert product item")
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestProductItemUseCase_DecSalesCount(t *testing.T) {
|
|
|
|
mockCtrl := gomock.NewController(t)
|
|
|
|
defer mockCtrl.Finish()
|
|
|
|
|
|
|
|
mockItemRepo := mockRepository.NewMockProductItemRepository(mockCtrl)
|
|
|
|
useCase := MustProductItemUseCase(ProductItemUseCaseParam{
|
|
|
|
ProductItems: mockItemRepo,
|
|
|
|
})
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
id := "item-123"
|
|
|
|
saleCount := uint64(5)
|
|
|
|
|
|
|
|
t.Run("成功減少銷售數量", func(t *testing.T) {
|
|
|
|
mockItemRepo.EXPECT().DecSalesCount(ctx, id, int64(saleCount)).Return(nil)
|
|
|
|
err := useCase.DecSalesCount(ctx, id, saleCount)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("資料庫錯誤", func(t *testing.T) {
|
|
|
|
mockItemRepo.EXPECT().DecSalesCount(ctx, id, int64(saleCount)).Return(errors.New("db error"))
|
|
|
|
err := useCase.DecSalesCount(ctx, id, saleCount)
|
|
|
|
assert.Error(t, err)
|
|
|
|
assert.Contains(t, err.Error(), "failed to Dec product item")
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestProductItemUseCase_Delete(t *testing.T) {
|
|
|
|
mockCtrl := gomock.NewController(t)
|
|
|
|
defer mockCtrl.Finish()
|
|
|
|
|
|
|
|
mockItemRepo := mockRepository.NewMockProductItemRepository(mockCtrl)
|
|
|
|
useCase := MustProductItemUseCase(ProductItemUseCaseParam{
|
|
|
|
ProductItems: mockItemRepo,
|
|
|
|
})
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
id := "item-456"
|
|
|
|
|
|
|
|
t.Run("刪除成功", func(t *testing.T) {
|
|
|
|
mockItemRepo.EXPECT().Delete(ctx, []string{id}).Return(nil)
|
|
|
|
err := useCase.Delete(ctx, id)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("資料庫錯誤", func(t *testing.T) {
|
|
|
|
mockItemRepo.EXPECT().Delete(ctx, []string{id}).Return(errors.New("db error"))
|
|
|
|
err := useCase.Delete(ctx, id)
|
|
|
|
assert.Error(t, err)
|
|
|
|
assert.Contains(t, err.Error(), "failed to delete product item")
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestProductItemUseCase_List(t *testing.T) {
|
|
|
|
mockCtrl := gomock.NewController(t)
|
|
|
|
defer mockCtrl.Finish()
|
|
|
|
|
|
|
|
mockItemRepo := mockRepository.NewMockProductItemRepository(mockCtrl)
|
|
|
|
useCase := MustProductItemUseCase(ProductItemUseCaseParam{
|
|
|
|
ProductItems: mockItemRepo,
|
|
|
|
})
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
refID := "ref-123"
|
|
|
|
params := usecase.QueryProductItemParam{
|
|
|
|
PageSize: 10,
|
|
|
|
PageIndex: 1,
|
|
|
|
ReferenceID: &refID,
|
|
|
|
}
|
|
|
|
repoParams := repository.ProductItemQueryParams{
|
|
|
|
PageSize: 10,
|
|
|
|
PageIndex: 1,
|
|
|
|
ReferenceID: &refID,
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Run("成功列出項目", func(t *testing.T) {
|
|
|
|
mockItemRepo.EXPECT().ListProductItem(ctx, repoParams).Return([]entity.ProductItems{{ReferenceID: refID, Name: "Item A"}}, int64(1), nil)
|
|
|
|
items, total, err := useCase.List(ctx, params)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, int64(1), total)
|
|
|
|
assert.Equal(t, 1, len(items))
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("查詢失敗 - DB 錯誤", func(t *testing.T) {
|
|
|
|
mockItemRepo.EXPECT().ListProductItem(ctx, repoParams).Return(nil, int64(0), errors.New("db error"))
|
|
|
|
items, total, err := useCase.List(ctx, params)
|
|
|
|
assert.Error(t, err)
|
|
|
|
assert.Nil(t, items)
|
|
|
|
assert.Equal(t, int64(0), total)
|
|
|
|
assert.Contains(t, err.Error(), "failed to list product item")
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestProductItemUseCase_UpdateStatus(t *testing.T) {
|
|
|
|
mockCtrl := gomock.NewController(t)
|
|
|
|
defer mockCtrl.Finish()
|
|
|
|
|
|
|
|
mockRepo := mockRepository.NewMockProductItemRepository(mockCtrl)
|
|
|
|
useCase := MustProductItemUseCase(ProductItemUseCaseParam{
|
|
|
|
ProductItems: mockRepo,
|
|
|
|
})
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
id := primitive.NewObjectID().Hex()
|
|
|
|
status := product.StatusUnderReview
|
|
|
|
|
|
|
|
t.Run("更新成功", func(t *testing.T) {
|
|
|
|
mockRepo.EXPECT().UpdateStatus(ctx, id, status).Return(nil)
|
|
|
|
|
|
|
|
err := useCase.UpdateStatus(ctx, id, status)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("更新失敗 - 資料庫錯誤", func(t *testing.T) {
|
|
|
|
mockRepo.EXPECT().UpdateStatus(ctx, id, status).Return(errors.New("db error"))
|
|
|
|
|
|
|
|
err := useCase.UpdateStatus(ctx, id, status)
|
|
|
|
assert.Error(t, err)
|
|
|
|
assert.Contains(t, err.Error(), "failed to update product item status")
|
|
|
|
})
|
|
|
|
}
|