mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
added tests on thresholds processing
This commit is contained in:
committed by
Dan Christian Bogos
parent
f3ca5b0016
commit
fbb625962c
@@ -877,3 +877,36 @@ func TestRSRParsersClone(t *testing.T) {
|
||||
t.Errorf("Expected clone to not modify the cloned")
|
||||
}
|
||||
}
|
||||
func TestRSRParsersValues(t *testing.T) {
|
||||
ts := []struct {
|
||||
name string
|
||||
prsRules string
|
||||
value string
|
||||
parsedValue string
|
||||
}{
|
||||
{name: "TestSearchAndReplaceNumber", prsRules: "~*req.Config.FilterIDs[0]:s/^\\*gt:.*(\\d+)$/${1}/", value: "*gt:~*req.*sum#1:9", parsedValue: "9"},
|
||||
{name: "TestSearchAndReplaceNumberNotMatch", prsRules: "~*req.Destination:s/^\\+41(\\d+)$/${1}/", value: "+415504", parsedValue: "5504"},
|
||||
{name: "TestSearchAndReplaceEmptyReplace", prsRules: "~*req.Destination:s/^\\+41(\\d+)$//", value: "+415504", parsedValue: ""},
|
||||
{name: "TestSearchAndReplaceEmptySearch", prsRules: "~*req.Account:s/^100/${1}/", value: "1001", parsedValue: ""},
|
||||
{name: "TestReplaceInMiddle", prsRules: "~*req:User-Agent:s/^(kamailio)_(\\w+)$/${1}-${2}/", value: "kamailio_agent", parsedValue: "kamailio-agent"},
|
||||
}
|
||||
|
||||
for _, tt := range ts {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
parser, err := NewRSRParser(tt.prsRules)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := parser.Compile(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
val, err := parser.parseValue(tt.value)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if val != tt.parsedValue {
|
||||
t.Errorf("expected %s, received %s", tt.parsedValue, val)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -768,3 +768,122 @@ func TestCheckDefaultTiming(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
func TestActionTimingGetNextStartTime2(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
actionTiming *ActionTiming
|
||||
startTime time.Time
|
||||
expectedTime time.Time
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
name: "MonthlyEstimatedTiming",
|
||||
actionTiming: &ActionTiming{
|
||||
Timing: &RateInterval{
|
||||
Timing: &RITiming{
|
||||
ID: utils.MetaMonthlyEstimated,
|
||||
StartTime: "00:00:00",
|
||||
Years: []int{2024},
|
||||
Months: []time.Month{1, 2, 3},
|
||||
MonthDays: []int{31},
|
||||
},
|
||||
},
|
||||
},
|
||||
startTime: time.Date(2024, 1, 15, 0, 0, 0, 0, time.UTC),
|
||||
expectedTime: time.Date(2024, 1, 31, 0, 0, 0, 0, time.UTC),
|
||||
},
|
||||
{
|
||||
name: "WeekDaysCron",
|
||||
actionTiming: &ActionTiming{
|
||||
Timing: &RateInterval{
|
||||
Timing: &RITiming{
|
||||
StartTime: "09:00:00",
|
||||
Years: []int{2024},
|
||||
Months: []time.Month{1, 2, 3},
|
||||
MonthDays: []int{1, 15},
|
||||
WeekDays: []time.Weekday{time.Monday, time.Wednesday, time.Friday},
|
||||
EndTime: "17:00:00",
|
||||
},
|
||||
},
|
||||
},
|
||||
startTime: time.Date(2024, 1, 1, 10, 0, 0, 0, time.UTC),
|
||||
expectedTime: time.Date(2024, 1, 3, 9, 0, 0, 0, time.UTC),
|
||||
},
|
||||
{
|
||||
name: "YearTransition",
|
||||
actionTiming: &ActionTiming{
|
||||
Timing: &RateInterval{
|
||||
Timing: &RITiming{
|
||||
StartTime: "00:00:00",
|
||||
Years: []int{2024, 2025},
|
||||
Months: []time.Month{12, 1},
|
||||
MonthDays: []int{31, 1},
|
||||
},
|
||||
},
|
||||
},
|
||||
startTime: time.Date(2024, 12, 31, 23, 0, 0, 0, time.UTC),
|
||||
expectedTime: time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
},
|
||||
{
|
||||
name: "LeapYear",
|
||||
actionTiming: &ActionTiming{
|
||||
Timing: &RateInterval{
|
||||
Timing: &RITiming{
|
||||
StartTime: "00:00:00",
|
||||
Years: []int{2024},
|
||||
Months: []time.Month{2},
|
||||
MonthDays: []int{29},
|
||||
},
|
||||
},
|
||||
},
|
||||
startTime: time.Date(2024, 2, 28, 0, 0, 0, 0, time.UTC),
|
||||
expectedTime: time.Date(2024, 2, 29, 0, 0, 0, 0, time.UTC),
|
||||
},
|
||||
{
|
||||
name: "NilTiming",
|
||||
actionTiming: &ActionTiming{
|
||||
Timing: nil,
|
||||
},
|
||||
startTime: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
expectedTime: time.Time{},
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "EmptyTiming",
|
||||
actionTiming: &ActionTiming{
|
||||
Timing: &RateInterval{
|
||||
Timing: nil,
|
||||
},
|
||||
},
|
||||
startTime: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
expectedTime: time.Time{},
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
if tt.actionTiming != nil {
|
||||
tt.actionTiming.ResetStartTimeCache()
|
||||
}
|
||||
|
||||
result := tt.actionTiming.GetNextStartTime(tt.startTime)
|
||||
|
||||
if tt.expectedError {
|
||||
if !result.IsZero() {
|
||||
t.Errorf("Expected zero time for error case, got: %v", result)
|
||||
}
|
||||
return
|
||||
}
|
||||
if !result.Equal(tt.expectedTime) {
|
||||
t.Errorf("GetNextStartTime(%v) = %v; want %v",
|
||||
tt.startTime, result, tt.expectedTime)
|
||||
}
|
||||
cachedResult := tt.actionTiming.GetNextStartTime(tt.startTime)
|
||||
if !cachedResult.Equal(result) {
|
||||
t.Errorf("Cached result differs: got %v, want %v",
|
||||
cachedResult, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3451,3 +3451,101 @@ func TestFilterToSQLQueryValidations(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWeightFromDynamics2(t *testing.T) {
|
||||
cfg := config.NewDefaultCGRConfig()
|
||||
connMgr = NewConnManager(cfg, nil)
|
||||
db, _ := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items)
|
||||
dm := NewDataManager(db, cfg.CacheCfg(), nil)
|
||||
filterS := NewFilterS(cfg, connMgr, dm)
|
||||
testCases := []struct {
|
||||
name string
|
||||
dynamicWeights []*utils.DynamicWeight
|
||||
tenant string
|
||||
event utils.DataProvider
|
||||
expectedWeight float64
|
||||
expectedErr bool
|
||||
}{
|
||||
{
|
||||
name: "EmptyDynamicWeight",
|
||||
dynamicWeights: []*utils.DynamicWeight{},
|
||||
tenant: "cgrates.org",
|
||||
event: utils.MapStorage{},
|
||||
expectedWeight: 0.0,
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "MatchingWeight",
|
||||
dynamicWeights: []*utils.DynamicWeight{
|
||||
{
|
||||
FilterIDs: []string{"*string:~*req.Account:1001"},
|
||||
Weight: 10.0,
|
||||
},
|
||||
},
|
||||
tenant: "cgrates.org",
|
||||
event: utils.MapStorage{
|
||||
"*req": utils.MapStorage{
|
||||
"Account": "1001",
|
||||
},
|
||||
},
|
||||
expectedWeight: 10.0,
|
||||
},
|
||||
{
|
||||
name: "MultipleMatchingWeights",
|
||||
dynamicWeights: []*utils.DynamicWeight{
|
||||
{
|
||||
FilterIDs: []string{"*prefix:~*req.Destination:GER"},
|
||||
Weight: 10.0,
|
||||
},
|
||||
{
|
||||
FilterIDs: []string{"*string:~*opts.subsys:*sessions"},
|
||||
Weight: 5.0,
|
||||
},
|
||||
},
|
||||
tenant: "cgrates.org",
|
||||
event: utils.MapStorage{
|
||||
"*req": utils.MapStorage{
|
||||
"Destination": "GER_0055",
|
||||
},
|
||||
"*opts": utils.MapStorage{
|
||||
"*subsys": "*sessions",
|
||||
},
|
||||
},
|
||||
expectedWeight: 10.0,
|
||||
},
|
||||
{
|
||||
name: "NoMatchingWeightFilterErr",
|
||||
dynamicWeights: []*utils.DynamicWeight{
|
||||
{
|
||||
FilterIDs: []string{"FLTR1"},
|
||||
Weight: 10.0,
|
||||
},
|
||||
},
|
||||
tenant: "cgrates.org",
|
||||
event: utils.MapStorage{
|
||||
"*req": utils.MapStorage{
|
||||
"Account": "1001",
|
||||
},
|
||||
},
|
||||
expectedErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
weight, err := WeightFromDynamics(tc.dynamicWeights, filterS, tc.tenant, tc.event)
|
||||
if tc.expectedErr {
|
||||
if err == nil {
|
||||
t.Error("Expected error but got none")
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if weight != tc.expectedWeight {
|
||||
t.Errorf("Expected weight %v, got %v", tc.expectedWeight, weight)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -2163,3 +2164,77 @@ func TestThresholdsStoreThresholdCacheSetErr(t *testing.T) {
|
||||
|
||||
utils.Logger.SetLogLevel(0)
|
||||
}
|
||||
|
||||
func TestThresholdProcessEvent(t *testing.T) {
|
||||
cfg := config.NewDefaultCGRConfig()
|
||||
cfg.ThresholdSCfg().IndexedSelects = false
|
||||
db, _ := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items)
|
||||
dm := NewDataManager(db, cfg.CacheCfg(), nil)
|
||||
fS := NewFilterS(cfg, nil, dm)
|
||||
ths := NewThresholdService(dm, cfg, fS, nil)
|
||||
thps := []*ThresholdProfile{
|
||||
{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "TH1",
|
||||
FilterIDs: []string{"*string:~*req.Account:1001"},
|
||||
MinHits: 3,
|
||||
MaxHits: 2,
|
||||
},
|
||||
{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "TH2",
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
MinHits: 2,
|
||||
MaxHits: 3,
|
||||
}, {
|
||||
Tenant: "cgrates.org",
|
||||
ID: "TH3",
|
||||
FilterIDs: []string{"*string:~*req.Account:1003"},
|
||||
MinHits: 1,
|
||||
MaxHits: -1,
|
||||
},
|
||||
}
|
||||
for _, thP := range thps {
|
||||
if err := ths.dm.SetThresholdProfile(thP, false); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
tts := []struct {
|
||||
name string
|
||||
runs int
|
||||
cgrEvnt map[string]any
|
||||
matchedthIDs []string
|
||||
}{
|
||||
{
|
||||
name: "MinHitsLargerThanMaxHits",
|
||||
runs: 3,
|
||||
cgrEvnt: map[string]any{
|
||||
utils.AccountField: "1001",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "MinHitsLargerThanMaxHits",
|
||||
runs: 4,
|
||||
cgrEvnt: map[string]any{
|
||||
utils.AccountField: "1002",
|
||||
},
|
||||
},
|
||||
{},
|
||||
}
|
||||
for _, tt := range tts {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var thIDs []string
|
||||
for range tt.runs {
|
||||
var err error
|
||||
thIDs, err = ths.processEvent("cgrates.org", &utils.CGREvent{Event: tt.cgrEvnt})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
if !slices.Equal(thIDs, tt.matchedthIDs) {
|
||||
t.Errorf("expected: %v, received: %v", tt.matchedthIDs, thIDs)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -978,3 +978,175 @@ func TestEngineUnitCounterString(t *testing.T) {
|
||||
t.Errorf("Expected JSON: %s, got: %s", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResetCounters(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
initialCounters UnitCounters
|
||||
action *Action
|
||||
expectedCounters UnitCounters
|
||||
}{
|
||||
{
|
||||
name: "ResetAlCountersNilAction",
|
||||
initialCounters: UnitCounters{
|
||||
utils.MetaMonetary: []*UnitCounter{
|
||||
{
|
||||
CounterType: utils.MetaCounterEvent,
|
||||
Counters: CounterFilters{
|
||||
{Value: 100.0, Filter: &BalanceFilter{ID: utils.StringPointer("BAL_MON1")}},
|
||||
{Value: 200.0, Filter: &BalanceFilter{ID: utils.StringPointer("BAL_MON2")}},
|
||||
},
|
||||
},
|
||||
{
|
||||
CounterType: utils.MetaBalance,
|
||||
Counters: CounterFilters{
|
||||
{Value: 50.0, Filter: &BalanceFilter{ID: utils.StringPointer("BAL_MON1")}},
|
||||
},
|
||||
},
|
||||
},
|
||||
utils.MetaVoice: []*UnitCounter{
|
||||
{
|
||||
CounterType: utils.MetaCounterEvent,
|
||||
Counters: CounterFilters{
|
||||
{Value: 150.0, Filter: &BalanceFilter{ID: utils.StringPointer("VOICE1")}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
action: nil,
|
||||
expectedCounters: UnitCounters{
|
||||
utils.MetaMonetary: []*UnitCounter{
|
||||
{
|
||||
CounterType: utils.MetaCounterEvent,
|
||||
Counters: CounterFilters{
|
||||
{Value: 0.0, Filter: &BalanceFilter{ID: utils.StringPointer("BAL_MON1")}},
|
||||
{Value: 0.0, Filter: &BalanceFilter{ID: utils.StringPointer("BAL_MON2")}},
|
||||
},
|
||||
},
|
||||
{
|
||||
CounterType: utils.MetaBalance,
|
||||
Counters: CounterFilters{
|
||||
{Value: 0.0, Filter: &BalanceFilter{ID: utils.StringPointer("BAL_MON1")}},
|
||||
},
|
||||
},
|
||||
},
|
||||
utils.MetaVoice: []*UnitCounter{
|
||||
{
|
||||
CounterType: utils.MetaCounterEvent,
|
||||
Counters: CounterFilters{
|
||||
{Value: 0.0, Filter: &BalanceFilter{ID: utils.StringPointer("VOICE1")}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ResetCountersMonetaryBalanceType",
|
||||
initialCounters: UnitCounters{
|
||||
"*monetary": []*UnitCounter{
|
||||
{
|
||||
CounterType: utils.MetaBalance,
|
||||
Counters: CounterFilters{
|
||||
{Value: 100.0, Filter: &BalanceFilter{ID: utils.StringPointer("MON1")}},
|
||||
{Value: 200.0, Filter: &BalanceFilter{ID: utils.StringPointer("MON2")}},
|
||||
},
|
||||
},
|
||||
},
|
||||
"*data": []*UnitCounter{
|
||||
{
|
||||
CounterType: utils.MetaCounterEvent,
|
||||
Counters: CounterFilters{
|
||||
{Value: 50.0, Filter: &BalanceFilter{ID: utils.StringPointer("MB_BAL")}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
action: &Action{
|
||||
Balance: &BalanceFilter{Type: utils.StringPointer("*monetary")},
|
||||
},
|
||||
expectedCounters: UnitCounters{
|
||||
"*monetary": []*UnitCounter{
|
||||
{
|
||||
CounterType: utils.MetaBalance,
|
||||
Counters: CounterFilters{
|
||||
{Value: 100.0, Filter: &BalanceFilter{ID: utils.StringPointer("MON1")}},
|
||||
{Value: 200.0, Filter: &BalanceFilter{ID: utils.StringPointer("MON2")}},
|
||||
},
|
||||
},
|
||||
},
|
||||
"*data": []*UnitCounter{
|
||||
{
|
||||
CounterType: utils.MetaCounterEvent,
|
||||
Counters: CounterFilters{
|
||||
{Value: 50.0, Filter: &BalanceFilter{ID: utils.StringPointer("MB_BAL")}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ResetSpecificBalanceType",
|
||||
initialCounters: UnitCounters{
|
||||
"*monetary": []*UnitCounter{
|
||||
{
|
||||
CounterType: utils.MetaBalance,
|
||||
Counters: CounterFilters{
|
||||
{Value: 100.0, Filter: &BalanceFilter{ID: utils.StringPointer("MON1"), Type: utils.StringPointer("*monetary")}},
|
||||
{Value: 200.0, Filter: &BalanceFilter{ID: utils.StringPointer("MON2"), Type: utils.StringPointer("*monetary")}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
action: &Action{
|
||||
Balance: &BalanceFilter{ID: utils.StringPointer("MON1"), Type: utils.StringPointer("*monetary")},
|
||||
},
|
||||
expectedCounters: UnitCounters{
|
||||
"*monetary": []*UnitCounter{
|
||||
{
|
||||
CounterType: utils.MetaBalance,
|
||||
Counters: CounterFilters{
|
||||
{Value: 0.0, Filter: &BalanceFilter{ID: utils.StringPointer("MON1"), Type: utils.StringPointer("*monetary")}},
|
||||
{Value: 200.0, Filter: &BalanceFilter{ID: utils.StringPointer("MON2"), Type: utils.StringPointer("*monetary")}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ActionBalanceTypeNotExist",
|
||||
initialCounters: UnitCounters{
|
||||
"*data": []*UnitCounter{
|
||||
{
|
||||
CounterType: utils.MetaCounterEvent,
|
||||
Counters: CounterFilters{
|
||||
{Value: 150.0, Filter: &BalanceFilter{ID: utils.StringPointer("DATA1"), Type: utils.StringPointer("*data")}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
action: &Action{
|
||||
Balance: &BalanceFilter{Type: utils.StringPointer("*monetary")},
|
||||
},
|
||||
expectedCounters: UnitCounters{
|
||||
"*data": []*UnitCounter{
|
||||
{
|
||||
CounterType: utils.MetaCounterEvent,
|
||||
Counters: CounterFilters{
|
||||
{Value: 150.0, Filter: &BalanceFilter{ID: utils.StringPointer("DATA1"), Type: utils.StringPointer("*data")}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cloneInitialCounters := tt.initialCounters.Clone()
|
||||
cloneInitialCounters.resetCounters(tt.action)
|
||||
if !reflect.DeepEqual(cloneInitialCounters, tt.expectedCounters) {
|
||||
t.Errorf("mismatch after resetCounters.\nExpected:\n%s\nGot:\n%s",
|
||||
utils.ToJSON(tt.expectedCounters), utils.ToJSON(cloneInitialCounters))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
178
general_tests/threshold_ees_it_test.go
Normal file
178
general_tests/threshold_ees_it_test.go
Normal file
@@ -0,0 +1,178 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
/*
|
||||
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
|
||||
Copyright (C) ITsysCOM GmbH
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU 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 general_tests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cgrates/birpc/context"
|
||||
"github.com/cgrates/cgrates/engine"
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
)
|
||||
|
||||
func TestThresholdEventEEs(t *testing.T) {
|
||||
var dbConfig engine.DBCfg
|
||||
switch *utils.DBType {
|
||||
case utils.MetaMySQL:
|
||||
case utils.MetaMongo:
|
||||
dbConfig = engine.MongoDBCfg
|
||||
case utils.MetaPostgres, utils.MetaInternal:
|
||||
t.SkipNow()
|
||||
default:
|
||||
t.Fatal("unsupported dbtype value")
|
||||
}
|
||||
content := `{
|
||||
"general": {
|
||||
"log_level": 7,
|
||||
},
|
||||
"apiers": {
|
||||
"enabled": true
|
||||
},
|
||||
"cdrs":{
|
||||
"enabled": true,
|
||||
"stats_conns": ["*localhost"],
|
||||
},
|
||||
"stats": {
|
||||
"enabled": true,
|
||||
"indexed_selects":false,
|
||||
"thresholds_conns": ["*localhost"],
|
||||
},
|
||||
"thresholds": {
|
||||
"enabled": true,
|
||||
"indexed_selects":false,
|
||||
"ees_conns": ["*localhost"]
|
||||
},
|
||||
"ees": {
|
||||
"enabled": true,
|
||||
"exporters": [
|
||||
{
|
||||
"id": "exporter1",
|
||||
"type": "*virt",
|
||||
"attempts": 1,
|
||||
"synchronous": true,
|
||||
"fields":[
|
||||
{"tag": "Filter1", "path": "*uch.Filter1", "type": "*variable", "value": "~*req.Config.FilterIDs[0]"},
|
||||
{"tag": "Filter2", "path": "*uch.Filter2", "type": "*variable", "value": "~*req.Config.FilterIDs[1]"},
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
}`
|
||||
|
||||
csvFiles := map[string]string{
|
||||
utils.StatsCsv: `#Tenant[0],Id[1],FilterIDs[2],ActivationInterval[3],QueueLength[4],TTL[5],MinItems[6],Metrics[7],MetricFilterIDs[8],Stored[9],Blocker[10],Weight[11],ThresholdIDs[12]
|
||||
cgrates.org,SQ_1,*string:~*req.Account:1001,,,-1,,*sum#1,,false,,,TH1`,
|
||||
utils.ThresholdsCsv: `#Tenant[0],Id[1],FilterIDs[2],ActivationInterval[3],MaxHits[4],MinHits[5],MinSleep[6],Blocker[7],Weight[8],ActionIDs[9],Async[10],EeIDs[11]
|
||||
cgrates.org,TH1,*string:~*req.StatID:SQ_1;*eq:~*req.*sum#1:2,,-1,1,0,false,,ACT_LOG,false,exporter1`,
|
||||
utils.ActionsCsv: `#ActionsId[0],Action[1],ExtraParameters[2],Filter[3],BalanceId[4],BalanceType[5],Categories[6],DestinationIds[7],RatingSubject[8],SharedGroup[9],ExpiryTime[10],TimingIds[11],Units[12],BalanceWeight[13],BalanceBlocker[14],BalanceDisabled[15],Weight[16]
|
||||
ACT_LOG,*log,,,,,,,,,,,,,,,0`,
|
||||
}
|
||||
|
||||
ng := engine.TestEngine{
|
||||
ConfigJSON: content,
|
||||
DBCfg: dbConfig,
|
||||
LogBuffer: bytes.NewBuffer(nil),
|
||||
TpFiles: csvFiles,
|
||||
}
|
||||
client, _ := ng.Run(t)
|
||||
|
||||
t.Run("CDREventStatsToThreshold", func(t *testing.T) {
|
||||
// event from StatS to ThresholdS returns NOT_FOUND but should be ignored
|
||||
var reply string
|
||||
if err := client.Call(context.Background(),
|
||||
utils.CDRsV1ProcessEvent,
|
||||
&engine.ArgV1ProcessEvent{
|
||||
CGREvent: utils.CGREvent{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "TestEv1",
|
||||
Event: map[string]any{
|
||||
utils.ToR: utils.MetaVoice,
|
||||
utils.OriginID: "Origin2",
|
||||
utils.RequestType: utils.MetaPrepaid,
|
||||
utils.AccountField: "1001",
|
||||
utils.Subject: "1001",
|
||||
utils.Destination: "1002",
|
||||
utils.Usage: time.Minute,
|
||||
},
|
||||
},
|
||||
}, &reply); err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
} else if reply != utils.OK {
|
||||
t.Error("Unexpected reply received: ", reply)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
t.Run("ThresholdsToEEsEvent", func(t *testing.T) {
|
||||
// it matches the threshold and passes the event to EEs without any errors
|
||||
var reply string
|
||||
if err := client.Call(context.Background(),
|
||||
utils.CDRsV1ProcessEvent,
|
||||
&engine.ArgV1ProcessEvent{
|
||||
CGREvent: utils.CGREvent{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "TestEv1",
|
||||
Event: map[string]any{
|
||||
utils.ToR: utils.MetaVoice,
|
||||
utils.OriginID: "Origin1",
|
||||
utils.RequestType: utils.MetaPrepaid,
|
||||
utils.AccountField: "1001",
|
||||
utils.Subject: "1001",
|
||||
utils.Destination: "1002",
|
||||
utils.Usage: time.Minute,
|
||||
},
|
||||
},
|
||||
}, &reply); err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
} else if reply != utils.OK {
|
||||
t.Error("Unexpected reply received: ", reply)
|
||||
}
|
||||
})
|
||||
t.Run("CheckExporterIDs", func(t *testing.T) {
|
||||
// filters in event always should be in ascending order
|
||||
var filter1 any
|
||||
if err := client.Call(context.Background(), utils.CacheSv1GetItem, &utils.ArgsGetCacheItemWithAPIOpts{
|
||||
Tenant: "cgrates.org",
|
||||
ArgsGetCacheItem: utils.ArgsGetCacheItem{
|
||||
CacheID: utils.CacheUCH,
|
||||
ItemID: "Filter1",
|
||||
},
|
||||
}, &filter1); err != nil {
|
||||
t.Error(err)
|
||||
} else if filter1 != "*eq:~*req.*sum#1:2" {
|
||||
t.Errorf("expected %v, received %v", "*eq:~*req.*sum#1:2", filter1)
|
||||
}
|
||||
var filter2 any
|
||||
if err := client.Call(context.Background(), utils.CacheSv1GetItem, &utils.ArgsGetCacheItemWithAPIOpts{
|
||||
Tenant: "cgrates.org",
|
||||
ArgsGetCacheItem: utils.ArgsGetCacheItem{
|
||||
CacheID: utils.CacheUCH,
|
||||
ItemID: "Filter2",
|
||||
},
|
||||
}, &filter2); err != nil {
|
||||
t.Error(err)
|
||||
} else if filter2 != "*string:~*req.StatID:SQ_1" {
|
||||
t.Errorf("expected %v, received %v", "*string:~*req.StatID:SQ_1", filter2)
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user