637 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			637 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
| package codec
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"reflect"
 | |
| 	"testing"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/google/go-cmp/cmp"
 | |
| 
 | |
| 	"github.com/influxdata/telegraf/plugins/inputs/zipkin/trace"
 | |
| )
 | |
| 
 | |
| func Test_MicroToTime(t *testing.T) {
 | |
| 	type args struct {
 | |
| 		micro int64
 | |
| 	}
 | |
| 	tests := []struct {
 | |
| 		name  string
 | |
| 		micro int64
 | |
| 		want  time.Time
 | |
| 	}{
 | |
| 		{
 | |
| 			name:  "given zero micro seconds expected unix time zero",
 | |
| 			micro: 0,
 | |
| 			want:  time.Unix(0, 0).UTC(),
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "given a million micro seconds expected unix time one",
 | |
| 			micro: 1000000,
 | |
| 			want:  time.Unix(1, 0).UTC(),
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "given a million micro seconds expected unix time one",
 | |
| 			micro: 1503031538791000,
 | |
| 			want:  time.Unix(0, 1503031538791000000).UTC(),
 | |
| 		},
 | |
| 	}
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			if got := MicroToTime(tt.micro); !reflect.DeepEqual(got, tt.want) {
 | |
| 				t.Errorf("microToTime() = %v, want %v", got, tt.want)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func Test_minMax(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name    string
 | |
| 		span    *MockSpan
 | |
| 		now     func() time.Time
 | |
| 		wantMin time.Time
 | |
| 		wantMax time.Time
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "Single annotation",
 | |
| 			span: &MockSpan{
 | |
| 				Anno: []Annotation{
 | |
| 					&MockAnnotation{
 | |
| 						Time: time.Unix(0, 0).UTC().Add(time.Second),
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			wantMin: time.Unix(1, 0).UTC(),
 | |
| 			wantMax: time.Unix(1, 0).UTC(),
 | |
| 		},
 | |
| 		{
 | |
| 			name: "Three annotations",
 | |
| 			span: &MockSpan{
 | |
| 				Anno: []Annotation{
 | |
| 					&MockAnnotation{
 | |
| 						Time: time.Unix(0, 0).UTC().Add(1 * time.Second),
 | |
| 					},
 | |
| 					&MockAnnotation{
 | |
| 						Time: time.Unix(0, 0).UTC().Add(2 * time.Second),
 | |
| 					},
 | |
| 					&MockAnnotation{
 | |
| 						Time: time.Unix(0, 0).UTC().Add(3 * time.Second),
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			wantMin: time.Unix(1, 0).UTC(),
 | |
| 			wantMax: time.Unix(3, 0).UTC(),
 | |
| 		},
 | |
| 		{
 | |
| 			name: "Annotations are in the future",
 | |
| 			span: &MockSpan{
 | |
| 				Anno: []Annotation{
 | |
| 					&MockAnnotation{
 | |
| 						Time: time.Unix(0, 0).UTC().Add(3 * time.Second),
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			wantMin: time.Unix(2, 0).UTC(),
 | |
| 			wantMax: time.Unix(3, 0).UTC(),
 | |
| 			now: func() time.Time {
 | |
| 				return time.Unix(2, 0).UTC()
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "No Annotations",
 | |
| 			span: &MockSpan{
 | |
| 				Anno: []Annotation{},
 | |
| 			},
 | |
| 			wantMin: time.Unix(2, 0).UTC(),
 | |
| 			wantMax: time.Unix(2, 0).UTC(),
 | |
| 			now: func() time.Time {
 | |
| 				return time.Unix(2, 0).UTC()
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			if tt.now != nil {
 | |
| 				now = tt.now
 | |
| 			}
 | |
| 			got, got1 := minMax(tt.span)
 | |
| 			if !reflect.DeepEqual(got, tt.wantMin) {
 | |
| 				t.Errorf("minMax() got = %v, want %v", got, tt.wantMin)
 | |
| 			}
 | |
| 			if !reflect.DeepEqual(got1, tt.wantMax) {
 | |
| 				t.Errorf("minMax() got1 = %v, want %v", got1, tt.wantMax)
 | |
| 			}
 | |
| 			now = time.Now
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func Test_guessTimestamp(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name string
 | |
| 		span Span
 | |
| 		now  func() time.Time
 | |
| 		want time.Time
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "simple timestamp",
 | |
| 			span: &MockSpan{
 | |
| 				Time: time.Unix(2, 0).UTC(),
 | |
| 			},
 | |
| 			want: time.Unix(2, 0).UTC(),
 | |
| 		},
 | |
| 		{
 | |
| 			name: "zero timestamp",
 | |
| 			span: &MockSpan{
 | |
| 				Time: time.Time{},
 | |
| 			},
 | |
| 			now: func() time.Time {
 | |
| 				return time.Unix(2, 0).UTC()
 | |
| 			},
 | |
| 			want: time.Unix(2, 0).UTC(),
 | |
| 		},
 | |
| 		{
 | |
| 			name: "zero timestamp with single annotation",
 | |
| 			span: &MockSpan{
 | |
| 				Time: time.Time{},
 | |
| 				Anno: []Annotation{
 | |
| 					&MockAnnotation{
 | |
| 						Time: time.Unix(0, 0).UTC(),
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			want: time.Unix(0, 0).UTC(),
 | |
| 		},
 | |
| 		{
 | |
| 			name: "zero timestamp with two annotations",
 | |
| 			span: &MockSpan{
 | |
| 				Time: time.Time{},
 | |
| 				Anno: []Annotation{
 | |
| 					&MockAnnotation{
 | |
| 						Time: time.Unix(0, 0).UTC(),
 | |
| 					},
 | |
| 					&MockAnnotation{
 | |
| 						Time: time.Unix(2, 0).UTC(),
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			want: time.Unix(0, 0).UTC(),
 | |
| 		},
 | |
| 	}
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			if tt.now != nil {
 | |
| 				now = tt.now
 | |
| 			}
 | |
| 			if got := guessTimestamp(tt.span); !reflect.DeepEqual(got, tt.want) {
 | |
| 				t.Errorf("guessTimestamp() = %v, want %v", got, tt.want)
 | |
| 			}
 | |
| 			now = time.Now
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func Test_convertDuration(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name string
 | |
| 		span Span
 | |
| 		want time.Duration
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "simple duration",
 | |
| 			span: &MockSpan{
 | |
| 				Dur: time.Hour,
 | |
| 			},
 | |
| 			want: time.Hour,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "no timestamp, but, 2 seconds between annotations",
 | |
| 			span: &MockSpan{
 | |
| 				Anno: []Annotation{
 | |
| 					&MockAnnotation{
 | |
| 						Time: time.Unix(0, 0).UTC().Add(1 * time.Second),
 | |
| 					},
 | |
| 					&MockAnnotation{
 | |
| 						Time: time.Unix(0, 0).UTC().Add(2 * time.Second),
 | |
| 					},
 | |
| 					&MockAnnotation{
 | |
| 						Time: time.Unix(0, 0).UTC().Add(3 * time.Second),
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			want: 2 * time.Second,
 | |
| 		},
 | |
| 	}
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			if got := convertDuration(tt.span); got != tt.want {
 | |
| 				t.Errorf("convertDuration() = %v, want %v", got, tt.want)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func Test_parentID(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name    string
 | |
| 		span    Span
 | |
| 		want    string
 | |
| 		wantErr bool
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "has parent id",
 | |
| 			span: &MockSpan{
 | |
| 				ParentID: "6b221d5bc9e6496c",
 | |
| 			},
 | |
| 			want: "6b221d5bc9e6496c",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "no parent, so use id",
 | |
| 			span: &MockSpan{
 | |
| 				ID: "abceasyas123",
 | |
| 			},
 | |
| 			want: "abceasyas123",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "bad parent value",
 | |
| 			span: &MockSpan{
 | |
| 				Error: fmt.Errorf("Mommie Dearest"),
 | |
| 			},
 | |
| 			wantErr: true,
 | |
| 		},
 | |
| 	}
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			got, err := parentID(tt.span)
 | |
| 			if (err != nil) != tt.wantErr {
 | |
| 				t.Errorf("parentID() error = %v, wantErr %v", err, tt.wantErr)
 | |
| 				return
 | |
| 			}
 | |
| 			if got != tt.want {
 | |
| 				t.Errorf("parentID() = %v, want %v", got, tt.want)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func Test_serviceEndpoint(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name string
 | |
| 		ann  []Annotation
 | |
| 		bann []BinaryAnnotation
 | |
| 		want Endpoint
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "Annotation with server receive",
 | |
| 			ann: []Annotation{
 | |
| 				&MockAnnotation{
 | |
| 					Val: "battery",
 | |
| 					H: &MockEndpoint{
 | |
| 						name: "aa",
 | |
| 					},
 | |
| 				},
 | |
| 				&MockAnnotation{
 | |
| 					Val: "sr",
 | |
| 					H: &MockEndpoint{
 | |
| 						name: "me",
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			want: &MockEndpoint{
 | |
| 				name: "me",
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "Annotation with no standard values",
 | |
| 			ann: []Annotation{
 | |
| 				&MockAnnotation{
 | |
| 					Val: "noop",
 | |
| 				},
 | |
| 				&MockAnnotation{
 | |
| 					Val: "aa",
 | |
| 					H: &MockEndpoint{
 | |
| 						name: "battery",
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			want: &MockEndpoint{
 | |
| 				name: "battery",
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "Annotation with no endpoints",
 | |
| 			ann: []Annotation{
 | |
| 				&MockAnnotation{
 | |
| 					Val: "noop",
 | |
| 				},
 | |
| 			},
 | |
| 			want: &DefaultEndpoint{},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "Binary annotation with local component",
 | |
| 			bann: []BinaryAnnotation{
 | |
| 				&MockBinaryAnnotation{
 | |
| 					K: "noop",
 | |
| 					H: &MockEndpoint{
 | |
| 						name: "aa",
 | |
| 					},
 | |
| 				},
 | |
| 				&MockBinaryAnnotation{
 | |
| 					K: "lc",
 | |
| 					H: &MockEndpoint{
 | |
| 						name: "me",
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			want: &MockEndpoint{
 | |
| 				name: "me",
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			if got := serviceEndpoint(tt.ann, tt.bann); !reflect.DeepEqual(got, tt.want) {
 | |
| 				t.Errorf("serviceEndpoint() = %v, want %v", got, tt.want)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestNewBinaryAnnotations(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name        string
 | |
| 		annotations []BinaryAnnotation
 | |
| 		endpoint    Endpoint
 | |
| 		want        []trace.BinaryAnnotation
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "Should override annotation with endpoint",
 | |
| 			annotations: []BinaryAnnotation{
 | |
| 				&MockBinaryAnnotation{
 | |
| 					K: "mykey",
 | |
| 					V: "myvalue",
 | |
| 					H: &MockEndpoint{
 | |
| 						host: "noop",
 | |
| 						name: "noop",
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			endpoint: &MockEndpoint{
 | |
| 				host: "myhost",
 | |
| 				name: "myservice",
 | |
| 			},
 | |
| 			want: []trace.BinaryAnnotation{
 | |
| 				{
 | |
| 					Host:        "myhost",
 | |
| 					ServiceName: "myservice",
 | |
| 					Key:         "mykey",
 | |
| 					Value:       "myvalue",
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			if got := NewBinaryAnnotations(tt.annotations, tt.endpoint); !reflect.DeepEqual(got, tt.want) {
 | |
| 				t.Errorf("NewBinaryAnnotations() = %v, want %v", got, tt.want)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestNewAnnotations(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name        string
 | |
| 		annotations []Annotation
 | |
| 		endpoint    Endpoint
 | |
| 		want        []trace.Annotation
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "Should override annotation with endpoint",
 | |
| 			annotations: []Annotation{
 | |
| 				&MockAnnotation{
 | |
| 					Time: time.Unix(0, 0).UTC(),
 | |
| 					Val:  "myvalue",
 | |
| 					H: &MockEndpoint{
 | |
| 						host: "noop",
 | |
| 						name: "noop",
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			endpoint: &MockEndpoint{
 | |
| 				host: "myhost",
 | |
| 				name: "myservice",
 | |
| 			},
 | |
| 			want: []trace.Annotation{
 | |
| 				{
 | |
| 					Host:        "myhost",
 | |
| 					ServiceName: "myservice",
 | |
| 					Timestamp:   time.Unix(0, 0).UTC(),
 | |
| 					Value:       "myvalue",
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			if got := NewAnnotations(tt.annotations, tt.endpoint); !reflect.DeepEqual(got, tt.want) {
 | |
| 				t.Errorf("NewAnnotations() = %v, want %v", got, tt.want)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestNewTrace(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name    string
 | |
| 		spans   []Span
 | |
| 		now     func() time.Time
 | |
| 		want    trace.Trace
 | |
| 		wantErr bool
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "empty span",
 | |
| 			spans: []Span{
 | |
| 				&MockSpan{},
 | |
| 			},
 | |
| 			now: func() time.Time {
 | |
| 				return time.Unix(0, 0).UTC()
 | |
| 			},
 | |
| 			want: trace.Trace{
 | |
| 				trace.Span{
 | |
| 					ServiceName:       "unknown",
 | |
| 					Timestamp:         time.Unix(0, 0).UTC(),
 | |
| 					Annotations:       []trace.Annotation{},
 | |
| 					BinaryAnnotations: []trace.BinaryAnnotation{},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "span has no id",
 | |
| 			spans: []Span{
 | |
| 				&MockSpan{
 | |
| 					Error: fmt.Errorf("Span has no id"),
 | |
| 				},
 | |
| 			},
 | |
| 			wantErr: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "complete span",
 | |
| 			spans: []Span{
 | |
| 				&MockSpan{
 | |
| 					TraceID:     "tid",
 | |
| 					ID:          "id",
 | |
| 					ParentID:    "",
 | |
| 					ServiceName: "me",
 | |
| 					Anno: []Annotation{
 | |
| 						&MockAnnotation{
 | |
| 							Time: time.Unix(1, 0).UTC(),
 | |
| 							Val:  "myval",
 | |
| 							H: &MockEndpoint{
 | |
| 								host: "myhost",
 | |
| 								name: "myname",
 | |
| 							},
 | |
| 						},
 | |
| 					},
 | |
| 					Time: time.Unix(0, 0).UTC(),
 | |
| 					Dur:  2 * time.Second,
 | |
| 				},
 | |
| 			},
 | |
| 			now: func() time.Time {
 | |
| 				return time.Unix(0, 0).UTC()
 | |
| 			},
 | |
| 			want: trace.Trace{
 | |
| 				trace.Span{
 | |
| 					ID:          "id",
 | |
| 					ParentID:    "id",
 | |
| 					TraceID:     "tid",
 | |
| 					Name:        "me",
 | |
| 					ServiceName: "myname",
 | |
| 					Timestamp:   time.Unix(0, 0).UTC(),
 | |
| 					Duration:    2 * time.Second,
 | |
| 					Annotations: []trace.Annotation{
 | |
| 						{
 | |
| 							Timestamp:   time.Unix(1, 0).UTC(),
 | |
| 							Value:       "myval",
 | |
| 							Host:        "myhost",
 | |
| 							ServiceName: "myname",
 | |
| 						},
 | |
| 					},
 | |
| 					BinaryAnnotations: []trace.BinaryAnnotation{},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			if tt.now != nil {
 | |
| 				now = tt.now
 | |
| 			}
 | |
| 			got, err := NewTrace(tt.spans)
 | |
| 			if (err != nil) != tt.wantErr {
 | |
| 				t.Errorf("NewTrace() error = %v, wantErr %v", err, tt.wantErr)
 | |
| 				return
 | |
| 			}
 | |
| 			if !cmp.Equal(tt.want, got) {
 | |
| 				t.Errorf("NewTrace() = %s", cmp.Diff(tt.want, got))
 | |
| 			}
 | |
| 			now = time.Now
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type MockSpan struct {
 | |
| 	TraceID     string
 | |
| 	ID          string
 | |
| 	ParentID    string
 | |
| 	ServiceName string
 | |
| 	Anno        []Annotation
 | |
| 	BinAnno     []BinaryAnnotation
 | |
| 	Time        time.Time
 | |
| 	Dur         time.Duration
 | |
| 	Error       error
 | |
| }
 | |
| 
 | |
| func (m *MockSpan) Trace() (string, error) {
 | |
| 	return m.TraceID, m.Error
 | |
| }
 | |
| 
 | |
| func (m *MockSpan) SpanID() (string, error) {
 | |
| 	return m.ID, m.Error
 | |
| }
 | |
| 
 | |
| func (m *MockSpan) Parent() (string, error) {
 | |
| 	return m.ParentID, m.Error
 | |
| }
 | |
| 
 | |
| func (m *MockSpan) Name() string {
 | |
| 	return m.ServiceName
 | |
| }
 | |
| 
 | |
| func (m *MockSpan) Annotations() []Annotation {
 | |
| 	return m.Anno
 | |
| }
 | |
| 
 | |
| func (m *MockSpan) BinaryAnnotations() ([]BinaryAnnotation, error) {
 | |
| 	return m.BinAnno, m.Error
 | |
| }
 | |
| 
 | |
| func (m *MockSpan) Timestamp() time.Time {
 | |
| 	return m.Time
 | |
| }
 | |
| 
 | |
| func (m *MockSpan) Duration() time.Duration {
 | |
| 	return m.Dur
 | |
| }
 | |
| 
 | |
| type MockAnnotation struct {
 | |
| 	Time time.Time
 | |
| 	Val  string
 | |
| 	H    Endpoint
 | |
| }
 | |
| 
 | |
| func (m *MockAnnotation) Timestamp() time.Time {
 | |
| 	return m.Time
 | |
| }
 | |
| 
 | |
| func (m *MockAnnotation) Value() string {
 | |
| 	return m.Val
 | |
| }
 | |
| 
 | |
| func (m *MockAnnotation) Host() Endpoint {
 | |
| 	return m.H
 | |
| }
 | |
| 
 | |
| type MockEndpoint struct {
 | |
| 	host string
 | |
| 	name string
 | |
| }
 | |
| 
 | |
| func (e *MockEndpoint) Host() string {
 | |
| 	return e.host
 | |
| }
 | |
| 
 | |
| func (e *MockEndpoint) Name() string {
 | |
| 	return e.name
 | |
| }
 | |
| 
 | |
| type MockBinaryAnnotation struct {
 | |
| 	Time time.Time
 | |
| 	K    string
 | |
| 	V    string
 | |
| 	H    Endpoint
 | |
| }
 | |
| 
 | |
| func (b *MockBinaryAnnotation) Key() string {
 | |
| 	return b.K
 | |
| }
 | |
| 
 | |
| func (b *MockBinaryAnnotation) Value() string {
 | |
| 	return b.V
 | |
| }
 | |
| 
 | |
| func (b *MockBinaryAnnotation) Host() Endpoint {
 | |
| 	return b.H
 | |
| }
 |