ably-rest 0.8.9 → 0.8.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (25) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +54 -21
  3. data/ably-rest.gemspec +6 -2
  4. data/lib/ably-rest.rb +2 -0
  5. data/lib/submodules/ably-ruby/CHANGELOG.md +42 -4
  6. data/lib/submodules/ably-ruby/README.md +2 -3
  7. data/lib/submodules/ably-ruby/ably.gemspec +6 -2
  8. data/lib/submodules/ably-ruby/lib/ably/auth.rb +80 -21
  9. data/lib/submodules/ably-ruby/lib/ably/modules/message_emitter.rb +5 -2
  10. data/lib/submodules/ably-ruby/lib/ably/realtime/auth.rb +22 -4
  11. data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +1 -1
  12. data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +8 -6
  13. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +1 -1
  14. data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +4 -2
  15. data/lib/submodules/ably-ruby/lib/ably/version.rb +3 -1
  16. data/lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb +189 -0
  17. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +19 -0
  18. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +20 -0
  19. data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +132 -10
  20. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +19 -0
  21. data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +122 -4
  22. data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +17 -0
  23. data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +3 -3
  24. data/lib/submodules/ably-ruby/spec/unit/models/token_details_spec.rb +28 -0
  25. metadata +2 -2
@@ -67,9 +67,9 @@ module Ably
67
67
  # token_details #=> Ably::Models::TokenDetails
68
68
  # end
69
69
  #
70
- def authorise(token_params = {}, auth_options = {}, &success_callback)
70
+ def authorise(token_params = nil, auth_options = nil, &success_callback)
71
71
  async_wrap(success_callback) do
72
- auth_sync.authorise(token_params, auth_options)
72
+ auth_sync.authorise(token_params, auth_options, &method(:upgrade_authentication_block).to_proc)
73
73
  end.tap do |deferrable|
74
74
  deferrable.errback do |error|
75
75
  client.connection.transition_state_machine :failed, reason: error if error.kind_of?(Ably::Exceptions::IncompatibleClientId)
@@ -82,8 +82,8 @@ module Ably
82
82
  # @option (see Ably::Auth#authorise)
83
83
  # @return [Ably::Models::TokenDetails]
84
84
  #
85
- def authorise_sync(token_params = {}, auth_options = {})
86
- auth_sync.authorise(token_params, auth_options)
85
+ def authorise_sync(token_params = nil, auth_options = nil)
86
+ auth_sync.authorise(token_params, auth_options, &method(:upgrade_authentication_block).to_proc)
87
87
  end
88
88
 
89
89
  # def_delegator :auth_sync, :request_token, :request_token_sync
@@ -196,6 +196,24 @@ module Ably
196
196
  def client
197
197
  @client
198
198
  end
199
+
200
+ # If authorise is called with true, this block is executed so that it
201
+ # can perform the authentication upgrade
202
+ def upgrade_authentication_block(new_token)
203
+ # This block is called if the authorisation was forced
204
+ if client.connection.connected? || client.connection.connecting?
205
+ logger.debug "Realtime::Auth - authorise called with { force: true } so forcibly disconnecting transport to initiate auth upgrade"
206
+ block = Proc.new do
207
+ if client.connection.transport
208
+ logger.debug "Realtime::Auth - current transport disconnected"
209
+ client.connection.transport.disconnect
210
+ else
211
+ EventMachine.add_timer(0.1, &block)
212
+ end
213
+ end
214
+ block.call
215
+ end
216
+ end
199
217
  end
200
218
  end
201
219
  end
@@ -144,7 +144,7 @@ module Ably::Realtime
144
144
  end
145
145
 
146
146
  def process_connection_error(protocol_message)
147
- connection.manager.error_received_from_server protocol_message.error
147
+ connection.manager.error_received_from_server(protocol_message.error || Ably::Models::ErrorInfo.new(message: 'Error reason unknown'))
148
148
  end
149
149
 
150
150
  def process_connected_message(protocol_message)
@@ -194,15 +194,15 @@ module Ably
194
194
 
195
195
  # Sends a ping to Ably and yields the provided block when a heartbeat ping request is echoed from the server.
196
196
  # This can be useful for measuring true roundtrip client to Ably server latency for a simple message, or checking that an underlying transport is responding currently.
