gocardless_pro 1.1.0 → 2.0.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 (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
@@ -7,31 +7,37 @@ describe GoCardlessPro::Services::EventsService do
7
7
  )
8
8
  end
9
9
 
10
+ let(:response_headers) { { 'Content-Type' => 'application/json' } }
11
+
10
12
  describe '#list' do
11
13
  describe 'with no filters' do
12
14
  subject(:get_list_response) { client.events.list }
13
15
 
14
- before do
15
- stub_request(:get, %r{.*api.gocardless.com/events}).to_return(
16
- body: {
17
- 'events' => [{
16
+ let(:body) do
17
+ {
18
+ 'events' => [{
18
19
 
19
- 'action' => 'action-input',
20
- 'created_at' => 'created_at-input',
21
- 'details' => 'details-input',
22
- 'id' => 'id-input',
23
- 'links' => 'links-input',
24
- 'metadata' => 'metadata-input',
25
- 'resource_type' => 'resource_type-input'
26
- }],
27
- meta: {
28
- cursors: {
29
- before: nil,
30
- after: 'ABC123'
31
- }
20
+ 'action' => 'action-input',
21
+ 'created_at' => 'created_at-input',
22
+ 'details' => 'details-input',
23
+ 'id' => 'id-input',
24
+ 'links' => 'links-input',
25
+ 'metadata' => 'metadata-input',
26
+ 'resource_type' => 'resource_type-input'
27
+ }],
28
+ meta: {
29
+ cursors: {
30
+ before: nil,
31
+ after: 'ABC123'
32
32
  }
33
- }.to_json,
34
- headers: { 'Content-Type' => 'application/json' }
33
+ }
34
+ }.to_json
35
+ end
36
+
37
+ before do
38
+ stub_request(:get, %r{.*api.gocardless.com/events}).to_return(
39
+ body: body,
40
+ headers: response_headers
35
41
  )
36
42
  end
37
43
 
@@ -57,6 +63,29 @@ describe GoCardlessPro::Services::EventsService do
57
63
  end
58
64
 
59
65
  specify { expect(get_list_response.api_response.headers).to eql('content-type' => 'application/json') }
66
+
67
+ describe 'retry behaviour' do
68
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
69
+
70
+ it 'retries timeouts' do
71
+ stub = stub_request(:get, %r{.*api.gocardless.com/events})
72
+ .to_timeout.then.to_return(status: 200, headers: response_headers, body: body)
73
+
74
+ get_list_response
75
+ expect(stub).to have_been_requested.twice
76
+ end
77
+
78
+ it 'retries 5XX errors' do
79
+ stub = stub_request(:get, %r{.*api.gocardless.com/events})
80
+ .to_return(status: 502,
81
+ headers: { 'Content-Type' => 'text/html' },
82
+ body: '<html><body>Response from Cloudflare</body></html>')
83
+ .then.to_return(status: 200, headers: response_headers, body: body)
84
+
85
+ get_list_response
86
+ expect(stub).to have_been_requested.twice
87
+ end
88
+ end
60
89
  end
61
90
  end
62
91
 
@@ -79,7 +108,7 @@ describe GoCardlessPro::Services::EventsService do
79
108
  limit: 1
80
109
  }
81
110
  }.to_json,
82
- headers: { 'Content-Type' => 'application/json' }
111
+ headers: response_headers
83
112
  )
84
113
  end
85
114
 
@@ -101,7 +130,7 @@ describe GoCardlessPro::Services::EventsService do
101
130
  cursors: {}
102
131
  }
103
132
  }.to_json,
104
- headers: { 'Content-Type' => 'application/json' }
133
+ headers: response_headers
105
134
  )
106
135
  end
107
136
 
@@ -110,6 +139,111 @@ describe GoCardlessPro::Services::EventsService do
110
139
  expect(first_response_stub).to have_been_requested
111
140
  expect(second_response_stub).to have_been_requested
112
141
  end
