/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>
*/

package utils

import (
	"reflect"
	"strings"
	"testing"
	"time"

	"github.com/cgrates/cron"
	"github.com/ericlagergren/decimal"
)

func TestLibratesTenantID(t *testing.T) {
	rp := &RateProfile{
		Tenant: "tenant",
		ID:     "testID",
	}
	expected := rp.Tenant + ":" + rp.ID
	received := rp.TenantID()
	if received != expected {
		t.Errorf("\nExpected: <%v>,\nReceived: <%v>", expected, received)
	}
}

func TestLibratesCompilerp(t *testing.T) {
	// empty struct
	rp := &RateProfile{}
	err := rp.Compile()
	if err != nil {
		t.Errorf("\nExpected: <%v>, \nReceived: <%v>", nil, err)
	}

	// non-empty
	fail := "shouldfail"
	rp.ID = "test"
	rp.Tenant = "tenant"
	rp.Rates = map[string]*Rate{
		"testKey1": {
			ID:              "ID1",
			ActivationTimes: fail,
		},
		"testKey2": {
			ID: "ID2",
		},
	}

	expected := "expected exactly 5 fields, found 1: [" + fail + "]"
	err = rp.Compile()

	if err == nil || err.Error() != expected {
		t.Errorf("\nExpected: <%v>, \nReceived: <%v>", expected, err)
	}
}

func TestLibratesUID(t *testing.T) {
	rt := &Rate{
		uID: "testString",
	}

	expected := "testString"
	received := rt.UID()

	if received != expected {
		t.Errorf("\nExpected: %q, \nReceived: %q", expected, received)
	}

}

func TestLibratesCompilert(t *testing.T) {
	rt := &Rate{
		ActivationTimes: EmptyString,
	}

	err := rt.Compile()

	if err != nil {
		t.Errorf("\nExpected: <%+v>, \nReceived: <%+v>", nil, err)
	}
}

func TestLibratesRunTimes(t *testing.T) {
	var (
		sTime     time.Time
		eTime     time.Time
		verbosity int
	)

	// memory leak test
	verbosity = 0

	rt := &Rate{}

	received, err := rt.RunTimes(sTime, eTime, verbosity)
	var expected [][]time.Time

	if err == nil || err != ErrMaxIterationsReached {
		t.Errorf("\nExpected: <%+v>, \nReceived: <%+v>", ErrMaxIterationsReached, err)
	}

	if !reflect.DeepEqual(received, expected) {
		t.Errorf("\nExpected: <%+v>, \nReceived: <%+v>", expected, received)
	}

	// aTime after eTime test
	schd, err := cron.ParseStandard("* * * * *")
	if err != nil {
		t.Errorf("\ndidn't expect error, got %v", err)
	}

	rt.sched = schd
	verbosity = 10
	eTime = sTime.Add(10 * time.Minute)

	received, err = rt.RunTimes(sTime, eTime, verbosity)

	if err != nil {
		t.Errorf("\ndidn't expect error, got %v", err)
	}

	if !reflect.DeepEqual(received, expected) {
		t.Errorf("\nExpected: <%+v>, \nReceived: <%+v>", expected, received)
	}

	// eTime before iTime test
	schd, err = cron.ParseStandard("* * 12 3 *")
	if err != nil {
		t.Errorf("\ndidn't expect error, got %v", err)
	}

	rt.sched = schd
	sTime, err = time.Parse(time.RFC3339, "2022-03-11T15:04:05Z")
	if err != nil {
		t.Errorf("\ndidn't expect error, got %v", err)
	}
	eTime = sTime.Add(24 * time.Hour)

	if received, err = rt.RunTimes(sTime, eTime, verbosity); err != nil {
		t.Error(err)
	}

	aT1, err := time.Parse(time.RFC3339, "2022-03-12T00:00:00Z")
	if err != nil {
		t.Errorf("\ndidn't expect error, got %v", err)
	}

	aT2, err := time.Parse(time.RFC3339, "2022-03-13T00:00:00Z")
	if err != nil {
		t.Errorf("\ndidn't expect error, got %v", err)
	}

	aTsl := make([]time.Time, 0)
	aTsl = append(aTsl, aT1, aT2)
	expected = append(expected, aTsl)

	if !reflect.DeepEqual(received, expected) {
		t.Errorf("\nExpected: <%+v>, \nReceived: <%+v>", expected, received)
	}

	//eTime after iTime
	schd, err = cron.ParseStandard("* * 12 3 *")
	if err != nil {
		t.Errorf("\ndidn't expect error, got %v", err)
	}

	rt.sched = schd
	sTime, err = time.Parse(time.RFC3339, "2022-03-11T15:04:05Z")
	if err != nil {
		t.Errorf("\ndidn't expect error, got %v", err)
	}
	eTime = sTime.Add(48 * time.Hour)

	received, err = rt.RunTimes(sTime, eTime, verbosity)

	if err != nil {
		t.Errorf("\ndidn't expect error, got %v", err)
	}

	if !reflect.DeepEqual(received, expected) {
		t.Errorf("\nExpected: <%+v>, \nReceived: <%+v>", expected, received)
	}
}

func TestLibratesCorrectCost(t *testing.T) {
	//CorrectCost does nothing in this case
	rPc := &RateProfileCost{
		Cost:    NewDecimal(1234, 3),
		MinCost: NewDecimal(1, 0),
		MaxCost: NewDecimal(2, 0),
		Altered: []string{},
	}

	expected := &RateProfileCost{
		Cost:    NewDecimal(1234, 3),
		MinCost: NewDecimal(1, 0),
		MaxCost: NewDecimal(2, 0),
		Altered: []string{},
	}
	rPc.CorrectCost(nil, "")

	if !rPc.Equals(expected) {
		t.Errorf("\nExpected: <%+v>, \nReceived: <%+v>", expected, rPc)
	}

	//CorrectCost rounds the cost
	expected = &RateProfileCost{
		Cost:    NewDecimal(12, 1),
		MinCost: NewDecimal(1, 0),
		MaxCost: NewDecimal(2, 0),
		Altered: []string{RoundingDecimals},
	}

	rPc.CorrectCost(IntPointer(2), MetaRoundingUp)

	if !rPc.Equals(expected) {
		t.Errorf("\nExpected: <%+v>, \nReceived: <%+v>", ToJSON(expected), ToJSON(rPc))
	}

	//CorrectCost assigns MaxCost to Cost when Cost > MaxCost
	expected = &RateProfileCost{
		Cost:    NewDecimal(2, 0),
		MinCost: NewDecimal(1, 0),
		MaxCost: NewDecimal(2, 0),
		Altered: []string{RoundingDecimals, MaxCost},
	}
	rPc.Cost = NewDecimal(234, 2)
	rPc.CorrectCost(nil, "")

	if !rPc.Equals(expected) {
		t.Errorf("\nExpected: <%+v>, \nReceived: <%+v>", expected, rPc)
	}

	//CorrectCost assigns MinCost to Cost when Cost < MinCost

	expected = &RateProfileCost{
		Cost:    NewDecimal(1, 0),
		MinCost: NewDecimal(1, 0),
		MaxCost: NewDecimal(2, 0),
		Altered: []string{RoundingDecimals, MaxCost, MinCost},
	}
	rPc.Cost = NewDecimal(12, 2)
	rPc.CorrectCost(nil, "")

	if !rPc.Equals(expected) {
		t.Errorf("\nExpected: <%+v>, \nReceived: <%+v>", expected, rPc)
	}
}

func TestRateProfileSort(t *testing.T) {
	minDecimal, err := NewDecimalFromUsage("1m")
	if err != nil {
		t.Error(err)
	}
	secDecimal, err := NewDecimalFromUsage("1s")
	if err != nil {
		t.Error(err)
	}
	rPrf := &RateProfile{
		Tenant: "cgrates.org",
		ID:     "RP1",
		Rates: map[string]*Rate{
			"RT_WEEK": {
				ID: "RT_WEEK",
				Weights: DynamicWeights{
					{
						Weight: 0,
					},
				},
				ActivationTimes: "* * * * 1-5",
				IntervalRates: []*IntervalRate{
					{
						IntervalStart: NewDecimal(0, 0),
						RecurrentFee:  NewDecimal(12, 2),
						Unit:          minDecimal,
						Increment:     minDecimal,
					},
					{
						IntervalStart: NewDecimal(int64(time.Minute), 0),
						RecurrentFee:  NewDecimal(6, 2),
						Unit:          minDecimal,
						Increment:     secDecimal,
					},
				},
			},
			"RT_Custom": {
				ID: "RT_Custom",
				Weights: DynamicWeights{
					{
						Weight: 0,
					},
				},
				ActivationTimes: "* * * * 1-5",
				IntervalRates: []*IntervalRate{
					{
						IntervalStart: NewDecimal(int64(time.Second), 0),
						RecurrentFee:  NewDecimal(12, 2),
						Unit:          minDecimal,
						Increment:     minDecimal,
					},
					{
						IntervalStart: NewDecimal(int64(time.Second), 0),
						RecurrentFee:  NewDecimal(19, 2),
						Unit:          minDecimal,
						Increment:     secDecimal,
					},
					{
						IntervalStart: NewDecimal(int64(15*time.Second), 0),
						RecurrentFee:  NewDecimal(4, 1),
						Unit:          minDecimal,
						Increment:     secDecimal,
					},
					{
						IntervalStart: NewDecimal(int64(10*time.Second), 0),
						RecurrentFee:  NewDecimal(27, 2),
						Unit:          minDecimal,
						Increment:     secDecimal,
					},
				},
			},
			"RT_WEEKEND": {
				ID: "RT_WEEKEND",
				Weights: DynamicWeights{
					{
						Weight: 10,
					},
				},
				ActivationTimes: "* * * * 0,6",
				IntervalRates: []*IntervalRate{
					{
						IntervalStart: NewDecimal(int64(10*time.Second), 0),
						RecurrentFee:  NewDecimal(6, 2),
						Unit:          minDecimal,
						Increment:     secDecimal,
					},
					{
						IntervalStart: NewDecimal(int64(time.Minute), 0),
						RecurrentFee:  NewDecimal(18, 2),
						Unit:          minDecimal,
						Increment:     secDecimal,
					},
					{
						IntervalStart: NewDecimal(int64(18*time.Second), 0),
						RecurrentFee:  NewDecimal(12, 2),
						Unit:          minDecimal,
						Increment:     secDecimal,
					},
				},
			},
			"RT_CHRISTMAS": {
				ID: "RT_CHRISTMAS",
				Weights: DynamicWeights{
					{
						Weight: 30,
					},
				},
				ActivationTimes: "* * 24 12 *",
				IntervalRates: []*IntervalRate{
					{
						IntervalStart: NewDecimal(0, 0),
						RecurrentFee:  NewDecimal(6, 2),
						Unit:          minDecimal,
						Increment:     secDecimal,
					},
				},
			},
		},
	}
	exp := &RateProfile{
		Tenant: "cgrates.org",
		ID:     "RP1",
		Rates: map[string]*Rate{
			"RT_WEEK": {
				ID: "RT_WEEK",
				Weights: DynamicWeights{
					{
						Weight: 0,
					},
				},
				ActivationTimes: "* * * * 1-5",
				IntervalRates: []*IntervalRate{
					{
						IntervalStart: NewDecimal(0, 0),
						RecurrentFee:  NewDecimal(12, 2),
						Unit:          minDecimal,
						Increment:     minDecimal,
					},
					{
						IntervalStart: NewDecimal(int64(time.Minute), 0),
						RecurrentFee:  NewDecimal(6, 2),
						Unit:          minDecimal,
						Increment:     secDecimal,
					},
				},
			},
			"RT_WEEKEND": {
				ID: "RT_WEEKEND",
				Weights: DynamicWeights{
					{
						Weight: 10,
					},
				},
				ActivationTimes: "* * * * 0,6",
				IntervalRates: []*IntervalRate{
					{
						IntervalStart: NewDecimal(int64(10*time.Second), 0),
						RecurrentFee:  NewDecimal(6, 2),
						Unit:          minDecimal,
						Increment:     secDecimal,
					},
					{
						IntervalStart: NewDecimal(int64(18*time.Second), 0),
						RecurrentFee:  NewDecimal(12, 2),
						Unit:          minDecimal,
						Increment:     secDecimal,
					},
					{
						IntervalStart: NewDecimal(int64(time.Minute), 0),
						RecurrentFee:  NewDecimal(18, 2),
						Unit:          minDecimal,
						Increment:     secDecimal,
					},
				},
			},
			"RT_Custom": {
				ID: "RT_Custom",
				Weights: DynamicWeights{
					{
						Weight: 0,
					},
				},
				ActivationTimes: "* * * * 1-5",
				IntervalRates: []*IntervalRate{
					{
						IntervalStart: NewDecimal(int64(time.Second), 0),
						RecurrentFee:  NewDecimal(12, 2),
						Unit:          minDecimal,
						Increment:     minDecimal,
					},
					{
						IntervalStart: NewDecimal(int64(time.Second), 0),
						RecurrentFee:  NewDecimal(19, 2),
						Unit:          minDecimal,
						Increment:     secDecimal,
					},
					{
						IntervalStart: NewDecimal(int64(10*time.Second), 0),
						RecurrentFee:  NewDecimal(27, 2),
						Unit:          minDecimal,
						Increment:     secDecimal,
					},
					{
						IntervalStart: NewDecimal(int64(15*time.Second), 0),
						RecurrentFee:  NewDecimal(4, 1),
						Unit:          minDecimal,
						Increment:     secDecimal,
					},
				},
			},
			"RT_CHRISTMAS": {
				ID: "RT_CHRISTMAS",
				Weights: DynamicWeights{
					{
						Weight: 30,
					},
				},
				ActivationTimes: "* * 24 12 *",
				IntervalRates: []*IntervalRate{
					{
						IntervalStart: NewDecimal(0, 0),
						RecurrentFee:  NewDecimal(6, 2),
						Unit:          minDecimal,
						Increment:     secDecimal,
					},
				},
			},
		},
	}
	rPrf.Sort()
	if !reflect.DeepEqual(rPrf, exp) {
		t.Errorf("Expected: %v,\n received: %v", ToJSON(exp), ToJSON(rPrf))
	}
}

