mirror of
https://github.com/stripe/stripe-ruby.git
synced 2025-10-16 00:02:00 -04:00
Prior to my last major serialization refactor, there was a check in the code that would remove any subobjects from serialization that were of their own proper resource type (for example, if a charge contained a customer, that customer would be removed). What I didn't realize at the time is that the old serialization code had a bug/quirk that would allow *certain types* of subobjects that were API resources to make it through unscathed. In short, the behavior requirement here is *directly* contradictory. There was a test in place that would make sure that `customer` was removed from this hash: ``` ruby { :id => 'ch_id', :object => 'charge', :customer => { :object => 'customer', :id => 'customer_id' } } ``` But, as reported in #406, we expect, and indeed need, for `source` (a card) to make it through to the API in this hash: ``` ruby { :id => 'cus_id', :object => 'customer', :source => { :object => 'card', :id => 'card_id' } } ``` My proposal here is to just remove the check on serializing API resources. The normal code that only sends up keys/hashes that have changed is still in place, so in the first example, `customer` still isn't sent unless the user has directly manipulated a field on that subobject. I propose that in those cases we allow the API itself to reject the request rather than try to cut it off at the client level. Unfortunately, there is some possibility that removing those API resources is important for some reason, but of course there's no documentation on it beyond the after-the-fact post-justification that I wrote during my last refactor. I can't think of any reason that it would be too destructive, but there is some level of risk.
290 lines
9.7 KiB
Ruby
290 lines
9.7 KiB
Ruby
require File.expand_path('../../test_helper', __FILE__)
|
|
|
|
module Stripe
|
|
class StripeObjectTest < Test::Unit::TestCase
|
|
should "implement #==" do
|
|
obj1 = Stripe::StripeObject.construct_from({ :id => 1, :foo => "bar" })
|
|
obj2 = Stripe::StripeObject.construct_from({ :id => 1, :foo => "bar" })
|
|
obj3 = Stripe::StripeObject.construct_from({ :id => 1, :foo => "rab" })
|
|
|
|
assert obj1 == obj2
|
|
refute obj1 == obj3
|
|
end
|
|
|
|
should "implement #deleted?" do
|
|
obj = Stripe::StripeObject.construct_from({})
|
|
refute obj.deleted?
|
|
|
|
obj = Stripe::StripeObject.construct_from({ :deleted => false })
|
|
refute obj.deleted?
|
|
|
|
obj = Stripe::StripeObject.construct_from({ :deleted => true })
|
|
assert obj.deleted?
|
|
end
|
|
|
|
should "implement #respond_to" do
|
|
obj = Stripe::StripeObject.construct_from({ :id => 1, :foo => 'bar' })
|
|
assert obj.respond_to?(:id)
|
|
assert obj.respond_to?(:foo)
|
|
assert !obj.respond_to?(:baz)
|
|
end
|
|
|
|
should "marshal be insensitive to strings vs. symbols when constructin" do
|
|
obj = Stripe::StripeObject.construct_from({ :id => 1, 'name' => 'Stripe' })
|
|
assert_equal 1, obj[:id]
|
|
assert_equal 'Stripe', obj[:name]
|
|
end
|
|
|
|
should "marshal a stripe object correctly" do
|
|
obj = Stripe::StripeObject.construct_from({ :id => 1, :name => 'Stripe' }, {:api_key => 'apikey'})
|
|
m = Marshal.load(Marshal.dump(obj))
|
|
assert_equal 1, m.id
|
|
assert_equal 'Stripe', m.name
|
|
expected_hash = {:api_key => 'apikey'}
|
|
assert_equal expected_hash, m.instance_variable_get('@opts')
|
|
end
|
|
|
|
should "recursively call to_hash on its values" do
|
|
# deep nested hash (when contained in an array) or StripeObject
|
|
nested_hash = { :id => 7, :foo => 'bar' }
|
|
nested = Stripe::StripeObject.construct_from(nested_hash)
|
|
|
|
obj = Stripe::StripeObject.construct_from({
|
|
:id => 1,
|
|
# simple hash that contains a StripeObject to help us test deep
|
|
# recursion
|
|
:nested => { :object => 'list', :data => [nested] },
|
|
:list => [nested]
|
|
})
|
|
|
|
expected_hash = {
|
|
:id => 1,
|
|
:nested => { :object => 'list', :data => [nested_hash] },
|
|
:list => [nested_hash]
|
|
}
|
|
assert_equal expected_hash, obj.to_hash
|
|
end
|
|
|
|
should "assign question mark accessors for booleans" do
|
|
obj = Stripe::StripeObject.construct_from({ :id => 1, :bool => true, :not_bool => 'bar' })
|
|
assert obj.respond_to?(:bool?)
|
|
assert obj.bool?
|
|
refute obj.respond_to?(:not_bool?)
|
|
end
|
|
|
|
should "mass assign values with #update_attributes" do
|
|
obj = Stripe::StripeObject.construct_from({ :id => 1, :name => 'Stripe' })
|
|
obj.update_attributes(:name => 'STRIPE')
|
|
assert_equal "STRIPE", obj.name
|
|
|
|
# unfortunately, we even assign unknown properties to duplicate the
|
|
# behavior that we currently have via magic accessors with
|
|
# method_missing
|
|
obj.update_attributes(:unknown => 'foo')
|
|
assert_equal "foo", obj.unknown
|
|
end
|
|
|
|
should "#update_attributes with a hash" do
|
|
obj = Stripe::StripeObject.construct_from({})
|
|
obj.update_attributes(:metadata => { :foo => 'bar' })
|
|
assert_equal Stripe::StripeObject, obj.metadata.class
|
|
end
|
|
|
|
should "warn that #refresh_from is deprecated" do
|
|
old_stderr = $stderr
|
|
$stderr = StringIO.new
|
|
begin
|
|
obj = Stripe::StripeObject.construct_from({})
|
|
obj.refresh_from({}, {})
|
|
message = "NOTE: Stripe::StripeObject#refresh_from is " +
|
|
"deprecated; use #update_attributes instead"
|
|
assert_match Regexp.new(message), $stderr.string
|
|
ensure
|
|
$stderr = old_stderr
|
|
end
|
|
end
|
|
|
|
should "pass opts down to children when initializing" do
|
|
opts = { :custom => "opts" }
|
|
|
|
# customer comes with a `sources` list that makes a convenient object to
|
|
# perform tests on
|
|
customer = Stripe::Customer.construct_from(make_customer, opts)
|
|
|
|
source = customer.sources.first
|
|
# Pulling `@opts` as an instance variable here is not ideal, but it's
|
|
# important enough argument that the test here is worth it. we should
|
|
# consider exposing it publicly on a future pull (and possibly renaming
|
|
# it to something more useful).
|
|
assert_equal opts, source.instance_variable_get(:@opts)
|
|
end
|
|
|
|
should "#serialize_params on an empty object" do
|
|
obj = Stripe::StripeObject.construct_from({})
|
|
assert_equal({}, obj.serialize_params)
|
|
end
|
|
|
|
should "#serialize_params on a new object with a subobject" do
|
|
obj = Stripe::StripeObject.new
|
|
obj.metadata = { :foo => "bar" }
|
|
assert_equal({ :metadata => { :foo => "bar" } },
|
|
obj.serialize_params)
|
|
end
|
|
|
|
should "#serialize_params on a basic object" do
|
|
obj = Stripe::StripeObject.construct_from({ :foo => nil })
|
|
obj.update_attributes(:foo => "bar")
|
|
assert_equal({ :foo => "bar" }, obj.serialize_params)
|
|
end
|
|
|
|
should "#serialize_params on a more complex object" do
|
|
obj = Stripe::StripeObject.construct_from({
|
|
:foo => Stripe::StripeObject.construct_from({
|
|
:bar => nil,
|
|
:baz => nil,
|
|
}),
|
|
})
|
|
obj.foo.bar = "newbar"
|
|
assert_equal({ :foo => { :bar => "newbar" } },
|
|
obj.serialize_params)
|
|
end
|
|
|
|
should "#serialize_params on an array" do
|
|
obj = Stripe::StripeObject.construct_from({
|
|
:foo => nil,
|
|
})
|
|
obj.foo = ["new-value"]
|
|
assert_equal({ :foo => ["new-value"] },
|
|
obj.serialize_params)
|
|
end
|
|
|
|
should "#serialize_params on an array that shortens" do
|
|
obj = Stripe::StripeObject.construct_from({
|
|
:foo => ["0-index", "1-index", "2-index"],
|
|
})
|
|
obj.foo = ["new-value"]
|
|
assert_equal({ :foo => ["new-value"] },
|
|
obj.serialize_params)
|
|
end
|
|
|
|
should "#serialize_params on an array that lengthens" do
|
|
obj = Stripe::StripeObject.construct_from({
|
|
:foo => ["0-index", "1-index", "2-index"],
|
|
})
|
|
obj.foo = ["new-value"] * 4
|
|
assert_equal({ :foo => ["new-value"] * 4 },
|
|
obj.serialize_params)
|
|
end
|
|
|
|
should "#serialize_params on an array of hashes" do
|
|
obj = Stripe::StripeObject.construct_from({
|
|
:foo => nil,
|
|
})
|
|
obj.foo = [
|
|
Stripe::StripeObject.construct_from({
|
|
:bar => nil
|
|
})
|
|
]
|
|
obj.foo[0].bar = "baz"
|
|
assert_equal({ :foo => [{ :bar => "baz" }] },
|
|
obj.serialize_params)
|
|
end
|
|
|
|
should "#serialize_params doesn't include unchanged values" do
|
|
obj = Stripe::StripeObject.construct_from({ :foo => nil })
|
|
assert_equal({}, obj.serialize_params)
|
|
end
|
|
|
|
should "#serialize_params on an array that is unchanged" do
|
|
obj = Stripe::StripeObject.construct_from({
|
|
:foo => ["0-index", "1-index", "2-index"],
|
|
})
|
|
obj.foo = ["0-index", "1-index", "2-index"]
|
|
assert_equal({}, obj.serialize_params)
|
|
end
|
|
|
|
should "#serialize_params with a StripeObject" do
|
|
obj = Stripe::StripeObject.construct_from({})
|
|
|
|
# using an #update_attributes will end up converting a Hash into a
|
|
# StripeObject
|
|
obj.metadata =
|
|
Stripe::StripeObject.construct_from({ :foo => 'bar' })
|
|
|
|
serialized = obj.serialize_params
|
|
assert_equal({ :foo => "bar" }, serialized[:metadata])
|
|
end
|
|
|
|
should "#serialize_params with a StripeObject that's been replaced" do
|
|
obj = Stripe::StripeObject.construct_from({
|
|
:metadata => Stripe::StripeObject.construct_from({ :bar => 'foo' })
|
|
})
|
|
|
|
# Here we replace the object wholesale which means that the client must
|
|
# be able to blank out the values that were in the old object, but which
|
|
# are no longer present in the new one.
|
|
obj.metadata =
|
|
Stripe::StripeObject.construct_from({ :baz => 'foo' })
|
|
|
|
serialized = obj.serialize_params
|
|
assert_equal({ :bar => "", :baz => 'foo' }, serialized[:metadata])
|
|
end
|
|
|
|
should "#serialize_params with an array of StripeObjects" do
|
|
obj = Stripe::StripeObject.construct_from({})
|
|
obj.metadata = [
|
|
Stripe::StripeObject.construct_from({ :foo => 'bar' })
|
|
]
|
|
|
|
serialized = obj.serialize_params
|
|
assert_equal([{ :foo => "bar" }], serialized[:metadata])
|
|
end
|
|
|
|
should "#serialize_params takes a force option" do
|
|
obj = Stripe::StripeObject.construct_from({
|
|
:id => 'id',
|
|
:metadata => Stripe::StripeObject.construct_from({ :foo => 'bar' })
|
|
})
|
|
|
|
serialized = obj.serialize_params(:force => true)
|
|
assert_equal({ :id => 'id', :metadata => { :foo => 'bar' } }, serialized)
|
|
end
|
|
|
|
should "#dirty! forces an object and its subobjects to be saved" do
|
|
obj = Stripe::StripeObject.construct_from({
|
|
:id => 'id',
|
|
:metadata => Stripe::StripeObject.construct_from({ :foo => 'bar' })
|
|
})
|
|
|
|
# note that `force` and `dirty!` are for different things, but are
|
|
# functionally equivalent
|
|
obj.dirty!
|
|
|
|
serialized = obj.serialize_params
|
|
assert_equal({ :id => 'id', :metadata => { :foo => 'bar' } }, serialized)
|
|
end
|
|
|
|
should "warn that .serialize_params is deprecated" do
|
|
old_stderr = $stderr
|
|
$stderr = StringIO.new
|
|
begin
|
|
obj = Stripe::StripeObject.construct_from({})
|
|
Stripe::StripeObject.serialize_params(obj)
|
|
message = "NOTE: Stripe::StripeObject.serialize_params is " +
|
|
"deprecated; use #serialize_params instead"
|
|
assert_match Regexp.new(message), $stderr.string
|
|
ensure
|
|
$stderr = old_stderr
|
|
end
|
|
end
|
|
|
|
should "error on setting a property to an empty string" do
|
|
obj = Stripe::StripeObject.construct_from({ :foo => 'bar' })
|
|
e = assert_raises ArgumentError do
|
|
obj.foo = ""
|
|
end
|
|
assert_match /\(object\).foo = nil/, e.message
|
|
end
|
|
end
|
|
end
|