graphql 1.8.0.pre6 → 1.8.0.pre7

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 (36) hide show
  1. checksums.yaml +5 -5
  2. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +14 -0
  3. data/lib/graphql/internal_representation/node.rb +3 -20
  4. data/lib/graphql/language/parser.rb +590 -598
  5. data/lib/graphql/language/parser.y +1 -2
  6. data/lib/graphql/query/context.rb +26 -0
  7. data/lib/graphql/schema.rb +77 -22
  8. data/lib/graphql/schema/argument.rb +11 -3
  9. data/lib/graphql/schema/field.rb +50 -3
  10. data/lib/graphql/schema/input_object.rb +6 -0
  11. data/lib/graphql/schema/interface.rb +0 -1
  12. data/lib/graphql/schema/member.rb +2 -16
  13. data/lib/graphql/schema/member/has_fields.rb +4 -0
  14. data/lib/graphql/schema/object.rb +5 -4
  15. data/lib/graphql/schema/union.rb +0 -12
  16. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +5 -1
  17. data/lib/graphql/upgrader/member.rb +97 -10
  18. data/lib/graphql/version.rb +1 -1
  19. data/spec/fixtures/upgrader/starrable.original.rb +46 -0
  20. data/spec/fixtures/upgrader/starrable.transformed.rb +43 -0
  21. data/spec/fixtures/upgrader/subscribable.transformed.rb +32 -30
  22. data/spec/fixtures/upgrader/type_x.original.rb +14 -2
  23. data/spec/fixtures/upgrader/type_x.transformed.rb +12 -0
  24. data/spec/graphql/query/executor_spec.rb +2 -1
  25. data/spec/graphql/rake_task_spec.rb +3 -1
  26. data/spec/graphql/schema/field_spec.rb +94 -1
  27. data/spec/graphql/schema/input_object_spec.rb +6 -0
  28. data/spec/graphql/schema/interface_spec.rb +15 -0
  29. data/spec/graphql/schema/object_spec.rb +5 -3
  30. data/spec/graphql/schema/printer_spec.rb +19 -0
  31. data/spec/graphql/schema_spec.rb +10 -3
  32. data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +10 -2
  33. data/spec/graphql/upgrader/member_spec.rb +2 -2
  34. data/spec/support/jazz.rb +21 -14
  35. data/spec/support/star_wars/schema.rb +1 -1
  36. metadata +7 -3
@@ -24,7 +24,11 @@ module GraphQL
24
24
  msg = if resolved_type.nil?
25
25
  nil
26
26
  elsif resolved_type.kind.scalar? && ast_node.selections.any?
27
- "Selections can't be made on scalars (%{node_name} returns #{resolved_type.name} but has selections [#{ast_node.selections.map(&:name).join(", ")}])"
27
+ if ast_node.selections.first.is_a?(GraphQL::Language::Nodes::InlineFragment)
28
+ "Selections can't be made on scalars (%{node_name} returns #{resolved_type.name} but has inline fragments [#{ast_node.selections.map(&:type).map(&:name).join(", ")}])"
29
+ else
30
+ "Selections can't be made on scalars (%{node_name} returns #{resolved_type.name} but has selections [#{ast_node.selections.map(&:name).join(", ")}])"
31
+ end
28
32
  elsif resolved_type.kind.object? && ast_node.selections.none?
29
33
  "Objects must have selections (%{node_name} returns #{resolved_type.name} but has no selections)"
30
34
  else
@@ -11,7 +11,6 @@ module GraphQL
11
11
 
12
12
  class Transform
13
13
  # @param input_text [String] Untransformed GraphQL-Ruby code
14
- # @param rewrite_options [Hash] Used during rewrite
15
14
  # @return [String] The input text, with a transformation applied if necessary
16
15
  def apply(input_text)
17
16
  raise NotImplementedError, "Return transformed text here"
@@ -19,11 +18,11 @@ module GraphQL
19
18
 
20
19
  # Recursively transform a `.define`-DSL-based type expression into a class-ready expression, for example:
21
20
  #
22
- # - `types[X]` -> `[X]`
21
+ # - `types[X]` -> `[X, null: true]`
23
22
  # - `Int` -> `Integer`
24
23
  # - `X!` -> `X`
25
24
  #
