//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 apis

import (
	"os"
	"path"
	"reflect"
	"sort"
	"testing"
	"time"

	"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 (
	ldrCfgPath   string
	ldrCfg       *config.CGRConfig
	ldrRPC       *birpc.Client
	ldrConfigDIR string //run tests for specific configuration

	sTestsLdr = []func(t *testing.T){
		testLoadersRemoveFolders,
		testLoadersCreateFolders,

		testLoadersInitCfg,
		testLoadersInitDataDB,
		testLoadersStartEngine,
		testLoadersRPCConn,

		testLoadersWriteCSVs,
		testLoadersLoad,

		testLoadersGetAccounts,
		testLoadersGetActionProfiles,
		testLoadersGetAttributeProfiles,
		testLoadersGetChargerProfiles,
		testLoadersGetFilters,
		testLoadersGetRateProfiles,
		testLoadersGetResourceProfiles,
		testLoadersGetIPProfiles,
		testLoadersGetRouteProfiles,
		testLoadersGetStatQueueProfiles,
		testLoadersGetThresholdProfiles,

		testLoadersRemove,
		testLoadersGetAccountAfterRemove,
		testLoadersGetActionProfileAfterRemove,
		testLoadersGetAttributeProfileAfterRemove,
		testLoadersGetChargerProfileAfterRemove,
		testLoadersGetFilterAfterRemove,
		testLoadersGetRateProfileAfterRemove,
		testLoadersGetResourceProfileAfterRemove,
		testLoadersGetRouteProfileAfterRemove,
		testLoadersGetStatQueueProfileAfterRemove,
		testLoadersGetThresholdProfileAfterRemove,

		testLoadersRemoveFolders,

		testLoadersPing,
		testLoadersKillEngine,
	}
)

func TestLoadersIT(t *testing.T) {
	switch *utils.DBType {
	case utils.MetaInternal:
		ldrConfigDIR = "apis_loaders_internal"
	case utils.MetaMongo:
		ldrConfigDIR = "apis_loaders_mongo"
	case utils.MetaRedis:
		t.SkipNow()
	case utils.MetaMySQL:
		ldrConfigDIR = "apis_loaders_mysql"
	case utils.MetaPostgres:
		t.SkipNow()
	default:
		t.Fatal("Unknown Database type")
	}
	for _, stest := range sTestsLdr {
		t.Run(ldrConfigDIR, stest)
	}
}

func testLoadersInitCfg(t *testing.T) {
	var err error
	ldrCfgPath = path.Join(*utils.DataDir, "conf", "samples", ldrConfigDIR)
	ldrCfg, err = config.NewCGRConfigFromPath(context.Background(), ldrCfgPath)
	if err != nil {
		t.Error(err)
	}
}

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

// Start CGR Engine
func testLoadersStartEngine(t *testing.T) {
	if _, err := engine.StopStartEngine(ldrCfgPath, *utils.WaitRater); err != nil {
		t.Fatal(err)
	}
}

func testLoadersRPCConn(t *testing.T) {
	ldrRPC = engine.NewRPCClient(t, ldrCfg.ListenCfg(), *utils.Encoding)
}

// Kill the engine when it is about to be finished
func testLoadersKillEngine(t *testing.T) {
	if err := engine.KillEngine(100); err != nil {
		t.Error(err)
	}
}

func testLoadersPing(t *testing.T) {
	var reply string
	if err := ldrRPC.Call(context.Background(), utils.LoaderSv1Ping,
		new(utils.CGREvent), &reply); err != nil {
		t.Error(err)
	} else if reply != utils.Pong {
		t.Error("Unexpected reply returned:", reply)
	}
}

func testLoadersCreateFolders(t *testing.T) {
	if err := os.MkdirAll("/tmp/TestLoadersIT/in", 0755); err != nil {
		t.Error(err)
	}
}

func testLoadersRemoveFolders(t *testing.T) {
	if err := os.RemoveAll("/tmp/TestLoadersIT/in"); err != nil {
		t.Error(err)
	}
}

