diff --git a/data/conf/samples/rerate_exp_multiple_engines/rerate_exp_engine1_internal/cgrates.json b/data/conf/samples/rerate_exp_multiple_engines/rerate_exp_engine1_internal/cgrates.json new file mode 100644 index 000000000..a8dc5979f --- /dev/null +++ b/data/conf/samples/rerate_exp_multiple_engines/rerate_exp_engine1_internal/cgrates.json @@ -0,0 +1,76 @@ +{ + "general": { + "log_level": 7, + "reply_timeout": "50s" + }, + + "listen": { + "rpc_json": ":2012", + "rpc_gob": ":2013", + "http": ":2080" + }, + + "data_db": { + "db_type": "*internal" + }, + + "stor_db": { + "db_type": "*internal" + }, + + "ees": { + "enabled": true, + "exporters": [ + { + "id": "toNg2", + "type": "*rpc", + "opts": { + "rpcCodec": "*json", + "connIDs": ["rplConn"], + "serviceMethod": "CDRsV1.ProcessEvent", + "keyPath": "" , + "certPath": "", + "caPath": "", + "tls": false, + "rpcConnTimeout" : "1s", + "rpcReplyTimeout":"5s", + "rpcAPIOpts": { + "*ralS": false + } + } + } + ] + }, + + + "rpc_conns": { + "rplConn": { + "strategy": "*broadcast_sync", + "conns": [{"address": "127.0.0.1:22012", "transport": "*json"}] + } + }, + + "rals": { + "enabled": true, + "max_increments":3000000 + }, + + "cdrs": { + "enabled": true, + "rals_conns": ["*localhost"], + "store_cdrs": false, + "online_cdr_exports": ["toNg2"], + "ees_conns": ["*localhost"] + }, + + "sessions": { + "enabled": true, + "rals_conns": ["*localhost"], + "cdrs_conns": ["*localhost"], + "replication_conns": ["rplConn"] + }, + + "apiers": { + "enabled": true + } +} \ No newline at end of file diff --git a/data/conf/samples/rerate_exp_multiple_engines/rerate_exp_engine1_mongo/cgrates.json b/data/conf/samples/rerate_exp_multiple_engines/rerate_exp_engine1_mongo/cgrates.json new file mode 100644 index 000000000..590ee5ebb --- /dev/null +++ b/data/conf/samples/rerate_exp_multiple_engines/rerate_exp_engine1_mongo/cgrates.json @@ -0,0 +1,82 @@ +{ + "general": { + "log_level": 7, + "reply_timeout": "50s" + }, + + + "listen": { + "rpc_json": ":2012", + "rpc_gob": ":2013", + "http": ":2080" + }, + + "data_db": { + "db_type": "mongo", + "db_name": "10", + "db_port": 27017 + }, + + + "stor_db": { + "db_type": "mongo", + "db_name": "cgrates", + "db_port": 27017 + }, + + "ees": { + "enabled": true, + "exporters": [ + { + "id": "toNg2", + "type": "*rpc", + "opts": { + "rpcCodec": "*json", + "connIDs": ["rplConn"], + "serviceMethod": "CDRsV1.ProcessEvent", + "keyPath": "" , + "certPath": "", + "caPath": "", + "tls": false, + "rpcConnTimeout" : "1s", + "rpcReplyTimeout":"5s", + "rpcAPIOpts": { + "*ralS": false + } + } + } + ] + }, + + + "rpc_conns": { + "rplConn": { + "strategy": "*broadcast_sync", + "conns": [{"address": "127.0.0.1:22012", "transport": "*json"}] + } + }, + + "rals": { + "enabled": true, + "max_increments":3000000 + }, + + "cdrs": { + "enabled": true, + "rals_conns": ["*localhost"], + "store_cdrs": false, + "online_cdr_exports": ["toNg2"], + "ees_conns": ["*localhost"] + }, + + "sessions": { + "enabled": true, + "rals_conns": ["*localhost"], + "cdrs_conns": ["*localhost"], + "replication_conns": ["rplConn"] + }, + + "apiers": { + "enabled": true + } +} \ No newline at end of file diff --git a/data/conf/samples/rerate_exp_multiple_engines/rerate_exp_engine1_mysql/cgrates.json b/data/conf/samples/rerate_exp_multiple_engines/rerate_exp_engine1_mysql/cgrates.json new file mode 100644 index 000000000..b7d56861f --- /dev/null +++ b/data/conf/samples/rerate_exp_multiple_engines/rerate_exp_engine1_mysql/cgrates.json @@ -0,0 +1,78 @@ +{ + "general": { + "log_level": 7, + "reply_timeout": "50s" + }, + + "listen": { + "rpc_json": ":2012", + "rpc_gob": ":2013", + "http": ":2080" + }, + + "data_db": { + "db_type": "redis", + "db_port": 6379, + "db_name": "10" + }, + + "stor_db": { + "db_password": "CGRateS.org" + }, + + "ees": { + "enabled": true, + "exporters": [ + { + "id": "toNg2", + "type": "*rpc", + "opts": { + "rpcCodec": "*json", + "connIDs": ["rplConn"], + "serviceMethod": "CDRsV1.ProcessEvent", + "keyPath": "" , + "certPath": "", + "caPath": "", + "tls": false, + "rpcConnTimeout" : "1s", + "rpcReplyTimeout":"5s", + "rpcAPIOpts": { + "*ralS": false + } + } + } + ] + }, + + + "rpc_conns": { + "rplConn": { + "strategy": "*broadcast_sync", + "conns": [{"address": "127.0.0.1:22012", "transport": "*json"}] + } + }, + + "rals": { + "enabled": true, + "max_increments":3000000 + }, + + "cdrs": { + "enabled": true, + "rals_conns": ["*localhost"], + "store_cdrs": false, + "online_cdr_exports": ["toNg2"], + "ees_conns": ["*localhost"] + }, + + "sessions": { + "enabled": true, + "rals_conns": ["*localhost"], + "cdrs_conns": ["*localhost"], + "replication_conns": ["rplConn"] + }, + + "apiers": { + "enabled": true + } +} \ No newline at end of file diff --git a/data/conf/samples/rerate_exp_multiple_engines/rerate_exp_engine2_internal/cgrates.json b/data/conf/samples/rerate_exp_multiple_engines/rerate_exp_engine2_internal/cgrates.json new file mode 100644 index 000000000..3ab965d3a --- /dev/null +++ b/data/conf/samples/rerate_exp_multiple_engines/rerate_exp_engine2_internal/cgrates.json @@ -0,0 +1,37 @@ +{ + "general": { + "log_level": 7, + "reply_timeout": "50s" + }, + + "listen": { + "rpc_json": ":22012", + "rpc_gob": ":22013", + "http": ":22080" + }, + + "data_db": { + "db_type": "*internal" + }, + + "stor_db": { + "db_type": "*internal" + }, + + "cdrs": { + "enabled": true, + "rals_conns": ["*localhost"], + "store_cdrs": true + }, + + "sessions": { + "enabled": true, + "rals_conns": ["*localhost"], + "cdrs_conns": ["*localhost"], + "listen_bijson": "127.0.0.1:22014" + }, + + "apiers": { + "enabled": true + } +} \ No newline at end of file diff --git a/data/conf/samples/rerate_exp_multiple_engines/rerate_exp_engine2_mongo/cgrates.json b/data/conf/samples/rerate_exp_multiple_engines/rerate_exp_engine2_mongo/cgrates.json new file mode 100644 index 000000000..cd09ff948 --- /dev/null +++ b/data/conf/samples/rerate_exp_multiple_engines/rerate_exp_engine2_mongo/cgrates.json @@ -0,0 +1,41 @@ +{ + "general": { + "log_level": 7, + "reply_timeout": "50s" + }, + + "listen": { + "rpc_json": ":22012", + "rpc_gob": ":22013", + "http": ":22080" + }, + + "data_db": { + "db_type": "mongo", + "db_name": "10", + "db_port": 27017 + }, + + "stor_db": { + "db_type": "mongo", + "db_name": "cgrates", + "db_port": 27017 + }, + + "cdrs": { + "enabled": true, + "rals_conns": ["*localhost"], + "store_cdrs": true + }, + + "sessions": { + "enabled": true, + "rals_conns": ["*localhost"], + "cdrs_conns": ["*localhost"], + "listen_bijson": "127.0.0.1:22014" + }, + + "apiers": { + "enabled": true + } +} \ No newline at end of file diff --git a/data/conf/samples/rerate_exp_multiple_engines/rerate_exp_engine2_mysql/cgrates.json b/data/conf/samples/rerate_exp_multiple_engines/rerate_exp_engine2_mysql/cgrates.json new file mode 100644 index 000000000..258d1d206 --- /dev/null +++ b/data/conf/samples/rerate_exp_multiple_engines/rerate_exp_engine2_mysql/cgrates.json @@ -0,0 +1,39 @@ +{ + "general": { + "log_level": 7, + "reply_timeout": "50s" + }, + + "listen": { + "rpc_json": ":22012", + "rpc_gob": ":22013", + "http": ":22080" + }, + + "data_db": { + "db_type": "redis", + "db_port": 6379, + "db_name": "10" + }, + + "stor_db": { + "db_password": "CGRateS.org" + }, + + "cdrs": { + "enabled": true, + "rals_conns": ["*localhost"], + "store_cdrs": true + }, + + "sessions": { + "enabled": true, + "rals_conns": ["*localhost"], + "cdrs_conns": ["*localhost"], + "listen_bijson": "127.0.0.1:22014" + }, + + "apiers": { + "enabled": true + } +} \ No newline at end of file diff --git a/general_tests/rerate_exp_it_test.go b/general_tests/rerate_exp_it_test.go new file mode 100644 index 000000000..1e2f2a764 --- /dev/null +++ b/general_tests/rerate_exp_it_test.go @@ -0,0 +1,501 @@ +//go:build integration +// +build integration + +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ +package general_tests + +import ( + "math" + "net/rpc" + "path" + "reflect" + "testing" + "time" + + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +var ( + ng1CfgPath, ng2CfgPath string + ng1Cfg, ng2Cfg *config.CGRConfig + ng1RPC, ng2RPC *rpc.Client + ng1ConfDIR, ng2ConfDIR string //run tests for specific configuration + rrDelay int + ng1UUID = utils.GenUUID() + ng2UUID = utils.GenUUID() + + rrTests = []func(t *testing.T){ + testRerateExpLoadConfig, + testRerateExpInitDataDb, + testRerateExpResetStorDb, + testRerateExpStartEngine, + testRerateExpRPCConn, + testRerateExpLoadTP, + testRerateExpSetBalance, + testRerateExpGetAccountAfterBalanceSet, + testRerateExpProcessEventCDR1, // * + testRerateExpCheckCDRCostAfterProcessEvent1, + testRerateExpGetAccountAfterProcessEvent1, + testRerateExpProcessEventCDR2, // * + testRerateExpCheckCDRCostAfterProcessEvent2, + testRerateExpGetAccountAfterProcessEvent2, + testRerateExpProcessEventCDR3, // ** + testRerateExpCheckCDRCostAfterProcessEvent3, + testRerateExpGetAccountAfterProcessEvent3, + testRerateExpRerateCDRs, // *** + testRerateExpCheckCDRCostsAfterRerate, + testRerateExpGetAccountAfterRerate, + testRerateExpStopEngine, + } + + // * -CDRs are processed and debited on the first engine, then exported and stored on second engine's storDB; + // ** -Mimics the storage of external unprocessed cdrs (second engine's storDB); + // *** -Rerates all the existing cdrs (processed or not), ordering them by AnswerTime. +) + +func TestRerateExpIT(t *testing.T) { + switch *dbType { + case utils.MetaInternal: + ng1ConfDIR = "rerate_exp_engine1_internal" + ng2ConfDIR = "rerate_exp_engine2_internal" + case utils.MetaMySQL: + ng1ConfDIR = "rerate_exp_engine1_mysql" + ng2ConfDIR = "rerate_exp_engine2_mysql" + case utils.MetaMongo: + ng1ConfDIR = "rerate_exp_engine1_mongo" + ng2ConfDIR = "rerate_exp_engine2_mongo" + case utils.MetaPostgres: + t.SkipNow() + default: + t.Fatal("Unknown Database type") + } + + for _, stest := range rrTests { + t.Run(ng1ConfDIR, stest) + } +} + +func testRerateExpLoadConfig(t *testing.T) { + var err error + ng1CfgPath = path.Join(*dataDir, "conf", "samples", "rerate_exp_multiple_engines", ng1ConfDIR) + if ng1Cfg, err = config.NewCGRConfigFromPath(ng1CfgPath); err != nil { + t.Error(err) + } + ng2CfgPath = path.Join(*dataDir, "conf", "samples", "rerate_exp_multiple_engines", ng2ConfDIR) + if ng2Cfg, err = config.NewCGRConfigFromPath(ng2CfgPath); err != nil { + t.Error(err) + } + rrDelay = 1000 +} + +func testRerateExpInitDataDb(t *testing.T) { + if err := engine.InitDataDb(ng1Cfg); err != nil { + t.Fatal(err) + } + if err := engine.InitDataDb(ng2Cfg); err != nil { + t.Fatal(err) + } +} + +func testRerateExpResetStorDb(t *testing.T) { + if err := engine.InitStorDb(ng1Cfg); err != nil { + t.Fatal(err) + } + if err := engine.InitStorDb(ng2Cfg); err != nil { + t.Fatal(err) + } +} + +func testRerateExpStartEngine(t *testing.T) { + if _, err := engine.StopStartEngine(ng1CfgPath, rrDelay); err != nil { + t.Fatal(err) + } + if _, err = engine.StartEngine(ng2CfgPath, rrDelay); err != nil { + t.Fatal(err) + } +} + +func testRerateExpRPCConn(t *testing.T) { + var err error + ng1RPC, err = newRPCClient(ng1Cfg.ListenCfg()) + if err != nil { + t.Fatal("Could not connect to rater: ", err.Error()) + } + ng2RPC, err = newRPCClient(ng2Cfg.ListenCfg()) + if err != nil { + t.Fatal("Could not connect to rater: ", err.Error()) + } +} + +func testRerateExpLoadTP(t *testing.T) { + var reply string + attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "reratecdrs")} + if err := ng1RPC.Call(utils.APIerSv1LoadTariffPlanFromFolder, attrs, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Error("Unexpected reply returned", reply) + } + time.Sleep(200 * time.Millisecond) +} + +func testRerateExpStopEngine(t *testing.T) { + if err := engine.KillEngine(rrDelay); err != nil { + t.Error(err) + } +} + +func testRerateExpSetBalance(t *testing.T) { + attrSetBalance := utils.AttrSetBalance{ + Tenant: "cgrates.org", + Account: "1001", + Value: float64(time.Minute), + BalanceType: utils.MetaVoice, + Balance: map[string]interface{}{ + utils.ID: "1001", + }, + } + var reply string + if err := ng1RPC.Call(utils.APIerSv2SetBalance, attrSetBalance, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Errorf("Received: %s", reply) + } +} + +func testRerateExpGetAccountAfterBalanceSet(t *testing.T) { + expAcnt := engine.Account{ + ID: "cgrates.org:1001", + BalanceMap: map[string]engine.Balances{ + utils.MetaVoice: { + { + ID: "1001", + Value: float64(time.Minute), + }, + }, + }, + } + var acnt engine.Account + attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} + if err := ng1RPC.Call(utils.APIerSv2GetAccount, attrs, &acnt); err != nil { + t.Error(err) + } else { + expAcnt.UpdateTime = acnt.UpdateTime + expAcnt.BalanceMap[utils.MetaVoice][0].Uuid = acnt.BalanceMap[utils.MetaVoice][0].Uuid + if !reflect.DeepEqual(acnt, expAcnt) { + t.Errorf("expected: <%+v>,\nreceived: <%+v>", utils.ToJSON(expAcnt), utils.ToJSON(acnt)) + } + } +} + +func testRerateExpProcessEventCDR1(t *testing.T) { + argsEv := &engine.ArgV1ProcessEvent{ + Flags: []string{utils.MetaRALs, utils.MetaExport}, + CGREvent: utils.CGREvent{ + Tenant: "cgrates.org", + ID: "event1", + Event: map[string]interface{}{ + utils.RunID: "run_1", + utils.CGRID: ng1UUID, + utils.Tenant: "cgrates.org", + utils.Category: "call", + utils.ToR: utils.MetaVoice, + utils.OriginID: "processCDR1", + utils.OriginHost: "OriginHost1", + utils.RequestType: utils.MetaPseudoPrepaid, + utils.AccountField: "1001", + utils.Destination: "1002", + utils.SetupTime: time.Date(2021, time.February, 2, 16, 14, 50, 0, time.UTC), + utils.AnswerTime: time.Date(2021, time.February, 2, 16, 15, 0, 0, time.UTC), + utils.Usage: 2 * time.Minute, + }, + }, + } + var reply string + if err := ng1RPC.Call(utils.CDRsV1ProcessEvent, argsEv, &reply); err != nil { + t.Error(err) + } + time.Sleep(50 * time.Millisecond) +} + +func testRerateExpCheckCDRCostAfterProcessEvent1(t *testing.T) { + var cdrs []*engine.CDR + if err := ng2RPC.Call(utils.CDRsV1GetCDRs, &utils.RPCCDRsFilterWithAPIOpts{ + RPCCDRsFilter: &utils.RPCCDRsFilter{ + RunIDs: []string{"run_1"}, + }}, &cdrs); err != nil { + t.Error(err) + } else if cdrs[0].Usage != 2*time.Minute { + t.Errorf("expected usage to be <%+v>, received <%+v>", 2*time.Minute, cdrs[0].Usage) + } else if cdrs[0].Cost != 0.6 { + t.Errorf("expected cost to be <%+v>, received <%+v>", 0.6, cdrs[0].Cost) + } +} + +func testRerateExpGetAccountAfterProcessEvent1(t *testing.T) { + expAcnt := engine.Account{ + ID: "cgrates.org:1001", + BalanceMap: map[string]engine.Balances{ + utils.MetaVoice: { + { + ID: "1001", + Value: 0, + }, + }, + utils.MetaMonetary: { + { + ID: utils.MetaDefault, + Value: -0.6, + }, + }, + }, + } + var acnt engine.Account + attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} + if err := ng1RPC.Call(utils.APIerSv2GetAccount, attrs, &acnt); err != nil { + t.Error(err) + } else { + expAcnt.UpdateTime = acnt.UpdateTime + expAcnt.BalanceMap[utils.MetaVoice][0].Uuid = acnt.BalanceMap[utils.MetaVoice][0].Uuid + expAcnt.BalanceMap[utils.MetaMonetary][0].Uuid = acnt.BalanceMap[utils.MetaMonetary][0].Uuid + acnt.BalanceMap[utils.MetaMonetary][0].Value = math.Round(acnt.BalanceMap[utils.MetaMonetary][0].Value*10) / 10 + if !reflect.DeepEqual(acnt, expAcnt) { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ToJSON(expAcnt), utils.ToJSON(acnt)) + } + } +} + +func testRerateExpProcessEventCDR2(t *testing.T) { + argsEv := &engine.ArgV1ProcessEvent{ + Flags: []string{utils.MetaRALs, utils.MetaExport}, + CGREvent: utils.CGREvent{ + Tenant: "cgrates.org", + ID: "event2", + Event: map[string]interface{}{ + utils.RunID: "run_2", + utils.CGRID: ng1UUID, + utils.Tenant: "cgrates.org", + utils.Category: "call", + utils.ToR: utils.MetaVoice, + utils.OriginID: "processCDR2", + utils.OriginHost: "OriginHost2", + utils.RequestType: utils.MetaPseudoPrepaid, + utils.AccountField: "1001", + utils.Destination: "1002", + utils.SetupTime: time.Date(2021, time.February, 2, 15, 14, 50, 0, time.UTC), + utils.AnswerTime: time.Date(2021, time.February, 2, 15, 15, 0, 0, time.UTC), + utils.Usage: 2 * time.Minute, + }, + }, + } + var reply string + if err := ng1RPC.Call(utils.CDRsV1ProcessEvent, argsEv, &reply); err != nil { + t.Error(err) + } + time.Sleep(50 * time.Millisecond) +} + +func testRerateExpCheckCDRCostAfterProcessEvent2(t *testing.T) { + var cdrs []*engine.CDR + if err := ng2RPC.Call(utils.CDRsV1GetCDRs, &utils.RPCCDRsFilterWithAPIOpts{ + RPCCDRsFilter: &utils.RPCCDRsFilter{ + RunIDs: []string{"run_2"}, + }}, &cdrs); err != nil { + t.Error(err) + } else if cdrs[0].Usage != 2*time.Minute { + t.Errorf("expected usage to be <%+v>, received <%+v>", 2*time.Minute, cdrs[0].Usage) + } else if cdrs[0].Cost != 1.2 { + t.Errorf("expected cost to be <%+v>, received <%+v>", 1.2, cdrs[0].Cost) + } +} + +func testRerateExpGetAccountAfterProcessEvent2(t *testing.T) { + expAcnt := engine.Account{ + ID: "cgrates.org:1001", + BalanceMap: map[string]engine.Balances{ + utils.MetaVoice: { + { + ID: "1001", + Value: 0, + }, + }, + utils.MetaMonetary: { + { + ID: utils.MetaDefault, + Value: -1.8, + }, + }, + }, + } + var acnt engine.Account + attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} + if err := ng1RPC.Call(utils.APIerSv2GetAccount, attrs, &acnt); err != nil { + t.Error(err) + } else { + expAcnt.UpdateTime = acnt.UpdateTime + expAcnt.BalanceMap[utils.MetaVoice][0].Uuid = acnt.BalanceMap[utils.MetaVoice][0].Uuid + expAcnt.BalanceMap[utils.MetaMonetary][0].Uuid = acnt.BalanceMap[utils.MetaMonetary][0].Uuid + acnt.BalanceMap[utils.MetaMonetary][0].Value = math.Round(acnt.BalanceMap[utils.MetaMonetary][0].Value*10) / 10 + if !reflect.DeepEqual(acnt, expAcnt) { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ToJSON(expAcnt), utils.ToJSON(acnt)) + } + } +} + +func testRerateExpProcessEventCDR3(t *testing.T) { + argsEv := &engine.ArgV1ProcessEvent{ + CGREvent: utils.CGREvent{ + Tenant: "cgrates.org", + ID: "event3", + Event: map[string]interface{}{ + utils.RunID: "run_3", + utils.CGRID: ng2UUID, + utils.Tenant: "cgrates.org", + utils.Category: "call", + utils.ToR: utils.MetaVoice, + utils.OriginID: "processCDR3", + utils.OriginHost: "OriginHost3", + utils.RequestType: utils.MetaPseudoPrepaid, + utils.AccountField: "1001", + utils.Destination: "1002", + utils.SetupTime: time.Date(2021, time.February, 2, 17, 14, 50, 0, time.UTC), + utils.AnswerTime: time.Date(2021, time.February, 2, 17, 15, 0, 0, time.UTC), + utils.Usage: 1 * time.Minute, + }, + }, + } + var reply string + if err := ng2RPC.Call(utils.CDRsV1ProcessEvent, argsEv, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Error("Unexpected reply received: ", reply) + } +} + +func testRerateExpCheckCDRCostAfterProcessEvent3(t *testing.T) { + var cdrs []*engine.CDR + if err := ng2RPC.Call(utils.CDRsV1GetCDRs, &utils.RPCCDRsFilterWithAPIOpts{ + RPCCDRsFilter: &utils.RPCCDRsFilter{ + RunIDs: []string{"run_2"}, + }}, &cdrs); err != nil { + t.Error(err) + } else if cdrs[0].Usage != 2*time.Minute { + t.Errorf("expected usage to be <%+v>, received <%+v>", 1*time.Minute, cdrs[0].Usage) + } else if cdrs[0].Cost != 1.2 { + t.Errorf("expected cost to be <%+v>, received <%+v>", 0.6, cdrs[0].Cost) + } +} + +func testRerateExpGetAccountAfterProcessEvent3(t *testing.T) { + expAcnt := engine.Account{ + ID: "cgrates.org:1001", + BalanceMap: map[string]engine.Balances{ + utils.MetaVoice: { + { + ID: "1001", + Value: 0, + }, + }, + utils.MetaMonetary: { + { + ID: utils.MetaDefault, + Value: -1.8, + }, + }, + }, + } + var acnt engine.Account + attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} + if err := ng1RPC.Call(utils.APIerSv2GetAccount, attrs, &acnt); err != nil { + t.Error(err) + } else { + expAcnt.UpdateTime = acnt.UpdateTime + expAcnt.BalanceMap[utils.MetaVoice][0].Uuid = acnt.BalanceMap[utils.MetaVoice][0].Uuid + expAcnt.BalanceMap[utils.MetaMonetary][0].Uuid = acnt.BalanceMap[utils.MetaMonetary][0].Uuid + acnt.BalanceMap[utils.MetaMonetary][0].Value = math.Round(acnt.BalanceMap[utils.MetaMonetary][0].Value*10) / 10 + if !reflect.DeepEqual(acnt, expAcnt) { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ToJSON(expAcnt), utils.ToJSON(acnt)) + } + } +} + +func testRerateExpRerateCDRs(t *testing.T) { + var reply string + if err := ng2RPC.Call(utils.CDRsV1RateCDRs, &engine.ArgRateCDRs{ + Flags: []string{utils.MetaRALs}, + RPCCDRsFilter: utils.RPCCDRsFilter{ + OrderBy: utils.AnswerTime, + CGRIDs: []string{ng1UUID, ng2UUID}, + }}, &reply); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if reply != utils.OK { + t.Error("Unexpected reply received: ", reply) + } +} + +func testRerateExpCheckCDRCostsAfterRerate(t *testing.T) { + var cdrs []*engine.CDR + if err := ng2RPC.Call(utils.CDRsV1GetCDRs, &utils.RPCCDRsFilterWithAPIOpts{ + RPCCDRsFilter: &utils.RPCCDRsFilter{ + CGRIDs: []string{ng1UUID, ng2UUID}, + OrderBy: utils.AnswerTime, + }}, &cdrs); err != nil { + t.Error(err) + } else if cdrs[0].Cost != 0.6 { + t.Errorf("expected cost to be <%+v>, received <%+v>", 0.6, cdrs[0].Cost) + } else if cdrs[1].Cost != 1.2 { + t.Errorf("expected cost to be <%+v>, received <%+v>", 1.2, cdrs[1].Cost) + } else if cdrs[2].Cost != 0.6 { + t.Errorf("expected cost to be <%+v>, received <%+v>", 0.6, cdrs[2].Cost) + } +} + +func testRerateExpGetAccountAfterRerate(t *testing.T) { + expAcnt := engine.Account{ + ID: "cgrates.org:1001", + BalanceMap: map[string]engine.Balances{ + utils.MetaVoice: { + { + ID: "1001", + Value: 0, + }, + }, + utils.MetaMonetary: { + { + ID: utils.MetaDefault, + Value: -2.4, + }, + }, + }, + } + var acnt engine.Account + attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} + if err := ng1RPC.Call(utils.APIerSv2GetAccount, attrs, &acnt); err != nil { + t.Error(err) + } else { + expAcnt.UpdateTime = acnt.UpdateTime + expAcnt.BalanceMap[utils.MetaVoice][0].Uuid = acnt.BalanceMap[utils.MetaVoice][0].Uuid + expAcnt.BalanceMap[utils.MetaMonetary][0].Uuid = acnt.BalanceMap[utils.MetaMonetary][0].Uuid + acnt.BalanceMap[utils.MetaMonetary][0].Value = math.Round(acnt.BalanceMap[utils.MetaMonetary][0].Value*10) / 10 + if !reflect.DeepEqual(acnt, expAcnt) { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ToJSON(expAcnt), utils.ToJSON(acnt)) + } + } +}