1 package followschema
2
3 import (
4 "context"
5 "fmt"
6 "testing"
7
8 "github.com/stretchr/testify/require"
9
10 "github.com/99designs/gqlgen/client"
11 "github.com/99designs/gqlgen/graphql"
12 "github.com/99designs/gqlgen/graphql/handler"
13 )
14
15 type ckey string
16
17 func TestDirectives(t *testing.T) {
18 resolvers := &Stub{}
19 ok := "Ok"
20 resolvers.QueryResolver.DirectiveArg = func(ctx context.Context, arg string) (i *string, e error) {
21 return &ok, nil
22 }
23
24 resolvers.QueryResolver.DirectiveInput = func(ctx context.Context, arg InputDirectives) (i *string, e error) {
25 return &ok, nil
26 }
27
28 resolvers.QueryResolver.DirectiveInputNullable = func(ctx context.Context, arg *InputDirectives) (i *string, e error) {
29 return &ok, nil
30 }
31
32 resolvers.QueryResolver.DirectiveNullableArg = func(ctx context.Context, arg *int, arg2 *int, arg3 *string) (*string, error) {
33 return &ok, nil
34 }
35
36 resolvers.QueryResolver.DirectiveInputType = func(ctx context.Context, arg InnerInput) (i *string, e error) {
37 return &ok, nil
38 }
39
40 resolvers.QueryResolver.DirectiveObject = func(ctx context.Context) (*ObjectDirectives, error) {
41 return &ObjectDirectives{
42 Text: ok,
43 NullableText: &ok,
44 }, nil
45 }
46
47 resolvers.QueryResolver.DirectiveObjectWithCustomGoModel = func(ctx context.Context) (*ObjectDirectivesWithCustomGoModel, error) {
48 return &ObjectDirectivesWithCustomGoModel{
49 NullableText: ok,
50 }, nil
51 }
52
53 resolvers.QueryResolver.DirectiveField = func(ctx context.Context) (*string, error) {
54 if s, ok := ctx.Value(ckey("request_id")).(*string); ok {
55 return s, nil
56 }
57
58 return nil, nil
59 }
60
61 resolvers.QueryResolver.DirectiveDouble = func(ctx context.Context) (*string, error) {
62 return &ok, nil
63 }
64
65 resolvers.QueryResolver.DirectiveUnimplemented = func(ctx context.Context) (*string, error) {
66 return &ok, nil
67 }
68
69 okchan := func() (<-chan *string, error) {
70 res := make(chan *string, 1)
71 res <- &ok
72 close(res)
73 return res, nil
74 }
75
76 resolvers.SubscriptionResolver.DirectiveArg = func(ctx context.Context, arg string) (strings <-chan *string, e error) {
77 return okchan()
78 }
79
80 resolvers.SubscriptionResolver.DirectiveNullableArg = func(ctx context.Context, arg *int, arg2 *int, arg3 *string) (strings <-chan *string, e error) {
81 return okchan()
82 }
83
84 resolvers.SubscriptionResolver.DirectiveDouble = func(ctx context.Context) (strings <-chan *string, e error) {
85 return okchan()
86 }
87
88 resolvers.SubscriptionResolver.DirectiveUnimplemented = func(ctx context.Context) (<-chan *string, error) {
89 return okchan()
90 }
91 srv := handler.NewDefaultServer(NewExecutableSchema(Config{
92 Resolvers: resolvers,
93 Directives: DirectiveRoot{
94 Length: func(ctx context.Context, obj interface{}, next graphql.Resolver, min int, max *int, message *string) (interface{}, error) {
95 e := func(msg string) error {
96 if message == nil {
97 return fmt.Errorf(msg)
98 }
99 return fmt.Errorf(*message)
100 }
101 res, err := next(ctx)
102 if err != nil {
103 return nil, err
104 }
105
106 s := res.(string)
107 if len(s) < min {
108 return nil, e("too short")
109 }
110 if max != nil && len(s) > *max {
111 return nil, e("too long")
112 }
113 return res, nil
114 },
115 Range: func(ctx context.Context, obj interface{}, next graphql.Resolver, min *int, max *int) (interface{}, error) {
116 res, err := next(ctx)
117 if err != nil {
118 return nil, err
119 }
120
121 switch res := res.(type) {
122 case int:
123 if min != nil && res < *min {
124 return nil, fmt.Errorf("too small")
125 }
126 if max != nil && res > *max {
127 return nil, fmt.Errorf("too large")
128 }
129 return next(ctx)
130
131 case int64:
132 if min != nil && int(res) < *min {
133 return nil, fmt.Errorf("too small")
134 }
135 if max != nil && int(res) > *max {
136 return nil, fmt.Errorf("too large")
137 }
138 return next(ctx)
139
140 case *int:
141 if min != nil && *res < *min {
142 return nil, fmt.Errorf("too small")
143 }
144 if max != nil && *res > *max {
145 return nil, fmt.Errorf("too large")
146 }
147 return next(ctx)
148 }
149 return nil, fmt.Errorf("unsupported type %T", res)
150 },
151 Custom: func(ctx context.Context, obj interface{}, next graphql.Resolver) (interface{}, error) {
152 return next(ctx)
153 },
154 Logged: func(ctx context.Context, obj interface{}, next graphql.Resolver, id string) (interface{}, error) {
155 return next(context.WithValue(ctx, ckey("request_id"), &id))
156 },
157 ToNull: func(ctx context.Context, obj interface{}, next graphql.Resolver) (interface{}, error) {
158 return nil, nil
159 },
160 Directive1: func(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) {
161 return next(ctx)
162 },
163 Directive2: func(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) {
164 return next(ctx)
165 },
166 Directive3: func(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) {
167 return next(ctx)
168 },
169 Order1: func(ctx context.Context, obj interface{}, next graphql.Resolver, location string) (res interface{}, err error) {
170 order := []string{location}
171 res, err = next(ctx)
172 od := res.(*ObjectDirectives)
173 od.Order = append(order, od.Order...)
174 return od, err
175 },
176 Order2: func(ctx context.Context, obj interface{}, next graphql.Resolver, location string) (res interface{}, err error) {
177 order := []string{location}
178 res, err = next(ctx)
179 od := res.(*ObjectDirectives)
180 od.Order = append(order, od.Order...)
181 return od, err
182 },
183 Unimplemented: nil,
184 },
185 }))
186
187 srv.AroundFields(func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) {
188 path, _ := ctx.Value(ckey("path")).([]int)
189 return next(context.WithValue(ctx, ckey("path"), append(path, 1)))
190 })
191
192 srv.AroundFields(func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) {
193 path, _ := ctx.Value(ckey("path")).([]int)
194 return next(context.WithValue(ctx, ckey("path"), append(path, 2)))
195 })
196
197 c := client.New(srv)
198
199 t.Run("arg directives", func(t *testing.T) {
200 t.Run("when function errors on directives", func(t *testing.T) {
201 var resp struct {
202 DirectiveArg *string
203 }
204
205 err := c.Post(`query { directiveArg(arg: "") }`, &resp)
206
207 require.EqualError(t, err, `[{"message":"invalid length","path":["directiveArg","arg"]}]`)
208 require.Nil(t, resp.DirectiveArg)
209 })
210 t.Run("when function errors on nullable arg directives", func(t *testing.T) {
211 var resp struct {
212 DirectiveNullableArg *string
213 }
214
215 err := c.Post(`query { directiveNullableArg(arg: -100) }`, &resp)
216
217 require.EqualError(t, err, `[{"message":"too small","path":["directiveNullableArg","arg"]}]`)
218 require.Nil(t, resp.DirectiveNullableArg)
219 })
220 t.Run("when function success on nullable arg directives", func(t *testing.T) {
221 var resp struct {
222 DirectiveNullableArg *string
223 }
224
225 err := c.Post(`query { directiveNullableArg }`, &resp)
226
227 require.Nil(t, err)
228 require.Equal(t, "Ok", *resp.DirectiveNullableArg)
229 })
230 t.Run("when function success on valid nullable arg directives", func(t *testing.T) {
231 var resp struct {
232 DirectiveNullableArg *string
233 }
234
235 err := c.Post(`query { directiveNullableArg(arg: 1) }`, &resp)
236
237 require.Nil(t, err)
238 require.Equal(t, "Ok", *resp.DirectiveNullableArg)
239 })
240 t.Run("when function success", func(t *testing.T) {
241 var resp struct {
242 DirectiveArg *string
243 }
244
245 err := c.Post(`query { directiveArg(arg: "test") }`, &resp)
246
247 require.Nil(t, err)
248 require.Equal(t, "Ok", *resp.DirectiveArg)
249 })
250 })
251 t.Run("field definition directives", func(t *testing.T) {
252 resolvers.QueryResolver.DirectiveFieldDef = func(ctx context.Context, ret string) (i string, e error) {
253 return ret, nil
254 }
255
256 t.Run("too short", func(t *testing.T) {
257 var resp struct {
258 DirectiveFieldDef string
259 }
260
261 err := c.Post(`query { directiveFieldDef(ret: "") }`, &resp)
262
263 require.EqualError(t, err, `[{"message":"not valid","path":["directiveFieldDef"]}]`)
264 })
265
266 t.Run("has 2 directives", func(t *testing.T) {
267 var resp struct {
268 DirectiveDouble string
269 }
270
271 c.MustPost(`query { directiveDouble }`, &resp)
272
273 require.Equal(t, "Ok", resp.DirectiveDouble)
274 })
275
276 t.Run("directive is not implemented", func(t *testing.T) {
277 var resp struct {
278 DirectiveUnimplemented string
279 }
280
281 err := c.Post(`query { directiveUnimplemented }`, &resp)
282
283 require.EqualError(t, err, `[{"message":"directive unimplemented is not implemented","path":["directiveUnimplemented"]}]`)
284 })
285
286 t.Run("ok", func(t *testing.T) {
287 var resp struct {
288 DirectiveFieldDef string
289 }
290
291 c.MustPost(`query { directiveFieldDef(ret: "aaa") }`, &resp)
292
293 require.Equal(t, "aaa", resp.DirectiveFieldDef)
294 })
295 })
296 t.Run("field directives", func(t *testing.T) {
297 t.Run("add field directive", func(t *testing.T) {
298 var resp struct {
299 DirectiveField string
300 }
301
302 c.MustPost(`query { directiveField@logged(id:"testes_id") }`, &resp)
303
304 require.Equal(t, resp.DirectiveField, `testes_id`)
305 })
306 t.Run("without field directive", func(t *testing.T) {
307 var resp struct {
308 DirectiveField *string
309 }
310
311 c.MustPost(`query { directiveField }`, &resp)
312
313 require.Nil(t, resp.DirectiveField)
314 })
315 })
316 t.Run("input field directives", func(t *testing.T) {
317 t.Run("when function errors on directives", func(t *testing.T) {
318 var resp struct {
319 DirectiveInputNullable *string
320 }
321
322 err := c.Post(`query { directiveInputNullable(arg: {text:"invalid text",inner:{message:"123"}}) }`, &resp)
323
324 require.EqualError(t, err, `[{"message":"not valid","path":["directiveInputNullable","arg","text"]}]`)
325 require.Nil(t, resp.DirectiveInputNullable)
326 })
327 t.Run("when function errors on inner directives", func(t *testing.T) {
328 var resp struct {
329 DirectiveInputNullable *string
330 }
331
332 err := c.Post(`query { directiveInputNullable(arg: {text:"2",inner:{message:""}}) }`, &resp)
333
334 require.EqualError(t, err, `[{"message":"not valid","path":["directiveInputNullable","arg","inner","message"]}]`)
335 require.Nil(t, resp.DirectiveInputNullable)
336 })
337 t.Run("when function errors on nullable inner directives", func(t *testing.T) {
338 var resp struct {
339 DirectiveInputNullable *string
340 }
341
342 err := c.Post(`query { directiveInputNullable(arg: {text:"success",inner:{message:"1"},innerNullable:{message:""}}) }`, &resp)
343
344 require.EqualError(t, err, `[{"message":"not valid","path":["directiveInputNullable","arg","innerNullable","message"]}]`)
345 require.Nil(t, resp.DirectiveInputNullable)
346 })
347 t.Run("when function success", func(t *testing.T) {
348 var resp struct {
349 DirectiveInputNullable *string
350 }
351
352 err := c.Post(`query { directiveInputNullable(arg: {text:"23",inner:{message:"1"}}) }`, &resp)
353
354 require.Nil(t, err)
355 require.Equal(t, "Ok", *resp.DirectiveInputNullable)
356 })
357 t.Run("when function inner nullable success", func(t *testing.T) {
358 var resp struct {
359 DirectiveInputNullable *string
360 }
361
362 err := c.Post(`query { directiveInputNullable(arg: {text:"23",nullableText:"23",inner:{message:"1"},innerNullable:{message:"success"}}) }`, &resp)
363
364 require.Nil(t, err)
365 require.Equal(t, "Ok", *resp.DirectiveInputNullable)
366 })
367 t.Run("when arg has directive", func(t *testing.T) {
368 var resp struct {
369 DirectiveInputType *string
370 }
371
372 err := c.Post(`query { directiveInputType(arg: {id: 1}) }`, &resp)
373
374 require.Nil(t, err)
375 require.Equal(t, "Ok", *resp.DirectiveInputType)
376 })
377 })
378 t.Run("object field directives", func(t *testing.T) {
379 t.Run("when function success", func(t *testing.T) {
380 var resp struct {
381 DirectiveObject *struct {
382 Text string
383 NullableText *string
384 Order []string
385 }
386 }
387
388 err := c.Post(`query { directiveObject{ text nullableText order} }`, &resp)
389
390 require.Nil(t, err)
391 require.Equal(t, "Ok", resp.DirectiveObject.Text)
392 require.True(t, resp.DirectiveObject.NullableText == nil)
393 require.Equal(t, "Query_field", resp.DirectiveObject.Order[0])
394 require.Equal(t, "order2_1", resp.DirectiveObject.Order[1])
395 require.Equal(t, "order1_2", resp.DirectiveObject.Order[2])
396 require.Equal(t, "order1_1", resp.DirectiveObject.Order[3])
397 })
398 t.Run("when directive returns nil & custom go field is not nilable", func(t *testing.T) {
399 var resp struct {
400 DirectiveObjectWithCustomGoModel *struct {
401 NullableText *string
402 }
403 }
404
405 err := c.Post(`query { directiveObjectWithCustomGoModel{ nullableText } }`, &resp)
406
407 require.Nil(t, err)
408 require.True(t, resp.DirectiveObjectWithCustomGoModel.NullableText == nil)
409 })
410 })
411
412 t.Run("Subscription directives", func(t *testing.T) {
413 t.Run("arg directives", func(t *testing.T) {
414 t.Run("when function errors on directives", func(t *testing.T) {
415 var resp struct {
416 DirectiveArg *string
417 }
418
419 err := c.WebsocketOnce(`subscription { directiveArg(arg: "") }`, &resp)
420
421 require.EqualError(t, err, `[{"message":"invalid length","path":["directiveArg","arg"]}]`)
422 require.Nil(t, resp.DirectiveArg)
423 })
424 t.Run("when function errors on nullable arg directives", func(t *testing.T) {
425 var resp struct {
426 DirectiveNullableArg *string
427 }
428
429 err := c.WebsocketOnce(`subscription { directiveNullableArg(arg: -100) }`, &resp)
430
431 require.EqualError(t, err, `[{"message":"too small","path":["directiveNullableArg","arg"]}]`)
432 require.Nil(t, resp.DirectiveNullableArg)
433 })
434 t.Run("when function success on nullable arg directives", func(t *testing.T) {
435 var resp struct {
436 DirectiveNullableArg *string
437 }
438
439 err := c.WebsocketOnce(`subscription { directiveNullableArg }`, &resp)
440
441 require.Nil(t, err)
442 require.Equal(t, "Ok", *resp.DirectiveNullableArg)
443 })
444 t.Run("when function success on valid nullable arg directives", func(t *testing.T) {
445 var resp struct {
446 DirectiveNullableArg *string
447 }
448
449 err := c.WebsocketOnce(`subscription { directiveNullableArg(arg: 1) }`, &resp)
450
451 require.Nil(t, err)
452 require.Equal(t, "Ok", *resp.DirectiveNullableArg)
453 })
454 t.Run("when function success", func(t *testing.T) {
455 var resp struct {
456 DirectiveArg *string
457 }
458
459 err := c.WebsocketOnce(`subscription { directiveArg(arg: "test") }`, &resp)
460
461 require.Nil(t, err)
462 require.Equal(t, "Ok", *resp.DirectiveArg)
463 })
464 })
465 })
466 }
467
View as plain text