graphql 1.5.15 → 1.6.0

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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql.rb +4 -19
  3. data/lib/graphql/analysis/analyze_query.rb +27 -2
  4. data/lib/graphql/analysis/query_complexity.rb +10 -11
  5. data/lib/graphql/argument.rb +7 -6
  6. data/lib/graphql/backwards_compatibility.rb +47 -0
  7. data/lib/graphql/compatibility/execution_specification.rb +14 -0
  8. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +6 -1
  9. data/lib/graphql/compatibility/lazy_execution_specification.rb +19 -0
  10. data/lib/graphql/compatibility/lazy_execution_specification/lazy_schema.rb +15 -6
  11. data/lib/graphql/directive.rb +1 -6
  12. data/lib/graphql/execution.rb +1 -0
  13. data/lib/graphql/execution/execute.rb +174 -160
  14. data/lib/graphql/execution/field_result.rb +5 -1
  15. data/lib/graphql/execution/lazy.rb +2 -2
  16. data/lib/graphql/execution/lazy/resolve.rb +8 -11
  17. data/lib/graphql/execution/multiplex.rb +134 -0
  18. data/lib/graphql/execution/selection_result.rb +5 -0
  19. data/lib/graphql/field.rb +1 -8
  20. data/lib/graphql/filter.rb +53 -0
  21. data/lib/graphql/internal_representation/node.rb +11 -6
  22. data/lib/graphql/internal_representation/rewrite.rb +3 -3
  23. data/lib/graphql/query.rb +160 -78
  24. data/lib/graphql/query/arguments.rb +14 -25
  25. data/lib/graphql/query/arguments_cache.rb +6 -13
  26. data/lib/graphql/query/context.rb +28 -10
  27. data/lib/graphql/query/executor.rb +1 -0
  28. data/lib/graphql/query/literal_input.rb +10 -4
  29. data/lib/graphql/query/null_context.rb +1 -1
  30. data/lib/graphql/query/serial_execution/field_resolution.rb +5 -1
  31. data/lib/graphql/query/validation_pipeline.rb +12 -7
  32. data/lib/graphql/query/variables.rb +1 -1
  33. data/lib/graphql/rake_task.rb +140 -0
  34. data/lib/graphql/relay/array_connection.rb +29 -48
  35. data/lib/graphql/relay/base_connection.rb +9 -7
  36. data/lib/graphql/relay/mutation.rb +0 -11
  37. data/lib/graphql/relay/mutation/instrumentation.rb +2 -2
  38. data/lib/graphql/relay/mutation/resolve.rb +7 -10
  39. data/lib/graphql/relay/relation_connection.rb +98 -61
  40. data/lib/graphql/scalar_type.rb +1 -15
  41. data/lib/graphql/schema.rb +90 -25
  42. data/lib/graphql/schema/build_from_definition.rb +22 -23
  43. data/lib/graphql/schema/build_from_definition/resolve_map.rb +70 -0
  44. data/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +47 -0
  45. data/lib/graphql/schema/middleware_chain.rb +1 -1
  46. data/lib/graphql/schema/printer.rb +2 -1
  47. data/lib/graphql/schema/timeout_middleware.rb +6 -6
  48. data/lib/graphql/schema/type_map.rb +1 -1
  49. data/lib/graphql/schema/warden.rb +5 -9
  50. data/lib/graphql/static_validation/definition_dependencies.rb +1 -1
  51. data/lib/graphql/version.rb +1 -1
  52. data/spec/graphql/analysis/analyze_query_spec.rb +2 -2
  53. data/spec/graphql/analysis/max_query_complexity_spec.rb +28 -0
  54. data/spec/graphql/argument_spec.rb +3 -3
  55. data/spec/graphql/execution/lazy_spec.rb +8 -114
  56. data/spec/graphql/execution/multiplex_spec.rb +131 -0
  57. data/spec/graphql/internal_representation/rewrite_spec.rb +10 -0
  58. data/spec/graphql/query/arguments_spec.rb +14 -16
  59. data/spec/graphql/query/context_spec.rb +14 -1
  60. data/spec/graphql/query/literal_input_spec.rb +19 -13
  61. data/spec/graphql/query/variables_spec.rb +1 -1
  62. data/spec/graphql/query_spec.rb +12 -1
  63. data/spec/graphql/rake_task_spec.rb +57 -0
  64. data/spec/graphql/relay/array_connection_spec.rb +24 -3
  65. data/spec/graphql/relay/connection_instrumentation_spec.rb +23 -0
  66. data/spec/graphql/relay/mutation_spec.rb +2 -10
  67. data/spec/graphql/relay/page_info_spec.rb +2 -2
  68. data/spec/graphql/relay/relation_connection_spec.rb +167 -3
  69. data/spec/graphql/schema/build_from_definition_spec.rb +93 -19
  70. data/spec/graphql/schema/warden_spec.rb +80 -0
  71. data/spec/graphql/schema_spec.rb +26 -2
  72. data/spec/spec_helper.rb +4 -2
  73. data/spec/support/lazy_helpers.rb +152 -0
  74. data/spec/support/star_wars/schema.rb +23 -0
  75. metadata +28 -3
  76. data/lib/graphql/schema/mask.rb +0 -55
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
3
+
4
+ describe GraphQL::Execution::Multiplex do
5
+ def multiplex(*a)
6
+ LazyHelpers::LazySchema.multiplex(*a)
7
+ end
8
+
9
+ let(:q1) { <<-GRAPHQL
10
+ query Q1 {
11
+ nestedSum(value: 3) {
12
+ value
13
+ nestedSum(value: 7) {
14
+ value
15
+ }
16
+ }
17
+ }
18
+ GRAPHQL
19
+ }
20
+ let(:q2) { <<-GRAPHQL
21
+ query Q2 {
22
+ nestedSum(value: 2) {
23
+ value
24
+ nestedSum(value: 11) {
25
+ value
26
+ }
27
+ }
28
+ }
29
+ GRAPHQL
30
+ }
31
+ let(:q3) { <<-GRAPHQL
32
+ query Q3 {
33
+ listSum(values: [1,2]) {
34
+ nestedSum(value: 3) {
35
+ value
36
+ }
37
+ }
38
+ }
39
+ GRAPHQL
40
+ }
41
+
42
+ let(:queries) { [{query: q1}, {query: q2}, {query: q3}] }
43
+
44
+ describe "multiple queries in the same lazy context" do
45
+ it "runs multiple queries in the same lazy context" do
46
+ expected_data = [
47
+ {"data"=>{"nestedSum"=>{"value"=>14, "nestedSum"=>{"value"=>46}}}},
48
+ {"data"=>{"nestedSum"=>{"value"=>14, "nestedSum"=>{"value"=>46}}}},
49
+ {"data"=>{"listSum"=>[{"nestedSum"=>{"value"=>14}}, {"nestedSum"=>{"value"=>14}}]}},
50
+ ]
51
+
52
+ res = multiplex(queries)
53
+ assert_equal expected_data, res
54
+ end
55
+ end
56
+
57
+ describe "when some have validation errors or runtime errors" do
58
+ let(:q1) { " { success: nullableNestedSum(value: 1) { value } }" }
59
+ let(:q2) { " { runtimeError: nullableNestedSum(value: 13) { value } }" }
60
+ let(:q3) { "{
61
+ invalidNestedNull: nullableNestedSum(value: 1) {
62
+ value
63
+ nullableNestedSum(value: 2) {
64
+ nestedSum(value: 13) {
65
+ value
66
+ }
67
+ }
68
+ }
69
+ }" }
70
+ let(:q4) { " { validationError: nullableNestedSum(value: true) }"}
71
+
72
+ it "returns a mix of errors and values" do
73
+ expected_res = [
74
+ {
75
+ "data"=>{"success"=>{"value"=>2}}
76
+ },
77
+ {
78
+ "data"=>{"runtimeError"=>nil},
79
+ "errors"=>[{
80
+ "message"=>"13 is unlucky",
81
+ "locations"=>[{"line"=>1, "column"=>4}],
82
+ "path"=>["runtimeError"]
83
+ }]
84
+ },
85
+ {
86
+ "data"=>{"invalidNestedNull"=>{"value" => 2,"nullableNestedSum" => nil}},
87
+ "errors"=>[{"message"=>"Cannot return null for non-nullable field LazySum.nestedSum"}],
88
+ },
89
+ {
90
+ "errors" => [{
91
+ "message"=>"Objects must have selections (field 'nullableNestedSum' returns LazySum but has no selections)",
92
+ "locations"=>[{"line"=>1, "column"=>4}],
93
+ "fields"=>["query", "validationError"]
94
+ }]
95
+ },
96
+ ]
97
+
98
+ res = multiplex([
99
+ {query: q1},
100
+ {query: q2},
101
+ {query: q3},
102
+ {query: q4},
103
+ ])
104
+ assert_equal expected_res, res
105
+ end
106
+ end
107
+
108
+ describe "context shared by a multiplex run" do
109
+ it "is provided as context:" do
110
+ checks = []
111
+ multiplex(queries, context: { instrumentation_checks: checks })
112
+ assert_equal ["before multiplex 1", "before multiplex 2", "after multiplex 2", "after multiplex 1"], checks
113
+ end
114
+ end
115
+
116
+ describe "instrumenting a multiplex run" do
117
+ it "runs query instrumentation for each query and multiplex-level instrumentation" do
118
+ checks = []
119
+ queries_with_context = queries.map { |q| q.merge(context: { instrumentation_checks: checks }) }
120
+ multiplex(queries_with_context, context: { instrumentation_checks: checks })
121
+ assert_equal [
122
+ "before multiplex 1",
123
+ "before multiplex 2",
124
+ "before Q1", "before Q2", "before Q3",
125
+ "after Q1", "after Q2", "after Q3",
126
+ "after multiplex 2",
127
+ "after multiplex 1",
128
+ ], checks
129
+ end
130
+ end
131
+ end
@@ -148,6 +148,16 @@ describe GraphQL::InternalRepresentation::Rewrite do
148
148
  assert_equal habitats_selection, seasons_selection.parent
