From 43296dd9e88c2bee6e5aa43ff87256d202dde044 Mon Sep 17 00:00:00 2001 From: DanB Date: Thu, 28 Feb 2019 19:30:01 +0100 Subject: [PATCH] EventCost.SyncKeys method with tests --- apier/v1/attributes_it_test.go | 28 +++- engine/eventcost.go | 101 ++++++++++++++ engine/eventcost_test.go | 241 +++++++++++++++++++++++++++++++++ sessions/sessions.go | 3 +- utils/consts.go | 1 + 5 files changed, 370 insertions(+), 4 deletions(-) diff --git a/apier/v1/attributes_it_test.go b/apier/v1/attributes_it_test.go index 8d9f5af7c..1160c46b4 100644 --- a/apier/v1/attributes_it_test.go +++ b/apier/v1/attributes_it_test.go @@ -53,6 +53,7 @@ var sTestsAlsPrf = []func(t *testing.T){ testAttributeSGetAttributeForEventNotFound, testAttributeSGetAttributeForEventWithMetaAnyContext, testAttributeSProcessEvent, + testAttributeSProcessEventNotFound, testAttributeSProcessEventMissing, testAttributeSProcessEventWithNoneSubstitute, testAttributeSProcessEventWithNoneSubstitute2, @@ -356,6 +357,26 @@ func testAttributeSProcessEvent(t *testing.T) { } } +func testAttributeSProcessEventNotFound(t *testing.T) { + ev := &engine.AttrArgsProcessEvent{ + Context: utils.StringPointer(utils.MetaSessionS), + CGREvent: utils.CGREvent{ + Tenant: "cgrates.org", + ID: "testAttributeSProcessEventNotFound", + Event: map[string]interface{}{ + utils.Account: "Inexistent", + utils.Destination: "+491511231234", + }, + }, + } + var rplyEv engine.AttrSProcessEventReply + if err := attrSRPC.Call(utils.AttributeSv1ProcessEvent, + ev, &rplyEv); err == nil || + err.Error() != utils.ErrNotFound.Error() { + t.Error(err) + } +} + func testAttributeSProcessEventMissing(t *testing.T) { ev := &engine.AttrArgsProcessEvent{ Context: utils.StringPointer(utils.MetaSessionS), @@ -369,10 +390,10 @@ func testAttributeSProcessEventMissing(t *testing.T) { }, }, } - var rplyEv engine.AttrSProcessEventReply if err := attrSRPC.Call(utils.AttributeSv1ProcessEvent, - ev, &rplyEv); err == nil && err != utils.ErrMandatoryIeMissing { + ev, &rplyEv); err == nil || + err.Error() != utils.ErrMandatoryIeMissing.Error() { t.Error(err) } } @@ -652,7 +673,8 @@ func testAttributeSProcessEventWithHeader(t *testing.T) { func testAttributeSGetAttPrfIDs(t *testing.T) { expected := []string{"ATTR_2", "ATTR_1", "ATTR_3", "ATTR_Header", "AttributeWithNonSubstitute"} var result []string - if err := attrSRPC.Call("ApierV1.GetAttributeProfileIDs", "", &result); err == nil || err.Error() != utils.NewErrMandatoryIeMissing("Tenant").Error() { + if err := attrSRPC.Call("ApierV1.GetAttributeProfileIDs", "", &result); err == nil || + err.Error() != utils.NewErrMandatoryIeMissing("Tenant").Error() { t.Errorf("Expected error recived reply %+v with err=%v", result, err) } if err := attrSRPC.Call("ApierV1.GetAttributeProfileIDs", "cgrates.org", &result); err != nil { diff --git a/engine/eventcost.go b/engine/eventcost.go index 809395e8f..280432952 100644 --- a/engine/eventcost.go +++ b/engine/eventcost.go @@ -397,6 +397,107 @@ func (ec *EventCost) appendChargingIntervalFromEventCost(oEC *EventCost, cIlIdx } } +// SyncKeys will sync the keys present into ec with the ones in refEC +func (ec *EventCost) SyncKeys(refEC *EventCost) { + // sync RatingFilters + sncedRFilterIDs := make(map[string]string) + for key, rf := range ec.RatingFilters { + for refKey, refRf := range refEC.RatingFilters { + if rf.Equals(refRf) { + delete(ec.RatingFilters, key) + sncedRFilterIDs[key] = refKey + ec.RatingFilters[refKey] = rf + break + } + } + } + // sync Rates + sncedRateIDs := make(map[string]string) + for key, rt := range ec.Rates { + for refKey, refRt := range refEC.Rates { + if rt.Equals(refRt) { + delete(ec.Rates, key) + sncedRateIDs[key] = refKey + ec.Rates[refKey] = rt + break + } + } + } + // sync Timings + sncedTimingIDs := make(map[string]string) + for key, tm := range ec.Timings { + for refKey, refTm := range refEC.Timings { + if tm.Equals(refTm) { + delete(ec.Timings, key) + sncedTimingIDs[key] = refKey + ec.Timings[refKey] = tm + break + } + } + } + // sync Rating + sncedRatingIDs := make(map[string]string) + for key, ru := range ec.Rating { + if tmRefKey, has := sncedTimingIDs[ru.TimingID]; has { + ru.TimingID = tmRefKey + } + if rtRefID, has := sncedRateIDs[ru.RatesID]; has { + ru.RatesID = rtRefID + } + if rfRefID, has := sncedRFilterIDs[ru.RatingFiltersID]; has { + ru.RatingFiltersID = rfRefID + } + for refKey, refRU := range refEC.Rating { + if ru.Equals(refRU) { + delete(ec.Rating, key) + sncedRatingIDs[key] = refKey + ec.Rating[refKey] = ru + break + } + } + } + // sync Accounting + sncedAcntIDs := make(map[string]string) + for key, acnt := range ec.Accounting { + for refKey, refAcnt := range refEC.Accounting { + if acnt.Equals(refAcnt) { + delete(ec.Accounting, key) + sncedAcntIDs[key] = refKey + ec.Accounting[refKey] = acnt + break + } + } + } + // correct the ExtraCharge + for _, acnt := range ec.Accounting { + if acntRefID, has := sncedAcntIDs[acnt.ExtraChargeID]; has { + acnt.ExtraChargeID = acntRefID + } + } + // need another sync for the corrected ExtraChargeIDs + for key, acnt := range ec.Accounting { + for refKey, refAcnt := range refEC.Accounting { + if acnt.Equals(refAcnt) { + delete(ec.Accounting, key) + sncedAcntIDs[key] = refKey + ec.Accounting[refKey] = acnt + break + } + } + } + // sync Charges + for _, ci := range ec.Charges { + if refRatingID, has := sncedRatingIDs[ci.RatingID]; has { + ci.RatingID = refRatingID + } + for _, cIcrmt := range ci.Increments { + if refAcntID, has := sncedAcntIDs[cIcrmt.AccountingID]; has { + cIcrmt.AccountingID = refAcntID + } + } + } +} + // Merge will merge a list of EventCosts into this one func (ec *EventCost) Merge(ecs ...*EventCost) { for _, newEC := range ecs { diff --git a/engine/eventcost_test.go b/engine/eventcost_test.go index bbd00f152..c4a8c073f 100644 --- a/engine/eventcost_test.go +++ b/engine/eventcost_test.go @@ -2030,3 +2030,244 @@ func TestECAppendCIlFromEC(t *testing.T) { t.Errorf("expecting: %s, received: %s", utils.ToJSON(eEC), utils.ToJSON(ec)) } } + +func TestECSyncKeys(t *testing.T) { + ec := testEC.Clone() + + refEC := &EventCost{ + Rating: Rating{ + "21a5ab9": &RatingUnit{ + ConnectFee: 0.1, + RoundingMethod: "*up", + RoundingDecimals: 5, + TimingID: "2f324ab", + RatesID: "2c1a177", + RatingFiltersID: "23e77dc", + }, + }, + Accounting: Accounting{ + "2012888": &BalanceCharge{ + AccountID: "cgrates.org:dan", + BalanceUUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", + Units: 0.01, + }, + "288bfa6": &BalanceCharge{ + AccountID: "cgrates.org:dan", + BalanceUUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", + Units: 0.005, + }, + "24d6c02": &BalanceCharge{ + AccountID: "cgrates.org:dan", + BalanceUUID: "7a54a9e9-d610-4c82-bcb5-a315b9a65010", + RatingID: "3cd6425", + Units: 1, + ExtraChargeID: "288bfa6", + }, + }, + RatingFilters: RatingFilters{ + "23e77dc": RatingMatchedFilters{ + "DestinationID": "GERMANY", + "DestinationPrefix": "+49", + "RatingPlanID": "RPL_RETAIL1", + "Subject": "*out:cgrates.org:call:*any", + }, + }, + Timings: ChargedTimings{ + "2f324ab": &ChargedTiming{ + StartTime: "00:00:00", + }, + }, + Rates: ChargedRates{ + "2c1a177": RateGroups{ + &Rate{ + GroupIntervalStart: time.Duration(0), + Value: 0.01, + RateIncrement: time.Duration(1 * time.Minute), + RateUnit: time.Duration(1 * time.Second)}, + }, + }, + } + + eEC := &EventCost{ + CGRID: "164b0422fdc6a5117031b427439482c6a4f90e41", + RunID: utils.META_DEFAULT, + StartTime: time.Date(2017, 1, 9, 16, 18, 21, 0, time.UTC), + Charges: []*ChargingInterval{ + &ChargingInterval{ + RatingID: "21a5ab9", + Increments: []*ChargingIncrement{ + &ChargingIncrement{ + Usage: time.Duration(0), + Cost: 0.1, + AccountingID: "9bdad10", + CompressFactor: 1, + }, + &ChargingIncrement{ + Usage: time.Duration(1 * time.Second), + Cost: 0, + AccountingID: "3455b83", + CompressFactor: 10, + }, + &ChargingIncrement{ + Usage: time.Duration(10 * time.Second), + Cost: 0.01, + AccountingID: "2012888", + CompressFactor: 2, + }, + &ChargingIncrement{ + Usage: time.Duration(1 * time.Second), + Cost: 0.005, + AccountingID: "24d6c02", + CompressFactor: 30, + }, + }, + CompressFactor: 1, + }, + &ChargingInterval{ + RatingID: "21a5ab9", + Increments: []*ChargingIncrement{ + &ChargingIncrement{ + Usage: time.Duration(1 * time.Second), + Cost: 0.01, + AccountingID: "2012888", + CompressFactor: 60, + }, + }, + CompressFactor: 4, + }, + &ChargingInterval{ + RatingID: "21a5ab9", + Increments: []*ChargingIncrement{ + &ChargingIncrement{ + Usage: time.Duration(1 * time.Second), + Cost: 0, + AccountingID: "3455b83", + CompressFactor: 10, + }, + &ChargingIncrement{ + Usage: time.Duration(10 * time.Second), + Cost: 0.01, + AccountingID: "2012888", + CompressFactor: 2, + }, + &ChargingIncrement{ + Usage: time.Duration(1 * time.Second), + Cost: 0.005, + AccountingID: "24d6c02", + CompressFactor: 30, + }, + }, + CompressFactor: 5, + }, + }, + AccountSummary: &AccountSummary{ + Tenant: "cgrates.org", + ID: "dan", + BalanceSummaries: []*BalanceSummary{ + &BalanceSummary{ + Type: "*monetary", + Value: 50, + Disabled: false}, + &BalanceSummary{ + ID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", + Type: "*monetary", + Value: 25, + Disabled: false}, + &BalanceSummary{ + Type: "*voice", + Value: 200, + Disabled: false, + }, + }, + AllowNegative: false, + Disabled: false, + }, + Rating: Rating{ + "3cd6425": &RatingUnit{ + RoundingMethod: "*up", + RoundingDecimals: 5, + TimingID: "2f324ab", + RatesID: "4910ecf", + RatingFiltersID: "23e77dc", + }, + "21a5ab9": &RatingUnit{ + ConnectFee: 0.1, + RoundingMethod: "*up", + RoundingDecimals: 5, + TimingID: "2f324ab", + RatesID: "2c1a177", + RatingFiltersID: "23e77dc", + }, + }, + Accounting: Accounting{ + "2012888": &BalanceCharge{ + AccountID: "cgrates.org:dan", + BalanceUUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", + Units: 0.01, + }, + "288bfa6": &BalanceCharge{ + AccountID: "cgrates.org:dan", + BalanceUUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", + Units: 0.005, + }, + "9bdad10": &BalanceCharge{ + AccountID: "cgrates.org:dan", + BalanceUUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", + Units: 0.1, + }, + "24d6c02": &BalanceCharge{ + AccountID: "cgrates.org:dan", + BalanceUUID: "7a54a9e9-d610-4c82-bcb5-a315b9a65010", + RatingID: "3cd6425", + Units: 1, + ExtraChargeID: "288bfa6", + }, + "3455b83": &BalanceCharge{ + AccountID: "cgrates.org:dan", + BalanceUUID: "9d54a9e9-d610-4c82-bcb5-a315b9a65089", + Units: 1, + ExtraChargeID: "*none", + }, + }, + RatingFilters: RatingFilters{ + "23e77dc": RatingMatchedFilters{ + "DestinationID": "GERMANY", + "DestinationPrefix": "+49", + "RatingPlanID": "RPL_RETAIL1", + "Subject": "*out:cgrates.org:call:*any", + }, + }, + Rates: ChargedRates{ + "2c1a177": RateGroups{ + &Rate{ + GroupIntervalStart: time.Duration(0), + Value: 0.01, + RateIncrement: time.Duration(1 * time.Minute), + RateUnit: time.Duration(1 * time.Second)}, + }, + "4910ecf": RateGroups{ + &Rate{ + GroupIntervalStart: time.Duration(0), + Value: 0.005, + RateIncrement: time.Duration(1 * time.Second), + RateUnit: time.Duration(1 * time.Second)}, + &Rate{ + GroupIntervalStart: time.Duration(60 * time.Second), + Value: 0.005, + RateIncrement: time.Duration(1 * time.Second), + RateUnit: time.Duration(1 * time.Second)}, + }, + }, + Timings: ChargedTimings{ + "2f324ab": &ChargedTiming{ + StartTime: "00:00:00", + }, + }, + } + + ec.SyncKeys(refEC) + if !reflect.DeepEqual(eEC, ec) { + t.Errorf("expecting: %s \nreceived: %s", + utils.ToIJSON(eEC), utils.ToIJSON(ec)) + } +} diff --git a/sessions/sessions.go b/sessions/sessions.go index 5f538fdd8..6a07019fc 100644 --- a/sessions/sessions.go +++ b/sessions/sessions.go @@ -460,7 +460,7 @@ func (sS *SessionS) debitSession(s *Session, sRunIdx int, dur time.Duration, cd := sr.CD.Clone() s.Unlock() cc := new(engine.CallCost) - if err := sS.ralS.Call("Responder.MaxDebit", cd, cc); err != nil { + if err := sS.ralS.Call(utils.ResponderMaxDebit, cd, cc); err != nil { s.Lock() sr.ExtraDuration += dbtRsrv s.Unlock() @@ -489,6 +489,7 @@ func (sS *SessionS) debitSession(s *Session, sRunIdx int, dur time.Duration, sr.EventCost = ec } } else { + ec.SyncKeys(sr.EventCost) sr.EventCost.Merge(ec) } maxDur = sr.LastUsage diff --git a/utils/consts.go b/utils/consts.go index 05109145d..ed5f98431 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -785,6 +785,7 @@ const ( ResponderRefundIncrements = "Responder.RefundIncrements" ResponderGetMaxSessionTime = "Responder.GetMaxSessionTime" ResponderStatus = "Responder.Status" + ResponderMaxDebit = "Responder.MaxDebit" ) // DispatcherS APIs