diff --git a/apier/apier_local_test.go b/apier/apier_local_test.go index 937f49518..8bc4d6ad4 100644 --- a/apier/apier_local_test.go +++ b/apier/apier_local_test.go @@ -1315,6 +1315,30 @@ func TestGetCallCostLog(t *testing.T) { } } +func TestMaxDebitInexistentAcnt(t *testing.T) { + if !*testLocal { + return + } + cc := &engine.CallCost{} + cd := engine.CallDescriptor{ + Direction: "*out", + Tenant: "cgrates.org", + TOR: "call", + Subject: "INVALID", + Account: "INVALID", + Destination: "1002", + TimeStart: time.Date(2014, 3, 27, 10, 42, 26, 0, time.UTC), + TimeEnd: time.Date(2014, 3, 27, 10, 42, 26, 0, time.UTC).Add(time.Duration(10) * time.Second), + } + if err := rater.Call("Responder.MaxDebit", cd, cc); err == nil { + t.Error(err.Error()) + } + if err := rater.Call("Responder.Debit", cd, cc); err == nil { + t.Error(err.Error()) + } + +} + func TestCdrServer(t *testing.T) { if !*testLocal { return diff --git a/apier/cdre.go b/apier/cdre.go index 84e8f1093..60b48d707 100644 --- a/apier/cdre.go +++ b/apier/cdre.go @@ -118,7 +118,8 @@ func (self *ApierV1) ExportCdrsToFile(attr utils.AttrExpFileCdrs, reply *utils.E } } csvWriter.Close() - *reply = utils.ExportedFileCdrs{ExportedFilePath: filePath, TotalRecords: len(cdrs), ExportedCgrIds: exportedIds, UnexportedCgrIds: unexportedIds, + *reply = utils.ExportedFileCdrs{ExportedFilePath: filePath, TotalRecords: len(cdrs), TotalCost: csvWriter.TotalCost(), + ExportedCgrIds: exportedIds, UnexportedCgrIds: unexportedIds, FirstOrderId: csvWriter.FirstOrderId(), LastOrderId: csvWriter.LastOrderId()} case utils.CDRE_FIXED_WIDTH: if len(exportDir) == 0 { @@ -155,7 +156,8 @@ func (self *ApierV1) ExportCdrsToFile(attr utils.AttrExpFileCdrs, reply *utils.E } } fww.Close() - *reply = utils.ExportedFileCdrs{ExportedFilePath: filePath, TotalRecords: len(cdrs), ExportedCgrIds: exportedIds, UnexportedCgrIds: unexportedIds, + *reply = utils.ExportedFileCdrs{ExportedFilePath: filePath, TotalRecords: len(cdrs), TotalCost: fww.TotalCost(), + ExportedCgrIds: exportedIds, UnexportedCgrIds: unexportedIds, FirstOrderId: fww.FirstOrderId(), LastOrderId: fww.LastOrderId()} } return nil diff --git a/cdre/cdrexporter.go b/cdre/cdrexporter.go index 00c361cd2..1c4c0aee9 100644 --- a/cdre/cdrexporter.go +++ b/cdre/cdrexporter.go @@ -25,6 +25,7 @@ import ( type CdrWriter interface { FirstOrderId() int64 LastOrderId() int64 + TotalCost() float64 WriteCdr(cdr *utils.StoredCdr) string Close() } diff --git a/cdre/csv.go b/cdre/csv.go index 9dfe80c52..e02989992 100644 --- a/cdre/csv.go +++ b/cdre/csv.go @@ -32,6 +32,7 @@ type CsvCdrWriter struct { maskLen int exportedFields []*utils.RSRField // The fields exported, order important firstExpOrderId, lastExpOrderId int64 + totalCost float64 // Cummulated cost of all the } func NewCsvCdrWriter(writer io.Writer, costShiftDigits, roundDecimals int, maskDestId string, maskLen int, exportedFields []*utils.RSRField) *CsvCdrWriter { @@ -47,6 +48,10 @@ func (csvwr *CsvCdrWriter) LastOrderId() int64 { return csvwr.lastExpOrderId } +func (csvwr *CsvCdrWriter) TotalCost() float64 { + return csvwr.totalCost +} + func (csvwr *CsvCdrWriter) WriteCdr(cdr *utils.StoredCdr) error { row := make([]string, len(csvwr.exportedFields)) for idx, fld := range csvwr.exportedFields { @@ -69,6 +74,8 @@ func (csvwr *CsvCdrWriter) WriteCdr(cdr *utils.StoredCdr) error { if csvwr.lastExpOrderId < cdr.OrderId { csvwr.lastExpOrderId = cdr.OrderId } + csvwr.totalCost += cdr.Cost + csvwr.totalCost = utils.Round(csvwr.totalCost, csvwr.roundDecimals, utils.ROUNDING_MIDDLE) return csvwr.writer.Write(row) } diff --git a/cdre/csv_test.go b/cdre/csv_test.go index 08b357456..309413a4a 100644 --- a/cdre/csv_test.go +++ b/cdre/csv_test.go @@ -32,7 +32,8 @@ func TestCsvCdrWriter(t *testing.T) { cfg, _ := config.NewDefaultCGRConfig() exportedFields := append(cfg.CdreExportedFields, &utils.RSRField{Id: "extra3"}, &utils.RSRField{Id: "dummy_extra"}, &utils.RSRField{Id: "extra1"}) csvCdrWriter := NewCsvCdrWriter(writer, 0, 4, "", -1, exportedFields) - ratedCdr := &utils.StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Unix(1383813745, 0).UTC().String()), AccId: "dsafdsaf", CdrHost: "192.168.1.1", ReqType: "rated", Direction: "*out", Tenant: "cgrates.org", + ratedCdr := &utils.StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Unix(1383813745, 0).UTC().String()), AccId: "dsafdsaf", CdrHost: "192.168.1.1", + ReqType: "rated", Direction: "*out", Tenant: "cgrates.org", TOR: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Unix(1383813745, 0).UTC(), AnswerTime: time.Unix(1383813746, 0).UTC(), Duration: time.Duration(10) * time.Second, MediationRunId: utils.DEFAULT_RUNID, ExtraFields: map[string]string{"extra1": "val_extra1", "extra2": "val_extra2", "extra3": "val_extra3"}, Cost: 1.01, @@ -44,4 +45,7 @@ func TestCsvCdrWriter(t *testing.T) { if result != expected { t.Errorf("Expected: \n%s received: \n%s.", expected, result) } + if csvCdrWriter.TotalCost() != 1.01 { + t.Error("Unexpected TotalCost: ", csvCdrWriter.TotalCost()) + } } diff --git a/cdre/fixedwidth.go b/cdre/fixedwidth.go index 36455c7b7..d3a7f6715 100644 --- a/cdre/fixedwidth.go +++ b/cdre/fixedwidth.go @@ -172,6 +172,10 @@ func (fwv *FixedWidthCdrWriter) LastOrderId() int64 { return fwv.lastExpOrderId } +func (fwv *FixedWidthCdrWriter) TotalCost() float64 { + return fwv.totalCost +} + // Writes the header into it's buffer func (fwv *FixedWidthCdrWriter) ComposeHeader() error { header := "" diff --git a/cdre/fixedwidth_test.go b/cdre/fixedwidth_test.go index 6dc3a7d92..9cfb01597 100644 --- a/cdre/fixedwidth_test.go +++ b/cdre/fixedwidth_test.go @@ -130,6 +130,9 @@ func TestWriteCdr(t *testing.T) { if fwWriter.LastOrderId() != 1 { t.Error("Unexpected LastOrderId", fwWriter.LastOrderId()) } + if fwWriter.TotalCost() != utils.Round(cdr.Cost, fwWriter.roundDecimals, utils.ROUNDING_MIDDLE) { + t.Error("Unexpected TotalCost: ", fwWriter.TotalCost()) + } } func TestWriteCdrs(t *testing.T) { @@ -199,4 +202,7 @@ func TestWriteCdrs(t *testing.T) { if fwWriter.LastOrderId() != 4 { t.Error("Unexpected LastOrderId", fwWriter.LastOrderId()) } + if fwWriter.TotalCost() != 5.9957 { + t.Error("Unexpected TotalCost: ", fwWriter.TotalCost()) + } } diff --git a/cmd/cgr-engine/cgr-engine.go b/cmd/cgr-engine/cgr-engine.go index 3c3394776..ef2269ff4 100644 --- a/cmd/cgr-engine/cgr-engine.go +++ b/cmd/cgr-engine/cgr-engine.go @@ -25,7 +25,7 @@ import ( "log" "net/rpc" "os" - "runtime" + //"runtime" "strconv" "time" @@ -287,7 +287,7 @@ func main() { if *pidFile != "" { writePid() } - runtime.GOMAXPROCS(runtime.NumCPU()) + // runtime.GOMAXPROCS(runtime.NumCPU()) // For now it slows down computing due to CPU management, to be reviewed in future Go releases cfg, err = config.NewCGRConfigFromFile(cfgPath) if err != nil { diff --git a/config/xmlconfig_test.go b/config/xmlconfig_test.go index fbfb73cf7..922747a6a 100644 --- a/config/xmlconfig_test.go +++ b/config/xmlconfig_test.go @@ -60,7 +60,7 @@ func TestParseXmlConfig(t *testing.T) { - + diff --git a/engine/responder.go b/engine/responder.go index 84096ab6a..b050fb298 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -46,7 +46,11 @@ func (rs *Responder) GetCost(arg CallDescriptor, reply *CallCost) (err error) { r, e := AccLock.GuardGetCost(arg.GetAccountKey(), func() (*CallCost, error) { return arg.GetCost() }) - *reply, err = *r, e + if e != nil { + return e + } else if r != nil { + *reply = *r + } } return } @@ -59,7 +63,11 @@ func (rs *Responder) Debit(arg CallDescriptor, reply *CallCost) (err error) { r, e := AccLock.GuardGetCost(arg.GetAccountKey(), func() (*CallCost, error) { return arg.Debit() }) - *reply, err = *r, e + if e != nil { + return e + } else if r != nil { + *reply = *r + } } return } @@ -72,7 +80,11 @@ func (rs *Responder) MaxDebit(arg CallDescriptor, reply *CallCost) (err error) { r, e := AccLock.GuardGetCost(arg.GetAccountKey(), func() (*CallCost, error) { return arg.MaxDebit() }) - *reply, err = *r, e + if e != nil { + return e + } else if r != nil { + *reply = *r + } } return } diff --git a/utils/apitpdata.go b/utils/apitpdata.go index f66980635..ef2a64cef 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -347,6 +347,7 @@ type AttrExpFileCdrs struct { type ExportedFileCdrs struct { ExportedFilePath string // Full path to the newly generated export file TotalRecords int // Number of CDRs to be exported + TotalCost float64 // Sum of all costs in exported CDRs FirstOrderId, LastOrderId int64 // The order id of the last exported CDR ExportedCgrIds []string // List of successfuly exported cgrids in the file UnexportedCgrIds map[string]string // Map of errored CDRs, map key is cgrid, value will be the error string diff --git a/utils/coreutils.go b/utils/coreutils.go index ef49791bc..db5c74880 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -131,7 +131,7 @@ func ParseTimeDetectLayout(tmStr string) (time.Time, error) { } else { return time.Unix(tmstmp, 0), nil } - case len(tmStr) == 0: // Time probably missing from request + case tmStr == "0" || len(tmStr) == 0: // Time probably missing from request return nilTime, nil } return nilTime, errors.New("Unsupported time format") diff --git a/utils/researchreplace_test.go b/utils/researchreplace_test.go index e9780b120..f6a6a5116 100644 --- a/utils/researchreplace_test.go +++ b/utils/researchreplace_test.go @@ -40,3 +40,12 @@ func TestProcessReSearchReplace2(t *testing.T) { t.Error("Unexpected output from SearchReplace: ", outStr) } } + +func TestProcessReSearchReplace3(t *testing.T) { //"MatchedDestId":"CST_31800_DE080" + rsr := &ReSearchReplace{regexp.MustCompile(`"MatchedDestId":".+_(\w{5})"`), "$1"} + source := `[{"TimeStart":"2014-04-15T22:17:57+02:00","TimeEnd":"2014-04-15T22:18:01+02:00","Cost":0,"RateInterval":{"Timing":{"Years":[],"Months":[],"MonthDays":[],"WeekDays":[],"StartTime":"00:00:00","EndTime":""},"Rating":{"ConnectFee":0,"Rates":[{"GroupIntervalStart":0,"Value":0,"RateIncrement":1000000000,"RateUnit":60000000000}],"RoundingMethod":"*middle","RoundingDecimals":4},"Weight":10},"CallDuration":4000000000,"Increments":null,"MatchedSubject":"*out:sip.test.cgrates.org:call:*any","MatchedPrefix":"+49800","MatchedDestId":"CST_31800_DE080"}]` + expectOut := "DE080" + if outStr := rsr.Process(source); outStr != expectOut { + t.Error("Unexpected output from SearchReplace: ", outStr) + } +} diff --git a/utils/rsrfield_test.go b/utils/rsrfield_test.go index ce602de8e..6818c1d8b 100644 --- a/utils/rsrfield_test.go +++ b/utils/rsrfield_test.go @@ -92,3 +92,12 @@ func TestNewRSRFieldDDz(t *testing.T) { t.Errorf("Unexpected RSRField received: %v", rsrField) } } + +func TestNewRSRFieldIvo(t *testing.T) { + expectRSRField := &RSRField{Id: "cost_details", RSRule: &ReSearchReplace{regexp.MustCompile(`MatchedDestId":".+_(\s\s\s\s\s)"`), "$1"}} + if rsrField, err := NewRSRField(`~cost_details:s/MatchedDestId":".+_(\s\s\s\s\s)"/$1/`); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(rsrField, expectRSRField) { + t.Errorf("Unexpected RSRField received: %v", rsrField) + } +} diff --git a/utils/utils_test.go b/utils/utils_test.go index 1e5bcefb0..af01ef7ec 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -194,6 +194,14 @@ func TestParseTimeDetectLayout(t *testing.T) { } else if !fsTm.Equal(expectedTime) { t.Errorf("Unexpected time parsed: %v, expecting: %v", fsTm, expectedTime) } + fsTmstampStr = "0" + fsTm, err = ParseTimeDetectLayout(fsTmstampStr) + expectedTime = time.Time{} + if err != nil { + t.Error(err) + } else if !fsTm.Equal(expectedTime) { + t.Errorf("Unexpected time parsed: %v, expecting: %v", fsTm, expectedTime) + } } func TestParseDateUnix(t *testing.T) {