ably-rest 0.8.9 → 0.8.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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