149
149
  assert_equal habitats_selection, average_weight_selection.parent
150
150
  end
151
+
152
+ it "tracks field return type" do
153
+ doc = rewrite_result.operation_definitions["getPlant"]
154
+
155
+ assert plant_selection = doc.typed_children[schema.types["Query"]]["plant"]
156
+ assert_equal "Plant", plant_selection.return_type.to_s
157
+
158
+ assert tree_selection = plant_selection.typed_children[schema.types["Fruit"]]
159
+ assert_equal "[Int!]!", tree_selection["color"].return_type.to_s
160
+ end
151
161
  end
152
162
 
153
163
  describe "tracking directives on fragment spreads" do
@@ -26,8 +26,8 @@ describe GraphQL::Query::Arguments do
26
26
  }, argument_definitions: test_input_2.arguments)
27
27
  }
28
28
 
29
- it "returns keys as strings" do
30
- assert_equal(["a", "b", "c"], arguments.keys)
29
+ it "returns keys as strings, with aliases" do
30
+ assert_equal(["a", "b", "inputObject"], arguments.keys)
31
31
  end
32
32
 
33
33
  it "delegates values to values hash" do
@@ -39,11 +39,11 @@ describe GraphQL::Query::Arguments do
39
39
  arguments.each do |key, value|
