1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
|
# frozen_string_literal: true
require "spec_helper"
describe GraphQL::Testing::Helpers do
class AssertionsSchema < GraphQL::Schema
class BillSource < GraphQL::Dataloader::Source
def fetch(students)
students.map { |s| { amount: 1_000_001 } }
end
end
class TuitionBill < GraphQL::Schema::Object
def self.visible?(ctx)
ctx[:current_user]&.admin?
end
field :amount_in_cents, Int, hash_key: :amount
end
class Transcript < GraphQL::Schema::Object
def self.authorized?(object, context)
(current_user = context[:current_user]) &&
(admin_for = current_user[:admin_for]) &&
(admin_for.include?(object && object[:name]))
end
field :gpa, Float
end
class Student < GraphQL::Schema::Object
field :name, String do
argument :full_name, Boolean, required: false
argument :prefix, String, required: false, default_value: "Mc", prepare: ->(val, ctx) { -> { val.capitalize } }
end
def name(full_name: nil, prefix: nil)
name = object[:name]
if full_name
"#{name} #{prefix}#{name}"
else
name
end
end
field :latest_bill, TuitionBill
def latest_bill
dataloader.with(BillSource).load(object)
end
field :is_admin_for, Boolean
def is_admin_for
(list = context[:admin_for]) && list.include?(object[:name])
end
field :transcript, Transcript, resolver_method: :object
class Upcase < GraphQL::Schema::FieldExtension
def after_resolve(value:, **rest)
value.upcase
end
end
field :upcased_name, String, extensions: [Upcase], resolver_method: :name
field :ssn, String do
def authorized?(obj, args, ctx)
ctx[:current_user]&.admin?
end
end
end
class Query < GraphQL::Schema::Object
field :students, [Student]
end
query(Query)
use GraphQL::Dataloader
lazy_resolve Proc, :call
def self.unauthorized_object(err)
raise err
end
def self.unauthorized_field(err)
raise err
end
end
include GraphQL::Testing::Helpers
let(:admin_context) { { current_user: OpenStruct.new(admin?: true) } }
describe "top-level helpers" do
describe "run_graphql_field" do
it "resolves fields" do
assert_equal "Blah", run_graphql_field(AssertionsSchema, "Student.name", { name: "Blah" })
assert_equal "Blah McBlah", run_graphql_field(AssertionsSchema, "Student.name", { name: "Blah" }, arguments: { "fullName" => true })
assert_equal({ amount: 1_000_001 }, run_graphql_field(AssertionsSchema, "Student.latestBill", :student, context: admin_context))
end
it "works with resolution context" do
with_resolution_context(AssertionsSchema, object: { name: "Foo" }, type: "Student", context: { admin_for: ["Foo"] }) do |rc|
rc.run_graphql_field("name")
rc.run_graphql_field("isAdminFor")
end
end
it "raises an error when the type is hidden" do
assert_equal 1_000_000, run_graphql_field(AssertionsSchema, "TuitionBill.amountInCents", { amount: 1_000_000 }, context: admin_context)
err = assert_raises(GraphQL::Testing::Helpers::TypeNotVisibleError) do
run_graphql_field(AssertionsSchema, "TuitionBill.amountInCents", { amount: 1_000_000 })
end
expected_message = "`TuitionBill` should be `visible?` this field resolution and `context`, but it was not"
assert_equal expected_message, err.message
end
it "raises an error when the type isn't authorized" do
err = assert_raises GraphQL::UnauthorizedError do
run_graphql_field(AssertionsSchema, "Student.transcript.gpa", { gpa: 3.1 })
end
assert_equal "An instance of Hash failed Transcript's authorization check", err.message
assert_equal 3.1, run_graphql_field(AssertionsSchema, "Student.transcript.gpa", { gpa: 3.1, name: "Jim" }, context: { current_user: OpenStruct.new(admin_for: ["Jim"])})
end
it "works with field extensions" do
assert_equal "BILL", run_graphql_field(AssertionsSchema, "Student.upcasedName", { name: "Bill" })
end
it "prepares arguments" do
assert_equal "Blah De Blah", run_graphql_field(AssertionsSchema, "Student.name", { name: "Blah" }, arguments: { full_name: true, prefix: "de " })
end
it "handles unauthorized field errors" do
assert_equal "123-45-6789", run_graphql_field(AssertionsSchema, "Student.ssn", { ssn: "123-45-6789"}, context: admin_context)
err = assert_raises GraphQL::UnauthorizedFieldError do
run_graphql_field(AssertionsSchema, "Student.ssn", {})
end
assert_equal "An instance of Hash failed AssertionsSchema::Student's authorization check on field ssn", err.message
end
it "raises when the type doesn't exist" do
err = assert_raises GraphQL::Testing::Helpers::TypeNotDefinedError do
run_graphql_field(AssertionsSchema, "Nothing.nothing", :nothing)
end
assert_equal "No type named `Nothing` is defined; choose another type name or define this type.", err.message
end
it "raises when the field doesn't exist" do
err = assert_raises GraphQL::Testing::Helpers::FieldNotDefinedError do
run_graphql_field(AssertionsSchema, "Student.nonsense", :student)
end
assert_equal "`Student` has no field named `nonsense`; pick another name or define this field.", err.message
end
end
end
describe "schema-level helpers" do
include GraphQL::Testing::Helpers.for(AssertionsSchema)
it "resolves fields" do
assert_equal 5, run_graphql_field("TuitionBill.amountInCents", { amount: 5 }, context: admin_context)
end
it "works with resolution context" do
with_resolution_context(object: { name: "Foo" }, type: "Student", context: { admin_for: ["Bar"] }) do |rc|
assert_equal "Foo", rc.run_graphql_field("name")
assert_equal false, rc.run_graphql_field("isAdminFor")
end
end
end
end
|