hanami-utils/test/hash_test.rb

389 lines
10 KiB
Ruby

require 'test_helper'
require 'bigdecimal'
require 'hanami/utils/hash'
describe Hanami::Utils::Hash do
describe '#initialize' do
it 'holds values passed to the constructor' do
hash = Hanami::Utils::Hash.new('foo' => 'bar')
hash['foo'].must_equal('bar')
end
it 'assigns default via block' do
hash = Hanami::Utils::Hash.new {|h,k| h[k] = []}
hash['foo'].push 'bar'
hash.must_equal({'foo' => ['bar']})
end
it 'accepts a Hanami::Utils::Hash' do
arg = Hanami::Utils::Hash.new('foo' => 'bar')
hash = Hanami::Utils::Hash.new(arg)
hash.to_h.must_be_kind_of(::Hash)
end
end
describe '#symbolize!' do
it 'symbolize keys' do
hash = Hanami::Utils::Hash.new('fub' => 'baz')
hash.symbolize!
hash['fub'].must_be_nil
hash[:fub].must_equal('baz')
end
it 'symbolize nested hashes' do
hash = Hanami::Utils::Hash.new('nested' => {'key' => 'value'})
hash.symbolize!
hash[:nested].must_be_kind_of Hanami::Utils::Hash
hash[:nested][:key].must_equal('value')
end
end
describe '#stringify!' do
it 'covert keys to strings' do
hash = Hanami::Utils::Hash.new(fub: 'baz')
hash.stringify!
hash[:fub].must_be_nil
hash['fub'].must_equal('baz')
end
it 'stringifies nested hashes' do
hash = Hanami::Utils::Hash.new(nested: {key: 'value'})
hash.stringify!
hash['nested'].must_be_kind_of Hanami::Utils::Hash
hash['nested']['key'].must_equal('value')
end
end
describe '#deep_dup' do
it 'returns an instance of Utils::Hash' do
duped = Hanami::Utils::Hash.new('foo' => 'bar').deep_dup
duped.must_be_kind_of(Hanami::Utils::Hash)
end
it 'returns a hash with duplicated values' do
hash = Hanami::Utils::Hash.new('foo' => 'bar', 'baz' => 'x')
duped = hash.deep_dup
duped['foo'] = nil
duped['baz'].upcase!
hash['foo'].must_equal('bar')
hash['baz'].must_equal('x')
end
it "doesn't try to duplicate value that can't perform this operation" do
original = {
'nil' => nil,
'false' => false,
'true' => true,
'symbol' => :symbol,
'fixnum' => 23,
'bignum' => 13289301283 ** 2,
'float' => 1.0,
'complex' => Complex(0.3),
'bigdecimal' => BigDecimal.new('12.0001'),
'rational' => Rational(0.3)
}
hash = Hanami::Utils::Hash.new(original)
duped = hash.deep_dup
duped.must_equal(original)
duped.object_id.wont_equal(original.object_id)
end
it 'returns a hash with nested duplicated values' do
hash = Hanami::Utils::Hash.new('foo' => {'bar' => 'baz'}, 'x' => Hanami::Utils::Hash.new('y' => 'z'))
duped = hash.deep_dup
duped['foo']['bar'].reverse!
duped['x']['y'].upcase!
hash['foo']['bar'].must_equal('baz')
hash['x']['y'].must_equal('z')
end
it 'preserves original class' do
duped = Hanami::Utils::Hash.new('foo' => {}, 'x' => Hanami::Utils::Hash.new).deep_dup
duped['foo'].must_be_kind_of(::Hash)
duped['x'].must_be_kind_of(Hanami::Utils::Hash)
end
end
describe 'hash interface' do
it 'returns a new Hanami::Utils::Hash for methods which return a ::Hash' do
hash = Hanami::Utils::Hash.new({'a' => 1})
result = hash.clear
assert hash.empty?
result.must_be_kind_of(Hanami::Utils::Hash)
end
it 'returns a value that is compliant with ::Hash return value' do
hash = Hanami::Utils::Hash.new({'a' => 1})
result = hash.assoc('a')
result.must_equal ['a', 1]
end
it 'responds to whatever ::Hash responds to' do
hash = Hanami::Utils::Hash.new({'a' => 1})
hash.must_respond_to :rehash
hash.wont_respond_to :unknown_method
end
it 'accepts blocks for methods' do
hash = Hanami::Utils::Hash.new({'a' => 1})
result = hash.delete_if {|k, _| k == 'a' }
assert result.empty?
end
describe '#to_h' do
it 'returns a ::Hash' do
actual = Hanami::Utils::Hash.new({'a' => 1}).to_h
actual.must_equal({'a' => 1})
end
it 'returns nested ::Hash' do
hash = {
tutorial: {
instructions: [
{title: 'foo', body: 'bar'},
{title: 'hoge', body: 'fuga'}
]
}
}
utils_hash = Hanami::Utils::Hash.new(hash)
utils_hash.wont_be_kind_of(::Hash)
actual = utils_hash.to_h
actual.must_equal(hash)
actual[:tutorial].must_be_kind_of(::Hash)
actual[:tutorial][:instructions].each do |h|
h.must_be_kind_of(::Hash)
end
end
it 'returns nested ::Hash (when symbolized)' do
hash = {
'tutorial' => {
'instructions' => [
{'title' => 'foo', 'body' => 'bar'},
{'title' => 'hoge', 'body' => 'fuga'}
]
}
}
utils_hash = Hanami::Utils::Hash.new(hash).symbolize!
utils_hash.wont_be_kind_of(::Hash)
actual = utils_hash.to_h
actual.must_equal(hash)
actual[:tutorial].must_be_kind_of(::Hash)
actual[:tutorial][:instructions].each do |h|
h.must_be_kind_of(::Hash)
end
end
it 'prevents information escape' do
actual = Hanami::Utils::Hash.new({'a' => 1})
hash = actual.to_h
hash.merge!('b' => 2)
actual.to_h.must_equal({'a' => 1})
end
it 'prevents information escape for nested hash'
# it 'prevents information escape for nested hash' do
# actual = Hanami::Utils::Hash.new({'a' => {'b' => 2}})
# hash = actual.to_h
# subhash = hash['a']
# subhash.merge!('c' => 3)
# actual.to_h.must_equal({'a' => {'b' => 2}})
# end
end
describe '#to_hash' do
it 'returns a ::Hash' do
actual = Hanami::Utils::Hash.new({'a' => 1}).to_hash
actual.must_equal({'a' => 1})
end
it 'returns nested ::Hash' do
hash = {
tutorial: {
instructions: [
{title: 'foo', body: 'bar'},
{title: 'hoge', body: 'fuga'}
]
}
}
utils_hash = Hanami::Utils::Hash.new(hash)
utils_hash.wont_be_kind_of(::Hash)
actual = utils_hash.to_h
actual.must_equal(hash)
actual[:tutorial].must_be_kind_of(::Hash)
actual[:tutorial][:instructions].each do |h|
h.must_be_kind_of(::Hash)
end
end
it 'returns nested ::Hash (when symbolized)' do
hash = {
'tutorial' => {
'instructions' => [
{'title' => 'foo', 'body' => 'bar'},
{'title' => 'hoge', 'body' => 'fuga'}
]
}
}
utils_hash = Hanami::Utils::Hash.new(hash).symbolize!
utils_hash.wont_be_kind_of(::Hash)
actual = utils_hash.to_h
actual.must_equal(hash)
actual[:tutorial].must_be_kind_of(::Hash)
actual[:tutorial][:instructions].each do |h|
h.must_be_kind_of(::Hash)
end
end
it 'prevents information escape' do
actual = Hanami::Utils::Hash.new({'a' => 1})
hash = actual.to_hash
hash.merge!('b' => 2)
actual.to_hash.must_equal({'a' => 1})
end
end
describe '#to_a' do
it 'returns an ::Array' do
actual = Hanami::Utils::Hash.new({'a' => 1}).to_a
actual.must_equal([['a', 1]])
end
it 'prevents information escape' do
actual = Hanami::Utils::Hash.new({'a' => 1})
array = actual.to_a
array.push(['b', 2])
actual.to_a.must_equal([['a', 1]])
end
end
describe 'equality' do
it 'has a working equality' do
hash = Hanami::Utils::Hash.new({'a' => 1})
other = Hanami::Utils::Hash.new({'a' => 1})
assert hash == other
end
it 'has a working equality with raw hashes' do
hash = Hanami::Utils::Hash.new({'a' => 1})
assert hash == {'a' => 1}
end
end
describe 'case equality' do
it 'has a working case equality' do
hash = Hanami::Utils::Hash.new({'a' => 1})
other = Hanami::Utils::Hash.new({'a' => 1})
assert hash === other
end
it 'has a working case equality with raw hashes' do
hash = Hanami::Utils::Hash.new({'a' => 1})
assert hash === {'a' => 1}
end
end
describe 'value equality' do
it 'has a working value equality' do
hash = Hanami::Utils::Hash.new({'a' => 1})
other = Hanami::Utils::Hash.new({'a' => 1})
assert hash.eql?(other)
end
it 'has a working value equality with raw hashes' do
hash = Hanami::Utils::Hash.new({'a' => 1})
assert hash.eql?({'a' => 1})
end
end
describe 'identity equality' do
it 'has a working identity equality' do
hash = Hanami::Utils::Hash.new({'a' => 1})
assert hash.equal?(hash)
end
it 'has a working identity equality with raw hashes' do
hash = Hanami::Utils::Hash.new({'a' => 1})
assert !hash.equal?({'a' => 1})
end
end
describe '#hash' do
it 'returns the same hash result of ::Hash' do
expected = {'l' => 23}.hash
actual = Hanami::Utils::Hash.new({'l' => 23}).hash
actual.must_equal expected
end
end
describe '#inspect' do
it 'returns the same output of ::Hash' do
expected = {'l' => 23, l: 23}.inspect
actual = Hanami::Utils::Hash.new({'l' => 23, l: 23}).inspect
actual.must_equal expected
end
end
describe 'unknown method' do
it 'raises error' do
begin
Hanami::Utils::Hash.new('l' => 23).party!
rescue NoMethodError => e
e.message.must_equal %(undefined method `party!' for {\"l\"=>23}:Hanami::Utils::Hash)
end
end
# See: https://github.com/hanami/utils/issues/48
it 'returns the correct object when a NoMethodError is raised' do
hash = Hanami::Utils::Hash.new({'a' => 1})
exception_message = if Hanami::Utils.rubinius?
"undefined method `foo' on 1:Fixnum."
else
"undefined method `foo' for 1:Fixnum"
end
exception = -> { hash.all? { |_, v| v.foo } }.must_raise NoMethodError
exception.message.must_match exception_message
end
end
end
end