graphql 1.5.4 → 1.5.5

Sign up to get free protection for your applications and to get access to all the features.
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