graphql 1.4.5 → 1.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (139) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/enum_generator.rb +33 -0
  3. data/lib/generators/graphql/function_generator.rb +15 -0
  4. data/lib/generators/graphql/install_generator.rb +118 -0
  5. data/lib/generators/graphql/interface_generator.rb +27 -0
  6. data/lib/generators/graphql/loader_generator.rb +17 -0
  7. data/lib/generators/graphql/mutation_generator.rb +19 -0
  8. data/lib/generators/graphql/object_generator.rb +34 -0
  9. data/lib/generators/graphql/templates/enum.erb +4 -0
  10. data/lib/generators/graphql/templates/function.erb +17 -0
  11. data/lib/generators/graphql/templates/graphql_controller.erb +32 -0
  12. data/lib/generators/graphql/templates/interface.erb +4 -0
  13. data/lib/generators/graphql/templates/loader.erb +15 -0
  14. data/lib/generators/graphql/templates/mutation.erb +12 -0
  15. data/lib/generators/graphql/templates/object.erb +5 -0
  16. data/lib/generators/graphql/templates/query_type.erb +15 -0
  17. data/lib/generators/graphql/templates/schema.erb +34 -0
  18. data/lib/generators/graphql/templates/union.erb +4 -0
  19. data/lib/generators/graphql/type_generator.rb +78 -0
  20. data/lib/generators/graphql/union_generator.rb +33 -0
  21. data/lib/graphql.rb +10 -0
  22. data/lib/graphql/analysis/analyze_query.rb +1 -1
  23. data/lib/graphql/analysis/query_complexity.rb +6 -50
  24. data/lib/graphql/analysis/query_depth.rb +1 -1
  25. data/lib/graphql/argument.rb +21 -0
  26. data/lib/graphql/compatibility/execution_specification/counter_schema.rb +3 -3
  27. data/lib/graphql/define.rb +1 -0
  28. data/lib/graphql/define/assign_argument.rb +3 -19
  29. data/lib/graphql/define/assign_mutation_function.rb +34 -0
  30. data/lib/graphql/define/assign_object_field.rb +26 -14
  31. data/lib/graphql/define/defined_object_proxy.rb +21 -0
  32. data/lib/graphql/define/instance_definable.rb +61 -11
  33. data/lib/graphql/directive.rb +6 -1
  34. data/lib/graphql/execution/directive_checks.rb +1 -0
  35. data/lib/graphql/execution/execute.rb +14 -9
  36. data/lib/graphql/execution/field_result.rb +1 -0
  37. data/lib/graphql/execution/lazy.rb +8 -17
  38. data/lib/graphql/execution/lazy/lazy_method_map.rb +2 -0
  39. data/lib/graphql/execution/lazy/resolve.rb +1 -0
  40. data/lib/graphql/execution/selection_result.rb +1 -0
  41. data/lib/graphql/execution/typecast.rb +39 -26
  42. data/lib/graphql/field.rb +15 -3
  43. data/lib/graphql/field/resolve.rb +3 -3
  44. data/lib/graphql/function.rb +134 -0
  45. data/lib/graphql/id_type.rb +1 -1
  46. data/lib/graphql/input_object_type.rb +1 -1
  47. data/lib/graphql/internal_representation.rb +1 -1
  48. data/lib/graphql/internal_representation/node.rb +35 -107
  49. data/lib/graphql/internal_representation/rewrite.rb +189 -183
  50. data/lib/graphql/internal_representation/visit.rb +38 -0
  51. data/lib/graphql/introspection/input_value_type.rb +10 -1
  52. data/lib/graphql/introspection/schema_type.rb +1 -1
  53. data/lib/graphql/language/lexer.rb +6 -3
  54. data/lib/graphql/language/lexer.rl +6 -3
  55. data/lib/graphql/object_type.rb +53 -13
  56. data/lib/graphql/query.rb +30 -14
  57. data/lib/graphql/query/arguments.rb +2 -0
  58. data/lib/graphql/query/context.rb +2 -2
  59. data/lib/graphql/query/literal_input.rb +9 -0
  60. data/lib/graphql/query/serial_execution/field_resolution.rb +2 -2
  61. data/lib/graphql/query/serial_execution/selection_resolution.rb +1 -1
  62. data/lib/graphql/relay.rb +1 -0
  63. data/lib/graphql/relay/array_connection.rb +1 -1
  64. data/lib/graphql/relay/base_connection.rb +34 -15
  65. data/lib/graphql/relay/connection_resolve.rb +7 -2
  66. data/lib/graphql/relay/mutation.rb +45 -4
  67. data/lib/graphql/relay/node.rb +18 -6
  68. data/lib/graphql/relay/range_add.rb +45 -0
  69. data/lib/graphql/relay/relation_connection.rb +17 -2
  70. data/lib/graphql/runtime_type_error.rb +1 -0
  71. data/lib/graphql/schema.rb +40 -5
  72. data/lib/graphql/schema/base_64_encoder.rb +1 -0
  73. data/lib/graphql/schema/build_from_definition.rb +56 -21
  74. data/lib/graphql/schema/default_parse_error.rb +10 -0
  75. data/lib/graphql/schema/loader.rb +8 -1
  76. data/lib/graphql/schema/null_mask.rb +1 -0
  77. data/lib/graphql/schema/validation.rb +35 -0
  78. data/lib/graphql/static_validation.rb +1 -0
  79. data/lib/graphql/static_validation/all_rules.rb +1 -0
  80. data/lib/graphql/static_validation/arguments_validator.rb +7 -4
  81. data/lib/graphql/static_validation/definition_dependencies.rb +183 -0
  82. data/lib/graphql/static_validation/rules/fields_will_merge.rb +28 -96
  83. data/lib/graphql/static_validation/rules/fragment_names_are_unique.rb +23 -0
  84. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +8 -5
  85. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +6 -31
  86. data/lib/graphql/static_validation/rules/fragments_are_used.rb +11 -41
  87. data/lib/graphql/static_validation/rules/operation_names_are_valid.rb +2 -2
  88. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +19 -7
  89. data/lib/graphql/static_validation/validation_context.rb +22 -1
  90. data/lib/graphql/static_validation/validator.rb +4 -1
  91. data/lib/graphql/string_type.rb +5 -1
  92. data/lib/graphql/version.rb +1 -1
  93. data/readme.md +12 -3
  94. data/spec/generators/graphql/enum_generator_spec.rb +29 -0
  95. data/spec/generators/graphql/function_generator_spec.rb +33 -0
  96. data/spec/generators/graphql/install_generator_spec.rb +185 -0
  97. data/spec/generators/graphql/interface_generator_spec.rb +32 -0
  98. data/spec/generators/graphql/loader_generator_spec.rb +31 -0
  99. data/spec/generators/graphql/mutation_generator_spec.rb +28 -0
  100. data/spec/generators/graphql/object_generator_spec.rb +42 -0
  101. data/spec/generators/graphql/union_generator_spec.rb +50 -0
  102. data/spec/graphql/analysis/query_complexity_spec.rb +2 -1
  103. data/spec/graphql/define/instance_definable_spec.rb +38 -0
  104. data/spec/graphql/directive/skip_directive_spec.rb +1 -0
  105. data/spec/graphql/directive_spec.rb +18 -0
  106. data/spec/graphql/execution/typecast_spec.rb +41 -46
  107. data/spec/graphql/field_spec.rb +1 -1
  108. data/spec/graphql/function_spec.rb +128 -0
  109. data/spec/graphql/internal_representation/rewrite_spec.rb +166 -129
  110. data/spec/graphql/introspection/type_type_spec.rb +1 -1
  111. data/spec/graphql/language/lexer_spec.rb +6 -0
  112. data/spec/graphql/object_type_spec.rb +73 -2
  113. data/spec/graphql/query/arguments_spec.rb +28 -0
  114. data/spec/graphql/query/variables_spec.rb +7 -1
  115. data/spec/graphql/query_spec.rb +30 -0
  116. data/spec/graphql/relay/base_connection_spec.rb +26 -8
  117. data/spec/graphql/relay/connection_resolve_spec.rb +45 -0
  118. data/spec/graphql/relay/connection_type_spec.rb +21 -0
  119. data/spec/graphql/relay/node_spec.rb +30 -2
  120. data/spec/graphql/relay/range_add_spec.rb +113 -0
  121. data/spec/graphql/schema/build_from_definition_spec.rb +114 -0
  122. data/spec/graphql/schema/loader_spec.rb +1 -0
  123. data/spec/graphql/schema/printer_spec.rb +2 -2
  124. data/spec/graphql/schema/validation_spec.rb +80 -11
  125. data/spec/graphql/schema/warden_spec.rb +10 -10
  126. data/spec/graphql/schema_spec.rb +18 -1
  127. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +16 -0
  128. data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +50 -3
  129. data/spec/graphql/static_validation/rules/fragment_names_are_unique_spec.rb +27 -0
  130. data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +57 -0
  131. data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +1 -1
  132. data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +14 -0
  133. data/spec/graphql/string_type_spec.rb +7 -0
  134. data/spec/spec_helper.rb +3 -3
  135. data/spec/support/base_generator_test.rb +7 -0
  136. data/spec/support/dummy/schema.rb +32 -30
  137. data/spec/support/star_wars/schema.rb +81 -23
  138. metadata +98 -20
  139. data/lib/graphql/internal_representation/selection.rb +0 -85
