2024-02-11 22:31:46 +05:30
package rules
import (
"testing"
"time"
2025-07-30 19:25:27 +05:30
"github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest"
2025-03-20 21:01:41 +05:30
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
2025-04-18 00:04:25 +05:30
ruletypes "github.com/SigNoz/signoz/pkg/types/ruletypes"
2025-05-03 18:30:07 +05:30
"github.com/SigNoz/signoz/pkg/valuer"
2024-02-11 22:31:46 +05:30
pql "github.com/prometheus/prometheus/promql"
"github.com/stretchr/testify/assert"
)
2025-09-12 13:11:54 +05:30
func getVectorValues ( vectors [ ] ruletypes . Sample ) [ ] float64 {
if len ( vectors ) == 0 {
return [ ] float64 { } // Return empty slice instead of nil
}
var values [ ] float64
for _ , v := range vectors {
values = append ( values , v . V )
}
return values
}
2024-02-11 22:31:46 +05:30
func TestPromRuleShouldAlert ( t * testing . T ) {
2025-04-18 00:04:25 +05:30
postableRule := ruletypes . PostableRule {
2024-03-27 20:25:18 +05:30
AlertName : "Test Rule" ,
2025-04-18 00:04:25 +05:30
AlertType : ruletypes . AlertTypeMetric ,
RuleType : ruletypes . RuleTypeProm ,
EvalWindow : ruletypes . Duration ( 5 * time . Minute ) ,
Frequency : ruletypes . Duration ( 1 * time . Minute ) ,
RuleCondition : & ruletypes . RuleCondition {
2024-02-11 22:31:46 +05:30
CompositeQuery : & v3 . CompositeQuery {
QueryType : v3 . QueryTypePromQL ,
PromQueries : map [ string ] * v3 . PromQuery {
"A" : {
Query : "dummy_query" , // This is not used in the test
} ,
} ,
} ,
} ,
}
cases := [ ] struct {
2025-09-12 13:11:54 +05:30
values pql . Series
expectAlert bool
compareOp string
matchType string
target float64
expectedAlertSample v3 . Point
expectedVectorValues [ ] float64 // Expected values in result vector
2024-02-11 22:31:46 +05:30
} {
// Test cases for Equals Always
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 0.0 } ,
{ F : 0.0 } ,
{ F : 0.0 } ,
{ F : 0.0 } ,
{ F : 0.0 } ,
} ,
} ,
2025-09-12 13:11:54 +05:30
expectAlert : true ,
compareOp : "3" , // Equals
matchType : "2" , // Always
target : 0.0 ,
expectedAlertSample : v3 . Point { Value : 0.0 } ,
expectedVectorValues : [ ] float64 { 0.0 } ,
2024-02-11 22:31:46 +05:30
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 0.0 } ,
{ F : 0.0 } ,
{ F : 0.0 } ,
{ F : 0.0 } ,
{ F : 1.0 } ,
} ,
} ,
2025-09-12 13:11:54 +05:30
expectAlert : false ,
compareOp : "3" , // Equals
matchType : "2" , // Always
target : 0.0 ,
expectedVectorValues : [ ] float64 { } ,
2024-02-11 22:31:46 +05:30
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 0.0 } ,
{ F : 1.0 } ,
{ F : 0.0 } ,
{ F : 1.0 } ,
{ F : 1.0 } ,
} ,
} ,
2025-09-12 13:11:54 +05:30
expectAlert : false ,
compareOp : "3" , // Equals
matchType : "2" , // Always
target : 0.0 ,
expectedVectorValues : [ ] float64 { } ,
2024-02-11 22:31:46 +05:30
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 1.0 } ,
{ F : 1.0 } ,
{ F : 1.0 } ,
{ F : 1.0 } ,
{ F : 1.0 } ,
} ,
} ,
expectAlert : false ,
compareOp : "3" , // Equals
matchType : "2" , // Always
target : 0.0 ,
} ,
// Test cases for Equals Once
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 0.0 } ,
{ F : 0.0 } ,
{ F : 0.0 } ,
{ F : 0.0 } ,
{ F : 0.0 } ,
} ,
} ,
2025-09-12 13:11:54 +05:30
expectAlert : true ,
compareOp : "3" , // Equals
matchType : "1" , // Once
target : 0.0 ,
expectedAlertSample : v3 . Point { Value : 0.0 } ,
expectedVectorValues : [ ] float64 { 0.0 } ,
2024-02-11 22:31:46 +05:30
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 0.0 } ,
{ F : 0.0 } ,
{ F : 0.0 } ,
{ F : 0.0 } ,
{ F : 1.0 } ,
} ,
} ,
2024-09-04 18:30:04 +05:30
expectAlert : true ,
compareOp : "3" , // Equals
matchType : "1" , // Once
target : 0.0 ,
expectedAlertSample : v3 . Point { Value : 0.0 } ,
2024-02-11 22:31:46 +05:30
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 0.0 } ,
{ F : 1.0 } ,
{ F : 0.0 } ,
{ F : 1.0 } ,
{ F : 1.0 } ,
} ,
} ,
2024-09-04 18:30:04 +05:30
expectAlert : true ,
compareOp : "3" , // Equals
matchType : "1" , // Once
target : 0.0 ,
expectedAlertSample : v3 . Point { Value : 0.0 } ,
2024-02-11 22:31:46 +05:30
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 1.0 } ,
{ F : 1.0 } ,
{ F : 1.0 } ,
{ F : 1.0 } ,
{ F : 1.0 } ,
} ,
} ,
2025-09-12 13:11:54 +05:30
expectAlert : false ,
compareOp : "3" , // Equals
matchType : "1" , // Once
target : 0.0 ,
expectedVectorValues : [ ] float64 { } ,
2024-02-11 22:31:46 +05:30
} ,
// Test cases for Greater Than Always
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 10.0 } ,
{ F : 4.0 } ,
{ F : 6.0 } ,
{ F : 8.0 } ,
{ F : 2.0 } ,
} ,
} ,
2025-09-12 13:11:54 +05:30
expectAlert : true ,
compareOp : "1" , // Greater Than
matchType : "2" , // Always
target : 1.5 ,
expectedAlertSample : v3 . Point { Value : 2.0 } ,
expectedVectorValues : [ ] float64 { 2.0 } ,
2024-09-04 18:30:04 +05:30
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 11.0 } ,
{ F : 4.0 } ,
{ F : 3.0 } ,
{ F : 7.0 } ,
{ F : 12.0 } ,
} ,
} ,
expectAlert : true ,
compareOp : "1" , // Above
matchType : "2" , // Always
target : 2.0 ,
expectedAlertSample : v3 . Point { Value : 3.0 } ,
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 11.0 } ,
{ F : 4.0 } ,
{ F : 3.0 } ,
{ F : 7.0 } ,
{ F : 12.0 } ,
} ,
} ,
expectAlert : true ,
compareOp : "2" , // Below
matchType : "2" , // Always
target : 13.0 ,
expectedAlertSample : v3 . Point { Value : 12.0 } ,
2024-02-11 22:31:46 +05:30
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 10.0 } ,
{ F : 4.0 } ,
{ F : 6.0 } ,
{ F : 8.0 } ,
{ F : 2.0 } ,
} ,
} ,
expectAlert : false ,
compareOp : "1" , // Greater Than
matchType : "2" , // Always
target : 4.5 ,
} ,
// Test cases for Greater Than Once
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 10.0 } ,
{ F : 4.0 } ,
{ F : 6.0 } ,
{ F : 8.0 } ,
{ F : 2.0 } ,
} ,
} ,
2025-09-12 13:11:54 +05:30
expectAlert : true ,
compareOp : "1" , // Greater Than
matchType : "1" , // Once
target : 4.5 ,
expectedAlertSample : v3 . Point { Value : 10.0 } ,
expectedVectorValues : [ ] float64 { 10.0 } ,
2024-02-11 22:31:46 +05:30
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 4.0 } ,
{ F : 4.0 } ,
{ F : 4.0 } ,
{ F : 4.0 } ,
{ F : 4.0 } ,
} ,
} ,
expectAlert : false ,
compareOp : "1" , // Greater Than
matchType : "1" , // Once
target : 4.5 ,
} ,
// Test cases for Not Equals Always
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 0.0 } ,
{ F : 1.0 } ,
{ F : 0.0 } ,
{ F : 1.0 } ,
{ F : 0.0 } ,
} ,
} ,
expectAlert : false ,
compareOp : "4" , // Not Equals
matchType : "2" , // Always
target : 0.0 ,
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 1.0 } ,
{ F : 1.0 } ,
{ F : 1.0 } ,
{ F : 1.0 } ,
{ F : 0.0 } ,
} ,
} ,
expectAlert : false ,
compareOp : "4" , // Not Equals
matchType : "2" , // Always
target : 0.0 ,
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 1.0 } ,
{ F : 1.0 } ,
{ F : 1.0 } ,
{ F : 1.0 } ,
{ F : 1.0 } ,
} ,
} ,
2024-09-04 18:30:04 +05:30
expectAlert : true ,
compareOp : "4" , // Not Equals
matchType : "2" , // Always
target : 0.0 ,
expectedAlertSample : v3 . Point { Value : 1.0 } ,
2024-02-11 22:31:46 +05:30
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 1.0 } ,
{ F : 0.0 } ,
{ F : 1.0 } ,
{ F : 1.0 } ,
{ F : 1.0 } ,
} ,
} ,
expectAlert : false ,
compareOp : "4" , // Not Equals
matchType : "2" , // Always
target : 0.0 ,
} ,
// Test cases for Not Equals Once
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 0.0 } ,
{ F : 1.0 } ,
{ F : 0.0 } ,
{ F : 1.0 } ,
{ F : 0.0 } ,
} ,
} ,
2024-09-04 18:30:04 +05:30
expectAlert : true ,
compareOp : "4" , // Not Equals
matchType : "1" , // Once
target : 0.0 ,
expectedAlertSample : v3 . Point { Value : 1.0 } ,
2024-02-11 22:31:46 +05:30
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 0.0 } ,
{ F : 0.0 } ,
{ F : 0.0 } ,
{ F : 0.0 } ,
{ F : 0.0 } ,
} ,
} ,
expectAlert : false ,
compareOp : "4" , // Not Equals
matchType : "1" , // Once
target : 0.0 ,
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 0.0 } ,
{ F : 0.0 } ,
{ F : 1.0 } ,
{ F : 0.0 } ,
{ F : 1.0 } ,
} ,
} ,
2024-09-04 18:30:04 +05:30
expectAlert : true ,
compareOp : "4" , // Not Equals
matchType : "1" , // Once
target : 0.0 ,
expectedAlertSample : v3 . Point { Value : 1.0 } ,
2024-02-11 22:31:46 +05:30
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 1.0 } ,
{ F : 1.0 } ,
{ F : 1.0 } ,
{ F : 1.0 } ,
{ F : 1.0 } ,
} ,
} ,
2024-09-04 18:30:04 +05:30
expectAlert : true ,
compareOp : "4" , // Not Equals
matchType : "1" , // Once
target : 0.0 ,
expectedAlertSample : v3 . Point { Value : 1.0 } ,
2024-02-11 22:31:46 +05:30
} ,
// Test cases for Less Than Always
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 1.5 } ,
{ F : 1.5 } ,
{ F : 1.5 } ,
{ F : 1.5 } ,
{ F : 1.5 } ,
} ,
} ,
2024-09-04 18:30:04 +05:30
expectAlert : true ,
compareOp : "2" , // Less Than
matchType : "2" , // Always
target : 4 ,
expectedAlertSample : v3 . Point { Value : 1.5 } ,
2024-02-11 22:31:46 +05:30
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 4.5 } ,
{ F : 4.5 } ,
{ F : 4.5 } ,
{ F : 4.5 } ,
{ F : 4.5 } ,
} ,
} ,
expectAlert : false ,
compareOp : "2" , // Less Than
matchType : "2" , // Always
target : 4 ,
} ,
// Test cases for Less Than Once
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 4.5 } ,
{ F : 4.5 } ,
{ F : 4.5 } ,
{ F : 4.5 } ,
{ F : 2.5 } ,
} ,
} ,
2024-09-04 18:30:04 +05:30
expectAlert : true ,
compareOp : "2" , // Less Than
matchType : "1" , // Once
target : 4 ,
expectedAlertSample : v3 . Point { Value : 2.5 } ,
2024-02-11 22:31:46 +05:30
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 4.5 } ,
{ F : 4.5 } ,
{ F : 4.5 } ,
{ F : 4.5 } ,
{ F : 4.5 } ,
} ,
} ,
expectAlert : false ,
compareOp : "2" , // Less Than
matchType : "1" , // Once
target : 4 ,
} ,
// Test cases for OnAverage
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 10.0 } ,
{ F : 4.0 } ,
{ F : 6.0 } ,
{ F : 8.0 } ,
{ F : 2.0 } ,
} ,
} ,
2024-09-04 18:30:04 +05:30
expectAlert : true ,
compareOp : "3" , // Equals
matchType : "3" , // OnAverage
target : 6.0 ,
expectedAlertSample : v3 . Point { Value : 6.0 } ,
2024-02-11 22:31:46 +05:30
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 10.0 } ,
{ F : 4.0 } ,
{ F : 6.0 } ,
{ F : 8.0 } ,
{ F : 2.0 } ,
} ,
} ,
expectAlert : false ,
compareOp : "3" , // Equals
matchType : "3" , // OnAverage
target : 4.5 ,
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 10.0 } ,
{ F : 4.0 } ,
{ F : 6.0 } ,
{ F : 8.0 } ,
{ F : 2.0 } ,
} ,
} ,
2024-09-04 18:30:04 +05:30
expectAlert : true ,
compareOp : "4" , // Not Equals
matchType : "3" , // OnAverage
target : 4.5 ,
expectedAlertSample : v3 . Point { Value : 6.0 } ,
2024-02-11 22:31:46 +05:30
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 10.0 } ,
{ F : 4.0 } ,
{ F : 6.0 } ,
{ F : 8.0 } ,
{ F : 2.0 } ,
} ,
} ,
expectAlert : false ,
compareOp : "4" , // Not Equals
matchType : "3" , // OnAverage
target : 6.0 ,
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 10.0 } ,
{ F : 4.0 } ,
{ F : 6.0 } ,
{ F : 8.0 } ,
{ F : 2.0 } ,
} ,
} ,
2024-09-04 18:30:04 +05:30
expectAlert : true ,
compareOp : "1" , // Greater Than
matchType : "3" , // OnAverage
target : 4.5 ,
expectedAlertSample : v3 . Point { Value : 6.0 } ,
2024-02-11 22:31:46 +05:30
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 10.0 } ,
{ F : 4.0 } ,
{ F : 6.0 } ,
{ F : 8.0 } ,
{ F : 2.0 } ,
} ,
} ,
2024-09-04 18:30:04 +05:30
expectAlert : true ,
compareOp : "2" , // Less Than
matchType : "3" , // OnAverage
target : 12.0 ,
expectedAlertSample : v3 . Point { Value : 6.0 } ,
2024-02-11 22:31:46 +05:30
} ,
// Test cases for InTotal
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 10.0 } ,
{ F : 4.0 } ,
{ F : 6.0 } ,
{ F : 8.0 } ,
{ F : 2.0 } ,
} ,
} ,
2024-09-04 18:30:04 +05:30
expectAlert : true ,
compareOp : "3" , // Equals
matchType : "4" , // InTotal
target : 30.0 ,
expectedAlertSample : v3 . Point { Value : 30.0 } ,
2024-02-11 22:31:46 +05:30
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 10.0 } ,
{ F : 4.0 } ,
{ F : 6.0 } ,
{ F : 8.0 } ,
{ F : 2.0 } ,
} ,
} ,
expectAlert : false ,
compareOp : "3" , // Equals
matchType : "4" , // InTotal
target : 20.0 ,
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 10.0 } ,
} ,
} ,
2024-09-04 18:30:04 +05:30
expectAlert : true ,
compareOp : "4" , // Not Equals
matchType : "4" , // InTotal
target : 9.0 ,
expectedAlertSample : v3 . Point { Value : 10.0 } ,
2024-02-11 22:31:46 +05:30
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 10.0 } ,
} ,
} ,
expectAlert : false ,
compareOp : "4" , // Not Equals
matchType : "4" , // InTotal
target : 10.0 ,
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 10.0 } ,
{ F : 10.0 } ,
} ,
} ,
2024-09-04 18:30:04 +05:30
expectAlert : true ,
compareOp : "1" , // Greater Than
matchType : "4" , // InTotal
target : 10.0 ,
expectedAlertSample : v3 . Point { Value : 20.0 } ,
2024-02-11 22:31:46 +05:30
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 10.0 } ,
{ F : 10.0 } ,
} ,
} ,
expectAlert : false ,
compareOp : "1" , // Greater Than
matchType : "4" , // InTotal
target : 20.0 ,
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 10.0 } ,
{ F : 10.0 } ,
} ,
} ,
2024-09-04 18:30:04 +05:30
expectAlert : true ,
compareOp : "2" , // Less Than
matchType : "4" , // InTotal
target : 30.0 ,
expectedAlertSample : v3 . Point { Value : 20.0 } ,
2024-02-11 22:31:46 +05:30
} ,
{
values : pql . Series {
Floats : [ ] pql . FPoint {
{ F : 10.0 } ,
{ F : 10.0 } ,
} ,
} ,
expectAlert : false ,
compareOp : "2" , // Less Than
matchType : "4" , // InTotal
target : 20.0 ,
} ,
}
2025-07-30 19:25:27 +05:30
logger := instrumentationtest . New ( ) . Logger ( )
2024-02-11 22:31:46 +05:30
for idx , c := range cases {
2025-04-18 00:04:25 +05:30
postableRule . RuleCondition . CompareOp = ruletypes . CompareOp ( c . compareOp )
postableRule . RuleCondition . MatchType = ruletypes . MatchType ( c . matchType )
2024-02-11 22:31:46 +05:30
postableRule . RuleCondition . Target = & c . target
2025-09-12 13:11:54 +05:30
postableRule . RuleCondition . Thresholds = & ruletypes . RuleThresholdData {
Kind : ruletypes . BasicThresholdKind ,
Spec : ruletypes . BasicRuleThresholds {
{
TargetValue : & c . target ,
MatchType : ruletypes . MatchType ( c . matchType ) ,
CompareOp : ruletypes . CompareOp ( c . compareOp ) ,
} ,
} ,
}
2024-02-11 22:31:46 +05:30
2025-07-30 19:25:27 +05:30
rule , err := NewPromRule ( "69" , valuer . GenerateUUID ( ) , & postableRule , logger , nil , nil )
2024-02-11 22:31:46 +05:30
if err != nil {
assert . NoError ( t , err )
}
2025-09-12 13:11:54 +05:30
resultVectors , err := rule . Threshold . ShouldAlert ( toCommonSeries ( c . values ) )
assert . NoError ( t , err )
// Compare full result vector with expected vector
actualValues := getVectorValues ( resultVectors )
if c . expectedVectorValues != nil {
// If expected vector values are specified, compare them exactly
assert . Equal ( t , c . expectedVectorValues , actualValues , "Result vector values don't match expected for case %d" , idx )
} else {
// Fallback to the old logic for cases without expectedVectorValues
if c . expectAlert {
assert . NotEmpty ( t , resultVectors , "Expected alert but got no result vectors for case %d" , idx )
// Verify at least one of the result vectors matches the expected alert sample
if len ( resultVectors ) > 0 {
found := false
for _ , sample := range resultVectors {
if sample . V == c . expectedAlertSample . Value {
found = true
break
}
}
assert . True ( t , found , "Expected alert sample value %.2f not found in result vectors for case %d. Got values: %v" , c . expectedAlertSample . Value , idx , actualValues )
}
} else {
assert . Empty ( t , resultVectors , "Expected no alert but got result vectors for case %d" , idx )
}
}
2024-02-11 22:31:46 +05:30
}
}