Codebase list golang-github-go-kit-kit / 8cbddfe tracing / zipkin / zipkin_test.go
8cbddfe

Tree @8cbddfe (Download .tar.gz)

zipkin_test.go @8cbddferaw · history · blame

package zipkin_test

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"reflect"
	"runtime"
	"strconv"
	"strings"
	"testing"

	"golang.org/x/net/context"
	"google.golang.org/grpc/metadata"

	"github.com/go-kit/kit/endpoint"
	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/tracing/zipkin"
)

func TestToContext(t *testing.T) {
	const (
		hostport           = "5.5.5.5:5555"
		serviceName        = "foo-service"
		methodName         = "foo-method"
		traceID      int64 = 12
		spanID       int64 = 34
		parentSpanID int64 = 56
	)

	r, _ := http.NewRequest("GET", "https://best.horse", nil)
	r.Header.Set("X-B3-TraceId", strconv.FormatInt(traceID, 16))
	r.Header.Set("X-B3-SpanId", strconv.FormatInt(spanID, 16))
	r.Header.Set("X-B3-ParentSpanId", strconv.FormatInt(parentSpanID, 16))

	newSpan := zipkin.MakeNewSpanFunc(hostport, serviceName, methodName)
	toContext := zipkin.ToContext(newSpan, log.NewLogfmtLogger(ioutil.Discard))

	ctx := toContext(context.Background(), r)
	val := ctx.Value(zipkin.SpanContextKey)
	if val == nil {
		t.Fatalf("%s returned no value", zipkin.SpanContextKey)
	}
	span, ok := val.(*zipkin.Span)
	if !ok {
		t.Fatalf("%s was not a Span object", zipkin.SpanContextKey)
	}

	for want, haveFunc := range map[int64]func() int64{
		traceID:      span.TraceID,
		spanID:       span.SpanID,
		parentSpanID: span.ParentSpanID,
	} {
		if have := haveFunc(); want != have {
			name := runtime.FuncForPC(reflect.ValueOf(haveFunc).Pointer()).Name()
			name = strings.Split(name, "·")[0]
			toks := strings.Split(name, ".")
			name = toks[len(toks)-1]
			t.Errorf("%s: want %d, have %d", name, want, have)
		}
	}
}

func TestToGRPCContext(t *testing.T) {
	const (
		hostport           = "5.5.5.5:5555"
		serviceName        = "foo-service"
		methodName         = "foo-method"
		traceID      int64 = 12
		spanID       int64 = 34
		parentSpanID int64 = 56
	)

	md := metadata.MD{
		"x-b3-traceid":      []string{strconv.FormatInt(traceID, 16)},
		"x-b3-spanid":       []string{strconv.FormatInt(spanID, 16)},
		"x-b3-parentspanid": []string{strconv.FormatInt(parentSpanID, 16)},
	}

	newSpan := zipkin.MakeNewSpanFunc(hostport, serviceName, methodName)
	toContext := zipkin.ToGRPCContext(newSpan, log.NewLogfmtLogger(ioutil.Discard))

	ctx := toContext(context.Background(), &md)
	val := ctx.Value(zipkin.SpanContextKey)
	if val == nil {
		t.Fatalf("%s returned no value", zipkin.SpanContextKey)
	}
	span, ok := val.(*zipkin.Span)
	if !ok {
		t.Fatalf("%s was not a Span object", zipkin.SpanContextKey)
	}

	for want, haveFunc := range map[int64]func() int64{
		traceID:      span.TraceID,
		spanID:       span.SpanID,
		parentSpanID: span.ParentSpanID,
	} {
		if have := haveFunc(); want != have {
			name := runtime.FuncForPC(reflect.ValueOf(haveFunc).Pointer()).Name()
			name = strings.Split(name, "·")[0]
			toks := strings.Split(name, ".")
			name = toks[len(toks)-1]
			t.Errorf("%s: want %d, have %d", name, want, have)
		}
	}
}

