//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 Affero 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 Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>
*/

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/routes"
	"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,
		testV1RtsCaseFlushDBs,

		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(*utils.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]any{
			utils.AccountField: "acnt22",
			utils.Destination:  "104423",
		},
		APIOpts: map[string]any{
			utils.MetaStartTime: "2013-06-01T05:00:00Z",
			utils.MetaUsage:     "50s",
		},
	}
	expSrtdRoutes := &routes.SortedRoutesList{
		{
			ProfileID: "ROUTE_LCR_ACCOUNTS",
			Sorting:   "*lc",
			Routes: []*routes.SortedRoute{
				{
					RouteID: "route4",
					SortingData: map[string]any{
						utils.Cost:       nil,
						utils.AccountIDs: []any{"ACCNT_ROUTES2"},
						utils.Weight:     55.,
					},
				},
				{
					RouteID: "route1",
					SortingData: map[string]any{
						utils.Cost:       5.,
						utils.AccountIDs: []any{"ACCNT_ROUTES1"},
						utils.Weight:     20.,
					},
				},
				{
					RouteID: "route2",
					SortingData: map[string]any{
						utils.Cost:       5.,
						utils.AccountIDs: []any{"ACCNT_ROUTES1"},
						utils.Weight:     15.,
					},
				},
				{
					RouteID: "route3",
					SortingData: map[string]any{
						utils.Cost:       5.,
						utils.AccountIDs: []any{"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 *routes.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 *utils.DBType {
	case utils.MetaInternal:
		rtsCaseSv1ConfDIR = "routes_cases_internal"
	case utils.MetaRedis:
		t.SkipNow()
	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(*utils.DataDir, "conf", "samples", rtsCaseSv1ConfDIR)
	if rtsCaseSv1Cfg, err = config.NewCGRConfigFromPath(context.Background(), rtsCaseSv1CfgPath); err != nil {
		t.Error(err)
	}
}

func testV1RtsCaseFlushDBs(t *testing.T) {
	if err := engine.InitDB(rtsCaseSv1Cfg); err != nil {
		t.Fatal(err)
	}
}

func testV1RtsCaseStartEngine(t *testing.T) {
	if _, err := engine.StopStartEngine(rtsCaseSv1CfgPath, *utils.WaitRater); err != nil {
		t.Fatal(err)
	}
}

func testV1RtsCaseRpcConn(t *testing.T) {
	rtsCaseSv1BiRpc = engine.NewRPCClient(t, rtsCaseSv1Cfg.ListenCfg(), *utils.Encoding)
}

func testV1RtsCaseFromFolder(t *testing.T) {
	caching := utils.MetaReload
	if rtsCaseSv1Cfg.DbCfg().DBConns[utils.MetaDefault].Type == utils.MetaInternal {
		caching = utils.MetaNone
	}
	var reply string
	if err := rtsCaseSv1BiRpc.Call(context.Background(), utils.LoaderSv1Run,
		&loaders.ArgsProcessFolder{
			// StopOnError: true,
			APIOpts: map[string]any{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 := &utils.RouteProfile{
		ID:        "ROUTE_ACNT_1001",
		Tenant:    "cgrates.org",
		FilterIDs: []string{"*string:~*req.Account:1001"},
		Sorting:   "*weight",
		Routes: []*utils.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 *utils.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 := &utils.RouteProfile{
		ID:        "ROUTE_ACNT_1002",
		Tenant:    "cgrates.org",
		FilterIDs: []string{"*string:~*req.Account:1002"},
		Sorting:   "*lc",
		Routes: []*utils.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 := &utils.RouteProfile{
		ID:                "ROUTE_ACNT_1003",
		Tenant:            "cgrates.org",
		FilterIDs:         []string{"*string:~*req.Account:1003"},
		Sorting:           "*qos",
		SortingParameters: []string{"*acd", "*tcc"},
		Routes: []*utils.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 := &utils.RouteProfile{
		ID:        "ROUTE_ACNT_1004",
		Tenant:    "cgrates.org",
		FilterIDs: []string{"*string:~*req.Account:1004"},
		Sorting:   "*reas",
		Routes: []*utils.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 := &utils.RouteProfile{
		ID:                "ROUTE_ACNT_1005",
		Tenant:            "cgrates.org",
		FilterIDs:         []string{"*string:~*req.Account:1005"},
		Sorting:           "*load",
		SortingParameters: []string{"vendor1:3", "*default:2"},
		Routes: []*utils.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 := &utils.RouteProfile{
		ID:        "ROUTE_HC1",
		Tenant:    "cgrates.org",
		FilterIDs: []string{"Fltr_tcc"},
		Sorting:   "*hc",
		Routes: []*utils.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]any{
			utils.AccountField: "1001",
		},
	}
	expSrtdRoutes := &routes.SortedRoutesList{
		{
			ProfileID: "ROUTE_ACNT_1001",
			Sorting:   "*weight",
			Routes: []*routes.SortedRoute{
				{
					RouteID: "vendor4",
					SortingData: map[string]any{
						utils.Weight: 35.,
					},
				},
				{
					RouteID: "vendor2",
					SortingData: map[string]any{
						utils.Weight: 20.,
					},
				},
			},
		},
	}
	var reply *routes.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]any{
			utils.AccountField: "1001",
			utils.Destination:  "1003",
		},
	}
	expSrtdRoutes := &routes.SortedRoutesList{
		{
			ProfileID: "ROUTE_ACNT_1001",
			Sorting:   "*weight",
			Routes: []*routes.SortedRoute{
				{
					RouteID: "vendor3",
					SortingData: map[string]any{
						utils.Weight: 40.,
					},
				},
				{
					RouteID: "vendor4",
					SortingData: map[string]any{
						utils.Weight: 35.,
					},
				},
				{
					RouteID: "vendor2",
					SortingData: map[string]any{
						utils.Weight: 20.,
					},
				},
				{
					RouteID: "vendor1",
					SortingData: map[string]any{
						utils.Weight: 10.,
					},
				},
			},
		},
	}
	var reply *routes.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]any{
			utils.AccountField: "1001",
			utils.Destination:  "1003",
		},
	}
	expSrtdRoutes := &routes.SortedRoutesList{
		{
			ProfileID: "ROUTE_ACNT_1001",
			Sorting:   "*weight",
			Routes: []*routes.SortedRoute{
				{
					RouteID: "vendor3",
					SortingData: map[string]any{
						utils.Weight: 40.,
					},
				},
				{
					RouteID: "vendor4",
					SortingData: map[string]any{
						utils.Weight: 35.,
					},
				},
				{
					RouteID: "vendor1",
					SortingData: map[string]any{
						utils.Weight: 10.,
					},
				},
			},
		},
	}
	var reply *routes.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]any{
			utils.AccountField: "1002",
			utils.Destination:  "1003",
		},
		APIOpts: map[string]any{
			utils.MetaUsage: "2m30s",
		},
	}
	expSrtdRoutes := &routes.SortedRoutesList{
		{
			ProfileID: "ROUTE_ACNT_1002",
			Sorting:   "*lc",
			Routes: []*routes.SortedRoute{
				{
					RouteID: "vendor3",
					SortingData: map[string]any{
						utils.Cost:          0.125,
						utils.RateProfileID: "RP_VENDOR2",
						utils.Weight:        10.,
					},
				},
				{
					RouteID: "vendor1",
					SortingData: map[string]any{
						utils.Cost:          0.25,
						utils.RateProfileID: "RP_VENDOR1",
						utils.Weight:        0.,
					},
				},
			},
		},
	}
	var reply *routes.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]any{
			utils.AccountField: "1002",
			utils.Destination:  "1003",
		},
		APIOpts: map[string]any{
			utils.MetaStartTime: "2013-06-01T05:00:00Z",
		},
	}
	expSrtdRoutes := &routes.SortedRoutesList{
		{
			ProfileID: "ROUTE_ACNT_1002",
			Sorting:   "*lc",
			Routes: []*routes.SortedRoute{
				{
					RouteID: "vendor3",
					SortingData: map[string]any{
						utils.Cost:          0.05,
						utils.RateProfileID: "RP_VENDOR2",
						utils.Weight:        10.,
					},
				},
				{
					RouteID: "vendor1",
					SortingData: map[string]any{
						utils.Cost:          0.1,
						utils.RateProfileID: "RP_VENDOR1",
						utils.Weight:        0.,
					},
				},
				{
					RouteID: "vendor4",
					SortingData: map[string]any{
						utils.Cost:          0.6,
						utils.RateProfileID: "RP_STANDARD",
						utils.Weight:        30.,
					},
				},
			},
		},
	}
	var reply *routes.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]any{
			"Account": "1002",
		},
		APIOpts: map[string]any{
			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]any{
			utils.AccountField: "1004",
			utils.Category:     "vendor2",
		},
		APIOpts: map[string]any{
			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]any{
			utils.AccountField: "1002",
			utils.Destination:  "1003",
		},
		APIOpts: map[string]any{
			utils.MetaStartTime: "2013-06-01T05:00:00Z",
			utils.MetaUsage:     "2m30s",
		},
	}
	expSrtdRoutes := &routes.SortedRoutesList{
		{
			ProfileID: "ROUTE_ACNT_1002",
			Sorting:   "*lc",
			Routes: []*routes.SortedRoute{
				{
					RouteID: "vendor3",
					SortingData: map[string]any{
						utils.Cost:          0.125,
						utils.RateProfileID: "RP_VENDOR2",
						utils.Weight:        10.,
					},
				},
				{
					RouteID: "vendor2",
					SortingData: map[string]any{
						utils.Cost:          0.125,
						utils.RateProfileID: "RP_VENDOR2",
						utils.Weight:        0.,
					},
				},
				{
					RouteID: "vendor4",
					SortingData: map[string]any{
						utils.Cost:          1.5,
						utils.RateProfileID: "RP_STANDARD",
						utils.Weight:        30.,
					},
				},
			},
		},
	}
	var reply *routes.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]any{
			"Account": "1002",
		},
		APIOpts: map[string]any{
			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]any{
			utils.AccountField: "1002",
			utils.Destination:  "1003",
		},
		APIOpts: map[string]any{
			utils.MetaStartTime: "2013-06-01T05:00:00Z",
			utils.MetaUsage:     "2m30s",
		},
	}
	expSrtdRoutes := &routes.SortedRoutesList{
		{
			ProfileID: "ROUTE_ACNT_1002",
			Sorting:   "*lc",
			Routes: []*routes.SortedRoute{
				{
					RouteID: "vendor3",
					SortingData: map[string]any{
						utils.Cost:          0.125,
						utils.RateProfileID: "RP_VENDOR2",
						utils.Weight:        10.,
					},
				},
				{
					RouteID: "vendor2",
					SortingData: map[string]any{
						utils.Cost:          0.125,
						utils.RateProfileID: "RP_VENDOR2",
						utils.Weight:        0.,
					},
				},
				{
					RouteID: "vendor1",
					SortingData: map[string]any{
						utils.Cost:          0.25,
						utils.RateProfileID: "RP_VENDOR1",
						utils.Weight:        0.,
					},
				},
				{
					RouteID: "vendor4",
					SortingData: map[string]any{
						utils.Cost:          1.5,
						utils.RateProfileID: "RP_STANDARD",
						utils.Weight:        30.,
					},
				},
			},
		},
	}
	var reply *routes.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]any{
			utils.AccountField: "1002",
			utils.Destination:  "1003",
		},
		APIOpts: map[string]any{
			utils.MetaStartTime:     "2013-06-01T05:00:00Z",
			utils.OptsRoutesMaxCost: "0.35",
			utils.MetaUsage:         "2m30s",
		},
	}
	expSrtdRoutes := &routes.SortedRoutesList{
		{
			ProfileID: "ROUTE_ACNT_1002",
			Sorting:   "*lc",
			Routes: []*routes.SortedRoute{
				{
					RouteID: "vendor3",
					SortingData: map[string]any{
						utils.Cost:          0.125,
						utils.RateProfileID: "RP_VENDOR2",
						utils.Weight:        10.,
					},
				},
				{
					RouteID: "vendor2",
					SortingData: map[string]any{
						utils.Cost:          0.125,
						utils.RateProfileID: "RP_VENDOR2",
						utils.Weight:        0.,
					},
				},
				{
					RouteID: "vendor1",
					SortingData: map[string]any{
						utils.Cost:          0.25,
						utils.RateProfileID: "RP_VENDOR1",
						utils.Weight:        0.,
					},
				},
			},
		},
	}
	var reply *routes.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]any{
			utils.AccountField: "1002",
			utils.Destination:  "1003",
		},
		APIOpts: map[string]any{
			utils.MetaStartTime:     "2013-06-01T05:00:00Z",
			utils.OptsRoutesMaxCost: "0.05",
			utils.MetaUsage:         "2m30s",
		},
	}
	var reply *routes.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]any{
			utils.AccountField: "1004",
			utils.Category:     "vendor2",
		},
		APIOpts: map[string]any{
			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]any{
			utils.AccountField: "1004",
			utils.Category:     "vendor1",
		},
		APIOpts: map[string]any{
			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]any{
			utils.AccountField: "1003",
			utils.Destination:  "1007",
		},
		APIOpts: map[string]any{
			utils.MetaStartTime: "2013-06-01T05:00:00Z",
			utils.MetaUsage:     "50s",
		},
	}
	expSrtdRoutes := &routes.SortedRoutesList{
		{
			ProfileID: "ROUTE_ACNT_1003",
			Sorting:   "*qos",
			Routes: []*routes.SortedRoute{
				{
					RouteID: "vendor2",
					SortingData: map[string]any{
						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]any{
						utils.MetaACC: 1.,
						utils.MetaACD: 150. * 1e9,
						"*sum#1":      2.,
						utils.MetaTCC: 2.,
						utils.MetaTCD: 300. * 1e9,
						utils.Weight:  0.,
					},
				},
			},
		},
	}
	var reply *routes.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]any{
			utils.AccountField: "1004",
			utils.Category:     "vendor1",
		},
		APIOpts: map[string]any{
			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]any{
			utils.AccountField: "1003",
			utils.Destination:  "1007",
		},
		APIOpts: map[string]any{
			utils.MetaStartTime: "2013-06-01T05:00:00Z",
			utils.MetaUsage:     "50s",
		},
	}
	expSrtdRoutes := &routes.SortedRoutesList{
		{
			ProfileID: "ROUTE_ACNT_1003",
			Sorting:   "*qos",
			Routes: []*routes.SortedRoute{
				{
					RouteID: "vendor3",
					SortingData: map[string]any{
						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]any{
						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]any{
						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 *routes.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]any{
			utils.AccountField: "1008",
			utils.Destination:  "1007",
		},
		APIOpts: map[string]any{
			utils.MetaStartTime: "2013-06-01T05:00:00Z",
			utils.MetaUsage:     "50s",
		},
	}
	var reply *routes.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]any{
			"Account": "1002",
		},
		APIOpts: map[string]any{
			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]any{
			"Account": "1004",
		},
		APIOpts: map[string]any{
			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]any{
			utils.AccountField: "1004",
			utils.Destination:  "1007",
		},
	}
	expSrtdRoutes := &routes.SortedRoutesList{
		{
			ProfileID: "ROUTE_ACNT_1004",
			Sorting:   "*reas",
			Routes: []*routes.SortedRoute{
				{
					RouteID: "vendor1",
					SortingData: map[string]any{
						utils.ResourceUsageStr: 6.0,
						utils.Weight:           0.,
					},
				},
				{
					RouteID: "vendor2",
					SortingData: map[string]any{
						utils.ResourceUsageStr: 7.0,
						utils.Weight:           0.,
					},
				},
			},
		},
	}
	var reply *routes.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]any{
			"Account": "1002",
		},
		APIOpts: map[string]any{
			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]any{
			utils.AccountField: "1004",
			utils.Destination:  "1007",
		},
	}
	expSrtdRoutes := &routes.SortedRoutesList{
		{
			ProfileID: "ROUTE_ACNT_1004",
			Sorting:   "*reas",
			Routes: []*routes.SortedRoute{
				{
					RouteID: "vendor3",
					SortingData: map[string]any{
						utils.ResourceUsageStr: 7.0,
						utils.Weight:           10.,
					},
				},
				{
					RouteID: "vendor2",
					SortingData: map[string]any{
						utils.ResourceUsageStr: 7.0,
						utils.Weight:           0.,
					},
				},
				{
					RouteID: "vendor1",
					SortingData: map[string]any{
						utils.ResourceUsageStr: 9.0,
						utils.Weight:           0.,
					},
				},
			},
		},
	}
	var reply *routes.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]any{
			utils.Category: "vendor1",
		},
		APIOpts: map[string]any{
			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]any{
			utils.Category: "vendor1",
		},
		APIOpts: map[string]any{
			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]any{
			utils.Category: "vendor2",
		},
		APIOpts: map[string]any{
			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]any{
			utils.AccountField: "1005",
			//utils.Destination:  "1007",
		},
	}
	expSrtdRoutes := &routes.SortedRoutesList{
		{
			ProfileID: "ROUTE_ACNT_1005",
			Sorting:   "*load",
			Routes: []*routes.SortedRoute{
				{
					RouteID: "vendor3",
					SortingData: map[string]any{
						utils.Load:   2.,
						utils.Ratio:  "2",
						utils.Weight: 0.,
					},
				},
				{
					RouteID: "vendor2",
					SortingData: map[string]any{
						utils.Load:   4.,
						utils.Ratio:  "2",
						utils.Weight: 10.,
					},
				},
				{
					RouteID: "vendor1",
					SortingData: map[string]any{
						utils.Load:   7.,
						utils.Ratio:  "3",
						utils.Weight: 0.,
					},
				},
			},
		},
	}
	var reply *routes.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]any{
			utils.AccountField: "1008",
			utils.Destination:  "1007",
		},
		APIOpts: map[string]any{
			utils.MetaUsage: "3m25s",
		},
	}
	expSrtdRoutes := &routes.SortedRoutesList{
		{
			ProfileID: "ROUTE_HC1",
			Sorting:   "*hc",
			Routes: []*routes.SortedRoute{
				{
					RouteID: "route2",
					SortingData: map[string]any{
						utils.Cost:          0.3416666666666667,
						utils.RateProfileID: "RP_VENDOR1",
						utils.Weight:        20.,
					},
				},
				{
					RouteID: "route3",
					SortingData: map[string]any{
						utils.Cost:          0.3416666666666667,
						utils.RateProfileID: "RP_VENDOR1",
						utils.Weight:        10.,
					},
				},
			},
		},
	}
	var reply *routes.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]any{
			"Account": "1004",
		},
		APIOpts: map[string]any{
			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]any{
			"Account": "1002",
		},
		APIOpts: map[string]any{
			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]any{
			"Account": "1004",
		},
		APIOpts: map[string]any{
			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]any{
			"Account": "1002",
		},
		APIOpts: map[string]any{
			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]any{
			utils.AccountField: "1008",
			utils.Destination:  "1007",
		},
		APIOpts: map[string]any{
			utils.MetaUsage: "3m25s",
		},
	}
	expSrtdRoutes := &routes.SortedRoutesList{
		{
			ProfileID: "ROUTE_HC1",
			Sorting:   "*hc",
			Routes: []*routes.SortedRoute{
				{
					RouteID: "route3",
					SortingData: map[string]any{
						utils.Cost:          0.3416666666666667,
						utils.RateProfileID: "RP_VENDOR1",
						utils.Weight:        10.,
					},
				},
				{
					RouteID: "route1",
					SortingData: map[string]any{
						utils.Cost:          0.1708333333333333,
						utils.RateProfileID: "RP_VENDOR2",
						utils.Weight:        20.,
					},
				},
			},
		},
	}
	var reply *routes.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]any{
			"Account": "1002",
		},
		APIOpts: map[string]any{
			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]any{
			utils.AccountField: "1008",
			utils.Destination:  "1007",
		},
		APIOpts: map[string]any{
			utils.MetaUsage: "3m25s",
		},
	}
	expSrtdRoutes := &routes.SortedRoutesList{
		{
			ProfileID: "ROUTE_HC1",
			Sorting:   "*hc",
			Routes: []*routes.SortedRoute{
				{
					RouteID: "route2",
					SortingData: map[string]any{
						utils.Cost:          0.3416666666666667,
						utils.RateProfileID: "RP_VENDOR1",
						utils.Weight:        20.,
					},
				},
				{
					RouteID: "route3",
					SortingData: map[string]any{
						utils.Cost:          0.3416666666666667,
						utils.RateProfileID: "RP_VENDOR1",
						utils.Weight:        10.,
					},
				},
				{
					RouteID: "route1",
					SortingData: map[string]any{
						utils.Cost:          0.1708333333333333,
						utils.RateProfileID: "RP_VENDOR2",
						utils.Weight:        20.,
					},
				},
			},
		},
	}
	var reply *routes.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]any{
			utils.AccountField: "acnt22",
			utils.Destination:  "104423",
		},
		APIOpts: map[string]any{
			utils.MetaStartTime: "2013-06-01T05:00:00Z",
			utils.MetaUsage:     "50s",
		},
	}
	expSrtdRoutes := &routes.SortedRoutesList{
		{
			ProfileID: "ROUTE_LCR_ACCOUNTS",
			Sorting:   "*lc",
			Routes: []*routes.SortedRoute{
				{
					RouteID: "route4",
					SortingData: map[string]any{
						utils.Cost:       nil,
						utils.AccountIDs: []any{"ACCNT_ROUTES2"},
						utils.Weight:     55.,
					},
				},
				{
					RouteID: "route1",
					SortingData: map[string]any{
						utils.Cost:       5.,
						utils.AccountIDs: []any{"ACCNT_ROUTES1"},
						utils.Weight:     20.,
					},
				},
				{
					RouteID: "route2",
					SortingData: map[string]any{
						utils.Cost:       5.,
						utils.AccountIDs: []any{"ACCNT_ROUTES1"},
						utils.Weight:     15.,
					},
				},
				{
					RouteID: "route3",
					SortingData: map[string]any{
						utils.Cost:       5.,
						utils.AccountIDs: []any{"ACCNT_ROUTES1"},
						utils.Weight:     10.,
					},
				},
			},
		},
	}
	var reply *routes.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)
	}
}
