ably-rest 0.9.3 → 1.0.0

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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/ably-rest.gemspec +2 -1
  3. data/lib/submodules/ably-ruby/.travis.yml +6 -4
  4. data/lib/submodules/ably-ruby/CHANGELOG.md +52 -61
  5. data/lib/submodules/ably-ruby/README.md +10 -0
  6. data/lib/submodules/ably-ruby/SPEC.md +1473 -852
  7. data/lib/submodules/ably-ruby/ably.gemspec +2 -1
  8. data/lib/submodules/ably-ruby/lib/ably/auth.rb +57 -25
  9. data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +34 -8
  10. data/lib/submodules/ably-ruby/lib/ably/logger.rb +10 -1
  11. data/lib/submodules/ably-ruby/lib/ably/models/auth_details.rb +42 -0
  12. data/lib/submodules/ably-ruby/lib/ably/models/channel_state_change.rb +18 -4
  13. data/lib/submodules/ably-ruby/lib/ably/models/connection_details.rb +6 -3
  14. data/lib/submodules/ably-ruby/lib/ably/models/connection_state_change.rb +4 -3
  15. data/lib/submodules/ably-ruby/lib/ably/models/error_info.rb +1 -1
  16. data/lib/submodules/ably-ruby/lib/ably/models/message.rb +12 -1
  17. data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/base.rb +101 -97
  18. data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +13 -1
  19. data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +20 -3
  20. data/lib/submodules/ably-ruby/lib/ably/modules/async_wrapper.rb +7 -3
  21. data/lib/submodules/ably-ruby/lib/ably/modules/enum.rb +17 -7
  22. data/lib/submodules/ably-ruby/lib/ably/modules/event_emitter.rb +29 -14
  23. data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +7 -4
  24. data/lib/submodules/ably-ruby/lib/ably/modules/state_machine.rb +2 -4
  25. data/lib/submodules/ably-ruby/lib/ably/modules/uses_state_machine.rb +7 -3
  26. data/lib/submodules/ably-ruby/lib/ably/realtime.rb +2 -0
  27. data/lib/submodules/ably-ruby/lib/ably/realtime/auth.rb +79 -31
  28. data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +62 -26
  29. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +154 -65
  30. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_state_machine.rb +14 -15
  31. data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +16 -3
  32. data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +38 -29
  33. data/lib/submodules/ably-ruby/lib/ably/realtime/client/outgoing_message_dispatcher.rb +6 -1
  34. data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +108 -49
  35. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +165 -59
  36. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_state_machine.rb +22 -3
  37. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/websocket_transport.rb +19 -10
  38. data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +67 -45
  39. data/lib/submodules/ably-ruby/lib/ably/realtime/presence/members_map.rb +198 -36
  40. data/lib/submodules/ably-ruby/lib/ably/realtime/presence/presence_manager.rb +30 -6
  41. data/lib/submodules/ably-ruby/lib/ably/realtime/presence/presence_state_machine.rb +5 -12
  42. data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +3 -3
  43. data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +21 -8
  44. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/exceptions.rb +1 -3
  45. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/logger.rb +2 -2
  46. data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +1 -1
  47. data/lib/submodules/ably-ruby/lib/ably/util/pub_sub.rb +1 -1
  48. data/lib/submodules/ably-ruby/lib/ably/util/safe_deferrable.rb +26 -0
  49. data/lib/submodules/ably-ruby/lib/ably/version.rb +2 -2
  50. data/lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb +416 -99
  51. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +5 -3
  52. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +1011 -160
  53. data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +2 -2
  54. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +458 -27
  55. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +436 -97
  56. data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +52 -23
  57. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +5 -3
  58. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +1160 -105
  59. data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +151 -22
  60. data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +1 -1
  61. data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +88 -27
  62. data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +42 -15
  63. data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +4 -4
  64. data/lib/submodules/ably-ruby/spec/rspec_config.rb +2 -1
  65. data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +2 -2
  66. data/lib/submodules/ably-ruby/spec/shared/safe_deferrable_behaviour.rb +6 -2
  67. data/lib/submodules/ably-ruby/spec/support/debug_failure_helper.rb +20 -4
  68. data/lib/submodules/ably-ruby/spec/support/event_machine_helper.rb +32 -1
  69. data/lib/submodules/ably-ruby/spec/unit/auth_spec.rb +4 -11
  70. data/lib/submodules/ably-ruby/spec/unit/logger_spec.rb +28 -2
  71. data/lib/submodules/ably-ruby/spec/unit/models/auth_details_spec.rb +49 -0
  72. data/lib/submodules/ably-ruby/spec/unit/models/channel_state_change_spec.rb +23 -3
  73. data/lib/submodules/ably-ruby/spec/unit/models/connection_details_spec.rb +12 -1
  74. data/lib/submodules/ably-ruby/spec/unit/models/connection_state_change_spec.rb +15 -4
  75. data/lib/submodules/ably-ruby/spec/unit/models/message_spec.rb +34 -2
  76. data/lib/submodules/ably-ruby/spec/unit/models/presence_message_spec.rb +73 -2
  77. data/lib/submodules/ably-ruby/spec/unit/models/protocol_message_spec.rb +64 -6
  78. data/lib/submodules/ably-ruby/spec/unit/models/token_details_spec.rb +1 -1
  79. data/lib/submodules/ably-ruby/spec/unit/models/token_request_spec.rb +1 -1
  80. data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +2 -1
  81. data/lib/submodules/ably-ruby/spec/unit/modules/enum_spec.rb +69 -0
  82. data/lib/submodules/ably-ruby/spec/unit/modules/event_emitter_spec.rb +149 -22
  83. data/lib/submodules/ably-ruby/spec/unit/modules/state_emitter_spec.rb +9 -3
  84. data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +1 -1
  85. data/lib/submodules/ably-ruby/spec/unit/realtime/connection_spec.rb +8 -5
  86. data/lib/submodules/ably-ruby/spec/unit/realtime/incoming_message_dispatcher_spec.rb +1 -1
  87. data/lib/submodules/ably-ruby/spec/unit/realtime/presence_spec.rb +4 -3
  88. data/lib/submodules/ably-ruby/spec/unit/rest/client_spec.rb +1 -1
  89. data/lib/submodules/ably-ruby/spec/unit/util/crypto_spec.rb +3 -3
  90. metadata +7 -5
