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.
- checksums.yaml +4 -4
- data/README.md +54 -21
- data/ably-rest.gemspec +6 -2
- data/lib/ably-rest.rb +2 -0
- data/lib/submodules/ably-ruby/CHANGELOG.md +42 -4
- data/lib/submodules/ably-ruby/README.md +2 -3
- data/lib/submodules/ably-ruby/ably.gemspec +6 -2
- data/lib/submodules/ably-ruby/lib/ably/auth.rb +80 -21
- data/lib/submodules/ably-ruby/lib/ably/modules/message_emitter.rb +5 -2
- data/lib/submodules/ably-ruby/lib/ably/realtime/auth.rb +22 -4
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +8 -6
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +4 -2
- data/lib/submodules/ably-ruby/lib/ably/version.rb +3 -1
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb +189 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +19 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +20 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +132 -10
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +19 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +122 -4
- data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +17 -0
- data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +3 -3
- data/lib/submodules/ably-ruby/spec/unit/models/token_details_spec.rb +28 -0
- 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 =
|
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 =
|
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
|
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
|
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
|
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 |
|
205
|
-
# puts "Ping took #{
|
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 =
|
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(
|
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(
|
381
|
+
auth.authorise(nil, force: true)
|
380
382
|
yield
|
381
383
|
else
|
382
384
|
raise e
|
@@ -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
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
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
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
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
|