ably-rest 0.8.3 → 0.8.5

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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -0
  3. data/lib/submodules/ably-ruby/CHANGELOG.md +10 -0
  4. data/lib/submodules/ably-ruby/README.md +1 -1
  5. data/lib/submodules/ably-ruby/Rakefile +1 -1
  6. data/lib/submodules/ably-ruby/lib/ably/auth.rb +24 -20
  7. data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +3 -0
  8. data/lib/submodules/ably-ruby/lib/ably/models/channel_state_change.rb +41 -0
  9. data/lib/submodules/ably-ruby/lib/ably/models/connection_state_change.rb +43 -0
  10. data/lib/submodules/ably-ruby/lib/ably/models/message.rb +1 -1
  11. data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +1 -1
  12. data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +2 -1
  13. data/lib/submodules/ably-ruby/lib/ably/models/token_details.rb +8 -6
  14. data/lib/submodules/ably-ruby/lib/ably/modules/conversions.rb +4 -0
  15. data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +4 -1
  16. data/lib/submodules/ably-ruby/lib/ably/modules/uses_state_machine.rb +35 -4
  17. data/lib/submodules/ably-ruby/lib/ably/realtime/auth.rb +13 -13
  18. data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +13 -5
  19. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +27 -7
  20. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_state_machine.rb +22 -12
  21. data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +16 -10
  22. data/lib/submodules/ably-ruby/lib/ably/realtime/client/outgoing_message_dispatcher.rb +6 -0
  23. data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +5 -4
  24. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +42 -24
  25. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_state_machine.rb +25 -17
  26. data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +4 -4
  27. data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +3 -2
  28. data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +2 -2
  29. data/lib/submodules/ably-ruby/lib/ably/util/crypto.rb +15 -0
  30. data/lib/submodules/ably-ruby/lib/ably/version.rb +1 -1
  31. data/lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb +9 -9
  32. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +2 -2
  33. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +168 -21
  34. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channels_spec.rb +6 -2
  35. data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +6 -5
  36. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +29 -19
  37. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +150 -35
  38. data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +146 -23
  39. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +2 -2
  40. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +44 -24
  41. data/lib/submodules/ably-ruby/spec/acceptance/realtime/stats_spec.rb +1 -1
  42. data/lib/submodules/ably-ruby/spec/acceptance/realtime/time_spec.rb +1 -1
  43. data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +77 -46
  44. data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +31 -3
  45. data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +15 -5
  46. data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +9 -7
  47. data/lib/submodules/ably-ruby/spec/support/event_machine_helper.rb +30 -4
  48. data/lib/submodules/ably-ruby/spec/support/protocol_helper.rb +9 -6
  49. data/lib/submodules/ably-ruby/spec/unit/auth_spec.rb +1 -1
  50. data/lib/submodules/ably-ruby/spec/unit/models/channel_state_change_spec.rb +44 -0
  51. data/lib/submodules/ably-ruby/spec/unit/models/connection_state_change_spec.rb +54 -0
  52. data/lib/submodules/ably-ruby/spec/unit/models/token_details_spec.rb +8 -0
  53. data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +1 -1
  54. data/lib/submodules/ably-ruby/spec/unit/util/crypto_spec.rb +18 -0
  55. metadata +6 -2
@@ -41,23 +41,25 @@ module Ably::Realtime
41
41
  end
42
42
 
43
43
  before_transition(to: [:connected]) do |connection, current_transition|
44
- connection.manager.connected current_transition.metadata
44
+ connection.manager.connected current_transition.metadata.protocol_message
45
45
  end
46
46
 
47
47
  after_transition(to: [:connected]) do |connection, current_transition|
48
- protocol_message = current_transition.metadata
49
- if is_error_type?(protocol_message.error)
50
- connection.logger.warn "ConnectionManager: Connected with error - #{protocol_message.error.message}"
51
- connection.emit :error, protocol_message.error
48
+ error = current_transition.metadata.reason
49
+ if is_error_type?(error)
50
+ connection.logger.warn "ConnectionManager: Connected with error - #{error.message}"
51
+ connection.emit :error, error
52
52
  end
