/* Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments Copyright (C) ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see */ package engine import ( "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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicFloat64Opt{ // tenant will not be recognized, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1001"}, Tenant: "cgrates.net", Value: 3, }, // filter will not pass, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1002"}, Tenant: "cgrates.org", Value: 4, }, { FilterIDs: []string{"*string:~*req.Account:1001"}, Tenant: "cgrates.org", Value: 5, }, } expected := 5. if rcv, err := GetFloat64Opts(context.Background(), "cgrates.org", ev, fS, dynOpts, config.ResourcesUnitsDftOpt, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicFloat64Opt{ // function will return error after trying to parse the filter { FilterIDs: []string{"*string.invalid:filter"}, Tenant: "cgrates.org", Value: 4, }, } experr := `inline parse error for string: <*string.invalid:filter>` if _, err := GetFloat64Opts(context.Background(), "cgrates.org", ev, fS, dynOpts, config.ResourcesUnitsDftOpt, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicFloat64Opt{ // filter will not pass, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1002"}, Tenant: "cgrates.org", Value: 4, }, } if rcv, err := GetFloat64Opts(context.Background(), "cgrates.org", ev, fS, dynOpts, config.ResourcesUnitsDftOpt, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{ utils.OptsResourcesUnits: 6, }, } dynOpts := []*utils.DynamicFloat64Opt{ // 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: 5, }, } expected := 6. if rcv, err := GetFloat64Opts(context.Background(), "cgrates.org", ev, fS, dynOpts, config.ResourcesUnitsDftOpt, "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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicStringOpt{ // tenant will not be recognized, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1001"}, Tenant: "cgrates.net", Value: "value1", }, // 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 := GetStringOpts(context.Background(), "cgrates.org", ev, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicStringOpt{ // function will return error after trying to parse the filter { FilterIDs: []string{"*string.invalid:filter"}, Tenant: "cgrates.org", Value: "value2", }, } experr := `inline parse error for string: <*string.invalid:filter>` if _, err := GetStringOpts(context.Background(), "cgrates.org", ev, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicStringOpt{ // filter will not pass, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1002"}, Tenant: "cgrates.org", Value: "value2", }, } if rcv, err := GetStringOpts(context.Background(), "cgrates.org", ev, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{ utils.OptsResourcesUsageID: "value4", }, } dynOpts := []*utils.DynamicStringOpt{ // 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 := "value4" if rcv, err := GetStringOpts(context.Background(), "cgrates.org", ev, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicDurationOpt{ // tenant will not be recognized, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1001"}, Tenant: "cgrates.net", Value: time.Millisecond, }, // filter will not pass, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1002"}, Tenant: "cgrates.org", Value: time.Second, }, { FilterIDs: []string{"*string:~*req.Account:1001"}, Tenant: "cgrates.org", Value: time.Minute, }, } expected := time.Minute if rcv, err := GetDurationOpts(context.Background(), "cgrates.org", ev, fS, dynOpts, config.ResourcesUsageTTLDftOpt, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicDurationOpt{ // function will return error after trying to parse the filter { FilterIDs: []string{"*string.invalid:filter"}, Tenant: "cgrates.org", Value: time.Second, }, } experr := `inline parse error for string: <*string.invalid:filter>` if _, err := GetDurationOpts(context.Background(), "cgrates.org", ev, fS, dynOpts, config.ResourcesUsageTTLDftOpt, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicDurationOpt{ // filter will not pass, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1002"}, Tenant: "cgrates.org", Value: time.Second, }, } if rcv, err := GetDurationOpts(context.Background(), "cgrates.org", ev, fS, dynOpts, config.ResourcesUsageTTLDftOpt, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{ utils.OptsResourcesUsageTTL: time.Hour, }, } dynOpts := []*utils.DynamicDurationOpt{ // 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: time.Minute, }, } expected := time.Hour if rcv, err := GetDurationOpts(context.Background(), "cgrates.org", ev, fS, dynOpts, config.ResourcesUsageTTLDftOpt, "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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicIntOpt{ // tenant will not be recognized, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1001"}, Tenant: "cgrates.net", Value: 3, }, // filter will not pass, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1002"}, Tenant: "cgrates.org", Value: 4, }, { FilterIDs: []string{"*string:~*req.Account:1001"}, Tenant: "cgrates.org", Value: 5, }, } expected := 5 if rcv, err := GetIntOpts(context.Background(), "cgrates.org", ev, fS, dynOpts, config.AttributesProcessRunsDftOpt, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicIntOpt{ // function will return error after trying to parse the filter { FilterIDs: []string{"*string.invalid:filter"}, Tenant: "cgrates.org", Value: 4, }, } experr := `inline parse error for string: <*string.invalid:filter>` if _, err := GetIntOpts(context.Background(), "cgrates.org", ev, fS, dynOpts, config.AttributesProcessRunsDftOpt, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicIntOpt{ // filter will not pass, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1002"}, Tenant: "cgrates.org", Value: 4, }, } if rcv, err := GetIntOpts(context.Background(), "cgrates.org", ev, fS, dynOpts, config.AttributesProcessRunsDftOpt, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{ utils.OptsAttributesProcessRuns: 6, }, } dynOpts := []*utils.DynamicIntOpt{ // 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: 5, }, } expected := 6 if rcv, err := GetIntOpts(context.Background(), "cgrates.org", ev, fS, dynOpts, config.AttributesProcessRunsDftOpt, "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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{ utils.OptsAttributesProcessRuns: true, }, } dynOpts := []*utils.DynamicIntOpt{ // 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: 5, }, } experr := `cannot convert field: true to int` if _, err := GetIntOpts(context.Background(), "cgrates.org", ev, fS, dynOpts, config.AttributesProcessRunsDftOpt, "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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicStringOpt{ // tenant will not be recognized, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1001"}, Tenant: "cgrates.net", Value: "2022-03-07T15:04:05", }, // filter will not pass, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1002"}, Tenant: "cgrates.org", Value: "*daily", }, { FilterIDs: []string{"*string:~*req.Account:1001"}, Tenant: "cgrates.org", Value: "*monthly", }, } expected := time.Now().AddDate(0, 1, 0) if rcv, err := GetTimeOpts(context.Background(), "cgrates.org", ev, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicStringOpt{ // function will return error after trying to parse the filter { FilterIDs: []string{"*string.invalid:filter"}, Tenant: "cgrates.org", Value: "*daily", }, } experr := `inline parse error for string: <*string.invalid:filter>` if _, err := GetTimeOpts(context.Background(), "cgrates.org", ev, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicStringOpt{ // filter will not pass, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1002"}, Tenant: "cgrates.org", Value: "*daily", }, } expected, err := utils.ParseTimeDetectLayout(config.RatesStartTimeDftOpt, cfg.GeneralCfg().DefaultTimezone) if err != nil { t.Error(err) } if rcv, err := GetTimeOpts(context.Background(), "cgrates.org", ev, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{ utils.OptsRatesStartTime: "*yearly", }, } dynOpts := []*utils.DynamicStringOpt{ // 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: "*monthly", }, } expected := time.Now().AddDate(1, 0, 0) if rcv, err := GetTimeOpts(context.Background(), "cgrates.org", ev, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicBoolOpt{ // tenant will not be recognized, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1001"}, Tenant: "cgrates.net", Value: false, }, // filter will not pass, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1002"}, Tenant: "cgrates.org", Value: false, }, { FilterIDs: []string{"*string:~*req.Account:1001"}, Tenant: "cgrates.org", Value: true, }, } expected := true if rcv, err := GetBoolOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), fS, dynOpts, config.ThresholdsProfileIgnoreFiltersDftOpt, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicBoolOpt{ // function will return error after trying to parse the filter { FilterIDs: []string{"*string.invalid:filter"}, Tenant: "cgrates.org", Value: false, }, } experr := `inline parse error for string: <*string.invalid:filter>` if _, err := GetBoolOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), fS, dynOpts, config.ThresholdsProfileIgnoreFiltersDftOpt, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicBoolOpt{ // filter will not pass, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1002"}, Tenant: "cgrates.org", Value: true, }, } if rcv, err := GetBoolOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), fS, dynOpts, config.ThresholdsProfileIgnoreFiltersDftOpt, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{ utils.MetaProfileIgnoreFilters: true, }, } dynOpts := []*utils.DynamicBoolOpt{ // 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: false, }, } expected := true if rcv, err := GetBoolOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), fS, dynOpts, config.ThresholdsProfileIgnoreFiltersDftOpt, "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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.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, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.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, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicInterfaceOpt{ // filter will not pass, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1002"}, Tenant: "cgrates.org", Value: 2, }, } if rcv, err := GetInterfaceOpts(context.Background(), "cgrates.org", ev, fS, dynOpts, config.RoutesMaxCostDftOpt, utils.OptsRoutesMaxCost); err != nil { t.Error(err) } else if rcv != config.RoutesMaxCostDftOpt { t.Errorf("expected: <%+v>,\nreceived: <%+v>", config.RoutesMaxCostDftOpt, rcv) } } func TestLibFiltersGetInterfaceOptsReturnOptFromAPIOpts(t *testing.T) { cfg := config.NewDefaultCGRConfig() dataDB := NewInternalDB(nil, nil, nil) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{ utils.OptsRoutesMaxCost: 4, }, } dynOpts := []*utils.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, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicStringSliceOpt{ // tenant will not be recognized, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1001"}, Tenant: "cgrates.net", Value: []string{"value1"}, }, // filter will not pass, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1002"}, Tenant: "cgrates.org", Value: []string{"value2"}, }, { FilterIDs: []string{"*string:~*req.Account:1001"}, Tenant: "cgrates.org", Value: []string{"value3"}, }, } expected := []string{"value3"} if rcv, err := GetStringSliceOpts(context.Background(), "cgrates.org", ev, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicStringSliceOpt{ // function will return error after trying to parse the filter { FilterIDs: []string{"*string.invalid:filter"}, Tenant: "cgrates.org", Value: []string{"value2"}, }, } experr := `inline parse error for string: <*string.invalid:filter>` if _, err := GetStringSliceOpts(context.Background(), "cgrates.org", ev, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicStringSliceOpt{ // filter will not pass, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1002"}, Tenant: "cgrates.org", Value: []string{"value2"}, }, } if rcv, err := GetStringSliceOpts(context.Background(), "cgrates.org", ev, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{ utils.OptsAttributesProfileIDs: []string{"value4"}, }, } dynOpts := []*utils.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", Value: []string{"value3"}, }, } expected := []string{"value4"} if rcv, err := GetStringSliceOpts(context.Background(), "cgrates.org", ev, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicDecimalBigOpt{ // tenant will not be recognized, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1001"}, Tenant: "cgrates.net", Value: decimal.New(42, 0), }, // filter will not pass, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1002"}, Tenant: "cgrates.org", Value: decimal.New(-1, 0), }, { FilterIDs: []string{"*string:~*req.Account:1001"}, Tenant: "cgrates.org", Value: decimal.New(1234, 3), }, } expected := decimal.New(1234, 3) if rcv, err := GetDecimalBigOpts(context.Background(), "cgrates.org", ev, fS, dynOpts, config.RatesUsageDftOpt, utils.OptsRatesUsage); err != nil { t.Error(err) } else if rcv.Cmp(expected) != 0 { t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv) } } func TestLibFiltersGetDecimalBigOptsFilterCheckErr(t *testing.T) { cfg := config.NewDefaultCGRConfig() dataDB := NewInternalDB(nil, nil, nil) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicDecimalBigOpt{ // function will return error after trying to parse the filter { FilterIDs: []string{"*string.invalid:filter"}, Tenant: "cgrates.org", Value: decimal.New(-1, 0), }, } experr := `inline parse error for string: <*string.invalid:filter>` if _, err := GetDecimalBigOpts(context.Background(), "cgrates.org", ev, fS, dynOpts, config.RatesUsageDftOpt, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicDecimalBigOpt{ // filter will not pass, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1002"}, Tenant: "cgrates.org", Value: decimal.New(-1, 0), }, } if rcv, err := GetDecimalBigOpts(context.Background(), "cgrates.org", ev, fS, dynOpts, config.RatesUsageDftOpt, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{ utils.OptsRatesUsage: decimal.New(4321, 5), }, } dynOpts := []*utils.DynamicDecimalBigOpt{ // 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, 3), }, } expected := decimal.New(4321, 5) if rcv, err := GetDecimalBigOpts(context.Background(), "cgrates.org", ev, fS, dynOpts, config.RatesUsageDftOpt, "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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicIntPointerOpt{ // tenant will not be recognized, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1001"}, Tenant: "cgrates.net", Value: utils.IntPointer(3), }, // filter will not pass, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1002"}, Tenant: "cgrates.org", Value: utils.IntPointer(4), }, { FilterIDs: []string{"*string:~*req.Account:1001"}, Tenant: "cgrates.org", Value: utils.IntPointer(5), }, } expected := 5 if rcv, err := GetIntPointerOpts(context.Background(), "cgrates.org", ev, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicIntPointerOpt{ // function will return error after trying to parse the filter { FilterIDs: []string{"*string.invalid:filter"}, Tenant: "cgrates.org", Value: utils.IntPointer(4), }, } experr := `inline parse error for string: <*string.invalid:filter>` if _, err := GetIntPointerOpts(context.Background(), "cgrates.org", ev, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{}, } dynOpts := []*utils.DynamicIntPointerOpt{ // filter will not pass, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1002"}, Tenant: "cgrates.org", Value: utils.IntPointer(4), }, } if _, err := GetIntPointerOpts(context.Background(), "cgrates.org", ev, fS, dynOpts, utils.OptsRoutesProfilesCount); err != nil { t.Error(err) } } func TestLibFiltersGetIntPointerOptsReturnOptFromAPIOptsOK(t *testing.T) { cfg := config.NewDefaultCGRConfig() dataDB := NewInternalDB(nil, nil, nil) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{ utils.OptsRoutesProfilesCount: 6, }, } dynOpts := []*utils.DynamicIntPointerOpt{ // 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: utils.IntPointer(5), }, } expected := 6 if rcv, err := GetIntPointerOpts(context.Background(), "cgrates.org", ev, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: "TestEvent", Event: map[string]interface{}{ utils.AccountField: 1001, }, APIOpts: map[string]interface{}{ utils.OptsRoutesProfilesCount: true, }, } dynOpts := []*utils.DynamicIntPointerOpt{ // 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: utils.IntPointer(5), }, } experr := `cannot convert field: true to int` if _, err := GetIntPointerOpts(context.Background(), "cgrates.org", ev, 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) eventStart := map[string]interface{}{ utils.AccountField: 1001, } apiOpts := map[string]interface{}{} startOpts := map[string]interface{}{} dynOpts := []*utils.DynamicDurationOpt{ // tenant will not be recognized, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1001"}, Tenant: "cgrates.net", Value: time.Millisecond, }, // filter will not pass, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1002"}, Tenant: "cgrates.org", Value: time.Second, }, { FilterIDs: []string{"*string:~*req.Account:1001"}, Tenant: "cgrates.org", Value: time.Minute, }, } 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) eventStart := map[string]interface{}{ utils.AccountField: 1001, } apiOpts := map[string]interface{}{} startOpts := map[string]interface{}{} dynOpts := []*utils.DynamicDurationOpt{ // function will return error after trying to parse the filter { FilterIDs: []string{"*string.invalid:filter"}, Tenant: "cgrates.org", Value: time.Second, }, } 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) eventStart := map[string]interface{}{ utils.AccountField: 1001, } apiOpts := map[string]interface{}{} startOpts := map[string]interface{}{} dynOpts := []*utils.DynamicDurationOpt{ // filter will not pass, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1002"}, Tenant: "cgrates.org", Value: time.Second, }, } 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) eventStart := map[string]interface{}{ utils.AccountField: 1001, } apiOpts := map[string]interface{}{ utils.OptsSesTTL: time.Hour, } startOpts := map[string]interface{}{} dynOpts := []*utils.DynamicDurationOpt{ // 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: time.Minute, }, } 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) eventStart := map[string]interface{}{ utils.AccountField: 1001, } apiOpts := map[string]interface{}{ utils.OptsSesTTL: true, } startOpts := map[string]interface{}{} dynOpts := []*utils.DynamicDurationOpt{ // 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: time.Minute, }, } 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) eventStart := map[string]interface{}{ utils.AccountField: 1001, } apiOpts := map[string]interface{}{} startOpts := map[string]interface{}{ utils.OptsSesTTL: time.Hour, } dynOpts := []*utils.DynamicDurationOpt{ // 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: time.Minute, }, } 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) eventStart := map[string]interface{}{ utils.AccountField: 1001, } apiOpts := map[string]interface{}{} startOpts := map[string]interface{}{ utils.OptsSesTTL: true, } dynOpts := []*utils.DynamicDurationOpt{ // 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: time.Minute, }, } 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) eventStart := map[string]interface{}{ utils.AccountField: 1001, } apiOpts := map[string]interface{}{} startOpts := map[string]interface{}{} dynOpts := []*utils.DynamicDurationPointerOpt{ // tenant will not be recognized, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1001"}, Tenant: "cgrates.net", Value: utils.DurationPointer(time.Millisecond), }, // filter will not pass, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1002"}, Tenant: "cgrates.org", Value: utils.DurationPointer(time.Second), }, { FilterIDs: []string{"*string:~*req.Account:1001"}, Tenant: "cgrates.org", Value: utils.DurationPointer(time.Minute), }, } 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) eventStart := map[string]interface{}{ utils.AccountField: 1001, } apiOpts := map[string]interface{}{} startOpts := map[string]interface{}{} dynOpts := []*utils.DynamicDurationPointerOpt{ // function will return error after trying to parse the filter { FilterIDs: []string{"*string.invalid:filter"}, Tenant: "cgrates.org", Value: utils.DurationPointer(time.Second), }, } 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) eventStart := map[string]interface{}{ utils.AccountField: 1001, } apiOpts := map[string]interface{}{} startOpts := map[string]interface{}{} dynOpts := []*utils.DynamicDurationPointerOpt{ // filter will not pass, will ignore this opt { FilterIDs: []string{"*string:~*req.Account:1002"}, Tenant: "cgrates.org", Value: utils.DurationPointer(time.Second), }, } 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) eventStart := map[string]interface{}{ utils.AccountField: 1001, } apiOpts := map[string]interface{}{ utils.OptsSesTTLUsage: time.Hour, } startOpts := map[string]interface{}{} dynOpts := []*utils.DynamicDurationPointerOpt{ // 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: utils.DurationPointer(time.Minute), }, } 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) eventStart := map[string]interface{}{ utils.AccountField: 1001, } apiOpts := map[string]interface{}{ utils.OptsSesTTLUsage: true, } startOpts := map[string]interface{}{} dynOpts := []*utils.DynamicDurationPointerOpt{ // 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: utils.DurationPointer(time.Minute), }, } 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) eventStart := map[string]interface{}{ utils.AccountField: 1001, } apiOpts := map[string]interface{}{} startOpts := map[string]interface{}{ utils.OptsSesTTLUsage: time.Hour, } dynOpts := []*utils.DynamicDurationPointerOpt{ // 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: utils.DurationPointer(time.Minute), }, } 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) dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) fS := NewFilterS(cfg, nil, dm) eventStart := map[string]interface{}{ utils.AccountField: 1001, } apiOpts := map[string]interface{}{} startOpts := map[string]interface{}{ utils.OptsSesTTLUsage: true, } dynOpts := []*utils.DynamicDurationPointerOpt{ // 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: utils.DurationPointer(time.Minute), }, } 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) } }