197
- # The elapsed milliseconds is passed as an argument to the block and represents the time taken to echo a ping heartbeat once the connection is in the `:connected` state.
197
+ # The elapsed time in seconds is passed as an argument to the block and represents the time taken to echo a ping heartbeat once the connection is in the `:connected` state.
198
198
  #
199
- # @yield [Integer] if a block is passed to this method, then this block will be called once the ping heartbeat is received with the time elapsed in milliseconds.
199
+ # @yield [Integer] if a block is passed to this method, then this block will be called once the ping heartbeat is received with the time elapsed in seconds.
200
200
  # If the ping is not received within an acceptable timeframe, the block will be called with +nil+ as he first argument
201
201
  #
202
202
  # @example
203
203
  # client = Ably::Rest::Client.new(key: 'key.id:secret')
204
- # client.connection.ping do |ms_elapsed|
205
- # puts "Ping took #{ms_elapsed}ms"
204
+ # client.connection.ping do |elapsed_s|
205
+ # puts "Ping took #{elapsed_s}s"
206
206
  # end
207
207
  #
208
208
  # @return [void]
@@ -219,7 +219,7 @@ module Ably
219
219
  if protocol_message.action == Ably::Models::ProtocolMessage::ACTION.Heartbeat
220
220
  finished = true
221
221
  __incoming_protocol_msgbus__.unsubscribe(:protocol_message, &wait_for_ping)
222
- time_passed = (Time.now.to_f * 1000 - started.to_f * 1000).to_i
222
+ time_passed = Time.now.to_f - started.to_f
223
223
  safe_yield block, time_passed if block_given?
224
224
  end
225
225
  end
@@ -387,7 +387,9 @@ module Ably
387
387
  auth_deferrable.callback do |auth_params|
388
388
  url_params = auth_params.merge(
389
389
  format: client.protocol,
390
- echo: client.echo_messages
390
+ echo: client.echo_messages,
391
+ v: Ably::PROTOCOL_VERSION,
392
+ lib: Ably::LIB_VERSION_ID,
391
393
  )
392
394
 
393
395
  url_params['clientId'] = client.auth.client_id if client.auth.has_client_id?
@@ -402,7 +402,7 @@ module Ably::Realtime
402
402
  @renewing_token = true
403
403
  logger.info "ConnectionManager: Token has expired and is renewable, renewing token now"
404
404
 
405
- client.auth.authorise({}, force: true).tap do |authorise_deferrable|
405
+ client.auth.authorise(nil, force: true).tap do |authorise_deferrable|
406
406
  authorise_deferrable.callback do |token_details|
407
407
  logger.info 'ConnectionManager: Token renewed succesfully following expiration'
408
408
 
@@ -120,7 +120,7 @@ module Ably
120
120
 
121
121
  options = options.clone
122
122
  if options.kind_of?(String)
123
- options = if options.match(/^[\w]{2,}\.[\w]{2,}:[\w]{2,}$/)
123
+ options = if options.match(/^[\w-]{2,}\.[\w-]{2,}:[\w-]{2,}$/)
124
124
  { key: options }
125
125
  else
126
126
  { token: options }
@@ -352,6 +352,8 @@ module Ably
352
352
  unless options[:send_auth_header] == false
353
353
  request.headers[:authorization] = auth.auth_header
354
354
  end
355
+ request.headers['X-Ably-Version'] = Ably::PROTOCOL_VERSION
356
+ request.headers['X-Ably-Lib'] = Ably::LIB_VERSION_ID
355
357
  end
356
358
 
357
359
  rescue Faraday::TimeoutError, Faraday::ClientError, Ably::Exceptions::ServerError => error
@@ -376,7 +378,7 @@ module Ably
376
378
  yield
377
379
  rescue Ably::Exceptions::TokenExpired => e
378
380
  if auth.token_renewable?
379
- auth.authorise({}, force: true)
381
+ auth.authorise(nil, force: true)
380
382
  yield
381
383
  else
382
384
  raise e
@@ -1,3 +1,5 @@
1
1
  module Ably
2
- VERSION = '0.8.9'
2
+ VERSION = '0.8.13'
3
+ PROTOCOL_VERSION = '0.8'
4
+ LIB_VERSION_ID = "ruby-#{Ably::VERSION}"
3
5
  end
