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
@@ -2,11 +2,14 @@ require 'spec_helper'
2
2
 
3
3
  describe GoCardlessPro::Response do
4
4
  subject(:response) { described_class.new(raw_response) }
5
+
5
6
  let(:default_headers) do
6
7
  { 'Content-Type' => 'application/json' }
7
8
  end
8
9
 
9
- context 'when the response is not an error' do
10
+ describe '#body' do
11
+ subject(:body) { response.body }
12
+
10
13
  let(:raw_response) do
11
14
  double('response',
12
15
  headers: default_headers,
@@ -16,87 +19,17 @@ describe GoCardlessPro::Response do
16
19
  end
17
20
 
18
21
  it 'returns the body parsed into a hash' do
19
- expect(response.body).to eq('customers' => [])
22
+ expect(body).to eq('customers' => [])
20
23
  end
21
- end
22
24
 
23
- context 'when the response is empty' do
24
- let(:raw_response) do
25
- double('response', headers: default_headers, status: 204, body: '')
26
- end
27
-
28
- it 'returns nil' do
29
- expect(response.body).to be_nil
30
- end
31
- end
32
-
33
- context 'when the resonse is a validation error' do
34
- let(:raw_response) do
35
- double('response',
36
- headers: default_headers,
37
- status: 400,
38
- body: { error: { type: 'validation_failed' } }.to_json
39
- )
40
- end
41
-
42
- it 'raises a ValidationError' do
43
- expect { response.body }.to raise_error(GoCardlessPro::ValidationError)
44
- end
45
- end
46
-
47
- context 'when the resonse is a gocardless error' do
48
- let(:raw_response) do
49
- double('response',
50
- headers: default_headers,
51
- status: 400,
52
- body: { error: { type: 'gocardless' } }.to_json
53
- )
54
- end
55
-
56
- it 'raises a ValidationError' do
57
- expect { response.body }.to raise_error(GoCardlessPro::GoCardlessError)
58
- end
59
- end
60
-
61
- context 'when the resonse is an invalid api usage error' do
62
- let(:raw_response) do
63
- double('response',
64
- headers: default_headers,
65
- status: 400,
66
- body: { error: { type: 'invalid_api_usage' } }.to_json
67
- )
68
- end
69
-
70
- it 'raises a ValidationError' do
71
- expect { response.body }.to raise_error(GoCardlessPro::InvalidApiUsageError)
72
- end
73
- end
74
-
75
- context "when the response isn't JSON" do
76
- let(:raw_response) do
77
- double('response',
78
- headers: {},
79
- status: 400,
80
- body: ''
81
- )
82
- end
83
-
84
- it 'raises an ApiError' do
85
- expect { response.body }.to raise_error(GoCardlessPro::ApiError)
86
- end
87
- end
88
-
89
- context 'when the response is an invalid state error' do
90
- let(:raw_response) do
91
- double('response',
92
- headers: default_headers,
93
- status: 400,
94
- body: { error: { type: 'invalid_state' } }.to_json
95
- )
96
- end
25
+ context 'when the response is empty' do
26
+ let(:raw_response) do
27
+ double('response', headers: default_headers, status: 204, body: '')
28
+ end
97
29
 
98
- it 'raises a ValidationError' do
99
- expect { response.body }.to raise_error(GoCardlessPro::InvalidStateError)
30
+ it 'returns nil' do
31
+ expect(body).to be_nil
32
+ end
100
33
  end
101
34
  end
102
35
  end
@@ -7,6 +7,8 @@ describe GoCardlessPro::Services::BankDetailsLookupsService do
7
7
  )
8
8
  end
9
9
 
10
+ let(:response_headers) { { 'Content-Type' => 'application/json' } }
11
+
10
12
  describe '#create' do
11
13
  subject(:post_create_response) { client.bank_details_lookups.create(params: new_resource) }
12
14
  context 'with a valid request' do
@@ -43,13 +45,36 @@ describe GoCardlessPro::Services::BankDetailsLookupsService do
43
45
  }
44
46
 
45
47
  }.to_json,
46
- headers: { 'Content-Type' => 'application/json' }
48
+ headers: response_headers
47
49
  )
