mirror of
https://github.com/3ybactuk/marketplace-go-service-project.git
synced 2025-10-30 14:03:45 +03:00
779 lines
15 KiB
Go
779 lines
15 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/gojuno/minimock/v3"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/goleak"
|
|
|
|
"route256/cart/internal/domain/entity"
|
|
"route256/cart/internal/domain/model"
|
|
"route256/cart/internal/domain/service/mock"
|
|
)
|
|
|
|
const (
|
|
validPrice = 123
|
|
validName = "some product name"
|
|
)
|
|
|
|
type productServiceFake struct{}
|
|
|
|
func (f *productServiceFake) GetProductBySku(_ context.Context, sku entity.Sku) (*model.Product, error) {
|
|
if sku%2 == 0 {
|
|
return nil, errors.New("empty shelf")
|
|
}
|
|
|
|
return &model.Product{
|
|
Name: validName,
|
|
Price: validPrice,
|
|
Sku: sku,
|
|
}, nil
|
|
}
|
|
|
|
func (f *productServiceFake) GetProducts(ctx context.Context, skus []entity.Sku) ([]*model.Product, error) {
|
|
res := make([]*model.Product, len(skus))
|
|
|
|
for i, sku := range skus {
|
|
prod, err := f.GetProductBySku(ctx, sku)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
res[i] = prod
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
type lomsServiceFake struct{}
|
|
|
|
func (f *lomsServiceFake) StocksInfo(_ context.Context, sku entity.Sku) (uint32, error) {
|
|
if sku == 1111 {
|
|
return 0, errors.New("stock error")
|
|
}
|
|
|
|
return 100, nil
|
|
}
|
|
|
|
func (f *lomsServiceFake) OrderCreate(_ context.Context, cart *model.Cart) (int64, error) {
|
|
if cart.UserID == 1111 {
|
|
return 0, errors.New("order create error")
|
|
}
|
|
|
|
return 1234, nil
|
|
}
|
|
|
|
func TestMain(m *testing.M) {
|
|
goleak.VerifyTestMain(m)
|
|
}
|
|
|
|
func TestCartService_AddItem(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := context.Background()
|
|
mc := minimock.NewController(t)
|
|
|
|
testSKU := entity.Sku(199)
|
|
testItem := model.Item{
|
|
Product: &model.Product{
|
|
Name: validName,
|
|
Price: 123,
|
|
Sku: testSKU,
|
|
},
|
|
Count: 1,
|
|
}
|
|
|
|
testSKULomsFailing := entity.Sku(1111)
|
|
testItemLomsFailing := model.Item{
|
|
Product: &model.Product{
|
|
Name: validName,
|
|
Price: validPrice,
|
|
Sku: testSKULomsFailing,
|
|
},
|
|
Count: 1,
|
|
}
|
|
|
|
type fields struct {
|
|
repository Repository
|
|
productService ProductService
|
|
lomsService LomsService
|
|
}
|
|
type args struct {
|
|
ctx context.Context
|
|
item *model.Item
|
|
userID entity.UID
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
wantErr require.ErrorAssertionFunc
|
|
}{
|
|
{
|
|
name: "success",
|
|
fields: fields{
|
|
repository: mock.NewRepositoryMock(mc).
|
|
AddItemMock.
|
|
Expect(ctx, 1337, &testItem).
|
|
Return(nil),
|
|
productService: &productServiceFake{},
|
|
lomsService: &lomsServiceFake{},
|
|
},
|
|
args: args{
|
|
ctx: ctx,
|
|
item: &testItem,
|
|
userID: 1337,
|
|
},
|
|
wantErr: require.NoError,
|
|
},
|
|
{
|
|
name: "invalid item",
|
|
fields: fields{
|
|
repository: nil,
|
|
productService: nil,
|
|
},
|
|
args: args{
|
|
ctx: ctx,
|
|
item: &model.Item{
|
|
Product: &model.Product{
|
|
Name: "name",
|
|
Price: 123,
|
|
Sku: 0,
|
|
},
|
|
Count: 0,
|
|
},
|
|
userID: 0,
|
|
},
|
|
wantErr: require.Error,
|
|
},
|
|
{
|
|
name: "invalid user id",
|
|
fields: fields{
|
|
repository: nil,
|
|
productService: nil,
|
|
},
|
|
args: args{
|
|
ctx: ctx,
|
|
item: &testItem,
|
|
userID: 0,
|
|
},
|
|
wantErr: require.Error,
|
|
},
|
|
{
|
|
name: "product service error",
|
|
fields: fields{
|
|
repository: nil,
|
|
productService: &productServiceFake{},
|
|
lomsService: &lomsServiceFake{},
|
|
},
|
|
args: args{
|
|
ctx: ctx,
|
|
item: &model.Item{
|
|
Product: &model.Product{
|
|
Name: "",
|
|
Price: 0,
|
|
Sku: 4,
|
|
},
|
|
Count: 1,
|
|
},
|
|
userID: 1337,
|
|
},
|
|
wantErr: require.Error,
|
|
},
|
|
{
|
|
name: "repository error",
|
|
fields: fields{
|
|
repository: mock.NewRepositoryMock(mc).
|
|
AddItemMock.
|
|
Expect(ctx, 1337, &testItem).
|
|
Return(assert.AnError),
|
|
productService: &productServiceFake{},
|
|
lomsService: &lomsServiceFake{},
|
|
},
|
|
args: args{
|
|
ctx: ctx,
|
|
item: &testItem,
|
|
userID: 1337,
|
|
},
|
|
wantErr: require.Error,
|
|
},
|
|
{
|
|
name: "stocks acquiring error",
|
|
fields: fields{
|
|
repository: nil,
|
|
productService: &productServiceFake{},
|
|
lomsService: &lomsServiceFake{},
|
|
},
|
|
args: args{
|
|
ctx: ctx,
|
|
item: &testItemLomsFailing,
|
|
userID: 1337,
|
|
},
|
|
wantErr: require.Error,
|
|
},
|
|
{
|
|
name: "not enough stocks",
|
|
fields: fields{
|
|
repository: nil,
|
|
productService: &productServiceFake{},
|
|
lomsService: &lomsServiceFake{},
|
|
},
|
|
args: args{
|
|
ctx: ctx,
|
|
item: &model.Item{
|
|
Product: &model.Product{
|
|
Name: validName,
|
|
Price: validPrice,
|
|
Sku: testSKU,
|
|
},
|
|
Count: 10000,
|
|
},
|
|
userID: 1337,
|
|
},
|
|
wantErr: require.Error,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
s := &CartService{
|
|
repository: tt.fields.repository,
|
|
productService: tt.fields.productService,
|
|
lomsService: tt.fields.lomsService,
|
|
}
|
|
|
|
err := s.AddItem(tt.args.ctx, tt.args.userID, tt.args.item)
|
|
tt.wantErr(t, err, "check add review error")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCartService_GetItemsByUserID(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := context.Background()
|
|
mc := minimock.NewController(t)
|
|
|
|
testUID := entity.UID(1337)
|
|
testSKU := entity.Sku(199)
|
|
testModelCart := model.Cart{
|
|
UserID: testUID,
|
|
Items: []*model.Item{
|
|
{
|
|
Product: &model.Product{
|
|
Name: validName,
|
|
Price: validPrice,
|
|
Sku: testSKU,
|
|
},
|
|
Count: 1,
|
|
},
|
|
},
|
|
TotalPrice: validPrice,
|
|
}
|
|
|
|
testEntityCart := entity.Cart{
|
|
UserID: testUID,
|
|
Items: []entity.Sku{testSKU},
|
|
ItemCount: map[entity.Sku]uint32{testSKU: 1},
|
|
}
|
|
|
|
type fields struct {
|
|
repository Repository
|
|
productService ProductService
|
|
}
|
|
type args struct {
|
|
ctx context.Context
|
|
userID entity.UID
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
want *model.Cart
|
|
wantErr require.ErrorAssertionFunc
|
|
}{
|
|
{
|
|
name: "success 1 item",
|
|
fields: fields{
|
|
repository: mock.NewRepositoryMock(mc).
|
|
GetItemsByUserIDMock.
|
|
Expect(ctx, testUID).
|
|
Return(testEntityCart, nil),
|
|
productService: &productServiceFake{},
|
|
},
|
|
args: args{
|
|
ctx: ctx,
|
|
userID: testUID,
|
|
},
|
|
want: &testModelCart,
|
|
wantErr: require.NoError,
|
|
},
|
|
{
|
|
name: "success 2 items",
|
|
fields: fields{
|
|
repository: mock.NewRepositoryMock(mc).
|
|
GetItemsByUserIDMock.
|
|
Expect(ctx, testUID).
|
|
Return(entity.Cart{
|
|
UserID: testUID,
|
|
Items: []entity.Sku{testSKU},
|
|
ItemCount: map[entity.Sku]uint32{testSKU: 2},
|
|
}, nil),
|
|
productService: &productServiceFake{},
|
|
},
|
|
args: args{
|
|
ctx: ctx,
|
|
userID: testUID,
|
|
},
|
|
want: &model.Cart{
|
|
UserID: testUID,
|
|
Items: []*model.Item{
|
|
{
|
|
Product: &model.Product{
|
|
Name: validName,
|
|
Price: validPrice,
|
|
Sku: testSKU,
|
|
},
|
|
Count: 2,
|
|
},
|
|
},
|
|
TotalPrice: validPrice * 2,
|
|
},
|
|
wantErr: require.NoError,
|
|
},
|
|
{
|
|
name: "success 2 different items",
|
|
fields: fields{
|
|
repository: mock.NewRepositoryMock(mc).
|
|
GetItemsByUserIDMock.
|
|
Expect(ctx, testUID).
|
|
Return(entity.Cart{
|
|
UserID: testUID,
|
|
Items: []entity.Sku{testSKU, 1},
|
|
ItemCount: map[entity.Sku]uint32{testSKU: 1, 1: 1},
|
|
}, nil),
|
|
productService: &productServiceFake{},
|
|
},
|
|
args: args{
|
|
ctx: ctx,
|
|
userID: testUID,
|
|
},
|
|
want: &model.Cart{
|
|
UserID: testUID,
|
|
Items: []*model.Item{
|
|
{
|
|
Product: &model.Product{
|
|
Name: validName,
|
|
Price: validPrice,
|
|
Sku: 1,
|
|
},
|
|
Count: 1,
|
|
},
|
|
{
|
|
Product: &model.Product{
|
|
Name: validName,
|
|
Price: validPrice,
|
|
Sku: testSKU,
|
|
},
|
|
Count: 1,
|
|
},
|
|
},
|
|
TotalPrice: validPrice * 2,
|
|
},
|
|
wantErr: require.NoError,
|
|
},
|
|
{
|
|
name: "invalid user id",
|
|
fields: fields{
|
|
repository: nil,
|
|
productService: nil,
|
|
},
|
|
args: args{
|
|
ctx: ctx,
|
|
userID: 0,
|
|
},
|
|
want: nil,
|
|
wantErr: require.Error,
|
|
},
|
|
{
|
|
name: "repository error",
|
|
fields: fields{
|
|
repository: mock.NewRepositoryMock(mc).
|
|
GetItemsByUserIDMock.
|
|
Expect(ctx, testUID).
|
|
Return(entity.Cart{}, assert.AnError),
|
|
productService: nil,
|
|
},
|
|
args: args{
|
|
ctx: ctx,
|
|
userID: testUID,
|
|
},
|
|
want: nil,
|
|
wantErr: require.Error,
|
|
},
|
|
{
|
|
name: "empty cart",
|
|
fields: fields{
|
|
repository: mock.NewRepositoryMock(mc).
|
|
GetItemsByUserIDMock.
|
|
Expect(ctx, testUID).
|
|
Return(entity.Cart{}, nil),
|
|
productService: nil,
|
|
},
|
|
args: args{
|
|
ctx: ctx,
|
|
userID: testUID,
|
|
},
|
|
want: nil,
|
|
wantErr: require.Error,
|
|
},
|
|
{
|
|
name: "product service error",
|
|
fields: fields{
|
|
repository: mock.NewRepositoryMock(mc).
|
|
GetItemsByUserIDMock.
|
|
Expect(ctx, testUID).
|
|
Return(entity.Cart{
|
|
UserID: testUID,
|
|
Items: []entity.Sku{2},
|
|
ItemCount: map[entity.Sku]uint32{2: 1},
|
|
}, nil),
|
|
productService: &productServiceFake{},
|
|
},
|
|
args: args{
|
|
ctx: ctx,
|
|
userID: testUID,
|
|
},
|
|
want: nil,
|
|
wantErr: require.Error,
|
|
},
|
|
{
|
|
name: "product service error",
|
|
fields: fields{
|
|
repository: mock.NewRepositoryMock(mc).
|
|
GetItemsByUserIDMock.
|
|
Expect(ctx, testUID).
|
|
Return(entity.Cart{
|
|
UserID: testUID,
|
|
Items: []entity.Sku{2},
|
|
ItemCount: map[entity.Sku]uint32{2: 1},
|
|
}, nil),
|
|
productService: &productServiceFake{},
|
|
},
|
|
args: args{
|
|
ctx: ctx,
|
|
userID: testUID,
|
|
},
|
|
want: nil,
|
|
wantErr: require.Error,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
s := &CartService{
|
|
repository: tt.fields.repository,
|
|
productService: tt.fields.productService,
|
|
}
|
|
|
|
got, err := s.GetItemsByUserID(tt.args.ctx, tt.args.userID)
|
|
|
|
tt.wantErr(t, err, "check add review error")
|
|
|
|
assert.True(t, assert.EqualExportedValues(t, tt.want, got), "got unexpected cart")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCartService_DeleteItem(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := context.Background()
|
|
mc := minimock.NewController(t)
|
|
|
|
testSKU := entity.Sku(199)
|
|
testUID := entity.UID(1337)
|
|
|
|
type fields struct {
|
|
repository Repository
|
|
}
|
|
type args struct {
|
|
ctx context.Context
|
|
userID entity.UID
|
|
sku entity.Sku
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
wantErr require.ErrorAssertionFunc
|
|
}{
|
|
{
|
|
name: "success",
|
|
fields: fields{
|
|
repository: mock.NewRepositoryMock(mc).
|
|
DeleteItemMock.
|
|
Expect(ctx, testUID, testSKU).
|
|
Return(nil),
|
|
},
|
|
args: args{
|
|
ctx: ctx,
|
|
userID: testUID,
|
|
sku: testSKU,
|
|
},
|
|
wantErr: require.NoError,
|
|
},
|
|
{
|
|
name: "invalid user id",
|
|
fields: fields{
|
|
repository: nil,
|
|
},
|
|
args: args{
|
|
ctx: ctx,
|
|
userID: 0,
|
|
sku: testSKU,
|
|
},
|
|
wantErr: require.Error,
|
|
},
|
|
{
|
|
name: "invalid sku",
|
|
fields: fields{
|
|
repository: nil,
|
|
},
|
|
args: args{
|
|
ctx: ctx,
|
|
userID: testUID,
|
|
sku: 0,
|
|
},
|
|
wantErr: require.Error,
|
|
},
|
|
{
|
|
name: "repository error",
|
|
fields: fields{
|
|
repository: mock.NewRepositoryMock(mc).
|
|
DeleteItemMock.
|
|
Expect(ctx, testUID, testSKU).
|
|
Return(assert.AnError),
|
|
},
|
|
args: args{
|
|
ctx: ctx,
|
|
sku: testSKU,
|
|
userID: testUID,
|
|
},
|
|
wantErr: require.Error,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
s := &CartService{
|
|
repository: tt.fields.repository,
|
|
}
|
|
|
|
err := s.DeleteItem(tt.args.ctx, tt.args.userID, tt.args.sku)
|
|
tt.wantErr(t, err, "check add review error")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCartService_DeleteItemsByUserID(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := context.Background()
|
|
mc := minimock.NewController(t)
|
|
|
|
testUID := entity.UID(1337)
|
|
|
|
type fields struct {
|
|
repository Repository
|
|
}
|
|
type args struct {
|
|
ctx context.Context
|
|
userID entity.UID
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
wantErr require.ErrorAssertionFunc
|
|
}{
|
|
{
|
|
name: "success",
|
|
fields: fields{
|
|
repository: mock.NewRepositoryMock(mc).
|
|
DeleteItemsByUserIDMock.
|
|
Expect(ctx, testUID).
|
|
Return(nil),
|
|
},
|
|
args: args{
|
|
ctx: ctx,
|
|
userID: testUID,
|
|
},
|
|
wantErr: require.NoError,
|
|
},
|
|
{
|
|
name: "invalid user id",
|
|
fields: fields{
|
|
repository: nil,
|
|
},
|
|
args: args{
|
|
ctx: ctx,
|
|
userID: 0,
|
|
},
|
|
wantErr: require.Error,
|
|
},
|
|
{
|
|
name: "repository error",
|
|
fields: fields{
|
|
repository: mock.NewRepositoryMock(mc).
|
|
DeleteItemsByUserIDMock.
|
|
Expect(ctx, testUID).
|
|
Return(assert.AnError),
|
|
},
|
|
args: args{
|
|
ctx: ctx,
|
|
userID: testUID,
|
|
},
|
|
wantErr: require.Error,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
s := &CartService{
|
|
repository: tt.fields.repository,
|
|
}
|
|
|
|
err := s.DeleteItemsByUserID(tt.args.ctx, tt.args.userID)
|
|
tt.wantErr(t, err, "check add review error")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCartService_CheckoutUserCart(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := context.Background()
|
|
mc := minimock.NewController(t)
|
|
|
|
testSKU := entity.Sku(199)
|
|
testCart := entity.Cart{
|
|
Items: []entity.Sku{testSKU},
|
|
ItemCount: map[entity.Sku]uint32{testSKU: 1},
|
|
}
|
|
|
|
type fields struct {
|
|
repository Repository
|
|
productService ProductService
|
|
lomsService LomsService
|
|
}
|
|
|
|
type args struct {
|
|
ctx context.Context
|
|
userID entity.UID
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
wantOrderID int64
|
|
wantErr require.ErrorAssertionFunc
|
|
}{
|
|
{
|
|
name: "success",
|
|
fields: fields{
|
|
repository: mock.NewRepositoryMock(mc).
|
|
GetItemsByUserIDMock.
|
|
Expect(ctx, 1337).
|
|
Return(testCart, nil).
|
|
DeleteItemsByUserIDMock.
|
|
Expect(ctx, 1337).
|
|
Return(nil),
|
|
productService: &productServiceFake{},
|
|
lomsService: &lomsServiceFake{},
|
|
},
|
|
args: args{ctx: ctx, userID: 1337},
|
|
wantOrderID: 1234,
|
|
wantErr: require.NoError,
|
|
},
|
|
{
|
|
name: "invalid user id",
|
|
fields: fields{},
|
|
args: args{ctx: ctx, userID: 0},
|
|
wantErr: require.Error,
|
|
},
|
|
{
|
|
name: "get cart error",
|
|
fields: fields{
|
|
repository: mock.NewRepositoryMock(mc).
|
|
GetItemsByUserIDMock.
|
|
Expect(ctx, 1337).
|
|
Return(entity.Cart{}, assert.AnError),
|
|
},
|
|
args: args{ctx: ctx, userID: 1337},
|
|
wantErr: require.Error,
|
|
},
|
|
{
|
|
name: "order create error",
|
|
fields: fields{
|
|
repository: mock.NewRepositoryMock(mc).
|
|
GetItemsByUserIDMock.
|
|
Expect(ctx, 1111).
|
|
Return(testCart, nil),
|
|
productService: &productServiceFake{},
|
|
lomsService: &lomsServiceFake{},
|
|
},
|
|
args: args{ctx: ctx, userID: 1111},
|
|
wantErr: require.Error,
|
|
},
|
|
{
|
|
name: "delete order error",
|
|
fields: fields{
|
|
repository: mock.NewRepositoryMock(mc).
|
|
GetItemsByUserIDMock.
|
|
Expect(ctx, 1337).
|
|
Return(testCart, nil).
|
|
DeleteItemsByUserIDMock.
|
|
Expect(ctx, 1337).
|
|
Return(assert.AnError),
|
|
productService: &productServiceFake{},
|
|
lomsService: &lomsServiceFake{},
|
|
},
|
|
args: args{ctx: ctx, userID: 1337},
|
|
wantErr: require.Error,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tt := tt
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
svc := &CartService{
|
|
repository: tt.fields.repository,
|
|
productService: tt.fields.productService,
|
|
lomsService: tt.fields.lomsService,
|
|
}
|
|
|
|
orderID, err := svc.CheckoutUserCart(tt.args.ctx, tt.args.userID)
|
|
tt.wantErr(t, err)
|
|
if err == nil {
|
|
require.Equal(t, tt.wantOrderID, orderID)
|
|
}
|
|
})
|
|
}
|
|
}
|