graphql 1.5.4 → 1.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/define/instance_definable.rb +54 -29
  3. data/lib/graphql/enum_type.rb +0 -1
  4. data/lib/graphql/execution/execute.rb +15 -9
  5. data/lib/graphql/execution/field_result.rb +3 -6
  6. data/lib/graphql/execution/lazy/lazy_method_map.rb +66 -12
  7. data/lib/graphql/language.rb +15 -1
  8. data/lib/graphql/language/nodes.rb +17 -1
  9. data/lib/graphql/language/parser.rb +8 -8
  10. data/lib/graphql/language/parser.y +8 -8
  11. data/lib/graphql/query.rb +3 -4
  12. data/lib/graphql/query/literal_input.rb +2 -0
  13. data/lib/graphql/relay/mutation.rb +2 -3
  14. data/lib/graphql/schema.rb +1 -3
  15. data/lib/graphql/schema/loader.rb +26 -4
  16. data/lib/graphql/static_validation/all_rules.rb +1 -0
  17. data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
  18. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +32 -0
  19. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +11 -5
  20. data/lib/graphql/static_validation/validation_context.rb +0 -1
  21. data/lib/graphql/version.rb +1 -1
  22. data/spec/graphql/define/instance_definable_spec.rb +21 -0
  23. data/spec/graphql/execution/execute_spec.rb +61 -0
  24. data/spec/graphql/execution/lazy/lazy_method_map_spec.rb +57 -0
  25. data/spec/graphql/input_object_type_spec.rb +2 -2
  26. data/spec/graphql/introspection/input_value_type_spec.rb +3 -1
  27. data/spec/graphql/introspection/type_type_spec.rb +1 -0
  28. data/spec/graphql/query_spec.rb +69 -10
  29. data/spec/graphql/schema/loader_spec.rb +7 -3
  30. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +0 -1
  31. data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +6 -6
  32. data/spec/graphql/static_validation/rules/no_definitions_are_present_spec.rb +28 -0
  33. data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +17 -0
  34. data/spec/spec_helper.rb +2 -1
  35. data/spec/support/dummy/schema.rb +11 -0
  36. data/spec/support/star_wars/data.rb +16 -2
  37. metadata +21 -30
@@ -265,7 +265,7 @@ rule
265
265
  | directive_definition
266
266
 
267
267
  schema_definition:
268
- SCHEMA LCURLY operation_type_definition_list RCURLY { return make_node(:SchemaDefinition, val[2]) }
268
+ SCHEMA LCURLY operation_type_definition_list RCURLY { return make_node(:SchemaDefinition, position_source: val[0], **val[2]) }
269
269
 
270
270
  operation_type_definition_list:
271
271
  operation_type_definition
@@ -282,11 +282,11 @@ rule
282
282
  | enum_type_definition
283
283
  | input_object_type_definition
284
284
 
285
- scalar_type_definition: SCALAR name directives_list_opt { return make_node(:ScalarTypeDefinition, name: val[1], directives: val[2], description: get_description(val[0])) }
285
+ scalar_type_definition: SCALAR name directives_list_opt { return make_node(:ScalarTypeDefinition, name: val[1], directives: val[2], description: get_description(val[0]), position_source: val[0]) }
286
286
 
287
287
  object_type_definition:
288
288
  TYPE name implements_opt directives_list_opt LCURLY field_definition_list RCURLY {
289
- return make_node(:ObjectTypeDefinition, name: val[1], interfaces: val[2], directives: val[3], fields: val[5], description: get_description(val[0]))
289
+ return make_node(:ObjectTypeDefinition, name: val[1], interfaces: val[2], directives: val[3], fields: val[5], description: get_description(val[0]), position_source: val[0])
290
290
  }
291
291
 
292
292
  implements_opt:
@@ -317,7 +317,7 @@ rule
317
317
 
318
318
  interface_type_definition:
319
319
  INTERFACE name directives_list_opt LCURLY field_definition_list RCURLY {
320
- return make_node(:InterfaceTypeDefinition, name: val[1], directives: val[2], fields: val[4], description: get_description(val[0]))
320
+ return make_node(:InterfaceTypeDefinition, name: val[1], directives: val[2], fields: val[4], description: get_description(val[0]), position_source: val[0])
321
321
  }