48
50
  end
49
51
 
50
52
  it 'creates and returns the resource' do
51
53
  expect(post_create_response).to be_a(GoCardlessPro::Resources::BankDetailsLookup)
52
54
  end
55
+
56
+ describe 'retry behaviour' do
57
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
58
+
59
+ it 'retries timeouts' do
60
+ stub = stub_request(:post, %r{.*api.gocardless.com/bank_details_lookups})
61
+ .to_timeout.then.to_return(status: 200, headers: response_headers)
62
+
63
+ post_create_response
64
+ expect(stub).to have_been_requested.twice
65
+ end
66
+
67
+ it 'retries 5XX errors' do
68
+ stub = stub_request(:post, %r{.*api.gocardless.com/bank_details_lookups})
69
+ .to_return(status: 502,
70
+ headers: { 'Content-Type' => 'text/html' },
71
+ body: '<html><body>Response from Cloudflare</body></html>')
72
+ .then.to_return(status: 200, headers: response_headers)
73
+
74
+ post_create_response
75
+ expect(stub).to have_been_requested.twice
76
+ end
77
+ end
53
78
  end
54
79
 
55
80
  context 'with a request that returns a validation error' do
@@ -66,7 +91,7 @@ describe GoCardlessPro::Services::BankDetailsLookupsService do
66
91
  ]
67
92
  }
68
93
  }.to_json,
69
- headers: { 'Content-Type' => 'application/json' },
94
+ headers: response_headers,
70
95
  status: 422
71
96
  )
72
97
  end
@@ -75,5 +100,45 @@ describe GoCardlessPro::Services::BankDetailsLookupsService do
75
100
  expect { post_create_response }.to raise_error(GoCardlessPro::ValidationError)
76
101
  end
77
102
  end
103
+
104
+ context 'with a request that returns an idempotent creation conflict error' do
105
+ let(:id) { 'ID123' }
106
+
107
+ let(:new_resource) do
108
+ {
109
+
110
+ 'available_debit_schemes' => 'available_debit_schemes-input',
111
+ 'bank_name' => 'bank_name-input',
112
+ 'bic' => 'bic-input'
113
+ }
114
+ end
115
+
116
+ let!(:post_stub) do
117
+ stub_request(:post, %r{.*api.gocardless.com/bank_details_lookups}).to_return(
118
+ body: {
119
+ error: {
120
+ type: 'invalid_state',
121
+ code: 409,
122
+ errors: [
123
+ {
124
+ message: 'A resource has already been created with this idempotency key',
125
+ reason: 'idempotent_creation_conflict',
126
+ links: {
127
+ conflicting_resource_id: id
128
+ }
129
+ }
130
+ ]
131
+ }
132
+ }.to_json,
133
+ headers: response_headers,
134
+ status: 409
135
+ )
136
+ end
137
+
138
+ it 'raises an InvalidStateError' do
139
+ expect { post_create_response }.to raise_error(GoCardlessPro::InvalidStateError)
140
+ expect(post_stub).to have_been_requested
141
+ end
142
+ end
78
143
  end
79
144
  end
@@ -7,6 +7,8 @@ describe GoCardlessPro::Services::CreditorBankAccountsService do
7
7
  )
8
8
  end
9
9
 
10
+ let(:response_headers) { { 'Content-Type' => 'application/json' } }
11
+
10
12
  describe '#create' do
11
13
  subject(:post_create_response) { client.creditor_bank_accounts.create(params: new_resource) }
12
14
  context 'with a valid request' do
@@ -64,13 +66,36 @@ describe GoCardlessPro::Services::CreditorBankAccountsService do
64
66
  }
65
67
 
66
68
  }.to_json,
67
- headers: { 'Content-Type' => 'application/json' }
69
+ headers: response_headers
68
70
  )
69
71
  end
70
72
 
71
73
  it 'creates and returns the resource' do
72
74
  expect(post_create_response).to be_a(GoCardlessPro::Resources::CreditorBankAccount)
73
75
  end