@@ -103,6 +103,7 @@ describe GraphQL::Schema::Loader do
103
103
  argument :id, !types.ID
104
104
  argument :varied, variant_input_type, default_value: { id: "123", int: 234, float: 2.3, enum: :foo, sub: [{ string: "str" }] }
105
105
  argument :variedWithNull, variant_input_type_with_nulls, default_value: { id: nil, int: nil, float: nil, enum: nil, sub: nil, bigint: nil, bool: nil }
106
+ argument :enum, choice_type, default_value: :foo
106
107
  end
107
108
 
108
109
  field :content do
@@ -499,7 +499,7 @@ input Varied {
499
499
  }
500
500
  SCHEMA
501
501
 
502
- only_filter = -> (member, ctx) {
502
+ only_filter = ->(member, ctx) {
503
503
  case member
504
504
  when GraphQL::ScalarType
505
505
  true
@@ -583,7 +583,7 @@ type Subscription {
583
583
  }
584
584
  SCHEMA
585
585
 
586
- except_filter = -> (member, ctx) {
586
+ except_filter = ->(member, ctx) {
587
587
  ctx[:names].include?(member.name) || (member.respond_to?(:deprecation_reason) && member.deprecation_reason)
588
588
  }
589
589
 
@@ -2,11 +2,22 @@
2
2
  require "spec_helper"
3
3
 
4
4
  describe GraphQL::Schema::Validation do
5
+ def integer_class_name
6
+ RUBY_VERSION >= "2.4.0" ? "Integer" : "Fixnum"
7
+ end
8
+
5
9
  def assert_error_includes(object, error_substring)
6
10
  validation_error = GraphQL::Schema::Validation.validate(object)
7
11
  assert_includes validation_error, error_substring
8
12
  end
9
13
 
14
+ def refute_error_includes(object, *error_substrings)
15
+ validation_error = GraphQL::Schema::Validation.validate(object)
16
+ error_substrings.each do |substr|
17
+ refute_includes(validation_error, substr)
18
+ end
19
+ end
20
+
10
21
  def assert_validation_warns(object, warning)
11
22
  assert_output("", warning + "\n") { GraphQL::Schema::Validation.validate(object) }
12
23
  end
@@ -102,18 +113,11 @@ describe GraphQL::Schema::Validation do
102
113
  end
103
114
 
104
115
  it "requires String-or-nil description" do
105
- assert_error_includes wrongly_described_type, "must return String or NilClass, not Fixnum"
116
+ assert_error_includes wrongly_described_type, "must return String or NilClass, not #{integer_class_name}"
106
117
  end
107
118
  end
108
119
 
109
120
  describe "validating ObjectTypes" do
110
- let(:invalid_interfaces_object) {
111
- GraphQL::ObjectType.define do
112
- name "InvalidInterfaces"
113
- interfaces(55)
114
- end
115
- }
116
-
117
121
  let(:invalid_interface_member_object) {
118
122
  GraphQL::ObjectType.define do
119
123
  name "InvalidInterfaceMember"
@@ -129,13 +133,78 @@ describe GraphQL::Schema::Validation do
129
133
  }
130
134
 
131
135
  it "requires an Array for interfaces" do
132
- assert_error_includes invalid_interfaces_object, "must be an Array of GraphQL::InterfaceType, not a Fixnum"
133
136
  assert_error_includes invalid_interface_member_object, "must contain GraphQL::InterfaceType, not Symbol"
134
137
  end
135
138
 
136
139
  it "validates the fields" do
137
140
  assert_error_includes invalid_field_object, "must return GraphQL::BaseType, not Symbol"
138
141
  end
142
+
143
+ it "requires interfaces to be implemented" do
144
+ iface_1 = GraphQL::InterfaceType.define do
145
+ name "Interface1"
146
+ field :field_1, types.String
147
+ field :field_2, types.Int
148
+ end
149
+
150
+ iface_2 = GraphQL::InterfaceType.define do
151
+ name "Interface2"
152
+ field :field_3, types.String do
153
+ argument :arg_1, types.Boolean
154
+ end
155
+ field :field_4, types.Int
156
+ end
157
+
158
+ iface_3 = GraphQL::InterfaceType.define do
159
+ name "Interface3"
160
+ field :field_5, types.String do
161
+ argument :arg_2, types.Float
162
+ end
163
+ field :field_6, types.Int do
164
+ argument :arg_3, types.Float
165
+ end
166
+ field :field_7, types.ID
167
+ field :field_8, !types[iface_2]
168
+ field :field_9, !types[iface_2]
169
+ end
170
+
171
+ obj_type = GraphQL::ObjectType.define do
172
+ name "Object1"
173
+ implements iface_1, inherit: true
174
+ implements iface_2, iface_3
175
+ field :field_2, types.Int do
176
+ # Acceptable extra optional argument:
177
+ argument :arg_0_extra, types.String
178
+ # Unacceptable extra required argument:
179
+ argument :arg_0_extra_2, !types.String
180
+ end
181
+ # Correct:
182
+ field :field_3, types.String do
183
+ argument :arg_1, types.Boolean
184
+ end
185
+ # Wrong return type:
186
+ field :field_4, types.Float
187
+ # Wrong argument type:
188
+ field :field_5, types.String do
189
+ argument :arg_2, types.Int
190
+ end
191
+ # Missing argument
192
+ field :field_6, types.Int
193
+ # Missing altogether:
194
+ # field :field_7
195
+ # Valid subtype:
196
+ field :field_8, !types[!iface_2]
197
+ # Invalid subtype:
198
+ field :field_9, types[iface_2]
199
+ end
200
+
201
+ assert_error_includes(obj_type, '"arg_0_extra_2" is not accepted by Interface1.field_2 but required by Object1.field_2')
202
+ assert_error_includes(obj_type, '"field_4" is required by Interface2 to return Int but Object1.field_4 returns Float')
203
+ assert_error_includes(obj_type, '"arg_2" is required by Interface3.field_5 to accept Float but Object1.field_5 accepts Int for "arg_2"')
204
+ assert_error_includes(obj_type, '"field_7" is required by Interface3 but not implemented by Object1')
205
+ assert_error_includes(obj_type, '"field_9" is required by Interface3 to return [Interface2]! but Object1.field_9 returns [Interface2]')
206
+ refute_error_includes(obj_type, "field_1", "field_3", "field_8")
207
+ end
139
208
  end
140
209
 
141
210
  describe "validating UnionTypes" do
@@ -163,7 +232,7 @@ describe GraphQL::Schema::Validation do
163
232
  }
164
233
 
165
234
  it "requires an array of ObjectTypes for possible_types" do
166
- assert_error_includes non_array_union, "must be an Array of GraphQL::ObjectType, not a Fixnum"
235
+ assert_error_includes non_array_union, "must be an Array of GraphQL::ObjectType, not a #{integer_class_name}"
167
236
 
168
237
  assert_error_includes non_object_type_union, "must contain GraphQL::ObjectType, not GraphQL::InterfaceType"
169
238
  end
@@ -193,7 +262,7 @@ describe GraphQL::Schema::Validation do
193
262
  }
194
263
 
195
264
  it "requires {String => Argument} arguments" do
196
- assert_error_includes invalid_arguments_input, "map String => GraphQL::Argument, not Fixnum => Symbol"
265
+ assert_error_includes invalid_arguments_input, "map String => GraphQL::Argument, not #{integer_class_name} => Symbol"
197
266
  end
198
267
 
199
268
  it "applies validation to its member Arguments" do
@@ -107,7 +107,7 @@ module MaskHelpers
107
107
  query QueryType
108
108
  mutation MutationType
109
109
  subscription MutationType
110
- resolve_type -> (obj, ctx) { PhonemeType }
110
+ resolve_type ->(obj, ctx) { PhonemeType }
111
111
  end
112
112
 
113
113
  module Data
@@ -208,7 +208,7 @@ describe GraphQL::Schema::Warden do
208
208
 
209
209
  describe "hiding fields" do
210
210
  let(:mask) {
211
- -> (member, ctx) { member.metadata[:hidden_field] || member.metadata[:hidden_type] }
211
+ ->(member, ctx) { member.metadata[:hidden_field] || member.metadata[:hidden_type] }
212
212
  }
213
213
 
214
214
  it "causes validation errors" do
@@ -254,7 +254,7 @@ describe GraphQL::Schema::Warden do
254
254
 
255
255
  describe "hiding types" do
256
256
  let(:whitelist) {
257
- -> (member, ctx) { !member.metadata[:hidden_type] }
257
+ ->(member, ctx) { !member.metadata[:hidden_type] }
258
258
  }
259
259
 
260
260
  it "hides types from introspection" do
@@ -340,7 +340,7 @@ describe GraphQL::Schema::Warden do
340
340
 
341
341
  describe "hiding an abstract type" do
342
342
  let(:mask) {
343
- -> (member, ctx) { member.metadata[:hidden_abstract_type] }
343
+ ->(member, ctx) { member.metadata[:hidden_abstract_type] }
344
344
  }
345
345
 
346
346
  it "isn't present in a type's interfaces" do
@@ -362,7 +362,7 @@ describe GraphQL::Schema::Warden do
362
362
 
363
363
  describe "hiding arguments" do
364
364
  let(:mask) {
365
- -> (member, ctx) { member.metadata[:hidden_argument] || member.metadata[:hidden_input_type] }
365
+ ->(member, ctx) { member.metadata[:hidden_argument] || member.metadata[:hidden_input_type] }
366
366
  }
367
367
 
368
368
  it "isn't present in introspection" do
@@ -398,7 +398,7 @@ describe GraphQL::Schema::Warden do
398
398
 
399
399
  describe "hidding input type arguments" do
400
400
  let(:mask) {
401
- -> (member, ctx) { member.metadata[:hidden_input_field] }
401
+ ->(member, ctx) { member.metadata[:hidden_input_field] }
402
402
  }
403
403
 
404
404
  it "isn't present in introspection" do
@@ -447,7 +447,7 @@ describe GraphQL::Schema::Warden do
447
447
 
448
448
  describe "hidding input types" do
449
449
  let(:mask) {
450
- -> (member, ctx) { member.metadata[:hidden_input_object_type] }
450
+ ->(member, ctx) { member.metadata[:hidden_input_object_type] }
451
451
  }
452
452
 
453
453
  it "isn't present in introspection" do
@@ -492,7 +492,7 @@ describe GraphQL::Schema::Warden do
492
492
 
493
493
  describe "hiding enum values" do
494
494
  let(:mask) {
495
- -> (member, ctx) { member.metadata[:hidden_enum_value] }
495
+ ->(member, ctx) { member.metadata[:hidden_enum_value] }
496
496
  }
497
497
 
498
498
  it "isn't present in introspection" do
@@ -570,7 +570,7 @@ describe GraphQL::Schema::Warden do
570
570
 
571
571
  describe "default_mask" do
572
572
  let(:default_mask) {
573
- -> (member, ctx) { member.metadata[:hidden_enum_value] }
573
+ ->(member, ctx) { member.metadata[:hidden_enum_value] }
574
574
  }
575
575
  let(:schema) {
576
576
  MaskHelpers::Schema.redefine(default_mask: default_mask)
@@ -584,7 +584,7 @@ describe GraphQL::Schema::Warden do
584
584
  }
585
585
 
586
586
  it "is additive with query filters" do
587
- query_except = -> (member, ctx) { member.metadata[:hidden_input_object_type] }
587
+ query_except = ->(member, ctx) { member.metadata[:hidden_input_object_type] }
588
588
  res = schema.execute(query_str, except: query_except)
589
589
  assert_equal nil, res["data"]["input"]
590
590
  enum_values = res["data"]["enum"]["enumValues"].map { |v| v["name"] }
@@ -236,7 +236,7 @@ type Query {
236
236
  name "Query"
237
237
  field :int, types.Int do
238
238
  argument :value, types.Int
239
- resolve -> (obj, args, ctx) { args[:value] == 13 ? raise("13 is unlucky") : args[:value] }
239
+ resolve ->(obj, args, ctx) { args[:value] == 13 ? raise("13 is unlucky") : args[:value] }
240
240
  end
241
241
  end
242
242
  }
@@ -319,4 +319,21 @@ type Query {
319
319
  refute_equal schema_2.middleware, schema.middleware
320
320
  end
321
321
  end
322
+
323
+ describe "#validate" do
324
+ it "returns errors on the query string" do
325
+ errors = schema.validate("{ cheese(id: 1) { flavor flavor: id } }")
326
+ assert_equal 1, errors.length
327
+ assert_equal "Field 'flavor' has a field conflict: flavor or id?", errors.first.message
328
+
329
+ errors = schema.validate("{ cheese(id: 1) { flavor id } }")
330
+ assert_equal [], errors
331
+ end
332
+
333
+ it "accepts a list of custom rules" do
334
+ custom_rules = GraphQL::StaticValidation::ALL_RULES - [GraphQL::StaticValidation::FragmentsAreNamed]
335
+ errors = schema.validate("fragment on Cheese { id }", rules: custom_rules)
336
+ assert_equal([], errors)
337
+ end
338
+ end
322
339
  end
@@ -67,6 +67,22 @@ describe GraphQL::StaticValidation::ArgumentLiteralsAreCompatible do
67
67
  assert_includes(errors, fragment_error)
68
68
  end
69
69
 
70
+ describe "using input objects for enums" do
71
+ let(:query_string) { <<-GRAPHQL
72
+ {
73
+ yakSource: searchDairy(product: [{source: {a: 1, b: 2}, fatContent: 1.1}]) { __typename }
74
+ }
75
+ GRAPHQL
76
+ }
77
+
78
+ it "adds an error" do
79
+ # TODO:
80
+ # It's annoying that this error cascades up, there should only be one:
81
+ assert_equal 2, errors.length
82
+ end
83
+ end
84
+
85
+
70
86
  describe "null value" do
71
87
  describe "nullable arg" do
72
88
  let(:schema) {
@@ -231,7 +231,7 @@ describe GraphQL::StaticValidation::FieldsWillMerge do
231
231
  |}
232
232
 
233
233
  it "fails rule" do
234
- assert_equal ["Field 'name' has a field conflict: nickname or name?"], error_messages
234
+ assert_equal ["Field 'name' has a field conflict: name or nickname?"], error_messages
235
235
  end
236
236
  end
237
237
 
@@ -317,8 +317,8 @@ describe GraphQL::StaticValidation::FieldsWillMerge do
317
317
 
318
318
  it "fails rule" do
319
319
  assert_equal [
320
+ "Field 'name' has a field conflict: name or nickname?",
320
321
  "Field 'x' has a field conflict: name or nickname?",
321
- "Field 'name' has a field conflict: nickname or name?",
322
322
  ], error_messages
323
323
  end
324
324
  end
@@ -337,7 +337,17 @@ describe GraphQL::StaticValidation::FieldsWillMerge do
337
337
  |}
338
338
 
339
339
  it "fails rule" do
340
- assert_equal ["Field 'x' has a field conflict: name or nickname?"], error_messages
340
+ expected_errors = [
341
+ {
342
+ "message"=>"Field 'x' has a field conflict: name or nickname?",
343
+ "locations"=>[
344
+ {"line"=>4, "column"=>11},
345
+ {"line"=>8, "column"=>11}
346
+ ],
347
+ "fields"=>[]
348
+ }
349
+ ]
350
+ assert_equal expected_errors, errors
341
351
  end
342
352
  end
343
353
 
@@ -388,6 +398,43 @@ describe GraphQL::StaticValidation::FieldsWillMerge do
388
398
  end
389
399
  end
390
400
 
401
+ describe "same aliases allowed on non-overlapping fields" do
402
+ let(:query_string) {%|
403
+ {
404
+ pet {
405
+ ... on Dog {
406
+ name
407
+ }
408
+ ... on Cat {
409
+ name: nickname
410
+ }
411
+ }
412
+ }
413
+ |}
414
+
415
+ it "passes rule" do
416
+ assert_equal [], errors
417
+ end
418
+ end
419
+
420
+ describe "allows different args where no conflict is possible" do
421
+ let(:query_string) {%|
422
+ {
423
+ pet {
424
+ ... on Dog {
425
+ name(surname: true)
426
+ }
427
+ ... on Cat {
428
+ name
429
+ }
430
+ }
431
+ }
432
+ |}
433
+
434
+ it "passes rule" do
435
+ assert_equal [], errors
436
+ end
437
+ end
391
438
  describe "return types must be unambiguous" do
392
439
  let(:schema) {
393
440
  GraphQL::Schema.from_definition(%|
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
3
+
4
+ describe GraphQL::StaticValidation::FragmentNamesAreUnique do
5
+ include StaticValidationHelpers
6
+
7
+ let(:query_string) {"
8
+ query {
9
+ cheese(id: 1) {
10
+ ... frag1
11
+ }
12
+ }
13
+
14
+ fragment frag1 on Cheese { id }
15
+ fragment frag1 on Cheese { id }
16
+ "}
17
+
18
+ it "requires unique fragment names" do
19
+ assert_equal(1, errors.length)
20
+ fragment_def_error = {
21
+ "message"=>"Fragment name \"frag1\" must be unique",
22
+ "locations"=>[{"line"=>8, "column"=>5}, {"line"=>9, "column"=>5}],
23
+ "fields"=>[],
24
+ }
25
+ assert_includes(errors, fragment_def_error)
26
+ end
27
+ end
@@ -61,4 +61,61 @@ describe GraphQL::StaticValidation::FragmentsAreFinite do
61
61
  assert_equal("Fragment frag2 was used, but not defined", errors.first["message"])
62
62
  end
63
63
  end
64
+
65
+ describe "a duplicate fragment name with a loop" do
66
+ let(:query_string) {%|
67
+ {
68
+ cheese(id: 1) { ... frag1 }
69
+ }
70
+ fragment frag1 on Cheese { id }
71
+ fragment frag1 on Cheese { ...frag1 }
72
+ |}
73
+
74
+ it "detects the loop" do
75
+ assert_equal 2, errors.length
76
+ assert_equal("Fragment frag1 contains an infinite loop", errors[0]["message"])
77
+ assert_equal("Fragment name \"frag1\" must be unique", errors[1]["message"])
78
+ end
79
+ end
80
+
81
+ describe "a duplicate operation name with a loop" do
82
+ let(:query_string) {%|
83
+ fragment frag1 on Cheese { ...frag1 }
84
+
85
+ query frag1 {
86
+ cheese(id: 1) {
87
+ ... frag1
88
+ }
89
+ }
90
+ |}
91
+
92
+ it "detects the loop" do
93
+ assert_equal 1, errors.length
94
+ assert_equal("Fragment frag1 contains an infinite loop", errors[0]["message"])
95
+ end
96
+ end
97
+
98
+ describe "several duplicate operation names with a loop" do
99
+ let(:query_string) {%|
100
+ query frag1 {
101
+ cheese(id: 1) {
102
+ id
103
+ }
104
+ }
105
+
106
+ fragment frag1 on Cheese { ...frag1 }
107
+
108
+ query frag1 {
109
+ cheese(id: 1) {
110
+ ... frag1
111
+ }
112
+ }
113
+ |}
114
+
115
+ it "detects the loop" do
116
+ assert_equal 2, errors.length
117
+ assert_equal("Fragment frag1 contains an infinite loop", errors[0]["message"])
118
+ assert_equal("Operation name \"frag1\" must be unique", errors[1]["message"])
119
+ end
120
+ end
64
121
  end