package tracefunnel import ( "testing" tracev4 "github.com/SigNoz/signoz/pkg/query-service/app/traces/v4" v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3" "github.com/SigNoz/signoz/pkg/types/tracefunneltypes" ) func TestValidateTracesMultipleSteps(t *testing.T) { tests := []struct { name string funnel *tracefunneltypes.StorableFunnel timeRange tracefunneltypes.TimeRange expectError bool }{ { name: "multi step funnel validation (4 steps)", funnel: &tracefunneltypes.StorableFunnel{ Steps: []*tracefunneltypes.FunnelStep{ { ServiceName: "service1", SpanName: "span1", HasErrors: false, Filters: &v3.FilterSet{}, }, { ServiceName: "service2", SpanName: "span2", HasErrors: true, Filters: &v3.FilterSet{}, }, { ServiceName: "service3", SpanName: "span3", HasErrors: false, Filters: &v3.FilterSet{}, }, { ServiceName: "service4", SpanName: "span4", HasErrors: true, Filters: &v3.FilterSet{}, }, }, }, timeRange: tracefunneltypes.TimeRange{ StartTime: 1000000000, EndTime: 2000000000, }, expectError: false, }, { name: "single step funnel validation (1 step)", funnel: &tracefunneltypes.StorableFunnel{ Steps: []*tracefunneltypes.FunnelStep{ { ServiceName: "service1", SpanName: "span1", HasErrors: false, Filters: &v3.FilterSet{}, }, }, }, timeRange: tracefunneltypes.TimeRange{ StartTime: 1000000000, EndTime: 2000000000, }, expectError: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := ValidateTraces(tt.funnel, tt.timeRange) if tt.expectError && err == nil { t.Errorf("ValidateTraces() expected error but got none") } if !tt.expectError && err != nil { t.Errorf("ValidateTraces() unexpected error: %v", err) } if !tt.expectError && result == nil { t.Errorf("ValidateTraces() expected result but got nil") } if !tt.expectError && result != nil && result.Query == "" { t.Errorf("ValidateTraces() expected non-empty query") } }) } } func TestGetFunnelAnalyticsMultipleSteps(t *testing.T) { tests := []struct { name string funnel *tracefunneltypes.StorableFunnel timeRange tracefunneltypes.TimeRange expectError bool }{ { name: "multi step funnel analytics (5 steps)", funnel: &tracefunneltypes.StorableFunnel{ Steps: []*tracefunneltypes.FunnelStep{ { ServiceName: "service1", SpanName: "span1", HasErrors: false, LatencyPointer: "start", Filters: &v3.FilterSet{}, }, { ServiceName: "service2", SpanName: "span2", HasErrors: true, LatencyPointer: "end", Filters: &v3.FilterSet{}, }, { ServiceName: "service3", SpanName: "span3", HasErrors: false, LatencyPointer: "start", Filters: &v3.FilterSet{}, }, { ServiceName: "service4", SpanName: "span4", HasErrors: false, LatencyPointer: "end", Filters: &v3.FilterSet{}, }, { ServiceName: "service5", SpanName: "span5", HasErrors: true, LatencyPointer: "start", Filters: &v3.FilterSet{}, }, }, }, timeRange: tracefunneltypes.TimeRange{ StartTime: 1000000000, EndTime: 2000000000, }, expectError: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := GetFunnelAnalytics(tt.funnel, tt.timeRange) if tt.expectError && err == nil { t.Errorf("GetFunnelAnalytics() expected error but got none") } if !tt.expectError && err != nil { t.Errorf("GetFunnelAnalytics() unexpected error: %v", err) } if !tt.expectError && result == nil { t.Errorf("GetFunnelAnalytics() expected result but got nil") } if !tt.expectError && result != nil && result.Query == "" { t.Errorf("GetFunnelAnalytics() expected non-empty query") } }) } } func TestGetStepAnalyticsMultipleSteps(t *testing.T) { funnel := &tracefunneltypes.StorableFunnel{ Steps: []*tracefunneltypes.FunnelStep{ {ServiceName: "s1", SpanName: "sp1", HasErrors: false, Filters: &v3.FilterSet{}}, {ServiceName: "s2", SpanName: "sp2", HasErrors: true, Filters: &v3.FilterSet{}}, {ServiceName: "s3", SpanName: "sp3", HasErrors: false, Filters: &v3.FilterSet{}}, {ServiceName: "s4", SpanName: "sp4", HasErrors: false, Filters: &v3.FilterSet{}}, {ServiceName: "s5", SpanName: "sp5", HasErrors: true, Filters: &v3.FilterSet{}}, }, } timeRange := tracefunneltypes.TimeRange{ StartTime: 1000000000, EndTime: 2000000000, } result, err := GetStepAnalytics(funnel, timeRange) if err != nil { t.Errorf("GetStepAnalytics() unexpected error: %v", err) } if result == nil { t.Errorf("GetStepAnalytics() expected result but got nil") } if result != nil && result.Query == "" { t.Errorf("GetStepAnalytics() expected non-empty query") } } func TestGetFunnelStepAnalyticsMultipleSteps(t *testing.T) { tests := []struct { name string funnel *tracefunneltypes.StorableFunnel timeRange tracefunneltypes.TimeRange stepStart int64 stepEnd int64 expectError bool }{ { name: "step 2 to 4 in 6-step funnel", funnel: &tracefunneltypes.StorableFunnel{ Steps: []*tracefunneltypes.FunnelStep{ {ServiceName: "s1", SpanName: "sp1", HasErrors: false, LatencyPointer: "start", LatencyType: "", Filters: &v3.FilterSet{}}, {ServiceName: "s2", SpanName: "sp2", HasErrors: false, LatencyPointer: "start", LatencyType: "p90", Filters: &v3.FilterSet{}}, {ServiceName: "s3", SpanName: "sp3", HasErrors: true, LatencyPointer: "end", LatencyType: "", Filters: &v3.FilterSet{}}, {ServiceName: "s4", SpanName: "sp4", HasErrors: false, LatencyPointer: "start", LatencyType: "p95", Filters: &v3.FilterSet{}}, {ServiceName: "s5", SpanName: "sp5", HasErrors: false, LatencyPointer: "end", LatencyType: "", Filters: &v3.FilterSet{}}, {ServiceName: "s6", SpanName: "sp6", HasErrors: true, LatencyPointer: "start", LatencyType: "", Filters: &v3.FilterSet{}}, }, }, timeRange: tracefunneltypes.TimeRange{ StartTime: 1000000000, EndTime: 2000000000, }, stepStart: 2, stepEnd: 4, expectError: false, }, { name: "invalid same step range", funnel: &tracefunneltypes.StorableFunnel{ Steps: []*tracefunneltypes.FunnelStep{ {ServiceName: "s1", SpanName: "sp1", HasErrors: false, LatencyPointer: "start", Filters: &v3.FilterSet{}}, {ServiceName: "s2", SpanName: "sp2", HasErrors: false, LatencyPointer: "start", Filters: &v3.FilterSet{}}, }, }, timeRange: tracefunneltypes.TimeRange{ StartTime: 1000000000, EndTime: 2000000000, }, stepStart: 1, stepEnd: 1, expectError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := GetFunnelStepAnalytics(tt.funnel, tt.timeRange, tt.stepStart, tt.stepEnd) if tt.expectError && err == nil { t.Errorf("GetFunnelStepAnalytics() expected error but got none") } if !tt.expectError && err != nil { t.Errorf("GetFunnelStepAnalytics() unexpected error: %v", err) } if !tt.expectError && result == nil { t.Errorf("GetFunnelStepAnalytics() expected result but got nil") } if !tt.expectError && result != nil && result.Query == "" { t.Errorf("GetFunnelStepAnalytics() expected non-empty query") } }) } } // Mock the tracev4.BuildTracesFilterQuery function since it's external func init() { // This would normally be handled by the actual implementation // For testing purposes, we'll assume it returns an empty string _ = tracev4.BuildTracesFilterQuery }