diff --git a/apier/v1/dispatcher.go b/apier/v1/dispatcher.go index 8828d72d9..f57020ffe 100755 --- a/apier/v1/dispatcher.go +++ b/apier/v1/dispatcher.go @@ -38,18 +38,10 @@ func (dT *DispatcherThresholdSv1) Ping(ign string, reply *string) error { return dT.dS.ThresholdSv1Ping(ign, reply) } -/* To be implemented in console -// GetThresholdIDs implements ThresholdSv1GetThresholdIDs -func (dT *DispatcherThresholdSv1) GetThresholdIDs(tenant string, - tIDs *[]string) error { - return dT.dS.ThresholdSv1GetThresholdIDs(tenant, tIDs) -} -*/ - -// GetThreshold implements ThresholdSv1GetThreshold +// GetThresholdsForEvent implements ThresholdSv1GetThresholdsForEvent func (dT *DispatcherThresholdSv1) GetThresholdsForEvent(tntID *dispatcher.ArgsProcessEventWithApiKey, - t *engine.Threshold) error { - return dT.dS.ThresholdSv1GetThresholdForEvent(tntID, t) + t *engine.Thresholds) error { + return dT.dS.ThresholdSv1GetThresholdsForEvent(tntID, t) } // ProcessEvent implements ThresholdSv1ProcessEvent diff --git a/data/conf/samples/dispatcher/cgrates.json b/data/conf/samples/dispatcher/cgrates.json index ecfbbac56..b92039a5e 100755 --- a/data/conf/samples/dispatcher/cgrates.json +++ b/data/conf/samples/dispatcher/cgrates.json @@ -10,9 +10,9 @@ "listen": { - "rpc_json": ":2012", - "rpc_gob": ":2013", - "http": ":2080", + "rpc_json": ":2112", + "rpc_gob": ":2113", + "http": ":2180", }, "data_db": { // database used to store runtime data (eg: accounts, cdr stats) @@ -32,32 +32,27 @@ }, -"resources": { // Resource service (*new) - "enabled": false, // starts ResourceLimiter service: . -}, - - "dispatcher":{ "enabled": true, // starts DispatcherS service: . "rals_conns": [ {"address": "*internal"}, ], "resources_conns": [ - {"address": "192.168.56.204:2012", "transport": "*json"}, + {"address": "192.168.56.203:2012", "transport": "*json"}, ], "thresholds_conns": [ - {"address": "192.168.56.204:2012", "transport": "*json"}, + {"address": "192.168.56.203:2012", "transport": "*json"}, ], "stats_conns": [ - {"address": "192.168.56.204:2012", "transport": "*json"}, + {"address": "192.168.56.203:2012", "transport": "*json"}, ], "suppliers_conns": [ - {"address": "192.168.56.204:2012", "transport": "*json"}, + {"address": "192.168.56.203:2012", "transport": "*json"}, ], "attributes_conns": [ - {"address": "192.168.56.204:2012", "transport": "*json"}, + {"address": "192.168.56.203:2012", "transport": "*json"}, ], - "dispatching_strategy":"*random", + "dispatching_strategy":"*first", }, diff --git a/dispatcher/attributes.go b/dispatcher/attributes.go index 8645287b0..bcc7deba5 100755 --- a/dispatcher/attributes.go +++ b/dispatcher/attributes.go @@ -19,8 +19,6 @@ along with this program. If not, see package dispatcher import ( - "time" - "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" ) @@ -41,7 +39,6 @@ func (dS *DispatcherService) AttributeSv1GetAttributeForEvent(args *CGREvWithApi Tenant: args.Tenant, ID: utils.UUIDSha1Prefix(), Context: utils.StringPointer(utils.MetaAuth), - Time: utils.TimePointer(time.Now()), Event: map[string]interface{}{ utils.APIKey: args.APIKey, }, @@ -70,7 +67,6 @@ func (dS *DispatcherService) AttributeSv1ProcessEvent(args *CGREvWithApiKey, Tenant: args.Tenant, ID: utils.UUIDSha1Prefix(), Context: utils.StringPointer(utils.MetaAuth), - Time: utils.TimePointer(time.Now()), Event: map[string]interface{}{ utils.APIKey: args.APIKey, }, diff --git a/dispatcher/attributes_it_test.go b/dispatcher/attributes_it_test.go new file mode 100755 index 000000000..d090a44fe --- /dev/null +++ b/dispatcher/attributes_it_test.go @@ -0,0 +1,301 @@ +// +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 dispatcher + +import ( + "net/rpc" + "net/rpc/jsonrpc" + "path" + "reflect" + "testing" + "time" + + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +var ( + dspAttrCfgPath string + dspAttrCfg *config.CGRConfig + dspAttrRPC *rpc.Client + instAttrCfgPath string + instAttrCfg *config.CGRConfig + instAttrRPC *rpc.Client +) + +var sTestsDspAttr = []func(t *testing.T){ + testDspAttrInitCfg, + testDspAttrInitDataDb, + testDspAttrResetStorDb, + testDspAttrStartEngine, + testDspAttrRPCConn, + testDspAttrPing, + testDspAttrLoadData, + testDspAttrAddAttributesWithPermision, + testDspAttrTestAuthKey, + testDspAttrAddAttributesWithPermision2, + testDspAttrTestAuthKey2, + testDspAttrKillEngine, +} + +//Test start here +func TestDspAttributeS(t *testing.T) { + for _, stest := range sTestsDspAttr { + t.Run("", stest) + } +} + +func testDspAttrInitCfg(t *testing.T) { + var err error + dspAttrCfgPath = path.Join(dspDataDir, "conf", "samples", "dispatcher") + dspAttrCfg, err = config.NewCGRConfigFromFolder(dspAttrCfgPath) + if err != nil { + t.Error(err) + } + dspAttrCfg.DataFolderPath = dspDataDir // Share DataFolderPath through config towards StoreDb for Flush() + config.SetCgrConfig(dspAttrCfg) + instAttrCfgPath = path.Join(dspDataDir, "conf", "samples", "tutmysql") + instAttrCfg, err = config.NewCGRConfigFromFolder(instAttrCfgPath) + if err != nil { + t.Error(err) + } + instAttrCfg.DataFolderPath = dspDataDir // Share DataFolderPath through config towards StoreDb for Flush() + config.SetCgrConfig(instAttrCfg) +} + +func testDspAttrInitDataDb(t *testing.T) { + if err := engine.InitDataDb(instAttrCfg); err != nil { + t.Fatal(err) + } +} + +// Wipe out the cdr database +func testDspAttrResetStorDb(t *testing.T) { + if err := engine.InitStorDb(instAttrCfg); err != nil { + t.Fatal(err) + } +} + +// Start CGR Engine +func testDspAttrStartEngine(t *testing.T) { + if _, err := engine.StartEngine(instAttrCfgPath, dspDelay); err != nil { + t.Fatal(err) + } + if _, err := engine.StartEngine(dspAttrCfgPath, dspDelay); err != nil { + t.Fatal(err) + } +} + +// Connect rpc client to rater +func testDspAttrRPCConn(t *testing.T) { + var err error + instAttrRPC, err = jsonrpc.Dial("tcp", instAttrCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal(err) + } + dspAttrRPC, err = jsonrpc.Dial("tcp", dspAttrCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal(err) + } + +} + +func testDspAttrPing(t *testing.T) { + var reply string + if err := instAttrRPC.Call(utils.AttributeSv1Ping, "", &reply); err != nil { + t.Error(err) + } else if reply != utils.Pong { + t.Errorf("Received: %s", reply) + } + if err := dspAttrRPC.Call(utils.AttributeSv1Ping, "", &reply); err != nil { + t.Error(err) + } else if reply != utils.Pong { + t.Errorf("Received: %s", reply) + } +} + +func testDspAttrLoadData(t *testing.T) { + var reply string + attrs := &utils.AttrLoadTpFromFolder{ + FolderPath: path.Join(dspDataDir, "tariffplans", "tutorial2")} + if err := instAttrRPC.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil { + t.Error(err) + } + time.Sleep(500 * time.Millisecond) +} + +func testDspAttrAddAttributesWithPermision(t *testing.T) { + alsPrf := &engine.AttributeProfile{ + Tenant: "cgrates.org", + ID: "AuthKey", + Contexts: []string{utils.MetaAuth}, + FilterIDs: []string{"*string:APIKey:12345"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC), + ExpiryTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC), + }, + Attributes: []*engine.Attribute{ + &engine.Attribute{ + FieldName: utils.APIMethods, + Initial: utils.META_ANY, + Substitute: "ThresholdSv1.GetThAttrholdsForEvent", + Append: true, + }, + }, + Weight: 20, + } + var Attrult string + if err := instAttrRPC.Call("ApierV1.SetAttributeProfile", alsPrf, &Attrult); err != nil { + t.Error(err) + } else if Attrult != utils.OK { + t.Error("Unexpected reply returned", Attrult) + } + var reply *engine.AttributeProfile + if err := instAttrRPC.Call("ApierV1.GetAttributeProfile", + &utils.TenantID{Tenant: "cgrates.org", ID: "AuthKey"}, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(alsPrf, reply) { + t.Errorf("Expecting : %+v, received: %+v", alsPrf, reply) + } +} + +func testDspAttrTestAuthKey(t *testing.T) { + args := &CGREvWithApiKey{ + APIKey: "12345", + CGREvent: utils.CGREvent{ + Tenant: "cgrates.org", + ID: "testAttributeSGetAttributeForEvent", + Context: utils.StringPointer("simpleauth"), + Event: map[string]interface{}{ + utils.Account: "1001", + }, + }, + } + var attrReply *engine.AttributeProfile + if err := dspAttrRPC.Call(utils.AttributeSv1GetAttributeForEvent, + args, &attrReply); err.Error() != utils.ErrUnauthorizedApi.Error() { + t.Error(err) + } +} + +func testDspAttrAddAttributesWithPermision2(t *testing.T) { + alsPrf := &engine.AttributeProfile{ + Tenant: "cgrates.org", + ID: "AuthKey", + Contexts: []string{utils.MetaAuth}, + FilterIDs: []string{"*string:APIKey:12345"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC), + ExpiryTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC), + }, + Attributes: []*engine.Attribute{ + &engine.Attribute{ + FieldName: utils.APIMethods, + Initial: utils.META_ANY, + Substitute: "AttributeSv1.GetAttributeForEvent;AttributeSv1.ProcessEvent", + Append: true, + }, + }, + Weight: 20, + } + var Attrult string + if err := instAttrRPC.Call("ApierV1.SetAttributeProfile", alsPrf, &Attrult); err != nil { + t.Error(err) + } else if Attrult != utils.OK { + t.Error("Unexpected reply returned", Attrult) + } + var reply *engine.AttributeProfile + if err := instAttrRPC.Call("ApierV1.GetAttributeProfile", + &utils.TenantID{Tenant: "cgrates.org", ID: "AuthKey"}, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(alsPrf, reply) { + t.Errorf("Expecting : %+v, received: %+v", alsPrf, reply) + } +} + +func testDspAttrTestAuthKey2(t *testing.T) { + args := &CGREvWithApiKey{ + APIKey: "12345", + CGREvent: utils.CGREvent{ + Tenant: "cgrates.org", + ID: "testAttributeSGetAttributeForEvent", + Context: utils.StringPointer("simpleauth"), + Event: map[string]interface{}{ + utils.Account: "1001", + }, + }, + } + eAttrPrf := &engine.AttributeProfile{ + Tenant: args.Tenant, + ID: "ATTR_1001_SIMPLEAUTH", + FilterIDs: []string{"*string:Account:1001"}, + Contexts: []string{"simpleauth"}, + Attributes: []*engine.Attribute{ + &engine.Attribute{ + FieldName: "Password", + Initial: utils.ANY, + Substitute: "CGRateS.org", + Append: true, + }, + }, + Weight: 20.0, + } + var attrReply *engine.AttributeProfile + if err := dspAttrRPC.Call(utils.AttributeSv1GetAttributeForEvent, + args, &attrReply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eAttrPrf, attrReply) { + t.Errorf("Expecting: %s, received: %s", utils.ToJSON(eAttrPrf), utils.ToJSON(attrReply)) + } + + eRply := &engine.AttrSProcessEventReply{ + MatchedProfile: "ATTR_1001_SIMPLEAUTH", + AlteredFields: []string{"Password"}, + CGREvent: &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "testAttributeSGetAttributeForEvent", + Context: utils.StringPointer("simpleauth"), + Event: map[string]interface{}{ + utils.Account: "1001", + "Password": "CGRateS.org", + }, + }, + } + + var rplyEv engine.AttrSProcessEventReply + if err := dspAttrRPC.Call(utils.AttributeSv1ProcessEvent, + args, &rplyEv); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eRply, &rplyEv) { + t.Errorf("Expecting: %s, received: %s", + utils.ToJSON(eRply), utils.ToJSON(rplyEv)) + } +} + +func testDspAttrKillEngine(t *testing.T) { + if err := engine.KillEngine(dspDelay); err != nil { + t.Error(err) + } + if err := engine.KillEngine(dspDelay); err != nil { + t.Error(err) + } +} diff --git a/dispatcher/resources_it_test.go b/dispatcher/resources_it_test.go new file mode 100755 index 000000000..d8e1cb882 --- /dev/null +++ b/dispatcher/resources_it_test.go @@ -0,0 +1,274 @@ +// +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 dispatcher + +import ( + "net/rpc" + "net/rpc/jsonrpc" + "path" + "reflect" + "testing" + "time" + + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +var ( + dspResCfgPath string + dspResCfg *config.CGRConfig + dspResRPC *rpc.Client + instResCfgPath string + instResCfg *config.CGRConfig + instResRPC *rpc.Client +) + +var sTestsDspRes = []func(t *testing.T){ + testDspResInitCfg, + testDspResInitDataDb, + testDspResResetStorDb, + testDspResStartEngine, + testDspResRPCConn, + testDspResPing, + testDspResLoadData, + testDspResAddAttributesWithPermision, + testDspResTestAuthKey, + testDspResAddAttributesWithPermision2, + testDspResTestAuthKey2, + testDspResKillEngine, +} + +//Test start here +func TestDspResourceS(t *testing.T) { + for _, stest := range sTestsDspRes { + t.Run("", stest) + } +} + +func testDspResInitCfg(t *testing.T) { + var err error + dspResCfgPath = path.Join(dspDataDir, "conf", "samples", "dispatcher") + dspResCfg, err = config.NewCGRConfigFromFolder(dspResCfgPath) + if err != nil { + t.Error(err) + } + dspResCfg.DataFolderPath = dspDataDir // Share DataFolderPath through config towards StoreDb for Flush() + config.SetCgrConfig(dspResCfg) + instResCfgPath = path.Join(dspDataDir, "conf", "samples", "tutmysql") + instResCfg, err = config.NewCGRConfigFromFolder(instResCfgPath) + if err != nil { + t.Error(err) + } + instResCfg.DataFolderPath = dspDataDir // Share DataFolderPath through config towards StoreDb for Flush() + config.SetCgrConfig(instResCfg) +} + +func testDspResInitDataDb(t *testing.T) { + if err := engine.InitDataDb(instResCfg); err != nil { + t.Fatal(err) + } +} + +// Wipe out the cdr database +func testDspResResetStorDb(t *testing.T) { + if err := engine.InitStorDb(instResCfg); err != nil { + t.Fatal(err) + } +} + +// Start CGR Engine +func testDspResStartEngine(t *testing.T) { + if _, err := engine.StartEngine(instResCfgPath, dspDelay); err != nil { + t.Fatal(err) + } + if _, err := engine.StartEngine(dspResCfgPath, dspDelay); err != nil { + t.Fatal(err) + } +} + +// Connect rpc client to rater +func testDspResRPCConn(t *testing.T) { + var err error + instResRPC, err = jsonrpc.Dial("tcp", instResCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal(err) + } + dspResRPC, err = jsonrpc.Dial("tcp", dspResCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal(err) + } + +} + +func testDspResPing(t *testing.T) { + var reply string + if err := instResRPC.Call(utils.ResourceSv1Ping, "", &reply); err != nil { + t.Error(err) + } else if reply != utils.Pong { + t.Errorf("Received: %s", reply) + } + if err := dspResRPC.Call(utils.ResourceSv1Ping, "", &reply); err != nil { + t.Error(err) + } else if reply != utils.Pong { + t.Errorf("Received: %s", reply) + } +} + +func testDspResLoadData(t *testing.T) { + var reply string + attrs := &utils.AttrLoadTpFromFolder{ + FolderPath: path.Join(dspDataDir, "tariffplans", "tutorial2")} + if err := instResRPC.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil { + t.Error(err) + } + time.Sleep(500 * time.Millisecond) +} + +func testDspResAddAttributesWithPermision(t *testing.T) { + alsPrf := &engine.AttributeProfile{ + Tenant: "cgrates.org", + ID: "AuthKey", + Contexts: []string{utils.MetaAuth}, + FilterIDs: []string{"*string:APIKey:12345"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC), + }, + Attributes: []*engine.Attribute{ + &engine.Attribute{ + FieldName: utils.APIMethods, + Initial: utils.META_ANY, + Substitute: "ThresholdSv1.GetThresholdsForEvent", + Append: true, + }, + }, + Weight: 20, + } + var result string + if err := instResRPC.Call("ApierV1.SetAttributeProfile", alsPrf, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } + var reply *engine.AttributeProfile + if err := instResRPC.Call("ApierV1.GetAttributeProfile", + &utils.TenantID{Tenant: "cgrates.org", ID: "AuthKey"}, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(alsPrf, reply) { + t.Errorf("Expecting : %+v, received: %+v", alsPrf, reply) + } +} + +func testDspResTestAuthKey(t *testing.T) { + var rs *engine.Resources + args := &ArgsV1ResUsageWithApiKey{ + APIKey: "12345", + ArgRSv1ResourceUsage: utils.ArgRSv1ResourceUsage{ + CGREvent: utils.CGREvent{ + Tenant: "cgrates.org", + ID: utils.UUIDSha1Prefix(), + Event: map[string]interface{}{ + utils.Account: "1001", + utils.Destination: "1002", + }, + }, + }, + } + + if err := dspResRPC.Call(utils.ResourceSv1GetResourcesForEvent, + args, &rs); err.Error() != utils.ErrUnauthorizedApi.Error() { + t.Error(err) + } +} + +func testDspResAddAttributesWithPermision2(t *testing.T) { + alsPrf := &engine.AttributeProfile{ + Tenant: "cgrates.org", + ID: "AuthKey", + Contexts: []string{utils.MetaAuth}, + FilterIDs: []string{"*string:APIKey:12345"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC), + }, + Attributes: []*engine.Attribute{ + &engine.Attribute{ + FieldName: utils.APIMethods, + Initial: utils.META_ANY, + Substitute: "ThresholdSv1.ProcessEvent;ResourceSv1.GetResourcesForEvent", + Append: true, + }, + }, + Weight: 20, + } + var result string + if err := instResRPC.Call("ApierV1.SetAttributeProfile", alsPrf, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } + var reply *engine.AttributeProfile + if err := instResRPC.Call("ApierV1.GetAttributeProfile", + &utils.TenantID{Tenant: "cgrates.org", ID: "AuthKey"}, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(alsPrf, reply) { + t.Errorf("Expecting : %+v, received: %+v", alsPrf, reply) + } +} + +func testDspResTestAuthKey2(t *testing.T) { + var rs *engine.Resources + args := &ArgsV1ResUsageWithApiKey{ + APIKey: "12345", + ArgRSv1ResourceUsage: utils.ArgRSv1ResourceUsage{ + CGREvent: utils.CGREvent{ + Tenant: "cgrates.org", + ID: utils.UUIDSha1Prefix(), + Event: map[string]interface{}{ + utils.Account: "1001", + utils.Destination: "1002", + }, + }, + }, + } + eRs := &engine.Resources{ + &engine.Resource{ + Tenant: "cgrates.org", + ID: "ResGroup2", + Usages: map[string]*engine.ResourceUsage{}, + }, + } + + if err := dspResRPC.Call(utils.ResourceSv1GetResourcesForEvent, + args, &rs); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eRs, rs) { + t.Errorf("Expecting : %+v, received: %+v", utils.ToJSON(eRs), utils.ToJSON(rs)) + } +} + +func testDspResKillEngine(t *testing.T) { + if err := engine.KillEngine(dspDelay); err != nil { + t.Error(err) + } + if err := engine.KillEngine(dspDelay); err != nil { + t.Error(err) + } +} diff --git a/dispatcher/stats_it_test.go b/dispatcher/stats_it_test.go new file mode 100755 index 000000000..22af5e028 --- /dev/null +++ b/dispatcher/stats_it_test.go @@ -0,0 +1,330 @@ +// +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 dispatcher + +import ( + "net/rpc" + "net/rpc/jsonrpc" + "path" + "reflect" + "testing" + "time" + + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +var ( + dspStsCfgPath string + dspStsCfg *config.CGRConfig + dspStsRPC *rpc.Client + instStsCfgPath string + instStsCfg *config.CGRConfig + instStsRPC *rpc.Client +) + +var sTestsDspSts = []func(t *testing.T){ + testDspStsInitCfg, + testDspStsInitDataDb, + testDspStsResetStorDb, + testDspStsStartEngine, + testDspStsRPCConn, + testDspStsPing, + testDspStsLoadData, + testDspStsAddStsibutesWithPermision, + testDspStsTestAuthKey, + testDspStsAddStsibutesWithPermision2, + testDspStsTestAuthKey2, + testDspStsKillEngine, +} + +//Test start here +func TestDspStatS(t *testing.T) { + for _, stest := range sTestsDspSts { + t.Run("", stest) + } +} + +func testDspStsInitCfg(t *testing.T) { + var err error + dspStsCfgPath = path.Join(dspDataDir, "conf", "samples", "dispatcher") + dspStsCfg, err = config.NewCGRConfigFromFolder(dspStsCfgPath) + if err != nil { + t.Error(err) + } + dspStsCfg.DataFolderPath = dspDataDir // Share DataFolderPath through config towards StoreDb for Flush() + config.SetCgrConfig(dspStsCfg) + instStsCfgPath = path.Join(dspDataDir, "conf", "samples", "tutmysql") + instStsCfg, err = config.NewCGRConfigFromFolder(instStsCfgPath) + if err != nil { + t.Error(err) + } + instStsCfg.DataFolderPath = dspDataDir // Share DataFolderPath through config towards StoreDb for Flush() + config.SetCgrConfig(instStsCfg) +} + +func testDspStsInitDataDb(t *testing.T) { + if err := engine.InitDataDb(instStsCfg); err != nil { + t.Fatal(err) + } +} + +// Wipe out the cdr database +func testDspStsResetStorDb(t *testing.T) { + if err := engine.InitStorDb(instStsCfg); err != nil { + t.Fatal(err) + } +} + +// Start CGR Engine +func testDspStsStartEngine(t *testing.T) { + if _, err := engine.StartEngine(instStsCfgPath, dspDelay); err != nil { + t.Fatal(err) + } + if _, err := engine.StartEngine(dspStsCfgPath, dspDelay); err != nil { + t.Fatal(err) + } +} + +// Connect rpc client to rater +func testDspStsRPCConn(t *testing.T) { + var err error + instStsRPC, err = jsonrpc.Dial("tcp", instStsCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal(err) + } + dspStsRPC, err = jsonrpc.Dial("tcp", dspStsCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal(err) + } + +} + +func testDspStsPing(t *testing.T) { + var reply string + if err := instStsRPC.Call(utils.StatSv1Ping, "", &reply); err != nil { + t.Error(err) + } else if reply != utils.Pong { + t.Errorf("Received: %s", reply) + } + if err := dspStsRPC.Call(utils.StatSv1Ping, "", &reply); err != nil { + t.Error(err) + } else if reply != utils.Pong { + t.Errorf("Received: %s", reply) + } +} + +func testDspStsLoadData(t *testing.T) { + var reply string + Stss := &utils.AttrLoadTpFromFolder{ + FolderPath: path.Join(dspDataDir, "tariffplans", "tutorial2")} + if err := instStsRPC.Call("ApierV1.LoadTariffPlanFromFolder", Stss, &reply); err != nil { + t.Error(err) + } + time.Sleep(500 * time.Millisecond) +} + +func testDspStsAddStsibutesWithPermision(t *testing.T) { + alsPrf := &engine.AttributeProfile{ + Tenant: "cgrates.org", + ID: "AuthKey", + Contexts: []string{utils.MetaAuth}, + FilterIDs: []string{"*string:APIKey:12345"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC), + }, + Attributes: []*engine.Attribute{ + &engine.Attribute{ + FieldName: utils.APIMethods, + Initial: utils.META_ANY, + Substitute: "ThresholdSv1.GetThAttrholdsForEvent", + Append: true, + }, + }, + Weight: 20, + } + var Attrult string + if err := instStsRPC.Call("ApierV1.SetAttributeProfile", alsPrf, &Attrult); err != nil { + t.Error(err) + } else if Attrult != utils.OK { + t.Error("Unexpected reply returned", Attrult) + } + var reply *engine.AttributeProfile + if err := instStsRPC.Call("ApierV1.GetAttributeProfile", + &utils.TenantID{Tenant: "cgrates.org", ID: "AuthKey"}, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(alsPrf, reply) { + t.Errorf("Expecting : %+v, received: %+v", alsPrf, reply) + } +} + +func testDspStsTestAuthKey(t *testing.T) { + var reply []string + args := CGREvWithApiKey{ + APIKey: "12345", + CGREvent: utils.CGREvent{ + Tenant: "cgrates.org", + ID: "event1", + Event: map[string]interface{}{ + utils.Account: "1001", + utils.AnswerTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + utils.Usage: time.Duration(135 * time.Second), + utils.COST: 123.0, + utils.PDD: time.Duration(12 * time.Second)}}, + } + if err := dspStsRPC.Call(utils.StatSv1ProcessEvent, + args, &reply); err.Error() != utils.ErrUnauthorizedApi.Error() { + t.Error(err) + } + + args2 := TntIDWithApiKey{ + APIKey: "12345", + TenantID: utils.TenantID{ + Tenant: "cgrates.org", + ID: "Stats2", + }, + } + + var metrics map[string]string + if err := dspStsRPC.Call(utils.StatSv1GetQueueStringMetrics, + args2, &metrics); err.Error() != utils.ErrUnauthorizedApi.Error() { + t.Error(err) + } +} + +func testDspStsAddStsibutesWithPermision2(t *testing.T) { + alsPrf := &engine.AttributeProfile{ + Tenant: "cgrates.org", + ID: "AuthKey", + Contexts: []string{utils.MetaAuth}, + FilterIDs: []string{"*string:APIKey:12345"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC), + }, + Attributes: []*engine.Attribute{ + &engine.Attribute{ + FieldName: utils.APIMethods, + Initial: utils.META_ANY, + Substitute: "StatSv1.ProcessEvent;StatSv1.GetQueueStringMetrics", + Append: true, + }, + }, + Weight: 20, + } + var Attrult string + if err := instStsRPC.Call("ApierV1.SetAttributeProfile", alsPrf, &Attrult); err != nil { + t.Error(err) + } else if Attrult != utils.OK { + t.Error("Unexpected reply returned", Attrult) + } + var reply *engine.AttributeProfile + if err := instStsRPC.Call("ApierV1.GetAttributeProfile", + &utils.TenantID{Tenant: "cgrates.org", ID: "AuthKey"}, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(alsPrf, reply) { + t.Errorf("Expecting : %+v, received: %+v", alsPrf, reply) + } +} + +func testDspStsTestAuthKey2(t *testing.T) { + var reply []string + var metrics map[string]string + expected := []string{"Stats2"} + args := CGREvWithApiKey{ + APIKey: "12345", + CGREvent: utils.CGREvent{ + Tenant: "cgrates.org", + ID: "event1", + Event: map[string]interface{}{ + utils.Account: "1001", + utils.AnswerTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + utils.Usage: time.Duration(135 * time.Second), + utils.COST: 123.0, + utils.RunID: utils.DEFAULT_RUNID, + }, + }, + } + if err := dspStsRPC.Call(utils.StatSv1ProcessEvent, args, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(reply, expected) { + t.Errorf("Expecting: %+v, received: %+v", expected, reply) + } + + args2 := TntIDWithApiKey{ + APIKey: "12345", + TenantID: utils.TenantID{ + Tenant: "cgrates.org", + ID: "Stats2", + }, + } + expectedMetrics := map[string]string{ + utils.MetaTCC: "123", + utils.MetaTCD: "2m15s", + } + + if err := dspStsRPC.Call(utils.StatSv1GetQueueStringMetrics, + args2, &metrics); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(expectedMetrics, metrics) { + t.Errorf("expecting: %+v, received reply: %s", expectedMetrics, metrics) + } + + args = CGREvWithApiKey{ + APIKey: "12345", + CGREvent: utils.CGREvent{ + Tenant: "cgrates.org", + ID: "event1", + Event: map[string]interface{}{ + utils.Account: "1002", + utils.AnswerTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + utils.Usage: time.Duration(45 * time.Second), + utils.RunID: utils.DEFAULT_RUNID, + utils.COST: 10.0, + }, + }, + } + if err := dspStsRPC.Call(utils.StatSv1ProcessEvent, args, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(reply, expected) { + t.Errorf("Expecting: %+v, received: %+v", expected, reply) + } + + expectedMetrics = map[string]string{ + utils.MetaTCC: "133", + utils.MetaTCD: "3m0s", + } + if err := dspStsRPC.Call(utils.StatSv1GetQueueStringMetrics, + args2, &metrics); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(expectedMetrics, metrics) { + t.Errorf("expecting: %+v, received reply: %s", expectedMetrics, metrics) + } +} + +func testDspStsKillEngine(t *testing.T) { + if err := engine.KillEngine(dspDelay); err != nil { + t.Error(err) + } + if err := engine.KillEngine(dspDelay); err != nil { + t.Error(err) + } +} diff --git a/dispatcher/suppliers_it_test.go b/dispatcher/suppliers_it_test.go new file mode 100755 index 000000000..991c3314e --- /dev/null +++ b/dispatcher/suppliers_it_test.go @@ -0,0 +1,297 @@ +// +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 dispatcher + +import ( + "net/rpc" + "net/rpc/jsonrpc" + "path" + "reflect" + "testing" + "time" + + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +var ( + dspSupCfgPath string + dspSupCfg *config.CGRConfig + dspSupRPC *rpc.Client + instSupCfgPath string + instSupCfg *config.CGRConfig + instSupRPC *rpc.Client +) + +var sTestsDspSup = []func(t *testing.T){ + testDspSupInitCfg, + testDspSupInitDataDb, + testDspSupResetStorDb, + testDspSupStartEngine, + testDspSupRPCConn, + testDspSupPing, + testDspSupLoadData, + testDspSupAddAttributesWithPermision, + testDspSupTestAuthKey, + testDspSupAddAttributesWithPermision2, + testDspSupTestAuthKey2, + testDspSupKillEngine, +} + +//Test start here +func TestDspSupplierS(t *testing.T) { + for _, stest := range sTestsDspSup { + t.Run("", stest) + } +} + +func testDspSupInitCfg(t *testing.T) { + var err error + dspSupCfgPath = path.Join(dspDataDir, "conf", "samples", "dispatcher") + dspSupCfg, err = config.NewCGRConfigFromFolder(dspSupCfgPath) + if err != nil { + t.Error(err) + } + dspSupCfg.DataFolderPath = dspDataDir // Share DataFolderPath through config towards StoreDb for Flush() + config.SetCgrConfig(dspSupCfg) + instSupCfgPath = path.Join(dspDataDir, "conf", "samples", "tutmysql") + instSupCfg, err = config.NewCGRConfigFromFolder(instSupCfgPath) + if err != nil { + t.Error(err) + } + instSupCfg.DataFolderPath = dspDataDir // Share DataFolderPath through config towards StoreDb for Flush() + config.SetCgrConfig(instSupCfg) +} + +func testDspSupInitDataDb(t *testing.T) { + if err := engine.InitDataDb(instSupCfg); err != nil { + t.Fatal(err) + } +} + +// Wipe out the cdr database +func testDspSupResetStorDb(t *testing.T) { + if err := engine.InitStorDb(instSupCfg); err != nil { + t.Fatal(err) + } +} + +// Start CGR Engine +func testDspSupStartEngine(t *testing.T) { + if _, err := engine.StartEngine(instSupCfgPath, dspDelay); err != nil { + t.Fatal(err) + } + if _, err := engine.StartEngine(dspSupCfgPath, dspDelay); err != nil { + t.Fatal(err) + } +} + +// Connect rpc client to rater +func testDspSupRPCConn(t *testing.T) { + var err error + instSupRPC, err = jsonrpc.Dial("tcp", instSupCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal(err) + } + dspSupRPC, err = jsonrpc.Dial("tcp", dspSupCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal(err) + } + +} + +func testDspSupPing(t *testing.T) { + var reply string + if err := instSupRPC.Call(utils.SupplierSv1Ping, "", &reply); err != nil { + t.Error(err) + } else if reply != utils.Pong { + t.Errorf("Received: %s", reply) + } + if err := dspSupRPC.Call(utils.SupplierSv1Ping, "", &reply); err != nil { + t.Error(err) + } else if reply != utils.Pong { + t.Errorf("Received: %s", reply) + } +} + +func testDspSupLoadData(t *testing.T) { + var reply string + attrs := &utils.AttrLoadTpFromFolder{ + FolderPath: path.Join(dspDataDir, "tariffplans", "tutorial2")} + if err := instSupRPC.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil { + t.Error(err) + } + time.Sleep(500 * time.Millisecond) +} + +func testDspSupAddAttributesWithPermision(t *testing.T) { + alsPrf := &engine.AttributeProfile{ + Tenant: "cgrates.org", + ID: "AuthKey", + Contexts: []string{utils.MetaAuth}, + FilterIDs: []string{"*string:APIKey:12345"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC), + }, + Attributes: []*engine.Attribute{ + &engine.Attribute{ + FieldName: utils.APIMethods, + Initial: utils.META_ANY, + Substitute: "ThresholdSv1.GetThresholdsForEvent", + Append: true, + }, + }, + Weight: 20, + } + var result string + if err := instSupRPC.Call("ApierV1.SetAttributeProfile", alsPrf, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } + var reply *engine.AttributeProfile + if err := instSupRPC.Call("ApierV1.GetAttributeProfile", + &utils.TenantID{Tenant: "cgrates.org", ID: "AuthKey"}, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(alsPrf, reply) { + t.Errorf("Expecting : %+v, received: %+v", alsPrf, reply) + } +} + +func testDspSupTestAuthKey(t *testing.T) { + var rpl *engine.SortedSuppliers + args := &ArgsGetSuppliersWithApiKey{ + APIKey: "12345", + ArgsGetSuppliers: engine.ArgsGetSuppliers{ + CGREvent: utils.CGREvent{ + Tenant: "cgrates.org", + ID: utils.UUIDSha1Prefix(), + Time: &nowTime, + Event: map[string]interface{}{ + utils.Account: "1002", + utils.Subject: "1002", + utils.Destination: "1001", + utils.SetupTime: time.Date(2017, 12, 1, 14, 25, 0, 0, time.UTC), + utils.Usage: "1m20s", + }, + }, + }, + } + if err := dspSupRPC.Call(utils.SupplierSv1GetSuppliers, + args, &rpl); err.Error() != utils.ErrUnauthorizedApi.Error() { + t.Error(err) + } +} + +func testDspSupAddAttributesWithPermision2(t *testing.T) { + alsPrf := &engine.AttributeProfile{ + Tenant: "cgrates.org", + ID: "AuthKey", + Contexts: []string{utils.MetaAuth}, + FilterIDs: []string{"*string:APIKey:12345"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC), + }, + Attributes: []*engine.Attribute{ + &engine.Attribute{ + FieldName: utils.APIMethods, + Initial: utils.META_ANY, + Substitute: "ThresholdSv1.ProcessEvent;SupplierSv1.GetSuppliers", + Append: true, + }, + }, + Weight: 20, + } + var result string + if err := instSupRPC.Call("ApierV1.SetAttributeProfile", alsPrf, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } + var reply *engine.AttributeProfile + if err := instSupRPC.Call("ApierV1.GetAttributeProfile", + &utils.TenantID{Tenant: "cgrates.org", ID: "AuthKey"}, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(alsPrf, reply) { + t.Errorf("Expecting : %+v, received: %+v", alsPrf, reply) + } +} + +func testDspSupTestAuthKey2(t *testing.T) { + var rpl *engine.SortedSuppliers + eRpl := &engine.SortedSuppliers{ + ProfileID: "SPL_LEASTCOST_1", + Sorting: utils.MetaLeastCost, + SortedSuppliers: []*engine.SortedSupplier{ + &engine.SortedSupplier{ + SupplierID: "supplier2", + SupplierParameters: "", + SortingData: map[string]interface{}{ + utils.Cost: 0.1166, + utils.RatingPlanID: "RP_1002_LOW", + utils.Weight: 10.0, + }, + }, + &engine.SortedSupplier{ + SupplierID: "supplier1", + SupplierParameters: "", + SortingData: map[string]interface{}{ + utils.Cost: 0.2334, + utils.RatingPlanID: "RP_1002", + utils.Weight: 10.0, + }, + }, + }, + } + args := &ArgsGetSuppliersWithApiKey{ + APIKey: "12345", + ArgsGetSuppliers: engine.ArgsGetSuppliers{ + CGREvent: utils.CGREvent{ + Tenant: "cgrates.org", + ID: utils.UUIDSha1Prefix(), + Time: &nowTime, + Event: map[string]interface{}{ + utils.Account: "1002", + utils.Subject: "1002", + utils.Destination: "1001", + utils.SetupTime: time.Date(2017, 12, 1, 14, 25, 0, 0, time.UTC), + utils.Usage: "1m20s", + }, + }, + }, + } + if err := dspSupRPC.Call(utils.SupplierSv1GetSuppliers, + args, &rpl); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eRpl, rpl) { + t.Errorf("Expecting : %+v, received: %+v", utils.ToJSON(eRpl), utils.ToJSON(rpl)) + } +} + +func testDspSupKillEngine(t *testing.T) { + if err := engine.KillEngine(dspDelay); err != nil { + t.Error(err) + } + if err := engine.KillEngine(dspDelay); err != nil { + t.Error(err) + } +} diff --git a/dispatcher/thresholds.go b/dispatcher/thresholds.go index 9c587b3cc..81beb5d72 100755 --- a/dispatcher/thresholds.go +++ b/dispatcher/thresholds.go @@ -20,6 +20,7 @@ package dispatcher import ( "fmt" + "time" "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" @@ -35,38 +36,8 @@ func (dS *DispatcherService) ThresholdSv1Ping(ign string, reply *string) error { return nil } -/* To be implemented (+console) -func (dS *DispatcherService) ThresholdSv1GetThresholdIDs(tenant string, tIDs *[]string) (err error) { - if dS.thdS == nil { - return utils.NewErrNotConnected(utils.ThresholdS) - } - ev := &utils.CGREvent{ - Tenant: args.Tenant, - ID: utils.UUIDSha1Prefix(), - Context: utils.StringPointer(utils.MetaAuth), - Time: args.ArgRSv1ResourceUsage.Time, - Event: map[string]interface{}{ - utils.APIKey: args.APIKey, - }, - } - var rplyEv engine.AttrSProcessEventReply - if err = dS.authorizeEvent(ev, &rplyEv); err != nil { - return - } - mp := utils.ParseStringMap(rplyEv.CGREvent.Event[utils.APIMethods]) - if !mp.HasKey(utils.ResourceSv1GetResourcesForEvent) { - return utils.ErrUnauthorizedApi - } - if err = dS.thdS.Call(utils.ThresholdSv1GetThresholdIDs, tenant, tIDs); err != nil { - utils.Logger.Warning( - fmt.Sprintf(" error: %s ThresholdS.", err.Error())) - } - return -} -*/ - -func (dS *DispatcherService) ThresholdSv1GetThresholdForEvent(args *ArgsProcessEventWithApiKey, - t *engine.Threshold) (err error) { +func (dS *DispatcherService) ThresholdSv1GetThresholdsForEvent(args *ArgsProcessEventWithApiKey, + t *engine.Thresholds) (err error) { if dS.thdS == nil { return utils.NewErrNotConnected(utils.ThresholdS) } @@ -74,6 +45,7 @@ func (dS *DispatcherService) ThresholdSv1GetThresholdForEvent(args *ArgsProcessE Tenant: args.Tenant, ID: utils.UUIDSha1Prefix(), Context: utils.StringPointer(utils.MetaAuth), + Time: utils.TimePointer(time.Now()), Event: map[string]interface{}{ utils.APIKey: args.APIKey, }, @@ -89,7 +61,7 @@ func (dS *DispatcherService) ThresholdSv1GetThresholdForEvent(args *ArgsProcessE if !utils.ParseStringMap(apiMethods).HasKey(utils.ThresholdSv1GetThresholdsForEvent) { return utils.ErrUnauthorizedApi } - return dS.thdS.Call(utils.ThresholdSv1GetThresholdsForEvent, args.TenantID, t) + return dS.thdS.Call(utils.ThresholdSv1GetThresholdsForEvent, args.ArgsProcessEvent, t) } func (dS *DispatcherService) ThresholdSv1ProcessEvent(args *ArgsProcessEventWithApiKey, @@ -101,6 +73,7 @@ func (dS *DispatcherService) ThresholdSv1ProcessEvent(args *ArgsProcessEventWith Tenant: args.Tenant, ID: utils.UUIDSha1Prefix(), Context: utils.StringPointer(utils.MetaAuth), + Time: utils.TimePointer(time.Now()), Event: map[string]interface{}{ utils.APIKey: args.APIKey, }, @@ -117,5 +90,4 @@ func (dS *DispatcherService) ThresholdSv1ProcessEvent(args *ArgsProcessEventWith return utils.ErrUnauthorizedApi } return dS.thdS.Call(utils.ThresholdSv1ProcessEvent, args.ArgsProcessEvent, tIDs) - } diff --git a/dispatcher/thresholds_it_test.go b/dispatcher/thresholds_it_test.go new file mode 100755 index 000000000..8b80a74c8 --- /dev/null +++ b/dispatcher/thresholds_it_test.go @@ -0,0 +1,296 @@ +// +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 dispatcher + +import ( + "net/rpc" + "net/rpc/jsonrpc" + "path" + "reflect" + "testing" + "time" + + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +var ( + dspThCfgPath string + dspThCfg *config.CGRConfig + dspThRPC *rpc.Client + instThCfgPath string + instThCfg *config.CGRConfig + instThRPC *rpc.Client +) + +var sTestsDspTh = []func(t *testing.T){ + testDspThInitCfg, + testDspThInitDataDb, + testDspThResetStorDb, + testDspThStartEngine, + testDspThRPCConn, + testDspThPing, + testDspThLoadData, + testDspThAddAttributesWithPermision, + testDspThTestAuthKey, + testDspThAddAttributesWithPermision2, + testDspThTestAuthKey2, + testDspThKillEngine, +} + +//Test start here +func TestDspThresholdS(t *testing.T) { + for _, stest := range sTestsDspTh { + t.Run("", stest) + } +} + +func testDspThInitCfg(t *testing.T) { + var err error + dspThCfgPath = path.Join(dspDataDir, "conf", "samples", "dispatcher") + dspThCfg, err = config.NewCGRConfigFromFolder(dspThCfgPath) + if err != nil { + t.Error(err) + } + dspThCfg.DataFolderPath = dspDataDir // Share DataFolderPath through config towards StoreDb for Flush() + config.SetCgrConfig(dspThCfg) + instThCfgPath = path.Join(dspDataDir, "conf", "samples", "tutmysql") + instThCfg, err = config.NewCGRConfigFromFolder(instThCfgPath) + if err != nil { + t.Error(err) + } + instThCfg.DataFolderPath = dspDataDir // Share DataFolderPath through config towards StoreDb for Flush() + config.SetCgrConfig(instThCfg) +} + +func testDspThInitDataDb(t *testing.T) { + if err := engine.InitDataDb(instThCfg); err != nil { + t.Fatal(err) + } +} + +// Wipe out the cdr database +func testDspThResetStorDb(t *testing.T) { + if err := engine.InitStorDb(instThCfg); err != nil { + t.Fatal(err) + } +} + +// Start CGR Engine +func testDspThStartEngine(t *testing.T) { + if _, err := engine.StartEngine(instThCfgPath, dspDelay); err != nil { + t.Fatal(err) + } + if _, err := engine.StartEngine(dspThCfgPath, dspDelay); err != nil { + t.Fatal(err) + } +} + +// Connect rpc client to rater +func testDspThRPCConn(t *testing.T) { + var err error + instThRPC, err = jsonrpc.Dial("tcp", instThCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal(err) + } + dspThRPC, err = jsonrpc.Dial("tcp", dspThCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal(err) + } +} + +func testDspThPing(t *testing.T) { + var reply string + if err := instThRPC.Call(utils.ThresholdSv1Ping, "", &reply); err != nil { + t.Error(err) + } else if reply != utils.Pong { + t.Errorf("Received: %s", reply) + } + if err := dspThRPC.Call(utils.ThresholdSv1Ping, "", &reply); err != nil { + t.Error(err) + } else if reply != utils.Pong { + t.Errorf("Received: %s", reply) + } +} + +func testDspThLoadData(t *testing.T) { + var reply string + attrs := &utils.AttrLoadTpFromFolder{ + FolderPath: path.Join(dspDataDir, "tariffplans", "tutorial2")} + if err := instThRPC.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil { + t.Error(err) + } + time.Sleep(500 * time.Millisecond) +} + +func testDspThAddAttributesWithPermision(t *testing.T) { + alsPrf := &engine.AttributeProfile{ + Tenant: "cgrates.org", + ID: "AuthKey", + Contexts: []string{utils.MetaAuth}, + FilterIDs: []string{"*string:APIKey:12345"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC), + }, + Attributes: []*engine.Attribute{ + &engine.Attribute{ + FieldName: utils.APIMethods, + Initial: utils.META_ANY, + Substitute: "ThresholdSv1.GetThresholdsForEvent", + Append: true, + }, + }, + Weight: 20, + } + var result string + if err := instThRPC.Call("ApierV1.SetAttributeProfile", alsPrf, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } + var reply *engine.AttributeProfile + if err := instThRPC.Call("ApierV1.GetAttributeProfile", + &utils.TenantID{Tenant: "cgrates.org", ID: "AuthKey"}, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(alsPrf, reply) { + t.Errorf("Expecting : %+v, received: %+v", alsPrf, reply) + } +} + +func testDspThTestAuthKey(t *testing.T) { + var ids []string + nowTime := time.Now() + args := &ArgsProcessEventWithApiKey{ + APIKey: "12345", + ArgsProcessEvent: engine.ArgsProcessEvent{ + CGREvent: utils.CGREvent{ + Tenant: "cgrates.org", + ID: utils.UUIDSha1Prefix(), + Time: &nowTime, + Event: map[string]interface{}{ + utils.Account: "1002"}, + }, + }, + } + + if err := dspThRPC.Call(utils.ThresholdSv1ProcessEvent, + args, &ids); err.Error() != utils.ErrUnauthorizedApi.Error() { + t.Error(err) + } + var th *engine.Thresholds + eTh := &engine.Thresholds{ + &engine.Threshold{ + Tenant: "cgrates.org", + ID: "THD_ACNT_1002", + Hits: 0, + }, + } + if err := dspThRPC.Call(utils.ThresholdSv1GetThresholdsForEvent, args, &th); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eTh, th) { + t.Errorf("expecting: %+v, received: %+v", eTh, th) + } +} + +func testDspThAddAttributesWithPermision2(t *testing.T) { + alsPrf := &engine.AttributeProfile{ + Tenant: "cgrates.org", + ID: "AuthKey", + Contexts: []string{utils.MetaAuth}, + FilterIDs: []string{"*string:APIKey:12345"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC), + }, + Attributes: []*engine.Attribute{ + &engine.Attribute{ + FieldName: utils.APIMethods, + Initial: utils.META_ANY, + Substitute: "ThresholdSv1.ProcessEvent;ThresholdSv1.GetThresholdsForEvent", + Append: true, + }, + }, + Weight: 20, + } + var result string + if err := instThRPC.Call("ApierV1.SetAttributeProfile", alsPrf, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } + var reply *engine.AttributeProfile + if err := instThRPC.Call("ApierV1.GetAttributeProfile", + &utils.TenantID{Tenant: "cgrates.org", ID: "AuthKey"}, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(alsPrf, reply) { + t.Errorf("Expecting : %+v, received: %+v", alsPrf, reply) + } +} + +func testDspThTestAuthKey2(t *testing.T) { + var ids []string + eIDs := []string{"THD_ACNT_1002"} + nowTime := time.Now() + args := &ArgsProcessEventWithApiKey{ + APIKey: "12345", + ArgsProcessEvent: engine.ArgsProcessEvent{ + CGREvent: utils.CGREvent{ + Tenant: "cgrates.org", + ID: utils.UUIDSha1Prefix(), + Time: &nowTime, + Event: map[string]interface{}{ + utils.Account: "1002"}, + }, + }, + } + + if err := dspThRPC.Call(utils.ThresholdSv1ProcessEvent, args, &ids); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eIDs, ids) { + t.Errorf("expecting: %+v, received: %+v", eIDs, ids) + } + var th *engine.Thresholds + eTh := &engine.Thresholds{ + &engine.Threshold{ + Tenant: "cgrates.org", + ID: "THD_ACNT_1002", + Hits: 1, + }, + } + if err := dspThRPC.Call(utils.ThresholdSv1GetThresholdsForEvent, args, &th); err != nil { + t.Error(err) + } else if !reflect.DeepEqual((*eTh)[0].Tenant, (*th)[0].Tenant) { + t.Errorf("expecting: %+v, received: %+v", (*eTh)[0].Tenant, (*th)[0].Tenant) + } else if !reflect.DeepEqual((*eTh)[0].ID, (*th)[0].ID) { + t.Errorf("expecting: %+v, received: %+v", (*eTh)[0].ID, (*th)[0].ID) + } else if !reflect.DeepEqual((*eTh)[0].Hits, (*th)[0].Hits) { + t.Errorf("expecting: %+v, received: %+v", (*eTh)[0].Hits, (*th)[0].Hits) + } +} + +func testDspThKillEngine(t *testing.T) { + if err := engine.KillEngine(dspDelay); err != nil { + t.Error(err) + } + if err := engine.KillEngine(dspDelay); err != nil { + t.Error(err) + } +} diff --git a/dispatcher/utils.go b/dispatcher/utils.go index 869b5b861..657f1fadc 100755 --- a/dispatcher/utils.go +++ b/dispatcher/utils.go @@ -19,11 +19,19 @@ along with this program. If not, see package dispatcher import ( + "time" + "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/sessions" "github.com/cgrates/cgrates/utils" ) +var ( //var used in all tests + dspDelay = 1000 + dspDataDir = "/usr/share/cgrates" + nowTime = time.Now() +) + type CGREvWithApiKey struct { APIKey string utils.CGREvent