53
53
  end
54
54
 
55
55
  after_transition(to: [:disconnected, :suspended], from: [:connecting]) do |connection, current_transition|
56
- connection.manager.respond_to_transport_disconnected_when_connecting current_transition
56
+ err = error_from_state_change(current_transition)
57
+ connection.manager.respond_to_transport_disconnected_when_connecting err
57
58
  end
58
59
 
59
- after_transition(to: [:disconnected], from: [:connected]) do |connection, current_transition|
60
- connection.manager.respond_to_transport_disconnected_whilst_connected current_transition
60
+ after_transition(to: [:disconnected, :suspended], from: [:connected]) do |connection, current_transition|
61
+ err = error_from_state_change(current_transition)
62
+ connection.manager.respond_to_transport_disconnected_whilst_connected err
61
63
  end
62
64
 
63
65
  after_transition(to: [:disconnected, :suspended]) do |connection|
@@ -65,7 +67,8 @@ module Ably::Realtime
65
67
  end
66
68
 
67
69
  before_transition(to: [:failed]) do |connection, current_transition|
68
- connection.manager.fail current_transition.metadata
70
+ err = error_from_state_change(current_transition)
71
+ connection.manager.fail err
69
72
  end
70
73
 
71
74
  after_transition(to: [:closing], from: [:initialized, :disconnected, :suspended]) do |connection|
@@ -82,24 +85,29 @@ module Ably::Realtime
82
85
 
83
86
  # Transitions responsible for updating connection#error_reason
84
87
  before_transition(to: [:disconnected, :suspended, :failed]) do |connection, current_transition|
85
- connection.set_failed_connection_error_reason current_transition.metadata
88
+ err = error_from_state_change(current_transition)
89
+ connection.set_failed_connection_error_reason err
86
90
  end
87
91
 
88
92
  before_transition(to: [:connected, :closed]) do |connection, current_transition|
89
- error = if current_transition.metadata.kind_of?(Ably::Models::ProtocolMessage)
90
- current_transition.metadata.error
91
- else
92
- current_transition.metadata
93
- end
93
+ err = error_from_state_change(current_transition)
94
94
 
95
- if is_error_type?(error)
96
- connection.set_failed_connection_error_reason error
95
+ if err
96
+ connection.set_failed_connection_error_reason err
97
97
  else
98
98
  # Connected & Closed are "healthy" final states so reset the error reason
99
99
  connection.clear_error_reason
100
100
  end
101
101
  end
102
102
 
103
+ def self.error_from_state_change(current_transition)
104
+ # ConnectionStateChange object is always passed in current_transition metadata object
105
+ connection_state_change = current_transition.metadata
106
+ # Reason attribute contains errors
107
+ err = connection_state_change && connection_state_change.reason
108
+ err if is_error_type?(err)
109
+ end
110
+
103
111
  private
104
112
  def connection
105
113
  object
@@ -405,7 +405,7 @@ module Ably::Realtime
405
405
  deferrable_succeed deferrable, &success_block
406
406
  end
407
407
 
408
- protocol_message.errback do |message, error|
408
+ protocol_message.errback do |error|
409
409
  change_state failed_state, error if failed_state
410
410
  deferrable_fail deferrable, error
411
411
  end
@@ -419,8 +419,8 @@ module Ably::Realtime
419
419
  end
420
420
 
421
421
  def deferrable_fail(deferrable, *args, &block)
422
- safe_yield block, self, *args if block_given?
423
- EventMachine.next_tick { deferrable.fail self, *args } # allow errback to be added to the returned Deferrable
422
+ safe_yield block, *args if block_given?
423
+ EventMachine.next_tick { deferrable.fail *args } # allow errback to be added to the returned Deferrable
424
424
  deferrable
425
425
  end
426
426
 
@@ -431,7 +431,7 @@ module Ably::Realtime
431
431
  ensure_channel_attached(deferrable) do
