ably-rest 0.8.5 → 0.8.6

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/SPEC.md +1380 -631
  4. data/ably-rest.gemspec +11 -5
  5. data/lib/submodules/ably-ruby/.travis.yml +1 -1
  6. data/lib/submodules/ably-ruby/CHANGELOG.md +42 -48
  7. data/lib/submodules/ably-ruby/ably.gemspec +7 -1
  8. data/lib/submodules/ably-ruby/lib/ably.rb +2 -0
  9. data/lib/submodules/ably-ruby/lib/ably/auth.rb +155 -47
  10. data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +2 -0
  11. data/lib/submodules/ably-ruby/lib/ably/models/channel_state_change.rb +2 -3
  12. data/lib/submodules/ably-ruby/lib/ably/models/connection_details.rb +54 -0
  13. data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +14 -4
  14. data/lib/submodules/ably-ruby/lib/ably/models/token_details.rb +13 -7
  15. data/lib/submodules/ably-ruby/lib/ably/models/token_request.rb +1 -2
  16. data/lib/submodules/ably-ruby/lib/ably/modules/ably.rb +3 -2
  17. data/lib/submodules/ably-ruby/lib/ably/modules/message_emitter.rb +1 -3
  18. data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +2 -2
  19. data/lib/submodules/ably-ruby/lib/ably/realtime/auth.rb +6 -0
  20. data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +15 -4
  21. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +2 -0
  22. data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +10 -3
  23. data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +11 -1
  24. data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +62 -6
  25. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +58 -54
  26. data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +18 -5
  27. data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +9 -1
  28. data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +32 -14
  29. data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +1 -1
  30. data/lib/submodules/ably-ruby/lib/ably/version.rb +1 -1
  31. data/lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb +251 -11
  32. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +12 -2
  33. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +316 -24
  34. data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +93 -1
  35. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +177 -86
  36. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +284 -60
  37. data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +45 -6
  38. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +4 -0
  39. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +181 -49
  40. data/lib/submodules/ably-ruby/spec/acceptance/realtime/time_spec.rb +13 -0
  41. data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +222 -4
  42. data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +132 -1
  43. data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +129 -28
  44. data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +7 -7
  45. data/lib/submodules/ably-ruby/spec/acceptance/rest/time_spec.rb +10 -0
  46. data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +41 -17
  47. data/lib/submodules/ably-ruby/spec/spec_helper.rb +1 -0
  48. data/lib/submodules/ably-ruby/spec/support/debug_failure_helper.rb +16 -0
  49. data/lib/submodules/ably-ruby/spec/unit/models/connection_details_spec.rb +60 -0
  50. data/lib/submodules/ably-ruby/spec/unit/models/protocol_message_spec.rb +45 -0
  51. data/lib/submodules/ably-ruby/spec/unit/modules/event_emitter_spec.rb +3 -1
  52. data/lib/submodules/ably-ruby/spec/unit/realtime/channel_spec.rb +6 -5
  53. data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +5 -1
  54. data/lib/submodules/ably-ruby/spec/unit/realtime/connection_spec.rb +5 -1
  55. data/lib/submodules/ably-ruby/spec/unit/realtime/realtime_spec.rb +5 -1
  56. metadata +57 -13
@@ -1,3 +1,3 @@
1
1
  module Ably
2
- VERSION = '0.8.5'
2
+ VERSION = '0.8.6'
3
3
  end
@@ -70,15 +70,6 @@ describe Ably::Realtime::Auth, :event_machine do
70
70
  end
71
71
  end
72
72
 
73
- context '#token' do
74
- let(:client_options) { default_options.merge(token: random_str) }
75
-
76
- it 'contains the current token after auth' do
77
- expect(auth.token).to_not be_nil
78
- stop_reactor
79
- end
80
- end
81
-
82
73
  context '#current_token_details' do
83
74
  it 'contains the current token after auth' do