322
322
 
323
323
  union_members:
@@ -326,22 +326,22 @@ rule
326
326
 
327
327
  union_type_definition:
328
328
  UNION name directives_list_opt EQUALS union_members {
329
- return make_node(:UnionTypeDefinition, name: val[1], directives: val[2], types: val[4], description: get_description(val[0]))
329
+ return make_node(:UnionTypeDefinition, name: val[1], directives: val[2], types: val[4], description: get_description(val[0]), position_source: val[0])
330
330
  }
331
331
 
332
332
  enum_type_definition:
333
333
  ENUM name directives_list_opt LCURLY enum_value_definitions RCURLY {
334
- return make_node(:EnumTypeDefinition, name: val[1], directives: val[2], values: val[4], description: get_description(val[0]))
334
+ return make_node(:EnumTypeDefinition, name: val[1], directives: val[2], values: val[4], description: get_description(val[0]), position_source: val[0])
335
335
  }
336
336
 
337
337
  input_object_type_definition:
338
338
  INPUT name directives_list_opt LCURLY input_value_definition_list RCURLY {
339
- return make_node(:InputObjectTypeDefinition, name: val[1], directives: val[2], fields: val[4], description: get_description(val[0]))
339
+ return make_node(:InputObjectTypeDefinition, name: val[1], directives: val[2], fields: val[4], description: get_description(val[0]), position_source: val[0])
340
340
  }
341
341
 
342
342
  directive_definition:
343
343
  DIRECTIVE DIR_SIGN name arguments_definitions_opt ON directive_locations {
344
- return make_node(:DirectiveDefinition, name: val[2], arguments: val[3], locations: val[5], description: get_description(val[0]))
344
+ return make_node(:DirectiveDefinition, name: val[2], arguments: val[3], locations: val[5], description: get_description(val[0]), position_source: val[0])
345
345
  }
346
346
 
347
347
  directive_locations:
data/lib/graphql/query.rb CHANGED
@@ -65,12 +65,11 @@ module GraphQL
65
65
  end
66
66
 
67
67
  @document && @document.definitions.each do |part|
68
- if part.is_a?(GraphQL::Language::Nodes::FragmentDefinition)
68
+ case part
69
+ when GraphQL::Language::Nodes::FragmentDefinition
69
70
  @fragments[part.name] = part
70
- elsif part.is_a?(GraphQL::Language::Nodes::OperationDefinition)
71
+ when GraphQL::Language::Nodes::OperationDefinition
71
72
  @operations[part.name] = part
72
- else
73
- raise GraphQL::ExecutionError, "GraphQL query cannot contain a schema definition"
74
73
  end
75
74
  end
76
75
 
@@ -7,6 +7,8 @@ module GraphQL
7
7
  case ast_node
8
8
  when nil
9
9
  nil
10
+ when Language::Nodes::NullValue
11
+ nil
10
12
  when Language::Nodes::VariableIdentifier
11
13
  variables[ast_node.name]
12
14
  else
@@ -169,9 +169,8 @@ module GraphQL
169
169
  input_field :clientMutationId, types.String, "A unique identifier for the client performing the mutation."
170
170
  relay_mutation.arguments.each do |input_field_name, field_obj|
171
171
  kwargs = {}
172
- if field_obj.default_value?
173
- kwargs[:default_value] = field_obj.default_value
174
- end
172
+ kwargs[:default_value] = field_obj.default_value if field_obj.default_value?
173
+ kwargs[:as] = field_obj.as
175
174
  input_field(input_field_name, field_obj.type, field_obj.description, **kwargs)
176
175
  end
177
176
  mutation(relay_mutation)
@@ -126,8 +126,7 @@ module GraphQL
126
126
 
127
127
  @possible_types = GraphQL::Schema::PossibleTypes.new(self)
128
128
 
129
- @lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new
130
- other.lazy_methods.each { |lazy_class, lazy_method| @lazy_methods.set(lazy_class, lazy_method) }
129
+ @lazy_methods = other.lazy_methods.dup
131
130
 
132
131
  @instrumenters = Hash.new { |h, k| h[k] = [] }
