/* Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments Copyright (C) ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see */ package engine import ( "reflect" "sort" "testing" "time" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" "github.com/cgrates/ltcache" ) func TestHealthAccountAction(t *testing.T) { Cache.Clear(nil) cfg := config.NewDefaultCGRConfig() db, dErr := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) if dErr != nil { t.Error(dErr) } dm := NewDataManager(db, cfg.CacheCfg(), nil) if err := dm.SetAccountActionPlans("1001", []string{"AP1", "AP2"}, true); err != nil { t.Fatal(err) } if err := dm.SetActionPlan("AP2", &ActionPlan{ Id: "AP2", AccountIDs: utils.NewStringMap("1002"), ActionTimings: []*ActionTiming{{}}, }, true, utils.NonTransactional); err != nil { t.Fatal(err) } exp := &AccountActionPlanIHReply{ MissingAccountActionPlans: map[string][]string{"1002": {"AP2"}}, BrokenReferences: map[string][]string{"AP2": {"1001"}, "AP1": nil}, } if rply, err := GetAccountActionPlansIndexHealth(dm, -1, -1, -1, -1, false, false); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(exp, rply) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } } func TestHealthAccountAction2(t *testing.T) { Cache.Clear(nil) cfg := config.NewDefaultCGRConfig() db, dErr := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) if dErr != nil { t.Error(dErr) } dm := NewDataManager(db, cfg.CacheCfg(), nil) if err := dm.SetAccountActionPlans("1001", []string{"AP1", "AP2"}, true); err != nil { t.Fatal(err) } if err := dm.SetActionPlan("AP2", &ActionPlan{ Id: "AP2", AccountIDs: utils.NewStringMap("1001"), ActionTimings: []*ActionTiming{{}}, }, true, utils.NonTransactional); err != nil { t.Fatal(err) } exp := &AccountActionPlanIHReply{ MissingAccountActionPlans: map[string][]string{}, BrokenReferences: map[string][]string{"AP1": nil}, } if rply, err := GetAccountActionPlansIndexHealth(dm, -1, -1, -1, -1, false, false); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(exp, rply) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } } func TestHealthAccountAction3(t *testing.T) { Cache.Clear(nil) cfg := config.NewDefaultCGRConfig() db, dErr := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) if dErr != nil { t.Error(dErr) } dm := NewDataManager(db, cfg.CacheCfg(), nil) if err := dm.SetAccountActionPlans("1002", []string{"AP1"}, true); err != nil { t.Fatal(err) } if err := dm.SetActionPlan("AP1", &ActionPlan{ Id: "AP1", AccountIDs: utils.NewStringMap("1002"), ActionTimings: []*ActionTiming{{}}, }, true, utils.NonTransactional); err != nil { t.Fatal(err) } if err := dm.SetActionPlan("AP2", &ActionPlan{ Id: "AP2", AccountIDs: utils.NewStringMap("1002"), ActionTimings: []*ActionTiming{{}}, }, true, utils.NonTransactional); err != nil { t.Fatal(err) } exp := &AccountActionPlanIHReply{ MissingAccountActionPlans: map[string][]string{"1002": {"AP2"}}, BrokenReferences: map[string][]string{}, } if rply, err := GetAccountActionPlansIndexHealth(dm, -1, -1, -1, -1, false, false); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(exp, rply) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } } func TestHealthAccountAction4(t *testing.T) { Cache.Clear(nil) cfg := config.NewDefaultCGRConfig() db, dErr := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) if dErr != nil { t.Error(dErr) } dm := NewDataManager(db, cfg.CacheCfg(), nil) if err := dm.SetAccountActionPlans("1002", []string{"AP2", "AP1"}, true); err != nil { t.Fatal(err) } if err := dm.SetAccountActionPlans("1001", []string{"AP2"}, true); err != nil { t.Fatal(err) } if err := dm.SetActionPlan("AP1", &ActionPlan{ Id: "AP1", AccountIDs: utils.NewStringMap("1002"), ActionTimings: []*ActionTiming{{}}, }, true, utils.NonTransactional); err != nil { t.Fatal(err) } if err := dm.SetActionPlan("AP2", &ActionPlan{ Id: "AP2", AccountIDs: utils.NewStringMap("1001"), ActionTimings: []*ActionTiming{{}}, }, true, utils.NonTransactional); err != nil { t.Fatal(err) } exp := &AccountActionPlanIHReply{ MissingAccountActionPlans: map[string][]string{}, BrokenReferences: map[string][]string{"AP2": {"1002"}}, } if rply, err := GetAccountActionPlansIndexHealth(dm, -1, -1, -1, -1, false, false); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(exp, rply) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } } func TestHealthReverseDestination(t *testing.T) { Cache.Clear(nil) cfg := config.NewDefaultCGRConfig() db, dErr := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) if dErr != nil { t.Error(dErr) } dm := NewDataManager(db, cfg.CacheCfg(), nil) if err := dm.SetReverseDestination("DST1", []string{"1001", "1002"}, utils.NonTransactional); err != nil { t.Fatal(err) } if err := dm.SetReverseDestination("DST2", []string{"1001"}, utils.NonTransactional); err != nil { t.Fatal(err) } if err := dm.SetDestination(&Destination{ Id: "DST2", Prefixes: []string{"1002"}, }, utils.NonTransactional); err != nil { t.Fatal(err) } exp := &ReverseDestinationsIHReply{ MissingReverseDestinations: map[string][]string{"1002": {"DST2"}}, BrokenReferences: map[string][]string{"DST1": nil, "DST2": {"1001"}}, } if rply, err := GetReverseDestinationsIndexHealth(dm, -1, -1, -1, -1, false, false); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(exp, rply) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } } func TestHealthReverseDestination2(t *testing.T) { Cache.Clear(nil) cfg := config.NewDefaultCGRConfig() db, dErr := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) if dErr != nil { t.Error(dErr) } dm := NewDataManager(db, cfg.CacheCfg(), nil) if err := dm.SetReverseDestination("DST1", []string{"1001"}, utils.NonTransactional); err != nil { t.Fatal(err) } if err := dm.SetReverseDestination("DST2", []string{"1001"}, utils.NonTransactional); err != nil { t.Fatal(err) } if err := dm.SetDestination(&Destination{ Id: "DST2", Prefixes: []string{"1001"}, }, utils.NonTransactional); err != nil { t.Fatal(err) } exp := &ReverseDestinationsIHReply{ MissingReverseDestinations: map[string][]string{}, BrokenReferences: map[string][]string{"DST1": nil}, } if rply, err := GetReverseDestinationsIndexHealth(dm, -1, -1, -1, -1, false, false); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(exp, rply) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } } func TestHealthReverseDestination3(t *testing.T) { Cache.Clear(nil) cfg := config.NewDefaultCGRConfig() db, dErr := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) if dErr != nil { t.Error(dErr) } dm := NewDataManager(db, cfg.CacheCfg(), nil) if err := dm.SetReverseDestination("DST1", []string{"1002"}, utils.NonTransactional); err != nil { t.Fatal(err) } if err := dm.SetDestination(&Destination{ Id: "DST1", Prefixes: []string{"1002"}, }, utils.NonTransactional); err != nil { t.Fatal(err) } if err := dm.SetDestination(&Destination{ Id: "DST2", Prefixes: []string{"1002"}, }, utils.NonTransactional); err != nil { t.Fatal(err) } exp := &ReverseDestinationsIHReply{ MissingReverseDestinations: map[string][]string{"1002": {"DST2"}}, BrokenReferences: map[string][]string{}, } if rply, err := GetReverseDestinationsIndexHealth(dm, -1, -1, -1, -1, false, false); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(exp, rply) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } } func TestHealthReverseDestination4(t *testing.T) { Cache.Clear(nil) cfg := config.NewDefaultCGRConfig() db, dErr := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) if dErr != nil { t.Error(dErr) } dm := NewDataManager(db, cfg.CacheCfg(), nil) if err := dm.SetReverseDestination("DST1", []string{"1002"}, utils.NonTransactional); err != nil { t.Fatal(err) } if err := dm.SetReverseDestination("DST2", []string{"1001", "1002"}, utils.NonTransactional); err != nil { t.Fatal(err) } if err := dm.SetDestination(&Destination{ Id: "DST1", Prefixes: []string{"1002"}, }, utils.NonTransactional); err != nil { t.Fatal(err) } if err := dm.SetDestination(&Destination{ Id: "DST2", Prefixes: []string{"1001"}, }, utils.NonTransactional); err != nil { t.Fatal(err) } exp := &ReverseDestinationsIHReply{ MissingReverseDestinations: map[string][]string{}, BrokenReferences: map[string][]string{"DST2": {"1002"}}, } if rply, err := GetReverseDestinationsIndexHealth(dm, -1, -1, -1, -1, false, false); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(exp, rply) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } } func TestHealthFilterAttributes(t *testing.T) { Cache.Clear(nil) cfg := config.NewDefaultCGRConfig() db, dErr := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) if dErr != nil { t.Error(dErr) } dm := NewDataManager(db, cfg.CacheCfg(), nil) if err := dm.SetAttributeProfile(&AttributeProfile{ Tenant: "cgrates.org", ID: "ATTR1", Contexts: []string{utils.MetaAny}, FilterIDs: []string{"*string:~*req.Account:1001", "Fltr1"}, }, false); err != nil { t.Fatal(err) } if err := dm.SetIndexes(utils.CacheAttributeFilterIndexes, "cgrates.org:*any", map[string]utils.StringSet{"*string:*req.Account:1002": { "ATTR1": {}, "ATTR2": {}, }}, true, utils.NonTransactional); err != nil { t.Fatal(err) } exp := &FilterIHReply{ MissingIndexes: map[string][]string{ "cgrates.org:*any:*string:*req.Account:1001": {"ATTR1"}, }, BrokenIndexes: map[string][]string{ "cgrates.org:*string:*req.Account:1002": {"ATTR1"}, }, MissingFilters: map[string][]string{ "cgrates.org:Fltr1": {"ATTR1"}, }, MissingObjects: []string{"cgrates.org:ATTR2"}, } if rply, err := GetFltrIdxHealth(dm, ltcache.NewCache(-1, 0, false, true, nil), ltcache.NewCache(-1, 0, false, true, nil), ltcache.NewCache(-1, 0, false, true, nil), utils.CacheAttributeFilterIndexes); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(exp, rply) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } } func TestHealthReverseFilter(t *testing.T) { Cache.Clear(nil) cfg := config.NewDefaultCGRConfig() db, dErr := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) if dErr != nil { t.Error(dErr) } dm := NewDataManager(db, cfg.CacheCfg(), nil) if err := dm.SetAttributeProfile(&AttributeProfile{ Tenant: "cgrates.org", ID: "ATTR1", Contexts: []string{utils.MetaAny}, FilterIDs: []string{"*string:~*req.Account:1001", "Fltr1"}, }, false); err != nil { t.Fatal(err) } if err := dm.SetIndexes(utils.CacheReverseFilterIndexes, "cgrates.org:Fltr2", map[string]utils.StringSet{utils.CacheAttributeFilterIndexes: {"ATTR1:*cdrs": {}, "ATTR2:*any": {}}}, true, utils.NonTransactional); err != nil { t.Fatal(err) } if err := dm.SetIndexes(utils.CacheReverseFilterIndexes, "cgrates.org:Fltr1", map[string]utils.StringSet{utils.CacheAttributeFilterIndexes: {"ATTR1:*cdrs": {}}}, true, utils.NonTransactional); err != nil { t.Fatal(err) } exp := map[string]*ReverseFilterIHReply{ utils.CacheAttributeFilterIndexes: { MissingReverseIndexes: map[string][]string{ // "cgrates.org:ATTR1": {"Fltr1:*any"}, ?? }, MissingFilters: map[string][]string{"cgrates.org:Fltr1": {"ATTR1"}}, BrokenReverseIndexes: map[string][]string{ "cgrates.org:ATTR1:*cdrs": {"Fltr1", "Fltr2"}, }, MissingObjects: []string{"cgrates.org:ATTR2"}, }, } objCaches := make(map[string]*ltcache.Cache) for indxType := range utils.CacheIndexesToPrefix { objCaches[indxType] = ltcache.NewCache(-1, 0, false, true, nil) } if rply, err := GetRevFltrIdxHealth(dm, ltcache.NewCache(-1, 0, false, true, nil), ltcache.NewCache(-1, 0, false, true, nil), objCaches); err != nil { t.Fatal(err) } else { sort.Strings(rply[utils.CacheAttributeFilterIndexes].BrokenReverseIndexes["cgrates.org:ATTR1:*cdrs"]) if !reflect.DeepEqual(exp, rply) { t.Errorf("Expecting: %+v,\n received: %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } } } func TestHealthIndexThreshold(t *testing.T) { Cache.Clear(nil) cfg := config.NewDefaultCGRConfig() db, dErr := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) if dErr != nil { t.Error(dErr) } dm := NewDataManager(db, cfg.CacheCfg(), nil) // we will set this threshold but without indexing thPrf := &ThresholdProfileWithAPIOpts{ ThresholdProfile: &ThresholdProfile{ Tenant: "cgrates.org", ID: "TestHealthIndexThreshold", FilterIDs: []string{"*string:~*opts.*eventType:AccountUpdate", "*string:~*asm.ID:1002", // *asm will not be indexing "*suffix:BrokenFilter:Invalid"}, // static value, won't index MaxHits: 1, }, } if err := dm.SetThresholdProfile(thPrf.ThresholdProfile, false); err != nil { t.Error(err) } args := &IndexHealthArgsWith3Ch{} exp := &FilterIHReply{ MissingIndexes: map[string][]string{ "cgrates.org:*string:*opts.*eventType:AccountUpdate": {"TestHealthIndexThreshold"}, }, BrokenIndexes: map[string][]string{}, MissingFilters: map[string][]string{}, MissingObjects: []string{}, } if rply, err := GetFltrIdxHealth(dm, ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, false, nil), ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, false, nil), ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, false, nil), utils.CacheThresholdFilterIndexes); err != nil { t.Error(err) } else if !reflect.DeepEqual(exp, rply) { t.Errorf("Expected %+v, received %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } indexes := map[string]utils.StringSet{ "*prefix:req.InvalidIdx:10": { // obj exist but the index don't "TestHealthIndexThreshold": {}, }, "*string:*req.Destination:123": { // index is valid but the obj does not exist "InexistingThreshold": {}, }, } // we will set manually some indexes that points to an nil object or index is valid but the obj is missing if err := dm.SetIndexes(utils.CacheThresholdFilterIndexes, "cgrates.org", indexes, true, utils.NonTransactional); err != nil { t.Error(err) } exp = &FilterIHReply{ MissingObjects: []string{"cgrates.org:InexistingThreshold"}, MissingIndexes: map[string][]string{ "cgrates.org:*string:*opts.*eventType:AccountUpdate": {"TestHealthIndexThreshold"}, }, BrokenIndexes: map[string][]string{ "cgrates.org:*prefix:req.InvalidIdx:10": {"TestHealthIndexThreshold"}, }, MissingFilters: map[string][]string{}, } if rply, err := GetFltrIdxHealth(dm, ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, false, nil), ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, false, nil), ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, false, nil), utils.CacheThresholdFilterIndexes); err != nil { t.Error(err) } else if !reflect.DeepEqual(exp, rply) { t.Errorf("Expected %+v, received %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } //we will use an inexisting Filter(not inline) for the same ThresholdProfile thPrf = &ThresholdProfileWithAPIOpts{ ThresholdProfile: &ThresholdProfile{ Tenant: "cgrates.org", ID: "TestHealthIndexThreshold", FilterIDs: []string{"*string:~*opts.*eventType:AccountUpdate", "*string:~*asm.ID:1002", "FLTR_1_DOES_NOT_EXIST"}, MaxHits: 1, }, } if err := dm.SetThresholdProfile(thPrf.ThresholdProfile, false); err != nil { t.Error(err) } exp = &FilterIHReply{ MissingObjects: []string{"cgrates.org:InexistingThreshold"}, MissingIndexes: map[string][]string{ "cgrates.org:*string:*opts.*eventType:AccountUpdate": {"TestHealthIndexThreshold"}, }, BrokenIndexes: map[string][]string{ "cgrates.org:*prefix:req.InvalidIdx:10": {"TestHealthIndexThreshold"}, }, MissingFilters: map[string][]string{ "cgrates.org:FLTR_1_DOES_NOT_EXIST": {"TestHealthIndexThreshold"}, }, } if rply, err := GetFltrIdxHealth(dm, ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, false, nil), ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, false, nil), ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, false, nil), utils.CacheThresholdFilterIndexes); err != nil { t.Error(err) } else if !reflect.DeepEqual(exp, rply) { t.Errorf("Expected %+v, received %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } } func TestHealthIndexCharger(t *testing.T) { Cache.Clear(nil) cfg := config.NewDefaultCGRConfig() db, dErr := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) if dErr != nil { t.Error(dErr) } dm := NewDataManager(db, cfg.CacheCfg(), nil) // we will set this charger but without indexing chPrf := &ChargerProfile{ Tenant: "cgrates.org", ID: "Raw", FilterIDs: []string{ "*string:~*opts.*eventType:ChargerAccountUpdate", "*string:~*req.*Account:1234", "*string:~*asm.ID:1002", // *asm will not be indexing "*suffix:BrokenFilter:Invalid"}, RunID: "raw", AttributeIDs: []string{"*constant:*req.RequestType:*none"}, Weight: 20, } if err := dm.SetChargerProfile(chPrf, false); err != nil { t.Error(err) } args := &IndexHealthArgsWith3Ch{} exp := &FilterIHReply{ MissingIndexes: map[string][]string{ "cgrates.org:*string:*opts.*eventType:ChargerAccountUpdate": {"Raw"}, "cgrates.org:*string:*req.*Account:1234": {"Raw"}, }, BrokenIndexes: map[string][]string{}, MissingFilters: map[string][]string{}, MissingObjects: []string{}, } if rply, err := GetFltrIdxHealth(dm, ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, false, nil), ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, false, nil), ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, false, nil), utils.CacheChargerFilterIndexes); err != nil { t.Error(err) } else if !reflect.DeepEqual(exp, rply) { t.Errorf("Expected %+v, received %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } indexes := map[string]utils.StringSet{ "*prefix:req.Destination:+10": { // obj exist but the index don't "Raw": {}, }, "*string:*req.Destination:123": { // index is valid but the obj does not exist "InexistingCharger": {}, }, } // we will set manually some indexes that points to an nil object or index is valid but the obj is missing if err := dm.SetIndexes(utils.CacheChargerFilterIndexes, "cgrates.org", indexes, true, utils.NonTransactional); err != nil { t.Error(err) } exp = &FilterIHReply{ MissingObjects: []string{"cgrates.org:InexistingCharger"}, MissingIndexes: map[string][]string{ "cgrates.org:*string:*opts.*eventType:ChargerAccountUpdate": {"Raw"}, "cgrates.org:*string:*req.*Account:1234": {"Raw"}, }, BrokenIndexes: map[string][]string{ "cgrates.org:*prefix:req.Destination:+10": {"Raw"}, }, MissingFilters: map[string][]string{}, } if rply, err := GetFltrIdxHealth(dm, ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, false, nil), ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, false, nil), ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, false, nil), utils.CacheChargerFilterIndexes); err != nil { t.Error(err) } else if !reflect.DeepEqual(exp, rply) { t.Errorf("Expected %+v, received %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } //we will use an inexisting Filter(not inline) for the same ChargerProfile chPrf = &ChargerProfile{ Tenant: "cgrates.org", ID: "Raw", FilterIDs: []string{ "*string:~*opts.*eventType:ChargerAccountUpdate", "*string:~*req.*Account:1234", "*string:~*asm.ID:1002", // *asm will not be indexing "*suffix:BrokenFilter:Invalid", "FLTR_1_DOES_NOT_EXIST_CHRGR"}, RunID: "raw", AttributeIDs: []string{"*constant:*req.RequestType:*none"}, Weight: 20, } if err := dm.SetChargerProfile(chPrf, false); err != nil { t.Error(err) } exp = &FilterIHReply{ MissingObjects: []string{"cgrates.org:InexistingCharger"}, MissingIndexes: map[string][]string{ "cgrates.org:*string:*opts.*eventType:ChargerAccountUpdate": {"Raw"}, "cgrates.org:*string:*req.*Account:1234": {"Raw"}, }, BrokenIndexes: map[string][]string{ "cgrates.org:*prefix:req.Destination:+10": {"Raw"}, }, MissingFilters: map[string][]string{ "cgrates.org:FLTR_1_DOES_NOT_EXIST_CHRGR": {"Raw"}, }, } if rply, err := GetFltrIdxHealth(dm, ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, false, nil), ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, false, nil), ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, false, nil), utils.CacheChargerFilterIndexes); err != nil { t.Error(err) } else if !reflect.DeepEqual(exp, rply) { t.Errorf("Expected %+v, received %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } } func TestHealthIndexResources(t *testing.T) { Cache.Clear(nil) cfg := config.NewDefaultCGRConfig() db, dErr := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) if dErr != nil { t.Error(dErr) } dm := NewDataManager(db, cfg.CacheCfg(), nil) // we will set this resource but without indexing rsPrf := &ResourceProfile{ Tenant: "tenant.custom", ID: "RES_GRP1", FilterIDs: []string{ "*string:~*opts.*eventType:ResourceAccountUpdate", "*string:~*req.RequestType:*rated", "*prefix:~*accounts.RES_GRP1.Available:10", // *accounts will not be indexing "*suffix:BrokenFilter:Invalid", }, UsageTTL: 10 * time.Microsecond, Limit: 10, AllocationMessage: "MessageAllocation", Blocker: true, Stored: true, Weight: 20, } if err := dm.SetResourceProfile(rsPrf, false); err != nil { t.Error(err) } args := &IndexHealthArgsWith3Ch{} exp := &FilterIHReply{ MissingIndexes: map[string][]string{ "tenant.custom:*string:*opts.*eventType:ResourceAccountUpdate": {"RES_GRP1"}, "tenant.custom:*string:*req.RequestType:*rated": {"RES_GRP1"}, }, BrokenIndexes: map[string][]string{}, MissingFilters: map[string][]string{}, MissingObjects: []string{}, } if rply, err := GetFltrIdxHealth(dm, ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, false, nil), ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, false, nil), ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, false, nil), utils.CacheResourceFilterIndexes); err != nil { t.Error(err) } else if !reflect.DeepEqual(exp, rply) { t.Errorf("Expected %+v, received %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } indexes := map[string]utils.StringSet{ "*suffix:*req.Destination:+10": { // obj exist but the index don't "RES_GRP1": {}, }, "*string:*req.CGRID:not_an_id": { // index is valid but the obj does not exist "InexistingResource": {}, }, } // we will set manually some indexes that points to an nil object or index is valid but the obj is missing if err := dm.SetIndexes(utils.CacheResourceFilterIndexes, "tenant.custom", indexes, true, utils.NonTransactional); err != nil { t.Error(err) } exp = &FilterIHReply{ MissingObjects: []string{"tenant.custom:InexistingResource"}, MissingIndexes: map[string][]string{ "tenant.custom:*string:*opts.*eventType:ResourceAccountUpdate": {"RES_GRP1"}, "tenant.custom:*string:*req.RequestType:*rated": {"RES_GRP1"}, }, BrokenIndexes: map[string][]string{ "tenant.custom:*suffix:*req.Destination:+10": {"RES_GRP1"}, }, MissingFilters: map[string][]string{}, } if rply, err := GetFltrIdxHealth(dm, ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, false, nil), ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, false, nil), ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, false, nil), utils.CacheResourceFilterIndexes); err != nil { t.Error(err) } else if !reflect.DeepEqual(exp, rply) { t.Errorf("Expected %+v, received %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } //we will use an inexisting Filter(not inline) for the same ResourceProfile rsPrf = &ResourceProfile{ Tenant: "tenant.custom", ID: "RES_GRP1", FilterIDs: []string{ "*string:~*opts.*eventType:ResourceAccountUpdate", "*string:~*req.RequestType:*rated", "*prefix:~*accounts.RES_GRP1.Available:10", // *asm will not be indexing "*suffix:BrokenFilter:Invalid", "FLTR_1_NOT_EXIST", }, UsageTTL: 10 * time.Microsecond, Limit: 10, AllocationMessage: "MessageAllocation", Blocker: true, Stored: true, Weight: 20, } if err := dm.SetResourceProfile(rsPrf, false); err != nil { t.Error(err) } exp = &FilterIHReply{ MissingObjects: []string{"tenant.custom:InexistingResource"}, MissingIndexes: map[string][]string{ "tenant.custom:*string:*opts.*eventType:ResourceAccountUpdate": {"RES_GRP1"}, "tenant.custom:*string:*req.RequestType:*rated": {"RES_GRP1"}, }, BrokenIndexes: map[string][]string{ "tenant.custom:*suffix:*req.Destination:+10": {"RES_GRP1"}, }, MissingFilters: map[string][]string{ "tenant.custom:FLTR_1_NOT_EXIST": {"RES_GRP1"}, }, } if rply, err := GetFltrIdxHealth(dm, ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, false, nil), ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, false, nil), ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, false, nil), utils.CacheResourceFilterIndexes); err != nil { t.Error(err) } else if !reflect.DeepEqual(exp, rply) { t.Errorf("Expected %+v, received %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } } func TestHealthIndexStats(t *testing.T) { Cache.Clear(nil) cfg := config.NewDefaultCGRConfig() db, dErr := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) if dErr != nil { t.Error(dErr) } dm := NewDataManager(db, cfg.CacheCfg(), nil) // we will set this statQueue but without indexing sqPrf := &StatQueueProfile{ Tenant: "cgrates.org", ID: "Stat_1", FilterIDs: []string{ "*string:~*opts.*apikey:sts1234", "*string:~*req.RequestType:*postpaid", "*prefix:~*resources.RES_GRP1.Available:10", // *resources will not be indexing "*suffix:BrokenFilter:Invalid", }, Weight: 30, QueueLength: 100, TTL: 10 * time.Second, MinItems: 0, Metrics: []*MetricWithFilters{ { MetricID: "*tcd", }, { MetricID: "*asr", }, { MetricID: "*acd", }, }, Blocker: true, ThresholdIDs: []string{utils.MetaNone}, } if err := dm.SetStatQueueProfile(sqPrf, false); err != nil { t.Error(err) } args := &IndexHealthArgsWith3Ch{} exp := &FilterIHReply{ MissingIndexes: map[string][]string{ "cgrates.org:*string:*opts.*apikey:sts1234": {"Stat_1"}, "cgrates.org:*string:*req.RequestType:*postpaid": {"Stat_1"}, }, BrokenIndexes: map[string][]string{}, MissingFilters: map[string][]string{}, MissingObjects: []string{}, } if rply, err := GetFltrIdxHealth(dm, ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, false, nil), ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, false, nil), ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, false, nil), utils.CacheStatFilterIndexes); err != nil { t.Error(err) } else if !reflect.DeepEqual(exp, rply) { t.Errorf("Expected %+v, received %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } indexes := map[string]utils.StringSet{ "*suffix:*req.Destination:+60": { // obj exist but the index don't "Stat_1": {}, }, "*string:*req.ExtraField:Usage": { // index is valid but the obj does not exist "InexistingStats": {}, }, } // we will set manually some indexes that points to an nil object or index is valid but the obj is missing if err := dm.SetIndexes(utils.CacheStatFilterIndexes, "cgrates.org", indexes, true, utils.NonTransactional); err != nil { t.Error(err) } exp = &FilterIHReply{ MissingObjects: []string{"cgrates.org:InexistingStats"}, MissingIndexes: map[string][]string{ "cgrates.org:*string:*opts.*apikey:sts1234": {"Stat_1"}, "cgrates.org:*string:*req.RequestType:*postpaid": {"Stat_1"}, }, BrokenIndexes: map[string][]string{ "cgrates.org:*suffix:*req.Destination:+60": {"Stat_1"}, }, MissingFilters: map[string][]string{}, } if rply, err := GetFltrIdxHealth(dm, ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, false, nil), ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, false, nil), ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, false, nil), utils.CacheStatFilterIndexes); err != nil { t.Error(err) } else if !reflect.DeepEqual(exp, rply) { t.Errorf("Expected %+v, received %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } //we will use an inexisting Filter(not inline) for the same StatQueueProfile sqPrf = &StatQueueProfile{ Tenant: "cgrates.org", ID: "Stat_1", FilterIDs: []string{ "*string:~*opts.*apikey:sts1234", "*string:~*req.RequestType:*postpaid", "*prefix:~*resources.RES_GRP1.Available:10", // *resources will not be indexing "*suffix:BrokenFilter:Invalid", "FLTR_1_NOT_EXIST", }, Weight: 30, QueueLength: 100, TTL: 10 * time.Second, MinItems: 0, Metrics: []*MetricWithFilters{ { MetricID: "*tcd", }, { MetricID: "*asr", }, { MetricID: "*acd", }, }, Blocker: true, ThresholdIDs: []string{utils.MetaNone}, } if err := dm.SetStatQueueProfile(sqPrf, false); err != nil { t.Error(err) } exp = &FilterIHReply{ MissingObjects: []string{"cgrates.org:InexistingStats"}, MissingIndexes: map[string][]string{ "cgrates.org:*string:*opts.*apikey:sts1234": {"Stat_1"}, "cgrates.org:*string:*req.RequestType:*postpaid": {"Stat_1"}, }, BrokenIndexes: map[string][]string{ "cgrates.org:*suffix:*req.Destination:+60": {"Stat_1"}, }, MissingFilters: map[string][]string{ "cgrates.org:FLTR_1_NOT_EXIST": {"Stat_1"}, }, } if rply, err := GetFltrIdxHealth(dm, ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, false, nil), ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, false, nil), ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, false, nil), utils.CacheStatFilterIndexes); err != nil { t.Error(err) } else if !reflect.DeepEqual(exp, rply) { t.Errorf("Expected %+v, received %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } } func TestHealthIndexRoutes(t *testing.T) { Cache.Clear(nil) cfg := config.NewDefaultCGRConfig() db, dErr := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) if dErr != nil { t.Error(dErr) } dm := NewDataManager(db, cfg.CacheCfg(), nil) // we will set this routes but without indexing rtPrf := &RouteProfile{ Tenant: "routes.com", ID: "ROUTE_ACNT_1001", FilterIDs: []string{"*string:~*opts.*apikey:rts1234", "*string:~*req.Usage:160s", "*string:~*stats.STATS_VENDOR_2.*acd:1m", // *stats will not be indexing "*string:~*nothing.Denied:true", "*suffix:BrokenFilter:Invalid"}, Sorting: utils.MetaLC, SortingParameters: []string{}, Routes: []*Route{ { ID: "route1", Weight: 10, Blocker: false, RouteParameters: "", }, { ID: "route2", RatingPlanIDs: []string{"RP_1002"}, Weight: 20, Blocker: false, }, }, Weight: 10, } if err := dm.SetRouteProfile(rtPrf, false); err != nil { t.Error(err) } args := &IndexHealthArgsWith3Ch{} exp := &FilterIHReply{ MissingIndexes: map[string][]string{ "routes.com:*string:*opts.*apikey:rts1234": {"ROUTE_ACNT_1001"}, "routes.com:*string:*req.Usage:160s": {"ROUTE_ACNT_1001"}, "routes.com:*string:*nothing.Denied:true": {"ROUTE_ACNT_1001"}, }, BrokenIndexes: map[string][]string{}, MissingFilters: map[string][]string{}, MissingObjects: []string{}, } if rply, err := GetFltrIdxHealth(dm, ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, false, nil), ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, false, nil), ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, false, nil), utils.CacheRouteFilterIndexes); err != nil { t.Error(err) } else if !reflect.DeepEqual(exp, rply) { t.Errorf("Expected %+v, received %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } indexes := map[string]utils.StringSet{ "*string:*req.RequestType:*rated": { // obj exist but the index don't "ROUTE_ACNT_1001": {}, }, "*suffix:*req.Destination:+222": { "ROUTE_ACNT_1001": {}, "ROUTE_ACNT_1002": {}, }, "*suffix:*req.Destination:+333": { "ROUTE_ACNT_1001": {}, "ROUTE_ACNT_1002": {}, }, "*string:*req.ExtraField:Usage": { // index is valid but the obj does not exist "InexistingRoute": {}, }, } // we will set manually some indexes that points to an nil object or index is valid but the obj is missing if err := dm.SetIndexes(utils.CacheRouteFilterIndexes, "routes.com", indexes, true, utils.NonTransactional); err != nil { t.Error(err) } exp = &FilterIHReply{ MissingObjects: []string{ "routes.com:InexistingRoute", "routes.com:ROUTE_ACNT_1002", }, MissingIndexes: map[string][]string{ "routes.com:*string:*opts.*apikey:rts1234": {"ROUTE_ACNT_1001"}, "routes.com:*string:*req.Usage:160s": {"ROUTE_ACNT_1001"}, "routes.com:*string:*nothing.Denied:true": {"ROUTE_ACNT_1001"}, }, BrokenIndexes: map[string][]string{ "routes.com:*suffix:*req.Destination:+222": {"ROUTE_ACNT_1001"}, "routes.com:*suffix:*req.Destination:+333": {"ROUTE_ACNT_1001"}, "routes.com:*string:*req.RequestType:*rated": {"ROUTE_ACNT_1001"}, }, MissingFilters: map[string][]string{}, } if rply, err := GetFltrIdxHealth(dm, ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, false, nil), ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, false, nil), ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, false, nil), utils.CacheRouteFilterIndexes); err != nil { t.Error(err) } else { sort.Strings(rply.MissingObjects) sort.Strings(exp.MissingObjects) if !reflect.DeepEqual(exp, rply) { t.Errorf("Expected %+v, received %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } } //we will use an inexisting Filter(not inline) for the same RouteProfile rtPrf = &RouteProfile{ Tenant: "routes.com", ID: "ROUTE_ACNT_1001", FilterIDs: []string{"*string:~*opts.*apikey:rts1234", "*string:~*req.Usage:160s", "*string:~*stats.STATS_VENDOR_2.*acd:1m", // *stats will not be indexing "*string:~*nothing.Denied:true", "*suffix:BrokenFilter:Invalid", "FLTR_1_NOT_EXIST", }, Sorting: utils.MetaLC, SortingParameters: []string{}, Routes: []*Route{ { ID: "route1", Weight: 10, Blocker: false, RouteParameters: "", }, { ID: "route2", RatingPlanIDs: []string{"RP_1002"}, Weight: 20, Blocker: false, }, }, Weight: 10, } if err := dm.SetRouteProfile(rtPrf, false); err != nil { t.Error(err) } exp = &FilterIHReply{ MissingObjects: []string{ "routes.com:InexistingRoute", "routes.com:ROUTE_ACNT_1002", }, MissingIndexes: map[string][]string{ "routes.com:*string:*opts.*apikey:rts1234": {"ROUTE_ACNT_1001"}, "routes.com:*string:*req.Usage:160s": {"ROUTE_ACNT_1001"}, "routes.com:*string:*nothing.Denied:true": {"ROUTE_ACNT_1001"}, }, BrokenIndexes: map[string][]string{ "routes.com:*suffix:*req.Destination:+222": {"ROUTE_ACNT_1001"}, "routes.com:*suffix:*req.Destination:+333": {"ROUTE_ACNT_1001"}, "routes.com:*string:*req.RequestType:*rated": {"ROUTE_ACNT_1001"}, }, MissingFilters: map[string][]string{ "routes.com:FLTR_1_NOT_EXIST": {"ROUTE_ACNT_1001"}, }, } if rply, err := GetFltrIdxHealth(dm, ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, false, nil), ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, false, nil), ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, false, nil), utils.CacheRouteFilterIndexes); err != nil { t.Error(err) } else { sort.Strings(rply.MissingObjects) sort.Strings(exp.MissingObjects) if !reflect.DeepEqual(exp, rply) { t.Errorf("Expected %+v, received %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } } } func TestHealthIndexDispatchers(t *testing.T) { Cache.Clear(nil) cfg := config.NewDefaultCGRConfig() db, dErr := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) if dErr != nil { t.Error(dErr) } dm := NewDataManager(db, cfg.CacheCfg(), nil) // we will set this dispatcherProfile but without indexing dspPrf := &DispatcherProfile{ Tenant: "cgrates.org", ID: "Dsp1", Subsystems: []string{utils.MetaAny, utils.MetaSessionS}, FilterIDs: []string{ "*string:~*opts.*apikey:dps1234|dsp9876", "*string:~*req.AnswerTime:2013-11-07T08:42:26Z", "*string:~*libphonenumber.<~*req.Destination>:+443234566", // *libphonenumber will not be indexing "*suffix:BrokenFilter:Invalid", }, Strategy: utils.MetaRandom, Weight: 20, Hosts: DispatcherHostProfiles{ { ID: "ALL", }, }, } if err := dm.SetDispatcherProfile(dspPrf, false); err != nil { t.Error(err) } args := &IndexHealthArgsWith3Ch{} exp := &FilterIHReply{ MissingIndexes: map[string][]string{ "cgrates.org:*any:*string:*opts.*apikey:dps1234": {"Dsp1"}, "cgrates.org:*any:*string:*opts.*apikey:dsp9876": {"Dsp1"}, "cgrates.org:*any:*string:*req.AnswerTime:2013-11-07T08:42:26Z": {"Dsp1"}, "cgrates.org:*sessions:*string:*opts.*apikey:dps1234": {"Dsp1"}, "cgrates.org:*sessions:*string:*opts.*apikey:dsp9876": {"Dsp1"}, "cgrates.org:*sessions:*string:*req.AnswerTime:2013-11-07T08:42:26Z": {"Dsp1"}, }, BrokenIndexes: map[string][]string{}, MissingFilters: map[string][]string{}, MissingObjects: []string{}, } if rply, err := GetFltrIdxHealth(dm, ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, false, nil), ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, false, nil), ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, false, nil), utils.CacheDispatcherFilterIndexes); err != nil { t.Error(err) } else if !reflect.DeepEqual(exp, rply) { t.Errorf("Expected %+v, received %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } // we will set manually some indexes that points to an nil object or index is valid but the obj is missing indexes := map[string]utils.StringSet{ "*string:*req.RequestType:*rated": { // obj exist but the index don't "Dsp1": {}, "Dsp2": {}, }, "*suffix:*opts.Destination:+100": { // obj exist but the index don't "Dsp1": {}, "Dsp2": {}, }, "*string:*req.ExtraField:Usage": { // index is valid but the obj does not exist "InexistingDispatcher": {}, "InexistingDispatcher2": {}, }, } if err := dm.SetIndexes(utils.CacheDispatcherFilterIndexes, "cgrates.org", indexes, true, utils.NonTransactional); err != nil { t.Error(err) } //get the newIdxHealth for dispatchersProfile exp = &FilterIHReply{ MissingIndexes: map[string][]string{ "cgrates.org:*any:*string:*opts.*apikey:dps1234": {"Dsp1"}, "cgrates.org:*any:*string:*opts.*apikey:dsp9876": {"Dsp1"}, "cgrates.org:*any:*string:*req.AnswerTime:2013-11-07T08:42:26Z": {"Dsp1"}, "cgrates.org:*sessions:*string:*opts.*apikey:dps1234": {"Dsp1"}, "cgrates.org:*sessions:*string:*opts.*apikey:dsp9876": {"Dsp1"}, "cgrates.org:*sessions:*string:*req.AnswerTime:2013-11-07T08:42:26Z": {"Dsp1"}, }, BrokenIndexes: map[string][]string{ "cgrates.org:*suffix:*opts.Destination:+100": {"Dsp1"}, "cgrates.org:*string:*req.RequestType:*rated": {"Dsp1"}, }, MissingFilters: map[string][]string{}, MissingObjects: []string{ "cgrates.org:Dsp2", "cgrates.org:InexistingDispatcher", "cgrates.org:InexistingDispatcher2", }, } if rply, err := GetFltrIdxHealth(dm, ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, false, nil), ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, false, nil), ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, false, nil), utils.CacheDispatcherFilterIndexes); err != nil { t.Error(err) } else { sort.Strings(rply.MissingObjects) sort.Strings(exp.MissingObjects) if !reflect.DeepEqual(exp, rply) { t.Errorf("Expected %+v, received %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } } //we will use an inexisting Filter(not inline) for the same DispatcherProfile dspPrf = &DispatcherProfile{ Tenant: "cgrates.org", ID: "Dsp1", Subsystems: []string{utils.MetaAny, utils.MetaSessionS}, FilterIDs: []string{ "*string:~*opts.*apikey:dps1234|dsp9876", "*string:~*req.AnswerTime:2013-11-07T08:42:26Z", "*string:~*libphonenumber.<~*req.Destination>:+443234566", // *libphonenumber will not be indexing "*suffix:BrokenFilter:Invalid", "FLTR_1_NOT_EXIST", }, Strategy: utils.MetaRandom, Weight: 20, Hosts: DispatcherHostProfiles{ { ID: "ALL", }, }, } if err := dm.SetDispatcherProfile(dspPrf, false); err != nil { t.Error(err) } //get the newIdxHealth for dispatchersProfile exp = &FilterIHReply{ MissingIndexes: map[string][]string{ "cgrates.org:*any:*string:*opts.*apikey:dps1234": {"Dsp1"}, "cgrates.org:*any:*string:*opts.*apikey:dsp9876": {"Dsp1"}, "cgrates.org:*any:*string:*req.AnswerTime:2013-11-07T08:42:26Z": {"Dsp1"}, "cgrates.org:*sessions:*string:*opts.*apikey:dps1234": {"Dsp1"}, "cgrates.org:*sessions:*string:*opts.*apikey:dsp9876": {"Dsp1"}, "cgrates.org:*sessions:*string:*req.AnswerTime:2013-11-07T08:42:26Z": {"Dsp1"}, }, BrokenIndexes: map[string][]string{ "cgrates.org:*suffix:*opts.Destination:+100": {"Dsp1"}, "cgrates.org:*string:*req.RequestType:*rated": {"Dsp1"}, }, MissingFilters: map[string][]string{ "cgrates.org:FLTR_1_NOT_EXIST": {"Dsp1"}, }, MissingObjects: []string{ "cgrates.org:Dsp2", "cgrates.org:InexistingDispatcher", "cgrates.org:InexistingDispatcher2", }, } if rply, err := GetFltrIdxHealth(dm, ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, false, nil), ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, false, nil), ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, false, nil), utils.CacheDispatcherFilterIndexes); err != nil { t.Error(err) } else { sort.Strings(rply.MissingObjects) sort.Strings(exp.MissingObjects) if !reflect.DeepEqual(exp, rply) { t.Errorf("Expected %+v, received %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } } } func TestIndexHealthMultipleProfiles(t *testing.T) { Cache.Clear(nil) cfg := config.NewDefaultCGRConfig() db, dErr := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) if dErr != nil { t.Error(dErr) } dm := NewDataManager(db, cfg.CacheCfg(), nil) // we will set this multiple chargers but without indexing(same and different indexes) chPrf1 := &ChargerProfile{ Tenant: "cgrates.org", ID: "Raw", FilterIDs: []string{ "*string:~*opts.*eventType:ChargerAccountUpdate", "*string:~*req.Account:1234", "*string:~*asm.ID:1002", // *asm will not be indexing "*suffix:BrokenFilter:Invalid"}, RunID: "raw", AttributeIDs: []string{"*constant:*req.RequestType:*none"}, Weight: 20, } chPrf2 := &ChargerProfile{ Tenant: "cgrates.org", ID: "Default", FilterIDs: []string{ "*string:~*opts.*eventType:ChargerAccountUpdate", "*prefix:~*req.Destination:+2234|~*req.CGRID", "*suffix:~*req.Usage:10", "*string:~*req.Account:1234", "FLTR_1_NOT_EXIST2", }, RunID: "*default", Weight: 10, } chPrf3 := &ChargerProfile{ Tenant: "cgrates.org", ID: "Call_Attr1", FilterIDs: []string{ "*string:~*req.Account:1234", "*string:*broken:index", "FLTR_1_NOT_EXIST", "FLTR_1_NOT_EXIST2", }, AttributeIDs: []string{"Attr1"}, RunID: "*attribute", Weight: 0, } if err := dm.SetChargerProfile(chPrf1, false); err != nil { t.Error(err) } if err := dm.SetChargerProfile(chPrf2, false); err != nil { t.Error(err) } if err := dm.SetChargerProfile(chPrf3, false); err != nil { t.Error(err) } // check the indexes health args := &IndexHealthArgsWith3Ch{} exp := &FilterIHReply{ MissingIndexes: map[string][]string{ "cgrates.org:*string:*opts.*eventType:ChargerAccountUpdate": {"Raw", "Default"}, "cgrates.org:*string:*req.Account:1234": {"Raw", "Default", "Call_Attr1"}, "cgrates.org:*prefix:*req.Destination:+2234": {"Default"}, "cgrates.org:*suffix:*req.Usage:10": {"Default"}, }, BrokenIndexes: map[string][]string{}, MissingFilters: map[string][]string{ "cgrates.org:FLTR_1_NOT_EXIST2": {"Default", "Call_Attr1"}, "cgrates.org:FLTR_1_NOT_EXIST": {"Call_Attr1"}, }, MissingObjects: []string{}, } if rply, err := GetFltrIdxHealth(dm, ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, false, nil), ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, false, nil), ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, false, nil), utils.CacheChargerFilterIndexes); err != nil { t.Error(err) } else { sort.Strings(exp.MissingIndexes["cgrates.org:*string:*opts.*eventType:ChargerAccountUpdate"]) sort.Strings(exp.MissingIndexes["cgrates.org:*string:*req.Account:1234"]) sort.Strings(exp.MissingFilters["cgrates.org:FLTR_1_NOT_EXIST2"]) sort.Strings(rply.MissingIndexes["cgrates.org:*string:*opts.*eventType:ChargerAccountUpdate"]) sort.Strings(rply.MissingIndexes["cgrates.org:*string:*req.Account:1234"]) sort.Strings(rply.MissingFilters["cgrates.org:FLTR_1_NOT_EXIST2"]) if !reflect.DeepEqual(exp, rply) { t.Errorf("Expected %+v, received %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } } } func TestIndexHealthReverseChecking(t *testing.T) { Cache.Clear(nil) cfg := config.NewDefaultCGRConfig() db, dErr := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) if dErr != nil { t.Error(dErr) } dm := NewDataManager(db, cfg.CacheCfg(), nil) // we will set this multiple chargers but without indexing(same and different indexes) chPrf1 := &ChargerProfile{ Tenant: "cgrates.org", ID: "Raw", FilterIDs: []string{ "*string:~*req.Account:1234", "FLTR_1", "FLTR_2", }, RunID: "raw", AttributeIDs: []string{"*constant:*req.RequestType:*none"}, Weight: 20, } if err := dm.SetChargerProfile(chPrf1, false); err != nil { t.Error(err) } // get cachePrefix for every subsystem objCaches := make(map[string]*ltcache.Cache) for indxType := range utils.CacheIndexesToPrefix { objCaches[indxType] = ltcache.NewCache(-1, 0, false, true, nil) } // reverse indexes for charger exp := map[string]*ReverseFilterIHReply{ utils.CacheChargerFilterIndexes: { MissingFilters: map[string][]string{ "cgrates.org:FLTR_1": {"Raw"}, "cgrates.org:FLTR_2": {"Raw"}, }, BrokenReverseIndexes: map[string][]string{}, MissingReverseIndexes: map[string][]string{}, }, } if rply, err := GetRevFltrIdxHealth(dm, ltcache.NewCache(-1, 0, false, true, nil), ltcache.NewCache(-1, 0, false, true, nil), objCaches); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(exp, rply) { t.Errorf("Expecting: %+v,\n received: %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } // set reverse indexes for Raw that is already set and 2 that does not exist indexes := map[string]utils.StringSet{ utils.CacheChargerFilterIndexes: { "Raw": {}, "Default": {}, "Call_Attr1": {}, }, } if err := dm.SetIndexes(utils.CacheReverseFilterIndexes, "cgrates.org:FLTR_1", indexes, true, utils.NonTransactional); err != nil { t.Error(err) } // reverse indexes for charger with the changes exp = map[string]*ReverseFilterIHReply{ utils.CacheChargerFilterIndexes: { MissingFilters: map[string][]string{ "cgrates.org:FLTR_1": {"Raw"}, "cgrates.org:FLTR_2": {"Raw"}, }, BrokenReverseIndexes: map[string][]string{}, MissingReverseIndexes: map[string][]string{}, MissingObjects: []string{"cgrates.org:Default", "cgrates.org:Call_Attr1"}, }, } if rply, err := GetRevFltrIdxHealth(dm, ltcache.NewCache(-1, 0, false, true, nil), ltcache.NewCache(-1, 0, false, true, nil), objCaches); err != nil { t.Fatal(err) } else { sort.Strings(exp[utils.CacheChargerFilterIndexes].MissingObjects) sort.Strings(rply[utils.CacheChargerFilterIndexes].MissingObjects) if !reflect.DeepEqual(exp, rply) { t.Errorf("Expecting: %+v,\n received: %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } } // reverse for a filter present in PROFILE but does not exist for the same indexes indexes = map[string]utils.StringSet{ utils.CacheChargerFilterIndexes: { "Raw": {}, "Default": {}, "Call_Attr1": {}, }, } if err := dm.SetIndexes(utils.CacheReverseFilterIndexes, "cgrates.org:FLTR_NOT_IN_PROFILE", indexes, true, utils.NonTransactional); err != nil { t.Error(err) } // reverse indexes for charger with the changes exp = map[string]*ReverseFilterIHReply{ utils.CacheChargerFilterIndexes: { MissingFilters: map[string][]string{ "cgrates.org:FLTR_1": {"Raw"}, "cgrates.org:FLTR_2": {"Raw"}, }, BrokenReverseIndexes: map[string][]string{ "cgrates.org:Raw": {"FLTR_NOT_IN_PROFILE"}, }, MissingReverseIndexes: map[string][]string{}, MissingObjects: []string{"cgrates.org:Default", "cgrates.org:Call_Attr1"}, }, } if rply, err := GetRevFltrIdxHealth(dm, ltcache.NewCache(-1, 0, false, true, nil), ltcache.NewCache(-1, 0, false, true, nil), objCaches); err != nil { t.Fatal(err) } else { sort.Strings(exp[utils.CacheChargerFilterIndexes].MissingObjects) sort.Strings(rply[utils.CacheChargerFilterIndexes].MissingObjects) if !reflect.DeepEqual(exp, rply) { t.Errorf("Expecting: %+v,\n received: %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } } } func TestIndexHealthMissingReverseIndexes(t *testing.T) { Cache.Clear(nil) cfg := config.NewDefaultCGRConfig() db, dErr := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) if dErr != nil { t.Error(dErr) } dm := NewDataManager(db, cfg.CacheCfg(), nil) filter1 := &Filter{ Tenant: "cgrates.org", ID: "FLTR_1", Rules: []*FilterRule{ { Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Destination, Type: utils.MetaString, Values: []string{"1001"}, }, }, } filter2 := &Filter{ Tenant: "cgrates.org", ID: "FLTR_2", Rules: []*FilterRule{ { Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Destination, Type: utils.MetaString, Values: []string{"1003"}, }, }, } filter3 := &Filter{ Tenant: "cgrates.org", ID: "FLTR_3", Rules: []*FilterRule{ { Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Destination, Type: utils.MetaString, Values: []string{"1004"}, }, }, } if err := dm.SetFilter(filter1, false); err != nil { t.Error(err) } if err := dm.SetFilter(filter2, false); err != nil { t.Error(err) } if err := dm.SetFilter(filter3, false); err != nil { t.Error(err) } chPrf1 := &ChargerProfile{ Tenant: "cgrates.org", ID: "Raw", FilterIDs: []string{ "*string:~*req.Account:1234", "FLTR_1", "FLTR_3", }, RunID: "raw", AttributeIDs: []string{"*constant:*req.RequestType:*none"}, Weight: 20, } if err := dm.SetChargerProfile(chPrf1, false); err != nil { t.Error(err) } // get cachePrefix for every subsystem objCaches := make(map[string]*ltcache.Cache) for indxType := range utils.CacheIndexesToPrefix { objCaches[indxType] = ltcache.NewCache(-1, 0, false, true, nil) } exp := map[string]*ReverseFilterIHReply{ utils.CacheChargerFilterIndexes: { MissingFilters: map[string][]string{}, BrokenReverseIndexes: map[string][]string{}, MissingReverseIndexes: map[string][]string{ "cgrates.org:Raw": {"FLTR_1", "FLTR_3"}, }, }, } if rply, err := GetRevFltrIdxHealth(dm, ltcache.NewCache(-1, 0, false, true, nil), ltcache.NewCache(-1, 0, false, true, nil), objCaches); err != nil { t.Fatal(err) } else { if !reflect.DeepEqual(exp, rply) { t.Errorf("Expecting: %+v,\n received: %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } } Cache.Clear(nil) if err := dm.SetFilter(filter1, true); err != nil { t.Error(err) } if err := dm.SetFilter(filter2, true); err != nil { t.Error(err) } if err := dm.SetFilter(filter3, true); err != nil { t.Error(err) } // initialized cachePrefix for every subsystem again for a new case objCaches = make(map[string]*ltcache.Cache) for indxType := range utils.CacheIndexesToPrefix { objCaches[indxType] = ltcache.NewCache(-1, 0, false, true, nil) } chPrf1 = &ChargerProfile{ Tenant: "cgrates.org", ID: "Raw", FilterIDs: []string{ "*string:~*req.Account:1234", "FLTR_1", "FLTR_2", "FLTR_3", }, RunID: "raw", AttributeIDs: []string{"*constant:*req.RequestType:*none"}, Weight: 20, } // now we set the charger with indexing and check the reverse indexes health if err := dm.SetChargerProfile(chPrf1, false); err != nil { t.Error(err) } exp = map[string]*ReverseFilterIHReply{ utils.CacheChargerFilterIndexes: { MissingFilters: map[string][]string{}, BrokenReverseIndexes: map[string][]string{}, MissingReverseIndexes: map[string][]string{ "cgrates.org:Raw": {"FLTR_1", "FLTR_2", "FLTR_3"}, }, }, } if rply, err := GetRevFltrIdxHealth(dm, ltcache.NewCache(-1, 0, false, true, nil), ltcache.NewCache(-1, 0, false, true, nil), objCaches); err != nil { t.Fatal(err) } else { sort.Strings(exp[utils.CacheChargerFilterIndexes].MissingReverseIndexes["cgrates.org:Raw"]) sort.Strings(rply[utils.CacheChargerFilterIndexes].MissingReverseIndexes["cgrates.org:Raw"]) if !reflect.DeepEqual(exp, rply) { t.Errorf("Expecting: %+v,\n received: %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } } // delete the filters and check again the reverse index health if err := dm.RemoveFilter("cgrates.org", "FLTR_1", true); err != nil { t.Error(err) } if err := dm.RemoveFilter("cgrates.org", "FLTR_2", true); err != nil { t.Error(err) } if err := dm.RemoveFilter("cgrates.org", "FLTR_3", true); err != nil { t.Error(err) } // Nnow the exepcted should be on missing filters as those were removed lately exp = map[string]*ReverseFilterIHReply{ utils.CacheChargerFilterIndexes: { MissingFilters: map[string][]string{ "cgrates.org:FLTR_1": {"Raw"}, "cgrates.org:FLTR_2": {"Raw"}, "cgrates.org:FLTR_3": {"Raw"}, }, BrokenReverseIndexes: map[string][]string{}, MissingReverseIndexes: map[string][]string{}, }, } if rply, err := GetRevFltrIdxHealth(dm, ltcache.NewCache(-1, 0, false, true, nil), ltcache.NewCache(-1, 0, false, true, nil), objCaches); err != nil { t.Fatal(err) } else { sort.Strings(exp[utils.CacheChargerFilterIndexes].MissingReverseIndexes["cgrates.org:Raw"]) sort.Strings(rply[utils.CacheChargerFilterIndexes].MissingReverseIndexes["cgrates.org:Raw"]) if !reflect.DeepEqual(exp, rply) { t.Errorf("Expecting: %+v,\n received: %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } } }