stripe 4.20.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +17 -4
  3. data/.rubocop_todo.yml +10 -9
  4. data/.travis.yml +2 -6
  5. data/CHANGELOG.md +52 -1
  6. data/Gemfile +2 -12
  7. data/README.md +10 -10
  8. data/Rakefile +8 -7
  9. data/VERSION +1 -1
  10. data/lib/stripe/api_operations/list.rb +0 -6
  11. data/lib/stripe/api_resource.rb +16 -0
  12. data/lib/stripe/connection_manager.rb +131 -0
  13. data/lib/stripe/error_object.rb +94 -0
  14. data/lib/stripe/errors.rb +15 -2
  15. data/lib/stripe/list_object.rb +2 -1
  16. data/lib/stripe/multipart_encoder.rb +131 -0
  17. data/lib/stripe/object_types.rb +1 -4
  18. data/lib/stripe/resources/account.rb +7 -7
  19. data/lib/stripe/resources/account_link.rb +1 -1
  20. data/lib/stripe/resources/alipay_account.rb +1 -1
  21. data/lib/stripe/resources/apple_pay_domain.rb +1 -1
  22. data/lib/stripe/resources/application_fee.rb +1 -12
  23. data/lib/stripe/resources/application_fee_refund.rb +1 -1
  24. data/lib/stripe/resources/balance.rb +1 -1
  25. data/lib/stripe/resources/balance_transaction.rb +1 -5
  26. data/lib/stripe/resources/bank_account.rb +1 -1
  27. data/lib/stripe/resources/bitcoin_receiver.rb +1 -1
  28. data/lib/stripe/resources/bitcoin_transaction.rb +1 -1
  29. data/lib/stripe/resources/capability.rb +1 -1
  30. data/lib/stripe/resources/card.rb +1 -1
  31. data/lib/stripe/resources/charge.rb +7 -69
  32. data/lib/stripe/resources/checkout/session.rb +1 -1
  33. data/lib/stripe/resources/country_spec.rb +1 -1
  34. data/lib/stripe/resources/coupon.rb +1 -1
  35. data/lib/stripe/resources/credit_note.rb +7 -3
  36. data/lib/stripe/resources/customer.rb +5 -66
  37. data/lib/stripe/resources/customer_balance_transaction.rb +1 -1
  38. data/lib/stripe/resources/discount.rb +1 -1
  39. data/lib/stripe/resources/dispute.rb +7 -9
  40. data/lib/stripe/resources/ephemeral_key.rb +1 -1
  41. data/lib/stripe/resources/event.rb +1 -1
  42. data/lib/stripe/resources/exchange_rate.rb +1 -1
  43. data/lib/stripe/resources/file.rb +3 -13
  44. data/lib/stripe/resources/file_link.rb +1 -1
  45. data/lib/stripe/resources/invoice.rb +36 -11
  46. data/lib/stripe/resources/invoice_item.rb +1 -1
  47. data/lib/stripe/resources/invoice_line_item.rb +1 -1
  48. data/lib/stripe/resources/issuing/authorization.rb +13 -5
  49. data/lib/stripe/resources/issuing/card.rb +7 -3
  50. data/lib/stripe/resources/issuing/card_details.rb +1 -1
  51. data/lib/stripe/resources/issuing/cardholder.rb +1 -1
  52. data/lib/stripe/resources/issuing/dispute.rb +1 -1
  53. data/lib/stripe/resources/issuing/transaction.rb +1 -1
  54. data/lib/stripe/resources/login_link.rb +1 -1
  55. data/lib/stripe/resources/order.rb +13 -13
  56. data/lib/stripe/resources/order_return.rb +1 -1
  57. data/lib/stripe/resources/payment_intent.rb +19 -7
  58. data/lib/stripe/resources/payment_method.rb +13 -5
  59. data/lib/stripe/resources/payout.rb +7 -9
  60. data/lib/stripe/resources/person.rb +1 -1
  61. data/lib/stripe/resources/plan.rb +1 -1
  62. data/lib/stripe/resources/product.rb +1 -1
  63. data/lib/stripe/resources/radar/early_fraud_warning.rb +1 -1
  64. data/lib/stripe/resources/radar/value_list.rb +1 -1
  65. data/lib/stripe/resources/radar/value_list_item.rb +1 -1
  66. data/lib/stripe/resources/recipient.rb +1 -5
  67. data/lib/stripe/resources/recipient_transfer.rb +1 -1
  68. data/lib/stripe/resources/refund.rb +1 -1
  69. data/lib/stripe/resources/reporting/report_run.rb +1 -1
  70. data/lib/stripe/resources/reporting/report_type.rb +1 -1
  71. data/lib/stripe/resources/reversal.rb +1 -1
  72. data/lib/stripe/resources/review.rb +7 -3
  73. data/lib/stripe/resources/setup_intent.rb +32 -0
  74. data/lib/stripe/resources/sigma/scheduled_query_run.rb +1 -1
  75. data/lib/stripe/resources/sku.rb +1 -1
  76. data/lib/stripe/resources/source.rb +7 -9
  77. data/lib/stripe/resources/source_transaction.rb +1 -1
  78. data/lib/stripe/resources/subscription.rb +9 -9
  79. data/lib/stripe/resources/subscription_item.rb +4 -1
  80. data/lib/stripe/resources/subscription_schedule.rb +13 -13
  81. data/lib/stripe/resources/tax_id.rb +1 -1
  82. data/lib/stripe/resources/tax_rate.rb +1 -1
  83. data/lib/stripe/resources/terminal/connection_token.rb +1 -1
  84. data/lib/stripe/resources/terminal/location.rb +1 -1
  85. data/lib/stripe/resources/terminal/reader.rb +1 -1
  86. data/lib/stripe/resources/three_d_secure.rb +1 -1
  87. data/lib/stripe/resources/token.rb +1 -1
  88. data/lib/stripe/resources/topup.rb +7 -3
  89. data/lib/stripe/resources/transfer.rb +7 -8
  90. data/lib/stripe/resources/usage_record.rb +1 -17
  91. data/lib/stripe/resources/usage_record_summary.rb +1 -1
  92. data/lib/stripe/resources/webhook_endpoint.rb +1 -1
  93. data/lib/stripe/resources.rb +1 -2
  94. data/lib/stripe/stripe_client.rb +281 -183
  95. data/lib/stripe/stripe_object.rb +4 -23
  96. data/lib/stripe/stripe_response.rb +53 -21
  97. data/lib/stripe/util.rb +14 -11
  98. data/lib/stripe/version.rb +1 -1
  99. data/lib/stripe/webhook.rb +1 -1
  100. data/lib/stripe.rb +56 -15
  101. data/stripe.gemspec +10 -3
  102. data/test/stripe/account_test.rb +0 -16
  103. data/test/stripe/api_operations_test.rb +2 -2
  104. data/test/stripe/api_resource_test.rb +98 -8
  105. data/test/stripe/balance_transaction_test.rb +20 -0
  106. data/test/stripe/charge_test.rb +0 -16
  107. data/test/stripe/connection_manager_test.rb +138 -0
  108. data/test/stripe/customer_test.rb +1 -44
  109. data/test/stripe/errors_test.rb +29 -8
  110. data/test/stripe/file_test.rb +0 -10
  111. data/test/stripe/invoice_test.rb +17 -1
  112. data/test/stripe/list_object_test.rb +0 -16
  113. data/test/stripe/login_link_test.rb +1 -1
  114. data/test/stripe/multipart_encoder_test.rb +130 -0
  115. data/test/stripe/payment_intent_test.rb +2 -2
  116. data/test/stripe/setup_intent_test.rb +84 -0
  117. data/test/stripe/source_test.rb +0 -18
  118. data/test/stripe/stripe_client_test.rb +214 -29
  119. data/test/stripe/stripe_object_test.rb +7 -35
  120. data/test/stripe/stripe_response_test.rb +70 -24
  121. data/test/stripe/subscription_item_test.rb +12 -0
  122. data/test/stripe/subscription_schedule_test.rb +0 -34
  123. data/test/stripe/subscription_test.rb +2 -2
  124. data/test/stripe/webhook_test.rb +2 -2
  125. data/test/stripe_mock.rb +4 -3
  126. data/test/stripe_test.rb +0 -13
  127. data/test/test_helper.rb +10 -5
  128. metadata +23 -43
  129. data/lib/stripe/resources/issuer_fraud_record.rb +0 -9
  130. data/lib/stripe/resources/subscription_schedule_revision.rb +0 -34
  131. data/test/stripe/file_upload_test.rb +0 -79
  132. data/test/stripe/issuer_fraud_record_test.rb +0 -20
  133. data/test/stripe/subscription_schedule_revision_test.rb +0 -37
  134. data/test/stripe/usage_record_test.rb +0 -28
