github.com/apipluspower/gqlgen@v0.15.2/graphql/executor/executor_test.go (about) 1 package executor_test 2 3 import ( 4 "context" 5 "testing" 6 7 "github.com/apipluspower/gqlgen/graphql" 8 "github.com/apipluspower/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 root field middleware in order", func(t *testing.T) { 57 var calls []string 58 exec.AroundRootFields(func(ctx context.Context, next graphql.RootResolver) graphql.Marshaler { 59 calls = append(calls, "first") 60 return next(ctx) 61 }) 62 exec.AroundRootFields(func(ctx context.Context, next graphql.RootResolver) graphql.Marshaler { 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 field middleware in order", func(t *testing.T) { 73 var calls []string 74 exec.AroundFields(func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) { 75 calls = append(calls, "first") 76 return next(ctx) 77 }) 78 exec.AroundFields(func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) { 79 calls = append(calls, "second") 80 return next(ctx) 81 }) 82 83 resp := query(exec, "", "{name}") 84 assert.Equal(t, `{"name":"test"}`, string(resp.Data)) 85 assert.Equal(t, []string{"first", "second"}, calls) 86 }) 87 88 t.Run("invokes operation mutators", func(t *testing.T) { 89 var calls []string 90 exec.Use(&testParamMutator{ 91 Mutate: func(ctx context.Context, req *graphql.RawParams) *gqlerror.Error { 92 calls = append(calls, "param") 93 return nil 94 }, 95 }) 96 exec.Use(&testCtxMutator{ 97 Mutate: func(ctx context.Context, rc *graphql.OperationContext) *gqlerror.Error { 98 calls = append(calls, "context") 99 return nil 100 }, 101 }) 102 resp := query(exec, "", "{name}") 103 assert.Equal(t, `{"name":"test"}`, string(resp.Data)) 104 assert.Equal(t, []string{"param", "context"}, calls) 105 }) 106 107 t.Run("get query parse error in AroundResponses", func(t *testing.T) { 108 var errors1 gqlerror.List 109 var errors2 gqlerror.List 110 exec.AroundResponses(func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response { 111 resp := next(ctx) 112 errors1 = graphql.GetErrors(ctx) 113 errors2 = resp.Errors 114 return resp 115 }) 116 117 resp := query(exec, "", "invalid") 118 assert.Equal(t, "", string(resp.Data)) 119 assert.Equal(t, 1, len(resp.Errors)) 120 assert.Equal(t, 1, len(errors1)) 121 assert.Equal(t, 1, len(errors2)) 122 }) 123 124 t.Run("query caching", func(t *testing.T) { 125 ctx := context.Background() 126 cache := &graphql.MapCache{} 127 exec.SetQueryCache(cache) 128 qry := `query Foo {name}` 129 130 t.Run("cache miss populates cache", func(t *testing.T) { 131 resp := query(exec, "Foo", qry) 132 assert.Equal(t, `{"name":"test"}`, string(resp.Data)) 133 134 cacheDoc, ok := cache.Get(ctx, qry) 135 require.True(t, ok) 136 require.Equal(t, "Foo", cacheDoc.(*ast.QueryDocument).Operations[0].Name) 137 }) 138 139 t.Run("cache hits use document from cache", func(t *testing.T) { 140 doc, err := parser.ParseQuery(&ast.Source{Input: `query Bar {name}`}) 141 require.Nil(t, err) 142 cache.Add(ctx, qry, doc) 143 144 resp := query(exec, "Bar", qry) 145 assert.Equal(t, `{"name":"test"}`, string(resp.Data)) 146 147 cacheDoc, ok := cache.Get(ctx, qry) 148 require.True(t, ok) 149 require.Equal(t, "Bar", cacheDoc.(*ast.QueryDocument).Operations[0].Name) 150 }) 151 }) 152 } 153 154 type testParamMutator struct { 155 Mutate func(context.Context, *graphql.RawParams) *gqlerror.Error 156 } 157 158 func (m *testParamMutator) ExtensionName() string { 159 return "Operation: Mutate Parameters" 160 } 161 162 func (m *testParamMutator) Validate(s graphql.ExecutableSchema) error { 163 return nil 164 } 165 166 func (m *testParamMutator) MutateOperationParameters(ctx context.Context, r *graphql.RawParams) *gqlerror.Error { 167 return m.Mutate(ctx, r) 168 } 169 170 type testCtxMutator struct { 171 Mutate func(context.Context, *graphql.OperationContext) *gqlerror.Error 172 } 173 174 func (m *testCtxMutator) ExtensionName() string { 175 return "Operation: Mutate the Context" 176 } 177 178 func (m *testCtxMutator) Validate(s graphql.ExecutableSchema) error { 179 return nil 180 } 181 182 func (m *testCtxMutator) MutateOperationContext(ctx context.Context, rc *graphql.OperationContext) *gqlerror.Error { 183 return m.Mutate(ctx, rc) 184 } 185 186 func TestErrorServer(t *testing.T) { 187 exec := testexecutor.NewError() 188 189 t.Run("get resolver error in AroundResponses", func(t *testing.T) { 190 var errors1 gqlerror.List 191 var errors2 gqlerror.List 192 exec.AroundResponses(func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response { 193 resp := next(ctx) 194 errors1 = graphql.GetErrors(ctx) 195 errors2 = resp.Errors 196 return resp 197 }) 198 199 resp := query(exec, "", "{name}") 200 assert.Equal(t, "null", string(resp.Data)) 201 assert.Equal(t, 1, len(errors1)) 202 assert.Equal(t, 1, len(errors2)) 203 }) 204 } 205 206 func query(exec *testexecutor.TestExecutor, op, q string) *graphql.Response { 207 ctx := graphql.StartOperationTrace(context.Background()) 208 now := graphql.Now() 209 rc, err := exec.CreateOperationContext(ctx, &graphql.RawParams{ 210 Query: q, 211 OperationName: op, 212 ReadTime: graphql.TraceTiming{ 213 Start: now, 214 End: now, 215 }, 216 }) 217 if err != nil { 218 return exec.DispatchError(ctx, err) 219 } 220 221 resp, ctx2 := exec.DispatchOperation(ctx, rc) 222 return resp(ctx2) 223 }