File: helpers_spec.rb

package info (click to toggle)
ruby-graphql 2.2.17-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 9,584 kB
  • sloc: ruby: 67,505; ansic: 1,753; yacc: 831; javascript: 331; makefile: 6
file content (174 lines) | stat: -rw-r--r-- 6,187 bytes parent folder | download
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