spec/unit/models/message_spec.rb in ably-0.6.2 vs spec/unit/models/message_spec.rb in ably-0.7.0

- old
+ new

@@ -1,7 +1,8 @@ +# encoding: utf-8 require 'spec_helper' -require 'support/model_helper' +require 'shared/model_behaviour' require 'base64' require 'msgpack' describe Ably::Models::Message do include Ably::Modules::Conversions @@ -14,25 +15,132 @@ let(:model_args) { [protocol_message] } end context '#timestamp' do let(:model) { subject.new({}, protocol_message) } - it 'retrieves attribute :timestamp from ProtocolMessage' do + + it 'retrieves attribute :timestamp as Time object from ProtocolMessage' do expect(model.timestamp).to be_a(Time) expect(model.timestamp.to_i).to be_within(1).of(Time.now.to_i) end end - context 'Java naming' do + context '#connection_id attribute' do + let(:protocol_connection_id) { random_str } + let(:protocol_message) { Ably::Models::ProtocolMessage.new('connectionId' => protocol_connection_id, action: 1, timestamp: protocol_message_timestamp) } + let(:model_connection_id) { random_str } + + context 'when this model has a connectionId attribute' do + context 'but no protocol message' do + let(:model) { subject.new('connectionId' => model_connection_id ) } + + it 'uses the model value' do + expect(model.connection_id).to eql(model_connection_id) + end + end + + context 'with a protocol message with a different connectionId' do + let(:model) { subject.new({ 'connectionId' => model_connection_id }, protocol_message) } + + it 'uses the model value' do + expect(model.connection_id).to eql(model_connection_id) + end + end + end + + context 'when this model has no connectionId attribute' do + context 'and no protocol message' do + let(:model) { subject.new({ }) } + + it 'uses the model value' do + expect(model.connection_id).to be_nil + end + end + + context 'with a protocol message with a connectionId' do + let(:model) { subject.new({ }, protocol_message) } + + it 'uses the model value' do + expect(model.connection_id).to eql(protocol_connection_id) + end + end + end + end + + context 'Java naming', :api_private do let(:model) { subject.new({ clientId: 'joe' }, protocol_message) } it 'converts the attribute to ruby symbol naming convention' do expect(model.client_id).to eql('joe') end end - context '#to_json' do + context 'initialized with' do + %w(name client_id encoding).each do |attribute| + context ":#{attribute}" do + let(:encoded_value) { value.encode(encoding) } + let(:value) { random_str } + let(:options) { { attribute.to_sym => encoded_value } } + let(:model) { subject.new(options, protocol_message) } + let(:model_attribute) { model.public_send(attribute) } + + context 'as UTF_8 string' do + let(:encoding) { Encoding::UTF_8 } + + it 'is permitted' do + expect(model_attribute).to eql(encoded_value) + end + + it 'remains as UTF-8' do + expect(model_attribute.encoding).to eql(encoding) + end + end + + context 'as SHIFT_JIS string' do + let(:encoding) { Encoding::SHIFT_JIS } + + it 'gets converted to UTF-8' do + expect(model_attribute.encoding).to eql(Encoding::UTF_8) + end + + it 'is compatible with original encoding' do + expect(model_attribute.encode(encoding)).to eql(encoded_value) + end + end + + context 'as ASCII_8BIT string' do + let(:encoding) { Encoding::ASCII_8BIT } + + it 'gets converted to UTF-8' do + expect(model_attribute.encoding).to eql(Encoding::UTF_8) + end + + it 'is compatible with original encoding' do + expect(model_attribute.encode(encoding)).to eql(encoded_value) + end + end + + context 'as Integer' do + let(:encoded_value) { 1 } + + it 'raises an argument error' do + expect { model_attribute }.to raise_error ArgumentError, /must be a String/ + end + end + + context 'as Nil' do + let(:encoded_value) { nil } + + it 'is permitted' do + expect(model_attribute).to be_nil + end + end + end + end + end + + context '#to_json', :api_private do let(:json_object) { JSON.parse(model.to_json) } context 'with valid data' do let(:model) { subject.new({ name: 'test', clientId: 'joe' }, protocol_message) } @@ -48,11 +156,11 @@ expect { model.to_json }.to raise_error RuntimeError, /cannot generate a valid Hash/ end end context 'with binary data' do - let(:data) { MessagePack.pack(SecureRandom.hex(32)) } + let(:data) { MessagePack.pack(random_str(32)) } let(:model) { subject.new({ name: 'test', data: data }, protocol_message) } it 'encodes as Base64 so that it can be converted to UTF-8 automatically by JSON#dump' do expect(json_object["data"]).to eql(::Base64.encode64(data)) end @@ -61,41 +169,55 @@ expect(json_object["encoding"]).to eql('base64') end end end - context 'from REST request with embedded fields' do - let(:id) { SecureRandom.hex } - let(:message_time) { Time.now + 60 } - let(:timestamp) { as_since_epoch(message_time) } - let(:model) { subject.new(id: id, timestamp: timestamp) } + context 'from REST request with embedded fields', :api_private do + let(:id) { random_str } + let(:protocol_message_id) { random_str } + let(:message_time) { Time.now + 60 } + let(:message_timestamp) { as_since_epoch(message_time) } + let(:protocol_time) { Time.now } + let(:protocol_timestamp) { as_since_epoch(protocol_time) } + let(:protocol_message) do + Ably::Models::ProtocolMessage.new({ + action: :message, + timestamp: protocol_timestamp, + id: protocol_message_id + }) + end + context 'with protocol message' do + let(:model) { subject.new({ id: id, timestamp: message_timestamp }, protocol_message) } + specify '#id prefers embedded ID' do expect(model.id).to eql(id) end specify '#timestamp prefers embedded timestamp' do expect(model.timestamp.to_i).to be_within(1).of(message_time.to_i) end end context 'without protocol message' do + let(:model) { subject.new(id: id, timestamp: message_timestamp) } + specify '#id uses embedded ID' do expect(model.id).to eql(id) end specify '#timestamp uses embedded timestamp' do expect(model.timestamp.to_i).to be_within(1).of(message_time.to_i) end end end - context 'part of ProtocolMessage' do + context 'part of ProtocolMessage', :api_private do let(:ably_time) { Time.now + 5 } - let(:message_serial) { SecureRandom.random_number(1_000_000) } - let(:connection_id) { SecureRandom.hex } + let(:message_serial) { random_int_str(1_000_000) } + let(:connection_id) { random_str } let(:message_0_payload) do { 'string_key' => 'string_value', 1 => 2, @@ -115,11 +237,11 @@ name: 'one', data: 'simple string' } end - let(:protocol_message_id) { SecureRandom.hex } + let(:protocol_message_id) { random_str } let(:protocol_message) do Ably::Models::ProtocolMessage.new({ action: :message, timestamp: ably_time.to_i, msg_serial: message_serial, @@ -148,12 +270,35 @@ end it 'should not allow changes to the payload' do expect { message_0.data["test"] = true }.to raise_error RuntimeError, /can't modify frozen Hash/ end + + context 'with identical message objects' do + let(:protocol_message) do + Ably::Models::ProtocolMessage.new({ + action: :message, + timestamp: ably_time.to_i, + msg_serial: message_serial, + id: protocol_message_id, + messages: [ + message_0_json, message_0_json, message_0_json + ] + }) + end + + it 'provide a unique ID:index' do + expect(protocol_message.messages.map(&:id).uniq.count).to eql(3) + end + + it 'recognises the index based on the object ID as opposed to message payload' do + expect(protocol_message.messages.first.id).to match(/0$/) + expect(protocol_message.messages.last.id).to match(/2$/) + end + end end - context 'Message conversion method' do + context 'Message conversion method', :api_private do let(:json) { { name: 'test', data: 'conversion' } } context 'with JSON' do context 'without ProtocolMessage' do subject { Ably::Models.Message(json) }