diff --git a/utils/options_test.go b/utils/options_test.go
new file mode 100644
index 000000000..422c5da5c
--- /dev/null
+++ b/utils/options_test.go
@@ -0,0 +1,386 @@
+/*
+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 utils
+
+import (
+ "reflect"
+ "testing"
+ "time"
+
+ "github.com/ericlagergren/decimal"
+)
+
+func TestOptionsGetFloat64Opts(t *testing.T) {
+
+ // option key not populated, retrieve default value
+ ev := &CGREvent{
+ APIOpts: make(map[string]interface{}),
+ }
+ if rcv, err := GetFloat64Opts(ev, 1.2, "optionName"); err != nil {
+ t.Error(err)
+ } else if rcv != 1.2 {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", 1.2, rcv)
+ }
+
+ // option key populated with valid input, get its value
+ ev.APIOpts["optionName"] = 0.11
+ if rcv, err := GetFloat64Opts(ev, 1.2, "optionName"); err != nil {
+ t.Error(err)
+ } else if rcv != 0.11 {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", 0.11, rcv)
+ }
+
+ // option key populated with invalid input, receive error
+ expectedErr := `strconv.ParseFloat: parsing "invalid": invalid syntax`
+ ev.APIOpts["optionName"] = "invalid"
+ if _, err := GetFloat64Opts(ev, 1.2, "optionName"); err == nil ||
+ err.Error() != expectedErr {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", expectedErr, err)
+ }
+}
+
+func TestOptionsGetDurationOpts(t *testing.T) {
+
+ // option key not populated, retrieve default value
+ ev := &CGREvent{
+ APIOpts: make(map[string]interface{}),
+ }
+ if rcv, err := GetDurationOpts(ev, time.Minute, "optionName"); err != nil {
+ t.Error(err)
+ } else if rcv != time.Minute {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", time.Minute, rcv)
+ }
+
+ // option key populated with valid input, get its value
+ ev.APIOpts["optionName"] = 2 * time.Second
+ if rcv, err := GetDurationOpts(ev, time.Minute, "optionName"); err != nil {
+ t.Error(err)
+ } else if rcv != 2*time.Second {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", 2*time.Second, rcv)
+ }
+ ev.APIOpts["optionName"] = 600
+ if rcv, err := GetDurationOpts(ev, time.Minute, "optionName"); err != nil {
+ t.Error(err)
+ } else if rcv != 600 {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", 600, rcv)
+ }
+ ev.APIOpts["optionName"] = "2m0s"
+ if rcv, err := GetDurationOpts(ev, time.Minute, "optionName"); err != nil {
+ t.Error(err)
+ } else if rcv != 2*time.Minute {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", 2*time.Minute, rcv)
+ }
+
+ // option key populated with invalid input, receive error
+ expectedErr := `time: invalid duration "invalid"`
+ ev.APIOpts["optionName"] = "invalid"
+ if _, err := GetDurationOpts(ev, time.Minute, "optionName"); err == nil ||
+ err.Error() != expectedErr {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", expectedErr, err)
+ }
+}
+
+func TestOptionsGetStringOpts(t *testing.T) {
+
+ // option key not populated, retrieve default value
+ ev := &CGREvent{
+ APIOpts: make(map[string]interface{}),
+ }
+ if rcv := GetStringOpts(ev, "default", "optionName"); rcv != "default" {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", "default", rcv)
+ }
+
+ // option key populated, get its value
+ ev.APIOpts["optionName"] = "optionValue"
+ if rcv := GetStringOpts(ev, "default", "optionName"); rcv != "optionValue" {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", "optionValue", rcv)
+ }
+ ev.APIOpts["optionName"] = false
+ if rcv := GetStringOpts(ev, "default", "optionName"); rcv != "false" {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", "false", rcv)
+ }
+ ev.APIOpts["optionName"] = 5 * time.Minute
+ if rcv := GetStringOpts(ev, "default", "optionName"); rcv != "5m0s" {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", "5m0s", rcv)
+ }
+ ev.APIOpts["optionName"] = 12.34
+ if rcv := GetStringOpts(ev, "default", "optionName"); rcv != "12.34" {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", "12.34", rcv)
+ }
+}
+
+func TestOptionsGetStringSliceOpts(t *testing.T) {
+
+ // option key not populated, retrieve default value
+ ev := &CGREvent{
+ APIOpts: make(map[string]interface{}),
+ }
+ defaultValue := []string{"default"}
+ if rcv, err := GetStringSliceOpts(ev, defaultValue, "optionName"); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(rcv, defaultValue) {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", defaultValue, rcv)
+ }
+
+ // option key populated with valid input, get its value
+ optValue := []string{"optValue"}
+ ev.APIOpts["optionName"] = optValue
+ if rcv, err := GetStringSliceOpts(ev, defaultValue, "optionName"); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(rcv, optValue) {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", optValue, rcv)
+ }
+ optValue2 := []string{"true", "false"}
+ ev.APIOpts["optionName"] = []bool{true, false}
+ if rcv, err := GetStringSliceOpts(ev, defaultValue, "optionName"); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(rcv, optValue2) {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", optValue2, rcv)
+ }
+ optValue3 := []string{"12.34", "3"}
+ ev.APIOpts["optionName"] = []float64{12.34, 3}
+ if rcv, err := GetStringSliceOpts(ev, defaultValue, "optionName"); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(rcv, optValue3) {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", optValue3, rcv)
+ }
+
+ // option key populated with invalid input, receive error
+ expectedErr := `cannot convert field: string to []string`
+ ev.APIOpts["optionName"] = "invalid"
+ if _, err := GetStringSliceOpts(ev, defaultValue, "optionName"); err == nil ||
+ err.Error() != expectedErr {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", expectedErr, err)
+ }
+}
+
+func TestOptionsGetIntOpts(t *testing.T) {
+
+ // option key not populated, retrieve default value
+ ev := &CGREvent{
+ APIOpts: make(map[string]interface{}),
+ }
+ if rcv, err := GetIntOpts(ev, 5, "optionName"); err != nil {
+ t.Error(err)
+ } else if rcv != 5 {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", 5, rcv)
+ }
+
+ // option key populated with valid input, get its value
+ ev.APIOpts["optionName"] = 12
+ if rcv, err := GetIntOpts(ev, 5, "optionName"); err != nil {
+ t.Error(err)
+ } else if rcv != 12 {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", 12, rcv)
+ }
+ ev.APIOpts["optionName"] = 12.7
+ if rcv, err := GetIntOpts(ev, 5, "optionName"); err != nil {
+ t.Error(err)
+ } else if rcv != 12 {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", 12, rcv)
+ }
+
+ // option key populated with invalid input, receive error
+ expectedErr := `strconv.ParseInt: parsing "invalid": invalid syntax`
+ ev.APIOpts["optionName"] = "invalid"
+ if _, err := GetIntOpts(ev, 5, "optionName"); err == nil ||
+ err.Error() != expectedErr {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", expectedErr, err)
+ }
+}
+
+func TestOptionsGetBoolOpts(t *testing.T) {
+
+ // option key not populated, retrieve default value
+ ev := &CGREvent{
+ APIOpts: make(map[string]interface{}),
+ }
+ if rcv, err := GetBoolOpts(ev, false, "optionName"); err != nil {
+ t.Error(err)
+ } else if rcv != false {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", false, rcv)
+ }
+
+ // option key populated with valid input, get its value
+ ev.APIOpts["optionName"] = true
+ if rcv, err := GetBoolOpts(ev, false, "optionName"); err != nil {
+ t.Error(err)
+ } else if rcv != true {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", true, rcv)
+ }
+ ev.APIOpts["optionName"] = 5
+ if rcv, err := GetBoolOpts(ev, false, "optionName"); err != nil {
+ t.Error(err)
+ } else if rcv != true {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", true, rcv)
+ }
+ ev.APIOpts["optionName"] = "true"
+ if rcv, err := GetBoolOpts(ev, false, "optionName"); err != nil {
+ t.Error(err)
+ } else if rcv != true {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", true, rcv)
+ }
+
+ // option key populated with invalid input, receive error
+ expectedErr := `strconv.ParseBool: parsing "invalid": invalid syntax`
+ ev.APIOpts["optionName"] = "invalid"
+ if _, err := GetBoolOpts(ev, false, "optionName"); err == nil ||
+ err.Error() != expectedErr {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", expectedErr, err)
+ }
+}
+
+func TestOptionsGetInterfaceOpts(t *testing.T) {
+
+ // option key not populated, retrieve default value
+ ev := &CGREvent{
+ APIOpts: make(map[string]interface{}),
+ }
+ if rcv := GetInterfaceOpts(ev, "default", "optionName"); rcv != "default" {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", "default", rcv)
+ }
+
+ // option key populated, get its value
+ ev.APIOpts["optionName"] = 0.11
+ if rcv := GetInterfaceOpts(ev, "default", "optionName"); rcv != 0.11 {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", 0.11, rcv)
+ }
+ ev.APIOpts["optionName"] = true
+ if rcv := GetInterfaceOpts(ev, "default", "optionName"); rcv != true {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", true, rcv)
+ }
+ ev.APIOpts["optionName"] = 5 * time.Minute
+ if rcv := GetInterfaceOpts(ev, "default", "optionName"); rcv != 5*time.Minute {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", 5*time.Minute, rcv)
+ }
+}
+
+func TestOptionsGetIntPointerOpts(t *testing.T) {
+ defaultValue := IntPointer(5)
+
+ // option key not populated, retrieve default value
+ ev := &CGREvent{
+ APIOpts: make(map[string]interface{}),
+ }
+ if rcv, err := GetIntPointerOpts(ev, defaultValue, "optionName"); err != nil {
+ t.Error(err)
+ } else if rcv == nil || rcv != defaultValue {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", defaultValue, rcv)
+ }
+
+ // option key populated with valid input, get its value
+ ev.APIOpts["optionName"] = 12
+ if rcv, err := GetIntPointerOpts(ev, defaultValue, "optionName"); err != nil {
+ t.Error(err)
+ } else if rcv == nil || *rcv != 12 {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", 12, rcv)
+ }
+ ev.APIOpts["optionName"] = 12.7
+ if rcv, err := GetIntPointerOpts(ev, defaultValue, "optionName"); err != nil {
+ t.Error(err)
+ } else if rcv == nil || *rcv != 12 {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", 12, rcv)
+ }
+
+ // option key populated with invalid input, receive error
+ expectedErr := `strconv.ParseInt: parsing "invalid": invalid syntax`
+ ev.APIOpts["optionName"] = "invalid"
+ if _, err := GetIntPointerOpts(ev, defaultValue, "optionName"); err == nil ||
+ err.Error() != expectedErr {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", expectedErr, err)
+ }
+}
+
+func TestOptionsGetDurationPointerOpts(t *testing.T) {
+ defaultValue := DurationPointer(time.Minute)
+ // option key not populated, retrieve default value
+ ev := &CGREvent{
+ APIOpts: make(map[string]interface{}),
+ }
+ if rcv, err := GetDurationPointerOpts(ev, defaultValue, "optionName"); err != nil {
+ t.Error(err)
+ } else if rcv == nil || rcv != defaultValue {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", defaultValue, rcv)
+ }
+
+ // option key populated with valid input, get its value
+ ev.APIOpts["optionName"] = 2 * time.Second
+ if rcv, err := GetDurationPointerOpts(ev, defaultValue, "optionName"); err != nil {
+ t.Error(err)
+ } else if rcv == nil || *rcv != 2*time.Second {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", 2*time.Second, rcv)
+ }
+ ev.APIOpts["optionName"] = 600
+ if rcv, err := GetDurationPointerOpts(ev, defaultValue, "optionName"); err != nil {
+ t.Error(err)
+ } else if rcv == nil || *rcv != 600 {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", 600, rcv)
+ }
+ ev.APIOpts["optionName"] = "2m0s"
+ if rcv, err := GetDurationPointerOpts(ev, defaultValue, "optionName"); err != nil {
+ t.Error(err)
+ } else if rcv == nil || *rcv != 2*time.Minute {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", 2*time.Minute, rcv)
+ }
+
+ // option key populated with invalid input, receive error
+ expectedErr := `time: invalid duration "invalid"`
+ ev.APIOpts["optionName"] = "invalid"
+ if _, err := GetDurationPointerOpts(ev, defaultValue, "optionName"); err == nil ||
+ err.Error() != expectedErr {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", expectedErr, err)
+ }
+}
+
+func TestOptionsGetDecimalBigOpts(t *testing.T) {
+ defaultValue := decimal.New(1234, 3)
+
+ // option key not populated, retrieve default value
+ ev := &CGREvent{
+ APIOpts: make(map[string]interface{}),
+ }
+ if rcv, err := GetDecimalBigOpts(ev, defaultValue, "optionName"); err != nil {
+ t.Error(err)
+ } else if rcv.Cmp(defaultValue) != 0 {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", defaultValue, rcv)
+ }
+
+ // option key populated with valid input, get its value
+ optValue := decimal.New(15, 1)
+ ev.APIOpts["optionName"] = 1.5
+ if rcv, err := GetDecimalBigOpts(ev, defaultValue, "optionName"); err != nil {
+ t.Error(err)
+ } else if rcv.Cmp(optValue) != 0 {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", optValue, rcv)
+ }
+ ev.APIOpts["optionName"] = "1.5"
+ if rcv, err := GetDecimalBigOpts(ev, defaultValue, "optionName"); err != nil {
+ t.Error(err)
+ } else if rcv.Cmp(optValue) != 0 {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", optValue, rcv)
+ }
+
+ // option key populated with invalid input, receive error
+ expectedErr := `can't convert to decimal`
+ ev.APIOpts["optionName"] = "invalid"
+ if _, err := GetDecimalBigOpts(ev, defaultValue, "optionName"); err == nil ||
+ err.Error() != expectedErr {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", expectedErr, err)
+ }
+}