ably 1.2.5 → 1.2.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/check.yml +1 -1
  3. data/CHANGELOG.md +17 -0
  4. data/README.md +24 -7
  5. data/SPEC.md +1722 -853
  6. data/ably.gemspec +1 -1
  7. data/lib/ably/auth.rb +19 -11
  8. data/lib/ably/models/protocol_message.rb +5 -26
  9. data/lib/ably/modules/safe_deferrable.rb +2 -2
  10. data/lib/ably/modules/state_emitter.rb +1 -1
  11. data/lib/ably/realtime/auth.rb +4 -0
  12. data/lib/ably/realtime/channel/channel_manager.rb +51 -48
  13. data/lib/ably/realtime/channel/channel_properties.rb +9 -0
  14. data/lib/ably/realtime/channel/channel_state_machine.rb +2 -0
  15. data/lib/ably/realtime/channel.rb +4 -3
  16. data/lib/ably/realtime/channels.rb +20 -0
  17. data/lib/ably/realtime/client/incoming_message_dispatcher.rb +14 -13
  18. data/lib/ably/realtime/client.rb +14 -6
  19. data/lib/ably/realtime/connection/connection_manager.rb +21 -22
  20. data/lib/ably/realtime/connection.rb +77 -109
  21. data/lib/ably/realtime/presence/members_map.rb +41 -92
  22. data/lib/ably/realtime/presence/presence_manager.rb +12 -17
  23. data/lib/ably/realtime/presence.rb +15 -6
  24. data/lib/ably/realtime/push.rb +0 -27
  25. data/lib/ably/realtime/recovery_key_context.rb +36 -0
  26. data/lib/ably/rest/client.rb +4 -6
  27. data/lib/ably/rest/push/admin.rb +1 -1
  28. data/lib/ably/rest/push.rb +0 -19
  29. data/lib/ably/util/ably_extensions.rb +29 -0
  30. data/lib/ably/util/crypto.rb +2 -2
  31. data/lib/ably/util/safe_deferrable.rb +1 -1
  32. data/lib/ably/version.rb +5 -7
  33. data/spec/acceptance/realtime/channel_history_spec.rb +8 -12
  34. data/spec/acceptance/realtime/channel_spec.rb +474 -300
  35. data/spec/acceptance/realtime/client_spec.rb +1 -1
  36. data/spec/acceptance/realtime/connection_failures_spec.rb +8 -25
  37. data/spec/acceptance/realtime/connection_spec.rb +28 -120
  38. data/spec/acceptance/realtime/message_spec.rb +23 -52
  39. data/spec/acceptance/realtime/presence_spec.rb +123 -92
  40. data/spec/acceptance/rest/channel_spec.rb +2 -2
  41. data/spec/acceptance/rest/client_spec.rb +9 -2
  42. data/spec/acceptance/rest/message_spec.rb +8 -11
  43. data/spec/acceptance/rest/push_admin_spec.rb +20 -15
  44. data/spec/shared/client_initializer_behaviour.rb +1 -1
  45. data/spec/support/markdown_spec_formatter.rb +1 -1
  46. data/spec/unit/models/protocol_message_spec.rb +0 -78
  47. data/spec/unit/models/token_details_spec.rb +4 -2
  48. data/spec/unit/realtime/channels_spec.rb +1 -1
  49. data/spec/unit/realtime/connection_spec.rb +0 -30
  50. data/spec/unit/realtime/recovery_key_context_spec.rb +36 -0
  51. data/spec/unit/util/crypto_spec.rb +15 -15
  52. metadata +9 -9
  53. data/spec/acceptance/realtime/push_spec.rb +0 -27
  54. data/spec/acceptance/rest/push_spec.rb +0 -25
@@ -60,13 +60,13 @@ describe Ably::Realtime::Presence, :event_machine do
60
60
  end
61
61
 
62
62
  unless expected_state == :left
63
- it 'raise an exception if the channel is detached' do
63
+ it "presence #{method_name} : raise an exception if the channel is detached" do
64
64
  setup_test(method_name, args, options) do
65
65
  channel_client_one.attach do
66
66
  channel_client_one.transition_state_machine :detaching
67
67
  channel_client_one.once(:detached) do
68
68
  presence_client_one.public_send(method_name, args).tap do |deferrable|
69
- deferrable.callback { raise 'Get should not succeed' }
69
+ deferrable.callback { raise "presence #{method_name} should not succeed" }
70
70
  deferrable.errback do |error|
71
71
  expect(error).to be_a(Ably::Exceptions::InvalidState)
72
72
  expect(error.message).to match(/Operation is not allowed when channel is in STATE.Detached/)
@@ -78,12 +78,12 @@ describe Ably::Realtime::Presence, :event_machine do
78
78
  end
