ably-rest 0.9.3 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ably-rest.gemspec +2 -1
- data/lib/submodules/ably-ruby/.travis.yml +6 -4
- data/lib/submodules/ably-ruby/CHANGELOG.md +52 -61
- data/lib/submodules/ably-ruby/README.md +10 -0
- data/lib/submodules/ably-ruby/SPEC.md +1473 -852
- data/lib/submodules/ably-ruby/ably.gemspec +2 -1
- data/lib/submodules/ably-ruby/lib/ably/auth.rb +57 -25
- data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +34 -8
- data/lib/submodules/ably-ruby/lib/ably/logger.rb +10 -1
- data/lib/submodules/ably-ruby/lib/ably/models/auth_details.rb +42 -0
- data/lib/submodules/ably-ruby/lib/ably/models/channel_state_change.rb +18 -4
- data/lib/submodules/ably-ruby/lib/ably/models/connection_details.rb +6 -3
- data/lib/submodules/ably-ruby/lib/ably/models/connection_state_change.rb +4 -3
- data/lib/submodules/ably-ruby/lib/ably/models/error_info.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/models/message.rb +12 -1
- data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/base.rb +101 -97
- data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +13 -1
- data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +20 -3
- data/lib/submodules/ably-ruby/lib/ably/modules/async_wrapper.rb +7 -3
- data/lib/submodules/ably-ruby/lib/ably/modules/enum.rb +17 -7
- data/lib/submodules/ably-ruby/lib/ably/modules/event_emitter.rb +29 -14
- data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +7 -4
- data/lib/submodules/ably-ruby/lib/ably/modules/state_machine.rb +2 -4
- data/lib/submodules/ably-ruby/lib/ably/modules/uses_state_machine.rb +7 -3
- data/lib/submodules/ably-ruby/lib/ably/realtime.rb +2 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/auth.rb +79 -31
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +62 -26
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +154 -65
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_state_machine.rb +14 -15
- data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +16 -3
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +38 -29
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/outgoing_message_dispatcher.rb +6 -1
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +108 -49
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +165 -59
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_state_machine.rb +22 -3
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/websocket_transport.rb +19 -10
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +67 -45
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence/members_map.rb +198 -36
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence/presence_manager.rb +30 -6
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence/presence_state_machine.rb +5 -12
- data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +3 -3
- data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +21 -8
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/exceptions.rb +1 -3
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/logger.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/util/pub_sub.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/util/safe_deferrable.rb +26 -0
- data/lib/submodules/ably-ruby/lib/ably/version.rb +2 -2
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb +416 -99
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +5 -3
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +1011 -160
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +2 -2
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +458 -27
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +436 -97
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +52 -23
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +5 -3
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +1160 -105
- data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +151 -22
- data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +88 -27
- data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +42 -15
- data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +4 -4
- data/lib/submodules/ably-ruby/spec/rspec_config.rb +2 -1
- data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +2 -2
- data/lib/submodules/ably-ruby/spec/shared/safe_deferrable_behaviour.rb +6 -2
- data/lib/submodules/ably-ruby/spec/support/debug_failure_helper.rb +20 -4
- data/lib/submodules/ably-ruby/spec/support/event_machine_helper.rb +32 -1
- data/lib/submodules/ably-ruby/spec/unit/auth_spec.rb +4 -11
- data/lib/submodules/ably-ruby/spec/unit/logger_spec.rb +28 -2
- data/lib/submodules/ably-ruby/spec/unit/models/auth_details_spec.rb +49 -0
- data/lib/submodules/ably-ruby/spec/unit/models/channel_state_change_spec.rb +23 -3
- data/lib/submodules/ably-ruby/spec/unit/models/connection_details_spec.rb +12 -1
- data/lib/submodules/ably-ruby/spec/unit/models/connection_state_change_spec.rb +15 -4
- data/lib/submodules/ably-ruby/spec/unit/models/message_spec.rb +34 -2
- data/lib/submodules/ably-ruby/spec/unit/models/presence_message_spec.rb +73 -2
- data/lib/submodules/ably-ruby/spec/unit/models/protocol_message_spec.rb +64 -6
- data/lib/submodules/ably-ruby/spec/unit/models/token_details_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/models/token_request_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +2 -1
- data/lib/submodules/ably-ruby/spec/unit/modules/enum_spec.rb +69 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/event_emitter_spec.rb +149 -22
- data/lib/submodules/ably-ruby/spec/unit/modules/state_emitter_spec.rb +9 -3
- data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/realtime/connection_spec.rb +8 -5
- data/lib/submodules/ably-ruby/spec/unit/realtime/incoming_message_dispatcher_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/realtime/presence_spec.rb +4 -3
- data/lib/submodules/ably-ruby/spec/unit/rest/client_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/util/crypto_spec.rb +3 -3
- metadata +7 -5
|
@@ -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,15 +149,75 @@ 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
215
|
channel.transition_state_machine :failed, reason: RuntimeError.new
|
|
96
216
|
expect(channel).to be_failed
|
|
217
|
+
expect(channel.error_reason).to_not be_nil
|
|
97
218
|
channel.attach do
|
|
98
219
|
expect(channel).to be_attached
|
|
220
|
+
expect(channel.error_reason).to be_nil
|
|
99
221
|
stop_reactor
|
|
100
222
|
end
|
|
101
223
|
end
|
|
@@ -103,14 +225,16 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
103
225
|
end
|
|
104
226
|
|
|
105
227
|
context 'when state is :detaching' do
|
|
106
|
-
it '
|
|
228
|
+
it 'does the attach operation after the completion of the pending request (#RTL4h)' do
|
|
107
229
|
channel.once(:detaching) do
|
|
108
|
-
channel.once(:detached)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
|
114
238
|
end
|
|
115
239
|
end
|
|
116
240
|
|
|
@@ -147,12 +271,21 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
147
271
|
end
|
|
148
272
|
|
|
149
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
|
|
150
283
|
let(:restricted_client) do
|
|
151
|
-
auto_close Ably::Realtime::Client.new(
|
|
284
|
+
auto_close Ably::Realtime::Client.new(auth_options)
|
|
152
285
|
end
|
|
153
|
-
let(:restricted_channel) { restricted_client.channel("
|
|
286
|
+
let(:restricted_channel) { restricted_client.channel("cansubscribe:foo") }
|
|
154
287
|
|
|
155
|
-
it 'emits failed event' do
|
|
288
|
+
it 'emits failed event (#RTL4e)' do
|
|
156
289
|
restricted_channel.attach
|
|
157
290
|
restricted_channel.on(:failed) do |connection_state|
|
|
158
291
|
expect(restricted_channel.state).to eq(:failed)
|
|
@@ -161,7 +294,7 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
161
294
|
end
|
|
162
295
|
end
|
|
163
296
|
|
|
164
|
-
it 'calls the errback of the returned Deferrable' do
|
|
297
|
+
it 'calls the errback of the returned Deferrable (#RTL4d)' do
|
|
165
298
|
restricted_channel.attach.errback do |error|
|
|
166
299
|
expect(restricted_channel.state).to eq(:failed)
|
|
167
300
|
expect(error.status).to eq(401)
|
|
@@ -169,15 +302,6 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
169
302
|
end
|
|
170
303
|
end
|
|
171
304
|
|
|
172
|
-
it 'emits an error event' do
|
|
173
|
-
restricted_channel.attach
|
|
174
|
-
restricted_channel.on(:error) do |error|
|
|
175
|
-
expect(restricted_channel.state).to eq(:failed)
|
|
176
|
-
expect(error.status).to eq(401)
|
|
177
|
-
stop_reactor
|
|
178
|
-
end
|
|
179
|
-
end
|
|
180
|
-
|
|
181
305
|
it 'updates the error_reason' do
|
|
182
306
|
restricted_channel.attach
|
|
183
307
|
restricted_channel.on(:failed) do
|
|
@@ -191,10 +315,8 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
191
315
|
restricted_channel.attach
|
|
192
316
|
restricted_channel.once(:failed) do
|
|
193
317
|
restricted_client.close do
|
|
194
|
-
|
|
195
|
-
restricted_client.auth.authorize(
|
|
196
|
-
|
|
197
|
-
restricted_client.connect do
|
|
318
|
+
token_params = { capability: { "cansubscribe:foo" => ["subscribe"] } }
|
|
319
|
+
restricted_client.auth.authorize(token_params) do
|
|
198
320
|
restricted_channel.once(:attached) do
|
|
199
321
|
expect(restricted_channel.error_reason).to be_nil
|
|
200
322
|
stop_reactor
|
|
@@ -206,98 +328,149 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
206
328
|
end
|
|
207
329
|
end
|
|
208
330
|
end
|
|
209
|
-
end
|
|
210
331
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
channel.
|
|
215
|
-
channel.on(:detached) do
|
|
216
|
-
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
|
|
217
336
|
stop_reactor
|
|
218
337
|
end
|
|
219
338
|
end
|
|
220
|
-
end
|
|
221
339
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
|
228
356
|
end
|
|
229
357
|
end
|
|
230
358
|
end
|
|
359
|
+
end
|
|
231
360
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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
|
|
236
370
|
end
|
|
237
371
|
end
|
|
238
372
|
|
|
239
|
-
channel
|
|
240
|
-
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
|
|
241
381
|
end
|
|
242
|
-
end
|
|
243
382
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
|
248
393
|
end
|
|
249
|
-
end
|
|
250
394
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
expect(channel).to be_a(Ably::Realtime::Channel)
|
|
255
|
-
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)
|
|
256
398
|
stop_reactor
|
|
257
399
|
end
|
|
258
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
|
|
259
431
|
end
|
|
260
432
|
|
|
261
433
|
context 'when state is :failed' do
|
|
262
434
|
let(:client_options) { default_options.merge(log_level: :fatal) }
|
|
263
435
|
|
|
264
|
-
it '
|
|
436
|
+
it 'fails the deferrable (#RTL5b)' do
|
|
265
437
|
channel.attach do
|
|
266
438
|
channel.transition_state_machine :failed, reason: RuntimeError.new
|
|
267
439
|
expect(channel).to be_failed
|
|
268
|
-
|
|
269
|
-
|
|
440
|
+
channel.detach.errback do |error|
|
|
441
|
+
expect(error).to be_a(Ably::Exceptions::InvalidStateChange)
|
|
442
|
+
stop_reactor
|
|
443
|
+
end
|
|
270
444
|
end
|
|
271
445
|
end
|
|
272
446
|
end
|
|
273
447
|
|
|
274
448
|
context 'when state is :attaching' do
|
|
275
|
-
it '
|
|
276
|
-
|
|
277
|
-
channel.once(:
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|
|
282
457
|
end
|
|
458
|
+
channel.detach
|
|
283
459
|
end
|
|
284
|
-
|
|
285
|
-
channel.detach
|
|
460
|
+
channel.attach
|
|
286
461
|
end
|
|
287
|
-
|
|
288
|
-
channel.attach
|
|
289
462
|
end
|
|
290
463
|
end
|
|
291
464
|
|
|
292
465
|
context 'when state is :detaching' do
|
|
293
|
-
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
|
|
294
467
|
channel.once(:detaching) do
|
|
295
|
-
channel.detach
|
|
296
468
|
channel.once(:detached) do
|
|
297
469
|
channel.detach do
|
|
298
470
|
stop_reactor
|
|
299
471
|
end
|
|
300
472
|
end
|
|
473
|
+
channel.detach
|
|
301
474
|
end
|
|
302
475
|
|
|
303
476
|
channel.attach do
|
|
@@ -306,8 +479,28 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
306
479
|
end
|
|
307
480
|
end
|
|
308
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
|
+
|
|
309
502
|
context 'when state is :initialized' do
|
|
310
|
-
it 'does nothing as there is no channel to detach' do
|
|
503
|
+
it 'does nothing as there is no channel to detach (#RTL5a)' do
|
|
311
504
|
expect(channel).to be_initialized
|
|
312
505
|
channel.detach do
|
|
313
506
|
expect(channel).to be_initialized
|
|
@@ -323,14 +516,210 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
323
516
|
end
|
|
324
517
|
end
|
|
325
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
|
|
326
652
|
end
|
|
327
653
|
|
|
328
|
-
describe 'channel recovery
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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)
|
|
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
|
|
333
720
|
end
|
|
721
|
+
|
|
722
|
+
# skip 'sends an ATTACH protocol message in response to a channel message being received on the attaching channel'
|
|
334
723
|
end
|
|
335
724
|
|
|
336
725
|
context '#publish' do
|
|
@@ -381,32 +770,38 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
381
770
|
let(:client_options) { default_options.merge(queue_messages: false) }
|
|
382
771
|
|
|
383
772
|
context 'and connection state initialized' do
|
|
384
|
-
it '
|
|
385
|
-
expect { channel.publish('event') }.to raise_error Ably::Exceptions::MessageQueueingDisabled
|
|
773
|
+
it 'fails the deferrable' do
|
|
386
774
|
expect(client.connection).to be_initialized
|
|
387
|
-
|
|
775
|
+
channel.publish('event').errback do |error|
|
|
776
|
+
expect(error).to be_a(Ably::Exceptions::MessageQueueingDisabled)
|
|
777
|
+
stop_reactor
|
|
778
|
+
end
|
|
388
779
|
end
|
|
389
780
|
end
|
|
390
781
|
|
|
391
782
|
context 'and connection state connecting' do
|
|
392
|
-
it '
|
|
783
|
+
it 'fails the deferrable' do
|
|
393
784
|
client.connect
|
|
394
785
|
EventMachine.next_tick do
|
|
395
|
-
expect { channel.publish('event') }.to raise_error Ably::Exceptions::MessageQueueingDisabled
|
|
396
786
|
expect(client.connection).to be_connecting
|
|
397
|
-
|
|
787
|
+
channel.publish('event').errback do |error|
|
|
788
|
+
expect(error).to be_a(Ably::Exceptions::MessageQueueingDisabled)
|
|
789
|
+
stop_reactor
|
|
790
|
+
end
|
|
398
791
|
end
|
|
399
792
|
end
|
|
400
793
|
end
|
|
401
794
|
|
|
402
795
|
context 'and connection state disconnected' do
|
|
403
|
-
let(:client_options) { default_options.merge(queue_messages: false
|
|
404
|
-
it '
|
|
796
|
+
let(:client_options) { default_options.merge(queue_messages: false) }
|
|
797
|
+
it 'fails the deferrable' do
|
|
405
798
|
client.connection.once(:connected) do
|
|
406
799
|
client.connection.once(:disconnected) do
|
|
407
|
-
expect { channel.publish('event') }.to raise_error Ably::Exceptions::MessageQueueingDisabled
|
|
408
800
|
expect(client.connection).to be_disconnected
|
|
409
|
-
|
|
801
|
+
channel.publish('event').errback do |error|
|
|
802
|
+
expect(error).to be_a(Ably::Exceptions::MessageQueueingDisabled)
|
|
803
|
+
stop_reactor
|
|
804
|
+
end
|
|
410
805
|
end
|
|
411
806
|
client.connection.transition_state_machine :disconnected
|
|
412
807
|
end
|
|
@@ -447,6 +842,15 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
447
842
|
end
|
|
448
843
|
end
|
|
449
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
|
|
450
854
|
end
|
|
451
855
|
|
|
452
856
|
context 'with an array of Hash objects with :name and :data attributes' do
|
|
@@ -696,6 +1100,13 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
696
1100
|
end
|
|
697
1101
|
end
|
|
698
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
|
+
|
|
699
1110
|
context 'with an empty client_id in the message' do
|
|
700
1111
|
it 'succeeds and publishes without a client_id' do
|
|
701
1112
|
channel.publish([name: 'event', client_id: nil]).tap do |deferrable|
|
|
@@ -913,7 +1324,9 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
913
1324
|
|
|
914
1325
|
it 'logs the error and continues' do
|
|
915
1326
|
emitted_exception = false
|
|
916
|
-
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
|
|
917
1330
|
channel.subscribe('click') do |message|
|
|
918
1331
|
emitted_exception = true
|
|
919
1332
|
raise exception
|
|
@@ -986,36 +1399,42 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
986
1399
|
client.connection.manager.error_received_from_server error
|
|
987
1400
|
end
|
|
988
1401
|
|
|
989
|
-
context 'an :
|
|
990
|
-
it 'transitions state to :failed' do
|
|
991
|
-
|
|
992
|
-
channel.
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
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
|
|
997
1413
|
end
|
|
998
|
-
|
|
1414
|
+
channel.attach
|
|
999
1415
|
end
|
|
1000
1416
|
end
|
|
1417
|
+
end
|
|
1001
1418
|
|
|
1002
|
-
|
|
1419
|
+
context 'an :attached channel' do
|
|
1420
|
+
it 'transitions state to :failed (#RTL3a)' do
|
|
1003
1421
|
channel.attach do
|
|
1004
|
-
channel.on(:
|
|
1422
|
+
channel.on(:failed) do |connection_state_change|
|
|
1423
|
+
error = connection_state_change.reason
|
|
1005
1424
|
expect(error).to be_a(Ably::Exceptions::ConnectionFailed)
|
|
1006
|
-
expect(error.code).to eql(
|
|
1425
|
+
expect(error.code).to eql(50000)
|
|
1007
1426
|
stop_reactor
|
|
1008
1427
|
end
|
|
1009
1428
|
fake_error connection_error
|
|
1010
1429
|
end
|
|
1011
1430
|
end
|
|
1012
1431
|
|
|
1013
|
-
it 'updates the channel error_reason' do
|
|
1432
|
+
it 'updates the channel error_reason (#RTL3a)' do
|
|
1014
1433
|
channel.attach do
|
|
1015
1434
|
channel.on(:failed) do |connection_state_change|
|
|
1016
1435
|
error = connection_state_change.reason
|
|
1017
1436
|
expect(error).to be_a(Ably::Exceptions::ConnectionFailed)
|
|
1018
|
-
expect(error.code).to eql(
|
|
1437
|
+
expect(error.code).to eql(50000)
|
|
1019
1438
|
stop_reactor
|
|
1020
1439
|
end
|
|
1021
1440
|
fake_error connection_error
|
|
@@ -1024,10 +1443,9 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
1024
1443
|
end
|
|
1025
1444
|
|
|
1026
1445
|
context 'a :detached channel' do
|
|
1027
|
-
it 'remains in the :detached state' do
|
|
1446
|
+
it 'remains in the :detached state (#RTL3a)' do
|
|
1028
1447
|
channel.attach do
|
|
1029
1448
|
channel.on(:failed) { raise 'Failed state should not have been reached' }
|
|
1030
|
-
channel.on(:error) { raise 'Error should not have been emitted' }
|
|
1031
1449
|
|
|
1032
1450
|
channel.detach do
|
|
1033
1451
|
EventMachine.add_timer(1) do
|
|
@@ -1044,11 +1462,10 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
1044
1462
|
context 'a :failed channel' do
|
|
1045
1463
|
let(:original_error) { RuntimeError.new }
|
|
1046
1464
|
|
|
1047
|
-
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
|
|
1048
1466
|
channel.attach do
|
|
1049
|
-
channel.on(:
|
|
1467
|
+
channel.on(:failed) do
|
|
1050
1468
|
channel.on(:failed) { raise 'Failed state should not have been reached' }
|
|
1051
|
-
channel.on(:error) { raise 'Error should not have been emitted' }
|
|
1052
1469
|
|
|
1053
1470
|
EventMachine.add_timer(1) do
|
|
1054
1471
|
expect(channel).to be_failed
|
|
@@ -1065,11 +1482,13 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
1065
1482
|
end
|
|
1066
1483
|
|
|
1067
1484
|
context 'a channel ATTACH request' do
|
|
1068
|
-
it '
|
|
1485
|
+
it 'fails the deferrable (#RTL4b)' do
|
|
1069
1486
|
client.connect do
|
|
1070
1487
|
client.connection.once(:failed) do
|
|
1071
|
-
|
|
1072
|
-
|
|
1488
|
+
channel.attach.errback do |error|
|
|
1489
|
+
expect(error).to be_a(Ably::Exceptions::InvalidStateChange)
|
|
1490
|
+
stop_reactor
|
|
1491
|
+
end
|
|
1073
1492
|
end
|
|
1074
1493
|
fake_error connection_error
|
|
1075
1494
|
end
|
|
@@ -1079,7 +1498,7 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
1079
1498
|
|
|
1080
1499
|
context ':closed' do
|
|
1081
1500
|
context 'an :attached channel' do
|
|
1082
|
-
it 'transitions state to :detached' do
|
|
1501
|
+
it 'transitions state to :detached (#RTL3b)' do
|
|
1083
1502
|
channel.attach do
|
|
1084
1503
|
channel.on(:detached) do
|
|
1085
1504
|
stop_reactor
|
|
@@ -1089,12 +1508,26 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
1089
1508
|
end
|
|
1090
1509
|
end
|
|
1091
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
|
+
|
|
1092
1526
|
context 'a :detached channel' do
|
|
1093
|
-
it 'remains in the :detached state' do
|
|
1527
|
+
it 'remains in the :detached state (#RTL3b)' do
|
|
1094
1528
|
channel.attach do
|
|
1095
1529
|
channel.detach do
|
|
1096
1530
|
channel.on(:detached) { raise 'Detached state should not have been reached' }
|
|
1097
|
-
channel.on(:error) { raise 'Error should not have been emitted' }
|
|
1098
1531
|
|
|
1099
1532
|
EventMachine.add_timer(1) do
|
|
1100
1533
|
expect(channel).to be_detached
|
|
@@ -1111,11 +1544,10 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
1111
1544
|
let(:client_options) { default_options.merge(log_level: :fatal) }
|
|
1112
1545
|
let(:original_error) { Ably::Models::ErrorInfo.new(message: 'Error') }
|
|
1113
1546
|
|
|
1114
|
-
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
|
|
1115
1548
|
channel.attach do
|
|
1116
|
-
channel.once(:
|
|
1549
|
+
channel.once(:failed) do
|
|
1117
1550
|
channel.on(:detached) { raise 'Detached state should not have been reached' }
|
|
1118
|
-
channel.on(:error) { raise 'Error should not have been emitted' }
|
|
1119
1551
|
|
|
1120
1552
|
EventMachine.add_timer(1) do
|
|
1121
1553
|
expect(channel).to be_failed
|
|
@@ -1132,11 +1564,13 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
1132
1564
|
end
|
|
1133
1565
|
|
|
1134
1566
|
context 'a channel ATTACH request when connection CLOSED' do
|
|
1135
|
-
it '
|
|
1567
|
+
it 'fails the deferrable (#RTL4b)' do
|
|
1136
1568
|
client.connect do
|
|
1137
1569
|
client.connection.once(:closed) do
|
|
1138
|
-
|
|
1139
|
-
|
|
1570
|
+
channel.attach.errback do |error|
|
|
1571
|
+
expect(error).to be_a(Ably::Exceptions::InvalidStateChange)
|
|
1572
|
+
stop_reactor
|
|
1573
|
+
end
|
|
1140
1574
|
end
|
|
1141
1575
|
client.close
|
|
1142
1576
|
end
|
|
@@ -1144,11 +1578,13 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
1144
1578
|
end
|
|
1145
1579
|
|
|
1146
1580
|
context 'a channel ATTACH request when connection CLOSING' do
|
|
1147
|
-
it '
|
|
1581
|
+
it 'fails the deferrable (#RTL4b)' do
|
|
1148
1582
|
client.connect do
|
|
1149
1583
|
client.connection.once(:closing) do
|
|
1150
|
-
|
|
1151
|
-
|
|
1584
|
+
channel.attach.errback do |error|
|
|
1585
|
+
expect(error).to be_a(Ably::Exceptions::InvalidStateChange)
|
|
1586
|
+
stop_reactor
|
|
1587
|
+
end
|
|
1152
1588
|
end
|
|
1153
1589
|
client.close
|
|
1154
1590
|
end
|
|
@@ -1157,25 +1593,48 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
1157
1593
|
end
|
|
1158
1594
|
|
|
1159
1595
|
context ':suspended' do
|
|
1160
|
-
context 'an :
|
|
1161
|
-
|
|
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
|
|
1162
1609
|
|
|
1163
|
-
|
|
1610
|
+
context 'an :attached channel' do
|
|
1611
|
+
it 'transitions state to :suspended (#RTL3c)' do
|
|
1164
1612
|
channel.attach do
|
|
1165
|
-
channel.on(:
|
|
1613
|
+
channel.on(:suspended) do
|
|
1166
1614
|
stop_reactor
|
|
1167
1615
|
end
|
|
1168
1616
|
client.connection.transition_state_machine :suspended
|
|
1169
1617
|
end
|
|
1170
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
|
|
1171
1631
|
end
|
|
1172
1632
|
|
|
1173
1633
|
context 'a :detached channel' do
|
|
1174
|
-
it 'remains in the :detached state' do
|
|
1634
|
+
it 'remains in the :detached state (#RTL3c)' do
|
|
1175
1635
|
channel.attach do
|
|
1176
1636
|
channel.detach do
|
|
1177
1637
|
channel.on(:detached) { raise 'Detached state should not have been reached' }
|
|
1178
|
-
channel.on(:error) { raise 'Error should not have been emitted' }
|
|
1179
1638
|
|
|
1180
1639
|
EventMachine.add_timer(1) do
|
|
1181
1640
|
expect(channel).to be_detached
|
|
@@ -1192,11 +1651,10 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
1192
1651
|
let(:original_error) { RuntimeError.new }
|
|
1193
1652
|
let(:client_options) { default_options.merge(log_level: :fatal) }
|
|
1194
1653
|
|
|
1195
|
-
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
|
|
1196
1655
|
channel.attach do
|
|
1197
|
-
channel.once(:
|
|
1656
|
+
channel.once(:failed) do
|
|
1198
1657
|
channel.on(:detached) { raise 'Detached state should not have been reached' }
|
|
1199
|
-
channel.on(:error) { raise 'Error should not have been emitted' }
|
|
1200
1658
|
|
|
1201
1659
|
EventMachine.add_timer(1) do
|
|
1202
1660
|
expect(channel).to be_failed
|
|
@@ -1212,18 +1670,140 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
1212
1670
|
end
|
|
1213
1671
|
end
|
|
1214
1672
|
|
|
1215
|
-
context 'a channel ATTACH request when connection SUSPENDED' do
|
|
1673
|
+
context 'a channel ATTACH request when connection SUSPENDED (#RTL4b)' do
|
|
1216
1674
|
let(:client_options) { default_options.merge(log_level: :fatal) }
|
|
1217
1675
|
|
|
1218
|
-
it '
|
|
1676
|
+
it 'fails the deferrable' do
|
|
1219
1677
|
client.connect do
|
|
1220
1678
|
client.connection.once(:suspended) do
|
|
1221
|
-
|
|
1222
|
-
|
|
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
|
|
1223
1699
|
end
|
|
1224
1700
|
client.connection.transition_state_machine :suspended
|
|
1225
1701
|
end
|
|
1226
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
|
|
1227
1807
|
end
|
|
1228
1808
|
end
|
|
1229
1809
|
end
|
|
@@ -1247,6 +1827,7 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
1247
1827
|
context 'ChannelStateChange object' do
|
|
1248
1828
|
it 'has current state' do
|
|
1249
1829
|
channel.on(:attached) do |channel_state_change|
|
|
1830
|
+
expect(channel_state_change.current).to be_a(Ably::Realtime::Channel::STATE)
|
|
1250
1831
|
expect(channel_state_change.current).to eq(:attached)
|
|
1251
1832
|
stop_reactor
|
|
1252
1833
|
end
|
|
@@ -1255,12 +1836,22 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
1255
1836
|
|
|
1256
1837
|
it 'has a previous state' do
|
|
1257
1838
|
channel.on(:attached) do |channel_state_change|
|
|
1839
|
+
expect(channel_state_change.previous).to be_a(Ably::Realtime::Channel::STATE)
|
|
1258
1840
|
expect(channel_state_change.previous).to eq(:attaching)
|
|
1259
1841
|
stop_reactor
|
|
1260
1842
|
end
|
|
1261
1843
|
channel.attach
|
|
1262
1844
|
end
|
|
1263
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
|
+
|
|
1264
1855
|
it 'contains a private API protocol_message attribute that is used for special state change events', :api_private do
|
|
1265
1856
|
channel.on(:attached) do |channel_state_change|
|
|
1266
1857
|
expect(channel_state_change.protocol_message).to be_a(Ably::Models::ProtocolMessage)
|
|
@@ -1294,6 +1885,266 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
|
1294
1885
|
end
|
|
1295
1886
|
end
|
|
1296
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
|
|
1297
2148
|
end
|
|
1298
2149
|
end
|
|
1299
2150
|
end
|