@@ -254,6 +254,195 @@ describe Ably::Realtime::Auth, :event_machine do
254
254
  end
255
255
  end
256
256
  end
257
+
258
+ context 'with force: true to trigger an authentication upgrade' do
259
+ let(:rest_client) { Ably::Rest::Client.new(default_options) }
260
+ let(:client_publisher) { auto_close Ably::Realtime::Client.new(default_options) }
261
+ let(:basic_capability) { JSON.dump("foo" => ["subscribe"]) }
262
+ let(:basic_token_cb) { Proc.new do
263
+ rest_client.auth.create_token_request({ capability: basic_capability })
264
+ end }
265
+ let(:upgraded_capability) { JSON.dump({ "foo" => ["subscribe", "publish"] }) }
266
+ let(:upgraded_token_cb) { Proc.new do
267
+ rest_client.auth.create_token_request({ capability: upgraded_capability })
268
+ end }
269
+ let(:identified_token_cb) { Proc.new do
270
+ rest_client.auth.create_token_request({ client_id: 'bob' })
271
+ end }
272
+ let(:downgraded_capability) { JSON.dump({ "bar" => ["subscribe"] }) }
273
+ let(:downgraded_token_cb) { Proc.new do
274
+ rest_client.auth.create_token_request({ capability: downgraded_capability })
275
+ end }
276
+
277
+ let(:client_options) { default_options.merge(auth_callback: basic_token_cb) }
278
+
279
+ it 'forces the connection to disconnect and reconnect with a new token when in the CONNECTED state' do
280
+ client.connection.once(:connected) do
281
+ existing_token = client.auth.current_token_details
282
+ client.auth.authorise(nil, force: true)
283
+ client.connection.once(:disconnected) do
284
+ client.connection.once(:connected) do
285
+ expect(existing_token).to_not eql(client.auth.current_token_details)
286
+ stop_reactor
287
+ end
288
+ end
289
+ end
290
+ end
291
+
292
+ it 'forces the connection to disconnect and reconnect with a new token when in the CONNECTING state' do
293
+ client.connection.once(:connecting) do
294
+ existing_token = client.auth.current_token_details
295
+ client.auth.authorise(nil, force: true)
296
+ client.connection.once(:disconnected) do
297
+ client.connection.once(:connected) do
298
+ expect(existing_token).to_not eql(client.auth.current_token_details)
299
+ stop_reactor
300
+ end
301
+ end
302
+ end
303
+ end
304
+
305
+ context 'when client is identified' do
306
+ let(:client_options) { default_options.merge(auth_callback: basic_token_cb, log_level: :none) }
307
+
308
+ let(:basic_token_cb) { Proc.new do
309
+ rest_client.auth.create_token_request({ client_id: 'mike', capability: basic_capability })
310
+ end }
311
+
312
+ it 'transisitions the connection state to FAILED if the client_id changes' do
313
+ client.connection.once(:connected) do
314
+ client.auth.authorise(nil, auth_callback: identified_token_cb, force: true)
315
+ client.connection.once(:failed) do
316
+ expect(client.connection.error_reason.message).to match(/incompatible.*client ID/)
317
+ stop_reactor
318
+ end
319
+ end
320
+ end
321
+ end
322
+
323
+ context 'when upgrading capabilities' do
324
+ let(:client_options) { default_options.merge(auth_callback: basic_token_cb, log_level: :error) }
325
+
326
+ it 'is allowed' do
327
+ client.connection.once(:connected) do
328
+ channel = client.channels.get('foo')
329
+ channel.publish('not-allowed').errback do |error|
330
+ expect(error.code).to eql(40160)
331
+ expect(error.message).to match(/permission denied/)
332
+ client.auth.authorise(nil, auth_callback: upgraded_token_cb, force: true)
333
+ client.connection.once(:connected) do
334
+ expect(client.connection.error_reason).to be_nil
335
+ channel.subscribe('allowed') do |message|
336
+ stop_reactor
337
+ end
338
+ channel.publish 'allowed'
339
+ end
340
+ end
341
+ end
342
+ end
343
+ end
344
+
345
+ context 'when downgrading capabilities' do
346
+ let(:client_options) { default_options.merge(auth_callback: basic_token_cb, log_level: :none) }
347
+
348
+ it 'is allowed and channels are detached' do
349
+ client.connection.once(:connected) do
350
+ channel = client.channels.get('foo')
351
+ channel.attach do
352
+ client.auth.authorise(nil, auth_callback: downgraded_token_cb, force: true)
353
+ channel.once(:failed) do
354
+ expect(channel.error_reason.code).to eql(40160)
355
+ expect(channel.error_reason.message).to match(/Channel denied access/)
356
+ stop_reactor
357
+ end
358
+ end
359
+ end
360
+ end
361
+ end
362
+
363
+ it 'ensures message delivery continuity whilst upgrading' do
364
+ received_messages = []
365
+ subscriber_channel = client.channels.get('foo')
366
+ publisher_channel = client_publisher.channels.get('foo')
367
+ subscriber_channel.attach do
368
+ subscriber_channel.subscribe do |message|
369
+ received_messages << message
370
+ end
371
+ publisher_channel.attach do
372
+ publisher_channel.publish('foo') do
373
+ EventMachine.add_timer(2) do
374
+ expect(received_messages.length).to eql(1)
375
+ client.auth.authorise(nil, force: true)
376
+ client.connection.once(:disconnected) do
377
+ publisher_channel.publish('bar') do
378
+ expect(received_messages.length).to eql(1)
379
+ end
380
+ end
381
+ client.connection.once(:connected) do
382
+ EventMachine.add_timer(2) do
383
+ expect(received_messages.length).to eql(2)
384
+ stop_reactor
385
+ end
386
+ end
387
+ end
388
+ end
389
+ end
390
+ end
391
+ end
392
+
393
+ it 'does not change the connection state if current connection state is closing' do
394
+ client.connection.once(:connected) do
395
+ client.connection.once(:closing) do
396
+ client.auth.authorise(nil, force: true)
397
+ client.connection.once(:connected) do
398
+ raise "Should not reconnect following auth force: true"
399
+ end
400
+ EventMachine.add_timer(4) do
401
+ expect(client.connection).to be_closed
402
+ stop_reactor
403
+ end
404
+ end
405
+ client.connection.close
406
+ end
407
+ end
408
+
409
+ it 'does not change the connection state if current connection state is closed' do
410
+ client.connection.once(:connected) do
411
+ client.connection.once(:closed) do
412
+ client.auth.authorise(nil, force: true)
413
+ client.connection.once(:connected) do
414
+ raise "Should not reconnect following auth force: true"
415
+ end
416
+ EventMachine.add_timer(4) do
417
+ expect(client.connection).to be_closed
418
+ stop_reactor
419
+ end
420
+ end
421
+ client.connection.close
422
+ end
423
+ end
424
+
425
+ context 'when state is failed' do
426
+ let(:client_options) { default_options.merge(auth_callback: basic_token_cb, log_level: :none) }
427
+
428
+ it 'does not change the connection state' do
429
+ client.connection.once(:connected) do
430
+ client.connection.once(:failed) do
431
+ client.auth.authorise(nil, force: true)
432
+ client.connection.once(:connected) do
433
+ raise "Should not reconnect following auth force: true"
434
+ end
435
+ EventMachine.add_timer(4) do
436
+ expect(client.connection).to be_failed
437
+ stop_reactor
438
+ end
439
+ end
440
+ protocol_message = Ably::Models::ProtocolMessage.new(action: Ably::Models::ProtocolMessage::ACTION.Error.to_i)
441
+ client.connection.__incoming_protocol_msgbus__.publish :protocol_message, protocol_message
442
+ end
443
+ end
444
+ end
445
+ end
257
446
  end