432
432
  send_presence_protocol_message(action, client_id, options).tap do |protocol_message|
433
433
  protocol_message.callback { |message| deferrable_succeed deferrable, &success_block }
434
- protocol_message.errback { |message, error| deferrable_fail deferrable, error }
434
+ protocol_message.errback { |error| deferrable_fail deferrable, error }
435
435
  end
436
436
  end
437
437
  end
@@ -34,6 +34,7 @@ module Ably
34
34
  #
35
35
  # @param name [String, Array<Ably::Models::Message|Hash>, nil] The event name of the message to publish, or an Array of [Ably::Model::Message] objects or [Hash] objects with +:name+ and +:data+ pairs
36
36
  # @param data [String, ByteArray, nil] The message payload unless an Array of [Ably::Model::Message] objects passed in the first argument
37
+ # @param attributes [Hash, nil] Optional additional message attributes such as :client_id or :connection_id, applied when name attribute is nil or a string
37
38
  # @return [Boolean] true if the message was published, otherwise false
38
39
  #
39
40
  # @example
@@ -54,13 +55,13 @@ module Ably
54
55
  # ]
55
56
  # channel.publish messages
56
57
  #
57
- def publish(name, data = nil)
58
+ def publish(name, data = nil, attributes = {})
58
59
  messages = if name.kind_of?(Enumerable)
59
60
  name
60
61
  else
61
62
  ensure_utf_8 :name, name, allow_nil: true
62
63
  ensure_supported_payload data
63
- [{ name: name, data: data }]
64
+ [{ name: name, data: data }.merge(attributes)]
64
65
  end
65
66
 
66
67
  payload = messages.map do |message|
@@ -144,7 +144,7 @@ module Ably
144
144
 
145
145
  token_params = options.delete(:token_params) || {}
146
146
  @options = options
147
- @auth = Auth.new(self, options, token_params)
147
+ @auth = Auth.new(self, token_params, options)
148
148
  @channels = Ably::Rest::Channels.new(self)
149
149
  @encoders = []
150
150
 
@@ -358,7 +358,7 @@ module Ably
358
358
  yield
359
359
  rescue Ably::Exceptions::TokenExpired => e
360
360
  if auth.token_renewable?
361
- auth.authorise force: true
361
+ auth.authorise({}, force: true)
362
362
  yield
363
363
  else
364
364
  raise e
@@ -37,6 +37,21 @@ module Ably::Util
37
37
  @options = DEFAULTS.merge(options).freeze
38
38
  end
39
39
 
40
+ # Obtain a default CipherParams. This uses default algorithm, mode and
41
+ # padding and key length. A key and IV are generated using the default
42
+ # system SecureRandom; the key may be obtained from the returned CipherParams
43
+ # for out-of-band distribution to other clients.
44
+ #
45
+ # @return [Hash] CipherParam options Hash with attributes :key, :algorithn, :mode, :key_length
46
+ #
47
+ def self.get_default_params(key = nil)
48
+ params = DEFAULTS.merge(key: key)
49
+ params[:key_length] = key.unpack('b*').first.length if params[:key]
50
+ cipher_type = "#{params[:algorithm]}-#{params[:key_length]}-#{params[:mode]}"
51
+ params[:key] = OpenSSL::Cipher.new(cipher_type.upcase).random_key unless params[:key]
52
+ params
53
+ end
54
+
40
55
  # Encrypt payload using configured Cipher
41
56
  #
42
57
  # @param [String] payload the payload to be encrypted
@@ -1,3 +1,3 @@
1
1
  module Ably
2
- VERSION = '0.8.3'
2
+ VERSION = '0.8.5'
3
3
  end
@@ -8,7 +8,7 @@ describe Ably::Realtime::Auth, :event_machine do
8
8
  vary_by_protocol do
9
9
  let(:default_options) { { key: api_key, environment: environment, protocol: protocol } }
10
10
  let(:client_options) { default_options }