@@ -28,6 +28,25 @@ module Ably::Realtime
28
28
  presence.members.change_state :sync_starting
29
29
  end
30
30
 
31
+ # Process presence messages from SYNC messages. Sync can be server-initiated or triggered following ATTACH
32
+ #
33
+ # @return [void]
34
+ #
35
+ # @api private
36
+ def sync_process_messages(serial, presence_messages)
37
+ unless presence.members.sync_starting?
38
+ presence.members.change_state :sync_starting
39
+ end
40
+
41
+ presence.members.update_sync_serial serial
42
+
43
+ presence_messages.each do |presence_message|
44
+ presence.__incoming_msgbus__.publish :sync, presence_message
45
+ end
46
+
47
+ presence.members.change_state :finalizing_sync if presence.members.sync_serial_cursor_at_end?
48
+ end
49
+
31
50
  # There server has indicated that there are no SYNC ProtocolMessages to come because
32
51
  # there are no members on this channel
33
52
  #
@@ -35,7 +54,8 @@ module Ably::Realtime
35
54
  #
36
55
  # @api private
37
56
  def sync_not_expected
38
- presence.members.change_state :in_sync
57
+ logger.debug { "#{self.class.name}: Emitting leave events for all members as a SYNC is not expected and thus there are no members on the channel" }
58
+ presence.members.change_state :sync_none
39
59
  end
40
60
 
41
61
  private
@@ -43,16 +63,20 @@ module Ably::Realtime
43
63
 
44
64
  def setup_channel_event_handlers
45
65
  channel.unsafe_on(:detached) do
46
- presence.transition_state_machine :left if presence.can_transition_to?(:left)
66
+ if !presence.initialized?
67
+ presence.transition_state_machine :left if presence.can_transition_to?(:left)
68
+ end
47
69
  end
48
70
 
49
71
  channel.unsafe_on(:failed) do |metadata|
50
- presence.transition_state_machine :failed, metadata if presence.can_transition_to?(:failed)
72
+ if !presence.initialized?
73
+ presence.transition_state_machine :left, metadata if presence.can_transition_to?(:left)
74
+ end
51
75
  end
76
+ end
52
77
 
53
- presence.unsafe_on(:entered) do |message|
54
- presence.set_connection_id message.connection_id
55
- end
78
+ def logger
79
+ presence.channel.client.logger
56
80
  end
57
81
  end
58
82
  end
@@ -15,19 +15,16 @@ module Ably::Realtime
15
15
  # :entered
16
16
  # :leaving
17
17
  # :left
18
- # :failed
19
18
  Presence::STATE.each_with_index do |state_enum, index|
20
19
  state state_enum.to_sym, initial: index == 0
21
20
  end
22
21
 
23
22
  # Entering or entered states can skip leaving and go straight to left if a channel is detached
24
23
  # A channel that detaches very quickly will also go straight to :left from :initialized
25
- # Failed states only occur when present and the channel fails or presence fails
26
24
  transition :from => :initialized, :to => [:entering, :left]
27
- transition :from => :entering, :to => [:entered, :leaving, :left, :failed]
28
- transition :from => :entered, :to => [:leaving, :left, :failed]
29
- transition :from => :leaving, :to => [:left, :entering, :failed]
30
- transition :from => :failed, :to => [:entering]
25
+ transition :from => :entering, :to => [:entered, :leaving, :left]
26
+ transition :from => :entered, :to => [:leaving, :left]
27
+ transition :from => :leaving, :to => [:left, :entering]
31
28
 
32
29
  after_transition do |presence, transition|
33
30
  presence.synchronize_state_with_statemachine
@@ -41,13 +38,9 @@ module Ably::Realtime
41
38
  presence.manager.leave current_transition.metadata
42
39
  end
43
40
 
44
- after_transition(to: [:failed]) do |presence, current_transition|
45
- presence.manager.emit_error current_transition.metadata
46
- end
47
-
48
41
  # Transitions responsible for updating channel#error_reason
49
- before_transition(to: [:left, :failed]) do |presence, current_transition|
50
- presence.channel.set_failed_channel_error_reason current_transition.metadata if is_error_type?(current_transition.metadata)
42
+ before_transition(to: [:left]) do |presence, current_transition|
43
+ presence.channel.set_channel_error_reason current_transition.metadata if is_error_type?(current_transition.metadata)
51
44
  end
52
45
 
53
46
  private
@@ -69,10 +69,10 @@ module Ably
69
69
 
70
70
  next if msg.client_id.nil?
71
71
  if msg.client_id == '*'
72
- raise Ably::Exceptions::IncompatibleClientId.new('Wildcard client_id is reserved and cannot be used when publishing messages', 400, 40012)
72
+ raise Ably::Exceptions::IncompatibleClientId.new('Wildcard client_id is reserved and cannot be used when publishing messages')
73
73
  end
74
74
  unless client.auth.can_assume_client_id?(msg.client_id)
75
- raise Ably::Exceptions::IncompatibleClientId.new("Cannot publish with client_id '#{msg.client_id}' as it is incompatible with the current configured client_id '#{client.client_id}'", 400, 40012)
75
+ raise Ably::Exceptions::IncompatibleClientId.new("Cannot publish with client_id '#{msg.client_id}' as it is incompatible with the current configured client_id '#{client.client_id}'")
76
76
  end
77
77
  end.as_json
78
78
  end
@@ -136,7 +136,7 @@ module Ably
136
136
  def decode_message(message)
137
137
  message.decode client.encoders, options
138
138
  rescue Ably::Exceptions::CipherError, Ably::Exceptions::EncoderError => e
139
- client.logger.error "Decoding Error on channel '#{name}', message event name '#{message.name}'. #{e.class.name}: #{e.message}"
139
+ client.logger.error { "Decoding Error on channel '#{name}', message event name '#{message.name}'. #{e.class.name}: #{e.message}" }
140
140
  end
141
141
  end
142
142
  end
@@ -25,8 +25,8 @@ module Ably
25
25
  # Configuration for HTTP timeouts and HTTP request reattempts to fallback hosts
