/* 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 engine import ( "errors" "reflect" "testing" "time" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" "github.com/cgrates/rpcclient" ) func TestDmSetSupplierProfileRpl(t *testing.T) { cfg, _ := config.NewDefaultCGRConfig() defer func() { cfg2, _ := config.NewDefaultCGRConfig() config.SetCgrConfig(cfg2) }() db := NewInternalDB(nil, nil, true, cfg.DataDbCfg().Items) cfg.DataDbCfg().Items[utils.MetaSupplierProfiles].Replicate = true cfg.DataDbCfg().RplConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.ReplicatorSv1)} clientConn := make(chan rpcclient.ClientConnector, 1) clientConn <- &ccMock{ calls: map[string]func(args interface{}, reply interface{}) error{ utils.ReplicatorSv1SetSupplierProfile: func(args, reply interface{}) error { *reply.(*string) = utils.OK return nil }, }, } connMgr := NewConnManager(cfg, map[string]chan rpcclient.ClientConnector{ utils.ConcatenatedKey(utils.MetaInternal, utils.ReplicatorSv1): clientConn, }) dm := NewDataManager(db, cfg.CacheCfg(), connMgr) fltrSupp1 := &Filter{ Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, ID: "FLTR_SUPP_1", Rules: []*FilterRule{ { Type: utils.MetaString, Element: "~*req.Supplier", Values: []string{"SupplierProfile2"}, }, { Type: utils.MetaGreaterOrEqual, Element: "~*req.PddInterval", Values: []string{(1 * time.Second).String()}, }, { Type: utils.MetaGreaterOrEqual, Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Weight, Values: []string{"15.0"}, }, }, } if err := dm.SetFilter(fltrSupp1); err != nil { t.Error(err) } fltrSupp2 := &Filter{ Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, ID: "FLTR_SUPP_2", Rules: []*FilterRule{ { Type: utils.MetaPrefix, Element: "~*req.Supplier", Values: []string{"SupplierProfilePrefix"}, }, }, } if err := dm.SetFilter(fltrSupp2); err != nil { t.Error(err) } supp := &SupplierProfile{ Tenant: "cgrates.org", ID: "SUP1", FilterIDs: []string{"FLTR_SUPP_1"}, Weight: 10, Sorting: utils.MetaQOS, SortingParameters: []string{}, Suppliers: []*Supplier{ { ID: "Sup", FilterIDs: []string{}, AccountIDs: []string{"1001"}, RatingPlanIDs: []string{"RT_PLAN1"}, ResourceIDs: []string{"RES1"}, Weight: 10, }, }, } config.SetCgrConfig(cfg) if err := dm.SetSupplierProfile(supp, true); err != nil { t.Error(err) } supp1 := &SupplierProfile{ Tenant: "cgrates.org", ID: "SUP1", FilterIDs: []string{"FLTR_SUPP_2"}, Weight: 10, Sorting: utils.MetaQOS, SortingParameters: []string{}, Suppliers: []*Supplier{ { ID: "Sup", FilterIDs: []string{}, AccountIDs: []string{"1001"}, RatingPlanIDs: []string{"RT_PLAN1"}, ResourceIDs: []string{"RES1"}, Weight: 10, }, }, } if err := dm.SetSupplierProfile(supp1, true); err != nil { t.Error(err) } if err := dm.RemoveSupplierProfile("cgrates.org", supp1.ID, utils.NonTransactional, true); err != nil { t.Error(err) } } func TestDmMatchFilterIndexFromKey(t *testing.T) { cfg, _ := config.NewDefaultCGRConfig() defer func() { cfg2, _ := config.NewDefaultCGRConfig() config.SetCgrConfig(cfg2) }() db := NewInternalDB(nil, nil, true, cfg.DataDbCfg().Items) cfg.DataDbCfg().Items[utils.MetaFilterIndexes].Remote = true cfg.DataDbCfg().RmtConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.ReplicatorSv1)} clientConn := make(chan rpcclient.ClientConnector, 1) clientConn <- &ccMock{ calls: map[string]func(args interface{}, reply interface{}) error{ utils.ReplicatorSv1MatchFilterIndex: func(args, reply interface{}) error { return nil }, }, } connMgr := NewConnManager(cfg, map[string]chan rpcclient.ClientConnector{ utils.ConcatenatedKey(utils.MetaInternal, utils.ReplicatorSv1): clientConn, }) dm := NewDataManager(db, cfg.CacheCfg(), connMgr) fltr := &Filter{ Tenant: "cgrates.org", ID: "RES_FLT_1", Rules: []*FilterRule{ { Type: utils.MetaString, Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Account, Values: []string{"1002"}, }, }, } if err := dm.SetFilter(fltr); err != nil { t.Error(err) } rp := &ResourceProfile{ Tenant: "cgrates.org", ID: "RES1", FilterIDs: []string{"RES_FLT_1"}, UsageTTL: time.Second, Limit: 1, Weight: 10, ThresholdIDs: []string{"TH1"}, } if err := dm.SetResourceProfile(rp, true); err != nil { t.Error(err) } config.SetCgrConfig(cfg) if err := dm.MatchFilterIndexFromKey(utils.CacheResourceFilterIndexes, "cgrates.org:*string:Account:1002"); err == nil { t.Error(err) } //unifinished } func TestCacheDataFromDB(t *testing.T) { cfg, _ := config.NewDefaultCGRConfig() db := NewInternalDB(nil, nil, true, cfg.DataDbCfg().Items) dm := NewDataManager(db, cfg.CacheCfg(), nil) chgS := ChargerProfiles{ &ChargerProfile{ Tenant: "cgrates.org", ID: "Charger1", FilterIDs: []string{"*string:~*req.Account:1015", "*gt:~*req.Usage:10"}, ActivationInterval: &utils.ActivationInterval{ ActivationTime: time.Date(2014, 7, 29, 15, 0, 0, 0, time.UTC), }, RunID: utils.MetaDefault, AttributeIDs: []string{"*none"}, Weight: 20, }, &ChargerProfile{ Tenant: "cgrates.com", ID: "CHRG_1", FilterIDs: []string{"*string:Account:1001"}, ActivationInterval: &utils.ActivationInterval{ ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), ExpiryTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), }, AttributeIDs: []string{"ATTR_1"}, Weight: 20, }, } for _, chg := range chgS { if err := dm.SetChargerProfile(chg, true); err != nil { t.Error(err) } } if err := dm.CacheDataFromDB(utils.ChargerProfilePrefix, nil, false); err != nil { t.Error(err) } } func TestCacheDataFromDBFilterIndexes(t *testing.T) { cfg, _ := config.NewDefaultCGRConfig() db := NewInternalDB(nil, nil, true, cfg.DataDbCfg().Items) dm := NewDataManager(db, cfg.CacheCfg(), nil) fltr := &Filter{ Tenant: "cgrates.org", ID: "FLTR_ATTR_1", Rules: []*FilterRule{ { Type: utils.MetaString, Element: "~*req.Attribute", Values: []string{"AttributeProfile1"}, }, { Type: utils.MetaGreaterOrEqual, Element: "~*req.UsageInterval", Values: []string{(1 * time.Second).String()}, }, { Type: utils.MetaGreaterOrEqual, Element: "~*req." + utils.Weight, Values: []string{"9.0"}, }, }, } dm.SetFilter(fltr) attr := &AttributeProfile{ Tenant: "cgrates.org", ID: "ATTR_1001_SIMPLEAUTH", FilterIDs: []string{"FLTR_ATTR_1"}, Contexts: []string{"simpleauth"}, Attributes: []*Attribute{ { FilterIDs: []string{}, Path: utils.MetaReq + utils.NestingSep + "Password", Type: utils.META_CONSTANT, Value: config.NewRSRParsersMustCompile("CGRateS.org", true, utils.INFIELD_SEP), }, }, Weight: 20.0, } dm.SetAttributeProfile(attr, true) if err := dm.CacheDataFromDB(utils.AttributeFilterIndexes, nil, false); err != nil { t.Error(err) } fltr2 := &Filter{ Tenant: "cgrates.org", ID: "RS_FLT", Rules: []*FilterRule{ { Type: utils.MetaString, Element: "~*req.Destination", Values: []string{"1002", "1003"}, }, }, } dm.SetFilter(fltr2) rsc := &ResourceProfile{ Tenant: "cgrates.org", ID: "RES1", FilterIDs: []string{"RS_FLT"}, ThresholdIDs: []string{utils.META_NONE}, AllocationMessage: "Approved", Weight: 10, Limit: 10, UsageTTL: time.Minute, Stored: true, } dm.SetResourceProfile(rsc, true) if err := dm.CacheDataFromDB(utils.ResourceFilterIndexes, nil, false); err != nil { t.Error(err) } statFlt := &Filter{ Tenant: "cgrates.org", ID: "FLTR_STATS_1", Rules: []*FilterRule{ { Type: utils.MetaString, Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Account, Values: []string{"1001"}, }, { Type: utils.MetaGreaterOrEqual, Element: "~*req.UsageInterval", Values: []string{(1 * time.Second).String()}, }, { Type: utils.MetaGreaterOrEqual, Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Usage, Values: []string{(1 * time.Second).String()}, }, { Type: utils.MetaGreaterOrEqual, Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Weight, Values: []string{"9.0"}, }, }, } dm.SetFilter(statFlt) sqP := &StatQueueProfile{ Tenant: "cgrates.org", ID: "DistinctMetricProfile", QueueLength: 10, FilterIDs: []string{"FLTR_STATS_1"}, TTL: time.Duration(10) * time.Second, Metrics: []*MetricWithFilters{ { MetricID: utils.MetaDDC, }, }, ThresholdIDs: []string{utils.META_NONE}, Stored: true, Weight: 20, } dm.SetStatQueueProfile(sqP, true) if err := dm.CacheDataFromDB(utils.StatFilterIndexes, nil, false); err != nil { t.Error(err) } thFltr := &Filter{ Tenant: "cgrates.org", ID: "FLTR_TH_2", Rules: []*FilterRule{ { Type: utils.MetaString, Element: "~*req.Threshold", Values: []string{"TH_2"}, }, { Type: utils.MetaPrefix, Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Destination, Values: []string{"100"}, }, }, } dm.SetFilter(thFltr) thP := &ThresholdProfile{ Tenant: "cgrates.org", ID: "THD_AccDisableAndLog", FilterIDs: []string{"FLTR_TH_2"}, MaxHits: -1, MinSleep: time.Duration(1 * time.Second), Weight: 30.0, ActionIDs: []string{"DISABLE_LOG"}, } dm.SetThresholdProfile(thP, true) if err := dm.CacheDataFromDB(utils.ThresholdFilterIndexes, nil, false); err != nil { t.Error(err) } suppFltr := &Filter{ Tenant: "cgrates.org", ID: "FLTR_SUPP_1", Rules: []*FilterRule{ { Type: utils.MetaString, Element: "~*req.Supplier", Values: []string{"SupplierProfile2"}, }}, } dm.SetFilter(suppFltr) supp := &SupplierProfile{ Tenant: "cgrates.org", ID: "SPP_1", FilterIDs: []string{"FLTR_SUPP_1"}, Sorting: utils.MetaLC, SortingParameters: []string{}, Suppliers: []*Supplier{ { ID: "supplier1", RatingPlanIDs: []string{"RPL_2"}, ResourceIDs: []string{"ResGroup2", "ResGroup4"}, StatIDs: []string{"Stat3"}, Weight: 10, Blocker: false, SupplierParameters: utils.EmptyString, }, }, Weight: 20, } dm.SetSupplierProfile(supp, true) if err := dm.CacheDataFromDB(utils.SupplierFilterIndexes, nil, false); err != nil { t.Error(err) } ddpFlt := &Filter{ Tenant: "cgrates.org", ID: "DSP_FLT", Rules: []*FilterRule{ { Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Account, Type: utils.MetaString, Values: []string{"2009"}, }, }, } dm.SetFilter(ddpFlt) dpp := &DispatcherProfile{ Tenant: "cgrates.org", ID: "DSP_Test1", FilterIDs: []string{"DSP_FLT"}, Strategy: utils.MetaFirst, Subsystems: []string{utils.MetaAttributes, utils.MetaSessionS}, Weight: 20, } if err := dm.SetDispatcherProfile(dpp, true); err != nil { t.Error(err) } if err := dm.CacheDataFromDB(utils.DispatcherFilterIndexes, nil, false); err != nil { t.Error(err) } chgFlt := &Filter{ Tenant: "cgrates.org", ID: "FLT_CPP", Rules: []*FilterRule{ { Type: utils.MetaString, Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + "Charger", Values: []string{"Charger1"}, }, }, } dm.SetFilter(chgFlt) cpp := &ChargerProfile{ Tenant: "cgrates.org", ID: "Default", FilterIDs: []string{"*string:~*req.Destination:+1442", "*prefix:~*opts.Accounts:1002;1004"}, RunID: utils.MetaDefault, AttributeIDs: []string{"*none"}, Weight: 20, } if err := dm.SetChargerProfile(cpp, true); err != nil { t.Error(err) } if err := dm.CacheDataFromDB(utils.ChargerFilterIndexes, nil, false); err != nil { t.Error(err) } } func TestFilterIndexesRmtRpl(t *testing.T) { cfg, _ := config.NewDefaultCGRConfig() Cache.Clear(nil) cfg.DataDbCfg().Items[utils.MetaFilterIndexes].Remote = true cfg.DataDbCfg().Items[utils.MetaFilterIndexes].Replicate = true cfg.DataDbCfg().RplConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.ReplicatorSv1)} cfg.DataDbCfg().RmtConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.ReplicatorSv1)} defer func() { cfg2, _ := config.NewDefaultCGRConfig() config.SetCgrConfig(cfg2) }() db := NewInternalDB(nil, nil, true, cfg.DataDbCfg().Items) expIndx := map[string]utils.StringMap{ "*string:Account:1001": { "RL1": true, }, "*string:Account:1002": { "RL1": true, "RL2": true, }, } clientConn := make(chan rpcclient.ClientConnector, 1) clientConn <- clMock(func(m string, a, r interface{}) error { if m == utils.ReplicatorSv1SetFilterIndexes { setFltrIndxArg, concat := a.(*utils.SetFilterIndexesArg) if !concat { return errors.New("Can't convert interfacea") } if err := dm.DataDB().SetFilterIndexesDrv(setFltrIndxArg.CacheID, setFltrIndxArg.ItemIDPrefix, setFltrIndxArg.Indexes, false, utils.EmptyString); err == nil { *r.(*string) = utils.OK } return nil } else if m == utils.ReplicatorSv1GetFilterIndexes { rpl := expIndx *r.(*map[string]utils.StringMap) = rpl return nil } return utils.ErrNotImplemented }) connMgr := NewConnManager(cfg, map[string]chan rpcclient.ClientConnector{ utils.ConcatenatedKey(utils.MetaInternal, utils.ReplicatorSv1): clientConn, }) dm := NewDataManager(db, cfg.CacheCfg(), connMgr) idx := map[string]utils.StringMap{ "*string:Account:1001": { "DSP1": true, "DSP2": true, }, "*suffix:*opts.Destination:+100": { "Dsp1": true, "Dsp2": true, }, } config.SetCgrConfig(cfg) if err := dm.SetFilterIndexes(utils.CacheDispatcherProfiles, "cgrates.org", idx, false, utils.NonTransactional); err != nil { t.Error(err) } if err := dm.RemoveFilterIndexes(utils.CacheDispatcherProfiles, "cgrates.org"); err != nil { t.Error(err) } if rcvIdx, err := dm.GetFilterIndexes(utils.CacheResourceProfiles, "cgrates.org", utils.EmptyString, nil); err != nil { t.Error(err) } else if !reflect.DeepEqual(expIndx, rcvIdx) { t.Errorf("Expected %+v,Received %+v", utils.ToJSON(expIndx), utils.ToJSON(idx)) } } func TestStatQueueProfileIndx(t *testing.T) { cfg, _ := config.NewDefaultCGRConfig() db := NewInternalDB(nil, nil, true, cfg.DataDbCfg().Items) dm := NewDataManager(db, cfg.CacheCfg(), nil) fltrs := []*Filter{ { Tenant: "cgrates.org", ID: "SQ_FLT_1", Rules: []*FilterRule{ { Type: utils.MetaString, Element: "~*req.Destination", Values: []string{"1002", "1003", "1004"}, }, }, }, { Tenant: "cgrates.org", ID: "SQ_FLT_2", Rules: []*FilterRule{ { Type: utils.MetaGreaterOrEqual, Element: "~*req.UsageInterval", Values: []string{(1 * time.Second).String()}, }, { Type: utils.MetaGreaterOrEqual, Element: "~*req." + utils.Weight, Values: []string{"9.0"}, }, }, }} for _, flt := range fltrs { if err := dm.SetFilter(flt); err != nil { t.Error(err) } } sqP := &StatQueueProfile{ Tenant: "cgrates.org", ID: "SQ_1", FilterIDs: []string{"SQ_FLT_1"}, QueueLength: 10, TTL: time.Duration(0) * time.Second, Metrics: []*MetricWithFilters{ { MetricID: "*asr", }, { MetricID: utils.MetaACD, }, { MetricID: "*acc", }, }, ThresholdIDs: []string{"Test"}, Blocker: false, Stored: true, Weight: float64(0), MinItems: 0, } if err := dm.SetStatQueueProfile(sqP, true); err != nil { t.Error(err) } sqP = &StatQueueProfile{ Tenant: "cgrates.org", ID: "SQ_1", FilterIDs: []string{"SQ_FLT_2"}, QueueLength: 10, TTL: time.Duration(0) * time.Second, Metrics: []*MetricWithFilters{ { MetricID: "*asr", }, { MetricID: utils.MetaACD, }, { MetricID: "*acc", }, }, ThresholdIDs: []string{"Test"}, Blocker: false, Stored: true, Weight: float64(0), MinItems: 0, } if err := dm.SetStatQueueProfile(sqP, true); err != nil { t.Error(err) } if err := dm.RemoveStatQueueProfile("cgrates.org", "SQ_1", utils.NonTransactional, true); err != nil { t.Error(err) } } func TestDmRatingProfileCategory(t *testing.T) { cfg, _ := config.NewDefaultCGRConfig() rdsITdb, err := NewRedisStorage( "127.0.0.1:6379", 10, "", "msgpack", 4, "") if err != nil { t.Fatal("Could not connect to Redis", err.Error()) } dm := NewDataManager(rdsITdb, cfg.CacheCfg(), nil) rprfs := []*RatingProfile{ { Id: utils.ConcatenatedKey(utils.META_OUT, "cgrates.org", "*any", "1002"), RatingPlanActivations: RatingPlanActivations{}, }, { Id: utils.ConcatenatedKey(utils.META_OUT, "cgrates.org", "call", "1002"), RatingPlanActivations: RatingPlanActivations{}, }, { Id: utils.ConcatenatedKey(utils.META_OUT, "cgrates.org", "*any", "1001"), RatingPlanActivations: RatingPlanActivations{}, }, { Id: utils.ConcatenatedKey(utils.META_OUT, "cgrates.org", "sms", "1001"), RatingPlanActivations: RatingPlanActivations{}, }, } for _, rprf := range rprfs { if err := dm.SetRatingProfile(rprf, utils.NonTransactional); err != nil { t.Error(err) } } if err := dm.RemoveRatingProfile(rprfs[2].Id, utils.NonTransactional); err != nil { t.Error(err) } if _, err := dm.GetRatingProfile(rprfs[1].Id, true, utils.NonTransactional); err != nil { t.Error(err) } if _, err := dm.GetRatingProfile(rprfs[0].Id, true, utils.NonTransactional); err != nil { t.Error(err) } if _, err := dm.GetRatingProfile(rprfs[3].Id, true, utils.NonTransactional); err != nil { t.Error(err) } } func TestDmDispatcherHost(t *testing.T) { cfg, _ := config.NewDefaultCGRConfig() db := NewInternalDB(nil, nil, true, cfg.DataDbCfg().Items) dm := NewDataManager(db, cfg.CacheCfg(), nil) dppH := &DispatcherHost{ Tenant: "cgrates.org", ID: "ALL1", Conns: []*config.RemoteHost{ { Address: "127.0.0.1:2012", Transport: utils.MetaJSON, TLS: true, }, { Address: "127.0.0.1:3012", Transport: utils.MetaJSON, }, }, } if err := dm.SetDispatcherHost(dppH); err != nil { t.Error(err) } if _, err := dm.GetDispatcherHost("cgrates.org", "ALL1", false, true, utils.NonTransactional); err != nil { t.Error(err) } if err := dm.RemoveDispatcherHost("cgrates.org", "ALL1", utils.NonTransactional); err != nil { t.Error(err) } } func TestDmRemoveThresholdProfile(t *testing.T) { cfg, _ := config.NewDefaultCGRConfig() db := NewInternalDB(nil, nil, true, cfg.DataDbCfg().Items) dm := NewDataManager(db, cfg.CacheCfg(), nil) thP := &ThresholdProfile{ Tenant: "cgrates.org", ID: "THD_ACNT_1001", FilterIDs: []string{"*prefix:Destination:46"}, ActivationInterval: &utils.ActivationInterval{ ActivationTime: time.Date(2014, 7, 29, 15, 0, 0, 0, time.UTC), }, MaxHits: -1, MinSleep: time.Duration(0), Blocker: false, Weight: 10.0, ActionIDs: []string{"TOPUP_MONETARY_10"}, Async: false, } dm.SetThresholdProfile(thP, true) if err := dm.RemoveThresholdProfile("cgrates.org", "THD_ACNT_1001", utils.NonTransactional, true); err != nil { t.Error(err) } }