func TestRateProfileCompile(t *testing.T) {
	rt := &RateProfile{
		Rates: map[string]*Rate{
			"randomVal1": {
				ID: "RT_CHRISTMAS",
				Weights: DynamicWeights{
					{
						Weight: 30,
					},
				},
				ActivationTimes: "* * 24 12 *",
			},
		},
		Tenant: "cgrates.org",
		ID:     "RTP1",
	}
	expectedATime, err := cron.ParseStandard("* * 24 12 *")
	if err != nil {
		t.Fatal(err)
	}
	expRt := &RateProfile{
		Rates: map[string]*Rate{
			"randomVal1": {
				ID: "RT_CHRISTMAS",
				Weights: DynamicWeights{
					{
						Weight: 30,
					},
				},
				ActivationTimes: "* * 24 12 *",
				sched:           expectedATime,
				uID:             ConcatenatedKey(rt.Tenant, rt.ID, "RT_CHRISTMAS"),
			},
		},
		Tenant: "cgrates.org",
		ID:     "RTP1",
	}
	if err := rt.Compile(); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(rt, expRt) {
		t.Errorf("Expected %+v, received %+v", ToJSON(expRt), ToJSON(rt))
	}
}

func TestRateUID(t *testing.T) {
	rt := &RateProfile{
		Rates: map[string]*Rate{
			"randomVal1": {
				ID: "RT_CHRISTMAS",
				Weights: DynamicWeights{
					{
						Weight: 30,
					},
				},
				ActivationTimes: "* * 24 12 *",
				uID:             "randomID",
			},
		},
	}
	expected := "randomID"
	if newID := rt.Rates["randomVal1"].UID(); !reflect.DeepEqual(newID, expected) {
		t.Errorf("Expected %+v, received %+v", expected, newID)
	}
}

func TestRateProfileCompileError(t *testing.T) {
	rt := &RateProfile{
		Rates: map[string]*Rate{
			"randomVal": {
				ID: "RT_CHRISTMAS",
				Weights: DynamicWeights{
					{
						Weight: 30,
					},
				},
				ActivationTimes: "* * * *",
			},
		},
	}
	expectedErr := "expected exactly 5 fields, found 4: [* * * *]"
	if err := rt.Compile(); err == nil || err.Error() != expectedErr {
		t.Errorf("Expected %+v, received %+v ", expectedErr, err)
	}
}

func TestRateCompileChristmasTime(t *testing.T) {
	rt := &Rate{
		ID: "RT_CHRISTMAS",
		Weights: DynamicWeights{
			{
				Weight: 30,
			},
		},
		ActivationTimes: "* * 24 12 *",
	}
	expTime, err := cron.ParseStandard("* * 24 12 *")
	if err != nil {
		t.Error(err)
	}
	expectedRt := &Rate{
		ID: "RT_CHRISTMAS",
		Weights: DynamicWeights{
			{
				Weight: 30,
			},
		},
		ActivationTimes: "* * 24 12 *",
		sched:           expTime,
	}
	if err := rt.Compile(); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(expectedRt, rt) {
		t.Errorf("Expected %+v, received %+v", expectedRt, rt)
	}
}

func TestRateCompileEmptyActivationTime(t *testing.T) {
	rt := &Rate{
		ID: "RT_CHRISTMAS",
		Weights: DynamicWeights{
			{
				Weight: 30,
			},
		},
		ActivationTimes: EmptyString,
	}
	expTime, err := cron.ParseStandard("* * * * *")
	if err != nil {
		t.Error(err)
	}
	expectedRt := &Rate{
		ID: "RT_CHRISTMAS",
		Weights: DynamicWeights{
			{
				Weight: 30,
			},
		},
		ActivationTimes: EmptyString,
		sched:           expTime,
	}
	if err := rt.Compile(); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(rt, expectedRt) {
		t.Errorf("Expected %+v, received %+v", expectedRt, rt)
	}
}

func TestRateProfileRunTimes(t *testing.T) {
	rt := &Rate{
		ID: "RATE0",
		IntervalRates: []*IntervalRate{
			{
				IntervalStart: NewDecimal(0, 0),
			},
		},
	}
	rt.Compile()

	sTime := time.Date(2020, time.June, 28, 18, 56, 05, 0, time.UTC)
	eTime := sTime.Add(2 * time.Minute)
	eRTimes := [][]time.Time{
		{time.Date(2020, time.June, 28, 18, 56, 0, 0, time.UTC),
			time.Time{}},
	}

	if rTimes, err := rt.RunTimes(sTime, eTime, 10); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(eRTimes, rTimes) {
		t.Errorf("expecting: %+v, received: %+v", eRTimes, rTimes)
	}

	rt = &Rate{
		ID: "RT_CHRISTMAS",
		Weights: DynamicWeights{
			{
				Weight: 30,
			},
		},
		ActivationTimes: "* * 24 12 *",
	}
	rt.Compile()

	// sTime and eTime inside the activation interval
	sTime = time.Date(2020, 12, 24, 12, 0, 0, 0, time.UTC)
	eTime = sTime.Add(time.Hour)
	eRTimes = [][]time.Time{
		{time.Date(2020, 12, 24, 12, 0, 0, 0, time.UTC), time.Date(2020, 12, 25, 0, 0, 0, 0, time.UTC)},
	}
	if rTimes, err := rt.RunTimes(sTime, eTime, 10); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(eRTimes, rTimes) {
		t.Errorf("expecting: %+v, received: %+v", eRTimes, rTimes)
	}
	// sTime smaller than activation time, eTime equals aTime
	sTime = time.Date(2020, 12, 23, 23, 0, 0, 0, time.UTC)
	eTime = sTime.Add(time.Hour)
	eRTimes = nil // cannot cover full interval
	if rTimes, err := rt.RunTimes(sTime, eTime, 10); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(eRTimes, rTimes) {
		t.Errorf("expecting: %+v, received: %+v", eRTimes, rTimes)
	}

	// sTime smaller than activation time but first aTime inside, eTime inside activation interval
	sTime = time.Date(2020, 12, 23, 23, 59, 59, 0, time.UTC)
	eTime = sTime.Add(time.Hour)
	eRTimes = [][]time.Time{
		{time.Date(2020, 12, 24, 0, 0, 0, 0, time.UTC), time.Date(2020, 12, 25, 0, 0, 0, 0, time.UTC)},
	}
	if rTimes, err := rt.RunTimes(sTime, eTime, 1000000); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(eRTimes, rTimes) {
		t.Errorf("expecting: %+v, received: %+v", eRTimes, rTimes)
	}

	// sTime way before aTime, eTime inside aInterval
	sTime = time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
	eTime = time.Date(2021, 12, 24, 0, 1, 0, 0, time.UTC)
	eRTimes = [][]time.Time{
		{time.Date(2021, 12, 24, 0, 0, 0, 0, time.UTC), time.Date(2021, 12, 25, 0, 0, 0, 0, time.UTC)},
	}
	if rTimes, err := rt.RunTimes(sTime, eTime, 1000000); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(eRTimes, rTimes) {
		t.Errorf("expecting: %+v, received: %+v", eRTimes, rTimes)
	}
}

func TestRateProfileRunTimesMaxIterations(t *testing.T) {
	rt := &Rate{
		ID: "RATE0",
		IntervalRates: []*IntervalRate{
			{
				IntervalStart: NewDecimal(0, 0),
			},
		},
		ActivationTimes: "* * 24 12 *",
	}
	err := rt.Compile()
	if err != nil {
		t.Error(err)
	}
	sTime := time.Date(2020, 12, 24, 23, 30, 0, 0, time.UTC)
	eTime := time.Date(2021, 12, 25, 23, 30, 0, 0, time.UTC)
	expectedErr := "maximum iterations reached"
	if _, err := rt.RunTimes(sTime, eTime, 2); err == nil || err.Error() != expectedErr {
		t.Errorf("Expected %+v, received %+v", expectedErr, err)
	}
}

