mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
Compare commits
6 Commits
a7d6e73182
...
2fe3fd6690
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2fe3fd6690 | ||
|
|
a106f7eb6f | ||
|
|
1c2aac5547 | ||
|
|
3af7df0e84 | ||
|
|
582cb31de6 | ||
|
|
f28c62f7ad |
@@ -699,7 +699,7 @@ func testResourceSCheckThresholdAfterResourceAllocate(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
expBody := `{"*opts":{"*actProfileIDs":["actPrfID"],"*eventType":"ResourceUpdate","*rsUnits":1,"*rsUsageID":"RU_1","*thdProfileIDs":["THD_1"]},"*req":{"EventType":"ResourceUpdate","ResourceID":"RES_1","Usage":1}}`
|
||||
expBody := `{"*opts":{"*actProfileIDs":["actPrfID"],"*eventType":"ResourceUpdate","*resUnits":1,"*resUsageID":"RU_1","*thdProfileIDs":["THD_1"]},"*req":{"EventType":"ResourceUpdate","ResourceID":"RES_1","Usage":1}}`
|
||||
if err := rsRPC.Call(context.Background(), utils.ResourceSv1AllocateResources,
|
||||
argsRU, &reply); err != nil {
|
||||
t.Error(err)
|
||||
|
||||
@@ -3747,3 +3747,252 @@ func TestResourceMatchingResourcesForEventWeightFromDynamicsErr(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
func TestStoreMatchedResourcesStoreIntervalZero(t *testing.T) {
|
||||
cfg := config.NewDefaultCGRConfig()
|
||||
cfg.ResourceSCfg().StoreInterval = 0
|
||||
|
||||
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)
|
||||
|
||||
rS := &ResourceS{
|
||||
dm: dm,
|
||||
cfg: cfg,
|
||||
storedResources: utils.NewStringSet(nil),
|
||||
}
|
||||
|
||||
dirty := true
|
||||
resources := Resources{
|
||||
{
|
||||
Resource: &utils.Resource{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RES1",
|
||||
Usages: map[string]*utils.ResourceUsage{},
|
||||
},
|
||||
dirty: &dirty,
|
||||
},
|
||||
}
|
||||
|
||||
err := rS.storeMatchedResources(context.Background(), resources)
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil error, got: %v", err)
|
||||
}
|
||||
|
||||
if !*resources[0].dirty {
|
||||
t.Error("Expected dirty flag to remain true when StoreInterval is 0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoreMatchedResourcesStoreIntervalPositive(t *testing.T) {
|
||||
cfg := config.NewDefaultCGRConfig()
|
||||
cfg.ResourceSCfg().StoreInterval = 10 * time.Second
|
||||
|
||||
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)
|
||||
|
||||
rS := &ResourceS{
|
||||
dm: dm,
|
||||
cfg: cfg,
|
||||
storedResources: utils.NewStringSet(nil),
|
||||
}
|
||||
|
||||
dirty := true
|
||||
resources := Resources{
|
||||
{
|
||||
Resource: &utils.Resource{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RES1",
|
||||
Usages: map[string]*utils.ResourceUsage{},
|
||||
},
|
||||
dirty: &dirty,
|
||||
},
|
||||
{
|
||||
Resource: &utils.Resource{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RES2",
|
||||
Usages: map[string]*utils.ResourceUsage{},
|
||||
},
|
||||
dirty: &dirty,
|
||||
},
|
||||
}
|
||||
|
||||
err := rS.storeMatchedResources(context.Background(), resources)
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil error, got: %v", err)
|
||||
}
|
||||
|
||||
if !*resources[0].dirty {
|
||||
t.Error("Expected dirty flag to be true for RES1")
|
||||
}
|
||||
if !*resources[1].dirty {
|
||||
t.Error("Expected dirty flag to be true for RES2")
|
||||
}
|
||||
|
||||
if !rS.storedResources.Has("cgrates.org:RES1") {
|
||||
t.Error("Expected RES1 to be in storedResources set")
|
||||
}
|
||||
if !rS.storedResources.Has("cgrates.org:RES2") {
|
||||
t.Error("Expected RES2 to be in storedResources set")
|
||||
}
|
||||
|
||||
if rS.storedResources.Size() != 2 {
|
||||
t.Errorf("Expected 2 resources in set, got: %d", rS.storedResources.Size())
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoreMatchedResourcesStoreIntervalNegative(t *testing.T) {
|
||||
cfg := config.NewDefaultCGRConfig()
|
||||
cfg.ResourceSCfg().StoreInterval = -1
|
||||
|
||||
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)
|
||||
|
||||
rS := &ResourceS{
|
||||
dm: dm,
|
||||
cfg: cfg,
|
||||
storedResources: utils.NewStringSet(nil),
|
||||
}
|
||||
|
||||
dirty := true
|
||||
resources := Resources{
|
||||
{
|
||||
Resource: &utils.Resource{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RES1",
|
||||
Usages: map[string]*utils.ResourceUsage{},
|
||||
},
|
||||
dirty: &dirty,
|
||||
rPrf: &resourceProfile{
|
||||
ResourceProfile: &utils.ResourceProfile{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RES1",
|
||||
Limit: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := rS.storeMatchedResources(context.Background(), resources)
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil error, got: %v", err)
|
||||
}
|
||||
|
||||
savedRes, err := dm.GetResource(context.Background(), "cgrates.org", "RES1", false, false, "")
|
||||
if err != nil {
|
||||
t.Errorf("Expected resource to be stored in database, got error: %v", err)
|
||||
}
|
||||
if savedRes == nil {
|
||||
t.Error("Expected resource to be stored in database")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoreMatchedResourcesNoDirtyFlag(t *testing.T) {
|
||||
cfg := config.NewDefaultCGRConfig()
|
||||
cfg.ResourceSCfg().StoreInterval = -1
|
||||
|
||||
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)
|
||||
|
||||
rS := &ResourceS{
|
||||
dm: dm,
|
||||
cfg: cfg,
|
||||
storedResources: utils.NewStringSet(nil),
|
||||
}
|
||||
|
||||
resources := Resources{
|
||||
{
|
||||
Resource: &utils.Resource{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RES1",
|
||||
Usages: map[string]*utils.ResourceUsage{},
|
||||
},
|
||||
dirty: nil,
|
||||
},
|
||||
}
|
||||
|
||||
err := rS.storeMatchedResources(context.Background(), resources)
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil error, got: %v", err)
|
||||
}
|
||||
|
||||
_, err = dm.GetResource(context.Background(), "cgrates.org", "RES1", false, false, "")
|
||||
if err == nil {
|
||||
t.Error("Expected resource with nil dirty flag not to be stored")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoreMatchedResourcesWithUsages(t *testing.T) {
|
||||
cfg := config.NewDefaultCGRConfig()
|
||||
cfg.ResourceSCfg().StoreInterval = -1
|
||||
|
||||
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)
|
||||
|
||||
rS := &ResourceS{
|
||||
dm: dm,
|
||||
cfg: cfg,
|
||||
storedResources: utils.NewStringSet(nil),
|
||||
}
|
||||
|
||||
dirty := true
|
||||
resources := Resources{
|
||||
{
|
||||
Resource: &utils.Resource{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RES1",
|
||||
Usages: map[string]*utils.ResourceUsage{
|
||||
"USAGE1": {
|
||||
Tenant: "cgrates.org",
|
||||
ID: "USAGE1",
|
||||
ExpiryTime: time.Now().Add(1 * time.Hour),
|
||||
Units: 5,
|
||||
},
|
||||
"USAGE2": {
|
||||
Tenant: "cgrates.org",
|
||||
ID: "USAGE2",
|
||||
ExpiryTime: time.Now().Add(2 * time.Hour),
|
||||
Units: 3,
|
||||
},
|
||||
},
|
||||
TTLIdx: []string{"USAGE1", "USAGE2"},
|
||||
},
|
||||
dirty: &dirty,
|
||||
rPrf: &resourceProfile{
|
||||
ResourceProfile: &utils.ResourceProfile{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RES1",
|
||||
Limit: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := rS.storeMatchedResources(context.Background(), resources)
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil error, got: %v", err)
|
||||
}
|
||||
|
||||
savedRes, err := dm.GetResource(context.Background(), "cgrates.org", "RES1", false, false, "")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected resource to be stored, got error: %v", err)
|
||||
}
|
||||
|
||||
if len(savedRes.Usages) != 2 {
|
||||
t.Errorf("Expected 2 usages, got: %d", len(savedRes.Usages))
|
||||
}
|
||||
if _, exists := savedRes.Usages["USAGE1"]; !exists {
|
||||
t.Error("Expected USAGE1 to be stored")
|
||||
}
|
||||
if _, exists := savedRes.Usages["USAGE2"]; !exists {
|
||||
t.Error("Expected USAGE2 to be stored")
|
||||
}
|
||||
|
||||
if len(savedRes.TTLIdx) != 2 {
|
||||
t.Errorf("Expected 2 TTL indexes, got: %d", len(savedRes.TTLIdx))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -272,3 +272,304 @@ func TestPopulateResourcesForRoutesLazyPassErr(t *testing.T) {
|
||||
t.Errorf("Expected error <%v>, received <%v>", expErr, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceDescendentSorterSortRoutesNoResourceSConns(t *testing.T) {
|
||||
cfg := config.NewDefaultCGRConfig()
|
||||
cM := engine.NewConnManager(cfg)
|
||||
rds := NewResourceDescendentSorter(cfg, cM)
|
||||
|
||||
routes := map[string]*RouteWithWeight{}
|
||||
ev := &utils.CGREvent{
|
||||
Tenant: "cgrates.org",
|
||||
Event: map[string]any{},
|
||||
APIOpts: map[string]any{},
|
||||
}
|
||||
extraOpts := &optsGetRoutes{}
|
||||
|
||||
_, err := rds.SortRoutes(context.Background(), "PROFILE1", routes, ev, extraOpts)
|
||||
errExpect := "MANDATORY_IE_MISSING: [connIDs]"
|
||||
if err == nil || err.Error() != errExpect {
|
||||
t.Errorf("Expected %v\n but received %v", errExpect, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceDescendentSorterSortRoutesNoResourceIDs(t *testing.T) {
|
||||
cfg := config.NewDefaultCGRConfig()
|
||||
cfg.RouteSCfg().ResourceSConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaResources)}
|
||||
cM := engine.NewConnManager(cfg)
|
||||
rds := NewResourceDescendentSorter(cfg, cM)
|
||||
|
||||
routes := map[string]*RouteWithWeight{
|
||||
"RW": {
|
||||
Route: &utils.Route{},
|
||||
},
|
||||
}
|
||||
ev := &utils.CGREvent{
|
||||
Tenant: "cgrates.org",
|
||||
Event: map[string]any{},
|
||||
APIOpts: map[string]any{},
|
||||
}
|
||||
extraOpts := &optsGetRoutes{}
|
||||
|
||||
_, err := rds.SortRoutes(context.Background(), "PROFILE1", routes, ev, extraOpts)
|
||||
errExpect := "MANDATORY_IE_MISSING: [ResourceIDs]"
|
||||
if err == nil || err.Error() != errExpect {
|
||||
t.Errorf("Expected %v\n but received %v", errExpect, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceDescendentSorterSortRoutesOK(t *testing.T) {
|
||||
|
||||
defer func() {
|
||||
engine.Cache = engine.NewCacheS(config.NewDefaultCGRConfig(), nil, nil, nil)
|
||||
}()
|
||||
|
||||
cfg := config.NewDefaultCGRConfig()
|
||||
cfg.RouteSCfg().ResourceSConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaResources)}
|
||||
|
||||
res1 := &utils.Resource{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RSC1",
|
||||
Usages: map[string]*utils.ResourceUsage{
|
||||
"RU1": {
|
||||
ID: "RU1",
|
||||
Units: 10,
|
||||
},
|
||||
},
|
||||
}
|
||||
res2 := &utils.Resource{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RSC2",
|
||||
Usages: map[string]*utils.ResourceUsage{
|
||||
"RU2": {
|
||||
ID: "RU2",
|
||||
Units: 5,
|
||||
},
|
||||
},
|
||||
}
|
||||
res3 := &utils.Resource{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RSC3",
|
||||
Usages: map[string]*utils.ResourceUsage{
|
||||
"RU3": {
|
||||
ID: "RU3",
|
||||
Units: 15,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cc := make(chan birpc.ClientConnector, 1)
|
||||
cc <- &ccMock{
|
||||
calls: map[string]func(ctx *context.Context, args any, reply any) error{
|
||||
utils.ResourceSv1GetResource: func(ctx *context.Context, args, reply any) error {
|
||||
rplCast, canCast := reply.(*utils.Resource)
|
||||
if !canCast {
|
||||
t.Errorf("Wrong argument type : %T", reply)
|
||||
return nil
|
||||
}
|
||||
argsCast := args.(*utils.TenantIDWithAPIOpts)
|
||||
switch argsCast.ID {
|
||||
case "RSC1":
|
||||
*rplCast = *res1
|
||||
case "RSC2":
|
||||
*rplCast = *res2
|
||||
case "RSC3":
|
||||
*rplCast = *res3
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
cM := engine.NewConnManager(cfg)
|
||||
cM.AddInternalConn(utils.ConcatenatedKey(utils.MetaInternal, utils.MetaResources), utils.ResourceSv1, cc)
|
||||
|
||||
rds := NewResourceDescendentSorter(cfg, cM)
|
||||
|
||||
routes := map[string]*RouteWithWeight{
|
||||
"RW1": {
|
||||
Route: &utils.Route{
|
||||
ID: "Route1",
|
||||
ResourceIDs: []string{"RSC1"},
|
||||
RouteParameters: "param1",
|
||||
},
|
||||
Weight: 10,
|
||||
},
|
||||
"RW2": {
|
||||
Route: &utils.Route{
|
||||
ID: "Route2",
|
||||
ResourceIDs: []string{"RSC2"},
|
||||
RouteParameters: "param2",
|
||||
},
|
||||
Weight: 20,
|
||||
},
|
||||
"RW3": {
|
||||
Route: &utils.Route{
|
||||
ID: "Route3",
|
||||
ResourceIDs: []string{"RSC3"},
|
||||
RouteParameters: "param3",
|
||||
},
|
||||
Weight: 15,
|
||||
blocker: true,
|
||||
},
|
||||
}
|
||||
ev := &utils.CGREvent{
|
||||
Tenant: "cgrates.org",
|
||||
Event: map[string]any{},
|
||||
APIOpts: map[string]any{},
|
||||
}
|
||||
extraOpts := &optsGetRoutes{}
|
||||
|
||||
rcv, err := rds.SortRoutes(context.Background(), "PROFILE1", routes, ev, extraOpts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if rcv.ProfileID != "PROFILE1" {
|
||||
t.Errorf("Expected ProfileID <%s>, received <%s>", "PROFILE1", rcv.ProfileID)
|
||||
}
|
||||
|
||||
if rcv.Sorting != utils.MetaReds {
|
||||
t.Errorf("Expected Sorting <%s>, received <%s>", utils.MetaReds, rcv.Sorting)
|
||||
}
|
||||
|
||||
if len(rcv.Routes) != 3 {
|
||||
t.Fatalf("Expected 3 routes, received %d", len(rcv.Routes))
|
||||
}
|
||||
|
||||
if rcv.Routes[0].RouteID != "Route3" {
|
||||
t.Errorf("Expected first route to be Route3, got %s", rcv.Routes[0].RouteID)
|
||||
}
|
||||
if rcv.Routes[1].RouteID != "Route1" {
|
||||
t.Errorf("Expected second route to be Route1, got %s", rcv.Routes[1].RouteID)
|
||||
}
|
||||
if rcv.Routes[2].RouteID != "Route2" {
|
||||
t.Errorf("Expected third route to be Route2, got %s", rcv.Routes[2].RouteID)
|
||||
}
|
||||
|
||||
if _, hasBlocker := rcv.Routes[0].SortingData[utils.Blocker]; !hasBlocker {
|
||||
t.Errorf("Expected Route3 to have blocker flag")
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceDescendentSorterSortRoutesEmptyRoutes(t *testing.T) {
|
||||
cfg := config.NewDefaultCGRConfig()
|
||||
cfg.RouteSCfg().ResourceSConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaResources)}
|
||||
|
||||
cc := make(chan birpc.ClientConnector, 1)
|
||||
cc <- &ccMock{
|
||||
calls: map[string]func(ctx *context.Context, args any, reply any) error{
|
||||
utils.ResourceSv1GetResource: func(ctx *context.Context, args, reply any) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
cM := engine.NewConnManager(cfg)
|
||||
cM.AddInternalConn(utils.ConcatenatedKey(utils.MetaInternal, utils.MetaResources), utils.ResourceSv1, cc)
|
||||
|
||||
rds := NewResourceDescendentSorter(cfg, cM)
|
||||
|
||||
routes := map[string]*RouteWithWeight{}
|
||||
ev := &utils.CGREvent{
|
||||
Tenant: "cgrates.org",
|
||||
Event: map[string]any{},
|
||||
APIOpts: map[string]any{},
|
||||
}
|
||||
extraOpts := &optsGetRoutes{}
|
||||
|
||||
rcv, err := rds.SortRoutes(context.Background(), "PROFILE1", routes, ev, extraOpts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if rcv.ProfileID != "PROFILE1" {
|
||||
t.Errorf("Expected ProfileID <%s>, received <%s>", "PROFILE1", rcv.ProfileID)
|
||||
}
|
||||
|
||||
if rcv.Sorting != utils.MetaReds {
|
||||
t.Errorf("Expected Sorting <%s>, received <%s>", utils.MetaReds, rcv.Sorting)
|
||||
}
|
||||
|
||||
if len(rcv.Routes) != 0 {
|
||||
t.Errorf("Expected 0 routes, received %d", len(rcv.Routes))
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceDescendentSorterSortRoutesSingleRoute(t *testing.T) {
|
||||
|
||||
defer func() {
|
||||
engine.Cache = engine.NewCacheS(config.NewDefaultCGRConfig(), nil, nil, nil)
|
||||
}()
|
||||
|
||||
cfg := config.NewDefaultCGRConfig()
|
||||
cfg.RouteSCfg().ResourceSConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaResources)}
|
||||
|
||||
res := &utils.Resource{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RSC1",
|
||||
Usages: map[string]*utils.ResourceUsage{
|
||||
"RU1": {
|
||||
ID: "RU1",
|
||||
Units: 25,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cc := make(chan birpc.ClientConnector, 1)
|
||||
cc <- &ccMock{
|
||||
calls: map[string]func(ctx *context.Context, args any, reply any) error{
|
||||
utils.ResourceSv1GetResource: func(ctx *context.Context, args, reply any) error {
|
||||
rplCast, canCast := reply.(*utils.Resource)
|
||||
if !canCast {
|
||||
t.Errorf("Wrong argument type : %T", reply)
|
||||
return nil
|
||||
}
|
||||
*rplCast = *res
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
cM := engine.NewConnManager(cfg)
|
||||
cM.AddInternalConn(utils.ConcatenatedKey(utils.MetaInternal, utils.MetaResources), utils.ResourceSv1, cc)
|
||||
|
||||
rds := NewResourceDescendentSorter(cfg, cM)
|
||||
|
||||
routes := map[string]*RouteWithWeight{
|
||||
"RW": {
|
||||
Route: &utils.Route{
|
||||
ID: "Route1",
|
||||
ResourceIDs: []string{"RSC1"},
|
||||
RouteParameters: "param1",
|
||||
},
|
||||
Weight: 50,
|
||||
},
|
||||
}
|
||||
ev := &utils.CGREvent{
|
||||
Tenant: "cgrates.org",
|
||||
Event: map[string]any{},
|
||||
APIOpts: map[string]any{},
|
||||
}
|
||||
|
||||
exp := &SortedRoutes{
|
||||
ProfileID: "PROFILE1",
|
||||
Sorting: utils.MetaReds,
|
||||
Routes: []*SortedRoute{
|
||||
{
|
||||
RouteID: "Route1",
|
||||
RouteParameters: "param1",
|
||||
SortingData: map[string]any{
|
||||
utils.ResourceUsageStr: 25.0,
|
||||
utils.Weight: 50.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
rcv, err := rds.SortRoutes(context.Background(), "PROFILE1", routes, ev, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(utils.ToJSON(exp), utils.ToJSON(rcv)) {
|
||||
t.Errorf("Expected \n<%+v>,\n received \n<%+v>", utils.ToJSON(exp), utils.ToJSON(rcv))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -955,6 +955,9 @@ func (sS *SessionS) BiRPCv1ProcessEvent(ctx *context.Context,
|
||||
if resID, err = sS.resourcesAuthorize(ctx, cgrEv); err != nil {
|
||||
return
|
||||
}
|
||||
if apiRply.ResourceAllocation == nil {
|
||||
apiRply.ResourceAllocation = make(map[string]string)
|
||||
}
|
||||
apiRply.ResourceAllocation[runID] = resID
|
||||
}
|
||||
|
||||
|
||||
320
sessions/sessions_processeventres_it_test.go
Normal file
320
sessions/sessions_processeventres_it_test.go
Normal file
@@ -0,0 +1,320 @@
|
||||
//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 sessions
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cgrates/birpc/context"
|
||||
"github.com/cgrates/cgrates/engine"
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
)
|
||||
|
||||
func TestSessionSv1ProcessEventResourcesAuthorize(t *testing.T) {
|
||||
var dbcfg engine.DBCfg
|
||||
switch *utils.DBType {
|
||||
case utils.MetaInternal:
|
||||
dbcfg = engine.InternalDBCfg
|
||||
case utils.MetaRedis:
|
||||
dbcfg = engine.RedisDBCfg
|
||||
case utils.MetaMySQL:
|
||||
dbcfg = engine.MySQLDBCfg
|
||||
case utils.MetaMongo:
|
||||
dbcfg = engine.MongoDBCfg
|
||||
case utils.MetaPostgres:
|
||||
dbcfg = engine.PostgresDBCfg
|
||||
default:
|
||||
t.Fatal("unsupported dbtype value")
|
||||
}
|
||||
|
||||
ng := engine.TestEngine{
|
||||
ConfigJSON: `{
|
||||
"logger": { "level": 7 },
|
||||
"sessions": {
|
||||
"enabled": true,
|
||||
"resources_conns": ["*localhost"]
|
||||
},
|
||||
"resources": {
|
||||
"enabled": true,
|
||||
"store_interval": "-1"
|
||||
},
|
||||
"admins": { "enabled": true }
|
||||
}`,
|
||||
TpFiles: map[string]string{
|
||||
utils.ResourcesCsv: `#Tenant[0],Id[1],FilterIDs[2],Weights[3],TTL[4],Limit[5],AllocationMessage[6],Blocker[7],Stored[8],ThresholdIDs[9]
|
||||
cgrates.org,RES1,*string:~*req.Account:1001,;10,1h,3,ResourceAllocationSuccess,false,true,`,
|
||||
},
|
||||
DBCfg: dbcfg,
|
||||
Encoding: *utils.Encoding,
|
||||
// LogBuffer: new(bytes.Buffer),
|
||||
}
|
||||
|
||||
// t.Cleanup(func() {
|
||||
// if ng.LogBuffer != nil {
|
||||
// fmt.Println(ng.LogBuffer)
|
||||
// }
|
||||
// })
|
||||
|
||||
client, _ := ng.Run(t)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
t.Run("noFlags", func(t *testing.T) {
|
||||
var rply V1ProcessEventReply
|
||||
err := client.Call(context.Background(), utils.SessionSv1ProcessEvent,
|
||||
&utils.CGREvent{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "noFlags",
|
||||
Event: map[string]any{
|
||||
utils.AccountField: "1001",
|
||||
},
|
||||
}, &rply)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(rply.ResourceAllocation) != 0 {
|
||||
t.Fatalf("expected no allocation, got %v", rply.ResourceAllocation)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("resourcesOnly", func(t *testing.T) {
|
||||
var rply V1ProcessEventReply
|
||||
err := client.Call(context.Background(), utils.SessionSv1ProcessEvent,
|
||||
&utils.CGREvent{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "resourcesOnly",
|
||||
APIOpts: map[string]any{
|
||||
utils.MetaResources: true,
|
||||
},
|
||||
Event: map[string]any{
|
||||
utils.AccountField: "1001",
|
||||
},
|
||||
}, &rply)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(rply.ResourceAllocation) != 0 {
|
||||
t.Fatalf("expected no allocation, got %v", rply.ResourceAllocation)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("authorizeAndResources", func(t *testing.T) {
|
||||
var rply V1ProcessEventReply
|
||||
err := client.Call(context.Background(), utils.SessionSv1ProcessEvent,
|
||||
&utils.CGREvent{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "authorizeAndResources",
|
||||
APIOpts: map[string]any{
|
||||
utils.MetaAuthorize: true,
|
||||
utils.MetaResources: true,
|
||||
utils.OptsResourcesUsageID: "usage1",
|
||||
},
|
||||
Event: map[string]any{
|
||||
utils.AccountField: "1001",
|
||||
},
|
||||
}, &rply)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if msg := rply.ResourceAllocation[utils.MetaDefault]; msg != "ResourceAllocationSuccess" {
|
||||
t.Fatalf("unexpected allocation msg: %q", msg)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("resourcesAuthorizeFlag", func(t *testing.T) {
|
||||
var rply V1ProcessEventReply
|
||||
err := client.Call(context.Background(), utils.SessionSv1ProcessEvent,
|
||||
&utils.CGREvent{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "resourcesAuthorizeFlag",
|
||||
APIOpts: map[string]any{
|
||||
utils.MetaResourcesAuthorizeCfg: true,
|
||||
utils.OptsResourcesUsageID: "2",
|
||||
utils.OptsResourcesUnits: 1,
|
||||
},
|
||||
Event: map[string]any{
|
||||
utils.AccountField: "1001",
|
||||
},
|
||||
}, &rply)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if msg := rply.ResourceAllocation[utils.MetaDefault]; msg != "ResourceAllocationSuccess" {
|
||||
t.Fatalf("unexpected allocation msg: %q", msg)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("missingUsageID", func(t *testing.T) {
|
||||
var rply V1ProcessEventReply
|
||||
err := client.Call(context.Background(), utils.SessionSv1ProcessEvent,
|
||||
&utils.CGREvent{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "missingUsageID",
|
||||
APIOpts: map[string]any{
|
||||
utils.MetaResourcesAuthorizeCfg: true,
|
||||
},
|
||||
Event: map[string]any{
|
||||
utils.AccountField: "1001",
|
||||
},
|
||||
}, &rply)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if _, exists := rply.ResourceAllocation[utils.MetaDefault]; !exists {
|
||||
t.Fatalf("expected allocation entry")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("noMatchingProfile", func(t *testing.T) {
|
||||
var rply V1ProcessEventReply
|
||||
err := client.Call(context.Background(), utils.SessionSv1ProcessEvent,
|
||||
&utils.CGREvent{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "noMatchingProfile",
|
||||
APIOpts: map[string]any{
|
||||
utils.MetaResourcesAuthorizeCfg: true,
|
||||
utils.OptsResourcesUsageID: "usage-nomatch",
|
||||
},
|
||||
Event: map[string]any{
|
||||
utils.AccountField: "9999",
|
||||
},
|
||||
}, &rply)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if msg := rply.ResourceAllocation[utils.MetaDefault]; msg != "" {
|
||||
t.Fatalf("expected empty allocation msg, got %q", msg)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("authorizeWithoutResourcesFlag", func(t *testing.T) {
|
||||
var rply V1ProcessEventReply
|
||||
err := client.Call(context.Background(), utils.SessionSv1ProcessEvent,
|
||||
&utils.CGREvent{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "authorizeWithoutResourcesFlag",
|
||||
APIOpts: map[string]any{
|
||||
utils.MetaAuthorize: true,
|
||||
utils.OptsResourcesUsageID: "usage3",
|
||||
},
|
||||
Event: map[string]any{
|
||||
utils.AccountField: "1001",
|
||||
},
|
||||
}, &rply)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(rply.ResourceAllocation) != 0 {
|
||||
t.Fatalf("expected no allocation without resources flag, got %v", rply.ResourceAllocation)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("multipleResourceAllocations", func(t *testing.T) {
|
||||
for i := 1; i <= 3; i++ {
|
||||
var rply V1ProcessEventReply
|
||||
err := client.Call(context.Background(), utils.SessionSv1ProcessEvent,
|
||||
&utils.CGREvent{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "multipleResourceAllocations",
|
||||
APIOpts: map[string]any{
|
||||
utils.MetaResourcesAuthorizeCfg: true,
|
||||
utils.OptsResourcesUsageID: utils.ConcatenatedKey("usage-multi", strconv.Itoa(i)),
|
||||
utils.OptsResourcesUnits: 1,
|
||||
},
|
||||
Event: map[string]any{
|
||||
utils.AccountField: "1001",
|
||||
},
|
||||
}, &rply)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("allocation %d failed: %v", i, err)
|
||||
}
|
||||
|
||||
if msg := rply.ResourceAllocation[utils.MetaDefault]; msg != "ResourceAllocationSuccess" {
|
||||
t.Fatalf("allocation %d: unexpected msg: %q", i, msg)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("resourcesWithZeroUnits", func(t *testing.T) {
|
||||
var rply V1ProcessEventReply
|
||||
err := client.Call(context.Background(), utils.SessionSv1ProcessEvent,
|
||||
&utils.CGREvent{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "resourcesWithZeroUnits",
|
||||
APIOpts: map[string]any{
|
||||
utils.MetaResourcesAuthorizeCfg: true,
|
||||
utils.OptsResourcesUsageID: "usage-zero",
|
||||
utils.OptsResourcesUnits: 0,
|
||||
},
|
||||
Event: map[string]any{
|
||||
utils.AccountField: "1001",
|
||||
},
|
||||
}, &rply)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if _, exists := rply.ResourceAllocation[utils.MetaDefault]; !exists {
|
||||
t.Fatal("expected allocation entry")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("resourcesAuthorizeCfgWithAuthorize", func(t *testing.T) {
|
||||
var rply V1ProcessEventReply
|
||||
err := client.Call(context.Background(), utils.SessionSv1ProcessEvent,
|
||||
&utils.CGREvent{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "resourcesAuthorizeCfgWithAuthorize",
|
||||
APIOpts: map[string]any{
|
||||
utils.MetaAuthorize: true,
|
||||
utils.MetaResourcesAuthorizeCfg: true,
|
||||
utils.OptsResourcesUsageID: "usage-both-flags",
|
||||
},
|
||||
Event: map[string]any{
|
||||
utils.AccountField: "1001",
|
||||
},
|
||||
}, &rply)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if msg := rply.ResourceAllocation[utils.MetaDefault]; msg != "ResourceAllocationSuccess" {
|
||||
t.Fatalf("unexpected allocation msg: %q", msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -389,3 +389,269 @@ func TestResourceProfileMerge(t *testing.T) {
|
||||
t.Errorf("Expected %v \n but received \n %v", ToJSON(exp), ToJSON(dp))
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceProfileCloneCacheClone(t *testing.T) {
|
||||
var nilRP *ResourceProfile
|
||||
if nilRP.Clone() != nil {
|
||||
t.Fatal("Expected nil clone for nil ResourceProfile")
|
||||
}
|
||||
|
||||
rp := &ResourceProfile{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RP1",
|
||||
FilterIDs: []string{"fltr1", "*string:~*req.Account:1001"},
|
||||
ThresholdIDs: []string{"TH1"},
|
||||
UsageTTL: 10,
|
||||
Limit: 100,
|
||||
AllocationMessage: "ok",
|
||||
Blocker: true,
|
||||
Stored: true,
|
||||
Weights: DynamicWeights{
|
||||
{Weight: 10},
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
clone func(*ResourceProfile) *ResourceProfile
|
||||
}{
|
||||
{
|
||||
name: "Clone",
|
||||
clone: func(r *ResourceProfile) *ResourceProfile {
|
||||
return r.Clone()
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "CacheClone",
|
||||
clone: func(r *ResourceProfile) *ResourceProfile {
|
||||
cc := r.CacheClone()
|
||||
cl, ok := cc.(*ResourceProfile)
|
||||
if !ok {
|
||||
t.Fatalf("CacheClone returned %T, expected *ResourceProfile", cc)
|
||||
}
|
||||
return cl
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cl := tt.clone(rp)
|
||||
|
||||
if !reflect.DeepEqual(rp, cl) {
|
||||
t.Fatalf("%s not equal\nexp=%v\ngot=%v",
|
||||
tt.name, ToJSON(rp), ToJSON(cl))
|
||||
}
|
||||
|
||||
cl.FilterIDs[0] = "changed"
|
||||
cl.ThresholdIDs[0] = "changed"
|
||||
cl.Weights[0].Weight = 99
|
||||
|
||||
if rp.FilterIDs[0] == "changed" {
|
||||
t.Errorf("%s did not deep-copy FilterIDs", tt.name)
|
||||
}
|
||||
if rp.ThresholdIDs[0] == "changed" {
|
||||
t.Errorf("%s did not deep-copy ThresholdIDs", tt.name)
|
||||
}
|
||||
if rp.Weights[0].Weight == 99 {
|
||||
t.Errorf("%s did not deep-copy Weights", tt.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceCloneAndCacheClone(t *testing.T) {
|
||||
var nilR *Resource
|
||||
if nilR.Clone() != nil {
|
||||
t.Fatal("Expected nil clone for nil Resource")
|
||||
}
|
||||
|
||||
r := &Resource{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RES1",
|
||||
Usages: map[string]*ResourceUsage{
|
||||
"u1": {
|
||||
Tenant: "cgrates.org",
|
||||
ID: "U1",
|
||||
Units: 10,
|
||||
ExpiryTime: time.Now().Add(time.Hour),
|
||||
},
|
||||
"u2": {
|
||||
Tenant: "cgrates.org",
|
||||
ID: "U2",
|
||||
Units: 20,
|
||||
},
|
||||
},
|
||||
TTLIdx: []string{"u1", "u2"},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
clone func(*Resource) *Resource
|
||||
}{
|
||||
{
|
||||
name: "Clone",
|
||||
clone: func(res *Resource) *Resource {
|
||||
return res.Clone()
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "CacheClone",
|
||||
clone: func(res *Resource) *Resource {
|
||||
cc := res.CacheClone()
|
||||
cl, ok := cc.(*Resource)
|
||||
if !ok {
|
||||
t.Fatalf("CacheClone returned %T, expected *Resource", cc)
|
||||
}
|
||||
return cl
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cl := tt.clone(r)
|
||||
|
||||
if !reflect.DeepEqual(r, cl) {
|
||||
t.Fatalf("%s not equal\nexp=%v\ngot=%v",
|
||||
tt.name, ToJSON(r), ToJSON(cl))
|
||||
}
|
||||
|
||||
delete(cl.Usages, "u1")
|
||||
if _, ok := r.Usages["u1"]; !ok {
|
||||
t.Errorf("%s did not deep-copy Usages map", tt.name)
|
||||
}
|
||||
|
||||
cl.Usages["u2"].Units = 999
|
||||
if r.Usages["u2"].Units == 999 {
|
||||
t.Errorf("%s did not deep-copy ResourceUsage", tt.name)
|
||||
}
|
||||
|
||||
cl.TTLIdx[0] = "changed"
|
||||
if r.TTLIdx[0] == "changed" {
|
||||
t.Errorf("%s did not deep-copy TTLIdx", tt.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceAsMapStringInterface(t *testing.T) {
|
||||
var nilR *Resource
|
||||
if nilR.AsMapStringInterface() != nil {
|
||||
t.Fatal("Expected nil map for nil Resource")
|
||||
}
|
||||
|
||||
r := &Resource{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RES1",
|
||||
Usages: map[string]*ResourceUsage{
|
||||
"u1": {
|
||||
Tenant: "cgrates.org",
|
||||
ID: "U1",
|
||||
Units: 10,
|
||||
},
|
||||
},
|
||||
TTLIdx: []string{"u1"},
|
||||
}
|
||||
|
||||
m := r.AsMapStringInterface()
|
||||
|
||||
if m == nil {
|
||||
t.Fatal("Expected non-nil map")
|
||||
}
|
||||
|
||||
if v, ok := m[Tenant].(string); !ok || v != r.Tenant {
|
||||
t.Errorf("Tenant mismatch, expected %v got %v", r.Tenant, m[Tenant])
|
||||
}
|
||||
if v, ok := m[ID].(string); !ok || v != r.ID {
|
||||
t.Errorf("ID mismatch, expected %v got %v", r.ID, m[ID])
|
||||
}
|
||||
|
||||
usg, ok := m[Usages].(map[string]*ResourceUsage)
|
||||
if !ok {
|
||||
t.Fatalf("Expected Usages as map[string]*ResourceUsage, got %T", m[Usages])
|
||||
}
|
||||
if !reflect.DeepEqual(usg, r.Usages) {
|
||||
t.Errorf("Usages mismatch\nexp=%v\ngot=%v", ToJSON(r.Usages), ToJSON(usg))
|
||||
}
|
||||
|
||||
ttl, ok := m[TTLIdx].([]string)
|
||||
if !ok {
|
||||
t.Fatalf("Expected TTLIdx as []string, got %T", m[TTLIdx])
|
||||
}
|
||||
if !reflect.DeepEqual(ttl, r.TTLIdx) {
|
||||
t.Errorf("TTLIdx mismatch\nexp=%v\ngot=%v", ToJSON(r.TTLIdx), ToJSON(ttl))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapStringInterfaceToResource(t *testing.T) {
|
||||
m := map[string]any{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RES1",
|
||||
Usages: map[string]any{
|
||||
"u1": map[string]any{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "U1",
|
||||
Units: 10.0,
|
||||
},
|
||||
"u2": &ResourceUsage{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "U2",
|
||||
Units: 20,
|
||||
},
|
||||
},
|
||||
TTLIdx: []any{"u1", "u2"},
|
||||
}
|
||||
|
||||
r := MapStringInterfaceToResource(m)
|
||||
|
||||
if r == nil {
|
||||
t.Fatal("Expected non-nil Resource")
|
||||
}
|
||||
|
||||
if r.Tenant != "cgrates.org" {
|
||||
t.Errorf("Tenant mismatch, got %v", r.Tenant)
|
||||
}
|
||||
if r.ID != "RES1" {
|
||||
t.Errorf("ID mismatch, got %v", r.ID)
|
||||
}
|
||||
|
||||
if len(r.Usages) != 2 {
|
||||
t.Fatalf("Expected 2 usages, got %d", len(r.Usages))
|
||||
}
|
||||
|
||||
if u1 := r.Usages["u1"]; u1 == nil || u1.ID != "U1" || u1.Units != 10 {
|
||||
t.Errorf("Usage u1 not correctly mapped: %+v", u1)
|
||||
}
|
||||
if u2 := r.Usages["u2"]; u2 == nil || u2.ID != "U2" || u2.Units != 20 {
|
||||
t.Errorf("Usage u2 not correctly mapped: %+v", u2)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(r.TTLIdx, []string{"u1", "u2"}) {
|
||||
t.Errorf("TTLIdx mismatch, got %v", r.TTLIdx)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceProfileLockKey(t *testing.T) {
|
||||
tnt := "cgrates.org"
|
||||
id := "RP1"
|
||||
|
||||
exp := ConcatenatedKey(CacheResourceProfiles, tnt, id)
|
||||
got := ResourceProfileLockKey(tnt, id)
|
||||
|
||||
if exp != got {
|
||||
t.Errorf("ResourceProfileLockKey mismatch\nexp=%v\ngot=%v", exp, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceLockKey(t *testing.T) {
|
||||
tnt := "cgrates.org"
|
||||
id := "RES1"
|
||||
|
||||
exp := ConcatenatedKey(CacheResources, tnt, id)
|
||||
got := ResourceLockKey(tnt, id)
|
||||
|
||||
if exp != got {
|
||||
t.Errorf("ResourceLockKey mismatch\nexp=%v\ngot=%v", exp, got)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user