84
75
  expect(auth.current_token_details).to be_nil
@@ -99,6 +90,7 @@ describe Ably::Realtime::Auth, :event_machine do
99
90
  context '#options (auth_options)' do
100
91
  let(:auth_url) { "https://echo.ably.io/?type=text" }
101
92
  let(:auth_params) { { :body => random_str } }
93
+ let(:client_options) { default_options.merge(auto_connect: false) }
102
94
 
103
95
  it 'contains the configured auth options' do
104
96
  auth.authorise({}, auth_url: auth_url, auth_params: auth_params) do
@@ -193,6 +185,75 @@ describe Ably::Realtime::Auth, :event_machine do
193
185
  stop_reactor
194
186
  end
195
187
  end
188
+
189
+ context 'when implicitly called, with an explicit ClientOptions client_id' do
190
+ let(:client_id) { random_str }
191
+ let(:client_options) { default_options.merge(auth_callback: Proc.new { auth_token_object }, client_id: client_id, log_level: :none) }
192
+ let(:rest_auth_client) { Ably::Rest::Client.new(default_options.merge(key: api_key, client_id: 'invalid')) }
193
+
194
+ context 'and an incompatible client_id in a TokenDetails object passed to the auth callback' do
195
+ let(:auth_token_object) { rest_auth_client.auth.request_token }
196
+
197
+ it 'rejects a TokenDetails object with an incompatible client_id and raises an exception' do
198
+ client.connect
199
+ client.connection.on(:error) do |error|
200
+ expect(error).to be_a(Ably::Exceptions::IncompatibleClientId)
201
+ EventMachine.add_timer(0.1) do
202
+ expect(client.connection).to be_failed
203
+ stop_reactor
204
+ end
205
+ end
206
+ end
207
+ end
208
+
209
+ context 'and an incompatible client_id in a TokenRequest object passed to the auth callback and raises an exception' do
210
+ let(:auth_token_object) { rest_auth_client.auth.create_token_request }
211
+
212
+ it 'rejects a TokenRequests object with an incompatible client_id and raises an exception' do
213
+ client.connect
214
+ client.connection.on(:error) do |error|
215
+ expect(error).to be_a(Ably::Exceptions::IncompatibleClientId)
216
+ EventMachine.add_timer(0.1) do
217
+ expect(client.connection).to be_failed
218
+ stop_reactor
219
+ end
220
+ end
221
+ end
222
+ end
223
+ end
224
+
225
+ context 'when explicitly called, with an explicit ClientOptions client_id' do
226
+ let(:auth_proc) do
227
+ Proc.new do
228
+ if !@requested
229
+ @requested = true
230
+ valid_auth_token
231
+ else
232
+ invalid_auth_token
233
+ end
234
+ end
235
+ end
236
+
237
+ let(:client_id) { random_str }
238
+ let(:client_options) { default_options.merge(auth_callback: auth_proc, client_id: client_id, log_level: :none) }
239
+ let(:valid_auth_token) { Ably::Rest::Client.new(default_options.merge(key: api_key, client_id: client_id)).auth.request_token }
240
+ let(:invalid_auth_token) { Ably::Rest::Client.new(default_options.merge(key: api_key, client_id: 'invalid')).auth.request_token }
241
+
242
+ context 'and an incompatible client_id in a TokenDetails object passed to the auth callback' do
243
+ it 'rejects a TokenDetails object with an incompatible client_id and raises an exception' do
244
+ client.connection.once(:connected) do
245
+ client.auth.authorise({}, force: true)
246
+ client.connection.on(:error) do |error|
247
+ expect(error).to be_a(Ably::Exceptions::IncompatibleClientId)
248
+ EventMachine.add_timer(0.1) do
249
+ expect(client.connection).to be_failed
250
+ stop_reactor
251
+ end
252
+ end
253
+ end
254
+ end
255
+ end
256
+ end
196
257
  end
197
258
 
198
259
  context '#authorise_async' do
