885 lines
18 KiB
Go
885 lines
18 KiB
Go
|
package rules
|
||
|
|
||
|
import (
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/stretchr/testify/assert"
|
||
|
"go.signoz.io/signoz/pkg/query-service/featureManager"
|
||
|
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
|
||
|
"go.signoz.io/signoz/pkg/query-service/utils/labels"
|
||
|
)
|
||
|
|
||
|
func TestThresholdRuleShouldAlert(t *testing.T) {
|
||
|
postableRule := PostableRule{
|
||
|
AlertName: "Tricky Condition Tests",
|
||
|
AlertType: "METRIC_BASED_ALERT",
|
||
|
RuleType: RuleTypeThreshold,
|
||
|
EvalWindow: Duration(5 * time.Minute),
|
||
|
Frequency: Duration(1 * time.Minute),
|
||
|
RuleCondition: &RuleCondition{
|
||
|
CompositeQuery: &v3.CompositeQuery{
|
||
|
QueryType: v3.QueryTypeBuilder,
|
||
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
||
|
"A": {
|
||
|
QueryName: "A",
|
||
|
StepInterval: 60,
|
||
|
AggregateAttribute: v3.AttributeKey{
|
||
|
Key: "probe_success",
|
||
|
},
|
||
|
AggregateOperator: v3.AggregateOperatorNoOp,
|
||
|
DataSource: v3.DataSourceMetrics,
|
||
|
Expression: "A",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
cases := []struct {
|
||
|
values v3.Series
|
||
|
expectAlert bool
|
||
|
compareOp string
|
||
|
matchType string
|
||
|
target float64
|
||
|
}{
|
||
|
// Test cases for Equals Always
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 0.0},
|
||
|
{Value: 0.0},
|
||
|
{Value: 0.0},
|
||
|
{Value: 0.0},
|
||
|
{Value: 0.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: true,
|
||
|
compareOp: "3", // Equals
|
||
|
matchType: "2", // Always
|
||
|
target: 0.0,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 0.0},
|
||
|
{Value: 0.0},
|
||
|
{Value: 0.0},
|
||
|
{Value: 0.0},
|
||
|
{Value: 1.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: false,
|
||
|
compareOp: "3", // Equals
|
||
|
matchType: "2", // Always
|
||
|
target: 0.0,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 0.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 0.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 1.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: false,
|
||
|
compareOp: "3", // Equals
|
||
|
matchType: "2", // Always
|
||
|
target: 0.0,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 1.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 1.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: false,
|
||
|
compareOp: "3", // Equals
|
||
|
matchType: "2", // Always
|
||
|
target: 0.0,
|
||
|
},
|
||
|
// Test cases for Equals Once
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 0.0},
|
||
|
{Value: 0.0},
|
||
|
{Value: 0.0},
|
||
|
{Value: 0.0},
|
||
|
{Value: 0.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: true,
|
||
|
compareOp: "3", // Equals
|
||
|
matchType: "1", // Once
|
||
|
target: 0.0,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 0.0},
|
||
|
{Value: 0.0},
|
||
|
{Value: 0.0},
|
||
|
{Value: 0.0},
|
||
|
{Value: 1.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: true,
|
||
|
compareOp: "3", // Equals
|
||
|
matchType: "1", // Once
|
||
|
target: 0.0,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 0.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 0.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 1.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: true,
|
||
|
compareOp: "3", // Equals
|
||
|
matchType: "1", // Once
|
||
|
target: 0.0,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 1.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 1.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: false,
|
||
|
compareOp: "3", // Equals
|
||
|
matchType: "1", // Once
|
||
|
target: 0.0,
|
||
|
},
|
||
|
// Test cases for Greater Than Always
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 10.0},
|
||
|
{Value: 4.0},
|
||
|
{Value: 6.0},
|
||
|
{Value: 8.0},
|
||
|
{Value: 2.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: true,
|
||
|
compareOp: "1", // Greater Than
|
||
|
matchType: "2", // Always
|
||
|
target: 1.5,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 10.0},
|
||
|
{Value: 4.0},
|
||
|
{Value: 6.0},
|
||
|
{Value: 8.0},
|
||
|
{Value: 2.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: false,
|
||
|
compareOp: "1", // Greater Than
|
||
|
matchType: "2", // Always
|
||
|
target: 4.5,
|
||
|
},
|
||
|
// Test cases for Greater Than Once
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 10.0},
|
||
|
{Value: 4.0},
|
||
|
{Value: 6.0},
|
||
|
{Value: 8.0},
|
||
|
{Value: 2.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: true,
|
||
|
compareOp: "1", // Greater Than
|
||
|
matchType: "1", // Once
|
||
|
target: 4.5,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 4.0},
|
||
|
{Value: 4.0},
|
||
|
{Value: 4.0},
|
||
|
{Value: 4.0},
|
||
|
{Value: 4.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: false,
|
||
|
compareOp: "1", // Greater Than
|
||
|
matchType: "1", // Once
|
||
|
target: 4.5,
|
||
|
},
|
||
|
// Test cases for Not Equals Always
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 0.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 0.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 0.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: false,
|
||
|
compareOp: "4", // Not Equals
|
||
|
matchType: "2", // Always
|
||
|
target: 0.0,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 1.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 0.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: false,
|
||
|
compareOp: "4", // Not Equals
|
||
|
matchType: "2", // Always
|
||
|
target: 0.0,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 1.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 1.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: true,
|
||
|
compareOp: "4", // Not Equals
|
||
|
matchType: "2", // Always
|
||
|
target: 0.0,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 1.0},
|
||
|
{Value: 0.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 1.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: false,
|
||
|
compareOp: "4", // Not Equals
|
||
|
matchType: "2", // Always
|
||
|
target: 0.0,
|
||
|
},
|
||
|
// Test cases for Not Equals Once
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 0.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 0.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 0.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: true,
|
||
|
compareOp: "4", // Not Equals
|
||
|
matchType: "1", // Once
|
||
|
target: 0.0,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 0.0},
|
||
|
{Value: 0.0},
|
||
|
{Value: 0.0},
|
||
|
{Value: 0.0},
|
||
|
{Value: 0.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: false,
|
||
|
compareOp: "4", // Not Equals
|
||
|
matchType: "1", // Once
|
||
|
target: 0.0,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 0.0},
|
||
|
{Value: 0.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 0.0},
|
||
|
{Value: 1.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: true,
|
||
|
compareOp: "4", // Not Equals
|
||
|
matchType: "1", // Once
|
||
|
target: 0.0,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 1.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 1.0},
|
||
|
{Value: 1.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: true,
|
||
|
compareOp: "4", // Not Equals
|
||
|
matchType: "1", // Once
|
||
|
target: 0.0,
|
||
|
},
|
||
|
// Test cases for Less Than Always
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 1.5},
|
||
|
{Value: 1.5},
|
||
|
{Value: 1.5},
|
||
|
{Value: 1.5},
|
||
|
{Value: 1.5},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: true,
|
||
|
compareOp: "2", // Less Than
|
||
|
matchType: "2", // Always
|
||
|
target: 4,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 4.5},
|
||
|
{Value: 4.5},
|
||
|
{Value: 4.5},
|
||
|
{Value: 4.5},
|
||
|
{Value: 4.5},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: false,
|
||
|
compareOp: "2", // Less Than
|
||
|
matchType: "2", // Always
|
||
|
target: 4,
|
||
|
},
|
||
|
// Test cases for Less Than Once
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 4.5},
|
||
|
{Value: 4.5},
|
||
|
{Value: 4.5},
|
||
|
{Value: 4.5},
|
||
|
{Value: 2.5},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: true,
|
||
|
compareOp: "2", // Less Than
|
||
|
matchType: "1", // Once
|
||
|
target: 4,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 4.5},
|
||
|
{Value: 4.5},
|
||
|
{Value: 4.5},
|
||
|
{Value: 4.5},
|
||
|
{Value: 4.5},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: false,
|
||
|
compareOp: "2", // Less Than
|
||
|
matchType: "1", // Once
|
||
|
target: 4,
|
||
|
},
|
||
|
// Test cases for OnAverage
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 10.0},
|
||
|
{Value: 4.0},
|
||
|
{Value: 6.0},
|
||
|
{Value: 8.0},
|
||
|
{Value: 2.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: true,
|
||
|
compareOp: "3", // Equals
|
||
|
matchType: "3", // OnAverage
|
||
|
target: 6.0,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 10.0},
|
||
|
{Value: 4.0},
|
||
|
{Value: 6.0},
|
||
|
{Value: 8.0},
|
||
|
{Value: 2.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: false,
|
||
|
compareOp: "3", // Equals
|
||
|
matchType: "3", // OnAverage
|
||
|
target: 4.5,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 10.0},
|
||
|
{Value: 4.0},
|
||
|
{Value: 6.0},
|
||
|
{Value: 8.0},
|
||
|
{Value: 2.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: true,
|
||
|
compareOp: "4", // Not Equals
|
||
|
matchType: "3", // OnAverage
|
||
|
target: 4.5,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 10.0},
|
||
|
{Value: 4.0},
|
||
|
{Value: 6.0},
|
||
|
{Value: 8.0},
|
||
|
{Value: 2.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: false,
|
||
|
compareOp: "4", // Not Equals
|
||
|
matchType: "3", // OnAverage
|
||
|
target: 6.0,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 10.0},
|
||
|
{Value: 4.0},
|
||
|
{Value: 6.0},
|
||
|
{Value: 8.0},
|
||
|
{Value: 2.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: true,
|
||
|
compareOp: "1", // Greater Than
|
||
|
matchType: "3", // OnAverage
|
||
|
target: 4.5,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 10.0},
|
||
|
{Value: 4.0},
|
||
|
{Value: 6.0},
|
||
|
{Value: 8.0},
|
||
|
{Value: 2.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: true,
|
||
|
compareOp: "2", // Less Than
|
||
|
matchType: "3", // OnAverage
|
||
|
target: 12.0,
|
||
|
},
|
||
|
// Test cases for InTotal
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 10.0},
|
||
|
{Value: 4.0},
|
||
|
{Value: 6.0},
|
||
|
{Value: 8.0},
|
||
|
{Value: 2.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: true,
|
||
|
compareOp: "3", // Equals
|
||
|
matchType: "4", // InTotal
|
||
|
target: 30.0,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 10.0},
|
||
|
{Value: 4.0},
|
||
|
{Value: 6.0},
|
||
|
{Value: 8.0},
|
||
|
{Value: 2.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: false,
|
||
|
compareOp: "3", // Equals
|
||
|
matchType: "4", // InTotal
|
||
|
target: 20.0,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 10.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: true,
|
||
|
compareOp: "4", // Not Equals
|
||
|
matchType: "4", // InTotal
|
||
|
target: 9.0,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 10.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: false,
|
||
|
compareOp: "4", // Not Equals
|
||
|
matchType: "4", // InTotal
|
||
|
target: 10.0,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 10.0},
|
||
|
{Value: 10.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: true,
|
||
|
compareOp: "1", // Greater Than
|
||
|
matchType: "4", // InTotal
|
||
|
target: 10.0,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 10.0},
|
||
|
{Value: 10.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: false,
|
||
|
compareOp: "1", // Greater Than
|
||
|
matchType: "4", // InTotal
|
||
|
target: 20.0,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 10.0},
|
||
|
{Value: 10.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: true,
|
||
|
compareOp: "2", // Less Than
|
||
|
matchType: "4", // InTotal
|
||
|
target: 30.0,
|
||
|
},
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 10.0},
|
||
|
{Value: 10.0},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: false,
|
||
|
compareOp: "2", // Less Than
|
||
|
matchType: "4", // InTotal
|
||
|
target: 20.0,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
fm := featureManager.StartManager()
|
||
|
for idx, c := range cases {
|
||
|
postableRule.RuleCondition.CompareOp = CompareOp(c.compareOp)
|
||
|
postableRule.RuleCondition.MatchType = MatchType(c.matchType)
|
||
|
postableRule.RuleCondition.Target = &c.target
|
||
|
|
||
|
rule, err := NewThresholdRule("69", &postableRule, ThresholdRuleOpts{}, fm, nil)
|
||
|
if err != nil {
|
||
|
assert.NoError(t, err)
|
||
|
}
|
||
|
|
||
|
values := c.values
|
||
|
for i := range values.Points {
|
||
|
values.Points[i].Timestamp = time.Now().UnixMilli()
|
||
|
}
|
||
|
|
||
|
_, shoulAlert := rule.shouldAlert(c.values)
|
||
|
assert.Equal(t, c.expectAlert, shoulAlert, "Test case %d", idx)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestNormalizeLabelName(t *testing.T) {
|
||
|
cases := []struct {
|
||
|
labelName string
|
||
|
expected string
|
||
|
}{
|
||
|
{
|
||
|
labelName: "label",
|
||
|
expected: "label",
|
||
|
},
|
||
|
{
|
||
|
labelName: "label.with.dots",
|
||
|
expected: "label_with_dots",
|
||
|
},
|
||
|
{
|
||
|
labelName: "label-with-dashes",
|
||
|
expected: "label_with_dashes",
|
||
|
},
|
||
|
{
|
||
|
labelName: "labelwithnospaces",
|
||
|
expected: "labelwithnospaces",
|
||
|
},
|
||
|
{
|
||
|
labelName: "label with spaces",
|
||
|
expected: "label_with_spaces",
|
||
|
},
|
||
|
{
|
||
|
labelName: "label with spaces and .dots",
|
||
|
expected: "label_with_spaces_and__dots",
|
||
|
},
|
||
|
{
|
||
|
labelName: "label with spaces and -dashes",
|
||
|
expected: "label_with_spaces_and__dashes",
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, c := range cases {
|
||
|
assert.Equal(t, c.expected, normalizeLabelName(c.labelName))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestPrepareLinksToLogs(t *testing.T) {
|
||
|
postableRule := PostableRule{
|
||
|
AlertName: "Tricky Condition Tests",
|
||
|
AlertType: "LOGS_BASED_ALERT",
|
||
|
RuleType: RuleTypeThreshold,
|
||
|
EvalWindow: Duration(5 * time.Minute),
|
||
|
Frequency: Duration(1 * time.Minute),
|
||
|
RuleCondition: &RuleCondition{
|
||
|
CompositeQuery: &v3.CompositeQuery{
|
||
|
QueryType: v3.QueryTypeBuilder,
|
||
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
||
|
"A": {
|
||
|
QueryName: "A",
|
||
|
StepInterval: 60,
|
||
|
AggregateAttribute: v3.AttributeKey{
|
||
|
Key: "",
|
||
|
},
|
||
|
AggregateOperator: v3.AggregateOperatorNoOp,
|
||
|
DataSource: v3.DataSourceLogs,
|
||
|
Expression: "A",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
CompareOp: "4", // Not Equals
|
||
|
MatchType: "1", // Once
|
||
|
Target: &[]float64{0.0}[0],
|
||
|
SelectedQuery: "A",
|
||
|
},
|
||
|
}
|
||
|
fm := featureManager.StartManager()
|
||
|
|
||
|
rule, err := NewThresholdRule("69", &postableRule, ThresholdRuleOpts{}, fm, nil)
|
||
|
if err != nil {
|
||
|
assert.NoError(t, err)
|
||
|
}
|
||
|
|
||
|
ts := time.UnixMilli(1705469040000)
|
||
|
|
||
|
link := rule.prepareLinksToLogs(ts, labels.Labels{})
|
||
|
assert.Contains(t, link, "&timeRange=%7B%22start%22%3A1705468620000%2C%22end%22%3A1705468920000%2C%22pageSize%22%3A100%7D&startTime=1705468620000&endTime=1705468920000")
|
||
|
}
|
||
|
|
||
|
func TestPrepareLinksToTraces(t *testing.T) {
|
||
|
postableRule := PostableRule{
|
||
|
AlertName: "Links to traces test",
|
||
|
AlertType: "TRACES_BASED_ALERT",
|
||
|
RuleType: RuleTypeThreshold,
|
||
|
EvalWindow: Duration(5 * time.Minute),
|
||
|
Frequency: Duration(1 * time.Minute),
|
||
|
RuleCondition: &RuleCondition{
|
||
|
CompositeQuery: &v3.CompositeQuery{
|
||
|
QueryType: v3.QueryTypeBuilder,
|
||
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
||
|
"A": {
|
||
|
QueryName: "A",
|
||
|
StepInterval: 60,
|
||
|
AggregateAttribute: v3.AttributeKey{
|
||
|
Key: "durationNano",
|
||
|
},
|
||
|
AggregateOperator: v3.AggregateOperatorAvg,
|
||
|
DataSource: v3.DataSourceTraces,
|
||
|
Expression: "A",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
CompareOp: "4", // Not Equals
|
||
|
MatchType: "1", // Once
|
||
|
Target: &[]float64{0.0}[0],
|
||
|
SelectedQuery: "A",
|
||
|
},
|
||
|
}
|
||
|
fm := featureManager.StartManager()
|
||
|
|
||
|
rule, err := NewThresholdRule("69", &postableRule, ThresholdRuleOpts{}, fm, nil)
|
||
|
if err != nil {
|
||
|
assert.NoError(t, err)
|
||
|
}
|
||
|
|
||
|
ts := time.UnixMilli(1705469040000)
|
||
|
|
||
|
link := rule.prepareLinksToTraces(ts, labels.Labels{})
|
||
|
assert.Contains(t, link, "&timeRange=%7B%22start%22%3A1705468620000000000%2C%22end%22%3A1705468920000000000%2C%22pageSize%22%3A100%7D&startTime=1705468620000000000&endTime=1705468920000000000")
|
||
|
}
|
||
|
|
||
|
func TestThresholdRuleLabelNormalization(t *testing.T) {
|
||
|
postableRule := PostableRule{
|
||
|
AlertName: "Tricky Condition Tests",
|
||
|
AlertType: "METRIC_BASED_ALERT",
|
||
|
RuleType: RuleTypeThreshold,
|
||
|
EvalWindow: Duration(5 * time.Minute),
|
||
|
Frequency: Duration(1 * time.Minute),
|
||
|
RuleCondition: &RuleCondition{
|
||
|
CompositeQuery: &v3.CompositeQuery{
|
||
|
QueryType: v3.QueryTypeBuilder,
|
||
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
||
|
"A": {
|
||
|
QueryName: "A",
|
||
|
StepInterval: 60,
|
||
|
AggregateAttribute: v3.AttributeKey{
|
||
|
Key: "probe_success",
|
||
|
},
|
||
|
AggregateOperator: v3.AggregateOperatorNoOp,
|
||
|
DataSource: v3.DataSourceMetrics,
|
||
|
Expression: "A",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
cases := []struct {
|
||
|
values v3.Series
|
||
|
expectAlert bool
|
||
|
compareOp string
|
||
|
matchType string
|
||
|
target float64
|
||
|
}{
|
||
|
// Test cases for Equals Always
|
||
|
{
|
||
|
values: v3.Series{
|
||
|
Points: []v3.Point{
|
||
|
{Value: 0.0},
|
||
|
{Value: 0.0},
|
||
|
{Value: 0.0},
|
||
|
{Value: 0.0},
|
||
|
{Value: 0.0},
|
||
|
},
|
||
|
Labels: map[string]string{
|
||
|
"service.name": "frontend",
|
||
|
},
|
||
|
LabelsArray: []map[string]string{
|
||
|
{
|
||
|
"service.name": "frontend",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
expectAlert: true,
|
||
|
compareOp: "3", // Equals
|
||
|
matchType: "2", // Always
|
||
|
target: 0.0,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
fm := featureManager.StartManager()
|
||
|
for idx, c := range cases {
|
||
|
postableRule.RuleCondition.CompareOp = CompareOp(c.compareOp)
|
||
|
postableRule.RuleCondition.MatchType = MatchType(c.matchType)
|
||
|
postableRule.RuleCondition.Target = &c.target
|
||
|
|
||
|
rule, err := NewThresholdRule("69", &postableRule, ThresholdRuleOpts{}, fm, nil)
|
||
|
if err != nil {
|
||
|
assert.NoError(t, err)
|
||
|
}
|
||
|
|
||
|
values := c.values
|
||
|
for i := range values.Points {
|
||
|
values.Points[i].Timestamp = time.Now().UnixMilli()
|
||
|
}
|
||
|
|
||
|
sample, shoulAlert := rule.shouldAlert(c.values)
|
||
|
for name, value := range c.values.Labels {
|
||
|
assert.Equal(t, value, sample.Metric.Get(normalizeLabelName(name)))
|
||
|
}
|
||
|
|
||
|
assert.Equal(t, c.expectAlert, shoulAlert, "Test case %d", idx)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestThresholdRuleClickHouseTmpl(t *testing.T) {
|
||
|
postableRule := PostableRule{
|
||
|
AlertName: "Tricky Condition Tests",
|
||
|
AlertType: "METRIC_BASED_ALERT",
|
||
|
RuleType: RuleTypeThreshold,
|
||
|
EvalWindow: Duration(5 * time.Minute),
|
||
|
Frequency: Duration(1 * time.Minute),
|
||
|
RuleCondition: &RuleCondition{
|
||
|
CompositeQuery: &v3.CompositeQuery{
|
||
|
QueryType: v3.QueryTypeClickHouseSQL,
|
||
|
ClickHouseQueries: map[string]*v3.ClickHouseQuery{
|
||
|
"A": {
|
||
|
Query: "SELECT 1 >= {{.start_timestamp_ms}} AND 1 <= {{.end_timestamp_ms}}",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
// 01:39:47
|
||
|
ts := time.Unix(1717205987, 0)
|
||
|
|
||
|
cases := []struct {
|
||
|
expectedQuery string
|
||
|
}{
|
||
|
// Test cases for Equals Always
|
||
|
{
|
||
|
// 01:32:00 - 01:37:00
|
||
|
expectedQuery: "SELECT 1 >= 1717205520000 AND 1 <= 1717205820000",
|
||
|
},
|
||
|
}
|
||
|
|
||
|
fm := featureManager.StartManager()
|
||
|
for idx, c := range cases {
|
||
|
rule, err := NewThresholdRule("69", &postableRule, ThresholdRuleOpts{}, fm, nil)
|
||
|
if err != nil {
|
||
|
assert.NoError(t, err)
|
||
|
}
|
||
|
|
||
|
params := rule.prepareQueryRange(ts)
|
||
|
|
||
|
assert.Equal(t, c.expectedQuery, params.CompositeQuery.ClickHouseQueries["A"].Query, "Test case %d", idx)
|
||
|
|
||
|
secondTimeParams := rule.prepareQueryRange(ts)
|
||
|
|
||
|
assert.Equal(t, c.expectedQuery, secondTimeParams.CompositeQuery.ClickHouseQueries["A"].Query, "Test case %d", idx)
|
||
|
}
|
||
|
}
|