40
40
  pairs << [key, value]
41
41
  end
42
- assert_equal([["a", 1], ["b", 2], ["c", {"d" => 3, "e" => 4}]], pairs)
42
+ assert_equal([["a", 1], ["b", 2], ["inputObject", {"d" => 3, "e" => 4}]], pairs)
43
43
  end
44
44
 
45
- it "returns original Ruby hash values with to_h" do
46
- assert_equal({ a: 1, b: 2, c: { d: 3, e: 4 } }, arguments.to_h)
45
+ it "returns a stringified, aliased hash with to_h" do
46
+ assert_equal({ "a"=> 1, "b" => 2, "inputObject" => { "d" => 3, "e" => 4 } }, arguments.to_h)
47
47
  end
48
48
 
49
49
  it "yields key, value, and arg_defnition" do
@@ -52,10 +52,11 @@ describe GraphQL::Query::Arguments do
52
52
  value = arg_value.value.is_a?(GraphQL::Query::Arguments) ? arg_value.value.to_h : arg_value.value
53
53
  type_info << [arg_value.key, value, arg_value.definition.type.unwrap.name]
54
54
  end
55
+
55
56
  expected_type_info =[
56
57
  ["a", 1, "Int"],
57
58
  ["b", 2, "Int"],
58
- ["inputObject", { d: 3, e: 4 }, "TestInput1"],
59
+ ["inputObject", { "d" => 3, "e" => 4 }, "TestInput1"],
59
60
  ]
60
61
  assert_equal expected_type_info, type_info