133
132
  other.instrumenters.each do |key, insts|
@@ -235,7 +234,6 @@ module GraphQL
235
234
  @possible_types.possible_types(type_defn)
236
235
  end
237
236
 
238
-
239
237
  # @see [GraphQL::Schema::Warden] Resticted access to root types
240
238
  # @return [GraphQL::ObjectType, nil]
241
239
  def root_type_for_operation(operation)
@@ -109,10 +109,32 @@ module GraphQL
109
109
  kwargs = {}
110
110
  if type["defaultValue"]
111
111
  kwargs[:default_value] = begin
112
- JSON.parse(type["defaultValue"], quirks_mode: true)
113
- rescue JSON::ParserError
114
- # Enum values are not valid JSON, they're bare identifiers
115
- type["default_value"]
112
+ default_value_str = type["defaultValue"]
113
+
114
+ dummy_query_str = "query getStuff($var: InputObj = #{default_value_str}) { __typename }"
115
+
116
+ # Returns a `GraphQL::Language::Nodes::Document`:
117
+ dummy_query_ast = GraphQL.parse(dummy_query_str)
118
+
119
+ # Reach into the AST for the default value:
120
+ input_value_ast = dummy_query_ast.definitions.first.variables.first.default_value
121
+
122
+ case input_value_ast
123
+ when String, Integer, Float, TrueClass, FalseClass, Array
124
+ input_value_ast
125
+ when GraphQL::Language::Nodes::Enum
126
+ input_value_ast.name
127
+ when GraphQL::Language::Nodes::NullValue
128
+ nil
129
+ when GraphQL::Language::Nodes::InputObject
130
+ input_value_ast.to_h
131
+ else
132
+ raise(
133
+ "Encountered unexpected type when loading default value. "\
134
+ "input_value_ast.class is #{input_value_ast.class} "\
135
+ "default_value is #{default_value_str}"
136
+ )
137
+ end
116
138
  end
117
139
  end
118
140
 
@@ -7,6 +7,7 @@ module GraphQL
7
7
  # which stops the visit on that node. That way it doesn't try to find fields on types that
8
8
  # don't exist, etc.
9
9
  ALL_RULES = [
10
+ GraphQL::StaticValidation::NoDefinitionsArePresent,
10
11
  GraphQL::StaticValidation::DirectivesAreDefined,
11
12
  GraphQL::StaticValidation::DirectivesAreInValidLocations,
12
13
  GraphQL::StaticValidation::UniqueDirectivesPerLocation,
@@ -32,7 +32,6 @@ module GraphQL
32
32
  @dependency_map ||= resolve_dependencies(&block)
33
33
  end
34
34
 
35
-
36
35
  def mount(context)
37
36
  visitor = context.visitor
38
37
  # When we encounter a spread,
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module StaticValidation
4
+ class NoDefinitionsArePresent
5
+ include GraphQL::StaticValidation::Message::MessageHelper
6
+
7
+ def validate(context)
8
+ schema_definition_nodes = []
9
+ register_node = ->(node, _p) {
10
+ schema_definition_nodes << node
11
+ GraphQL::Language::Visitor::SKIP
12
+ }
13
+
14
+ visitor = context.visitor
15
+ visitor[GraphQL::Language::Nodes::DirectiveDefinition] << register_node
16
+ visitor[GraphQL::Language::Nodes::SchemaDefinition] << register_node
17
+ visitor[GraphQL::Language::Nodes::ScalarTypeDefinition] << register_node
18
+ visitor[GraphQL::Language::Nodes::ObjectTypeDefinition] << register_node
19
+ visitor[GraphQL::Language::Nodes::InputObjectTypeDefinition] << register_node
20
+ visitor[GraphQL::Language::Nodes::InterfaceTypeDefinition] << register_node
21
+ visitor[GraphQL::Language::Nodes::UnionTypeDefinition] << register_node
22
+ visitor[GraphQL::Language::Nodes::EnumTypeDefinition] << register_node
23
+
24
+ visitor[GraphQL::Language::Nodes::Document].leave << ->(node, _p) {
25
+ if schema_definition_nodes.any?
26
+ context.errors << message(%|Query cannot contain schema definitions|, schema_definition_nodes, context: context)
27
+ end
28
+ }
29
+ end
30
+ end
31
+ end
32
+ end
@@ -14,19 +14,25 @@ module GraphQL
14
14
 
15
15
  context.visitor[GraphQL::Language::Nodes::Argument] << ->(node, parent) {
16
16
  return if !node.value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
17
- if parent.is_a?(GraphQL::Language::Nodes::Field)
17
+ arguments = nil
18
+
19
+ case parent
20
+ when GraphQL::Language::Nodes::Field
18
21
  arguments = context.field_definition.arguments
19
- elsif parent.is_a?(GraphQL::Language::Nodes::Directive)
22
+ when GraphQL::Language::Nodes::Directive
20
23
  arguments = context.directive_definition.arguments
21
- elsif parent.is_a?(GraphQL::Language::Nodes::InputObject)
22
- arguments = context.argument_definition.type.unwrap.input_fields
24
+ when GraphQL::Language::Nodes::InputObject
25
+ arg_type = context.argument_definition.type.unwrap
26
+ if arg_type.is_a?(GraphQL::InputObjectType)
27
+ arguments = arg_type.input_fields
28
+ end
23
29
  else
24
30
  raise("Unexpected argument parent: #{parent}")
25
31
  end
26
32
  var_defn_ast = declared_variables[node.value.name]
27
33
  # Might be undefined :(
28
34
  # VariablesAreUsedAndDefined can't finalize its search until the end of the document.
29
- var_defn_ast && validate_usage(arguments, node, var_defn_ast, context)
35
+ var_defn_ast && arguments && validate_usage(arguments, node, var_defn_ast, context)
30
36
  }
31
37
  end
32
38
 
@@ -36,7 +36,6 @@ module GraphQL
36
36
  }