@@ -17,6 +17,50 @@ module Stripe
17
17
  end
18
18
  end
19
19
 
20
+ context ".clear_all_connection_managers" do
21
+ should "clear connection managers across all threads" do
22
+ stub_request(:post, "#{Stripe.api_base}/path")
23
+ .to_return(body: JSON.generate(object: "account"))
24
+
25
+ num_threads = 3
26
+
27
+ # Poorly named class -- note this is actually a concurrent queue.
28
+ recv_queue = Queue.new
29
+ send_queue = Queue.new
30
+
31
+ threads = num_threads.times.map do |_|
32
+ Thread.start do
33
+ # Causes a connection manager to be created on this thread and a
34
+ # connection within that manager to be created for API access.
35
+ manager = StripeClient.default_connection_manager
36
+ manager.execute_request(:post, "#{Stripe.api_base}/path")
37
+
38
+ # Signal to the main thread we're ready.
39
+ recv_queue << true
40
+
41
+ # Wait for the main thread to signal continue.
42
+ send_queue.pop
43
+
44
+ # This check isn't great, but it's otherwise difficult to tell that
45
+ # anything happened with just the public-facing API.
46
+ assert_equal({}, manager.instance_variable_get(:@active_connections))
47
+ end
48
+ end
49
+
50
+ # Wait for threads to start up.
51
+ threads.each { recv_queue.pop }
52
+
53
+ # Do the clear (the method we're actually trying to test).
54
+ StripeClient.clear_all_connection_managers
55
+
56
+ # Tell threads to run their check.
57
+ threads.each { send_queue << true }
58
+
59
+ # And finally, give all threads time to perform their check.
60
+ threads.each(&:join)
61
+ end
62
+ end
63
+
20
64
  context ".default_client" do
