From c9972c62f911d83deec755c0bdc87bfc3a413183 Mon Sep 17 00:00:00 2001 From: armirveliaj Date: Thu, 4 Sep 2025 10:59:19 -0400 Subject: [PATCH] Add coverage tests on actions && sessions --- engine/action_trigger_test.go | 98 ++++++++++++++++++++++++++++++++ engine/actions_test.go | 50 ++++++++++++++++ sessions/session_test.go | 70 +++++++++++++++++++++++ sessions/sessionscover_test.go | 101 +++++++++++++++++++++++++++++++++ 4 files changed, 319 insertions(+) diff --git a/engine/action_trigger_test.go b/engine/action_trigger_test.go index 1c5e6be8b..249bc8bee 100644 --- a/engine/action_trigger_test.go +++ b/engine/action_trigger_test.go @@ -537,3 +537,101 @@ func TestUpdateInitialValue(t *testing.T) { }) } } + +func TestUpdateActionTrigger(t *testing.T) { + + attr := &AttrSetActionTrigger{} + updated, err := attr.UpdateActionTrigger(nil, "UTC") + if err == nil || err.Error() != "Empty ActionTrigger" { + t.Errorf("expected error, got %v", err) + } + if updated { + t.Errorf("expected updated=false for nil ActionTrigger") + } + + at := &ActionTrigger{ID: utils.EmptyString} + attr = &AttrSetActionTrigger{ + GroupID: "grp1", + ActionTrigger: map[string]any{"ThresholdType": "min_balance"}, + } + updated, err = attr.UpdateActionTrigger(at, "UTC") + if err == nil { + t.Errorf("expected error for missing ThresholdValue") + } + if updated { + t.Errorf("expected updated=false when mandatory fields missing") + } + + attr = &AttrSetActionTrigger{ + GroupID: "grp1", + ActionTrigger: map[string]any{ + "ThresholdType": "min_balance", + "ThresholdValue": 10.5, + "Recurrent": true, + "Executed": true, + "MinSleep": "1s", + "ExpirationDate": "2025-01-01T00:00:00Z", + "ActivationDate": "2024-01-01T00:00:00Z", + "BalanceType": "minutes", + "Weight": 5, + }, + } + at = &ActionTrigger{ID: utils.EmptyString} + updated, err = attr.UpdateActionTrigger(at, "UTC") + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if !updated { + t.Errorf("expected updated=true for proper ActionTrigger") + } + if at.ID != "grp1" { + t.Errorf("expected ID to be set, got %s", at.ID) + } + if at.ThresholdType != "min_balance" || at.ThresholdValue != 10.5 { + t.Errorf("expected ThresholdType/Value updated, got %s/%v", at.ThresholdType, at.ThresholdValue) + } + if !at.Recurrent || !at.Executed { + t.Errorf("expected Recurrent and Executed to be true") + } + if at.MinSleep != time.Second { + t.Errorf("expected MinSleep=1s, got %v", at.MinSleep) + } + if at.Balance == nil || at.Balance.GetType() != "minutes" { + t.Errorf("expected Balance.Type='minutes', got %v", at.Balance) + } + if at.Weight != 5 { + t.Errorf("expected Weight=5.5, got %v", at.Weight) + } + + attr = &AttrSetActionTrigger{ + GroupID: "NonMatchingGroupID", + ActionTrigger: map[string]any{"ThresholdType": "max_balance"}, + } + oldType := at.ThresholdType + updated, err = attr.UpdateActionTrigger(at, "UTC") + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if updated { + t.Errorf("expected updated=false when GroupID does not match") + } + if at.ThresholdType != oldType { + t.Errorf("expected ThresholdType unchanged, got %s", at.ThresholdType) + } + + attr = &AttrSetActionTrigger{ + UniqueID: "NonMatchingUniqueID", + ActionTrigger: map[string]any{"ThresholdType": "max_balance"}, + } + oldType = at.ThresholdType + updated, err = attr.UpdateActionTrigger(at, "UTC") + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if updated { + t.Errorf("expected updated=false when UniqueID does not match") + } + if at.ThresholdType != oldType { + t.Errorf("expected ThresholdType unchanged, got %s", at.ThresholdType) + } +} diff --git a/engine/actions_test.go b/engine/actions_test.go index f85432fd1..e2a97e1c3 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -19,6 +19,7 @@ package engine import ( "bytes" + "encoding/json" "errors" "fmt" "io" @@ -7603,3 +7604,52 @@ func TestDynamicActionTrigger(t *testing.T) { }) } } + +func TestUnsetRecurrentAction(t *testing.T) { + err := unsetRecurrentAction(nil, &Action{Id: "act1"}, nil, nil, nil, SharedActionsData{}, ActionConnCfg{}) + if err == nil || err.Error() != "nil account" { + t.Errorf("expected error 'nil account', got %v", err) + } + + trigger1 := &ActionTrigger{ + ID: "act1", + Recurrent: true, + Balance: &BalanceFilter{Type: utils.StringPointer("data")}, + } + trigger2 := &ActionTrigger{ + ID: "act2", + Recurrent: true, + Balance: &BalanceFilter{Type: utils.StringPointer("sms")}, + } + + account := &Account{ + ID: "user1", + ActionTriggers: ActionTriggers{trigger1, trigger2}, + } + + action := &Action{ + Id: "act1", + Balance: &BalanceFilter{Type: utils.StringPointer("data")}, + ExtraParameters: func() string { + tp := struct { + GroupID string + UniqueID string + ThresholdType string + }{} + data, _ := json.Marshal(tp) + return string(data) + }(), + } + + err = unsetRecurrentAction(account, action, nil, nil, nil, SharedActionsData{}, ActionConnCfg{}) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + if trigger1.Recurrent { + t.Errorf("expected trigger1.Recurrent to be false after unsetRecurrentAction") + } + if !trigger2.Recurrent { + t.Errorf("expected trigger2.Recurrent to remain true") + } +} diff --git a/sessions/session_test.go b/sessions/session_test.go index cc44306e0..080f60ba2 100644 --- a/sessions/session_test.go +++ b/sessions/session_test.go @@ -772,3 +772,73 @@ func TestUpdateSRuns(t *testing.T) { utils.MetaPostpaid, s.SRuns[0].Event[utils.RequestType]) } } + +func TestSessionLastUsage(t *testing.T) { + sess1 := &Session{} + if l := sess1.lastUsage(); l != 0 { + t.Errorf("expected 0 when no SRun, got %v", l) + } + + sr1 := &SRun{LastUsage: 5 * time.Second} + sess2 := &Session{ + SRuns: []*SRun{sr1}, + } + if l := sess2.lastUsage(); l != 5*time.Second { + t.Errorf("expected 5s, got %v", l) + } + + sr2 := &SRun{LastUsage: 10 * time.Second} + sess3 := &Session{ + SRuns: []*SRun{sr1, sr2}, + } + if l := sess3.lastUsage(); l != 5*time.Second { + t.Errorf("expected first SRun LastUsage 5s, got %v", l) + } +} +func TestSessionMidSessionUsage(t *testing.T) { + sess1 := &Session{} + usage, last := sess1.midSessionUsage(10 * time.Second) + if usage != 10*time.Second || last != nil { + t.Errorf("expected usage=10s and last=nil, got usage=%v, last=%v", usage, last) + } + + sess2 := &Session{ + SRuns: []*SRun{ + {LastUsage: 2 * time.Second, TotalUsage: 3 * time.Second}, + }, + } + usage, last = sess2.midSessionUsage(10 * time.Second) + if last != nil { + t.Errorf("expected last=nil, got %v", last) + } + if usage <= 0 { + t.Errorf("expected positive usage, got %v", usage) + } + + sess3 := &Session{ + SRuns: []*SRun{ + {LastUsage: 5 * time.Second, TotalUsage: 15 * time.Second}, + }, + } + usage, last = sess3.midSessionUsage(10 * time.Second) + if usage != 0 { + t.Errorf("expected usage=0, got %v", usage) + } + if last == nil || *last <= 0 { + t.Errorf("expected last>0, got %v", last) + } + + sess4 := &Session{ + SRuns: []*SRun{ + {LastUsage: 2 * time.Second, TotalUsage: 3 * time.Second}, + {LastUsage: 1 * time.Second, TotalUsage: 4 * time.Second}, + }, + } + usage, last = sess4.midSessionUsage(15 * time.Second) + if last != nil { + t.Errorf("expected last=nil, got %v", last) + } + if usage <= 0 { + t.Errorf("expected positive usage, got %v", usage) + } +} diff --git a/sessions/sessionscover_test.go b/sessions/sessionscover_test.go index 227654156..e16b24df5 100644 --- a/sessions/sessionscover_test.go +++ b/sessions/sessionscover_test.go @@ -26,6 +26,7 @@ import ( "os" "reflect" "strings" + "sync" "testing" "time" @@ -4744,3 +4745,103 @@ func TestSyncSessionsSync(t *testing.T) { //There are no sessions to be removed sessions.terminateSyncSessions([]string{"no_sesssion"}) } + +func TestBiRPCv1Sleep(t *testing.T) { + ss := &SessionS{} + + ctx := &context.Context{} + reply := "" + duration := 10 * time.Millisecond + + start := time.Now() + err := ss.BiRPCv1Sleep(ctx, &utils.DurationArgs{Duration: duration}, &reply) + elapsed := time.Since(start) + + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if reply != utils.OK { + t.Errorf("expected reply = %s, got %s", utils.OK, reply) + } + + if elapsed < duration { + t.Errorf("function returned too quickly, expected >= %v, got %v", duration, elapsed) + } +} + +func TestUnregisterSession(t *testing.T) { + ss := &SessionS{ + aSessions: map[string]*Session{ + "active1": {ClientConnID: "conn01"}, + }, + pSessions: map[string]*Session{ + "passive1": {ClientConnID: "conn02"}, + }, + bkpSessionIDs: make(utils.StringSet), + removeSsCGRIDs: make(utils.StringSet), + cgrCfg: config.NewDefaultCGRConfig(), + } + + ss.cgrCfg.SessionSCfg().BackupInterval = 10 + + ok := ss.unregisterSession("active1", false) + if !ok { + t.Errorf("expected active session to be unregistered") + } + if _, exists := ss.aSessions["active1"]; exists { + t.Errorf("active session still exists after unregister") + } + if _, inRemove := ss.removeSsCGRIDs["active1"]; !inRemove { + t.Errorf("active session ID not added to removeSsCGRIDs") + } + + ok = ss.unregisterSession("passive1", true) + if !ok { + t.Errorf("expected passive session to be unregistered") + } + if _, exists := ss.pSessions["passive1"]; exists { + t.Errorf("passive session still exists after unregister") + } +} + +func TestBiRPCv1BackupActiveSessions(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + data, _ := engine.NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg.CacheCfg(), nil) + + sess := &Session{ + CGRID: "sess01", + ClientConnID: "", + EventStart: map[string]any{ + "Destination": "1002", + "Tenant": "cgrates.org", + }, + lk: sync.RWMutex{}, + } + + ss := &SessionS{ + aSessions: map[string]*Session{ + sess.CGRID: sess, + }, + cgrCfg: cfg, + dm: dm, + storeSessMux: sync.RWMutex{}, + } + + var reply int + if err := ss.BiRPCv1BackupActiveSessions(context.Background(), "", &reply); err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if reply != 1 { + t.Errorf("expected 1 session stored, got %d", reply) + } + + storedSessions, err := dm.GetSessionsBackup(cfg.GeneralCfg().NodeID, cfg.GeneralCfg().DefaultTenant) + if err != nil { + t.Fatalf("failed to get backup sessions: %v", err) + } + if len(storedSessions) != 1 || storedSessions[0].CGRID != "sess01" { + t.Errorf("expected stored session with CGRID 'sess01', got %+v", storedSessions) + } +}