26
26
  HTTP_DEFAULTS = {
27
27
  open_timeout: 4,
28
- request_timeout: 15,
29
- max_retry_duration: 10,
28
+ request_timeout: 10,
29
+ max_retry_duration: 15,
30
30
  max_retry_count: 3
31
31
  }.freeze
32
32
 
@@ -104,12 +104,12 @@ module Ably
104
104
  # @option options [Proc] :auth_callback when provided, the Proc will be called with the token params hash as the first argument, whenever a new token is required.
105
105
  # The Proc should return a token string, {Ably::Models::TokenDetails} or JSON equivalent, {Ably::Models::TokenRequest} or JSON equivalent
106
106
  # @option options [Boolean] :query_time when true will query the {https://www.ably.io Ably} system for the current time instead of using the local time
107
- # @option options [Hash] :token_params convenience to pass in +token_params+ that will be used as a default for all token requests. See {Auth#create_token_request}
107
+ # @option options [Hash] :default_token_params convenience to pass in +token_params+ that will be used as a default for all token requests. See {Auth#create_token_request}
108
108
  #
109
109
  # @option options [Integer] :http_open_timeout (4 seconds) timeout in seconds for opening an HTTP connection for all HTTP requests
110
- # @option options [Integer] :http_request_timeout (15 seconds) timeout in seconds for any single complete HTTP request and response
110
+ # @option options [Integer] :http_request_timeout (10 seconds) timeout in seconds for any single complete HTTP request and response
111
111
  # @option options [Integer] :http_max_retry_count (3) maximum number of fallback host retries for HTTP requests that fail due to network issues or server problems
112
- # @option options [Integer] :http_max_retry_duration (10 seconds) maximum elapsed time in which fallback host retries for HTTP requests will be attempted i.e. if the first default host attempt takes 5s, and then the subsequent fallback retry attempt takes 7s, no further fallback host attempts will be made as the total elapsed time of 12s exceeds the default 10s limit
112
+ # @option options [Integer] :http_max_retry_duration (15 seconds) maximum elapsed time in which fallback host retries for HTTP requests will be attempted i.e. if the first default host attempt takes 5s, and then the subsequent fallback retry attempt takes 7s, no further fallback host attempts will be made as the total elapsed time of 12s exceeds the default 10s limit
113
113
  #
114
114
  # @option options [Boolean] :fallback_hosts_use_default (false) When true, forces the user of fallback hosts even if a non-default production endpoint is being used
115
115
  # @option options [Array<String>] :fallback_hosts When an array of fallback hosts are provided, these fallback hosts are always used if a request fails to the primary endpoint. If an empty array is provided, the fallback host functionality is disabled
@@ -128,13 +128,14 @@ module Ably
128
128
 
129
129
  options = options.clone
130
130
  if options.kind_of?(String)
131
- options = if options.match(/^[\w-]{2,}\.[\w-]{2,}:[\w-]{2,}$/)
131
+ options = if options.match(Auth::API_KEY_REGEX)
132
132
  { key: options }
133
133
  else
134
134
  { token: options }
135
135
  end
136
136
  end
137
137
 
138
+ @realtime_client = options.delete(:realtime_client)
138
139
  @tls = options.delete(:tls) == false ? false : true
139
140
  @environment = options.delete(:environment) # nil is production
140
141
  @environment = nil if [:production, 'production'].include?(@environment)
@@ -183,7 +184,7 @@ module Ably
183
184
  end
184
185
  raise ArgumentError, 'Protocol is invalid. Must be either :msgpack or :json' unless [:msgpack, :json].include?(@protocol)
185
186
 
186
- token_params = options.delete(:token_params) || {}
187
+ token_params = options.delete(:default_token_params) || {}
187
188
  @options = options
188
189
  @auth = Auth.new(self, token_params, options)
189
190
  @channels = Ably::Rest::Channels.new(self)
@@ -402,6 +403,18 @@ module Ably
402
403
  ].compact.join('-')
403
404
  end
404
405
 
406
+ # Allowable duration for an external auth request
407
+ # For REST client this defaults to request_timeout
408
+ # For Realtime clients this defaults to realtime_request_timeout
409
+ # @api private
410
+ def auth_request_timeout
411
+ if @realtime_client
412
+ @realtime_client.connection.defaults.fetch(:realtime_request_timeout)
413
+ else
414
+ http_defaults.fetch(:request_timeout)
415
+ end
416
+ end
417
+
405
418
  private
406
419
  def raw_request(method, path, params = {}, options = {})
407
420
  options = options.clone
@@ -440,7 +453,7 @@ module Ably
440
453
  time_passed = Time.now - requested_at
441
454
  if can_fallback_to_alternate_ably_host? && retry_count < max_retry_count && time_passed <= max_retry_duration
442
455
  retry_count += 1
443
- logger.warn "Ably::Rest::Client - Retry #{retry_count} for #{method} #{path} #{params} as initial attempt failed: #{error}"
456
+ logger.warn { "Ably::Rest::Client - Retry #{retry_count} for #{method} #{path} #{params} as initial attempt failed: #{error}" }
444
457
  retry
445
458
  end
446
459
 
@@ -7,8 +7,6 @@ module Ably
7
7
  # HTTP exceptions raised by Ably due to an error status code
8
8
  # Ably returns JSON/Msgpack error codes and messages so include this if possible in the exception messages
9
9
  class Exceptions < Faraday::Response::Middleware
10
- TOKEN_EXPIRED_CODE = 40140..40149
11
-
12
10
  def on_complete(env)
13
11
  if env.status >= 400
14
12
  error_status_code = env.status
@@ -35,7 +33,7 @@ module Ably
35
33
  if env.status >= 500
36
34
  raise Ably::Exceptions::ServerError.new(*exception_args)
37
35
  elsif env.status == 401
38
- if TOKEN_EXPIRED_CODE.include?(error_code)
36
+ if Ably::Exceptions::TOKEN_EXPIRED_CODE.include?(error_code)
39
37
  raise Ably::Exceptions::TokenExpired.new(*exception_args)
40
38
  else
41
39
  raise Ably::Exceptions::UnauthorizedRequest.new(*exception_args)
@@ -17,8 +17,8 @@ module Ably
17
17
  def_delegators :@logger, :debug, :info, :warn, :error, :fatal
18
18
 
19
19
  def call(env)