21
65
  should "be a StripeClient" do
22
66
  assert_kind_of StripeClient, StripeClient.default_client
@@ -32,18 +76,19 @@ module Stripe
32
76
  end
33
77
  end
34
78
 
35
- context ".default_conn" do
36
- should "be a Faraday::Connection" do
37
- assert_kind_of Faraday::Connection, StripeClient.default_conn
79
+ context ".default_connection_manager" do
80
+ should "be a ConnectionManager" do
81
+ assert_kind_of ConnectionManager,
82
+ StripeClient.default_connection_manager
38
83
  end
39
84
 
40
85
  should "be a different connection on each thread" do
41
- other_thread_conn = nil
86
+ other_thread_manager = nil
42
87
  thread = Thread.new do
43
- other_thread_conn = StripeClient.default_conn
88
+ other_thread_manager = StripeClient.default_connection_manager
44
89
  end
45
90
  thread.join
46
- refute_equal StripeClient.default_conn, other_thread_conn
91
+ refute_equal StripeClient.default_connection_manager, other_thread_manager
47
92
  end
48
93
  end
49
94
 
@@ -52,26 +97,54 @@ module Stripe
52
97
  Stripe.stubs(:max_network_retries).returns(2)
53
98
  end
54
99
 
55
- should "retry on timeout" do
56
- assert StripeClient.should_retry?(Faraday::TimeoutError.new(""), 0)
100
+ should "retry on Errno::ECONNREFUSED" do
101
+ assert StripeClient.should_retry?(Errno::ECONNREFUSED.new,
102
+ method: :post, num_retries: 0)
103
+ end
104
+
105
+ should "retry on Net::OpenTimeout" do
106
+ assert StripeClient.should_retry?(Net::OpenTimeout.new,
107
+ method: :post, num_retries: 0)
108
+ end
109
+
110
+ should "retry on Net::ReadTimeout" do
111
+ assert StripeClient.should_retry?(Net::ReadTimeout.new,
112
+ method: :post, num_retries: 0)
113
+ end
114
+
115
+ should "retry on SocketError" do
116
+ assert StripeClient.should_retry?(SocketError.new,
117
+ method: :post, num_retries: 0)
118
+ end
119
+
120
+ should "retry on a 409 Conflict" do
121
+ assert StripeClient.should_retry?(Stripe::StripeError.new(http_status: 409),
122
+ method: :post, num_retries: 0)
57
123
  end
58
124
 
59
- should "retry on a failed connection" do
60
- assert StripeClient.should_retry?(Faraday::ConnectionFailed.new(""), 0)
125
+ should "retry on a 500 Internal Server Error when non-POST" do
126
+ assert StripeClient.should_retry?(Stripe::StripeError.new(http_status: 500),
127
+ method: :get, num_retries: 0)
61
128
  end
62
129
 
63
- should "retry on a conflict" do
64
- error = make_rate_limit_error
65
- e = Faraday::ClientError.new(error[:error][:message], status: 409)
66
- assert StripeClient.should_retry?(e, 0)
130
+ should "retry on a 503 Service Unavailable" do
131
+ assert StripeClient.should_retry?(Stripe::StripeError.new(http_status: 503),
132
+ method: :post, num_retries: 0)
67
133
  end
