//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 ( "path" "reflect" "sort" "testing" "github.com/cgrates/birpc" "github.com/cgrates/birpc/context" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/loaders" "github.com/cgrates/cgrates/utils" ) var ( rtsCaseSv1CfgPath string rtsCaseSv1Cfg *config.CGRConfig rtsCaseSv1BiRpc *birpc.Client rtsCaseSv1ConfDIR string //run tests for specific configuration sTestsRtsCaseSV1 = []func(t *testing.T){ testV1RtsCaseLoadConfig, testV1RtsCaseInitDataDb, testV1RtsCaseStartEngine, testV1RtsCaseRpcConn, testV1RtsCaseFromFolder, testV1RtsCaseGetRoutesAfterLoading, testV1RtsCasesSortingRoutesWeightAccountValue, testV1RtsCasesSortingRoutesWeightAllRoutes, testV1RtsCasesSortingRoutesWeightNotMatchingValue, testV1RtsCasesSortingRoutesLowestCost, testV1RtsCasesSortingRoutesLowestCostDefaultUsage, testV1RtsCasesSortingRoutesLCSetStatsAndResForMatching, testV1RtsCasesSortingRoutesLowestCostStats, testV1RtsCasesSortingRoutesLowestCosMatchingAllRoutes, testV1RtsCasesSortingRoutesLowestCosMaxCost, testV1RtsCasesSortingRoutesLowestCosMaxCostNotMatch, testV1RtsCasesSortingRoutesProcessMetrics, testV1RtsCasesSortingRoutesQOS, testV1RtsCasesSortingRoutesQOSAllRoutes, testV1RtsCasesSortingRoutesQOSNotFound, testV1RtsCasesSortingRoutesAllocateResources, testV1RtsCasesSortingRoutesReasNotAllRoutes, testV1RtsCasesSortingRoutesReasAllRoutes, testV1RtsCasesRoutesProcessStatsForLoadRtsSorting, testV1RtsCasesRoutesLoadRtsSorting, testV1RtsCasesSortRoutesHigherCostV2V3, testV1RtsCasesSortRoutesHigherCostAllocateRes, testV1RtsCasesSortRoutesHigherCostV1V3, testV1RtsCasesSortRoutesHigherCostAllRoutes, testV1RtsCasesSortingRoutesLowestCostAccounts, testV1RtsCaseStopEngine, } ) // bench for routes with connection trough rates // go test -run=BenchmarkV1RtsCasesSortingRoutesLowestCost -tags=integration -bench=. -benchtime=5s -count=4 -v /* func BenchmarkV1RtsCasesSortingRoutesLowestCost(t *testing.B) { var err error rtsCaseSv1ConfDIR = "routes_cases_mysql" rtsCaseSv1CfgPath = path.Join(*dataDir, "conf", "samples", rtsCaseSv1ConfDIR) if rtsCaseSv1Cfg, err = config.NewCGRConfigFromPath(context.Background(), rtsCaseSv1CfgPath); err != nil { t.Error(err) } rtsCaseSv1BiRpc, err = newBiRPCClient(rtsCaseSv1Cfg.ListenCfg()) // We connect over JSON so we can also troubleshoot if needed if err != nil { t.Fatal("Could not connect to rater: ", err.Error()) } ev := &utils.CGREvent{ ID: "LC_SORT", Tenant: "cgrates.org", Event: map[string]interface{}{ utils.AccountField: "acnt22", utils.Destination: "104423", }, APIOpts: map[string]interface{}{ utils.MetaStartTime: "2013-06-01T05:00:00Z", utils.MetaUsage: "50s", }, } expSrtdRoutes := &engine.SortedRoutesList{ { ProfileID: "ROUTE_LCR_ACCOUNTS", Sorting: "*lc", Routes: []*engine.SortedRoute{ { RouteID: "route4", SortingData: map[string]interface{}{ utils.Cost: nil, utils.AccountIDs: []interface{}{"ACCNT_ROUTES2"}, utils.Weight: 55., }, }, { RouteID: "route1", SortingData: map[string]interface{}{ utils.Cost: 5., utils.AccountIDs: []interface{}{"ACCNT_ROUTES1"}, utils.Weight: 20., }, }, { RouteID: "route2", SortingData: map[string]interface{}{ utils.Cost: 5., utils.AccountIDs: []interface{}{"ACCNT_ROUTES1"}, utils.Weight: 15., }, }, { RouteID: "route3", SortingData: map[string]interface{}{ utils.Cost: 5., utils.AccountIDs: []interface{}{"ACCNT_ROUTES1"}, utils.Weight: 10., }, }, }, }, } //var wg sync.WaitGroup now := time.Now() for i := 0; i < t.N; i++ { //wg.Add(1) //go func() { var reply *engine.SortedRoutesList //gonna match one route because the totalUsage by ne-allocated resources is 0 if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.RouteSv1GetRoutes, ev, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(expSrtdRoutes, reply) { t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expSrtdRoutes), utils.ToJSON(reply)) } //wg.Done() //}() } //wg.Wait() fmt.Println(time.Since(now)) } */ // Test start here func TestRoutesCaseV1IT(t *testing.T) { switch *dbType { case utils.MetaInternal: rtsCaseSv1ConfDIR = "routes_cases_internal" case utils.MetaMySQL: rtsCaseSv1ConfDIR = "routes_cases_mysql" case utils.MetaMongo: rtsCaseSv1ConfDIR = "routes_cases_mongo" case utils.MetaPostgres: t.SkipNow() default: t.Fatal("Unknown Database type") } for _, stest := range sTestsRtsCaseSV1 { t.Run(rtsCaseSv1ConfDIR, stest) } } func testV1RtsCaseLoadConfig(t *testing.T) { var err error rtsCaseSv1CfgPath = path.Join(*dataDir, "conf", "samples", rtsCaseSv1ConfDIR) if rtsCaseSv1Cfg, err = config.NewCGRConfigFromPath(context.Background(), rtsCaseSv1CfgPath); err != nil { t.Error(err) } } func testV1RtsCaseInitDataDb(t *testing.T) { if err := engine.InitDataDB(rtsCaseSv1Cfg); err != nil { t.Fatal(err) } } func testV1RtsCaseStartEngine(t *testing.T) { if _, err := engine.StopStartEngine(rtsCaseSv1CfgPath, *waitRater); err != nil { t.Fatal(err) } } func testV1RtsCaseRpcConn(t *testing.T) { var err error rtsCaseSv1BiRpc, err = newBiRPCClient(rtsCaseSv1Cfg.ListenCfg()) // We connect over JSON so we can also troubleshoot if needed if err != nil { t.Fatal("Could not connect to rater: ", err.Error()) } } func testV1RtsCaseFromFolder(t *testing.T) { caching := utils.MetaReload if rtsCaseSv1Cfg.DataDbCfg().Type == utils.Internal { caching = utils.MetaNone } var reply string if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.LoaderSv1Run, &loaders.ArgsProcessFolder{ // StopOnError: true, APIOpts: map[string]interface{}{utils.MetaCache: caching}, }, &reply); err != nil { t.Error(err) } else if reply != utils.OK { t.Error("Unexpected reply returned:", reply) } } func testV1RtsCaseGetRoutesAfterLoading(t *testing.T) { // ROUTE_ACNT_1001 expRt1 := &engine.RouteProfile{ ID: "ROUTE_ACNT_1001", Tenant: "cgrates.org", FilterIDs: []string{"*string:~*req.Account:1001"}, Sorting: "*weight", Routes: []*engine.Route{ { ID: "vendor1", FilterIDs: []string{"FLTR_DEST_1003"}, Weights: utils.DynamicWeights{ { Weight: 10, }, }, }, { ID: "vendor2", FilterIDs: []string{"*gte:~*accounts.1001.Balances[Concrete1].Units:10"}, Weights: utils.DynamicWeights{ { Weight: 20, }, }, }, { ID: "vendor3", FilterIDs: []string{"FLTR_DEST_1003", "*prefix:~*req.Account:10"}, Weights: utils.DynamicWeights{ { Weight: 40, }, }, }, { ID: "vendor4", Weights: utils.DynamicWeights{ { Weight: 35, }, }, }, }, } var reply *engine.RouteProfile if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.AdminSv1GetRouteProfile, &utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{ID: "ROUTE_ACNT_1001", Tenant: "cgrates.org"}}, &reply); err != nil { t.Error(err) } else { sort.Slice(reply.Routes, func(i, j int) bool { return reply.Routes[i].ID < reply.Routes[j].ID }) if !reflect.DeepEqual(expRt1, reply) { t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expRt1), utils.ToJSON(reply)) } } // ROUTE_ACNT_1002 expRt2 := &engine.RouteProfile{ ID: "ROUTE_ACNT_1002", Tenant: "cgrates.org", FilterIDs: []string{"*string:~*req.Account:1002"}, Sorting: "*lc", Routes: []*engine.Route{ { ID: "vendor1", FilterIDs: []string{"*lte:~*resources.RES_GRP1.TotalUsage:5"}, RateProfileIDs: []string{"RP_VENDOR1"}, Weights: utils.DynamicWeights{ { Weight: 0, }, }, }, { ID: "vendor2", FilterIDs: []string{"*gte:~*stats.STATS_VENDOR_2.*acd:1m"}, RateProfileIDs: []string{"RP_VENDOR2"}, Weights: utils.DynamicWeights{ { Weight: 0, }, }, }, { ID: "vendor3", RateProfileIDs: []string{"RP_VENDOR2"}, Weights: utils.DynamicWeights{ { Weight: 10, }, }, }, { ID: "vendor4", FilterIDs: []string{"*ai:~*opts.*startTime:2013-06-01T00:00:00Z|2013-06-01T10:00:00Z"}, RateProfileIDs: []string{"RP_STANDARD"}, Weights: utils.DynamicWeights{ { Weight: 30, }, }, }, }, } if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.AdminSv1GetRouteProfile, &utils.TenantID{ID: "ROUTE_ACNT_1002", Tenant: "cgrates.org"}, &reply); err != nil { t.Error(err) } else { sort.Slice(reply.Routes, func(i, j int) bool { return reply.Routes[i].ID < reply.Routes[j].ID }) if !reflect.DeepEqual(expRt2, reply) { t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expRt2), utils.ToJSON(reply)) } } // ROUTE_ACNT_1003 expRt3 := &engine.RouteProfile{ ID: "ROUTE_ACNT_1003", Tenant: "cgrates.org", FilterIDs: []string{"*string:~*req.Account:1003"}, Sorting: "*qos", SortingParameters: []string{"*acd", "*tcc"}, Routes: []*engine.Route{ { ID: "vendor1", StatIDs: []string{"STATS_VENDOR_1"}, Weights: utils.DynamicWeights{ { Weight: 0, }, }, }, { ID: "vendor2", FilterIDs: []string{"*prefix:~*req.Destination:10"}, StatIDs: []string{"STATS_VENDOR_2"}, Weights: utils.DynamicWeights{ { Weight: 0, }, }, }, { ID: "vendor3", FilterIDs: []string{"*gte:~*stats.STATS_VENDOR_1.*tcc:6"}, StatIDs: []string{"STATS_VENDOR_1"}, Weights: utils.DynamicWeights{ { Weight: 20, }, }, }, }, } if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.AdminSv1GetRouteProfile, &utils.TenantID{ID: "ROUTE_ACNT_1003", Tenant: "cgrates.org"}, &reply); err != nil { t.Error(err) } else { sort.Slice(reply.Routes, func(i, j int) bool { return reply.Routes[i].ID < reply.Routes[j].ID }) sort.Strings(reply.SortingParameters) sort.Strings(expRt1.SortingParameters) if !reflect.DeepEqual(expRt3, reply) { t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expRt3), utils.ToJSON(reply)) } } // ROUTE_ACNT_1004 expRt4 := &engine.RouteProfile{ ID: "ROUTE_ACNT_1004", Tenant: "cgrates.org", FilterIDs: []string{"*string:~*req.Account:1004"}, Sorting: "*reas", Routes: []*engine.Route{ { ID: "vendor1", ResourceIDs: []string{"RES_GRP1"}, Weights: utils.DynamicWeights{ { Weight: 0, }, }, }, { ID: "vendor2", ResourceIDs: []string{"RES_GRP2"}, Weights: utils.DynamicWeights{ { Weight: 0, }, }, }, { ID: "vendor3", FilterIDs: []string{"*gte:~*resources.RES_GRP1.TotalUsage:9"}, ResourceIDs: []string{"RES_GRP2"}, Weights: utils.DynamicWeights{ { Weight: 10, }, }, }, }, } if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.AdminSv1GetRouteProfile, &utils.TenantID{ID: "ROUTE_ACNT_1004", Tenant: "cgrates.org"}, &reply); err != nil { t.Error(err) } else { sort.Slice(reply.Routes, func(i, j int) bool { return reply.Routes[i].ID < reply.Routes[j].ID }) if !reflect.DeepEqual(expRt4, reply) { t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expRt4), utils.ToJSON(reply)) } } // ROUTE_ACNT_1005 expRt5 := &engine.RouteProfile{ ID: "ROUTE_ACNT_1005", Tenant: "cgrates.org", FilterIDs: []string{"*string:~*req.Account:1005"}, Sorting: "*load", SortingParameters: []string{"vendor1:3", "*default:2"}, Routes: []*engine.Route{ { ID: "vendor1", StatIDs: []string{"STATS_VENDOR_1:*sum#1"}, }, { ID: "vendor2", StatIDs: []string{"STATS_VENDOR_2:*sum#1"}, Weights: utils.DynamicWeights{ { Weight: 10, }, }, }, { ID: "vendor3", StatIDs: []string{"STATS_VENDOR_2:*distinct#~*opts.*usage"}, }, }, } if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.AdminSv1GetRouteProfile, &utils.TenantID{ID: "ROUTE_ACNT_1005", Tenant: "cgrates.org"}, &reply); err != nil { t.Error(err) } else { sort.Slice(reply.Routes, func(i, j int) bool { return reply.Routes[i].ID < reply.Routes[j].ID }) sort.Strings(reply.SortingParameters) sort.Strings(expRt5.SortingParameters) if !reflect.DeepEqual(expRt5, reply) { t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expRt5), utils.ToJSON(reply)) } } // ROUTE_STATS1 expRt6 := &engine.RouteProfile{ ID: "ROUTE_HC1", Tenant: "cgrates.org", FilterIDs: []string{"Fltr_tcc"}, Sorting: "*hc", Routes: []*engine.Route{ { ID: "route1", FilterIDs: []string{"*gte:~*resources.RES_GRP2.Available:6"}, RateProfileIDs: []string{"RP_VENDOR2"}, ResourceIDs: []string{"RES_GRP2"}, Weights: utils.DynamicWeights{ { Weight: 20, }, }, }, { ID: "route2", FilterIDs: []string{"*gte:~*resources.RES_GRP1.TotalUsage:9"}, RateProfileIDs: []string{"RP_VENDOR1"}, ResourceIDs: []string{"RES_GRP1"}, Weights: utils.DynamicWeights{ { Weight: 20, }, }, }, { ID: "route3", RateProfileIDs: []string{"RP_VENDOR1"}, ResourceIDs: []string{"RES_GRP2"}, Weights: utils.DynamicWeights{ { Weight: 10, }, }, }, }, } if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.AdminSv1GetRouteProfile, &utils.TenantID{ID: "ROUTE_HC1", Tenant: "cgrates.org"}, &reply); err != nil { t.Error(err) } else { sort.Slice(reply.Routes, func(i, j int) bool { return reply.Routes[i].ID < reply.Routes[j].ID }) if !reflect.DeepEqual(expRt6, reply) { t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expRt6), utils.ToJSON(reply)) } } } func testV1RtsCasesSortingRoutesWeightAccountValue(t *testing.T) { ev := &utils.CGREvent{ ID: "WEIGHT_SORT", Tenant: "cgrates.org", Event: map[string]interface{}{ utils.AccountField: "1001", }, } expSrtdRoutes := &engine.SortedRoutesList{ { ProfileID: "ROUTE_ACNT_1001", Sorting: "*weight", Routes: []*engine.SortedRoute{ { RouteID: "vendor4", SortingData: map[string]interface{}{ utils.Weight: 35., }, }, { RouteID: "vendor2", SortingData: map[string]interface{}{ utils.Weight: 20., }, }, }, }, } var reply *engine.SortedRoutesList if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.RouteSv1GetRoutes, ev, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(expSrtdRoutes, reply) { t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expSrtdRoutes), utils.ToJSON(reply)) } } func testV1RtsCasesSortingRoutesWeightAllRoutes(t *testing.T) { ev := &utils.CGREvent{ ID: "WEIGHT_SORT", Tenant: "cgrates.org", Event: map[string]interface{}{ utils.AccountField: "1001", utils.Destination: "1003", }, } expSrtdRoutes := &engine.SortedRoutesList{ { ProfileID: "ROUTE_ACNT_1001", Sorting: "*weight", Routes: []*engine.SortedRoute{ { RouteID: "vendor3", SortingData: map[string]interface{}{ utils.Weight: 40., }, }, { RouteID: "vendor4", SortingData: map[string]interface{}{ utils.Weight: 35., }, }, { RouteID: "vendor2", SortingData: map[string]interface{}{ utils.Weight: 20., }, }, { RouteID: "vendor1", SortingData: map[string]interface{}{ utils.Weight: 10., }, }, }, }, } var reply *engine.SortedRoutesList if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.RouteSv1GetRoutes, ev, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(expSrtdRoutes, reply) { t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expSrtdRoutes), utils.ToJSON(reply)) } } func testV1RtsCasesSortingRoutesWeightNotMatchingValue(t *testing.T) { //change account 1001 balance for not matching vendor2 argsBal := &utils.ArgsActSetBalance{ Tenant: "cgrates.org", AccountID: "1001", Diktats: []*utils.BalDiktat{ { Path: "*balance.Concrete1.Units", Value: "5", }, }, Reset: true, } var result string if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.AccountSv1ActionSetBalance, argsBal, &result); err != nil { t.Error(err) } else if result != utils.OK { t.Errorf("Unexpected result returned") } ev := &utils.CGREvent{ ID: "WEIGHT_SORT", Tenant: "cgrates.org", Event: map[string]interface{}{ utils.AccountField: "1001", utils.Destination: "1003", }, } expSrtdRoutes := &engine.SortedRoutesList{ { ProfileID: "ROUTE_ACNT_1001", Sorting: "*weight", Routes: []*engine.SortedRoute{ { RouteID: "vendor3", SortingData: map[string]interface{}{ utils.Weight: 40., }, }, { RouteID: "vendor4", SortingData: map[string]interface{}{ utils.Weight: 35., }, }, { RouteID: "vendor1", SortingData: map[string]interface{}{ utils.Weight: 10., }, }, }, }, } var reply *engine.SortedRoutesList if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.RouteSv1GetRoutes, ev, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(expSrtdRoutes, reply) { t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expSrtdRoutes), utils.ToJSON(reply)) } } func testV1RtsCasesSortingRoutesLowestCost(t *testing.T) { ev := &utils.CGREvent{ ID: "LC_SORT", Tenant: "cgrates.org", Event: map[string]interface{}{ utils.AccountField: "1002", utils.Destination: "1003", }, APIOpts: map[string]interface{}{ utils.MetaUsage: "2m30s", }, } expSrtdRoutes := &engine.SortedRoutesList{ { ProfileID: "ROUTE_ACNT_1002", Sorting: "*lc", Routes: []*engine.SortedRoute{ { RouteID: "vendor3", SortingData: map[string]interface{}{ utils.Cost: 0.125, utils.RateProfileID: "RP_VENDOR2", utils.Weight: 10., }, }, { RouteID: "vendor1", SortingData: map[string]interface{}{ utils.Cost: 0.25, utils.RateProfileID: "RP_VENDOR1", utils.Weight: 0., }, }, }, }, } var reply *engine.SortedRoutesList //gonna match one route because the totalUsage by ne-allocated resources is 0 if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.RouteSv1GetRoutes, ev, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(expSrtdRoutes, reply) { t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expSrtdRoutes), utils.ToJSON(reply)) } } func testV1RtsCasesSortingRoutesLowestCostDefaultUsage(t *testing.T) { // default usage given by routes is 1m ev := &utils.CGREvent{ ID: "LC_SORT", Tenant: "cgrates.org", Event: map[string]interface{}{ utils.AccountField: "1002", utils.Destination: "1003", }, APIOpts: map[string]interface{}{ utils.MetaStartTime: "2013-06-01T05:00:00Z", }, } expSrtdRoutes := &engine.SortedRoutesList{ { ProfileID: "ROUTE_ACNT_1002", Sorting: "*lc", Routes: []*engine.SortedRoute{ { RouteID: "vendor3", SortingData: map[string]interface{}{ utils.Cost: 0.05, utils.RateProfileID: "RP_VENDOR2", utils.Weight: 10., }, }, { RouteID: "vendor1", SortingData: map[string]interface{}{ utils.Cost: 0.1, utils.RateProfileID: "RP_VENDOR1", utils.Weight: 0., }, }, { RouteID: "vendor4", SortingData: map[string]interface{}{ utils.Cost: 0.6, utils.RateProfileID: "RP_STANDARD", utils.Weight: 30., }, }, }, }, } var reply *engine.SortedRoutesList //gonna match one route because the totalUsage by ne-allocated resources is 0 if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.RouteSv1GetRoutes, ev, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(expSrtdRoutes, reply) { t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expSrtdRoutes), utils.ToJSON(reply)) } } func testV1RtsCasesSortingRoutesLCSetStatsAndResForMatching(t *testing.T) { //not gonna match our vendor1 filter because 6 > 5 ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: utils.UUIDSha1Prefix(), Event: map[string]interface{}{ "Account": "1002", }, APIOpts: map[string]interface{}{ utils.OptsResourcesUsageID: "651a8db2-4f67-4cf8-b622-169e8a482e51", utils.OptsResourcesUnits: 6, }, } var reply string if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.ResourceSv1AllocateResources, ev, &reply); err != nil { t.Error(err) } else if reply != "RES_GRP1" { t.Errorf("Unexpected reply returned: %s", reply) } //gonna match one stats for matching vendor 2 acd filter var result []string expected := []string{"STATS_VENDOR_2", "STATS_TCC1"} ev1 := &utils.CGREvent{ Tenant: "cgrates.org", ID: "event1", Event: map[string]interface{}{ utils.AccountField: "1004", utils.Category: "vendor2", }, APIOpts: map[string]interface{}{ utils.MetaStartTime: "2013-06-01T05:00:00Z", utils.MetaCost: 1.0, utils.MetaUsage: "2m30s", }, } if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.StatSv1ProcessEvent, ev1, &result); err != nil { t.Error(err) } else { sort.Strings(result) sort.Strings(expected) if !reflect.DeepEqual(result, expected) { t.Errorf("Expecting: %+v, received: %+v", expected, result) } } } func testV1RtsCasesSortingRoutesLowestCostStats(t *testing.T) { //not gonna match vendor1 because of its TotalUsage by allocating resources ev := &utils.CGREvent{ ID: "LC_SORT", Tenant: "cgrates.org", Event: map[string]interface{}{ utils.AccountField: "1002", utils.Destination: "1003", }, APIOpts: map[string]interface{}{ utils.MetaStartTime: "2013-06-01T05:00:00Z", utils.MetaUsage: "2m30s", }, } expSrtdRoutes := &engine.SortedRoutesList{ { ProfileID: "ROUTE_ACNT_1002", Sorting: "*lc", Routes: []*engine.SortedRoute{ { RouteID: "vendor3", SortingData: map[string]interface{}{ utils.Cost: 0.125, utils.RateProfileID: "RP_VENDOR2", utils.Weight: 10., }, }, { RouteID: "vendor2", SortingData: map[string]interface{}{ utils.Cost: 0.125, utils.RateProfileID: "RP_VENDOR2", utils.Weight: 0., }, }, { RouteID: "vendor4", SortingData: map[string]interface{}{ utils.Cost: 1.5, utils.RateProfileID: "RP_STANDARD", utils.Weight: 30., }, }, }, }, } var reply *engine.SortedRoutesList //gonna match one route because the totalUsage by ne-allocated resources is 0 if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.RouteSv1GetRoutes, ev, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(expSrtdRoutes, reply) { t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expSrtdRoutes), utils.ToJSON(reply)) } } func testV1RtsCasesSortingRoutesLowestCosMatchingAllRoutes(t *testing.T) { // deallocate resources for matching vendor1 evRes := &utils.CGREvent{ Tenant: "cgrates.org", ID: utils.UUIDSha1Prefix(), Event: map[string]interface{}{ "Account": "1002", }, APIOpts: map[string]interface{}{ utils.OptsResourcesUsageID: "651a8db2-4f67-4cf8-b622-169e8a482e51", utils.OptsResourcesUnits: 4, }, } var result string if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.ResourceSv1AllocateResources, evRes, &result); err != nil { t.Error(err) } else if result != "RES_GRP1" { t.Errorf("Unexpected result returned: %s", result) } ev := &utils.CGREvent{ ID: "LC_SORT", Tenant: "cgrates.org", Event: map[string]interface{}{ utils.AccountField: "1002", utils.Destination: "1003", }, APIOpts: map[string]interface{}{ utils.MetaStartTime: "2013-06-01T05:00:00Z", utils.MetaUsage: "2m30s", }, } expSrtdRoutes := &engine.SortedRoutesList{ { ProfileID: "ROUTE_ACNT_1002", Sorting: "*lc", Routes: []*engine.SortedRoute{ { RouteID: "vendor3", SortingData: map[string]interface{}{ utils.Cost: 0.125, utils.RateProfileID: "RP_VENDOR2", utils.Weight: 10., }, }, { RouteID: "vendor2", SortingData: map[string]interface{}{ utils.Cost: 0.125, utils.RateProfileID: "RP_VENDOR2", utils.Weight: 0., }, }, { RouteID: "vendor1", SortingData: map[string]interface{}{ utils.Cost: 0.25, utils.RateProfileID: "RP_VENDOR1", utils.Weight: 0., }, }, { RouteID: "vendor4", SortingData: map[string]interface{}{ utils.Cost: 1.5, utils.RateProfileID: "RP_STANDARD", utils.Weight: 30., }, }, }, }, } var reply *engine.SortedRoutesList if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.RouteSv1GetRoutes, ev, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(expSrtdRoutes, reply) { t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expSrtdRoutes), utils.ToJSON(reply)) } } func testV1RtsCasesSortingRoutesLowestCosMaxCost(t *testing.T) { ev := &utils.CGREvent{ ID: "LC_SORT", Tenant: "cgrates.org", Event: map[string]interface{}{ utils.AccountField: "1002", utils.Destination: "1003", }, APIOpts: map[string]interface{}{ utils.MetaStartTime: "2013-06-01T05:00:00Z", utils.OptsRoutesMaxCost: "0.35", utils.MetaUsage: "2m30s", }, } expSrtdRoutes := &engine.SortedRoutesList{ { ProfileID: "ROUTE_ACNT_1002", Sorting: "*lc", Routes: []*engine.SortedRoute{ { RouteID: "vendor3", SortingData: map[string]interface{}{ utils.Cost: 0.125, utils.RateProfileID: "RP_VENDOR2", utils.Weight: 10., }, }, { RouteID: "vendor2", SortingData: map[string]interface{}{ utils.Cost: 0.125, utils.RateProfileID: "RP_VENDOR2", utils.Weight: 0., }, }, { RouteID: "vendor1", SortingData: map[string]interface{}{ utils.Cost: 0.25, utils.RateProfileID: "RP_VENDOR1", utils.Weight: 0., }, }, }, }, } var reply *engine.SortedRoutesList if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.RouteSv1GetRoutes, ev, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(expSrtdRoutes, reply) { t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expSrtdRoutes), utils.ToJSON(reply)) } } func testV1RtsCasesSortingRoutesLowestCosMaxCostNotMatch(t *testing.T) { ev := &utils.CGREvent{ ID: "LC_SORT", Tenant: "cgrates.org", Event: map[string]interface{}{ utils.AccountField: "1002", utils.Destination: "1003", }, APIOpts: map[string]interface{}{ utils.MetaStartTime: "2013-06-01T05:00:00Z", utils.OptsRoutesMaxCost: "0.05", utils.MetaUsage: "2m30s", }, } var reply *engine.SortedRoutesList if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.RouteSv1GetRoutes, ev, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Errorf("Expected %+v, received %+v", utils.ErrNotFound, err) } } func testV1RtsCasesSortingRoutesProcessMetrics(t *testing.T) { //we will process this stats 2 times //Vendor2 expected := []string{"STATS_TCC1", "STATS_VENDOR_2"} ev1 := &utils.CGREvent{ Tenant: "cgrates.org", ID: "event1", Event: map[string]interface{}{ utils.AccountField: "1004", utils.Category: "vendor2", }, APIOpts: map[string]interface{}{ utils.MetaStartTime: "2013-06-01T05:00:00Z", utils.MetaCost: 1.0, utils.MetaUsage: "2m30s", }, } var result []string if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.StatSv1ProcessEvent, ev1, &result); err != nil { t.Error(err) } else { sort.Strings(expected) sort.Strings(result) if !reflect.DeepEqual(result, expected) { t.Errorf("Expecting: %+v, received: %+v", expected, result) } } if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.StatSv1ProcessEvent, ev1, &result); err != nil { t.Error(err) } else { sort.Strings(expected) sort.Strings(result) if !reflect.DeepEqual(result, expected) { t.Errorf("Expecting: %+v, received: %+v", expected, result) } } //Vendor1 expected = []string{"STATS_TCC1", "STATS_VENDOR_1"} ev1 = &utils.CGREvent{ Tenant: "cgrates.org", ID: "event1", Event: map[string]interface{}{ utils.AccountField: "1004", utils.Category: "vendor1", }, APIOpts: map[string]interface{}{ utils.MetaStartTime: "2013-06-01T05:00:00Z", utils.MetaCost: 1.0, utils.MetaUsage: "2m30s", }, } if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.StatSv1ProcessEvent, ev1, &result); err != nil { t.Error(err) } else { sort.Strings(expected) sort.Strings(result) if !reflect.DeepEqual(result, expected) { t.Errorf("Expecting: %+v, received: %+v", expected, result) } } if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.StatSv1ProcessEvent, ev1, &result); err != nil { t.Error(err) } else { sort.Strings(expected) sort.Strings(result) if !reflect.DeepEqual(result, expected) { t.Errorf("Expecting: %+v, received: %+v", expected, result) } } } func testV1RtsCasesSortingRoutesQOS(t *testing.T) { //not gonna match vendor3 because *tcc is not bigger that 6 ev := &utils.CGREvent{ ID: "LC_SORT", Tenant: "cgrates.org", Event: map[string]interface{}{ utils.AccountField: "1003", utils.Destination: "1007", }, APIOpts: map[string]interface{}{ utils.MetaStartTime: "2013-06-01T05:00:00Z", utils.MetaUsage: "50s", }, } expSrtdRoutes := &engine.SortedRoutesList{ { ProfileID: "ROUTE_ACNT_1003", Sorting: "*qos", Routes: []*engine.SortedRoute{ { RouteID: "vendor2", SortingData: map[string]interface{}{ utils.MetaACC: 1., utils.MetaACD: 150. * 1e9, "*sum#1": 3., "*distinct#~*opts.*usage": 1., utils.MetaTCC: 3., utils.MetaTCD: 450. * 1e9, utils.Weight: 0., }, }, { RouteID: "vendor1", SortingData: map[string]interface{}{ utils.MetaACC: 1., utils.MetaACD: 150. * 1e9, "*sum#1": 2., utils.MetaTCC: 2., utils.MetaTCD: 300. * 1e9, utils.Weight: 0., }, }, }, }, } var reply *engine.SortedRoutesList if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.RouteSv1GetRoutes, ev, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(reply, expSrtdRoutes) { t.Errorf("Expecting: %+v \n, received: %+v", utils.ToJSON(expSrtdRoutes), utils.ToJSON(reply)) } } func testV1RtsCasesSortingRoutesQOSAllRoutes(t *testing.T) { // process *tcc metric for matching vendor3 ev1 := &utils.CGREvent{ Tenant: "cgrates.org", ID: "event1", Event: map[string]interface{}{ utils.AccountField: "1004", utils.Category: "vendor1", }, APIOpts: map[string]interface{}{ utils.MetaStartTime: "2013-06-01T05:00:00Z", utils.MetaCost: 10.0, utils.MetaUsage: "2m30s", }, } var result []string if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.StatSv1ProcessEvent, ev1, &result); err != nil { t.Error(err) } // match all 3 routes ev := &utils.CGREvent{ ID: "LC_SORT", Tenant: "cgrates.org", Event: map[string]interface{}{ utils.AccountField: "1003", utils.Destination: "1007", }, APIOpts: map[string]interface{}{ utils.MetaStartTime: "2013-06-01T05:00:00Z", utils.MetaUsage: "50s", }, } expSrtdRoutes := &engine.SortedRoutesList{ { ProfileID: "ROUTE_ACNT_1003", Sorting: "*qos", Routes: []*engine.SortedRoute{ { RouteID: "vendor3", SortingData: map[string]interface{}{ utils.MetaACC: 4., utils.MetaACD: 150. * 1e9, "*sum#1": 3., utils.MetaTCC: 12., utils.MetaTCD: 450. * 1e9, utils.Weight: 20., }, }, { RouteID: "vendor1", SortingData: map[string]interface{}{ utils.MetaACC: 4., utils.MetaACD: 150. * 1e9, "*sum#1": 3., utils.MetaTCC: 12., utils.MetaTCD: 450. * 1e9, utils.Weight: 0., }, }, { RouteID: "vendor2", SortingData: map[string]interface{}{ utils.MetaACC: 1., utils.MetaACD: 150. * 1e9, "*sum#1": 3., "*distinct#~*opts.*usage": 1., utils.MetaTCC: 3., utils.MetaTCD: 450. * 1e9, utils.Weight: 0., }, }, }, }, } var reply *engine.SortedRoutesList if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.RouteSv1GetRoutes, ev, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(reply, expSrtdRoutes) { t.Errorf("Expecting: %+v \n, received: %+v", utils.ToJSON(expSrtdRoutes), utils.ToJSON(reply)) } } func testV1RtsCasesSortingRoutesQOSNotFound(t *testing.T) { ev := &utils.CGREvent{ ID: "LC_SORT", Tenant: "cgrates.org", Event: map[string]interface{}{ utils.AccountField: "1008", utils.Destination: "1007", }, APIOpts: map[string]interface{}{ utils.MetaStartTime: "2013-06-01T05:00:00Z", utils.MetaUsage: "50s", }, } var reply *engine.SortedRoutesList if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.RouteSv1GetRoutes, ev, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Errorf("Expected %+v, received %+v", utils.ErrNotFound, err) } } func testV1RtsCasesSortingRoutesAllocateResources(t *testing.T) { ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: utils.UUIDSha1Prefix(), Event: map[string]interface{}{ "Account": "1002", }, APIOpts: map[string]interface{}{ utils.OptsResourcesUsageID: "651a8db2-4f67-4cf8-b622-169e8a482e51", utils.OptsResourcesUnits: 6, }, } var reply string if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.ResourceSv1AllocateResources, ev, &reply); err != nil { t.Error(err) } else if reply != "RES_GRP1" { t.Errorf("Unexpected reply returned: %s", reply) } ev = &utils.CGREvent{ Tenant: "cgrates.org", ID: utils.UUIDSha1Prefix(), Event: map[string]interface{}{ "Account": "1004", }, APIOpts: map[string]interface{}{ utils.OptsResourcesUsageID: "651a8db2-4f67-4cf8-b622-169e8a482e31", utils.OptsResourcesUnits: 7, }, } if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.ResourceSv1AllocateResources, ev, &reply); err != nil { t.Error(err) } else if reply != "RES_GRP2" { t.Errorf("Unexpected reply returned: %s", reply) } } func testV1RtsCasesSortingRoutesReasNotAllRoutes(t *testing.T) { ev := &utils.CGREvent{ ID: "LC_SORT", Tenant: "cgrates.org", Event: map[string]interface{}{ utils.AccountField: "1004", utils.Destination: "1007", }, } expSrtdRoutes := &engine.SortedRoutesList{ { ProfileID: "ROUTE_ACNT_1004", Sorting: "*reas", Routes: []*engine.SortedRoute{ { RouteID: "vendor1", SortingData: map[string]interface{}{ utils.ResourceUsage: 6.0, utils.Weight: 0., }, }, { RouteID: "vendor2", SortingData: map[string]interface{}{ utils.ResourceUsage: 7.0, utils.Weight: 0., }, }, }, }, } var reply *engine.SortedRoutesList if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.RouteSv1GetRoutes, ev, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(reply, expSrtdRoutes) { t.Errorf("Expecting: %+v \n, received: %+v", utils.ToJSON(expSrtdRoutes), utils.ToJSON(reply)) } } func testV1RtsCasesSortingRoutesReasAllRoutes(t *testing.T) { evRs := &utils.CGREvent{ Tenant: "cgrates.org", ID: utils.UUIDSha1Prefix(), Event: map[string]interface{}{ "Account": "1002", }, APIOpts: map[string]interface{}{ utils.OptsResourcesUsageID: "651a8db2-4f67-4cf8-b622-169e8a482e51", utils.OptsResourcesUnits: 9, }, } var replyStr string if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.ResourceSv1AllocateResources, evRs, &replyStr); err != nil { t.Error(err) } else if replyStr != "RES_GRP1" { t.Errorf("Unexpected reply returned: %s", replyStr) } //allocate more resources for matching ev := &utils.CGREvent{ ID: "LC_SORT", Tenant: "cgrates.org", Event: map[string]interface{}{ utils.AccountField: "1004", utils.Destination: "1007", }, } expSrtdRoutes := &engine.SortedRoutesList{ { ProfileID: "ROUTE_ACNT_1004", Sorting: "*reas", Routes: []*engine.SortedRoute{ { RouteID: "vendor3", SortingData: map[string]interface{}{ utils.ResourceUsage: 7.0, utils.Weight: 10., }, }, { RouteID: "vendor2", SortingData: map[string]interface{}{ utils.ResourceUsage: 7.0, utils.Weight: 0., }, }, { RouteID: "vendor1", SortingData: map[string]interface{}{ utils.ResourceUsage: 9.0, utils.Weight: 0., }, }, }, }, } var reply *engine.SortedRoutesList if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.RouteSv1GetRoutes, ev, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(reply, expSrtdRoutes) { t.Errorf("Expecting: %+v \n, received: %+v", utils.ToJSON(expSrtdRoutes), utils.ToJSON(reply)) } } func testV1RtsCasesRoutesProcessStatsForLoadRtsSorting(t *testing.T) { // "STATS_VENDOR_1" var reply []string expected := []string{"STATS_VENDOR_1", "STATS_TCC1"} ev1 := &utils.CGREvent{ Tenant: "cgrates.org", ID: "event1", Event: map[string]interface{}{ utils.Category: "vendor1", }, APIOpts: map[string]interface{}{ utils.MetaStartTime: "2013-06-01T05:00:00Z", utils.MetaCost: 1.8, utils.MetaUsage: "1m20s", }, } if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.StatSv1ProcessEvent, ev1, &reply); err != nil { t.Error(err) } else if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.StatSv1ProcessEvent, ev1, &reply); err != nil { t.Error(err) } else { sort.Strings(expected) sort.Strings(reply) if !reflect.DeepEqual(reply, expected) { t.Errorf("Expecting: %+v, received: %+v", expected, reply) } } // different usage for *distinct metric ev1 = &utils.CGREvent{ Tenant: "cgrates.org", ID: "event1", Event: map[string]interface{}{ utils.Category: "vendor1", }, APIOpts: map[string]interface{}{ utils.MetaUsage: "20s", utils.MetaStartTime: "2013-06-01T05:00:00Z", utils.MetaCost: 1.8, }, } if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.StatSv1ProcessEvent, ev1, &reply); err != nil { t.Error(err) } else if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.StatSv1ProcessEvent, ev1, &reply); err != nil { t.Error(err) } else { sort.Strings(expected) sort.Strings(reply) if !reflect.DeepEqual(reply, expected) { t.Errorf("Expecting: %+v, received: %+v", expected, reply) } } // "STATS_VENDOR_2" expected = []string{"STATS_VENDOR_2", "STATS_TCC1"} ev1 = &utils.CGREvent{ Tenant: "cgrates.org", ID: "event1", Event: map[string]interface{}{ utils.Category: "vendor2", }, APIOpts: map[string]interface{}{ utils.MetaStartTime: "2013-06-01T05:00:00Z", utils.MetaCost: 0.77, utils.MetaUsage: "30s", }, } if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.StatSv1ProcessEvent, ev1, &reply); err != nil { t.Error(err) } else { sort.Strings(expected) sort.Strings(reply) if !reflect.DeepEqual(reply, expected) { t.Errorf("Expecting: %+v, received: %+v", expected, reply) } } } func testV1RtsCasesRoutesLoadRtsSorting(t *testing.T) { ev := &utils.CGREvent{ ID: "LC_SORT", Tenant: "cgrates.org", Event: map[string]interface{}{ utils.AccountField: "1005", //utils.Destination: "1007", }, } expSrtdRoutes := &engine.SortedRoutesList{ { ProfileID: "ROUTE_ACNT_1005", Sorting: "*load", Routes: []*engine.SortedRoute{ { RouteID: "vendor3", SortingData: map[string]interface{}{ utils.Load: 2., utils.Ratio: "2", utils.Weight: 0., }, }, { RouteID: "vendor2", SortingData: map[string]interface{}{ utils.Load: 4., utils.Ratio: "2", utils.Weight: 10., }, }, { RouteID: "vendor1", SortingData: map[string]interface{}{ utils.Load: 7., utils.Ratio: "3", utils.Weight: 0., }, }, }, }, } var reply *engine.SortedRoutesList if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.RouteSv1GetRoutes, ev, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(reply, expSrtdRoutes) { t.Errorf("Expecting: %+v \n, received: %+v", utils.ToJSON(expSrtdRoutes), utils.ToJSON(reply)) } } func testV1RtsCasesSortRoutesHigherCostV2V3(t *testing.T) { ev := &utils.CGREvent{ ID: "LC_SORT", Tenant: "cgrates.org", Event: map[string]interface{}{ utils.AccountField: "1008", utils.Destination: "1007", }, APIOpts: map[string]interface{}{ utils.MetaUsage: "3m25s", }, } expSrtdRoutes := &engine.SortedRoutesList{ { ProfileID: "ROUTE_HC1", Sorting: "*hc", Routes: []*engine.SortedRoute{ { RouteID: "route2", SortingData: map[string]interface{}{ utils.Cost: 0.3416666666666667, utils.RateProfileID: "RP_VENDOR1", utils.Weight: 20., }, }, { RouteID: "route3", SortingData: map[string]interface{}{ utils.Cost: 0.3416666666666667, utils.RateProfileID: "RP_VENDOR1", utils.Weight: 10., }, }, }, }, } var reply *engine.SortedRoutesList if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.RouteSv1GetRoutes, ev, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(reply, expSrtdRoutes) { t.Errorf("Expecting: %+v \n, received: %+v", utils.ToJSON(expSrtdRoutes), utils.ToJSON(reply)) } } func testV1RtsCasesSortRoutesHigherCostAllocateRes(t *testing.T) { // to match route 1, RES_GRP2 must have *gte available 6 resources // first we have to remove them var result string evRs := &utils.CGREvent{ Tenant: "cgrates.org", ID: utils.UUIDSha1Prefix(), Event: map[string]interface{}{ "Account": "1004", }, APIOpts: map[string]interface{}{ utils.OptsResourcesUsageID: "651a8db2-4f67-4cf8-b622-169e8a482e31", utils.OptsResourcesUnits: 7, }, } if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.ResourceSv1ReleaseResources, evRs, &result); err != nil { t.Error(err) } else if result != utils.OK { t.Errorf("Unexpected result returned: %s", result) } evRs = &utils.CGREvent{ Tenant: "cgrates.org", ID: utils.UUIDSha1Prefix(), Event: map[string]interface{}{ "Account": "1002", }, APIOpts: map[string]interface{}{ utils.OptsResourcesUsageID: "651a8db2-4f67-4cf8-b622-169e8a482e51", utils.OptsResourcesUnits: 7, }, } if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.ResourceSv1ReleaseResources, evRs, &result); err != nil { t.Error(err) } else if result != utils.OK { t.Errorf("Unexpected result returned: %s", result) } evRs = &utils.CGREvent{ Tenant: "cgrates.org", ID: utils.UUIDSha1Prefix(), Event: map[string]interface{}{ "Account": "1004", }, APIOpts: map[string]interface{}{ utils.OptsResourcesUsageID: "651a8db2-4f67-4cf8-b622-169e8a482e31", utils.OptsResourcesUnits: 1, }, } if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.ResourceSv1AllocateResources, evRs, &result); err != nil { t.Error(err) } else if result != "RES_GRP2" { t.Errorf("Unexpected result returned: %s", result) } // also, to not match route2, totalUsage of RES_GRP1 must be lower than 9 evRs = &utils.CGREvent{ Tenant: "cgrates.org", ID: utils.UUIDSha1Prefix(), Event: map[string]interface{}{ "Account": "1002", }, APIOpts: map[string]interface{}{ utils.OptsResourcesUsageID: "651a8db2-4f67-4cf8-b622-169e8a482e51", utils.OptsResourcesUnits: 4, }, } if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.ResourceSv1AllocateResources, evRs, &result); err != nil { t.Error(err) } else if result != "RES_GRP1" { t.Errorf("Unexpected result returned: %s", result) } } func testV1RtsCasesSortRoutesHigherCostV1V3(t *testing.T) { ev := &utils.CGREvent{ ID: "LC_SORT", Tenant: "cgrates.org", Event: map[string]interface{}{ utils.AccountField: "1008", utils.Destination: "1007", }, APIOpts: map[string]interface{}{ utils.MetaUsage: "3m25s", }, } expSrtdRoutes := &engine.SortedRoutesList{ { ProfileID: "ROUTE_HC1", Sorting: "*hc", Routes: []*engine.SortedRoute{ { RouteID: "route3", SortingData: map[string]interface{}{ utils.Cost: 0.3416666666666667, utils.RateProfileID: "RP_VENDOR1", utils.Weight: 10., }, }, { RouteID: "route1", SortingData: map[string]interface{}{ utils.Cost: 0.1708333333333333, utils.RateProfileID: "RP_VENDOR2", utils.Weight: 20., }, }, }, }, } var reply *engine.SortedRoutesList if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.RouteSv1GetRoutes, ev, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(reply, expSrtdRoutes) { t.Errorf("Expecting: %+v \n, received: %+v", utils.ToJSON(expSrtdRoutes), utils.ToJSON(reply)) } } func testV1RtsCasesSortRoutesHigherCostAllRoutes(t *testing.T) { //allocate for matching all routes evRs := &utils.CGREvent{ Tenant: "cgrates.org", ID: utils.UUIDSha1Prefix(), Event: map[string]interface{}{ "Account": "1002", }, APIOpts: map[string]interface{}{ utils.OptsResourcesUsageID: "651a8db2-4f67-4cf8-b622-169e8a482e51", utils.OptsResourcesUnits: 9, }, } var result string if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.ResourceSv1AllocateResources, evRs, &result); err != nil { t.Error(err) } else if result != "RES_GRP1" { t.Errorf("Unexpected result returned: %s", result) } ev := &utils.CGREvent{ ID: "LC_SORT", Tenant: "cgrates.org", Event: map[string]interface{}{ utils.AccountField: "1008", utils.Destination: "1007", }, APIOpts: map[string]interface{}{ utils.MetaUsage: "3m25s", }, } expSrtdRoutes := &engine.SortedRoutesList{ { ProfileID: "ROUTE_HC1", Sorting: "*hc", Routes: []*engine.SortedRoute{ { RouteID: "route2", SortingData: map[string]interface{}{ utils.Cost: 0.3416666666666667, utils.RateProfileID: "RP_VENDOR1", utils.Weight: 20., }, }, { RouteID: "route3", SortingData: map[string]interface{}{ utils.Cost: 0.3416666666666667, utils.RateProfileID: "RP_VENDOR1", utils.Weight: 10., }, }, { RouteID: "route1", SortingData: map[string]interface{}{ utils.Cost: 0.1708333333333333, utils.RateProfileID: "RP_VENDOR2", utils.Weight: 20., }, }, }, }, } var reply *engine.SortedRoutesList if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.RouteSv1GetRoutes, ev, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(reply, expSrtdRoutes) { t.Errorf("Expecting: %+v \n, received: %+v", utils.ToJSON(expSrtdRoutes), utils.ToJSON(reply)) } } func testV1RtsCasesSortingRoutesLowestCostAccounts(t *testing.T) { //gonna match all routes from ROUTE_LCR_ACCOUNTS ev := &utils.CGREvent{ ID: "LC_SORT", Tenant: "cgrates.org", Event: map[string]interface{}{ utils.AccountField: "acnt22", utils.Destination: "104423", }, APIOpts: map[string]interface{}{ utils.MetaStartTime: "2013-06-01T05:00:00Z", utils.MetaUsage: "50s", }, } expSrtdRoutes := &engine.SortedRoutesList{ { ProfileID: "ROUTE_LCR_ACCOUNTS", Sorting: "*lc", Routes: []*engine.SortedRoute{ { RouteID: "route4", SortingData: map[string]interface{}{ utils.Cost: nil, utils.AccountIDs: []interface{}{"ACCNT_ROUTES2"}, utils.Weight: 55., }, }, { RouteID: "route1", SortingData: map[string]interface{}{ utils.Cost: 5., utils.AccountIDs: []interface{}{"ACCNT_ROUTES1"}, utils.Weight: 20., }, }, { RouteID: "route2", SortingData: map[string]interface{}{ utils.Cost: 5., utils.AccountIDs: []interface{}{"ACCNT_ROUTES1"}, utils.Weight: 15., }, }, { RouteID: "route3", SortingData: map[string]interface{}{ utils.Cost: 5., utils.AccountIDs: []interface{}{"ACCNT_ROUTES1"}, utils.Weight: 10., }, }, }, }, } var reply *engine.SortedRoutesList //gonna match one route because the totalUsage by ne-allocated resources is 0 if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.RouteSv1GetRoutes, ev, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(expSrtdRoutes, reply) { t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expSrtdRoutes), utils.ToJSON(reply)) } } func testV1RtsCaseStopEngine(t *testing.T) { if err := engine.KillEngine(100); err != nil { t.Error(err) } }