76
+
77
+ describe 'retry behaviour' do
78
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
79
+
80
+ it 'retries timeouts' do
81
+ stub = stub_request(:post, %r{.*api.gocardless.com/creditor_bank_accounts})
82
+ .to_timeout.then.to_return(status: 200, headers: response_headers)
83
+
84
+ post_create_response
85
+ expect(stub).to have_been_requested.twice
86
+ end
87
+
88
+ it 'retries 5XX errors' do
89
+ stub = stub_request(:post, %r{.*api.gocardless.com/creditor_bank_accounts})
90
+ .to_return(status: 502,
91
+ headers: { 'Content-Type' => 'text/html' },
92
+ body: '<html><body>Response from Cloudflare</body></html>')
93
+ .then.to_return(status: 200, headers: response_headers)
94
+
95
+ post_create_response
96
+ expect(stub).to have_been_requested.twice
97
+ end
98
+ end
74
99
  end
75
100
 
76
101
  context 'with a request that returns a validation error' do
@@ -87,7 +112,7 @@ describe GoCardlessPro::Services::CreditorBankAccountsService do
87
112
  ]
88
113
  }
89
114
  }.to_json,
90
- headers: { 'Content-Type' => 'application/json' },
115
+ headers: response_headers,
91
116
  status: 422
92
117
  )
93
118
  end
@@ -96,36 +121,111 @@ describe GoCardlessPro::Services::CreditorBankAccountsService do
96
121
  expect { post_create_response }.to raise_error(GoCardlessPro::ValidationError)
97
122
  end
98
123
  end
124
+
125
+ context 'with a request that returns an idempotent creation conflict error' do
126
+ let(:id) { 'ID123' }
127
+
128
+ let(:new_resource) do
129
+ {
130
+
131
+ 'account_holder_name' => 'account_holder_name-input',
132
+ 'account_number_ending' => 'account_number_ending-input',
133
+ 'bank_name' => 'bank_name-input',
134
+ 'country_code' => 'country_code-input',
135
+ 'created_at' => 'created_at-input',
136
+ 'currency' => 'currency-input',
137
+ 'enabled' => 'enabled-input',
138
+ 'id' => 'id-input',
139
+ 'links' => 'links-input',
140
+ 'metadata' => 'metadata-input'
141
+ }
142
+ end
143
+
144
+ let!(:post_stub) do
145
+ stub_request(:post, %r{.*api.gocardless.com/creditor_bank_accounts}).to_return(
146
+ body: {
147
+ error: {
148
+ type: 'invalid_state',
149
+ code: 409,
150
+ errors: [
151
+ {
152
+ message: 'A resource has already been created with this idempotency key',
153
+ reason: 'idempotent_creation_conflict',
154
+ links: {
155
+ conflicting_resource_id: id
156
+ }
157
+ }
158
+ ]
159
+ }
160
+ }.to_json,
161
+ headers: response_headers,
162
+ status: 409
163
+ )
164
+ end
165
+
166
+ let!(:get_stub) do
167
+ stub_url = "/creditor_bank_accounts/#{id}"
168
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/)
169
+ .to_return(
170
+ body: {
171
+ 'creditor_bank_accounts' => {
172
+
173
+ 'account_holder_name' => 'account_holder_name-input',
174
+ 'account_number_ending' => 'account_number_ending-input',
175
+ 'bank_name' => 'bank_name-input',
176
+ 'country_code' => 'country_code-input',
177
+ 'created_at' => 'created_at-input',
178
+ 'currency' => 'currency-input',
179
+ 'enabled' => 'enabled-input',
180
+ 'id' => 'id-input',
181
+ 'links' => 'links-input',
182
+ 'metadata' => 'metadata-input'
183
+ }
184
+ }.to_json,
185
+ headers: response_headers
186
+ )
187
+ end
188
+
189
+ it 'fetches the already-created resource' do
190
+ post_create_response
191
+ expect(post_stub).to have_been_requested
192
+ expect(get_stub).to have_been_requested
193
+ end
194
+ end
99
195
  end
100
196
 
101
197
  describe '#list' do
102
198
  describe 'with no filters' do
103
199
  subject(:get_list_response) { client.creditor_bank_accounts.list }
104
200
 