func testLoadersWriteCSVs(t *testing.T) {
	writeFile := func(fileName, data string) error {
		csvAccounts, err := os.Create(path.Join(ldrCfg.LoaderCfg()[0].TpInDir, fileName))
		if err != nil {
			return err
		}
		defer csvAccounts.Close()
		_, err = csvAccounts.WriteString(data)
		if err != nil {
			return err

		}
		return csvAccounts.Sync()
	}
	// Create and populate Accounts.csv
	if err := writeFile(utils.AccountsCsv, `
#Tenant,ID,FilterIDs,Weights,Blockers,Opts,BalanceID,BalanceFilterIDs,BalanceWeights,BalanceBlockers,BalanceType,BalanceUnits,BalanceUnitFactors,BalanceOpts,BalanceCostIncrements,BalanceAttributeIDs,BalanceRateProfileIDs,ThresholdIDs
cgrates.org,1001,,,,,,,,,,,,,,,,
cgrates.org,1001,,;20,;false,,,,,,,,,,,,,
cgrates.org,1001,,,,,MonetaryBalance,,;10,*string:~*req.Account:1002;true;;false,*monetary,14,fltr1&fltr2;100;fltr3;200,,fltr1&fltr2;1.3;2.3;3.3,attr1;attr2,,*none
cgrates.org,1001,,,,,,,,,,,,,,,,
cgrates.org,1001,,,,,VoiceBalance,,;10,,*voice,1h,,,,,,
cgrates.org,1002,,,,,MonetaryBalance,,;20,,*monetary,1h,,,,,,
cgrates.org,1002,,;30,;false,,VoiceBalance,,;10,,*voice,14,fltr3&fltr4;150;fltr5;250,,fltr3&fltr4;1.3;2.3;3.3,attr3;attr4,,*none
`); err != nil {
		t.Fatal(err)
	}

	// Create and populate ActionProfiles.csv
	if err := writeFile(utils.ActionsCsv, `
#Tenant,ID,FilterIDs,Weights,Blockers,Schedule,TargetType,TargetIDs,ActionID,ActionFilterIDs,ActionTTL,ActionType,ActionOpts,ActionWeights,ActionBlockers,ActionDiktatsID,ActionDiktatsFilterIDs,ActionDiktatsOpts,ActionDiktatsWeights,ActionDiktatsBlockers
cgrates.org,ONE_TIME_ACT,,,,,,,,,,,,,,,,,,
cgrates.org,ONE_TIME_ACT,,;10,;true,*asap,*accounts,1001;1002,,,,,,,,,,,,
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP,,0s,*addBalance,,,,ADDBALVALUE,,,,
cgrates.org,ONE_TIME_ACT,,,,*asap,*accounts,1001;1002,,,,,,,,,,,,
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP,,,,,,,ADDBALVALUE,,*balancePath:*balance.TestBalance.Value;*balanceValue:10,,
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_DATA,,0s,*setBalance,,,,SETBALTYPE,,*balancePath:*balance.TestDataBalance.Type;*balanceValue:*data,,
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_DATA,,0s,*addBalance,,,,ADDBALVALUE,,*balancePath:*balance.TestDataBalance.Value;*balanceValue:1024,,
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_VOICE,,0s,*setBalance,,,,SETBALTYPE,,*balancePath:*balance.TestVoiceBalance.Type;*balanceValue:*voice,,
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_VOICE,,0s,*addBalance,,,,ADDBALVALUE1,,*balancePath:*balance.TestVoiceBalance.Value;*balanceValue:15m15s,,
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_VOICE,,0s,*addBalance,,,,ADDBALVALUE2,,*balancePath:*balance.TestVoiceBalance2.Value;*balanceValue:15m15s,,
`); err != nil {
		t.Fatal(err)
	}

	// Create and populate Attributes.csv
	if err := writeFile(utils.AttributesCsv, `
#Tenant,ID,FilterIDs,Weights,Blockers,AttributeFilterIDs,AttributeBlockers,Path,Type,Value
cgrates.org,ALS1,*string:~*req.Account:1001;*string:~*opts.*context:con1,;20,;true,,,,,
cgrates.org,ALS1,,,,*string:~*req.Field1:Initial,,*req.Field1,*variable,Sub1
cgrates.org,ALS1,*string:~*opts.*context:con2|con3,,,,,*req.Field2,*variable,Sub2
cgrates.org,ALS2,*string:~*opts.*context:con2|con3,,;false,,,*req.Field2,*variable,Sub2
cgrates.org,ALS2,*string:~*req.Account:1002;*string:~*opts.*context:con1,;20,,*string:~*req.Field1:Initial,,*req.Field1,*variable,Sub1
`); err != nil {
		t.Fatal(err)
	}

	// Create and populate Chargers.csv
	if err := writeFile(utils.ChargersCsv, `
#Tenant,ID,FilterIDs,Weights,Blockers,RunID,AttributeIDs
cgrates.org,Charger1,*string:~*req.Account:1001,;20,;true,,
cgrates.org,Charger1,,,,*rated,ATTR_1001_SIMPLEAUTH
cgrates.org,Charger2,,,,*rated,ATTR_1002_SIMPLEAUTH
cgrates.org,Charger2,*string:~*req.Account:1002,;15,;false,,
`); err != nil {
		t.Fatal(err)
	}

	// Create and populate Filters.csv
	if err := writeFile(utils.FiltersCsv, `
#Tenant[0],ID[1],Type[2],Element[3],Values[4]
cgrates.org,FLTR_ACCOUNT_1001,,,
cgrates.org,FLTR_ACCOUNT_1001,*string,~*req.Account,1001;1002
cgrates.org,FLTR_ACCOUNT_1001,,,
cgrates.org,FLTR_ACCOUNT_1001,*prefix,~*req.Destination,10;20
cgrates.org,FLTR_ACCOUNT_1001,*rsr,~*req.Subject,~^1.*1$
cgrates.org,FLTR_ACCOUNT_1001,*rsr,~*req.Destination,1002
cgrates.org,FLTR_ACNT_dan,*string,~*req.Account,dan
`); err != nil {
		t.Fatal(err)
	}

	// Create and populate RateProfiles.csv
	if err := writeFile(utils.RatesCsv, `
#Tenant,ID,FilterIDs,Weights,MinCost,MaxCost,MaxCostStrategy,RateID,RateFilterIDs,RateActivationStart,RateWeights,RateBlocker,RateIntervalStart,RateFixedFee,RateRecurrentFee,RateUnit,RateIncrement
cgrates.org,RP1,,,,,,,,,,,,,,,
cgrates.org,RP1,*string:~*req.Subject:1001,;0,0.1,0.6,*free,,,,,,,,,,
cgrates.org,RP1,,,,,,RT_WEEK,,"* * * * 1-5",;0,false,0s,0,0.12,1m,1m
cgrates.org,RP1,,,,,,RT_WEEK,,,,,1m,1.234,0.06,1m,1s
cgrates.org,RP1,,,,,,RT_WEEKEND,,,,true,,0.067,0.03,,
cgrates.org,RP1,,,,,,RT_WEEKEND,,"* * * * 0,6",;10,false,0s,0.089,0.06,1m,1s
cgrates.org,RP1,,,,,,RT_CHRISTMAS,,* * 24 12 *,;30,false,0s,0.0564,0.06,1m,1s
cgrates.org,RP1,,,,,,RT_CHRISTMAS,,,,true,,,,,
cgrates.org,RP2,*string:~*req.Account:1001;*string:~*req.Account:1001;*string:~*req.Account:1002;*string:~*req.Account:1001;*string:~*req.Account:1003,,,,,,,,,,,,,,
cgrates.org,RP2,*string:~*req.Account:1003;*string:~*req.Account:1004,,,,,,,,,,,,,,
cgrates.org,RP2,,,,,,RT_WEEK,,,,,1m,1.234,0.06,1m,1s
cgrates.org,RP2,*string:~*req.Subject:1002,;10,0.2,0.4,*free,RT_WEEK,,"* * * * 1-5",fltr1;20,false,0s,0,0.24,2m,30s
`); err != nil {
		t.Fatal(err)
	}

	// Create and populate Resources.csv
	if err := writeFile(utils.ResourcesCsv, `
#Tenant[0],Id[1],FilterIDs[2],Weights[3],TTL[4],Limit[5],AllocationMessage[6],Blocker[7],Stored[8],Thresholds[9]
cgrates.org,ResGroup21,*string:~*req.Account:1001,;10,1s,2,call,true,true,
cgrates.org,ResGroup21,,,,,,,,
cgrates.org,ResGroup22,,,,,,,,
cgrates.org,ResGroup22,*string:~*req.Account:dan,;10,3600s,2,premium_call,true,true,
`); err != nil {
		t.Fatal(err)
	}

	// Create and populate IPs.csv
	if err := writeFile(utils.IPsCsv, `
#Tenant[0],ID[1],FilterIDs[2],Weights[3],TTL[4],Stored[5],PoolID[6],PoolFilterIDs[7],PoolType[8],PoolRange[9],PoolStrategy[10],PoolMessage[11],PoolWeights[12],PoolBlockers[13]
cgrates.org,IPs1,,,,,,,,,,,,
cgrates.org,IPs1,*string:~*req.Account:1001,;10,1s,true,,,,,,,,
cgrates.org,IPs1,,,,,POOL1,*string:~*req.Destination:2001,*ipv4,172.16.1.1/24,*ascending,alloc_msg,;15,;false
cgrates.org,IPs1,,,,,,,,,,,,
cgrates.org,IPs1,,,,,POOL1,,,,,alloc_success,*exists:~*req.GimmeMoreWeight:;50,*exists:~*req.ShouldBlock:;true
cgrates.org,IPs1,,,,,POOL2,*string:~*req.Destination:2002,*ipv4,192.168.122.1/24,*random,alloc_new,;25,;true
cgrates.org,IPs2,,,,,,,,,,,,
cgrates.org,IPs2,*string:~*req.Account:1002,;20,2s,false,POOL1,*string:~*req.Destination:3001,*ipv4,127.0.0.1/24,*descending,alloc_msg,;35,;true
`); err != nil {
		t.Fatal(err)
	}

	// Create and populate Routes.csv
	if err := writeFile(utils.RoutesCsv, `
#Tenant[0],ID[1],FilterIDs[2],Weights[3],Blockers[4],Sorting[5],SortingParameters[6],RouteID[7],RouteFilterIDs[8],RouteAccountIDs[9],RouteRateProfileIDs[10],RouteResourceIDs[11],RouteStatIDs[12],RouteWeights[13],RouteBlockers[14],RouteParameters[15]
cgrates.org,RoutePrf1,,,,,,,,,,,,,,
cgrates.org,RoutePrf1,*string:~*req.Account:1001,;20,;true,*lc,,,,,,,,,,
cgrates.org,RoutePrf1,,,,,,route1,fltr1,Account1;Account2,RPL_1,ResGroup1,Stat1,,;true,param1
cgrates.org,RoutePrf1,,,,,,,,,,,,,,
cgrates.org,RoutePrf1,,,,,,route1,,,RPL_2,ResGroup2,,;10,,
cgrates.org,RoutePrf1,,,,,,route1,fltr2,,RPL_3,ResGroup3,Stat2,,,param2
cgrates.org,RoutePrf1,,,,,,route1,,,,ResGroup4,Stat3,,,
cgrates.org,RoutePrf1,,,,,,route2,fltr5,Account1,RPL_1,ResGroup1,Stat1,fltr1;10,;true,param1
cgrates.org,RoutePrf2,,,,,,,,,,,,,,
cgrates.org,RoutePrf2,*string:~*req.Account:1002,;20,,*lc,,route1,fltr3,Account3;Account4,RPL_2,ResGroup2,Stat2,;10,;true,param1
`); err != nil {
		t.Fatal(err)
	}

	// Create and populate Trends.csv
	if err := writeFile(utils.TrendsCsv, `
#Tenant[0],Id[1],Schedule[2],StatID[3],Metrics[4],TTL[5],QueueLength[6],MinItems[7],CorrelationType[8],Tolerance[9],Stored[10],ThresholdIDs[11]
cgrates.org,TREND_1,@every 1s,Stats1_1,*acc,-1,-1,1,*last,1,false,*none
cgrates.org,TREND_2,@every 1s,Stats1_2,*tcc,-1,-1,1,*last,1,false,*none
`); err != nil {
		t.Fatal(err)
	}

	// Create and populate Rankings.csv
	if err := writeFile(utils.RankingsCsv, `
#Tenant[0],Id[1],Schedule[2],StatIDs[3],MetricIDs[4],Sorting[5],SortingParameters[6],Stored[7],ThresholdIDs[8]
cgrates.org,RANK1,@every 1s,Stats1;Stats2;Stats3;Stats4,,*asc;*acc;*pdd,false;*acd,,
cgrates.org,RANK2,@every 1s,Stats3;Stats4;Stats1;Stats2,,*desc;*acc;*pdd,false;*acd,,
`); err != nil {
		t.Fatal(err)
	}

	// Create and populate Stats.csv
	if err := writeFile(utils.StatsCsv, `
#Tenant[0],ID[1],FilterIDs[2],Weights[3],Blockers[4],QueueLength[5],TTL[6],MinItems[7],Stored[8],ThresholdIDs[9],MetricIDs[10],MetricFilterIDs[11],MetricBlockers[12]
cgrates.org,TestStats,*string:~*req.Account:1001,;20,;true,100,1s,2,true,Th1;Th2,*sum#~*req.Value;*average#~*req.Value,fltr1;fltr2,
cgrates.org,TestStats,,,,,,2,,,*sum#~*req.Usage,,*string:~*req.Account:1003&fltr2;true;;false
cgrates.org,TestStats2,*string:~*req.Account:1002,,;true,100,1s,2,true,Th,*sum#~*req.Value;*sum#~*req.Usage;*average#~*req.Value;*average#~*req.Usage,,
cgrates.org,TestStats2,,;20,,,,2,true,,*sum#~*req.Cost;*average#~*req.Cost,,;false
cgrates.org,TestStats3,,,,,,,,,,,
cgrates.org,TestStats3,*string:~*req.Account:1003,;20,;true,100,1s,2,true,Th1;Th2,*sum#~*req.Value;*average#~*req.Value,,fltr_for_stats;false
`); err != nil {
		t.Fatal(err)
	}

	// Create and populate Thresholds.csv
	if err := writeFile(utils.ThresholdsCsv, `
#Tenant[0],Id[1],FilterIDs[2],Weights[3],MaxHits[4],MinHits[5],MinSleep[6],Blocker[7],ActionProfileIDs[8],Async[9]
cgrates.org,TH1,*string:~*req.Account:1001;*string:~*req.RunID:*default,;10,12,10,1s,true,ACT_PRF1,true
cgrates.org,TH1,,,,,,,,
cgrates.org,TH2,,,,,,,,
cgrates.org,TH2,*string:~*req.Account:1002,,,,,true,,true
cgrates.org,TH2,,;5,10,8,1s,true,ACT_PRF2,true
`); err != nil {
		t.Fatal(err)
	}
}