258
447
 
259
448
  context '#authorise_async' do
@@ -908,6 +908,25 @@ describe Ably::Realtime::Channel, :event_machine do
908
908
  end
909
909
  end
910
910
 
911
+ context 'with a callback that raises an exception' do
912
+ let(:exception) { StandardError.new("Intentional error") }
913
+
914
+ it 'logs the error and continues' do
915
+ emitted_exception = false
916
+ expect(client.logger).to receive(:error).with(/#{exception.message}/)
917
+ channel.subscribe('click') do |message|
918
+ emitted_exception = true
919
+ raise exception
920
+ end
921
+ channel.publish('click', 'data') do
922
+ EventMachine.add_timer(1) do
923
+ expect(emitted_exception).to eql(true)
924
+ stop_reactor
925
+ end
926
+ end
927
+ end
928
+ end
929
+
911
930
  context 'many times with different event names' do
912
931
  it 'filters events accordingly to each callback' do
913
932
  click_callback = proc { |message| messages << message }
@@ -1356,5 +1356,25 @@ describe Ably::Realtime::Connection, :event_machine do
1356
1356
  end
1357
1357
  end
1358
1358
  end
1359
+
1360
+ context 'version params' do
1361
+ it 'sends the protocol version param v' do
1362
+ expect(EventMachine).to receive(:connect) do |host, port, transport, object, url|
1363
+ uri = URI.parse(url)
1364
+ expect(CGI::parse(uri.query)['v'][0]).to eql(Ably::PROTOCOL_VERSION)
1365
+ stop_reactor
1366
+ end
1367
+ client
1368
+ end
1369
+
1370
+ it 'sends the lib version param lib' do
1371
+ expect(EventMachine).to receive(:connect) do |host, port, transport, object, url|
1372
+ uri = URI.parse(url)
1373
+ expect(CGI::parse(uri.query)['lib'][0]).to eql("ruby-#{Ably::VERSION}")
1374
+ stop_reactor
1375
+ end
1376
+ client
1377
+ end
1378
+ end
1359
1379
  end
1360
1380
  end
@@ -483,11 +483,13 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
483
483
  payload_description = "#{payload.class}#{" #{payload.encoding}" if payload.kind_of?(String)}"
484
484
 
485
485
  it "delivers a #{payload_description} payload to the receiver" do
486
- encrypted_channel_client1.publish 'example', payload
487
- encrypted_channel_client2.subscribe do |message|
488
- expect(message.data).to eql(payload)
489
- expect(message.encoding).to be_nil
490
- stop_reactor
486
+ encrypted_channel_client2.attach do
487
+ encrypted_channel_client1.publish 'example', payload
488
+ encrypted_channel_client2.subscribe do |message|
489
+ expect(message.data).to eql(payload)
490
+ expect(message.encoding).to be_nil
491
+ stop_reactor
492
+ end
491
493
  end
492
494
  end
493
495
  end
@@ -520,11 +522,13 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
520
522
  let(:payload) { MessagePack.pack({ 'key' => random_str }) }
521
523
 
522
524
  it 'delivers the message but still encrypted with a value in the #encoding attribute' do
523
- encrypted_channel_client1.publish 'example', payload
524
- unencrypted_channel_client2.subscribe do |message|
525
- expect(message.data).to_not eql(payload)
526
- expect(message.encoding).to match(/^cipher\+aes-256-cbc/)
527
- stop_reactor
525
+ unencrypted_channel_client2.attach do
526
+ encrypted_channel_client1.publish 'example', payload
527
+ unencrypted_channel_client2.subscribe do |message|
528
+ expect(message.data).to_not eql(payload)
529
+ expect(message.encoding).to match(/^cipher\+aes-256-cbc/)
530
+ stop_reactor
531
+ end
528
532
  end
529
533
  end
530
534
 
@@ -732,4 +736,122 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
732
736
  end
733
737
  end
734
738
  end
739
+
740
+ context 'message encoding interoperability' do
741
+ let(:client_options) { { key: api_key, environment: environment, protocol: :json } }
742
+ let(:channel_name) { "subscribe_send_text-#{random_str}" }
743
+
744
+ fixtures_path = File.expand_path('../../../../lib/submodules/ably-common/test-resources/messages-encoding.json', __FILE__)
745
+
746
+ context 'over a JSON transport' do
747
+ let(:realtime_client) do
748
+ auto_close Ably::Realtime::Client.new(client_options)
749
+ end
750
+ let(:rest_client) do
751
+ Ably::Rest::Client.new(client_options)
752
+ end
753
+ let(:realtime_channel) { realtime_client.channels.get(channel_name) }
754
+
755
+ JSON.parse(File.read(fixtures_path))['messages'].each do |encoding_spec|
756
+ context "when decoding #{encoding_spec['expectedType']}" do
757
+ it 'ensures that client libraries have compatible encoding and decoding using common fixtures' do
758
+ realtime_channel.attach do
759
+ realtime_channel.subscribe do |message|
760
+ if encoding_spec['expectedHexValue']
761
+ expect(message.data.unpack('H*').first).to eql(encoding_spec['expectedHexValue'])
762
+ else
763
+ expect(message.data).to eql(encoding_spec['expectedValue'])
764
+ end
765
+ stop_reactor
766
+ end
767
+
768
+ raw_message = { "data" => encoding_spec['data'], "encoding" => encoding_spec['encoding'] }
769
+ rest_client.post("/channels/#{channel_name}/messages", JSON.dump(raw_message))
770
+ end
771
+ end
772
+ end
773
+
774
+ context "when encoding #{encoding_spec['expectedType']}" do
775
+ it 'ensures that client libraries have compatible encoding and decoding using common fixtures' do
776
+ data = if encoding_spec['expectedHexValue']
777
+ encoding_spec['expectedHexValue'].scan(/../).map { |x| x.hex }.pack('c*')
778
+ else
779
+ encoding_spec['expectedValue']
780
+ end
781
+
782
+ realtime_channel.publish("event", data) do
783
+ response = rest_client.get("/channels/#{channel_name}/messages")
784
+ message = response.body[0]
785
+ expect(message['encoding']).to eql(encoding_spec['encoding'])
786
+ if message['encoding'] == 'json'
787
+ expect(JSON.parse(encoding_spec['data'])).to eql(JSON.parse(message['data']))
788
+ else
789
+ expect(encoding_spec['data']).to eql(message['data'])
790
+ end
791
+ stop_reactor
792
+ end
793
+ end
794
+ end
795
+ end
796
+ end
797
+
798
+ context 'over a MsgPack transport' do
799
+ JSON.parse(File.read(fixtures_path))['messages'].each do |encoding_spec|
800
+ context "when publishing a #{encoding_spec['expectedType']} using JSON protocol" do
801
+ let(:rest_publish_client) do
802
+ Ably::Rest::Client.new(client_options.merge(protocol: :json))
803
+ end
804
+ let(:realtime_subscribe_client) do
805
+ Ably::Realtime::Client.new(client_options.merge(protocol: :msgpack))
806
+ end
807
+ let(:realtime_subscribe_channel) { realtime_subscribe_client.channels.get(channel_name) }
808
+
809
+ it 'receives the message over MsgPack and the data matches' do
810
+ expect(realtime_subscribe_client).to be_protocol_binary
811
+
812
+ realtime_subscribe_channel.attach do
813
+ realtime_subscribe_channel.subscribe do |message|
814
+ if encoding_spec['expectedHexValue']
815
+ expect(message.data.unpack('H*').first).to eql(encoding_spec['expectedHexValue'])
816
+ else
817
+ expect(message.data).to eql(encoding_spec['expectedValue'])
818
+ end
819
+ stop_reactor
820
+ end
821
+
822
+ raw_message = { "data" => encoding_spec['data'], "encoding" => encoding_spec['encoding'] }
823
+ rest_publish_client.post("/channels/#{channel_name}/messages", JSON.dump(raw_message))
824
+ end
825
+ end
826
+ end
827
+
828
+ context "when retrieving a #{encoding_spec['expectedType']} using JSON protocol" do
829
+ let(:rest_publish_client) do
830
+ Ably::Rest::Client.new(client_options.merge(protocol: :msgpack))
831
+ end
832
+ let(:rest_retrieve_client) do
833
+ Ably::Rest::Client.new(client_options.merge(protocol: :json))
834
+ end
835
+ let(:rest_publish_channel) { rest_publish_client.channels.get(channel_name) }
836
+
837
+ it 'is compatible with a publishes using MsgPack' do
838
+ expect(rest_publish_client).to be_protocol_binary
839
+
840
+ data = if encoding_spec['expectedHexValue']
841
+ encoding_spec['expectedHexValue'].scan(/../).map { |x| x.hex }.pack('c*')
842
+ else
843
+ encoding_spec['expectedValue']
844
+ end
845
+ rest_publish_channel.publish "event", data
846
+
847
+ response = rest_retrieve_client.get("/channels/#{channel_name}/messages")
848
+ message = response.body[0]
849
+ expect(message['encoding']).to eql(encoding_spec['encoding'])
850
+ expect(encoding_spec['data']).to eql(message['data'])
851
+ stop_reactor
852
+ end
853
+ end
854
+ end
855
+ end
856
+ end
735
857
  end