11
- let(:client) { Ably::Realtime::Client.new(client_options) }
11
+ let(:client) { auto_close Ably::Realtime::Client.new(client_options) }
12
12
  let(:auth) { client.auth }
13
13
 
14
14
  context 'with basic auth' do
@@ -101,7 +101,7 @@ describe Ably::Realtime::Auth, :event_machine do
101
101
  let(:auth_params) { { :body => random_str } }
102
102
 
103
103
  it 'contains the configured auth options' do
104
- auth.authorise(auth_url: auth_url, auth_params: auth_params) do
104
+ auth.authorise({}, auth_url: auth_url, auth_params: auth_params) do
105
105
  expect(auth.options[:auth_url]).to eql(auth_url)
106
106
  stop_reactor
107
107
  end
@@ -112,7 +112,7 @@ describe Ably::Realtime::Auth, :event_machine do
112
112
  let(:custom_ttl) { 33 }
113
113
 
114
114
  it 'contains the configured auth options' do
115
- auth.authorise({}, ttl: custom_ttl) do
115
+ auth.authorise(ttl: custom_ttl) do
116
116
  expect(auth.token_params[:ttl]).to eql(custom_ttl)
117
117
  stop_reactor
118
118
  end
@@ -144,7 +144,7 @@ describe Ably::Realtime::Auth, :event_machine do
144
144
 
145
145
  context '#create_token_request' do
146
146
  it 'returns a token request asynchronously' do
147
- auth.create_token_request({}, { ttl: custom_ttl }) do |token_request|
147
+ auth.create_token_request(ttl: custom_ttl) do |token_request|
148
148
  expect(token_request).to be_a(Ably::Models::TokenRequest)
149
149
  expect(token_request.ttl).to eql(custom_ttl)
150
150
  stop_reactor
@@ -154,7 +154,7 @@ describe Ably::Realtime::Auth, :event_machine do
154
154
 
155
155
  context '#create_token_request_async' do
156
156
  it 'returns a token request synchronously' do
157
- auth.create_token_request_sync(token_params: { ttl: custom_ttl }).tap do |token_request|
157
+ auth.create_token_request_sync(ttl: custom_ttl).tap do |token_request|
158
158
  expect(token_request).to be_a(Ably::Models::TokenRequest)
159
159
  expect(token_request.ttl).to eql(custom_ttl)
160
160
  stop_reactor
@@ -164,7 +164,7 @@ describe Ably::Realtime::Auth, :event_machine do
164
164
 
165
165
  context '#request_token' do
166
166
  it 'returns a token asynchronously' do
167
- auth.request_token({ client_id: custom_client_id }, ttl: custom_ttl) do |token_details|
167
+ auth.request_token(client_id: custom_client_id, ttl: custom_ttl) do |token_details|
168
168
  expect(token_details).to be_a(Ably::Models::TokenDetails)
169
169
  expect(token_details.expires.to_i).to be_within(3).of(Time.now.to_i + custom_ttl)
170
170
  expect(token_details.client_id).to eql(custom_client_id)
@@ -175,7 +175,7 @@ describe Ably::Realtime::Auth, :event_machine do
175
175
 
176
176
  context '#request_token_async' do
177
177
  it 'returns a token synchronously' do
178
- auth.request_token_sync(client_id: custom_client_id, token_params: { ttl: custom_ttl }).tap do |token_details|
178
+ auth.request_token_sync(ttl: custom_ttl, client_id: custom_client_id).tap do |token_details|
179
179
  expect(token_details).to be_a(Ably::Models::TokenDetails)
180
180
  expect(token_details.expires.to_i).to be_within(3).of(Time.now.to_i + custom_ttl)
181
181
  expect(token_details.client_id).to eql(custom_client_id)
@@ -186,7 +186,7 @@ describe Ably::Realtime::Auth, :event_machine do
186
186
 
187
187
  context '#authorise' do
188
188
  it 'returns a token asynchronously' do
