From bb6a7ea6c3f2c03adc2225d65e63c938ae4c418e Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 21 May 2015 15:41:05 +0300 Subject: [PATCH] timings and rate groups validation --- engine/loader_csv.go | 6 +- engine/loader_helpers.go | 21 +++---- engine/ratingplan.go | 31 +++++++++- engine/ratingplan_test.go | 117 +++++++++++++++++++++++++++++++++----- engine/storage_sql.go | 3 - 5 files changed, 143 insertions(+), 35 deletions(-) diff --git a/engine/loader_csv.go b/engine/loader_csv.go index 511801110..2562296ce 100644 --- a/engine/loader_csv.go +++ b/engine/loader_csv.go @@ -162,12 +162,8 @@ func (csvr *CSVReader) LoadRates() (err error) { return err } // same tag only to create rate groups - existingRates, exists := csvr.tp.rates[tag] + _, exists := csvr.tp.rates[tag] if exists { - rss := existingRates.RateSlots - if err := ValidNextGroup(rss[len(rss)-1], r.RateSlots[0]); err != nil { - return fmt.Errorf("RatesTag: %s, error: <%s>", tag, err.Error()) - } csvr.tp.rates[tag].RateSlots = append(csvr.tp.rates[tag].RateSlots, r.RateSlots[0]) } else { csvr.tp.rates[tag] = r diff --git a/engine/loader_helpers.go b/engine/loader_helpers.go index 8b42034f4..f31d5ef41 100644 --- a/engine/loader_helpers.go +++ b/engine/loader_helpers.go @@ -23,7 +23,6 @@ import ( "errors" "fmt" "log" - "math" "os" "path" "regexp" @@ -170,10 +169,18 @@ func NewTPData() *TPData { func (tp *TPData) IsValid() bool { valid := true for rplTag, rpl := range tp.ratingPlans { - if !rpl.IsValid() { + if !rpl.isContinous() { log.Printf("The rating plan %s is not covering all weekdays", rplTag) valid = false } + if !rpl.areRatesSane() { + log.Printf("The rating plan %s contains invalid rate groups", rplTag) + valid = false + } + if !rpl.areTimingsSane() { + log.Printf("The rating plan %s contains invalid timings", rplTag) + valid = false + } } return valid } @@ -507,16 +514,6 @@ func NewLoadRate(tag, connectFee, price, ratedUnits, rateIncrements, groupInterv return } -func ValidNextGroup(present, next *utils.RateSlot) error { - if next.GroupIntervalStartDuration() <= present.GroupIntervalStartDuration() { - return errors.New(fmt.Sprintf("Next rate group interval start: %#v must be heigher than the previous one: %#v", next, present)) - } - if math.Mod(next.GroupIntervalStartDuration().Seconds(), present.RateIncrementDuration().Seconds()) != 0 { - return errors.New(fmt.Sprintf("GroupIntervalStart of %#v must be a multiple of RateIncrement of %#v", next, present)) - } - return nil -} - func NewTiming(timingInfo ...string) (rt *utils.TPTiming) { rt = &utils.TPTiming{} rt.Id = timingInfo[0] diff --git a/engine/ratingplan.go b/engine/ratingplan.go index 615f070c4..c0606e347 100644 --- a/engine/ratingplan.go +++ b/engine/ratingplan.go @@ -20,6 +20,7 @@ package engine import ( "encoding/json" + "math" "github.com/cgrates/cgrates/history" ) @@ -114,7 +115,7 @@ func (rp *RatingPlan) GetHistoryRecord() history.Record { } // IsValid determines if the rating plan covers a continous period of time -func (rp *RatingPlan) IsValid() bool { +func (rp *RatingPlan) isContinous() bool { weekdays := make([]int, 7) for _, tm := range rp.Timings { // if it is a blank timing than it will match all @@ -146,3 +147,31 @@ func (rp *RatingPlan) IsValid() bool { } return false } + +func (rp *RatingPlan) areRatesSane() bool { + for _, rating := range rp.Ratings { + rating.Rates.Sort() + for i, rate := range rating.Rates { + if i < (len(rating.Rates) - 1) { + nextRate := rating.Rates[i+1] + if nextRate.GroupIntervalStart <= rate.GroupIntervalStart { + return false + } + if math.Mod(nextRate.GroupIntervalStart.Seconds(), rate.RateIncrement.Seconds()) != 0 { + return false + } + } + } + } + return true +} + +func (rp *RatingPlan) areTimingsSane() bool { + for _, timing := range rp.Timings { + if (len(timing.Years) != 0 || len(timing.Months) != 0 || len(timing.MonthDays) != 0) && + len(timing.WeekDays) != 0 { + return false + } + } + return true +} diff --git a/engine/ratingplan_test.go b/engine/ratingplan_test.go index a20393a9d..6286a1c52 100644 --- a/engine/ratingplan_test.go +++ b/engine/ratingplan_test.go @@ -218,26 +218,26 @@ func TestGetActiveForCall(t *testing.T) { } } -func TestRatingPlanIsValidEmpty(t *testing.T) { +func TestRatingPlanIsContinousEmpty(t *testing.T) { rpl := &RatingPlan{} - if rpl.IsValid() { + if rpl.isContinous() { t.Errorf("Error determining rating plan's valididty: %+v", rpl) } } -func TestRatingPlanIsValidBlank(t *testing.T) { +func TestRatingPlanIsContinousBlank(t *testing.T) { rpl := &RatingPlan{ Timings: map[string]*RITiming{ "blank": &RITiming{StartTime: "00:00:00"}, "other": &RITiming{WeekDays: utils.WeekDays{1, 2, 3}, StartTime: "00:00:00"}, }, } - if !rpl.IsValid() { + if !rpl.isContinous() { t.Errorf("Error determining rating plan's valididty: %+v", rpl) } } -func TestRatingPlanIsValidGood(t *testing.T) { +func TestRatingPlanIsContinousGood(t *testing.T) { rpl := &RatingPlan{ Timings: map[string]*RITiming{ "first": &RITiming{WeekDays: utils.WeekDays{1, 2, 3}, StartTime: "00:00:00"}, @@ -245,24 +245,24 @@ func TestRatingPlanIsValidGood(t *testing.T) { "third": &RITiming{WeekDays: utils.WeekDays{0}, StartTime: "00:00:00"}, }, } - if !rpl.IsValid() { + if !rpl.isContinous() { t.Errorf("Error determining rating plan's valididty: %+v", rpl) } } -func TestRatingPlanIsValidBad(t *testing.T) { +func TestRatingPlanisContinousBad(t *testing.T) { rpl := &RatingPlan{ Timings: map[string]*RITiming{ "first": &RITiming{WeekDays: utils.WeekDays{1, 2, 3}, StartTime: "00:00:00"}, "second": &RITiming{WeekDays: utils.WeekDays{4, 5, 0}, StartTime: "00:00:00"}, }, } - if rpl.IsValid() { + if rpl.isContinous() { t.Errorf("Error determining rating plan's valididty: %+v", rpl) } } -func TestRatingPlanIsValidSpecial(t *testing.T) { +func TestRatingPlanIsContinousSpecial(t *testing.T) { rpl := &RatingPlan{ Timings: map[string]*RITiming{ "special": &RITiming{Years: utils.Years{2015}, Months: utils.Months{5}, MonthDays: utils.MonthDays{1}, StartTime: "00:00:00"}, @@ -271,12 +271,12 @@ func TestRatingPlanIsValidSpecial(t *testing.T) { "third": &RITiming{WeekDays: utils.WeekDays{0}, StartTime: "00:00:00"}, }, } - if !rpl.IsValid() { + if !rpl.isContinous() { t.Errorf("Error determining rating plan's valididty: %+v", rpl) } } -func TestRatingPlanIsValidMultiple(t *testing.T) { +func TestRatingPlanIsContinousMultiple(t *testing.T) { rpl := &RatingPlan{ Timings: map[string]*RITiming{ "special": &RITiming{Years: utils.Years{2015}, Months: utils.Months{5}, MonthDays: utils.MonthDays{1}, StartTime: "00:00:00"}, @@ -286,12 +286,12 @@ func TestRatingPlanIsValidMultiple(t *testing.T) { "third": &RITiming{WeekDays: utils.WeekDays{0}, StartTime: "00:00:00"}, }, } - if !rpl.IsValid() { + if !rpl.isContinous() { t.Errorf("Error determining rating plan's valididty: %+v", rpl) } } -func TestRatingPlanIsValidMissing(t *testing.T) { +func TestRatingPlanIsContinousMissing(t *testing.T) { rpl := &RatingPlan{ Timings: map[string]*RITiming{ "special": &RITiming{Years: utils.Years{2015}, Months: utils.Months{5}, MonthDays: utils.MonthDays{1}, StartTime: "00:00:00"}, @@ -300,11 +300,100 @@ func TestRatingPlanIsValidMissing(t *testing.T) { "third": &RITiming{WeekDays: utils.WeekDays{0}, StartTime: "00:00:00"}, }, } - if rpl.IsValid() { + if rpl.isContinous() { t.Errorf("Error determining rating plan's valididty: %+v", rpl) } } +func TestRatingPlanSaneTimingsBad(t *testing.T) { + rpl := &RatingPlan{ + Timings: map[string]*RITiming{ + "one": &RITiming{Years: utils.Years{2015}, WeekDays: utils.WeekDays{time.Monday}}, + }, + } + if rpl.areTimingsSane() { + t.Errorf("Error detecting bad timings in rating profile: %+v", rpl) + } +} + +func TestRatingPlanSaneTimingsGood(t *testing.T) { + rpl := &RatingPlan{ + Timings: map[string]*RITiming{ + "one": &RITiming{Years: utils.Years{2015}}, + "two": &RITiming{WeekDays: utils.WeekDays{0, 1, 2, 3, 4}, StartTime: "00:00:00"}, + }, + } + if !rpl.areTimingsSane() { + t.Errorf("Error detecting bad timings in rating profile: %+v", rpl) + } +} + +func TestRatingPlanSaneRatingsEqual(t *testing.T) { + rpl := &RatingPlan{ + Ratings: map[string]*RIRate{ + "one": &RIRate{ + Rates: RateGroups{ + &Rate{ + GroupIntervalStart: 0 * time.Second, + RateIncrement: 30 * time.Second, + }, + &Rate{ + GroupIntervalStart: 0 * time.Second, + RateIncrement: 30 * time.Second, + }, + }, + }, + }, + } + if rpl.areRatesSane() { + t.Errorf("Error detecting bad rate groups in rating profile: %+v", rpl) + } +} + +func TestRatingPlanSaneRatingsNotMultiple(t *testing.T) { + rpl := &RatingPlan{ + Ratings: map[string]*RIRate{ + "one": &RIRate{ + Rates: RateGroups{ + &Rate{ + GroupIntervalStart: 0 * time.Second, + RateIncrement: 30 * time.Second, + }, + &Rate{ + GroupIntervalStart: 15 * time.Second, + RateIncrement: 30 * time.Second, + }, + }, + }, + }, + } + if rpl.areRatesSane() { + t.Errorf("Error detecting bad rate groups in rating profile: %+v", rpl) + } +} + +func TestRatingPlanSaneRatingsGoot(t *testing.T) { + rpl := &RatingPlan{ + Ratings: map[string]*RIRate{ + "one": &RIRate{ + Rates: RateGroups{ + &Rate{ + GroupIntervalStart: 60 * time.Second, + RateIncrement: 30 * time.Second, + }, + &Rate{ + GroupIntervalStart: 0 * time.Second, + RateIncrement: 30 * time.Second, + }, + }, + }, + }, + } + if !rpl.areRatesSane() { + t.Errorf("Error detecting bad rate groups in rating profile: %+v", rpl) + } +} + /**************************** Benchmarks *************************************/ func BenchmarkRatingPlanMarshalJson(b *testing.B) { diff --git a/engine/storage_sql.go b/engine/storage_sql.go index 6a31ba73a..f53487fd8 100644 --- a/engine/storage_sql.go +++ b/engine/storage_sql.go @@ -1234,9 +1234,6 @@ func (self *SQLStorage) GetTpRates(tpid, tag string) (map[string]*utils.TPRate, // same tag only to create rate groups er, exists := rts[tr.Tag] if exists { - if err := ValidNextGroup(er.RateSlots[len(er.RateSlots)-1], r.RateSlots[0]); err != nil { - return nil, err - } er.RateSlots = append(er.RateSlots, r.RateSlots[0]) } else { rts[tr.Tag] = r