mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-12 02:26:26 +05:00
229
apier/v1/reslimiter_it_test.go
Normal file
229
apier/v1/reslimiter_it_test.go
Normal file
@@ -0,0 +1,229 @@
|
||||
// +build integration
|
||||
|
||||
/*
|
||||
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
|
||||
Copyright (C) ITsysCOM GmbH
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
package v1
|
||||
|
||||
import (
|
||||
"net/rpc"
|
||||
"net/rpc/jsonrpc"
|
||||
"path"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cgrates/cgrates/config"
|
||||
"github.com/cgrates/cgrates/engine"
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
rlsV1CfgPath string
|
||||
rlsV1Cfg *config.CGRConfig
|
||||
rlsV1Rpc *rpc.Client
|
||||
)
|
||||
|
||||
func TestRLsV1LoadConfig(t *testing.T) {
|
||||
var err error
|
||||
rlsV1CfgPath = path.Join(*dataDir, "conf", "samples", "reslimiter")
|
||||
if rlsV1Cfg, err = config.NewCGRConfigFromFolder(rlsV1CfgPath); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRLsV1InitDataDb(t *testing.T) {
|
||||
if err := engine.InitDataDb(rlsV1Cfg); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRLsV1StartEngine(t *testing.T) {
|
||||
if _, err := engine.StopStartEngine(rlsV1CfgPath, 1000); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRLsV1RpcConn(t *testing.T) {
|
||||
var err error
|
||||
rlsV1Rpc, err = jsonrpc.Dial("tcp", rlsV1Cfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
|
||||
if err != nil {
|
||||
t.Fatal("Could not connect to rater: ", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRLsV1TPFromFolder(t *testing.T) {
|
||||
var reply string
|
||||
time.Sleep(time.Duration(2000) * time.Millisecond)
|
||||
attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "tutorial")}
|
||||
if err := rlsV1Rpc.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
time.Sleep(time.Duration(1000) * time.Millisecond)
|
||||
}
|
||||
|
||||
func TestRLsV1GetLimitsForEvent(t *testing.T) {
|
||||
var reply *[]*engine.ResourceLimit
|
||||
|
||||
ev := map[string]interface{}{"Unknown": "unknown"}
|
||||
if err := rlsV1Rpc.Call("RLsV1.GetLimitsForEvent", ev, &reply); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if len(*reply) != 0 {
|
||||
t.Errorf("Expecting: %+v, received: %+v", 0, len(*reply))
|
||||
}
|
||||
|
||||
ev = map[string]interface{}{"Destination": "10"}
|
||||
if err := rlsV1Rpc.Call("RLsV1.GetLimitsForEvent", ev, &reply); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if len(*reply) != 1 {
|
||||
t.Errorf("Expecting: %+v, received: %+v", 1, len(*reply))
|
||||
}
|
||||
if (*reply)[0].ID != "ResGroup2" {
|
||||
t.Errorf("Expecting: %+v, received: %+v", "ResGroup2", (*reply)[0].ID)
|
||||
}
|
||||
|
||||
ev = map[string]interface{}{"Destination": "20"}
|
||||
if err := rlsV1Rpc.Call("RLsV1.GetLimitsForEvent", ev, &reply); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if len(*reply) != 0 {
|
||||
t.Errorf("Expecting: %+v, received: %+v", 0, len(*reply))
|
||||
}
|
||||
|
||||
ev = map[string]interface{}{"Account": "1002", "Subject": "test", "Destination": "1002"}
|
||||
if err := rlsV1Rpc.Call("RLsV1.GetLimitsForEvent", ev, &reply); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if len(*reply) != 2 {
|
||||
t.Errorf("Expecting: %+v, received: %+v", 2, len(*reply))
|
||||
}
|
||||
|
||||
ev = map[string]interface{}{"Account": "1002", "Subject": "test", "Destination": "1001"}
|
||||
if err := rlsV1Rpc.Call("RLsV1.GetLimitsForEvent", ev, &reply); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if len(*reply) != 1 {
|
||||
t.Errorf("Expecting: %+v, received: %+v", 1, len(*reply))
|
||||
}
|
||||
if (*reply)[0].ID != "ResGroup2" {
|
||||
t.Errorf("Expecting: %+v, received: %+v", "ResGroup2", (*reply)[0].ID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRLsV1AllocateResource(t *testing.T) {
|
||||
var reply string
|
||||
|
||||
attrRU := utils.AttrRLsResourceUsage{
|
||||
UsageID: "651a8db2-4f67-4cf8-b622-169e8a482e51",
|
||||
Event: map[string]interface{}{"Account": "1002", "Subject": "1001", "Destination": "1002"},
|
||||
Units: 3,
|
||||
}
|
||||
if err := rlsV1Rpc.Call("RLsV1.AllocateResource", attrRU, &reply); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if reply != "ResGroup1" {
|
||||
t.Errorf("Expecting: %+v, received: %+v", "ResGroup1", reply)
|
||||
}
|
||||
|
||||
time.Sleep(time.Duration(1000) * time.Millisecond)
|
||||
|
||||
attrRU = utils.AttrRLsResourceUsage{
|
||||
UsageID: "651a8db2-4f67-4cf8-b622-169e8a482e52",
|
||||
Event: map[string]interface{}{"Destination": "100"},
|
||||
Units: 5,
|
||||
}
|
||||
if err := rlsV1Rpc.Call("RLsV1.AllocateResource", attrRU, &reply); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if reply != "ResGroup2" {
|
||||
t.Errorf("Expecting: %+v, received: %+v", "ResGroup2", reply)
|
||||
}
|
||||
|
||||
time.Sleep(time.Duration(1000) * time.Millisecond)
|
||||
|
||||
attrRU = utils.AttrRLsResourceUsage{
|
||||
UsageID: "651a8db2-4f67-4cf8-b622-169e8a482e53",
|
||||
Event: map[string]interface{}{"Account": "1002", "Subject": "1001", "Destination": "1002"},
|
||||
Units: 3,
|
||||
}
|
||||
if err := rlsV1Rpc.Call("RLsV1.AllocateResource", attrRU, &reply); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if reply != "ResGroup1" {
|
||||
t.Errorf("Expecting: %+v, received: %+v", "ResGroup1", reply)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestRLsV1AllowUsage(t *testing.T) {
|
||||
var reply bool
|
||||
attrRU := utils.AttrRLsResourceUsage{
|
||||
UsageID: "651a8db2-4f67-4cf8-b622-169e8a482e51",
|
||||
Event: map[string]interface{}{"Account": "1002", "Subject": "1001", "Destination": "1002"},
|
||||
Units: 1,
|
||||
}
|
||||
if err := rlsV1Rpc.Call("RLsV1.AllowUsage", attrRU, &reply); err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
if reply != true {
|
||||
t.Errorf("Expecting: %+v, received: %+v", true, reply)
|
||||
}
|
||||
}
|
||||
|
||||
attrRU = utils.AttrRLsResourceUsage{
|
||||
UsageID: "651a8db2-4f67-4cf8-b622-169e8a482e51",
|
||||
Event: map[string]interface{}{"Account": "1002", "Subject": "1001", "Destination": "1002"},
|
||||
Units: 2,
|
||||
}
|
||||
if err := rlsV1Rpc.Call("RLsV1.AllowUsage", attrRU, &reply); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRLsV1ReleaseResource(t *testing.T) {
|
||||
var reply interface{}
|
||||
|
||||
attrRU := utils.AttrRLsResourceUsage{
|
||||
UsageID: "651a8db2-4f67-4cf8-b622-169e8a482e52",
|
||||
Event: map[string]interface{}{"Destination": "100"},
|
||||
Units: 2,
|
||||
}
|
||||
if err := rlsV1Rpc.Call("RLsV1.ReleaseResource", attrRU, &reply); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if err := rlsV1Rpc.Call("RLsV1.AllowUsage", attrRU, &reply); err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
if reply != true {
|
||||
t.Errorf("Expecting: %+v, received: %+v", true, reply)
|
||||
}
|
||||
}
|
||||
|
||||
attrRU.Units += 1
|
||||
if err := rlsV1Rpc.Call("RLsV1.AllowUsage", attrRU, &reply); err == nil {
|
||||
t.Errorf("Expecting: %+v, received: %+v", false, reply)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestRLsV1StopEngine(t *testing.T) {
|
||||
if err := engine.KillEngine(100); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
40
data/conf/samples/reslimiter/cgrates.json
Normal file
40
data/conf/samples/reslimiter/cgrates.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
|
||||
"general": {
|
||||
"log_level": 7,
|
||||
},
|
||||
|
||||
"listen": {
|
||||
"rpc_json": ":2012",
|
||||
"rpc_gob": ":2013",
|
||||
"http": ":2080",
|
||||
},
|
||||
|
||||
|
||||
"stor_db": {
|
||||
"db_password": "CGRateS.org",
|
||||
},
|
||||
|
||||
|
||||
"rals": {
|
||||
"enabled": true,
|
||||
"cdrstats_conns": [
|
||||
{"address": "*internal"}
|
||||
],
|
||||
},
|
||||
|
||||
"cdrstats": {
|
||||
"enabled": true,
|
||||
},
|
||||
|
||||
|
||||
"rls": {
|
||||
"enabled": true,
|
||||
"cdrstats_conns": [
|
||||
{"address": "*internal"}
|
||||
],
|
||||
"cache_dump_interval": "0s",
|
||||
"usage_ttl": "3h",
|
||||
},
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
#Id,FilterType,FilterFieldName,FilterFieldValues,ActivationInterval,TTL,Limit,AllocationReply,Weight,ActionTriggers
|
||||
ResGroup1,*string,Account,1001;1002,2014-07-29T15:00:00Z,1s,2,,10,
|
||||
ResGroup1,*string,Account,1001;1002,2014-07-29T15:00:00Z,1s,7,,20,
|
||||
ResGroup1,*string_prefix,Destination,10;20,,,,,,
|
||||
ResGroup1,*rsr_fields,,Subject(~^1.*1$);Destination(1002),,,,,,
|
||||
ResGroup2,*destinations,Destination,DST_FS,2014-07-29T15:00:00Z,3600s,2,SPECIAL_1002,10,
|
||||
ResGroup2,*destinations,Destination,DST_FS,2014-07-29T15:00:00Z,3600s,8,SPECIAL_1002,10,
|
||||
ResGroup2,*cdr_stats,,CDRST1:*min_ASR:34;CDRST_1001:*min_ASR:20,,,,,,
|
||||
|
||||
|
@@ -286,7 +286,7 @@ func (rls *ResourceLimiterService) V1AllowUsage(args utils.AttrRLsResourceUsage,
|
||||
return utils.NewErrServerError(err)
|
||||
}
|
||||
if _, err = mtcRLs.AllocateResource(&ResourceUsage{ID: args.UsageID,
|
||||
Time: time.Now(), Units: args.Units}, false); err != nil {
|
||||
Time: time.Now(), Units: args.Units}, true); err != nil {
|
||||
if err == utils.ErrResourceUnavailable {
|
||||
return // not error but still not allowed
|
||||
}
|
||||
|
||||
@@ -18,409 +18,276 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
package engine
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
// "reflect"
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
)
|
||||
|
||||
var rLS *ResourceLimiterService
|
||||
var (
|
||||
rl, rl2 *ResourceLimit
|
||||
ru, ru2, ru3 *ResourceUsage
|
||||
rls ResourceLimits
|
||||
)
|
||||
|
||||
/*
|
||||
func TestRLsIndexStringFilters(t *testing.T) {
|
||||
rls := []*ResourceLimit{
|
||||
&ResourceLimit{
|
||||
ID: "RL1",
|
||||
Weight: 20,
|
||||
Filters: []*RequestFilter{
|
||||
&RequestFilter{Type: MetaString, FieldName: "Account", Values: []string{"1001", "1002"}},
|
||||
&RequestFilter{Type: MetaRSRFields, Values: []string{"Subject(~^1.*1$)", "Destination(1002)"},
|
||||
rsrFields: utils.ParseRSRFieldsMustCompile("Subject(~^1.*1$);Destination(1002)", utils.INFIELD_SEP),
|
||||
}},
|
||||
ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
|
||||
Limit: 2,
|
||||
Usage: make(map[string]*ResourceUsage),
|
||||
},
|
||||
&ResourceLimit{
|
||||
ID: "RL2",
|
||||
Weight: 10,
|
||||
Filters: []*RequestFilter{
|
||||
&RequestFilter{Type: MetaString, FieldName: "Account", Values: []string{"dan", "1002"}},
|
||||
&RequestFilter{Type: MetaString, FieldName: "Subject", Values: []string{"dan"}},
|
||||
},
|
||||
ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
|
||||
Limit: 1,
|
||||
UsageTTL: time.Duration(1 * time.Millisecond),
|
||||
Usage: make(map[string]*ResourceUsage),
|
||||
},
|
||||
&ResourceLimit{
|
||||
ID: "RL4",
|
||||
Weight: 10,
|
||||
Filters: []*RequestFilter{
|
||||
&RequestFilter{Type: MetaStringPrefix, FieldName: "Destination", Values: []string{"+49"}},
|
||||
},
|
||||
ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
|
||||
Limit: 1,
|
||||
Usage: make(map[string]*ResourceUsage),
|
||||
},
|
||||
&ResourceLimit{
|
||||
ID: "RL5",
|
||||
Weight: 10,
|
||||
Filters: []*RequestFilter{
|
||||
&RequestFilter{Type: MetaStringPrefix, FieldName: "Destination", Values: []string{"+40"}},
|
||||
},
|
||||
ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
|
||||
Limit: 1,
|
||||
UsageTTL: time.Duration(10 * time.Millisecond),
|
||||
Usage: make(map[string]*ResourceUsage),
|
||||
},
|
||||
func TestResourceLimitRecordUsage(t *testing.T) {
|
||||
ru = &ResourceUsage{
|
||||
ID: "RU1",
|
||||
Time: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
|
||||
Units: 2,
|
||||
}
|
||||
for _, rl := range rls {
|
||||
cache.Set(utils.ResourceLimitsPrefix+rl.ID, rl, true, "")
|
||||
|
||||
ru2 = &ResourceUsage{
|
||||
ID: "RU2",
|
||||
Time: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
|
||||
Units: 2,
|
||||
}
|
||||
rLS = new(ResourceLimiterService)
|
||||
eIndexes := map[string]map[string]utils.StringMap{
|
||||
"Account": map[string]utils.StringMap{
|
||||
"1001": utils.StringMap{
|
||||
"RL1": true,
|
||||
},
|
||||
"1002": utils.StringMap{
|
||||
"RL1": true,
|
||||
"RL2": true,
|
||||
},
|
||||
"dan": utils.StringMap{
|
||||
"RL2": true,
|
||||
},
|
||||
},
|
||||
"Subject": map[string]utils.StringMap{
|
||||
"dan": utils.StringMap{
|
||||
"RL2": true,
|
||||
},
|
||||
},
|
||||
utils.NOT_AVAILABLE: map[string]utils.StringMap{
|
||||
utils.NOT_AVAILABLE: utils.StringMap{
|
||||
"RL4": true,
|
||||
"RL5": true,
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := rLS.indexStringFilters(nil); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(eIndexes, rLS.stringIndexes) {
|
||||
t.Errorf("Expecting: %+v, received: %+v", eIndexes, rLS.stringIndexes)
|
||||
}
|
||||
rl3 := &ResourceLimit{
|
||||
ID: "RL3",
|
||||
Weight: 10,
|
||||
|
||||
rl = &ResourceLimit{
|
||||
ID: "RL1",
|
||||
Filters: []*RequestFilter{
|
||||
&RequestFilter{Type: MetaString, FieldName: "Subject", Values: []string{"dan"}},
|
||||
&RequestFilter{Type: MetaString, FieldName: "Subject", Values: []string{"1003"}},
|
||||
},
|
||||
ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
|
||||
Limit: 1,
|
||||
Usage: make(map[string]*ResourceUsage),
|
||||
}
|
||||
cache.Set(utils.ResourceLimitsPrefix+rl3.ID, rl3, true, "")
|
||||
rl6 := &ResourceLimit{ // Add it so we can test expiryTime
|
||||
ID: "RL6",
|
||||
Weight: 10,
|
||||
Filters: []*RequestFilter{
|
||||
&RequestFilter{Type: MetaString, FieldName: "Subject", Values: []string{"dan"}},
|
||||
},
|
||||
ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
|
||||
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
|
||||
Limit: 1,
|
||||
Usage: make(map[string]*ResourceUsage),
|
||||
}
|
||||
cache.Set(utils.ResourceLimitsPrefix+rl6.ID, rl6, true, "")
|
||||
eIndexes = map[string]map[string]utils.StringMap{
|
||||
"Account": map[string]utils.StringMap{
|
||||
"1001": utils.StringMap{
|
||||
"RL1": true,
|
||||
&RequestFilter{
|
||||
Type: MetaString,
|
||||
FieldName: "Account",
|
||||
Values: []string{"1001", "1002"},
|
||||
},
|
||||
"1002": utils.StringMap{
|
||||
"RL1": true,
|
||||
"RL2": true,
|
||||
},
|
||||
"dan": utils.StringMap{
|
||||
"RL2": true,
|
||||
&RequestFilter{
|
||||
Type: MetaRSRFields,
|
||||
Values: []string{"Subject(~^1.*1$)", "Destination(1002)"},
|
||||
rsrFields: utils.ParseRSRFieldsMustCompile("Subject(~^1.*1$);Destination(1002)", utils.INFIELD_SEP),
|
||||
},
|
||||
},
|
||||
"Subject": map[string]utils.StringMap{
|
||||
"dan": utils.StringMap{
|
||||
"RL2": true,
|
||||
"RL3": true,
|
||||
"RL6": true,
|
||||
},
|
||||
"1003": utils.StringMap{
|
||||
"RL3": true,
|
||||
ActivationInterval: &utils.ActivationInterval{
|
||||
ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
|
||||
},
|
||||
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
|
||||
Weight: 100,
|
||||
Limit: 2,
|
||||
ActionTriggers: ActionTriggers{
|
||||
&ActionTrigger{
|
||||
// ID string // original csv tag
|
||||
// UniqueID string // individual id
|
||||
ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER,
|
||||
ThresholdValue: 2,
|
||||
// Recurrent bool // reset excuted flag each run
|
||||
// MinSleep time.Duration // Minimum duration between two executions in case of recurrent triggers
|
||||
// ExpirationDate time.Time
|
||||
// ActivationDate time.Time
|
||||
Balance: &BalanceFilter{
|
||||
Type: utils.StringPointer(utils.MONETARY),
|
||||
Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)),
|
||||
},
|
||||
ActionsID: "TEST_ACTIONS",
|
||||
// Weight float64
|
||||
// ActionsID string
|
||||
// MinQueuedItems int // Trigger actions only if this number is hit (stats only)
|
||||
// Executed bool
|
||||
// LastExecutionTime time.Time
|
||||
},
|
||||
},
|
||||
utils.NOT_AVAILABLE: map[string]utils.StringMap{
|
||||
utils.NOT_AVAILABLE: utils.StringMap{
|
||||
"RL4": true,
|
||||
"RL5": true,
|
||||
},
|
||||
UsageTTL: time.Duration(1 * time.Millisecond),
|
||||
AllocationMessage: "ALLOC",
|
||||
Usage: map[string]*ResourceUsage{
|
||||
ru.ID: ru,
|
||||
},
|
||||
TotalUsage: 2,
|
||||
}
|
||||
// Test index update
|
||||
if err := rLS.indexStringFilters([]string{rl3.ID, rl6.ID}); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(eIndexes, rLS.stringIndexes) {
|
||||
t.Errorf("Expecting: %+v, received: %+v", eIndexes, rLS.stringIndexes)
|
||||
}
|
||||
}
|
||||
*/
|
||||
/*
|
||||
func TestResourceLimitsAllowUsage(t *testing.T) {
|
||||
rls := make(ResourceLimits, 0)
|
||||
if !rls.AllowUsage(1.0) {
|
||||
t.Error("Not allowed for empty limits")
|
||||
}
|
||||
rls = ResourceLimits{
|
||||
&ResourceLimit{
|
||||
ID: "RLAU1",
|
||||
Weight: 20,
|
||||
Filters: []*RequestFilter{
|
||||
&RequestFilter{Type: MetaString, FieldName: "Account", Values: []string{"1001", "1002"}},
|
||||
&RequestFilter{Type: MetaRSRFields, Values: []string{"Subject(~^1.*1$)", "Destination(1002)"},
|
||||
rsrFields: utils.ParseRSRFieldsMustCompile("Subject(~^1.*1$);Destination(1002)", utils.INFIELD_SEP),
|
||||
}},
|
||||
ActivationInterval: &utils.ActivationInterval{ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC)},
|
||||
Limit: 1,
|
||||
Usage: map[string]*ResourceUsage{"call1": &ResourceUsage{
|
||||
ID: "call1", UsageTime: time.Now(), UsageUnits: 1}},
|
||||
TotalUsage: 1,
|
||||
},
|
||||
&ResourceLimit{
|
||||
ID: "RLAU2",
|
||||
Weight: 10,
|
||||
Filters: []*RequestFilter{
|
||||
&RequestFilter{Type: MetaString, FieldName: "Account", Values: []string{"dan", "1002"}},
|
||||
&RequestFilter{Type: MetaString, FieldName: "Subject", Values: []string{"dan"}},
|
||||
},
|
||||
ActivationInterval: &utils.ActivationInterval{ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC)},
|
||||
Limit: 2,
|
||||
UsageTTL: time.Duration(1 * time.Millisecond),
|
||||
Usage: make(map[string]*ResourceUsage),
|
||||
},
|
||||
}
|
||||
if !rls.AllowUsage(2.0) {
|
||||
t.Error("Not allowed")
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func TestRLsLoadRLs(t *testing.T) {
|
||||
rls := []*ResourceLimit{
|
||||
&ResourceLimit{
|
||||
ID: "RL1",
|
||||
Weight: 20,
|
||||
Filters: []*RequestFilter{
|
||||
&RequestFilter{Type: MetaString, FieldName: "Account", Values: []string{"1001", "1002"}},
|
||||
&RequestFilter{Type: MetaRSRFields, Values: []string{"Subject(~^1.*1$)", "Destination(1002)"},
|
||||
rsrFields: utils.ParseRSRFieldsMustCompile("Subject(~^1.*1$);Destination(1002)", utils.INFIELD_SEP),
|
||||
}},
|
||||
ActivationInterval: &utils.ActivationInterval{ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC)},
|
||||
Limit: 2,
|
||||
Usage: make(map[string]*ResourceUsage),
|
||||
},
|
||||
&ResourceLimit{
|
||||
ID: "RL2",
|
||||
Weight: 10,
|
||||
Filters: []*RequestFilter{
|
||||
&RequestFilter{Type: MetaString, FieldName: "Account", Values: []string{"dan", "1002"}},
|
||||
&RequestFilter{Type: MetaString, FieldName: "Subject", Values: []string{"dan"}},
|
||||
},
|
||||
ActivationInterval: &utils.ActivationInterval{ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC)},
|
||||
Limit: 1,
|
||||
UsageTTL: time.Duration(1 * time.Millisecond),
|
||||
Usage: make(map[string]*ResourceUsage),
|
||||
},
|
||||
&ResourceLimit{
|
||||
ID: "RL3",
|
||||
Weight: 10,
|
||||
Filters: []*RequestFilter{
|
||||
&RequestFilter{Type: MetaString, FieldName: "Subject", Values: []string{"dan2"}},
|
||||
&RequestFilter{Type: MetaString, FieldName: "Subject", Values: []string{"1003"}},
|
||||
},
|
||||
ActivationInterval: &utils.ActivationInterval{ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC)},
|
||||
Limit: 1,
|
||||
Usage: make(map[string]*ResourceUsage),
|
||||
},
|
||||
&ResourceLimit{
|
||||
ID: "RL4",
|
||||
Weight: 10,
|
||||
Filters: []*RequestFilter{
|
||||
&RequestFilter{Type: MetaStringPrefix, FieldName: "Destination", Values: []string{"+49"}},
|
||||
},
|
||||
ActivationInterval: &utils.ActivationInterval{ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC)},
|
||||
Limit: 1,
|
||||
Usage: make(map[string]*ResourceUsage),
|
||||
},
|
||||
&ResourceLimit{
|
||||
ID: "RL5",
|
||||
Weight: 10,
|
||||
Filters: []*RequestFilter{
|
||||
&RequestFilter{Type: MetaStringPrefix, FieldName: "Destination", Values: []string{"+40"}},
|
||||
},
|
||||
ActivationInterval: &utils.ActivationInterval{ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC)},
|
||||
Limit: 1,
|
||||
UsageTTL: time.Duration(10 * time.Millisecond),
|
||||
Usage: make(map[string]*ResourceUsage),
|
||||
},
|
||||
&ResourceLimit{ // Add it so we can test expiryTime
|
||||
ID: "RL6",
|
||||
Weight: 10,
|
||||
Filters: []*RequestFilter{
|
||||
&RequestFilter{Type: MetaString, FieldName: "Subject", Values: []string{"dan2"}},
|
||||
},
|
||||
ActivationInterval: &utils.ActivationInterval{ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC)},
|
||||
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
|
||||
Limit: 1,
|
||||
Usage: make(map[string]*ResourceUsage),
|
||||
},
|
||||
}
|
||||
rlIdxr, err := NewReqFilterIndexer(dataStorage, utils.ResourceLimitsIndex)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
for _, rl := range rls {
|
||||
if err := dataStorage.SetResourceLimit(rl, utils.NonTransactional); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
rlIdxr.IndexFilters(rl.ID, rl.Filters)
|
||||
}
|
||||
if err := rlIdxr.StoreIndexes(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRLsMatchingResourceLimitsForEvent(t *testing.T) {
|
||||
rLS = &ResourceLimiterService{dataDB: dataStorage, cdrStatS: nil}
|
||||
eResLimits := ResourceLimits{
|
||||
&ResourceLimit{
|
||||
ID: "RL1",
|
||||
Weight: 20,
|
||||
Filters: []*RequestFilter{
|
||||
&RequestFilter{Type: MetaString, FieldName: "Account", Values: []string{"1001", "1002"}},
|
||||
&RequestFilter{Type: MetaRSRFields, Values: []string{"Subject(~^1.*1$)", "Destination(1002)"},
|
||||
rsrFields: utils.ParseRSRFieldsMustCompile("Subject(~^1.*1$);Destination(1002)", utils.INFIELD_SEP),
|
||||
}},
|
||||
ActivationInterval: &utils.ActivationInterval{ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC)},
|
||||
Limit: 2,
|
||||
Usage: make(map[string]*ResourceUsage),
|
||||
},
|
||||
&ResourceLimit{
|
||||
ID: "RL2",
|
||||
Weight: 10,
|
||||
Filters: []*RequestFilter{
|
||||
&RequestFilter{Type: MetaString, FieldName: "Account", Values: []string{"dan", "1002"}},
|
||||
&RequestFilter{Type: MetaString, FieldName: "Subject", Values: []string{"dan"}},
|
||||
},
|
||||
ActivationInterval: &utils.ActivationInterval{ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC)},
|
||||
Limit: 1,
|
||||
UsageTTL: time.Duration(1 * time.Millisecond),
|
||||
Usage: make(map[string]*ResourceUsage),
|
||||
},
|
||||
}
|
||||
if resLimits, err := rLS.matchingResourceLimitsForEvent(
|
||||
map[string]interface{}{"Account": "1002", "Subject": "dan", "Destination": "1002"}); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(eResLimits[0].Filters[0], resLimits[0].Filters[0]) {
|
||||
t.Errorf("Expecting: %+v, received: %+v", eResLimits, resLimits)
|
||||
if err := rl.RecordUsage(ru2); err != nil {
|
||||
t.Error(err.Error())
|
||||
} else {
|
||||
// Make sure the filters are what we expect to be after retrieving from cache:
|
||||
fltr := resLimits[0].Filters[1]
|
||||
if pass, _ := fltr.Pass(map[string]interface{}{"Subject": "10000001"}, "", nil); !pass {
|
||||
t.Errorf("Expecting RL: %+v, received: %+v", eResLimits[0], resLimits[0])
|
||||
if err := rl.RecordUsage(ru); err == nil {
|
||||
t.Error("Duplicate ResourceUsage id should not be allowed")
|
||||
}
|
||||
if pass, _ := fltr.Pass(map[string]interface{}{"Account": "1002"}, "", nil); pass {
|
||||
t.Errorf("Expecting RL: %+v, received: %+v", eResLimits[0], resLimits[0])
|
||||
if _, found := rl.Usage[ru2.ID]; !found {
|
||||
t.Error("ResourceUsage was not recorded")
|
||||
}
|
||||
if rl.TotalUsage != 4 {
|
||||
t.Errorf("Expecting: %+v, received: %+v", 4, rl.TotalUsage)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestRLClearUsage(t *testing.T) {
|
||||
rl.Usage = map[string]*ResourceUsage{
|
||||
ru.ID: ru,
|
||||
}
|
||||
rl.TotalUsage = 3
|
||||
|
||||
rl.ClearUsage(ru.ID)
|
||||
|
||||
if len(rl.Usage) != 0 {
|
||||
t.Errorf("Expecting: %+v, received: %+v", 0, len(rl.Usage))
|
||||
}
|
||||
if rl.TotalUsage != 1 {
|
||||
t.Errorf("Expecting: %+v, received: %+v", 1, rl.TotalUsage)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRLsV1ResourceLimitsForEvent(t *testing.T) {
|
||||
eLimits := []*ResourceLimit{
|
||||
&ResourceLimit{
|
||||
ID: "RL1",
|
||||
Weight: 20,
|
||||
Filters: []*RequestFilter{
|
||||
&RequestFilter{Type: MetaString, FieldName: "Account", Values: []string{"1001", "1002"}},
|
||||
&RequestFilter{Type: MetaRSRFields, Values: []string{"Subject(~^1.*1$)", "Destination(1002)"},
|
||||
rsrFields: utils.ParseRSRFieldsMustCompile("Subject(~^1.*1$);Destination(1002)", utils.INFIELD_SEP),
|
||||
}},
|
||||
ActivationInterval: &utils.ActivationInterval{ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC)},
|
||||
Limit: 2,
|
||||
Usage: make(map[string]*ResourceUsage),
|
||||
},
|
||||
&ResourceLimit{
|
||||
ID: "RL2",
|
||||
Weight: 10,
|
||||
Filters: []*RequestFilter{
|
||||
&RequestFilter{Type: MetaString, FieldName: "Account", Values: []string{"dan", "1002"}},
|
||||
&RequestFilter{Type: MetaString, FieldName: "Subject", Values: []string{"dan"}},
|
||||
func TestRLRemoveExpiredUnits(t *testing.T) {
|
||||
rl.Usage = map[string]*ResourceUsage{
|
||||
ru.ID: ru,
|
||||
}
|
||||
rl.TotalUsage = 2
|
||||
|
||||
rl.removeExpiredUnits()
|
||||
|
||||
if len(rl.Usage) != 0 {
|
||||
t.Errorf("Expecting: %+v, received: %+v", 0, len(rl.Usage))
|
||||
}
|
||||
if rl.TotalUsage != 0 {
|
||||
t.Errorf("Expecting: %+v, received: %+v", 0, rl.TotalUsage)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRLUsedUnits(t *testing.T) {
|
||||
rl.Usage = map[string]*ResourceUsage{
|
||||
ru.ID: ru,
|
||||
}
|
||||
rl.TotalUsage = 2
|
||||
|
||||
usedUnits := rl.UsedUnits()
|
||||
|
||||
if len(rl.Usage) != 0 {
|
||||
t.Errorf("Expecting: %+v, received: %+v", 0, len(rl.Usage))
|
||||
}
|
||||
if usedUnits != 0 {
|
||||
t.Errorf("Expecting: %+v, received: %+v", 0, usedUnits)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRLSort(t *testing.T) {
|
||||
rl2 = &ResourceLimit{
|
||||
ID: "RL2",
|
||||
Filters: []*RequestFilter{
|
||||
&RequestFilter{
|
||||
Type: MetaString,
|
||||
FieldName: "Account",
|
||||
Values: []string{"1001", "1002"},
|
||||
},
|
||||
&RequestFilter{
|
||||
Type: MetaRSRFields,
|
||||
Values: []string{"Subject(~^1.*1$)", "Destination(1002)"},
|
||||
rsrFields: utils.ParseRSRFieldsMustCompile("Subject(~^1.*1$);Destination(1002)", utils.INFIELD_SEP),
|
||||
},
|
||||
ActivationInterval: &utils.ActivationInterval{ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC)},
|
||||
Limit: 1,
|
||||
UsageTTL: time.Duration(1 * time.Millisecond),
|
||||
Usage: make(map[string]*ResourceUsage),
|
||||
},
|
||||
ActivationInterval: &utils.ActivationInterval{
|
||||
ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
|
||||
},
|
||||
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
|
||||
Weight: 50,
|
||||
Limit: 2,
|
||||
ActionTriggers: ActionTriggers{
|
||||
&ActionTrigger{
|
||||
// ID string // original csv tag
|
||||
// UniqueID string // individual id
|
||||
ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER,
|
||||
ThresholdValue: 2,
|
||||
// Recurrent bool // reset excuted flag each run
|
||||
// MinSleep time.Duration // Minimum duration between two executions in case of recurrent triggers
|
||||
// ExpirationDate time.Time
|
||||
// ActivationDate time.Time
|
||||
Balance: &BalanceFilter{
|
||||
Type: utils.StringPointer(utils.MONETARY),
|
||||
Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)),
|
||||
},
|
||||
ActionsID: "TEST_ACTIONS",
|
||||
// Weight float64
|
||||
// ActionsID string
|
||||
// MinQueuedItems int // Trigger actions only if this number is hit (stats only)
|
||||
// Executed bool
|
||||
// LastExecutionTime time.Time
|
||||
},
|
||||
},
|
||||
UsageTTL: time.Duration(1 * time.Millisecond),
|
||||
// AllocationMessage: "ALLOC2",
|
||||
Usage: map[string]*ResourceUsage{
|
||||
ru2.ID: ru2,
|
||||
},
|
||||
TotalUsage: 2,
|
||||
}
|
||||
var rcvLmts []*ResourceLimit
|
||||
if err := rLS.V1ResourceLimitsForEvent(map[string]interface{}{"Account": "1002", "Subject": "dan", "Destination": "1002"}, &rcvLmts); err != nil {
|
||||
t.Error(err)
|
||||
} else if len(eLimits) != len(rcvLmts) {
|
||||
t.Errorf("Expecting: %+v, received: %+v", eLimits, rcvLmts)
|
||||
|
||||
rls = ResourceLimits{rl2, rl}
|
||||
rls.Sort()
|
||||
|
||||
if rls[0].ID != "RL1" {
|
||||
t.Error("Sort failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRLsV1InitiateResourceUsage(t *testing.T) {
|
||||
attrRU := utils.AttrRLsResourceUsage{
|
||||
UsageID: "651a8db2-4f67-4cf8-b622-169e8a482e50",
|
||||
Event: map[string]interface{}{"Account": "1002", "Subject": "dan", "Destination": "1002"},
|
||||
Units: 1,
|
||||
}
|
||||
var reply string
|
||||
if err := rLS.V1AllocateResource(attrRU, &reply); err != nil {
|
||||
t.Error(err)
|
||||
} else if reply != "RL1" {
|
||||
t.Error("Received reply: ", reply)
|
||||
}
|
||||
resLimits, err := rLS.matchingResourceLimitsForEvent(attrRU.Event)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else if len(resLimits) != 2 {
|
||||
t.Errorf("Received: %+v", resLimits)
|
||||
} else if resLimits[0].UsedUnits() != 1 {
|
||||
t.Errorf("RL1: %+v", resLimits[0])
|
||||
} else if _, hasKey := resLimits[0].Usage[attrRU.UsageID]; !hasKey {
|
||||
t.Errorf("RL1: %+v", resLimits[0])
|
||||
func TestRLsClearUsage(t *testing.T) {
|
||||
rls.ClearUsage(ru2.ID)
|
||||
for _, rl := range rls {
|
||||
if len(rl.Usage) > 0 {
|
||||
t.Errorf("Expecting: %+v, received: %+v", 0, len(rl.Usage))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRLsV1TerminateResourceUsage(t *testing.T) {
|
||||
attrRU := utils.AttrRLsResourceUsage{
|
||||
UsageID: "651a8db2-4f67-4cf8-b622-169e8a482e50",
|
||||
Event: map[string]interface{}{"Account": "1002", "Subject": "dan", "Destination": "1002"},
|
||||
Units: 1,
|
||||
}
|
||||
var reply string
|
||||
if err := rLS.V1ReleaseResource(attrRU, &reply); err != nil {
|
||||
t.Error(err)
|
||||
} else if reply != utils.OK {
|
||||
t.Error("Received reply: ", reply)
|
||||
}
|
||||
resLimits, err := rLS.matchingResourceLimitsForEvent(attrRU.Event)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else if len(resLimits) != 2 {
|
||||
t.Errorf("Received: %+v", resLimits)
|
||||
} else if resLimits[0].UsedUnits() != 0 {
|
||||
t.Errorf("RL1: %+v", resLimits[0])
|
||||
} else if _, hasKey := resLimits[0].Usage[attrRU.UsageID]; hasKey {
|
||||
t.Errorf("RL1: %+v", resLimits[0])
|
||||
func TestRLsRecordUsages(t *testing.T) {
|
||||
if err := rls.RecordUsage(ru); err != nil {
|
||||
for _, rl := range rls {
|
||||
if _, found := rl.Usage[ru.ID]; found {
|
||||
t.Error("Fallback on error failed")
|
||||
}
|
||||
}
|
||||
t.Error(err.Error())
|
||||
} else {
|
||||
for _, rl := range rls {
|
||||
if _, found := rl.Usage[ru.ID]; !found {
|
||||
t.Error("ResourceUsage not found ")
|
||||
}
|
||||
if rl.TotalUsage != 2 {
|
||||
t.Errorf("Expecting: %+v, received: %+v", 2, rl.TotalUsage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRLsAllocateResource(t *testing.T) {
|
||||
rls.ClearUsage(ru.ID)
|
||||
rls.ClearUsage(ru2.ID)
|
||||
|
||||
rls[0].UsageTTL = time.Duration(20 * time.Second)
|
||||
rls[1].UsageTTL = time.Duration(20 * time.Second)
|
||||
ru.Time = time.Now()
|
||||
ru2.Time = time.Now()
|
||||
|
||||
if alcMessage, err := rls.AllocateResource(ru, false); err != nil {
|
||||
t.Error(err.Error())
|
||||
} else {
|
||||
if alcMessage != "ALLOC" {
|
||||
t.Errorf("Wrong allocation message: %v", alcMessage)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := rls.AllocateResource(ru2, false); err != utils.ErrResourceUnavailable {
|
||||
t.Error("Did not receive " + utils.ErrResourceUnavailable.Error() + " error")
|
||||
}
|
||||
|
||||
rls[0].Limit = 2
|
||||
rls[1].Limit = 4
|
||||
|
||||
if alcMessage, err := rls.AllocateResource(ru, true); err != nil {
|
||||
t.Error(err.Error())
|
||||
} else {
|
||||
if alcMessage != "RL2" {
|
||||
t.Errorf("Wrong allocation message: %v", alcMessage)
|
||||
}
|
||||
}
|
||||
|
||||
if alcMessage, err := rls.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 := rls.AllocateResource(ru2, false); err == nil {
|
||||
t.Error("Duplicate ResourceUsage id should not be allowed")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user