20
- debug "=> URL: #{env.method} #{env.url}, Headers: #{dump_headers env.request_headers}"
21
- debug "=> Body: #{body_for(env)}"
20
+ debug { "=> URL: #{env.method} #{env.url}, Headers: #{dump_headers env.request_headers}" }
21
+ debug { "=> Body: #{body_for(env)}" }
22
22
  super
23
23
  end
24
24
 
@@ -90,7 +90,7 @@ module Ably
90
90
  def decode_message(presence_message)
91
91
  presence_message.decode client.encoders, channel.options
92
92
  rescue Ably::Exceptions::CipherError, Ably::Exceptions::EncoderError => e
93
- client.logger.error "Decoding Error on presence channel '#{channel.name}', presence message client_id '#{presence_message.client_id}'. #{e.class.name}: #{e.message}"
93
+ client.logger.error { "Decoding Error on presence channel '#{channel.name}', presence message client_id '#{presence_message.client_id}'. #{e.class.name}: #{e.message}" }
94
94
  end
95
95
  end
96
96
  end
@@ -38,7 +38,7 @@ module Ably::Util
38
38
 
39
39
  alias_method :subscribe, :unsafe_on
40
40
  alias_method :publish, :emit
41
- alias_method :unsubscribe, :off
41
+ alias_method :unsubscribe, :unsafe_off
42
42
  end
43
43
  end
44
44
  end
@@ -14,5 +14,31 @@ module Ably::Util
14
14
  def initialize(logger)
15
15
  @logger = logger
16
16
  end
17
+
18
+ # Create a new {SafeDeferrable} and fail immediately with the provided error in the next eventloop cycle
19
+ #
20
+ # @param error [Ably::Exceptions::BaseAblyException, Ably::Models::ErrorInfo] The error used to fail the newly created {SafeDeferrable}
21
+ #
22
+ # @return [SafeDeferrable]
23
+ #
24
+ def self.new_and_fail_immediately(logger, error)
25
+ new(logger).tap do |deferrable|
26
+ EventMachine.next_tick do
27
+ deferrable.fail error
28
+ end
29
+ end
30
+ end
31
+
32
+ # Create a new {SafeDeferrable} and succeed immediately with the provided arguments in the next eventloop cycle
33
+ #
34
+ # @return [SafeDeferrable]
35
+ #
36
+ def self.new_and_succeed_immediately(logger, *args)
37
+ new(logger).tap do |deferrable|
38
+ EventMachine.next_tick do
39
+ deferrable.succeed *args
40
+ end
41
+ end
42
+ end
17
43
  end
18
44
  end
@@ -1,6 +1,6 @@
1
1
  module Ably
2
- VERSION = '0.9.3'
3
- PROTOCOL_VERSION = '0.9'
2
+ VERSION = '1.0.0'
3
+ PROTOCOL_VERSION = '1.0'
4
4
 
5
5
  # Allow a variant to be configured for all instances of this client library
6
6
  # such as ruby-rest-[VERSION]
@@ -5,6 +5,14 @@ require 'spec_helper'
5
5
  # wrapper around the Ably::Auth object
6
6
  #
7
7
  describe Ably::Realtime::Auth, :event_machine do
8
+ def disconnect_transport(connection)
9
+ if connection.transport
10
+ connection.transport.close_connection_after_writing
11
+ else
12
+ EventMachine.next_tick { disconnect_transport connection }
13
+ end
14
+ end
15
+
8
16
  vary_by_protocol do
9
17
  let(:default_options) { { key: api_key, environment: environment, protocol: protocol } }
10
18
  let(:client_options) { default_options }
@@ -88,8 +96,9 @@ describe Ably::Realtime::Auth, :event_machine do
88
96
  end
89
97
 
90
98
  context '#options (auth_options)' do
99
+ let(:token_str) { auth.request_token_sync.token }
91
100
  let(:auth_url) { "https://echo.ably.io/?type=text" }
92
- let(:auth_params) { { :body => random_str } }
101
+ let(:auth_params) { { :body => token_str } }
93
102
  let(:client_options) { default_options.merge(auto_connect: false) }
94
103
 
95
104
  it 'contains the configured auth options' do
@@ -130,7 +139,7 @@ describe Ably::Realtime::Auth, :event_machine do
130
139
  end
131
140
  end
132
141
 
133
- context do
142
+ context 'methods' do
134
143
  let(:custom_ttl) { 33 }
135
144
  let(:custom_client_id) { random_str }
136
145
 
@@ -177,12 +186,16 @@ describe Ably::Realtime::Auth, :event_machine do
177
186
  end
178
187
 
179
188
  context '#authorize' do
180
- it 'returns a token asynchronously' do
181
- auth.authorize(ttl: custom_ttl, client_id: custom_client_id) do |token_details|
182
- expect(token_details).to be_a(Ably::Models::TokenDetails)
183
- expect(token_details.expires.to_i).to be_within(3).of(Time.now.to_i + custom_ttl)
184
- expect(token_details.client_id).to eql(custom_client_id)
185
- stop_reactor
189
+ context 'with token auth' do
190
+ let(:client_options) { default_options.merge(use_token_auth: true) }
191
+
192
+ it 'returns a token asynchronously' do
193
+ auth.authorize(ttl: custom_ttl, client_id: custom_client_id) do |token_details|
194
+ expect(token_details).to be_a(Ably::Models::TokenDetails)
195
+ expect(token_details.expires.to_i).to be_within(3).of(Time.now.to_i + custom_ttl)
196
+ expect(token_details.client_id).to eql(custom_client_id)
197
+ stop_reactor
198
+ end
186
199
  end
187
200
  end
188
201
 
@@ -223,10 +236,11 @@ describe Ably::Realtime::Auth, :event_machine do
223
236
  context 'and an incompatible client_id in a TokenDetails object passed to the auth callback' do
224
237
  let(:auth_token_object) { rest_auth_client.auth.request_token }
225
238
 
226
- it 'rejects a TokenDetails object with an incompatible client_id and raises an exception' do
239
+ it 'rejects a TokenDetails object with an incompatible client_id and fails with an exception' do
227
240
  client.connect
