spec/acceptance/realtime/presence_spec.rb in ably-0.1.6 vs spec/acceptance/realtime/presence_spec.rb in ably-0.2.0

- old
+ new

@@ -20,11 +20,11 @@ let(:channel_rest_client_one) { client_one.rest_client.channel(channel_name) } let(:presence_client_one) { channel_client_one.presence } let(:channel_client_two) { client_two.channel(channel_name) } let(:presence_client_two) { channel_client_two.presence } - let(:client_data_payload) { SecureRandom.hex(8) } + let(:data_payload) { SecureRandom.hex(8) } specify 'an attached channel that is not presence maintains presence state' do run_reactor do channel_anonymous_client.attach do presence_anonymous_client.subscribe(:enter) do |presence_message| @@ -124,11 +124,11 @@ end end specify 'verify two clients appear in members from #get' do run_reactor do - presence_client_one.enter(client_data: client_data_payload) + presence_client_one.enter(data: data_payload) presence_client_two.enter entered_callback = Proc.new do next unless presence_client_one.state == :entered && presence_client_two.state == :entered @@ -138,11 +138,11 @@ members = presence_client_one.get member_client_one = members.find { |presence| presence.client_id == client_one.client_id } member_client_two = members.find { |presence| presence.client_id == client_two.client_id } expect(member_client_one).to be_a(Ably::Models::PresenceMessage) - expect(member_client_one.client_data).to eql(client_data_payload) + expect(member_client_one.data).to eql(data_payload) expect(member_client_two).to be_a(Ably::Models::PresenceMessage) stop_reactor end end @@ -156,11 +156,11 @@ run_reactor do client_two_subscribe_messages = [] subscribe_client_one_leaving_callback = Proc.new do |presence_message| expect(presence_message.client_id).to eql(client_one.client_id) - expect(presence_message.client_data).to eql(client_data_payload) + expect(presence_message.data).to eql(data_payload) expect(presence_message.action).to eq(:leave) stop_reactor end @@ -169,48 +169,190 @@ expect(presence_message.action).to eq(:enter) presence_client_two.unsubscribe &subscribe_self_callback presence_client_two.subscribe &subscribe_client_one_leaving_callback - presence_client_one.leave client_data: client_data_payload + presence_client_one.leave data: data_payload end end presence_client_one.enter do presence_client_two.enter presence_client_two.subscribe &subscribe_self_callback end end end - specify 'verify REST #get returns current members' do + specify 'REST #get returns current members' do run_reactor do - presence_client_one.enter(client_data: client_data_payload) do + presence_client_one.enter(data: data_payload) do members = channel_rest_client_one.presence.get this_member = members.first expect(this_member).to be_a(Ably::Models::PresenceMessage) expect(this_member.client_id).to eql(client_one.client_id) - expect(this_member.client_data).to eql(client_data_payload) + expect(this_member.data).to eql(data_payload) stop_reactor end end end - specify 'verify REST #get returns no members once left' do + specify 'REST #get returns no members once left' do run_reactor do - presence_client_one.enter(client_data: client_data_payload) do + presence_client_one.enter(data: data_payload) do presence_client_one.leave do members = channel_rest_client_one.presence.get expect(members.count).to eql(0) stop_reactor end end end end + context 'encoding and decoding of presence message data' do + let(:secret_key) { SecureRandom.hex(32) } + let(:cipher_options) { { key: secret_key, algorithm: 'aes', mode: 'cbc', key_length: 256 } } + let(:channel_name) { SecureRandom.hex(32) } + let(:encrypted_channel) { client_one.channel(channel_name, encrypted: true, cipher_params: cipher_options) } + let(:channel_rest_client_one) { client_one.rest_client.channel(channel_name, encrypted: true, cipher_params: cipher_options) } + + let(:crypto) { Ably::Util::Crypto.new(cipher_options) } + + let(:data) { { 'key' => SecureRandom.hex(64) } } + let(:data_as_json) { data.to_json } + let(:data_as_cipher) { crypto.encrypt(data.to_json) } + + it 'encrypts presence message data' do + run_reactor do + encrypted_channel.attach do + encrypted_channel.presence.enter data: data + end + + encrypted_channel.presence.__incoming_msgbus__.unsubscribe(:presence) # remove all subscribe callbacks that could decrypt the message + encrypted_channel.presence.__incoming_msgbus__.subscribe(:presence) do |presence| + if protocol == :json + expect(presence['encoding']).to eql('json/utf-8/cipher+aes-256-cbc/base64') + expect(crypto.decrypt(Base64.decode64(presence['data']))).to eql(data_as_json) + else + expect(presence['encoding']).to eql('json/utf-8/cipher+aes-256-cbc') + expect(crypto.decrypt(presence['data'])).to eql(data_as_json) + end + stop_reactor + end + end + end + + it '#subscribe emits decrypted enter events' do + run_reactor do + encrypted_channel.attach do + encrypted_channel.presence.enter data: data + end + + encrypted_channel.presence.subscribe(:enter) do |presence_message| + expect(presence_message.encoding).to be_nil + expect(presence_message.data).to eql(data) + stop_reactor + end + end + end + + it '#subscribe emits decrypted update events' do + run_reactor do + encrypted_channel.attach do + encrypted_channel.presence.enter(data: 'to be updated') do + encrypted_channel.presence.update data: data + end + end + + encrypted_channel.presence.subscribe(:update) do |presence_message| + expect(presence_message.encoding).to be_nil + expect(presence_message.data).to eql(data) + stop_reactor + end + end + end + + it '#subscribe emits decrypted leave events' do + run_reactor do + encrypted_channel.attach do + encrypted_channel.presence.enter(data: 'to be updated') do + encrypted_channel.presence.leave data: data + end + end + + encrypted_channel.presence.subscribe(:leave) do |presence_message| + expect(presence_message.encoding).to be_nil + expect(presence_message.data).to eql(data) + stop_reactor + end + end + end + + it '#get returns a list of members with decrypted data' do + run_reactor do + encrypted_channel.attach do + encrypted_channel.presence.enter(data: data) do + member = encrypted_channel.presence.get.first + expect(member.encoding).to be_nil + expect(member.data).to eql(data) + stop_reactor + end + end + end + end + + it 'REST #get returns a list of members with decrypted data' do + run_reactor do + encrypted_channel.attach do + encrypted_channel.presence.enter(data: data) do + member = channel_rest_client_one.presence.get.first + expect(member.encoding).to be_nil + expect(member.data).to eql(data) + stop_reactor + end + end + end + end + + context 'when cipher settings do not match publisher' do + let(:incompatible_cipher_options) { { key: secret_key, algorithm: 'aes', mode: 'cbc', key_length: 128 } } + let(:incompatible_encrypted_channel) { client_two.channel(channel_name, encrypted: true, cipher_params: incompatible_cipher_options) } + + it 'delivers an unencoded presence message left with encoding value' do + run_reactor do + incompatible_encrypted_channel.attach do + encrypted_channel.attach do + encrypted_channel.presence.enter(data: data) do + member = incompatible_encrypted_channel.presence.get.first + expect(member.encoding).to match(/cipher\+aes-256-cbc/) + expect(member.data).to_not eql(data) + stop_reactor + end + end + end + end + end + + it 'emits an error when cipher does not match and presence data cannot be decoded' do + run_reactor do + incompatible_encrypted_channel.attach do + incompatible_encrypted_channel.on(:error) do |error| + expect(error).to be_a(Ably::Exceptions::CipherError) + expect(error.message).to match(/Cipher algorithm AES-128-CBC does not match/) + stop_reactor + end + + encrypted_channel.attach do + encrypted_channel.presence.enter data: data + end + end + end + end + end + end + specify 'expect :left event once underlying connection is closed' do run_reactor do presence_client_one.on(:left) do expect(presence_client_one.state).to eq(:left) stop_reactor @@ -219,38 +361,38 @@ client_one.close end end end - specify 'expect :left event with no client data to retain original client_data in Leave event' do + specify 'expect :left event with no client data to retain original data in Leave event' do run_reactor do presence_client_one.subscribe(:leave) do |message| expect(presence_client_one.get.count).to eq(0) - expect(message.client_data).to eq(client_data_payload) + expect(message.data).to eq(data_payload) stop_reactor end - presence_client_one.enter(client_data: client_data_payload) do + presence_client_one.enter(data: data_payload) do presence_client_one.leave end end end specify '#update automatically connects' do run_reactor do - presence_client_one.update(client_data: client_data_payload) do + presence_client_one.update(data: data_payload) do expect(presence_client_one.state).to eq(:entered) stop_reactor end end end - specify '#update changes the client_data' do + specify '#update changes the data' do run_reactor do - presence_client_one.enter(client_data: 'prior') do - presence_client_one.update(client_data: client_data_payload) + presence_client_one.enter(data: 'prior') do + presence_client_one.update(data: data_payload) end presence_client_one.subscribe(:update) do |message| - expect(message.client_data).to eql(client_data_payload) + expect(message.data).to eql(data_payload) stop_reactor end end end