189
- auth.authorise({ client_id: custom_client_id }, ttl: custom_ttl) do |token_details|
189
+ auth.authorise(ttl: custom_ttl, client_id: custom_client_id) do |token_details|
190
190
  expect(token_details).to be_a(Ably::Models::TokenDetails)
191
191
  expect(token_details.expires.to_i).to be_within(3).of(Time.now.to_i + custom_ttl)
192
192
  expect(token_details.client_id).to eql(custom_client_id)
@@ -197,7 +197,7 @@ describe Ably::Realtime::Auth, :event_machine do
197
197
 
198
198
  context '#authorise_async' do
199
199
  it 'returns a token synchronously' do
200
- auth.authorise_sync(client_id: custom_client_id, token_params: { ttl: custom_ttl }).tap do |token_details|
200
+ auth.authorise_sync(ttl: custom_ttl, client_id: custom_client_id).tap do |token_details|
201
201
  expect(auth.authorise_sync).to be_a(Ably::Models::TokenDetails)
202
202
  expect(token_details.expires.to_i).to be_within(3).of(Time.now.to_i + custom_ttl)
203
203
  expect(token_details.client_id).to eql(custom_client_id)
@@ -5,11 +5,11 @@ describe Ably::Realtime::Channel, '#history', :event_machine do
5
5
  vary_by_protocol do
6
6
  let(:default_options) { options.merge(key: api_key, environment: environment, protocol: protocol) }
7
7
 
8
- let(:client) { Ably::Realtime::Client.new(default_options) }
8
+ let(:client) { auto_close Ably::Realtime::Client.new(default_options) }
9
9
  let(:channel) { client.channel(channel_name) }
10
10
  let(:rest_channel) { client.rest_client.channel(channel_name) }
11
11
 
12
- let(:client2) { Ably::Realtime::Client.new(default_options) }
12
+ let(:client2) { auto_close Ably::Realtime::Client.new(default_options) }
13
13
  let(:channel2) { client2.channel(channel_name) }
14
14
 
15
15
  let(:channel_name) { "persisted:#{random_str(2)}" }
@@ -6,7 +6,7 @@ describe Ably::Realtime::Channel, :event_machine do
6
6
  let(:default_options) { { key: api_key, environment: environment, protocol: protocol } }
7
7
  let(:client_options) { default_options }
8
8
 
9
- let(:client) { Ably::Realtime::Client.new(client_options) }
9
+ let(:client) { auto_close Ably::Realtime::Client.new(client_options) }
10
10
  let(:channel_name) { random_str }
11
11
  let(:payload) { random_str }
12
12
  let(:channel) { client.channel(channel_name) }
@@ -15,7 +15,7 @@ describe Ably::Realtime::Channel, :event_machine do
15
15
  describe 'initialization' do
16
16
  context 'with :auto_connect option set to false on connection' do
17
17
  let(:client) do
18
- Ably::Realtime::Client.new(default_options.merge(auto_connect: false))
18
+ auto_close Ably::Realtime::Client.new(default_options.merge(auto_connect: false))
19
19
  end
20
20
 
21
21
  it 'remains initialized when accessing a channel' do
@@ -80,7 +80,7 @@ describe Ably::Realtime::Channel, :event_machine do
80
80
  end
81
81
 
82
82
  it 'calls the SafeDeferrable callback on success' do
83
- channel.attach.callback do |channel|
83
+ channel.attach.callback do
84
84
  expect(channel).to be_a(Ably::Realtime::Channel)
85
85
  expect(channel.state).to eq(:attached)
86
86
  stop_reactor
@@ -92,7 +92,7 @@ describe Ably::Realtime::Channel, :event_machine do
92
92
 
93
93
  it 'reattaches' do
94
94
  channel.attach do
95
- channel.transition_state_machine :failed, RuntimeError.new
95
+ channel.transition_state_machine :failed, reason: RuntimeError.new
96
96
  expect(channel).to be_failed
97
97
  channel.attach do
98
98
  expect(channel).to be_attached
@@ -131,7 +131,7 @@ describe Ably::Realtime::Channel, :event_machine do
131
131
 