228
- client.connection.on(:error) do |error|
229
- expect(error).to be_a(Ably::Exceptions::IncompatibleClientId)
241
+ client.connection.on(:failed) do |state_change|
242
+ expect(state_change.reason).to be_a(Ably::Exceptions::AuthenticationFailed)
243
+ expect(state_change.reason.code).to eql(40012)
230
244
  EventMachine.add_timer(0.1) do
231
245
  expect(client.connection).to be_failed
232
246
  stop_reactor
@@ -235,13 +249,14 @@ describe Ably::Realtime::Auth, :event_machine do
235
249
  end
236
250
  end
237
251
 
238
- context 'and an incompatible client_id in a TokenRequest object passed to the auth callback and raises an exception' do
252
+ context 'and an incompatible client_id in a TokenRequest object passed to the auth callback and fails with an exception' do
239
253
  let(:auth_token_object) { rest_auth_client.auth.create_token_request }
240
254
 
241
- it 'rejects a TokenRequests object with an incompatible client_id and raises an exception' do
255
+ it 'rejects a TokenRequests object with an incompatible client_id and fails with an exception' do
242
256
  client.connect
243
- client.connection.on(:error) do |error|
244
- expect(error).to be_a(Ably::Exceptions::IncompatibleClientId)
257
+ client.connection.on(:failed) do |state_change|
258
+ expect(state_change.reason).to be_a(Ably::Exceptions::AuthenticationFailed)
259
+ expect(state_change.reason.code).to eql(40012)
245
260
  EventMachine.add_timer(0.1) do
246
261
  expect(client.connection).to be_failed
247
262
  stop_reactor
@@ -269,11 +284,12 @@ describe Ably::Realtime::Auth, :event_machine do
269
284
  let(:invalid_auth_token) { Ably::Rest::Client.new(default_options.merge(key: api_key, client_id: 'invalid')).auth.request_token }
270
285
 
271
286
  context 'and an incompatible client_id in a TokenDetails object passed to the auth callback' do
272
- it 'rejects a TokenDetails object with an incompatible client_id and raises an exception' do
287
+ it 'rejects a TokenDetails object with an incompatible client_id and fails with an exception' do
273
288
  client.connection.once(:connected) do
274
289
  client.auth.authorize({})
275
- client.connection.on(:error) do |error|
276
- expect(error).to be_a(Ably::Exceptions::IncompatibleClientId)
290
+ client.connection.on(:failed) do |state_change|
291
+ expect(state_change.reason).to be_a(Ably::Exceptions::IncompatibleClientId)
292
+ expect(state_change.reason.code).to eql(40012)
277
293
  EventMachine.add_timer(0.1) do
278
294
  expect(client.connection).to be_failed
279
295
  stop_reactor
@@ -304,29 +320,268 @@ describe Ably::Realtime::Auth, :event_machine do
304
320
  end }
305
321
 
306
322
  let(:client_options) { default_options.merge(auth_callback: basic_token_cb) }
323
+ let(:connection) { client.connection }
324
+
325
+ context 'when INITIALIZED' do
326
+ let(:client_options) { default_options.merge(auth_callback: basic_token_cb, auto_connect: false) }
327
+
328
+ it 'obtains a token and connects to Ably (#RTC8c, #RTC8b1)' do
329
+ has_connected = false
330
+ EventMachine.add_timer(0.2) do
331
+ expect(client.connection).to be_initialized
332
+ connection.once(:connected) do
333
+ expect(client.auth.client_id).to_not be_nil
334
+ has_connected = true
335
+ end
336
+ client.auth.authorize(nil, auth_callback: identified_token_cb) do |token|
337
+ expect(token.client_id).to eql('bob')
338
+ EventMachine.add_timer(0.25) do
339
+ expect(has_connected).to be_truthy
340
+ stop_reactor
341
+ end
342
+ end
343
+ end
344
+ end
345
+ end
307
346
 
308
- it 'forces the connection to disconnect and reconnect with a new token when in the CONNECTED state' do
309
- client.connection.once(:connected) do
310
- existing_token = client.auth.current_token_details
311
- client.auth.authorize(nil)
312
- client.connection.once(:disconnected) do
347
+ context 'when CONNECTING' do
348
+ let(:client_options) { default_options.merge(auth_callback: basic_token_cb) }
349
+
350
+ it 'aborts the current connection process, obtains a token, and connects to Ably again (#RTC8b)' do
351
+ connected_count = 0
352
+ connection.once(:connecting) do
353
+ connection.once(:connected) { connected_count += 1 }
354
+
355
+ client.auth.authorize(nil, auth_callback: identified_token_cb) do |token|
356
+ expect(token.client_id).to eql('bob')
357
+ EventMachine.add_timer(0.25) do
358
+ expect(connected_count).to eql(1)
359
+ stop_reactor
360
+ end
361
+ end
362
+ end
363
+ end
364
+ end
365
+
366
+ context 'when FAILED' do
367
+ let(:client_options) { default_options.merge(token: 'this.token:is.invalid', log_level: :none) }
368
+
369
+ it 'obtains a token and connects to Ably (#RTC8c, #RTC8b1)' do
370
+ has_connected = false
371
+ connection.once(:failed) do
313
372
  client.connection.once(:connected) do
314
- expect(existing_token).to_not eql(client.auth.current_token_details)
315
- stop_reactor
373
+ has_connected = true
374
+ end
375
+ client.auth.authorize(nil, auth_callback: basic_token_cb) do
376
+ EventMachine.add_timer(0.25) do
377
+ expect(has_connected).to be_truthy
378
+ stop_reactor
379
+ end
380
+ end
381
+ end
382
+ end
383
+ end
384
+
385
+ context 'when CLOSED' do
386
+ it 'obtains a token and connects to Ably (#RTC8c, #RTC8b1, #RTC8a3)' do
387
+ has_connected = false
388
+ connection.once(:connected) do
389
+ connection.once(:closed) do
390
+ client.connection.once(:connected) do
391
+ has_connected = true
392
+ end
393
+ client.auth.authorize(nil, auth_callback: basic_token_cb) do
394
+ EventMachine.add_timer(0.25) do
395
+ expect(has_connected).to be_truthy
396
+ stop_reactor
397
+ end
398
+ end
316
399
  end
400
+ connection.close
317
401
  end
318
402
  end
319
403
  end
320
404
 
