diff --git a/ers/ers_it_test.go b/ers/ers_it_test.go index 7d7a19804..929beea6a 100644 --- a/ers/ers_it_test.go +++ b/ers/ers_it_test.go @@ -20,8 +20,12 @@ along with this program. If not, see package ers import ( + "bytes" "errors" + "log" + "os" "reflect" + "strings" "testing" "time" @@ -758,3 +762,165 @@ func TestERsProcessEvent11(t *testing.T) { t.Fatalf("\nExpecting <%+v>,\n Received <%+v>", "RALS_ERROR", err) } } + +func TestErsOnEvictedMetaDumpToFileOK(t *testing.T) { + dirPath := "/tmp/TestErsOnEvictedMetaDumpToFile" + err := os.Mkdir(dirPath, 0755) + if err != nil { + t.Error(err) + } + defer os.RemoveAll(dirPath) + + value := &erEvents{ + events: []*utils.CGREvent{ + { + Tenant: "cgrates.org", + ID: "EventErsOnEvicted", + Event: map[string]interface{}{ + utils.AccountField: "1001", + }, + }, + }, + rdrCfg: &config.EventReaderCfg{ + ID: "ER1", + Type: utils.MetaNone, + Opts: map[string]interface{}{ + utils.PartialCacheActionOpt: utils.MetaDumpToFile, + utils.PartialPathOpt: dirPath, + }, + }, + } + cfg := config.NewDefaultCGRConfig() + data := engine.NewInternalDB(nil, nil, true) + dm := engine.NewDataManager(data, cfg.CacheCfg(), nil) + fltrS := engine.NewFilterS(cfg, nil, dm) + erS := &ERService{ + cfg: cfg, + rdrEvents: make(chan *erEvent, 1), + filterS: fltrS, + } + erS.onEvicted("ID", value) + + // rcv, err := os.ReadFile(filepath.Join(dirPath, "ID.*.*")) + // if err != nil { + // t.Error(err) + // } + // fmt.Println(rcv) +} + +func TestErsOnEvictedMetaDumpToFileCSVWriteErr(t *testing.T) { + utils.Logger.SetLogLevel(3) + utils.Logger.SetSyslog(nil) + + var buf bytes.Buffer + log.SetOutput(&buf) + defer func() { + log.SetOutput(os.Stderr) + }() + + dirPath := "/tmp/TestErsOnEvictedMetaDumpToFile" + err := os.Mkdir(dirPath, 0755) + if err != nil { + t.Error(err) + } + defer os.RemoveAll(dirPath) + + value := &erEvents{ + events: []*utils.CGREvent{ + { + Tenant: "cgrates.org", + ID: "EventErsOnEvicted", + Event: map[string]interface{}{ + utils.AccountField: "1001", + }, + }, + }, + rdrCfg: &config.EventReaderCfg{ + ID: "ER1", + Type: utils.MetaNone, + Opts: map[string]interface{}{ + utils.PartialCacheActionOpt: utils.MetaDumpToFile, + utils.PartialPathOpt: dirPath, + utils.PartialCSVFieldSepartorOpt: "\"", + }, + }, + } + cfg := config.NewDefaultCGRConfig() + data := engine.NewInternalDB(nil, nil, true) + dm := engine.NewDataManager(data, cfg.CacheCfg(), nil) + fltrS := engine.NewFilterS(cfg, nil, dm) + erS := &ERService{ + cfg: cfg, + rdrEvents: make(chan *erEvent, 1), + filterS: fltrS, + } + + erS.onEvicted("ID", value) + + rcvLog := buf.String()[20:] + if !strings.Contains(rcvLog, "error: csv: invalid field or comment delimiter") { + t.Errorf("expected: <%s> to be included in log message: <%s>", + "error: csv: invalid field or comment delimiter", rcvLog) + } + utils.Logger.SetLogLevel(0) +} + +func TestErsOnEvictedMetaDumpToFileCreateErr(t *testing.T) { + utils.Logger.SetLogLevel(3) + utils.Logger.SetSyslog(nil) + + var buf bytes.Buffer + log.SetOutput(&buf) + defer func() { + log.SetOutput(os.Stderr) + }() + + dirPath := "/tmp/TestErsOnEvictedMetaDumpToFile" + err := os.Mkdir(dirPath, 0755) + if err != nil { + t.Error(err) + } + defer os.RemoveAll(dirPath) + + value := &erEvents{ + events: []*utils.CGREvent{ + { + Tenant: "cgrates.org", + ID: "EventErsOnEvicted", + Event: map[string]interface{}{ + utils.AccountField: "1001", + }, + }, + }, + rdrCfg: &config.EventReaderCfg{ + ID: "ER1", + Type: utils.MetaNone, + Opts: map[string]interface{}{ + utils.PartialCacheActionOpt: utils.MetaDumpToFile, + utils.PartialPathOpt: dirPath + "/non-existent", + }, + }, + } + cfg := config.NewDefaultCGRConfig() + data := engine.NewInternalDB(nil, nil, true) + dm := engine.NewDataManager(data, cfg.CacheCfg(), nil) + fltrS := engine.NewFilterS(cfg, nil, dm) + erS := &ERService{ + cfg: cfg, + rdrEvents: make(chan *erEvent, 1), + filterS: fltrS, + } + + erS.onEvicted("ID", value) + + rcvLog := buf.String()[20:] + if !strings.Contains(rcvLog, "CGRateS <> [ERROR] Failed creating /tmp/TestErsOnEvictedMetaDumpToFile/non-existent/ID.") && + !strings.Contains(rcvLog, "error: open /tmp/TestErsOnEvictedMetaDumpToFile/non-existent/ID.") { + t.Errorf("expected: <%s> and <%s> to be included in log message: <%s>", + "CGRateS <> [ERROR] Failed creating /tmp/TestErsOnEvictedMetaDumpToFile/non-existent/ID.", + "error: open /tmp/TestErsOnEvictedMetaDumpToFile/non-existent/ID.", + rcvLog) + } + + utils.Logger.SetLogLevel(0) +} diff --git a/ers/ers_test.go b/ers/ers_test.go index ccd321563..836a16f4f 100644 --- a/ers/ers_test.go +++ b/ers/ers_test.go @@ -19,10 +19,16 @@ along with this program. If not, see package ers import ( + "bytes" + "log" + "os" "reflect" + "strings" "testing" + "time" "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" ) @@ -60,3 +66,270 @@ func TestERsProcessPartialEvent(t *testing.T) { } } } + +func TestErsOnEvictedNilValue(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + erS := &ERService{ + cfg: cfg, + rdrEvents: make(chan *erEvent, 1), + } + erS.onEvicted("id", nil) + + // Verification TBA +} + +func TestErsOnEvictedMetaPostCDROK(t *testing.T) { + value := &erEvents{ + events: []*utils.CGREvent{ + { + Tenant: "cgrates.org", + ID: "EventErsOnEvicted", + Event: map[string]interface{}{ + utils.AccountField: "1001", + }, + }, + }, + rdrCfg: &config.EventReaderCfg{ + ID: "ER1", + Type: utils.MetaNone, + ProcessedPath: "/tmp", + Opts: map[string]interface{}{ + utils.PartialCacheActionOpt: utils.MetaPostCDR, + }, + }, + } + cfg := config.NewDefaultCGRConfig() + erS := &ERService{ + cfg: cfg, + rdrEvents: make(chan *erEvent, 1), + } + erS.onEvicted("id", value) + + if len(erS.rdrEvents) != 1 { + t.Fatal("Expected channel to contain a value") + } + select { + case data := <-erS.rdrEvents: + if !reflect.DeepEqual(data.rdrCfg, value.rdrCfg) { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", + utils.ToJSON(value.rdrCfg), utils.ToJSON(data.rdrCfg)) + } + if !reflect.DeepEqual(data.cgrEvent, value.events[0]) { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", + utils.ToJSON(value.events[0]), utils.ToJSON(data.cgrEvent)) + } + case <-time.After(40 * time.Millisecond): + t.Error("Time limit exceeded") + } +} + +func TestErsOnEvictedMetaPostCDRMergeErr(t *testing.T) { + utils.Logger.SetLogLevel(4) + utils.Logger.SetSyslog(nil) + + var buf bytes.Buffer + log.SetOutput(&buf) + defer func() { + log.SetOutput(os.Stderr) + }() + + value := &erEvents{ + events: []*utils.CGREvent{ + { + Tenant: "cgrates.org", + ID: "EventErsOnEvicted", + Event: map[string]interface{}{ + utils.AnswerTime: time.Date(2021, 6, 1, 12, 0, 0, 0, time.UTC), + utils.AccountField: "1001", + utils.Destination: "1002", + }, + }, + { + Tenant: "cgrates.org", + ID: "EventErsOnEvicted", + Event: map[string]interface{}{ + utils.AnswerTime: time.Date(2021, 6, 1, 13, 0, 0, 0, time.UTC), + utils.AccountField: "1001", + utils.Destination: "1003", + }, + }, + }, + rdrCfg: &config.EventReaderCfg{ + ID: "ER1", + Type: utils.MetaNone, + ProcessedPath: "/tmp", + Opts: map[string]interface{}{ + utils.PartialCacheActionOpt: utils.MetaPostCDR, + }, + }, + } + cfg := config.NewDefaultCGRConfig() + data := engine.NewInternalDB(nil, nil, true) + dm := engine.NewDataManager(data, cfg.CacheCfg(), nil) + fltrS := engine.NewFilterS(cfg, nil, dm) + erS := &ERService{ + cfg: cfg, + rdrEvents: make(chan *erEvent, 1), + filterS: fltrS, + } + expLog := `[WARNING] failed posting expired parial events <[{"Tenant":"cgrates.org","ID":"EventErsOnEvicted","Time":null,"Event":{"Account":"1001","AnswerTime":"2021-06-01T13:00:00Z","Destination":"1003"},"APIOpts":null},{"Tenant":"cgrates.org","ID":"EventErsOnEvicted","Time":null,"Event":{"Account":"1001","AnswerTime":"2021-06-01T12:00:00Z","Destination":"1002"},"APIOpts":null}]> due error ` + erS.onEvicted("id", value) + rcvLog := buf.String()[20:] + if !strings.Contains(rcvLog, expLog) { + t.Errorf("expected: <%+v> to be included in <%+v>", expLog, rcvLog) + } + + utils.Logger.SetLogLevel(0) +} + +func TestErsOnEvictedMetaDumpToFileSetFieldsErr(t *testing.T) { + utils.Logger.SetLogLevel(4) + utils.Logger.SetSyslog(nil) + + var buf bytes.Buffer + log.SetOutput(&buf) + defer func() { + log.SetOutput(os.Stderr) + }() + + dirPath := "/tmp/TestErsOnEvictedMetaDumpToFile" + value := &erEvents{ + events: []*utils.CGREvent{ + { + Tenant: "cgrates.org", + ID: "EventErsOnEvicted", + Event: map[string]interface{}{ + utils.AccountField: "1001", + }, + }, + }, + rdrCfg: &config.EventReaderCfg{ + ID: "ER1", + Type: utils.MetaNone, + Opts: map[string]interface{}{ + utils.PartialCacheActionOpt: utils.MetaDumpToFile, + utils.PartialPathOpt: dirPath, + }, + CacheDumpFields: []*config.FCTemplate{ + { + Tag: "cacheDump", + }, + }, + }, + } + cfg := config.NewDefaultCGRConfig() + data := engine.NewInternalDB(nil, nil, true) + dm := engine.NewDataManager(data, cfg.CacheCfg(), nil) + fltrS := engine.NewFilterS(cfg, nil, dm) + erS := &ERService{ + cfg: cfg, + rdrEvents: make(chan *erEvent, 1), + filterS: fltrS, + } + expLog := `[WARNING] Converting CDR with CGRID: to record , ignoring due to error: > +` + erS.onEvicted("ID", value) + + rcvLog := buf.String()[20:] + if !strings.Contains(rcvLog, expLog) { + t.Errorf("expected <%+v> to be included in: <%+v>", expLog, rcvLog) + } + + utils.Logger.SetLogLevel(0) +} + +func TestErsOnEvictedMetaDumpToFileMergeErr(t *testing.T) { + utils.Logger.SetLogLevel(4) + utils.Logger.SetSyslog(nil) + + var buf bytes.Buffer + log.SetOutput(&buf) + defer func() { + log.SetOutput(os.Stderr) + }() + + dirPath := "/tmp/TestErsOnEvictedMetaDumpToFile" + value := &erEvents{ + events: []*utils.CGREvent{ + { + Tenant: "cgrates.org", + ID: "EventErsOnEvicted", + Event: map[string]interface{}{ + utils.AnswerTime: time.Date(2021, 6, 1, 12, 0, 0, 0, time.UTC), + utils.AccountField: "1001", + utils.Destination: "1002", + }, + }, + { + Tenant: "cgrates.org", + ID: "EventErsOnEvicted", + Event: map[string]interface{}{ + utils.AnswerTime: time.Date(2021, 6, 1, 13, 0, 0, 0, time.UTC), + utils.AccountField: "1001", + utils.Destination: "1003", + }, + }, + }, + rdrCfg: &config.EventReaderCfg{ + ID: "ER1", + Type: utils.MetaNone, + Opts: map[string]interface{}{ + utils.PartialCacheActionOpt: utils.MetaDumpToFile, + utils.PartialPathOpt: dirPath, + }, + }, + } + cfg := config.NewDefaultCGRConfig() + data := engine.NewInternalDB(nil, nil, true) + dm := engine.NewDataManager(data, cfg.CacheCfg(), nil) + fltrS := engine.NewFilterS(cfg, nil, dm) + erS := &ERService{ + cfg: cfg, + rdrEvents: make(chan *erEvent, 1), + filterS: fltrS, + } + + expLog := `[WARNING] failed posting expired parial events <[{"Tenant":"cgrates.org","ID":"EventErsOnEvicted","Time":null,"Event":{"Account":"1001","AnswerTime":"2021-06-01T13:00:00Z","Destination":"1003"},"APIOpts":null},{"Tenant":"cgrates.org","ID":"EventErsOnEvicted","Time":null,"Event":{"Account":"1001","AnswerTime":"2021-06-01T12:00:00Z","Destination":"1002"},"APIOpts":null}]> due error +` + erS.onEvicted("ID", value) + + rcvLog := buf.String()[20:] + if !strings.Contains(rcvLog, expLog) { + t.Errorf("expected <%+v> to be included in: <%+v>", expLog, rcvLog) + } + + utils.Logger.SetLogLevel(0) +} + +func TestErsOnEvictedMetaDumpToFileEmptyPath(t *testing.T) { + value := &erEvents{ + events: []*utils.CGREvent{ + { + Tenant: "cgrates.org", + ID: "EventErsOnEvicted", + Event: map[string]interface{}{ + utils.AccountField: "1001", + }, + }, + }, + rdrCfg: &config.EventReaderCfg{ + ID: "ER1", + Type: utils.MetaNone, + Opts: map[string]interface{}{ + utils.PartialCacheActionOpt: utils.MetaDumpToFile, + }, + }, + } + cfg := config.NewDefaultCGRConfig() + data := engine.NewInternalDB(nil, nil, true) + dm := engine.NewDataManager(data, cfg.CacheCfg(), nil) + fltrS := engine.NewFilterS(cfg, nil, dm) + erS := &ERService{ + cfg: cfg, + rdrEvents: make(chan *erEvent, 1), + filterS: fltrS, + } + erS.onEvicted("ID", value) + + // Verification TBA +}