package zipkin import ( "reflect" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/testutil" "github.com/openzipkin/zipkin-go-opentracing/_thrift/gen-go/zipkincore" ) func TestLineProtocolConverter_Record(t *testing.T) { mockAcc := testutil.Accumulator{} type fields struct { acc telegraf.Accumulator } type args struct { t Trace } tests := []struct { name string fields fields args args wantErr bool want []testutil.Metric }{ { name: "threespan", fields: fields{ acc: &mockAcc, }, args: args{ t: Trace{ Span{ ID: "8090652509916334619", TraceID: "2505404965370368069", Name: "Child", ParentID: "22964302721410078", Timestamp: time.Unix(0, 1498688360851331000).UTC(), Duration: time.Duration(53106) * time.Microsecond, ServiceName: "trivial", Annotations: []Annotation{}, BinaryAnnotations: []BinaryAnnotation{ BinaryAnnotation{ Key: "lc", Value: "dHJpdmlhbA==", Host: "2130706433:0", ServiceName: "trivial", Type: "STRING", }, }, }, Span{ ID: "103618986556047333", TraceID: "2505404965370368069", Name: "Child", ParentID: "22964302721410078", Timestamp: time.Unix(0, 1498688360904552000).UTC(), Duration: time.Duration(50410) * time.Microsecond, ServiceName: "trivial", Annotations: []Annotation{}, BinaryAnnotations: []BinaryAnnotation{ BinaryAnnotation{ Key: "lc", Value: "dHJpdmlhbA==", Host: "2130706433:0", ServiceName: "trivial", Type: "STRING", }, }, }, Span{ ID: "22964302721410078", TraceID: "2505404965370368069", Name: "Parent", ParentID: "22964302721410078", Timestamp: time.Unix(0, 1498688360851318000).UTC(), Duration: time.Duration(103680) * time.Microsecond, ServiceName: "trivial", Annotations: []Annotation{ Annotation{ Timestamp: time.Unix(0, 1498688360851325000).UTC(), Value: "Starting child #0", Host: "2130706433:0", ServiceName: "trivial", }, Annotation{ Timestamp: time.Unix(0, 1498688360904545000).UTC(), Value: "Starting child #1", Host: "2130706433:0", ServiceName: "trivial", }, Annotation{ Timestamp: time.Unix(0, 1498688360954992000).UTC(), Value: "A Log", Host: "2130706433:0", ServiceName: "trivial", }, }, BinaryAnnotations: []BinaryAnnotation{ BinaryAnnotation{ Key: "lc", Value: "dHJpdmlhbA==", Host: "2130706433:0", ServiceName: "trivial", Type: "STRING", }, }, }, }, }, want: []testutil.Metric{ testutil.Metric{ Measurement: "zipkin", Tags: map[string]string{ "id": "8090652509916334619", "parent_id": "22964302721410078", "trace_id": "2505404965370368069", "service_name": "trivial", "name": "Child", }, Fields: map[string]interface{}{ "duration_ns": (time.Duration(53106) * time.Microsecond).Nanoseconds(), }, Time: time.Unix(0, 1498688360851331000).UTC(), }, testutil.Metric{ Measurement: "zipkin", Tags: map[string]string{ "id": "8090652509916334619", "parent_id": "22964302721410078", "trace_id": "2505404965370368069", "name": "Child", "service_name": "trivial", "annotation": "dHJpdmlhbA==", "endpoint_host": "2130706433:0", "annotation_key": "lc", }, Fields: map[string]interface{}{ "duration_ns": (time.Duration(53106) * time.Microsecond).Nanoseconds(), }, Time: time.Unix(0, 1498688360851331000).UTC(), }, testutil.Metric{ Measurement: "zipkin", Tags: map[string]string{ "id": "103618986556047333", "parent_id": "22964302721410078", "trace_id": "2505404965370368069", "service_name": "trivial", "name": "Child", }, Fields: map[string]interface{}{ "duration_ns": (time.Duration(50410) * time.Microsecond).Nanoseconds(), }, Time: time.Unix(0, 1498688360904552000).UTC(), }, testutil.Metric{ Measurement: "zipkin", Tags: map[string]string{ "id": "103618986556047333", "parent_id": "22964302721410078", "trace_id": "2505404965370368069", "name": "Child", "service_name": "trivial", "annotation": "dHJpdmlhbA==", "endpoint_host": "2130706433:0", "annotation_key": "lc", }, Fields: map[string]interface{}{ "duration_ns": (time.Duration(50410) * time.Microsecond).Nanoseconds(), }, Time: time.Unix(0, 1498688360904552000).UTC(), }, testutil.Metric{ Measurement: "zipkin", Tags: map[string]string{ "id": "22964302721410078", "parent_id": "22964302721410078", "trace_id": "2505404965370368069", "service_name": "trivial", "name": "Parent", }, Fields: map[string]interface{}{ "duration_ns": (time.Duration(103680) * time.Microsecond).Nanoseconds(), }, Time: time.Unix(0, 1498688360851318000).UTC(), }, testutil.Metric{ Measurement: "zipkin", Tags: map[string]string{ "service_name": "trivial", "annotation": "Starting child #0", "endpoint_host": "2130706433:0", "id": "22964302721410078", "parent_id": "22964302721410078", "trace_id": "2505404965370368069", "name": "Parent", }, Fields: map[string]interface{}{ "duration_ns": (time.Duration(103680) * time.Microsecond).Nanoseconds(), }, Time: time.Unix(0, 1498688360851318000).UTC(), }, testutil.Metric{ Measurement: "zipkin", Tags: map[string]string{ "service_name": "trivial", "annotation": "Starting child #1", "endpoint_host": "2130706433:0", "id": "22964302721410078", "parent_id": "22964302721410078", "trace_id": "2505404965370368069", "name": "Parent", }, Fields: map[string]interface{}{ "duration_ns": (time.Duration(103680) * time.Microsecond).Nanoseconds(), }, Time: time.Unix(0, 1498688360851318000).UTC(), }, testutil.Metric{ Measurement: "zipkin", Tags: map[string]string{ "parent_id": "22964302721410078", "trace_id": "2505404965370368069", "name": "Parent", "service_name": "trivial", "annotation": "A Log", "endpoint_host": "2130706433:0", "id": "22964302721410078", }, Fields: map[string]interface{}{ "duration_ns": (time.Duration(103680) * time.Microsecond).Nanoseconds(), }, Time: time.Unix(0, 1498688360851318000).UTC(), }, testutil.Metric{ Measurement: "zipkin", Tags: map[string]string{ "trace_id": "2505404965370368069", "service_name": "trivial", "annotation": "dHJpdmlhbA==", "annotation_key": "lc", "id": "22964302721410078", "parent_id": "22964302721410078", "name": "Parent", "endpoint_host": "2130706433:0", }, Fields: map[string]interface{}{ "duration_ns": (time.Duration(103680) * time.Microsecond).Nanoseconds(), }, Time: time.Unix(0, 1498688360851318000).UTC(), }, }, wantErr: false, }, //// Test data from distributed trace repo sample json // https://github.com/mattkanwisher/distributedtrace/blob/master/testclient/sample.json { name: "distributed_trace_sample", fields: fields{ acc: &mockAcc, }, args: args{ t: Trace{ Span{ ID: "6802735349851856000", TraceID: "0:6802735349851856000", Name: "main.dud", ParentID: "6802735349851856000", Timestamp: time.Unix(1, 0).UTC(), Duration: 1, ServiceName: "trivial", Annotations: []Annotation{ Annotation{ Timestamp: time.Unix(0, 1433330263415871000).UTC(), Value: "cs", Host: "0:9410", ServiceName: "go-zipkin-testclient", }, }, BinaryAnnotations: []BinaryAnnotation{}, }, }, }, want: []testutil.Metric{ testutil.Metric{ Measurement: "zipkin", Tags: map[string]string{ "id": "6802735349851856000", "parent_id": "6802735349851856000", "trace_id": "0:6802735349851856000", "name": "main.dud", "service_name": "trivial", }, Fields: map[string]interface{}{ "duration_ns": (time.Duration(1) * time.Nanosecond).Nanoseconds(), }, Time: time.Unix(1, 0).UTC(), }, testutil.Metric{ Measurement: "zipkin", Tags: map[string]string{ "annotation": "cs", "endpoint_host": "0:9410", "id": "6802735349851856000", "parent_id": "6802735349851856000", "trace_id": "0:6802735349851856000", "name": "main.dud", "service_name": "go-zipkin-testclient", }, Fields: map[string]interface{}{ "duration_ns": (time.Duration(1) * time.Nanosecond).Nanoseconds(), }, Time: time.Unix(1, 0).UTC(), }, }, }, } for i, tt := range tests { t.Run(tt.name, func(t *testing.T) { mockAcc.ClearMetrics() l := &LineProtocolConverter{ acc: tt.fields.acc, } if err := l.Record(tt.args.t); (err != nil) != tt.wantErr { t.Errorf("LineProtocolConverter.Record() error = %v, wantErr %v", err, tt.wantErr) } got := []testutil.Metric{} for _, metric := range mockAcc.Metrics { got = append(got, *metric) } if !cmp.Equal(got, tt.want) { t.Errorf("LineProtocolConverter.Record()/%s/%d error = %s ", tt.name, i, cmp.Diff(got, tt.want)) } }) } } func Test_microToTime(t *testing.T) { type args struct { micro int64 } tests := []struct { name string args args want time.Time }{ { name: "given zero micro seconds expected unix time zero", args: args{ micro: 0, }, want: time.Unix(0, 0).UTC(), }, { name: "given a million micro seconds expected unix time one", args: args{ micro: 1000000, }, want: time.Unix(1, 0).UTC(), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := microToTime(tt.args.micro); !reflect.DeepEqual(got, tt.want) { t.Errorf("microToTime() = %v, want %v", got, tt.want) } }) } } func newAnnotation(micro int64) *zipkincore.Annotation { return &zipkincore.Annotation{ Timestamp: micro, } } func Test_minMax(t *testing.T) { type args struct { span *zipkincore.Span } tests := []struct { name string args args now func() time.Time wantMin time.Time wantMax time.Time }{ { name: "Single annotation", args: args{ span: &zipkincore.Span{ Annotations: []*zipkincore.Annotation{ newAnnotation(1000000), }, }, }, wantMin: time.Unix(1, 0).UTC(), wantMax: time.Unix(1, 0).UTC(), }, { name: "Three annotations", args: args{ span: &zipkincore.Span{ Annotations: []*zipkincore.Annotation{ newAnnotation(1000000), newAnnotation(2000000), newAnnotation(3000000), }, }, }, wantMin: time.Unix(1, 0).UTC(), wantMax: time.Unix(3, 0).UTC(), }, { name: "Annotations are in the future", args: args{ span: &zipkincore.Span{ Annotations: []*zipkincore.Annotation{ newAnnotation(3000000), }, }, }, 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", args: args{ span: &zipkincore.Span{ Annotations: []*zipkincore.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.args.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_host(t *testing.T) { type args struct { h *zipkincore.Endpoint } tests := []struct { name string args args want string }{ { name: "Host Found", args: args{ h: &zipkincore.Endpoint{ Ipv4: 1234, Port: 8888, }, }, want: "0.0.4.210:8888", }, { name: "No Host", args: args{ h: nil, }, want: "0.0.0.0", }, { name: "int overflow zipkin uses an int16 type as an unsigned int 16.", args: args{ h: &zipkincore.Endpoint{ Ipv4: 1234, Port: -1, }, }, want: "0.0.4.210:65535", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := host(tt.args.h); got != tt.want { t.Errorf("host() = %v, want %v", got, tt.want) } }) } } func Test_serviceName(t *testing.T) { type args struct { h *zipkincore.Endpoint } tests := []struct { name string args args want string }{ { name: "Found ServiceName", args: args{ h: &zipkincore.Endpoint{ ServiceName: "zipkin", }, }, want: "zipkin", }, { name: "No ServiceName", args: args{ h: nil, }, want: "unknown", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := serviceName(tt.args.h); got != tt.want { t.Errorf("serviceName() = %v, want %v", got, tt.want) } }) } }