79
79
  end
80
80
 
81
- it 'raise an exception if the channel becomes detached' do
81
+ it "presence #{method_name} : raise an exception if the channel becomes detached" do
82
82
  setup_test(method_name, args, options) do
83
83
  channel_client_one.attach do
84
84
  channel_client_one.transition_state_machine :detaching
85
85
  presence_client_one.public_send(method_name, args).tap do |deferrable|
86
- deferrable.callback { raise 'Get should not succeed' }
86
+ deferrable.callback { raise "presence #{method_name} should not succeed" }
87
87
  deferrable.errback do |error|
88
88
  expect(error).to be_a(Ably::Exceptions::InvalidState)
89
89
  expect(error.message).to match(/Operation failed as channel transitioned to STATE.Detached/)
@@ -94,13 +94,13 @@ describe Ably::Realtime::Presence, :event_machine do
94
94
  end
95
95
  end
96
96
 
97
- it 'raise an exception if the channel is failed' do
97
+ it "presence #{method_name} : raise an exception if the channel is failed" do
98
98
  setup_test(method_name, args, options) do
99
99
  channel_client_one.attach do
100
100
  channel_client_one.transition_state_machine :failed
101
101
  expect(channel_client_one.state).to eq(:failed)
102
102
  presence_client_one.public_send(method_name, args).tap do |deferrable|
103
- deferrable.callback { raise 'Get should not succeed' }
103
+ deferrable.callback { raise "presence #{method_name} : Get should not succeed" }
104
104
  deferrable.errback do |error|
105
105
  expect(error).to be_a(Ably::Exceptions::InvalidState)
106
106
  expect(error.message).to match(/Operation is not allowed when channel is in STATE.Failed/)
@@ -111,11 +111,11 @@ describe Ably::Realtime::Presence, :event_machine do
111
111
  end
112
112
  end
113
113
 
114
- it 'raise an exception if the channel becomes failed' do
114
+ it "presence #{method_name} : raise an exception if the channel becomes failed" do
115
115
  setup_test(method_name, args, options) do
116
116
  channel_client_one.attach do
117
117
  presence_client_one.public_send(method_name, args).tap do |deferrable|
118
- deferrable.callback { raise 'Get should not succeed' }
118
+ deferrable.callback { raise "presence #{method_name} : Get should not succeed" }
119
119
  deferrable.errback do |error|
120
120
  expect(error).to be_a(Ably::Exceptions::MessageDeliveryFailed)
121
121
  stop_reactor
@@ -516,11 +516,11 @@ describe Ably::Realtime::Presence, :event_machine do
516
516
  stop_reactor
517
517
  end
518
518
 
519
- it 'will emit an :in_sync event when synchronisation is complete' do
519
+ it 'will emit an :sync_complete event when synchronisation is complete' do
520
520
  presence_client_one.enter
521
521
  presence_client_two.enter
522
522
 
523
- presence_anonymous_client.members.once(:in_sync) do
523
+ presence_anonymous_client.members.once(:sync_complete) do
524
524
  stop_reactor
525
525
  end
526
526
 
@@ -545,7 +545,7 @@ describe Ably::Realtime::Presence, :event_machine do
545
545
  entered += 1
546
546
  next unless entered == 2
547
547
 
548
- presence_anonymous_client.members.once(:in_sync) do
548
+ presence_anonymous_client.members.once(:sync_complete) do
549
549
  expect(presence_anonymous_client.members.count).to eql(2)
550
550
  member_ids = presence_anonymous_client.members.map(&:member_key)
551
551
  expect(member_ids.count).to eql(2)
@@ -580,7 +580,6 @@ describe Ably::Realtime::Presence, :event_machine do
580
580
  action = Ably::Models::ProtocolMessage::ACTION.Presence
