gocardless_pro 1.1.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +13 -4
  3. data/lib/gocardless_pro.rb +1 -0
  4. data/lib/gocardless_pro/api_service.rb +2 -0
  5. data/lib/gocardless_pro/client.rb +4 -3
  6. data/lib/gocardless_pro/error/invalid_state_error.rb +17 -0
  7. data/lib/gocardless_pro/middlewares/raise_gocardless_errors.rb +50 -0
  8. data/lib/gocardless_pro/request.rb +38 -1
  9. data/lib/gocardless_pro/resources/creditor.rb +2 -2
  10. data/lib/gocardless_pro/resources/creditor_bank_account.rb +11 -11
  11. data/lib/gocardless_pro/resources/customer_bank_account.rb +2 -2
  12. data/lib/gocardless_pro/resources/event.rb +2 -2
  13. data/lib/gocardless_pro/resources/mandate.rb +2 -2
  14. data/lib/gocardless_pro/resources/payment.rb +7 -7
  15. data/lib/gocardless_pro/resources/payout.rb +7 -6
  16. data/lib/gocardless_pro/resources/redirect_flow.rb +2 -2
  17. data/lib/gocardless_pro/resources/refund.rb +2 -2
  18. data/lib/gocardless_pro/resources/subscription.rb +2 -2
  19. data/lib/gocardless_pro/response.rb +2 -54
  20. data/lib/gocardless_pro/services/bank_details_lookups_service.rb +3 -0
  21. data/lib/gocardless_pro/services/creditor_bank_accounts_service.rb +31 -2
  22. data/lib/gocardless_pro/services/creditors_service.rb +21 -1
  23. data/lib/gocardless_pro/services/customer_bank_accounts_service.rb +34 -2
  24. data/lib/gocardless_pro/services/customers_service.rb +21 -1
  25. data/lib/gocardless_pro/services/events_service.rb +5 -0
  26. data/lib/gocardless_pro/services/mandate_pdfs_service.rb +3 -0
  27. data/lib/gocardless_pro/services/mandates_service.rb +47 -3
  28. data/lib/gocardless_pro/services/payments_service.rb +47 -3
  29. data/lib/gocardless_pro/services/payouts_service.rb +5 -0
  30. data/lib/gocardless_pro/services/redirect_flows_service.rb +28 -2
  31. data/lib/gocardless_pro/services/refunds_service.rb +21 -1
  32. data/lib/gocardless_pro/services/subscriptions_service.rb +34 -2
  33. data/lib/gocardless_pro/version.rb +1 -1
  34. data/spec/api_service_spec.rb +106 -0
  35. data/spec/middlewares/raise_gocardless_errors_spec.rb +98 -0
  36. data/spec/resources/bank_details_lookup_spec.rb +102 -19
  37. data/spec/resources/creditor_bank_account_spec.rb +416 -40
  38. data/spec/resources/creditor_spec.rb +414 -53
  39. data/spec/resources/customer_bank_account_spec.rb +452 -40
  40. data/spec/resources/customer_spec.rb +457 -45
  41. data/spec/resources/event_spec.rb +171 -72
  42. data/spec/resources/mandate_pdf_spec.rb +100 -17
  43. data/spec/resources/mandate_spec.rb +501 -44
  44. data/spec/resources/payment_spec.rb +531 -48
  45. data/spec/resources/payout_spec.rb +189 -45
  46. data/spec/resources/redirect_flow_spec.rb +277 -43
  47. data/spec/resources/refund_spec.rb +349 -34
  48. data/spec/resources/subscription_spec.rb +531 -53
  49. data/spec/response_spec.rb +12 -79
  50. data/spec/services/bank_details_lookups_service_spec.rb +67 -2
  51. data/spec/services/creditor_bank_accounts_service_spec.rb +309 -31
  52. data/spec/services/creditors_service_spec.rb +343 -33
  53. data/spec/services/customer_bank_accounts_service_spec.rb +335 -32
  54. data/spec/services/customers_service_spec.rb +364 -36
  55. data/spec/services/events_service_spec.rb +185 -24
  56. data/spec/services/mandate_pdfs_service_spec.rb +66 -2
  57. data/spec/services/mandates_service_spec.rb +341 -33
  58. data/spec/services/payments_service_spec.rb +355 -35
  59. data/spec/services/payouts_service_spec.rb +206 -26
  60. data/spec/services/redirect_flows_service_spec.rb +137 -7
  61. data/spec/services/refunds_service_spec.rb +301 -27
  62. data/spec/services/subscriptions_service_spec.rb +377 -38
  63. metadata +6 -3