func testLoadersLoad(t *testing.T) {
	var reply string
	if err := ldrRPC.Call(context.Background(), utils.LoaderSv1Run,
		&loaders.ArgsProcessFolder{
			APIOpts: map[string]any{
				utils.MetaCache:       utils.MetaReload,
				utils.MetaStopOnError: true,
			},
		}, &reply); err != nil {
		t.Error(err)
	} else if reply != utils.OK {
		t.Error("Unexpected reply returned:", reply)
	}
}

func testLoadersGetAccounts(t *testing.T) {
	expAccs := []*utils.Account{
		{
			Tenant: "cgrates.org",
			ID:     "1001",
			Weights: utils.DynamicWeights{
				{
					Weight: 20,
				},
			},
			Blockers: utils.DynamicBlockers{
				{
					Blocker: false,
				},
			},
			Opts: make(map[string]any),
			Balances: map[string]*utils.Balance{
				"MonetaryBalance": {
					ID: "MonetaryBalance",
					Weights: utils.DynamicWeights{
						{
							Weight: 10,
						},
					},
					Blockers: utils.DynamicBlockers{
						{
							FilterIDs: []string{"*string:~*req.Account:1002"},
							Blocker:   true,
						},
						{
							Blocker: false,
						},
					},
					Type:  utils.MetaMonetary,
					Units: utils.NewDecimal(14, 0),
					UnitFactors: []*utils.UnitFactor{
						{
							FilterIDs: []string{"fltr1", "fltr2"},
							Factor:    utils.NewDecimal(100, 0),
						},
						{
							FilterIDs: []string{"fltr3"},
							Factor:    utils.NewDecimal(200, 0),
						},
					},
					Opts: map[string]any{},
					CostIncrements: []*utils.CostIncrement{
						{
							FilterIDs:    []string{"fltr1", "fltr2"},
							Increment:    utils.NewDecimal(13, 1),
							FixedFee:     utils.NewDecimal(23, 1),
							RecurrentFee: utils.NewDecimal(33, 1),
						},
					},
					AttributeIDs: []string{"attr1", "attr2"},
				},
				"VoiceBalance": {
					ID: "VoiceBalance",
					Weights: utils.DynamicWeights{
						{
							Weight: 10,
						},
					},
					Type:  utils.MetaVoice,
					Units: utils.NewDecimal(int64(time.Hour), 0),
					Opts:  make(map[string]any),
				},
			},
			ThresholdIDs: []string{utils.MetaNone},
		},
		{
			Tenant: "cgrates.org",
			ID:     "1002",
			Weights: utils.DynamicWeights{
				{
					Weight: 30,
				},
			},
			Blockers: utils.DynamicBlockers{
				{
					Blocker: false,
				},
			},
			Opts: make(map[string]any),
			Balances: map[string]*utils.Balance{
				"MonetaryBalance": {
					ID: "MonetaryBalance",
					Weights: utils.DynamicWeights{
						{
							Weight: 20,
						},
					},

					Type:  utils.MetaMonetary,
					Units: utils.NewDecimal(int64(time.Hour), 0),
					Opts:  map[string]any{},
				},
				"VoiceBalance": {
					ID: "VoiceBalance",
					Weights: utils.DynamicWeights{
						{
							Weight: 10,
						},
					},
					Type:  utils.MetaVoice,
					Units: utils.NewDecimal(14, 0),
					Opts:  make(map[string]any),
					UnitFactors: []*utils.UnitFactor{
						{
							FilterIDs: []string{"fltr3", "fltr4"},
							Factor:    utils.NewDecimal(150, 0),
						},
						{
							FilterIDs: []string{"fltr5"},
							Factor:    utils.NewDecimal(250, 0),
						},
					},
					CostIncrements: []*utils.CostIncrement{
						{
							FilterIDs:    []string{"fltr3", "fltr4"},
							Increment:    utils.NewDecimal(13, 1),
							FixedFee:     utils.NewDecimal(23, 1),
							RecurrentFee: utils.NewDecimal(33, 1),
						},
					},
					AttributeIDs: []string{"attr3", "attr4"},
				},
			},
			ThresholdIDs: []string{utils.MetaNone},
		},
	}
	var accs []*utils.Account
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetAccounts,
		&utils.ArgsItemIDs{
			Tenant: "cgrates.org",
		}, &accs); err != nil {
		t.Error(err)
	} else {
		sort.Slice(accs, func(i, j int) bool {
			return accs[i].ID < accs[j].ID
		})
		if !reflect.DeepEqual(accs, expAccs) {
			t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ToJSON(expAccs), utils.ToJSON(accs))
		}
	}
}

