spec/acceptance/realtime/presence_spec.rb in ably-0.8.1 vs spec/acceptance/realtime/presence_spec.rb in ably-0.8.2

- old
+ new

@@ -51,10 +51,111 @@ end end end end + context 'with supported data payload content type' do + def register_presence_and_check_data(method_name, data) + if method_name.to_s.match(/_client/) + presence_client_one.public_send(method_name, 'client_id', data: data) + else + presence_client_one.public_send(method_name, data: data) + end + + presence_client_one.subscribe do |presence_message| + expect(presence_message.data).to eql(data) + stop_reactor + end + end + + context 'JSON Object (Hash)' do + let(:data) { { 'Hash' => 'true' } } + + it 'is encoded and decoded to the same hash' do + setup_test(method_name, args, options) do + register_presence_and_check_data method_name, data + end + end + end + + context 'JSON Array' do + let(:data) { [ nil, true, false, 55, 'string', { 'Hash' => true }, ['array'] ] } + + it 'is encoded and decoded to the same Array' do + setup_test(method_name, args, options) do + register_presence_and_check_data method_name, data + end + end + end + + context 'String' do + let(:data) { random_str } + + it 'is encoded and decoded to the same Array' do + setup_test(method_name, args, options) do + register_presence_and_check_data method_name, data + end + end + end + + context 'Binary' do + let(:data) { Base64.encode64(random_str) } + + it 'is encoded and decoded to the same Array' do + setup_test(method_name, args, options) do + register_presence_and_check_data method_name, data + end + end + end + end + + context 'with unsupported data payload content type' do + def presence_action(method_name, data) + if method_name.to_s.match(/_client/) + presence_client_one.public_send(method_name, 'client_id', data: data) + else + presence_client_one.public_send(method_name, data: data) + end + end + + context 'Integer' do + let(:data) { 1 } + + it 'raises an UnsupportedDataTypeError 40011 exception' do + expect { presence_action(method_name, data) }.to raise_error(Ably::Exceptions::UnsupportedDataTypeError) + stop_reactor + end + end + + context 'Float' do + let(:data) { 1.1 } + + it 'raises an UnsupportedDataTypeError 40011 exception' do + expect { presence_action(method_name, data) }.to raise_error(Ably::Exceptions::UnsupportedDataTypeError) + stop_reactor + end + end + + context 'Boolean' do + let(:data) { true } + + it 'raises an UnsupportedDataTypeError 40011 exception' do + expect { presence_action(method_name, data) }.to raise_error(Ably::Exceptions::UnsupportedDataTypeError) + stop_reactor + end + end + + context 'False' do + let(:data) { false } + + it 'raises an UnsupportedDataTypeError 40011 exception' do + expect { presence_action(method_name, data) }.to raise_error(Ably::Exceptions::UnsupportedDataTypeError) + stop_reactor + end + end + end + it 'returns a SafeDeferrable that catches exceptions in callbacks and logs them' do setup_test(method_name, args, options) do expect(presence_client_one.public_send(method_name, args)).to be_a(Ably::Util::SafeDeferrable) stop_reactor end @@ -151,17 +252,19 @@ it 'is not synchronised when initially created' do expect(presence_anonymous_client.members).to_not be_sync_complete stop_reactor end - it 'will trigger an :in_sync event when synchronisation is complete' do + it 'will emit an :in_sync event when synchronisation is complete' do presence_client_one.enter presence_client_two.enter presence_anonymous_client.members.once(:in_sync) do stop_reactor end + + channel_anonymous_client.attach end context 'before server sync complete' do it 'behaves like an Enumerable allowing direct access to current members' do expect(presence_anonymous_client.members.count).to eql(0) @@ -178,10 +281,12 @@ member_ids = presence_anonymous_client.members.map(&:member_key) expect(member_ids.count).to eql(2) expect(member_ids.uniq.count).to eql(2) stop_reactor end + + channel_anonymous_client.attach end end end end @@ -198,11 +303,11 @@ context 'when attaching to a channel with members present' do it 'is false and the presence channel will subsequently be synced' do presence_client_one.enter do channel_anonymous_client.attach do expect(channel_anonymous_client.presence).to_not be_sync_complete - channel_anonymous_client.presence.get do + channel_anonymous_client.presence.get(wait_for_sync: true) do expect(channel_anonymous_client.presence).to be_sync_complete stop_reactor end end end @@ -317,11 +422,11 @@ end end end end - it 'does not emit :present after the :leave event has been emitted, and that member is not included in the list of members via #get' do + it 'does not emit :present after the :leave event has been emitted, and that member is not included in the list of members via #get with :wait_for_sync' do left_client = 10 left_client_id = "client:#{left_client}" setup_members_on(presence_client_one) do member_left_emitted = false @@ -339,11 +444,11 @@ end expect(leave_message.client_id).to eql(left_client_id) member_left_emitted = true end - presence_anonymous_client.get do |members| + presence_anonymous_client.get(wait_for_sync: true) do |members| expect(members.count).to eql(enter_expected_count - 1) expect(member_left_emitted).to eql(true) expect(members.map(&:client_id)).to_not include(left_client_id) stop_reactor end @@ -363,47 +468,44 @@ end end end context '#get' do - it 'waits until sync is complete', event_machine: 15 do - enter_expected_count.times do |index| - presence_client_one.enter_client("client:#{index}") do |message| - entered << message - next unless entered.count == enter_expected_count + context 'with :wait_for_sync option set to true' do + it 'waits until sync is complete', event_machine: 15 do + enter_expected_count.times do |index| + presence_client_one.enter_client("client:#{index}") do |message| + entered << message + next unless entered.count == enter_expected_count - presence_anonymous_client.get do |members| - expect(members.map(&:client_id).uniq.count).to eql(enter_expected_count) - expect(members.count).to eql(enter_expected_count) - stop_reactor + presence_anonymous_client.get(wait_for_sync: true) do |members| + expect(members.map(&:client_id).uniq.count).to eql(enter_expected_count) + expect(members.count).to eql(enter_expected_count) + stop_reactor + end end end end end - end - end - end - end - context 'automatic attachment of channel on access to presence object' do - it 'is implicit if presence state is initialized' do - channel_client_one.presence - channel_client_one.on(:attached) do - expect(channel_client_one.state).to eq(:attached) - stop_reactor - end - end + context 'by default' do + it 'it does not wait for sync', event_machine: 15 do + enter_expected_count.times do |index| + presence_client_one.enter_client("client:#{index}") do |message| + entered << message + next unless entered.count == enter_expected_count - it 'is disabled if presence state is not initialized' do - channel_client_one.attach do - channel_client_one.detach do - expect(channel_client_one.state).to eq(:detached) - - channel_client_one.presence # access the presence object - EventMachine.add_timer(1) do - expect(channel_client_one.state).to eq(:detached) - stop_reactor + channel_anonymous_client.attach do + presence_anonymous_client.get do |members| + expect(presence_anonymous_client.members).to_not be_in_sync + expect(members.count).to eql(0) + stop_reactor + end + end + end + end + end end end end end end @@ -569,11 +671,11 @@ end end end context 'when set to nil' do - it 'emits the previously defined value as a convenience' do + it 'emits a nil value for the data attribute when leaving' do presence_client_one.enter data: enter_data do presence_client_one.leave data: nil end presence_client_one.subscribe(:leave) do |presence_message| @@ -593,10 +695,26 @@ expect(presence_message.data).to eql(enter_data) stop_reactor end end end + + context 'and sync is complete' do + it 'does not cache members that have left' do + presence_client_one.enter data: enter_data do + expect(presence_client_one.members).to be_in_sync + expect(presence_client_one.members.send(:members).count).to eql(1) + presence_client_one.leave data: data + end + + presence_client_one.subscribe(:leave) do |presence_message| + expect(presence_message.data).to eql(data) + expect(presence_client_one.members.send(:members).count).to eql(0) + stop_reactor + end + end + end end it 'raises an exception if not entered' do expect { channel_anonymous_client.presence.leave }.to raise_error(Ably::Exceptions::Standard, /Unable to leave presence channel that is not entered/) stop_reactor @@ -909,47 +1027,49 @@ disconnected: { retry_every: 0.1, max_time_in_state: 0 }, suspended: { retry_every: 0.1, max_time_in_state: 0 } ) end - it 'fails if the connection fails' do - when_all(*connect_members_deferrables) do - channel_client_two.attach do - client_two.connection.transport.__incoming_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message| - if protocol_message.action == :sync - sync_pages_received << protocol_message - force_connection_failure client_two if sync_pages_received.count == 1 + context 'when :wait_for_sync is true' do + it 'fails if the connection fails' do + when_all(*connect_members_deferrables) do + channel_client_two.attach do + client_two.connection.transport.__incoming_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message| + if protocol_message.action == :sync + sync_pages_received << protocol_message + force_connection_failure client_two if sync_pages_received.count == 1 + end end end - end - presence_client_two.get.tap do |deferrable| - deferrable.callback { raise 'Get should not succeed' } - deferrable.errback do |error| - stop_reactor + presence_client_two.get(wait_for_sync: true).tap do |deferrable| + deferrable.callback { raise 'Get should not succeed' } + deferrable.errback do |error| + stop_reactor + end end end end - end - it 'fails if the channel is detached' do - when_all(*connect_members_deferrables) do - channel_client_two.attach do - client_two.connection.transport.__incoming_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message| - if protocol_message.action == :sync - # prevent any more SYNC messages coming through - client_two.connection.transport.__incoming_protocol_msgbus__.unsubscribe - channel_client_two.change_state :detaching - channel_client_two.change_state :detached + it 'fails if the channel is detached' do + when_all(*connect_members_deferrables) do + channel_client_two.attach do + client_two.connection.transport.__incoming_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message| + if protocol_message.action == :sync + # prevent any more SYNC messages coming through + client_two.connection.transport.__incoming_protocol_msgbus__.unsubscribe + channel_client_two.change_state :detaching + channel_client_two.change_state :detached + end end end - end - presence_client_two.get.tap do |deferrable| - deferrable.callback { raise 'Get should not succeed' } - deferrable.errback do |error| - stop_reactor + presence_client_two.get(wait_for_sync: true).tap do |deferrable| + deferrable.callback { raise 'Get should not succeed' } + deferrable.errback do |error| + stop_reactor + end end end end end end @@ -1057,16 +1177,16 @@ presence_client_two.subscribe(:enter) do clients_entered[:client_two] += 1 end wait_until(proc { clients_entered[:client_one] + clients_entered[:client_two] == total_members * 2 }) do - presence_anonymous_client.get do |anonymous_members| + presence_anonymous_client.get(wait_for_sync: true) do |anonymous_members| expect(anonymous_members.count).to eq(total_members) expect(anonymous_members.map(&:client_id).uniq.count).to eq(total_members) - presence_client_one.get do |client_one_members| - presence_client_two.get do |client_two_members| + presence_client_one.get(wait_for_sync: true) do |client_one_members| + presence_client_two.get(wait_for_sync: true) do |client_two_members| expect(client_one_members.count).to eq(total_members) expect(client_one_members.count).to eq(client_two_members.count) stop_reactor end end @@ -1317,11 +1437,11 @@ end end specify 'expect :left event with client data from enter event' do presence_client_one.subscribe(:leave) do |message| - presence_client_one.get do |members| + presence_client_one.get(wait_for_sync: true) do |members| expect(members.count).to eq(0) expect(message.data).to eql(data_payload) stop_reactor end end @@ -1333,12 +1453,11 @@ context 'connection failure mid-way through a large member sync' do let(:members_count) { 400 } let(:sync_pages_received) { [] } - # Will re-enable once https://github.com/ably/realtime/issues/91 is resolved - skip 'resumes the SYNC operation', em_timeout: 15 do + it 'resumes the SYNC operation', em_timeout: 15 do when_all(*members_count.times.map do |index| presence_client_one.enter_client("client:#{index}") end) do channel_client_two.attach do client_two.connection.transport.__incoming_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message| @@ -1347,10 +1466,10 @@ force_connection_failure client_two if sync_pages_received.count == 2 end end end - presence_client_two.get do |members| + presence_client_two.get(wait_for_sync: true) do |members| expect(members.count).to eql(members_count) expect(members.map(&:member_key).uniq.count).to eql(members_count) stop_reactor end end