321
- it 'forces the connection to disconnect and reconnect with a new token when in the CONNECTING state' do
322
- client.connection.once(:connecting) do
323
- existing_token = client.auth.current_token_details
324
- client.auth.authorize(nil)
325
- client.connection.once(:disconnected) do
405
+ context 'when in the CONNECTED state' do
406
+ context 'with a valid token in the AUTH ProtocolMessage sent' do
407
+ let(:client_options) { default_options.merge(use_token_auth: true) }
408
+
409
+ it 'obtains a new token (that upgrades from anonymous to identified) and upgrades the connection after receiving an updated CONNECTED ProtocolMessage (#RTC8a, #RTC8a3)' do
410
+ skip "This capability to upgrade from anonymous to identified is not yet implemented, see https://github.com/ably/wiki/issues/182"
411
+
326
412
  client.connection.once(:connected) do
327
- expect(existing_token).to_not eql(client.auth.current_token_details)
328
- stop_reactor
413
+ existing_token = client.auth.current_token_details
414
+ expect(client.connection.details.client_id).to be_nil
415
+ auth_sent = false
416
+ has_updated = false
417
+ new_connected_message_received = false
418
+
419
+ client.connection.once(:disconnected) { raise "Should not disconnnect during auth process"}
420
+ client.connection.once(:update) do
421
+ expect(auth_sent).to be_truthy
422
+ expect(new_connected_message_received).to be_truthy
423
+ expect(existing_token).to_not eql(client.auth.current_token_details)
424
+ expect(client.auth.client_id).to_not be_nil
425
+ expect(client.connection.details.client_id).to_not be_nil
426
+ has_updated = true
427
+ end
428
+
429
+ connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
430
+ auth_sent = true if protocol_message.action == :auth
431
+ end
432
+ connection.__incoming_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
433
+ new_connected_message_received = true if protocol_message.action == :connected
434
+ end
435
+
436
+ client.auth.authorize(nil, auth_callback: identified_token_cb) do
437
+ EventMachine.add_timer(0.25) do
438
+ expect(has_updated).to be_truthy
439
+ stop_reactor
440
+ end
441
+ end
442
+ end
443
+ end
444
+
445
+ it 'obtains a new token (as anonymous user before & after) and upgrades the connection after receiving an updated CONNECTED ProtocolMessage (#RTC8a, #RTC8a3)' do
446
+ client.connection.once(:connected) do
447
+ existing_token = client.auth.current_token_details
448
+ auth_sent = false
449
+ has_updated = false
450
+ new_connected_message_received = false
451
+
452
+ client.connection.once(:disconnected) { raise "Should not disconnnect during auth process"}
453
+ client.connection.once(:update) do
454
+ EventMachine.next_tick do
455
+ expect(auth_sent).to be_truthy
456
+ expect(new_connected_message_received).to be_truthy
457
+ expect(existing_token).to_not eql(client.auth.current_token_details)
458
+ has_updated = true
459
+ end
460
+ end
461
+
462
+ connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
463
+ auth_sent = true if protocol_message.action == :auth
464
+ end
465
+ connection.__incoming_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
466
+ new_connected_message_received = true if protocol_message.action == :connected
467
+ end
468
+
469
+ client.auth.authorize do
470
+ EventMachine.add_timer(0.25) do
471
+ expect(has_updated).to be_truthy
472
+ stop_reactor
473
+ end
474
+ end
475
+ end
476
+ end
477
+ end
478
+ end
479
+
480
+ context 'when DISCONNECTED' do
481
+ it 'obtains a token, upgrades from anonymous to identified, and connects to Ably immediately (#RTC8c, #RTC8b1)' do
482
+ skip "This capability to upgrade from anonymous to identified is not yet implemented, see https://github.com/ably/wiki/issues/182"
483
+
484
+ disconnected_waiting = false
485
+ has_connected = false
486
+
487
+ connection.once(:connected) do
488
+ expect(client.auth.client_id).to be_nil
489
+
490
+ connection.on(:disconnected) do |connection_state_change|
491
+ # Once we detect the connection will remain DISCONNECTED for 15s, then we can call authorize
492
+ # else we can't be sure authorize was responsible for the reconnect
493
+ if connection_state_change.retry_in > 1
494
+ disconnected_waiting = true
495
+
496
+ client.connection.once(:connected) do
497
+ expect(client.auth.client_id).to_not be_nil
498
+ has_connected = true
499
+ end
500
+
501
+ client.auth.authorize(nil, auth_callback: identified_token_cb) do
502
+ EventMachine.add_timer(0.25) do
503
+ expect(has_connected).to be_truthy
504
+ stop_reactor
505
+ end
506
+ end
507
+ end
508
+ end
509
+
510
+ connection.on(:connecting) do
511
+ disconnect_transport connection unless disconnected_waiting
512
+ end
513
+ disconnect_transport connection
514
+ end
515
+ end
516
+
517
+ it 'obtains a similar anonymous token and connects to Ably immediately (#RTC8c, #RTC8b1)' do
518
+ disconnected_waiting = false
519
+ has_connected = false
520
+
521
+ connection.once(:connected) do
522
+ expect(client.auth.client_id).to be_nil
523
+
524
+ connection.on(:disconnected) do |connection_state_change|
525
+ # Once we detect the connection will remain DISCONNECTED for 15s, then we can call authorize
526
+ # else we can't be sure authorize was responsible for the reconnect
527
+ if connection_state_change.retry_in > 1
528
+ disconnected_waiting = true
529
+
530
+ client.connection.once(:connected) do
531
+ expect(client.auth.client_id).to be_nil
532
+ has_connected = true
533
+ end
534
+
535
+ client.auth.authorize do
536
+ EventMachine.add_timer(0.25) do
537
+ expect(has_connected).to be_truthy
538
+ stop_reactor
539
+ end
540
+ end
541
+ end
542
+ end
543
+
544
+ connection.on(:connecting) do
545
+ disconnect_transport connection unless disconnected_waiting
546
+ end
547
+ disconnect_transport connection
548
+ end
549
+ end
550
+ end
551
+
552
+ context 'when SUSPENDED' do
553
+ let(:client_options) do
554
+ default_options.merge(
555
+ disconnected_retry_timeout: 0.1,
556
+ max_connection_state_ttl: 0.3,
557
+ use_token_auth: true
558
+ )
559
+ end
560
+
561
+ it 'obtains a token and connects to Ably immediately (#RTC8c, #RTC8b1)' do
562
+ been_suspended = false
563
+ has_connected = false
564
+
565
+ connection.once(:connected) do
566
+ connection.on(:suspended) do |connection_state_change|
567
+ been_suspended = true
568
+
569
+ client.connection.once(:connected) do
570
+ has_connected = true
571
+ end
572
+
573
+ client.auth.authorize do
574
+ EventMachine.add_timer(0.25) do
575
+ expect(has_connected).to be_truthy
576
+ stop_reactor
577
+ end
578
+ end
329
579
  end
