spec/acceptance/realtime/message_spec.rb in ably-0.6.2 vs spec/acceptance/realtime/message_spec.rb in ably-0.7.0

- old
+ new

@@ -1,440 +1,454 @@ +# encoding: utf-8 require 'spec_helper' -require 'securerandom' -require 'json' require 'base64' +require 'json' +require 'securerandom' -describe 'Ably::Realtime::Channel Messages' do - include RSpec::EventMachine +describe 'Ably::Realtime::Channel Message', :event_machine do + vary_by_protocol do + let(:default_options) { options.merge(api_key: api_key, environment: environment, protocol: protocol) } + let(:client_options) { default_options } + let(:client) do + Ably::Realtime::Client.new(client_options) + end + let(:channel) { client.channel(channel_name) } - [:msgpack, :json].each do |protocol| - context "over #{protocol}" do - let(:default_options) { options.merge(api_key: api_key, environment: environment, protocol: protocol) } - let(:client) do - Ably::Realtime::Client.new(default_options) + let(:other_client) do + Ably::Realtime::Client.new(client_options) + end + let(:other_client_channel) { other_client.channel(channel_name) } + + let(:channel_name) { "subscribe_send_text-#{random_str}" } + let(:options) { { :protocol => :json } } + let(:payload) { 'Test message (subscribe_send_text)' } + + it 'sends a String data payload' do + channel.attach + channel.on(:attached) do + channel.publish('test_event', payload) do |message| + expect(message.data).to eql(payload) + stop_reactor + end end - let(:channel) { client.channel(channel_name) } + end - let(:other_client) do - Ably::Realtime::Client.new(default_options) + context 'with ASCII_8BIT message name' do + let(:message_name) { random_str.encode(Encoding::ASCII_8BIT) } + it 'is converted into UTF_8' do + channel.attach do + channel.publish message_name, payload + end + channel.subscribe do |message| + expect(message.name.encoding).to eql(Encoding::UTF_8) + expect(message.name.encode(Encoding::ASCII_8BIT)).to eql(message_name) + stop_reactor + end end - let(:other_client_channel) { other_client.channel(channel_name) } + end - let(:channel_name) { 'subscribe_send_text' } - let(:options) { { :protocol => :json } } - let(:payload) { 'Test message (subscribe_send_text)' } + context 'when the message publisher has a client_id' do + let(:client_id) { random_str } + let(:client_options) { default_options.merge(client_id: client_id) } - it 'sends a string message' do - run_reactor do - channel.attach - channel.on(:attached) do - channel.publish('test_event', payload) do |message| - expect(message.data).to eql(payload) + it 'contains a #client_id attribute' do + when_all(channel.attach, other_client_channel.attach) do + other_client_channel.subscribe('event') do |message| + expect(message.client_id).to eql(client_id) + stop_reactor + end + channel.publish('event', payload) + end + end + end + + describe '#connection_id attribute' do + context 'over realtime' do + it 'matches the sender connection#id' do + when_all(channel.attach, other_client_channel.attach) do + other_client_channel.subscribe('event') do |message| + expect(message.connection_id).to eql(client.connection.id) stop_reactor end + channel.publish('event', payload) end end end - it 'sends a single message with an echo on another connection' do - run_reactor do - other_client_channel.attach do - channel.publish 'test_event', payload - other_client_channel.subscribe('test_event') do |message| - expect(message.data).to eql(payload) + context 'when retrieved over REST' do + it 'matches the sender connection#id' do + channel.publish('event', payload) do + channel.history do |messages| + expect(messages.first.connection_id).to eql(client.connection.id) stop_reactor end end end end + end - context 'with echo_messages => false' do + describe 'local echo when published' do + it 'is enabled by default' do + channel.attach do + channel.publish 'test_event', payload + channel.subscribe('test_event') do |message| + expect(message.data).to eql(payload) + stop_reactor + end + end + end + + context 'with :echo_messages option set to false' do let(:no_echo_client) do Ably::Realtime::Client.new(default_options.merge(echo_messages: false)) end let(:no_echo_channel) { no_echo_client.channel(channel_name) } - it 'sends a single message without a reply yet the messages is echoed on another normal connection' do - run_reactor(10) do - channel.attach do |echo_channel| - no_echo_channel.attach do - no_echo_channel.publish 'test_event', payload + it 'will not echo messages to the client but will still broadcast messages to other connected clients', em_timeout: 10 do + channel.attach do |echo_channel| + no_echo_channel.attach do + no_echo_channel.publish 'test_event', payload - no_echo_channel.subscribe('test_event') do |message| - fail "Message should not have been echoed back" - end + no_echo_channel.subscribe('test_event') do |message| + fail "Message should not have been echoed back" + end - echo_channel.subscribe('test_event') do |message| - expect(message.data).to eql(payload) - EventMachine.add_timer(1) do - stop_reactor - end + echo_channel.subscribe('test_event') do |message| + expect(message.data).to eql(payload) + EventMachine.add_timer(1) do + stop_reactor end end end end end end + end - context 'with multiple messages' do - let(:send_count) { 15 } - let(:expected_echos) { send_count * 2 } - let(:channel_name) { SecureRandom.hex } - let(:echos) do - { client: 0, other: 0 } - end - let(:callbacks) do - { client: 0, other: 0 } - end + context 'publishing lots of messages across two connections' do + let(:send_count) { 30 } + let(:expected_echos) { send_count * 2 } + let(:channel_name) { random_str } + let(:echos) do + { client: 0, other: 0 } + end + let(:callbacks) do + { client: 0, other: 0 } + end - it 'sends and receives the messages on both opened connections and calls the callbacks (expects twice number of messages due to local echos)' do - run_reactor(8) do - check_message_and_callback_counts = Proc.new do - if echos[:client] == expected_echos && echos[:other] == expected_echos - # Wait for message backlog to clear - EventMachine.add_timer(0.5) do - expect(echos[:client]).to eql(expected_echos) - expect(echos[:other]).to eql(expected_echos) + it 'sends and receives the messages on both opened connections and calls the success callbacks for each message published', em_timeout: 10 do + check_message_and_callback_counts = Proc.new do + if echos[:client] == expected_echos && echos[:other] == expected_echos + # Wait for message backlog to clear + EventMachine.add_timer(0.5) do + expect(echos[:client]).to eql(expected_echos) + expect(echos[:other]).to eql(expected_echos) - expect(callbacks[:client]).to eql(send_count) - expect(callbacks[:other]).to eql(send_count) + expect(callbacks[:client]).to eql(send_count) + expect(callbacks[:other]).to eql(send_count) - EventMachine.stop - end - end + stop_reactor end + end + end - published = false - attach_callback = Proc.new do - next if published + channel.subscribe('test_event') do |message| + echos[:client] += 1 + check_message_and_callback_counts.call + end + other_client_channel.subscribe('test_event') do |message| + echos[:other] += 1 + check_message_and_callback_counts.call + end - if channel.attached? && other_client_channel.attached? - send_count.times do |index| - channel.publish('test_event', "#{index}: #{payload}") do - callbacks[:client] += 1 - end - other_client_channel.publish('test_event', "#{index}: #{payload}") do - callbacks[:other] += 1 - end - end - - published = true - end + when_all(channel.attach, other_client_channel.attach) do + send_count.times do |index| + channel.publish('test_event', "#{index}: #{payload}") do + callbacks[:client] += 1 end - - channel.subscribe('test_event') do |message| - echos[:client] += 1 - check_message_and_callback_counts.call + other_client_channel.publish('test_event', "#{index}: #{payload}") do + callbacks[:other] += 1 end - other_client_channel.subscribe('test_event') do |message| - echos[:other] += 1 - check_message_and_callback_counts.call - end - - channel.attach &attach_callback - other_client_channel.attach &attach_callback end end end + end - context 'without suitable publishing permissions' do - let(:restricted_client) do - Ably::Realtime::Client.new(options.merge(api_key: restricted_api_key, environment: environment, protocol: protocol)) - end - let(:restricted_channel) { restricted_client.channel("cansubscribe:example") } - let(:payload) { 'Test message without permission to publish' } + context 'without suitable publishing permissions' do + let(:restricted_client) do + Ably::Realtime::Client.new(options.merge(api_key: restricted_api_key, environment: environment, protocol: protocol)) + end + let(:restricted_channel) { restricted_client.channel("cansubscribe:example") } + let(:payload) { 'Test message without permission to publish' } - it 'calls the error callback' do - run_reactor do - restricted_channel.attach - restricted_channel.on(:attached) do - deferrable = restricted_channel.publish('test_event', payload) - deferrable.errback do |message, error| - expect(message.data).to eql(payload) - expect(error.status).to eql(401) - stop_reactor - end - deferrable.callback do |message| - fail 'Success callback should not have been called' - stop_reactor - end - end + it 'calls the error callback' do + restricted_channel.attach do + deferrable = restricted_channel.publish('test_event', payload) + deferrable.errback do |message, error| + expect(message.data).to eql(payload) + expect(error.status).to eql(401) + stop_reactor end + deferrable.callback do |message| + fail 'Success callback should not have been called' + end end end + end - context 'encoding and decoding encrypted messages' do - shared_examples 'an Ably encrypter and decrypter' do |item, data| - let(:algorithm) { data['algorithm'].upcase } - let(:mode) { data['mode'].upcase } - let(:key_length) { data['keylength'] } - let(:secret_key) { Base64.decode64(data['key']) } - let(:iv) { Base64.decode64(data['iv']) } + context 'encoding and decoding encrypted messages' do + shared_examples 'an Ably encrypter and decrypter' do |item, data| + let(:algorithm) { data['algorithm'].upcase } + let(:mode) { data['mode'].upcase } + let(:key_length) { data['keylength'] } + let(:secret_key) { Base64.decode64(data['key']) } + let(:iv) { Base64.decode64(data['iv']) } - let(:cipher_options) { { key: secret_key, iv: iv, algorithm: algorithm, mode: mode, key_length: key_length } } + let(:cipher_options) { { key: secret_key, iv: iv, algorithm: algorithm, mode: mode, key_length: key_length } } - context 'publish & subscribe' do - let(:encoded) { item['encoded'] } - let(:encoded_data) { encoded['data'] } - let(:encoded_encoding) { encoded['encoding'] } - let(:encoded_data_decoded) do - if encoded_encoding == 'json' - JSON.parse(encoded_data) - elsif encoded_encoding == 'base64' - Base64.decode64(encoded_data) - else - encoded_data - end + context 'with #publish and #subscribe' do + let(:encoded) { item['encoded'] } + let(:encoded_data) { encoded['data'] } + let(:encoded_encoding) { encoded['encoding'] } + let(:encoded_data_decoded) do + if encoded_encoding == 'json' + JSON.parse(encoded_data) + elsif encoded_encoding == 'base64' + Base64.decode64(encoded_data) + else + encoded_data end + end - let(:encrypted) { item['encrypted'] } - let(:encrypted_data) { encrypted['data'] } - let(:encrypted_encoding) { encrypted['encoding'] } - let(:encrypted_data_decoded) do - if encrypted_encoding.match(%r{/base64$}) - Base64.decode64(encrypted_data) - else - encrypted_data - end + let(:encrypted) { item['encrypted'] } + let(:encrypted_data) { encrypted['data'] } + let(:encrypted_encoding) { encrypted['encoding'] } + let(:encrypted_data_decoded) do + if encrypted_encoding.match(%r{/base64$}) + Base64.decode64(encrypted_data) + else + encrypted_data end + end - let(:encrypted_channel) { client.channel(channel_name, encrypted: true, cipher_params: cipher_options) } + let(:encrypted_channel) { client.channel(channel_name, encrypted: true, cipher_params: cipher_options) } - it 'encrypts message automatically when published' do - run_reactor do - encrypted_channel.__incoming_msgbus__.unsubscribe # remove all subscribe callbacks that could decrypt the message + it 'encrypts message automatically before they are pushed to the server' do + encrypted_channel.__incoming_msgbus__.unsubscribe # remove all subscribe callbacks that could decrypt the message - encrypted_channel.__incoming_msgbus__.subscribe(:message) do |message| - if protocol == :json - expect(message['encoding']).to eql(encrypted_encoding) - expect(message['data']).to eql(encrypted_data) - else - # Messages received over binary protocol will not have Base64 encoded data - expect(message['encoding']).to eql(encrypted_encoding.gsub(%r{/base64$}, '')) - expect(message['data']).to eql(encrypted_data_decoded) - end - stop_reactor - end - - encrypted_channel.publish 'example', encoded_data_decoded + encrypted_channel.__incoming_msgbus__.subscribe(:message) do |message| + if protocol == :json + expect(message['encoding']).to eql(encrypted_encoding) + expect(message['data']).to eql(encrypted_data) + else + # Messages received over binary protocol will not have Base64 encoded data + expect(message['encoding']).to eql(encrypted_encoding.gsub(%r{/base64$}, '')) + expect(message['data']).to eql(encrypted_data_decoded) end + stop_reactor end - it 'sends and receives messages that are encrypted & decrypted by the Ably library' do - run_reactor do - encrypted_channel.publish 'example', encoded_data_decoded - encrypted_channel.subscribe do |message| - expect(message.data).to eql(encoded_data_decoded) - expect(message.encoding).to be_nil - stop_reactor - end - end - end + encrypted_channel.publish 'example', encoded_data_decoded end - end - resources_root = File.expand_path('../../../resources', __FILE__) - - def self.add_tests_for_data(data) - data['items'].each_with_index do |item, index| - context "item #{index} with encrypted encoding #{item['encrypted']['encoding']}" do - it_behaves_like 'an Ably encrypter and decrypter', item, data + it 'sends and receives messages that are encrypted & decrypted by the Ably library' do + encrypted_channel.publish 'example', encoded_data_decoded + encrypted_channel.subscribe do |message| + expect(message.data).to eql(encoded_data_decoded) + expect(message.encoding).to be_nil + stop_reactor end end end + end - context 'with AES-128-CBC' do - data = JSON.parse(File.read(File.join(resources_root, 'crypto-data-128.json'))) - add_tests_for_data data - end + resources_root = File.expand_path('../../../resources', __FILE__) - context 'with AES-256-CBC' do - data = JSON.parse(File.read(File.join(resources_root, 'crypto-data-256.json'))) - add_tests_for_data data + def self.add_tests_for_data(data) + data['items'].each_with_index do |item, index| + context "item #{index} with encrypted encoding #{item['encrypted']['encoding']}" do + it_behaves_like 'an Ably encrypter and decrypter', item, data + end end + end - context 'multiple sends from one client to another' do - let(:cipher_options) { { key: SecureRandom.hex(32) } } - let(:encrypted_channel_client1) { client.channel(channel_name, encrypted: true, cipher_params: cipher_options) } - let(:encrypted_channel_client2) { other_client.channel(channel_name, encrypted: true, cipher_params: cipher_options) } + context 'with AES-128-CBC using crypto-data-128.json fixtures' do + data = JSON.parse(File.read(File.join(resources_root, 'crypto-data-128.json'))) + add_tests_for_data data + end - let(:data) { MessagePack.pack({ 'key' => SecureRandom.hex }) } - let(:message_count) { 50 } + context 'with AES-256-CBC using crypto-data-256.json fixtures' do + data = JSON.parse(File.read(File.join(resources_root, 'crypto-data-256.json'))) + add_tests_for_data data + end - it 'encrypt and decrypt messages' do - messages_received = { - decrypted: 0, - encrypted: 0 - } + context 'with multiple sends from one client to another' do + let(:cipher_options) { { key: random_str(32) } } + let(:encrypted_channel_client1) { client.channel(channel_name, encrypted: true, cipher_params: cipher_options) } + let(:encrypted_channel_client2) { other_client.channel(channel_name, encrypted: true, cipher_params: cipher_options) } - run_reactor do - encrypted_channel_client2.attach do - encrypted_channel_client2.subscribe do |message| - expect(message.data).to eql("#{message.name}-#{data}") - expect(message.encoding).to be_nil - messages_received[:decrypted] += 1 - stop_reactor if messages_received[:decrypted] == message_count - end + let(:data) { MessagePack.pack({ 'key' => random_str }) } + let(:message_count) { 50 } - encrypted_channel_client1.__incoming_msgbus__.subscribe(:message) do |message| - expect(message['encoding']).to match(/cipher\+/) - messages_received[:encrypted] += 1 - end - end + it 'encrypts and decrypts all messages' do + messages_received = { + decrypted: 0, + encrypted: 0 + } - message_count.times do |index| - encrypted_channel_client2.publish index.to_s, "#{index}-#{data}" - end + encrypted_channel_client2.attach do + encrypted_channel_client2.subscribe do |message| + expect(message.data).to eql("#{message.name}-#{data}") + expect(message.encoding).to be_nil + messages_received[:decrypted] += 1 + stop_reactor if messages_received[:decrypted] == message_count end + + encrypted_channel_client1.__incoming_msgbus__.subscribe(:message) do |message| + expect(message['encoding']).to match(/cipher\+/) + messages_received[:encrypted] += 1 + end end - end - context "sending using protocol #{protocol} and subscribing with a different protocol" do - let(:other_protocol) { protocol == :msgpack ? :json : :msgpack } - let(:other_client) do - Ably::Realtime::Client.new(default_options.merge(protocol: other_protocol)) + message_count.times do |index| + encrypted_channel_client2.publish index.to_s, "#{index}-#{data}" end + end + end - let(:cipher_options) { { key: SecureRandom.hex(32), algorithm: 'aes', mode: 'cbc', key_length: 256 } } - let(:encrypted_channel_client1) { client.channel(channel_name, encrypted: true, cipher_params: cipher_options) } - let(:encrypted_channel_client2) { other_client.channel(channel_name, encrypted: true, cipher_params: cipher_options) } + context 'subscribing with a different transport protocol' do + let(:other_protocol) { protocol == :msgpack ? :json : :msgpack } + let(:other_client) do + Ably::Realtime::Client.new(default_options.merge(protocol: other_protocol)) + end - before do - expect(other_client.protocol_binary?).to_not eql(client.protocol_binary?) - end + let(:cipher_options) { { key: random_str(32), algorithm: 'aes', mode: 'cbc', key_length: 256 } } + let(:encrypted_channel_client1) { client.channel(channel_name, encrypted: true, cipher_params: cipher_options) } + let(:encrypted_channel_client2) { other_client.channel(channel_name, encrypted: true, cipher_params: cipher_options) } - [MessagePack.pack({ 'key' => SecureRandom.hex }), '€ unicode', { 'key' => SecureRandom.hex }].each do |payload| - payload_description = "#{payload.class}#{" #{payload.encoding}" if payload.kind_of?(String)}" + before do + expect(other_client.protocol_binary?).to_not eql(client.protocol_binary?) + end - specify "delivers a #{payload_description} payload to the receiver" do - run_reactor do - encrypted_channel_client1.publish 'example', payload - encrypted_channel_client2.subscribe do |message| - expect(message.data).to eql(payload) - expect(message.encoding).to be_nil - stop_reactor - end - end + [MessagePack.pack({ 'key' => SecureRandom.hex }), 'ã unicode', { 'key' => SecureRandom.hex }].each do |payload| + payload_description = "#{payload.class}#{" #{payload.encoding}" if payload.kind_of?(String)}" + + it "delivers a #{payload_description} payload to the receiver" do + encrypted_channel_client1.publish 'example', payload + encrypted_channel_client2.subscribe do |message| + expect(message.data).to eql(payload) + expect(message.encoding).to be_nil + stop_reactor end end end + end - context 'publishing on an unencrypted channel and subscribing on an encrypted channel with another client' do - let(:cipher_options) { { key: SecureRandom.hex(32), algorithm: 'aes', mode: 'cbc', key_length: 256 } } - let(:unencrypted_channel_client1) { client.channel(channel_name) } - let(:encrypted_channel_client2) { other_client.channel(channel_name, encrypted: true, cipher_params: cipher_options) } + context 'publishing on an unencrypted channel and subscribing on an encrypted channel with another client' do + let(:client_options) { default_options.merge(log_level: :fatal) } + let(:cipher_options) { { key: random_str(32), algorithm: 'aes', mode: 'cbc', key_length: 256 } } + let(:unencrypted_channel_client1) { client.channel(channel_name) } + let(:encrypted_channel_client2) { other_client.channel(channel_name, encrypted: true, cipher_params: cipher_options) } - let(:payload) { MessagePack.pack({ 'key' => SecureRandom.hex }) } + let(:payload) { MessagePack.pack({ 'key' => random_str }) } - it 'does not attempt to decrypt the message' do - run_reactor do - unencrypted_channel_client1.publish 'example', payload - encrypted_channel_client2.subscribe do |message| - expect(message.data).to eql(payload) - expect(message.encoding).to be_nil - stop_reactor - end - end + it 'does not attempt to decrypt the message' do + unencrypted_channel_client1.publish 'example', payload + encrypted_channel_client2.subscribe do |message| + expect(message.data).to eql(payload) + expect(message.encoding).to be_nil + stop_reactor end end + end - context 'publishing on an encrypted channel and subscribing on an unencrypted channel with another client' do - let(:cipher_options) { { key: SecureRandom.hex(32), algorithm: 'aes', mode: 'cbc', key_length: 256 } } - let(:encrypted_channel_client1) { client.channel(channel_name, encrypted: true, cipher_params: cipher_options) } - let(:unencrypted_channel_client2) { other_client.channel(channel_name) } + context 'publishing on an encrypted channel and subscribing on an unencrypted channel with another client' do + let(:client_options) { default_options.merge(log_level: :fatal) } + let(:cipher_options) { { key: random_str(32), algorithm: 'aes', mode: 'cbc', key_length: 256 } } + let(:encrypted_channel_client1) { client.channel(channel_name, encrypted: true, cipher_params: cipher_options) } + let(:unencrypted_channel_client2) { other_client.channel(channel_name) } - let(:payload) { MessagePack.pack({ 'key' => SecureRandom.hex }) } + let(:payload) { MessagePack.pack({ 'key' => random_str }) } - it 'delivers the message but still encrypted' do - run_reactor do - encrypted_channel_client1.publish 'example', payload - unencrypted_channel_client2.subscribe do |message| - expect(message.data).to_not eql(payload) - expect(message.encoding).to match(/^cipher\+aes-256-cbc/) - stop_reactor - end - end + it 'delivers the message but still encrypted with a value in the #encoding attribute' do + encrypted_channel_client1.publish 'example', payload + unencrypted_channel_client2.subscribe do |message| + expect(message.data).to_not eql(payload) + expect(message.encoding).to match(/^cipher\+aes-256-cbc/) + stop_reactor end + end - it 'triggers a Cipher error on the channel' do - run_reactor do - unencrypted_channel_client2.attach do - encrypted_channel_client1.publish 'example', payload - unencrypted_channel_client2.on(:error) do |error| - expect(error).to be_a(Ably::Exceptions::CipherError) - expect(error.code).to eql(92001) - expect(error.message).to match(/Message cannot be decrypted/) - stop_reactor - end - end + it 'triggers a Cipher error on the channel' do + unencrypted_channel_client2.attach do + encrypted_channel_client1.publish 'example', payload + unencrypted_channel_client2.on(:error) do |error| + expect(error).to be_a(Ably::Exceptions::CipherError) + expect(error.code).to eql(92001) + expect(error.message).to match(/Message cannot be decrypted/) + stop_reactor end end end + end - context 'publishing on an encrypted channel and subscribing with a different algorithm on another client' do - let(:cipher_options_client1) { { key: SecureRandom.hex(32), algorithm: 'aes', mode: 'cbc', key_length: 256 } } - let(:encrypted_channel_client1) { client.channel(channel_name, encrypted: true, cipher_params: cipher_options_client1) } - let(:cipher_options_client2) { { key: SecureRandom.hex(32), algorithm: 'aes', mode: 'cbc', key_length: 128 } } - let(:encrypted_channel_client2) { other_client.channel(channel_name, encrypted: true, cipher_params: cipher_options_client2) } + context 'publishing on an encrypted channel and subscribing with a different algorithm on another client' do + let(:client_options) { default_options.merge(log_level: :fatal) } + let(:cipher_options_client1) { { key: random_str(32), algorithm: 'aes', mode: 'cbc', key_length: 256 } } + let(:encrypted_channel_client1) { client.channel(channel_name, encrypted: true, cipher_params: cipher_options_client1) } + let(:cipher_options_client2) { { key: random_str(32), algorithm: 'aes', mode: 'cbc', key_length: 128 } } + let(:encrypted_channel_client2) { other_client.channel(channel_name, encrypted: true, cipher_params: cipher_options_client2) } - let(:payload) { MessagePack.pack({ 'key' => SecureRandom.hex }) } + let(:payload) { MessagePack.pack({ 'key' => random_str }) } - it 'delivers the message but still encrypted' do - run_reactor do - encrypted_channel_client1.publish 'example', payload - encrypted_channel_client2.subscribe do |message| - expect(message.data).to_not eql(payload) - expect(message.encoding).to match(/^cipher\+aes-256-cbc/) - stop_reactor - end - end + it 'delivers the message but still encrypted with the cipher detials in the #encoding attribute' do + encrypted_channel_client1.publish 'example', payload + encrypted_channel_client2.subscribe do |message| + expect(message.data).to_not eql(payload) + expect(message.encoding).to match(/^cipher\+aes-256-cbc/) + stop_reactor end + end - it 'triggers a Cipher error on the channel' do - run_reactor do - encrypted_channel_client2.attach do - encrypted_channel_client1.publish 'example', payload - encrypted_channel_client2.on(:error) do |error| - expect(error).to be_a(Ably::Exceptions::CipherError) - expect(error.code).to eql(92002) - expect(error.message).to match(/Cipher algorithm [\w\d-]+ does not match/) - stop_reactor - end - end + it 'triggers a Cipher error on the channel' do + encrypted_channel_client2.attach do + encrypted_channel_client1.publish 'example', payload + encrypted_channel_client2.on(:error) do |error| + expect(error).to be_a(Ably::Exceptions::CipherError) + expect(error.code).to eql(92002) + expect(error.message).to match(/Cipher algorithm [\w-]+ does not match/) + stop_reactor end end end + end - context 'publishing on an encrypted channel and subscribing with a different key on another client' do - let(:cipher_options_client1) { { key: SecureRandom.hex(32), algorithm: 'aes', mode: 'cbc', key_length: 256 } } - let(:encrypted_channel_client1) { client.channel(channel_name, encrypted: true, cipher_params: cipher_options_client1) } - let(:cipher_options_client2) { { key: SecureRandom.hex(32), algorithm: 'aes', mode: 'cbc', key_length: 256 } } - let(:encrypted_channel_client2) { other_client.channel(channel_name, encrypted: true, cipher_params: cipher_options_client2) } + context 'publishing on an encrypted channel and subscribing with a different key on another client' do + let(:client_options) { default_options.merge(log_level: :fatal) } + let(:cipher_options_client1) { { key: random_str(32), algorithm: 'aes', mode: 'cbc', key_length: 256 } } + let(:encrypted_channel_client1) { client.channel(channel_name, encrypted: true, cipher_params: cipher_options_client1) } + let(:cipher_options_client2) { { key: random_str(32), algorithm: 'aes', mode: 'cbc', key_length: 256 } } + let(:encrypted_channel_client2) { other_client.channel(channel_name, encrypted: true, cipher_params: cipher_options_client2) } - let(:payload) { MessagePack.pack({ 'key' => SecureRandom.hex }) } + let(:payload) { MessagePack.pack({ 'key' => random_str }) } - it 'delivers the message but still encrypted' do - run_reactor do - encrypted_channel_client1.publish 'example', payload - encrypted_channel_client2.subscribe do |message| - expect(message.data).to_not eql(payload) - expect(message.encoding).to match(/^cipher\+aes-256-cbc/) - stop_reactor - end - end + it 'delivers the message but still encrypted with the cipher details in the #encoding attribute' do + encrypted_channel_client1.publish 'example', payload + encrypted_channel_client2.subscribe do |message| + expect(message.data).to_not eql(payload) + expect(message.encoding).to match(/^cipher\+aes-256-cbc/) + stop_reactor end + end - it 'triggers a Cipher error on the channel' do - run_reactor do - encrypted_channel_client2.attach do - encrypted_channel_client1.publish 'example', payload - encrypted_channel_client2.on(:error) do |error| - expect(error).to be_a(Ably::Exceptions::CipherError) - expect(error.code).to eql(92003) - expect(error.message).to match(/CipherError decrypting data/) - stop_reactor - end - end + it 'triggers a Cipher error on the channel' do + encrypted_channel_client2.attach do + encrypted_channel_client1.publish 'example', payload + encrypted_channel_client2.on(:error) do |error| + expect(error).to be_a(Ably::Exceptions::CipherError) + expect(error.code).to eql(92003) + expect(error.message).to match(/CipherError decrypting data/) + stop_reactor end end end end end