68
134
 
69
135
  should "not retry at maximum count" do
70
- refute StripeClient.should_retry?(RuntimeError.new, Stripe.max_network_retries)
136
+ refute StripeClient.should_retry?(RuntimeError.new,
137
+ method: :post, num_retries: Stripe.max_network_retries)
71
138
  end
72
139
 
73
140
  should "not retry on a certificate validation error" do
74
- refute StripeClient.should_retry?(Faraday::SSLError.new(""), 0)
141
+ refute StripeClient.should_retry?(OpenSSL::SSL::SSLError.new,
142
+ method: :post, num_retries: 0)
143
+ end
144
+
145
+ should "not retry on a 500 Internal Server Error when POST" do
146
+ refute StripeClient.should_retry?(Stripe::StripeError.new(http_status: 500),
147
+ method: :post, num_retries: 0)
75
148
  end
76
149
  end
77
150
 
@@ -115,15 +188,16 @@ module Stripe
115
188
  end
116
189
 
117
190
  context "#initialize" do
118
- should "set Stripe.default_conn" do
191
+ should "set Stripe.default_connection_manager" do
119
192
  client = StripeClient.new
120
- assert_equal StripeClient.default_conn, client.conn
193
+ assert_equal StripeClient.default_connection_manager,
194
+ client.connection_manager
121
195
  end
122
196
 
123
197
  should "set a different connection if one was specified" do
124
- conn = Faraday.new
125
- client = StripeClient.new(conn)
126
- assert_equal conn, client.conn
198
+ connection_manager = ConnectionManager.new
199
+ client = StripeClient.new(connection_manager)
200
+ assert_equal connection_manager, client.connection_manager
127
201
  end
128
202
  end
129
203
 
@@ -178,7 +252,7 @@ module Stripe
178
252
  Util.expects(:log_debug).with("Request details",
179
253
  body: "",
180
254
  idempotency_key: "abc",
181
- query_params: nil)
255
+ query: nil)
182
256
 
