minty 1.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.
- checksums.yaml +7 -0
- data/.bundle/config +4 -0
- data/.devcontainer/Dockerfile +19 -0
- data/.devcontainer/devcontainer.json +37 -0
- data/.env.example +2 -0
- data/.gemrelease +2 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +33 -0
- data/.github/dependabot.yml +10 -0
- data/.github/stale.yml +20 -0
- data/.gitignore +18 -0
- data/.rspec +3 -0
- data/.rubocop.yml +9 -0
- data/CODE_OF_CONDUCT.md +3 -0
- data/DEPLOYMENT.md +61 -0
- data/DEVELOPMENT.md +35 -0
- data/Dockerfile +5 -0
- data/EXAMPLES.md +195 -0
- data/Gemfile +21 -0
- data/Gemfile.lock +250 -0
- data/Guardfile +39 -0
- data/LICENSE +21 -0
- data/Makefile +5 -0
- data/README.md +88 -0
- data/RUBYGEM.md +9 -0
- data/Rakefile +33 -0
- data/codecov.yml +22 -0
- data/lib/minty/algorithm.rb +7 -0
- data/lib/minty/api/authentication_endpoints.rb +30 -0
- data/lib/minty/api/v2.rb +8 -0
- data/lib/minty/client.rb +7 -0
- data/lib/minty/exception.rb +50 -0
- data/lib/minty/mixins/api_token_struct.rb +4 -0
- data/lib/minty/mixins/headers.rb +19 -0
- data/lib/minty/mixins/httpproxy.rb +125 -0
- data/lib/minty/mixins/initializer.rb +38 -0
- data/lib/minty/mixins/validation.rb +113 -0
- data/lib/minty/mixins.rb +23 -0
- data/lib/minty/version.rb +5 -0
- data/lib/minty.rb +11 -0
- data/lib/minty_client.rb +4 -0
- data/minty.gemspec +39 -0
- data/publish_rubygem.sh +10 -0
- data/spec/integration/lib/minty/api/api_authentication_spec.rb +122 -0
- data/spec/integration/lib/minty/minty_client_spec.rb +92 -0
- data/spec/lib/minty/client_spec.rb +223 -0
- data/spec/lib/minty/mixins/httpproxy_spec.rb +658 -0
- data/spec/lib/minty/mixins/initializer_spec.rb +121 -0
- data/spec/lib/minty/mixins/token_management_spec.rb +129 -0
- data/spec/lib/minty/mixins/validation_spec.rb +559 -0
- data/spec/spec_helper.rb +75 -0
- data/spec/support/credentials.rb +14 -0
- data/spec/support/dummy_class.rb +20 -0
- data/spec/support/dummy_class_for_proxy.rb +6 -0
- data/spec/support/dummy_class_for_restclient.rb +4 -0
- data/spec/support/dummy_class_for_tokens.rb +18 -0
- data/spec/support/import_users.json +13 -0
- data/spec/support/stub_response.rb +3 -0
- metadata +366 -0
@@ -0,0 +1,658 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'spec_helper'
|
5
|
+
|
6
|
+
describe Minty::Mixins::HTTPProxy do
|
7
|
+
before :each do
|
8
|
+
dummy_instance = DummyClassForProxy.new
|
9
|
+
dummy_instance.extend(Minty::Mixins::HTTPProxy)
|
10
|
+
dummy_instance.base_uri = 'https://minty.page'
|
11
|
+
dummy_instance.retry_count = 0
|
12
|
+
|
13
|
+
@instance = dummy_instance
|
14
|
+
@exception = DummyClassForRestClient.new
|
15
|
+
end
|
16
|
+
|
17
|
+
%i[get delete].each do |http_method|
|
18
|
+
context ".#{http_method}" do
|
19
|
+
it { expect(@instance).to respond_to(http_method.to_sym) }
|
20
|
+
it "should call send http #{http_method} method to path defined through HTTP" do
|
21
|
+
expect(RestClient::Request).to receive(:execute).with(method: http_method,
|
22
|
+
url: 'https://minty.page/test',
|
23
|
+
timeout: nil,
|
24
|
+
headers: { params: {} },
|
25
|
+
payload: nil)
|
26
|
+
.and_return(StubResponse.new({}, true, 200))
|
27
|
+
expect { @instance.send(http_method, '/test') }.not_to raise_error
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should not raise exception if data returned not in json format (should be fixed in v2)' do
|
31
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
32
|
+
url: 'https://minty.page/test',
|
33
|
+
timeout: nil,
|
34
|
+
headers: { params: {} },
|
35
|
+
payload: nil)
|
36
|
+
.and_return(StubResponse.new('Some random text here', true, 200))
|
37
|
+
expect { @instance.send(http_method, '/test') }.not_to raise_error
|
38
|
+
expect(@instance.send(http_method, '/test')).to eql('Some random text here')
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should raise Minty::Unauthorized on send http #{http_method}
|
42
|
+
method to path defined through HTTP when 401 status received" do
|
43
|
+
expect(RestClient::Request).to receive(:execute).with(method: http_method,
|
44
|
+
url: 'https://minty.page/test',
|
45
|
+
timeout: nil,
|
46
|
+
headers: { params: {} },
|
47
|
+
payload: nil)
|
48
|
+
.and_return(StubResponse.new({}, false, 401))
|
49
|
+
expect { @instance.send(http_method, '/test') }.to raise_error(Minty::Unauthorized)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should raise Minty::NotFound on send http #{http_method} method
|
53
|
+
to path defined through HTTP when 404 status received" do
|
54
|
+
expect(RestClient::Request).to receive(:execute).with(method: http_method,
|
55
|
+
url: 'https://minty.page/test',
|
56
|
+
timeout: nil,
|
57
|
+
headers: { params: {} },
|
58
|
+
payload: nil)
|
59
|
+
.and_return(StubResponse.new({}, false, 404))
|
60
|
+
expect { @instance.send(http_method, '/test') }.to raise_error(Minty::NotFound)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should raise Minty::Unsupported on send http #{http_method} method
|
64
|
+
to path defined through HTTP when 418 or other unknown status received" do
|
65
|
+
expect(RestClient::Request).to receive(:execute).with(method: http_method,
|
66
|
+
url: 'https://minty.page/test',
|
67
|
+
timeout: nil,
|
68
|
+
headers: { params: {} },
|
69
|
+
payload: nil)
|
70
|
+
.and_return(StubResponse.new({}, false, 418))
|
71
|
+
expect { @instance.send(http_method, '/test') }.to raise_error(Minty::Unsupported)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should raise Minty::RequestTimeout on send http #{http_method} method
|
75
|
+
to path defined through HTTP when RestClient::RequestTimeout received" do
|
76
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
77
|
+
url: 'https://minty.page/test',
|
78
|
+
timeout: nil,
|
79
|
+
headers: { params: {} },
|
80
|
+
payload: nil)
|
81
|
+
.and_raise(RestClient::Exceptions::OpenTimeout.new)
|
82
|
+
expect { @instance.send(http_method, '/test') }.to raise_error(Minty::RequestTimeout)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should raise Minty::BadRequest on send http #{http_method} method
|
86
|
+
to path defined through HTTP when 400 status received" do
|
87
|
+
@exception.response = StubResponse.new({}, false, 400)
|
88
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
89
|
+
url: 'https://minty.page/test',
|
90
|
+
timeout: nil,
|
91
|
+
headers: { params: {} },
|
92
|
+
payload: nil)
|
93
|
+
.and_raise(@exception)
|
94
|
+
expect { @instance.send(http_method, '/test') }.to raise_error(Minty::BadRequest)
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should raise Minty::AccessDenied on send http #{http_method} method
|
98
|
+
to path defined through HTTP when 403" do
|
99
|
+
@exception.response = StubResponse.new({}, false, 403)
|
100
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
101
|
+
url: 'https://minty.page/test',
|
102
|
+
timeout: nil,
|
103
|
+
headers: { params: {} },
|
104
|
+
payload: nil)
|
105
|
+
.and_raise(@exception)
|
106
|
+
expect { @instance.send(http_method, '/test') }.to raise_error(Minty::AccessDenied)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should raise Minty::RateLimitEncountered on send http #{http_method} method
|
110
|
+
to path defined through HTTP when 429 recieved" do
|
111
|
+
headers = {
|
112
|
+
x_ratelimit_limit: 10,
|
113
|
+
x_ratelimit_remaining: 0,
|
114
|
+
x_ratelimit_reset: 1_560_564_149
|
115
|
+
}
|
116
|
+
@exception.response = StubResponse.new({}, false, 429, headers)
|
117
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
118
|
+
url: 'https://minty.page/test',
|
119
|
+
timeout: nil,
|
120
|
+
headers: { params: {} },
|
121
|
+
payload: nil)
|
122
|
+
.and_raise(@exception)
|
123
|
+
expect { @instance.send(http_method, '/test') }.to raise_error { |error|
|
124
|
+
expect(error).to be_a(Minty::RateLimitEncountered)
|
125
|
+
expect(error).to have_attributes(
|
126
|
+
error_data: {
|
127
|
+
headers: headers,
|
128
|
+
code: 429
|
129
|
+
},
|
130
|
+
headers: headers,
|
131
|
+
http_code: 429,
|
132
|
+
reset: Time.zone.at(1_560_564_149)
|
133
|
+
)
|
134
|
+
}
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should raise Minty::ServerError on send http #{http_method} method
|
138
|
+
to path defined through HTTP when 500 received" do
|
139
|
+
@exception.response = StubResponse.new({}, false, 500)
|
140
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
141
|
+
url: 'https://minty.page/test',
|
142
|
+
timeout: nil,
|
143
|
+
headers: { params: {} },
|
144
|
+
payload: nil)
|
145
|
+
.and_raise(@exception)
|
146
|
+
expect { @instance.send(http_method, '/test') }.to raise_error(Minty::ServerError)
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'should normalize path with Addressable::URI' do
|
150
|
+
expect(RestClient::Request).to receive(:execute).with(method: http_method,
|
151
|
+
url: 'https://minty.page/te%20st%23test',
|
152
|
+
timeout: nil,
|
153
|
+
headers: { params: {} },
|
154
|
+
payload: nil)
|
155
|
+
.and_return(StubResponse.new({}, true, 200))
|
156
|
+
expect { @instance.send(http_method, '/te st#test') }.not_to raise_error
|
157
|
+
end
|
158
|
+
|
159
|
+
context "when status 429 is recieved on send http #{http_method} method" do
|
160
|
+
it 'should retry 3 times when retry_count is not set' do
|
161
|
+
retry_instance = DummyClassForProxy.new
|
162
|
+
retry_instance.extend(Minty::Mixins::HTTPProxy)
|
163
|
+
retry_instance.base_uri = 'https://minty.page'
|
164
|
+
|
165
|
+
@exception.response = StubResponse.new({}, false, 429)
|
166
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
167
|
+
url: 'https://minty.page/test',
|
168
|
+
timeout: nil,
|
169
|
+
headers: { params: {} },
|
170
|
+
payload: nil)
|
171
|
+
.and_raise(@exception)
|
172
|
+
expect(RestClient::Request).to receive(:execute).exactly(4).times
|
173
|
+
|
174
|
+
expect { retry_instance.send(http_method, '/test') }.to raise_error { |error|
|
175
|
+
expect(error).to be_a(Minty::RateLimitEncountered)
|
176
|
+
}
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'should retry 2 times when retry_count is set to 2' do
|
180
|
+
retry_instance = DummyClassForProxy.new
|
181
|
+
retry_instance.extend(Minty::Mixins::HTTPProxy)
|
182
|
+
retry_instance.base_uri = 'https://minty.page'
|
183
|
+
retry_instance.retry_count = 2
|
184
|
+
|
185
|
+
@exception.response = StubResponse.new({}, false, 429)
|
186
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
187
|
+
url: 'https://minty.page/test',
|
188
|
+
timeout: nil,
|
189
|
+
headers: { params: {} },
|
190
|
+
payload: nil)
|
191
|
+
.and_raise(@exception)
|
192
|
+
expect(RestClient::Request).to receive(:execute).exactly(3).times
|
193
|
+
|
194
|
+
expect { retry_instance.send(http_method, '/test') }.to raise_error { |error|
|
195
|
+
expect(error).to be_a(Minty::RateLimitEncountered)
|
196
|
+
}
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'should not retry when retry_count is set to 0' do
|
200
|
+
retry_instance = DummyClassForProxy.new
|
201
|
+
retry_instance.extend(Minty::Mixins::HTTPProxy)
|
202
|
+
retry_instance.base_uri = 'https://minty.page'
|
203
|
+
retry_instance.retry_count = 0
|
204
|
+
|
205
|
+
@exception.response = StubResponse.new({}, false, 429)
|
206
|
+
|
207
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
208
|
+
url: 'https://minty.page/test',
|
209
|
+
timeout: nil,
|
210
|
+
headers: { params: {} },
|
211
|
+
payload: nil)
|
212
|
+
.and_raise(@exception)
|
213
|
+
|
214
|
+
expect(RestClient::Request).to receive(:execute).exactly(1).times
|
215
|
+
expect { retry_instance.send(http_method, '/test') }.to raise_error { |error|
|
216
|
+
expect(error).to be_a(Minty::RateLimitEncountered)
|
217
|
+
}
|
218
|
+
end
|
219
|
+
|
220
|
+
it 'should have have random retry times grow with jitter backoff' do
|
221
|
+
retry_instance = DummyClassForProxy.new
|
222
|
+
retry_instance.extend(Minty::Mixins::HTTPProxy)
|
223
|
+
retry_instance.base_uri = 'https://minty.page'
|
224
|
+
retry_instance.retry_count = 2
|
225
|
+
time_entries = []
|
226
|
+
@time_start
|
227
|
+
|
228
|
+
@exception.response = StubResponse.new({}, false, 429)
|
229
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
230
|
+
url: 'https://minty.page/test',
|
231
|
+
timeout: nil,
|
232
|
+
headers: { params: {} },
|
233
|
+
payload: nil) do
|
234
|
+
time_entries.push(Time.now.to_f - @time_start.to_f)
|
235
|
+
@time_start = Time.now.to_f # restart the clock
|
236
|
+
raise @exception
|
237
|
+
end
|
238
|
+
|
239
|
+
@time_start = Time.now.to_f # start the clock
|
240
|
+
begin
|
241
|
+
retry_instance.send(http_method, '/test')
|
242
|
+
rescue StandardError
|
243
|
+
nil
|
244
|
+
end
|
245
|
+
time_entries_first_set = time_entries.shift(time_entries.length)
|
246
|
+
|
247
|
+
begin
|
248
|
+
retry_instance.send(http_method, '/test')
|
249
|
+
rescue StandardError
|
250
|
+
nil
|
251
|
+
end
|
252
|
+
time_entries.each_with_index do |entry, index|
|
253
|
+
expect(entry != time_entries_first_set[index]) if index > 0 # skip the first request
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
%i[post put patch].each do |http_method|
|
261
|
+
context ".#{http_method}" do
|
262
|
+
it { expect(@instance).to respond_to(http_method.to_sym) }
|
263
|
+
it "should call send http #{http_method} method to path defined through HTTP" do
|
264
|
+
expect(RestClient::Request).to receive(:execute).with(method: http_method,
|
265
|
+
url: 'https://minty.page/test',
|
266
|
+
timeout: nil,
|
267
|
+
headers: nil,
|
268
|
+
payload: '{}')
|
269
|
+
.and_return(StubResponse.new({}, true, 200))
|
270
|
+
expect { @instance.send(http_method, '/test') }.not_to raise_error
|
271
|
+
end
|
272
|
+
|
273
|
+
it 'should not raise exception if data returned not in json format (should be fixed in v2)' do
|
274
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
275
|
+
url: 'https://minty.page/test',
|
276
|
+
timeout: nil,
|
277
|
+
headers: nil,
|
278
|
+
payload: '{}')
|
279
|
+
.and_return(StubResponse.new('Some random text here', true, 200))
|
280
|
+
expect { @instance.send(http_method, '/test') }.not_to raise_error
|
281
|
+
expect(@instance.send(http_method, '/test')).to eql('Some random text here')
|
282
|
+
end
|
283
|
+
|
284
|
+
it "should raise Minty::Unauthorized on send http #{http_method} method
|
285
|
+
to path defined through HTTP when 401 status received" do
|
286
|
+
@exception.response = StubResponse.new({}, false, 401)
|
287
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
288
|
+
url: 'https://minty.page/test',
|
289
|
+
timeout: nil,
|
290
|
+
headers: nil,
|
291
|
+
payload: '{}')
|
292
|
+
.and_raise(@exception)
|
293
|
+
expect { @instance.send(http_method, '/test') }.to raise_error(Minty::Unauthorized)
|
294
|
+
end
|
295
|
+
|
296
|
+
it "should raise Minty::RateLimitEncountered on send http #{http_method} method
|
297
|
+
to path defined through HTTP when 429 status received" do
|
298
|
+
headers = {
|
299
|
+
x_ratelimit_limit: 10,
|
300
|
+
x_ratelimit_remaining: 0,
|
301
|
+
x_ratelimit_reset: 1_560_564_149
|
302
|
+
}
|
303
|
+
@exception.response = StubResponse.new({}, false, 429, headers)
|
304
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
305
|
+
url: 'https://minty.page/test',
|
306
|
+
timeout: nil,
|
307
|
+
headers: nil,
|
308
|
+
payload: '{}')
|
309
|
+
.and_raise(@exception)
|
310
|
+
expect { @instance.send(http_method, '/test') }.to raise_error { |error|
|
311
|
+
expect(error).to be_a(Minty::RateLimitEncountered)
|
312
|
+
expect(error).to have_attributes(
|
313
|
+
error_data: {
|
314
|
+
headers: headers,
|
315
|
+
code: 429
|
316
|
+
},
|
317
|
+
headers: headers,
|
318
|
+
http_code: 429,
|
319
|
+
reset: Time.zone.at(1_560_564_149)
|
320
|
+
)
|
321
|
+
}
|
322
|
+
end
|
323
|
+
|
324
|
+
it "should raise Minty::NotFound on send http #{http_method} method
|
325
|
+
to path defined through HTTP when 404 status received" do
|
326
|
+
@exception.response = StubResponse.new({}, false, 404)
|
327
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
328
|
+
url: 'https://minty.page/test',
|
329
|
+
timeout: nil,
|
330
|
+
headers: nil,
|
331
|
+
payload: '{}')
|
332
|
+
.and_raise(@exception)
|
333
|
+
expect { @instance.send(http_method, '/test') }.to raise_error(Minty::NotFound)
|
334
|
+
end
|
335
|
+
|
336
|
+
it "should raise Minty::Unsupported on send http #{http_method} method
|
337
|
+
to path defined through HTTP when 418 or other unknown status received" do
|
338
|
+
@exception.response = StubResponse.new({}, false, 418)
|
339
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
340
|
+
url: 'https://minty.page/test',
|
341
|
+
timeout: nil,
|
342
|
+
headers: nil,
|
343
|
+
payload: '{}')
|
344
|
+
.and_raise(@exception)
|
345
|
+
expect { @instance.send(http_method, '/test') }.to raise_error(Minty::Unsupported)
|
346
|
+
end
|
347
|
+
|
348
|
+
it "should raise Minty::RequestTimeout on send http #{http_method} method
|
349
|
+
to path defined through HTTP when RestClient::RequestTimeout received" do
|
350
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
351
|
+
url: 'https://minty.page/test',
|
352
|
+
timeout: nil,
|
353
|
+
headers: nil,
|
354
|
+
payload: '{}')
|
355
|
+
.and_raise(RestClient::Exceptions::OpenTimeout.new)
|
356
|
+
expect { @instance.send(http_method, '/test') }.to raise_error(Minty::RequestTimeout)
|
357
|
+
end
|
358
|
+
|
359
|
+
it "should raise Minty::BadRequest on send http #{http_method} method
|
360
|
+
to path defined through HTTP when 400 status received" do
|
361
|
+
@exception.response = StubResponse.new({}, false, 400)
|
362
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
363
|
+
url: 'https://minty.page/test',
|
364
|
+
timeout: nil,
|
365
|
+
headers: nil,
|
366
|
+
payload: '{}')
|
367
|
+
.and_raise(@exception)
|
368
|
+
expect { @instance.send(http_method, '/test') }.to raise_error(Minty::BadRequest)
|
369
|
+
end
|
370
|
+
|
371
|
+
it "should raise Minty::ServerError on send http #{http_method} method
|
372
|
+
to path defined through HTTP when 500 received" do
|
373
|
+
@exception.response = StubResponse.new({}, false, 500)
|
374
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method, url: 'https://minty.page/test',
|
375
|
+
timeout: nil,
|
376
|
+
headers: nil,
|
377
|
+
payload: '{}')
|
378
|
+
.and_raise(@exception)
|
379
|
+
expect { @instance.send(http_method, '/test') }.to raise_error(Minty::ServerError)
|
380
|
+
end
|
381
|
+
|
382
|
+
it 'should normalize path with Addressable::URI' do
|
383
|
+
expect(RestClient::Request).to receive(:execute).with(method: http_method,
|
384
|
+
url: 'https://minty.page/te%20st',
|
385
|
+
timeout: nil,
|
386
|
+
headers: nil,
|
387
|
+
payload: '{}')
|
388
|
+
.and_return(StubResponse.new({}, true, 200))
|
389
|
+
expect { @instance.send(http_method, '/te st') }.not_to raise_error
|
390
|
+
end
|
391
|
+
|
392
|
+
it 'should give the JSON representation of the error as the error message' do
|
393
|
+
res = JSON.generate('statusCode' => 404,
|
394
|
+
'error' => 'Bad Request',
|
395
|
+
'message' => "Path validation error: 'String does not match pattern ^.+\\|.+$:
|
396
|
+
3241312' on property id (The user_id of the user to retrieve).",
|
397
|
+
'errorCode' => 'invalid_uri')
|
398
|
+
expect(RestClient::Request).to receive(:execute).with(method: http_method,
|
399
|
+
url: 'https://minty.page/test',
|
400
|
+
timeout: nil,
|
401
|
+
headers: nil,
|
402
|
+
payload: '{}')
|
403
|
+
.and_return(StubResponse.new(res, true, 404))
|
404
|
+
expect { @instance.send(http_method, '/test') }.to raise_error(Minty::NotFound, res)
|
405
|
+
end
|
406
|
+
|
407
|
+
context "when status 429 is recieved on send http #{http_method} method" do
|
408
|
+
it 'should retry 3 times when retry_count is not set' do
|
409
|
+
retry_instance = DummyClassForProxy.new
|
410
|
+
retry_instance.extend(Minty::Mixins::HTTPProxy)
|
411
|
+
retry_instance.base_uri = 'https://minty.page'
|
412
|
+
|
413
|
+
@exception.response = StubResponse.new({}, false, 429)
|
414
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
415
|
+
url: 'https://minty.page/test',
|
416
|
+
timeout: nil,
|
417
|
+
headers: nil,
|
418
|
+
payload: '{}')
|
419
|
+
.and_raise(@exception)
|
420
|
+
expect(RestClient::Request).to receive(:execute).exactly(4).times
|
421
|
+
|
422
|
+
expect { retry_instance.send(http_method, '/test') }.to raise_error { |error|
|
423
|
+
expect(error).to be_a(Minty::RateLimitEncountered)
|
424
|
+
}
|
425
|
+
end
|
426
|
+
|
427
|
+
it 'should retry 2 times when retry_count is set to 2' do
|
428
|
+
retry_instance = DummyClassForProxy.new
|
429
|
+
retry_instance.extend(Minty::Mixins::HTTPProxy)
|
430
|
+
retry_instance.base_uri = 'https://minty.page'
|
431
|
+
retry_instance.retry_count = 2
|
432
|
+
|
433
|
+
@exception.response = StubResponse.new({}, false, 429)
|
434
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
435
|
+
url: 'https://minty.page/test',
|
436
|
+
timeout: nil,
|
437
|
+
headers: nil,
|
438
|
+
payload: '{}')
|
439
|
+
.and_raise(@exception)
|
440
|
+
expect(RestClient::Request).to receive(:execute).exactly(3).times
|
441
|
+
|
442
|
+
expect { retry_instance.send(http_method, '/test') }.to raise_error { |error|
|
443
|
+
expect(error).to be_a(Minty::RateLimitEncountered)
|
444
|
+
}
|
445
|
+
end
|
446
|
+
|
447
|
+
it 'should not retry when retry_count is set to 0' do
|
448
|
+
retry_instance = DummyClassForProxy.new
|
449
|
+
retry_instance.extend(Minty::Mixins::HTTPProxy)
|
450
|
+
retry_instance.base_uri = 'https://minty.page'
|
451
|
+
retry_instance.retry_count = 0
|
452
|
+
|
453
|
+
@exception.response = StubResponse.new({}, false, 429)
|
454
|
+
|
455
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
456
|
+
url: 'https://minty.page/test',
|
457
|
+
timeout: nil,
|
458
|
+
headers: nil,
|
459
|
+
payload: '{}')
|
460
|
+
.and_raise(@exception)
|
461
|
+
|
462
|
+
expect(RestClient::Request).to receive(:execute).exactly(1).times
|
463
|
+
expect { retry_instance.send(http_method, '/test') }.to raise_error { |error|
|
464
|
+
expect(error).to be_a(Minty::RateLimitEncountered)
|
465
|
+
}
|
466
|
+
end
|
467
|
+
|
468
|
+
it 'should have have random retry times grow with jitter backoff' do
|
469
|
+
retry_instance = DummyClassForProxy.new
|
470
|
+
retry_instance.extend(Minty::Mixins::HTTPProxy)
|
471
|
+
retry_instance.base_uri = 'https://minty.page'
|
472
|
+
retry_instance.retry_count = 2
|
473
|
+
time_entries = []
|
474
|
+
@time_start
|
475
|
+
|
476
|
+
@exception.response = StubResponse.new({}, false, 429)
|
477
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
478
|
+
url: 'https://minty.page/test',
|
479
|
+
timeout: nil,
|
480
|
+
headers: nil,
|
481
|
+
payload: '{}') do
|
482
|
+
time_entries.push(Time.now.to_f - @time_start.to_f)
|
483
|
+
@time_start = Time.now.to_f # restart the clock
|
484
|
+
raise @exception
|
485
|
+
end
|
486
|
+
|
487
|
+
@time_start = Time.now.to_f # start the clock
|
488
|
+
begin
|
489
|
+
retry_instance.send(http_method, '/test')
|
490
|
+
rescue StandardError
|
491
|
+
nil
|
492
|
+
end
|
493
|
+
time_entries_first_set = time_entries.shift(time_entries.length)
|
494
|
+
|
495
|
+
begin
|
496
|
+
retry_instance.send(http_method, '/test')
|
497
|
+
rescue StandardError
|
498
|
+
nil
|
499
|
+
end
|
500
|
+
time_entries.each_with_index do |entry, index|
|
501
|
+
expect(entry != time_entries_first_set[index]) if index > 0 # skip the first request
|
502
|
+
end
|
503
|
+
end
|
504
|
+
end
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
context 'Renewing tokens' do
|
509
|
+
let(:httpproxy_instance) do
|
510
|
+
DummyClassForTokens.new(
|
511
|
+
client_id: 'test-client-id',
|
512
|
+
client_secret: 'test-client-secret',
|
513
|
+
domain: 'minty.page'
|
514
|
+
)
|
515
|
+
end
|
516
|
+
|
517
|
+
%i[get delete].each do |http_method|
|
518
|
+
context "for #{http_method}" do
|
519
|
+
it 'should renew the token' do
|
520
|
+
expect(RestClient::Request).to receive(:execute).with(hash_including(
|
521
|
+
method: :post,
|
522
|
+
url: 'https://minty.page/oauth/token'
|
523
|
+
)).and_return(StubResponse.new({
|
524
|
+
'access_token' => 'access_token',
|
525
|
+
'expires_in' => 86_400
|
526
|
+
},
|
527
|
+
true,
|
528
|
+
200))
|
529
|
+
|
530
|
+
expect(RestClient::Request).to receive(:execute).with(hash_including(
|
531
|
+
method: http_method,
|
532
|
+
url: 'https://minty.page/test'
|
533
|
+
)).and_return(StubResponse.new('Some random text here',
|
534
|
+
true, 200))
|
535
|
+
|
536
|
+
expect { httpproxy_instance.send(http_method, '/test') }.not_to raise_error
|
537
|
+
end
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
%i[post put patch].each do |http_method|
|
542
|
+
context "for #{http_method}" do
|
543
|
+
it 'should renew the token' do
|
544
|
+
expect(RestClient::Request).to receive(:execute).with(hash_including(
|
545
|
+
method: :post,
|
546
|
+
url: 'https://minty.page/oauth/token'
|
547
|
+
)).and_return(StubResponse.new({
|
548
|
+
'access_token' => 'access_token',
|
549
|
+
'expires_in' => 86_400
|
550
|
+
},
|
551
|
+
true,
|
552
|
+
200))
|
553
|
+
|
554
|
+
expect(RestClient::Request).to receive(:execute).with(hash_including(
|
555
|
+
method: http_method,
|
556
|
+
url: 'https://minty.page/test',
|
557
|
+
headers: hash_including('Authorization' => 'Bearer access_token')
|
558
|
+
)).and_return(StubResponse.new('Some random text here',
|
559
|
+
true, 200))
|
560
|
+
|
561
|
+
expect { httpproxy_instance.send(http_method, '/test') }.not_to raise_error
|
562
|
+
end
|
563
|
+
end
|
564
|
+
end
|
565
|
+
end
|
566
|
+
|
567
|
+
context 'Using cached tokens' do
|
568
|
+
let(:httpproxy_instance) do
|
569
|
+
DummyClassForTokens.new(
|
570
|
+
client_id: 'test-client-id',
|
571
|
+
client_secret: 'test-client-secret',
|
572
|
+
domain: 'minty.page',
|
573
|
+
token: 'access_token',
|
574
|
+
token_expires_at: Time.now.to_i + 86_400
|
575
|
+
)
|
576
|
+
end
|
577
|
+
|
578
|
+
%i[get delete].each do |http_method|
|
579
|
+
context "for #{http_method}" do
|
580
|
+
it 'should use the cached token' do
|
581
|
+
expect(RestClient::Request).not_to receive(:execute).with(hash_including(
|
582
|
+
method: :post,
|
583
|
+
url: 'https://minty.page/oauth/token'
|
584
|
+
))
|
585
|
+
|
586
|
+
expect(RestClient::Request).to receive(:execute).with(hash_including(
|
587
|
+
method: http_method,
|
588
|
+
url: 'https://minty.page/test',
|
589
|
+
headers: hash_including(params: {},
|
590
|
+
'Authorization' => 'Bearer access_token')
|
591
|
+
)).and_return(StubResponse.new('Some random text here',
|
592
|
+
true, 200))
|
593
|
+
|
594
|
+
expect { httpproxy_instance.send(http_method, '/test') }.not_to raise_error
|
595
|
+
end
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
599
|
+
%i[post put patch].each do |http_method|
|
600
|
+
context "for #{http_method}" do
|
601
|
+
it 'should use the cached token' do
|
602
|
+
expect(RestClient::Request).not_to receive(:execute).with(hash_including(
|
603
|
+
method: :post,
|
604
|
+
url: 'https://minty.page/oauth/token'
|
605
|
+
))
|
606
|
+
|
607
|
+
expect(RestClient::Request).to receive(:execute).with(hash_including(
|
608
|
+
method: http_method,
|
609
|
+
url: 'https://minty.page/test',
|
610
|
+
headers: hash_including('Authorization' => 'Bearer access_token')
|
611
|
+
)).and_return(StubResponse.new('Some random text here',
|
612
|
+
true, 200))
|
613
|
+
|
614
|
+
expect { httpproxy_instance.send(http_method, '/test') }.not_to raise_error
|
615
|
+
end
|
616
|
+
end
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
620
|
+
context 'Normal operation' do
|
621
|
+
let(:httpproxy_instance) do
|
622
|
+
DummyClassForTokens.new(
|
623
|
+
client_id: 'test-client-id',
|
624
|
+
client_secret: 'test-client-secret',
|
625
|
+
domain: 'minty.page',
|
626
|
+
token: 'access_token',
|
627
|
+
token_expires_at: Time.now.to_i + 86_400
|
628
|
+
)
|
629
|
+
end
|
630
|
+
|
631
|
+
# This sets up a test matrix to verify that both :get and :delete calls (the only two HTTP methods in the proxy that mutated headers)
|
632
|
+
# don't bleed query params into subsequent calls to :post :patch and :put.
|
633
|
+
%i[get delete].each do |http_get_delete|
|
634
|
+
%i[post patch put].each do |http_ppp|
|
635
|
+
it "should not bleed :#{http_get_delete} headers/parameters to the subsequent :#{http_ppp} request" do
|
636
|
+
expect(RestClient::Request).to receive(:execute).with(hash_including(
|
637
|
+
method: http_get_delete,
|
638
|
+
url: "https://minty.page/test-#{http_get_delete}",
|
639
|
+
headers: hash_including(params: { email: 'test@test.com' })
|
640
|
+
)).and_return(StubResponse.new('OK', true, 200))
|
641
|
+
|
642
|
+
# email: parameter that is sent in the GET request should not appear
|
643
|
+
# as a parameter in the `headers` hash for the subsequent PATCH request.
|
644
|
+
expect(RestClient::Request).to receive(:execute).with(hash_including(
|
645
|
+
method: http_ppp,
|
646
|
+
url: "https://minty.page/test-#{http_ppp}",
|
647
|
+
headers: hash_not_including(:params)
|
648
|
+
)).and_return(StubResponse.new('OK', true, 200))
|
649
|
+
|
650
|
+
expect do
|
651
|
+
httpproxy_instance.send(http_get_delete, "/test-#{http_get_delete}", { email: 'test@test.com' })
|
652
|
+
end.not_to raise_error
|
653
|
+
expect { httpproxy_instance.send(http_ppp, "/test-#{http_ppp}") }.not_to raise_error
|
654
|
+
end
|
655
|
+
end
|
656
|
+
end
|
657
|
+
end
|
658
|
+
end
|