1// Unless explicitly stated otherwise all files in this repository are licensed
2// under the Apache License Version 2.0.
3// This product includes software developed at Datadog (https://www.datadoghq.com/).
4// Copyright 2016 Datadog, Inc.
5
6package http
7
8import (
9	"net/http"
10	"net/http/httptest"
11	"testing"
12
13	"github.com/stretchr/testify/assert"
14	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
15	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/mocktracer"
16	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
17	"gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig"
18)
19
20func TestHttpTracer200(t *testing.T) {
21	mt := mocktracer.Start()
22	defer mt.Stop()
23
24	url := "/200"
25	r := httptest.NewRequest("GET", url, nil)
26	w := httptest.NewRecorder()
27	router().ServeHTTP(w, r)
28
29	assert := assert.New(t)
30	assert.Equal(200, w.Code)
31	assert.Equal("OK\n", w.Body.String())
32
33	spans := mt.FinishedSpans()
34	assert.Equal(1, len(spans))
35
36	s := spans[0]
37	assert.Equal("http.request", s.OperationName())
38	assert.Equal("my-service", s.Tag(ext.ServiceName))
39	assert.Equal("GET "+url, s.Tag(ext.ResourceName))
40	assert.Equal("200", s.Tag(ext.HTTPCode))
41	assert.Equal("GET", s.Tag(ext.HTTPMethod))
42	assert.Equal(url, s.Tag(ext.HTTPURL))
43	assert.Equal(nil, s.Tag(ext.Error))
44	assert.Equal("bar", s.Tag("foo"))
45}
46
47func TestHttpTracer500(t *testing.T) {
48	mt := mocktracer.Start()
49	defer mt.Stop()
50
51	// Send and verify a 500 request
52	url := "/500"
53	r := httptest.NewRequest("GET", url, nil)
54	w := httptest.NewRecorder()
55	router().ServeHTTP(w, r)
56
57	assert := assert.New(t)
58	assert.Equal(500, w.Code)
59	assert.Equal("500!\n", w.Body.String())
60
61	spans := mt.FinishedSpans()
62	assert.Equal(1, len(spans))
63
64	s := spans[0]
65	assert.Equal("http.request", s.OperationName())
66	assert.Equal("my-service", s.Tag(ext.ServiceName))
67	assert.Equal("GET "+url, s.Tag(ext.ResourceName))
68	assert.Equal("500", s.Tag(ext.HTTPCode))
69	assert.Equal("GET", s.Tag(ext.HTTPMethod))
70	assert.Equal(url, s.Tag(ext.HTTPURL))
71	assert.Equal("500: Internal Server Error", s.Tag(ext.Error).(error).Error())
72	assert.Equal("bar", s.Tag("foo"))
73}
74
75func TestWrapHandler200(t *testing.T) {
76	mt := mocktracer.Start()
77	defer mt.Stop()
78	assert := assert.New(t)
79
80	handler := WrapHandler(http.HandlerFunc(handler200), "my-service", "my-resource",
81		WithSpanOptions(tracer.Tag("foo", "bar")),
82	)
83
84	url := "/"
85	r := httptest.NewRequest("GET", url, nil)
86	w := httptest.NewRecorder()
87	handler.ServeHTTP(w, r)
88	assert.Equal(200, w.Code)
89	assert.Equal("OK\n", w.Body.String())
90
91	spans := mt.FinishedSpans()
92	assert.Equal(1, len(spans))
93
94	s := spans[0]
95	assert.Equal("http.request", s.OperationName())
96	assert.Equal("my-service", s.Tag(ext.ServiceName))
97	assert.Equal("my-resource", s.Tag(ext.ResourceName))
98	assert.Equal("200", s.Tag(ext.HTTPCode))
99	assert.Equal("GET", s.Tag(ext.HTTPMethod))
100	assert.Equal(url, s.Tag(ext.HTTPURL))
101	assert.Equal(nil, s.Tag(ext.Error))
102	assert.Equal("bar", s.Tag("foo"))
103}
104
105func TestNoStack(t *testing.T) {
106	mt := mocktracer.Start()
107	defer mt.Stop()
108	assert := assert.New(t)
109
110	handler := WrapHandler(http.HandlerFunc(handler500), "my-service", "my-resource",
111		NoDebugStack())
112
113	r := httptest.NewRequest("GET", "/", nil)
114	w := httptest.NewRecorder()
115	handler.ServeHTTP(w, r)
116	assert.Equal(http.StatusInternalServerError, w.Code)
117	assert.Equal("500!\n", w.Body.String())
118
119	spans := mt.FinishedSpans()
120	assert.Equal(1, len(spans))
121	s := spans[0]
122	assert.EqualError(spans[0].Tags()[ext.Error].(error), "500: Internal Server Error")
123	assert.Equal("<debug stack disabled>", s.Tags()[ext.ErrorStack])
124
125}
126
127func TestAnalyticsSettings(t *testing.T) {
128	tests := map[string]func(t *testing.T, mt mocktracer.Tracer, rate interface{}, opts ...Option){
129		"ServeMux": func(t *testing.T, mt mocktracer.Tracer, rate interface{}, opts ...Option) {
130			mux := NewServeMux(opts...)
131			mux.HandleFunc("/200", handler200)
132			r := httptest.NewRequest("GET", "/200", nil)
133			w := httptest.NewRecorder()
134			mux.ServeHTTP(w, r)
135
136			spans := mt.FinishedSpans()
137			assert.Len(t, spans, 1)
138			s := spans[0]
139			assert.Equal(t, rate, s.Tag(ext.EventSampleRate))
140		},
141		"WrapHandler": func(t *testing.T, mt mocktracer.Tracer, rate interface{}, opts ...Option) {
142			f := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
143				message := "Hello \n"
144				w.Write([]byte(message))
145			})
146			handler := WrapHandler(f, "my-service", "my-resource", opts...)
147			r := httptest.NewRequest("GET", "/200", nil)
148			w := httptest.NewRecorder()
149			handler.ServeHTTP(w, r)
150
151			spans := mt.FinishedSpans()
152			assert.Len(t, spans, 1)
153			s := spans[0]
154			assert.Equal(t, rate, s.Tag(ext.EventSampleRate))
155		},
156	}
157
158	for name, test := range tests {
159		t.Run("defaults/"+name, func(t *testing.T) {
160			mt := mocktracer.Start()
161			defer mt.Stop()
162
163			test(t, mt, nil)
164		})
165
166		t.Run("global/"+name, func(t *testing.T) {
167			mt := mocktracer.Start()
168			defer mt.Stop()
169
170			rate := globalconfig.AnalyticsRate()
171			defer globalconfig.SetAnalyticsRate(rate)
172			globalconfig.SetAnalyticsRate(0.4)
173
174			test(t, mt, 0.4)
175		})
176
177		t.Run("enabled/"+name, func(t *testing.T) {
178			mt := mocktracer.Start()
179			defer mt.Stop()
180
181			test(t, mt, 1.0, WithAnalytics(true))
182		})
183
184		t.Run("disabled/"+name, func(t *testing.T) {
185			mt := mocktracer.Start()
186			defer mt.Stop()
187
188			test(t, mt, nil, WithAnalytics(false))
189		})
190
191		t.Run("override/"+name, func(t *testing.T) {
192			mt := mocktracer.Start()
193			defer mt.Stop()
194
195			rate := globalconfig.AnalyticsRate()
196			defer globalconfig.SetAnalyticsRate(rate)
197			globalconfig.SetAnalyticsRate(0.4)
198
199			test(t, mt, 0.23, WithAnalyticsRate(0.23))
200		})
201	}
202}
203
204func router() http.Handler {
205	mux := NewServeMux(WithServiceName("my-service"), WithSpanOptions(tracer.Tag("foo", "bar")))
206	mux.HandleFunc("/200", handler200)
207	mux.HandleFunc("/500", handler500)
208	return mux
209}
210
211func handler200(w http.ResponseWriter, r *http.Request) {
212	w.Write([]byte("OK\n"))
213}
214
215func handler500(w http.ResponseWriter, r *http.Request) {
216	http.Error(w, "500!", http.StatusInternalServerError)
217}
218