37
37
  end
38
38
 
39
-
40
39
  def on_dependency_resolve(&handler)
41
40
  @on_dependency_resolve_handlers << handler
42
41
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.5.4"
3
+ VERSION = "1.5.5"
4
4
  end
@@ -78,6 +78,27 @@ describe GraphQL::Define::InstanceDefinable do
78
78
  okra.define { name "Okra" }
79
79
  assert_equal "Okra", okra.name
80
80
  end
81
+
82
+ describe "errors in define blocks" do
83
+ it "preserves the definition block to try again" do
84
+ magic_number = 12
85
+
86
+ radish = Garden::Vegetable.define {
87
+ name "Pre-error"
88
+ magic_number += 1
89
+ if magic_number == 13
90
+ raise "👻"
91
+ end
92
+ name "Radish"
93
+ }
94
+
95
+ # The first call triggers an error:
96
+ assert_raises(RuntimeError) { radish.name }
97
+ # Calling definintion-dependent method should re-run the block,
98
+ # not leave old values around:
99
+ assert_equal "Radish", radish.name
100
+ end
101
+ end
81
102
  end
82
103
 
83
104
  describe "#redefine" do
@@ -76,4 +76,65 @@ describe GraphQL::Execution::Execute do
76
76
  end
77
77
  end
78
78
  end
79
+
80
+ describe "when a list member raises an error" do
81
+ let(:schema) {
82
+ thing_type = GraphQL::ObjectType.define do
83
+ name "Thing"
84
+ field :name, !types.String do
85
+ resolve ->(o, a, c) {
86
+ -> {
87
+ raise GraphQL::ExecutionError.new("👻")
88
+ }
89
+ }
90
+ end
91
+ end
92
+
93
+ query_type = GraphQL::ObjectType.define do
94
+ name "Query"
95
+ field :things, !types[!thing_type] do
96
+ resolve ->(o, a, c) {
97
+ [OpenStruct.new(name: "A")]
98
+ }
99
+ end
100
+
101
+ field :nullableThings, !types[thing_type] do
102
+ resolve ->(o, a, c) {
103
+ [OpenStruct.new(name: "A")]
104
+ }
105
+ end
106
+ end
107
+
108
+ GraphQL::Schema.define do
109
+ query query_type
110
+ lazy_resolve(Proc, :call)
111
+ end
112
+ }
113
+
114
+ it "handles the error & propagates the null" do
115
+ res = schema.execute <<-GRAPHQL
116
+ {
117
+ things {
118
+ name
119
+ }
120
+ }
121
+ GRAPHQL
122
+
123
+ assert_equal nil, res["data"]
124
+ assert_equal "👻", res["errors"].first["message"]
125
+ end
126
+
127
+ it "allows nulls" do
128
+ res = schema.execute <<-GRAPHQL
129
+ {
130
+ nullableThings {
131
+ name
132
+ }
133
+ }
134
+ GRAPHQL
135
+
136
+ assert_equal [nil], res["data"]["nullableThings"]
137
+ assert_equal "👻", res["errors"].first["message"]
138
+ end
139
+ end
79
140
  end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