@@ -1,67 +1,382 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe GoCardlessPro::Resources::Refund do
4
- describe 'initialising' do
5
- let(:data) do
6
- {
4
+ let(:client) do
5
+ GoCardlessPro::Client.new(
6
+ access_token: 'SECRET_TOKEN'
7
+ )
8
+ end
9
+
10
+ let(:response_headers) { { 'Content-Type' => 'application/json' } }
11
+
12
+ describe '#create' do
13
+ subject(:post_create_response) { client.refunds.create(params: new_resource) }
14
+ context 'with a valid request' do
15
+ let(:new_resource) do
16
+ {
17
+
18
+ 'amount' => 'amount-input',
19
+ 'created_at' => 'created_at-input',
20
+ 'currency' => 'currency-input',
21
+ 'id' => 'id-input',
22
+ 'links' => 'links-input',
23
+ 'metadata' => 'metadata-input',
24
+ 'reference' => 'reference-input'
25
+ }
26
+ end
27
+
28
+ before do
29
+ stub_request(:post, %r{.*api.gocardless.com/refunds})
30
+ .with(
31
+ body: {
32
+ 'refunds' => {
33
+
34
+ 'amount' => 'amount-input',
35
+ 'created_at' => 'created_at-input',
36
+ 'currency' => 'currency-input',
37
+ 'id' => 'id-input',
38
+ 'links' => 'links-input',
39
+ 'metadata' => 'metadata-input',
40
+ 'reference' => 'reference-input'
41
+ }
42
+ }
43
+ )
44
+ .to_return(
45
+ body: {
46
+ 'refunds' =>
47
+
48
+ {
49
+
50
+ 'amount' => 'amount-input',
51
+ 'created_at' => 'created_at-input',
52
+ 'currency' => 'currency-input',
53
+ 'id' => 'id-input',
54
+ 'links' => 'links-input',
55
+ 'metadata' => 'metadata-input',
56
+ 'reference' => 'reference-input'
57
+ }
58
+
59
+ }.to_json,
60
+ headers: response_headers
61
+ )
62
+ end
63
+
64
+ it 'creates and returns the resource' do
65
+ expect(post_create_response).to be_a(GoCardlessPro::Resources::Refund)
66
+ end
67
+ end
68
+
69
+ context 'with a request that returns a validation error' do
70
+ let(:new_resource) { {} }
71
+
72
+ before do
73
+ stub_request(:post, %r{.*api.gocardless.com/refunds}).to_return(
74
+ body: {
75
+ error: {
76
+ type: 'validation_failed',
77
+ code: 422,
78
+ errors: [
79
+ { message: 'test error message', field: 'test_field' }
80
+ ]
81
+ }
82
+ }.to_json,
83
+ headers: response_headers,
84
+ status: 422
85
+ )
86
+ end
87
+
88
+ it 'throws the correct error' do
89
+ expect { post_create_response }.to raise_error(GoCardlessPro::ValidationError)
90
+ end
91
+ end
92
+
93
+ context 'with a request that returns an idempotent creation conflict error' do
94
+ let(:id) { 'ID123' }
95
+
96
+ let(:new_resource) do
97
+ {
98
+
99
+ 'amount' => 'amount-input',
100
+ 'created_at' => 'created_at-input',
101
+ 'currency' => 'currency-input',
102
+ 'id' => 'id-input',
103
+ 'links' => 'links-input',
104
+ 'metadata' => 'metadata-input',
105
+ 'reference' => 'reference-input'
106
+ }
107
+ end
108
+
109
+ let!(:post_stub) do
110
+ stub_request(:post, %r{.*api.gocardless.com/refunds}).to_return(
111
+ body: {
112
+ error: {
113
+ type: 'invalid_state',
114
+ code: 409,
115
+ errors: [
116
+ {
117
+ message: 'A resource has already been created with this idempotency key',
118
+ reason: 'idempotent_creation_conflict',
119
+ links: {
120
+ conflicting_resource_id: id
121
+ }
122
+ }
123
+ ]
124
+ }
125
+ }.to_json,
126
+ headers: response_headers,
127
+ status: 409
128
+ )
129
+ end
130
+
131
+ let!(:get_stub) do
132
+ stub_url = "/refunds/#{id}"
133
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/)
134
+ .to_return(
135
+ body: {
136
+ 'refunds' => {
137
+
138
+ 'amount' => 'amount-input',
139
+ 'created_at' => 'created_at-input',
140
+ 'currency' => 'currency-input',
141
+ 'id' => 'id-input',
142
+ 'links' => 'links-input',
143
+ 'metadata' => 'metadata-input',
144
+ 'reference' => 'reference-input'
145
+ }
146
+ }.to_json,
147
+ headers: response_headers
148
+ )
149
+ end
150
+
151
+ it 'fetches the already-created resource' do
152
+ post_create_response
153
+ expect(post_stub).to have_been_requested
154
+ expect(get_stub).to have_been_requested
155
+ end
156
+ end
157
+ end
158
+
159
+ describe '#list' do
160
+ describe 'with no filters' do
161
+ subject(:get_list_response) { client.refunds.list }
162
+
163
+ before do
164
+ stub_request(:get, %r{.*api.gocardless.com/refunds}).to_return(
165
+ body: {
166
+ 'refunds' => [{
167
+
168
+ 'amount' => 'amount-input',
169
+ 'created_at' => 'created_at-input',
170
+ 'currency' => 'currency-input',
171
+ 'id' => 'id-input',
172
+ 'links' => 'links-input',
173
+ 'metadata' => 'metadata-input',
174
+ 'reference' => 'reference-input'
175
+ }],
176
+ meta: {
177
+ cursors: {
178
+ before: nil,
179
+ after: 'ABC123'
180
+ }
181
+ }
182
+ }.to_json,
183
+ headers: response_headers
184
+ )
185
+ end
186
+
187
+ it 'wraps each item in the resource class' do
188
+ expect(get_list_response.records.map(&:class).uniq.first).to eq(GoCardlessPro::Resources::Refund)
7
189
 
8
- 'amount' => 'amount-input',
190
+ expect(get_list_response.records.first.amount).to eq('amount-input')
9
191
 
10
- 'created_at' => 'created_at-input',
192
+ expect(get_list_response.records.first.created_at).to eq('created_at-input')
11
193
 
