signoz/pkg/querier/postprocess_fillzero_test.go
2025-06-18 15:30:29 +05:30

231 lines
5.8 KiB
Go

package querier
import (
"testing"
"time"
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
)
func TestPrepareFillZeroArgsWithStep(t *testing.T) {
q := &querier{}
req := &qbtypes.QueryRangeRequest{
Start: 1000000,
End: 2000000,
}
tests := []struct {
name string
functions []qbtypes.Function
step int64
checkArgs bool
}{
{
name: "fillZero without args",
functions: []qbtypes.Function{
{
Name: qbtypes.FunctionNameFillZero,
Args: []qbtypes.FunctionArg{},
},
},
step: 30000, // 30 seconds
checkArgs: true,
},
{
name: "fillZero with existing args",
functions: []qbtypes.Function{
{
Name: qbtypes.FunctionNameFillZero,
Args: []qbtypes.FunctionArg{
{Value: 500000.0},
{Value: 1500000.0},
{Value: 15000.0},
},
},
},
step: 60000,
checkArgs: false, // Should not modify existing args
},
{
name: "other function should not be modified",
functions: []qbtypes.Function{
{
Name: qbtypes.FunctionNameAbsolute,
Args: []qbtypes.FunctionArg{},
},
},
step: 60000,
checkArgs: false,
},
{
name: "no copy when fillZero already has args",
functions: []qbtypes.Function{
{
Name: qbtypes.FunctionNameFillZero,
Args: []qbtypes.FunctionArg{
{Value: 1000.0},
{Value: 2000.0},
{Value: 500.0},
},
},
{
Name: qbtypes.FunctionNameAbsolute,
},
},
step: 60000,
checkArgs: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := q.prepareFillZeroArgsWithStep(tt.functions, req, tt.step)
if len(result) != len(tt.functions) {
t.Fatalf("Expected %d functions, got %d", len(tt.functions), len(result))
}
// Check if no copy was made when not needed
if tt.name == "no copy when fillZero already has args" || tt.name == "other function should not be modified" {
// Verify that the result is the same slice (no copy)
if &result[0] != &tt.functions[0] {
t.Errorf("Expected no copy, but a copy was made")
}
}
for _, fn := range result {
if fn.Name == qbtypes.FunctionNameFillZero && tt.checkArgs {
if len(fn.Args) != 3 {
t.Errorf("Expected 3 args for fillZero, got %d", len(fn.Args))
}
// Check start
if start, ok := fn.Args[0].Value.(float64); !ok || start != float64(req.Start) {
t.Errorf("Expected start %f, got %v", float64(req.Start), fn.Args[0].Value)
}
// Check end
if end, ok := fn.Args[1].Value.(float64); !ok || end != float64(req.End) {
t.Errorf("Expected end %f, got %v", float64(req.End), fn.Args[1].Value)
}
// Check step
if step, ok := fn.Args[2].Value.(float64); !ok || step != float64(tt.step) {
t.Errorf("Expected step %f, got %v", float64(tt.step), fn.Args[2].Value)
}
}
}
})
}
}
func TestCalculateFormulaStep(t *testing.T) {
tests := []struct {
name string
expression string
req *qbtypes.QueryRangeRequest
expected int64
}{
{
name: "single query reference",
expression: "A * 2",
req: &qbtypes.QueryRangeRequest{
CompositeQuery: qbtypes.CompositeQuery{
Queries: []qbtypes.QueryEnvelope{
{
Type: qbtypes.QueryTypeBuilder,
Spec: qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{
Name: "A",
StepInterval: qbtypes.Step{Duration: 60 * time.Second},
},
},
{
Type: qbtypes.QueryTypeBuilder,
Spec: qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{
Name: "B",
StepInterval: qbtypes.Step{Duration: 120 * time.Second},
},
},
},
},
},
expected: 60000, // Only A is referenced
},
{
name: "multiple query references",
expression: "A + B",
req: &qbtypes.QueryRangeRequest{
CompositeQuery: qbtypes.CompositeQuery{
Queries: []qbtypes.QueryEnvelope{
{
Type: qbtypes.QueryTypeBuilder,
Spec: qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{
Name: "A",
StepInterval: qbtypes.Step{Duration: 30 * time.Second},
},
},
{
Type: qbtypes.QueryTypeBuilder,
Spec: qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{
Name: "B",
StepInterval: qbtypes.Step{Duration: 60 * time.Second},
},
},
},
},
},
expected: 30000, // GCD of 30s and 60s
},
{
name: "complex expression",
expression: "(A + B) / C",
req: &qbtypes.QueryRangeRequest{
CompositeQuery: qbtypes.CompositeQuery{
Queries: []qbtypes.QueryEnvelope{
{
Type: qbtypes.QueryTypeBuilder,
Spec: qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{
Name: "A",
StepInterval: qbtypes.Step{Duration: 60 * time.Second},
},
},
{
Type: qbtypes.QueryTypeBuilder,
Spec: qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{
Name: "B",
StepInterval: qbtypes.Step{Duration: 120 * time.Second},
},
},
{
Type: qbtypes.QueryTypeBuilder,
Spec: qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{
Name: "C",
StepInterval: qbtypes.Step{Duration: 180 * time.Second},
},
},
},
},
},
expected: 60000, // GCD of 60s, 120s, and 180s
},
{
name: "no query references",
expression: "100",
req: &qbtypes.QueryRangeRequest{
CompositeQuery: qbtypes.CompositeQuery{
Queries: []qbtypes.QueryEnvelope{},
},
},
expected: 60000, // Default
},
}
q := &querier{}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := q.calculateFormulaStep(tt.expression, tt.req)
if result != tt.expected {
t.Errorf("Expected step %d, got %d", tt.expected, result)
}
})
}
}