stripe 5.2.0 → 5.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0ec1d1414aafc87b63dca172263ecd4666467cda2ab39eaac1abdbd13e7cd90b
4
- data.tar.gz: 3adde22d80c8f867e34d2735dd0aea142f0b69e6cd2a84e13fe7c6a53655ee85
3
+ metadata.gz: b717ef70627c0f7467317a2e2f08e8d553b04411b6324df41f4cc5eac6bd6800
4
+ data.tar.gz: 26a05a4ceeb4412c95c2ad12776785c72bd291841c5843eba2ec03a6dc21a722
5
5
  SHA512:
6
- metadata.gz: e4086cfa6bc5281bf1e31e88a2435156d6928716d7022e319afe5845157571731ad260c3728c6bf021d593f11727fa6fdba06c26667d7d5774b6a29cfadf9829
7
- data.tar.gz: 4b6477d988e90607c8ad8c1d8a4a11e3f0afb3494948403d2b6b9e23cde4a6be0dcb9779100306c28449e6d7d24876043edcc2239ab00cdb1dc7fd88942b57de
6
+ metadata.gz: ef1b962195d8a8510a1d360d8fbd18a246c65363877b43a451bccd1340c1ea18008581c04c4a0e62eb6eed9f2f8afebfccf9c4f5d059b08a2ccd921cae5b5cd0
7
+ data.tar.gz: 98fb62f907a16aadb4cab5564053ce85e96931193a8e1eeccb8ef10ebcfa726ddf086e51aef09db0825c685129867d5a91716addcc2252ae771869ca4d83bc8a
data/.rubocop.yml CHANGED
@@ -4,6 +4,12 @@ AllCops:
4
4
  DisplayCopNames: true
5
5
  TargetRubyVersion: 2.3
6
6
 
7
+ Exclude:
8
+ # brandur: Exclude ephmeral script-like files that I use to try and
9
+ # reproduce problems with the library. If you know of a better way of doing
10
+ # this (e.g. exclude files not tracked by Git), feel free to change it.
11
+ - "example_*"
12
+
7
13
  Layout/CaseIndentation:
8
14
  EnforcedStyle: end