183
257
  Util.expects(:log_info).with("Response from Stripe API",
184
258
  account: "acct_123",
@@ -403,6 +477,20 @@ module Stripe
403
477
  assert_equal 'Invalid response object from API: "" (HTTP response code was 200)', e.message
404
478
  end
405
479
 
480
+ should "handle low level error" do
481
+ stub_request(:post, "#{Stripe.api_base}/v1/charges")
482
+ .to_raise(Errno::ECONNREFUSED.new)
483
+
484
+ client = StripeClient.new
485
+ e = assert_raises Stripe::APIConnectionError do
486
+ client.execute_request(:post, "/v1/charges")
487
+ end
488
+
489
+ assert_equal StripeClient::ERROR_MESSAGE_CONNECTION % Stripe.api_base +
490
+ "\n\n(Network error: Connection refused)",
491
+ e.message
492
+ end
493
+
406
494
  should "handle error response with unknown value" do
407
495
  stub_request(:post, "#{Stripe.api_base}/v1/charges")
408
496
  .to_return(body: JSON.generate(bar: "foo"), status: 500)
@@ -738,14 +826,106 @@ module Stripe
738
826
 
739
827
  should "reset local thread state after a call" do
740
828
  begin
741
- Thread.current[:stripe_client] = :stripe_client
829
+ StripeClient.current_thread_context.active_client = :stripe_client
742
830
 
743
831
  client = StripeClient.new
744
832
  client.request {}
745
833
 
746
- assert_equal :stripe_client, Thread.current[:stripe_client]
834
+ assert_equal :stripe_client,
835
+ StripeClient.current_thread_context.active_client
747
836
  ensure
748
- Thread.current[:stripe_client] = nil
837
+ StripeClient.current_thread_context.active_client = nil
838
+ end
839
+ end
840
+
841
+ should "correctly return last responses despite multiple clients" do
842
+ charge_resp = { object: "charge" }
843
+ coupon_resp = { object: "coupon" }
844
+
845
+ stub_request(:post, "#{Stripe.api_base}/v1/charges")
846
+ .to_return(body: JSON.generate(charge_resp))
847
+ stub_request(:post, "#{Stripe.api_base}/v1/coupons")
848
+ .to_return(body: JSON.generate(coupon_resp))
849
+
850
+ client1 = StripeClient.new
851
+ client2 = StripeClient.new
852
+
853
+ client2_resp = nil
854
+ _charge, client1_resp = client1.request do
855
+ Charge.create
856
+
857
+ # This is contrived, but we run one client nested in the `request`
858
+ # block of another one just to ensure that the parent is still
859
+ # unwinding when this goes through. If the parent's last response
860
+ # were to be overridden by this client (through a bug), then it would
861
+ # happen here.
862
+ _coupon, client2_resp = client2.request do
863
+ Coupon.create
864
+ end
865
+ end
866
+
867
+ assert_equal charge_resp, client1_resp.data
868
+ assert_equal coupon_resp, client2_resp.data
869
+ end
870
+
871
+ should "correctly return last responses despite multiple threads" do
872
+ charge_resp = { object: "charge" }
873
+ coupon_resp = { object: "coupon" }
874
+
875
+ stub_request(:post, "#{Stripe.api_base}/v1/charges")
876
+ .to_return(body: JSON.generate(charge_resp))
877
+ stub_request(:post, "#{Stripe.api_base}/v1/coupons")
878
+ .to_return(body: JSON.generate(coupon_resp))
879
+
880
+ client = StripeClient.new
881
+
882
+ # Poorly named class -- note this is actually a concurrent queue.
883
+ recv_queue = Queue.new
884
+ send_queue = Queue.new
885
+
886
+ # Start a thread, make an API request, but then idle in the `request`
887
+ # block until the main thread has been able to make its own API request
888
+ # and signal that it's done. If this thread's last response were to be
889
+ # overridden by the main thread (through a bug), then this routine
890
+ # should suss it out.
891
+ resp1 = nil
892
+ thread = Thread.start do
893
+ _charge, resp1 = client.request do
894
+ Charge.create
895
+
896
+ # Idle in `request` block until main thread signals.
897
+ send_queue.pop
898
+ end
899
+
900
+ # Signal main thread that we're done and it can run its checks.
901
+ recv_queue << true
902
+ end
903
+
904
+ # Make an API request.
905
+ _coupon, resp2 = client.request do
906
+ Coupon.create
907
+ end
908
+
909
+ # Tell background thread to finish `request`, then wait for it to
910
+ # signal back to us that it's ready.
911
+ send_queue << true
912
+ recv_queue.pop
913
+
914
+ assert_equal charge_resp, resp1.data
915
+ assert_equal coupon_resp, resp2.data
916
+
917
+ # And for maximum hygiene, make sure that our thread rejoins.
918
+ thread.join
919
+ end
920
+
921
+ should "error if calls to #request are nested on the same thread" do
922
+ client = StripeClient.new
923
+ client.request do
924
+ e = assert_raises(RuntimeError) do
925
+ client.request {}
926
+ end
927
+ assert_equal "calls to StripeClient#request cannot be nested within a thread",
928
+ e.message
749
929
  end
750
930
  end
751
931
  end
@@ -753,18 +933,23 @@ module Stripe
753
933
  context "#proxy" do
754
934
  should "run the request through the proxy" do
755
935
  begin
756
- Thread.current[:stripe_client_default_conn] = nil
936
+ StripeClient.current_thread_context.default_connection_manager = nil
757
937
 
758
- Stripe.proxy = "http://localhost:8080"
938
+ Stripe.proxy = "http://user:pass@localhost:8080"
759
939
 
760
940
  client = StripeClient.new
761
941
  client.request {}
762
942
 
763
- assert_equal "http://localhost:8080", Stripe::StripeClient.default_conn.proxy.uri.to_s
943
+ connection = Stripe::StripeClient.default_connection_manager.connection_for(Stripe.api_base)
944
+
945
+ assert_equal "localhost", connection.proxy_address
946
+ assert_equal 8080, connection.proxy_port
947
+ assert_equal "user", connection.proxy_user
948
+ assert_equal "pass", connection.proxy_pass
764
949
  ensure
765
950
  Stripe.proxy = nil
766
951
 
767
- Thread.current[:stripe_client_default_conn] = nil
952
+ StripeClient.current_thread_context.default_connection_manager = nil
768
953
  end
769
954
  end
770
955
  end
@@ -52,9 +52,9 @@ module Stripe
52
52
  2,
53
53
  ],
54
54
  map: {
55
- :"0" => StripeObject.construct_from({ id: "index0" }, opts),
56
- :"1" => "index1",
57
- :"2" => 2,
55
+ "0": StripeObject.construct_from({ id: "index0" }, opts),
56
+ "1": "index1",
57
+ "2": 2,
58
58
  },
59
59
  }
60
60
 
@@ -235,20 +235,6 @@ module Stripe
235
235
  assert_equal true, obj.send(:metaclass).method_defined?(:foo)
236
236
  end
237
237
 
