diff --git a/engine/librankings.go b/engine/librankings.go index 0b87081b2..e82e00665 100644 --- a/engine/librankings.go +++ b/engine/librankings.go @@ -1,3 +1,21 @@ +/* +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 engine import ( diff --git a/engine/librankings_test.go b/engine/librankings_test.go new file mode 100644 index 000000000..0034fc131 --- /dev/null +++ b/engine/librankings_test.go @@ -0,0 +1,241 @@ +/* +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 engine + +import ( + "reflect" + "testing" + "time" + + "github.com/cgrates/cgrates/utils" +) + +func TestRankingProfileTenantID(t *testing.T) { + tests := []struct { + name string + profile RankingProfile + expected string + }{ + { + name: "Tenant and ID", + profile: RankingProfile{Tenant: "cgrates.org", ID: "1"}, + expected: "cgrates.org:1", + }, + { + name: "Empty tenant", + profile: RankingProfile{ID: "2"}, + expected: ":2", + }, + { + name: "Empty ID", + profile: RankingProfile{Tenant: "cgrates.org"}, + expected: "cgrates.org:", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := tc.profile.TenantID() + if got != tc.expected { + t.Errorf("TenantID() = %v, want %v", got, tc.expected) + } + }) + } +} + +func TestRankingProfileClone(t *testing.T) { + original := &RankingProfile{ + Tenant: "cgrates.org", + ID: "ID1", + Schedule: "@every 1sec", + Sorting: "asc", + } + + cloned := original.Clone() + + if cloned.Tenant != original.Tenant { + t.Errorf("Expected Tenant %s, got %s", original.Tenant, cloned.Tenant) + } + if cloned.ID != original.ID { + t.Errorf("Expected ID %s, got %s", original.ID, cloned.ID) + } + if cloned.Schedule != original.Schedule { + t.Errorf("Expected Schedule %s, got %s", original.Schedule, cloned.Schedule) + } + if cloned.Sorting != original.Sorting { + t.Errorf("Expected Sorting %s, got %s", original.Sorting, cloned.Sorting) + } + + if cloned == original { + t.Error("Clone should return a new instance, but it returned the same reference") + } +} + +func TestNewRankingFromProfile(t *testing.T) { + profile := &RankingProfile{ + Tenant: "cgrates.org", + ID: "ID1", + Sorting: "asc", + MetricIDs: []string{"metric1", "metric2"}, + SortingParameters: []string{"param1", "param2"}, + } + + ranking := NewRankingFromProfile(profile) + + if ranking.Tenant != profile.Tenant { + t.Errorf("Expected Tenant %s, got %s", profile.Tenant, ranking.Tenant) + } + if ranking.ID != profile.ID { + t.Errorf("Expected ID %s, got %s", profile.ID, ranking.ID) + } + if ranking.Sorting != profile.Sorting { + t.Errorf("Expected Sorting %s, got %s", profile.Sorting, ranking.Sorting) + } + + if ranking.Metrics == nil { + t.Error("Expected Metrics map to be initialized, but it is nil") + } + + expectedMetricIDs := utils.NewStringSet(profile.MetricIDs) + if !ranking.metricIDs.Equals(expectedMetricIDs) { + t.Errorf("Expected metricIDs %v, got %v", expectedMetricIDs, ranking.metricIDs) + } + + if len(ranking.SortingParameters) != len(profile.SortingParameters) { + t.Errorf("Expected SortingParameters length %d, got %d", len(profile.SortingParameters), len(ranking.SortingParameters)) + } + for i, param := range profile.SortingParameters { + if ranking.SortingParameters[i] != param { + t.Errorf("Expected SortingParameters[%d] %s, got %s", i, param, ranking.SortingParameters[i]) + } + } + + if ranking.rkPrfl != profile { + t.Error("Expected rkPrfl to reference the original profile") + } +} + +func TestRankingTenantID(t *testing.T) { + r := &Ranking{ + Tenant: "cgrates.org", + ID: "1", + } + expectedTenantID := "cgrates.org:1" + actualTenantID := r.TenantID() + if actualTenantID != expectedTenantID { + t.Errorf("Expected tenant ID %s, got %s", expectedTenantID, actualTenantID) + } +} + +func TestRanking_asRankingSummary(t *testing.T) { + rk := &Ranking{ + Tenant: "cgrates.org", + ID: "ID1", + LastUpdate: time.Now(), + SortedStatIDs: []string{"stat1", "stat2", "stat3"}, + } + + rkSummary := rk.asRankingSummary() + + if rkSummary.Tenant != rk.Tenant { + t.Errorf("Expected Tenant %s, but got %s", rk.Tenant, rkSummary.Tenant) + } + if rkSummary.ID != rk.ID { + t.Errorf("Expected ID %s, but got %s", rk.ID, rkSummary.ID) + } + if !rkSummary.LastUpdate.Equal(rk.LastUpdate) { + t.Errorf("Expected LastUpdate %v, but got %v", rk.LastUpdate, rkSummary.LastUpdate) + } + if !reflect.DeepEqual(rkSummary.SortedStatIDs, rk.SortedStatIDs) { + t.Errorf("Expected SortedStatIDs %v, but got %v", rk.SortedStatIDs, rkSummary.SortedStatIDs) + } + + if &rkSummary.SortedStatIDs == &rk.SortedStatIDs { + t.Errorf("Expected SortedStatIDs slice to be copied, not referenced") + } +} + +func TestRankingSortStats(t *testing.T) { + metrics := map[string]map[string]float64{ + "stat1": { + "metric1": 5.0, + "metric2": 10.0, + }, + "stat2": { + "metric1": 3.0, + "metric2": 12.0, + }, + "stat3": { + "metric1": 7.0, + "metric2": 8.0, + }, + } + + tests := []struct { + name string + sortingType string + sortingParams []string + expectedOrder []string + expectError bool + }{ + { + name: "Sort Descending by metric1", + sortingType: utils.MetaDesc, + sortingParams: []string{"metric1"}, + expectedOrder: []string{"stat3", "stat1", "stat2"}, + expectError: false, + }, + { + name: "Sort Ascending by metric2", + sortingType: utils.MetaAsc, + sortingParams: []string{"metric2"}, + expectedOrder: []string{"stat3", "stat1", "stat2"}, + expectError: false, + }, + { + name: "Unsupported sorting type", + sortingType: "unsupported", + sortingParams: []string{"metric1"}, + expectedOrder: nil, + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + sortedStatIDs, err := rankingSortStats(tt.sortingType, tt.sortingParams, metrics) + if tt.expectError { + if err == nil { + t.Errorf("expected error but got nil") + } + return + } + + if err != nil { + t.Errorf("unexpected error: %v", err) + return + } + + for i, id := range sortedStatIDs { + if id != tt.expectedOrder[i] { + t.Errorf("expected sorted statID %v at index %d, got %v", tt.expectedOrder[i], i, id) + } + } + }) + } +} diff --git a/engine/libtrends_test.go b/engine/libtrends_test.go index 7d1c09a6f..0dd39aad2 100644 --- a/engine/libtrends_test.go +++ b/engine/libtrends_test.go @@ -370,3 +370,43 @@ func TestGetTrendGrowth(t *testing.T) { t.Errorf("Expected error ErrCorrelationUndefined, got: %v", err) } } + +func TestNewTrendFromProfile(t *testing.T) { + profile := &TrendProfile{ + Tenant: "cgrates.org", + ID: "trendProfileID", + Schedule: "@every 1sec", + StatID: "statID1", + QueueLength: 10, + TTL: 5 * time.Minute, + MinItems: 1, + CorrelationType: "average", + Tolerance: 0.1, + Stored: true, + ThresholdIDs: []string{"threshold1", "threshold2"}, + } + + trend := NewTrendFromProfile(profile) + + if trend.Tenant != profile.Tenant { + t.Errorf("Expected Tenant %s, got %s", profile.Tenant, trend.Tenant) + } + if trend.ID != profile.ID { + t.Errorf("Expected ID %s, got %s", profile.ID, trend.ID) + } + if trend.RunTimes == nil { + t.Errorf("Expected RunTimes to be initialized, got nil") + } + if len(trend.RunTimes) != 0 { + t.Errorf("Expected RunTimes to be empty, got length %d", len(trend.RunTimes)) + } + if trend.Metrics == nil { + t.Errorf("Expected Metrics to be initialized, got nil") + } + if len(trend.Metrics) != 0 { + t.Errorf("Expected Metrics to be empty, got length %d", len(trend.Metrics)) + } + if trend.tPrfl != profile { + t.Errorf("Expected tPrfl to point to the original profile, got a different value") + } +}