func TestToRequest(t *testing.T) {
	const (
		hostport           = "5.5.5.5:5555"
		serviceName        = "foo-service"
		methodName         = "foo-method"
		traceID      int64 = 20
		spanID       int64 = 40
		parentSpanID int64 = 90
	)

	newSpan := zipkin.MakeNewSpanFunc(hostport, serviceName, methodName)
	span := newSpan(traceID, spanID, parentSpanID)
	ctx := context.WithValue(context.Background(), zipkin.SpanContextKey, span)
	r, _ := http.NewRequest("GET", "https://best.horse", nil)
	ctx = zipkin.ToRequest(newSpan)(ctx, r)

	for header, wantInt := range map[string]int64{
		"X-B3-TraceId":      traceID,
		"X-B3-SpanId":       spanID,
		"X-B3-ParentSpanId": parentSpanID,
	} {
		if want, have := strconv.FormatInt(wantInt, 16), r.Header.Get(header); want != have {
			t.Errorf("%s: want %q, have %q", header, want, have)
		}
	}
}

func TestToGRPCRequest(t *testing.T) {
	const (
		hostport           = "5.5.5.5:5555"
		serviceName        = "foo-service"
		methodName         = "foo-method"
		traceID      int64 = 20
		spanID       int64 = 40
		parentSpanID int64 = 90
	)

	newSpan := zipkin.MakeNewSpanFunc(hostport, serviceName, methodName)
	span := newSpan(traceID, spanID, parentSpanID)
	ctx := context.WithValue(context.Background(), zipkin.SpanContextKey, span)
	md := &metadata.MD{}
	ctx = zipkin.ToGRPCRequest(newSpan)(ctx, md)

	for header, wantInt := range map[string]int64{
		"x-b3-traceid":      traceID,
		"x-b3-spanid":       spanID,
		"x-b3-parentspanid": parentSpanID,
	} {
		if want, have := strconv.FormatInt(wantInt, 16), (*md)[header][0]; want != have {
			t.Errorf("%s: want %q, have %q", header, want, have)
		}
	}
}

func TestAnnotateServer(t *testing.T) {
	if err := testAnnotate(zipkin.AnnotateServer, zipkin.ServerReceive, zipkin.ServerSend); err != nil {
		t.Fatal(err)
	}
}

func TestAnnotateClient(t *testing.T) {
	if err := testAnnotate(zipkin.AnnotateClient, zipkin.ClientSend, zipkin.ClientReceive); err != nil {
		t.Fatal(err)
	}
}

func testAnnotate(
	annotate func(newSpan zipkin.NewSpanFunc, c zipkin.Collector) endpoint.Middleware,
	wantAnnotations ...string,
) error {
	const (
		hostport    = "1.2.3.4:1234"
		serviceName = "some-service"
		methodName  = "some-method"
	)

	newSpan := zipkin.MakeNewSpanFunc(hostport, serviceName, methodName)
	collector := &countingCollector{}

	var e endpoint.Endpoint
	e = func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil }
	e = annotate(newSpan, collector)(e)

	if want, have := 0, len(collector.annotations); want != have {
		return fmt.Errorf("pre-invocation: want %d, have %d", want, have)
	}
	if _, err := e(context.Background(), struct{}{}); err != nil {
		return fmt.Errorf("during invocation: %v", err)
	}
	if want, have := wantAnnotations, collector.annotations; !reflect.DeepEqual(want, have) {
		return fmt.Errorf("after invocation: want %v, have %v", want, have)
	}

	return nil
}

type countingCollector struct{ annotations []string }

func (c *countingCollector) Collect(s *zipkin.Span) error {
	for _, annotation := range s.Encode().GetAnnotations() {
		c.annotations = append(c.annotations, annotation.GetValue())
	}
	return nil
}

func (c *countingCollector) Close() error {
	return nil
}