From 1ba9b569e2cd69da3608eb56f4b2231f55e948c4 Mon Sep 17 00:00:00 2001 From: ionutboangiu Date: Mon, 7 Jun 2021 16:51:48 +0300 Subject: [PATCH] Add integration tests for stats apis (incomplete) --- apis/resources_it_test.go | 1 + apis/stats_it_test.go | 413 ++++++++++++++++++ config/config_it_test.go | 1 - data/conf/samples/stats_internal/cgrates.json | 37 ++ data/conf/samples/stats_mongo/cgrates.json | 39 ++ data/conf/samples/stats_mysql/cgrates.json | 37 ++ utils/consts.go | 25 +- 7 files changed, 540 insertions(+), 13 deletions(-) create mode 100644 apis/stats_it_test.go create mode 100644 data/conf/samples/stats_internal/cgrates.json create mode 100644 data/conf/samples/stats_mongo/cgrates.json create mode 100644 data/conf/samples/stats_mysql/cgrates.json diff --git a/apis/resources_it_test.go b/apis/resources_it_test.go index 906754c96..2eeb056bd 100644 --- a/apis/resources_it_test.go +++ b/apis/resources_it_test.go @@ -522,6 +522,7 @@ func testResourceSStartServer(t *testing.T) { func testResourceSStopServer(t *testing.T) { rsSrv.Close() } + func testResourceSSetActionProfile(t *testing.T) { actPrf := &engine.ActionProfileWithAPIOpts{ ActionProfile: &engine.ActionProfile{ diff --git a/apis/stats_it_test.go b/apis/stats_it_test.go new file mode 100644 index 000000000..be63aeb4f --- /dev/null +++ b/apis/stats_it_test.go @@ -0,0 +1,413 @@ +// +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 apis + +import ( + "path" + "testing" + + "github.com/cgrates/birpc" + "github.com/cgrates/birpc/context" + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +var ( + sqCfgPath string + sqCfg *config.CGRConfig + sqRPC *birpc.Client + sqConfigDIR string //run tests for specific configuration + + sTestsSq = []func(t *testing.T){ + testStatsInitCfg, + testStatsInitDataDB, + testStatsResetStorDB, + testStatsStartEngine, + testStatsRPCConn, + testStatsGetStatQueueBeforeSet, + testStatsSetStatQueueProfiles, + // testStatsGetStatQueueAfterSet, + // testStatsGetStatQueueIDs, + // testStatsGetStatQueueProfileIDs, + // testStatsGetStatQueueProfileCount, + // testStatsGetStatQueuesForEvent, + // testStatsRemoveStatQueueProfiles, + // testStatsGetStatQueuesAfterRemove, + testStatsPing, + testStatsKillEngine, + } +) + +func TestStatsIT(t *testing.T) { + switch *dbType { + case utils.MetaInternal: + sqConfigDIR = "stats_internal" + case utils.MetaMongo: + sqConfigDIR = "stats_mongo" + case utils.MetaMySQL: + sqConfigDIR = "stats_mysql" + case utils.MetaPostgres: + t.SkipNow() + default: + t.Fatal("Unknown Database type") + } + for _, stest := range sTestsSq { + t.Run(sqConfigDIR, stest) + } +} + +func testStatsInitCfg(t *testing.T) { + var err error + sqCfgPath = path.Join(*dataDir, "conf", "samples", sqConfigDIR) + sqCfg, err = config.NewCGRConfigFromPath(sqCfgPath) + if err != nil { + t.Error(err) + } +} + +func testStatsInitDataDB(t *testing.T) { + if err := engine.InitDataDB(sqCfg); err != nil { + t.Fatal(err) + } +} + +func testStatsResetStorDB(t *testing.T) { + if err := engine.InitStorDB(sqCfg); err != nil { + t.Fatal(err) + } +} + +// Start CGR Engine +func testStatsStartEngine(t *testing.T) { + if _, err := engine.StopStartEngine(sqCfgPath, *waitRater); err != nil { + t.Fatal(err) + } +} + +func testStatsRPCConn(t *testing.T) { + var err error + sqRPC, err = newRPCClient(sqCfg.ListenCfg()) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal(err) + } +} + +//Kill the engine when it is about to be finished +func testStatsKillEngine(t *testing.T) { + if err := engine.KillEngine(100); err != nil { + t.Error(err) + } +} + +func testStatsPing(t *testing.T) { + var reply string + if err := sqRPC.Call(context.Background(), utils.StatSv1Ping, + new(utils.CGREvent), &reply); err != nil { + t.Error(err) + } else if reply != utils.Pong { + t.Error("Unexpected reply returned:", reply) + } +} + +func testStatsGetStatQueueBeforeSet(t *testing.T) { + args := &engine.StatsArgsProcessEvent{ + StatIDs: []string{"SQ_1", "SQ_2"}, + CGREvent: &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "StatsEventTest", + Event: map[string]interface{}{ + utils.AccountField: "1001", + }, + }, + } + + var rplySqs engine.StatQueues + if err := sqRPC.Call(context.Background(), utils.StatSv1GetStatQueuesForEvent, + args, &rplySqs); err == nil || err.Error() != utils.ErrNotFound.Error() { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err) + } +} + +func testStatsSetStatQueueProfiles(t *testing.T) { + sqPrf1 := &engine.StatQueueProfileWithAPIOpts{ + StatQueueProfile: &engine.StatQueueProfile{ + Tenant: "cgrates.org", + ID: "SQ_1", + Weight: 10, + ThresholdIDs: []string{utils.MetaNone}, + }, + } + + var reply string + if err := sqRPC.Call(context.Background(), utils.AdminSv1SetStatQueueProfile, + sqPrf1, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Error("Unexpected reply returned:", reply) + } + + sqPrf2 := &engine.StatQueueProfileWithAPIOpts{ + StatQueueProfile: &engine.StatQueueProfile{ + Tenant: "cgrates.org", + ID: "SQ_2", + Weight: 20, + ThresholdIDs: []string{utils.MetaNone}, + }, + } + + if err := sqRPC.Call(context.Background(), utils.AdminSv1SetStatQueueProfile, + sqPrf2, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Error("Unexpected reply returned:", reply) + } +} + +// func testStatsGetStatQueueAfterSet(t *testing.T) { +// var rplySq engine.StatQueue +// var rplySqPrf engine.StatQueueProfile +// expSq := engine.StatQueue{ +// Tenant: "cgrates.org", +// ID: "SQ_1", +// SQMetrics: make(map[string]engine.StatMetric), +// SQItems: []engine.SQItem{}, +// } +// expSqPrf := engine.StatQueueProfile{ +// Tenant: "cgrates.org", +// ID: "SQ_1", +// Weight: 10, +// ThresholdIDs: []string{utils.MetaNone}, +// } + +// if err := sqRPC.Call(context.Background(), utils.StatSv1GetStatQueue, +// &utils.TenantIDWithAPIOpts{ +// TenantID: &utils.TenantID{ +// Tenant: "cgrates.org", +// ID: "SQ_1", +// }, +// }, &rplySq); err != nil { +// t.Error(err) +// } else if !reflect.DeepEqual(rplySq, expSq) { +// t.Errorf("expected: <%+v>, \nreceived: <%+v>", +// utils.ToJSON(expSq), utils.ToJSON(rplySq)) +// } + +// if err := sqRPC.Call(context.Background(), utils.AdminSv1GetStatQueueProfile, +// utils.TenantID{ +// Tenant: "cgrates.org", +// ID: "SQ_1", +// }, &rplySqPrf); err != nil { +// t.Error(err) +// } else if !reflect.DeepEqual(rplySqPrf, expSqPrf) { +// t.Errorf("expected: <%+v>, \nreceived: <%+v>", +// utils.ToJSON(expSqPrf), utils.ToJSON(rplySqPrf)) +// } + +// expSq = engine.StatQueue{ +// Tenant: "cgrates.org", +// ID: "SQ_2", +// SQMetrics: make(map[string]engine.StatMetric), +// SQItems: []engine.SQItem{}, +// } +// expSqPrf = engine.StatQueueProfile{ +// Tenant: "cgrates.org", +// ID: "SQ_2", +// Weight: 20, +// ThresholdIDs: []string{utils.MetaNone}, +// } + +// if err := sqRPC.Call(context.Background(), utils.StatSv1GetStatQueue, +// &utils.TenantIDWithAPIOpts{ +// TenantID: &utils.TenantID{ +// Tenant: "cgrates.org", +// ID: "SQ_2", +// }, +// }, &rplySq); err != nil { +// t.Error(err) +// } else if !reflect.DeepEqual(rplySq, expSq) { +// t.Errorf("expected: <%+v>, \nreceived: <%+v>", +// utils.ToJSON(expSq), utils.ToJSON(rplySq)) +// } + +// if err := sqRPC.Call(context.Background(), utils.AdminSv1GetStatQueueProfile, +// &utils.TenantID{ +// Tenant: "cgrates.org", +// ID: "SQ_2", +// }, &rplySqPrf); err != nil { +// t.Error(err) +// } else if !reflect.DeepEqual(rplySqPrf, expSqPrf) { +// t.Errorf("expected: <%+v>, \nreceived: <%+v>", +// utils.ToJSON(expSqPrf), utils.ToJSON(rplySqPrf)) +// } +// } + +// func testStatsGetStatQueueIDs(t *testing.T) { +// expIDs := []string{"SQ_1", "SQ_2"} +// var sqIDs []string +// if err := sqRPC.Call(context.Background(), utils.StatSv1GetQueueIDs, +// &utils.TenantWithAPIOpts{ +// Tenant: "cgrates.org", +// }, &sqIDs); err != nil { +// t.Error(err) +// } else { +// sort.Strings(sqIDs) +// if !reflect.DeepEqual(sqIDs, expIDs) { +// t.Errorf("expected: <%+v>, \nreceived: <%+v>", expIDs, sqIDs) +// } +// } +// } + +// func testStatsGetStatQueueProfileIDs(t *testing.T) { +// expIDs := []string{"SQ_1", "SQ_2"} +// var sqIDs []string +// if err := sqRPC.Call(context.Background(), utils.AdminSv1GetStatQueueProfileIDs, +// &utils.PaginatorWithTenant{ +// Tenant: "cgrates.org", +// Paginator: utils.Paginator{}, +// }, &sqIDs); err != nil { +// t.Error(err) +// } else { +// sort.Strings(sqIDs) +// if !reflect.DeepEqual(sqIDs, expIDs) { +// t.Errorf("expected: <%+v>, \nreceived: <%+v>", expIDs, sqIDs) +// } +// } +// } + +// func testStatsGetStatQueueProfileCount(t *testing.T) { +// var reply int +// if err := sqRPC.Call(context.Background(), utils.AdminSv1GetStatQueueProfileCount, +// &utils.TenantWithAPIOpts{ +// Tenant: "cgrates.org", +// }, &reply); err != nil { +// t.Error(err) +// } else if reply != 2 { +// t.Errorf("expected: <%+v>, \nreceived: <%+v>", 2, reply) +// } +// } + +// func testStatsGetStatQueuesForEvent(t *testing.T) { +// args := &engine.StatsArgsProcessEvent{ +// StatIDs: []string{"SQ_1", "SQ_2"}, +// CGREvent: &utils.CGREvent{ +// Tenant: "cgrates.org", +// ID: "StatsEventTest", +// Event: map[string]interface{}{ +// utils.AccountField: "1001", +// }, +// }, +// } +// expSqs := engine.StatQueues{ +// &engine.StatQueue{ +// Tenant: "cgrates.org", +// ID: "SQ_2", +// SQMetrics: make(map[string]engine.StatMetric), +// }, +// &engine.StatQueue{ +// Tenant: "cgrates.org", +// ID: "SQ_1", +// SQMetrics: make(map[string]engine.StatMetric), +// }, +// } + +// rplySqs := engine.StatQueues{ +// { +// SQMetrics: map[string]engine.StatMetric{ +// utils.MetaTCD: &engine.StatTCD{}, +// utils.MetaASR: &engine.StatASR{}, +// utils.MetaACD: &engine.StatACD{}, +// }, +// }, +// { +// SQMetrics: map[string]engine.StatMetric{ +// utils.MetaTCD: &engine.StatTCD{}, +// utils.MetaASR: &engine.StatASR{}, +// utils.MetaACD: &engine.StatACD{}, +// }, +// }, +// } +// if err := sqRPC.Call(context.Background(), utils.StatSv1GetStatQueuesForEvent, +// args, &rplySqs); err != nil { +// t.Error(err) +// } else if !reflect.DeepEqual(rplySqs, expSqs) { +// t.Errorf("expected: <%+v>, \nreceived: <%+v>", +// utils.ToJSON(expSqs), utils.ToJSON(rplySqs)) +// } +// } + +// func testStatsRemoveStatQueueProfiles(t *testing.T) { +// var reply string + +// if err := sqRPC.Call(context.Background(), utils.AdminSv1RemoveStatQueueProfile, +// &utils.TenantIDWithAPIOpts{ +// TenantID: &utils.TenantID{ +// Tenant: "cgrates.org", +// ID: "SQ_1", +// }}, &reply); err != nil { +// t.Error(err) +// } else if reply != utils.OK { +// t.Error("Unexpected reply returned:", reply) +// } + +// if err := sqRPC.Call(context.Background(), utils.AdminSv1RemoveStatQueueProfile, +// &utils.TenantIDWithAPIOpts{ +// TenantID: &utils.TenantID{ +// Tenant: "cgrates.org", +// ID: "SQ_2", +// }}, &reply); err != nil { +// t.Error(err) +// } else if reply != utils.OK { +// t.Error("Unexpected reply returned:", reply) +// } +// } + +// func testStatsGetStatQueuesAfterRemove(t *testing.T) { +// args := &engine.StatsArgsProcessEvent{ +// StatIDs: []string{"SQ_1", "SQ_2"}, +// CGREvent: &utils.CGREvent{ +// Tenant: "cgrates.org", +// ID: "StatsEventTest", +// Event: map[string]interface{}{ +// utils.AccountField: "1001", +// }, +// }, +// } +// expSqs := engine.StatQueues{ +// &engine.StatQueue{ +// Tenant: "cgrates.org", +// ID: "SQ_2", +// }, +// &engine.StatQueue{ +// Tenant: "cgrates.org", +// ID: "SQ_1", +// }, +// } + +// var rplySqs engine.StatQueues +// if err := sqRPC.Call(context.Background(), utils.StatSv1GetStatQueuesForEvent, +// args, &rplySqs); err != nil { +// t.Error(err) +// } else if !reflect.DeepEqual(rplySqs, expSqs) { +// t.Errorf("expected: <%+v>, \nreceived: <%+v>", +// utils.ToJSON(expSqs), utils.ToJSON(rplySqs)) +// } +// } diff --git a/config/config_it_test.go b/config/config_it_test.go index 1dfcaf3c0..d04d59bca 100644 --- a/config/config_it_test.go +++ b/config/config_it_test.go @@ -265,7 +265,6 @@ func testCGRConfigReloadThresholdS(t *testing.T) { SuffixIndexedFields: &[]string{}, ActionSConns: []string{}, IndexedSelects: true, - ActionSConns: []string{}, } if !reflect.DeepEqual(expAttr, cfg.ThresholdSCfg()) { t.Errorf("Expected %s , received: %s ", utils.ToJSON(expAttr), utils.ToJSON(cfg.ThresholdSCfg())) diff --git a/data/conf/samples/stats_internal/cgrates.json b/data/conf/samples/stats_internal/cgrates.json new file mode 100644 index 000000000..a8e32ac1f --- /dev/null +++ b/data/conf/samples/stats_internal/cgrates.json @@ -0,0 +1,37 @@ +{ + // CGRateS Configuration file + // will be used in apis/stats_it_test.go + + "general": { + "log_level": 7, + }, + + "data_db": { + "db_type": "*internal", + }, + + "stor_db": { + "db_type": "*internal", + }, + + "actions": { + "enabled": true, + "thresholds_conns": ["*internal"], + }, + + "stats": { + "enabled": true, + "store_interval": "-1", + "thresholds_conns": ["*internal"], + }, + + "thresholds": { + "enabled": true, + "store_interval": "-1", + "actions_conns": ["*internal"], + }, + + "admins": { + "enabled": true, + }, + } \ No newline at end of file diff --git a/data/conf/samples/stats_mongo/cgrates.json b/data/conf/samples/stats_mongo/cgrates.json new file mode 100644 index 000000000..79b1d4963 --- /dev/null +++ b/data/conf/samples/stats_mongo/cgrates.json @@ -0,0 +1,39 @@ +{ + // CGRateS Configuration file + // will be used in apis/stats_it_test.go + + "general": { + "log_level": 7, + }, + + "data_db": { + "db_type": "mongo", + "db_name": "10", + "db_port": 27017, + }, + + "stor_db": { + "db_type": "mongo", + "db_name": "cgrates", + "db_port": 27017, + }, + + "actions": { + "enabled": true, + "thresholds_conns": ["*internal"], + }, + + "stats": { + "enabled": true, + "store_interval": "-1", + }, + + "thresholds": { + "enabled": true, + "actions_conns": ["*internal"], + }, + + "admins": { + "enabled": true, + } + } \ No newline at end of file diff --git a/data/conf/samples/stats_mysql/cgrates.json b/data/conf/samples/stats_mysql/cgrates.json new file mode 100644 index 000000000..c7c81ee4d --- /dev/null +++ b/data/conf/samples/stats_mysql/cgrates.json @@ -0,0 +1,37 @@ +{ + // CGRateS Configuration file + // will be used in apis/stats_it_test.go + "general": { + "log_level": 7, + }, + + "data_db": { // database used to store runtime data (eg: accounts, cdr stats) + "db_type": "redis", // data_db type: + "db_port": 6379, // data_db port to reach the database + "db_name": "10", // data_db database name to connect to + }, + + "stor_db": { + "db_password": "CGRateS.org", + }, + + "actions": { + "enabled": true, + "thresholds_conns": ["*internal"], + }, + + "stats": { + "enabled": true, + "store_interval": "-1", + }, + + "thresholds": { + "enabled": true, + "store_interval": "-1", + "actions_conns": ["*internal"], + }, + + "admins": { + "enabled": true, + } + } \ No newline at end of file diff --git a/utils/consts.go b/utils/consts.go index a5dd8b5ff..92808bf2d 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -1344,18 +1344,19 @@ const ( // StatS APIs const ( - StatSv1ProcessEvent = "StatSv1.ProcessEvent" - StatSv1GetQueueIDs = "StatSv1.GetQueueIDs" - StatSv1GetQueueStringMetrics = "StatSv1.GetQueueStringMetrics" - StatSv1GetQueueFloatMetrics = "StatSv1.GetQueueFloatMetrics" - StatSv1Ping = "StatSv1.Ping" - StatSv1GetStatQueuesForEvent = "StatSv1.GetStatQueuesForEvent" - StatSv1GetStatQueue = "StatSv1.GetStatQueue" - StatSv1ResetStatQueue = "StatSv1.ResetStatQueue" - APIerSv1GetStatQueueProfile = "APIerSv1.GetStatQueueProfile" - APIerSv1RemoveStatQueueProfile = "APIerSv1.RemoveStatQueueProfile" - APIerSv1SetStatQueueProfile = "APIerSv1.SetStatQueueProfile" - APIerSv1GetStatQueueProfileIDs = "APIerSv1.GetStatQueueProfileIDs" + StatSv1ProcessEvent = "StatSv1.ProcessEvent" + StatSv1GetQueueIDs = "StatSv1.GetQueueIDs" + StatSv1GetQueueStringMetrics = "StatSv1.GetQueueStringMetrics" + StatSv1GetQueueFloatMetrics = "StatSv1.GetQueueFloatMetrics" + StatSv1Ping = "StatSv1.Ping" + StatSv1GetStatQueuesForEvent = "StatSv1.GetStatQueuesForEvent" + StatSv1GetStatQueue = "StatSv1.GetStatQueue" + StatSv1ResetStatQueue = "StatSv1.ResetStatQueue" + AdminSv1GetStatQueueProfile = "AdminSv1.GetStatQueueProfile" + AdminSv1RemoveStatQueueProfile = "AdminSv1.RemoveStatQueueProfile" + AdminSv1SetStatQueueProfile = "AdminSv1.SetStatQueueProfile" + AdminSv1GetStatQueueProfileIDs = "AdminSv1.GetStatQueueProfileIDs" + AdminSv1GetStatQueueProfileCount = "AdminSv1.GetStatQueueProfileCount" ) // ResourceS APIs