238
- should "warn that #refresh_from is deprecated" do
239
- old_stderr = $stderr
240
- $stderr = StringIO.new
241
- begin
242
- obj = Stripe::StripeObject.construct_from({})
243
- obj.refresh_from({}, {})
244
- message = "NOTE: Stripe::StripeObject#refresh_from is " \
245
- "deprecated; use #update_attributes instead"
246
- assert_match Regexp.new(message), $stderr.string
247
- ensure
248
- $stderr = old_stderr
249
- end
250
- end
251
-
252
238
  should "pass opts down to children when initializing" do
253
239
  opts = { custom: "opts" }
254
240
 
@@ -302,14 +288,14 @@ module Stripe
302
288
  end
303
289
 
304
290
  should "#serialize_params on an array that shortens" do
305
- obj = Stripe::StripeObject.construct_from(foo: ["0-index", "1-index", "2-index"])
291
+ obj = Stripe::StripeObject.construct_from(foo: %w[0-index 1-index 2-index])
306
292
  obj.foo = ["new-value"]
307
293
  assert_equal({ foo: ["new-value"] },
308
294
  obj.serialize_params)
309
295
  end
310
296
 
311
297
  should "#serialize_params on an array that lengthens" do
312
- obj = Stripe::StripeObject.construct_from(foo: ["0-index", "1-index", "2-index"])
298
+ obj = Stripe::StripeObject.construct_from(foo: %w[0-index 1-index 2-index])
313
299
  obj.foo = ["new-value"] * 4
314
300
  assert_equal({ foo: ["new-value"] * 4 },
315
301
  obj.serialize_params)
@@ -331,8 +317,8 @@ module Stripe
331
317
  end
332
318
 
333
319
  should "#serialize_params on an array that is unchanged" do
334
- obj = Stripe::StripeObject.construct_from(foo: ["0-index", "1-index", "2-index"])
335
- obj.foo = ["0-index", "1-index", "2-index"]
320
+ obj = Stripe::StripeObject.construct_from(foo: %w[0-index 1-index 2-index])
321
+ obj.foo = %w[0-index 1-index 2-index]
336
322
  assert_equal({}, obj.serialize_params)
337
323
  end
338
324
 
@@ -475,20 +461,6 @@ module Stripe
475
461
  assert_equal(expected, obj.to_s)
476
462
  end
477
463
 
478
- should "warn that .serialize_params is deprecated" do
479
- old_stderr = $stderr
480
- $stderr = StringIO.new
481
- begin
482
- obj = Stripe::StripeObject.construct_from({})
483
- Stripe::StripeObject.serialize_params(obj)
484
- message = "NOTE: Stripe::StripeObject.serialize_params is " \
485
- "deprecated; use #serialize_params instead"
486
- assert_match Regexp.new(message), $stderr.string
487
- ensure
488
- $stderr = old_stderr
489
- end
490
- end
491
-
492
464
  should "error on setting a property to an empty string" do
493
465
  obj = Stripe::StripeObject.construct_from(foo: "bar")
494
466
  e = assert_raises ArgumentError do
@@ -4,46 +4,92 @@ require ::File.expand_path("../test_helper", __dir__)
4
4
 
5
5
  module Stripe
6
6
  class StripeResponseTest < Test::Unit::TestCase
7
- context ".from_faraday_hash" do
8
- should "converts to StripeResponse" do
9
- body = '{"foo": "bar"}'
7
+ context "Headers" do
8
+ should "allow case-insensitive header access" do
10
9
  headers = { "Request-Id" => "request-id" }
10
+ http_resp = create_net_http_resp(200, "", headers)
11
11
 
12
- http_resp = {
13
- body: body,
14
- headers: headers,
15
- status: 200,
16
- }
12
+ headers = StripeResponse::Headers.from_net_http(http_resp)
17
13
 
18
- resp = StripeResponse.from_faraday_hash(http_resp)
14
+ assert_equal "request-id", headers["request-id"]
15
+ assert_equal "request-id", headers["Request-Id"]
16
+ assert_equal "request-id", headers["Request-ID"]
17
+ end
19
18
 
