From 5766f2f26521a32fa73216ce2b8c1d858b045b50 Mon Sep 17 00:00:00 2001 From: Long Zhang Date: Fri, 29 Aug 2025 13:23:34 +0200 Subject: [PATCH 1/2] fix stake change comparision, skip when DelegatedTokens is zero in the previous check Signed-off-by: Long Zhang --- td2/alert.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/td2/alert.go b/td2/alert.go index 6c49058..7f8369e 100644 --- a/td2/alert.go +++ b/td2/alert.go @@ -742,7 +742,7 @@ func evaluateRPCNodeDownAlert(cc *ChainConfig) (bool, bool) { func evaluateStakeChangeAlert(cc *ChainConfig) (bool, bool) { alert, resolved := false, false - if cc.valInfo != nil && cc.lastValInfo != nil { + if cc.valInfo != nil && cc.lastValInfo != nil && cc.lastValInfo.DelegatedTokens > 0 { stakeNow := cc.valInfo.DelegatedTokens stakeBefore := cc.lastValInfo.DelegatedTokens stakeChangePercent := (stakeNow - stakeBefore) / stakeBefore From 2c42c691018cf66e9ea7c3d76d3d92a273a45063 Mon Sep 17 00:00:00 2001 From: Long Zhang Date: Fri, 29 Aug 2025 13:49:55 +0200 Subject: [PATCH 2/2] add more unit tests for stsake change alerts Signed-off-by: Long Zhang --- td2/alert_test.go | 120 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/td2/alert_test.go b/td2/alert_test.go index c4402ff..f79cdca 100644 --- a/td2/alert_test.go +++ b/td2/alert_test.go @@ -1612,6 +1612,126 @@ func TestEvaluateStakeChangeAlert(t *testing.T) { expectedResolved: false, description: "Should not alert when stake change is within threshold", }, + { + name: "should not trigger alert when previous stake is zero (avoid division by zero)", + currentStake: 1000.0, + previousStake: 0.0, + increaseThreshold: 0.15, + dropThreshold: 0.10, + existingAlert: false, + expectedAlert: false, + expectedResolved: false, + description: "Should not alert and avoid division by zero when previous stake is zero", + }, + { + name: "should not process alert when previous stake becomes zero", + currentStake: 1000.0, + previousStake: 0.0, + increaseThreshold: 0.15, + dropThreshold: 0.10, + existingAlert: true, + expectedAlert: false, + expectedResolved: false, + description: "Should not process alert when previous stake becomes zero to avoid division by zero", + }, + { + name: "should not trigger alert when both current and previous stakes are zero", + currentStake: 0.0, + previousStake: 0.0, + increaseThreshold: 0.15, + dropThreshold: 0.10, + existingAlert: false, + expectedAlert: false, + expectedResolved: false, + description: "Should not alert when both stakes are zero", + }, + { + name: "should trigger alert when current stake drops to zero", + currentStake: 0.0, + previousStake: 1000.0, + increaseThreshold: 0.15, + dropThreshold: 0.10, + existingAlert: false, + expectedAlert: true, + expectedResolved: false, + description: "Should trigger alert when stake drops to zero (100% drop)", + }, + } + + // Add test cases for nil value scenarios to ensure robustness + nilTests := []struct { + name string + valInfo *ValInfo + lastValInfo *ValInfo + existingAlert bool + expectedAlert bool + expectedResolved bool + description string + }{ + { + name: "should not trigger alert when valInfo is nil", + valInfo: nil, + lastValInfo: &ValInfo{DelegatedTokens: 1000.0}, + existingAlert: false, + expectedAlert: false, + expectedResolved: false, + description: "Should not alert when valInfo is nil", + }, + { + name: "should not trigger alert when lastValInfo is nil", + valInfo: &ValInfo{DelegatedTokens: 1000.0}, + lastValInfo: nil, + existingAlert: false, + expectedAlert: false, + expectedResolved: false, + description: "Should not alert when lastValInfo is nil", + }, + { + name: "should not trigger alert when both valInfo and lastValInfo are nil", + valInfo: nil, + lastValInfo: nil, + existingAlert: false, + expectedAlert: false, + expectedResolved: false, + description: "Should not alert when both valInfo and lastValInfo are nil", + }, + } + + // Run the nil value tests + for _, tt := range nilTests { + t.Run(tt.name, func(t *testing.T) { + // Reset alarms for each test + testAlarms.AllAlarms = make(map[string]map[string]alertMsgCache) + + cc := &ChainConfig{ + name: "test-chain", + ChainId: "test-chain-1", + ValAddress: "testval123", + valInfo: tt.valInfo, + lastValInfo: tt.lastValInfo, + Alerts: AlertConfig{ + StakeChangeIncreaseThreshold: &[]float64{0.15}[0], + StakeChangeDropThreshold: &[]float64{0.10}[0], + }, + } + + if tt.existingAlert { + testAlarms.AllAlarms["test-chain"] = make(map[string]alertMsgCache) + testAlarms.AllAlarms["test-chain"]["StakeChange_testval123"] = alertMsgCache{ + Message: "test alert", + SentTime: time.Now(), + } + } + + alert, resolved := evaluateStakeChangeAlert(cc) + + if alert != tt.expectedAlert { + t.Errorf("%s: expected alert %v, got %v", tt.description, tt.expectedAlert, alert) + } + if resolved != tt.expectedResolved { + t.Errorf("%s: expected resolved %v, got %v", tt.description, tt.expectedResolved, resolved) + } + }) } for _, tt := range tests {