1 package zipkin_test
2
3 import (
4 "context"
5 "errors"
6 "fmt"
7 "net/http"
8 "net/http/httptest"
9 "net/url"
10 "reflect"
11 "testing"
12
13 zipkin "github.com/openzipkin/zipkin-go"
14 "github.com/openzipkin/zipkin-go/model"
15 "github.com/openzipkin/zipkin-go/propagation/b3"
16 "github.com/openzipkin/zipkin-go/reporter/recorder"
17
18 "github.com/go-kit/kit/endpoint"
19 zipkinkit "github.com/go-kit/kit/tracing/zipkin"
20 kithttp "github.com/go-kit/kit/transport/http"
21 )
22
23 const (
24 testName = "test"
25 testBody = "test_body"
26 testTagKey = "test_key"
27 testTagValue = "test_value"
28 )
29
30 func TestHTTPClientTracePropagatesParentSpan(t *testing.T) {
31 rec := recorder.NewReporter()
32 defer rec.Close()
33
34 tr, _ := zipkin.NewTracer(rec)
35
36 rURL, _ := url.Parse("https://httpbin.org/get")
37
38 clientTracer := zipkinkit.HTTPClientTrace(tr)
39 ep := kithttp.NewClient(
40 "GET",
41 rURL,
42 func(ctx context.Context, r *http.Request, i interface{}) error {
43 return nil
44 },
45 func(ctx context.Context, r *http.Response) (response interface{}, err error) {
46 return nil, nil
47 },
48 clientTracer,
49 ).Endpoint()
50
51 parentSpan := tr.StartSpan("test")
52
53 ctx := zipkin.NewContext(context.Background(), parentSpan)
54
55 _, err := ep(ctx, nil)
56 if err != nil {
57 t.Fatalf("unexpected error: %s", err.Error())
58 }
59
60 spans := rec.Flush()
61 if want, have := 1, len(spans); want != have {
62 t.Fatalf("incorrect number of spans, want %d, have %d", want, have)
63 }
64
65 span := spans[0]
66 if span.SpanContext.ParentID == nil {
67 t.Fatalf("incorrect parent ID, want %s have nil", parentSpan.Context().ID)
68 }
69
70 if want, have := parentSpan.Context().ID, *span.SpanContext.ParentID; want != have {
71 t.Fatalf("incorrect parent ID, want %s, have %s", want, have)
72 }
73 }
74
75 func TestHTTPClientTraceAddsExpectedTags(t *testing.T) {
76 dataProvider := []struct {
77 ResponseStatusCode int
78 ErrorTagValue string
79 }{
80 {http.StatusOK, ""},
81 {http.StatusForbidden, fmt.Sprint(http.StatusForbidden)},
82 }
83
84 for _, data := range dataProvider {
85 testHTTPClientTraceCase(t, data.ResponseStatusCode, data.ErrorTagValue)
86 }
87 }
88
89 func testHTTPClientTraceCase(t *testing.T, responseStatusCode int, errTagValue string) {
90 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
91 w.WriteHeader(responseStatusCode)
92 w.Write([]byte(testBody))
93 }))
94 defer ts.Close()
95
96 rec := recorder.NewReporter()
97 defer rec.Close()
98
99 tr, err := zipkin.NewTracer(rec)
100 if err != nil {
101 t.Errorf("Unwanted error: %s", err.Error())
102 }
103
104 rMethod := "GET"
105 rURL, _ := url.Parse(ts.URL)
106
107 clientTracer := zipkinkit.HTTPClientTrace(
108 tr,
109 zipkinkit.Name(testName),
110 zipkinkit.Tags(map[string]string{testTagKey: testTagValue}),
111 )
112
113 ep := kithttp.NewClient(
114 rMethod,
115 rURL,
116 func(ctx context.Context, r *http.Request, i interface{}) error {
117 return nil
118 },
119 func(ctx context.Context, r *http.Response) (response interface{}, err error) {
120 return nil, nil
121 },
122 clientTracer,
123 ).Endpoint()
124
125 _, err = ep(context.Background(), nil)
126 if err != nil {
127 t.Fatalf("unwanted error: %s", err.Error())
128 }
129
130 spans := rec.Flush()
131 if want, have := 1, len(spans); want != have {
132 t.Fatalf("incorrect number of spans, wanted %d, got %d", want, have)
133 }
134
135 span := spans[0]
136 if span.SpanContext.ParentID != nil {
137 t.Fatalf("incorrect parentID, wanted nil, got %s", span.SpanContext.ParentID)
138 }
139
140 if want, have := testName, span.Name; want != have {
141 t.Fatalf("incorrect span name, wanted %s, got %s", want, have)
142 }
143
144 if want, have := model.Client, span.Kind; want != have {
145 t.Fatalf("incorrect span kind, wanted %s, got %s", want, have)
146 }
147
148 tags := map[string]string{
149 testTagKey: testTagValue,
150 string(zipkin.TagHTTPStatusCode): fmt.Sprint(responseStatusCode),
151 string(zipkin.TagHTTPMethod): rMethod,
152 string(zipkin.TagHTTPUrl): rURL.String(),
153 string(zipkin.TagHTTPResponseSize): fmt.Sprint(len(testBody)),
154 }
155
156 if errTagValue != "" {
157 tags[string(zipkin.TagError)] = fmt.Sprint(errTagValue)
158 }
159
160 if !reflect.DeepEqual(span.Tags, tags) {
161 t.Fatalf("invalid tags set, wanted %+v, got %+v", tags, span.Tags)
162 }
163 }
164
165 func TestHTTPServerTrace(t *testing.T) {
166 rec := recorder.NewReporter()
167 defer rec.Close()
168
169
170
171
172 tr, _ := zipkin.NewTracer(rec, zipkin.WithSharedSpans(true))
173
174 handler := kithttp.NewServer(
175 endpoint.Nop,
176 func(context.Context, *http.Request) (interface{}, error) { return nil, nil },
177 func(context.Context, http.ResponseWriter, interface{}) error { return errors.New("dummy") },
178 zipkinkit.HTTPServerTrace(tr),
179 )
180
181 server := httptest.NewServer(handler)
182 defer server.Close()
183
184 const httpMethod = "GET"
185
186 req, err := http.NewRequest(httpMethod, server.URL, nil)
187 if err != nil {
188 t.Fatalf("unable to create HTTP request: %s", err.Error())
189 }
190
191 parentSpan := tr.StartSpan("Dummy")
192
193 b3.InjectHTTP(req)(parentSpan.Context())
194
195 client := http.Client{}
196 resp, err := client.Do(req)
197 if err != nil {
198 t.Fatalf("unable to send HTTP request: %s", err.Error())
199 }
200 resp.Body.Close()
201
202 spans := rec.Flush()
203 if want, have := 1, len(spans); want != have {
204 t.Fatalf("incorrect number of spans, want %d, have %d", want, have)
205 }
206
207 if want, have := parentSpan.Context().TraceID, spans[0].SpanContext.TraceID; want != have {
208 t.Errorf("incorrect TraceID, want %+v, have %+v", want, have)
209 }
210
211 if want, have := parentSpan.Context().ID, spans[0].SpanContext.ID; want != have {
212 t.Errorf("incorrect span ID, want %d, have %d", want, have)
213 }
214
215 if want, have := httpMethod, spans[0].Name; want != have {
216 t.Errorf("incorrect span name, want %s, have %s", want, have)
217 }
218
219 if want, have := http.StatusText(500), spans[0].Tags["error"]; want != have {
220 t.Fatalf("incorrect error tag, want %s, have %s", want, have)
221 }
222 }
223
224 func TestHTTPServerTraceIsRequestBasedSampled(t *testing.T) {
225 rec := recorder.NewReporter()
226 defer rec.Close()
227
228 const httpMethod = "DELETE"
229
230 tr, _ := zipkin.NewTracer(rec)
231
232 handler := kithttp.NewServer(
233 endpoint.Nop,
234 func(context.Context, *http.Request) (interface{}, error) { return nil, nil },
235 func(context.Context, http.ResponseWriter, interface{}) error { return nil },
236 zipkinkit.HTTPServerTrace(tr, zipkinkit.RequestSampler(func(r *http.Request) bool {
237 return r.Method == httpMethod
238 })),
239 )
240
241 server := httptest.NewServer(handler)
242 defer server.Close()
243
244 req, err := http.NewRequest(httpMethod, server.URL, nil)
245 if err != nil {
246 t.Fatalf("unable to create HTTP request: %s", err.Error())
247 }
248
249 client := http.Client{}
250 resp, err := client.Do(req)
251 if err != nil {
252 t.Fatalf("unable to send HTTP request: %s", err.Error())
253 }
254 resp.Body.Close()
255
256 spans := rec.Flush()
257 if want, have := 1, len(spans); want != have {
258 t.Fatalf("incorrect number of spans, want %d, have %d", want, have)
259 }
260 }
261
View as plain text