stripe 4.21.2 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 +40 -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 +0 -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 +3 -63
  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 +13 -5
  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 +0 -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 +6 -9
  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 +1 -1
  116. data/test/stripe/setup_intent_test.rb +1 -1
  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 +13 -42
  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