func TestRateProfileRunTimesPassingActivationTIme(t *testing.T) {
	rt := &Rate{
		ID: "RATE0",
		IntervalRates: []*IntervalRate{
			{
				IntervalStart: NewDecimal(0, 0),
			},
		},
		ActivationTimes: "* * 24 * *",
	}
	err := rt.Compile()
	if err != nil {
		t.Error(err)
	}
	sTime := time.Date(2020, 12, 23, 0, 0, 0, 0, time.UTC)
	eTime := time.Date(2020, 12, 27, 0, 0, 0, 0, time.UTC)
	expectedTime := [][]time.Time{
		{time.Date(2020, 12, 24, 0, 0, 0, 0, time.UTC), time.Date(2020, 12, 25, 0, 0, 0, 0, time.UTC)},
	}
	if rTimes, err := rt.RunTimes(sTime, eTime, 2); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(expectedTime, rTimes) {
		t.Errorf("Expected %+v, received %+v", expectedTime, rTimes)
	}
}

func TestRateProfileCostCorrectCostMinCost(t *testing.T) {
	testRPC := &RateProfileCost{
		Cost:    NewDecimal(5, 1),
		MinCost: NewDecimal(15, 1),
	}
	testRPC.CorrectCost(IntPointer(2), "")
	if testRPC.Cost.Compare(NewDecimal(15, 1)) != 0 {
		t.Errorf("\nExpecting: <1.5>,\n Received: <%+v>", testRPC.Cost)
	}
}

func TestRateProfileCostCorrectCostMaxCost(t *testing.T) {
	testRPC := &RateProfileCost{
		Cost:    NewDecimal(25, 1),
		MaxCost: NewDecimal(15, 1),
	}
	testRPC.CorrectCost(IntPointer(2), "")
	if testRPC.Cost.Compare(NewDecimal(15, 1)) != 0 {
		t.Errorf("\nExpecting: <1.5>,\n Received: <%+v>", ToJSON(testRPC.Cost))
	}
}

func TestRateSIncrementCompressEquals(t *testing.T) {
	inCr1 := &RateSIncrement{
		IncrementStart:    NewDecimal(0, 0),
		Usage:             NewDecimal(int64(time.Minute), 0),
		RateIntervalIndex: 0,
		CompressFactor:    1,
	}
	inCr2 := &RateSIncrement{
		IncrementStart:    NewDecimal(0, 0),
		Usage:             NewDecimal(int64(time.Minute), 0),
		RateIntervalIndex: 0,
		CompressFactor:    1,
	}
	result := inCr1.CompressEquals(inCr2)
	if result != true {
		t.Errorf("\nExpecting: <true>,\n Received: <%+v>", result)
	}
}

func TestRateSIncrementCompressEqualsCase1(t *testing.T) {
	inCr1 := &RateSIncrement{
		IncrementStart:    NewDecimal(0, 0),
		Usage:             NewDecimal(int64(time.Minute), 0),
		RateIntervalIndex: 1,
		CompressFactor:    1,
	}
	inCr2 := &RateSIncrement{
		IncrementStart:    NewDecimal(0, 0),
		Usage:             NewDecimal(int64(time.Minute), 0),
		RateIntervalIndex: 0,
		CompressFactor:    1,
	}
	result := inCr1.CompressEquals(inCr2)
	if result != false {
		t.Errorf("\nExpecting: <false>,\n Received: <%+v>", result)
	}
}

func TestRateSIncrementCompressEqualsCase2(t *testing.T) {
	inCr1 := &RateSIncrement{
		IncrementStart:    NewDecimal(0, 0),
		Usage:             NewDecimal(int64(time.Minute), 0),
		RateIntervalIndex: 0,
		CompressFactor:    1,
	}
	inCr2 := &RateSIncrement{
		IncrementStart:    NewDecimal(0, 0),
		Usage:             NewDecimal(int64(time.Minute), 0),
		RateIntervalIndex: 1,
		CompressFactor:    1,
	}
	result := inCr1.CompressEquals(inCr2)
	if result != false {
		t.Errorf("\nExpecting: <false>,\n Received: <%+v>", result)
	}
}

func TestRateSIncrementCompressEqualsCase3(t *testing.T) {
	inCr1 := &RateSIncrement{
		IncrementStart:    NewDecimal(0, 0),
		Usage:             NewDecimal(int64(time.Second), 0),
		RateIntervalIndex: 1,
		CompressFactor:    1,
	}
	inCr2 := &RateSIncrement{
		IncrementStart:    NewDecimal(0, 0),
		Usage:             NewDecimal(int64(time.Minute), 0),
		RateIntervalIndex: 1,
		CompressFactor:    1,
	}
	result := inCr1.CompressEquals(inCr2)
	if result != false {
		t.Errorf("\nExpecting: <false>,\n Received: <%+v>", result)
	}
}

func TestRateSIntervalCompressEqualsCase1(t *testing.T) {
	rateSintrv1 := &RateSInterval{
		IntervalStart: NewDecimal(0, 0),
		Increments: []*RateSIncrement{
			{
				IncrementStart:    NewDecimal(0, 0),
				Usage:             NewDecimal(int64(time.Minute), 0),
				RateIntervalIndex: 0,
				CompressFactor:    1,
			},
			{
				IncrementStart:    NewDecimal(int64(time.Minute), 0),
				Usage:             NewDecimal(int64(time.Minute+10*time.Second), 0),
				RateIntervalIndex: 1,
				CompressFactor:    70,
			},
		},
		CompressFactor: 1,
	}

	rateSintrv2 := &RateSInterval{
		IntervalStart: NewDecimal(0, 0),
		Increments: []*RateSIncrement{
			{
				IncrementStart:    NewDecimal(0, 0),
				Usage:             NewDecimal(int64(time.Minute), 0),
				RateIntervalIndex: 0,
				CompressFactor:    1,
			},
		},
		CompressFactor: 1,
	}
	result := rateSintrv1.CompressEquals(rateSintrv2)
	if result != false {
		t.Errorf("\nExpecting <false>,\nReceived <%+v>", result)
	}
}

func TestRateSIntervalCompressEqualsCase2(t *testing.T) {
	rateSintrv1 := &RateSInterval{
		IntervalStart: NewDecimal(0, 0),
		Increments: []*RateSIncrement{
			{
				IncrementStart:    NewDecimal(0, 0),
				Usage:             NewDecimal(int64(time.Minute), 0),
				RateIntervalIndex: 0,
				CompressFactor:    1,
			},
			{
				IncrementStart:    NewDecimal(0, 0),
				Usage:             NewDecimal(int64(time.Second), 0),
				RateIntervalIndex: 0,
				CompressFactor:    1,
			},
		},
		CompressFactor: 1,
	}

	rateSintrv2 := &RateSInterval{
		IntervalStart: NewDecimal(1, 0),
		Increments: []*RateSIncrement{
			{
				IncrementStart:    NewDecimal(0, 0),
				Usage:             NewDecimal(int64(time.Minute), 0),
				RateIntervalIndex: 0,
				CompressFactor:    1,
			},
			{
				IncrementStart:    NewDecimal(0, 0),
				Usage:             NewDecimal(int64(time.Minute), 0),
				RateIntervalIndex: 2,
				CompressFactor:    1,
			},
		},
		CompressFactor: 0,
	}
	result := rateSintrv1.CompressEquals(rateSintrv2)
	if result != false {
		t.Errorf("\nExpecting <false>,\nReceived <%+v>", result)
	}
}

func TestRateSIntervalCompressEqualsCase3(t *testing.T) {
	rateSintrv1 := &RateSInterval{
		IntervalStart: NewDecimal(0, 0),
		Increments: []*RateSIncrement{
			{
				IncrementStart:    NewDecimal(0, 0),
				Usage:             NewDecimal(int64(time.Minute), 0),
				RateIntervalIndex: 0,
				CompressFactor:    1,
			},
		},
		CompressFactor: 1,
	}

	rateSintrv2 := &RateSInterval{
		IntervalStart: NewDecimal(0, 0),
		Increments: []*RateSIncrement{
			{
				IncrementStart:    NewDecimal(0, 0),
				Usage:             NewDecimal(int64(time.Minute), 0),
				RateIntervalIndex: 0,
				CompressFactor:    1,
			},
		},
		CompressFactor: 1,
	}
	result := rateSintrv1.CompressEquals(rateSintrv2)
	if result != true {
		t.Errorf("\nExpecting <true>,\nReceived <%+v>", result)
	}
}

func TestRatesIntervalEquals(t *testing.T) {
	rtInt1 := &RateSInterval{
		IntervalStart: NewDecimal(int64(10*time.Second), 0),
		Increments: []*RateSIncrement{
			{
				IncrementStart:    NewDecimal(int64(time.Second), 0),
				RateIntervalIndex: 1,
				RateID:            "ID1",
				CompressFactor:    2,
				Usage:             NewDecimal(int64(5*time.Second), 0),
			},
		},
		CompressFactor: 2,
	}
	rtInt2 := &RateSInterval{
		IntervalStart: NewDecimal(int64(10*time.Second), 0),
		Increments: []*RateSIncrement{
			{
				IncrementStart:    NewDecimal(int64(time.Second), 0),
				RateIntervalIndex: 1,
				RateID:            "ID1",
				CompressFactor:    2,
				Usage:             NewDecimal(int64(5*time.Second), 0),
			},
		},
		CompressFactor: 2,
	}
	riRef := map[string]*IntervalRate{
		"ID1": {
			IntervalStart: NewDecimal(0, 0),
			RecurrentFee:  NewDecimal(12, 1),
		},
	}
	nRiRef := map[string]*IntervalRate{
		"ID1": {
			IntervalStart: NewDecimal(0, 0),
			RecurrentFee:  NewDecimal(12, 1),
		},
	}

	// equals is looking for compressFactor
	if !rtInt1.Equals(rtInt2, riRef, nRiRef) {
		t.Errorf("Intervals %+v and %+v are not equal", ToJSON(rtInt1), ToJSON(rtInt2))
	}

	// not equals for IntervalStart
	rtInt1.IntervalStart = NewDecimal(int64(20*time.Second), 0)
	if rtInt1.Equals(rtInt2, riRef, nRiRef) {
		t.Errorf("Intervals %+v and %+v are equal", ToJSON(rtInt1), ToJSON(rtInt2))
	}
	rtInt1.IntervalStart = NewDecimal(int64(10*time.Second), 0)

	rtInt2.IntervalStart = NewDecimal(int64(20*time.Second), 0)
	if rtInt1.Equals(rtInt2, riRef, nRiRef) {
		t.Errorf("Intervals %+v and %+v are equal", ToJSON(rtInt1), ToJSON(rtInt2))
	}
	rtInt2.IntervalStart = NewDecimal(int64(10*time.Second), 0)

	// not equals for CompressFactor
	rtInt1.CompressFactor = 5
	if rtInt1.Equals(rtInt2, riRef, nRiRef) {
		t.Errorf("Intervals %+v and %+v are equal", ToJSON(rtInt1), ToJSON(rtInt2))
	}
	rtInt1.CompressFactor = 2

	rtInt2.CompressFactor = 8
	if rtInt1.Equals(rtInt2, riRef, nRiRef) {
		t.Errorf("Intervals %+v and %+v are equal", ToJSON(rtInt1), ToJSON(rtInt2))
	}
	rtInt2.CompressFactor = 2

	//not equals for Increments and their length
	rtInt1.Increments[0].Usage = NewDecimal(int64(90*time.Second), 0)
	if rtInt1.Equals(rtInt2, riRef, nRiRef) {
		t.Errorf("Intervals %+v and %+v are equal", ToJSON(rtInt1), ToJSON(rtInt2))
	}
	rtInt1.Increments[0].Usage = NewDecimal(int64(5*time.Second), 0)

	rtInt2.Increments[0].Usage = NewDecimal(int64(80*time.Second), 0)
	if rtInt1.Equals(rtInt2, riRef, nRiRef) {
		t.Errorf("Intervals %+v and %+v are equal", ToJSON(rtInt1), ToJSON(rtInt2))
	}
	rtInt2.Increments[0].Usage = NewDecimal(int64(5*time.Second), 0)

	rtInt1 = &RateSInterval{
		IntervalStart: NewDecimal(int64(10*time.Second), 0),
		Increments: []*RateSIncrement{
			{
				IncrementStart:    NewDecimal(int64(time.Second), 0),
				RateIntervalIndex: 1,
				CompressFactor:    2,
				Usage:             NewDecimal(int64(5*time.Second), 0),
			},
			{
				IncrementStart: NewDecimal(int64(time.Second), 0),
				Usage:          NewDecimal(int64(5*time.Second), 0),
			},
		},
		CompressFactor: 2,
	}
	if rtInt1.Equals(rtInt2, riRef, nRiRef) {
		t.Errorf("Intervals %+v and %+v are equal", ToJSON(rtInt1), ToJSON(rtInt2))
	}
}

