spec/graphql/schema/resolver_spec.rb in graphql-1.8.6 vs spec/graphql/schema/resolver_spec.rb in graphql-1.8.7
- old
+ new
@@ -74,11 +74,11 @@
class Resolver8 < Resolver7
end
class PrepResolver1 < BaseResolver
argument :int, Integer, required: true
-
+ undef_method :load_int
def load_int(i)
i * 10
end
type Integer, null: false
@@ -125,41 +125,126 @@
end
class PrepResolver5 < PrepResolver1
type Integer, null: true
- def before_prepare(int:)
+ def ready?(int:)
check_for_magic_number(int)
end
end
class PrepResolver6 < PrepResolver5
- def before_prepare(**args)
+ def ready?(**args)
LazyBlock.new {
super
}
end
end
- class PrepResolver7 < PrepResolver1
+ class PrepResolver7 < GraphQL::Schema::Mutation
+ argument :int, Integer, required: true
+ field :errors, [String], null: true
+ field :int, Integer, null: true
+
+ def ready?(int:)
+ if int == 13
+ return false, { errors: ["Bad number!"] }
+ else
+ true
+ end
+ end
+
+ def resolve(int:)
+ { int: int }
+ end
+ end
+
+ module HasValue
+ include GraphQL::Schema::Interface
+ field :value, Integer, null: false
+ def self.resolve_type(obj, ctx)
+ if obj.is_a?(Integer)
+ IntegerWrapper
+ else
+ raise "Unexpected: #{obj.inspect}"
+ end
+ end
+ end
+
+ class IntegerWrapper < GraphQL::Schema::Object
+ implements HasValue
+ field :value, Integer, null: false, method: :object
+ end
+
+ class PrepResolver9 < BaseResolver
+ argument :int_id, ID, required: true, loads: HasValue
+ # Make sure the lazy object is resolved properly:
+ type HasValue, null: false
+ def object_from_id(type, id, ctx)
+ # Make sure a lazy object is handled appropriately
+ LazyBlock.new {
+ # Make sure that the right type ends up here
+ id.to_i + type.graphql_name.length
+ }
+ end
+
+ def resolve(int:)
+ int * 3
+ end
+ end
+
+ class PrepResolver10 < BaseResolver
+ argument :int1, Integer, required: true
+ argument :int2, Integer, required: true
type Integer, null: true
+ def authorized?(int1:, int2:)
+ if int1 + int2 > context[:max_int]
+ raise GraphQL::ExecutionError, "Inputs too big"
+ elsif context[:min_int] && (int1 + int2 < context[:min_int])
+ false
+ else
+ true
+ end
+ end
- def load_int(int)
- int
+ def resolve(int1:, int2:)
+ int1 + int2
end
+ end
- def validate_int(int)
- check_for_magic_number(int)
+ class PrepResolver11 < PrepResolver10
+ def authorized?(int1:, int2:)
+ LazyBlock.new { super(int1: int1 * 2, int2: int2) }
end
end
- class PrepResolver8 < PrepResolver7
- def validate_int(int)
- LazyBlock.new { super }
+ class PrepResolver12 < GraphQL::Schema::Mutation
+ argument :int1, Integer, required: true
+ argument :int2, Integer, required: true
+ field :error_messages, [String], null: true
+ field :value, Integer, null: true
+ def authorized?(int1:, int2:)
+ if int1 + int2 > context[:max_int]
+ return false, { error_messages: ["Inputs must be less than #{context[:max_int]} (but you provided #{int1 + int2})"] }
+ else
+ true
+ end
end
+
+ def resolve(int1:, int2:)
+ { value: int1 + int2 }
+ end
end
+ class PrepResolver13 < PrepResolver12
+ def authorized?(int1:, int2:)
+ # Increment the numbers so we can be sure they're passing through here
+ LazyBlock.new { super(int1: int1 + 1, int2: int2 + 1) }
+ end
+ end
+
+
class Query < GraphQL::Schema::Object
class CustomField < GraphQL::Schema::Field
def resolve_field(*args)
value = super
if @name == "resolver3"
@@ -186,16 +271,21 @@
field :prep_resolver_3, resolver: PrepResolver3
field :prep_resolver_4, resolver: PrepResolver4
field :prep_resolver_5, resolver: PrepResolver5
field :prep_resolver_6, resolver: PrepResolver6
field :prep_resolver_7, resolver: PrepResolver7
- field :prep_resolver_8, resolver: PrepResolver8
+ field :prep_resolver_9, resolver: PrepResolver9
+ field :prep_resolver_10, resolver: PrepResolver10
+ field :prep_resolver_11, resolver: PrepResolver11
+ field :prep_resolver_12, resolver: PrepResolver12
+ field :prep_resolver_13, resolver: PrepResolver13
end
class Schema < GraphQL::Schema
query(Query)
lazy_resolve LazyBlock, :value
+ orphan_types IntegerWrapper
end
end
def exec_query(*args)
ResolverTest::Schema.execute(*args)
@@ -278,22 +368,30 @@
res = exec_query("{ int: #{field_name}(int: 200) }")
assert_nil res["data"].fetch("int"), "#{description}: No result for authorization error"
refute res.key?("errors"), "#{description}: silent auth failure (no top-level error)"
end
- describe "before_prepare" do
+ describe "ready?" do
it "can raise errors" do
res = exec_query("{ int: prepResolver5(int: 5) }")
assert_equal 50, res["data"]["int"]
- add_error_assertions("prepResolver5", "before_prepare")
+ add_error_assertions("prepResolver5", "ready?")
end
it "can raise errors in lazy sync" do
res = exec_query("{ int: prepResolver6(int: 5) }")
assert_equal 50, res["data"]["int"]
- add_error_assertions("prepResolver6", "lazy before_prepare")
+ add_error_assertions("prepResolver6", "lazy ready?")
end
+
+ it "can return false and data" do
+ res = exec_query("{ int: prepResolver7(int: 13) { errors int } }")
+ assert_equal ["Bad number!"], res["data"]["int"]["errors"]
+
+ res = exec_query("{ int: prepResolver7(int: 213) { errors int } }")
+ assert_equal 213, res["data"]["int"]["int"]
+ end
end
describe "loading arguments" do
it "calls load methods and injects the return value" do
res = exec_query("{ prepResolver1(int: 5) }")
@@ -317,20 +415,62 @@
add_error_assertions("prepResolver4", "lazy load_ hook")
end
end
describe "validating arguments" do
- test_cases = {
- "eager" => "prepResolver7",
- "lazy" => "prepResolver8",
- }
+ describe ".authorized?" do
+ it "can raise an error to halt" do
+ res = exec_query("{ prepResolver10(int1: 5, int2: 6) }", context: { max_int: 9 })
+ assert_equal ["Inputs too big"], res["errors"].map { |e| e["message"] }
- test_cases.each do |mode, field_name|
- it "supports raising #{mode} errors" do
- res = exec_query("{ validatedInt: #{field_name}(int: 5) }")
- assert_equal 5, res["data"]["validatedInt"]
- add_error_assertions(field_name, "#{mode} validation")
+ res = exec_query("{ prepResolver10(int1: 5, int2: 6) }", context: { max_int: 90 })
+ assert_equal 11, res["data"]["prepResolver10"]
end
+
+ it "can return a lazy object" do
+ # This is too big because it's modified in the overridden authorized? hook:
+ res = exec_query("{ prepResolver11(int1: 3, int2: 5) }", context: { max_int: 9 })
+ assert_equal ["Inputs too big"], res["errors"].map { |e| e["message"] }
+
+ res = exec_query("{ prepResolver11(int1: 3, int2: 5) }", context: { max_int: 90 })
+ assert_equal 8, res["data"]["prepResolver11"]
+ end
+
+ it "can return data early" do
+ res = exec_query("{ prepResolver12(int1: 9, int2: 5) { errorMessages } }", context: { max_int: 9 })
+ assert_equal ["Inputs must be less than 9 (but you provided 14)"], res["data"]["prepResolver12"]["errorMessages"]
+ # This works
+ res = exec_query("{ prepResolver12(int1: 2, int2: 5) { value } }", context: { max_int: 9 })
+ assert_equal 7, res["data"]["prepResolver12"]["value"]
+ end
+
+ it "can return data early in a promise" do
+ # This is too big because it's modified in the overridden authorized? hook:
+ res = exec_query("{ prepResolver13(int1: 4, int2: 4) { errorMessages } }", context: { max_int: 9 })
+ assert_equal ["Inputs must be less than 9 (but you provided 10)"], res["data"]["prepResolver13"]["errorMessages"]
+ # This works
+ res = exec_query("{ prepResolver13(int1: 2, int2: 5) { value } }", context: { max_int: 9 })
+ assert_equal 7, res["data"]["prepResolver13"]["value"]
+ end
+
+ it "can return false to halt" do
+ str = <<-GRAPHQL
+ {
+ prepResolver10(int1: 5, int2: 10)
+ prepResolver11(int1: 3, int2: 5)
+ }
+ GRAPHQL
+ res = exec_query(str, context: { max_int: 100, min_int: 20 })
+ assert_equal({ "prepResolver10" => nil, "prepResolver11" => nil }, res["data"])
+ end
+ end
+ end
+
+ describe "Loading inputs" do
+ it "calls object_from_id" do
+ res = exec_query('{ prepResolver9(intId: "5") { value } }')
+ # (5 + 8) * 3
+ assert_equal 39, res["data"]["prepResolver9"]["value"]
end
end
end
end