mirror of
https://github.com/SigNoz/signoz.git
synced 2025-12-17 23:47:12 +00:00
271 lines
7.8 KiB
Go
271 lines
7.8 KiB
Go
|
|
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
|
||
|
|
}
|