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