func testLoadersGetActionProfiles(t *testing.T) {
	expActs := []*utils.ActionProfile{
		{
			Tenant: "cgrates.org",
			ID:     "ONE_TIME_ACT",
			Weights: utils.DynamicWeights{
				{
					Weight: 10,
				},
			},
			Blockers: utils.DynamicBlockers{
				{
					Blocker: true,
				},
			},
			Schedule: utils.MetaASAP,
			Targets: map[string]utils.StringSet{
				"*accounts": {
					"1001": {},
					"1002": {},
				},
			},
			Actions: []*utils.APAction{
				{
					ID:   "TOPUP",
					TTL:  0,
					Type: utils.MetaAddBalance,
					Opts: map[string]any{},
					Diktats: []*utils.APDiktat{
						{
							ID: "ADDBALVALUE",
							Opts: map[string]any{
								"*balancePath":  "*balance.TestBalance.Value",
								"*balanceValue": "10",
							},
						},
					},
				},
				{
					ID:   "SET_BALANCE_TEST_DATA",
					TTL:  0,
					Type: utils.MetaSetBalance,
					Opts: map[string]any{},
					Diktats: []*utils.APDiktat{
						{
							ID: "SETBALTYPE",
							Opts: map[string]any{
								"*balancePath":  "*balance.TestDataBalance.Type",
								"*balanceValue": utils.MetaData,
							},
						},
					},
				},
				{
					ID:   "TOPUP_TEST_DATA",
					TTL:  0,
					Type: utils.MetaAddBalance,
					Opts: map[string]any{},
					Diktats: []*utils.APDiktat{
						{
							ID: "ADDBALVALUE",
							Opts: map[string]any{
								"*balancePath":  "*balance.TestDataBalance.Value",
								"*balanceValue": "1024",
							},
						},
					},
				},
				{
					ID:   "SET_BALANCE_TEST_VOICE",
					TTL:  0,
					Type: utils.MetaSetBalance,
					Opts: map[string]any{},
					Diktats: []*utils.APDiktat{
						{
							ID: "SETBALTYPE",
							Opts: map[string]any{
								"*balancePath":  "*balance.TestVoiceBalance.Type",
								"*balanceValue": utils.MetaVoice,
							},
						},
					},
				},
				{
					ID:   "TOPUP_TEST_VOICE",
					TTL:  0,
					Type: utils.MetaAddBalance,
					Opts: map[string]any{},
					Diktats: []*utils.APDiktat{
						{
							ID: "ADDBALVALUE1",
							Opts: map[string]any{
								"*balancePath":  "*balance.TestVoiceBalance.Value",
								"*balanceValue": "15m15s",
							},
						},
						{
							ID: "ADDBALVALUE2",
							Opts: map[string]any{
								"*balancePath":  "*balance.TestVoiceBalance2.Value",
								"*balanceValue": "15m15s",
							},
						},
					},
				},
			},
		},
	}
	var acts []*utils.ActionProfile
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetActionProfiles,
		&utils.ArgsItemIDs{
			Tenant: "cgrates.org",
		}, &acts); err != nil {
		t.Error(err)
	} else {
		sort.Slice(acts, func(i, j int) bool {
			return acts[i].ID < acts[j].ID
		})
		if !reflect.DeepEqual(acts, expActs) {
			t.Errorf("expected: <%+v>, \nreceived: <%+v>", expActs, acts)
		}
	}
}

func testLoadersGetAttributeProfiles(t *testing.T) {
	expAttrs := []*utils.APIAttributeProfile{
		{
			Tenant:    "cgrates.org",
			ID:        "ALS1",
			FilterIDs: []string{"*string:~*req.Account:1001", "*string:~*opts.*context:con1", "*string:~*opts.*context:con2|con3"},
			Attributes: []*utils.ExternalAttribute{
				{
					FilterIDs: []string{"*string:~*req.Field1:Initial"},
					Path:      utils.MetaReq + utils.NestingSep + "Field1",
					Type:      utils.MetaVariable,
					Value:     "Sub1",
				},
				{
					Path:  utils.MetaReq + utils.NestingSep + "Field2",
					Type:  utils.MetaVariable,
					Value: "Sub2",
				},
			},
			Blockers: utils.DynamicBlockers{
				{
					Blocker: true,
				},
			},
			Weights: utils.DynamicWeights{
				{
					Weight: 20,
				},
			},
		},
		{
			Tenant:    "cgrates.org",
			ID:        "ALS2",
			FilterIDs: []string{"*string:~*opts.*context:con2|con3", "*string:~*req.Account:1002", "*string:~*opts.*context:con1"},
			Attributes: []*utils.ExternalAttribute{
				{
					Path:  utils.MetaReq + utils.NestingSep + "Field2",
					Type:  utils.MetaVariable,
					Value: "Sub2",
				},
				{
					FilterIDs: []string{"*string:~*req.Field1:Initial"},
					Path:      utils.MetaReq + utils.NestingSep + "Field1",
					Type:      utils.MetaVariable,
					Value:     "Sub1",
				},
			},
			Blockers: utils.DynamicBlockers{
				{
					Blocker: false,
				},
			},
			Weights: utils.DynamicWeights{
				{
					Weight: 20,
				},
			},
		},
	}
	var attrs []*utils.APIAttributeProfile
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetAttributeProfiles,
		&utils.ArgsItemIDs{
			Tenant: "cgrates.org",
		}, &attrs); err != nil {
		t.Error(err)
	} else {
		sort.Slice(attrs, func(i, j int) bool {
			return attrs[i].ID < attrs[j].ID
		})
		if !reflect.DeepEqual(attrs, expAttrs) {
			t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ToJSON(expAttrs), utils.ToJSON(attrs))
		}
	}
}

