ably 0.6.2 → 0.7.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.
- checksums.yaml +4 -4
- data/.rspec +1 -0
- data/.ruby-version.old +1 -0
- data/.travis.yml +0 -2
- data/Rakefile +22 -4
- data/SPEC.md +1676 -0
- data/ably.gemspec +1 -1
- data/lib/ably.rb +0 -8
- data/lib/ably/auth.rb +54 -46
- data/lib/ably/exceptions.rb +19 -5
- data/lib/ably/logger.rb +1 -1
- data/lib/ably/models/error_info.rb +1 -1
- data/lib/ably/models/idiomatic_ruby_wrapper.rb +11 -9
- data/lib/ably/models/message.rb +15 -12
- data/lib/ably/models/message_encoders/base.rb +6 -5
- data/lib/ably/models/message_encoders/base64.rb +1 -0
- data/lib/ably/models/message_encoders/cipher.rb +6 -3
- data/lib/ably/models/message_encoders/json.rb +1 -0
- data/lib/ably/models/message_encoders/utf8.rb +2 -9
- data/lib/ably/models/nil_logger.rb +20 -0
- data/lib/ably/models/paginated_resource.rb +5 -2
- data/lib/ably/models/presence_message.rb +21 -12
- data/lib/ably/models/protocol_message.rb +22 -6
- data/lib/ably/modules/ably.rb +11 -0
- data/lib/ably/modules/async_wrapper.rb +2 -0
- data/lib/ably/modules/conversions.rb +23 -3
- data/lib/ably/modules/encodeable.rb +2 -1
- data/lib/ably/modules/enum.rb +2 -0
- data/lib/ably/modules/event_emitter.rb +7 -1
- data/lib/ably/modules/event_machine_helpers.rb +2 -0
- data/lib/ably/modules/http_helpers.rb +2 -0
- data/lib/ably/modules/model_common.rb +12 -2
- data/lib/ably/modules/state_emitter.rb +76 -0
- data/lib/ably/modules/state_machine.rb +53 -0
- data/lib/ably/modules/statesman_monkey_patch.rb +33 -0
- data/lib/ably/modules/uses_state_machine.rb +74 -0
- data/lib/ably/realtime.rb +4 -2
- data/lib/ably/realtime/channel.rb +51 -58
- data/lib/ably/realtime/channel/channel_manager.rb +91 -0
- data/lib/ably/realtime/channel/channel_state_machine.rb +68 -0
- data/lib/ably/realtime/client.rb +70 -26
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +31 -13
- data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +1 -1
- data/lib/ably/realtime/connection.rb +135 -92
- data/lib/ably/realtime/connection/connection_manager.rb +216 -33
- data/lib/ably/realtime/connection/connection_state_machine.rb +30 -73
- data/lib/ably/realtime/models/nil_channel.rb +10 -1
- data/lib/ably/realtime/presence.rb +336 -92
- data/lib/ably/rest.rb +2 -2
- data/lib/ably/rest/channel.rb +13 -4
- data/lib/ably/rest/client.rb +138 -38
- data/lib/ably/rest/middleware/logger.rb +24 -3
- data/lib/ably/rest/presence.rb +12 -7
- data/lib/ably/version.rb +1 -1
- data/spec/acceptance/realtime/channel_history_spec.rb +101 -85
- data/spec/acceptance/realtime/channel_spec.rb +461 -120
- data/spec/acceptance/realtime/client_spec.rb +119 -0
- data/spec/acceptance/realtime/connection_failures_spec.rb +499 -0
- data/spec/acceptance/realtime/connection_spec.rb +571 -97
- data/spec/acceptance/realtime/message_spec.rb +347 -333
- data/spec/acceptance/realtime/presence_history_spec.rb +35 -40
- data/spec/acceptance/realtime/presence_spec.rb +769 -239
- data/spec/acceptance/realtime/stats_spec.rb +14 -22
- data/spec/acceptance/realtime/time_spec.rb +16 -20
- data/spec/acceptance/rest/auth_spec.rb +425 -364
- data/spec/acceptance/rest/base_spec.rb +108 -176
- data/spec/acceptance/rest/channel_spec.rb +89 -89
- data/spec/acceptance/rest/channels_spec.rb +30 -32
- data/spec/acceptance/rest/client_spec.rb +273 -0
- data/spec/acceptance/rest/encoders_spec.rb +185 -0
- data/spec/acceptance/rest/message_spec.rb +186 -163
- data/spec/acceptance/rest/presence_spec.rb +150 -111
- data/spec/acceptance/rest/stats_spec.rb +45 -40
- data/spec/acceptance/rest/time_spec.rb +8 -10
- data/spec/rspec_config.rb +10 -1
- data/spec/shared/client_initializer_behaviour.rb +212 -0
- data/spec/{support/model_helper.rb → shared/model_behaviour.rb} +6 -6
- data/spec/{support/protocol_msgbus_helper.rb → shared/protocol_msgbus_behaviour.rb} +1 -1
- data/spec/spec_helper.rb +9 -0
- data/spec/support/api_helper.rb +11 -0
- data/spec/support/event_machine_helper.rb +101 -3
- data/spec/support/markdown_spec_formatter.rb +90 -0
- data/spec/support/private_api_formatter.rb +36 -0
- data/spec/support/protocol_helper.rb +32 -0
- data/spec/support/random_helper.rb +15 -0
- data/spec/support/test_app.rb +4 -0
- data/spec/unit/auth_spec.rb +68 -0
- data/spec/unit/logger_spec.rb +77 -66
- data/spec/unit/models/error_info_spec.rb +1 -1
- data/spec/unit/models/idiomatic_ruby_wrapper_spec.rb +2 -3
- data/spec/unit/models/message_encoders/base64_spec.rb +2 -2
- data/spec/unit/models/message_encoders/cipher_spec.rb +2 -2
- data/spec/unit/models/message_encoders/utf8_spec.rb +2 -46
- data/spec/unit/models/message_spec.rb +160 -15
- data/spec/unit/models/paginated_resource_spec.rb +29 -27
- data/spec/unit/models/presence_message_spec.rb +163 -20
- data/spec/unit/models/protocol_message_spec.rb +43 -8
- data/spec/unit/modules/async_wrapper_spec.rb +2 -3
- data/spec/unit/modules/conversions_spec.rb +1 -1
- data/spec/unit/modules/enum_spec.rb +2 -3
- data/spec/unit/modules/event_emitter_spec.rb +62 -5
- data/spec/unit/modules/state_emitter_spec.rb +283 -0
- data/spec/unit/realtime/channel_spec.rb +107 -2
- data/spec/unit/realtime/channels_spec.rb +1 -0
- data/spec/unit/realtime/client_spec.rb +8 -48
- data/spec/unit/realtime/connection_spec.rb +3 -3
- data/spec/unit/realtime/incoming_message_dispatcher_spec.rb +2 -2
- data/spec/unit/realtime/presence_spec.rb +13 -4
- data/spec/unit/realtime/realtime_spec.rb +0 -11
- data/spec/unit/realtime/websocket_transport_spec.rb +2 -2
- data/spec/unit/rest/channel_spec.rb +109 -0
- data/spec/unit/rest/channels_spec.rb +4 -3
- data/spec/unit/rest/client_spec.rb +30 -125
- data/spec/unit/rest/rest_spec.rb +10 -0
- data/spec/unit/util/crypto_spec.rb +10 -5
- data/spec/unit/util/pub_sub_spec.rb +5 -5
- metadata +44 -12
- data/spec/integration/modules/state_emitter_spec.rb +0 -80
- data/spec/integration/rest/auth.rb +0 -9
@@ -1,211 +1,554 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require 'spec_helper'
|
2
|
-
require 'securerandom'
|
3
3
|
|
4
|
-
describe Ably::Realtime::Channel do
|
5
|
-
|
4
|
+
describe Ably::Realtime::Channel, :event_machine do
|
5
|
+
vary_by_protocol do
|
6
|
+
let(:default_options) { { api_key: api_key, environment: environment, protocol: protocol } }
|
7
|
+
let(:client_options) { default_options }
|
8
|
+
|
9
|
+
let(:client) { Ably::Realtime::Client.new(client_options) }
|
10
|
+
let(:channel_name) { random_str }
|
11
|
+
let(:payload) { random_str }
|
12
|
+
let(:channel) { client.channel(channel_name) }
|
13
|
+
let(:messages) { [] }
|
14
|
+
|
15
|
+
describe 'initialization' do
|
16
|
+
context 'with :connect_automatically option set to false on connection' do
|
17
|
+
let(:client) do
|
18
|
+
Ably::Realtime::Client.new(default_options.merge(connect_automatically: false))
|
19
|
+
end
|
6
20
|
|
7
|
-
|
8
|
-
|
9
|
-
|
21
|
+
it 'remains initialized when accessing a channel' do
|
22
|
+
client.channel('test')
|
23
|
+
EventMachine.add_timer(2) do
|
24
|
+
expect(client.connection).to be_initialized
|
25
|
+
stop_reactor
|
26
|
+
end
|
27
|
+
end
|
10
28
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
29
|
+
it 'opens a connection implicitly on #attach' do
|
30
|
+
client.channel('test').attach do
|
31
|
+
expect(client.connection).to be_connected
|
32
|
+
stop_reactor
|
33
|
+
end
|
34
|
+
end
|
16
35
|
|
17
|
-
|
18
|
-
|
36
|
+
it 'opens a connection implicitly when accessing #presence' do
|
37
|
+
client.channel('test').tap do |channel|
|
38
|
+
channel.on(:attached) do
|
39
|
+
expect(client.connection).to be_connected
|
40
|
+
stop_reactor
|
41
|
+
end
|
42
|
+
channel.presence
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#attach' do
|
49
|
+
it 'emits attaching then attached events' do
|
50
|
+
channel.once(:attaching) do
|
51
|
+
channel.once(:attached) do
|
52
|
+
stop_reactor
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
channel.attach
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'ignores subsequent #attach calls but calls the success callback if provided' do
|
60
|
+
channel.once(:attaching) do
|
19
61
|
channel.attach
|
20
|
-
channel.
|
21
|
-
|
62
|
+
channel.once(:attached) do
|
63
|
+
channel.attach do
|
64
|
+
stop_reactor
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
channel.attach
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'attaches to a channel' do
|
73
|
+
channel.attach
|
74
|
+
channel.on(:attached) do
|
75
|
+
expect(channel.state).to eq(:attached)
|
76
|
+
stop_reactor
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'attaches to a channel and calls the provided block' do
|
81
|
+
channel.attach do
|
82
|
+
expect(channel.state).to eq(:attached)
|
83
|
+
stop_reactor
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'returns a Deferrable' do
|
88
|
+
expect(channel.attach).to be_a(EventMachine::Deferrable)
|
89
|
+
stop_reactor
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'calls the Deferrable callback on success' do
|
93
|
+
channel.attach.callback do |channel|
|
94
|
+
expect(channel).to be_a(Ably::Realtime::Channel)
|
95
|
+
expect(channel.state).to eq(:attached)
|
96
|
+
stop_reactor
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context 'when state is :failed' do
|
101
|
+
let(:client_options) { default_options.merge(log_level: :fatal) }
|
102
|
+
|
103
|
+
it 'reattaches' do
|
104
|
+
channel.attach do
|
105
|
+
channel.transition_state_machine :failed, RuntimeError.new
|
106
|
+
expect(channel).to be_failed
|
107
|
+
channel.attach do
|
108
|
+
expect(channel).to be_attached
|
109
|
+
stop_reactor
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'when state is :detaching' do
|
116
|
+
it 'moves straight to attaching and skips detached' do
|
117
|
+
channel.once(:detaching) do
|
118
|
+
channel.once(:detached) { raise 'Detach should not have been reached' }
|
119
|
+
|
120
|
+
channel.once(:attaching) do
|
121
|
+
channel.once(:attached) do
|
122
|
+
channel.off
|
123
|
+
stop_reactor
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
channel.attach
|
128
|
+
end
|
129
|
+
|
130
|
+
channel.attach do
|
131
|
+
channel.detach
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context 'with many connections and many channels on each simultaneously' do
|
137
|
+
let(:connection_count) { 30 }
|
138
|
+
let(:channel_count) { 10 }
|
139
|
+
let(:permutation_count) { connection_count * channel_count }
|
140
|
+
let(:channel_connection_ids) { [] }
|
141
|
+
|
142
|
+
it 'attaches all channels', em_timeout: 15 do
|
143
|
+
connection_count.times.map do
|
144
|
+
Ably::Realtime::Client.new(default_options)
|
145
|
+
end.each do |client|
|
146
|
+
channel_count.times.map do |index|
|
147
|
+
client.channel("channel-#{index}").attach do
|
148
|
+
channel_connection_ids << "#{client.connection.id}:#{index}"
|
149
|
+
next unless channel_connection_ids.count == permutation_count
|
150
|
+
|
151
|
+
expect(channel_connection_ids.uniq.count).to eql(permutation_count)
|
152
|
+
stop_reactor
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
context 'failure as a result of insufficient key permissions' do
|
160
|
+
let(:restricted_client) do
|
161
|
+
Ably::Realtime::Client.new(default_options.merge(api_key: restricted_api_key, log_level: :fatal))
|
162
|
+
end
|
163
|
+
let(:restricted_channel) { restricted_client.channel("cannot_subscribe") }
|
164
|
+
|
165
|
+
it 'triggers failed event' do
|
166
|
+
restricted_channel.attach
|
167
|
+
restricted_channel.on(:failed) do |error|
|
168
|
+
expect(restricted_channel.state).to eq(:failed)
|
169
|
+
expect(error.status).to eq(401)
|
170
|
+
stop_reactor
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'calls the errback of the returned Deferrable' do
|
175
|
+
restricted_channel.attach.errback do |channel, error|
|
176
|
+
expect(restricted_channel.state).to eq(:failed)
|
177
|
+
expect(error.status).to eq(401)
|
178
|
+
stop_reactor
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'triggers an error event' do
|
183
|
+
restricted_channel.attach
|
184
|
+
restricted_channel.on(:error) do |error|
|
185
|
+
expect(restricted_channel.state).to eq(:failed)
|
186
|
+
expect(error.status).to eq(401)
|
187
|
+
stop_reactor
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'updates the error_reason' do
|
192
|
+
restricted_channel.attach
|
193
|
+
restricted_channel.on(:failed) do
|
194
|
+
expect(restricted_channel.error_reason.status).to eq(401)
|
195
|
+
stop_reactor
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
describe '#detach' do
|
202
|
+
it 'detaches from a channel' do
|
203
|
+
channel.attach do
|
204
|
+
channel.detach
|
205
|
+
channel.on(:detached) do
|
206
|
+
expect(channel.state).to eq(:detached)
|
207
|
+
stop_reactor
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'detaches from a channel and calls the provided block' do
|
213
|
+
channel.attach do |chan|
|
214
|
+
chan.detach do |detached_chan|
|
215
|
+
expect(detached_chan.state).to eq(:detached)
|
216
|
+
stop_reactor
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
it 'emits :detaching then :detached events' do
|
222
|
+
channel.once(:detaching) do
|
223
|
+
channel.once(:detached) do
|
224
|
+
stop_reactor
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
channel.attach do
|
229
|
+
channel.detach
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'returns a Deferrable' do
|
234
|
+
expect(channel.attach).to be_a(EventMachine::Deferrable)
|
235
|
+
stop_reactor
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'calls the Deferrable callback on success' do
|
239
|
+
channel.attach do
|
240
|
+
channel.detach.callback do |channel|
|
241
|
+
expect(channel).to be_a(Ably::Realtime::Channel)
|
242
|
+
expect(channel.state).to eq(:detached)
|
22
243
|
stop_reactor
|
23
244
|
end
|
24
245
|
end
|
25
246
|
end
|
26
247
|
|
27
|
-
|
28
|
-
|
248
|
+
context 'when state is :failed' do
|
249
|
+
let(:client_options) { default_options.merge(log_level: :fatal) }
|
250
|
+
|
251
|
+
it 'raises an exception' do
|
29
252
|
channel.attach do
|
30
|
-
|
253
|
+
channel.transition_state_machine :failed, RuntimeError.new
|
254
|
+
expect(channel).to be_failed
|
255
|
+
expect { channel.detach }.to raise_error Ably::Exceptions::StateChangeError
|
31
256
|
stop_reactor
|
32
257
|
end
|
33
258
|
end
|
34
259
|
end
|
35
260
|
|
36
|
-
|
37
|
-
|
38
|
-
channel.
|
39
|
-
|
40
|
-
|
41
|
-
|
261
|
+
context 'when state is :attaching' do
|
262
|
+
it 'moves straight to :detaching state and skips :attached' do
|
263
|
+
channel.once(:attaching) do
|
264
|
+
channel.once(:attached) { raise 'Attached should never be reached' }
|
265
|
+
|
266
|
+
channel.once(:detaching) do
|
267
|
+
channel.once(:detached) do
|
268
|
+
stop_reactor
|
269
|
+
end
|
42
270
|
end
|
271
|
+
|
272
|
+
channel.detach
|
43
273
|
end
|
274
|
+
|
275
|
+
channel.attach
|
44
276
|
end
|
45
277
|
end
|
46
278
|
|
47
|
-
|
48
|
-
|
49
|
-
channel.
|
50
|
-
|
279
|
+
context 'when state is :detaching' do
|
280
|
+
it 'ignores subsequent #detach calls but calls the callback if provided' do
|
281
|
+
channel.once(:detaching) do
|
282
|
+
channel.detach
|
283
|
+
channel.once(:detached) do
|
284
|
+
channel.detach do
|
285
|
+
stop_reactor
|
286
|
+
end
|
287
|
+
end
|
51
288
|
end
|
52
|
-
|
53
|
-
|
54
|
-
|
289
|
+
|
290
|
+
channel.attach do
|
291
|
+
channel.detach
|
55
292
|
end
|
56
293
|
end
|
294
|
+
end
|
295
|
+
end
|
57
296
|
|
58
|
-
|
297
|
+
describe 'channel recovery in :attaching state' do
|
298
|
+
context 'the transport is disconnected before the ATTACHED protocol message is received' do
|
299
|
+
skip 'attach times out and fails if not ATTACHED protocol message received'
|
300
|
+
skip 'channel is ATTACHED if ATTACHED protocol message is later received'
|
301
|
+
skip 'sends an ATTACH protocol message in response to a channel message being received on the attaching channel'
|
59
302
|
end
|
303
|
+
end
|
60
304
|
|
61
|
-
|
62
|
-
|
63
|
-
|
305
|
+
context '#publish' do
|
306
|
+
context 'when attached' do
|
307
|
+
it 'publishes messages' do
|
308
|
+
channel.attach do
|
309
|
+
3.times { channel.publish('event', payload) }
|
310
|
+
end
|
64
311
|
channel.subscribe do |message|
|
65
|
-
messages << message if message.
|
66
|
-
stop_reactor if messages.
|
312
|
+
messages << message if message.data == payload
|
313
|
+
stop_reactor if messages.count == 3
|
67
314
|
end
|
68
315
|
end
|
69
|
-
|
70
|
-
expect(messages.count).to eql(3)
|
71
316
|
end
|
72
317
|
|
73
|
-
|
74
|
-
|
75
|
-
3.times { channel.publish('event',
|
318
|
+
context 'when not yet attached' do
|
319
|
+
it 'publishes queued messages once attached' do
|
320
|
+
3.times { channel.publish('event', random_str) }
|
76
321
|
channel.subscribe do |message|
|
77
322
|
messages << message if message.name == 'event'
|
78
|
-
stop_reactor if messages.
|
323
|
+
stop_reactor if messages.count == 3
|
79
324
|
end
|
80
325
|
end
|
81
326
|
|
82
|
-
|
83
|
-
|
327
|
+
it 'publishes queued messages within a single protocol message' do
|
328
|
+
3.times { channel.publish('event', random_str) }
|
329
|
+
channel.subscribe do |message|
|
330
|
+
messages << message if message.name == 'event'
|
331
|
+
next unless messages.length == 3
|
84
332
|
|
85
|
-
|
86
|
-
|
87
|
-
|
333
|
+
# All 3 messages should be batched into a single Protocol Message by the client library
|
334
|
+
# message.id = "{protocol_message.id}:{protocol_message_index}"
|
335
|
+
# Check that all messages share the same protocol_message.id
|
336
|
+
message_id = messages.map { |msg| msg.id.split(':')[0] }
|
337
|
+
expect(message_id.uniq.count).to eql(1)
|
88
338
|
|
89
|
-
|
90
|
-
|
91
|
-
|
339
|
+
# Check that messages use index 0,1,2 in the ID
|
340
|
+
message_indexes = messages.map { |msg| msg.id.split(':')[1] }
|
341
|
+
expect(message_indexes).to include("0", "1", "2")
|
342
|
+
stop_reactor
|
343
|
+
end
|
344
|
+
end
|
92
345
|
end
|
346
|
+
end
|
93
347
|
|
94
|
-
|
95
|
-
|
348
|
+
describe '#subscribe' do
|
349
|
+
context 'with an event argument' do
|
350
|
+
it 'subscribes for a single event' do
|
96
351
|
channel.subscribe('click') do |message|
|
97
|
-
|
352
|
+
expect(message.data).to eql('data')
|
353
|
+
stop_reactor
|
98
354
|
end
|
99
|
-
channel.
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
355
|
+
channel.publish('click', 'data')
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
context 'with no event argument' do
|
360
|
+
it 'subscribes for all events' do
|
361
|
+
channel.subscribe do |message|
|
362
|
+
expect(message.data).to eql('data')
|
363
|
+
stop_reactor
|
106
364
|
end
|
365
|
+
channel.publish('click', 'data')
|
107
366
|
end
|
108
367
|
end
|
109
368
|
|
110
|
-
|
111
|
-
|
112
|
-
click_callback =
|
369
|
+
context 'many times with different event names' do
|
370
|
+
it 'filters events accordingly to each callback' do
|
371
|
+
click_callback = proc { |message| messages << message }
|
113
372
|
|
114
373
|
channel.subscribe('click', &click_callback)
|
115
374
|
channel.subscribe('move', &click_callback)
|
116
375
|
channel.subscribe('press', &click_callback)
|
117
376
|
|
118
377
|
channel.attach do
|
119
|
-
channel.unsubscribe('click')
|
120
|
-
channel.unsubscribe('move', &click_callback)
|
121
|
-
channel.unsubscribe('press') { this_callback_is_not_subscribed_so_ignored }
|
122
|
-
|
123
378
|
channel.publish('click', 'data')
|
124
379
|
channel.publish('move', 'data')
|
125
|
-
channel.publish('press', 'data')
|
380
|
+
channel.publish('press', 'data') do
|
381
|
+
EventMachine.add_timer(2) do
|
382
|
+
expect(messages.count).to eql(3)
|
383
|
+
stop_reactor
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
describe '#unsubscribe' do
|
392
|
+
context 'with an event argument' do
|
393
|
+
it 'unsubscribes for a single event' do
|
394
|
+
channel.subscribe('click') { raise 'Should not have been called' }
|
395
|
+
channel.unsubscribe('click')
|
126
396
|
|
127
|
-
|
397
|
+
channel.publish('click', 'data') do
|
398
|
+
EventMachine.add_timer(1) do
|
128
399
|
stop_reactor
|
129
|
-
# Only the press subscribe callback should still be subscribed
|
130
|
-
expect(messages.length).to eql(1)
|
131
400
|
end
|
132
401
|
end
|
133
402
|
end
|
134
403
|
end
|
135
404
|
|
136
|
-
|
137
|
-
|
138
|
-
|
405
|
+
context 'with no event argument' do
|
406
|
+
it 'unsubscribes for a single event' do
|
407
|
+
channel.subscribe { raise 'Should not have been called' }
|
408
|
+
channel.unsubscribe
|
139
409
|
|
140
|
-
|
141
|
-
|
410
|
+
channel.publish('click', 'data') do
|
411
|
+
EventMachine.add_timer(1) do
|
412
|
+
stop_reactor
|
413
|
+
end
|
142
414
|
end
|
415
|
+
end
|
416
|
+
end
|
417
|
+
end
|
143
418
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
419
|
+
context 'when connection state changes to' do
|
420
|
+
context ':failed' do
|
421
|
+
let(:connection_error) { Ably::Exceptions::ConnectionError.new('forced failure', 500, 50000) }
|
422
|
+
let(:client_options) { default_options.merge(log_level: :none) }
|
423
|
+
|
424
|
+
def fake_error(error)
|
425
|
+
client.connection.manager.error_received_from_server error
|
426
|
+
end
|
427
|
+
|
428
|
+
context 'an :attached channel' do
|
429
|
+
it 'transitions state to :failed' do
|
430
|
+
channel.attach do
|
431
|
+
channel.on(:failed) do |error|
|
432
|
+
expect(error).to eql(connection_error)
|
433
|
+
stop_reactor
|
155
434
|
end
|
435
|
+
fake_error connection_error
|
156
436
|
end
|
157
437
|
end
|
158
438
|
|
159
|
-
|
160
|
-
|
161
|
-
|
439
|
+
it 'triggers an error event on the channel' do
|
440
|
+
channel.attach do
|
441
|
+
channel.on(:error) do |error|
|
442
|
+
expect(error).to eql(connection_error)
|
443
|
+
stop_reactor
|
444
|
+
end
|
445
|
+
fake_error connection_error
|
446
|
+
end
|
447
|
+
end
|
162
448
|
|
163
|
-
|
164
|
-
|
165
|
-
|
449
|
+
it 'updates the channel error_reason' do
|
450
|
+
channel.attach do
|
451
|
+
channel.on(:failed) do |error|
|
452
|
+
expect(channel.error_reason).to eql(connection_error)
|
453
|
+
stop_reactor
|
166
454
|
end
|
455
|
+
fake_error connection_error
|
167
456
|
end
|
168
457
|
end
|
169
458
|
end
|
170
|
-
end
|
171
459
|
|
172
|
-
|
173
|
-
|
174
|
-
|
460
|
+
context 'a :detached channel' do
|
461
|
+
it 'remains in the :detached state' do
|
462
|
+
channel.attach do
|
463
|
+
channel.on(:failed) { raise 'Failed state should not have been reached' }
|
464
|
+
channel.on(:error) { raise 'Error should not have been emitted' }
|
175
465
|
|
176
|
-
|
177
|
-
|
466
|
+
channel.detach do
|
467
|
+
EventMachine.add_timer(1) do
|
468
|
+
expect(channel).to be_detached
|
469
|
+
stop_reactor
|
470
|
+
end
|
471
|
+
|
472
|
+
fake_error connection_error
|
473
|
+
end
|
474
|
+
end
|
178
475
|
end
|
476
|
+
end
|
179
477
|
|
180
|
-
|
478
|
+
context 'a :failed channel' do
|
479
|
+
let(:original_error) { RuntimeError.new }
|
181
480
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
481
|
+
it 'remains in the :failed state and ignores the failure error' do
|
482
|
+
channel.attach do
|
483
|
+
channel.on(:error) do
|
484
|
+
channel.on(:failed) { raise 'Failed state should not have been reached' }
|
485
|
+
channel.on(:error) { raise 'Error should not have been emitted' }
|
486
|
+
|
487
|
+
EventMachine.add_timer(1) do
|
488
|
+
expect(channel).to be_failed
|
489
|
+
expect(channel.error_reason).to eql(original_error)
|
188
490
|
stop_reactor
|
189
491
|
end
|
492
|
+
|
493
|
+
fake_error connection_error
|
190
494
|
end
|
495
|
+
|
496
|
+
channel.transition_state_machine :failed, original_error
|
191
497
|
end
|
192
498
|
end
|
193
499
|
end
|
194
500
|
end
|
195
501
|
|
196
|
-
context '
|
197
|
-
|
198
|
-
|
502
|
+
context ':closed' do
|
503
|
+
context 'an :attached channel' do
|
504
|
+
it 'transitions state to :detached' do
|
505
|
+
channel.attach do
|
506
|
+
channel.on(:detached) do
|
507
|
+
stop_reactor
|
508
|
+
end
|
509
|
+
client.connection.close
|
510
|
+
end
|
511
|
+
end
|
199
512
|
end
|
200
|
-
let(:restricted_channel) { restricted_client.channel("cannot_subscribe") }
|
201
513
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
514
|
+
context 'a :detached channel' do
|
515
|
+
it 'remains in the :detached state' do
|
516
|
+
channel.attach do
|
517
|
+
channel.detach do
|
518
|
+
channel.on(:detached) { raise 'Detached state should not have been reached' }
|
519
|
+
channel.on(:error) { raise 'Error should not have been emitted' }
|
520
|
+
|
521
|
+
EventMachine.add_timer(1) do
|
522
|
+
expect(channel).to be_detached
|
523
|
+
stop_reactor
|
524
|
+
end
|
525
|
+
|
526
|
+
client.connection.close
|
527
|
+
end
|
528
|
+
end
|
529
|
+
end
|
530
|
+
end
|
531
|
+
|
532
|
+
context 'a :failed channel' do
|
533
|
+
let(:original_error) { RuntimeError.new }
|
534
|
+
let(:client_options) { default_options.merge(log_level: :fatal) }
|
535
|
+
|
536
|
+
it 'remains in the :failed state and retains the error_reason' do
|
537
|
+
channel.attach do
|
538
|
+
channel.once(:error) do
|
539
|
+
channel.on(:detached) { raise 'Detached state should not have been reached' }
|
540
|
+
channel.on(:error) { raise 'Error should not have been emitted' }
|
541
|
+
|
542
|
+
EventMachine.add_timer(1) do
|
543
|
+
expect(channel).to be_failed
|
544
|
+
expect(channel.error_reason).to eql(original_error)
|
545
|
+
stop_reactor
|
546
|
+
end
|
547
|
+
|
548
|
+
client.connection.close
|
549
|
+
end
|
550
|
+
|
551
|
+
channel.transition_state_machine :failed, original_error
|
209
552
|
end
|
210
553
|
end
|
211
554
|
end
|
@@ -213,5 +556,3 @@ describe Ably::Realtime::Channel do
|
|
213
556
|
end
|
214
557
|
end
|
215
558
|
end
|
216
|
-
|
217
|
-
|