ably 0.8.15 → 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.
- checksums.yaml +4 -4
- data/.travis.yml +6 -4
- data/CHANGELOG.md +6 -2
- data/README.md +5 -1
- data/SPEC.md +1473 -852
- data/ably.gemspec +11 -8
- data/lib/ably/auth.rb +90 -53
- data/lib/ably/exceptions.rb +37 -8
- data/lib/ably/logger.rb +10 -1
- data/lib/ably/models/auth_details.rb +42 -0
- data/lib/ably/models/channel_state_change.rb +18 -4
- data/lib/ably/models/connection_details.rb +6 -3
- data/lib/ably/models/connection_state_change.rb +4 -3
- data/lib/ably/models/error_info.rb +1 -1
- data/lib/ably/models/message.rb +17 -1
- data/lib/ably/models/message_encoders/base.rb +103 -82
- data/lib/ably/models/message_encoders/base64.rb +1 -1
- data/lib/ably/models/presence_message.rb +16 -1
- data/lib/ably/models/protocol_message.rb +20 -3
- data/lib/ably/models/token_details.rb +11 -1
- data/lib/ably/models/token_request.rb +16 -6
- data/lib/ably/modules/async_wrapper.rb +7 -3
- data/lib/ably/modules/encodeable.rb +51 -12
- data/lib/ably/modules/enum.rb +17 -7
- data/lib/ably/modules/event_emitter.rb +29 -14
- data/lib/ably/modules/model_common.rb +13 -21
- data/lib/ably/modules/state_emitter.rb +7 -4
- data/lib/ably/modules/state_machine.rb +2 -4
- data/lib/ably/modules/uses_state_machine.rb +7 -3
- data/lib/ably/realtime.rb +2 -0
- data/lib/ably/realtime/auth.rb +102 -42
- data/lib/ably/realtime/channel.rb +68 -26
- data/lib/ably/realtime/channel/channel_manager.rb +154 -65
- data/lib/ably/realtime/channel/channel_state_machine.rb +14 -15
- data/lib/ably/realtime/client.rb +18 -3
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +38 -29
- data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +6 -1
- data/lib/ably/realtime/connection.rb +108 -49
- data/lib/ably/realtime/connection/connection_manager.rb +167 -61
- data/lib/ably/realtime/connection/connection_state_machine.rb +22 -3
- data/lib/ably/realtime/connection/websocket_transport.rb +19 -10
- data/lib/ably/realtime/presence.rb +70 -45
- data/lib/ably/realtime/presence/members_map.rb +201 -36
- data/lib/ably/realtime/presence/presence_manager.rb +30 -6
- data/lib/ably/realtime/presence/presence_state_machine.rb +5 -12
- data/lib/ably/rest.rb +2 -2
- data/lib/ably/rest/channel.rb +5 -5
- data/lib/ably/rest/client.rb +31 -27
- data/lib/ably/rest/middleware/exceptions.rb +1 -3
- data/lib/ably/rest/middleware/logger.rb +2 -2
- data/lib/ably/rest/presence.rb +2 -2
- data/lib/ably/util/pub_sub.rb +1 -1
- data/lib/ably/util/safe_deferrable.rb +26 -0
- data/lib/ably/version.rb +2 -2
- data/spec/acceptance/realtime/auth_spec.rb +470 -111
- data/spec/acceptance/realtime/channel_history_spec.rb +5 -3
- data/spec/acceptance/realtime/channel_spec.rb +1017 -168
- data/spec/acceptance/realtime/client_spec.rb +6 -6
- data/spec/acceptance/realtime/connection_failures_spec.rb +458 -27
- data/spec/acceptance/realtime/connection_spec.rb +424 -105
- data/spec/acceptance/realtime/message_spec.rb +52 -23
- data/spec/acceptance/realtime/presence_history_spec.rb +5 -3
- data/spec/acceptance/realtime/presence_spec.rb +1110 -96
- data/spec/acceptance/rest/auth_spec.rb +222 -59
- data/spec/acceptance/rest/base_spec.rb +1 -1
- data/spec/acceptance/rest/channel_spec.rb +1 -2
- data/spec/acceptance/rest/client_spec.rb +104 -48
- data/spec/acceptance/rest/message_spec.rb +42 -15
- data/spec/acceptance/rest/presence_spec.rb +4 -11
- data/spec/rspec_config.rb +2 -1
- data/spec/shared/client_initializer_behaviour.rb +2 -2
- data/spec/shared/safe_deferrable_behaviour.rb +6 -2
- data/spec/spec_helper.rb +4 -2
- data/spec/support/debug_failure_helper.rb +20 -4
- data/spec/support/event_machine_helper.rb +32 -1
- data/spec/unit/auth_spec.rb +4 -11
- data/spec/unit/logger_spec.rb +28 -2
- data/spec/unit/models/auth_details_spec.rb +49 -0
- data/spec/unit/models/channel_state_change_spec.rb +23 -3
- data/spec/unit/models/connection_details_spec.rb +12 -1
- data/spec/unit/models/connection_state_change_spec.rb +15 -4
- data/spec/unit/models/message_encoders/base64_spec.rb +2 -1
- data/spec/unit/models/message_spec.rb +153 -0
- data/spec/unit/models/presence_message_spec.rb +192 -0
- data/spec/unit/models/protocol_message_spec.rb +64 -6
- data/spec/unit/models/token_details_spec.rb +75 -0
- data/spec/unit/models/token_request_spec.rb +74 -0
- data/spec/unit/modules/async_wrapper_spec.rb +2 -1
- data/spec/unit/modules/enum_spec.rb +69 -0
- data/spec/unit/modules/event_emitter_spec.rb +149 -22
- data/spec/unit/modules/state_emitter_spec.rb +9 -3
- data/spec/unit/realtime/client_spec.rb +1 -1
- data/spec/unit/realtime/connection_spec.rb +8 -5
- data/spec/unit/realtime/incoming_message_dispatcher_spec.rb +1 -1
- data/spec/unit/realtime/presence_spec.rb +4 -3
- data/spec/unit/rest/client_spec.rb +1 -1
- data/spec/unit/util/crypto_spec.rb +3 -3
- metadata +22 -19
@@ -206,9 +206,11 @@ describe Ably::Realtime::Channel, '#history', :event_machine do
|
|
206
206
|
end
|
207
207
|
end
|
208
208
|
|
209
|
-
it '
|
210
|
-
|
211
|
-
|
209
|
+
it 'fails the deferrable unless the state is attached' do
|
210
|
+
channel.history(until_attach: true).errback do |error|
|
211
|
+
expect(error.message).to match(/not attached/)
|
212
|
+
stop_reactor
|
213
|
+
end
|
212
214
|
end
|
213
215
|
end
|
214
216
|
end
|
@@ -7,11 +7,16 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
7
7
|
let(:client_options) { default_options }
|
8
8
|
|
9
9
|
let(:client) { auto_close Ably::Realtime::Client.new(client_options) }
|
10
|
+
let(:connection) { client.connection }
|
10
11
|
let(:channel_name) { random_str }
|
11
12
|
let(:payload) { random_str }
|
12
13
|
let(:channel) { client.channel(channel_name) }
|
13
14
|
let(:messages) { [] }
|
14
15
|
|
16
|
+
def disconnect_transport
|
17
|
+
connection.transport.unbind
|
18
|
+
end
|
19
|
+
|
15
20
|
describe 'initialization' do
|
16
21
|
context 'with :auto_connect option set to false on connection' do
|
17
22
|
let(:client) do
|
@@ -36,41 +41,98 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
36
41
|
end
|
37
42
|
|
38
43
|
describe '#attach' do
|
39
|
-
|
40
|
-
|
41
|
-
channel.once(:
|
42
|
-
|
44
|
+
context 'when initialized' do
|
45
|
+
it 'emits attaching then attached events' do
|
46
|
+
channel.once(:attaching) do
|
47
|
+
channel.once(:attached) do
|
48
|
+
stop_reactor
|
49
|
+
end
|
43
50
|
end
|
51
|
+
|
52
|
+
channel.attach
|
44
53
|
end
|
45
54
|
|
46
|
-
|
47
|
-
|
55
|
+
it 'ignores subsequent #attach calls but calls the success callback if provided' do
|
56
|
+
channel.once(:attaching) do
|
57
|
+
channel.attach
|
58
|
+
channel.once(:attached) do
|
59
|
+
channel.attach do
|
60
|
+
stop_reactor
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
48
64
|
|
49
|
-
it 'ignores subsequent #attach calls but calls the success callback if provided' do
|
50
|
-
channel.once(:attaching) do
|
51
65
|
channel.attach
|
52
|
-
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'attaches to a channel' do
|
69
|
+
channel.attach
|
70
|
+
channel.on(:attached) do
|
71
|
+
expect(channel.state).to eq(:attached)
|
72
|
+
stop_reactor
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'attaches to a channel and calls the provided block (#RTL4d)' do
|
77
|
+
channel.attach do
|
78
|
+
expect(channel.state).to eq(:attached)
|
79
|
+
stop_reactor
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'sends an ATTACH and waits for an ATTACHED (#RTL4c)' do
|
84
|
+
connection.once(:connected) do
|
85
|
+
attach_count = 0
|
86
|
+
attached_count = 0
|
87
|
+
test_complete = false
|
88
|
+
client.connection.__incoming_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
|
89
|
+
next if test_complete
|
90
|
+
attached_count += 1 if protocol_message.action == :attached
|
91
|
+
end
|
92
|
+
client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
|
93
|
+
next if test_complete
|
94
|
+
attach_count += 1 if protocol_message.action == :attach
|
95
|
+
end
|
53
96
|
channel.attach do
|
54
|
-
|
97
|
+
EventMachine.add_timer(1) do
|
98
|
+
test_complete = true
|
99
|
+
expect(attach_count).to eql(1)
|
100
|
+
expect(attached_count).to eql(1)
|
101
|
+
stop_reactor
|
102
|
+
end
|
55
103
|
end
|
56
104
|
end
|
57
105
|
end
|
58
106
|
|
59
|
-
channel
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
expect(channel.state).to eq(:attached)
|
66
|
-
stop_reactor
|
107
|
+
it 'implicitly attaches the channel (#RTL7c)' do
|
108
|
+
expect(channel).to be_initialized
|
109
|
+
channel.subscribe { |message| }
|
110
|
+
channel.once(:attached) do
|
111
|
+
stop_reactor
|
112
|
+
end
|
67
113
|
end
|
68
|
-
end
|
69
114
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
115
|
+
context 'when the implicit channel attach fails' do
|
116
|
+
let(:allowed_params) do
|
117
|
+
{ capability: { "*" => ["*"] } }
|
118
|
+
end
|
119
|
+
let(:not_allowed_params) do
|
120
|
+
{ capability: { "only_this_channel" => ["*"] } }
|
121
|
+
end
|
122
|
+
let(:client_options) { default_options.merge(default_token_params: not_allowed_params, use_token_auth: true, log_level: :fatal) }
|
123
|
+
|
124
|
+
it 'registers the listener anyway (#RTL7c)' do
|
125
|
+
channel.subscribe do |message|
|
126
|
+
stop_reactor
|
127
|
+
end
|
128
|
+
channel.once(:failed) do
|
129
|
+
client.auth.authorize(allowed_params) do
|
130
|
+
channel.attach do
|
131
|
+
channel.publish 'foo'
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
74
136
|
end
|
75
137
|
end
|
76
138
|
|
@@ -79,7 +141,7 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
79
141
|
stop_reactor
|
80
142
|
end
|
81
143
|
|
82
|
-
it 'calls the SafeDeferrable callback on success' do
|
144
|
+
it 'calls the SafeDeferrable callback on success (#RTL4d)' do
|
83
145
|
channel.attach.callback do
|
84
146
|
expect(channel).to be_a(Ably::Realtime::Channel)
|
85
147
|
expect(channel.state).to eq(:attached)
|
@@ -87,32 +149,92 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
87
149
|
end
|
88
150
|
end
|
89
151
|
|
152
|
+
context 'when an ATTACHED acknowledge is not received on the current connection' do
|
153
|
+
# As soon as the client sends the ATTACH on a CONNECTED connection
|
154
|
+
# simulate a transport failure that triggers the DISCONNECTED state twice
|
155
|
+
it 'sends another ATTACH each time the connection becomes connected' do
|
156
|
+
attached_messages = []
|
157
|
+
client.connection.__outgoing_protocol_msgbus__.on(:protocol_message) do |protocol_message|
|
158
|
+
if protocol_message.action == :attach
|
159
|
+
attached_messages << protocol_message
|
160
|
+
if attached_messages.count < 3
|
161
|
+
EventMachine.next_tick do
|
162
|
+
disconnect_transport
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
connection.once(:connected) do
|
169
|
+
connection.once(:disconnected) do
|
170
|
+
expect(attached_messages.count).to eql(1)
|
171
|
+
connection.once(:disconnected) do
|
172
|
+
expect(attached_messages.count).to eql(2)
|
173
|
+
connection.once(:connected) do
|
174
|
+
EventMachine.add_timer(0.1) do
|
175
|
+
expect(attached_messages.count).to eql(3)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
channel.attach
|
181
|
+
end
|
182
|
+
|
183
|
+
channel.once(:attached) do
|
184
|
+
EventMachine.add_timer(1) do
|
185
|
+
expect(attached_messages.count).to eql(3)
|
186
|
+
stop_reactor
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
context 'when state is :attached' do
|
193
|
+
it 'does nothing (#RTL4a)' do
|
194
|
+
channel.attach do
|
195
|
+
stopping = false
|
196
|
+
client.connection.__outgoing_protocol_msgbus__.once(:protocol_message) do |protocol_message|
|
197
|
+
raise "No outgoing messages should be sent as already ATTACHED" unless stopping
|
198
|
+
end
|
199
|
+
5.times do |index|
|
200
|
+
EventMachine.add_timer(0.2 * index) { channel.attach }
|
201
|
+
end
|
202
|
+
EventMachine.add_timer(1.5) do
|
203
|
+
stopping = true
|
204
|
+
stop_reactor
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
90
210
|
context 'when state is :failed' do
|
91
211
|
let(:client_options) { default_options.merge(log_level: :fatal) }
|
92
212
|
|
93
|
-
it 'reattaches' do
|
213
|
+
it 'reattaches and sets the errorReason to nil (#RTL4g)' do
|
94
214
|
channel.attach do
|
95
|
-
channel.once(:failed) do
|
96
|
-
expect(channel).to be_failed
|
97
|
-
channel.attach do
|
98
|
-
expect(channel).to be_attached
|
99
|
-
stop_reactor
|
100
|
-
end
|
101
|
-
end
|
102
215
|
channel.transition_state_machine :failed, reason: RuntimeError.new
|
216
|
+
expect(channel).to be_failed
|
217
|
+
expect(channel.error_reason).to_not be_nil
|
218
|
+
channel.attach do
|
219
|
+
expect(channel).to be_attached
|
220
|
+
expect(channel.error_reason).to be_nil
|
221
|
+
stop_reactor
|
222
|
+
end
|
103
223
|
end
|
104
224
|
end
|
105
225
|
end
|
106
226
|
|
107
227
|
context 'when state is :detaching' do
|
108
|
-
it '
|
228
|
+
it 'does the attach operation after the completion of the pending request (#RTL4h)' do
|
109
229
|
channel.once(:detaching) do
|
110
|
-
channel.once(:detached)
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
230
|
+
channel.once(:detached) do
|
231
|
+
channel.once(:attaching) do
|
232
|
+
channel.once(:attached) do
|
233
|
+
EventMachine.add_timer(1) do
|
234
|
+
expect(channel).to be_attached
|
235
|
+
stop_reactor
|
236
|
+
end
|
237
|
+
end
|
116
238
|
end
|
117
239
|
end
|
118
240
|
|
@@ -149,12 +271,21 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
149
271
|
end
|
150
272
|
|
151
273
|
context 'failure as a result of insufficient key permissions' do
|
274
|
+
let(:auth_options) do
|
275
|
+
default_options.merge(
|
276
|
+
key: restricted_api_key,
|
277
|
+
log_level: :fatal,
|
278
|
+
use_token_auth: true,
|
279
|
+
# TODO: Use wildcard / default when intersection issue resolved, realtime#780
|
280
|
+
default_token_params: { capability: { "canpublish:foo" => ["publish"] } }
|
281
|
+
)
|
282
|
+
end
|
152
283
|
let(:restricted_client) do
|
153
|
-
auto_close Ably::Realtime::Client.new(
|
284
|
+
auto_close Ably::Realtime::Client.new(auth_options)
|
154
285
|
end
|
155
|
-
let(:restricted_channel) { restricted_client.channel("
|
286
|
+
let(:restricted_channel) { restricted_client.channel("cansubscribe:foo") }
|
156
287
|
|
157
|
-
it 'emits failed event' do
|
288
|
+
it 'emits failed event (#RTL4e)' do
|
158
289
|
restricted_channel.attach
|
159
290
|
restricted_channel.on(:failed) do |connection_state|
|
160
291
|
expect(restricted_channel.state).to eq(:failed)
|
@@ -163,7 +294,7 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
163
294
|
end
|
164
295
|
end
|
165
296
|
|
166
|
-
it 'calls the errback of the returned Deferrable' do
|
297
|
+
it 'calls the errback of the returned Deferrable (#RTL4d)' do
|
167
298
|
restricted_channel.attach.errback do |error|
|
168
299
|
expect(restricted_channel.state).to eq(:failed)
|
169
300
|
expect(error.status).to eq(401)
|
@@ -171,15 +302,6 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
171
302
|
end
|
172
303
|
end
|
173
304
|
|
174
|
-
it 'emits an error event' do
|
175
|
-
restricted_channel.attach
|
176
|
-
restricted_channel.on(:error) do |error|
|
177
|
-
expect(restricted_channel.state).to eq(:failed)
|
178
|
-
expect(error.status).to eq(401)
|
179
|
-
stop_reactor
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
305
|
it 'updates the error_reason' do
|
184
306
|
restricted_channel.attach
|
185
307
|
restricted_channel.on(:failed) do
|
@@ -193,10 +315,8 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
193
315
|
restricted_channel.attach
|
194
316
|
restricted_channel.once(:failed) do
|
195
317
|
restricted_client.close do
|
196
|
-
|
197
|
-
restricted_client.auth.
|
198
|
-
|
199
|
-
restricted_client.connect do
|
318
|
+
token_params = { capability: { "cansubscribe:foo" => ["subscribe"] } }
|
319
|
+
restricted_client.auth.authorize(token_params) do
|
200
320
|
restricted_channel.once(:attached) do
|
201
321
|
expect(restricted_channel.error_reason).to be_nil
|
202
322
|
stop_reactor
|
@@ -208,98 +328,149 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
208
328
|
end
|
209
329
|
end
|
210
330
|
end
|
211
|
-
end
|
212
331
|
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
channel.
|
217
|
-
channel.on(:detached) do
|
218
|
-
expect(channel.state).to eq(:detached)
|
332
|
+
context 'with connection state' do
|
333
|
+
it 'is initialized (#RTL4i)' do
|
334
|
+
expect(connection).to be_initialized
|
335
|
+
channel.attach do
|
219
336
|
stop_reactor
|
220
337
|
end
|
221
338
|
end
|
222
|
-
end
|
223
339
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
340
|
+
it 'is connecting (#RTL4i)' do
|
341
|
+
connection.once(:connecting) do
|
342
|
+
channel.attach do
|
343
|
+
stop_reactor
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
it 'is disconnected (#RTL4i)' do
|
349
|
+
connection.once(:connected) do
|
350
|
+
connection.once(:disconnected) do
|
351
|
+
channel.attach do
|
352
|
+
stop_reactor
|
353
|
+
end
|
354
|
+
end
|
355
|
+
disconnect_transport
|
230
356
|
end
|
231
357
|
end
|
232
358
|
end
|
359
|
+
end
|
233
360
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
361
|
+
describe '#detach' do
|
362
|
+
context 'when state is :attached' do
|
363
|
+
it 'it detaches from a channel (#RTL5d)' do
|
364
|
+
channel.attach do
|
365
|
+
channel.detach
|
366
|
+
channel.on(:detached) do
|
367
|
+
expect(channel.state).to eq(:detached)
|
368
|
+
stop_reactor
|
369
|
+
end
|
238
370
|
end
|
239
371
|
end
|
240
372
|
|
241
|
-
channel
|
242
|
-
channel.
|
373
|
+
it 'detaches from a channel and calls the provided block (#RTL5d, #RTL5e)' do
|
374
|
+
channel.attach do
|
375
|
+
expect(channel.state).to eq(:attached)
|
376
|
+
channel.detach do
|
377
|
+
expect(channel.state).to eq(:detached)
|
378
|
+
stop_reactor
|
379
|
+
end
|
380
|
+
end
|
243
381
|
end
|
244
|
-
end
|
245
382
|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
383
|
+
it 'emits :detaching then :detached events' do
|
384
|
+
channel.once(:detaching) do
|
385
|
+
channel.once(:detached) do
|
386
|
+
stop_reactor
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
channel.attach do
|
391
|
+
channel.detach
|
392
|
+
end
|
250
393
|
end
|
251
|
-
end
|
252
394
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
expect(channel).to be_a(Ably::Realtime::Channel)
|
257
|
-
expect(channel.state).to eq(:detached)
|
395
|
+
it 'returns a SafeDeferrable that catches exceptions in callbacks and logs them' do
|
396
|
+
channel.attach do
|
397
|
+
expect(channel.detach).to be_a(Ably::Util::SafeDeferrable)
|
258
398
|
stop_reactor
|
259
399
|
end
|
260
400
|
end
|
401
|
+
|
402
|
+
it 'calls the Deferrable callback on success' do
|
403
|
+
channel.attach do
|
404
|
+
channel.detach.callback do
|
405
|
+
expect(channel).to be_a(Ably::Realtime::Channel)
|
406
|
+
expect(channel.state).to eq(:detached)
|
407
|
+
stop_reactor
|
408
|
+
end
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
context 'and DETACHED message is not received within realtime request timeout' do
|
413
|
+
let(:request_timeout) { 2 }
|
414
|
+
let(:client_options) { default_options.merge(realtime_request_timeout: request_timeout) }
|
415
|
+
|
416
|
+
it 'fails the deferrable and returns to the previous state (#RTL5f, #RTL5e)' do
|
417
|
+
channel.attach do
|
418
|
+
# don't process any incoming ProtocolMessages so the channel never becomes detached
|
419
|
+
connection.__incoming_protocol_msgbus__.unsubscribe
|
420
|
+
detached_requested_at = Time.now.to_i
|
421
|
+
channel.detach do
|
422
|
+
raise "The detach should not succeed if no incoming protocol messages are processed"
|
423
|
+
end.errback do
|
424
|
+
expect(channel).to be_attached
|
425
|
+
expect(Time.now.to_i - detached_requested_at).to be_within(1).of(request_timeout)
|
426
|
+
stop_reactor
|
427
|
+
end
|
428
|
+
end
|
429
|
+
end
|
430
|
+
end
|
261
431
|
end
|
262
432
|
|
263
433
|
context 'when state is :failed' do
|
264
434
|
let(:client_options) { default_options.merge(log_level: :fatal) }
|
265
435
|
|
266
|
-
it '
|
436
|
+
it 'fails the deferrable (#RTL5b)' do
|
267
437
|
channel.attach do
|
268
438
|
channel.transition_state_machine :failed, reason: RuntimeError.new
|
269
439
|
expect(channel).to be_failed
|
270
|
-
|
271
|
-
|
440
|
+
channel.detach.errback do |error|
|
441
|
+
expect(error).to be_a(Ably::Exceptions::InvalidStateChange)
|
442
|
+
stop_reactor
|
443
|
+
end
|
272
444
|
end
|
273
445
|
end
|
274
446
|
end
|
275
447
|
|
276
448
|
context 'when state is :attaching' do
|
277
|
-
it '
|
278
|
-
|
279
|
-
channel.once(:
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
449
|
+
it 'waits for the attach to complete and then moves to detached' do
|
450
|
+
connection.once(:connected) do
|
451
|
+
channel.once(:attaching) do
|
452
|
+
reached_attached = false
|
453
|
+
channel.once(:attached) do
|
454
|
+
channel.once(:detached) do
|
455
|
+
stop_reactor
|
456
|
+
end
|
284
457
|
end
|
458
|
+
channel.detach
|
285
459
|
end
|
286
|
-
|
287
|
-
channel.detach
|
460
|
+
channel.attach
|
288
461
|
end
|
289
|
-
|
290
|
-
channel.attach
|
291
462
|
end
|
292
463
|
end
|
293
464
|
|
294
465
|
context 'when state is :detaching' do
|
295
|
-
it 'ignores subsequent #detach calls but calls the callback if provided' do
|
466
|
+
it 'ignores subsequent #detach calls but calls the callback if provided (#RTL5i)' do
|
296
467
|
channel.once(:detaching) do
|
297
|
-
channel.detach
|
298
468
|
channel.once(:detached) do
|
299
469
|
channel.detach do
|
300
470
|
stop_reactor
|
301
471
|
end
|
302
472
|
end
|
473
|
+
channel.detach
|
303
474
|
end
|
304
475
|
|
305
476
|
channel.attach do
|
@@ -308,8 +479,28 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
308
479
|
end
|
309
480
|
end
|
310
481
|
|
482
|
+
context 'when state is :suspended' do
|
483
|
+
it 'moves the channel state immediately to DETACHED state (#RTL5j)' do
|
484
|
+
channel.attach do
|
485
|
+
channel.once(:suspended) do
|
486
|
+
channel.on do |channel_state_change|
|
487
|
+
expect(channel_state_change.current).to eq(:detached)
|
488
|
+
expect(channel.state).to eq(:detached)
|
489
|
+
EventMachine.add_timer(1) do
|
490
|
+
stop_reactor
|
491
|
+
end
|
492
|
+
end
|
493
|
+
EventMachine.next_tick do
|
494
|
+
channel.detach
|
495
|
+
end
|
496
|
+
end
|
497
|
+
channel.transition_state_machine :suspended
|
498
|
+
end
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
311
502
|
context 'when state is :initialized' do
|
312
|
-
it 'does nothing as there is no channel to detach' do
|
503
|
+
it 'does nothing as there is no channel to detach (#RTL5a)' do
|
313
504
|
expect(channel).to be_initialized
|
314
505
|
channel.detach do
|
315
506
|
expect(channel).to be_initialized
|
@@ -325,14 +516,210 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
325
516
|
end
|
326
517
|
end
|
327
518
|
end
|
519
|
+
|
520
|
+
context 'when state is :detached' do
|
521
|
+
it 'does nothing as the channel is detached (#RTL5a)' do
|
522
|
+
channel.attach do
|
523
|
+
channel.detach do
|
524
|
+
expect(channel).to be_detached
|
525
|
+
channel.on do
|
526
|
+
raise "Channel state should not change when calling detached if already detached"
|
527
|
+
end
|
528
|
+
channel.detach do
|
529
|
+
EventMachine.add_timer(1) { stop_reactor }
|
530
|
+
end
|
531
|
+
end
|
532
|
+
end
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
context 'when connection state is' do
|
537
|
+
context 'closing' do
|
538
|
+
it 'fails the deferrable (#RTL5b)' do
|
539
|
+
connection.once(:connected) do
|
540
|
+
channel.attach do
|
541
|
+
connection.once(:closing) do
|
542
|
+
channel.detach.errback do |error|
|
543
|
+
expect(error).to be_a(Ably::Exceptions::InvalidStateChange)
|
544
|
+
stop_reactor
|
545
|
+
end
|
546
|
+
end
|
547
|
+
connection.close
|
548
|
+
end
|
549
|
+
end
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
context 'failed and channel is failed' do
|
554
|
+
let(:client_options) do
|
555
|
+
default_options.merge(log_level: :none)
|
556
|
+
end
|
557
|
+
|
558
|
+
it 'fails the deferrable (#RTL5b)' do
|
559
|
+
connection.once(:connected) do
|
560
|
+
channel.attach do
|
561
|
+
connection.once(:failed) do
|
562
|
+
expect(channel).to be_failed
|
563
|
+
channel.detach.errback do |error|
|
564
|
+
expect(error).to be_a(Ably::Exceptions::InvalidStateChange)
|
565
|
+
stop_reactor
|
566
|
+
end
|
567
|
+
end
|
568
|
+
error = Ably::Exceptions::ConnectionFailed.new('forced failure', 500, 50000)
|
569
|
+
client.connection.manager.error_received_from_server error
|
570
|
+
end
|
571
|
+
end
|
572
|
+
end
|
573
|
+
end
|
574
|
+
|
575
|
+
context 'failed and channel is detached' do
|
576
|
+
let(:client_options) do
|
577
|
+
default_options.merge(log_level: :none)
|
578
|
+
end
|
579
|
+
|
580
|
+
it 'fails the deferrable (#RTL5b)' do
|
581
|
+
connection.once(:connected) do
|
582
|
+
channel.attach do
|
583
|
+
channel.detach do
|
584
|
+
connection.once(:failed) do
|
585
|
+
expect(channel).to be_detached
|
586
|
+
channel.detach.errback do |error|
|
587
|
+
expect(error).to be_a(Ably::Exceptions::InvalidStateChange)
|
588
|
+
stop_reactor
|
589
|
+
end
|
590
|
+
end
|
591
|
+
error = Ably::Exceptions::ConnectionFailed.new('forced failure', 500, 50000)
|
592
|
+
client.connection.manager.error_received_from_server error
|
593
|
+
end
|
594
|
+
end
|
595
|
+
end
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
599
|
+
context 'initialized' do
|
600
|
+
it 'does the detach operation once the connection state is connected (#RTL5h)' do
|
601
|
+
expect(connection).to be_initialized
|
602
|
+
channel.attach
|
603
|
+
channel.detach
|
604
|
+
connection.once(:connected) do
|
605
|
+
channel.once(:attached) do
|
606
|
+
channel.once(:detached) do
|
607
|
+
stop_reactor
|
608
|
+
end
|
609
|
+
end
|
610
|
+
end
|
611
|
+
end
|
612
|
+
end
|
613
|
+
|
614
|
+
context 'connecting' do
|
615
|
+
it 'does the detach operation once the connection state is connected (#RTL5h)' do
|
616
|
+
connection.once(:connecting) do
|
617
|
+
channel.attach
|
618
|
+
channel.detach
|
619
|
+
connection.once(:connected) do
|
620
|
+
channel.once(:attached) do
|
621
|
+
channel.once(:detached) do
|
622
|
+
stop_reactor
|
623
|
+
end
|
624
|
+
end
|
625
|
+
end
|
626
|
+
end
|
627
|
+
end
|
628
|
+
end
|
629
|
+
|
630
|
+
context 'disconnected' do
|
631
|
+
let(:client_options) do
|
632
|
+
default_options.merge(log_level: :fatal)
|
633
|
+
end
|
634
|
+
it 'does the detach operation once the connection state is connected (#RTL5h)' do
|
635
|
+
connection.once(:connected) do
|
636
|
+
connection.once(:disconnected) do
|
637
|
+
channel.attach
|
638
|
+
channel.detach
|
639
|
+
connection.once(:connected) do
|
640
|
+
channel.once(:attached) do
|
641
|
+
channel.once(:detached) do
|
642
|
+
stop_reactor
|
643
|
+
end
|
644
|
+
end
|
645
|
+
end
|
646
|
+
end
|
647
|
+
disconnect_transport
|
648
|
+
end
|
649
|
+
end
|
650
|
+
end
|
651
|
+
end
|
328
652
|
end
|
329
653
|
|
330
|
-
describe 'channel recovery
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
skip 'sends an ATTACH protocol message in response to a channel message being received on the attaching channel'
|
654
|
+
describe 'automatic channel recovery' do
|
655
|
+
let(:realtime_request_timeout) { 2 }
|
656
|
+
let(:client_options) do
|
657
|
+
default_options.merge(realtime_request_timeout: 2, log_level: :fatal)
|
335
658
|
end
|
659
|
+
|
660
|
+
context 'when an ATTACH request times out' do
|
661
|
+
it 'moves to the SUSPENDED state (#RTL4f)' do
|
662
|
+
connection.once(:connected) do
|
663
|
+
attach_request_sent_at = Time.now
|
664
|
+
channel.attach
|
665
|
+
client.connection.__incoming_protocol_msgbus__.unsubscribe
|
666
|
+
channel.once(:suspended) do
|
667
|
+
expect(attach_request_sent_at.to_i).to be_within(realtime_request_timeout + 1).of(Time.now.to_i)
|
668
|
+
stop_reactor
|
669
|
+
end
|
670
|
+
end
|
671
|
+
end
|
672
|
+
end
|
673
|
+
|
674
|
+
context 'if a subsequent ATTACHED is received on an ATTACHED channel' do
|
675
|
+
it 'ignores the additional ATTACHED if resumed is true (#RTL12)' do
|
676
|
+
channel.attach do
|
677
|
+
channel.once do |obj|
|
678
|
+
fail "No state change expected: #{obj}"
|
679
|
+
end
|
680
|
+
attached_message = Ably::Models::ProtocolMessage.new(action: 11, channel: channel_name, flags: 4) # ATTACHED with resumed flag
|
681
|
+
client.connection.__incoming_protocol_msgbus__.publish :protocol_message, attached_message
|
682
|
+
EventMachine.add_timer(1) do
|
683
|
+
channel.off
|
684
|
+
stop_reactor
|
685
|
+
end
|
686
|
+
end
|
687
|
+
end
|
688
|
+
|
689
|
+
it 'emits an UPDATE only when resumed is true (#RTL12)' do
|
690
|
+
channel.attach do
|
691
|
+
expect(channel.error_reason).to be_nil
|
692
|
+
channel.on(:update) do |state_change|
|
693
|
+
expect(state_change.current).to eq(:attached)
|
694
|
+
expect(state_change.previous).to eq(:attached)
|
695
|
+
expect(state_change.resumed).to be_falsey
|
696
|
+
expect(state_change.reason).to be_nil
|
697
|
+
expect(channel.error_reason).to be_nil
|
698
|
+
stop_reactor
|
699
|
+
end
|
700
|
+
attached_message = Ably::Models::ProtocolMessage.new(action: 11, channel: channel_name, flags: 0) # No resumed flag
|
701
|
+
client.connection.__incoming_protocol_msgbus__.publish :protocol_message, attached_message
|
702
|
+
end
|
703
|
+
end
|
704
|
+
|
705
|
+
it 'emits an UPDATE when resumed is true and includes the reason error from the ProtocolMessage (#RTL12)' do
|
706
|
+
channel.attach do
|
707
|
+
expect(channel.error_reason).to be_nil
|
708
|
+
channel.on(:update) do |state_change|
|
709
|
+
expect(state_change.current).to eq(:attached)
|
710
|
+
expect(state_change.previous).to eq(:attached)
|
711
|
+
expect(state_change.resumed).to be_falsey
|
712
|
+
expect(state_change.reason.code).to eql(50505)
|
713
|
+
expect(channel.error_reason.code).to eql(50505)
|
714
|
+
stop_reactor
|
715
|
+
end
|
716
|
+
attached_message = Ably::Models::ProtocolMessage.new(action: 11, channel: channel_name, error: { code: 50505 }, flags: 0) # No resumed flag with error
|
717
|
+
client.connection.__incoming_protocol_msgbus__.publish :protocol_message, attached_message
|
718
|
+
end
|
719
|
+
end
|
720
|
+
end
|
721
|
+
|
722
|
+
# skip 'sends an ATTACH protocol message in response to a channel message being received on the attaching channel'
|
336
723
|
end
|
337
724
|
|
338
725
|
context '#publish' do
|
@@ -369,7 +756,7 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
369
756
|
# All 3 messages should be batched into a single Protocol Message by the client library
|
370
757
|
# message.id = "{protocol_message.id}:{protocol_message_index}"
|
371
758
|
# Check that all messages share the same protocol_message.id
|
372
|
-
message_id = messages.map { |msg| msg.id.split(':')[0] }
|
759
|
+
message_id = messages.map { |msg| msg.id.split(':')[0...-1].join(':') }
|
373
760
|
expect(message_id.uniq.count).to eql(1)
|
374
761
|
|
375
762
|
# Check that messages use index 0,1,2 in the ID
|
@@ -383,32 +770,38 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
383
770
|
let(:client_options) { default_options.merge(queue_messages: false) }
|
384
771
|
|
385
772
|
context 'and connection state initialized' do
|
386
|
-
it '
|
387
|
-
expect { channel.publish('event') }.to raise_error Ably::Exceptions::MessageQueueingDisabled
|
773
|
+
it 'fails the deferrable' do
|
388
774
|
expect(client.connection).to be_initialized
|
389
|
-
|
775
|
+
channel.publish('event').errback do |error|
|
776
|
+
expect(error).to be_a(Ably::Exceptions::MessageQueueingDisabled)
|
777
|
+
stop_reactor
|
778
|
+
end
|
390
779
|
end
|
391
780
|
end
|
392
781
|
|
393
782
|
context 'and connection state connecting' do
|
394
|
-
it '
|
783
|
+
it 'fails the deferrable' do
|
395
784
|
client.connect
|
396
785
|
EventMachine.next_tick do
|
397
|
-
expect { channel.publish('event') }.to raise_error Ably::Exceptions::MessageQueueingDisabled
|
398
786
|
expect(client.connection).to be_connecting
|
399
|
-
|
787
|
+
channel.publish('event').errback do |error|
|
788
|
+
expect(error).to be_a(Ably::Exceptions::MessageQueueingDisabled)
|
789
|
+
stop_reactor
|
790
|
+
end
|
400
791
|
end
|
401
792
|
end
|
402
793
|
end
|
403
794
|
|
404
795
|
context 'and connection state disconnected' do
|
405
|
-
let(:client_options) { default_options.merge(queue_messages: false
|
406
|
-
it '
|
796
|
+
let(:client_options) { default_options.merge(queue_messages: false) }
|
797
|
+
it 'fails the deferrable' do
|
407
798
|
client.connection.once(:connected) do
|
408
799
|
client.connection.once(:disconnected) do
|
409
|
-
expect { channel.publish('event') }.to raise_error Ably::Exceptions::MessageQueueingDisabled
|
410
800
|
expect(client.connection).to be_disconnected
|
411
|
-
|
801
|
+
channel.publish('event').errback do |error|
|
802
|
+
expect(error).to be_a(Ably::Exceptions::MessageQueueingDisabled)
|
803
|
+
stop_reactor
|
804
|
+
end
|
412
805
|
end
|
413
806
|
client.connection.transition_state_machine :disconnected
|
414
807
|
end
|
@@ -449,6 +842,15 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
449
842
|
end
|
450
843
|
end
|
451
844
|
end
|
845
|
+
|
846
|
+
context 'and additional invalid attributes' do
|
847
|
+
let(:client_id) { 1 }
|
848
|
+
|
849
|
+
it 'throws an exception' do
|
850
|
+
expect { channel.publish([name: 'event', client_id: 1]) }.to raise_error ArgumentError, /client_id must be a String/
|
851
|
+
stop_reactor
|
852
|
+
end
|
853
|
+
end
|
452
854
|
end
|
453
855
|
|
454
856
|
context 'with an array of Hash objects with :name and :data attributes' do
|
@@ -698,6 +1100,13 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
698
1100
|
end
|
699
1101
|
end
|
700
1102
|
|
1103
|
+
context 'with a non-String client_id in the message' do
|
1104
|
+
it 'throws an exception' do
|
1105
|
+
expect { channel.publish([name: 'event', client_id: 1]) }.to raise_error ArgumentError, /client_id must be a String/
|
1106
|
+
stop_reactor
|
1107
|
+
end
|
1108
|
+
end
|
1109
|
+
|
701
1110
|
context 'with an empty client_id in the message' do
|
702
1111
|
it 'succeeds and publishes without a client_id' do
|
703
1112
|
channel.publish([name: 'event', client_id: nil]).tap do |deferrable|
|
@@ -915,7 +1324,9 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
915
1324
|
|
916
1325
|
it 'logs the error and continues' do
|
917
1326
|
emitted_exception = false
|
918
|
-
expect(client.logger).to receive(:error)
|
1327
|
+
expect(client.logger).to receive(:error) do |*args, &block|
|
1328
|
+
expect(args.concat([block ? block.call : nil]).join(',')).to match(/#{exception.message}/)
|
1329
|
+
end
|
919
1330
|
channel.subscribe('click') do |message|
|
920
1331
|
emitted_exception = true
|
921
1332
|
raise exception
|
@@ -988,36 +1399,42 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
988
1399
|
client.connection.manager.error_received_from_server error
|
989
1400
|
end
|
990
1401
|
|
991
|
-
context 'an :
|
992
|
-
it 'transitions state to :failed' do
|
993
|
-
|
994
|
-
channel.
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
1402
|
+
context 'an :attaching channel' do
|
1403
|
+
it 'transitions state to :failed (#RTL3a)' do
|
1404
|
+
connection.once(:connected) do
|
1405
|
+
channel.once(:attaching) do
|
1406
|
+
channel.on(:failed) do |connection_state_change|
|
1407
|
+
error = connection_state_change.reason
|
1408
|
+
expect(error).to be_a(Ably::Exceptions::ConnectionFailed)
|
1409
|
+
expect(error.code).to eql(50000)
|
1410
|
+
stop_reactor
|
1411
|
+
end
|
1412
|
+
fake_error connection_error
|
999
1413
|
end
|
1000
|
-
|
1414
|
+
channel.attach
|
1001
1415
|
end
|
1002
1416
|
end
|
1417
|
+
end
|
1003
1418
|
|
1004
|
-
|
1419
|
+
context 'an :attached channel' do
|
1420
|
+
it 'transitions state to :failed (#RTL3a)' do
|
1005
1421
|
channel.attach do
|
1006
|
-
channel.on(:
|
1422
|
+
channel.on(:failed) do |connection_state_change|
|
1423
|
+
error = connection_state_change.reason
|
1007
1424
|
expect(error).to be_a(Ably::Exceptions::ConnectionFailed)
|
1008
|
-
expect(error.code).to eql(
|
1425
|
+
expect(error.code).to eql(50000)
|
1009
1426
|
stop_reactor
|
1010
1427
|
end
|
1011
1428
|
fake_error connection_error
|
1012
1429
|
end
|
1013
1430
|
end
|
1014
1431
|
|
1015
|
-
it 'updates the channel error_reason' do
|
1432
|
+
it 'updates the channel error_reason (#RTL3a)' do
|
1016
1433
|
channel.attach do
|
1017
1434
|
channel.on(:failed) do |connection_state_change|
|
1018
1435
|
error = connection_state_change.reason
|
1019
1436
|
expect(error).to be_a(Ably::Exceptions::ConnectionFailed)
|
1020
|
-
expect(error.code).to eql(
|
1437
|
+
expect(error.code).to eql(50000)
|
1021
1438
|
stop_reactor
|
1022
1439
|
end
|
1023
1440
|
fake_error connection_error
|
@@ -1026,10 +1443,9 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
1026
1443
|
end
|
1027
1444
|
|
1028
1445
|
context 'a :detached channel' do
|
1029
|
-
it 'remains in the :detached state' do
|
1446
|
+
it 'remains in the :detached state (#RTL3a)' do
|
1030
1447
|
channel.attach do
|
1031
1448
|
channel.on(:failed) { raise 'Failed state should not have been reached' }
|
1032
|
-
channel.on(:error) { raise 'Error should not have been emitted' }
|
1033
1449
|
|
1034
1450
|
channel.detach do
|
1035
1451
|
EventMachine.add_timer(1) do
|
@@ -1046,11 +1462,10 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
1046
1462
|
context 'a :failed channel' do
|
1047
1463
|
let(:original_error) { RuntimeError.new }
|
1048
1464
|
|
1049
|
-
it 'remains in the :failed state and ignores the failure error' do
|
1465
|
+
it 'remains in the :failed state and ignores the failure error (#RTL3a)' do
|
1050
1466
|
channel.attach do
|
1051
|
-
channel.on(:
|
1467
|
+
channel.on(:failed) do
|
1052
1468
|
channel.on(:failed) { raise 'Failed state should not have been reached' }
|
1053
|
-
channel.on(:error) { raise 'Error should not have been emitted' }
|
1054
1469
|
|
1055
1470
|
EventMachine.add_timer(1) do
|
1056
1471
|
expect(channel).to be_failed
|
@@ -1067,11 +1482,13 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
1067
1482
|
end
|
1068
1483
|
|
1069
1484
|
context 'a channel ATTACH request' do
|
1070
|
-
it '
|
1485
|
+
it 'fails the deferrable (#RTL4b)' do
|
1071
1486
|
client.connect do
|
1072
1487
|
client.connection.once(:failed) do
|
1073
|
-
|
1074
|
-
|
1488
|
+
channel.attach.errback do |error|
|
1489
|
+
expect(error).to be_a(Ably::Exceptions::InvalidStateChange)
|
1490
|
+
stop_reactor
|
1491
|
+
end
|
1075
1492
|
end
|
1076
1493
|
fake_error connection_error
|
1077
1494
|
end
|
@@ -1081,7 +1498,7 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
1081
1498
|
|
1082
1499
|
context ':closed' do
|
1083
1500
|
context 'an :attached channel' do
|
1084
|
-
it 'transitions state to :detached' do
|
1501
|
+
it 'transitions state to :detached (#RTL3b)' do
|
1085
1502
|
channel.attach do
|
1086
1503
|
channel.on(:detached) do
|
1087
1504
|
stop_reactor
|
@@ -1091,12 +1508,26 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
1091
1508
|
end
|
1092
1509
|
end
|
1093
1510
|
|
1511
|
+
context 'an :attaching channel (#RTL3b)' do
|
1512
|
+
it 'transitions state to :detached' do
|
1513
|
+
channel.on(:attaching) do
|
1514
|
+
channel.on(:detached) do
|
1515
|
+
stop_reactor
|
1516
|
+
end
|
1517
|
+
client.connection.__incoming_protocol_msgbus__.unsubscribe
|
1518
|
+
client.connection.close
|
1519
|
+
closed_message = Ably::Models::ProtocolMessage.new(action: 8) # CLOSED
|
1520
|
+
client.connection.__incoming_protocol_msgbus__.publish :protocol_message, closed_message
|
1521
|
+
end
|
1522
|
+
channel.attach
|
1523
|
+
end
|
1524
|
+
end
|
1525
|
+
|
1094
1526
|
context 'a :detached channel' do
|
1095
|
-
it 'remains in the :detached state' do
|
1527
|
+
it 'remains in the :detached state (#RTL3b)' do
|
1096
1528
|
channel.attach do
|
1097
1529
|
channel.detach do
|
1098
1530
|
channel.on(:detached) { raise 'Detached state should not have been reached' }
|
1099
|
-
channel.on(:error) { raise 'Error should not have been emitted' }
|
1100
1531
|
|
1101
1532
|
EventMachine.add_timer(1) do
|
1102
1533
|
expect(channel).to be_detached
|
@@ -1113,11 +1544,10 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
1113
1544
|
let(:client_options) { default_options.merge(log_level: :fatal) }
|
1114
1545
|
let(:original_error) { Ably::Models::ErrorInfo.new(message: 'Error') }
|
1115
1546
|
|
1116
|
-
it 'remains in the :failed state and retains the error_reason' do
|
1547
|
+
it 'remains in the :failed state and retains the error_reason (#RTL3b)' do
|
1117
1548
|
channel.attach do
|
1118
|
-
channel.once(:
|
1549
|
+
channel.once(:failed) do
|
1119
1550
|
channel.on(:detached) { raise 'Detached state should not have been reached' }
|
1120
|
-
channel.on(:error) { raise 'Error should not have been emitted' }
|
1121
1551
|
|
1122
1552
|
EventMachine.add_timer(1) do
|
1123
1553
|
expect(channel).to be_failed
|
@@ -1134,11 +1564,13 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
1134
1564
|
end
|
1135
1565
|
|
1136
1566
|
context 'a channel ATTACH request when connection CLOSED' do
|
1137
|
-
it '
|
1567
|
+
it 'fails the deferrable (#RTL4b)' do
|
1138
1568
|
client.connect do
|
1139
1569
|
client.connection.once(:closed) do
|
1140
|
-
|
1141
|
-
|
1570
|
+
channel.attach.errback do |error|
|
1571
|
+
expect(error).to be_a(Ably::Exceptions::InvalidStateChange)
|
1572
|
+
stop_reactor
|
1573
|
+
end
|
1142
1574
|
end
|
1143
1575
|
client.close
|
1144
1576
|
end
|
@@ -1146,11 +1578,13 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
1146
1578
|
end
|
1147
1579
|
|
1148
1580
|
context 'a channel ATTACH request when connection CLOSING' do
|
1149
|
-
it '
|
1581
|
+
it 'fails the deferrable (#RTL4b)' do
|
1150
1582
|
client.connect do
|
1151
1583
|
client.connection.once(:closing) do
|
1152
|
-
|
1153
|
-
|
1584
|
+
channel.attach.errback do |error|
|
1585
|
+
expect(error).to be_a(Ably::Exceptions::InvalidStateChange)
|
1586
|
+
stop_reactor
|
1587
|
+
end
|
1154
1588
|
end
|
1155
1589
|
client.close
|
1156
1590
|
end
|
@@ -1159,25 +1593,48 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
1159
1593
|
end
|
1160
1594
|
|
1161
1595
|
context ':suspended' do
|
1162
|
-
context 'an :
|
1163
|
-
|
1596
|
+
context 'an :attaching channel' do
|
1597
|
+
it 'transitions state to :suspended (#RTL3c)' do
|
1598
|
+
channel.on(:attaching) do
|
1599
|
+
channel.on(:suspended) do
|
1600
|
+
stop_reactor
|
1601
|
+
end
|
1602
|
+
client.connection.once_or_if(:connecting) do
|
1603
|
+
client.connection.transition_state_machine :suspended
|
1604
|
+
end
|
1605
|
+
end
|
1606
|
+
channel.attach
|
1607
|
+
end
|
1608
|
+
end
|
1164
1609
|
|
1165
|
-
|
1610
|
+
context 'an :attached channel' do
|
1611
|
+
it 'transitions state to :suspended (#RTL3c)' do
|
1166
1612
|
channel.attach do
|
1167
|
-
channel.on(:
|
1613
|
+
channel.on(:suspended) do
|
1168
1614
|
stop_reactor
|
1169
1615
|
end
|
1170
1616
|
client.connection.transition_state_machine :suspended
|
1171
1617
|
end
|
1172
1618
|
end
|
1619
|
+
|
1620
|
+
it 'transitions state automatically to :attaching once the connection is re-established (#RTN15c3)' do
|
1621
|
+
channel.attach do
|
1622
|
+
channel.on(:suspended) do
|
1623
|
+
client.connection.connect
|
1624
|
+
channel.once(:attached) do
|
1625
|
+
stop_reactor
|
1626
|
+
end
|
1627
|
+
end
|
1628
|
+
client.connection.transition_state_machine :suspended
|
1629
|
+
end
|
1630
|
+
end
|
1173
1631
|
end
|
1174
1632
|
|
1175
1633
|
context 'a :detached channel' do
|
1176
|
-
it 'remains in the :detached state' do
|
1634
|
+
it 'remains in the :detached state (#RTL3c)' do
|
1177
1635
|
channel.attach do
|
1178
1636
|
channel.detach do
|
1179
1637
|
channel.on(:detached) { raise 'Detached state should not have been reached' }
|
1180
|
-
channel.on(:error) { raise 'Error should not have been emitted' }
|
1181
1638
|
|
1182
1639
|
EventMachine.add_timer(1) do
|
1183
1640
|
expect(channel).to be_detached
|
@@ -1194,11 +1651,10 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
1194
1651
|
let(:original_error) { RuntimeError.new }
|
1195
1652
|
let(:client_options) { default_options.merge(log_level: :fatal) }
|
1196
1653
|
|
1197
|
-
it 'remains in the :failed state and retains the error_reason' do
|
1654
|
+
it 'remains in the :failed state and retains the error_reason (#RTL3c)' do
|
1198
1655
|
channel.attach do
|
1199
|
-
channel.once(:
|
1656
|
+
channel.once(:failed) do
|
1200
1657
|
channel.on(:detached) { raise 'Detached state should not have been reached' }
|
1201
|
-
channel.on(:error) { raise 'Error should not have been emitted' }
|
1202
1658
|
|
1203
1659
|
EventMachine.add_timer(1) do
|
1204
1660
|
expect(channel).to be_failed
|
@@ -1214,18 +1670,140 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
1214
1670
|
end
|
1215
1671
|
end
|
1216
1672
|
|
1217
|
-
context 'a channel ATTACH request when connection SUSPENDED' do
|
1673
|
+
context 'a channel ATTACH request when connection SUSPENDED (#RTL4b)' do
|
1218
1674
|
let(:client_options) { default_options.merge(log_level: :fatal) }
|
1219
1675
|
|
1220
|
-
it '
|
1676
|
+
it 'fails the deferrable' do
|
1221
1677
|
client.connect do
|
1222
1678
|
client.connection.once(:suspended) do
|
1223
|
-
|
1224
|
-
|
1679
|
+
channel.attach.errback do |error|
|
1680
|
+
expect(error).to be_a(Ably::Exceptions::InvalidStateChange)
|
1681
|
+
stop_reactor
|
1682
|
+
end
|
1683
|
+
end
|
1684
|
+
client.connection.transition_state_machine :suspended
|
1685
|
+
end
|
1686
|
+
end
|
1687
|
+
end
|
1688
|
+
end
|
1689
|
+
|
1690
|
+
context ':connected' do
|
1691
|
+
context 'a :suspended channel' do
|
1692
|
+
it 'is automatically reattached (#RTL3d)' do
|
1693
|
+
channel.attach do
|
1694
|
+
channel.once(:suspended) do
|
1695
|
+
client.connection.connect
|
1696
|
+
channel.once(:attached) do
|
1697
|
+
stop_reactor
|
1698
|
+
end
|
1225
1699
|
end
|
1226
1700
|
client.connection.transition_state_machine :suspended
|
1227
1701
|
end
|
1228
1702
|
end
|
1703
|
+
|
1704
|
+
context 'when re-attach attempt fails' do
|
1705
|
+
let(:client_options) do
|
1706
|
+
default_options.merge(realtime_request_timeout: 2, log_level: :fatal)
|
1707
|
+
end
|
1708
|
+
|
1709
|
+
it 'returns to a suspended state (#RTL3d)' do
|
1710
|
+
channel.attach do
|
1711
|
+
channel.once(:attached) do
|
1712
|
+
fail "Channel should not have become attached"
|
1713
|
+
end
|
1714
|
+
|
1715
|
+
channel.once(:suspended) do
|
1716
|
+
client.connection.connect
|
1717
|
+
channel.once(:attaching) do
|
1718
|
+
# don't process any incoming ProtocolMessages so the connection never opens
|
1719
|
+
client.connection.__incoming_protocol_msgbus__.unsubscribe
|
1720
|
+
channel.once(:suspended) do |state_change|
|
1721
|
+
expect(state_change.reason.code).to eql(90007)
|
1722
|
+
stop_reactor
|
1723
|
+
end
|
1724
|
+
end
|
1725
|
+
end
|
1726
|
+
client.connection.transition_state_machine :suspended
|
1727
|
+
end
|
1728
|
+
end
|
1729
|
+
end
|
1730
|
+
end
|
1731
|
+
end
|
1732
|
+
|
1733
|
+
context ':disconnected' do
|
1734
|
+
context 'with an initialized channel' do
|
1735
|
+
it 'has no effect on the channel states (#RTL3e)' do
|
1736
|
+
connection.once(:connected) do
|
1737
|
+
expect(channel).to be_initialized
|
1738
|
+
connection.once(:disconnected) do
|
1739
|
+
expect(channel).to be_initialized
|
1740
|
+
stop_reactor
|
1741
|
+
end
|
1742
|
+
disconnect_transport
|
1743
|
+
end
|
1744
|
+
end
|
1745
|
+
end
|
1746
|
+
|
1747
|
+
context 'with an attaching channel' do
|
1748
|
+
it 'has no effect on the channel states (#RTL3e)' do
|
1749
|
+
connection.once(:connected) do
|
1750
|
+
channel.once(:attaching) do
|
1751
|
+
connection.once(:disconnected) do
|
1752
|
+
expect(channel).to be_attaching
|
1753
|
+
stop_reactor
|
1754
|
+
end
|
1755
|
+
disconnect_transport
|
1756
|
+
end
|
1757
|
+
channel.attach
|
1758
|
+
end
|
1759
|
+
end
|
1760
|
+
end
|
1761
|
+
|
1762
|
+
context 'with an attached channel' do
|
1763
|
+
it 'has no effect on the channel states (#RTL3e)' do
|
1764
|
+
channel.attach do
|
1765
|
+
connection.once(:disconnected) do
|
1766
|
+
expect(channel).to be_attached
|
1767
|
+
stop_reactor
|
1768
|
+
end
|
1769
|
+
disconnect_transport
|
1770
|
+
end
|
1771
|
+
end
|
1772
|
+
end
|
1773
|
+
|
1774
|
+
context 'with a detached channel' do
|
1775
|
+
it 'has no effect on the channel states (#RTL3e)' do
|
1776
|
+
channel.attach do
|
1777
|
+
channel.detach do
|
1778
|
+
connection.once(:disconnected) do
|
1779
|
+
expect(channel).to be_detached
|
1780
|
+
stop_reactor
|
1781
|
+
end
|
1782
|
+
disconnect_transport
|
1783
|
+
end
|
1784
|
+
end
|
1785
|
+
end
|
1786
|
+
end
|
1787
|
+
|
1788
|
+
context 'with a failed channel' do
|
1789
|
+
let(:client_options) do
|
1790
|
+
default_options.merge(
|
1791
|
+
default_token_params: { capability: { "foo" =>["*"] } },
|
1792
|
+
use_token_auth: true,
|
1793
|
+
log_level: :fatal
|
1794
|
+
)
|
1795
|
+
end
|
1796
|
+
|
1797
|
+
it 'has no effect on the channel states (#RTL3e)' do
|
1798
|
+
channel.once(:failed) do
|
1799
|
+
connection.once(:disconnected) do
|
1800
|
+
expect(channel).to be_failed
|
1801
|
+
stop_reactor
|
1802
|
+
end
|
1803
|
+
disconnect_transport
|
1804
|
+
end
|
1805
|
+
channel.attach
|
1806
|
+
end
|
1229
1807
|
end
|
1230
1808
|
end
|
1231
1809
|
end
|
@@ -1249,6 +1827,7 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
1249
1827
|
context 'ChannelStateChange object' do
|
1250
1828
|
it 'has current state' do
|
1251
1829
|
channel.on(:attached) do |channel_state_change|
|
1830
|
+
expect(channel_state_change.current).to be_a(Ably::Realtime::Channel::STATE)
|
1252
1831
|
expect(channel_state_change.current).to eq(:attached)
|
1253
1832
|
stop_reactor
|
1254
1833
|
end
|
@@ -1257,12 +1836,22 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
1257
1836
|
|
1258
1837
|
it 'has a previous state' do
|
1259
1838
|
channel.on(:attached) do |channel_state_change|
|
1839
|
+
expect(channel_state_change.previous).to be_a(Ably::Realtime::Channel::STATE)
|
1260
1840
|
expect(channel_state_change.previous).to eq(:attaching)
|
1261
1841
|
stop_reactor
|
1262
1842
|
end
|
1263
1843
|
channel.attach
|
1264
1844
|
end
|
1265
1845
|
|
1846
|
+
it 'has the event that generated the state change (#TA5)' do
|
1847
|
+
channel.on(:attached) do |channel_state_change|
|
1848
|
+
expect(channel_state_change.event).to be_a(Ably::Realtime::Channel::EVENT)
|
1849
|
+
expect(channel_state_change.event).to eq(:attached)
|
1850
|
+
stop_reactor
|
1851
|
+
end
|
1852
|
+
channel.attach
|
1853
|
+
end
|
1854
|
+
|
1266
1855
|
it 'contains a private API protocol_message attribute that is used for special state change events', :api_private do
|
1267
1856
|
channel.on(:attached) do |channel_state_change|
|
1268
1857
|
expect(channel_state_change.protocol_message).to be_a(Ably::Models::ProtocolMessage)
|
@@ -1296,6 +1885,266 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
1296
1885
|
end
|
1297
1886
|
end
|
1298
1887
|
end
|
1888
|
+
|
1889
|
+
context '#resume (#RTL2f)' do
|
1890
|
+
it 'is false when a channel first attaches' do
|
1891
|
+
channel.attach
|
1892
|
+
channel.on(:attached) do |channel_state_change|
|
1893
|
+
expect(channel_state_change.resumed).to be_falsey
|
1894
|
+
stop_reactor
|
1895
|
+
end
|
1896
|
+
end
|
1897
|
+
|
1898
|
+
it 'is true when a connection is recovered and the channel is attached' do
|
1899
|
+
channel.attach
|
1900
|
+
channel.once(:attached) do |channel_state_change|
|
1901
|
+
connection_id = client.connection.id
|
1902
|
+
expect(channel_state_change.resumed).to be_falsey
|
1903
|
+
|
1904
|
+
recover_client = auto_close Ably::Realtime::Client.new(client_options.merge(recover: client.connection.recovery_key))
|
1905
|
+
recover_client.connection.once(:connected) do
|
1906
|
+
expect(recover_client.connection.id).to eql(connection_id)
|
1907
|
+
recover_channel = recover_client.channels.get(channel_name)
|
1908
|
+
recover_channel.attach
|
1909
|
+
recover_channel.once(:attached) do |recover_channel_state_change|
|
1910
|
+
expect(recover_channel_state_change.resumed).to be_truthy
|
1911
|
+
stop_reactor
|
1912
|
+
end
|
1913
|
+
end
|
1914
|
+
end
|
1915
|
+
end
|
1916
|
+
|
1917
|
+
it 'is false when a connection fails to recover and the channel is attached' do
|
1918
|
+
client.connection.once(:connected) do
|
1919
|
+
recovery_key = client.connection.recovery_key
|
1920
|
+
client.connection.once(:closed) do
|
1921
|
+
recover_client = auto_close Ably::Realtime::Client.new(client_options.merge(recover: recovery_key, log_level: :error))
|
1922
|
+
recover_client.connection.once(:connected) do
|
1923
|
+
recover_channel = recover_client.channels.get(channel_name)
|
1924
|
+
recover_channel.attach
|
1925
|
+
recover_channel.once(:attached) do |recover_channel_state_change|
|
1926
|
+
expect(recover_channel_state_change.resumed).to be_falsey
|
1927
|
+
stop_reactor
|
1928
|
+
end
|
1929
|
+
end
|
1930
|
+
end
|
1931
|
+
|
1932
|
+
client.close
|
1933
|
+
end
|
1934
|
+
end
|
1935
|
+
|
1936
|
+
context 'when a resume fails' do
|
1937
|
+
let(:client_options) { default_options.merge(log_level: :error) }
|
1938
|
+
|
1939
|
+
it 'is false when a resume fails to recover and the channel is automatically re-attached' do
|
1940
|
+
channel.attach do
|
1941
|
+
connection_id = client.connection.id
|
1942
|
+
channel.once(:attached) do |channel_state_change|
|
1943
|
+
expect(client.connection.id).to_not eql(connection_id)
|
1944
|
+
expect(channel_state_change.resumed).to be_falsey
|
1945
|
+
stop_reactor
|
1946
|
+
end
|
1947
|
+
client.connection.transport.close_connection_after_writing
|
1948
|
+
client.connection.configure_new '0123456789abcdef', 'wVIsgTHAB1UvXh7z-1991d8586', -1 # force the resume connection key to be invalid
|
1949
|
+
end
|
1950
|
+
end
|
1951
|
+
end
|
1952
|
+
end
|
1953
|
+
end
|
1954
|
+
|
1955
|
+
context 'moves to' do
|
1956
|
+
%w(suspended detached failed).each do |channel_state|
|
1957
|
+
context(channel_state) do
|
1958
|
+
specify 'all queued messages fail with NACK (#RTL11)' do
|
1959
|
+
channel.attach do
|
1960
|
+
# Move to disconnected
|
1961
|
+
disconnect_transport_proc = Proc.new do
|
1962
|
+
if connection.transport
|
1963
|
+
connection.transport.close_connection_after_writing
|
1964
|
+
else
|
1965
|
+
EventMachine.next_tick { disconnect_transport_proc.call }
|
1966
|
+
end
|
1967
|
+
end
|
1968
|
+
disconnect_transport_proc.call
|
1969
|
+
|
1970
|
+
connection.on(:connecting) { disconnect_transport_proc.call }
|
1971
|
+
|
1972
|
+
connection.once(:disconnected) do
|
1973
|
+
channel.publish("foo").errback do |error|
|
1974
|
+
stop_reactor
|
1975
|
+
end
|
1976
|
+
channel.transition_state_machine channel_state.to_sym
|
1977
|
+
end
|
1978
|
+
end
|
1979
|
+
end
|
1980
|
+
|
1981
|
+
specify 'all published messages awaiting an ACK do nothing (#RTL11a)' do
|
1982
|
+
connection_been_disconnected = false
|
1983
|
+
|
1984
|
+
channel.attach do
|
1985
|
+
deferrable = channel.publish("foo")
|
1986
|
+
deferrable.errback do |error|
|
1987
|
+
fail "Message publish should not fail"
|
1988
|
+
end
|
1989
|
+
deferrable.callback do |error|
|
1990
|
+
EventMachine.add_timer(0.5) do
|
1991
|
+
expect(connection_been_disconnected).to be_truthy
|
1992
|
+
stop_reactor
|
1993
|
+
end
|
1994
|
+
end
|
1995
|
+
|
1996
|
+
# Allow 5ms for message to be sent into the socket TCP/IP stack
|
1997
|
+
EventMachine.add_timer(0.005) do
|
1998
|
+
connection.transport.close_connection_after_writing
|
1999
|
+
connection.once(:disconnected) do
|
2000
|
+
connection_been_disconnected = true
|
2001
|
+
channel.transition_state_machine channel_state.to_sym
|
2002
|
+
end
|
2003
|
+
end
|
2004
|
+
end
|
2005
|
+
end
|
2006
|
+
end
|
2007
|
+
end
|
2008
|
+
end
|
2009
|
+
end
|
2010
|
+
|
2011
|
+
context 'when it receives a server-initiated DETACHED (#RTL13)' do
|
2012
|
+
let(:detached_action) { 13 }
|
2013
|
+
|
2014
|
+
context 'and channel is initialized (#RTL13)' do
|
2015
|
+
it 'does nothing' do
|
2016
|
+
connection.once(:connected) do
|
2017
|
+
channel.on { raise 'Channel state should not change' }
|
2018
|
+
|
2019
|
+
detach_message = Ably::Models::ProtocolMessage.new(action: detached_action, channel: channel_name)
|
2020
|
+
client.connection.__incoming_protocol_msgbus__.publish :protocol_message, detach_message
|
2021
|
+
|
2022
|
+
EventMachine.add_timer(1) { stop_reactor }
|
2023
|
+
end
|
2024
|
+
end
|
2025
|
+
end
|
2026
|
+
|
2027
|
+
context 'and channel is failed' do
|
2028
|
+
let(:client_options) {
|
2029
|
+
default_options.merge(
|
2030
|
+
use_token_auth: true,
|
2031
|
+
default_token_params: { capability: { "foo" => ["publish"] } },
|
2032
|
+
log_level: :fatal
|
2033
|
+
)
|
2034
|
+
}
|
2035
|
+
|
2036
|
+
it 'does nothing (#RTL13)' do
|
2037
|
+
connection.once(:connected) do
|
2038
|
+
channel.attach
|
2039
|
+
channel.once(:failed) do
|
2040
|
+
channel.on { raise 'Channel state should not change' }
|
2041
|
+
|
2042
|
+
detach_message = Ably::Models::ProtocolMessage.new(action: detached_action, channel: channel_name)
|
2043
|
+
client.connection.__incoming_protocol_msgbus__.publish :protocol_message, detach_message
|
2044
|
+
|
2045
|
+
EventMachine.add_timer(1) { stop_reactor }
|
2046
|
+
end
|
2047
|
+
end
|
2048
|
+
end
|
2049
|
+
end
|
2050
|
+
|
2051
|
+
context 'and channel is attached' do
|
2052
|
+
it 'reattaches immediately (#RTL13a)' do
|
2053
|
+
channel.attach do
|
2054
|
+
channel.once(:attaching) do |state_change|
|
2055
|
+
expect(state_change.reason.code).to eql(50505)
|
2056
|
+
channel.once(:attached) do
|
2057
|
+
stop_reactor
|
2058
|
+
end
|
2059
|
+
end
|
2060
|
+
|
2061
|
+
detach_message = Ably::Models::ProtocolMessage.new(action: detached_action, channel: channel_name, error: { code: 50505 })
|
2062
|
+
client.connection.__incoming_protocol_msgbus__.publish :protocol_message, detach_message
|
2063
|
+
end
|
2064
|
+
end
|
2065
|
+
end
|
2066
|
+
|
2067
|
+
context 'and channel is suspended' do
|
2068
|
+
it 'reattaches immediately (#RTL13a)' do
|
2069
|
+
channel.attach do
|
2070
|
+
channel.once(:suspended) do
|
2071
|
+
channel.once(:attaching) do |state_change|
|
2072
|
+
expect(state_change.reason.code).to eql(50505)
|
2073
|
+
channel.once(:attached) do
|
2074
|
+
stop_reactor
|
2075
|
+
end
|
2076
|
+
end
|
2077
|
+
|
2078
|
+
detach_message = Ably::Models::ProtocolMessage.new(action: detached_action, channel: channel_name, error: { code: 50505 })
|
2079
|
+
client.connection.__incoming_protocol_msgbus__.publish :protocol_message, detach_message
|
2080
|
+
end
|
2081
|
+
|
2082
|
+
channel.transition_state_machine! :suspended
|
2083
|
+
end
|
2084
|
+
end
|
2085
|
+
end
|
2086
|
+
|
2087
|
+
context 'and channel is attaching' do
|
2088
|
+
let(:client_options) { default_options.merge(channel_retry_timeout: 2, realtime_request_timeout: 1, log_level: :fatal) }
|
2089
|
+
|
2090
|
+
it 'will move to the SUSPENDED state and then attempt to ATTACH with the ATTACHING state (#RTL13b)' do
|
2091
|
+
connection.once(:connected) do
|
2092
|
+
# Prevent any incoming or outgoing ATTACH/ATTACHED message from Ably
|
2093
|
+
prevent_protocol_messages_proc = Proc.new do
|
2094
|
+
if client.connection.transport
|
2095
|
+
client.connection.transport.__incoming_protocol_msgbus__.unsubscribe
|
2096
|
+
client.connection.transport.__outgoing_protocol_msgbus__.unsubscribe
|
2097
|
+
else
|
2098
|
+
EventMachine.next_tick { prevent_protocol_messages_proc.call }
|
2099
|
+
end
|
2100
|
+
end
|
2101
|
+
prevent_protocol_messages_proc.call
|
2102
|
+
end
|
2103
|
+
|
2104
|
+
channel.once(:attaching) do
|
2105
|
+
attaching_at = Time.now
|
2106
|
+
# First attaching fails during server-initiated ATTACHED received
|
2107
|
+
channel.once(:suspended) do |state_change|
|
2108
|
+
expect(Time.now.to_i - attaching_at.to_i).to be_within(1).of(1)
|
2109
|
+
|
2110
|
+
suspended_at = Time.now
|
2111
|
+
# Automatic attach happens at channel_retry_timeout
|
2112
|
+
channel.once(:attaching) do
|
2113
|
+
expect(Time.now.to_i - attaching_at.to_i).to be_within(1).of(2)
|
2114
|
+
channel.once(:suspended) do
|
2115
|
+
channel.once(:attaching) do
|
2116
|
+
channel.once(:attached) do
|
2117
|
+
stop_reactor
|
2118
|
+
end
|
2119
|
+
# Simulate ATTACHED from Ably
|
2120
|
+
attached_message = Ably::Models::ProtocolMessage.new(action: 11, channel: channel_name) # ATTACHED
|
2121
|
+
client.connection.__incoming_protocol_msgbus__.publish :protocol_message, attached_message
|
2122
|
+
end
|
2123
|
+
end
|
2124
|
+
end
|
2125
|
+
end
|
2126
|
+
|
2127
|
+
detach_message = Ably::Models::ProtocolMessage.new(action: detached_action, channel: channel_name)
|
2128
|
+
client.connection.__incoming_protocol_msgbus__.publish :protocol_message, detach_message
|
2129
|
+
end
|
2130
|
+
channel.attach
|
2131
|
+
end
|
2132
|
+
end
|
2133
|
+
end
|
2134
|
+
|
2135
|
+
context 'when it receives an ERROR ProtocolMessage' do
|
2136
|
+
let(:client_options) { default_options.merge(log_level: :fatal) }
|
2137
|
+
|
2138
|
+
it 'should transition to the failed state and the error_reason should be set (#RTL14)' do
|
2139
|
+
channel.attach do
|
2140
|
+
channel.once(:failed) do |state_change|
|
2141
|
+
expect(state_change.reason.code).to eql(50505)
|
2142
|
+
expect(channel.error_reason.code).to eql(50505)
|
2143
|
+
stop_reactor
|
2144
|
+
end
|
2145
|
+
error_message = Ably::Models::ProtocolMessage.new(action: 9, channel: channel_name, error: { code: 50505 }) # ProtocolMessage ERROR type
|
2146
|
+
client.connection.__incoming_protocol_msgbus__.publish :protocol_message, error_message
|
2147
|
+
end
|
1299
2148
|
end
|
1300
2149
|
end
|
1301
2150
|
end
|