/* 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 cdrs import ( "reflect" "testing" "time" "github.com/cgrates/birpc" "github.com/cgrates/birpc/context" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" ) func TestCDRsV1ProcessEventMock(t *testing.T) { testCache := engine.Cache tmpC := config.CgrConfig() defer func() { engine.Cache = testCache config.SetCgrConfig(tmpC) }() cfg := config.NewDefaultCGRConfig() cfg.CdrsCfg().EEsConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaEEs)} data := engine.NewInternalDB(nil, nil, cfg.DataDbCfg().Items) connMng := engine.NewConnManager(cfg) dm := engine.NewDataManager(data, cfg.CacheCfg(), nil) fltrs := engine.NewFilterS(cfg, nil, dm) engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) storDB := engine.NewInternalDB(nil, nil, cfg.DataDbCfg().Items) newCDRSrv := NewCDRServer(cfg, dm, fltrs, connMng, storDB) ccM := &ccMock{ calls: map[string]func(ctx *context.Context, args any, reply any) error{ utils.EeSv1ProcessEvent: func(ctx *context.Context, args, reply any) error { *reply.(*map[string]map[string]any) = map[string]map[string]any{} return utils.ErrNotFound }, }, } rpcInternal := make(chan birpc.ClientConnector, 1) rpcInternal <- ccM newCDRSrv.connMgr.AddInternalConn(utils.ConcatenatedKey(utils.MetaInternal, utils.MetaEEs), utils.ThresholdSv1, rpcInternal) cgrEv := &utils.CGREvent{ Event: map[string]any{ "Resources": "ResourceProfile1", utils.AnswerTime: time.Date(2014, 7, 14, 14, 30, 0, 0, time.UTC), "UsageInterval": "1s", "PddInterval": "1s", utils.Weight: "20.0", utils.Usage: 135 * time.Second, utils.Cost: 123.0, }, APIOpts: map[string]any{ utils.OptsCDRsExport: true, "*context": utils.MetaCDRs, }, } var rply string err := newCDRSrv.V1ProcessEvent(context.Background(), cgrEv, &rply) if err != nil { t.Errorf("\nExpected <%+v> \n, received <%+v>", nil, err) } expected := &utils.CGREvent{ Tenant: "cgrates.org", ID: "testID", Event: map[string]any{ "Resources": "ResourceProfile1", utils.AnswerTime: time.Date(2014, 7, 14, 14, 30, 0, 0, time.UTC), "UsageInterval": "1s", "PddInterval": "1s", utils.Weight: "20.0", utils.Usage: 135 * time.Second, utils.Cost: 123.0, }, APIOpts: map[string]any{ utils.OptsCDRsExport: true, "*context": utils.MetaCDRs, }, } cgrEv.ID = "testID" delete(cgrEv.APIOpts, utils.MetaCDRID) // ignore autogenerated *cdr field when comparing if !reflect.DeepEqual(expected, cgrEv) { t.Errorf("\nExpected <%+v> \n,received <%+v>", utils.ToJSON(expected), utils.ToJSON(cgrEv)) } } func TestCDRsV1ProcessEventMockErr(t *testing.T) { testCache := engine.Cache tmpC := config.CgrConfig() defer func() { engine.Cache = testCache config.SetCgrConfig(tmpC) }() cfg := config.NewDefaultCGRConfig() cfg.CdrsCfg().EEsConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaEEs)} data := engine.NewInternalDB(nil, nil, cfg.DataDbCfg().Items) connMng := engine.NewConnManager(cfg) dm := engine.NewDataManager(data, cfg.CacheCfg(), nil) fltrs := engine.NewFilterS(cfg, nil, dm) engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) storDB := engine.NewInternalDB(nil, nil, cfg.DataDbCfg().Items) newCDRSrv := NewCDRServer(cfg, dm, fltrs, connMng, storDB) ccM := &ccMock{ calls: map[string]func(ctx *context.Context, args any, reply any) error{ utils.EeSv1ProcessEvent: func(ctx *context.Context, args, reply any) error { *reply.(*map[string]map[string]any) = map[string]map[string]any{} return utils.ErrNotFound }, }, } rpcInternal := make(chan birpc.ClientConnector, 1) rpcInternal <- ccM newCDRSrv.connMgr.AddInternalConn(utils.ConcatenatedKey(utils.MetaInternal, utils.MetaEEs), utils.ThresholdSv1, rpcInternal) cgrEv := &utils.CGREvent{ Tenant: "cgrates.org", ID: "testID", Event: map[string]any{ "Resources": "ResourceProfile1", utils.AnswerTime: time.Date(2014, 7, 14, 14, 30, 0, 0, time.UTC), "UsageInterval": "1s", "PddInterval": "1s", utils.Weight: "20.0", utils.Usage: 135 * time.Second, utils.Cost: 123.0, }, APIOpts: map[string]any{ utils.MetaStats: true, utils.OptsCDRsExport: true, "*context": utils.MetaCDRs, }, } var rply string err := newCDRSrv.V1ProcessEvent(context.Background(), cgrEv, &rply) if err == nil || err.Error() != "PARTIALLY_EXECUTED" { t.Errorf("\nExpected <%+v> \n, received <%+v>", "PARTIALLY_EXECUTED", err) } expected := &utils.CGREvent{ Tenant: "cgrates.org", ID: "testID", Event: map[string]any{ "Resources": "ResourceProfile1", utils.AnswerTime: time.Date(2014, 7, 14, 14, 30, 0, 0, time.UTC), "UsageInterval": "1s", "PddInterval": "1s", utils.Weight: "20.0", utils.Usage: 135 * time.Second, utils.Cost: 123.0, }, APIOpts: map[string]any{ utils.MetaStats: true, utils.OptsCDRsExport: true, "*context": utils.MetaCDRs, }, } delete(cgrEv.APIOpts, utils.MetaCDRID) // ignore autogenerated *cdr field when comparing if !reflect.DeepEqual(expected, cgrEv) { t.Errorf("\nExpected <%+v> \n,received <%+v>", expected, cgrEv) } } func TestCDRsV1ProcessEventMockCache(t *testing.T) { testCache := engine.Cache tmpC := config.CgrConfig() defer func() { engine.Cache = testCache config.SetCgrConfig(tmpC) }() cfg := config.NewDefaultCGRConfig() cfg.CdrsCfg().EEsConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaEEs)} data := engine.NewInternalDB(nil, nil, cfg.DataDbCfg().Items) connMng := engine.NewConnManager(cfg) dm := engine.NewDataManager(data, cfg.CacheCfg(), nil) fltrs := engine.NewFilterS(cfg, nil, dm) engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) storDB := engine.NewInternalDB(nil, nil, cfg.DataDbCfg().Items) newCDRSrv := NewCDRServer(cfg, dm, fltrs, connMng, storDB) ccM := &ccMock{ calls: map[string]func(ctx *context.Context, args any, reply any) error{ utils.EeSv1ProcessEvent: func(ctx *context.Context, args, reply any) error { *reply.(*map[string]map[string]any) = map[string]map[string]any{} return utils.ErrNotFound }, }, } rpcInternal := make(chan birpc.ClientConnector, 1) rpcInternal <- ccM newCDRSrv.connMgr.AddInternalConn(utils.ConcatenatedKey(utils.MetaInternal, utils.MetaEEs), utils.ThresholdSv1, rpcInternal) cgrEv := &utils.CGREvent{ Tenant: "cgrates.org", ID: "testID", Event: map[string]any{ "Resources": "ResourceProfile1", utils.AnswerTime: time.Date(2014, 7, 14, 14, 30, 0, 0, time.UTC), "UsageInterval": "1s", "PddInterval": "1s", utils.Weight: "20.0", utils.Usage: 135 * time.Second, utils.Cost: 123.0, }, APIOpts: map[string]any{ utils.OptsCDRsExport: true, "*context": utils.MetaCDRs, }, } defaultConf := config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses] config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses].Limit = 1 defer func() { config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses] = defaultConf }() var rply string err := newCDRSrv.V1ProcessEvent(context.Background(), cgrEv, &rply) if err != nil { t.Errorf("\nExpected <%+v> \n, received <%+v>", nil, err) } expected := &utils.CGREvent{ Tenant: "cgrates.org", ID: "testID", Event: map[string]any{ "Resources": "ResourceProfile1", utils.AnswerTime: time.Date(2014, 7, 14, 14, 30, 0, 0, time.UTC), "UsageInterval": "1s", "PddInterval": "1s", utils.Weight: "20.0", utils.Usage: 135 * time.Second, utils.Cost: 123.0, }, APIOpts: map[string]any{ utils.OptsCDRsExport: true, "*context": utils.MetaCDRs, }, } delete(cgrEv.APIOpts, utils.MetaCDRID) // ignore autogenerated *cdr field when comparing if !reflect.DeepEqual(expected, cgrEv) { t.Errorf("\nExpected <%+v> \n,received <%+v>", expected, cgrEv) } } func TestCDRsV1ProcessEventWithGetMockCache(t *testing.T) { testCache := engine.Cache tmpC := config.CgrConfig() defer func() { engine.Cache = testCache config.SetCgrConfig(tmpC) }() cfg := config.NewDefaultCGRConfig() cfg.CdrsCfg().EEsConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaEEs)} data := engine.NewInternalDB(nil, nil, cfg.DataDbCfg().Items) connMng := engine.NewConnManager(cfg) dm := engine.NewDataManager(data, cfg.CacheCfg(), nil) fltrs := engine.NewFilterS(cfg, nil, dm) engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) storDB := engine.NewInternalDB(nil, nil, cfg.DataDbCfg().Items) newCDRSrv := NewCDRServer(cfg, dm, fltrs, connMng, storDB) ccM := &ccMock{ calls: map[string]func(ctx *context.Context, args any, reply any) error{ utils.EeSv1ProcessEvent: func(ctx *context.Context, args, reply any) error { *reply.(*map[string]map[string]any) = map[string]map[string]any{} return utils.ErrNotFound }, }, } rpcInternal := make(chan birpc.ClientConnector, 1) rpcInternal <- ccM newCDRSrv.connMgr.AddInternalConn(utils.ConcatenatedKey(utils.MetaInternal, utils.MetaEEs), utils.ThresholdSv1, rpcInternal) cgrEv := &utils.CGREvent{ Event: map[string]any{ "Resources": "ResourceProfile1", utils.AnswerTime: time.Date(2014, 7, 14, 14, 30, 0, 0, time.UTC), "UsageInterval": "1s", "PddInterval": "1s", utils.Weight: "20.0", utils.Usage: 135 * time.Second, utils.Cost: 123.0, }, APIOpts: map[string]any{ utils.OptsCDRsExport: true, "*context": utils.MetaCDRs, }, } defaultConf := config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses] config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses].Limit = 1 defer func() { config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses] = defaultConf }() var rply []*utils.EventsWithOpts err := newCDRSrv.V1ProcessEventWithGet(context.Background(), cgrEv, &rply) if err != nil { t.Errorf("\nExpected <%+v> \n, received <%+v>", nil, err) } expected := &utils.CGREvent{ Tenant: "cgrates.org", ID: "testID", Event: map[string]any{ "Resources": "ResourceProfile1", utils.AnswerTime: time.Date(2014, 7, 14, 14, 30, 0, 0, time.UTC), "UsageInterval": "1s", "PddInterval": "1s", utils.Weight: "20.0", utils.Usage: 135 * time.Second, utils.Cost: 123.0, }, APIOpts: map[string]any{ utils.OptsCDRsExport: true, "*context": utils.MetaCDRs, }, } cgrEv.ID = "testID" delete(cgrEv.APIOpts, utils.MetaCDRID) // ignore autogenerated *cdr field when comparing if !reflect.DeepEqual(expected, cgrEv) { t.Errorf("\nExpected <%+v> \n,received <%+v>", expected, cgrEv) } } func TestCDRsV1ProcessEventWithGetMockCacheErr(t *testing.T) { testCache := engine.Cache tmpC := config.CgrConfig() defer func() { engine.Cache = testCache config.SetCgrConfig(tmpC) }() cfg := config.NewDefaultCGRConfig() cfg.CdrsCfg().EEsConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaEEs)} data := engine.NewInternalDB(nil, nil, cfg.DataDbCfg().Items) connMng := engine.NewConnManager(cfg) dm := engine.NewDataManager(data, cfg.CacheCfg(), nil) fltrs := engine.NewFilterS(cfg, nil, dm) engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) storDB := engine.NewInternalDB(nil, nil, cfg.DataDbCfg().Items) newCDRSrv := NewCDRServer(cfg, dm, fltrs, connMng, storDB) ccM := &ccMock{ calls: map[string]func(ctx *context.Context, args any, reply any) error{ utils.EeSv1ProcessEvent: func(ctx *context.Context, args, reply any) error { *reply.(*map[string]map[string]any) = map[string]map[string]any{} return utils.ErrNotFound }, }, } rpcInternal := make(chan birpc.ClientConnector, 1) rpcInternal <- ccM newCDRSrv.connMgr.AddInternalConn(utils.ConcatenatedKey(utils.MetaInternal, utils.MetaEEs), utils.ThresholdSv1, rpcInternal) cgrEv := &utils.CGREvent{ Tenant: "cgrates.org", ID: "testID", Event: map[string]any{ "Resources": "ResourceProfile1", utils.AnswerTime: time.Date(2014, 7, 14, 14, 30, 0, 0, time.UTC), "UsageInterval": "1s", "PddInterval": "1s", utils.Weight: "20.0", utils.Usage: 135 * time.Second, utils.Cost: 123.0, }, APIOpts: map[string]any{ utils.OptsCDRsExport: true, utils.MetaAttributes: time.Second, "*context": utils.MetaCDRs, }, } defaultConf := config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses] config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses].Limit = 1 defer func() { config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses] = defaultConf }() expectedErr := `retrieving *attributes option failed: cannot convert field: 1s to bool` var rply []*utils.EventsWithOpts err := newCDRSrv.V1ProcessEventWithGet(context.Background(), cgrEv, &rply) if err == nil || err.Error() != expectedErr { t.Errorf("expected <%v>, received <%v>", expectedErr, err) } } func TestCDRsV1ProcessEventCacheGet(t *testing.T) { testCache := engine.Cache tmpC := config.CgrConfig() defer func() { engine.Cache = testCache config.SetCgrConfig(tmpC) }() cfg := config.NewDefaultCGRConfig() cfg.CacheCfg().Partitions[utils.CacheRPCResponses].Limit = 1 data := engine.NewInternalDB(nil, nil, cfg.DataDbCfg().Items) dm := engine.NewDataManager(data, cfg.CacheCfg(), nil) fltrs := engine.NewFilterS(cfg, nil, dm) engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) storDB := engine.NewInternalDB(nil, nil, cfg.DataDbCfg().Items) newCDRSrv := NewCDRServer(cfg, dm, fltrs, nil, storDB) cgrEv := &utils.CGREvent{ Tenant: "cgrates.org", ID: "testID", Event: map[string]any{ utils.Cost: 123, }, } rply := "string" engine.Cache.Set(context.Background(), utils.CacheRPCResponses, "CDRsV1.ProcessEvent:testID", &utils.CachedRPCResponse{Result: &rply, Error: nil}, nil, true, utils.NonTransactional) err := newCDRSrv.V1ProcessEvent(context.Background(), cgrEv, &rply) if err != nil { t.Errorf("\nExpected <%+v> \n, received <%+v>", nil, err) } expected := &utils.CGREvent{ Tenant: "cgrates.org", ID: "testID", Event: map[string]any{ utils.Cost: 123, }, } if !reflect.DeepEqual(expected, cgrEv) { t.Errorf("\nExpected <%+v> \n,received <%+v>", expected, cgrEv) } } func TestCDRsV1ProcessEventWithGetCacheGet(t *testing.T) { testCache := engine.Cache tmpC := config.CgrConfig() defer func() { engine.Cache = testCache config.SetCgrConfig(tmpC) }() cfg := config.NewDefaultCGRConfig() cfg.CacheCfg().Partitions[utils.CacheRPCResponses].Limit = 1 data := engine.NewInternalDB(nil, nil, cfg.DataDbCfg().Items) dm := engine.NewDataManager(data, cfg.CacheCfg(), nil) fltrs := engine.NewFilterS(cfg, nil, dm) engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) storDB := engine.NewInternalDB(nil, nil, cfg.DataDbCfg().Items) newCDRSrv := NewCDRServer(cfg, dm, fltrs, nil, storDB) cgrEv := &utils.CGREvent{ Tenant: "cgrates.org", ID: "testID", Event: map[string]any{ utils.Cost: 123, }, } rply := []*utils.EventsWithOpts{} engine.Cache.Set(context.Background(), utils.CacheRPCResponses, "CDRsV1.ProcessEvent:testID", &utils.CachedRPCResponse{Result: &rply, Error: nil}, nil, true, utils.NonTransactional) err := newCDRSrv.V1ProcessEventWithGet(context.Background(), cgrEv, &rply) if err != nil { t.Errorf("\nExpected <%+v> \n, received <%+v>", nil, err) } expected := &utils.CGREvent{ Tenant: "cgrates.org", ID: "testID", Event: map[string]any{ utils.Cost: 123, }, APIOpts: map[string]any{}, } delete(cgrEv.APIOpts, utils.MetaCDRID) // ignore autogenerated *cdr field when comparing if !reflect.DeepEqual(expected, cgrEv) { t.Errorf("\nExpected <%+v> \n,received <%+v>", expected, cgrEv) } } func TestCDRsV1ProcessEventWithGetMockCacheErrResp(t *testing.T) { testCache := engine.Cache tmpC := config.CgrConfig() defer func() { engine.Cache = testCache config.SetCgrConfig(tmpC) }() engine.Cache.Clear(nil) cfg := config.NewDefaultCGRConfig() cfg.CacheCfg().Partitions[utils.CacheRPCResponses].Limit = 1 config.SetCgrConfig(cfg) data := engine.NewInternalDB(nil, nil, cfg.DataDbCfg().Items) dm := engine.NewDataManager(data, cfg.CacheCfg(), nil) fltrs := engine.NewFilterS(cfg, nil, dm) engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) storDB := engine.NewInternalDB(nil, nil, cfg.DataDbCfg().Items) newCDRSrv := NewCDRServer(cfg, dm, fltrs, nil, storDB) cgrEv := &utils.CGREvent{ Tenant: "cgrates.org", ID: "testID", Event: map[string]any{ utils.Cost: 123, }, APIOpts: map[string]any{}, } evs := []*utils.EventsWithOpts{ { Event: map[string]any{ utils.Cost: 666, }, }, } engine.Cache.Set(context.Background(), utils.CacheRPCResponses, "CDRsV1.ProcessEventWithGet:testID", &utils.CachedRPCResponse{Result: &evs, Error: nil}, nil, true, utils.NonTransactional) var reply []*utils.EventsWithOpts err := newCDRSrv.V1ProcessEventWithGet(context.Background(), cgrEv, &reply) if err != nil { t.Errorf("\nExpected <%+v> \n, received <%+v>", nil, err) } expectedVal := []*utils.EventsWithOpts{ { Event: map[string]any{ utils.Cost: 666, }, }, } if !reflect.DeepEqual(expectedVal, reply) { t.Errorf("Expected %v, received %v", utils.ToJSON(expectedVal), utils.ToJSON(reply)) } }