From 84c8cf038cb7097ed91a49936f171292778bb252 Mon Sep 17 00:00:00 2001 From: porosnicuadrian Date: Tue, 3 Nov 2020 18:14:06 +0200 Subject: [PATCH] Synched FSCDR with FSCdr object + coeverage tests --- agents/fsevent_test.go | 12 +- engine/cdrs.go | 28 ++--- engine/fscdr.go | 98 +++++++++------ engine/fscdr_test.go | 277 +++++++++++++++++++++++++++++++++++++++-- 4 files changed, 350 insertions(+), 65 deletions(-) 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) +}