105
- before do
106
- stub_request(:get, %r{.*api.gocardless.com/creditor_bank_accounts}).to_return(
107
- body: {
108
- 'creditor_bank_accounts' => [{
201
+ let(:body) do
202
+ {
203
+ 'creditor_bank_accounts' => [{
109
204
 
110
- 'account_holder_name' => 'account_holder_name-input',
111
- 'account_number_ending' => 'account_number_ending-input',
112
- 'bank_name' => 'bank_name-input',
113
- 'country_code' => 'country_code-input',
114
- 'created_at' => 'created_at-input',
115
- 'currency' => 'currency-input',
116
- 'enabled' => 'enabled-input',
117
- 'id' => 'id-input',
118
- 'links' => 'links-input',
119
- 'metadata' => 'metadata-input'
120
- }],
121
- meta: {
122
- cursors: {
123
- before: nil,
124
- after: 'ABC123'
125
- }
205
+ 'account_holder_name' => 'account_holder_name-input',
206
+ 'account_number_ending' => 'account_number_ending-input',
207
+ 'bank_name' => 'bank_name-input',
208
+ 'country_code' => 'country_code-input',
209
+ 'created_at' => 'created_at-input',
210
+ 'currency' => 'currency-input',
211
+ 'enabled' => 'enabled-input',
212
+ 'id' => 'id-input',
213
+ 'links' => 'links-input',
214
+ 'metadata' => 'metadata-input'
215
+ }],
216
+ meta: {
217
+ cursors: {
218
+ before: nil,
219
+ after: 'ABC123'
126
220
  }
127
- }.to_json,
128
- headers: { 'Content-Type' => 'application/json' }
221
+ }
222
+ }.to_json
223
+ end
224
+
225
+ before do
226
+ stub_request(:get, %r{.*api.gocardless.com/creditor_bank_accounts}).to_return(
227
+ body: body,
228
+ headers: response_headers
129
229
  )
130
230
  end
131
231
 
@@ -157,6 +257,29 @@ describe GoCardlessPro::Services::CreditorBankAccountsService do
157
257
  end
158
258
 
159
259
  specify { expect(get_list_response.api_response.headers).to eql('content-type' => 'application/json') }
260
+
261
+ describe 'retry behaviour' do
262
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
263
+
264
+ it 'retries timeouts' do
265
+ stub = stub_request(:get, %r{.*api.gocardless.com/creditor_bank_accounts})
266
+ .to_timeout.then.to_return(status: 200, headers: response_headers, body: body)
267
+
268
+ get_list_response
269
+ expect(stub).to have_been_requested.twice
270
+ end
271
+
272
+ it 'retries 5XX errors' do
273
+ stub = stub_request(:get, %r{.*api.gocardless.com/creditor_bank_accounts})
274
+ .to_return(status: 502,
275
+ headers: { 'Content-Type' => 'text/html' },
276
+ body: '<html><body>Response from Cloudflare</body></html>')
277
+ .then.to_return(status: 200, headers: response_headers, body: body)
278
+
279
+ get_list_response
280
+ expect(stub).to have_been_requested.twice
281
+ end
282
+ end
160
283
  end
161
284
  end
162
285
 
@@ -182,7 +305,7 @@ describe GoCardlessPro::Services::CreditorBankAccountsService do
182
305
  limit: 1
183
306
  }
184
307
  }.to_json,
185
- headers: { 'Content-Type' => 'application/json' }
308
+ headers: response_headers
186
309
  )
187
310
  end
188
311
 
@@ -207,7 +330,7 @@ describe GoCardlessPro::Services::CreditorBankAccountsService do
207
330
  cursors: {}
208
331
  }
209
332
  }.to_json,
210
- headers: { 'Content-Type' => 'application/json' }
333
+ headers: response_headers
211
334
  )
212
335
  end
213
336
 
@@ -216,6 +339,123 @@ describe GoCardlessPro::Services::CreditorBankAccountsService do
216
339
  expect(first_response_stub).to have_been_requested
217
340
  expect(second_response_stub).to have_been_requested
218
341
  end
