stripe 5.28.0 → 8.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (243) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +286 -0
  3. data/Gemfile +5 -3
  4. data/Makefile +13 -0
  5. data/OPENAPI_VERSION +1 -0
  6. data/README.md +43 -5
  7. data/VERSION +1 -1
  8. data/lib/data/ca-certificates.crt +1241 -1937
  9. data/lib/stripe/api_operations/create.rb +6 -2
  10. data/lib/stripe/api_operations/delete.rb +12 -7
  11. data/lib/stripe/api_operations/list.rb +6 -9
  12. data/lib/stripe/api_operations/nested_resource.rb +62 -34
  13. data/lib/stripe/api_operations/request.rb +45 -2
  14. data/lib/stripe/api_operations/save.rb +13 -3
  15. data/lib/stripe/api_operations/search.rb +16 -0
  16. data/lib/stripe/api_resource.rb +11 -18
  17. data/lib/stripe/api_resource_test_helpers.rb +49 -0
  18. data/lib/stripe/api_version.rb +8 -0
  19. data/lib/stripe/connection_manager.rb +49 -11
  20. data/lib/stripe/error_object.rb +2 -3
  21. data/lib/stripe/instrumentation.rb +46 -6
  22. data/lib/stripe/oauth.rb +4 -3
  23. data/lib/stripe/object_types.rb +36 -10
  24. data/lib/stripe/resources/account.rb +39 -17
  25. data/lib/stripe/resources/account_link.rb +4 -0
  26. data/lib/stripe/resources/application_fee_refund.rb +7 -1
  27. data/lib/stripe/resources/apps/secret.rb +40 -0
  28. data/lib/stripe/resources/balance.rb +11 -0
  29. data/lib/stripe/resources/balance_transaction.rb +4 -0
  30. data/lib/stripe/resources/bank_account.rb +7 -0
  31. data/lib/stripe/resources/billing_portal/configuration.rb +15 -0
  32. data/lib/stripe/resources/billing_portal/session.rb +14 -0
  33. data/lib/stripe/resources/capability.rb +5 -1
  34. data/lib/stripe/resources/card.rb +6 -3
  35. data/lib/stripe/resources/cash_balance.rb +23 -0
  36. data/lib/stripe/resources/charge.rb +23 -3
  37. data/lib/stripe/resources/checkout/session.rb +49 -2
  38. data/lib/stripe/resources/country_spec.rb +6 -0
  39. data/lib/stripe/resources/coupon.rb +3 -0
  40. data/lib/stripe/resources/credit_note.rb +27 -9
  41. data/lib/stripe/resources/credit_note_line_item.rb +2 -0
  42. data/lib/stripe/resources/customer.rb +132 -4
  43. data/lib/stripe/resources/customer_balance_transaction.rb +9 -2
  44. data/lib/stripe/resources/customer_cash_balance_transaction.rb +14 -0
  45. data/lib/stripe/resources/discount.rb +5 -0
  46. data/lib/stripe/resources/dispute.rb +17 -3
  47. data/lib/stripe/resources/event.rb +29 -0
  48. data/lib/stripe/resources/exchange_rate.rb +11 -0
  49. data/lib/stripe/resources/file.rb +9 -1
  50. data/lib/stripe/resources/file_link.rb +3 -0
  51. data/lib/stripe/resources/financial_connections/account.rb +67 -0
  52. data/lib/stripe/resources/financial_connections/account_owner.rb +11 -0
  53. data/lib/stripe/resources/financial_connections/account_ownership.rb +11 -0
  54. data/lib/stripe/resources/financial_connections/session.rb +13 -0
  55. data/lib/stripe/resources/funding_instructions.rb +21 -0
  56. data/lib/stripe/resources/identity/verification_report.rb +23 -0
  57. data/lib/stripe/resources/identity/verification_session.rb +61 -0
  58. data/lib/stripe/resources/invoice.rb +105 -17
  59. data/lib/stripe/resources/invoice_item.rb +11 -0
  60. data/lib/stripe/resources/invoice_line_item.rb +1 -0
  61. data/lib/stripe/resources/issuing/authorization.rb +25 -5
  62. data/lib/stripe/resources/issuing/card.rb +78 -8
  63. data/lib/stripe/resources/issuing/cardholder.rb +3 -0
  64. data/lib/stripe/resources/issuing/dispute.rb +13 -3
  65. data/lib/stripe/resources/issuing/transaction.rb +5 -0
  66. data/lib/stripe/resources/line_item.rb +2 -0
  67. data/lib/stripe/resources/login_link.rb +2 -0
  68. data/lib/stripe/resources/mandate.rb +1 -0
  69. data/lib/stripe/resources/payment_intent.rb +103 -6
  70. data/lib/stripe/resources/payment_link.rb +35 -0
  71. data/lib/stripe/resources/payment_method.rb +25 -5
  72. data/lib/stripe/resources/payout.rb +28 -5
  73. data/lib/stripe/resources/person.rb +7 -0
  74. data/lib/stripe/resources/plan.rb +8 -0
  75. data/lib/stripe/resources/price.rb +15 -0
  76. data/lib/stripe/resources/product.rb +17 -0
  77. data/lib/stripe/resources/promotion_code.rb +2 -0
  78. data/lib/stripe/resources/quote.rb +128 -0
  79. data/lib/stripe/resources/radar/early_fraud_warning.rb +4 -0
  80. data/lib/stripe/resources/radar/value_list.rb +3 -0
  81. data/lib/stripe/resources/radar/value_list_item.rb +3 -0
  82. data/lib/stripe/resources/refund.rb +49 -0
  83. data/lib/stripe/resources/reporting/report_run.rb +8 -0
  84. data/lib/stripe/resources/reporting/report_type.rb +8 -1
  85. data/lib/stripe/resources/reversal.rb +16 -2
  86. data/lib/stripe/resources/review.rb +14 -3
  87. data/lib/stripe/resources/setup_attempt.rb +4 -0
  88. data/lib/stripe/resources/setup_intent.rb +60 -5
  89. data/lib/stripe/resources/shipping_rate.rb +14 -0
  90. data/lib/stripe/resources/sigma/scheduled_query_run.rb +4 -0
  91. data/lib/stripe/resources/source.rb +26 -6
  92. data/lib/stripe/resources/source_transaction.rb +5 -0
  93. data/lib/stripe/resources/subscription.rb +57 -3
  94. data/lib/stripe/resources/subscription_item.rb +2 -7
  95. data/lib/stripe/resources/subscription_schedule.rb +23 -5
  96. data/lib/stripe/resources/tax/calculation.rb +33 -0
  97. data/lib/stripe/resources/tax/calculation_line_item.rb +10 -0
  98. data/lib/stripe/resources/tax/settings.rb +15 -0
  99. data/lib/stripe/resources/tax/transaction.rb +49 -0
  100. data/lib/stripe/resources/tax/transaction_line_item.rb +10 -0
  101. data/lib/stripe/resources/tax_code.rb +11 -0
  102. data/lib/stripe/resources/tax_id.rb +5 -0
  103. data/lib/stripe/resources/tax_rate.rb +3 -0
  104. data/lib/stripe/resources/terminal/configuration.rb +16 -0
  105. data/lib/stripe/resources/terminal/connection_token.rb +3 -0
  106. data/lib/stripe/resources/terminal/location.rb +3 -0
  107. data/lib/stripe/resources/terminal/reader.rb +119 -0
  108. data/lib/stripe/resources/test_helpers/test_clock.rb +35 -0
  109. data/lib/stripe/resources/token.rb +20 -0
  110. data/lib/stripe/resources/topup.rb +15 -3
  111. data/lib/stripe/resources/transfer.rb +10 -11
  112. data/lib/stripe/resources/treasury/credit_reversal.rb +14 -0
  113. data/lib/stripe/resources/treasury/debit_reversal.rb +14 -0
  114. data/lib/stripe/resources/treasury/financial_account.rb +52 -0
  115. data/lib/stripe/resources/treasury/financial_account_features.rb +12 -0
  116. data/lib/stripe/resources/treasury/inbound_transfer.rb +94 -0
  117. data/lib/stripe/resources/treasury/outbound_payment.rb +96 -0
  118. data/lib/stripe/resources/treasury/outbound_transfer.rb +100 -0
  119. data/lib/stripe/resources/treasury/received_credit.rb +30 -0
  120. data/lib/stripe/resources/treasury/received_debit.rb +30 -0
  121. data/lib/stripe/resources/treasury/transaction.rb +13 -0
  122. data/lib/stripe/resources/treasury/transaction_entry.rb +17 -0
  123. data/lib/stripe/resources/usage_record.rb +5 -0
  124. data/lib/stripe/resources/usage_record_summary.rb +1 -0
  125. data/lib/stripe/resources/webhook_endpoint.rb +7 -0
  126. data/lib/stripe/resources.rb +34 -10
  127. data/lib/stripe/search_result_object.rb +86 -0
  128. data/lib/stripe/stripe_client.rb +267 -121
  129. data/lib/stripe/stripe_configuration.rb +29 -10
  130. data/lib/stripe/stripe_object.rb +25 -2
  131. data/lib/stripe/stripe_response.rb +80 -52
  132. data/lib/stripe/util.rb +100 -9
  133. data/lib/stripe/version.rb +1 -1
  134. data/lib/stripe.rb +27 -23
  135. data/stripe.gemspec +12 -5
  136. metadata +46 -204
  137. data/.editorconfig +0 -10
  138. data/.gitattributes +0 -4
  139. data/.github/ISSUE_TEMPLATE.md +0 -5
  140. data/.gitignore +0 -8
  141. data/.rubocop.yml +0 -80
  142. data/.rubocop_todo.yml +0 -33
  143. data/.travis.yml +0 -38
  144. data/.vscode/extensions.json +0 -7
  145. data/.vscode/settings.json +0 -8
  146. data/lib/stripe/resources/bitcoin_receiver.rb +0 -24
  147. data/lib/stripe/resources/bitcoin_transaction.rb +0 -15
  148. data/lib/stripe/resources/issuing/card_details.rb +0 -9
  149. data/lib/stripe/resources/order.rb +0 -33
  150. data/lib/stripe/resources/order_return.rb +0 -10
  151. data/lib/stripe/resources/recipient.rb +0 -14
  152. data/lib/stripe/resources/sku.rb +0 -13
  153. data/lib/stripe/resources/three_d_secure.rb +0 -14
  154. data/test/openapi/README.md +0 -9
  155. data/test/stripe/account_link_test.rb +0 -18
  156. data/test/stripe/account_test.rb +0 -412
  157. data/test/stripe/alipay_account_test.rb +0 -37
  158. data/test/stripe/api_operations_test.rb +0 -80
  159. data/test/stripe/api_resource_test.rb +0 -646
  160. data/test/stripe/apple_pay_domain_test.rb +0 -46
  161. data/test/stripe/application_fee_refund_test.rb +0 -37
  162. data/test/stripe/application_fee_test.rb +0 -58
  163. data/test/stripe/balance_test.rb +0 -13
  164. data/test/stripe/balance_transaction_test.rb +0 -20
  165. data/test/stripe/bank_account_test.rb +0 -36
  166. data/test/stripe/billing_portal/session_test.rb +0 -18
  167. data/test/stripe/capability_test.rb +0 -45
  168. data/test/stripe/charge_test.rb +0 -64
  169. data/test/stripe/checkout/session_test.rb +0 -53
  170. data/test/stripe/connection_manager_test.rb +0 -167
  171. data/test/stripe/country_spec_test.rb +0 -20
  172. data/test/stripe/coupon_test.rb +0 -61
  173. data/test/stripe/credit_note_test.rb +0 -90
  174. data/test/stripe/customer_balance_transaction_test.rb +0 -37
  175. data/test/stripe/customer_card_test.rb +0 -48
  176. data/test/stripe/customer_test.rb +0 -226
  177. data/test/stripe/dispute_test.rb +0 -51
  178. data/test/stripe/ephemeral_key_test.rb +0 -93
  179. data/test/stripe/errors_test.rb +0 -53
  180. data/test/stripe/exchange_rate_test.rb +0 -20
  181. data/test/stripe/file_link_test.rb +0 -41
  182. data/test/stripe/file_test.rb +0 -87
  183. data/test/stripe/instrumentation_test.rb +0 -74
  184. data/test/stripe/invoice_item_test.rb +0 -66
  185. data/test/stripe/invoice_line_item_test.rb +0 -8
  186. data/test/stripe/invoice_test.rb +0 -229
  187. data/test/stripe/issuing/authorization_test.rb +0 -72
  188. data/test/stripe/issuing/card_test.rb +0 -74
  189. data/test/stripe/issuing/cardholder_test.rb +0 -53
  190. data/test/stripe/issuing/dispute_test.rb +0 -54
  191. data/test/stripe/issuing/transaction_test.rb +0 -48
  192. data/test/stripe/list_object_test.rb +0 -202
  193. data/test/stripe/login_link_test.rb +0 -37
  194. data/test/stripe/mandate_test.rb +0 -14
  195. data/test/stripe/multipart_encoder_test.rb +0 -130
  196. data/test/stripe/oauth_test.rb +0 -104
  197. data/test/stripe/order_return_test.rb +0 -21
  198. data/test/stripe/order_test.rb +0 -82
  199. data/test/stripe/payment_intent_test.rb +0 -107
  200. data/test/stripe/payment_method_test.rb +0 -84
  201. data/test/stripe/payout_test.rb +0 -72
  202. data/test/stripe/person_test.rb +0 -46
  203. data/test/stripe/plan_test.rb +0 -98
  204. data/test/stripe/price_test.rb +0 -48
  205. data/test/stripe/product_test.rb +0 -58
  206. data/test/stripe/promotion_code_test.rb +0 -42
  207. data/test/stripe/radar/early_fraud_warning_test.rb +0 -22
  208. data/test/stripe/radar/value_list_item_test.rb +0 -48
  209. data/test/stripe/radar/value_list_test.rb +0 -61
  210. data/test/stripe/recipient_test.rb +0 -62
  211. data/test/stripe/refund_test.rb +0 -39
  212. data/test/stripe/reporting/report_run_test.rb +0 -33
  213. data/test/stripe/reporting/report_type_test.rb +0 -22
  214. data/test/stripe/reversal_test.rb +0 -43
  215. data/test/stripe/review_test.rb +0 -27
  216. data/test/stripe/setup_attempt_test.rb +0 -16
  217. data/test/stripe/setup_intent_test.rb +0 -84
  218. data/test/stripe/sigma/scheduled_query_run_test.rb +0 -22
  219. data/test/stripe/sku_test.rb +0 -60
  220. data/test/stripe/source_test.rb +0 -119
  221. data/test/stripe/stripe_client_test.rb +0 -1291
  222. data/test/stripe/stripe_configuration_test.rb +0 -131
  223. data/test/stripe/stripe_object_test.rb +0 -500
  224. data/test/stripe/stripe_response_test.rb +0 -95
  225. data/test/stripe/subscription_item_test.rb +0 -84
  226. data/test/stripe/subscription_schedule_test.rb +0 -82
  227. data/test/stripe/subscription_test.rb +0 -80
  228. data/test/stripe/tax_id_test.rb +0 -31
  229. data/test/stripe/tax_rate_test.rb +0 -43
  230. data/test/stripe/terminal/connection_token_test.rb +0 -16
  231. data/test/stripe/terminal/location_test.rb +0 -68
  232. data/test/stripe/terminal/reader_test.rb +0 -62
  233. data/test/stripe/three_d_secure_test.rb +0 -23
  234. data/test/stripe/topup_test.rb +0 -62
  235. data/test/stripe/transfer_test.rb +0 -88
  236. data/test/stripe/usage_record_summary_test.rb +0 -29
  237. data/test/stripe/util_test.rb +0 -402
  238. data/test/stripe/webhook_endpoint_test.rb +0 -59
  239. data/test/stripe/webhook_test.rb +0 -135
  240. data/test/stripe_mock.rb +0 -78
  241. data/test/stripe_test.rb +0 -132
  242. data/test/test_data.rb +0 -61
  243. data/test/test_helper.rb +0 -77