func testLoadersGetChargerProfiles(t *testing.T) {
	expChrgs := []*utils.ChargerProfile{
		{
			Tenant:       "cgrates.org",
			ID:           "Charger1",
			FilterIDs:    []string{"*string:~*req.Account:1001"},
			RunID:        "*rated",
			AttributeIDs: []string{"ATTR_1001_SIMPLEAUTH"},
			Weights: utils.DynamicWeights{
				{
					Weight: 20,
				},
			},
			Blockers: utils.DynamicBlockers{
				{
					Blocker: true,
				},
			},
		},
		{
			Tenant:       "cgrates.org",
			ID:           "Charger2",
			FilterIDs:    []string{"*string:~*req.Account:1002"},
			RunID:        "*rated",
			AttributeIDs: []string{"ATTR_1002_SIMPLEAUTH"},
			Weights: utils.DynamicWeights{
				{
					Weight: 15,
				},
			},
			Blockers: utils.DynamicBlockers{
				{
					Blocker: false,
				},
			},
		},
	}
	var chrgs []*utils.ChargerProfile
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetChargerProfiles,
		&utils.ArgsItemIDs{
			Tenant: "cgrates.org",
		}, &chrgs); err != nil {
		t.Error(err)
	} else {
		sort.Slice(chrgs, func(i, j int) bool {
			return chrgs[i].ID < chrgs[j].ID
		})
		if !reflect.DeepEqual(chrgs, expChrgs) {
			t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ToJSON(expChrgs), utils.ToJSON(chrgs))
		}
	}
}

func testLoadersGetFilters(t *testing.T) {
	expFltrs := []*engine.Filter{
		{
			Tenant: "cgrates.org",
			ID:     "FLTR_ACCOUNT_1001",
			Rules: []*engine.FilterRule{
				{
					Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.AccountField,
					Type:    utils.MetaString,
					Values:  []string{"1001", "1002"},
				},
				{
					Element: "~*req.Destination",
					Type:    utils.MetaPrefix,
					Values:  []string{"10", "20"},
				},
				{
					Element: "~*req.Subject",
					Type:    utils.MetaRSR,
					Values:  []string{"~^1.*1$"},
				},
				{
					Element: "~*req.Destination",
					Type:    utils.MetaRSR,
					Values:  []string{"1002"},
				},
			},
		},
		{
			Tenant: "cgrates.org",
			ID:     "FLTR_ACNT_dan",
			Rules: []*engine.FilterRule{
				{
					Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.AccountField,
					Type:    utils.MetaString,
					Values:  []string{"dan"},
				},
			},
		},
	}
	var fltrs []*engine.Filter
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetFilters,
		&utils.ArgsItemIDs{
			Tenant: "cgrates.org",
		}, &fltrs); err != nil {
		t.Error(err)
	} else {
		sort.Slice(fltrs, func(i, j int) bool {
			return fltrs[i].ID < fltrs[j].ID
		})
		if !reflect.DeepEqual(fltrs, expFltrs) {
			t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ToJSON(expFltrs), utils.ToJSON(fltrs))
		}
	}
}

func testLoadersGetRateProfiles(t *testing.T) {
	expRatePrfs := []*utils.RateProfile{
		{
			Tenant:    "cgrates.org",
			ID:        "RP1",
			FilterIDs: []string{"*string:~*req.Subject:1001"},
			Weights: utils.DynamicWeights{
				{
					Weight: 0,
				},
			},
			MinCost:         utils.NewDecimal(1, 1),
			MaxCost:         utils.NewDecimal(6, 1),
			MaxCostStrategy: "*free",
			Rates: map[string]*utils.Rate{
				"RT_WEEK": {
					ID: "RT_WEEK",
					Weights: utils.DynamicWeights{
						{
							Weight: 0,
						},
					},
					ActivationTimes: "* * * * 1-5",
					IntervalRates: []*utils.IntervalRate{
						{
							IntervalStart: utils.NewDecimal(int64(0*time.Second), 0),
							FixedFee:      utils.NewDecimal(0, 0),
							RecurrentFee:  utils.NewDecimal(12, 2),
							Unit:          utils.NewDecimal(int64(time.Minute), 0),
							Increment:     utils.NewDecimal(int64(time.Minute), 0),
						},
						{
							IntervalStart: utils.NewDecimal(int64(time.Minute), 0),
							FixedFee:      utils.NewDecimal(1234, 3),
							RecurrentFee:  utils.NewDecimal(6, 2),
							Unit:          utils.NewDecimal(int64(time.Minute), 0),
							Increment:     utils.NewDecimal(int64(time.Second), 0),
						},
					},
				},
				"RT_WEEKEND": {
					ID: "RT_WEEKEND",
					Weights: utils.DynamicWeights{
						{
							Weight: 10,
						},
					},
					ActivationTimes: "* * * * 0,6",
					Blocker:         true,
					IntervalRates: []*utils.IntervalRate{
						{
							IntervalStart: utils.NewDecimal(int64(0*time.Second), 0),
							FixedFee:      utils.NewDecimal(89, 3),
							RecurrentFee:  utils.NewDecimal(6, 2),
							Unit:          utils.NewDecimal(int64(time.Minute), 0),
							Increment:     utils.NewDecimal(int64(time.Second), 0),
						},
					},
				},
				"RT_CHRISTMAS": {
					ID: "RT_CHRISTMAS",
					Weights: utils.DynamicWeights{
						{
							Weight: 30,
						},
					},
					Blocker:         true,
					ActivationTimes: "* * 24 12 *",
					IntervalRates: []*utils.IntervalRate{
						{
							IntervalStart: utils.NewDecimal(int64(0*time.Second), 0),
							FixedFee:      utils.NewDecimal(564, 4),
							RecurrentFee:  utils.NewDecimal(6, 2),
							Unit:          utils.NewDecimal(int64(time.Minute), 0),
							Increment:     utils.NewDecimal(int64(time.Second), 0),
						},
					},
				},
			},
		},
		{
			Tenant: "cgrates.org",
			ID:     "RP2",
			FilterIDs: []string{"*string:~*req.Account:1001", "*string:~*req.Account:1002", "*string:~*req.Account:1001",
				"*string:~*req.Account:1003", "*string:~*req.Account:1004", "*string:~*req.Subject:1002"},
			Weights: utils.DynamicWeights{
				{
					Weight: 10,
				},
			},
			MinCost:         utils.NewDecimal(2, 1),
			MaxCost:         utils.NewDecimal(4, 1),
			MaxCostStrategy: "*free",
			Rates: map[string]*utils.Rate{
				"RT_WEEK": {
					ID: "RT_WEEK",
					Weights: utils.DynamicWeights{
						{
							FilterIDs: []string{"fltr1"},
							Weight:    20,
						},
					},
					ActivationTimes: "* * * * 1-5",
					IntervalRates: []*utils.IntervalRate{
						{
							IntervalStart: utils.NewDecimal(int64(0*time.Second), 0),
							FixedFee:      utils.NewDecimal(0, 0),
							RecurrentFee:  utils.NewDecimal(24, 2),
							Unit:          utils.NewDecimal(int64(2*time.Minute), 0),
							Increment:     utils.NewDecimal(int64(30*time.Second), 0),
						},
						{
							IntervalStart: utils.NewDecimal(int64(time.Minute), 0),
							FixedFee:      utils.NewDecimal(1234, 3),
							RecurrentFee:  utils.NewDecimal(6, 2),
							Unit:          utils.NewDecimal(int64(time.Minute), 0),
							Increment:     utils.NewDecimal(int64(time.Second), 0),
						},
					},
				},
			},
		},
	}
	var ratePrfs []*utils.RateProfile
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetRateProfiles,
		&utils.ArgsItemIDs{
			Tenant: "cgrates.org",
		}, &ratePrfs); err != nil {
		t.Error(err)
	} else {
		sort.Slice(ratePrfs, func(i, j int) bool {
			return ratePrfs[i].ID < ratePrfs[j].ID
		})
		if !reflect.DeepEqual(ratePrfs, expRatePrfs) {
			t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ToJSON(expRatePrfs), utils.ToJSON(ratePrfs))
		}
	}
}