26
- # Notice that `!` is removed entirely, because it doesn't exist in the class API.
25
+ # Notice that `!` is removed sometimes, because it doesn't exist in the class API.
27
26
  #
28
27
  # @param type_expr [String] A `.define`-ready expression of a return type or input type
29
28
  # @return [String] A class-ready expression of the same type`
@@ -34,7 +33,15 @@ module GraphQL
34
33
  "#{preserve_bang ? "!" : ""}#{normalize_type_expression(type_expr[1..-1], preserve_bang: preserve_bang)}"
35
34
  when /\Atypes\[.*\]\Z/
36
35
  # Unwrap the brackets, normalize, then re-wrap
37
- "[#{normalize_type_expression(type_expr[6..-2], preserve_bang: preserve_bang)}]"
36
+ inner_type = type_expr[6..-2]
37
+ if inner_type.start_with?("!")
38
+ nullable = false
39
+ inner_type = inner_type[1..-1]
40
+ else
41
+ nullable = true
42
+ end
43
+
44
+ "[#{normalize_type_expression(inner_type, preserve_bang: preserve_bang)}#{nullable ? ", null: true" : ""}]"
38
45
  when /\Atypes\./
39
46
  # Remove the prefix
40
47
  normalize_type_expression(type_expr[6..-1], preserve_bang: preserve_bang)
@@ -326,8 +333,12 @@ module GraphQL
326
333
  obj_arg_name, args_arg_name, ctx_arg_name = processor.proc_arg_names
327
334
  # This is not good, it will hit false positives
328
335
  # Should use AST to make this substitution
