/* Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments Copyright (C) ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see */ package engine import ( "bytes" "reflect" "testing" "time" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" ) var body = []byte(`{ "core-uuid": "eb8bcdd2-d9eb-4f8f-80c3-1a4042aabe31", "switchname": "FSDev1", "channel_data": { "state": "CS_REPORTING", "state_number": "11", "flags": "0=1;1=1;3=1;20=1;37=1;38=1;40=1;43=1;48=1;53=1;75=1;98=1;112=1;113=1;122=1;134=1", "caps": "1=1;2=1;3=1;4=1;5=1;6=1" }, "callStats": { "audio": { "inbound": { "raw_bytes": 572588, "media_bytes": 572588, "packet_count": 3329, "media_packet_count": 3329, "skip_packet_count": 10, "jitter_packet_count": 0, "dtmf_packet_count": 0, "cng_packet_count": 0, "flush_packet_count": 0, "largest_jb_size": 0, "jitter_min_variance": 0, "jitter_max_variance": 0, "jitter_loss_rate": 0, "jitter_burst_rate": 0, "mean_interval": 0, "flaw_total": 0, "quality_percentage": 100, "mos": 4.500000 }, "outbound": { "raw_bytes": 0, "media_bytes": 0, "packet_count": 0, "media_packet_count": 0, "skip_packet_count": 0, "dtmf_packet_count": 0, "cng_packet_count": 0, "rtcp_packet_count": 0, "rtcp_octet_count": 0 } } }, "variables": { "uuid": "3da8bf84-c133-4959-9e24-e72875cb33a1", "session_id": "7", "sip_from_user": "1001", "sip_from_uri": "1001@10.10.10.204", "sip_from_host": "10.10.10.204", "channel_name": "sofia/internal/1001@10.10.10.204", "ep_codec_string": "CORE_PCM_MODULE.PCMU@8000h@20i@64000b,CORE_PCM_MODULE.PCMA@8000h@20i@64000b", "sip_local_network_addr": "10.10.10.204", "sip_network_ip": "10.10.10.100", "sip_network_port": "5060", "sip_invite_stamp": "1515666344534355", "sip_received_ip": "10.10.10.100", "sip_received_port": "5060", "sip_via_protocol": "udp", "sip_from_user_stripped": "1001", "sofia_profile_name": "internal", "recovery_profile_name": "internal", "sip_req_user": "1002", "sip_req_uri": "1002@10.10.10.204", "sip_req_host": "10.10.10.204", "sip_to_user": "1002", "sip_to_uri": "1002@10.10.10.204", "sip_to_host": "10.10.10.204", "sip_contact_params": "transport=udp;registering_acc=10_10_10_204", "sip_contact_user": "1001", "sip_contact_port": "5060", "sip_contact_uri": "1001@10.10.10.100:5060", "sip_contact_host": "10.10.10.100", "sip_user_agent": "Jitsi2.10.5550Linux", "sip_via_host": "10.10.10.100", "sip_via_port": "5060", "presence_id": "1001@10.10.10.204", "cgr_notify": "AUTH_OK", "max_forwards": "69", "transfer_history": "1515666344:b4300942-e809-4393-99cb-d39a1bc3c219:bl_xfer:1002/default/XML", "transfer_source": "1515666344:b4300942-e809-4393-99cb-d39a1bc3c219:bl_xfer:1002/default/XML", "DP_MATCH": "ARRAY::1002|:1002", "call_uuid": "3da8bf84-c133-4959-9e24-e72875cb33a1", "call_timeout": "30", "current_application_data": "user/1002@10.10.10.204", "current_application": "bridge", "dialed_user": "1002", "dialed_domain": "10.10.10.204", "originated_legs": "ARRAY::f52c26f1-b018-4963-bf6d-a3111d1a0320;Outbound Call;1002|:f52c26f1-b018-4963-bf6d-a3111d1a0320;Outbound Call;1002", "switch_m_sdp": "v=0\r\no=1002-jitsi.org 0 0 IN IP4 10.10.10.100\r\ns=-\r\nc=IN IP4 10.10.10.100\r\nt=0 0\r\nm=audio 5022 RTP/AVP 0 8 101\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:101 telephone-event/8000\r\n", "rtp_use_codec_name": "PCMU", "rtp_use_codec_rate": "8000", "rtp_use_codec_ptime": "20", "rtp_use_codec_channels": "1", "rtp_last_audio_codec_string": "PCMU@8000h@20i@1c", "read_codec": "PCMU", "original_read_codec": "PCMU", "read_rate": "8000", "original_read_rate": "8000", "write_codec": "PCMU", "write_rate": "8000", "video_possible": "true", "video_media_flow": "sendonly", "local_media_ip": "10.10.10.204", "local_media_port": "21566", "advertised_media_ip": "10.10.10.204", "rtp_use_timer_name": "soft", "rtp_use_pt": "0", "rtp_use_ssrc": "1448966920", "endpoint_disposition": "ANSWER", "originate_causes": "ARRAY::f52c26f1-b018-4963-bf6d-a3111d1a0320;NONE|:f52c26f1-b018-4963-bf6d-a3111d1a0320;NONE", "originate_disposition": "SUCCESS", "DIALSTATUS": "SUCCESS", "last_bridge_to": "f52c26f1-b018-4963-bf6d-a3111d1a0320", "bridge_channel": "sofia/internal/1002@10.10.10.100:5060", "bridge_uuid": "f52c26f1-b018-4963-bf6d-a3111d1a0320", "signal_bond": "f52c26f1-b018-4963-bf6d-a3111d1a0320", "last_sent_callee_id_name": "Outbound Call", "last_sent_callee_id_number": "1002", "switch_r_sdp": "v=0\r\no=1001-jitsi.org 0 1 IN IP4 10.10.10.100\r\ns=-\r\nc=IN IP4 10.10.10.100\r\nt=0 0\r\nm=audio 5018 RTP/AVP 96 97 98 9 100 102 0 8 103 3 104 101\r\na=rtpmap:96 opus/48000/2\r\na=fmtp:96 usedtx=1\r\na=rtpmap:97 SILK/24000\r\na=rtpmap:98 SILK/16000\r\na=rtpmap:9 G722/8000\r\na=rtpmap:100 speex/32000\r\na=rtpmap:102 speex/16000\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:103 iLBC/8000\r\na=rtpmap:3 GSM/8000\r\na=rtpmap:104 speex/8000\r\na=rtpmap:101 telephone-event/8000\r\na=sendonly\r\na=ptime:20\r\na=extmap:1 urn:ietf:params:rtp-hdrext:csrc-audio-level\r\na=extmap:2 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=rtcp-xr:voip-metrics\r\nm=video 0 RTP/AVP 105 99\r\n", "rtp_use_codec_string": "PCMU,PCMA", "audio_media_flow": "recvonly", "remote_media_ip": "10.10.10.100", "remote_media_port": "5018", "rtp_audio_recv_pt": "0", "dtmf_type": "rfc2833", "rtp_2833_send_payload": "101", "rtp_2833_recv_payload": "101", "rtp_local_sdp_str": "v=0\r\no=FreeSWITCH 1515644781 1515644783 IN IP4 10.10.10.204\r\ns=FreeSWITCH\r\nc=IN IP4 10.10.10.204\r\nt=0 0\r\nm=audio 21566 RTP/AVP 0 101\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:101 telephone-event/8000\r\na=fmtp:101 0-16\r\na=ptime:20\r\na=recvonly\r\n", "sip_to_tag": "m3g4NZ4rXFX3p", "sip_from_tag": "f25afe20", "sip_cseq": "2", "sip_call_id": "818e26f805701988c1a330175d7d2629@0:0:0:0:0:0:0:0", "sip_full_via": "SIP/2.0/UDP 10.10.10.100:5060;branch=z9hG4bK-313838-2ee350643dea826b4a74f8049852f307", "sip_from_display": "1001", "sip_full_from": "\"1001\" ;tag=f25afe20", "sip_full_to": ";tag=m3g4NZ4rXFX3p", "sip_hangup_phrase": "OK", "last_bridge_hangup_cause": "NORMAL_CLEARING", "last_bridge_proto_specific_hangup_cause": "sip:200", "bridge_hangup_cause": "NORMAL_CLEARING", "hangup_cause": "NORMAL_CLEARING", "hangup_cause_q850": "16", "digits_dialed": "none", "start_stamp": "2018-01-11 11:25:44", "profile_start_stamp": "2018-01-11 11:25:44", "answer_stamp": "2018-01-11 11:25:47", "bridge_stamp": "2018-01-11 11:25:47", "hold_stamp": "2018-01-11 11:25:48", "progress_stamp": "2018-01-11 11:25:44", "progress_media_stamp": "2018-01-11 11:25:47", "hold_events": "{{1515666348363496,1515666415502648}}", "end_stamp": "2018-01-11 11:26:55", "start_epoch": "1515666344", "start_uepoch": "1515666344534355", "profile_start_epoch": "1515666344", "profile_start_uepoch": "1515666344534355", "answer_epoch": "1515666347", "answer_uepoch": "1515666347954373", "bridge_epoch": "1515666347", "bridge_uepoch": "1515666347954373", "last_hold_epoch": "1515666348", "last_hold_uepoch": "1515666348363496", "hold_accum_seconds": "67", "hold_accum_usec": "67139151", "hold_accum_ms": "67139", "resurrect_epoch": "0", "resurrect_uepoch": "0", "progress_epoch": "1515666344", "progress_uepoch": "1515666344594267", "progress_media_epoch": "1515666347", "progress_media_uepoch": "1515666347954373", "end_epoch": "1515666415", "end_uepoch": "1515666415494269", "last_app": "bridge", "last_arg": "user/1002@10.10.10.204", "caller_id": "\"1001\" <1001>", "duration": "71", "billsec": "68", "progresssec": "0", "answersec": "3", "waitsec": "3", "progress_mediasec": "3", "flow_billsec": "71", "mduration": "70960", "billmsec": "67540", "progressmsec": "60", "answermsec": "3420", "waitmsec": "3420", "progress_mediamsec": "3420", "flow_billmsec": "70960", "uduration": "70959914", "billusec": "67539896", "progressusec": "59912", "answerusec": "3420018", "waitusec": "3420018", "progress_mediausec": "3420018", "flow_billusec": "70959914", "sip_hangup_disposition": "send_bye", "rtp_audio_in_raw_bytes": "572588", "rtp_audio_in_media_bytes": "572588", "rtp_audio_in_packet_count": "3329", "rtp_audio_in_media_packet_count": "3329", "rtp_audio_in_skip_packet_count": "10", "rtp_audio_in_jitter_packet_count": "0", "rtp_audio_in_dtmf_packet_count": "0", "rtp_audio_in_cng_packet_count": "0", "rtp_audio_in_flush_packet_count": "0", "rtp_audio_in_largest_jb_size": "0", "rtp_audio_in_jitter_min_variance": "0.00", "rtp_audio_in_jitter_max_variance": "0.00", "rtp_audio_in_jitter_loss_rate": "0.00", "rtp_audio_in_jitter_burst_rate": "0.00", "rtp_audio_in_mean_interval": "0.00", "rtp_audio_in_flaw_total": "0", "rtp_audio_in_quality_percentage": "100.00", "rtp_audio_in_mos": "4.50", "rtp_audio_out_raw_bytes": "0", "rtp_audio_out_media_bytes": "0", "rtp_audio_out_packet_count": "0", "rtp_audio_out_media_packet_count": "0", "rtp_audio_out_skip_packet_count": "0", "rtp_audio_out_dtmf_packet_count": "0", "rtp_audio_out_cng_packet_count": "0", "rtp_audio_rtcp_packet_count": "0", "rtp_audio_rtcp_octet_count": "0" }, "app_log": { "applications": [{ "app_name": "park", "app_data": "", "app_stamp": "1515666344548466" }, { "app_name": "set", "app_data": "ringback=", "app_stamp": "1515666344575066" }, { "app_name": "set", "app_data": "call_timeout=30", "app_stamp": "1515666344576009" }, { "app_name": "bridge", "app_data": "user/1002@10.10.10.204", "app_stamp": "1515666344576703" }] }, "callflow": [{ "dialplan": "XML", "profile_index": "2", "extension": { "name": "Local_Extension", "number": "1002", "applications": [{ "app_name": "set", "app_data": "ringback=${us-ring}" }, { "app_name": "set", "app_data": "call_timeout=30" }, { "app_name": "bridge", "app_data": "user/${destination_number}@${domain_name}" }] }, "caller_profile": { "username": "1001", "dialplan": "XML", "caller_id_name": "1001", "ani": "1001", "aniii": "", "caller_id_number": "1001", "network_addr": "10.10.10.100", "rdnis": "1002", "destination_number": "1002", "uuid": "3da8bf84-c133-4959-9e24-e72875cb33a1", "source": "mod_sofia", "context": "default", "chan_name": "sofia/internal/1001@10.10.10.204", "originatee": { "originatee_caller_profiles": [{ "username": "1001", "dialplan": "XML", "caller_id_name": "1001", "ani": "1001", "aniii": "", "caller_id_number": "1001", "network_addr": "10.10.10.100", "rdnis": "1002", "destination_number": "1002", "uuid": "f52c26f1-b018-4963-bf6d-a3111d1a0320", "source": "mod_sofia", "context": "default", "chan_name": "sofia/internal/1002@10.10.10.100:5060" }, { "username": "1001", "dialplan": "XML", "caller_id_name": "1001", "ani": "1001", "aniii": "", "caller_id_number": "1001", "network_addr": "10.10.10.100", "rdnis": "1002", "destination_number": "1002", "uuid": "f52c26f1-b018-4963-bf6d-a3111d1a0320", "source": "mod_sofia", "context": "default", "chan_name": "sofia/internal/1002@10.10.10.100:5060" }] } }, "times": { "created_time": "1515666344534355", "profile_created_time": "1515666344534355", "progress_time": "1515666344594267", "progress_media_time": "1515666347954373", "answered_time": "1515666347954373", "bridged_time": "1515666347954373", "last_hold_time": "1515666348363496", "hold_accum_time": "67139151", "hangup_time": "1515666415494269", "resurrect_time": "0", "transfer_time": "0" } }, { "dialplan": "XML", "profile_index": "1", "extension": { "name": "CGRateS_Auth", "number": "1002", "applications": [{ "app_name": "park", "app_data": "" }] }, "caller_profile": { "username": "1001", "dialplan": "XML", "caller_id_name": "1001", "ani": "1001", "aniii": "", "caller_id_number": "1001", "network_addr": "10.10.10.100", "rdnis": "", "destination_number": "1002", "uuid": "3da8bf84-c133-4959-9e24-e72875cb33a1", "source": "mod_sofia", "context": "default", "chan_name": "sofia/internal/1001@10.10.10.204" }, "times": { "created_time": "1515666344534355", "profile_created_time": "1515666344534355", "progress_time": "0", "progress_media_time": "0", "answered_time": "0", "bridged_time": "0", "last_hold_time": "0", "hold_accum_time": "0", "hangup_time": "0", "resurrect_time": "0", "transfer_time": "1515666344534355" } }] }`) var fsCdrCfg *config.CGRConfig func TestFsCdrFirstNonEmpty(t *testing.T) { fsCdrCfg = config.NewDefaultCGRConfig() reader := bytes.NewReader(body) fsCdr, err := NewFSCdr(reader, fsCdrCfg) if err != nil { t.Errorf("Error loading cdr: %v", err) } //fsc := fsCdr.(FSCdr) if _, ok := fsCdr.vars["cgr_notify"]; !ok { t.Error("Error parsing cdr: ", fsCdr) } } func TestFsCdrCDRFields(t *testing.T) { fsCdrCfg.CdrsCfg().ExtraFields = config.NewRSRParsersMustCompile("~*req.sip_user_agent", utils.FieldsSep) reader := bytes.NewReader(body) fsCdr, err := NewFSCdr(reader, fsCdrCfg) if err != nil { t.Errorf("Error loading cdr: %v", err) } setupTime, _ := utils.ParseTimeDetectLayout("1515666344", "") answerTime, _ := utils.ParseTimeDetectLayout("1515666347", "") expctCDR := &CDR{ CGRID: "24b5766be325fa751fab5a0a06373e106f33a257", ToR: utils.MetaVoice, OriginID: "3da8bf84-c133-4959-9e24-e72875cb33a1", OriginHost: "", Source: "freeswitch_json", Category: "call", RequestType: utils.MetaRated, Tenant: "cgrates.org", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: setupTime, AnswerTime: answerTime, Usage: 68 * time.Second, Cost: -1, ExtraFields: map[string]string{"sip_user_agent": "Jitsi2.10.5550Linux"}, } 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) { 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) } } func TestFsCdrSearchExtraField(t *testing.T) { newReader := bytes.NewReader(body) fsCdr, err := NewFSCdr(newReader, fsCdrCfg) if err != nil { t.Error(err) } fsCdrCfg.CdrsCfg().ExtraFields, err = config.NewRSRParsersFromSlice([]string{"~*req.caller_id_name"}) if err != nil { t.Fatal(err) } extraFields := fsCdr.getExtraFields() if len(extraFields) != 1 || extraFields["caller_id_name"] != "1001" { t.Error("Error parsing extra fields: ", utils.ToJSON(extraFields)) } } func TestFsCdrSearchExtraFieldInSlice(t *testing.T) { newReader := bytes.NewReader(body) if fsCdr, err := NewFSCdr(newReader, fsCdrCfg); err != nil { t.Error(err) } else if value := fsCdr.searchExtraField("floatfld1", map[string]any{"floatfld1": 6.4}); value != "6.4" { t.Errorf("Expecting: 6.4, received: %s", value) } } func TestFsCdrSearchReplaceInExtraFields(t *testing.T) { fsCdrCfg.CdrsCfg().ExtraFields = config.NewRSRParsersMustCompile(`~*req.read_codec;~*req.sip_user_agent:s/([A-Za-z]*).+/$1/;~*req.write_codec`, utils.InfieldSep) 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) } if extraFields["sip_user_agent"] != "Jitsi" { t.Error("Error parsing extra fields: ", utils.ToJSON(extraFields)) } } func TestFsCdrDDazRSRExtraFields(t *testing.T) { eFieldsCfg := `{"cdrs": { "extra_fields": ["~*req.effective_caller_id_number:s/(\\d+)/+$1/"], },}` simpleJSONCdr := []byte(`{ "core-uuid": "feef0b51-7fdf-4c4a-878e-aff233752de2", "channel_data": { "state": "CS_REPORTING", "state_number": "11", "flags": "0=1;1=1;3=1;36=1;37=1;39=1;42=1;47=1;52=1;73=1;75=1;94=1", "caps": "1=1;2=1;3=1;4=1;5=1;6=1" }, "variables": { "uuid": "86cfd6e2-dbda-45a3-b59d-f683ec368e8b", "session_id": "5", "accountcode": "1001", "user_context": "default", "effective_caller_id_name": "Extension 1001", "effective_caller_id_number": "4986517174963", "outbound_caller_id_name": "FreeSWITCH", "outbound_caller_id_number": "0000000000" }, "times": { "created_time": "1396984221278217", "profile_created_time": "1396984221278217", "progress_time": "0", "progress_media_time": "0", "answered_time": "0", "hangup_time": "0", "resurrect_time": "0", "transfer_time": "1396984221377035" } }`) var err error fsCdrCfg, err = config.NewCGRConfigFromJSONStringWithDefaults(eFieldsCfg) expCdrExtra := config.NewRSRParsersMustCompile(`~*req.effective_caller_id_number:s/(\d+)/+$1/`, utils.InfieldSep) if err != nil { t.Error("Could not parse the config", err.Error()) } 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", utils.ToJSON(expCdrExtra), utils.ToJSON(fsCdrCfg.CdrsCfg().ExtraFields)) } newReader := bytes.NewReader(simpleJSONCdr) fsCdr, err := NewFSCdr(newReader, fsCdrCfg) if err != nil { t.Error("Could not parse cdr", err.Error()) } extraFields := fsCdr.getExtraFields() if extraFields["effective_caller_id_number"] != "+4986517174963" { t.Errorf("Unexpected effective_caller_id_number received: %+v", extraFields["effective_caller_id_number"]) } } func TestFsCdrFirstDefined(t *testing.T) { newReader := bytes.NewReader(body) fsCdr, _ := NewFSCdr(newReader, fsCdrCfg) value := fsCdr.firstDefined([]string{utils.CGRSubject, utils.CGRAccount, FS_USERNAME}, FsUsername) if value != "1001" { t.Errorf("Expecting: 1001, received: %s", value) } value = fsCdr.firstDefined([]string{utils.CGRAccount, FS_USERNAME}, FsUsername) if value != "1001" { t.Errorf("Expecting: 1001, received: %s", value) } } func TestFscdrAsCDR(t *testing.T) { cgrCfg := config.NewDefaultCGRConfig() cgrCfg.CdrsCfg().ExtraFields, err = config.NewRSRParsersFromSlice([]string{"~*req.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.MetaVoice, 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 := config.NewDefaultCGRConfig() 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 := config.NewDefaultCGRConfig() 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 := config.NewDefaultCGRConfig() 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 := config.NewDefaultCGRConfig() 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 := config.NewDefaultCGRConfig() 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 := config.NewDefaultCGRConfig() 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 := config.NewDefaultCGRConfig() fsCdrByte := []byte(` { "variables": { "cgr_account": "randomAccount" } }`) expectedCdr := &CDR{ ToR: utils.MetaVoice, 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 := config.NewDefaultCGRConfig() 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 := config.NewDefaultCGRConfig() newMap := map[string]any{ "variables": map[string]string{ "cgr_orderid": "123", }, } fsCdr := FSCdr{ cgrCfg: cgrCfg, body: newMap, } fsCdr.searchExtraField(utils.EmptyString, newMap) } func TestSearchExtraFieldInterface(t *testing.T) { cgrCfg := config.NewDefaultCGRConfig() newMap := map[string]any{ "variables": []any{ 2, "randomValue", true, }, } fsCdr := FSCdr{ cgrCfg: cgrCfg, body: newMap, } fsCdr.searchExtraField(utils.EmptyString, newMap) } func TestGetExtraFields(t *testing.T) { cgrCfg := config.NewDefaultCGRConfig() cgrCfg.CdrsCfg().ExtraFields, err = config.NewRSRParsersFromSlice([]string{"PayPalAccount"}) if err != nil { t.Error(err) } fsCdr := FSCdr{ cgrCfg: cgrCfg, } expected := map[string]string{} if reply := fsCdr.getExtraFields(); !reflect.DeepEqual(reply, expected) { t.Errorf("Expected %+v, received %+v", utils.ToJSON(expected), utils.ToJSON(reply)) } }