stripe 1.30.3 → 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 (122) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +4 -0
  3. data/.github/ISSUE_TEMPLATE.md +5 -0
  4. data/.travis.yml +3 -14
  5. data/Gemfile +28 -4
  6. data/History.txt +180 -0
  7. data/README.md +147 -0
  8. data/Rakefile +10 -0
  9. data/VERSION +1 -1
  10. data/bin/stripe-console +12 -5
  11. data/lib/data/ca-certificates.crt +3868 -5114
  12. data/lib/stripe/account.rb +43 -23
  13. data/lib/stripe/alipay_account.rb +20 -0
  14. data/lib/stripe/api_operations/create.rb +2 -2
  15. data/lib/stripe/api_operations/delete.rb +2 -2
  16. data/lib/stripe/api_operations/list.rb +2 -3
  17. data/lib/stripe/api_operations/request.rb +9 -3
  18. data/lib/stripe/api_operations/save.rb +85 -0
  19. data/lib/stripe/api_resource.rb +38 -5
  20. data/lib/stripe/apple_pay_domain.rb +12 -0
  21. data/lib/stripe/application_fee.rb +8 -8
  22. data/lib/stripe/application_fee_refund.rb +7 -3
  23. data/lib/stripe/balance_transaction.rb +1 -1
  24. data/lib/stripe/bank_account.rb +13 -4
  25. data/lib/stripe/bitcoin_receiver.rb +6 -6
  26. data/lib/stripe/bitcoin_transaction.rb +1 -1
  27. data/lib/stripe/card.rb +9 -5
  28. data/lib/stripe/charge.rb +38 -20
  29. data/lib/stripe/country_spec.rb +9 -0
  30. data/lib/stripe/coupon.rb +1 -1
  31. data/lib/stripe/customer.rb +12 -10
  32. data/lib/stripe/dispute.rb +4 -5
  33. data/lib/stripe/errors.rb +92 -0
  34. data/lib/stripe/file_upload.rb +1 -1
  35. data/lib/stripe/invoice.rb +7 -7
  36. data/lib/stripe/invoice_item.rb +1 -1
  37. data/lib/stripe/list_object.rb +8 -7
  38. data/lib/stripe/order.rb +12 -4
  39. data/lib/stripe/order_return.rb +9 -0
  40. data/lib/stripe/plan.rb +1 -1
  41. data/lib/stripe/product.rb +2 -10
  42. data/lib/stripe/recipient.rb +1 -1
  43. data/lib/stripe/refund.rb +1 -1
  44. data/lib/stripe/reversal.rb +7 -3
  45. data/lib/stripe/singleton_api_resource.rb +3 -3
  46. data/lib/stripe/sku.rb +2 -2
  47. data/lib/stripe/source.rb +11 -0
  48. data/lib/stripe/stripe_client.rb +396 -0
  49. data/lib/stripe/stripe_object.rb +167 -91
  50. data/lib/stripe/stripe_response.rb +48 -0
  51. data/lib/stripe/subscription.rb +15 -9
  52. data/lib/stripe/subscription_item.rb +12 -0
  53. data/lib/stripe/three_d_secure.rb +9 -0
  54. data/lib/stripe/transfer.rb +4 -5
  55. data/lib/stripe/util.rb +105 -33
  56. data/lib/stripe/version.rb +1 -1
  57. data/lib/stripe.rb +69 -266
  58. data/spec/fixtures.json +1409 -0
  59. data/spec/fixtures.yaml +1153 -0
  60. data/spec/spec.json +19949 -0
  61. data/spec/spec.yaml +15504 -0
  62. data/stripe.gemspec +5 -18
  63. data/test/api_fixtures.rb +29 -0
  64. data/test/api_stub_helpers.rb +125 -0
  65. data/test/stripe/account_test.rb +163 -211
  66. data/test/stripe/alipay_account_test.rb +19 -0
  67. data/test/stripe/api_operations_test.rb +31 -0
  68. data/test/stripe/api_resource_test.rb +174 -340
  69. data/test/stripe/apple_pay_domain_test.rb +33 -0
  70. data/test/stripe/application_fee_refund_test.rb +22 -31
  71. data/test/stripe/application_fee_test.rb +6 -14
  72. data/test/stripe/balance_test.rb +3 -3
  73. data/test/stripe/bank_account_test.rb +41 -0
  74. data/test/stripe/bitcoin_receiver_test.rb +51 -42
  75. data/test/stripe/bitcoin_transaction_test.rb +11 -19
  76. data/test/stripe/charge_test.rb +39 -98
  77. data/test/stripe/country_spec_test.rb +20 -0
  78. data/test/stripe/coupon_test.rb +35 -11
  79. data/test/stripe/customer_card_test.rb +25 -46
  80. data/test/stripe/customer_test.rb +89 -61
  81. data/test/stripe/dispute_test.rb +28 -31
  82. data/test/stripe/errors_test.rb +18 -0
  83. data/test/stripe/file_upload_test.rb +32 -24
  84. data/test/stripe/invoice_item_test.rb +55 -0
  85. data/test/stripe/invoice_test.rb +50 -24
  86. data/test/stripe/list_object_test.rb +57 -45
  87. data/test/stripe/order_return_test.rb +21 -0
  88. data/test/stripe/order_test.rb +41 -34
  89. data/test/stripe/plan_test.rb +52 -0
  90. data/test/stripe/product_test.rb +31 -25
  91. data/test/stripe/recipient_card_test.rb +23 -40
  92. data/test/stripe/recipient_test.rb +50 -0
  93. data/test/stripe/refund_test.rb +20 -36
  94. data/test/stripe/reversal_test.rb +27 -31
  95. data/test/stripe/sku_test.rb +39 -13
  96. data/test/stripe/source_test.rb +43 -0
  97. data/test/stripe/stripe_client_test.rb +428 -0
  98. data/test/stripe/stripe_object_test.rb +186 -13
  99. data/test/stripe/stripe_response_test.rb +46 -0
  100. data/test/stripe/subscription_item_test.rb +54 -0
  101. data/test/stripe/subscription_test.rb +40 -52
  102. data/test/stripe/three_d_secure_test.rb +23 -0
  103. data/test/stripe/transfer_test.rb +38 -13
  104. data/test/stripe/util_test.rb +48 -16
  105. data/test/stripe_test.rb +25 -0
  106. data/test/test_data.rb +5 -621
  107. data/test/test_helper.rb +24 -24
  108. metadata +60 -139
  109. data/README.rdoc +0 -68
  110. data/gemfiles/default-with-activesupport.gemfile +0 -10
  111. data/gemfiles/json.gemfile +0 -12
  112. data/gemfiles/yajl.gemfile +0 -12
  113. data/lib/stripe/api_operations/update.rb +0 -58
  114. data/lib/stripe/errors/api_connection_error.rb +0 -4
  115. data/lib/stripe/errors/api_error.rb +0 -4
  116. data/lib/stripe/errors/authentication_error.rb +0 -4
  117. data/lib/stripe/errors/card_error.rb +0 -12
  118. data/lib/stripe/errors/invalid_request_error.rb +0 -11
  119. data/lib/stripe/errors/rate_limit_error.rb +0 -4
  120. data/lib/stripe/errors/stripe_error.rb +0 -26
  121. data/test/stripe/charge_refund_test.rb +0 -55
  122. data/test/stripe/metadata_test.rb +0 -129