132
132
  it 'attaches all channels', em_timeout: 15 do
133
133
  connection_count.times.map do
134
- Ably::Realtime::Client.new(default_options)
134
+ auto_close Ably::Realtime::Client.new(default_options)
135
135
  end.each do |client|
136
136
  channel_count.times.map do |index|
137
137
  client.channel("channel-#{index}").attach do
@@ -148,21 +148,21 @@ describe Ably::Realtime::Channel, :event_machine do
148
148
 
149
149
  context 'failure as a result of insufficient key permissions' do
150
150
  let(:restricted_client) do
151
- Ably::Realtime::Client.new(default_options.merge(key: restricted_api_key, log_level: :fatal))
151
+ auto_close Ably::Realtime::Client.new(default_options.merge(key: restricted_api_key, log_level: :fatal))
152
152
  end
153
153
  let(:restricted_channel) { restricted_client.channel("cannot_subscribe") }
154
154
 
155
155
  it 'emits failed event' do
156
156
  restricted_channel.attach
157
- restricted_channel.on(:failed) do |error|
157
+ restricted_channel.on(:failed) do |connection_state|
158
158
  expect(restricted_channel.state).to eq(:failed)
159
- expect(error.status).to eq(401)
159
+ expect(connection_state.reason.status).to eq(401)
160
160
  stop_reactor
161
161
  end
162
162
  end
163
163
 
164
164
  it 'calls the errback of the returned Deferrable' do
165
- restricted_channel.attach.errback do |channel, error|
165
+ restricted_channel.attach.errback do |error|
166
166
  expect(restricted_channel.state).to eq(:failed)
167
167
  expect(error.status).to eq(401)
168
168
  stop_reactor
@@ -192,7 +192,7 @@ describe Ably::Realtime::Channel, :event_machine do
192
192
  restricted_channel.once(:failed) do
193
193
  restricted_client.close do
194
194
  # A direct call to #authorise is synchronous
195
- restricted_client.auth.authorise(key: api_key)
195
+ restricted_client.auth.authorise({}, key: api_key)
196
196
 
197
197
  restricted_client.connect do
198
198
  restricted_channel.once(:attached) do
@@ -220,9 +220,10 @@ describe Ably::Realtime::Channel, :event_machine do
220
220
  end
221
221
 
222
222
  it 'detaches from a channel and calls the provided block' do
223
- channel.attach do |chan|
224
- chan.detach do |detached_chan|
225
- expect(detached_chan.state).to eq(:detached)
223
+ channel.attach do
224
+ expect(channel.state).to eq(:attached)
225
+ channel.detach do
226
+ expect(channel.state).to eq(:detached)
226
227
  stop_reactor
227
228
  end
228
229
  end
@@ -249,7 +250,7 @@ describe Ably::Realtime::Channel, :event_machine do
249
250
 
250
251
  it 'calls the Deferrable callback on success' do
251
252
  channel.attach do
252
- channel.detach.callback do |channel|
253
+ channel.detach.callback do
253
254
  expect(channel).to be_a(Ably::Realtime::Channel)
254
255
  expect(channel.state).to eq(:detached)
255
256
  stop_reactor
@@ -262,7 +263,7 @@ describe Ably::Realtime::Channel, :event_machine do
262
263
 
263
264
  it 'raises an exception' do
264
265
  channel.attach do
265
- channel.transition_state_machine :failed, RuntimeError.new
266
+ channel.transition_state_machine :failed, reason: RuntimeError.new
266
267
  expect(channel).to be_failed
267
268
  expect { channel.detach }.to raise_error Ably::Exceptions::InvalidStateChange
268
269
  stop_reactor
@@ -433,6 +434,19 @@ describe Ably::Realtime::Channel, :event_machine do
433
434
  end
434
435
  end
435
436
  end