12
- 'currency' => 'currency-input',
194
+ expect(get_list_response.records.first.currency).to eq('currency-input')
13
195
 
14
- 'id' => 'id-input',
196
+ expect(get_list_response.records.first.id).to eq('id-input')
15
197
 
16
- 'links' => {
198
+ expect(get_list_response.records.first.metadata).to eq('metadata-input')
17
199
 
18
- 'payment' => 'payment-input'
200
+ expect(get_list_response.records.first.reference).to eq('reference-input')
201
+ end
19
202
 
20
- },
203
+ it 'exposes the cursors for before and after' do
204
+ expect(get_list_response.before).to eq(nil)
205
+ expect(get_list_response.after).to eq('ABC123')
206
+ end
21
207
 
22
- 'metadata' => 'metadata-input',
208
+ specify { expect(get_list_response.api_response.headers).to eql('content-type' => 'application/json') }
209
+ end
210
+ end
23
211
 
24
- 'reference' => 'reference-input'
212
+ describe '#all' do
213
+ let!(:first_response_stub) do
214
+ stub_request(:get, %r{.*api.gocardless.com/refunds$}).to_return(
215
+ body: {
216
+ 'refunds' => [{
25
217
 
26
- }
218
+ 'amount' => 'amount-input',
219
+ 'created_at' => 'created_at-input',
220
+ 'currency' => 'currency-input',
221
+ 'id' => 'id-input',
222
+ 'links' => 'links-input',
223
+ 'metadata' => 'metadata-input',
224
+ 'reference' => 'reference-input'
225
+ }],
226
+ meta: {
227
+ cursors: { after: 'AB345' },
228
+ limit: 1
229
+ }
230
+ }.to_json,
231
+ headers: response_headers
232
+ )
27
233
  end
28
234
 
29
- it 'can be initialized from an unenveloped response' do
30
- resource = described_class.new(data)
235
+ let!(:second_response_stub) do
236
+ stub_request(:get, %r{.*api.gocardless.com/refunds\?after=AB345}).to_return(
237
+ body: {
238
+ 'refunds' => [{
239
+
240
+ 'amount' => 'amount-input',
241
+ 'created_at' => 'created_at-input',
242
+ 'currency' => 'currency-input',
243
+ 'id' => 'id-input',
244
+ 'links' => 'links-input',
245
+ 'metadata' => 'metadata-input',
246
+ 'reference' => 'reference-input'
247
+ }],
248
+ meta: {
249
+ limit: 2,
250
+ cursors: {}
251
+ }
252
+ }.to_json,
253
+ headers: response_headers
254
+ )
255
+ end
31
256
 
32
- expect(resource.amount).to eq('amount-input')
257
+ it 'automatically makes the extra requests' do
258
+ expect(client.refunds.all.to_a.length).to eq(2)
259
+ expect(first_response_stub).to have_been_requested
260
+ expect(second_response_stub).to have_been_requested
261
+ end
262
+ end
33
263
 
34
- expect(resource.created_at).to eq('created_at-input')
264
+ describe '#get' do
265
+ let(:id) { 'ID123' }
35
266
 
36
- expect(resource.currency).to eq('currency-input')
267
+ subject(:get_response) { client.refunds.get(id) }
37
268
 
38
- expect(resource.id).to eq('id-input')
269
+ context 'passing in a custom header' do
270
+ let!(:stub) do
271
+ stub_url = '/refunds/:identity'.gsub(':identity', id)
272
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/)
273
+ .with(headers: { 'Foo' => 'Bar' })
274
+ .to_return(
275
+ body: {
276
+ 'refunds' => {
39
277
 
40
- expect(resource.links.payment).to eq('payment-input')
278
+ 'amount' => 'amount-input',
279
+ 'created_at' => 'created_at-input',
280
+ 'currency' => 'currency-input',
281
+ 'id' => 'id-input',
282
+ 'links' => 'links-input',
283
+ 'metadata' => 'metadata-input',
284
+ 'reference' => 'reference-input'
285
+ }
286
+ }.to_json,
287
+ headers: response_headers
288
+ )
289
+ end
41
290
 
42
- expect(resource.metadata).to eq('metadata-input')
291
+ subject(:get_response) do
292
+ client.refunds.get(id, headers: {
293
+ 'Foo' => 'Bar'
294
+ })
295
+ end
43
296
 
44
- expect(resource.reference).to eq('reference-input')
297
+ it 'includes the header' do
298
+ get_response
299
+ expect(stub).to have_been_requested
300
+ end
45
301
  end
46
302
 
47
- it 'can handle new attributes without erroring' do
48
- data['foo'] = 'bar'
49
- expect { described_class.new(data) }.to_not raise_error
303
+ context 'when there is a refund to return' do
304
+ before do
305
+ stub_url = '/refunds/:identity'.gsub(':identity', id)
306
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
307
+ body: {
308
+ 'refunds' => {
309
+
310
+ 'amount' => 'amount-input',
311
+ 'created_at' => 'created_at-input',
312
+ 'currency' => 'currency-input',
313
+ 'id' => 'id-input',
314
+ 'links' => 'links-input',
315
+ 'metadata' => 'metadata-input',
316
+ 'reference' => 'reference-input'
317
+ }
318
+ }.to_json,
319
+ headers: response_headers
320
+ )
321
+ end
322
+
323
+ it 'wraps the response in a resource' do
324
+ expect(get_response).to be_a(GoCardlessPro::Resources::Refund)
325
+ end
50
326
  end
