From a483d2304e42dac54eaa375b71ec822c802d49a1 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 18 Nov 2013 19:35:53 +0200 Subject: [PATCH] tests for rating info activation times --- engine/calldesc.go | 26 +++++++++++------- engine/calldesc_test.go | 2 +- engine/loader_csv_test.go | 38 +++++++++++++++----------- engine/ratingprofile.go | 7 +++-- engine/ratingprofile_test.go | 52 ++++++++++++++++++++++++++---------- 5 files changed, 83 insertions(+), 42 deletions(-) diff --git a/engine/calldesc.go b/engine/calldesc.go index 8e6f1049f..dcae9b82e 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -21,6 +21,7 @@ package engine import ( "errors" "fmt" + "github.com/cgrates/cgrates/cache2go" "github.com/cgrates/cgrates/history" "github.com/cgrates/cgrates/utils" @@ -136,12 +137,10 @@ func (cd *CallDescriptor) getUserBalance() (ub *UserBalance, err error) { Restores the activation periods for the specified prefix from storage. */ func (cd *CallDescriptor) LoadRatingPlans() (err error) { - err = cd.getRatingPlansForPrefix(cd.GetKey(), 1) - if err != nil { - // try general fallback - fallbackKey := fmt.Sprintf("%s:%s:%s:%s", cd.Direction, cd.Tenant, cd.TOR, FALLBACK_SUBJECT) + err = cd.getRatingPlansForPrefix(cd.GetKey(cd.Subject), 1) + if err != nil || !cd.continousRatingInfos() { // use the default subject - err = cd.getRatingPlansForPrefix(fallbackKey, 1) + err = cd.getRatingPlansForPrefix(cd.GetKey(FALLBACK_SUBJECT), 1) } //load the rating plans if err != nil || !cd.continousRatingInfos() { @@ -164,6 +163,7 @@ func (cd *CallDescriptor) getRatingPlansForPrefix(key string, recursionDepth int } if err = rp.GetRatingPlansForPrefix(cd); err != nil || !cd.continousRatingInfos() { // try rating profile fallback + recursionDepth++ for index := 0; index < len(cd.RatingInfos); index++ { ri := cd.RatingInfos[index] if len(ri.RateIntervals) > 0 { @@ -171,8 +171,12 @@ func (cd *CallDescriptor) getRatingPlansForPrefix(key string, recursionDepth int continue } if len(ri.FallbackKeys) > 0 { - recursionDepth++ - tempCD := &CallDescriptor{} + tempCD := &CallDescriptor{ + TOR: cd.TOR, + Direction: cd.Direction, + Tenant: cd.Tenant, + Destination: cd.Destination, + } if index == 0 { tempCD.TimeStart = cd.TimeStart } else { @@ -189,6 +193,10 @@ func (cd *CallDescriptor) getRatingPlansForPrefix(key string, recursionDepth int } // extract the rate infos and break for newIndex, newRI := range tempCD.RatingInfos { + // check if the new ri is filled + if len(newRI.RateIntervals) == 0 { + continue + } if newIndex == 0 { cd.RatingInfos[index] = newRI } else { @@ -254,8 +262,8 @@ func (cd *CallDescriptor) addRatingInfos(ris RatingInfos) bool { Constructs the key for the storage lookup. The prefixLen is limiting the length of the destination prefix. */ -func (cd *CallDescriptor) GetKey() string { - return fmt.Sprintf("%s:%s:%s:%s", cd.Direction, cd.Tenant, cd.TOR, cd.Subject) +func (cd *CallDescriptor) GetKey(subject string) string { + return fmt.Sprintf("%s:%s:%s:%s", cd.Direction, cd.Tenant, cd.TOR, subject) } /* diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index 8c04c6d08..6b131c346 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -341,7 +341,7 @@ func BenchmarkStorageGetting(b *testing.B) { cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2} b.StartTimer() for i := 0; i < b.N; i++ { - storageGetter.GetRatingProfile(cd.GetKey()) + storageGetter.GetRatingProfile(cd.GetKey(cd.Subject)) } } diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 8892b6dd6..e61320e56 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -88,6 +88,8 @@ EVENING,P2,WORKDAYS_18,10 EVENING,P2,WEEKENDS,10 TDRT,T1,WORKDAYS_00,10 TDRT,T2,WORKDAYS_00,10 +G,RT_STANDARD,WORKDAYS_00,10 +R,P1,WORKDAYS_00,10 ` ratingProfiles = ` CUSTOMER_1,0,*out,rif:from:tm,2012-01-01T00:00:00Z,PREMIUM,danb @@ -102,6 +104,10 @@ vdf,0,*out,one,2012-02-28T00:00:00Z,STANDARD, vdf,0,*out,inf,2012-02-28T00:00:00Z,STANDARD,inf vdf,0,*out,fall,2012-02-28T00:00:00Z,PREMIUM,rif test,0,*out,trp,2013-10-01T00:00:00Z,TDRT,rif;danb +vdf,0,*out,fallback1,2013-11-18T13:45:00Z,G,fallback2 +vdf,0,*out,fallback1,2013-11-18T13:46:00Z,G,fallback2 +vdf,0,*out,fallback1,2013-11-18T13:47:00Z,G,fallback2 +vdf,0,*out,fallback2,2013-11-18T13:45:00Z,R,rif ` actions = ` MINI,*topup_reset,*monetary,*out,10,*unlimited,,,10,,10 @@ -240,68 +246,68 @@ func TestLoadRates(t *testing.T) { t.Error("Failed to load rates: ", csvr.rates) } rate := csvr.rates["R1"].RateSlots[0] - expctRs,err := utils.NewRateSlot(0, 0.2, "60s", "1s", "0", utils.ROUNDING_MIDDLE, 2) + expctRs, err := utils.NewRateSlot(0, 0.2, "60s", "1s", "0", utils.ROUNDING_MIDDLE, 2) if err != nil { t.Error("Error loading rate: ", rate, err.Error()) } else if !reflect.DeepEqual(rate, expctRs) || rate.RateUnitDuration() != expctRs.RateUnitDuration() || rate.RateIncrementDuration() != expctRs.RateIncrementDuration() || rate.GroupIntervalStartDuration() != expctRs.GroupIntervalStartDuration() { - t.Error("Error loading rate: ", rate, expctRs) + t.Error("Error loading rate: ", rate, expctRs) } rate = csvr.rates["R2"].RateSlots[0] - if expctRs,err = utils.NewRateSlot(0, 0.1, "60s", "1s", "0", utils.ROUNDING_MIDDLE, 2); err != nil { + if expctRs, err = utils.NewRateSlot(0, 0.1, "60s", "1s", "0", utils.ROUNDING_MIDDLE, 2); err != nil { t.Error("Error loading rate: ", rate, err.Error()) } else if !reflect.DeepEqual(rate, expctRs) || rate.RateUnitDuration() != expctRs.RateUnitDuration() || rate.RateIncrementDuration() != expctRs.RateIncrementDuration() || rate.GroupIntervalStartDuration() != expctRs.GroupIntervalStartDuration() { - t.Error("Error loading rate: ", rate) + t.Error("Error loading rate: ", rate) } rate = csvr.rates["R3"].RateSlots[0] - if expctRs, err = utils.NewRateSlot( 0, 0.05, "60s", "1s", "0", utils.ROUNDING_MIDDLE, 2 ); err != nil { + if expctRs, err = utils.NewRateSlot(0, 0.05, "60s", "1s", "0", utils.ROUNDING_MIDDLE, 2); err != nil { t.Error("Error loading rate: ", rate, err.Error()) } else if !reflect.DeepEqual(rate, expctRs) || rate.RateUnitDuration() != expctRs.RateUnitDuration() || rate.RateIncrementDuration() != expctRs.RateIncrementDuration() || rate.GroupIntervalStartDuration() != expctRs.GroupIntervalStartDuration() { - t.Error("Error loading rate: ", rate) + t.Error("Error loading rate: ", rate) } rate = csvr.rates["R4"].RateSlots[0] - if expctRs, err = utils.NewRateSlot( 1, 1.0, "1s", "1s", "0", utils.ROUNDING_UP, 2 ); err != nil { + if expctRs, err = utils.NewRateSlot(1, 1.0, "1s", "1s", "0", utils.ROUNDING_UP, 2); err != nil { t.Error("Error loading rate: ", rate, err.Error()) } else if !reflect.DeepEqual(rate, expctRs) || rate.RateUnitDuration() != expctRs.RateUnitDuration() || rate.RateIncrementDuration() != expctRs.RateIncrementDuration() || rate.GroupIntervalStartDuration() != expctRs.GroupIntervalStartDuration() { - t.Error("Error loading rate: ", rate) + t.Error("Error loading rate: ", rate) } rate = csvr.rates["R5"].RateSlots[0] - if expctRs, err = utils.NewRateSlot( 0, 0.5, "1s", "1s", "0", utils.ROUNDING_DOWN, 2 ); err != nil { + if expctRs, err = utils.NewRateSlot(0, 0.5, "1s", "1s", "0", utils.ROUNDING_DOWN, 2); err != nil { t.Error("Error loading rate: ", rate, err.Error()) } else if !reflect.DeepEqual(rate, expctRs) || rate.RateUnitDuration() != expctRs.RateUnitDuration() || rate.RateIncrementDuration() != expctRs.RateIncrementDuration() || rate.GroupIntervalStartDuration() != expctRs.GroupIntervalStartDuration() { - t.Error("Error loading rate: ", rate) + t.Error("Error loading rate: ", rate) } rate = csvr.rates["LANDLINE_OFFPEAK"].RateSlots[0] - if expctRs, err = utils.NewRateSlot( 0, 1, "1s", "60s", "0s", utils.ROUNDING_UP, 4 ); err != nil { + if expctRs, err = utils.NewRateSlot(0, 1, "1s", "60s", "0s", utils.ROUNDING_UP, 4); err != nil { t.Error("Error loading rate: ", rate, err.Error()) } else if !reflect.DeepEqual(rate, expctRs) || rate.RateUnitDuration() != expctRs.RateUnitDuration() || rate.RateIncrementDuration() != expctRs.RateIncrementDuration() || rate.GroupIntervalStartDuration() != expctRs.GroupIntervalStartDuration() { - t.Error("Error loading rate: ", rate) + t.Error("Error loading rate: ", rate) } rate = csvr.rates["LANDLINE_OFFPEAK"].RateSlots[1] - if expctRs, err = utils.NewRateSlot( 0, 1, "1s", "1s", "60s", utils.ROUNDING_UP, 4 ); err != nil { + if expctRs, err = utils.NewRateSlot(0, 1, "1s", "1s", "60s", utils.ROUNDING_UP, 4); err != nil { t.Error("Error loading rate: ", rate, err.Error()) } else if !reflect.DeepEqual(rate, expctRs) || rate.RateUnitDuration() != expctRs.RateUnitDuration() || rate.RateIncrementDuration() != expctRs.RateIncrementDuration() || rate.GroupIntervalStartDuration() != expctRs.GroupIntervalStartDuration() { - t.Error("Error loading rate: ", rate) + t.Error("Error loading rate: ", rate) } } @@ -418,7 +424,7 @@ func TestLoadDestinationRates(t *testing.T) { } func TestLoadDestinationRateTimings(t *testing.T) { - if len(csvr.ratingPlans) != 5 { + if len(csvr.ratingPlans) != 7 { t.Error("Failed to load rate timings: ", csvr.ratingPlans) } rplan := csvr.ratingPlans["STANDARD"] @@ -538,7 +544,7 @@ func TestLoadDestinationRateTimings(t *testing.T) { } func TestLoadRatingProfiles(t *testing.T) { - if len(csvr.ratingProfiles) != 10 { + if len(csvr.ratingProfiles) != 12 { t.Error("Failed to load rating profiles: ", len(csvr.ratingProfiles), csvr.ratingProfiles) } rp := csvr.ratingProfiles["*out:test:0:trp"] diff --git a/engine/ratingprofile.go b/engine/ratingprofile.go index 92ce57a2b..d5e6b6122 100644 --- a/engine/ratingprofile.go +++ b/engine/ratingprofile.go @@ -100,10 +100,9 @@ func (ris RatingInfos) Sort() { sort.Sort(ris) } -// TODO: what happens if there is no match for part of the call func (rp *RatingProfile) GetRatingPlansForPrefix(cd *CallDescriptor) (err error) { var ris RatingInfos - for _, rpa := range rp.RatingPlanActivations.GetActiveForCall(cd) { + for index, rpa := range rp.RatingPlanActivations.GetActiveForCall(cd) { rpl, err := storageGetter.GetRatingPlan(rpa.RatingPlanId) if err != nil || rpl == nil { Logger.Err(fmt.Sprintf("Error checking destination: %v", err)) @@ -124,6 +123,10 @@ func (rp *RatingProfile) GetRatingPlansForPrefix(cd *CallDescriptor) (err error) rps = rpl.RateIntervalList(dId) } } + // check if it's the first ri and add a blank one for the initial part not covered + if index == 0 && cd.TimeStart.Before(rpa.ActivationTime) { + ris = append(ris, &RatingInfo{"", "", cd.TimeStart, nil, []string{cd.GetKey(FALLBACK_SUBJECT)}}) + } if bestPrecision > 0 { ris = append(ris, &RatingInfo{rp.Id, cd.Destination[:bestPrecision], rpa.ActivationTime, rps, rpa.FallbackKeys}) } else { diff --git a/engine/ratingprofile_test.go b/engine/ratingprofile_test.go index 6655c2596..3a12f63c9 100644 --- a/engine/ratingprofile_test.go +++ b/engine/ratingprofile_test.go @@ -20,20 +20,44 @@ package engine import ( "testing" + "time" ) -func TestRpAddAPIfNotPresent(t *testing.T) { - /* ap1 := &RatingPlan{Id: "test1"} - ap2 := &RatingPlan{Id: "test1"} - ap3 := &RatingPlan{Id: "test2"} - rp := &RatingProfile{} - rp.AddRatingPlanIfNotPresent("test", ap1) - rp.AddRatingPlanIfNotPresent("test", ap2) - if len(rp.DestinationMap["test"]) != 1 { - t.Error("Wronfully appended activation period ;)", len(rp.DestinationMap["test"])) - } - rp.AddRatingPlanIfNotPresent("test", ap3) - if len(rp.DestinationMap["test"]) != 2 { - t.Error("Wronfully not appended activation period ;)", len(rp.DestinationMap["test"])) - }*/ +func TestGetRatingProfileForPrefix(t *testing.T) { + cd := &CallDescriptor{ + TimeStart: time.Date(2013, 11, 18, 13, 45, 1, 0, time.UTC), + TimeEnd: time.Date(2013, 11, 18, 13, 47, 30, 0, time.UTC), + Tenant: "vdf", + TOR: "0", + Direction: OUTBOUND, + Subject: "fallback1", + Destination: "0256098", + } + cd.LoadRatingPlans() + if len(cd.RatingInfos) != 3 || !cd.continousRatingInfos() { + t.Logf("0: %+v", cd.RatingInfos[0]) + t.Logf("1: %+v", cd.RatingInfos[1]) + t.Logf("2: %+v", cd.RatingInfos[2]) + t.Errorf("Error loading rating information: %+v %+v", cd.RatingInfos, cd.continousRatingInfos()) + } +} + +func TestGetRatingProfileForPrefixFirstEmpty(t *testing.T) { + cd := &CallDescriptor{ + TimeStart: time.Date(2013, 11, 18, 13, 44, 1, 0, time.UTC), + TimeEnd: time.Date(2013, 11, 18, 13, 47, 30, 0, time.UTC), + Tenant: "vdf", + TOR: "0", + Direction: OUTBOUND, + Subject: "fallback1", + Destination: "0256098", + } + cd.LoadRatingPlans() + if len(cd.RatingInfos) != 4 || !cd.continousRatingInfos() { + t.Logf("0: %+v", cd.RatingInfos[0]) + t.Logf("1: %+v", cd.RatingInfos[1]) + t.Logf("2: %+v", cd.RatingInfos[2]) + t.Logf("3: %+v", cd.RatingInfos[3]) + t.Errorf("Error loading rating information: %+v %+v", cd.RatingInfos, cd.continousRatingInfos()) + } }