1// Copyright The OpenTelemetry Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package zipkinv2 16 17import ( 18 "testing" 19 "time" 20 21 zipkinmodel "github.com/openzipkin/zipkin-go/model" 22 "github.com/stretchr/testify/assert" 23 24 "go.opentelemetry.io/collector/model/pdata" 25 "go.opentelemetry.io/collector/translator/conventions" 26 "go.opentelemetry.io/collector/translator/trace/internal/zipkin" 27) 28 29func TestZipkinSpansToInternalTraces(t *testing.T) { 30 tests := []struct { 31 name string 32 zs []*zipkinmodel.SpanModel 33 td pdata.Traces 34 err error 35 }{ 36 { 37 name: "empty", 38 zs: make([]*zipkinmodel.SpanModel, 0), 39 td: pdata.NewTraces(), 40 err: nil, 41 }, 42 { 43 name: "minimalSpan", 44 zs: generateSpanNoEndpoints(), 45 td: generateTraceSingleSpanNoResourceOrInstrLibrary(), 46 err: nil, 47 }, 48 { 49 name: "onlyLocalEndpointSpan", 50 zs: generateSpanNoTags(), 51 td: generateTraceSingleSpanMinmalResource(), 52 err: nil, 53 }, 54 { 55 name: "errorTag", 56 zs: generateSpanErrorTags(), 57 td: generateTraceSingleSpanErrorStatus(), 58 err: nil, 59 }, 60 } 61 for _, test := range tests { 62 t.Run(test.name, func(t *testing.T) { 63 td, err := ToTranslator{}.ToTraces(test.zs) 64 assert.EqualValues(t, test.err, err) 65 if test.name != "nilSpan" { 66 assert.Equal(t, len(test.zs), td.SpanCount()) 67 } 68 assert.EqualValues(t, test.td, td) 69 }) 70 } 71} 72 73func generateSpanNoEndpoints() []*zipkinmodel.SpanModel { 74 spans := make([]*zipkinmodel.SpanModel, 1) 75 spans[0] = &zipkinmodel.SpanModel{ 76 SpanContext: zipkinmodel.SpanContext{ 77 TraceID: convertTraceID( 78 pdata.NewTraceID([16]byte{0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0x80})), 79 ID: convertSpanID(pdata.NewSpanID([8]byte{0xAF, 0xAE, 0xAD, 0xAC, 0xAB, 0xAA, 0xA9, 0xA8})), 80 }, 81 Name: "MinimalData", 82 Kind: zipkinmodel.Client, 83 Timestamp: time.Unix(1596911098, 294000000), 84 Duration: 1000000, 85 Shared: false, 86 LocalEndpoint: nil, 87 RemoteEndpoint: nil, 88 Annotations: nil, 89 Tags: nil, 90 } 91 return spans 92} 93 94func generateSpanNoTags() []*zipkinmodel.SpanModel { 95 spans := generateSpanNoEndpoints() 96 spans[0].LocalEndpoint = &zipkinmodel.Endpoint{ServiceName: "SoleAttr"} 97 return spans 98} 99 100func generateSpanErrorTags() []*zipkinmodel.SpanModel { 101 errorTags := make(map[string]string) 102 errorTags["error"] = "true" 103 104 spans := generateSpanNoEndpoints() 105 spans[0].Tags = errorTags 106 return spans 107} 108 109func generateTraceSingleSpanNoResourceOrInstrLibrary() pdata.Traces { 110 td := pdata.NewTraces() 111 span := td.ResourceSpans().AppendEmpty().InstrumentationLibrarySpans().AppendEmpty().Spans().AppendEmpty() 112 span.SetTraceID( 113 pdata.NewTraceID([16]byte{0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0x80})) 114 span.SetSpanID(pdata.NewSpanID([8]byte{0xAF, 0xAE, 0xAD, 0xAC, 0xAB, 0xAA, 0xA9, 0xA8})) 115 span.SetName("MinimalData") 116 span.SetKind(pdata.SpanKindClient) 117 span.SetStartTimestamp(1596911098294000000) 118 span.SetEndTimestamp(1596911098295000000) 119 return td 120} 121 122func generateTraceSingleSpanMinmalResource() pdata.Traces { 123 td := generateTraceSingleSpanNoResourceOrInstrLibrary() 124 rs := td.ResourceSpans().At(0) 125 rsc := rs.Resource() 126 rsc.Attributes().UpsertString(conventions.AttributeServiceName, "SoleAttr") 127 return td 128} 129 130func generateTraceSingleSpanErrorStatus() pdata.Traces { 131 td := pdata.NewTraces() 132 span := td.ResourceSpans().AppendEmpty().InstrumentationLibrarySpans().AppendEmpty().Spans().AppendEmpty() 133 span.SetTraceID( 134 pdata.NewTraceID([16]byte{0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0x80})) 135 span.SetSpanID(pdata.NewSpanID([8]byte{0xAF, 0xAE, 0xAD, 0xAC, 0xAB, 0xAA, 0xA9, 0xA8})) 136 span.SetName("MinimalData") 137 span.SetKind(pdata.SpanKindClient) 138 span.SetStartTimestamp(1596911098294000000) 139 span.SetEndTimestamp(1596911098295000000) 140 span.Status().SetCode(pdata.StatusCodeError) 141 return td 142} 143 144func TestV2SpanWithoutTimestampGetsTag(t *testing.T) { 145 duration := int64(2948533333) 146 spans := make([]*zipkinmodel.SpanModel, 1) 147 spans[0] = &zipkinmodel.SpanModel{ 148 SpanContext: zipkinmodel.SpanContext{ 149 TraceID: convertTraceID( 150 pdata.NewTraceID([16]byte{0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0x80})), 151 ID: convertSpanID(pdata.NewSpanID([8]byte{0xAF, 0xAE, 0xAD, 0xAC, 0xAB, 0xAA, 0xA9, 0xA8})), 152 }, 153 Name: "NoTimestamps", 154 Kind: zipkinmodel.Client, 155 Duration: time.Duration(duration), 156 Shared: false, 157 LocalEndpoint: nil, 158 RemoteEndpoint: nil, 159 Annotations: nil, 160 Tags: nil, 161 } 162 163 gb, err := ToTranslator{}.ToTraces(spans) 164 if err != nil { 165 t.Errorf("Unexpected error: %v", err) 166 return 167 } 168 169 gs := gb.ResourceSpans().At(0).InstrumentationLibrarySpans().At(0).Spans().At(0) 170 assert.NotNil(t, gs.StartTimestamp) 171 assert.NotNil(t, gs.EndTimestamp) 172 173 // expect starttime to be set to zero (unix time) 174 unixTime := gs.StartTimestamp().AsTime().Unix() 175 assert.Equal(t, int64(0), unixTime) 176 177 // expect end time to be zero (unix time) plus the duration 178 assert.Equal(t, duration, gs.EndTimestamp().AsTime().UnixNano()) 179 180 wasAbsent, mapContainedKey := gs.Attributes().Get(zipkin.StartTimeAbsent) 181 assert.True(t, mapContainedKey) 182 assert.True(t, wasAbsent.BoolVal()) 183} 184