func testLoadersGetResourceProfiles(t *testing.T) {
	expRsPrfs := []*utils.ResourceProfile{
		{
			Tenant:            "cgrates.org",
			ID:                "ResGroup21",
			FilterIDs:         []string{"*string:~*req.Account:1001"},
			UsageTTL:          time.Second,
			AllocationMessage: "call",
			Weights: utils.DynamicWeights{
				{
					Weight: 10,
				}},
			Limit:   2,
			Blocker: true,
			Stored:  true,
		},
		{
			Tenant:            "cgrates.org",
			ID:                "ResGroup22",
			FilterIDs:         []string{"*string:~*req.Account:dan"},
			UsageTTL:          time.Hour,
			AllocationMessage: "premium_call",
			Blocker:           true,
			Stored:            true,
			Weights: utils.DynamicWeights{
				{
					Weight: 10,
				}},
			Limit: 2,
		},
	}
	var rsPrfs []*utils.ResourceProfile
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetResourceProfiles,
		&utils.ArgsItemIDs{
			Tenant: "cgrates.org",
		}, &rsPrfs); err != nil {
		t.Error(err)
	} else {
		sort.Slice(rsPrfs, func(i, j int) bool {
			return rsPrfs[i].ID < rsPrfs[j].ID
		})
		if !reflect.DeepEqual(rsPrfs, expRsPrfs) {
			t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ToJSON(expRsPrfs), utils.ToJSON(rsPrfs))
		}
	}
}

func testLoadersGetIPProfiles(t *testing.T) {
	expIPPs := []*utils.IPProfile{
		{
			Tenant:    "cgrates.org",
			ID:        "IPs1",
			FilterIDs: []string{"*string:~*req.Account:1001"},
			Weights: utils.DynamicWeights{
				{
					Weight: 10,
				},
			},
			TTL:    time.Second,
			Stored: true,
			Pools: []*utils.IPPool{
				{
					ID:        "POOL1",
					FilterIDs: []string{"*string:~*req.Destination:2001"},
					Type:      "*ipv4",
					Range:     "172.16.1.1/24",
					Strategy:  "*ascending",
					Message:   "alloc_success",
					Weights: utils.DynamicWeights{
						{
							Weight: 15,
						},
						{
							FilterIDs: []string{"*exists:~*req.GimmeMoreWeight:"},
							Weight:    50,
						},
					},
					Blockers: utils.DynamicBlockers{
						{
							Blocker: false,
						},
						{
							Blocker:   true,
							FilterIDs: []string{"*exists:~*req.ShouldBlock:"},
						},
					},
				},
				{
					ID:        "POOL2",
					FilterIDs: []string{"*string:~*req.Destination:2002"},
					Type:      "*ipv4",
					Range:     "192.168.122.1/24",
					Strategy:  "*random",
					Message:   "alloc_new",
					Weights: utils.DynamicWeights{
						{
							Weight: 25,
						},
					},
					Blockers: utils.DynamicBlockers{
						{
							Blocker: true,
						},
					},
				},
			},
		},
		{
			Tenant:    "cgrates.org",
			ID:        "IPs2",
			FilterIDs: []string{"*string:~*req.Account:1002"},
			Weights: utils.DynamicWeights{
				{
					Weight: 20,
				},
			},
			TTL:    2 * time.Second,
			Stored: false,
			Pools: []*utils.IPPool{
				{
					ID:        "POOL1",
					FilterIDs: []string{"*string:~*req.Destination:3001"},
					Type:      "*ipv4",
					Range:     "127.0.0.1/24",
					Strategy:  "*descending",
					Message:   "alloc_msg",
					Weights: utils.DynamicWeights{
						{
							Weight: 35,
						},
					},
					Blockers: utils.DynamicBlockers{
						{
							Blocker: true,
						},
					},
				},
			},
		},
	}
	var ipps []*utils.IPProfile
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetIPProfiles,
		&utils.ArgsItemIDs{
			Tenant: "cgrates.org",
		}, &ipps); err != nil {
		t.Error(err)
	} else {
		sort.Slice(ipps, func(i, j int) bool {
			return ipps[i].ID < ipps[j].ID
		})
		if !reflect.DeepEqual(ipps, expIPPs) {
			t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ToJSON(expIPPs), utils.ToJSON(ipps))
		}
	}
}

func testLoadersGetRouteProfiles(t *testing.T) {
	expRouPrfs := []*utils.RouteProfile{
		{
			Tenant:    "cgrates.org",
			ID:        "RoutePrf1",
			FilterIDs: []string{"*string:~*req.Account:1001"},
			Sorting:   utils.MetaLC,
			Blockers: utils.DynamicBlockers{
				{
					Blocker: true,
				},
			},
			Routes: []*utils.Route{
				{
					ID:             "route1",
					FilterIDs:      []string{"fltr1", "fltr2"},
					AccountIDs:     []string{"Account1", "Account2"},
					RateProfileIDs: []string{"RPL_1", "RPL_2", "RPL_3"},
					ResourceIDs:    []string{"ResGroup1", "ResGroup2", "ResGroup3", "ResGroup4"},
					StatIDs:        []string{"Stat1", "Stat2", "Stat3"},
					Weights: utils.DynamicWeights{
						{
							Weight: 10,
						},
					},
					Blockers: utils.DynamicBlockers{
						{
							Blocker: true,
						},
					},
					RouteParameters: "param2",
				},
				{
					ID:             "route2",
					FilterIDs:      []string{"fltr5"},
					AccountIDs:     []string{"Account1"},
					RateProfileIDs: []string{"RPL_1"},
					ResourceIDs:    []string{"ResGroup1"},
					StatIDs:        []string{"Stat1"},
					Weights: utils.DynamicWeights{
						{
							FilterIDs: []string{"fltr1"},
							Weight:    10,
						},
					},
					Blockers: utils.DynamicBlockers{
						{
							Blocker: true,
						},
					},
					RouteParameters: "param1",
				},
			},
			Weights: utils.DynamicWeights{
				{
					Weight: 20,
				},
			},
		},
		{
			Tenant:    "cgrates.org",
			ID:        "RoutePrf2",
			FilterIDs: []string{"*string:~*req.Account:1002"},
			Sorting:   utils.MetaLC,
			Routes: []*utils.Route{
				{
					ID:             "route1",
					FilterIDs:      []string{"fltr3"},
					AccountIDs:     []string{"Account3", "Account4"},
					RateProfileIDs: []string{"RPL_2"},
					ResourceIDs:    []string{"ResGroup2"},
					StatIDs:        []string{"Stat2"},
					Weights: utils.DynamicWeights{
						{
							Weight: 10,
						},
					},
					Blockers: utils.DynamicBlockers{
						{
							Blocker: true,
						},
					},
					RouteParameters: "param1",
				},
			},
			Weights: utils.DynamicWeights{
				{
					Weight: 20,
				},
			},
		},
	}
	var rouPrfs []*utils.RouteProfile
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetRouteProfiles,
		&utils.ArgsItemIDs{
			Tenant: "cgrates.org",
		}, &rouPrfs); err != nil {
		t.Error(err)
	} else {
		sort.Slice(rouPrfs, func(i, j int) bool {
			return rouPrfs[i].ID < rouPrfs[j].ID
		})
		if !reflect.DeepEqual(rouPrfs, expRouPrfs) {
			t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ToJSON(expRouPrfs), utils.ToJSON(rouPrfs))
		}
	}
}

