/* 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 ( "fmt" "reflect" "regexp" "testing" "time" "github.com/cgrates/birpc" "github.com/cgrates/birpc/context" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" "github.com/cgrates/ltcache" ) func TestCachesReplicateRemove(t *testing.T) { cfg := config.NewDefaultCGRConfig() cfg.CacheCfg().ReplicationConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.ReplicationConnsCfg)} cfg.CacheCfg().Partitions = map[string]*config.CacheParamCfg{ utils.ReplicatorSv1GetDispatcherHost: { Replicate: true, }, } db, dErr := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) if dErr != nil { t.Error(dErr) } dm := NewDataManager(db, cfg.CacheCfg(), nil) clientconn := make(chan birpc.ClientConnector, 1) clientconn <- &ccMock{ calls: map[string]func(ctx *context.Context, args any, reply any) error{ utils.CacheSv1ReplicateRemove: func(ctx *context.Context, args, reply any) error { *reply.(*string) = utils.OK return nil }, }, } connMgr := NewConnManager(cfg, map[string]chan birpc.ClientConnector{ utils.ConcatenatedKey(utils.MetaInternal, utils.ReplicationConnsCfg): clientconn, }) chS := CacheS{ cfg: cfg, dm: dm, } SetConnManager(connMgr) if err := chS.ReplicateRemove(utils.ReplicatorSv1GetDispatcherHost, "itm_id"); err != nil { t.Error(err) } } func TestCacheSSetWithReplicate(t *testing.T) { Cache.Clear(nil) args := &utils.ArgCacheReplicateSet{ CacheID: utils.ReplicatorSv1GetActions, ItemID: "itemID", Value: &utils.CachedRPCResponse{Result: "reply", Error: nil}, GroupIDs: []string{"groupId", "groupId"}, } cfg := config.NewDefaultCGRConfig() cfg.CacheCfg().ReplicationConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.ReplicationConnsCfg)} cfg.CacheCfg().Partitions = map[string]*config.CacheParamCfg{ args.CacheID: { Replicate: true, }, } db, dErr := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) if dErr != nil { t.Error(dErr) } dm := NewDataManager(db, cfg.CacheCfg(), nil) clientconn := make(chan birpc.ClientConnector, 1) clientconn <- &ccMock{ calls: map[string]func(ctx *context.Context, args any, reply any) error{ utils.CacheSv1ReplicateSet: func(ctx *context.Context, args, reply any) error { *reply.(*string) = "reply" return nil }, }, } connMgr := NewConnManager(cfg, map[string]chan birpc.ClientConnector{ utils.ConcatenatedKey(utils.MetaInternal, utils.ReplicationConnsCfg): clientconn, }) ltcache := ltcache.NewTransCache(map[string]*ltcache.CacheConfig{ args.CacheID: { MaxItems: 2, }, }) cacheS := &CacheS{ dm: dm, cfg: cfg, tCache: ltcache, } SetConnManager(connMgr) if err := cacheS.SetWithReplicate(args); err != nil { t.Error(err) } } func TestCacheSV1GetItemIDs(t *testing.T) { args := &utils.ArgsGetCacheItemIDsWithAPIOpts{ APIOpts: map[string]any{}, Tenant: "cgrates.org", ArgsGetCacheItemIDs: utils.ArgsGetCacheItemIDs{ CacheID: "cacheID", ItemIDPrefix: "itemID", }, } tscache := ltcache.NewTransCache( map[string]*ltcache.CacheConfig{ "cacheID": { MaxItems: 3, TTL: time.Second * 1, StaticTTL: true, OnEvicted: []func(itmID string, value interface{}){ func(itmID string, value any) { }, }, }, }) tscache.Set("cacheID", "itemID", "", []string{}, true, "tId") 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) chS := &CacheS{ cfg: cfg, dm: dm, tCache: tscache, } reply := &[]string{} exp := &[]string{"itemID"} if err := chS.V1GetItemIDs(context.Background(), args, reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(reply, exp) { t.Errorf("expected %+v,received %+v", utils.ToJSON(exp), utils.ToJSON(reply)) } tscache.Remove("cacheID", "itemID", true, utils.NonTransactional) if err := chS.V1GetItemIDs(context.Background(), args, reply); err == nil || err != utils.ErrNotFound { t.Error(err) } } func TestCacheSV1HasItem(t *testing.T) { args := &utils.ArgsGetCacheItemWithAPIOpts{ APIOpts: map[string]any{}, Tenant: "cgrates.org", ArgsGetCacheItem: utils.ArgsGetCacheItem{ CacheID: "cacheID", ItemID: "itemID", }, } tscache := ltcache.NewTransCache( map[string]*ltcache.CacheConfig{ "cacheID": { MaxItems: 3, TTL: time.Second * 1, StaticTTL: true, OnEvicted: []func(itmID string, value interface{}){ func(itmID string, value any) { }, }, }, }) tscache.Set("cacheID", "itemID", "", []string{}, true, "tId") 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) chS := &CacheS{ cfg: cfg, dm: dm, tCache: tscache, } reply := utils.BoolPointer(false) if err := chS.V1HasItem(context.Background(), args, reply); err != nil { t.Error(err) } } func TestCacheSV1GetItemWithRemote(t *testing.T) { args := &utils.ArgsGetCacheItemWithAPIOpts{ ArgsGetCacheItem: utils.ArgsGetCacheItem{ CacheID: "cacheID", ItemID: "itemId", }, } cfg := config.NewDefaultCGRConfig() cfg.CacheCfg().RemoteConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.RemoteConnsCfg)} cfg.CacheCfg().Partitions = map[string]*config.CacheParamCfg{ args.CacheID: { Remote: true, }, } clientconn := make(chan birpc.ClientConnector, 1) clientconn <- &ccMock{ calls: map[string]func(ctx *context.Context, args any, reply any) error{ utils.CacheSv1GetItem: func(ctx *context.Context, args, reply any) error { return nil }, }, } connMgr := NewConnManager(cfg, map[string]chan birpc.ClientConnector{ utils.ConcatenatedKey(utils.MetaInternal, utils.RemoteConnsCfg): clientconn, }, ) tscache := ltcache.NewTransCache( map[string]*ltcache.CacheConfig{ "cacheID": { MaxItems: 3, TTL: time.Second * 1, StaticTTL: true, OnEvicted: []func(itmID string, value any){ func(itmID string, value any) { }, }, }, }) chS := &CacheS{ cfg: cfg, dm: dm, tCache: tscache, } SetConnManager(connMgr) var reply any = "str" if err := chS.V1GetItemWithRemote(context.Background(), args, &reply); err != nil { t.Error(err) } } func TestCacheSV1GetItem(t *testing.T) { args := &utils.ArgsGetCacheItemWithAPIOpts{ ArgsGetCacheItem: utils.ArgsGetCacheItem{ CacheID: "cacheID", ItemID: "itemID", }, } tscache := ltcache.NewTransCache( map[string]*ltcache.CacheConfig{ "cacheID": { MaxItems: 3, TTL: time.Second * 1, StaticTTL: true, OnEvicted: []func(itmID string, value any){ func(itmID string, value any) {}, }, }, }) tscache.Set("cacheID", "itemID", "value", []string{}, true, "tId") 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) chS := &CacheS{ cfg: cfg, dm: dm, tCache: tscache, } var reply any if err := chS.V1GetItem(context.Background(), args, &reply); err != nil { t.Error(err) } else if val, cancast := reply.(string); cancast { if val != "value" { t.Errorf("expected value,received %v", val) } } tscache.Remove("cacheID", "itemID", true, utils.NonTransactional) if err := chS.V1GetItem(context.Background(), args, &reply); err == nil || err != utils.ErrNotFound { t.Error(err) } } func TestCacheSV1GetItemExpiryTime(t *testing.T) { cacheID := "testCache" itemID := "testItem" tscache := ltcache.NewTransCache( map[string]*ltcache.CacheConfig{ cacheID: { MaxItems: -1, TTL: 30 * time.Minute, }, }) chS := &CacheS{ tCache: tscache, } chS.tCache.Set(cacheID, itemID, "value", []string{}, true, "") want := time.Now().Add(30 * time.Minute) var got time.Time if err := chS.V1GetItemExpiryTime(context.Background(), &utils.ArgsGetCacheItemWithAPIOpts{ ArgsGetCacheItem: utils.ArgsGetCacheItem{ CacheID: cacheID, ItemID: itemID, }, }, &got); err != nil { t.Fatalf("V1GetItemExpiryTime(%q,%q): got unexpected err=%v", cacheID, itemID, err) } if diff := want.Sub(got); diff < 0 || diff > time.Millisecond { t.Errorf("V1GetItemExpiryTime(%q,%q) = %v, want %v (diff %v, margin 1ms)", cacheID, itemID, got.Format(time.StampMicro), want.Format(time.StampMicro), diff) } } func TestCacheSV1RemoveItem(t *testing.T) { args := &utils.ArgsGetCacheItemWithAPIOpts{ ArgsGetCacheItem: utils.ArgsGetCacheItem{ CacheID: "cacheID", ItemID: "itemID", }, } tscache := ltcache.NewTransCache( map[string]*ltcache.CacheConfig{ "cacheID": { MaxItems: 3, TTL: time.Minute * 30, StaticTTL: false, OnEvicted: []func(itmID string, value any){ func(itmID string, value any) {}, }, }, }) tscache.Set("cacheID", "itemID", "value", []string{}, true, "tId") 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) chS := &CacheS{ cfg: cfg, dm: dm, tCache: tscache, } var reply string if err := chS.V1RemoveItem(context.Background(), args, &reply); err != nil { t.Error(err) } else if reply != utils.OK { t.Errorf("expected %v,received %v", utils.OK, reply) } } func TestCacheSV1RemoveItems(t *testing.T) { args := utils.NewAttrReloadCacheWithOpts() args.DestinationIDs = []string{"dest1", "dest2", "dest3"} args.ResourceIDs = []string{"res1", "res2", "res3"} args.FilterIDs = []string{"fltr1", "filtr2", "filtr3"} cfgCache := map[string]*ltcache.CacheConfig{ utils.CacheDestinations: { MaxItems: 3, TTL: time.Minute * 30, StaticTTL: false, OnEvicted: []func(itmID string, value any){ func(itmID string, value any) {}, }, }, utils.CacheResources: { MaxItems: 3, TTL: time.Minute * 30, StaticTTL: false, OnEvicted: []func(itmID string, value any){ func(itmID string, value any) {}, }, }, utils.CacheFilters: { MaxItems: 3, TTL: time.Minute * 30, StaticTTL: false, OnEvicted: []func(itmID string, value any){ func(itmID string, value any) {}, }, }, } args2 := map[string][]string{ utils.CacheDestinations: {"dest1", "dest2", "dest3"}, utils.CacheResources: {"res1", "res2", "res3"}, utils.CacheFilters: {"fltr1", "filtr2", "filtr3"}, } tscache := ltcache.NewTransCache(cfgCache) for keyId := range cfgCache { if itemids, has := args2[keyId]; has { for _, itemid := range itemids { tscache.Set(keyId, itemid, "value", []string{}, true, "tId") } } } 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) chS := &CacheS{ cfg: cfg, dm: dm, tCache: tscache, } reply := "error" if err := chS.V1RemoveItems(context.Background(), args, &reply); err != nil { t.Error(err) } else if reply != utils.OK { t.Errorf("expected %v,received %v", utils.OK, reply) } } func TestCacheSV1Clear(t *testing.T) { args := &utils.AttrCacheIDsWithAPIOpts{ APIOpts: map[string]any{}, Tenant: "cgrates.org", CacheIDs: []string{"cacheID", "cacheID2", "cacheID3"}, } 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) cfgCache := map[string]*ltcache.CacheConfig{ "cacheID": { MaxItems: 3, TTL: time.Minute * 30, StaticTTL: false, OnEvicted: []func(itmID string, value any){ func(itmID string, value any) {}, }, }, "cacheID2": { MaxItems: 3, TTL: time.Minute * 30, StaticTTL: false, OnEvicted: []func(itmID string, value any){ func(itmID string, value any) {}, }, }, "cacheID3": { MaxItems: 3, TTL: time.Minute * 30, StaticTTL: false, OnEvicted: []func(itmID string, value any){ func(itmID string, value any) {}, }, }, } tscache := ltcache.NewTransCache(cfgCache) chS := &CacheS{ cfg: cfg, dm: dm, tCache: tscache, } reply := "error" if err := chS.V1Clear(context.Background(), args, &reply); err != nil { t.Error(err) } } func TestCacheSV1ReplicateSet(t *testing.T) { fltr := &Filter{ Tenant: "cgrates", ID: "filterID", } args := &utils.ArgCacheReplicateSet{ CacheID: "cacheID", ItemID: "itemID", Value: fltr, } 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) tscache := ltcache.NewTransCache( map[string]*ltcache.CacheConfig{ "cacheID": { MaxItems: 3, TTL: time.Minute * 30, StaticTTL: false, OnEvicted: []func(itmID string, value any){ func(itmID string, value any) {}, }, }}, ) chS := &CacheS{ cfg: cfg, dm: dm, tCache: tscache, } reply := "reply" if err := chS.V1ReplicateSet(context.Background(), args, &reply); err != nil { t.Error(err) } else if reply != utils.OK { t.Errorf("expected %+v,received %+v", utils.OK, reply) } val, has := chS.tCache.Get(args.CacheID, args.ItemID) if !has { t.Error("has no value") } if !reflect.DeepEqual(val, fltr) { t.Errorf("expected %+v,received %+v", "", utils.ToJSON(val)) } } func TestCacheSV1GetCacheStats(t *testing.T) { args := &utils.AttrCacheIDsWithAPIOpts{ APIOpts: map[string]any{}, Tenant: "cgrates.org", CacheIDs: []string{"cacheID", "cacheID2", "cacheID3"}, } reply := map[string]*ltcache.CacheStats{} 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) tscache := ltcache.NewTransCache( map[string]*ltcache.CacheConfig{ "cacheID": { MaxItems: 3, TTL: time.Minute * 30, StaticTTL: false, OnEvicted: []func(itmID string, value any){ func(itmID string, value any) {}, }, }, "cacheID2": { MaxItems: 3, TTL: time.Minute * 30, StaticTTL: false, OnEvicted: []func(itmID string, value any){ func(itmID string, value any) {}, }, }, "cacheID3": { MaxItems: 3, TTL: time.Minute * 30, StaticTTL: false, OnEvicted: []func(itmID string, value any){ func(itmID string, value any) {}, }, }, }, ) for i, id := range args.CacheIDs { tscache.Set(id, fmt.Sprintf("%s%d", "item", i), "value", []string{}, true, "tId") } chS := &CacheS{ cfg: cfg, dm: dm, tCache: tscache, } exp := map[string]*ltcache.CacheStats{ "cacheID": {Items: 1, Groups: 0}, "cacheID2": {Items: 1, Groups: 0}, "cacheID3": {Items: 1, Groups: 0}, } if err := chS.V1GetCacheStats(context.Background(), args, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(reply, exp) { t.Errorf("expected %v,received %v", utils.ToJSON(reply), utils.ToJSON(exp)) } } func TestCachesPrecache(t *testing.T) { cfg := config.NewDefaultCGRConfig() cfg.CacheCfg().Partitions = map[string]*config.CacheParamCfg{ utils.CacheDestinations: { Limit: 1, Precache: true, TTL: time.Minute * 2, Remote: true, }, } pcI := map[string]chan struct{}{ utils.CacheDestinations: make(chan struct{})} db, dErr := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) if dErr != nil { t.Error(dErr) } dm := NewDataManager(db, cfg.CacheCfg(), nil) chS := &CacheS{ cfg: cfg, dm: dm, pcItems: pcI, } if err := chS.Precache(); err != nil { t.Error(err) } } func TestV1PrecacheStatus(t *testing.T) { args := &utils.AttrCacheIDsWithAPIOpts{ APIOpts: map[string]any{}, Tenant: "cgrates.org", CacheIDs: []string{utils.CacheFilters}, } cfg := config.NewDefaultCGRConfig() pcI := map[string]chan struct{}{ utils.CacheFilters: make(chan struct{}), } db, dErr := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) if dErr != nil { t.Error(dErr) } dm := NewDataManager(db, cfg.CacheCfg(), nil) chS := &CacheS{ cfg: cfg, dm: dm, pcItems: pcI, } reply := map[string]string{} exp := map[string]string{ utils.CacheFilters: utils.MetaPrecaching, } if err := chS.V1PrecacheStatus(context.Background(), args, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(exp, reply) { t.Errorf("expected %+v,received %+v", exp, reply) } args.CacheIDs = []string{} if err := chS.V1PrecacheStatus(context.Background(), args, &reply); err == nil { t.Error(err) } } func TestCacheSV1HasGroup(t *testing.T) { args := &utils.ArgsGetGroupWithAPIOpts{ ArgsGetGroup: utils.ArgsGetGroup{ CacheID: "cacheId", GroupID: "groupId", }, APIOpts: map[string]any{}, } 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) tscache := ltcache.NewTransCache( map[string]*ltcache.CacheConfig{ "cacheId": { MaxItems: 3, TTL: time.Minute * 30, StaticTTL: false, OnEvicted: []func(itmID string, value any){ func(itmID string, value any) {}, }, }}, ) tscache.Set("cacheId", "itemId", "value", []string{"groupId"}, true, "tId") chS := &CacheS{ cfg: cfg, dm: dm, tCache: tscache, } var reply bool if err := chS.V1HasGroup(context.Background(), args, &reply); err != nil { t.Error(err) } else if !reply { t.Error("expected true,received false") } } func TestCacheSV1HasGroupItemIDs(t *testing.T) { args := &utils.ArgsGetGroupWithAPIOpts{ ArgsGetGroup: utils.ArgsGetGroup{ CacheID: "cacheId", GroupID: "groupId", }, APIOpts: map[string]any{}, } 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) tscache := ltcache.NewTransCache( map[string]*ltcache.CacheConfig{ "cacheId": { MaxItems: 3, TTL: time.Minute * 30, StaticTTL: false, OnEvicted: []func(itmID string, value any){ func(itmID string, value any) {}, }, }}, ) tscache.Set("cacheId", "itemId", "value", []string{"groupId"}, true, "tId") chS := &CacheS{ cfg: cfg, dm: dm, tCache: tscache, } var reply []string exp := []string{"itemId"} if err := chS.V1GetGroupItemIDs(context.Background(), args, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(exp, reply) { t.Errorf("expected %+v,received %+v", exp, reply) } } func TestV1RemoveGroup(t *testing.T) { args := &utils.ArgsGetGroupWithAPIOpts{ ArgsGetGroup: utils.ArgsGetGroup{ CacheID: "cacheId", GroupID: "groupId", }, APIOpts: map[string]any{}, } 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) tscache := ltcache.NewTransCache( map[string]*ltcache.CacheConfig{ "cacheId": { MaxItems: 3, TTL: time.Minute * 30, StaticTTL: false, OnEvicted: []func(itmID string, value any){ func(itmID string, value any) {}, }, }}, ) tscache.Set("cacheId", "itemId", "value", []string{"groupId"}, true, "tId") chS := &CacheS{ cfg: cfg, dm: dm, tCache: tscache, } var reply string if err := chS.V1RemoveGroup(context.Background(), args, &reply); err != nil { t.Error(err) } else if reply != utils.OK { t.Errorf("expected %v,received %v", utils.OK, reply) } if has := tscache.HasGroup(args.CacheID, args.GroupID); has { t.Errorf("expected false,received %+v", has) } } func TestCacheSV1ReplicateRemove(t *testing.T) { args := &utils.ArgCacheReplicateRemove{ CacheID: "cacheID", ItemID: "itemID", APIOpts: map[string]any{}, Tenant: "cgrates.org", } 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) tscache := ltcache.NewTransCache( map[string]*ltcache.CacheConfig{ "cacheId": { MaxItems: 3, TTL: time.Minute * 30, StaticTTL: false, OnEvicted: []func(itmID string, value any){ func(itmID string, value any) {}, }, }}, ) tscache.Set(args.CacheID, args.ItemID, "value", []string{"groupId"}, true, "tId") chS := &CacheS{ cfg: cfg, dm: dm, tCache: tscache, } var reply string if err := chS.V1ReplicateRemove(context.Background(), args, &reply); err != nil { t.Error(err) } else if reply != utils.OK { t.Errorf("expected %v,received %v", utils.OK, reply) } if _, has := tscache.Get(args.CacheID, args.ItemID); has { t.Errorf("expected false,received %+v", has) } } func TestNewCacheS(t *testing.T) { cfg := config.NewDefaultCGRConfig() cfg.CacheCfg().ReplicationConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.ReplicationConnsCfg)} cfg.CacheCfg().Partitions = map[string]*config.CacheParamCfg{ "cacheID": { Limit: 3, TTL: 2 * time.Minute, StaticTTL: true, Replicate: true, }, } db, dErr := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) if dErr != nil { t.Error(dErr) } dm := NewDataManager(db, cfg.CacheCfg(), nil) clientconn := make(chan birpc.ClientConnector, 1) clientconn <- &ccMock{ calls: map[string]func(ctx *context.Context, args any, reply any) error{ utils.CacheSv1ReplicateRemove: func(ctx *context.Context, args, reply any) error { *reply.(*string) = "reply" return nil }, }, } connMgr := NewConnManager(cfg, map[string]chan birpc.ClientConnector{ utils.ConcatenatedKey(utils.MetaInternal, utils.ReplicationConnsCfg): clientconn, }) expCacheS := &CacheS{} SetConnManager(connMgr) if c := NewCacheS(cfg, dm, &CapsStats{}); reflect.DeepEqual(expCacheS, c) { t.Errorf("expected %+v,received %+v", utils.ToJSON(expCacheS), utils.ToJSON(c)) } } func TestCacheRemoveWithoutReplicate(t *testing.T) { 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) tscache := ltcache.NewTransCache( map[string]*ltcache.CacheConfig{ "cacheId": { MaxItems: 3, TTL: time.Minute * 30, StaticTTL: false, OnEvicted: []func(itmID string, value any){ func(itmID string, value any) { }, }, }}, ) tscache.Set("cacheID", "itemId", "value", []string{"groupId"}, true, "tId") chS := &CacheS{ cfg: cfg, dm: dm, tCache: tscache, } chS.RemoveWithoutReplicate("cacheID", "itemId", true, "tId") if _, has := tscache.Get("cacheID", "itemId"); has { t.Error("shouldn't exist") } } func TestCacheRemoveGroup(t *testing.T) { 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) tscache := ltcache.NewTransCache( map[string]*ltcache.CacheConfig{ "cacheId": { MaxItems: 3, TTL: time.Minute * 30, StaticTTL: false, OnEvicted: []func(itmID string, value any){ func(itmID string, value any) { }, }, }}, ) tscache.Set("cacheID", "itemId", "value", []string{"groupId"}, true, "tId") chS := &CacheS{ cfg: cfg, dm: dm, tCache: tscache, } chS.RemoveGroup("cacheID", "groupId") if _, has := tscache.Get("cacheID", "itemId"); has { t.Error("shouldn't exist") } } func TestUpdateReplicationFilters(t *testing.T) { cfg := config.NewDefaultCGRConfig() tmp := *Cache defer func() { *Cache = tmp }() Cache.Clear(nil) Cache = NewCacheS(cfg, nil, nil) Cache.tCache = ltcache.NewTransCache(map[string]*ltcache.CacheConfig{ utils.CacheReplicationHosts: { MaxItems: 3, }, }) objType, objID, connID := "obj", "id", "conn" UpdateReplicationFilters("obj", "id", "conn") if val, has := Cache.Get(utils.CacheReplicationHosts, objType+objID+utils.ConcatenatedKeySep+connID); !has { t.Error("has no value") } else if val.(string) != connID { t.Errorf("expected %v,received %v", connID, val) } } func TestReplicateMultipleIDs(t *testing.T) { tmp := Cache defer func() { Cache = tmp }() Cache.Clear(nil) cfg := config.NewDefaultCGRConfig() cfg.ApierCfg().CachesConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaCaches)} cfg.AttributeSCfg().Enabled = true cfg.CacheCfg().Partitions = map[string]*config.CacheParamCfg{ utils.CacheReplicationHosts: { Limit: 3, }, } Cache = NewCacheS(cfg, nil, nil) connClient := make(chan birpc.ClientConnector, 1) connClient <- &ccMock{ calls: map[string]func(ctx *context.Context, args any, reply any) error{ utils.CacheSv1ReloadCache: func(ctx *context.Context, args, reply any) error { *reply.(*string) = "reply" return nil }, }, } connMgr := NewConnManager(cfg, map[string]chan birpc.ClientConnector{ utils.ConcatenatedKey(utils.MetaInternal, utils.MetaCaches): connClient, }) objType := "obj" objIds := []string{"rpl1", "rpl2"} method := utils.CacheSv1ReloadCache args := &utils.AttrReloadCacheWithAPIOpts{ AccountActionPlanIDs: []string{"accID"}, } if err := replicateMultipleIDs(connMgr, cfg.ApierCfg().CachesConns, false, objType, objIds, method, args); err != nil { t.Error(err) } if err := replicateMultipleIDs(connMgr, cfg.ApierCfg().CachesConns, true, objType, objIds, method, args); err != nil { t.Error(err) } } func TestCachesGetWithRemote(t *testing.T) { Cache.Clear(nil) args := &utils.ArgsGetCacheItemWithAPIOpts{ ArgsGetCacheItem: utils.ArgsGetCacheItem{ CacheID: utils.CacheTBLTPActionPlans, ItemID: "cacheItem", }, } cfg := config.NewDefaultCGRConfig() cfg.DataDbCfg().Items = map[string]*config.ItemOpt{ utils.CacheTBLTPActionPlans: { Limit: 3, Remote: false, }, } db, dErr := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) if dErr != nil { t.Error(dErr) } dm := NewDataManager(db, cfg.CacheCfg(), nil) chS := NewCacheS(cfg, dm, nil) clientconn := make(chan birpc.ClientConnector, 1) clientconn <- &ccMock{ calls: map[string]func(ctx *context.Context, args any, reply any) error{ utils.CacheSv1GetItem: func(ctx *context.Context, args, reply any) error { *reply.(*string) = utils.OK return utils.ErrNotFound }, }, } connMgr := NewConnManager(cfg, map[string]chan birpc.ClientConnector{ utils.ConcatenatedKey(utils.MetaInternal, utils.RemoteConnsCfg): clientconn, }) SetConnManager(connMgr) tpAc := &utils.TPActionTrigger{ BalanceId: "id", Id: "STANDARD_TRIGGERS", ThresholdType: "*min_balance", ThresholdValue: 2, Recurrent: false, MinSleep: "0", ExpirationDate: "date", BalanceType: "*monetary", BalanceDestinationIds: "FS_USERS", ActionsId: "LOG_WARNING", Weight: 10, } chS.tCache.Set(args.CacheID, "cacheItem", tpAc, []string{"cacheItem"}, true, utils.NonTransactional) if val, err := chS.GetWithRemote(args); err != nil { t.Error(err) } else if !reflect.DeepEqual(val, tpAc) { t.Errorf("expected %v,received %v", utils.ToJSON(tpAc), utils.ToJSON(val)) } chS.tCache.Remove(utils.CacheTBLTPActionPlans, "cacheItem", true, utils.NonTransactional) if _, err := chS.GetWithRemote(args); err == nil || err != utils.ErrNotFound { t.Errorf("expected %v,received %v", utils.ErrNotFound, err) } cfg.DataDbCfg().Items[utils.CacheTBLTPActionPlans].Remote = true cfg.CacheCfg().RemoteConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.RemoteConnsCfg)} /* if _, err = chS.GetWithRemote(args); err == nil || err != utils.ErrNotFound { t.Errorf("expected %v,received %v", utils.ErrNotFound, err) } */ } func TestV1LoadCache(t *testing.T) { cfg := config.NewDefaultCGRConfig() tmpDm := dm defer func() { dm = tmpDm }() attr := &utils.AttrReloadCacheWithAPIOpts{ Tenant: "cgrates.org", FilterIDs: []string{"cgrates.org:FLTR_ID"}, AttributeFilterIndexIDs: []string{"cgrates.org:*any:*string:*req.Account:1001", "cgrates.org:*any:*string:*req.Account:1002"}, } chS := NewCacheS(cfg, dm, nil) var reply string if err := chS.V1LoadCache(context.Background(), attr, &reply); err != nil { t.Error(err) } else if reply != utils.OK { t.Errorf("reply should be %v", utils.OK) } } func TestCacheSBeginTransaction(t *testing.T) { tmp := Cache defer func() { Cache = tmp }() Cache.Clear(nil) cfg := config.NewDefaultCGRConfig() db, err := NewInternalDB(nil, nil, false, nil, cfg.DataDbCfg().Items) if err != nil { t.Error(err) } dm := NewDataManager(db, cfg.CacheCfg(), nil) cacheS := NewCacheS(cfg, dm, nil) expFormat := `........-....-....-....-............` rcv := cacheS.BeginTransaction() if matched, err := regexp.Match(expFormat, []byte(rcv)); err != nil { t.Error(err) } else if !matched { t.Errorf("Unexpected transaction format, Received <%v>", rcv) } } func TestCachesV1ReLoadCache(t *testing.T) { cfg := config.NewDefaultCGRConfig() tmpDm := dm defer func() { dm = tmpDm }() attr := &utils.AttrReloadCacheWithAPIOpts{ Tenant: "cgrates.org", FilterIDs: []string{"cgrates.org:FLTR_ID"}, AttributeFilterIndexIDs: []string{"cgrates.org:*any:*string:*req.Account:1001", "cgrates.org:*any:*string:*req.Account:1002"}, } chS := NewCacheS(cfg, dm, nil) var reply string if err := chS.V1ReloadCache(context.Background(), attr, &reply); err != nil { t.Error(err) } else if reply != utils.OK { t.Errorf("reply should be %v", utils.OK) } }