diff --git a/pkg/modules/tracefunnel/utils_test.go b/pkg/modules/tracefunnel/utils_test.go new file mode 100644 index 0000000000..d9f8f87872 --- /dev/null +++ b/pkg/modules/tracefunnel/utils_test.go @@ -0,0 +1,335 @@ +package tracefunnel + +import ( + "testing" + "time" + + tracefunnel "github.com/SigNoz/signoz/pkg/types/tracefunnel" + "github.com/SigNoz/signoz/pkg/valuer" + "github.com/stretchr/testify/assert" +) + +func TestValidateTimestamp(t *testing.T) { + tests := []struct { + name string + timestamp int64 + fieldName string + expectError bool + }{ + { + name: "valid timestamp", + timestamp: time.Now().UnixMilli(), + fieldName: "timestamp", + expectError: false, + }, + { + name: "zero timestamp", + timestamp: 0, + fieldName: "timestamp", + expectError: true, + }, + { + name: "negative timestamp", + timestamp: -1, + fieldName: "timestamp", + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := ValidateTimestamp(tt.timestamp, tt.fieldName) + if tt.expectError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestValidateTimestampIsMilliseconds(t *testing.T) { + tests := []struct { + name string + timestamp int64 + expected bool + }{ + { + name: "valid millisecond timestamp", + timestamp: 1700000000000, // 2023-11-14 12:00:00 UTC + expected: true, + }, + { + name: "too small timestamp", + timestamp: 999999999999, + expected: false, + }, + { + name: "too large timestamp", + timestamp: 10000000000000, + expected: false, + }, + { + name: "second precision timestamp", + timestamp: 1700000000, + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := ValidateTimestampIsMilliseconds(tt.timestamp) + assert.Equal(t, tt.expected, result) + }) + } +} + +func TestValidateFunnelSteps(t *testing.T) { + tests := []struct { + name string + steps []tracefunnel.FunnelStep + expectError bool + }{ + { + name: "valid steps", + steps: []tracefunnel.FunnelStep{ + { + Id: valuer.GenerateUUID(), + Name: "Step 1", + ServiceName: "test-service", + SpanName: "test-span", + Order: 1, + }, + { + Id: valuer.GenerateUUID(), + Name: "Step 2", + ServiceName: "test-service", + SpanName: "test-span-2", + Order: 2, + }, + }, + expectError: false, + }, + { + name: "too few steps", + steps: []tracefunnel.FunnelStep{ + { + Id: valuer.GenerateUUID(), + Name: "Step 1", + ServiceName: "test-service", + SpanName: "test-span", + Order: 1, + }, + }, + expectError: true, + }, + { + name: "missing service name", + steps: []tracefunnel.FunnelStep{ + { + Id: valuer.GenerateUUID(), + Name: "Step 1", + SpanName: "test-span", + Order: 1, + }, + { + Id: valuer.GenerateUUID(), + Name: "Step 2", + ServiceName: "test-service", + SpanName: "test-span-2", + Order: 2, + }, + }, + expectError: true, + }, + { + name: "missing span name", + steps: []tracefunnel.FunnelStep{ + { + Id: valuer.GenerateUUID(), + Name: "Step 1", + ServiceName: "test-service", + Order: 1, + }, + { + Id: valuer.GenerateUUID(), + Name: "Step 2", + ServiceName: "test-service", + SpanName: "test-span-2", + Order: 2, + }, + }, + expectError: true, + }, + { + name: "negative order", + steps: []tracefunnel.FunnelStep{ + { + Id: valuer.GenerateUUID(), + Name: "Step 1", + ServiceName: "test-service", + SpanName: "test-span", + Order: -1, + }, + { + Id: valuer.GenerateUUID(), + Name: "Step 2", + ServiceName: "test-service", + SpanName: "test-span-2", + Order: 2, + }, + }, + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := ValidateFunnelSteps(tt.steps) + if tt.expectError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestNormalizeFunnelSteps(t *testing.T) { + tests := []struct { + name string + steps []tracefunnel.FunnelStep + expected []tracefunnel.FunnelStep + }{ + { + name: "already normalized steps", + steps: []tracefunnel.FunnelStep{ + { + Id: valuer.GenerateUUID(), + Name: "Step 1", + ServiceName: "test-service", + SpanName: "test-span", + Order: 1, + }, + { + Id: valuer.GenerateUUID(), + Name: "Step 2", + ServiceName: "test-service", + SpanName: "test-span-2", + Order: 2, + }, + }, + expected: []tracefunnel.FunnelStep{ + { + Name: "Step 1", + ServiceName: "test-service", + SpanName: "test-span", + Order: 1, + }, + { + Name: "Step 2", + ServiceName: "test-service", + SpanName: "test-span-2", + Order: 2, + }, + }, + }, + { + name: "unordered steps", + steps: []tracefunnel.FunnelStep{ + { + Id: valuer.GenerateUUID(), + Name: "Step 2", + ServiceName: "test-service", + SpanName: "test-span-2", + Order: 2, + }, + { + Id: valuer.GenerateUUID(), + Name: "Step 1", + ServiceName: "test-service", + SpanName: "test-span", + Order: 1, + }, + }, + expected: []tracefunnel.FunnelStep{ + { + Name: "Step 1", + ServiceName: "test-service", + SpanName: "test-span", + Order: 1, + }, + { + Name: "Step 2", + ServiceName: "test-service", + SpanName: "test-span-2", + Order: 2, + }, + }, + }, + { + name: "steps with gaps in order", + steps: []tracefunnel.FunnelStep{ + { + Id: valuer.GenerateUUID(), + Name: "Step 1", + ServiceName: "test-service", + SpanName: "test-span", + Order: 1, + }, + { + Id: valuer.GenerateUUID(), + Name: "Step 3", + ServiceName: "test-service", + SpanName: "test-span-3", + Order: 3, + }, + { + Id: valuer.GenerateUUID(), + Name: "Step 2", + ServiceName: "test-service", + SpanName: "test-span-2", + Order: 2, + }, + }, + expected: []tracefunnel.FunnelStep{ + { + Name: "Step 1", + ServiceName: "test-service", + SpanName: "test-span", + Order: 1, + }, + { + Name: "Step 2", + ServiceName: "test-service", + SpanName: "test-span-2", + Order: 2, + }, + { + Name: "Step 3", + ServiceName: "test-service", + SpanName: "test-span-3", + Order: 3, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Make a copy of the steps to avoid modifying the original + steps := make([]tracefunnel.FunnelStep, len(tt.steps)) + copy(steps, tt.steps) + + result := NormalizeFunnelSteps(steps) + + // Compare only the relevant fields + for i := range result { + assert.Equal(t, tt.expected[i].Name, result[i].Name) + assert.Equal(t, tt.expected[i].ServiceName, result[i].ServiceName) + assert.Equal(t, tt.expected[i].SpanName, result[i].SpanName) + assert.Equal(t, tt.expected[i].Order, result[i].Order) + } + }) + } +}