1 package validator_test
2
3 import (
4 "os"
5 "testing"
6
7 "github.com/stretchr/testify/require"
8
9 "github.com/vektah/gqlparser/v2"
10 "github.com/vektah/gqlparser/v2/ast"
11 "github.com/vektah/gqlparser/v2/validator"
12 )
13
14 func TestValidateVars(t *testing.T) {
15 schema := gqlparser.MustLoadSchema(&ast.Source{
16 Name: "vars.graphql",
17 Input: mustReadFile("./testdata/vars.graphql"),
18 })
19
20 t.Run("undefined variable", func(t *testing.T) {
21 t.Run("without default", func(t *testing.T) {
22 q := gqlparser.MustLoadQuery(schema, `query($id: Int!) { intArg(i: $id) }`)
23 _, gerr := validator.VariableValues(schema, q.Operations.ForName(""), nil)
24 require.EqualError(t, gerr, "input: variable.id must be defined")
25 })
26
27 t.Run("nil in required value", func(t *testing.T) {
28 q := gqlparser.MustLoadQuery(schema, `query($id: Int!) { intArg(i: $id) }`)
29 _, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
30 "id": nil,
31 })
32 require.EqualError(t, gerr, "input: variable.id cannot be null")
33 })
34
35 t.Run("with default", func(t *testing.T) {
36 q := gqlparser.MustLoadQuery(schema, `query($id: Int! = 1) { intArg(i: $id) }`)
37 vars, gerr := validator.VariableValues(schema, q.Operations.ForName(""), nil)
38 require.Nil(t, gerr)
39 require.EqualValues(t, 1, vars["id"])
40 })
41
42 t.Run("with union", func(t *testing.T) {
43 q := gqlparser.MustLoadQuery(schema, `query($id: Int! = 1) { intArg(i: $id) }`)
44 vars, gerr := validator.VariableValues(schema, q.Operations.ForName(""), nil)
45 require.Nil(t, gerr)
46 require.EqualValues(t, 1, vars["id"])
47 })
48 })
49
50 t.Run("input object", func(t *testing.T) {
51 t.Run("non object", func(t *testing.T) {
52 q := gqlparser.MustLoadQuery(schema, `query foo($var: InputType!) { structArg(i: $var) }`)
53 _, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
54 "var": "hello",
55 })
56 require.EqualError(t, gerr, "input: variable.var must be a InputType")
57 })
58
59 t.Run("defaults", func(t *testing.T) {
60 q := gqlparser.MustLoadQuery(schema, `query foo($var: InputType! = {name: "foo"}) { structArg(i: $var) }`)
61 vars, gerr := validator.VariableValues(schema, q.Operations.ForName(""), nil)
62 require.Nil(t, gerr)
63 require.EqualValues(t, map[string]interface{}{"name": "foo"}, vars["var"])
64 })
65
66 t.Run("valid value", func(t *testing.T) {
67 q := gqlparser.MustLoadQuery(schema, `query foo($var: InputType!) { structArg(i: $var) }`)
68 vars, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
69 "var": map[string]interface{}{
70 "name": "foobar",
71 },
72 })
73 require.Nil(t, gerr)
74 require.EqualValues(t, map[string]interface{}{"name": "foobar"}, vars["var"])
75 })
76
77 t.Run("null object field", func(t *testing.T) {
78 q := gqlparser.MustLoadQuery(schema, `query foo($var: InputType!) { structArg(i: $var) }`)
79 vars, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
80 "var": map[string]interface{}{
81 "name": "foobar",
82 "nullName": nil,
83 },
84 })
85 require.Nil(t, gerr)
86 require.EqualValues(t, map[string]interface{}{"name": "foobar", "nullName": nil}, vars["var"])
87 })
88
89 t.Run("missing required values", func(t *testing.T) {
90 q := gqlparser.MustLoadQuery(schema, `query foo($var: InputType!) { structArg(i: $var) }`)
91 _, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
92 "var": map[string]interface{}{},
93 })
94 require.EqualError(t, gerr, "input: variable.var.name must be defined")
95 })
96
97 t.Run("null required field", func(t *testing.T) {
98 q := gqlparser.MustLoadQuery(schema, `query foo($var: InputType!) { structArg(i: $var) }`)
99 _, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
100 "var": map[string]interface{}{
101 "name": nil,
102 },
103 })
104 require.EqualError(t, gerr, "input: variable.var.name cannot be null")
105 })
106
107 t.Run("null embedded input object", func(t *testing.T) {
108 q := gqlparser.MustLoadQuery(schema, `query foo($var: InputType!) { structArg(i: $var) }`)
109 _, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
110 "var": map[string]interface{}{
111 "name": "foo",
112 "nullEmbedded": nil,
113 },
114 })
115 require.Nil(t, gerr)
116 })
117
118 t.Run("unknown field", func(t *testing.T) {
119 q := gqlparser.MustLoadQuery(schema, `query foo($var: InputType!) { structArg(i: $var) }`)
120 _, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
121 "var": map[string]interface{}{
122 "name": "foobar",
123 "foobard": true,
124 },
125 })
126 require.EqualError(t, gerr, "input: variable.var.foobard unknown field")
127 })
128
129 t.Run("unknown __typefield", func(t *testing.T) {
130 q := gqlparser.MustLoadQuery(schema, `query foo($var: InputType!) { structArg(i: $var) }`)
131 vars, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
132 "var": map[string]interface{}{
133 "name": "foobar",
134 "__typename": "InputType",
135 },
136 })
137 require.Nil(t, gerr)
138 require.EqualValues(t, map[string]interface{}{"__typename": "InputType", "name": "foobar"}, vars["var"])
139
140 })
141
142 t.Run("enum input object", func(t *testing.T) {
143 q := gqlparser.MustLoadQuery(schema, `query foo($var: InputType!) { structArg(i: $var) }`)
144 _, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
145 "var": map[string]interface{}{
146 "name": "foobar",
147 "enum": "A",
148 },
149 })
150 require.Nil(t, gerr)
151 })
152
153 t.Run("unknown enum value input object", func(t *testing.T) {
154 q := gqlparser.MustLoadQuery(schema, `query foo($var: InputType!) { structArg(i: $var) }`)
155 _, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
156 "var": map[string]interface{}{
157 "name": "foobar",
158 "enum": "B",
159 },
160 })
161 require.EqualError(t, gerr, "input: variable.var.enum B is not a valid Enum")
162 })
163 })
164
165 t.Run("array", func(t *testing.T) {
166 t.Run("non-null object value should be coerced to an array", func(t *testing.T) {
167 q := gqlparser.MustLoadQuery(schema, `query foo($var: [InputType!]) { arrayArg(i: $var) }`)
168 vars, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
169 "var": map[string]interface{}{"name": "hello"},
170 })
171 require.Nil(t, gerr)
172 require.EqualValues(t, []map[string]interface{}{{"name": "hello"}}, vars["var"])
173 })
174
175 t.Run("non-null int value should be coerced to an array", func(t *testing.T) {
176 q := gqlparser.MustLoadQuery(schema, `query foo($var: [Int!]) { intArrayArg(i: $var) }`)
177 vars, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
178 "var": 5,
179 })
180 require.Nil(t, gerr)
181 expected := []int{5}
182 require.EqualValues(t, expected, vars["var"])
183 })
184
185 t.Run("non-null int deep value should be coerced to an array", func(t *testing.T) {
186 q := gqlparser.MustLoadQuery(schema, `query foo($var: [CustomType]) { typeArrayArg(i: $var) }`)
187 vars, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
188 "var": []map[string]interface{}{{"and": 5}},
189 })
190 require.Nil(t, gerr)
191 expected := []map[string]interface{}{{"and": []int{5}}}
192 require.EqualValues(t, expected, vars["var"])
193 })
194
195 t.Run("defaults", func(t *testing.T) {
196 q := gqlparser.MustLoadQuery(schema, `query foo($var: [InputType!] = [{name: "foo"}]) { arrayArg(i: $var) }`)
197 vars, gerr := validator.VariableValues(schema, q.Operations.ForName(""), nil)
198 require.Nil(t, gerr)
199 require.EqualValues(t, []interface{}{map[string]interface{}{
200 "name": "foo",
201 }}, vars["var"])
202 })
203
204 t.Run("valid value", func(t *testing.T) {
205 q := gqlparser.MustLoadQuery(schema, `query foo($var: [InputType!]) { arrayArg(i: $var) }`)
206 vars, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
207 "var": []interface{}{map[string]interface{}{
208 "name": "foo",
209 }},
210 })
211 require.Nil(t, gerr)
212 require.EqualValues(t, []interface{}{map[string]interface{}{
213 "name": "foo",
214 }}, vars["var"])
215 })
216
217 t.Run("null element value", func(t *testing.T) {
218 q := gqlparser.MustLoadQuery(schema, `query foo($var: [InputType!]) { arrayArg(i: $var) }`)
219 _, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
220 "var": []interface{}{nil},
221 })
222 require.EqualError(t, gerr, "input: variable.var[0] cannot be null")
223 })
224
225 t.Run("missing required values", func(t *testing.T) {
226 q := gqlparser.MustLoadQuery(schema, `query foo($var: [InputType!]) { arrayArg(i: $var) }`)
227 _, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
228 "var": []interface{}{map[string]interface{}{}},
229 })
230 require.EqualError(t, gerr, "input: variable.var[0].name must be defined")
231 })
232 t.Run("invalid variable paths", func(t *testing.T) {
233 q := gqlparser.MustLoadQuery(schema, `query foo($var1: InputType!, $var2: InputType!) { a:structArg(i: $var1) b:structArg(i: $var2) }`)
234 _, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
235 "var1": map[string]interface{}{
236 "name": "foobar",
237 },
238 "var2": map[string]interface{}{
239 "nullName": "foobar",
240 },
241 })
242 require.EqualError(t, gerr, "input: variable.var2.name must be defined")
243 })
244 })
245
246 t.Run("Scalars", func(t *testing.T) {
247 t.Run("String -> String", func(t *testing.T) {
248 q := gqlparser.MustLoadQuery(schema, `query foo($var: String!) { stringArg(i: $var) }`)
249 vars, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
250 "var": "asdf",
251 })
252 require.Nil(t, gerr)
253 require.EqualValues(t, "asdf", vars["var"])
254 })
255
256 t.Run("Int -> String", func(t *testing.T) {
257 q := gqlparser.MustLoadQuery(schema, `query foo($var: String!) { stringArg(i: $var) }`)
258 _, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
259 "var": 1,
260 })
261 require.EqualError(t, gerr, "input: variable.var cannot use int as String")
262 })
263
264 t.Run("Nil -> String", func(t *testing.T) {
265 q := gqlparser.MustLoadQuery(schema, `query foo($var: String!) { stringArg(i: $var) }`)
266 _, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
267 "var": nil,
268 })
269 require.EqualError(t, gerr, "input: variable.var cannot be null")
270 })
271
272 t.Run("Undefined -> String!", func(t *testing.T) {
273 q := gqlparser.MustLoadQuery(schema, `query foo($var: String!) { stringArg(i: $var) }`)
274 _, gerr := validator.VariableValues(schema, q.Operations.ForName(""), nil)
275 require.EqualError(t, gerr, "input: variable.var must be defined")
276 })
277
278 t.Run("Undefined -> Int", func(t *testing.T) {
279 q := gqlparser.MustLoadQuery(schema, `query foo($var: Int) { optionalIntArg(i: $var) }`)
280 _, gerr := validator.VariableValues(schema, q.Operations.ForName(""), nil)
281 require.Nil(t, gerr)
282 })
283
284 t.Run("Json Number -> Int", func(t *testing.T) {
285 q := gqlparser.MustLoadQuery(schema, `query foo($var: Int) { optionalIntArg(i: $var) }`)
286 vars, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
287 "var": 10,
288 })
289 require.Nil(t, gerr)
290 require.Equal(t, 10, vars["var"])
291 })
292
293 t.Run("Json Number -> Float", func(t *testing.T) {
294 q := gqlparser.MustLoadQuery(schema, `query foo($var: Float!) { floatArg(i: $var) }`)
295 vars, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
296 "var": 10.2,
297 })
298 require.Nil(t, gerr)
299 require.Equal(t, 10.2, vars["var"])
300 })
301
302 t.Run("Nil -> Int", func(t *testing.T) {
303 q := gqlparser.MustLoadQuery(schema, `query foo($var: Int) { optionalIntArg(i: $var) }`)
304 vars, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
305 "var": nil,
306 })
307 require.Nil(t, gerr)
308 require.Equal(t, nil, vars["var"])
309 })
310
311 t.Run("Bool -> Int", func(t *testing.T) {
312 q := gqlparser.MustLoadQuery(schema, `query foo($var: Int!) { intArg(i: $var) }`)
313 _, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
314 "var": true,
315 })
316 require.EqualError(t, gerr, "input: variable.var cannot use bool as Int")
317 })
318 })
319
320 t.Run("Int Array", func(t *testing.T) {
321 t.Run("Array with null", func(t *testing.T) {
322 q := gqlparser.MustLoadQuery(schema, `query foo($var: [Int]) { intArrayArg(i: $var) }`)
323 a := 1
324 b := 2
325
326 _, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
327 "var": []*int{&a, &b, nil},
328 })
329 require.Nil(t, gerr)
330 })
331 })
332
333 t.Run("String Array", func(t *testing.T) {
334 t.Run("Array with null", func(t *testing.T) {
335 q := gqlparser.MustLoadQuery(schema, `query foo($var: [String]) { stringArrayArg(i: $var) }`)
336 a := "1"
337 b := "2"
338
339 _, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
340 "var": []*string{&a, &b, nil},
341 })
342 require.Nil(t, gerr)
343 })
344 })
345
346 t.Run("Boolean Array", func(t *testing.T) {
347 t.Run("Array with null", func(t *testing.T) {
348 q := gqlparser.MustLoadQuery(schema, `query foo($var: [Boolean]) { boolArrayArg(i: $var) }`)
349 a := true
350 b := false
351
352 _, gerr := validator.VariableValues(schema, q.Operations.ForName(""), map[string]interface{}{
353 "var": []*bool{&a, &b, nil},
354 })
355 require.Nil(t, gerr)
356 })
357 })
358 }
359
360 func mustReadFile(name string) string {
361 src, err := os.ReadFile(name)
362 if err != nil {
363 panic(err)
364 }
365
366 return string(src)
367 }
368
View as plain text