github.com/99designs/gqlgen@v0.17.45/codegen/testserver/singlefile/nulls_test.go (about) 1 package singlefile 2 3 import ( 4 "context" 5 "errors" 6 "testing" 7 8 "github.com/stretchr/testify/require" 9 10 "github.com/99designs/gqlgen/client" 11 "github.com/99designs/gqlgen/graphql/handler" 12 ) 13 14 func TestNullBubbling(t *testing.T) { 15 resolvers := &Stub{} 16 resolvers.QueryResolver.Valid = func(ctx context.Context) (s string, e error) { 17 return "Ok", nil 18 } 19 resolvers.QueryResolver.Invalid = func(ctx context.Context) (s string, e error) { 20 return "Ok", errors.New("ERROR") 21 } 22 resolvers.QueryResolver.Errors = func(ctx context.Context) (errors *Errors, e error) { 23 return &Errors{}, nil 24 } 25 resolvers.QueryResolver.ErrorBubble = func(ctx context.Context) (i *Error, e error) { 26 return &Error{ID: "E1234"}, nil 27 } 28 resolvers.QueryResolver.ErrorBubbleList = func(ctx context.Context) (i []*Error, e error) { 29 return []*Error{{ID: "1"}, nil, nil}, nil 30 } 31 resolvers.QueryResolver.ErrorList = func(ctx context.Context) (i []*Error, e error) { 32 return []*Error{nil}, nil 33 } 34 35 c := client.New(handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolvers}))) 36 37 t.Run("when function errors on non required field", func(t *testing.T) { 38 var resp struct { 39 Valid string 40 ErrorBubble *struct { 41 Id string 42 ErrorOnNonRequiredField *string 43 } 44 } 45 err := c.Post(`query { valid, errorBubble { id, errorOnNonRequiredField } }`, &resp) 46 47 require.EqualError(t, err, `[{"message":"boom","path":["errorBubble","errorOnNonRequiredField"]}]`) 48 require.Equal(t, "E1234", resp.ErrorBubble.Id) 49 require.Nil(t, resp.ErrorBubble.ErrorOnNonRequiredField) 50 require.Equal(t, "Ok", resp.Valid) 51 }) 52 53 t.Run("when function errors", func(t *testing.T) { 54 var resp struct { 55 Valid string 56 ErrorBubble *struct { 57 NilOnRequiredField string 58 } 59 } 60 err := c.Post(`query { valid, errorBubble { id, errorOnRequiredField } }`, &resp) 61 62 require.EqualError(t, err, `[{"message":"boom","path":["errorBubble","errorOnRequiredField"]}]`) 63 require.Nil(t, resp.ErrorBubble) 64 require.Equal(t, "Ok", resp.Valid) 65 }) 66 67 t.Run("when user returns null on required field", func(t *testing.T) { 68 var resp struct { 69 Valid string 70 ErrorBubble *struct { 71 NilOnRequiredField string 72 } 73 } 74 err := c.Post(`query { valid, errorBubble { id, nilOnRequiredField } }`, &resp) 75 76 require.EqualError(t, err, `[{"message":"the requested element is null which the schema does not allow","path":["errorBubble","nilOnRequiredField"]}]`) 77 require.Nil(t, resp.ErrorBubble) 78 require.Equal(t, "Ok", resp.Valid) 79 }) 80 81 t.Run("when list element is null", func(t *testing.T) { 82 var resp struct { 83 Valid string 84 ErrorList []*struct{} 85 } 86 err := c.Post(`query { valid, errorList { id } }`, &resp) 87 88 require.Nil(t, err) 89 require.Equal(t, len(resp.ErrorList), 1) 90 require.Nil(t, resp.ErrorList[0]) 91 require.Equal(t, "Ok", resp.Valid) 92 }) 93 94 t.Run("when non-null list element is null", func(t *testing.T) { 95 var resp struct { 96 Valid string 97 ErrorBubbleList []*struct{} 98 } 99 err := c.Post(`query { valid, errorBubbleList { id } }`, &resp) 100 101 require.Contains(t, err.Error(), `{"message":"the requested element is null which the schema does not allow","path":["errorBubbleList",2]}`) 102 require.Contains(t, err.Error(), `{"message":"the requested element is null which the schema does not allow","path":["errorBubbleList",1]}`) 103 require.Nil(t, resp.ErrorBubbleList) 104 require.Equal(t, "Ok", resp.Valid) 105 }) 106 107 t.Run("null args", func(t *testing.T) { 108 var resp struct { 109 NullableArg *string 110 } 111 resolvers.QueryResolver.NullableArg = func(ctx context.Context, arg *int) (i *string, e error) { 112 v := "Ok" 113 return &v, nil 114 } 115 116 err := c.Post(`query { nullableArg(arg: null) }`, &resp) 117 require.Nil(t, err) 118 require.Equal(t, "Ok", *resp.NullableArg) 119 }) 120 121 t.Run("concurrent null detection", func(t *testing.T) { 122 var resp interface{} 123 resolvers.ErrorsResolver.A = func(ctx context.Context, obj *Errors) (i *Error, e error) { return nil, nil } 124 resolvers.ErrorsResolver.B = func(ctx context.Context, obj *Errors) (i *Error, e error) { return nil, nil } 125 resolvers.ErrorsResolver.C = func(ctx context.Context, obj *Errors) (i *Error, e error) { return nil, nil } 126 resolvers.ErrorsResolver.D = func(ctx context.Context, obj *Errors) (i *Error, e error) { return nil, nil } 127 resolvers.ErrorsResolver.E = func(ctx context.Context, obj *Errors) (i *Error, e error) { return nil, nil } 128 129 err := c.Post(`{ errors { 130 a { id }, 131 b { id }, 132 c { id }, 133 d { id }, 134 e { id }, 135 } }`, &resp) 136 137 require.Error(t, err) 138 require.Contains(t, err.Error(), "the requested element is null which the schema does not allow") 139 }) 140 141 t.Run("when non-nullable field returns content while error occurred", func(t *testing.T) { 142 var resp any 143 err := c.Post(`query { invalid }`, &resp) 144 require.Nil(t, resp) 145 require.NotNil(t, err) 146 require.Contains(t, err.Error(), `{"message":"ERROR","path":["invalid"]}`) 147 }) 148 }