@@ -216,7 +277,7 @@ describe Ably::Realtime::Auth, :event_machine do
216
277
  end
217
278
  end
218
279
 
219
- context '#auth_params' do
280
+ context '#auth_params_sync' do
220
281
  it 'returns the auth params synchronously' do
221
282
  expect(auth.auth_params_sync).to be_a(Hash)
222
283
  stop_reactor
@@ -232,11 +293,190 @@ describe Ably::Realtime::Auth, :event_machine do
232
293
  end
233
294
  end
234
295
 
235
- context '#auth_header' do
296
+ context '#auth_header_sync' do
236
297
  it 'returns an auth header synchronously' do
237
298
  expect(auth.auth_header_sync).to be_a(String)
238
299
  stop_reactor
239
300
  end
240
301
  end
302
+
303
+ describe '#client_id_validated?' do
304
+ let(:auth) { Ably::Rest::Client.new(default_options.merge(key: api_key)).auth }
305
+
306
+ context 'when using basic auth' do
307
+ let(:client_options) { default_options.merge(key: api_key) }
308
+
309
+ context 'before connected' do
310
+ it 'is false as basic auth users do not have an identity' do
311
+ expect(client.auth).to_not be_client_id_validated
312
+ stop_reactor
313
+ end
314
+ end
315
+
316
+ context 'once connected' do
317
+ it 'is true' do
318
+ client.connection.once(:connected) do
319
+ expect(client.auth).to be_client_id_validated
320
+ stop_reactor
321
+ end
322
+ end
323
+
324
+ it 'contains a validated wildcard client_id' do
325
+ client.connection.once(:connected) do
326
+ expect(client.auth.client_id).to eql('*')
327
+ stop_reactor
328
+ end
329
+ end
330
+ end
331
+ end
332
+
333
+ context 'when using a token string' do
334
+ context 'with a valid client_id' do
335
+ let(:client_options) { default_options.merge(token: auth.request_token(client_id: 'present').token) }
336
+
337
+ context 'before connected' do
338
+ it 'is false as identification is not possible from an opaque token string' do
339
+ expect(client.auth).to_not be_client_id_validated
340
+ stop_reactor
341
+ end
342
+
343
+ specify '#client_id is nil' do
344
+ expect(client.auth.client_id).to be_nil
345
+ stop_reactor
346
+ end
347
+ end
348
+
349
+ context 'once connected' do
350
+ it 'is true' do
351
+ client.connection.once(:connected) do
352
+ expect(client.auth).to be_client_id_validated
353
+ stop_reactor
354
+ end
355
+ end
356
+
357
+ specify '#client_id is populated' do
358
+ client.connection.once(:connected) do
359
+ expect(client.auth.client_id).to eql('present')
360
+ stop_reactor
361
+ end
362
+ end
363
+ end
364
+ end
365
+
366
+ context 'with no client_id (anonymous)' do
367
+ let(:client_options) { default_options.merge(token: auth.request_token(client_id: nil).token) }
368
+
369
+ context 'before connected' do
370
+ it 'is false as identification is not possible from an opaque token string' do
371
+ expect(client.auth).to_not be_client_id_validated
372
+ stop_reactor
373
+ end
374
+ end
375
+
376
+ context 'once connected' do
377
+ it 'is true' do
378
+ client.connection.once(:connected) do
379
+ expect(client.auth).to be_client_id_validated
380
+ stop_reactor
381
+ end
382
+ end
383
+ end
384
+ end
385
+
386
+ context 'with a wildcard client_id (anonymous)' do
387
+ let(:client_options) { default_options.merge(token: auth.request_token(client_id: '*').token) }
388
+
389
+ context 'before connected' do
390
+ it 'is false as identification is not possible from an opaque token string' do
391
+ expect(client.auth).to_not be_client_id_validated
392
+ stop_reactor
393
+ end
394
+ end
395
+
396
+ context 'once connected' do
397
+ it 'is true' do
398
+ client.connection.once(:connected) do
399
+ expect(client.auth).to be_client_id_validated
400
+ stop_reactor
401
+ end
402
+ end
403
+ end
404
+ end
405
+ end
406
+
407
+ context 'when using a token' do
408
+ context 'with a client_id' do
409
+ let(:client_options) { default_options.merge(token: auth.request_token(client_id: 'present')) }
410
+
411
+ it 'is true' do
412
+ expect(client.auth).to be_client_id_validated
413
+ stop_reactor
414
+ end
415
+
416
+ context 'once connected' do
417
+ it 'is true' do
418
+ client.connection.once(:connected) do
419
+ expect(client.auth).to be_client_id_validated
420
+ stop_reactor
421
+ end
422
+ end
423
+ end
424
+ end
425
+
426
+ context 'with no client_id (anonymous)' do
427
+ let(:client_options) { default_options.merge(token: auth.request_token(client_id: nil)) }
428
+
429
+ it 'is true' do
430
+ expect(client.auth).to be_client_id_validated
431
+ stop_reactor
432
+ end
433
+
434
+ context 'once connected' do
435
+ it 'is true' do
436
+ client.connection.once(:connected) do
437
+ expect(client.auth).to be_client_id_validated
438
+ stop_reactor
439
+ end
440
+ end
441
+ end
442
+ end
443
+
444
+ context 'with a wildcard client_id (anonymous)' do
445
+ let(:client_options) { default_options.merge(token: auth.request_token(client_id: '*')) }
446
+
447
+ it 'is true' do
448
+ expect(client.auth).to be_client_id_validated
449
+ stop_reactor
450
+ end
451
+
452
+ context 'once connected' do
453
+ it 'is true' do
454
+ client.connection.once(:connected) do
455
+ expect(client.auth).to be_client_id_validated
456
+ stop_reactor
457
+ end
458
+ end
459
+ end
460
+ end
461
+ end
462
+
463
+ context 'when using a token request with a client_id' do
464
+ let(:client_options) { default_options.merge(token: auth.create_token_request(client_id: 'present')) }
465
+
466
+ it 'is not true as identification is not confirmed until authenticated' do
467
+ expect(client.auth).to_not be_client_id_validated
468
+ stop_reactor
469
+ end
470
+
471
+ context 'once connected' do
472
+ it 'is true as identification is completed following CONNECTED ProtocolMessage' do
473
+ client.channel('test').publish('a') do
474
+ expect(client.auth).to be_client_id_validated
475
+ stop_reactor
476
+ end
477
+ end
478
+ end
479
+ end
480
+ end
241
481
  end