142
+
143
+ describe 'retry behaviour' do
144
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
145
+
146
+ it 'retries timeouts' do
147
+ first_response_stub = stub_request(:get, %r{.*api.gocardless.com/events$}).to_return(
148
+ body: {
149
+ 'events' => [{
150
+
151
+ 'action' => 'action-input',
152
+ 'created_at' => 'created_at-input',
153
+ 'details' => 'details-input',
154
+ 'id' => 'id-input',
155
+ 'links' => 'links-input',
156
+ 'metadata' => 'metadata-input',
157
+ 'resource_type' => 'resource_type-input'
158
+ }],
159
+ meta: {
160
+ cursors: { after: 'AB345' },
161
+ limit: 1
162
+ }
163
+ }.to_json,
164
+ headers: response_headers
165
+ )
166
+
167
+ second_response_stub = stub_request(:get, %r{.*api.gocardless.com/events\?after=AB345})
168
+ .to_timeout.then
169
+ .to_return(
170
+ body: {
171
+ 'events' => [{
172
+
173
+ 'action' => 'action-input',
174
+ 'created_at' => 'created_at-input',
175
+ 'details' => 'details-input',
176
+ 'id' => 'id-input',
177
+ 'links' => 'links-input',
178
+ 'metadata' => 'metadata-input',
179
+ 'resource_type' => 'resource_type-input'
180
+ }],
181
+ meta: {
182
+ limit: 2,
183
+ cursors: {}
184
+ }
185
+ }.to_json,
186
+ headers: response_headers
187
+ )
188
+
189
+ client.events.all.to_a
190
+
191
+ expect(first_response_stub).to have_been_requested
192
+ expect(second_response_stub).to have_been_requested.twice
193
+ end
194
+
195
+ it 'retries 5XX errors' do
196
+ first_response_stub = stub_request(:get, %r{.*api.gocardless.com/events$}).to_return(
197
+ body: {
198
+ 'events' => [{
199
+
200
+ 'action' => 'action-input',
201
+ 'created_at' => 'created_at-input',
202
+ 'details' => 'details-input',
203
+ 'id' => 'id-input',
204
+ 'links' => 'links-input',
205
+ 'metadata' => 'metadata-input',
206
+ 'resource_type' => 'resource_type-input'
207
+ }],
208
+ meta: {
209
+ cursors: { after: 'AB345' },
210
+ limit: 1
211
+ }
212
+ }.to_json,
213
+ headers: response_headers
214
+ )
215
+
216
+ second_response_stub = stub_request(:get, %r{.*api.gocardless.com/events\?after=AB345})
217
+ .to_return(
218
+ status: 502,
219
+ body: '<html><body>Response from Cloudflare</body></html>',
220
+ headers: { 'Content-Type' => 'text/html' }
221
+ ).then.to_return(
222
+ body: {
223
+ 'events' => [{
224
+
225
+ 'action' => 'action-input',
226
+ 'created_at' => 'created_at-input',
227
+ 'details' => 'details-input',
228
+ 'id' => 'id-input',
229
+ 'links' => 'links-input',
230
+ 'metadata' => 'metadata-input',
231
+ 'resource_type' => 'resource_type-input'
232
+ }],
233
+ meta: {
234
+ limit: 2,
235
+ cursors: {}
236
+ }
237
+ }.to_json,
238
+ headers: response_headers
239
+ )
240
+
241
+ client.events.all.to_a
242
+
243
+ expect(first_response_stub).to have_been_requested
244
+ expect(second_response_stub).to have_been_requested.twice
245
+ end
246
+ end
113
247
  end
114
248
 
115
249
  describe '#get' do
@@ -135,7 +269,7 @@ describe GoCardlessPro::Services::EventsService do
135
269
  'resource_type' => 'resource_type-input'
136
270
  }
137
271
  }.to_json,
138
- headers: { 'Content-Type' => 'application/json' }
272
+ headers: response_headers
139
273
  )
140
274
  end
141
275
 
@@ -167,7 +301,7 @@ describe GoCardlessPro::Services::EventsService do
167
301
  'resource_type' => 'resource_type-input'
168
302
  }
169
303
  }.to_json,
170
- headers: { 'Content-Type' => 'application/json' }
304
+ headers: response_headers
171
305
  )
172
306
  end
173
307
 
