mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
3967 lines
108 KiB
Go
3967 lines
108 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 Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
*/
|
|
package engine
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"reflect"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/cgrates/birpc"
|
|
"github.com/cgrates/birpc/context"
|
|
"github.com/cgrates/cgrates/config"
|
|
"github.com/cgrates/cgrates/utils"
|
|
)
|
|
|
|
func mustNewStatSum(minItems uint64, fieldName string, filterIDs []string) StatMetric {
|
|
sum, err := NewStatSum(minItems, fieldName, filterIDs)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return sum
|
|
}
|
|
|
|
var (
|
|
testStatsPrfs = []*StatQueueProfile{
|
|
{
|
|
Tenant: "cgrates.org",
|
|
ID: "StatQueueProfile1",
|
|
FilterIDs: []string{"FLTR_STATS_1", "*ai:*now:2014-07-14T14:25:00Z"},
|
|
QueueLength: 10,
|
|
TTL: 10 * time.Second,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: "*sum#~*req.Usage",
|
|
},
|
|
},
|
|
ThresholdIDs: []string{},
|
|
Blockers: utils.DynamicBlockers{{Blocker: false}},
|
|
Stored: true,
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 20,
|
|
},
|
|
},
|
|
MinItems: 1,
|
|
},
|
|
{
|
|
Tenant: "cgrates.org",
|
|
ID: "StatQueueProfile2",
|
|
FilterIDs: []string{"FLTR_STATS_2", "*ai:*now:2014-07-14T14:25:00Z"},
|
|
QueueLength: 10,
|
|
TTL: 10 * time.Second,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: "*sum#~*req.Usage",
|
|
},
|
|
},
|
|
ThresholdIDs: []string{},
|
|
Blockers: utils.DynamicBlockers{{Blocker: false}},
|
|
Stored: true,
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 20,
|
|
},
|
|
},
|
|
MinItems: 1,
|
|
},
|
|
{
|
|
Tenant: "cgrates.org",
|
|
ID: "StatQueueProfilePrefix",
|
|
FilterIDs: []string{"FLTR_STATS_3", "*ai:*now:2014-07-14T14:25:00Z"},
|
|
QueueLength: 10,
|
|
TTL: 10 * time.Second,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: "*sum#~*req.Usage",
|
|
},
|
|
},
|
|
ThresholdIDs: []string{},
|
|
Blockers: utils.DynamicBlockers{{Blocker: false}},
|
|
Stored: true,
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 20,
|
|
},
|
|
},
|
|
MinItems: 1,
|
|
},
|
|
}
|
|
testStatsQ = []*StatQueue{
|
|
{
|
|
Tenant: "cgrates.org",
|
|
ID: "StatQueueProfile1",
|
|
sqPrfl: testStatsPrfs[0],
|
|
SQMetrics: map[string]StatMetric{
|
|
utils.MetaSum: mustNewStatSum(1, "~*req.Usage", nil),
|
|
},
|
|
},
|
|
{
|
|
Tenant: "cgrates.org",
|
|
ID: "StatQueueProfile2",
|
|
sqPrfl: testStatsPrfs[1],
|
|
SQMetrics: map[string]StatMetric{
|
|
utils.MetaSum: mustNewStatSum(1, "~*req.Usage", nil),
|
|
},
|
|
},
|
|
{
|
|
Tenant: "cgrates.org",
|
|
ID: "StatQueueProfilePrefix",
|
|
sqPrfl: testStatsPrfs[2],
|
|
SQMetrics: map[string]StatMetric{
|
|
utils.MetaSum: mustNewStatSum(1, "~*req.Usage", nil),
|
|
},
|
|
},
|
|
}
|
|
testStatsArgs = []*utils.CGREvent{
|
|
{
|
|
Tenant: "cgrates.org",
|
|
ID: "event1",
|
|
Event: map[string]any{
|
|
"Stats": "StatQueueProfile1",
|
|
utils.AnswerTime: time.Date(2014, 7, 14, 14, 30, 0, 0, time.UTC),
|
|
"UsageInterval": "1s",
|
|
"PddInterval": "1s",
|
|
"Weight": "9.0",
|
|
utils.Usage: 135 * time.Second,
|
|
utils.Cost: 123.0,
|
|
},
|
|
},
|
|
{
|
|
Tenant: "cgrates.org",
|
|
ID: "event2",
|
|
Event: map[string]any{
|
|
"Stats": "StatQueueProfile2",
|
|
utils.AnswerTime: time.Date(2014, 7, 14, 14, 30, 0, 0, time.UTC),
|
|
"UsageInterval": "1s",
|
|
"PddInterval": "1s",
|
|
"Weight": "15.0",
|
|
utils.Usage: 45 * time.Second,
|
|
},
|
|
},
|
|
{
|
|
Tenant: "cgrates.org",
|
|
ID: "event3",
|
|
Event: map[string]any{
|
|
"Stats": "StatQueueProfilePrefix",
|
|
utils.Usage: 30 * time.Second,
|
|
},
|
|
},
|
|
}
|
|
)
|
|
|
|
func prepareStatsData(t *testing.T, dm *DataManager) {
|
|
if err := dm.SetFilter(context.Background(), &Filter{
|
|
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
|
|
ID: "FLTR_STATS_1",
|
|
Rules: []*FilterRule{
|
|
{
|
|
Type: utils.MetaString,
|
|
Element: "~*req.Stats",
|
|
Values: []string{"StatQueueProfile1"},
|
|
},
|
|
{
|
|
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"},
|
|
},
|
|
},
|
|
}, true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := dm.SetFilter(context.Background(), &Filter{
|
|
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
|
|
ID: "FLTR_STATS_2",
|
|
Rules: []*FilterRule{
|
|
{
|
|
Type: utils.MetaString,
|
|
Element: "~*req.Stats",
|
|
Values: []string{"StatQueueProfile2"},
|
|
},
|
|
{
|
|
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"},
|
|
},
|
|
},
|
|
}, true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := dm.SetFilter(context.Background(), &Filter{
|
|
Tenant: config.CgrConfig().GeneralCfg().DefaultTenant,
|
|
ID: "FLTR_STATS_3",
|
|
Rules: []*FilterRule{
|
|
{
|
|
Type: utils.MetaPrefix,
|
|
Element: "~*req.Stats",
|
|
Values: []string{"StatQueueProfilePrefix"},
|
|
},
|
|
},
|
|
}, true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, statQueueProfile := range testStatsPrfs {
|
|
dm.SetStatQueueProfile(context.Background(), statQueueProfile, true)
|
|
}
|
|
for _, statQueue := range testStatsQ {
|
|
statSum, err := NewStatMetric("*sum#~*req.Usage", uint64(statQueue.sqPrfl.MinItems), []string{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
statQueue.SQMetrics["*sum#~*req.Usage"] = statSum
|
|
dm.SetStatQueue(context.Background(), statQueue)
|
|
}
|
|
//Test each statQueueProfile from cache
|
|
for _, sqp := range testStatsPrfs {
|
|
if tempStat, err := dm.GetStatQueueProfile(context.Background(), sqp.Tenant,
|
|
sqp.ID, true, false, utils.NonTransactional); err != nil {
|
|
t.Errorf("Error: %+v", err)
|
|
} else if !reflect.DeepEqual(sqp, tempStat) {
|
|
t.Errorf("Expecting: %+v, received: %+v", sqp, tempStat)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNewStatService(t *testing.T) {
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
fltrS := &FilterS{dm: dm, cfg: cfg}
|
|
sSrv := &StatS{
|
|
dm: dm,
|
|
fltrS: fltrS,
|
|
cfg: cfg,
|
|
storedStatQueues: make(utils.StringSet),
|
|
}
|
|
result := NewStatService(dm, cfg, fltrS, nil)
|
|
if !reflect.DeepEqual(sSrv.dm, result.dm) {
|
|
t.Errorf("\nExpecting <%+v>,\n Received <%+v>", sSrv.dm, result.dm)
|
|
}
|
|
if !reflect.DeepEqual(sSrv.fltrS, result.fltrS) {
|
|
t.Errorf("\nExpecting <%+v>,\n Received <%+v>", sSrv.fltrS, result.fltrS)
|
|
}
|
|
if !reflect.DeepEqual(sSrv.cfg, result.cfg) {
|
|
t.Errorf("\nExpecting <%+v>,\n Received <%+v>", sSrv.cfg, result.cfg)
|
|
}
|
|
if !reflect.DeepEqual(sSrv.storedStatQueues, sSrv.storedStatQueues) {
|
|
t.Errorf("\nExpecting <%+v>,\n Received <%+v>", sSrv.storedStatQueues, sSrv.storedStatQueues)
|
|
}
|
|
}
|
|
|
|
func TestStatQueuesMatchingStatQueuesForEvent(t *testing.T) {
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dmSTS := NewDataManager(dbCM, cfg, nil)
|
|
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
cfg.StatSCfg().StringIndexedFields = nil
|
|
cfg.StatSCfg().PrefixIndexedFields = nil
|
|
statService := NewStatService(dmSTS, cfg,
|
|
&FilterS{dm: dmSTS, cfg: cfg}, nil)
|
|
prepareStatsData(t, dmSTS)
|
|
msq, err := statService.matchingStatQueuesForEvent(context.TODO(), testStatsArgs[0].Tenant, nil,
|
|
testStatsArgs[0].AsDataProvider(), false)
|
|
if err != nil {
|
|
t.Errorf("Error: %+v", err)
|
|
}
|
|
unlockStatQueues(msq)
|
|
if !reflect.DeepEqual(testStatsQ[0].Tenant, msq[0].Tenant) {
|
|
t.Errorf("Expecting: %+v, received: %+v", testStatsQ[0].Tenant, msq[0].Tenant)
|
|
} else if !reflect.DeepEqual(testStatsQ[0].ID, msq[0].ID) {
|
|
t.Errorf("Expecting: %+v, received: %+v", testStatsQ[0].ID, msq[0].ID)
|
|
} else if !reflect.DeepEqual(testStatsQ[0].sqPrfl, msq[0].sqPrfl) {
|
|
t.Errorf("Expecting: %+v, received: %+v", testStatsQ[0].sqPrfl, msq[0].sqPrfl)
|
|
}
|
|
msq, err = statService.matchingStatQueuesForEvent(context.TODO(), testStatsArgs[1].Tenant, nil,
|
|
testStatsArgs[1].AsDataProvider(), false)
|
|
if err != nil {
|
|
t.Errorf("Error: %+v", err)
|
|
}
|
|
unlockStatQueues(msq)
|
|
if !reflect.DeepEqual(testStatsQ[1].Tenant, msq[0].Tenant) {
|
|
t.Errorf("Expecting: %+v, received: %+v", testStatsQ[1].Tenant, msq[0].Tenant)
|
|
} else if !reflect.DeepEqual(testStatsQ[1].ID, msq[0].ID) {
|
|
t.Errorf("Expecting: %+v, received: %+v", testStatsQ[1].ID, msq[0].ID)
|
|
} else if !reflect.DeepEqual(testStatsQ[1].sqPrfl, msq[0].sqPrfl) {
|
|
t.Errorf("Expecting: %+v, received: %+v", testStatsQ[1].sqPrfl, msq[0].sqPrfl)
|
|
}
|
|
msq, err = statService.matchingStatQueuesForEvent(context.TODO(), testStatsArgs[2].Tenant, nil,
|
|
testStatsArgs[2].AsDataProvider(), false)
|
|
if err != nil {
|
|
t.Errorf("Error: %+v", err)
|
|
}
|
|
unlockStatQueues(msq)
|
|
if !reflect.DeepEqual(testStatsQ[2].Tenant, msq[0].Tenant) {
|
|
t.Errorf("Expecting: %+v, received: %+v", testStatsQ[2].Tenant, msq[0].Tenant)
|
|
} else if !reflect.DeepEqual(testStatsQ[2].ID, msq[0].ID) {
|
|
t.Errorf("Expecting: %+v, received: %+v", testStatsQ[2].ID, msq[0].ID)
|
|
} else if !reflect.DeepEqual(testStatsQ[2].sqPrfl, msq[0].sqPrfl) {
|
|
t.Errorf("Expecting: %+v, received: %+v", testStatsQ[2].sqPrfl, msq[0].sqPrfl)
|
|
}
|
|
}
|
|
|
|
func TestStatQueuesProcessEvent(t *testing.T) {
|
|
Cache.Clear(nil)
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dmSTS := NewDataManager(dbCM, cfg, nil)
|
|
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
cfg.StatSCfg().StringIndexedFields = nil
|
|
cfg.StatSCfg().PrefixIndexedFields = nil
|
|
statService := NewStatService(dmSTS, cfg,
|
|
&FilterS{dm: dmSTS, cfg: cfg}, nil)
|
|
|
|
prepareStatsData(t, dmSTS)
|
|
|
|
stq := map[string]string{}
|
|
reply := []string{}
|
|
expected := []string{"StatQueueProfile1"}
|
|
err := statService.V1ProcessEvent(context.TODO(), testStatsArgs[0], &reply)
|
|
if err != nil {
|
|
t.Errorf("Error: %+v", err)
|
|
} else if !reflect.DeepEqual(reply, expected) {
|
|
t.Errorf("Expecting: %+v, received: %+v", expected, reply)
|
|
}
|
|
err = statService.V1GetQueueStringMetrics(context.TODO(), &utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{Tenant: testStatsQ[0].Tenant, ID: testStatsQ[0].ID}}, &stq)
|
|
if err != nil {
|
|
t.Errorf("Error: %+v", err)
|
|
}
|
|
|
|
expected = []string{"StatQueueProfile2"}
|
|
err = statService.V1ProcessEvent(context.TODO(), testStatsArgs[1], &reply)
|
|
if err != nil {
|
|
t.Errorf("Error: %+v", err)
|
|
} else if !reflect.DeepEqual(reply, expected) {
|
|
t.Errorf("Expecting: %+v, received: %+v", expected, reply)
|
|
}
|
|
err = statService.V1GetQueueStringMetrics(context.TODO(), &utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{Tenant: testStatsQ[1].Tenant, ID: testStatsQ[1].ID}}, &stq)
|
|
if err != nil {
|
|
t.Errorf("Error: %+v", err)
|
|
}
|
|
|
|
expected = []string{"StatQueueProfilePrefix"}
|
|
err = statService.V1ProcessEvent(context.TODO(), testStatsArgs[2], &reply)
|
|
if err != nil {
|
|
t.Errorf("Error: %+v", err)
|
|
} else if !reflect.DeepEqual(reply, expected) {
|
|
t.Errorf("Expecting: %+v, received: %+v", expected, reply)
|
|
}
|
|
err = statService.V1GetQueueStringMetrics(context.TODO(), &utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{Tenant: testStatsQ[2].Tenant, ID: testStatsQ[2].ID}}, &stq)
|
|
if err != nil {
|
|
t.Errorf("Error: %+v", err)
|
|
}
|
|
}
|
|
|
|
func TestStatQueuesMatchWithIndexFalse(t *testing.T) {
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dmSTS := NewDataManager(dbCM, cfg, nil)
|
|
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
cfg.StatSCfg().StringIndexedFields = nil
|
|
cfg.StatSCfg().PrefixIndexedFields = nil
|
|
statService := NewStatService(dmSTS, cfg,
|
|
&FilterS{dm: dmSTS, cfg: cfg}, nil)
|
|
prepareStatsData(t, dmSTS)
|
|
|
|
statService.cfg.StatSCfg().IndexedSelects = false
|
|
msq, err := statService.matchingStatQueuesForEvent(context.TODO(), testStatsArgs[0].Tenant, nil,
|
|
testStatsArgs[0].AsDataProvider(), false)
|
|
if err != nil {
|
|
t.Errorf("Error: %+v", err)
|
|
}
|
|
unlockStatQueues(msq)
|
|
if !reflect.DeepEqual(testStatsQ[0].Tenant, msq[0].Tenant) {
|
|
t.Errorf("Expecting: %+v, received: %+v", testStatsQ[0].Tenant, msq[0].Tenant)
|
|
} else if !reflect.DeepEqual(testStatsQ[0].ID, msq[0].ID) {
|
|
t.Errorf("Expecting: %+v, received: %+v", testStatsQ[0].ID, msq[0].ID)
|
|
} else if !reflect.DeepEqual(testStatsQ[0].sqPrfl, msq[0].sqPrfl) {
|
|
t.Errorf("Expecting: %+v, received: %+v", testStatsQ[0].sqPrfl, msq[0].sqPrfl)
|
|
}
|
|
msq, err = statService.matchingStatQueuesForEvent(context.TODO(), testStatsArgs[1].Tenant, nil,
|
|
testStatsArgs[1].AsDataProvider(), false)
|
|
if err != nil {
|
|
t.Errorf("Error: %+v", err)
|
|
}
|
|
unlockStatQueues(msq)
|
|
if !reflect.DeepEqual(testStatsQ[1].Tenant, msq[0].Tenant) {
|
|
t.Errorf("Expecting: %+v, received: %+v", testStatsQ[1].Tenant, msq[0].Tenant)
|
|
} else if !reflect.DeepEqual(testStatsQ[1].ID, msq[0].ID) {
|
|
t.Errorf("Expecting: %+v, received: %+v", testStatsQ[1].ID, msq[0].ID)
|
|
} else if !reflect.DeepEqual(testStatsQ[1].sqPrfl, msq[0].sqPrfl) {
|
|
t.Errorf("Expecting: %+v, received: %+v", testStatsQ[1].sqPrfl, msq[0].sqPrfl)
|
|
}
|
|
msq, err = statService.matchingStatQueuesForEvent(context.TODO(), testStatsArgs[2].Tenant, nil,
|
|
testStatsArgs[2].AsDataProvider(), false)
|
|
if err != nil {
|
|
t.Errorf("Error: %+v", err)
|
|
}
|
|
unlockStatQueues(msq)
|
|
if !reflect.DeepEqual(testStatsQ[2].Tenant, msq[0].Tenant) {
|
|
t.Errorf("Expecting: %+v, received: %+v", testStatsQ[2].Tenant, msq[0].Tenant)
|
|
} else if !reflect.DeepEqual(testStatsQ[2].ID, msq[0].ID) {
|
|
t.Errorf("Expecting: %+v, received: %+v", testStatsQ[2].ID, msq[0].ID)
|
|
} else if !reflect.DeepEqual(testStatsQ[2].sqPrfl, msq[0].sqPrfl) {
|
|
t.Errorf("Expecting: %+v, received: %+v", testStatsQ[2].sqPrfl, msq[0].sqPrfl)
|
|
}
|
|
}
|
|
|
|
func TestStatQueuesV1ProcessEvent(t *testing.T) {
|
|
Cache.Clear(nil)
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dmSTS := NewDataManager(dbCM, cfg, nil)
|
|
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
cfg.StatSCfg().StringIndexedFields = nil
|
|
cfg.StatSCfg().PrefixIndexedFields = nil
|
|
statService := NewStatService(dmSTS, cfg,
|
|
&FilterS{dm: dmSTS, cfg: cfg}, nil)
|
|
prepareStatsData(t, dmSTS)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "StatQueueProfile3",
|
|
FilterIDs: []string{"FLTR_STATS_1", "*ai:~*req.AnswerTime:2014-07-14T14:25:00Z"},
|
|
QueueLength: 10,
|
|
TTL: 10 * time.Second,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: "*sum#~*req.Usage",
|
|
},
|
|
},
|
|
ThresholdIDs: []string{},
|
|
Stored: true,
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 20,
|
|
},
|
|
},
|
|
MinItems: 1,
|
|
}
|
|
statSum, err := NewStatMetric("*sum#~*req.Usage", uint64(sqPrf.MinItems), []string{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
sq := &StatQueue{Tenant: "cgrates.org", ID: "StatQueueProfile3", sqPrfl: sqPrf, SQMetrics: map[string]StatMetric{"*sum#~*req.Usage": statSum}}
|
|
if err := dmSTS.SetStatQueueProfile(context.TODO(), sqPrf, true); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := dmSTS.SetStatQueue(context.TODO(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if tempStat, err := dmSTS.GetStatQueueProfile(context.TODO(), sqPrf.Tenant,
|
|
sqPrf.ID, true, false, utils.NonTransactional); err != nil {
|
|
t.Errorf("Error: %+v", err)
|
|
} else if !reflect.DeepEqual(sqPrf, tempStat) {
|
|
t.Errorf("Expecting: %+v, received: %+v", sqPrf, tempStat)
|
|
}
|
|
ev := testStatsArgs[0].Clone()
|
|
ev.APIOpts = make(map[string]any)
|
|
ev.APIOpts[utils.OptsStatsProfileIDs] = []string{"StatQueueProfile1", "StatQueueProfile2", "StatQueueProfile3"}
|
|
reply := []string{}
|
|
expected := []string{"StatQueueProfile1", "StatQueueProfile3"}
|
|
expectedRev := []string{"StatQueueProfile3", "StatQueueProfile1"}
|
|
if err := statService.V1ProcessEvent(context.TODO(), ev, &reply); err != nil {
|
|
t.Errorf("Error: %+v", err)
|
|
} else if !reflect.DeepEqual(reply, expected) && !reflect.DeepEqual(reply, expectedRev) {
|
|
t.Errorf("Expecting: %+v, received: %+v", expected, reply)
|
|
}
|
|
}
|
|
|
|
func TestStatQueuesUpdateStatQueue(t *testing.T) {
|
|
cfg := config.NewDefaultCGRConfig()
|
|
idb, err := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: idb}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
sqp := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "THUP1",
|
|
Stored: true,
|
|
QueueLength: 1,
|
|
Metrics: []*MetricWithFilters{{MetricID: utils.MetaTCC}},
|
|
}
|
|
sqm := NewTCC(0, utils.EmptyString, nil)
|
|
if err := sqm.AddEvent("ev1", utils.MapStorage{utils.MetaOpts: utils.MapStorage{utils.MetaCost: 10}}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
sq := &StatQueue{
|
|
Tenant: sqp.Tenant,
|
|
ID: sqp.ID,
|
|
SQItems: []SQItem{{EventID: "ev1"}},
|
|
SQMetrics: map[string]StatMetric{utils.MetaTCC: sqm, utils.MetaTCD: sqm},
|
|
}
|
|
sqm2 := NewTCC(0, utils.EmptyString, nil)
|
|
|
|
expTh := &StatQueue{
|
|
Tenant: sqp.Tenant,
|
|
ID: sqp.ID,
|
|
SQMetrics: map[string]StatMetric{utils.MetaTCC: sqm2},
|
|
}
|
|
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqp, true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if th, err := dm.GetStatQueue(context.Background(), sqp.Tenant, sqp.ID, false, false, utils.NonTransactional); err != nil {
|
|
t.Fatal(err)
|
|
} else if !reflect.DeepEqual(expTh, th) {
|
|
t.Errorf("Expected: %s, received: %s", utils.ToJSON(expTh), utils.ToJSON(th))
|
|
}
|
|
|
|
if err := dm.RemoveStatQueue(context.Background(), sqp.Tenant, sqp.ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqp, true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if th, err := dm.GetStatQueue(context.Background(), sqp.Tenant, sqp.ID, false, false, utils.NonTransactional); err != nil {
|
|
t.Fatal(err)
|
|
} else if !reflect.DeepEqual(expTh, th) {
|
|
t.Errorf("Expected: %s, received: %s", utils.ToJSON(expTh), utils.ToJSON(th))
|
|
}
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
sqp = &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "THUP1",
|
|
Stored: true,
|
|
QueueLength: 1,
|
|
Metrics: []*MetricWithFilters{{MetricID: utils.MetaTCC, FilterIDs: []string{"*string:~*req.Account:1001"}}},
|
|
}
|
|
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqp, true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
sqm3 := NewTCC(0, utils.EmptyString, []string{"*string:~*req.Account:1001"})
|
|
|
|
expTh = &StatQueue{
|
|
Tenant: sqp.Tenant,
|
|
ID: sqp.ID,
|
|
SQItems: []SQItem{{EventID: "ev1"}},
|
|
SQMetrics: map[string]StatMetric{utils.MetaTCC: sqm3},
|
|
}
|
|
if th, err := dm.GetStatQueue(context.Background(), sqp.Tenant, sqp.ID, false, false, utils.NonTransactional); err != nil {
|
|
t.Fatal(err)
|
|
} else if !reflect.DeepEqual(expTh, th) {
|
|
t.Errorf("Expected: %s, received: %s", utils.ToJSON(expTh), utils.ToJSON(th))
|
|
}
|
|
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
sqm2 = NewTCC(5, utils.EmptyString, nil)
|
|
|
|
expTh = &StatQueue{
|
|
Tenant: sqp.Tenant,
|
|
ID: sqp.ID,
|
|
SQMetrics: map[string]StatMetric{utils.MetaTCC: sqm2},
|
|
}
|
|
delete(sq.SQMetrics, utils.MetaTCD)
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
sqp = &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "THUP1",
|
|
Stored: true,
|
|
QueueLength: 1,
|
|
Metrics: []*MetricWithFilters{{MetricID: utils.MetaTCC}},
|
|
MinItems: 5,
|
|
}
|
|
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqp, true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if th, err := dm.GetStatQueue(context.Background(), sqp.Tenant, sqp.ID, false, false, utils.NonTransactional); err != nil {
|
|
t.Fatal(err)
|
|
} else if !reflect.DeepEqual(expTh, th) {
|
|
t.Errorf("Expected: %s, received: %s", utils.ToJSON(expTh), utils.ToJSON(th))
|
|
}
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
sqp = &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "THUP1",
|
|
Stored: true,
|
|
QueueLength: 1,
|
|
Metrics: []*MetricWithFilters{{MetricID: utils.MetaTCC}},
|
|
MinItems: 5,
|
|
TTL: 10,
|
|
}
|
|
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqp, true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if th, err := dm.GetStatQueue(context.Background(), sqp.Tenant, sqp.ID, false, false, utils.NonTransactional); err != nil {
|
|
t.Fatal(err)
|
|
} else if !reflect.DeepEqual(expTh, th) {
|
|
t.Errorf("Expected: %s, received: %s", utils.ToJSON(expTh), utils.ToJSON(th))
|
|
}
|
|
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
sqp = &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "THUP1",
|
|
Stored: true,
|
|
QueueLength: 10,
|
|
Metrics: []*MetricWithFilters{{MetricID: utils.MetaTCC}},
|
|
TTL: 10,
|
|
MinItems: 5,
|
|
}
|
|
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqp, true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if th, err := dm.GetStatQueue(context.Background(), sqp.Tenant, sqp.ID, false, false, utils.NonTransactional); err != nil {
|
|
t.Fatal(err)
|
|
} else if !reflect.DeepEqual(expTh, th) {
|
|
t.Errorf("Expected: %s, received: %s", utils.ToJSON(expTh), utils.ToJSON(th))
|
|
}
|
|
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
sqp = &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "THUP1",
|
|
Stored: false,
|
|
QueueLength: 10,
|
|
Metrics: []*MetricWithFilters{{MetricID: utils.MetaTCC}},
|
|
TTL: 10,
|
|
MinItems: 5,
|
|
}
|
|
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqp, true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if th, err := dm.GetStatQueue(context.Background(), sqp.Tenant, sqp.ID, false, false, utils.NonTransactional); err != nil {
|
|
t.Fatal(err)
|
|
} else if !reflect.DeepEqual(expTh, th) {
|
|
t.Errorf("Expected: %s, received: %s", utils.ToJSON(expTh), utils.ToJSON(th))
|
|
}
|
|
|
|
if err := dm.RemoveStatQueueProfile(context.Background(), sqp.Tenant, sqp.ID, true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := dm.GetStatQueue(context.Background(), sqp.Tenant, sqp.ID, false, false, utils.NonTransactional); err != utils.ErrNotFound {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueMatchingStatQueuesForEventLocks(t *testing.T) {
|
|
cfg := config.NewDefaultCGRConfig()
|
|
tmp := Cache
|
|
defer func() { Cache = tmp }()
|
|
Cache = NewCacheS(cfg, nil, nil, nil)
|
|
db, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: db}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
cfg.StatSCfg().StringIndexedFields = nil
|
|
cfg.StatSCfg().PrefixIndexedFields = nil
|
|
rS := NewStatService(dm, cfg,
|
|
&FilterS{dm: dm, cfg: cfg}, nil)
|
|
|
|
prfs := make([]*StatQueueProfile, 0)
|
|
ids := utils.StringSet{}
|
|
for i := 0; i < 10; i++ {
|
|
rPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: fmt.Sprintf("STS%d", i),
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 20.00,
|
|
},
|
|
},
|
|
ThresholdIDs: []string{utils.MetaNone},
|
|
QueueLength: 1,
|
|
Stored: true,
|
|
}
|
|
dm.SetStatQueueProfile(context.Background(), rPrf, true)
|
|
prfs = append(prfs, rPrf)
|
|
ids.Add(rPrf.ID)
|
|
}
|
|
dm.RemoveStatQueue(context.Background(), "cgrates.org", "STS1")
|
|
_, err := rS.matchingStatQueuesForEvent(context.Background(), "cgrates.org", ids.AsSlice(), utils.MapStorage{}, false)
|
|
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.ID)
|
|
}
|
|
if rPrf.ID == "STS1" {
|
|
continue
|
|
}
|
|
if r, err := dm.GetStatQueue(context.Background(), rPrf.Tenant, rPrf.ID, true, false, utils.NonTransactional); err != nil {
|
|
t.Errorf("error %s for <%s>", err, rPrf.ID)
|
|
} else if r.isLocked() {
|
|
t.Fatalf("Expected StatQueue to not be locked %q", rPrf.ID)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
func TestStatQueueMatchingStatQueuesForEventLocks2(t *testing.T) {
|
|
cfg := config.NewDefaultCGRConfig()
|
|
tmp := Cache
|
|
defer func() { Cache = tmp }()
|
|
Cache = NewCacheS(cfg, nil, nil, nil)
|
|
db, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: db}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
cfg.StatSCfg().StringIndexedFields = nil
|
|
cfg.StatSCfg().PrefixIndexedFields = nil
|
|
rS := NewStatService(dm, cfg,
|
|
&FilterS{dm: dm, cfg: cfg}, nil)
|
|
|
|
prfs := make([]*StatQueueProfile, 0)
|
|
ids := utils.StringSet{}
|
|
for i := 0; i < 10; i++ {
|
|
rPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: fmt.Sprintf("STS%d", i),
|
|
QueueLength: 1,
|
|
Stored: true,
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 20.00,
|
|
},
|
|
},
|
|
ThresholdIDs: []string{utils.MetaNone},
|
|
}
|
|
dm.SetStatQueueProfile(context.Background(), rPrf, true)
|
|
prfs = append(prfs, rPrf)
|
|
ids.Add(rPrf.ID)
|
|
}
|
|
rPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "STS20",
|
|
FilterIDs: []string{"FLTR_RES_201"},
|
|
QueueLength: 1,
|
|
Stored: true,
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 20.00,
|
|
},
|
|
},
|
|
ThresholdIDs: []string{utils.MetaNone},
|
|
}
|
|
err := db.SetStatQueueProfileDrv(context.Background(), rPrf)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
prfs = append(prfs, rPrf)
|
|
ids.Add(rPrf.ID)
|
|
_, err = rS.matchingStatQueuesForEvent(context.Background(), "cgrates.org", ids.AsSlice(), utils.MapStorage{}, false)
|
|
expErr := utils.ErrPrefixNotFound(rPrf.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.ID)
|
|
}
|
|
if rPrf.ID == "STS20" {
|
|
continue
|
|
}
|
|
if r, err := dm.GetStatQueue(context.Background(), rPrf.Tenant, rPrf.ID, true, false, utils.NonTransactional); err != nil {
|
|
t.Errorf("error %s for <%s>", err, rPrf.ID)
|
|
} else if r.isLocked() {
|
|
t.Fatalf("Expected StatQueue to not be locked %q", rPrf.ID)
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
func TestStatQueueMatchingStatQueuesForEventLocksBlocker(t *testing.T) {
|
|
cfg := config.NewDefaultCGRConfig()
|
|
tmp := Cache
|
|
defer func() { Cache = tmp }()
|
|
Cache = NewCacheS(cfg, nil, nil,nil)
|
|
db , _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: db}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, config.CgrConfig().CacheCfg(), nil)
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
cfg.StatSCfg().StringIndexedFields = nil
|
|
cfg.StatSCfg().PrefixIndexedFields = nil
|
|
rS := NewStatService(dm, cfg,
|
|
&FilterS{dm: dm, cfg: cfg}, nil)
|
|
|
|
prfs := make([]*StatQueueProfile, 0)
|
|
ids := utils.StringSet{}
|
|
for i := 0; i < 10; i++ {
|
|
rPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: fmt.Sprintf("STS%d", i),
|
|
QueueLength: 1,
|
|
Stored: true,
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: float64(10 - i),
|
|
},
|
|
},
|
|
Blockers: utils.Blockers{{Blocker: i == 4}},
|
|
ThresholdIDs: []string{utils.MetaNone},
|
|
}
|
|
dm.SetStatQueueProfile(context.Background(), rPrf, true)
|
|
prfs = append(prfs, rPrf)
|
|
ids.Add(rPrf.ID)
|
|
}
|
|
mres, err := rS.matchingStatQueuesForEvent(context.Background(), "cgrates.org", ids.AsSlice(), utils.MapStorage{}, false)
|
|
if err != nil {
|
|
t.Errorf("Error: %+v", err)
|
|
}
|
|
defer mres.unlock()
|
|
if len(mres) != 10 {
|
|
t.Fatalf("Expected 10 StatQueues, but received %v", len(mres))
|
|
}
|
|
for _, rPrf := range prfs[5:] {
|
|
if rPrf.isLocked() {
|
|
t.Errorf("Expected profile to not be locked %q", rPrf.ID)
|
|
}
|
|
if r, err := dm.GetStatQueue(context.Background(), rPrf.Tenant, rPrf.ID, true, false, utils.NonTransactional); err != nil {
|
|
t.Errorf("error %s for <%s>", err, rPrf.ID)
|
|
} else if r.isLocked() {
|
|
t.Fatalf("Expected StatQueue to not be locked %q", rPrf.ID)
|
|
}
|
|
}
|
|
for _, rPrf := range prfs[:5] {
|
|
if !rPrf.isLocked() {
|
|
t.Errorf("Expected profile to be locked %q", rPrf.ID)
|
|
}
|
|
if r, err := dm.GetStatQueue(context.Background(), rPrf.Tenant, rPrf.ID, true, false, utils.NonTransactional); err != nil {
|
|
t.Errorf("error %s for <%s>", err, rPrf.ID)
|
|
} else if !r.isLocked() {
|
|
t.Fatalf("Expected StatQueue to be locked %q", rPrf.ID)
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
func TestStatQueueMatchingStatQueuesForEventLocks3(t *testing.T) {
|
|
cfg := config.NewDefaultCGRConfig()
|
|
prfs := make([]*StatQueueProfile, 0)
|
|
tmp := Cache
|
|
defer func() { Cache = tmp }()
|
|
Cache = NewCacheS(cfg, nil, nil, nil)
|
|
db := &DataDBMock{
|
|
GetStatQueueProfileDrvF: func(ctx *context.Context, tnt, id string) (*StatQueueProfile, error) {
|
|
if id == "STS1" {
|
|
return nil, utils.ErrNotImplemented
|
|
}
|
|
rPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: id,
|
|
QueueLength: 1,
|
|
Stored: true,
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 20.00,
|
|
},
|
|
},
|
|
ThresholdIDs: []string{utils.MetaNone},
|
|
}
|
|
Cache.Set(ctx, utils.CacheStatQueues, rPrf.TenantID(), &StatQueue{
|
|
Tenant: rPrf.Tenant,
|
|
ID: rPrf.ID,
|
|
SQMetrics: make(map[string]StatMetric),
|
|
}, nil, true, utils.NonTransactional)
|
|
prfs = append(prfs, rPrf)
|
|
return rPrf, nil
|
|
},
|
|
}
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: db}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
cfg.StatSCfg().StringIndexedFields = nil
|
|
cfg.StatSCfg().PrefixIndexedFields = nil
|
|
rS := NewStatService(dm, cfg,
|
|
&FilterS{dm: dm, cfg: cfg}, nil)
|
|
|
|
ids := utils.StringSet{}
|
|
for i := 0; i < 10; i++ {
|
|
ids.Add(fmt.Sprintf("STS%d", i))
|
|
}
|
|
_, err := rS.matchingStatQueuesForEvent(context.Background(), "cgrates.org", ids.AsSlice(), utils.MapStorage{}, false)
|
|
if err != utils.ErrNotImplemented {
|
|
t.Fatalf("Error: %+v", err)
|
|
}
|
|
for _, rPrf := range prfs {
|
|
if rPrf.isLocked() {
|
|
t.Fatalf("Expected profile to not be locked %q", rPrf.ID)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
func TestStatQueueReload(t *testing.T) {
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().StoreInterval = 5 * time.Millisecond
|
|
sS := &StatS{
|
|
stopBackup: make(chan struct{}),
|
|
loopStopped: make(chan struct{}, 1),
|
|
cfg: cfg,
|
|
}
|
|
sS.loopStopped <- struct{}{}
|
|
sS.Reload(context.Background())
|
|
close(sS.stopBackup)
|
|
select {
|
|
case <-sS.loopStopped:
|
|
case <-time.After(time.Second):
|
|
t.Error("timed out waiting for loop to stop")
|
|
}
|
|
}
|
|
|
|
func TestStatQueueStartLoop(t *testing.T) {
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().StoreInterval = -1
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := &StatS{
|
|
dm: dm,
|
|
fltrS: filterS,
|
|
stopBackup: make(chan struct{}),
|
|
loopStopped: make(chan struct{}, 1),
|
|
cfg: cfg,
|
|
}
|
|
|
|
sS.StartLoop(context.Background())
|
|
time.Sleep(10 * time.Millisecond)
|
|
|
|
if len(sS.loopStopped) != 1 {
|
|
t.Errorf("expected loopStopped field to have only one element, received: <%+v>", len(sS.loopStopped))
|
|
}
|
|
}
|
|
|
|
func TestStatQueueStoreStatsOK(t *testing.T) {
|
|
tmp := Cache
|
|
defer func() {
|
|
Cache = tmp
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
sS := NewStatService(dm, cfg, nil, nil)
|
|
|
|
exp := &StatQueue{
|
|
dirty: utils.BoolPointer(true),
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQMetrics: make(map[string]StatMetric),
|
|
}
|
|
Cache.SetWithoutReplicate(utils.CacheStatQueues, "cgrates.org:SQ1", exp, nil, true,
|
|
utils.NonTransactional)
|
|
sS.storedStatQueues.Add("cgrates.org:SQ1")
|
|
sS.storeStats(context.Background())
|
|
|
|
if rcv, err := sS.dm.GetStatQueue(context.Background(), "cgrates.org", "SQ1", true, false,
|
|
utils.NonTransactional); err != nil {
|
|
t.Error(err)
|
|
} else if !reflect.DeepEqual(rcv, exp) {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>",
|
|
utils.ToJSON(exp), utils.ToJSON(rcv))
|
|
}
|
|
|
|
Cache.Remove(context.Background(), utils.CacheStatQueues, "cgrates.org:SQ1", true, utils.NonTransactional)
|
|
}
|
|
|
|
func TestStatQueueStoreStatsStoreSQErr(t *testing.T) {
|
|
tmp := Cache
|
|
tmpLogger := utils.Logger
|
|
defer func() {
|
|
Cache = tmp
|
|
utils.Logger = tmpLogger
|
|
}()
|
|
|
|
var buf bytes.Buffer
|
|
utils.Logger = utils.NewStdLoggerWithWriter(&buf, "", 4)
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
sS := NewStatService(nil, cfg, nil, nil)
|
|
|
|
value := &StatQueue{
|
|
dirty: utils.BoolPointer(true),
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQMetrics: make(map[string]StatMetric),
|
|
}
|
|
|
|
Cache.SetWithoutReplicate(utils.CacheStatQueues, "SQ1", value, nil, true,
|
|
utils.NonTransactional)
|
|
sS.storedStatQueues.Add("SQ1")
|
|
exp := utils.StringSet{
|
|
"SQ1": struct{}{},
|
|
}
|
|
expLog := `[WARNING] <StatS> failed saving StatQueue with ID: cgrates.org:SQ1, error: NO_DATABASE_CONNECTION`
|
|
sS.storeStats(context.Background())
|
|
|
|
if !reflect.DeepEqual(sS.storedStatQueues, exp) {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", exp, sS.storedStatQueues)
|
|
}
|
|
if rcvLog := buf.String(); !strings.Contains(rcvLog, expLog) {
|
|
t.Errorf("expected log <%+v>\n to be in included in: <%+v>", expLog, rcvLog)
|
|
}
|
|
|
|
Cache.Remove(context.Background(), utils.CacheStatQueues, "SQ1", true, utils.NonTransactional)
|
|
}
|
|
|
|
func TestStatQueueStoreStatsCacheGetErr(t *testing.T) {
|
|
tmp := Cache
|
|
tmpLogger := utils.Logger
|
|
defer func() {
|
|
utils.Logger = tmpLogger
|
|
Cache = tmp
|
|
}()
|
|
var buf bytes.Buffer
|
|
utils.Logger = utils.NewStdLoggerWithWriter(&buf, "", 4)
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
sS := NewStatService(dm, cfg, nil, nil)
|
|
|
|
value := &StatQueue{
|
|
dirty: utils.BoolPointer(true),
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQMetrics: make(map[string]StatMetric),
|
|
}
|
|
|
|
Cache.SetWithoutReplicate(utils.CacheStatQueues, "SQ2", value, nil, true,
|
|
utils.NonTransactional)
|
|
sS.storedStatQueues.Add("SQ1")
|
|
expLog := `[WARNING] <StatS> failed retrieving from cache stat queue with ID: SQ1`
|
|
sS.storeStats(context.Background())
|
|
|
|
if rcvLog := buf.String(); !strings.Contains(rcvLog, expLog) {
|
|
t.Errorf("expected <%+v> \nto be included in: <%+v>", expLog, rcvLog)
|
|
}
|
|
|
|
Cache.Remove(context.Background(), utils.CacheStatQueues, "SQ2", true, utils.NonTransactional)
|
|
}
|
|
|
|
func TestStatQueueStoreStatQueueCacheSetErr(t *testing.T) {
|
|
tmpLogger := utils.Logger
|
|
defer func() {
|
|
utils.Logger = tmpLogger
|
|
}()
|
|
var buf bytes.Buffer
|
|
utils.Logger = utils.NewStdLoggerWithWriter(&buf, "", 4)
|
|
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
tmpCM := connMgr
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
connMgr = tmpCM
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.CacheCfg().ReplicationConns = []string{"test"}
|
|
cfg.CacheCfg().Partitions[utils.CacheStatQueues].Replicate = true
|
|
cfg.RPCConns()["test"] = &config.RPCConn{Conns: []*config.RemoteHost{{}}}
|
|
config.SetCgrConfig(cfg)
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
connMgr = NewConnManager(cfg)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, connMgr)
|
|
|
|
sq := &StatQueue{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQMetrics: make(map[string]StatMetric),
|
|
dirty: utils.BoolPointer(true),
|
|
}
|
|
Cache.SetWithoutReplicate(utils.CacheStatQueues, sq.TenantID(), sq, nil, true, utils.NonTransactional)
|
|
expLog := `[WARNING] <StatS> failed caching StatQueue with ID: cgrates.org:SQ1, error: DISCONNECTED`
|
|
if err := sS.StoreStatQueue(context.Background(), sq); err == nil ||
|
|
err.Error() != utils.ErrDisconnected.Error() {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrDisconnected, err)
|
|
} else if rcv := buf.String(); !strings.Contains(rcv, expLog) {
|
|
t.Errorf("expected log <%+v> to be included in <%+v>", expLog, rcv)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueStoreThresholdNilDirtyField(t *testing.T) {
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
sS := NewStatService(dm, cfg, nil, nil)
|
|
|
|
sq := &StatQueue{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQMetrics: make(map[string]StatMetric),
|
|
}
|
|
|
|
if err := sS.StoreStatQueue(context.Background(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueProcessEventOK(t *testing.T) {
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
stat, err := NewStatMetric("*tcd", uint64(sqPrf.MinItems), []string{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
sq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
},
|
|
},
|
|
SQMetrics: map[string]StatMetric{
|
|
utils.MetaTCD: stat,
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqPrf, true); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
args := &utils.CGREvent{
|
|
Tenant: "cgrates.org",
|
|
ID: "SqProcessEvent",
|
|
Event: map[string]any{
|
|
utils.AccountField: "1001",
|
|
},
|
|
APIOpts: map[string]any{
|
|
utils.MetaUsage: "10s",
|
|
utils.OptsStatsProfileIDs: []string{"SQ1"},
|
|
},
|
|
}
|
|
|
|
expIDs := []string{"SQ1"}
|
|
if rcvIDs, err := sS.processEvent(context.Background(), args.Tenant, args); err != nil {
|
|
t.Error(err)
|
|
} else if !reflect.DeepEqual(rcvIDs, expIDs) {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", expIDs, rcvIDs)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueProcessEventProcessThPartExec(t *testing.T) {
|
|
Cache.Clear(nil)
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
}
|
|
sq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
},
|
|
},
|
|
SQMetrics: make(map[string]StatMetric),
|
|
}
|
|
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqPrf, true); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
args := &utils.CGREvent{
|
|
Tenant: "cgrates.org",
|
|
ID: "SqProcessEvent",
|
|
Event: map[string]any{
|
|
utils.AccountField: "1002",
|
|
},
|
|
}
|
|
|
|
if _, err := sS.processEvent(context.Background(), args.Tenant, args); err == nil ||
|
|
err != utils.ErrNotFound {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueV1ProcessEventMissingArgs(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
sq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
},
|
|
},
|
|
SQMetrics: map[string]StatMetric{
|
|
utils.MetaTCD: &StatTCD{},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqPrf, true); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
var reply []string
|
|
experr := `MANDATORY_IE_MISSING: [CGREvent]`
|
|
if err := sS.V1ProcessEvent(context.Background(), nil, &reply); err == nil ||
|
|
err.Error() != experr {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
|
|
}
|
|
|
|
args := &utils.CGREvent{
|
|
Tenant: "cgrates.org",
|
|
Event: map[string]any{
|
|
utils.AccountField: "1001",
|
|
},
|
|
APIOpts: map[string]any{
|
|
utils.OptsStatsProfileIDs: []string{"SQ1"},
|
|
},
|
|
}
|
|
|
|
experr = `MANDATORY_IE_MISSING: [ID]`
|
|
if err := sS.V1ProcessEvent(context.Background(), args, &reply); err == nil ||
|
|
err.Error() != experr {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
|
|
}
|
|
|
|
args = &utils.CGREvent{
|
|
Tenant: "cgrates.org",
|
|
ID: "SqProcessEvent",
|
|
Event: nil,
|
|
APIOpts: map[string]any{
|
|
utils.OptsStatsProfileIDs: []string{"SQ1"},
|
|
},
|
|
}
|
|
|
|
experr = `MANDATORY_IE_MISSING: [Event]`
|
|
if err := sS.V1ProcessEvent(context.Background(), args, &reply); err == nil ||
|
|
err.Error() != experr {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueV1GetQueueIDsOK(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
sq1 := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
},
|
|
},
|
|
SQMetrics: map[string]StatMetric{
|
|
utils.MetaTCD: &StatTCD{},
|
|
},
|
|
}
|
|
|
|
sq2 := &StatQueue{
|
|
sqPrfl: nil,
|
|
Tenant: "testTenant",
|
|
ID: "SQ2",
|
|
SQMetrics: make(map[string]StatMetric),
|
|
}
|
|
sq3 := &StatQueue{
|
|
sqPrfl: nil,
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ3",
|
|
SQMetrics: make(map[string]StatMetric),
|
|
}
|
|
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqPrf, true); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := dm.SetStatQueue(context.Background(), sq1); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := dm.SetStatQueue(context.Background(), sq2); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := dm.SetStatQueue(context.Background(), sq3); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
expIDs := []string{"SQ1", "SQ3"}
|
|
var qIDs []string
|
|
if err := sS.V1GetQueueIDs(context.Background(), &utils.TenantWithAPIOpts{}, &qIDs); err != nil {
|
|
t.Error(err)
|
|
} else {
|
|
sort.Strings(qIDs)
|
|
if !reflect.DeepEqual(qIDs, expIDs) {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", expIDs, qIDs)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestStatQueueV1GetQueueIDsGetKeysForPrefixErr(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data := &DataDBMock{}
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
var qIDs []string
|
|
if err := sS.V1GetQueueIDs(context.Background(), &utils.TenantWithAPIOpts{}, &qIDs); err == nil ||
|
|
err.Error() != utils.ErrNotImplemented.Error() {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotImplemented, err)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueV1GetStatQueueOK(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
sq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
},
|
|
},
|
|
SQMetrics: map[string]StatMetric{
|
|
utils.MetaTCD: &StatTCD{},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqPrf, true); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
var reply StatQueue
|
|
if err := sS.V1GetStatQueue(context.Background(), &utils.TenantIDWithAPIOpts{
|
|
TenantID: &utils.TenantID{
|
|
ID: "SQ1",
|
|
},
|
|
}, &reply); err != nil {
|
|
t.Error(err)
|
|
} else if !reflect.DeepEqual(reply, *sq) {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>",
|
|
utils.ToJSON(*sq), utils.ToJSON(reply))
|
|
}
|
|
}
|
|
|
|
func TestStatQueueV1GetStatQueueNotFound(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
var reply StatQueue
|
|
if err := sS.V1GetStatQueue(context.Background(), &utils.TenantIDWithAPIOpts{
|
|
TenantID: &utils.TenantID{
|
|
ID: "SQ1",
|
|
},
|
|
}, &reply); err == nil || err != utils.ErrNotFound {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueV1GetStatQueueMissingArgs(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
sq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
},
|
|
},
|
|
SQMetrics: map[string]StatMetric{
|
|
utils.MetaTCD: &StatTCD{},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqPrf, true); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
experr := `MANDATORY_IE_MISSING: [ID]`
|
|
var reply StatQueue
|
|
if err := sS.V1GetStatQueue(context.Background(), &utils.TenantIDWithAPIOpts{
|
|
TenantID: &utils.TenantID{},
|
|
}, &reply); err == nil || err.Error() != experr {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueV1GetStatQueuesForEventOK(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf1 := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqPrf1, true); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
sqPrf2 := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ2",
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 20,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: false}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaACD,
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqPrf2, true); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
args := &utils.CGREvent{
|
|
ID: "TestGetStatQueuesForEvent",
|
|
Event: map[string]any{
|
|
utils.AccountField: "1001",
|
|
},
|
|
}
|
|
|
|
exp := []string{"SQ1", "SQ2"}
|
|
var reply []string
|
|
if err := sS.V1GetStatQueuesForEvent(context.Background(), args, &reply); err != nil {
|
|
t.Error(err)
|
|
} else {
|
|
sort.Strings(reply)
|
|
if !reflect.DeepEqual(reply, exp) {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", exp, reply)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestStatQueueV1GetStatQueuesForEventNotFoundErr(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqPrf, true); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
args := &utils.CGREvent{
|
|
ID: "TestGetStatQueuesForEvent",
|
|
Event: map[string]any{
|
|
utils.AccountField: "1002",
|
|
},
|
|
}
|
|
|
|
var reply []string
|
|
if err := sS.V1GetStatQueuesForEvent(context.Background(), args, &reply); err == nil ||
|
|
err != utils.ErrNotFound {
|
|
t.Errorf("expected: <%+v>, received: <%+v>", utils.ErrNotFound, err)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueV1GetStatQueuesForEventMissingArgs(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqPrf, true); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
experr := `MANDATORY_IE_MISSING: [CGREvent]`
|
|
var reply []string
|
|
if err := sS.V1GetStatQueuesForEvent(context.Background(), nil, &reply); err == nil ||
|
|
err.Error() != experr {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
|
|
}
|
|
|
|
args := &utils.CGREvent{
|
|
Tenant: "cgrates.org",
|
|
ID: utils.EmptyString,
|
|
Event: map[string]any{
|
|
utils.AccountField: "1001",
|
|
},
|
|
}
|
|
|
|
experr = `MANDATORY_IE_MISSING: [ID]`
|
|
if err := sS.V1GetStatQueuesForEvent(context.Background(), args, &reply); err == nil ||
|
|
err.Error() != experr {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
|
|
}
|
|
|
|
args = &utils.CGREvent{
|
|
Tenant: "cgrates.org",
|
|
ID: "TestGetStatQueuesForEvent",
|
|
Event: nil,
|
|
}
|
|
|
|
experr = `MANDATORY_IE_MISSING: [Event]`
|
|
if err := sS.V1GetStatQueuesForEvent(context.Background(), args, &reply); err == nil ||
|
|
err.Error() != experr {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueV1ResetStatQueueOK(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
sq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
dirty: utils.BoolPointer(false),
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
},
|
|
},
|
|
SQMetrics: map[string]StatMetric{
|
|
utils.MetaTCD: &StatTCD{
|
|
Metric: &Metric{
|
|
Value: utils.NewDecimal(int64(time.Hour), 0),
|
|
Count: 1,
|
|
Events: make(map[string]*DecimalWithCompress),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
Cache.Clear(nil)
|
|
expStored := utils.StringSet{
|
|
"cgrates.org:SQ1": {},
|
|
}
|
|
expSq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
dirty: utils.BoolPointer(true),
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{},
|
|
SQMetrics: map[string]StatMetric{
|
|
utils.MetaTCD: &StatTCD{
|
|
Metric: &Metric{
|
|
Value: utils.NewDecimal(0, 0),
|
|
Events: make(map[string]*DecimalWithCompress),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
var reply string
|
|
|
|
if err := sS.V1ResetStatQueue(context.Background(), &utils.TenantIDWithAPIOpts{
|
|
TenantID: &utils.TenantID{
|
|
ID: "SQ1",
|
|
},
|
|
}, &reply); err != nil {
|
|
t.Error(err)
|
|
} else if reply != utils.OK {
|
|
t.Errorf("Unexpected reply returned: <%q>", reply)
|
|
}
|
|
if nSq, err := sS.dm.GetStatQueue(context.Background(), "cgrates.org", "SQ1", true, true, utils.NonTransactional); err != nil {
|
|
t.Fatal(err)
|
|
} else if !reflect.DeepEqual(nSq, expSq) {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ToJSON(expSq), utils.ToJSON(nSq))
|
|
} else if !reflect.DeepEqual(sS.storedStatQueues, expStored) {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", expStored, sS.storedStatQueues)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueV1ResetStatQueueNotFoundErr(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
sq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
dirty: utils.BoolPointer(false),
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
},
|
|
},
|
|
SQMetrics: map[string]StatMetric{
|
|
utils.MetaTCD: &StatTCD{
|
|
Metric: &Metric{
|
|
Value: utils.NewDecimal(int64(time.Hour), 0),
|
|
Count: 1,
|
|
Events: make(map[string]*DecimalWithCompress),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
var reply string
|
|
if err := sS.V1ResetStatQueue(context.Background(), &utils.TenantIDWithAPIOpts{
|
|
TenantID: &utils.TenantID{
|
|
ID: "SQ2",
|
|
},
|
|
}, &reply); err == nil || err != utils.ErrNotFound {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueV1ResetStatQueueMissingArgs(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
sq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
dirty: utils.BoolPointer(false),
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
},
|
|
},
|
|
SQMetrics: map[string]StatMetric{
|
|
utils.MetaTCD: &StatTCD{
|
|
Metric: &Metric{
|
|
Value: utils.NewDecimal(int64(time.Hour), 0),
|
|
Count: 1,
|
|
Events: make(map[string]*DecimalWithCompress),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
experr := `MANDATORY_IE_MISSING: [ID]`
|
|
var reply string
|
|
if err := sS.V1ResetStatQueue(context.Background(), &utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{}}, &reply); err == nil ||
|
|
err.Error() != experr {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueV1ResetStatQueueUnsupportedMetricType(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: "testMetricType",
|
|
},
|
|
},
|
|
}
|
|
sq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
dirty: utils.BoolPointer(false),
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
},
|
|
},
|
|
SQMetrics: map[string]StatMetric{
|
|
"testMetricType": &StatTCD{
|
|
Metric: &Metric{
|
|
Value: utils.NewDecimal(int64(time.Hour), 0),
|
|
Count: 1,
|
|
Events: make(map[string]*DecimalWithCompress),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
experr := `unsupported metric type <testMetricType>`
|
|
var reply string
|
|
|
|
if err := sS.V1ResetStatQueue(context.Background(), &utils.TenantIDWithAPIOpts{
|
|
TenantID: &utils.TenantID{
|
|
ID: "SQ1",
|
|
},
|
|
}, &reply); err == nil || err.Error() != experr {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueProcessThresholdsOKNoThIDs(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
tmpCM := connMgr
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
connMgr = tmpCM
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().ThresholdSConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaThresholds)}
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: "testMetricType",
|
|
},
|
|
},
|
|
}
|
|
sq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
dirty: utils.BoolPointer(false),
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
},
|
|
},
|
|
SQMetrics: map[string]StatMetric{
|
|
"testMetricType": &StatTCD{
|
|
Metric: &Metric{
|
|
Value: utils.NewDecimal(int64(time.Hour), 0),
|
|
Count: 1,
|
|
Events: make(map[string]*DecimalWithCompress),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
sQs := []*StatQueue{sq}
|
|
|
|
if err := sS.processThresholds(context.Background(), sQs, nil); err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueProcessThresholdsOK(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
tmpCM := connMgr
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
connMgr = tmpCM
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().ThresholdSConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaThresholds)}
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, 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.StatUpdate,
|
|
utils.StatID: "SQ1",
|
|
"testMetricType": utils.NewDecimal(int64(time.Hour), 0),
|
|
},
|
|
APIOpts: map[string]any{
|
|
utils.MetaEventType: utils.StatUpdate,
|
|
utils.OptsThresholdsProfileIDs: []string{"TH1"},
|
|
},
|
|
}
|
|
if !reflect.DeepEqual(exp, args) {
|
|
t.Errorf("Expected: <%+v>, \nreceived: <%+v>",
|
|
utils.ToJSON(exp), utils.ToJSON(args))
|
|
return fmt.Errorf("expected: <%+v>, \nreceived: <%+v>",
|
|
utils.ToJSON(exp), utils.ToJSON(args))
|
|
}
|
|
return nil
|
|
},
|
|
},
|
|
}
|
|
rpcInternal := make(chan birpc.ClientConnector, 1)
|
|
rpcInternal <- ccM
|
|
connMgr = NewConnManager(cfg)
|
|
connMgr.AddInternalConn(utils.ConcatenatedKey(utils.MetaInternal, utils.MetaThresholds), utils.ThresholdSv1, rpcInternal)
|
|
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, connMgr)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"TH1"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: "testMetricType",
|
|
},
|
|
},
|
|
}
|
|
sq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
dirty: utils.BoolPointer(false),
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
},
|
|
},
|
|
SQMetrics: map[string]StatMetric{
|
|
"testMetricType": &StatTCD{
|
|
Metric: &Metric{
|
|
Value: utils.NewDecimal(int64(time.Hour), 0),
|
|
Count: 1,
|
|
Events: map[string]*DecimalWithCompress{
|
|
"SqProcessEvent": {},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if err := sS.processThresholds(context.Background(), []*StatQueue{sq}, nil); err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueProcessThresholdsErrPartExec(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
tmpCM := connMgr
|
|
tmpLogger := utils.Logger
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
connMgr = tmpCM
|
|
utils.Logger = tmpLogger
|
|
}()
|
|
var buf bytes.Buffer
|
|
utils.Logger = utils.NewStdLoggerWithWriter(&buf, "", 4)
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().ThresholdSConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaThresholds)}
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, 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 {
|
|
return utils.ErrExists
|
|
},
|
|
},
|
|
}
|
|
rpcInternal := make(chan birpc.ClientConnector, 1)
|
|
rpcInternal <- ccM
|
|
connMgr = NewConnManager(cfg)
|
|
connMgr.AddInternalConn(utils.ConcatenatedKey(utils.MetaInternal, utils.MetaThresholds), utils.ThresholdSv1, rpcInternal)
|
|
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, connMgr)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"TH1"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: "testMetricType",
|
|
},
|
|
},
|
|
}
|
|
sq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
dirty: utils.BoolPointer(false),
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
},
|
|
},
|
|
SQMetrics: map[string]StatMetric{
|
|
"testMetricType": &StatTCD{
|
|
Metric: &Metric{
|
|
Value: utils.NewDecimal(int64(time.Hour), 0),
|
|
Count: 1,
|
|
Events: make(map[string]*DecimalWithCompress),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
sQs := []*StatQueue{sq}
|
|
|
|
expLog := `[WARNING] <StatS> error: EXISTS`
|
|
if err := sS.processThresholds(context.Background(), sQs, nil); err == nil ||
|
|
err != utils.ErrPartiallyExecuted {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrPartiallyExecuted, err)
|
|
} else if rcvLog := buf.String(); !strings.Contains(rcvLog, expLog) {
|
|
t.Errorf("expected log <%+v> to be included in <%+v>", expLog, rcvLog)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueV1GetQueueFloatMetricsOK(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
sq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
dirty: utils.BoolPointer(false),
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
},
|
|
},
|
|
SQMetrics: map[string]StatMetric{
|
|
utils.MetaTCD: &StatTCD{
|
|
Metric: &Metric{
|
|
Value: utils.NewDecimal(int64(time.Hour), 0),
|
|
Count: 1,
|
|
Events: map[string]*DecimalWithCompress{
|
|
"SqProcessEvent": {},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
expected := map[string]float64{
|
|
utils.MetaTCD: 3600000000000,
|
|
}
|
|
reply := map[string]float64{}
|
|
if err := sS.V1GetQueueFloatMetrics(context.Background(), &utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{ID: "SQ1"}}, &reply); err != nil {
|
|
t.Error(err)
|
|
} else if !reflect.DeepEqual(reply, expected) {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", expected, reply)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueV1GetQueueFloatMetricsErrNotFound(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
sq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
dirty: utils.BoolPointer(false),
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
},
|
|
},
|
|
SQMetrics: map[string]StatMetric{
|
|
utils.MetaTCD: &StatTCD{
|
|
Metric: &Metric{
|
|
Value: utils.NewDecimal(int64(time.Hour), 0),
|
|
Count: 1,
|
|
Events: map[string]*DecimalWithCompress{
|
|
"SqProcessEvent": {},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
reply := map[string]float64{}
|
|
if err := sS.V1GetQueueFloatMetrics(context.Background(), &utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{ID: "SQ2"}}, &reply); err == nil || err != utils.ErrNotFound {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueV1GetQueueFloatMetricsMissingArgs(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
sq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
dirty: utils.BoolPointer(false),
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
},
|
|
},
|
|
SQMetrics: map[string]StatMetric{
|
|
utils.MetaTCD: &StatTCD{
|
|
Metric: &Metric{
|
|
Value: utils.NewDecimal(int64(time.Hour), 0),
|
|
Count: 1,
|
|
Events: make(map[string]*DecimalWithCompress),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
experr := `MANDATORY_IE_MISSING: [ID]`
|
|
reply := map[string]float64{}
|
|
if err := sS.V1GetQueueFloatMetrics(context.Background(), &utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{}}, &reply); err == nil ||
|
|
err.Error() != experr {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueV1GetQueueFloatMetricsErrGetStats(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
Cache = NewCacheS(cfg, nil, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, nil)
|
|
sS := NewStatService(nil, cfg, filterS, nil)
|
|
|
|
experr := `SERVER_ERROR: NO_DATABASE_CONNECTION`
|
|
reply := map[string]float64{}
|
|
if err := sS.V1GetQueueFloatMetrics(context.Background(), &utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{ID: "SQ1"}}, &reply); err == nil || err.Error() != experr {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueV1GetQueueStringMetricsOK(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
sq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
dirty: utils.BoolPointer(false),
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
},
|
|
},
|
|
SQMetrics: map[string]StatMetric{
|
|
utils.MetaTCD: &StatTCD{
|
|
Metric: &Metric{
|
|
Value: utils.NewDecimal(int64(time.Hour), 0),
|
|
Count: 1,
|
|
Events: map[string]*DecimalWithCompress{
|
|
"SqProcessEvent": {},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
expected := map[string]string{
|
|
utils.MetaTCD: "1h0m0s",
|
|
}
|
|
reply := map[string]string{}
|
|
if err := sS.V1GetQueueStringMetrics(context.Background(), &utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{ID: "SQ1"}}, &reply); err != nil {
|
|
t.Error(err)
|
|
} else if !reflect.DeepEqual(reply, expected) {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", expected, reply)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueV1GetQueueStringMetricsErrNotFound(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
sq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
dirty: utils.BoolPointer(false),
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
},
|
|
},
|
|
SQMetrics: map[string]StatMetric{
|
|
utils.MetaTCD: &StatTCD{
|
|
Metric: &Metric{
|
|
Value: utils.NewDecimal(int64(time.Hour), 0),
|
|
Count: 1,
|
|
Events: make(map[string]*DecimalWithCompress),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
reply := map[string]string{}
|
|
if err := sS.V1GetQueueStringMetrics(context.Background(), &utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{ID: "SQ2"}}, &reply); err == nil || err != utils.ErrNotFound {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueV1GetQueueStringMetricsMissingArgs(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
sq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
dirty: utils.BoolPointer(false),
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
},
|
|
},
|
|
SQMetrics: map[string]StatMetric{
|
|
utils.MetaTCD: &StatTCD{
|
|
Metric: &Metric{
|
|
Value: utils.NewDecimal(int64(time.Hour), 0),
|
|
Count: 1,
|
|
Events: make(map[string]*DecimalWithCompress),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
experr := `MANDATORY_IE_MISSING: [ID]`
|
|
reply := map[string]string{}
|
|
if err := sS.V1GetQueueStringMetrics(context.Background(), &utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{}}, &reply); err == nil ||
|
|
err.Error() != experr {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueV1GetQueueStringMetricsErrGetStats(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
Cache = NewCacheS(cfg, nil, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, nil)
|
|
sS := NewStatService(nil, cfg, filterS, nil)
|
|
|
|
experr := `SERVER_ERROR: NO_DATABASE_CONNECTION`
|
|
reply := map[string]string{}
|
|
if err := sS.V1GetQueueStringMetrics(context.Background(), &utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{ID: "SQ1"}}, &reply); err == nil || err.Error() != experr {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueStoreStatQueueStoreIntervalDisabled(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
tmpCM := connMgr
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
connMgr = tmpCM
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().StoreInterval = -1
|
|
config.SetCgrConfig(cfg)
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
connMgr = NewConnManager(cfg)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, connMgr)
|
|
|
|
sq := &StatQueue{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQMetrics: make(map[string]StatMetric),
|
|
dirty: utils.BoolPointer(true),
|
|
}
|
|
|
|
sS.storeStatQueue(context.Background(), sq)
|
|
|
|
if *sq.dirty != false {
|
|
t.Error("expected dirty to be false")
|
|
}
|
|
}
|
|
|
|
func TestStatQueueGetStatQueueOK(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
sq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
dirty: utils.BoolPointer(false),
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
ExpiryTime: utils.TimePointer(time.Now()),
|
|
},
|
|
},
|
|
SQMetrics: map[string]StatMetric{
|
|
utils.MetaTCD: &StatTCD{
|
|
Metric: &Metric{
|
|
Value: utils.NewDecimal(int64(time.Hour), 0),
|
|
Count: 1,
|
|
Events: make(map[string]*DecimalWithCompress),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
expSq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
dirty: utils.BoolPointer(false),
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{},
|
|
SQMetrics: map[string]StatMetric{
|
|
utils.MetaTCD: &StatTCD{
|
|
Metric: &Metric{
|
|
Value: utils.NewDecimal(int64(time.Hour), 0),
|
|
Count: 1,
|
|
Events: make(map[string]*DecimalWithCompress),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
expected := utils.StringSet{
|
|
utils.ConcatenatedKey(sq.Tenant, sq.ID): struct{}{},
|
|
}
|
|
if rcv, err := sS.getStatQueue(context.Background(), "cgrates.org", "SQ1"); err != nil {
|
|
t.Error(err)
|
|
} else if utils.ToJSON(expSq) != utils.ToJSON(rcv) {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>",
|
|
utils.ToJSON(expSq), utils.ToJSON(rcv))
|
|
} else if !reflect.DeepEqual(sS.storedStatQueues, expected) {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", expected, sS.storedStatQueues)
|
|
}
|
|
}
|
|
func TestStatQueueProcessEventProfileIgnoreFilters(t *testing.T) {
|
|
Cache.Clear(nil)
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
cfg.StatSCfg().Opts.ProfileIgnoreFilters = []*config.DynamicBoolOpt{
|
|
config.NewDynamicBoolOpt(nil, "", true, nil),
|
|
}
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Stat:testStatValue"},
|
|
}
|
|
sq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
},
|
|
},
|
|
SQMetrics: make(map[string]StatMetric),
|
|
}
|
|
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqPrf, true); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
//should match the stat queue for event because the option is false but the filter matches
|
|
args2 := &utils.CGREvent{
|
|
Tenant: "cgrates.org",
|
|
ID: "SqProcessEvent",
|
|
Event: map[string]any{
|
|
"Stat": "testStatValue",
|
|
},
|
|
APIOpts: map[string]any{
|
|
utils.OptsStatsProfileIDs: []string{"SQ1"},
|
|
utils.MetaProfileIgnoreFilters: false,
|
|
},
|
|
}
|
|
|
|
expIDs2 := []string{"SQ1"}
|
|
if rcvIDs2, err := sS.processEvent(context.Background(), args2.Tenant, args2); err != nil {
|
|
t.Error(err)
|
|
} else if !reflect.DeepEqual(rcvIDs2, expIDs2) {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", expIDs2, rcvIDs2)
|
|
}
|
|
//should match the stat queue for event because the option is true even if the filter doesn't match
|
|
args := &utils.CGREvent{
|
|
Tenant: "cgrates.org",
|
|
ID: "SqProcessEvent",
|
|
Event: map[string]any{
|
|
"Stat": "testStatValue2",
|
|
},
|
|
APIOpts: map[string]any{
|
|
utils.OptsStatsProfileIDs: []string{"SQ1"},
|
|
utils.MetaProfileIgnoreFilters: true,
|
|
},
|
|
}
|
|
|
|
expIDs := []string{"SQ1"}
|
|
if rcvIDs, err := sS.processEvent(context.Background(), args.Tenant, args); err != nil {
|
|
t.Error(err)
|
|
} else if !reflect.DeepEqual(rcvIDs, expIDs) {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", expIDs, rcvIDs)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueProcessEventProfileIgnoreFiltersError(t *testing.T) {
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
cfg.StatSCfg().Opts.ProfileIgnoreFilters = []*config.DynamicBoolOpt{
|
|
config.NewDynamicBoolOpt(nil, "", true, nil),
|
|
}
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Stat:testStatValue"},
|
|
}
|
|
sq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
},
|
|
},
|
|
SQMetrics: make(map[string]StatMetric),
|
|
}
|
|
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqPrf, true); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
args2 := &utils.CGREvent{
|
|
Tenant: "cgrates.org",
|
|
ID: "SqProcessEvent",
|
|
Event: map[string]any{
|
|
"Stat": "testStatValue",
|
|
},
|
|
APIOpts: map[string]any{
|
|
utils.OptsStatsProfileIDs: []string{"SQ1"},
|
|
utils.MetaProfileIgnoreFilters: time.Second,
|
|
},
|
|
}
|
|
|
|
if _, err := sS.processEvent(context.Background(), args2.Tenant, args2); err == nil || err.Error() != "cannot convert field: 1s to bool" {
|
|
t.Errorf("\nExpected <%+v>, \nReceived <%+v>", "cannot convert field: 1s to bool", err)
|
|
}
|
|
|
|
}
|
|
|
|
func TestStatQueueV1GetStatQueuesForEventProfileIgnoreFilters(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().Opts.ProfileIgnoreFilters = []*config.DynamicBoolOpt{
|
|
config.NewDynamicBoolOpt(nil, "", true, nil),
|
|
}
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf1 := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqPrf1, true); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
args := &utils.CGREvent{
|
|
ID: "TestGetStatQueuesForEvent",
|
|
Event: map[string]any{
|
|
utils.AccountField: "1002",
|
|
},
|
|
APIOpts: map[string]any{
|
|
utils.OptsStatsProfileIDs: []string{"SQ1"},
|
|
utils.MetaProfileIgnoreFilters: true,
|
|
},
|
|
}
|
|
|
|
exp := []string{"SQ1"}
|
|
var reply []string
|
|
if err := sS.V1GetStatQueuesForEvent(context.Background(), args, &reply); err != nil {
|
|
t.Error(err)
|
|
} else {
|
|
sort.Strings(reply)
|
|
if !reflect.DeepEqual(reply, exp) {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", exp, reply)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestStatSV1GetQueueDecimalMetricsOK(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
sq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
dirty: utils.BoolPointer(false),
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
},
|
|
},
|
|
SQMetrics: map[string]StatMetric{
|
|
utils.MetaTCD: &StatTCD{
|
|
Metric: &Metric{
|
|
Value: utils.NewDecimal(int64(time.Hour), 0),
|
|
Count: 1,
|
|
Events: map[string]*DecimalWithCompress{
|
|
"SqProcessEvent": {},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
expected := map[string]float64{
|
|
utils.MetaTCD: 3600000000000,
|
|
}
|
|
var reply map[string]*utils.Decimal
|
|
if err := sS.V1GetQueueDecimalMetrics(context.Background(), &utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{ID: "SQ1"}}, &reply); err != nil {
|
|
t.Error(err)
|
|
} else if !reflect.DeepEqual(utils.ToJSON(expected), utils.ToJSON(reply)) {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ToJSON(expected), utils.ToJSON(reply))
|
|
}
|
|
}
|
|
|
|
func TestStatSV1GetQueueDecimalMetricsErrNotFound(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
sq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
dirty: utils.BoolPointer(false),
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
},
|
|
},
|
|
SQMetrics: map[string]StatMetric{
|
|
utils.MetaTCD: &StatTCD{
|
|
Metric: &Metric{
|
|
Value: utils.NewDecimal(int64(time.Hour), 0),
|
|
Count: 1,
|
|
Events: map[string]*DecimalWithCompress{
|
|
"SqProcessEvent": {},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
var reply map[string]*utils.Decimal
|
|
if err := sS.V1GetQueueDecimalMetrics(context.Background(), &utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{ID: "SQ2"}}, &reply); err == nil || err != utils.ErrNotFound {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
|
|
}
|
|
}
|
|
|
|
func TestStatV1GetQueueDecimalMetricsMissingArgs(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
sq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
dirty: utils.BoolPointer(false),
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
},
|
|
},
|
|
SQMetrics: map[string]StatMetric{
|
|
utils.MetaTCD: &StatTCD{
|
|
Metric: &Metric{
|
|
Value: utils.NewDecimal(int64(time.Hour), 0),
|
|
Count: 1,
|
|
Events: make(map[string]*DecimalWithCompress),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueue(context.Background(), sq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
experr := `MANDATORY_IE_MISSING: [ID]`
|
|
var reply map[string]*utils.Decimal
|
|
if err := sS.V1GetQueueDecimalMetrics(context.Background(), &utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{}}, &reply); err == nil ||
|
|
err.Error() != experr {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
|
|
}
|
|
}
|
|
|
|
func TestStatV1GetQueueDecimalMetricsErrGetStats(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
Cache = NewCacheS(cfg, nil, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, nil)
|
|
sS := NewStatService(nil, cfg, filterS, nil)
|
|
|
|
experr := `SERVER_ERROR: NO_DATABASE_CONNECTION`
|
|
var reply map[string]*utils.Decimal
|
|
if err := sS.V1GetQueueDecimalMetrics(context.Background(), &utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{ID: "SQ1"}}, &reply); err == nil || err.Error() != experr {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
|
|
}
|
|
}
|
|
|
|
func TestStatSV1GetQueueStringMetricsIntOptsErr(t *testing.T) {
|
|
Cache.Clear(nil)
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dmSTS := NewDataManager(dbCM, cfg, nil)
|
|
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
cfg.StatSCfg().StringIndexedFields = nil
|
|
cfg.StatSCfg().PrefixIndexedFields = nil
|
|
cfg.StatSCfg().Opts.RoundingDecimals = []*config.DynamicIntOpt{
|
|
// function will return error after trying to parse the filter
|
|
config.NewDynamicIntOpt([]string{"*string.invalid:filter"}, "cgrates.org", 4, nil),
|
|
}
|
|
statService := NewStatService(dmSTS, cfg,
|
|
&FilterS{dm: dmSTS, cfg: cfg}, nil)
|
|
|
|
prepareStatsData(t, dmSTS)
|
|
|
|
stq := map[string]string{}
|
|
|
|
experr := `inline parse error for string: <*string.invalid:filter>`
|
|
|
|
err := statService.V1GetQueueStringMetrics(context.TODO(), &utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{Tenant: testStatsQ[0].Tenant, ID: testStatsQ[0].ID}}, &stq)
|
|
if err.Error() != experr {
|
|
t.Errorf("Expected error <%v>, Received <%v>", experr, err)
|
|
}
|
|
|
|
}
|
|
|
|
func TestStatSV1GetStatQueuesForEventsqIDsErr(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().Opts.ProfileIDs = []*config.DynamicStringSliceOpt{
|
|
// function will return error after trying to parse the filter
|
|
{
|
|
FilterIDs: []string{"*string.invalid:filter"},
|
|
Tenant: "cgrates.org",
|
|
Values: []string{"value2"},
|
|
},
|
|
}
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf1 := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqPrf1, true); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
sqPrf2 := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ2",
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 20,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: false}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaACD,
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqPrf2, true); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
args := &utils.CGREvent{
|
|
ID: "TestGetStatQueuesForEvent",
|
|
Event: map[string]any{
|
|
utils.AccountField: "1001",
|
|
},
|
|
}
|
|
|
|
experr := `inline parse error for string: <*string.invalid:filter>`
|
|
var reply []string
|
|
if err := sS.V1GetStatQueuesForEvent(context.Background(), args, &reply); err.Error() != experr {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
|
|
}
|
|
}
|
|
|
|
func TestStatSV1GetStatQueuesForEventignFiltersErr(t *testing.T) {
|
|
tmp := Cache
|
|
tmpC := config.CgrConfig()
|
|
defer func() {
|
|
Cache = tmp
|
|
config.SetCgrConfig(tmpC)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
cfg.StatSCfg().Opts.ProfileIgnoreFilters = []*config.DynamicBoolOpt{
|
|
// function will return error after trying to parse the filter
|
|
config.NewDynamicBoolOpt([]string{"*string.invalid:filter"}, "cgrates.org", false, nil),
|
|
}
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
Cache = NewCacheS(cfg, dm, nil, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf1 := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqPrf1, true); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
sqPrf2 := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ2",
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 20,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: false}},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaACD,
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqPrf2, true); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
args := &utils.CGREvent{
|
|
ID: "TestGetStatQueuesForEvent",
|
|
Event: map[string]any{
|
|
utils.AccountField: "1001",
|
|
},
|
|
}
|
|
|
|
experr := `inline parse error for string: <*string.invalid:filter>`
|
|
var reply []string
|
|
if err := sS.V1GetStatQueuesForEvent(context.Background(), args, &reply); err.Error() != experr {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
|
|
}
|
|
}
|
|
|
|
func TestStatQueuesProcessEventidsErr(t *testing.T) {
|
|
Cache.Clear(nil)
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dmSTS := NewDataManager(dbCM, cfg, nil)
|
|
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
cfg.StatSCfg().StringIndexedFields = nil
|
|
cfg.StatSCfg().PrefixIndexedFields = nil
|
|
statService := NewStatService(dmSTS, cfg,
|
|
&FilterS{dm: dmSTS, cfg: cfg}, nil)
|
|
|
|
prepareStatsData(t, dmSTS)
|
|
args := &utils.CGREvent{
|
|
Tenant: utils.EmptyString,
|
|
ID: "SqProcessEvent",
|
|
Event: map[string]any{
|
|
utils.AccountField: "1002",
|
|
},
|
|
}
|
|
|
|
reply := []string{}
|
|
err := statService.V1ProcessEvent(context.TODO(), args, &reply)
|
|
if err != utils.ErrNotFound {
|
|
t.Errorf("Expecting error: %+v, received error: %+v", utils.ErrNotFound, err)
|
|
}
|
|
}
|
|
|
|
func TestStatSMatchingStatQueuesForEventNoSqs(t *testing.T) {
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dmSTS := NewDataManager(dbCM, cfg, nil)
|
|
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
cfg.StatSCfg().StringIndexedFields = nil
|
|
cfg.StatSCfg().PrefixIndexedFields = nil
|
|
|
|
statService := NewStatService(dmSTS, cfg,
|
|
&FilterS{dm: dmSTS, cfg: cfg}, nil)
|
|
prepareStatsData(t, dmSTS)
|
|
_, err := statService.matchingStatQueuesForEvent(context.TODO(), testStatsArgs[0].Tenant, []string{"statsIds"},
|
|
testStatsArgs[0].AsDataProvider(), false)
|
|
if err == nil || err != utils.ErrNotFound {
|
|
t.Errorf("Expected error <%v>, Received error <%v>", utils.ErrNotFound, err)
|
|
}
|
|
}
|
|
|
|
func TestStatQueuesMatchingStatQueuesForEventWeightErr(t *testing.T) {
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dmSTS := NewDataManager(dbCM, cfg, nil)
|
|
|
|
cfg.StatSCfg().StoreInterval = 1
|
|
cfg.StatSCfg().StringIndexedFields = nil
|
|
cfg.StatSCfg().PrefixIndexedFields = nil
|
|
statService := NewStatService(dmSTS, cfg,
|
|
&FilterS{dm: dmSTS, cfg: cfg}, nil)
|
|
prepareStatsData(t, dmSTS)
|
|
|
|
sqp := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "StatQueueProfile1",
|
|
FilterIDs: []string{"FLTR_STATS_1"},
|
|
QueueLength: 10,
|
|
TTL: 10 * time.Second,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: "*sum#~*req.Usage",
|
|
},
|
|
},
|
|
ThresholdIDs: []string{},
|
|
Stored: true,
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
FilterIDs: []string{"*stirng:~*req.Account:1001"},
|
|
Weight: 64,
|
|
},
|
|
},
|
|
MinItems: 1,
|
|
}
|
|
|
|
if err := statService.dm.SetStatQueueProfile(context.Background(), sqp, true); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
expErr := "NOT_IMPLEMENTED:*stirng"
|
|
_, err := statService.matchingStatQueuesForEvent(context.TODO(), testStatsArgs[0].Tenant, nil,
|
|
testStatsArgs[0].AsDataProvider(), false)
|
|
|
|
if err == nil || err.Error() != expErr {
|
|
t.Errorf("Expected error <%v>, Received error <%v>", expErr, err)
|
|
}
|
|
}
|
|
|
|
func TestStatQueueProcessEventProfileIDsErr(t *testing.T) {
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
args := &utils.CGREvent{
|
|
Tenant: "cgrates.org",
|
|
ID: "SqProcessEvent",
|
|
Event: map[string]any{
|
|
utils.AccountField: "1001",
|
|
},
|
|
APIOpts: map[string]any{},
|
|
}
|
|
sS.cfg.StatSCfg().Opts.ProfileIDs = []*config.DynamicStringSliceOpt{
|
|
|
|
{
|
|
FilterIDs: []string{"*string.invalid:filter"},
|
|
Tenant: "cgrates.org",
|
|
Values: []string{"value2"},
|
|
},
|
|
}
|
|
|
|
experr := `inline parse error for string: <*string.invalid:filter>`
|
|
if _, err := sS.processEvent(context.Background(), args.Tenant, args); err == nil ||
|
|
err.Error() != experr {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
|
|
}
|
|
|
|
}
|
|
|
|
func TestStatQueueProcessEventExpiredErr(t *testing.T) {
|
|
|
|
tmpl := utils.Logger
|
|
defer func() {
|
|
utils.Logger = tmpl
|
|
Cache = NewCacheS(config.NewDefaultCGRConfig(), nil, nil, nil)
|
|
}()
|
|
|
|
buf := new(bytes.Buffer)
|
|
utils.Logger = utils.NewStdLoggerWithWriter(buf, "", 7)
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: utils.DynamicBlockers{{Blocker: true}},
|
|
QueueLength: -1,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
expiry := time.Date(2021, 1, 1, 23, 59, 59, 10, time.UTC)
|
|
|
|
stq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
ExpiryTime: &expiry,
|
|
},
|
|
},
|
|
SQMetrics: map[string]StatMetric{
|
|
"key": statMetricMock("remExpired error"),
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqPrf, true); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := dm.SetStatQueue(context.Background(), stq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
args := &utils.CGREvent{
|
|
Tenant: "cgrates.org",
|
|
ID: "SqProcessEvent",
|
|
Event: map[string]any{
|
|
utils.AccountField: "1001",
|
|
},
|
|
APIOpts: map[string]any{
|
|
utils.MetaUsage: "10s",
|
|
utils.OptsStatsProfileIDs: []string{"SQ1"},
|
|
},
|
|
}
|
|
|
|
if rcv, err := sS.processEvent(context.Background(), args.Tenant, args); err == nil || err.Error() != utils.ErrPartiallyExecuted.Error() {
|
|
t.Errorf("*StatS.processEvent err=%v, want %v", err, utils.ErrPartiallyExecuted)
|
|
} else if !reflect.DeepEqual([]string{"SQ1"}, rcv) {
|
|
t.Errorf("expected: <%+v>, \nreceived: <%+v>", nil, rcv)
|
|
|
|
}
|
|
expErr := "cgrates.org:SQ1, ignoring event: cgrates.org:SqProcessEvent, error: remExpired mock error"
|
|
if rcvTxt := buf.String(); !strings.Contains(rcvTxt, expErr) {
|
|
t.Errorf("Expected <%v>, Received <%v>", expErr, rcvTxt)
|
|
}
|
|
|
|
buf.Reset()
|
|
|
|
}
|
|
|
|
func TestStatQueueProcessEventBlockerErr(t *testing.T) {
|
|
|
|
defer func() {
|
|
Cache = NewCacheS(config.NewDefaultCGRConfig(), nil, nil, nil)
|
|
}()
|
|
|
|
cfg := config.NewDefaultCGRConfig()
|
|
data, _ := NewInternalDB(nil, nil, nil, cfg.DbCfg().Items)
|
|
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: data}, cfg.DbCfg())
|
|
dm := NewDataManager(dbCM, cfg, nil)
|
|
filterS := NewFilterS(cfg, nil, dm)
|
|
sS := NewStatService(dm, cfg, filterS, nil)
|
|
|
|
sqPrf := &StatQueueProfile{
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
FilterIDs: []string{"*string:~*req.Account:1001"},
|
|
Weights: utils.DynamicWeights{
|
|
{
|
|
Weight: 10,
|
|
},
|
|
},
|
|
Blockers: []*utils.DynamicBlocker{
|
|
{
|
|
FilterIDs: []string{"*stirng:~*req.Account:1001"},
|
|
Blocker: true,
|
|
},
|
|
},
|
|
QueueLength: 10,
|
|
ThresholdIDs: []string{"*none"},
|
|
MinItems: 5,
|
|
Metrics: []*MetricWithFilters{
|
|
{
|
|
MetricID: utils.MetaTCD,
|
|
},
|
|
},
|
|
}
|
|
stat, err := NewStatMetric("*tcd", uint64(sqPrf.MinItems), []string{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
stq := &StatQueue{
|
|
sqPrfl: sqPrf,
|
|
Tenant: "cgrates.org",
|
|
ID: "SQ1",
|
|
SQItems: []SQItem{
|
|
{
|
|
EventID: "SqProcessEvent",
|
|
},
|
|
},
|
|
SQMetrics: map[string]StatMetric{
|
|
utils.MetaTCD: stat,
|
|
},
|
|
}
|
|
|
|
if err := dm.SetStatQueueProfile(context.Background(), sqPrf, true); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := dm.SetStatQueue(context.Background(), stq); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
args := &utils.CGREvent{
|
|
Tenant: "cgrates.org",
|
|
ID: "SqProcessEvent",
|
|
Event: map[string]any{
|
|
utils.AccountField: "1001",
|
|
},
|
|
APIOpts: map[string]any{
|
|
utils.MetaUsage: "10s",
|
|
utils.OptsStatsProfileIDs: []string{"SQ1"},
|
|
},
|
|
}
|
|
|
|
expErr := "NOT_IMPLEMENTED:*stirng"
|
|
if _, err := sS.processEvent(context.Background(), args.Tenant, args); err == nil || err.Error() != expErr {
|
|
t.Errorf("Expected error <%v>, Received error <%v>", expErr, err)
|
|
}
|
|
|
|
}
|