github.com/99designs/gqlgen@v0.17.45/codegen/testserver/singlefile/directive_test.go (about) 1 package singlefile 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.NoError(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.NoError(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.NoError(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.NoError(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.NoError(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.NoError(t, err) 442 require.NotNil(t, resp.DirectiveNullableArg) 443 require.Equal(t, "Ok", *resp.DirectiveNullableArg) 444 }) 445 t.Run("when function success on valid nullable arg directives", func(t *testing.T) { 446 var resp struct { 447 DirectiveNullableArg *string 448 } 449 450 err := c.WebsocketOnce(`subscription { directiveNullableArg(arg: 1) }`, &resp) 451 452 require.NoError(t, err) 453 require.NotNil(t, resp.DirectiveNullableArg) 454 require.Equal(t, "Ok", *resp.DirectiveNullableArg) 455 }) 456 t.Run("when function success", func(t *testing.T) { 457 var resp struct { 458 DirectiveArg *string 459 } 460 461 err := c.WebsocketOnce(`subscription { directiveArg(arg: "test") }`, &resp) 462 463 require.NoError(t, err) 464 require.NotNil(t, resp.DirectiveArg) 465 require.Equal(t, "Ok", *resp.DirectiveArg) 466 }) 467 }) 468 }) 469 }