580
+
581
+ connection.on(:connecting) do
582
+ disconnect_transport connection unless been_suspended
583
+ end
584
+ disconnect_transport connection
330
585
  end
331
586
  end
332
587
  end
@@ -338,44 +593,97 @@ describe Ably::Realtime::Auth, :event_machine do
338
593
  rest_client.auth.create_token_request({ client_id: 'mike', capability: basic_capability })
339
594
  end }
340
595
 
341
- it 'transisitions the connection state to FAILED if the client_id changes' do
596
+ it 'transitions the connection state to FAILED if the client_id changes (#RSA15c, #RTC8a2)' do
342
597
  client.connection.once(:connected) do
343
598
  client.auth.authorize(nil, auth_callback: identified_token_cb)
344
599
  client.connection.once(:failed) do
345
- expect(client.connection.error_reason.message).to match(/incompatible.*client ID/)
600
+ expect(client.connection.error_reason.message).to match(/incompatible.*clientId/i)
601
+ expect(client.connection.error_reason.code).to eql(40012)
346
602
  stop_reactor
347
603
  end
348
604
  end
349
605
  end
350
606
  end
351
607
 
608
+ context 'when auth fails' do
609
+ let(:client_options) { default_options.merge(auth_callback: basic_token_cb, log_level: :none) }
610
+
611
+ it 'transitions the connection state to the FAILED state (#RSA15c, #RTC8a2, #RTC8a3)' do
612
+ connection_failed = false
613
+
614
+ client.connection.once(:connected) do
615
+ client.auth.authorize(nil, auth_callback: Proc.new { 'invalid.token:will.cause.failure' }).tap do |deferrable|
616
+ deferrable.errback do |error|
617
+ EventMachine.add_timer(0.2) do
618
+ expect(connection_failed).to eql(true)
619
+ expect(error.message).to match(/Invalid accessToken/i)
620
+ expect(error.code).to eql(40005)
621
+ stop_reactor
622
+ end
623
+ end
624
+ deferrable.callback { raise "Authorize should not succed" }
625
+ end
626
+ end
627
+
628
+ client.connection.once(:failed) do
629
+ expect(client.connection.error_reason.message).to match(/Invalid accessToken/i)
630
+ expect(client.connection.error_reason.code).to eql(40005)
631
+ connection_failed = true
632
+ end
633
+ end
634
+ end
635
+
636
+ context 'when the authCallback fails' do
637
+ let(:client_options) { default_options.merge(auth_callback: basic_token_cb, log_level: :none) }
638
+
639
+ it 'calls the error callback of authorize and leaves the connection intact (#RSA4c3)' do
640
+ client.connection.once(:connected) do
641
+ client.auth.authorize(nil, auth_callback: Proc.new { raise 'Exception raised' }).errback do |error|
642
+ EventMachine.add_timer(0.2) do
643
+ expect(connection).to be_connected
644
+ expect(error.message).to match(/Exception raised/i)
645
+ stop_reactor
646
+ end
647
+ end
648
+ client.connection.once(:failed) do
649
+ raise "Connection should not fail"
650
+ end
651
+ end
652
+ end
653
+ end
654
+
352
655
  context 'when upgrading capabilities' do
353
656
  let(:client_options) { default_options.merge(auth_callback: basic_token_cb, log_level: :error) }
354
657
 
355
- it 'is allowed' do
658
+ it 'is allowed (#RTC8a1)' do
356
659
  client.connection.once(:connected) do
660
+ client.connection.once(:disconnected) { raise 'Upgrade does not require a disconnect' }
661
+
357
662
  channel = client.channels.get('foo')
358
663
  channel.publish('not-allowed').errback do |error|
359
664
  expect(error.code).to eql(40160)
360
665
  expect(error.message).to match(/permission denied/)
666
+
361
667
  client.auth.authorize(nil, auth_callback: upgraded_token_cb)
362
- client.connection.once(:connected) do
668
+ client.connection.once(:update) do
363
669
  expect(client.connection.error_reason).to be_nil
364
- channel.subscribe('allowed') do |message|
670
+ channel.subscribe('now-allowed') do |message|
365
671
  stop_reactor
366
672
  end
367
- channel.publish 'allowed'
673
+ channel.publish 'now-allowed'
368
674
  end
369
675
  end
370
676
  end
371
677
  end
372
678
  end
373
679
 
374
- context 'when downgrading capabilities' do
680
+ context 'when downgrading capabilities (#RTC8a1)' do
375
681
  let(:client_options) { default_options.merge(auth_callback: basic_token_cb, log_level: :none) }
376
682
 
377
683
  it 'is allowed and channels are detached' do
378
684
  client.connection.once(:connected) do
685
+ client.connection.once(:disconnected) { raise 'Upgrade does not require a disconnect' }
686
+
379
687
  channel = client.channels.get('foo')
380
688
  channel.attach do
381
689
  client.auth.authorize(nil, auth_callback: downgraded_token_cb)
@@ -389,11 +697,13 @@ describe Ably::Realtime::Auth, :event_machine do
389
697
  end
390
698
  end
391
699
 
392
- it 'ensures message delivery continuity whilst upgrading' do
700
+ it 'ensures message delivery continuity whilst upgrading (#RTC8a1)' do
393
701
  received_messages = []
394
702
  subscriber_channel = client.channels.get('foo')
395
703
  publisher_channel = client_publisher.channels.get('foo')
396
704
  subscriber_channel.attach do
705
+ client.connection.once(:disconnected) { raise 'Upgrade does not require a disconnect' }
706
+
397
707
  subscriber_channel.subscribe do |message|
