diff --git a/agents/fsevent_test.go b/agents/fsevent_test.go
index d930d49c7..d811a7703 100644
--- a/agents/fsevent_test.go
+++ b/agents/fsevent_test.go
@@ -18,6 +18,7 @@ along with this program. If not, see
package agents
import (
+ "bytes"
"reflect"
"testing"
"time"
@@ -953,15 +954,20 @@ variable_rtp_audio_rtcp_octet_count: 0`
var fsCdrCfg *config.CGRConfig
timezone := config.CgrConfig().GeneralCfg().DefaultTimezone
fsCdrCfg, _ = config.NewDefaultCGRConfig()
- fsCdr, _ := engine.NewFSCdr(body, fsCdrCfg)
+ newReader := bytes.NewReader(body)
+ fsCdr, err := engine.NewFSCdr(newReader, fsCdrCfg)
+ if err != nil {
+ t.Error(err)
+ }
smGev := engine.NewMapEvent(NewFSEvent(hangUp).AsMapStringInterface(timezone))
sessions.GetSetCGRID(smGev)
smCDR, err := smGev.AsCDR(fsCdrCfg, utils.EmptyString, timezone)
if err != nil {
t.Fatal(err)
}
- fsCDR := fsCdr.AsCDR(timezone)
- if fsCDR.CGRID != smCDR.CGRID {
+ if fsCDR, err := fsCdr.AsCDR(timezone); err != nil {
+ t.Error(err)
+ } else if fsCDR.CGRID != smCDR.CGRID {
t.Errorf("Expecting: %s, received: %s", fsCDR.CGRID, smCDR.CGRID)
}
}
diff --git a/engine/cdrs.go b/engine/cdrs.go
index 8783376e5..b93c61c77 100644
--- a/engine/cdrs.go
+++ b/engine/cdrs.go
@@ -17,7 +17,6 @@ package engine
import (
"fmt"
- "io/ioutil"
"net/http"
"reflect"
"strings"
@@ -29,10 +28,8 @@ import (
"github.com/cgrates/rpcclient"
)
-var cdrServer *CDRServer // Share the server so we can use it in http handlers
-
// cgrCdrHandler handles CDRs received over HTTP REST
-func cgrCdrHandler(w http.ResponseWriter, r *http.Request) {
+func (cdrS *CDRServer) cgrCdrHandler(w http.ResponseWriter, r *http.Request) {
cgrCdr, err := NewCgrCdrFromHttpReq(r)
if err != nil {
utils.Logger.Warning(
@@ -40,7 +37,7 @@ func cgrCdrHandler(w http.ResponseWriter, r *http.Request) {
utils.CDRs, r.Form, err.Error()))
return
}
- cdr, err := cgrCdr.AsCDR(cdrServer.cgrCfg.GeneralCfg().DefaultTimezone)
+ cdr, err := cgrCdr.AsCDR(cdrS.cgrCfg.GeneralCfg().DefaultTimezone)
if err != nil {
utils.Logger.Warning(
fmt.Sprintf("<%s> could not create CDR entry from rawCDR: %+v, err <%s>",
@@ -48,7 +45,7 @@ func cgrCdrHandler(w http.ResponseWriter, r *http.Request) {
return
}
var ignored string
- if err := cdrServer.V1ProcessCDR(&CDRWithOpts{CDR: cdr}, &ignored); err != nil {
+ if err := cdrS.V1ProcessCDR(&CDRWithOpts{CDR: cdr}, &ignored); err != nil {
utils.Logger.Warning(
fmt.Sprintf("<%s> processing CDR: %s, err: <%s>",
utils.CDRs, cdr, err.Error()))
@@ -56,16 +53,20 @@ func cgrCdrHandler(w http.ResponseWriter, r *http.Request) {
}
// fsCdrHandler will handle CDRs received from FreeSWITCH over HTTP-JSON
-func fsCdrHandler(w http.ResponseWriter, r *http.Request) {
- body, _ := ioutil.ReadAll(r.Body)
- fsCdr, err := NewFSCdr(body, cdrServer.cgrCfg)
+func (cdrS *CDRServer) fsCdrHandler(w http.ResponseWriter, r *http.Request) {
+ fsCdr, err := NewFSCdr(r.Body, cdrS.cgrCfg)
+ r.Body.Close()
if err != nil {
utils.Logger.Err(fmt.Sprintf(" Could not create CDR entry: %s", err.Error()))
return
}
- cdr := fsCdr.AsCDR(cdrServer.cgrCfg.GeneralCfg().DefaultTimezone)
+ cdr, err := fsCdr.AsCDR(cdrS.cgrCfg.GeneralCfg().DefaultTimezone)
+ if err != nil {
+ utils.Logger.Err(fmt.Sprintf(" Could not create AsCDR entry: %s", err.Error()))
+ return
+ }
var ignored string
- if err := cdrServer.V1ProcessCDR(&CDRWithOpts{CDR: cdr}, &ignored); err != nil {
+ if err := cdrS.V1ProcessCDR(&CDRWithOpts{CDR: cdr}, &ignored); err != nil {
utils.Logger.Warning(
fmt.Sprintf("<%s> processing CDR: %s, err: <%s>",
utils.CDRs, cdr, err.Error()))
@@ -115,9 +116,8 @@ func (cdrS *CDRServer) ListenAndServe(stopChan chan struct{}) (err error) {
// RegisterHandlersToServer is called by cgr-engine to register HTTP URL handlers
func (cdrS *CDRServer) RegisterHandlersToServer(server *utils.Server) {
- cdrServer = cdrS // Share the server object for handlers
- server.RegisterHttpFunc(cdrS.cgrCfg.HTTPCfg().HTTPCDRsURL, cgrCdrHandler)
- server.RegisterHttpFunc(cdrS.cgrCfg.HTTPCfg().HTTPFreeswitchCDRsURL, fsCdrHandler)
+ server.RegisterHttpFunc(cdrS.cgrCfg.HTTPCfg().HTTPCDRsURL, cdrS.cgrCdrHandler)
+ server.RegisterHttpFunc(cdrS.cgrCfg.HTTPCfg().HTTPFreeswitchCDRsURL, cdrS.fsCdrHandler)
}
// storeSMCost will store a SMCost
diff --git a/engine/fscdr.go b/engine/fscdr.go
index d91843e4a..577115b76 100644
--- a/engine/fscdr.go
+++ b/engine/fscdr.go
@@ -21,6 +21,7 @@ package engine
import (
"encoding/json"
"fmt"
+ "io"
"reflect"
"strconv"
"strings"
@@ -48,20 +49,20 @@ const (
FsIPv4 = "FreeSWITCH-IPv4"
)
-func NewFSCdr(body []byte, cgrCfg *config.CGRConfig) (*FSCdr, error) {
+func NewFSCdr(body io.Reader, cgrCfg *config.CGRConfig) (*FSCdr, error) {
fsCdr := &FSCdr{cgrCfg: cgrCfg, vars: make(map[string]string)}
var err error
- if err = json.Unmarshal(body, &fsCdr.body); err == nil {
- if variables, ok := fsCdr.body[FS_CDR_MAP]; ok {
- if variables, ok := variables.(map[string]interface{}); ok {
- for k, v := range variables {
- fsCdr.vars[k] = v.(string)
- }
+ if err = json.NewDecoder(body).Decode(&fsCdr.body); err != nil {
+ return nil, err
+ }
+ if variables, ok := fsCdr.body[FS_CDR_MAP]; ok {
+ if variables, ok := variables.(map[string]interface{}); ok {
+ for k, v := range variables {
+ fsCdr.vars[k] = v.(string)
}
- return fsCdr, nil
}
}
- return nil, err
+ return fsCdr, nil
}
type FSCdr struct {
@@ -88,7 +89,6 @@ func (fsCdr FSCdr) getExtraFields() map[string]string {
if parsed, err := field.Parse(origFieldVal); err == nil {
extraFields[field.Id] = parsed
}
-
}
return extraFields
}
@@ -136,32 +136,54 @@ func (fsCdr FSCdr) firstDefined(fldNames []string, dfltFld string) (val string)
return fsCdr.searchExtraField(dfltFld, fsCdr.body)
}
-func (fsCdr FSCdr) AsCDR(timezone string) *CDR {
- storCdr := new(CDR)
- storCdr.CGRID = fsCdr.getCGRID()
- storCdr.ToR = utils.VOICE
- storCdr.OriginID = fsCdr.vars[FS_UUID]
- storCdr.OriginHost = utils.FirstNonEmpty(fsCdr.vars[utils.CGROriginHost],
- fsCdr.vars[FsIPv4])
- storCdr.Source = FS_CDR_SOURCE
- storCdr.RequestType = utils.FirstNonEmpty(fsCdr.vars[utils.CGR_REQTYPE],
- fsCdr.cgrCfg.GeneralCfg().DefaultReqType)
- storCdr.Tenant = utils.FirstNonEmpty(fsCdr.vars[utils.CGR_TENANT],
- fsCdr.cgrCfg.GeneralCfg().DefaultTenant)
- storCdr.Category = utils.FirstNonEmpty(fsCdr.vars[utils.CGR_CATEGORY],
- fsCdr.cgrCfg.GeneralCfg().DefaultCategory)
- storCdr.Account = fsCdr.firstDefined([]string{utils.CGR_ACCOUNT, FS_USERNAME},
- FsUsername)
- storCdr.Subject = fsCdr.firstDefined([]string{utils.CGR_SUBJECT,
- utils.CGR_ACCOUNT, FS_USERNAME}, FsUsername)
- storCdr.Destination = utils.FirstNonEmpty(fsCdr.vars[utils.CGR_DESTINATION],
- fsCdr.vars[FS_CALL_DEST_NR], fsCdr.vars[FS_SIP_REQUSER])
- storCdr.SetupTime, _ = utils.ParseTimeDetectLayout(fsCdr.vars[FS_SETUP_TIME],
- timezone) // Not interested to process errors, should do them if necessary in a previous step
- storCdr.AnswerTime, _ = utils.ParseTimeDetectLayout(fsCdr.vars[FS_ANSWER_TIME],
- timezone)
- storCdr.Usage, _ = utils.ParseDurationWithSecs(fsCdr.vars[FS_DURATION])
- storCdr.ExtraFields = fsCdr.getExtraFields()
- storCdr.Cost = -1
- return storCdr
+func (fsCdr FSCdr) AsCDR(timezone string) (storCdr *CDR, err error) {
+ storCdr = &CDR{
+ CGRID: fsCdr.getCGRID(),
+ RunID: fsCdr.vars["cgr_runid"],
+ OriginHost: utils.FirstNonEmpty(fsCdr.vars[utils.CGROriginHost], fsCdr.vars[FsIPv4]),
+ Source: FS_CDR_SOURCE,
+ OriginID: fsCdr.vars[FS_UUID],
+ ToR: utils.VOICE,
+ RequestType: utils.FirstNonEmpty(fsCdr.vars[utils.CGR_REQTYPE], fsCdr.cgrCfg.GeneralCfg().DefaultReqType),
+ Tenant: utils.FirstNonEmpty(fsCdr.vars[utils.CGR_TENANT], fsCdr.cgrCfg.GeneralCfg().DefaultTenant),
+ Category: utils.FirstNonEmpty(fsCdr.vars[utils.CGR_CATEGORY], fsCdr.cgrCfg.GeneralCfg().DefaultCategory),
+ Account: fsCdr.firstDefined([]string{utils.CGR_ACCOUNT, FS_USERNAME}, FsUsername),
+ Subject: fsCdr.firstDefined([]string{utils.CGR_SUBJECT, utils.CGR_ACCOUNT, FS_USERNAME}, FsUsername),
+ Destination: utils.FirstNonEmpty(fsCdr.vars[utils.CGR_DESTINATION], fsCdr.vars[FS_CALL_DEST_NR], fsCdr.vars[FS_SIP_REQUSER]),
+ ExtraFields: fsCdr.getExtraFields(),
+ ExtraInfo: fsCdr.vars["cgr_extrainfo"],
+ CostSource: fsCdr.vars["cgr_costsource"],
+ Cost: -1,
+ }
+ if orderId, hasIt := fsCdr.vars["cgr_orderid"]; hasIt {
+ if storCdr.OrderID, err = strconv.ParseInt(orderId, 10, 64); err != nil {
+ return nil, err
+ }
+ }
+ if setupTime, hasIt := fsCdr.vars[FS_SETUP_TIME]; hasIt {
+ if storCdr.SetupTime, err = utils.ParseTimeDetectLayout(setupTime, timezone); err != nil {
+ return nil, err
+ } // Not interested to process errors, should do them if necessary in a previous step
+ }
+ if answerTime, hasIt := fsCdr.vars[FS_ANSWER_TIME]; hasIt {
+ if storCdr.AnswerTime, err = utils.ParseTimeDetectLayout(answerTime, timezone); err != nil {
+ return nil, err
+ }
+ }
+ if usage, hasIt := fsCdr.vars[FS_DURATION]; hasIt {
+ if storCdr.Usage, err = utils.ParseDurationWithSecs(usage); err != nil {
+ return nil, err
+ }
+ }
+ if partial, hasIt := fsCdr.vars["cgr_partial"]; hasIt {
+ if storCdr.Partial, err = strconv.ParseBool(partial); err != nil {
+ return nil, err
+ }
+ }
+ if preRated, hasIt := fsCdr.vars["cgr_prerated"]; hasIt {
+ if storCdr.PreRated, err = strconv.ParseBool(preRated); err != nil {
+ return nil, err
+ }
+ }
+ return
}
diff --git a/engine/fscdr_test.go b/engine/fscdr_test.go
index 73fe1b597..cd6612549 100644
--- a/engine/fscdr_test.go
+++ b/engine/fscdr_test.go
@@ -18,6 +18,7 @@ along with this program. If not, see
package engine
import (
+ "bytes"
"reflect"
"testing"
"time"
@@ -394,7 +395,8 @@ var fsCdrCfg *config.CGRConfig
func TestFsCdrFirstNonEmpty(t *testing.T) {
fsCdrCfg, _ = config.NewDefaultCGRConfig()
- fsCdr, err := NewFSCdr(body, fsCdrCfg)
+ reader := bytes.NewReader(body)
+ fsCdr, err := NewFSCdr(reader, fsCdrCfg)
if err != nil {
t.Errorf("Error loading cdr: %v", err)
}
@@ -406,7 +408,8 @@ func TestFsCdrFirstNonEmpty(t *testing.T) {
func TestFsCdrCDRFields(t *testing.T) {
fsCdrCfg.CdrsCfg().ExtraFields = []*utils.RSRField{{Id: "sip_user_agent"}}
- fsCdr, err := NewFSCdr(body, fsCdrCfg)
+ reader := bytes.NewReader(body)
+ fsCdr, err := NewFSCdr(reader, fsCdrCfg)
if err != nil {
t.Errorf("Error loading cdr: %v", err)
}
@@ -422,13 +425,19 @@ func TestFsCdrCDRFields(t *testing.T) {
AnswerTime: answerTime, Usage: 68 * time.Second,
Cost: -1,
ExtraFields: map[string]string{"sip_user_agent": "Jitsi2.10.5550Linux"}}
- if CDR := fsCdr.AsCDR(""); !reflect.DeepEqual(expctCDR, CDR) {
+ if CDR, err := fsCdr.AsCDR(""); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(expctCDR, CDR) {
t.Errorf("Expecting: %+v, received: %+v", expctCDR, CDR)
}
}
func TestFsCdrSearchExtraFieldLast(t *testing.T) {
- fsCdr, _ := NewFSCdr(body, fsCdrCfg)
+ newReader := bytes.NewReader(body)
+ fsCdr, err := NewFSCdr(newReader, fsCdrCfg)
+ if err != nil {
+ t.Error(err)
+ }
value := fsCdr.searchExtraField("progress_media_time", fsCdr.body)
if value != "1515666347954373" {
t.Error("Error finding extra field: ", value)
@@ -436,7 +445,11 @@ func TestFsCdrSearchExtraFieldLast(t *testing.T) {
}
func TestFsCdrSearchExtraField(t *testing.T) {
- fsCdr, _ := NewFSCdr(body, fsCdrCfg)
+ newReader := bytes.NewReader(body)
+ fsCdr, err := NewFSCdr(newReader, fsCdrCfg)
+ if err != nil {
+ t.Error(err)
+ }
rsrSt1, _ := utils.NewRSRField("^injected_value")
rsrSt2, _ := utils.NewRSRField("^injected_hdr::injected_value/")
fsCdrCfg.CdrsCfg().ExtraFields = []*utils.RSRField{{Id: "caller_id_name"}, rsrSt1, rsrSt2}
@@ -450,15 +463,21 @@ func TestFsCdrSearchExtraField(t *testing.T) {
}
func TestFsCdrSearchExtraFieldInSlice(t *testing.T) {
- fsCdr, _ := NewFSCdr(body, fsCdrCfg)
- if value := fsCdr.searchExtraField("floatfld1", map[string]interface{}{"floatfld1": 6.4}); value != "6.4" {
+ newReader := bytes.NewReader(body)
+ if fsCdr, err := NewFSCdr(newReader, fsCdrCfg); err != nil {
+ t.Error(err)
+ } else if value := fsCdr.searchExtraField("floatfld1", map[string]interface{}{"floatfld1": 6.4}); value != "6.4" {
t.Errorf("Expecting: 6.4, received: %s", value)
}
}
func TestFsCdrSearchReplaceInExtraFields(t *testing.T) {
fsCdrCfg.CdrsCfg().ExtraFields = utils.ParseRSRFieldsMustCompile(`read_codec;~sip_user_agent:s/([A-Za-z]*).+/$1/;write_codec`, utils.INFIELD_SEP)
- fsCdr, _ := NewFSCdr(body, fsCdrCfg)
+ newReader := bytes.NewReader(body)
+ fsCdr, err := NewFSCdr(newReader, fsCdrCfg)
+ if err != nil {
+ t.Error(err)
+ }
extraFields := fsCdr.getExtraFields()
if len(extraFields) != 3 {
t.Error("Error parsing extra fields: ", extraFields)
@@ -509,7 +528,8 @@ func TestFsCdrDDazRSRExtraFields(t *testing.T) {
} else if !reflect.DeepEqual(expCdrExtra[0], fsCdrCfg.CdrsCfg().ExtraFields[0]) { // Kinda deepEqual bug since without index does not match
t.Errorf("Expecting: %+v, received: %+v", expCdrExtra, fsCdrCfg.CdrsCfg().ExtraFields)
}
- fsCdr, err := NewFSCdr(simpleJsonCdr, fsCdrCfg)
+ newReader := bytes.NewReader(simpleJsonCdr)
+ fsCdr, err := NewFSCdr(newReader, fsCdrCfg)
if err != nil {
t.Error("Could not parse cdr", err.Error())
}
@@ -520,7 +540,8 @@ func TestFsCdrDDazRSRExtraFields(t *testing.T) {
}
func TestFsCdrFirstDefined(t *testing.T) {
- fsCdr, _ := NewFSCdr(body, fsCdrCfg)
+ newReader := bytes.NewReader(body)
+ fsCdr, _ := NewFSCdr(newReader, fsCdrCfg)
value := fsCdr.firstDefined([]string{utils.CGR_SUBJECT, utils.CGR_ACCOUNT, FS_USERNAME}, FsUsername)
if value != "1001" {
t.Errorf("Expecting: 1001, received: %s", value)
@@ -530,3 +551,239 @@ func TestFsCdrFirstDefined(t *testing.T) {
t.Errorf("Expecting: 1001, received: %s", value)
}
}
+
+func TestFscdrAsCDR(t *testing.T) {
+ cgrCfg, err := config.NewDefaultCGRConfig()
+ if err != nil {
+ t.Error(err)
+ }
+ cgrCfg.CdrsCfg().ExtraFields, err = utils.ParseRSRFieldsFromSlice([]string{"PayPalAccount"})
+ if err != nil {
+ t.Error(err)
+ }
+ fsCdrByte := []byte(` {
+ "variables": {
+ "cgr_orderid": "123",
+ "cgr_partial": "true",
+ "cgr_prerated": "false"
+ }
+}`)
+ expectedCdr := &CDR{
+ OrderID: 123,
+ ToR: utils.META_VOICE,
+ Source: FS_CDR_SOURCE, Category: cgrCfg.GeneralCfg().DefaultCategory,
+ Tenant: cgrCfg.GeneralCfg().DefaultTenant,
+ RequestType: cgrCfg.GeneralCfg().DefaultReqType,
+ Partial: true,
+ PreRated: false,
+ ExtraFields: map[string]string{
+ "PayPalAccount": "",
+ },
+ Cost: -1,
+ }
+ newReader := bytes.NewReader(fsCdrByte)
+ if fsCdr, err := NewFSCdr(newReader, cgrCfg); err != nil {
+ t.Error(err)
+ } else {
+ expectedCdr.CGRID = fsCdr.getCGRID()
+ if cdr, err := fsCdr.AsCDR(""); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(expectedCdr, cdr) {
+ t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expectedCdr), utils.ToJSON(cdr))
+ }
+ }
+}
+
+func TestFscdrAsCdrOrderId(t *testing.T) {
+ cgrCfg, err := config.NewDefaultCGRConfig()
+ if err != nil {
+ t.Error(err)
+ }
+ fsCdrByte := []byte(` {
+ "variables": {
+ "cgr_orderid": "123s"
+ }
+}`)
+ expectedErr := "strconv.ParseInt: parsing \"123s\": invalid syntax"
+ newReader := bytes.NewReader(fsCdrByte)
+ if fsCdr, err := NewFSCdr(newReader, cgrCfg); err != nil {
+ t.Error(err)
+ } else if _, err := fsCdr.AsCDR(""); err == nil || err.Error() != expectedErr {
+ t.Errorf("Expected %+v \n, received %+v", expectedErr, err)
+ }
+}
+
+func TestFscdrAsCdrSetupTime(t *testing.T) {
+ cgrCfg, err := config.NewDefaultCGRConfig()
+ if err != nil {
+ t.Error(err)
+ }
+ fsCdrByte := []byte(` {
+ "variables": {
+ "start_epoch": "123ss"
+ }
+}`)
+ expectedErr := "Unsupported time format"
+ newReader := bytes.NewReader(fsCdrByte)
+ if fsCdr, err := NewFSCdr(newReader, cgrCfg); err != nil {
+ t.Error(err)
+ } else if _, err := fsCdr.AsCDR(""); err == nil || err.Error() != expectedErr {
+ t.Errorf("Expected %+v \n, received %+v", expectedErr, err)
+ }
+}
+
+func TestFscdrAsCdrAnswerTime(t *testing.T) {
+ cgrCfg, err := config.NewDefaultCGRConfig()
+ if err != nil {
+ t.Error(err)
+ }
+ fsCdrByte := []byte(` {
+ "variables": {
+ "answer_epoch": "123ss"
+ }
+}`)
+ expectedErr := "Unsupported time format"
+ newReader := bytes.NewReader(fsCdrByte)
+ if fsCdr, err := NewFSCdr(newReader, cgrCfg); err != nil {
+ t.Error(err)
+ } else if _, err := fsCdr.AsCDR(""); err == nil || err.Error() != expectedErr {
+ t.Errorf("Expected %+v \n, received %+v", expectedErr, err)
+ }
+}
+
+func TestFscdrAsCdrUsage(t *testing.T) {
+ cgrCfg, err := config.NewDefaultCGRConfig()
+ if err != nil {
+ t.Error(err)
+ }
+ fsCdrByte := []byte(` {
+ "variables": {
+ "billsec": "1ss"
+ }
+}`)
+ expectedErr := "time: unknown unit \"ss\" in duration \"1ss\""
+ newReader := bytes.NewReader(fsCdrByte)
+ if fsCdr, err := NewFSCdr(newReader, cgrCfg); err != nil {
+ t.Error(err)
+ } else if _, err := fsCdr.AsCDR(""); err == nil || err.Error() != expectedErr {
+ t.Errorf("Expected %+v \n, received %+v", expectedErr, err)
+ }
+}
+
+func TestFscdrAsCdrPartial(t *testing.T) {
+ cgrCfg, err := config.NewDefaultCGRConfig()
+ if err != nil {
+ t.Error(err)
+ }
+ fsCdrByte := []byte(` {
+ "variables": {
+ "cgr_partial": "InvalidBoolFormat"
+ }
+}`)
+ expectedErr := "strconv.ParseBool: parsing \"InvalidBoolFormat\": invalid syntax"
+ newReader := bytes.NewReader(fsCdrByte)
+ if fsCdr, err := NewFSCdr(newReader, cgrCfg); err != nil {
+ t.Error(err)
+ } else if _, err := fsCdr.AsCDR(""); err == nil || err.Error() != expectedErr {
+ t.Errorf("Expected %+v \n, received %+v", expectedErr, err)
+ }
+}
+
+func TestFscdrAsCdrPreRated(t *testing.T) {
+ cgrCfg, err := config.NewDefaultCGRConfig()
+ if err != nil {
+ t.Error(err)
+ }
+ fsCdrByte := []byte(` {
+ "variables": {
+ "cgr_prerated": "InvalidBoolFormat"
+ }
+}`)
+ expectedErr := "strconv.ParseBool: parsing \"InvalidBoolFormat\": invalid syntax"
+ newReader := bytes.NewReader(fsCdrByte)
+ if fsCdr, err := NewFSCdr(newReader, cgrCfg); err != nil {
+ t.Error(err)
+ } else if _, err := fsCdr.AsCDR(""); err == nil || err.Error() != expectedErr {
+ t.Errorf("Expected %+v \n, received %+v", expectedErr, err)
+ }
+}
+
+func TestFscdrAsCdrFirstDefined(t *testing.T) {
+ cgrCfg, err := config.NewDefaultCGRConfig()
+ if err != nil {
+ t.Error(err)
+ }
+ fsCdrByte := []byte(` {
+ "variables": {
+ "cgr_account": "randomAccount"
+ }
+}`)
+ expectedCdr := &CDR{
+ ToR: utils.META_VOICE,
+ Source: FS_CDR_SOURCE, Category: cgrCfg.GeneralCfg().DefaultCategory,
+ Tenant: cgrCfg.GeneralCfg().DefaultTenant,
+ RequestType: cgrCfg.GeneralCfg().DefaultReqType,
+ Account: "randomAccount",
+ Subject: "randomAccount",
+ ExtraFields: map[string]string{},
+ Cost: -1,
+ }
+ newReader := bytes.NewReader(fsCdrByte)
+ if fsCdr, err := NewFSCdr(newReader, cgrCfg); err != nil {
+ t.Error(err)
+ } else {
+ expectedCdr.CGRID = fsCdr.getCGRID()
+ if cdr, err := fsCdr.AsCDR(""); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(expectedCdr, cdr) {
+ t.Errorf("Expected %+v \n, redceived %+v", utils.ToJSON(expectedCdr), utils.ToJSON(cdr))
+ }
+ }
+}
+func TestNewFSCdrDecodeError(t *testing.T) {
+ cgrCfg, err := config.NewDefaultCGRConfig()
+ if err != nil {
+ t.Error(err)
+ }
+ expectedErr := "EOF"
+ newReader := bytes.NewReader(nil)
+ if _, err := NewFSCdr(newReader, cgrCfg); err == nil || err.Error() != expectedErr {
+ t.Errorf("Expected %+v, received %+v", expectedErr, err)
+ }
+}
+
+func TestSearchExtraFieldDefaultType(t *testing.T) {
+ cgrCfg, err := config.NewDefaultCGRConfig()
+ if err != nil {
+ t.Error(err)
+ }
+ newMap := map[string]interface{}{
+ "variables": map[string]string{
+ "cgr_orderid": "123",
+ },
+ }
+ fsCdr := FSCdr{
+ cgrCfg: cgrCfg,
+ body: newMap,
+ }
+ fsCdr.searchExtraField(utils.EmptyString, newMap)
+}
+
+func TestSearchExtraFieldInterface(t *testing.T) {
+ cgrCfg, err := config.NewDefaultCGRConfig()
+ if err != nil {
+ t.Error(err)
+ }
+ newMap := map[string]interface{}{ //There is a slice with no maps
+ "variables": []interface{}{
+ 2,
+ "randomValue",
+ true,
+ },
+ }
+ fsCdr := FSCdr{
+ cgrCfg: cgrCfg,
+ body: newMap,
+ }
+ fsCdr.searchExtraField(utils.EmptyString, newMap)
+}