From 5076e6f668e430a41b9d22e9caa9badcc300d26f Mon Sep 17 00:00:00 2001 From: armirveliaj Date: Wed, 9 Oct 2024 10:34:51 -0400 Subject: [PATCH] Add new unit tests on TrendS --- engine/libtrends_test.go | 56 +++++++++ services/trends_test.go | 97 +++++++++++++++ utils/cdr_test.go | 253 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 406 insertions(+) create mode 100644 services/trends_test.go create mode 100644 utils/cdr_test.go diff --git a/engine/libtrends_test.go b/engine/libtrends_test.go index 15199ad0f..ce82d3532 100644 --- a/engine/libtrends_test.go +++ b/engine/libtrends_test.go @@ -19,7 +19,9 @@ along with this program. If not, see package engine import ( + "errors" "reflect" + "sync" "testing" "time" @@ -318,3 +320,57 @@ func TestGetTrendLabel(t *testing.T) { } } } + +func TestGetTrendGrowth(t *testing.T) { + + trend := Trend{ + tMux: &sync.RWMutex{}, + mLast: map[string]time.Time{}, + Metrics: map[time.Time]map[string]*MetricWithTrend{}, + mTotals: map[string]float64{}, + mCounts: map[string]int{}, + } + + _, err := trend.getTrendGrowth("unknownID", 100, utils.MetaLast, 2) + if !errors.Is(err, utils.ErrNotFound) { + t.Errorf("Expected error ErrNotFound, got: %v", err) + } + + now := time.Now() + trend.mLast["metric1"] = now + + _, err = trend.getTrendGrowth("metric1", 100, utils.MetaLast, 2) + if !errors.Is(err, utils.ErrNotFound) { + t.Errorf("Expected error ErrNotFound, got: %v", err) + } + + trend.Metrics = map[time.Time]map[string]*MetricWithTrend{ + now: { + "metric1": {ID: "metric1", Value: 80}, + }, + } + + got, err := trend.getTrendGrowth("metric1", 100, utils.MetaLast, 2) + expected := utils.Round(20.0/100, 2, utils.MetaRoundingMiddle) + if err != nil || !reflect.DeepEqual(got, expected) { + t.Errorf("Mismatch for MetaLast correlation. Got: %v, expected: %v", got, expected) + } + + trend.mTotals = map[string]float64{ + "metric1": 400, + } + trend.mCounts = map[string]int{ + "metric1": 4, + } + + got, err = trend.getTrendGrowth("metric1", 120, utils.MetaAverage, 2) + expected = utils.Round(20.0/100, 2, utils.MetaRoundingMiddle) + if err != nil || !reflect.DeepEqual(got, expected) { + t.Errorf("Mismatch for MetaAverage correlation. Got: %v, expected: %v", got, expected) + } + + _, err = trend.getTrendGrowth("metric1", 100, "invalidCorrelation", 2) + if !errors.Is(err, utils.ErrCorrelationUndefined) { + t.Errorf("Expected error ErrCorrelationUndefined, got: %v", err) + } +} diff --git a/services/trends_test.go b/services/trends_test.go new file mode 100644 index 000000000..eb8967823 --- /dev/null +++ b/services/trends_test.go @@ -0,0 +1,97 @@ +/* +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 services + +import ( + "sync" + "testing" + + "github.com/cgrates/birpc" + "github.com/cgrates/birpc/context" + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/cores" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +func TestNewTrendService(t *testing.T) { + cfg := &config.CGRConfig{} + dm := &DataDBService{} + cacheS := &CacheService{} + filterSChan := make(chan *engine.FilterS) + server := &cores.Server{} + internalTrendSChan := make(chan birpc.ClientConnector) + connMgr := &engine.ConnManager{} + anz := &AnalyzerService{} + srvDep := make(map[string]*sync.WaitGroup) + + service := NewTrendService(cfg, dm, cacheS, filterSChan, server, internalTrendSChan, connMgr, anz, srvDep) + + trendService, ok := service.(*TrendService) + if !ok { + t.Errorf("Expected type *TrendService, but got %T", service) + } + + if trendService.cfg != cfg { + t.Errorf("Expected cfg to be %v, but got %v", cfg, trendService.cfg) + } + if trendService.dm != dm { + t.Errorf("Expected dm to be %v, but got %v", dm, trendService.dm) + } + if trendService.cacheS != cacheS { + t.Errorf("Expected cacheS to be %v, but got %v", cacheS, trendService.cacheS) + } + + if trendService.server != server { + t.Errorf("Expected server to be %v, but got %v", server, trendService.server) + } + if trendService.connChan != internalTrendSChan { + t.Errorf("Expected connChan to be %v, but got %v", internalTrendSChan, trendService.connChan) + } + if trendService.connMgr != connMgr { + t.Errorf("Expected connMgr to be %v, but got %v", connMgr, trendService.connMgr) + } + if trendService.anz != anz { + t.Errorf("Expected anz to be %v, but got %v", anz, trendService.anz) + } + +} + +func TestTrendServiceServiceName(t *testing.T) { + tr := &TrendService{} + + serviceName := tr.ServiceName() + + expected := utils.TrendS + if serviceName != expected { + t.Errorf("Expected service name to be %s, but got %s", expected, serviceName) + } +} + +func TestTrendService_Reload(t *testing.T) { + tr := &TrendService{} + + ctx := context.Background() + + err := tr.Reload(ctx, nil) + + if err != nil { + t.Errorf("Expected no error, but got: %v", err) + } +} diff --git a/utils/cdr_test.go b/utils/cdr_test.go new file mode 100644 index 000000000..d6576f7e8 --- /dev/null +++ b/utils/cdr_test.go @@ -0,0 +1,253 @@ +/* +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 utils + +import ( + "encoding/json" + "errors" + "reflect" + "testing" +) + +func TestTableName(t *testing.T) { + cdrSQLTable := CDRSQLTable{} + + tableName := cdrSQLTable.TableName() + + if tableName != CDRsTBL { + t.Errorf("TableName() = %s, expected %s", tableName, CDRsTBL) + } +} + +func TestJSONB_GormDataType(t *testing.T) { + j := JSONB{"key": "value"} + + dataType := j.GormDataType() + + if dataType != "JSONB" { + t.Errorf("GormDataType() returned %v, expected 'JSONB'", dataType) + } +} + +func TestJSONBScan(t *testing.T) { + { + j := &JSONB{} + value := []byte(`{"key": "value"}`) + + err := j.Scan(value) + + if err != nil { + t.Errorf("Scan([]byte) returned error: %v, expected no error", err) + } + + expected := JSONB{"key": "value"} + if !reflect.DeepEqual(*j, expected) { + t.Errorf("Scan([]byte) resulted in %v, expected %v", *j, expected) + } + } + + { + j := &JSONB{} + value := `{"key": "value"}` + + err := j.Scan(value) + + if err != nil { + t.Errorf("Scan(string) returned error: %v, expected no error", err) + } + + expected := JSONB{"key": "value"} + if !reflect.DeepEqual(*j, expected) { + t.Errorf("Scan(string) resulted in %v, expected %v", *j, expected) + } + } + + { + j := &JSONB{} + value := 123 + + err := j.Scan(value) + + if err == nil { + t.Errorf("Scan(int) did not return an error, expected an error") + } else { + expectedError := errors.New("Failed to unmarshal JSONB value:123") // Removed space after ":" + if err.Error() != expectedError.Error() { + t.Errorf("Scan(int) returned error: %v, expected %v", err, expectedError) + } + } + } +} + +func TestJSONBValue(t *testing.T) { + { + j := JSONB{"key": "value"} + + value, err := j.Value() + + if err != nil { + t.Errorf("Value() returned error: %v, expected no error", err) + } + + expected, _ := json.Marshal(j) + if string(value.([]byte)) != string(expected) { + t.Errorf("Value() resulted in %s, expected %s", value, expected) + } + } + + { + j := JSONB{} + + value, err := j.Value() + + if err != nil { + t.Errorf("Value() returned error: %v, expected no error", err) + } + + expected, _ := json.Marshal(j) + if string(value.([]byte)) != string(expected) { + t.Errorf("Value() resulted in %s, expected %s", value, expected) + } + } +} + +func TestGetUniqueCDRID(t *testing.T) { + { + cgrEv := &CGREvent{ + APIOpts: map[string]interface{}{ + MetaChargeID: "charge_id_123", + }, + } + + result := GetUniqueCDRID(cgrEv) + + expected := "charge_id_123" + if result != expected { + t.Errorf("GetUniqueCDRID() returned %s, expected %s", result, expected) + } + } + + { + cgrEv := &CGREvent{ + APIOpts: map[string]interface{}{ + MetaOriginID: "origin_id_456", + }, + } + + result := GetUniqueCDRID(cgrEv) + + expected := "origin_id_456" + if result != expected { + t.Errorf("GetUniqueCDRID() returned %s, expected %s", result, expected) + } + } + + { + cgrEv := &CGREvent{ + APIOpts: map[string]interface{}{}, + } + + result := GetUniqueCDRID(cgrEv) + + if len(result) != 7 { + t.Errorf("GetUniqueCDRID() returned %s, expected a 7-character UUID prefix", result) + } + } +} + +func TestCDR_CGREvent(t *testing.T) { + { + cdr := &CDR{ + Tenant: "test_tenant", + Event: map[string]interface{}{"key": "value"}, + Opts: map[string]interface{}{"opt_key": "opt_value"}, + } + + cgrEvent := cdr.CGREvent() + + if cgrEvent.Tenant != cdr.Tenant { + t.Errorf("CGREvent().Tenant = %s, expected %s", cgrEvent.Tenant, cdr.Tenant) + } + + if cgrEvent.Event["key"] != cdr.Event["key"] { + t.Errorf("CGREvent().Event = %v, expected %v", cgrEvent.Event, cdr.Event) + } + + if cgrEvent.APIOpts["opt_key"] != cdr.Opts["opt_key"] { + t.Errorf("CGREvent().APIOpts = %v, expected %v", cgrEvent.APIOpts, cdr.Opts) + } + + if cgrEvent.ID == "" { + t.Errorf("CGREvent().ID is empty, expected a generated ID") + } + } +} + +func TestCDRsToCGREvents(t *testing.T) { + { + cdrs := []*CDR{ + { + Tenant: "tenant1", + Event: map[string]interface{}{"event_key1": "event_value1"}, + Opts: map[string]interface{}{"opt_key1": "opt_value1"}, + }, + { + Tenant: "tenant2", + Event: map[string]interface{}{"event_key2": "event_value2"}, + Opts: map[string]interface{}{"opt_key2": "opt_value2"}, + }, + } + + cgrEvents := CDRsToCGREvents(cdrs) + + if len(cgrEvents) != len(cdrs) { + t.Errorf("CDRsToCGREvents() returned %d events, expected %d", len(cgrEvents), len(cdrs)) + } + + for i, cgrEvent := range cgrEvents { + cdr := cdrs[i] + + if cgrEvent.Tenant != cdr.Tenant { + t.Errorf("CGREvent[%d].Tenant = %s, expected %s", i, cgrEvent.Tenant, cdr.Tenant) + } + + if cgrEvent.Event["event_key1"] != cdr.Event["event_key1"] { + t.Errorf("CGREvent[%d].Event = %v, expected %v", i, cgrEvent.Event, cdr.Event) + } + + if cgrEvent.APIOpts["opt_key1"] != cdr.Opts["opt_key1"] { + t.Errorf("CGREvent[%d].APIOpts = %v, expected %v", i, cgrEvent.APIOpts, cdr.Opts) + } + + if cgrEvent.ID == "" { + t.Errorf("CGREvent[%d].ID is empty, expected a generated ID", i) + } + } + } + + { + cdrs := []*CDR{} + + cgrEvents := CDRsToCGREvents(cdrs) + + if len(cgrEvents) != 0 { + t.Errorf("CDRsToCGREvents() returned %d events, expected 0", len(cgrEvents)) + } + } +}