51
327
 
52
- it 'can handle new link attributes without erroring' do
53
- data['links']['foo'] = 'bar'
54
- expect { described_class.new(data) }.to_not raise_error
328
+ context 'when nothing is returned' do
329
+ before do
330
+ stub_url = '/refunds/:identity'.gsub(':identity', id)
331
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
332
+ body: '',
333
+ headers: response_headers
334
+ )
335
+ end
336
+
337
+ it 'returns nil' do
338
+ expect(get_response).to be_nil
339
+ end
55
340
  end
56
341
 
57
- it 'can handle a nil links value' do
58
- data['links'] = nil
59
- expect { described_class.new(data).links }.to_not raise_error
342
+ context "when an ID is specified which can't be included in a valid URI" do
343
+ let(:id) { '`' }
344
+
345
+ it "doesn't raise an error" do
346
+ expect { get_response }.to_not raise_error(/bad URI/)
347
+ end
60
348
  end
349
+ end
350
+
351
+ describe '#update' do
352
+ subject(:put_update_response) { client.refunds.update(id, params: update_params) }
353
+ let(:id) { 'ABC123' }
354
+
355
+ context 'with a valid request' do
356
+ let(:update_params) { { 'hello' => 'world' } }
357
+
358
+ let!(:stub) do
359
+ stub_url = '/refunds/:identity'.gsub(':identity', id)
360
+ stub_request(:put, /.*api.gocardless.com#{stub_url}/).to_return(
361
+ body: {
362
+ 'refunds' => {
363
+
364
+ 'amount' => 'amount-input',
365
+ 'created_at' => 'created_at-input',
366
+ 'currency' => 'currency-input',
367
+ 'id' => 'id-input',
368
+ 'links' => 'links-input',
369
+ 'metadata' => 'metadata-input',
370
+ 'reference' => 'reference-input'
371
+ }
372
+ }.to_json,
373
+ headers: response_headers
374
+ )
375
+ end
61
376
 
62
- describe '#to_h' do
63
- it 'returns a hash representing the resource' do
64
- expect(described_class.new(data).to_h).to eq(data)
377
+ it 'updates and returns the resource' do
378
+ expect(put_update_response).to be_a(GoCardlessPro::Resources::Refund)
379
+ expect(stub).to have_been_requested
65
380
  end
66
381
  end
67
382
  end
