lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb in ably-rest-0.8.2 vs lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb in ably-rest-0.8.3

- old
+ new

@@ -262,11 +262,11 @@ it 'raises an exception' do channel.attach do channel.transition_state_machine :failed, RuntimeError.new expect(channel).to be_failed - expect { channel.detach }.to raise_error Ably::Exceptions::StateChangeError + expect { channel.detach }.to raise_error Ably::Exceptions::InvalidStateChange stop_reactor end end end @@ -302,10 +302,28 @@ channel.attach do channel.detach end end end + + context 'when state is :initialized' do + it 'does nothing as there is no channel to detach' do + expect(channel).to be_initialized + channel.detach do + expect(channel).to be_initialized + stop_reactor + end + end + + it 'returns a valid deferrable' do + expect(channel).to be_initialized + channel.detach.callback do + expect(channel).to be_initialized + stop_reactor + end + end + end end describe 'channel recovery in :attaching state' do context 'the transport is disconnected before the ATTACHED protocol message is received' do skip 'attach times out and fails if not ATTACHED protocol message received' @@ -313,10 +331,13 @@ skip 'sends an ATTACH protocol message in response to a channel message being received on the attaching channel' end end context '#publish' do + let(:name) { random_str } + let(:data) { random_str } + context 'when attached' do it 'publishes messages' do channel.attach do 3.times { channel.publish('event', payload) } end @@ -352,11 +373,202 @@ message_indexes = messages.map { |msg| msg.id.split(':')[1] } expect(message_indexes).to include("0", "1", "2") stop_reactor end end + + context 'with :queue_messages client option set to false' do + let(:client_options) { default_options.merge(queue_messages: false) } + + context 'and connection state initialized' do + it 'raises an exception' do + expect { channel.publish('event') }.to raise_error Ably::Exceptions::MessageQueueingDisabled + expect(client.connection).to be_initialized + stop_reactor + end + end + + context 'and connection state connecting' do + it 'raises an exception' do + client.connect + EventMachine.next_tick do + expect { channel.publish('event') }.to raise_error Ably::Exceptions::MessageQueueingDisabled + expect(client.connection).to be_connecting + stop_reactor + end + end + end + + context 'and connection state disconnected' do + let(:client_options) { default_options.merge(queue_messages: false, :log_level => :error ) } + it 'raises an exception' do + client.connection.once(:connected) do + client.connection.once(:disconnected) do + expect { channel.publish('event') }.to raise_error Ably::Exceptions::MessageQueueingDisabled + expect(client.connection).to be_disconnected + stop_reactor + end + client.connection.transition_state_machine :disconnected + end + end + end + + context 'and connection state connected' do + it 'publishes the message' do + client.connection.once(:connected) do + channel.publish('event') + stop_reactor + end + end + end + end end + + context 'with name and data arguments' do + it 'publishes the message and return true indicating success' do + channel.publish(name, data) do + channel.history do |page| + expect(page.items.first.name).to eql(name) + expect(page.items.first.data).to eql(data) + stop_reactor + end + end + end + end + + context 'with an array of Hash objects with :name and :data attributes' do + let(:messages) do + 10.times.map do |index| + { name: index.to_s, data: { "index" => index + 10 } } + end + end + + it 'publishes an array of messages in one ProtocolMessage' do + published = false + + channel.attach do + client.connection.__outgoing_protocol_msgbus__.once(:protocol_message) do |protocol_message| + expect(protocol_message.messages.count).to eql(messages.count) + published = true + end + + channel.publish(messages).callback do + channel.history do |page| + expect(page.items.map(&:name)).to match_array(messages.map { |message| message[:name] }) + expect(page.items.map(&:data)).to match_array(messages.map { |message| message[:data] }) + expect(published).to eql(true) + stop_reactor + end + end + end + end + end + + context 'with an array of Message objects' do + let(:messages) do + 10.times.map do |index| + Ably::Models::Message(name: index.to_s, data: { "index" => index + 10 }) + end + end + + it 'publishes an array of messages in one ProtocolMessage' do + published = false + + channel.attach do + client.connection.__outgoing_protocol_msgbus__.once(:protocol_message) do |protocol_message| + expect(protocol_message.messages.count).to eql(messages.count) + published = true + end + + channel.publish(messages).callback do + channel.history do |page| + expect(page.items.map(&:name)).to match_array(messages.map { |message| message[:name] }) + expect(page.items.map(&:data)).to match_array(messages.map { |message| message[:data] }) + expect(published).to eql(true) + stop_reactor + end + end + end + end + + context 'with two invalid message out of 12' do + let(:client_options) { default_options.merge(client_id: 'valid') } + let(:invalid_messages) do + 2.times.map do |index| + Ably::Models::Message(name: index.to_s, data: { "index" => index + 10 }, client_id: 'prohibited') + end + end + + it 'calls the errback once' do + skip 'Waiting for issue #256 to be resolved' + channel.publish(messages + invalid_messages).tap do |deferrable| + deferrable.callback do + raise 'Publish should have failed' + end + + deferrable.errback do |error, message| + # TODO: Review whether we should fail once or multiple times + channel.history do |page| + expect(page.items.count).to eql(0) + stop_reactor + end + end + end + end + end + + context 'only invalid messages' do + let(:client_options) { default_options.merge(client_id: 'valid') } + let(:invalid_messages) do + 10.times.map do |index| + Ably::Models::Message(name: index.to_s, data: { "index" => index + 10 }, client_id: 'prohibited') + end + end + + it 'calls the errback once' do + skip 'Waiting for issue #256 to be resolved' + channel.publish(invalid_messages).tap do |deferrable| + deferrable.callback do + raise 'Publish should have failed' + end + + deferrable.errback do |error, message| + channel.history do |page| + expect(page.items.count).to eql(0) + stop_reactor + end + end + end + end + end + end + + context 'with many many messages and many connections simultaneously' do + let(:connection_count) { 5 } + let(:messages) { 5.times.map { |index| { name: "test", data: "message-#{index}" } } } + let(:published) { [] } + let(:channel_name) { random_str } + + it 'publishes all messages, all success callbacks are called, and a history request confirms all messages were published' do + connection_count.times.map do + Ably::Realtime::Client.new(client_options) + end.each do |client| + channel = client.channels.get(channel_name) + messages.each do |message| + channel.publish(message.fetch(:name), message.fetch(:data)) do + published << message + if published.count == connection_count * messages.count + channel.history do |history_page| + expect(history_page.items.count).to eql(connection_count * messages.count) + stop_reactor + end + end + end + end + end + end + end end describe '#subscribe' do context 'with an event argument' do it 'subscribes for a single event' do @@ -366,10 +578,24 @@ end channel.publish('click', 'data') end end + context 'before attach' do + it 'receives messages as soon as attached' do + channel.subscribe('click') do |message| + expect(channel).to be_attached + expect(message.data).to eql('data') + stop_reactor + end + + channel.publish('click', 'data') + + expect(channel).to be_attaching + end + end + context 'with no event argument' do it 'subscribes for all events' do channel.subscribe do |message| expect(message.data).to eql('data') stop_reactor @@ -428,42 +654,45 @@ end end context 'when connection state changes to' do context ':failed' do - let(:connection_error) { Ably::Exceptions::ConnectionError.new('forced failure', 500, 50000) } + let(:connection_error) { Ably::Exceptions::ConnectionFailed.new('forced failure', 500, 50000) } let(:client_options) { default_options.merge(log_level: :none) } def fake_error(error) client.connection.manager.error_received_from_server error end context 'an :attached channel' do it 'transitions state to :failed' do channel.attach do channel.on(:failed) do |error| - expect(error).to eql(connection_error) + expect(error).to be_a(Ably::Exceptions::ConnectionFailed) + expect(error.code).to eql(80002) stop_reactor end fake_error connection_error end end it 'emits an error event on the channel' do channel.attach do channel.on(:error) do |error| - expect(error).to eql(connection_error) + expect(error).to be_a(Ably::Exceptions::ConnectionFailed) + expect(error.code).to eql(80002) stop_reactor end fake_error connection_error end end it 'updates the channel error_reason' do channel.attach do channel.on(:failed) do |error| - expect(channel.error_reason).to eql(connection_error) + expect(error).to be_a(Ably::Exceptions::ConnectionFailed) + expect(error.code).to eql(80002) stop_reactor end fake_error connection_error end end @@ -507,10 +736,22 @@ channel.transition_state_machine :failed, original_error end end end + + context 'a channel ATTACH request' do + it 'raises an exception' do + client.connect do + client.connection.once(:failed) do + expect { channel.attach }.to raise_error Ably::Exceptions::InvalidStateChange + stop_reactor + end + fake_error connection_error + end + end + end end context ':closed' do context 'an :attached channel' do it 'transitions state to :detached' do @@ -562,9 +803,40 @@ channel.transition_state_machine :failed, original_error end end end + + context 'a channel ATTACH request when connection CLOSED' do + it 'raises an exception' do + client.connect do + client.connection.once(:closed) do + expect { channel.attach }.to raise_error Ably::Exceptions::InvalidStateChange + stop_reactor + end + client.close + end + end + end + + context 'a channel ATTACH request when connection CLOSING' do + it 'raises an exception' do + client.connect do + client.connection.once(:closing) do + expect { channel.attach }.to raise_error Ably::Exceptions::InvalidStateChange + stop_reactor + end + client.close + end + end + end + end + end + + describe '#presence' do + it 'returns a Ably::Realtime::Presence object' do + expect(channel.presence).to be_a(Ably::Realtime::Presence) + stop_reactor end end end end