func testLoadersGetStatQueueProfiles(t *testing.T) {
	expSqPrfs := []*engine.StatQueueProfile{
		{
			Tenant:      "cgrates.org",
			ID:          "TestStats",
			FilterIDs:   []string{"*string:~*req.Account:1001"},
			QueueLength: 100,
			TTL:         time.Second,
			Metrics: []*engine.MetricWithFilters{
				{
					MetricID: "*sum#~*req.Value",
				},
				{
					FilterIDs: []string{"fltr1", "fltr2"},
					MetricID:  "*average#~*req.Value",
				},
				{
					MetricID: "*sum#~*req.Usage",
					Blockers: utils.DynamicBlockers{
						{
							FilterIDs: []string{"*string:~*req.Account:1003", "fltr2"},
							Blocker:   true,
						},
						{
							Blocker: false,
						},
					},
				},
			},
			ThresholdIDs: []string{"Th1", "Th2"},
			Blockers:     utils.DynamicBlockers{{Blocker: true}},
			Stored:       true,
			Weights: utils.DynamicWeights{
				{
					Weight: 20,
				},
			},
			MinItems: 2,
		},
		{
			Tenant:      "cgrates.org",
			ID:          "TestStats2",
			FilterIDs:   []string{"*string:~*req.Account:1002"},
			QueueLength: 100,
			TTL:         time.Second,
			Metrics: []*engine.MetricWithFilters{
				{
					MetricID: "*sum#~*req.Value",
				},
				{
					MetricID: "*sum#~*req.Usage",
				},
				{
					MetricID: "*average#~*req.Value",
				},
				{
					MetricID: "*average#~*req.Usage",
				},
				{
					MetricID: "*sum#~*req.Cost",
				},
				{
					MetricID: "*average#~*req.Cost",
					Blockers: utils.DynamicBlockers{{Blocker: false}},
				},
			},
			ThresholdIDs: []string{"Th"},
			Blockers:     utils.DynamicBlockers{{Blocker: true}},
			Stored:       true,
			Weights: utils.DynamicWeights{
				{
					Weight: 20,
				},
			},
			MinItems: 2,
		},
		{
			Tenant:      "cgrates.org",
			ID:          "TestStats3",
			FilterIDs:   []string{"*string:~*req.Account:1003"},
			QueueLength: 100,
			TTL:         time.Second,
			Metrics: []*engine.MetricWithFilters{
				{
					MetricID: "*sum#~*req.Value",
				},
				{
					MetricID: "*average#~*req.Value",
					Blockers: utils.DynamicBlockers{
						{
							FilterIDs: []string{"fltr_for_stats"},
							Blocker:   false,
						},
					},
				},
			},
			ThresholdIDs: []string{"Th1", "Th2"},
			Blockers:     utils.DynamicBlockers{{Blocker: true}},
			Stored:       true,
			Weights: utils.DynamicWeights{
				{
					Weight: 20,
				},
			},
			MinItems: 2,
		},
	}
	var sqPrfs []*engine.StatQueueProfile
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetStatQueueProfiles,
		&utils.ArgsItemIDs{
			Tenant: "cgrates.org",
		}, &sqPrfs); err != nil {
		t.Error(err)
	} else {
		sort.Slice(sqPrfs, func(i, j int) bool {
			return sqPrfs[i].ID < sqPrfs[j].ID
		})
		if !reflect.DeepEqual(sqPrfs, expSqPrfs) {
			t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ToJSON(expSqPrfs), utils.ToJSON(sqPrfs))
		}
	}
}

func testLoadersGetThresholdProfiles(t *testing.T) {
	expThPrfs := []*engine.ThresholdProfile{
		{
			Tenant:    "cgrates.org",
			ID:        "TH1",
			FilterIDs: []string{"*string:~*req.Account:1001", "*string:~*req.RunID:*default"},
			MaxHits:   12,
			MinHits:   10,
			MinSleep:  time.Second,
			Blocker:   true,
			Weights: utils.DynamicWeights{
				{
					Weight: 10,
				},
			},
			ActionProfileIDs: []string{"ACT_PRF1"},
			Async:            true,
		},
		{
			Tenant:    "cgrates.org",
			ID:        "TH2",
			FilterIDs: []string{"*string:~*req.Account:1002"},
			MaxHits:   10,
			MinHits:   8,
			MinSleep:  time.Second,
			Blocker:   true,
			Weights: utils.DynamicWeights{
				{
					Weight: 5,
				},
			},
			ActionProfileIDs: []string{"ACT_PRF2"},
			Async:            true,
		},
	}
	var thPrfs []*engine.ThresholdProfile
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetThresholdProfiles,
		&utils.ArgsItemIDs{
			Tenant: "cgrates.org",
		}, &thPrfs); err != nil {
		t.Error(err)
	} else {
		sort.Slice(thPrfs, func(i, j int) bool {
			return thPrfs[i].ID < thPrfs[j].ID
		})
		if !reflect.DeepEqual(thPrfs, expThPrfs) {
			t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ToJSON(expThPrfs), utils.ToJSON(thPrfs))
		}
	}
}

func testLoadersRemove(t *testing.T) {
	var reply string
	if err := ldrRPC.Call(context.Background(), utils.LoaderSv1Run, //Remove,
		&loaders.ArgsProcessFolder{
			LoaderID: "remove",
			APIOpts: map[string]any{
				utils.MetaCache:       utils.MetaReload,
				utils.MetaStopOnError: true,
			},
		}, &reply); err != nil {
		t.Error(err)
	} else if reply != utils.OK {
		t.Error("Unexpected reply returned:", reply)
	}
}

func testLoadersGetAccountAfterRemove(t *testing.T) {
	var accIDs []string
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetAccountIDs,
		&utils.ArgsItemIDs{
			Tenant: "cgrates.org",
		}, &accIDs); err == nil || err.Error() != utils.ErrNotFound.Error() {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
	}

	var rplyAccPrf utils.Account
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetAccount,
		utils.TenantID{
			Tenant: "cgrates.org",
			ID:     "ACC_PRF",
		}, &rplyAccPrf); err == nil || err.Error() != utils.ErrNotFound.Error() {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
	}
}