342
+
343
+ describe 'retry behaviour' do
344
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
345
+
346
+ it 'retries timeouts' do
347
+ first_response_stub = stub_request(:get, %r{.*api.gocardless.com/creditor_bank_accounts$}).to_return(
348
+ body: {
349
+ 'creditor_bank_accounts' => [{
350
+
351
+ 'account_holder_name' => 'account_holder_name-input',
352
+ 'account_number_ending' => 'account_number_ending-input',
353
+ 'bank_name' => 'bank_name-input',
354
+ 'country_code' => 'country_code-input',
355
+ 'created_at' => 'created_at-input',
356
+ 'currency' => 'currency-input',
357
+ 'enabled' => 'enabled-input',
358
+ 'id' => 'id-input',
359
+ 'links' => 'links-input',
360
+ 'metadata' => 'metadata-input'
361
+ }],
362
+ meta: {
363
+ cursors: { after: 'AB345' },
364
+ limit: 1
365
+ }
366
+ }.to_json,
367
+ headers: response_headers
368
+ )
369
+
370
+ second_response_stub = stub_request(:get, %r{.*api.gocardless.com/creditor_bank_accounts\?after=AB345})
371
+ .to_timeout.then
372
+ .to_return(
373
+ body: {
374
+ 'creditor_bank_accounts' => [{
375
+
376
+ 'account_holder_name' => 'account_holder_name-input',
377
+ 'account_number_ending' => 'account_number_ending-input',
378
+ 'bank_name' => 'bank_name-input',
379
+ 'country_code' => 'country_code-input',
380
+ 'created_at' => 'created_at-input',
381
+ 'currency' => 'currency-input',
382
+ 'enabled' => 'enabled-input',
383
+ 'id' => 'id-input',
384
+ 'links' => 'links-input',
385
+ 'metadata' => 'metadata-input'
386
+ }],
387
+ meta: {
388
+ limit: 2,
389
+ cursors: {}
390
+ }
391
+ }.to_json,
392
+ headers: response_headers
393
+ )
394
+
395
+ client.creditor_bank_accounts.all.to_a
396
+
397
+ expect(first_response_stub).to have_been_requested
398
+ expect(second_response_stub).to have_been_requested.twice
399
+ end
400
+
401
+ it 'retries 5XX errors' do
402
+ first_response_stub = stub_request(:get, %r{.*api.gocardless.com/creditor_bank_accounts$}).to_return(
403
+ body: {
404
+ 'creditor_bank_accounts' => [{
405
+
406
+ 'account_holder_name' => 'account_holder_name-input',
407
+ 'account_number_ending' => 'account_number_ending-input',
408
+ 'bank_name' => 'bank_name-input',
409
+ 'country_code' => 'country_code-input',
410
+ 'created_at' => 'created_at-input',
411
+ 'currency' => 'currency-input',
412
+ 'enabled' => 'enabled-input',
413
+ 'id' => 'id-input',
414
+ 'links' => 'links-input',
415
+ 'metadata' => 'metadata-input'
416
+ }],
417
+ meta: {
418
+ cursors: { after: 'AB345' },
419
+ limit: 1
420
+ }
421
+ }.to_json,
422
+ headers: response_headers
423
+ )
424
+
425
+ second_response_stub = stub_request(:get, %r{.*api.gocardless.com/creditor_bank_accounts\?after=AB345})
426
+ .to_return(
427
+ status: 502,
428
+ body: '<html><body>Response from Cloudflare</body></html>',
429
+ headers: { 'Content-Type' => 'text/html' }
430
+ ).then.to_return(
431
+ body: {
432
+ 'creditor_bank_accounts' => [{
433
+
434
+ 'account_holder_name' => 'account_holder_name-input',
435
+ 'account_number_ending' => 'account_number_ending-input',
436
+ 'bank_name' => 'bank_name-input',
437
+ 'country_code' => 'country_code-input',
438
+ 'created_at' => 'created_at-input',
439
+ 'currency' => 'currency-input',
440
+ 'enabled' => 'enabled-input',
441
+ 'id' => 'id-input',
442
+ 'links' => 'links-input',
443
+ 'metadata' => 'metadata-input'
444
+ }],
445
+ meta: {
446
+ limit: 2,
447
+ cursors: {}
448
+ }
449
+ }.to_json,
450
+ headers: response_headers
451
+ )
452
+
453
+ client.creditor_bank_accounts.all.to_a
454
+
455
+ expect(first_response_stub).to have_been_requested
456
+ expect(second_response_stub).to have_been_requested.twice
457
+ end
458
+ end
219
459
  end
