1// Licensed to Elasticsearch B.V. under one or more contributor
2// license agreements. See the NOTICE file distributed with
3// this work for additional information regarding copyright
4// ownership. Elasticsearch B.V. licenses this file to you under
5// the Apache License, Version 2.0 (the "License"); you may
6// not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9//     http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18package apm_test
19
20import (
21	"context"
22	"testing"
23	"time"
24
25	"github.com/stretchr/testify/assert"
26	"github.com/stretchr/testify/require"
27
28	"go.elastic.co/apm"
29	"go.elastic.co/apm/apmtest"
30	"go.elastic.co/apm/model"
31	"go.elastic.co/apm/transport/transporttest"
32)
33
34func TestStartSpanTransactionNotSampled(t *testing.T) {
35	tracer, _ := apm.NewTracer("tracer_testing", "")
36	defer tracer.Close()
37	// sample nothing
38	tracer.SetSampler(apm.NewRatioSampler(0))
39
40	tx := tracer.StartTransaction("name", "type")
41	assert.False(t, tx.Sampled())
42	span := tx.StartSpan("name", "type", nil)
43	assert.True(t, span.Dropped())
44}
45
46func TestTracerStartSpan(t *testing.T) {
47	tracer, r := transporttest.NewRecorderTracer()
48	defer tracer.Close()
49
50	txTimestamp := time.Now()
51	tx := tracer.StartTransactionOptions("name", "type", apm.TransactionOptions{
52		Start: txTimestamp,
53	})
54	txTraceContext := tx.TraceContext()
55	span0 := tx.StartSpan("name", "type", nil)
56	span0TraceContext := span0.TraceContext()
57	span0.End()
58	tx.End()
59
60	// Even if the transaction and parent span have been ended,
61	// it is possible to report a span with their IDs.
62	tracer.StartSpan("name", "type",
63		txTraceContext.Span,
64		apm.SpanOptions{
65			Parent: span0TraceContext,
66			Start:  txTimestamp.Add(time.Second),
67		},
68	).End()
69
70	tracer.Flush(nil)
71	payloads := r.Payloads()
72	assert.Len(t, payloads.Transactions, 1)
73	assert.Len(t, payloads.Spans, 2)
74
75	assert.Equal(t, payloads.Transactions[0].ID, payloads.Spans[0].ParentID)
76	assert.Equal(t, payloads.Spans[0].ID, payloads.Spans[1].ParentID)
77	for _, span := range payloads.Spans {
78		assert.Equal(t, payloads.Transactions[0].TraceID, span.TraceID)
79		assert.Equal(t, payloads.Transactions[0].ID, span.TransactionID)
80	}
81	assert.NotZero(t, payloads.Spans[1].ID)
82
83	assert.Equal(t, time.Time(payloads.Transactions[0].Timestamp).Add(time.Second), time.Time(payloads.Spans[1].Timestamp))
84
85	// The span created after the transaction (obviously?)
86	// doesn't get included in the transaction's span count.
87	assert.Equal(t, 1, payloads.Transactions[0].SpanCount.Started)
88}
89
90func TestSpanTiming(t *testing.T) {
91	tx, spans, _ := apmtest.WithTransaction(func(ctx context.Context) {
92		time.Sleep(200 * time.Millisecond)
93		span, _ := apm.StartSpan(ctx, "name", "type")
94		time.Sleep(200 * time.Millisecond)
95		span.End()
96	})
97	require.Len(t, spans, 1)
98	span := spans[0]
99
100	assert.InDelta(t,
101		time.Time(span.Timestamp).Sub(time.Time(tx.Timestamp)),
102		float64(200*time.Millisecond),
103		float64(100*time.Millisecond),
104	)
105	assert.InDelta(t, span.Duration, 200, 100)
106}
107
108func TestSpanType(t *testing.T) {
109	spanTypes := []string{"type", "type.subtype", "type.subtype.action", "type.subtype.action.figure"}
110	_, spans, _ := apmtest.WithTransaction(func(ctx context.Context) {
111		for _, spanType := range spanTypes {
112			span, _ := apm.StartSpan(ctx, "name", spanType)
113			span.End()
114		}
115	})
116	require.Len(t, spans, 4)
117
118	check := func(s model.Span, spanType, spanSubtype, spanAction string) {
119		assert.Equal(t, spanType, s.Type)
120		assert.Equal(t, spanSubtype, s.Subtype)
121		assert.Equal(t, spanAction, s.Action)
122	}
123	check(spans[0], "type", "", "")
124	check(spans[1], "type", "subtype", "")
125	check(spans[2], "type", "subtype", "action")
126	check(spans[3], "type", "subtype", "action.figure")
127}
128
129func TestTracerStartSpanIDSpecified(t *testing.T) {
130	spanID := apm.SpanID{0, 1, 2, 3, 4, 5, 6, 7}
131	_, spans, _ := apmtest.WithTransaction(func(ctx context.Context) {
132		span, _ := apm.StartSpanOptions(ctx, "name", "type", apm.SpanOptions{SpanID: spanID})
133		span.End()
134	})
135	require.Len(t, spans, 1)
136	assert.Equal(t, model.SpanID(spanID), spans[0].ID)
137}
138