Merge branch 'master' of github.com:cgrates/cgrates

This commit is contained in:
DanB
2017-06-14 19:45:57 +02:00
5 changed files with 510 additions and 374 deletions

View 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)
}
}

View 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",
},
}

View File

@@ -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,,,,,,
1 #Id FilterType FilterFieldName FilterFieldValues ActivationInterval TTL Limit AllocationReply Weight ActionTriggers
2 ResGroup1 *string Account 1001;1002 2014-07-29T15:00:00Z 1s 2 7 10 20
3 ResGroup1 *string_prefix Destination 10;20
4 ResGroup1 *rsr_fields Subject(~^1.*1$);Destination(1002)
5 ResGroup2 *destinations Destination DST_FS 2014-07-29T15:00:00Z 3600s 2 8 SPECIAL_1002 10
6 ResGroup2 *cdr_stats CDRST1:*min_ASR:34;CDRST_1001:*min_ASR:20

View File

@@ -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
}

View File

@@ -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")
}
}