61
62
  end
@@ -65,14 +66,18 @@ describe GraphQL::Query::Arguments do
65
66
  types = {}
66
67
  arguments.each_value do |arg_value|
67
68
  transformed_args[arg_value.key.upcase] = arg_value.value
68
- types[arg_value.key.upcase] = arg_value.definition
69
+ defn = arg_value.definition
70
+ types[arg_value.key.upcase] = defn.redefine(
71
+ name: defn.name.upcase,
72
+ as: defn.as ? defn.as.to_s.upcase : nil,
73
+ )
69
74
  end
70
75
 
71
76
  new_arguments = GraphQL::Query::Arguments.new(transformed_args, argument_definitions: types)
72
77
  expected_hash = {
73
78
  "A" => 1,
74
79
  "B" => 2,
75
- "INPUTOBJECT" => { d: 3 , e: 4 },
80
+ "INPUTOBJECT" => { "d" => 3 , "e" => 4 },
76
81
  }
77
82
  assert_equal expected_hash, new_arguments.to_h
78
83
  end
@@ -176,6 +181,7 @@ describe GraphQL::Query::Arguments do
176
181
  last_args = arg_values.last
177
182
 
178
183
  assert_equal true, last_args.key?(:specialKeyName)
184
+ assert_equal true, last_args.key?("specialKeyName")
179
185
  end
180
186
 
181
187
  it "works from query literals" do
@@ -190,14 +196,6 @@ describe GraphQL::Query::Arguments do
190
196
  assert_equal({"a" => 1, "b" => 2}, last_args.to_h)
191
197
  end
192
198
 
193
- it "uses Field#default_arguments when no args are provided" do
194
- schema.execute("{ argTest noArgTest noDefaultsTest }")
195
-
196
- assert schema.query.get_field("argTest").default_arguments.eql?(arg_values[0])
197
- assert GraphQL::Query::Arguments::NO_ARGS.eql?(arg_values[1])
198
- assert GraphQL::Query::Arguments::NO_ARGS.eql?(arg_values[2])
199
- end
200
-
201
199
  it "works from variables" do
202
200
  variables = { "arg" => { "a" => 1, "d" => nil } }
203
201
  schema.execute("query ArgTest($arg: TestInput){ argTest(d: $arg) }", variables: variables)
@@ -75,8 +75,10 @@ describe GraphQL::Query::Context do
75
75
  describe "empty values" do
76
76
  let(:context) { GraphQL::Query::Context.new(query: OpenStruct.new(schema: schema), values: nil) }
77
77
 
78
- it "returns nil for any key" do
78
+ it "returns returns nil and reports key? => false" do
79
79
  assert_equal(nil, context[:some_key])
80
+ assert_equal(false, context.key?(:some_key))
81
+ assert_raises(KeyError) { context.fetch(:some_key) }
80
82
  end
81
83
  end
82
84
 
@@ -88,6 +90,17 @@ describe GraphQL::Query::Context do
88
90
  context[:some_key] = "wow!"
89
91
  assert_equal("wow!", context[:some_key])
90
92
  end
93
+
94
+ describe "namespaces" do
95
+ let(:context) { GraphQL::Query::Context.new(query: OpenStruct.new(schema: schema), values: {a: 1}) }
96
+
97
+ it "doesn't conflict with base values" do
98
+ ns = context.namespace(:stuff)
99
+ ns[:b] = 2
100
+ assert_equal({a: 1}, context.to_h)
101
+ assert_equal({b: 2}, context.namespace(:stuff))
102
+ end
103
+ end
91
104
  end
92
105
 
93
106
  describe "accessing context after the fact" do
@@ -8,13 +8,14 @@ describe GraphQL::Query::LiteralInput do
8
8
  query = GraphQL::ObjectType.define do
9
9
  name "Query"
10
10
 
11
- field :addOneToArgumentValue do
11
+ field :addToArgumentValue do
12
12
  type !types.Int
13
13
  argument :value do
14
- type !types.Int
15
- prepare ->(arg) do
14
+ type types.Int
15
+ default_value 3
16
+ prepare ->(arg, ctx) do
16
17
  return GraphQL::ExecutionError.new("Can't return more than 3 digits") if arg > 998
17
- arg + 1
18
+ arg + ctx[:val]
18
19
  end
19
20
  end
20
21
  resolve ->(t, a, c) { a[:value] }