@@ -181,7 +315,7 @@ describe GoCardlessPro::Services::EventsService do
181
315
  stub_url = '/events/:identity'.gsub(':identity', id)
182
316
  stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
183
317
  body: '',
184
- headers: { 'Content-Type' => 'application/json' }
318
+ headers: response_headers
185
319
  )
186
320
  end
187
321
 
@@ -197,5 +331,32 @@ describe GoCardlessPro::Services::EventsService do
197
331
  expect { get_response }.to_not raise_error(/bad URI/)
198
332
  end
199
333
  end
334
+
335
+ describe 'retry behaviour' do
336
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
337
+
338
+ it 'retries timeouts' do
339
+ stub_url = '/events/:identity'.gsub(':identity', id)
340
+
341
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/)
342
+ .to_timeout.then.to_return(status: 200, headers: response_headers)
343
+
344
+ get_response
345
+ expect(stub).to have_been_requested.twice
346
+ end
347
+
348
+ it 'retries 5XX errors' do
349
+ stub_url = '/events/:identity'.gsub(':identity', id)
350
+
351
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/)
352
+ .to_return(status: 502,
353
+ headers: { 'Content-Type' => 'text/html' },
354
+ body: '<html><body>Response from Cloudflare</body></html>')
355
+ .then.to_return(status: 200, headers: response_headers)
356
+
357
+ get_response
358
+ expect(stub).to have_been_requested.twice
359
+ end
360
+ end
200
361
  end
201
362
  end
@@ -7,6 +7,8 @@ describe GoCardlessPro::Services::MandatePdfsService 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.mandate_pdfs.create(params: new_resource) }
12
14
  context 'with a valid request' do
@@ -40,13 +42,36 @@ describe GoCardlessPro::Services::MandatePdfsService do
40
42
  }
41
43
 
42
44
  }.to_json,
43
- headers: { 'Content-Type' => 'application/json' }
45
+ headers: response_headers
44
46
  )
45
47
  end
46
48
 
47
49
  it 'creates and returns the resource' do
48
50
  expect(post_create_response).to be_a(GoCardlessPro::Resources::MandatePdf)
49
51
  end
52
+
53
+ describe 'retry behaviour' do
54
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
55
+
56
+ it 'retries timeouts' do
57
+ stub = stub_request(:post, %r{.*api.gocardless.com/mandate_pdfs})
58
+ .to_timeout.then.to_return(status: 200, headers: response_headers)
59
+
60
+ post_create_response
61
+ expect(stub).to have_been_requested.twice
62
+ end
63
+
64
+ it 'retries 5XX errors' do
65
+ stub = stub_request(:post, %r{.*api.gocardless.com/mandate_pdfs})
66
+ .to_return(status: 502,
67
+ headers: { 'Content-Type' => 'text/html' },
68
+ body: '<html><body>Response from Cloudflare</body></html>')
69
+ .then.to_return(status: 200, headers: response_headers)
70
+
71
+ post_create_response
72
+ expect(stub).to have_been_requested.twice
73
+ end
74
+ end
50
75
  end
51
76
 
52
77
  context 'with a request that returns a validation error' do
@@ -63,7 +88,7 @@ describe GoCardlessPro::Services::MandatePdfsService do
63
88
  ]
64
89
  }
65
90
  }.to_json,
66
- headers: { 'Content-Type' => 'application/json' },
91
+ headers: response_headers,
67
92
  status: 422
68
93
  )
69
94
  end
@@ -72,5 +97,44 @@ describe GoCardlessPro::Services::MandatePdfsService do
72
97
  expect { post_create_response }.to raise_error(GoCardlessPro::ValidationError)
73
98
  end
74
99
  end
