2023-09-27 22:34:49 +05:30
package rules
import (
2024-08-09 12:34:40 +05:30
"context"
"strings"
2023-09-27 22:34:49 +05:30
"testing"
"time"
2025-03-31 19:41:11 +05:30
"github.com/SigNoz/signoz/pkg/cache"
2025-05-03 18:30:07 +05:30
"github.com/SigNoz/signoz/pkg/cache/cachetest"
2025-03-31 19:41:11 +05:30
"github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest"
"github.com/SigNoz/signoz/pkg/prometheus"
"github.com/SigNoz/signoz/pkg/prometheus/prometheustest"
"github.com/SigNoz/signoz/pkg/telemetrystore"
"github.com/SigNoz/signoz/pkg/telemetrystore/telemetrystoretest"
2025-04-18 00:04:25 +05:30
ruletypes "github.com/SigNoz/signoz/pkg/types/ruletypes"
2025-07-30 19:25:27 +05:30
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
2025-05-03 18:30:07 +05:30
"github.com/SigNoz/signoz/pkg/valuer"
2025-03-31 19:41:11 +05:30
2025-03-20 21:01:41 +05:30
"github.com/SigNoz/signoz/pkg/query-service/app/clickhouseReader"
"github.com/SigNoz/signoz/pkg/query-service/common"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
"github.com/SigNoz/signoz/pkg/query-service/utils/labels"
2023-09-27 22:34:49 +05:30
"github.com/stretchr/testify/assert"
2025-03-31 19:41:11 +05:30
"github.com/stretchr/testify/require"
2024-08-09 12:34:40 +05:30
cmock "github.com/srikanthccv/ClickHouse-go-mock"
2025-07-30 19:25:27 +05:30
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
2023-09-27 22:34:49 +05:30
)
2024-05-24 12:11:34 +05:30
func TestThresholdRuleShouldAlert ( t * testing . T ) {
2025-04-18 00:04:25 +05:30
postableRule := ruletypes . PostableRule {
2024-03-27 20:25:18 +05:30
AlertName : "Tricky Condition Tests" ,
2025-04-18 00:04:25 +05:30
AlertType : ruletypes . AlertTypeMetric ,
RuleType : ruletypes . RuleTypeThreshold ,
EvalWindow : ruletypes . Duration ( 5 * time . Minute ) ,
Frequency : ruletypes . Duration ( 1 * time . Minute ) ,
RuleCondition : & ruletypes . RuleCondition {
2023-09-27 22:34:49 +05:30
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" ,
} ,
} ,
} ,
} ,
}
2025-07-30 19:25:27 +05:30
logger := instrumentationtest . New ( ) . Logger ( )
2023-09-27 22:34:49 +05:30
cases := [ ] struct {
2024-09-04 18:30:04 +05:30
values v3 . Series
expectAlert bool
compareOp string
matchType string
target float64
expectedAlertSample v3 . Point
2023-09-27 22:34:49 +05:30
} {
// Test cases for Equals Always
{
2024-05-24 12:11:34 +05:30
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 0.0 } ,
{ Value : 0.0 } ,
{ Value : 0.0 } ,
{ Value : 0.0 } ,
{ Value : 0.0 } ,
} ,
2023-09-27 22:34:49 +05:30
} ,
2024-09-04 18:30:04 +05:30
expectAlert : true ,
compareOp : "3" , // Equals
matchType : "2" , // Always
target : 0.0 ,
expectedAlertSample : v3 . Point { Value : 0.0 } ,
2023-09-27 22:34:49 +05:30
} ,
{
2024-05-24 12:11:34 +05:30
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 0.0 } ,
{ Value : 0.0 } ,
{ Value : 0.0 } ,
{ Value : 0.0 } ,
{ Value : 1.0 } ,
} ,
2023-09-27 22:34:49 +05:30
} ,
expectAlert : false ,
compareOp : "3" , // Equals
matchType : "2" , // Always
target : 0.0 ,
} ,
{
2024-05-24 12:11:34 +05:30
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 0.0 } ,
{ Value : 1.0 } ,
{ Value : 0.0 } ,
{ Value : 1.0 } ,
{ Value : 1.0 } ,
} ,
2023-09-27 22:34:49 +05:30
} ,
expectAlert : false ,
compareOp : "3" , // Equals
matchType : "2" , // Always
target : 0.0 ,
} ,
{
2024-05-24 12:11:34 +05:30
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 1.0 } ,
{ Value : 1.0 } ,
{ Value : 1.0 } ,
{ Value : 1.0 } ,
{ Value : 1.0 } ,
} ,
2023-09-27 22:34:49 +05:30
} ,
expectAlert : false ,
compareOp : "3" , // Equals
matchType : "2" , // Always
target : 0.0 ,
} ,
// Test cases for Equals Once
{
2024-05-24 12:11:34 +05:30
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 0.0 } ,
{ Value : 0.0 } ,
{ Value : 0.0 } ,
{ Value : 0.0 } ,
{ Value : 0.0 } ,
} ,
2023-09-27 22:34:49 +05:30
} ,
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 } ,
2023-09-27 22:34:49 +05:30
} ,
{
2024-05-24 12:11:34 +05:30
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 0.0 } ,
{ Value : 0.0 } ,
{ Value : 0.0 } ,
{ Value : 0.0 } ,
{ Value : 1.0 } ,
} ,
2023-09-27 22:34:49 +05:30
} ,
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 } ,
2023-09-27 22:34:49 +05:30
} ,
{
2024-05-24 12:11:34 +05:30
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 0.0 } ,
{ Value : 1.0 } ,
{ Value : 0.0 } ,
{ Value : 1.0 } ,
{ Value : 1.0 } ,
} ,
2023-09-27 22:34:49 +05:30
} ,
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 } ,
2023-09-27 22:34:49 +05:30
} ,
{
2024-05-24 12:11:34 +05:30
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 1.0 } ,
{ Value : 1.0 } ,
{ Value : 1.0 } ,
{ Value : 1.0 } ,
{ Value : 1.0 } ,
} ,
2023-09-27 22:34:49 +05:30
} ,
expectAlert : false ,
compareOp : "3" , // Equals
matchType : "1" , // Once
target : 0.0 ,
} ,
2024-05-24 12:11:34 +05:30
// 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 } ,
} ,
} ,
2024-09-04 18:30:04 +05:30
expectAlert : true ,
compareOp : "1" , // Greater Than
matchType : "2" , // Always
target : 1.5 ,
expectedAlertSample : v3 . Point { Value : 2.0 } ,
2024-05-24 12:11:34 +05:30
} ,
{
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 } ,
} ,
} ,
2024-09-04 18:30:04 +05:30
expectAlert : true ,
compareOp : "1" , // Greater Than
matchType : "1" , // Once
target : 4.5 ,
expectedAlertSample : v3 . Point { Value : 10.0 } ,
2024-05-24 12:11:34 +05:30
} ,
{
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 ,
} ,
2023-09-27 22:34:49 +05:30
// Test cases for Not Equals Always
{
2024-05-24 12:11:34 +05:30
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 0.0 } ,
{ Value : 1.0 } ,
{ Value : 0.0 } ,
{ Value : 1.0 } ,
{ Value : 0.0 } ,
} ,
2023-09-27 22:34:49 +05:30
} ,
expectAlert : false ,
compareOp : "4" , // Not Equals
matchType : "2" , // Always
target : 0.0 ,
} ,
{
2024-05-24 12:11:34 +05:30
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 1.0 } ,
{ Value : 1.0 } ,
{ Value : 1.0 } ,
{ Value : 1.0 } ,
{ Value : 0.0 } ,
} ,
2023-09-27 22:34:49 +05:30
} ,
expectAlert : false ,
compareOp : "4" , // Not Equals
matchType : "2" , // Always
target : 0.0 ,
} ,
{
2024-05-24 12:11:34 +05:30
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 1.0 } ,
{ Value : 1.0 } ,
{ Value : 1.0 } ,
{ Value : 1.0 } ,
{ Value : 1.0 } ,
} ,
2023-09-27 22:34:49 +05:30
} ,
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 } ,
2023-09-27 22:34:49 +05:30
} ,
{
2024-05-24 12:11:34 +05:30
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 1.0 } ,
{ Value : 0.0 } ,
{ Value : 1.0 } ,
{ Value : 1.0 } ,
{ Value : 1.0 } ,
} ,
2023-09-27 22:34:49 +05:30
} ,
expectAlert : false ,
compareOp : "4" , // Not Equals
matchType : "2" , // Always
target : 0.0 ,
} ,
// Test cases for Not Equals Once
{
2024-05-24 12:11:34 +05:30
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 0.0 } ,
{ Value : 1.0 } ,
{ Value : 0.0 } ,
{ Value : 1.0 } ,
{ Value : 0.0 } ,
} ,
2023-09-27 22:34:49 +05:30
} ,
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 } ,
2023-09-27 22:34:49 +05:30
} ,
{
2024-05-24 12:11:34 +05:30
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 0.0 } ,
{ Value : 0.0 } ,
{ Value : 0.0 } ,
{ Value : 0.0 } ,
{ Value : 0.0 } ,
} ,
2023-09-27 22:34:49 +05:30
} ,
expectAlert : false ,
compareOp : "4" , // Not Equals
matchType : "1" , // Once
target : 0.0 ,
} ,
{
2024-05-24 12:11:34 +05:30
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 0.0 } ,
{ Value : 0.0 } ,
{ Value : 1.0 } ,
{ Value : 0.0 } ,
{ Value : 1.0 } ,
} ,
2023-09-27 22:34:49 +05:30
} ,
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 } ,
2023-09-27 22:34:49 +05:30
} ,
{
2024-05-24 12:11:34 +05:30
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 1.0 } ,
{ Value : 1.0 } ,
{ Value : 1.0 } ,
{ Value : 1.0 } ,
{ Value : 1.0 } ,
} ,
2023-09-27 22:34:49 +05:30
} ,
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 } ,
2023-09-27 22:34:49 +05:30
} ,
2024-05-24 12:11:34 +05:30
// Test cases for Less Than Always
2024-05-01 17:03:46 +05:30
{
2024-05-24 12:11:34 +05:30
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 1.5 } ,
{ Value : 1.5 } ,
{ Value : 1.5 } ,
{ Value : 1.5 } ,
{ Value : 1.5 } ,
} ,
2024-05-01 17:03:46 +05:30
} ,
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 } ,
} ,
{
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 1.5 } ,
{ Value : 2.5 } ,
{ Value : 1.5 } ,
{ Value : 3.5 } ,
{ Value : 1.5 } ,
} ,
} ,
expectAlert : true ,
compareOp : "2" , // Less Than
matchType : "2" , // Always
target : 4 ,
expectedAlertSample : v3 . Point { Value : 3.5 } ,
2024-05-24 12:11:34 +05:30
} ,
{
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 } ,
} ,
} ,
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-05-24 12:11:34 +05:30
} ,
{
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 } ,
} ,
} ,
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-05-01 17:03:46 +05:30
} ,
{
2024-05-24 12:11:34 +05:30
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 10.0 } ,
{ Value : 4.0 } ,
{ Value : 6.0 } ,
{ Value : 8.0 } ,
{ Value : 2.0 } ,
} ,
2024-05-01 17:03:46 +05:30
} ,
expectAlert : false ,
2024-05-24 12:11:34 +05:30
compareOp : "3" , // Equals
matchType : "3" , // OnAverage
target : 4.5 ,
2024-05-01 17:03:46 +05:30
} ,
{
2024-05-24 12:11:34 +05:30
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 10.0 } ,
{ Value : 4.0 } ,
{ Value : 6.0 } ,
{ Value : 8.0 } ,
{ Value : 2.0 } ,
} ,
2024-05-01 17:03:46 +05:30
} ,
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-05-24 12:11:34 +05:30
} ,
{
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
2024-05-01 17:03:46 +05:30
target : 6.0 ,
} ,
2024-05-24 12:11:34 +05:30
{
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 10.0 } ,
{ Value : 4.0 } ,
{ Value : 6.0 } ,
{ Value : 8.0 } ,
{ Value : 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 } ,
} ,
{
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 11.0 } ,
{ Value : 4.0 } ,
{ Value : 3.0 } ,
{ Value : 7.0 } ,
{ Value : 12.0 } ,
} ,
} ,
expectAlert : true ,
compareOp : "1" , // Above
matchType : "2" , // Always
target : 2.0 ,
expectedAlertSample : v3 . Point { Value : 3.0 } ,
} ,
{
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 11.0 } ,
{ Value : 4.0 } ,
{ Value : 3.0 } ,
{ Value : 7.0 } ,
{ Value : 12.0 } ,
} ,
} ,
expectAlert : true ,
compareOp : "2" , // Below
matchType : "2" , // Always
target : 13.0 ,
expectedAlertSample : v3 . Point { Value : 12.0 } ,
2024-05-24 12:11:34 +05:30
} ,
{
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 10.0 } ,
{ Value : 4.0 } ,
{ Value : 6.0 } ,
{ Value : 8.0 } ,
{ Value : 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-05-24 12:11:34 +05:30
} ,
// 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 } ,
} ,
} ,
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-05-24 12:11:34 +05:30
} ,
{
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 } ,
} ,
} ,
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-05-24 12:11:34 +05:30
} ,
{
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 } ,
} ,
} ,
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-05-24 12:11:34 +05:30
} ,
{
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 } ,
} ,
} ,
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-05-24 12:11:34 +05:30
} ,
{
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 10.0 } ,
{ Value : 10.0 } ,
} ,
} ,
expectAlert : false ,
compareOp : "2" , // Less Than
matchType : "4" , // InTotal
target : 20.0 ,
} ,
2024-09-19 23:21:31 +05:30
// Test cases for Last
// greater than last
{
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 10.0 } ,
{ Value : 10.0 } ,
} ,
} ,
expectAlert : true ,
compareOp : "1" , // Greater Than
matchType : "5" , // Last
target : 5.0 ,
expectedAlertSample : v3 . Point { Value : 10.0 } ,
} ,
{
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 10.0 } ,
{ Value : 10.0 } ,
} ,
} ,
expectAlert : false ,
compareOp : "1" , // Greater Than
matchType : "5" , // Last
target : 20.0 ,
} ,
// less than last
{
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 10.0 } ,
{ Value : 10.0 } ,
} ,
} ,
expectAlert : true ,
compareOp : "2" , // Less Than
matchType : "5" , // Last
target : 15.0 ,
expectedAlertSample : v3 . Point { Value : 10.0 } ,
} ,
{
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 10.0 } ,
{ Value : 10.0 } ,
} ,
} ,
expectAlert : false ,
compareOp : "2" , // Less Than
matchType : "5" , // Last
target : 5.0 ,
} ,
// equals last
{
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 10.0 } ,
{ Value : 10.0 } ,
} ,
} ,
expectAlert : true ,
compareOp : "3" , // Equals
matchType : "5" , // Last
target : 10.0 ,
expectedAlertSample : v3 . Point { Value : 10.0 } ,
} ,
{
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 10.0 } ,
{ Value : 10.0 } ,
} ,
} ,
expectAlert : false ,
compareOp : "3" , // Equals
matchType : "5" , // Last
target : 5.0 ,
} ,
// not equals last
{
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 10.0 } ,
{ Value : 10.0 } ,
} ,
} ,
expectAlert : true ,
compareOp : "4" , // Not Equals
matchType : "5" , // Last
target : 5.0 ,
expectedAlertSample : v3 . Point { Value : 10.0 } ,
} ,
{
values : v3 . Series {
Points : [ ] v3 . Point {
{ Value : 10.0 } ,
{ Value : 10.0 } ,
} ,
} ,
expectAlert : false ,
compareOp : "4" , // Not Equals
matchType : "5" , // Last
target : 10.0 ,
} ,
2023-09-27 22:34:49 +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 )
2023-09-27 22:34:49 +05:30
postableRule . RuleCondition . Target = & c . target
2025-07-30 19:25:27 +05:30
rule , err := NewThresholdRule ( "69" , valuer . GenerateUUID ( ) , & postableRule , nil , nil , logger , WithEvalDelay ( 2 * time . Minute ) )
2023-09-27 22:34:49 +05:30
if err != nil {
assert . NoError ( t , err )
}
2024-05-24 12:11:34 +05:30
values := c . values
for i := range values . Points {
values . Points [ i ] . Timestamp = time . Now ( ) . UnixMilli ( )
2023-09-27 22:34:49 +05:30
}
2024-05-24 12:11:34 +05:30
2024-09-24 10:22:52 +05:30
smpl , shoulAlert := rule . ShouldAlert ( c . values )
2024-05-24 12:11:34 +05:30
assert . Equal ( t , c . expectAlert , shoulAlert , "Test case %d" , idx )
2024-09-04 18:30:04 +05:30
if shoulAlert {
assert . Equal ( t , c . expectedAlertSample . Value , smpl . V , "Test case %d" , idx )
}
2023-09-27 22:34:49 +05:30
}
}
2023-12-28 20:22:42 +05:30
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 {
2024-09-24 10:22:52 +05:30
assert . Equal ( t , c . expected , common . NormalizeLabelName ( c . labelName ) )
2023-12-28 20:22:42 +05:30
}
}
2024-02-02 21:16:14 +05:30
func TestPrepareLinksToLogs ( t * testing . T ) {
2025-04-18 00:04:25 +05:30
postableRule := ruletypes . PostableRule {
2024-03-27 20:25:18 +05:30
AlertName : "Tricky Condition Tests" ,
2025-04-18 00:04:25 +05:30
AlertType : ruletypes . AlertTypeLogs ,
RuleType : ruletypes . RuleTypeThreshold ,
EvalWindow : ruletypes . Duration ( 5 * time . Minute ) ,
Frequency : ruletypes . Duration ( 1 * time . Minute ) ,
RuleCondition : & ruletypes . RuleCondition {
2024-02-02 21:16:14 +05:30
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" ,
} ,
}
2025-07-30 19:25:27 +05:30
logger := instrumentationtest . New ( ) . Logger ( )
rule , err := NewThresholdRule ( "69" , valuer . GenerateUUID ( ) , & postableRule , nil , nil , logger , WithEvalDelay ( 2 * time . Minute ) )
2024-02-02 21:16:14 +05:30
if err != nil {
assert . NoError ( t , err )
}
ts := time . UnixMilli ( 1705469040000 )
2025-07-30 19:25:27 +05:30
link := rule . prepareLinksToLogs ( context . Background ( ) , ts , labels . Labels { } )
2024-03-12 17:30:01 +05:30
assert . Contains ( t , link , "&timeRange=%7B%22start%22%3A1705468620000%2C%22end%22%3A1705468920000%2C%22pageSize%22%3A100%7D&startTime=1705468620000&endTime=1705468920000" )
2024-02-02 21:16:14 +05:30
}
2025-07-30 19:25:27 +05:30
func TestPrepareLinksToLogsV5 ( t * testing . T ) {
postableRule := ruletypes . PostableRule {
AlertName : "Tricky Condition Tests" ,
AlertType : ruletypes . AlertTypeLogs ,
RuleType : ruletypes . RuleTypeThreshold ,
EvalWindow : ruletypes . Duration ( 5 * time . Minute ) ,
Frequency : ruletypes . Duration ( 1 * time . Minute ) ,
RuleCondition : & ruletypes . RuleCondition {
CompositeQuery : & v3 . CompositeQuery {
QueryType : v3 . QueryTypeBuilder ,
Queries : [ ] qbtypes . QueryEnvelope {
{
Type : qbtypes . QueryTypeBuilder ,
Spec : qbtypes . QueryBuilderQuery [ qbtypes . LogAggregation ] {
Name : "A" ,
StepInterval : qbtypes . Step { Duration : 1 * time . Minute } ,
Aggregations : [ ] qbtypes . LogAggregation {
{
Expression : "count()" ,
} ,
} ,
Filter : & qbtypes . Filter {
Expression : "service.name EXISTS" ,
} ,
Signal : telemetrytypes . SignalLogs ,
} ,
} ,
} ,
} ,
CompareOp : "4" , // Not Equals
MatchType : "1" , // Once
Target : & [ ] float64 { 0.0 } [ 0 ] ,
SelectedQuery : "A" ,
} ,
Version : "v5" ,
}
logger := instrumentationtest . New ( ) . Logger ( )
rule , err := NewThresholdRule ( "69" , valuer . GenerateUUID ( ) , & postableRule , nil , nil , logger , WithEvalDelay ( 2 * time . Minute ) )
if err != nil {
assert . NoError ( t , err )
}
ts := time . UnixMilli ( 1753527163000 )
link := rule . prepareLinksToLogs ( context . Background ( ) , ts , labels . Labels { } )
assert . Contains ( t , link , "compositeQuery=%257B%2522queryType%2522%253A%2522builder%2522%252C%2522builder%2522%253A%257B%2522queryData%2522%253A%255B%257B%2522queryName%2522%253A%2522A%2522%252C%2522stepInterval%2522%253A60%252C%2522dataSource%2522%253A%2522logs%2522%252C%2522aggregateOperator%2522%253A%2522noop%2522%252C%2522aggregateAttribute%2522%253A%257B%2522key%2522%253A%2522%2522%252C%2522dataType%2522%253A%2522%2522%252C%2522type%2522%253A%2522%2522%252C%2522isColumn%2522%253Afalse%252C%2522isJSON%2522%253Afalse%257D%252C%2522expression%2522%253A%2522A%2522%252C%2522disabled%2522%253Afalse%252C%2522limit%2522%253A0%252C%2522offset%2522%253A0%252C%2522pageSize%2522%253A0%252C%2522ShiftBy%2522%253A0%252C%2522IsAnomaly%2522%253Afalse%252C%2522QueriesUsedInFormula%2522%253Anull%252C%2522filter%2522%253A%257B%2522expression%2522%253A%2522service.name%2BEXISTS%2522%257D%257D%255D%252C%2522queryFormulas%2522%253A%255B%255D%257D%257D&timeRange=%7B%22start%22%3A1753526700000%2C%22end%22%3A1753527000000%2C%22pageSize%22%3A100%7D&startTime=1753526700000&endTime=1753527000000&options=%7B%22maxLines%22%3A0%2C%22format%22%3A%22%22%2C%22selectColumns%22%3Anull%7D" )
}
func TestPrepareLinksToTracesV5 ( t * testing . T ) {
postableRule := ruletypes . PostableRule {
AlertName : "Tricky Condition Tests" ,
AlertType : ruletypes . AlertTypeTraces ,
RuleType : ruletypes . RuleTypeThreshold ,
EvalWindow : ruletypes . Duration ( 5 * time . Minute ) ,
Frequency : ruletypes . Duration ( 1 * time . Minute ) ,
RuleCondition : & ruletypes . RuleCondition {
CompositeQuery : & v3 . CompositeQuery {
QueryType : v3 . QueryTypeBuilder ,
Queries : [ ] qbtypes . QueryEnvelope {
{
Type : qbtypes . QueryTypeBuilder ,
Spec : qbtypes . QueryBuilderQuery [ qbtypes . TraceAggregation ] {
Name : "A" ,
StepInterval : qbtypes . Step { Duration : 1 * time . Minute } ,
Aggregations : [ ] qbtypes . TraceAggregation {
{
Expression : "count()" ,
} ,
} ,
Filter : & qbtypes . Filter {
Expression : "service.name EXISTS" ,
} ,
Signal : telemetrytypes . SignalTraces ,
} ,
} ,
} ,
} ,
CompareOp : "4" , // Not Equals
MatchType : "1" , // Once
Target : & [ ] float64 { 0.0 } [ 0 ] ,
SelectedQuery : "A" ,
} ,
Version : "v5" ,
}
logger := instrumentationtest . New ( ) . Logger ( )
rule , err := NewThresholdRule ( "69" , valuer . GenerateUUID ( ) , & postableRule , nil , nil , logger , WithEvalDelay ( 2 * time . Minute ) )
if err != nil {
assert . NoError ( t , err )
}
ts := time . UnixMilli ( 1753527163000 )
link := rule . prepareLinksToTraces ( context . Background ( ) , ts , labels . Labels { } )
assert . Contains ( t , link , "compositeQuery=%257B%2522queryType%2522%253A%2522builder%2522%252C%2522builder%2522%253A%257B%2522queryData%2522%253A%255B%257B%2522queryName%2522%253A%2522A%2522%252C%2522stepInterval%2522%253A60%252C%2522dataSource%2522%253A%2522traces%2522%252C%2522aggregateOperator%2522%253A%2522noop%2522%252C%2522aggregateAttribute%2522%253A%257B%2522key%2522%253A%2522%2522%252C%2522dataType%2522%253A%2522%2522%252C%2522type%2522%253A%2522%2522%252C%2522isColumn%2522%253Afalse%252C%2522isJSON%2522%253Afalse%257D%252C%2522expression%2522%253A%2522A%2522%252C%2522disabled%2522%253Afalse%252C%2522limit%2522%253A0%252C%2522offset%2522%253A0%252C%2522pageSize%2522%253A0%252C%2522ShiftBy%2522%253A0%252C%2522IsAnomaly%2522%253Afalse%252C%2522QueriesUsedInFormula%2522%253Anull%252C%2522filter%2522%253A%257B%2522expression%2522%253A%2522service.name%2BEXISTS%2522%257D%257D%255D%252C%2522queryFormulas%2522%253A%255B%255D%257D%257D&timeRange=%7B%22start%22%3A1753526700000000000%2C%22end%22%3A1753527000000000000%2C%22pageSize%22%3A100%7D&startTime=1753526700000000000&endTime=1753527000000000000&options=%7B%22maxLines%22%3A0%2C%22format%22%3A%22%22%2C%22selectColumns%22%3Anull%7D" )
}
2024-02-02 21:16:14 +05:30
func TestPrepareLinksToTraces ( t * testing . T ) {
2025-04-18 00:04:25 +05:30
postableRule := ruletypes . PostableRule {
2024-03-27 20:25:18 +05:30
AlertName : "Links to traces test" ,
2025-04-18 00:04:25 +05:30
AlertType : ruletypes . AlertTypeTraces ,
RuleType : ruletypes . RuleTypeThreshold ,
EvalWindow : ruletypes . Duration ( 5 * time . Minute ) ,
Frequency : ruletypes . Duration ( 1 * time . Minute ) ,
RuleCondition : & ruletypes . RuleCondition {
2024-02-02 21:16:14 +05:30
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" ,
} ,
}
2025-07-30 19:25:27 +05:30
logger := instrumentationtest . New ( ) . Logger ( )
rule , err := NewThresholdRule ( "69" , valuer . GenerateUUID ( ) , & postableRule , nil , nil , logger , WithEvalDelay ( 2 * time . Minute ) )
2024-02-02 21:16:14 +05:30
if err != nil {
assert . NoError ( t , err )
}
ts := time . UnixMilli ( 1705469040000 )
2025-07-30 19:25:27 +05:30
link := rule . prepareLinksToTraces ( context . Background ( ) , ts , labels . Labels { } )
2024-03-12 17:30:01 +05:30
assert . Contains ( t , link , "&timeRange=%7B%22start%22%3A1705468620000000000%2C%22end%22%3A1705468920000000000%2C%22pageSize%22%3A100%7D&startTime=1705468620000000000&endTime=1705468920000000000" )
2024-02-02 21:16:14 +05:30
}
2024-05-27 13:19:28 +05:30
func TestThresholdRuleLabelNormalization ( t * testing . T ) {
2025-04-18 00:04:25 +05:30
postableRule := ruletypes . PostableRule {
2024-05-27 13:19:28 +05:30
AlertName : "Tricky Condition Tests" ,
2025-04-18 00:04:25 +05:30
AlertType : ruletypes . AlertTypeMetric ,
RuleType : ruletypes . RuleTypeThreshold ,
EvalWindow : ruletypes . Duration ( 5 * time . Minute ) ,
Frequency : ruletypes . Duration ( 1 * time . Minute ) ,
RuleCondition : & ruletypes . RuleCondition {
2024-05-27 13:19:28 +05:30
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 {
2024-06-11 20:10:38 +05:30
{
2024-05-27 13:19:28 +05:30
"service.name" : "frontend" ,
} ,
} ,
} ,
expectAlert : true ,
compareOp : "3" , // Equals
matchType : "2" , // Always
target : 0.0 ,
} ,
}
2025-07-30 19:25:27 +05:30
logger := instrumentationtest . New ( ) . Logger ( )
2024-05-27 13:19:28 +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-05-27 13:19:28 +05:30
postableRule . RuleCondition . Target = & c . target
2025-07-30 19:25:27 +05:30
rule , err := NewThresholdRule ( "69" , valuer . GenerateUUID ( ) , & postableRule , nil , nil , logger , WithEvalDelay ( 2 * time . Minute ) )
2024-05-27 13:19:28 +05:30
if err != nil {
assert . NoError ( t , err )
}
values := c . values
for i := range values . Points {
values . Points [ i ] . Timestamp = time . Now ( ) . UnixMilli ( )
}
2024-09-24 10:22:52 +05:30
sample , shoulAlert := rule . ShouldAlert ( c . values )
2024-05-27 13:19:28 +05:30
for name , value := range c . values . Labels {
2024-10-03 16:56:58 +05:30
assert . Equal ( t , value , sample . Metric . Get ( name ) )
2024-05-27 13:19:28 +05:30
}
assert . Equal ( t , c . expectAlert , shoulAlert , "Test case %d" , idx )
}
}
2024-06-01 08:22:16 +05:30
2024-08-08 17:34:25 +05:30
func TestThresholdRuleEvalDelay ( t * testing . T ) {
2025-04-18 00:04:25 +05:30
postableRule := ruletypes . PostableRule {
2024-08-08 17:34:25 +05:30
AlertName : "Test Eval Delay" ,
2025-04-18 00:04:25 +05:30
AlertType : ruletypes . AlertTypeMetric ,
RuleType : ruletypes . RuleTypeThreshold ,
EvalWindow : ruletypes . Duration ( 5 * time . Minute ) ,
Frequency : ruletypes . Duration ( 1 * time . Minute ) ,
RuleCondition : & ruletypes . RuleCondition {
2024-08-08 17:34:25 +05:30
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:34:00 - 01:39:00
expectedQuery : "SELECT 1 >= 1717205640000 AND 1 <= 1717205940000" ,
} ,
}
2025-07-30 19:25:27 +05:30
logger := instrumentationtest . New ( ) . Logger ( )
2024-08-08 17:34:25 +05:30
for idx , c := range cases {
2025-07-30 19:25:27 +05:30
rule , err := NewThresholdRule ( "69" , valuer . GenerateUUID ( ) , & postableRule , nil , nil , logger ) // no eval delay
2024-08-08 17:34:25 +05:30
if err != nil {
assert . NoError ( t , err )
}
2025-07-30 19:25:27 +05:30
params , err := rule . prepareQueryRange ( context . Background ( ) , ts )
2024-09-11 09:56:59 +05:30
assert . NoError ( t , err )
2024-08-08 17:34:25 +05:30
assert . Equal ( t , c . expectedQuery , params . CompositeQuery . ClickHouseQueries [ "A" ] . Query , "Test case %d" , idx )
2025-07-30 19:25:27 +05:30
secondTimeParams , err := rule . prepareQueryRange ( context . Background ( ) , ts )
2024-09-11 09:56:59 +05:30
assert . NoError ( t , err )
2024-08-08 17:34:25 +05:30
assert . Equal ( t , c . expectedQuery , secondTimeParams . CompositeQuery . ClickHouseQueries [ "A" ] . Query , "Test case %d" , idx )
}
}
2024-06-01 08:22:16 +05:30
func TestThresholdRuleClickHouseTmpl ( t * testing . T ) {
2025-04-18 00:04:25 +05:30
postableRule := ruletypes . PostableRule {
2024-06-01 08:22:16 +05:30
AlertName : "Tricky Condition Tests" ,
2025-04-18 00:04:25 +05:30
AlertType : ruletypes . AlertTypeMetric ,
RuleType : ruletypes . RuleTypeThreshold ,
EvalWindow : ruletypes . Duration ( 5 * time . Minute ) ,
Frequency : ruletypes . Duration ( 1 * time . Minute ) ,
RuleCondition : & ruletypes . RuleCondition {
2024-06-01 08:22:16 +05:30
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" ,
} ,
}
2025-07-30 19:25:27 +05:30
logger := instrumentationtest . New ( ) . Logger ( )
2024-06-01 08:22:16 +05:30
for idx , c := range cases {
2025-07-30 19:25:27 +05:30
rule , err := NewThresholdRule ( "69" , valuer . GenerateUUID ( ) , & postableRule , nil , nil , logger , WithEvalDelay ( 2 * time . Minute ) )
2024-06-01 08:22:16 +05:30
if err != nil {
assert . NoError ( t , err )
}
2025-07-30 19:25:27 +05:30
params , err := rule . prepareQueryRange ( context . Background ( ) , ts )
2024-09-11 09:56:59 +05:30
assert . NoError ( t , err )
2024-06-01 08:22:16 +05:30
assert . Equal ( t , c . expectedQuery , params . CompositeQuery . ClickHouseQueries [ "A" ] . Query , "Test case %d" , idx )
2024-06-05 19:35:48 +05:30
2025-07-30 19:25:27 +05:30
secondTimeParams , err := rule . prepareQueryRange ( context . Background ( ) , ts )
2024-09-11 09:56:59 +05:30
assert . NoError ( t , err )
2024-06-05 19:35:48 +05:30
assert . Equal ( t , c . expectedQuery , secondTimeParams . CompositeQuery . ClickHouseQueries [ "A" ] . Query , "Test case %d" , idx )
2024-06-01 08:22:16 +05:30
}
}
2024-08-09 12:34:40 +05:30
type queryMatcherAny struct {
}
2024-11-22 12:00:29 +05:30
func ( m * queryMatcherAny ) Match ( x string , y string ) error {
2024-08-09 12:34:40 +05:30
return nil
}
func TestThresholdRuleUnitCombinations ( t * testing . T ) {
2025-04-18 00:04:25 +05:30
postableRule := ruletypes . PostableRule {
2024-08-09 12:34:40 +05:30
AlertName : "Units test" ,
2025-04-18 00:04:25 +05:30
AlertType : ruletypes . AlertTypeMetric ,
RuleType : ruletypes . RuleTypeThreshold ,
EvalWindow : ruletypes . Duration ( 5 * time . Minute ) ,
Frequency : ruletypes . Duration ( 1 * time . Minute ) ,
RuleCondition : & ruletypes . RuleCondition {
2024-08-09 12:34:40 +05:30
CompositeQuery : & v3 . CompositeQuery {
QueryType : v3 . QueryTypeBuilder ,
BuilderQueries : map [ string ] * v3 . BuilderQuery {
"A" : {
QueryName : "A" ,
StepInterval : 60 ,
AggregateAttribute : v3 . AttributeKey {
Key : "signoz_calls_total" ,
} ,
AggregateOperator : v3 . AggregateOperatorSumRate ,
DataSource : v3 . DataSourceMetrics ,
Expression : "A" ,
} ,
} ,
} ,
} ,
}
2025-03-31 19:41:11 +05:30
telemetryStore := telemetrystoretest . New ( telemetrystore . Config { } , & queryMatcherAny { } )
2024-08-09 12:34:40 +05:30
cols := make ( [ ] cmock . ColumnType , 0 )
cols = append ( cols , cmock . ColumnType { Name : "value" , Type : "Float64" } )
cols = append ( cols , cmock . ColumnType { Name : "attr" , Type : "String" } )
cols = append ( cols , cmock . ColumnType { Name : "timestamp" , Type : "String" } )
cases := [ ] struct {
targetUnit string
yAxisUnit string
values [ ] [ ] interface { }
expectAlerts int
compareOp string
matchType string
target float64
summaryAny [ ] string
} {
{
targetUnit : "s" ,
yAxisUnit : "ns" ,
values : [ ] [ ] interface { } {
{ float64 ( 572588400 ) , "attr" , time . Now ( ) } , // 0.57 seconds
{ float64 ( 572386400 ) , "attr" , time . Now ( ) . Add ( 1 * time . Second ) } , // 0.57 seconds
{ float64 ( 300947400 ) , "attr" , time . Now ( ) . Add ( 2 * time . Second ) } , // 0.3 seconds
{ float64 ( 299316000 ) , "attr" , time . Now ( ) . Add ( 3 * time . Second ) } , // 0.3 seconds
{ float64 ( 66640400.00000001 ) , "attr" , time . Now ( ) . Add ( 4 * time . Second ) } , // 0.06 seconds
} ,
expectAlerts : 0 ,
compareOp : "1" , // Above
matchType : "1" , // Once
target : 1 , // 1 second
} ,
{
targetUnit : "ms" ,
yAxisUnit : "ns" ,
values : [ ] [ ] interface { } {
{ float64 ( 572588400 ) , "attr" , time . Now ( ) } , // 572.58 ms
{ float64 ( 572386400 ) , "attr" , time . Now ( ) . Add ( 1 * time . Second ) } , // 572.38 ms
{ float64 ( 300947400 ) , "attr" , time . Now ( ) . Add ( 2 * time . Second ) } , // 300.94 ms
{ float64 ( 299316000 ) , "attr" , time . Now ( ) . Add ( 3 * time . Second ) } , // 299.31 ms
{ float64 ( 66640400.00000001 ) , "attr" , time . Now ( ) . Add ( 4 * time . Second ) } , // 66.64 ms
} ,
expectAlerts : 4 ,
compareOp : "1" , // Above
matchType : "1" , // Once
target : 200 , // 200 ms
summaryAny : [ ] string {
"observed metric value is 299 ms" ,
"the observed metric value is 573 ms" ,
"the observed metric value is 572 ms" ,
"the observed metric value is 301 ms" ,
} ,
} ,
{
targetUnit : "decgbytes" ,
yAxisUnit : "bytes" ,
values : [ ] [ ] interface { } {
{ float64 ( 2863284053 ) , "attr" , time . Now ( ) } , // 2.86 GB
{ float64 ( 2863388842 ) , "attr" , time . Now ( ) . Add ( 1 * time . Second ) } , // 2.86 GB
{ float64 ( 300947400 ) , "attr" , time . Now ( ) . Add ( 2 * time . Second ) } , // 0.3 GB
{ float64 ( 299316000 ) , "attr" , time . Now ( ) . Add ( 3 * time . Second ) } , // 0.3 GB
{ float64 ( 66640400.00000001 ) , "attr" , time . Now ( ) . Add ( 4 * time . Second ) } , // 66.64 MB
} ,
expectAlerts : 0 ,
compareOp : "1" , // Above
matchType : "1" , // Once
target : 200 , // 200 GB
} ,
2025-08-20 11:04:50 +07:00
{
targetUnit : "decgbytes" ,
yAxisUnit : "By" ,
values : [ ] [ ] interface { } {
{ float64 ( 2863284053 ) , "attr" , time . Now ( ) } , // 2.86 GB
{ float64 ( 2863388842 ) , "attr" , time . Now ( ) . Add ( 1 * time . Second ) } , // 2.86 GB
{ float64 ( 300947400 ) , "attr" , time . Now ( ) . Add ( 2 * time . Second ) } , // 0.3 GB
{ float64 ( 299316000 ) , "attr" , time . Now ( ) . Add ( 3 * time . Second ) } , // 0.3 GB
{ float64 ( 66640400.00000001 ) , "attr" , time . Now ( ) . Add ( 4 * time . Second ) } , // 66.64 MB
} ,
expectAlerts : 0 ,
compareOp : "1" , // Above
matchType : "1" , // Once
target : 200 , // 200 GB
} ,
{
targetUnit : "h" ,
yAxisUnit : "min" ,
values : [ ] [ ] interface { } {
{ float64 ( 55 ) , "attr" , time . Now ( ) } , // 55 minutes
{ float64 ( 57 ) , "attr" , time . Now ( ) . Add ( 1 * time . Minute ) } , // 57 minutes
{ float64 ( 30 ) , "attr" , time . Now ( ) . Add ( 2 * time . Minute ) } , // 30 minutes
{ float64 ( 29 ) , "attr" , time . Now ( ) . Add ( 3 * time . Minute ) } , // 29 minutes
} ,
expectAlerts : 0 ,
compareOp : "1" , // Above
matchType : "1" , // Once
target : 1 , // 1 hour
} ,
2024-08-09 12:34:40 +05:30
}
2025-07-30 19:25:27 +05:30
logger := instrumentationtest . New ( ) . Logger ( )
2024-08-09 12:34:40 +05:30
for idx , c := range cases {
rows := cmock . NewRows ( cols , c . values )
// We are testing the eval logic after the query is run
// so we don't care about the query string here
queryString := "SELECT any"
2025-03-31 19:41:11 +05:30
telemetryStore . Mock ( ) .
2024-08-09 12:34:40 +05:30
ExpectQuery ( queryString ) .
WillReturnRows ( rows )
2025-04-18 00:04:25 +05:30
postableRule . RuleCondition . CompareOp = ruletypes . CompareOp ( c . compareOp )
postableRule . RuleCondition . MatchType = ruletypes . MatchType ( c . matchType )
2024-08-09 12:34:40 +05:30
postableRule . RuleCondition . Target = & c . target
postableRule . RuleCondition . CompositeQuery . Unit = c . yAxisUnit
postableRule . RuleCondition . TargetUnit = c . targetUnit
postableRule . Annotations = map [ string ] string {
"description" : "This alert is fired when the defined metric (current value: {{$value}}) crosses the threshold ({{$threshold}})" ,
"summary" : "The rule threshold is set to {{$threshold}}, and the observed metric value is {{$value}}" ,
}
2025-01-30 15:51:55 +05:30
options := clickhouseReader . NewOptions ( "" , "" , "archiveNamespace" )
2025-05-03 18:30:07 +05:30
readerCache , err := cachetest . New ( cache . Config { Provider : "memory" , Memory : cache . Memory { TTL : DefaultFrequency } } )
2025-03-31 19:41:11 +05:30
require . NoError ( t , err )
2025-04-28 21:01:35 +05:30
reader := clickhouseReader . NewReaderFromClickhouseConnection ( options , nil , telemetryStore , prometheustest . New ( instrumentationtest . New ( ) . Logger ( ) , prometheus . Config { } ) , "" , time . Duration ( time . Second ) , readerCache )
2025-07-30 19:25:27 +05:30
rule , err := NewThresholdRule ( "69" , valuer . GenerateUUID ( ) , & postableRule , reader , nil , logger )
2024-09-24 10:22:52 +05:30
rule . TemporalityMap = map [ string ] map [ v3 . Temporality ] bool {
2024-08-09 12:34:40 +05:30
"signoz_calls_total" : {
v3 . Delta : true ,
} ,
}
if err != nil {
assert . NoError ( t , err )
}
2024-09-11 09:56:59 +05:30
retVal , err := rule . Eval ( context . Background ( ) , time . Now ( ) )
2024-08-09 12:34:40 +05:30
if err != nil {
assert . NoError ( t , err )
}
assert . Equal ( t , c . expectAlerts , retVal . ( int ) , "case %d" , idx )
if c . expectAlerts != 0 {
foundCount := 0
2024-09-24 10:22:52 +05:30
for _ , item := range rule . Active {
2024-08-09 12:34:40 +05:30
for _ , summary := range c . summaryAny {
if strings . Contains ( item . Annotations . Get ( "summary" ) , summary ) {
foundCount ++
break
}
}
}
assert . Equal ( t , c . expectAlerts , foundCount , "case %d" , idx )
}
}
}
2024-08-09 15:31:39 +05:30
func TestThresholdRuleNoData ( t * testing . T ) {
2025-04-18 00:04:25 +05:30
postableRule := ruletypes . PostableRule {
2024-09-13 17:30:02 +05:30
AlertName : "No data test" ,
2025-04-18 00:04:25 +05:30
AlertType : ruletypes . AlertTypeMetric ,
RuleType : ruletypes . RuleTypeThreshold ,
EvalWindow : ruletypes . Duration ( 5 * time . Minute ) ,
Frequency : ruletypes . Duration ( 1 * time . Minute ) ,
RuleCondition : & ruletypes . RuleCondition {
2024-08-09 15:31:39 +05:30
CompositeQuery : & v3 . CompositeQuery {
QueryType : v3 . QueryTypeBuilder ,
BuilderQueries : map [ string ] * v3 . BuilderQuery {
"A" : {
QueryName : "A" ,
StepInterval : 60 ,
AggregateAttribute : v3 . AttributeKey {
Key : "signoz_calls_total" ,
} ,
AggregateOperator : v3 . AggregateOperatorSumRate ,
DataSource : v3 . DataSourceMetrics ,
Expression : "A" ,
} ,
} ,
} ,
AlertOnAbsent : true ,
} ,
}
2025-03-31 19:41:11 +05:30
telemetryStore := telemetrystoretest . New ( telemetrystore . Config { } , & queryMatcherAny { } )
2024-08-09 15:31:39 +05:30
cols := make ( [ ] cmock . ColumnType , 0 )
cols = append ( cols , cmock . ColumnType { Name : "value" , Type : "Float64" } )
cols = append ( cols , cmock . ColumnType { Name : "attr" , Type : "String" } )
cols = append ( cols , cmock . ColumnType { Name : "timestamp" , Type : "String" } )
cases := [ ] struct {
values [ ] [ ] interface { }
expectNoData bool
} {
{
values : [ ] [ ] interface { } { } ,
expectNoData : true ,
} ,
}
2025-07-30 19:25:27 +05:30
logger := instrumentationtest . New ( ) . Logger ( )
2024-08-09 15:31:39 +05:30
for idx , c := range cases {
rows := cmock . NewRows ( cols , c . values )
// We are testing the eval logic after the query is run
// so we don't care about the query string here
queryString := "SELECT any"
2025-03-31 19:41:11 +05:30
telemetryStore . Mock ( ) .
2024-08-09 15:31:39 +05:30
ExpectQuery ( queryString ) .
WillReturnRows ( rows )
var target float64 = 0
2025-04-18 00:04:25 +05:30
postableRule . RuleCondition . CompareOp = ruletypes . ValueIsEq
postableRule . RuleCondition . MatchType = ruletypes . AtleastOnce
2024-08-09 15:31:39 +05:30
postableRule . RuleCondition . Target = & target
postableRule . Annotations = map [ string ] string {
"description" : "This alert is fired when the defined metric (current value: {{$value}}) crosses the threshold ({{$threshold}})" ,
"summary" : "The rule threshold is set to {{$threshold}}, and the observed metric value is {{$value}}" ,
}
2025-05-03 18:30:07 +05:30
readerCache , err := cachetest . New ( cache . Config { Provider : "memory" , Memory : cache . Memory { TTL : DefaultFrequency } } )
assert . NoError ( t , err )
2025-01-30 15:51:55 +05:30
options := clickhouseReader . NewOptions ( "" , "" , "archiveNamespace" )
2025-04-28 21:01:35 +05:30
reader := clickhouseReader . NewReaderFromClickhouseConnection ( options , nil , telemetryStore , prometheustest . New ( instrumentationtest . New ( ) . Logger ( ) , prometheus . Config { } ) , "" , time . Duration ( time . Second ) , readerCache )
2024-08-09 15:31:39 +05:30
2025-07-30 19:25:27 +05:30
rule , err := NewThresholdRule ( "69" , valuer . GenerateUUID ( ) , & postableRule , reader , nil , logger )
2024-09-24 10:22:52 +05:30
rule . TemporalityMap = map [ string ] map [ v3 . Temporality ] bool {
2024-08-09 15:31:39 +05:30
"signoz_calls_total" : {
v3 . Delta : true ,
} ,
}
if err != nil {
assert . NoError ( t , err )
}
2024-09-11 09:56:59 +05:30
retVal , err := rule . Eval ( context . Background ( ) , time . Now ( ) )
2024-08-09 15:31:39 +05:30
if err != nil {
assert . NoError ( t , err )
}
assert . Equal ( t , 1 , retVal . ( int ) , "case %d" , idx )
2024-09-24 10:22:52 +05:30
for _ , item := range rule . Active {
2024-08-09 15:31:39 +05:30
if c . expectNoData {
assert . True ( t , strings . Contains ( item . Labels . Get ( labels . AlertNameLabel ) , "[No data]" ) , "case %d" , idx )
} else {
assert . False ( t , strings . Contains ( item . Labels . Get ( labels . AlertNameLabel ) , "[No data]" ) , "case %d" , idx )
}
}
}
}
2024-09-13 17:30:02 +05:30
func TestThresholdRuleTracesLink ( t * testing . T ) {
2025-04-18 00:04:25 +05:30
postableRule := ruletypes . PostableRule {
2024-09-13 17:30:02 +05:30
AlertName : "Traces link test" ,
2025-04-18 00:04:25 +05:30
AlertType : ruletypes . AlertTypeTraces ,
RuleType : ruletypes . RuleTypeThreshold ,
EvalWindow : ruletypes . Duration ( 5 * time . Minute ) ,
Frequency : ruletypes . Duration ( 1 * time . Minute ) ,
RuleCondition : & ruletypes . RuleCondition {
2024-09-13 17:30:02 +05:30
CompositeQuery : & v3 . CompositeQuery {
QueryType : v3 . QueryTypeBuilder ,
BuilderQueries : map [ string ] * v3 . BuilderQuery {
"A" : {
QueryName : "A" ,
StepInterval : 60 ,
AggregateAttribute : v3 . AttributeKey {
Key : "durationNano" ,
} ,
AggregateOperator : v3 . AggregateOperatorP95 ,
DataSource : v3 . DataSourceTraces ,
Expression : "A" ,
Filters : & v3 . FilterSet {
Operator : "AND" ,
Items : [ ] v3 . FilterItem {
{
Key : v3 . AttributeKey { Key : "httpMethod" , IsColumn : true , Type : v3 . AttributeKeyTypeTag , DataType : v3 . AttributeKeyDataTypeString } ,
Value : "GET" ,
Operator : v3 . FilterOperatorEqual ,
} ,
} ,
} ,
} ,
} ,
} ,
} ,
}
2025-03-31 19:41:11 +05:30
telemetryStore := telemetrystoretest . New ( telemetrystore . Config { } , & queryMatcherAny { } )
2024-09-13 17:30:02 +05:30
2024-09-17 15:33:17 +05:30
metaCols := make ( [ ] cmock . ColumnType , 0 )
metaCols = append ( metaCols , cmock . ColumnType { Name : "DISTINCT(tagKey)" , Type : "String" } )
metaCols = append ( metaCols , cmock . ColumnType { Name : "tagType" , Type : "String" } )
metaCols = append ( metaCols , cmock . ColumnType { Name : "dataType" , Type : "String" } )
metaCols = append ( metaCols , cmock . ColumnType { Name : "isColumn" , Type : "Bool" } )
2024-09-13 17:30:02 +05:30
cols := make ( [ ] cmock . ColumnType , 0 )
cols = append ( cols , cmock . ColumnType { Name : "value" , Type : "Float64" } )
cols = append ( cols , cmock . ColumnType { Name : "attr" , Type : "String" } )
cols = append ( cols , cmock . ColumnType { Name : "timestamp" , Type : "String" } )
2025-07-30 19:25:27 +05:30
logger := instrumentationtest . New ( ) . Logger ( )
2024-09-13 17:30:02 +05:30
for idx , c := range testCases {
2024-09-17 15:33:17 +05:30
metaRows := cmock . NewRows ( metaCols , c . metaValues )
2025-03-31 19:41:11 +05:30
telemetryStore . Mock ( ) .
2024-11-22 12:00:29 +05:30
ExpectQuery ( "SELECT DISTINCT(tagKey), tagType, dataType FROM archiveNamespace.span_attributes_keys" ) .
2024-09-17 15:33:17 +05:30
WillReturnRows ( metaRows )
2025-03-31 19:41:11 +05:30
telemetryStore . Mock ( ) .
2024-11-22 12:00:29 +05:30
ExpectSelect ( "SHOW CREATE TABLE signoz_traces.distributed_signoz_index_v3" ) . WillReturnRows ( & cmock . Rows { } )
2024-09-13 17:30:02 +05:30
rows := cmock . NewRows ( cols , c . values )
// We are testing the eval logic after the query is run
// so we don't care about the query string here
queryString := "SELECT any"
2025-03-31 19:41:11 +05:30
telemetryStore . Mock ( ) .
2024-09-13 17:30:02 +05:30
ExpectQuery ( queryString ) .
WillReturnRows ( rows )
2025-04-18 00:04:25 +05:30
postableRule . RuleCondition . CompareOp = ruletypes . CompareOp ( c . compareOp )
postableRule . RuleCondition . MatchType = ruletypes . MatchType ( c . matchType )
2024-09-13 17:30:02 +05:30
postableRule . RuleCondition . Target = & c . target
postableRule . RuleCondition . CompositeQuery . Unit = c . yAxisUnit
postableRule . RuleCondition . TargetUnit = c . targetUnit
postableRule . Annotations = map [ string ] string {
"description" : "This alert is fired when the defined metric (current value: {{$value}}) crosses the threshold ({{$threshold}})" ,
"summary" : "The rule threshold is set to {{$threshold}}, and the observed metric value is {{$value}}" ,
}
2025-01-30 15:51:55 +05:30
options := clickhouseReader . NewOptions ( "" , "" , "archiveNamespace" )
2025-04-28 21:01:35 +05:30
reader := clickhouseReader . NewReaderFromClickhouseConnection ( options , nil , telemetryStore , prometheustest . New ( instrumentationtest . New ( ) . Logger ( ) , prometheus . Config { } ) , "" , time . Duration ( time . Second ) , nil )
2024-09-13 17:30:02 +05:30
2025-07-30 19:25:27 +05:30
rule , err := NewThresholdRule ( "69" , valuer . GenerateUUID ( ) , & postableRule , reader , nil , logger )
2024-09-24 10:22:52 +05:30
rule . TemporalityMap = map [ string ] map [ v3 . Temporality ] bool {
2024-09-13 17:30:02 +05:30
"signoz_calls_total" : {
v3 . Delta : true ,
} ,
}
if err != nil {
assert . NoError ( t , err )
}
retVal , err := rule . Eval ( context . Background ( ) , time . Now ( ) )
if err != nil {
assert . NoError ( t , err )
}
if c . expectAlerts == 0 {
assert . Equal ( t , 0 , retVal . ( int ) , "case %d" , idx )
} else {
assert . Equal ( t , c . expectAlerts , retVal . ( int ) , "case %d" , idx )
2024-09-24 10:22:52 +05:30
for _ , item := range rule . Active {
2024-09-13 17:30:02 +05:30
for name , value := range item . Annotations . Map ( ) {
if name == "related_traces" {
assert . NotEmpty ( t , value , "case %d" , idx )
assert . Contains ( t , value , "GET" )
}
}
}
}
}
}
func TestThresholdRuleLogsLink ( t * testing . T ) {
2025-04-18 00:04:25 +05:30
postableRule := ruletypes . PostableRule {
2024-09-13 17:30:02 +05:30
AlertName : "Logs link test" ,
2025-04-18 00:04:25 +05:30
AlertType : ruletypes . AlertTypeLogs ,
RuleType : ruletypes . RuleTypeThreshold ,
EvalWindow : ruletypes . Duration ( 5 * time . Minute ) ,
Frequency : ruletypes . Duration ( 1 * time . Minute ) ,
RuleCondition : & ruletypes . RuleCondition {
2024-09-13 17:30:02 +05:30
CompositeQuery : & v3 . CompositeQuery {
QueryType : v3 . QueryTypeBuilder ,
BuilderQueries : map [ string ] * v3 . BuilderQuery {
"A" : {
QueryName : "A" ,
StepInterval : 60 ,
AggregateAttribute : v3 . AttributeKey {
Key : "component" ,
} ,
AggregateOperator : v3 . AggregateOperatorCountDistinct ,
DataSource : v3 . DataSourceLogs ,
Expression : "A" ,
Filters : & v3 . FilterSet {
Operator : "AND" ,
Items : [ ] v3 . FilterItem {
{
Key : v3 . AttributeKey { Key : "k8s.container.name" , IsColumn : false , Type : v3 . AttributeKeyTypeTag , DataType : v3 . AttributeKeyDataTypeString } ,
Value : "testcontainer" ,
Operator : v3 . FilterOperatorEqual ,
} ,
} ,
} ,
} ,
} ,
} ,
} ,
}
2025-03-31 19:41:11 +05:30
telemetryStore := telemetrystoretest . New ( telemetrystore . Config { } , & queryMatcherAny { } )
2024-09-13 17:30:02 +05:30
2024-09-17 15:33:17 +05:30
attrMetaCols := make ( [ ] cmock . ColumnType , 0 )
attrMetaCols = append ( attrMetaCols , cmock . ColumnType { Name : "name" , Type : "String" } )
2025-07-23 15:30:37 +05:30
attrMetaCols = append ( attrMetaCols , cmock . ColumnType { Name : "datatype" , Type : "String" } )
2024-09-17 15:33:17 +05:30
resourceMetaCols := make ( [ ] cmock . ColumnType , 0 )
resourceMetaCols = append ( resourceMetaCols , cmock . ColumnType { Name : "name" , Type : "String" } )
2025-07-23 15:30:37 +05:30
resourceMetaCols = append ( resourceMetaCols , cmock . ColumnType { Name : "datatype" , Type : "String" } )
2024-09-17 15:33:17 +05:30
createTableCols := make ( [ ] cmock . ColumnType , 0 )
createTableCols = append ( createTableCols , cmock . ColumnType { Name : "statement" , Type : "String" } )
2024-09-13 17:30:02 +05:30
cols := make ( [ ] cmock . ColumnType , 0 )
cols = append ( cols , cmock . ColumnType { Name : "value" , Type : "Float64" } )
cols = append ( cols , cmock . ColumnType { Name : "attr" , Type : "String" } )
cols = append ( cols , cmock . ColumnType { Name : "timestamp" , Type : "String" } )
2025-07-30 19:25:27 +05:30
logger := instrumentationtest . New ( ) . Logger ( )
2024-09-13 17:30:02 +05:30
for idx , c := range testCases {
2024-09-17 15:33:17 +05:30
attrMetaRows := cmock . NewRows ( attrMetaCols , c . attrMetaValues )
2025-03-31 19:41:11 +05:30
telemetryStore . Mock ( ) .
2025-07-23 15:30:37 +05:30
ExpectSelect ( "SELECT DISTINCT name, datatype from signoz_logs.distributed_logs_attribute_keys where name in ('component','k8s.container.name') group by name, datatype" ) .
2024-09-17 15:33:17 +05:30
WillReturnRows ( attrMetaRows )
resourceMetaRows := cmock . NewRows ( resourceMetaCols , c . resourceMetaValues )
2025-03-31 19:41:11 +05:30
telemetryStore . Mock ( ) .
2025-07-23 15:30:37 +05:30
ExpectSelect ( "SELECT DISTINCT name, datatype from signoz_logs.distributed_logs_resource_keys where name in ('component','k8s.container.name') group by name, datatype" ) .
2024-09-17 15:33:17 +05:30
WillReturnRows ( resourceMetaRows )
createTableRows := cmock . NewRows ( createTableCols , c . createTableValues )
2025-03-31 19:41:11 +05:30
telemetryStore . Mock ( ) .
2024-09-17 15:33:17 +05:30
ExpectSelect ( "SHOW CREATE TABLE signoz_logs.logs" ) .
WillReturnRows ( createTableRows )
2024-09-13 17:30:02 +05:30
rows := cmock . NewRows ( cols , c . values )
// We are testing the eval logic after the query is run
// so we don't care about the query string here
queryString := "SELECT any"
2025-03-31 19:41:11 +05:30
telemetryStore . Mock ( ) .
2024-09-13 17:30:02 +05:30
ExpectQuery ( queryString ) .
WillReturnRows ( rows )
2025-04-18 00:04:25 +05:30
postableRule . RuleCondition . CompareOp = ruletypes . CompareOp ( c . compareOp )
postableRule . RuleCondition . MatchType = ruletypes . MatchType ( c . matchType )
2024-09-13 17:30:02 +05:30
postableRule . RuleCondition . Target = & c . target
postableRule . RuleCondition . CompositeQuery . Unit = c . yAxisUnit
postableRule . RuleCondition . TargetUnit = c . targetUnit
postableRule . Annotations = map [ string ] string {
"description" : "This alert is fired when the defined metric (current value: {{$value}}) crosses the threshold ({{$threshold}})" ,
"summary" : "The rule threshold is set to {{$threshold}}, and the observed metric value is {{$value}}" ,
}
2025-01-30 15:51:55 +05:30
options := clickhouseReader . NewOptions ( "" , "" , "archiveNamespace" )
2025-04-28 21:01:35 +05:30
reader := clickhouseReader . NewReaderFromClickhouseConnection ( options , nil , telemetryStore , prometheustest . New ( instrumentationtest . New ( ) . Logger ( ) , prometheus . Config { } ) , "" , time . Duration ( time . Second ) , nil )
2024-09-13 17:30:02 +05:30
2025-07-30 19:25:27 +05:30
rule , err := NewThresholdRule ( "69" , valuer . GenerateUUID ( ) , & postableRule , reader , nil , logger )
2024-09-24 10:22:52 +05:30
rule . TemporalityMap = map [ string ] map [ v3 . Temporality ] bool {
2024-09-13 17:30:02 +05:30
"signoz_calls_total" : {
v3 . Delta : true ,
} ,
}
if err != nil {
assert . NoError ( t , err )
}
retVal , err := rule . Eval ( context . Background ( ) , time . Now ( ) )
if err != nil {
assert . NoError ( t , err )
}
if c . expectAlerts == 0 {
assert . Equal ( t , 0 , retVal . ( int ) , "case %d" , idx )
} else {
assert . Equal ( t , c . expectAlerts , retVal . ( int ) , "case %d" , idx )
2024-09-24 10:22:52 +05:30
for _ , item := range rule . Active {
2024-09-13 17:30:02 +05:30
for name , value := range item . Annotations . Map ( ) {
if name == "related_logs" {
assert . NotEmpty ( t , value , "case %d" , idx )
assert . Contains ( t , value , "testcontainer" )
}
}
}
}
}
}
2024-10-24 02:20:32 +05:30
func TestThresholdRuleShiftBy ( t * testing . T ) {
target := float64 ( 10 )
2025-04-18 00:04:25 +05:30
postableRule := ruletypes . PostableRule {
2024-10-24 02:20:32 +05:30
AlertName : "Logs link test" ,
2025-04-18 00:04:25 +05:30
AlertType : ruletypes . AlertTypeLogs ,
RuleType : ruletypes . RuleTypeThreshold ,
EvalWindow : ruletypes . Duration ( 5 * time . Minute ) ,
Frequency : ruletypes . Duration ( 1 * time . Minute ) ,
RuleCondition : & ruletypes . RuleCondition {
2024-10-24 02:20:32 +05:30
CompositeQuery : & v3 . CompositeQuery {
QueryType : v3 . QueryTypeBuilder ,
BuilderQueries : map [ string ] * v3 . BuilderQuery {
"A" : {
QueryName : "A" ,
StepInterval : 60 ,
AggregateAttribute : v3 . AttributeKey {
Key : "component" ,
} ,
AggregateOperator : v3 . AggregateOperatorCountDistinct ,
DataSource : v3 . DataSourceLogs ,
Expression : "A" ,
Filters : & v3 . FilterSet {
Operator : "AND" ,
Items : [ ] v3 . FilterItem {
{
Key : v3 . AttributeKey { Key : "k8s.container.name" , IsColumn : false , Type : v3 . AttributeKeyTypeTag , DataType : v3 . AttributeKeyDataTypeString } ,
Value : "testcontainer" ,
Operator : v3 . FilterOperatorEqual ,
} ,
} ,
} ,
Functions : [ ] v3 . Function {
{
Name : v3 . FunctionNameTimeShift ,
Args : [ ] interface { } { float64 ( 10 ) } ,
} ,
} ,
} ,
} ,
} ,
Target : & target ,
2025-04-18 00:04:25 +05:30
CompareOp : ruletypes . ValueAboveOrEq ,
2024-10-24 02:20:32 +05:30
} ,
}
2025-07-30 19:25:27 +05:30
logger := instrumentationtest . New ( ) . Logger ( )
rule , err := NewThresholdRule ( "69" , valuer . GenerateUUID ( ) , & postableRule , nil , nil , logger )
2024-10-24 02:20:32 +05:30
if err != nil {
assert . NoError ( t , err )
}
rule . TemporalityMap = map [ string ] map [ v3 . Temporality ] bool {
"signoz_calls_total" : {
v3 . Delta : true ,
} ,
}
2025-07-30 19:25:27 +05:30
params , err := rule . prepareQueryRange ( context . Background ( ) , time . Now ( ) )
2024-10-24 02:20:32 +05:30
if err != nil {
assert . NoError ( t , err )
}
assert . Equal ( t , int64 ( 10 ) , params . CompositeQuery . BuilderQueries [ "A" ] . ShiftBy )
}