@@ -1,103 +1,581 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe GoCardlessPro::Resources::Subscription do
4
- describe 'initialising' do
5
- let(:data) do
6
- {
4
+ let(:client) do
5
+ GoCardlessPro::Client.new(
6
+ access_token: 'SECRET_TOKEN'
7
+ )
8
+ end
7
9
 
8
- 'amount' => 'amount-input',
10
+ let(:response_headers) { { 'Content-Type' => 'application/json' } }
11
+
12
+ describe '#create' do
13
+ subject(:post_create_response) { client.subscriptions.create(params: new_resource) }
14
+ context 'with a valid request' do
15
+ let(:new_resource) do
16
+ {
17
+
18
+ 'amount' => 'amount-input',
19
+ 'created_at' => 'created_at-input',
20
+ 'currency' => 'currency-input',
21
+ 'day_of_month' => 'day_of_month-input',
22
+ 'end_date' => 'end_date-input',
23
+ 'id' => 'id-input',
24
+ 'interval' => 'interval-input',
25
+ 'interval_unit' => 'interval_unit-input',
26
+ 'links' => 'links-input',
27
+ 'metadata' => 'metadata-input',
28
+ 'month' => 'month-input',
29
+ 'name' => 'name-input',
30
+ 'payment_reference' => 'payment_reference-input',
31
+ 'start_date' => 'start_date-input',
32
+ 'status' => 'status-input',
33
+ 'upcoming_payments' => 'upcoming_payments-input'
34
+ }
35
+ end
9
36
 
10
- 'created_at' => 'created_at-input',
37
+ before do
38
+ stub_request(:post, %r{.*api.gocardless.com/subscriptions})
39
+ .with(
40
+ body: {
41
+ 'subscriptions' => {
42
+
43
+ 'amount' => 'amount-input',
44
+ 'created_at' => 'created_at-input',
45
+ 'currency' => 'currency-input',
46
+ 'day_of_month' => 'day_of_month-input',
47
+ 'end_date' => 'end_date-input',
48
+ 'id' => 'id-input',
49
+ 'interval' => 'interval-input',
50
+ 'interval_unit' => 'interval_unit-input',
51
+ 'links' => 'links-input',
52
+ 'metadata' => 'metadata-input',
53
+ 'month' => 'month-input',
54
+ 'name' => 'name-input',
55
+ 'payment_reference' => 'payment_reference-input',
56
+ 'start_date' => 'start_date-input',
57
+ 'status' => 'status-input',
58
+ 'upcoming_payments' => 'upcoming_payments-input'
59
+ }
60
+ }
61
+ )
62
+ .to_return(
63
+ body: {
64
+ 'subscriptions' =>
65
+
66
+ {
67
+
68
+ 'amount' => 'amount-input',
69
+ 'created_at' => 'created_at-input',
70
+ 'currency' => 'currency-input',
71
+ 'day_of_month' => 'day_of_month-input',
72
+ 'end_date' => 'end_date-input',
73
+ 'id' => 'id-input',
74
+ 'interval' => 'interval-input',
75
+ 'interval_unit' => 'interval_unit-input',
76
+ 'links' => 'links-input',
77
+ 'metadata' => 'metadata-input',
78
+ 'month' => 'month-input',
79
+ 'name' => 'name-input',
80
+ 'payment_reference' => 'payment_reference-input',
81
+ 'start_date' => 'start_date-input',
82
+ 'status' => 'status-input',
83
+ 'upcoming_payments' => 'upcoming_payments-input'
84
+ }
85
+
86
+ }.to_json,
87
+ headers: response_headers
88
+ )
89
+ end
11
90
 
12
- 'currency' => 'currency-input',
91
+ it 'creates and returns the resource' do
92
+ expect(post_create_response).to be_a(GoCardlessPro::Resources::Subscription)
93
+ end
94
+ end
13
95
 
14
- 'day_of_month' => 'day_of_month-input',
96
+ context 'with a request that returns a validation error' do
97
+ let(:new_resource) { {} }
98
+
99
+ before do
100
+ stub_request(:post, %r{.*api.gocardless.com/subscriptions}).to_return(
101
+ body: {
102
+ error: {
103
+ type: 'validation_failed',
104
+ code: 422,
105
+ errors: [
106
+ { message: 'test error message', field: 'test_field' }
107
+ ]
108
+ }
109
+ }.to_json,
110
+ headers: response_headers,
111
+ status: 422
112
+ )
113
+ end
15
114
 
16
- 'end_date' => 'end_date-input',
115
+ it 'throws the correct error' do
116
+ expect { post_create_response }.to raise_error(GoCardlessPro::ValidationError)
117
+ end
118
+ end
17
119
 
18
- 'id' => 'id-input',
120
+ context 'with a request that returns an idempotent creation conflict error' do
121
+ let(:id) { 'ID123' }
122
+
123
+ let(:new_resource) do
124
+ {
125
+
126
+ 'amount' => 'amount-input',
127
+ 'created_at' => 'created_at-input',
128
+ 'currency' => 'currency-input',
129
+ 'day_of_month' => 'day_of_month-input',
130
+ 'end_date' => 'end_date-input',
131
+ 'id' => 'id-input',
132
+ 'interval' => 'interval-input',
133
+ 'interval_unit' => 'interval_unit-input',
134
+ 'links' => 'links-input',
135
+ 'metadata' => 'metadata-input',
136
+ 'month' => 'month-input',
137
+ 'name' => 'name-input',
138
+ 'payment_reference' => 'payment_reference-input',
139
+ 'start_date' => 'start_date-input',
140
+ 'status' => 'status-input',
141
+ 'upcoming_payments' => 'upcoming_payments-input'
142
+ }
143
+ end
19
144
 
20
- 'interval' => 'interval-input',
145
+ let!(:post_stub) do
146
+ stub_request(:post, %r{.*api.gocardless.com/subscriptions}).to_return(
147
+ body: {
148
+ error: {
149
+ type: 'invalid_state',
150
+ code: 409,
151
+ errors: [
152
+ {
153
+ message: 'A resource has already been created with this idempotency key',
154
+ reason: 'idempotent_creation_conflict',
155
+ links: {
156
+ conflicting_resource_id: id
157
+ }
158
+ }
159
+ ]
160
+ }
161
+ }.to_json,
162
+ headers: response_headers,
163
+ status: 409
164
+ )
165
+ end
21
166
 
22
- 'interval_unit' => 'interval_unit-input',
167
+ let!(:get_stub) do
168
+ stub_url = "/subscriptions/#{id}"
169
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/)
170
+ .to_return(
171
+ body: {
172
+ 'subscriptions' => {
173
+
174
+ 'amount' => 'amount-input',
175
+ 'created_at' => 'created_at-input',
176
+ 'currency' => 'currency-input',
177
+ 'day_of_month' => 'day_of_month-input',
178
+ 'end_date' => 'end_date-input',
179
+ 'id' => 'id-input',
180
+ 'interval' => 'interval-input',
181
+ 'interval_unit' => 'interval_unit-input',
182
+ 'links' => 'links-input',
183
+ 'metadata' => 'metadata-input',
184
+ 'month' => 'month-input',
185
+ 'name' => 'name-input',
186
+ 'payment_reference' => 'payment_reference-input',
187
+ 'start_date' => 'start_date-input',
188
+ 'status' => 'status-input',
189
+ 'upcoming_payments' => 'upcoming_payments-input'
190
+ }
191
+ }.to_json,
192
+ headers: response_headers
193
+ )
194
+ end
23
195
 