100
+
101
+ context 'with a request that returns an idempotent creation conflict error' do
102
+ let(:id) { 'ID123' }
103
+
104
+ let(:new_resource) do
105
+ {
106
+
107
+ 'expires_at' => 'expires_at-input',
108
+ 'url' => 'url-input'
109
+ }
110
+ end
111
+
112
+ let!(:post_stub) do
113
+ stub_request(:post, %r{.*api.gocardless.com/mandate_pdfs}).to_return(
114
+ body: {
115
+ error: {
116
+ type: 'invalid_state',
117
+ code: 409,
118
+ errors: [
119
+ {
120
+ message: 'A resource has already been created with this idempotency key',
121
+ reason: 'idempotent_creation_conflict',
122
+ links: {
123
+ conflicting_resource_id: id
124
+ }
125
+ }
126
+ ]
127
+ }
128
+ }.to_json,
129
+ headers: response_headers,
130
+ status: 409
131
+ )
132
+ end
133
+
134
+ it 'raises an InvalidStateError' do
135
+ expect { post_create_response }.to raise_error(GoCardlessPro::InvalidStateError)
136
+ expect(post_stub).to have_been_requested
137
+ end
138
+ end
75
139
  end
76
140
  end
@@ -7,6 +7,8 @@ describe GoCardlessPro::Services::MandatesService 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.mandates.create(params: new_resource) }
12
14
  context 'with a valid request' do
@@ -61,13 +63,36 @@ describe GoCardlessPro::Services::MandatesService do
61
63
  }
62
64
 
63
65
  }.to_json,
64
- headers: { 'Content-Type' => 'application/json' }
66
+ headers: response_headers
65
67
  )
66
68
  end
67
69
 
68
70
  it 'creates and returns the resource' do
69
71
  expect(post_create_response).to be_a(GoCardlessPro::Resources::Mandate)
70
72
  end
73
+
74
+ describe 'retry behaviour' do
75
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
76
+
77
+ it 'retries timeouts' do
78
+ stub = stub_request(:post, %r{.*api.gocardless.com/mandates})
79
+ .to_timeout.then.to_return(status: 200, headers: response_headers)
80
+
81
+ post_create_response
82
+ expect(stub).to have_been_requested.twice
83
+ end
84
+
85
+ it 'retries 5XX errors' do
86
+ stub = stub_request(:post, %r{.*api.gocardless.com/mandates})
87
+ .to_return(status: 502,
88
+ headers: { 'Content-Type' => 'text/html' },
89
+ body: '<html><body>Response from Cloudflare</body></html>')
90
+ .then.to_return(status: 200, headers: response_headers)
91
+
92
+ post_create_response
93
+ expect(stub).to have_been_requested.twice
94
+ end
95
+ end
71
96
  end
72
97
 
73
98
  context 'with a request that returns a validation error' do
@@ -84,7 +109,7 @@ describe GoCardlessPro::Services::MandatesService do
84
109
  ]
85
110
  }
86
111
  }.to_json,
87
- headers: { 'Content-Type' => 'application/json' },
112
+ headers: response_headers,
88
113
  status: 422
89
114
  )
90
115
  end
@@ -93,35 +118,108 @@ describe GoCardlessPro::Services::MandatesService do
93
118
  expect { post_create_response }.to raise_error(GoCardlessPro::ValidationError)
94
119
  end
95
120
  end
