mirror of
https://github.com/stripe/stripe-ruby.git
synced 2025-10-06 00:02:18 -04:00
Adds the magic `frozen_string_literal: true` comment to every file and enables a Rubocop rule to make sure that it's always going to be there going forward as well. See here for more background [1], but the basic idea is that unlike many other languages, static strings in code are mutable by default. This has since been acknowledged as not a particularly good idea, and the intention is to rectify the mistake when Ruby 3 comes out, where all string literals will be frozen. The `frozen_string_literal` magic comment was introduced in Ruby 2.3 as a way of easing the transition, and allows libraries and projects to freeze their literals in advance. I don't think this is breaking in any way: it's possible that users might've been pulling out one of are literals somehow and mutating it, but that would probably not have been useful for anything and would certainly not be recommended, so I'm quite comfortable pushing this change through as a minor version. As discussed in #641. [1] https://stackoverflow.com/a/37799399
435 lines
13 KiB
Ruby
435 lines
13 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require File.expand_path("../../test_helper", __FILE__)
|
|
|
|
module Stripe
|
|
class UtilTest < Test::Unit::TestCase
|
|
context "OPTS_COPYABLE" do
|
|
should "include :apibase" do
|
|
assert_include Stripe::Util::OPTS_COPYABLE, :api_base
|
|
end
|
|
end
|
|
|
|
context "OPTS_PERSISTABLE" do
|
|
should "include :client" do
|
|
assert_include Stripe::Util::OPTS_PERSISTABLE, :client
|
|
end
|
|
|
|
should "not include :idempotency_key" do
|
|
refute_includes Stripe::Util::OPTS_PERSISTABLE, :idempotency_key
|
|
end
|
|
end
|
|
|
|
should "#encode_parameters should prepare parameters for an HTTP request" do
|
|
params = {
|
|
a: 3,
|
|
b: "+foo?",
|
|
c: "bar&baz",
|
|
d: { a: "a", b: "b" },
|
|
e: [0, 1],
|
|
f: "",
|
|
|
|
# note the empty hash won't even show up in the request
|
|
g: [],
|
|
}
|
|
assert_equal(
|
|
"a=3&b=%2Bfoo%3F&c=bar%26baz&d[a]=a&d[b]=b&e[]=0&e[]=1&f=",
|
|
Stripe::Util.encode_parameters(params)
|
|
)
|
|
end
|
|
|
|
should "#encode_params should throw an error on an array of maps that cannot be encoded" do
|
|
params = {
|
|
a: [
|
|
{ a: 1, b: 2 },
|
|
{ c: 3, a: 4 },
|
|
],
|
|
}
|
|
e = assert_raises(ArgumentError) do
|
|
Stripe::Util.encode_parameters(params)
|
|
end
|
|
expected = "All maps nested in an array should start with the same key " \
|
|
"(expected starting key 'a', got 'c')"
|
|
assert_equal expected, e.message
|
|
|
|
# Make sure the check is recursive by taking our original params and
|
|
# nesting it into yet another map and array. Should throw exactly the
|
|
# same error because it's still the in inner array of maps that's wrong.
|
|
params = {
|
|
x: [
|
|
params,
|
|
],
|
|
}
|
|
e = assert_raises(ArgumentError) do
|
|
Stripe::Util.encode_parameters(params)
|
|
end
|
|
assert_equal expected, e.message
|
|
end
|
|
|
|
should "#url_encode should prepare strings for HTTP" do
|
|
assert_equal "foo", Stripe::Util.url_encode("foo")
|
|
assert_equal "foo", Stripe::Util.url_encode(:foo)
|
|
assert_equal "foo%2B", Stripe::Util.url_encode("foo+")
|
|
assert_equal "foo%26", Stripe::Util.url_encode("foo&")
|
|
assert_equal "foo[bar]", Stripe::Util.url_encode("foo[bar]")
|
|
end
|
|
|
|
should "#flatten_params should encode parameters according to Rails convention" do
|
|
params = [
|
|
[:a, 3],
|
|
[:b, "foo?"],
|
|
[:c, "bar&baz"],
|
|
[:d, { a: "a", b: "b" }],
|
|
[:e, [0, 1]],
|
|
[:f, [
|
|
{ foo: "1", ghi: "2" },
|
|
{ foo: "3", bar: "4" },
|
|
],],
|
|
]
|
|
assert_equal([
|
|
["a", 3],
|
|
["b", "foo?"],
|
|
["c", "bar&baz"],
|
|
["d[a]", "a"],
|
|
["d[b]", "b"],
|
|
["e[]", 0],
|
|
["e[]", 1],
|
|
|
|
# *The key here is the order*. In order to be properly interpreted as
|
|
# an array of hashes on the server, everything from a single hash must
|
|
# come in at once. A duplicate key in an array triggers a new element.
|
|
["f[][foo]", "1"],
|
|
["f[][ghi]", "2"],
|
|
["f[][foo]", "3"],
|
|
["f[][bar]", "4"],
|
|
], Stripe::Util.flatten_params(params))
|
|
end
|
|
|
|
should "#symbolize_names should convert names to symbols" do
|
|
start = {
|
|
"foo" => "bar",
|
|
"array" => [{ "foo" => "bar" }],
|
|
"nested" => {
|
|
1 => 2,
|
|
:symbol => 9,
|
|
"string" => nil,
|
|
},
|
|
}
|
|
finish = {
|
|
foo: "bar",
|
|
array: [{ foo: "bar" }],
|
|
nested: {
|
|
1 => 2,
|
|
:symbol => 9,
|
|
:string => nil,
|
|
},
|
|
}
|
|
|
|
symbolized = Stripe::Util.symbolize_names(start)
|
|
assert_equal(finish, symbolized)
|
|
end
|
|
|
|
should "#normalize_opts should reject nil keys" do
|
|
assert_raise { Stripe::Util.normalize_opts(nil) }
|
|
assert_raise { Stripe::Util.normalize_opts(api_key: nil) }
|
|
end
|
|
|
|
should "#convert_to_stripe_object should pass through unknown types" do
|
|
obj = Util.convert_to_stripe_object(7, {})
|
|
assert_equal 7, obj
|
|
end
|
|
|
|
should "#convert_to_stripe_object should turn hashes into StripeObjects" do
|
|
obj = Util.convert_to_stripe_object({ foo: "bar" }, {})
|
|
assert obj.is_a?(StripeObject)
|
|
assert_equal "bar", obj.foo
|
|
end
|
|
|
|
should "#convert_to_stripe_object should turn lists into ListObjects" do
|
|
obj = Util.convert_to_stripe_object({ object: "list" }, {})
|
|
assert obj.is_a?(ListObject)
|
|
end
|
|
|
|
should "#convert_to_stripe_object should marshal other classes" do
|
|
obj = Util.convert_to_stripe_object({ object: "account" }, {})
|
|
assert obj.is_a?(Account)
|
|
end
|
|
|
|
should "#convert_to_stripe_object should marshal arrays" do
|
|
obj = Util.convert_to_stripe_object([1, 2, 3], {})
|
|
assert_equal [1, 2, 3], obj
|
|
end
|
|
|
|
should "#array_to_hash should convert an array into a hash with integer keys" do
|
|
assert_equal({ "0" => 1, "1" => 2, "2" => 3 }, Util.array_to_hash([1, 2, 3]))
|
|
end
|
|
|
|
context ".request_id_dashboard_url" do
|
|
should "generate a livemode URL" do
|
|
assert_equal "https://dashboard.stripe.com/live/logs/request-id",
|
|
Util.request_id_dashboard_url("request-id", "sk_live_123")
|
|
end
|
|
|
|
should "generate a testmode URL" do
|
|
assert_equal "https://dashboard.stripe.com/test/logs/request-id",
|
|
Util.request_id_dashboard_url("request-id", "sk_test_123")
|
|
end
|
|
|
|
should "default to a testmode URL" do
|
|
assert_equal "https://dashboard.stripe.com/test/logs/request-id",
|
|
Util.request_id_dashboard_url("request-id", nil)
|
|
end
|
|
end
|
|
|
|
context ".log_*" do
|
|
setup do
|
|
@old_log_level = Stripe.log_level
|
|
Stripe.log_level = nil
|
|
|
|
@old_stderr = $stderr
|
|
$stderr = StringIO.new
|
|
|
|
@old_stdout = $stdout
|
|
$stdout = StringIO.new
|
|
end
|
|
|
|
teardown do
|
|
Stripe.log_level = @old_log_level
|
|
$stderr = @old_stderr
|
|
$stdout = @old_stdout
|
|
end
|
|
|
|
context ".log_debug" do
|
|
should "not log if logging is disabled" do
|
|
Util.log_debug("foo")
|
|
assert_equal "", $stdout.string
|
|
end
|
|
|
|
should "log if level set to debug" do
|
|
Stripe.log_level = Stripe::LEVEL_DEBUG
|
|
Util.log_debug("foo")
|
|
assert_equal "message=foo level=debug \n", $stdout.string
|
|
end
|
|
|
|
should "not log if level set to error" do
|
|
Stripe.log_level = Stripe::LEVEL_ERROR
|
|
Util.log_debug("foo")
|
|
assert_equal "", $stdout.string
|
|
end
|
|
|
|
should "not log if level set to info" do
|
|
Stripe.log_level = Stripe::LEVEL_INFO
|
|
Util.log_debug("foo")
|
|
assert_equal "", $stdout.string
|
|
end
|
|
end
|
|
|
|
context ".log_error" do
|
|
should "not log if logging is disabled" do
|
|
Util.log_error("foo")
|
|
assert_equal "", $stdout.string
|
|
end
|
|
|
|
should "log if level set to debug" do
|
|
Stripe.log_level = Stripe::LEVEL_DEBUG
|
|
Util.log_error("foo")
|
|
assert_equal "message=foo level=error \n", $stderr.string
|
|
end
|
|
|
|
should "log if level set to error" do
|
|
Stripe.log_level = Stripe::LEVEL_ERROR
|
|
Util.log_error("foo")
|
|
assert_equal "message=foo level=error \n", $stderr.string
|
|
end
|
|
|
|
should "log if level set to info" do
|
|
Stripe.log_level = Stripe::LEVEL_INFO
|
|
Util.log_error("foo")
|
|
assert_equal "message=foo level=error \n", $stderr.string
|
|
end
|
|
end
|
|
|
|
context ".log_info" do
|
|
should "not log if logging is disabled" do
|
|
Util.log_info("foo")
|
|
assert_equal "", $stdout.string
|
|
end
|
|
|
|
should "log if level set to debug" do
|
|
Stripe.log_level = Stripe::LEVEL_DEBUG
|
|
Util.log_info("foo")
|
|
assert_equal "message=foo level=info \n", $stdout.string
|
|
end
|
|
|
|
should "not log if level set to error" do
|
|
Stripe.log_level = Stripe::LEVEL_ERROR
|
|
Util.log_debug("foo")
|
|
assert_equal "", $stdout.string
|
|
end
|
|
|
|
should "log if level set to info" do
|
|
Stripe.log_level = Stripe::LEVEL_INFO
|
|
Util.log_info("foo")
|
|
assert_equal "message=foo level=info \n", $stdout.string
|
|
end
|
|
end
|
|
end
|
|
|
|
context ".log_* with a logger" do
|
|
setup do
|
|
@out = StringIO.new
|
|
logger = ::Logger.new(@out)
|
|
|
|
# Set a really simple formatter to make matching output as easy as
|
|
# possible.
|
|
logger.formatter = proc { |_severity, _datetime, _progname, message|
|
|
message
|
|
}
|
|
|
|
Stripe.logger = logger
|
|
end
|
|
|
|
context ".log_debug" do
|
|
should "log to the logger" do
|
|
Util.log_debug("foo")
|
|
assert_equal "message=foo ", @out.string
|
|
end
|
|
end
|
|
|
|
context ".log_error" do
|
|
should "log to the logger" do
|
|
Util.log_error("foo")
|
|
assert_equal "message=foo ", @out.string
|
|
end
|
|
end
|
|
|
|
context ".log_info" do
|
|
should "log to the logger" do
|
|
Util.log_info("foo")
|
|
assert_equal "message=foo ", @out.string
|
|
end
|
|
end
|
|
end
|
|
|
|
context ".normalize_headers" do
|
|
should "normalize the format of a header key" do
|
|
assert_equal({ "Request-Id" => nil },
|
|
Util.normalize_headers("Request-Id" => nil))
|
|
assert_equal({ "Request-Id" => nil },
|
|
Util.normalize_headers("request-id" => nil))
|
|
assert_equal({ "Request-Id" => nil },
|
|
Util.normalize_headers("Request-ID" => nil))
|
|
assert_equal({ "Request-Id" => nil },
|
|
Util.normalize_headers(request_id: nil))
|
|
end
|
|
|
|
should "tolerate bad formatting" do
|
|
assert_equal({ "Request-Id" => nil },
|
|
Util.normalize_headers("-Request--Id-" => nil))
|
|
assert_equal({ "Request-Id" => nil },
|
|
Util.normalize_headers(request__id: nil))
|
|
end
|
|
end
|
|
|
|
#
|
|
# private
|
|
#
|
|
# I don't feel particularly good about using #send to invoke these, but I
|
|
# want them hidden from the public interface, and each method is far easier
|
|
# to test in isolation (as opposed to going through a public method).
|
|
#
|
|
|
|
context ".colorize" do
|
|
should "colorize for a TTY" do
|
|
assert_equal "\033[0;32;49mfoo\033[0m",
|
|
Util.send(:colorize, "foo", :green, true)
|
|
end
|
|
|
|
should "not colorize otherwise" do
|
|
assert_equal "foo", Util.send(:colorize, "foo", :green, false)
|
|
end
|
|
end
|
|
|
|
context ".level_name" do
|
|
should "convert levels to names" do
|
|
assert_equal "debug", Util.send(:level_name, LEVEL_DEBUG)
|
|
assert_equal "error", Util.send(:level_name, LEVEL_ERROR)
|
|
assert_equal "info", Util.send(:level_name, LEVEL_INFO)
|
|
end
|
|
end
|
|
|
|
context ".log_internal" do
|
|
should "log in a terminal friendly way" do
|
|
out = StringIO.new
|
|
|
|
# Sketchy as anything, but saves us from pulling in a mocking library.
|
|
# Open this instance of StringIO, and add a method override so that it
|
|
# looks like a TTY.
|
|
out.instance_eval do
|
|
def isatty
|
|
true
|
|
end
|
|
end
|
|
|
|
Util.send(:log_internal, "message", { foo: "bar" },
|
|
color: :green, level: Stripe::LEVEL_DEBUG, logger: nil, out: out)
|
|
assert_equal "\e[0;32;49mDEBU\e[0m message \e[0;32;49mfoo\e[0m=bar\n",
|
|
out.string
|
|
end
|
|
|
|
should "log in a data friendly way" do
|
|
out = StringIO.new
|
|
Util.send(:log_internal, "message", { foo: "bar" },
|
|
color: :green, level: Stripe::LEVEL_DEBUG, logger: nil, out: out)
|
|
assert_equal "message=message level=debug foo=bar\n",
|
|
out.string
|
|
end
|
|
|
|
should "log to a logger if set" do
|
|
out = StringIO.new
|
|
logger = ::Logger.new(out)
|
|
|
|
# Set a really simple formatter to make matching output as easy as
|
|
# possible.
|
|
logger.formatter = proc { |_severity, _datetime, _progname, message|
|
|
message
|
|
}
|
|
|
|
Util.send(:log_internal, "message", { foo: "bar" },
|
|
color: :green, level: Stripe::LEVEL_DEBUG, logger: logger, out: nil)
|
|
assert_equal "message=message foo=bar",
|
|
out.string
|
|
end
|
|
end
|
|
|
|
context ".wrap_logfmt_value" do
|
|
should "pass through simple values" do
|
|
assert_equal "abc", Util.send(:wrap_logfmt_value, "abc")
|
|
assert_equal "123", Util.send(:wrap_logfmt_value, "123")
|
|
assert_equal "a-b_c/d", Util.send(:wrap_logfmt_value, "a-b_c/d")
|
|
end
|
|
|
|
should "pass through numerics" do
|
|
assert_equal 123, Util.send(:wrap_logfmt_value, 123)
|
|
assert_equal 1.23, Util.send(:wrap_logfmt_value, 1.23)
|
|
end
|
|
|
|
should "wrap more complex values in double quotes" do
|
|
assert_equal %("abc=123"), Util.send(:wrap_logfmt_value, %(abc=123))
|
|
end
|
|
|
|
should "escape double quotes already in the value" do
|
|
assert_equal %("abc=\\"123\\""), Util.send(:wrap_logfmt_value, %(abc="123"))
|
|
end
|
|
|
|
should "remove newlines" do
|
|
assert_equal %("abc"), Util.send(:wrap_logfmt_value, "a\nb\nc")
|
|
end
|
|
|
|
should "not error if given a non-string" do
|
|
assert_equal "true", Util.send(:wrap_logfmt_value, true)
|
|
end
|
|
end
|
|
end
|
|
end
|