From ad0b45b4d271f5f1d705c05ebd031c432d847e05 Mon Sep 17 00:00:00 2001 From: armirveliaj Date: Mon, 17 Feb 2025 12:14:00 -0500 Subject: [PATCH] Add coverage tests on accounts && engine --- accounts/abstractbalance_test.go | 35 ++++ accounts/concretebalance_test.go | 19 ++ engine/librankings_test.go | 326 +++++++++++++++++++++++++++++++ engine/rankings_test.go | 38 ++++ 4 files changed, 418 insertions(+) diff --git a/accounts/abstractbalance_test.go b/accounts/abstractbalance_test.go index c685f309b..5a514ab94 100644 --- a/accounts/abstractbalance_test.go +++ b/accounts/abstractbalance_test.go @@ -24,6 +24,7 @@ import ( "time" "github.com/cgrates/birpc/context" + "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" "github.com/ericlagergren/decimal" ) @@ -1275,3 +1276,37 @@ func TestAMCostWithUnitFactor(t *testing.T) { } } */ + +func TestAccountsListenAndServe(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + aS := &AccountS{ + cfg: cfg, + } + stopChan := make(chan struct{}) + cfgRld := make(chan struct{}, 1) + go aS.ListenAndServe(stopChan, cfgRld) + cfgRld <- struct{}{} + select { + case <-cfgRld: + case <-time.After(1 * time.Second): + t.Error("Expected configuration reload signal to be processed") + } + close(stopChan) + time.Sleep(100 * time.Millisecond) +} + +func TestAbstractBalanceId(t *testing.T) { + balance := &utils.Balance{ + ID: "1001", + } + + abstractBalanceInstance := &abstractBalance{ + blnCfg: balance, + } + + result := abstractBalanceInstance.id() + + if result != "1001" { + t.Errorf("Expected id to be '1001', but got '%s'", result) + } +} diff --git a/accounts/concretebalance_test.go b/accounts/concretebalance_test.go index 0e5a74bc6..24cd853a8 100644 --- a/accounts/concretebalance_test.go +++ b/accounts/concretebalance_test.go @@ -18,6 +18,12 @@ along with this program. If not, see package accounts +import ( + "testing" + + "github.com/cgrates/cgrates/utils" +) + /* func TestCBDebitUnits(t *testing.T) { // with limit and unit factor @@ -820,3 +826,16 @@ func TestCBSDebitAbstractsCoverProcessAttributes2(t *testing.T) { // coverage pu } } */ + +func TestConcreteBalanceID(t *testing.T) { + balance := &utils.Balance{ + ID: "1001", + } + concreteBalanceInstance := &concreteBalance{ + blnCfg: balance, + } + result := concreteBalanceInstance.id() + if result != "1001" { + t.Errorf("Expected id to be '1001', but got '%s'", result) + } +} diff --git a/engine/librankings_test.go b/engine/librankings_test.go index b49ec0aec..10ba6f94a 100644 --- a/engine/librankings_test.go +++ b/engine/librankings_test.go @@ -18,6 +18,7 @@ along with this program. If not, see package engine import ( + "encoding/json" "reflect" "testing" "time" @@ -373,3 +374,328 @@ func TestRankingProfileFieldAsString(t *testing.T) { }) } } + +func TestNewRankingSorter(t *testing.T) { + Metrics := map[string]map[string]float64{ + "STATS1": {"*acc": 12.1, "*tcc": 24.2}, + "STATS2": {"*acc": 12.1, "*tcc": 24.3}, + "STATS3": {"*acc": 10.1, "*tcc": 25.3}, + "STATS4": {"*tcc": 26.3}, + } + + tests := []struct { + sortingType string + sortingParams []string + expectErr bool + expectSorterType string + }{ + { + sortingType: utils.MetaAsc, + sortingParams: []string{"*acc"}, + expectErr: false, + expectSorterType: "RankingAscSorter", + }, + { + sortingType: utils.MetaDesc, + sortingParams: []string{"*tcc"}, + expectErr: false, + expectSorterType: "RankingDescSorter", + }, + { + sortingType: "unsupported", + sortingParams: []string{"*tcc"}, + expectErr: true, + expectSorterType: "", + }, + } + + for _, test := range tests { + rkSorter, err := newRankingSorter(test.sortingType, test.sortingParams, Metrics) + + if test.expectErr { + if err == nil { + t.Errorf("Expected an error for sorting type %q, but got none", test.sortingType) + } + } else { + if err != nil { + t.Errorf("Did not expect an error for sorting type %q, but got: %v", test.sortingType, err) + } + switch test.sortingType { + case utils.MetaAsc: + if _, ok := rkSorter.(*rankingAscSorter); !ok { + t.Errorf("Expected sorter type 'rankingAscSorter', but got %T", rkSorter) + } + case utils.MetaDesc: + if _, ok := rkSorter.(*rankingDescSorter); !ok { + t.Errorf("Expected sorter type 'rankingDescSorter', but got %T", rkSorter) + } + } + } + } +} + +func TestRankingProfileSet(t *testing.T) { + tests := []struct { + name string + path []string + val any + expectedErr error + expectedRP RankingProfile + }{ + { + name: "Set Tenant", + path: []string{utils.Tenant}, + val: "cgrates.org", + expectedErr: nil, + expectedRP: RankingProfile{Tenant: "cgrates.org"}, + }, + { + name: "Set ID", + path: []string{utils.ID}, + val: "profile1", + expectedErr: nil, + expectedRP: RankingProfile{ID: "profile1"}, + }, + { + name: "Set Schedule", + path: []string{utils.Schedule}, + val: "0 0 * * *", + expectedErr: nil, + expectedRP: RankingProfile{Schedule: "0 0 * * *"}, + }, + { + name: "Set StatIDs", + path: []string{utils.StatIDs}, + val: []string{"stat1", "stat2"}, + expectedErr: nil, + expectedRP: RankingProfile{StatIDs: []string{"stat1", "stat2"}}, + }, + { + name: "Set MetricIDs", + path: []string{utils.MetricIDs}, + val: []string{"metric1", "metric2"}, + expectedErr: nil, + expectedRP: RankingProfile{MetricIDs: []string{"metric1", "metric2"}}, + }, + { + name: "Set Sorting", + path: []string{utils.Sorting}, + val: "asc", + expectedErr: nil, + expectedRP: RankingProfile{Sorting: "asc"}, + }, + { + name: "Set SortingParameters", + path: []string{utils.SortingParameters}, + val: []string{"param1", "param2"}, + expectedErr: nil, + expectedRP: RankingProfile{SortingParameters: []string{"param1", "param2"}}, + }, + { + name: "Set Stored", + path: []string{utils.Stored}, + val: true, + expectedErr: nil, + expectedRP: RankingProfile{Stored: true}, + }, + { + name: "Set ThresholdIDs", + path: []string{utils.ThresholdIDs}, + val: []string{"threshold1", "threshold2"}, + expectedErr: nil, + expectedRP: RankingProfile{ThresholdIDs: []string{"threshold1", "threshold2"}}, + }, + { + name: "Wrong path", + path: []string{"wrongpath"}, + val: "value", + expectedErr: utils.ErrWrongPath, + expectedRP: RankingProfile{}, + }, + { + name: "Empty path", + path: []string{}, + val: "value", + expectedErr: utils.ErrWrongPath, + expectedRP: RankingProfile{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rp := &RankingProfile{} + err := rp.Set(tt.path, tt.val, false, "") + + if err != tt.expectedErr { + t.Errorf("Test %s failed: expected error %v, got %v", tt.name, tt.expectedErr, err) + } + + if rp.Tenant != tt.expectedRP.Tenant { + t.Errorf("Test %s failed: expected Tenant %s, got %s", tt.name, tt.expectedRP.Tenant, rp.Tenant) + } + if rp.ID != tt.expectedRP.ID { + t.Errorf("Test %s failed: expected ID %s, got %s", tt.name, tt.expectedRP.ID, rp.ID) + } + if rp.Schedule != tt.expectedRP.Schedule { + t.Errorf("Test %s failed: expected Schedule %s, got %s", tt.name, tt.expectedRP.Schedule, rp.Schedule) + } + + if !reflect.DeepEqual(rp.StatIDs, tt.expectedRP.StatIDs) { + t.Errorf("Test %s failed: expected StatIDs %v, got %v", tt.name, tt.expectedRP.StatIDs, rp.StatIDs) + } + if !reflect.DeepEqual(rp.MetricIDs, tt.expectedRP.MetricIDs) { + t.Errorf("Test %s failed: expected MetricIDs %v, got %v", tt.name, tt.expectedRP.MetricIDs, rp.MetricIDs) + } + if !reflect.DeepEqual(rp.SortingParameters, tt.expectedRP.SortingParameters) { + t.Errorf("Test %s failed: expected SortingParameters %v, got %v", tt.name, tt.expectedRP.SortingParameters, rp.SortingParameters) + } + if !reflect.DeepEqual(rp.ThresholdIDs, tt.expectedRP.ThresholdIDs) { + t.Errorf("Test %s failed: expected ThresholdIDs %v, got %v", tt.name, tt.expectedRP.ThresholdIDs, rp.ThresholdIDs) + } + if rp.Sorting != tt.expectedRP.Sorting { + t.Errorf("Test %s failed: expected Sorting %s, got %s", tt.name, tt.expectedRP.Sorting, rp.Sorting) + } + if rp.Stored != tt.expectedRP.Stored { + t.Errorf("Test %s failed: expected Stored %v, got %v", tt.name, tt.expectedRP.Stored, rp.Stored) + } + }) + } +} + +func TestRankingProfileStringJson(t *testing.T) { + tests := []struct { + name string + rp RankingProfile + expectedJSON string + }{ + { + name: "Valid RankingProfile", + rp: RankingProfile{ + Tenant: "cgrates.org", + ID: "profile1", + Schedule: "0 0 * * *", + StatIDs: []string{"stat1", "stat2"}, + MetricIDs: []string{"metric1", "metric2"}, + Sorting: "asc", + SortingParameters: []string{"param1", "param2"}, + Stored: true, + ThresholdIDs: []string{"threshold1"}, + }, + expectedJSON: `{"Tenant":"cgrates.org","ID":"profile1","Schedule":"0 0 * * *","StatIDs":["stat1","stat2"],"MetricIDs":["metric1","metric2"],"Sorting":"asc","SortingParameters":["param1","param2"],"Stored":true,"ThresholdIDs":["threshold1"]}`, + }, + { + name: "Empty RankingProfile", + rp: RankingProfile{ + Tenant: "", + ID: "", + Schedule: "", + StatIDs: []string{}, + MetricIDs: []string{}, + Sorting: "", + SortingParameters: []string{}, + Stored: false, + ThresholdIDs: []string{}, + }, + expectedJSON: `{"Tenant":"","ID":"","Schedule":"","StatIDs":[],"MetricIDs":[],"Sorting":"","SortingParameters":[],"Stored":false,"ThresholdIDs":[]}`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := tt.rp.String() + + var resultMap map[string]interface{} + err := json.Unmarshal([]byte(result), &resultMap) + if err != nil { + t.Errorf("Error unmarshalling result: %v", err) + } + + expectedMap := map[string]interface{}{} + err = json.Unmarshal([]byte(tt.expectedJSON), &expectedMap) + if err != nil { + t.Errorf("Error unmarshalling expected JSON: %v", err) + } + + for key, value1 := range resultMap { + if value2, exists := expectedMap[key]; exists { + if value1Slice, ok1 := value1.([]interface{}); ok1 { + if value2Slice, ok2 := value2.([]interface{}); ok2 { + if len(value1Slice) != len(value2Slice) { + t.Errorf("Test %s failed: slice length mismatch for key %s", tt.name, key) + } + for i, v1 := range value1Slice { + if v1 != value2Slice[i] { + t.Errorf("Test %s failed: slice mismatch for key %s at index %d", tt.name, key, i) + } + } + } + } else { + if value1 != value2 { + t.Errorf("Test %s failed: expected %v for key %s, got %v", tt.name, value2, key, value1) + } + } + } else { + t.Errorf("Test %s failed: key %s not found in expected result", tt.name, key) + } + } + }) + } +} + +func TestTpRankingProfileFieldAsString(t *testing.T) { + tests := []struct { + name string + profile RankingProfile + fldPath []string + expected string + expectErr bool + }{ + { + name: "Valid field path", + profile: RankingProfile{ + Tenant: "cgrates.org", + ID: "profile1", + Schedule: "0 0 * * *", + StatIDs: []string{"stat1", "stat2"}, + MetricIDs: []string{"metric1", "metric2"}, + SortingParameters: []string{"param1", "param2"}, + ThresholdIDs: []string{"threshold1", "threshold2"}, + }, + fldPath: []string{"Tenant"}, + expected: "cgrates.org", + expectErr: false, + }, + { + name: "Invalid field path", + profile: RankingProfile{ + Tenant: "cgrates.org", + ID: "profile1", + Schedule: "0 0 * * *", + StatIDs: []string{"stat1", "stat2"}, + MetricIDs: []string{"metric1", "metric2"}, + SortingParameters: []string{"param1", "param2"}, + ThresholdIDs: []string{"threshold1", "threshold2"}, + }, + fldPath: []string{"NonExistentField"}, + expected: "", + expectErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := tt.profile.FieldAsString(tt.fldPath) + + if tt.expectErr && err == nil { + t.Errorf("Expected an error for test %s, but got none", tt.name) + } + if !tt.expectErr && err != nil { + t.Errorf("Unexpected error for test %s: %v", tt.name, err) + } + + if result != tt.expected { + t.Errorf("Test %s failed: expected %v, got %v", tt.name, tt.expected, result) + } + }) + } +} diff --git a/engine/rankings_test.go b/engine/rankings_test.go index 344845b26..2bcce4bf7 100644 --- a/engine/rankings_test.go +++ b/engine/rankings_test.go @@ -20,7 +20,9 @@ package engine import ( "testing" + "time" + "github.com/cgrates/birpc/context" "github.com/cgrates/cgrates/config" ) @@ -121,3 +123,39 @@ func TestNewRankingService(t *testing.T) { t.Errorf("Expected connMgr to be %v, got %v", connMgr, rankingService.connMgr) } } + +func TestStoreRanking(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + dataDB := NewInternalDB([]string{}, []string{}, map[string]*config.ItemOpts{}) + dm := NewDataManager(dataDB, cfg.CacheCfg(), nil) + rkg := NewRankingS(dm, nil, nil, cfg) + ranking := &Ranking{ + rkPrfl: &RankingProfile{ + Tenant: "cgrates.org", + ID: "ID1", + Schedule: "@every 1s", + StatIDs: []string{"stat1", "stat2"}, + MetricIDs: []string{"metric1", "metric2"}, + Sorting: "asc", + SortingParameters: []string{"metric1:true"}, + Stored: true, + ThresholdIDs: []string{"threshold1"}, + }, + } + ctx := context.Background() + cfg.RankingSCfg().StoreInterval = 0 + if err := rkg.storeRanking(ctx, ranking); err != nil { + t.Errorf("Expected no error when StoreInterval is 0, but got: %v", err) + } + if len(rkg.storedRankings) != 0 { + t.Error("Expected storedRankings to be empty when StoreInterval is 0") + } + cfg.RankingSCfg().StoreInterval = -1 + if err := rkg.storeRanking(ctx, ranking); err != nil { + t.Errorf("Expected no error when StoreInterval is -1, but got: %v", err) + } + cfg.RankingSCfg().StoreInterval = time.Second + if err := rkg.storeRanking(ctx, ranking); err != nil { + t.Errorf("Expected no error when StoreInterval is positive, but got: %v", err) + } +}