/* 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 cdrc import ( "bytes" "path" "reflect" "testing" "time" "github.com/ChrisTrenkamp/goxpath" "github.com/ChrisTrenkamp/goxpath/tree/xmltree" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" ) var cdrXmlBroadsoft = ` 0002183384 CGRateSaabb 20160419210000.104 1+020000 Start 0002183385 CGRateSaabb 20160419210005.247 1+020000 MBC Normal 1001 2001 Terminating Network 1001 Public +4986517174963 20160419210005.247 1+020000 25160047719:0 Yes 20160419210006.813 20160419210020.296 016 y local 1001@cgrates.org Yes Yes CGR_GROUP CGR_GROUP/CGR_GROUP_TRUNK30 Normal 1001@cgrates.org Primary Device 31.882 gw04.cgrates.org 74122796919420162305@172.16.1.2 PCMA/8000 172.16.1.4 BW2300052501904161738474465@172.16.1.10 31.882 OmniPCX Enterprise R11.0.1 k1.520.22.b 0002183386 CGRateSaabb 20160419210006.909 1+020000 MBC Normal 1002 2001 Terminating Network +4986517174964 Public 1002 20160419210006.909 1+020000 27280048121:0 Yes 20160419210007.037 20160419210030.322 016 y local 314028947650@cgrates.org Yes Yes CGR_GROUP CGR_GROUP/CGR_GROUP_TRUNK65 Normal 31403456100@cgrates.org Primary Device 26.244 gw01.cgrates.org 108352493719420162306@172.31.250.150 PCMA/8000 172.16.1.4 2345300069121904161716512907@172.16.1.10 26.244 Altitude vBox 0002183486 CGRateSaabb 20160419211500.104 1+020000 End ` func optsNotStrict(s *xmltree.ParseOptions) { s.Strict = false } func TestXMLElementText(t *testing.T) { xp := goxpath.MustParse(path.Join("/broadWorksCDR/cdrData/")) xmlTree := xmltree.MustParseXML(bytes.NewBufferString(cdrXmlBroadsoft), optsNotStrict) cdrs := goxpath.MustExec(xp, xmlTree, nil) cdrWithoutUserNr := cdrs[0] if _, err := elementText(cdrWithoutUserNr, "cdrData/basicModule/userNumber"); err != utils.ErrNotFound { t.Error(err) } cdrWithUser := cdrs[1] if val, err := elementText(cdrWithUser, "cdrData/basicModule/userNumber"); err != nil { t.Error(err) } else if val != "1001" { t.Errorf("Expecting: 1001, received: %s", val) } if val, err := elementText(cdrWithUser, "/cdrData/centrexModule/locationList/locationInformation/locationType"); err != nil { t.Error(err) } else if val != "Primary Device" { t.Errorf("Expecting: , received: <%s>", val) } } func TestXMLHandlerSubstractUsage(t *testing.T) { xp := goxpath.MustParse(path.Join("/broadWorksCDR/cdrData/")) xmlTree := xmltree.MustParseXML(bytes.NewBufferString(cdrXmlBroadsoft), optsNotStrict) cdrs := goxpath.MustExec(xp, xmlTree, nil) cdrWithUsage := cdrs[1] if usage, err := handlerSubstractUsage(cdrWithUsage, utils.ParseRSRFieldsMustCompile("broadWorksCDR>cdrData>basicModule>releaseTime;^|;broadWorksCDR>cdrData>basicModule>answerTime", utils.INFIELD_SEP), utils.HierarchyPath([]string{"broadWorksCDR", "cdrData"}), "UTC"); err != nil { t.Error(err) } else if usage != time.Duration(13483000000) { t.Errorf("Expected: 13.483s, received: %v", usage) } } func TestXMLRPProcess(t *testing.T) { cdrcCfgs := []*config.CdrcConfig{ &config.CdrcConfig{ ID: "TestXML", Enabled: true, CdrFormat: "xml", DataUsageMultiplyFactor: 1024, CDRPath: utils.HierarchyPath([]string{"broadWorksCDR", "cdrData"}), CdrSourceId: "TestXML", CdrFilter: utils.ParseRSRFieldsMustCompile("broadWorksCDR>cdrData>headerModule>type(Normal)", utils.INFIELD_SEP), ContentFields: []*config.CfgCdrField{ &config.CfgCdrField{Tag: "TOR", Type: utils.META_COMPOSED, FieldId: utils.TOR, Value: utils.ParseRSRFieldsMustCompile("^*voice", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "OriginID", Type: utils.META_COMPOSED, FieldId: utils.OriginID, Value: utils.ParseRSRFieldsMustCompile("broadWorksCDR>cdrData>basicModule>localCallId", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "RequestType", Type: utils.META_COMPOSED, FieldId: utils.RequestType, Value: utils.ParseRSRFieldsMustCompile("^*rated", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "Tenant", Type: utils.META_COMPOSED, FieldId: utils.Tenant, Value: utils.ParseRSRFieldsMustCompile("~broadWorksCDR>cdrData>basicModule>userId:s/.*@(.*)/${1}/", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "Category", Type: utils.META_COMPOSED, FieldId: utils.Category, Value: utils.ParseRSRFieldsMustCompile("^call", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "Account", Type: utils.META_COMPOSED, FieldId: utils.Account, Value: utils.ParseRSRFieldsMustCompile("broadWorksCDR>cdrData>basicModule>userNumber", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "Destination", Type: utils.META_COMPOSED, FieldId: utils.Destination, Value: utils.ParseRSRFieldsMustCompile("broadWorksCDR>cdrData>basicModule>calledNumber", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "SetupTime", Type: utils.META_COMPOSED, FieldId: utils.SetupTime, Value: utils.ParseRSRFieldsMustCompile("broadWorksCDR>cdrData>basicModule>startTime", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "AnswerTime", Type: utils.META_COMPOSED, FieldId: utils.AnswerTime, Value: utils.ParseRSRFieldsMustCompile("broadWorksCDR>cdrData>basicModule>answerTime", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "Usage", Type: utils.META_HANDLER, FieldId: utils.Usage, HandlerId: utils.HandlerSubstractUsage, Value: utils.ParseRSRFieldsMustCompile("broadWorksCDR>cdrData>basicModule>releaseTime;^|;broadWorksCDR>cdrData>basicModule>answerTime", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "UsageSeconds", Type: utils.META_COMPOSED, FieldId: utils.Usage, Value: utils.ParseRSRFieldsMustCompile("^s", utils.INFIELD_SEP), Mandatory: true}, }, }, } xmlRP, err := NewXMLRecordsProcessor(bytes.NewBufferString(cdrXmlBroadsoft), utils.HierarchyPath([]string{"broadWorksCDR", "cdrData"}), "UTC", true, cdrcCfgs) if err != nil { t.Error(err) } var cdrs []*engine.CDR for i := 0; i < 4; i++ { cdrs, err = xmlRP.ProcessNextRecord() if i == 1 { // Take second CDR since the first one cannot be processed break } } if err != nil { t.Error(err) } expectedCDRs := []*engine.CDR{ &engine.CDR{CGRID: "1f045359a0784d15e051d7e41ae30132b139d714", OriginHost: "0.0.0.0", Source: "TestXML", OriginID: "25160047719:0", ToR: "*voice", RequestType: "*rated", Tenant: "cgrates.org", Category: "call", Account: "1001", Destination: "+4986517174963", SetupTime: time.Date(2016, 4, 19, 21, 0, 5, 247000000, time.UTC), AnswerTime: time.Date(2016, 4, 19, 21, 0, 6, 813000000, time.UTC), Usage: time.Duration(13483000000), ExtraFields: map[string]string{}, Cost: -1}, } if !reflect.DeepEqual(expectedCDRs, cdrs) { t.Errorf("Expecting: %+v\n, received: %+v\n", expectedCDRs, cdrs) } }