Files
cgrates/engine/libdynopts_test.go
2025-10-19 13:15:57 +02:00

3274 lines
103 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 (
"fmt"
"reflect"
"testing"
"time"
"github.com/cgrates/birpc/context"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/utils"
"github.com/ericlagergren/decimal"
)
func TestLibFiltersGetFloat64OptsReturnConfigOpt(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicFloat64Opt{
// tenant will not be recognized, will ignore this opt
config.NewDynamicFloat64Opt([]string{"*string:~*req.Account:1001"}, "cgrates.net", 3, nil),
// filter will not pass, will ignore this opt
config.NewDynamicFloat64Opt([]string{"*string:~*req.Account:1002"}, "cgrates.org", 4, nil),
config.NewDynamicFloat64Opt([]string{"*string:~*req.Account:1001"}, "cgrates.org", 5, nil),
}
expected := 5.
if rcv, err := GetFloat64Opts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
utils.OptsResourcesUnits); err != nil {
t.Error(err)
} else if rcv != expected {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetFloat64OptsFilterCheckErr(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicFloat64Opt{
// function will return error after trying to parse the filter
config.NewDynamicFloat64Opt([]string{"*string.invalid:filter"}, "cgrates.org", 4, nil),
}
experr := `inline parse error for string: <*string.invalid:filter>`
if _, err := GetFloat64Opts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
utils.OptsResourcesUnits); err == nil ||
err.Error() != experr {
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
}
}
func TestLibFiltersGetFloat64OptsReturnDefaultOpt(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicFloat64Opt{
// filter will not pass, will ignore this opt
config.NewDynamicFloat64Opt([]string{"*string:~*req.Account:1002"}, "cgrates.org", 4, nil),
config.NewDynamicFloat64Opt(nil, utils.EmptyString, config.ResourcesUnitsDftOpt, nil),
}
if rcv, err := GetFloat64Opts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
utils.OptsResourcesUnits); err != nil {
t.Error(err)
} else if rcv != config.ResourcesUnitsDftOpt {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", config.ResourcesUnitsDftOpt, rcv)
}
}
func TestLibFiltersGetFloat64OptsReturnOptFromAPIOpts(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{
utils.OptsResourcesUnits: 6,
},
}
dynOpts := []*config.DynamicFloat64Opt{
// will never get to this opt because it will return once it
// finds the one set in APIOpts
config.NewDynamicFloat64Opt([]string{"*string:~*req.Account:1001"}, "cgrates.org", 5, nil),
}
expected := 6.
if rcv, err := GetFloat64Opts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
"nonExistingAPIOpt", utils.OptsResourcesUnits); err != nil {
t.Error(err)
} else if rcv != expected {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetStringOptsReturnConfigOpt(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicStringOpt{
// tenant will not be recognized, will ignore this opt
config.NewDynamicStringOpt([]string{"*string:~*req.Account:1001"}, "cgrates.net", "value1", nil),
// filter will not pass, will ignore this opt
config.NewDynamicStringOpt([]string{"*string:~*req.Account:1002"}, "cgrates.net", "value2", nil),
config.NewDynamicStringOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", "value3", nil),
}
expected := "value3"
if rcv, err := GetStringOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
config.ResourcesUsageIDDftOpt, utils.OptsResourcesUsageID); err != nil {
t.Error(err)
} else if rcv != expected {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetStringOptsFilterCheckErr(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicStringOpt{
// function will return error after trying to parse the filter
config.NewDynamicStringOpt([]string{"*string.invalid:filter"}, "cgrates.org", "value2", nil),
}
experr := `inline parse error for string: <*string.invalid:filter>`
if _, err := GetStringOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
config.ResourcesUsageIDDftOpt, utils.OptsResourcesUsageID); err == nil ||
err.Error() != experr {
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
}
}
func TestLibFiltersGetStringOptsReturnDefaultOpt(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicStringOpt{
// filter will not pass, will ignore this opt
config.NewDynamicStringOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", "value2", nil),
}
if rcv, err := GetStringOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
config.ResourcesUsageIDDftOpt, utils.OptsResourcesUsageID); err != nil {
t.Error(err)
} else if rcv != config.ResourcesUsageIDDftOpt {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", config.ResourcesUsageIDDftOpt, rcv)
}
}
func TestLibFiltersGetStringOptsReturnOptFromAPIOpts(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{
utils.OptsResourcesUsageID: "value4",
},
}
dynOpts := []*config.DynamicStringOpt{
// will never get to this opt because it will return once it
// finds the one set in APIOpts
config.NewDynamicStringOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", "value3", nil),
}
expected := "value4"
if rcv, err := GetStringOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
config.ResourcesUsageIDDftOpt, "nonExistingAPIOpt", utils.OptsResourcesUsageID); err != nil {
t.Error(err)
} else if rcv != expected {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetDurationOptsReturnConfigOpt(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicDurationOpt{
// tenant will not be recognized, will ignore this opt
config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1001"}, "cgrates.net", time.Millisecond, nil),
// filter will not pass, will ignore this opt
config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1002"}, "cgrates.net", time.Second, nil),
config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", time.Minute, nil),
}
expected := time.Minute
if rcv, err := GetDurationOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
utils.OptsResourcesUsageTTL); err != nil {
t.Error(err)
} else if rcv != expected {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetDurationOptsFilterCheckErr(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicDurationOpt{
// function will return error after trying to parse the filter
config.NewDynamicDurationOpt([]string{"*string.invalid:filter"}, "cgrates.org", time.Second, nil),
}
experr := `inline parse error for string: <*string.invalid:filter>`
if _, err := GetDurationOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
utils.OptsResourcesUsageTTL); err == nil ||
err.Error() != experr {
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
}
}
func TestLibFiltersGetDurationOptsReturnDefaultOpt(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicDurationOpt{
// filter will not pass, will ignore this opt
config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", time.Second, nil),
config.NewDynamicDurationOpt(nil, "", config.ResourcesUsageTTLDftOpt, nil),
}
if rcv, err := GetDurationOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
utils.OptsResourcesUsageTTL); err != nil {
t.Error(err)
} else if rcv != config.ResourcesUsageTTLDftOpt {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", config.ResourcesUsageTTLDftOpt, rcv)
}
}
func TestLibFiltersGetDurationPointerOptsReturnOptFromAPIOpts(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{
utils.OptsResourcesUsageTTL: time.Hour,
},
}
dynOpts := []*config.DynamicDurationPointerOpt{
// will never get to this opt because it will return once it
// finds the one set in APIOpts
config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", utils.DurationPointer(time.Minute), nil),
}
expected := time.Hour
if rcv, err := GetDurationPointerOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
"nonExistingAPIOpt", utils.OptsResourcesUsageTTL); err != nil {
t.Error(err)
} else if *rcv != expected {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetDurationPointerOptsReturnConfigOpt(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicDurationPointerOpt{
// tenant will not be recognized, will ignore this opt
config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.net", utils.DurationPointer(time.Millisecond), nil),
// filter will not pass, will ignore this opt
config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1002"}, "cgrates.net", utils.DurationPointer(time.Second), nil),
config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", utils.DurationPointer(time.Minute), nil),
}
expected := time.Minute
if rcv, err := GetDurationPointerOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
utils.OptsResourcesUsageTTL); err != nil {
t.Error(err)
} else if *rcv != expected {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetDurationPointerOptsFilterCheckErr(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicDurationPointerOpt{
// function will return error after trying to parse the filter
config.NewDynamicDurationPointerOpt([]string{"*string.invalid:filter"}, "cgrates.org", utils.DurationPointer(time.Second), nil),
}
experr := `inline parse error for string: <*string.invalid:filter>`
if _, err := GetDurationPointerOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
utils.OptsResourcesUsageTTL); err == nil ||
err.Error() != experr {
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
}
}
func TestLibFiltersGetDurationPointerOptsReturnDefaultOpt(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicDurationPointerOpt{
// filter will not pass, will ignore this opt
config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", utils.DurationPointer(time.Second), nil),
config.NewDynamicDurationPointerOpt(nil, "", utils.DurationPointer(config.ResourcesUsageTTLDftOpt), nil),
}
if rcv, err := GetDurationPointerOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
utils.OptsResourcesUsageTTL); err != nil {
t.Error(err)
} else if *rcv != config.ResourcesUsageTTLDftOpt {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", config.ResourcesUsageTTLDftOpt, rcv)
}
}
func TestLibFiltersGetDurationOptsReturnOptFromAPIOpts(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{
utils.OptsResourcesUsageTTL: time.Hour,
},
}
dynOpts := []*config.DynamicDurationOpt{
// will never get to this opt because it will return once it
// finds the one set in APIOpts
config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", time.Minute, nil),
}
expected := time.Hour
if rcv, err := GetDurationOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
"nonExistingAPIOpt", utils.OptsResourcesUsageTTL); err != nil {
t.Error(err)
} else if rcv != expected {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetIntOptsReturnConfigOpt(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicIntOpt{
// tenant will not be recognized, will ignore this opt
config.NewDynamicIntOpt([]string{"*string:~*req.Account:1002"}, "cgrates.net", 3, nil),
// filter will not pass, will ignore this opt
config.NewDynamicIntOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", 4, nil),
config.NewDynamicIntOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", 5, nil),
}
expected := 5
if rcv, err := GetIntOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
utils.OptsAttributesProcessRuns); err != nil {
t.Error(err)
} else if rcv != expected {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetIntOptsFilterCheckErr(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicIntOpt{
// function will return error after trying to parse the filter
config.NewDynamicIntOpt([]string{"*string.invalid:filter"}, "cgrates.org", 4, nil),
}
experr := `inline parse error for string: <*string.invalid:filter>`
if _, err := GetIntOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
utils.OptsAttributesProcessRuns); err == nil ||
err.Error() != experr {
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
}
}
func TestLibFiltersGetIntOptsReturnDefaultOpt(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicIntOpt{
// filter will not pass, will ignore this opt
config.NewDynamicIntOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", 4, nil),
config.NewDynamicIntOpt(nil, utils.EmptyString, config.AttributesProcessRunsDftOpt, nil),
}
if rcv, err := GetIntOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
utils.OptsAttributesProcessRuns); err != nil {
t.Error(err)
} else if rcv != config.AttributesProcessRunsDftOpt {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", config.AttributesProcessRunsDftOpt, rcv)
}
}
func TestLibFiltersGetIntOptsReturnOptFromAPIOptsOK(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{
utils.OptsAttributesProcessRuns: 6,
},
}
dynOpts := []*config.DynamicIntOpt{
// will never get to this opt because it will return once it
// finds the one set in APIOpts
config.NewDynamicIntOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", 5, nil),
}
expected := 6
if rcv, err := GetIntOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
"nonExistingAPIOpt", utils.OptsAttributesProcessRuns); err != nil {
t.Error(err)
} else if rcv != expected {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetIntOptsReturnOptFromAPIOptsErr(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{
utils.OptsAttributesProcessRuns: true,
},
}
dynOpts := []*config.DynamicIntOpt{
// will never get to this opt because it will return once it
// finds the one set in APIOpts
config.NewDynamicIntOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", 5, nil),
}
experr := `cannot convert field<bool>: true to int`
if _, err := GetIntOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
"nonExistingAPIOpt", utils.OptsAttributesProcessRuns); err == nil || err.Error() != experr {
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
}
}
func TestLibFiltersGetTimeOptsReturnConfigOpt(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicStringOpt{
// tenant will not be recognized, will ignore this opt
config.NewDynamicStringOpt([]string{"*string:~*req.Account:1001"}, "cgrates.net", "2022-03-07T15:04:05", nil),
// filter will not pass, will ignore this opt
config.NewDynamicStringOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", "*daily", nil),
config.NewDynamicStringOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", "*monthly", nil),
}
expected := time.Now().AddDate(0, 1, 0)
if rcv, err := GetTimeOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
cfg.GeneralCfg().DefaultTimezone, config.RatesStartTimeDftOpt, utils.OptsRatesStartTime); err != nil {
t.Error(err)
} else if !dateEqual(rcv, expected) {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetTimeOptsFilterCheckErr(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicStringOpt{
// function will return error after trying to parse the filter
config.NewDynamicStringOpt([]string{"*string.invalid:filter"}, "cgrates.org", "*daily", nil),
}
experr := `inline parse error for string: <*string.invalid:filter>`
if _, err := GetTimeOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
cfg.GeneralCfg().DefaultTimezone, config.RatesStartTimeDftOpt, utils.OptsRatesStartTime); err == nil ||
err.Error() != experr {
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
}
}
func TestLibFiltersGetTimeOptsReturnDefaultOpt(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
expected, err := utils.ParseTimeDetectLayout(config.RatesStartTimeDftOpt, cfg.GeneralCfg().DefaultTimezone)
if err != nil {
t.Error(err)
}
dynOpts := []*config.DynamicStringOpt{
// filter will not pass, will ignore this opt
config.NewDynamicStringOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", "*daily", nil),
config.NewDynamicStringOpt(nil, "", config.RatesStartTimeDftOpt, nil),
}
if rcv, err := GetTimeOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
cfg.GeneralCfg().DefaultTimezone, config.RatesStartTimeDftOpt, utils.OptsRatesStartTime); err != nil {
t.Error(err)
} else if !dateEqual(rcv, expected) {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetTimeOptsReturnOptFromAPIOpts(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{
utils.OptsRatesStartTime: "*yearly",
},
}
dynOpts := []*config.DynamicStringOpt{
// will never get to this opt because it will return once it
// finds the one set in APIOpts
config.NewDynamicStringOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", "*monthly", nil),
}
expected := time.Now().AddDate(1, 0, 0)
if rcv, err := GetTimeOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
cfg.GeneralCfg().DefaultTimezone, config.RatesStartTimeDftOpt, "nonExistingAPIOpt", utils.OptsRatesStartTime); err != nil {
t.Error(err)
} else if !dateEqual(rcv, expected) {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func dateEqual(date1, date2 time.Time) bool {
y1, m1, d1 := date1.Date()
y2, m2, d2 := date2.Date()
return y1 == y2 && m1 == m2 && d1 == d2
}
func TestLibFiltersGetBoolOptsReturnConfigOpt(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicBoolOpt{
// tenant will not be recognized, will ignore this opt
config.NewDynamicBoolOpt([]string{"*string:~*req.Account:1001"}, "cgrates.net", false, nil),
// filter will not pass, will ignore this opt
config.NewDynamicBoolOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", false, nil),
config.NewDynamicBoolOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", true, nil),
}
expected := true
if rcv, err := GetBoolOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
utils.MetaProfileIgnoreFilters); err != nil {
t.Error(err)
} else if rcv != expected {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetBoolOptsFilterCheckErr(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicBoolOpt{
// function will return error after trying to parse the filter
config.NewDynamicBoolOpt([]string{"*string.invalid:filter"}, "cgrates.org", false, nil),
}
experr := `inline parse error for string: <*string.invalid:filter>`
if _, err := GetBoolOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
utils.MetaProfileIgnoreFilters); err == nil ||
err.Error() != experr {
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
}
}
func TestLibFiltersGetBoolOptsReturnDefaultOpt(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicBoolOpt{
// filter will not pass, will ignore this opt
config.NewDynamicBoolOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", true, nil),
}
if rcv, err := GetBoolOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
utils.MetaProfileIgnoreFilters); err != nil {
t.Error(err)
} else if rcv != config.ThresholdsProfileIgnoreFiltersDftOpt {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", config.ThresholdsProfileIgnoreFiltersDftOpt, rcv)
}
}
func TestLibFiltersGetBoolOptsReturnOptFromAPIOpts(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{
utils.MetaProfileIgnoreFilters: true,
},
}
dynOpts := []*config.DynamicBoolOpt{
// will never get to this opt because it will return once it
// finds the one set in APIOpts
config.NewDynamicBoolOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", false, nil),
}
expected := true
if rcv, err := GetBoolOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
"nonExistingAPIOpt", utils.MetaProfileIgnoreFilters); err != nil {
t.Error(err)
} else if rcv != expected {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetInterfaceOptsReturnConfigOpt(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicInterfaceOpt{
// tenant will not be recognized, will ignore this opt
{
FilterIDs: []string{"*string:~*req.Account:1001"},
Tenant: "cgrates.net",
Value: 1,
},
// filter will not pass, will ignore this opt
{
FilterIDs: []string{"*string:~*req.Account:1002"},
Tenant: "cgrates.org",
Value: "value2",
},
{
FilterIDs: []string{"*string:~*req.Account:1001"},
Tenant: "cgrates.org",
Value: "value3",
},
}
expected := "value3"
if rcv, err := GetInterfaceOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
config.RoutesMaxCostDftOpt, utils.OptsRoutesMaxCost); err != nil {
t.Error(err)
} else if rcv != expected {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetInterfaceOptsFilterCheckErr(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicInterfaceOpt{
// function will return error after trying to parse the filter
{
FilterIDs: []string{"*string.invalid:filter"},
Tenant: "cgrates.org",
Value: 2,
},
}
experr := `inline parse error for string: <*string.invalid:filter>`
if _, err := GetInterfaceOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
config.RoutesMaxCostDftOpt, utils.OptsRoutesMaxCost); err == nil ||
err.Error() != experr {
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
}
}
func TestLibFiltersGetInterfaceOptsReturnDefaultOpt(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicInterfaceOpt{
// filter will not pass, will ignore this opt
{
FilterIDs: []string{"*string:~*req.Account:1002"},
Tenant: "cgrates.org",
Value: 2,
},
{
Value: config.RoutesMaxCostDftOpt,
},
}
if rcv, err := GetInterfaceOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
utils.OptsRoutesMaxCost); err != nil {
t.Error(err)
} else if rcv != config.RoutesMaxCostDftOpt {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", utils.ToJSON(config.RoutesMaxCostDftOpt), rcv)
}
}
func TestLibFiltersGetInterfaceOptsReturnOptFromAPIOpts(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{
utils.OptsRoutesMaxCost: 4,
},
}
dynOpts := []*config.DynamicInterfaceOpt{
// will never get to this opt because it will return once it
// finds the one set in APIOpts
{
FilterIDs: []string{"*string:~*req.Account:1001"},
Tenant: "cgrates.org",
Value: "value3",
},
}
expected := 4
if rcv, err := GetInterfaceOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
config.RoutesMaxCostDftOpt, "nonExistingAPIOpt", utils.OptsRoutesMaxCost); err != nil {
t.Error(err)
} else if rcv != expected {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetStringSliceOptsReturnConfigOpt(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicStringSliceOpt{
// tenant will not be recognized, will ignore this opt
{
FilterIDs: []string{"*string:~*req.Account:1001"},
Tenant: "cgrates.net",
Values: []string{"value1"},
},
// filter will not pass, will ignore this opt
{
FilterIDs: []string{"*string:~*req.Account:1002"},
Tenant: "cgrates.org",
Values: []string{"value2"},
},
{
FilterIDs: []string{"*string:~*req.Account:1001"},
Tenant: "cgrates.org",
Values: []string{"value3"},
},
}
expected := []string{"value3"}
if rcv, err := GetStringSliceOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
config.AttributesProfileIDsDftOpt, utils.OptsAttributesProfileIDs); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(rcv, expected) {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetStringSliceOptsFilterCheckErr(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicStringSliceOpt{
// function will return error after trying to parse the filter
{
FilterIDs: []string{"*string.invalid:filter"},
Tenant: "cgrates.org",
Values: []string{"value2"},
},
}
experr := `inline parse error for string: <*string.invalid:filter>`
if _, err := GetStringSliceOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
config.AttributesProfileIDsDftOpt, utils.OptsAttributesProfileIDs); err == nil ||
err.Error() != experr {
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
}
}
func TestLibFiltersGetStringSliceOptsReturnDefaultOpt(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicStringSliceOpt{
// filter will not pass, will ignore this opt
{
FilterIDs: []string{"*string:~*req.Account:1002"},
Tenant: "cgrates.org",
Values: []string{"value2"},
},
}
if rcv, err := GetStringSliceOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
config.AttributesProfileIDsDftOpt, utils.OptsAttributesProfileIDs); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(rcv, config.AttributesProfileIDsDftOpt) {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", config.AttributesProfileIDsDftOpt, rcv)
}
}
func TestLibFiltersGetStringSliceOptsReturnOptFromAPIOpts(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{
utils.OptsAttributesProfileIDs: []string{"value4"},
},
}
dynOpts := []*config.DynamicStringSliceOpt{
// will never get to this opt because it will return once it
// finds the one set in APIOpts
{
FilterIDs: []string{"*string:~*req.Account:1001"},
Tenant: "cgrates.org",
Values: []string{"value3"},
},
}
expected := []string{"value4"}
if rcv, err := GetStringSliceOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
config.AttributesProfileIDsDftOpt, "nonExistingAPIOpt", utils.OptsAttributesProfileIDs); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(rcv, expected) {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetDecimalBigOptsReturnConfigOpt(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
strOpts := []*config.DynamicInterfaceOpt{
{
FilterIDs: []string{"*string:~*req.Account:1001"},
Tenant: "cgrates.net",
Value: "42",
},
// filter will not pass, will ignore this opt
{
FilterIDs: []string{"*string:~*req.Account:1002"},
Tenant: "cgrates.org",
Value: "-1",
},
{
FilterIDs: []string{"*string:~*req.Account:1001"},
Tenant: "cgrates.org",
Value: "1234",
},
}
dynOpts, _ := config.IfaceToDecimalBigDynamicOpts(strOpts)
expected := decimal.New(1234, 0)
if rcv, err := GetDecimalBigOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
utils.OptsRatesUsage); err != nil {
t.Error(err)
} else if rcv.Cmp(expected) != 0 {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestDynamicDecimalBigOptsDynVal(t *testing.T) {
tests := []struct {
name string
dynOpts []*config.DynamicInterfaceOpt
expVal int64
expErr error
}{
{
name: "DynOptsVal",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*opts.*usage",
},
},
expVal: int64(time.Second * 27),
},
{
name: "DynReqVal",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*req.Usage",
},
},
expVal: int64(time.Second * 12),
},
{
name: "StaticVal",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: 4334,
},
},
expVal: 4334,
},
{
name: "NotFound",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*req.RandomField",
},
},
expErr: utils.ErrNotFound,
},
{
name: "ValueNotConvertedCorrectly",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*req.Usage2",
},
},
expErr: fmt.Errorf("can't convert <twenty-five> to decimal"),
},
}
ev := &utils.CGREvent{
Tenant: utils.CGRateSorg,
ID: "testIDEvent",
Event: map[string]any{
utils.AccountField: "1001",
utils.Usage: "12s",
"Usage2": "twenty-five",
},
APIOpts: map[string]any{
utils.MetaUsage: "27s",
},
}
fs := NewFilterS(config.CgrConfig(), nil, nil)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dynOpts, err := config.IfaceToDecimalBigDynamicOpts(tt.dynOpts)
if err != nil {
t.Error(err)
return
}
out, err := GetDecimalBigOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fs, dynOpts, utils.OptsRatesUsage)
if tt.expErr != nil {
if err == nil {
t.Error("expected err,received nil")
}
if err.Error() != tt.expErr.Error() {
t.Errorf("expected error %v,received %v", tt.expErr, err)
}
return
}
if err != nil {
t.Errorf("unexpected err %v", err)
}
val, _ := out.Int64()
if tt.expVal != val {
t.Errorf("expected %d,received %d", tt.expVal, val)
}
})
}
}
func TestDynamicIntOptsDynVal(t *testing.T) {
tests := []struct {
name string
dynOpts []*config.DynamicInterfaceOpt
expVal int
expErr error
}{
{
name: "DynOptsVal",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*opts.*destination",
},
},
expVal: 6548454,
},
{
name: "DynReqVal",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*req.Destination",
},
},
expVal: 34534353,
},
{
name: "StaticVal",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: 5346633,
},
},
expVal: 5346633,
},
{
name: "NotFound",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*req.RandomField",
},
},
expErr: utils.ErrNotFound,
},
{
name: "ValueNotConvertedCorrectly",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*req.Usage2",
},
},
expErr: fmt.Errorf("strconv.Atoi: parsing \"twenty-five\": invalid syntax"),
},
}
ev := &utils.CGREvent{
Tenant: utils.CGRateSorg,
ID: "testIDEvent",
Event: map[string]any{
utils.AccountField: "1001",
utils.Destination: 34534353,
"Usage2": "twenty-five",
},
APIOpts: map[string]any{
utils.MetaDestination: 6548454,
},
}
fs := NewFilterS(config.CgrConfig(), nil, nil)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dynOpts, err := config.IfaceToIntDynamicOpts(tt.dynOpts)
if err != nil {
t.Error(err)
return
}
out, err := GetIntOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fs, dynOpts, utils.OptsRatesUsage)
if tt.expErr != nil {
if err == nil {
t.Error("expected err,received nil")
}
if err.Error() != tt.expErr.Error() {
t.Errorf("expected error %v,received %v", tt.expErr, err)
}
return
}
if err != nil {
t.Errorf("unexpected err %v", err)
}
if tt.expVal != out {
t.Errorf("expected %d,received %d", tt.expVal, out)
}
})
}
}
func TestDynamicFloat64OptsDynVal(t *testing.T) {
tests := []struct {
name string
dynOpts []*config.DynamicInterfaceOpt
expVal float64
expErr error
}{
{
name: "DynOptsVal",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*opts.*acc",
},
},
expVal: 3434,
},
{
name: "DynReqVal",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*req.*acd",
},
},
expVal: 2213.,
},
{
name: "StaticVal",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: 23.1,
},
},
expVal: 23.1,
},
{
name: "NotFound",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*req.RandomField",
},
},
expErr: utils.ErrNotFound,
},
{
name: "ValueNotConvertedCorrectly",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*req.Usage2",
},
},
expErr: fmt.Errorf("strconv.ParseFloat: parsing \"twenty-five\": invalid syntax"),
},
}
ev := &utils.CGREvent{
Tenant: utils.CGRateSorg,
ID: "testIDEvent",
Event: map[string]any{
utils.AccountField: "1001",
"*acd": 2213,
"Usage2": "twenty-five",
},
APIOpts: map[string]any{
"*acc": 3434,
},
}
fs := NewFilterS(config.CgrConfig(), nil, nil)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dynOpts, err := config.InterfaceToFloat64DynamicOpts(tt.dynOpts)
if err != nil {
t.Error(err)
return
}
out, err := GetFloat64Opts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fs, dynOpts, utils.OptsRatesUsage)
if tt.expErr != nil {
if err == nil {
t.Error("expected err,received nil")
}
if err.Error() != tt.expErr.Error() {
t.Errorf("expected error %v,received %v", tt.expErr, err)
}
return
}
if err != nil {
t.Errorf("unexpected err %v", err)
}
if tt.expVal != out {
t.Errorf("expected %f,received %f", tt.expVal, out)
}
})
}
}
func TestDynamicBoolOptsDynVal(t *testing.T) {
tests := []struct {
name string
dynOpts []*config.DynamicInterfaceOpt
expVal bool
expErr error
}{
{
name: "DynOptsVal",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*opts.Field2",
},
},
expVal: true,
},
{
name: "DynReqVal",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*req.Field1",
},
},
expVal: false,
},
{
name: "StaticVal",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: true,
},
},
expVal: true,
},
{
name: "NotFound",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*req.RandomField",
},
},
expErr: utils.ErrNotFound,
},
{
name: "ValueNotConvertedCorrectly",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*req.Usage2",
},
},
expErr: fmt.Errorf("strconv.ParseBool: parsing \"twenty-five\": invalid syntax"),
},
}
ev := &utils.CGREvent{
Tenant: utils.CGRateSorg,
ID: "testIDEvent",
Event: map[string]any{
utils.AccountField: "1001",
"Field1": false,
"Usage2": "twenty-five",
},
APIOpts: map[string]any{
"Field2": true,
},
}
fs := NewFilterS(config.CgrConfig(), nil, nil)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dynOpts, err := config.IfaceToBoolDynamicOpts(tt.dynOpts)
if err != nil {
t.Error(err)
return
}
out, err := GetBoolOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fs, dynOpts, utils.OptsRatesUsage)
if tt.expErr != nil {
if err == nil {
t.Error("expected err,received nil")
}
if err.Error() != tt.expErr.Error() {
t.Errorf("expected error %v,received %v", tt.expErr, err)
}
return
}
if err != nil {
t.Errorf("unexpected err %v", err)
}
if tt.expVal != out {
t.Errorf("expected %v,received %v", tt.expVal, out)
}
})
}
}
func TestDynamicDurationOptsDynVal(t *testing.T) {
tests := []struct {
name string
dynOpts []*config.DynamicInterfaceOpt
expVal time.Duration
expErr error
}{
{
name: "DynOptsVal",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*opts.Usage",
},
},
expVal: time.Second * 10,
},
{
name: "DynReqVal",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*req.*acd",
},
},
expVal: 3500000,
},
{
name: "StaticVal",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: 1000000000,
},
},
expVal: 1 * time.Second,
},
{
name: "NotFound",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*req.RandomField",
},
},
expErr: utils.ErrNotFound,
},
{
name: "ValueNotConvertedCorrectly",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*req.Usage2",
},
},
expErr: fmt.Errorf("time: invalid duration \"twenty-five\""),
},
}
ev := &utils.CGREvent{
Tenant: utils.CGRateSorg,
ID: "testIDEvent",
Event: map[string]any{
utils.AccountField: "1001",
"*acd": 3500000,
"Usage2": "twenty-five",
},
APIOpts: map[string]any{
"Usage": "10s",
},
}
fs := NewFilterS(config.CgrConfig(), nil, nil)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dynOpts, err := config.IfaceToDurationDynamicOpts(tt.dynOpts)
if err != nil {
t.Error(err)
return
}
out, err := GetDurationOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fs, dynOpts, utils.OptsRatesUsage)
if tt.expErr != nil {
if err == nil {
t.Error("expected err,received nil")
}
if err.Error() != tt.expErr.Error() {
t.Errorf("expected error %v,received %v", tt.expErr, err)
}
return
}
if err != nil {
t.Errorf("unexpected err %v", err)
}
if tt.expVal != out {
t.Errorf("expected %v,received %v", tt.expVal, out)
}
})
}
}
func TestDynamicDurationPointerOptsDynVal(t *testing.T) {
tests := []struct {
name string
dynOpts []*config.DynamicInterfaceOpt
expVal *time.Duration
expErr error
}{
{
name: "DynOptsVal",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*opts.Usage",
},
},
expVal: utils.DurationPointer(time.Second * 10),
},
{
name: "DynReqVal",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*req.*acd",
},
},
expVal: utils.DurationPointer(3500000),
},
{
name: "StaticVal",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: 1000000000,
},
},
expVal: utils.DurationPointer(1 * time.Second),
},
{
name: "NotFound",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*req.RandomField",
},
},
expErr: utils.ErrNotFound,
},
{
name: "ValueNotConvertedCorrectly",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*req.Usage2",
},
},
expErr: fmt.Errorf("time: invalid duration \"twenty-five\""),
},
}
ev := &utils.CGREvent{
Tenant: utils.CGRateSorg,
ID: "testIDEvent",
Event: map[string]any{
utils.AccountField: "1001",
"*acd": 3500000,
"Usage2": "twenty-five",
},
APIOpts: map[string]any{
"Usage": "10s",
},
}
fs := NewFilterS(config.CgrConfig(), nil, nil)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dynOpts, err := config.IfaceToDurationPointerDynamicOpts(tt.dynOpts)
if err != nil {
t.Error(err)
return
}
out, err := GetDurationPointerOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fs, dynOpts, utils.OptsRatesUsage)
if tt.expErr != nil {
if err == nil {
t.Error("expected err,received nil")
}
if err.Error() != tt.expErr.Error() {
t.Errorf("expected error %v,received %v", tt.expErr, err)
}
return
}
if err != nil {
t.Errorf("unexpected err %v", err)
}
if *tt.expVal != *out {
t.Errorf("expected %v,received %v", tt.expVal, out)
}
})
}
}
func TestDynamicStringOptsDynVal(t *testing.T) {
tests := []struct {
name string
dynOpts []*config.DynamicInterfaceOpt
expVal string
expErr error
}{
{
name: "DynOptsVal",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*opts.*context",
},
},
expVal: "chargers",
},
{
name: "DynReqVal",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*req.Supplier",
},
},
expVal: "Supplier1",
},
{
name: "StaticVal",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "value",
},
},
expVal: "value",
},
}
ev := &utils.CGREvent{
Tenant: utils.CGRateSorg,
ID: "testIDEvent",
Event: map[string]any{
utils.AccountField: "1001",
"Supplier": "Supplier1",
},
APIOpts: map[string]any{
"*context": "chargers",
},
}
fs := NewFilterS(config.CgrConfig(), nil, nil)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dynOpts, err := config.InterfaceToDynamicStringOpts(tt.dynOpts)
if err != nil {
t.Error(err)
return
}
out, err := GetStringOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fs, dynOpts)
if tt.expErr != nil {
if err == nil {
t.Error("expected err,received nil")
}
if err.Error() != tt.expErr.Error() {
t.Errorf("expected error %v,received %v", tt.expErr, err)
}
return
}
if err != nil {
t.Errorf("unexpected err %v", err)
}
if tt.expVal != out {
t.Errorf("expected %v,received %v", tt.expVal, out)
}
})
}
}
func TestAttrDynamicOptsFromJson(t *testing.T) {
cfgJSONStr := `{
"attributes": {
"enabled": true,
"stats_conns": ["*internal"],
"resources_conns": ["*internal"],
"accounts_conns": ["*internal"],
"prefix_indexed_fields": ["*req.index1","*req.index2"],
"string_indexed_fields": ["*req.index1"],
"exists_indexed_fields": ["*req.index1","*req.index2"],
"notexists_indexed_fields": ["*req.index1"],
"opts": {
"*processRuns": [
{
"Value": "~*req.ProcessRuns",
"FilterIDs": ["*string:~*req.Account:1001"]
},
{
"Value": 11,
"FilterIDs": ["*string:~*req.Account:1003"]
},
],
"*profileRuns": [
{
"FilterIDs": ["*string:~*req.Account:1001"],
"Value": "~*opts.ProfileRuns"
}
],
"*profileIgnoreFilters": [
{
"FilterIDs": ["*string:~*req.Account:1001"],
"Value": "~*req.IgnoreFilters"
}
]
},
},
}`
cgrCfg, err := config.NewCGRConfigFromJSONStringWithDefaults(cfgJSONStr)
if err != nil {
t.Error(err)
}
ev := &utils.CGREvent{
Tenant: utils.CGRateSorg,
ID: "testIDEvent",
Event: map[string]any{
utils.AccountField: "1001",
"ProcessRuns": 4,
"IgnoreFilters": true,
},
APIOpts: map[string]any{
"ProfileRuns": 5,
},
}
ev2 := &utils.CGREvent{
Tenant: utils.CGRateSorg,
ID: "testIDEvent",
Event: map[string]any{
utils.AccountField: "1002",
"ProcessRuns": 4,
"IgnoreFilters": true,
},
APIOpts: map[string]any{
"ProfileRuns": 5,
},
}
ev3 := &utils.CGREvent{
Tenant: utils.CGRateSorg,
ID: "testIDEvent",
Event: map[string]any{
utils.AccountField: "1003",
},
APIOpts: map[string]any{},
}
fltrs := NewFilterS(cgrCfg, nil, nil)
if rcv, err := GetIntOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.AttributeSCfg().Opts.ProcessRuns); err != nil {
t.Error(err)
} else if rcv != 4 {
t.Errorf("expected %d,received %d", 4, rcv)
}
if rcv, err := GetIntOpts(context.Background(), "cgrates.org", ev3.AsDataProvider(), nil, fltrs, cgrCfg.AttributeSCfg().Opts.ProcessRuns); err != nil {
t.Error(err)
} else if rcv != 11 {
t.Errorf("expected %d,received %d", 11, rcv)
}
if rcv, err := GetIntOpts(context.Background(), "cgrates.org", ev2.AsDataProvider(), nil, fltrs, cgrCfg.AttributeSCfg().Opts.ProcessRuns); err != nil {
t.Error(err)
} else if rcv != config.AttributesProcessRunsDftOpt {
t.Errorf("expected %d,received %d", config.AttributesProcessRunsDftOpt, rcv)
}
if rcv, err := GetIntOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.AttributeSCfg().Opts.ProfileRuns); err != nil {
t.Error(err)
} else if rcv != 5 {
t.Errorf("expected %d,received %d", 5, rcv)
}
if rcv, err := GetIntOpts(context.Background(), "cgrates.org", ev2.AsDataProvider(), nil, fltrs, cgrCfg.AttributeSCfg().Opts.ProfileRuns); err != nil {
t.Error(err)
} else if rcv != config.AttributesProfileRunsDftOpt {
t.Errorf("expected %d,received %d", config.AttributesProcessRunsDftOpt, rcv)
}
if rcv, err := GetBoolOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.AttributeSCfg().Opts.ProfileIgnoreFilters); err != nil {
t.Error(err)
} else if !rcv {
t.Errorf("expected true,received %v", rcv)
}
if rcv, err := GetBoolOpts(context.Background(), "cgrates.org", ev2.AsDataProvider(), nil, fltrs, cgrCfg.AttributeSCfg().Opts.ProfileIgnoreFilters); err != nil {
t.Error(err)
} else if rcv != config.AttributesProfileIgnoreFiltersDftOpt {
t.Errorf("expected %v,received %v", config.AttributesProcessRunsDftOpt, rcv)
}
}
func TestSessionDynamicOptsFromJson(t *testing.T) {
cfgJSONStr := `{
"sessions": {
"enabled": true,
"listen_bijson": "127.0.0.1:2018",
"replication_conns": ["*localhost"],
"store_session_costs": true,
"min_dur_low_balance": "1s",
"client_protocol": 2.0,
"terminate_attempts": 10,
"opts": {
"*accounts": [
{
"FilterIDs": ["*string:~*req.Account:1001"],
"Value": "~*opts.*accounts"
}
],
"*attributes": [
{
"FilterIDs": ["*string:~*req.Account:1001"],
"Value": "~*opts.*attributes"
}
],
"*ttl": [
{
"FilterIDs": ["*string:~*req.Account:1001"],
"Value": "~*req.TTL",
},
],
"*debitInterval": [
{
"FilterIDs": ["*string:~*req.Account:1001"],
"Value": "~*req.Usage",
},
],
},
},
}`
cgrCfg, err := config.NewCGRConfigFromJSONStringWithDefaults(cfgJSONStr)
if err != nil {
t.Error(err)
}
ev := &utils.CGREvent{
Tenant: utils.CGRateSorg,
ID: "testIDEvent",
Event: map[string]any{
utils.AccountField: "1001",
utils.Usage: 8 * time.Second,
utils.TTL: "1s",
},
APIOpts: map[string]any{
utils.MetaAttributes: true,
utils.MetaAccounts: true,
},
}
ev2 := &utils.CGREvent{
Tenant: utils.CGRateSorg,
ID: "testIDEvent",
Event: map[string]any{
utils.AccountField: "1002",
utils.Usage: 8 * time.Second,
utils.TTL: "1s",
},
APIOpts: map[string]any{
utils.MetaAttributes: true,
utils.MetaAccounts: true,
},
}
fltrs := NewFilterS(cgrCfg, nil, nil)
if rcv, err := GetBoolOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.SessionSCfg().Opts.Accounts); err != nil {
t.Error(err)
} else if !rcv {
t.Errorf("expected true,received %v", rcv)
}
if rcv, err := GetBoolOpts(context.Background(), "cgrates.org", ev2.AsDataProvider(), nil, fltrs, cgrCfg.SessionSCfg().Opts.Accounts); err != nil {
t.Error(err)
} else if rcv != config.SessionsAccountsDftOpt {
t.Errorf("expected %v,received %v", config.SessionsAccountsDftOpt, rcv)
}
if rcv, err := GetBoolOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.SessionSCfg().Opts.Attributes); err != nil {
t.Error(err)
} else if !rcv {
t.Errorf("expected true,received %v", rcv)
}
if rcv, err := GetBoolOpts(context.Background(), "cgrates.org", ev2.AsDataProvider(), nil, fltrs, cgrCfg.SessionSCfg().Opts.Attributes); err != nil {
t.Error(err)
} else if rcv != config.SessionsAttributesDftOpt {
t.Errorf("expected %v,received %v", config.SessionsAttributesDftOpt, rcv)
}
if rcv, err := GetDurationOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.SessionSCfg().Opts.TTL); err != nil {
t.Error(err)
} else if rcv != time.Second {
t.Errorf("expected %v,received %v", time.Second, rcv)
}
if rcv, err := GetDurationOpts(context.Background(), "cgrates.org", ev2.AsDataProvider(), nil, fltrs, cgrCfg.SessionSCfg().Opts.TTL); err != nil {
t.Error(err)
} else if rcv != config.SessionsTTLDftOpt {
t.Errorf("expected %v,received %v", config.SessionsTTLDftOpt, rcv)
}
if rcv, err := GetDurationOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.SessionSCfg().Opts.DebitInterval); err != nil {
t.Error(err)
} else if rcv != (8 * time.Second) {
t.Errorf("expected %v,received %v", 8*time.Second, rcv)
}
if rcv, err := GetDurationOpts(context.Background(), "cgrates.org", ev2.AsDataProvider(), nil, fltrs, cgrCfg.SessionSCfg().Opts.DebitInterval); err != nil {
t.Error(err)
} else if rcv != config.SessionsDebitIntervalDftOpt {
t.Errorf("expected %v,received %v", config.SessionsDebitIntervalDftOpt, rcv)
}
}
func TestResDynamicOptsFromJson(t *testing.T) {
cfgJSONStr := `{
"resources": {
"enabled": true,
"store_interval": "7m",
"thresholds_conns": ["*internal:*thresholds", "*conn1"],
"indexed_selects":true,
"nested_fields": true,
"opts":{
"*usageID": [
{
"FilterIDs": ["*string:~*req.Account:1001"],
"Value": "~*req.UsageID"
}
],
"*usageTTL": [
{
"FilterIDs": ["*string:~*req.Account:1001"],
"Value": "~*req.UsageTTL"
}
],
"*units": [
{
"FilterIDs": ["*string:~*req.Account:1001"],
"Value": "~*opts.*units"
}
]
}
},
}`
cgrCfg, err := config.NewCGRConfigFromJSONStringWithDefaults(cfgJSONStr)
if err != nil {
t.Error(err)
}
ev := &utils.CGREvent{
Tenant: utils.CGRateSorg,
ID: "testIDEvent",
Event: map[string]any{
utils.AccountField: "1001",
"UsageID": "UsgID3232",
"UsageTTL": 3 * time.Second,
},
APIOpts: map[string]any{
"*units": 23.22,
},
}
ev2 := &utils.CGREvent{
Tenant: utils.CGRateSorg,
ID: "testIDEvent",
Event: map[string]any{
utils.AccountField: "1002",
"UsageID": "UsgID3232",
"UsageTTL": 3 * time.Second,
},
APIOpts: map[string]any{
"*units": 23.22,
},
}
fltrs := NewFilterS(cgrCfg, nil, nil)
if rcv, err := GetStringOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.ResourceSCfg().Opts.UsageID); err != nil {
t.Error(err)
} else if rcv != "UsgID3232" {
t.Errorf("expected %s,received %s", "UsgID3232", rcv)
}
if rcv, err := GetStringOpts(context.Background(), "cgrates.org", ev2.AsDataProvider(), nil, fltrs, cgrCfg.ResourceSCfg().Opts.UsageID); err != nil {
t.Error(err)
} else if rcv != config.ResourcesUsageIDDftOpt {
t.Errorf("expected %s,received %s", config.ResourcesUsageTTLDftOpt, rcv)
}
if rcv, err := GetDurationOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.ResourceSCfg().Opts.UsageTTL); err != nil {
t.Error(err)
} else if rcv != 3*time.Second {
t.Errorf("expected %v,received %d", 3*time.Second, rcv)
}
if rcv, err := GetDurationOpts(context.Background(), "cgrates.org", ev2.AsDataProvider(), nil, fltrs, cgrCfg.ResourceSCfg().Opts.UsageTTL); err != nil {
t.Error(err)
} else if rcv != config.ResourcesUsageTTLDftOpt {
t.Errorf("expected %d,received %d", config.ResourcesUsageTTLDftOpt, rcv)
}
if rcv, err := GetFloat64Opts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.ResourceSCfg().Opts.Units); err != nil {
t.Error(err)
} else if rcv != 23.22 {
t.Errorf("expected %f,received %f", 23.22, rcv)
}
if rcv, err := GetFloat64Opts(context.Background(), "cgrates.org", ev2.AsDataProvider(), nil, fltrs, cgrCfg.ResourceSCfg().Opts.Units); err != nil {
t.Error(err)
} else if rcv != config.ResourcesUnitsDftOpt {
t.Errorf("expected %v,received %v", config.ResourcesUnitsDftOpt, rcv)
}
}
func TestRoutesDynamicOptsFromJson(t *testing.T) {
routeJsnStr := `{
"routes": {
"enabled": true,
"indexed_selects":false,
"opts":{
"*usage": [
{
"FilterIDs": ["*string:~*req.Account:1001"],
"Value": "~*req.Usage"
},
{
"FilterIDs": ["*string:~*req.Account:1002"],
"Value": 15555
},
{
"FilterIDs": ["*string:~*req.Account:1003"],
"Value": "5m"
},
{
"FilterIDs": ["*string:~*req.Account:1004"],
"Value": "~*opts.*usage"
},
]
}
}
}`
cgrCfg, err := config.NewCGRConfigFromJSONStringWithDefaults(routeJsnStr)
if err != nil {
t.Error(err)
}
ev := &utils.CGREvent{
Tenant: utils.CGRateSorg,
ID: "testIDEvent",
Event: map[string]any{
utils.AccountField: "1001",
utils.Usage: 43364.4,
},
APIOpts: map[string]any{
"*usage": "12m",
},
}
var dec decimal.Big
dec.SetFloat64(43364.4)
fltrs := NewFilterS(cgrCfg, nil, nil)
if rcv, err := GetDecimalBigOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.RouteSCfg().Opts.Usage); err != nil {
t.Error(err)
} else if rcv.Cmp(&dec) != 0 {
t.Errorf("expected %v,received %v", dec.String(), rcv.String())
}
ev.Event[utils.AccountField] = 1002
dec.SetUint64(15555)
if rcv, err := GetDecimalBigOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.RouteSCfg().Opts.Usage); err != nil {
t.Error(err)
} else if rcv.Cmp(&dec) != 0 {
t.Errorf("expected %v,received %v", dec.String(), rcv.String())
}
ev.Event[utils.AccountField] = 1003
dec = *decimal.New(int64(5*time.Minute), 0)
if rcv, err := GetDecimalBigOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.RouteSCfg().Opts.Usage); err != nil {
t.Error(err)
} else if rcv.Cmp(&dec) != 0 {
t.Errorf("expected %v,received %v", dec.String(), rcv.String())
}
ev.Event[utils.AccountField] = 1004
dec = *decimal.New(int64(12*time.Minute), 0)
if rcv, err := GetDecimalBigOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.RouteSCfg().Opts.Usage); err != nil {
t.Error(err)
} else if rcv.Cmp(&dec) != 0 {
t.Errorf("expected %v,received %v", dec.String(), rcv.String())
}
ev.Event[utils.AccountField] = 1005
if rcv, err := GetDecimalBigOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.RouteSCfg().Opts.Usage); err != nil {
t.Error(err)
} else if rcv.Cmp(config.RatesUsageDftOpt) != 0 {
t.Errorf("expected %v,received %v", config.RatesUsageDftOpt.String(), rcv.String())
}
}
func TestLibFiltersGetDecimalBigOptsFilterCheckErr(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
strOpts := []*config.DynamicInterfaceOpt{
// function will return error after trying to parse the filter
{
FilterIDs: []string{"*string.invalid:filter"},
Tenant: "cgrates.org",
Value: -1,
},
}
dynOpts, _ := config.IfaceToDecimalBigDynamicOpts(strOpts)
experr := `inline parse error for string: <*string.invalid:filter>`
if _, err := GetDecimalBigOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
utils.OptsRatesUsage); err == nil ||
err.Error() != experr {
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
}
}
func TestLibFiltersGetDecimalBigOptsReturnDefaultOpt(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
strOpts := []*config.DynamicInterfaceOpt{
{
FilterIDs: []string{"*string:~*req.Account:1002"},
Tenant: "cgrates.org",
Value: -1,
},
{Value: config.RatesUsageDftOpt},
}
dynOpts, err := config.IfaceToDecimalBigDynamicOpts(strOpts)
if err != nil {
t.Fatal(err)
}
if rcv, err := GetDecimalBigOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
utils.OptsRatesUsage); err != nil {
t.Error(err)
} else if rcv.Cmp(config.RatesUsageDftOpt) != 0 {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", config.RatesUsageDftOpt, rcv)
}
}
func TestLibFiltersGetDecimalBigOptsReturnOptFromAPIOpts(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{
utils.OptsRatesUsage: decimal.New(4321, 5),
},
}
strOpts := []*config.DynamicInterfaceOpt{
// will never get to this opt because it will return once it
// finds the one set in APIOpts
{
FilterIDs: []string{"*string:~*req.Account:1001"},
Tenant: "cgrates.org",
Value: decimal.New(1234, 5),
},
}
dynOpts, _ := config.IfaceToDecimalBigDynamicOpts(strOpts)
expected := decimal.New(4321, 5)
if rcv, err := GetDecimalBigOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
"nonExistingAPIOpt", utils.OptsRatesUsage); err != nil {
t.Error(err)
} else if rcv.Cmp(expected) != 0 {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetIntPointerOptsReturnConfigOpt(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicIntPointerOpt{
// tenant will not be recognized, will ignore this opt
config.NewDynamicIntPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.net", utils.IntPointer(3), nil),
// filter will not pass, will ignore this opt
config.NewDynamicIntPointerOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", utils.IntPointer(4), nil),
config.NewDynamicIntPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", utils.IntPointer(5), nil),
}
expected := 5
if rcv, err := GetIntPointerOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
utils.OptsRoutesProfilesCount); err != nil {
t.Error(err)
} else if *rcv != expected {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, *rcv)
}
}
func TestLibFiltersGetIntPointerOptsFilterCheckErr(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicIntPointerOpt{
// function will return error after trying to parse the filter
config.NewDynamicIntPointerOpt([]string{"*string.invalid:filter"}, "cgrates.org", utils.IntPointer(4), nil),
}
experr := `inline parse error for string: <*string.invalid:filter>`
if _, err := GetIntPointerOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
utils.OptsRoutesProfilesCount); err == nil ||
err.Error() != experr {
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
}
}
func TestLibFiltersGetIntPointerOptsReturnDft(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicIntPointerOpt{
// filter will not pass, will ignore this opt
config.NewDynamicIntPointerOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", utils.IntPointer(4), nil),
}
if rcv, err := GetIntPointerOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
utils.OptsRoutesProfilesCount); err != nil {
t.Error(err)
} else if rcv != nil {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", nil, rcv)
}
}
func TestLibFiltersGetIntPointerOptsReturnOptFromAPIOptsOK(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{
utils.OptsRoutesProfilesCount: 6,
},
}
dynOpts := []*config.DynamicIntPointerOpt{
// will never get to this opt because it will return once it
// finds the one set in APIOpts
config.NewDynamicIntPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", utils.IntPointer(5), nil),
}
expected := 6
if rcv, err := GetIntPointerOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
"nonExistingAPIOpt", utils.OptsRoutesProfilesCount); err != nil {
t.Error(err)
} else if *rcv != expected {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetIntPointerOptsReturnOptFromAPIOptsErr(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{
utils.OptsRoutesProfilesCount: true,
},
}
dynOpts := []*config.DynamicIntPointerOpt{
// will never get to this opt because it will return once it
// finds the one set in APIOpts
config.NewDynamicIntPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", utils.IntPointer(5), nil),
}
experr := `cannot convert field<bool>: true to int`
if _, err := GetIntPointerOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
"nonExistingAPIOpt", utils.OptsRoutesProfilesCount); err == nil || err.Error() != experr {
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
}
}
func TestLibFiltersGetDurationOptsFromMultipleMapsReturnConfigOpt(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
eventStart := map[string]any{
utils.AccountField: 1001,
}
apiOpts := map[string]any{}
startOpts := map[string]any{}
dynOpts := []*config.DynamicDurationOpt{
// tenant will not be recognized, will ignore this opt
config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1001"}, "cgrates.net", time.Millisecond, nil),
// filter will not pass, will ignore this opt
config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1002"}, "cgrates.net", time.Second, nil),
config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", time.Minute, nil),
}
expected := time.Minute
if rcv, err := GetDurationOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
config.SessionsTTLDftOpt, utils.OptsSesTTL); err != nil {
t.Error(err)
} else if rcv != expected {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetDurationOptsFromMultipleMapsFilterCheckErr(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
eventStart := map[string]any{
utils.AccountField: 1001,
}
apiOpts := map[string]any{}
startOpts := map[string]any{}
dynOpts := []*config.DynamicDurationOpt{
// function will return error after trying to parse the filter
config.NewDynamicDurationOpt([]string{"*string.invalid:filter"}, "cgrates.org", time.Second, nil),
}
experr := `inline parse error for string: <*string.invalid:filter>`
if _, err := GetDurationOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
config.SessionsTTLDftOpt, utils.OptsSesTTL); err == nil ||
err.Error() != experr {
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
}
}
func TestLibFiltersGetDurationOptsFromMultipleMapsErrNotFound(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
eventStart := map[string]any{
utils.AccountField: 1001,
}
apiOpts := map[string]any{}
startOpts := map[string]any{}
dynOpts := []*config.DynamicDurationOpt{
// filter will not pass, will ignore this opt
config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", time.Second, nil),
}
if rcv, err := GetDurationOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
config.SessionsTTLDftOpt, utils.OptsSesTTL); err != nil {
t.Error(err)
} else if rcv != config.SessionsTTLDftOpt {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", config.SessionsTTLDftOpt, rcv)
}
}
func TestLibFiltersGetDurationOptsFromMultipleMapsReturnOptFromAPIOptsOK(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
eventStart := map[string]any{
utils.AccountField: 1001,
}
apiOpts := map[string]any{
utils.OptsSesTTL: time.Hour,
}
startOpts := map[string]any{}
dynOpts := []*config.DynamicDurationOpt{
// will never get to this opt because it will return once it
// finds the one set in APIOpts
config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", time.Minute, nil),
}
expected := time.Hour
if rcv, err := GetDurationOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
config.SessionsTTLDftOpt, utils.OptsSesTTL); err != nil {
t.Error(err)
} else if rcv != expected {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetDurationOptsFromMultipleMapsReturnOptFromAPIOptsErr(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
eventStart := map[string]any{
utils.AccountField: 1001,
}
apiOpts := map[string]any{
utils.OptsSesTTL: true,
}
startOpts := map[string]any{}
dynOpts := []*config.DynamicDurationOpt{
// will never get to this opt because it will return once it
// finds the one set in APIOpts
config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", time.Minute, nil),
}
experr := `cannot convert field: true to time.Duration`
if _, err := GetDurationOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
config.SessionsTTLDftOpt, utils.OptsSesTTL); err == nil || err.Error() != experr {
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
}
}
func TestLibFiltersGetDurationOptsFromMultipleMapsReturnOptFromStartOptsOK(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
eventStart := map[string]any{
utils.AccountField: 1001,
}
apiOpts := map[string]any{}
startOpts := map[string]any{
utils.OptsSesTTL: time.Hour,
}
dynOpts := []*config.DynamicDurationOpt{
// will never get to this opt because it will return once it
// finds the one set in APIOpts
config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", time.Minute, nil),
}
expected := time.Hour
if rcv, err := GetDurationOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
config.SessionsTTLDftOpt, utils.OptsSesTTL); err != nil {
t.Error(err)
} else if rcv != expected {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetDurationOptsFromMultipleMapsReturnOptFromStartOptsErr(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
eventStart := map[string]any{
utils.AccountField: 1001,
}
apiOpts := map[string]any{}
startOpts := map[string]any{
utils.OptsSesTTL: true,
}
dynOpts := []*config.DynamicDurationOpt{
// will never get to this opt because it will return once it
// finds the one set in APIOpts
config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", time.Minute, nil),
}
experr := `cannot convert field: true to time.Duration`
if _, err := GetDurationOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
config.SessionsTTLDftOpt, utils.OptsSesTTL); err == nil || err.Error() != experr {
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
}
}
func TestLibFiltersGetDurationPointerOptsFromMultipleMapsReturnConfigOpt(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
eventStart := map[string]any{
utils.AccountField: 1001,
}
apiOpts := map[string]any{}
startOpts := map[string]any{}
dynOpts := []*config.DynamicDurationPointerOpt{
// tenant will not be recognized, will ignore this opt
config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.net", utils.DurationPointer(time.Millisecond), nil),
// filter will not pass, will ignore this opt
config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", utils.DurationPointer(time.Second), nil),
config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", utils.DurationPointer(time.Minute), nil),
}
expected := time.Minute
if rcv, err := GetDurationPointerOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
utils.OptsSesTTLUsage); err != nil {
t.Error(err)
} else if *rcv != expected {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetDurationPointerOptsFromMultipleMapsFilterCheckErr(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
eventStart := map[string]any{
utils.AccountField: 1001,
}
apiOpts := map[string]any{}
startOpts := map[string]any{}
dynOpts := []*config.DynamicDurationPointerOpt{
// function will return error after trying to parse the filter
config.NewDynamicDurationPointerOpt([]string{"*string.invalid:filter"}, "cgrates.org", utils.DurationPointer(time.Second), nil),
}
experr := `inline parse error for string: <*string.invalid:filter>`
if _, err := GetDurationPointerOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
utils.OptsSesTTLUsage); err == nil ||
err.Error() != experr {
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
}
}
func TestLibFiltersGetDurationPointerOptsFromMultipleMapsReturnDft(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
eventStart := map[string]any{
utils.AccountField: 1001,
}
apiOpts := map[string]any{}
startOpts := map[string]any{}
dynOpts := []*config.DynamicDurationPointerOpt{
// filter will not pass, will ignore this opt
config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", utils.DurationPointer(time.Second), nil),
}
if _, err := GetDurationPointerOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
utils.OptsSesTTLUsage); err != nil {
t.Error(err)
}
}
func TestLibFiltersGetDurationPointerOptsFromMultipleMapsReturnOptFromAPIOptsOK(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
eventStart := map[string]any{
utils.AccountField: 1001,
}
apiOpts := map[string]any{
utils.OptsSesTTLUsage: time.Hour,
}
startOpts := map[string]any{}
dynOpts := []*config.DynamicDurationPointerOpt{
// will never get to this opt because it will return once it
// finds the one set in APIOpts
config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", utils.DurationPointer(time.Minute), nil),
}
expected := time.Hour
if rcv, err := GetDurationPointerOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
utils.OptsSesTTLUsage); err != nil {
t.Error(err)
} else if *rcv != expected {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetDurationPointerOptsFromMultipleMapsReturnOptFromAPIOptsErr(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
eventStart := map[string]any{
utils.AccountField: 1001,
}
apiOpts := map[string]any{
utils.OptsSesTTLUsage: true,
}
startOpts := map[string]any{}
dynOpts := []*config.DynamicDurationPointerOpt{
// will never get to this opt because it will return once it
// finds the one set in APIOpts
config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", utils.DurationPointer(time.Minute), nil),
}
experr := `cannot convert field: true to time.Duration`
if _, err := GetDurationPointerOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
utils.OptsSesTTLUsage); err == nil || err.Error() != experr {
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
}
}
func TestLibFiltersGetDurationPointerOptsFromMultipleMapsReturnOptFromStartOptsOK(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
eventStart := map[string]any{
utils.AccountField: 1001,
}
apiOpts := map[string]any{}
startOpts := map[string]any{
utils.OptsSesTTLUsage: time.Hour,
}
dynOpts := []*config.DynamicDurationPointerOpt{
// will never get to this opt because it will return once it
// finds the one set in APIOpts
config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", utils.DurationPointer(time.Minute), nil),
}
expected := time.Hour
if rcv, err := GetDurationPointerOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
utils.OptsSesTTLUsage); err != nil {
t.Error(err)
} else if *rcv != expected {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetDurationPointerOptsFromMultipleMapsReturnOptFromStartOptsErr(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
eventStart := map[string]any{
utils.AccountField: 1001,
}
apiOpts := map[string]any{}
startOpts := map[string]any{
utils.OptsSesTTLUsage: true,
}
dynOpts := []*config.DynamicDurationPointerOpt{
// will never get to this opt because it will return once it
// finds the one set in APIOpts
config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", utils.DurationPointer(time.Minute), nil),
}
experr := `cannot convert field: true to time.Duration`
if _, err := GetDurationPointerOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
utils.OptsSesTTLUsage); err == nil || err.Error() != experr {
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
}
}
func TestGetBoolOptsFieldAsInterfaceErr(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
dynOpts := []*config.DynamicBoolOpt{
config.NewDynamicBoolOpt([]string{"*string:~*req.Account:1"}, "cgrates.org", false, nil),
}
if _, err := GetBoolOpts(context.Background(), "cgrates.org", new(mockDP), nil, fS, dynOpts,
"nonExistingAPIOpt", utils.MetaProfileIgnoreFilters); err != utils.ErrAccountNotFound {
t.Errorf("Expecting error <%+v>,\n Recevied error <%+v>", utils.ErrAccountNotFound, err)
}
}
func TestGetBoolOptsCantCastErr(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB, _ := NewInternalDB(nil, nil, nil, nil)
dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
dm := NewDataManager(dbCM, cfg, nil)
fS := NewFilterS(cfg, nil, dm)
dynOpts := []*config.DynamicBoolOpt{
config.NewDynamicBoolOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", false, nil),
}
if _, err := GetBoolOpts(context.Background(), "cgrates.org", utils.StringSet{utils.MetaOpts: {}}, nil, fS, dynOpts,
"nonExistingAPIOpt", utils.MetaProfileIgnoreFilters); err.Error() != "cannot convert to map[string]any" {
t.Errorf("Expecting error <%+v>,\n Recevied error <%+v>", utils.ErrCastFailed, err)
}
}
func TestOptIfaceFromDP(t *testing.T) {
tests := []struct {
name string
values []string
exp any
err error
}{
{name: "TestOptBoolValue", values: []string{utils.MetaProfileIgnoreFilters}, exp: true, err: nil},
{name: "TestOptBoolValue", values: []string{utils.OptsRoutesIgnoreErrors}, exp: true, err: nil},
{name: "TestOptFloat64Value", values: []string{utils.OptsAttributesProcessRuns}, exp: 5, err: nil},
{name: "TestOptIntValue", values: []string{utils.OptsResourcesUnits}, exp: float64(23.1), err: nil},
{name: "TestOptIntValue", values: []string{utils.OptsAttributesProfileRuns}, exp: 3, err: nil},
{name: "TestOptStringValue", values: []string{utils.OptsRatesStartTime}, exp: "2021-01-01T00:00:00Z", err: nil},
{name: "TestOptFloat64Value", values: []string{utils.OptsResourcesUsageID}, exp: "RES1", err: nil},
{name: "TestFieldNotExist", values: []string{"field1"}, err: utils.ErrNotFound},
}
cgrEv := utils.MapStorage{
utils.MetaReq: map[string]any{},
utils.MetaOpts: map[string]any{
utils.MetaProfileIgnoreFilters: true,
utils.OptsRoutesIgnoreErrors: true,
utils.OptsAttributesProcessRuns: 5,
utils.OptsAttributesProfileRuns: 3,
utils.OptsRatesStartTime: "2021-01-01T00:00:00Z",
utils.OptsResourcesUsageID: "RES1",
utils.OptsResourcesUnits: 23.1,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if rcv, err := optIfaceFromDP(cgrEv, nil, tt.values); err != tt.err {
t.Errorf("expected: <%v>,\nreceived: <%v>", tt.err, err)
} else if rcv != tt.exp {
t.Errorf("expected: <%v>,\nreceived: <%v>", tt.exp, rcv)
}
})
}
}
func TestConvertOptsToMapStringAny(t *testing.T) {
t.Run("MapEvent_Account", func(t *testing.T) {
accountEvent := MapEvent{
"Account": "1001",
"Tenant": "cgrates.org",
"BalanceMap": map[string]float64{"*monetary": 100.5},
"ActionTriggers": []string{"STANDARD_TRIGGERS"},
}
result, err := ConvertOptsToMapStringAny(accountEvent)
if err != nil {
t.Fatalf("Expected no error, got: %v", err)
}
if len(result) != 4 {
t.Fatalf("Expected 4 keys, got %d", len(result))
}
if result["Account"] != "1001" {
t.Errorf("Expected Account=1001, got %v", result["Account"])
}
if result["Tenant"] != "cgrates.org" {
t.Errorf("Expected Tenant=cgrates.org, got %v", result["Tenant"])
}
})
t.Run("MapEvent_CDR", func(t *testing.T) {
cdrEvent := MapEvent{
"CGRID": "cdr123456789",
"OrderID": 12345,
"OriginHost": "192.168.1.1",
"Source": "SessionS",
"OriginID": "session_001",
"ToR": "*voice",
"RequestType": "*prepaid",
"Tenant": "cgrates.org",
"Category": "call",
"Account": "1001",
"Subject": "1001",
"Destination": "+4986517174963",
"SetupTime": "2024-01-15T10:30:00Z",
"AnswerTime": "2024-01-15T10:30:05Z",
"Usage": 300.0,
"Cost": 2.5,
}
result, err := ConvertOptsToMapStringAny(cdrEvent)
if err != nil {
t.Fatalf("Expected no error, got: %v", err)
}
if result["CGRID"] != "cdr123456789" {
t.Errorf("Expected CGRID=cdr123456789, got %v", result["CGRID"])
}
if result["Account"] != "1001" {
t.Errorf("Expected Account=1001, got %v", result["Account"])
}
if result["Usage"] != 300.0 {
t.Errorf("Expected Usage=300.0, got %v", result["Usage"])
}
})
t.Run("MapStorage_RatingProfile", func(t *testing.T) {
ratingStorage := utils.MapStorage{
"Tenant": "cgrates.org",
"Category": "call",
"Subject": "*any",
"RatingPlanActivations": []map[string]any{
{
"ActivationTime": "2024-01-01T00:00:00Z",
"RatingPlanId": "RP_RETAIL",
"FallbackKeys": []string{"*any"},
},
},
}
result, err := ConvertOptsToMapStringAny(ratingStorage)
if err != nil {
t.Fatalf("Expected no error, got: %v", err)
}
if result["Tenant"] != "cgrates.org" {
t.Errorf("Expected Tenant=cgrates.org, got %v", result["Tenant"])
}
if result["Subject"] != "*any" {
t.Errorf("Expected Subject=*any, got %v", result["Subject"])
}
})
t.Run("MapStorage_Destination", func(t *testing.T) {
destStorage := utils.MapStorage{
"Id": "DST_GERMANY",
"Prefixes": []string{"+49", "+4915", "+4916"},
}
result, err := ConvertOptsToMapStringAny(destStorage)
if err != nil {
t.Fatalf("Expected no error, got: %v", err)
}
if result["Id"] != "DST_GERMANY" {
t.Errorf("Expected Id=DST_GERMANY, got %v", result["Id"])
}
prefixes, ok := result["Prefixes"].([]string)
if !ok || len(prefixes) != 3 {
t.Errorf("Expected Prefixes to be []string with 3 elements, got %v", result["Prefixes"])
}
})
t.Run("DirectMapStringAny", func(t *testing.T) {
directMap := map[string]any{
"ActionId": "TOPUP_MONETARY_10",
"ActionType": "*topup_reset",
"ExtraParameters": map[string]string{
"*balance_type": "*monetary",
"*units": "10",
},
}
result, err := ConvertOptsToMapStringAny(directMap)
if err != nil {
t.Fatalf("Expected no error, got: %v", err)
}
if !reflect.DeepEqual(result, directMap) {
t.Errorf("Expected result to be identical to input map")
}
if result["ActionId"] != "TOPUP_MONETARY_10" {
t.Errorf("Expected ActionId=TOPUP_MONETARY_10, got %v", result["ActionId"])
}
})
t.Run("EmptyMapEvent", func(t *testing.T) {
emptyEvent := MapEvent{}
result, err := ConvertOptsToMapStringAny(emptyEvent)
if err != nil {
t.Fatalf("Expected no error, got: %v", err)
}
if len(result) != 0 {
t.Errorf("Expected empty map, got %v", result)
}
})
t.Run("EmptyMapStorage", func(t *testing.T) {
emptyStorage := utils.MapStorage{}
result, err := ConvertOptsToMapStringAny(emptyStorage)
if err != nil {
t.Fatalf("Expected no error, got: %v", err)
}
if len(result) != 0 {
t.Errorf("Expected empty map, got %v", result)
}
})
t.Run("UnsupportedType_String", func(t *testing.T) {
_, err := ConvertOptsToMapStringAny("invalid_string")
if err == nil {
t.Fatal("Expected error for unsupported type, got nil")
}
expectedErr := "cannot convert to map[string]any"
if err.Error() != expectedErr {
t.Errorf("Expected error '%s', got '%s'", expectedErr, err.Error())
}
})
t.Run("UnsupportedType_Int", func(t *testing.T) {
_, err := ConvertOptsToMapStringAny(12345)
if err == nil {
t.Fatal("Expected error for unsupported type, got nil")
}
})
t.Run("UnsupportedType_Slice", func(t *testing.T) {
_, err := ConvertOptsToMapStringAny([]string{"account1", "account2"})
if err == nil {
t.Fatal("Expected error for unsupported type, got nil")
}
})
t.Run("MapEvent_Session", func(t *testing.T) {
sessionEvent := MapEvent{
"CGRID": "session_abc123",
"Tenant": "cgrates.org",
"Account": "1002",
"Destination": "+4986517174999",
"InitialMaxUsage": 3600.0,
"MaxUsage": 1800.0,
"LastUsed": 600.0,
"LoopIndex": 0,
"DurationIndex": 600.0,
}
result, err := ConvertOptsToMapStringAny(sessionEvent)
if err != nil {
t.Fatalf("Expected no error, got: %v", err)
}
if result["Account"] != "1002" {
t.Errorf("Expected Account=1002, got %v", result["Account"])
}
if result["MaxUsage"] != 1800.0 {
t.Errorf("Expected MaxUsage=1800.0, got %v", result["MaxUsage"])
}
})
t.Run("MapStorage_ActionPlan", func(t *testing.T) {
actionPlanStorage := utils.MapStorage{
"Id": "PACKAGE_10_SHARED_A_5",
"ActionTimings": []map[string]any{
{
"Uuid": "088fc6cc-192c-4d8e-9f5e-7b9c3d2c5b8d",
"Timing": "*asap",
"ActionsId": "TOPUP_RST_10",
"Weight": 10.0,
},
},
}
result, err := ConvertOptsToMapStringAny(actionPlanStorage)
if err != nil {
t.Fatalf("Expected no error, got: %v", err)
}
if result["Id"] != "PACKAGE_10_SHARED_A_5" {
t.Errorf("Expected Id=PACKAGE_10_SHARED_A_5, got %v", result["Id"])
}
})
t.Run("MapEvent_ResourceEvent", func(t *testing.T) {
resourceEvent := MapEvent{
"Tenant": "cgrates.org",
"ID": "RES_GRP_1",
"UsageID": "call_usage_001",
"ResourceID": "RES_ACNT_1001",
"Units": 1.0,
"Account": "1001",
"Destination": "+4986517174963",
}
result, err := ConvertOptsToMapStringAny(resourceEvent)
if err != nil {
t.Fatalf("Expected no error, got: %v", err)
}
if result["ID"] != "RES_GRP_1" {
t.Errorf("Expected ID=RES_GRP_1, got %v", result["ID"])
}
if result["Units"] != 1.0 {
t.Errorf("Expected Units=1.0, got %v", result["Units"])
}
})
t.Run("MapStorage_ThresholdProfile", func(t *testing.T) {
thresholdStorage := utils.MapStorage{
"Tenant": "cgrates.org",
"ID": "THD_ACNT_1001",
"FilterIDs": []string{"*string:~*req.Account:1001"},
"ActivationInterval": map[string]any{
"ActivationTime": "2024-01-01T00:00:00Z",
"ExpiryTime": "*never",
},
"MaxHits": -1,
"MinHits": 1,
"MinSleep": "1s",
"Blocker": false,
"Weight": 20.0,
"ActionIDs": []string{"LOG_WARNING"},
}
result, err := ConvertOptsToMapStringAny(thresholdStorage)
if err != nil {
t.Fatalf("Expected no error, got: %v", err)
}
if result["ID"] != "THD_ACNT_1001" {
t.Errorf("Expected ID=THD_ACNT_1001, got %v", result["ID"])
}
if result["Weight"] != 20.0 {
t.Errorf("Expected Weight=20.0, got %v", result["Weight"])
}
})
}