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")
+ }
+}