121
+
122
+ context 'with a request that returns an idempotent creation conflict error' do
123
+ let(:id) { 'ID123' }
124
+
125
+ let(:new_resource) do
126
+ {
127
+
128
+ 'created_at' => 'created_at-input',
129
+ 'id' => 'id-input',
130
+ 'links' => 'links-input',
131
+ 'metadata' => 'metadata-input',
132
+ 'next_possible_charge_date' => 'next_possible_charge_date-input',
133
+ 'payments_require_approval' => 'payments_require_approval-input',
134
+ 'reference' => 'reference-input',
135
+ 'scheme' => 'scheme-input',
136
+ 'status' => 'status-input'
137
+ }
138
+ end
139
+
140
+ let!(:post_stub) do
141
+ stub_request(:post, %r{.*api.gocardless.com/mandates}).to_return(
142
+ body: {
143
+ error: {
144
+ type: 'invalid_state',
145
+ code: 409,
146
+ errors: [
147
+ {
148
+ message: 'A resource has already been created with this idempotency key',
149
+ reason: 'idempotent_creation_conflict',
150
+ links: {
151
+ conflicting_resource_id: id
152
+ }
153
+ }
154
+ ]
155
+ }
156
+ }.to_json,
157
+ headers: response_headers,
158
+ status: 409
159
+ )
160
+ end
161
+
162
+ let!(:get_stub) do
163
+ stub_url = "/mandates/#{id}"
164
+ stub_request(:get, /.*api.gocardless.com#{stub_url}/)
165
+ .to_return(
166
+ body: {
167
+ 'mandates' => {
168
+
169
+ 'created_at' => 'created_at-input',
170
+ 'id' => 'id-input',
171
+ 'links' => 'links-input',
172
+ 'metadata' => 'metadata-input',
173
+ 'next_possible_charge_date' => 'next_possible_charge_date-input',
174
+ 'payments_require_approval' => 'payments_require_approval-input',
175
+ 'reference' => 'reference-input',
176
+ 'scheme' => 'scheme-input',
177
+ 'status' => 'status-input'
178
+ }
179
+ }.to_json,
180
+ headers: response_headers
181
+ )
182
+ end
183
+
184
+ it 'fetches the already-created resource' do
185
+ post_create_response
186
+ expect(post_stub).to have_been_requested
187
+ expect(get_stub).to have_been_requested
188
+ end
189
+ end
96
190
  end
97
191
 
98
192
  describe '#list' do
99
193
  describe 'with no filters' do
100
194
  subject(:get_list_response) { client.mandates.list }
101
195
 
102
- before do
103
- stub_request(:get, %r{.*api.gocardless.com/mandates}).to_return(
104
- body: {
105
- 'mandates' => [{
196
+ let(:body) do
197
+ {
198
+ 'mandates' => [{
106
199
 
107
- 'created_at' => 'created_at-input',
108
- 'id' => 'id-input',
109
- 'links' => 'links-input',
110
- 'metadata' => 'metadata-input',
111
- 'next_possible_charge_date' => 'next_possible_charge_date-input',
112
- 'payments_require_approval' => 'payments_require_approval-input',
113
- 'reference' => 'reference-input',
114
- 'scheme' => 'scheme-input',
115
- 'status' => 'status-input'
116
- }],
117
- meta: {
118
- cursors: {
119
- before: nil,
120
- after: 'ABC123'
121
- }
200
+ 'created_at' => 'created_at-input',
201
+ 'id' => 'id-input',
202
+ 'links' => 'links-input',
203
+ 'metadata' => 'metadata-input',
204
+ 'next_possible_charge_date' => 'next_possible_charge_date-input',
205
+ 'payments_require_approval' => 'payments_require_approval-input',
206
+ 'reference' => 'reference-input',
207
+ 'scheme' => 'scheme-input',
208
+ 'status' => 'status-input'
209
+ }],
210
+ meta: {
211
+ cursors: {
212
+ before: nil,
213
+ after: 'ABC123'
122
214
  }
123
- }.to_json,
124
- headers: { 'Content-Type' => 'application/json' }
215
+ }
216
+ }.to_json
217
+ end
218
+
219
+ before do
220
+ stub_request(:get, %r{.*api.gocardless.com/mandates}).to_return(
221
+ body: body,
222
+ headers: response_headers
125
223
  )
126
224
  end
127
225
 
@@ -151,6 +249,29 @@ describe GoCardlessPro::Services::MandatesService do
151
249
  end
152
250
 
153
251
  specify { expect(get_list_response.api_response.headers).to eql('content-type' => 'application/json') }
252
+
253
+ describe 'retry behaviour' do
254
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
255
+
256
+ it 'retries timeouts' do
257
+ stub = stub_request(:get, %r{.*api.gocardless.com/mandates})
258
+ .to_timeout.then.to_return(status: 200, headers: response_headers, body: body)
259
+
260
+ get_list_response
261
+ expect(stub).to have_been_requested.twice
262
+ end
263
+
264
+ it 'retries 5XX errors' do
265
+ stub = stub_request(:get, %r{.*api.gocardless.com/mandates})
266
+ .to_return(status: 502,
267
+ headers: { 'Content-Type' => 'text/html' },
268
+ body: '<html><body>Response from Cloudflare</body></html>')
269
+ .then.to_return(status: 200, headers: response_headers, body: body)
270
+
271
+ get_list_response
272
+ expect(stub).to have_been_requested.twice
273
+ end
274
+ end
154
275
  end
155
276
  end
156
277
 
@@ -175,7 +296,7 @@ describe GoCardlessPro::Services::MandatesService do
175
296
  limit: 1
176
297
  }
177
298
  }.to_json,
178
- headers: { 'Content-Type' => 'application/json' }
299
+ headers: response_headers
179
300
  )