@@ -2,46 +2,42 @@ require File.expand_path('../../test_helper', __FILE__)
2
2
 
3
3
  module Stripe
4
4
  class ReversalTest < Test::Unit::TestCase
5
- should "reversals should be listable" do
6
- @mock.expects(:get).once.returns(make_response(make_transfer))
5
+ FIXTURE = API_FIXTURES.fetch(:transfer_reversal)
7
6
 
8
- transfer = Stripe::Transfer.retrieve('test_transfer')
9
-
10
- assert transfer.reversals.first.kind_of?(Stripe::Reversal)
7
+ setup do
8
+ @transfer = Stripe::Transfer.retrieve(API_FIXTURES.fetch(:transfer)[:id])
11
9
  end
12
10
 
13
- should "reversals should be refreshable" do
14
- @mock.expects(:get).twice.returns(make_response(make_transfer), make_response(make_reversal(:id => 'refreshed_reversal')))
15
-
16
- transfer = Stripe::Transfer.retrieve('test_transfer')
17
- reversal = transfer.reversals.first
18
- reversal.refresh
19
-
20
- assert_equal 'refreshed_reversal', reversal.id
11
+ should "be listable" do
12
+ reversals = @transfer.reversals.list
13
+ assert_requested :get,
14
+ "#{Stripe.api_base}/v1/transfers/#{@transfer.id}/reversals"
15
+ assert reversals.data.kind_of?(Array)
16
+ assert reversals.data[0].kind_of?(Stripe::Reversal)
21
17
  end