242
482
  end
@@ -186,9 +186,19 @@ describe Ably::Realtime::Channel, '#history', :event_machine do
186
186
  messages.next do |next_page_messages|
187
187
  expect(next_page_messages.items.count).to eql(5)
188
188
  expect(next_page_messages.items.map(&:data).uniq.first).to eql(message_before_attach)
189
- expect(next_page_messages).to be_last
190
189
 
191
- stop_reactor
190
+ if next_page_messages.last?
191
+ expect(next_page_messages).to be_last
192
+ stop_reactor
193
+ else
194
+ # If previous page said there is another page it is plausible and correct that
195
+ # the next page is empty and then the last, if the limit was satisfied
196
+ next_page_messages.next do |empty_page|
197
+ expect(empty_page.items.count).to eql(0)
198
+ expect(empty_page).to be_last
199
+ stop_reactor
200
+ end
201
+ end
192
202
  end
193
203
  end
194
204
  end
@@ -504,55 +504,143 @@ describe Ably::Realtime::Channel, :event_machine do
504
504
  end
505
505
  end
506
506
 
507
+ context 'nil attributes' do
508
+ context 'when name is nil' do
509
+ let(:data) { random_str }
510
+
511
+ it 'publishes the message without a name attribute in the payload' do
512
+ published = false
513
+ channel.publish(nil, data) do
514
+ published = true
515
+ end
516
+
517
+ channel.subscribe do |message|
518
+ expect(message.name).to be_nil
519
+ channel.history do |page|
520
+ expect(page.items.first.name).to be_nil
521
+ expect(page.items.first.data).to eql(data)
522
+ EM.add_timer(0.5) do
523
+ expect(published).to eql(true)
524
+ stop_reactor
525
+ end
526
+ end
527
+ end
528
+ end
529
+ end
530
+
531
+ context 'when data is nil' do
532
+ let(:name) { random_str }
533
+
534
+ it 'publishes the message without a data attribute in the payload' do
535
+ published = false
536
+ channel.publish(name, nil) do
537
+ published = true
538
+ end
539
+
540
+ channel.subscribe do |message|
541
+ expect(message.data).to be_nil
542
+ channel.history do |page|
543
+ expect(page.items.first.name).to eql(name)
544
+ expect(page.items.first.data).to be_nil
545
+ EM.add_timer(0.5) do
546
+ expect(published).to eql(true)
547
+ stop_reactor
548
+ end
549
+ end
550
+ end
551
+ end
552
+ end
553
+
554
+ context 'with neither name or data attributes' do
555
+ let(:name) { random_str }
556
+
557
+ it 'publishes the message without any attributes in the payload' do
558
+ channel.publish(nil) do
559
+ channel.history do |page|
560
+ expect(page.items.first.name).to be_nil
561
+ expect(page.items.first.data).to be_nil
562
+ stop_reactor
563
+ end
564
+ end
565
+ end
566
+ end
567
+ end
568
+
507
569
  context 'with two invalid message out of 12' do
