/* 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 */ package engine import ( "encoding/json" "errors" "fmt" "reflect" "testing" "time" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" ) // testEC is used as sample through various tests var testEC = &EventCost{ CGRID: "164b0422fdc6a5117031b427439482c6a4f90e41", RunID: utils.MetaDefault, StartTime: time.Date(2017, 1, 9, 16, 18, 21, 0, time.UTC), Charges: []*ChargingInterval{ { RatingID: "c1a5ab9", Increments: []*ChargingIncrement{ { Usage: 0, Cost: 0.1, AccountingID: "9bdad10", CompressFactor: 1, }, { Usage: time.Second, Cost: 0, AccountingID: "3455b83", CompressFactor: 10, }, { Usage: 10 * time.Second, Cost: 0.01, AccountingID: "a012888", CompressFactor: 2, }, { Usage: time.Second, Cost: 0.005, AccountingID: "44d6c02", CompressFactor: 30, }, }, CompressFactor: 1, }, { RatingID: "c1a5ab9", Increments: []*ChargingIncrement{ { Usage: time.Second, Cost: 0.01, AccountingID: "a012888", CompressFactor: 60, }, }, CompressFactor: 4, }, { RatingID: "c1a5ab9", Increments: []*ChargingIncrement{ { Usage: time.Second, Cost: 0, AccountingID: "3455b83", CompressFactor: 10, }, { Usage: 10 * time.Second, Cost: 0.01, AccountingID: "a012888", CompressFactor: 2, }, { Usage: time.Second, Cost: 0.005, AccountingID: "44d6c02", CompressFactor: 30, }, }, CompressFactor: 5, }, }, AccountSummary: &AccountSummary{ Tenant: "cgrates.org", ID: "dan", BalanceSummaries: []*BalanceSummary{ { UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", ID: "BALANCE_1", Type: utils.MetaMonetary, Value: 50, Initial: 60, Weight: 20, Disabled: false, Factors: ValueFactors{ "factor2": 2, "factor3": 3, }, }, { UUID: "7a54a9e9-d610-4c82-bcb5-a315b9a65010", ID: "BALANCE_2", Type: utils.MetaMonetary, Value: 25, Weight: 0, Initial: 60, Disabled: false, Factors: ValueFactors{ "factor2": 2, }, }, { UUID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", ID: "BALANCE_3", Type: utils.MetaVoice, Value: 200, Weight: 10, Initial: 250, Disabled: true, Factors: ValueFactors{ "factor4": 4, "factor5": 5, }, }, }, AllowNegative: false, Disabled: false, }, Rating: Rating{ "3cd6425": &RatingUnit{ ConnectFee: 0.4, RoundingMethod: "*up", RoundingDecimals: 5, MaxCostStrategy: utils.MetaMaxCostDisconnect, TimingID: "7f324ab", RatesID: "4910ecf", RatingFiltersID: "43e77dc", }, "c1a5ab9": &RatingUnit{ ConnectFee: 0.1, RoundingMethod: "*up", RoundingDecimals: 5, MaxCostStrategy: utils.MetaMaxCostDisconnect, TimingID: "7f324ab", RatesID: "ec1a177", RatingFiltersID: "43e77dc", }, }, Accounting: Accounting{ "a012888": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", Units: 0.01, }, "188bfa6": &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, }, "44d6c02": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", RatingID: "3cd6425", Units: 1, ExtraChargeID: "188bfa6", }, "3455b83": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", Units: 1, ExtraChargeID: "*none", }, }, RatingFilters: RatingFilters{ "43e77dc": RatingMatchedFilters{ "DestinationID": "GERMANY", "DestinationPrefix": "+49", "RatingPlanID": "RPL_RETAIL1", "Subject": "*out:cgrates.org:call:*any", }, }, Rates: ChargedRates{ "ec1a177": RateGroups{ &RGRate{ GroupIntervalStart: 0, Value: 0.01, RateIncrement: time.Minute, RateUnit: time.Second}, }, "4910ecf": RateGroups{ &RGRate{ GroupIntervalStart: 0, Value: 0.005, RateIncrement: time.Second, RateUnit: time.Second}, &RGRate{ GroupIntervalStart: 60 * time.Second, Value: 0.005, RateIncrement: time.Second, RateUnit: time.Second}, }, }, Timings: ChargedTimings{ "7f324ab": &ChargedTiming{ StartTime: "00:00:00", }, }, cache: utils.NewSecureMapStorage(), } func TestECClone(t *testing.T) { ec := testEC.Clone() if !reflect.DeepEqual(testEC, ec) { t.Errorf("Expecting: %s, received: %s", utils.ToJSON(testEC), utils.ToJSON(ec)) } // making sure we don't influence the original values ec.Usage = utils.DurationPointer(time.Second) if testEC.Usage != nil { t.Error("Usage is not nil") } ec.Cost = utils.Float64Pointer(1.0) if testEC.Cost != nil { t.Error("Cost is not nil") } ec.Charges[0].Increments[0].Cost = 1.0 if testEC.Charges[0].Increments[0].Cost == 1.0 { t.Error("Cost is 1.0") } ec.AccountSummary.Disabled = true if testEC.AccountSummary.Disabled { t.Error("Account is disabled") } ec.AccountSummary.BalanceSummaries[0].Value = 5.0 if testEC.AccountSummary.BalanceSummaries[0].Value == 5.0 { t.Error("Wrong balance summary") } ec.Rates["ec1a177"][0].Value = 5.0 if testEC.Rates["ec1a177"][0].Value == 5.0 { t.Error("Wrong Value") } delete(ec.Rates, "ec1a177") if _, has := testEC.Rates["ec1a177"]; !has { t.Error("Key removed from testEC") } ec.Timings["7f324ab"].StartTime = "10:00:00" if testEC.Timings["7f324ab"].StartTime == "10:00:00" { t.Error("Wrong StartTime") } delete(ec.Timings, "7f324ab") if _, has := testEC.Timings["7f324ab"]; !has { t.Error("Key removed from testEC") } ec.RatingFilters["43e77dc"]["DestinationID"] = "GERMANY_MOBILE" if testEC.RatingFilters["43e77dc"]["DestinationID"] == "GERMANY_MOBILE" { t.Error("Wrong DestinationID") } delete(ec.RatingFilters, "43e77dc") if _, has := testEC.RatingFilters["43e77dc"]; !has { t.Error("Key removed from testEC") } ec.Accounting["a012888"].Units = 5.0 if testEC.Accounting["a012888"].Units == 5.0 { t.Error("Wrong Units") } delete(ec.Accounting, "a012888") if _, has := testEC.Accounting["a012888"]; !has { t.Error("Key removed from testEC") } } func TestECComputeAndReset(t *testing.T) { ec := testEC.Clone() eEc := testEC.Clone() eEc.Usage = utils.DurationPointer(10 * time.Minute) eEc.Cost = utils.Float64Pointer(3.52) eEc.Charges[0].ecUsageIdx = utils.DurationPointer(0) eEc.Charges[0].usage = utils.DurationPointer(time.Minute) eEc.Charges[0].cost = utils.Float64Pointer(0.27) eEc.Charges[1].ecUsageIdx = utils.DurationPointer(time.Minute) eEc.Charges[1].usage = utils.DurationPointer(time.Minute) eEc.Charges[1].cost = utils.Float64Pointer(0.6) eEc.Charges[2].ecUsageIdx = utils.DurationPointer(5 * time.Minute) eEc.Charges[2].usage = utils.DurationPointer(time.Minute) eEc.Charges[2].cost = utils.Float64Pointer(0.17) ec.Compute() if !reflect.DeepEqual(eEc, ec) { t.Errorf("Expecting: %s\n, received: %s", utils.ToJSON(eEc), utils.ToJSON(ec)) } ec.ResetCounters() if !reflect.DeepEqual(testEC, ec) { t.Errorf("Expecting: %+v, received: %+v", testEC, ec) } } func TestNewEventCostFromCallCost(t *testing.T) { acntSummary := &AccountSummary{ Tenant: "cgrates.org", ID: "dan", BalanceSummaries: []*BalanceSummary{ { Type: "*monetary", Value: 50, Disabled: false}, { ID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", Type: "*monetary", Value: 25, Disabled: false}, { Type: "*voice", Value: 200, Disabled: false, }, }, AllowNegative: false, Disabled: false, } cc := &CallCost{ Category: "call", Tenant: "cgrates.org", Subject: "dan", Account: "dan", Destination: "+4986517174963", ToR: utils.MetaVoice, Cost: 0.75, RatedUsage: 120.0, Timespans: TimeSpans{ &TimeSpan{ TimeStart: time.Date(2017, 1, 9, 16, 18, 21, 0, time.UTC), TimeEnd: time.Date(2017, 1, 9, 16, 19, 21, 0, time.UTC), Cost: 0.15, RateInterval: &RateInterval{ // standard rating Timing: &RITiming{ StartTime: "00:00:00", }, Rating: &RIRate{ ConnectFee: 0.1, RoundingMethod: "*up", RoundingDecimals: 5, Rates: RateGroups{ &RGRate{ GroupIntervalStart: 0, Value: 0.01, RateUnit: time.Second, RateIncrement: time.Minute, }, }, }, }, DurationIndex: time.Minute, MatchedSubject: "*out:cgrates.org:call:*any", MatchedPrefix: "+49", MatchedDestId: "GERMANY", RatingPlanId: "RPL_RETAIL1", CompressFactor: 1, RoundIncrement: &Increment{ Cost: -0.1, BalanceInfo: &DebitInfo{ Monetary: &MonetaryInfo{UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", ID: utils.MetaDefault, Value: 9.9}, AccountID: "cgrates.org:dan", }, CompressFactor: 1, }, Increments: Increments{ &Increment{ // ConnectFee Cost: 0.1, BalanceInfo: &DebitInfo{ Monetary: &MonetaryInfo{UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", ID: utils.MetaDefault, Value: 9.9}, AccountID: "cgrates.org:dan", }, CompressFactor: 1, }, &Increment{ // First 30 seconds free Duration: time.Second, Cost: 0, BalanceInfo: &DebitInfo{ Unit: &UnitInfo{ UUID: "9d54a9e9-d610-4c82-bcb5-a315b9a65089", ID: "free_mins", Value: 0, Consumed: 1.0, ToR: utils.MetaVoice, }, AccountID: "cgrates.org:dan", }, CompressFactor: 30, }, &Increment{ // Minutes with special price Duration: time.Second, Cost: 0.005, BalanceInfo: &DebitInfo{ Unit: &UnitInfo{ // Minutes with special price UUID: "7a54a9e9-d610-4c82-bcb5-a315b9a65010", ID: "discounted_mins", Value: 0, Consumed: 1.0, ToR: utils.MetaVoice, RateInterval: &RateInterval{ Timing: &RITiming{ StartTime: "00:00:00", }, Rating: &RIRate{ ConnectFee: 0, RoundingMethod: "*up", RoundingDecimals: 5, Rates: RateGroups{ &RGRate{ GroupIntervalStart: 0, Value: 0.005, RateUnit: time.Second, RateIncrement: time.Second, }, &RGRate{ GroupIntervalStart: 60 * time.Second, Value: 0.005, RateUnit: time.Second, RateIncrement: time.Second, }, }, }, }, }, Monetary: &MonetaryInfo{ UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", ID: utils.MetaDefault, Value: 9.75}, AccountID: "cgrates.org:dan", }, CompressFactor: 30, }, }, }, &TimeSpan{ TimeStart: time.Date(2017, 1, 9, 16, 19, 21, 0, time.UTC), TimeEnd: time.Date(2017, 1, 9, 16, 20, 21, 0, time.UTC), Cost: 0.6, RateInterval: &RateInterval{ // standard rating Timing: &RITiming{ StartTime: "00:00:00", }, Rating: &RIRate{ ConnectFee: 0.1, RoundingMethod: "*up", RoundingDecimals: 5, Rates: RateGroups{ &RGRate{ GroupIntervalStart: 0, Value: 0.01, RateUnit: time.Second, RateIncrement: time.Minute, }, }, }, }, DurationIndex: time.Minute, MatchedSubject: "*out:cgrates.org:call:*any", MatchedPrefix: "+49", MatchedDestId: "GERMANY", RatingPlanId: "RPL_RETAIL1", CompressFactor: 1, Increments: Increments{ &Increment{ Cost: 0.01, Duration: time.Second, BalanceInfo: &DebitInfo{ Monetary: &MonetaryInfo{UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", ID: utils.MetaDefault, Value: 9.15}, AccountID: "cgrates.org:dan", }, CompressFactor: 60, }, }, }, }, AccountSummary: acntSummary, } eEC := &EventCost{ CGRID: "164b0422fdc6a5117031b427439482c6a4f90e41", RunID: utils.MetaDefault, StartTime: time.Date(2017, 1, 9, 16, 18, 21, 0, time.UTC), Cost: utils.Float64Pointer(0.85), Usage: utils.DurationPointer(2 * time.Minute), Charges: []*ChargingInterval{ { RatingID: "f2518464-68b8-42f4-acec-aef23d714314", Increments: []*ChargingIncrement{ { Usage: 0, Cost: 0.1, AccountingID: "44e97dec-8a7e-43d0-8b0a-736d46b5613e", CompressFactor: 1, }, { Usage: time.Second, Cost: 0, AccountingID: "a555cde8-4bd0-408a-afbc-c3ba64888927", CompressFactor: 30, }, { Usage: time.Second, Cost: 0.005, AccountingID: "906bfd0f-035c-40a3-93a8-46f71627983e", CompressFactor: 30, }, { Cost: -0.1, AccountingID: "44e97dec-8a7e-43d0-8b0a-e34a152", CompressFactor: 1, }, }, CompressFactor: 1, usage: utils.DurationPointer(60 * time.Second), cost: utils.Float64Pointer(0.15), ecUsageIdx: utils.DurationPointer(0), }, { RatingID: "f2518464-68b8-42f4-acec-aef23d714314", Increments: []*ChargingIncrement{ { Usage: time.Second, Cost: 0.01, AccountingID: "c890a899-df43-497a-9979-38492713f57b", CompressFactor: 60, }, }, CompressFactor: 1, usage: utils.DurationPointer(60 * time.Second), cost: utils.Float64Pointer(0.6), ecUsageIdx: utils.DurationPointer(60 * time.Second), }, }, Rating: Rating{ "4607d907-02c3-4f2b-bc08-95a0dcc7222c": &RatingUnit{ RoundingMethod: "*up", RoundingDecimals: 5, TimingID: "27f1e5f8-05bb-4f1c-a596-bf1010ad296c", RatesID: "e5eb0f1c-3612-4e8c-b749-7f8f41dd90d4", RatingFiltersID: "7e73a00d-be53-4083-a1ee-8ee0b546c62a", }, "f2518464-68b8-42f4-acec-aef23d714314": &RatingUnit{ ConnectFee: 0.1, RoundingMethod: "*up", RoundingDecimals: 5, TimingID: "27f1e5f8-05bb-4f1c-a596-bf1010ad296c", RatesID: "6504fb84-6b27-47a8-a1c6-c0d843959f89", RatingFiltersID: "7e73a00d-be53-4083-a1ee-8ee0b546c62a", }, }, Accounting: Accounting{ "c890a899-df43-497a-9979-38492713f57b": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", Units: 0.01, }, "a894f8f1-206a-4457-99ce-df21a0c7fedc": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", Units: 0.005, }, "44e97dec-8a7e-43d0-8b0a-736d46b5613e": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", Units: 0.1, }, "906bfd0f-035c-40a3-93a8-46f71627983e": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "7a54a9e9-d610-4c82-bcb5-a315b9a65010", RatingID: "4607d907-02c3-4f2b-bc08-95a0dcc7222c", Units: 1, ExtraChargeID: "a894f8f1-206a-4457-99ce-df21a0c7fedc", }, "a555cde8-4bd0-408a-afbc-c3ba64888927": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "9d54a9e9-d610-4c82-bcb5-a315b9a65089", Units: 1, ExtraChargeID: "*none", }, "44e97dec-8a7e-43d0-8b0a-e34a152": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", RatingID: "*rounding", Units: -0.1, ExtraChargeID: "", }, }, RatingFilters: RatingFilters{ "7e73a00d-be53-4083-a1ee-8ee0b546c62a": RatingMatchedFilters{ "DestinationID": "GERMANY", "DestinationPrefix": "+49", "RatingPlanID": "RPL_RETAIL1", "Subject": "*out:cgrates.org:call:*any", }, }, Rates: ChargedRates{ "6504fb84-6b27-47a8-a1c6-c0d843959f89": RateGroups{ &RGRate{ GroupIntervalStart: 0, Value: 0.01, RateIncrement: time.Minute, RateUnit: time.Second}, }, "e5eb0f1c-3612-4e8c-b749-7f8f41dd90d4": RateGroups{ &RGRate{ GroupIntervalStart: 0, Value: 0.005, RateIncrement: time.Second, RateUnit: time.Second}, &RGRate{ GroupIntervalStart: 60 * time.Second, Value: 0.005, RateIncrement: time.Second, RateUnit: time.Second}, }, }, Timings: ChargedTimings{ "27f1e5f8-05bb-4f1c-a596-bf1010ad296c": &ChargedTiming{ StartTime: "00:00:00", }, }, AccountSummary: acntSummary, } ec := NewEventCostFromCallCost(cc, "164b0422fdc6a5117031b427439482c6a4f90e41", utils.MetaDefault) if cost := ec.GetCost(); cost != cc.Cost { t.Errorf("Expecting: %f, received: %f", cc.Cost, cost) } eUsage := time.Duration(int64(cc.RatedUsage * 1000000000)) if usage := ec.GetUsage(); usage != eUsage { t.Errorf("Expecting: %v, received: %v", eUsage, usage) } if len(ec.Charges) != len(eEC.Charges) { t.Errorf("Expecting: %+v, received: %+v", eEC, ec) } ec.ComputeEventCostUsageIndexes() for i := range ec.Charges { // Make sure main rating is correct if cc.Timespans[i].RateInterval.Rating != nil && !reflect.DeepEqual(cc.Timespans[i].RateInterval.Rating.Rates, ec.Rates[ec.Rating[ec.Charges[i].RatingID].RatesID]) { t.Errorf("Index: %d, expecting: %+v, received: %+v", i, utils.ToJSON(cc.Timespans[i].RateInterval.Rating.Rates), ec.Rates[ec.Rating[ec.Charges[i].RatingID].RatesID]) } // Make sure it matches also the expected rates if !reflect.DeepEqual(eEC.Rates[eEC.Rating[eEC.Charges[i].RatingID].RatesID], ec.Rates[ec.Rating[ec.Charges[i].RatingID].RatesID]) { t.Errorf("Index: %d, expecting: %s, received: %s", i, utils.ToJSON(eEC.Rates[eEC.Rating[eEC.Charges[i].RatingID].RatesID]), utils.ToJSON(ec.Rates[ec.Rating[ec.Charges[i].RatingID].RatesID])) } if len(eEC.Charges[i].Increments) != len(ec.Charges[i].Increments) { t.Errorf("Index %d, expecting: %+v, received: %+v", i, eEC.Charges[i].Increments, ec.Charges[i].Increments) } if !reflect.DeepEqual(eEC.Charges[i].Usage(), ec.Charges[i].Usage()) { t.Errorf("Expecting: %v, received: %v", eEC.Charges[i].Usage(), ec.Charges[i].Usage()) } if !reflect.DeepEqual(eEC.Charges[i].Cost(), ec.Charges[i].Cost()) { t.Errorf("Expecting: %f, received: %f", eEC.Charges[i].Cost(), ec.Charges[i].Cost()) } if !reflect.DeepEqual(eEC.Charges[i].ecUsageIdx, ec.Charges[i].ecUsageIdx) { t.Errorf("Expecting: %v, received: %v", eEC.Charges[i].ecUsageIdx, ec.Charges[i].ecUsageIdx) } cIlStartTime := ec.Charges[i].StartTime(ec.StartTime) if !cc.Timespans[i].TimeStart.Equal(cIlStartTime) { t.Errorf("Expecting: %v, received: %v", cc.Timespans[i].TimeStart, cIlStartTime) } if !cc.Timespans[i].TimeEnd.Equal(ec.Charges[i].EndTime(cIlStartTime)) { t.Errorf("Expecting: %v, received: %v", cc.Timespans[i].TimeStart, ec.Charges[i].EndTime(cIlStartTime)) } } if len(ec.Rating) != len(eEC.Rating) { t.Errorf("Expecting: %+v, received: %+v", eEC, ec) } // Compare to original timestamp if !reflect.DeepEqual(cc.Timespans[0].Increments[2].BalanceInfo.Unit.RateInterval.Rating.Rates, ec.Rates[ec.Rating[ec.Accounting[ec.Charges[0].Increments[2].AccountingID].RatingID].RatesID]) { t.Errorf("Expecting: %s, received: %s", utils.ToJSON(cc.Timespans[0].Increments[2].BalanceInfo.Unit.RateInterval.Rating.Rates), utils.ToJSON(ec.Rates[ec.Rating[ec.Accounting[ec.Charges[0].Increments[2].AccountingID].RatingID].RatesID])) } // Compare to expected EC if !reflect.DeepEqual(eEC.Rates[eEC.Rating[eEC.Accounting[eEC.Charges[0].Increments[2].AccountingID].RatingID].RatesID], ec.Rates[ec.Rating[ec.Accounting[ec.Charges[0].Increments[2].AccountingID].RatingID].RatesID]) { t.Errorf("Expecting: %s, received: %s", utils.ToJSON(eEC.Rates[eEC.Rating[eEC.Accounting[eEC.Charges[0].Increments[2].AccountingID].RatingID].RatesID]), utils.ToJSON(ec.Rates[ec.Rating[ec.Accounting[ec.Charges[0].Increments[2].AccountingID].RatingID].RatesID])) } // Compare to expected EC if !reflect.DeepEqual(eEC.Accounting[eEC.Charges[0].Increments[3].AccountingID], ec.Accounting[ec.Charges[0].Increments[3].AccountingID]) { t.Errorf("Expecting: %s, received: %s", utils.ToJSON(eEC.Accounting[eEC.Charges[0].Increments[3].AccountingID]), utils.ToJSON(ec.Accounting[ec.Charges[0].Increments[3].AccountingID])) } ec.Charges[0].Increments[3].AccountingID = eEC.Charges[0].Increments[3].AccountingID if !reflect.DeepEqual(eEC.Charges[0].Increments[3], ec.Charges[0].Increments[3]) { t.Errorf("Expecting: %s, received: %s", utils.ToJSON(eEC.Charges[0].Increments[3]), utils.ToJSON(ec.Charges[0].Increments[3])) } if len(ec.Accounting) != len(eEC.Accounting) { t.Errorf("Expecting: %+v, received: %+v", eEC, ec) } if len(ec.Rates) != len(eEC.Rates) { t.Errorf("Expecting: %+v, received: %+v", eEC, ec) } if len(ec.Timings) != len(eEC.Timings) { t.Errorf("Expecting: %+v, received: %+v", eEC, ec) } if !reflect.DeepEqual(eEC.AccountSummary, ec.AccountSummary) { t.Errorf("Expecting: %+v, received: %+v", eEC, ec) } } func TestECAsRefundIncrements(t *testing.T) { eCD := &CallDescriptor{ CgrID: "164b0422fdc6a5117031b427439482c6a4f90e41", RunID: utils.MetaDefault, ToR: utils.MetaVoice, TimeStart: time.Date(2017, 1, 9, 16, 18, 21, 0, time.UTC), TimeEnd: time.Date(2017, 1, 9, 16, 28, 21, 0, time.UTC), DurationIndex: 10 * time.Minute, } eCD.Increments = Increments{ &Increment{ Duration: 0, Cost: 0.1, CompressFactor: 1, BalanceInfo: &DebitInfo{ AccountID: "cgrates.org:dan", Monetary: &MonetaryInfo{ UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010"}}, }, &Increment{ Duration: time.Second, Cost: 0, CompressFactor: 10, BalanceInfo: &DebitInfo{ AccountID: "cgrates.org:dan", Unit: &UnitInfo{ UUID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", Factor: 1, }, }, }, &Increment{ Duration: 10 * time.Second, Cost: 0.01, CompressFactor: 2, BalanceInfo: &DebitInfo{ AccountID: "cgrates.org:dan", Monetary: &MonetaryInfo{ UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010"}}, }, &Increment{ Duration: time.Second, Cost: 0.005, CompressFactor: 30, BalanceInfo: &DebitInfo{ AccountID: "cgrates.org:dan", Unit: &UnitInfo{ UUID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", Factor: 1, }, Monetary: &MonetaryInfo{ UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", }, }, }, &Increment{ Duration: time.Second, Cost: 0.01, CompressFactor: 60, BalanceInfo: &DebitInfo{ AccountID: "cgrates.org:dan", Monetary: &MonetaryInfo{ UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010"}}, }, &Increment{ Duration: time.Second, Cost: 0.01, CompressFactor: 60, BalanceInfo: &DebitInfo{ AccountID: "cgrates.org:dan", Monetary: &MonetaryInfo{ UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010"}}, }, &Increment{ Duration: time.Second, Cost: 0.01, CompressFactor: 60, BalanceInfo: &DebitInfo{ AccountID: "cgrates.org:dan", Monetary: &MonetaryInfo{ UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010"}}, }, &Increment{ Duration: time.Second, Cost: 0.01, CompressFactor: 60, BalanceInfo: &DebitInfo{ AccountID: "cgrates.org:dan", Monetary: &MonetaryInfo{ UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010"}}, }, &Increment{ Duration: time.Second, Cost: 0, CompressFactor: 10, BalanceInfo: &DebitInfo{ AccountID: "cgrates.org:dan", Unit: &UnitInfo{ UUID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", Factor: 1, }, }, }, &Increment{ Duration: 10 * time.Second, Cost: 0.01, CompressFactor: 2, BalanceInfo: &DebitInfo{ AccountID: "cgrates.org:dan", Monetary: &MonetaryInfo{ UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010"}}, }, &Increment{ Duration: time.Second, Cost: 0.005, CompressFactor: 30, BalanceInfo: &DebitInfo{ AccountID: "cgrates.org:dan", Unit: &UnitInfo{ UUID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", Factor: 1, }, Monetary: &MonetaryInfo{ UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", }, }, }, &Increment{ Duration: time.Second, Cost: 0, CompressFactor: 10, BalanceInfo: &DebitInfo{ AccountID: "cgrates.org:dan", Unit: &UnitInfo{ UUID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", Factor: 1, }, }, }, &Increment{ Duration: 10 * time.Second, Cost: 0.01, CompressFactor: 2, BalanceInfo: &DebitInfo{ AccountID: "cgrates.org:dan", Monetary: &MonetaryInfo{ UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010"}}, }, &Increment{ Duration: time.Second, Cost: 0.005, CompressFactor: 30, BalanceInfo: &DebitInfo{ AccountID: "cgrates.org:dan", Unit: &UnitInfo{ UUID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", Factor: 1, }, Monetary: &MonetaryInfo{ UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", }, }, }, &Increment{ Duration: time.Second, Cost: 0, CompressFactor: 10, BalanceInfo: &DebitInfo{ AccountID: "cgrates.org:dan", Unit: &UnitInfo{ UUID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", Factor: 1, }, }, }, &Increment{ Duration: 10 * time.Second, Cost: 0.01, CompressFactor: 2, BalanceInfo: &DebitInfo{ AccountID: "cgrates.org:dan", Monetary: &MonetaryInfo{ UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010"}}, }, &Increment{ Duration: time.Second, Cost: 0.005, CompressFactor: 30, BalanceInfo: &DebitInfo{ AccountID: "cgrates.org:dan", Unit: &UnitInfo{ UUID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", Factor: 1, }, Monetary: &MonetaryInfo{ UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", }, }, }, &Increment{ Duration: time.Second, Cost: 0, CompressFactor: 10, BalanceInfo: &DebitInfo{ AccountID: "cgrates.org:dan", Unit: &UnitInfo{ UUID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", Factor: 1, }, }, }, &Increment{ Duration: 10 * time.Second, Cost: 0.01, CompressFactor: 2, BalanceInfo: &DebitInfo{ AccountID: "cgrates.org:dan", Monetary: &MonetaryInfo{ UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010"}}, }, &Increment{ Duration: time.Second, Cost: 0.005, CompressFactor: 30, BalanceInfo: &DebitInfo{ AccountID: "cgrates.org:dan", Unit: &UnitInfo{ UUID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", Factor: 1, }, Monetary: &MonetaryInfo{ UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", }, }, }, &Increment{ Duration: time.Second, Cost: 0, CompressFactor: 10, BalanceInfo: &DebitInfo{ AccountID: "cgrates.org:dan", Unit: &UnitInfo{ UUID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", Factor: 1, }, }, }, &Increment{ Duration: 10 * time.Second, Cost: 0.01, CompressFactor: 2, BalanceInfo: &DebitInfo{ AccountID: "cgrates.org:dan", Monetary: &MonetaryInfo{ UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010"}}, }, &Increment{ Duration: time.Second, Cost: 0.005, CompressFactor: 30, BalanceInfo: &DebitInfo{ AccountID: "cgrates.org:dan", Unit: &UnitInfo{ UUID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", Factor: 1, }, Monetary: &MonetaryInfo{ UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", }, }, }, } if cd := testEC.Clone().AsRefundIncrements(utils.MetaVoice); !reflect.DeepEqual(eCD, cd) { t.Errorf("expecting: %s\n\n, received: %s", utils.ToIJSON(eCD), utils.ToIJSON(cd)) } } func TestECAsCallCost(t *testing.T) { acntSummary := &AccountSummary{ Tenant: "cgrates.org", ID: "dan", BalanceSummaries: []*BalanceSummary{ { Type: "*monetary", Value: 50, Disabled: false}, { ID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", Type: "*monetary", Value: 25, Disabled: false}, { Type: "*voice", Value: 200, Disabled: false, }, }, AllowNegative: false, Disabled: false, } ec := &EventCost{ CGRID: "164b0422fdc6a5117031b427439482c6a4f90e41", RunID: utils.MetaDefault, StartTime: time.Date(2017, 1, 9, 16, 18, 21, 0, time.UTC), Cost: utils.Float64Pointer(0.85), Usage: utils.DurationPointer(2 * time.Minute), Charges: []*ChargingInterval{ { RatingID: "f2518464-68b8-42f4-acec-aef23d714314", Increments: []*ChargingIncrement{ { Usage: 0, Cost: 0.1, AccountingID: "44e97dec-8a7e-43d0-8b0a-736d46b5613e", CompressFactor: 1, }, { Usage: time.Second, Cost: 0, AccountingID: "a555cde8-4bd0-408a-afbc-c3ba64888927", CompressFactor: 30, }, { Usage: time.Second, Cost: 0.005, AccountingID: "906bfd0f-035c-40a3-93a8-46f71627983e", CompressFactor: 30, }, }, CompressFactor: 1, }, { RatingID: "f2518464-68b8-42f4-acec-aef23d714314", Increments: []*ChargingIncrement{ { Usage: time.Second, Cost: 0.01, AccountingID: "c890a899-df43-497a-9979-38492713f57b", CompressFactor: 60, }, }, CompressFactor: 1, }, }, AccountSummary: acntSummary, Rating: Rating{ "4607d907-02c3-4f2b-bc08-95a0dcc7222c": &RatingUnit{ RoundingMethod: "*up", RoundingDecimals: 5, TimingID: "27f1e5f8-05bb-4f1c-a596-bf1010ad296c", RatesID: "e5eb0f1c-3612-4e8c-b749-7f8f41dd90d4", RatingFiltersID: "7e73a00d-be53-4083-a1ee-8ee0b546c62a", }, "f2518464-68b8-42f4-acec-aef23d714314": &RatingUnit{ ConnectFee: 0.1, RoundingMethod: "*up", RoundingDecimals: 5, TimingID: "27f1e5f8-05bb-4f1c-a596-bf1010ad296c", RatesID: "6504fb84-6b27-47a8-a1c6-c0d843959f89", RatingFiltersID: "7e73a00d-be53-4083-a1ee-8ee0b546c62a", }, }, Accounting: Accounting{ "c890a899-df43-497a-9979-38492713f57b": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", Units: 0.01, }, "a894f8f1-206a-4457-99ce-df21a0c7fedc": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", Units: 0.005, }, "44e97dec-8a7e-43d0-8b0a-736d46b5613e": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", Units: 0.1, }, "906bfd0f-035c-40a3-93a8-46f71627983e": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "7a54a9e9-d610-4c82-bcb5-a315b9a65010", RatingID: "4607d907-02c3-4f2b-bc08-95a0dcc7222c", Units: 1, ExtraChargeID: "a894f8f1-206a-4457-99ce-df21a0c7fedc", }, "a555cde8-4bd0-408a-afbc-c3ba64888927": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "9d54a9e9-d610-4c82-bcb5-a315b9a65089", Units: 1, ExtraChargeID: "*none", }, }, RatingFilters: RatingFilters{ "7e73a00d-be53-4083-a1ee-8ee0b546c62a": RatingMatchedFilters{ "DestinationID": "GERMANY", "DestinationPrefix": "+49", "RatingPlanID": "RPL_RETAIL1", "Subject": "*out:cgrates.org:call:*any", }, }, Rates: ChargedRates{ "6504fb84-6b27-47a8-a1c6-c0d843959f89": RateGroups{ &RGRate{ GroupIntervalStart: 0, Value: 0.01, RateIncrement: time.Minute, RateUnit: time.Second}, }, "e5eb0f1c-3612-4e8c-b749-7f8f41dd90d4": RateGroups{ &RGRate{ GroupIntervalStart: 0, Value: 0.005, RateIncrement: time.Second, RateUnit: time.Second}, &RGRate{ GroupIntervalStart: 60 * time.Second, Value: 0.005, RateIncrement: time.Second, RateUnit: time.Second}, }, }, Timings: ChargedTimings{ "27f1e5f8-05bb-4f1c-a596-bf1010ad296c": &ChargedTiming{ StartTime: "00:00:00", }, }, } eCC := &CallCost{ ToR: utils.MetaVoice, Cost: 0.85, RatedUsage: 120000000000, AccountSummary: acntSummary, Timespans: TimeSpans{ &TimeSpan{ TimeStart: time.Date(2017, 1, 9, 16, 18, 21, 0, time.UTC), TimeEnd: time.Date(2017, 1, 9, 16, 19, 21, 0, time.UTC), Cost: 0.25, RateInterval: &RateInterval{ // standard rating Timing: &RITiming{ ID: "27f1e5f8-05bb-4f1c-a596-bf1010ad296c", StartTime: "00:00:00", }, Rating: &RIRate{ ConnectFee: 0.1, RoundingMethod: "*up", RoundingDecimals: 5, Rates: RateGroups{ &RGRate{ GroupIntervalStart: 0, Value: 0.01, RateUnit: time.Second, RateIncrement: time.Minute, }, }, }, }, DurationIndex: time.Minute, MatchedSubject: "*out:cgrates.org:call:*any", MatchedPrefix: "+49", MatchedDestId: "GERMANY", RatingPlanId: "RPL_RETAIL1", CompressFactor: 1, Increments: Increments{ &Increment{ // ConnectFee Cost: 0.1, BalanceInfo: &DebitInfo{ Monetary: &MonetaryInfo{ UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010"}, AccountID: "cgrates.org:dan", }, CompressFactor: 1, }, &Increment{ // First 30 seconds free Duration: time.Second, Cost: 0, BalanceInfo: &DebitInfo{ Unit: &UnitInfo{ UUID: "9d54a9e9-d610-4c82-bcb5-a315b9a65089", Consumed: 1, Factor: 1, }, AccountID: "cgrates.org:dan", }, CompressFactor: 30, }, &Increment{ // Minutes with special price Duration: time.Second, Cost: 0.005, BalanceInfo: &DebitInfo{ Unit: &UnitInfo{ // Minutes with special price UUID: "7a54a9e9-d610-4c82-bcb5-a315b9a65010", Consumed: 1, Factor: 1, RateInterval: &RateInterval{ Timing: &RITiming{ ID: "27f1e5f8-05bb-4f1c-a596-bf1010ad296c", StartTime: "00:00:00", }, Rating: &RIRate{ ConnectFee: 0, RoundingMethod: "*up", RoundingDecimals: 5, Rates: RateGroups{ &RGRate{ GroupIntervalStart: 0, Value: 0.005, RateUnit: time.Second, RateIncrement: time.Second, }, &RGRate{ GroupIntervalStart: 60 * time.Second, Value: 0.005, RateUnit: time.Second, RateIncrement: time.Second, }, }, }, }, }, Monetary: &MonetaryInfo{ UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010"}, AccountID: "cgrates.org:dan", }, CompressFactor: 30, }, }, }, &TimeSpan{ TimeStart: time.Date(2017, 1, 9, 16, 19, 21, 0, time.UTC), TimeEnd: time.Date(2017, 1, 9, 16, 20, 21, 0, time.UTC), Cost: 0.6, RateInterval: &RateInterval{ // standard rating Timing: &RITiming{ ID: "27f1e5f8-05bb-4f1c-a596-bf1010ad296c", StartTime: "00:00:00", }, Rating: &RIRate{ ConnectFee: 0.1, RoundingMethod: "*up", RoundingDecimals: 5, Rates: RateGroups{ &RGRate{ GroupIntervalStart: 0, Value: 0.01, RateUnit: time.Second, RateIncrement: time.Minute, }, }, }, }, DurationIndex: time.Minute, MatchedSubject: "*out:cgrates.org:call:*any", MatchedPrefix: "+49", MatchedDestId: "GERMANY", RatingPlanId: "RPL_RETAIL1", CompressFactor: 1, Increments: Increments{ &Increment{ Cost: 0.01, Duration: time.Second, BalanceInfo: &DebitInfo{ Monetary: &MonetaryInfo{ UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010"}, AccountID: "cgrates.org:dan", }, CompressFactor: 60, }, }, }, }, } cc := ec.AsCallCost(utils.EmptyString) if !reflect.DeepEqual(eCC, cc) { t.Errorf("Expecting: %+v,\n received: %+v", utils.ToJSON(eCC), utils.ToJSON(cc)) } } func TestECAsCallCost2(t *testing.T) { eCC := &CallCost{ ToR: utils.MetaVoice, Cost: 0, RatedUsage: 60000000000, Timespans: TimeSpans{ &TimeSpan{ TimeStart: time.Date(2017, 1, 9, 16, 18, 21, 0, time.UTC), TimeEnd: time.Date(2017, 1, 9, 16, 19, 21, 0, time.UTC), Cost: 0, RateInterval: &RateInterval{ // standard rating Timing: &RITiming{ StartTime: "00:00:00", }, Rating: &RIRate{ ConnectFee: 0.1, RoundingMethod: "*up", RoundingDecimals: 5, Rates: RateGroups{ &RGRate{ GroupIntervalStart: time.Duration(0), Value: 0.01, RateUnit: time.Duration(1 * time.Second), RateIncrement: time.Duration(1 * time.Minute), }, }, }, }, DurationIndex: time.Minute, MatchedSubject: "*out:cgrates.org:call:*any", MatchedPrefix: "+49", MatchedDestId: "GERMANY", RatingPlanId: "RPL_RETAIL1", CompressFactor: 1, Increments: Increments{ &Increment{ // ConnectFee Cost: 0, Duration: time.Minute, BalanceInfo: &DebitInfo{}, CompressFactor: 1, }, }, }, }, } ec := NewEventCostFromCallCost(eCC, "cgrID", utils.MetaDefault) for k := range ec.Timings { eCC.Timespans[0].RateInterval.Timing.ID = k } cc := ec.AsCallCost(utils.EmptyString) if !reflect.DeepEqual(eCC, cc) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(eCC), utils.ToJSON(cc)) } } func TestECTrimZeroAndFull(t *testing.T) { ec := testEC.Clone() if srplsEC, err := ec.Trim(10 * time.Minute); err != nil { t.Error(err) } else if srplsEC != nil { t.Errorf("Expecting nil, got: %+v", srplsEC) } eFullSrpls := testEC.Clone() eFullSrpls.Usage = utils.DurationPointer(10 * time.Minute) if srplsEC, err := ec.Trim(0); err != nil { t.Error(err) } else if !reflect.DeepEqual(eFullSrpls, srplsEC) { t.Errorf("\tExpecting: %s,\n\treceived: %s", utils.ToJSON(eFullSrpls), utils.ToJSON(srplsEC)) } //verify the event cost newEc := NewBareEventCost() newEc.CGRID = eFullSrpls.CGRID newEc.RunID = eFullSrpls.RunID newEc.StartTime = eFullSrpls.StartTime newEc.AccountSummary = eFullSrpls.AccountSummary.Clone() if !reflect.DeepEqual(newEc, ec) { t.Errorf("\tExpecting: %s,\n\treceived: %s", utils.ToJSON(newEc), utils.ToJSON(ec)) } } func TestECTrimMiddle1(t *testing.T) { // trim in the middle of increments ec := testEC.Clone() eEC := testEC.Clone() eEC.Charges = []*ChargingInterval{ { RatingID: "c1a5ab9", Increments: []*ChargingIncrement{ { Usage: 0, Cost: 0.1, AccountingID: "9bdad10", CompressFactor: 1, }, { Usage: time.Second, Cost: 0, AccountingID: "3455b83", CompressFactor: 10, }, { Usage: 10 * time.Second, Cost: 0.01, AccountingID: "a012888", CompressFactor: 2, }, { Usage: time.Second, Cost: 0.005, AccountingID: "44d6c02", CompressFactor: 30, }, }, CompressFactor: 1, }, { RatingID: "c1a5ab9", Increments: []*ChargingIncrement{ { Usage: time.Second, Cost: 0.01, AccountingID: "a012888", CompressFactor: 60, }, }, CompressFactor: 2, }, { RatingID: "c1a5ab9", Increments: []*ChargingIncrement{ { Usage: time.Second, Cost: 0.01, AccountingID: "a012888", CompressFactor: 10, }, }, CompressFactor: 1, }, } eSrplsEC := testEC.Clone() eSrplsEC.StartTime = time.Date(2017, 1, 9, 16, 21, 31, 0, time.UTC) eSrplsEC.Charges = []*ChargingInterval{ { RatingID: "c1a5ab9", Increments: []*ChargingIncrement{ { Usage: time.Second, Cost: 0.01, AccountingID: "a012888", CompressFactor: 50, }, }, CompressFactor: 1, }, { RatingID: "c1a5ab9", Increments: []*ChargingIncrement{ { Usage: time.Second, Cost: 0.01, AccountingID: "a012888", CompressFactor: 60, }, }, CompressFactor: 1, }, { RatingID: "c1a5ab9", Increments: []*ChargingIncrement{ { Usage: time.Second, Cost: 0, AccountingID: "3455b83", CompressFactor: 10, }, { Usage: 10 * time.Second, Cost: 0.01, AccountingID: "a012888", CompressFactor: 2, }, { Usage: time.Second, Cost: 0.005, AccountingID: "44d6c02", CompressFactor: 30, }, }, CompressFactor: 5, }, } reqDuration := 190 * time.Second srplsEC, err := ec.Trim(reqDuration) if err != nil { t.Error(err) } if reqDuration != *ec.Usage { t.Errorf("Expecting request duration: %v, received: %v", reqDuration, *ec.Usage) } eSrplsDur := 410 * time.Second if srplsUsage := srplsEC.GetUsage(); srplsUsage != eSrplsDur { t.Errorf("Expecting surplus duration: %v, received: %v", eSrplsDur, srplsUsage) } ec.ResetCounters() srplsEC.ResetCounters() if !reflect.DeepEqual(eEC, ec) { t.Errorf("Expecting: %s\n, received: %s", utils.ToIJSON(eEC), utils.ToIJSON(ec)) } // test surplus, which is not easy to estimate due it's different item ids if !eSrplsEC.StartTime.Equal(srplsEC.StartTime) || len(eSrplsEC.Charges) != len(srplsEC.Charges) { t.Errorf("Expecting: \n%s, received: \n%s", utils.ToIJSON(eSrplsEC), utils.ToIJSON(srplsEC)) } } // TestECTrimMUsage is targeting simpler testing of the durations trimmed/remainders // using subtests so we can cover the tests with less code func TestECTrimMUsage(t *testing.T) { // each subtest will trim at some usage duration testCases := []struct { atUsage time.Duration ecUsage time.Duration ecCost float64 srplsUsage time.Duration srplsCost float64 }{ {5 * time.Second, 5 * time.Second, 0.1, 595 * time.Second, 3.42}, {10 * time.Second, 10 * time.Second, 0.1, 590 * time.Second, 3.42}, {15 * time.Second, 20 * time.Second, 0.11, 580 * time.Second, 3.41}, {20 * time.Second, 20 * time.Second, 0.11, 580 * time.Second, 3.41}, {25 * time.Second, 30 * time.Second, 0.12, 570 * time.Second, 3.40}, {38 * time.Second, 38 * time.Second, 0.16, 562 * time.Second, 3.36}, {60 * time.Second, 60 * time.Second, 0.27, 540 * time.Second, 3.25}, {62 * time.Second, 62 * time.Second, 0.29, 538 * time.Second, 3.23}, {120 * time.Second, 120 * time.Second, 0.87, 480 * time.Second, 2.65}, {121 * time.Second, 121 * time.Second, 0.88, 479 * time.Second, 2.64}, {180 * time.Second, 180 * time.Second, 1.47, 420 * time.Second, 2.05}, {250 * time.Second, 250 * time.Second, 2.17, 350 * time.Second, 1.35}, {299 * time.Second, 299 * time.Second, 2.66, 301 * time.Second, 0.86}, {300 * time.Second, 300 * time.Second, 2.67, 300 * time.Second, 0.85}, {302 * time.Second, 302 * time.Second, 2.67, 298 * time.Second, 0.85}, {310 * time.Second, 310 * time.Second, 2.67, 290 * time.Second, 0.85}, {316 * time.Second, 320 * time.Second, 2.68, 280 * time.Second, 0.84}, {320 * time.Second, 320 * time.Second, 2.68, 280 * time.Second, 0.84}, {321 * time.Second, 330 * time.Second, 2.69, 270 * time.Second, 0.83}, {330 * time.Second, 330 * time.Second, 2.69, 270 * time.Second, 0.83}, {331 * time.Second, 331 * time.Second, 2.695, 269 * time.Second, 0.825}, {359 * time.Second, 359 * time.Second, 2.835, 241 * time.Second, 0.685}, {360 * time.Second, 360 * time.Second, 2.84, 240 * time.Second, 0.68}, {376 * time.Second, 380 * time.Second, 2.85, 220 * time.Second, 0.67}, {391 * time.Second, 391 * time.Second, 2.865, 209 * time.Second, 0.655}, {479 * time.Second, 479 * time.Second, 3.175, 121 * time.Second, 0.345}, {599 * time.Second, 599 * time.Second, 3.515, time.Second, 0.005}, } for _, tC := range testCases { t.Run(fmt.Sprintf("AtUsage:%s", tC.atUsage), func(t *testing.T) { var ec, srplsEC *EventCost ec = testEC.Clone() if srplsEC, err = ec.Trim(tC.atUsage); err != nil { t.Fatal(err) } if ec.GetUsage() != tC.ecUsage { t.Errorf("Wrongly trimmed EC: %s", utils.ToIJSON(ec)) } else if ec.GetCost() != tC.ecCost { t.Errorf("Wrong cost for event: %s", utils.ToIJSON(ec)) } if srplsEC.GetUsage() != tC.srplsUsage { t.Errorf("Wrong usage: %v for surplusEC: %s", srplsEC.GetUsage(), utils.ToIJSON(srplsEC)) } else if srplsEC.GetCost() != tC.srplsCost { t.Errorf("Wrong cost: %f in surplus: %s", srplsEC.GetCost(), utils.ToIJSON(srplsEC)) } }) } } func TestECMergeGT(t *testing.T) { // InitialEventCost ecGT := &EventCost{ CGRID: "7636f3f1a06dffa038ba7900fb57f52d28830a24", RunID: utils.MetaDefault, StartTime: time.Date(2018, 7, 27, 0, 59, 21, 0, time.UTC), Charges: []*ChargingInterval{ { RatingID: "cc68da4", Increments: []*ChargingIncrement{ { Usage: 102400, AccountingID: "0d87a64", CompressFactor: 103, }, }, CompressFactor: 1, }, }, AccountSummary: &AccountSummary{ Tenant: "cgrates.org", ID: "dan", BalanceSummaries: []*BalanceSummary{ { UUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5", ID: "addon_data", Type: utils.MetaData, Value: 10726871040}, }, }, Rating: Rating{ "cc68da4": &RatingUnit{ RatesID: "06dee2e", RatingFiltersID: "216b0a5", }, }, Accounting: Accounting{ "0d87a64": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5", Units: 102400, ExtraChargeID: utils.MetaNone, }, }, RatingFilters: RatingFilters{ "216b0a5": RatingMatchedFilters{ "DestinationID": utils.MetaAny, "DestinationPrefix": "42502", "RatingPlanID": utils.MetaNone, "Subject": "9a767726-fe69-4940-b7bd-f43de9f0f8a5", }, }, Rates: ChargedRates{ "06dee2e": RateGroups{ &RGRate{ RateIncrement: 102400, RateUnit: 102400}, }, }, } ecGTUpdt := &EventCost{ CGRID: "7636f3f1a06dffa038ba7900fb57f52d28830a24", RunID: utils.MetaDefault, StartTime: time.Date(2018, 7, 27, 0, 59, 38, 0105472, time.UTC), Charges: []*ChargingInterval{ { RatingID: "6a83227", Increments: []*ChargingIncrement{ { Usage: 102400, AccountingID: "9288f93", CompressFactor: 84, }, }, CompressFactor: 1, }, }, AccountSummary: &AccountSummary{ Tenant: "cgrates.org", ID: "dan", BalanceSummaries: []*BalanceSummary{ { UUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5", ID: "addon_data", Type: utils.MetaData, Value: 10718269440}, }, }, Rating: Rating{ "6a83227": &RatingUnit{ RatesID: "52f8b0f", RatingFiltersID: "17f7216", }, }, Accounting: Accounting{ "9288f93": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5", Units: 102400, ExtraChargeID: utils.MetaNone, }, }, RatingFilters: RatingFilters{ "17f7216": RatingMatchedFilters{ "DestinationID": utils.MetaAny, "DestinationPrefix": "42502", "RatingPlanID": utils.MetaNone, "Subject": "9a767726-fe69-4940-b7bd-f43de9f0f8a5", }, }, Rates: ChargedRates{ "52f8b0f": RateGroups{ &RGRate{ RateIncrement: 102400, RateUnit: 102400}, }, }, } ecGT.Merge(ecGTUpdt) ecExpct := &EventCost{ CGRID: "7636f3f1a06dffa038ba7900fb57f52d28830a24", RunID: utils.MetaDefault, StartTime: time.Date(2018, 7, 27, 0, 59, 21, 0, time.UTC), Charges: []*ChargingInterval{ { RatingID: "cc68da4", Increments: []*ChargingIncrement{ { Usage: 102400, AccountingID: "0d87a64", CompressFactor: 187, }, }, CompressFactor: 1, }, }, AccountSummary: &AccountSummary{ Tenant: "cgrates.org", ID: "dan", BalanceSummaries: []*BalanceSummary{ { UUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5", ID: "addon_data", Type: utils.MetaData, Value: 10718269440}, }, }, Rating: Rating{ "cc68da4": &RatingUnit{ RatesID: "06dee2e", RatingFiltersID: "216b0a5", }, }, Accounting: Accounting{ "0d87a64": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5", Units: 102400, ExtraChargeID: utils.MetaNone, }, }, RatingFilters: RatingFilters{ "216b0a5": RatingMatchedFilters{ "DestinationID": utils.MetaAny, "DestinationPrefix": "42502", "RatingPlanID": utils.MetaNone, "Subject": "9a767726-fe69-4940-b7bd-f43de9f0f8a5", }, }, Rates: ChargedRates{ "06dee2e": RateGroups{ &RGRate{ RateIncrement: 102400, RateUnit: 102400}, }, }, } if len(ecGT.Charges) != len(ecExpct.Charges) || !reflect.DeepEqual(ecGT.Charges[0].TotalUsage(), ecExpct.Charges[0].TotalUsage()) || !reflect.DeepEqual(ecGT.Charges[0].TotalCost(), ecExpct.Charges[0].TotalCost()) { t.Errorf("expecting: %s\n\n, received: %s", utils.ToJSON(ecExpct), utils.ToJSON(ecGT)) } } func TestECAppendCIlFromEC(t *testing.T) { // Standard compressing 1-1 ec := &EventCost{ Charges: []*ChargingInterval{ { RatingID: "cc68da4", Increments: []*ChargingIncrement{ { Usage: 102400, AccountingID: "0d87a64", CompressFactor: 103, }, }, CompressFactor: 1, }, }, Rating: Rating{ "cc68da4": &RatingUnit{ RatesID: "06dee2e", RatingFiltersID: "216b0a5", }, }, Accounting: Accounting{ "0d87a64": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5", Units: 102400, ExtraChargeID: utils.MetaNone, }, }, RatingFilters: RatingFilters{ "216b0a5": RatingMatchedFilters{ "DestinationID": utils.MetaAny, "DestinationPrefix": "42502", "RatingPlanID": utils.MetaNone, "Subject": "9a767726-fe69-4940-b7bd-f43de9f0f8a5", }, }, Rates: ChargedRates{ "06dee2e": RateGroups{ &RGRate{ RateIncrement: 102400, RateUnit: 102400}, }, }, } oEC := &EventCost{ Charges: []*ChargingInterval{ { RatingID: "6a83227", Increments: []*ChargingIncrement{ { Usage: 102400, AccountingID: "9288f93", CompressFactor: 84, }, }, CompressFactor: 1, }, }, Rating: Rating{ "6a83227": &RatingUnit{ RatesID: "52f8b0f", RatingFiltersID: "17f7216", }, }, Accounting: Accounting{ "9288f93": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5", Units: 102400, ExtraChargeID: utils.MetaNone, }, }, RatingFilters: RatingFilters{ "17f7216": RatingMatchedFilters{ "DestinationID": utils.MetaAny, "DestinationPrefix": "42502", "RatingPlanID": utils.MetaNone, "Subject": "9a767726-fe69-4940-b7bd-f43de9f0f8a5", }, }, Rates: ChargedRates{ "52f8b0f": RateGroups{ &RGRate{ RateIncrement: 102400, RateUnit: 102400}, }, }, } ec.appendCIlFromEC(oEC, 0) eEC := &EventCost{ Charges: []*ChargingInterval{ { RatingID: "cc68da4", Increments: []*ChargingIncrement{ { Usage: 102400, AccountingID: "0d87a64", CompressFactor: 187, }, }, CompressFactor: 1, }, }, Rating: Rating{ "cc68da4": &RatingUnit{ RatesID: "06dee2e", RatingFiltersID: "216b0a5", }, }, Accounting: Accounting{ "0d87a64": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5", Units: 102400, ExtraChargeID: utils.MetaNone, }, }, RatingFilters: RatingFilters{ "216b0a5": RatingMatchedFilters{ "DestinationID": utils.MetaAny, "DestinationPrefix": "42502", "RatingPlanID": utils.MetaNone, "Subject": "9a767726-fe69-4940-b7bd-f43de9f0f8a5", }, }, Rates: ChargedRates{ "06dee2e": RateGroups{ &RGRate{ RateIncrement: 102400, RateUnit: 102400}, }, }, } if !reflect.DeepEqual(eEC, ec) { t.Errorf("expecting: %s, received: %s", utils.ToJSON(eEC), utils.ToJSON(ec)) } // Second case, do not compress if first interval's compress factor is different than 1 ec = &EventCost{ Charges: []*ChargingInterval{ { RatingID: "cc68da4", Increments: []*ChargingIncrement{ { Usage: 102400, AccountingID: "0d87a64", CompressFactor: 103, }, }, CompressFactor: 2, }, }, Rating: Rating{ "cc68da4": &RatingUnit{ RatesID: "06dee2e", RatingFiltersID: "216b0a5", }, }, Accounting: Accounting{ "0d87a64": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5", Units: 102400, ExtraChargeID: utils.MetaNone, }, }, RatingFilters: RatingFilters{ "216b0a5": RatingMatchedFilters{ "DestinationID": utils.MetaAny, "DestinationPrefix": "42502", "RatingPlanID": utils.MetaNone, "Subject": "9a767726-fe69-4940-b7bd-f43de9f0f8a5", }, }, Rates: ChargedRates{ "06dee2e": RateGroups{ &RGRate{ RateIncrement: 102400, RateUnit: 102400}, }, }, } oEC = &EventCost{ Charges: []*ChargingInterval{ { RatingID: "6a83227", Increments: []*ChargingIncrement{ { Usage: 102400, AccountingID: "9288f93", CompressFactor: 84, }, }, CompressFactor: 1, }, }, Rating: Rating{ "6a83227": &RatingUnit{ RatesID: "52f8b0f", RatingFiltersID: "17f7216", }, }, Accounting: Accounting{ "9288f93": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5", Units: 102400, ExtraChargeID: utils.MetaNone, }, }, RatingFilters: RatingFilters{ "17f7216": RatingMatchedFilters{ "DestinationID": utils.MetaAny, "DestinationPrefix": "42502", "RatingPlanID": utils.MetaNone, "Subject": "9a767726-fe69-4940-b7bd-f43de9f0f8a5", }, }, Rates: ChargedRates{ "52f8b0f": RateGroups{ &RGRate{ RateIncrement: 102400, RateUnit: 102400}, }, }, } ec.appendCIlFromEC(oEC, 0) eEC = &EventCost{ Charges: []*ChargingInterval{ { RatingID: "cc68da4", Increments: []*ChargingIncrement{ { Usage: 102400, AccountingID: "0d87a64", CompressFactor: 103, }, }, CompressFactor: 2, }, { RatingID: "cc68da4", Increments: []*ChargingIncrement{ { Usage: 102400, AccountingID: "0d87a64", CompressFactor: 84, }, }, CompressFactor: 1, }, }, Rating: Rating{ "cc68da4": &RatingUnit{ RatesID: "06dee2e", RatingFiltersID: "216b0a5", }, }, Accounting: Accounting{ "0d87a64": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5", Units: 102400, ExtraChargeID: utils.MetaNone, }, }, RatingFilters: RatingFilters{ "216b0a5": RatingMatchedFilters{ "DestinationID": utils.MetaAny, "DestinationPrefix": "42502", "RatingPlanID": utils.MetaNone, "Subject": "9a767726-fe69-4940-b7bd-f43de9f0f8a5", }, }, Rates: ChargedRates{ "06dee2e": RateGroups{ &RGRate{ RateIncrement: 102400, RateUnit: 102400}, }, }, } if !reflect.DeepEqual(eEC, ec) { t.Errorf("expecting: %s, received: %s", utils.ToJSON(eEC), utils.ToJSON(ec)) } // Third case, split oEC ec = &EventCost{ Charges: []*ChargingInterval{ { RatingID: "cc68da4", Increments: []*ChargingIncrement{ { Usage: 100, AccountingID: "0d87a64", CompressFactor: 1, }, }, CompressFactor: 1, }, { RatingID: "cc68da4", Increments: []*ChargingIncrement{ { Usage: 102400, AccountingID: "0d87a64", CompressFactor: 103, }, }, CompressFactor: 1, }, }, Rating: Rating{ "cc68da4": &RatingUnit{ RatesID: "06dee2e", RatingFiltersID: "216b0a5", }, }, Accounting: Accounting{ "0d87a64": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5", Units: 102400, ExtraChargeID: utils.MetaNone, }, }, RatingFilters: RatingFilters{ "216b0a5": RatingMatchedFilters{ "DestinationID": utils.MetaAny, "DestinationPrefix": "42502", "RatingPlanID": utils.MetaNone, "Subject": "9a767726-fe69-4940-b7bd-f43de9f0f8a5", }, }, Rates: ChargedRates{ "06dee2e": RateGroups{ &RGRate{ RateIncrement: 102400, RateUnit: 102400}, }, }, } oEC = &EventCost{ Charges: []*ChargingInterval{ { RatingID: "6a83227", Increments: []*ChargingIncrement{ { Usage: 102400, AccountingID: "9288f93", CompressFactor: 42, }, { Usage: 10240, AccountingID: "9288f93", CompressFactor: 20, }, }, CompressFactor: 3, }, }, Rating: Rating{ "6a83227": &RatingUnit{ RatesID: "52f8b0f", RatingFiltersID: "17f7216", }, }, Accounting: Accounting{ "9288f93": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5", Units: 102400, ExtraChargeID: utils.MetaNone, }, }, RatingFilters: RatingFilters{ "17f7216": RatingMatchedFilters{ "DestinationID": utils.MetaAny, "DestinationPrefix": "42502", "RatingPlanID": utils.MetaNone, "Subject": "9a767726-fe69-4940-b7bd-f43de9f0f8a5", }, }, Rates: ChargedRates{ "52f8b0f": RateGroups{ &RGRate{ RateIncrement: 102400, RateUnit: 102400}, }, }, } ec.appendCIlFromEC(oEC, 0) eEC = &EventCost{ Charges: []*ChargingInterval{ { RatingID: "cc68da4", Increments: []*ChargingIncrement{ { Usage: 100, AccountingID: "0d87a64", CompressFactor: 1, }, }, CompressFactor: 1, }, { RatingID: "cc68da4", Increments: []*ChargingIncrement{ { Usage: 102400, AccountingID: "0d87a64", CompressFactor: 145, }, }, CompressFactor: 1, }, { RatingID: "cc68da4", Increments: []*ChargingIncrement{ { Usage: 10240, AccountingID: "0d87a64", CompressFactor: 20, }, }, CompressFactor: 1, }, { RatingID: "cc68da4", Increments: []*ChargingIncrement{ { Usage: 102400, AccountingID: "0d87a64", CompressFactor: 42, }, { Usage: 10240, AccountingID: "0d87a64", CompressFactor: 20, }, }, CompressFactor: 2, }, }, Rating: Rating{ "cc68da4": &RatingUnit{ RatesID: "06dee2e", RatingFiltersID: "216b0a5", }, }, Accounting: Accounting{ "0d87a64": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5", Units: 102400, ExtraChargeID: utils.MetaNone, }, }, RatingFilters: RatingFilters{ "216b0a5": RatingMatchedFilters{ "DestinationID": utils.MetaAny, "DestinationPrefix": "42502", "RatingPlanID": utils.MetaNone, "Subject": "9a767726-fe69-4940-b7bd-f43de9f0f8a5", }, }, Rates: ChargedRates{ "06dee2e": RateGroups{ &RGRate{ RateIncrement: 102400, RateUnit: 102400}, }, }, } if !reflect.DeepEqual(eEC, ec) { t.Errorf("expecting: %s, received: %s", utils.ToJSON(eEC), utils.ToJSON(ec)) } // Fourth case, increase ChargingInterval.CompressFactor ec = &EventCost{ Charges: []*ChargingInterval{ { RatingID: "cc68da4", Increments: []*ChargingIncrement{ { Usage: 102400, AccountingID: "0d87a64", CompressFactor: 103, }, }, CompressFactor: 2, }, }, Rating: Rating{ "cc68da4": &RatingUnit{ RatesID: "06dee2e", RatingFiltersID: "216b0a5", }, }, Accounting: Accounting{ "0d87a64": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5", Units: 102400, ExtraChargeID: utils.MetaNone, }, }, RatingFilters: RatingFilters{ "216b0a5": RatingMatchedFilters{ "DestinationID": utils.MetaAny, "DestinationPrefix": "42502", "RatingPlanID": utils.MetaNone, "Subject": "9a767726-fe69-4940-b7bd-f43de9f0f8a5", }, }, Rates: ChargedRates{ "06dee2e": RateGroups{ &RGRate{ RateIncrement: 102400, RateUnit: 102400}, }, }, } oEC = &EventCost{ Charges: []*ChargingInterval{ { RatingID: "6a83227", Increments: []*ChargingIncrement{ { Usage: 102400, AccountingID: "9288f93", CompressFactor: 103, }, }, CompressFactor: 3, }, }, Rating: Rating{ "6a83227": &RatingUnit{ RatesID: "52f8b0f", RatingFiltersID: "17f7216", }, }, Accounting: Accounting{ "9288f93": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5", Units: 102400, ExtraChargeID: utils.MetaNone, }, }, RatingFilters: RatingFilters{ "17f7216": RatingMatchedFilters{ "DestinationID": utils.MetaAny, "DestinationPrefix": "42502", "RatingPlanID": utils.MetaNone, "Subject": "9a767726-fe69-4940-b7bd-f43de9f0f8a5", }, }, Rates: ChargedRates{ "52f8b0f": RateGroups{ &RGRate{ RateIncrement: 102400, RateUnit: 102400}, }, }, } ec.appendCIlFromEC(oEC, 0) eEC = &EventCost{ Charges: []*ChargingInterval{ { RatingID: "cc68da4", Increments: []*ChargingIncrement{ { Usage: 102400, AccountingID: "0d87a64", CompressFactor: 103, }, }, CompressFactor: 5, }, }, Rating: Rating{ "cc68da4": &RatingUnit{ RatesID: "06dee2e", RatingFiltersID: "216b0a5", }, }, Accounting: Accounting{ "0d87a64": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5", Units: 102400, ExtraChargeID: utils.MetaNone, }, }, RatingFilters: RatingFilters{ "216b0a5": RatingMatchedFilters{ "DestinationID": utils.MetaAny, "DestinationPrefix": "42502", "RatingPlanID": utils.MetaNone, "Subject": "9a767726-fe69-4940-b7bd-f43de9f0f8a5", }, }, Rates: ChargedRates{ "06dee2e": RateGroups{ &RGRate{ RateIncrement: 102400, RateUnit: 102400}, }, }, } if !reflect.DeepEqual(eEC, ec) { t.Errorf("expecting: %s, received: %s", utils.ToJSON(eEC), utils.ToJSON(ec)) } } func TestECSyncKeys(t *testing.T) { ec := testEC.Clone() ec.Accounting["a012888"].RatingID = "c1a5ab9" refEC := &EventCost{ Rating: Rating{ "21a5ab9": &RatingUnit{ ConnectFee: 0.1, RoundingMethod: "*up", RoundingDecimals: 5, MaxCostStrategy: utils.MetaMaxCostDisconnect, TimingID: "2f324ab", RatesID: "2c1a177", RatingFiltersID: "23e77dc", }, }, Accounting: Accounting{ "2012888": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", Units: 0.01, RatingID: "21a5ab9", }, "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{ &RGRate{ GroupIntervalStart: 0, Value: 0.01, RateIncrement: time.Minute, RateUnit: time.Second}, }, }, } eEC := &EventCost{ CGRID: "164b0422fdc6a5117031b427439482c6a4f90e41", RunID: utils.MetaDefault, StartTime: time.Date(2017, 1, 9, 16, 18, 21, 0, time.UTC), Charges: []*ChargingInterval{ { RatingID: "21a5ab9", Increments: []*ChargingIncrement{ { Usage: 0, Cost: 0.1, AccountingID: "9bdad10", CompressFactor: 1, }, { Usage: time.Second, Cost: 0, AccountingID: "3455b83", CompressFactor: 10, }, { Usage: 10 * time.Second, Cost: 0.01, AccountingID: "2012888", CompressFactor: 2, }, { Usage: time.Second, Cost: 0.005, AccountingID: "44d6c02", CompressFactor: 30, }, }, CompressFactor: 1, }, { RatingID: "21a5ab9", Increments: []*ChargingIncrement{ { Usage: time.Second, Cost: 0.01, AccountingID: "2012888", CompressFactor: 60, }, }, CompressFactor: 4, }, { RatingID: "21a5ab9", Increments: []*ChargingIncrement{ { Usage: time.Second, Cost: 0, AccountingID: "3455b83", CompressFactor: 10, }, { Usage: 10 * time.Second, Cost: 0.01, AccountingID: "2012888", CompressFactor: 2, }, { Usage: time.Second, Cost: 0.005, AccountingID: "44d6c02", CompressFactor: 30, }, }, CompressFactor: 5, }, }, AccountSummary: &AccountSummary{ Tenant: "cgrates.org", ID: "dan", BalanceSummaries: []*BalanceSummary{ { UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", ID: "BALANCE_1", Type: utils.MetaMonetary, Value: 50, Initial: 60, Weight: 20, Disabled: false, Factors: ValueFactors{ "factor2": 2, "factor3": 3, }, }, { UUID: "7a54a9e9-d610-4c82-bcb5-a315b9a65010", ID: "BALANCE_2", Type: utils.MetaMonetary, Value: 25, Initial: 60, Weight: 0, Disabled: false, Factors: ValueFactors{ "factor2": 2, }, }, { UUID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", ID: "BALANCE_3", Type: utils.MetaVoice, Value: 200, Initial: 250, Weight: 10, Disabled: true, Factors: ValueFactors{ "factor4": 4, "factor5": 5, }, }, }, AllowNegative: false, Disabled: false, }, Rating: Rating{ "3cd6425": &RatingUnit{ ConnectFee: 0.4, RoundingMethod: "*up", RoundingDecimals: 5, MaxCostStrategy: utils.MetaMaxCostDisconnect, TimingID: "2f324ab", RatesID: "4910ecf", RatingFiltersID: "23e77dc", }, "21a5ab9": &RatingUnit{ ConnectFee: 0.1, RoundingMethod: "*up", RoundingDecimals: 5, MaxCostStrategy: utils.MetaMaxCostDisconnect, TimingID: "2f324ab", RatesID: "2c1a177", RatingFiltersID: "23e77dc", }, }, Accounting: Accounting{ "2012888": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", Units: 0.01, RatingID: "21a5ab9", }, "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, }, "44d6c02": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", RatingID: "3cd6425", Units: 1, ExtraChargeID: "288bfa6", }, "3455b83": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", 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{ &RGRate{ GroupIntervalStart: 0, Value: 0.01, RateIncrement: time.Minute, RateUnit: time.Second}, }, "4910ecf": RateGroups{ &RGRate{ GroupIntervalStart: 0, Value: 0.005, RateIncrement: time.Second, RateUnit: time.Second}, &RGRate{ GroupIntervalStart: 60 * time.Second, Value: 0.005, RateIncrement: time.Second, RateUnit: time.Second}, }, }, Timings: ChargedTimings{ "2f324ab": &ChargedTiming{ StartTime: "00:00:00", }, }, cache: utils.NewSecureMapStorage(), } ec.SyncKeys(refEC) if !reflect.DeepEqual(eEC, ec) { t.Errorf("expecting: %s \nreceived: %s", utils.ToJSON(eEC), utils.ToJSON(ec)) } } func TestECAsDataProvider(t *testing.T) { ecDP := config.NewObjectDP(testEC) if data, err := ecDP.FieldAsInterface([]string{"RunID"}); err != nil { t.Error(err) } else if data != utils.MetaDefault { t.Errorf("Expecting: <%s> \nreceived: <%s>", utils.MetaDefault, data) } if data, err := ecDP.FieldAsInterface([]string{"AccountSummary", "ID"}); err != nil { t.Error(err) } else if data != "dan" { t.Errorf("Expecting: <%s> \nreceived: <%s>", "data", data) } if data, err := ecDP.FieldAsInterface([]string{"AccountSummary", "BalanceSummaries[1]", "UUID"}); err != nil { t.Error(err) } else if data != "7a54a9e9-d610-4c82-bcb5-a315b9a65010" { t.Errorf("Expecting: <%s> \nreceived: <%s>", "4b8b53d7-c1a1-4159-b845-4623a00a0165", data) } if data, err := ecDP.FieldAsInterface([]string{"AccountSummary", "BalanceSummaries[2]", "Type"}); err != nil { t.Error(err) } else if data != "*voice" { t.Errorf("Expecting: <%s> \nreceived: <%s>", "*voice", data) } } func TestInitCache(t *testing.T) { eventCost := &EventCost{} eventCost.initCache() eOut := utils.NewSecureMapStorage() if !reflect.DeepEqual(eOut, eventCost.cache) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(eOut), utils.ToJSON(eventCost.cache)) } } func TestEventCostFieldAsInterface(t *testing.T) { eventCost := &EventCost{} eventCost.initCache() // item found in cache eventCost.cache = utils.NewSecureMapStorage() eventCost.cache.Set([]string{"test"}, nil) if rcv, err := eventCost.FieldAsInterface([]string{"test"}); err == nil || err != utils.ErrNotFound { t.Errorf("Expecting: nil, received: %+v", err) } else if rcv != nil { t.Errorf("Expecting: nil, received: %+v", rcv) } // data found in cache eventCost.cache.Set([]string{"test"}, "test") if rcv, err := eventCost.FieldAsInterface([]string{"test"}); err != nil { t.Errorf("Expecting: nil, received: %+v", err) } else if rcv != "test" { t.Errorf("Expecting: nil, received: %+v", rcv) } } func TestEventCostfieldAsInterface(t *testing.T) { eventCost := &EventCost{} // default case if rcv, err := eventCost.fieldAsInterface([]string{utils.EmptyString}); err == nil || err.Error() != "unsupported field prefix: <>" { t.Error(err) } else if rcv != nil { t.Errorf("Expecting: nil, received: %+v", rcv) } // case utils.Charges: if rcv, err := eventCost.fieldAsInterface([]string{utils.Charges, utils.Charges}); err == nil || err != utils.ErrNotFound { t.Errorf("Expecting: %+v, received: %+v", utils.ErrNotFound, err) } else if rcv != nil { t.Errorf("Expecting: nil, received: %+v", rcv) } eventCost = &EventCost{ Charges: []*ChargingInterval{ { RatingID: "RatingID1", }, { RatingID: "RatingID2", }, }, } expectedCharges := []*ChargingInterval{ { RatingID: "RatingID1", }, { RatingID: "RatingID2", }, } if rcv, err := eventCost.fieldAsInterface([]string{utils.Charges}); err != nil { t.Error(err) } else if !reflect.DeepEqual(expectedCharges, rcv) { t.Errorf("Expecting: %+v, received: %+v", expectedCharges, rcv) } // case utils.CGRID: if rcv, err := eventCost.fieldAsInterface([]string{utils.CGRID, utils.CGRID}); err == nil || err != utils.ErrNotFound { t.Errorf("Expecting: %+v, received: %+v", utils.ErrNotFound, err) } else if rcv != nil { t.Errorf("Expecting: nil, received: %+v", rcv) } eventCost = &EventCost{ CGRID: "CGRID", } if rcv, err := eventCost.fieldAsInterface([]string{utils.CGRID}); err != nil { t.Error(err) } else if !reflect.DeepEqual("CGRID", rcv) { t.Errorf("Expecting: \"CGRID\", received: %+v", rcv) } // case utils.RunID: if rcv, err := eventCost.fieldAsInterface([]string{utils.RunID, utils.RunID}); err == nil || err != utils.ErrNotFound { t.Errorf("Expecting: %+v, received: %+v", utils.ErrNotFound, err) } else if rcv != nil { t.Errorf("Expecting: nil, received: %+v", rcv) } eventCost = &EventCost{ RunID: "RunID", } if rcv, err := eventCost.fieldAsInterface([]string{utils.RunID}); err != nil { t.Error(err) } else if !reflect.DeepEqual("RunID", rcv) { t.Errorf("Expecting: \"RunID\", received: %+v", rcv) } // case utils.StartTime: if rcv, err := eventCost.fieldAsInterface([]string{utils.StartTime, utils.StartTime}); err == nil || err != utils.ErrNotFound { t.Errorf("Expecting: %+v, received: %+v", utils.ErrNotFound, err) } else if rcv != nil { t.Errorf("Expecting: nil, received: %+v", rcv) } eventCost = &EventCost{ StartTime: time.Date(2020, time.April, 18, 23, 0, 4, 0, time.UTC), } expectedStartTime := time.Date(2020, time.April, 18, 23, 0, 4, 0, time.UTC) if rcv, err := eventCost.fieldAsInterface([]string{utils.StartTime}); err != nil { t.Error(err) } else if !reflect.DeepEqual(expectedStartTime, rcv) { t.Errorf("Expecting: %+v, received: %+v", expectedStartTime, rcv) } // case utils.Usage: if rcv, err := eventCost.fieldAsInterface([]string{utils.Usage, utils.Usage}); err == nil || err != utils.ErrNotFound { t.Errorf("Expecting: %+v, received: %+v", utils.ErrNotFound, err) } else if rcv != nil { t.Errorf("Expecting: nil, received: %+v", rcv) } eventCost = &EventCost{ Usage: utils.DurationPointer(5 * time.Minute), } if rcv, err := eventCost.fieldAsInterface([]string{utils.Usage}); err != nil { t.Error(err) } else if !reflect.DeepEqual(5*time.Minute, rcv) { t.Errorf("Expecting: %+v, received: %+v", 5*time.Minute, rcv) } // case utils.Cost: if rcv, err := eventCost.fieldAsInterface([]string{utils.Cost, utils.Cost}); err == nil || err != utils.ErrNotFound { t.Errorf("Expecting: %+v, received: %+v", utils.ErrNotFound, err) } else if rcv != nil { t.Errorf("Expecting: nil, received: %+v", rcv) } eventCost = &EventCost{ Cost: utils.Float64Pointer(0.7), } if rcv, err := eventCost.fieldAsInterface([]string{utils.Cost}); err != nil { t.Error(err) } else if !reflect.DeepEqual(0.7, rcv) { t.Errorf("Expecting: %+v, received: %+v", 0.7, rcv) } // case utils.AccountSummary: eventCost = &EventCost{ AccountSummary: &AccountSummary{ ID: "IDtest", }, } expectedAccountSummary := &AccountSummary{ID: "IDtest"} if rcv, err := eventCost.fieldAsInterface([]string{utils.AccountSummary}); err != nil { t.Error(err) } else if !reflect.DeepEqual(expectedAccountSummary, rcv) { t.Errorf("Expecting: %+v, received: %+v", expectedAccountSummary, rcv) } eventCost = &EventCost{ AccountSummary: &AccountSummary{ ID: "IDtest1", Tenant: "Tenant", }, CGRID: "test", } if rcv, err := eventCost.fieldAsInterface([]string{utils.AccountSummary, utils.Tenant}); err != nil { t.Error(err) } else if !reflect.DeepEqual("Tenant", rcv) { t.Errorf("Expecting: Tenant, received: %+v", utils.ToJSON(rcv)) } // case utils.Timings: eventCost = &EventCost{ Timings: ChargedTimings{ "test1": &ChargedTiming{ StartTime: "StartTime", }, }, } eTimings := ChargedTimings{"test1": &ChargedTiming{StartTime: "StartTime"}} if rcv, err := eventCost.fieldAsInterface([]string{utils.Timings}); err != nil { t.Error(err) } else if !reflect.DeepEqual(eTimings, rcv) { t.Errorf("Expecting: %+v, received: %+v", eTimings, rcv) } eChargedTiming := &ChargedTiming{ StartTime: "StartTime", } if rcv, err := eventCost.fieldAsInterface([]string{utils.Timings, "test1"}); err != nil { t.Error(err) } else if !reflect.DeepEqual(eChargedTiming, rcv) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(eChargedTiming), utils.ToJSON(rcv)) } // case utils.Rates: eventCost = &EventCost{ Rates: ChargedRates{ "test1": RateGroups{ &RGRate{Value: 0.7}, }, }, } eChargedRates := ChargedRates{ "test1": RateGroups{ &RGRate{Value: 0.7}, }, } if rcv, err := eventCost.fieldAsInterface([]string{utils.Rates}); err != nil { t.Error(err) } else if !reflect.DeepEqual(eChargedRates, rcv) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(eChargedRates), utils.ToJSON(rcv)) } eRateGroup := RateGroups{ &RGRate{Value: 0.7}, } if rcv, err := eventCost.fieldAsInterface([]string{utils.Rates, "test1"}); err != nil { t.Error(err) } else if !reflect.DeepEqual(eRateGroup, rcv) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(eRateGroup), utils.ToJSON(rcv)) } // case utils.RatingFilters: eventCost = &EventCost{ RatingFilters: RatingFilters{ "test1": RatingMatchedFilters{ AccountActionsCSVContent: "AccountActionsCSVContent", }, }, } eRatingFilters := RatingFilters{ "test1": RatingMatchedFilters{ AccountActionsCSVContent: "AccountActionsCSVContent", }, } if rcv, err := eventCost.fieldAsInterface([]string{utils.RatingFilters}); err != nil { t.Error(err) } else if !reflect.DeepEqual(eRatingFilters, rcv) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(eRatingFilters), utils.ToJSON(rcv)) } eRatingMatchedFilters := RatingMatchedFilters{ AccountActionsCSVContent: "AccountActionsCSVContent", } if rcv, err := eventCost.fieldAsInterface([]string{utils.RatingFilters, "test1"}); err != nil { t.Error(err) } else if !reflect.DeepEqual(eRatingMatchedFilters, rcv) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(eRatingMatchedFilters), utils.ToJSON(rcv)) } // case utils.Accounting: eventCost = &EventCost{ Accounting: Accounting{ "test1": &BalanceCharge{ AccountID: "AccountID", }, }, } eAccounting := Accounting{ "test1": &BalanceCharge{ AccountID: "AccountID", }, } if rcv, err := eventCost.fieldAsInterface([]string{utils.Accounting}); err != nil { t.Error(err) } else if !reflect.DeepEqual(eAccounting, rcv) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(eAccounting), utils.ToJSON(rcv)) } eBalanceCharge := &BalanceCharge{ AccountID: "AccountID", } if rcv, err := eventCost.fieldAsInterface([]string{utils.Accounting, "test1"}); err != nil { t.Error(err) } else if !reflect.DeepEqual(eBalanceCharge, rcv) { t.Errorf("Expecting: %+v, \nreceived: %+v", utils.ToJSON(eBalanceCharge), utils.ToJSON(rcv)) } // case utils.Rating: eventCost = &EventCost{ Rating: Rating{ "test1": &RatingUnit{ ConnectFee: 0.7, }, }, } eRating := Rating{ "test1": &RatingUnit{ ConnectFee: 0.7, }, } if rcv, err := eventCost.fieldAsInterface([]string{utils.Rating}); err != nil { t.Error(err) } else if !reflect.DeepEqual(eRating, rcv) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(eRating), utils.ToJSON(rcv)) } eRateUnit := &RatingUnit{ ConnectFee: 0.7, } if rcv, err := eventCost.fieldAsInterface([]string{utils.Rating, "test1"}); err != nil { t.Error(err) } else if !reflect.DeepEqual(eRateUnit, rcv) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(eRateUnit), utils.ToJSON(rcv)) } //default case, utils.Charges eventCost = &EventCost{ Charges: []*ChargingInterval{ { RatingID: "RatingID", }, }, } eCharges := []*ChargingInterval{{RatingID: "RatingID"}} if rcv, err := eventCost.fieldAsInterface([]string{utils.Charges}); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCharges, rcv) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(eCharges), utils.ToJSON(rcv)) } if rcv, err := eventCost.fieldAsInterface([]string{utils.Charges + "[0]"}); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCharges[0], rcv) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(eCharges[0]), utils.ToJSON(rcv)) } if rcv, err := eventCost.fieldAsInterface([]string{utils.Charges + "[1]"}); err == nil || err != utils.ErrNotFound { t.Error(err) } else if rcv != nil { t.Errorf("Expecting: nil, received: %+v", rcv) } } func TestEventCostgetChargesForPath(t *testing.T) { eventCost := &EventCost{} chargingInterval := &ChargingInterval{ RatingID: "RatingID", } // chr == nil if rcv, err := eventCost.getChargesForPath(nil, nil); err == nil || err != utils.ErrNotFound { t.Errorf("Expecting: %+v, received: %+v", utils.ErrNotFound, err) } else if rcv != nil { t.Errorf("Expecting: nil, received: %+v", rcv) } // len(fldPath) == 0 eChargingInterval := &ChargingInterval{ RatingID: "RatingID", } if rcv, err := eventCost.getChargesForPath([]string{}, chargingInterval); err != nil { t.Errorf("Expecting: nil, received: %+v", err) } else if !reflect.DeepEqual(eChargingInterval, rcv) { t.Errorf("Expecting: %+v, received: %+v", eChargingInterval, rcv) } // fldPath[0] == utils.CompressFactor chargingInterval = &ChargingInterval{ RatingID: "RatingID", CompressFactor: 7, } if rcv, err := eventCost.getChargesForPath([]string{utils.CompressFactor}, chargingInterval); err != nil { t.Errorf("Expecting: nil, received: %+v", err) } else if rcv != 7 { t.Errorf("Expecting: 7, received: %+v", rcv) } if rcv, err := eventCost.getChargesForPath([]string{utils.CompressFactor, "mustFail"}, chargingInterval); err == nil || err != utils.ErrNotFound { t.Errorf("Expecting: %+v, received: %+v", utils.ErrNotFound, err) } else if rcv != nil { t.Errorf("Expecting: nil, received: %+v", rcv) } // fldPath[0] == utils.Rating eventCost = &EventCost{ Rates: ChargedRates{ "RatingID": RateGroups{&RGRate{Value: 0.8}}}, } if rcv, err := eventCost.getChargesForPath([]string{utils.Rating}, chargingInterval); err == nil || err != utils.ErrNotFound { t.Errorf("Expecting: %+v, received: %+v", utils.ErrNotFound, err) } else if rcv != nil { t.Errorf("Expecting: nil, received: %+v", rcv) } eventCost = &EventCost{ Rating: Rating{"RatingID": &RatingUnit{}}, Rates: ChargedRates{ "RatingID": RateGroups{&RGRate{Value: 0.8}}}, } if rcv, err := eventCost.getChargesForPath([]string{utils.Rating, "unsupportedfield"}, chargingInterval); err == nil || err.Error() != "unsupported field prefix: " { t.Errorf("Expecting: unsupported field prefix: , received: %+v", err) } else if rcv != nil { t.Errorf("Expecting: nil, received: %+v", rcv) } chargingInterval = &ChargingInterval{ RatingID: "RatingID", CompressFactor: 7, } eventCost = &EventCost{ Rating: Rating{"RatingID": &RatingUnit{ RatesID: "RatesID", }}, Rates: ChargedRates{"RatesID": RateGroups{&RGRate{Value: 0.8}}}, } RateGroups := RateGroups{&RGRate{Value: 0.8}} if rcv, err := eventCost.getChargesForPath([]string{utils.Rating, utils.Rates}, chargingInterval); err != nil { t.Error(err) } else if !reflect.DeepEqual(RateGroups, rcv) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(RateGroups), utils.ToJSON(rcv)) t.Errorf("Expecting: %+v, received: %+v", RateGroups, rcv) } // opath != utils.Increments if rcv, err := eventCost.getChargesForPath([]string{"unsupportedfield"}, chargingInterval); err == nil || err.Error() != "unsupported field prefix: " { t.Errorf("Expecting: unsupported field prefix: , received: %+v", err) } else if rcv != nil { t.Errorf("Expecting: nil, received: %+v", rcv) } // utils.Increments eventCost = &EventCost{} chargingInterval = &ChargingInterval{ Increments: []*ChargingIncrement{ { AccountingID: "AccountingID", }, }, } eChargingIncrement := &ChargingIncrement{ AccountingID: "AccountingID", } if rcv, err := eventCost.getChargesForPath([]string{"Increments[0]"}, chargingInterval); err != nil { t.Errorf("Expecting: nil, received: %+v", err) } else if !reflect.DeepEqual(eChargingIncrement, rcv) { t.Errorf("Expecting: %+v, received: %+v", eChargingIncrement, rcv) } eIncrements := []*ChargingIncrement{ { AccountingID: "AccountingID", }, } // indx == nil if rcv, err := eventCost.getChargesForPath([]string{"Increments"}, chargingInterval); err != nil { t.Errorf("Expecting: nil, received: %+v", err) } else if !reflect.DeepEqual(eIncrements, rcv) { t.Errorf("Expecting: %+v, received: %+v", eIncrements, utils.ToJSON(rcv)) } // len(fldPath) != 1 if rcv, err := eventCost.getChargesForPath([]string{"Increments", utils.Accounting}, chargingInterval); err == nil || err != utils.ErrNotFound { t.Errorf("Expecting: %+v, received: %+v", utils.ErrNotFound, err) } else if rcv != nil { t.Errorf("Expecting: nil, received: %+v", rcv) } // fldPath[1] == utils.Accounting eventCost = &EventCost{ Accounting: Accounting{ "AccountingID": &BalanceCharge{ AccountID: "AccountID", }, }, } eBalanceCharge := &BalanceCharge{AccountID: "AccountID"} if rcv, err := eventCost.getChargesForPath([]string{"Increments[0]", utils.Accounting}, chargingInterval); err != nil { t.Errorf("Expecting: nil, received: %+v", err) } else if !reflect.DeepEqual(eBalanceCharge, rcv) { t.Errorf("Expecting: %+v, received: %+v", eBalanceCharge, rcv) } } func TestEventCostgetRatingForPath(t *testing.T) { eventCost := &EventCost{} ratingUnit := &RatingUnit{} // rating == nil if rcv, err := eventCost.getRatingForPath(nil, nil); err == nil || err != utils.ErrNotFound { t.Errorf("Expecting: %+v, received: %+v", utils.ErrNotFound, err) } else if rcv != nil { t.Errorf("Expecting: nil, received: %+v", rcv) } // len(fldPath) == 0 eratingUnit := &RatingUnit{} if rcv, err := eventCost.getRatingForPath([]string{}, ratingUnit); err != nil { t.Errorf("Expecting: nil, received: %+v", err) } else if !reflect.DeepEqual(eratingUnit, rcv) { t.Errorf("Expecting: %+v, received: %+v", eratingUnit, rcv) } // case utils.Rates: eventCost = &EventCost{ Rates: ChargedRates{ "RatesID": RateGroups{ &RGRate{Value: 0.7}, }, }, } eChargedRates := RateGroups{ &RGRate{Value: 0.7}, } // !has || rts == nil ratingUnit = &RatingUnit{ RatesID: "notfound", } if rcv, err := eventCost.getRatingForPath([]string{utils.Rates}, ratingUnit); err == nil || err != utils.ErrNotFound { t.Errorf("Expecting: %+v, received: %+v", utils.ErrNotFound, err) } else if rcv != nil { t.Errorf("Expecting: nil, received: %+v", rcv) } // len(fldPath) != 1 ratingUnit = &RatingUnit{ RatesID: "RatesID", } if rcv, err := eventCost.getRatingForPath([]string{utils.Rates, utils.Rates}, ratingUnit); err == nil || err != utils.ErrNotFound { t.Errorf("Expecting: %+v, received: %+v", utils.ErrNotFound, err) } else if rcv != nil { t.Errorf("Expecting: nil, received: %+v", rcv) } //normal case if rcv, err := eventCost.getRatingForPath([]string{utils.Rates}, ratingUnit); err != nil { t.Errorf("Expecting: nil, received: %+v", err) } else if !reflect.DeepEqual(eChargedRates, rcv) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(eChargedRates), utils.ToJSON(rcv)) } // case utils.Timing: eventCost = &EventCost{ Timings: ChargedTimings{ "test1": &ChargedTiming{ StartTime: "StartTime", }, }, } eTimings := &ChargedTiming{ StartTime: "StartTime", } // !has || tmg == nil ratingUnit = &RatingUnit{ TimingID: "notfound", } if rcv, err := eventCost.getRatingForPath([]string{utils.Timing}, ratingUnit); err == nil || err != utils.ErrNotFound { t.Errorf("Expecting: %+v, received: %+v", utils.ErrNotFound, err) } else if rcv != nil { t.Errorf("Expecting: nil, received: %+v", rcv) } // len(fldPath) == 1 ratingUnit = &RatingUnit{ TimingID: "test1", } if rcv, err := eventCost.getRatingForPath([]string{utils.Timing}, ratingUnit); err != nil { t.Errorf("Expecting: nil, received: %+v", err) } else if !reflect.DeepEqual(eTimings, rcv) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(eTimings), utils.ToJSON(rcv)) } //normal case eventCost = &EventCost{ Timings: ChargedTimings{ "test1": &ChargedTiming{ Months: utils.Months{time.April}, StartTime: "StartTime", }, }, } eMonths := utils.Months{time.April} if rcv, err := eventCost.getRatingForPath([]string{utils.Timing, utils.MonthsFieldName}, ratingUnit); err != nil { t.Errorf("Expecting: nil, received: %+v", err) } else if !reflect.DeepEqual(eMonths, rcv) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(eMonths), utils.ToJSON(rcv)) } // case utils.RatingFilter: eventCost = &EventCost{ RatingFilters: RatingFilters{ "RatingFilters1": RatingMatchedFilters{ "test1": "test1", }, }, } eRatingMatchedFilters := RatingMatchedFilters{ "test1": "test1", } // !has || tmg == nil ratingUnit = &RatingUnit{ TimingID: "notfound", } if rcv, err := eventCost.getRatingForPath([]string{utils.RatingFilter}, ratingUnit); err == nil || err != utils.ErrNotFound { t.Errorf("Expecting: %+v, received: %+v", utils.ErrNotFound, err) } else if rcv != nil { t.Errorf("Expecting: nil, received: %+v", rcv) } // len(fldPath) == 1 ratingUnit = &RatingUnit{ RatingFiltersID: "RatingFilters1", } if rcv, err := eventCost.getRatingForPath([]string{utils.RatingFilter}, ratingUnit); err != nil { t.Errorf("Expecting: nil, received: %+v", err) } else if !reflect.DeepEqual(eRatingMatchedFilters, rcv) { t.Errorf("Expecting: %+v, received: %+v", eRatingMatchedFilters, rcv) t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(eRatingMatchedFilters), utils.ToJSON(rcv)) } //normal case eventCost = &EventCost{ RatingFilters: RatingFilters{ "RatingFilters1": RatingMatchedFilters{ "test1": "test-1", }, }, } if rcv, err := eventCost.getRatingForPath([]string{utils.RatingFilter, "test1"}, ratingUnit); err != nil { t.Errorf("Expecting: nil, received: %+v", err) } else if !reflect.DeepEqual("test-1", rcv) { t.Errorf("Expecting: test-1, received: %+v", utils.ToJSON(rcv)) } //default case eventCost = &EventCost{ Rates: ChargedRates{ "RatesID": RateGroups{ &RGRate{Value: 0.7}, }, "RatesID2": RateGroups{ &RGRate{Value: 0.7}, }, }, } if rcv, err := eventCost.getRatingForPath([]string{ "unsupportedprefix"}, ratingUnit); err == nil || err.Error() != "unsupported field prefix: " { t.Errorf("Expecting: 'unsupported field prefix: ', received: %+v", err) } else if rcv != nil { t.Errorf("Expecting: nil, received: %+v", utils.ToJSON(rcv)) } ratingUnit = &RatingUnit{ RatesID: "RatesID", } eRate := &RGRate{Value: 0.7} if rcv, err := eventCost.getRatingForPath([]string{"Rates[0]"}, ratingUnit); err != nil { t.Errorf("Expecting: nil, received: %+v", err) } else if !reflect.DeepEqual(eRate, rcv) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(eRate), utils.ToJSON(rcv)) } ratingUnit = &RatingUnit{} if rcv, err := eventCost.getRatingForPath([]string{"Rates[1]"}, ratingUnit); err == nil || err != utils.ErrNotFound { t.Errorf("Expecting: %+v, received: %+v", utils.ErrNotFound, err) } else if rcv != nil { t.Errorf("Expecting: nil, received: %+v", rcv) } } func TestEventCostgetAcountingForPath(t *testing.T) { eventCost := &EventCost{} balanceCharge := &BalanceCharge{} // bc == nil if rcv, err := eventCost.getAcountingForPath(nil, nil); err == nil || err != utils.ErrNotFound { t.Errorf("Expecting: %+v, received: %+v", utils.ErrNotFound, err) } else if rcv != nil { t.Errorf("Expecting: nil, received: %+v", rcv) } // len(fldPath) == 0 eBalanceCharge := &BalanceCharge{} if rcv, err := eventCost.getAcountingForPath([]string{}, balanceCharge); err != nil { t.Errorf("Expecting: nil, received: %+v", err) } else if !reflect.DeepEqual(eBalanceCharge, rcv) { t.Errorf("Expecting: %+v, received: %+v", eBalanceCharge, rcv) } // fldPath[0] == utils.Balance eventCost = &EventCost{ AccountSummary: &AccountSummary{ BalanceSummaries: BalanceSummaries{ { ID: "ID", }, }, }, } eBalanceSummaries := &BalanceSummary{ ID: "ID", } //len(fldPath) == 1 if rcv, err := eventCost.getAcountingForPath([]string{utils.BalanceField}, balanceCharge); err != nil { t.Errorf("Expecting: nil, received: %+v", err) } else if !reflect.DeepEqual(eBalanceSummaries, rcv) { t.Errorf("Expecting: %+v, received: %+v", eBalanceSummaries, rcv) } // bl == nil eventCost = &EventCost{AccountSummary: &AccountSummary{}} if rcv, err := eventCost.getAcountingForPath([]string{utils.BalanceField}, balanceCharge); err == nil || err != utils.ErrNotFound { t.Errorf("Expecting: %+v, received: %+v", utils.ErrNotFound, err) } else if rcv != nil { t.Errorf("Expecting: nil, received: %+v", rcv) } // len(fldPath) != 1 eventCost = &EventCost{ AccountSummary: &AccountSummary{ BalanceSummaries: BalanceSummaries{ { ID: "ID", }, }, }, } if rcv, err := eventCost.getAcountingForPath([]string{utils.BalanceField, "ID"}, balanceCharge); err != nil { t.Errorf("Expecting: nil, received: %+v", err) } else if !reflect.DeepEqual("ID", rcv) { t.Errorf("Expecting: \"ID\", received: %+v", rcv) } // fldPath[0] != utils.Balance balanceCharge = &BalanceCharge{ AccountID: "AccountID", } if rcv, err := eventCost.getAcountingForPath([]string{utils.AccountID}, balanceCharge); err != nil { t.Errorf("Expecting: nil, received: %+v", err) } else if !reflect.DeepEqual("AccountID", rcv) { t.Errorf("Expecting: \"AccountID\", received: %+v", rcv) } } func TestEventCostString(t *testing.T) { eventCost := &EventCost{} eOut := `{"CGRID":"","RunID":"","StartTime":"0001-01-01T00:00:00Z","Usage":null,"Cost":null,"Charges":null,"AccountSummary":null,"Rating":null,"Accounting":null,"RatingFilters":null,"Rates":null,"Timings":null}` if rcv := eventCost.String(); !reflect.DeepEqual(eOut, rcv) { t.Errorf("Expecting: %+v, received: %+v", eOut, rcv) } eventCost = &EventCost{ AccountSummary: &AccountSummary{ BalanceSummaries: BalanceSummaries{ &BalanceSummary{ ID: "ID", }, }, }, } eOut = `{"CGRID":"","RunID":"","StartTime":"0001-01-01T00:00:00Z","Usage":null,"Cost":null,"Charges":null,"AccountSummary":{"Tenant":"","ID":"","BalanceSummaries":[{"UUID":"","ID":"ID","Type":"","Initial":0,"Value":0,"Disabled":false}],"AllowNegative":false,"Disabled":false},"Rating":null,"Accounting":null,"RatingFilters":null,"Rates":null,"Timings":null}` if rcv := eventCost.String(); !reflect.DeepEqual(eOut, rcv) { t.Errorf("Expecting: %+v, received: %+v", eOut, rcv) } } func TestEventCostFieldAsString(t *testing.T) { eventCost := &EventCost{ CGRID: "CGRID", } eventCost.initCache() if rcv, err := eventCost.FieldAsString([]string{utils.CGRID}); err != nil { t.Error(err) } else if rcv != "CGRID" { t.Errorf("Expecting: CGRID, received: %+v", rcv) } if rcv, err := eventCost.FieldAsString([]string{"err"}); err == nil || err.Error() != "unsupported field prefix: " { t.Error(err) } else if !reflect.DeepEqual(rcv, utils.EmptyString) { t.Errorf("Expecting: EmptyString, received: %+v", rcv) } } func TestECAsCallCost3(t *testing.T) { eCC := &CallCost{ Category: "call", Tenant: "cgrates.org", Subject: "dan", Account: "dan", Destination: "+4986517174963", ToR: utils.MetaVoice, Cost: 0.85, RatedUsage: 120.0, Timespans: TimeSpans{ &TimeSpan{ TimeStart: time.Date(2017, 1, 9, 16, 18, 21, 0, time.UTC), TimeEnd: time.Date(2017, 1, 9, 16, 19, 21, 0, time.UTC), Cost: 0.25, RateInterval: &RateInterval{ // standard rating Timing: &RITiming{ StartTime: "00:00:00", }, Rating: &RIRate{ ConnectFee: 0.1, RoundingMethod: "*up", RoundingDecimals: 5, Rates: RateGroups{ &RGRate{ GroupIntervalStart: time.Duration(0), Value: 0.01, RateUnit: time.Duration(1 * time.Second), RateIncrement: time.Duration(1 * time.Minute), }, }, }, }, DurationIndex: time.Duration(1 * time.Minute), MatchedSubject: "*out:cgrates.org:call:*any", MatchedPrefix: "+49", MatchedDestId: "GERMANY", RatingPlanId: "RPL_RETAIL1", CompressFactor: 1, Increments: Increments{ &Increment{ // ConnectFee Cost: 0.1, BalanceInfo: &DebitInfo{ Monetary: &MonetaryInfo{UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", ID: utils.MetaDefault, Value: 9.9}, AccountID: "cgrates.org:dan", }, CompressFactor: 1, }, &Increment{ // First 30 seconds free Duration: time.Duration(1 * time.Second), Cost: 0, BalanceInfo: &DebitInfo{ Unit: &UnitInfo{ UUID: "9d54a9e9-d610-4c82-bcb5-a315b9a65089", ID: "free_mins", Value: 0, Consumed: 1.0, ToR: utils.MetaVoice, }, AccountID: "cgrates.org:dan", }, CompressFactor: 30, }, &Increment{ // Minutes with special price Duration: time.Duration(1 * time.Second), Cost: 0.005, BalanceInfo: &DebitInfo{ Unit: &UnitInfo{ // Minutes with special price UUID: "7a54a9e9-d610-4c82-bcb5-a315b9a65010", ID: "discounted_mins", Value: 0, Consumed: 1.0, ToR: utils.MetaVoice, RateInterval: &RateInterval{ Timing: &RITiming{ StartTime: "00:00:00", }, Rating: &RIRate{ ConnectFee: 0, RoundingMethod: "*up", RoundingDecimals: 5, Rates: RateGroups{ &RGRate{ GroupIntervalStart: time.Duration(0), Value: 0.005, RateUnit: time.Duration(1 * time.Second), RateIncrement: time.Duration(1 * time.Second), }, &RGRate{ GroupIntervalStart: time.Duration(60 * time.Second), Value: 0.005, RateUnit: time.Duration(1 * time.Second), RateIncrement: time.Duration(1 * time.Second), }, }, }, }, }, Monetary: &MonetaryInfo{ UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", ID: utils.MetaDefault, Value: 9.75}, AccountID: "cgrates.org:dan", }, CompressFactor: 30, }, }, }, &TimeSpan{ TimeStart: time.Date(2017, 1, 9, 16, 19, 21, 0, time.UTC), TimeEnd: time.Date(2017, 1, 9, 16, 20, 21, 0, time.UTC), Cost: 0.6, RateInterval: &RateInterval{ // standard rating Timing: &RITiming{ StartTime: "00:00:00", }, Rating: &RIRate{ ConnectFee: 0.1, RoundingMethod: "*up", RoundingDecimals: 5, Rates: RateGroups{ &RGRate{ GroupIntervalStart: time.Duration(0), Value: 0.01, RateUnit: time.Duration(1 * time.Second), RateIncrement: time.Duration(1 * time.Minute), }, }, }, }, DurationIndex: time.Duration(1 * time.Minute), MatchedSubject: "*out:cgrates.org:call:*any", MatchedPrefix: "+49", MatchedDestId: "GERMANY", RatingPlanId: "RPL_RETAIL1", CompressFactor: 1, Increments: Increments{ &Increment{ Cost: 0.01, Duration: time.Duration(1 * time.Second), BalanceInfo: &DebitInfo{ Monetary: &MonetaryInfo{UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", ID: utils.MetaDefault, Value: 9.15}, AccountID: "cgrates.org:dan", }, CompressFactor: 60, }, }, }, }, AccountSummary: &AccountSummary{ Tenant: "cgrates.org", ID: "dan", BalanceSummaries: []*BalanceSummary{ { Type: utils.MetaMonetary, Value: 50, Disabled: false}, { ID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", Type: utils.MetaMonetary, Value: 25, Disabled: false}, { Type: utils.MetaVoice, Value: 200, Disabled: false, }, }, AllowNegative: false, Disabled: false, }, } eEC := &EventCost{ CGRID: "164b0422fdc6a5117031b427439482c6a4f90e41", RunID: utils.MetaDefault, StartTime: time.Date(2017, 1, 9, 16, 18, 21, 0, time.UTC), Charges: []*ChargingInterval{ { RatingID: "21a5ab9", Increments: []*ChargingIncrement{ { Usage: time.Duration(0), Cost: 0.1, AccountingID: "9bdad10", CompressFactor: 1, }, { Usage: time.Duration(1 * time.Second), Cost: 0, AccountingID: "3455b83", CompressFactor: 10, }, { Usage: time.Duration(10 * time.Second), Cost: 0.01, AccountingID: "2012888", CompressFactor: 2, }, { Usage: time.Duration(1 * time.Second), Cost: 0.005, AccountingID: "44d6c02", CompressFactor: 30, }, }, CompressFactor: 1, }, { RatingID: "21a5ab9", Increments: []*ChargingIncrement{ { Usage: time.Duration(1 * time.Second), Cost: 0.01, AccountingID: "2012888", CompressFactor: 60, }, }, CompressFactor: 4, }, { RatingID: "21a5ab9", Increments: []*ChargingIncrement{ { Usage: time.Duration(1 * time.Second), Cost: 0, AccountingID: "3455b83", CompressFactor: 10, }, { Usage: time.Duration(10 * time.Second), Cost: 0.01, AccountingID: "2012888", CompressFactor: 2, }, { Usage: time.Duration(1 * time.Second), Cost: 0.005, AccountingID: "44d6c02", CompressFactor: 30, }, }, CompressFactor: 5, }, }, AccountSummary: &AccountSummary{ Tenant: "cgrates.org", ID: "dan", BalanceSummaries: []*BalanceSummary{ { UUID: "8c54a9e9-d610-4c82-bcb5-a315b9a65010", Type: utils.MetaMonetary, Value: 50, Disabled: false}, { UUID: "7a54a9e9-d610-4c82-bcb5-a315b9a65010", Type: utils.MetaMonetary, Value: 25, Disabled: false}, { UUID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", Type: utils.MetaVoice, 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, RatingID: "21a5ab9", }, "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, }, "44d6c02": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", RatingID: "3cd6425", Units: 1, ExtraChargeID: "288bfa6", }, "3455b83": &BalanceCharge{ AccountID: "cgrates.org:dan", BalanceUUID: "4b8b53d7-c1a1-4159-b845-4623a00a0165", 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{ &RGRate{ GroupIntervalStart: time.Duration(0), Value: 0.01, RateIncrement: time.Duration(1 * time.Minute), RateUnit: time.Duration(1 * time.Second)}, }, "4910ecf": RateGroups{ &RGRate{ GroupIntervalStart: time.Duration(0), Value: 0.005, RateIncrement: time.Duration(1 * time.Second), RateUnit: time.Duration(1 * time.Second)}, &RGRate{ 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", }, }, cache: utils.NewSecureMapStorage(), } ec := NewEventCostFromCallCost(eCC, "cgrID", utils.MetaDefault) eEC.SyncKeys(ec) ec.Merge(eEC) if _, err := ec.Trim(time.Second); err != nil { t.Fatal(err) } } func TestECAsDataProvider2(t *testing.T) { ecDP := testEC if data, err := ecDP.FieldAsInterface([]string{"RunID"}); err != nil { t.Error(err) } else if data != utils.MetaDefault { t.Errorf("Expecting: <%s> \nreceived: <%s>", utils.MetaDefault, data) } if data, err := ecDP.FieldAsInterface([]string{"AccountSummary", "ID"}); err != nil { t.Error(err) } else if data != "dan" { t.Errorf("Expecting: <%s> \nreceived: <%s>", "data", data) } if data, err := ecDP.FieldAsInterface([]string{"AccountSummary", "BalanceSummaries[1]", "UUID"}); err != nil { t.Error(err) } else if data != "7a54a9e9-d610-4c82-bcb5-a315b9a65010" { t.Errorf("Expecting: <%s> \nreceived: <%s>", "4b8b53d7-c1a1-4159-b845-4623a00a0165", data) } if data, err := ecDP.FieldAsInterface([]string{"AccountSummary", "BalanceSummaries[2]", "Type"}); err != nil { t.Error(err) } else if data != "*voice" { t.Errorf("Expecting: <%s> \nreceived: <%s>", "*voice", data) } ecDP = &EventCost{AccountSummary: &AccountSummary{}} ecDP.initCache() if _, err := ecDP.FieldAsInterface([]string{"AccountSummary", "BalanceSummaries[0]", "Type"}); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Errorf("Unexpected error:%v", err) } if _, err := ecDP.FieldAsInterface([]string{"Charges[0]", "Increments"}); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Errorf("Unexpected error:%v", err) } ecDP.Charges = []*ChargingInterval{{}} if _, err := ecDP.FieldAsInterface([]string{"Charges[0]", "Increments[0]"}); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Errorf("Unexpected error:%v", err) } ecDP.Rating = Rating{"": {}} ecDP.Rates = ChargedRates{"": {}, "b": {}} if _, err := ecDP.FieldAsInterface([]string{"Charges[0]", "Rating", "Rates[0]"}); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Errorf("Unexpected error:%v", err) } if _, err := ecDP.FieldAsInterface([]string{"Rates", "b[0]"}); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Errorf("Unexpected error:%v", err) } } func TestECAsDataProvider3(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: "Initial", fields: []string{"Charges[0]", "Increments[3]", "Accounting", "Balance", "Initial"}, exp: "250", }, { name: "Disabled", fields: []string{"Charges[0]", "Increments[3]", "Accounting", "Balance", "Disabled"}, exp: "true", }, { name: "Weight", fields: []string{"Charges[0]", "Increments[3]", "Accounting", "Balance", "Weight"}, exp: "10", }, { name: "Factors map", fields: []string{"Charges[0]", "Increments[3]", "Accounting", "Balance", "Factors"}, exp: `{"factor4":4,"factor5":5}`, }, { name: "Factors value", fields: []string{"Charges[0]", "Increments[3]", "Accounting", "Balance", "Factors", "factor4"}, exp: "4", }, { 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: "BalanceFactor", fields: []string{"Charges[0]", "Increments[1]", "Accounting", "BalanceFactor"}, exp: "1", }, { 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.4", }, { name: "RoundingMethod through Accounting", fields: []string{"Charges[0]", "Increments[3]", "Accounting", "Rating", "RoundingMethod"}, exp: utils.MetaRoundingUp, }, { 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.MetaMaxCostDisconnect, }, { 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.MetaRoundingUp, }, { 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.MetaMaxCostDisconnect, }, { 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.MetaMonetary, }, { 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: "Initial through ExtraCharge", fields: []string{"Charges[0]", "Increments[3]", "Accounting", "ExtraCharge", "Balance", "Initial"}, exp: "60", }, { name: "Disabled through ExtraCharge", fields: []string{"Charges[0]", "Increments[3]", "Accounting", "ExtraCharge", "Balance", "Disabled"}, exp: "false", }, { name: "Weight through ExtraCharge", fields: []string{"Charges[0]", "Increments[3]", "Accounting", "ExtraCharge", "Balance", "Weight"}, exp: "20", }, { name: "Factors map through ExtraCharge", fields: []string{"Charges[0]", "Increments[3]", "Accounting", "ExtraCharge", "Balance", "Factors"}, exp: `{"factor2":2,"factor3":3}`, }, { name: "Factors value through ExtraCharge", fields: []string{"Charges[0]", "Increments[3]", "Accounting", "ExtraCharge", "Balance", "Factors", "factor3"}, exp: "3", }, { 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", }, { name: "BalanceFactor through ExtraCharge", fields: []string{"Charges[0]", "Increments[3]", "Accounting", "ExtraCharge", "BalanceFactor"}, exp: "1", }, // ~*ec.Charges[0].Increments[3].Accounting.Rating.ConnectFee // ~*ec.Charges[0].Rating.ConnectFee // ~*ec.Charges[0].Increments[0].Accounting.Rating.RatingFilter.DestinationID } 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") if err != nil { t.Fatal(err) } nM := cdr.AsMapStorage() if _, err := nM.FieldAsInterface([]string{"*ec", "Charges[0]", "Increments[0]", "Accounting", "Balance", "ID"}); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Fatalf("Expected error:%s, received: %v", utils.ErrNotFound.Error(), err) } if _, err := nM.FieldAsInterface([]string{"*req", "CostDetails", "Charges[0]", "Increments[0]", "Accounting", "Balance", "ID"}); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Fatalf("Expected error:%s, received: %v", utils.ErrNotFound.Error(), err) } } func TestECnewChargingIncrementMissingBalanceInfo(t *testing.T) { ec := &EventCost{} incr := &Increment{} rf := make(RatingMatchedFilters) exp := &ChargingIncrement{ Usage: incr.Duration, Cost: incr.Cost, CompressFactor: incr.CompressFactor, } rcv := ec.newChargingIncrement(incr, rf, false, false) if !reflect.DeepEqual(rcv, exp) { t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", exp, rcv) } } func TestECnewChargingIncrementWithUnitInfo(t *testing.T) { ec := &EventCost{ Accounting: Accounting{}, } incr := &Increment{ BalanceInfo: &DebitInfo{ Unit: &UnitInfo{}, Monetary: &MonetaryInfo{}, }, } rf := make(RatingMatchedFilters) exp := &ChargingIncrement{ Usage: incr.Duration, Cost: incr.Cost, CompressFactor: incr.CompressFactor, AccountingID: utils.MetaPause, } rcv := ec.newChargingIncrement(incr, rf, false, true) if !reflect.DeepEqual(rcv, exp) { t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", exp, rcv) } } func TestECnewChargingIncrementNoUnitInfo(t *testing.T) { ec := &EventCost{ Accounting: Accounting{}, } incr := &Increment{ BalanceInfo: &DebitInfo{ Monetary: &MonetaryInfo{}, }, } rf := make(RatingMatchedFilters) exp := &ChargingIncrement{ Usage: incr.Duration, Cost: incr.Cost, CompressFactor: incr.CompressFactor, AccountingID: utils.MetaPause, } expEC := &EventCost{ Accounting: Accounting{ utils.MetaPause: &BalanceCharge{}, }, } rcv := ec.newChargingIncrement(incr, rf, false, true) if !reflect.DeepEqual(rcv, exp) { t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", exp, rcv) } if !reflect.DeepEqual(ec, expEC) { t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", expEC, ec) } } func TestECAsRefundIncrementsNoCharges(t *testing.T) { ec := &EventCost{ Charges: []*ChargingInterval{}, CGRID: "asdfgh", RunID: "runID", StartTime: time.Date(2021, 4, 13, 17, 0, 0, 0, time.UTC), Usage: utils.DurationPointer(time.Hour), Cost: utils.Float64Pointer(10), } exp := &CallDescriptor{ CgrID: "asdfgh", RunID: "runID", ToR: utils.MetaVoice, TimeStart: time.Date(2021, 4, 13, 17, 0, 0, 0, time.UTC), TimeEnd: time.Date(2021, 4, 13, 18, 0, 0, 0, time.UTC), DurationIndex: time.Hour, } rcv := ec.AsRefundIncrements(utils.MetaVoice) if !reflect.DeepEqual(rcv, exp) { t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", exp, rcv) } } func TestECAsRefundIncrements2(t *testing.T) { ec := &EventCost{ Charges: []*ChargingInterval{ { cost: utils.Float64Pointer(10), CompressFactor: 1, Increments: []*ChargingIncrement{ { Cost: 5, Usage: 2 * time.Second, AccountingID: "accID", CompressFactor: 1, }, }, }, }, CGRID: "asdfgh", RunID: "runID", StartTime: time.Date(2021, 4, 13, 17, 0, 0, 0, time.UTC), Usage: utils.DurationPointer(time.Hour), Cost: utils.Float64Pointer(10), Accounting: Accounting{ "accID": &BalanceCharge{ AccountID: "bcAccID", BalanceUUID: "bcUUID", ExtraChargeID: "extrachargeID", }, "extrachargeID": &BalanceCharge{ AccountID: "extraAccID", BalanceUUID: "extraBcUUID", }, }, AccountSummary: &AccountSummary{ BalanceSummaries: BalanceSummaries{ { UUID: "bcUUID", Type: utils.MetaSMS, }, { UUID: "extraBcUUID", Type: utils.MetaSMS, }, }, }, } exp := &CallDescriptor{ CgrID: "asdfgh", RunID: "runID", ToR: utils.MetaVoice, TimeStart: time.Date(2021, 4, 13, 17, 0, 0, 0, time.UTC), TimeEnd: time.Date(2021, 4, 13, 18, 0, 0, 0, time.UTC), DurationIndex: time.Hour, Increments: Increments{ { Duration: 2 * time.Second, Cost: 5, BalanceInfo: &DebitInfo{ Unit: &UnitInfo{ UUID: "extraBcUUID", Factor: 1, }, AccountID: "bcAccID", }, CompressFactor: 1, }, }, } rcv := ec.AsRefundIncrements(utils.MetaVoice) if !reflect.DeepEqual(rcv, exp) { t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", exp, rcv) } } func TestECratingGetIDFromEventCostPause(t *testing.T) { ec := &EventCost{ Timings: ChargedTimings{ utils.MetaPause: &ChargedTiming{}, }, RatingFilters: RatingFilters{ utils.MetaPause: RatingMatchedFilters{}, }, Rates: ChargedRates{ utils.MetaPause: RateGroups{}, }, Rating: Rating{ utils.MetaPause: &RatingUnit{}, }, } oEC := &EventCost{ Rating: Rating{ utils.MetaPause: &RatingUnit{ ConnectFee: 0.4, RoundingMethod: "*up", RoundingDecimals: 4, MaxCost: 100, MaxCostStrategy: "*disconnect", TimingID: "TM_NOON", }, }, Timings: ChargedTimings{ utils.MetaPause: &ChargedTiming{ Years: utils.Years{2010, 2011}, Months: utils.Months{1, 2}, MonthDays: utils.MonthDays{24, 25}, WeekDays: utils.WeekDays{2}, StartTime: "00:00:00", }, }, } oRatingID := utils.MetaPause exp := utils.MetaPause rcv := ec.ratingGetIDFromEventCost(oEC, oRatingID) if rcv != exp { t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", exp, rcv) } } func TestECaccountingGetIDFromEventCostPause(t *testing.T) { ec := &EventCost{ Timings: ChargedTimings{ utils.MetaPause: &ChargedTiming{}, }, RatingFilters: RatingFilters{ utils.MetaPause: RatingMatchedFilters{}, }, Rates: ChargedRates{ utils.MetaPause: RateGroups{}, }, Rating: Rating{ utils.MetaPause: &RatingUnit{}, }, Accounting: Accounting{ utils.MetaPause: &BalanceCharge{}, }, } oEC := &EventCost{ Accounting: Accounting{ utils.MetaPause: &BalanceCharge{ AccountID: "1001", BalanceUUID: "asdfg", RatingID: utils.MetaPause, Units: 10, ExtraChargeID: "extra", }, }, Rating: Rating{ utils.MetaPause: &RatingUnit{ ConnectFee: 0.4, RoundingMethod: "*up", RoundingDecimals: 4, MaxCost: 100, MaxCostStrategy: "*disconnect", TimingID: "TM_NOON", }, }, } oAccountingID := utils.MetaPause exp := utils.MetaPause rcv := ec.accountingGetIDFromEventCost(oEC, oAccountingID) if rcv != exp { t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", exp, rcv) } } func TestECappendChargingIntervalFromEventCost(t *testing.T) { ec := &EventCost{ Charges: []*ChargingInterval{ { RatingID: "RT_ID", CompressFactor: 1, }, }, } oEC := &EventCost{ Charges: []*ChargingInterval{ {}, { RatingID: "RT_ID", }, }, } cIlIdx := 1 exp := &EventCost{ Charges: []*ChargingInterval{ { RatingID: "RT_ID", CompressFactor: 2, }, }, } ec.appendChargingIntervalFromEventCost(oEC, cIlIdx) if !reflect.DeepEqual(ec, exp) { t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", exp, ec) } } func TestECratingIDForRateIntervalPause(t *testing.T) { ec := &EventCost{ RatingFilters: RatingFilters{}, Rating: Rating{}, Rates: ChargedRates{}, } ri := &RateInterval{ Rating: &RIRate{ ConnectFee: 0.4, RoundingMethod: "*up", MaxCost: 100, MaxCostStrategy: "*disconnect", RoundingDecimals: 4, Rates: RateGroups{ { RateIncrement: 60, RateUnit: 60, Value: 10, GroupIntervalStart: 1, }, }, }, } rf := RatingMatchedFilters{ "key": "filter", } exp := utils.MetaPause expEC := &EventCost{ Rating: Rating{ utils.MetaPause: &RatingUnit{ ConnectFee: 0.4, RoundingMethod: "*up", MaxCost: 100, MaxCostStrategy: "*disconnect", RoundingDecimals: 4, RatesID: utils.MetaPause, RatingFiltersID: utils.MetaPause, }, }, RatingFilters: RatingFilters{ utils.MetaPause: RatingMatchedFilters{ "key": "filter", }, }, Rates: ChargedRates{ utils.MetaPause: RateGroups{ { RateIncrement: 60, RateUnit: 60, Value: 10, GroupIntervalStart: 1, }, }, }, } rcv := ec.ratingIDForRateInterval(ri, rf, true) if rcv != exp { t.Fatalf("\nexpected: <%+v>, \nreceived: <%+v>", exp, rcv) } if !reflect.DeepEqual(ec, expEC) { t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", expEC, ec) } } func TestECnewIntervalFromCharge(t *testing.T) { ec := &EventCost{ Charges: []*ChargingInterval{ { Increments: []*ChargingIncrement{ { Usage: 100, Cost: 10, AccountingID: "accID1", CompressFactor: 1, }, { Usage: 150, Cost: 15, AccountingID: "accID2", CompressFactor: 1, }, }, }, }, Accounting: Accounting{ "accID1": &BalanceCharge{ RatingID: utils.MetaRounding, BalanceUUID: "asdfgh1", AccountID: "1001", }, "accID2": &BalanceCharge{ RatingID: utils.MetaRounding, BalanceUUID: "asdfgh2", AccountID: "1001", }, }, Rating: Rating{ utils.MetaRounding: &RatingUnit{ ConnectFee: 0.4, RoundingMethod: "*up", RoundingDecimals: 4, MaxCost: 100, MaxCostStrategy: "*disconnect", RatesID: "RT_ID", TimingID: "TM_NOON", }, }, Timings: ChargedTimings{ "TM_NOON": &ChargedTiming{ StartTime: "00:00:00", }, }, AccountSummary: &AccountSummary{ Tenant: "cgrates.org", ID: "1001", AllowNegative: true, BalanceSummaries: BalanceSummaries{ &BalanceSummary{ UUID: "asdfgh1", ID: "bsID", Type: utils.MetaData, Initial: 0, Value: 10, Disabled: true, }, }, }, } cInc := &ChargingIncrement{ AccountingID: "accID1", } exp := &Increment{ BalanceInfo: &DebitInfo{ AccountID: "1001", Unit: &UnitInfo{ UUID: "asdfgh1", Factor: 1, RateInterval: &RateInterval{ Rating: &RIRate{ RoundingDecimals: 4, RoundingMethod: "*up", ConnectFee: 0.4, MaxCost: 100, MaxCostStrategy: "*disconnect", }, Timing: &RITiming{ ID: "TM_NOON", StartTime: "00:00:00", }, }, }, }, } rcv := ec.newIntervalFromCharge(cInc) if !reflect.DeepEqual(rcv, exp) { t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", utils.ToJSON(exp), utils.ToJSON(rcv)) } } func TestECRemoveStaleReferences(t *testing.T) { ec := &EventCost{ Rating: Rating{ "unusedKey1": &RatingUnit{ ConnectFee: 0.4, RoundingMethod: "*up", RoundingDecimals: 4, MaxCost: 100, MaxCostStrategy: "*disconnect", TimingID: "TM_MORNING", RatesID: "RT_ID", RatingFiltersID: "RF_ID", }, }, RatingFilters: RatingFilters{ "unusedKey2": RatingMatchedFilters{}, }, Rates: ChargedRates{ "unusedKey3": RateGroups{ { RateIncrement: 60, RateUnit: 60, Value: 10, }, }, }, Timings: ChargedTimings{ "unusedKey4": &ChargedTiming{ StartTime: "00:00:00", }, }, } exp := &EventCost{ Rating: Rating{}, RatingFilters: RatingFilters{}, Rates: ChargedRates{}, Timings: ChargedTimings{ "unusedKey4": &ChargedTiming{ StartTime: "00:00:00", }, }, } ec.RemoveStaleReferences() if !reflect.DeepEqual(ec, exp) { t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", utils.ToJSON(exp), utils.ToJSON(ec)) } } func TestECTrimUnreachableLastChargingInterval(t *testing.T) { ec := &EventCost{ Usage: utils.DurationPointer(30 * time.Second), AccountSummary: &AccountSummary{}, Charges: []*ChargingInterval{ { RatingID: "RT_ID", cost: utils.Float64Pointer(10), ecUsageIdx: utils.DurationPointer(1), }, }, } atUsage := 15 * time.Second experr := "cannot find last active ChargingInterval" rcv, err := ec.Trim(atUsage) if err == nil || err.Error() != experr { t.Fatalf("\nexpected: <%+v>, \nreceived: <%+v>", experr, err) } if rcv != nil { t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", nil, rcv) } } func TestECTrimNoActiveIncrement(t *testing.T) { ec := &EventCost{ Usage: utils.DurationPointer(30 * time.Second), AccountSummary: &AccountSummary{}, Charges: []*ChargingInterval{ { RatingID: "RT_ID", cost: utils.Float64Pointer(10), ecUsageIdx: utils.DurationPointer(1 * time.Second), CompressFactor: 4, usage: utils.DurationPointer(45 * time.Second), }, }, } atUsage := 15 * time.Second experr := "no active increment found" rcv, err := ec.Trim(atUsage) if err == nil || err.Error() != experr { t.Fatalf("\nexpected: <%+v>, \nreceived: <%+v>", experr, err) } if rcv != nil { t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", nil, rcv) } } func TestECTrimUnableToDetectLastActiveChargingInterval(t *testing.T) { ec := &EventCost{ Usage: utils.DurationPointer(30 * time.Second), AccountSummary: &AccountSummary{}, Charges: []*ChargingInterval{ { RatingID: "RT_ID1", cost: utils.Float64Pointer(10), ecUsageIdx: utils.DurationPointer(30 * time.Second), CompressFactor: 0, usage: utils.DurationPointer(45 * time.Second), }, { RatingID: "RT_ID2", cost: utils.Float64Pointer(10), ecUsageIdx: utils.DurationPointer(25 * time.Second), CompressFactor: 4, usage: utils.DurationPointer(45 * time.Second), }, }, } atUsage := 15 * time.Second experr := "failed detecting last active ChargingInterval" rcv, err := ec.Trim(atUsage) if err == nil || err.Error() != experr { t.Fatalf("\nexpected: <%+v>, \nreceived: <%+v>", experr, err) } if rcv != nil { t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", nil, rcv) } } func TestECFieldAsInterfaceEmptyFieldPath(t *testing.T) { ec := &EventCost{} fldPath := []string{} experr := "empty field path" rcv, err := ec.FieldAsInterface(fldPath) if err == nil || err.Error() != experr { t.Fatalf("\nexpected: <%+v>, \nreceived: <%+v>", experr, err) } if rcv != nil { t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", nil, rcv) } } func TestECfieldAsInterfaceNilECUsage(t *testing.T) { ec := &EventCost{} fldPath := []string{utils.Usage} rcv, err := ec.fieldAsInterface(fldPath) if err != nil { t.Fatalf("\nexpected: <%+v>, \nreceived: <%+v>", nil, err) } if rcv != nil { t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", nil, rcv) } } func TestECfieldAsInterfaceNilECCost(t *testing.T) { ec := &EventCost{} fldPath := []string{utils.Cost} rcv, err := ec.fieldAsInterface(fldPath) if err != nil { t.Fatalf("\nexpected: <%+v>, \nreceived: <%+v>", nil, err) } if rcv != nil { t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", nil, rcv) } } func TestEventCostSet(t *testing.T) { ec := &EventCost{} testCases := []struct { name string fldPath []string val interface{} wantErr error }{ { name: "cgrates", fldPath: []string{"field1"}, val: "value1", wantErr: utils.ErrNotImplemented, }, { name: "cgrates2", fldPath: []string{"field2", "subfield"}, val: 123, wantErr: utils.ErrNotImplemented, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { err := ec.Set(tc.fldPath, tc.val) if err != tc.wantErr { t.Errorf("Set() error = %v, wantErr %v", err, tc.wantErr) } }) } } func TestEventCostRemove(t *testing.T) { ec := &EventCost{} testCases := []struct { name string fldPath []string wantErr error }{ { name: "cgrates", fldPath: []string{"field1"}, wantErr: utils.ErrNotImplemented, }, { name: "cgrates2", fldPath: []string{"field2", "subfield"}, wantErr: utils.ErrNotImplemented, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { err := ec.Remove(tc.fldPath) if err != tc.wantErr { t.Errorf("Remove() error = %v, wantErr %v", err, tc.wantErr) } }) } } func TestEventCostGetKeys(t *testing.T) { ec := &EventCost{} testCases := []struct { name string nested bool nestedLimit int prefix string expectedKeys []string }{ { name: "cgrates", nested: true, nestedLimit: 3, prefix: "prefix", expectedKeys: nil, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { keys := ec.GetKeys(tc.nested, tc.nestedLimit, tc.prefix) if !reflect.DeepEqual(keys, tc.expectedKeys) { t.Errorf("GetKeys() returned %+v, expected %+v", keys, tc.expectedKeys) } }) } } func TestEvenCostProcessEventCostField(t *testing.T) { testCases := []struct { name string fldPath []string cd interface{} event map[string]interface{} expectedValue interface{} expectedErr error }{ { name: "cgrates", fldPath: []string{"field1"}, cd: nil, event: make(map[string]interface{}), expectedValue: nil, expectedErr: errors.New("unsupported field prefix: "), }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { val, err := processEventCostField(tc.fldPath, tc.cd, tc.event) if !reflect.DeepEqual(val, tc.expectedValue) { t.Errorf("ProcessEventCostField() returned %+v, expected %+v", val, tc.expectedValue) } if (err == nil && tc.expectedErr != nil) || (err != nil && tc.expectedErr == nil) || (err != nil && tc.expectedErr != nil && err.Error() != tc.expectedErr.Error()) { t.Errorf("ProcessEventCostField() error = %v, expected error = %v", err, tc.expectedErr) } }) } } func TestConvertToEventCost(t *testing.T) { eventCost := &EventCost{ CGRID: "cgrid", RunID: "run_id", StartTime: time.Now(), } convertedEventCost, err := ConvertToEventCost(eventCost) if err != nil { t.Errorf("Unexpected error: %v", err) } if convertedEventCost != eventCost { t.Error("Expected identical EventCost objects") } eventCostJSON, _ := json.Marshal(eventCost) convertedEventCost, err = ConvertToEventCost(string(eventCostJSON)) if err != nil { t.Errorf("Unexpected error: %v", err) } tStruct := struct { CGRID string `json:"CGRID"` RunID string `json:"RunID"` }{ CGRID: "cgrid", RunID: "run_id", } convertedEventCost, err = ConvertToEventCost(tStruct) if err != nil { t.Errorf("Unexpected error: %v", err) } if convertedEventCost.CGRID != "cgrid" || convertedEventCost.RunID != "run_id" { t.Error("Expected correct values for CGRID and RunID") } _, err = ConvertToEventCost(123) if err == nil { t.Error("Expected error for invalid input type") } invalidJSON := []byte("invalid_json") _, err = ConvertToEventCost(invalidJSON) if err == nil { t.Error("Expected error for invalid JSON") } }