mirror of
https://github.com/stripe/stripe-ruby.git
synced 2025-10-04 00:00:47 -04:00
As discussed previously in #354 and alluded to in #363, this patch deprecates the `#refund` helpers on `Charge` and `ApplicationFee` in favor of the resource-centric approach (i.e. `charge.refunds.create`). We do this for a few reasons: 1. The new approach is far preferred and uses our modern endpoints. It's also been the mechanism suggested by the documentation for ages now. 2. The old approach is somewhat risky in that a forgotten "s" can lead to an accidental refund (i.e. `charge.refund` instead of `charge.refunds`). Follows up #354. Fixes #363.
777 lines
30 KiB
Ruby
777 lines
30 KiB
Ruby
# -*- coding: utf-8 -*-
|
|
require File.expand_path('../../test_helper', __FILE__)
|
|
|
|
module Stripe
|
|
class ApiResourceTest < Test::Unit::TestCase
|
|
should "creating a new APIResource should not fetch over the network" do
|
|
@mock.expects(:get).never
|
|
Stripe::Customer.new("someid")
|
|
end
|
|
|
|
should "creating a new APIResource from a hash should not fetch over the network" do
|
|
@mock.expects(:get).never
|
|
Stripe::Customer.construct_from({
|
|
:id => "somecustomer",
|
|
:card => {:id => "somecard", :object => "card"},
|
|
:object => "customer"
|
|
})
|
|
end
|
|
|
|
should "setting an attribute should not cause a network request" do
|
|
@mock.expects(:get).never
|
|
@mock.expects(:post).never
|
|
c = Stripe::Customer.new("test_customer");
|
|
c.card = {:id => "somecard", :object => "card"}
|
|
end
|
|
|
|
should "accessing id should not issue a fetch" do
|
|
@mock.expects(:get).never
|
|
c = Stripe::Customer.new("test_customer")
|
|
c.id
|
|
end
|
|
|
|
should "not specifying api credentials should raise an exception" do
|
|
Stripe.api_key = nil
|
|
assert_raises Stripe::AuthenticationError do
|
|
Stripe::Customer.new("test_customer").refresh
|
|
end
|
|
end
|
|
|
|
should "using a nil api key should raise an exception" do
|
|
assert_raises TypeError do
|
|
Stripe::Customer.list({}, nil)
|
|
end
|
|
assert_raises TypeError do
|
|
Stripe::Customer.list({}, { :api_key => nil })
|
|
end
|
|
end
|
|
|
|
should "specifying api credentials containing whitespace should raise an exception" do
|
|
Stripe.api_key = "key "
|
|
assert_raises Stripe::AuthenticationError do
|
|
Stripe::Customer.new("test_customer").refresh
|
|
end
|
|
end
|
|
|
|
should "specifying invalid api credentials should raise an exception" do
|
|
Stripe.api_key = "invalid"
|
|
response = make_response(make_invalid_api_key_error, 401)
|
|
assert_raises Stripe::AuthenticationError do
|
|
@mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 401))
|
|
Stripe::Customer.retrieve("failing_customer")
|
|
end
|
|
end
|
|
|
|
should "AuthenticationErrors should have an http status, http body, and JSON body" do
|
|
Stripe.api_key = "invalid"
|
|
response = make_response(make_invalid_api_key_error, 401)
|
|
begin
|
|
@mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 401))
|
|
Stripe::Customer.retrieve("failing_customer")
|
|
rescue Stripe::AuthenticationError => e
|
|
assert_equal(401, e.http_status)
|
|
assert_equal(true, !!e.http_body)
|
|
assert_equal(true, !!e.json_body[:error][:message])
|
|
assert_equal(make_invalid_api_key_error[:error][:message], e.json_body[:error][:message])
|
|
end
|
|
end
|
|
|
|
should "send expand on fetch properly" do
|
|
@mock.expects(:get).once.
|
|
with("#{Stripe.api_base}/v1/charges/ch_test_charge?expand[]=customer", nil, nil).
|
|
returns(make_response(make_charge))
|
|
|
|
Stripe::Charge.retrieve({:id => 'ch_test_charge', :expand => [:customer]})
|
|
end
|
|
|
|
should "preserve expand across refreshes" do
|
|
@mock.expects(:get).twice.
|
|
with("#{Stripe.api_base}/v1/charges/ch_test_charge?expand[]=customer", nil, nil).
|
|
returns(make_response(make_charge))
|
|
|
|
ch = Stripe::Charge.retrieve({:id => 'ch_test_charge', :expand => [:customer]})
|
|
ch.refresh
|
|
end
|
|
|
|
should "send expand when fetching through ListObject" do
|
|
@mock.expects(:get).once.
|
|
with("#{Stripe.api_base}/v1/customers/c_test_customer", nil, nil).
|
|
returns(make_response(make_customer))
|
|
|
|
@mock.expects(:get).once.
|
|
with("#{Stripe.api_base}/v1/customers/c_test_customer/sources/cc_test_card?expand[]=customer", nil, nil).
|
|
returns(make_response(make_card))
|
|
|
|
customer = Stripe::Customer.retrieve('c_test_customer')
|
|
customer.sources.retrieve({:id => 'cc_test_card', :expand => [:customer]})
|
|
end
|
|
|
|
should "send stripe account as header when set" do
|
|
stripe_account = "acct_0000"
|
|
Stripe.expects(:execute_request).with do |opts|
|
|
opts[:headers][:stripe_account] == stripe_account
|
|
end.returns(make_response(make_charge))
|
|
|
|
Stripe::Charge.create({:card => {:number => '4242424242424242'}},
|
|
{:stripe_account => stripe_account, :api_key => 'sk_test_local'})
|
|
end
|
|
|
|
should "not send stripe account as header when not set" do
|
|
Stripe.expects(:execute_request).with do |opts|
|
|
opts[:headers][:stripe_account].nil?
|
|
end.returns(make_response(make_charge))
|
|
|
|
Stripe::Charge.create({:card => {:number => '4242424242424242'}},
|
|
'sk_test_local')
|
|
end
|
|
|
|
should "handle error response with empty body" do
|
|
response = make_response('', 500)
|
|
@mock.expects(:post).once.raises(RestClient::ExceptionWithResponse.new(response, 500))
|
|
|
|
e = assert_raises Stripe::APIError do
|
|
Stripe::Charge.create
|
|
end
|
|
|
|
assert_equal 'Invalid response object from API: "" (HTTP response code was 500)', e.message
|
|
end
|
|
|
|
should "handle error response with non-object error value" do
|
|
response = make_response('{"error": "foo"}', 500)
|
|
@mock.expects(:post).once.raises(RestClient::ExceptionWithResponse.new(response, 500))
|
|
|
|
e = assert_raises Stripe::APIError do
|
|
Stripe::Charge.create
|
|
end
|
|
|
|
assert_equal 'Invalid response object from API: "{\"error\": \"foo\"}" (HTTP response code was 500)', e.message
|
|
end
|
|
|
|
should "have default open and read timeouts" do
|
|
assert_equal Stripe.open_timeout, 30
|
|
assert_equal Stripe.read_timeout, 80
|
|
end
|
|
|
|
should "allow configurable open and read timeouts" do
|
|
original_timeouts = Stripe.open_timeout, Stripe.read_timeout
|
|
|
|
begin
|
|
Stripe.open_timeout = 999
|
|
Stripe.read_timeout = 998
|
|
|
|
Stripe.expects(:execute_request).with do |opts|
|
|
opts[:open_timeout] == 999 && opts[:timeout] == 998
|
|
end.returns(make_response(make_charge))
|
|
|
|
Stripe::Charge.create({:card => {:number => '4242424242424242'}},
|
|
'sk_test_local')
|
|
ensure
|
|
Stripe.open_timeout, Stripe.read_timeout = original_timeouts
|
|
end
|
|
end
|
|
|
|
context "when specifying per-object credentials" do
|
|
context "with no global API key set" do
|
|
should "use the per-object credential when creating" do
|
|
Stripe.expects(:execute_request).with do |opts|
|
|
opts[:headers][:authorization] == 'Bearer sk_test_local'
|
|
end.returns(make_response(make_charge))
|
|
|
|
Stripe::Charge.create({:card => {:number => '4242424242424242'}},
|
|
'sk_test_local')
|
|
end
|
|
end
|
|
|
|
context "with a global API key set" do
|
|
setup do
|
|
Stripe.api_key = "global"
|
|
end
|
|
|
|
teardown do
|
|
Stripe.api_key = nil
|
|
end
|
|
|
|
should "use the per-object credential when creating" do
|
|
Stripe.expects(:execute_request).with do |opts|
|
|
opts[:headers][:authorization] == 'Bearer local'
|
|
end.returns(make_response(make_charge))
|
|
|
|
Stripe::Charge.create({:card => {:number => '4242424242424242'}},
|
|
'local')
|
|
end
|
|
|
|
should "use the per-object credential when retrieving and making other calls" do
|
|
Stripe.expects(:execute_request).with do |opts|
|
|
opts[:url] == "#{Stripe.api_base}/v1/charges/ch_test_charge" &&
|
|
opts[:headers][:authorization] == 'Bearer local'
|
|
end.returns(make_response(make_charge))
|
|
Stripe.expects(:execute_request).with do |opts|
|
|
opts[:url] == "#{Stripe.api_base}/v1/charges/ch_test_charge/refunds" &&
|
|
opts[:headers][:authorization] == 'Bearer local'
|
|
end.returns(make_response(make_refund))
|
|
|
|
ch = Stripe::Charge.retrieve('ch_test_charge', 'local')
|
|
ch.refunds.create
|
|
end
|
|
end
|
|
end
|
|
|
|
context "with valid credentials" do
|
|
should "send along the idempotency-key header" do
|
|
Stripe.expects(:execute_request).with do |opts|
|
|
opts[:headers][:idempotency_key] == 'bar'
|
|
end.returns(make_response(make_charge))
|
|
|
|
Stripe::Charge.create({:card => {:number => '4242424242424242'}}, {
|
|
:idempotency_key => 'bar',
|
|
:api_key => 'local',
|
|
})
|
|
end
|
|
|
|
should "urlencode values in GET params" do
|
|
response = make_response(make_charge_array)
|
|
@mock.expects(:get).with("#{Stripe.api_base}/v1/charges?customer=test+customer", nil, nil).returns(response)
|
|
charges = Stripe::Charge.list(:customer => 'test customer').data
|
|
assert charges.kind_of? Array
|
|
end
|
|
|
|
should "construct URL properly with base query parameters" do
|
|
response = make_response(make_invoice_customer_array)
|
|
@mock.expects(:get).with("#{Stripe.api_base}/v1/invoices?customer=test_customer", nil, nil).returns(response)
|
|
invoices = Stripe::Invoice.list(:customer => 'test_customer')
|
|
|
|
@mock.expects(:get).with("#{Stripe.api_base}/v1/invoices?customer=test_customer&paid=true", nil, nil).returns(response)
|
|
invoices.list(:paid => true)
|
|
end
|
|
|
|
should "a 400 should give an InvalidRequestError with http status, body, and JSON body" do
|
|
response = make_response(make_missing_id_error, 400)
|
|
@mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 404))
|
|
begin
|
|
Stripe::Customer.retrieve("foo")
|
|
rescue Stripe::InvalidRequestError => e
|
|
assert_equal(400, e.http_status)
|
|
assert_equal(true, !!e.http_body)
|
|
assert_equal(true, e.json_body.kind_of?(Hash))
|
|
end
|
|
end
|
|
|
|
should "a 401 should give an AuthenticationError with http status, body, and JSON body" do
|
|
response = make_response(make_missing_id_error, 401)
|
|
@mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 404))
|
|
begin
|
|
Stripe::Customer.retrieve("foo")
|
|
rescue Stripe::AuthenticationError => e
|
|
assert_equal(401, e.http_status)
|
|
assert_equal(true, !!e.http_body)
|
|
assert_equal(true, e.json_body.kind_of?(Hash))
|
|
end
|
|
end
|
|
|
|
should "a 402 should give a CardError with http status, body, and JSON body" do
|
|
response = make_response(make_missing_id_error, 402)
|
|
@mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 404))
|
|
begin
|
|
Stripe::Customer.retrieve("foo")
|
|
rescue Stripe::CardError => e
|
|
assert_equal(402, e.http_status)
|
|
assert_equal(true, !!e.http_body)
|
|
assert_equal(true, e.json_body.kind_of?(Hash))
|
|
end
|
|
end
|
|
|
|
should "a 404 should give an InvalidRequestError with http status, body, and JSON body" do
|
|
response = make_response(make_missing_id_error, 404)
|
|
@mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 404))
|
|
begin
|
|
Stripe::Customer.retrieve("foo")
|
|
rescue Stripe::InvalidRequestError => e
|
|
assert_equal(404, e.http_status)
|
|
assert_equal(true, !!e.http_body)
|
|
assert_equal(true, e.json_body.kind_of?(Hash))
|
|
end
|
|
end
|
|
|
|
should "a 429 should give a RateLimitError with http status, body, and JSON body" do
|
|
response = make_response(make_rate_limit_error, 429)
|
|
@mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 429))
|
|
begin
|
|
Stripe::Customer.retrieve("foo")
|
|
rescue Stripe::RateLimitError => e
|
|
assert_equal(429, e.http_status)
|
|
assert_equal(true, !!e.http_body)
|
|
assert_equal(true, e.json_body.kind_of?(Hash))
|
|
end
|
|
end
|
|
|
|
should "setting a nil value for a param should exclude that param from the request" do
|
|
@mock.expects(:get).with do |url, api_key, params|
|
|
uri = URI(url)
|
|
query = CGI.parse(uri.query)
|
|
(url =~ %r{^#{Stripe.api_base}/v1/charges?} &&
|
|
query.keys.sort == ['offset', 'sad'])
|
|
end.returns(make_response({ :count => 1, :data => [make_charge] }))
|
|
Stripe::Charge.list(:count => nil, :offset => 5, :sad => false)
|
|
|
|
@mock.expects(:post).with do |url, api_key, params|
|
|
url == "#{Stripe.api_base}/v1/charges" &&
|
|
api_key.nil? &&
|
|
CGI.parse(params) == { 'amount' => ['50'], 'currency' => ['usd'] }
|
|
end.returns(make_response({ :count => 1, :data => [make_charge] }))
|
|
Stripe::Charge.create(:amount => 50, :currency => 'usd', :card => { :number => nil })
|
|
end
|
|
|
|
should "requesting with a unicode ID should result in a request" do
|
|
response = make_response(make_missing_id_error, 404)
|
|
@mock.expects(:get).once.with("#{Stripe.api_base}/v1/customers/%E2%98%83", nil, nil).raises(RestClient::ExceptionWithResponse.new(response, 404))
|
|
c = Stripe::Customer.new("☃")
|
|
assert_raises(Stripe::InvalidRequestError) { c.refresh }
|
|
end
|
|
|
|
should "requesting with no ID should result in an InvalidRequestError with no request" do
|
|
c = Stripe::Customer.new
|
|
assert_raises(Stripe::InvalidRequestError) { c.refresh }
|
|
end
|
|
|
|
should "making a GET request with parameters should have a query string and no body" do
|
|
params = { :limit => 1 }
|
|
@mock.expects(:get).once.with("#{Stripe.api_base}/v1/charges?limit=1", nil, nil).
|
|
returns(make_response({ :data => [make_charge] }))
|
|
Stripe::Charge.list(params)
|
|
end
|
|
|
|
should "making a POST request with parameters should have a body and no query string" do
|
|
params = { :amount => 100, :currency => 'usd', :card => 'sc_token' }
|
|
@mock.expects(:post).once.with do |url, get, post|
|
|
get.nil? && CGI.parse(post) == {'amount' => ['100'], 'currency' => ['usd'], 'card' => ['sc_token']}
|
|
end.returns(make_response(make_charge))
|
|
Stripe::Charge.create(params)
|
|
end
|
|
|
|
should "loading an object should issue a GET request" do
|
|
@mock.expects(:get).once.returns(make_response(make_customer))
|
|
c = Stripe::Customer.new("test_customer")
|
|
c.refresh
|
|
end
|
|
|
|
should "using array accessors should be the same as the method interface" do
|
|
@mock.expects(:get).once.returns(make_response(make_customer))
|
|
c = Stripe::Customer.new("test_customer")
|
|
c.refresh
|
|
assert_equal c.created, c[:created]
|
|
assert_equal c.created, c['created']
|
|
c['created'] = 12345
|
|
assert_equal c.created, 12345
|
|
end
|
|
|
|
should "accessing a property other than id or parent on an unfetched object should fetch it" do
|
|
@mock.expects(:get).once.returns(make_response(make_customer))
|
|
c = Stripe::Customer.new("test_customer")
|
|
c.charges
|
|
end
|
|
|
|
should "updating an object should issue a POST request with only the changed properties" do
|
|
@mock.expects(:post).with do |url, api_key, params|
|
|
url == "#{Stripe.api_base}/v1/customers/c_test_customer" && api_key.nil? && CGI.parse(params) == {'description' => ['another_mn']}
|
|
end.once.returns(make_response(make_customer))
|
|
c = Stripe::Customer.construct_from(make_customer)
|
|
c.description = "another_mn"
|
|
c.save
|
|
end
|
|
|
|
should "updating should merge in returned properties" do
|
|
@mock.expects(:post).once.returns(make_response(make_customer))
|
|
c = Stripe::Customer.new("c_test_customer")
|
|
c.description = "another_mn"
|
|
c.save
|
|
assert_equal false, c.livemode
|
|
end
|
|
|
|
should "deleting should send no props and result in an object that has no props other deleted" do
|
|
@mock.expects(:get).never
|
|
@mock.expects(:post).never
|
|
@mock.expects(:delete).with("#{Stripe.api_base}/v1/customers/c_test_customer", nil, nil).once.returns(make_response({ "id" => "test_customer", "deleted" => true }))
|
|
c = Stripe::Customer.construct_from(make_customer)
|
|
c.delete
|
|
assert_equal true, c.deleted
|
|
|
|
assert_raises NoMethodError do
|
|
c.livemode
|
|
end
|
|
end
|
|
|
|
should "loading an object with properties that have specific types should instantiate those classes" do
|
|
@mock.expects(:get).once.returns(make_response(make_charge))
|
|
c = Stripe::Charge.retrieve("test_charge")
|
|
assert c.card.kind_of?(Stripe::StripeObject) && c.card.object == 'card'
|
|
end
|
|
|
|
should "loading all of an APIResource should return an array of recursively instantiated objects" do
|
|
@mock.expects(:get).once.returns(make_response(make_charge_array))
|
|
c = Stripe::Charge.list.data
|
|
assert c.kind_of? Array
|
|
assert c[0].kind_of? Stripe::Charge
|
|
assert c[0].card.kind_of?(Stripe::StripeObject) && c[0].card.object == 'card'
|
|
end
|
|
|
|
should "passing in a stripe_account header should pass it through on call" do
|
|
Stripe.expects(:execute_request).with do |opts|
|
|
opts[:method] == :get &&
|
|
opts[:url] == "#{Stripe.api_base}/v1/customers/c_test_customer" &&
|
|
opts[:headers][:stripe_account] == 'acct_abc'
|
|
end.once.returns(make_response(make_customer))
|
|
c = Stripe::Customer.retrieve("c_test_customer", {:stripe_account => 'acct_abc'})
|
|
end
|
|
|
|
should "passing in a stripe_account header should pass it through on save" do
|
|
Stripe.expects(:execute_request).with do |opts|
|
|
opts[:method] == :get &&
|
|
opts[:url] == "#{Stripe.api_base}/v1/customers/c_test_customer" &&
|
|
opts[:headers][:stripe_account] == 'acct_abc'
|
|
end.once.returns(make_response(make_customer))
|
|
c = Stripe::Customer.retrieve("c_test_customer", {:stripe_account => 'acct_abc'})
|
|
|
|
Stripe.expects(:execute_request).with do |opts|
|
|
opts[:method] == :post &&
|
|
opts[:url] == "#{Stripe.api_base}/v1/customers/c_test_customer" &&
|
|
opts[:headers][:stripe_account] == 'acct_abc' &&
|
|
opts[:payload] == 'description=FOO'
|
|
end.once.returns(make_response(make_customer))
|
|
c.description = 'FOO'
|
|
c.save
|
|
end
|
|
|
|
context "error checking" do
|
|
|
|
should "404s should raise an InvalidRequestError" do
|
|
response = make_response(make_missing_id_error, 404)
|
|
@mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 404))
|
|
|
|
rescued = false
|
|
begin
|
|
Stripe::Customer.new("test_customer").refresh
|
|
assert false #shouldn't get here either
|
|
rescue Stripe::InvalidRequestError => e # we don't use assert_raises because we want to examine e
|
|
rescued = true
|
|
assert e.kind_of? Stripe::InvalidRequestError
|
|
assert_equal "id", e.param
|
|
assert_equal "Missing id", e.message
|
|
end
|
|
|
|
assert_equal true, rescued
|
|
end
|
|
|
|
should "5XXs should raise an APIError" do
|
|
response = make_response(make_api_error, 500)
|
|
@mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 500))
|
|
|
|
rescued = false
|
|
begin
|
|
Stripe::Customer.new("test_customer").refresh
|
|
assert false #shouldn't get here either
|
|
rescue Stripe::APIError => e # we don't use assert_raises because we want to examine e
|
|
rescued = true
|
|
assert e.kind_of? Stripe::APIError
|
|
end
|
|
|
|
assert_equal true, rescued
|
|
end
|
|
|
|
should "402s should raise a CardError" do
|
|
response = make_response(make_invalid_exp_year_error, 402)
|
|
@mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 402))
|
|
|
|
rescued = false
|
|
begin
|
|
Stripe::Customer.new("test_customer").refresh
|
|
assert false #shouldn't get here either
|
|
rescue Stripe::CardError => e # we don't use assert_raises because we want to examine e
|
|
rescued = true
|
|
assert e.kind_of? Stripe::CardError
|
|
assert_equal "invalid_expiry_year", e.code
|
|
assert_equal "exp_year", e.param
|
|
assert_equal "Your card's expiration year is invalid", e.message
|
|
end
|
|
|
|
assert_equal true, rescued
|
|
end
|
|
end
|
|
|
|
should 'add key to nested objects' do
|
|
acct = Stripe::Account.construct_from({
|
|
:id => 'myid',
|
|
:legal_entity => {
|
|
:size => 'l',
|
|
:score => 4,
|
|
:height => 10
|
|
}
|
|
})
|
|
|
|
@mock.expects(:post).once.with("#{Stripe.api_base}/v1/accounts/myid", nil, 'legal_entity[first_name]=Bob').returns(make_response({"id" => "myid"}))
|
|
|
|
acct.legal_entity.first_name = 'Bob'
|
|
acct.save
|
|
end
|
|
|
|
should 'save nothing if nothing changes' do
|
|
acct = Stripe::Account.construct_from({
|
|
:id => 'myid',
|
|
:metadata => {
|
|
:key => 'value'
|
|
}
|
|
})
|
|
|
|
@mock.expects(:post).once.with("#{Stripe.api_base}/v1/accounts/myid", nil, '').returns(make_response({"id" => "myid"}))
|
|
|
|
acct.save
|
|
end
|
|
|
|
should 'not save nested API resources' do
|
|
ch = Stripe::Charge.construct_from({
|
|
:id => 'charge_id',
|
|
:customer => {
|
|
:object => 'customer',
|
|
:id => 'customer_id'
|
|
}
|
|
})
|
|
|
|
@mock.expects(:post).once.with("#{Stripe.api_base}/v1/charges/charge_id", nil, '').returns(make_response({"id" => "charge_id"}))
|
|
|
|
ch.customer.description = 'Bob'
|
|
ch.save
|
|
end
|
|
|
|
should 'correctly handle replaced nested objects' do
|
|
acct = Stripe::Account.construct_from({
|
|
:id => 'myid',
|
|
:legal_entity => {
|
|
:last_name => 'Smith',
|
|
:address => {
|
|
:line1 => "test",
|
|
:city => "San Francisco"
|
|
}
|
|
}
|
|
})
|
|
|
|
@mock.expects(:post).once.with(
|
|
"#{Stripe.api_base}/v1/accounts/myid",
|
|
nil,
|
|
any_of(
|
|
'legal_entity[address][line1]=Test2&legal_entity[address][city]=',
|
|
'legal_entity[address][city]=&legal_entity[address][line1]=Test2'
|
|
)
|
|
).returns(make_response({"id" => "myid"}))
|
|
|
|
acct.legal_entity.address = {:line1 => 'Test2'}
|
|
acct.save
|
|
end
|
|
|
|
should 'correctly handle array setting' do
|
|
acct = Stripe::Account.construct_from({
|
|
:id => 'myid',
|
|
:legal_entity => {}
|
|
})
|
|
|
|
@mock.expects(:post).once.with("#{Stripe.api_base}/v1/accounts/myid", nil, 'legal_entity[additional_owners][0][first_name]=Bob').returns(make_response({"id" => "myid"}))
|
|
|
|
acct.legal_entity.additional_owners = [{:first_name => 'Bob'}]
|
|
acct.save
|
|
end
|
|
|
|
should 'correctly handle array insertion' do
|
|
acct = Stripe::Account.construct_from({
|
|
:id => 'myid',
|
|
:legal_entity => {
|
|
:additional_owners => []
|
|
}
|
|
})
|
|
|
|
@mock.expects(:post).once.with("#{Stripe.api_base}/v1/accounts/myid", nil, 'legal_entity[additional_owners][0][first_name]=Bob').returns(make_response({"id" => "myid"}))
|
|
|
|
acct.legal_entity.additional_owners << {:first_name => 'Bob'}
|
|
acct.save
|
|
end
|
|
|
|
should 'correctly handle array updates' do
|
|
acct = Stripe::Account.construct_from({
|
|
:id => 'myid',
|
|
:legal_entity => {
|
|
:additional_owners => [{:first_name => 'Bob'}, {:first_name => 'Jane'}]
|
|
}
|
|
})
|
|
|
|
@mock.expects(:post).once.with("#{Stripe.api_base}/v1/accounts/myid", nil, 'legal_entity[additional_owners][1][first_name]=Janet').returns(make_response({"id" => "myid"}))
|
|
|
|
acct.legal_entity.additional_owners[1].first_name = 'Janet'
|
|
acct.save
|
|
end
|
|
|
|
should 'correctly handle array noops' do
|
|
acct = Stripe::Account.construct_from({
|
|
:id => 'myid',
|
|
:legal_entity => {
|
|
:additional_owners => [{:first_name => 'Bob'}]
|
|
},
|
|
:currencies_supported => ['usd', 'cad']
|
|
})
|
|
|
|
@mock.expects(:post).once.with("#{Stripe.api_base}/v1/accounts/myid", nil, '').returns(make_response({"id" => "myid"}))
|
|
|
|
acct.save
|
|
end
|
|
|
|
should 'correctly handle hash noops' do
|
|
acct = Stripe::Account.construct_from({
|
|
:id => 'myid',
|
|
:legal_entity => {
|
|
:address => {:line1 => '1 Two Three'}
|
|
}
|
|
})
|
|
|
|
@mock.expects(:post).once.with("#{Stripe.api_base}/v1/accounts/myid", nil, '').returns(make_response({"id" => "myid"}))
|
|
|
|
acct.save
|
|
end
|
|
|
|
should 'should create a new resource when an object without an id is saved' do
|
|
account = Stripe::Account.construct_from({
|
|
:id => nil,
|
|
:display_name => nil,
|
|
})
|
|
|
|
@mock.expects(:post).once.with("#{Stripe.api_base}/v1/accounts", nil, 'display_name=stripe').
|
|
returns(make_response({"id" => "charge_id"}))
|
|
|
|
account.display_name = 'stripe'
|
|
account.save
|
|
end
|
|
|
|
should 'set attributes as part of save' do
|
|
account = Stripe::Account.construct_from({
|
|
:id => nil,
|
|
:display_name => nil,
|
|
})
|
|
|
|
@mock.expects(:post).once.with("#{Stripe.api_base}/v1/accounts", nil, 'display_name=stripe').
|
|
returns(make_response({"id" => "charge_id"}))
|
|
|
|
account.save(:display_name => 'stripe')
|
|
end
|
|
end
|
|
|
|
context "with retries" do
|
|
|
|
setup do
|
|
Stripe.stubs(:max_network_retries).returns(2)
|
|
end
|
|
|
|
should 'retry failed network requests if specified and raise if error persists' do
|
|
Stripe.expects(:sleep_time).at_least_once.returns(0)
|
|
@mock.expects(:post).times(3).with('https://api.stripe.com/v1/charges', nil, 'amount=50¤cy=usd').raises(Errno::ECONNREFUSED.new)
|
|
|
|
err = assert_raises Stripe::APIConnectionError do
|
|
Stripe::Charge.create(:amount => 50, :currency => 'usd', :card => { :number => nil })
|
|
end
|
|
assert_match(/Request was retried 2 times/, err.message)
|
|
end
|
|
|
|
should 'retry failed network requests if specified and return successful response' do
|
|
Stripe.expects(:sleep_time).at_least_once.returns(0)
|
|
response = make_response({"id" => "myid"})
|
|
err = Errno::ECONNREFUSED.new
|
|
@mock.expects(:post).times(2).with('https://api.stripe.com/v1/charges', nil, 'amount=50¤cy=usd').raises(err).then.returns(response)
|
|
|
|
result = Stripe::Charge.create(:amount => 50, :currency => 'usd', :card => { :number => nil })
|
|
assert_equal "myid", result.id
|
|
end
|
|
|
|
should 'not retry a SSLCertificateNotVerified error' do
|
|
@mock.expects(:post).times(1).with('https://api.stripe.com/v1/charges', nil, 'amount=50¤cy=usd').raises(RestClient::SSLCertificateNotVerified.new('message'))
|
|
|
|
err = assert_raises Stripe::APIConnectionError do
|
|
Stripe::Charge.create(:amount => 50, :currency => 'usd', :card => { :number => nil })
|
|
end
|
|
assert_no_match(/retried/, err.message)
|
|
end
|
|
|
|
should 'not add an idempotency key to GET requests' do
|
|
SecureRandom.expects(:uuid).times(0)
|
|
Stripe.expects(:execute_request).with do |opts|
|
|
opts[:headers][:idempotency_key].nil?
|
|
end.returns(make_response({"id" => "myid"}))
|
|
|
|
Stripe::Charge.list
|
|
end
|
|
|
|
should 'ensure there is always an idempotency_key on POST requests' do
|
|
SecureRandom.expects(:uuid).at_least_once.returns("random_key")
|
|
Stripe.expects(:execute_request).with do |opts|
|
|
opts[:headers][:idempotency_key] == "random_key"
|
|
end.returns(make_response({"id" => "myid"}))
|
|
|
|
Stripe::Charge.create(:amount => 50, :currency => 'usd', :card => { :number => nil })
|
|
end
|
|
|
|
should 'ensure there is always an idempotency_key on DELETE requests' do
|
|
SecureRandom.expects(:uuid).at_least_once.returns("random_key")
|
|
Stripe.expects(:execute_request).with do |opts|
|
|
opts[:headers][:idempotency_key] == "random_key"
|
|
end.returns(make_response({"id" => "myid"}))
|
|
|
|
c = Stripe::Customer.construct_from(make_customer)
|
|
c.delete
|
|
end
|
|
|
|
should 'not override a provided idempotency_key' do
|
|
Stripe.expects(:execute_request).with do |opts|
|
|
opts[:headers][:idempotency_key] == "provided_key"
|
|
end.returns(make_response({"id" => "myid"}))
|
|
|
|
Stripe::Charge.create({:amount => 50, :currency => 'usd', :card => { :number => nil }}, {:idempotency_key => 'provided_key'})
|
|
end
|
|
|
|
end
|
|
|
|
context "sleep_time" do
|
|
|
|
should "should grow exponentially" do
|
|
Stripe.stubs(:rand).returns(1)
|
|
Stripe.stubs(:max_network_retry_delay).returns(999)
|
|
assert_equal(Stripe.initial_network_retry_delay, Stripe.sleep_time(1))
|
|
assert_equal(Stripe.initial_network_retry_delay * 2, Stripe.sleep_time(2))
|
|
assert_equal(Stripe.initial_network_retry_delay * 4, Stripe.sleep_time(3))
|
|
assert_equal(Stripe.initial_network_retry_delay * 8, Stripe.sleep_time(4))
|
|
end
|
|
|
|
should "enforce the max_network_retry_delay" do
|
|
Stripe.stubs(:rand).returns(1)
|
|
Stripe.stubs(:initial_network_retry_delay).returns(1)
|
|
Stripe.stubs(:max_network_retry_delay).returns(2)
|
|
assert_equal(1, Stripe.sleep_time(1))
|
|
assert_equal(2, Stripe.sleep_time(2))
|
|
assert_equal(2, Stripe.sleep_time(3))
|
|
assert_equal(2, Stripe.sleep_time(4))
|
|
end
|
|
|
|
should "add some randomness" do
|
|
random_value = 0.8
|
|
Stripe.stubs(:rand).returns(random_value)
|
|
Stripe.stubs(:initial_network_retry_delay).returns(1)
|
|
Stripe.stubs(:max_network_retry_delay).returns(8)
|
|
|
|
base_value = Stripe.initial_network_retry_delay * (0.5 * (1 + random_value))
|
|
|
|
# the initial value cannot be smaller than the base,
|
|
# so the randomness is ignored
|
|
assert_equal(Stripe.initial_network_retry_delay, Stripe.sleep_time(1))
|
|
|
|
# after the first one, the randomness is applied
|
|
assert_equal(base_value * 2, Stripe.sleep_time(2))
|
|
assert_equal(base_value * 4, Stripe.sleep_time(3))
|
|
assert_equal(base_value * 8, Stripe.sleep_time(4))
|
|
end
|
|
|
|
end
|
|
end
|
|
end
|