9
15
 
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.6.0 - 2019-10-04
4
+ * [#861](https://github.com/stripe/stripe-ruby/pull/861) Nicer error when specifying non-nil non-string opt value
5
+
6
+ ## 5.5.0 - 2019-10-03
7
+ * [#859](https://github.com/stripe/stripe-ruby/pull/859) User-friendly messages and retries for `EOFError`, `Errno::ECONNRESET`, `Errno::ETIMEDOUT`, and `Errno::EHOSTUNREACH` network errors
8
+
9
+ ## 5.4.1 - 2019-10-01
10
+ * [#858](https://github.com/stripe/stripe-ruby/pull/858) Drop Timecop dependency
11
+
12
+ ## 5.4.0 - 2019-10-01
13
+ * [#857](https://github.com/stripe/stripe-ruby/pull/857) Move to monotonic time for duration calculations
14
+
15
+ ## 5.3.0 - 2019-10-01
16
+ * [#853](https://github.com/stripe/stripe-ruby/pull/853) Support `Stripe-Should-Retry` header
17
+
3
18
  ## 5.2.0 - 2019-09-19
4
19
  * [#851](https://github.com/stripe/stripe-ruby/pull/851) Introduce system for garbage collecting connection managers
5
20
 
data/Gemfile CHANGED
@@ -11,7 +11,6 @@ group :development do
11
11
  gem "rake"
12
12
  gem "shoulda-context"
13
13
  gem "test-unit"
14
- gem "timecop"
15
14
  gem "webmock"
16
15
 
17
16
  # Rubocop changes pretty quickly: new cops get added and old cops change
data/VERSION CHANGED
@@ -1 +1 @@
1
- 5.2.0
1
+ 5.6.0
@@ -6,7 +6,7 @@ module Stripe
6
6
  # that it's possible to do so from a static context (i.e. without a
7
7
  # pre-existing collection of subresources on the parent).
8
8
  #
9
- # For examle, a transfer gains the static methods for reversals so that the
9
+ # For example, a transfer gains the static methods for reversals so that the
10
10
  # methods `.create_reversal`, `.retrieve_reversal`, `.update_reversal`,
11
11
  # etc. all become available.
12
12
  module NestedResource
@@ -14,9 +14,11 @@ module Stripe
14
14
  resource_plural: nil)
15
15
  resource_plural ||= "#{resource}s"
16
16
  path ||= resource_plural
17
+
17
18
  raise ArgumentError, "operations array required" if operations.nil?
18
19
 
19
20
  resource_url_method = :"#{resource}s_url"
21
+
20
22
  define_singleton_method(resource_url_method) do |id, nested_id = nil|
21
23
  url = "#{resource_url}/#{CGI.escape(id)}/#{CGI.escape(path)}"
22
24
  url += "/#{CGI.escape(nested_id)}" unless nested_id.nil?
@@ -27,39 +29,39 @@ module Stripe
27
29
  case operation
28
30
  when :create
29
31
  define_singleton_method(:"create_#{resource}") \
30
- do |id, params = {}, opts = {}|
31
- url = send(resource_url_method, id)
32
- resp, opts = request(:post, url, params, opts)
33
- Util.convert_to_stripe_object(resp.data, opts)
34
- end
32
+ do |id, params = {}, opts = {}|
33
+ url = send(resource_url_method, id)
34
+ resp, opts = request(:post, url, params, opts)
35
+ Util.convert_to_stripe_object(resp.data, opts)
36
+ end
35
37
  when :retrieve
36
38
  define_singleton_method(:"retrieve_#{resource}") \
37
- do |id, nested_id, opts = {}|
38
- url = send(resource_url_method, id, nested_id)
39
- resp, opts = request(:get, url, {}, opts)
40
- Util.convert_to_stripe_object(resp.data, opts)
41
- end
39
+ do |id, nested_id, opts = {}|
40
+ url = send(resource_url_method, id, nested_id)
41
+ resp, opts = request(:get, url, {}, opts)
42
+ Util.convert_to_stripe_object(resp.data, opts)
43
+ end
42
44
  when :update
43
45
  define_singleton_method(:"update_#{resource}") \
44
- do |id, nested_id, params = {}, opts = {}|
45
- url = send(resource_url_method, id, nested_id)
46
- resp, opts = request(:post, url, params, opts)
47
- Util.convert_to_stripe_object(resp.data, opts)
48
- end
46
+ do |id, nested_id, params = {}, opts = {}|
47
+ url = send(resource_url_method, id, nested_id)
48
+ resp, opts = request(:post, url, params, opts)
49
+ Util.convert_to_stripe_object(resp.data, opts)
50
+ end
49
51
  when :delete
50
52
  define_singleton_method(:"delete_#{resource}") \
51
- do |id, nested_id, params = {}, opts = {}|
52
- url = send(resource_url_method, id, nested_id)
53
- resp, opts = request(:delete, url, params, opts)
54
- Util.convert_to_stripe_object(resp.data, opts)
55
- end
53
+ do |id, nested_id, params = {}, opts = {}|
54
+ url = send(resource_url_method, id, nested_id)
55
+ resp, opts = request(:delete, url, params, opts)
56
+ Util.convert_to_stripe_object(resp.data, opts)
57
+ end
56
58
  when :list
57
59
  define_singleton_method(:"list_#{resource_plural}") \
58
- do |id, params = {}, opts = {}|
59
- url = send(resource_url_method, id)
60
- resp, opts = request(:get, url, params, opts)
61
- Util.convert_to_stripe_object(resp.data, opts)
62
- end
60
+ do |id, params = {}, opts = {}|
61
+ url = send(resource_url_method, id)
62
+ resp, opts = request(:get, url, params, opts)
63
+ Util.convert_to_stripe_object(resp.data, opts)
64
+ end
63
65
  else
64
66
  raise ArgumentError, "Unknown operation: #{operation.inspect}"
65
67
  end
@@ -8,6 +8,8 @@ module Stripe
8
8
  warn_on_opts_in_params(params)
9
9
 
10
10
  opts = Util.normalize_opts(opts)
11
+ error_on_non_string_user_opts(opts)
12
+
11
13
  opts[:client] ||= StripeClient.active_client
12
14
 
13
15
  headers = opts.clone
@@ -31,10 +33,24 @@ module Stripe
31
33
  [resp, opts_to_persist]
32
34
  end
33
35
 
36
+ private def error_on_non_string_user_opts(opts)
37
+ Util::OPTS_USER_SPECIFIED.each do |opt|
38
+ next unless opts.key?(opt)
39
+
40
+ val = opts[opt]
41
+ next if val.nil?
42
+ next if val.is_a?(String)
43
+
44
+ raise ArgumentError,
45
+ "request option '#{opt}' should be a string value " \
46
+ "(was a #{val.class})"
47
+ end
48
+ end
49
+
34
50
  private def warn_on_opts_in_params(params)
35
51
  Util::OPTS_USER_SPECIFIED.each do |opt|
36
52
  if params.key?(opt)
37
- warn("WARNING: #{opt} should be in opts instead of params.")
53
+ warn("WARNING: '#{opt}' should be in opts instead of params.")
38
54
  end
39
55
  end
40
56
  end
@@ -10,14 +10,15 @@ module Stripe
10
10
  # Note that this class in itself is *not* thread safe. We expect it to be
11
11
  # instantiated once per thread.
12
12
  class ConnectionManager
13
- # Timestamp indicating when the connection manager last made a request.
14
- # This is used by `StripeClient` to determine whether a connection manager
15
- # should be garbage collected or not.
13
+ # Timestamp (in seconds procured from the system's monotonic clock)
14
+ # indicating when the connection manager last made a request. This is used
15
+ # by `StripeClient` to determine whether a connection manager should be
16
+ # garbage collected or not.
16
17
  attr_reader :last_used
17
18
 
18
19
  def initialize
19
20
  @active_connections = {}
20
- @last_used = Time.now
21
+ @last_used = Util.monotonic_time
21
22
 
22
23
  # A connection manager may be accessed across threads as one thread makes
23
24
  # requests on it while another is trying to clear it (either because it's
@@ -78,7 +79,7 @@ module Stripe
78
79
  raise ArgumentError, "query should be a string" \
79
80
  if query && !query.is_a?(String)
80
81
 
81
- @last_used = Time.now
82
+ @last_used = Util.monotonic_time
82
83
 
83
84
  connection = connection_for(uri)
84
85
 
@@ -9,7 +9,7 @@ module Stripe
9
9
  # synchronize global access to them.
10
10
  @thread_contexts_with_connection_managers = []
11
11
  @thread_contexts_with_connection_managers_mutex = Mutex.new
12
- @last_connection_manager_gc = Time.now
12
+ @last_connection_manager_gc = Util.monotonic_time
13
13
 
14
14
  # Initializes a new `StripeClient`.
15
15
  #
@@ -81,17 +81,23 @@ module Stripe
81
81
  def self.should_retry?(error, method:, num_retries:)
82
82
  return false if num_retries >= Stripe.max_network_retries
83
83
 
84
- # Retry on timeout-related problems (either on open or read).
85
- return true if error.is_a?(Net::OpenTimeout)
86
- return true if error.is_a?(Net::ReadTimeout)
84
+ case error
85
+ when Net::OpenTimeout, Net::ReadTimeout
86
+ # Retry on timeout-related problems (either on open or read).
87
+ true
88
+ when EOFError, Errno::ECONNREFUSED, Errno::ECONNRESET,
89
+ Errno::EHOSTUNREACH, Errno::ETIMEDOUT, SocketError
90
+ # Destination refused the connection, the connection was reset, or a
91
+ # variety of other connection failures. This could occur from a single
92
+ # saturated server, so retry in case it's intermittent.
93
+ true
94
+ when Stripe::StripeError
95
+ # The API may ask us not to retry (e.g. if doing so would be a no-op),
96
+ # or advise us to retry (e.g. in cases of lock timeouts). Defer to
97
+ # those instructions if given.
98
+ return false if error.http_headers["stripe-should-retry"] == "false"
99
+ return true if error.http_headers["stripe-should-retry"] == "true"
87
100
 
88
- # Destination refused the connection, the connection was reset, or a
89
- # variety of other connection failures. This could occur from a single
90
- # saturated server, so retry in case it's intermittent.
91
- return true if error.is_a?(Errno::ECONNREFUSED)
92
- return true if error.is_a?(SocketError)
93
-
94
- if error.is_a?(Stripe::StripeError)
95
101
  # 409 Conflict
96
102
  return true if error.http_status == 409
97
103
 
@@ -113,10 +119,10 @@ module Stripe
113
119
  return true if error.http_status == 500 && method != :post
114
120
 
115
121
  # 503 Service Unavailable
116
- return true if error.http_status == 503
122
+ error.http_status == 503
123
+ else
124
+ false
117
125
  end
118
-
119
- false
120
126
  end
121
127
 
122
128
  def self.sleep_time(num_retries)
@@ -290,7 +296,11 @@ module Stripe
290
296
  # The original error message is also appended onto the final exception for
291
297
  # full transparency.
292
298
  NETWORK_ERROR_MESSAGES_MAP = {
299
+ EOFError => ERROR_MESSAGE_CONNECTION,
293
300
  Errno::ECONNREFUSED => ERROR_MESSAGE_CONNECTION,
301
+ Errno::ECONNRESET => ERROR_MESSAGE_CONNECTION,
302
+ Errno::EHOSTUNREACH => ERROR_MESSAGE_CONNECTION,
303
+ Errno::ETIMEDOUT => ERROR_MESSAGE_TIMEOUT_CONNECT,
294
304
  SocketError => ERROR_MESSAGE_CONNECTION,
295
305
 
296
306
  Net::OpenTimeout => ERROR_MESSAGE_TIMEOUT_CONNECT,
@@ -362,11 +372,11 @@ module Stripe
362
372
  # For internal use only. Does not provide a stable API and may be broken
363
373
  # with future non-major changes.
364
374
  def self.maybe_gc_connection_managers
365
- if @last_connection_manager_gc + CONNECTION_MANAGER_GC_PERIOD > Time.now
366
- return nil
367
- end
375
+ next_gc_time = @last_connection_manager_gc + CONNECTION_MANAGER_GC_PERIOD
376
+ return nil if next_gc_time > Util.monotonic_time
368
377
 
369
- last_used_threshold = Time.now - CONNECTION_MANAGER_GC_LAST_USED_EXPIRY
378
+ last_used_threshold =
379
+ Util.monotonic_time - CONNECTION_MANAGER_GC_LAST_USED_EXPIRY
370
380
 
371
381
  pruned_thread_contexts = []
372
382
  @thread_contexts_with_connection_managers.each do |thread_context|
@@ -379,7 +389,7 @@ module Stripe
379
389
  end
380
390
 
381
391
  @thread_contexts_with_connection_managers -= pruned_thread_contexts
382
- @last_connection_manager_gc = Time.now
392
+ @last_connection_manager_gc = Util.monotonic_time
383
393
 
384
394
  pruned_thread_contexts.count
385
395
  end
@@ -439,7 +449,7 @@ module Stripe
439
449
  private def execute_request_with_rescues(method, api_base, context)
440
450
  num_retries = 0
441
451
  begin
442
- request_start = Time.now
452
+ request_start = Util.monotonic_time
443
453
  log_request(context, num_retries)
444
454
  resp = yield
445
455
  context = context.dup_from_response_headers(resp)
@@ -449,7 +459,8 @@ module Stripe
449
459
  log_response(context, request_start, resp.code.to_i, resp.body)
450
460
 
451
461
  if Stripe.enable_telemetry? && context.request_id
452
- request_duration_ms = ((Time.now - request_start) * 1000).to_int
462
+ request_duration_ms =
463
+ ((Util.monotonic_time - request_start) * 1000).to_int
453
464
  @last_request_metrics =
454
465
  StripeRequestMetrics.new(context.request_id, request_duration_ms)
455
466
  end
@@ -720,7 +731,7 @@ module Stripe
720
731
  Util.log_info("Response from Stripe API",
721
732
  account: context.account,
722
733
  api_version: context.api_version,
723
- elapsed: Time.now - request_start,
734
+ elapsed: Util.monotonic_time - request_start,
724
735
  idempotency_key: context.idempotency_key,
725
736
  method: context.method,
726
737
  path: context.path,
@@ -742,7 +753,7 @@ module Stripe
742
753
 
743
754
  private def log_response_error(context, request_start, error)
744
755
  Util.log_error("Request error",
745
- elapsed: Time.now - request_start,
756
+ elapsed: Util.monotonic_time - request_start,
746
757
  error_message: error.message,
747
758
  idempotency_key: context.idempotency_key,
748
759
  method: context.method,
data/lib/stripe/util.rb CHANGED
@@ -172,6 +172,18 @@ module Stripe
172
172
  result
173
173
  end
174
174
 
175
+ # `Time.now` can be unstable in cases like an administrator manually
176
+ # updating its value or a reconcilation via NTP. For this reason, prefer
177
+ # the use of the system's monotonic clock especially where comparing times
178
+ # to calculate an elapsed duration.
179
+ #
180
+ # Shortcut for getting monotonic time, mostly for purposes of line length
181
+ # and test stubbing. Returns time in seconds since the event used for
182
+ # monotonic reference purposes by the platform (e.g. system boot time).
183
+ def self.monotonic_time
184
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
185
+ end
186
+
175
187
  def self.normalize_id(id)
176
188
  if id.is_a?(Hash) # overloaded id
177
189
  params_hash = id.dup
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Stripe
4
- VERSION = "5.2.0"
4
+ VERSION = "5.6.0"
5
5
  end
@@ -227,6 +227,23 @@ module Stripe
227
227
  end
228
228
  end
229
229
 
230
+ should "error if a user-specified opt is given a non-nil non-string value" do
231
+ stub_request(:post, "#{Stripe.api_base}/v1/charges")
232
+ .to_return(body: JSON.generate(charge_fixture))
233
+
234
+ # Works fine if not included or a string.
235
+ Stripe::Charge.create({ amount: 100, currency: "usd" }, {})
236
+ Stripe::Charge.create({ amount: 100, currency: "usd" }, idempotency_key: "12345")
237
+
238
+ # Errors on a non-string.
239
+ e = assert_raises(ArgumentError) do
240
+ Stripe::Charge.create({ amount: 100, currency: "usd" }, idempotency_key: :foo)
241
+ end
242
+ assert_equal "request option 'idempotency_key' should be a string value " \
243
+ "(was a Symbol)",
244
+ e.message
245
+ end
246
+
230
247
  should "requesting with a unicode ID should result in a request" do
231
248
  stub_request(:get, "#{Stripe.api_base}/v1/customers/%E2%98%83")
232
249
  .to_return(body: JSON.generate(make_missing_id_error), status: 404)
@@ -10,10 +10,9 @@ module Stripe
10
10
 
11
11
  context "#initialize" do
12
12
  should "set #last_used to current time" do
13
- t = Time.new(2019)
14
- Timecop.freeze(t) do
15
- assert_equal t, Stripe::ConnectionManager.new.last_used
16
- end
13
+ t = 123.0
14
+ Util.stubs(:monotonic_time).returns(t)
15
+ assert_equal t, Stripe::ConnectionManager.new.last_used
17
16
  end
18
17
  end
19
18
 
@@ -147,19 +146,17 @@ module Stripe
147
146
  stub_request(:post, "#{Stripe.api_base}/path")
148
147
  .to_return(body: JSON.generate(object: "account"))
149
148
 
150
- t = Time.new(2019)
149
+ t = 123.0
150
+ Util.stubs(:monotonic_time).returns(t)
151
151
 
152
- # Make sure the connection manager is initialized at a different time
153
- # than the one we're going to measure at because `#last_used` is also
154
- # set by the constructor.
155
- manager = Timecop.freeze(t) do
156
- Stripe::ConnectionManager.new
157
- end
152
+ manager = Stripe::ConnectionManager.new
158
153
 
159
- Timecop.freeze(t + 1) do
160
- manager.execute_request(:post, "#{Stripe.api_base}/path")
161
- assert_equal t + 1, manager.last_used
162
- end
154
+ # `#last_used` is also set by the constructor, so make sure we get a
155
+ # new value for it.
156
+ Util.stubs(:monotonic_time).returns(t + 1.0)
157
+
158
+ manager.execute_request(:post, "#{Stripe.api_base}/path")
159
+ assert_equal t + 1.0, manager.last_used
163
160
  end
164
161
  end
165
162
  end
@@ -27,47 +27,63 @@ module Stripe
27
27
  StripeClient.clear_all_connection_managers
28
28
 
29
29
  # Establish a base time.
30
- t = Time.new(2019)
30
+ t = 0.0
31
31
 
32
32
  # And pretend that `StripeClient` was just initialized for the first
33
33
  # time. (Don't access instance variables like this, but it's tricky to
34
34
  # test properly otherwise.)
35
35
  StripeClient.instance_variable_set(:@last_connection_manager_gc, t)
36
36
 
37
- Timecop.freeze(t) do
38
- # Execute an initial request to ensure that a connection manager was
39
- # created.
40
- client = StripeClient.new
41
- client.execute_request(:post, "/v1/path")
37
+ #
38
+ # t
39
+ #
40
+ Util.stubs(:monotonic_time).returns(t)
42
41
 
43
- # The GC shouldn't run yet (a `nil` return indicates that GC didn't run).
44
- assert_equal nil, StripeClient.maybe_gc_connection_managers
45
- end
42
+ # Execute an initial request to ensure that a connection manager was
43
+ # created.
44
+ client = StripeClient.new
45
+ client.execute_request(:post, "/v1/path")
46
+
47
+ # The GC shouldn't run yet (a `nil` return indicates that GC didn't run).
48
+ assert_equal nil, StripeClient.maybe_gc_connection_managers
46
49
 
50
+ #
51
+ # t + StripeClient::CONNECTION_MANAGER_GC_PERIOD - 1
52
+ #
47
53
  # Move time to just *before* garbage collection is eligible to run.
48
54
  # Nothing should happen.
49
- Timecop.freeze(t + StripeClient::CONNECTION_MANAGER_GC_PERIOD - 1) do
50
- assert_equal nil, StripeClient.maybe_gc_connection_managers
51
- end
55
+ #
56
+ Util.stubs(:monotonic_time).returns(t + StripeClient::CONNECTION_MANAGER_GC_PERIOD - 1)
57
+
58
+ assert_equal nil, StripeClient.maybe_gc_connection_managers
52
59
 
60
+ #
61
+ # t + StripeClient::CONNECTION_MANAGER_GC_PERIOD + 1
62
+ #
53
63
  # Move time to just *after* garbage collection is eligible to run.
54
64
  # Garbage collection will run, but because the connection manager is
55
65
  # not passed its expiry age, it will not be collected. Zero is returned
56
66
  # to indicate so.
57
- Timecop.freeze(t + StripeClient::CONNECTION_MANAGER_GC_PERIOD + 1) do
58
- assert_equal 0, StripeClient.maybe_gc_connection_managers
59
- end
67
+ #
68
+ Util.stubs(:monotonic_time).returns(t + StripeClient::CONNECTION_MANAGER_GC_PERIOD + 1)
69
+
70
+ assert_equal 0, StripeClient.maybe_gc_connection_managers
60
71
 
72
+ #
73
+ # t + StripeClient::CONNECTION_MANAGER_GC_LAST_USED_EXPIRY + 1
74
+ #
61
75
  # Move us far enough into the future that we're passed the horizons for
62
76
  # both a GC run as well as well as the expiry age of a connection
63
77
  # manager. That means the GC will run and collect the connection
64
78
  # manager that we created above.
65
- Timecop.freeze(t + StripeClient::CONNECTION_MANAGER_GC_LAST_USED_EXPIRY + 1) do
66
- assert_equal 1, StripeClient.maybe_gc_connection_managers
79
+ #
80
+ Util.stubs(:monotonic_time).returns(t + StripeClient::CONNECTION_MANAGER_GC_LAST_USED_EXPIRY + 1)
67
81
 
68
- # And as an additional check, the connection manager of the current thread context should have been set to `nil` as it was GCed.
69
- assert_nil StripeClient.current_thread_context.default_connection_manager
70
- end
82
+ assert_equal 1, StripeClient.maybe_gc_connection_managers
83
+
84
+ # And as an additional check, the connection manager of the current
85
+ # thread context should have been set to `nil` as it was GCed.
86
+ assert_nil StripeClient.current_thread_context.default_connection_manager
71
87
  end
72
88
  end
73
89
 
@@ -156,6 +172,26 @@ module Stripe
156
172
  method: :post, num_retries: 0)
157
173
  end
158
174
 
175
+ should "retry on EOFError" do
176
+ assert StripeClient.should_retry?(EOFError.new,
177
+ method: :post, num_retries: 0)
178
+ end
179
+
180
+ should "retry on Errno::ECONNRESET" do
181
+ assert StripeClient.should_retry?(Errno::ECONNRESET.new,
182
+ method: :post, num_retries: 0)
183
+ end
184
+
185
+ should "retry on Errno::ETIMEDOUT" do
186
+ assert StripeClient.should_retry?(Errno::ETIMEDOUT.new,
187
+ method: :post, num_retries: 0)
188
+ end
189
+
190
+ should "retry on Errno::EHOSTUNREACH" do
191
+ assert StripeClient.should_retry?(Errno::EHOSTUNREACH.new,
192
+ method: :post, num_retries: 0)
193
+ end
194
+
159
195
  should "retry on Net::OpenTimeout" do
160
196
  assert StripeClient.should_retry?(Net::OpenTimeout.new,
161
197
  method: :post, num_retries: 0)
@@ -171,6 +207,28 @@ module Stripe
171
207
  method: :post, num_retries: 0)
172
208
  end
173
209
 
210
+ should "retry when the `Stripe-Should-Retry` header is `true`" do
211
+ headers = StripeResponse::Headers.new(
212
+ "Stripe-Should-Retry" => ["true"]
213
+ )
214
+
215
+ # Note we send status 400 here, which would normally not be retried.
216
+ assert StripeClient.should_retry?(Stripe::StripeError.new(http_headers: headers,
217
+ http_status: 400),
218
+ method: :post, num_retries: 0)
219
+ end
220
+
221
+ should "not retry when the `Stripe-Should-Retry` header is `false`" do
222
+ headers = StripeResponse::Headers.new(
223
+ "Stripe-Should-Retry" => ["false"]
224
+ )
225
+
226
+ # Note we send status 409 here, which would normally be retried.
227
+ refute StripeClient.should_retry?(Stripe::StripeError.new(http_headers: headers,
228
+ http_status: 409),
229
+ method: :post, num_retries: 0)
230
+ end
231
+
174
232
  should "retry on a 409 Conflict" do
175
233
  assert StripeClient.should_retry?(Stripe::StripeError.new(http_status: 409),
176
234
  method: :post, num_retries: 0)
@@ -279,16 +337,11 @@ module Stripe
279
337
  context "logging" do
280
338
  setup do
281
339
  # Freeze time for the purposes of the `elapsed` parameter that we
282
- # emit for responses. I didn't want to bring in a new dependency for
283
- # this, but Mocha's `anything` parameter can't match inside of a hash
284
- # and is therefore not useful for this purpose. If we switch over to
285
- # rspec-mocks at some point, we can probably remove Timecop from the
286
- # project.
287
- Timecop.freeze(Time.local(1990))
288
- end
289
-
290
- teardown do
291
- Timecop.return
340
+ # emit for responses. Mocha's `anything` parameter can't match inside
341
+ # of a hash and is therefore not useful for this purpose. If we
342
+ # switch over to rspec-mocks at some point, we can probably remove
343
+ # this.
344
+ Util.stubs(:monotonic_time).returns(0.0)
292
345
  end
293
346
 
294
347
  should "produce appropriate logging" do
data/test/test_helper.rb CHANGED
@@ -8,7 +8,6 @@ require "test/unit"
8
8
  require "mocha/setup"
9
9
  require "stringio"
10
10
  require "shoulda/context"
11
- require "timecop"
12
11
  require "webmock/test_unit"
13
12
 
14
13
  PROJECT_ROOT = ::File.expand_path("../", __dir__)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stripe
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.0
4
+ version: 5.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stripe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-20 00:00:00.000000000 Z
11
+ date: 2019-10-04 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Stripe is the easiest way to accept payments online. See https://stripe.com
14
14
  for details.
@@ -246,7 +246,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
246
246
  - !ruby/object:Gem::Version
247
247
  version: '0'
248
248
  requirements: []
249
- rubygems_version: 3.0.3
249
+ rubygems_version: 3.0.6
250
250
  signing_key:
251
251
  specification_version: 4
252
252
  summary: Ruby bindings for the Stripe API