# Copyright (C) 2020 MongoDB Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require "spec_helper" describe "BSON::ExtJSON.parse" do let(:parsed) { BSON::ExtJSON.parse_obj(input) } context 'when input is true' do let(:input) { true } it 'returns true' do parsed.should == true end end context 'when input is false' do let(:input) { false } it 'returns false' do parsed.should == false end end context 'when input is nil' do let(:input) { nil } it 'returns nil' do parsed.should be nil end end context 'when input is a string' do let(:input) { 'hello' } it 'returns the string' do parsed.should == 'hello' end end context 'when input is a BSON timestamp' do let(:input) { {'$timestamp' => {'t' => 12345, 'i' => 42}} } it 'returns a BSON::Timestamp instance' do parsed.should == BSON::Timestamp.new(12345, 42) end end context 'when input is an ISO time' do let(:input) { {'$date' => '1970-01-01T00:00:04Z'} } it 'returns a Time instance ' do parsed.should be_a(Time) end it 'returns a Time instance with correct value' do parsed.should == Time.at(4) end it 'returns a Time instance in UTC' do parsed.zone.should == 'UTC' end end context 'when input is a Unix timestamp' do let(:input) { {'$date' => {'$numberLong' => '4000'}} } it 'returns a Time instance ' do parsed.should be_a(Time) end it 'returns a Time instance with correct value' do parsed.should == Time.at(4) end it 'returns a Time instance in UTC' do parsed.zone.should == 'UTC' end end context 'when input is an int32' do let(:input) do {'$numberInt' => '42'} end let(:parsed) { BSON::ExtJSON.parse_obj(input, mode: mode) } context 'when :mode is nil' do let(:mode) { nil } it 'returns Integer instance' do parsed.should be_a(Integer) parsed.should == 42 end end context 'when :mode is :bson' do let(:mode) { :bson } it 'returns Integer instance' do parsed.should be_a(Integer) parsed.should == 42 end end end context 'when input is an int64' do let(:input) do {'$numberLong' => '42'} end let(:parsed) { BSON::ExtJSON.parse_obj(input, mode: mode) } context 'when :mode is nil' do let(:mode) { nil } it 'returns Integer instance' do parsed.should be_a(Integer) parsed.should == 42 end end context 'when :mode is :bson' do let(:mode) { :bson } it 'returns Int64 instance' do parsed.should be_a(BSON::Int64) parsed.value.should == 42 end end end context 'when input is a hash' do let(:input) do {} end let(:parsed) { BSON::ExtJSON.parse_obj(input, mode: mode) } let(:mode) { :bson } context 'when mode is invalid' do let(:mode) { :foo } it 'raises an exception' do lambda do parsed end.should raise_error(ArgumentError, /Invalid value for :mode option/) end end context 'when it contains a string key with a null byte' do let(:input) do { "key\x00" => 1 } end it 'raises an exception' do lambda do parsed end.should raise_error(BSON::Error::ExtJSONParseError, /Hash key cannot contain a null byte/) end end context 'when it contains a symbol key with a null byte' do let(:input) do { "key\x00".to_sym => 1 } end it 'raises an exception' do lambda do parsed end.should raise_error(BSON::Error::ExtJSONParseError, /Hash key cannot contain a null byte/) end end context 'when it contains an integer key' do let(:input) do { 0 => 1 } end it 'does not raises an exception' do lambda do parsed end.should_not raise_error end end end context 'when input is a binary' do let(:data) do Base64.decode64("//8=") end context 'in current format' do let(:input) do { "$binary" => { "base64"=>"//8=", "subType"=>"00" } } end context 'when :mode is nil' do let(:mode) { nil } it 'returns BSON::Binary instance' do parsed.should be_a(BSON::Binary) parsed.data.should == data end end context 'when mode is :bson' do let(:mode) { :bson } it 'returns BSON::Binary instance' do parsed.should be_a(BSON::Binary) parsed.data.should == data end end end context 'in legacy format' do let(:input) do { "$binary"=>"//8=", "$type"=>"00" } end context 'when :mode is nil' do let(:mode) { nil } it 'returns BSON::Binary instance' do parsed.should be_a(BSON::Binary) parsed.data.should == data end end context 'when mode is :bson' do let(:mode) { :bson } it 'returns BSON::Binary instance' do parsed.should be_a(BSON::Binary) parsed.data.should == data end end end end context 'when input is a regex' do let(:pattern) { 'abc' } let(:options) { 'im' } context 'in current format' do let(:input) do { "$regularExpression" => { "pattern" => pattern, "options" => options } } end context 'when :mode is nil' do let(:mode) { nil } it 'returns a BSON::Regexp::Raw instance' do parsed.should be_a(BSON::Regexp::Raw) parsed.pattern.should == pattern parsed.options.should == options end end context 'when :mode is :bson' do let(:mode) { :bson } it 'returns a BSON::Regexp::Raw instance' do parsed.should be_a(BSON::Regexp::Raw) parsed.pattern.should == pattern parsed.options.should == options end end end context 'in legacy format' do let(:input) do { "$regex" => pattern, "$options" => options } end context 'when :mode is nil' do let(:mode) { nil } it 'returns a BSON::Regexp::Raw instance' do parsed.should be_a(BSON::Regexp::Raw) parsed.pattern.should == pattern parsed.options.should == options end end context 'when :mode is :bson' do let(:mode) { :bson } it 'returns a BSON::Regexp::Raw instance' do parsed.should be_a(BSON::Regexp::Raw) parsed.pattern.should == pattern parsed.options.should == options end end end context 'when $regularExpression is nested in $regex' do context 'with options' do let(:input) do { "$regex" => { "$regularExpression" => { "pattern" => "foo*", "options" => "" }, }, "$options" => "ix", } end it 'parses' do parsed.should == { '$regex' => BSON::Regexp::Raw.new('foo*'), '$options' => 'ix' } end end context 'without options' do let(:input) do { "$regex" => { "$regularExpression" => { "pattern" => "foo*", "options" => "" }, }, } end it 'parses' do parsed.should == { '$regex' => BSON::Regexp::Raw.new('foo*'), } end end end end end