describe Object do describe '#deep_symbolize' do subject { obj.deep_symbolize } context 'when object is an Hash' do let(:obj) { { 'key1' => 'value1', key2: 'value2' } } it { should eq(key1: 'value1', key2: 'value2') } context 'with inner Hashes' do before { obj['key2'] = { 'key3' => { 'key4' => 'value4' } } } it { should eq(key1: 'value1', key2: { key3: { key4: 'value4' } }) } end end context 'when object is an Array' do let(:obj) { ['value1'] } it { should eq obj } context 'with inner Hashes' do let(:obj) { [{ 'key1' => 'value1' }, { 'key2' => 'value2' }] } it { should eq([{ key1: 'value1' }, { key2: 'value2' }]) } end end context 'when object is something else' do let(:obj) { 'value1' } it { should eq obj } end end describe '#validate_structure!' do subject { obj.validate_structure!(structure) } context 'when object type differs of the structure type' do let(:obj) { 'a string' } let(:structure) { { type: Integer } } it { expect { subject }.to raise_error /expected Integer, got String/i } end context 'when object is a Nil' do let(:obj) { nil } let(:structure) { { type: Integer } } it { expect { subject }.to raise_error /expected Integer, got NilClass/i } context 'but is also optional' do before { structure[:optional] = true } it { expect { subject }.not_to raise_error } end end context 'when object is an Array' do let(:obj) { %w[elem1 elem2] } let(:structure) { { type: Array } } it { expect { subject }.not_to raise_error } context 'given an element structure' do before { structure[:elements] = { type: String } } it { expect { subject }.not_to raise_error } context 'which is not respected' do before { structure[:elements] = { type: Integer } } it { expect { subject }.to raise_error /expected Integer, got String/i } end end end context 'when object is an Hash' do let(:obj) { { key1: 'value1', key2: 'value2', key3: 'value3' } } let(:structure) { { type: Hash } } it { expect { subject }.not_to raise_error } context 'when given an element structure' do let(:structure) { { type: Hash, elements: { key1: { type: String }, key2: { type: String }, key3: { type: String } } } } it { expect { subject }.not_to raise_error } context 'which is not respected' do before { obj[:key2] = {} } it { expect { subject }.to raise_error /expected String, got Hash/i } end context 'when missing keys' do before do obj.delete(:key1) obj.delete(:key3) end it { expect { subject }.to raise_error /missing keys: key1, key3/i } context 'which are optional' do before do structure[:elements][:key1][:optional] = true structure[:elements][:key3][:optional] = true end it { expect { subject }.not_to raise_error } end end end end end end