@@ -25,26 +26,31 @@ describe GraphQL::Query::LiteralInput do
25
26
  }
26
27
 
27
28
  it "prepares values from query literals" do
28
- result = schema.execute("{ addOneToArgumentValue(value: 1) }")
29
- assert_equal(result["data"]["addOneToArgumentValue"], 2)
29
+ result = schema.execute("{ addToArgumentValue(value: 1) }", context: { val: 1 })
30
+ assert_equal(result["data"]["addToArgumentValue"], 2)
31
+ end
32
+
33
+ it "prepares default values" do
34
+ result = schema.execute("{ addToArgumentValue }", context: { val: 4 })
35
+ assert_equal(7, result["data"]["addToArgumentValue"])
30
36
  end
31
37
 
32
38
  it "prepares values from variables" do
33
- result = schema.execute("query ($value: Int!) { addOneToArgumentValue(value: $value) }", variables: { "value" => 1} )
34
- assert_equal(result["data"]["addOneToArgumentValue"], 2)
39
+ result = schema.execute("query ($value: Int!) { addToArgumentValue(value: $value) }", variables: { "value" => 1}, context: { val: 2 } )
40
+ assert_equal(result["data"]["addToArgumentValue"], 3)
35
41
  end
36
42
 
37
43
  it "prepares values correctly if called multiple times with different arguments" do
38
- result = schema.execute("{ first: addOneToArgumentValue(value: 1) second: addOneToArgumentValue(value: 2) }")
39
- assert_equal(result["data"]["first"], 2)
40
- assert_equal(result["data"]["second"], 3)
44
+ result = schema.execute("{ first: addToArgumentValue(value: 1) second: addToArgumentValue(value: 2) }", context: { val: 3 })
45
+ assert_equal(result["data"]["first"], 4)
46
+ assert_equal(result["data"]["second"], 5)
41
47
  end
42
48
 
43
49
  it "adds message to errors key if an ExecutionError is returned from the prepare function" do
44
- result = schema.execute("{ addOneToArgumentValue(value: 999) }")
50
+ result = schema.execute("{ addToArgumentValue(value: 999) }")
45
51
  assert_equal(result["errors"][0]["message"], "Can't return more than 3 digits")
46
52
  assert_equal(result["errors"][0]["locations"][0]["line"], 1)
47
- assert_equal(result["errors"][0]["locations"][0]["column"], 25)
53
+ assert_equal(result["errors"][0]["locations"][0]["column"], 22)
48
54
  end
49
55
  end
50
56
  end
@@ -19,7 +19,7 @@ describe GraphQL::Query::Variables do
19
19
  let(:variables) { GraphQL::Query::Variables.new(
20
20
  OpenStruct.new({
21
21
  schema: schema,
22
- warden: GraphQL::Schema::Warden.new(schema.default_mask, schema: schema, context: nil),
22
+ warden: GraphQL::Schema::Warden.new(schema.default_filter, schema: schema, context: nil),
23
23
  }),
24
24
  ast_variables,
25
25
  provided_variables)
@@ -52,9 +52,20 @@ describe GraphQL::Query do
52
52
  variables: query_variables,
53
53
  operation_name: operation_name,
54
54
  max_depth: max_depth,
55
- )
55
+ ).result
56
56
  }
57
57
  end
58
+
59
+ it 'can be assigned later' do
60
+ query = GraphQL::Query.new(
61
+ schema,
62
+ variables: query_variables,
63
+ operation_name: operation_name,
64
+ max_depth: max_depth,
65
+ )
66
+ query.query_string = '{ __type(name: "Cheese") { name } }'
67
+ assert_equal "Cheese", query.result["data"] ["__type"]["name"]
68
+ end
58
69
  end
59
70
 