508
- let(:client_options) { default_options.merge(client_id: 'valid') }
570
+ let(:rest_client) { Ably::Rest::Client.new(default_options.merge(client_id: 'valid')) }
571
+
509
572
  let(:invalid_messages) do
510
573
  2.times.map do |index|
511
574
  Ably::Models::Message(name: index.to_s, data: { "index" => index + 10 }, client_id: 'prohibited')
512
575
  end
513
576
  end
514
577
 
515
- it 'calls the errback once' do
516
- skip 'Waiting for issue #256 to be resolved'
517
- channel.publish(messages + invalid_messages).tap do |deferrable|
518
- deferrable.callback do
519
- raise 'Publish should have failed'
520
- end
578
+ context 'before client_id is known (validated)' do
579
+ let(:client_options) { default_options.merge(token: rest_client.auth.request_token.token, log_level: :error) }
521
580
 
522
- deferrable.errback do |error, message|
523
- # TODO: Review whether we should fail once or multiple times
524
- channel.history do |page|
525
- expect(page.items.count).to eql(0)
526
- stop_reactor
581
+ it 'calls the errback once' do
582
+ channel.publish(messages + invalid_messages).tap do |deferrable|
583
+ deferrable.callback do
584
+ raise 'Publish should have failed'
585
+ end
586
+
587
+ deferrable.errback do |error, message|
588
+ # TODO: Review whether we should fail once or multiple times
589
+ channel.history do |page|
590
+ expect(page.items.count).to eql(0)
591
+ stop_reactor
592
+ end
527
593
  end
528
594
  end
529
595
  end
530
596
  end
597
+
598
+ context 'when client_id is known (validated)' do
599
+ let(:client_options) { default_options.merge(client_id: 'valid') }
600
+
601
+ it 'raises an exception' do
602
+ expect { channel.publish(messages + invalid_messages) }.to raise_error Ably::Exceptions::IncompatibleClientId
603
+ stop_reactor
604
+ end
605
+ end
531
606
  end
532
607
 
533
608
  context 'only invalid messages' do
534
- let(:client_options) { default_options.merge(client_id: 'valid') }
609
+ let(:rest_client) { Ably::Rest::Client.new(default_options.merge(client_id: 'valid')) }
610
+
535
611
  let(:invalid_messages) do
