Files
cgrates/resources/resources_test.go
2025-05-29 15:46:42 +02:00

3730 lines
107 KiB
Go

/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package resources
import (
"bytes"
"errors"
"fmt"
"reflect"
"strings"
"testing"
"time"
"github.com/cgrates/birpc"
"github.com/cgrates/birpc/context"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/rpcclient"
"github.com/cgrates/cgrates/utils"
)
func TestResourcesRecordUsage(t *testing.T) {
testStruct := &resource{
Resource: &utils.Resource{
Tenant: "test_tenant",
ID: "test_id",
Usages: map[string]*utils.ResourceUsage{
"test_id2": {
Tenant: "test_tenant2",
ID: "test_id2",
ExpiryTime: time.Date(2015, 1, 14, 0, 0, 0, 0, time.UTC),
Units: 1,
},
},
},
}
recordStruct := &utils.ResourceUsage{
Tenant: "test_tenant3",
ID: "test_id3",
ExpiryTime: time.Date(2016, 1, 14, 0, 0, 0, 0, time.UTC),
Units: 1,
}
expStruct := resource{
Resource: &utils.Resource{
Tenant: "test_tenant",
ID: "test_id",
Usages: map[string]*utils.ResourceUsage{
"test_id2": {
Tenant: "test_tenant2",
ID: "test_id2",
ExpiryTime: time.Date(2015, 1, 14, 0, 0, 0, 0, time.UTC),
Units: 1,
},
"test_id3": {
Tenant: "test_tenant3",
ID: "test_id3",
ExpiryTime: time.Date(2016, 1, 14, 0, 0, 0, 0, time.UTC),
Units: 1,
},
},
},
}
err := testStruct.recordUsage(recordStruct)
if err != nil {
t.Errorf("\nExpecting <nil>,\n Received <%+v>", err)
}
if reflect.DeepEqual(testStruct, expStruct) {
t.Errorf("\nExpecting <%+v>,\n Received <%+v>", expStruct, testStruct)
}
}
func TestResourcesClearUsage(t *testing.T) {
testStruct := &resource{
Resource: &utils.Resource{
Tenant: "test_tenant",
ID: "test_id",
Usages: map[string]*utils.ResourceUsage{
"test_id2": {
Tenant: "test_tenant2",
ID: "test_id2",
ExpiryTime: time.Date(2015, 1, 14, 0, 0, 0, 0, time.UTC),
Units: 1,
},
"test_id3": {
Tenant: "test_tenant3",
ID: "test_id3",
ExpiryTime: time.Date(2016, 1, 14, 0, 0, 0, 0, time.UTC),
Units: 1,
},
},
},
}
expStruct := resource{
Resource: &utils.Resource{
Tenant: "test_tenant",
ID: "test_id",
Usages: map[string]*utils.ResourceUsage{
"test_id2": {
Tenant: "test_tenant2",
ID: "test_id2",
ExpiryTime: time.Date(2015, 1, 14, 0, 0, 0, 0, time.UTC),
Units: 1,
},
},
},
}
err := testStruct.clearUsage("test_id3")
if err != nil {
t.Errorf("\nExpecting <nil>,\n Received <%+v>", err)
}
if reflect.DeepEqual(testStruct, expStruct) {
t.Errorf("\nExpecting <%+v>,\n Received <%+v>", expStruct, testStruct)
}
}
func TestResourceRecordUsage(t *testing.T) {
var r1 *resource
var ru1 *utils.ResourceUsage
var ru2 *utils.ResourceUsage
ru1 = &utils.ResourceUsage{
Tenant: "cgrates.org",
ID: "RU1",
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
Units: 1,
}
ru2 = &utils.ResourceUsage{
Tenant: "cgrates.org",
ID: "RU2",
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
Units: 2,
}
r1 = &resource{
Resource: &utils.Resource{
Tenant: "cgrates.org",
ID: "RL1",
Usages: map[string]*utils.ResourceUsage{
ru1.ID: ru1,
},
TTLIdx: []string{ru1.ID},
},
rPrf: &resourceProfile{
ResourceProfile: &utils.ResourceProfile{
Tenant: "cgrates.org",
ID: "RL1",
FilterIDs: []string{"FLTR_RES_RL1", "*ai:~*req.AnswerTime:2014-07-03T13:43:00Z|2014-07-03T13:44:00Z"},
Weights: utils.DynamicWeights{
{
Weight: 100,
}},
Limit: 2,
ThresholdIDs: []string{"TEST_ACTIONS"},
UsageTTL: time.Millisecond,
AllocationMessage: "ALLOC",
},
},
tUsage: utils.Float64Pointer(2),
}
if err := r1.recordUsage(ru2); err != nil {
t.Error(err.Error())
} else {
if err := r1.recordUsage(ru1); err == nil {
t.Error("duplicate ResourceUsage id should not be allowed")
}
if _, found := r1.Resource.Usages[ru2.ID]; !found {
t.Error("ResourceUsage was not recorded")
}
if *r1.tUsage != 4 {
t.Errorf("expecting: %+v, received: %+v", 4, r1.tUsage)
}
}
}
func TestResourceRemoveExpiredUnits(t *testing.T) {
var r1 *resource
var ru1 *utils.ResourceUsage
var ru2 *utils.ResourceUsage
ru1 = &utils.ResourceUsage{
Tenant: "cgrates.org",
ID: "RU1",
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
Units: 1,
}
ru2 = &utils.ResourceUsage{
Tenant: "cgrates.org",
ID: "RU2",
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
Units: 2,
}
r1 = &resource{
Resource: &utils.Resource{
Tenant: "cgrates.org",
ID: "RL1",
Usages: map[string]*utils.ResourceUsage{
ru1.ID: ru1,
},
TTLIdx: []string{ru1.ID},
},
rPrf: &resourceProfile{
ResourceProfile: &utils.ResourceProfile{
Tenant: "cgrates.org",
ID: "RL1",
FilterIDs: []string{"FLTR_RES_RL1", "*ai:~*req.AnswerTime:2014-07-03T13:43:00Z|2014-07-03T13:44:00Z"},
Weights: utils.DynamicWeights{
{
Weight: 100,
}},
Limit: 2,
ThresholdIDs: []string{"TEST_ACTIONS"},
UsageTTL: time.Millisecond,
AllocationMessage: "ALLOC",
},
},
tUsage: utils.Float64Pointer(2),
}
if err := r1.recordUsage(ru2); err != nil {
t.Error(err.Error())
} else {
if err := r1.recordUsage(ru1); err == nil {
t.Error("duplicate ResourceUsage id should not be allowed")
}
if _, found := r1.Resource.Usages[ru2.ID]; !found {
t.Error("ResourceUsage was not recorded")
}
if *r1.tUsage != 4 {
t.Errorf("expecting: %+v, received: %+v", 4, r1.tUsage)
}
}
r1.Resource.Usages = map[string]*utils.ResourceUsage{
ru1.ID: ru1,
}
*r1.tUsage = 2
r1.removeExpiredUnits()
if len(r1.Resource.Usages) != 0 {
t.Errorf("Expecting: %+v, received: %+v", 0, len(r1.Resource.Usages))
}
if len(r1.Resource.TTLIdx) != 0 {
t.Errorf("Expecting: %+v, received: %+v", 0, len(r1.Resource.TTLIdx))
}
if r1.tUsage != nil && *r1.tUsage != 0 {
t.Errorf("Expecting: %+v, received: %+v", 0, r1.tUsage)
}
}
func TestResourceUsedUnits(t *testing.T) {
ru1 := &utils.ResourceUsage{
Tenant: "cgrates.org",
ID: "RU1",
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
Units: 1,
}
ru2 := &utils.ResourceUsage{
Tenant: "cgrates.org",
ID: "RU2",
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
Units: 2,
}
r1 := &resource{
Resource: &utils.Resource{
Tenant: "cgrates.org",
ID: "RL1",
Usages: map[string]*utils.ResourceUsage{
ru1.ID: ru1,
},
TTLIdx: []string{ru1.ID},
},
rPrf: &resourceProfile{
ResourceProfile: &utils.ResourceProfile{
Tenant: "cgrates.org",
ID: "RL1",
FilterIDs: []string{"FLTR_RES_RL1", "*ai:~*req.AnswerTime:2014-07-03T13:43:00Z|2014-07-03T13:44:00Z"},
Weights: utils.DynamicWeights{
{
Weight: 100,
}},
Limit: 2,
ThresholdIDs: []string{"TEST_ACTIONS"},
UsageTTL: time.Millisecond,
AllocationMessage: "ALLOC",
},
},
tUsage: utils.Float64Pointer(2),
}
if err := r1.recordUsage(ru2); err != nil {
t.Error(err.Error())
} else {
if err := r1.recordUsage(ru1); err == nil {
t.Error("duplicate ResourceUsage id should not be allowed")
}
if _, found := r1.Resource.Usages[ru2.ID]; !found {
t.Error("ResourceUsage was not recorded")
}
if *r1.tUsage != 4 {
t.Errorf("expecting: %+v, received: %+v", 4, r1.tUsage)
}
}
r1.Resource.Usages = map[string]*utils.ResourceUsage{
ru1.ID: ru1,
}
r1.tUsage = nil
if usedUnits := r1.Resource.TotalUsage(); usedUnits != 1 {
t.Errorf("Expecting: %+v, received: %+v", 1, usedUnits)
}
}
func TestResourceClearUsage(t *testing.T) {
ru1 := &utils.ResourceUsage{
Tenant: "cgrates.org",
ID: "RU1",
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
Units: 1,
}
ru2 := &utils.ResourceUsage{
Tenant: "cgrates.org",
ID: "RU2",
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
Units: 2,
}
r1 := &resource{
Resource: &utils.Resource{
Tenant: "cgrates.org",
ID: "RL1",
Usages: map[string]*utils.ResourceUsage{
ru1.ID: ru1,
},
TTLIdx: []string{ru1.ID},
},
rPrf: &resourceProfile{
ResourceProfile: &utils.ResourceProfile{
Tenant: "cgrates.org",
ID: "RL1",
FilterIDs: []string{"FLTR_RES_RL1", "*ai:~*req.AnswerTime:2014-07-03T13:43:00Z|2014-07-03T13:44:00Z"},
Weights: utils.DynamicWeights{
{
Weight: 100,
}},
Limit: 2,
ThresholdIDs: []string{"TEST_ACTIONS"},
UsageTTL: time.Millisecond,
AllocationMessage: "ALLOC",
},
},
tUsage: utils.Float64Pointer(2),
}
if err := r1.recordUsage(ru2); err != nil {
t.Error(err.Error())
} else {
if err := r1.recordUsage(ru1); err == nil {
t.Error("duplicate ResourceUsage id should not be allowed")
}
if _, found := r1.Resource.Usages[ru2.ID]; !found {
t.Error("ResourceUsage was not recorded")
}
if *r1.tUsage != 4 {
t.Errorf("expecting: %+v, received: %+v", 4, r1.tUsage)
}
}
r2 := &resource{
Resource: &utils.Resource{
Tenant: "cgrates.org",
ID: "RL2",
// AllocationMessage: "ALLOC2",
Usages: map[string]*utils.ResourceUsage{
ru2.ID: ru2,
},
},
rPrf: &resourceProfile{
ResourceProfile: &utils.ResourceProfile{
ID: "RL2",
FilterIDs: []string{"FLTR_RES_RL2", "*ai:~*req.AnswerTime:2014-07-03T13:43:00Z|2014-07-03T13:44:00Z"},
Weights: utils.DynamicWeights{
{
Weight: 50,
}},
Limit: 2,
ThresholdIDs: []string{"TEST_ACTIONS"},
UsageTTL: time.Millisecond,
},
},
tUsage: utils.Float64Pointer(2),
}
r1.Resource.Usages = map[string]*utils.ResourceUsage{
ru1.ID: ru1,
}
r1.tUsage = nil
r1.clearUsage(ru1.ID)
if len(r1.Resource.Usages) != 0 {
t.Errorf("Expecting: %+v, received: %+v", 0, len(r1.Resource.Usages))
}
if r1.Resource.TotalUsage() != 0 {
t.Errorf("Expecting: %+v, received: %+v", 0, r1.tUsage)
}
if err := r2.clearUsage(ru2.ID); err != nil {
t.Error(err)
} else if len(r2.Resource.Usages) != 0 {
t.Errorf("Unexpected usages %+v", r2.Resource.Usages)
} else if *r2.tUsage != 0 {
t.Errorf("Unexpected tUsage %+v", r2.tUsage)
}
}
func TestResourceRecordUsages(t *testing.T) {
ru1 := &utils.ResourceUsage{
Tenant: "cgrates.org",
ID: "RU1",
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
Units: 1,
}
ru2 := &utils.ResourceUsage{
Tenant: "cgrates.org",
ID: "RU2",
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
Units: 2,
}
r1 := &resource{
Resource: &utils.Resource{
Tenant: "cgrates.org",
ID: "RL1",
Usages: map[string]*utils.ResourceUsage{
ru1.ID: ru1,
},
TTLIdx: []string{ru1.ID},
},
rPrf: &resourceProfile{
ResourceProfile: &utils.ResourceProfile{
Tenant: "cgrates.org",
ID: "RL1",
FilterIDs: []string{"FLTR_RES_RL1", "*ai:~*req.AnswerTime:2014-07-03T13:43:00Z|2014-07-03T13:44:00Z"},
Weights: utils.DynamicWeights{
{
Weight: 100,
}},
Limit: 2,
ThresholdIDs: []string{"TEST_ACTIONS"},
UsageTTL: time.Millisecond,
AllocationMessage: "ALLOC",
},
},
tUsage: utils.Float64Pointer(2),
}
if err := r1.recordUsage(ru2); err != nil {
t.Error(err.Error())
} else {
if err := r1.recordUsage(ru1); err == nil {
t.Error("duplicate ResourceUsage id should not be allowed")
}
if _, found := r1.Resource.Usages[ru2.ID]; !found {
t.Error("ResourceUsage was not recorded")
}
if *r1.tUsage != 4 {
t.Errorf("expecting: %+v, received: %+v", 4, r1.tUsage)
}
}
r2 := &resource{
Resource: &utils.Resource{
Tenant: "cgrates.org",
ID: "RL2",
// AllocationMessage: "ALLOC2",
Usages: map[string]*utils.ResourceUsage{
ru2.ID: ru2,
},
},
rPrf: &resourceProfile{
ResourceProfile: &utils.ResourceProfile{
ID: "RL2",
FilterIDs: []string{"FLTR_RES_RL2", "*ai:~*req.AnswerTime:2014-07-03T13:43:00Z|2014-07-03T13:44:00Z"},
Weights: utils.DynamicWeights{
{
Weight: 50,
}},
Limit: 2,
ThresholdIDs: []string{"TEST_ACTIONS"},
UsageTTL: time.Millisecond,
},
},
tUsage: utils.Float64Pointer(2),
}
rs := Resources{r2, r1}
r1.Resource.Usages = map[string]*utils.ResourceUsage{
ru1.ID: ru1,
}
r1.Resource.Usages = map[string]*utils.ResourceUsage{
ru1.ID: ru1,
}
r1.tUsage = nil
if err := rs.recordUsage(ru1); err == nil {
t.Error("should get duplicated error")
}
}
func TestResourceAllocateResource(t *testing.T) {
ru1 := &utils.ResourceUsage{
Tenant: "cgrates.org",
ID: "RU1",
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
Units: 1,
}
ru2 := &utils.ResourceUsage{
Tenant: "cgrates.org",
ID: "RU2",
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
Units: 2,
}
r1 := &resource{
Resource: &utils.Resource{
Tenant: "cgrates.org",
ID: "RL1",
Usages: map[string]*utils.ResourceUsage{
ru1.ID: ru1,
},
TTLIdx: []string{ru1.ID},
},
rPrf: &resourceProfile{
ResourceProfile: &utils.ResourceProfile{
Tenant: "cgrates.org",
ID: "RL1",
FilterIDs: []string{"FLTR_RES_RL1", "*ai:~*req.AnswerTime:2014-07-03T13:43:00Z|2014-07-03T13:44:00Z"},
Weights: utils.DynamicWeights{
{
Weight: 100,
}},
Limit: 2,
ThresholdIDs: []string{"TEST_ACTIONS"},
UsageTTL: time.Millisecond,
AllocationMessage: "ALLOC",
},
},
tUsage: utils.Float64Pointer(2),
}
if err := r1.recordUsage(ru2); err != nil {
t.Error(err.Error())
} else {
if err := r1.recordUsage(ru1); err == nil {
t.Error("duplicate ResourceUsage id should not be allowed")
}
if _, found := r1.Resource.Usages[ru2.ID]; !found {
t.Error("ResourceUsage was not recorded")
}
if *r1.tUsage != 4 {
t.Errorf("expecting: %+v, received: %+v", 4, r1.tUsage)
}
}
r2 := &resource{
Resource: &utils.Resource{
Tenant: "cgrates.org",
ID: "RL2",
// AllocationMessage: "ALLOC2",
Usages: map[string]*utils.ResourceUsage{
ru2.ID: ru2,
},
},
rPrf: &resourceProfile{
ResourceProfile: &utils.ResourceProfile{
ID: "RL2",
FilterIDs: []string{"FLTR_RES_RL2", "*ai:~*req.AnswerTime:2014-07-03T13:43:00Z|2014-07-03T13:44:00Z"},
Weights: utils.DynamicWeights{
{
Weight: 50,
}},
Limit: 2,
ThresholdIDs: []string{"TEST_ACTIONS"},
UsageTTL: time.Millisecond,
},
},
tUsage: utils.Float64Pointer(2),
}
rs := Resources{r1, r2}
r1.Resource.Usages = map[string]*utils.ResourceUsage{
ru1.ID: ru1,
}
r1.Resource.Usages = map[string]*utils.ResourceUsage{
ru1.ID: ru1,
}
r1.tUsage = nil
if err := rs.recordUsage(ru1); err == nil {
t.Error("should get duplicated error")
}
rs.clearUsage(ru1.ID)
rs.clearUsage(ru2.ID)
ru1.ExpiryTime = time.Now().Add(time.Second)
ru2.ExpiryTime = time.Now().Add(time.Second)
if alcMessage, err := rs.allocateResource(ru1, false); err != nil {
t.Error(err.Error())
} else {
if alcMessage != "ALLOC" {
t.Errorf("Wrong allocation message: %v", alcMessage)
}
}
if _, err := rs.allocateResource(ru2, false); err != utils.ErrResourceUnavailable {
t.Error("Did not receive " + utils.ErrResourceUnavailable.Error() + " error")
}
rs[0].rPrf.ResourceProfile.Limit = 1
rs[1].rPrf.ResourceProfile.Limit = 4
if alcMessage, err := rs.allocateResource(ru1, false); err != nil {
t.Error(err.Error())
} else {
if alcMessage != "ALLOC" {
t.Errorf("Wrong allocation message: %v", alcMessage)
}
}
if alcMessage, err := rs.allocateResource(ru2, false); err != nil {
t.Error(err.Error())
} else {
if alcMessage != "RL2" {
t.Errorf("Wrong allocation message: %v", alcMessage)
}
}
ru2.Units = 0
if _, err := rs.allocateResource(ru2, false); err != nil {
t.Error(err)
}
}
// TestRSCacheSetGet assurace the presence of private params in cached resource
func TestRSCacheSetGet(t *testing.T) {
r := &utils.Resource{
Tenant: "cgrates.org",
ID: "RL",
Usages: map[string]*utils.ResourceUsage{
"RU2": {
Tenant: "cgrates.org",
ID: "RU2",
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
Units: 2,
},
},
}
if err := engine.Cache.Set(context.TODO(), utils.CacheResources, r.TenantID(), r, nil, true, ""); err != nil {
t.Errorf("Expecting: nil, received: %s", err)
}
if x, ok := engine.Cache.Get(utils.CacheResources, r.TenantID()); !ok {
t.Error("not in cache")
} else if x == nil {
t.Error("nil resource")
} else if !reflect.DeepEqual(r, x.(*utils.Resource)) {
t.Errorf("Expecting: %+v, received: %+v", r, x)
}
}
func TestResourceAddResourceProfile(t *testing.T) {
var dmRES *engine.DataManager
cfg := config.NewDefaultCGRConfig()
data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items)
dmRES = engine.NewDataManager(data, cfg, nil)
cfg.ResourceSCfg().StoreInterval = 1
cfg.ResourceSCfg().StringIndexedFields = nil
cfg.ResourceSCfg().PrefixIndexedFields = nil
fltrRes1 := &engine.Filter{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "FLTR_RES_1",
Rules: []*engine.FilterRule{
{
Type: utils.MetaString,
Element: "~*req.Resources",
Values: []string{"ResourceProfile1"},
},
{
Type: utils.MetaGreaterOrEqual,
Element: "~*req.UsageInterval",
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Usage,
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Weight,
Values: []string{"9.0"},
},
},
}
dmRES.SetFilter(context.Background(), fltrRes1, true)
fltrRes2 := &engine.Filter{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "FLTR_RES_2",
Rules: []*engine.FilterRule{
{
Type: utils.MetaString,
Element: "~*req.Resources",
Values: []string{"ResourceProfile2"},
},
{
Type: utils.MetaGreaterOrEqual,
Element: "~*req.PddInterval",
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Usage,
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Weight,
Values: []string{"15.0"},
},
},
}
dmRES.SetFilter(context.Background(), fltrRes2, true)
fltrRes3 := &engine.Filter{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "FLTR_RES_3",
Rules: []*engine.FilterRule{
{
Type: utils.MetaPrefix,
Element: "~*req.Resources",
Values: []string{"ResourceProfilePrefix"},
},
},
}
dmRES.SetFilter(context.Background(), fltrRes3, true)
resprf := []*utils.ResourceProfile{
{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile1",
FilterIDs: []string{"FLTR_RES_1", "*ai:~*req.AnswerTime:2014-07-14T14:25:00Z"},
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{""},
},
{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile2", // identifier of this resource
FilterIDs: []string{"FLTR_RES_2", "*ai:~*req.AnswerTime:2014-07-14T14:25:00Z"},
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{""},
},
{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile3",
FilterIDs: []string{"FLTR_RES_3", "*ai:~*req.AnswerTime:2014-07-14T14:25:00Z"},
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{""},
},
}
resourceTest := []*utils.Resource{
{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile1",
Usages: map[string]*utils.ResourceUsage{},
TTLIdx: []string{},
},
{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile2",
Usages: map[string]*utils.ResourceUsage{},
TTLIdx: []string{},
},
{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile3",
Usages: map[string]*utils.ResourceUsage{},
TTLIdx: []string{},
},
}
for _, resProfile := range resprf {
dmRES.SetResourceProfile(context.TODO(), resProfile, true)
}
for _, res := range resourceTest {
dmRES.SetResource(context.TODO(), res)
}
//Test each resourceProfile from cache
for _, resPrf := range resprf {
if tempRes, err := dmRES.GetResourceProfile(context.TODO(), resPrf.Tenant,
resPrf.ID, true, false, utils.NonTransactional); err != nil {
t.Errorf("Error: %+v", err)
} else if !reflect.DeepEqual(resPrf, tempRes) {
t.Errorf("Expecting: %+v, received: %+v", resPrf, tempRes)
}
}
}
func TestResourceMatchingResourcesForEvent(t *testing.T) {
var dmRES *engine.DataManager
cfg := config.NewDefaultCGRConfig()
data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items)
dmRES = engine.NewDataManager(data, cfg, nil)
cfg.ResourceSCfg().StoreInterval = 1
cfg.ResourceSCfg().StringIndexedFields = nil
cfg.ResourceSCfg().PrefixIndexedFields = nil
fltrs := engine.NewFilterS(cfg, nil, dmRES)
resService := NewResourceService(dmRES, cfg,
fltrs, nil)
fltrRes1 := &engine.Filter{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "FLTR_RES_1",
Rules: []*engine.FilterRule{
{
Type: utils.MetaString,
Element: "~*req.Resources",
Values: []string{"ResourceProfile1"},
},
{
Type: utils.MetaGreaterOrEqual,
Element: "~*req.UsageInterval",
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Usage,
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Weight,
Values: []string{"9.0"},
},
},
}
dmRES.SetFilter(context.Background(), fltrRes1, true)
fltrRes2 := &engine.Filter{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "FLTR_RES_2",
Rules: []*engine.FilterRule{
{
Type: utils.MetaString,
Element: "~*req.Resources",
Values: []string{"ResourceProfile2"},
},
{
Type: utils.MetaGreaterOrEqual,
Element: "~*req.PddInterval",
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Usage,
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Weight,
Values: []string{"15.0"},
},
},
}
dmRES.SetFilter(context.Background(), fltrRes2, true)
fltrRes3 := &engine.Filter{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "FLTR_RES_3",
Rules: []*engine.FilterRule{
{
Type: utils.MetaPrefix,
Element: "~*req.Resources",
Values: []string{"ResourceProfilePrefix"},
},
},
}
dmRES.SetFilter(context.Background(), fltrRes3, true)
resprf := []*resourceProfile{
{
ResourceProfile: &utils.ResourceProfile{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile1",
FilterIDs: []string{"FLTR_RES_1", "*ai:*now:2014-07-14T14:25:00Z"},
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{""},
},
},
{
ResourceProfile: &utils.ResourceProfile{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile2", // identifier of this resource
FilterIDs: []string{"FLTR_RES_2", "*ai:*now:2014-07-14T14:25:00Z"},
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{""},
},
},
{
ResourceProfile: &utils.ResourceProfile{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile3",
FilterIDs: []string{"FLTR_RES_3", "*ai:*now:2014-07-14T14:25:00Z"},
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{""},
},
},
}
resourceTest := Resources{
{
Resource: &utils.Resource{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile1",
Usages: map[string]*utils.ResourceUsage{},
TTLIdx: []string{},
},
rPrf: resprf[0],
},
{
Resource: &utils.Resource{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile2",
Usages: map[string]*utils.ResourceUsage{},
TTLIdx: []string{},
},
rPrf: resprf[1],
},
{
Resource: &utils.Resource{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile3",
Usages: map[string]*utils.ResourceUsage{},
TTLIdx: []string{},
},
rPrf: resprf[2],
},
}
resEvs := []*utils.CGREvent{
{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "event1",
Event: map[string]any{
"Resources": "ResourceProfile1",
utils.AnswerTime: time.Date(2014, 7, 14, 14, 30, 0, 0, time.UTC),
"UsageInterval": "1s",
"PddInterval": "1s",
utils.Weight: "20.0",
utils.Usage: 135 * time.Second,
utils.Cost: 123.0,
},
},
{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "event2",
Event: map[string]any{
"Resources": "ResourceProfile2",
utils.AnswerTime: time.Date(2014, 7, 14, 14, 30, 0, 0, time.UTC),
"UsageInterval": "1s",
"PddInterval": "1s",
utils.Weight: "15.0",
utils.Usage: 45 * time.Second,
},
},
{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "event3",
Event: map[string]any{
"Resources": "ResourceProfilePrefix",
utils.Usage: 30 * time.Second,
},
},
}
timeDurationExample := 10 * time.Second
for _, resProfile := range resprf {
dmRES.SetResourceProfile(context.TODO(), resProfile.ResourceProfile, true)
}
for _, res := range resourceTest {
dmRES.SetResource(context.TODO(), res.Resource)
}
mres, err := resService.matchingResourcesForEvent(context.TODO(), resEvs[0].Tenant, resEvs[0],
"TestResourceMatchingResourcesForEvent1", &timeDurationExample)
if err != nil {
t.Errorf("Error: %+v", err)
}
mres.unlock()
if !reflect.DeepEqual(resourceTest[0].Resource.Tenant, mres[0].Resource.Tenant) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[0].Resource.Tenant, mres[0].Resource.Tenant)
} else if !reflect.DeepEqual(resourceTest[0].Resource.ID, mres[0].Resource.ID) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[0].Resource.ID, mres[0].Resource.ID)
} else if !reflect.DeepEqual(resourceTest[0].rPrf, mres[0].rPrf) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[0].rPrf, mres[0].rPrf)
}
mres, err = resService.matchingResourcesForEvent(context.TODO(), resEvs[1].Tenant, resEvs[1],
"TestResourceMatchingResourcesForEvent2", &timeDurationExample)
if err != nil {
t.Errorf("Error: %+v", err)
}
mres.unlock()
if !reflect.DeepEqual(resourceTest[1].Resource.Tenant, mres[0].Resource.Tenant) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[1].Resource.Tenant, mres[0].Resource.Tenant)
} else if !reflect.DeepEqual(resourceTest[1].Resource.ID, mres[0].Resource.ID) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[1].Resource.ID, mres[0].Resource.ID)
} else if !reflect.DeepEqual(resourceTest[1].rPrf, mres[0].rPrf) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[1].rPrf, mres[0].rPrf)
}
mres, err = resService.matchingResourcesForEvent(context.TODO(), resEvs[2].Tenant, resEvs[2],
"TestResourceMatchingResourcesForEvent3", &timeDurationExample)
if err != nil {
t.Errorf("Error: %+v", err)
}
mres.unlock()
if !reflect.DeepEqual(resourceTest[2].Resource.Tenant, mres[0].Resource.Tenant) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[2].Resource.Tenant, mres[0].Resource.Tenant)
} else if !reflect.DeepEqual(resourceTest[2].Resource.ID, mres[0].Resource.ID) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[2].Resource.ID, mres[0].Resource.ID)
} else if !reflect.DeepEqual(resourceTest[2].rPrf, mres[0].rPrf) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[2].rPrf, mres[0].rPrf)
}
}
// UsageTTL 0 in ResourceProfile and give 10s duration
func TestResourceUsageTTLCase1(t *testing.T) {
resprf := []*resourceProfile{
{
ResourceProfile: &utils.ResourceProfile{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile1",
FilterIDs: []string{"FLTR_RES_1", "*ai:~*req.AnswerTime:2014-07-14T14:25:00Z"},
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{""},
},
},
{
ResourceProfile: &utils.ResourceProfile{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile2", // identifier of this resource
FilterIDs: []string{"FLTR_RES_2", "*ai:~*req.AnswerTime:2014-07-14T14:25:00Z"},
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{""},
},
},
{
ResourceProfile: &utils.ResourceProfile{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile3",
FilterIDs: []string{"FLTR_RES_3", "*ai:~*req.AnswerTime:2014-07-14T14:25:00Z"},
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{""},
},
},
}
resourceTest := Resources{
{
Resource: &utils.Resource{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile1",
Usages: map[string]*utils.ResourceUsage{},
TTLIdx: []string{},
},
rPrf: resprf[0],
},
{
Resource: &utils.Resource{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile2",
Usages: map[string]*utils.ResourceUsage{},
TTLIdx: []string{},
},
rPrf: resprf[1],
},
{
Resource: &utils.Resource{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile3",
Usages: map[string]*utils.ResourceUsage{},
TTLIdx: []string{},
},
rPrf: resprf[2],
},
}
resEvs := []*utils.CGREvent{
{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "event1",
Event: map[string]any{
"Resources": "ResourceProfile1",
utils.AnswerTime: time.Date(2014, 7, 14, 14, 30, 0, 0, time.UTC),
"UsageInterval": "1s",
"PddInterval": "1s",
utils.Weight: "20.0",
utils.Usage: 135 * time.Second,
utils.Cost: 123.0,
},
},
{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "event2",
Event: map[string]any{
"Resources": "ResourceProfile2",
utils.AnswerTime: time.Date(2014, 7, 14, 14, 30, 0, 0, time.UTC),
"UsageInterval": "1s",
"PddInterval": "1s",
utils.Weight: "15.0",
utils.Usage: 45 * time.Second,
},
},
{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "event3",
Event: map[string]any{
"Resources": "ResourceProfilePrefix",
utils.Usage: 30 * time.Second,
},
},
}
timeDurationExample := 10 * time.Second
var dmRES *engine.DataManager
cfg := config.NewDefaultCGRConfig()
data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items)
dmRES = engine.NewDataManager(data, cfg, nil)
cfg.ResourceSCfg().StoreInterval = 1
cfg.ResourceSCfg().StringIndexedFields = nil
cfg.ResourceSCfg().PrefixIndexedFields = nil
engine.Cache.Clear(nil)
fltrs := engine.NewFilterS(cfg, nil, dmRES)
resService := NewResourceService(dmRES, cfg,
fltrs, nil)
fltrRes1 := &engine.Filter{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "FLTR_RES_1",
Rules: []*engine.FilterRule{
{
Type: utils.MetaString,
Element: "~*req.Resources",
Values: []string{"ResourceProfile1"},
},
{
Type: utils.MetaGreaterOrEqual,
Element: "~*req.UsageInterval",
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Usage,
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Weight,
Values: []string{"9.0"},
},
},
}
dmRES.SetFilter(context.Background(), fltrRes1, true)
fltrRes2 := &engine.Filter{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "FLTR_RES_2",
Rules: []*engine.FilterRule{
{
Type: utils.MetaString,
Element: "~*req.Resources",
Values: []string{"ResourceProfile2"},
},
{
Type: utils.MetaGreaterOrEqual,
Element: "~*req.PddInterval",
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Usage,
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Weight,
Values: []string{"15.0"},
},
},
}
dmRES.SetFilter(context.Background(), fltrRes2, true)
fltrRes3 := &engine.Filter{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "FLTR_RES_3",
Rules: []*engine.FilterRule{
{
Type: utils.MetaPrefix,
Element: "~*req.Resources",
Values: []string{"ResourceProfilePrefix"},
},
},
}
dmRES.SetFilter(context.Background(), fltrRes3, true)
resprf[0].ResourceProfile.UsageTTL = 0
resourceTest[0].rPrf = resprf[0]
resourceTest[0].ttl = &timeDurationExample
if err := dmRES.SetResourceProfile(context.TODO(), resprf[0].ResourceProfile, true); err != nil {
t.Error(err)
}
if err := dmRES.SetResource(context.TODO(), resourceTest[0].Resource); err != nil {
t.Error(err)
}
mres, err := resService.matchingResourcesForEvent(context.TODO(), resEvs[0].Tenant, resEvs[0],
"TestResourceUsageTTLCase1", &timeDurationExample)
if err != nil {
t.Errorf("Error: %+v", err)
}
mres.unlock()
if !reflect.DeepEqual(resourceTest[0].Resource.Tenant, mres[0].Resource.Tenant) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[0].Resource.Tenant, mres[0].Resource.Tenant)
} else if !reflect.DeepEqual(resourceTest[0].Resource.ID, mres[0].Resource.ID) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[0].Resource.ID, mres[0].Resource.ID)
} else if !reflect.DeepEqual(resourceTest[0].rPrf, mres[0].rPrf) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[0].rPrf, mres[0].rPrf)
} else if !reflect.DeepEqual(resourceTest[0].ttl, mres[0].ttl) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[0].ttl, mres[0].ttl)
}
}
// UsageTTL 5s in ResourceProfile and give nil duration
func TestResourceUsageTTLCase2(t *testing.T) {
resprf := []*resourceProfile{
{
ResourceProfile: &utils.ResourceProfile{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile1",
FilterIDs: []string{"FLTR_RES_1", "*ai:~*req.AnswerTime:2014-07-14T14:25:00Z"},
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{""},
},
},
{
ResourceProfile: &utils.ResourceProfile{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile2", // identifier of this resource
FilterIDs: []string{"FLTR_RES_2", "*ai:~*req.AnswerTime:2014-07-14T14:25:00Z"},
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{""},
},
},
{
ResourceProfile: &utils.ResourceProfile{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile3",
FilterIDs: []string{"FLTR_RES_3", "*ai:~*req.AnswerTime:2014-07-14T14:25:00Z"},
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{""},
},
},
}
resourceTest := Resources{
{
Resource: &utils.Resource{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile1",
Usages: map[string]*utils.ResourceUsage{},
TTLIdx: []string{},
},
rPrf: resprf[0],
},
{
Resource: &utils.Resource{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile2",
Usages: map[string]*utils.ResourceUsage{},
TTLIdx: []string{},
},
rPrf: resprf[1],
},
{
Resource: &utils.Resource{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile3",
Usages: map[string]*utils.ResourceUsage{},
TTLIdx: []string{},
},
rPrf: resprf[2],
},
}
resEvs := []*utils.CGREvent{
{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "event1",
Event: map[string]any{
"Resources": "ResourceProfile1",
utils.AnswerTime: time.Date(2014, 7, 14, 14, 30, 0, 0, time.UTC),
"UsageInterval": "1s",
"PddInterval": "1s",
utils.Weight: "20.0",
utils.Usage: 135 * time.Second,
utils.Cost: 123.0,
},
},
{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "event2",
Event: map[string]any{
"Resources": "ResourceProfile2",
utils.AnswerTime: time.Date(2014, 7, 14, 14, 30, 0, 0, time.UTC),
"UsageInterval": "1s",
"PddInterval": "1s",
utils.Weight: "15.0",
utils.Usage: 45 * time.Second,
},
},
{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "event3",
Event: map[string]any{
"Resources": "ResourceProfilePrefix",
utils.Usage: 30 * time.Second,
},
},
}
var dmRES *engine.DataManager
cfg := config.NewDefaultCGRConfig()
data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items)
dmRES = engine.NewDataManager(data, cfg, nil)
cfg.ResourceSCfg().StoreInterval = 1
cfg.ResourceSCfg().StringIndexedFields = nil
cfg.ResourceSCfg().PrefixIndexedFields = nil
fltrs := engine.NewFilterS(cfg, nil, dmRES)
resService := NewResourceService(dmRES, cfg,
fltrs, nil)
fltrRes1 := &engine.Filter{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "FLTR_RES_1",
Rules: []*engine.FilterRule{
{
Type: utils.MetaString,
Element: "~*req.Resources",
Values: []string{"ResourceProfile1"},
},
{
Type: utils.MetaGreaterOrEqual,
Element: "~*req.UsageInterval",
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Usage,
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Weight,
Values: []string{"9.0"},
},
},
}
dmRES.SetFilter(context.Background(), fltrRes1, true)
fltrRes2 := &engine.Filter{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "FLTR_RES_2",
Rules: []*engine.FilterRule{
{
Type: utils.MetaString,
Element: "~*req.Resources",
Values: []string{"ResourceProfile2"},
},
{
Type: utils.MetaGreaterOrEqual,
Element: "~*req.PddInterval",
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Usage,
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Weight,
Values: []string{"15.0"},
},
},
}
dmRES.SetFilter(context.Background(), fltrRes2, true)
fltrRes3 := &engine.Filter{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "FLTR_RES_3",
Rules: []*engine.FilterRule{
{
Type: utils.MetaPrefix,
Element: "~*req.Resources",
Values: []string{"ResourceProfilePrefix"},
},
},
}
dmRES.SetFilter(context.Background(), fltrRes3, true)
resprf[0].ResourceProfile.UsageTTL = 0
resourceTest[0].rPrf = resprf[0]
resourceTest[0].ttl = &resprf[0].ResourceProfile.UsageTTL
if err := dmRES.SetResourceProfile(context.TODO(), resprf[0].ResourceProfile, true); err != nil {
t.Error(err)
}
if err := dmRES.SetResource(context.TODO(), resourceTest[0].Resource); err != nil {
t.Error(err)
}
mres, err := resService.matchingResourcesForEvent(context.TODO(), resEvs[0].Tenant, resEvs[0],
"TestResourceUsageTTLCase2", nil)
if err != nil {
t.Errorf("Error: %+v", err)
}
mres.unlock()
if !reflect.DeepEqual(resourceTest[0].Resource.Tenant, mres[0].Resource.Tenant) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[0].Resource.Tenant, mres[0].Resource.Tenant)
} else if !reflect.DeepEqual(resourceTest[0].Resource.ID, mres[0].Resource.ID) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[0].Resource.ID, mres[0].Resource.ID)
} else if !reflect.DeepEqual(resourceTest[0].rPrf, mres[0].rPrf) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[0].rPrf, mres[0].rPrf)
} else if !reflect.DeepEqual(resourceTest[0].ttl, mres[0].ttl) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[0].ttl, mres[0].ttl)
}
}
// UsageTTL 5s in ResourceProfile and give 0 duration
func TestResourceUsageTTLCase3(t *testing.T) {
resprf := []*resourceProfile{
{
ResourceProfile: &utils.ResourceProfile{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile1",
FilterIDs: []string{"FLTR_RES_1", "*ai:~*req.AnswerTime:2014-07-14T14:25:00Z"},
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{""},
},
},
{
ResourceProfile: &utils.ResourceProfile{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile2", // identifier of this resource
FilterIDs: []string{"FLTR_RES_2", "*ai:~*req.AnswerTime:2014-07-14T14:25:00Z"},
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{""},
},
},
{
ResourceProfile: &utils.ResourceProfile{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile3",
FilterIDs: []string{"FLTR_RES_3", "*ai:~*req.AnswerTime:2014-07-14T14:25:00Z"},
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{""},
},
},
}
resourceTest := Resources{
{
Resource: &utils.Resource{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile1",
Usages: map[string]*utils.ResourceUsage{},
TTLIdx: []string{},
},
rPrf: resprf[0],
},
{
Resource: &utils.Resource{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile2",
Usages: map[string]*utils.ResourceUsage{},
TTLIdx: []string{},
},
rPrf: resprf[1],
},
{
Resource: &utils.Resource{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile3",
Usages: map[string]*utils.ResourceUsage{},
TTLIdx: []string{},
},
rPrf: resprf[2],
},
}
resEvs := []*utils.CGREvent{
{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "event1",
Event: map[string]any{
"Resources": "ResourceProfile1",
utils.AnswerTime: time.Date(2014, 7, 14, 14, 30, 0, 0, time.UTC),
"UsageInterval": "1s",
"PddInterval": "1s",
utils.Weight: "20.0",
utils.Usage: 135 * time.Second,
utils.Cost: 123.0,
},
},
{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "event2",
Event: map[string]any{
"Resources": "ResourceProfile2",
utils.AnswerTime: time.Date(2014, 7, 14, 14, 30, 0, 0, time.UTC),
"UsageInterval": "1s",
"PddInterval": "1s",
utils.Weight: "15.0",
utils.Usage: 45 * time.Second,
},
},
{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "event3",
Event: map[string]any{
"Resources": "ResourceProfilePrefix",
utils.Usage: 30 * time.Second,
},
},
}
var dmRES *engine.DataManager
cfg := config.NewDefaultCGRConfig()
data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items)
dmRES = engine.NewDataManager(data, cfg, nil)
cfg.ResourceSCfg().StoreInterval = 1
cfg.ResourceSCfg().StringIndexedFields = nil
cfg.ResourceSCfg().PrefixIndexedFields = nil
engine.Cache.Clear(nil)
fltrs := engine.NewFilterS(cfg, nil, dmRES)
resService := NewResourceService(dmRES, cfg,
fltrs, nil)
fltrRes1 := &engine.Filter{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "FLTR_RES_1",
Rules: []*engine.FilterRule{
{
Type: utils.MetaString,
Element: "~*req.Resources",
Values: []string{"ResourceProfile1"},
},
{
Type: utils.MetaGreaterOrEqual,
Element: "~*req.UsageInterval",
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Usage,
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Weight,
Values: []string{"9.0"},
},
},
}
dmRES.SetFilter(context.Background(), fltrRes1, true)
fltrRes2 := &engine.Filter{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "FLTR_RES_2",
Rules: []*engine.FilterRule{
{
Type: utils.MetaString,
Element: "~*req.Resources",
Values: []string{"ResourceProfile2"},
},
{
Type: utils.MetaGreaterOrEqual,
Element: "~*req.PddInterval",
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Usage,
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Weight,
Values: []string{"15.0"},
},
},
}
dmRES.SetFilter(context.Background(), fltrRes2, true)
fltrRes3 := &engine.Filter{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "FLTR_RES_3",
Rules: []*engine.FilterRule{
{
Type: utils.MetaPrefix,
Element: "~*req.Resources",
Values: []string{"ResourceProfilePrefix"},
},
},
}
dmRES.SetFilter(context.Background(), fltrRes3, true)
resprf[0].ResourceProfile.UsageTTL = 0
resourceTest[0].rPrf = resprf[0]
resourceTest[0].ttl = nil
if err := dmRES.SetResourceProfile(context.TODO(), resprf[0].ResourceProfile, true); err != nil {
t.Error(err)
}
if err := dmRES.SetResource(context.TODO(), resourceTest[0].Resource); err != nil {
t.Error(err)
}
mres, err := resService.matchingResourcesForEvent(context.TODO(), resEvs[0].Tenant, resEvs[0],
"TestResourceUsageTTLCase3", utils.DurationPointer(0))
if err != nil {
t.Errorf("Error: %+v", err)
}
mres.unlock()
if !reflect.DeepEqual(resourceTest[0].Resource.Tenant, mres[0].Resource.Tenant) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[0].Resource.Tenant, mres[0].Resource.Tenant)
} else if !reflect.DeepEqual(resourceTest[0].Resource.ID, mres[0].Resource.ID) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[0].Resource.ID, mres[0].Resource.ID)
} else if !reflect.DeepEqual(resourceTest[0].rPrf, mres[0].rPrf) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[0].rPrf, mres[0].rPrf)
} else if !reflect.DeepEqual(resourceTest[0].ttl, mres[0].ttl) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[0].ttl, mres[0].ttl)
}
}
// UsageTTL 5s in ResourceProfile and give 10s duration
func TestResourceUsageTTLCase4(t *testing.T) {
resprf := []*resourceProfile{
{
ResourceProfile: &utils.ResourceProfile{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile1",
FilterIDs: []string{"FLTR_RES_1", "*ai:~*req.AnswerTime:2014-07-14T14:25:00Z"},
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{""},
},
},
{
ResourceProfile: &utils.ResourceProfile{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile2", // identifier of this resource
FilterIDs: []string{"FLTR_RES_2", "*ai:~*req.AnswerTime:2014-07-14T14:25:00Z"},
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{""},
},
},
{
ResourceProfile: &utils.ResourceProfile{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile3",
FilterIDs: []string{"FLTR_RES_3", "*ai:~*req.AnswerTime:2014-07-14T14:25:00Z"},
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{""},
},
},
}
resourceTest := Resources{
{
Resource: &utils.Resource{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile1",
Usages: map[string]*utils.ResourceUsage{},
TTLIdx: []string{},
},
rPrf: resprf[0],
},
{
Resource: &utils.Resource{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile2",
Usages: map[string]*utils.ResourceUsage{},
TTLIdx: []string{},
},
rPrf: resprf[1],
},
{
Resource: &utils.Resource{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile3",
Usages: map[string]*utils.ResourceUsage{},
TTLIdx: []string{},
},
rPrf: resprf[2],
},
}
resEvs := []*utils.CGREvent{
{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "event1",
Event: map[string]any{
"Resources": "ResourceProfile1",
utils.AnswerTime: time.Date(2014, 7, 14, 14, 30, 0, 0, time.UTC),
"UsageInterval": "1s",
"PddInterval": "1s",
utils.Weight: "20.0",
utils.Usage: 135 * time.Second,
utils.Cost: 123.0,
},
},
{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "event2",
Event: map[string]any{
"Resources": "ResourceProfile2",
utils.AnswerTime: time.Date(2014, 7, 14, 14, 30, 0, 0, time.UTC),
"UsageInterval": "1s",
"PddInterval": "1s",
utils.Weight: "15.0",
utils.Usage: 45 * time.Second,
},
},
{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "event3",
Event: map[string]any{
"Resources": "ResourceProfilePrefix",
utils.Usage: 30 * time.Second,
},
},
}
var dmRES *engine.DataManager
cfg := config.NewDefaultCGRConfig()
data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items)
dmRES = engine.NewDataManager(data, cfg, nil)
cfg.ResourceSCfg().StoreInterval = 1
cfg.ResourceSCfg().StringIndexedFields = nil
cfg.ResourceSCfg().PrefixIndexedFields = nil
engine.Cache.Clear(nil)
fltrs := engine.NewFilterS(cfg, nil, dmRES)
resService := NewResourceService(dmRES, cfg,
fltrs, nil)
fltrRes1 := &engine.Filter{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "FLTR_RES_1",
Rules: []*engine.FilterRule{
{
Type: utils.MetaString,
Element: "~*req.Resources",
Values: []string{"ResourceProfile1"},
},
{
Type: utils.MetaGreaterOrEqual,
Element: "~*req.UsageInterval",
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Usage,
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Weight,
Values: []string{"9.0"},
},
},
}
dmRES.SetFilter(context.Background(), fltrRes1, true)
fltrRes2 := &engine.Filter{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "FLTR_RES_2",
Rules: []*engine.FilterRule{
{
Type: utils.MetaString,
Element: "~*req.Resources",
Values: []string{"ResourceProfile2"},
},
{
Type: utils.MetaGreaterOrEqual,
Element: "~*req.PddInterval",
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Usage,
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Weight,
Values: []string{"15.0"},
},
},
}
dmRES.SetFilter(context.Background(), fltrRes2, true)
fltrRes3 := &engine.Filter{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "FLTR_RES_3",
Rules: []*engine.FilterRule{
{
Type: utils.MetaPrefix,
Element: "~*req.Resources",
Values: []string{"ResourceProfilePrefix"},
},
},
}
timeDurationExample := 10 * time.Second
dmRES.SetFilter(context.Background(), fltrRes3, true)
resprf[0].ResourceProfile.UsageTTL = 5
resourceTest[0].rPrf = resprf[0]
resourceTest[0].ttl = &timeDurationExample
if err := dmRES.SetResourceProfile(context.TODO(), resprf[0].ResourceProfile, true); err != nil {
t.Error(err)
}
if err := dmRES.SetResource(context.TODO(), resourceTest[0].Resource); err != nil {
t.Error(err)
}
mres, err := resService.matchingResourcesForEvent(context.TODO(), resEvs[0].Tenant, resEvs[0],
"TestResourceUsageTTLCase4", &timeDurationExample)
if err != nil {
t.Errorf("Error: %+v", err)
}
mres.unlock()
if !reflect.DeepEqual(resourceTest[0].Resource.Tenant, mres[0].Resource.Tenant) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[0].Resource.Tenant, mres[0].Resource.Tenant)
} else if !reflect.DeepEqual(resourceTest[0].Resource.ID, mres[0].Resource.ID) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[0].Resource.ID, mres[0].Resource.ID)
} else if !reflect.DeepEqual(resourceTest[0].rPrf, mres[0].rPrf) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[0].rPrf, mres[0].rPrf)
} else if !reflect.DeepEqual(resourceTest[0].ttl, mres[0].ttl) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[0].ttl, mres[0].ttl)
}
}
func TestResourceResIDsMp(t *testing.T) {
resprf := []*resourceProfile{
{
ResourceProfile: &utils.ResourceProfile{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile1",
FilterIDs: []string{"FLTR_RES_1", "*ai:~*req.AnswerTime:2014-07-14T14:25:00Z"},
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{""},
},
},
{
ResourceProfile: &utils.ResourceProfile{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile2", // identifier of this resource
FilterIDs: []string{"FLTR_RES_2", "*ai:~*req.AnswerTime:2014-07-14T14:25:00Z"},
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{""},
},
},
{
ResourceProfile: &utils.ResourceProfile{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile3",
FilterIDs: []string{"FLTR_RES_3", "*ai:~*req.AnswerTime:2014-07-14T14:25:00Z"},
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{""},
},
},
}
resourceTest := Resources{
{
Resource: &utils.Resource{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile1",
Usages: map[string]*utils.ResourceUsage{},
TTLIdx: []string{},
},
rPrf: resprf[0],
},
{
Resource: &utils.Resource{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile2",
Usages: map[string]*utils.ResourceUsage{},
TTLIdx: []string{},
},
rPrf: resprf[1],
},
{
Resource: &utils.Resource{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile3",
Usages: map[string]*utils.ResourceUsage{},
TTLIdx: []string{},
},
rPrf: resprf[2],
},
}
expected := utils.StringSet{
"ResourceProfile1": struct{}{},
"ResourceProfile2": struct{}{},
"ResourceProfile3": struct{}{},
}
if rcv := resourceTest.resIDsMp(); !reflect.DeepEqual(rcv, expected) {
t.Errorf("Expecting: %+v, received: %+v", expected, rcv)
}
}
func TestResourceMatchWithIndexFalse(t *testing.T) {
var dmRES *engine.DataManager
cfg := config.NewDefaultCGRConfig()
data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items)
dmRES = engine.NewDataManager(data, cfg, nil)
cfg.ResourceSCfg().StoreInterval = 1
cfg.ResourceSCfg().StringIndexedFields = nil
cfg.ResourceSCfg().PrefixIndexedFields = nil
fltrs := engine.NewFilterS(cfg, nil, dmRES)
resService := NewResourceService(dmRES, cfg,
fltrs, nil)
engine.Cache.Clear(nil)
fltrRes1 := &engine.Filter{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "FLTR_RES_1",
Rules: []*engine.FilterRule{
{
Type: utils.MetaString,
Element: "~*req.Resources",
Values: []string{"ResourceProfile1"},
},
{
Type: utils.MetaGreaterOrEqual,
Element: "~*req.UsageInterval",
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Usage,
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Weight,
Values: []string{"9.0"},
},
},
}
dmRES.SetFilter(context.Background(), fltrRes1, true)
fltrRes2 := &engine.Filter{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "FLTR_RES_2",
Rules: []*engine.FilterRule{
{
Type: utils.MetaString,
Element: "~*req.Resources",
Values: []string{"ResourceProfile2"},
},
{
Type: utils.MetaGreaterOrEqual,
Element: "~*req.PddInterval",
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Usage,
Values: []string{time.Second.String()},
},
{
Type: utils.MetaGreaterOrEqual,
Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Weight,
Values: []string{"15.0"},
},
},
}
dmRES.SetFilter(context.Background(), fltrRes2, true)
fltrRes3 := &engine.Filter{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "FLTR_RES_3",
Rules: []*engine.FilterRule{
{
Type: utils.MetaPrefix,
Element: "~*req.Resources",
Values: []string{"ResourceProfilePrefix"},
},
},
}
dmRES.SetFilter(context.Background(), fltrRes3, true)
resprf := []*resourceProfile{
{
ResourceProfile: &utils.ResourceProfile{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile1",
FilterIDs: []string{"FLTR_RES_1", "*ai:*now:2014-07-14T14:25:00Z"},
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{""},
},
},
{
ResourceProfile: &utils.ResourceProfile{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile2", // identifier of this resource
FilterIDs: []string{"FLTR_RES_2", "*ai:*now:2014-07-14T14:25:00Z"},
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{""},
},
},
{
ResourceProfile: &utils.ResourceProfile{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile3",
FilterIDs: []string{"FLTR_RES_3", "*ai:*now:2014-07-14T14:25:00Z"},
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{""},
},
},
}
resourceTest := Resources{
{
Resource: &utils.Resource{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile1",
Usages: map[string]*utils.ResourceUsage{},
TTLIdx: []string{},
},
rPrf: resprf[0],
},
{
Resource: &utils.Resource{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile2",
Usages: map[string]*utils.ResourceUsage{},
TTLIdx: []string{},
},
rPrf: resprf[1],
},
{
Resource: &utils.Resource{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "ResourceProfile3",
Usages: map[string]*utils.ResourceUsage{},
TTLIdx: []string{},
},
rPrf: resprf[2],
},
}
resEvs := []*utils.CGREvent{
{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "event1",
Event: map[string]any{
"Resources": "ResourceProfile1",
utils.AnswerTime: time.Date(2014, 7, 14, 14, 30, 0, 0, time.UTC),
"UsageInterval": "1s",
"PddInterval": "1s",
utils.Weight: "20.0",
utils.Usage: 135 * time.Second,
utils.Cost: 123.0,
},
},
{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "event2",
Event: map[string]any{
"Resources": "ResourceProfile2",
utils.AnswerTime: time.Date(2014, 7, 14, 14, 30, 0, 0, time.UTC),
"UsageInterval": "1s",
"PddInterval": "1s",
utils.Weight: "15.0",
utils.Usage: 45 * time.Second,
},
},
{
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
ID: "event3",
Event: map[string]any{
"Resources": "ResourceProfilePrefix",
utils.Usage: 30 * time.Second,
},
},
}
timeDurationExample := 10 * time.Second
for _, resProfile := range resprf {
dmRES.SetResourceProfile(context.TODO(), resProfile.ResourceProfile, true)
}
for _, res := range resourceTest {
dmRES.SetResource(context.TODO(), res.Resource)
}
resService.cfg.ResourceSCfg().IndexedSelects = false
mres, err := resService.matchingResourcesForEvent(context.TODO(), resEvs[0].Tenant, resEvs[0],
"TestResourceMatchWithIndexFalse1", &timeDurationExample)
if err != nil {
t.Errorf("Error: %+v", err)
}
mres.unlock()
if !reflect.DeepEqual(resourceTest[0].Resource.Tenant, mres[0].Resource.Tenant) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[0].Resource.Tenant, mres[0].Resource.Tenant)
} else if !reflect.DeepEqual(resourceTest[0].Resource.ID, mres[0].Resource.ID) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[0].Resource.ID, mres[0].Resource.ID)
} else if !reflect.DeepEqual(resourceTest[0].rPrf, mres[0].rPrf) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[0].rPrf, mres[0].rPrf)
}
mres, err = resService.matchingResourcesForEvent(context.TODO(), resEvs[1].Tenant, resEvs[1],
"TestResourceMatchWithIndexFalse2", &timeDurationExample)
if err != nil {
t.Errorf("Error: %+v", err)
}
mres.unlock()
if !reflect.DeepEqual(resourceTest[1].Resource.Tenant, mres[0].Resource.Tenant) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[1].Resource.Tenant, mres[0].Resource.Tenant)
} else if !reflect.DeepEqual(resourceTest[1].Resource.ID, mres[0].Resource.ID) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[1].Resource.ID, mres[0].Resource.ID)
} else if !reflect.DeepEqual(resourceTest[1].rPrf, mres[0].rPrf) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[1].rPrf, mres[0].rPrf)
}
mres, err = resService.matchingResourcesForEvent(context.TODO(), resEvs[2].Tenant, resEvs[2],
"TestResourceMatchWithIndexFalse3", &timeDurationExample)
if err != nil {
t.Errorf("Error: %+v", err)
}
mres.unlock()
if !reflect.DeepEqual(resourceTest[2].Resource.Tenant, mres[0].Resource.Tenant) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[2].Resource.Tenant, mres[0].Resource.Tenant)
} else if !reflect.DeepEqual(resourceTest[2].Resource.ID, mres[0].Resource.ID) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[2].Resource.ID, mres[0].Resource.ID)
} else if !reflect.DeepEqual(resourceTest[2].rPrf, mres[0].rPrf) {
t.Errorf("Expecting: %+v, received: %+v", resourceTest[2].rPrf, mres[0].rPrf)
}
}
func TestResourceCaching(t *testing.T) {
//clear the cache
engine.Cache.Clear(nil)
// start fresh with new dataManager
cfg := config.NewDefaultCGRConfig()
idb, err := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items)
if err != nil {
t.Error(err)
}
dm := engine.NewDataManager(idb, cfg, nil)
cfg.ResourceSCfg().StoreInterval = 1
cfg.ResourceSCfg().StringIndexedFields = nil
cfg.ResourceSCfg().PrefixIndexedFields = nil
fltrs := engine.NewFilterS(cfg, nil, dm)
rS := NewResourceService(dm, cfg,
fltrs, nil)
resProf := &utils.ResourceProfile{
Tenant: "cgrates.org",
ID: "ResourceProfileCached",
FilterIDs: []string{"*string:~*req.Account:1001", "*ai:*now:2014-07-14T14:25:00Z"},
UsageTTL: -1,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{utils.MetaNone},
}
if err := engine.Cache.Set(context.TODO(), utils.CacheResourceProfiles, "cgrates.org:ResourceProfileCached",
resProf, nil, true, utils.EmptyString); err != nil {
t.Errorf("Expecting: nil, received: %s", err)
}
res := &utils.Resource{Tenant: resProf.Tenant,
ID: resProf.ID,
Usages: make(map[string]*utils.ResourceUsage)}
if err := engine.Cache.Set(context.TODO(), utils.CacheResources, "cgrates.org:ResourceProfileCached",
res, nil, true, utils.EmptyString); err != nil {
t.Errorf("Expecting: nil, received: %s", err)
}
resources := Resources{
{
Resource: res,
rPrf: &resourceProfile{ResourceProfile: resProf},
},
}
if err := engine.Cache.Set(context.TODO(), utils.CacheEventResources, "TestResourceCaching", resources.resIDsMp(), nil, true, ""); err != nil {
t.Errorf("Expecting: nil, received: %s", err)
}
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: utils.UUIDSha1Prefix(),
Event: map[string]any{
"Account": "1001",
"Destination": "3002"},
}
mres, err := rS.matchingResourcesForEvent(context.TODO(), ev.Tenant, ev,
"TestResourceCaching", nil)
if err != nil {
t.Fatal(err)
}
mres.unlock()
if !reflect.DeepEqual(resources[0].Resource.Tenant, mres[0].Resource.Tenant) {
t.Errorf("Expecting: %+v, received: %+v", resources[0].Resource.Tenant, mres[0].Resource.Tenant)
} else if !reflect.DeepEqual(resources[0].Resource.ID, mres[0].Resource.ID) {
t.Errorf("Expecting: %+v, received: %+v", resources[0].Resource.ID, mres[0].Resource.ID)
} else if !reflect.DeepEqual(resources[0].rPrf, mres[0].rPrf) {
t.Errorf("Expecting: %+v, received: %+v", resources[0].rPrf, mres[0].rPrf)
} else if !reflect.DeepEqual(resources[0].ttl, mres[0].ttl) {
t.Errorf("Expecting: %+v, received: %+v", resources[0].ttl, mres[0].ttl)
}
}
func TestResourcesRemoveExpiredUnitsResetTotalUsage(t *testing.T) {
tmpLogger := utils.Logger
defer func() {
utils.Logger = tmpLogger
}()
var buf bytes.Buffer
utils.Logger = utils.NewStdLoggerWithWriter(&buf, "", 4)
r := &resource{
Resource: &utils.Resource{
TTLIdx: []string{"ResGroup1", "ResGroup2", "ResGroup3"},
Usages: map[string]*utils.ResourceUsage{
"ResGroup2": {
Tenant: "cgrates.org",
ID: "RU_2",
Units: 11,
ExpiryTime: time.Date(2021, 5, 3, 13, 0, 0, 0, time.UTC),
},
"ResGroup3": {
Tenant: "cgrates.org",
ID: "RU_3",
},
},
},
tUsage: utils.Float64Pointer(10),
}
exp := &resource{
Resource: &utils.Resource{
TTLIdx: []string{"ResGroup3"},
Usages: map[string]*utils.ResourceUsage{
"ResGroup3": {
Tenant: "cgrates.org",
ID: "RU_3",
},
},
},
}
explog := "CGRateS <> [WARNING] resetting total usage for resourceID: , usage smaller than 0: -1.000000\n"
r.removeExpiredUnits()
if !reflect.DeepEqual(r, exp) {
t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", exp, r)
}
rcvlog := buf.String()
if rcvlog != explog {
t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", explog, rcvlog)
}
}
func TestResourcesAvailable(t *testing.T) {
r := utils.ResourceWithConfig{
Resource: &utils.Resource{
Usages: map[string]*utils.ResourceUsage{
"RU_1": {
Units: 4,
},
"RU_2": {
Units: 7,
},
},
},
Config: &utils.ResourceProfile{
Limit: 10,
},
}
exp := -1.0
rcv := r.Available()
if rcv != exp {
t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", exp, rcv)
}
}
func TestResourcesRecordUsageZeroTTL(t *testing.T) {
r := &resource{
Resource: &utils.Resource{
Usages: map[string]*utils.ResourceUsage{
"RU_1": {
Tenant: "cgrates.org",
ID: "RU_1",
},
},
},
ttl: utils.DurationPointer(0),
}
ru := &utils.ResourceUsage{
ID: "RU_2",
}
err := r.recordUsage(ru)
if err != nil {
t.Error(err)
}
}
func TestResourcesRecordUsageGtZeroTTL(t *testing.T) {
r := &resource{
Resource: &utils.Resource{
Usages: map[string]*utils.ResourceUsage{
"RU_1": {
Tenant: "cgrates.org",
ID: "RU_1",
},
},
TTLIdx: []string{"RU_1"},
},
ttl: utils.DurationPointer(1 * time.Second),
}
ru := &utils.ResourceUsage{
Tenant: "cgrates.org",
ID: "RU_2",
}
exp := &resource{
Resource: &utils.Resource{
Usages: map[string]*utils.ResourceUsage{
"RU_1": {
Tenant: "cgrates.org",
ID: "RU_1",
},
"RU_2": {
Tenant: "cgrates.org",
ID: "RU_2",
},
},
TTLIdx: []string{"RU_1", "RU_2"},
},
ttl: utils.DurationPointer(1 * time.Second),
}
err := r.recordUsage(ru)
exp.Resource.Usages[ru.ID].ExpiryTime = r.Resource.Usages[ru.ID].ExpiryTime
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(r, exp) {
t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", utils.ToJSON(exp), utils.ToJSON(r))
}
}
type mockWriter struct {
WriteF func(p []byte) (n int, err error)
}
func (mW *mockWriter) Write(p []byte) (n int, err error) {
if mW.WriteF != nil {
return mW.WriteF(p)
}
return 0, nil
}
func TestResourcesRecordUsageClearErr(t *testing.T) {
tmpLogger := utils.Logger
defer func() {
utils.Logger = tmpLogger
}()
var buf bytes.Buffer
utils.Logger = utils.NewStdLoggerWithWriter(&buf, "", 4)
rs := Resources{
{
Resource: &utils.Resource{
Usages: map[string]*utils.ResourceUsage{
"RU_1": {
Tenant: "cgrates.org",
ID: "RU_1",
},
"RU_2": {
Tenant: "cgrates.org",
ID: "RU_2",
},
},
TTLIdx: []string{"RU_1", "RU_2"},
},
ttl: utils.DurationPointer(1 * time.Second),
},
{
Resource: &utils.Resource{
Usages: map[string]*utils.ResourceUsage{
"RU_3": {
Tenant: "cgrates.org",
ID: "RU_3",
},
"RU_4": {
Tenant: "cgrates.org",
ID: "RU_4",
},
},
TTLIdx: []string{"RU_3"},
},
ttl: utils.DurationPointer(2 * time.Second),
},
}
ru := &utils.ResourceUsage{
Tenant: "cgrates.org",
ID: "RU_4",
}
exp := Resources{
{
Resource: &utils.Resource{
Usages: map[string]*utils.ResourceUsage{
"RU_1": {
Tenant: "cgrates.org",
ID: "RU_1",
},
"RU_2": {
Tenant: "cgrates.org",
ID: "RU_2",
},
},
TTLIdx: []string{"RU_1", "RU_2", "RU_4"},
},
ttl: utils.DurationPointer(1 * time.Second),
},
{
Resource: &utils.Resource{
Usages: map[string]*utils.ResourceUsage{
"RU_3": {
Tenant: "cgrates.org",
ID: "RU_3",
},
"RU_4": {
Tenant: "cgrates.org",
ID: "RU_4",
},
},
TTLIdx: []string{"RU_3"},
},
ttl: utils.DurationPointer(2 * time.Second),
},
}
explog := []string{
fmt.Sprintf("CGRateS <> [WARNING] <%s>cannot record usage, err: duplicate resource usage with id: %s:%s", utils.ResourceS, ru.Tenant, ru.ID),
fmt.Sprintf("CGRateS <> [WARNING] <%s> cannot clear usage, err: cannot find usage record with id: %s", utils.ResourceS, ru.ID),
}
experr := fmt.Sprintf("duplicate resource usage with id: %s", "cgrates.org:"+ru.ID)
utils.Logger = utils.NewStdLoggerWithWriter(&mockWriter{
WriteF: func(p []byte) (n int, err error) {
delete(rs[0].Resource.Usages, "RU_4")
return buf.Write(p)
},
}, "", 4)
err := rs.recordUsage(ru)
if err == nil || err.Error() != experr {
t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", experr, err)
}
if !reflect.DeepEqual(rs, exp) {
t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", utils.ToJSON(exp), utils.ToJSON(rs))
}
rcv := strings.Split(buf.String(), "\n")
for idx, exp := range explog {
if rcv[idx] != exp {
t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", exp, rcv[idx])
}
}
}
func TestResourceClearUsageErr(t *testing.T) {
tmpLogger := utils.Logger
defer func() {
utils.Logger = tmpLogger
}()
var buf bytes.Buffer
utils.Logger = utils.NewStdLoggerWithWriter(&buf, "", 4)
rs := Resources{
{
Resource: &utils.Resource{
Usages: map[string]*utils.ResourceUsage{
"RU_1": {
Tenant: "cgrates.org",
ID: "RU_1",
},
"RU_2": {
Tenant: "cgrates.org",
ID: "RU_2",
},
},
TTLIdx: []string{"RU_1", "RU_2"},
},
ttl: utils.DurationPointer(1 * time.Second),
},
}
ruTntID := "cgrates.org:RU_3"
experr := fmt.Sprintf("cannot find usage record with id: %s", ruTntID)
explog := fmt.Sprintf("CGRateS <> [WARNING] <ResourceS>, clear ruID: %s, err: %s\n", ruTntID, experr)
err := rs.clearUsage(ruTntID)
if err == nil || err.Error() != experr {
t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", experr, err)
}
rcvlog := buf.String()
if rcvlog != explog {
t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", explog, rcvlog)
}
}
func TestResourcesAllocateResourceErrRsUnavailable(t *testing.T) {
rs := Resources{}
ru := &utils.ResourceUsage{}
experr := utils.ErrResourceUnavailable
rcv, err := rs.allocateResource(ru, false)
if err == nil || !errors.Is(err, experr) {
t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", experr, err)
}
if rcv != "" {
t.Errorf("\nexpected empty string, got %s", rcv)
}
}
func TestResourcesAllocateResourceEmptyConfiguration(t *testing.T) {
rs := Resources{
{
Resource: &utils.Resource{
Usages: map[string]*utils.ResourceUsage{
"RU_1": {
Tenant: "cgrates.org",
ID: "RU_1",
},
"RU_2": {
Tenant: "cgrates.org",
ID: "RU_2",
},
},
TTLIdx: []string{"RU_1", "RU_2"},
Tenant: "cgrates.org",
ID: "Res_1",
},
ttl: utils.DurationPointer(1 * time.Second),
},
}
ru := &utils.ResourceUsage{
Tenant: "cgrates.org",
ID: "RU_2",
}
experr := fmt.Sprintf("empty configuration for resourceID: %s", rs[0].Resource.TenantID())
rcv, err := rs.allocateResource(ru, false)
if err == nil || err.Error() != experr {
t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", experr, err)
}
if rcv != "" {
t.Errorf("\nexpected empty string, got %s", rcv)
}
}
func TestResourcesAllocateResourceDryRun(t *testing.T) {
rs := Resources{
{
Resource: &utils.Resource{
Tenant: "cgrates.org",
ID: "Res_1",
Usages: map[string]*utils.ResourceUsage{
"RU_1": {
Tenant: "cgrates.org",
ID: "RU_1",
},
"RU_2": {
Tenant: "cgrates.org",
ID: "RU_2",
},
},
TTLIdx: []string{"RU_1", "RU_2"},
},
ttl: utils.DurationPointer(1 * time.Second),
rPrf: &resourceProfile{
ResourceProfile: &utils.ResourceProfile{
Tenant: "cgrates.org",
ID: "ResGroup1",
},
},
},
}
ru := &utils.ResourceUsage{
Tenant: "cgrates.org",
ID: "RU_2",
}
exp := "ResGroup1"
rcv, err := rs.allocateResource(ru, true)
if err != nil {
t.Errorf("\nexpected nil, got %+v", err)
}
if rcv != exp {
t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", exp, rcv)
}
}
func TestResourcesStoreResources(t *testing.T) {
tmp := engine.Cache
tmpLogger := utils.Logger
defer func() {
utils.Logger = tmpLogger
engine.Cache = tmp
}()
var buf bytes.Buffer
utils.Logger = utils.NewStdLoggerWithWriter(&buf, "", 6)
rS := &ResourceS{
storedResources: utils.StringSet{
"Res1": struct{}{},
},
}
value := &utils.Resource{
Tenant: "cgrates.org",
ID: "testResource",
}
engine.Cache.SetWithoutReplicate(utils.CacheResources, "Res1", value, nil, true,
utils.NonTransactional)
explog := fmt.Sprintf("CGRateS <> [WARNING] <%s> failed saving Resource with ID: %s, error: %s\n",
utils.ResourceS, value.ID, utils.ErrNoDatabaseConn.Error())
exp := &ResourceS{
storedResources: utils.StringSet{
"Res1": struct{}{},
},
}
rS.storeResources(context.TODO())
if !reflect.DeepEqual(rS, exp) {
t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", exp, rS)
}
rcvlog := buf.String()
if rcvlog != explog {
t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", explog, rcvlog)
}
}
func TestResourcesStoreResourceNotDirty(t *testing.T) {
rS := &ResourceS{}
r := &resource{
dirty: utils.BoolPointer(false),
}
err := rS.storeResource(context.TODO(), r)
if err != nil {
t.Errorf("\nexpected nil, received %+v", err)
}
}
func TestResourcesStoreResourceOK(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
idb, err := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items)
if err != nil {
t.Fatal(err)
}
rS := &ResourceS{
dm: engine.NewDataManager(idb, cfg, nil),
}
r := &resource{
Resource: &utils.Resource{},
dirty: utils.BoolPointer(true),
}
err = rS.storeResource(context.TODO(), r)
if err != nil {
t.Errorf("\nexpected nil, received %+v", err)
}
if *r.dirty != false {
t.Errorf("\nexpected false, received %+v", *r.dirty)
}
}
func TestResourcesStoreResourceErrCache(t *testing.T) {
tmp := engine.Cache
tmpLogger := utils.Logger
var buf bytes.Buffer
utils.Logger = utils.NewStdLoggerWithWriter(&buf, "", 6)
defer func() {
engine.Cache = tmp
utils.Logger = tmpLogger
}()
dft := config.CgrConfig()
defer config.SetCgrConfig(dft)
cfg := config.NewDefaultCGRConfig()
cfg.CacheCfg().ReplicationConns = []string{"test"}
cfg.CacheCfg().Partitions[utils.CacheResources].Replicate = true
cfg.RPCConns()["test"] = &config.RPCConn{Conns: []*config.RemoteHost{{}}}
config.SetCgrConfig(cfg)
idb, err := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items)
if err != nil {
t.Error(err)
}
dm := engine.NewDataManager(idb, cfg, engine.NewConnManager(cfg))
rS := NewResourceService(dm, cfg, nil, nil)
engine.Cache = engine.NewCacheS(cfg, dm, nil, nil)
r := &resource{
Resource: &utils.Resource{
Tenant: "cgrates.org",
ID: "RES1",
},
dirty: utils.BoolPointer(true),
}
engine.Cache.Set(context.Background(), utils.CacheResources, r.Resource.TenantID(), r, nil, true, "")
explog := `CGRateS <> [WARNING] <ResourceS> failed caching Resource with ID: cgrates.org:RES1, error: DISCONNECTED
`
if err := rS.storeResource(context.Background(), r); err == nil ||
err.Error() != rpcclient.ErrDisconnected.Error() {
t.Errorf("expected: <%+v>, \nreceived: <%+v>", rpcclient.ErrDisconnected, err)
}
if *r.dirty != true {
t.Errorf("\nexpected true, received %+v", *r.dirty)
}
rcvlog := buf.String()
if rcvlog != explog {
t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", explog, rcvlog)
}
}
func TestResourcesAllocateResourceEmptyKey(t *testing.T) {
rs := Resources{
{
Resource: &utils.Resource{
Usages: map[string]*utils.ResourceUsage{
"": {},
},
},
rPrf: &resourceProfile{
ResourceProfile: &utils.ResourceProfile{
Tenant: "cgrates.org",
ID: "RP_1",
AllocationMessage: "allocation msg",
},
},
},
}
ru := &utils.ResourceUsage{}
exp := "allocation msg"
rcv, err := rs.allocateResource(ru, false)
if err != nil {
t.Errorf("\nexpected nil, received %+v", err)
}
if rcv != exp {
t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", exp, rcv)
}
}
func TestResourcesProcessThresholdsNoConns(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
rS := &ResourceS{
cfg: cfg,
}
r := &resource{
Resource: &utils.Resource{
Tenant: "cgrates.org",
ID: "RES_1",
},
}
opts := map[string]any{}
err := rS.processThresholds(context.TODO(), Resources{r}, opts)
if err != nil {
t.Errorf("\nexpected nil, received %+v", err)
}
}
func TestResourcesProcessThresholdsOK(t *testing.T) {
tmp := engine.Cache
defer func() {
engine.Cache = tmp
}()
cfg := config.NewDefaultCGRConfig()
cfg.ResourceSCfg().ThresholdSConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaThresholds)}
engine.Cache = engine.NewCacheS(cfg, nil, nil, nil)
ccM := &ccMock{
calls: map[string]func(ctx *context.Context, args any, reply any) error{
utils.ThresholdSv1ProcessEvent: func(ctx *context.Context, args, reply any) error {
exp := &utils.CGREvent{
Tenant: "cgrates.org",
ID: args.(*utils.CGREvent).ID,
Event: map[string]any{
utils.EventType: utils.ResourceUpdate,
utils.ResourceID: "RES_1",
utils.Usage: 0.,
},
APIOpts: map[string]any{
utils.MetaEventType: utils.ResourceUpdate,
utils.OptsThresholdsProfileIDs: []string{"THD_1"},
},
}
if !reflect.DeepEqual(exp, args) {
return fmt.Errorf("\nexpected: <%+v>, \nreceived: <%+v>",
utils.ToJSON(exp), utils.ToJSON(args))
}
return nil
},
},
}
rpcInternal := make(chan birpc.ClientConnector, 1)
rpcInternal <- ccM
rS := &ResourceS{
cfg: cfg,
connMgr: engine.NewConnManager(cfg),
}
rS.connMgr.AddInternalConn(utils.ConcatenatedKey(utils.MetaInternal, utils.MetaThresholds), utils.ThresholdSv1, rpcInternal)
r := &resource{
Resource: &utils.Resource{
Tenant: "cgrates.org",
ID: "RES_1",
},
rPrf: &resourceProfile{
ResourceProfile: &utils.ResourceProfile{
Tenant: "cgrates.org",
ID: "RP_1",
ThresholdIDs: []string{"THD_1"},
},
},
}
err := rS.processThresholds(context.TODO(), Resources{r}, nil)
if err != nil {
t.Errorf("\nexpected nil, received %+v", err)
}
}
func TestResourcesProcessThresholdsCallErr(t *testing.T) {
tmp := engine.Cache
tmpLogger := utils.Logger
defer func() {
engine.Cache = tmp
utils.Logger = tmpLogger
}()
var buf bytes.Buffer
utils.Logger = utils.NewStdLoggerWithWriter(&buf, "", 4)
cfg := config.NewDefaultCGRConfig()
cfg.ResourceSCfg().ThresholdSConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaThresholds)}
engine.Cache = engine.NewCacheS(cfg, nil, nil, nil)
ccM := &ccMock{
calls: map[string]func(ctx *context.Context, args any, reply any) error{
utils.ThresholdSv1ProcessEvent: func(ctx *context.Context, args, reply any) error {
exp := &utils.CGREvent{
Tenant: "cgrates.org",
ID: args.(*utils.CGREvent).ID,
Event: map[string]any{
utils.EventType: utils.ResourceUpdate,
utils.ResourceID: "RES_1",
utils.Usage: 0.,
},
APIOpts: map[string]any{
utils.MetaEventType: utils.ResourceUpdate,
utils.OptsThresholdsProfileIDs: []string{"THD_1"},
},
}
if !reflect.DeepEqual(exp, args) {
return fmt.Errorf("\nexpected: <%+v>, \nreceived: <%+v>",
utils.ToJSON(exp), utils.ToJSON(args))
}
return utils.ErrExists
},
},
}
rpcInternal := make(chan birpc.ClientConnector, 1)
rpcInternal <- ccM
rS := &ResourceS{
cfg: cfg,
connMgr: engine.NewConnManager(cfg),
}
rS.connMgr.AddInternalConn(utils.ConcatenatedKey(utils.MetaInternal, utils.MetaThresholds), utils.ThresholdSv1, rpcInternal)
r := &resource{
Resource: &utils.Resource{
Tenant: "cgrates.org",
ID: "RES_1",
},
rPrf: &resourceProfile{
ResourceProfile: &utils.ResourceProfile{
Tenant: "cgrates.org",
ID: "RP_1",
ThresholdIDs: []string{"THD_1"},
},
},
}
experr := utils.ErrPartiallyExecuted
err := rS.processThresholds(context.TODO(), Resources{r}, nil)
if err == nil || err != experr {
t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", experr, err)
}
if !strings.Contains(buf.String(), "CGRateS <> [WARNING] <ResourceS> error: EXISTS") {
t.Errorf("expected log warning")
}
}
func TestResourcesProcessThresholdsThdConnMetaNone(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
cfg.ResourceSCfg().ThresholdSConns = []string{"connID"}
rS := &ResourceS{
cfg: cfg,
}
r := &resource{
Resource: &utils.Resource{
Tenant: "cgrates.org",
ID: "RES_1",
},
rPrf: &resourceProfile{
ResourceProfile: &utils.ResourceProfile{
ThresholdIDs: []string{utils.MetaNone},
},
},
}
opts := map[string]any{}
err := rS.processThresholds(context.TODO(), Resources{r}, opts)
if err != nil {
t.Errorf("\nexpected nil, received: %+v", err)
}
}
func TestResourceMatchingResourcesForEventNotFoundInCache(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
db, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items)
dmRES := engine.NewDataManager(db, cfg, nil)
cfg.ResourceSCfg().StoreInterval = 1
cfg.ResourceSCfg().StringIndexedFields = nil
cfg.ResourceSCfg().PrefixIndexedFields = nil
fltrs := engine.NewFilterS(cfg, nil, dmRES)
rS := NewResourceService(dmRES, cfg,
fltrs, nil)
engine.Cache.Set(context.Background(), utils.CacheEventResources, "TestResourceMatchingResourcesForEventNotFoundInCache", nil, nil, true, utils.NonTransactional)
_, err := rS.matchingResourcesForEvent(context.Background(), "cgrates.org", new(utils.CGREvent),
"TestResourceMatchingResourcesForEventNotFoundInCache", utils.DurationPointer(10*time.Second))
if err != utils.ErrNotFound {
t.Errorf("Error: %+v", err)
}
}
func TestResourceMatchingResourcesForEventNotFoundInDB(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
db, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items)
dmRES := engine.NewDataManager(db, cfg, nil)
cfg.ResourceSCfg().StoreInterval = 1
cfg.ResourceSCfg().StringIndexedFields = nil
cfg.ResourceSCfg().PrefixIndexedFields = nil
fltrs := engine.NewFilterS(cfg, nil, dmRES)
rS := NewResourceService(dmRES, cfg,
fltrs, nil)
engine.Cache.Set(context.Background(), utils.CacheEventResources, "TestResourceMatchingResourcesForEventNotFoundInDB", utils.StringSet{"Res2": {}}, nil, true, utils.NonTransactional)
_, err := rS.matchingResourcesForEvent(context.Background(), "cgrates.org", new(utils.CGREvent),
"TestResourceMatchingResourcesForEventNotFoundInDB", utils.DurationPointer(10*time.Second))
if err != utils.ErrNotFound {
t.Errorf("Error: %+v", err)
}
}
func TestResourceMatchingResourcesForEventLocks(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
db, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items)
dm := engine.NewDataManager(db, cfg, nil)
cfg.ResourceSCfg().StoreInterval = 1
cfg.ResourceSCfg().StringIndexedFields = nil
cfg.ResourceSCfg().PrefixIndexedFields = nil
fltrs := engine.NewFilterS(cfg, nil, dm)
rS := NewResourceService(dm, cfg,
fltrs, nil)
engine.Cache.Clear(nil)
prfs := make([]*resourceProfile, 0)
ids := utils.StringSet{}
for i := range 10 {
rPrf := &resourceProfile{
ResourceProfile: &utils.ResourceProfile{
Tenant: "cgrates.org",
ID: fmt.Sprintf("RES%d", i),
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{utils.MetaNone},
},
}
dm.SetResourceProfile(context.Background(), rPrf.ResourceProfile, true)
prfs = append(prfs, rPrf)
ids.Add(rPrf.ResourceProfile.ID)
}
dm.RemoveResource(context.Background(), "cgrates.org", "RES1")
engine.Cache.Set(context.Background(), utils.CacheEventResources, "TestResourceMatchingResourcesForEventLocks", ids, nil, true, utils.NonTransactional)
rs, err := rS.matchingResourcesForEvent(context.Background(), "cgrates.org", new(utils.CGREvent),
"TestResourceMatchingResourcesForEventLocks", utils.DurationPointer(10*time.Second))
if err != utils.ErrNotFound {
t.Errorf("Error: %+v", err)
}
for _, rPrf := range prfs {
if rPrf.isLocked() {
t.Fatalf("Expected profile to not be locked %q", rPrf.ResourceProfile.ID)
}
if rPrf.ResourceProfile.ID == "RES1" {
continue
}
for _, r := range rs {
if r.Resource.ID != rPrf.ResourceProfile.ID {
continue
}
if r.isLocked() {
t.Fatalf("Expected resource to not be locked %q", rPrf.ResourceProfile.ID)
}
}
}
}
func TestResourceMatchingResourcesForEventLocks2(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
db, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items)
dm := engine.NewDataManager(db, cfg, nil)
cfg.ResourceSCfg().StoreInterval = 1
cfg.ResourceSCfg().StringIndexedFields = nil
cfg.ResourceSCfg().PrefixIndexedFields = nil
fltrs := engine.NewFilterS(cfg, nil, dm)
rS := NewResourceService(dm, cfg,
fltrs, nil)
engine.Cache.Clear(nil)
prfs := make([]*resourceProfile, 0)
ids := utils.StringSet{}
for i := range 10 {
rPrf := &resourceProfile{
ResourceProfile: &utils.ResourceProfile{
Tenant: "cgrates.org",
ID: fmt.Sprintf("RES%d", i),
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{utils.MetaNone},
},
}
dm.SetResourceProfile(context.Background(), rPrf.ResourceProfile, true)
prfs = append(prfs, rPrf)
ids.Add(rPrf.ResourceProfile.ID)
}
rPrf := &resourceProfile{
ResourceProfile: &utils.ResourceProfile{
Tenant: "cgrates.org",
ID: "RES20",
FilterIDs: []string{"FLTR_RES_201"},
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{utils.MetaNone},
},
}
err := db.SetResourceProfileDrv(context.Background(), rPrf.ResourceProfile)
if err != nil {
t.Fatal(err)
}
prfs = append(prfs, rPrf)
ids.Add(rPrf.ResourceProfile.ID)
engine.Cache.Set(context.Background(), utils.CacheEventResources, "TestResourceMatchingResourcesForEventLocks2", ids, nil, true, utils.NonTransactional)
rs, err := rS.matchingResourcesForEvent(context.Background(), "cgrates.org", new(utils.CGREvent),
"TestResourceMatchingResourcesForEventLocks2", utils.DurationPointer(10*time.Second))
expErr := utils.ErrPrefixNotFound(rPrf.ResourceProfile.FilterIDs[0])
if err == nil || err.Error() != expErr.Error() {
t.Errorf("Expected error: %s ,received: %+v", expErr, err)
}
for _, rPrf := range prfs {
if rPrf.isLocked() {
t.Fatalf("Expected profile to not be locked %q", rPrf.ResourceProfile.ID)
}
if rPrf.ResourceProfile.ID == "RES20" {
continue
}
for _, r := range rs {
if r.Resource.ID != rPrf.ResourceProfile.ID {
continue
}
if r.isLocked() {
t.Fatalf("Expected resource to not be locked %q", rPrf.ResourceProfile.ID)
}
}
}
}
func TestResourceMatchingResourcesForEventLocks3(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
prfs := make([]*utils.ResourceProfile, 0)
engine.Cache.Clear(nil)
db := &engine.DataDBMock{
GetResourceProfileDrvF: func(_ *context.Context, tnt, id string) (*utils.ResourceProfile, error) {
if id == "RES1" {
return nil, utils.ErrNotImplemented
}
rPrf := &utils.ResourceProfile{
Tenant: "cgrates.org",
ID: id,
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
Weight: 20.00,
}},
ThresholdIDs: []string{utils.MetaNone},
}
engine.Cache.Set(context.Background(), utils.CacheResources, rPrf.TenantID(),
&utils.Resource{
Tenant: rPrf.Tenant,
ID: rPrf.ID,
Usages: make(map[string]*utils.ResourceUsage),
}, nil, true, utils.NonTransactional)
prfs = append(prfs, rPrf)
return rPrf, nil
},
}
dm := engine.NewDataManager(db, cfg, nil)
cfg.ResourceSCfg().StoreInterval = 1
cfg.ResourceSCfg().StringIndexedFields = nil
cfg.ResourceSCfg().PrefixIndexedFields = nil
fltrs := engine.NewFilterS(cfg, nil, dm)
rS := NewResourceService(dm, cfg,
fltrs, nil)
ids := utils.StringSet{}
for i := range 10 {
ids.Add(fmt.Sprintf("RES%d", i))
}
engine.Cache.Set(context.Background(), utils.CacheEventResources, "TestResourceMatchingResourcesForEventLocks3", ids, nil, true, utils.NonTransactional)
mres, err := rS.matchingResourcesForEvent(context.Background(), "cgrates.org", new(utils.CGREvent),
"TestResourceMatchingResourcesForEventLocks3", utils.DurationPointer(10*time.Second))
if err != utils.ErrNotImplemented {
t.Errorf("Error: %+v", err)
}
for _, r := range mres {
if r.rPrf.isLocked() {
t.Fatalf("Expected profile to not be locked %q", r.rPrf.ResourceProfile.ID)
}
}
}
func TestResourcesLockUnlockResourceProfiles(t *testing.T) {
rp := &resourceProfile{
ResourceProfile: &utils.ResourceProfile{
Tenant: "cgrates.org",
ID: "RES1",
Limit: 10,
AllocationMessage: "Approved",
Weights: utils.DynamicWeights{
{
Weight: 10,
}},
ThresholdIDs: []string{utils.MetaNone},
},
}
//lock profile with empty lkID parameter
rp.lock(utils.EmptyString)
if !rp.isLocked() {
t.Fatal("expected profile to be locked")
} else if rp.lkID == utils.EmptyString {
t.Fatal("expected struct field \"lkID\" to be non-empty")
}
//unlock previously locked profile
rp.unlock()
if rp.isLocked() {
t.Fatal("expected profile to be unlocked")
} else if rp.lkID != utils.EmptyString {
t.Fatal("expected struct field \"lkID\" to be empty")
}
//unlock an already unlocked profile - nothing happens
rp.unlock()
if rp.isLocked() {
t.Fatal("expected profile to be unlocked")
} else if rp.lkID != utils.EmptyString {
t.Fatal("expected struct field \"lkID\" to be empty")
}
}
func TestResourcesLockUnlockResources(t *testing.T) {
rs := &resource{
Resource: &utils.Resource{
Tenant: "cgrates.org",
ID: "RES1",
},
}
//lock resource with empty lkID parameter
rs.lock(utils.EmptyString)
if !rs.isLocked() {
t.Fatal("expected resource to be locked")
} else if rs.lkID == utils.EmptyString {
t.Fatal("expected struct field \"lkID\" to be non-empty")
}
//unlock previously locked resource
rs.unlock()
if rs.isLocked() {
t.Fatal("expected resource to be unlocked")
} else if rs.lkID != utils.EmptyString {
t.Fatal("expected struct field \"lkID\" to be empty")
}
//unlock an already unlocked resource - nothing happens
rs.unlock()
if rs.isLocked() {
t.Fatal("expected resource to be unlocked")
} else if rs.lkID != utils.EmptyString {
t.Fatal("expected struct field \"lkID\" to be empty")
}
}
func TestResourcesRunBackupStoreIntervalLessThanZero(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
cfg.ResourceSCfg().StoreInterval = -1
rS := &ResourceS{
cfg: cfg,
loopStopped: make(chan struct{}, 1),
}
rS.runBackup(context.Background())
select {
case <-rS.loopStopped:
case <-time.After(time.Second):
t.Error("timed out waiting for loop to stop")
}
}
func TestResourcesRunBackupStop(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
cfg.ResourceSCfg().StoreInterval = 5 * time.Millisecond
data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items)
dm := engine.NewDataManager(data, cfg, nil)
tnt := "cgrates.org"
resID := "Res1"
rS := &ResourceS{
dm: dm,
storedResources: utils.StringSet{
resID: struct{}{},
},
cfg: cfg,
loopStopped: make(chan struct{}, 1),
stopBackup: make(chan struct{}),
}
value := &utils.Resource{
Tenant: tnt,
ID: resID,
}
engine.Cache.SetWithoutReplicate(utils.CacheResources, resID, value, nil, true, "")
// Backup loop checks for the state of the stopBackup
// channel after storing the resource. Channel can be
// safely closed beforehand.
close(rS.stopBackup)
rS.runBackup(context.Background())
want := &utils.Resource{
Tenant: tnt,
ID: resID,
}
if got, err := rS.dm.GetResource(context.Background(), tnt, resID, true, false, ""); err != nil {
t.Errorf("dm.GetResource(%q,%q): got unexpected err=%v", tnt, resID, err)
} else if !reflect.DeepEqual(got, want) {
t.Errorf("dm.GetResource(%q,%q) = %v, want %v", tnt, resID, got, want)
}
select {
case <-rS.loopStopped:
case <-time.After(time.Second):
t.Error("timed out waiting for loop to stop")
}
}
func TestResourcesReload(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
cfg.ResourceSCfg().StoreInterval = 5 * time.Millisecond
rS := &ResourceS{
stopBackup: make(chan struct{}),
loopStopped: make(chan struct{}, 1),
cfg: cfg,
}
rS.loopStopped <- struct{}{}
rS.Reload(context.Background())
close(rS.stopBackup)
select {
case <-rS.loopStopped:
case <-time.After(time.Second):
t.Error("timed out waiting for loop to stop")
}
}
func TestResourcesStartLoop(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
cfg.ResourceSCfg().StoreInterval = -1
rS := &ResourceS{
loopStopped: make(chan struct{}),
cfg: cfg,
}
rS.StartLoop(context.Background())
select {
case <-rS.loopStopped:
case <-time.After(time.Second):
t.Error("timed out waiting for loop to stop")
}
}
// func TestResourcesMatchingResourcesForEvent2(t *testing.T) {
// tmp := engine.Cache
// tmpC := config.CgrConfig()
// defer func() {
// engine.Cache = tmp
// config.SetCgrConfig(tmpC)
// }()
// engine.Cache.Clear(nil)
// cfg := config.NewDefaultCGRConfig()
// cfg.CacheCfg().ReplicationConns = []string{"test"}
// cfg.CacheCfg().Partitions[utils.CacheEventResources].Replicate = true
// cfg.RPCConns()["test"] = &config.RPCConn{Conns: []*config.RemoteHost{{}}}
// config.SetCgrConfig(cfg)
// data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items)
// dm := engine.NewDataManager(data, cfg.CacheCfg(), nil)
// connMgr = engine.NewConnManager(cfg)
// engine.Cache = engine.NewCacheS(cfg, dm, nil,nil)
// fltrs := engine.NewFilterS(cfg, nil, dm)
// rsPrf := &utils.ResourceProfile{
// Tenant: "cgrates.org",
// ID: "RES1",
// FilterIDs: []string{"*string:~*req.Account:1001"},
// ThresholdIDs: []string{utils.MetaNone},
// AllocationMessage: "Approved",
// Weight: 10,
// Limit: 10,
// UsageTTL: time.Minute,
// Stored: true,
// }
// err := dm.SetResourceProfile(context.Background(), rsPrf, true)
// if err != nil {
// t.Fatal(err)
// }
// rS := NewResourceService(dm, cfg, fltrs, connMgr)
// ev := &utils.CGREvent{
// Tenant: "cgrates.org",
// ID: "TestMatchingResourcesForEvent",
// Event: map[string]any{
// utils.AccountField: "1001",
// },
// APIOpts: map[string]any{},
// }
// engine.Cache.SetWithoutReplicate(utils.CacheEventResources, ev.ID, utils.StringSet{
// "RES1": struct{}{},
// }, nil, true, utils.NonTransactional)
// _, err = rS.matchingResourcesForEvent(context.Background(), "cgrates.org", ev, ev.ID, utils.DurationPointer(10*time.Second))
// }
func TestResourcesMatchingResourcesForEventCacheSetErr(t *testing.T) {
tmp := engine.Cache
tmpC := config.CgrConfig()
defer func() {
engine.Cache = tmp
config.SetCgrConfig(tmpC)
}()
engine.Cache.Clear(nil)
cfg := config.NewDefaultCGRConfig()
cfg.CacheCfg().ReplicationConns = []string{"test"}
cfg.CacheCfg().Partitions[utils.CacheEventResources].Replicate = true
cfg.RPCConns()["test"] = &config.RPCConn{Conns: []*config.RemoteHost{{}}}
config.SetCgrConfig(cfg)
data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items)
dm := engine.NewDataManager(data, cfg, nil)
connMgr := engine.NewConnManager(cfg)
engine.Cache = engine.NewCacheS(cfg, dm, nil, nil)
fltrs := engine.NewFilterS(cfg, nil, dm)
rS := NewResourceService(dm, cfg, fltrs, connMgr)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestMatchingResourcesForEvent",
Event: map[string]any{
utils.AccountField: "1001",
},
APIOpts: map[string]any{},
}
if rcv, err := rS.matchingResourcesForEvent(context.Background(), "cgrates.org", ev, ev.ID,
utils.DurationPointer(10*time.Second)); err == nil || err.Error() != utils.ErrDisconnected.Error() {
t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrDisconnected, err)
} else if rcv != nil {
t.Errorf("expected nil, received: <%+v>", rcv)
}
}
func TestResourcesMatchingResourcesForEventFinalCacheSetErr(t *testing.T) {
tmp := engine.Cache
tmpC := config.CgrConfig()
defer func() {
engine.Cache = tmp
config.SetCgrConfig(tmpC)
}()
engine.Cache.Clear(nil)
cfg := config.NewDefaultCGRConfig()
cfg.CacheCfg().ReplicationConns = []string{"test"}
cfg.CacheCfg().Partitions[utils.CacheEventResources].Replicate = true
cfg.RPCConns()["test"] = &config.RPCConn{Conns: []*config.RemoteHost{{}}}
config.SetCgrConfig(cfg)
data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items)
dm := engine.NewDataManager(data, cfg, nil)
connMgr := engine.NewConnManager(cfg)
engine.Cache = engine.NewCacheS(cfg, dm, nil, nil)
fltrs := engine.NewFilterS(cfg, nil, dm)
rsPrf := &resourceProfile{
ResourceProfile: &utils.ResourceProfile{
Tenant: "cgrates.org",
ID: "RES1",
FilterIDs: []string{"*string:~*req.Account:1001"},
ThresholdIDs: []string{utils.MetaNone},
AllocationMessage: "Approved",
Weights: utils.DynamicWeights{
{
Weight: 10,
}},
Limit: 10,
UsageTTL: time.Minute,
Stored: true,
},
}
err := dm.SetResourceProfile(context.Background(), rsPrf.ResourceProfile, true)
if err != nil {
t.Fatal(err)
}
rS := NewResourceService(dm, cfg, fltrs, connMgr)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestMatchingResourcesForEvent",
Event: map[string]any{
utils.AccountField: "1001",
},
APIOpts: map[string]any{},
}
exp := &resource{
Resource: &utils.Resource{
Tenant: "cgrates.org",
ID: "RES1",
Usages: make(map[string]*utils.ResourceUsage),
},
ttl: utils.DurationPointer(10 * time.Second),
dirty: utils.BoolPointer(false),
rPrf: rsPrf,
}
if rcv, err := rS.matchingResourcesForEvent(context.Background(), "cgrates.org", ev, ev.ID,
utils.DurationPointer(10*time.Second)); err == nil || err.Error() != utils.ErrDisconnected.Error() {
t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrDisconnected, err)
} else if !reflect.DeepEqual(rcv[0], exp) {
t.Errorf("expected: <%+v>, received: <%+v>", exp, rcv[0])
} else if rcv[0].isLocked() {
t.Error("expected resource to be unlocked")
} else if rcv[0].lkID != utils.EmptyString {
t.Error("expected struct field \"lkID\" to be empty")
}
}
func TestResourceMatchingResourcesForEventWeightFromDynamicsErr(t *testing.T) {
defer func() {
engine.Cache = engine.NewCacheS(config.NewDefaultCGRConfig(), nil, nil, nil)
}()
cfg := config.NewDefaultCGRConfig()
db, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items)
dm := engine.NewDataManager(db, cfg, nil)
cfg.ResourceSCfg().StoreInterval = 1
cfg.ResourceSCfg().StringIndexedFields = nil
cfg.ResourceSCfg().PrefixIndexedFields = nil
fltrs := engine.NewFilterS(cfg, nil, dm)
rS := NewResourceService(dm, cfg,
fltrs, nil)
ids := utils.StringSet{}
for i := range 10 {
rPrf := &utils.ResourceProfile{
Tenant: "cgrates.org",
ID: fmt.Sprintf("RES%d", i),
UsageTTL: 10 * time.Second,
Limit: 10.00,
AllocationMessage: "AllocationMessage",
Weights: utils.DynamicWeights{
{
FilterIDs: []string{"*stirng:~*req.Account:1001"},
Weight: float64(10 - i),
}},
Blocker: i == 4,
ThresholdIDs: []string{utils.MetaNone},
}
dm.SetResourceProfile(context.Background(), rPrf, true)
ids.Add(rPrf.ID)
}
if err := engine.Cache.Set(context.Background(), utils.CacheEventResources, "TestResourceMatchingResourcesForEventLocksBlocker", ids, nil, true, utils.NonTransactional); err != nil {
t.Error(err)
}
expErr := "NOT_IMPLEMENTED:*stirng"
_, err := rS.matchingResourcesForEvent(context.Background(), "cgrates.org", new(utils.CGREvent),
"TestResourceMatchingResourcesForEventLocksBlocker", utils.DurationPointer(10*time.Second))
if err == nil || err.Error() != expErr {
t.Errorf("Expected error <%+v>, received error <%+v>", expErr, err)
}
}