3
+
4
+ describe GraphQL::Execution::Lazy::LazyMethodMap do
5
+ def self.test_lazy_method_map
6
+ it "handles multithreaded access" do
7
+ a = Class.new
8
+ b = Class.new(a)
9
+ c = Class.new(b)
10
+ lazy_method_map.set(a, :a)
11
+ threads = 1000.times.map do |i|
12
+ Thread.new {
13
+ d = Class.new(c)
14
+ assert_equal :a, lazy_method_map.get(d.new)
15
+ }
16
+ end
17
+ threads.map(&:join)
18
+ end
19
+
20
+ it "dups" do
21
+ a = Class.new
22
+ b = Class.new(a)
23
+ c = Class.new(b)
24
+ lazy_method_map.set(a, :a)
25
+ lazy_method_map.get(b.new)
26
+ lazy_method_map.get(c.new)
27
+
28
+ dup_map = lazy_method_map.dup
29
+ assert_equal 3, dup_map.instance_variable_get(:@storage).size
30
+ assert_equal :a, dup_map.get(a.new)
31
+ assert_equal :a, dup_map.get(b.new)
32
+ assert_equal :a, dup_map.get(c.new)
33
+ end
34
+ end
35
+
36
+ describe "with a plain hash" do
37
+ let(:lazy_method_map) { GraphQL::Execution::Lazy::LazyMethodMap.new(use_concurrent: false) }
38
+ test_lazy_method_map
39
+
40
+ it "has a Ruby Hash inside" do
41
+ storage = lazy_method_map
42
+ .instance_variable_get(:@storage)
43
+ .instance_variable_get(:@storage)
44
+ assert_instance_of Hash, storage
45
+ end
46
+ end
47
+
48
+ describe "with a Concurrent::Map" do
49
+ let(:lazy_method_map) { GraphQL::Execution::Lazy::LazyMethodMap.new(use_concurrent: true) }
50
+ test_lazy_method_map
51
+
52
+ it "has a Concurrent::Map inside" do
53
+ storage = lazy_method_map.instance_variable_get(:@storage)
54
+ assert_instance_of Concurrent::Map, storage
55
+ end
56
+ end
57
+ end
@@ -325,8 +325,8 @@ describe GraphQL::InputObjectType do
325
325
  it "shallow-copies internal state" do
326
326
  input_object_2 = input_object.dup
327
327
  input_object_2.arguments["nonsense"] = GraphQL::Argument.define(name: "int", type: GraphQL::INT_TYPE)
328
- assert_equal 4, input_object.arguments.size
329
- assert_equal 5, input_object_2.arguments.size
328
+ assert_equal 5, input_object.arguments.size
329
+ assert_equal 6, input_object_2.arguments.size
330
330
  end
331
331
  end
332
332
  end
@@ -34,7 +34,9 @@ describe GraphQL::Introspection::InputValueType do
34
34
  {"name"=>"fatContent", "type"=>{"kind"=>"SCALAR", "name" => "Float"}, "defaultValue"=>"0.3",
35
35
  "description" => "How much fat it has"},
36
36
  {"name"=>"organic", "type"=>{"kind"=>"SCALAR", "name" => "Boolean"}, "defaultValue"=>"false",
37
- "description" => nil}
37
+ "description" => nil},
38
+ {"name"=>"order_by", "type"=>{"kind"=>"INPUT_OBJECT", "name"=>"ResourceOrderType"}, "defaultValue"=>"{direction:\"ASC\"}",
39
+ "description" => nil},
38
40
  ]
