flamingo.me/flamingo-commerce/v3@v3.11.0/cart/application/cartService_test.go (about)

     1  package application_test
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"testing"
     8  
     9  	"flamingo.me/flamingo/v3/core/auth"
    10  	"github.com/go-test/deep"
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/mock"
    13  	"github.com/stretchr/testify/require"
    14  
    15  	mocks2 "flamingo.me/flamingo-commerce/v3/cart/domain/cart/mocks"
    16  	mocks3 "flamingo.me/flamingo-commerce/v3/cart/domain/validation/mocks"
    17  	"flamingo.me/flamingo-commerce/v3/cart/infrastructure"
    18  	"flamingo.me/flamingo-commerce/v3/product/domain/mocks"
    19  
    20  	"flamingo.me/flamingo-commerce/v3/cart/domain/decorator"
    21  	"flamingo.me/flamingo-commerce/v3/cart/domain/events"
    22  	"flamingo.me/flamingo-commerce/v3/cart/domain/placeorder"
    23  	"flamingo.me/flamingo-commerce/v3/cart/domain/validation"
    24  
    25  	"flamingo.me/flamingo/v3/framework/flamingo"
    26  	"flamingo.me/flamingo/v3/framework/web"
    27  
    28  	cartApplication "flamingo.me/flamingo-commerce/v3/cart/application"
    29  	cartDomain "flamingo.me/flamingo-commerce/v3/cart/domain/cart"
    30  	productDomain "flamingo.me/flamingo-commerce/v3/product/domain"
    31  )
    32  
    33  func TestCartService_DeleteSavedSessionGuestCartID(t *testing.T) {
    34  	type fields struct {
    35  		CartReceiverService *cartApplication.CartReceiverService
    36  		ProductService      productDomain.ProductService
    37  		Logger              flamingo.Logger
    38  		EventPublisher      events.EventPublisher
    39  		EventRouter         flamingo.EventRouter
    40  		RestrictionService  *validation.RestrictionService
    41  		config              *struct {
    42  			DefaultDeliveryCode string `inject:"config:commerce.cart.defaultDeliveryCode,optional"`
    43  			DeleteEmptyDelivery bool   `inject:"config:commerce.cart.deleteEmptyDelivery,optional"`
    44  		}
    45  		DeliveryInfoBuilder cartDomain.DeliveryInfoBuilder
    46  		CartCache           cartApplication.CartCache
    47  		PlaceOrderService   placeorder.Service
    48  	}
    49  	type args struct {
    50  		session *web.Session
    51  	}
    52  	tests := []struct {
    53  		name          string
    54  		fields        fields
    55  		args          args
    56  		wantErr       bool
    57  		valuesCleared bool
    58  	}{
    59  		{
    60  			name: "basic clearing of guest cart session value",
    61  			fields: fields{
    62  				CartReceiverService: func() *cartApplication.CartReceiverService {
    63  					result := &cartApplication.CartReceiverService{}
    64  					result.Inject(
    65  						new(MockGuestCartServiceAdapter),
    66  						new(MockCustomerCartService),
    67  						func() *decorator.DecoratedCartFactory {
    68  							result := &decorator.DecoratedCartFactory{}
    69  							result.Inject(
    70  								&MockProductService{},
    71  								flamingo.NullLogger{},
    72  							)
    73  
    74  							return result
    75  						}(),
    76  						nil,
    77  						flamingo.NullLogger{},
    78  						new(MockEventRouter),
    79  						&struct {
    80  							CartCache cartApplication.CartCache `inject:",optional"`
    81  						}{
    82  							CartCache: new(MockCartCache),
    83  						},
    84  					)
    85  					return result
    86  				}(),
    87  				ProductService: &MockProductService{},
    88  				Logger:         flamingo.NullLogger{},
    89  				EventPublisher: new(MockEventPublisher),
    90  				config: &struct {
    91  					DefaultDeliveryCode string `inject:"config:commerce.cart.defaultDeliveryCode,optional"`
    92  					DeleteEmptyDelivery bool   `inject:"config:commerce.cart.deleteEmptyDelivery,optional"`
    93  				}{
    94  					DefaultDeliveryCode: "default_delivery_code",
    95  					DeleteEmptyDelivery: false,
    96  				},
    97  				DeliveryInfoBuilder: new(MockDeliveryInfoBuilder),
    98  				CartCache:           new(MockCartCache),
    99  			},
   100  			args: args{
   101  				session: web.EmptySession().Store(cartApplication.GuestCartSessionKey, "some_guest_id"),
   102  			},
   103  			wantErr:       false,
   104  			valuesCleared: true,
   105  		},
   106  	}
   107  
   108  	for _, tt := range tests {
   109  		t.Run(tt.name, func(t *testing.T) {
   110  			cs := &cartApplication.CartService{}
   111  			cs.Inject(
   112  				tt.fields.CartReceiverService,
   113  				tt.fields.ProductService,
   114  				tt.fields.EventPublisher,
   115  				tt.fields.EventRouter,
   116  				tt.fields.DeliveryInfoBuilder,
   117  				tt.fields.RestrictionService,
   118  				nil,
   119  				tt.fields.Logger,
   120  				tt.fields.config,
   121  				nil,
   122  			)
   123  
   124  			err := cs.DeleteSavedSessionGuestCartID(tt.args.session)
   125  
   126  			if (err != nil) != tt.wantErr {
   127  				t.Errorf("CartService.DeleteSavedSessionGuestCartID() error = %v, wantErr %v", err, tt.wantErr)
   128  
   129  				return
   130  			}
   131  
   132  			if tt.valuesCleared == true {
   133  				if len(tt.args.session.Keys()) > 0 {
   134  					t.Error("Session Values should be empty, but aren't")
   135  				}
   136  			}
   137  		})
   138  	}
   139  }
   140  
   141  func TestCartService_AdjustItemsToRestrictedQty(t *testing.T) {
   142  	type fields struct {
   143  		CartReceiverService *cartApplication.CartReceiverService
   144  		ProductService      productDomain.ProductService
   145  		Logger              flamingo.Logger
   146  		EventPublisher      *MockEventPublisher
   147  		EventRouter         *MockEventRouter
   148  		RestrictionService  *validation.RestrictionService
   149  		config              *struct {
   150  			DefaultDeliveryCode string `inject:"config:commerce.cart.defaultDeliveryCode,optional"`
   151  			DeleteEmptyDelivery bool   `inject:"config:commerce.cart.deleteEmptyDelivery,optional"`
   152  		}
   153  		DeliveryInfoBuilder cartDomain.DeliveryInfoBuilder
   154  		CartCache           cartApplication.CartCache
   155  		PlaceOrderService   placeorder.Service
   156  	}
   157  	type args struct {
   158  		ctx     context.Context
   159  		session *web.Session
   160  	}
   161  	tests := []struct {
   162  		name   string
   163  		fields fields
   164  		args   args
   165  		want   cartApplication.QtyAdjustmentResults
   166  	}{
   167  		{
   168  			name: "restrictors higher than qty dont reduce qty",
   169  			fields: fields{
   170  				CartReceiverService: func() *cartApplication.CartReceiverService {
   171  					result := &cartApplication.CartReceiverService{}
   172  					result.Inject(
   173  						new(MockGuestCartServiceWithModifyBehaviour),
   174  						new(MockCustomerCartService),
   175  						func() *decorator.DecoratedCartFactory {
   176  							result := &decorator.DecoratedCartFactory{}
   177  							result.Inject(
   178  								&MockProductService{},
   179  								flamingo.NullLogger{},
   180  							)
   181  
   182  							return result
   183  						}(),
   184  						nil,
   185  						flamingo.NullLogger{},
   186  						new(MockEventRouter),
   187  						&struct {
   188  							CartCache cartApplication.CartCache `inject:",optional"`
   189  						}{
   190  							CartCache: new(MockCartWithItemCache),
   191  						},
   192  					)
   193  					return result
   194  				}(),
   195  				ProductService: &MockProductService{},
   196  				Logger:         flamingo.NullLogger{},
   197  				EventRouter:    new(MockEventRouter),
   198  				EventPublisher: new(MockEventPublisher),
   199  				config: &struct {
   200  					DefaultDeliveryCode string `inject:"config:commerce.cart.defaultDeliveryCode,optional"`
   201  					DeleteEmptyDelivery bool   `inject:"config:commerce.cart.deleteEmptyDelivery,optional"`
   202  				}{
   203  					DefaultDeliveryCode: "default_delivery_code",
   204  					DeleteEmptyDelivery: false,
   205  				},
   206  				DeliveryInfoBuilder: new(MockDeliveryInfoBuilder),
   207  				CartCache:           new(MockCartWithItemCache),
   208  				RestrictionService: func() *validation.RestrictionService {
   209  					rs := &validation.RestrictionService{}
   210  					rs.Inject(
   211  						[]validation.MaxQuantityRestrictor{
   212  							&MockRestrictor{IsRestricted: true, MaxQty: 10, DifferenceQty: 0},
   213  						},
   214  					)
   215  					return rs
   216  				}(),
   217  			},
   218  			args: args{
   219  				ctx:     context.Background(),
   220  				session: web.EmptySession().Store(cartApplication.GuestCartSessionKey, "some_guest_id"),
   221  			},
   222  			want: cartApplication.QtyAdjustmentResults{},
   223  		},
   224  		{
   225  			name: "restrictors lower than qty reduce qty",
   226  			fields: fields{
   227  				CartReceiverService: func() *cartApplication.CartReceiverService {
   228  					result := &cartApplication.CartReceiverService{}
   229  					result.Inject(
   230  						new(MockGuestCartServiceWithModifyBehaviour),
   231  						new(MockCustomerCartService),
   232  						func() *decorator.DecoratedCartFactory {
   233  							result := &decorator.DecoratedCartFactory{}
   234  							result.Inject(
   235  								&MockProductService{},
   236  								flamingo.NullLogger{},
   237  							)
   238  
   239  							return result
   240  						}(),
   241  						nil,
   242  						flamingo.NullLogger{},
   243  						new(MockEventRouter),
   244  						&struct {
   245  							CartCache cartApplication.CartCache `inject:",optional"`
   246  						}{
   247  							CartCache: new(MockCartWithItemCache),
   248  						},
   249  					)
   250  					return result
   251  				}(),
   252  				ProductService: &MockProductService{},
   253  				Logger:         flamingo.NullLogger{},
   254  				EventRouter:    new(MockEventRouter),
   255  				EventPublisher: new(MockEventPublisher),
   256  				config: &struct {
   257  					DefaultDeliveryCode string `inject:"config:commerce.cart.defaultDeliveryCode,optional"`
   258  					DeleteEmptyDelivery bool   `inject:"config:commerce.cart.deleteEmptyDelivery,optional"`
   259  				}{
   260  					DefaultDeliveryCode: "default_delivery_code",
   261  					DeleteEmptyDelivery: false,
   262  				},
   263  				DeliveryInfoBuilder: new(MockDeliveryInfoBuilder),
   264  				CartCache:           new(MockCartWithItemCache),
   265  				RestrictionService: func() *validation.RestrictionService {
   266  					rs := &validation.RestrictionService{}
   267  					rs.Inject(
   268  						[]validation.MaxQuantityRestrictor{
   269  							&MockRestrictor{IsRestricted: true, MaxQty: 5, DifferenceQty: -2},
   270  						},
   271  					)
   272  					return rs
   273  				}(),
   274  			},
   275  			args: args{
   276  				ctx:     context.Background(),
   277  				session: web.EmptySession().Store(cartApplication.GuestCartSessionKey, "some_guest_id"),
   278  			},
   279  			want: cartApplication.QtyAdjustmentResults{
   280  				cartApplication.QtyAdjustmentResult{
   281  					OriginalItem: cartDomain.Item{
   282  						ID:  "mock_item",
   283  						Qty: 7,
   284  					},
   285  					DeliveryCode: "default_delivery_code",
   286  					WasDeleted:   false,
   287  					RestrictionResult: &validation.RestrictionResult{
   288  						IsRestricted:        true,
   289  						MaxAllowed:          5,
   290  						RemainingDifference: -2,
   291  						RestrictorName:      "",
   292  					},
   293  					NewQty: 5,
   294  				},
   295  			},
   296  		},
   297  		{
   298  			name: "maxAllowed of 0 deletes item",
   299  			fields: fields{
   300  				CartReceiverService: func() *cartApplication.CartReceiverService {
   301  					result := &cartApplication.CartReceiverService{}
   302  					result.Inject(
   303  						new(MockGuestCartServiceWithModifyBehaviour),
   304  						new(MockCustomerCartService),
   305  						func() *decorator.DecoratedCartFactory {
   306  							result := &decorator.DecoratedCartFactory{}
   307  							result.Inject(
   308  								&MockProductService{},
   309  								flamingo.NullLogger{},
   310  							)
   311  
   312  							return result
   313  						}(),
   314  						nil,
   315  						flamingo.NullLogger{},
   316  						new(MockEventRouter),
   317  						&struct {
   318  							CartCache cartApplication.CartCache `inject:",optional"`
   319  						}{
   320  							CartCache: new(MockCartWithItemCache),
   321  						},
   322  					)
   323  					return result
   324  				}(),
   325  				ProductService: &MockProductService{},
   326  				Logger:         flamingo.NullLogger{},
   327  				EventRouter:    new(MockEventRouter),
   328  				EventPublisher: new(MockEventPublisher),
   329  				config: &struct {
   330  					DefaultDeliveryCode string `inject:"config:commerce.cart.defaultDeliveryCode,optional"`
   331  					DeleteEmptyDelivery bool   `inject:"config:commerce.cart.deleteEmptyDelivery,optional"`
   332  				}{
   333  					DefaultDeliveryCode: "default_delivery_code",
   334  					DeleteEmptyDelivery: false,
   335  				},
   336  				DeliveryInfoBuilder: new(MockDeliveryInfoBuilder),
   337  				CartCache:           new(MockCartWithItemCache),
   338  				RestrictionService: func() *validation.RestrictionService {
   339  					rs := &validation.RestrictionService{}
   340  					rs.Inject(
   341  						[]validation.MaxQuantityRestrictor{
   342  							&MockRestrictor{IsRestricted: true, MaxQty: 0, DifferenceQty: -7},
   343  						},
   344  					)
   345  					return rs
   346  				}(),
   347  			},
   348  			args: args{
   349  				ctx:     context.Background(),
   350  				session: web.EmptySession().Store(cartApplication.GuestCartSessionKey, "some_guest_id"),
   351  			},
   352  			want: cartApplication.QtyAdjustmentResults{
   353  				cartApplication.QtyAdjustmentResult{
   354  					OriginalItem: cartDomain.Item{
   355  						ID:  "mock_item",
   356  						Qty: 7,
   357  					},
   358  					DeliveryCode: "default_delivery_code",
   359  					WasDeleted:   true,
   360  					RestrictionResult: &validation.RestrictionResult{
   361  						IsRestricted:        true,
   362  						MaxAllowed:          0,
   363  						RemainingDifference: -7,
   364  						RestrictorName:      "",
   365  					},
   366  					NewQty: 0,
   367  				},
   368  			},
   369  		},
   370  	}
   371  
   372  	for _, tt := range tests {
   373  		t.Run(tt.name, func(t *testing.T) {
   374  			cs := &cartApplication.CartService{}
   375  			cs.Inject(
   376  				tt.fields.CartReceiverService,
   377  				tt.fields.ProductService,
   378  				tt.fields.EventPublisher,
   379  				tt.fields.EventRouter,
   380  				tt.fields.DeliveryInfoBuilder,
   381  				tt.fields.RestrictionService,
   382  				nil,
   383  				tt.fields.Logger,
   384  				tt.fields.config,
   385  				nil,
   386  			)
   387  			got, _ := cs.AdjustItemsToRestrictedQty(tt.args.ctx, tt.args.session)
   388  
   389  			if diff := deep.Equal(got, tt.want); diff != nil {
   390  				t.Errorf("CartService.AdjustItemsToRestrictedQty() got!=want, diff: %#v", diff)
   391  			}
   392  
   393  		})
   394  	}
   395  }
   396  
   397  func TestCartService_ReserveOrderIDAndSave(t *testing.T) {
   398  	type fields struct {
   399  		CartReceiverService *cartApplication.CartReceiverService
   400  		ProductService      productDomain.ProductService
   401  		Logger              flamingo.Logger
   402  		EventPublisher      events.EventPublisher
   403  		EventRouter         flamingo.EventRouter
   404  		RestrictionService  *validation.RestrictionService
   405  		config              *struct {
   406  			DefaultDeliveryCode string `inject:"config:commerce.cart.defaultDeliveryCode,optional"`
   407  			DeleteEmptyDelivery bool   `inject:"config:commerce.cart.deleteEmptyDelivery,optional"`
   408  		}
   409  		DeliveryInfoBuilder cartDomain.DeliveryInfoBuilder
   410  		CartCache           cartApplication.CartCache
   411  		PlaceOrderService   placeorder.Service
   412  	}
   413  
   414  	type args struct {
   415  		ctx     context.Context
   416  		session *web.Session
   417  	}
   418  
   419  	tests := []struct {
   420  		name   string
   421  		fields fields
   422  		args   args
   423  		want   string
   424  	}{
   425  		{
   426  			name: "reserve order id, reserved before",
   427  			fields: fields{
   428  				CartReceiverService: func() *cartApplication.CartReceiverService {
   429  					result := &cartApplication.CartReceiverService{}
   430  					result.Inject(
   431  						new(MockGuestCartServiceWithModifyBehaviour),
   432  						new(MockCustomerCartService),
   433  						func() *decorator.DecoratedCartFactory {
   434  							result := &decorator.DecoratedCartFactory{}
   435  							result.Inject(
   436  								&MockProductService{},
   437  								flamingo.NullLogger{},
   438  							)
   439  
   440  							return result
   441  						}(),
   442  						nil,
   443  						flamingo.NullLogger{},
   444  						new(MockEventRouter),
   445  						&struct {
   446  							CartCache cartApplication.CartCache `inject:",optional"`
   447  						}{
   448  							CartCache: new(MockCartWithItemCacheWithAdditionalData),
   449  						},
   450  					)
   451  					return result
   452  				}(),
   453  				ProductService: &MockProductService{},
   454  				Logger:         flamingo.NullLogger{},
   455  				EventRouter:    new(MockEventRouter),
   456  				EventPublisher: new(MockEventPublisher),
   457  				config: &struct {
   458  					DefaultDeliveryCode string `inject:"config:commerce.cart.defaultDeliveryCode,optional"`
   459  					DeleteEmptyDelivery bool   `inject:"config:commerce.cart.deleteEmptyDelivery,optional"`
   460  				}{
   461  					DefaultDeliveryCode: "default_delivery_code",
   462  					DeleteEmptyDelivery: false,
   463  				},
   464  				DeliveryInfoBuilder: new(MockDeliveryInfoBuilder),
   465  				CartCache:           new(MockCartWithItemCacheWithAdditionalData),
   466  				RestrictionService:  nil,
   467  				PlaceOrderService:   &MockPlaceOrderService{},
   468  			},
   469  			args: args{
   470  				ctx:     context.Background(),
   471  				session: web.EmptySession().Store(cartApplication.GuestCartSessionKey, "some_guest_id"),
   472  			},
   473  			want: "201910251128792ZM",
   474  		},
   475  		{
   476  			name: "reserved order id, not reserved before",
   477  			fields: fields{
   478  				CartReceiverService: func() *cartApplication.CartReceiverService {
   479  					result := &cartApplication.CartReceiverService{}
   480  					result.Inject(
   481  						new(MockGuestCartServiceWithModifyBehaviour),
   482  						new(MockCustomerCartService),
   483  						func() *decorator.DecoratedCartFactory {
   484  							result := &decorator.DecoratedCartFactory{}
   485  							result.Inject(
   486  								&MockProductService{},
   487  								flamingo.NullLogger{},
   488  							)
   489  
   490  							return result
   491  						}(),
   492  						nil,
   493  						flamingo.NullLogger{},
   494  						new(MockEventRouter),
   495  						&struct {
   496  							CartCache cartApplication.CartCache `inject:",optional"`
   497  						}{
   498  							CartCache: new(MockCartWithItemCache),
   499  						},
   500  					)
   501  					return result
   502  				}(),
   503  				ProductService: &MockProductService{},
   504  				Logger:         flamingo.NullLogger{},
   505  				EventPublisher: new(MockEventPublisher),
   506  				EventRouter:    new(MockEventRouter),
   507  				config: &struct {
   508  					DefaultDeliveryCode string `inject:"config:commerce.cart.defaultDeliveryCode,optional"`
   509  					DeleteEmptyDelivery bool   `inject:"config:commerce.cart.deleteEmptyDelivery,optional"`
   510  				}{
   511  					DefaultDeliveryCode: "default_delivery_code",
   512  					DeleteEmptyDelivery: false,
   513  				},
   514  				DeliveryInfoBuilder: new(MockDeliveryInfoBuilder),
   515  				CartCache:           new(MockCartWithItemCache),
   516  				RestrictionService:  nil,
   517  				PlaceOrderService:   &MockPlaceOrderService{},
   518  			},
   519  			args: args{
   520  				ctx:     context.Background(),
   521  				session: web.EmptySession().Store(cartApplication.GuestCartSessionKey, "some_guest_id"),
   522  			},
   523  			want: "201910251128788TD",
   524  		},
   525  	}
   526  
   527  	for _, tt := range tests {
   528  		t.Run(tt.name, func(t *testing.T) {
   529  			cs := &cartApplication.CartService{}
   530  			cs.Inject(
   531  				tt.fields.CartReceiverService,
   532  				tt.fields.ProductService,
   533  				tt.fields.EventPublisher,
   534  				tt.fields.EventRouter,
   535  				tt.fields.DeliveryInfoBuilder,
   536  				tt.fields.RestrictionService,
   537  				nil,
   538  				tt.fields.Logger,
   539  				tt.fields.config,
   540  				&struct {
   541  					CartValidator     validation.Validator      `inject:",optional"`
   542  					ItemValidator     validation.ItemValidator  `inject:",optional"`
   543  					CartCache         cartApplication.CartCache `inject:",optional"`
   544  					PlaceOrderService placeorder.Service        `inject:",optional"`
   545  				}{
   546  					PlaceOrderService: tt.fields.PlaceOrderService,
   547  				},
   548  			)
   549  
   550  			got, _ := cs.ReserveOrderIDAndSave(tt.args.ctx, tt.args.session)
   551  
   552  			reservedOrderIDFromGot := got.AdditionalData.ReservedOrderID
   553  			if reservedOrderIDFromGot != tt.want {
   554  				t.Errorf("CartService.ReserveOrderIDAndSave() got!=want, got: %s, want: %s", reservedOrderIDFromGot, tt.want)
   555  			}
   556  		})
   557  	}
   558  }
   559  
   560  func TestCartService_ForceReserveOrderIDAndSave(t *testing.T) {
   561  	type fields struct {
   562  		CartReceiverService *cartApplication.CartReceiverService
   563  		ProductService      productDomain.ProductService
   564  		Logger              flamingo.Logger
   565  		EventPublisher      events.EventPublisher
   566  		EventRouter         flamingo.EventRouter
   567  		RestrictionService  *validation.RestrictionService
   568  		config              *struct {
   569  			DefaultDeliveryCode string `inject:"config:commerce.cart.defaultDeliveryCode,optional"`
   570  			DeleteEmptyDelivery bool   `inject:"config:commerce.cart.deleteEmptyDelivery,optional"`
   571  		}
   572  		DeliveryInfoBuilder cartDomain.DeliveryInfoBuilder
   573  		CartCache           cartApplication.CartCache
   574  		PlaceOrderService   placeorder.Service
   575  	}
   576  
   577  	type args struct {
   578  		ctx     context.Context
   579  		session *web.Session
   580  	}
   581  
   582  	tests := []struct {
   583  		name   string
   584  		fields fields
   585  		args   args
   586  		want   string
   587  	}{
   588  		{
   589  			name: "force reserved order id, reserved before",
   590  			fields: fields{
   591  				CartReceiverService: func() *cartApplication.CartReceiverService {
   592  					result := &cartApplication.CartReceiverService{}
   593  					result.Inject(
   594  						new(MockGuestCartServiceWithModifyBehaviour),
   595  						new(MockCustomerCartService),
   596  						func() *decorator.DecoratedCartFactory {
   597  							result := &decorator.DecoratedCartFactory{}
   598  							result.Inject(
   599  								&MockProductService{},
   600  								flamingo.NullLogger{},
   601  							)
   602  
   603  							return result
   604  						}(),
   605  						nil,
   606  						flamingo.NullLogger{},
   607  						new(MockEventRouter),
   608  						&struct {
   609  							CartCache cartApplication.CartCache `inject:",optional"`
   610  						}{
   611  							CartCache: new(MockCartWithItemCacheWithAdditionalData),
   612  						},
   613  					)
   614  					return result
   615  				}(),
   616  				ProductService: &MockProductService{},
   617  				Logger:         flamingo.NullLogger{},
   618  				EventPublisher: new(MockEventPublisher),
   619  				EventRouter:    new(MockEventRouter),
   620  				config: &struct {
   621  					DefaultDeliveryCode string `inject:"config:commerce.cart.defaultDeliveryCode,optional"`
   622  					DeleteEmptyDelivery bool   `inject:"config:commerce.cart.deleteEmptyDelivery,optional"`
   623  				}{
   624  					DefaultDeliveryCode: "default_delivery_code",
   625  					DeleteEmptyDelivery: false,
   626  				},
   627  				DeliveryInfoBuilder: new(MockDeliveryInfoBuilder),
   628  				CartCache:           new(MockCartWithItemCacheWithAdditionalData),
   629  				RestrictionService:  nil,
   630  				PlaceOrderService:   &MockPlaceOrderService{},
   631  			},
   632  			args: args{
   633  				ctx:     context.Background(),
   634  				session: web.EmptySession().Store(cartApplication.GuestCartSessionKey, "some_guest_id"),
   635  			},
   636  			want: "201910251128788TD",
   637  		},
   638  		{
   639  			name: "force reserved order id, not reserved before",
   640  			fields: fields{
   641  				CartReceiverService: func() *cartApplication.CartReceiverService {
   642  					result := &cartApplication.CartReceiverService{}
   643  					result.Inject(
   644  						new(MockGuestCartServiceWithModifyBehaviour),
   645  						new(MockCustomerCartService),
   646  						func() *decorator.DecoratedCartFactory {
   647  							result := &decorator.DecoratedCartFactory{}
   648  							result.Inject(
   649  								&MockProductService{},
   650  								flamingo.NullLogger{},
   651  							)
   652  
   653  							return result
   654  						}(),
   655  						nil,
   656  						flamingo.NullLogger{},
   657  						new(MockEventRouter),
   658  						&struct {
   659  							CartCache cartApplication.CartCache `inject:",optional"`
   660  						}{
   661  							CartCache: new(MockCartWithItemCache),
   662  						},
   663  					)
   664  					return result
   665  				}(),
   666  				ProductService: &MockProductService{},
   667  				Logger:         flamingo.NullLogger{},
   668  				EventPublisher: new(MockEventPublisher),
   669  				EventRouter:    new(MockEventRouter),
   670  				config: &struct {
   671  					DefaultDeliveryCode string `inject:"config:commerce.cart.defaultDeliveryCode,optional"`
   672  					DeleteEmptyDelivery bool   `inject:"config:commerce.cart.deleteEmptyDelivery,optional"`
   673  				}{
   674  					DefaultDeliveryCode: "default_delivery_code",
   675  					DeleteEmptyDelivery: false,
   676  				},
   677  				DeliveryInfoBuilder: new(MockDeliveryInfoBuilder),
   678  				CartCache:           new(MockCartWithItemCache),
   679  				RestrictionService:  nil,
   680  				PlaceOrderService:   &MockPlaceOrderService{},
   681  			},
   682  			args: args{
   683  				ctx:     context.Background(),
   684  				session: web.EmptySession().Store(cartApplication.GuestCartSessionKey, "some_guest_id"),
   685  			},
   686  			want: "201910251128788TD",
   687  		},
   688  	}
   689  
   690  	for _, tt := range tests {
   691  		t.Run(tt.name, func(t *testing.T) {
   692  			cs := &cartApplication.CartService{}
   693  			cs.Inject(
   694  				tt.fields.CartReceiverService,
   695  				tt.fields.ProductService,
   696  				tt.fields.EventPublisher,
   697  				tt.fields.EventRouter,
   698  				tt.fields.DeliveryInfoBuilder,
   699  				tt.fields.RestrictionService,
   700  				nil,
   701  				tt.fields.Logger,
   702  				tt.fields.config,
   703  				&struct {
   704  					CartValidator     validation.Validator      `inject:",optional"`
   705  					ItemValidator     validation.ItemValidator  `inject:",optional"`
   706  					CartCache         cartApplication.CartCache `inject:",optional"`
   707  					PlaceOrderService placeorder.Service        `inject:",optional"`
   708  				}{
   709  					PlaceOrderService: tt.fields.PlaceOrderService,
   710  				},
   711  			)
   712  
   713  			got, _ := cs.ForceReserveOrderIDAndSave(tt.args.ctx, tt.args.session)
   714  
   715  			reservedOrderIDFromGot := got.AdditionalData.ReservedOrderID
   716  			if reservedOrderIDFromGot != tt.want {
   717  				t.Errorf("CartService.ReserveOrderIDAndSave() got!=want, got: %s, want: %s", reservedOrderIDFromGot, tt.want)
   718  			}
   719  		})
   720  	}
   721  }
   722  
   723  type (
   724  	MockGuestCartServiceWithModifyBehaviour struct {
   725  		cart *cartDomain.Cart
   726  	}
   727  )
   728  
   729  func (m *MockGuestCartServiceWithModifyBehaviour) GetCart(ctx context.Context, cartID string) (*cartDomain.Cart, error) {
   730  	if m.cart != nil {
   731  		return m.cart, nil
   732  	}
   733  
   734  	return &cartDomain.Cart{
   735  		ID: "mock_guest_cart",
   736  	}, nil
   737  }
   738  
   739  func (m *MockGuestCartServiceWithModifyBehaviour) GetNewCart(ctx context.Context) (*cartDomain.Cart, error) {
   740  	return &cartDomain.Cart{
   741  		ID: "mock_guest_cart",
   742  	}, nil
   743  }
   744  
   745  func (m *MockGuestCartServiceWithModifyBehaviour) GetModifyBehaviour(context.Context) (cartDomain.ModifyBehaviour, error) {
   746  	cob := &infrastructure.DefaultCartBehaviour{}
   747  
   748  	storage := &infrastructure.InMemoryCartStorage{}
   749  	storage.Inject()
   750  
   751  	cart := cartDomain.Cart{
   752  		ID: "mock_guest_cart",
   753  		Deliveries: []cartDomain.Delivery{
   754  			{
   755  				DeliveryInfo: cartDomain.DeliveryInfo{Code: "default_delivery_code"},
   756  				Cartitems: []cartDomain.Item{
   757  					{
   758  						ID:  "mock_item",
   759  						Qty: 7,
   760  					},
   761  				},
   762  			},
   763  		},
   764  	}
   765  
   766  	_ = storage.StoreCart(context.Background(), &cart)
   767  
   768  	cob.Inject(
   769  		storage,
   770  		&MockProductService{},
   771  		flamingo.NullLogger{},
   772  		nil,
   773  		nil,
   774  		nil,
   775  	)
   776  
   777  	return cob, nil
   778  }
   779  
   780  func (m *MockGuestCartServiceWithModifyBehaviour) RestoreCart(ctx context.Context, cart cartDomain.Cart) (*cartDomain.Cart, error) {
   781  	return &cart, nil
   782  }
   783  
   784  type (
   785  	MockCartWithItemCache struct {
   786  		CachedCart cartDomain.Cart
   787  	}
   788  )
   789  
   790  func (m *MockCartWithItemCache) GetCart(context.Context, *web.Session, cartApplication.CartCacheIdentifier) (*cartDomain.Cart, error) {
   791  	m.CachedCart = cartDomain.Cart{
   792  		ID: "mock_guest_cart",
   793  		Deliveries: []cartDomain.Delivery{
   794  			{
   795  				DeliveryInfo: cartDomain.DeliveryInfo{Code: "default_delivery_code"},
   796  				Cartitems: []cartDomain.Item{
   797  					{
   798  						ID:  "mock_item",
   799  						Qty: 7,
   800  					},
   801  				},
   802  			},
   803  		},
   804  	}
   805  
   806  	return &m.CachedCart, nil
   807  }
   808  
   809  func (m *MockCartWithItemCache) CacheCart(ctx context.Context, s *web.Session, cci cartApplication.CartCacheIdentifier, cart *cartDomain.Cart) error {
   810  	m.CachedCart = *cart
   811  	return nil
   812  }
   813  
   814  func (m *MockCartWithItemCache) Invalidate(context.Context, *web.Session, cartApplication.CartCacheIdentifier) error {
   815  	return nil
   816  }
   817  
   818  func (m *MockCartWithItemCache) Delete(context.Context, *web.Session, cartApplication.CartCacheIdentifier) error {
   819  	return nil
   820  }
   821  
   822  func (m *MockCartWithItemCache) DeleteAll(context.Context, *web.Session) error {
   823  	return nil
   824  }
   825  
   826  func (m *MockCartWithItemCache) BuildIdentifier(context.Context, *web.Session) (cartApplication.CartCacheIdentifier, error) {
   827  	return cartApplication.CartCacheIdentifier{}, nil
   828  }
   829  
   830  type (
   831  	MockCartWithItemCacheWithAdditionalData struct {
   832  		CachedCart cartDomain.Cart
   833  	}
   834  )
   835  
   836  func (m *MockCartWithItemCacheWithAdditionalData) GetCart(context.Context, *web.Session, cartApplication.CartCacheIdentifier) (*cartDomain.Cart, error) {
   837  	m.CachedCart = cartDomain.Cart{
   838  		ID: "mock_guest_cart",
   839  		Deliveries: []cartDomain.Delivery{
   840  			{
   841  				DeliveryInfo: cartDomain.DeliveryInfo{Code: "default_delivery_code"},
   842  				Cartitems: []cartDomain.Item{
   843  					{
   844  						ID:  "mock_item",
   845  						Qty: 7,
   846  					},
   847  				},
   848  			},
   849  		},
   850  		AdditionalData: struct {
   851  			CustomAttributes map[string]string
   852  			ReservedOrderID  string
   853  		}{CustomAttributes: nil, ReservedOrderID: "201910251128792ZM"},
   854  	}
   855  
   856  	return &m.CachedCart, nil
   857  }
   858  
   859  func (m *MockCartWithItemCacheWithAdditionalData) CacheCart(ctx context.Context, s *web.Session, cci cartApplication.CartCacheIdentifier, cart *cartDomain.Cart) error {
   860  	m.CachedCart = *cart
   861  	return nil
   862  }
   863  
   864  func (m *MockCartWithItemCacheWithAdditionalData) Invalidate(context.Context, *web.Session, cartApplication.CartCacheIdentifier) error {
   865  	return nil
   866  }
   867  
   868  func (m *MockCartWithItemCacheWithAdditionalData) Delete(context.Context, *web.Session, cartApplication.CartCacheIdentifier) error {
   869  	return nil
   870  }
   871  
   872  func (m *MockCartWithItemCacheWithAdditionalData) DeleteAll(context.Context, *web.Session) error {
   873  	return nil
   874  }
   875  
   876  func (m *MockCartWithItemCacheWithAdditionalData) BuildIdentifier(context.Context, *web.Session) (cartApplication.CartCacheIdentifier, error) {
   877  	return cartApplication.CartCacheIdentifier{}, nil
   878  }
   879  
   880  type MockRestrictor struct {
   881  	IsRestricted  bool
   882  	MaxQty        int
   883  	DifferenceQty int
   884  }
   885  
   886  func (r *MockRestrictor) Name() string {
   887  	return "MockRestrictor"
   888  }
   889  
   890  func (r *MockRestrictor) Restrict(ctx context.Context, session *web.Session, product productDomain.BasicProduct, currentCart *cartDomain.Cart, deliveryCode string) *validation.RestrictionResult {
   891  	return &validation.RestrictionResult{
   892  		IsRestricted:        r.IsRestricted,
   893  		MaxAllowed:          r.MaxQty,
   894  		RemainingDifference: r.DifferenceQty,
   895  	}
   896  }
   897  
   898  type (
   899  	MockPlaceOrderService struct{}
   900  )
   901  
   902  func (mpos *MockPlaceOrderService) PlaceGuestCart(ctx context.Context, cart *cartDomain.Cart, payment *placeorder.Payment) (placeorder.PlacedOrderInfos, error) {
   903  	return nil, nil
   904  }
   905  
   906  func (mpos *MockPlaceOrderService) PlaceCustomerCart(ctx context.Context, identity auth.Identity, cart *cartDomain.Cart, payment *placeorder.Payment) (placeorder.PlacedOrderInfos, error) {
   907  	return nil, nil
   908  }
   909  
   910  func (mpos *MockPlaceOrderService) ReserveOrderID(ctx context.Context, cart *cartDomain.Cart) (string, error) {
   911  	return "201910251128788TD", nil
   912  }
   913  
   914  func (mpos *MockPlaceOrderService) CancelGuestOrder(ctx context.Context, orderInfos placeorder.PlacedOrderInfos) error {
   915  	return nil
   916  }
   917  
   918  func (mpos *MockPlaceOrderService) CancelCustomerOrder(ctx context.Context, orderInfos placeorder.PlacedOrderInfos, identity auth.Identity) error {
   919  	return nil
   920  }
   921  
   922  func TestCartService_CartInEvent(t *testing.T) {
   923  	// bootstrap cart service
   924  	cartReceiverService := func() *cartApplication.CartReceiverService {
   925  		result := &cartApplication.CartReceiverService{}
   926  		result.Inject(
   927  			new(MockGuestCartServiceWithModifyBehaviour),
   928  			new(MockCustomerCartService),
   929  			func() *decorator.DecoratedCartFactory {
   930  				result := &decorator.DecoratedCartFactory{}
   931  				result.Inject(
   932  					&MockProductService{},
   933  					flamingo.NullLogger{},
   934  				)
   935  
   936  				return result
   937  			}(),
   938  			nil,
   939  			flamingo.NullLogger{},
   940  			new(MockEventRouter),
   941  			&struct {
   942  				CartCache cartApplication.CartCache `inject:",optional"`
   943  			}{
   944  				CartCache: new(MockCartWithItemCacheWithAdditionalData),
   945  			},
   946  		)
   947  		return result
   948  	}()
   949  	productService := &MockProductService{}
   950  	logger := flamingo.NullLogger{}
   951  	eventPublisher := new(MockEventPublisher)
   952  	eventRouter := new(MockEventRouter)
   953  	eventRouter.On("Dispatch", mock.Anything, mock.Anything, mock.Anything).Return()
   954  	config := &struct {
   955  		DefaultDeliveryCode string `inject:"config:commerce.cart.defaultDeliveryCode,optional"`
   956  		DeleteEmptyDelivery bool   `inject:"config:commerce.cart.deleteEmptyDelivery,optional"`
   957  	}{
   958  		DefaultDeliveryCode: "default_delivery_code",
   959  		DeleteEmptyDelivery: false,
   960  	}
   961  	deliveryInfoBuilder := new(MockDeliveryInfoBuilder)
   962  	restrictionService := func() *validation.RestrictionService {
   963  		rs := &validation.RestrictionService{}
   964  		rs.Inject(
   965  			[]validation.MaxQuantityRestrictor{
   966  				&MockRestrictor{},
   967  			},
   968  		)
   969  		return rs
   970  	}()
   971  
   972  	// init cart service with dependencies
   973  	cartService := cartApplication.CartService{}
   974  	cartService.Inject(
   975  		cartReceiverService,
   976  		productService,
   977  		eventPublisher,
   978  		eventRouter,
   979  		deliveryInfoBuilder,
   980  		restrictionService,
   981  		nil,
   982  		logger,
   983  		config,
   984  		nil,
   985  	)
   986  
   987  	// add product to cart, we expect event to be thrown with updated cart
   988  	addRequest := cartDomain.AddRequest{
   989  		MarketplaceCode: "code-1",
   990  		Qty:             1,
   991  	}
   992  	ctx := context.Background()
   993  	session := web.EmptySession()
   994  	_, err := cartService.AddProduct(ctx, session, "default_delivery_code", addRequest)
   995  	assert.Nil(t, err)
   996  	// white box tests that event router has been called as expected (once)
   997  	eventRouter.AssertNumberOfCalls(t, "Dispatch", 1)
   998  	// white box test that ensures router has been called with expected parameter (add to cart event)
   999  	// with the expected marketplace code of the item
  1000  	eventRouter.AssertCalled(t, "Dispatch", mock.Anything, fmt.Sprintf("%T", new(events.AddToCartEvent)), addRequest.MarketplaceCode)
  1001  }
  1002  
  1003  func createCartServiceWithDependencies() *cartApplication.CartService {
  1004  	eventRouter := new(MockEventRouter)
  1005  	eventRouter.On("Dispatch", mock.Anything, mock.Anything, mock.Anything).Return()
  1006  	cartCache := new(MockCartCache)
  1007  
  1008  	crs := &cartApplication.CartReceiverService{}
  1009  	crs.Inject(new(MockGuestCartServiceWithModifyBehaviour),
  1010  		nil,
  1011  		func() *decorator.DecoratedCartFactory {
  1012  			result := &decorator.DecoratedCartFactory{}
  1013  			result.Inject(
  1014  				&MockProductService{},
  1015  				flamingo.NullLogger{},
  1016  			)
  1017  
  1018  			return result
  1019  		}(),
  1020  		nil,
  1021  		flamingo.NullLogger{},
  1022  		eventRouter,
  1023  		&struct {
  1024  			CartCache cartApplication.CartCache `inject:",optional"`
  1025  		}{
  1026  			CartCache: cartCache,
  1027  		})
  1028  
  1029  	cs := &cartApplication.CartService{}
  1030  	cs.Inject(
  1031  		crs,
  1032  		&MockProductService{},
  1033  		new(MockEventPublisher),
  1034  		eventRouter,
  1035  		new(MockDeliveryInfoBuilder),
  1036  		func() *validation.RestrictionService {
  1037  			rs := &validation.RestrictionService{}
  1038  			rs.Inject(
  1039  				[]validation.MaxQuantityRestrictor{
  1040  					&MockRestrictor{},
  1041  				},
  1042  			)
  1043  			return rs
  1044  		}(),
  1045  		nil,
  1046  		flamingo.NullLogger{},
  1047  		&struct {
  1048  			DefaultDeliveryCode string `inject:"config:commerce.cart.defaultDeliveryCode,optional"`
  1049  			DeleteEmptyDelivery bool   `inject:"config:commerce.cart.deleteEmptyDelivery,optional"`
  1050  		}{
  1051  			DefaultDeliveryCode: "default_delivery_code",
  1052  			DeleteEmptyDelivery: false,
  1053  		},
  1054  		&struct {
  1055  			CartValidator     validation.Validator      `inject:",optional"`
  1056  			ItemValidator     validation.ItemValidator  `inject:",optional"`
  1057  			CartCache         cartApplication.CartCache `inject:",optional"`
  1058  			PlaceOrderService placeorder.Service        `inject:",optional"`
  1059  		}{
  1060  			CartCache: cartCache,
  1061  		},
  1062  	)
  1063  	return cs
  1064  }
  1065  
  1066  func TestCartService_SetAdditionalData(t *testing.T) {
  1067  	cs := createCartServiceWithDependencies()
  1068  
  1069  	cart, err := cs.UpdateAdditionalData(context.Background(), web.EmptySession(), map[string]string{"test": "data", "foo": "bar"})
  1070  	assert.NoError(t, err)
  1071  	assert.Equal(t, "data", cart.AdditionalData.CustomAttributes["test"])
  1072  	assert.Equal(t, "bar", cart.AdditionalData.CustomAttributes["foo"])
  1073  }
  1074  
  1075  func TestCartService_SetAdditionalDataForDelivery(t *testing.T) {
  1076  	cs := createCartServiceWithDependencies()
  1077  
  1078  	ctx := context.Background()
  1079  	session := web.EmptySession()
  1080  	addRequest := cartDomain.AddRequest{
  1081  		MarketplaceCode: "code-1",
  1082  		Qty:             1,
  1083  	}
  1084  	_, err := cs.AddProduct(ctx, session, "default_delivery_code", addRequest)
  1085  	assert.Nil(t, err)
  1086  	cart, err := cs.UpdateDeliveryAdditionalData(ctx, session, "default_delivery_code", map[string]string{"test": "data", "foo": "bar"})
  1087  	assert.NoError(t, err)
  1088  	var deliveryInfo *cartDomain.DeliveryInfo
  1089  	for _, delivery := range cart.Deliveries {
  1090  		if delivery.DeliveryInfo.Code == "default_delivery_code" {
  1091  			deliveryInfo = &delivery.DeliveryInfo
  1092  		}
  1093  	}
  1094  
  1095  	require.NotNil(t, deliveryInfo)
  1096  	assert.Equal(t, "data", deliveryInfo.AdditionalData["test"])
  1097  	assert.Equal(t, "bar", deliveryInfo.AdditionalData["foo"])
  1098  }
  1099  
  1100  func TestCartService_UpdateItemBundleConfig(t *testing.T) {
  1101  	t.Parallel()
  1102  
  1103  	eventRouter := new(MockEventRouter)
  1104  	eventRouter.On("Dispatch", mock.Anything, mock.Anything, mock.Anything).Return()
  1105  
  1106  	t.Run("error when bundle configuration not provided", func(t *testing.T) {
  1107  		t.Parallel()
  1108  
  1109  		behaviour := &mocks2.ModifyBehaviour{}
  1110  		behaviour.EXPECT().UpdateItem(mock.Anything, mock.Anything, mock.Anything)
  1111  
  1112  		cart := &cartDomain.Cart{
  1113  			Deliveries: []cartDomain.Delivery{
  1114  				{
  1115  					Cartitems: []cartDomain.Item{
  1116  						{
  1117  							ID:              "fakeID",
  1118  							MarketplaceCode: "fake",
  1119  						},
  1120  					},
  1121  				},
  1122  			},
  1123  		}
  1124  
  1125  		guestCartService := &mocks2.GuestCartService{}
  1126  		guestCartService.EXPECT().GetModifyBehaviour(mock.Anything).Return(behaviour, nil)
  1127  
  1128  		cartReceiverService, _ := getCartReceiverServiceForBundleUpdateTest(cart, guestCartService)
  1129  
  1130  		cartService := &cartApplication.CartService{}
  1131  		cartService.Inject(
  1132  			cartReceiverService,
  1133  			&mocks.ProductService{},
  1134  			new(MockEventPublisher),
  1135  			eventRouter,
  1136  			new(MockDeliveryInfoBuilder),
  1137  			nil,
  1138  			nil,
  1139  			flamingo.NullLogger{},
  1140  			&struct {
  1141  				DefaultDeliveryCode string `inject:"config:commerce.cart.defaultDeliveryCode,optional"`
  1142  				DeleteEmptyDelivery bool   `inject:"config:commerce.cart.deleteEmptyDelivery,optional"`
  1143  			}{},
  1144  			&struct {
  1145  				CartValidator     validation.Validator      `inject:",optional"`
  1146  				ItemValidator     validation.ItemValidator  `inject:",optional"`
  1147  				CartCache         cartApplication.CartCache `inject:",optional"`
  1148  				PlaceOrderService placeorder.Service        `inject:",optional"`
  1149  			}{},
  1150  		)
  1151  
  1152  		session := web.EmptySession()
  1153  		session.Store(cartApplication.GuestCartSessionKey, "fakeCartSession")
  1154  
  1155  		updateCommand := cartDomain.ItemUpdateCommand{ItemID: "fakeID"}
  1156  
  1157  		err := cartService.UpdateItemBundleConfig(context.Background(), session, updateCommand)
  1158  		assert.ErrorIs(t, err, cartApplication.ErrBundleConfigNotProvided)
  1159  	})
  1160  
  1161  	t.Run("error when trying to update item that is not a bundle", func(t *testing.T) {
  1162  		t.Parallel()
  1163  
  1164  		behaviour := &mocks2.ModifyBehaviour{}
  1165  		behaviour.EXPECT().UpdateItem(mock.Anything, mock.Anything, mock.Anything)
  1166  
  1167  		cart := &cartDomain.Cart{
  1168  			Deliveries: []cartDomain.Delivery{
  1169  				{
  1170  					Cartitems: []cartDomain.Item{
  1171  						{
  1172  							ID:              "fakeID",
  1173  							MarketplaceCode: "fake",
  1174  						},
  1175  					},
  1176  				},
  1177  			},
  1178  		}
  1179  
  1180  		guestCartService := &mocks2.GuestCartService{}
  1181  		guestCartService.EXPECT().GetModifyBehaviour(mock.Anything).Return(behaviour, nil)
  1182  
  1183  		cartReceiverService, _ := getCartReceiverServiceForBundleUpdateTest(cart, guestCartService)
  1184  
  1185  		product := productDomain.SimpleProduct{}
  1186  
  1187  		productService := &mocks.ProductService{}
  1188  		productService.EXPECT().Get(mock.Anything, mock.Anything).Return(product, nil)
  1189  
  1190  		cartService := &cartApplication.CartService{}
  1191  		cartService.Inject(
  1192  			cartReceiverService,
  1193  			productService,
  1194  			new(MockEventPublisher),
  1195  			eventRouter,
  1196  			new(MockDeliveryInfoBuilder),
  1197  			nil,
  1198  			nil,
  1199  			flamingo.NullLogger{},
  1200  			&struct {
  1201  				DefaultDeliveryCode string `inject:"config:commerce.cart.defaultDeliveryCode,optional"`
  1202  				DeleteEmptyDelivery bool   `inject:"config:commerce.cart.deleteEmptyDelivery,optional"`
  1203  			}{},
  1204  			&struct {
  1205  				CartValidator     validation.Validator      `inject:",optional"`
  1206  				ItemValidator     validation.ItemValidator  `inject:",optional"`
  1207  				CartCache         cartApplication.CartCache `inject:",optional"`
  1208  				PlaceOrderService placeorder.Service        `inject:",optional"`
  1209  			}{},
  1210  		)
  1211  
  1212  		session := web.EmptySession()
  1213  		session.Store(cartApplication.GuestCartSessionKey, "fakeCartSession")
  1214  
  1215  		updateCommand := cartDomain.ItemUpdateCommand{
  1216  			ItemID: "fakeID",
  1217  			BundleConfiguration: productDomain.BundleConfiguration{
  1218  				"choice1": {
  1219  					MarketplaceCode: "test",
  1220  					Qty:             1,
  1221  				},
  1222  			},
  1223  		}
  1224  
  1225  		err := cartService.UpdateItemBundleConfig(context.Background(), session, updateCommand)
  1226  		assert.ErrorIs(t, err, cartApplication.ErrProductNotTypeBundle)
  1227  	})
  1228  
  1229  	t.Run("if item validator is not provided, call update item on cart behaviour", func(t *testing.T) {
  1230  		t.Parallel()
  1231  
  1232  		cart := &cartDomain.Cart{
  1233  			Deliveries: []cartDomain.Delivery{
  1234  				{
  1235  					Cartitems: []cartDomain.Item{
  1236  						{
  1237  							ID:              "fakeID",
  1238  							MarketplaceCode: "fake",
  1239  						},
  1240  					},
  1241  				},
  1242  			},
  1243  		}
  1244  
  1245  		behaviour := &mocks2.ModifyBehaviour{}
  1246  		behaviour.EXPECT().UpdateItem(mock.Anything, mock.Anything, mock.Anything).Return(cart, nil, nil)
  1247  
  1248  		guestCartService := &mocks2.GuestCartService{}
  1249  		guestCartService.EXPECT().GetModifyBehaviour(mock.Anything).Return(behaviour, nil)
  1250  
  1251  		cartReceiverService, _ := getCartReceiverServiceForBundleUpdateTest(cart, guestCartService)
  1252  
  1253  		product := productDomain.BundleProduct{}
  1254  
  1255  		productService := &mocks.ProductService{}
  1256  		productService.EXPECT().Get(mock.Anything, mock.Anything).Return(product, nil)
  1257  
  1258  		cartService := &cartApplication.CartService{}
  1259  		cartService.Inject(
  1260  			cartReceiverService,
  1261  			productService,
  1262  			new(MockEventPublisher),
  1263  			eventRouter,
  1264  			new(MockDeliveryInfoBuilder),
  1265  			nil,
  1266  			nil,
  1267  			flamingo.NullLogger{},
  1268  			&struct {
  1269  				DefaultDeliveryCode string `inject:"config:commerce.cart.defaultDeliveryCode,optional"`
  1270  				DeleteEmptyDelivery bool   `inject:"config:commerce.cart.deleteEmptyDelivery,optional"`
  1271  			}{},
  1272  			&struct {
  1273  				CartValidator     validation.Validator      `inject:",optional"`
  1274  				ItemValidator     validation.ItemValidator  `inject:",optional"`
  1275  				CartCache         cartApplication.CartCache `inject:",optional"`
  1276  				PlaceOrderService placeorder.Service        `inject:",optional"`
  1277  			}{},
  1278  		)
  1279  
  1280  		session := web.EmptySession()
  1281  		session.Store(cartApplication.GuestCartSessionKey, "fakeCartSession")
  1282  
  1283  		updateCommand := cartDomain.ItemUpdateCommand{
  1284  			ItemID: "fakeID",
  1285  			BundleConfiguration: productDomain.BundleConfiguration{
  1286  				"choice1": {
  1287  					MarketplaceCode: "test",
  1288  					Qty:             1,
  1289  				},
  1290  			},
  1291  		}
  1292  
  1293  		err := cartService.UpdateItemBundleConfig(context.Background(), session, updateCommand)
  1294  		assert.NoError(t, err)
  1295  
  1296  		behaviour.MethodCalled("UpdateItem")
  1297  	})
  1298  
  1299  	t.Run("if item validator does not complain, call update item on cart behaviour", func(t *testing.T) {
  1300  		t.Parallel()
  1301  
  1302  		cart := &cartDomain.Cart{
  1303  			Deliveries: []cartDomain.Delivery{
  1304  				{
  1305  					Cartitems: []cartDomain.Item{
  1306  						{
  1307  							ID:              "fakeID",
  1308  							MarketplaceCode: "fake",
  1309  						},
  1310  					},
  1311  				},
  1312  			},
  1313  		}
  1314  
  1315  		behaviour := &mocks2.ModifyBehaviour{}
  1316  		behaviour.EXPECT().UpdateItem(mock.Anything, mock.Anything, mock.Anything).Return(cart, nil, nil)
  1317  
  1318  		guestCartService := &mocks2.GuestCartService{}
  1319  		guestCartService.EXPECT().GetModifyBehaviour(mock.Anything).Return(behaviour, nil)
  1320  
  1321  		cartReceiverService, _ := getCartReceiverServiceForBundleUpdateTest(cart, guestCartService)
  1322  
  1323  		product := productDomain.BundleProduct{}
  1324  
  1325  		productService := &mocks.ProductService{}
  1326  		productService.EXPECT().Get(mock.Anything, mock.Anything).Return(product, nil)
  1327  
  1328  		itemValidator := &mocks3.ItemValidator{}
  1329  		itemValidator.EXPECT().Validate(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
  1330  			Return(nil)
  1331  
  1332  		cartService := &cartApplication.CartService{}
  1333  		cartService.Inject(
  1334  			cartReceiverService,
  1335  			productService,
  1336  			new(MockEventPublisher),
  1337  			eventRouter,
  1338  			new(MockDeliveryInfoBuilder),
  1339  			nil,
  1340  			nil,
  1341  			flamingo.NullLogger{},
  1342  			&struct {
  1343  				DefaultDeliveryCode string `inject:"config:commerce.cart.defaultDeliveryCode,optional"`
  1344  				DeleteEmptyDelivery bool   `inject:"config:commerce.cart.deleteEmptyDelivery,optional"`
  1345  			}{},
  1346  			&struct {
  1347  				CartValidator     validation.Validator      `inject:",optional"`
  1348  				ItemValidator     validation.ItemValidator  `inject:",optional"`
  1349  				CartCache         cartApplication.CartCache `inject:",optional"`
  1350  				PlaceOrderService placeorder.Service        `inject:",optional"`
  1351  			}{
  1352  				ItemValidator: itemValidator,
  1353  			},
  1354  		)
  1355  
  1356  		session := web.EmptySession()
  1357  		session.Store(cartApplication.GuestCartSessionKey, "fakeCartSession")
  1358  
  1359  		updateCommand := cartDomain.ItemUpdateCommand{
  1360  			ItemID: "fakeID",
  1361  			BundleConfiguration: productDomain.BundleConfiguration{
  1362  				"choice1": {
  1363  					MarketplaceCode: "test",
  1364  					Qty:             1,
  1365  				},
  1366  			},
  1367  		}
  1368  
  1369  		err := cartService.UpdateItemBundleConfig(context.Background(), session, updateCommand)
  1370  		assert.NoError(t, err)
  1371  
  1372  		behaviour.MethodCalled("UpdateItem")
  1373  	})
  1374  
  1375  	t.Run("if item validator fails, return an error and don't call update item on cart behaviour", func(t *testing.T) {
  1376  		t.Parallel()
  1377  
  1378  		validationError := errors.New("some error")
  1379  
  1380  		cart := &cartDomain.Cart{
  1381  			Deliveries: []cartDomain.Delivery{
  1382  				{
  1383  					Cartitems: []cartDomain.Item{
  1384  						{
  1385  							ID:              "fakeID",
  1386  							MarketplaceCode: "fake",
  1387  						},
  1388  					},
  1389  				},
  1390  			},
  1391  		}
  1392  
  1393  		behaviour := &mocks2.ModifyBehaviour{}
  1394  		behaviour.EXPECT().UpdateItem(mock.Anything, mock.Anything, mock.Anything).Return(cart, nil, nil)
  1395  
  1396  		guestCartService := &mocks2.GuestCartService{}
  1397  		guestCartService.EXPECT().GetModifyBehaviour(mock.Anything).Return(behaviour, nil)
  1398  
  1399  		cartReceiverService, _ := getCartReceiverServiceForBundleUpdateTest(cart, guestCartService)
  1400  
  1401  		product := productDomain.BundleProduct{}
  1402  
  1403  		productService := &mocks.ProductService{}
  1404  		productService.EXPECT().Get(mock.Anything, mock.Anything).Return(product, nil)
  1405  
  1406  		itemValidator := &mocks3.ItemValidator{}
  1407  		itemValidator.EXPECT().Validate(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
  1408  			Return(validationError)
  1409  
  1410  		cartService := &cartApplication.CartService{}
  1411  		cartService.Inject(
  1412  			cartReceiverService,
  1413  			productService,
  1414  			new(MockEventPublisher),
  1415  			eventRouter,
  1416  			new(MockDeliveryInfoBuilder),
  1417  			nil,
  1418  			nil,
  1419  			flamingo.NullLogger{},
  1420  			&struct {
  1421  				DefaultDeliveryCode string `inject:"config:commerce.cart.defaultDeliveryCode,optional"`
  1422  				DeleteEmptyDelivery bool   `inject:"config:commerce.cart.deleteEmptyDelivery,optional"`
  1423  			}{},
  1424  			&struct {
  1425  				CartValidator     validation.Validator      `inject:",optional"`
  1426  				ItemValidator     validation.ItemValidator  `inject:",optional"`
  1427  				CartCache         cartApplication.CartCache `inject:",optional"`
  1428  				PlaceOrderService placeorder.Service        `inject:",optional"`
  1429  			}{
  1430  				ItemValidator: itemValidator,
  1431  			},
  1432  		)
  1433  
  1434  		session := web.EmptySession()
  1435  		session.Store(cartApplication.GuestCartSessionKey, "fakeCartSession")
  1436  
  1437  		updateCommand := cartDomain.ItemUpdateCommand{
  1438  			ItemID: "fakeID",
  1439  			BundleConfiguration: productDomain.BundleConfiguration{
  1440  				"choice1": {
  1441  					MarketplaceCode: "test",
  1442  					Qty:             1,
  1443  				},
  1444  			},
  1445  		}
  1446  
  1447  		err := cartService.UpdateItemBundleConfig(context.Background(), session, updateCommand)
  1448  		assert.ErrorIs(t, err, validationError)
  1449  	})
  1450  }
  1451  
  1452  func getCartReceiverServiceForBundleUpdateTest(cart *cartDomain.Cart, guestCartService cartDomain.GuestCartService) (*cartApplication.CartReceiverService, *MockCartCache) {
  1453  	cartCache := new(MockCartCache)
  1454  	cartCache.CachedCart = cart
  1455  
  1456  	return func() (*cartApplication.CartReceiverService, *MockCartCache) {
  1457  		result := &cartApplication.CartReceiverService{}
  1458  		result.Inject(
  1459  			guestCartService,
  1460  			nil,
  1461  			func() *decorator.DecoratedCartFactory {
  1462  				result := &decorator.DecoratedCartFactory{}
  1463  				result.Inject(
  1464  					&MockProductService{},
  1465  					flamingo.NullLogger{},
  1466  				)
  1467  
  1468  				return result
  1469  			}(),
  1470  			nil,
  1471  			flamingo.NullLogger{},
  1472  			new(MockEventRouter),
  1473  			&struct {
  1474  				CartCache cartApplication.CartCache `inject:",optional"`
  1475  			}{
  1476  				CartCache: cartCache,
  1477  			},
  1478  		)
  1479  		return result, cartCache
  1480  	}()
  1481  }