220
460
 
221
461
  describe '#get' do
@@ -244,7 +484,7 @@ describe GoCardlessPro::Services::CreditorBankAccountsService do
244
484
  'metadata' => 'metadata-input'
245
485
  }
246
486
  }.to_json,
247
- headers: { 'Content-Type' => 'application/json' }
487
+ headers: response_headers
248
488
  )
249
489
  end
250
490
 
@@ -279,7 +519,7 @@ describe GoCardlessPro::Services::CreditorBankAccountsService do
279
519
  'metadata' => 'metadata-input'
280
520
  }
281
521
  }.to_json,
282
- headers: { 'Content-Type' => 'application/json' }
522
+ headers: response_headers
283
523
  )
284
524
  end
285
525
 
@@ -293,7 +533,7 @@ describe GoCardlessPro::Services::CreditorBankAccountsService do
293
533
  stub_url = '/creditor_bank_accounts/:identity'.gsub(':identity', id)
294
534
  stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
295
535
  body: '',
296
- headers: { 'Content-Type' => 'application/json' }
536
+ headers: response_headers
297
537
  )
298
538
  end
299
539
 
@@ -309,6 +549,33 @@ describe GoCardlessPro::Services::CreditorBankAccountsService do
309
549
  expect { get_response }.to_not raise_error(/bad URI/)
310
550
  end
311
551
  end
552
+
553
+ describe 'retry behaviour' do
554
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
555
+
556
+ it 'retries timeouts' do
557
+ stub_url = '/creditor_bank_accounts/:identity'.gsub(':identity', id)
558
+
559
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/)
560
+ .to_timeout.then.to_return(status: 200, headers: response_headers)
561
+
562
+ get_response
563
+ expect(stub).to have_been_requested.twice
564
+ end
565
+
566
+ it 'retries 5XX errors' do
567
+ stub_url = '/creditor_bank_accounts/:identity'.gsub(':identity', id)
568
+
569
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/)
570
+ .to_return(status: 502,
571
+ headers: { 'Content-Type' => 'text/html' },
572
+ body: '<html><body>Response from Cloudflare</body></html>')
573
+ .then.to_return(status: 200, headers: response_headers)
574
+
575
+ get_response
576
+ expect(stub).to have_been_requested.twice
577
+ end
578
+ end
312
579
  end
313
580
 
314
581
  describe '#disable' do
@@ -335,7 +602,7 @@ describe GoCardlessPro::Services::CreditorBankAccountsService do
335
602
  'metadata' => 'metadata-input'
336
603
  }
337
604
  }.to_json,
338
- headers: { 'Content-Type' => 'application/json' }
605
+ headers: response_headers
339
606
  )
340
607
  end
341
608
 
@@ -345,6 +612,17 @@ describe GoCardlessPro::Services::CreditorBankAccountsService do
345
612
  expect(stub).to have_been_requested
346
613
  end
347
614
 
615
+ describe 'retry behaviour' do
616
+ it "doesn't retry errors" do
617
+ stub_url = '/creditor_bank_accounts/:identity/actions/disable'.gsub(':identity', resource_id)
618
+ stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/)
619
+ .to_timeout
620
+
621
+ expect { post_response }.to raise_error(Faraday::TimeoutError)
622
+ expect(stub).to have_been_requested
623
+ end
624
+ end
625
+
348
626
  context 'when the request needs a body and custom header' do
349
627
  let(:body) { { foo: 'bar' } }
350
628
  let(:headers) { { 'Foo' => 'Bar' } }
@@ -375,7 +653,7 @@ describe GoCardlessPro::Services::CreditorBankAccountsService do
375
653
  'metadata' => 'metadata-input'
376
654
  }
377
655
  }.to_json,
378
- headers: { 'Content-Type' => 'application/json' }
656
+ headers: response_headers
379
657
  )
380
658
  end
381
659
  end