func TestRatesIncrementEquals(t *testing.T) {
	incr1 := &RateSIncrement{
		IncrementStart:    NewDecimal(int64(time.Second), 0),
		RateIntervalIndex: 1,
		RateID:            "ID1",
		CompressFactor:    2,
		Usage:             NewDecimal(int64(5*time.Second), 0),
	}
	incr2 := &RateSIncrement{
		IncrementStart:    NewDecimal(int64(time.Second), 0),
		RateIntervalIndex: 1,
		RateID:            "ID1",
		CompressFactor:    2,
		Usage:             NewDecimal(int64(5*time.Second), 0),
	}
	riRef := map[string]*IntervalRate{
		"ID1": {
			IntervalStart: NewDecimal(0, 0),
			RecurrentFee:  NewDecimal(12, 1),
		},
	}
	nRiRef := map[string]*IntervalRate{
		"ID1": {
			IntervalStart: NewDecimal(0, 0),
			RecurrentFee:  NewDecimal(12, 1),
		},
	}

	// equals is not looking for compress factor
	if !incr1.Equals(incr2, riRef, nRiRef) {
		t.Errorf("Intervals %+v and %+v are not equal", ToJSON(incr1), ToJSON(incr2))
	}

	// not equals by IncrementStart
	incr1.IncrementStart = NewDecimal(int64(10*time.Second), 0)
	if incr1.Equals(incr2, riRef, nRiRef) {
		t.Errorf("Intervals %+v and %+v are equal", ToJSON(incr1), ToJSON(incr2))
	}
	incr1.IncrementStart = NewDecimal(int64(time.Second), 0)

	incr2.IncrementStart = NewDecimal(int64(10*time.Second), 0)
	if incr1.Equals(incr2, riRef, nRiRef) {
		t.Errorf("Intervals %+v and %+v are equal", ToJSON(incr1), ToJSON(incr2))
	}
	incr2.IncrementStart = NewDecimal(int64(time.Second), 0)

	// not equals by RateIntervalIndex
	incr1.RateIntervalIndex = 5
	if incr1.Equals(incr2, riRef, nRiRef) {
		t.Errorf("Intervals %+v and %+v are equal", ToJSON(incr1), ToJSON(incr2))
	}
	incr1.RateIntervalIndex = 1

	incr2.RateIntervalIndex = 5
	if incr1.Equals(incr2, riRef, nRiRef) {
		t.Errorf("Intervals %+v and %+v are equal", ToJSON(incr1), ToJSON(incr2))
	}
	incr2.RateIntervalIndex = 1

	// not equals by CompressFactor
	incr1.CompressFactor = 0
	if incr1.Equals(incr2, riRef, nRiRef) {
		t.Errorf("Intervals %+v and %+v are equal", ToJSON(incr1), ToJSON(incr2))
	}
	incr1.CompressFactor = 2

	incr2.CompressFactor = 9
	if incr1.Equals(incr2, riRef, nRiRef) {
		t.Errorf("Intervals %+v and %+v are equal", ToJSON(incr1), ToJSON(incr2))
	}
	incr2.CompressFactor = 2
}

func TestRateProfileCostEquals(t *testing.T) {
	rtPrfCost := &RateProfileCost{
		ID:              "RATE_1",
		Cost:            NewDecimal(2, 1),
		MinCost:         NewDecimal(1, 2),
		MaxCost:         NewDecimal(15, 0),
		MaxCostStrategy: "*round",
		CostIntervals: []*RateSIntervalCost{
			{
				Increments: []*RateSIncrementCost{
					{
						RateIntervalIndex: 0,
						RateID:            "RATE1",
						CompressFactor:    1,
						Usage:             NewDecimal(int64(time.Minute), 0),
					},
				},
				CompressFactor: 1,
			},
		},
		Rates: map[string]*IntervalRate{
			"RATE1": {
				IntervalStart: NewDecimal(0, 0),
				RecurrentFee:  NewDecimal(2, 1),
				Unit:          NewDecimal(int64(time.Second), 1),
				Increment:     NewDecimal(int64(time.Second), 1),
			},
		},
		Altered: []string{MetaRoundingDown},
	}

	expectedRT := &RateProfileCost{
		ID:              "RATE_1",
		Cost:            NewDecimal(2, 1),
		MinCost:         NewDecimal(1, 2),
		MaxCost:         NewDecimal(15, 0),
		MaxCostStrategy: "*round",
		CostIntervals: []*RateSIntervalCost{
			{
				Increments: []*RateSIncrementCost{
					{
						RateIntervalIndex: 0,
						RateID:            "RATE1",
						CompressFactor:    1,
						Usage:             NewDecimal(int64(time.Minute), 0),
					},
				},
				CompressFactor: 1,
			},
		},
		Rates: map[string]*IntervalRate{
			"RATE1": {
				IntervalStart: NewDecimal(0, 0),
				RecurrentFee:  NewDecimal(2, 1),
				Unit:          NewDecimal(int64(time.Second), 1),
				Increment:     NewDecimal(int64(time.Second), 1),
			},
		},
		Altered: []string{MetaRoundingDown},
	}
	if !rtPrfCost.Equals(expectedRT) {
		t.Errorf("%v and \n%v are not equals", ToJSON(rtPrfCost), ToJSON(expectedRT))
	}
}

func TestRateProfileCostNotEquals(t *testing.T) {
	rtPrfCost := &RateProfileCost{
		ID:              "RATE_1",
		Cost:            NewDecimal(4, 1),
		MinCost:         NewDecimal(1, 2),
		MaxCost:         NewDecimal(15, 0),
		MaxCostStrategy: "*round",
		CostIntervals: []*RateSIntervalCost{
			{
				Increments: []*RateSIncrementCost{
					{
						RateIntervalIndex: 0,
						RateID:            "RATE1",
						CompressFactor:    1,
						Usage:             NewDecimal(int64(time.Minute), 0),
					},
				},
				CompressFactor: 1,
			},
		},
		Rates: map[string]*IntervalRate{
			"RATE1": {
				IntervalStart: NewDecimal(0, 0),
				RecurrentFee:  NewDecimal(2, 1),
				Unit:          NewDecimal(int64(time.Second), 1),
				Increment:     NewDecimal(int64(time.Second), 1),
			},
		},
		Altered: []string{MetaRoundingDown},
	}
	expectedRT := &RateProfileCost{
		ID:              "RATE_1",
		Cost:            NewDecimal(2, 1),
		MinCost:         NewDecimal(1, 2),
		MaxCost:         NewDecimal(15, 0),
		MaxCostStrategy: "*round",
		CostIntervals: []*RateSIntervalCost{
			{
				Increments: []*RateSIncrementCost{
					{
						RateIntervalIndex: 0,
						RateID:            "RATE1",
						CompressFactor:    1,
						Usage:             NewDecimal(int64(time.Minute), 0),
					},
				},
				CompressFactor: 1,
			},
		},
		Rates: map[string]*IntervalRate{
			"RATE1": {
				IntervalStart: NewDecimal(0, 0),
				RecurrentFee:  NewDecimal(2, 1),
				Unit:          NewDecimal(int64(time.Second), 1),
				Increment:     NewDecimal(int64(time.Second), 1),
			},
		},
		Altered: []string{MetaRoundingDown},
	}
	if rtPrfCost.Equals(expectedRT) {
		t.Errorf("%v and \n%v are equals", ToJSON(rtPrfCost), ToJSON(expectedRT))
	}
}

func TestRateProfileCostAlteredNotEquals(t *testing.T) {
	rtPrfCost := &RateProfileCost{
		ID:              "RATE_1",
		Cost:            NewDecimal(2, 1),
		MinCost:         NewDecimal(1, 2),
		MaxCost:         NewDecimal(15, 0),
		MaxCostStrategy: "*round",
		CostIntervals: []*RateSIntervalCost{
			{
				Increments: []*RateSIncrementCost{
					{
						RateIntervalIndex: 0,
						RateID:            "RATE1",
						CompressFactor:    1,
						Usage:             NewDecimal(int64(time.Minute), 0),
					},
				},
				CompressFactor: 1,
			},
		},
		Rates: map[string]*IntervalRate{
			"RATE1": {
				IntervalStart: NewDecimal(0, 0),
				RecurrentFee:  NewDecimal(2, 1),
				Unit:          NewDecimal(int64(time.Second), 1),
				Increment:     NewDecimal(int64(time.Second), 1),
			},
		},
		Altered: []string{MetaRoundingDown},
	}
	expectedRT := &RateProfileCost{
		ID:              "RATE_1",
		Cost:            NewDecimal(2, 1),
		MinCost:         NewDecimal(1, 2),
		MaxCost:         NewDecimal(15, 0),
		MaxCostStrategy: "*round",
		CostIntervals: []*RateSIntervalCost{
			{
				Increments: []*RateSIncrementCost{
					{
						RateIntervalIndex: 0,
						RateID:            "RATE1",
						CompressFactor:    1,
						Usage:             NewDecimal(int64(time.Minute), 0),
					},
				},
				CompressFactor: 1,
			},
		},
		Rates: map[string]*IntervalRate{
			"RATE1": {
				IntervalStart: NewDecimal(0, 0),
				RecurrentFee:  NewDecimal(2, 1),
				Unit:          NewDecimal(int64(time.Second), 1),
				Increment:     NewDecimal(int64(time.Second), 1),
			},
		},
		Altered: []string{},
	}
	if rtPrfCost.Equals(expectedRT) {
		t.Errorf("%v and \n%v are equals", ToJSON(rtPrfCost), ToJSON(expectedRT))
	}
}

