payload-api 0.4.1 → 0.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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +2 -0
  3. data/LICENSE +1 -1
  4. data/README.md +23 -2
  5. data/lib/payload/arm/attr.rb +169 -0
  6. data/lib/payload/arm/object.rb +44 -1
  7. data/lib/payload/arm/request.rb +66 -6
  8. data/lib/payload/arm/session.rb +13 -9
  9. data/lib/payload/exceptions.rb +15 -0
  10. data/lib/payload/objects.rb +81 -18
  11. data/lib/payload/version.rb +2 -2
  12. data/lib/payload.rb +15 -5
  13. data/spec/objects/v1/access_token_spec.rb +19 -0
  14. data/spec/objects/v1/account_spec.rb +97 -0
  15. data/spec/objects/v1/billing_spec.rb +54 -0
  16. data/spec/objects/v1/invoice_spec.rb +53 -0
  17. data/spec/objects/v1/payment_link_spec.rb +50 -0
  18. data/spec/objects/v1/payment_method_spec.rb +106 -0
  19. data/spec/objects/{payment_spec.rb → v1/payment_spec.rb} +5 -6
  20. data/spec/objects/v1/session_spec.rb +89 -0
  21. data/spec/objects/v1/transaction_spec.rb +55 -0
  22. data/spec/objects/v2/account_spec.rb +211 -0
  23. data/spec/objects/v2/invoice_spec.rb +53 -0
  24. data/spec/objects/v2/payment_method_spec.rb +106 -0
  25. data/spec/objects/v2/transaction_spec.rb +48 -0
  26. data/spec/payload/arm/arm_request_query_spec.rb +226 -0
  27. data/spec/payload/arm/attr_spec.rb +216 -0
  28. data/spec/payload/arm/object_spec.rb +114 -0
  29. data/spec/payload/arm/request_format_integration_spec.rb +166 -0
  30. data/spec/payload/arm/request_spec.rb +259 -1
  31. data/spec/payload/arm/session_spec.rb +40 -0
  32. data/spec/payload/exceptions_spec.rb +82 -0
  33. data/spec/support/helpers/v1_helpers.rb +159 -0
  34. data/spec/support/helpers/v2_helpers.rb +205 -0
  35. data/spec/support/helpers.rb +15 -0
  36. data/spec/support/helpers_spec.rb +21 -0
  37. metadata +28 -6