398
708
  received_messages << message
399
709
  end
@@ -401,73 +711,19 @@ describe Ably::Realtime::Auth, :event_machine do
401
711
  publisher_channel.publish('foo') do
402
712
  EventMachine.add_timer(2) do
403
713
  expect(received_messages.length).to eql(1)
404
- client.auth.authorize(nil)
405
- client.connection.once(:disconnected) do
406
- publisher_channel.publish('bar') do
407
- expect(received_messages.length).to eql(1)
408
- end
409
- end
410
- client.connection.once(:connected) do
714
+
715
+ client.connection.once(:update) do
411
716
  EventMachine.add_timer(2) do
412
717
  expect(received_messages.length).to eql(2)
413
718
  stop_reactor
414
719
  end
415
720
  end
416
- end
417
- end
418
- end
419
- end
420
- end
421
721
 
422
- it 'does not change the connection state if current connection state is closing' do
423
- client.connection.once(:connected) do
424
- client.connection.once(:closing) do
425
- client.auth.authorize(nil)
426
- client.connection.once(:connected) do
427
- raise "Should not reconnect following #authorize"
428
- end
429
- EventMachine.add_timer(4) do
430
- expect(client.connection).to be_closed
431
- stop_reactor
432
- end
433
- end
434
- client.connection.close
435
- end
436
- end
437
-
438
- it 'does not change the connection state if current connection state is closed' do
439
- client.connection.once(:connected) do
440
- client.connection.once(:closed) do
441
- client.auth.authorize(nil)
442
- client.connection.once(:connected) do
443
- raise "Should not reconnect following #authorize"
444
- end
445
- EventMachine.add_timer(4) do
446
- expect(client.connection).to be_closed
447
- stop_reactor
448
- end
449
- end
450
- client.connection.close
451
- end
452
- end
453
-
454
- context 'when state is failed' do
455
- let(:client_options) { default_options.merge(auth_callback: basic_token_cb, log_level: :none) }
722
+ client.auth.authorize(nil)
456
723
 
457
- it 'does not change the connection state' do
458
- client.connection.once(:connected) do
459
- client.connection.once(:failed) do
460
- client.auth.authorize(nil)
461
- client.connection.once(:connected) do
462
- raise "Should not reconnect following #authorize"
463
- end
464
- EventMachine.add_timer(4) do
465
- expect(client.connection).to be_failed
466
- stop_reactor
724
+ publisher_channel.publish('bar')
467
725
  end
468
726
  end
469
- protocol_message = Ably::Models::ProtocolMessage.new(action: Ably::Models::ProtocolMessage::ACTION.Error.to_i)
470
- client.connection.__incoming_protocol_msgbus__.publish :protocol_message, protocol_message
471
727
  end
472
728
  end
473
729
  end
@@ -486,6 +742,67 @@ describe Ably::Realtime::Auth, :event_machine do
486
742
  end
487
743
  end
488
744
 
745
+ context 'server initiated AUTH ProtocolMessage' do
746
+ before do
747
+ stub_const 'Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER', 0 # allow token to be used even if about to expire
748
+ stub_const 'Ably::Auth::TOKEN_DEFAULTS', Ably::Auth::TOKEN_DEFAULTS.merge(renew_token_buffer: 0) # Ensure tokens issued expire immediately after issue
749
+ end
750
+
751
+ context 'when received' do
752
+ # Ably in all environments other than locla will send AUTH 30 seconds before expiry
753
+ # We set the TTL to 33s and wait (3s window)
754
+ # In local env, that window is 5 seconds instead of 30 seconds
755
+ let(:local_offset) { ENV['ABLY_ENV'] == 'local' ? 25 : 0 }
756
+ let(:client_options) { default_options.merge(use_token_auth: :true, default_token_params: { ttl: 33 - local_offset }) }
757
+
758
+ it 'should immediately start a new authentication process (#RTN22)' do
759
+ client.connection.once(:connected) do
760
+ original_token = auth.current_token_details
761
+ received_auth = false
762
+
763
+ client.connection.__incoming_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
764
+ received_auth = true if protocol_message.action == :auth
765
+ end
766
+
767
+ client.connection.once(:update) do
768
+ expect(received_auth).to be_truthy
769
+ expect(original_token).to_not eql(auth.current_token_details)
770
+ stop_reactor
771
+ end
772
+ end
773
+ end
774
+ end
775
+
776
+ context 'when not received' do
777
+ # Ably in all environments other than production will send AUTH 5 seconds before expiry, so
778
+ # set TTL to 5s so that the window for Realtime to send has passed
779
+ let(:client_options) { default_options.merge(use_token_auth: :true, default_token_params: { ttl: 5 }) }
780
+
781
+ it 'should expect the connection to be disconnected by the server but should resume automatically (#RTN22a)' do
782
+ client.connection.once(:connected) do
783
+ original_token = auth.current_token_details
784
+ original_conn_id = client.connection.id
785
+ received_auth = false
786
+
787
+ client.connection.__incoming_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
788
+ received_auth = true if protocol_message.action == :auth
789
+ end
790
+
791
+ client.connection.once(:disconnected) do |state_change|
792
+ expect(state_change.reason.code).to eql(40142)
793
+
794
+ client.connection.once(:connected) do
795
+ expect(received_auth).to be_falsey
796
+ expect(original_token).to_not eql(auth.current_token_details)
797
+ expect(original_conn_id).to eql(client.connection.id)
798
+ stop_reactor
799
+ end
800
+ end
801
+ end
802
+ end
803
+ end
804
+ end
805
+
489
806
  context '#auth_params' do
490
807
  it 'returns the auth params asynchronously' do
491
808
  auth.auth_params do |auth_params|
@@ -698,7 +1015,7 @@ describe Ably::Realtime::Auth, :event_machine do
698
1015
  end
699
1016
 
700
1017
  context 'deprecated #authorise' do
701
- let(:client_options) { default_options.merge(key: api_key, logger: custom_logger_object) }
1018
+ let(:client_options) { default_options.merge(key: api_key, logger: custom_logger_object, use_token_auth: true) }
702
1019
  let(:custom_logger) do
703
1020
  Class.new do
704
1021
  def initialize