trace_test.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  1. // Copyright 2018, OpenCensus 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. package ochttp
  15. import (
  16. "bytes"
  17. "context"
  18. "encoding/hex"
  19. "encoding/json"
  20. "errors"
  21. "fmt"
  22. "io"
  23. "io/ioutil"
  24. "log"
  25. "net"
  26. "net/http"
  27. "net/http/httptest"
  28. "net/url"
  29. "reflect"
  30. "strings"
  31. "testing"
  32. "time"
  33. "go.opencensus.io/plugin/ochttp/propagation/b3"
  34. "go.opencensus.io/plugin/ochttp/propagation/tracecontext"
  35. "go.opencensus.io/trace"
  36. )
  37. type testExporter struct {
  38. spans []*trace.SpanData
  39. }
  40. func (t *testExporter) ExportSpan(s *trace.SpanData) {
  41. t.spans = append(t.spans, s)
  42. }
  43. type testTransport struct {
  44. ch chan *http.Request
  45. }
  46. func (t *testTransport) RoundTrip(req *http.Request) (*http.Response, error) {
  47. t.ch <- req
  48. return nil, errors.New("noop")
  49. }
  50. type testPropagator struct{}
  51. func (t testPropagator) SpanContextFromRequest(req *http.Request) (sc trace.SpanContext, ok bool) {
  52. header := req.Header.Get("trace")
  53. buf, err := hex.DecodeString(header)
  54. if err != nil {
  55. log.Fatalf("Cannot decode trace header: %q", header)
  56. }
  57. r := bytes.NewReader(buf)
  58. r.Read(sc.TraceID[:])
  59. r.Read(sc.SpanID[:])
  60. opts, err := r.ReadByte()
  61. if err != nil {
  62. log.Fatalf("Cannot read trace options from trace header: %q", header)
  63. }
  64. sc.TraceOptions = trace.TraceOptions(opts)
  65. return sc, true
  66. }
  67. func (t testPropagator) SpanContextToRequest(sc trace.SpanContext, req *http.Request) {
  68. var buf bytes.Buffer
  69. buf.Write(sc.TraceID[:])
  70. buf.Write(sc.SpanID[:])
  71. buf.WriteByte(byte(sc.TraceOptions))
  72. req.Header.Set("trace", hex.EncodeToString(buf.Bytes()))
  73. }
  74. func TestTransport_RoundTrip_Race(t *testing.T) {
  75. // This tests that we don't modify the request in accordance with the
  76. // specification for http.RoundTripper.
  77. // We attempt to trigger a race by reading the request from a separate
  78. // goroutine. If the request is modified by Transport, this should trigger
  79. // the race detector.
  80. transport := &testTransport{ch: make(chan *http.Request, 1)}
  81. rt := &Transport{
  82. Propagation: &testPropagator{},
  83. Base: transport,
  84. }
  85. req, _ := http.NewRequest("GET", "http://foo.com", nil)
  86. go func() {
  87. fmt.Println(*req)
  88. }()
  89. rt.RoundTrip(req)
  90. _ = <-transport.ch
  91. }
  92. func TestTransport_RoundTrip(t *testing.T) {
  93. _, parent := trace.StartSpan(context.Background(), "parent")
  94. tests := []struct {
  95. name string
  96. parent *trace.Span
  97. }{
  98. {
  99. name: "no parent",
  100. parent: nil,
  101. },
  102. {
  103. name: "parent",
  104. parent: parent,
  105. },
  106. }
  107. for _, tt := range tests {
  108. t.Run(tt.name, func(t *testing.T) {
  109. transport := &testTransport{ch: make(chan *http.Request, 1)}
  110. rt := &Transport{
  111. Propagation: &testPropagator{},
  112. Base: transport,
  113. }
  114. req, _ := http.NewRequest("GET", "http://foo.com", nil)
  115. if tt.parent != nil {
  116. req = req.WithContext(trace.NewContext(req.Context(), tt.parent))
  117. }
  118. rt.RoundTrip(req)
  119. req = <-transport.ch
  120. span := trace.FromContext(req.Context())
  121. if header := req.Header.Get("trace"); header == "" {
  122. t.Fatalf("Trace header = empty; want valid trace header")
  123. }
  124. if span == nil {
  125. t.Fatalf("Got no spans in req context; want one")
  126. }
  127. if tt.parent != nil {
  128. if got, want := span.SpanContext().TraceID, tt.parent.SpanContext().TraceID; got != want {
  129. t.Errorf("span.SpanContext().TraceID=%v; want %v", got, want)
  130. }
  131. }
  132. })
  133. }
  134. }
  135. func TestHandler(t *testing.T) {
  136. traceID := [16]byte{16, 84, 69, 170, 120, 67, 188, 139, 242, 6, 177, 32, 0, 16, 0, 0}
  137. tests := []struct {
  138. header string
  139. wantTraceID trace.TraceID
  140. wantTraceOptions trace.TraceOptions
  141. }{
  142. {
  143. header: "105445aa7843bc8bf206b12000100000000000000000000000",
  144. wantTraceID: traceID,
  145. wantTraceOptions: trace.TraceOptions(0),
  146. },
  147. {
  148. header: "105445aa7843bc8bf206b12000100000000000000000000001",
  149. wantTraceID: traceID,
  150. wantTraceOptions: trace.TraceOptions(1),
  151. },
  152. }
  153. for _, tt := range tests {
  154. t.Run(tt.header, func(t *testing.T) {
  155. handler := &Handler{
  156. Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  157. span := trace.FromContext(r.Context())
  158. sc := span.SpanContext()
  159. if got, want := sc.TraceID, tt.wantTraceID; got != want {
  160. t.Errorf("TraceID = %q; want %q", got, want)
  161. }
  162. if got, want := sc.TraceOptions, tt.wantTraceOptions; got != want {
  163. t.Errorf("TraceOptions = %v; want %v", got, want)
  164. }
  165. }),
  166. StartOptions: trace.StartOptions{Sampler: trace.ProbabilitySampler(0.0)},
  167. Propagation: &testPropagator{},
  168. }
  169. req, _ := http.NewRequest("GET", "http://foo.com", nil)
  170. req.Header.Add("trace", tt.header)
  171. handler.ServeHTTP(nil, req)
  172. })
  173. }
  174. }
  175. var _ http.RoundTripper = (*traceTransport)(nil)
  176. type collector []*trace.SpanData
  177. func (c *collector) ExportSpan(s *trace.SpanData) {
  178. *c = append(*c, s)
  179. }
  180. func TestEndToEnd(t *testing.T) {
  181. tc := []struct {
  182. name string
  183. handler *Handler
  184. transport *Transport
  185. wantSameTraceID bool
  186. wantLinks bool // expect a link between client and server span
  187. }{
  188. {
  189. name: "internal default propagation",
  190. handler: &Handler{},
  191. transport: &Transport{},
  192. wantSameTraceID: true,
  193. },
  194. {
  195. name: "external default propagation",
  196. handler: &Handler{IsPublicEndpoint: true},
  197. transport: &Transport{},
  198. wantSameTraceID: false,
  199. wantLinks: true,
  200. },
  201. {
  202. name: "internal TraceContext propagation",
  203. handler: &Handler{Propagation: &tracecontext.HTTPFormat{}},
  204. transport: &Transport{Propagation: &tracecontext.HTTPFormat{}},
  205. wantSameTraceID: true,
  206. },
  207. {
  208. name: "misconfigured propagation",
  209. handler: &Handler{IsPublicEndpoint: true, Propagation: &tracecontext.HTTPFormat{}},
  210. transport: &Transport{Propagation: &b3.HTTPFormat{}},
  211. wantSameTraceID: false,
  212. wantLinks: false,
  213. },
  214. }
  215. for _, tt := range tc {
  216. t.Run(tt.name, func(t *testing.T) {
  217. var spans collector
  218. trace.RegisterExporter(&spans)
  219. defer trace.UnregisterExporter(&spans)
  220. // Start the server.
  221. serverDone := make(chan struct{})
  222. serverReturn := make(chan time.Time)
  223. tt.handler.StartOptions.Sampler = trace.AlwaysSample()
  224. url := serveHTTP(tt.handler, serverDone, serverReturn, 200)
  225. ctx := context.Background()
  226. // Make the request.
  227. req, err := http.NewRequest(
  228. http.MethodPost,
  229. fmt.Sprintf("%s/example/url/path?qparam=val", url),
  230. strings.NewReader("expected-request-body"))
  231. if err != nil {
  232. t.Fatal(err)
  233. }
  234. req = req.WithContext(ctx)
  235. tt.transport.StartOptions.Sampler = trace.AlwaysSample()
  236. c := &http.Client{
  237. Transport: tt.transport,
  238. }
  239. resp, err := c.Do(req)
  240. if err != nil {
  241. t.Fatal(err)
  242. }
  243. if resp.StatusCode != http.StatusOK {
  244. t.Fatalf("resp.StatusCode = %d", resp.StatusCode)
  245. }
  246. // Tell the server to return from request handling.
  247. serverReturn <- time.Now().Add(time.Millisecond)
  248. respBody, err := ioutil.ReadAll(resp.Body)
  249. if err != nil {
  250. t.Fatal(err)
  251. }
  252. if got, want := string(respBody), "expected-response"; got != want {
  253. t.Fatalf("respBody = %q; want %q", got, want)
  254. }
  255. resp.Body.Close()
  256. <-serverDone
  257. trace.UnregisterExporter(&spans)
  258. if got, want := len(spans), 2; got != want {
  259. t.Fatalf("len(spans) = %d; want %d", got, want)
  260. }
  261. var client, server *trace.SpanData
  262. for _, sp := range spans {
  263. switch sp.SpanKind {
  264. case trace.SpanKindClient:
  265. client = sp
  266. if got, want := client.Name, "/example/url/path"; got != want {
  267. t.Errorf("Span name: %q; want %q", got, want)
  268. }
  269. case trace.SpanKindServer:
  270. server = sp
  271. if got, want := server.Name, "/example/url/path"; got != want {
  272. t.Errorf("Span name: %q; want %q", got, want)
  273. }
  274. default:
  275. t.Fatalf("server or client span missing; kind = %v", sp.SpanKind)
  276. }
  277. }
  278. if tt.wantSameTraceID {
  279. if server.TraceID != client.TraceID {
  280. t.Errorf("TraceID does not match: server.TraceID=%q client.TraceID=%q", server.TraceID, client.TraceID)
  281. }
  282. if !server.HasRemoteParent {
  283. t.Errorf("server span should have remote parent")
  284. }
  285. if server.ParentSpanID != client.SpanID {
  286. t.Errorf("server span should have client span as parent")
  287. }
  288. }
  289. if !tt.wantSameTraceID {
  290. if server.TraceID == client.TraceID {
  291. t.Errorf("TraceID should not be trusted")
  292. }
  293. }
  294. if tt.wantLinks {
  295. if got, want := len(server.Links), 1; got != want {
  296. t.Errorf("len(server.Links) = %d; want %d", got, want)
  297. } else {
  298. link := server.Links[0]
  299. if got, want := link.Type, trace.LinkTypeParent; got != want {
  300. t.Errorf("link.Type = %v; want %v", got, want)
  301. }
  302. }
  303. }
  304. if server.StartTime.Before(client.StartTime) {
  305. t.Errorf("server span starts before client span")
  306. }
  307. if server.EndTime.After(client.EndTime) {
  308. t.Errorf("client span ends before server span")
  309. }
  310. })
  311. }
  312. }
  313. func serveHTTP(handler *Handler, done chan struct{}, wait chan time.Time, statusCode int) string {
  314. handler.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  315. w.WriteHeader(statusCode)
  316. w.(http.Flusher).Flush()
  317. // Simulate a slow-responding server.
  318. sleepUntil := <-wait
  319. for time.Now().Before(sleepUntil) {
  320. time.Sleep(sleepUntil.Sub(time.Now()))
  321. }
  322. io.WriteString(w, "expected-response")
  323. close(done)
  324. })
  325. server := httptest.NewServer(handler)
  326. go func() {
  327. <-done
  328. server.Close()
  329. }()
  330. return server.URL
  331. }
  332. func TestSpanNameFromURL(t *testing.T) {
  333. tests := []struct {
  334. u string
  335. want string
  336. }{
  337. {
  338. u: "http://localhost:80/hello?q=a",
  339. want: "/hello",
  340. },
  341. {
  342. u: "/a/b?q=c",
  343. want: "/a/b",
  344. },
  345. }
  346. for _, tt := range tests {
  347. t.Run(tt.u, func(t *testing.T) {
  348. req, err := http.NewRequest("GET", tt.u, nil)
  349. if err != nil {
  350. t.Errorf("url issue = %v", err)
  351. }
  352. if got := spanNameFromURL(req); got != tt.want {
  353. t.Errorf("spanNameFromURL() = %v, want %v", got, tt.want)
  354. }
  355. })
  356. }
  357. }
  358. func TestFormatSpanName(t *testing.T) {
  359. formatSpanName := func(r *http.Request) string {
  360. return r.Method + " " + r.URL.Path
  361. }
  362. handler := &Handler{
  363. Handler: http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
  364. resp.Write([]byte("Hello, world!"))
  365. }),
  366. FormatSpanName: formatSpanName,
  367. }
  368. server := httptest.NewServer(handler)
  369. defer server.Close()
  370. client := &http.Client{
  371. Transport: &Transport{
  372. FormatSpanName: formatSpanName,
  373. StartOptions: trace.StartOptions{
  374. Sampler: trace.AlwaysSample(),
  375. },
  376. },
  377. }
  378. tests := []struct {
  379. u string
  380. want string
  381. }{
  382. {
  383. u: "/hello?q=a",
  384. want: "GET /hello",
  385. },
  386. {
  387. u: "/a/b?q=c",
  388. want: "GET /a/b",
  389. },
  390. }
  391. for _, tt := range tests {
  392. t.Run(tt.u, func(t *testing.T) {
  393. var te testExporter
  394. trace.RegisterExporter(&te)
  395. res, err := client.Get(server.URL + tt.u)
  396. if err != nil {
  397. t.Fatalf("error creating request: %v", err)
  398. }
  399. res.Body.Close()
  400. trace.UnregisterExporter(&te)
  401. if want, got := 2, len(te.spans); want != got {
  402. t.Fatalf("got exported spans %#v, wanted two spans", te.spans)
  403. }
  404. if got := te.spans[0].Name; got != tt.want {
  405. t.Errorf("spanNameFromURL() = %v, want %v", got, tt.want)
  406. }
  407. if got := te.spans[1].Name; got != tt.want {
  408. t.Errorf("spanNameFromURL() = %v, want %v", got, tt.want)
  409. }
  410. })
  411. }
  412. }
  413. func TestRequestAttributes(t *testing.T) {
  414. tests := []struct {
  415. name string
  416. makeReq func() *http.Request
  417. wantAttrs []trace.Attribute
  418. }{
  419. {
  420. name: "GET example.com/hello",
  421. makeReq: func() *http.Request {
  422. req, _ := http.NewRequest("GET", "http://example.com:779/hello", nil)
  423. req.Header.Add("User-Agent", "ua")
  424. return req
  425. },
  426. wantAttrs: []trace.Attribute{
  427. trace.StringAttribute("http.path", "/hello"),
  428. trace.StringAttribute("http.url", "http://example.com:779/hello"),
  429. trace.StringAttribute("http.host", "example.com:779"),
  430. trace.StringAttribute("http.method", "GET"),
  431. trace.StringAttribute("http.user_agent", "ua"),
  432. },
  433. },
  434. }
  435. for _, tt := range tests {
  436. t.Run(tt.name, func(t *testing.T) {
  437. req := tt.makeReq()
  438. attrs := requestAttrs(req)
  439. if got, want := attrs, tt.wantAttrs; !reflect.DeepEqual(got, want) {
  440. t.Errorf("Request attributes = %#v; want %#v", got, want)
  441. }
  442. })
  443. }
  444. }
  445. func TestResponseAttributes(t *testing.T) {
  446. tests := []struct {
  447. name string
  448. resp *http.Response
  449. wantAttrs []trace.Attribute
  450. }{
  451. {
  452. name: "non-zero HTTP 200 response",
  453. resp: &http.Response{StatusCode: 200},
  454. wantAttrs: []trace.Attribute{
  455. trace.Int64Attribute("http.status_code", 200),
  456. },
  457. },
  458. {
  459. name: "zero HTTP 500 response",
  460. resp: &http.Response{StatusCode: 500},
  461. wantAttrs: []trace.Attribute{
  462. trace.Int64Attribute("http.status_code", 500),
  463. },
  464. },
  465. }
  466. for _, tt := range tests {
  467. t.Run(tt.name, func(t *testing.T) {
  468. attrs := responseAttrs(tt.resp)
  469. if got, want := attrs, tt.wantAttrs; !reflect.DeepEqual(got, want) {
  470. t.Errorf("Response attributes = %#v; want %#v", got, want)
  471. }
  472. })
  473. }
  474. }
  475. type TestCase struct {
  476. Name string
  477. Method string
  478. URL string
  479. Headers map[string]string
  480. ResponseCode int
  481. SpanName string
  482. SpanStatus string
  483. SpanKind string
  484. SpanAttributes map[string]string
  485. }
  486. func TestAgainstSpecs(t *testing.T) {
  487. fmt.Println("start")
  488. dat, err := ioutil.ReadFile("testdata/http-out-test-cases.json")
  489. if err != nil {
  490. t.Fatalf("error reading file: %v", err)
  491. }
  492. tests := make([]TestCase, 0)
  493. err = json.Unmarshal(dat, &tests)
  494. if err != nil {
  495. t.Fatalf("error parsing json: %v", err)
  496. }
  497. trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})
  498. for _, tt := range tests {
  499. t.Run(tt.Name, func(t *testing.T) {
  500. var spans collector
  501. trace.RegisterExporter(&spans)
  502. defer trace.UnregisterExporter(&spans)
  503. handler := &Handler{}
  504. transport := &Transport{}
  505. serverDone := make(chan struct{})
  506. serverReturn := make(chan time.Time)
  507. host := ""
  508. port := ""
  509. serverRequired := strings.Contains(tt.URL, "{")
  510. if serverRequired {
  511. // Start the server.
  512. localServerURL := serveHTTP(handler, serverDone, serverReturn, tt.ResponseCode)
  513. u, _ := url.Parse(localServerURL)
  514. host, port, _ = net.SplitHostPort(u.Host)
  515. tt.URL = strings.Replace(tt.URL, "{host}", host, 1)
  516. tt.URL = strings.Replace(tt.URL, "{port}", port, 1)
  517. }
  518. // Start a root Span in the client.
  519. ctx, _ := trace.StartSpan(
  520. context.Background(),
  521. "top-level")
  522. // Make the request.
  523. req, err := http.NewRequest(
  524. tt.Method,
  525. tt.URL,
  526. nil)
  527. for headerName, headerValue := range tt.Headers {
  528. req.Header.Add(headerName, headerValue)
  529. }
  530. if err != nil {
  531. t.Fatal(err)
  532. }
  533. req = req.WithContext(ctx)
  534. resp, err := transport.RoundTrip(req)
  535. if err != nil {
  536. // do not fail. We want to validate DNS issues
  537. //t.Fatal(err)
  538. }
  539. if serverRequired {
  540. // Tell the server to return from request handling.
  541. serverReturn <- time.Now().Add(time.Millisecond)
  542. }
  543. if resp != nil {
  544. // If it simply closes body without reading
  545. // synchronization problem may happen for spans slice.
  546. // Server span and client span will write themselves
  547. // at the same time
  548. ioutil.ReadAll(resp.Body)
  549. resp.Body.Close()
  550. if serverRequired {
  551. <-serverDone
  552. }
  553. }
  554. trace.UnregisterExporter(&spans)
  555. var client *trace.SpanData
  556. for _, sp := range spans {
  557. if sp.SpanKind == trace.SpanKindClient {
  558. client = sp
  559. }
  560. }
  561. if client.Name != tt.SpanName {
  562. t.Errorf("span names don't match: expected: %s, actual: %s", tt.SpanName, client.Name)
  563. }
  564. spanKindToStr := map[int]string{
  565. trace.SpanKindClient: "Client",
  566. trace.SpanKindServer: "Server",
  567. }
  568. if !strings.EqualFold(codeToStr[client.Status.Code], tt.SpanStatus) {
  569. t.Errorf("span status don't match: expected: %s, actual: %d (%s)", tt.SpanStatus, client.Status.Code, codeToStr[client.Status.Code])
  570. }
  571. if !strings.EqualFold(spanKindToStr[client.SpanKind], tt.SpanKind) {
  572. t.Errorf("span kind don't match: expected: %s, actual: %d (%s)", tt.SpanKind, client.SpanKind, spanKindToStr[client.SpanKind])
  573. }
  574. normalizedActualAttributes := map[string]string{}
  575. for k, v := range client.Attributes {
  576. normalizedActualAttributes[k] = fmt.Sprintf("%v", v)
  577. }
  578. normalizedExpectedAttributes := map[string]string{}
  579. for k, v := range tt.SpanAttributes {
  580. normalizedValue := v
  581. normalizedValue = strings.Replace(normalizedValue, "{host}", host, 1)
  582. normalizedValue = strings.Replace(normalizedValue, "{port}", port, 1)
  583. normalizedExpectedAttributes[k] = normalizedValue
  584. }
  585. if got, want := normalizedActualAttributes, normalizedExpectedAttributes; !reflect.DeepEqual(got, want) {
  586. t.Errorf("Request attributes = %#v; want %#v", got, want)
  587. }
  588. })
  589. }
  590. }
  591. func TestStatusUnitTest(t *testing.T) {
  592. tests := []struct {
  593. in int
  594. want trace.Status
  595. }{
  596. {200, trace.Status{Code: trace.StatusCodeOK, Message: `OK`}},
  597. {204, trace.Status{Code: trace.StatusCodeOK, Message: `OK`}},
  598. {100, trace.Status{Code: trace.StatusCodeUnknown, Message: `UNKNOWN`}},
  599. {500, trace.Status{Code: trace.StatusCodeUnknown, Message: `UNKNOWN`}},
  600. {404, trace.Status{Code: trace.StatusCodeNotFound, Message: `NOT_FOUND`}},
  601. {600, trace.Status{Code: trace.StatusCodeUnknown, Message: `UNKNOWN`}},
  602. {401, trace.Status{Code: trace.StatusCodeUnauthenticated, Message: `UNAUTHENTICATED`}},
  603. {403, trace.Status{Code: trace.StatusCodePermissionDenied, Message: `PERMISSION_DENIED`}},
  604. {301, trace.Status{Code: trace.StatusCodeOK, Message: `OK`}},
  605. {501, trace.Status{Code: trace.StatusCodeUnimplemented, Message: `UNIMPLEMENTED`}},
  606. }
  607. for _, tt := range tests {
  608. got, want := TraceStatus(tt.in, ""), tt.want
  609. if got != want {
  610. t.Errorf("status(%d) got = (%#v) want = (%#v)", tt.in, got, want)
  611. }
  612. }
  613. }