24
- 'links' => {
196
+ it 'fetches the already-created resource' do
197
+ post_create_response
198
+ expect(post_stub).to have_been_requested
199
+ expect(get_stub).to have_been_requested
200
+ end
201
+ end
202
+ end
25
203
 
26
- 'mandate' => 'mandate-input'
204
+ describe '#list' do
205
+ describe 'with no filters' do
206
+ subject(:get_list_response) { client.subscriptions.list }
207
+
208
+ before do
209
+ stub_request(:get, %r{.*api.gocardless.com/subscriptions}).to_return(
210
+ body: {
211
+ 'subscriptions' => [{
212
+
213
+ 'amount' => 'amount-input',
214
+ 'created_at' => 'created_at-input',
215
+ 'currency' => 'currency-input',
216
+ 'day_of_month' => 'day_of_month-input',
217
+ 'end_date' => 'end_date-input',
218
+ 'id' => 'id-input',
219
+ 'interval' => 'interval-input',
220
+ 'interval_unit' => 'interval_unit-input',
221
+ 'links' => 'links-input',
222
+ 'metadata' => 'metadata-input',
223
+ 'month' => 'month-input',
224
+ 'name' => 'name-input',
225
+ 'payment_reference' => 'payment_reference-input',
226
+ 'start_date' => 'start_date-input',
227
+ 'status' => 'status-input',
228
+ 'upcoming_payments' => 'upcoming_payments-input'
229
+ }],
230
+ meta: {
231
+ cursors: {
232
+ before: nil,
233
+ after: 'ABC123'
234
+ }
235
+ }
236
+ }.to_json,
237
+ headers: response_headers
238
+ )
239
+ end
27
240
 
28
- },
241
+ it 'wraps each item in the resource class' do
242
+ expect(get_list_response.records.map(&:class).uniq.first).to eq(GoCardlessPro::Resources::Subscription)
29
243
 
30
- 'metadata' => 'metadata-input',
244
+ expect(get_list_response.records.first.amount).to eq('amount-input')
31
245
 
32
- 'month' => 'month-input',
246
+ expect(get_list_response.records.first.created_at).to eq('created_at-input')
33
247
 
34
- 'name' => 'name-input',
248
+ expect(get_list_response.records.first.currency).to eq('currency-input')
35
249
 
36
- 'payment_reference' => 'payment_reference-input',
250
+ expect(get_list_response.records.first.day_of_month).to eq('day_of_month-input')
37
251
 
38
- 'start_date' => 'start_date-input',
252
+ expect(get_list_response.records.first.end_date).to eq('end_date-input')
39
253
 
40
- 'status' => 'status-input',
254
+ expect(get_list_response.records.first.id).to eq('id-input')
41
255
 
42
- 'upcoming_payments' => 'upcoming_payments-input'
256
+ expect(get_list_response.records.first.interval).to eq('interval-input')
43
257
 
44
- }
45
- end
258
+ expect(get_list_response.records.first.interval_unit).to eq('interval_unit-input')
46
259
 
47
- it 'can be initialized from an unenveloped response' do
48
- resource = described_class.new(data)
260
+ expect(get_list_response.records.first.metadata).to eq('metadata-input')
49
261
 
50
- expect(resource.amount).to eq('amount-input')
262
+ expect(get_list_response.records.first.month).to eq('month-input')
51
263
 
52
- expect(resource.created_at).to eq('created_at-input')
264
+ expect(get_list_response.records.first.name).to eq('name-input')
53
265
 
54
- expect(resource.currency).to eq('currency-input')
266
+ expect(get_list_response.records.first.payment_reference).to eq('payment_reference-input')
55
267
 
56
- expect(resource.day_of_month).to eq('day_of_month-input')
268
+ expect(get_list_response.records.first.start_date).to eq('start_date-input')
57
269
 
58
- expect(resource.end_date).to eq('end_date-input')
270
+ expect(get_list_response.records.first.status).to eq('status-input')
59
271
 
60
- expect(resource.id).to eq('id-input')
272
+ expect(get_list_response.records.first.upcoming_payments).to eq('upcoming_payments-input')
273
+ end
61
274
 
62
- expect(resource.interval).to eq('interval-input')
275
+ it 'exposes the cursors for before and after' do
276
+ expect(get_list_response.before).to eq(nil)
277
+ expect(get_list_response.after).to eq('ABC123')
278
+ end
63
279
 
64
- expect(resource.interval_unit).to eq('interval_unit-input')
280
+ specify { expect(get_list_response.api_response.headers).to eql('content-type' => 'application/json') }
281
+ end
282
+ end
65
283
 
66
- expect(resource.links.mandate).to eq('mandate-input')
284
+ describe '#all' do
285
+ let!(:first_response_stub) do
286
+ stub_request(:get, %r{.*api.gocardless.com/subscriptions$}).to_return(
287
+ body: {
288
+ 'subscriptions' => [{
289
+
290
+ 'amount' => 'amount-input',
291
+ 'created_at' => 'created_at-input',
292
+ 'currency' => 'currency-input',
293
+ 'day_of_month' => 'day_of_month-input',
294
+ 'end_date' => 'end_date-input',
295
+ 'id' => 'id-input',
296
+ 'interval' => 'interval-input',
297
+ 'interval_unit' => 'interval_unit-input',
298
+ 'links' => 'links-input',
299
+ 'metadata' => 'metadata-input',
300
+ 'month' => 'month-input',
301
+ 'name' => 'name-input',
302
+ 'payment_reference' => 'payment_reference-input',
303
+ 'start_date' => 'start_date-input',
304
+ 'status' => 'status-input',
305
+ 'upcoming_payments' => 'upcoming_payments-input'
306
+ }],
307
+ meta: {
308
+ cursors: { after: 'AB345' },
309
+ limit: 1
310
+ }
311
+ }.to_json,
312
+ headers: response_headers
313
+ )
314
+ end
67
315
 
