spec/lib/protobuf/message_spec.rb in protobuf-3.6.12 vs spec/lib/protobuf/message_spec.rb in protobuf-3.7.0.pre0

- old
+ new

@@ -179,68 +179,10 @@ it "initializes with an object with a block" do test_enum = Test::EnumTestMessage.new { |p| p.non_default_enum = 2 } expect(test_enum.non_default_enum).to eq(2) end - - context 'ignoring unknown fields' do - around do |example| - orig = ::Protobuf.ignore_unknown_fields? - ::Protobuf.ignore_unknown_fields = true - example.call - ::Protobuf.ignore_unknown_fields = orig - end - - context 'with valid fields' do - let(:values) { { :name => "Jim" } } - - it "does not raise an error" do - expect { ::Test::Resource.new(values) }.to_not raise_error - end - end - - context 'with non-existent field' do - let(:values) { { :name => "Jim", :othername => "invalid" } } - - it "does not raise an error" do - expect { ::Test::Resource.new(values) }.to_not raise_error - end - end - end - - context 'not ignoring unknown fields' do - around do |example| - orig = ::Protobuf.ignore_unknown_fields? - ::Protobuf.ignore_unknown_fields = false - example.call - ::Protobuf.ignore_unknown_fields = orig - end - - context 'with valid fields' do - let(:values) { { :name => "Jim" } } - - it "does not raise an error" do - expect { ::Test::Resource.new(values) }.to_not raise_error - end - end - - context 'with non-existent field' do - let(:values) { { :name => "Jim", :othername => "invalid" } } - - it "raises an error and mentions the erroneous field" do - expect { ::Test::Resource.new(values) }.to raise_error(::Protobuf::FieldNotDefinedError, /othername/) - end - - context 'with a nil value' do - let(:values) { { :name => "Jim", :othername => nil } } - - it "raises an error and mentions the erroneous field" do - expect { ::Test::Resource.new(values) }.to raise_error(::Protobuf::FieldNotDefinedError, /othername/) - end - end - end - end end describe '#encode' do context "encoding" do it "accepts UTF-8 strings into string fields" do @@ -446,10 +388,33 @@ { :name => 'Resource 2' }, ], ) end end + + it 'uses simple field names as keys when possible and fully qualified names otherwise' do + message = Class.new(::Protobuf::Message) do + optional :int32, :field, 1 + optional :int32, :colliding_field, 2 + extensions 100...200 + optional :int32, :".ext.normal_ext_field", 100, :extension => true + optional :int32, :".ext.colliding_field", 101, :extension => true + optional :int32, :".ext.colliding_field2", 102, :extension => true + optional :int32, :".ext2.colliding_field2", 103, :extension => true + end + + hash = { + :field => 1, + :colliding_field => 2, + :normal_ext_field => 3, + :".ext.colliding_field" => 4, + :".ext.colliding_field2" => 5, + :".ext2.colliding_field2" => 6, + } + instance = message.new(hash) + expect(instance.to_hash).to eq(hash) + end end describe '#to_json' do subject do ::Test::ResourceFindRequest.new(:name => 'Test Name', :active => false) @@ -466,34 +431,61 @@ end end.not_to raise_error end end - describe "#define_setter" do + describe "#define_accessor" do subject { ::Test::Resource.new } - it "allows string fields to be set to nil" do + it 'allows string fields to be set to nil' do expect { subject.name = nil }.to_not raise_error end - it "does not allow string fields to be set to Numeric" do + it 'does not allow string fields to be set to Numeric' do expect { subject.name = 1 }.to raise_error(/name/) end + + context '#{simple_field_name}!' do + it 'returns value of set field' do + expect(::Test::Resource.new(:name => "Joe").name!).to eq("Joe") + end + + it 'returns value of set field with default' do + expect(::Test::Resource.new(:name => "").name!).to eq("") + end + + it 'returns nil if extension field is unset' do + expect(subject.ext_is_searchable!).to be_nil + end + + it 'returns value of set extension field' do + message = ::Test::Resource.new(:ext_is_searchable => true) + expect(message.ext_is_searchable!).to be(true) + end + + it 'returns value of set extension field with default' do + message = ::Test::Resource.new(:ext_is_searchable => false) + expect(message.ext_is_searchable!).to be(false) + end + end end describe '.get_extension_field' do it 'fetches an extension field by its tag' do field = ::Test::Resource.get_extension_field(100) expect(field).to be_a(::Protobuf::Field::BoolField) expect(field.tag).to eq(100) expect(field.name).to eq(:ext_is_searchable) + expect(field.fully_qualified_name).to eq(:'.test.Searchable.ext_is_searchable') expect(field).to be_extension end it 'fetches an extension field by its symbolized name' do expect(::Test::Resource.get_extension_field(:ext_is_searchable)).to be_a(::Protobuf::Field::BoolField) expect(::Test::Resource.get_extension_field('ext_is_searchable')).to be_a(::Protobuf::Field::BoolField) + expect(::Test::Resource.get_extension_field(:'.test.Searchable.ext_is_searchable')).to be_a(::Protobuf::Field::BoolField) + expect(::Test::Resource.get_extension_field('.test.Searchable.ext_is_searchable')).to be_a(::Protobuf::Field::BoolField) end it 'returns nil when attempting to get a non-extension field' do expect(::Test::Resource.get_extension_field(1)).to be_nil end @@ -502,28 +494,87 @@ expect(::Test::Resource.get_extension_field(-1)).to be_nil expect(::Test::Resource.get_extension_field(nil)).to be_nil end end + describe '#field?' do + it 'returns false for non-existent field' do + expect(::Test::Resource.get_field('doesnotexist')).to be_nil + expect(::Test::Resource.new.field?('doesnotexist')).to be(false) + end + + it 'returns false for unset field' do + expect(::Test::Resource.get_field('name')).to be + expect(::Test::Resource.new.field?('name')).to be(false) + end + + it 'returns false for unset field from tag' do + expect(::Test::Resource.get_field(1)).to be + expect(::Test::Resource.new.field?(1)).to be(false) + end + + it 'returns true for set field' do + expect(::Test::Resource.new(:name => "Joe").field?('name')).to be(true) + end + + it 'returns true for set field with default' do + expect(::Test::Resource.new(:name => "").field?('name')).to be(true) + end + + it 'returns true from field tag value' do + expect(::Test::Resource.new(:name => "Joe").field?(1)).to be(true) + end + + it 'returns false for unset extension field' do + ext_field = :".test.Searchable.ext_is_searchable" + expect(::Test::Resource.get_extension_field(ext_field)).to be + expect(::Test::Resource.new.field?(ext_field)).to be(false) + end + + it 'returns false for unset extension field from tag' do + expect(::Test::Resource.get_extension_field(100)).to be + expect(::Test::Resource.new.field?(100)).to be(false) + end + + it 'returns true for set extension field' do + ext_field = :".test.Searchable.ext_is_searchable" + message = ::Test::Resource.new(ext_field => true) + expect(message.field?(ext_field)).to be(true) + end + + it 'returns true for set extension field with default' do + ext_field = :".test.Searchable.ext_is_searchable" + message = ::Test::Resource.new(ext_field => false) + expect(message.field?(ext_field)).to be(true) + end + + it 'returns true for set extension field from tag' do + ext_field = :".test.Searchable.ext_is_searchable" + message = ::Test::Resource.new(ext_field => false) + expect(message.field?(100)).to be(true) + end + end + describe '.get_field' do it 'fetches a non-extension field by its tag' do field = ::Test::Resource.get_field(1) expect(field).to be_a(::Protobuf::Field::StringField) expect(field.tag).to eq(1) expect(field.name).to eq(:name) + expect(field.fully_qualified_name).to eq(:name) expect(field).not_to be_extension end it 'fetches a non-extension field by its symbolized name' do expect(::Test::Resource.get_field(:name)).to be_a(::Protobuf::Field::StringField) expect(::Test::Resource.get_field('name')).to be_a(::Protobuf::Field::StringField) end it 'fetches an extension field when forced' do expect(::Test::Resource.get_field(100, true)).to be_a(::Protobuf::Field::BoolField) - expect(::Test::Resource.get_field(:ext_is_searchable, true)).to be_a(::Protobuf::Field::BoolField) - expect(::Test::Resource.get_field('ext_is_searchable', true)).to be_a(::Protobuf::Field::BoolField) + expect(::Test::Resource.get_field(:'.test.Searchable.ext_is_searchable', true)).to be_a(::Protobuf::Field::BoolField) + expect(::Test::Resource.get_field('.test.Searchable.ext_is_searchable', true)).to be_a(::Protobuf::Field::BoolField) end it 'returns nil when attempting to get an extension field' do expect(::Test::Resource.get_field(100)).to be_nil end @@ -532,6 +583,220 @@ expect(::Test::Resource.get_field(-1)).to be_nil expect(::Test::Resource.get_field(nil)).to be_nil end end + describe 'defining a field' do + # Case 1 + context 'single base field' do + let(:klass) do + Class.new(Protobuf::Message) do + optional :string, :foo, 1 + end + end + + it 'has an accessor for foo' do + message = klass.new(:foo => 'bar') + expect(message.foo).to eq('bar') + expect(message[:foo]).to eq('bar') + expect(message['foo']).to eq('bar') + end + end + + # Case 2 + context 'base field and extension field name collision' do + let(:klass) do + Class.new(Protobuf::Message) do + optional :string, :foo, 1 + optional :string, :".boom.foo", 2, :extension => true + end + end + + it 'has an accessor for foo that refers to the base field' do + message = klass.new(:foo => 'bar', '.boom.foo' => 'bam') + expect(message.foo).to eq('bar') + expect(message[:foo]).to eq('bar') + expect(message['foo']).to eq('bar') + expect(message[:'.boom.foo']).to eq('bam') + expect(message['.boom.foo']).to eq('bam') + end + end + + # Case 3 + context 'no base field with extension fields with name collision' do + let(:klass) do + Class.new(Protobuf::Message) do + optional :string, :".boom.foo", 2, :extension => true + optional :string, :".goat.foo", 3, :extension => true + end + end + + it 'has an accessor for foo that refers to the extension field' do + message = klass.new('.boom.foo' => 'bam', '.goat.foo' => 'red') + expect { message.foo }.to raise_error(NoMethodError) + expect { message[:foo] }.to raise_error(ArgumentError) + expect { message['foo'] }.to raise_error(ArgumentError) + expect(message[:'.boom.foo']).to eq('bam') + expect(message['.boom.foo']).to eq('bam') + expect(message[:'.goat.foo']).to eq('red') + expect(message['.goat.foo']).to eq('red') + end + end + + # Case 4 + context 'no base field with an extension field' do + let(:klass) do + Class.new(Protobuf::Message) do + optional :string, :".boom.foo", 2, :extension => true + end + end + + it 'has an accessor for foo that refers to the extension field' do + message = klass.new('.boom.foo' => 'bam') + expect(message.foo).to eq('bam') + expect(message[:foo]).to eq('bam') + expect(message['foo']).to eq('bam') + expect(message[:'.boom.foo']).to eq('bam') + expect(message['.boom.foo']).to eq('bam') + end + end + end + + describe '.[]=' do + context 'clearing fields' do + it 'clears repeated fields with an empty array' do + instance = ::Test::Resource.new(:repeated_enum => [::Test::StatusType::ENABLED]) + expect(instance.field?(:repeated_enum)).to be(true) + instance[:repeated_enum] = [] + expect(instance.field?(:repeated_enum)).to be(false) + end + + it 'clears optional fields with nil' do + instance = ::Test::Resource.new(:name => "Joe") + expect(instance.field?(:name)).to be(true) + instance[:name] = nil + expect(instance.field?(:name)).to be(false) + end + + it 'clears optional extenstion fields with nil' do + instance = ::Test::Resource.new(:ext_is_searchable => true) + expect(instance.field?(:ext_is_searchable)).to be(true) + instance[:ext_is_searchable] = nil + expect(instance.field?(:ext_is_searchable)).to be(false) + end + end + + context 'setting fields' do + let(:instance) { ::Test::Resource.new } + + it 'sets and replaces repeated fields' do + initial = [::Test::StatusType::ENABLED, ::Test::StatusType::DISABLED] + instance[:repeated_enum] = initial + expect(instance[:repeated_enum]).to eq(initial) + replacement = [::Test::StatusType::DELETED] + instance[:repeated_enum] = replacement + expect(instance[:repeated_enum]).to eq(replacement) + end + + it 'sets acceptable optional field values' do + instance[:name] = "Joe" + expect(instance[:name]).to eq("Joe") + instance[1] = "Tom" + expect(instance[:name]).to eq("Tom") + end + + it 'sets acceptable empty string field values' do + instance[:name] = "" + expect(instance.name!).to eq("") + end + + it 'sets acceptable empty message field values' do + instance = ::Test::Nested.new + instance[:resource] = {} + expect(instance.resource!).to eq(::Test::Resource.new) + end + + it 'sets acceptable extension field values' do + instance[:ext_is_searchable] = true + expect(instance[:ext_is_searchable]).to eq(true) + instance[:".test.Searchable.ext_is_searchable"] = false + expect(instance[:ext_is_searchable]).to eq(false) + instance[100] = true + expect(instance[:ext_is_searchable]).to eq(true) + end + end + + context 'throwing TypeError' do + let(:instance) { ::Test::Resource.new } + + it 'throws when a repeated value is set with a non array' do + expect { instance[:repeated_enum] = "string" }.to raise_error(TypeError) + end + + it 'throws when a repeated value is set with an array of the wrong type' do + expect { instance[:repeated_enum] = [true, false] }.to raise_error(TypeError) + end + + it 'throws when an optional value is not #acceptable?' do + expect { instance[:name] = 1 }.to raise_error(TypeError) + end + end + + context 'ignoring unknown fields' do + around do |example| + orig = ::Protobuf.ignore_unknown_fields? + ::Protobuf.ignore_unknown_fields = true + example.call + ::Protobuf.ignore_unknown_fields = orig + end + + context 'with valid fields' do + let(:values) { { :name => "Jim" } } + + it "does not raise an error" do + expect { ::Test::Resource.new(values) }.to_not raise_error + end + end + + context 'with non-existent field' do + let(:values) { { :name => "Jim", :othername => "invalid" } } + + it "does not raise an error" do + expect { ::Test::Resource.new(values) }.to_not raise_error + end + end + end + + context 'not ignoring unknown fields' do + around do |example| + orig = ::Protobuf.ignore_unknown_fields? + ::Protobuf.ignore_unknown_fields = false + example.call + ::Protobuf.ignore_unknown_fields = orig + end + + context 'with valid fields' do + let(:values) { { :name => "Jim" } } + + it "does not raise an error" do + expect { ::Test::Resource.new(values) }.to_not raise_error + end + end + + context 'with non-existent field' do + let(:values) { { :name => "Jim", :othername => "invalid" } } + + it "raises an error and mentions the erroneous field" do + expect { ::Test::Resource.new(values) }.to raise_error(::Protobuf::FieldNotDefinedError, /othername/) + end + + context 'with a nil value' do + let(:values) { { :name => "Jim", :othername => nil } } + + it "raises an error and mentions the erroneous field" do + expect { ::Test::Resource.new(values) }.to raise_error(::Protobuf::FieldNotDefinedError, /othername/) + end + end + end + end + end end