flamingo.me/flamingo-commerce/v3@v3.11.0/product/domain/productBasics_test.go (about)

     1  package domain
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"math/big"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	"flamingo.me/flamingo-commerce/v3/price/domain"
    14  )
    15  
    16  func TestAttributeValue(t *testing.T) {
    17  	a := Attribute{RawValue: "testValue"}
    18  
    19  	assert.Equal(t, a.Value(), "testValue")
    20  }
    21  
    22  func TestAttributeIsEnabledValue(t *testing.T) {
    23  	a := Attribute{RawValue: "Yes"}
    24  	assert.True(t, a.IsEnabledValue())
    25  
    26  	a.RawValue = "yes"
    27  	assert.True(t, a.IsEnabledValue())
    28  
    29  	a.RawValue = "true"
    30  	assert.True(t, a.IsEnabledValue())
    31  
    32  	a.RawValue = true
    33  	assert.True(t, a.IsEnabledValue())
    34  
    35  	a.RawValue = "1"
    36  	assert.True(t, a.IsEnabledValue())
    37  
    38  	a.RawValue = 1
    39  	assert.True(t, a.IsEnabledValue())
    40  
    41  	a.RawValue = "anything"
    42  	assert.False(t, a.IsEnabledValue())
    43  }
    44  
    45  func TestAttributeIsDisabledValue(t *testing.T) {
    46  	a := Attribute{RawValue: "No"}
    47  	assert.True(t, a.IsDisabledValue())
    48  
    49  	a.RawValue = "no"
    50  	assert.True(t, a.IsDisabledValue())
    51  
    52  	a.RawValue = "false"
    53  	assert.True(t, a.IsDisabledValue())
    54  
    55  	a.RawValue = false
    56  	assert.True(t, a.IsDisabledValue())
    57  
    58  	a.RawValue = "0"
    59  	assert.True(t, a.IsDisabledValue())
    60  
    61  	a.RawValue = 0
    62  	assert.True(t, a.IsDisabledValue())
    63  
    64  	a.RawValue = "anything"
    65  	assert.False(t, a.IsDisabledValue())
    66  }
    67  
    68  func TestAttributeHasMultipleValues(t *testing.T) {
    69  	t.Run("[]interface{} raw value", func(t *testing.T) {
    70  		a := Attribute{RawValue: "some string"}
    71  		assert.False(t, a.HasMultipleValues())
    72  
    73  		var rawValue []interface{}
    74  		for _, val := range []string{"some", "string"} {
    75  			rawValue = append(rawValue, val)
    76  		}
    77  		a.RawValue = rawValue
    78  
    79  		assert.True(t, a.HasMultipleValues())
    80  	})
    81  
    82  	t.Run("Translated multi value", func(t *testing.T) {
    83  		a := Attribute{RawValue: []Attribute{{Code: "foo"}}}
    84  		assert.True(t, a.HasMultipleValues())
    85  	})
    86  }
    87  
    88  func TestAttributeValues(t *testing.T) {
    89  	t.Run("string values", func(t *testing.T) {
    90  		a := Attribute{RawValue: "some string"}
    91  		result := a.Values()
    92  		assert.IsType(t, []string{}, result)
    93  		assert.Len(t, result, 0)
    94  
    95  		var rawValue []interface{}
    96  		for _, val := range []string{"some", "  string    "} {
    97  			rawValue = append(rawValue, val)
    98  		}
    99  		a.RawValue = rawValue
   100  		result = a.Values()
   101  		assert.IsType(t, []string{}, result)
   102  		assert.Len(t, result, 2)
   103  		assert.Equal(t, "some", result[0])
   104  		assert.Equal(t, "string", result[1])
   105  	})
   106  
   107  	t.Run("translated values", func(t *testing.T) {
   108  		a := Attribute{RawValue: []Attribute{{Label: "translation-A", RawValue: "raw-A"}, {Label: "translation-B", RawValue: "raw-B"}}}
   109  		values := a.Values()
   110  		assert.Len(t, values, 2)
   111  		assert.Equal(t, "raw-A", values[0])
   112  		assert.Equal(t, "raw-B", values[1])
   113  	})
   114  }
   115  
   116  func TestAttributeLabels(t *testing.T) {
   117  	t.Run("translated values", func(t *testing.T) {
   118  		a := Attribute{RawValue: []Attribute{{Label: "translation-A", RawValue: "raw-A"}, {Label: "translation-B", RawValue: "raw-B"}}}
   119  		labels := a.Labels()
   120  		assert.Len(t, labels, 2)
   121  		assert.Equal(t, "translation-A", labels[0])
   122  		assert.Equal(t, "translation-B", labels[1])
   123  	})
   124  
   125  	t.Run("no translated values will fallback to raw values", func(t *testing.T) {
   126  		a := Attribute{}
   127  		var rawValue []interface{}
   128  		for _, val := range []string{"raw-1", "raw-2"} {
   129  			rawValue = append(rawValue, val)
   130  		}
   131  		a.RawValue = rawValue
   132  
   133  		labels := a.Labels()
   134  		assert.Len(t, labels, 2)
   135  		assert.Equal(t, "raw-1", labels[0])
   136  		assert.Equal(t, "raw-2", labels[1])
   137  	})
   138  }
   139  
   140  func TestAttributeHasUnit(t *testing.T) {
   141  	a := Attribute{}
   142  	assert.False(t, a.HasUnitCode())
   143  
   144  	a.UnitCode = "UNIT"
   145  	assert.True(t, a.HasUnitCode())
   146  }
   147  
   148  func TestAttributeGetUnit(t *testing.T) {
   149  	a := Attribute{}
   150  	assert.IsType(t, Unit{}, a.GetUnit(), "Get an empty unit if not available")
   151  
   152  	a.UnitCode = "PCS"
   153  	assert.IsType(t, Unit{}, a.GetUnit(), "Get a unit if available")
   154  	assert.Equal(t, "PCS", a.GetUnit().Code, "Fetched unit contains the correct content")
   155  }
   156  
   157  func TestBasicProductHasAttribute(t *testing.T) {
   158  	b := BasicProductData{}
   159  	assert.False(t, b.HasAttribute("code"))
   160  
   161  	b.Attributes = map[string]Attribute{"code": {Code: "code"}}
   162  	assert.True(t, b.HasAttribute("code"))
   163  	assert.False(t, b.HasAttribute("Code"))
   164  }
   165  
   166  func TestBasicProductHasAllAttributes(t *testing.T) {
   167  	b := BasicProductData{}
   168  	assert.False(t, b.HasAllAttributes([]string{"code", "color"}))
   169  
   170  	b.Attributes = map[string]Attribute{"code": {Code: "code"}, "color": {Code: "color"}}
   171  	assert.True(t, b.HasAllAttributes([]string{"code", "color"}))
   172  	assert.False(t, b.HasAllAttributes([]string{"Code", "Color"}))
   173  }
   174  
   175  func TestBasicProductHasGetAttributesByKey(t *testing.T) {
   176  	b := BasicProductData{}
   177  	b.Attributes = map[string]Attribute{
   178  		"foo": {Code: "foo"},
   179  		"bar": {Code: "bar"},
   180  	}
   181  	assert.Equal(t, []Attribute{
   182  		{Code: "foo"},
   183  		{Code: "bar"},
   184  	}, b.Attributes.AttributesByKey([]string{"foo", "bar"}))
   185  
   186  	assert.Equal(t, []Attribute{
   187  		{Code: "bar"},
   188  		{Code: "foo"},
   189  	}, b.Attributes.AttributesByKey([]string{"bar", "foo"}))
   190  
   191  	assert.Equal(t, []Attribute{
   192  		{Code: "foo"},
   193  		{Code: "bar"},
   194  	}, b.Attributes.AttributesByKey([]string{"foo", "baz", "bar"}))
   195  }
   196  
   197  func TestBasicProductGetFinalPrice(t *testing.T) {
   198  	p := PriceInfo{
   199  		IsDiscounted: false,
   200  		Discounted:   domain.NewFromFloat(0.99, "EUR"),
   201  		Default:      domain.NewFromFloat(1.99, "EUR"),
   202  	}
   203  	assert.Equal(t, 1.99, p.GetFinalPrice().FloatAmount())
   204  
   205  	p.IsDiscounted = true
   206  	assert.Equal(t, 0.99, p.GetFinalPrice().FloatAmount())
   207  }
   208  
   209  func TestBasicProductGetMedia(t *testing.T) {
   210  	var m []Media
   211  	p := BasicProductData{Media: m}
   212  
   213  	result := p.GetMedia("something")
   214  	assert.IsType(t, Media{}, result, "Media returned on unknown type")
   215  	assert.Empty(t, result.Usage, "empty media returned on unknown type")
   216  
   217  	result = p.GetListMedia()
   218  	assert.Empty(t, result.Usage, "empty list media returned if it does not exist")
   219  
   220  	p.Media = append(m, Media{Usage: MediaUsageList})
   221  	result = p.GetMedia(MediaUsageList)
   222  	assert.Equal(t, "list", result.Usage, "media with correct usage returned if exists")
   223  
   224  	result = p.GetListMedia()
   225  	assert.Equal(t, "list", result.Usage, "list media returned if exists")
   226  }
   227  
   228  func TestBasicProductBadgesGetFirst(t *testing.T) {
   229  	t.Parallel()
   230  	var badges Badges
   231  
   232  	assert.Nil(t, badges.First(), "get nil if badges are nil - don't fail")
   233  
   234  	badges = Badges{}
   235  	assert.Nil(t, badges.First(), "get nil if badges are empty")
   236  
   237  	badges = Badges{
   238  		{
   239  			Code:  "first",
   240  			Label: "First",
   241  		},
   242  		{
   243  			Code:  "second",
   244  			Label: "Second",
   245  		},
   246  	}
   247  	assert.Equal(t, &Badge{Code: "first", Label: "First"}, badges.First(), "get the first badge")
   248  }
   249  
   250  func TestIsSaleableNow(t *testing.T) {
   251  	s := Saleable{}
   252  	assert.False(t, s.IsSaleableNow(), "not saleable now if nothing is set")
   253  
   254  	s.IsSaleable = true
   255  	assert.True(t, s.IsSaleableNow(), "saleable now if just saleable")
   256  
   257  	future := time.Now().Add(time.Hour)
   258  	past := time.Now().Add(-time.Hour)
   259  	s.SaleableFrom = future
   260  	assert.False(t, s.IsSaleableNow(), "not saleable now if saleable from is in future")
   261  	s.SaleableFrom = past
   262  	assert.True(t, s.IsSaleableNow(), "saleable now if saleable from is in the past")
   263  
   264  	s.SaleableTo = past
   265  	assert.False(t, s.IsSaleableNow(), "not saleable now if saleable to is in the past")
   266  	s.SaleableTo = future
   267  	assert.True(t, s.IsSaleableNow(), "saleable now if saleable to is in the future")
   268  }
   269  
   270  func TestSaleable_GetLoyaltyChargeSplit(t *testing.T) {
   271  	t.Parallel()
   272  
   273  	t.Run("get correct charges", func(t *testing.T) {
   274  		t.Parallel()
   275  
   276  		p := Saleable{
   277  			ActivePrice: PriceInfo{
   278  				// 100EUR value
   279  				Default: domain.NewFromInt(100, 1, "EUR"),
   280  			},
   281  			ActiveLoyaltyPrice: &LoyaltyPriceInfo{
   282  				Type:             "loyalty.miles",
   283  				MaxPointsToSpent: new(big.Float).SetInt64(50),
   284  				// 10 is the minimum to pay in miles (=20EUR value)
   285  				MinPointsToSpent: *new(big.Float).SetInt64(10),
   286  				// 50 miles == 100EUR meaning 1Mile = 2EUR
   287  				Default: domain.NewFromInt(50, 1, "Miles"),
   288  			},
   289  		}
   290  
   291  		// Test default charges (the min price in points should be evaluated)
   292  		charges := p.GetLoyaltyChargeSplit(nil, nil, 1)
   293  
   294  		chargeLoyaltyMiles, found := charges.GetByType("loyalty.miles")
   295  		assert.True(t, found)
   296  		assert.Equal(t, domain.NewFromInt(10, 1, "Miles"), chargeLoyaltyMiles.Price, "only minimum points expected")
   297  
   298  		chargeMain, found := charges.GetByType(domain.ChargeTypeMain)
   299  		assert.True(t, found)
   300  		assert.Equal(t, domain.NewFromInt(80, 1, "EUR"), chargeMain.Price)
   301  
   302  		// Test when we pass 15 miles as wish
   303  		wished := NewWishedToPay().Add("loyalty.miles", domain.NewFromInt(15, 1, "Miles"))
   304  		charges = p.GetLoyaltyChargeSplit(nil, &wished, 1)
   305  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   306  		assert.True(t, found)
   307  
   308  		assert.Equal(t, domain.NewFromInt(70, 1, "EUR"), chargeMain.Price)
   309  
   310  		chargeLoyaltyMiles, found = charges.GetByType("loyalty.miles")
   311  		assert.True(t, found)
   312  		assert.Equal(t, domain.NewFromInt(15, 1, "Miles"), chargeLoyaltyMiles.Price, "the whished 15 points expected")
   313  
   314  		// Test when we pass 100 miles as wish
   315  		wished = NewWishedToPay().Add("loyalty.miles", domain.NewFromInt(100, 1, "Miles"))
   316  		charges = p.GetLoyaltyChargeSplit(nil, &wished, 1)
   317  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   318  		assert.True(t, found)
   319  
   320  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price, "Main charge should be 0")
   321  
   322  		chargeLoyaltyMiles, found = charges.GetByType("loyalty.miles")
   323  		assert.True(t, found)
   324  		assert.Equal(t, domain.NewFromInt(50, 1, "Miles"), chargeLoyaltyMiles.Price, "50 points expected as max")
   325  
   326  		// Test when we pass 30 miles as desired payment and wish for qty 2
   327  		wished = NewWishedToPay().Add("loyalty.miles", domain.NewFromInt(30, 1, "Miles"))
   328  		doublePrice := p.ActivePrice.GetFinalPrice().Multiply(2)
   329  		charges = p.GetLoyaltyChargeSplit(&doublePrice, &wished, 1)
   330  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   331  		assert.True(t, found)
   332  
   333  		assert.Equal(t, domain.NewFromInt(140, 1, "EUR"), chargeMain.Price)
   334  
   335  		chargeLoyaltyMiles, found = charges.GetByType("loyalty.miles")
   336  		assert.True(t, found)
   337  		assert.Equal(t, domain.NewFromInt(30, 1, "Miles"), chargeLoyaltyMiles.Price, "the whished 30 points expected")
   338  	})
   339  
   340  	t.Run("empty charges, when active loyalty is nil", func(t *testing.T) {
   341  		t.Parallel()
   342  
   343  		p := Saleable{
   344  			ActivePrice: PriceInfo{
   345  				// 100EUR value
   346  				Default: domain.NewFromInt(100, 1, "EUR"),
   347  			},
   348  			ActiveLoyaltyPrice: nil,
   349  		}
   350  
   351  		// Test default charges (the min price in points should be evaluated)
   352  		charges := p.GetLoyaltyChargeSplit(nil, nil, 1)
   353  
   354  		chargeLoyaltyMiles, found := charges.GetByType("loyalty.miles")
   355  		assert.False(t, found)
   356  		assert.Equal(t, domain.Price{}, chargeLoyaltyMiles.Price)
   357  
   358  		chargeMain, found := charges.GetByType(domain.ChargeTypeMain)
   359  		assert.True(t, found)
   360  		assert.Equal(t, domain.NewFromInt(100, 1, "EUR"), chargeMain.Price)
   361  
   362  		// Test when we pass 15 miles as wish
   363  		wished := NewWishedToPay().Add("loyalty.miles", domain.NewFromInt(15, 1, "Miles"))
   364  		charges = p.GetLoyaltyChargeSplit(nil, &wished, 1)
   365  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   366  		assert.True(t, found)
   367  
   368  		assert.Equal(t, domain.NewFromInt(100, 1, "EUR"), chargeMain.Price)
   369  
   370  		chargeLoyaltyMiles, found = charges.GetByType("loyalty.miles")
   371  		assert.False(t, found)
   372  		assert.Equal(t, domain.Price{}, chargeLoyaltyMiles.Price)
   373  
   374  		// Test when we pass 100 miles as wish
   375  		wished = NewWishedToPay().Add("loyalty.miles", domain.NewFromInt(100, 1, "Miles"))
   376  		charges = p.GetLoyaltyChargeSplit(nil, &wished, 1)
   377  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   378  		assert.True(t, found)
   379  
   380  		assert.Equal(t, domain.NewFromInt(100, 1, "EUR"), chargeMain.Price)
   381  
   382  		chargeLoyaltyMiles, found = charges.GetByType("loyalty.miles")
   383  		assert.False(t, found)
   384  		assert.Equal(t, domain.Price{}, chargeLoyaltyMiles.Price)
   385  
   386  		// Test when we pass 30 miles as desired payment and wish for qty 2
   387  		wished = NewWishedToPay().Add("loyalty.miles", domain.NewFromInt(30, 1, "Miles"))
   388  		doublePrice := p.ActivePrice.GetFinalPrice().Multiply(2)
   389  		charges = p.GetLoyaltyChargeSplit(&doublePrice, &wished, 1)
   390  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   391  		assert.True(t, found)
   392  
   393  		assert.Equal(t, domain.NewFromInt(200, 1, "EUR"), chargeMain.Price)
   394  
   395  		chargeLoyaltyMiles, found = charges.GetByType("loyalty.miles")
   396  		assert.False(t, found)
   397  		assert.Equal(t, domain.Price{}, chargeLoyaltyMiles.Price)
   398  	})
   399  }
   400  
   401  func TestSaleable_GetLoyaltyChargeSplitWithAdjustedValue(t *testing.T) {
   402  
   403  	p := Saleable{
   404  		ActivePrice: PriceInfo{
   405  			// 100EUR value
   406  			Default: domain.NewFromInt(100, 1, "EUR"),
   407  		},
   408  		ActiveLoyaltyPrice: &LoyaltyPriceInfo{
   409  			Type:             "loyalty.miles",
   410  			MaxPointsToSpent: nil,
   411  			// 10 is the minimum to pay in miles (=20EUR value)
   412  			MinPointsToSpent: *new(big.Float).SetInt64(10),
   413  			// 50 miles == 100EUR meaning 1Mile = 2EUR
   414  			Default: domain.NewFromInt(50, 1, "Miles"),
   415  		},
   416  	}
   417  
   418  	// we need to pay 150EUR (e,g. because some tax are added)
   419  	newValue := domain.NewFromInt(150, 1, "EUR")
   420  	charges := p.GetLoyaltyChargeSplit(&newValue, nil, 1)
   421  
   422  	chargeLoyaltyMiles, found := charges.GetByType("loyalty.miles")
   423  	assert.True(t, found)
   424  	assert.Equal(t, domain.NewFromInt(10, 1, "Miles"), chargeLoyaltyMiles.Price, "only minimum points expected")
   425  
   426  	chargeMain, found := charges.GetByType(domain.ChargeTypeMain)
   427  	assert.True(t, found)
   428  	assert.Equal(t, domain.NewFromInt(130, 1, "EUR"), chargeMain.Price)
   429  
   430  	// pay 150 - and want to spend less then min
   431  	newValue = domain.NewFromInt(150, 1, "EUR")
   432  	wished := NewWishedToPay()
   433  	wished.Add("loyalty.miles", domain.NewFromInt(8, 1, "Miles"))
   434  	charges = p.GetLoyaltyChargeSplit(&newValue, &wished, 1)
   435  
   436  	chargeLoyaltyMiles, found = charges.GetByType("loyalty.miles")
   437  	assert.True(t, found)
   438  	assert.Equal(t, domain.NewFromInt(10, 1, "Miles"), chargeLoyaltyMiles.Price, "only minimum points expected")
   439  
   440  	chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   441  	assert.True(t, found)
   442  	assert.Equal(t, domain.NewFromInt(130, 1, "EUR"), chargeMain.Price)
   443  
   444  	// we need to pay 50EUR (e,g. because some discounts are applied)
   445  	newValue = domain.NewFromInt(50, 1, "EUR")
   446  	charges = p.GetLoyaltyChargeSplit(&newValue, nil, 1)
   447  
   448  	chargeLoyaltyMiles, found = charges.GetByType("loyalty.miles")
   449  	assert.True(t, found)
   450  	assert.Equal(t, domain.NewFromInt(10, 1, "Miles"), chargeLoyaltyMiles.Price, "only minimum points expected")
   451  
   452  	chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   453  	assert.True(t, found)
   454  	assert.Equal(t, domain.NewFromInt(30, 1, "EUR"), chargeMain.Price)
   455  
   456  	// we need to pay 150EUR and wish to pay everything with miles
   457  	newValue = domain.NewFromInt(150, 1, "EUR")
   458  
   459  	wished = NewWishedToPay()
   460  	wished.Add("loyalty.miles", domain.NewFromInt(200000, 1, "Miles"))
   461  	charges = p.GetLoyaltyChargeSplit(&newValue, &wished, 1)
   462  
   463  	chargeLoyaltyMiles, found = charges.GetByType("loyalty.miles")
   464  	assert.True(t, found)
   465  	assert.Equal(t, domain.NewFromInt(75, 1, "Miles"), chargeLoyaltyMiles.Price, "adjusted  points expected as charge (not more than total value)")
   466  
   467  	chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   468  	assert.True(t, found)
   469  	assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price)
   470  
   471  }
   472  
   473  func TestSaleable_GetLoyaltyChargeSplitCentRoundingCheck(t *testing.T) {
   474  	t.Parallel()
   475  
   476  	t.Run("9.99 EUR and 53 Miles", func(t *testing.T) {
   477  		t.Parallel()
   478  
   479  		p := Saleable{
   480  			ActivePrice: PriceInfo{
   481  				// 100EUR value
   482  				Default: domain.NewFromFloat(9.99, "EUR"),
   483  			},
   484  			ActiveLoyaltyPrice: &LoyaltyPriceInfo{
   485  				Type:             "loyalty.miles",
   486  				MaxPointsToSpent: nil,
   487  				// 10 is the minimum to pay in miles (=20EUR value)
   488  				MinPointsToSpent: *new(big.Float).SetInt64(10),
   489  				// 50 miles == 100EUR meaning 1Mile = 2EUR
   490  				Default: domain.NewFromInt(53, 1, "Miles"), // one mile = 5.305305305305305 EUR
   491  			},
   492  		}
   493  
   494  		wishedMax := NewWishedToPay()
   495  		wishedMax.Add("loyalty.miles", domain.NewFromInt(math.MaxInt64, 1, "Miles"))
   496  
   497  		// 107.06 would be 567.98 miles - so  we pay 568 miles we expect to pay everything in miles.
   498  		expectedMilesMax := int64(568)
   499  		newValue := domain.NewFromFloat(107.06, "EUR")
   500  		charges := p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 1)
   501  		chargeLoyaltyMiles, _ := charges.GetByType("loyalty.miles")
   502  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "107.06 expected to be 568 miles")
   503  
   504  		wished := NewWishedToPay()
   505  		wished.Add("loyalty.miles", domain.NewFromInt(expectedMilesMax, 1, "Miles"))
   506  
   507  		charges = p.GetLoyaltyChargeSplit(&newValue, &wished, 1)
   508  
   509  		chargeLoyaltyMiles, found := charges.GetByType("loyalty.miles")
   510  		assert.True(t, found)
   511  		assert.Equal(t, domain.NewFromInt(568, 1, "Miles"), chargeLoyaltyMiles.Price, "adjusted  points expected as charge (not more than total value)")
   512  		assert.Equal(t, 107.06, chargeLoyaltyMiles.Value.FloatAmount(), "adjusted  points expected as charge (not more than total value)")
   513  
   514  		chargeMain, found := charges.GetByType(domain.ChargeTypeMain)
   515  		assert.True(t, found)
   516  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price)
   517  
   518  		// 106.89 would be 567.084084084084084 miles - so  we also pay 567 miles (rounded) we expect to pay everything in miles.
   519  		newValue = domain.NewFromFloat(106.89, "EUR")
   520  
   521  		wished = NewWishedToPay()
   522  		wished.Add("loyalty.miles", domain.NewFromInt(567, 1, "Miles"))
   523  		charges = p.GetLoyaltyChargeSplit(&newValue, &wished, 1)
   524  
   525  		chargeLoyaltyMiles, found = charges.GetByType("loyalty.miles")
   526  		assert.True(t, found)
   527  		assert.Equal(t, domain.NewFromInt(567, 1, "Miles"), chargeLoyaltyMiles.Price, "adjusted  points expected as charge (not more than total value)")
   528  		assert.Equal(t, 106.89, chargeLoyaltyMiles.Value.FloatAmount(), "adjusted  points expected as charge (not more than total value)")
   529  
   530  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   531  		assert.True(t, found)
   532  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price)
   533  	})
   534  
   535  	t.Run("10 items by 9.50 EUR and 53 Miles", func(t *testing.T) {
   536  		t.Parallel()
   537  
   538  		p := Saleable{
   539  			ActivePrice: PriceInfo{
   540  				Default: domain.NewFromFloat(9.50, "EUR"),
   541  			},
   542  			ActiveLoyaltyPrice: &LoyaltyPriceInfo{
   543  				Type:             "loyalty.miles",
   544  				MaxPointsToSpent: nil,
   545  				MinPointsToSpent: *new(big.Float).SetInt64(10),
   546  				Default:          domain.NewFromInt(53, 1, "Miles"), // one mile = 5.57894737 EUR
   547  			},
   548  		}
   549  
   550  		wishedMax := NewWishedToPay()
   551  		wishedMax.Add("loyalty.miles", domain.NewFromInt(math.MaxInt64, 1, "Miles"))
   552  
   553  		// 107.06 would be 567.98 miles - so  we pay 568 miles we expect to pay everything in miles.
   554  		expectedMilesMax := int64(530)
   555  		newValue := domain.NewFromFloat(95, "EUR")
   556  		charges := p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 10)
   557  		chargeLoyaltyMiles, _ := charges.GetByType("loyalty.miles")
   558  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "95 expected to be 530 miles")
   559  
   560  		chargeMain, found := charges.GetByType(domain.ChargeTypeMain)
   561  		assert.True(t, found)
   562  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price)
   563  	})
   564  
   565  	t.Run("10 items by 9.49 EUR and 53 Miles", func(t *testing.T) {
   566  		t.Parallel()
   567  
   568  		p := Saleable{
   569  			ActivePrice: PriceInfo{
   570  				Default: domain.NewFromFloat(9.49, "EUR"),
   571  			},
   572  			ActiveLoyaltyPrice: &LoyaltyPriceInfo{
   573  				Type:             "loyalty.miles",
   574  				MaxPointsToSpent: nil,
   575  				MinPointsToSpent: *new(big.Float).SetInt64(10),
   576  				Default:          domain.NewFromInt(53, 1, "Miles"), // one mile = 5.58482613 EUR
   577  			},
   578  		}
   579  
   580  		wishedMax := NewWishedToPay()
   581  		wishedMax.Add("loyalty.miles", domain.NewFromInt(math.MaxInt64, 1, "Miles"))
   582  
   583  		expectedMilesMax := int64(530)
   584  		newValue := domain.NewFromFloat(94.9, "EUR")
   585  		charges := p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 10)
   586  		chargeLoyaltyMiles, _ := charges.GetByType("loyalty.miles")
   587  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "94.9 expected to be 530 miles")
   588  
   589  		chargeMain, found := charges.GetByType(domain.ChargeTypeMain)
   590  		assert.True(t, found)
   591  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price)
   592  	})
   593  
   594  	t.Run("item for 1.50 EUR or 450 Miles", func(t *testing.T) {
   595  		t.Parallel()
   596  
   597  		p := Saleable{
   598  			ActivePrice: PriceInfo{
   599  				Default: domain.NewFromFloat(1.50, "EUR"),
   600  			},
   601  			ActiveLoyaltyPrice: &LoyaltyPriceInfo{
   602  				Type:             "loyalty.miles",
   603  				MaxPointsToSpent: nil,
   604  				MinPointsToSpent: *new(big.Float).SetInt64(30),
   605  				Default:          domain.NewFromInt(450, 1, "Miles"),
   606  			},
   607  		}
   608  
   609  		wishedMax := NewWishedToPay()
   610  		wishedMax.Add("loyalty.miles", domain.NewFromInt(math.MaxInt64, 1, "Miles"))
   611  
   612  		expectedMilesMax := int64(450)
   613  		newValue := domain.NewFromFloat(1.5, "EUR")
   614  		charges := p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 1)
   615  		chargeLoyaltyMiles, _ := charges.GetByType("loyalty.miles")
   616  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "1.5 expected to be 450 miles")
   617  
   618  		chargeMain, found := charges.GetByType(domain.ChargeTypeMain)
   619  		require.True(t, found)
   620  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price, "should be nothing left to pay")
   621  
   622  		expectedMilesMax = int64(300)
   623  		newValue = domain.NewFromFloat(1.00, "EUR")
   624  		charges = p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 1)
   625  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
   626  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "1 expected to be 300 miles")
   627  
   628  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   629  		assert.True(t, found)
   630  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price, "should be nothing left to pay")
   631  
   632  		expectedMilesMax = int64(900)
   633  		newValue = domain.NewFromFloat(3.00, "EUR") // reduced price still calculated perfectly
   634  		charges = p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 3)
   635  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
   636  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "3 expected to be 900 miles")
   637  
   638  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   639  		assert.True(t, found)
   640  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price, "should be nothing left to pay")
   641  
   642  		expectedMiles := int64(90)
   643  
   644  		wished := NewWishedToPay()
   645  		wished.Add("loyalty.miles", domain.NewZero("Miles"))
   646  
   647  		newValue = domain.NewFromFloat(4.50, "EUR")
   648  		charges = p.GetLoyaltyChargeSplit(&newValue, &wished, 3)
   649  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
   650  		assert.Equal(t, domain.NewFromInt(expectedMiles, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "you cannot pay less then 90 Miles")
   651  
   652  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   653  		assert.True(t, found)
   654  		assert.Equal(t, domain.NewFromFloat(4.200000123, "EUR").GetPayable(), chargeMain.Price.GetPayable(), fmt.Sprintf("should de left to pay 4.20, but got %f", chargeMain.Price.FloatAmount()))
   655  	})
   656  
   657  	t.Run("item for 1.49 EUR or 447 Miles", func(t *testing.T) {
   658  		t.Parallel()
   659  
   660  		p := Saleable{
   661  			ActivePrice: PriceInfo{
   662  				Default: domain.NewFromFloat(1.49, "EUR"),
   663  			},
   664  			ActiveLoyaltyPrice: &LoyaltyPriceInfo{
   665  				Type:             "loyalty.miles",
   666  				MaxPointsToSpent: nil,
   667  				MinPointsToSpent: *new(big.Float).SetInt64(30),
   668  				Default:          domain.NewFromInt(447, 1, "Miles"),
   669  			},
   670  		}
   671  
   672  		wishedMax := NewWishedToPay()
   673  		wishedMax.Add("loyalty.miles", domain.NewFromInt(math.MaxInt64, 1, "Miles"))
   674  
   675  		// 1.49 would be 447 miles - so  we pay 447 miles we expect to pay everything in miles.
   676  		expectedMilesMax := int64(447)
   677  		newValue := domain.NewFromFloat(1.49, "EUR")
   678  		charges := p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 1)
   679  		chargeLoyaltyMiles, _ := charges.GetByType("loyalty.miles")
   680  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "1.49 expected to be 447 miles")
   681  
   682  		chargeMain, found := charges.GetByType(domain.ChargeTypeMain)
   683  		require.True(t, found)
   684  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price, "should be nothing left to pay")
   685  
   686  		expectedMilesMax = int64(300)
   687  		newValue = domain.NewFromFloat(1.00, "EUR")
   688  		charges = p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 1)
   689  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
   690  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "1 expected to be 300 miles")
   691  
   692  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   693  		assert.True(t, found)
   694  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price, "should be nothing left to pay")
   695  
   696  		expectedMilesMax = int64(1341)
   697  		newValue = domain.NewFromFloat(4.47, "EUR")
   698  		charges = p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 1)
   699  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
   700  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "4.47 expected to be 1341 miles")
   701  
   702  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   703  		assert.True(t, found)
   704  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price, "should be nothing left to pay")
   705  
   706  		expectedMiles := int64(90)
   707  
   708  		wished := NewWishedToPay()
   709  		wished.Add("loyalty.miles", domain.NewZero("Miles"))
   710  
   711  		newValue = domain.NewFromFloat(4.00, "EUR")
   712  		charges = p.GetLoyaltyChargeSplit(&newValue, &wished, 3)
   713  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
   714  		assert.Equal(t, domain.NewFromInt(expectedMiles, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "you cannot pay less then 90 Miles")
   715  
   716  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   717  		assert.True(t, found)
   718  		assert.Equal(t, domain.NewFromFloat(3.70000123, "EUR").GetPayable(), chargeMain.Price.GetPayable(), fmt.Sprintf("should de left to pay 3.7, but got %f", chargeMain.Price.FloatAmount()))
   719  	})
   720  
   721  	t.Run("item for 1.01 EUR or 303 Miles", func(t *testing.T) {
   722  		t.Parallel()
   723  
   724  		p := Saleable{
   725  			ActivePrice: PriceInfo{
   726  				Default: domain.NewFromFloat(1.01, "EUR"),
   727  			},
   728  			ActiveLoyaltyPrice: &LoyaltyPriceInfo{
   729  				Type:             "loyalty.miles",
   730  				MaxPointsToSpent: nil,
   731  				MinPointsToSpent: *new(big.Float).SetInt64(30),
   732  				Default:          domain.NewFromInt(303, 1, "Miles"),
   733  			},
   734  		}
   735  
   736  		wishedMax := NewWishedToPay()
   737  		wishedMax.Add("loyalty.miles", domain.NewFromInt(math.MaxInt64, 1, "Miles"))
   738  
   739  		// 1.01 would be 303 miles - so  we pay 303 miles we expect to pay everything in miles.
   740  		expectedMilesMax := int64(303)
   741  		newValue := domain.NewFromFloat(1.01, "EUR")
   742  		charges := p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 1)
   743  		chargeLoyaltyMiles, _ := charges.GetByType("loyalty.miles")
   744  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "1.01 expected to be 303 miles")
   745  
   746  		chargeMain, found := charges.GetByType(domain.ChargeTypeMain)
   747  		require.True(t, found)
   748  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price, "should be nothing left to pay")
   749  
   750  		// 1.0 would be 300 miles - so  we pay 300 miles we expect to pay everything in miles.
   751  		expectedMilesMax = int64(300)
   752  		newValue = domain.NewFromFloat(1.00, "EUR")
   753  		charges = p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 1)
   754  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
   755  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "1 expected to be 300 miles")
   756  
   757  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   758  		assert.True(t, found)
   759  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price, "should be nothing left to pay")
   760  
   761  		expectedMilesMax = int64(909)
   762  		newValue = domain.NewFromFloat(3.03, "EUR")
   763  		charges = p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 1)
   764  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
   765  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "3.03 expected to be 909 miles")
   766  
   767  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   768  		assert.True(t, found)
   769  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price, "should be nothing left to pay")
   770  
   771  		expectedMiles := int64(90)
   772  
   773  		wished := NewWishedToPay()
   774  		wished.Add("loyalty.miles", domain.NewZero("Miles"))
   775  
   776  		newValue = domain.NewFromFloat(3.03, "EUR")
   777  		charges = p.GetLoyaltyChargeSplit(&newValue, &wished, 3)
   778  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
   779  		assert.Equal(t, domain.NewFromInt(expectedMiles, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "you cannot pay less then 90 Miles")
   780  
   781  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   782  		assert.True(t, found)
   783  		assert.Equal(t, domain.NewFromFloat(2.729999999, "EUR").GetPayable(), chargeMain.Price.GetPayable(), fmt.Sprintf("should de left to pay 2.73, but got %f", chargeMain.Price.FloatAmount()))
   784  	})
   785  
   786  	t.Run("item for 1.99 EUR or 597 Miles", func(t *testing.T) {
   787  		t.Parallel()
   788  
   789  		p := Saleable{
   790  			ActivePrice: PriceInfo{
   791  				Default: domain.NewFromFloat(1.99, "EUR"),
   792  			},
   793  			ActiveLoyaltyPrice: &LoyaltyPriceInfo{
   794  				Type:             "loyalty.miles",
   795  				MaxPointsToSpent: nil,
   796  				MinPointsToSpent: *new(big.Float).SetInt64(30),
   797  				Default:          domain.NewFromInt(597, 1, "Miles"), // 300 miles = 1 EUR
   798  			},
   799  		}
   800  
   801  		wishedMax := NewWishedToPay()
   802  		wishedMax.Add("loyalty.miles", domain.NewFromInt(math.MaxInt64, 1, "Miles"))
   803  
   804  		// 1.99 would be 597 miles - so  we pay 597 miles we expect to pay everything in miles.
   805  		expectedMilesMax := int64(597)
   806  		newValue := domain.NewFromFloat(1.99, "EUR")
   807  		charges := p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 1)
   808  		chargeLoyaltyMiles, _ := charges.GetByType("loyalty.miles")
   809  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "1.99 expected to be 597 miles")
   810  
   811  		chargeMain, found := charges.GetByType(domain.ChargeTypeMain)
   812  		require.True(t, found)
   813  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price, "should be nothing left to pay")
   814  
   815  		expectedMilesMax = int64(300)
   816  		newValue = domain.NewFromFloat(1.00, "EUR")
   817  		charges = p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 1)
   818  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
   819  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "1 expected to be 300 miles")
   820  
   821  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   822  		assert.True(t, found)
   823  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price, "should be nothing left to pay")
   824  
   825  		expectedMilesMax = int64(1791)
   826  		newValue = domain.NewFromFloat(5.97, "EUR")
   827  		charges = p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 1)
   828  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
   829  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "5.97 expected to be 1791 miles")
   830  
   831  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   832  		assert.True(t, found)
   833  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price, "should be nothing left to pay")
   834  
   835  		expectedMiles := int64(90)
   836  
   837  		wished := NewWishedToPay()
   838  		wished.Add("loyalty.miles", domain.NewZero("Miles"))
   839  
   840  		newValue = domain.NewFromFloat(5.97, "EUR")
   841  		charges = p.GetLoyaltyChargeSplit(&newValue, &wished, 3)
   842  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
   843  		assert.Equal(t, domain.NewFromInt(expectedMiles, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "you cannot pay less then 90 Miles")
   844  
   845  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   846  		assert.True(t, found)
   847  		assert.Equal(t, domain.NewFromFloat(5.6666666, "EUR").GetPayable(), chargeMain.Price.GetPayable(), fmt.Sprintf("should de left to pay 5.67, but got %f", chargeMain.Price.FloatAmount()))
   848  	})
   849  
   850  	t.Run("item for 1.50 EUR or 450 Miles with max 90", func(t *testing.T) {
   851  		t.Parallel()
   852  
   853  		p := Saleable{
   854  			ActivePrice: PriceInfo{
   855  				Default: domain.NewFromFloat(1.50, "EUR"),
   856  			},
   857  			ActiveLoyaltyPrice: &LoyaltyPriceInfo{
   858  				Type:             "loyalty.miles",
   859  				MaxPointsToSpent: new(big.Float).SetInt64(90),
   860  				MinPointsToSpent: *new(big.Float).SetInt64(30),
   861  				Default:          domain.NewFromInt(450, 1, "Miles"),
   862  			},
   863  		}
   864  
   865  		wishedMax := NewWishedToPay()
   866  		wishedMax.Add("loyalty.miles", domain.NewFromInt(math.MaxInt64, 1, "Miles")) // we wish to pay everything in miles
   867  
   868  		expectedMilesMax := int64(90)
   869  		newValue := domain.NewFromFloat(1.5, "EUR")
   870  		charges := p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 1)
   871  		chargeLoyaltyMiles, _ := charges.GetByType("loyalty.miles")
   872  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount())
   873  
   874  		chargeMain, found := charges.GetByType(domain.ChargeTypeMain)
   875  		require.True(t, found)
   876  		assert.Equal(t, domain.NewFromFloat(1.20, "EUR").GetPayable(), chargeMain.Price.GetPayable(), fmt.Sprintf("should be left to pay 1.20 but got %f", chargeMain.Price.FloatAmount()))
   877  
   878  		expectedMilesMax = int64(90)
   879  		newValue = domain.NewFromFloat(1.00, "EUR")
   880  		charges = p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 1)
   881  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
   882  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount())
   883  
   884  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   885  		assert.True(t, found)
   886  		assert.Equal(t, domain.NewFromFloat(0.70, "EUR").GetPayable(), chargeMain.Price.GetPayable(), fmt.Sprintf("should be left to pay 1.20 but got %f", chargeMain.Price.FloatAmount()))
   887  
   888  		expectedMilesMax = int64(270)
   889  		newValue = domain.NewFromFloat(3.00, "EUR") // reduced price still calculated perfectly
   890  		charges = p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 3)
   891  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
   892  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount())
   893  
   894  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   895  		assert.True(t, found)
   896  		assert.Equal(t, domain.NewFromFloat(2.10, "EUR").GetPayable(), chargeMain.Price.GetPayable(), fmt.Sprintf("should be left to pay 2.10 but got %f", chargeMain.Price.FloatAmount()))
   897  	})
   898  
   899  	t.Run("item for 1.50 EUR or 2 Miles", func(t *testing.T) {
   900  		t.Parallel()
   901  
   902  		p := Saleable{
   903  			ActivePrice: PriceInfo{
   904  				Default: domain.NewFromFloat(1.50, "EUR"),
   905  			},
   906  			ActiveLoyaltyPrice: &LoyaltyPriceInfo{
   907  				Type:             "loyalty.miles",
   908  				MaxPointsToSpent: nil,
   909  				MinPointsToSpent: *new(big.Float).SetInt64(1),
   910  				//Default:          domain.NewFromInt(2, 1, "Miles"), // Should come unrounded!!!
   911  				Default: domain.NewFromFloat(1.5, "Miles"),
   912  			},
   913  		}
   914  
   915  		wishedMax := NewWishedToPay()
   916  		wishedMax.Add("loyalty.miles", domain.NewFromInt(math.MaxInt64, 1, "Miles"))
   917  
   918  		expectedMilesMax := int64(2)
   919  		newValue := domain.NewFromFloat(1.5, "EUR")
   920  		charges := p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 1)
   921  		chargeLoyaltyMiles, _ := charges.GetByType("loyalty.miles")
   922  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "1.5 expected to be 2 miles")
   923  
   924  		chargeMain, found := charges.GetByType(domain.ChargeTypeMain)
   925  		require.True(t, found)
   926  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price, "should be nothing left to pay")
   927  
   928  		expectedMilesMax = int64(1)
   929  		newValue = domain.NewFromFloat(1.00, "EUR")
   930  		charges = p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 1)
   931  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
   932  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "1 expected to be 1 mile")
   933  
   934  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   935  		assert.True(t, found)
   936  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price, "should be nothing left to pay")
   937  
   938  		expectedMilesMax = int64(5)
   939  		newValue = domain.NewFromFloat(4.50, "EUR")
   940  		charges = p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 3)
   941  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
   942  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "4.50 expected to be 5 miles")
   943  
   944  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   945  		assert.True(t, found)
   946  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price, "should be nothing left to pay")
   947  
   948  		expectedMiles := int64(3)
   949  
   950  		wished := NewWishedToPay()
   951  		wished.Add("loyalty.miles", domain.NewZero("Miles"))
   952  
   953  		newValue = domain.NewFromFloat(4.10, "EUR")
   954  		charges = p.GetLoyaltyChargeSplit(&newValue, &wished, 3)
   955  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
   956  		assert.Equal(t, domain.NewFromInt(expectedMiles, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "you cannot pay less then 3 Miles")
   957  
   958  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   959  		assert.True(t, found)
   960  		assert.Equal(t, domain.NewFromFloat(1.10, "EUR").GetPayable(), chargeMain.Price.GetPayable(), fmt.Sprintf("should de left to pay 1.10, but got %f", chargeMain.Price.FloatAmount()))
   961  	})
   962  
   963  	t.Run("item for 1.49 EUR or 1 Mile", func(t *testing.T) {
   964  		t.Parallel()
   965  
   966  		p := Saleable{
   967  			ActivePrice: PriceInfo{
   968  				Default: domain.NewFromFloat(1.49, "EUR"),
   969  			},
   970  			ActiveLoyaltyPrice: &LoyaltyPriceInfo{
   971  				Type:             "loyalty.miles",
   972  				MaxPointsToSpent: nil,
   973  				MinPointsToSpent: *new(big.Float).SetInt64(1),
   974  				//Default:          domain.NewFromInt(447, 1, "Miles"), // Should come unrounded!!!
   975  				Default: domain.NewFromFloat(1.49, "Miles"),
   976  			},
   977  		}
   978  
   979  		wishedMax := NewWishedToPay()
   980  		wishedMax.Add("loyalty.miles", domain.NewFromInt(math.MaxInt64, 1, "Miles"))
   981  
   982  		expectedMilesMax := int64(1)
   983  		newValue := domain.NewFromFloat(1.49, "EUR")
   984  		charges := p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 1)
   985  		chargeLoyaltyMiles, _ := charges.GetByType("loyalty.miles")
   986  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "1.49 expected to be 1 miles")
   987  
   988  		chargeMain, found := charges.GetByType(domain.ChargeTypeMain)
   989  		require.True(t, found)
   990  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price, "should be nothing left to pay")
   991  
   992  		expectedMilesMax = int64(1)
   993  		newValue = domain.NewFromFloat(1.00, "EUR")
   994  		charges = p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 1)
   995  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
   996  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "1 expected to be 300 miles")
   997  
   998  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
   999  		assert.True(t, found)
  1000  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price, "should be nothing left to pay")
  1001  
  1002  		expectedMilesMax = int64(4)
  1003  		newValue = domain.NewFromFloat(4.47, "EUR")
  1004  		charges = p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 3)
  1005  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
  1006  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "4.47 expected to be 4 miles")
  1007  
  1008  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
  1009  		assert.True(t, found)
  1010  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price, "should be nothing left to pay")
  1011  
  1012  		expectedMiles := int64(3)
  1013  
  1014  		wished := NewWishedToPay()
  1015  		wished.Add("loyalty.miles", domain.NewZero("Miles"))
  1016  
  1017  		newValue = domain.NewFromFloat(3.47, "EUR")
  1018  		charges = p.GetLoyaltyChargeSplit(&newValue, &wished, 3)
  1019  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
  1020  		assert.Equal(t, domain.NewFromInt(expectedMiles, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "you cannot pay less then 3 Miles")
  1021  
  1022  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
  1023  		assert.True(t, found)
  1024  		assert.Equal(t, domain.NewZero("EUR").GetPayable(), chargeMain.Price.GetPayable(), fmt.Sprintf("we should gift 0.47 cents in this case, but got %f to pay", chargeMain.Price.FloatAmount()))
  1025  	})
  1026  
  1027  	t.Run("item for 1.01 EUR or 1 Mile", func(t *testing.T) {
  1028  		t.Parallel()
  1029  
  1030  		p := Saleable{
  1031  			ActivePrice: PriceInfo{
  1032  				Default: domain.NewFromFloat(1.01, "EUR"),
  1033  			},
  1034  			ActiveLoyaltyPrice: &LoyaltyPriceInfo{
  1035  				Type:             "loyalty.miles",
  1036  				MaxPointsToSpent: nil,
  1037  				MinPointsToSpent: *new(big.Float).SetInt64(1),
  1038  				//Default:          domain.NewFromInt(303, 1, "Miles"), // Should come unrounded!!!
  1039  				Default: domain.NewFromFloat(1.01, "Miles"),
  1040  			},
  1041  		}
  1042  
  1043  		wishedMax := NewWishedToPay()
  1044  		wishedMax.Add("loyalty.miles", domain.NewFromInt(math.MaxInt64, 1, "Miles"))
  1045  
  1046  		expectedMilesMax := int64(1)
  1047  		newValue := domain.NewFromFloat(1.01, "EUR")
  1048  		charges := p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 1)
  1049  		chargeLoyaltyMiles, _ := charges.GetByType("loyalty.miles")
  1050  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "1.01 expected to be 1 mile")
  1051  
  1052  		chargeMain, found := charges.GetByType(domain.ChargeTypeMain)
  1053  		require.True(t, found)
  1054  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price, "should be nothing left to pay")
  1055  
  1056  		expectedMilesMax = int64(1)
  1057  		newValue = domain.NewFromFloat(1.00, "EUR")
  1058  		charges = p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 1)
  1059  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
  1060  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "1 expected to be 1 mile")
  1061  
  1062  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
  1063  		assert.True(t, found)
  1064  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price, "should be nothing left to pay")
  1065  
  1066  		expectedMilesMax = int64(3)
  1067  		newValue = domain.NewFromFloat(3.03, "EUR")
  1068  		charges = p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 3)
  1069  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
  1070  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "3.03 expected to be 3 miles")
  1071  
  1072  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
  1073  		assert.True(t, found)
  1074  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price, "should be nothing left to pay")
  1075  
  1076  		expectedMiles := int64(3)
  1077  
  1078  		wished := NewWishedToPay()
  1079  		wished.Add("loyalty.miles", domain.NewZero("Miles"))
  1080  
  1081  		newValue = domain.NewFromFloat(3.50, "EUR")
  1082  		charges = p.GetLoyaltyChargeSplit(&newValue, &wished, 3)
  1083  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
  1084  		assert.Equal(t, domain.NewFromInt(expectedMiles, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "you cannot pay less then 3 Miles")
  1085  
  1086  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
  1087  		assert.True(t, found)
  1088  		assert.Equal(t, domain.NewFromFloat(0.50, "EUR").GetPayable(), chargeMain.Price.GetPayable(), fmt.Sprintf("should de left to pay 0.50, but got %f", chargeMain.Price.FloatAmount()))
  1089  	})
  1090  
  1091  	t.Run("item for 1.99 EUR or 2 Miles", func(t *testing.T) {
  1092  		t.Parallel()
  1093  
  1094  		p := Saleable{
  1095  			ActivePrice: PriceInfo{
  1096  				Default: domain.NewFromFloat(1.99, "EUR"),
  1097  			},
  1098  			ActiveLoyaltyPrice: &LoyaltyPriceInfo{
  1099  				Type:             "loyalty.miles",
  1100  				MaxPointsToSpent: nil,
  1101  				MinPointsToSpent: *new(big.Float).SetInt64(1),
  1102  				Default:          domain.NewFromFloat(1.99, "Miles"),
  1103  			},
  1104  		}
  1105  
  1106  		wishedMax := NewWishedToPay()
  1107  		wishedMax.Add("loyalty.miles", domain.NewFromInt(math.MaxInt64, 1, "Miles"))
  1108  
  1109  		expectedMilesMax := int64(2)
  1110  		newValue := domain.NewFromFloat(1.99, "EUR")
  1111  		charges := p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 1)
  1112  		chargeLoyaltyMiles, _ := charges.GetByType("loyalty.miles")
  1113  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "1.01 expected to be 2 miles")
  1114  
  1115  		chargeMain, found := charges.GetByType(domain.ChargeTypeMain)
  1116  		require.True(t, found)
  1117  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price, "should be nothing left to pay")
  1118  
  1119  		expectedMilesMax = int64(1)
  1120  		newValue = domain.NewFromFloat(1.00, "EUR")
  1121  		charges = p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 1)
  1122  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
  1123  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "1 expected to be 1 mile")
  1124  
  1125  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
  1126  		assert.True(t, found)
  1127  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price, "should be nothing left to pay")
  1128  
  1129  		expectedMilesMax = int64(6)
  1130  		newValue = domain.NewFromFloat(5.97, "EUR")
  1131  		charges = p.GetLoyaltyChargeSplit(&newValue, &wishedMax, 3)
  1132  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
  1133  		assert.Equal(t, domain.NewFromInt(expectedMilesMax, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "5.97 expected to be 6 miles")
  1134  
  1135  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
  1136  		assert.True(t, found)
  1137  		assert.Equal(t, domain.NewFromInt(0, 1, "EUR"), chargeMain.Price, "should be nothing left to pay")
  1138  
  1139  		expectedMiles := int64(3)
  1140  
  1141  		wished := NewWishedToPay()
  1142  		wished.Add("loyalty.miles", domain.NewZero("Miles"))
  1143  
  1144  		newValue = domain.NewFromFloat(5.97, "EUR")
  1145  		charges = p.GetLoyaltyChargeSplit(&newValue, &wished, 3)
  1146  		chargeLoyaltyMiles, _ = charges.GetByType("loyalty.miles")
  1147  		assert.Equal(t, domain.NewFromInt(expectedMiles, 1, "Miles").FloatAmount(), chargeLoyaltyMiles.Price.FloatAmount(), "you cannot pay less then 3 Miles")
  1148  
  1149  		chargeMain, found = charges.GetByType(domain.ChargeTypeMain)
  1150  		assert.True(t, found)
  1151  		assert.Equal(t, domain.NewFromFloat(2.97, "EUR").GetPayable(), chargeMain.Price.GetPayable(), fmt.Sprintf("should de left to pay 2.97, but got %f", chargeMain.Price.FloatAmount()))
  1152  	})
  1153  }
  1154  
  1155  func TestSaleable_GetLoyaltyChargeSplitIgnoreMin(t *testing.T) {
  1156  
  1157  	p := Saleable{
  1158  		ActivePrice: PriceInfo{
  1159  			// 100EUR value
  1160  			Default: domain.NewFromInt(100, 1, "EUR"),
  1161  		},
  1162  		LoyaltyPrices: []LoyaltyPriceInfo{
  1163  			{
  1164  				Type:             "loyalty.miles",
  1165  				MaxPointsToSpent: new(big.Float).SetInt64(50),
  1166  				// 10 is the minimum to pay in miles (=20EUR value)
  1167  				MinPointsToSpent: *new(big.Float).SetInt64(10),
  1168  				// 50 miles == 100EUR meaning 1Mile = 2EUR
  1169  				Default: domain.NewFromInt(50, 1, "Miles"),
  1170  			},
  1171  		},
  1172  	}
  1173  
  1174  	// Test default charges (the min price in points should be evaluated)
  1175  	charges := p.GetLoyaltyChargeSplitIgnoreMin(nil, nil, 1)
  1176  
  1177  	_, found := charges.GetByType("loyalty.miles")
  1178  	assert.False(t, found)
  1179  
  1180  	chargeMain, found := charges.GetByType(domain.ChargeTypeMain)
  1181  	assert.True(t, found)
  1182  	assert.Equal(t, domain.NewFromInt(100, 1, "EUR"), chargeMain.Price)
  1183  }
  1184  
  1185  func TestSaleable_GetLoyaltyEarningByType(t *testing.T) {
  1186  
  1187  	tests := []struct {
  1188  		name            string
  1189  		loyaltyEarnings []LoyaltyEarningInfo
  1190  		leType          string
  1191  		wantBool        bool
  1192  		wantEarning     *LoyaltyEarningInfo
  1193  	}{
  1194  		{
  1195  			name:            "empty loyalty info",
  1196  			loyaltyEarnings: nil,
  1197  			leType:          "dontCare",
  1198  			wantBool:        false,
  1199  			wantEarning:     nil,
  1200  		},
  1201  		{
  1202  			name: "matching loyalty info",
  1203  			loyaltyEarnings: []LoyaltyEarningInfo{
  1204  				{
  1205  					Type:    "MilesAndMore",
  1206  					Default: domain.NewFromFloat(23.23, "NZD"),
  1207  				},
  1208  				{
  1209  					Type:    "TheOtherThing",
  1210  					Default: domain.NewFromFloat(24.24, "NZD"),
  1211  				},
  1212  			},
  1213  			leType:   "MilesAndMore",
  1214  			wantBool: true,
  1215  			wantEarning: &LoyaltyEarningInfo{
  1216  				Type:    "MilesAndMore",
  1217  				Default: domain.NewFromFloat(23.23, "NZD"),
  1218  			},
  1219  		},
  1220  		{
  1221  			name: "no matching loyalty info",
  1222  			loyaltyEarnings: []LoyaltyEarningInfo{
  1223  				{
  1224  					Type:    "MilesAndMoreX",
  1225  					Default: domain.NewFromFloat(23.23, "NZD"),
  1226  				},
  1227  				{
  1228  					Type:    "TheOtherThing",
  1229  					Default: domain.NewFromFloat(24.24, "NZD"),
  1230  				},
  1231  			},
  1232  			leType:      "MilesAndMore",
  1233  			wantBool:    false,
  1234  			wantEarning: nil,
  1235  		},
  1236  	}
  1237  
  1238  	for _, tt := range tests {
  1239  		saleable := new(Saleable)
  1240  		saleable.LoyaltyEarnings = tt.loyaltyEarnings
  1241  
  1242  		resultEarning, resultBool := saleable.GetLoyaltyEarningByType(tt.leType)
  1243  		assert.Equal(t, tt.wantBool, resultBool, tt.name)
  1244  		assert.Equal(t, tt.wantEarning, resultEarning, tt.name)
  1245  	}
  1246  
  1247  }
  1248  
  1249  func TestBasicProductData_IsInStockForDeliveryCode(t *testing.T) {
  1250  	t.Parallel()
  1251  
  1252  	t.Run("when in stock for delivery code then return true", func(t *testing.T) {
  1253  		t.Parallel()
  1254  
  1255  		product := BasicProductData{
  1256  			Stock: []Stock{
  1257  				{
  1258  					InStock:      true,
  1259  					DeliveryCode: "test",
  1260  				},
  1261  				{
  1262  					InStock:      false,
  1263  					DeliveryCode: "not this one",
  1264  				},
  1265  			},
  1266  		}
  1267  
  1268  		result := product.IsInStockForDeliveryCode("test")
  1269  
  1270  		assert.True(t, result)
  1271  	})
  1272  
  1273  	t.Run("when not in stock for delivery code then return false", func(t *testing.T) {
  1274  		t.Parallel()
  1275  
  1276  		product := BasicProductData{
  1277  			Stock: []Stock{
  1278  				{
  1279  					InStock:      true,
  1280  					DeliveryCode: "not this one",
  1281  				},
  1282  				{
  1283  					InStock:      false,
  1284  					DeliveryCode: "test",
  1285  				},
  1286  			},
  1287  		}
  1288  
  1289  		result := product.IsInStockForDeliveryCode("test")
  1290  
  1291  		assert.False(t, result)
  1292  	})
  1293  
  1294  	t.Run("when delivery code not found return false", func(t *testing.T) {
  1295  		t.Parallel()
  1296  
  1297  		product := BasicProductData{
  1298  			Stock: []Stock{
  1299  				{
  1300  					InStock:      true,
  1301  					DeliveryCode: "not this one",
  1302  				},
  1303  				{
  1304  					InStock:      false,
  1305  					DeliveryCode: "nope",
  1306  				},
  1307  			},
  1308  		}
  1309  
  1310  		result := product.IsInStockForDeliveryCode("test")
  1311  
  1312  		assert.False(t, result)
  1313  	})
  1314  }
  1315  
  1316  func TestSaleable_GetLoyaltyPriceByType(t *testing.T) {
  1317  	t.Parallel()
  1318  
  1319  	t.Run("returns loyalty from active loyalty price", func(t *testing.T) {
  1320  		t.Parallel()
  1321  
  1322  		activeLoyaltyPrice := LoyaltyPriceInfo{
  1323  			Type:    "valid_type",
  1324  			Default: domain.NewFromFloat(10.00, "LOYALTY"),
  1325  		}
  1326  
  1327  		availablePrice1 := LoyaltyPriceInfo{
  1328  			Type:    "valid_type",
  1329  			Default: domain.NewFromFloat(5.00, "LOYALTY"),
  1330  		}
  1331  
  1332  		availablePrice2 := LoyaltyPriceInfo{
  1333  			Type:    "invalid_type",
  1334  			Default: domain.NewFromFloat(100.00, "LOYALTY"),
  1335  		}
  1336  
  1337  		saleable := Saleable{
  1338  			ActiveLoyaltyPrice: &activeLoyaltyPrice,
  1339  			LoyaltyPrices: []LoyaltyPriceInfo{
  1340  				availablePrice1,
  1341  				availablePrice2,
  1342  			},
  1343  		}
  1344  
  1345  		resultPrice, found := saleable.GetLoyaltyPriceByType("valid_type")
  1346  		require.True(t, found)
  1347  		assert.Equal(t, activeLoyaltyPrice, *resultPrice)
  1348  	})
  1349  
  1350  	t.Run("returns loyalty from available loyalty prices", func(t *testing.T) {
  1351  		t.Parallel()
  1352  
  1353  		activeLoyaltyPrice := LoyaltyPriceInfo{
  1354  			Type:    "invalid_type",
  1355  			Default: domain.NewFromFloat(10.00, "LOYALTY"),
  1356  		}
  1357  
  1358  		availablePrice1 := LoyaltyPriceInfo{
  1359  			Type:    "valid_type",
  1360  			Default: domain.NewFromFloat(5.00, "LOYALTY"),
  1361  		}
  1362  
  1363  		availablePrice2 := LoyaltyPriceInfo{
  1364  			Type:    "invalid_type",
  1365  			Default: domain.NewFromFloat(100.00, "LOYALTY"),
  1366  		}
  1367  
  1368  		saleable := Saleable{
  1369  			ActiveLoyaltyPrice: &activeLoyaltyPrice,
  1370  			LoyaltyPrices: []LoyaltyPriceInfo{
  1371  				availablePrice1,
  1372  				availablePrice2,
  1373  			},
  1374  		}
  1375  
  1376  		resultPrice, found := saleable.GetLoyaltyPriceByType("valid_type")
  1377  		require.True(t, found)
  1378  		assert.Equal(t, availablePrice1, *resultPrice)
  1379  	})
  1380  
  1381  	t.Run("returns loyalty from available loyalty prices when active is nil", func(t *testing.T) {
  1382  		t.Parallel()
  1383  
  1384  		availablePrice1 := LoyaltyPriceInfo{
  1385  			Type:    "valid_type",
  1386  			Default: domain.NewFromFloat(5.00, "LOYALTY"),
  1387  		}
  1388  
  1389  		availablePrice2 := LoyaltyPriceInfo{
  1390  			Type:    "invalid_type",
  1391  			Default: domain.NewFromFloat(100.00, "LOYALTY"),
  1392  		}
  1393  
  1394  		saleable := Saleable{
  1395  			ActiveLoyaltyPrice: nil,
  1396  			LoyaltyPrices: []LoyaltyPriceInfo{
  1397  				availablePrice1,
  1398  				availablePrice2,
  1399  			},
  1400  		}
  1401  
  1402  		resultPrice, found := saleable.GetLoyaltyPriceByType("valid_type")
  1403  		require.True(t, found)
  1404  		assert.Equal(t, availablePrice1, *resultPrice)
  1405  	})
  1406  
  1407  	t.Run("returns loyalty from available loyalty prices", func(t *testing.T) {
  1408  		t.Parallel()
  1409  
  1410  		activeLoyaltyPrice := LoyaltyPriceInfo{
  1411  			Type:    "invalid_type",
  1412  			Default: domain.NewFromFloat(10.00, "LOYALTY"),
  1413  		}
  1414  
  1415  		availablePrice1 := LoyaltyPriceInfo{
  1416  			Type:    "invalid_type",
  1417  			Default: domain.NewFromFloat(5.00, "LOYALTY"),
  1418  		}
  1419  
  1420  		availablePrice2 := LoyaltyPriceInfo{
  1421  			Type:    "invalid_type",
  1422  			Default: domain.NewFromFloat(100.00, "LOYALTY"),
  1423  		}
  1424  
  1425  		saleable := Saleable{
  1426  			ActiveLoyaltyPrice: &activeLoyaltyPrice,
  1427  			LoyaltyPrices: []LoyaltyPriceInfo{
  1428  				availablePrice1,
  1429  				availablePrice2,
  1430  			},
  1431  		}
  1432  
  1433  		resultPrice, found := saleable.GetLoyaltyPriceByType("valid_type")
  1434  		require.False(t, found)
  1435  		assert.Nil(t, resultPrice)
  1436  	})
  1437  }