github.com/mstephano/gqlgen-schemagen@v0.0.0-20230113041936-dd2cd4ea46aa/graphql/handler/apollofederatedtracingv1/tracing_test.go (about) 1 package apollofederatedtracingv1_test 2 3 import ( 4 "context" 5 "encoding/base64" 6 "encoding/json" 7 "fmt" 8 "net/http" 9 "net/http/httptest" 10 "strings" 11 "testing" 12 "time" 13 14 "github.com/mstephano/gqlgen-schemagen/graphql" 15 "github.com/mstephano/gqlgen-schemagen/graphql/handler/apollofederatedtracingv1" 16 "github.com/mstephano/gqlgen-schemagen/graphql/handler/apollofederatedtracingv1/generated" 17 "github.com/mstephano/gqlgen-schemagen/graphql/handler/apollotracing" 18 "github.com/mstephano/gqlgen-schemagen/graphql/handler/extension" 19 "github.com/mstephano/gqlgen-schemagen/graphql/handler/lru" 20 "github.com/mstephano/gqlgen-schemagen/graphql/handler/testserver" 21 "github.com/mstephano/gqlgen-schemagen/graphql/handler/transport" 22 "github.com/stretchr/testify/assert" 23 "github.com/stretchr/testify/require" 24 "github.com/vektah/gqlparser/v2/gqlerror" 25 "google.golang.org/protobuf/proto" 26 ) 27 28 func TestApolloTracing(t *testing.T) { 29 h := testserver.New() 30 h.AddTransport(transport.POST{}) 31 h.Use(&apollofederatedtracingv1.Tracer{}) 32 h.Use(&delayMiddleware{}) 33 34 resp := doRequest(h, http.MethodPost, "/graphql", `{"query":"{ name }"}`) 35 assert.Equal(t, http.StatusOK, resp.Code, resp.Body.String()) 36 var respData struct { 37 Extensions struct { 38 FTV1 string `json:"ftv1"` 39 } `json:"extensions"` 40 } 41 require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &respData)) 42 43 tracing := respData.Extensions.FTV1 44 pbuf, err := base64.StdEncoding.DecodeString(tracing) 45 require.Nil(t, err) 46 47 ftv1 := &generated.Trace{} 48 err = proto.Unmarshal(pbuf, ftv1) 49 require.Nil(t, err) 50 51 require.NotZero(t, ftv1.StartTime.Nanos) 52 require.Less(t, ftv1.StartTime.Nanos, ftv1.EndTime.Nanos) 53 require.EqualValues(t, ftv1.EndTime.Nanos-ftv1.StartTime.Nanos, ftv1.DurationNs) 54 55 fmt.Printf("%#v\n", resp.Body.String()) 56 require.Equal(t, "Query", ftv1.Root.Child[0].ParentType) 57 require.Equal(t, "name", ftv1.Root.Child[0].GetResponseName()) 58 require.Equal(t, "String!", ftv1.Root.Child[0].Type) 59 } 60 61 func TestApolloTracing_Concurrent(t *testing.T) { 62 h := testserver.New() 63 h.AddTransport(transport.POST{}) 64 h.Use(&apollofederatedtracingv1.Tracer{}) 65 for i := 0; i < 2; i++ { 66 go func() { 67 resp := doRequest(h, http.MethodPost, "/graphql", `{"query":"{ name }"}`) 68 assert.Equal(t, http.StatusOK, resp.Code, resp.Body.String()) 69 var respData struct { 70 Extensions struct { 71 FTV1 string `json:"ftv1"` 72 } `json:"extensions"` 73 } 74 require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &respData)) 75 76 tracing := respData.Extensions.FTV1 77 pbuf, err := base64.StdEncoding.DecodeString(tracing) 78 require.Nil(t, err) 79 80 ftv1 := &generated.Trace{} 81 err = proto.Unmarshal(pbuf, ftv1) 82 require.Nil(t, err) 83 require.NotZero(t, ftv1.StartTime.Nanos) 84 }() 85 } 86 } 87 88 func TestApolloTracing_withFail(t *testing.T) { 89 h := testserver.New() 90 h.AddTransport(transport.POST{}) 91 h.Use(extension.AutomaticPersistedQuery{Cache: lru.New(100)}) 92 h.Use(apollotracing.Tracer{}) 93 94 resp := doRequest(h, http.MethodPost, "/graphql", `{"operationName":"A","extensions":{"persistedQuery":{"version":1,"sha256Hash":"338bbc16ac780daf81845339fbf0342061c1e9d2b702c96d3958a13a557083a6"}}}`) 95 assert.Equal(t, http.StatusOK, resp.Code, resp.Body.String()) 96 b := resp.Body.Bytes() 97 t.Log(string(b)) 98 var respData struct { 99 Errors gqlerror.List 100 } 101 require.NoError(t, json.Unmarshal(b, &respData)) 102 require.Len(t, respData.Errors, 1) 103 require.Equal(t, "PersistedQueryNotFound", respData.Errors[0].Message) 104 } 105 106 func doRequest(handler http.Handler, method, target, body string) *httptest.ResponseRecorder { 107 r := httptest.NewRequest(method, target, strings.NewReader(body)) 108 r.Header.Set("Content-Type", "application/json") 109 r.Header.Set("apollo-federation-include-trace", "ftv1") 110 w := httptest.NewRecorder() 111 112 handler.ServeHTTP(w, r) 113 return w 114 } 115 116 type delayMiddleware struct{} 117 118 func (*delayMiddleware) InterceptOperation(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler { 119 time.Sleep(time.Millisecond) 120 return next(ctx) 121 } 122 123 func (*delayMiddleware) ExtensionName() string { 124 return "delay" 125 } 126 127 func (*delayMiddleware) Validate(schema graphql.ExecutableSchema) error { 128 return nil 129 }