func testLoadersGetActionProfileAfterRemove(t *testing.T) {
	var actIDs []string
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetActionProfileIDs,
		&utils.ArgsItemIDs{
			Tenant: "cgrates.org",
		}, &actIDs); err == nil || utils.ErrNotFound.Error() != err.Error() {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
	}

	var rplyActPrf utils.ActionProfile
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetActionProfile,
		utils.TenantID{
			Tenant: "cgrates.org",
			ID:     "ACT_PRF",
		}, &rplyActPrf); err == nil || err.Error() != utils.ErrNotFound.Error() {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
	}
}

func testLoadersGetAttributeProfileAfterRemove(t *testing.T) {
	var attrIDs []string
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetAttributeProfileIDs,
		&utils.ArgsItemIDs{
			Tenant: "cgrates.org",
		}, &attrIDs); err == nil || err.Error() != utils.ErrNotFound.Error() {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
	}

	var rplyAttrPrf utils.AttributeProfile
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetAttributeProfile,
		utils.TenantID{
			Tenant: "cgrates.org",
			ID:     "ATTR_ACNT_1001",
		}, &rplyAttrPrf); err == nil || err.Error() != utils.ErrNotFound.Error() {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
	}
}

func testLoadersGetChargerProfileAfterRemove(t *testing.T) {
	var chgIDs []string
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetChargerProfileIDs,
		&utils.ArgsItemIDs{
			Tenant: "cgrates.org",
		}, &chgIDs); err == nil || err.Error() != utils.ErrNotFound.Error() {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
	}

	var rplyChgPrf utils.ChargerProfile
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetChargerProfile,
		utils.TenantID{
			Tenant: "cgrates.org",
			ID:     "Raw",
		}, &rplyChgPrf); err == nil || err.Error() != utils.ErrNotFound.Error() {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
	}
}

func testLoadersGetFilterAfterRemove(t *testing.T) {
	var fltrIDs []string
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetFilterIDs,
		&utils.ArgsItemIDs{
			Tenant: "cgrates.org",
		}, &fltrIDs); err == nil || err.Error() != utils.ErrNotFound.Error() {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
	}

	var rplyFltrPrf engine.Filter
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetFilter,
		utils.TenantID{
			Tenant: "cgrates.org",
			ID:     "FLTR_ACCOUNT_1001",
		}, &rplyFltrPrf); err == nil || err.Error() != utils.ErrNotFound.Error() {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
	}
}

func testLoadersGetRateProfileAfterRemove(t *testing.T) {
	var rateIDs []string
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetRateProfileIDs,
		&utils.ArgsItemIDs{
			Tenant: "cgrates.org",
		}, &rateIDs); err == nil || err.Error() != utils.ErrNotFound.Error() {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
	}

	var rplyRatePrf utils.RateProfile
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetRateProfile,
		utils.TenantID{
			Tenant: "cgrates.org",
			ID:     "RP1",
		}, &rplyRatePrf); err == nil || err.Error() != utils.ErrNotFound.Error() {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
	}
}

func testLoadersGetResourceProfileAfterRemove(t *testing.T) {
	var rsIDs []string
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetResourceProfileIDs,
		&utils.ArgsItemIDs{
			Tenant: "cgrates.org",
		}, &rsIDs); err == nil || err.Error() != utils.ErrNotFound.Error() {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
	}

	var rplyRsPrf utils.ResourceProfile
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetResourceProfile,
		utils.TenantID{
			Tenant: "cgrates.org",
			ID:     "RES_ACNT_1001",
		}, &rplyRsPrf); err == nil || err.Error() != utils.ErrNotFound.Error() {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
	}
}

func testLoadersGetRouteProfileAfterRemove(t *testing.T) {
	var rtIDs []string
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetRouteProfileIDs,
		&utils.ArgsItemIDs{
			Tenant: "cgrates.org",
		}, &rtIDs); err == nil || err.Error() != utils.ErrNotFound.Error() {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
	}

	var rplyRtPrf utils.RouteProfile
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetRouteProfile,
		utils.TenantID{
			Tenant: "cgrates.org",
			ID:     "ROUTE_ACNT_1001",
		}, &rplyRtPrf); err == nil || err.Error() != utils.ErrNotFound.Error() {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
	}
}

func testLoadersGetStatQueueProfileAfterRemove(t *testing.T) {
	var sqIDs []string
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetStatQueueProfileIDs,
		&utils.ArgsItemIDs{
			Tenant: "cgrates.org",
		}, &sqIDs); err == nil || err.Error() != utils.ErrNotFound.Error() {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
	}

	var rplySqPrf engine.StatQueueProfile
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetStatQueueProfile,
		utils.TenantID{
			Tenant: "cgrates.org",
			ID:     "Stat_1",
		}, &rplySqPrf); err == nil || err.Error() != utils.ErrNotFound.Error() {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
	}
}

func testLoadersGetThresholdProfileAfterRemove(t *testing.T) {
	var thIDs []string
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetThresholdProfileIDs,
		&utils.ArgsItemIDs{
			Tenant: "cgrates.org",
		}, &thIDs); err == nil || err.Error() != utils.ErrNotFound.Error() {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
	}

	var rplyThPrf engine.ThresholdProfile
	if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetThresholdProfile,
		utils.TenantID{
			Tenant: "cgrates.org",
			ID:     "THD_ACNT_1001",
		}, &rplyThPrf); err == nil || err.Error() != utils.ErrNotFound.Error() {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
	}
}

func TestLoadersLoad(t *testing.T) {
	dirPath := "/tmp/TestLoadersLoad"
	err := os.Mkdir(dirPath, 0755)
	if err != nil {
		t.Error(err)
	}
	defer os.RemoveAll(dirPath)

	dirPathIn := "/tmp/TestLoadersLoad/in"
	err = os.Mkdir(dirPathIn, 0755)
	if err != nil {
		t.Error(err)
	}
	defer os.RemoveAll(dirPathIn)

	f1, err := os.Create(dirPathIn + "/emptyfile.txt")
	if err != nil {
		t.Error(err)
	}
	defer f1.Close()

	dirPathOut := "/tmp/TestLoadersLoad/out"
	err = os.Mkdir(dirPathOut, 0755)
	if err != nil {
		t.Error(err)
	}
	defer os.RemoveAll(dirPathOut)

	f2, err := os.Create(dirPathOut + "/emptyfile.txt")
	if err != nil {
		t.Error(err)
	}
	defer f2.Close()

	cfg := config.NewDefaultCGRConfig()
	loaderCfg := &config.LoaderSCfg{
		ID:             "LoaderID",
		Enabled:        true,
		Tenant:         "cgrates.org",
		RunDelay:       1 * time.Millisecond,
		LockFilePath:   "lockFileName",
		FieldSeparator: ";",
		TpInDir:        dirPathIn,
		TpOutDir:       dirPathOut,
		Opts:           &config.LoaderSOptsCfg{},
	}

	cfg.LoaderCfg()[0] = loaderCfg
	data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
	dbCM := engine.NewDBConnManager(map[string]engine.DataDB{utils.MetaDefault: data}, cfg.DbCfg())
	dm := engine.NewDataManager(dbCM, cfg, nil)
	fltrs := engine.NewFilterS(cfg, nil, dm)
	ldrS := loaders.NewLoaderS(cfg, dm, fltrs, nil)
	lSv1 := NewLoaderSv1(ldrS)

	args := &loaders.ArgsProcessFolder{
		LoaderID: "LoaderID",
	}
	var reply string
	if err := lSv1.Run(context.Background(), args, &reply); err != nil {
		t.Error(err)
	}
}