180
301
  end
181
302
 
@@ -199,7 +320,7 @@ describe GoCardlessPro::Services::MandatesService do
199
320
  cursors: {}
200
321
  }
201
322
  }.to_json,
202
- headers: { 'Content-Type' => 'application/json' }
323
+ headers: response_headers
203
324
  )
204
325
  end
205
326
 
@@ -208,6 +329,119 @@ describe GoCardlessPro::Services::MandatesService do
208
329
  expect(first_response_stub).to have_been_requested
209
330
  expect(second_response_stub).to have_been_requested
210
331
  end
332
+
333
+ describe 'retry behaviour' do
334
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
335
+
336
+ it 'retries timeouts' do
337
+ first_response_stub = stub_request(:get, %r{.*api.gocardless.com/mandates$}).to_return(
338
+ body: {
339
+ 'mandates' => [{
340
+
341
+ 'created_at' => 'created_at-input',
342
+ 'id' => 'id-input',
343
+ 'links' => 'links-input',
344
+ 'metadata' => 'metadata-input',
345
+ 'next_possible_charge_date' => 'next_possible_charge_date-input',
346
+ 'payments_require_approval' => 'payments_require_approval-input',
347
+ 'reference' => 'reference-input',
348
+ 'scheme' => 'scheme-input',
349
+ 'status' => 'status-input'
350
+ }],
351
+ meta: {
352
+ cursors: { after: 'AB345' },
353
+ limit: 1
354
+ }
355
+ }.to_json,
356
+ headers: response_headers
357
+ )
358
+
359
+ second_response_stub = stub_request(:get, %r{.*api.gocardless.com/mandates\?after=AB345})
360
+ .to_timeout.then
361
+ .to_return(
362
+ body: {
363
+ 'mandates' => [{
364
+
365
+ 'created_at' => 'created_at-input',
366
+ 'id' => 'id-input',
367
+ 'links' => 'links-input',
368
+ 'metadata' => 'metadata-input',
369
+ 'next_possible_charge_date' => 'next_possible_charge_date-input',
370
+ 'payments_require_approval' => 'payments_require_approval-input',
371
+ 'reference' => 'reference-input',
372
+ 'scheme' => 'scheme-input',
373
+ 'status' => 'status-input'
374
+ }],
375
+ meta: {
376
+ limit: 2,
377
+ cursors: {}
378
+ }
379
+ }.to_json,
380
+ headers: response_headers
381
+ )
382
+
383
+ client.mandates.all.to_a
384
+
385
+ expect(first_response_stub).to have_been_requested
386
+ expect(second_response_stub).to have_been_requested.twice
387
+ end
388
+
389
+ it 'retries 5XX errors' do
390
+ first_response_stub = stub_request(:get, %r{.*api.gocardless.com/mandates$}).to_return(
391
+ body: {
392
+ 'mandates' => [{
393
+
394
+ 'created_at' => 'created_at-input',
395
+ 'id' => 'id-input',
396
+ 'links' => 'links-input',
397
+ 'metadata' => 'metadata-input',
398
+ 'next_possible_charge_date' => 'next_possible_charge_date-input',
399
+ 'payments_require_approval' => 'payments_require_approval-input',
400
+ 'reference' => 'reference-input',
401
+ 'scheme' => 'scheme-input',
402
+ 'status' => 'status-input'
403
+ }],
404
+ meta: {
405
+ cursors: { after: 'AB345' },
406
+ limit: 1
407
+ }
408
+ }.to_json,
409
+ headers: response_headers
410
+ )
411
+
412
+ second_response_stub = stub_request(:get, %r{.*api.gocardless.com/mandates\?after=AB345})
413
+ .to_return(
414
+ status: 502,
415
+ body: '<html><body>Response from Cloudflare</body></html>',
416
+ headers: { 'Content-Type' => 'text/html' }
417
+ ).then.to_return(
418
+ body: {
419
+ 'mandates' => [{
420
+
421
+ 'created_at' => 'created_at-input',
422
+ 'id' => 'id-input',
423
+ 'links' => 'links-input',
424
+ 'metadata' => 'metadata-input',
425
+ 'next_possible_charge_date' => 'next_possible_charge_date-input',
426
+ 'payments_require_approval' => 'payments_require_approval-input',
427
+ 'reference' => 'reference-input',
428
+ 'scheme' => 'scheme-input',
429
+ 'status' => 'status-input'
430
+ }],
431
+ meta: {
432
+ limit: 2,
433
+ cursors: {}
434
+ }
435
+ }.to_json,
436
+ headers: response_headers
437
+ )
438
+
439
+ client.mandates.all.to_a
440
+
441
+ expect(first_response_stub).to have_been_requested
442
+ expect(second_response_stub).to have_been_requested.twice
443
+ end
444
+ end
211
445
  end
