stripe 1.30.3 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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