536
612
  10.times.map do |index|
537
613
  Ably::Models::Message(name: index.to_s, data: { "index" => index + 10 }, client_id: 'prohibited')
538
614
  end
539
615
  end
540
616
 
541
- it 'calls the errback once' do
542
- skip 'Waiting for issue #256 to be resolved'
543
- channel.publish(invalid_messages).tap do |deferrable|
544
- deferrable.callback do
545
- raise 'Publish should have failed'
546
- end
617
+ context 'before client_id is known (validated)' do
618
+ let(:client_options) { default_options.merge(token: rest_client.auth.request_token.token, log_level: :error) }
547
619
 
548
- deferrable.errback do |error, message|
549
- channel.history do |page|
550
- expect(page.items.count).to eql(0)
551
- stop_reactor
620
+ it 'calls the errback once' do
621
+ channel.publish(invalid_messages).tap do |deferrable|
622
+ deferrable.callback do
623
+ raise 'Publish should have failed'
624
+ end
625
+
626
+ deferrable.errback do |error, message|
627
+ channel.history do |page|
628
+ expect(page.items.count).to eql(0)
629
+ stop_reactor
630
+ end
552
631
  end
553
632
  end
554
633
  end
555
634
  end
635
+
636
+ context 'when client_id is known (validated)' do
637
+ let(:client_options) { default_options.merge(client_id: 'valid') }
638
+
639
+ it 'raises an exception' do
640
+ expect { channel.publish(invalid_messages) }.to raise_error Ably::Exceptions::IncompatibleClientId
641
+ stop_reactor
642
+ end
643
+ end
556
644
  end
557
645
  end
558
646
 
@@ -581,6 +669,208 @@ describe Ably::Realtime::Channel, :event_machine do
581
669
  end
582
670
  end
583
671
  end