39
41
  }
40
42
  }}
@@ -122,6 +122,7 @@ describe GraphQL::Introspection::TypeType do
122
122
  {"name"=>"originDairy", "type"=>{"kind"=>"SCALAR","name"=>"String"}, "defaultValue"=>"\"Sugar Hollow Dairy\""},
123
123
  {"name"=>"fatContent", "type"=>{"kind"=>"SCALAR","name" => "Float"}, "defaultValue"=>"0.3"},
124
124
  {"name"=>"organic", "type"=>{"kind"=>"SCALAR","name" => "Boolean"}, "defaultValue"=>"false"},
125
+ {"name"=>"order_by", "type"=>{"kind"=>"INPUT_OBJECT", "name"=>"ResourceOrderType"}, "defaultValue"=>"{direction:\"ASC\"}"},
125
126
  ]
126
127
  }
127
128
  }}
@@ -143,16 +143,6 @@ describe GraphQL::Query do
143
143
  end
144
144
  end
145
145
 
146
- it "fails to execute a query containing a type definition" do
147
- query_string = '
148
- { root }
149
-
150
- type Query { foo: String }
151
- '
152
- exc = assert_raises(GraphQL::ExecutionError) { GraphQL::Query.new(schema, query_string) }
153
- assert_equal "GraphQL query cannot contain a schema definition", exc.message
154
- end
155
-
156
146
  it "uses root_value as the object for the root type" do
157
147
  result = GraphQL::Query.new(schema, '{ root }', root_value: "I am root").result
158
148
  assert_equal 'I am root', result.fetch('data').fetch('root')
@@ -470,4 +460,73 @@ describe GraphQL::Query do
470
460
  }
471
461
  end
472
462
  end
463
+
464
+ describe 'NullValue type arguments' do
465
+ let(:schema_definition) {
466
+ <<-GRAPHQL
467
+ type Query {
468
+ foo(id: [ID]): Int
469
+ }
470
+ GRAPHQL
471
+ }
472
+ let(:expected_args) { [] }
473
+ let(:default_resolver) do
474
+ {
475
+ 'Query' => { 'foo' => ->(_obj, args, _ctx) { expected_args.push(args); 1 } },
476
+ }
477
+ end
478
+ let(:schema) { GraphQL::Schema.from_definition(schema_definition, default_resolve: default_resolver) }
479
+
480
+ it 'sets argument to nil when null is passed' do
481
+ query = <<-GRAPHQL
482
+ {
483
+ foo(id: null)
484
+ }
485
+ GRAPHQL
486
+
487
+ schema.execute(query)
488
+
489
+ assert(expected_args.first.key?('id'))
490
+ assert_nil(expected_args.first['id'])
491
+ end
492
+
493
+ it 'sets argument to nil when nil is passed via variable' do
494
+ query = <<-GRAPHQL
495
+ query baz($id: [ID]) {
496
+ foo(id: $id)
497
+ }
498
+ GRAPHQL
499
+
500
+ schema.execute(query, variables: { 'id' => nil })
501
+
502
+ assert(expected_args.first.key?('id'))
503
+ assert([nil], expected_args.first['id'])
504
+ end
505
+
506
+ it 'sets argument to [nil] when [null] is passed' do
507
+ query = <<-GRAPHQL
508
+ {
509
+ foo(id: [null])
510
+ }
511
+ GRAPHQL
512
+
513
+ schema.execute(query)
514
+
515
+ assert(expected_args.first.key?('id'))
516
+ assert_equal([nil], expected_args.first['id'])
517
+ end
518
+
519
+ it 'sets argument to [nil] when [nil] is passed via variable' do
520
+ query = <<-GRAPHQL
521
+ query baz($id: [ID]) {
522
+ foo(id: $id)
523
+ }
524
+ GRAPHQL
525
+
526
+ schema.execute(query, variables: { 'id' => [nil] })
527
+
528
+ assert(expected_args.first.key?('id'))
529
+ assert_equal([nil], expected_args.first['id'])
530
+ end
531
+ end
473
532
  end