func TestRateProfileCostCINotEquals(t *testing.T) {
	rtPrfCost := &RateProfileCost{
		ID:              "RATE_1",
		Cost:            NewDecimal(2, 1),
		MinCost:         NewDecimal(1, 2),
		MaxCost:         NewDecimal(15, 0),
		MaxCostStrategy: "*round",
		CostIntervals: []*RateSIntervalCost{
			{
				Increments: []*RateSIncrementCost{
					{
						RateIntervalIndex: 0,
						RateID:            "RATE1",
						CompressFactor:    1,
						Usage:             NewDecimal(int64(time.Minute), 0),
					},
				},
				CompressFactor: 1,
			},
		},
		Rates: map[string]*IntervalRate{
			"RATE1": {
				IntervalStart: NewDecimal(0, 0),
				RecurrentFee:  NewDecimal(2, 1),
				Unit:          NewDecimal(int64(time.Second), 1),
				Increment:     NewDecimal(int64(time.Second), 1),
			},
		},
		Altered: []string{MetaRoundingDown},
	}
	expectedRT := &RateProfileCost{
		ID:              "RATE_1",
		Cost:            NewDecimal(2, 1),
		MinCost:         NewDecimal(1, 2),
		MaxCost:         NewDecimal(15, 0),
		MaxCostStrategy: "*round",
		CostIntervals: []*RateSIntervalCost{
			{
				Increments: []*RateSIncrementCost{
					{
						RateIntervalIndex: 1,
						RateID:            "RATE2",
						CompressFactor:    2,
						Usage:             NewDecimal(int64(time.Minute), 0),
					},
				},
				CompressFactor: 1,
			},
		},
		Rates: map[string]*IntervalRate{
			"RATE1": {
				IntervalStart: NewDecimal(0, 0),
				RecurrentFee:  NewDecimal(2, 1),
				Unit:          NewDecimal(int64(time.Second), 1),
				Increment:     NewDecimal(int64(time.Second), 1),
			},
		},
		Altered: []string{MetaRoundingDown},
	}
	if rtPrfCost.Equals(expectedRT) {
		t.Errorf("%v and \n%v are equals", ToJSON(rtPrfCost), ToJSON(expectedRT))
	}
}

func TestAsRatesIntervalsCost(t *testing.T) {
	rtsIntrvl := &RateSInterval{
		IntervalStart: NewDecimal(0, 0),
		Increments: []*RateSIncrement{
			{
				IncrementStart:    NewDecimal(0, 0),
				RateIntervalIndex: 0,
				RateID:            "RATE1",
				CompressFactor:    1,
				Usage:             NewDecimal(int64(time.Minute), 0),
			},
			{
				IncrementStart:    NewDecimal(int64(time.Minute), 0),
				RateIntervalIndex: 1,
				RateID:            "RATE1",
				CompressFactor:    5,
				Usage:             NewDecimal(int64(2*time.Minute), 0),
			},
		},
		CompressFactor: 1,
	}
	expRtsIntCost := &RateSIntervalCost{
		Increments: []*RateSIncrementCost{
			{
				RateIntervalIndex: 0,
				RateID:            "RATE1",
				CompressFactor:    1,
				Usage:             NewDecimal(int64(time.Minute), 0),
			},
			{
				RateIntervalIndex: 1,
				RateID:            "RATE1",
				CompressFactor:    5,
				Usage:             NewDecimal(int64(2*time.Minute), 0),
			},
		},
		CompressFactor: 1,
	}
	if rcv := rtsIntrvl.AsRatesIntervalsCost(); !reflect.DeepEqual(rcv, expRtsIntCost) {
		t.Errorf("Expected %+v, received %+v", ToJSON(expRtsIntCost), ToJSON(rcv))
	} else if !rcv.Equals(expRtsIntCost, nil, nil) {
		t.Errorf("Expected %+v, received %+v", ToJSON(expRtsIntCost), ToJSON(rcv))
	}
}

func TestRateSIncrementCost(t *testing.T) {
	rIc := &RateSIncrement{
		IncrementStart:    NewDecimal(int64(2), 0),
		RateIntervalIndex: 0,
		RateID:            "RI1",
		CompressFactor:    int64(3),
		Usage:             NewDecimal(int64(30), 0),
	}

	rts := map[string]*IntervalRate{
		"RI1": {
			IntervalStart: NewDecimal(int64(2), 0),
			FixedFee:      NewDecimal(int64(10), 0),
			RecurrentFee:  NewDecimal(int64(5), 0),
		},
	}

	cost := rIc.Cost(rts)
	exp := decimal.WithContext(DecimalContext).SetUint64(15)
	if !reflect.DeepEqual(cost, exp) {
		t.Errorf("Expected %T \n but received \n %T", exp, cost)
	}
}

func TestRateSIncrementCostNotNil(t *testing.T) {
	rIc := &RateSIncrement{
		IncrementStart:    NewDecimal(int64(2), 0),
		RateIntervalIndex: 0,
		RateID:            "RI1",
		CompressFactor:    int64(3),
		Usage:             NewDecimal(int64(30), 0),
		cost:              decimal.WithContext(DecimalContext).SetUint64(15),
	}

	rts := map[string]*IntervalRate{
		"RI1": {
			IntervalStart: NewDecimal(int64(2), 0),
			FixedFee:      NewDecimal(int64(10), 0),
			RecurrentFee:  NewDecimal(int64(5), 0),
		},
	}

	cost := rIc.Cost(rts)
	exp := decimal.WithContext(DecimalContext).SetUint64(15)
	if !reflect.DeepEqual(cost, exp) {
		t.Errorf("Expected %T \n but received \n %T", exp, cost)
	}
}

func TestRateSIncrementCostNoID(t *testing.T) {
	rIc := &RateSIncrement{
		IncrementStart:    NewDecimal(int64(2), 0),
		RateIntervalIndex: 0,
		RateID:            "RI1",
		CompressFactor:    int64(3),
		Usage:             NewDecimal(int64(30), 0),
	}

	rts := map[string]*IntervalRate{
		"not_RI1": {
			IntervalStart: NewDecimal(int64(2), 0),
			FixedFee:      NewDecimal(int64(10), 0),
			RecurrentFee:  NewDecimal(int64(5), 0),
		},
	}

	cost := rIc.Cost(rts)
	if cost != nil {
		t.Errorf("expected cost to be nil, received <%+v>", cost)
	}
}

func TestRateSIncrementCostFixedFee(t *testing.T) {
	rIc := &RateSIncrement{
		IncrementStart:    NewDecimal(int64(2), 0),
		RateIntervalIndex: 0,
		RateID:            "RI1",
		CompressFactor:    int64(3),
		Usage:             NewDecimal(int64(-1), 0),
	}

	rts := map[string]*IntervalRate{
		"RI1": {
			IntervalStart: NewDecimal(int64(2), 0),
			FixedFee:      NewDecimal(int64(10), 0),
			RecurrentFee:  NewDecimal(int64(5), 0),
		},
	}

	cost := rIc.Cost(rts)
	exp := decimal.WithContext(DecimalContext).SetUint64(10)
	if !reflect.DeepEqual(cost, exp) {
		t.Errorf("Expected %v \n but received \n %v", exp, cost)
	}
}

func TestRateSIncrementCostDiffUnitIncrement(t *testing.T) {
	rIc := &RateSIncrement{
		IncrementStart:    NewDecimal(int64(2), 0),
		RateIntervalIndex: 0,
		RateID:            "RI1",
		CompressFactor:    int64(1),
		Usage:             NewDecimal(int64(2), 0),
	}

	rts := map[string]*IntervalRate{
		"RI1": {
			IntervalStart: NewDecimal(int64(2), 0),
			FixedFee:      NewDecimal(int64(10), 0),
			RecurrentFee:  NewDecimal(int64(2), 0),
			Unit:          NewDecimal(int64(2), 0),
			Increment:     NewDecimal(int64(3), 0),
		},
	}

	cost := rIc.Cost(rts)
	exp := decimal.WithContext(DecimalContext).SetUint64(3)
	if !reflect.DeepEqual(cost, exp) {
		t.Errorf("Expected %v \n but received \n %v", exp, cost)
	}
}

func TestAPIIntervalRateAsIR(t *testing.T) {
	ext := &APIIntervalRate{
		IntervalStart: "2",
		FixedFee:      Float64Pointer(10),
		RecurrentFee:  Float64Pointer(2),
		Unit:          Float64Pointer(2),
		Increment:     Float64Pointer(3),
	}

	exp := &IntervalRate{
		IntervalStart: NewDecimal(int64(2), 0),
		FixedFee:      NewDecimal(int64(10), 0),
		RecurrentFee:  NewDecimal(int64(2), 0),
		Unit:          NewDecimal(int64(2), 0),
		Increment:     NewDecimal(int64(3), 0),
	}

	rcv, err := ext.AsIntervalRate()
	if err != nil {
		t.Error(err)
	}
	if !reflect.DeepEqual(exp, rcv) {
		t.Errorf("Expected %v \n but received \n %v", exp, rcv)
	}
}

func TestAPIIntervalRateAsIRError(t *testing.T) {
	ext := &APIIntervalRate{
		IntervalStart: "not_a_decimal",
		FixedFee:      Float64Pointer(10),
		RecurrentFee:  Float64Pointer(2),
		Unit:          Float64Pointer(2),
		Increment:     Float64Pointer(3),
	}

	exp := "can't convert <not_a_decimal> to decimal"

	_, err := ext.AsIntervalRate()
	if !reflect.DeepEqual(exp, err.Error()) {
		t.Errorf("Expected %v \n but received \n %v", exp, err.Error())
	}
}

func TestAPIRateAsRate(t *testing.T) {
	aR := &APIRate{
		ID:              "rate_id1",
		FilterIDs:       []string{"fltr1"},
		ActivationTimes: "1 1 3",
		Weights:         ";10",
		Blocker:         false,
		IntervalRates: []*APIIntervalRate{
			{
				IntervalStart: "2",
				FixedFee:      Float64Pointer(10),
				RecurrentFee:  Float64Pointer(2),
				Unit:          Float64Pointer(2),
				Increment:     Float64Pointer(3),
			},
		},
	}

	exp := &Rate{
		ID:              "rate_id1",
		FilterIDs:       []string{"fltr1"},
		ActivationTimes: "1 1 3",
		Weights: DynamicWeights{
			{
				Weight: 10,
			},
		},
		Blocker: false,
		IntervalRates: []*IntervalRate{
			{
				IntervalStart: NewDecimal(int64(2), 0),
				FixedFee:      NewDecimal(int64(10), 0),
				RecurrentFee:  NewDecimal(int64(2), 0),
				Unit:          NewDecimal(int64(2), 0),
				Increment:     NewDecimal(int64(3), 0),
			},
		},
	}

	rcv, err := aR.AsRate()
	if err != nil {
		t.Error(err)
	}
	if !reflect.DeepEqual(exp, rcv) {
		t.Errorf("Expected %v \n but received \n %v", exp, rcv)
	}
}

func TestAPIRateAsRateError(t *testing.T) {
	aR := &APIRate{
		ID:              "rate_id1",
		FilterIDs:       []string{"fltr1"},
		ActivationTimes: "1 1 3",
		Weights:         ";10",
		Blocker:         false,
		IntervalRates: []*APIIntervalRate{
			{
				IntervalStart: "not_a_decimal",
				FixedFee:      Float64Pointer(10),
				RecurrentFee:  Float64Pointer(2),
				Unit:          Float64Pointer(2),
				Increment:     Float64Pointer(3),
			},
		},
	}

	exp := "can't convert <not_a_decimal> to decimal"

	_, err := aR.AsRate()
	if !reflect.DeepEqual(exp, err.Error()) {
		t.Errorf("Expected %v \n but received \n %v", exp, err.Error())
	}
}

