From b38eb22c4642965052818598f728dadcc069efc7 Mon Sep 17 00:00:00 2001 From: DanB Date: Mon, 11 Mar 2019 20:18:42 +0100 Subject: [PATCH] Complete set of cost simulation for tutorial2 --- data/conf/samples/tutmongo2/cgrates.json | 166 +++++++++++++++ engine/responder.go | 6 + general_tests/tutorial2_it_test.go | 252 +++++++++++++++++++++++ utils/consts.go | 20 +- 4 files changed, 435 insertions(+), 9 deletions(-) create mode 100644 data/conf/samples/tutmongo2/cgrates.json create mode 100644 general_tests/tutorial2_it_test.go diff --git a/data/conf/samples/tutmongo2/cgrates.json b/data/conf/samples/tutmongo2/cgrates.json new file mode 100644 index 000000000..c756d051e --- /dev/null +++ b/data/conf/samples/tutmongo2/cgrates.json @@ -0,0 +1,166 @@ +{ +// CGRateS Configuration file +// + + +"general": { + "node_id": "CGRateSTutorial", + "log_level": 7 +}, + + +"listen": { + "rpc_json": ":2012", + "rpc_gob": ":2013", + "http": ":2080", +}, + + +"data_db": { + "db_type": "mongo", + "db_port": 27017, +}, + + +"stor_db": { + "db_type": "mongo", + "db_port": 27017, +}, + + + +"scheduler": { + "enabled": true, + "cdrs_conns": [ + {"address": "127.0.0.1:2012", "transport":"*json"}, + ], +}, + + +"rals": { + "enabled": true, + "thresholds_conns": [ + {"address": "127.0.0.1:2012", "transport":"*json"}, + ], + "stats_conns": [ + {"address": "127.0.0.1:2012", "transport":"*json"}, + ], +}, + + +"cdrs": { + "enabled": true, + "extra_fields": ["PayPalAccount", "LCRProfile", "ResourceID"], + "chargers_conns":[ + {"address": "127.0.0.1:2012", "transport":"*json"}, + ], + "store_cdrs": true, + "online_cdr_exports": [] +}, + + +"cdre": { + "amqp_localhost": { + "export_format": "*amqp_json_map", + "export_path": "amqp://guest:guest@localhost:5672/?queue_id=cgrates_cdrs", + "content_fields": [ + {"field_id": "CGRID", "type": "*variable", "value": "~CGRID"}, + {"field_id": "RunID", "type": "*variable", "value": "~RunID"}, + {"field_id": "ToR", "type": "*variable", "value": "~ToR"}, + {"field_id": "OriginID", "type": "*variable", "value": "~OriginID"}, + {"field_id": "OriginHost", "type": "*variable", "value": "~OriginHost"}, + {"field_id": "RequestType", "type": "*variable", "value": "~RequestType", }, + {"field_id": "Tenant", "type": "*variable", "value": "~Tenant"}, + {"field_id": "Category", "type": "*variable", "value": "~Category"}, + {"field_id": "Account", "type": "*variable", "value": "~Account"}, + {"field_id": "Destination", "type": "*variable", "value": "~Destination"}, + {"field_id": "SetupTime", "type": "*variable", "value": "~SetupTime"}, + {"field_id": "AnswerTime", "type": "*variable", "value": "~AnswerTime"}, + {"field_id": "Usage", "type": "*variable", "value": "~Usage"}, + {"field_id": "Cost", "type": "*variable", "value": "~Cost"}, + {"field_id": "PayPalAccount", "type": "*variable", "value": "~PayPalAccount"}, + {"field_id": "LCRProfile", "type": "*variable", "value": "~LCRProfile", }, + {"field_id": "ResourceID", "type": "*variable", "value": "~ResourceID", }, + ], + }, +}, + + +"cdrc": [ + { + "id": "tutorial_csv_cdr", + "enabled": true, + "cdr_source_id": "cgr_tutorial", + "content_fields":[ + {"field_id": "OriginID", "type": "*composed", "value": "~3", "mandatory": true}, + {"field_id": "Account", "type": "*composed", "value": "~8", "mandatory": true}, + {"field_id": "Destination", "type": "*composed", "value": "~10", "mandatory": true}, + {"field_id": "SetupTime", "type": "*composed", "value": "~11", "mandatory": true}, + {"field_id": "AnswerTime", "type": "*composed", "value": "~12", "mandatory": true}, + {"field_id": "Usage", "type": "*composed", "value": "~13", "mandatory": true}, + ], + }, +], + + +"sessions": { + "enabled": true, + "resources_conns": [ + {"address": "127.0.0.1:2012", "transport":"*json"} + ], + "suppliers_conns": [ + {"address": "127.0.0.1:2012", "transport":"*json"} + ], + "attributes_conns": [ + {"address": "127.0.0.1:2012", "transport":"*json"} + ], +}, + + +"attributes": { + "enabled": true, + "string_indexed_fields": ["Account"] +}, + + +"chargers": { + "enabled": true, + "attributes_conns": [ + {"address": "127.0.0.1:2012", "transport":"*json"} + ], + "string_indexed_fields": ["Account"] +}, + + +"resources": { + "enabled": true, + "thresholds_conns": [ + {"address": "127.0.0.1:2012", "transport":"*json"} + ], + "string_indexed_fields": ["Account"] +}, + + +"stats": { + "enabled": true, + "thresholds_conns": [ + {"address": "127.0.0.1:2012", "transport":"*json"} + ], + "string_indexed_fields": ["Account"] +}, + + +"thresholds": { + "enabled": true, + "string_indexed_fields": ["Account"] +}, + + +"suppliers": { + "enabled": true, + "string_indexed_fields": ["LCRProfile"], + "prefix_indexed_fields":["Destination"], +}, + + +} diff --git a/engine/responder.go b/engine/responder.go index 1b74b30f0..e4e589393 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -61,6 +61,12 @@ func (rs *Responder) usageAllowed(tor string, reqUsage time.Duration) (allowed b RPC method thet provides the external RPC interface for getting the rating information. */ func (rs *Responder) GetCost(arg *CallDescriptor, reply *CallCost) (err error) { + if arg.Tenant == "" { + arg.Tenant = config.CgrConfig().GeneralCfg().DefaultTenant + } + if arg.Category == "" { + arg.Category = config.CgrConfig().GeneralCfg().DefaultCategory + } if arg.Subject == "" { arg.Subject = arg.Account } diff --git a/general_tests/tutorial2_it_test.go b/general_tests/tutorial2_it_test.go new file mode 100644 index 000000000..827f9ee35 --- /dev/null +++ b/general_tests/tutorial2_it_test.go @@ -0,0 +1,252 @@ +// +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 ( + "net/rpc" + "net/rpc/jsonrpc" + "path" + "testing" + "time" + + v1 "github.com/cgrates/cgrates/apier/v1" + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +var ( + tutCfgPath string + tutCfg *config.CGRConfig + tutRpc *rpc.Client + tutCfgDir string //run tests for specific configuration + tutDelay int +) + +var sTutTests = []func(t *testing.T){ + testTutLoadConfig, + testTutResetDB, + testTutStartEngine, + testTutRpcConn, + testTutFromFolder, + testTutGetCost, + testTutStopEngine, +} + +//Test start here +func TestTutorial2MySQL(t *testing.T) { + tutCfgDir = "tutmysql2" + for _, stest := range sTutTests { + t.Run(tutCfgDir, stest) + } +} + +func TestTutorial2Mongo(t *testing.T) { + tutCfgDir = "tutmongo2" + for _, stest := range sTutTests { + t.Run(tutCfgDir, stest) + } +} + +func testTutLoadConfig(t *testing.T) { + var err error + tutCfgPath = path.Join(*dataDir, "conf", "samples", tutCfgDir) + if tutCfg, err = config.NewCGRConfigFromFolder(tutCfgPath); err != nil { + t.Error(err) + } + switch tutCfgDir { + default: + tutDelay = 2000 + } +} + +func testTutResetDB(t *testing.T) { + if err := engine.InitDataDb(tutCfg); err != nil { + t.Fatal(err) + } + if err := engine.InitStorDb(tutCfg); err != nil { + t.Fatal(err) + } +} + +func testTutStartEngine(t *testing.T) { + if _, err := engine.StopStartEngine(tutCfgPath, tutDelay); err != nil { + t.Fatal(err) + } +} + +func testTutRpcConn(t *testing.T) { + var err error + if tutRpc, err = jsonrpc.Dial("tcp", tutCfg.ListenCfg().RPCJSONListen); err != nil { + t.Fatal("could not connect to rater: ", err.Error()) + } +} + +func testTutFromFolder(t *testing.T) { + var reply string + attrs := &utils.AttrLoadTpFromFolder{ + FolderPath: path.Join(*dataDir, "tariffplans", "tutorial2")} + if err := tutRpc.Call(utils.ApierV1LoadTariffPlanFromFolder, + attrs, &reply); err != nil { + t.Error(err) + } + time.Sleep(500 * time.Millisecond) +} + +func testTutGetCost(t *testing.T) { + // Standard pricing for 1001->1002 + attrs := v1.AttrGetCost{ + Subject: "1001", + Destination: "1002", + AnswerTime: "*now", + Usage: "45s", + } + var rply *engine.EventCost + if err := tutRpc.Call(utils.ApierV1GetCost, attrs, &rply); err != nil { + t.Error("Unexpected nil error received: ", err.Error()) + } else if *rply.Cost != 0.45 { // FixMe: missing ConnectFee out of Cost + t.Errorf("Unexpected cost received: %f", *rply.Cost) + } + // Fallback pricing from *any, Usage will be rounded to 60s + attrs = v1.AttrGetCost{ + Subject: "1001", + Destination: "1003", + AnswerTime: "2019-03-11T09:00:00Z", + Usage: "45s", + } + if err := tutRpc.Call(utils.ApierV1GetCost, attrs, &rply); err != nil { + t.Error("Unexpected nil error received: ", err.Error()) + } else if *rply.Cost != 1.2 { // FixMe: missing ConnectFee out of Cost + t.Errorf("Unexpected cost received: %f", *rply.Cost) + } + // Fallback pricing from *any, Usage will be rounded to 60s + attrs = v1.AttrGetCost{ + Subject: "1001", + Destination: "1003", + AnswerTime: "2019-03-11T21:00:00Z", + Usage: "45s", + } + /* + // *any to 2001 + attrs = v1.AttrGetCost{ + Subject: "1002", + Destination: "2001", + AnswerTime: "*now", + Usage: "45s", + } + if err := tutRpc.Call(utils.ApierV1GetCost, attrs, &rply); err != nil { + t.Error("Unexpected nil error received: ", err.Error()) + } else if *rply.Cost != 1.2 { // FixMe: missing ConnectFee out of Cost + t.Errorf("Unexpected cost received: %f", *rply.Cost) + } + */ + // *any to 2001 on NEW_YEAR + attrs = v1.AttrGetCost{ + Subject: "1002", + Destination: "2001", + AnswerTime: "2020-01-01T21:00:00Z", + Usage: "45s", + } + if err := tutRpc.Call(utils.ApierV1GetCost, attrs, &rply); err != nil { + t.Error("Unexpected nil error received: ", err.Error()) + } else if *rply.Cost != 0.45 { + t.Errorf("Unexpected cost received: %f", *rply.Cost) + } + // Fallback pricing from *any, Usage will be rounded to 60s + attrs = v1.AttrGetCost{ + Subject: "1001", + Destination: "1003", + AnswerTime: "2019-03-11T21:00:00Z", + Usage: "45s", + } + if err := tutRpc.Call(utils.ApierV1GetCost, attrs, &rply); err != nil { + t.Error("Unexpected nil error received: ", err.Error()) + } else if *rply.Cost != 0.45 { // FixMe: missing ConnectFee out of Cost + t.Errorf("Unexpected cost received: %f", *rply.Cost) + } + // Unauthorized destination + attrs = v1.AttrGetCost{ + Subject: "1001", + Destination: "4003", + AnswerTime: "2019-03-11T09:00:00Z", + Usage: "1m", + } + if err := tutRpc.Call(utils.ApierV1GetCost, attrs, &rply); err == nil || + err.Error() != "SERVER_ERROR: UNAUTHORIZED_DESTINATION" { + t.Error("Unexpected nil error received: ", err) + } + // Data charging + attrs = v1.AttrGetCost{ + Category: "data", + Subject: "1001", + AnswerTime: "*now", + Usage: "2048", + } + if err := tutRpc.Call(utils.ApierV1GetCost, attrs, &rply); err != nil { + t.Error("Unexpected nil error received: ", err.Error()) + } else if *rply.Cost != 2.0 { // FixMe: missing ConnectFee out of Cost + t.Errorf("Unexpected cost received: %f", *rply.Cost) + } + // SMS charging 1002 + attrs = v1.AttrGetCost{ + Category: "sms", + Subject: "1003", + Destination: "1002", + AnswerTime: "*now", + Usage: "1", + } + if err := tutRpc.Call(utils.ApierV1GetCost, attrs, &rply); err != nil { + t.Error("Unexpected nil error received: ", err.Error()) + } else if *rply.Cost != 0.1 { + t.Errorf("Unexpected cost received: %f", *rply.Cost) + } + // SMS charging 10 + attrs = v1.AttrGetCost{ + Category: "sms", + Subject: "1001", + Destination: "1003", + AnswerTime: "*now", + Usage: "1", + } + if err := tutRpc.Call(utils.ApierV1GetCost, attrs, &rply); err != nil { + t.Error("Unexpected nil error received: ", err.Error()) + } else if *rply.Cost != 0.2 { + t.Errorf("Unexpected cost received: %f", *rply.Cost) + } + // SMS charging UNAUTHORIZED + attrs = v1.AttrGetCost{ + Category: "sms", + Subject: "1001", + Destination: "2001", + AnswerTime: "*now", + Usage: "1", + } + if err := tutRpc.Call(utils.ApierV1GetCost, attrs, &rply); err == nil || + err.Error() != "SERVER_ERROR: UNAUTHORIZED_DESTINATION" { + t.Error("Unexpected nil error received: ", err) + } +} + +func testTutStopEngine(t *testing.T) { + if err := engine.KillEngine(tutDelay); err != nil { + t.Error(err) + } +} diff --git a/utils/consts.go b/utils/consts.go index fdc2bb3e2..af0dae4f0 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -686,15 +686,17 @@ const ( // ApierV1 APIs const ( - ApierV1ComputeFilterIndexes = "ApierV1.ComputeFilterIndexes" - ApierV1ReloadCache = "ApierV1.ReloadCache" - ApierV1ReloadScheduler = "ApierV1.ReloadScheduler" - ApierV1Ping = "ApierV1.Ping" - ApierV1SetDispatcherProfile = "ApierV1.SetDispatcherProfile" - ApierV1GetDispatcherProfile = "ApierV1.GetDispatcherProfile" - ApierV1RemoveDispatcherProfile = "ApierV1.RemoveDispatcherProfile" - ApierV1GetResource = "ApierV1.GetResource" - ApierV1GetEventCost = "ApierV1.GetEventCost" + ApierV1ComputeFilterIndexes = "ApierV1.ComputeFilterIndexes" + ApierV1ReloadCache = "ApierV1.ReloadCache" + ApierV1ReloadScheduler = "ApierV1.ReloadScheduler" + ApierV1Ping = "ApierV1.Ping" + ApierV1SetDispatcherProfile = "ApierV1.SetDispatcherProfile" + ApierV1GetDispatcherProfile = "ApierV1.GetDispatcherProfile" + ApierV1RemoveDispatcherProfile = "ApierV1.RemoveDispatcherProfile" + ApierV1GetResource = "ApierV1.GetResource" + ApierV1GetEventCost = "ApierV1.GetEventCost" + ApierV1LoadTariffPlanFromFolder = "ApierV1.LoadTariffPlanFromFolder" + ApierV1GetCost = "ApierV1.GetCost" ) const (