package opencensus_test
import (
"context"
"errors"
"testing"
"go.opencensus.io/trace"
"go.opencensus.io/trace/propagation"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"github.com/go-kit/kit/endpoint"
ockit "github.com/go-kit/kit/tracing/opencensus"
grpctransport "github.com/go-kit/kit/transport/grpc"
)
type dummy struct{}
const traceContextKey = "grpc-trace-bin"
func unaryInterceptor(
ctx context.Context, method string, req, reply interface{},
cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption,
) error {
return nil
}
func TestGRPCClientTrace(t *testing.T) {
rec := &recordingExporter{}
trace.RegisterExporter(rec)
cc, err := grpc.Dial(
"",
grpc.WithUnaryInterceptor(unaryInterceptor),
grpc.WithInsecure(),
)
if err != nil {
t.Fatalf("unable to create gRPC dialer: %s", err.Error())
}
traces := []struct {
name string
err error
}{
{"", nil},
{"CustomName", nil},
{"", errors.New("dummy-error")},
}
for _, tr := range traces {
clientTracer := ockit.GRPCClientTrace(
ockit.WithName(tr.name),
ockit.WithSampler(trace.AlwaysSample()),
)
ep := grpctransport.NewClient(
cc,
"dummyService",
"dummyMethod",
func(context.Context, interface{}) (interface{}, error) {
return nil, nil
},
func(context.Context, interface{}) (interface{}, error) {
return nil, tr.err
},
dummy{},
clientTracer,
).Endpoint()
ctx, parentSpan := trace.StartSpan(context.Background(), "test")
_, err = ep(ctx, nil)
if want, have := tr.err, err; want != have {
t.Fatalf("unexpected error, want %s, have %s", tr.err.Error(), err.Error())
}
spans := rec.Flush()
if want, have := 1, len(spans); want != have {
t.Fatalf("incorrect number of spans, want %d, have %d", want, have)
}
span := spans[0]
if want, have := parentSpan.SpanContext().SpanID, span.ParentSpanID; want != have {
t.Errorf("incorrect parent ID, want %s, have %s", want, have)
}
if want, have := tr.name, span.Name; want != have && want != "" {
t.Errorf("incorrect span name, want %s, have %s", want, have)
}
if want, have := "/dummyService/dummyMethod", span.Name; want != have && tr.name == "" {
t.Errorf("incorrect span name, want %s, have %s", want, have)
}
code := trace.StatusCodeOK
if tr.err != nil {
code = trace.StatusCodeUnknown
if want, have := err.Error(), span.Status.Message; want != have {
t.Errorf("incorrect span status msg, want %s, have %s", want, have)
}
}
if want, have := int32(code), span.Status.Code; want != have {
t.Errorf("incorrect span status code, want %d, have %d", want, have)
}
}
}
func TestGRPCServerTrace(t *testing.T) {
rec := &recordingExporter{}
trace.RegisterExporter(rec)
traces := []struct {
useParent bool
name string
err error
}{
{false, "", nil},
{true, "", nil},
{true, "CustomName", nil},
{true, "", errors.New("dummy-error")},
}
for _, tr := range traces {
var (
ctx = context.Background()
parentSpan *trace.Span
)
server := grpctransport.NewServer(
endpoint.Nop,
func(context.Context, interface{}) (interface{}, error) {
return nil, nil
},
func(context.Context, interface{}) (interface{}, error) {
return nil, tr.err
},
ockit.GRPCServerTrace(
ockit.WithName(tr.name),
ockit.WithSampler(trace.AlwaysSample()),
),
)
if tr.useParent {
_, parentSpan = trace.StartSpan(context.Background(), "test")
traceContextBinary := propagation.Binary(parentSpan.SpanContext())
md := metadata.MD{}
md.Set(traceContextKey, string(traceContextBinary))
ctx = metadata.NewIncomingContext(ctx, md)
}
server.ServeGRPC(ctx, nil)
spans := rec.Flush()
if want, have := 1, len(spans); want != have {
t.Fatalf("incorrect number of spans, want %d, have %d", want, have)
}
if tr.useParent {
if want, have := parentSpan.SpanContext().TraceID, spans[0].TraceID; want != have {
t.Errorf("incorrect trace ID, want %s, have %s", want, have)
}
if want, have := parentSpan.SpanContext().SpanID, spans[0].ParentSpanID; want != have {
t.Errorf("incorrect span ID, want %s, have %s", want, have)
}
}
if want, have := tr.name, spans[0].Name; want != have && want != "" {
t.Errorf("incorrect span name, want %s, have %s", want, have)
}
if tr.err != nil {
if want, have := int32(codes.Internal), spans[0].Status.Code; want != have {
t.Errorf("incorrect span status code, want %d, have %d", want, have)
}
if want, have := tr.err.Error(), spans[0].Status.Message; want != have {
t.Errorf("incorrect span status message, want %s, have %s", want, have)
}
}
}
}