func TestIntervalRateEqualsNilIR(t *testing.T) {
	var iR *IntervalRate
	iR = nil
	iR.Equals(nil)

	iR = &IntervalRate{
		Unit: NewDecimal(int64(2), 0),
	}
	iR.Equals(nil)
}

func TestRateSIntervalCostEquals(t *testing.T) {
	rIC := &RateSIntervalCost{
		Increments: nil,
	}
	nRIc := &RateSIntervalCost{
		Increments: []*RateSIncrementCost{
			{
				RateIntervalIndex: 0,
				RateID:            "RI1",
				CompressFactor:    int64(1),
				Usage:             NewDecimal(int64(2), 0),
			},
		},
	}
	if rIC.Equals(nRIc, nil, nil) {
		t.Error("Shouldn't match")
	}
	rIC.Increments = []*RateSIncrementCost{
		{
			RateIntervalIndex: 3,
			RateID:            "RI2",
			CompressFactor:    int64(3),
			Usage:             NewDecimal(int64(5), 0),
		},
	}
	if rIC.Equals(nRIc, nil, nil) {
		t.Error("Shouldn't match")
	}
}
func TestRateSIntervalCost(t *testing.T) {
	rIv := &RateSInterval{
		Increments: []*RateSIncrement{
			{
				RateID: "ir1",
				Usage:  NewDecimal(int64(-1), 0),
			},
		},
	}

	rts := map[string]*IntervalRate{
		"ir1": {
			FixedFee: NewDecimal(int64(2), 0),
		},
	}

	rcv := rIv.Cost(rts)
	exp := decimal.WithContext(DecimalContext).SetUint64(2)
	if !reflect.DeepEqual(rcv, exp) {
		t.Errorf("expected <%v>,\nreceived <%v>", exp, rcv)
	}
}

func TestRateProfile(t *testing.T) {
	rp := RateProfile{Rates: make(map[string]*Rate)}
	exp := RateProfile{
		Tenant:    "cgrates.org",
		ID:        ID,
		FilterIDs: []string{"fltr1", "*string:~*req.Account:1001"},
		Weights: DynamicWeights{
			{
				Weight: 10,
			},
		},
		MinCost:         NewDecimal(10, 0),
		MaxCost:         NewDecimal(10, 0),
		MaxCostStrategy: "strategy",
		Rates: map[string]*Rate{
			"rat1": {
				ID:        "rat1",
				FilterIDs: []string{"fltr1"},
				Weights: DynamicWeights{
					{
						Weight: 20,
					},
				},
				ActivationTimes: "* * * * *",
				Blocker:         true,
				IntervalRates: []*IntervalRate{{
					IntervalStart: NewDecimal(10, 0),
					FixedFee:      NewDecimal(10, 0),
					RecurrentFee:  NewDecimal(10, 0),
					Unit:          NewDecimal(10, 0),
					Increment:     NewDecimal(10, 0),
				}},
			},
		},
	}
	if err := rp.Set([]string{}, "", false); err != ErrWrongPath {
		t.Error(err)
	}
	if err := rp.Set([]string{"NotAField"}, "", false); err != ErrWrongPath {
		t.Error(err)
	}
	if err := rp.Set([]string{"NotAField", "1"}, "", false); err != ErrWrongPath {
		t.Error(err)
	}

	if err := rp.Set([]string{Tenant}, "cgrates.org", false); err != nil {
		t.Error(err)
	}
	if err := rp.Set([]string{ID}, ID, false); err != nil {
		t.Error(err)
	}
	if err := rp.Set([]string{FilterIDs}, "fltr1;*string:~*req.Account:1001", false); err != nil {
		t.Error(err)
	}
	if err := rp.Set([]string{Weights}, ";10", false); err != nil {
		t.Error(err)
	}
	if err := rp.Set([]string{MinCost}, "10", false); err != nil {
		t.Error(err)
	}
	if err := rp.Set([]string{MaxCost}, "10", false); err != nil {
		t.Error(err)
	}
	if err := rp.Set([]string{MaxCostStrategy}, "strategy", false); err != nil {
		t.Error(err)
	}

	if err := rp.Set([]string{Rates + "[rat1]", ID}, "rat1", false); err != nil {
		t.Error(err)
	}

	if err := rp.Set([]string{Rates, "rat1", FilterIDs}, "fltr1", false); err != nil {
		t.Error(err)
	}
	if err := rp.Set([]string{Rates, "rat1", Weights}, ";20", false); err != nil {
		t.Error(err)
	}
	if err := rp.Set([]string{Rates, "rat1", ActivationTimes}, "* * * * *", false); err != nil {
		t.Error(err)
	}
	if err := rp.Set([]string{Rates, "rat1", Blocker}, "true", false); err != nil {
		t.Error(err)
	}

	if err := rp.Set([]string{Rates, "rat1", "Wrong"}, "true", false); err != ErrWrongPath {
		t.Error(err)
	}
	if err := rp.Set([]string{Rates, "rat1", "Wrong", "Path"}, "true", false); err != ErrWrongPath {
		t.Error(err)
	}
	if err := rp.Set([]string{Rates, "rat1", "Wrong", "Path", "2"}, "true", false); err != ErrWrongPath {
		t.Error(err)
	}
	if err := rp.Set([]string{Rates, "rat1", IntervalRates, "Wrong"}, "true", false); err != ErrWrongPath {
		t.Error(err)
	}

	if err := rp.Set([]string{Rates, "rat1", IntervalRates, IntervalStart}, "10", false); err != nil {
		t.Error(err)
	}
	if err := rp.Set([]string{Rates, "rat1", IntervalRates, FixedFee}, "10", false); err != nil {
		t.Error(err)
	}
	if err := rp.Set([]string{Rates, "rat1", IntervalRates, RecurrentFee}, "10", false); err != nil {
		t.Error(err)
	}
	if err := rp.Set([]string{Rates, "rat1", IntervalRates, Unit}, "10", false); err != nil {
		t.Error(err)
	}
	if err := rp.Set([]string{Rates, "rat1", IntervalRates, Increment}, "10", false); err != nil {
		t.Error(err)
	}

	if !reflect.DeepEqual(exp, rp) {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(rp))
	}
}