581
581
  presence_msg = Ably::Models::ProtocolMessage.new(
582
582
  action: action,
583
- connection_serial: 20,
584
583
  channel: channel_name,
585
584
  presence: presence_data,
586
585
  timestamp: Time.now.to_i * 1000
@@ -633,7 +632,6 @@ describe Ably::Realtime::Presence, :event_machine do
633
632
  action = Ably::Models::ProtocolMessage::ACTION.Presence
634
633
  presence_msg = Ably::Models::ProtocolMessage.new(
635
634
  action: action,
636
- connection_serial: anonymous_client.connection.serial + 1,
637
635
  channel: channel_name,
638
636
  presence: presence_data,
639
637
  timestamp: Time.now.to_i * 1000
@@ -644,7 +642,6 @@ describe Ably::Realtime::Presence, :event_machine do
644
642
  action = Ably::Models::ProtocolMessage::ACTION.Sync
645
643
  sync_msg = Ably::Models::ProtocolMessage.new(
646
644
  action: action,
647
- connection_serial: anonymous_client.connection.serial + 2,
648
645
  channel: channel_name,
649
646
  channel_serial: 'validserialprefix:', # with no part after the `:` this indicates the end to the SYNC
650
647
  presence: [],
@@ -707,21 +704,29 @@ describe Ably::Realtime::Presence, :event_machine do
707
704
 
708
705
  context '#sync_complete? and SYNC flags (#RTP1)' do
709
706
  context 'when attaching to a channel without any members present' do
710
- xit 'sync_complete? is true, there is no presence flag, and the presence channel is considered synced immediately (#RTP1)' do
711
- flag_checked = false
707
+ it 'sync_complete? is true, no members are received and the presence channel is synced (#RTP1)' do
708
+ sync_info_received = false
712
709
 
713
710
  anonymous_client.connection.__incoming_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
714
711
  if protocol_message.action == :attached
715
- flag_checked = true
716
- expect(protocol_message.has_presence_flag?).to eql(false)
712
+ if protocol_message.has_presence_flag?
713
+ sync_info_received = false
714
+ else
715
+ sync_info_received = true
716
+ end
717
+ end
718
+ if protocol_message.action == Ably::Models::ProtocolMessage::ACTION.Sync
719
+ expect(protocol_message.presence).to be_empty
720
+ sync_info_received = true
717
721
  end
718
722
  end
719
723
 
720
724
  channel_anonymous_client.attach do
721
- expect(channel_anonymous_client.presence).to be_sync_complete
722
- EventMachine.next_tick do
723
- expect(flag_checked).to eql(true)
724
- stop_reactor
725
+ wait_until(lambda { channel_anonymous_client.presence.sync_complete? and sync_info_received}) do
726
+ channel_anonymous_client.presence.get do |members|
727
+ expect(members).to be_empty
728
+ stop_reactor
729
+ end
725
730
  end
726
731
  end
727
732
  end
@@ -851,7 +856,7 @@ describe Ably::Realtime::Presence, :event_machine do
851
856
  # Hacky accessing a private method, but absent members are intentionally not exposed to any public APIs
852
857
  expect(presence_anonymous_client.members.send(:absent_members).length).to eql(1)
853
858
 
854
- presence_anonymous_client.members.once(:in_sync) do
859
+ presence_anonymous_client.members.once(:sync_complete) do
855
860
  # Check that members count is exact indicating the members with LEAVE action after sync are removed
856
861
  expect(presence_anonymous_client).to be_sync_complete
857
862
  expect(presence_anonymous_client.members.length).to eql(enter_expected_count - 1)
@@ -1007,7 +1012,7 @@ describe Ably::Realtime::Presence, :event_machine do
1007
1012
 
1008
1013
  channel_anonymous_client.attach do
1009
1014
  presence_anonymous_client.get(wait_for_sync: false) do |members|
1010
- expect(presence_anonymous_client.members).to_not be_in_sync
1015
+ expect(presence_anonymous_client.members).to_not be_sync_complete
1011
1016
  expect(members.count).to eql(0)
1012
1017
  stop_reactor
1013
1018
  end
@@ -1214,7 +1219,7 @@ describe Ably::Realtime::Presence, :event_machine do
1214
1219
  presence_client_one.subscribe(:enter) do
1215
1220
  presence_client_one.unsubscribe :enter
1216
1221
  EventMachine.add_timer(0.5) do
1217
- expect(presence_client_one.members).to be_in_sync
1222
+ expect(presence_client_one.members).to be_sync_complete
1218
1223
  expect(presence_client_one.members.send(:members).count).to eql(1)
1219
1224
  presence_client_one.leave data
1220
1225
  end
@@ -1592,14 +1597,16 @@ describe Ably::Realtime::Presence, :event_machine do
1592
1597
  end
1593
1598
 
1594
1599
  it 'fails if the connection is DETACHED (#RTP11b)' do
1595
- channel_client_one.attach do
1596
- channel_client_one.detach do
1597
- presence_client_one.get.tap do |deferrable|
1598
- deferrable.callback { raise 'Get should not succeed' }
1599
- deferrable.errback do |error|
1600
- expect(error).to be_a(Ably::Exceptions::InvalidState)
1601
- expect(error.message).to match(/Operation is not allowed when channel is in STATE.Detached/)
1602
- stop_reactor
1600
+ client_one.connection.once :connected do
1601
+ channel_client_one.attach do
1602
+ channel_client_one.detach do
1603
+ presence_client_one.get.tap do |deferrable|
1604
+ deferrable.callback { raise 'Get should not succeed' }
1605
+ deferrable.errback do |error|
1606
+ expect(error).to be_a(Ably::Exceptions::InvalidState)
1607
+ expect(error.message).to match(/Operation is not allowed when channel is in STATE.Detached/)
1608
+ stop_reactor
1609
+ end
1603
1610
  end
1604
1611
  end
1605
1612
  end
@@ -1815,29 +1822,31 @@ describe Ably::Realtime::Presence, :event_machine do
1815
1822
  let(:total_members) { members_per_client * 2 }
1816
1823
 
1817
1824
  it 'returns a complete list of members on all clients' do
1818
- members_per_client.times do |indx|
1819
- presence_client_one.enter_client("client_1:#{indx}")
1820
- presence_client_two.enter_client("client_2:#{indx}")
1821
- end
1825
+ wait_until(lambda { client_one.connection.state == :connected and client_two.connection.state == :connected }) do
1826
+ presence_client_one.subscribe(:enter) do
1827
+ clients_entered[:client_one] += 1
1828
+ end
1822
1829
 
1823
- presence_client_one.subscribe(:enter) do
1824
- clients_entered[:client_one] += 1
1825
- end
1830
+ presence_client_two.subscribe(:enter) do
1831
+ clients_entered[:client_two] += 1
1832
+ end
1826
1833
 
1827
- presence_client_two.subscribe(:enter) do
1828
- clients_entered[:client_two] += 1
1829
- end
1834
+ members_per_client.times do |indx|
1835
+ presence_client_one.enter_client("client_1:#{indx}")
1836
+ presence_client_two.enter_client("client_2:#{indx}")
1837
+ end
1830
1838
 
1831
- wait_until(lambda { clients_entered[:client_one] + clients_entered[:client_two] == total_members * 2 }) do
1832
- presence_anonymous_client.get(wait_for_sync: true) do |anonymous_members|
1833
- expect(anonymous_members.count).to eq(total_members)
1834
- expect(anonymous_members.map(&:client_id).uniq.count).to eq(total_members)
1839
+ wait_until(lambda { clients_entered[:client_one] + clients_entered[:client_two] == total_members * 2 }) do
1840
+ presence_anonymous_client.get(wait_for_sync: true) do |anonymous_members|
1841
+ expect(anonymous_members.count).to eq(total_members)
1842
+ expect(anonymous_members.map(&:client_id).uniq.count).to eq(total_members)
1835
1843
 
1836
- presence_client_one.get(wait_for_sync: true) do |client_one_members|
1837
- presence_client_two.get(wait_for_sync: true) do |client_two_members|
1838
- expect(client_one_members.count).to eq(total_members)
1839
- expect(client_one_members.count).to eq(client_two_members.count)
1840
- stop_reactor
1844
+ presence_client_one.get(wait_for_sync: true) do |client_one_members|
1845
+ presence_client_two.get(wait_for_sync: true) do |client_two_members|
1846
+ expect(client_one_members.count).to eq(total_members)
1847
+ expect(client_one_members.count).to eq(client_two_members.count)
1848
+ stop_reactor
1849
+ end
1841
1850
  end
1842
1851
  end
1843
1852
  end
@@ -2243,7 +2252,6 @@ describe Ably::Realtime::Presence, :event_machine do
2243
2252
  action = Ably::Models::ProtocolMessage::ACTION.Sync
2244
2253
  sync_message = Ably::Models::ProtocolMessage.new(
2245
2254
  action: action,
2246
- connection_serial: 10,
2247
2255
  channel_serial: 'sequenceid:cursor',
2248
2256
  channel: channel_name,
2249
2257
  presence: presence_sync_1,
@@ -2253,7 +2261,6 @@ describe Ably::Realtime::Presence, :event_machine do
2253
2261
 
2254
2262
  sync_message = Ably::Models::ProtocolMessage.new(
2255
2263
  action: action,
2256
- connection_serial: 11,
2257
2264
  channel_serial: 'sequenceid:', # indicates SYNC is complete
2258
2265
  channel: channel_name,
2259
2266
  presence: presence_sync_2,
@@ -2294,7 +2301,6 @@ describe Ably::Realtime::Presence, :event_machine do
2294
2301
  action = Ably::Models::ProtocolMessage::ACTION.Sync
2295
2302
  sync_message = Ably::Models::ProtocolMessage.new(
2296
2303
  action: action,
2297
- connection_serial: 10,
2298
2304
  channel: channel_name,
2299
2305
  presence: presence_sync,
2300
2306
  timestamp: Time.now.to_i * 1000
@@ -2348,7 +2354,6 @@ describe Ably::Realtime::Presence, :event_machine do
2348
2354
  action = Ably::Models::ProtocolMessage::ACTION.Sync
2349
2355
  sync_message = Ably::Models::ProtocolMessage.new(
2350
2356
  action: action,
2351
- connection_serial: 10,
2352
2357
  channel: channel_name,
2353
2358
  presence: presence_sync_protocol_message,
2354
2359
  timestamp: Time.now.to_i * 1000
@@ -2467,7 +2472,7 @@ describe Ably::Realtime::Presence, :event_machine do
2467
2472
  end
2468
2473
 
2469
2474
  leave_message = Ably::Models::PresenceMessage.new(
2470
- 'id' => "#{client_two.connection.id}:#{presence_client_two.client_id}:1",
2475
+ 'id' => "#{client_two.connection.id}:#{client_two.connection.send(:client_msg_serial)}:1",
2471
2476
  'clientId' => presence_client_two.client_id,
2472
2477
  'connectionId' => client_two.connection.id,
2473
2478
  'timestamp' => as_since_epoch(Time.now),
@@ -2532,37 +2537,59 @@ describe Ably::Realtime::Presence, :event_machine do
2532
2537
  let(:member_data) { random_str }
2533
2538
 
2534
2539
  it 'immediately resends all local presence members (#RTP5c2, #RTP19a)' do
2535
- in_sync_confirmed_no_local_members = false
2536
- local_member_leave_event_fired = false
2540
+ member_leave_event_fired = false
2541
+ local_members_sent = false
2542
+
2543
+ presence_client_one.subscribe(:enter) do |entered_member|
2544
+ expect(entered_member.action).to eq(Ably::Models::PresenceMessage::ACTION.Enter)
2545
+ expect(entered_member.data).to eq(member_data)
2546
+ expect(entered_member.client_id).to eq(client_one.auth.client_id)
2547
+ expect(entered_member.id).to be_truthy
2548
+ entered_member_id = entered_member.id
2537
2549
 
2538
- presence_client_one.enter(member_data)
2539
- presence_client_one.subscribe(:enter) do
2540
2550
  presence_client_one.unsubscribe :enter
2541
2551
 
2542
- presence_client_one.subscribe(:leave) do |message|
2543
- # The local member will leave the PresenceMap due to the ATTACHED without Presence
2544
- local_member_leave_event_fired = true
2552
+ expect(presence_client_one.members.length).to eql(1)
2553
+ expect(presence_client_one.members.local_members.length).to eql(1)
2554
+
2555
+ # subscribe to outgoing messages to check for entered local members with id
2556
+ client_one.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
2557
+ if protocol_message.action == :presence
2558
+ protocol_message.presence.each do |local_member|
2559
+ expect(local_member.id).to eq(entered_member_id)
2560
+ expect(local_member.action).to eq(Ably::Models::PresenceMessage::ACTION.Enter)
2561
+ expect(local_member.data).to eq(member_data)
2562
+ expect(local_member.client_id).to eq(client_one.auth.client_id)
2563
+ local_members_sent = true
2564
+ end
2565
+ end
2545
2566
  end
2546
2567
 
2547
- # Local members re-entered automatically appear as updates due to the
2548
- # fabricated ATTACHED message sent and the members already being present
2549
- presence_client_one.subscribe(:update) do |message|
2550
- expect(local_member_leave_event_fired).to be_truthy
2568
+ presence_client_one.subscribe(:leave) do |message|
2569
+ # Member will leave the PresenceMap due to the ATTACHED without Presence
2551
2570
  expect(message.data).to eq(member_data)
2552
2571
  expect(message.client_id).to eq(client_one.auth.client_id)
2553
- EventMachine.next_tick do
2554
- expect(presence_client_one.members.length).to eql(1)
2555
- expect(presence_client_one.members.local_members.length).to eql(1)
2556
- expect(in_sync_confirmed_no_local_members).to be_truthy
2557
- stop_reactor
2558
- end
2572
+ member_leave_event_fired = true
2573
+ end
2574
+
2575
+ # Shouldn't receive enter/update message when local_members are entered
2576
+ # This is due to the fact that, when we enter local member we also send
2577
+ # member id, and server automatically checks for duplicate id and doesn't
2578
+ # send presenceEnter or presenceUpdate if id is found.
2579
+ presence_client_one.subscribe(:enter, :update) do |message|
2580
+ raise { "client shouldn't receive update event for entered local members" }
2559
2581
  end
2560
2582
 
2561
- presence_client_one.members.once(:in_sync) do
2562
- # Immediately after SYNC (no sync actually occurred, but this event fires immediately after a channel SYNCs or is not expecting to SYNC)
2583
+ presence_client_one.members.once(:sync_complete) do
2563
2584
  expect(presence_client_one.members.length).to eql(0)
2564
- expect(presence_client_one.members.local_members.length).to eql(0)
2565
- in_sync_confirmed_no_local_members = true
2585
+
2586
+ # Since, this is a client sent event, local_members are not cleared
2587
+ # local_members acts a source of truth for server and not vice versa
2588
+ expect(presence_client_one.members.local_members.length).to eql(1)
2589
+
2590
+ wait_until(lambda { member_leave_event_fired and local_members_sent}) do
2591
+ stop_reactor
2592
+ end
2566
2593
  end
2567
2594
 
2568
2595
  # ATTACHED ProtocolMessage with no presence flag will clear the presence set immediately, #RTP19a
@@ -2572,6 +2599,8 @@ describe Ably::Realtime::Presence, :event_machine do
2572
2599
  flags: 0 # no resume or presence flag
2573
2600
  )
2574
2601
  end
2602
+
2603
+ presence_client_one.enter(member_data)
2575
2604
  end
2576
2605
  end
2577
2606
  end
@@ -2684,23 +2713,25 @@ describe Ably::Realtime::Presence, :event_machine do
2684
2713
 
2685
2714
  context 'channel transitions to the DETACHED state' do
2686
2715
  it 'clears the PresenceMap and local member map copy and does not emit any presence events (#RTP5a)' do
2687
- presence_client_one.enter
2688
- presence_client_one.subscribe(:enter) do
2689
- presence_client_one.unsubscribe :enter
2716
+ wait_until(lambda { client_one.connection.state == :connected and anonymous_client.connection.state == :connected }) do
2717
+ presence_client_one.enter
2718
+ presence_client_one.subscribe(:enter) do
2719
+ presence_client_one.unsubscribe :enter
2690
2720
 
2691
- channel_anonymous_client.attach do
2692
- presence_anonymous_client.get do |members|
2693
- expect(members.count).to eq(1)
2721
+ channel_anonymous_client.attach do
2722
+ presence_anonymous_client.get do |members|
2723
+ expect(members.count).to eq(1)
2694
2724
 
2695
- presence_anonymous_client.subscribe { raise 'No presence events should be emitted' }
2696
- channel_anonymous_client.detach do
2697
- expect(presence_anonymous_client.members.length).to eq(0)
2698
- expect(channel_anonymous_client).to be_detached
2725
+ presence_anonymous_client.subscribe { raise 'No presence events should be emitted' }
2726
+ channel_anonymous_client.detach do
2727
+ expect(presence_anonymous_client.members.length).to eq(0)
2728
+ expect(channel_anonymous_client).to be_detached
2699
2729
 
2700
- expect(presence_client_one.members.local_members.count).to eq(1)
2701
- channel_client_one.detach do
2702
- expect(presence_client_one.members.local_members.count).to eq(0)
2703
- stop_reactor
2730
+ expect(presence_client_one.members.local_members.count).to eq(1)
2731
+ channel_client_one.detach do
2732
+ expect(presence_client_one.members.local_members.count).to eq(0)
2733
+ stop_reactor
2734
+ end
2704
2735
  end
2705
2736
  end
2706
2737
  end
@@ -191,7 +191,7 @@ describe Ably::Rest::Channel do
191
191
  let(:client_options) { default_options.merge(use_token_auth: true, default_token_params: { capability: capability }) }
192
192
 
193
193
  it 'raises a permission error when publishing' do
194
- expect { channel.publish(name, data) }.to raise_error(Ably::Exceptions::UnauthorizedRequest, /not permitted/)
194
+ expect { channel.publish(name, data) }.to raise_error(Ably::Exceptions::UnauthorizedRequest, /40160/)
195
195
  end
196
196
  end
197
197
 
@@ -280,7 +280,7 @@ describe Ably::Rest::Channel do
280
280
 
281
281
  context 'with an invalid client_id in the message' do
282
282
  it 'succeeds in the client library but then fails when published to Ably' do
283
- expect { channel.publish([name: 'event', client_id: 'invalid']) }.to raise_error Ably::Exceptions::InvalidRequest, /mismatched clientId/
283
+ expect { channel.publish([name: 'event', client_id: 'invalid']) }.to raise_error(Ably::Exceptions::InvalidRequest, /40012/)
284
284
  end
285
285
  end
286
286
 
@@ -1095,9 +1095,16 @@ describe Ably::Rest::Client do
1095
1095
  end
1096
1096
 
1097
1097
  it 'sends a protocol version and lib version header (#G4, #RSC7a, #RSC7b)' do
1098
- client.channels.get('foo').publish("event")
1098
+ response = client.channels.get('foo').publish("event")
1099
+ expect(response).to eql true
1099
1100
  expect(publish_message_stub).to have_been_requested
1100
- expect(Ably::PROTOCOL_VERSION).to eql('1.2')
1101
+ if agent.nil?
1102
+ expect(publish_message_stub.to_s).to include("'Ably-Agent'=>'#{Ably::AGENT}'")
1103
+ expect(publish_message_stub.to_s).to include("'X-Ably-Version'=>'2'")
1104
+ else
1105
+ expect(publish_message_stub.to_s).to include("'Ably-Agent'=>'ably-ruby/1.1.1 ruby/3.1.1'")
1106
+ expect(publish_message_stub.to_s).to include("'X-Ably-Version'=>'2'")
1107
+ end
1101
1108
  end
1102
1109
  end
1103
1110
  end
@@ -204,20 +204,17 @@ describe Ably::Rest::Channel, 'messages' do
204
204
  end
205
205
  end
206
206
 
207
- specify 'idempotent publishing is disabled by default with <= 1.1 (#TO3n)' do
208
- stub_const 'Ably::PROTOCOL_VERSION', '1.0'
209
- client = Ably::Rest::Client.new(key: api_key, protocol: protocol)
210
- expect(client.idempotent_rest_publishing).to be_falsey
211
- stub_const 'Ably::PROTOCOL_VERSION', '1.1'
212
- client = Ably::Rest::Client.new(key: api_key, protocol: protocol)
207
+ specify 'idempotent publishing is set as per clientOptions' do
208
+ # set idempotent_rest_publishing to false
209
+ client = Ably::Rest::Client.new(key: api_key, protocol: protocol, idempotent_rest_publishing: false)
213
210
  expect(client.idempotent_rest_publishing).to be_falsey
214
- end
215
211
 
216
- specify 'idempotent publishing is enabled by default with >= 1.2 (#TO3n)' do
217
- stub_const 'Ably::PROTOCOL_VERSION', '1.2'
218
- client = Ably::Rest::Client.new(key: api_key, protocol: protocol)
212
+ # set idempotent_rest_publishing to true
213
+ client = Ably::Rest::Client.new(key: api_key, protocol: protocol, idempotent_rest_publishing: true)
219
214
  expect(client.idempotent_rest_publishing).to be_truthy
220
- stub_const 'Ably::PROTOCOL_VERSION', '1.3'
215
+ end
216
+
217
+ specify 'idempotent publishing is enabled by default (#TO3n)' do
221
218
  client = Ably::Rest::Client.new(key: api_key, protocol: protocol)
222
219
  expect(client.idempotent_rest_publishing).to be_truthy
223
220
  end
@@ -181,7 +181,7 @@ describe Ably::Rest::Push::Admin do
181
181
  client_id: client_id,
182
182
  push: {
183
183
  recipient: {
184
- transport_type: 'gcm',
184
+ transport_type: 'fcm',
185
185
  registration_token: 'secret_token',
186
186
  }
187
187
  }
@@ -250,7 +250,7 @@ describe Ably::Rest::Push::Admin do
250
250
  client_id: client_id,
251
251
  push: {
252
252
  recipient: {
253
- transport_type: 'gcm',
253
+ transport_type: 'fcm',
254
254
  registration_token: 'secret_token',
255
255
  }
256
256
  }
@@ -268,7 +268,7 @@ describe Ably::Rest::Push::Admin do
268
268
  expect(device).to be_a(Ably::Models::DeviceDetails)
269
269
  expect(device.platform).to eql('ios')
270
270
  expect(device.client_id).to eql(client_id)
271
- expect(device.push.recipient.fetch(:transport_type)).to eql('gcm')
271
+ expect(device.push.recipient.fetch(:transport_type)).to eql('fcm')
272
272
  end
273
273
 
274
274
  it 'returns a DeviceDetails object if a DeviceDetails object is provided' do
@@ -276,7 +276,7 @@ describe Ably::Rest::Push::Admin do
276
276
  expect(device).to be_a(Ably::Models::DeviceDetails)
277
277
  expect(device.platform).to eql('ios')
278
278
  expect(device.client_id).to eql(client_id)
279
- expect(device.push.recipient.fetch(:transport_type)).to eql('gcm')
279
+ expect(device.push.recipient.fetch(:transport_type)).to eql('fcm')
280
280
  end
281
281
 
282
282
  it 'raises a ResourceMissing exception if device ID does not exist' do
@@ -350,14 +350,14 @@ describe Ably::Rest::Push::Admin do
350
350
  expect(device_retrieved.push.recipient['foo_bar']).to eql('string')
351
351
  end
352
352
 
353
- context 'with GCM target' do
353
+ context 'with FCM target' do
354
354
  let(:device_token) { random_str }
355
355
 
356
356
  it 'saves the associated DevicePushDetails' do
357
357
  subject.save(device_details.merge(
358
358
  push: {
359
359
  recipient: {
360
- transport_type: 'gcm',
360
+ transport_type: 'fcm',
361
361
  registrationToken: device_token
362
362
  }
363
363
  }
@@ -365,14 +365,15 @@ describe Ably::Rest::Push::Admin do
365
365
 
366
366
  device_retrieved = subject.get(device_details.fetch(:id))
367
367
 
368
- expect(device_retrieved.push.recipient.fetch('transportType')).to eql('gcm')
368
+ expect(device_retrieved.push.recipient.fetch('transportType')).to eql('fcm')
369
369
  expect(device_retrieved.push.recipient[:registration_token]).to eql(device_token)
370
370
  end
371
371
  end
372
372
 
373
373
  context 'with web target' do
374
374
  let(:target_url) { 'http://foo.com/bar' }
375
- let(:encryption_key) { random_str }
375
+ let(:p256dh) { random_str }
376
+ let(:auth) { random_str }
376
377
 
377
378
  it 'saves the associated DevicePushDetails' do
378
379
  subject.save(device_details.merge(
@@ -380,7 +381,10 @@ describe Ably::Rest::Push::Admin do
380
381
  recipient: {
381
382
  transport_type: 'web',
382
383
  targetUrl: target_url,
383
- encryptionKey: encryption_key
384
+ encryptionKey: {
385
+ p256dh: p256dh,
386
+ auth: auth
387
+ }
384
388
  }
385
389
  }
386
390
  ))
@@ -389,7 +393,8 @@ describe Ably::Rest::Push::Admin do
389
393
 
390
394
  expect(device_retrieved.push.recipient[:transport_type]).to eql('web')
391
395
  expect(device_retrieved.push.recipient['targetUrl']).to eql(target_url)
392
- expect(device_retrieved.push.recipient['encryptionKey']).to eql(encryption_key)
396
+ expect(device_retrieved.push.recipient['encryptionKey']['p256dh']).to eql(p256dh)
397
+ expect(device_retrieved.push.recipient['encryptionKey']['auth']).to eql(auth)
393
398
  end
394
399
  end
395
400
 
@@ -462,7 +467,7 @@ describe Ably::Rest::Push::Admin do
462
467
  client_id: client_id,
463
468
  push: {
464
469
  recipient: {
465
- transport_type: 'gcm',
470
+ transport_type: 'fcm',
466
471
  registrationToken: 'secret_token',
467
472
  }
468
473
  }
@@ -476,7 +481,7 @@ describe Ably::Rest::Push::Admin do
476
481
  client_id: client_id,
477
482
  push: {
478
483
  recipient: {
479
- transport_type: 'gcm',
484
+ transport_type: 'fcm',
480
485
  registration_token: 'secret_token',
481
486
  }
482
487
  }
@@ -525,7 +530,7 @@ describe Ably::Rest::Push::Admin do
525
530
  client_id: client_id,
526
531
  push: {
527
532
  recipient: {
528
- transport_type: 'gcm',
533
+ transport_type: 'fcm',
529
534
  registration_token: 'secret_token',
530
535
  }
531
536
  }
@@ -539,7 +544,7 @@ describe Ably::Rest::Push::Admin do
539
544
  client_id: client_id,
540
545
  push: {
541
546
  recipient: {
542
- transport_type: 'gcm',
547
+ transport_type: 'fcm',
543
548
  registration_token: 'secret_token',
544
549
  }
545
550
  }
@@ -580,7 +585,7 @@ describe Ably::Rest::Push::Admin do
580
585
  client_id: client_id,
581
586
  push: {
582
587
  recipient: {
583
- transport_type: 'gcm',
588
+ transport_type: 'fcm',
584
589
  registration_token: 'secret_token',
585
590
  }
586
591
  }
@@ -130,7 +130,7 @@ shared_examples 'a client initializer' do
130
130
  end
131
131
 
132
132
  context 'with token' do
133
- let(:client_options) { { token: 'token', auth_connect: false } }
133
+ let(:client_options) { { token: 'token', auto_connect: false } }
134
134
 
135
135
  it 'sets the token' do
136
136
  expect(subject.auth.current_token_details.token).to eql('token')
@@ -83,7 +83,7 @@ module Ably
83
83
  attr_reader :output, :indent
84
84
 
85
85
  def documenting_rest_only?
86
- File.exists?(File.expand_path('../../../../../../ably-rest.gemspec', __FILE__))
86
+ File.exist?(File.expand_path('../../../../../../ably-rest.gemspec', __FILE__))
87
87
  end
88
88
 
89
89
  def example_name_and_link(notification)