672
+
673
+ context 'identified clients' do
674
+ context 'when authenticated with a wildcard client_id' do
675
+ let(:token) { Ably::Rest::Client.new(default_options).auth.request_token(client_id: '*') }
676
+ let(:client_options) { default_options.merge(key: nil, token: token) }
677
+ let(:client) { auto_close Ably::Realtime::Client.new(client_options) }
678
+ let(:channel) { client.channels.get(channel_name) }
679
+
680
+ context 'with a valid client_id in the message' do
681
+ it 'succeeds' do
682
+ channel.publish([name: 'event', client_id: 'validClient']).tap do |deferrable|
683
+ deferrable.errback { raise 'Should have succeeded' }
684
+ end
685
+ channel.subscribe('event') do |message|
686
+ expect(message.client_id).to eql('validClient')
687
+ EM.add_timer(0.5) { stop_reactor }
688
+ end
689
+ end
690
+ end
691
+
692
+ context 'with a wildcard client_id in the message' do
693
+ it 'throws an exception' do
694
+ expect { channel.publish([name: 'event', client_id: '*']) }.to raise_error Ably::Exceptions::IncompatibleClientId
695
+ stop_reactor
696
+ end
697
+ end
698
+
699
+ context 'with an empty client_id in the message' do
700
+ it 'succeeds and publishes without a client_id' do
701
+ channel.publish([name: 'event', client_id: nil]).tap do |deferrable|
702
+ deferrable.errback { raise 'Should have succeeded' }
703
+ end
704
+ channel.subscribe('event') do |message|
705
+ expect(message.client_id).to be_nil
706
+ EM.add_timer(0.5) { stop_reactor }
707
+ end
708
+ end
709
+ end
710
+ end
711
+
712
+ context 'when authenticated with a Token string with an implicit client_id' do
713
+ let(:token) { Ably::Rest::Client.new(default_options).auth.request_token(client_id: 'valid').token }
714
+ let(:client_options) { default_options.merge(key: nil, token: token) }
715
+ let(:client) { auto_close Ably::Realtime::Client.new(client_options) }
716
+ let(:channel) { client.channels.get(channel_name) }
717
+
718
+ context 'before the client is CONNECTED and the client\'s identity has been obtained' do
719
+ context 'with a valid client_id in the message' do
720
+ it 'succeeds' do
721
+ channel.publish([name: 'event', client_id: 'valid']).tap do |deferrable|
722
+ deferrable.errback { raise 'Should have succeeded' }
723
+ end
724
+ channel.subscribe('event') do |message|
725
+ expect(message.client_id).to eql('valid')
726
+ EM.add_timer(0.5) { stop_reactor }
727
+ end
728
+ end
729
+ end
730
+
731
+ context 'with an invalid client_id in the message' do
732
+ let(:client_options) { default_options.merge(key: nil, token: token, log_level: :error) }
733
+ it 'succeeds in the client library but then fails when delivered to Ably' do
734
+ channel.publish([name: 'event', client_id: 'invalid']).tap do |deferrable|
735
+ EM.add_timer(0.5) { stop_reactor }
736
+ end
737
+ channel.subscribe('event') do |message|
738
+ raise 'Message should not have been published'
739
+ end
740
+ end
741
+ end
742
+
743
+ context 'with an empty client_id in the message' do
744
+ it 'succeeds and publishes with an implicit client_id' do
745
+ channel.publish([name: 'event', client_id: nil]).tap do |deferrable|
746
+ deferrable.errback { raise 'Should have succeeded' }
747
+ end
748
+ channel.subscribe('event') do |message|
749
+ expect(message.client_id).to eql('valid')
750
+ EM.add_timer(0.5) { stop_reactor }
751
+ end
752
+ end
753
+ end
754
+ end
755
+
756
+ context 'after the client is CONNECTED and the client\'s identity is known' do
757
+ context 'with a valid client_id in the message' do
758
+ it 'succeeds' do
759
+ client.connection.once(:connected) do
760
+ channel.publish([name: 'event', client_id: 'valid']).tap do |deferrable|
761
+ deferrable.errback { raise 'Should have succeeded' }
762
+ end
763
+ channel.subscribe('event') do |message|
764
+ expect(message.client_id).to eql('valid')
765
+ EM.add_timer(0.5) { stop_reactor }
766
+ end
767
+ end
768
+ end
769
+ end
770
+
771
+ context 'with an invalid client_id in the message' do
772
+ it 'throws an exception' do
773
+ client.connection.once(:connected) do
774
+ expect { channel.publish([name: 'event', client_id: 'invalid']) }.to raise_error Ably::Exceptions::IncompatibleClientId
775
+ stop_reactor
776
+ end
777
+ end
778
+ end
779
+
780
+ context 'with an empty client_id in the message' do
781
+ it 'succeeds and publishes with an implicit client_id' do
782
+ client.connection.once(:connected) do
783
+ channel.publish([name: 'event', client_id: nil]).tap do |deferrable|
784
+ deferrable.errback { raise 'Should have succeeded' }
785
+ end
786
+ channel.subscribe('event') do |message|
787
+ expect(message.client_id).to eql('valid')
788
+ EM.add_timer(0.5) { stop_reactor }
789
+ end
790
+ end
791
+ end
792
+ end
793
+ end
794
+ end
795
+
796
+ context 'when authenticated with a valid client_id' do
797
+ let(:token) { Ably::Rest::Client.new(default_options).auth.request_token(client_id: 'valid') }
798
+ let(:client_options) { default_options.merge(key: nil, token: token) }
799
+ let(:client) { auto_close Ably::Realtime::Client.new(client_options) }
800
+ let(:channel) { client.channels.get(channel_name) }
801
+
802
+ context 'with a valid client_id' do
803
+ it 'succeeds' do
804
+ channel.publish([name: 'event', client_id: 'valid']).tap do |deferrable|
805
+ deferrable.errback { raise 'Should have succeeded' }
806
+ end
807
+ channel.subscribe('event') do |message|
808
+ expect(message.client_id).to eql('valid')
809
+ EM.add_timer(0.5) { stop_reactor }
810
+ end
811
+ end
812
+ end
813
+
814
+ context 'with a wildcard client_id in the message' do
815
+ it 'throws an exception' do
816
+ expect { channel.publish([name: 'event', client_id: '*']) }.to raise_error Ably::Exceptions::IncompatibleClientId
817
+ stop_reactor
818
+ end
819
+ end
820
+
821
+ context 'with an invalid client_id in the message' do
822
+ it 'throws an exception' do
823
+ expect { channel.publish([name: 'event', client_id: 'invalid']) }.to raise_error Ably::Exceptions::IncompatibleClientId
824
+ stop_reactor
825
+ end
826
+ end
827
+
828
+ context 'with an empty client_id in the message' do
829
+ it 'succeeds and publishes with an implicit client_id' do
830
+ channel.publish([name: 'event', client_id: nil]).tap do |deferrable|
831
+ deferrable.errback { raise 'Should have succeeded' }
832
+ end
833
+ channel.subscribe('event') do |message|
834
+ expect(message.client_id).to eql('valid')
835
+ EM.add_timer(0.5) { stop_reactor }
836
+ end
837
+ end
838
+ end
839
+ end
840
+
841
+ context 'when anonymous and no client_id' do
842
+ let(:token) { Ably::Rest::Client.new(default_options).auth.request_token(client_id: nil) }
843
+ let(:client_options) { default_options.merge(key: nil, token: token) }
844
+ let(:client) { auto_close Ably::Realtime::Client.new(client_options) }
845
+ let(:channel) { client.channels.get(channel_name) }
846
+
847
+ context 'with a client_id in the message' do
848
+ it 'throws an exception' do
849
+ expect { channel.publish([name: 'event', client_id: 'invalid']) }.to raise_error Ably::Exceptions::IncompatibleClientId
850
+ stop_reactor
851
+ end
852
+ end
853
+
854
+ context 'with a wildcard client_id in the message' do
855
+ it 'throws an exception' do
856
+ expect { channel.publish([name: 'event', client_id: '*']) }.to raise_error Ably::Exceptions::IncompatibleClientId
857
+ stop_reactor
858
+ end
859
+ end
860
+
861
+ context 'with an empty client_id in the message' do
862
+ it 'succeeds and publishes with an implicit client_id' do
863
+ channel.publish([name: 'event', client_id: nil]).tap do |deferrable|
864
+ deferrable.errback { raise 'Should have succeeded' }
865
+ end
866
+ channel.subscribe('event') do |message|
867
+ expect(message.client_id).to be_nil
868
+ EM.add_timer(0.5) { stop_reactor }
869
+ end
870
+ end
871
+ end
872
+ end
873
+ end
584
874
  end
585
875
 
586
876
  describe '#subscribe' do
@@ -881,7 +1171,7 @@ describe Ably::Realtime::Channel, :event_machine do
881
1171
 
882
1172
  context 'a :failed channel' do
883
1173
  let(:original_error) { RuntimeError.new }
884
- let(:client_options) { default_options.merge(log_level: :fatal) }
1174
+ let(:client_options) { default_options.merge(log_level: :fatal) }
885
1175
 
886
1176
  it 'remains in the :failed state and retains the error_reason' do
887
1177
  channel.attach do
@@ -904,6 +1194,8 @@ describe Ably::Realtime::Channel, :event_machine do
904
1194
  end
905
1195
 
906
1196
  context 'a channel ATTACH request when connection SUSPENDED' do
1197
+ let(:client_options) { default_options.merge(log_level: :fatal) }
1198
+
907
1199
  it 'raises an exception' do
908
1200
  client.connect do
909
1201
  client.connection.once(:suspended) do