func TestRateProfileFieldAsInterface(t *testing.T) {
	rp := RateProfile{
		Tenant:          "cgrates.org",
		ID:              ID,
		FilterIDs:       []string{"fltr1", "*string:~*req.Account:1001"},
		Weights:         DynamicWeights{{}},
		MinCost:         NewDecimal(10, 0),
		MaxCost:         NewDecimal(10, 0),
		MaxCostStrategy: "strategy",
		Rates: map[string]*Rate{
			"rat1": {
				ID:              "rat1",
				FilterIDs:       []string{"fltr1"},
				Weights:         DynamicWeights{{}},
				ActivationTimes: "* * * * *",
				Blocker:         true,
				IntervalRates: []*IntervalRate{{
					IntervalStart: NewDecimal(10, 0),
					FixedFee:      NewDecimal(10, 0),
					RecurrentFee:  NewDecimal(10, 0),
					Unit:          NewDecimal(10, 0),
					Increment:     NewDecimal(10, 0),
				}},
			},
		},
	}
	if _, err := rp.FieldAsInterface(nil); err != ErrNotFound {
		t.Fatal(err)
	}
	if _, err := rp.FieldAsInterface([]string{"field"}); err != ErrNotFound {
		t.Fatal(err)
	}
	if _, err := rp.FieldAsInterface([]string{"field", ""}); err != ErrNotFound {
		t.Fatal(err)
	}
	if _, err := rp.FieldAsInterface([]string{Opts + "[f]"}); err != ErrNotFound {
		t.Fatal(err)
	}
	if _, err := rp.FieldAsInterface([]string{Opts + "[f]", ""}); err != ErrNotFound {
		t.Fatal(err)
	}
	if val, err := rp.FieldAsInterface([]string{Tenant}); err != nil {
		t.Fatal(err)
	} else if exp := "cgrates.org"; exp != val {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	if val, err := rp.FieldAsInterface([]string{ID}); err != nil {
		t.Fatal(err)
	} else if exp := ID; exp != val {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	if val, err := rp.FieldAsInterface([]string{Weights}); err != nil {
		t.Fatal(err)
	} else if exp := ";0"; exp != val {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	if val, err := rp.FieldAsInterface([]string{MaxCostStrategy}); err != nil {
		t.Fatal(err)
	} else if exp := "strategy"; exp != val {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	if val, err := rp.FieldAsInterface([]string{FilterIDs}); err != nil {
		t.Fatal(err)
	} else if exp := rp.FilterIDs; !reflect.DeepEqual(exp, val) {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	if val, err := rp.FieldAsInterface([]string{FilterIDs + "[0]"}); err != nil {
		t.Fatal(err)
	} else if exp := rp.FilterIDs[0]; exp != val {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	if val, err := rp.FieldAsInterface([]string{Rates}); err != nil {
		t.Fatal(err)
	} else if exp := rp.Rates; !reflect.DeepEqual(exp, val) {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	expErrMsg := `strconv.Atoi: parsing "a": invalid syntax`
	if _, err := rp.FieldAsInterface([]string{FilterIDs + "[a]"}); err == nil || err.Error() != expErrMsg {
		t.Errorf("Expeceted: %v, received: %v", expErrMsg, err)
	}
	if val, err := rp.FieldAsInterface([]string{Rates + "[rat1]"}); err != nil {
		t.Fatal(err)
	} else if exp := rp.Rates["rat1"]; !reflect.DeepEqual(exp, val) {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	if val, err := rp.FieldAsInterface([]string{Rates, "rat1"}); err != nil {
		t.Fatal(err)
	} else if exp := rp.Rates["rat1"]; !reflect.DeepEqual(exp, val) {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	if val, err := rp.FieldAsInterface([]string{MinCost}); err != nil {
		t.Fatal(err)
	} else if exp := rp.MinCost; exp.Cmp(val.(*Decimal).Big) != 0 {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	if val, err := rp.FieldAsInterface([]string{MaxCost}); err != nil {
		t.Fatal(err)
	} else if exp := rp.MaxCost; exp.Cmp(val.(*Decimal).Big) != 0 {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	if _, err := rp.FieldAsInterface([]string{Rates, "rat2"}); err != ErrNotFound {
		t.Fatal(err)
	}
	if _, err := rp.FieldAsInterface([]string{Rates, "rat1", ""}); err != ErrNotFound {
		t.Fatal(err)
	}
	if _, err := rp.FieldAsInterface([]string{Rates, "rat1", "", ""}); err != ErrNotFound {
		t.Fatal(err)
	}
	if _, err := rp.FieldAsInterface([]string{Rates, "rat1", "", "", ""}); err != ErrNotFound {
		t.Fatal(err)
	}

	if val, err := rp.FieldAsInterface([]string{Rates + "[rat1]", ID}); err != nil {
		t.Fatal(err)
	} else if exp := rp.Rates["rat1"].ID; !reflect.DeepEqual(exp, val) {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	if val, err := rp.FieldAsInterface([]string{Rates + "[rat1]", FilterIDs}); err != nil {
		t.Fatal(err)
	} else if exp := rp.Rates["rat1"].FilterIDs; !reflect.DeepEqual(exp, val) {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	if val, err := rp.FieldAsInterface([]string{Rates + "[rat1]", IntervalRates}); err != nil {
		t.Fatal(err)
	} else if exp := rp.Rates["rat1"].IntervalRates; !reflect.DeepEqual(exp, val) {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	if val, err := rp.FieldAsInterface([]string{Rates + "[rat1]", Blocker}); err != nil {
		t.Fatal(err)
	} else if exp := rp.Rates["rat1"].Blocker; !reflect.DeepEqual(exp, val) {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	if val, err := rp.FieldAsInterface([]string{Rates + "[rat1]", ActivationTimes}); err != nil {
		t.Fatal(err)
	} else if exp := rp.Rates["rat1"].ActivationTimes; !reflect.DeepEqual(exp, val) {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	if val, err := rp.FieldAsInterface([]string{Rates + "[rat1]", Weights}); err != nil {
		t.Fatal(err)
	} else if exp := ";0"; !reflect.DeepEqual(exp, val) {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	if val, err := rp.FieldAsInterface([]string{Rates + "[rat1]", FilterIDs + "[0]"}); err != nil {
		t.Fatal(err)
	} else if exp := rp.Rates["rat1"].FilterIDs[0]; !reflect.DeepEqual(exp, val) {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	if val, err := rp.FieldAsInterface([]string{Rates + "[rat1]", IntervalRates + "[0]"}); err != nil {
		t.Fatal(err)
	} else if exp := rp.Rates["rat1"].IntervalRates[0]; !reflect.DeepEqual(exp, val) {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	if _, err := rp.FieldAsInterface([]string{Rates + "[rat1]", IntervalRates + "[0]", ""}); err != ErrNotFound {
		t.Fatal(err)
	}
	if _, err := rp.Rates["rat1"].IntervalRates[0].FieldAsInterface([]string{"", ""}); err != ErrNotFound {
		t.Fatal(err)
	}
	if val, err := rp.FieldAsInterface([]string{Rates + "[rat1]", IntervalRates + "[0]", IntervalStart}); err != nil {
		t.Fatal(err)
	} else if exp := rp.Rates["rat1"].IntervalRates[0].IntervalStart; exp.Cmp(val.(*Decimal).Big) != 0 {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	if val, err := rp.FieldAsInterface([]string{Rates + "[rat1]", IntervalRates + "[0]", FixedFee}); err != nil {
		t.Fatal(err)
	} else if exp := rp.Rates["rat1"].IntervalRates[0].FixedFee; exp.Cmp(val.(*Decimal).Big) != 0 {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	if val, err := rp.FieldAsInterface([]string{Rates + "[rat1]", IntervalRates + "[0]", RecurrentFee}); err != nil {
		t.Fatal(err)
	} else if exp := rp.Rates["rat1"].IntervalRates[0].RecurrentFee; exp.Cmp(val.(*Decimal).Big) != 0 {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	if val, err := rp.FieldAsInterface([]string{Rates + "[rat1]", IntervalRates + "[0]", Unit}); err != nil {
		t.Fatal(err)
	} else if exp := rp.Rates["rat1"].IntervalRates[0].Unit; exp.Cmp(val.(*Decimal).Big) != 0 {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	if val, err := rp.FieldAsInterface([]string{Rates + "[rat1]", IntervalRates + "[0]", Increment}); err != nil {
		t.Fatal(err)
	} else if exp := rp.Rates["rat1"].IntervalRates[0].Increment; exp.Cmp(val.(*Decimal).Big) != 0 {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}

	if _, err := rp.FieldAsString([]string{""}); err != ErrNotFound {
		t.Fatal(err)
	}
	if val, err := rp.FieldAsString([]string{Tenant}); err != nil {
		t.Fatal(err)
	} else if exp := "cgrates.org"; exp != val {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	if val, exp := rp.String(), ToJSON(rp); exp != val {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}

	if _, err := rp.Rates["rat1"].FieldAsString([]string{""}); err != ErrNotFound {
		t.Fatal(err)
	}
	if val, err := rp.Rates["rat1"].FieldAsString([]string{ID}); err != nil {
		t.Fatal(err)
	} else if exp := "rat1"; exp != val {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	if val, exp := rp.Rates["rat1"].String(), ToJSON(rp.Rates["rat1"]); exp != val {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}

	if _, err := rp.Rates["rat1"].IntervalRates[0].FieldAsString([]string{""}); err != ErrNotFound {
		t.Fatal(err)
	}
	if val, err := rp.Rates["rat1"].IntervalRates[0].FieldAsString([]string{IntervalStart}); err != nil {
		t.Fatal(err)
	} else if exp := "10"; exp != val {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
	if val, exp := rp.Rates["rat1"].IntervalRates[0].String(), ToJSON(rp.Rates["rat1"].IntervalRates[0]); exp != val {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(val))
	}
}

func TestRateProfileMerge(t *testing.T) {
	acc := &RateProfile{
		Rates: map[string]*Rate{
			"rat1": {},
			"rat2": {Blocker: true},
		},
	}
	exp := &RateProfile{
		Tenant:          "cgrates.org",
		ID:              "ID",
		FilterIDs:       []string{"fltr1"},
		Weights:         DynamicWeights{{}},
		MinCost:         NewDecimal(10, 0),
		MaxCost:         NewDecimal(10, 0),
		MaxCostStrategy: "strategy",
		Rates: map[string]*Rate{
			"rat1": {
				ID:              "rat1",
				FilterIDs:       []string{"fltr1"},
				ActivationTimes: "* * * * *",
				Weights:         DynamicWeights{{}},
				Blocker:         true,
				IntervalRates:   []*IntervalRate{{}},
			},
			"rat2": {
				ID:      "rat2",
				Blocker: true,
			},
			"rat3": {},
		},
	}
	if acc.Merge(&RateProfile{
		Tenant:          "cgrates.org",
		ID:              "ID",
		FilterIDs:       []string{"fltr1"},
		Weights:         DynamicWeights{{}},
		MinCost:         NewDecimal(10, 0),
		MaxCost:         NewDecimal(10, 0),
		MaxCostStrategy: "strategy",
		Rates: map[string]*Rate{
			"rat1": {
				ID:              "rat1",
				FilterIDs:       []string{"fltr1"},
				ActivationTimes: "* * * * *",
				Weights:         DynamicWeights{{}},
				Blocker:         true,
				IntervalRates:   []*IntervalRate{{}},
			},
			"rat2": {
				ID: "rat2",
			},
			"rat3": {},
		},
	}); !reflect.DeepEqual(exp, acc) {
		t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(acc))
	}

}

func TestLibratesIntervalRateCloneEmpty(t *testing.T) {
	iR := &IntervalRate{}
	if rcv := iR.Clone(); !reflect.DeepEqual(rcv, iR) {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>",
			ToJSON(iR), ToJSON(rcv))
	}
}

func TestLibratesIntervalRateClone(t *testing.T) {
	iR := &IntervalRate{
		IntervalStart: NewDecimalFromFloat64(1.2),
		FixedFee:      NewDecimalFromFloat64(1.234),
		RecurrentFee:  NewDecimalFromFloat64(0.5),
		Unit:          NewDecimalFromFloat64(7.1),
		Increment:     NewDecimalFromFloat64(-321),
	}
	if rcv := iR.Clone(); !reflect.DeepEqual(rcv, iR) {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>",
			ToJSON(iR), ToJSON(rcv))
	}
}

func TestLibratesRateSIntervalCloneEmpty(t *testing.T) {
	ri := &RateSInterval{}
	if rcv := ri.Clone(); !reflect.DeepEqual(rcv, ri) {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>",
			ToJSON(ri), ToJSON(rcv))
	}
}

func TestLibratesRateSIntervalClone(t *testing.T) {
	ri := &RateSInterval{
		IntervalStart: NewDecimalFromFloat64(1.234),
		Increments: []*RateSIncrement{
			{
				IncrementStart:    NewDecimalFromFloat64(1.234),
				RateIntervalIndex: 1,
				RateID:            "Rate1",
				CompressFactor:    1,
				Usage:             NewDecimalFromFloat64(-321),
				cost:              decimal.New(4321, 5),
			},
			{
				IncrementStart:    NewDecimalFromFloat64(4.321),
				RateIntervalIndex: 1,
				RateID:            "Rate2",
				CompressFactor:    1,
				Usage:             NewDecimalFromFloat64(-123),
				cost:              decimal.New(123, 1),
			},
		},
		CompressFactor: 1,
		cost:           decimal.New(4321, 5),
	}
	if rcv := ri.Clone(); !reflect.DeepEqual(rcv, ri) {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>",
			ToJSON(ri), ToJSON(rcv))
	}
}

func TestLibratesRateSIncrementCloneEmpty(t *testing.T) {
	ri := &RateSIncrement{}
	if rcv := ri.Clone(); !reflect.DeepEqual(rcv, ri) {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>",
			ToJSON(ri), ToJSON(rcv))
	}
}

func TestLibratesRateSIncrementClone(t *testing.T) {
	ri := &RateSIncrement{
		IncrementStart:    NewDecimalFromFloat64(1.234),
		RateIntervalIndex: 1,
		RateID:            "testRateID",
		CompressFactor:    1,
		Usage:             NewDecimalFromFloat64(-321),
		cost:              decimal.New(4321, 5),
	}
	if rcv := ri.Clone(); !reflect.DeepEqual(rcv, ri) {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>",
			ToJSON(ri), ToJSON(rcv))
	}
}

func TestAsRateAPIConvert(t *testing.T) {
	ext := APIRate{

		Weights: "notEmpty",
	}
	expErr := "invalid DynamicWeight format for string <notEmpty>"

	if _, err := ext.AsRate(); err == nil || err.Error() != expErr {
		t.Errorf("%+v", err)
	}
}

func TestCloneRate(t *testing.T) {
	rt := &Rate{
		FilterIDs: []string{"id1", "id2"},
		Weights: DynamicWeights{
			{
				Weight: 0,
			},
		},
		IntervalRates: []*IntervalRate{
			{
				IntervalStart: NewDecimal(0, 0),
				RecurrentFee:  NewDecimal(12, 2),
			},
		},
	}

	exp := &Rate{
		FilterIDs: []string{"id1", "id2"},
		Weights: DynamicWeights{
			{
				Weight: 0,
			},
		},
		IntervalRates: []*IntervalRate{
			{
				IntervalStart: NewDecimal(0, 0),
				RecurrentFee:  NewDecimal(12, 2),
			},
		},
	}
	if rcv := rt.Clone(); !reflect.DeepEqual(exp, rcv) {

		t.Errorf("Expected <%v>, Received <%v>", exp, rcv)
	}

}

func TestEqualsNRpCt(t *testing.T) {
	rtPrfCost := &RateProfileCost{
		ID:              "RATE_1",
		Cost:            NewDecimal(2, 1),
		MinCost:         NewDecimal(1, 2),
		MaxCost:         NewDecimal(15, 0),
		MaxCostStrategy: "*round",
		CostIntervals: []*RateSIntervalCost{
			{
				Increments: []*RateSIncrementCost{
					{
						RateIntervalIndex: 0,
						RateID:            "RATE1",
						CompressFactor:    1,
						Usage:             NewDecimal(int64(time.Minute), 0),
					},
				},
				CompressFactor: 1,
			},
		},
		Rates: map[string]*IntervalRate{
			"RATE1": {
				IntervalStart: NewDecimal(0, 0),
				RecurrentFee:  NewDecimal(2, 1),
				Unit:          NewDecimal(int64(time.Second), 1),
				Increment:     NewDecimal(int64(time.Second), 1),
			},
		},
		Altered: []string{MetaRoundingDown},
	}
	expectedRT := &RateProfileCost{
		ID:              "RATE_1",
		Cost:            NewDecimal(2, 1),
		MinCost:         NewDecimal(1, 2),
		MaxCost:         NewDecimal(15, 0),
		MaxCostStrategy: "*round",
		CostIntervals: []*RateSIntervalCost{
			{
				Increments: []*RateSIncrementCost{
					{
						RateIntervalIndex: 0,
						RateID:            "RATE1",
						CompressFactor:    1,
						Usage:             NewDecimal(int64(time.Minute), 0),
					},
				},
				CompressFactor: 1,
			},
		},
		Rates: map[string]*IntervalRate{
			"RATE1": {
				IntervalStart: NewDecimal(0, 0),
				RecurrentFee:  NewDecimal(2, 1),
				Unit:          NewDecimal(int64(time.Second), 1),
				Increment:     NewDecimal(int64(time.Second), 1),
			},
		},
		Altered: []string{MetaRoundingUp},
	}
	if rtPrfCost.Equals(expectedRT) {
		t.Errorf("%v and \n%v are equals", ToJSON(rtPrfCost), ToJSON(expectedRT))
	}
}

func TestMergeRate(t *testing.T) {
	rt := &Rate{ID: "rate_id1",
		FilterIDs:       []string{"fltr1"},
		ActivationTimes: "1 1 3",
		Weights: DynamicWeights{
			{
				Weight: 10,
			},
		},
		Blocker: false,
		IntervalRates: []*IntervalRate{
			{
				IntervalStart: NewDecimal(int64(1), 0),
				FixedFee:      NewDecimal(int64(10), 0),
				RecurrentFee:  NewDecimal(int64(2), 0),
				Unit:          NewDecimal(int64(2), 0),
				Increment:     NewDecimal(int64(3), 0),
			},
		}}
	vi := &Rate{
		ID:              "rate_id1",
		FilterIDs:       []string{"fltr1"},
		ActivationTimes: "1 1 3",
		Weights: DynamicWeights{
			{
				Weight: 10,
			},
		},
		Blocker: false,
		IntervalRates: []*IntervalRate{
			{
				IntervalStart: NewDecimal(int64(1), 0),
				FixedFee:      NewDecimal(int64(10), 0),
				RecurrentFee:  NewDecimal(int64(2), 0),
				Unit:          NewDecimal(int64(2), 0),
				Increment:     NewDecimal(int64(3), 0),
			},
		},
	}

	rt.Merge(vi)

}

type MockMarshaler struct{}

func (MockMarshaler) Marshal(any) ([]byte, error) {
	return nil, ErrNotImplemented
}

func (MockMarshaler) Unmarshal([]byte, any) error {
	return ErrNotImplemented
}

func TestAsDataDBMap(t *testing.T) {

	ms := MockMarshaler{}
	rp := &RateProfile{
		FilterIDs: []string{"fltr1", "fltr2"},
		Weights: DynamicWeights{{

			Weight: 10,
		}},
		MinCost:         NewDecimal(10, 0),
		MaxCost:         NewDecimal(10, 0),
		MaxCostStrategy: "strategy",
		Rates: map[string]*Rate{
			"rat1": {ID: "rat1"}, "rat2": {ID: "rat2"},
		},
	}

	exp := "{\"FilterIDs\":\"fltr1;fltr2\",\"MaxCost\":\"10\",\"MaxCostStrategy\":\"strategy\",\"MinCost\":\"10\",\"Rates:rat1\":\"{\\\"ID\\\":\\\"rat1\\\",\\\"FilterIDs\\\":null,\\\"ActivationTimes\\\":\\\"\\\",\\\"Weights\\\":null,\\\"Blocker\\\":false,\\\"IntervalRates\\\":null}\",\"Rates:rat2\":\"{\\\"ID\\\":\\\"rat2\\\",\\\"FilterIDs\\\":null,\\\"ActivationTimes\\\":\\\"\\\",\\\"Weights\\\":null,\\\"Blocker\\\":false,\\\"IntervalRates\\\":null}\",\"Weights\":\";10\"}"

	if rcv, err := rp.AsDataDBMap(JSONMarshaler{}); err != nil {
		t.Error(err)

	} else if !reflect.DeepEqual(exp, ToJSON(rcv)) {
		t.Errorf("Expected <%v %T>, \nReceived <%v %T>", exp, exp, ToJSON(rcv), ToJSON(rcv))
	}

	if _, err := rp.AsDataDBMap(ms); err == nil || err != ErrNotImplemented {
		t.Errorf("Expected <%v>, Received <%v>", ErrNotImplemented, err)
	}
}

func TestNewRateProfileFromMapDataDBMap(t *testing.T) {
	mapRP := map[string]any{
		"FilterIDs":  "fltrID1",
		"Weights":    "fltrID1;20",
		"MinCost":    "2",
		"MaxCost":    "10",
		"Rates:rat1": "{\"ID\":\"rat1\",\"FilterIDs\":null,\"ActivationTimes\":\"\",\"Weights\":null,\"Blocker\":false,\"IntervalRates\":null}",
	}
	if rcv, err := NewRateProfileFromMapDataDBMap("cgrates.org", "ExID", mapRP, JSONMarshaler{}); err != nil {
		t.Error(rcv, err)
	}

	mapRP = map[string]any{
		"FilterIDs": "fltrID1",
		"Weights":   "wrong",
	}
	expErr := "invalid DynamicWeight format for string <wrong>"
	if _, err := NewRateProfileFromMapDataDBMap("cgrates.org", "ExID", mapRP, JSONMarshaler{}); err == nil || err.Error() != expErr {
		t.Errorf("Expected error <%v>, received <%v>", expErr, err)
	}
	mapRP = map[string]any{
		"FilterIDs": "fltrID1",
		"MinCost":   "wrong",
	}
	expErr = "can't convert <wrong> to decimal"
	if _, err := NewRateProfileFromMapDataDBMap("cgrates.org", "ExID", mapRP, JSONMarshaler{}); err == nil || err.Error() != expErr {
		t.Errorf("Expected error <%v>, received <%v>", expErr, err)
	}
	mapRP = map[string]any{
		"FilterIDs": "fltrID1",
		"MaxCost":   "wrong",
	}
	expErr = "can't convert <wrong> to decimal"
	if _, err := NewRateProfileFromMapDataDBMap("cgrates.org", "ExID", mapRP, JSONMarshaler{}); err == nil || err.Error() != expErr {
		t.Errorf("Expected error <%v>, received <%v>", expErr, err)
	}
	mapRP = map[string]any{
		"FilterIDs":  "fltrID1",
		"Rates:rat1": "{\"ID\":\"rat1\",\"FilterIDs\":null,\"ActivationTimes\":\"\",\"Weights\":null,\"Blocker\":false,\"IntervalRates\":null}\",\"Rates:rat2\":\"{\"ID\":\"rat2\",\"FilterIDs\":null,\"ActivationTimes\":\"\",\"Weights\":null,\"Blocker\":false,\"IntervalRates\":null}\",\"Weights\":\";10\"}",
	}
	expErr = "invalid character '\"' after top-level value"
	if _, err := NewRateProfileFromMapDataDBMap("cgrates.org", "ExID", mapRP, JSONMarshaler{}); err == nil || err.Error() != expErr {
		t.Errorf("Expected error <%v>, received <%v>", expErr, err)
	}
}

func TestRateSIncrementFieldAsInterface(t *testing.T) {
	incStart := &Decimal{Big: decimal.New(123, -2)}
	usage := &Decimal{Big: decimal.New(456, -2)}

	rI := &RateSIncrement{
		IncrementStart:    incStart,
		RateIntervalIndex: 5,
		RateID:            "rateID1",
		CompressFactor:    2,
		Usage:             usage,
	}

	tests := []struct {
		name        string
		fldPath     []string
		expectedVal any
		expectErr   bool
		expectedErr string
	}{
		{
			name:        "IncrementStart returns Decimal",
			fldPath:     []string{IncrementStart},
			expectedVal: incStart,
			expectErr:   false,
		},
		{
			name:        "RateIntervalIndex returns int",
			fldPath:     []string{RateIntervalIndex},
			expectedVal: 5,
			expectErr:   false,
		},
		{
			name:        "RateID returns string",
			fldPath:     []string{RateID},
			expectedVal: "rateID1",
			expectErr:   false,
		},
		{
			name:        "CompressFactor returns int64",
			fldPath:     []string{CompressFactor},
			expectedVal: int64(2),
			expectErr:   false,
		},
		{
			name:        "Usage returns Decimal",
			fldPath:     []string{Usage},
			expectedVal: usage,
			expectErr:   false,
		},
		{
			name:        "UnknownField returns error",
			fldPath:     []string{"UnknownField"},
			expectedVal: nil,
			expectErr:   true,
			expectedErr: "unsupported field prefix",
		},
		{
			name:        "Empty path returns error",
			fldPath:     []string{},
			expectedVal: nil,
			expectErr:   true,
			expectedErr: "NOT_FOUND",
		},
		{
			name:        "Too many path parts returns error",
			fldPath:     []string{"NFound", "NFound"},
			expectedVal: nil,
			expectErr:   true,
			expectedErr: "NOT_FOUND",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			val, err := rI.FieldAsInterface(tt.fldPath)

			if tt.expectErr {
				if err == nil {
					t.Errorf("Expected error for field path %v, got nil", tt.fldPath)
				} else if tt.expectedErr != "" && !strings.Contains(strings.ToUpper(err.Error()), strings.ToUpper(tt.expectedErr)) {
					t.Errorf("Expected error containing %q, got %v", tt.expectedErr, err)
				}
			} else {
				if err != nil {
					t.Errorf("Unexpected error for field path %v: %v", tt.fldPath, err)
				} else if !reflect.DeepEqual(val, tt.expectedVal) {
					t.Errorf("For field path %v, expected %v, got %v", tt.fldPath, tt.expectedVal, val)
				}
			}
		})
	}
}

func TestRateProfileSetFilterIDs(t *testing.T) {
	rp := &RateProfile{
		Tenant: "cgrates.org",
		ID:     "RP1",
		Rates: map[string]*Rate{
			"RT_MIN": {
				ID:              "RT_MIN",
				Weights:         DynamicWeights{{Weight: 0}},
				ActivationTimes: "* * * * *",
				IntervalRates: []*IntervalRate{
					{
						IntervalStart: NewDecimal(0, 0),
						RecurrentFee:  NewDecimal(1, 2),
					},
				},
			},
		},
	}

	path := []string{FilterIDs}

	err := rp.Set(path, []string{"*string:~*req.CustomerID:1001"}, false)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	err = rp.Set(path, []string{"*string:~*req.CustomerID:1001"}, false)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	// even thought they are identical filters, they are both appended

	if len(rp.FilterIDs) != 2 {
		t.Fatalf("expected 2 duplicated FilterIDs, got %d", len(rp.FilterIDs))
	}

	if rp.FilterIDs[0] != rp.FilterIDs[1] {
		t.Fatalf("expected duplicates to be identical, got %#v", rp.FilterIDs)
	}
}