437
+
438
+ context 'and additional attributes' do
439
+ let(:client_id) { random_str }
440
+
441
+ it 'publishes the message with the attributes and return true indicating success' do
442
+ channel.publish(name, data, client_id: client_id) do
443
+ channel.history do |page|
444
+ expect(page.items.first.client_id).to eql(client_id)
445
+ stop_reactor
446
+ end
447
+ end
448
+ end
449
+ end
436
450
  end
437
451
 
438
452
  context 'with an array of Hash objects with :name and :data attributes' do
@@ -550,7 +564,7 @@ describe Ably::Realtime::Channel, :event_machine do
550
564
 
551
565
  it 'publishes all messages, all success callbacks are called, and a history request confirms all messages were published' do
552
566
  connection_count.times.map do
553
- Ably::Realtime::Client.new(client_options)
567
+ auto_close Ably::Realtime::Client.new(client_options)
554
568
  end.each do |client|
555
569
  channel = client.channels.get(channel_name)
556
570
  messages.each do |message|
@@ -666,7 +680,8 @@ describe Ably::Realtime::Channel, :event_machine do
666
680
  context 'an :attached channel' do
667
681
  it 'transitions state to :failed' do
668
682
  channel.attach do
669
- channel.on(:failed) do |error|
683
+ channel.on(:failed) do |connection_state_change|
684
+ error = connection_state_change.reason
670
685
  expect(error).to be_a(Ably::Exceptions::ConnectionFailed)
671
686
  expect(error.code).to eql(80002)
672
687
  stop_reactor
@@ -688,7 +703,8 @@ describe Ably::Realtime::Channel, :event_machine do
688
703
 
689
704
  it 'updates the channel error_reason' do
690
705
  channel.attach do
691
- channel.on(:failed) do |error|
706
+ channel.on(:failed) do |connection_state_change|
707
+ error = connection_state_change.reason
692
708
  expect(error).to be_a(Ably::Exceptions::ConnectionFailed)
693
709
  expect(error.code).to eql(80002)
694
710
  stop_reactor
@@ -734,7 +750,7 @@ describe Ably::Realtime::Channel, :event_machine do
734
750
  fake_error connection_error
735
751
  end
736
752
 
737
- channel.transition_state_machine :failed, original_error
753
+ channel.transition_state_machine :failed, reason: original_error
738
754
  end
739
755
  end
740
756
  end
@@ -783,8 +799,8 @@ describe Ably::Realtime::Channel, :event_machine do
783
799
  end
784
800
 
785
801
  context 'a :failed channel' do
786
- let(:original_error) { RuntimeError.new }
787
802
  let(:client_options) { default_options.merge(log_level: :fatal) }
803
+ let(:original_error) { Ably::Models::ErrorInfo.new(message: 'Error') }
788
804
 
789
805
  it 'remains in the :failed state and retains the error_reason' do
790
806
  channel.attach do
@@ -801,7 +817,7 @@ describe Ably::Realtime::Channel, :event_machine do
801
817
  client.connection.close
802
818
  end
803
819
 
804
- channel.transition_state_machine :failed, original_error
820
+ channel.transition_state_machine :failed, reason: original_error
805
821
  end
806
822
  end
807
823
  end
@@ -830,6 +846,75 @@ describe Ably::Realtime::Channel, :event_machine do
830
846
  end
831
847
  end
832
848
  end
