diff --git a/data/conf/samples/twoengines/engine1/cgrates.json b/data/conf/samples/twoengines/engine1/cgrates.json new file mode 100644 index 000000000..68350b91a --- /dev/null +++ b/data/conf/samples/twoengines/engine1/cgrates.json @@ -0,0 +1,49 @@ +{ +"general": { + "log_level": 7, + "node_id": "Engine1", +}, + + +"listen": { + "rpc_json": ":2012", + "rpc_gob": ":2013", + "http": ":2080", +}, + + +"rpc_conns": { + "cacheConn": { + "strategy": "*broadcast", + "conns": [ + {"address": "*internal"}, + {"address": "127.0.0.1:20212", "transport":"*json"} + ], + }, +}, + + +"data_db": { + "db_type": "redis", + "db_port": 6379, + "db_name": "10", +}, + + +"stor_db": { + "db_password": "CGRateS.org", +}, + + +"rals": { + "enabled": true, +}, + + +"apiers": { + "enabled": true, + "caches_conns":["cacheConn"] +}, + + +} diff --git a/data/conf/samples/twoengines/engine2/cgrates.json b/data/conf/samples/twoengines/engine2/cgrates.json new file mode 100644 index 000000000..7750a7e8c --- /dev/null +++ b/data/conf/samples/twoengines/engine2/cgrates.json @@ -0,0 +1,32 @@ +{ +"general": { + "log_level": 7, + "node_id": "Engine2", +}, + + +"listen": { + "rpc_json": ":20212", + "rpc_gob": ":20213", + "http": ":20280", +}, + + +"data_db": { + "db_type": "redis", + "db_port": 6379, + "db_name": "10", +}, + + +"stor_db": { + "db_password": "CGRateS.org", +}, + + +"rals": { + "enabled": true, +}, + + +} diff --git a/general_tests/twoengines_it_test.go b/general_tests/twoengines_it_test.go new file mode 100644 index 000000000..93fedfadc --- /dev/null +++ b/general_tests/twoengines_it_test.go @@ -0,0 +1,209 @@ +// +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" + "path" + "reflect" + "testing" + "time" + + "github.com/cgrates/cgrates/utils" + + "github.com/cgrates/cgrates/engine" + + "github.com/cgrates/cgrates/config" +) + +var ( + engineOneCfgPath string + engineOneCfg *config.CGRConfig + engineOneRpc *rpc.Client + + engineTwoCfgPath string + engineTwoCfg *config.CGRConfig + engineTwoRpc *rpc.Client +) + +var sTestsTwoEnginesIT = []func(t *testing.T){ + testTwoEnginesInitConfig, + testTwoEnginesInitDataDB, + testTwoEnginesInitStorDB, + testTwoEnginesStartEngine, + testTwoEnginesRPC, + testTwoEnginesCheckCacheBeforeSet, + testTwoEnginesSetThreshold, + testTwoEnginesCheckCacheAfterSet, + testTwoEnginesKillEngines, +} + +func TestTwoEngines(t *testing.T) { + for _, test := range sTestsTwoEnginesIT { + t.Run("TestTwoEngines", test) + } +} + +func testTwoEnginesInitConfig(t *testing.T) { + engineOneCfgPath = path.Join(*dataDir, "conf", "samples", "twoengines", "engine1") + if engineOneCfg, err = config.NewCGRConfigFromPath(engineOneCfgPath); err != nil { + t.Fatal(err) + } + config.SetCgrConfig(engineOneCfg) + engineTwoCfgPath = path.Join(*dataDir, "conf", "samples", "twoengines", "engine2") + if engineTwoCfg, err = config.NewCGRConfigFromPath(engineTwoCfgPath); err != nil { + t.Fatal(err) + } + +} +func testTwoEnginesInitDataDB(t *testing.T) { + if err := engine.InitDataDb(engineOneCfg); err != nil { + t.Fatal(err) + } +} +func testTwoEnginesInitStorDB(t *testing.T) { + if err := engine.InitStorDb(engineOneCfg); err != nil { + t.Fatal(err) + } +} +func testTwoEnginesStartEngine(t *testing.T) { + if _, err := engine.StopStartEngine(engineOneCfgPath, *waitRater); err != nil { + t.Fatal(err) + } + if _, err := engine.StartEngine(engineTwoCfgPath, *waitRater); err != nil { + t.Fatal(err) + } +} + +func testTwoEnginesRPC(t *testing.T) { + var err error + engineOneRpc, err = newRPCClient(engineOneCfg.ListenCfg()) + if err != nil { + t.Fatal(err) + } + engineTwoRpc, err = newRPCClient(engineTwoCfg.ListenCfg()) + if err != nil { + t.Fatal(err) + } +} + +func testTwoEnginesCheckCacheBeforeSet(t *testing.T) { + var reply bool + argHasItem := utils.ArgsGetCacheItem{ + CacheID: utils.CacheThresholdProfiles, + ItemID: "cgrates.org:THD_TwoEnginesTest", + } + if err := engineOneRpc.Call(utils.CacheSv1HasItem, argHasItem, &reply); err != nil { + t.Error(err) + } else if reply { + t.Errorf("Expected: false , received: %v ", reply) + } + var rcvKeys []string + argGetItemIDs := utils.ArgsGetCacheItemIDs{ + CacheID: utils.CacheThresholdProfiles, + } + if err := engineOneRpc.Call(utils.CacheSv1GetItemIDs, argGetItemIDs, &rcvKeys); err == nil || + err.Error() != utils.ErrNotFound.Error() { + t.Fatalf("Expected error: %s received error: %s and reply: %v ", utils.ErrNotFound, err.Error(), rcvKeys) + } + + if err := engineTwoRpc.Call(utils.CacheSv1HasItem, argHasItem, &reply); err != nil { + t.Error(err) + } else if reply { + t.Errorf("Expected: false , received: %v ", reply) + } + if err := engineTwoRpc.Call(utils.CacheSv1GetItemIDs, argGetItemIDs, &rcvKeys); err == nil || + err.Error() != utils.ErrNotFound.Error() { + t.Fatalf("Expected error: %s received error: %s and reply: %v ", utils.ErrNotFound, err.Error(), rcvKeys) + } +} + +func testTwoEnginesSetThreshold(t *testing.T) { + var reply *engine.ThresholdProfile + var result string + tPrfl := &engine.ThresholdWithCache{ + ThresholdProfile: &engine.ThresholdProfile{ + Tenant: "cgrates.org", + ID: "THD_TwoEnginesTest", + FilterIDs: []string{"*string:~*req.Account:1001"}, + MaxHits: -1, + MinSleep: time.Duration(5 * time.Minute), + Blocker: false, + Weight: 20.0, + ActionIDs: []string{"ACT_1"}, + Async: true, + }, + Cache: utils.StringPointer(utils.MetaLoad), + } + if err := engineOneRpc.Call(utils.APIerSv1SetThresholdProfile, tPrfl, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } + time.Sleep(50 * time.Millisecond) + if err := engineOneRpc.Call(utils.APIerSv1GetThresholdProfile, + &utils.TenantID{Tenant: "cgrates.org", ID: "THD_TwoEnginesTest"}, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(tPrfl.ThresholdProfile, reply) { + t.Errorf("Expecting: %+v, received: %+v", tPrfl.ThresholdProfile, reply) + } +} + +func testTwoEnginesCheckCacheAfterSet(t *testing.T) { + var reply bool + expected := true + argHasItem := utils.ArgsGetCacheItem{ + CacheID: utils.CacheThresholdProfiles, + ItemID: "cgrates.org:THD_TwoEnginesTest", + } + if err := engineOneRpc.Call(utils.CacheSv1HasItem, argHasItem, &reply); err != nil { + t.Error(err) + } else if !reply { + t.Errorf("Expected: %v , received:%v", expected, reply) + } + var rcvKeys []string + expKeys := []string{"cgrates.org:THD_TwoEnginesTest"} + argGetItemIDs := utils.ArgsGetCacheItemIDs{ + CacheID: utils.CacheThresholdProfiles, + } + if err := engineOneRpc.Call(utils.CacheSv1GetItemIDs, argGetItemIDs, &rcvKeys); err != nil { + t.Fatalf("Got error on APIerSv1.GetCacheStats: %s ", err.Error()) + } else if !reflect.DeepEqual(expKeys, rcvKeys) { + t.Errorf("Expected: %+v, received: %+v", expKeys, rcvKeys) + } + + if err := engineTwoRpc.Call(utils.CacheSv1HasItem, argHasItem, &reply); err != nil { + t.Error(err) + } else if !reply { + t.Errorf("Expected: %v , received:%v", expected, reply) + } + if err := engineTwoRpc.Call(utils.CacheSv1GetItemIDs, argGetItemIDs, &rcvKeys); err != nil { + t.Fatalf("Got error on APIerSv1.GetCacheStats: %s ", err.Error()) + } else if !reflect.DeepEqual(expKeys, rcvKeys) { + t.Errorf("Expected: %+v, received: %+v", expKeys, rcvKeys) + } +} + +func testTwoEnginesKillEngines(t *testing.T) { + if err := engine.KillEngine(*waitRater); err != nil { + t.Error(err) + } +}