From 56f8549ea42f4d1a17106e81ccc1bd0c04305001 Mon Sep 17 00:00:00 2001 From: ionutboangiu Date: Wed, 10 Apr 2024 18:23:08 +0300 Subject: [PATCH] Ensure EC FieldAsInterface method works correctly - trim fldPath Increments before retrieving the field - Rating map can be accessed from Accounting - ExtraCharges can be accessed from Accounting - RatingUnit fields that did not represent the id of another EC struct are now retrievable - add unit tests --- engine/actions_test.go | 4 +- engine/eventcost.go | 16 ++- engine/eventcost_test.go | 268 ++++++++++++++++++++++++++++++++++++++- utils/consts.go | 1 + 4 files changed, 276 insertions(+), 13 deletions(-) diff --git a/engine/actions_test.go b/engine/actions_test.go index c150267f5..71b881f4f 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -85,9 +85,9 @@ func TestActionPlanOnlyHour(t *testing.T) { } func TestActionPlanHourYear(t *testing.T) { - at := &ActionTiming{Timing: &RateInterval{Timing: &RITiming{Years: utils.Years{2024}, StartTime: "10:01:00"}}} + at := &ActionTiming{Timing: &RateInterval{Timing: &RITiming{Years: utils.Years{2029}, StartTime: "10:01:00"}}} st := at.GetNextStartTime(referenceDate) - expected := time.Date(2024, 1, 1, 10, 1, 0, 0, time.Local) + expected := time.Date(2029, 1, 1, 10, 1, 0, 0, time.Local) if !st.Equal(expected) { t.Errorf("Expected %v was %v", expected, st) } diff --git a/engine/eventcost.go b/engine/eventcost.go index fc855cc69..2c4f6220a 100644 --- a/engine/eventcost.go +++ b/engine/eventcost.go @@ -1032,7 +1032,7 @@ func (ec *EventCost) getChargesForPath(fldPath []string, chr *ChargingInterval) if fldPath[1] == utils.Accounting { return ec.getAcountingForPath(fldPath[2:], ec.Accounting[incr.AccountingID]) } - return incr.FieldAsInterface(fldPath) + return incr.FieldAsInterface(fldPath[1:]) } func (ec *EventCost) getRatingForPath(fldPath []string, rating *RatingUnit) (val any, err error) { @@ -1046,9 +1046,13 @@ func (ec *EventCost) getRatingForPath(fldPath []string, rating *RatingUnit) (val switch fldPath[0] { default: opath, indx := utils.GetPathIndex(fldPath[0]) + + // Break the switch and leave the validation to + // the RatingUnit's FieldAsInterface method. if opath != utils.Rates { - return nil, fmt.Errorf("unsupported field prefix: <%s>", opath) + break } + rts, has := ec.Rates[rating.RatesID] if !has || rts == nil { return nil, utils.ErrNotFound @@ -1102,7 +1106,8 @@ func (ec *EventCost) getAcountingForPath(fldPath []string, bc *BalanceCharge) (v return bc, nil } - if fldPath[0] == utils.Balance { + switch fldPath[0] { + case utils.Balance: bl := ec.AccountSummary.BalanceSummaries.BalanceSummaryWithUUD(bc.BalanceUUID) if bl == nil { return nil, utils.ErrNotFound @@ -1111,7 +1116,10 @@ func (ec *EventCost) getAcountingForPath(fldPath []string, bc *BalanceCharge) (v return bl, nil } return bl.FieldAsInterface(fldPath[1:]) - + case utils.Rating: + return ec.getRatingForPath(fldPath[1:], ec.Rating[bc.RatingID]) + case utils.ExtraCharge: + return ec.getAcountingForPath(fldPath[1:], ec.Accounting[bc.ExtraChargeID]) } return bc.FieldAsInterface(fldPath) } diff --git a/engine/eventcost_test.go b/engine/eventcost_test.go index a6498b8ed..5d739c813 100644 --- a/engine/eventcost_test.go +++ b/engine/eventcost_test.go @@ -106,16 +106,19 @@ var testEC = &EventCost{ BalanceSummaries: []*BalanceSummary{ { UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", + ID: "BALANCE_1", Type: utils.MONETARY, Value: 50, }, { UUID: "7a54a9e9-d610-4c82-bcb5-a315b9a65010", + ID: "BALANCE_2", Type: utils.MONETARY, Value: 25, }, { UUID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", + ID: "BALANCE_3", Type: utils.VOICE, Value: 200, }, @@ -130,6 +133,7 @@ var testEC = &EventCost{ TimingID: "7f324ab", RatesID: "4910ecf", RatingFiltersID: "43e77dc", + MaxCostStrategy: utils.MAX_COST_DISCONNECT, }, "c1a5ab9": &RatingUnit{ ConnectFee: 0.1, @@ -138,6 +142,7 @@ var testEC = &EventCost{ TimingID: "7f324ab", RatesID: "ec1a177", RatingFiltersID: "43e77dc", + MaxCostStrategy: utils.MAX_COST_DISCONNECT, }, }, Accounting: Accounting{ @@ -2380,6 +2385,7 @@ func TestECSyncKeys(t *testing.T) { TimingID: "2f324ab", RatesID: "2c1a177", RatingFiltersID: "23e77dc", + MaxCostStrategy: utils.MAX_COST_DISCONNECT, }, }, Accounting: Accounting{ @@ -2504,17 +2510,22 @@ func TestECSyncKeys(t *testing.T) { BalanceSummaries: []*BalanceSummary{ { UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", + ID: "BALANCE_1", Type: utils.MONETARY, Value: 50, - Disabled: false}, + Disabled: false, + }, { UUID: "7a54a9e9-d610-4c82-bcb5-a315b9a65010", + ID: "BALANCE_2", Type: utils.MONETARY, Value: 25, - Disabled: false}, + Disabled: false, + }, { UUID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", - Type: "*voice", + ID: "BALANCE_3", + Type: utils.VOICE, Value: 200, Disabled: false, }, @@ -2529,6 +2540,7 @@ func TestECSyncKeys(t *testing.T) { TimingID: "2f324ab", RatesID: "4910ecf", RatingFiltersID: "23e77dc", + MaxCostStrategy: utils.MAX_COST_DISCONNECT, }, "21a5ab9": &RatingUnit{ ConnectFee: 0.1, @@ -2537,6 +2549,7 @@ func TestECSyncKeys(t *testing.T) { TimingID: "2f324ab", RatesID: "2c1a177", RatingFiltersID: "23e77dc", + MaxCostStrategy: utils.MAX_COST_DISCONNECT, }, }, Accounting: Accounting{ @@ -3051,6 +3064,247 @@ func TestECAsDataProvider2(t *testing.T) { } } +func TestECAsDataProviderTT(t *testing.T) { + ecDP := testEC + testDPs := []struct { + name string + fields []string + exp any + }{ + { + name: "StartTime", + fields: []string{"Charges[0]", "Rating", "Timing", "StartTime"}, + exp: "00:00:00", + }, + { + name: "RateIncrement", + fields: []string{"Charges[0]", "Rating", "Rates[0]", "RateIncrement"}, + exp: "1m0s", + }, + { + name: "DestinationID", + fields: []string{"Charges[0]", "Rating", "RatingFilter", "DestinationID"}, + exp: "GERMANY", + }, + { + name: "Units", + fields: []string{"Charges[0]", "Increments[0]", "Accounting", "Units"}, + exp: "0.1", + }, + { + name: "Subject", + fields: []string{"Charges[0]", "Rating", "RatingFilter", "Subject"}, + exp: "*out:cgrates.org:call:*any", + }, + { + name: "Value", + fields: []string{"Charges[2]", "Increments[2]", "Accounting", "Balance", "Value"}, + exp: "200", + }, + { + name: "Type", + fields: []string{"Charges[1]", "Increments[0]", "Accounting", "Balance", "Type"}, + exp: "*monetary", + }, + { + name: "UUID", + fields: []string{"Charges[0]", "Increments[3]", "Accounting", "Balance", "UUID"}, + exp: "4b8b53d7-c1a1-4159-b845-4623a00a0165", + }, + { + name: "ID", + fields: []string{"Charges[0]", "Increments[3]", "Accounting", "Balance", "ID"}, + exp: "BALANCE_3", + }, + { + name: "Disabled", + fields: []string{"Charges[0]", "Increments[3]", "Accounting", "Balance", "Disabled"}, + exp: "false", + }, + { + name: "IncrementCost", + fields: []string{"Charges[0]", "Increments[2]", "Cost"}, + exp: "0.01", + }, + { + name: "DestinationPrefix", + fields: []string{"Charges[2]", "Rating", "RatingFilter", "DestinationPrefix"}, + exp: "+49", + }, + { + name: "RatingPlanID", + fields: []string{"Charges[2]", "Rating", "RatingFilter", "RatingPlanID"}, + exp: "RPL_RETAIL1", + }, + { + name: "AccountID", + fields: []string{"Charges[0]", "Increments[3]", "Accounting", "AccountID"}, + exp: "cgrates.org:dan", + }, + { + name: "RateValue", + fields: []string{"Charges[1]", "Rating", "Rates[0]", "Value"}, + exp: "0.01", + }, + { + name: "RateUnit", + fields: []string{"Charges[2]", "Rating", "Rates[0]", "RateUnit"}, + exp: "1s", + }, + { + name: "ExtraCharge", + fields: []string{"Charges[0]", "Increments[1]", "Accounting", "ExtraChargeID"}, + exp: "*none", + }, + { + name: "AccountSummary", + fields: []string{"AccountSummary", "BalanceSummaries[1]", "Value"}, + exp: "25", + }, + { + name: "RoundingMethod", + fields: []string{"AccountSummary", "AllowNegative"}, + exp: "false", + }, + { + name: "CompressFactor", + fields: []string{"Charges[0]", "CompressFactor"}, + exp: "1", + }, + { + name: "ConnectFee through Accounting", + fields: []string{"Charges[0]", "Increments[3]", "Accounting", "Rating", "ConnectFee"}, + exp: "0", + }, + { + name: "RoundingMethod through Accounting", + fields: []string{"Charges[0]", "Increments[3]", "Accounting", "Rating", "RoundingMethod"}, + exp: utils.ROUNDING_UP, + }, + { + name: "RoundingDecimals through Accounting", + fields: []string{"Charges[0]", "Increments[3]", "Accounting", "Rating", "RoundingDecimals"}, + exp: "5", + }, + { + name: "MaxCost through Accounting", + fields: []string{"Charges[0]", "Increments[3]", "Accounting", "Rating", "MaxCost"}, + exp: "0", + }, + { + name: "MaxCostStrategy through Accounting", + fields: []string{"Charges[0]", "Increments[3]", "Accounting", "Rating", "MaxCostStrategy"}, + exp: utils.MAX_COST_DISCONNECT, + }, + { + name: "TimingID through Accounting", + fields: []string{"Charges[0]", "Increments[3]", "Accounting", "Rating", "TimingID"}, + exp: "7f324ab", + }, + { + name: "RatesID through Accounting", + fields: []string{"Charges[0]", "Increments[3]", "Accounting", "Rating", "RatesID"}, + exp: "4910ecf", + }, + { + name: "RatingFiltersID through Accounting", + fields: []string{"Charges[0]", "Increments[3]", "Accounting", "Rating", "RatingFiltersID"}, + exp: "43e77dc", + }, + { + name: "ConnectFee", + fields: []string{"Charges[0]", "Rating", "ConnectFee"}, + exp: "0.1", + }, + { + name: "RoundingMethod", + fields: []string{"Charges[0]", "Rating", "RoundingMethod"}, + exp: utils.ROUNDING_UP, + }, + { + name: "RoundingDecimals", + fields: []string{"Charges[0]", "Rating", "RoundingDecimals"}, + exp: "5", + }, + { + name: "MaxCost", + fields: []string{"Charges[0]", "Rating", "MaxCost"}, + exp: "0", + }, + { + name: "MaxCostStrategy", + fields: []string{"Charges[0]", "Rating", "MaxCostStrategy"}, + exp: utils.MAX_COST_DISCONNECT, + }, + { + name: "TimingID", + fields: []string{"Charges[0]", "Rating", "TimingID"}, + exp: "7f324ab", + }, + { + name: "RatesID", + fields: []string{"Charges[0]", "Rating", "RatesID"}, + exp: "ec1a177", + }, + { + name: "RatingFiltersID", + fields: []string{"Charges[0]", "Rating", "RatingFiltersID"}, + exp: "43e77dc", + }, + { + name: "DestinationID through Accounting", + fields: []string{"Charges[0]", "Increments[3]", "Accounting", "Rating", "RatingFilter", "DestinationID"}, + exp: "GERMANY", + }, + { + name: "Value through ExtraCharge", + fields: []string{"Charges[0]", "Increments[3]", "Accounting", "ExtraCharge", "Balance", "Value"}, + exp: "50", + }, + { + name: "Type through ExtraCharge", + fields: []string{"Charges[0]", "Increments[3]", "Accounting", "ExtraCharge", "Balance", "Type"}, + exp: utils.MONETARY, + }, + { + name: "UUID through ExtraCharge", + fields: []string{"Charges[0]", "Increments[3]", "Accounting", "ExtraCharge", "Balance", "UUID"}, + exp: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", + }, + { + name: "ID through ExtraCharge", + fields: []string{"Charges[0]", "Increments[3]", "Accounting", "ExtraCharge", "Balance", "ID"}, + exp: "BALANCE_1", + }, + { + name: "Disabled through ExtraCharge", + fields: []string{"Charges[0]", "Increments[3]", "Accounting", "ExtraCharge", "Balance", "Disabled"}, + exp: "false", + }, + { + name: "AccountID through ExtraCharge", + fields: []string{"Charges[0]", "Increments[3]", "Accounting", "ExtraCharge", "AccountID"}, + exp: "cgrates.org:dan", + }, + { + name: "Units through ExtraCharge", + fields: []string{"Charges[0]", "Increments[3]", "Accounting", "ExtraCharge", "Units"}, + exp: "0.005", + }, + } + + for _, testDp := range testDPs { + + t.Run(testDp.name, func(t *testing.T) { + if val, err := ecDP.FieldAsString(testDp.fields); err != nil { + t.Error(err) + } else if testDp.exp != val { + t.Errorf("Expecting: <%s> \nreceived: <%s>", testDp.exp, val) + } + }) + } +} + func TestECFieldAsInterfaceNilEventCost(t *testing.T) { dft, _ := config.NewDefaultCGRConfig() cdr, err := NewMapEvent(map[string]any{}).AsCDR(dft, "cgrates.org", "UTC") @@ -3342,7 +3596,7 @@ func TestEventCostgetChargesForPath(t *testing.T) { name: "extrafield in increments", fl: []string{"Increments[0]", "CompressFactor"}, chr: &chr, - exp: nil, + exp: 1, err: "NOT_FOUND", }, } @@ -3353,7 +3607,7 @@ func TestEventCostgetChargesForPath(t *testing.T) { if err != nil { if err.Error() != tt.err { - t.Error(err) + t.Errorf("error: want %v, got %v", tt.err, err) } } @@ -3417,7 +3671,7 @@ func TestEventCostgetRatingForPath(t *testing.T) { fldPath: []string{"test[0]"}, rating: &r, exp: nil, - err: "unsupported field prefix: ", + err: "unsupported field prefix: ", }, { name: "rating not found in event cost", @@ -3514,7 +3768,7 @@ func TestEventCostgetRatingForPath(t *testing.T) { if err != nil { if err.Error() != tt.err { - t.Error(err) + t.Errorf("error: want %v, got %v", tt.err, err) } } diff --git a/utils/consts.go b/utils/consts.go index 5b2f06771..b78736f2e 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -504,6 +504,7 @@ const ( RatingFilter = "RatingFilter" Accounting = "Accounting" Rating = "Rating" + ExtraCharge = "ExtraCharge" Charges = "Charges" CompressFactor = "CompressFactor" Increments = "Increments"