68
- expect(resource.metadata).to eq('metadata-input')
316
+ let!(:second_response_stub) do
317
+ stub_request(:get, %r{.*api.gocardless.com/subscriptions\?after=AB345}).to_return(
318
+ body: {
319
+ 'subscriptions' => [{
320
+
321
+ 'amount' => 'amount-input',
322
+ 'created_at' => 'created_at-input',
323
+ 'currency' => 'currency-input',
324
+ 'day_of_month' => 'day_of_month-input',
325
+ 'end_date' => 'end_date-input',
326
+ 'id' => 'id-input',
327
+ 'interval' => 'interval-input',
328
+ 'interval_unit' => 'interval_unit-input',
329
+ 'links' => 'links-input',
330
+ 'metadata' => 'metadata-input',
331
+ 'month' => 'month-input',
332
+ 'name' => 'name-input',
333
+ 'payment_reference' => 'payment_reference-input',
334
+ 'start_date' => 'start_date-input',
335
+ 'status' => 'status-input',
336
+ 'upcoming_payments' => 'upcoming_payments-input'
337
+ }],
338
+ meta: {
339
+ limit: 2,
340
+ cursors: {}
341
+ }
342
+ }.to_json,
343
+ headers: response_headers
344
+ )
345
+ end
69
346
 
70
- expect(resource.month).to eq('month-input')
347
+ it 'automatically makes the extra requests' do
348
+ expect(client.subscriptions.all.to_a.length).to eq(2)
349
+ expect(first_response_stub).to have_been_requested
350
+ expect(second_response_stub).to have_been_requested
351
+ end
352
+ end
353
+
354
+ describe '#get' do
355
+ let(:id) { 'ID123' }
356
+
357
+ subject(:get_response) { client.subscriptions.get(id) }
358
+
359
+ context 'passing in a custom header' do
360
+ let!(:stub) do
361
+ stub_url = '/subscriptions/:identity'.gsub(':identity', id)
362
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/)
363
+ .with(headers: { 'Foo' => 'Bar' })
364
+ .to_return(
365
+ body: {
366
+ 'subscriptions' => {
367
+
368
+ 'amount' => 'amount-input',
369
+ 'created_at' => 'created_at-input',
370
+ 'currency' => 'currency-input',
371
+ 'day_of_month' => 'day_of_month-input',
372
+ 'end_date' => 'end_date-input',
373
+ 'id' => 'id-input',
374
+ 'interval' => 'interval-input',
375
+ 'interval_unit' => 'interval_unit-input',
376
+ 'links' => 'links-input',
377
+ 'metadata' => 'metadata-input',
378
+ 'month' => 'month-input',
379
+ 'name' => 'name-input',
380
+ 'payment_reference' => 'payment_reference-input',
381
+ 'start_date' => 'start_date-input',
382
+ 'status' => 'status-input',
383
+ 'upcoming_payments' => 'upcoming_payments-input'
384
+ }
385
+ }.to_json,
386
+ headers: response_headers
387
+ )
388
+ end
71
389
 
72
- expect(resource.name).to eq('name-input')
390
+ subject(:get_response) do
391
+ client.subscriptions.get(id, headers: {
392
+ 'Foo' => 'Bar'
393
+ })
394
+ end
395
+
396
+ it 'includes the header' do
397
+ get_response
398
+ expect(stub).to have_been_requested
399
+ end
400
+ end
401
+
402
+ context 'when there is a subscription to return' do
403
+ before do
404
+ stub_url = '/subscriptions/:identity'.gsub(':identity', id)
405
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
406
+ body: {
407
+ 'subscriptions' => {
408
+
409
+ 'amount' => 'amount-input',
410
+ 'created_at' => 'created_at-input',
411
+ 'currency' => 'currency-input',
412
+ 'day_of_month' => 'day_of_month-input',
413
+ 'end_date' => 'end_date-input',
414
+ 'id' => 'id-input',
415
+ 'interval' => 'interval-input',
416
+ 'interval_unit' => 'interval_unit-input',
417
+ 'links' => 'links-input',
418
+ 'metadata' => 'metadata-input',
419
+ 'month' => 'month-input',
420
+ 'name' => 'name-input',
421
+ 'payment_reference' => 'payment_reference-input',
422
+ 'start_date' => 'start_date-input',
423
+ 'status' => 'status-input',
424
+ 'upcoming_payments' => 'upcoming_payments-input'
425
+ }
426
+ }.to_json,
427
+ headers: response_headers
428
+ )
429
+ end
430
+
431
+ it 'wraps the response in a resource' do
432
+ expect(get_response).to be_a(GoCardlessPro::Resources::Subscription)
433
+ end
434
+ end
73
435
 
74
- expect(resource.payment_reference).to eq('payment_reference-input')
436
+ context 'when nothing is returned' do
437
+ before do
438
+ stub_url = '/subscriptions/:identity'.gsub(':identity', id)
439
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
440
+ body: '',
441
+ headers: response_headers
442
+ )
443
+ end
75
444
 
76
- expect(resource.start_date).to eq('start_date-input')
445
+ it 'returns nil' do
446
+ expect(get_response).to be_nil
447
+ end
448
+ end
77
449
 
78
- expect(resource.status).to eq('status-input')
450
+ context "when an ID is specified which can't be included in a valid URI" do
451
+ let(:id) { '`' }
79
452
 
80
- expect(resource.upcoming_payments).to eq('upcoming_payments-input')
453
+ it "doesn't raise an error" do
454
+ expect { get_response }.to_not raise_error(/bad URI/)
455
+ end
81
456
  end
457
+ end
82
458
 