20
- assert_equal JSON.parse(body, symbolize_names: true), resp.data
21
- assert_equal body, resp.http_body
22
- assert_equal headers, resp.http_headers
23
- assert_equal 200, resp.http_status
24
- assert_equal "request-id", resp.request_id
19
+ should "initialize without error" do
20
+ StripeResponse::Headers.new({})
21
+ StripeResponse::Headers.new("Request-Id" => [])
22
+ StripeResponse::Headers.new("Request-Id" => ["request-id"])
23
+ end
24
+
25
+ should "initialize with error on a malformed hash" do
26
+ assert_raises(ArgumentError) do
27
+ StripeResponse::Headers.new(nil)
28
+ end
29
+
30
+ assert_raises(ArgumentError) do
31
+ StripeResponse::Headers.new(1 => [])
32
+ end
33
+
34
+ assert_raises(ArgumentError) do
35
+ StripeResponse::Headers.new("Request-Id" => 1)
36
+ end
37
+
38
+ assert_raises(ArgumentError) do
39
+ StripeResponse::Headers.new("Request-Id" => [1])
40
+ end
41
+ end
42
+
43
+ should "warn on duplicate header values" do
44
+ old_stderr = $stderr
45
+ $stderr = StringIO.new
46
+ begin
47
+ headers = StripeResponse::Headers.new("Duplicated" => %w[a b])
48
+ assert_equal "a", headers["Duplicated"]
49
+ assert_equal "Duplicate header values for `Duplicated`; returning only first",
50
+ $stderr.string.rstrip
51
+ ensure
52
+ $stderr = old_stderr
53
+ end
25
54
  end
26
55
  end
27
56
 
28
- context ".from_faraday_response" do
57
+ context ".from_net_http" do
29
58
  should "converts to StripeResponse" do
59
+ code = 200
30
60
  body = '{"foo": "bar"}'
31
61
  headers = { "Request-Id" => "request-id" }
62
+ http_resp = create_net_http_resp(code, body, headers)
32
63
 
33
- env = Faraday::Env.from(
34
- status: 200, body: body,
35
- response_headers: headers
36
- )
37
- http_resp = Faraday::Response.new(env)
38
-
39
- resp = StripeResponse.from_faraday_response(http_resp)
64
+ resp = StripeResponse.from_net_http(http_resp)
40
65
 
41
66
  assert_equal JSON.parse(body, symbolize_names: true), resp.data
42
67
  assert_equal body, resp.http_body
43
- assert_equal headers, resp.http_headers
44
- assert_equal 200, resp.http_status
68
+ assert_equal "request-id", resp.http_headers["Request-ID"]
69
+ assert_equal code, resp.http_status
45
70
  assert_equal "request-id", resp.request_id
46
71
  end
47
72
  end
73
+
74
+ # Synthesizes a `Net::HTTPResponse` object for testing purposes.
75
+ private def create_net_http_resp(code, body, headers)
76
+ # The "1.1" is HTTP version.
77
+ http_resp = Net::HTTPResponse.new("1.1", code.to_s, nil)
78
+ http_resp.body = body
79
+
80
+ # This is obviously super sketchy, but the Ruby team has done everything
81
+ # in their power to make these objects as difficult to test with as
82
+ # possible. Even if you specify a body, accessing `#body` the first time
83
+ # will attempt to read from a non-existent socket which will subsequently
84
+ # blow up. Setting this internal variable skips that read and allows the
85
+ # object to use the body that we specified above.
86
+ http_resp.instance_variable_set(:@read, true)
87
+
88
+ headers.each do |name, value|
89
+ http_resp[name] = value
90
+ end
91
+
92
+ http_resp
93
+ end
48
94
  end
49
95
  end
@@ -59,5 +59,17 @@ module Stripe
59
59
  assert item.is_a?(Stripe::SubscriptionItem)
60
60
  end
61
61
  end
62
+
63
+ context "#create_usage_record" do
64
+ should "create a usage record" do
65
+ Stripe::SubscriptionItem.create_usage_record(
66
+ "si_123",
67
+ quantity: 5000,
68
+ timestamp: Time.now.to_i,
69
+ action: "increment"
70
+ )
71
+ assert_requested :post, "#{Stripe.api_base}/v1/subscription_items/si_123/usage_records"
72
+ end
73
+ end
62
74
  end
63
75
  end
@@ -78,39 +78,5 @@ module Stripe
78
78
  assert schedule.is_a?(Stripe::SubscriptionSchedule)
79
79
  end
80
80
  end