22
18
 
23
- should "reversals should be updateable" do
24
- @mock.expects(:get).once.returns(make_response(make_transfer))
25
- @mock.expects(:post).once.returns(make_response(make_reversal(:metadata => {'key' => 'value'})))
26
-
27
- transfer = Stripe::Transfer.retrieve('test_transfer')
28
- reversal = transfer.reversals.first
19
+ should "be retrievable" do
20
+ reversal = @transfer.reversals.retrieve(FIXTURE[:id])
21
+ assert_requested :get,
22
+ "#{Stripe.api_base}/v1/transfers/#{@transfer.id}/reversals/#{FIXTURE[:id]}"
23
+ assert reversal.kind_of?(Stripe::Reversal)
24
+ end
29
25
 
30
- assert_equal nil, reversal.metadata['key']
26
+ should "be creatable" do
27
+ reversal = @transfer.reversals.create(
28
+ amount: 100
29
+ )
30
+ assert_requested :post,
31
+ "#{Stripe.api_base}/v1/transfers/#{@transfer.id}/reversals"
32
+ assert reversal.kind_of?(Stripe::Reversal)
33
+ end
31
34
 
35
+ should "be saveable" do
36
+ reversal = @transfer.reversals.retrieve(FIXTURE[:id])
32
37
  reversal.metadata['key'] = 'value'
33
38
  reversal.save
34
-
35
- assert_equal 'value', reversal.metadata['key']
36
- end
37
-
38
- should "create should return a new reversal" do
39
- @mock.expects(:get).once.returns(make_response(make_transfer))
40
- @mock.expects(:post).once.returns(make_response(make_reversal(:id => 'test_new_reversal')))
41
-
42
- transfer = Stripe::Transfer.retrieve('test_transfer')
43
- reversals = transfer.reversals.create(:amount => 20)
44
- assert_equal 'test_new_reversal', reversals.id
39
+ assert_requested :post,
40
+ "#{Stripe.api_base}/v1/transfers/#{@transfer.id}/reversals/#{FIXTURE[:id]}"
45
41
  end
46
42
  end
47
43
  end
@@ -2,23 +2,49 @@ require File.expand_path('../../test_helper', __FILE__)
2
2
 
3
3
  module Stripe
4
4
  class SKUTest < Test::Unit::TestCase
5
- should "SKUs should be listable" do
6
- @mock.expects(:get).once.
7
- returns(make_response(make_sku_array("test_product")))
5
+ FIXTURE = API_FIXTURES.fetch(:sku)
6
+
7
+ should "be listable" do
8
8
  skus = Stripe::SKU.list
9
- assert skus.data.kind_of? Array
10
- skus.each do |sku|
11
- assert sku.kind_of?(Stripe::SKU)
12
- end
9
+ assert_requested :get, "#{Stripe.api_base}/v1/skus"
10
+ assert skus.data.kind_of?(Array)
11
+ assert skus.data[0].kind_of?(Stripe::SKU)
12
+ end
13
+
14
+ should "be retrievable" do
15
+ sku = Stripe::SKU.retrieve(FIXTURE[:id])
16
+ assert_requested :get, "#{Stripe.api_base}/v1/skus/#{FIXTURE[:id]}"
17
+ assert sku.kind_of?(Stripe::SKU)
18
+ end
19
+
20
+ should "be creatable" do
21
+ _ = Stripe::SKU.create(
22
+ currency: "USD",
23
+ inventory: { type: "finite", quantity: 500 },
24
+ price: 100,
25
+ product: API_FIXTURES.fetch(:product)[:id]
26
+ )
27
+ assert_requested :post, "#{Stripe.api_base}/v1/skus"
28
+ end
29
+
30
+ should "be saveable" do
31
+ sku = Stripe::SKU.retrieve(FIXTURE[:id])
32
+ sku.metadata['key'] = 'value'
33
+ sku.save
34
+ assert_requested :post, "#{Stripe.api_base}/v1/skus/#{FIXTURE[:id]}"
13
35
  end
