graphql 1.4.5 → 1.5.3

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 (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