212
446
 
213
447
  describe '#get' do
@@ -235,7 +469,7 @@ describe GoCardlessPro::Services::MandatesService do
235
469
  'status' => 'status-input'
236
470
  }
237
471
  }.to_json,
238
- headers: { 'Content-Type' => 'application/json' }
472
+ headers: response_headers
239
473
  )
240
474
  end
241
475
 
@@ -269,7 +503,7 @@ describe GoCardlessPro::Services::MandatesService do
269
503
  'status' => 'status-input'
270
504
  }
271
505
  }.to_json,
272
- headers: { 'Content-Type' => 'application/json' }
506
+ headers: response_headers
273
507
  )
274
508
  end
275
509
 
@@ -283,7 +517,7 @@ describe GoCardlessPro::Services::MandatesService do
283
517
  stub_url = '/mandates/:identity'.gsub(':identity', id)
284
518
  stub_request(:get, /.*api.gocardless.com#{stub_url}/).to_return(
285
519
  body: '',
286
- headers: { 'Content-Type' => 'application/json' }
520
+ headers: response_headers
287
521
  )
288
522
  end
289
523
 
@@ -299,6 +533,33 @@ describe GoCardlessPro::Services::MandatesService do
299
533
  expect { get_response }.to_not raise_error(/bad URI/)
300
534
  end
301
535
  end
536
+
537
+ describe 'retry behaviour' do
538
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
539
+
540
+ it 'retries timeouts' do
541
+ stub_url = '/mandates/:identity'.gsub(':identity', id)
542
+
543
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/)
544
+ .to_timeout.then.to_return(status: 200, headers: response_headers)
545
+
546
+ get_response
547
+ expect(stub).to have_been_requested.twice
548
+ end
549
+
550
+ it 'retries 5XX errors' do
551
+ stub_url = '/mandates/:identity'.gsub(':identity', id)
552
+
553
+ stub = stub_request(:get, /.*api.gocardless.com#{stub_url}/)
554
+ .to_return(status: 502,
555
+ headers: { 'Content-Type' => 'text/html' },
556
+ body: '<html><body>Response from Cloudflare</body></html>')
557
+ .then.to_return(status: 200, headers: response_headers)
558
+
559
+ get_response
560
+ expect(stub).to have_been_requested.twice
561
+ end
562
+ end
302
563
  end
303
564
 
304
565
  describe '#update' do
@@ -325,7 +586,7 @@ describe GoCardlessPro::Services::MandatesService do
325
586
  'status' => 'status-input'
326
587
  }
327
588
  }.to_json,
328
- headers: { 'Content-Type' => 'application/json' }
589
+ headers: response_headers
329
590
  )
330
591
  end
331
592
 
@@ -333,6 +594,31 @@ describe GoCardlessPro::Services::MandatesService do
333
594
  expect(put_update_response).to be_a(GoCardlessPro::Resources::Mandate)
334
595
  expect(stub).to have_been_requested