60
71
  describe "operation_name" do
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
3
+
4
+ rake_task_schema_defn = <<-GRAPHQL
5
+ type Query {
6
+ allowed(allowed: ID!, excluded: ID!): Int
7
+ excluded(excluded: ID!): Boolean
8
+ ignored: Float
9
+ }
10
+ GRAPHQL
11
+
12
+ RakeTaskSchema = GraphQL::Schema.from_definition(rake_task_schema_defn)
13
+
14
+ # Default task
15
+ GraphQL::RakeTask.new(schema_name: "RakeTaskSchema")
16
+ # Configured task
17
+ GraphQL::RakeTask.new(idl_outfile: "tmp/configured_schema.graphql") do |t|
18
+ t.namespace = "graphql_custom"
19
+ t.load_context = ->(task) { {filtered: true} }
20
+ t.only = ->(member, ctx) { member.is_a?(GraphQL::ScalarType) || (ctx[:filtered] && ["Query", "allowed"].include?(member.name)) }
21
+ t.load_schema = ->(task) { RakeTaskSchema }
22
+ end
23
+
24
+ describe GraphQL::RakeTask do
25
+ describe "default settings" do
26
+ after do
27
+ FileUtils.rm_rf("./schema.json")
28
+ FileUtils.rm_rf("./schema.graphql")
29
+ end
30
+
31
+ it "writes JSON" do
32
+ capture_io do
33
+ Rake::Task["graphql:schema:dump"].invoke
34
+ end
35
+ dumped_json = File.read("./schema.json")
36
+ expected_json = JSON.pretty_generate(RakeTaskSchema.execute(GraphQL::Introspection::INTROSPECTION_QUERY))
37
+ assert_equal(expected_json, dumped_json)
38
+
39
+ dumped_idl = File.read("./schema.graphql")
40
+ expected_idl = rake_task_schema_defn.chomp
41
+ assert_equal(expected_idl, dumped_idl)
42
+ end
43
+ end
44
+
45
+ describe "customized settings" do
46
+ it "writes GraphQL" do
47
+ capture_io do
48
+ Rake::Task["graphql_custom:schema:idl"].invoke
49
+ end
50
+ dumped_idl = File.read("./tmp/configured_schema.graphql")
51
+ expected_idl = "type Query {
52
+ allowed(allowed: ID!): Int
53
+ }"
54
+ assert_equal expected_idl, dumped_idl
55
+ end
56
+ end
57
+ end
@@ -89,6 +89,27 @@ describe GraphQL::Relay::ArrayConnection do
89
89
  assert_equal(false, result["data"]["rebels"]["ships"]["pageInfo"]["hasPreviousPage"])
90
90
  end
91
91
 
92
+ it 'works with before and after specified together' do
93
+ result = star_wars_query(query_string, "first" => 1)
94
+ assert_equal(["X-Wing"], get_names(result))
95
+
96
+ first_cursor = get_last_cursor(result)
97
+
98
+ # There is no records between before and after if they point to the same cursor
99
+ result = star_wars_query(query_string, "before" => first_cursor, "after" => first_cursor, "last" => 2)
100
+ assert_equal([], get_names(result))
101
+
102
+ result = star_wars_query(query_string, "after" => first_cursor, "first" => 2)
103
+ assert_equal(["Y-Wing", "A-Wing"], get_names(result))
104
+
105
+ # After the last result, find the next 2:
106
+ second_cursor = get_last_cursor(result)
107
+
108
+ # There is only 2 results between the cursors
109
+ result = star_wars_query(query_string, "after" => first_cursor, "before" => second_cursor, "first" => 5)
110
+ assert_equal(["Y-Wing", "A-Wing"], get_names(result))
111
+ end
112
+
92
113
  it 'handles cursors beyond the bounds of the array' do
93
114
  overreaching_cursor = Base64.strict_encode64("100")
94
115
  result = star_wars_query(query_string, "after" => overreaching_cursor, "first" => 2)
@@ -157,13 +178,13 @@ describe GraphQL::Relay::ArrayConnection do
157
178
 
158
179
  it "applies to queries by `last`" do
159
180
  last_cursor = "Ng=="
160
- second_to_last_two_names = ["Death Star", "Shield Generator"]
181
+
161
182
  result = star_wars_query(query_string, "last" => 100, "before" => last_cursor)
162
- assert_equal(second_to_last_two_names, get_names(result))
183
+ assert_equal(["Death Star", "Shield Generator"], get_names(result))
163
184
  assert_equal(true, get_page_info(result)["hasPreviousPage"])
164
185
 
165
186
  result = star_wars_query(query_string, "before" => last_cursor)
166
- assert_equal(second_to_last_two_names, get_names(result))
187
+ assert_equal(["Yavin", "Echo Base"], get_names(result))
167
188
  assert_equal(false, get_page_info(result)["hasPreviousPage"], "hasPreviousPage is false when last is not specified")
168
189
 
169
190
  third_cursor = "Mw=="