@@ -1,1291 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require ::File.expand_path("../test_helper", __dir__)
4
-
5
- module Stripe
6
- class StripeClientTest < Test::Unit::TestCase
7
- context ".active_client" do
8
- should "be .default_client outside of #request" do
9
- assert_equal StripeClient.default_client, StripeClient.active_client
10
- end
11
-
12
- should "be active client inside of #request" do
13
- client = StripeClient.new
14
- client.request do
15
- assert_equal client, StripeClient.active_client
16
- end
17
- end
18
- end
19
-
20
- context ".maybe_gc_connection_managers" do
21
- should "garbage collect old connection managers when appropriate" do
22
- stub_request(:post, "#{Stripe.api_base}/v1/path")
23
- .to_return(body: JSON.generate(object: "account"))
24
-
25
- # Make sure we start with a blank slate (state may have been left in
26
- # place by other tests).
27
- StripeClient.clear_all_connection_managers
28
-
29
- # Establish a base time.
30
- t = 0.0
31
-
32
- # And pretend that `StripeClient` was just initialized for the first
33
- # time. (Don't access instance variables like this, but it's tricky to
34
- # test properly otherwise.)
35
- StripeClient.instance_variable_set(:@last_connection_manager_gc, t)
36
-
37
- #
38
- # t
39
- #
40
- Util.stubs(:monotonic_time).returns(t)
41
-
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
49
-
50
- #
51
- # t + StripeClient::CONNECTION_MANAGER_GC_PERIOD - 1
52
- #
53
- # Move time to just *before* garbage collection is eligible to run.
54
- # Nothing should happen.
55
- #
56
- Util.stubs(:monotonic_time).returns(t + StripeClient::CONNECTION_MANAGER_GC_PERIOD - 1)
57
-
58
- assert_equal nil, StripeClient.maybe_gc_connection_managers
59
-
60
- #
61
- # t + StripeClient::CONNECTION_MANAGER_GC_PERIOD + 1
62
- #
63
- # Move time to just *after* garbage collection is eligible to run.
64
- # Garbage collection will run, but because the connection manager is
65
- # not passed its expiry age, it will not be collected. Zero is returned
66
- # to indicate so.
67
- #
68
- Util.stubs(:monotonic_time).returns(t + StripeClient::CONNECTION_MANAGER_GC_PERIOD + 1)
69
-
70
- assert_equal 0, StripeClient.maybe_gc_connection_managers
71
-
72
- #
73
- # t + StripeClient::CONNECTION_MANAGER_GC_LAST_USED_EXPIRY + 1
74
- #
75
- # Move us far enough into the future that we're passed the horizons for
76
- # both a GC run as well as well as the expiry age of a connection
77
- # manager. That means the GC will run and collect the connection
78
- # manager that we created above.
79
- #
80
- Util.stubs(:monotonic_time).returns(t + StripeClient::CONNECTION_MANAGER_GC_LAST_USED_EXPIRY + 1)
81
-
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
87
- end
88
- end
89
-
90
- context ".clear_all_connection_managers" do
91
- should "clear connection managers across all threads" do
92
- stub_request(:post, "#{Stripe.api_base}/path")
93
- .to_return(body: JSON.generate(object: "account"))
94
-
95
- num_threads = 3
96
-
97
- # Poorly named class -- note this is actually a concurrent queue.
98
- recv_queue = Queue.new
99
- send_queue = Queue.new
100
-
101
- threads = num_threads.times.map do |_|
102
- Thread.start do
103
- # Causes a connection manager to be created on this thread and a
104
- # connection within that manager to be created for API access.
105
- manager = StripeClient.default_connection_manager
106
- manager.execute_request(:post, "#{Stripe.api_base}/path")
107
-
108
- # Signal to the main thread we're ready.
109
- recv_queue << true
110
-
111
- # Wait for the main thread to signal continue.
112
- send_queue.pop
113
-
114
- # This check isn't great, but it's otherwise difficult to tell that
115
- # anything happened with just the public-facing API.
116
- assert_equal({}, manager.instance_variable_get(:@active_connections))
117
- end
118
- end
119
-
120
- # Wait for threads to start up.
121
- threads.each { recv_queue.pop }
122
-
123
- # Do the clear (the method we're actually trying to test).
124
- StripeClient.clear_all_connection_managers
125
-
126
- # Tell threads to run their check.
127
- threads.each { send_queue << true }
128
-
129
- # And finally, give all threads time to perform their check.
130
- threads.each(&:join)
131
- end
132
- end
133
-
134
- context ".default_client" do
135
- should "be a StripeClient" do
136
- assert_kind_of StripeClient, StripeClient.default_client
137
- end
138
-
139
- should "be a different client on each thread" do
140
- other_thread_client = nil
141
- thread = Thread.new do
142
- other_thread_client = StripeClient.default_client
143
- end
144
- thread.join
145
- refute_equal StripeClient.default_client, other_thread_client
146
- end
147
- end
148
-
149
- context ".default_connection_manager" do
150
- should "be a ConnectionManager" do
151
- assert_kind_of ConnectionManager,
152
- StripeClient.default_connection_manager
153
- end
154
-
155
- should "be a different connection on each thread" do
156
- other_thread_manager = nil
157
- thread = Thread.new do
158
- other_thread_manager = StripeClient.default_connection_manager
159
- end
160
- thread.join
161
- refute_equal StripeClient.default_connection_manager, other_thread_manager
162
- end
163
- end
164
-
165
- context ".should_retry?" do
166
- setup do
167
- Stripe.stubs(:max_network_retries).returns(2)
168
- end
169
-
170
- should "retry on Errno::ECONNREFUSED" do
171
- assert StripeClient.should_retry?(Errno::ECONNREFUSED.new,
172
- method: :post, num_retries: 0)
173
- end
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
-
195
- should "retry on Net::OpenTimeout" do
196
- assert StripeClient.should_retry?(Net::OpenTimeout.new,
197
- method: :post, num_retries: 0)
198
- end
199
-
200
- should "retry on Net::ReadTimeout" do
201
- assert StripeClient.should_retry?(Net::ReadTimeout.new,
202
- method: :post, num_retries: 0)
203
- end
204
-
205
- should "retry on SocketError" do
206
- assert StripeClient.should_retry?(SocketError.new,
207
- method: :post, num_retries: 0)
208
- end
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
-
232
- should "retry on a 409 Conflict" do
233
- assert StripeClient.should_retry?(Stripe::StripeError.new(http_status: 409),
234
- method: :post, num_retries: 0)
235
- end
236
-
237
- should "retry on a 429 Too Many Requests when lock timeout" do
238
- assert StripeClient.should_retry?(Stripe::StripeError.new(http_status: 429,
239
- code: "lock_timeout"),
240
- method: :post, num_retries: 0)
241
- end
242
-
243
- should "retry on a 500 Internal Server Error when non-POST" do
244
- assert StripeClient.should_retry?(Stripe::StripeError.new(http_status: 500),
245
- method: :get, num_retries: 0)
246
- end
247
-
248
- should "retry on a 503 Service Unavailable" do
249
- assert StripeClient.should_retry?(Stripe::StripeError.new(http_status: 503),
250
- method: :post, num_retries: 0)
251
- end
252
-
253
- should "not retry at maximum count" do
254
- refute StripeClient.should_retry?(RuntimeError.new,
255
- method: :post, num_retries: Stripe.max_network_retries)
256
- end
257
-
258
- should "not retry on a certificate validation error" do
259
- refute StripeClient.should_retry?(OpenSSL::SSL::SSLError.new,
260
- method: :post, num_retries: 0)
261
- end
262
-
263
- should "not retry on a 429 Too Many Requests when not lock timeout" do
264
- refute StripeClient.should_retry?(Stripe::StripeError.new(http_status: 429,
265
- code: "rate_limited"),
266
- method: :post, num_retries: 0)
267
- end
268
-
269
- should "not retry on a 500 Internal Server Error when POST" do
270
- refute StripeClient.should_retry?(Stripe::StripeError.new(http_status: 500),
271
- method: :post, num_retries: 0)
272
- end
273
- end
274
-
275
- context ".sleep_time" do
276
- should "should grow exponentially" do
277
- StripeClient.stubs(:rand).returns(1)
278
- Stripe.stubs(:max_network_retry_delay).returns(999)
279
- assert_equal(Stripe.initial_network_retry_delay, StripeClient.sleep_time(1))
280
- assert_equal(Stripe.initial_network_retry_delay * 2, StripeClient.sleep_time(2))
281
- assert_equal(Stripe.initial_network_retry_delay * 4, StripeClient.sleep_time(3))
282
- assert_equal(Stripe.initial_network_retry_delay * 8, StripeClient.sleep_time(4))
283
- end
284
-
285
- should "enforce the max_network_retry_delay" do
286
- StripeClient.stubs(:rand).returns(1)
287
- Stripe.stubs(:initial_network_retry_delay).returns(1)
288
- Stripe.stubs(:max_network_retry_delay).returns(2)
289
- assert_equal(1, StripeClient.sleep_time(1))
290
- assert_equal(2, StripeClient.sleep_time(2))
291
- assert_equal(2, StripeClient.sleep_time(3))
292
- assert_equal(2, StripeClient.sleep_time(4))
293
- end
294
-
295
- should "add some randomness" do
296
- random_value = 0.8
297
- StripeClient.stubs(:rand).returns(random_value)
298
- Stripe.stubs(:initial_network_retry_delay).returns(1)
299
- Stripe.stubs(:max_network_retry_delay).returns(8)
300
-
301
- base_value = Stripe.initial_network_retry_delay * (0.5 * (1 + random_value))
302
-
303
- # the initial value cannot be smaller than the base,
304
- # so the randomness is ignored
305
- assert_equal(Stripe.initial_network_retry_delay, StripeClient.sleep_time(1))
306
-
307
- # after the first one, the randomness is applied
308
- assert_equal(base_value * 2, StripeClient.sleep_time(2))
309
- assert_equal(base_value * 4, StripeClient.sleep_time(3))
310
- assert_equal(base_value * 8, StripeClient.sleep_time(4))
311
- end
312
- end
313
-
314
- context "#execute_request" do
315
- context "headers" do
316
- should "support literal headers" do
317
- stub_request(:post, "#{Stripe.api_base}/v1/account")
318
- .with(headers: { "Stripe-Account" => "bar" })
319
- .to_return(body: JSON.generate(object: "account"))
320
-
321
- client = StripeClient.new
322
- client.execute_request(:post, "/v1/account",
323
- headers: { "Stripe-Account" => "bar" })
324
- end
325
-
326
- should "support RestClient-style header keys" do
327
- stub_request(:post, "#{Stripe.api_base}/v1/account")
328
- .with(headers: { "Stripe-Account" => "bar" })
329
- .to_return(body: JSON.generate(object: "account"))
330
-
331
- client = StripeClient.new
332
- client.execute_request(:post, "/v1/account",
333
- headers: { stripe_account: "bar" })
334
- end
335
- end
336
-
337
- context "logging" do
338
- setup do
339
- # Freeze time for the purposes of the `elapsed` parameter that we
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)
345
- end
346
-
347
- should "produce appropriate logging" do
348
- body = JSON.generate(object: "account")
349
-
350
- Util.expects(:log_info).with("Request to Stripe API",
351
- account: "acct_123",
352
- api_version: "2010-11-12",
353
- idempotency_key: "abc",
354
- method: :post,
355
- num_retries: 0,
356
- path: "/v1/account")
357
- Util.expects(:log_debug).with("Request details",
358
- body: "",
359
- idempotency_key: "abc",
360
- query: nil)
361
-
362
- Util.expects(:log_info).with("Response from Stripe API",
363
- account: "acct_123",
364
- api_version: "2010-11-12",
365
- elapsed: 0.0,
366
- idempotency_key: "abc",
367
- method: :post,
368
- path: "/v1/account",
369
- request_id: "req_123",
370
- status: 200)
371
- Util.expects(:log_debug).with("Response details",
372
- body: body,
373
- idempotency_key: "abc",
374
- request_id: "req_123")
375
- Util.expects(:log_debug).with("Dashboard link for request",
376
- idempotency_key: "abc",
377
- request_id: "req_123",
378
- url: Util.request_id_dashboard_url("req_123", Stripe.api_key))
379
-
380
- stub_request(:post, "#{Stripe.api_base}/v1/account")
381
- .to_return(
382
- body: body,
383
- headers: {
384
- "Idempotency-Key" => "abc",
385
- "Request-Id" => "req_123",
386
- "Stripe-Account" => "acct_123",
387
- "Stripe-Version" => "2010-11-12",
388
- }
389
- )
390
-
391
- client = StripeClient.new
392
- client.execute_request(:post, "/v1/account",
393
- headers: {
394
- "Idempotency-Key" => "abc",
395
- "Stripe-Account" => "acct_123",
396
- "Stripe-Version" => "2010-11-12",
397
- })
398
- end
399
-
400
- should "produce logging on API error" do
401
- Util.expects(:log_info).with("Request to Stripe API",
402
- account: nil,
403
- api_version: nil,
404
- idempotency_key: nil,
405
- method: :post,
406
- num_retries: 0,
407
- path: "/v1/account")
408
- Util.expects(:log_info).with("Response from Stripe API",
409
- account: nil,
410
- api_version: nil,
411
- elapsed: 0.0,
412
- idempotency_key: nil,
413
- method: :post,
414
- path: "/v1/account",
415
- request_id: nil,
416
- status: 500)
417
-
418
- error = {
419
- code: "code",
420
- message: "message",
421
- param: "param",
422
- type: "type",
423
- }
424
- Util.expects(:log_error).with("Stripe API error",
425
- status: 500,
426
- error_code: error[:code],
427
- error_message: error[:message],
428
- error_param: error[:param],
429
- error_type: error[:type],
430
- idempotency_key: nil,
431
- request_id: nil)
432
-
433
- stub_request(:post, "#{Stripe.api_base}/v1/account")
434
- .to_return(
435
- body: JSON.generate(error: error),
436
- status: 500
437
- )
438
-
439
- client = StripeClient.new
440
- assert_raises Stripe::APIError do
441
- client.execute_request(:post, "/v1/account")
442
- end
443
- end
444
-
445
- should "produce logging on OAuth error" do
446
- Util.expects(:log_info).with("Request to Stripe API",
447
- account: nil,
448
- api_version: nil,
449
- idempotency_key: nil,
450
- method: :post,
451
- num_retries: 0,
452
- path: "/oauth/token")
453
- Util.expects(:log_info).with("Response from Stripe API",
454
- account: nil,
455
- api_version: nil,
456
- elapsed: 0.0,
457
- idempotency_key: nil,
458
- method: :post,
459
- path: "/oauth/token",
460
- request_id: nil,
461
- status: 400)
462
-
463
- Util.expects(:log_error).with("Stripe OAuth error",
464
- status: 400,
465
- error_code: "invalid_request",
466
- error_description: "No grant type specified",
467
- idempotency_key: nil,
468
- request_id: nil)
469
-
470
- stub_request(:post, "#{Stripe.connect_base}/oauth/token")
471
- .to_return(body: JSON.generate(error: "invalid_request",
472
- error_description: "No grant type specified"), status: 400)
473
-
474
- client = StripeClient.new
475
- opts = { api_base: Stripe.connect_base }
476
- assert_raises Stripe::OAuth::InvalidRequestError do
477
- client.execute_request(:post, "/oauth/token", **opts)
478
- end
479
- end
480
- end
481
-
482
- context "Stripe-Account header" do
483
- should "use a globally set header" do
484
- begin
485
- old = Stripe.stripe_account
486
- Stripe.stripe_account = "acct_1234"
487
-
488
- stub_request(:post, "#{Stripe.api_base}/v1/account")
489
- .with(headers: { "Stripe-Account" => Stripe.stripe_account })
490
- .to_return(body: JSON.generate(object: "account"))
491
-
492
- client = StripeClient.new
493
- client.execute_request(:post, "/v1/account")
494
- ensure
495
- Stripe.stripe_account = old
496
- end
497
- end
498
-
499
- should "use a locally set header" do
500
- stripe_account = "acct_0000"
501
- stub_request(:post, "#{Stripe.api_base}/v1/account")
502
- .with(headers: { "Stripe-Account" => stripe_account })
503
- .to_return(body: JSON.generate(object: "account"))
504
-
505
- client = StripeClient.new
506
- client.execute_request(:post, "/v1/account",
507
- headers: { stripe_account: stripe_account })
508
- end
509
-
510
- should "not send it otherwise" do
511
- stub_request(:post, "#{Stripe.api_base}/v1/account")
512
- .with do |req|
513
- req.headers["Stripe-Account"].nil?
514
- end.to_return(body: JSON.generate(object: "account"))
515
-
516
- client = StripeClient.new
517
- client.execute_request(:post, "/v1/account")
518
- end
519
- end
520
-
521
- context "app_info" do
522
- should "send app_info if set" do
523
- begin
524
- old = Stripe.app_info
525
- Stripe.set_app_info(
526
- "MyAwesomePlugin",
527
- partner_id: "partner_1234",
528
- url: "https://myawesomeplugin.info",
529
- version: "1.2.34"
530
- )
531
-
532
- stub_request(:post, "#{Stripe.api_base}/v1/account")
533
- .with do |req|
534
- assert_equal \
535
- "Stripe/v1 RubyBindings/#{Stripe::VERSION} " \
536
- "MyAwesomePlugin/1.2.34 (https://myawesomeplugin.info)",
537
- req.headers["User-Agent"]
538
-
539
- data = JSON.parse(req.headers["X-Stripe-Client-User-Agent"],
540
- symbolize_names: true)
541
-
542
- assert_equal({
543
- name: "MyAwesomePlugin",
544
- partner_id: "partner_1234",
545
- url: "https://myawesomeplugin.info",
546
- version: "1.2.34",
547
- }, data[:application])
548
-
549
- true
550
- end.to_return(body: JSON.generate(object: "account"))
551
-
552
- client = StripeClient.new
553
- client.execute_request(:post, "/v1/account")
554
- ensure
555
- Stripe.app_info = old
556
- end
557
- end
558
- end
559
-
560
- context "error handling" do
561
- should "handle error response with empty body" do
562
- stub_request(:post, "#{Stripe.api_base}/v1/charges")
563
- .to_return(body: "", status: 500)
564
-
565
- client = StripeClient.new
566
-
567
- e = assert_raises Stripe::APIError do
568
- client.execute_request(:post, "/v1/charges")
569
- end
570
- assert_equal 'Invalid response object from API: "" (HTTP response code was 500)', e.message
571
- end
572
-
573
- should "handle success response with empty body" do
574
- stub_request(:post, "#{Stripe.api_base}/v1/charges")
575
- .to_return(body: "", status: 200)
576
-
577
- client = StripeClient.new
578
-
579
- e = assert_raises Stripe::APIError do
580
- client.execute_request(:post, "/v1/charges")
581
- end
582
- assert_equal 'Invalid response object from API: "" (HTTP response code was 200)', e.message
583
- end
584
-
585
- should "feed a request ID through to the error object" do
586
- stub_request(:post, "#{Stripe.api_base}/v1/charges")
587
- .to_return(body: JSON.generate(make_missing_id_error),
588
- headers: { "Request-ID": "req_123" },
589
- status: 400)
590
-
591
- client = StripeClient.new
592
-
593
- e = assert_raises Stripe::InvalidRequestError do
594
- client.execute_request(:post, "/v1/charges")
595
- end
596
- assert_equal("req_123", e.request_id)
597
- end
598
-
599
- should "handle low level error" do
600
- stub_request(:post, "#{Stripe.api_base}/v1/charges")
601
- .to_raise(Errno::ECONNREFUSED.new)
602
-
603
- client = StripeClient.new
604
-
605
- e = assert_raises Stripe::APIConnectionError do
606
- client.execute_request(:post, "/v1/charges")
607
- end
608
- assert_equal StripeClient::ERROR_MESSAGE_CONNECTION % Stripe.api_base +
609
- "\n\n(Network error: Connection refused)",
610
- e.message
611
- end
612
-
613
- should "handle error response with unknown value" do
614
- stub_request(:post, "#{Stripe.api_base}/v1/charges")
615
- .to_return(body: JSON.generate(bar: "foo"), status: 500)
616
-
617
- client = StripeClient.new
618
-
619
- e = assert_raises Stripe::APIError do
620
- client.execute_request(:post, "/v1/charges")
621
- end
622
- assert_equal 'Invalid response object from API: "{\"bar\":\"foo\"}" (HTTP response code was 500)', e.message
623
- end
624
-
625
- should "raise IdempotencyError on 400 of type idempotency_error" do
626
- data = make_missing_id_error
627
- data[:error][:type] = "idempotency_error"
628
-
629
- stub_request(:post, "#{Stripe.api_base}/v1/charges")
630
- .to_return(body: JSON.generate(data), status: 400)
631
-
632
- client = StripeClient.new
633
-
634
- e = assert_raises Stripe::IdempotencyError do
635
- client.execute_request(:post, "/v1/charges")
636
- end
637
- assert_equal(400, e.http_status)
638
- assert_equal(true, e.json_body.is_a?(Hash))
639
- end
640
-
641
- should "raise InvalidRequestError on other 400s" do
642
- stub_request(:post, "#{Stripe.api_base}/v1/charges")
643
- .to_return(body: JSON.generate(make_missing_id_error), status: 400)
644
-
645
- client = StripeClient.new
646
-
647
- e = assert_raises Stripe::InvalidRequestError do
648
- client.execute_request(:post, "/v1/charges")
649
- end
650
- assert_equal(400, e.http_status)
651
- assert_equal(true, e.json_body.is_a?(Hash))
652
- end
653
-
654
- should "raise AuthenticationError on 401" do
655
- stub_request(:post, "#{Stripe.api_base}/v1/charges")
656
- .to_return(body: JSON.generate(make_missing_id_error), status: 401)
657
-
658
- client = StripeClient.new
659
-
660
- e = assert_raises Stripe::AuthenticationError do
661
- client.execute_request(:post, "/v1/charges")
662
- end
663
- assert_equal(401, e.http_status)
664
- assert_equal(true, e.json_body.is_a?(Hash))
665
- end
666
-
667
- should "raise CardError on 402" do
668
- stub_request(:post, "#{Stripe.api_base}/v1/charges")
669
- .to_return(body: JSON.generate(make_invalid_exp_year_error), status: 402)
670
-
671
- client = StripeClient.new
672
-
673
- e = assert_raises Stripe::CardError do
674
- client.execute_request(:post, "/v1/charges")
675
- end
676
- assert_equal(402, e.http_status)
677
- assert_equal(true, e.json_body.is_a?(Hash))
678
- assert_equal("invalid_expiry_year", e.code)
679
- assert_equal("exp_year", e.param)
680
- end
681
-
682
- should "raise PermissionError on 403" do
683
- stub_request(:post, "#{Stripe.api_base}/v1/charges")
684
- .to_return(body: JSON.generate(make_missing_id_error), status: 403)
685
-
686
- client = StripeClient.new
687
-
688
- e = assert_raises Stripe::PermissionError do
689
- client.execute_request(:post, "/v1/charges")
690
- end
691
- assert_equal(403, e.http_status)
692
- assert_equal(true, e.json_body.is_a?(Hash))
693
- end
694
-
695
- should "raise InvalidRequestError on 404" do
696
- stub_request(:post, "#{Stripe.api_base}/v1/charges")
697
- .to_return(body: JSON.generate(make_missing_id_error), status: 404)
698
-
699
- client = StripeClient.new
700
-
701
- e = assert_raises Stripe::InvalidRequestError do
702
- client.execute_request(:post, "/v1/charges")
703
- end
704
- assert_equal(404, e.http_status)
705
- assert_equal(true, e.json_body.is_a?(Hash))
706
- end
707
-
708
- should "raise RateLimitError on 429" do
709
- stub_request(:post, "#{Stripe.api_base}/v1/charges")
710
- .to_return(body: JSON.generate(make_rate_limit_error), status: 429)
711
-
712
- client = StripeClient.new
713
-
714
- e = assert_raises Stripe::RateLimitError do
715
- client.execute_request(:post, "/v1/charges")
716
- end
717
- assert_equal(429, e.http_status)
718
- assert_equal(true, e.json_body.is_a?(Hash))
719
- end
720
-
721
- should "raise OAuth::InvalidRequestError when error is a string with value 'invalid_request'" do
722
- stub_request(:post, "#{Stripe.connect_base}/oauth/token")
723
- .to_return(body: JSON.generate(error: "invalid_request",
724
- error_description: "No grant type specified"), status: 400)
725
-
726
- client = StripeClient.new
727
-
728
- opts = { api_base: Stripe.connect_base }
729
- e = assert_raises Stripe::OAuth::InvalidRequestError do
730
- client.execute_request(:post, "/oauth/token", **opts)
731
- end
732
-
733
- assert_equal(400, e.http_status)
734
- assert_equal("No grant type specified", e.message)
735
- end
736
-
737
- should "raise OAuth::InvalidGrantError when error is a string with value 'invalid_grant'" do
738
- stub_request(:post, "#{Stripe.connect_base}/oauth/token")
739
- .to_return(body: JSON.generate(error: "invalid_grant",
740
- error_description: "This authorization code has already been used. All tokens issued with this code have been revoked."), status: 400)
741
-
742
- client = StripeClient.new
743
-
744
- opts = { api_base: Stripe.connect_base }
745
- e = assert_raises Stripe::OAuth::InvalidGrantError do
746
- client.execute_request(:post, "/oauth/token", **opts)
747
- end
748
-
749
- assert_equal(400, e.http_status)
750
- assert_equal("invalid_grant", e.code)
751
- assert_equal("This authorization code has already been used. All tokens issued with this code have been revoked.", e.message)
752
- end
753
-
754
- should "raise OAuth::InvalidClientError when error is a string with value 'invalid_client'" do
755
- stub_request(:post, "#{Stripe.connect_base}/oauth/deauthorize")
756
- .to_return(body: JSON.generate(error: "invalid_client",
757
- error_description: "This application is not connected to stripe account acct_19tLK7DSlTMT26Mk, or that account does not exist."), status: 401)
758
-
759
- client = StripeClient.new
760
-
761
- opts = { api_base: Stripe.connect_base }
762
- e = assert_raises Stripe::OAuth::InvalidClientError do
763
- client.execute_request(:post, "/oauth/deauthorize", **opts)
764
- end
765
-
766
- assert_equal(401, e.http_status)
767
- assert_equal("invalid_client", e.code)
768
- assert_equal("This application is not connected to stripe account acct_19tLK7DSlTMT26Mk, or that account does not exist.", e.message)
769
- end
770
-
771
- should "raise Stripe::OAuthError on indeterminate OAuth error" do
772
- stub_request(:post, "#{Stripe.connect_base}/oauth/deauthorize")
773
- .to_return(body: JSON.generate(error: "new_code_not_recognized",
774
- error_description: "Something."), status: 401)
775
-
776
- client = StripeClient.new
777
-
778
- opts = { api_base: Stripe.connect_base }
779
- e = assert_raises Stripe::OAuth::OAuthError do
780
- client.execute_request(:post, "/oauth/deauthorize", **opts)
781
- end
782
-
783
- assert_equal(401, e.http_status)
784
- assert_equal("new_code_not_recognized", e.code)
785
- assert_equal("Something.", e.message)
786
- end
787
- end
788
-
789
- context "idempotency keys" do
790
- setup do
791
- Stripe.stubs(:max_network_retries).returns(2)
792
- end
793
-
794
- should "not add an idempotency key to GET requests" do
795
- SecureRandom.expects(:uuid).times(0)
796
- stub_request(:get, "#{Stripe.api_base}/v1/charges/ch_123")
797
- .with do |req|
798
- req.headers["Idempotency-Key"].nil?
799
- end.to_return(body: JSON.generate(object: "charge"))
800
- client = StripeClient.new
801
- client.execute_request(:get, "/v1/charges/ch_123")
802
- end
803
-
804
- should "ensure there is always an idempotency_key on POST requests" do
805
- SecureRandom.expects(:uuid).at_least_once.returns("random_key")
806
- stub_request(:post, "#{Stripe.api_base}/v1/charges")
807
- .with(headers: { "Idempotency-Key" => "random_key" })
808
- .to_return(body: JSON.generate(object: "charge"))
809
- client = StripeClient.new
810
- client.execute_request(:post, "/v1/charges")
811
- end
812
-
813
- should "ensure there is always an idempotency_key on DELETE requests" do
814
- SecureRandom.expects(:uuid).at_least_once.returns("random_key")
815
- stub_request(:delete, "#{Stripe.api_base}/v1/charges/ch_123")
816
- .with(headers: { "Idempotency-Key" => "random_key" })
817
- .to_return(body: JSON.generate(object: "charge"))
818
- client = StripeClient.new
819
- client.execute_request(:delete, "/v1/charges/ch_123")
820
- end
821
-
822
- should "not override a provided idempotency_key" do
823
- # Note that this expectation looks like `:idempotency_key` instead of
824
- # the header `Idempotency-Key` because it's user provided as seen
825
- # below. The ones injected by the library itself look like headers
826
- # (`Idempotency-Key`), but rest-client does allow this symbol
827
- # formatting and will properly override the system generated one as
828
- # expected.
829
- stub_request(:post, "#{Stripe.api_base}/v1/charges")
830
- .with(headers: { "Idempotency-Key" => "provided_key" })
831
- .to_return(body: JSON.generate(object: "charge"))
832
-
833
- client = StripeClient.new
834
- client.execute_request(:post, "/v1/charges",
835
- headers: { idempotency_key: "provided_key" })
836
- end
837
- end
838
-
839
- context "retry logic" do
840
- setup do
841
- Stripe.stubs(:max_network_retries).returns(2)
842
- end
843
-
844
- should "retry failed requests and raise if error persists" do
845
- StripeClient.expects(:sleep_time).at_least_once.returns(0)
846
- stub_request(:post, "#{Stripe.api_base}/v1/charges")
847
- .to_raise(Errno::ECONNREFUSED.new)
848
-
849
- client = StripeClient.new
850
- err = assert_raises Stripe::APIConnectionError do
851
- client.execute_request(:post, "/v1/charges")
852
- end
853
- assert_match(/Request was retried 2 times/, err.message)
854
- end
855
-
856
- should "retry failed requests and return successful response" do
857
- StripeClient.expects(:sleep_time).at_least_once.returns(0)
858
-
859
- i = 0
860
- stub_request(:post, "#{Stripe.api_base}/v1/charges")
861
- .to_return do |_|
862
- if i < 2
863
- i += 1
864
- raise Errno::ECONNREFUSED
865
- else
866
- { body: JSON.generate("id" => "myid") }
867
- end
868
- end
869
-
870
- client = StripeClient.new
871
- client.execute_request(:post, "/v1/charges")
872
- end
873
- end
874
-
875
- context "params serialization" do
876
- should "allows empty strings in params" do
877
- client = StripeClient.new
878
- client.execute_request(:get, "/v1/invoices/upcoming", params: {
879
- customer: "cus_123",
880
- coupon: "",
881
- })
882
- assert_requested(
883
- :get,
884
- "#{Stripe.api_base}/v1/invoices/upcoming?",
885
- query: {
886
- customer: "cus_123",
887
- coupon: "",
888
- }
889
- )
890
- end
891
-
892
- should "filter nils in params" do
893
- client = StripeClient.new
894
- client.execute_request(:get, "/v1/invoices/upcoming", params: {
895
- customer: "cus_123",
896
- coupon: nil,
897
- })
898
- assert_requested(
899
- :get,
900
- "#{Stripe.api_base}/v1/invoices/upcoming?",
901
- query: {
902
- customer: "cus_123",
903
- }
904
- )
905
- end
906
-
907
- should "merge query parameters in URL and params" do
908
- client = StripeClient.new
909
- client.execute_request(:get, "/v1/invoices/upcoming?coupon=25OFF", params: {
910
- customer: "cus_123",
911
- })
912
- assert_requested(
913
- :get,
914
- "#{Stripe.api_base}/v1/invoices/upcoming?",
915
- query: {
916
- coupon: "25OFF",
917
- customer: "cus_123",
918
- }
919
- )
920
- end
921
-
922
- should "prefer query parameters in params when specified in URL as well" do
923
- client = StripeClient.new
924
- client.execute_request(:get, "/v1/invoices/upcoming?customer=cus_query", params: {
925
- customer: "cus_param",
926
- })
927
- assert_requested(
928
- :get,
929
- "#{Stripe.api_base}/v1/invoices/upcoming?",
930
- query: {
931
- customer: "cus_param",
932
- }
933
- )
934
- end
935
- end
936
- end
937
-
938
- context "#connection_manager" do
939
- should "warn that #connection_manager is deprecated" do
940
- old_stderr = $stderr
941
- $stderr = StringIO.new
942
- begin
943
- client = StripeClient.new
944
- client.connection_manager
945
- message = "NOTE: Stripe::StripeClient#connection_manager is " \
946
- "deprecated"
947
- assert_match Regexp.new(message), $stderr.string
948
- ensure
949
- $stderr = old_stderr
950
- end
951
- end
952
- end
953
-
954
- context "#request" do
955
- should "return a result and response object" do
956
- stub_request(:post, "#{Stripe.api_base}/v1/charges")
957
- .to_return(body: JSON.generate(object: "charge"))
958
-
959
- client = StripeClient.new
960
- charge, resp = client.request { Charge.create }
961
-
962
- assert charge.is_a?(Charge)
963
- assert resp.is_a?(StripeResponse)
964
- assert_equal 200, resp.http_status
965
- end
966
-
967
- should "return the value of given block" do
968
- client = StripeClient.new
969
- ret, = client.request { 7 }
970
- assert_equal 7, ret
971
- end
972
-
973
- should "reset local thread state after a call" do
974
- begin
975
- StripeClient.current_thread_context.active_client = :stripe_client
976
-
977
- client = StripeClient.new
978
- client.request {}
979
-
980
- assert_equal :stripe_client,
981
- StripeClient.current_thread_context.active_client
982
- ensure
983
- StripeClient.current_thread_context.active_client = nil
984
- end
985
- end
986
-
987
- should "correctly return last responses despite multiple clients" do
988
- charge_resp = { object: "charge" }
989
- coupon_resp = { object: "coupon" }
990
-
991
- stub_request(:post, "#{Stripe.api_base}/v1/charges")
992
- .to_return(body: JSON.generate(charge_resp))
993
- stub_request(:post, "#{Stripe.api_base}/v1/coupons")
994
- .to_return(body: JSON.generate(coupon_resp))
995
-
996
- client1 = StripeClient.new
997
- client2 = StripeClient.new
998
-
999
- client2_resp = nil
1000
- _charge, client1_resp = client1.request do
1001
- Charge.create
1002
-
1003
- # This is contrived, but we run one client nested in the `request`
1004
- # block of another one just to ensure that the parent is still
1005
- # unwinding when this goes through. If the parent's last response
1006
- # were to be overridden by this client (through a bug), then it would
1007
- # happen here.
1008
- _coupon, client2_resp = client2.request do
1009
- Coupon.create
1010
- end
1011
- end
1012
-
1013
- assert_equal charge_resp, client1_resp.data
1014
- assert_equal coupon_resp, client2_resp.data
1015
- end
1016
-
1017
- should "correctly return last responses despite multiple threads" do
1018
- charge_resp = { object: "charge" }
1019
- coupon_resp = { object: "coupon" }
1020
-
1021
- stub_request(:post, "#{Stripe.api_base}/v1/charges")
1022
- .to_return(body: JSON.generate(charge_resp))
1023
- stub_request(:post, "#{Stripe.api_base}/v1/coupons")
1024
- .to_return(body: JSON.generate(coupon_resp))
1025
-
1026
- client = StripeClient.new
1027
-
1028
- # Poorly named class -- note this is actually a concurrent queue.
1029
- recv_queue = Queue.new
1030
- send_queue = Queue.new
1031
-
1032
- # Start a thread, make an API request, but then idle in the `request`
1033
- # block until the main thread has been able to make its own API request
1034
- # and signal that it's done. If this thread's last response were to be
1035
- # overridden by the main thread (through a bug), then this routine
1036
- # should suss it out.
1037
- resp1 = nil
1038
- thread = Thread.start do
1039
- _charge, resp1 = client.request do
1040
- Charge.create
1041
-
1042
- # Idle in `request` block until main thread signals.
1043
- send_queue.pop
1044
- end
1045
-
1046
- # Signal main thread that we're done and it can run its checks.
1047
- recv_queue << true
1048
- end
1049
-
1050
- # Make an API request.
1051
- _coupon, resp2 = client.request do
1052
- Coupon.create
1053
- end
1054
-
1055
- # Tell background thread to finish `request`, then wait for it to
1056
- # signal back to us that it's ready.
1057
- send_queue << true
1058
- recv_queue.pop
1059
-
1060
- assert_equal charge_resp, resp1.data
1061
- assert_equal coupon_resp, resp2.data
1062
-
1063
- # And for maximum hygiene, make sure that our thread rejoins.
1064
- thread.join
1065
- end
1066
-
1067
- should "error if calls to #request are nested on the same thread" do
1068
- client = StripeClient.new
1069
- client.request do
1070
- e = assert_raises(RuntimeError) do
1071
- client.request {}
1072
- end
1073
- assert_equal "calls to StripeClient#request cannot be nested within a thread",
1074
- e.message
1075
- end
1076
- end
1077
- end
1078
-
1079
- context "#proxy" do
1080
- should "run the request through the proxy" do
1081
- begin
1082
- StripeClient.current_thread_context.default_connection_manager = nil
1083
-
1084
- Stripe.proxy = "http://user:pass@localhost:8080"
1085
-
1086
- client = StripeClient.new
1087
- client.request {}
1088
-
1089
- connection = Stripe::StripeClient.default_connection_manager.connection_for(Stripe.api_base)
1090
-
1091
- assert_equal "localhost", connection.proxy_address
1092
- assert_equal 8080, connection.proxy_port
1093
- assert_equal "user", connection.proxy_user
1094
- assert_equal "pass", connection.proxy_pass
1095
- ensure
1096
- Stripe.proxy = nil
1097
-
1098
- StripeClient.current_thread_context.default_connection_manager = nil
1099
- end
1100
- end
1101
- end
1102
-
1103
- context "#telemetry" do
1104
- teardown do
1105
- # make sure to always set telemetry back to false
1106
- # to not mutate global state
1107
- Stripe.enable_telemetry = false
1108
- end
1109
-
1110
- should "not send metrics if enable trace flag is not set" do
1111
- Stripe.enable_telemetry = false
1112
-
1113
- trace_metrics_header = nil
1114
- stub_request(:get, "#{Stripe.api_base}/v1/charges")
1115
- .with do |req|
1116
- trace_metrics_header = req.headers["X-Stripe-Client-Telemetry"]
1117
- false
1118
- end.to_return(body: JSON.generate(object: "charge"))
1119
-
1120
- Stripe::Charge.list
1121
- assert(trace_metrics_header.nil?)
1122
-
1123
- Stripe::Charge.list
1124
- assert(trace_metrics_header.nil?)
1125
- end
1126
-
1127
- should "send metrics if enabled telemetry is true" do
1128
- Stripe.enable_telemetry = true
1129
-
1130
- trace_metrics_header = nil
1131
- stub_request(:get, "#{Stripe.api_base}/v1/charges")
1132
- .with do |req|
1133
- trace_metrics_header = req.headers["X-Stripe-Client-Telemetry"]
1134
- false
1135
- end.to_return(body: JSON.generate(object: "charge"))
1136
-
1137
- Stripe::Charge.list
1138
- Stripe::Charge.list
1139
-
1140
- assert(!trace_metrics_header.nil?)
1141
-
1142
- trace_payload = JSON.parse(trace_metrics_header)
1143
- assert(trace_payload["last_request_metrics"]["request_id"] == "req_123")
1144
- assert(!trace_payload["last_request_metrics"]["request_duration_ms"].nil?)
1145
- end
1146
- end
1147
-
1148
- context "instrumentation" do
1149
- teardown do
1150
- Stripe::Instrumentation.unsubscribe(:request, :test)
1151
- end
1152
-
1153
- should "notify a subscribe on HTTP request start" do
1154
- events = []
1155
- Stripe::Instrumentation.subscribe(:request_end, :test) { |event| events << event }
1156
-
1157
- stub_request(:get, "#{Stripe.api_base}/v1/charges")
1158
- .to_return(body: JSON.generate(object: "charge"))
1159
- Stripe::Charge.list
1160
-
1161
- assert_equal(1, events.size)
1162
- event = events.first
1163
- assert_equal(:get, event.method)
1164
- assert_equal("/v1/charges", event.path)
1165
- end
1166
-
1167
- should "notify a subscriber of a successful HTTP request" do
1168
- events = []
1169
- Stripe::Instrumentation.subscribe(:request_end, :test) { |event| events << event }
1170
-
1171
- stub_request(:get, "#{Stripe.api_base}/v1/charges")
1172
- .to_return(body: JSON.generate(object: "charge"))
1173
- Stripe::Charge.list
1174
-
1175
- assert_equal(1, events.size)
1176
- event = events.first
1177
- assert_equal(:get, event.method)
1178
- assert_equal("/v1/charges", event.path)
1179
- assert_equal(200, event.http_status)
1180
- assert(event.duration.positive?)
1181
- assert_equal(0, event.num_retries)
1182
- end
1183
-
1184
- should "notify a subscriber of a StripeError" do
1185
- events = []
1186
- Stripe::Instrumentation.subscribe(:request_end, :test) { |event| events << event }
1187
-
1188
- error = {
1189
- code: "code",
1190
- message: "message",
1191
- param: "param",
1192
- type: "type",
1193
- }
1194
- stub_request(:get, "#{Stripe.api_base}/v1/charges")
1195
- .to_return(
1196
- body: JSON.generate(error: error),
1197
- status: 500
1198
- )
1199
- assert_raises(Stripe::APIError) do
1200
- Stripe::Charge.list
1201
- end
1202
-
1203
- assert_equal(1, events.size)
1204
- event = events.first
1205
- assert_equal(:get, event.method)
1206
- assert_equal("/v1/charges", event.path)
1207
- assert_equal(500, event.http_status)
1208
- assert(event.duration.positive?)
1209
- assert_equal(0, event.num_retries)
1210
- end
1211
-
1212
- should "notify a subscriber of a network error" do
1213
- events = []
1214
- Stripe::Instrumentation.subscribe(:request_end, :test) { |event| events << event }
1215
-
1216
- stub_request(:get, "#{Stripe.api_base}/v1/charges")
1217
- .to_raise(Net::OpenTimeout)
1218
- assert_raises(Stripe::APIConnectionError) do
1219
- Stripe::Charge.list
1220
- end
1221
-
1222
- assert_equal(1, events.size)
1223
- event = events.first
1224
- assert_equal(:get, event.method)
1225
- assert_equal("/v1/charges", event.path)
1226
- assert_nil(event.http_status)
1227
- assert(event.duration.positive?)
1228
- assert_equal(0, event.num_retries)
1229
- end
1230
-
1231
- should "pass `user_data` from `request_begin` to `request_end`" do
1232
- actual_user_data = nil
1233
-
1234
- Stripe::Instrumentation.subscribe(:request_begin) do |event|
1235
- event.user_data[:foo] = :bar
1236
- end
1237
- Stripe::Instrumentation.subscribe(:request_end) do |event|
1238
- actual_user_data = event.user_data
1239
- end
1240
-
1241
- stub_request(:get, "#{Stripe.api_base}/v1/charges")
1242
- .to_return(body: JSON.generate(object: "charge"))
1243
- Stripe::Charge.list
1244
-
1245
- assert_equal({ foo: :bar }, actual_user_data)
1246
- end
1247
-
1248
- should "provide backward compatibility on `request` topic" do
1249
- events = []
1250
- Stripe::Instrumentation.subscribe(:request, :test) { |event| events << event }
1251
-
1252
- stub_request(:get, "#{Stripe.api_base}/v1/charges")
1253
- .to_return(body: JSON.generate(object: "charge"))
1254
- Stripe::Charge.list
1255
-
1256
- assert_equal(1, events.size)
1257
- event = events.first
1258
- assert_equal(:get, event.method)
1259
- assert_equal("/v1/charges", event.path)
1260
- assert_equal(200, event.http_status)
1261
- assert(event.duration.positive?)
1262
- assert_equal(0, event.num_retries)
1263
- end
1264
- end
1265
- end
1266
-
1267
- class SystemProfilerTest < Test::Unit::TestCase
1268
- context "#uname" do
1269
- should "run without failure" do
1270
- # Don't actually check the result because we try a variety of different
1271
- # strategies that will have different results depending on where this
1272
- # test and running. We're mostly making sure that no exception is thrown.
1273
- _ = StripeClient::SystemProfiler.uname
1274
- end
1275
- end
1276
-
1277
- context "#uname_from_system" do
1278
- should "run without failure" do
1279
- # as above, just verify that an exception is not thrown
1280
- _ = StripeClient::SystemProfiler.uname_from_system
1281
- end
1282
- end
1283
-
1284
- context "#uname_from_system_ver" do
1285
- should "run without failure" do
1286
- # as above, just verify that an exception is not thrown
1287
- _ = StripeClient::SystemProfiler.uname_from_system_ver
1288
- end
1289
- end
1290
- end
1291
- end