849
+
850
+ context ':suspended' do
851
+ context 'an :attached channel' do
852
+ let(:client_options) { default_options.merge(log_level: :fatal) }
853
+
854
+ it 'transitions state to :detached' do
855
+ channel.attach do
856
+ channel.on(:detached) do
857
+ stop_reactor
858
+ end
859
+ client.connection.transition_state_machine :suspended
860
+ end
861
+ end
862
+ end
863
+
864
+ context 'a :detached channel' do
865
+ it 'remains in the :detached state' do
866
+ channel.attach do
867
+ channel.detach do
868
+ channel.on(:detached) { raise 'Detached state should not have been reached' }
869
+ channel.on(:error) { raise 'Error should not have been emitted' }
870
+
871
+ EventMachine.add_timer(1) do
872
+ expect(channel).to be_detached
873
+ stop_reactor
874
+ end
875
+
876
+ client.connection.transition_state_machine :suspended
877
+ end
878
+ end
879
+ end
880
+ end
881
+
882
+ context 'a :failed channel' do
883
+ let(:original_error) { RuntimeError.new }
884
+ let(:client_options) { default_options.merge(log_level: :fatal) }
885
+
886
+ it 'remains in the :failed state and retains the error_reason' do
887
+ channel.attach do
888
+ channel.once(:error) do
889
+ channel.on(:detached) { raise 'Detached state should not have been reached' }
890
+ channel.on(:error) { raise 'Error should not have been emitted' }
891
+
892
+ EventMachine.add_timer(1) do
893
+ expect(channel).to be_failed
894
+ expect(channel.error_reason).to eql(original_error)
895
+ stop_reactor
896
+ end
897
+
898
+ client.connection.transition_state_machine :suspended
899
+ end
900
+
901
+ channel.transition_state_machine :failed, reason: original_error
902
+ end
903
+ end
904
+ end
905
+
906
+ context 'a channel ATTACH request when connection SUSPENDED' do
907
+ it 'raises an exception' do
908
+ client.connect do
909
+ client.connection.once(:suspended) do
910
+ expect { channel.attach }.to raise_error Ably::Exceptions::InvalidStateChange
911
+ stop_reactor
912
+ end
913
+ client.connection.transition_state_machine :suspended
914
+ end
915
+ end
916
+ end
917
+ end
833
918
  end
834
919
 
835
920
  describe '#presence' do
@@ -838,5 +923,67 @@ describe Ably::Realtime::Channel, :event_machine do
838
923
  stop_reactor
839
924
  end
840
925
  end
926
+
927
+ context 'channel state change' do
928
+ it 'emits a ChannelStateChange object' do
929
+ channel.on(:attached) do |channel_state_change|
930
+ expect(channel_state_change).to be_a(Ably::Models::ChannelStateChange)
931
+ stop_reactor
932
+ end
933
+ channel.attach
934
+ end
935
+
936
+ context 'ChannelStateChange object' do
937
+ it 'has current state' do
938
+ channel.on(:attached) do |channel_state_change|
939
+ expect(channel_state_change.current).to eq(:attached)
940
+ stop_reactor
941
+ end
942
+ channel.attach
943
+ end
944
+
945
+ it 'has a previous state' do
946
+ channel.on(:attached) do |channel_state_change|
947
+ expect(channel_state_change.previous).to eq(:attaching)
948
+ stop_reactor
949
+ end
950
+ channel.attach
951
+ end
952
+
953
+ it 'contains a private API protocol_message attribute that is used for special state change events', :api_private do
954
+ channel.on(:attached) do |channel_state_change|
955
+ expect(channel_state_change.protocol_message).to be_a(Ably::Models::ProtocolMessage)
956
+ expect(channel_state_change.reason).to be_nil
957
+ stop_reactor
958
+ end
959
+ channel.attach
960
+ end
961
+
962
+ it 'has an empty reason when there is no error' do
963
+ channel.on(:detached) do |channel_state_change|
964
+ expect(channel_state_change.reason).to be_nil
965
+ stop_reactor
966
+ end
967
+ channel.attach do
968
+ channel.detach
969
+ end
970
+ end
971
+
972
+ context 'on failure' do
973
+ let(:client_options) { default_options.merge(log_level: :none) }
974
+
975
+ it 'has a reason Error object when there is an error on the channel' do
976
+ channel.on(:failed) do |channel_state_change|
977
+ expect(channel_state_change.reason).to be_a(Ably::Exceptions::BaseAblyException)
978
+ stop_reactor
979
+ end
980
+ channel.attach do
981
+ error = Ably::Exceptions::ConnectionFailed.new('forced failure', 500, 50000)
982
+ client.connection.manager.error_received_from_server error
983
+ end
984
+ end
985
+ end
986
+ end
987
+ end
841
988
  end
842
989
  end