14
36
 
15
- should "SKUs should not be deletable" do
16
- assert_raises NoMethodError do
17
- @mock.expects(:get).once.returns(make_response(make_sku))
18
- p = Stripe::SKU.retrieve("test_product")
19
- p.delete
20
- end
37
+ should "be updateable" do
38
+ sku = Stripe::SKU.update(FIXTURE[:id], metadata: {foo: 'bar'})
39
+ assert_requested :post, "#{Stripe.api_base}/v1/skus/#{FIXTURE[:id]}"
40
+ assert sku.kind_of?(Stripe::SKU)
21
41
  end
22
42
 
43
+ should "be deletable" do
44
+ sku = Stripe::SKU.retrieve(FIXTURE[:id])
45
+ sku = sku.delete
46
+ assert_requested :delete, "#{Stripe.api_base}/v1/skus/#{FIXTURE[:id]}"
47
+ assert sku.kind_of?(Stripe::SKU)
48
+ end
23
49
  end
24
50
  end
@@ -0,0 +1,43 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ module Stripe
4
+ class SourceTest < Test::Unit::TestCase
5
+ FIXTURE = API_FIXTURES.fetch(:source)
6
+
7
+ should "be retrievable" do
8
+ source = Stripe::Source.retrieve(FIXTURE[:id])
9
+ assert_requested :get, "#{Stripe.api_base}/v1/sources/#{FIXTURE[:id]}"
10
+ assert source.kind_of?(Stripe::Source)
11
+ end
12
+
13
+ should "be creatable" do
14
+ source = Stripe::Source.create(
15
+ type: 'card',
16
+ token: API_FIXTURES.fetch(:token)[:id]
17
+ )
18
+ assert_requested :post, "#{Stripe.api_base}/v1/sources"
19
+ assert source.kind_of?(Stripe::Card)
20
+ end
21
+
22
+ should "be saveable" do
23
+ source = Stripe::Source.retrieve(FIXTURE[:id])
24
+ source.metadata['key'] = 'value'
25
+ source.save
26
+ assert_requested :post, "#{Stripe.api_base}/v1/sources/#{FIXTURE[:id]}"
27
+ end
28
+
29
+ should "be updateable" do
30
+ source = Stripe::Source.update(FIXTURE[:id], metadata: {foo: 'bar'})
31
+ assert_requested :post, "#{Stripe.api_base}/v1/sources/#{FIXTURE[:id]}"
32
+ assert source.kind_of?(Stripe::Card)
33
+ end
34
+
35
+ context "#verify" do
36
+ should "verify the source" do
37
+ source = Stripe::Source.retrieve(FIXTURE[:id])
38
+ source = source.verify(:values => [1,2])
39
+ assert source.kind_of?(Stripe::Source)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,428 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ module Stripe
4
+ class StripeClientTest < Test::Unit::TestCase
5
+ context ".active_client" do
6
+ should "be .default_client outside of #request" do
7
+ assert_equal StripeClient.default_client, StripeClient.active_client
8
+ end
9
+
10
+ should "be active client inside of #request" do
11
+ client = StripeClient.new
12
+ client.request do
13
+ assert_equal client, StripeClient.active_client
14
+ end
15
+ end
16
+ end
17
+
18
+ context ".default_client" do
19
+ should "be a StripeClient" do
20
+ assert_kind_of StripeClient, StripeClient.default_client
21
+ end
22
+ end
23
+
24
+ context ".default_conn" do
25
+ should "be a Faraday::Connection" do
26
+ assert_kind_of Faraday::Connection, StripeClient.default_conn
27
+ end
28
+
29
+ should "be a different connection on each thread" do
30
+ other_thread_conn = nil
31
+ thread = Thread.new do
32
+ other_thread_conn = StripeClient.default_conn
33
+ end
34
+ thread.join
35
+ refute_equal StripeClient.default_conn, other_thread_conn
36
+ end
37
+ end
38
+
39
+ context ".should_retry?" do
40
+ setup do
41
+ Stripe.stubs(:max_network_retries).returns(2)
42
+ end
43
+
44
+ should 'retry on timeout' do
45
+ assert StripeClient.should_retry?(Faraday::TimeoutError.new(""), 0)
46
+ end
47
+
48
+ should 'retry on a failed connection' do
49
+ assert StripeClient.should_retry?(Faraday::ConnectionFailed.new(""), 0)
50
+ end
51
+
52
+ should 'retry on a conflict' do
53
+ error = make_rate_limit_error
54
+ e = Faraday::ClientError.new(error[:error][:message], { status: 409 })
55
+ assert StripeClient.should_retry?(e, 0)
56
+ end
57
+
58
+ should 'not retry at maximum count' do
59
+ refute StripeClient.should_retry?(RuntimeError.new, Stripe.max_network_retries)
60
+ end
61
+
62
+ should 'not retry on a certificate validation error' do
63
+ refute StripeClient.should_retry?(Faraday::SSLError.new(""), 0)
64
+ end
65
+ end
66
+
67
+ context ".sleep_time" do
68
+ should "should grow exponentially" do
69
+ StripeClient.stubs(:rand).returns(1)
70
+ Stripe.stubs(:max_network_retry_delay).returns(999)
71
+ assert_equal(Stripe.initial_network_retry_delay, StripeClient.sleep_time(1))
72
+ assert_equal(Stripe.initial_network_retry_delay * 2, StripeClient.sleep_time(2))
73
+ assert_equal(Stripe.initial_network_retry_delay * 4, StripeClient.sleep_time(3))
74
+ assert_equal(Stripe.initial_network_retry_delay * 8, StripeClient.sleep_time(4))
75
+ end
76
+
77
+ should "enforce the max_network_retry_delay" do
78
+ StripeClient.stubs(:rand).returns(1)
79
+ Stripe.stubs(:initial_network_retry_delay).returns(1)
80
+ Stripe.stubs(:max_network_retry_delay).returns(2)
81
+ assert_equal(1, StripeClient.sleep_time(1))
82
+ assert_equal(2, StripeClient.sleep_time(2))
83
+ assert_equal(2, StripeClient.sleep_time(3))
84
+ assert_equal(2, StripeClient.sleep_time(4))
85
+ end
86
+
87
+ should "add some randomness" do
88
+ random_value = 0.8
89
+ StripeClient.stubs(:rand).returns(random_value)
90
+ Stripe.stubs(:initial_network_retry_delay).returns(1)
91
+ Stripe.stubs(:max_network_retry_delay).returns(8)
92
+
93
+ base_value = Stripe.initial_network_retry_delay * (0.5 * (1 + random_value))
94
+
95
+ # the initial value cannot be smaller than the base,
96
+ # so the randomness is ignored
97
+ assert_equal(Stripe.initial_network_retry_delay, StripeClient.sleep_time(1))
98
+
99
+ # after the first one, the randomness is applied
100
+ assert_equal(base_value * 2, StripeClient.sleep_time(2))
101
+ assert_equal(base_value * 4, StripeClient.sleep_time(3))
102
+ assert_equal(base_value * 8, StripeClient.sleep_time(4))
103
+ end
104
+ end
105
+
106
+ context "#initialize" do
107
+ should "set Stripe.default_conn" do
108
+ client = StripeClient.new
109
+ assert_equal StripeClient.default_conn, client.conn
110
+ end
111
+
112
+ should "set a different connection if one was specified" do
113
+ conn = Faraday.new
114
+ client = StripeClient.new(conn)
115
+ assert_equal conn, client.conn
116
+ end
117
+ end
118
+
119
+ context "#execute_request" do
120
+ context "headers" do
121
+ should "support literal headers" do
122
+ stub_request(:post, "#{Stripe.api_base}/v1/account").
123
+ with(headers: { "Stripe-Account" => "bar" }).
124
+ to_return(body: JSON.generate(API_FIXTURES.fetch(:account)))
125
+
126
+ client = StripeClient.new
127
+ client.execute_request(:post, '/v1/account',
128
+ headers: { "Stripe-Account" => "bar" }
129
+ )
130
+ end
131
+
132
+ should "support RestClient-style header keys" do
133
+ stub_request(:post, "#{Stripe.api_base}/v1/account").
134
+ with(headers: { "Stripe-Account" => "bar" }).
135
+ to_return(body: JSON.generate(API_FIXTURES.fetch(:account)))
136
+
137
+ client = StripeClient.new
138
+ client.execute_request(:post, '/v1/account',
139
+ headers: { :stripe_account => "bar" }
140
+ )
141
+ end
142
+ end
143
+
144
+ context "Stripe-Account header" do
145
+ should "use a globally set header" do
146
+ Stripe.stripe_account = 'acct_1234'
147
+
148
+ stub_request(:post, "#{Stripe.api_base}/v1/account").
149
+ with(headers: {"Stripe-Account" => Stripe.stripe_account}).
150
+ to_return(body: JSON.generate(API_FIXTURES.fetch(:account)))
151
+
152
+ client = StripeClient.new
153
+ client.execute_request(:post, '/v1/account')
154
+ end
155
+
156
+ should "use a locally set header" do
157
+ stripe_account = "acct_0000"
158
+ stub_request(:post, "#{Stripe.api_base}/v1/account").
159
+ with(headers: {"Stripe-Account" => stripe_account}).
160
+ to_return(body: JSON.generate(API_FIXTURES.fetch(:account)))
161
+
162
+ client = StripeClient.new
163
+ client.execute_request(:post, '/v1/account',
164
+ headers: { :stripe_account => stripe_account }
165
+ )
166
+ end
167
+
168
+ should "not send it otherwise" do
169
+ stub_request(:post, "#{Stripe.api_base}/v1/account").
170
+ with { |req|
171
+ req.headers["Stripe-Account"].nil?
172
+ }.to_return(body: JSON.generate(API_FIXTURES.fetch(:account)))
173
+
174
+ client = StripeClient.new
175
+ client.execute_request(:post, '/v1/account')
176
+ end
177
+ end
178
+
179
+ context "error handling" do
180
+ should "handle error response with empty body" do
181
+ stub_request(:post, "#{Stripe.api_base}/v1/charges").
182
+ to_return(body: '', status: 500)
183
+
184
+ client = StripeClient.new
185
+ e = assert_raises Stripe::APIError do
186
+ client.execute_request(:post, '/v1/charges')
187
+ end
188
+
189
+ assert_equal 'Invalid response object from API: "" (HTTP response code was 500)', e.message
190
+ end
191
+
192
+ should "handle error response with non-object error value" do
193
+ stub_request(:post, "#{Stripe.api_base}/v1/charges").
194
+ to_return(body: JSON.generate({ error: "foo" }), status: 500)
195
+
196
+ client = StripeClient.new
197
+ e = assert_raises Stripe::APIError do
198
+ client.execute_request(:post, '/v1/charges')
199
+ end
200
+
201
+ assert_equal 'Invalid response object from API: "{\"error\":\"foo\"}" (HTTP response code was 500)', e.message
202
+ end
203
+
204
+ should "raise InvalidRequestError on 400" do
205
+ stub_request(:post, "#{Stripe.api_base}/v1/charges").
206
+ to_return(body: JSON.generate(make_missing_id_error), status: 400)
207
+ client = StripeClient.new
208
+ begin
209
+ client.execute_request(:post, '/v1/charges')
210
+ rescue Stripe::InvalidRequestError => e
211
+ assert_equal(400, e.http_status)
212
+ assert_equal(true, !!e.http_body)
213
+ assert_equal(true, e.json_body.kind_of?(Hash))
214
+ end
215
+ end
216
+
217
+ should "raise AuthenticationError on 401" do
218
+ stub_request(:post, "#{Stripe.api_base}/v1/charges").
219
+ to_return(body: JSON.generate(make_missing_id_error), status: 401)
220
+ client = StripeClient.new
221
+ begin
222
+ client.execute_request(:post, '/v1/charges')
223
+ rescue Stripe::AuthenticationError => e
224
+ assert_equal(401, e.http_status)
225
+ assert_equal(true, !!e.http_body)
226
+ assert_equal(true, e.json_body.kind_of?(Hash))
227
+ end
228
+ end
229
+
230
+ should "raise CardError on 402" do
231
+ stub_request(:post, "#{Stripe.api_base}/v1/charges").
232
+ to_return(body: JSON.generate(make_missing_id_error), status: 402)
233
+ client = StripeClient.new
234
+ begin
235
+ client.execute_request(:post, '/v1/charges')
236
+ rescue Stripe::CardError => e
237
+ assert_equal(402, e.http_status)
238
+ assert_equal(true, !!e.http_body)
239
+ assert_equal(true, e.json_body.kind_of?(Hash))
240
+ end
241
+ end
242
+
243
+ should "raise PermissionError on 403" do
244
+ stub_request(:post, "#{Stripe.api_base}/v1/charges").
245
+ to_return(body: JSON.generate(make_missing_id_error), status: 403)
246
+ client = StripeClient.new
247
+ begin
248
+ client.execute_request(:post, '/v1/charges')
249
+ rescue Stripe::PermissionError => e
250
+ assert_equal(403, e.http_status)
251
+ assert_equal(true, !!e.http_body)
252
+ assert_equal(true, e.json_body.kind_of?(Hash))
253
+ end
254
+ end
255
+
256
+ should "raise InvalidRequestError on 404" do
257
+ stub_request(:post, "#{Stripe.api_base}/v1/charges").
258
+ to_return(body: JSON.generate(make_missing_id_error), status: 404)
259
+ client = StripeClient.new
260
+ begin
261
+ client.execute_request(:post, '/v1/charges')
262
+ rescue Stripe::InvalidRequestError => e
263
+ assert_equal(404, e.http_status)
264
+ assert_equal(true, !!e.http_body)
265
+ assert_equal(true, e.json_body.kind_of?(Hash))
266
+ end
267
+ end
268
+
269
+ should "raise RateLimitError on 429" do
270
+ stub_request(:post, "#{Stripe.api_base}/v1/charges").
271
+ to_return(body: JSON.generate(make_rate_limit_error), status: 429)
272
+ client = StripeClient.new
273
+ begin
274
+ client.execute_request(:post, '/v1/charges')
275
+ rescue Stripe::RateLimitError => e
276
+ assert_equal(429, e.http_status)
277
+ assert_equal(true, !!e.http_body)
278
+ assert_equal(true, e.json_body.kind_of?(Hash))
279
+ end
280
+ end
281
+ end
282
+
283
+ context "idempotency keys" do
284
+ setup do
285
+ Stripe.stubs(:max_network_retries).returns(2)
286
+ end
287
+
288
+ should 'not add an idempotency key to GET requests' do
289
+ SecureRandom.expects(:uuid).times(0)
290
+ stub_request(:get, "#{Stripe.api_base}/v1/charges/#{API_FIXTURES.fetch(:charge)[:id]}").
291
+ with { |req|
292
+ req.headers['Idempotency-Key'].nil?
293
+ }.to_return(body: JSON.generate(API_FIXTURES.fetch(:charge)))
294
+ client = StripeClient.new
295
+ client.execute_request(:get, "/v1/charges/#{API_FIXTURES.fetch(:charge)[:id]}")
296
+ end
297
+
298
+ should 'ensure there is always an idempotency_key on POST requests' do
299
+ SecureRandom.expects(:uuid).at_least_once.returns("random_key")
300
+ stub_request(:post, "#{Stripe.api_base}/v1/charges").
301
+ with(headers: {"Idempotency-Key" => "random_key"}).
302
+ to_return(body: JSON.generate(API_FIXTURES.fetch(:charge)))
303
+ client = StripeClient.new
304
+ client.execute_request(:post, "/v1/charges")
305
+ end
306
+
307
+ should 'ensure there is always an idempotency_key on DELETE requests' do
308
+ SecureRandom.expects(:uuid).at_least_once.returns("random_key")
309
+ stub_request(:delete, "#{Stripe.api_base}/v1/charges/#{API_FIXTURES.fetch(:charge)[:id]}").
310
+ with(headers: {"Idempotency-Key" => "random_key"}).
311
+ to_return(body: JSON.generate(API_FIXTURES.fetch(:charge)))
312
+ client = StripeClient.new
313
+ client.execute_request(:delete, "/v1/charges/#{API_FIXTURES.fetch(:charge)[:id]}")
314
+ end
315
+
316
+ should 'not override a provided idempotency_key' do
317
+ # Note that this expectation looks like `:idempotency_key` instead of
318
+ # the header `Idempotency-Key` because it's user provided as seen
319
+ # below. The ones injected by the library itself look like headers
320
+ # (`Idempotency-Key`), but rest-client does allow this symbol
321
+ # formatting and will properly override the system generated one as
322
+ # expected.
323
+ stub_request(:post, "#{Stripe.api_base}/v1/charges").
324
+ with(headers: {"Idempotency-Key" => "provided_key"}).
325
+ to_return(body: JSON.generate(API_FIXTURES.fetch(:charge)))
326
+
327
+ client = StripeClient.new
328
+ client.execute_request(:post, "/v1/charges",
329
+ headers: {:idempotency_key => 'provided_key'})
330
+ end
331
+ end
332
+
333
+ context "retry logic" do
334
+ setup do
335
+ Stripe.stubs(:max_network_retries).returns(2)
336
+ end
337
+
338
+ should 'retry failed requests and raise if error persists' do
339
+ StripeClient.expects(:sleep_time).at_least_once.returns(0)
340
+ stub_request(:post, "#{Stripe.api_base}/v1/charges").
341
+ to_raise(Errno::ECONNREFUSED.new)
342
+
343
+ client = StripeClient.new
344
+ err = assert_raises Stripe::APIConnectionError do
345
+ client.execute_request(:post, '/v1/charges')
346
+ end
347
+ assert_match(/Request was retried 2 times/, err.message)
348
+ end
349
+
350
+ should 'retry failed requests and return successful response' do
351
+ StripeClient.expects(:sleep_time).at_least_once.returns(0)
352
+
353
+ i = 0
354
+ stub_request(:post, "#{Stripe.api_base}/v1/charges").
355
+ to_return { |_|
356
+ if i < 2
357
+ i += 1
358
+ raise Errno::ECONNREFUSED.new
359
+ else
360
+ { body: JSON.generate({"id" => "myid"}) }
361
+ end
362
+ }
363
+
364
+ client = StripeClient.new
365
+ client.execute_request(:post, '/v1/charges')
366
+ end
367
+ end
368
+ end
369
+
370
+ context "#request" do
371
+ should "return a result and response object" do
372
+ stub_request(:post, "#{Stripe.api_base}/v1/charges").
373
+ to_return(body: JSON.generate(API_FIXTURES.fetch(:charge)))
374
+
375
+ client = StripeClient.new
376
+ charge, resp = client.request { Charge.create }
377
+
378
+ assert charge.is_a?(Charge)
379
+ assert resp.is_a?(StripeResponse)
380
+ assert_equal 200, resp.http_status
381
+ end
382
+
383
+ should "return the value of given block" do
384
+ client = StripeClient.new
385
+ ret, _ = client.request { 7 }
386
+ assert_equal 7, ret
387
+ end
388
+
389
+ should "reset local thread state after a call" do
390
+ begin
391
+ Thread.current[:stripe_client] = :stripe_client
392
+
393
+ client = StripeClient.new
394
+ client.request {}
395
+
396
+ assert_equal :stripe_client, Thread.current[:stripe_client]
397
+ ensure
398
+ Thread.current[:stripe_client] = nil
399
+ end
400
+ end
401
+ end
402
+ end
403
+
404
+ class SystemProfilerTest < Test::Unit::TestCase
405
+ context "#get_uname" do
406
+ should "run without failure" do
407
+ # Don't actually check the result because we try a variety of different
408
+ # strategies that will have different results depending on where this
409
+ # test and running. We're mostly making sure that no exception is thrown.
410
+ _ = StripeClient::SystemProfiler.get_uname
411
+ end
412
+ end
413
+
414
+ context "#get_uname_from_system" do
415
+ should "run without failure" do
416
+ # as above, just verify that an exception is not thrown
417
+ _ = StripeClient::SystemProfiler.get_uname_from_system
418
+ end
419
+ end
420
+
421
+ context "#get_uname_from_system_ver" do
422
+ should "run without failure" do
423
+ # as above, just verify that an exception is not thrown
424
+ _ = StripeClient::SystemProfiler.get_uname_from_system_ver
425
+ end
426
+ end
427
+ end
428
+ end