81
-
82
- context "#revisions" do
83
- should "retrieve the subscription schedule's revisions" do
84
- schedule = Stripe::SubscriptionSchedule.retrieve("sub_sched_123")
85
- revisions = schedule.revisions
86
- assert_requested :get,
87
- "#{Stripe.api_base}/v1/subscription_schedules/sub_sched_123/revisions"
88
- assert revisions.data.is_a?(Array)
89
- assert revisions.data[0].is_a?(Stripe::SubscriptionScheduleRevision)
90
- end
91
- end
92
-
93
- context "#retrieve_revision" do
94
- should "retrieve a subscription schedule revision" do
95
- revision = Stripe::SubscriptionSchedule.retrieve_revision(
96
- "sub_sched_123",
97
- "sub_sched_rev_123"
98
- )
99
- assert_requested :get, "#{Stripe.api_base}/v1/subscription_schedules/sub_sched_123/revisions/sub_sched_rev_123"
100
- assert revision.is_a?(Stripe::SubscriptionScheduleRevision)
101
- end
102
- end
103
-
104
- context "#list_revisions" do
105
- should "list a subscription schedule's revisions" do
106
- revisions = Stripe::SubscriptionSchedule.list_revisions(
107
- "sub_sched_123"
108
- )
109
- assert_requested :get, "#{Stripe.api_base}/v1/subscription_schedules/sub_sched_123/revisions"
110
- assert revisions.is_a?(Stripe::ListObject)
111
- assert revisions.data.is_a?(Array)
112
- assert revisions.data[0].is_a?(Stripe::SubscriptionScheduleRevision)
113
- end
114
- end
115
81
  end
116
82
  end
@@ -63,9 +63,9 @@ module Stripe
63
63
  context "#delete_discount" do
64
64
  should "be able to delete a subscriptions's discount" do
65
65
  subscription = Stripe::Subscription.retrieve("sub_123")
66
- subscription = subscription.delete_discount
66
+ discount = subscription.delete_discount
67
67
  assert_requested :delete, "#{Stripe.api_base}/v1/subscriptions/sub_123/discount"
68
- assert subscription.is_a?(Stripe::Subscription)
68
+ assert discount.is_a?(Stripe::Discount)
69
69
  end
70
70
  end
71
71
 
@@ -4,13 +4,13 @@ require ::File.expand_path("../test_helper", __dir__)
4
4
 
5
5
  module Stripe
6
6
  class WebhookTest < Test::Unit::TestCase
7
- EVENT_PAYLOAD = <<-PAYLOAD.freeze
7
+ EVENT_PAYLOAD = <<~PAYLOAD
8
8
  {
9
9
  "id": "evt_test_webhook",
10
10
  "object": "event"
11
11
  }
12
12
  PAYLOAD
13
- SECRET = "whsec_test_secret".freeze
13
+ SECRET = "whsec_test_secret"
14
14
 
15
15
  def generate_header(opts = {})
16
16
  opts[:timestamp] ||= Time.now.to_i
data/test/stripe_mock.rb CHANGED
@@ -4,8 +4,8 @@ module Stripe
4
4
  class StripeMock
5
5
  include Singleton
6
6
 
7
- PATH_SPEC = "#{::File.dirname(__FILE__)}/openapi/spec3.json".freeze
8
- PATH_FIXTURES = "#{::File.dirname(__FILE__)}/openapi/fixtures3.json".freeze
7
+ PATH_SPEC = "#{::File.dirname(__FILE__)}/openapi/spec3.json"
8
+ PATH_FIXTURES = "#{::File.dirname(__FILE__)}/openapi/fixtures3.json"
9
9
 
10
10
  @pid = nil
11
11
  @port = -1
@@ -29,7 +29,7 @@ module Stripe
29
29
  @stderr, @child_stderr = ::IO.pipe
30
30
 
31
31
  @pid = ::Process.spawn(
32
- ["stripe-mock", "stripe-mock"],
32
+ %w[stripe-mock stripe-mock],
33
33
  "-http-port",
34
34
  "0", # have stripe-mock select a port
35
35
  "-spec",
@@ -66,6 +66,7 @@ module Stripe
66
66
  # Stops stripe-mock, if necessary.
67
67
  def self.stop
68
68
  return if @pid.nil?
69
+
69
70
  puts("Stopping stripe-mock...")
70
71
  ::Process.kill(:SIGTERM, @pid)
71
72
  ::Process.waitpid2(@pid)
data/test/stripe_test.rb CHANGED
@@ -3,19 +3,6 @@
3
3
  require ::File.expand_path("test_helper", __dir__)
4
4
 
5
5
  class StripeTest < Test::Unit::TestCase
6
- should "warn that #refresh_from is deprecated" do
7
- old_stderr = $stderr
8
- $stderr = StringIO.new
9
- begin
10
- Stripe.uri_encode({})
11
- message = "NOTE: Stripe.uri_encode is deprecated; use " \
12
- "Stripe::Util#encode_parameters instead"
13
- assert_match Regexp.new(message), $stderr.string
14
- ensure
15
- $stderr = old_stderr
16
- end
17
- end
18
-
19
6
  should "allow app_info to be configured" do
20
7
  begin
21
8
  old = Stripe.app_info