335
596
  end
597
+
598
+ describe 'retry behaviour' do
599
+ before { allow_any_instance_of(GoCardlessPro::Request).to receive(:sleep) }
600
+
601
+ it 'retries timeouts' do
602
+ stub_url = '/mandates/:identity'.gsub(':identity', id)
603
+ stub = stub_request(:put, /.*api.gocardless.com#{stub_url}/)
604
+ .to_timeout.then.to_return(status: 200, headers: response_headers)
605
+
606
+ put_update_response
607
+ expect(stub).to have_been_requested.twice
608
+ end
609
+
610
+ it 'retries 5XX errors' do
611
+ stub_url = '/mandates/:identity'.gsub(':identity', id)
612
+ stub = stub_request(:put, /.*api.gocardless.com#{stub_url}/)
613
+ .to_return(status: 502,
614
+ headers: { 'Content-Type' => 'text/html' },
615
+ body: '<html><body>Response from Cloudflare</body></html>')
616
+ .then.to_return(status: 200, headers: response_headers)
617
+
618
+ put_update_response
619
+ expect(stub).to have_been_requested.twice
620
+ end
621
+ end
336
622
  end
337
623
  end
338
624
 
@@ -359,7 +645,7 @@ describe GoCardlessPro::Services::MandatesService do
359
645
  'status' => 'status-input'
360
646
  }
361
647
  }.to_json,
362
- headers: { 'Content-Type' => 'application/json' }
648
+ headers: response_headers
363
649
  )
364
650
  end
365
651
 
@@ -369,6 +655,17 @@ describe GoCardlessPro::Services::MandatesService do
369
655
  expect(stub).to have_been_requested
370
656
  end
371
657
 
658
+ describe 'retry behaviour' do
659
+ it "doesn't retry errors" do
660
+ stub_url = '/mandates/:identity/actions/cancel'.gsub(':identity', resource_id)
661
+ stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/)
662
+ .to_timeout
663
+
664
+ expect { post_response }.to raise_error(Faraday::TimeoutError)
665
+ expect(stub).to have_been_requested
666
+ end
667
+ end
668
+
372
669
  context 'when the request needs a body and custom header' do
373
670
  let(:body) { { foo: 'bar' } }
374
671
  let(:headers) { { 'Foo' => 'Bar' } }
@@ -398,7 +695,7 @@ describe GoCardlessPro::Services::MandatesService do
398
695
  'status' => 'status-input'
399
696
  }
400
697
  }.to_json,
401
- headers: { 'Content-Type' => 'application/json' }
698
+ headers: response_headers
402
699
  )
403
700
  end
404
701
  end
@@ -427,7 +724,7 @@ describe GoCardlessPro::Services::MandatesService do
427
724
  'status' => 'status-input'
428
725
  }
429
726
  }.to_json,
430
- headers: { 'Content-Type' => 'application/json' }
727
+ headers: response_headers
431
728
  )
432
729
  end
433
730
 
@@ -437,6 +734,17 @@ describe GoCardlessPro::Services::MandatesService do
437
734
  expect(stub).to have_been_requested
438
735
  end
439
736
 
737
+ describe 'retry behaviour' do
738
+ it "doesn't retry errors" do
739
+ stub_url = '/mandates/:identity/actions/reinstate'.gsub(':identity', resource_id)
740
+ stub = stub_request(:post, /.*api.gocardless.com#{stub_url}/)
741
+ .to_timeout
742
+
743
+ expect { post_response }.to raise_error(Faraday::TimeoutError)
744
+ expect(stub).to have_been_requested
745
+ end
746
+ end
747
+
440
748
  context 'when the request needs a body and custom header' do
441
749
  let(:body) { { foo: 'bar' } }
442
750
  let(:headers) { { 'Foo' => 'Bar' } }
@@ -466,7 +774,7 @@ describe GoCardlessPro::Services::MandatesService do
466
774
  'status' => 'status-input'
467
775
  }
468
776
  }.to_json,
469
- headers: { 'Content-Type' => 'application/json' }
777
+ headers: response_headers
470
778
  )
471
779
  end
472
780
  end