github.com/maeglindeveloper/gqlgen@v0.13.1-0.20210413081235-57808b12a0a0/graphql/executor/executor_test.go (about) 1 package executor_test 2 3 import ( 4 "context" 5 "testing" 6 7 "github.com/99designs/gqlgen/graphql" 8 "github.com/99designs/gqlgen/graphql/executor/testexecutor" 9 "github.com/stretchr/testify/assert" 10 "github.com/stretchr/testify/require" 11 "github.com/vektah/gqlparser/v2/ast" 12 "github.com/vektah/gqlparser/v2/gqlerror" 13 "github.com/vektah/gqlparser/v2/parser" 14 ) 15 16 func TestExecutor(t *testing.T) { 17 exec := testexecutor.New() 18 19 t.Run("calls query on executable schema", func(t *testing.T) { 20 resp := query(exec, "", "{name}") 21 assert.Equal(t, `{"name":"test"}`, string(resp.Data)) 22 }) 23 24 t.Run("invokes operation middleware in order", func(t *testing.T) { 25 var calls []string 26 exec.AroundOperations(func(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler { 27 calls = append(calls, "first") 28 return next(ctx) 29 }) 30 exec.AroundOperations(func(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler { 31 calls = append(calls, "second") 32 return next(ctx) 33 }) 34 35 resp := query(exec, "", "{name}") 36 assert.Equal(t, `{"name":"test"}`, string(resp.Data)) 37 assert.Equal(t, []string{"first", "second"}, calls) 38 }) 39 40 t.Run("invokes response middleware in order", func(t *testing.T) { 41 var calls []string 42 exec.AroundResponses(func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response { 43 calls = append(calls, "first") 44 return next(ctx) 45 }) 46 exec.AroundResponses(func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response { 47 calls = append(calls, "second") 48 return next(ctx) 49 }) 50 51 resp := query(exec, "", "{name}") 52 assert.Equal(t, `{"name":"test"}`, string(resp.Data)) 53 assert.Equal(t, []string{"first", "second"}, calls) 54 }) 55 56 t.Run("invokes field middleware in order", func(t *testing.T) { 57 var calls []string 58 exec.AroundFields(func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) { 59 calls = append(calls, "first") 60 return next(ctx) 61 }) 62 exec.AroundFields(func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) { 63 calls = append(calls, "second") 64 return next(ctx) 65 }) 66 67 resp := query(exec, "", "{name}") 68 assert.Equal(t, `{"name":"test"}`, string(resp.Data)) 69 assert.Equal(t, []string{"first", "second"}, calls) 70 }) 71 72 t.Run("invokes operation mutators", func(t *testing.T) { 73 var calls []string 74 exec.Use(&testParamMutator{ 75 Mutate: func(ctx context.Context, req *graphql.RawParams) *gqlerror.Error { 76 calls = append(calls, "param") 77 return nil 78 }, 79 }) 80 exec.Use(&testCtxMutator{ 81 Mutate: func(ctx context.Context, rc *graphql.OperationContext) *gqlerror.Error { 82 calls = append(calls, "context") 83 return nil 84 }, 85 }) 86 resp := query(exec, "", "{name}") 87 assert.Equal(t, `{"name":"test"}`, string(resp.Data)) 88 assert.Equal(t, []string{"param", "context"}, calls) 89 }) 90 91 t.Run("get query parse error in AroundResponses", func(t *testing.T) { 92 var errors1 gqlerror.List 93 var errors2 gqlerror.List 94 exec.AroundResponses(func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response { 95 resp := next(ctx) 96 errors1 = graphql.GetErrors(ctx) 97 errors2 = resp.Errors 98 return resp 99 }) 100 101 resp := query(exec, "", "invalid") 102 assert.Equal(t, "", string(resp.Data)) 103 assert.Equal(t, 1, len(resp.Errors)) 104 assert.Equal(t, 1, len(errors1)) 105 assert.Equal(t, 1, len(errors2)) 106 }) 107 108 t.Run("query caching", func(t *testing.T) { 109 ctx := context.Background() 110 cache := &graphql.MapCache{} 111 exec.SetQueryCache(cache) 112 qry := `query Foo {name}` 113 114 t.Run("cache miss populates cache", func(t *testing.T) { 115 resp := query(exec, "Foo", qry) 116 assert.Equal(t, `{"name":"test"}`, string(resp.Data)) 117 118 cacheDoc, ok := cache.Get(ctx, qry) 119 require.True(t, ok) 120 require.Equal(t, "Foo", cacheDoc.(*ast.QueryDocument).Operations[0].Name) 121 }) 122 123 t.Run("cache hits use document from cache", func(t *testing.T) { 124 doc, err := parser.ParseQuery(&ast.Source{Input: `query Bar {name}`}) 125 require.Nil(t, err) 126 cache.Add(ctx, qry, doc) 127 128 resp := query(exec, "Bar", qry) 129 assert.Equal(t, `{"name":"test"}`, string(resp.Data)) 130 131 cacheDoc, ok := cache.Get(ctx, qry) 132 require.True(t, ok) 133 require.Equal(t, "Bar", cacheDoc.(*ast.QueryDocument).Operations[0].Name) 134 }) 135 }) 136 } 137 138 type testParamMutator struct { 139 Mutate func(context.Context, *graphql.RawParams) *gqlerror.Error 140 } 141 142 func (m *testParamMutator) ExtensionName() string { 143 return "Operation: Mutate Parameters" 144 } 145 146 func (m *testParamMutator) Validate(s graphql.ExecutableSchema) error { 147 return nil 148 } 149 150 func (m *testParamMutator) MutateOperationParameters(ctx context.Context, r *graphql.RawParams) *gqlerror.Error { 151 return m.Mutate(ctx, r) 152 } 153 154 type testCtxMutator struct { 155 Mutate func(context.Context, *graphql.OperationContext) *gqlerror.Error 156 } 157 158 func (m *testCtxMutator) ExtensionName() string { 159 return "Operation: Mutate the Context" 160 } 161 162 func (m *testCtxMutator) Validate(s graphql.ExecutableSchema) error { 163 return nil 164 } 165 166 func (m *testCtxMutator) MutateOperationContext(ctx context.Context, rc *graphql.OperationContext) *gqlerror.Error { 167 return m.Mutate(ctx, rc) 168 } 169 170 func TestErrorServer(t *testing.T) { 171 exec := testexecutor.NewError() 172 173 t.Run("get resolver error in AroundResponses", func(t *testing.T) { 174 var errors1 gqlerror.List 175 var errors2 gqlerror.List 176 exec.AroundResponses(func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response { 177 resp := next(ctx) 178 errors1 = graphql.GetErrors(ctx) 179 errors2 = resp.Errors 180 return resp 181 }) 182 183 resp := query(exec, "", "{name}") 184 assert.Equal(t, "null", string(resp.Data)) 185 assert.Equal(t, 1, len(errors1)) 186 assert.Equal(t, 1, len(errors2)) 187 }) 188 } 189 190 func query(exec *testexecutor.TestExecutor, op, q string) *graphql.Response { 191 ctx := graphql.StartOperationTrace(context.Background()) 192 now := graphql.Now() 193 rc, err := exec.CreateOperationContext(ctx, &graphql.RawParams{ 194 Query: q, 195 OperationName: op, 196 ReadTime: graphql.TraceTiming{ 197 Start: now, 198 End: now, 199 }, 200 }) 201 202 if err != nil { 203 return exec.DispatchError(ctx, err) 204 } 205 206 resp, ctx2 := exec.DispatchOperation(ctx, rc) 207 return resp(ctx2) 208 }