329
- proc_body.gsub!(/([^\w:]|^)#{obj_arg_name}([^\w]|$)/, '\1@object\2')
330
- proc_body.gsub!(/([^\w:]|^)#{ctx_arg_name}([^\w]|$)/, '\1@context\2')
336
+ if obj_arg_name != "_"
337
+ proc_body.gsub!(/([^\w:.]|^)#{obj_arg_name}([^\w]|$)/, '\1@object\2')
338
+ end
339
+ if ctx_arg_name != "_"
340
+ proc_body.gsub!(/([^\w:.]|^)#{ctx_arg_name}([^\w]|$)/, '\1@context\2')
341
+ end
331
342
 
332
343
  method_def_indent = " " * (processor.resolve_indent - 2)
333
344
  # Turn the proc body into a method body
@@ -442,7 +453,7 @@ module GraphQL
442
453
  class UpdateMethodSignatureTransform < Transform
443
454
  def apply(input_text)
444
455
  input_text.scan(/(?:input_field|field|connection|argument) .*$/).each do |field|
445
- matches = /(?<field_type>input_field|field|connection|argument) :(?<name>[a-zA-Z_0-9_]*)?(:?, (?<return_type>([A-Za-z\[\]\.\!_0-9\(\)]|::|-> ?\{ ?| ?\})+))?(?<remainder>( |,|$).*)/.match(field)
456
+ matches = /(?<field_type>input_field|field|connection|argument) :(?<name>[a-zA-Z_0-9_]*)?(:?, +(?<return_type>([A-Za-z\[\]\.\!_0-9\(\)]|::|-> ?\{ ?| ?\})+))?(?<remainder>( |,|$).*)/.match(field)
446
457
  if matches
447
458
  name = matches[:name]
448
459
  return_type = matches[:return_type]
@@ -458,7 +469,6 @@ module GraphQL
458
469
  non_nullable = return_type.sub! /(^|[^\[])!/, '\1'
459
470
  nullable = !non_nullable
460
471
  return_type = normalize_type_expression(return_type)
461
- return_type = return_type.gsub ',', ''
462
472
  else
463
473
  non_nullable = nil
464
474
  nullable = nil
@@ -516,15 +526,91 @@ module GraphQL
516
526
  # Remove redundant newlines, which may have trailing spaces
517
527
  # Remove double newline after `do`
518
528
  # Remove double newline before `end`
529
+ # Remove lines with whitespace only
519
530
  class RemoveExcessWhitespaceTransform < Transform
520
531
  def apply(input_text)
521
532
  input_text
522
533
  .gsub(/\n{3,}/m, "\n\n")
523
534
  .gsub(/do\n{2,}/m, "do\n")
524
535
  .gsub(/\n{2,}(\s*)end/m, "\n\\1end")
536
+ .gsub(/\n +\n/m, "\n\n")
525
537
  end
526
538
  end
527
539
 
540
+ class MoveInterfaceMethodsToImplementationTransform < Transform
541
+ def initialize(interface_file_pattern: /<.*Interface/)
542
+ @interface_file_pattern = interface_file_pattern
543
+ end
544
+
545
+ def apply(input_text)
546
+ if input_text =~ @interface_file_pattern && input_text =~ /\n *def /
547
+ # Extract the method bodies and figure out where the module should be inserted
548
+ method_bodies = []
549
+ processor = apply_processor(input_text, InterfaceMethodProcessor.new)
550
+ processor.methods.each do |(begin_pos, end_pos)|
551
+ # go all the way back to the newline so we get whitespace too
552
+ while input_text[begin_pos] != "\n" && begin_pos >= 0
553
+ begin_pos -= 1
554
+ end
555
+ # Tuck away the method body, and remove it from here
556
+ method_body = input_text[begin_pos..end_pos]
557
+ method_bodies << method_body
558
+ end
559
+ # How far are these method bodies indented? The module will be this indented
560
+ leading_indent = method_bodies.first[/\n +/][1..-1]
561
+ # Increase the indent since it will be in a nested module
562
+ indented_method_bodies = method_bodies.map {|m| m.gsub("\n", "\n ").rstrip }
563
+ # Build the ruby module definition
564
+ module_body = "\n\n#{leading_indent}module Implementation#{indented_method_bodies.join("\n")}\n#{leading_indent}end"
565
+
566
+ # find the `end` of the class definition, and put the module before it
567
+ _class_start, class_end = processor.class_definition
568
+ # This might target the newline _after_ `end`, but we don't want that one
569
+ if input_text[class_end] == "\n"
570
+ class_end -= 1
571
+ end
572
+
573
+ while input_text[class_end] != "\n" && class_end > 0
574
+ class_end -= 1
575
+ end
576
+
577
+ input_text.insert(class_end, module_body)
578
+
579
+ # Do the replacement _after_ identifying the bodies,
580
+ # otherwise the offsets get messed up
581
+ method_bodies.each do |method_body|
582
+ input_text.sub!(method_body, "")
583
+ end
584
+ end
585
+ input_text
586
+ end
587
+
588
+ # Find the beginning and end of each method def,
589
+ # so that we can move it wholesale
590
+ class InterfaceMethodProcessor < Parser::AST::Processor
591
+ attr_reader :methods, :class_definition
592
+ def initialize
593
+ @class_definition = nil
594
+ @methods = []
595
+ super
596
+ end
597
+
598
+ def on_def(node)
599
+ start = node.loc.expression.begin_pos
600
+ finish = node.loc.expression.end_pos
601
+ @methods << [start, finish]
602
+ super(node)
603
+ end
604
+
605
+ def on_class(node)
606
+ @class_definition = [
607
+ node.loc.expression.begin_pos,
608
+ node.loc.expression.end_pos
609
+ ]
610
+ super(node)
611
+ end
612
+ end
613
+ end
528
614
  # Skip this file if you see any `field`
529
615
  # helpers with `null: true` or `null: false` keywords
530
616
  # or `argument` helpers with `required:` keywords,
@@ -562,14 +648,15 @@ module GraphQL
562
648
  ConfigurationToKwargTransform.new(kwarg: "deprecation_reason"),
563
649
  ConfigurationToKwargTransform.new(kwarg: "hash_key"),
564
650
  PropertyToMethodTransform,
565
- RemoveRedundantKwargTransform.new(kwarg: "hash_key"),
566
- RemoveRedundantKwargTransform.new(kwarg: "method"),
567
651
  UnderscoreizeFieldNameTransform,
568
652
  ResolveProcToMethodTransform,
569
653
  UpdateMethodSignatureTransform,
654
+ RemoveRedundantKwargTransform.new(kwarg: "hash_key"),
655
+ RemoveRedundantKwargTransform.new(kwarg: "method"),
570
656
  ]
571
657
 
572
658
  DEFAULT_CLEAN_UP_TRANSFORMS = [
659
+ MoveInterfaceMethodsToImplementationTransform,
573
660
  RemoveExcessWhitespaceTransform,
574
661
  RemoveEmptyBlocksTransform,
575
662
  ]
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.8.0.pre6"
3
+ VERSION = "1.8.0.pre7"
4
4
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Platform
4
+ module Interfaces
5
+ Starrable = GraphQL::InterfaceType.define do
6
+ name "Starrable"
7
+ description "Things that can be starred."
8
+
9
+ global_id_field :id
10
+
11
+ field :viewerHasStarred, !types.Boolean do
12
+ description "Returns a boolean indicating whether the viewing user has starred this starrable."
13
+
14
+ resolve ->(object, arguments, context) do
15
+ if context[:viewer]
16
+ context[:viewer].starred?(object)
17
+ else
18
+ false
19
+ end
20
+ end
21
+ end
22
+
23
+ connection :stargazers, -> { !Connections::Stargazer } do
24
+ description "A list of users who have starred this starrable."
25
+
26
+ argument :orderBy, Inputs::StarOrder, "Order for connection"
27
+
28
+ resolve ->(object, arguments, context) do
29
+ scope = case object
30
+ when Repository
31
+ object.stars
32
+ when Gist
33
+ GistStar.where(gist_id: object.id)
34
+ end
35
+
36
+ table = scope.table_name
37
+ if order_by = arguments["orderBy"]
38
+ scope = scope.order("#{table}.#{order_by["field"]} #{order_by["direction"]}")
39
+ end
40
+
41
+ scope
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Platform
4
+ module Interfaces
5
+ class Starrable < Platform::Interfaces::Base
6
+ description "Things that can be starred."
7
+
8
+ global_id_field :id
9
+
10
+ field :viewer_has_starred, Boolean, description: "Returns a boolean indicating whether the viewing user has starred this starrable.", null: false
11
+
12
+ field :stargazers, Connections::Stargazer, description: "A list of users who have starred this starrable.", null: false, connection: true do
13
+ argument :order_by, Inputs::StarOrder, "Order for connection", required: false
14
+ end
15
+
16
+ module Implementation
17
+ def viewer_has_starred
18
+ if @context[:viewer]
19
+ @context[:viewer].starred?(@object)
20
+ else
21
+ false
22
+ end
23
+ end
24
+
25
+ def stargazers(**arguments)
26
+ scope = case @object
27
+ when Repository
28
+ @object.stars
29
+ when Gist
30
+ GistStar.where(gist_id: @object.id)
31
+ end
32
+
33
+ table = scope.table_name
34
+ if order_by = arguments[:order_by]
35
+ scope = scope.order("#{table}.#{order_by["field"]} #{order_by["direction"]}")
36
+ end
37
+
38
+ scope
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -9,42 +9,44 @@ module Platform
9
9
 
10
10
  field :viewer_subscription, Enums::SubscriptionState, description: "Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.", null: false
11
11
 
12
- def viewer_subscription
13
- if @context[:viewer].nil?
14
- return "unsubscribed"
15
- end
12
+ field :viewer_can_subscribe, Boolean, description: "Check if the viewer is able to change their subscription status for the repository.", null: false
16
13
 
17
- subscription_status_response = @object.async_subscription_status(@context[:viewer]).sync
14
+ field :issues, function: Platform::Functions::Issues.new, description: "A list of issues associated with the milestone.", connection: true
15
+ field :files, Connections.define(PackageFile), description: "List of files associated with this registry package version", null: false, connection: true
16
+ field :enabled, Boolean, "Whether enabled for this project", method: :enabled?, null: false
18
17
 
19
- if subscription_status_response.failed?
20
- error = Platform::Errors::ServiceUnavailable.new("Subscriptions are currently unavailable. Please try again later.")
21
- error.ast_node = @context.irep_node.ast_node
22
- error.path = @context.path
23
- @context.errors << error
24
- return "unavailable"
18
+ module Implementation
19
+ def viewer_subscription
20
+ if @context[:viewer].nil?
21
+ return "unsubscribed"
22
+ end
23
+
24
+ subscription_status_response = @object.async_subscription_status(@context[:viewer]).sync
25
+
26
+ if subscription_status_response.failed?
27
+ error = Platform::Errors::ServiceUnavailable.new("Subscriptions are currently unavailable. Please try again later.")
28
+ error.ast_node = @context.irep_node.ast_node
29
+ error.path = @context.path
30
+ @context.errors << error
31
+ return "unavailable"
32
+ end
33
+
34
+ subscription = subscription_status_response.value
35
+ if subscription.included?
36
+ "unsubscribed"
37
+ elsif subscription.subscribed?
38
+ "subscribed"
39
+ elsif subscription.ignored?
40
+ "ignored"
41
+ end
25
42
  end
26
43
 
27
- subscription = subscription_status_response.value
28
- if subscription.included?
29
- "unsubscribed"
30
- elsif subscription.subscribed?
31
- "subscribed"
32
- elsif subscription.ignored?
33
- "ignored"
34
- end
35
- end
44
+ def viewer_can_subscribe
45
+ return false if @context[:viewer].nil?
36
46
 
37
- field :viewer_can_subscribe, Boolean, description: "Check if the viewer is able to change their subscription status for the repository.", null: false
38
-
39
- def viewer_can_subscribe
40
- return false if @context[:viewer].nil?
41
-
42
- @object.async_subscription_status(@context[:viewer]).then(&:success?)
47
+ @object.async_subscription_status(@context[:viewer]).then(&:success?)
48
+ end
43
49
  end
44
-
45
- field :issues, function: Platform::Functions::Issues.new, description: "A list of issues associated with the milestone.", connection: true
46
- field :files, Connections.define(PackageFile), description: "List of files associated with this registry package version", null: false, connection: true
47
- field :enabled, Boolean, "Whether enabled for this project", method: :enabled?, null: false
48
50
  end
49
51
  end
50
52
  end
@@ -11,8 +11,8 @@ module Platform
11
11
  global_id_field :id
12
12
  interfaces [GraphQL::Relay::Node.interface]
13
13
 
14
- field :f1, !Objects::O1, "The x being y."
15
- field :f2, !Enums::E1, "x for the y.",
14
+ field :f1, !Objects::O1, "The x being y."
15
+ field :f2, !Enums::E1, "x for the y.",
16
16
  property: :field_2
17
17
  field :f3, Enums::E2, "x for y."
18
18
  field :details, types.String, "Details."
@@ -48,6 +48,18 @@ module Platform
48
48
 
49
49
  field :f7, field: SomeField
50
50
  field :f8, function: SomeFunction
51
+ field :f9, types[Objects::O2]
52
+ field :fieldField, types.String, hash_key: "fieldField"
53
+ field :fieldField2, types.String, property: :field_field2
54
+
55
+ field :f10, types.String do
56
+ resolve ->(obj, _, _) do
57
+ obj.something do |_|
58
+ xyz_obj.obj
59
+ obj.f10
60
+ end
61
+ end
62
+ end
51
63
  end
52
64
  end
53
65
  end
@@ -39,6 +39,18 @@ module Platform
39
39
 
40
40
  field :f7, field: SomeField
41
41
  field :f8, function: SomeFunction
42
+ field :f9, [Objects::O2, null: true], null: true
43
+ field :field_field, String, hash_key: "fieldField", null: true
44
+ field :field_field2, String, null: true
45
+
46
+ field :f10, String, null: true
47
+
48
+ def f10
49
+ @object.something do |_|
50
+ xyz_obj.obj
51
+ @object.f10
52
+ end
53
+ end
42
54
  end
43
55
  end
44
56
  end
@@ -188,7 +188,8 @@ describe GraphQL::Query::Executor do
188
188
 
189
189
  describe "if the schema has a rescue handler" do
190
190
  before do
191
- schema.rescue_from(RuntimeError) { "Error was handled!" }
191
+ # HACK: reach to the underlying instance to perform a side-effect
192
+ schema.graphql_definition.rescue_from(RuntimeError) { "Error was handled!" }
192
193
  end
193
194
 
194
195
  after do
@@ -34,7 +34,9 @@ describe GraphQL::RakeTask do
34
34
  end
35
35
  dumped_json = File.read("./schema.json")
36
36
  expected_json = JSON.pretty_generate(RakeTaskSchema.execute(GraphQL::Introspection::INTROSPECTION_QUERY))
37
- assert_equal(expected_json, dumped_json)
37
+
38
+ # Test that that JSON is logically equivalent, not serialized the same
39
+ assert_equal(JSON.parse(expected_json), JSON.parse(dumped_json))
38
40
 
39
41
  dumped_idl = File.read("./schema.graphql")
40
42
  expected_idl = rake_task_schema_defn.chomp
@@ -11,8 +11,17 @@ describe GraphQL::Schema::Field do
11
11
  assert_equal :ok, arg_defn.metadata[:custom]
12
12
  end
13
13
 
14
- it "camelizes the field name" do
14
+ it "camelizes the field name, unless camelize: false" do
15
15
  assert_equal 'inspectInput', field.graphql_definition.name
16
+
17
+ underscored_field = GraphQL::Schema::Field.new(:underscored_field, String, null: false, camelize: false) do
18
+ argument :underscored_arg, String, required: true, camelize: false
19
+ end
20
+
21
+ assert_equal 'underscored_field', underscored_field.to_graphql.name
22
+ arg_name, arg_defn = underscored_field.to_graphql.arguments.first
23
+ assert_equal 'underscored_arg', arg_name
24
+ assert_equal 'underscored_arg', arg_defn.name
16
25
  end
17
26
 
18
27
  it "exposes the method override" do
@@ -36,5 +45,89 @@ describe GraphQL::Schema::Field do
36
45
  assert_equal "test", object.fields["test"].arguments["test"].name
37
46
  assert_equal "A Description.", object.fields["test"].description
38
47
  end
48
+
49
+ describe "extras" do
50
+ it "can get errors, which adds path" do
51
+ query_str = <<-GRAPHQL
52
+ query {
53
+ find(id: "Musician/Herbie Hancock") {
54
+ ... on Musician {
55
+ addError
56
+ }
57
+ }
58
+ }
59
+ GRAPHQL
60
+
61
+ res = Jazz::Schema.execute(query_str)
62
+ err = res["errors"].first
63
+ assert_equal "this has a path", err["message"]
64
+ assert_equal ["find", "addError"], err["path"]
65
+ assert_equal [{"line"=>4, "column"=>15}], err["locations"]
66
+ end
67
+ end
68
+
69
+ describe "complexity" do
70
+ it "accepts a keyword argument" do
71
+ object = Class.new(Jazz::BaseObject) do
72
+ graphql_name "complexityKeyword"
73
+
74
+ field :complexityTest, String, null: true, complexity: 25
75
+ end.to_graphql
76
+
77
+ assert_equal 25, object.fields["complexityTest"].complexity
78
+ end
79
+
80
+ it "accepts a proc in the definition block" do
81
+ object = Class.new(Jazz::BaseObject) do
82
+ graphql_name "complexityKeyword"
83
+
84
+ field :complexityTest, String, null: true do
85
+ complexity ->(_ctx, _args, _child_complexity) { 52 }
86
+ end
87
+ end.to_graphql
88
+
89
+ assert_equal 52, object.fields["complexityTest"].complexity.call(nil, nil, nil)
90
+ end
91
+
92
+ it "accepts an integer in the definition block" do
93
+ object = Class.new(Jazz::BaseObject) do
94
+ graphql_name "complexityKeyword"
95
+
96
+ field :complexityTest, String, null: true do
97
+ complexity 38
98
+ end
99
+ end.to_graphql
100
+
101
+ assert_equal 38, object.fields["complexityTest"].complexity
102
+ end
103
+
104
+ it 'fails if the complexity is not numeric and not a proc' do
105
+ err = assert_raises(RuntimeError) do
106
+ Class.new(Jazz::BaseObject) do
107
+ graphql_name "complexityKeyword"
108
+
109
+ field :complexityTest, String, null: true do
110
+ complexity 'One hundred and eighty'
111
+ end
112
+ end.to_graphql
113
+ end
114
+
115
+ assert_match /^Invalid complexity:/, err.message
116
+ end
117
+
118
+ it 'fails if the proc does not accept 3 parameters' do
119
+ err = assert_raises(RuntimeError) do
120
+ Class.new(Jazz::BaseObject) do
121
+ graphql_name "complexityKeyword"
122
+
123
+ field :complexityTest, String, null: true do
124
+ complexity ->(one, two) { 52 }
125
+ end
126
+ end.to_graphql
127
+ end
128
+
129
+ assert_match /^A complexity proc should always accept 3 parameters/, err.message
130
+ end
131
+ end
39
132
  end
40
133
  end