From 51bacf2c93ad9e0d4369801ce6cba144e4a92911 Mon Sep 17 00:00:00 2001 From: ionutboangiu Date: Wed, 31 Mar 2021 18:10:52 +0300 Subject: [PATCH] Remove Rates --- accounts/abstractbalance.go | 180 +++ accounts/abstractbalance_test.go | 1183 +++++++++++++++ accounts/actsetbalance.go | 322 ++++ accounts/actsetbalance_test.go | 456 ++++++ accounts/concretebalance.go | 173 +++ accounts/concretebalance_test.go | 832 +++++++++++ accounts/libaccounts.go | 330 ++++ accounts/libaccounts_test.go | 581 ++++++++ apier/v1/accountsv1_it_test.go | 1326 +++++++++++++++++ apier/v1/cost_bench_it_test.go | 234 --- apier/v1/dispatcher.go | 18 - apier/v1/full_remote_it_test.go | 79 - apier/v1/precache_it_test.go | 109 +- apier/v1/remote_it_test.go | 93 -- apier/v1/replicate_it_test.go | 108 -- apier/v1/replicator.go | 37 - apier/v1/tprateprofiles.go | 95 -- apier/v1/tprateprofiles_it_test.go | 242 --- cmd/cgr-engine/cgr-engine.go | 2 - cmd/cgr-tester/proc_event/proc_ev.go | 153 -- config/config_defaults.go | 31 - config/config_json_test.go | 100 -- config/config_test.go | 132 +- console/ping_test.go | 68 - .../engine1_mongo/cgrates.json | 1 - .../engine1_redis/cgrates.json | 1 - .../engine2_mongo/cgrates.json | 1 - .../engine2_redis/cgrates.json | 1 - .../internal/cgrates.json | 1 - .../samples/full_remote/internal/cgrates.json | 4 - .../remote_replication/internal/cgrates.json | 1 - .../internal_gob/cgrates.json | 1 - .../samples/replication/internal/cgrates.json | 1 - .../replication/internal_gob/cgrates.json | 1 - .../replication_cache/engine1/cgrates.json | 3 - .../mysql/create_tariffplan_tables.sql | 34 - .../postgres/create_tariffplan_tables.sql | 31 - engine/storage_mongo_datadb.go | 5 +- general_tests/export_it_test.go | 62 - migrator/migrator.go | 3 - migrator/migrator_datadb.go | 1 - migrator/routes.go | 75 - migrator/storage_mongo_datadb.go | 19 - services/dispatchers.go | 3 - utils/consts.go | 1 - 45 files changed, 5440 insertions(+), 1694 deletions(-) create mode 100644 accounts/abstractbalance.go create mode 100644 accounts/abstractbalance_test.go create mode 100644 accounts/actsetbalance.go create mode 100644 accounts/actsetbalance_test.go create mode 100644 accounts/concretebalance.go create mode 100644 accounts/concretebalance_test.go create mode 100644 accounts/libaccounts.go create mode 100644 accounts/libaccounts_test.go create mode 100644 apier/v1/accountsv1_it_test.go delete mode 100644 apier/v1/cost_bench_it_test.go delete mode 100644 apier/v1/tprateprofiles.go delete mode 100644 apier/v1/tprateprofiles_it_test.go delete mode 100644 cmd/cgr-tester/proc_event/proc_ev.go diff --git a/accounts/abstractbalance.go b/accounts/abstractbalance.go new file mode 100644 index 000000000..02fcd661e --- /dev/null +++ b/accounts/abstractbalance.go @@ -0,0 +1,180 @@ +/* +Real-time Online/Offline Charging System (OerS) 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 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package accounts + +import ( + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" + "github.com/ericlagergren/decimal" +) + +// newAbstractBalance constructs an abstractBalanceOperator +func newAbstractBalanceOperator(acntID string, blnCfg *utils.Balance, + cncrtBlncs []*concreteBalance, + fltrS *engine.FilterS, connMgr *engine.ConnManager, + attrSConns, rateSConns []string) balanceOperator { + return &abstractBalance{acntID, blnCfg, cncrtBlncs, fltrS, connMgr, attrSConns, rateSConns} +} + +// abstractBalance is the operator for *abstract balance type +type abstractBalance struct { + acntID string + blnCfg *utils.Balance + cncrtBlncs []*concreteBalance // paying balances + fltrS *engine.FilterS + connMgr *engine.ConnManager + attrSConns, + rateSConns []string +} + +// debitAbstracts implements the balanceOperator interface +func (aB *abstractBalance) debitAbstracts(usage *decimal.Big, + cgrEv *utils.CGREvent) (ec *utils.EventCharges, err error) { + + evNm := utils.MapStorage{ + utils.MetaOpts: cgrEv.APIOpts, + utils.MetaReq: cgrEv.Event, + } + + // pass the general balance filters + var pass bool + if pass, err = aB.fltrS.Pass(cgrEv.Tenant, aB.blnCfg.FilterIDs, evNm); err != nil { + return + } else if !pass { + return nil, utils.ErrFilterNotPassingNoCaps + } + + // balanceLimit + var hasLmt bool + var blncLmt *utils.Decimal + if blncLmt, err = balanceLimit(aB.blnCfg.Opts); err != nil { + return + } + if blncLmt != nil && blncLmt.Cmp(decimal.New(0, 0)) != 0 { + aB.blnCfg.Units.Big = utils.SubstractBig(aB.blnCfg.Units.Big, blncLmt.Big) + hasLmt = true + } + // unitFactor + var uF *utils.UnitFactor + if uF, err = unitFactor(aB.blnCfg.UnitFactors, aB.fltrS, cgrEv.Tenant, evNm); err != nil { + return + } + var hasUF bool + if uF != nil && uF.Factor.Cmp(decimal.New(1, 0)) != 0 { + usage = utils.MultiplyBig(usage, uF.Factor.Big) + hasUF = true + } + + // costIncrement + var costIcrm *utils.CostIncrement + if costIcrm, err = costIncrement(aB.blnCfg.CostIncrements, aB.fltrS, + cgrEv.Tenant, evNm); err != nil { + return + } + + // balance smaller than usage, correct usage if the balance has limit + if aB.blnCfg.Units.Big.Cmp(usage) == -1 && blncLmt != nil { + // decrease the usage to match the maximum increments + // will use special rounding to 0 since otherwise we go negative (ie: 0.05 as increment) + usage = roundUnitsWithIncrements(aB.blnCfg.Units.Big, costIcrm.Increment.Big) + } + if costIcrm.RecurrentFee.Cmp(decimal.New(0, 0)) == 0 && + (costIcrm.FixedFee == nil || + costIcrm.FixedFee.Cmp(decimal.New(0, 0)) == 0) { + // cost 0, no need of concrete + ec = utils.NewEventCharges() + ec.Abstracts = &utils.Decimal{usage} + // UnitFactors + var ufID string + if hasUF { + ufID = utils.UUIDSha1Prefix() + ec.UnitFactors[ufID] = uF + } + // Rating + ratingID := utils.UUIDSha1Prefix() + // ec.Rating[ratingID] = &utils.RateSInterval{ + // IntervalStart: utils.NewDecimal(0, 0), + // Increments: []*utils.RateSIncrement{ + // { + // IncrementStart: utils.NewDecimal(0, 0), + // Rate: &utils.Rate{ + // ID: utils.MetaCostIncrement, + // IntervalRates: []*utils.IntervalRate{ + // { + // FixedFee: utils.NewDecimal(0, 0), + // }, + // }, + // }, + // CompressFactor: 1, + // Usage: &utils.Decimal{usage}, + // }, + // }, + // CompressFactor: 1, + // } + acntID := utils.UUIDSha1Prefix() + ec.Accounting[acntID] = &utils.AccountCharge{ + AccountID: aB.acntID, + BalanceID: aB.blnCfg.ID, + Units: &utils.Decimal{usage}, + BalanceLimit: blncLmt, + UnitFactorID: ufID, + RatingID: ratingID, + } + ec.ChargingIntervals = []*utils.ChargingInterval{ + { + Increments: []*utils.ChargingIncrement{ + { + Units: &utils.Decimal{usage}, + AccountChargeID: acntID, + CompressFactor: 1, + }, + }, + CompressFactor: 1, + }, + } + } + // else { + // // attempt to debit usage with cost + // if ec, err = maxDebitAbstractsFromConcretes(usage, + // aB.acntID, aB.cncrtBlncs, + // aB.connMgr, cgrEv, + // aB.attrSConns, aB.blnCfg.AttributeIDs, + // aB.rateSConns, aB.blnCfg.RateProfileIDs, + // costIcrm); err != nil { + // return + // } + // } + + if ec.Abstracts.Cmp(decimal.New(0, 0)) != 0 { + aB.blnCfg.Units.Big = utils.SubstractBig(aB.blnCfg.Units.Big, ec.Abstracts.Big) + } + if hasLmt { // put back the limit + aB.blnCfg.Units.Big = utils.SumBig(aB.blnCfg.Units.Big, blncLmt.Big) + } + if hasUF { + usage = utils.DivideBig(usage, uF.Factor.Big) + } + return +} + +// debitConcretes implements the balanceOperator interface +func (aB *abstractBalance) debitConcretes(usage *decimal.Big, + cgrEv *utils.CGREvent) (ec *utils.EventCharges, err error) { + return nil, utils.ErrNotImplemented +} diff --git a/accounts/abstractbalance_test.go b/accounts/abstractbalance_test.go new file mode 100644 index 000000000..9bc9a7ed6 --- /dev/null +++ b/accounts/abstractbalance_test.go @@ -0,0 +1,1183 @@ +/* +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 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package accounts + +import ( + "reflect" + "testing" + "time" + + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" + "github.com/ericlagergren/decimal" +) + +func TestABDebitUsageFromConcretes1(t *testing.T) { + aB := &abstractBalance{ + cncrtBlncs: []*concreteBalance{ + { + blnCfg: &utils.Balance{ + ID: "CB1", + Type: utils.MetaConcrete, + Opts: map[string]interface{}{ + utils.MetaBalanceLimit: -200.0, + }, + UnitFactors: []*utils.UnitFactor{ + { + Factor: utils.NewDecimal(100, 0), // EuroCents + }, + }, + Units: utils.NewDecimal(500, 0), // 500 EuroCents + }, + }, + { + blnCfg: &utils.Balance{ + ID: "CB2", + Type: utils.MetaConcrete, + Opts: map[string]interface{}{ + utils.MetaBalanceLimit: -1.0, + }, + Units: utils.NewDecimal(125, 2), + }, + }, + }, + } + + // consume only from first balance + expectedEvCharg := &utils.EventCharges{ + Concretes: utils.NewDecimal(5, 0), + Accounting: make(map[string]*utils.AccountCharge), + UnitFactors: make(map[string]*utils.UnitFactor), + // Rating: make(map[string]*utils.RateSInterval), + } + if evCh, err := debitConcreteUnits(decimal.New(5, 0), + utils.EmptyString, aB.cncrtBlncs, new(utils.CGREvent)); err != nil { + t.Error(err) + } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(0, 0)) != 0 { + t.Errorf("Unexpected units in first balance: %s", aB.cncrtBlncs[0].blnCfg.Units) + } else if aB.cncrtBlncs[1].blnCfg.Units.Compare(utils.NewDecimal(125, 2)) != 0 { + t.Errorf("Unexpected units in second balance: %s", aB.cncrtBlncs[1].blnCfg.Units) + } else if !reflect.DeepEqual(evCh, expectedEvCharg) { + t.Errorf("Expected %+v, received %+v", utils.ToJSON(expectedEvCharg), utils.ToJSON(evCh)) + } + + //back with the main units + aB.cncrtBlncs[0].blnCfg.Units = utils.NewDecimal(500, 0) + expectedEvCharg = &utils.EventCharges{ + Concretes: utils.NewDecimal(9, 0), + Accounting: make(map[string]*utils.AccountCharge), + UnitFactors: make(map[string]*utils.UnitFactor), + // Rating: make(map[string]*utils.RateSInterval), + } + + if evCh, err := debitConcreteUnits(decimal.New(9, 0), + utils.EmptyString, aB.cncrtBlncs, new(utils.CGREvent)); err != nil { + t.Error(err) + } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(-200, 0)) != 0 { + t.Errorf("Unexpected units in first balance: %s", aB.cncrtBlncs[0].blnCfg.Units) + } else if aB.cncrtBlncs[1].blnCfg.Units.Compare(utils.NewDecimal(-75, 2)) != 0 { + t.Errorf("Unexpected units in second balance: %s", aB.cncrtBlncs[1].blnCfg.Units) + } else if !reflect.DeepEqual(evCh, expectedEvCharg) { + t.Errorf("Expected %+v, received %+v", utils.ToJSON(expectedEvCharg), utils.ToJSON(evCh)) + } + + //back with the main units + aB.cncrtBlncs[0].blnCfg.Units = utils.NewDecimal(500, 0) + aB.cncrtBlncs[1].blnCfg.Units = utils.NewDecimal(125, 2) + + if _, err := debitConcreteUnits(decimal.New(int64(time.Duration(10*time.Minute)), 0), + utils.EmptyString, aB.cncrtBlncs, new(utils.CGREvent)); err == nil || err != utils.ErrInsufficientCredit { + t.Error(err) + } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(500, 0)) != 0 { + t.Errorf("Unexpected units in first balance: %s", aB.cncrtBlncs[0].blnCfg.Units) + } else if aB.cncrtBlncs[1].blnCfg.Units.Compare(utils.NewDecimal(125, 2)) != 0 { + t.Errorf("Unexpected units in second balance: %s", aB.cncrtBlncs[1].blnCfg.Units) + } + + expectedEvCharg = &utils.EventCharges{ + Concretes: utils.NewDecimal(925, 2), + Accounting: make(map[string]*utils.AccountCharge), + UnitFactors: make(map[string]*utils.UnitFactor), + // Rating: make(map[string]*utils.RateSInterval), + } + if evCh, err := debitConcreteUnits(decimal.New(925, 2), + utils.EmptyString, aB.cncrtBlncs, new(utils.CGREvent)); err != nil { + t.Error(err) + } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(-200, 0)) != 0 { + t.Errorf("Unexpected units in first balance: %s", aB.cncrtBlncs[0].blnCfg.Units) + } else if aB.cncrtBlncs[1].blnCfg.Units.Compare(utils.NewDecimal(-1, 0)) != 0 { + t.Errorf("Unexpected units in second balance: %s", aB.cncrtBlncs[1].blnCfg.Units) + } else if !reflect.DeepEqual(evCh, expectedEvCharg) { + t.Errorf("Expected %+v, received %+v", utils.ToJSON(expectedEvCharg), utils.ToJSON(evCh)) + } + +} + +// func TestABDebitAbstracts(t *testing.T) { +// aB := &abstractBalance{ +// blnCfg: &utils.Balance{ +// ID: "AB1", +// Type: utils.MetaAbstract, +// CostIncrements: []*utils.CostIncrement{ +// { +// Increment: utils.NewDecimal(int64(time.Duration(time.Second)), 0), +// RecurrentFee: utils.NewDecimal(1, 0)}, +// }, +// Units: utils.NewDecimal(int64(time.Duration(60*time.Second)), 0), // 1 Minute +// }, +// cncrtBlncs: []*concreteBalance{ +// { +// blnCfg: &utils.Balance{ +// ID: "CB1", +// Type: utils.MetaConcrete, +// UnitFactors: []*utils.UnitFactor{ +// { +// Factor: utils.NewDecimal(1, 0), // EuroCents +// }, +// }, +// Units: utils.NewDecimal(50, 0), // 50 EURcents +// }, +// }, +// }, +// fltrS: new(engine.FilterS), +// } + +// if ec, err := aB.debitAbstracts(decimal.New(int64(30*time.Second), 0), +// new(utils.CGREvent)); err != nil { +// t.Error(err) +// } else if ec.Abstracts.Cmp(decimal.New(int64(30*time.Second), 0)) != 0 { +// t.Errorf("Unexpected debited units: %s", ec.Abstracts) +// } else if aB.blnCfg.Units.Compare(utils.NewDecimal(int64(30*time.Second), 0)) != 0 { +// t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) +// } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(20, 0)) != 0 { +// t.Errorf("Unexpected units in concrete balance: %s", aB.cncrtBlncs[0].blnCfg.Units) +// } + +// // limited by concrete +// aB.blnCfg.Units = utils.NewDecimal(int64(time.Duration(60*time.Second)), 0) +// aB.cncrtBlncs[0].blnCfg.Units = utils.NewDecimal(29, 0) // not enough concrete + +// if ec, err := aB.debitAbstracts(decimal.New(int64(30*time.Second), 0), +// new(utils.CGREvent)); err != nil { +// t.Error(err) +// } else if ec.Abstracts.Cmp(decimal.New(int64(29*time.Second), 0)) != 0 { +// t.Errorf("Unexpected debited units: %s", ec.Abstracts) +// } else if aB.blnCfg.Units.Compare(utils.NewDecimal(int64(time.Duration(31*time.Second)), 0)) != 0 { // used 29 units +// t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) +// } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(0, 0)) != 0 { +// t.Errorf("Unexpected units in concrete balance: %s", aB.cncrtBlncs[0].blnCfg.Units) +// } + +// // limited by concrete +// aB.cncrtBlncs[0].blnCfg.Units = utils.NewDecimal(0, 0) // not enough concrete + +// if ec, err := aB.debitAbstracts(decimal.New(int64(30*time.Second), 0), +// new(utils.CGREvent)); err != nil { +// t.Error(err) +// } else if ec.Abstracts.Cmp(decimal.New(0, 0)) != 0 { +// t.Errorf("Unexpected debited units: %s", ec.Abstracts) +// } else if aB.blnCfg.Units.Compare(utils.NewDecimal(int64(31*time.Second), 0)) != 0 { // same as above +// t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) +// } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(0, 0)) != 0 { // same as above +// t.Errorf("Unexpected units in concrete balance: %s", aB.cncrtBlncs[0].blnCfg.Units) +// } + +// // limited by abstract +// aB.blnCfg.Units = utils.NewDecimal(int64(time.Duration(29*time.Second)), 0) // not enough abstract +// aB.cncrtBlncs[0].blnCfg.Units = utils.NewDecimal(60, 0) + +// if ec, err := aB.debitAbstracts(decimal.New(int64(30*time.Second), 0), +// new(utils.CGREvent)); err != nil { +// t.Error(err) +// } else if ec.Abstracts.Cmp(decimal.New(int64(29*time.Second), 0)) != 0 { +// t.Errorf("Unexpected debited units: %s", ec.Abstracts) +// } else if aB.blnCfg.Units.Compare(utils.NewDecimal(0, 0)) != 0 { // should be all used +// t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) +// } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(31, 0)) != 0 { +// t.Errorf("Unexpected units in concrete balance: %s", aB.cncrtBlncs[0].blnCfg.Units) +// } +// } + +func TestABCost0WithConcrete(t *testing.T) { + // consume units only from abstract balance + aB := &abstractBalance{ + blnCfg: &utils.Balance{ + ID: "AB_COST_0", + Type: utils.MetaAbstract, + Units: utils.NewDecimal(int64(time.Duration(60*time.Second)), 0), // 1 Minute + CostIncrements: []*utils.CostIncrement{ + { + Increment: utils.NewDecimal(int64(time.Duration(time.Second)), 0), + RecurrentFee: utils.NewDecimal(0, 0), + }, + }, + }, + cncrtBlncs: []*concreteBalance{ + { + blnCfg: &utils.Balance{ + ID: "CB", + Type: utils.MetaConcrete, + Units: utils.NewDecimal(10, 0), + }, + }, + }, + fltrS: new(engine.FilterS), + } + + if ec, err := aB.debitAbstracts(decimal.New(int64(30*time.Second), 0), + new(utils.CGREvent)); err != nil { + t.Error(err) + } else if ec.Abstracts.Cmp(decimal.New(int64(30*time.Second), 0)) != 0 { + t.Errorf("Unexpected debited units: %s", ec.Abstracts) + } else if aB.blnCfg.Units.Compare(utils.NewDecimal(int64(30*time.Second), 0)) != 0 { + t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) + } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(10, 0)) != 0 { + t.Errorf("Unexpected units in concrete balance: %s", aB.cncrtBlncs[0].blnCfg.Units) + } +} + +func TestABCost0WithoutConcrete(t *testing.T) { + // consume units only from abstract balance + aB := &abstractBalance{ + blnCfg: &utils.Balance{ + ID: "AB_COST_0", + Type: utils.MetaAbstract, + Units: utils.NewDecimal(int64(time.Duration(60*time.Second)), 0), // 1 Minute + CostIncrements: []*utils.CostIncrement{ + { + Increment: utils.NewDecimal(int64(time.Duration(time.Second)), 0), + RecurrentFee: utils.NewDecimal(0, 0), + }, + }, + }, + fltrS: new(engine.FilterS), + } + + if ec, err := aB.debitAbstracts(decimal.New(int64(30*time.Second), 0), + new(utils.CGREvent)); err != nil { + t.Error(err) + } else if ec.Abstracts.Cmp(decimal.New(int64(30*time.Second), 0)) != 0 { + t.Errorf("Unexpected debited units: %s", ec.Abstracts) + } else if aB.blnCfg.Units.Compare(utils.NewDecimal(int64(30*time.Second), 0)) != 0 { + t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) + } +} + +func TestABCost0Exceed(t *testing.T) { + // consume more units that has an abstract balance + aB := &abstractBalance{ + blnCfg: &utils.Balance{ + ID: "AB_COST_0", + Type: utils.MetaAbstract, + Units: utils.NewDecimal(int64(time.Duration(60*time.Second)), 0), // 1 Minute + CostIncrements: []*utils.CostIncrement{ + { + Increment: utils.NewDecimal(int64(time.Duration(time.Second)), 0), + RecurrentFee: utils.NewDecimal(0, 0), + }, + }, + }, + cncrtBlncs: []*concreteBalance{ + { + blnCfg: &utils.Balance{ + ID: "CB", + Type: utils.MetaConcrete, + Units: utils.NewDecimal(10, 0), + }, + }, + }, + fltrS: new(engine.FilterS), + } + + if ec, err := aB.debitAbstracts(decimal.New(int64(70*time.Second), 0), + new(utils.CGREvent)); err != nil { + t.Error(err) + } else if ec.Abstracts.Cmp(decimal.New(int64(60*time.Second), 0)) != 0 { + t.Errorf("Unexpected debited units: %s", ec.Abstracts) + } else if aB.blnCfg.Units.Compare(utils.NewDecimal(0, 0)) != 0 { + t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) + } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(10, 0)) != 0 { + t.Errorf("Unexpected units in concrete balance: %s", aB.cncrtBlncs[0].blnCfg.Units) + } +} + +func TestABCost0ExceedWithoutConcrete(t *testing.T) { + // consume more units that has an abstract balance + aB := &abstractBalance{ + blnCfg: &utils.Balance{ + ID: "AB_COST_0", + Type: utils.MetaAbstract, + Units: utils.NewDecimal(int64(time.Duration(60*time.Second)), 0), // 1 Minute + CostIncrements: []*utils.CostIncrement{ + { + Increment: utils.NewDecimal(int64(time.Duration(time.Second)), 0), + RecurrentFee: utils.NewDecimal(0, 0), + }, + }, + }, + fltrS: new(engine.FilterS), + } + + if ec, err := aB.debitAbstracts(decimal.New(int64(70*time.Second), 0), + new(utils.CGREvent)); err != nil { + t.Error(err) + } else if ec.Abstracts.Cmp(decimal.New(int64(60*time.Second), 0)) != 0 { + t.Errorf("Unexpected debited units: %s", ec.Abstracts) + } else if aB.blnCfg.Units.Compare(utils.NewDecimal(0, 0)) != 0 { + t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) + } +} + +func TestABCost0WithUnlimitedWithConcrete(t *testing.T) { + // consume more units that has an abstract balance + aB := &abstractBalance{ + blnCfg: &utils.Balance{ + ID: "AB_COST_0", + Type: utils.MetaAbstract, + Units: utils.NewDecimal(int64(time.Duration(60*time.Second)), 0), // 1 Minute + Opts: map[string]interface{}{ + utils.MetaBalanceUnlimited: true, + }, + CostIncrements: []*utils.CostIncrement{ + { + Increment: utils.NewDecimal(int64(time.Duration(time.Second)), 0), + RecurrentFee: utils.NewDecimal(0, 0), + }, + }, + }, + cncrtBlncs: []*concreteBalance{ + { + blnCfg: &utils.Balance{ + ID: "CB", + Type: utils.MetaConcrete, + Units: utils.NewDecimal(10, 0), + }, + }, + }, + fltrS: new(engine.FilterS), + } + + if ec, err := aB.debitAbstracts(decimal.New(int64(80*time.Second), 0), + new(utils.CGREvent)); err != nil { + t.Error(err) + } else if ec.Abstracts.Cmp(decimal.New(int64(80*time.Second), 0)) != 0 { + t.Errorf("Unexpected debited units: %s", ec.Abstracts) + } else if aB.blnCfg.Units.Compare(utils.NewDecimal(-int64(time.Duration(20*time.Second)), 0)) != 0 { + t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) + } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(10, 0)) != 0 { + t.Errorf("Unexpected units in concrete balance: %s", aB.cncrtBlncs[0].blnCfg.Units) + } +} + +func TestABCost0WithLimit(t *testing.T) { + // consume more units that has an abstract balance + aB := &abstractBalance{ + blnCfg: &utils.Balance{ + ID: "AB_COST_0", + Type: utils.MetaAbstract, + Units: utils.NewDecimal(int64(time.Duration(60*time.Second)), 0), // 1 Minute + Opts: map[string]interface{}{ + utils.MetaBalanceLimit: 30000000000.0, + }, + UnitFactors: []*utils.UnitFactor{ + { + Factor: utils.NewDecimal(int64(2*time.Second), 0), + }, + }, + CostIncrements: []*utils.CostIncrement{ + { + Increment: utils.NewDecimal(int64(time.Duration(time.Second)), 0), + RecurrentFee: utils.NewDecimal(0, 0), + }, + }, + }, + fltrS: new(engine.FilterS), + } + + if ec, err := aB.debitAbstracts(decimal.New(int64(30*time.Second), 0), + new(utils.CGREvent)); err != nil { + t.Error(err) + } else if aB.blnCfg.Units.Compare(utils.NewDecimal(int64(time.Duration(30*time.Second)), 0)) != 0 { + t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) + } else if ec.Abstracts.Cmp(decimal.New(int64(30*time.Second), 0)) != 0 { + t.Errorf("Expected %+v, received %+v", decimal.New(int64(30*time.Second), 0), ec.Abstracts) + } +} + +func TestABCost0WithLimitWithConcrete(t *testing.T) { + // consume more units that has an abstract balance + aB := &abstractBalance{ + blnCfg: &utils.Balance{ + ID: "AB_COST_0", + Type: utils.MetaAbstract, + Units: utils.NewDecimal(int64(time.Duration(60*time.Second)), 0), // 1 Minute + Opts: map[string]interface{}{ + utils.MetaBalanceLimit: 30000000000.0, + }, + CostIncrements: []*utils.CostIncrement{ + { + Increment: utils.NewDecimal(int64(time.Duration(time.Second)), 0), + RecurrentFee: utils.NewDecimal(0, 0), + }, + }, + }, + cncrtBlncs: []*concreteBalance{ + { + blnCfg: &utils.Balance{ + ID: "CB", + Type: utils.MetaConcrete, + Units: utils.NewDecimal(10, 0), + }, + }, + }, + fltrS: new(engine.FilterS), + } + + if ec, err := aB.debitAbstracts(decimal.New(int64(30*time.Second), 0), + new(utils.CGREvent)); err != nil { + t.Error(err) + } else if ec.Abstracts.Cmp(decimal.New(int64(30*time.Second), 0)) != 0 { + t.Errorf("Unexpected debited units: %s", ec.Abstracts) + } else if aB.blnCfg.Units.Compare(utils.NewDecimal(int64(time.Duration(30*time.Second)), 0)) != 0 { + t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) + } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(10, 0)) != 0 { + t.Errorf("Unexpected units in concrete balance: %s", aB.cncrtBlncs[0].blnCfg.Units) + } +} + +func TestABCost0WithLimitExceed(t *testing.T) { + // consume more units that has an abstract balance + aB := &abstractBalance{ + blnCfg: &utils.Balance{ + ID: "AB_COST_0", + Type: utils.MetaAbstract, + Units: utils.NewDecimal(int64(time.Duration(60*time.Second)), 0), // 1 Minute + Opts: map[string]interface{}{ + utils.MetaBalanceLimit: 30000000000.0, + }, + CostIncrements: []*utils.CostIncrement{ + { + Increment: utils.NewDecimal(int64(time.Duration(time.Second)), 0), + RecurrentFee: utils.NewDecimal(0, 0), + }, + }, + }, + fltrS: new(engine.FilterS), + } + + if ec, err := aB.debitAbstracts(decimal.New(int64(50*time.Second), 0), + new(utils.CGREvent)); err != nil { + t.Error(err) + } else if ec.Abstracts.Cmp(decimal.New(int64(30*time.Second), 0)) != 0 { + t.Errorf("Unexpected debited units: %s", ec.Abstracts) + } else if aB.blnCfg.Units.Compare(utils.NewDecimal(int64(time.Duration(30*time.Second)), 0)) != 0 { + t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) + } +} + +func TestABCost0WithLimitExceedWithConcrete(t *testing.T) { + // consume more units that has an abstract balance + aB := &abstractBalance{ + blnCfg: &utils.Balance{ + ID: "AB_COST_0", + Type: utils.MetaAbstract, + Units: utils.NewDecimal(int64(time.Duration(60*time.Second)), 0), // 1 Minute + Opts: map[string]interface{}{ + utils.MetaBalanceLimit: 30000000000.0, + }, + CostIncrements: []*utils.CostIncrement{ + { + Increment: utils.NewDecimal(int64(time.Duration(time.Second)), 0), + RecurrentFee: utils.NewDecimal(0, 0), + }, + }, + }, + cncrtBlncs: []*concreteBalance{ + { + blnCfg: &utils.Balance{ + ID: "CB", + Type: utils.MetaConcrete, + Units: utils.NewDecimal(10, 0), + }, + }, + }, + fltrS: new(engine.FilterS), + } + + if ec, err := aB.debitAbstracts(decimal.New(int64(50*time.Second), 0), + new(utils.CGREvent)); err != nil { + t.Error(err) + } else if ec.Abstracts.Cmp(decimal.New(int64(30*time.Second), 0)) != 0 { + t.Errorf("Unexpected debited units: %s", ec.Abstracts) + } else if aB.blnCfg.Units.Compare(utils.NewDecimal(int64(time.Duration(30*time.Second)), 0)) != 0 { + t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) + } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(10, 0)) != 0 { + t.Errorf("Unexpected units in concrete balance: %s", aB.cncrtBlncs[0].blnCfg.Units) + } +} + +func TestDebitUsageFiltersError(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + filters := engine.NewFilterS(cfg, nil, nil) + aB := &abstractBalance{ + blnCfg: &utils.Balance{ + ID: "ID_TEST", + Type: utils.MetaAbstract, + FilterIDs: []string{"*string:*~req.Usage:10s"}, + CostIncrements: []*utils.CostIncrement{ + { + Increment: utils.NewDecimal(int64(1*time.Second), 0), + RecurrentFee: utils.NewDecimal(2, 0), + }, + }, + Units: utils.NewDecimal(int64(50*time.Second), 0), + }, + fltrS: filters, + } + + cgrEv := &utils.CGREvent{ + Tenant: "cgrates.org", + Event: map[string]interface{}{ + utils.Usage: "10s", + }, + } + _, err := aB.debitAbstracts(decimal.New(int64(40*time.Second), 0), + cgrEv) + if err == nil || err != utils.ErrFilterNotPassingNoCaps { + t.Errorf("Expected %+v, received %+v", utils.ErrFilterNotPassingNoCaps, err) + } + + aB.blnCfg.FilterIDs = []string{"invalid_filter_format"} + _, err = aB.debitAbstracts(decimal.New(int64(40*time.Second), 0), + cgrEv) + if err == nil || err != utils.ErrNoDatabaseConn { + t.Errorf("Expected %+v, received %+v", utils.ErrNoDatabaseConn, err) + } +} + +// func TestDebitUsageBalanceLimitErrors(t *testing.T) { +// aB := &abstractBalance{ +// blnCfg: &utils.Balance{ +// ID: "ID_TEST", +// Type: utils.MetaAbstract, +// Opts: map[string]interface{}{ +// utils.MetaBalanceLimit: "not_FLOAT64", +// }, +// CostIncrements: []*utils.CostIncrement{ +// { +// Increment: utils.NewDecimal(int64(1*time.Second), 0), +// RecurrentFee: utils.NewDecimal(2, 0), +// }, +// }, +// Units: utils.NewDecimal(int64(60*time.Second), 0), +// }, +// fltrS: new(engine.FilterS), +// } + +// cgrEv := &utils.CGREvent{ +// Tenant: "cgrates.org", +// Event: map[string]interface{}{ +// utils.Usage: "10s", +// }, +// } + +// expectedErr := "unsupported *balanceLimit format" +// _, err := aB.debitAbstracts(decimal.New(int64(40*time.Second), 0), +// cgrEv) +// if err == nil || err.Error() != expectedErr { +// t.Errorf("Expected %+v, received %+v", expectedErr, err) +// } + +// aB.blnCfg.Opts[utils.MetaBalanceLimit] = float64(16 * time.Second) +// if _, err = aB.debitAbstracts(decimal.New(int64(40*time.Second), 0), +// cgrEv); err != nil { +// t.Error(err) +// } +// if aB.blnCfg.Units.Compare(utils.NewDecimal(int64(60*time.Second), 0)) != 0 { +// t.Errorf("Expected %+v, received %+v", aB.blnCfg.Units.Big, utils.NewDecimal(int64(50*time.Second), 0)) +// } +// } + +// func TestDebitUsageUnitFactorsErrors(t *testing.T) { +// cfg := config.NewDefaultCGRConfig() +// filters := engine.NewFilterS(cfg, nil, nil) +// aB := &abstractBalance{ +// blnCfg: &utils.Balance{ +// ID: "ID_TEST", +// Type: utils.MetaAbstract, +// UnitFactors: []*utils.UnitFactor{ +// { +// FilterIDs: []string{"invalid_filter_fromat"}, +// Factor: utils.NewDecimal(2, 0), +// }, +// }, +// CostIncrements: []*utils.CostIncrement{ +// { +// Increment: utils.NewDecimal(int64(1*time.Second), 0), +// RecurrentFee: utils.NewDecimal(2, 0), +// }, +// }, +// Units: utils.NewDecimal(int64(60*time.Second), 0), +// }, +// fltrS: filters, +// } + +// cgrEv := &utils.CGREvent{ +// Tenant: "cgrates.org", +// Event: map[string]interface{}{ +// utils.Usage: "10s", +// }, +// } + +// if _, err := aB.debitAbstracts(decimal.New(int64(20*time.Second), 0), cgrEv); err == nil || +// err != utils.ErrNoDatabaseConn { +// t.Errorf("Expected %+v, received %+v", utils.ErrNoDatabaseConn, err) +// } + +// aB.blnCfg.UnitFactors[0].FilterIDs = []string{"*string:*~req.Usage:10s"} +// if ec, err := aB.debitAbstracts(decimal.New(int64(20*time.Second), 0), cgrEv); err != nil { +// t.Error(err) +// } else if ec.Abstracts.Cmp(decimal.New(0, 0)) != 0 { +// t.Error(err) +// } +// } + +// func TestDebitUsageCostIncrementError(t *testing.T) { +// cfg := config.NewDefaultCGRConfig() +// filters := engine.NewFilterS(cfg, nil, nil) +// aB := &abstractBalance{ +// blnCfg: &utils.Balance{ +// ID: "ID_TEST", +// Type: utils.MetaAbstract, +// CostIncrements: []*utils.CostIncrement{ +// { +// FilterIDs: []string{"INVALID_FILTER_FORMAT"}, +// Increment: utils.NewDecimal(int64(1*time.Second), 0), +// RecurrentFee: utils.NewDecimal(2, 0), +// }, +// }, +// Units: utils.NewDecimal(int64(60*time.Second), 0), +// }, +// fltrS: filters, +// } +// cgrEv := &utils.CGREvent{ +// Tenant: "cgrates.org", +// Event: map[string]interface{}{ +// utils.Usage: "10s", +// }, +// } + +// if _, err := aB.debitAbstracts(decimal.New(int64(20*time.Second), 0), cgrEv); err == nil || +// err != utils.ErrNoDatabaseConn { +// t.Errorf("Expected %+v, received %+v", utils.ErrNoDatabaseConn, err) +// } + +// //Will check the error by making the event charge +// //the cost is unknown, will use attributes to query from rates +// aB.blnCfg.CostIncrements = nil +// aB.blnCfg.AttributeIDs = []string{"attr11"} +// expected := "NOT_CONNECTED: AttributeS" +// if _, err := aB.debitAbstracts(decimal.New(int64(20*time.Second), 0), cgrEv); err == nil || +// err.Error() != expected { +// t.Errorf("Expected %+v, received %+v", expected, err) +// } +// } + +// func TestABCost(t *testing.T) { +// // debit 10 seconds with cost of 0.1 per second +// aB := &abstractBalance{ +// blnCfg: &utils.Balance{ +// ID: "AB_COST_0", +// Type: utils.MetaAbstract, +// Units: utils.NewDecimal(int64(time.Duration(60*time.Second)), 0), // 1 Minute +// CostIncrements: []*utils.CostIncrement{ +// { +// Increment: utils.NewDecimal(int64(time.Duration(time.Second)), 0), +// RecurrentFee: utils.NewDecimal(1, 1), +// }, +// }, +// }, +// cncrtBlncs: []*concreteBalance{ +// { +// blnCfg: &utils.Balance{ +// ID: "CB", +// Type: utils.MetaConcrete, +// Units: utils.NewDecimal(10, 0), +// }, +// }, +// }, +// fltrS: new(engine.FilterS), +// } + +// if ec, err := aB.debitAbstracts(decimal.New(int64(10*time.Second), 0), +// new(utils.CGREvent)); err != nil { +// t.Error(err) +// } else if ec.Abstracts.Cmp(decimal.New(int64(10*time.Second), 0)) != 0 { +// t.Errorf("Unexpected debited units: %s", ec.Abstracts) +// } else if aB.blnCfg.Units.Compare(utils.NewDecimal(int64(time.Duration(50*time.Second)), 0)) != 0 { +// t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) +// } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(9, 0)) != 0 { +// t.Errorf("Unexpected units in concrete balance: %s", aB.cncrtBlncs[0].blnCfg.Units) +// } +// } + +// func TestABCostWithFiltersNotMatch(t *testing.T) { +// cfg := config.NewDefaultCGRConfig() +// data := engine.NewInternalDB(nil, nil, true) +// dm := engine.NewDataManager(data, config.CgrConfig().CacheCfg(), nil) +// filterS := engine.NewFilterS(cfg, nil, dm) +// // we expect to receive an error because it will try calculate the cost from rates +// aB := &abstractBalance{ +// blnCfg: &utils.Balance{ +// ID: "AB_COST_0", +// Type: utils.MetaAbstract, +// Units: utils.NewDecimal(int64(time.Duration(60*time.Second)), 0), // 1 Minute +// CostIncrements: []*utils.CostIncrement{ +// { +// FilterIDs: []string{"*string:~*req.CustomField:CustomValue"}, +// Increment: utils.NewDecimal(int64(time.Duration(time.Second)), 0), +// RecurrentFee: utils.NewDecimal(1, 1), +// }, +// }, +// }, +// cncrtBlncs: []*concreteBalance{ +// { +// blnCfg: &utils.Balance{ +// ID: "CB", +// Type: utils.MetaConcrete, +// Units: utils.NewDecimal(10, 0), +// }, +// }, +// }, +// fltrS: filterS, +// } +// cgrEv := &utils.CGREvent{ +// Tenant: "cgrates.org", +// ID: "EV", +// Event: map[string]interface{}{ +// "CustomField2": "CustomValue2", +// }, +// } +// if _, err := aB.debitAbstracts(decimal.New(int64(10*time.Second), 0), +// cgrEv); err == nil || err.Error() != "RATES_ERROR:NOT_CONNECTED: RateS" { +// t.Error(err) +// } +// } + +// func TestABCostWithFilters(t *testing.T) { +// // debit 10 seconds with cost of 0.1 per second +// cfg := config.NewDefaultCGRConfig() +// data := engine.NewInternalDB(nil, nil, true) +// dm := engine.NewDataManager(data, config.CgrConfig().CacheCfg(), nil) +// filterS := engine.NewFilterS(cfg, nil, dm) +// aB := &abstractBalance{ +// blnCfg: &utils.Balance{ +// ID: "AB_COST_0", +// Type: utils.MetaAbstract, +// Units: utils.NewDecimal(int64(time.Duration(60*time.Second)), 0), // 1 Minute +// CostIncrements: []*utils.CostIncrement{ +// { +// FilterIDs: []string{"*string:~*req.CustomField:CustomValue"}, +// Increment: utils.NewDecimal(int64(time.Duration(time.Second)), 0), +// RecurrentFee: utils.NewDecimal(1, 1), +// }, +// }, +// }, +// cncrtBlncs: []*concreteBalance{ +// { +// blnCfg: &utils.Balance{ +// ID: "CB", +// Type: utils.MetaConcrete, +// Units: utils.NewDecimal(10, 0), +// }, +// }, +// }, +// fltrS: filterS, +// } +// cgrEv := &utils.CGREvent{ +// Tenant: "cgrates.org", +// ID: "EV", +// Event: map[string]interface{}{ +// "CustomField": "CustomValue", +// }, +// } + +// if ec, err := aB.debitAbstracts(decimal.New(int64(10*time.Second), 0), +// cgrEv); err != nil { +// t.Error(err) +// } else if ec.Abstracts.Cmp(decimal.New(int64(10*time.Second), 0)) != 0 { +// t.Errorf("Unexpected debited units: %s", ec.Abstracts) +// } else if aB.blnCfg.Units.Compare(utils.NewDecimal(int64(time.Duration(50*time.Second)), 0)) != 0 { +// t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) +// } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(9, 0)) != 0 { +// t.Errorf("Unexpected units in concrete balance: %s", aB.cncrtBlncs[0].blnCfg.Units) +// } +// } + +// func TestABCostExceed(t *testing.T) { +// // debit 70 seconds with cost of 0.1 per second +// aB := &abstractBalance{ +// blnCfg: &utils.Balance{ +// ID: "AB_COST_0", +// Type: utils.MetaAbstract, +// Units: utils.NewDecimal(int64(time.Duration(60*time.Second)), 0), // 1 Minute +// CostIncrements: []*utils.CostIncrement{ +// { +// Increment: utils.NewDecimal(int64(time.Duration(time.Second)), 0), +// RecurrentFee: utils.NewDecimal(1, 1), +// }, +// }, +// }, +// cncrtBlncs: []*concreteBalance{ +// { +// blnCfg: &utils.Balance{ +// ID: "CB", +// Type: utils.MetaConcrete, +// Units: utils.NewDecimal(10, 0), +// }, +// }, +// }, +// fltrS: new(engine.FilterS), +// } + +// if ec, err := aB.debitAbstracts(decimal.New(int64(70*time.Second), 0), +// new(utils.CGREvent)); err != nil { +// t.Error(err) +// } else if ec.Abstracts.Cmp(decimal.New(int64(60*time.Second), 0)) != 0 { +// t.Errorf("Unexpected debited units: %s", ec.Abstracts) +// } else if aB.blnCfg.Units.Compare(utils.NewDecimal(0, 0)) != 0 { +// t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) +// } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(4, 0)) != 0 { +// t.Errorf("Unexpected units in concrete balance: %s", aB.cncrtBlncs[0].blnCfg.Units) +// } +// } + +// func TestABCostUnlimitedExceed(t *testing.T) { +// // debit 70 seconds with cost of 0.1 per second +// aB := &abstractBalance{ +// blnCfg: &utils.Balance{ +// ID: "AB_COST_0", +// Type: utils.MetaAbstract, +// Units: utils.NewDecimal(int64(time.Duration(60*time.Second)), 0), // 1 Minute +// Opts: map[string]interface{}{ +// utils.MetaBalanceUnlimited: true, +// }, +// CostIncrements: []*utils.CostIncrement{ +// { +// Increment: utils.NewDecimal(int64(time.Duration(time.Second)), 0), +// RecurrentFee: utils.NewDecimal(1, 1), +// }, +// }, +// }, +// cncrtBlncs: []*concreteBalance{ +// { +// blnCfg: &utils.Balance{ +// ID: "CB", +// Type: utils.MetaConcrete, +// Units: utils.NewDecimal(10, 0), +// }, +// }, +// }, +// fltrS: new(engine.FilterS), +// } + +// if ec, err := aB.debitAbstracts(decimal.New(int64(70*time.Second), 0), +// new(utils.CGREvent)); err != nil { +// t.Error(err) +// } else if ec.Abstracts.Cmp(decimal.New(int64(70*time.Second), 0)) != 0 { +// t.Errorf("Unexpected debited units: %s", ec.Abstracts) +// } else if aB.blnCfg.Units.Compare(utils.NewDecimal(-int64(time.Duration(10*time.Second)), 0)) != 0 { +// t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) +// } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(3, 0)) != 0 { +// t.Errorf("Unexpected units in concrete balance: %s", aB.cncrtBlncs[0].blnCfg.Units) +// } +// } + +// func TestABCostLimit(t *testing.T) { +// // debit 70 seconds with cost of 0.1 per second +// aB := &abstractBalance{ +// blnCfg: &utils.Balance{ +// ID: "AB_COST_0", +// Type: utils.MetaAbstract, +// Units: utils.NewDecimal(int64(time.Duration(60*time.Second)), 0), // 1 Minute +// Opts: map[string]interface{}{ +// utils.MetaBalanceLimit: 30000000000.0, +// }, +// CostIncrements: []*utils.CostIncrement{ +// { +// Increment: utils.NewDecimal(int64(time.Duration(time.Second)), 0), +// RecurrentFee: utils.NewDecimal(1, 1), +// }, +// }, +// }, +// cncrtBlncs: []*concreteBalance{ +// { +// blnCfg: &utils.Balance{ +// ID: "CB", +// Type: utils.MetaConcrete, +// Units: utils.NewDecimal(10, 0), +// }, +// }, +// }, +// fltrS: new(engine.FilterS), +// } + +// if ec, err := aB.debitAbstracts(decimal.New(int64(30*time.Second), 0), +// new(utils.CGREvent)); err != nil { +// t.Error(err) +// } else if ec.Abstracts.Cmp(decimal.New(int64(30*time.Second), 0)) != 0 { +// t.Errorf("Unexpected debited units: %s", ec.Abstracts) +// } else if aB.blnCfg.Units.Compare(utils.NewDecimal(int64(time.Duration(30*time.Second)), 0)) != 0 { +// t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) +// } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(7, 0)) != 0 { +// t.Errorf("Unexpected units in concrete balance: %s", aB.cncrtBlncs[0].blnCfg.Units) +// } +// } + +// func TestABCostLimitExceed(t *testing.T) { +// // debit 70 seconds with cost of 0.1 per second +// aB := &abstractBalance{ +// blnCfg: &utils.Balance{ +// ID: "AB_COST_0", +// Type: utils.MetaAbstract, +// Units: utils.NewDecimal(int64(time.Duration(60*time.Second)), 0), // 1 Minute +// Opts: map[string]interface{}{ +// utils.MetaBalanceLimit: 30000000000.0, +// }, +// CostIncrements: []*utils.CostIncrement{ +// { +// Increment: utils.NewDecimal(int64(time.Duration(time.Second)), 0), +// RecurrentFee: utils.NewDecimal(1, 1), +// }, +// }, +// }, +// cncrtBlncs: []*concreteBalance{ +// { +// blnCfg: &utils.Balance{ +// ID: "CB", +// Type: utils.MetaConcrete, +// Units: utils.NewDecimal(10, 0), +// }, +// }, +// }, +// fltrS: new(engine.FilterS), +// } + +// if ec, err := aB.debitAbstracts(decimal.New(int64(70*time.Second), 0), +// new(utils.CGREvent)); err != nil { +// t.Error(err) +// } else if ec.Abstracts.Cmp(decimal.New(int64(30*time.Second), 0)) != 0 { +// t.Errorf("Unexpected debited units: %s", ec.Abstracts) +// } else if aB.blnCfg.Units.Compare(utils.NewDecimal(int64(time.Duration(30*time.Second)), 0)) != 0 { +// t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) +// } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(7, 0)) != 0 { +// t.Errorf("Unexpected units in concrete balance: %s", aB.cncrtBlncs[0].blnCfg.Units) +// } +// } + +// func TestABCostNotEnoughConcrete(t *testing.T) { +// // debit 55 seconds with cost of 0.1 per second +// aB := &abstractBalance{ +// blnCfg: &utils.Balance{ +// ID: "AB_COST_0", +// Type: utils.MetaAbstract, +// Units: utils.NewDecimal(int64(time.Duration(60*time.Second)), 0), // 1 Minute +// CostIncrements: []*utils.CostIncrement{ +// { +// Increment: utils.NewDecimal(int64(time.Duration(time.Second)), 0), +// RecurrentFee: utils.NewDecimal(1, 1), +// }, +// }, +// }, +// cncrtBlncs: []*concreteBalance{ +// { +// blnCfg: &utils.Balance{ +// ID: "CB", +// Type: utils.MetaConcrete, +// Units: utils.NewDecimal(5, 0), +// }, +// }, +// }, +// fltrS: new(engine.FilterS), +// } + +// if ec, err := aB.debitAbstracts(decimal.New(int64(55*time.Second), 0), +// new(utils.CGREvent)); err != nil { +// t.Error(err) +// } else if ec.Abstracts.Cmp(decimal.New(int64(50*time.Second), 0)) != 0 { +// t.Errorf("Unexpected debited units: %s", ec.Abstracts) +// } else if aB.blnCfg.Units.Compare(utils.NewDecimal(int64(time.Duration(10*time.Second)), 0)) != 0 { +// t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) +// } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(0, 0)) != 0 { +// t.Errorf("Unexpected units in concrete balance: %s", aB.cncrtBlncs[0].blnCfg.Units) +// } +// } + +// func TestABCostMultipleConcrete(t *testing.T) { +// // debit 55 seconds with cost of 0.1 per second +// aB := &abstractBalance{ +// blnCfg: &utils.Balance{ +// ID: "AB_COST_0", +// Type: utils.MetaAbstract, +// Units: utils.NewDecimal(int64(time.Duration(60*time.Second)), 0), // 1 Minute +// CostIncrements: []*utils.CostIncrement{ +// { +// Increment: utils.NewDecimal(int64(time.Duration(time.Second)), 0), +// RecurrentFee: utils.NewDecimal(1, 1), +// }, +// }, +// }, +// cncrtBlncs: []*concreteBalance{ +// { +// blnCfg: &utils.Balance{ +// ID: "CB1", +// Type: utils.MetaConcrete, +// Units: utils.NewDecimal(5, 0), +// }, +// }, +// { +// blnCfg: &utils.Balance{ +// ID: "CB2", +// Type: utils.MetaConcrete, +// Units: utils.NewDecimal(5, 0), +// }, +// }, +// }, +// fltrS: new(engine.FilterS), +// } + +// if ec, err := aB.debitAbstracts(decimal.New(int64(55*time.Second), 0), +// new(utils.CGREvent)); err != nil { +// t.Error(err) +// } else if ec.Abstracts.Cmp(decimal.New(int64(55*time.Second), 0)) != 0 { +// t.Errorf("Unexpected debited units: %s", ec.Abstracts) +// } else if aB.blnCfg.Units.Compare(utils.NewDecimal(int64(time.Duration(5*time.Second)), 0)) != 0 { +// t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) +// } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(0, 0)) != 0 { +// t.Errorf("Unexpected units in concrete balance: %s", aB.cncrtBlncs[0].blnCfg.Units) +// } else if aB.cncrtBlncs[1].blnCfg.Units.Compare(utils.NewDecimal(45, 1)) != 0 { +// t.Errorf("Unexpected units in concrete balance: %s", aB.cncrtBlncs[1].blnCfg.Units) +// } +// } + +// func TestABCostMultipleConcreteUnlimited(t *testing.T) { +// // debit 55 seconds with cost of 0.1 per second +// aB := &abstractBalance{ +// blnCfg: &utils.Balance{ +// ID: "AB_COST_0", +// Type: utils.MetaAbstract, +// Units: utils.NewDecimal(int64(time.Duration(60*time.Second)), 0), // 1 Minute +// Opts: map[string]interface{}{ +// utils.MetaBalanceUnlimited: true, +// }, +// CostIncrements: []*utils.CostIncrement{ +// { +// Increment: utils.NewDecimal(int64(time.Duration(time.Second)), 0), +// RecurrentFee: utils.NewDecimal(1, 1), +// }, +// }, +// }, +// cncrtBlncs: []*concreteBalance{ +// { +// blnCfg: &utils.Balance{ +// ID: "CB1", +// Type: utils.MetaConcrete, +// Units: utils.NewDecimal(5, 0), +// }, +// }, +// { +// blnCfg: &utils.Balance{ +// ID: "CB2", +// Type: utils.MetaConcrete, +// Units: utils.NewDecimal(5, 0), +// }, +// }, +// }, +// fltrS: new(engine.FilterS), +// } + +// if ec, err := aB.debitAbstracts(decimal.New(int64(70*time.Second), 0), +// new(utils.CGREvent)); err != nil { +// t.Error(err) +// } else if ec.Abstracts.Cmp(decimal.New(int64(70*time.Second), 0)) != 0 { +// t.Errorf("Unexpected debited units: %s", ec.Abstracts) +// } else if aB.blnCfg.Units.Compare(utils.NewDecimal(-int64(time.Duration(10*time.Second)), 0)) != 0 { +// t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) +// } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(0, 0)) != 0 { +// t.Errorf("Unexpected units in concrete balance: %s", aB.cncrtBlncs[0].blnCfg.Units) +// } else if aB.cncrtBlncs[1].blnCfg.Units.Compare(utils.NewDecimal(3, 0)) != 0 { +// t.Errorf("Unexpected units in concrete balance: %s", aB.cncrtBlncs[1].blnCfg.Units) +// } +// } + +// func TestAMCostWithUnitFactor(t *testing.T) { +// cfg := config.NewDefaultCGRConfig() +// data := engine.NewInternalDB(nil, nil, true) +// dm := engine.NewDataManager(data, config.CgrConfig().CacheCfg(), nil) +// filterS := engine.NewFilterS(cfg, nil, dm) + +// aB := &abstractBalance{ +// blnCfg: &utils.Balance{ +// ID: "ID_TEST", +// Type: utils.MetaAbstract, +// Units: utils.NewDecimal(int64(60*time.Second), 0), +// UnitFactors: []*utils.UnitFactor{ +// { +// Factor: utils.NewDecimal(2, 0), +// }, +// }, +// CostIncrements: []*utils.CostIncrement{ +// { +// Increment: utils.NewDecimal(int64(1*time.Second), 0), +// RecurrentFee: utils.NewDecimal(1, 0), +// }, +// }, +// }, +// cncrtBlncs: []*concreteBalance{ +// { +// blnCfg: &utils.Balance{ +// ID: "CB1", +// Type: utils.MetaConcrete, +// Units: utils.NewDecimal(50, 0), +// }, +// }, +// }, +// fltrS: filterS, +// } + +// cgrEv := &utils.CGREvent{ +// Tenant: "cgrates.org", +// ID: "EV", +// Event: map[string]interface{}{ +// "CustomField": "CustomValue", +// }, +// } + +// if ec, err := aB.debitAbstracts(decimal.New(int64(10*time.Second), 0), +// cgrEv); err != nil { +// t.Error(err) +// } else if ec.Abstracts.Cmp(decimal.New(int64(20*time.Second), 0)) != 0 { +// t.Errorf("Unexpected debited units: %s", ec.Abstracts) +// } else if aB.blnCfg.Units.Compare(utils.NewDecimal(int64(time.Duration(40*time.Second)), 0)) != 0 { +// t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) +// } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(30, 0)) != 0 { +// t.Errorf("Unexpected units in concrete balance: %s", aB.cncrtBlncs[0].blnCfg.Units) +// } +// } diff --git a/accounts/actsetbalance.go b/accounts/actsetbalance.go new file mode 100644 index 000000000..ee53a3541 --- /dev/null +++ b/accounts/actsetbalance.go @@ -0,0 +1,322 @@ +/* +Real-time Online/Offline Charging System (OerS) 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 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package accounts + +import ( + "fmt" + "strconv" + "strings" + + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +// actSetAccount updates the balances base on the diktat +func actSetAccount(dm *engine.DataManager, tnt, acntID string, diktats []*utils.BalDiktat, reset bool) (err error) { + var qAcnt *utils.AccountProfile + if qAcnt, err = dm.GetAccountProfile(tnt, acntID); err != nil { + if err != utils.ErrNotFound { + return + } + // in case the account doesn't exist create it with minimal information + qAcnt = &utils.AccountProfile{ + Tenant: tnt, + ID: acntID, + } + } + for _, dk := range diktats { + // check if we have a valid path(e.g. *balance.Test.ID) + path := strings.Split(dk.Path, utils.NestingSep) + // check the path to be a valid one + + switch path[0] { + case utils.MetaBalance: + if len(path) < 3 { + return utils.ErrWrongPath + } + bal, has := qAcnt.Balances[path[1]] + if !has { + // no balance for that ID create one + bal = utils.NewDefaultBalance(path[1]) + if qAcnt.Balances == nil { + // in case the account has no balance create the balance map + qAcnt.Balances = make(map[string]*utils.Balance) + } + qAcnt.Balances[path[1]] = bal + } + if err = actSetBalance(bal, path[2:], dk.Value, reset); err != nil { + return + } + case utils.MetaAccount: + // special case in order to handle account field set in *set_balance/*add_balance action + if len(path) < 2 { + return utils.ErrWrongPath + } + if err = actSetAccountFields(qAcnt, path[1:], dk.Value); err != nil { + return + } + default: + return utils.ErrWrongPath + } + } + return dm.SetAccountProfile(qAcnt, false) +} + +// actSetAccountFields sets the fields inside the account +func actSetAccountFields(ac *utils.AccountProfile, path []string, value string) (err error) { + switch path[0] { + // the tenant and ID should come from user and should not change + case utils.FilterIDs: + ac.FilterIDs = utils.NewStringSet(strings.Split(value, utils.InfieldSep)).AsSlice() + case utils.ActivationIntervalString: + // similar how the TP are loaded split the value based on ; + // the first element is ActivationTime and the second if any ExpiryTime + ac.ActivationInterval = &utils.ActivationInterval{} + valSpl := strings.SplitN(value, utils.InfieldSep, 2) + if ac.ActivationInterval.ActivationTime, err = utils.ParseTimeDetectLayout(valSpl[0], utils.EmptyString); err != nil { + return + } + if len(valSpl) == 2 { + ac.ActivationInterval.ExpiryTime, err = utils.ParseTimeDetectLayout(valSpl[1], utils.EmptyString) + } + case utils.Weights: + ac.Weights, err = utils.NewDynamicWeightsFromString(value, utils.InfieldSep, utils.ANDSep) + case utils.Opts: + if ac.Opts == nil { // if the options are not initialized already init them here + ac.Opts = make(map[string]interface{}) + } + err = utils.MapStorage(ac.Opts).Set(path[1:], value) + case utils.ThresholdIDs: + ac.ThresholdIDs = utils.NewStringSet(strings.Split(value, utils.InfieldSep)).AsSlice() + default: + err = utils.ErrWrongPath + } + return +} + +// actSetBalance will set the field at path from balance with value +// value is string as the value received from action is string +// the balance must not be nil +func actSetBalance(bal *utils.Balance, path []string, value string, reset bool) (err error) { + // check if we have path past *balance + if len(path) == 0 { + return utils.ErrWrongPath + } + // select what field is update based on the first value from path + // special case for CostIncrements and UnitFactors + // that are converted from string similar to how are loaded from CSVs + switch path[0] { + case utils.ID: + bal.ID = value + case utils.FilterIDs: + if value != utils.EmptyString { + bal.FilterIDs = utils.NewStringSet(strings.Split(value, utils.InfieldSep)).AsSlice() + } + case utils.Weights: + if value != utils.EmptyString { + bal.Weights, err = utils.NewDynamicWeightsFromString(value, utils.InfieldSep, utils.ANDSep) + } + case utils.Type: + bal.Type = value + case utils.Units: + var z *utils.Decimal + if z, err = utils.NewDecimalFromString(value); err != nil { + return + } + // do not overwrite the Units if the action is *add_balance + // this flag makes the difference between the *add_balance and *set_balance actions + if !reset && bal.Units != nil { + bal.Units.Add(bal.Units.Big, z.Big) + } else { + bal.Units = z + } + case utils.UnitFactors: + // just recreate them from string + if value != utils.EmptyString { + bal.UnitFactors, err = actNewUnitFactorsFromString(value) + } + case utils.Opts: + if bal.Opts == nil { // if the options are not initilized already init them here + bal.Opts = make(map[string]interface{}) + } + err = utils.MapStorage(bal.Opts).Set(path[1:], value) + case utils.CostIncrements: + // just recreate them from string + if value != utils.EmptyString { + bal.CostIncrements, err = actNewCostIncrementsFromString(value) + } + case utils.AttributeIDs: + if value != utils.EmptyString { + bal.AttributeIDs = strings.Split(value, utils.InfieldSep) + } + // case utils.RateProfileIDs: + // if value != utils.EmptyString { + // bal.RateProfileIDs = utils.NewStringSet(strings.Split(value, utils.InfieldSep)).AsSlice() + // } + default: + // we modify the UnitFactors explicit + // e.g. *balance.TEST.UnitFactors[0].Factor + if strings.HasPrefix(path[0], utils.UnitFactors) { + bal.UnitFactors, err = actSetUnitFactor(bal.UnitFactors, path, value) + return + } + + // we modify the CostIncrements explicit + // e.g. *balance.TEST.CostIncrements[0].Increment + if strings.HasPrefix(path[0], utils.CostIncrements) { + bal.CostIncrements, err = actSetCostIncrement(bal.CostIncrements, path, value) + return + } + // not a valid path + err = utils.ErrWrongPath + } + return +} + +// actNewUnitFactorsFromString converts a string to a list of UnitFactors +// similar to the how the TP are loaded from CSV +func actNewUnitFactorsFromString(value string) (units []*utils.UnitFactor, err error) { + sls := strings.Split(value, utils.InfieldSep) + if len(sls)%2 != 0 { + return nil, fmt.Errorf("invalid key: <%s> for BalanceUnitFactors", value) + } + units = make([]*utils.UnitFactor, 0, len(sls)/2) + + for j := 0; j < len(sls); j += 2 { + var z *utils.Decimal + if z, err = utils.NewDecimalFromString(sls[j+1]); err != nil { + return + } + var fltrs []string + if sls[j] != utils.EmptyString { + fltrs = strings.Split(sls[j], utils.ANDSep) + } + units = append(units, &utils.UnitFactor{ + FilterIDs: fltrs, + Factor: z, + }) + } + return +} + +// actNewCostIncrementsFromString converts a string to a list of CostIncrements +// similar to the how the TP are loaded from CSV +func actNewCostIncrementsFromString(value string) (costs []*utils.CostIncrement, err error) { + sls := strings.Split(value, utils.InfieldSep) + if len(sls)%4 != 0 { + return nil, fmt.Errorf("invalid key: <%s> for BalanceCostIncrements", value) + } + costs = make([]*utils.CostIncrement, 0, len(sls)/4) + for j := 0; j < len(sls); j += 4 { + cost := &utils.CostIncrement{} + if sls[j] != utils.EmptyString { + cost.FilterIDs = strings.Split(sls[j], utils.ANDSep) + } + if incrementStr := sls[j+1]; incrementStr != utils.EmptyString { + if cost.Increment, err = utils.NewDecimalFromString(incrementStr); err != nil { + return + } + } + if fixedFeeStr := sls[j+2]; fixedFeeStr != utils.EmptyString { + if cost.FixedFee, err = utils.NewDecimalFromString(fixedFeeStr); err != nil { + return + } + } + if recurrentFeeStr := sls[j+3]; recurrentFeeStr != utils.EmptyString { + if cost.RecurrentFee, err = utils.NewDecimalFromString(recurrentFeeStr); err != nil { + return + } + } + costs = append(costs, cost) + } + return +} + +// actSetUnitFactor will update the UnitFactors +func actSetUnitFactor(uFs []*utils.UnitFactor, path []string, value string) (untFctr []*utils.UnitFactor, err error) { + pathVal := path[0][11:] + lp := len(pathVal) + // check path requierments + // exact 2 elements + // and the first element have an index between brackets + if len(path) != 2 || + pathVal[0] != '[' || + pathVal[lp-1] != ']' { + return nil, utils.ErrWrongPath + } + pathVal = pathVal[1 : lp-1] + var idx int + // convert the index from string to int + if idx, err = strconv.Atoi(pathVal); err != nil { + return + } + if len(uFs) == idx { // special case add a new unitFactor + uFs = append(uFs, &utils.UnitFactor{}) + } else if len(uFs) < idx { // make sure we are in slice range + return nil, utils.ErrWrongPath + } + + switch path[1] { + case utils.FilterIDs: + uFs[idx].FilterIDs = utils.NewStringSet(strings.Split(value, utils.InfieldSep)).AsSlice() + case utils.Factor: + uFs[idx].Factor, err = utils.NewDecimalFromString(value) + default: + err = utils.ErrWrongPath + } + return uFs, err +} + +func actSetCostIncrement(cIs []*utils.CostIncrement, path []string, value string) (cstIncr []*utils.CostIncrement, err error) { + pathVal := path[0][14:] + lp := len(pathVal) + // check path requierments + // exact 2 elements + // and the first element have an index between brackets + if len(path) != 2 || + pathVal[0] != '[' || + pathVal[lp-1] != ']' { + return nil, utils.ErrWrongPath + } + pathVal = pathVal[1 : lp-1] + var idx int + // convert the index from string to int + if idx, err = strconv.Atoi(pathVal); err != nil { + return + } + if len(cIs) == idx { // special case add a new CostIncrement + cIs = append(cIs, &utils.CostIncrement{}) + } else if len(cIs) < idx { // make sure we are in slice range + return nil, utils.ErrWrongPath + } + + switch path[1] { + case utils.FilterIDs: + cIs[idx].FilterIDs = utils.NewStringSet(strings.Split(value, utils.InfieldSep)).AsSlice() + case utils.Increment: + cIs[idx].Increment, err = utils.NewDecimalFromString(value) + case utils.FixedFee: + cIs[idx].FixedFee, err = utils.NewDecimalFromString(value) + case utils.RecurrentFee: + cIs[idx].RecurrentFee, err = utils.NewDecimalFromString(value) + default: + err = utils.ErrWrongPath + } + return cIs, err +} diff --git a/accounts/actsetbalance_test.go b/accounts/actsetbalance_test.go new file mode 100644 index 000000000..1d83d7da4 --- /dev/null +++ b/accounts/actsetbalance_test.go @@ -0,0 +1,456 @@ +/* +Real-time Online/Offline Charging System (OerS) 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 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package accounts + +import ( + "reflect" + "testing" + "time" + + "github.com/cgrates/cgrates/utils" + + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/ericlagergren/decimal" +) + +func TestActSetAccountBalance(t *testing.T) { + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data := engine.NewInternalDB(nil, nil, true) + + acntID := "TestActSetAccount" + diktats := []*utils.BalDiktat{ + { + Path: "*balance.Concrete1", + Value: ";10", + }, + } + + expected := "NO_DATA_BASE_CONNECTION" + if err := actSetAccount(nil, "cgrates.org", acntID, diktats, false); err == nil || err.Error() != expected { + t.Errorf("Expected %+v, received %+v", expected, err) + } + dm := engine.NewDataManager(data, cfg.CacheCfg(), nil) + + expected = "WRONG_PATH" + if err := actSetAccount(dm, "cgrates.org", acntID, diktats, false); err == nil || err.Error() != expected { + t.Errorf("Expected %+v, received %+v", expected, err) + } + diktats[0].Path = "*balance.Concrete1.NOT_A_FIELD" + + if err := actSetAccount(dm, "cgrates.org", acntID, diktats, false); err == nil || err.Error() != expected { + t.Errorf("Expected %+v, received %+v", expected, err) + } + diktats[0].Path = "*balance.Concrete1.Weights" + + expectedAcc := &utils.AccountProfile{ + Tenant: "cgrates.org", + ID: acntID, + Balances: map[string]*utils.Balance{ + "Concrete1": { + ID: "Concrete1", + Type: utils.MetaConcrete, + Units: &utils.Decimal{decimal.New(0, 0)}, + Weights: []*utils.DynamicWeight{ + { + Weight: 10, + }, + }, + CostIncrements: []*utils.CostIncrement{ + { + FilterIDs: []string{"*string:~*req.ToR:*voice"}, + Increment: utils.NewDecimal(int64(time.Second), 0), + RecurrentFee: utils.NewDecimal(0, 0), + }, + { + FilterIDs: []string{"*string:~*req.ToR:*data"}, + Increment: utils.NewDecimal(1024*1024, 0), + RecurrentFee: utils.NewDecimal(0, 0), + }, + { + FilterIDs: []string{"*string:~*req.ToR:*sms"}, + Increment: utils.NewDecimal(1, 0), + RecurrentFee: utils.NewDecimal(0, 0), + }, + }, + }, + }, + } + if err := actSetAccount(dm, "cgrates.org", acntID, diktats, false); err != nil { + t.Error(err) + } else if rcv, err := dm.GetAccountProfile("cgrates.org", acntID); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(rcv, expectedAcc) { + t.Errorf("Expected %+v, received %+v", utils.ToJSON(expectedAcc), utils.ToJSON(rcv)) + } +} + +func TestActSetAccount(t *testing.T) { + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data := engine.NewInternalDB(nil, nil, true) + dm := engine.NewDataManager(data, cfg.CacheCfg(), nil) + + acntID := "TestActSetAccount" + diktats := []*utils.BalDiktat{ + { + Path: "*accountFilterIDs", + Value: "10", + }, + } + + expected := "WRONG_PATH" + if err := actSetAccount(dm, "cgrates.org", acntID, diktats, false); err == nil || err.Error() != expected { + t.Errorf("Expected %+v, received %+v", expected, err) + } + diktats[0].Path = "*account" + + if err := actSetAccount(dm, "cgrates.org", acntID, diktats, false); err == nil || err.Error() != expected { + t.Errorf("Expected %+v, received %+v", expected, err) + } + diktats[0].Path = "*account.Weights" + + expected = "invalid DynamicWeight format for string <10>" + if err := actSetAccount(dm, "cgrates.org", acntID, diktats, false); err == nil || err.Error() != expected { + t.Errorf("Expected %+v, received %+v", expected, err) + } + diktats[0].Value = ";10" + + expectedAcc := &utils.AccountProfile{ + Tenant: "cgrates.org", + ID: acntID, + Weights: []*utils.DynamicWeight{ + { + Weight: 10, + }, + }, + } + if err := actSetAccount(dm, "cgrates.org", acntID, diktats, false); err != nil { + t.Error(err) + } else if rcv, err := dm.GetAccountProfile("cgrates.org", acntID); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(rcv, expectedAcc) { + t.Errorf("Expected %+v, received %+v", utils.ToJSON(expectedAcc), utils.ToJSON(rcv)) + } +} + +func TestActSetAccountFields(t *testing.T) { + accPrf := &utils.AccountProfile{} + + expectedAccprf := &utils.AccountProfile{ + FilterIDs: []string{"*string:~*req.ToR:*sms"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 29, 15, 0, 0, 0, time.UTC), + ExpiryTime: time.Date(2014, 8, 29, 15, 0, 0, 0, time.UTC), + }, + Weights: []*utils.DynamicWeight{ + { + Weight: 10, + }, + }, + Opts: map[string]interface{}{ + utils.AccountField: "1004", + }, + ThresholdIDs: []string{"TH_ID1"}, + } + if err := actSetAccountFields(accPrf, []string{utils.FilterIDs}, "*string:~*req.ToR:*sms"); err != nil { + t.Error(err) + } else if err := actSetAccountFields(accPrf, []string{utils.ActivationIntervalString}, "2014-07-29T15:00:00Z;2014-08-29T15:00:00Z"); err != nil { + t.Error(err) + } else if err := actSetAccountFields(accPrf, []string{utils.Weights}, ";10"); err != nil { + t.Error(err) + } else if err := actSetAccountFields(accPrf, []string{utils.Opts, utils.AccountField}, "1004"); err != nil { + t.Error(err) + } else if err := actSetAccountFields(accPrf, []string{utils.ThresholdIDs}, "TH_ID1"); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(expectedAccprf, accPrf) { + t.Errorf("Expected %+v, received %+v", utils.ToJSON(expectedAccprf), utils.ToJSON(accPrf)) + } + + expected := "Unsupported time format" + if err := actSetAccountFields(accPrf, []string{utils.ActivationIntervalString}, "not_a_time"); err == nil || err.Error() != expected { + t.Errorf("Expected %+v, received %+v", expected, err) + } + expected = "WRONG_PATH" + if err := actSetAccountFields(accPrf, []string{"not_an_account_field"}, utils.EmptyString); err == nil || err.Error() != expected { + t.Errorf("Expected %+v, received %+v", expected, err) + } +} + +func TestActSetBalanceFields(t *testing.T) { + bal := &utils.Balance{} + + expectedBal := &utils.Balance{ + ID: "TestActSetBalanceFields", + FilterIDs: []string{"*string:~*req.ToR:*sms"}, + Weights: []*utils.DynamicWeight{ + { + Weight: 10, + }, + }, + Type: utils.MetaAbstract, + Units: &utils.Decimal{decimal.New(20, 0)}, + UnitFactors: []*utils.UnitFactor{ + { + FilterIDs: []string{"fltr1"}, + Factor: &utils.Decimal{decimal.New(100, 0)}, + }, + }, + Opts: map[string]interface{}{ + utils.AccountField: "1004", + }, + CostIncrements: []*utils.CostIncrement{ + { + FilterIDs: []string{"fltr1"}, + Increment: &utils.Decimal{decimal.New(1, 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(1, 0)}, + }, + }, + AttributeIDs: []string{"ATTR_ID"}, + // RateProfileIDs: []string{"RATE_ID"}, + } + if err := actSetBalance(bal, []string{utils.ID}, "TestActSetBalanceFields", true); err != nil { + t.Error(err) + } else if err := actSetBalance(bal, []string{utils.FilterIDs}, "*string:~*req.ToR:*sms", true); err != nil { + t.Error(err) + } else if err := actSetBalance(bal, []string{utils.Weights}, ";10", true); err != nil { + t.Error(err) + } else if err := actSetBalance(bal, []string{utils.Type}, utils.MetaAbstract, true); err != nil { + t.Error(err) + } else if err := actSetBalance(bal, []string{utils.Units}, "20", true); err != nil { + t.Error(err) + } else if err := actSetBalance(bal, []string{utils.UnitFactors}, "fltr1;100", true); err != nil { + t.Error(err) + } else if err := actSetBalance(bal, []string{utils.Opts, utils.AccountField}, "1004", true); err != nil { + t.Error(err) + } else if err := actSetBalance(bal, []string{utils.CostIncrements}, "fltr1;1;0;1", true); err != nil { + t.Error(err) + } else if err := actSetBalance(bal, []string{utils.AttributeIDs}, "ATTR_ID", true); err != nil { + t.Error(err) + // } else if err := actSetBalance(bal, []string{utils.RateProfileIDs}, "RATE_ID", true); err != nil { + // t.Error(err) + } else if !reflect.DeepEqual(bal, expectedBal) { + t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expectedBal), utils.ToJSON(bal)) + } + + expected := "WRONG_PATH" + if err := actSetBalance(bal, []string{}, utils.EmptyString, true); err == nil || err.Error() != expected { + t.Errorf("Expected %+v, received %+v", expected, err) + } else if err := actSetBalance(bal, []string{"not_a_balance_field"}, utils.EmptyString, true); err == nil || err.Error() != expected { + t.Errorf("Expected %+v, received %+v", expected, err) + } else if err := actSetBalance(bal, []string{"UnitFactors[0].Factor"}, "200", true); err == nil || err.Error() != expected { + t.Errorf("Expected %+v, received %+v", expected, err) + } else if err := actSetBalance(bal, []string{"CostIncrements[0].Increment"}, "2", true); err == nil || err.Error() != expected { + t.Errorf("Expected %+v, received %+v", expected, err) + } + + expected = "can't convert to decimal" + if err := actSetBalance(bal, []string{utils.Units}, "not_converting_decimal", true); err == nil || err.Error() != expected { + t.Errorf("Expected %+v, received %+v", expected, err) + } +} + +func TestActNewUnitFactorsFromString(t *testing.T) { + value := "OneValue" + expected := "invalid key: for BalanceUnitFactors" + if _, err := actNewUnitFactorsFromString(value); err == nil || err.Error() != expected { + t.Errorf("Expected %+v, received %+v", expected, err) + } + + expected = "can't convert to decimal" + value = ";not_decimal" + if _, err := actNewUnitFactorsFromString(value); err == nil || err.Error() != expected { + t.Errorf("Expected %+v, received %+v", expected, err) + } +} + +func TestActNewCostIncrementsFromString(t *testing.T) { + value := "OneValue" + expected := "invalid key: for BalanceCostIncrements" + if _, err := actNewCostIncrementsFromString(value); err == nil || err.Error() != expected { + t.Errorf("Expected %+v, received %+v", expected, err) + } + + value = ";invalid_decimal;;" + expected = "can't convert to decimal" + if _, err := actNewCostIncrementsFromString(value); err == nil || err.Error() != expected { + t.Errorf("Expected %+v, received %+v", expected, err) + } + + value = ";;invalid_decimal;" + if _, err := actNewCostIncrementsFromString(value); err == nil || err.Error() != expected { + t.Errorf("Expected %+v, received %+v", expected, err) + } + + value = ";;;invalid_decimal" + if _, err := actNewCostIncrementsFromString(value); err == nil || err.Error() != expected { + t.Errorf("Expected %+v, received %+v", expected, err) + } +} + +func TestActSetUnitFactor(t *testing.T) { + unitFctr := []*utils.UnitFactor{ + { + Factor: &utils.Decimal{decimal.New(100, 0)}, + }, + } + path := []string{"UnitFactors[0]", utils.Factor} + value := "200" + + expected := []*utils.UnitFactor{ + { + Factor: &utils.Decimal{decimal.New(200, 0)}, + }, + } + if rcv, err := actSetUnitFactor(unitFctr, path, value); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(expected, rcv) { + t.Errorf("Expected %+v, received %+v", expected, rcv) + } + + //change the filters + unitFctr = []*utils.UnitFactor{ + { + FilterIDs: []string{"fltr1"}, + }, + } + path = []string{"UnitFactors[1]", utils.FilterIDs} + value = "fltr2" + expected = []*utils.UnitFactor{ + { + FilterIDs: []string{"fltr1"}, + }, + { + FilterIDs: []string{"fltr2"}, + }, + } + if rcv, err := actSetUnitFactor(unitFctr, path, value); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(expected, rcv) { + t.Errorf("Expected %+v, received %+v", utils.ToJSON(expected), utils.ToJSON(rcv)) + } +} + +func TestActSetUnitFactorErrors(t *testing.T) { + unitFctr := []*utils.UnitFactor{ + { + FilterIDs: []string{"fltr1"}, + }, + } + path := []string{"UnitFactors[a]", utils.FilterIDs} + value := "fltr2" + expected := "strconv.Atoi: parsing \"a\": invalid syntax" + if _, err := actSetUnitFactor(unitFctr, path, value); err == nil || err.Error() != expected { + t.Errorf("Expected %+v, received %+v", expected, err) + } + + path = []string{"UnitFactors[7]", utils.FilterIDs} + expected = "WRONG_PATH" + if _, err := actSetUnitFactor(unitFctr, path, value); err == nil || err.Error() != expected { + t.Errorf("Expected %+v, received %+v", expected, err) + } + + path = []string{"UnitFactors[0]", "not_a_field"} + expected = "WRONG_PATH" + if _, err := actSetUnitFactor(unitFctr, path, value); err == nil || err.Error() != expected { + t.Errorf("Expected %+v, received %+v", expected, err) + } +} + +func TestActSetCostIncrement(t *testing.T) { + costIncr := []*utils.CostIncrement{ + { + FilterIDs: []string{"fltr1"}, + Increment: &utils.Decimal{decimal.New(1, 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(1, 0)}, + }, + } + + expected := []*utils.CostIncrement{ + { + FilterIDs: []string{"fltr1"}, + Increment: &utils.Decimal{decimal.New(1, 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(2, 0)}, + }, + } + if rcv, err := actSetCostIncrement(costIncr, []string{"CostIncrements[0]", utils.RecurrentFee}, "2"); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(expected, rcv) { + t.Errorf("Expected %+v, received %+v", utils.ToJSON(expected), utils.ToJSON(rcv)) + } + + expected[0].FilterIDs = []string{"fltr2"} + if rcv, err := actSetCostIncrement(costIncr, []string{"CostIncrements[0]", utils.FilterIDs}, "fltr2"); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(expected, rcv) { + t.Errorf("Expected %+v, received %+v", utils.ToJSON(expected), utils.ToJSON(rcv)) + } + + expected[0].FixedFee = &utils.Decimal{decimal.New(1, 0)} + if rcv, err := actSetCostIncrement(costIncr, []string{"CostIncrements[0]", utils.FixedFee}, "1"); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(expected, rcv) { + t.Errorf("Expected %+v, received %+v", utils.ToJSON(expected), utils.ToJSON(rcv)) + } + + expected[0].Increment = &utils.Decimal{decimal.New(2, 0)} + if rcv, err := actSetCostIncrement(costIncr, []string{"CostIncrements[0]", utils.Increment}, "2"); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(expected, rcv) { + t.Errorf("Expected %+v, received %+v", utils.ToJSON(expected), utils.ToJSON(rcv)) + } + + expected = []*utils.CostIncrement{ + { + FilterIDs: []string{"fltr2"}, + Increment: &utils.Decimal{decimal.New(2, 0)}, + FixedFee: &utils.Decimal{decimal.New(1, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(2, 0)}, + }, + { + Increment: &utils.Decimal{decimal.New(2, 0)}, + }, + } + if rcv, err := actSetCostIncrement(costIncr, []string{"CostIncrements[1]", utils.Increment}, "2"); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(expected, rcv) { + t.Errorf("Expected %+v, received %+v", utils.ToJSON(expected), utils.ToJSON(rcv)) + } +} + +func TestActSetCostIncrementErrors(t *testing.T) { + costIncr := []*utils.CostIncrement{} + expected := "strconv.Atoi: parsing \"a\": invalid syntax" + if _, err := actSetCostIncrement(costIncr, []string{"CostIncrements[a]", utils.Increment}, "2"); err == nil || err.Error() != expected { + t.Errorf("Expected %+v, received %+v", expected, err) + } + + expected = "WRONG_PATH" + if _, err := actSetCostIncrement(costIncr, []string{"CostIncrements[8]", utils.Increment}, "2"); err == nil || err.Error() != expected { + t.Errorf("Expected %+v, received %+v", expected, err) + } + + if _, err := actSetCostIncrement(costIncr, []string{"CostIncrements[0]", "not_a_field"}, "2"); err == nil || err.Error() != expected { + t.Errorf("Expected %+v, received %+v", expected, err) + } +} diff --git a/accounts/concretebalance.go b/accounts/concretebalance.go new file mode 100644 index 000000000..77846bcdf --- /dev/null +++ b/accounts/concretebalance.go @@ -0,0 +1,173 @@ +/* +Real-time Online/Offline Charging System (OerS) 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 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package accounts + +import ( + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" + "github.com/ericlagergren/decimal" +) + +// cloneUnitsFromConcretes returns cloned units from the concrete balances passed as parameters +func cloneUnitsFromConcretes(cBs []*concreteBalance) (clnedUnts []*utils.Decimal) { + if cBs == nil { + return + } + clnedUnts = make([]*utils.Decimal, len(cBs)) + for i := range cBs { + clnedUnts[i] = cBs[i].blnCfg.Units.Clone() + } + return +} + +// restoreUnitsFromClones will restore the units from the clones +func restoreUnitsFromClones(cBs []*concreteBalance, clnedUnts []*utils.Decimal) { + for i, clnedUnt := range clnedUnts { + cBs[i].blnCfg.Units.Big = clnedUnt.Big + } +} + +// newConcreteBalance constructs a concreteBalanceOperator +func newConcreteBalanceOperator(acntID string, blnCfg *utils.Balance, + fltrS *engine.FilterS, connMgr *engine.ConnManager, + attrSConns, rateSConns []string) balanceOperator { + return &concreteBalance{acntID, blnCfg, fltrS, connMgr, attrSConns, rateSConns} +} + +// concreteBalance is the operator for *concrete balance type +type concreteBalance struct { + acntID string + blnCfg *utils.Balance + fltrS *engine.FilterS + connMgr *engine.ConnManager + attrSConns, + rateSConns []string +} + +// debitAbstracts implements the balanceOperator interface +func (cB *concreteBalance) debitAbstracts(aUnits *decimal.Big, + cgrEv *utils.CGREvent) (ec *utils.EventCharges, err error) { + evNm := cgrEv.AsDataProvider() + // pass the general balance filters + var pass bool + if pass, err = cB.fltrS.Pass(cgrEv.Tenant, cB.blnCfg.FilterIDs, evNm); err != nil { + return + } else if !pass { + return nil, utils.ErrFilterNotPassingNoCaps + } + + // costIncrement + // var costIcrm *utils.CostIncrement + // if costIcrm, err = costIncrement(cB.blnCfg.CostIncrements, + // cB.fltrS, cgrEv.Tenant, evNm); err != nil { + // return + // } + // if ec, err = maxDebitAbstractsFromConcretes(aUnits, + // cB.acntID, []*concreteBalance{cB}, + // cB.connMgr, cgrEv, + // cB.attrSConns, cB.blnCfg.AttributeIDs, + // cB.rateSConns, cB.blnCfg.RateProfileIDs, + // costIcrm); err != nil { + // return + // } + return +} + +// debitConcretes implements the balanceOperator interface +func (cB *concreteBalance) debitConcretes(cUnits *decimal.Big, + cgrEv *utils.CGREvent) (ec *utils.EventCharges, err error) { + evNm := cgrEv.AsDataProvider() + // pass the general balance filters + var pass bool + if pass, err = cB.fltrS.Pass(cgrEv.Tenant, cB.blnCfg.FilterIDs, evNm); err != nil { + return + } else if !pass { + return nil, utils.ErrFilterNotPassingNoCaps + } + + // unitFactor + var uF *utils.UnitFactor + if uF, err = unitFactor(cB.blnCfg.UnitFactors, cB.fltrS, cgrEv.Tenant, evNm); err != nil { + return + } + var hasUF bool + if uF != nil && uF.Factor.Cmp(decimal.New(1, 0)) != 0 { + hasUF = true + cUnits = utils.MultiplyBig(cUnits, uF.Factor.Big) + } + + // balanceLimit + var hasLmt bool + var blncLmt *utils.Decimal + if blncLmt, err = balanceLimit(cB.blnCfg.Opts); err != nil { + return + } + if blncLmt != nil && blncLmt.Big.Cmp(decimal.New(0, 0)) != 0 { + cB.blnCfg.Units.Big = utils.SubstractBig(cB.blnCfg.Units.Big, blncLmt.Big) + hasLmt = true + } + var dbted *decimal.Big + if cB.blnCfg.Units.Big.Cmp(cUnits) <= 0 && blncLmt != nil { // balance smaller than debit and limited + dbted = cB.blnCfg.Units.Big + cB.blnCfg.Units.Big = blncLmt.Big + } else { + cB.blnCfg.Units.Big = utils.SubstractBig(cB.blnCfg.Units.Big, cUnits) + if hasLmt { // put back the limit + cB.blnCfg.Units.Big = utils.SumBig(cB.blnCfg.Units.Big, blncLmt.Big) + } + dbted = cUnits + } + if hasUF { + dbted = utils.DivideBig(dbted, uF.Factor.Big) + } + if dbted.Cmp(decimal.New(0, 0)) == 0 { + return // no event cost for 0 debit + } + // EventCharges + ec = utils.NewEventCharges() + ec.Concretes = &utils.Decimal{dbted} + // UnitFactors + var ufID string + if hasUF { + ufID = utils.UUIDSha1Prefix() + ec.UnitFactors[ufID] = uF + } + acntID := utils.UUIDSha1Prefix() + ec.Accounting[acntID] = &utils.AccountCharge{ + AccountID: cB.acntID, + BalanceID: cB.blnCfg.ID, + Units: &utils.Decimal{dbted}, + BalanceLimit: blncLmt, + UnitFactorID: ufID, + } + ec.ChargingIntervals = []*utils.ChargingInterval{ + { + Increments: []*utils.ChargingIncrement{ + { + Units: &utils.Decimal{dbted}, + AccountChargeID: acntID, + CompressFactor: 1, + }, + }, + CompressFactor: 1, + }, + } + + return +} diff --git a/accounts/concretebalance_test.go b/accounts/concretebalance_test.go new file mode 100644 index 000000000..47f807bbb --- /dev/null +++ b/accounts/concretebalance_test.go @@ -0,0 +1,832 @@ +/* +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 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package accounts + +import ( + "reflect" + "testing" + + "github.com/cgrates/cgrates/config" + + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" + "github.com/ericlagergren/decimal" +) + +func TestCBDebitUnits(t *testing.T) { + // with limit and unit factor + cb := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "TestCBDebitUnits", + Type: utils.MetaConcrete, + Opts: map[string]interface{}{ + utils.MetaBalanceLimit: -200.0, + }, + UnitFactors: []*utils.UnitFactor{ + { + Factor: utils.NewDecimal(100, 0), // EuroCents + }, + }, + Units: utils.NewDecimal(500, 0), // 500 EURcents + }, + fltrS: new(engine.FilterS), + } + cgrEvent := &utils.CGREvent{ + Tenant: "cgrates.org", + } + unitFct := &utils.UnitFactor{ + Factor: utils.NewDecimal(100, 0), + } + toDebit := utils.NewDecimal(6, 0) + if evChrgr, err := cb.debitConcretes(toDebit.Big, + cgrEvent); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(cb.blnCfg.UnitFactors[0], unitFct) { + t.Errorf("received unit factor: %+v", unitFct) + } else if evChrgr.Concretes.Compare(toDebit) != 0 { + t.Errorf("debited: %s", evChrgr.Concretes) + } else if cb.blnCfg.Units.Cmp(decimal.New(-100, 0)) != 0 { + t.Errorf("balance remaining: %s", cb.blnCfg.Units) + } + + //with increment and not enough balance + cb = &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "TestCBDebitUnits", + Type: utils.MetaConcrete, + Opts: map[string]interface{}{ + utils.MetaBalanceLimit: -1.0, + }, + Units: utils.NewDecimal(125, 2), // 1.25 + }, + fltrS: new(engine.FilterS), + } + toDebit = utils.NewDecimal(25, 1) //2.5 + if evChrgr, err := cb.debitConcretes(toDebit.Big, + cgrEvent); err != nil { + t.Error(err) + } else if evChrgr.Concretes.Cmp(decimal.New(225, 2)) != 0 { // 2.25 debited + t.Errorf("debited: %s", evChrgr.Concretes) + } else if cb.blnCfg.Units.Cmp(decimal.New(-1, 0)) != 0 { + t.Errorf("balance remaining: %s", cb.blnCfg.Units) + } + + //with increment and unlimited balance + cb = &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "TestCBDebitUnits", + Type: utils.MetaConcrete, + Opts: map[string]interface{}{ + utils.MetaBalanceUnlimited: true, + }, + Units: utils.NewDecimal(125, 2), // 1.25 + }, + fltrS: new(engine.FilterS), + } + toDebit = utils.NewDecimal(25, 1) // 2.5 + if evChrgr, err := cb.debitConcretes(toDebit.Big, + cgrEvent); err != nil { + t.Error(err) + } else if evChrgr.Concretes.Cmp(decimal.New(25, 1)) != 0 { // debit more than available since we have unlimited + t.Errorf("debited: %s", evChrgr.Concretes) + } else if cb.blnCfg.Units.Cmp(decimal.New(-125, 2)) != 0 { + t.Errorf("balance remaining: %s", cb.blnCfg.Units) + } + + //with increment and positive limit + cb = &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "TestCBDebitUnits", + Type: utils.MetaConcrete, + Opts: map[string]interface{}{ + utils.MetaBalanceLimit: 0.5, // 0.5 as limit + }, + Units: utils.NewDecimal(125, 2), // 1.25 + }, + fltrS: new(engine.FilterS), + } + toDebit = utils.NewDecimal(25, 1) //2.5 + if evChrgr, err := cb.debitConcretes(toDebit.Big, + cgrEvent); err != nil { + t.Error(err) + } else if evChrgr.Concretes.Cmp(decimal.New(75, 2)) != 0 { // limit is 0.5 + t.Errorf("debited: %s", evChrgr.Concretes) + } else if cb.blnCfg.Units.Cmp(decimal.New(5, 1)) != 0 { + t.Errorf("balance remaining: %s", cb.blnCfg.Units) + } +} + +func TestCBSimpleDebit(t *testing.T) { + // debit 10 units from a concrete balance with 500 units + cb := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "CB", + Type: utils.MetaConcrete, + Units: utils.NewDecimal(500, 0), // 500 Units + }, + fltrS: new(engine.FilterS), + } + toDebit := utils.NewDecimal(10, 0) + if evChrgr, err := cb.debitConcretes(toDebit.Big, + new(utils.CGREvent)); err != nil { + t.Error(err) + } else if evChrgr.Concretes.Compare(toDebit) != 0 { + t.Errorf("debited: %s", evChrgr.Concretes) + } else if cb.blnCfg.Units.Cmp(decimal.New(490, 0)) != 0 { + t.Errorf("balance remaining: %s", cb.blnCfg.Units) + } +} + +func TestCBDebitExceed(t *testing.T) { + // debit 510 units from a concrete balance with 500 units + cb := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "CB", + Type: utils.MetaConcrete, + Units: utils.NewDecimal(500, 0), // 500 Units + }, + fltrS: new(engine.FilterS), + } + if evChrgr, err := cb.debitConcretes(utils.NewDecimal(510, 0).Big, + new(utils.CGREvent)); err != nil { + t.Error(err) + } else if evChrgr.Concretes.Compare(utils.NewDecimal(500, 0)) != 0 { + t.Errorf("debited: %s", evChrgr.Concretes) + } else if cb.blnCfg.Units.Cmp(decimal.New(0, 0)) != 0 { + t.Errorf("balance remaining: %s", cb.blnCfg.Units) + } +} + +func TestCBDebitUnlimited(t *testing.T) { + // debit 510 units from an unlimited concrete balance with 100 units + cb := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "CB", + Type: utils.MetaConcrete, + Opts: map[string]interface{}{ + utils.MetaBalanceUnlimited: true, + }, + Units: utils.NewDecimal(100, 0), + }, + fltrS: new(engine.FilterS), + } + if evChrgr, err := cb.debitConcretes(utils.NewDecimal(510, 0).Big, + new(utils.CGREvent)); err != nil { + t.Error(err) + } else if evChrgr.Concretes.Compare(utils.NewDecimal(510, 0)) != 0 { + t.Errorf("debited: %s", evChrgr.Concretes) + } else if cb.blnCfg.Units.Cmp(decimal.New(-410, 0)) != 0 { + t.Errorf("balance remaining: %s", cb.blnCfg.Units) + } +} + +func TestCBDebitLimit(t *testing.T) { + // debit 190 units from a concrete balance with 500 units and limit of 300 + cb := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "CB", + Type: utils.MetaConcrete, + Opts: map[string]interface{}{ + utils.MetaBalanceLimit: 300.0, // 300 as limit + }, + Units: utils.NewDecimal(500, 0), // 500 Units + }, + fltrS: new(engine.FilterS), + } + toDebit := utils.NewDecimal(190, 0) + if evChrgr, err := cb.debitConcretes(toDebit.Big, + new(utils.CGREvent)); err != nil { + t.Error(err) + } else if evChrgr.Concretes.Compare(toDebit) != 0 { + t.Errorf("debited: %s", evChrgr.Concretes) + } else if cb.blnCfg.Units.Cmp(decimal.New(310, 0)) != 0 { + t.Errorf("balance remaining: %s", cb.blnCfg.Units) + } +} + +func TestCBDebitLimitExceed(t *testing.T) { + // debit 210 units from a concrete balance with 500 units and limit of 300 + cb := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "CB", + Type: utils.MetaConcrete, + Opts: map[string]interface{}{ + utils.MetaBalanceLimit: 300.0, // 300 as limit + }, + Units: utils.NewDecimal(500, 0), // 500 Units + }, + fltrS: new(engine.FilterS), + } + if evChrgr, err := cb.debitConcretes(utils.NewDecimal(210, 0).Big, + new(utils.CGREvent)); err != nil { + t.Error(err) + } else if evChrgr.Concretes.Compare(utils.NewDecimal(200, 0)) != 0 { + t.Errorf("debited: %s", evChrgr.Concretes) + } else if cb.blnCfg.Units.Cmp(decimal.New(300, 0)) != 0 { + t.Errorf("balance remaining: %s", cb.blnCfg.Units) + } +} + +func TestCBDebitLimitExceed2(t *testing.T) { + // debit 510 units from a concrete balance with 500 units but because of limit it will debit only 200 + cb := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "CB", + Type: utils.MetaConcrete, + Opts: map[string]interface{}{ + utils.MetaBalanceLimit: 300.0, // 300 as limit + }, + Units: utils.NewDecimal(500, 0), // 500 Units + }, + fltrS: new(engine.FilterS), + } + if evChrgr, err := cb.debitConcretes(utils.NewDecimal(510, 0).Big, + new(utils.CGREvent)); err != nil { + t.Error(err) + } else if evChrgr.Concretes.Compare(utils.NewDecimal(200, 0)) != 0 { + t.Errorf("debited: %s", evChrgr.Concretes) + } else if cb.blnCfg.Units.Cmp(decimal.New(300, 0)) != 0 { + t.Errorf("balance remaining: %s", cb.blnCfg.Units) + } +} + +func TestCBDebitWithUnitFactor(t *testing.T) { + // debit 1 unit from balance but because of unit factor it will debit 100 + cb := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "CB", + Type: utils.MetaConcrete, + UnitFactors: []*utils.UnitFactor{ + &utils.UnitFactor{ + Factor: utils.NewDecimal(100, 0), + }, + }, + Units: utils.NewDecimal(500, 0), // 500 Units + }, + fltrS: new(engine.FilterS), + } + toDebit := utils.NewDecimal(1, 0) + if evChrgr, err := cb.debitConcretes(toDebit.Big, + new(utils.CGREvent)); err != nil { + t.Error(err) + } else if evChrgr.Concretes.Compare(toDebit) != 0 { + t.Errorf("debited: %s", evChrgr.Concretes) + } else if cb.blnCfg.Units.Cmp(decimal.New(400, 0)) != 0 { + t.Errorf("balance remaining: %s", cb.blnCfg.Units) + } +} + +func TestCBDebitWithUnitFactorWithLimit(t *testing.T) { + // debit 3 units from balance but because of unit factor and limit it will debit 200 + cb := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "CB", + Type: utils.MetaConcrete, + UnitFactors: []*utils.UnitFactor{ + &utils.UnitFactor{ + Factor: utils.NewDecimal(100, 0), + }, + }, + Opts: map[string]interface{}{ + utils.MetaBalanceLimit: 300.0, // 300 as limit + }, + Units: utils.NewDecimal(500, 0), // 500 Units + }, + fltrS: new(engine.FilterS), + } + if evChrgr, err := cb.debitConcretes(utils.NewDecimal(3, 0).Big, + new(utils.CGREvent)); err != nil { + t.Error(err) + } else if evChrgr.Concretes.Compare(utils.NewDecimal(2, 0)) != 0 { + t.Errorf("debited: %s", evChrgr.Concretes) + } else if cb.blnCfg.Units.Cmp(decimal.New(300, 0)) != 0 { + t.Errorf("balance remaining: %s", cb.blnCfg.Units) + } +} + +func TestCBDebitWithUnitFactorWithUnlimited(t *testing.T) { + // debit 3 units from balance but because of unit factor and limit it will debit 200 + cb := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "CB", + Type: utils.MetaConcrete, + UnitFactors: []*utils.UnitFactor{ + &utils.UnitFactor{ + Factor: utils.NewDecimal(100, 0), + }, + }, + Opts: map[string]interface{}{ + utils.MetaBalanceUnlimited: true, + }, + Units: utils.NewDecimal(500, 0), // 500 Units + }, + fltrS: new(engine.FilterS), + } + if evChrgr, err := cb.debitConcretes(utils.NewDecimal(7, 0).Big, + new(utils.CGREvent)); err != nil { + t.Error(err) + } else if evChrgr.Concretes.Compare(utils.NewDecimal(7, 0)) != 0 { + t.Errorf("debited: %s", evChrgr.Concretes) + } else if cb.blnCfg.Units.Cmp(decimal.New(-200, 0)) != 0 { + t.Errorf("balance remaining: %s", cb.blnCfg.Units) + } +} + +func TestCBDebitWithUnitFactorWithFilters1(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + data := engine.NewInternalDB(nil, nil, true) + dm := engine.NewDataManager(data, config.CgrConfig().CacheCfg(), nil) + filterS := engine.NewFilterS(cfg, nil, dm) + // debit 100 units from a balance ( the unit factor doesn't match ) + cb := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "CB", + Type: utils.MetaConcrete, + UnitFactors: []*utils.UnitFactor{ + &utils.UnitFactor{ + FilterIDs: []string{"*string:~*req.CustomField:CustomValue"}, + Factor: utils.NewDecimal(100, 0), + }, + }, + Opts: map[string]interface{}{ + utils.MetaBalanceUnlimited: true, + }, + Units: utils.NewDecimal(500, 0), // 500 Units + }, + fltrS: filterS, + } + + cgrEvent := &utils.CGREvent{ + Event: map[string]interface{}{ + "CustomField": "CustomValueee", + }, + } + if evChrgr, err := cb.debitConcretes(utils.NewDecimal(100, 0).Big, + cgrEvent); err != nil { + t.Error(err) + } else if evChrgr.Concretes.Compare(utils.NewDecimal(100, 0)) != 0 { + t.Errorf("debited: %s", evChrgr.Concretes) + } else if cb.blnCfg.Units.Cmp(decimal.New(400, 0)) != 0 { + t.Errorf("balance remaining: %s", cb.blnCfg.Units) + } +} + +func TestCBDebitWithUnitFactorWithFiltersWithLimit(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + data := engine.NewInternalDB(nil, nil, true) + dm := engine.NewDataManager(data, config.CgrConfig().CacheCfg(), nil) + filterS := engine.NewFilterS(cfg, nil, dm) + // debit 100 units from a balance ( the unit factor match) + cb := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "CB", + Type: utils.MetaConcrete, + UnitFactors: []*utils.UnitFactor{ + &utils.UnitFactor{ + FilterIDs: []string{"*string:~*req.CustomField:CustomValue"}, + Factor: utils.NewDecimal(100, 0), + }, + }, + Opts: map[string]interface{}{ + utils.MetaBalanceLimit: 300.0, // 300 as limit + }, + Units: utils.NewDecimal(500, 0), // 500 Units + }, + fltrS: filterS, + } + cgrEvent := &utils.CGREvent{ + Event: map[string]interface{}{ + "CustomField": "CustomValue", + }, + } + if evChrgr, err := cb.debitConcretes(utils.NewDecimal(3, 0).Big, + cgrEvent); err != nil { + t.Error(err) + } else if evChrgr.Concretes.Compare(utils.NewDecimal(2, 0)) != 0 { + t.Errorf("debited: %s", evChrgr.Concretes) + } else if cb.blnCfg.Units.Cmp(decimal.New(300, 0)) != 0 { + t.Errorf("balance remaining: %s", cb.blnCfg.Units) + } +} + +func TestCBDebitWithMultipleUnitFactor(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + data := engine.NewInternalDB(nil, nil, true) + dm := engine.NewDataManager(data, config.CgrConfig().CacheCfg(), nil) + filterS := engine.NewFilterS(cfg, nil, dm) + // debit 100 units from a balance ( the unit factor doesn't match ) + cb := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "CB", + Type: utils.MetaConcrete, + UnitFactors: []*utils.UnitFactor{ + &utils.UnitFactor{ + FilterIDs: []string{"*string:~*req.CustomField:CustomValue"}, + Factor: utils.NewDecimal(100, 0), + }, + &utils.UnitFactor{ + FilterIDs: []string{"*string:~*req.CustomField2:CustomValue2"}, + Factor: utils.NewDecimal(50, 0), + }, + }, + Units: utils.NewDecimal(500, 0), // 500 Units + }, + fltrS: filterS, + } + cgrEvent := &utils.CGREvent{ + Event: map[string]interface{}{ + "CustomField2": "CustomValue2", + }, + } + if evChrgr, err := cb.debitConcretes(utils.NewDecimal(3, 0).Big, + cgrEvent); err != nil { + t.Error(err) + } else if evChrgr.Concretes.Compare(utils.NewDecimal(3, 0)) != 0 { + t.Errorf("debited: %s", evChrgr.Concretes) + } else if cb.blnCfg.Units.Cmp(decimal.New(350, 0)) != 0 { + t.Errorf("balance remaining: %s", cb.blnCfg.Units) + } +} + +func TestCBDebitWithBalanceFilter(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + data := engine.NewInternalDB(nil, nil, true) + dm := engine.NewDataManager(data, config.CgrConfig().CacheCfg(), nil) + filterS := engine.NewFilterS(cfg, nil, dm) + // debit 3 units from a balance (the filter match) + cb := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "CB", + FilterIDs: []string{"*string:~*req.CustomField:CustomValue"}, + Type: utils.MetaConcrete, + Units: utils.NewDecimal(500, 0), // 500 Units + }, + fltrS: filterS, + } + cgrEvent := &utils.CGREvent{ + Event: map[string]interface{}{ + "CustomField": "CustomValue", + }, + } + if evChrgr, err := cb.debitConcretes(utils.NewDecimal(3, 0).Big, + cgrEvent); err != nil { + t.Error(err) + } else if evChrgr.Concretes.Compare(utils.NewDecimal(3, 0)) != 0 { + t.Errorf("debited: %s", evChrgr.Concretes) + } else if cb.blnCfg.Units.Cmp(decimal.New(497, 0)) != 0 { + t.Errorf("balance remaining: %s", cb.blnCfg.Units) + } +} + +func TestCBDebitWithBalanceFilterNotPassing(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + data := engine.NewInternalDB(nil, nil, true) + dm := engine.NewDataManager(data, config.CgrConfig().CacheCfg(), nil) + filterS := engine.NewFilterS(cfg, nil, dm) + // filter doesn't match ) + cb := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "CB", + FilterIDs: []string{"*string:~*req.CustomField2:CustomValue2"}, + Type: utils.MetaConcrete, + Units: utils.NewDecimal(500, 0), // 500 Units + }, + fltrS: filterS, + } + cgrEvent := &utils.CGREvent{ + Event: map[string]interface{}{ + "CustomField": "CustomValue", + }, + } + if _, err := cb.debitConcretes(utils.NewDecimal(3, 0).Big, + cgrEvent); err == nil || err != utils.ErrFilterNotPassingNoCaps { + t.Error(err) + } +} + +func TestCBDebitWithBalanceInvalidFilter(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + data := engine.NewInternalDB(nil, nil, true) + dm := engine.NewDataManager(data, config.CgrConfig().CacheCfg(), nil) + filterS := engine.NewFilterS(cfg, nil, dm) + // debit 100 units from a balance ( the unit factor doesn't match ) + cb := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "CB", + FilterIDs: []string{"*string"}, + Type: utils.MetaConcrete, + Units: utils.NewDecimal(500, 0), // 500 Units + }, + fltrS: filterS, + } + cgrEvent := &utils.CGREvent{ + Event: map[string]interface{}{ + "CustomField": "CustomValue", + }, + } + if _, err := cb.debitConcretes(utils.NewDecimal(3, 0).Big, + cgrEvent); err == nil || err.Error() != "inline parse error for string: <*string>" { + t.Error(err) + } +} + +func TestCBDebitWithInvalidUnitFactorFilter(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + data := engine.NewInternalDB(nil, nil, true) + dm := engine.NewDataManager(data, config.CgrConfig().CacheCfg(), nil) + filterS := engine.NewFilterS(cfg, nil, dm) + // debit 100 units from a balance ( the unit factor doesn't match ) + cb := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "CB", + Type: utils.MetaConcrete, + UnitFactors: []*utils.UnitFactor{ + &utils.UnitFactor{ + FilterIDs: []string{"*string"}, + Factor: utils.NewDecimal(100, 0), + }, + }, + Units: utils.NewDecimal(500, 0), // 500 Units + }, + fltrS: filterS, + } + cgrEvent := &utils.CGREvent{ + Event: map[string]interface{}{ + "CustomField": "CustomValue", + }, + } + if _, err := cb.debitConcretes(utils.NewDecimal(3, 0).Big, + cgrEvent); err == nil || err.Error() != "inline parse error for string: <*string>" { + t.Error(err) + } +} + +func TestCBDebitWithInvalidLimit(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + data := engine.NewInternalDB(nil, nil, true) + dm := engine.NewDataManager(data, config.CgrConfig().CacheCfg(), nil) + filterS := engine.NewFilterS(cfg, nil, dm) + // debit 100 units from a balance ( the unit factor doesn't match ) + cb := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "CB", + Type: utils.MetaConcrete, + Opts: map[string]interface{}{ + utils.MetaBalanceLimit: "invalid", + }, + Units: utils.NewDecimal(500, 0), // 500 Units + }, + fltrS: filterS, + } + cgrEvent := &utils.CGREvent{ + Event: map[string]interface{}{ + "CustomField": "CustomValue", + }, + } + if _, err := cb.debitConcretes(utils.NewDecimal(3, 0).Big, + cgrEvent); err == nil || err.Error() != "unsupported *balanceLimit format" { + t.Error(err) + } +} + +// func TestCBSDebitAbstracts(t *testing.T) { +// // debit 10 units from a concrete balance with 500 units +// cb := &concreteBalance{ +// blnCfg: &utils.Balance{ +// ID: "CB", +// Type: utils.MetaConcrete, +// Units: utils.NewDecimal(500, 0), // 500 Units +// CostIncrements: []*utils.CostIncrement{ +// { +// Increment: utils.NewDecimal(5, 0), +// RecurrentFee: utils.NewDecimal(1, 0), +// }, +// }, +// }, +// fltrS: new(engine.FilterS), +// } +// toDebit := decimal.New(10, 0) +// if dbted, err := cb.debitAbstracts(toDebit, +// new(utils.CGREvent)); err != nil { +// t.Error(err) +// } else if dbted.Abstracts.Big.Cmp(toDebit) != 0 { +// t.Errorf("debited: %+v", dbted) +// } else if cb.blnCfg.Units.Cmp(decimal.New(498, 0)) != 0 { +// t.Errorf("balance remaining: %s", cb.blnCfg.Units) +// } +// } + +func TestCBSDebitAbstractsInvalidFilter(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + data := engine.NewInternalDB(nil, nil, true) + dm := engine.NewDataManager(data, config.CgrConfig().CacheCfg(), nil) + filterS := engine.NewFilterS(cfg, nil, dm) + // debit 10 units from a concrete balance with 500 units + cb := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "CB", + Type: utils.MetaConcrete, + Units: utils.NewDecimal(500, 0), // 500 Units + FilterIDs: []string{"*string"}, + CostIncrements: []*utils.CostIncrement{ + { + Increment: utils.NewDecimal(5, 0), + RecurrentFee: utils.NewDecimal(1, 0), + }, + }, + }, + fltrS: filterS, + } + toDebit := decimal.New(10, 0) + if _, err := cb.debitAbstracts(toDebit, new(utils.CGREvent)); err == nil || + err.Error() != "inline parse error for string: <*string>" { + t.Error(err) + } +} + +func TestCBSDebitAbstractsNoMatchFilter(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + data := engine.NewInternalDB(nil, nil, true) + dm := engine.NewDataManager(data, config.CgrConfig().CacheCfg(), nil) + filterS := engine.NewFilterS(cfg, nil, dm) + // debit 10 units from a concrete balance with 500 units + cb := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "CB", + Type: utils.MetaConcrete, + Units: utils.NewDecimal(500, 0), // 500 Units + FilterIDs: []string{"*string:~*req.CustomField:CustomValue"}, + CostIncrements: []*utils.CostIncrement{ + { + Increment: utils.NewDecimal(5, 0), + RecurrentFee: utils.NewDecimal(1, 0), + }, + }, + }, + fltrS: filterS, + } + cgrEv := &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "EV", + Event: map[string]interface{}{ + "CustomField2": "CustomValue2", + }, + } + toDebit := decimal.New(10, 0) + if _, err := cb.debitAbstracts(toDebit, cgrEv); err == nil || + err != utils.ErrFilterNotPassingNoCaps { + t.Error(err) + } +} + +// func TestCBSDebitAbstractsInvalidCostIncrementFilter(t *testing.T) { +// cfg := config.NewDefaultCGRConfig() +// data := engine.NewInternalDB(nil, nil, true) +// dm := engine.NewDataManager(data, config.CgrConfig().CacheCfg(), nil) +// filterS := engine.NewFilterS(cfg, nil, dm) +// // debit 10 units from a concrete balance with 500 units +// cb := &concreteBalance{ +// blnCfg: &utils.Balance{ +// ID: "CB", +// Type: utils.MetaConcrete, +// Units: utils.NewDecimal(500, 0), // 500 Units +// CostIncrements: []*utils.CostIncrement{ +// { +// FilterIDs: []string{"*string"}, +// Increment: utils.NewDecimal(5, 0), +// RecurrentFee: utils.NewDecimal(1, 0), +// }, +// }, +// }, +// fltrS: filterS, +// } +// toDebit := decimal.New(10, 0) +// if _, err := cb.debitAbstracts(toDebit, new(utils.CGREvent)); err == nil || +// err.Error() != "inline parse error for string: <*string>" { +// t.Error(err) +// } +// } + +// func TestCBSDebitAbstractsCoverProcessAttributes(t *testing.T) { // coverage purpose +// cfg := config.NewDefaultCGRConfig() +// data := engine.NewInternalDB(nil, nil, true) +// dm := engine.NewDataManager(data, config.CgrConfig().CacheCfg(), nil) +// filterS := engine.NewFilterS(cfg, nil, dm) + +// engine.Cache.Clear(nil) + +// sTestMock := &testMockCall{ +// calls: map[string]func(args interface{}, reply interface{}) error{ +// utils.AttributeSv1ProcessEvent: func(args interface{}, reply interface{}) error { +// return utils.ErrNotImplemented +// }, +// }, +// } +// chanInternal := make(chan rpcclient.ClientConnector, 1) +// chanInternal <- sTestMock +// connMgr := engine.NewConnManager(cfg, map[string]chan rpcclient.ClientConnector{ +// utils.ConcatenatedKey(utils.MetaInternal, utils.MetaAttributes): chanInternal, +// }) + +// // debit 10 units from a concrete balance with 500 units +// cb := &concreteBalance{ +// blnCfg: &utils.Balance{ +// ID: "CB", +// Type: utils.MetaConcrete, +// Units: utils.NewDecimal(500, 0), // 500 Units +// CostIncrements: []*utils.CostIncrement{ +// { +// Increment: utils.NewDecimal(5, 0), +// RecurrentFee: utils.NewDecimal(-1, 0), +// }, +// }, +// AttributeIDs: []string{"CustomAttr"}, +// }, +// fltrS: filterS, +// connMgr: connMgr, +// } +// toDebit := decimal.New(10, 0) +// if _, err := cb.debitAbstracts(toDebit, new(utils.CGREvent)); err == nil || +// err.Error() != "NOT_CONNECTED: AttributeS" { +// t.Error(err) +// } +// } + +// func TestCBSDebitAbstractsCoverProcessAttributes2(t *testing.T) { // coverage purpose +// cfg := config.NewDefaultCGRConfig() +// data := engine.NewInternalDB(nil, nil, true) +// dm := engine.NewDataManager(data, config.CgrConfig().CacheCfg(), nil) +// filterS := engine.NewFilterS(cfg, nil, dm) + +// engine.Cache.Clear(nil) + +// sTestMock := &testMockCall{ +// calls: map[string]func(args interface{}, reply interface{}) error{ +// utils.AttributeSv1ProcessEvent: func(args interface{}, reply interface{}) error { +// rplCast, canCast := reply.(*engine.AttrSProcessEventReply) +// if !canCast { +// t.Errorf("Wrong argument type : %T", reply) +// return nil +// } +// customEv := &engine.AttrSProcessEventReply{ +// MatchedProfiles: nil, +// AlteredFields: []string{"CustomField2"}, +// CGREvent: &utils.CGREvent{ +// Tenant: "cgrates.org", +// ID: "EV", +// Event: map[string]interface{}{ +// "CustomField2": "CustomValue2", +// }, +// }, +// } +// *rplCast = *customEv +// return nil +// }, +// }, +// } +// chanInternal := make(chan rpcclient.ClientConnector, 1) +// chanInternal <- sTestMock +// connMgr := engine.NewConnManager(cfg, map[string]chan rpcclient.ClientConnector{ +// utils.ConcatenatedKey(utils.MetaInternal, utils.MetaAttributes): chanInternal, +// }) + +// // debit 10 units from a concrete balance with 500 units +// cb := &concreteBalance{ +// blnCfg: &utils.Balance{ +// ID: "CB", +// Type: utils.MetaConcrete, +// Units: utils.NewDecimal(500, 0), // 500 Units +// CostIncrements: []*utils.CostIncrement{ +// { +// Increment: utils.NewDecimal(5, 0), +// RecurrentFee: utils.NewDecimal(-1, 0), +// }, +// }, +// AttributeIDs: []string{"CustomAttr"}, +// }, +// fltrS: filterS, +// connMgr: connMgr, +// attrSConns: []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaAttributes)}, +// } +// toDebit := decimal.New(10, 0) +// if _, err := cb.debitAbstracts(toDebit, new(utils.CGREvent)); err == nil || +// err.Error() != "RATES_ERROR:NOT_CONNECTED: RateS" { +// t.Error(err) +// } +// } diff --git a/accounts/libaccounts.go b/accounts/libaccounts.go new file mode 100644 index 000000000..253b21f69 --- /dev/null +++ b/accounts/libaccounts.go @@ -0,0 +1,330 @@ +/* +Real-time Online/Offline Charging System (OerS) 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 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package accounts + +import ( + "errors" + "fmt" + + "github.com/cgrates/cgrates/config" + + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/guardian" + "github.com/cgrates/cgrates/utils" + "github.com/ericlagergren/decimal" +) + +// newAccountBalances constructs accountBalances +func newBalanceOperators(acntID string, blnCfgs []*utils.Balance, + fltrS *engine.FilterS, connMgr *engine.ConnManager, + attrSConns, rateSConns []string) (blncOpers []balanceOperator, err error) { + + blncOpers = make([]balanceOperator, len(blnCfgs)) + var cncrtBlncs []*concreteBalance + for i, blnCfg := range blnCfgs { // build the concrete balances + if blnCfg.Type != utils.MetaConcrete { + continue + } + blncOpers[i] = newConcreteBalanceOperator(acntID, blnCfg, + fltrS, connMgr, attrSConns, rateSConns) + cncrtBlncs = append(cncrtBlncs, blncOpers[i].(*concreteBalance)) + } + + for i, blnCfg := range blnCfgs { // build the abstract balances + if blnCfg.Type == utils.MetaConcrete { + continue + } + if blncOpers[i], err = newBalanceOperator(acntID, blnCfg, cncrtBlncs, + fltrS, connMgr, attrSConns, rateSConns); err != nil { + return + } + } + + return +} + +// newBalanceOperator instantiates balanceOperator interface +// cncrtBlncs are needed for abstract balance debits +func newBalanceOperator(acntID string, blncCfg *utils.Balance, cncrtBlncs []*concreteBalance, + fltrS *engine.FilterS, connMgr *engine.ConnManager, + attrSConns, rateSConns []string) (bP balanceOperator, err error) { + switch blncCfg.Type { + default: + return nil, fmt.Errorf("unsupported balance type: <%s>", blncCfg.Type) + case utils.MetaConcrete: + return newConcreteBalanceOperator(acntID, blncCfg, fltrS, connMgr, attrSConns, rateSConns), nil + case utils.MetaAbstract: + return newAbstractBalanceOperator(acntID, blncCfg, cncrtBlncs, fltrS, connMgr, attrSConns, rateSConns), nil + } +} + +// balanceOperator is the implementation of a balance type +type balanceOperator interface { + debitAbstracts(usage *decimal.Big, cgrEv *utils.CGREvent) (ec *utils.EventCharges, err error) + debitConcretes(usage *decimal.Big, cgrEv *utils.CGREvent) (ec *utils.EventCharges, err error) +} + +// roundUnitsWithIncrements rounds the usage based on increments +func roundUnitsWithIncrements(usage, incrm *decimal.Big) (rndedUsage *decimal.Big) { + usgMaxIncrm := decimal.WithContext( + decimal.Context{RoundingMode: decimal.ToZero}).Quo(usage, + incrm).RoundToInt() + rndedUsage = utils.MultiplyBig(usgMaxIncrm, incrm) + return +} + +// processAttributeS will process the event with AttributeS +func processAttributeS(connMgr *engine.ConnManager, cgrEv *utils.CGREvent, + attrSConns, attrIDs []string) (rplyEv *engine.AttrSProcessEventReply, err error) { + if len(attrSConns) == 0 { + return nil, utils.NewErrNotConnected(utils.AttributeS) + } + var procRuns *int + if val, has := cgrEv.APIOpts[utils.OptsAttributesProcessRuns]; has { + if v, err := utils.IfaceAsTInt64(val); err == nil { + procRuns = utils.IntPointer(int(v)) + } + } + attrArgs := &engine.AttrArgsProcessEvent{ + Context: utils.StringPointer(utils.FirstNonEmpty( + engine.MapEvent(cgrEv.APIOpts).GetStringIgnoreErrors(utils.OptsContext), + utils.MetaAccounts)), + CGREvent: cgrEv, + AttributeIDs: attrIDs, + ProcessRuns: procRuns, + } + var tmpReply engine.AttrSProcessEventReply + if err = connMgr.Call(attrSConns, nil, utils.AttributeSv1ProcessEvent, + attrArgs, &tmpReply); err != nil { + return + } + return &tmpReply, nil +} + +// costIncrement computes the costIncrement for the event +func costIncrement(cfgCostIncrmts []*utils.CostIncrement, + fltrS *engine.FilterS, tnt string, ev utils.DataProvider) (costIcrm *utils.CostIncrement, err error) { + for _, cIcrm := range cfgCostIncrmts { + var pass bool + if pass, err = fltrS.Pass(tnt, cIcrm.FilterIDs, ev); err != nil { + return + } else if !pass { + continue + } + costIcrm = cIcrm + break + } + if costIcrm == nil { + costIcrm = new(utils.CostIncrement) + } + if costIcrm.Increment == nil { + costIcrm.Increment = utils.NewDecimal(1, 0) + } + if costIcrm.RecurrentFee == nil { + costIcrm.RecurrentFee = utils.NewDecimal(-1, 0) + } + return +} + +// unitFactor detects the unitFactor for the event +func unitFactor(cfgUnitFactors []*utils.UnitFactor, + fltrS *engine.FilterS, tnt string, ev utils.DataProvider) (uF *utils.UnitFactor, err error) { + for _, uFcfg := range cfgUnitFactors { + var pass bool + if pass, err = fltrS.Pass(tnt, uFcfg.FilterIDs, ev); err != nil { + return + } else if !pass { + continue + } + uF = uFcfg + return + } + return +} + +// balanceLimit returns the balance limit based on configuration +func balanceLimit(optsCfg map[string]interface{}) (bL *utils.Decimal, err error) { + if _, isUnlimited := optsCfg[utils.MetaBalanceUnlimited]; isUnlimited { + return // unlimited is nil pointer + } + if lmtIface, has := optsCfg[utils.MetaBalanceLimit]; has { + flt64Lmt, canCast := lmtIface.(float64) + if !canCast { + return nil, errors.New("unsupported *balanceLimit format") + } + return utils.NewDecimalFromFloat64(flt64Lmt), nil + } + // nothing matched, return default + bL = utils.NewDecimal(0, 0) + return +} + +// debitConcreteUnits debits concrete units out of concrete balances +// returns utils.ErrInsufficientCredit if complete usage cannot be debited +func debitConcreteUnits(cUnits *decimal.Big, + acntID string, cncrtBlncs []*concreteBalance, + cgrEv *utils.CGREvent) (ec *utils.EventCharges, err error) { + + clnedUnts := cloneUnitsFromConcretes(cncrtBlncs) + for _, cB := range cncrtBlncs { + var ecCncrt *utils.EventCharges + if ecCncrt, err = cB.debitConcretes(new(decimal.Big).Copy(cUnits), cgrEv); err != nil { + restoreUnitsFromClones(cncrtBlncs, clnedUnts) + return nil, err + } + if ecCncrt == nil { // no debit performed + continue + } + if ec == nil { + ec = utils.NewEventCharges() + } + ec.Merge(ecCncrt) + cUnits = utils.SubstractBig(cUnits, ecCncrt.Concretes.Big) + if cUnits.Cmp(decimal.New(0, 0)) <= 0 { + return // have debited all, total is smaller or equal to 0 + } + } + // we could not debit all, put back what we have debited + restoreUnitsFromClones(cncrtBlncs, clnedUnts) + return nil, utils.ErrInsufficientCredit +} + +// maxDebitAbstractsFromConcretes will debit the maximum possible abstract units out of concretes +func maxDebitAbstractsFromConcretes(aUnits *decimal.Big, + acndID string, cncrtBlncs []*concreteBalance, + connMgr *engine.ConnManager, cgrEv *utils.CGREvent, + attrSConns, attributeIDs, rateSConns, rpIDs []string, + costIcrm *utils.CostIncrement) (ec *utils.EventCharges, err error) { + // Init EventCharges + calculateCost := costIcrm.RecurrentFee.Cmp(decimal.New(-1, 0)) == 0 && costIcrm.FixedFee == nil + //var attrIDs []string // will be populated if attributes are processed successfully + // process AttributeS if needed + if calculateCost && len(attributeIDs) != 0 { // cost unknown, apply AttributeS to query from RateS + var rplyAttrS *engine.AttrSProcessEventReply + if rplyAttrS, err = processAttributeS(connMgr, cgrEv, attrSConns, + attributeIDs); err != nil { + return + } + if len(rplyAttrS.AlteredFields) != 0 { // event was altered + cgrEv = rplyAttrS.CGREvent + //attrIDs = rplyAttrS.MatchedProfiles + } + } + // fix the maximum number of iterations + origConcrtUnts := cloneUnitsFromConcretes(cncrtBlncs) // so we can revert on errors + paidConcrtUnts := origConcrtUnts // so we can revert when higher abstracts are not possible + var aPaid, aDenied *decimal.Big + maxItr := config.CgrConfig().AccountSCfg().MaxIterations + for i := 0; i <= maxItr; i++ { + if i != 0 { + restoreUnitsFromClones(cncrtBlncs, origConcrtUnts) + } + if i == maxItr { + return nil, utils.ErrMaxIncrementsExceeded + } + var cUnits *decimal.Big // concrete units to debit + if costIcrm.FixedFee != nil { + cUnits = costIcrm.FixedFee.Big + } + // RecurrentFee is configured, used it with increments + if costIcrm.RecurrentFee.Big.Cmp(decimal.New(-1, 0)) != 0 { + rcrntCost := utils.MultiplyBig( + utils.DivideBig(aUnits, costIcrm.Increment.Big), + costIcrm.RecurrentFee.Big) + if cUnits == nil { + cUnits = rcrntCost + } else { + cUnits = utils.SumBig(cUnits, rcrntCost) + } + } + aQried := aUnits // so we can detect loops + var ecDbt *utils.EventCharges + if ecDbt, err = debitConcreteUnits(cUnits, acndID, cncrtBlncs, cgrEv); err != nil { + if err != utils.ErrInsufficientCredit { + return + } + err = nil + // ErrInsufficientCredit + aDenied = new(decimal.Big).Copy(aUnits) + if aPaid == nil { // going backwards + aUnits = utils.DivideBig( // divide by 2 + aUnits, decimal.New(2, 0)) + aUnits = roundUnitsWithIncrements(aUnits, costIcrm.Increment.Big) // make sure abstracts are multiple of increments + if aUnits.Cmp(aDenied) >= 0 || + aUnits.Cmp(decimal.New(0, 0)) == 0 || + aUnits.Cmp(aQried) == 0 { // loop + break + } + continue + } + } else { // debit for the usage succeeded + aPaid = new(decimal.Big).Copy(aUnits) + paidConcrtUnts = cloneUnitsFromConcretes(cncrtBlncs) + ec = utils.NewEventCharges() + ec.Merge(ecDbt) + if i == 0 { // no estimation done, covering full + break + } + } + // going upwards + aUnits = utils.SumBig(aPaid, + utils.DivideBig(aPaid, decimal.New(2, 0)).RoundToInt()) + if aUnits.Cmp(aDenied) >= 0 { + aUnits = utils.SumBig(aPaid, costIcrm.Increment.Big) + } + aUnits = roundUnitsWithIncrements(aUnits, costIcrm.Increment.Big) + if aUnits.Cmp(aPaid) <= 0 || + aUnits.Cmp(aDenied) >= 0 || + aUnits.Cmp(aQried) == 0 { // loop + break + } + } + // Nothing paid + if aPaid == nil { + // since we are erroring, we restore the concerete balances + aPaid = decimal.New(0, 0) + ec = utils.NewEventCharges() + } + ec.Abstracts = &utils.Decimal{aPaid} + restoreUnitsFromClones(cncrtBlncs, paidConcrtUnts) + return +} + +// restoreAccounts will restore the accounts in DataDB out of their backups if present +func restoreAccounts(dm *engine.DataManager, + acnts []*utils.AccountProfileWithWeight, bkps []utils.AccountBalancesBackup) { + for i, bkp := range bkps { + if bkp == nil || + !acnts[i].AccountProfile.BalancesAltered(bkp) { + continue + } + acnts[i].AccountProfile.RestoreFromBackup(bkp) + if err := dm.SetAccountProfile(acnts[i].AccountProfile, false); err != nil { + utils.Logger.Warning(fmt.Sprintf("<%s> error <%s> restoring account <%s>", + utils.AccountS, err, acnts[i].AccountProfile.TenantID())) + } + } +} + +// unlockAccountProfiles is used to unlock the accounts based on their lock identifiers +func unlockAccountProfiles(acnts utils.AccountProfilesWithWeight) { + for _, lkID := range acnts.LockIDs() { + guardian.Guardian.UnguardIDs(lkID) + } +} diff --git a/accounts/libaccounts_test.go b/accounts/libaccounts_test.go new file mode 100644 index 000000000..a84971d51 --- /dev/null +++ b/accounts/libaccounts_test.go @@ -0,0 +1,581 @@ +/* +Real-time Online/Offline Charging System (OerS) 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 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package accounts + +import ( + "bytes" + "log" + "os" + "reflect" + "strings" + "testing" + "time" + + "github.com/ericlagergren/decimal" + + "github.com/cgrates/cgrates/config" + + "github.com/cgrates/rpcclient" + + "github.com/cgrates/cgrates/engine" + + "github.com/cgrates/cgrates/utils" +) + +func TestNewAccountBalanceOperators(t *testing.T) { + acntPrf := &utils.AccountProfile{ + ID: "TEST_ID", + Tenant: "cgrates.org", + Balances: map[string]*utils.Balance{ + "BL0": { + ID: "BALANCE1", + Type: utils.MetaAbstract, + }, + "BL1": { + ID: "BALANCE1", + Type: utils.MetaConcrete, + CostIncrements: []*utils.CostIncrement{ + { + Increment: utils.NewDecimal(int64(time.Second), 0), + }, + }, + }, + }, + } + filters := engine.NewFilterS(config.NewDefaultCGRConfig(), nil, nil) + + concrete, err := newBalanceOperator(acntPrf.ID, acntPrf.Balances["BL1"], nil, filters, nil, nil, nil) + if err != nil { + t.Error(err) + } + var cncrtBlncs []*concreteBalance + cncrtBlncs = append(cncrtBlncs, concrete.(*concreteBalance)) + + expected := &abstractBalance{ + acntID: acntPrf.ID, + blnCfg: acntPrf.Balances["BL0"], + fltrS: filters, + cncrtBlncs: cncrtBlncs, + } + blnCfgs := []*utils.Balance{acntPrf.Balances["BL0"], acntPrf.Balances["BL1"]} + if blcOp, err := newBalanceOperators(acntPrf.ID, blnCfgs, filters, nil, + nil, nil); err != nil { + t.Error(err) + } else { + for _, bal := range blcOp { + if rcv, canCast := bal.(*abstractBalance); canCast { + if !reflect.DeepEqual(expected, rcv) { + t.Errorf("Expected %+v, received %+v", expected, rcv) + } + } + } + } + + acntPrf.Balances["BL1"].Type = "INVALID_TYPE" + expectedErr := "unsupported balance type: " + if _, err := newBalanceOperators(acntPrf.ID, blnCfgs, filters, nil, + nil, nil); err == nil || err.Error() != expectedErr { + t.Errorf("Expected %+v, received %+v", expectedErr, err) + } +} + +type testMockCall struct { + calls map[string]func(args interface{}, reply interface{}) error +} + +func (tS *testMockCall) Call(method string, args interface{}, rply interface{}) error { + if call, has := tS.calls[method]; !has { + return rpcclient.ErrUnsupporteServiceMethod + } else { + return call(args, rply) + } +} + +func TestProcessAttributeS(t *testing.T) { + engine.Cache.Clear(nil) + + config := config.NewDefaultCGRConfig() + sTestMock := &testMockCall{ // coverage purpose + calls: map[string]func(args interface{}, reply interface{}) error{ + utils.AttributeSv1ProcessEvent: func(args interface{}, reply interface{}) error { + return utils.ErrNotImplemented + }, + }, + } + chanInternal := make(chan rpcclient.ClientConnector, 1) + chanInternal <- sTestMock + connMgr := engine.NewConnManager(config, map[string]chan rpcclient.ClientConnector{ + utils.ConcatenatedKey(utils.MetaInternal, utils.MetaAttributes): chanInternal, + }) + cgrEvent := &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "TEST_ID1", + APIOpts: map[string]interface{}{ + utils.OptsAttributesProcessRuns: "20", + }, + } + + attrsConns := []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaAttributes)} + + if _, err := processAttributeS(connMgr, cgrEvent, attrsConns, nil); err == nil || err != utils.ErrNotImplemented { + t.Errorf("Expected %+v, received %+v", utils.ErrNotImplemented, err) + } +} + +func TestDebitUsageFromConcretes(t *testing.T) { + engine.Cache.Clear(nil) + + data := engine.NewInternalDB(nil, nil, true) + dm := engine.NewDataManager(data, config.CgrConfig().CacheCfg(), nil) + cfg := config.NewDefaultCGRConfig() + + filterS := engine.NewFilterS(cfg, nil, dm) + cb1 := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "CB1", + Type: utils.MetaConcrete, + Units: utils.NewDecimal(500, 0), // 500 Units + }, + fltrS: filterS, + } + cb2 := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "CB2", + Type: utils.MetaConcrete, + Units: utils.NewDecimal(500, 0), // 500 Units + }, + fltrS: filterS, + } + expectedEvCh := &utils.EventCharges{ + Concretes: utils.NewDecimal(700, 0), + Accounting: make(map[string]*utils.AccountCharge), + UnitFactors: make(map[string]*utils.UnitFactor), + } + + if evCh, err := debitConcreteUnits(decimal.New(700, 0), utils.EmptyString, + []*concreteBalance{cb1, cb2}, new(utils.CGREvent)); err != nil { + t.Error(err) + } else if cb1.blnCfg.Units.Cmp(decimal.New(0, 0)) != 0 { + t.Errorf("balance remaining: %s", cb1.blnCfg.Units) + } else if cb2.blnCfg.Units.Cmp(decimal.New(300, 0)) != 0 { + t.Errorf("balance remaining: %s", cb2.blnCfg.Units) + } else if !reflect.DeepEqual(expectedEvCh, evCh) { + t.Errorf("Expected %+v, received %+v", utils.ToJSON(expectedEvCh), utils.ToJSON(evCh)) + } + + cb1.blnCfg.Units = utils.NewDecimal(500, 0) + cb2.blnCfg.Units = utils.NewDecimal(500, 0) + + if _, err := debitConcreteUnits(decimal.New(1100, 0), utils.EmptyString, + []*concreteBalance{cb1, cb2}, new(utils.CGREvent)); err == nil || err != utils.ErrInsufficientCredit { + t.Errorf("Expected %+v, received %+v", utils.ErrInsufficientCredit, err) + } else if cb1.blnCfg.Units.Cmp(decimal.New(500, 0)) != 0 { + t.Errorf("balance remaining: %s", cb1.blnCfg.Units) + } else if cb2.blnCfg.Units.Cmp(decimal.New(500, 0)) != 0 { + t.Errorf("balance remaining: %s", cb2.blnCfg.Units) + } +} + +func TestDebitUsageFromConcretesRestore(t *testing.T) { + engine.Cache.Clear(nil) + + data := engine.NewInternalDB(nil, nil, true) + dm := engine.NewDataManager(data, config.CgrConfig().CacheCfg(), nil) + cfg := config.NewDefaultCGRConfig() + + filterS := engine.NewFilterS(cfg, nil, dm) + cb1 := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "CB1", + Type: utils.MetaConcrete, + FilterIDs: []string{"*string"}, + Units: utils.NewDecimal(500, 0), // 500 Units + }, + fltrS: filterS, + } + cb2 := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "CB2", + Type: utils.MetaConcrete, + Units: utils.NewDecimal(500, 0), // 500 Units + }, + fltrS: filterS, + } + + if _, err := debitConcreteUnits(decimal.New(200, 0), utils.EmptyString, + []*concreteBalance{cb1, cb2}, + new(utils.CGREvent)); err == nil || err.Error() != "inline parse error for string: <*string>" { + t.Error(err) + } else if cb1.blnCfg.Units.Cmp(decimal.New(500, 0)) != 0 { + t.Errorf("balance remaining: %s", cb1.blnCfg.Units) + } else if cb2.blnCfg.Units.Cmp(decimal.New(500, 0)) != 0 { + t.Errorf("balance remaining: %s", cb2.blnCfg.Units) + } +} + +func TestMaxDebitUsageFromConcretes(t *testing.T) { + engine.Cache.Clear(nil) + + data := engine.NewInternalDB(nil, nil, true) + dm := engine.NewDataManager(data, config.CgrConfig().CacheCfg(), nil) + cfg := config.NewDefaultCGRConfig() + cfg.AccountSCfg().MaxIterations = 100 + config.SetCgrConfig(cfg) + filterS := engine.NewFilterS(cfg, nil, dm) + cb1 := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "CB1", + Type: utils.MetaConcrete, + Units: utils.NewDecimal(500, 0), // 500 Units + }, + fltrS: filterS, + } + cb2 := &concreteBalance{ + blnCfg: &utils.Balance{ + ID: "CB2", + Type: utils.MetaConcrete, + Units: utils.NewDecimal(500, 0), // 500 Units + }, + fltrS: filterS, + } + + if _, err := maxDebitAbstractsFromConcretes(decimal.New(900, 0), utils.EmptyString, + []*concreteBalance{cb1, cb2}, nil, new(utils.CGREvent), + nil, nil, nil, nil, &utils.CostIncrement{ + Increment: utils.NewDecimal(1, 0), + RecurrentFee: utils.NewDecimal(1, 0), + }); err != nil { + t.Error(err) + } else if cb1.blnCfg.Units.Cmp(decimal.New(0, 0)) != 0 { + t.Errorf("balance remaining: %s", cb1.blnCfg.Units) + } else if cb2.blnCfg.Units.Cmp(decimal.New(100, 0)) != 0 { + t.Errorf("balance remaining: %s", cb2.blnCfg.Units) + } + + //debit more than we have in balances with the restored units + cb1.blnCfg.Units = utils.NewDecimal(500, 0) + cb2.blnCfg.Units = utils.NewDecimal(500, 0) + if _, err := maxDebitAbstractsFromConcretes(decimal.New(1100, 0), utils.EmptyString, + []*concreteBalance{cb1, cb2}, nil, new(utils.CGREvent), + nil, nil, nil, nil, &utils.CostIncrement{ + Increment: utils.NewDecimal(1, 0), + RecurrentFee: utils.NewDecimal(1, 0), + }); err == nil || err != utils.ErrMaxIncrementsExceeded { + t.Error(err) + } else if cb1.blnCfg.Units.Cmp(decimal.New(500, 0)) != 0 { + t.Errorf("balance remaining: %s", cb1.blnCfg.Units) + } else if cb2.blnCfg.Units.Cmp(decimal.New(500, 0)) != 0 { + t.Errorf("balance remaining: %s", cb2.blnCfg.Units) + } +} + +func TestRestoreAccount(t *testing.T) { //coverage purpose + engine.Cache.Clear(nil) + + data := engine.NewInternalDB(nil, nil, true) + dm := engine.NewDataManager(data, config.CgrConfig().CacheCfg(), nil) + acntPrf := &utils.AccountProfile{ + Tenant: "cgrates.org", + ID: "1001", + Balances: map[string]*utils.Balance{ + "CB1": { + ID: "CB1", + Type: utils.MetaConcrete, + Units: utils.NewDecimal(500, 0), + }, + "CB2": { + ID: "CB2", + Type: utils.MetaConcrete, + Units: utils.NewDecimal(300, 0), + }, + }, + } + if err := dm.SetAccountProfile(acntPrf, false); err != nil { + t.Error(err) + } + + restoreAccounts(dm, []*utils.AccountProfileWithWeight{ + {acntPrf, 0, utils.EmptyString}, + }, []utils.AccountBalancesBackup{ + map[string]*decimal.Big{"CB2": decimal.New(100, 0)}, + }) + + if rcv, err := dm.GetAccountProfile("cgrates.org", "1001"); err != nil { + t.Error(err) + } else if len(rcv.Balances) != 2 { + t.Errorf("Unexpected number of balances received") + } else if rcv.Balances["CB2"].Units.Cmp(decimal.New(100, 0)) != 0 { + t.Errorf("Unexpected balance received after restore") + } +} + +type dataDBMockError struct { + *engine.DataDBMock +} + +func TestRestoreAccount2(t *testing.T) { //coverage purpose + engine.Cache.Clear(nil) + + dm := engine.NewDataManager(&dataDBMockError{}, config.CgrConfig().CacheCfg(), nil) + acntPrf := &utils.AccountProfile{ + Tenant: "cgrates.org", + ID: "1001", + Balances: map[string]*utils.Balance{ + "CB1": { + ID: "CB1", + Type: utils.MetaConcrete, + Units: utils.NewDecimal(500, 0), // 500 Units + }, + "CB2": { + ID: "CB2", + Type: utils.MetaConcrete, + Units: utils.NewDecimal(100, 0), // 500 Units + }, + }, + } + var err error + utils.Logger, err = utils.Newlogger(utils.MetaStdLog, utils.EmptyString) + if err != nil { + t.Error(err) + } + utils.Logger.SetLogLevel(7) + buff := new(bytes.Buffer) + log.SetOutput(buff) + + restoreAccounts(dm, []*utils.AccountProfileWithWeight{ + {acntPrf, 0, utils.EmptyString}, + }, []utils.AccountBalancesBackup{ + map[string]*decimal.Big{"CB1": decimal.New(100, 0)}, + }) + + subString := " error restoring account " + if rcv := buff.String(); !strings.Contains(rcv, subString) { + t.Errorf("Expected %+q, received %+q", subString, rcv) + } + + log.SetOutput(os.Stderr) +} + +func TestRestoreAccount3(t *testing.T) { //coverage purpose + engine.Cache.Clear(nil) + + data := engine.NewInternalDB(nil, nil, true) + dm := engine.NewDataManager(data, config.CgrConfig().CacheCfg(), nil) + acntPrf := &utils.AccountProfile{ + Tenant: "cgrates.org", + ID: "1001", + Balances: map[string]*utils.Balance{ + "CB1": { + ID: "CB1", + Type: utils.MetaConcrete, + Units: utils.NewDecimal(500, 0), // 500 Units + }, + "CB2": { + ID: "CB2", + Type: utils.MetaConcrete, + Units: utils.NewDecimal(100, 0), // 500 Units + }, + }, + } + if err := dm.SetAccountProfile(acntPrf, false); err != nil { + t.Error(err) + } + + restoreAccounts(dm, []*utils.AccountProfileWithWeight{ + {acntPrf, 0, utils.EmptyString}, + }, []utils.AccountBalancesBackup{ + nil, + }) +} + +/* +func TestDebitFromBothBalances(t *testing.T) { + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data := engine.NewInternalDB(nil, nil, true) + dm := engine.NewDataManager(data, cfg.CacheCfg(), nil) + fltr := engine.NewFilterS(cfg, nil, dm) + rates := rates2.NewRateS(cfg, fltr, dm) + //RateS + rpcClientConn := make(chan rpcclient.ClientConnector, 1) + rpcClientConn <- rates + connMngr := engine.NewConnManager(cfg, map[string]chan rpcclient.ClientConnector{ + utils.ConcatenatedKey(utils.MetaInternal, utils.MetaRateS): rpcClientConn, + }) + cfg.AccountSCfg().RateSConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaRateS)} + //AccountS + accnts := NewAccountS(cfg, fltr, connMngr, dm) + + accPrf := &utils.AccountProfile{ + Tenant: "cgrates.org", + ID: "1002", + FilterIDs: []string{"*string:~*req.Account:2003"}, + Balances: map[string]*utils.Balance{ + "AbstractBalance": { + ID: "AbstractBalance", + Type: utils.MetaAbstract, + Units: utils.NewDecimal(1500, 0), + Weights: []*utils.DynamicWeight{ + { + Weight: 50, + }, + }, + }, + "ConcreteBalance1": { + ID: "ConcreteCalance1", + Type: utils.MetaConcrete, + Weights: []*utils.DynamicWeight{ + { + Weight: 10, + }, + }, + Units: utils.NewDecimal(20, 0), + }, + "ConcreteBalance2": { + ID: "ConcreteCalance2", + Type: utils.MetaConcrete, + Weights: []*utils.DynamicWeight{ + { + Weight: 20, + }, + }, + Units: utils.NewDecimal(50, 0), + }, + }, + } + + if err := dm.SetAccountProfile(accPrf, true); err != nil { + t.Error(err) + } + + minDecimal, err := utils.NewDecimalFromUsage("1s") + if err != nil { + t.Error(err) + } + secDecimal, err := utils.NewDecimalFromUsage("1ns") + if err != nil { + t.Error(err) + } + rtPrf := &utils.RateProfile{ + Tenant: "cgrates.org", + ID: "RATE_1", + FilterIDs: []string{"*string:~*req.Account:2003"}, + Rates: map[string]*utils.Rate{ + "RT_ALWAYS": { + ID: "RT_ALWAYS", + FilterIDs: nil, + ActivationTimes: "* * * * *", + IntervalRates: []*utils.IntervalRate{ + { + IntervalStart: utils.NewDecimal(0, 0), + RecurrentFee: utils.NewDecimal(1, 2), + Unit: minDecimal, + Increment: secDecimal, + FixedFee: utils.NewDecimal(0, 0), + }, + }, + }, + }, + } + if err := dm.SetRateProfile(rtPrf, true); err != nil { + t.Error(err) + } + + args := &utils.ArgsAccountsForEvent{ + CGREvent: &utils.CGREvent{ + ID: "TestV1DebitID", + Tenant: "cgrates.org", + Event: map[string]interface{}{ + utils.AccountField: "2003", + utils.Usage: "300", + }, + }, + } + + var reply utils.ExtEventCharges + exEvCh := utils.ExtEventCharges{ + Abstracts: utils.Float64Pointer(300), + } + if err := accnts.V1DebitAbstracts(args, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(exEvCh, reply) { + t.Errorf("Expected %+v, received %+v", utils.ToJSON(exEvCh), utils.ToJSON(reply)) + } + + accPrf.Balances["AbstractBalance"].Units = utils.NewDecimal(1200, 0) + accPrf.Balances["ConcreteBalance2"].Units = utils.NewDecimal(49999999997, 9) + //as we debited, the account is changed + if rcvAcc, err := dm.GetAccountProfile(accPrf.Tenant, accPrf.ID); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(rcvAcc, accPrf) { + t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(accPrf), utils.ToJSON(rcvAcc)) + } + + if err := dm.RemoveAccountProfile(accPrf.Tenant, accPrf.ID, + utils.NonTransactional, true); err != nil { + t.Error(err) + } else if err := dm.RemoveRateProfile(rtPrf.Tenant, rtPrf.ID, + utils.NonTransactional, true); err != nil { + t.Error(err) + } +} +*/ + +func TestMaxDebitAbstractFromConcretesInsufficientCredit(t *testing.T) { + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data := engine.NewInternalDB(nil, nil, true) + dm := engine.NewDataManager(data, cfg.CacheCfg(), nil) + filters := engine.NewFilterS(cfg, nil, dm) + cncrtBlncs := []*concreteBalance{ + { + blnCfg: &utils.Balance{ + ID: "CB1", + Type: utils.MetaAbstract, + UnitFactors: []*utils.UnitFactor{ + { + FilterIDs: []string{"*test"}, + Factor: utils.NewDecimal(10, 0), // EuroCents + }, + }, + Units: utils.NewDecimal(80, 0), // 500 EuroCents + }, + fltrS: filters, + }, + { + blnCfg: &utils.Balance{ + ID: "CB2", + Type: utils.MetaConcrete, + Units: utils.NewDecimal(1, 0), + }, + fltrS: filters, + }, + } + + expectedErr := "inline parse error for string: <*test>" + if _, err := maxDebitAbstractsFromConcretes(decimal.New(110, 0), utils.EmptyString, + cncrtBlncs, nil, new(utils.CGREvent), + nil, nil, nil, nil, &utils.CostIncrement{ + Increment: utils.NewDecimal(2, 0), + RecurrentFee: utils.NewDecimal(1, 0), + }); err == nil || err.Error() != expectedErr { + t.Errorf("Expected %+v, received %+v", expectedErr, err) + } + +} diff --git a/apier/v1/accountsv1_it_test.go b/apier/v1/accountsv1_it_test.go new file mode 100644 index 000000000..5b9719529 --- /dev/null +++ b/apier/v1/accountsv1_it_test.go @@ -0,0 +1,1326 @@ +// +build integration + +/* +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 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package v1 + +import ( + "net/rpc" + "path" + "reflect" + "testing" + "time" + + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" + "github.com/ericlagergren/decimal" +) + +var ( + acntSConfigDIR string //run tests for specific configuration + acntSCfgPath string + acntSCfg *config.CGRConfig + acntSRPC *rpc.Client +) + +//Test start here +func TestAccountSv1IT(t *testing.T) { + sTestsAccountS := []func(t *testing.T){ + testAccountSv1InitCfg, + testAccountSv1InitDataDb, + testAccountSv1ResetStorDb, + testAccountSv1StartEngine, + testAccountSv1RPCConn, + testAccountSv1LoadFromFolder, + //testAccountSv1AccountProfilesForEvent, + //testAccountSv1MaxAbstracts, + //testAccountSv1DebitAbstracts, + //testAccountSv1SimpleDebit, + //testAccountSv1DebitMultipleAcc, + //testAccountSv1DebitMultipleAccLimited, + testAccountSv1DebitWithAttributeSandRateS, + //testAccountSv1DebitWithRateS, + //testAccountSv1DebitWithRateS2, + //testAccountSv1MaxConcretes, + //testAccountSv1DebitConcretes, + //testAccountSv1ActionSetBalance, + //testAccountSv1ActionRemoveBalance, + testAccountSv1KillEngine, + } + switch *dbType { + case utils.MetaInternal: + acntSConfigDIR = "accounts_internal" + case utils.MetaMySQL: + t.SkipNow() + case utils.MetaMongo: + t.SkipNow() + case utils.MetaPostgres: + t.SkipNow() + default: + t.Fatalf("unknown Database type <%s>", *dbType) + } + for _, stest := range sTestsAccountS { + t.Run(acntSConfigDIR, stest) + } +} + +func testAccountSv1InitCfg(t *testing.T) { + var err error + acntSCfgPath = path.Join(*dataDir, "conf", "samples", acntSConfigDIR) + acntSCfg, err = config.NewCGRConfigFromPath(acntSCfgPath) + if err != nil { + t.Error(err) + } +} + +func testAccountSv1InitDataDb(t *testing.T) { + if err := engine.InitDataDb(acntSCfg); err != nil { + t.Fatal(err) + } +} + +// Wipe out the cdr database +func testAccountSv1ResetStorDb(t *testing.T) { + if err := engine.InitStorDb(acntSCfg); err != nil { + t.Fatal(err) + } +} + +// Start CGR Engine +func testAccountSv1StartEngine(t *testing.T) { + if _, err := engine.StopStartEngine(acntSCfgPath, *waitRater); err != nil { + t.Fatal(err) + } +} + +// Connect rpc client to rater +func testAccountSv1RPCConn(t *testing.T) { + var err error + acntSRPC, err = newRPCClient(acntSCfg.ListenCfg()) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal(err) + } +} + +func testAccountSv1LoadFromFolder(t *testing.T) { + var reply string + attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "tutaccounts")} + if err := acntSRPC.Call(utils.APIerSv1LoadTariffPlanFromFolder, attrs, &reply); err != nil { + t.Error(err) + } + time.Sleep(100 * time.Millisecond) +} + +func testAccountSv1AccountProfilesForEvent(t *testing.T) { + eAcnts := []*utils.AccountProfile{ + { + Tenant: "cgrates.org", + ID: "1001", + FilterIDs: []string{"*string:~*req.Account:1001"}, + Balances: map[string]*utils.Balance{ + "GenericBalance1": &utils.Balance{ + ID: "GenericBalance1", + Weights: utils.DynamicWeights{ + { + Weight: 20, + }, + }, + Type: utils.MetaAbstract, + Units: &utils.Decimal{decimal.New(int64(time.Hour), 0)}, + UnitFactors: []*utils.UnitFactor{ + &utils.UnitFactor{ + FilterIDs: []string{"*string:~*req.ToR:*data"}, + Factor: &utils.Decimal{decimal.New(1024, 3)}, + }, + }, + CostIncrements: []*utils.CostIncrement{ + &utils.CostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*voice"}, + Increment: &utils.Decimal{decimal.New(int64(time.Second), 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(1, 2)}, + }, + &utils.CostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*data"}, + Increment: &utils.Decimal{decimal.New(1024, 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(1, 2)}, + }, + }, + }, + "MonetaryBalance1": &utils.Balance{ + ID: "MonetaryBalance1", + Weights: utils.DynamicWeights{ + { + Weight: 30, + }, + }, + Type: utils.MetaConcrete, + Units: &utils.Decimal{decimal.New(5, 0)}, + CostIncrements: []*utils.CostIncrement{ + &utils.CostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*voice"}, + Increment: &utils.Decimal{decimal.New(int64(time.Second), 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(1, 2)}, + }, + &utils.CostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*data"}, + Increment: &utils.Decimal{decimal.New(1024, 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(1, 2)}, + }, + }, + }, + "MonetaryBalance2": &utils.Balance{ + ID: "MonetaryBalance2", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }, + }, + Type: utils.MetaConcrete, + Units: &utils.Decimal{decimal.New(3, 0)}, + CostIncrements: []*utils.CostIncrement{ + &utils.CostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*voice"}, + Increment: &utils.Decimal{decimal.New(int64(time.Second), 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(1, 0)}, + }, + }, + }, + }, + ThresholdIDs: []string{utils.MetaNone}, + }, + } + var acnts []*utils.AccountProfile + if err := acntSRPC.Call(utils.AccountSv1AccountProfilesForEvent, + &utils.ArgsAccountsForEvent{ + CGREvent: &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "testAccountSv1AccountProfileForEvent", + Event: map[string]interface{}{ + utils.AccountField: "1001", + }}}, &acnts); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eAcnts, acnts) { + t.Errorf("Expecting : %s \n received: %s", utils.ToJSON(eAcnts), utils.ToJSON(acnts)) + } +} + +func testAccountSv1MaxAbstracts(t *testing.T) { + var eEc *utils.ExtEventCharges + if err := acntSRPC.Call(utils.AccountSv1MaxAbstracts, + &utils.ArgsAccountsForEvent{CGREvent: &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "testAccountSv1MaxUsage", + Event: map[string]interface{}{ + utils.AccountField: "1001", + utils.ToR: utils.MetaVoice, + utils.Usage: "15m", + }}}, &eEc); err != nil { + t.Error(err) + } else if eEc.Abstracts == nil || *eEc.Abstracts != 800000000000.0 { // 500s from first monetary + 300s from last monetary + t.Errorf("received usage: %v", *eEc.Abstracts) + } + + // Make sure we did not Debit anything from Account + eAcnt := &utils.AccountProfile{ + Tenant: "cgrates.org", + ID: "1001", + FilterIDs: []string{"*string:~*req.Account:1001"}, + Balances: map[string]*utils.Balance{ + "GenericBalance1": &utils.Balance{ + ID: "GenericBalance1", + Weights: utils.DynamicWeights{ + { + Weight: 20, + }, + }, + Type: utils.MetaAbstract, + Units: &utils.Decimal{decimal.New(int64(time.Hour), 0)}, + UnitFactors: []*utils.UnitFactor{ + &utils.UnitFactor{ + FilterIDs: []string{"*string:~*req.ToR:*data"}, + Factor: &utils.Decimal{decimal.New(1024, 3)}, + }, + }, + CostIncrements: []*utils.CostIncrement{ + &utils.CostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*voice"}, + Increment: &utils.Decimal{decimal.New(int64(time.Second), 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(1, 2)}, + }, + &utils.CostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*data"}, + Increment: &utils.Decimal{decimal.New(1024, 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(1, 2)}, + }, + }, + }, + "MonetaryBalance1": &utils.Balance{ + ID: "MonetaryBalance1", + Weights: utils.DynamicWeights{ + { + Weight: 30, + }, + }, + Type: utils.MetaConcrete, + Units: &utils.Decimal{decimal.New(5, 0)}, + CostIncrements: []*utils.CostIncrement{ + &utils.CostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*voice"}, + Increment: &utils.Decimal{decimal.New(int64(time.Second), 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(1, 2)}, + }, + &utils.CostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*data"}, + Increment: &utils.Decimal{decimal.New(1024, 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(1, 2)}, + }, + }, + }, + "MonetaryBalance2": &utils.Balance{ + ID: "MonetaryBalance2", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }, + }, + Type: utils.MetaConcrete, + Units: &utils.Decimal{decimal.New(3, 0)}, + CostIncrements: []*utils.CostIncrement{ + &utils.CostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*voice"}, + Increment: &utils.Decimal{decimal.New(int64(time.Second), 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(1, 0)}, + }, + }, + }, + }, + ThresholdIDs: []string{utils.MetaNone}, + } + + var reply *utils.AccountProfile + if err := acntSRPC.Call(utils.APIerSv1GetAccountProfile, + utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "1001"}}, &reply); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(eAcnt, reply) { + t.Errorf("Expecting : %+v \n received: %+v", utils.ToJSON(eAcnt), utils.ToJSON(reply)) + } +} + +func testAccountSv1DebitAbstracts(t *testing.T) { + var eEc *utils.ExtEventCharges + if err := acntSRPC.Call(utils.AccountSv1DebitAbstracts, + &utils.ArgsAccountsForEvent{CGREvent: &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "testAccountSv1MaxUsage", + Event: map[string]interface{}{ + utils.AccountField: "1001", + utils.ToR: utils.MetaVoice, + utils.Usage: "15m", + }}}, &eEc); err != nil { + t.Error(err) + } else if eEc.Abstracts == nil || *eEc.Abstracts != 800000000000.0 { // 500s from first monetary + 300s from last monetary + t.Fatalf("received usage: %v", *eEc.Abstracts) + } + + // Make sure we debit the right units from Account + eAcnt := &utils.AccountProfile{ + Tenant: "cgrates.org", + ID: "1001", + FilterIDs: []string{"*string:~*req.Account:1001"}, + Balances: map[string]*utils.Balance{ + "GenericBalance1": &utils.Balance{ + ID: "GenericBalance1", + Weights: utils.DynamicWeights{ + { + Weight: 20, + }, + }, + Type: utils.MetaAbstract, + Units: &utils.Decimal{decimal.New(int64(3300*time.Second), 0)}, + UnitFactors: []*utils.UnitFactor{ + &utils.UnitFactor{ + FilterIDs: []string{"*string:~*req.ToR:*data"}, + Factor: &utils.Decimal{decimal.New(1024, 3)}, + }, + }, + CostIncrements: []*utils.CostIncrement{ + &utils.CostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*voice"}, + Increment: &utils.Decimal{decimal.New(int64(time.Second), 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(1, 2)}, + }, + &utils.CostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*data"}, + Increment: &utils.Decimal{decimal.New(1024, 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(1, 2)}, + }, + }, + }, + "MonetaryBalance1": &utils.Balance{ + ID: "MonetaryBalance1", + Weights: utils.DynamicWeights{ + { + Weight: 30, + }, + }, + Type: utils.MetaConcrete, + Units: &utils.Decimal{decimal.New(0, 0)}, + CostIncrements: []*utils.CostIncrement{ + &utils.CostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*voice"}, + Increment: &utils.Decimal{decimal.New(int64(time.Second), 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(1, 2)}, + }, + &utils.CostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*data"}, + Increment: &utils.Decimal{decimal.New(1024, 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(1, 2)}, + }, + }, + }, + "MonetaryBalance2": &utils.Balance{ + ID: "MonetaryBalance2", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }, + }, + Type: utils.MetaConcrete, + Units: &utils.Decimal{decimal.New(0, 0)}, + CostIncrements: []*utils.CostIncrement{ + &utils.CostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*voice"}, + Increment: &utils.Decimal{decimal.New(int64(time.Second), 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(1, 0)}, + }, + }, + }, + }, + ThresholdIDs: []string{utils.MetaNone}, + } + + var reply *utils.AccountProfile + if err := acntSRPC.Call(utils.APIerSv1GetAccountProfile, + utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "1001"}}, &reply); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(eAcnt, reply) { + t.Errorf("Expecting : %+v \n received: %+v", utils.ToJSON(eAcnt), utils.ToJSON(reply)) + } +} + +func testAccountSv1SimpleDebit(t *testing.T) { + accPrfAPI := &utils.APIAccountProfileWithOpts{ + APIAccountProfile: &utils.APIAccountProfile{ + Tenant: "cgrates.org", + ID: "CustomAccount", + FilterIDs: []string{"*string:~*req.Account:CustomAccount"}, + Weights: ";10", + Balances: map[string]*utils.APIBalance{ + "Balance1": &utils.APIBalance{ + ID: "Balance1", + Weights: ";10", + Type: utils.MetaConcrete, + Units: 100, + CostIncrements: []*utils.APICostIncrement{ + { + Increment: utils.Float64Pointer(1), + RecurrentFee: utils.Float64Pointer(0.1), + }, + }, + }, + }, + ThresholdIDs: []string{utils.MetaNone}, + }, + } + var result string + expErr := utils.ErrNotFound.Error() + if err := acntSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "CustomAccount"}}, &result); err == nil || err.Error() != expErr { + t.Errorf("Expected error: %v received: %v", expErr, err) + } + var reply string + if err := acntSRPC.Call(utils.APIerSv1SetAccountProfile, accPrfAPI, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Error("Unexpected reply returned", reply) + } + var err error + var convAcc *utils.AccountProfile + if convAcc, err = accPrfAPI.AsAccountProfile(); err != nil { + t.Error(err) + } + var reply2 *utils.AccountProfile + if err := acntSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "CustomAccount"}}, &reply2); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(convAcc, reply2) { + t.Errorf("Expecting : %+v, received: %+v", convAcc, reply2) + } + + var eEc *utils.ExtEventCharges + if err := acntSRPC.Call(utils.AccountSv1DebitAbstracts, + &utils.ArgsAccountsForEvent{CGREvent: &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "testAccountSv1SimpleDebit", + Event: map[string]interface{}{ + utils.AccountField: "CustomAccount", + utils.Usage: "10", + }}}, &eEc); err != nil { + t.Error(err) + } else if eEc.Abstracts == nil || *eEc.Abstracts != 10.0 { + t.Fatalf("received usage: %v", *eEc.Abstracts) + } + + if err := acntSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "CustomAccount"}}, &reply2); err != nil { + t.Error(err) + } else if reply2.Balances["Balance1"].Units.Cmp(decimal.New(99, 0)) != 0 { + t.Errorf("Expecting : %+v, received: %s", decimal.New(99, 0), reply2.Balances["Balance1"].Units) + } +} + +func testAccountSv1DebitMultipleAcc(t *testing.T) { + accPrfAPI := &utils.APIAccountProfileWithOpts{ + APIAccountProfile: &utils.APIAccountProfile{ + Tenant: "cgrates.org", + ID: "CustomAccount", + FilterIDs: []string{"*string:~*req.Account:CustomAccount"}, + Weights: ";20", + Balances: map[string]*utils.APIBalance{ + "Balance1": &utils.APIBalance{ + ID: "Balance1", + Weights: ";10", + Type: utils.MetaConcrete, + Units: 100, + CostIncrements: []*utils.APICostIncrement{ + { + Increment: utils.Float64Pointer(1), + RecurrentFee: utils.Float64Pointer(0.1), + }, + }, + }, + }, + ThresholdIDs: []string{utils.MetaNone}, + }, + } + var reply string + if err := acntSRPC.Call(utils.APIerSv1SetAccountProfile, accPrfAPI, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Error("Unexpected reply returned", reply) + } + var err error + var convAcc *utils.AccountProfile + if convAcc, err = accPrfAPI.AsAccountProfile(); err != nil { + t.Error(err) + } + var reply2 *utils.AccountProfile + if err := acntSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "CustomAccount"}}, &reply2); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(convAcc, reply2) { + t.Errorf("Expecting : %+v, received: %+v", convAcc, reply2) + } + + accPrfAPI2 := &utils.APIAccountProfileWithOpts{ + APIAccountProfile: &utils.APIAccountProfile{ + Tenant: "cgrates.org", + ID: "CustomAccount2", + FilterIDs: []string{"*string:~*req.Account:CustomAccount"}, + Weights: ";10", + Balances: map[string]*utils.APIBalance{ + "Balance1": &utils.APIBalance{ + ID: "Balance1", + Weights: ";10", + Type: utils.MetaConcrete, + Units: 50, + CostIncrements: []*utils.APICostIncrement{ + { + Increment: utils.Float64Pointer(1), + RecurrentFee: utils.Float64Pointer(0.1), + }, + }, + }, + }, + ThresholdIDs: []string{utils.MetaNone}, + }, + } + if err := acntSRPC.Call(utils.APIerSv1SetAccountProfile, accPrfAPI2, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Error("Unexpected reply returned", reply) + } + var convAcc2 *utils.AccountProfile + if convAcc2, err = accPrfAPI2.AsAccountProfile(); err != nil { + t.Fatal(err) + } + if err := acntSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "CustomAccount2"}}, &reply2); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(convAcc2, reply2) { + t.Errorf("Expecting : %+v, received: %+v", convAcc2, reply2) + } + + var eEc *utils.ExtEventCharges + if err := acntSRPC.Call(utils.AccountSv1DebitAbstracts, + &utils.ArgsAccountsForEvent{CGREvent: &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "testAccountSv1SimpleDebit", + Event: map[string]interface{}{ + utils.AccountField: "CustomAccount", + utils.Usage: "1400", + }}}, &eEc); err != nil { + t.Error(err) + } else if eEc.Abstracts == nil || *eEc.Abstracts != 1400.0 { + t.Fatalf("received usage: %v", *eEc.Abstracts) + } + + if err := acntSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "CustomAccount"}}, &reply2); err != nil { + t.Error(err) + } else if reply2.Balances["Balance1"].Units.Cmp(decimal.New(0, 0)) != 0 { + t.Errorf("Expecting : %s, received: %s", decimal.New(0, 0), reply2.Balances["Balance1"].Units) + } + + if err := acntSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "CustomAccount2"}}, &reply2); err != nil { + t.Error(err) + } else if reply2.Balances["Balance1"].Units.Cmp(decimal.New(10, 0)) != 0 { + t.Errorf("Expecting : %s, received: %s", decimal.New(10, 0), reply2.Balances["Balance1"].Units) + } +} + +func testAccountSv1DebitMultipleAccLimited(t *testing.T) { + accPrfAPI := &utils.APIAccountProfileWithOpts{ + APIAccountProfile: &utils.APIAccountProfile{ + Tenant: "cgrates.org", + ID: "CustomAccount", + FilterIDs: []string{"*string:~*req.Account:CustomAccount"}, + Weights: ";20", + Balances: map[string]*utils.APIBalance{ + "Balance1": &utils.APIBalance{ + ID: "Balance1", + Weights: ";10", + Type: utils.MetaConcrete, + Units: 100, + Opts: map[string]interface{}{ + utils.MetaBalanceLimit: 50.0, + }, + CostIncrements: []*utils.APICostIncrement{ + { + Increment: utils.Float64Pointer(1), + RecurrentFee: utils.Float64Pointer(0.1), + }, + }, + }, + }, + ThresholdIDs: []string{utils.MetaNone}, + }, + } + var reply string + if err := acntSRPC.Call(utils.APIerSv1SetAccountProfile, accPrfAPI, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Error("Unexpected reply returned", reply) + } + var err error + var convAcc *utils.AccountProfile + if convAcc, err = accPrfAPI.AsAccountProfile(); err != nil { + t.Error(err) + } + var reply2 *utils.AccountProfile + if err := acntSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "CustomAccount"}}, &reply2); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(convAcc, reply2) { + t.Errorf("Expecting : %+v, received: %+v", convAcc, reply2) + } + + accPrfAPI2 := &utils.APIAccountProfileWithOpts{ + APIAccountProfile: &utils.APIAccountProfile{ + Tenant: "cgrates.org", + ID: "CustomAccount2", + FilterIDs: []string{"*string:~*req.Account:CustomAccount"}, + Weights: ";10", + Balances: map[string]*utils.APIBalance{ + "Balance1": &utils.APIBalance{ + ID: "Balance1", + Weights: ";10", + Type: utils.MetaConcrete, + Units: 50, + CostIncrements: []*utils.APICostIncrement{ + { + Increment: utils.Float64Pointer(1), + RecurrentFee: utils.Float64Pointer(0.1), + }, + }, + }, + }, + ThresholdIDs: []string{utils.MetaNone}, + }, + } + if err := acntSRPC.Call(utils.APIerSv1SetAccountProfile, accPrfAPI2, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Error("Unexpected reply returned", reply) + } + var convAcc2 *utils.AccountProfile + if convAcc2, err = accPrfAPI2.AsAccountProfile(); err != nil { + t.Fatal(err) + } + if err := acntSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "CustomAccount2"}}, &reply2); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(convAcc2, reply2) { + t.Errorf("Expecting : %+v, received: %+v", convAcc2, reply2) + } + + var eEc *utils.ExtEventCharges + if err := acntSRPC.Call(utils.AccountSv1DebitAbstracts, + &utils.ArgsAccountsForEvent{CGREvent: &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "testAccountSv1SimpleDebit", + Event: map[string]interface{}{ + utils.AccountField: "CustomAccount", + utils.Usage: "900", + }}}, &eEc); err != nil { + t.Error(err) + } else if eEc.Abstracts == nil || *eEc.Abstracts != 900.0 { + t.Fatalf("received usage: %v", *eEc.Abstracts) + } + + if err := acntSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "CustomAccount"}}, &reply2); err != nil { + t.Error(err) + } else if reply2.Balances["Balance1"].Units.Cmp(decimal.New(50, 0)) != 0 { + t.Errorf("Expecting : %s, received: %s", decimal.New(50, 0), reply2.Balances["Balance1"].Units) + } + + if err := acntSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "CustomAccount2"}}, &reply2); err != nil { + t.Error(err) + } else if reply2.Balances["Balance1"].Units.Cmp(decimal.New(10, 0)) != 0 { + t.Errorf("Expecting : %s, received: %s", decimal.New(10, 0), reply2.Balances["Balance1"].Units) + } +} + +func testAccountSv1DebitWithAttributeSandRateS(t *testing.T) { + accPrfAPI := &utils.APIAccountProfileWithOpts{ + APIAccountProfile: &utils.APIAccountProfile{ + Tenant: "cgrates.org", + ID: "ACC_WITH_ATTRIBUTES", + FilterIDs: []string{"*string:~*req.Account:ACC_WITH_ATTRIBUTES"}, + Weights: ";10", + Balances: map[string]*utils.APIBalance{ + "Balance1": &utils.APIBalance{ + ID: "Balance1", + Weights: ";10", + Type: utils.MetaConcrete, + Units: 100, + CostIncrements: []*utils.APICostIncrement{ + { + Increment: utils.Float64Pointer(1), + RecurrentFee: utils.Float64Pointer(-1), + }, + }, + AttributeIDs: []string{"*constant:*req.CustomField:CustomValue"}, + }, + }, + ThresholdIDs: []string{utils.MetaNone}, + }, + } + var reply string + if err := acntSRPC.Call(utils.APIerSv1SetAccountProfile, accPrfAPI, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Error("Unexpected reply returned", reply) + } + var err error + var convAcc *utils.AccountProfile + if convAcc, err = accPrfAPI.AsAccountProfile(); err != nil { + t.Error(err) + } + var reply2 *utils.AccountProfile + if err := acntSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "ACC_WITH_ATTRIBUTES"}}, &reply2); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(convAcc, reply2) { + t.Errorf("Expecting : %+v, received: %+v", convAcc, reply2) + } + + var eEc *utils.ExtEventCharges + if err := acntSRPC.Call(utils.AccountSv1DebitAbstracts, + &utils.ArgsAccountsForEvent{CGREvent: &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "testAccountSv1DebitWithAttributeS", + Event: map[string]interface{}{ + utils.AccountField: "ACC_WITH_ATTRIBUTES", + utils.Usage: "10", + }}}, &eEc); err != nil { + t.Error(err) + } else if eEc.Abstracts == nil || *eEc.Abstracts != 10.0 { + t.Fatalf("received usage: %v", *eEc.Abstracts) + } + + if err := acntSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "ACC_WITH_ATTRIBUTES"}}, &reply2); err != nil { + t.Error(err) + } else if reply2.Balances["Balance1"].Units.Cmp(decimal.New(99, 0)) != 0 { + t.Errorf("Expecting : %+v, received: %s", decimal.New(99, 0), reply2.Balances["Balance1"].Units) + } +} + +func testAccountSv1DebitWithRateS(t *testing.T) { + accPrfAPI := &utils.APIAccountProfileWithOpts{ + APIAccountProfile: &utils.APIAccountProfile{ + Tenant: "cgrates.org", + ID: "ACC_WITH_RATES", + FilterIDs: []string{"*string:~*req.Account:ACC_WITH_RATES"}, + Weights: ";10", + Balances: map[string]*utils.APIBalance{ + "Balance1": &utils.APIBalance{ + ID: "Balance1", + Weights: ";10", + Type: utils.MetaConcrete, + Units: 100, + CostIncrements: []*utils.APICostIncrement{ + { + Increment: utils.Float64Pointer(1), + RecurrentFee: utils.Float64Pointer(-1), + }, + }, + }, + }, + ThresholdIDs: []string{utils.MetaNone}, + }, + } + var reply string + if err := acntSRPC.Call(utils.APIerSv1SetAccountProfile, accPrfAPI, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Error("Unexpected reply returned", reply) + } + var err error + var convAcc *utils.AccountProfile + if convAcc, err = accPrfAPI.AsAccountProfile(); err != nil { + t.Error(err) + } + var reply2 *utils.AccountProfile + if err := acntSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "ACC_WITH_RATES"}}, &reply2); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(convAcc, reply2) { + t.Errorf("Expecting : %+v, received: %+v", convAcc, reply2) + } + + var eEc *utils.ExtEventCharges + if err := acntSRPC.Call(utils.AccountSv1DebitAbstracts, + &utils.ArgsAccountsForEvent{CGREvent: &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "testAccountSv1DebitWithAttributeS", + Event: map[string]interface{}{ + utils.AccountField: "ACC_WITH_RATES", + utils.Usage: "20", + }}}, &eEc); err != nil { + t.Error(err) + } else if eEc.Abstracts == nil || *eEc.Abstracts != 20.0 { + t.Fatalf("received usage: %v", *eEc.Abstracts) + } + + if err := acntSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "ACC_WITH_RATES"}}, &reply2); err != nil { + t.Error(err) + } else if reply2.Balances["Balance1"].Units.Cmp(decimal.New(95, 0)) != 0 { + t.Errorf("Expecting : %+v, received: %s", decimal.New(95, 0), reply2.Balances["Balance1"].Units) + } +} + +func testAccountSv1DebitWithRateS2(t *testing.T) { + accPrfAPI := &utils.APIAccountProfileWithOpts{ + APIAccountProfile: &utils.APIAccountProfile{ + Tenant: "cgrates.org", + ID: "ACC_WITH_RATES2", + FilterIDs: []string{"*string:~*req.Account:ACC_WITH_RATES2"}, + Weights: ";10", + Balances: map[string]*utils.APIBalance{ + "Balance1": &utils.APIBalance{ + ID: "Balance1", + Weights: ";10", + Type: utils.MetaAbstract, + Units: 100, + CostIncrements: []*utils.APICostIncrement{ + { + Increment: utils.Float64Pointer(1), + RecurrentFee: utils.Float64Pointer(-1), + }, + }, + }, + "Balance2": &utils.APIBalance{ + ID: "Balance2", + Weights: ";10", + Type: utils.MetaConcrete, + Units: 100, + }, + }, + ThresholdIDs: []string{utils.MetaNone}, + }, + } + var reply string + if err := acntSRPC.Call(utils.APIerSv1SetAccountProfile, accPrfAPI, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Error("Unexpected reply returned", reply) + } + var err error + var convAcc *utils.AccountProfile + if convAcc, err = accPrfAPI.AsAccountProfile(); err != nil { + t.Error(err) + } + var reply2 *utils.AccountProfile + if err := acntSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "ACC_WITH_RATES2"}}, &reply2); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(convAcc, reply2) { + t.Errorf("Expecting : %+v, received: %+v", convAcc, reply2) + } + + var eEc *utils.ExtEventCharges + if err := acntSRPC.Call(utils.AccountSv1DebitAbstracts, + &utils.ArgsAccountsForEvent{CGREvent: &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "testAccountSv1DebitWithAttributeS", + Event: map[string]interface{}{ + utils.AccountField: "ACC_WITH_RATES2", + utils.Usage: "20", + }}}, &eEc); err != nil { + t.Error(err) + } else if eEc.Abstracts == nil || *eEc.Abstracts != 20.0 { + t.Fatalf("received usage: %v", *eEc.Abstracts) + } + + if err := acntSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "ACC_WITH_RATES2"}}, &reply2); err != nil { + t.Error(err) + } else if reply2.Balances["Balance1"].Units.Cmp(decimal.New(80, 0)) != 0 { + t.Errorf("Expecting : %+v, received: %s", decimal.New(80, 0), reply2.Balances["Balance1"].Units) + } +} + +func testAccountSv1MaxConcretes(t *testing.T) { + apiAccPrf = &utils.APIAccountProfileWithOpts{ + APIAccountProfile: &utils.APIAccountProfile{ + Tenant: "cgrates.org", + ID: "1004", + FilterIDs: []string{"*string:~*req.Account:1004"}, + Balances: map[string]*utils.APIBalance{ + "ConcreteBalance1": &utils.APIBalance{ + ID: "ConcreteBalance1", + Weights: ";20", + Type: utils.MetaConcrete, + Units: 21, + CostIncrements: []*utils.APICostIncrement{ + &utils.APICostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*data"}, + Increment: utils.Float64Pointer(1), + FixedFee: utils.Float64Pointer(0), + RecurrentFee: utils.Float64Pointer(1), + }, + }, + }, + "ConcreteBalance2": &utils.APIBalance{ + ID: "ConcreteBalance2", + Weights: ";10", + Type: utils.MetaConcrete, + Units: 20, + CostIncrements: []*utils.APICostIncrement{ + &utils.APICostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*data"}, + Increment: utils.Float64Pointer(1), + FixedFee: utils.Float64Pointer(0), + RecurrentFee: utils.Float64Pointer(1), + }, + }, + }, + "AbstractBalance1": &utils.APIBalance{ + ID: "AbstractBalance1", + Weights: ";5", + Type: utils.MetaAbstract, + Units: 20, + CostIncrements: []*utils.APICostIncrement{ + &utils.APICostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*data"}, + Increment: utils.Float64Pointer(float64(time.Second)), + FixedFee: utils.Float64Pointer(0), + RecurrentFee: utils.Float64Pointer(1), + }, + }, + }, + }, + ThresholdIDs: []string{utils.MetaNone}, + }, + } + + var reply string + if err := acntSRPC.Call(utils.APIerSv1SetAccountProfile, apiAccPrf, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Error("Unexpected reply returned", reply) + } + + exp, err := apiAccPrf.AsAccountProfile() + if err != nil { + t.Error(err) + } + var result *utils.AccountProfile + if err := acntSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "1004"}}, &result); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(exp, result) { + t.Errorf("Expected %+v\n, received %+v", utils.ToJSON(exp), utils.ToJSON(result)) + } + + var eEc *utils.ExtEventCharges + if err := acntSRPC.Call(utils.AccountSv1MaxConcretes, + &utils.ArgsAccountsForEvent{CGREvent: &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "testAccountSv1MaxConcretes", + Event: map[string]interface{}{ + utils.AccountField: "1004", + utils.ToR: utils.MetaData, + utils.Usage: "50ns", + }, + }}, &eEc); err != nil { + t.Error(err) + } else if eEc.Concretes == nil || *eEc.Concretes != 41 { + t.Errorf("received usage: %v", *eEc.Concretes) + } + + //make sure we did not Debit from our Account + exp, err = apiAccPrf.AsAccountProfile() + if err != nil { + t.Error(err) + } + if err := acntSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "1004"}}, &result); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(exp, result) { + t.Errorf("Expected %+v\n, received %+v", utils.ToJSON(exp), utils.ToJSON(result)) + } +} +func testAccountSv1DebitConcretes(t *testing.T) { + var eEc *utils.ExtEventCharges + //Now we know the usage, we will debit it from account + if err := acntSRPC.Call(utils.AccountSv1DebitConcretes, + &utils.ArgsAccountsForEvent{CGREvent: &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "testAccountSv1MaxConcretes", + Event: map[string]interface{}{ + utils.AccountField: "1004", + utils.ToR: utils.MetaData, + utils.Usage: "50ns", + }, + }}, &eEc); err != nil { + t.Error(err) + } else if eEc.Concretes == nil || *eEc.Concretes != 41 { + t.Errorf("received usage: %v", *eEc.Concretes) + } + + exp := &utils.AccountProfile{ + Tenant: "cgrates.org", + ID: "1004", + FilterIDs: []string{"*string:~*req.Account:1004"}, + Balances: map[string]*utils.Balance{ + "ConcreteBalance1": &utils.Balance{ + ID: "ConcreteBalance1", + Weights: utils.DynamicWeights{ + { + Weight: 20, + }, + }, + Type: utils.MetaConcrete, + Units: &utils.Decimal{decimal.New(0, 0)}, + CostIncrements: []*utils.CostIncrement{ + &utils.CostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*data"}, + Increment: &utils.Decimal{decimal.New(1, 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(1, 0)}, + }, + }, + }, + "ConcreteBalance2": &utils.Balance{ + ID: "ConcreteBalance2", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }, + }, + Type: utils.MetaConcrete, + Units: &utils.Decimal{decimal.New(0, 0)}, + CostIncrements: []*utils.CostIncrement{ + &utils.CostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*data"}, + Increment: &utils.Decimal{decimal.New(1, 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(1, 0)}, + }, + }, + }, + "AbstractBalance1": &utils.Balance{ + ID: "AbstractBalance1", + Weights: utils.DynamicWeights{ + { + Weight: 5, + }, + }, + Type: utils.MetaAbstract, + Units: &utils.Decimal{decimal.New(20, 0)}, + CostIncrements: []*utils.CostIncrement{ + &utils.CostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*data"}, + Increment: &utils.Decimal{decimal.New(int64(time.Second), 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(1, 0)}, + }, + }, + }, + }, + ThresholdIDs: []string{utils.MetaNone}, + } + + var result *utils.AccountProfile + //As we debit, our Account balances are changed now + if err := acntSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "1004"}}, &result); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(exp, result) { + t.Errorf("Expected %+v\n, received %+v", utils.ToJSON(exp), utils.ToJSON(result)) + } +} + +func testAccountSv1ActionSetBalance(t *testing.T) { + expectedSetBalance := &utils.AccountProfile{ + Tenant: "cgrates.org", + ID: "1004", + FilterIDs: []string{"*string:~*req.Account:1004"}, + Balances: map[string]*utils.Balance{ + "ConcreteBalance1": &utils.Balance{ + ID: "ConcreteBalance1", + Weights: utils.DynamicWeights{ + { + FilterIDs: []string{"fltr1", "fltr2"}, + Weight: 20, + }, + { + FilterIDs: []string{"fltr1"}, + Weight: 30, + }, + }, + Type: utils.MetaConcrete, + Units: &utils.Decimal{decimal.New(0, 0)}, + CostIncrements: []*utils.CostIncrement{ + &utils.CostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*data"}, + Increment: &utils.Decimal{decimal.New(1, 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(1, 0)}, + }, + }, + }, + "ConcreteBalance2": &utils.Balance{ + ID: "ConcreteBalance2", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }, + }, + Type: utils.MetaConcrete, + Units: &utils.Decimal{decimal.New(0, 0)}, + CostIncrements: []*utils.CostIncrement{ + &utils.CostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*data"}, + Increment: &utils.Decimal{decimal.New(1, 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(1, 0)}, + }, + }, + }, + "AbstractBalance1": &utils.Balance{ + ID: "AbstractBalance1", + Weights: utils.DynamicWeights{ + { + Weight: 5, + }, + }, + Type: utils.MetaAbstract, + Units: &utils.Decimal{decimal.New(120, 0)}, + CostIncrements: []*utils.CostIncrement{ + &utils.CostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*data"}, + Increment: &utils.Decimal{decimal.New(int64(time.Second), 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(1, 0)}, + }, + }, + }, + }, + ThresholdIDs: []string{"TH_ID1"}, + } + var reply string + if err := acntSRPC.Call(utils.AccountSv1ActionSetBalance, &utils.ArgsActSetBalance{ + Tenant: "cgrates.org", AccountID: "1004", + Diktats: []*utils.BalDiktat{ + { + Path: "*account.ThresholdIDs", + Value: "TH_ID1", + }, + }, + }, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Errorf("Unexpected reply returned") + } + if err := acntSRPC.Call(utils.AccountSv1ActionSetBalance, &utils.ArgsActSetBalance{ + Tenant: "cgrates.org", AccountID: "1004", + Diktats: []*utils.BalDiktat{ + { + Path: "*balance.AbstractBalance1.Units", + Value: "120", + }, + }, + Reset: true, + }, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Errorf("Unexpected reply returned") + } + if err := acntSRPC.Call(utils.AccountSv1ActionSetBalance, &utils.ArgsActSetBalance{ + Tenant: "cgrates.org", AccountID: "1004", + Diktats: []*utils.BalDiktat{ + { + Path: "*balance.ConcreteBalance1.Weights", + Value: "fltr1&fltr2;20;fltr1;30", + }, + }, + }, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Errorf("Unexpected reply returned") + } + + var result *utils.AccountProfile + if err := acntSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "1004"}}, &result); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(expectedSetBalance, result) { + t.Errorf("Expected %+v\n, received %+v", utils.ToJSON(expectedSetBalance), utils.ToJSON(result)) + } +} + +func testAccountSv1ActionRemoveBalance(t *testing.T) { + var reply string + if err := acntSRPC.Call(utils.AccountSv1ActionRemoveBalance, &utils.ArgsActRemoveBalances{ + Tenant: "cgrates.org", AccountID: "1004", + BalanceIDs: []string{"AbstractBalance1"}, + }, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Errorf("Unexpected reply returned") + } + + expectedSetBalance := &utils.AccountProfile{ + Tenant: "cgrates.org", + ID: "1004", + FilterIDs: []string{"*string:~*req.Account:1004"}, + Balances: map[string]*utils.Balance{ + "ConcreteBalance1": &utils.Balance{ + ID: "ConcreteBalance1", + Weights: utils.DynamicWeights{ + { + FilterIDs: []string{"fltr1", "fltr2"}, + Weight: 20, + }, + { + FilterIDs: []string{"fltr1"}, + Weight: 30, + }, + }, + Type: utils.MetaConcrete, + Units: &utils.Decimal{decimal.New(0, 0)}, + CostIncrements: []*utils.CostIncrement{ + &utils.CostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*data"}, + Increment: &utils.Decimal{decimal.New(1, 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(1, 0)}, + }, + }, + }, + "ConcreteBalance2": &utils.Balance{ + ID: "ConcreteBalance2", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }, + }, + Type: utils.MetaConcrete, + Units: &utils.Decimal{decimal.New(0, 0)}, + CostIncrements: []*utils.CostIncrement{ + &utils.CostIncrement{ + FilterIDs: []string{"*string:~*req.ToR:*data"}, + Increment: &utils.Decimal{decimal.New(1, 0)}, + FixedFee: &utils.Decimal{decimal.New(0, 0)}, + RecurrentFee: &utils.Decimal{decimal.New(1, 0)}, + }, + }, + }, + }, + ThresholdIDs: []string{"TH_ID1"}, + } + + var result *utils.AccountProfile + if err := acntSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "1004"}}, &result); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(expectedSetBalance, result) { + t.Errorf("Expected %+v\n, received %+v", utils.ToJSON(expectedSetBalance), utils.ToJSON(result)) + } +} + +func testAccountSv1KillEngine(t *testing.T) { + if err := engine.KillEngine(100); err != nil { + t.Error(err) + } +} diff --git a/apier/v1/cost_bench_it_test.go b/apier/v1/cost_bench_it_test.go deleted file mode 100644 index 52333ca91..000000000 --- a/apier/v1/cost_bench_it_test.go +++ /dev/null @@ -1,234 +0,0 @@ -// +build integration - -/* -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 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see -*/ - -package v1 - -import ( - "net/rpc" - "path" - "testing" - "time" - - "github.com/cgrates/cgrates/config" - "github.com/cgrates/cgrates/engine" - "github.com/cgrates/cgrates/utils" -) - -var ( - costBenchCfgPath string - costBenchCfg *config.CGRConfig - costBenchRPC *rpc.Client - costBenchConfigDIR string //run tests for specific configuration -) - -func testCostBenchInitCfg(b *testing.B) { - var err error - costBenchCfgPath = path.Join(*dataDir, "conf", "samples", costBenchConfigDIR) - costBenchCfg, err = config.NewCGRConfigFromPath(costBenchCfgPath) - if err != nil { - b.Error(err) - } -} - -func testCostBenchInitDataDb(b *testing.B) { - if err := engine.InitDataDb(costBenchCfg); err != nil { - b.Fatal(err) - } -} - -// Wipe out the cdr database -func testCostBenchResetStorDb(b *testing.B) { - if err := engine.InitStorDb(costBenchCfg); err != nil { - b.Fatal(err) - } -} - -// Start CGR Engine -func testCostBenchStartEngine(b *testing.B) { - if _, err := engine.StopStartEngine(costBenchCfgPath, 500); err != nil { - b.Fatal(err) - } -} - -// Connect rpc client to rater -func testCostBenchRPCConn(b *testing.B) { - var err error - costBenchRPC, err = newRPCClient(costBenchCfg.ListenCfg()) // We connect over JSON so we can also troubleshoot if needed - if err != nil { - b.Fatal(err) - } -} - -func testCostBenchLoadFromFolder(b *testing.B) { - var reply string - attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "tutorial")} - if err := costBenchRPC.Call(utils.APIerSv1LoadTariffPlanFromFolder, attrs, &reply); err != nil { - b.Error(err) - } - time.Sleep(500 * time.Millisecond) -} - -func testCostBenchLoadFromFolder2(b *testing.B) { - var reply string - attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "tutorial2")} - if err := costBenchRPC.Call(utils.APIerSv1LoadTariffPlanFromFolder, attrs, &reply); err != nil { - b.Error(err) - } - time.Sleep(500 * time.Millisecond) -} - -// go test -run=^$ -tags=integration -v -bench=BenchmarkCostWithRALs -benchtime=5s -func BenchmarkCostWithRALs(b *testing.B) { - costBenchConfigDIR = "tutinternal" - testCostBenchInitCfg(b) - testCostBenchInitDataDb(b) - testCostBenchResetStorDb(b) - testCostBenchStartEngine(b) - testCostBenchRPCConn(b) - testCostBenchLoadFromFolder(b) - - tNow := time.Now() - cd := &engine.CallDescriptorWithAPIOpts{ - CallDescriptor: &engine.CallDescriptor{ - Category: "call", - Tenant: "cgrates.org", - Subject: "1001", - Account: "1001", - Destination: "1002", - DurationIndex: 120000000000, - TimeStart: tNow, - TimeEnd: tNow.Add(120000000000), - }, - } - var cc engine.CallCost - b.ResetTimer() - for i := 0; i < b.N; i++ { - if err := costBenchRPC.Call(utils.ResponderGetCost, cd, &cc); err != nil { - b.Error("Got error on Responder.GetCost: ", err.Error()) - } - } - b.StopTimer() - testCostBenchKillEngine(b) -} - -// go test -run=^$ -tags=integration -v -bench=BenchmarkCostDiffPeriodWithRALs -benchtime=5s -func BenchmarkCostDiffPeriodWithRALs(b *testing.B) { - costBenchConfigDIR = "tutinternal" - testCostBenchInitCfg(b) - testCostBenchInitDataDb(b) - testCostBenchResetStorDb(b) - testCostBenchStartEngine(b) - testCostBenchRPCConn(b) - testCostBenchLoadFromFolder2(b) - - tStart, _ := utils.ParseTimeDetectLayout("2020-12-09T07:00:00Z", utils.EmptyString) - tEnd, _ := utils.ParseTimeDetectLayout("2020-12-09T09:00:00Z", utils.EmptyString) - cd := &engine.CallDescriptorWithAPIOpts{ - CallDescriptor: &engine.CallDescriptor{ - Category: "call", - Tenant: "cgrates.org", - Subject: "1010", - Destination: "1012", - DurationIndex: tEnd.Sub(tStart), - TimeStart: tStart, - TimeEnd: tEnd, - }, - } - var cc engine.CallCost - b.ResetTimer() - for i := 0; i < b.N; i++ { - if err := costBenchRPC.Call(utils.ResponderGetCost, cd, &cc); err != nil { - b.Error("Got error on Responder.GetCost: ", err.Error()) - } - } - b.StopTimer() - testCostBenchKillEngine(b) -} - -// go test -run=^$ -tags=integration -v -bench=BenchmarkCostWithRateS -benchtime=5s -func BenchmarkCostWithRateS(b *testing.B) { - costBenchConfigDIR = "tutinternal" - testCostBenchInitCfg(b) - testCostBenchInitDataDb(b) - testCostBenchResetStorDb(b) - testCostBenchStartEngine(b) - testCostBenchRPCConn(b) - testCostBenchSetRateProfile(b) - var rply *utils.RateProfileCost - argsRt := &utils.ArgsCostForEvent{ - CGREvent: &utils.CGREvent{ - Tenant: "cgrates.org", - ID: utils.UUIDSha1Prefix(), - Event: map[string]interface{}{ - utils.Subject: "1001", - }, - APIOpts: map[string]interface{}{ - utils.OptsRatesUsage: "2m", - }, - }, - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - if err := costBenchRPC.Call(utils.RateSv1CostForEvent, &argsRt, &rply); err != nil { - b.Error(err) - } - } - b.StopTimer() - testCostBenchKillEngine(b) -} - -// go test -run=^$ -tags=integration -v -bench=BenchmarkCostDiffPeriodWithRateS -benchtime=5s -func BenchmarkCostDiffPeriodWithRateS(b *testing.B) { - costBenchConfigDIR = "tutinternal" - testCostBenchInitCfg(b) - testCostBenchInitDataDb(b) - testCostBenchResetStorDb(b) - testCostBenchStartEngine(b) - testCostBenchRPCConn(b) - testCostBenchSetRateProfile2(b) - var rply *utils.RateProfileCost - argsRt := &utils.ArgsCostForEvent{ - CGREvent: &utils.CGREvent{ - Tenant: "cgrates.org", - ID: utils.UUIDSha1Prefix(), - Event: map[string]interface{}{ - utils.Subject: "1010", - }, - APIOpts: map[string]interface{}{ - utils.OptsRatesStartTime: time.Date(2020, 12, 23, 59, 0, 0, 0, time.UTC), - utils.OptsRatesUsage: "2h", - }, - }, - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - if err := costBenchRPC.Call(utils.RateSv1CostForEvent, &argsRt, &rply); err != nil { - b.Error(err) - } - } - b.StopTimer() - testCostBenchKillEngine(b) -} - -func testCostBenchKillEngine(b *testing.B) { - if err := engine.KillEngine(500); err != nil { - b.Error(err) - } -} diff --git a/apier/v1/dispatcher.go b/apier/v1/dispatcher.go index aaaf0a9c3..32a4ff2bd 100755 --- a/apier/v1/dispatcher.go +++ b/apier/v1/dispatcher.go @@ -1291,24 +1291,6 @@ func (dS *DispatcherReplicatorSv1) RemoveActionProfile(args *utils.TenantIDWithA return dS.dS.ReplicatorSv1RemoveActionProfile(args, reply) } -func NewDispatcherRateSv1(dps *dispatchers.DispatcherService) *DispatcherRateSv1 { - return &DispatcherRateSv1{dR: dps} -} - -// Exports RPC from RLs -type DispatcherRateSv1 struct { - dR *dispatchers.DispatcherService -} - -// Ping implements RateSv1Ping -func (dR *DispatcherRateSv1) Ping(args *utils.CGREvent, reply *string) error { - return dR.dR.RateSv1Ping(args, reply) -} - -func (dR *DispatcherRateSv1) CostForEvent(args *utils.ArgsCostForEvent, rpCost *utils.RateProfileCost) error { - return dR.dR.RateSv1CostForEvent(args, rpCost) -} - func NewDispatcherActionSv1(dps *dispatchers.DispatcherService) *DispatcherActionSv1 { return &DispatcherActionSv1{dR: dps} } diff --git a/apier/v1/full_remote_it_test.go b/apier/v1/full_remote_it_test.go index 77b82649f..d6b61d1e2 100644 --- a/apier/v1/full_remote_it_test.go +++ b/apier/v1/full_remote_it_test.go @@ -56,7 +56,6 @@ var ( testFullRemoteITFilter, testFullRemoteITCharger, testFullRemoteITDispatcher, - testFullRemoteITRate, testFullRemoteITAction, testFullRemoteITKillEngine, } @@ -585,84 +584,6 @@ func testFullRemoteITDispatcher(t *testing.T) { } } -func testFullRemoteITRate(t *testing.T) { - // verify for not found in internal - var reply *utils.RateProfile - if err := fullRemInternalRPC.Call(utils.APIerSv1GetRateProfile, - utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "RP1"}}, - &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { - t.Fatal(err) - } - - var replySet string - apiRPrf := &utils.APIRateProfileWithAPIOpts{ - APIRateProfile: &utils.APIRateProfile{ - Tenant: "cgrates.org", - ID: "RP1", - FilterIDs: []string{"*string:~*req.Subject:1001"}, - Weights: ";0", - MaxCostStrategy: "*free", - Rates: map[string]*utils.APIRate{ - "RT_WEEK": { - ID: "RT_WEEK", - Weights: ";0", - ActivationTimes: "* * * * 1-5", - }, - "RT_WEEKEND": { - ID: "RT_WEEKEND", - Weights: ";10", - ActivationTimes: "* * * * 0,6", - }, - "RT_CHRISTMAS": { - ID: "RT_CHRISTMAS", - Weights: ";30", - ActivationTimes: "* * 24 12 *", - }, - }, - }, - } - // add a threshold profile in engine1 and verify it internal - if err := fullRemEngineOneRPC.Call(utils.APIerSv1SetRateProfile, apiRPrf, &replySet); err != nil { - t.Error(err) - } else if replySet != utils.OK { - t.Error("Unexpected reply returned", replySet) - } - - rPrf, err := apiRPrf.AsRateProfile() - if err != nil { - t.Error(err) - } - if err := fullRemInternalRPC.Call(utils.APIerSv1GetRateProfile, - utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "RP1"}}, - &reply); err != nil { - t.Fatal(err) - } - reply.Compile() - if !reflect.DeepEqual(rPrf, reply) { - t.Errorf("Expecting : %+v, received: %+v", utils.ToJSON(rPrf), utils.ToJSON(reply)) - } - // update the threshold profile and verify it to be updated - apiRPrf.FilterIDs = []string{"*string:~*req.Account:1001", "*string:~*req.Destination:1002"} - if err := fullRemEngineOneRPC.Call(utils.APIerSv1SetRateProfile, apiRPrf, &replySet); err != nil { - t.Error(err) - } else if replySet != utils.OK { - t.Error("Unexpected reply returned", replySet) - } - rPrf, err = apiRPrf.AsRateProfile() - if err != nil { - t.Error(err) - } - if err := fullRemInternalRPC.Call(utils.APIerSv1GetRateProfile, - utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "RP1"}}, - &reply); err != nil { - t.Fatal(err) - } - reply.Compile() - if !reflect.DeepEqual(rPrf, reply) { - t.Errorf("Expecting : %+v, received: %+v", utils.ToJSON(rPrf), utils.ToJSON(reply)) - } -} - func testFullRemoteITAction(t *testing.T) { // verify for not found in internal var reply *engine.ActionProfile diff --git a/apier/v1/precache_it_test.go b/apier/v1/precache_it_test.go index bb57f2bff..700c5ab98 100644 --- a/apier/v1/precache_it_test.go +++ b/apier/v1/precache_it_test.go @@ -170,23 +170,20 @@ func testPrecacheGetCacheStatsAfterRestart(t *testing.T) { Items: 2, Groups: 2, }, - utils.CacheAttributeProfiles: {Items: 1}, - utils.CacheChargerFilterIndexes: {}, - utils.CacheChargerProfiles: {}, - utils.CacheDispatcherFilterIndexes: {}, - utils.CacheDispatcherProfiles: {Items: 6}, - utils.CacheDispatcherHosts: {Items: 1}, - utils.CacheDispatcherRoutes: {}, - utils.CacheDispatcherLoads: {}, - utils.CacheDestinations: {Items: 5}, - utils.CacheDispatchers: {}, - utils.CacheEventResources: {}, - utils.CacheFilters: {Items: 15}, - utils.CacheRateProfilesFilterIndexes: {}, - utils.CacheRateFilterIndexes: {}, - utils.CacheRateProfiles: {}, - utils.CacheRatingPlans: {Items: 4}, - utils.CacheRatingProfiles: {Items: 5}, + utils.CacheAttributeProfiles: {Items: 1}, + utils.CacheChargerFilterIndexes: {}, + utils.CacheChargerProfiles: {}, + utils.CacheDispatcherFilterIndexes: {}, + utils.CacheDispatcherProfiles: {Items: 6}, + utils.CacheDispatcherHosts: {Items: 1}, + utils.CacheDispatcherRoutes: {}, + utils.CacheDispatcherLoads: {}, + utils.CacheDestinations: {Items: 5}, + utils.CacheDispatchers: {}, + utils.CacheEventResources: {}, + utils.CacheFilters: {Items: 15}, + utils.CacheRatingPlans: {Items: 4}, + utils.CacheRatingProfiles: {Items: 5}, utils.CacheResourceFilterIndexes: { Items: 6, Groups: 1, @@ -214,47 +211,43 @@ func testPrecacheGetCacheStatsAfterRestart(t *testing.T) { Items: 10, Groups: 1, }, - utils.CacheThresholdProfiles: {Items: 7}, - utils.CacheThresholds: {Items: 7}, - utils.CacheTimings: {}, - utils.CacheDiameterMessages: {}, - utils.CacheClosedSessions: {}, - utils.CacheLoadIDs: {}, - utils.CacheRPCConnections: {}, - utils.CacheCDRIDs: {}, - utils.CacheRatingProfilesTmp: {}, - utils.CacheUCH: {}, - utils.CacheReverseFilterIndexes: {}, - utils.CacheAccounts: {}, - utils.CacheVersions: {}, - utils.CacheTBLTPTimings: {}, - utils.CacheTBLTPDestinations: {}, - utils.CacheTBLTPRates: {}, - utils.CacheTBLTPDestinationRates: {}, - utils.CacheTBLTPRatingPlans: {}, - utils.CacheTBLTPRatingProfiles: {}, - utils.CacheTBLTPSharedGroups: {}, - utils.CacheTBLTPActions: {}, - utils.CacheTBLTPActionPlans: {}, - utils.CacheTBLTPActionTriggers: {}, - utils.CacheTBLTPAccountActions: {}, - utils.CacheTBLTPResources: {}, - utils.CacheTBLTPStats: {}, - utils.CacheTBLTPThresholds: {}, - utils.CacheTBLTPFilters: {}, - utils.CacheSessionCostsTBL: {}, - utils.CacheCDRsTBL: {}, - utils.CacheTBLTPRoutes: {}, - utils.CacheTBLTPAttributes: {}, - utils.CacheTBLTPChargers: {}, - utils.CacheTBLTPDispatchers: {}, - utils.CacheTBLTPDispatcherHosts: {}, - utils.CacheTBLTPRateProfiles: {}, - utils.MetaAPIBan: {}, - utils.CacheActionProfiles: {}, - utils.CacheActionProfilesFilterIndexes: {}, - utils.CacheTBLTPActionProfiles: {}, - utils.CacheReplicationHosts: {}, + utils.CacheThresholdProfiles: {Items: 7}, + utils.CacheThresholds: {Items: 7}, + utils.CacheTimings: {}, + utils.CacheDiameterMessages: {}, + utils.CacheClosedSessions: {}, + utils.CacheLoadIDs: {}, + utils.CacheRPCConnections: {}, + utils.CacheCDRIDs: {}, + utils.CacheRatingProfilesTmp: {}, + utils.CacheUCH: {}, + utils.CacheReverseFilterIndexes: {}, + utils.CacheAccounts: {}, + utils.CacheVersions: {}, + utils.CacheTBLTPTimings: {}, + utils.CacheTBLTPDestinations: {}, + utils.CacheTBLTPRates: {}, + utils.CacheTBLTPDestinationRates: {}, + utils.CacheTBLTPRatingPlans: {}, + utils.CacheTBLTPRatingProfiles: {}, + utils.CacheTBLTPSharedGroups: {}, + utils.CacheTBLTPActions: {}, + utils.CacheTBLTPActionPlans: {}, + utils.CacheTBLTPActionTriggers: {}, + utils.CacheTBLTPAccountActions: {}, + utils.CacheTBLTPResources: {}, + utils.CacheTBLTPStats: {}, + utils.CacheTBLTPThresholds: {}, + utils.CacheTBLTPFilters: {}, + utils.CacheSessionCostsTBL: {}, + utils.CacheCDRsTBL: {}, + utils.CacheTBLTPRoutes: {}, + utils.CacheTBLTPAttributes: {}, + utils.CacheTBLTPChargers: {}, + utils.CacheTBLTPDispatchers: {}, + utils.CacheTBLTPDispatcherHosts: {}, + utils.MetaAPIBan: {}, + utils.CacheReplicationHosts: {}, } if *apiBan { (*expectedStats)[utils.MetaAPIBan] = <cache.CacheStats{Items: 254} diff --git a/apier/v1/remote_it_test.go b/apier/v1/remote_it_test.go index d48cc1dd4..95ee337a4 100644 --- a/apier/v1/remote_it_test.go +++ b/apier/v1/remote_it_test.go @@ -73,7 +73,6 @@ var ( testInternalRemoteITGetChargerProfile, testInternalRemoteITGetDispatcherProfile, testInternalRemoteITGetDispatcherHost, - testInternalRemoteITGetRateProfile, testInternalReplicationSetThreshold, testInternalMatchThreshold, @@ -663,98 +662,6 @@ func testInternalRemoteITGetDispatcherHost(t *testing.T) { } } -func testInternalRemoteITGetRateProfile(t *testing.T) { - var rcv *utils.RateProfile - if err := internalRPC.Call(utils.APIerSv1GetRateProfile, - &utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "RP1"}}, - &rcv); err == nil || err.Error() != utils.ErrNotFound.Error() { - t.Error(err) - } - - rPrf := &utils.RateProfile{ - Tenant: "cgrates.org", - ID: "RP1", - FilterIDs: []string{"*string:~*req.Subject:1001"}, - Weights: utils.DynamicWeights{ - { - Weight: 0, - }, - }, - MaxCostStrategy: "*free", - Rates: map[string]*utils.Rate{ - "RT_WEEK": { - ID: "RT_WEEK", - Weights: utils.DynamicWeights{ - { - Weight: 0, - }, - }, - ActivationTimes: "* * * * 1-5", - }, - "RT_WEEKEND": { - ID: "RT_WEEKEND", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }, - }, - ActivationTimes: "* * * * 0,6", - }, - "RT_CHRISTMAS": { - ID: "RT_CHRISTMAS", - Weights: utils.DynamicWeights{ - { - Weight: 30, - }, - }, - ActivationTimes: "* * 24 12 *", - }, - }, - } - apiRPrf := &utils.APIRateProfileWithAPIOpts{ - APIRateProfile: &utils.APIRateProfile{ - Tenant: "cgrates.org", - ID: "RP1", - FilterIDs: []string{"*string:~*req.Subject:1001"}, - Weights: ";0", - MaxCostStrategy: "*free", - Rates: map[string]*utils.APIRate{ - "RT_WEEK": { - ID: "RT_WEEK", - Weights: ";0", - ActivationTimes: "* * * * 1-5", - }, - "RT_WEEKEND": { - ID: "RT_WEEKEND", - Weights: ";10", - ActivationTimes: "* * * * 0,6", - }, - "RT_CHRISTMAS": { - ID: "RT_CHRISTMAS", - Weights: ";30", - ActivationTimes: "* * 24 12 *", - }, - }, - }, - } - var reply string - if err := engineTwoRPC.Call(utils.APIerSv1SetRateProfile, - apiRPrf, &reply); err != nil { - t.Error(err) - } else if reply != utils.OK { - t.Errorf("Expecting : %+v, received: %+v", utils.OK, reply) - } - - var rPfrg *utils.RateProfile - if err := internalRPC.Call(utils.APIerSv1GetRateProfile, - &utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "RP1"}}, - &rPfrg); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(rPrf, rPfrg) { - t.Errorf("Expecting : %+v, received: %+v", rPrf, rPfrg) - } -} - func testInternalReplicationSetThreshold(t *testing.T) { var reply *engine.ThresholdProfile var result string diff --git a/apier/v1/replicate_it_test.go b/apier/v1/replicate_it_test.go index 7ce33a747..6cd90d974 100644 --- a/apier/v1/replicate_it_test.go +++ b/apier/v1/replicate_it_test.go @@ -54,7 +54,6 @@ var ( testInternalReplicateITSetAccount, testInternalReplicateITActionTrigger, testInternalReplicateITThreshold, - testInternalReplicateITRateProfile, testInternalReplicateITLoadIds, testInternalReplicateITKillEngine, @@ -1335,113 +1334,6 @@ func testInternalReplicateITThreshold(t *testing.T) { } -func testInternalReplicateITRateProfile(t *testing.T) { - //set - rPrf := &utils.RateProfile{ - Tenant: "cgrates.org", - ID: "RP1", - FilterIDs: []string{"*string:~*req.Subject:1001"}, - Weights: utils.DynamicWeights{ - { - Weight: 0, - }, - }, - MaxCostStrategy: "*free", - Rates: map[string]*utils.Rate{ - "RT_WEEK": { - ID: "RT_WEEK", - Weights: utils.DynamicWeights{ - { - Weight: 0, - }, - }, - ActivationTimes: "* * * * 1-5", - }, - "RT_WEEKEND": { - ID: "RT_WEEKEND", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }, - }, - ActivationTimes: "* * * * 0,6", - }, - "RT_CHRISTMAS": { - ID: "RT_CHRISTMAS", - Weights: utils.DynamicWeights{ - { - Weight: 30, - }, - }, - ActivationTimes: "* * 24 12 *", - }, - }, - } - - apiRPrf := &utils.APIRateProfile{ - Tenant: "cgrates.org", - ID: "RP1", - FilterIDs: []string{"*string:~*req.Subject:1001"}, - Weights: ";0", - MaxCostStrategy: "*free", - Rates: map[string]*utils.APIRate{ - "RT_WEEK": { - ID: "RT_WEEK", - Weights: ";0", - ActivationTimes: "* * * * 1-5", - }, - "RT_WEEKEND": { - ID: "RT_WEEKEND", - Weights: ";10", - ActivationTimes: "* * * * 0,6", - }, - "RT_CHRISTMAS": { - ID: "RT_CHRISTMAS", - Weights: ";30", - ActivationTimes: "* * 24 12 *", - }, - }, - } - var result string - if err := internalRPC.Call(utils.APIerSv1SetRateProfile, apiRPrf, &result); err != nil { - t.Error(err) - } else if result != utils.OK { - t.Error("Unexpected reply returned", result) - } - // check - var reply *utils.RateProfile - if err := engineOneRPC.Call(utils.APIerSv1GetRateProfile, - utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{Tenant: rPrf.Tenant, ID: rPrf.ID}}, &reply); err != nil { - t.Fatal(err) - } else if !reflect.DeepEqual(rPrf, reply) { - t.Errorf("Expecting : %+v, received: %+v", rPrf, reply) - } - if err := engineTwoRPC.Call(utils.APIerSv1GetRateProfile, - utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{Tenant: rPrf.Tenant, ID: rPrf.ID}}, &reply); err != nil { - t.Fatal(err) - } else if !reflect.DeepEqual(rPrf, reply) { - t.Errorf("Expecting : %+v, received: %+v", rPrf, reply) - } - //remove - if err := internalRPC.Call(utils.APIerSv1RemoveRateProfile, &utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{ - Tenant: rPrf.Tenant, ID: rPrf.ID}}, &result); err != nil { - t.Error(err) - } else if result != utils.OK { - t.Error("Unexpected reply returned", result) - } - //check again - if err := engineOneRPC.Call(utils.APIerSv1GetRateProfile, - utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{Tenant: rPrf.Tenant, ID: rPrf.ID}}, &reply); err == nil || - err.Error() != utils.ErrNotFound.Error() { - t.Errorf("Expecting: %+v received: %+v", utils.ErrNotFound, err) - } - if err := engineTwoRPC.Call(utils.APIerSv1GetRateProfile, - utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{Tenant: rPrf.Tenant, ID: rPrf.ID}}, &reply); err == nil || - err.Error() != utils.ErrNotFound.Error() { - t.Errorf("Expecting: %+v received: %+v", utils.ErrNotFound, err) - } -} - func testInternalReplicateITLoadIds(t *testing.T) { // get LoadIDs var rcv1e1 map[string]int64 diff --git a/apier/v1/replicator.go b/apier/v1/replicator.go index b252a4882..c6964b812 100644 --- a/apier/v1/replicator.go +++ b/apier/v1/replicator.go @@ -310,17 +310,6 @@ func (rplSv1 *ReplicatorSv1) GetDispatcherHost(tntID *utils.TenantIDWithAPIOpts, return nil } -// GetRateProfile is the remote method coresponding to the dataDb driver method -func (rplSv1 *ReplicatorSv1) GetRateProfile(tntID *utils.TenantIDWithAPIOpts, reply *utils.RateProfile) error { - engine.UpdateReplicationFilters(utils.RateProfilePrefix, tntID.TenantID.TenantID(), utils.IfaceAsString(tntID.APIOpts[utils.RemoteHostOpt])) - rcv, err := rplSv1.dm.DataDB().GetRateProfileDrv(tntID.Tenant, tntID.ID) - if err != nil { - return err - } - *reply = *rcv - return nil -} - // GetActionProfile is the remote method coresponding to the dataDb driver method func (rplSv1 *ReplicatorSv1) GetActionProfile(tntID *utils.TenantIDWithAPIOpts, reply *engine.ActionProfile) error { engine.UpdateReplicationFilters(utils.ActionProfilePrefix, tntID.TenantID.TenantID(), utils.IfaceAsString(tntID.APIOpts[utils.RemoteHostOpt])) @@ -650,19 +639,6 @@ func (rplSv1 *ReplicatorSv1) SetDispatcherHost(dpp *engine.DispatcherHostWithAPI return } -// SetRateProfile is the replication method coresponding to the dataDb driver method -func (rplSv1 *ReplicatorSv1) SetRateProfile(dpp *utils.RateProfileWithAPIOpts, reply *string) (err error) { - if err = rplSv1.dm.DataDB().SetRateProfileDrv(dpp.RateProfile); err != nil { - return - } - if err = rplSv1.v1.CallCache(utils.IfaceAsString(dpp.APIOpts[utils.CacheOpt]), - dpp.Tenant, utils.CacheRateProfiles, dpp.TenantID(), &dpp.FilterIDs, nil, dpp.APIOpts); err != nil { - return - } - *reply = utils.OK - return -} - // SetActionProfile is the replication method coresponding to the dataDb driver method func (rplSv1 *ReplicatorSv1) SetActionProfile(acp *engine.ActionProfileWithAPIOpts, reply *string) (err error) { if err = rplSv1.dm.DataDB().SetActionProfileDrv(acp.ActionProfile); err != nil { @@ -980,19 +956,6 @@ func (rplSv1 *ReplicatorSv1) RemoveDispatcherProfile(args *utils.TenantIDWithAPI return } -// RemoveRateProfile is the replication method coresponding to the dataDb driver method -func (rplSv1 *ReplicatorSv1) RemoveRateProfile(args *utils.TenantIDWithAPIOpts, reply *string) (err error) { - if err = rplSv1.dm.DataDB().RemoveRateProfileDrv(args.Tenant, args.ID); err != nil { - return - } - if err = rplSv1.v1.CallCache(utils.IfaceAsString(args.APIOpts[utils.CacheOpt]), - args.Tenant, utils.CacheRateProfiles, args.TenantID.TenantID(), nil, nil, args.APIOpts); err != nil { - return - } - *reply = utils.OK - return -} - // RemoveActionProfile is the replication method coresponding to the dataDb driver method func (rplSv1 *ReplicatorSv1) RemoveActionProfile(args *utils.TenantIDWithAPIOpts, reply *string) (err error) { if err = rplSv1.dm.DataDB().RemoveActionProfileDrv(args.Tenant, args.ID); err != nil { diff --git a/apier/v1/tprateprofiles.go b/apier/v1/tprateprofiles.go deleted file mode 100644 index 331bc944f..000000000 --- a/apier/v1/tprateprofiles.go +++ /dev/null @@ -1,95 +0,0 @@ -/* -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 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see -*/ - -package v1 - -import ( - "github.com/cgrates/cgrates/utils" -) - -// SetTPRateProfile creates a new TPRateProfile within a tariff plan -func (apierSv1 *APIerSv1) SetTPRateProfile(attrs *utils.TPRateProfile, reply *string) error { - if missing := utils.MissingStructFields(attrs, []string{utils.TPid, utils.ID}); len(missing) != 0 { - return utils.NewErrMandatoryIeMissing(missing...) - } - if attrs.Tenant == utils.EmptyString { - attrs.Tenant = apierSv1.Config.GeneralCfg().DefaultTenant - } - if err := apierSv1.StorDb.SetTPRateProfiles([]*utils.TPRateProfile{attrs}); err != nil { - return utils.NewErrServerError(err) - } - *reply = utils.OK - return nil -} - -// GetTPRateProfile queries specific TPRateProfile on tariff plan -func (apierSv1 *APIerSv1) GetTPRateProfile(attr *utils.TPTntID, reply *utils.TPRateProfile) error { - if missing := utils.MissingStructFields(attr, []string{utils.TPid, utils.ID}); len(missing) != 0 { //Params missing - return utils.NewErrMandatoryIeMissing(missing...) - } - if attr.Tenant == utils.EmptyString { - attr.Tenant = apierSv1.Config.GeneralCfg().DefaultTenant - } - filter, err := apierSv1.StorDb.GetTPRateProfiles(attr.TPid, attr.Tenant, attr.ID) - if err != nil { - if err.Error() != utils.ErrNotFound.Error() { - err = utils.NewErrServerError(err) - } - return err - } - *reply = *filter[0] - return nil -} - -type AttrGetTPRateProfileIds struct { - TPid string // Tariff plan id - utils.PaginatorWithSearch -} - -// GetTPRateProfileIds queries TPRateProfiles identities on specific tariff plan. -func (apierSv1 *APIerSv1) GetTPRateProfileIds(attrs *AttrGetTPRateProfileIds, reply *[]string) error { - if missing := utils.MissingStructFields(attrs, []string{utils.TPid}); len(missing) != 0 { //Params missing - return utils.NewErrMandatoryIeMissing(missing...) - } - ids, err := apierSv1.StorDb.GetTpTableIds(attrs.TPid, utils.TBLTPRateProfiles, utils.TPDistinctIds{utils.TenantCfg, utils.IDCfg}, - nil, &attrs.PaginatorWithSearch) - if err != nil { - if err.Error() != utils.ErrNotFound.Error() { - err = utils.NewErrServerError(err) - } - return err - } - *reply = ids - return nil -} - -// RemoveTPRateProfile removes specific TPRateProfile on Tariff plan -func (apierSv1 *APIerSv1) RemoveTPRateProfile(attrs *utils.TPTntID, reply *string) error { - if missing := utils.MissingStructFields(attrs, []string{utils.TPid, utils.ID}); len(missing) != 0 { //Params missing - return utils.NewErrMandatoryIeMissing(missing...) - } - if attrs.Tenant == utils.EmptyString { - attrs.Tenant = apierSv1.Config.GeneralCfg().DefaultTenant - } - if err := apierSv1.StorDb.RemTpData(utils.TBLTPRateProfiles, attrs.TPid, - map[string]string{utils.TenantCfg: attrs.Tenant, utils.IDCfg: attrs.ID}); err != nil { - return utils.NewErrServerError(err) - } - *reply = utils.OK - return nil -} diff --git a/apier/v1/tprateprofiles_it_test.go b/apier/v1/tprateprofiles_it_test.go deleted file mode 100644 index b08f488d5..000000000 --- a/apier/v1/tprateprofiles_it_test.go +++ /dev/null @@ -1,242 +0,0 @@ -// +build offline - -/* -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 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see -*/ - -package v1 - -import ( - "net/rpc" - "net/rpc/jsonrpc" - "path" - "reflect" - "sort" - "testing" - - "github.com/cgrates/cgrates/config" - "github.com/cgrates/cgrates/engine" - "github.com/cgrates/cgrates/utils" -) - -var ( - tpRatePrfCfgPath string - tpRatePrfCfg *config.CGRConfig - tpRatePrfRPC *rpc.Client - tpRatePrf *utils.TPRateProfile - tpRatePrfDelay int - tpRatePrfConfigDIR string //run tests for specific configuration -) - -var sTestsTPRatePrf = []func(t *testing.T){ - testTPRatePrfInitCfg, - testTPRatePrfResetStorDb, - testTPRatePrfStartEngine, - testTPRatePrfRPCConn, - testTPRatePrfGetTPRatePrfBeforeSet, - testTPRatePrfSetTPRatePrf, - testTPRatePrfGetTPRatePrfAfterSet, - testTPRatePrfGetTPRatePrfIDs, - testTPRatePrfUpdateTPRatePrf, - testTPRatePrfGetTPRatePrfAfterUpdate, - testTPRatePrfRemTPRatePrf, - testTPRatePrfGetTPRatePrfAfterRemove, - testTPRatePrfKillEngine, -} - -//Test start here -func TestTPRatePrfIT(t *testing.T) { - switch *dbType { - case utils.MetaInternal: - tpRatePrfConfigDIR = "tutinternal" - case utils.MetaMySQL: - tpRatePrfConfigDIR = "tutmysql" - case utils.MetaMongo: - tpRatePrfConfigDIR = "tutmongo" - case utils.MetaPostgres: - t.SkipNow() - default: - t.Fatal("Unknown Database type") - } - for _, stest := range sTestsTPRatePrf { - t.Run(tpRatePrfConfigDIR, stest) - } -} - -func testTPRatePrfInitCfg(t *testing.T) { - var err error - tpRatePrfCfgPath = path.Join(*dataDir, "conf", "samples", tpRatePrfConfigDIR) - tpRatePrfCfg, err = config.NewCGRConfigFromPath(tpRatePrfCfgPath) - if err != nil { - t.Error(err) - } - tpRatePrfDelay = 1000 -} - -// Wipe out the cdr database -func testTPRatePrfResetStorDb(t *testing.T) { - if err := engine.InitStorDb(tpRatePrfCfg); err != nil { - t.Fatal(err) - } -} - -// Start CGR Engine -func testTPRatePrfStartEngine(t *testing.T) { - if _, err := engine.StopStartEngine(tpRatePrfCfgPath, tpRatePrfDelay); err != nil { - t.Fatal(err) - } -} - -// Connect rpc client to rater -func testTPRatePrfRPCConn(t *testing.T) { - var err error - tpRatePrfRPC, err = jsonrpc.Dial(utils.TCP, tpRatePrfCfg.ListenCfg().RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed - if err != nil { - t.Fatal(err) - } -} - -func testTPRatePrfGetTPRatePrfBeforeSet(t *testing.T) { - var reply *utils.TPRateProfile - if err := tpRatePrfRPC.Call(utils.APIerSv1GetTPRateProfile, - &utils.TPTntID{TPid: "TP1", Tenant: "cgrates.org", ID: "Attr1"}, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { - t.Error(err) - } -} - -func testTPRatePrfSetTPRatePrf(t *testing.T) { - tpRatePrf = &utils.TPRateProfile{ - TPid: "TP1", - Tenant: "cgrates.org", - ID: "RT_SPECIAL_1002", - Weights: ";10", - Rates: map[string]*utils.TPRate{ - "RT_ALWAYS": { - ID: "RT_ALWAYS", - FilterIDs: []string{"* * * * *"}, - Weights: ";0", - Blocker: false, - IntervalRates: []*utils.TPIntervalRate{ - { - IntervalStart: "0s", - RecurrentFee: 0.01, - Unit: "1m", - Increment: "1s", - }, - }, - }, - }, - } - var result string - if err := tpRatePrfRPC.Call(utils.APIerSv1SetTPRateProfile, tpRatePrf, &result); err != nil { - t.Error(err) - } else if result != utils.OK { - t.Error("Unexpected reply returned", result) - } -} - -func testTPRatePrfGetTPRatePrfAfterSet(t *testing.T) { - var reply *utils.TPRateProfile - if err := tpRatePrfRPC.Call(utils.APIerSv1GetTPRateProfile, - &utils.TPTntID{TPid: "TP1", Tenant: "cgrates.org", ID: "RT_SPECIAL_1002"}, &reply); err != nil { - t.Fatal(err) - } - sort.Strings(reply.FilterIDs) - if !reflect.DeepEqual(tpRatePrf, reply) { - t.Errorf("Expecting : %+v, received: %+v", utils.ToJSON(tpRatePrf), utils.ToJSON(reply)) - } -} - -func testTPRatePrfGetTPRatePrfIDs(t *testing.T) { - var result []string - expectedTPID := []string{"cgrates.org:RT_SPECIAL_1002"} - if err := tpRatePrfRPC.Call(utils.APIerSv1GetTPRateProfileIds, - &AttrGetTPRateProfileIds{TPid: "TP1"}, &result); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(expectedTPID, result) { - t.Errorf("Expecting: %+v, received: %+v", expectedTPID, result) - } -} - -func testTPRatePrfUpdateTPRatePrf(t *testing.T) { - tpRatePrf.Rates = map[string]*utils.TPRate{ - "RT_ALWAYS": { - ID: "RT_ALWAYS", - FilterIDs: []string{"* * * * *"}, - Weights: ";0", - Blocker: false, - IntervalRates: []*utils.TPIntervalRate{ - { - IntervalStart: "0s", - RecurrentFee: 0.01, - Unit: "1m", - Increment: "1s", - }, - }, - }, - } - var result string - if err := tpRatePrfRPC.Call(utils.APIerSv1SetTPRateProfile, tpRatePrf, &result); err != nil { - t.Error(err) - } else if result != utils.OK { - t.Error("Unexpected reply returned", result) - } -} - -func testTPRatePrfGetTPRatePrfAfterUpdate(t *testing.T) { - var reply *utils.TPRateProfile - revTPRatePrf := &utils.TPRateProfile{ - TPid: "TP1", - Tenant: "cgrates.org", - ID: "RT_SPECIAL_1002", - Weights: ";10", - } - - if err := tpRatePrfRPC.Call(utils.APIerSv1GetTPRateProfile, - &utils.TPTntID{TPid: "TP1", Tenant: "cgrates.org", ID: "RT_SPECIAL_1002"}, &reply); err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(tpRatePrf, reply) && !reflect.DeepEqual(revTPRatePrf, reply) { - t.Errorf("Expecting : %+v, \n received: %+v", utils.ToJSON(tpRatePrf), utils.ToJSON(reply)) - } -} - -func testTPRatePrfRemTPRatePrf(t *testing.T) { - var resp string - if err := tpRatePrfRPC.Call(utils.APIerSv1RemoveTPRateProfile, - &utils.TPTntID{TPid: "TP1", Tenant: "cgrates.org", ID: "RT_SPECIAL_1002"}, - &resp); err != nil { - t.Error(err) - } else if resp != utils.OK { - t.Error("Unexpected reply returned", resp) - } -} - -func testTPRatePrfGetTPRatePrfAfterRemove(t *testing.T) { - var reply *utils.TPActionProfile - if err := tpRatePrfRPC.Call(utils.APIerSv1GetTPRateProfile, - &utils.TPTntID{TPid: "TP1", Tenant: "cgrates.org", ID: "RT_SPECIAL_1002"}, - &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { - t.Error(err) - } -} - -func testTPRatePrfKillEngine(t *testing.T) { - if err := engine.KillEngine(tpRatePrfDelay); err != nil { - t.Error(err) - } -} diff --git a/cmd/cgr-engine/cgr-engine.go b/cmd/cgr-engine/cgr-engine.go index 21e3a3c35..aa3fccab5 100644 --- a/cmd/cgr-engine/cgr-engine.go +++ b/cmd/cgr-engine/cgr-engine.go @@ -677,8 +677,6 @@ func main() { ldrs, anz, dspS, dspH, dmService, storDBService, services.NewEventExporterService(cfg, filterSChan, connManager, server, internalEEsChan, anz, srvDep), - services.NewRateService(cfg, cacheS, filterSChan, dmService, - server, internalRateSChan, anz, srvDep), services.NewSIPAgent(cfg, filterSChan, shdChan, connManager, srvDep), services.NewActionService(cfg, dmService, cacheS, filterSChan, connManager, server, internalActionSChan, anz, srvDep), ) diff --git a/cmd/cgr-tester/proc_event/proc_ev.go b/cmd/cgr-tester/proc_event/proc_ev.go deleted file mode 100644 index 0d57511de..000000000 --- a/cmd/cgr-tester/proc_event/proc_ev.go +++ /dev/null @@ -1,153 +0,0 @@ -/* -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 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see -*/ - -package main - -import ( - "flag" - "fmt" - "log" - "math/rand" - "net/rpc" - "net/rpc/jsonrpc" - "path" - "strconv" - "sync" - "time" - - v1 "github.com/cgrates/cgrates/apier/v1" - "github.com/cgrates/cgrates/engine" - - "github.com/cgrates/cgrates/config" - "github.com/cgrates/cgrates/utils" -) - -var ( - dataDir = flag.String("data_dir", "/usr/share/cgrates", "CGR data dir path here") - requests = flag.Int("requests", 10000, "Number of requests") - gorutines = flag.Int("goroutines", 5, "Number of simultaneous goroutines") -) - -// How to run: -// 1) Start the engine with the following configuration cgr-engine -config_path=/usr/share/cgrates/conf/samples/hundred_rates -// 2) Load the data with cgr-loader cgr-loader -config_path=/usr/share/cgrates/conf/samples/hundred_rates -verbose -path=/usr/share/cgrates/tariffplans/hundredrates -// 3) Run the program with go run proc_ev.go -requests=10000 -goroutines=5 - -func main() { - flag.Parse() - var err error - var rpc *rpc.Client - var cfgPath string - var cfg *config.CGRConfig - cfgPath = path.Join(*dataDir, "conf", "samples", "cdrsv1mysql") - if cfg, err = config.NewCGRConfigFromPath(cfgPath); err != nil { - log.Fatal("Got config error: ", err.Error()) - } - if rpc, err = jsonrpc.Dial(utils.TCP, cfg.ListenCfg().RPCJSONListen); err != nil { - return - } - var sumApier float64 - var sumRateS float64 - s1 := rand.NewSource(time.Now().UnixNano()) - r1 := rand.New(s1) - // - var sls []string - for i := 100; i < 200; i++ { - sls = append(sls, strconv.Itoa(i)) - } - - var wgApier sync.WaitGroup - var wgRateS sync.WaitGroup - var apierTime time.Duration - var rateSTime time.Duration - for i := 0; i < *requests; i++ { - wgApier.Add(1) - wgRateS.Add(1) - destination := fmt.Sprintf("%+v%+v", sls[r1.Intn(100)], 1000000+rand.Intn(9999999-1000000)) - usage := fmt.Sprintf("%+vm", r1.Intn(250)) - go func() { - attrs := v1.AttrGetCost{ - Category: "call", - Tenant: "cgrates.org", - Subject: "*any", - AnswerTime: utils.MetaNow, - Destination: destination, - Usage: usage, - } - var replyApier *engine.EventCost - tNow := time.Now() - if err := rpc.Call(utils.APIerSv1GetCost, &attrs, &replyApier); err != nil { - fmt.Println(err) - return - } - apierTime += time.Now().Sub(tNow) - sumApier += *replyApier.Cost - wgApier.Done() - }() - - go func() { - argsRateS := &utils.ArgsCostForEvent{ - CGREvent: &utils.CGREvent{ - Tenant: "cgrates.org", - ID: utils.UUIDSha1Prefix(), - Event: map[string]interface{}{ - utils.Category: "call", - utils.Tenant: "cgrates.org", - utils.Subject: "*any", - utils.AnswerTime: utils.MetaNow, - utils.Destination: destination, - "PrefixDestination": destination[:3], - }, - APIOpts: map[string]interface{}{ - utils.OptsRatesUsage: usage, - }, - }, - } - - var rplyRateS *utils.RateProfileCost - tNow := time.Now() - if err := rpc.Call(utils.RateSv1CostForEvent, argsRateS, &rplyRateS); err != nil { - fmt.Printf("Unexpected nil error received for RateSv1CostForEvent: %+v\n", err.Error()) - return - } - rateSTime += time.Now().Sub(tNow) - sumRateS += rplyRateS.Cost - wgRateS.Done() - }() - if i%*gorutines == 0 { - wgApier.Wait() - wgRateS.Wait() - } - - } - wgApier.Wait() - wgRateS.Wait() - fmt.Println("Cost for apier get cost : ") - fmt.Println(sumApier) - fmt.Println("Cost for RateS") - fmt.Println(sumRateS) - fmt.Println("Average ApierTime") - fmt.Println(apierTime / time.Duration(*requests)) - fmt.Println("Average RateSTime") - fmt.Println(rateSTime / time.Duration(*requests)) - fmt.Println("Total ApierTime") - fmt.Println(apierTime) - fmt.Println("Total RateSTime") - fmt.Println(rateSTime) - -} diff --git a/config/config_defaults.go b/config/config_defaults.go index 06f14f1e4..4d84f0d69 100644 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -107,7 +107,6 @@ const CGRATES_CFG_JSON = ` "*charger_profiles": {"remote":false, "replicate":false}, "*dispatcher_profiles":{"remote":false, "replicate":false}, "*dispatcher_hosts":{"remote":false, "replicate":false}, - "*rate_profiles":{"remote":false, "replicate":false}, "*action_profiles":{"remote":false, "replicate":false}, "*load_ids":{"remote":false, "replicate":false}, "*indexes":{"remote":false, "replicate":false}, @@ -167,7 +166,6 @@ const CGRATES_CFG_JSON = ` "*versions": {"remote":false, "replicate":false}, "*tp_dispatcher_profiles":{"remote":false, "replicate":false}, "*tp_dispatcher_hosts":{"remote":false, "replicate":false}, - "*tp_rate_profiles":{"remote":false, "replicate":false}, "*tp_action_profiles":{"remote":false, "replicate":false}, }, }, @@ -257,7 +255,6 @@ const CGRATES_CFG_JSON = ` "*charger_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // control charger profile caching "*dispatcher_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // control dispatcher profile caching "*dispatcher_hosts": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // control dispatcher hosts caching - "*rate_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // control rate profile caching "*action_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // control action profile caching "*resource_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, // control resource filter indexes caching "*stat_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, // control stat filter indexes caching @@ -266,8 +263,6 @@ const CGRATES_CFG_JSON = ` "*attribute_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, // control attribute filter indexes caching "*charger_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, // control charger filter indexes caching "*dispatcher_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, // control dispatcher filter indexes caching - "*rate_profile_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, // control rate profile filter indexes caching - "*rate_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, // control rate filter indexes caching "*action_profile_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, // control action profile filter indexes caching "*reverse_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, // control reverse filter indexes caching used only for set and remove filters "*dispatcher_routes": {"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, // control dispatcher routes caching @@ -312,7 +307,6 @@ const CGRATES_CFG_JSON = ` "*tp_chargers":{"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, "*tp_dispatcher_profiles":{"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, "*tp_dispatcher_hosts":{"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, - "*tp_rate_profiles":{"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, "*tp_action_profiles":{"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, }, "replication_conns": [], @@ -799,30 +793,6 @@ const CGRATES_CFG_JSON = ` {"tag": "TLS", "path": "TLS", "type": "*variable", "value": "~*req.4"}, ], }, - { - "type": "*rate_profiles", // data source type - "file_name": "RateProfiles.csv", // file name in the tp_in_dir - "fields": [ - {"tag": "Tenant", "path": "Tenant", "type": "*variable", "value": "~*req.0", "mandatory": true}, - {"tag": "ID", "path": "ID", "type": "*variable", "value": "~*req.1", "mandatory": true}, - {"tag": "FilterIDs", "path": "FilterIDs", "type": "*variable", "value": "~*req.2"}, - {"tag": "ActivationInterval", "path": "ActivationInterval", "type": "*variable", "value": "~*req.3"}, - {"tag": "Weight", "path": "Weight", "type": "*variable", "value": "~*req.4"}, - {"tag": "MinCost", "path": "MinCost", "type": "*variable", "value": "~*req.5"}, - {"tag": "MaxCost", "path": "MaxCost", "type": "*variable", "value": "~*req.6"}, - {"tag": "MaxCostStrategy", "path": "MaxCostStrategy", "type": "*variable", "value": "~*req.7"}, - {"tag": "RateID", "path": "RateID", "type": "*variable", "value": "~*req.8"}, - {"tag": "RateFilterIDs", "path": "RateFilterIDs", "type": "*variable", "value": "~*req.9"}, - {"tag": "RateActivationTimes", "path": "RateActivationTimes", "type": "*variable", "value": "~*req.10"}, - {"tag": "RateWeight", "path": "RateWeight", "type": "*variable", "value": "~*req.11"}, - {"tag": "RateBlocker", "path": "RateBlocker", "type": "*variable", "value": "~*req.12"}, - {"tag": "RateIntervalStart", "path": "RateIntervalStart", "type": "*variable", "value": "~*req.13"}, - {"tag": "RateFixedFee", "path": "RateFixedFee", "type": "*variable", "value": "~*req.14"}, - {"tag": "RateRecurrentFee", "path": "RateRecurrentFee", "type": "*variable", "value": "~*req.15"}, - {"tag": "RateUnit", "path": "RateUnit", "type": "*variable", "value": "~*req.16"}, - {"tag": "RateIncrement", "path": "RateIncrement", "type": "*variable", "value": "~*req.17"}, - ], - }, { "type": "*action_profiles", // data source type "file_name": "ActionProfiles.csv", // file name in the tp_in_dir @@ -845,7 +815,6 @@ const CGRATES_CFG_JSON = ` {"tag": "ActionValue", "path": "ActionValue", "type": "*variable", "value": "~*req.15"}, ], }, - ], }, ], diff --git a/config/config_json_test.go b/config/config_json_test.go index d5c397f73..798f11797 100644 --- a/config/config_json_test.go +++ b/config/config_json_test.go @@ -151,9 +151,6 @@ func TestCacheJsonCfg(t *testing.T) { utils.CacheDispatcherProfiles: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), Precache: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, - utils.CacheRateProfiles: {Limit: utils.IntPointer(-1), - Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Precache: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheActionProfiles: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), Precache: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, @@ -181,12 +178,6 @@ func TestCacheJsonCfg(t *testing.T) { utils.CacheDispatcherFilterIndexes: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, - utils.CacheRateProfilesFilterIndexes: {Limit: utils.IntPointer(-1), - Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Replicate: utils.BoolPointer(false)}, - utils.CacheRateFilterIndexes: {Limit: utils.IntPointer(-1), - Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Replicate: utils.BoolPointer(false)}, utils.CacheActionProfilesFilterIndexes: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, @@ -307,9 +298,6 @@ func TestCacheJsonCfg(t *testing.T) { utils.CacheTBLTPDispatcherHosts: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, - utils.CacheTBLTPRateProfiles: {Limit: utils.IntPointer(-1), - Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Replicate: utils.BoolPointer(false)}, utils.CacheTBLTPActionProfiles: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, @@ -463,10 +451,6 @@ func TestDfDataDbJsonCfg(t *testing.T) { Replicate: utils.BoolPointer(false), Remote: utils.BoolPointer(false), }, - utils.MetaRateProfiles: { - Replicate: utils.BoolPointer(false), - Remote: utils.BoolPointer(false), - }, utils.MetaActionProfiles: { Replicate: utils.BoolPointer(false), Remote: utils.BoolPointer(false), @@ -599,10 +583,6 @@ func TestDfStorDBJsonCfg(t *testing.T) { Replicate: utils.BoolPointer(false), Remote: utils.BoolPointer(false), }, - utils.CacheTBLTPRateProfiles: { - Replicate: utils.BoolPointer(false), - Remote: utils.BoolPointer(false), - }, utils.CacheTBLTPDispatcherHosts: { Replicate: utils.BoolPointer(false), Remote: utils.BoolPointer(false), @@ -1540,86 +1520,6 @@ func TestDfLoaderJsonCfg(t *testing.T) { Value: utils.StringPointer("~*req.4")}, }, }, - { - Type: utils.StringPointer(utils.MetaRateProfiles), - File_name: utils.StringPointer(utils.RateProfilesCsv), - Fields: &[]*FcTemplateJsonCfg{ - {Tag: utils.StringPointer(utils.Tenant), - Path: utils.StringPointer(utils.Tenant), - Type: utils.StringPointer(utils.MetaVariable), - Value: utils.StringPointer("~*req.0"), - Mandatory: utils.BoolPointer(true)}, - {Tag: utils.StringPointer(utils.ID), - Path: utils.StringPointer(utils.ID), - Type: utils.StringPointer(utils.MetaVariable), - Value: utils.StringPointer("~*req.1"), - Mandatory: utils.BoolPointer(true)}, - {Tag: utils.StringPointer("FilterIDs"), - Path: utils.StringPointer("FilterIDs"), - Type: utils.StringPointer(utils.MetaVariable), - Value: utils.StringPointer("~*req.2")}, - {Tag: utils.StringPointer("ActivationInterval"), - Path: utils.StringPointer("ActivationInterval"), - Type: utils.StringPointer(utils.MetaVariable), - Value: utils.StringPointer("~*req.3")}, - {Tag: utils.StringPointer("Weight"), - Path: utils.StringPointer("Weight"), - Type: utils.StringPointer(utils.MetaVariable), - Value: utils.StringPointer("~*req.4")}, - {Tag: utils.StringPointer("MinCost"), - Path: utils.StringPointer("MinCost"), - Type: utils.StringPointer(utils.MetaVariable), - Value: utils.StringPointer("~*req.5")}, - {Tag: utils.StringPointer("MaxCost"), - Path: utils.StringPointer("MaxCost"), - Type: utils.StringPointer(utils.MetaVariable), - Value: utils.StringPointer("~*req.6")}, - {Tag: utils.StringPointer("MaxCostStrategy"), - Path: utils.StringPointer("MaxCostStrategy"), - Type: utils.StringPointer(utils.MetaVariable), - Value: utils.StringPointer("~*req.7")}, - {Tag: utils.StringPointer("RateID"), - Path: utils.StringPointer("RateID"), - Type: utils.StringPointer(utils.MetaVariable), - Value: utils.StringPointer("~*req.8")}, - {Tag: utils.StringPointer("RateFilterIDs"), - Path: utils.StringPointer("RateFilterIDs"), - Type: utils.StringPointer(utils.MetaVariable), - Value: utils.StringPointer("~*req.9")}, - {Tag: utils.StringPointer("RateActivationTimes"), - Path: utils.StringPointer("RateActivationTimes"), - Type: utils.StringPointer(utils.MetaVariable), - Value: utils.StringPointer("~*req.10")}, - {Tag: utils.StringPointer("RateWeight"), - Path: utils.StringPointer("RateWeight"), - Type: utils.StringPointer(utils.MetaVariable), - Value: utils.StringPointer("~*req.11")}, - {Tag: utils.StringPointer("RateBlocker"), - Path: utils.StringPointer("RateBlocker"), - Type: utils.StringPointer(utils.MetaVariable), - Value: utils.StringPointer("~*req.12")}, - {Tag: utils.StringPointer("RateIntervalStart"), - Path: utils.StringPointer("RateIntervalStart"), - Type: utils.StringPointer(utils.MetaVariable), - Value: utils.StringPointer("~*req.13")}, - {Tag: utils.StringPointer("RateFixedFee"), - Path: utils.StringPointer("RateFixedFee"), - Type: utils.StringPointer(utils.MetaVariable), - Value: utils.StringPointer("~*req.14")}, - {Tag: utils.StringPointer("RateRecurrentFee"), - Path: utils.StringPointer("RateRecurrentFee"), - Type: utils.StringPointer(utils.MetaVariable), - Value: utils.StringPointer("~*req.15")}, - {Tag: utils.StringPointer("RateUnit"), - Path: utils.StringPointer("RateUnit"), - Type: utils.StringPointer(utils.MetaVariable), - Value: utils.StringPointer("~*req.16")}, - {Tag: utils.StringPointer("RateIncrement"), - Path: utils.StringPointer("RateIncrement"), - Type: utils.StringPointer(utils.MetaVariable), - Value: utils.StringPointer("~*req.17")}, - }, - }, { Type: utils.StringPointer(utils.MetaActionProfiles), File_name: utils.StringPointer(utils.ActionProfilesCsv), diff --git a/config/config_test.go b/config/config_test.go index 5bb6dc14c..cb50ff6fe 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -570,8 +570,6 @@ func TestCgrCfgJSONDefaultsCacheCFG(t *testing.T) { TTL: 0, StaticTTL: false, Precache: false}, utils.CacheDispatcherProfiles: {Limit: -1, TTL: 0, StaticTTL: false, Precache: false}, - utils.CacheRateProfiles: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, utils.CacheDispatcherHosts: {Limit: -1, TTL: 0, StaticTTL: false, Precache: false}, utils.CacheActionProfiles: {Limit: -1, @@ -590,10 +588,6 @@ func TestCgrCfgJSONDefaultsCacheCFG(t *testing.T) { TTL: 0, StaticTTL: false, Precache: false}, utils.CacheDispatcherFilterIndexes: {Limit: -1, TTL: 0, StaticTTL: false, Precache: false}, - utils.CacheRateProfilesFilterIndexes: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, - utils.CacheRateFilterIndexes: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, utils.CacheActionProfilesFilterIndexes: {Limit: -1, TTL: 0, StaticTTL: false, Precache: false}, utils.CacheReverseFilterIndexes: {Limit: -1, @@ -674,8 +668,6 @@ func TestCgrCfgJSONDefaultsCacheCFG(t *testing.T) { TTL: 0, StaticTTL: false, Precache: false}, utils.CacheTBLTPActionProfiles: {Limit: -1, TTL: 0, StaticTTL: false, Precache: false}, - utils.CacheTBLTPRateProfiles: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, utils.MetaAPIBan: {Limit: -1, TTL: 2 * time.Minute, StaticTTL: false, Precache: false}, utils.CacheReplicationHosts: {Limit: 0, @@ -3270,120 +3262,6 @@ func TestCgrLoaderCfgITDefaults(t *testing.T) { }, }, }, - { - Type: utils.MetaRateProfiles, - Filename: utils.RateProfilesCsv, - Fields: []*FCTemplate{ - {Tag: "Tenant", - Path: "Tenant", - Type: utils.MetaVariable, - Value: NewRSRParsersMustCompile("~*req.0", utils.InfieldSep), - Mandatory: true, - Layout: time.RFC3339}, - {Tag: "ID", - Path: "ID", - Type: utils.MetaVariable, - Value: NewRSRParsersMustCompile("~*req.1", utils.InfieldSep), - Mandatory: true, - Layout: time.RFC3339}, - {Tag: "FilterIDs", - Path: "FilterIDs", - Type: utils.MetaVariable, - Value: NewRSRParsersMustCompile("~*req.2", utils.InfieldSep), - Layout: time.RFC3339, - }, - {Tag: "ActivationInterval", - Path: "ActivationInterval", - Type: utils.MetaVariable, - Value: NewRSRParsersMustCompile("~*req.3", utils.InfieldSep), - Layout: time.RFC3339, - }, - {Tag: "Weight", - Path: "Weight", - Type: utils.MetaVariable, - Value: NewRSRParsersMustCompile("~*req.4", utils.InfieldSep), - Layout: time.RFC3339, - }, - {Tag: "MinCost", - Path: "MinCost", - Type: utils.MetaVariable, - Value: NewRSRParsersMustCompile("~*req.5", utils.InfieldSep), - Layout: time.RFC3339, - }, - {Tag: "MaxCost", - Path: "MaxCost", - Type: utils.MetaVariable, - Value: NewRSRParsersMustCompile("~*req.6", utils.InfieldSep), - Layout: time.RFC3339, - }, - {Tag: "MaxCostStrategy", - Path: "MaxCostStrategy", - Type: utils.MetaVariable, - Value: NewRSRParsersMustCompile("~*req.7", utils.InfieldSep), - Layout: time.RFC3339, - }, - {Tag: "RateID", - Path: "RateID", - Type: utils.MetaVariable, - Value: NewRSRParsersMustCompile("~*req.8", utils.InfieldSep), - Layout: time.RFC3339, - }, - {Tag: "RateFilterIDs", - Path: "RateFilterIDs", - Type: utils.MetaVariable, - Value: NewRSRParsersMustCompile("~*req.9", utils.InfieldSep), - Layout: time.RFC3339, - }, - {Tag: "RateActivationTimes", - Path: "RateActivationTimes", - Type: utils.MetaVariable, - Value: NewRSRParsersMustCompile("~*req.10", utils.InfieldSep), - Layout: time.RFC3339, - }, - {Tag: "RateWeight", - Path: "RateWeight", - Type: utils.MetaVariable, - Value: NewRSRParsersMustCompile("~*req.11", utils.InfieldSep), - Layout: time.RFC3339, - }, - {Tag: "RateBlocker", - Path: "RateBlocker", - Type: utils.MetaVariable, - Value: NewRSRParsersMustCompile("~*req.12", utils.InfieldSep), - Layout: time.RFC3339, - }, - {Tag: "RateIntervalStart", - Path: "RateIntervalStart", - Type: utils.MetaVariable, - Value: NewRSRParsersMustCompile("~*req.13", utils.InfieldSep), - Layout: time.RFC3339, - }, - {Tag: "RateFixedFee", - Path: "RateFixedFee", - Type: utils.MetaVariable, - Value: NewRSRParsersMustCompile("~*req.14", utils.InfieldSep), - Layout: time.RFC3339, - }, - {Tag: "RateRecurrentFee", - Path: "RateRecurrentFee", - Type: utils.MetaVariable, - Value: NewRSRParsersMustCompile("~*req.15", utils.InfieldSep), - Layout: time.RFC3339, - }, - {Tag: "RateUnit", - Path: "RateUnit", - Type: utils.MetaVariable, - Value: NewRSRParsersMustCompile("~*req.16", utils.InfieldSep), - Layout: time.RFC3339, - }, - {Tag: "RateIncrement", - Path: "RateIncrement", - Type: utils.MetaVariable, - Value: NewRSRParsersMustCompile("~*req.17", utils.InfieldSep), - Layout: time.RFC3339, - }, - }, - }, { Type: utils.MetaActionProfiles, Filename: utils.ActionProfilesCsv, @@ -3472,12 +3350,10 @@ func TestCgrLoaderCfgITDefaults(t *testing.T) { Layout: time.RFC3339}, }, }, - }, - }, - } - for _, profile := range eCfg { - for _, fields := range profile.Data { - for _, v := range fields.Fields { + { + Type: utils.MetaAccountProfiles, + Filename: utils.AccountProfilesCsv, + Fields: []*FCTemplate{ v.ComputePath() } } diff --git a/console/ping_test.go b/console/ping_test.go index 458ad0ef7..f3479d457 100644 --- a/console/ping_test.go +++ b/console/ping_test.go @@ -540,74 +540,6 @@ func TestCmdPingEEsLow(t *testing.T) { } } -func TestCmdPingRateSLow(t *testing.T) { - // commands map is initiated in init function - command := commands["ping"] - castCommand, canCast := command.(*CmdApierPing) - if !canCast { - t.Fatalf("cannot cast") - } - castCommand.item = utils.RateSLow - result2 := command.RpcMethod() - if !reflect.DeepEqual(result2, utils.RateSv1Ping) { - t.Errorf("Expected <%+v>, Received <%+v>", utils.RateSv1Ping, result2) - } - m, ok := reflect.TypeOf(new(v1.RateSv1)).MethodByName(strings.Split(command.RpcMethod(), utils.NestingSep)[1]) - if !ok { - t.Fatal("method not found") - } - if m.Type.NumIn() != 3 { // ApierSv1 is consider and we expect 3 inputs - t.Fatalf("invalid number of input parameters ") - } - // for coverage purpose - result := command.RpcParams(true) - if !reflect.DeepEqual(result, new(StringWrapper)) { - t.Errorf("Expected <%T>, Received <%T>", new(StringWrapper), result) - } - // verify the type of output parameter - if ok := m.Type.In(2).AssignableTo(reflect.TypeOf(command.RpcResult())); !ok { - t.Fatalf("cannot assign output parameter") - } - // for coverage purpose - if err := command.PostprocessRpcParams(); err != nil { - t.Fatal(err) - } -} - -func TestCmdPingActionSLow(t *testing.T) { - // commands map is initiated in init function - command := commands["ping"] - castCommand, canCast := command.(*CmdApierPing) - if !canCast { - t.Fatalf("cannot cast") - } - castCommand.item = utils.ActionSLow - result2 := command.RpcMethod() - if !reflect.DeepEqual(result2, utils.ActionSv1Ping) { - t.Errorf("Expected <%+v>, Received <%+v>", utils.ActionSv1Ping, result2) - } - m, ok := reflect.TypeOf(new(v1.ActionSv1)).MethodByName(strings.Split(command.RpcMethod(), utils.NestingSep)[1]) - if !ok { - t.Fatal("method not found") - } - if m.Type.NumIn() != 3 { // ApierSv1 is consider and we expect 3 inputs - t.Fatalf("invalid number of input parameters ") - } - // for coverage purpose - result := command.RpcParams(true) - if !reflect.DeepEqual(result, new(StringWrapper)) { - t.Errorf("Expected <%T>, Received <%T>", new(StringWrapper), result) - } - // verify the type of output parameter - if ok := m.Type.In(2).AssignableTo(reflect.TypeOf(command.RpcResult())); !ok { - t.Fatalf("cannot assign output parameter") - } - // for coverage purpose - if err := command.PostprocessRpcParams(); err != nil { - t.Fatal(err) - } -} - func TestCmdPingTestDefault(t *testing.T) { // commands map is initiated in init function command := commands["ping"] diff --git a/data/conf/samples/filtered_replication/engine1_mongo/cgrates.json b/data/conf/samples/filtered_replication/engine1_mongo/cgrates.json index 959a25578..adc663554 100644 --- a/data/conf/samples/filtered_replication/engine1_mongo/cgrates.json +++ b/data/conf/samples/filtered_replication/engine1_mongo/cgrates.json @@ -50,7 +50,6 @@ "*charger_profiles": {"remote":true,"replicate":false}, "*dispatcher_profiles":{"remote":true,"replicate":false}, "*dispatcher_hosts":{"remote":true,"replicate":false}, - "*rate_profiles":{"remote":true,"replicate":false}, "*load_ids":{"remote":true,"replicate":false}, "*indexes":{"remote":true, "replicate":false}, "*action_profiles":{"remote":true,"replicate":false}, diff --git a/data/conf/samples/filtered_replication/engine1_redis/cgrates.json b/data/conf/samples/filtered_replication/engine1_redis/cgrates.json index 8d9510477..deebb3b39 100644 --- a/data/conf/samples/filtered_replication/engine1_redis/cgrates.json +++ b/data/conf/samples/filtered_replication/engine1_redis/cgrates.json @@ -49,7 +49,6 @@ "*charger_profiles": {"remote":true,"replicate":false}, "*dispatcher_profiles":{"remote":true,"replicate":false}, "*dispatcher_hosts":{"remote":true,"replicate":false}, - "*rate_profiles":{"remote":true,"replicate":false}, "*load_ids":{"remote":true,"replicate":false}, "*indexes":{"remote":true, "replicate":false}, "*action_profiles":{"remote":true,"replicate":false}, diff --git a/data/conf/samples/filtered_replication/engine2_mongo/cgrates.json b/data/conf/samples/filtered_replication/engine2_mongo/cgrates.json index a5ce68cd3..48e75b22c 100644 --- a/data/conf/samples/filtered_replication/engine2_mongo/cgrates.json +++ b/data/conf/samples/filtered_replication/engine2_mongo/cgrates.json @@ -50,7 +50,6 @@ "*charger_profiles": {"remote":true,"replicate":false}, "*dispatcher_profiles":{"remote":true,"replicate":false}, "*dispatcher_hosts":{"remote":true,"replicate":false}, - "*rate_profiles":{"remote":true,"replicate":false}, "*load_ids":{"remote":true,"replicate":false}, "*indexes":{"remote":true, "replicate":false}, "*action_profiles":{"remote":true,"replicate":false}, diff --git a/data/conf/samples/filtered_replication/engine2_redis/cgrates.json b/data/conf/samples/filtered_replication/engine2_redis/cgrates.json index f470b4063..560a3827e 100644 --- a/data/conf/samples/filtered_replication/engine2_redis/cgrates.json +++ b/data/conf/samples/filtered_replication/engine2_redis/cgrates.json @@ -50,7 +50,6 @@ "*charger_profiles": {"remote":true,"replicate":false}, "*dispatcher_profiles":{"remote":true,"replicate":false}, "*dispatcher_hosts":{"remote":true,"replicate":false}, - "*rate_profiles":{"remote":true,"replicate":false}, "*load_ids":{"remote":true,"replicate":false}, "*indexes":{"remote":true, "replicate":false}, "*action_profiles":{"remote":true,"replicate":false}, diff --git a/data/conf/samples/filtered_replication/internal/cgrates.json b/data/conf/samples/filtered_replication/internal/cgrates.json index a787bb126..8b13eeac1 100644 --- a/data/conf/samples/filtered_replication/internal/cgrates.json +++ b/data/conf/samples/filtered_replication/internal/cgrates.json @@ -52,7 +52,6 @@ "*dispatcher_profiles":{"remote":false,"replicate":true}, "*dispatcher_hosts":{"remote":false,"replicate":true}, "*indexes" :{"remote":false,"replicate":true}, - "*rate_profiles":{"remote":false,"replicate":true}, "*load_ids":{"remote":false,"replicate":true}, "*action_profiles":{"remote":false,"replicate":true}, }, diff --git a/data/conf/samples/full_remote/internal/cgrates.json b/data/conf/samples/full_remote/internal/cgrates.json index 57ea613d3..c72b845cb 100644 --- a/data/conf/samples/full_remote/internal/cgrates.json +++ b/data/conf/samples/full_remote/internal/cgrates.json @@ -43,7 +43,6 @@ "*charger_profiles": {"limit":0 }, "*dispatcher_profiles": {"limit":0 }, "*dispatcher_hosts": {"limit":0 }, - "*rate_profiles": {"limit":0 }, "*action_profiles": {"limit":0 }, "*resource_filter_indexes" : {"limit":0}, "*stat_filter_indexes" : {"limit":0}, @@ -52,8 +51,6 @@ "*attribute_filter_indexes" : {"limit":0}, "*charger_filter_indexes" : {"limit":0}, "*dispatcher_filter_indexes" : {"limit":0}, - "*rate_profile_filter_indexes" : {"limit":0}, - "*rate_filter_indexes" : {"limit":0}, "*action_profile_filter_indexes" : {"limit":0}, "*reverse_filter_indexes" : {"limit":0}, "*dispatcher_routes": {"limit":0}, @@ -91,7 +88,6 @@ "*action_profiles":{"remote":true,"replicate":false}, "*dispatcher_profiles":{"remote":true,"replicate":false}, "*dispatcher_hosts":{"remote":true,"replicate":false}, - "*rate_profiles":{"remote":true,"replicate":false}, "*load_ids":{"remote":true,"replicate":false}, "*indexes":{"remote":true, "replicate":false}, }, diff --git a/data/conf/samples/remote_replication/internal/cgrates.json b/data/conf/samples/remote_replication/internal/cgrates.json index c7a540862..380590dcf 100644 --- a/data/conf/samples/remote_replication/internal/cgrates.json +++ b/data/conf/samples/remote_replication/internal/cgrates.json @@ -63,7 +63,6 @@ "*charger_profiles": {"remote":true,"replicate":true}, "*dispatcher_profiles":{"remote":true,"replicate":true}, "*dispatcher_hosts":{"remote":true,"replicate":true}, - "*rate_profiles":{"remote":true,"replicate":true}, "*load_ids":{"remote":true,"replicate":true}, "*indexes":{"remote":true, "replicate":true}, }, diff --git a/data/conf/samples/remote_replication/internal_gob/cgrates.json b/data/conf/samples/remote_replication/internal_gob/cgrates.json index be9957958..e4f92d79c 100644 --- a/data/conf/samples/remote_replication/internal_gob/cgrates.json +++ b/data/conf/samples/remote_replication/internal_gob/cgrates.json @@ -55,7 +55,6 @@ "*charger_profiles": {"remote":true,"replicate":true}, "*dispatcher_profiles":{"remote":true,"replicate":true}, "*dispatcher_hosts":{"remote":true,"replicate":true}, - "*rate_profiles":{"remote":true,"replicate":true}, "*load_ids":{"remote":true,"replicate":true}, "*indexes":{"remote":true, "replicate":true}, }, diff --git a/data/conf/samples/replication/internal/cgrates.json b/data/conf/samples/replication/internal/cgrates.json index 1381a02fd..84e34d166 100644 --- a/data/conf/samples/replication/internal/cgrates.json +++ b/data/conf/samples/replication/internal/cgrates.json @@ -58,7 +58,6 @@ "*dispatcher_profiles":{"remote":false,"replicate":true}, "*dispatcher_hosts":{"remote":false,"replicate":true}, "*indexes" :{"remote":false,"replicate":true}, - "*rate_profiles":{"remote":false,"replicate":true}, "*load_ids":{"remote":false,"replicate":true}, }, }, diff --git a/data/conf/samples/replication/internal_gob/cgrates.json b/data/conf/samples/replication/internal_gob/cgrates.json index 534a640ca..83505bebd 100644 --- a/data/conf/samples/replication/internal_gob/cgrates.json +++ b/data/conf/samples/replication/internal_gob/cgrates.json @@ -58,7 +58,6 @@ "*dispatcher_profiles":{"remote":false,"replicate":true}, "*dispatcher_hosts":{"remote":false,"replicate":true}, "*indexes" :{"remote":false,"replicate":true}, - "*rate_profiles":{"remote":false,"replicate":true}, "*load_ids":{"remote":false,"replicate":true}, } }, diff --git a/data/conf/samples/replication_cache/engine1/cgrates.json b/data/conf/samples/replication_cache/engine1/cgrates.json index b325260e3..962a62a13 100644 --- a/data/conf/samples/replication_cache/engine1/cgrates.json +++ b/data/conf/samples/replication_cache/engine1/cgrates.json @@ -37,9 +37,6 @@ "partitions": { "*attribute_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": true}, "*attribute_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "replicate": true}, - "*rate_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": true}, - "*rate_profile_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "replicate": true}, - "*rate_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "replicate": true} }, "replication_conns": ["connCache"] }, diff --git a/data/storage/mysql/create_tariffplan_tables.sql b/data/storage/mysql/create_tariffplan_tables.sql index f801942c9..be33255b8 100644 --- a/data/storage/mysql/create_tariffplan_tables.sql +++ b/data/storage/mysql/create_tariffplan_tables.sql @@ -469,40 +469,6 @@ CREATE TABLE tp_dispatcher_hosts ( `id`,`address`) ); --- --- Table structure for table `tp_rate_profiles` --- - - -DROP TABLE IF EXISTS tp_rate_profiles; -CREATE TABLE tp_rate_profiles ( - `pk` int(11) NOT NULL AUTO_INCREMENT, - `tpid` varchar(64) NOT NULL, - `tenant` varchar(64) NOT NULL, - `id` varchar(64) NOT NULL, - `filter_ids` varchar(64) NOT NULL, - `activation_interval` varchar(64) NOT NULL, - `weights` varchar(64) NOT NULL, - `min_cost` decimal(8,4) NOT NULL, - `max_cost` decimal(8,4) NOT NULL, - `max_cost_strategy` varchar(64) NOT NULL, - `rate_id` varchar(32) NOT NULL, - `rate_filter_ids` varchar(64) NOT NULL, - `rate_activation_times` varchar(64) NOT NULL, - `rate_weights` varchar(64) NOT NULL, - `rate_blocker` BOOLEAN NOT NULL, - `rate_interval_start` varchar(64) NOT NULL, - `rate_fixed_fee` decimal(8,4) NOT NULL, - `rate_recurrent_fee` decimal(8,4) NOT NULL, - `rate_unit` varchar(64) NOT NULL, - `rate_increment` varchar(64) NOT NULL, - `created_at` TIMESTAMP, - PRIMARY KEY (`pk`), - KEY `tpid` (`tpid`), - UNIQUE KEY `unique_tp_rate_profiles` (`tpid`,`tenant`, - `id`,`filter_ids`,`rate_id` ) -); - -- -- Table structure for table `tp_action_profiles` -- diff --git a/data/storage/postgres/create_tariffplan_tables.sql b/data/storage/postgres/create_tariffplan_tables.sql index d823fc2ea..fb504fdb1 100644 --- a/data/storage/postgres/create_tariffplan_tables.sql +++ b/data/storage/postgres/create_tariffplan_tables.sql @@ -455,37 +455,6 @@ CREATE INDEX tp_routes_unique ON tp_routes ("tpid", "tenant", "id", CREATE INDEX tp_dispatcher_hosts_unique ON tp_dispatcher_hosts ("tpid", "tenant", "id", "address"); --- --- Table structure for table `tp_rate_profiles` --- - - DROP TABLE IF EXISTS tp_rate_profiles; - CREATE TABLE tp_rate_profiles ( - "pk" SERIAL PRIMARY KEY, - "tpid" varchar(64) NOT NULL, - "tenant" varchar(64) NOT NULL, - "id" varchar(64) NOT NULL, - "filter_ids" varchar(64) NOT NULL, - "activation_interval" varchar(64) NOT NULL, - "weights" varchar(64) NOT NULL, - "min_cost" decimal(8,4) NOT NULL, - "max_cost" decimal(8,4) NOT NULL, - "max_cost_strategy" VARCHAR(64) NOT NULL, - "rate_id" VARCHAR(64) NOT NULL, - "rate_filter_ids" VARCHAR(64) NOT NULL, - "rate_activation_times" VARCHAR(64) NOT NULL, - "rate_weights" varchar(64) NOT NULL, - "rate_blocker" BOOLEAN NOT NULL, - "rate_interval_start" VARCHAR(64) NOT NULL, - "rate_fixed_fee" decimal(8,4) NOT NULL, - "rate_recurrent_fee" decimal(8,4) NOT NULL, - "rate_unit" VARCHAR(64) NOT NULL, - "rate_increment" VARCHAR(64) NOT NULL, - "created_at" TIMESTAMP WITH TIME ZONE - ); - CREATE INDEX tp_rate_profiles_ids ON tp_rate_profiles (tpid); - CREATE INDEX tp_rate_profiles_unique ON tp_rate_profiles ("tpid", "tenant", "id", - "filter_ids", "rate_id"); -- -- Table structure for table `tp_action_profiles` diff --git a/engine/storage_mongo_datadb.go b/engine/storage_mongo_datadb.go index 68b6aa28c..c57084b05 100644 --- a/engine/storage_mongo_datadb.go +++ b/engine/storage_mongo_datadb.go @@ -76,7 +76,6 @@ const ( ColCpp = "charger_profiles" ColDpp = "dispatcher_profiles" ColDph = "dispatcher_hosts" - ColRpp = "rate_profiles" ColApp = "action_profiles" ColLID = "load_ids" ) @@ -315,7 +314,7 @@ func (ms *MongoStorage) ensureIndexesForCol(col string) (err error) { // exporte if err = ms.enusureIndex(col, true, "key"); err != nil { return } - case ColRsP, ColRes, ColSqs, ColSqp, ColTps, ColThs, ColRts, ColAttr, ColFlt, ColCpp, ColDpp, ColDph, ColRpp, ColApp: + case ColRsP, ColRes, ColSqs, ColSqp, ColTps, ColThs, ColRts, ColAttr, ColFlt, ColCpp, ColDpp, ColDph: if err = ms.enusureIndex(col, true, "tenant", "id"); err != nil { return } @@ -379,7 +378,7 @@ func (ms *MongoStorage) EnsureIndexes(cols ...string) (err error) { if ms.storageType == utils.DataDB { for _, col := range []string{ColAct, ColApl, ColAAp, ColAtr, ColRpl, ColDst, ColRds, ColLht, ColIndx, ColRsP, ColRes, ColSqs, ColSqp, - ColTps, ColThs, ColRts, ColAttr, ColFlt, ColCpp, ColDpp, ColRpp, ColApp, + ColTps, ColThs, ColRts, ColAttr, ColFlt, ColCpp, ColDpp, ColRpf, ColShg, ColAcc} { if err = ms.ensureIndexesForCol(col); err != nil { return diff --git a/general_tests/export_it_test.go b/general_tests/export_it_test.go index dce1b0da2..388b436a6 100644 --- a/general_tests/export_it_test.go +++ b/general_tests/export_it_test.go @@ -59,7 +59,6 @@ var ( testExpVerifyResources, testExpVerifyStats, testExpVerifyRoutes, - testExpVerifyRateProfiles, testExpVerifyActionProfiles, testExpCleanFiles, testExpStopCgrEngine, @@ -363,67 +362,6 @@ func testExpVerifyRoutes(t *testing.T) { } } -func testExpVerifyRateProfiles(t *testing.T) { - var reply *utils.RateProfile - minDecimal, err := utils.NewDecimalFromUsage("1m") - if err != nil { - t.Error(err) - } - secDecimal, err := utils.NewDecimalFromUsage("1s") - if err != nil { - t.Error(err) - } - - splPrf := &utils.RateProfile{ - Tenant: "cgrates.org", - ID: "RT_SPECIAL_1002", - FilterIDs: []string{"*string:~*req.Account:1002"}, - ActivationInterval: nil, - Weights: utils.DynamicWeights{ - { - Weight: 10, - }, - }, - MinCost: utils.NewDecimal(0, 0), - MaxCost: utils.NewDecimal(0, 0), - MaxCostStrategy: utils.MetaMaxCostFree, - Rates: map[string]*utils.Rate{ - "RT_ALWAYS": { - ID: "RT_ALWAYS", - FilterIDs: nil, - ActivationTimes: "* * * * *", - Weights: utils.DynamicWeights{ - { - Weight: 0, - }, - }, - Blocker: false, - IntervalRates: []*utils.IntervalRate{ - { - IntervalStart: utils.NewDecimal(int64(0*time.Second), 0), - RecurrentFee: utils.NewDecimal(1, 2), - Unit: minDecimal, - Increment: secDecimal, - FixedFee: utils.NewDecimal(0, 0), - }, - }, - }, - }, - } - - if *encoding == utils.MetaGOB { - splPrf.FilterIDs = nil - } - if err := expRpc.Call(utils.APIerSv1GetRateProfile, &utils.TenantIDWithAPIOpts{ - TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "RT_SPECIAL_1002"}}, &reply); err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(splPrf, reply) { - t.Errorf("Expecting: %+v,\n received: %+v", - utils.ToJSON(splPrf), utils.ToJSON(reply)) - } -} - func testExpVerifyActionProfiles(t *testing.T) { var reply *engine.ActionProfile actPrf := &engine.ActionProfile{ diff --git a/migrator/migrator.go b/migrator/migrator.go index 2c66fd8a1..04d9cd168 100755 --- a/migrator/migrator.go +++ b/migrator/migrator.go @@ -219,9 +219,6 @@ func (m *Migrator) Migrate(taskIDs []string) (err error, stats map[string]int) { if err := m.migrateThresholds(); err != nil { log.Print("ERROR: ", utils.MetaThresholds, " ", err) } - if err := m.migrateRouteProfiles(); err != nil { - log.Print("ERROR: ", utils.MetaRoutes, " ", err) - } if err := m.migrateAttributeProfile(); err != nil { log.Print("ERROR: ", utils.MetaAttributes, " ", err) } diff --git a/migrator/migrator_datadb.go b/migrator/migrator_datadb.go index a3ea977fc..5c58ef843 100644 --- a/migrator/migrator_datadb.go +++ b/migrator/migrator_datadb.go @@ -83,7 +83,6 @@ type MigratorDataDB interface { getV1ChargerProfile() (v1chrPrf *engine.ChargerProfile, err error) getV1DispatcherProfile() (v1chrPrf *engine.DispatcherProfile, err error) - getV1RouteProfile() (v1chrPrf *engine.RouteProfile, err error) getV3Stats() (v1st *engine.StatQueueProfile, err error) getV3ThresholdProfile() (v2T *engine.ThresholdProfile, err error) diff --git a/migrator/routes.go b/migrator/routes.go index 82f79de52..1013c8327 100644 --- a/migrator/routes.go +++ b/migrator/routes.go @@ -19,7 +19,6 @@ along with this program. If not, see package migrator import ( - "errors" "fmt" "strings" @@ -137,67 +136,6 @@ func (m *Migrator) migrateCurrentRouteProfile() (err error) { return } -func (m *Migrator) migrateRouteProfiles() (err error) { - var vrs engine.Versions - current := engine.CurrentDataDBVersions() - if vrs, err = m.getVersions(utils.ActionTriggers); err != nil { - return - } - routeVersion, has := vrs[utils.Routes] - if !has { - if vrs[utils.RQF] != current[utils.RQF] { - return fmt.Errorf("please migrate the filters before migrating the routes") - } - if err = m.migrateFromSupplierToRoute(); err != nil { - return - } - } - migrated := true - var v2 *engine.RouteProfile - for { - version := routeVersion - for { - switch version { - default: - return fmt.Errorf("Unsupported version %v", version) - case current[utils.Routes]: - migrated = false - if m.sameDataDB { - break - } - if err = m.migrateCurrentRouteProfile(); err != nil { - return err - } - case 1: - if v2, err = m.migrateV1ToV2Routes(); err != nil && err != utils.ErrNoMoreData { - return - } else if err == utils.ErrNoMoreData { - break - } - version = 2 - } - if version == current[utils.Routes] || err == utils.ErrNoMoreData { - break - } - } - if err == utils.ErrNoMoreData || !migrated { - break - } - if !m.dryRun { - if err = m.dmIN.DataManager().SetRouteProfile(v2, true); err != nil { - return - } - } - m.stats[utils.Routes]++ - } - // All done, update version wtih current one - if err = m.setVersions(utils.Routes); err != nil { - return - } - - return m.ensureIndexesDataDB(engine.ColRts) -} - func convertSupplierToRoute(spp *SupplierProfile) (route *engine.RouteProfile) { route = &engine.RouteProfile{ Tenant: spp.Tenant, @@ -224,16 +162,3 @@ func convertSupplierToRoute(spp *SupplierProfile) (route *engine.RouteProfile) { } return } - -func (m *Migrator) migrateV1ToV2Routes() (v4Cpp *engine.RouteProfile, err error) { - v4Cpp, err = m.dmIN.getV1RouteProfile() - if err != nil { - return nil, err - } else if v4Cpp == nil { - return nil, errors.New("Dispatcher NIL") - } - if v4Cpp.FilterIDs, err = migrateInlineFilterV4(v4Cpp.FilterIDs); err != nil { - return nil, err - } - return -} diff --git a/migrator/storage_mongo_datadb.go b/migrator/storage_mongo_datadb.go index 1f898cd52..d2da3456f 100644 --- a/migrator/storage_mongo_datadb.go +++ b/migrator/storage_mongo_datadb.go @@ -852,22 +852,3 @@ func (v1ms *mongoMigrator) getV1DispatcherProfile() (v1dppPrf *engine.Dispatcher } return } - -func (v1ms *mongoMigrator) getV1RouteProfile() (v1dppPrf *engine.RouteProfile, err error) { - if v1ms.cursor == nil { - v1ms.cursor, err = v1ms.mgoDB.DB().Collection(engine.ColRpp).Find(v1ms.mgoDB.GetContext(), bson.D{}) - if err != nil { - return nil, err - } - } - if !(*v1ms.cursor).Next(v1ms.mgoDB.GetContext()) { - (*v1ms.cursor).Close(v1ms.mgoDB.GetContext()) - v1ms.cursor = nil - return nil, utils.ErrNoMoreData - } - v1dppPrf = new(engine.RouteProfile) - if err := (*v1ms.cursor).Decode(v1dppPrf); err != nil { - return nil, err - } - return -} diff --git a/services/dispatchers.go b/services/dispatchers.go index ede861874..4964c98fe 100644 --- a/services/dispatchers.go +++ b/services/dispatchers.go @@ -145,9 +145,6 @@ func (dspS *DispatcherService) Start() (err error) { dspS.server.RpcRegisterName(utils.CDRsV2, v2.NewDispatcherSCDRsV2(dspS.dspS)) - dspS.server.RpcRegisterName(utils.RateSv1, - v1.NewDispatcherRateSv1(dspS.dspS)) - dspS.server.RpcRegisterName(utils.ActionSv1, v1.NewDispatcherActionSv1(dspS.dspS)) diff --git a/utils/consts.go b/utils/consts.go index 075342722..ce3d33837 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -1924,7 +1924,6 @@ const ( CacheTBLTPChargers = "*tp_chargers" CacheTBLTPDispatchers = "*tp_dispatcher_profiles" CacheTBLTPDispatcherHosts = "*tp_dispatcher_hosts" - CacheTBLTPActionProfiles = "*tp_action_profiles" ) // Prefix for indexing