package tracing import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/exporters/otlp/otlptrace" semconv "go.opentelemetry.io/otel/semconv/v1.7.0" "go.opentelemetry.io/otel/trace" commonpb "go.opentelemetry.io/proto/otlp/common/v1" resourcepb "go.opentelemetry.io/proto/otlp/resource/v1" tracepb "go.opentelemetry.io/proto/otlp/trace/v1" ) const ( resourceSchemaUrl = "http://example.com/custom-resource-schema" instrumentSchemaUrl = semconv.SchemaURL ) var ( traceId = []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F} spanId = []byte{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8} parentSpanId = []byte{0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08} startTime = time.Date(2022, 4, 4, 0, 0, 0, 0, time.UTC) endTime = startTime.Add(5 * time.Second) traceState, _ = trace.ParseTraceState("key1=val1,key2=val2") instrScope = &commonpb.InstrumentationScope{Name: "go.opentelemetry.io/test/otel", Version: "v1.6.0"} otlpKeyValues = []*commonpb.KeyValue{ { Key: "string_key", Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_StringValue{ StringValue: "string value", }, }, }, { Key: "bool_key", Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_BoolValue{ BoolValue: true, }, }, }, } otlpResource = &resourcepb.Resource{ Attributes: []*commonpb.KeyValue{ { Key: "service.name", Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_StringValue{ StringValue: "service-name", }, }, }, }, } ) var _ otlptrace.Client = (*InMemoryOtlpClient)(nil) var _ InMemoryClient = (*InMemoryOtlpClient)(nil) var _ otlptrace.Client = (*NoopOtlpClient)(nil) var _ InMemoryClient = (*NoopOtlpClient)(nil) func TestUploadTraces(t *testing.T) { client := &InMemoryOtlpClient{} spans := createResourceSpans([]*tracepb.Span{createOtlpSpan(traceId)}) spans2 := createResourceSpans([]*tracepb.Span{createOtlpSpan(traceId)}) err := client.UploadTraces(context.Background(), spans) assert.NoError(t, err) err = client.UploadTraces(context.Background(), spans2) assert.NoError(t, err) assert.Len(t, client.spans, 2) } func TestSpans(t *testing.T) { client := &InMemoryOtlpClient{} spans := createResourceSpans([]*tracepb.Span{createOtlpSpan(traceId)}) err := client.UploadTraces(context.Background(), spans) assert.NoError(t, err) assert.Len(t, client.spans, 1) enc, err := client.Spans() assert.NoError(t, err) expected := "CsECCiAKHgoMc2VydmljZS5uYW1lEg4KDHNlcnZpY2UtbmFtZRLxAQonCh1nby5vcGVudGVsZW1ldHJ5LmlvL3Rlc3Qvb3RlbBIGdjEuNi4wEp0BChAAAQIDBAUGBwgJCgsMDQ4PEgj//v38+/r5+BoTa2V5MT12YWwxLGtleTI9dmFsMiIIDw4NDAsKCQgqCnRyYWNlX25hbWUwATkAANJvaYjiFkEA8teZaojiFkocCgpzdHJpbmdfa2V5Eg4KDHN0cmluZyB2YWx1ZUoOCghib29sX2tleRICEAF6EhIOc3RhdHVzIG1lc3NhZ2UYARomaHR0cHM6Ly9vcGVudGVsZW1ldHJ5LmlvL3NjaGVtYXMvMS43LjAaKWh0dHA6Ly9leGFtcGxlLmNvbS9jdXN0b20tcmVzb3VyY2Utc2NoZW1h" assert.Equal(t, expected, enc) } func TestSpansEmpty(t *testing.T) { client := &InMemoryOtlpClient{} err := client.UploadTraces(context.Background(), []*tracepb.ResourceSpans{}) assert.NoError(t, err) assert.Len(t, client.spans, 0) _, err = client.Spans() assert.ErrorIs(t, err, errNoTraces) } func TestSpansNil(t *testing.T) { client := &InMemoryOtlpClient{} err := client.UploadTraces(context.Background(), nil) assert.NoError(t, err) assert.Len(t, client.spans, 0) _, err = client.Spans() assert.ErrorIs(t, err, errNoTraces) } func TestSpansTooManySpans(t *testing.T) { client := &InMemoryOtlpClient{} for i := 0; i < MaxTraceAmount+1; i++ { spans := createResourceSpans([]*tracepb.Span{createOtlpSpan(traceId)}) err := client.UploadTraces(context.Background(), spans) assert.NoError(t, err) } assert.Len(t, client.spans, MaxTraceAmount) _, err := client.Spans() assert.NoError(t, err) } func createResourceSpans(spans []*tracepb.Span) []*tracepb.ResourceSpans { return []*tracepb.ResourceSpans{createResourceSpan(spans)} } func createResourceSpan(spans []*tracepb.Span) *tracepb.ResourceSpans { return &tracepb.ResourceSpans{ Resource: otlpResource, ScopeSpans: []*tracepb.ScopeSpans{ { Scope: instrScope, Spans: spans, SchemaUrl: instrumentSchemaUrl, }, }, InstrumentationLibrarySpans: nil, SchemaUrl: resourceSchemaUrl, } } func createOtlpSpan(tid []byte) *tracepb.Span { return &tracepb.Span{ TraceId: tid, SpanId: spanId, TraceState: traceState.String(), ParentSpanId: parentSpanId, Name: "trace_name", Kind: tracepb.Span_SPAN_KIND_INTERNAL, StartTimeUnixNano: uint64(startTime.UnixNano()), EndTimeUnixNano: uint64(endTime.UnixNano()), Attributes: otlpKeyValues, DroppedAttributesCount: 0, Events: nil, DroppedEventsCount: 0, Links: nil, DroppedLinksCount: 0, Status: &tracepb.Status{ Message: "status message", Code: tracepb.Status_STATUS_CODE_OK, }, } }