83
- it 'can handle new attributes without erroring' do
84
- data['foo'] = 'bar'
85
- expect { described_class.new(data) }.to_not raise_error
459
+ describe '#update' do
460
+ subject(:put_update_response) { client.subscriptions.update(id, params: update_params) }
461
+ let(:id) { 'ABC123' }
462
+
463
+ context 'with a valid request' do
464
+ let(:update_params) { { 'hello' => 'world' } }
465
+
466
+ let!(:stub) do
467
+ stub_url = '/subscriptions/:identity'.gsub(':identity', id)
468
+ stub_request(:put, /.*api.gocardless.com#{stub_url}/).to_return(
469
+ body: {
470
+ 'subscriptions' => {
471
+
472
+ 'amount' => 'amount-input',
473
+ 'created_at' => 'created_at-input',
474
+ 'currency' => 'currency-input',
475
+ 'day_of_month' => 'day_of_month-input',
476
+ 'end_date' => 'end_date-input',
477
+ 'id' => 'id-input',
478
+ 'interval' => 'interval-input',
479
+ 'interval_unit' => 'interval_unit-input',
480
+ 'links' => 'links-input',
481
+ 'metadata' => 'metadata-input',
482
+ 'month' => 'month-input',
483
+ 'name' => 'name-input',
484
+ 'payment_reference' => 'payment_reference-input',
485
+ 'start_date' => 'start_date-input',
486
+ 'status' => 'status-input',
487
+ 'upcoming_payments' => 'upcoming_payments-input'
488
+ }
489
+ }.to_json,
490
+ headers: response_headers
491
+ )
492
+ end
493
+
494
+ it 'updates and returns the resource' do
495
+ expect(put_update_response).to be_a(GoCardlessPro::Resources::Subscription)
496
+ expect(stub).to have_been_requested
497
+ end
86
498
  end
499
+ end
87
500
 
88
- it 'can handle new link attributes without erroring' do
89
- data['links']['foo'] = 'bar'
90
- expect { described_class.new(data) }.to_not raise_error
501
+ describe '#cancel' do
502
+ subject(:post_response) { client.subscriptions.cancel(resource_id) }
503
+
504
+ let(:resource_id) { 'ABC123' }
505
+
506
+ let!(:stub) do
507
+ # /subscriptions/%v/actions/cancel
508
+ stub_url = '/subscriptions/:identity/actions/cancel'.gsub(':identity', resource_id)
509
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/).to_return(
510
+ body: {
511
+ 'subscriptions' => {
512
+
513
+ 'amount' => 'amount-input',
514
+ 'created_at' => 'created_at-input',
515
+ 'currency' => 'currency-input',
516
+ 'day_of_month' => 'day_of_month-input',
517
+ 'end_date' => 'end_date-input',
518
+ 'id' => 'id-input',
519
+ 'interval' => 'interval-input',
520
+ 'interval_unit' => 'interval_unit-input',
521
+ 'links' => 'links-input',
522
+ 'metadata' => 'metadata-input',
523
+ 'month' => 'month-input',
524
+ 'name' => 'name-input',
525
+ 'payment_reference' => 'payment_reference-input',
526
+ 'start_date' => 'start_date-input',
527
+ 'status' => 'status-input',
528
+ 'upcoming_payments' => 'upcoming_payments-input'
529
+ }
530
+ }.to_json,
531
+ headers: response_headers
532
+ )
91
533
  end
92
534
 
93
- it 'can handle a nil links value' do
94
- data['links'] = nil
95
- expect { described_class.new(data).links }.to_not raise_error
535
+ it 'wraps the response and calls the right endpoint' do
536
+ expect(post_response).to be_a(GoCardlessPro::Resources::Subscription)
537
+
538
+ expect(stub).to have_been_requested
96
539
  end
97
540
 
98
- describe '#to_h' do
99
- it 'returns a hash representing the resource' do
100
- expect(described_class.new(data).to_h).to eq(data)
541
+ context 'when the request needs a body and custom header' do
542
+ let(:body) { { foo: 'bar' } }
543
+ let(:headers) { { 'Foo' => 'Bar' } }
544
+ subject(:post_response) { client.subscriptions.cancel(resource_id, body, headers) }
545
+
546
+ let(:resource_id) { 'ABC123' }
547
+
548
+ let!(:stub) do
549
+ # /subscriptions/%v/actions/cancel
550
+ stub_url = '/subscriptions/:identity/actions/cancel'.gsub(':identity', resource_id)
551
+ stub_request(:post, /.*api.gocardless.com#{stub_url}/)
552
+ .with(
553
+ body: { foo: 'bar' },
554
+ headers: { 'Foo' => 'Bar' }
555
+ ).to_return(
556
+ body: {
557
+ 'subscriptions' => {
558
+
559
+ 'amount' => 'amount-input',
560
+ 'created_at' => 'created_at-input',
561
+ 'currency' => 'currency-input',
562
+ 'day_of_month' => 'day_of_month-input',
563
+ 'end_date' => 'end_date-input',
564
+ 'id' => 'id-input',
565
+ 'interval' => 'interval-input',
566
+ 'interval_unit' => 'interval_unit-input',
567
+ 'links' => 'links-input',
568
+ 'metadata' => 'metadata-input',
569
+ 'month' => 'month-input',
570
+ 'name' => 'name-input',
571
+ 'payment_reference' => 'payment_reference-input',
572
+ 'start_date' => 'start_date-input',
573
+ 'status' => 'status-input',
574
+ 'upcoming_payments' => 'upcoming_payments-input'
575
+ }
576
+ }.to_json,
577
+ headers: response_headers
578
+ )
101
579
  end
102
580
  end
103
581
  end