github.com/blend/go-sdk@v1.20240719.1/tracing/webtrace/tracer_test.go (about)

     1  /*
     2  
     3  Copyright (c) 2024 - Present. Blend Labs, Inc. All rights reserved
     4  Use of this source code is governed by a MIT license that can be found in the LICENSE file.
     5  
     6  */
     7  
     8  package webtrace
     9  
    10  import (
    11  	"bytes"
    12  	"fmt"
    13  	"net/http"
    14  	"testing"
    15  
    16  	opentracing "github.com/opentracing/opentracing-go"
    17  	opentracingExt "github.com/opentracing/opentracing-go/ext"
    18  	"github.com/opentracing/opentracing-go/mocktracer"
    19  
    20  	"github.com/blend/go-sdk/assert"
    21  	"github.com/blend/go-sdk/logger"
    22  	"github.com/blend/go-sdk/tracing"
    23  	"github.com/blend/go-sdk/web"
    24  )
    25  
    26  func TestStart(t *testing.T) {
    27  	assert := assert.New(t)
    28  	mockTracer := mocktracer.New()
    29  	webTracer := Tracer(mockTracer)
    30  
    31  	ctx := web.MockCtx("GET", "/test-resource")
    32  	_ = webTracer.Start(ctx)
    33  
    34  	span := opentracing.SpanFromContext(ctx.Context())
    35  	mockSpan := span.(*mocktracer.MockSpan)
    36  	assert.Equal(tracing.OperationHTTPRequest, mockSpan.OperationName)
    37  
    38  	assert.Len(mockSpan.Tags(), 9)
    39  	assert.Equal(opentracingExt.SpanKindRPCServerEnum, mockSpan.Tags()[string(opentracingExt.SpanKind)])
    40  	assert.Equal("GET /test-resource", mockSpan.Tags()[tracing.TagKeyResourceName])
    41  	assert.Equal(tracing.SpanTypeWeb, mockSpan.Tags()[tracing.TagKeySpanType])
    42  	assert.Equal("GET", mockSpan.Tags()[tracing.TagKeyHTTPMethod])
    43  	assert.Equal("/test-resource", mockSpan.Tags()[tracing.TagKeyHTTPURL])
    44  	assert.Equal("127.0.0.1", mockSpan.Tags()["http.remote_addr"])
    45  	assert.Equal("localhost", mockSpan.Tags()["http.host"])
    46  	assert.Equal("go-sdk test", mockSpan.Tags()["http.user_agent"])
    47  	assert.True(mockSpan.FinishTime.IsZero())
    48  }
    49  
    50  func TestStartWithRoute(t *testing.T) {
    51  	assert := assert.New(t)
    52  	mockTracer := mocktracer.New()
    53  	webTracer := Tracer(mockTracer)
    54  
    55  	ctx := web.MockCtx(
    56  		"GET", "/test-resource/3",
    57  		web.OptCtxRoute(&web.Route{Path: "/test-resource/:id"}),
    58  	)
    59  	_ = webTracer.Start(ctx)
    60  
    61  	span := opentracing.SpanFromContext(ctx.Context())
    62  	mockSpan := span.(*mocktracer.MockSpan)
    63  	assert.Equal(tracing.OperationHTTPRequest, mockSpan.OperationName)
    64  
    65  	assert.Len(mockSpan.Tags(), 10)
    66  	assert.Equal("GET /test-resource/:id", mockSpan.Tags()[tracing.TagKeyResourceName])
    67  	assert.Equal(tracing.SpanTypeWeb, mockSpan.Tags()[tracing.TagKeySpanType])
    68  	assert.Equal(opentracingExt.SpanKindRPCServerEnum, mockSpan.Tags()[string(opentracingExt.SpanKind)])
    69  	assert.Equal("GET", mockSpan.Tags()[tracing.TagKeyHTTPMethod])
    70  	assert.Equal("/test-resource/3", mockSpan.Tags()[tracing.TagKeyHTTPURL])
    71  	assert.Equal("127.0.0.1", mockSpan.Tags()["http.remote_addr"])
    72  	assert.Equal("localhost", mockSpan.Tags()["http.host"])
    73  	assert.Equal("go-sdk test", mockSpan.Tags()["http.user_agent"])
    74  	assert.Equal("/test-resource/:id", mockSpan.Tags()["http.route"])
    75  	assert.True(mockSpan.FinishTime.IsZero())
    76  }
    77  
    78  func TestStartAndFinishWithRouteIncludingLabelsFalse(t *testing.T) {
    79  	assert := assert.New(t)
    80  	mockTracer := mocktracer.New()
    81  	webTracer := Tracer(mockTracer, OptIncludeCtxLabels(false))
    82  
    83  	logBuffer := bytes.NewBuffer([]byte{})
    84  	ctx := web.MockCtx(
    85  		"GET", "/test-resource/3",
    86  		web.OptCtxRoute(&web.Route{Path: "/test-resource/:id"}),
    87  		web.OptCtxLog(logger.Memory(logBuffer)),
    88  	)
    89  	ctx.Response.WriteHeader(http.StatusOK)
    90  
    91  	ctx.WithContext(logger.WithLabel(ctx.Context(), "foo", "bar"))
    92  	labelValue, labelFound := logger.GetLabel(ctx.Context(), "foo")
    93  	assert.True(labelFound)
    94  	assert.Equal("bar", labelValue)
    95  	wt := webTracer.Start(ctx)
    96  	wt.Finish(ctx, nil)
    97  	span := opentracing.SpanFromContext(ctx.Context())
    98  	mockSpan := span.(*mocktracer.MockSpan)
    99  	assert.Equal(tracing.OperationHTTPRequest, mockSpan.OperationName)
   100  
   101  	assert.Len(mockSpan.Tags(), 11)
   102  	assert.Equal("GET /test-resource/:id", mockSpan.Tags()[tracing.TagKeyResourceName])
   103  	assert.Equal(tracing.SpanTypeWeb, mockSpan.Tags()[tracing.TagKeySpanType])
   104  	assert.Equal(opentracingExt.SpanKindRPCServerEnum, mockSpan.Tags()[string(opentracingExt.SpanKind)])
   105  	assert.Equal("GET", mockSpan.Tags()[tracing.TagKeyHTTPMethod])
   106  	assert.Equal("/test-resource/3", mockSpan.Tags()[tracing.TagKeyHTTPURL])
   107  	assert.Equal("127.0.0.1", mockSpan.Tags()["http.remote_addr"])
   108  	assert.Equal("localhost", mockSpan.Tags()["http.host"])
   109  	assert.Equal("go-sdk test", mockSpan.Tags()["http.user_agent"])
   110  	assert.Equal("/test-resource/:id", mockSpan.Tags()["http.route"])
   111  	assert.Equal("200", mockSpan.Tags()[tracing.TagKeyHTTPCode])
   112  	assert.False(mockSpan.FinishTime.IsZero())
   113  }
   114  
   115  func TestStartAndFinishWithRouteIncludingLabelsTrue(t *testing.T) {
   116  	assert := assert.New(t)
   117  	mockTracer := mocktracer.New()
   118  	webTracer := Tracer(mockTracer, OptIncludeCtxLabels(true))
   119  
   120  	logBuffer := bytes.NewBuffer([]byte{})
   121  	ctx := web.MockCtx(
   122  		"GET", "/test-resource/3",
   123  		web.OptCtxRoute(&web.Route{Path: "/test-resource/:id"}),
   124  		web.OptCtxLog(logger.Memory(logBuffer)),
   125  	)
   126  	ctx.Response.WriteHeader(http.StatusOK)
   127  
   128  	ctx.WithContext(logger.WithLabel(ctx.Context(), "foo", "bar"))
   129  	labelValue, labelFound := logger.GetLabel(ctx.Context(), "foo")
   130  	assert.True(labelFound)
   131  	assert.Equal("bar", labelValue)
   132  
   133  	wt := webTracer.Start(ctx)
   134  	wt.Finish(ctx, nil)
   135  	span := opentracing.SpanFromContext(ctx.Context())
   136  	mockSpan := span.(*mocktracer.MockSpan)
   137  	assert.Equal(tracing.OperationHTTPRequest, mockSpan.OperationName)
   138  
   139  	assert.Len(mockSpan.Tags(), 13)
   140  	assert.Equal("GET /test-resource/:id", mockSpan.Tags()[tracing.TagKeyResourceName])
   141  	assert.Equal(tracing.SpanTypeWeb, mockSpan.Tags()[tracing.TagKeySpanType])
   142  	assert.Equal(opentracingExt.SpanKindRPCServerEnum, mockSpan.Tags()[string(opentracingExt.SpanKind)])
   143  	assert.Equal("GET", mockSpan.Tags()[tracing.TagKeyHTTPMethod])
   144  	assert.Equal("/test-resource/3", mockSpan.Tags()[tracing.TagKeyHTTPURL])
   145  	assert.Equal("127.0.0.1", mockSpan.Tags()["http.remote_addr"])
   146  	assert.Equal("localhost", mockSpan.Tags()["http.host"])
   147  	assert.Equal("go-sdk test", mockSpan.Tags()["http.user_agent"])
   148  	assert.Equal("/test-resource/:id", mockSpan.Tags()["http.route"])
   149  	assert.Equal("200", mockSpan.Tags()[tracing.TagKeyHTTPCode])
   150  	assert.Equal("bar", mockSpan.Tags()[fmt.Sprintf("%s.foo", tracing.TagKeyCtx)])
   151  	assert.Equal("/test-resource/:id", mockSpan.Tags()[fmt.Sprintf("%s.web.route", tracing.TagKeyCtx)])
   152  	assert.False(mockSpan.FinishTime.IsZero())
   153  }
   154  
   155  func optCtxIncomingSpan(t opentracing.Tracer, s opentracing.Span) web.CtxOption {
   156  	return func(c *web.Ctx) {
   157  		_ = t.Inject(
   158  			s.Context(),
   159  			opentracing.HTTPHeaders,
   160  			opentracing.HTTPHeadersCarrier(c.Request.Header),
   161  		)
   162  	}
   163  }
   164  
   165  func TestStartWithParentSpan(t *testing.T) {
   166  	assert := assert.New(t)
   167  	mockTracer := mocktracer.New()
   168  	webTracer := Tracer(mockTracer)
   169  
   170  	parentSpan := mockTracer.StartSpan("test_op")
   171  	ctx := web.MockCtx(
   172  		"GET", "/test-resource",
   173  		optCtxIncomingSpan(mockTracer, parentSpan),
   174  	)
   175  	_ = webTracer.Start(ctx)
   176  
   177  	span := opentracing.SpanFromContext(ctx.Context())
   178  	mockSpan := span.(*mocktracer.MockSpan)
   179  	assert.Equal(tracing.OperationHTTPRequest, mockSpan.OperationName)
   180  
   181  	mockParentSpan := parentSpan.(*mocktracer.MockSpan)
   182  	assert.Equal(mockSpan.ParentID, mockParentSpan.SpanContext.SpanID)
   183  }
   184  
   185  func TestFinish(t *testing.T) {
   186  	assert := assert.New(t)
   187  	mockTracer := mocktracer.New()
   188  	webTracer := Tracer(mockTracer)
   189  
   190  	ctx := web.MockCtx("GET", "/test-resource")
   191  	tf := webTracer.Start(ctx)
   192  
   193  	result := web.Raw([]byte("success"))
   194  	assert.Nil(result.Render(ctx))
   195  
   196  	tf.Finish(ctx, nil)
   197  
   198  	span := opentracing.SpanFromContext(ctx.Context())
   199  	mockSpan := span.(*mocktracer.MockSpan)
   200  	assert.Equal("200", mockSpan.Tags()[tracing.TagKeyHTTPCode])
   201  	assert.False(mockSpan.FinishTime.IsZero())
   202  }
   203  
   204  func TestFinishError(t *testing.T) {
   205  	assert := assert.New(t)
   206  	mockTracer := mocktracer.New()
   207  	webTracer := Tracer(mockTracer)
   208  
   209  	ctx := web.MockCtx("GET", "/test-resource")
   210  	tf := webTracer.Start(ctx)
   211  
   212  	result := web.Raw([]byte("success"))
   213  	result.StatusCode = 500
   214  	assert.Nil(result.Render(ctx))
   215  
   216  	tf.Finish(ctx, fmt.Errorf("error"))
   217  
   218  	span := opentracing.SpanFromContext(ctx.Context())
   219  	mockSpan := span.(*mocktracer.MockSpan)
   220  	assert.Equal("500", mockSpan.Tags()[tracing.TagKeyHTTPCode])
   221  	assert.Equal("error", mockSpan.Tags()[tracing.TagKeyError])
   222  	assert.False(mockSpan.FinishTime.IsZero())
   223  }
   224  
   225  func TestFinishNilSpan(t *testing.T) {
   226  	assert := assert.New(t)
   227  
   228  	ctx := web.MockCtx("GET", "/test-resource")
   229  	webTraceFinisher{}.Finish(ctx, nil)
   230  	assert.Nil(opentracing.SpanFromContext(ctx.Context()))
   231  }
   232  
   233  func TestStartView(t *testing.T) {
   234  	assert := assert.New(t)
   235  	mockTracer := mocktracer.New()
   236  	webViewTracer := Tracer(mockTracer).(web.ViewTracer)
   237  
   238  	ctx := web.MockCtx("GET", "/test-resource")
   239  	viewResult := &web.ViewResult{
   240  		ViewName:   "test_view",
   241  		StatusCode: 200,
   242  	}
   243  	wvtf := webViewTracer.StartView(ctx, viewResult)
   244  	span := wvtf.(*webViewTraceFinisher).span
   245  	mockSpan := span.(*mocktracer.MockSpan)
   246  	assert.Equal(tracing.OperationHTTPRender, mockSpan.OperationName)
   247  
   248  	assert.Len(mockSpan.Tags(), 4)
   249  	assert.Equal("test_view", mockSpan.Tags()[tracing.TagKeyResourceName])
   250  	assert.Equal(tracing.SpanTypeWeb, mockSpan.Tags()[tracing.TagKeySpanType])
   251  	assert.Equal(opentracingExt.SpanKindRPCServerEnum, mockSpan.Tags()[string(opentracingExt.SpanKind)])
   252  	assert.True(mockSpan.FinishTime.IsZero())
   253  }
   254  
   255  func TestStartViewWithParentSpan(t *testing.T) {
   256  	assert := assert.New(t)
   257  	mockTracer := mocktracer.New()
   258  	webViewTracer := Tracer(mockTracer).(web.ViewTracer)
   259  
   260  	parentSpan := mockTracer.StartSpan("test_op")
   261  	ctx := web.MockCtx("GET", "/test-resource")
   262  	ctx.WithContext(opentracing.ContextWithSpan(ctx.Context(), parentSpan))
   263  	viewResult := &web.ViewResult{
   264  		ViewName:   "test_view",
   265  		StatusCode: 200,
   266  	}
   267  	wvtf := webViewTracer.StartView(ctx, viewResult)
   268  	span := wvtf.(*webViewTraceFinisher).span
   269  	mockSpan := span.(*mocktracer.MockSpan)
   270  	assert.Equal(tracing.OperationHTTPRender, mockSpan.OperationName)
   271  
   272  	mockParentSpan := parentSpan.(*mocktracer.MockSpan)
   273  	assert.Equal(mockSpan.ParentID, mockParentSpan.SpanContext.SpanID)
   274  }
   275  
   276  func TestFinishView(t *testing.T) {
   277  	assert := assert.New(t)
   278  	mockTracer := mocktracer.New()
   279  	webViewTracer := Tracer(mockTracer).(web.ViewTracer)
   280  
   281  	ctx := web.MockCtx("GET", "/test-resource")
   282  	viewResult := &web.ViewResult{
   283  		ViewName:   "test_view",
   284  		StatusCode: 200,
   285  	}
   286  	wvtf := webViewTracer.StartView(ctx, viewResult)
   287  	wvtf.FinishView(ctx, viewResult, nil)
   288  
   289  	span := wvtf.(*webViewTraceFinisher).span
   290  	mockSpan := span.(*mocktracer.MockSpan)
   291  
   292  	assert.Nil(mockSpan.Tags()[tracing.TagKeyError])
   293  	assert.False(mockSpan.FinishTime.IsZero())
   294  }
   295  
   296  func TestFinishViewNilSpan(t *testing.T) {
   297  	assert := assert.New(t)
   298  
   299  	ctx := web.MockCtx("GET", "/test-resource")
   300  	webViewTraceFinisher{}.FinishView(ctx, nil, nil)
   301  	assert.Nil(opentracing.SpanFromContext(ctx.Context()))
   302  }