@@ -0,0 +1,211 @@
1
+ require 'payload'
2
+ require 'payload/arm/object'
3
+ require_relative '../../support/helpers'
4
+
5
+ RSpec.describe 'Account Integration Tests - V2' do
6
+ include_context 'test helpers'
7
+
8
+ let(:session) { Payload::Session.new(Payload.api_key, Payload.api_url, 2) }
9
+
10
+ describe 'Customer Account' do
11
+ it 'creates a customer account' do
12
+ customer_account = session.Account.create(
13
+ type: 'customer',
14
+ name: 'Test',
15
+ contact_details: {
16
+ email: 'test@example.com'
17
+ }
18
+ )
19
+ expect(customer_account.id).to be_truthy
20
+ end
21
+
22
+ it 'deletes a customer account' do
23
+ cust_account = session.Account.create(
24
+ type: 'customer',
25
+ name: 'Test',
26
+ contact_details: {
27
+ email: 'test@example.com'
28
+ }
29
+ )
30
+ cust_account.delete
31
+
32
+ expect {
33
+ session.Account.get(cust_account.id)
34
+ }.to raise_error(Payload::NotFound)
35
+ end
36
+
37
+ it 'creates multiple accounts' do
38
+ rand_email1 = (0...5).map { ('a'..'z').to_a[rand(26)] }.join + '@example.com'
39
+ rand_email2 = (0...5).map { ('a'..'z').to_a[rand(26)] }.join + '@example.com'
40
+
41
+ name1 = 'Matt Perez'
42
+ name2 = 'Andrea Kearney'
43
+
44
+ accounts_to_create = [
45
+ session.Account.new(type: 'customer', contact_details: { email: rand_email1 }, name: name1),
46
+ session.Account.new(type: 'customer', contact_details: { email: rand_email2 }, name: name2)
47
+ ]
48
+ session.create(accounts_to_create)
49
+
50
+ get_account_1 = session.Account.filter_by(type: 'customer', 'contact_details[email]': rand_email1).all[0]
51
+ get_account_2 = session.Account.filter_by(type: 'customer', 'contact_details[email]': rand_email2).all[0]
52
+
53
+ expect(get_account_1).to be_truthy
54
+ expect(get_account_2).to be_truthy
55
+ end
56
+
57
+ it 'gets a processing account' do
58
+ orgs = session.Profile.all()
59
+ org = orgs[0]
60
+ proc_account = session.Account.create(
61
+ type: 'processing',
62
+ name: 'Processing Account',
63
+ processing: {
64
+ status: { funding: 'pending' },
65
+ settings_id: org.processing_settings_id,
66
+ },
67
+ payment_methods: [session.PaymentMethod.new(
68
+ type: 'bank_account',
69
+ bank_account: {
70
+ account_number: '123456789',
71
+ routing_number: '036001808',
72
+ account_type: 'checking'
73
+ },
74
+ billing_address: {
75
+ postal_code: '11111',
76
+ },
77
+ account_defaults: {
78
+ funding: 'all',
79
+ }
80
+ )],
81
+ entity: {
82
+ type: 'business',
83
+ legal_name: 'Example',
84
+ country: 'US',
85
+ phone_number: '123 123-1234',
86
+ tax_id: { value: '123 12 1234' },
87
+ address: {
88
+ address_line_1: '123 Example St',
89
+ city: 'New York',
90
+ state_province: 'NY',
91
+ postal_code: '11111',
92
+ },
93
+ business: {
94
+ category: 'real_estate',
95
+ structure: 'llc',
96
+ website: 'https://example.com',
97
+ formation: {
98
+ state_province: 'NY',
99
+ date: '2019-10-01',
100
+ },
101
+ primary_contact: {
102
+ name: 'John Smith',
103
+ email: 'johnsmith@gmail.com',
104
+ },
105
+ stakeholders: [
106
+ {
107
+ country: 'US',
108
+ personal_information: {
109
+ full_name: 'John Smith',
110
+ email: 'johnsmith@gmail.com',
111
+ birth_date: '1990-05-10',
112
+ phone_number: '123 123-1234',
113
+ },
114
+ address: {
115
+ address_line_1: '123 Example St',
116
+ city: 'New York',
117
+ state_province: 'NY',
118
+ postal_code: '11111',
119
+ },
120
+ govt_id: {
121
+ tax_id: { value: '123 12 1234' },
122
+ },
123
+ association: {
124
+ roles: ['principal_officer'],
125
+ title: 'CEO',
126
+ ownership: {
127
+ percentage: 100,
128
+ years_owned: 5,
129
+ },
130
+ },
131
+ },
132
+ ],
133
+ },
134
+ },
135
+ )
136
+ retrieved = session.Account.get(proc_account.id)
137
+ expect(retrieved).to be_truthy
138
+ expect(proc_account.processing['status']['funding']).to eq('pending')
139
+ end
140
+
141
+ it 'pages and orders results' do
142
+ accounts_to_create = [
143
+ session.Account.new(type: 'customer', contact_details: { email: 'account1@example.com' }, name: 'Randy Robson'),
144
+ session.Account.new(type: 'customer', contact_details: { email: 'account2@example.com' }, name: 'Brandy Bobson'),
145
+ session.Account.new(type: 'customer', contact_details: { email: 'account3@example.com' }, name: 'Mandy Johnson')
146
+ ]
147
+ session.create(accounts_to_create)
148
+ customers = session.Account.filter_by(type: 'customer', order_by: 'created_at', limit: 3, offset: 1).all
149
+
150
+ expect(customers.length).to eq(3)
151
+ require 'time'
152
+ expect(Time.parse(customers[0].created_at)).to be <= Time.parse(customers[1].created_at)
153
+ expect(Time.parse(customers[1].created_at)).to be <= Time.parse(customers[2].created_at)
154
+ end
155
+
156
+ it 'updates a customer' do
157
+ cust_account = session.Account.create(
158
+ type: 'customer',
159
+ name: 'Test',
160
+ contact_details: {
161
+ email: 'test@example.com'
162
+ }
163
+ )
164
+ cust_account.update(contact_details: { email: 'test2@example.com' })
165
+ expect(cust_account.contact_details['email']).to eq('test2@example.com')
166
+ end
167
+
168
+ it 'updates multiple accounts' do
169
+ customer_account_1 = session.Account.create(
170
+ type: 'customer',
171
+ name: 'Brandy',
172
+ contact_details: { email: 'test1@example.com' }
173
+ )
174
+ customer_account_2 = session.Account.create(
175
+ type: 'customer',
176
+ name: 'Sandy',
177
+ contact_details: { email: 'test2@example.com' }
178
+ )
179
+ updated_accounts = session.update([
180
+ [customer_account_1, { contact_details: { email: 'brandy@example.com' } }],
181
+ [customer_account_2, { contact_details: { email: 'sandy@example.com' } }]
182
+ ])
183
+
184
+ expect(updated_accounts[0].contact_details['email']).to eq('brandy@example.com')
185
+ expect(updated_accounts[1].contact_details['email']).to eq('sandy@example.com')
186
+ end
187
+
188
+ it 'gets a customer' do
189
+ cust_account = session.Account.create(
190
+ type: 'customer',
191
+ name: 'Test',
192
+ contact_details: {
193
+ email: 'test@example.com'
194
+ }
195
+ )
196
+ expect(session.Account.get(cust_account.id)).to be_truthy
197
+ end
198
+
199
+ it 'selects customer attributes' do
200
+ cust_account = session.Account.create(
201
+ type: 'customer',
202
+ name: 'Test',
203
+ contact_details: {
204
+ email: 'test@example.com'
205
+ }
206
+ )
207
+ selected = session.Account.select('id').get(cust_account.id)
208
+ expect(selected['id']).to eq(cust_account.id)
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,53 @@
1
+ require 'payload'
2
+ require 'payload/arm/object'
3
+ require 'date'
4
+ require_relative '../../support/helpers'
5
+
6
+ RSpec.describe 'Invoice Integration Tests - V2' do
7
+ include_context 'test helpers'
8
+
9
+ let(:session) { Payload::Session.new(Payload.api_key, Payload.api_url, 2) }
10
+ let(:h) { V2Helpers.new(session) }
11
+
12
+ let(:proc_account) { h.create_processing_account }
13
+ let(:customer_account) { h.create_customer_account }
14
+ let(:invoice) { h.create_invoice(proc_account, customer_account) }
15
+
16
+ describe 'Invoice' do
17
+ it 'creates an invoice' do
18
+ inv = invoice
19
+ expect(inv.due_date).to eq(Date.today.strftime('%Y-%m-%d'))
20
+ expect(inv.status).to eq('unpaid')
21
+ end
22
+
23
+ it 'pays an invoice' do
24
+ inv = invoice
25
+ expect(inv.due_date).to eq(Date.today.strftime('%Y-%m-%d'))
26
+ expect(inv.status).to eq('unpaid')
27
+
28
+ amount = inv.totals['balance_due']
29
+
30
+ if inv.status != 'paid'
31
+ h.create_card_payment(
32
+ proc_account.id,
33
+ amount: amount,
34
+ description: 'Test Payment',
35
+ customer_id: customer_account.id,
36
+ invoice_id: inv.id
37
+ )
38
+ end
39
+
40
+ get_invoice = session.Invoice.get(inv.id)
41
+ expect(get_invoice.status).to eq('paid')
42
+ end
43
+
44
+ it 'deletes an invoice' do
45
+ inv = invoice
46
+ inv.delete
47
+
48
+ expect {
49
+ session.Invoice.get(inv.id)
50
+ }.to raise_error(Payload::NotFound)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,106 @@
1
+ require 'payload'
2
+ require 'payload/arm/object'
3
+ require_relative '../../support/helpers'
4
+
5
+ RSpec.describe 'Payment Method Integration Tests - V2' do
6
+ include_context 'test helpers'
7
+
8
+ let(:session) { Payload::Session.new(Payload.api_key, Payload.api_url, 2) }
9
+ let(:h) { V2Helpers.new(session) }
10
+ let(:proc_account) { h.create_processing_account }
11
+
12
+ describe 'Payment Methods' do
13
+ it 'creates a payment with card' do
14
+ card_payment = h.create_card_payment(proc_account.id)
15
+ expect(card_payment.status['value']).to eq('processed')
16
+ end
17
+
18
+ it 'creates a payment with bank account' do
19
+ bank_payment = h.create_bank_payment
20
+ expect(bank_payment.status['value']).to eq('processed')
21
+ end
22
+
23
+ it 'filters payments' do
24
+ rand_description = (0...10).map { ('a'..'z').to_a[rand(26)] }.join
25
+
26
+ amounts = [90.0, 100.0, 110.0]
27
+ card_payments = []
28
+ amounts.each do |amount|
29
+ card_payment = h.create_card_payment(proc_account.id, amount: amount, description: rand_description)
30
+ card_payments << card_payment
31
+ end
32
+
33
+ payments = session.Transaction.filter_by(
34
+ type: 'payment',
35
+ amount: '100',
36
+ description: rand_description
37
+ ).all
38
+
39
+ expect(payments.length).to be == 1
40
+ expect(payments.map(&:id)).to include(card_payments[1].id)
41
+ end
42
+
43
+ it 'voids a card payment' do
44
+ card_payment = h.create_card_payment(proc_account.id)
45
+ card_payment.update(status: { value: 'voided' })
46
+ expect(card_payment.status['value']).to eq('voided')
47
+ end
48
+
49
+ it 'voids a bank payment' do
50
+ bank_payment = h.create_bank_payment
51
+ bank_payment.update(status: { value: 'voided' })
52
+ expect(bank_payment.status['value']).to eq('voided')
53
+ end
54
+
55
+ it 'refunds a card payment' do
56
+ card_payment = h.create_card_payment(proc_account.id)
57
+ refund = h.create_refund(card_payment)
58
+
59
+ expect(refund.type).to eq('refund')
60
+ expect(refund.amount).to eq(card_payment.amount)
61
+ end
62
+
63
+ it 'partially refunds a card payment' do
64
+ card_payment = h.create_card_payment(proc_account.id)
65
+ amount = (card_payment.amount/2).round(2) # rounded to 2 decimal places
66
+ refund = h.create_refund(card_payment, amount: amount)
67
+
68
+ expect(refund.type).to eq('refund')
69
+ expect(refund.amount).to eq(amount)
70
+ end
71
+
72
+ it 'creates a blind refund for card payment' do
73
+ refund = h.create_blind_refund(10, proc_account.id)
74
+
75
+ expect(refund.type).to eq('refund')
76
+ expect(refund.amount).to eq(10)
77
+ end
78
+
79
+ it 'refunds a bank payment' do
80
+ bank_payment = h.create_bank_payment
81
+ refund = h.create_refund(bank_payment)
82
+
83
+ expect(refund.type).to eq('refund')
84
+ expect(refund.amount).to eq(bank_payment.amount)
85
+ end
86
+
87
+ it 'partially refunds a bank payment' do
88
+ bank_payment = h.create_bank_payment
89
+ amount = (bank_payment.amount/2).round(2) # rounded to 2 decimal places
90
+ refund = h.create_refund(bank_payment, amount: amount)
91
+
92
+ expect(refund.type).to eq('refund')
93
+ expect(refund.amount).to eq(amount)
94
+ end
95
+
96
+ it 'raises error for invalid payment method type' do
97
+ expect {
98
+ session.Transaction.create(
99
+ type: 'invalid',
100
+ card_number: '4242 4242 4242 4242',
101
+ expiry: '12/30'
102
+ )
103
+ }.to raise_error(Payload::InvalidAttributes)
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,48 @@
1
+ require 'payload'
2
+ require 'payload/arm/object'
3
+ require_relative '../../support/helpers'
4
+
5
+ RSpec.describe 'Transaction Integration Tests - V2' do
6
+ include_context 'test helpers'
7
+
8
+ let(:session) { Payload::Session.new(Payload.api_key, Payload.api_url, 2) }
9
+ let(:h) { V2Helpers.new(session) }
10
+ let(:proc_account) { h.create_processing_account }
11
+
12
+ describe 'Transactions' do
13
+
14
+ it 'has empty transaction ledger' do
15
+ card_payment = h.create_card_payment(proc_account.id)
16
+ transaction = session.Transaction.select('*', 'ledger').get(card_payment.id)
17
+ expect(transaction.ledger).to eq([])
18
+ end
19
+
20
+ it 'tests unified payout batching' do
21
+ h.create_blind_refund(10, proc_account.id)
22
+
23
+ transactions = session.Transaction.select('*', 'ledger')
24
+ .filter_by(type: 'refund', processing_id: proc_account.id)
25
+ .all
26
+
27
+ expect(transactions.length).to eq(1)
28
+ end
29
+
30
+ it 'gets transactions' do
31
+ h.create_card_payment(proc_account.id)
32
+ payments = session.Transaction.filter_by('status[value]': 'processed', type: 'payment', 'receiver[account_id]': proc_account.id).all
33
+ expect(payments.length).to be > 0
34
+ end
35
+
36
+ it 'updates processed transaction' do
37
+ card_payment = h.create_card_payment(proc_account.id)
38
+ card_payment.update(status: { value: 'voided' })
39
+ expect(card_payment.status['value']).to eq('voided')
40
+ end
41
+
42
+ it 'raises error for transaction not found' do
43
+ expect {
44
+ session.Transaction.get('invalid')
45
+ }.to raise_error(Payload::NotFound)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,226 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "payload"
4
+ require "payload/arm/object"
5
+
6
+ RSpec.describe Payload::ARMRequest do
7
+ describe "#group_by" do
8
+ let(:instance) { described_class.new(Payload::Invoice, nil) }
9
+
10
+ it "appends to group_by and returns self" do
11
+ year_attr = Payload::Attr.new("year", Payload::Attr.new("created_at"))
12
+ year_attr.call
13
+ result = instance.group_by(year_attr, Payload::Attr.status)
14
+ expect(result).to be(instance)
15
+ expect(instance.instance_variable_get(:@group_by).map(&:to_s)).to eq(["year(created_at)", "status"])
16
+ end
17
+
18
+ it "includes group_by in request params when all() is called" do
19
+ Payload::api_key = "test_key"
20
+ instance.instance_variable_set(:@cls, Payload::Invoice)
21
+
22
+ expect(instance).to receive(:_execute_request) do |_http, request|
23
+ query = request.path.split("?")[1]
24
+ expect(query).to include("group_by%5B0%5D=") # group_by[0]=
25
+ expect(query).to include("group_by%5B1%5D=") # group_by[1]=
26
+ QuerySpecMockResponse.new('{"object":"list","values":[]}')
27
+ end
28
+
29
+ instance.group_by("year(created_at)", "status").all()
30
+ end
31
+ end
32
+
33
+ describe "#order_by" do
34
+ let(:instance) { described_class.new(Payload::Account, nil) }
35
+
36
+ it "appends to order_by and returns self" do
37
+ result = instance.order_by("created_at", "desc(id)")
38
+ expect(result).to be(instance)
39
+ expect(instance.instance_variable_get(:@order_by)).to eq(["created_at", "desc(id)"])
40
+ end
41
+
42
+ it "includes order_by in request params when all() is called" do
43
+ Payload::api_key = "test_key"
44
+ instance.instance_variable_set(:@cls, Payload::Account)
45
+
46
+ expect(instance).to receive(:_execute_request) do |_http, request|
47
+ query = request.path.split("?")[1]
48
+ expect(query).to include("order_by%5B0%5D=created_at")
49
+ expect(query).to include("order_by%5B1%5D=desc%28id%29")
50
+ QuerySpecMockResponse.new('{"object":"list","values":[]}')
51
+ end
52
+
53
+ instance.order_by("created_at", "desc(id)").all()
54
+ end
55
+ end
56
+
57
+ describe "#limit and #offset" do
58
+ let(:instance) { described_class.new(Payload::Account, nil) }
59
+
60
+ it "sets limit and offset and returns self" do
61
+ result = instance.limit(10).offset(20)
62
+ expect(result).to be(instance)
63
+ expect(instance.instance_variable_get(:@limit)).to eq(10)
64
+ expect(instance.instance_variable_get(:@offset)).to eq(20)
65
+ end
66
+
67
+ it "includes limit and offset in request params when all() is called" do
68
+ Payload::api_key = "test_key"
69
+ instance.instance_variable_set(:@cls, Payload::Account)
70
+
71
+ expect(instance).to receive(:_execute_request) do |_http, request|
72
+ query = request.path.split("?")[1]
73
+ expect(query).to include("limit=10")
74
+ expect(query).to include("offset=20")
75
+ QuerySpecMockResponse.new('{"object":"list","values":[]}')
76
+ end
77
+
78
+ instance.limit(10).offset(20).all()
79
+ end
80
+ end
81
+
82
+ describe "#request_params" do
83
+ it "merges filters, filter_objects, group_by, order_by, limit, offset into query params" do
84
+ instance = described_class.new(Payload::Transaction, nil)
85
+ instance.instance_variable_set(:@filters, { "fields" => "id,amount" })
86
+ instance.instance_variable_set(:@group_by, ["status"])
87
+ instance.instance_variable_set(:@order_by, ["desc(created_at)"])
88
+ instance.instance_variable_set(:@limit, 5)
89
+ instance.instance_variable_set(:@offset, 10)
90
+
91
+ filter_obj = Payload::ARMGreaterThan.new(Payload::Attr.amount, 100)
92
+ instance.instance_variable_set(:@filter_objects, [filter_obj])
93
+
94
+ params = instance.request_params
95
+
96
+ expect(params["fields"]).to eq("id,amount")
97
+ expect(params["group_by[0]"]).to eq("status")
98
+ expect(params["order_by[0]"]).to eq("desc(created_at)")
99
+ expect(params["limit"]).to eq("5")
100
+ expect(params["offset"]).to eq("10")
101
+ expect(params[filter_obj.attr]).to eq(filter_obj.opval)
102
+ end
103
+
104
+ it "is encoded as URL query string in _request (url.query = URI.encode_www_form(params))" do
105
+ Payload::api_key = "test_key"
106
+ session = Payload::Session.new("test_key", "https://api.test.com", "v2")
107
+ instance = described_class.new(Payload::Invoice, session)
108
+ instance.select("id", "status").filter_by(session.attr.status == "open").order_by("created_at").limit(5).offset(1)
109
+
110
+ expected_params = instance.request_params.dup
111
+
112
+ expect(instance).to receive(:_execute_request) do |_http, request|
113
+ query_str = request.path.split("?", 2)[1]
114
+ expect(query_str).not_to be_nil
115
+ decoded = URI.decode_www_form(query_str || "").to_h
116
+ expected_params.each do |key, value|
117
+ expect(decoded[key]).to eq(value.to_s)
118
+ end
119
+ expect(decoded).to include("fields" => "id,status", "limit" => "5", "offset" => "1")
120
+ expect(decoded.keys).to include("status", "order_by[0]")
121
+ QuerySpecMockResponse.new('{"object":"list","values":[]}')
122
+ end
123
+
124
+ instance.all()
125
+ end
126
+ end
127
+
128
+ describe "#[] (slice)" do
129
+ let(:instance) { described_class.new(Payload::Account, nil) }
130
+
131
+ it "raises TypeError for non-Range key" do
132
+ expect { instance["foo"] }.to raise_error(TypeError, /invalid key or index/)
133
+ expect { instance[5] }.to raise_error(TypeError, /invalid key or index/)
134
+ end
135
+
136
+ it "raises ArgumentError for negative begin" do
137
+ expect { instance[-1..10] }.to raise_error(ArgumentError, /Negative slice indices not supported/)
138
+ end
139
+
140
+ it "raises ArgumentError for negative end" do
141
+ expect { instance[0..-5] }.to raise_error(ArgumentError, /Negative slice indices not supported/)
142
+ end
143
+
144
+ it "calls offset(begin).limit(size).all() for a range and returns result" do
145
+ Payload::api_key = "test_key"
146
+ instance.instance_variable_set(:@cls, Payload::Account)
147
+
148
+ expect(instance).to receive(:_execute_request) do |_http, request|
149
+ query = request.path.split("?")[1]
150
+ expect(query).to include("offset=10")
151
+ expect(query).to include("limit=10")
152
+ QuerySpecMockResponse.new('{"object":"list","values":[{"id":"acct_1","object":"customer"}]}')
153
+ end
154
+
155
+ result = instance[10..19]
156
+ expect(result).to be_an(Array)
157
+ expect(result.size).to eq(1)
158
+ expect(result[0].id).to eq("acct_1")
159
+ end
160
+ end
161
+
162
+ describe "#filter_by with pl.attr filter objects" do
163
+ it "extracts ARM filter objects and merges their attr/opval into request params" do
164
+ session = Payload::Session.new("test_key", "https://api.test.com", "v2")
165
+ instance = described_class.new(Payload::Transaction, session)
166
+ pl = session
167
+ filter_expr = (pl.attr.amount > 100) | (pl.attr.amount < 200)
168
+
169
+ instance.filter_by(filter_expr)
170
+
171
+ expect(instance.instance_variable_get(:@filter_objects)).to include(be_a(Payload::ARMFilter))
172
+ params = instance.request_params
173
+ expect(params.keys).to include(filter_expr.attr)
174
+ expect(params[filter_expr.attr]).to eq(filter_expr.opval)
175
+ end
176
+
177
+ it "builds GET request with filter object serialized in query string" do
178
+ Payload::api_key = "test_key"
179
+ session = Payload::Session.new("test_key", "https://api.test.com", "v2")
180
+ instance = described_class.new(Payload::Transaction, session)
181
+ filter_expr = session.attr.amount > 100
182
+
183
+ instance.filter_by(filter_expr)
184
+
185
+ expect(instance).to receive(:_execute_request) do |_http, request|
186
+ query = request.path.split("?")[1]
187
+ expect(query).to include("amount=")
188
+ expect(query).to include("%3E100") # URL-encoded ">100"
189
+ QuerySpecMockResponse.new('{"object":"list","values":[]}')
190
+ end
191
+
192
+ instance.all()
193
+ end
194
+ end
195
+
196
+ describe "#filter_by with multiple filters" do
197
+ it "accumulates multiple filter objects and merges keyword filters into @filters" do
198
+ instance = described_class.new(Payload::Invoice, nil)
199
+ f1 = Payload::ARMEqual.new(Payload::Attr.status, "open")
200
+ f2 = Payload::ARMGreaterThan.new(Payload::Attr.amount, 50)
201
+
202
+ instance.filter_by(f1).filter_by(f2).filter_by(custom_key: "value")
203
+
204
+ fo = instance.instance_variable_get(:@filter_objects)
205
+ expect(fo).to include(f1, f2)
206
+ params = instance.request_params
207
+ expect(params["status"]).to eq("open")
208
+ expect(params["amount"]).to eq(">50")
209
+ expect(instance.instance_variable_get(:@filters)[:custom_key]).to eq("value")
210
+ end
211
+ end
212
+ end
213
+
214
+ class QuerySpecMockResponse
215
+ def initialize(body = '{"object":"list","values":[]}')
216
+ @body = body
217
+ end
218
+
219
+ def code
220
+ "200"
221
+ end
222
+
223
+ def body
224
+ @body
225
+ end
226
+ end