ably-rest 1.1.0 → 1.1.2.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +9 -1
  3. data/ably-rest.gemspec +2 -2
  4. data/lib/submodules/ably-ruby/.travis.yml +9 -6
  5. data/lib/submodules/ably-ruby/CHANGELOG.md +18 -1
  6. data/lib/submodules/ably-ruby/README.md +9 -1
  7. data/lib/submodules/ably-ruby/ably.gemspec +4 -4
  8. data/lib/submodules/ably-ruby/lib/ably/logger.rb +7 -1
  9. data/lib/submodules/ably-ruby/lib/ably/modules/state_machine.rb +1 -1
  10. data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +4 -3
  11. data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +26 -15
  12. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +19 -3
  13. data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +4 -2
  14. data/lib/submodules/ably-ruby/lib/ably/version.rb +1 -1
  15. data/lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb +13 -10
  16. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +26 -20
  17. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +11 -8
  18. data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +8 -4
  19. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +71 -5
  20. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +125 -14
  21. data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +17 -17
  22. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +196 -162
  23. data/lib/submodules/ably-ruby/spec/acceptance/realtime/push_admin_spec.rb +46 -6
  24. data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +37 -0
  25. data/lib/submodules/ably-ruby/spec/acceptance/rest/channels_spec.rb +6 -0
  26. data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +8 -27
  27. data/lib/submodules/ably-ruby/spec/acceptance/rest/push_admin_spec.rb +64 -8
  28. data/lib/submodules/ably-ruby/spec/spec_helper.rb +1 -1
  29. data/lib/submodules/ably-ruby/spec/support/debug_failure_helper.rb +9 -5
  30. data/lib/submodules/ably-ruby/spec/unit/modules/enum_spec.rb +1 -1
  31. data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +1 -1
  32. data/lib/submodules/ably-ruby/spec/unit/realtime/connection_spec.rb +1 -1
  33. metadata +11 -11
@@ -112,22 +112,26 @@ describe Ably::Realtime::Channel, '#history', :event_machine do
112
112
 
113
113
  context 'in multiple ProtocolMessages', em_timeout: (30 / 10) + 5 do
114
114
  it 'retrieves limited history forwards with pagination' do
115
- messages_sent.times do |index|
116
- EventMachine.add_timer(index.to_f / rate_per_second) do
117
- channel.publish('event', "history#{index}") do
118
- next unless index == messages_sent - 1
119
- ensure_message_history_direction_and_paging_is_correct :forwards
115
+ channel.attach do
116
+ messages_sent.times do |index|
117
+ EventMachine.add_timer(index.to_f / rate_per_second) do
118
+ channel.publish('event', "history#{index}") do
119
+ next unless index == messages_sent - 1
120
+ ensure_message_history_direction_and_paging_is_correct :forwards
121
+ end
120
122
  end
121
123
  end
122
124
  end
123
125
  end
124
126
 
125
127
  it 'retrieves limited history backwards with pagination' do
126
- messages_sent.times.to_a.reverse.each do |index|
127
- EventMachine.add_timer((messages_sent - index).to_f / rate_per_second) do
128
- channel.publish('event', "history#{index}") do
129
- next unless index == 0
130
- ensure_message_history_direction_and_paging_is_correct :backwards if index == 0
128
+ channel.attach do
129
+ messages_sent.times.to_a.reverse.each do |index|
130
+ EventMachine.add_timer((messages_sent - index).to_f / rate_per_second) do
131
+ channel.publish('event', "history#{index}") do
132
+ next unless index == 0
133
+ ensure_message_history_direction_and_paging_is_correct :backwards if index == 0
134
+ end
131
135
  end
132
136
  end
133
137
  end
@@ -139,18 +143,20 @@ describe Ably::Realtime::Channel, '#history', :event_machine do
139
143
  let(:messages_per_batch) { 10 }
140
144
 
141
145
  it 'return the same results with unique matching message IDs' do
142
- batches.times do |batch|
143
- EventMachine.add_timer(batch.to_f / batches.to_f) do
144
- messages_per_batch.times { channel.publish('event', 'data') }
146
+ channel.attach do
147
+ batches.times do |batch|
148
+ EventMachine.add_timer(batch.to_f / batches.to_f) do
149
+ messages_per_batch.times { |index| channel.publish('event') }
150
+ end
145
151
  end
146
- end
147
152
 
148
- channel.subscribe('event') do |message|
149
- messages << message
150
- if messages.count == batches * messages_per_batch
151
- channel.history do |page|
152
- expect(page.items.map(&:id).sort).to eql(messages.map(&:id).sort)
153
- stop_reactor
153
+ channel.subscribe('event') do |message|
154
+ messages << message
155
+ if messages.count == batches * messages_per_batch
156
+ channel.history do |page|
157
+ expect(page.items.map(&:id).sort).to eql(messages.map(&:id).sort)
158
+ stop_reactor
159
+ end
154
160
  end
155
161
  end
156
162
  end
@@ -279,8 +279,7 @@ describe Ably::Realtime::Channel, :event_machine do
279
279
  key: restricted_api_key,
280
280
  log_level: :fatal,
281
281
  use_token_auth: true,
282
- # TODO: Use wildcard / default when intersection issue resolved, realtime#780
283
- default_token_params: { capability: { "canpublish:foo" => ["publish"] } }
282
+ default_token_params: { capability: { "canpublish:foo" => ["*"] } }
284
283
  )
285
284
  end
286
285
  let(:restricted_client) do
@@ -1009,9 +1008,6 @@ describe Ably::Realtime::Channel, :event_machine do
1009
1008
 
1010
1009
  it 'publishes the message without a name attribute in the payload' do
1011
1010
  published = false
1012
- channel.publish(nil, data) do
1013
- published = true
1014
- end
1015
1011
 
1016
1012
  channel.subscribe do |message|
1017
1013
  expect(message.name).to be_nil
@@ -1024,6 +1020,10 @@ describe Ably::Realtime::Channel, :event_machine do
1024
1020
  end
1025
1021
  end
1026
1022
  end
1023
+
1024
+ channel.publish(nil, data) do
1025
+ published = true
1026
+ end
1027
1027
  end
1028
1028
  end
1029
1029
 
@@ -1032,9 +1032,6 @@ describe Ably::Realtime::Channel, :event_machine do
1032
1032
 
1033
1033
  it 'publishes the message without a data attribute in the payload' do
1034
1034
  published = false
1035
- channel.publish(name, nil) do
1036
- published = true
1037
- end
1038
1035
 
1039
1036
  channel.subscribe do |message|
1040
1037
  expect(message.data).to be_nil
@@ -1047,6 +1044,10 @@ describe Ably::Realtime::Channel, :event_machine do
1047
1044
  end
1048
1045
  end
1049
1046
  end
1047
+
1048
+ channel.publish(name, nil) do
1049
+ published = true
1050
+ end
1050
1051
  end
1051
1052
  end
1052
1053
 
@@ -1997,6 +1998,8 @@ describe Ably::Realtime::Channel, :event_machine do
1997
1998
  end
1998
1999
 
1999
2000
  context '#resume (#RTL2f)' do
2001
+ let(:client_options) { default_options.merge(log_level: :fatal) }
2002
+
2000
2003
  it 'is false when a channel first attaches' do
2001
2004
  channel.attach
2002
2005
  channel.on(:attached) do |channel_state_change|
@@ -315,8 +315,9 @@ describe Ably::Realtime::Client, :event_machine do
315
315
  expect(msg.data).to eql(data)
316
316
  stop_reactor
317
317
  end
318
+
319
+ subject.publish channel_name, event_name, data
318
320
  end
319
- subject.publish channel_name, event_name, data
320
321
  end
321
322
 
322
323
  specify 'publishing does not result in a channel being created' do
@@ -341,8 +342,9 @@ describe Ably::Realtime::Client, :event_machine do
341
342
  expect(msg.extras).to eql(extras)
342
343
  stop_reactor
343
344
  end
345
+
346
+ subject.publish channel_name, event_name, {}, extras: extras
344
347
  end
345
- subject.publish channel_name, event_name, {}, extras: extras
346
348
  end
347
349
  end
348
350
 
@@ -353,8 +355,9 @@ describe Ably::Realtime::Client, :event_machine do
353
355
  expect(msg.data).to eql(data)
354
356
  stop_reactor
355
357
  end
358
+
359
+ subject.publish channel_name, [message]
356
360
  end
357
- subject.publish channel_name, [message]
358
361
  end
359
362
 
360
363
  specify 'publishing supports an array of Hash objects' do
@@ -364,8 +367,9 @@ describe Ably::Realtime::Client, :event_machine do
364
367
  expect(msg.data).to eql(data)
365
368
  stop_reactor
366
369
  end
370
+
371
+ subject.publish channel_name, [name: event_name, data: data]
367
372
  end
368
- subject.publish channel_name, [name: event_name, data: data]
369
373
  end
370
374
 
371
375
  specify 'publishing on a closed connection fails' do
@@ -45,7 +45,7 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
45
45
  connection.on(:failed) do |connection_state_change|
46
46
  error = connection_state_change.reason
47
47
  expect(connection.state).to eq(:failed)
48
- # TODO: Check error type is a TokenNotFOund exception
48
+ # TODO: Check error type is a TokenNotFound exception
49
49
  expect(error.status).to eq(401)
50
50
  expect(error.code).to eq(40400) # not found
51
51
  stop_reactor
@@ -110,6 +110,72 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
110
110
  end
111
111
  end
112
112
  end
113
+
114
+ context 'request fails due to slow response and subsequent timeout', :webmock, em_timeout: (Ably::Rest::Client::HTTP_DEFAULTS.fetch(:request_timeout) + 5) * 2 do
115
+ let(:auth_url) { "http://#{random_str}.domain.will.be.stubbed/path" }
116
+ let(:client_options) { default_options.reject { |k, v| k == :key }.merge(auth_url: auth_url, log_level: :fatal) }
117
+
118
+ # Timeout +5 seconds, beyond default allowed timeout
119
+ before do
120
+ stub_request(:get, auth_url).
121
+ to_return do |request|
122
+ sleep Ably::Rest::Client::HTTP_DEFAULTS.fetch(:request_timeout) + 5
123
+ { status: [500, "Internal Server Error"] }
124
+ end
125
+ end
126
+
127
+ specify 'the connection moves to the disconnected state and tries again, returning again to the disconnected state (#RSA4c, #RSA4c1, #RSA4c2)' do
128
+ states = Hash.new { |hash, key| hash[key] = [] }
129
+
130
+ connection.once(:connected) { raise "Connection can never move to connected because of auth failures" }
131
+
132
+ connection.on do |connection_state|
133
+ states[connection_state.current.to_sym] << Time.now
134
+ if states[:disconnected].count == 2 && connection_state.current == :disconnected
135
+ expect(connection.error_reason).to be_a(Ably::Exceptions::ConnectionError)
136
+ expect(connection.error_reason.message).to match(/auth_url/)
137
+ EventMachine.add_timer(2) do
138
+ expect(states.keys).to include(:connecting, :disconnected)
139
+ expect(states[:connecting].count).to eql(2)
140
+ expect(states[:connected].count).to eql(0)
141
+ stop_reactor
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ context 'request fails once due to slow response but succeeds the second time' do
149
+ let(:auth_url) { "http://#{random_str}.domain.will.be.stubbed/path" }
150
+ let(:client_options) { default_options.reject { |k, v| k == :key }.merge(auth_url: auth_url, log_level: :fatal) }
151
+
152
+ # Timeout +5 seconds, beyond default allowed timeout
153
+ before do
154
+ token_response = Ably::Rest::Client.new(default_options).auth.request_token
155
+ WebMock.enable!
156
+
157
+ stub_request(:get, auth_url).
158
+ to_return do |request|
159
+ sleep Ably::Rest::Client::HTTP_DEFAULTS.fetch(:request_timeout)
160
+ { status: [500, "Internal Server Error"] }
161
+ end.then.
162
+ to_return(:status => 201, :body => token_response.to_json, :headers => { 'Content-Type' => 'application/json' })
163
+ end
164
+
165
+ specify 'the connection moves to the disconnected state and tries again, returning again to the disconnected state (#RSA4c, #RSA4c1, #RSA4c2)' do
166
+ states = Hash.new { |hash, key| hash[key] = [] }
167
+
168
+ connection.once(:connected) do
169
+ expect(states[:disconnected].count).to eql(1)
170
+ expect(states[:connecting].count).to eql(2)
171
+ stop_reactor
172
+ end
173
+
174
+ connection.on do |connection_state|
175
+ states[connection_state.current.to_sym] << Time.now
176
+ end
177
+ end
178
+ end
113
179
  end
114
180
 
115
181
  context 'existing CONNECTED connection' do
@@ -425,7 +491,7 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
425
491
  let(:client_options) do
426
492
  default_options.merge(
427
493
  log_level: :none,
428
- realtime_request_timeout: timeout
494
+ realtime_request_timeout: timeout,
429
495
  )
430
496
  end
431
497
 
@@ -921,7 +987,7 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
921
987
  end
922
988
  end
923
989
 
924
- it 'retains the client_serial (#RTN15c2, #RTN15c3)' do
990
+ it 'retains the client_msg_serial (#RTN15c2, #RTN15c3)' do
925
991
  last_message = nil
926
992
  channel = client.channels.get("foo")
927
993
 
@@ -1103,7 +1169,7 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
1103
1169
  end
1104
1170
  end
1105
1171
 
1106
- it 'resets the client_serial (#RTN15c3)' do
1172
+ it 'continues to use the client_msg_serial (#RTN15c3)' do
1107
1173
  last_message = nil
1108
1174
  channel = client.channels.get("foo")
1109
1175
 
@@ -1121,7 +1187,7 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
1121
1187
  connection.once(:connected) do
1122
1188
  channel.publish("first on new connection") do
1123
1189
  # Message serial reset after failed resume
1124
- expect(last_message.message_serial).to eql(0)
1190
+ expect(last_message.message_serial).to eql(2)
1125
1191
  stop_reactor
1126
1192
  end
1127
1193
  end
@@ -27,6 +27,13 @@ describe Ably::Realtime::Connection, :event_machine do
27
27
  end
28
28
  end
29
29
 
30
+ context 'current_host' do
31
+ it 'is available immediately after the client is instanced' do
32
+ expect(connection.current_host.to_s).to match(/\.ably\.io$/)
33
+ stop_reactor
34
+ end
35
+ end
36
+
30
37
  context 'with :auto_connect option set to false' do
31
38
  let(:client) do
32
39
  auto_close Ably::Realtime::Client.new(default_options.merge(auto_connect: false))
@@ -264,7 +271,7 @@ describe Ably::Realtime::Connection, :event_machine do
264
271
  channel.subscribe('event') do |message|
265
272
  messages_received << message.data.to_i
266
273
  if messages_received.count == total_expected
267
- expect(messages_received).to match(total_expected.times)
274
+ expect(messages_received).to match(total_expected.times.to_a)
268
275
  expect(auth_requests.count).to eql(iteration + 1)
269
276
  EventMachine.add_timer(1) do
270
277
  channel.unsubscribe 'event'
@@ -649,16 +656,47 @@ describe Ably::Realtime::Connection, :event_machine do
649
656
  end
650
657
 
651
658
  it 'is set to 1 when the second message is received' do
652
- channel.publish('event', 'data') do
653
- channel.publish('event', 'data')
659
+ channel.attach do
660
+ messages = []
661
+ channel.subscribe do |message|
662
+ messages << message
663
+ if messages.length == 2
664
+ expect(connection.serial).to eql(1)
665
+ stop_reactor
666
+ end
667
+ end
668
+
669
+ channel.publish('event', 'data') do
670
+ channel.publish('event', 'data')
671
+ end
654
672
  end
673
+ end
674
+ end
655
675
 
656
- messages = []
657
- channel.subscribe do |message|
658
- messages << message
659
- if messages.length == 2
660
- expect(connection.serial).to eql(1)
661
- stop_reactor
676
+ describe '#msgSerial' do
677
+ context 'when messages are queued for publish before a connection is established' do
678
+ let(:batches) { 6 }
679
+ let(:messages_per_batch) { 10 }
680
+
681
+ let(:publishing_client) { auto_close Ably::Realtime::Client.new(default_options) }
682
+ let(:channel_name) { random_str }
683
+ let(:publishing_channel) { publishing_client.channels.get(channel_name) }
684
+ let(:receiving_channel) { client.channels.get(channel_name) }
685
+
686
+ it 'the msgSerial is always incrementing (and not reset when the new connection is established) ensuring messages are never de-duped by the realtime service' do
687
+ messages = []
688
+
689
+ receiving_channel.attach do
690
+ receiving_channel.subscribe('event') do |message|
691
+ messages << message
692
+ stop_reactor if messages.count == batches * messages_per_batch
693
+ end
694
+
695
+ batches.times do |batch|
696
+ EventMachine.add_timer(batch.to_f / batches.to_f) do
697
+ messages_per_batch.times { |index| publishing_channel.publish('event') }
698
+ end
699
+ end
662
700
  end
663
701
  end
664
702
  end
@@ -1005,10 +1043,9 @@ describe Ably::Realtime::Connection, :event_machine do
1005
1043
  let(:client_options) { default_options.merge(websocket_heartbeats_disabled: true) }
1006
1044
 
1007
1045
  it 'does not provide the heartbeats argument in the websocket connection params (#RTN23b)' do
1008
- skip 'Native heartbeats not yet supported in the WS driver https://github.com/ably/ably-ruby/issues/116'
1009
1046
  expect(EventMachine).to receive(:connect) do |host, port, transport, object, url|
1010
1047
  uri = URI.parse(url)
1011
- expect(CGI::parse(uri.query)['heartbeats'][0]).to be_nil
1048
+ expect(CGI::parse(uri.query)['heartbeats'][0]).to eql('true')
1012
1049
  stop_reactor
1013
1050
  end
1014
1051
  client
@@ -1126,7 +1163,7 @@ describe Ably::Realtime::Connection, :event_machine do
1126
1163
  expected_serial += 1 # attach message received
1127
1164
  expect(connection.serial).to eql(expected_serial)
1128
1165
 
1129
- expect(connection.recovery_key).to eql("#{connection.key}:#{connection.serial}")
1166
+ expect(connection.recovery_key).to eql("#{connection.key}:#{connection.serial}:#{connection.send(:client_msg_serial)}")
1130
1167
  stop_reactor
1131
1168
  end
1132
1169
  end
@@ -1237,6 +1274,80 @@ describe Ably::Realtime::Connection, :event_machine do
1237
1274
  end
1238
1275
  end
1239
1276
  end
1277
+
1278
+ context 'when messages have been published' do
1279
+ describe 'the new connection' do
1280
+ it 'uses the correct msgSerial from the old connection' do
1281
+ msg_serial, recovery_key, connection_id = nil, nil, nil
1282
+
1283
+ channel.attach do
1284
+ expect(connection.send(:client_msg_serial)).to eql(-1) # no messages published yet
1285
+ connection_id = client.connection.id
1286
+ connection.transport.__incoming_protocol_msgbus__
1287
+ channel.publish('event', 'message') do
1288
+ msg_serial = connection.send(:client_msg_serial)
1289
+ expect(msg_serial).to eql(0)
1290
+ recovery_key = client.connection.recovery_key
1291
+ connection.transition_state_machine! :failed
1292
+ end
1293
+ end
1294
+
1295
+ connection.on(:failed) do
1296
+ recover_client = auto_close Ably::Realtime::Client.new(default_options.merge(recover: recovery_key))
1297
+ recover_client_channel = recover_client.channel(channel_name)
1298
+ recover_client_channel.attach do
1299
+ expect(recover_client.connection.id).to eql(connection_id)
1300
+ expect(recover_client.connection.send(:client_msg_serial)).to eql(msg_serial)
1301
+ stop_reactor
1302
+ end
1303
+ end
1304
+ end
1305
+ end
1306
+ end
1307
+
1308
+ context 'when messages are published before the new connection is recovered' do
1309
+ describe 'the new connection' do
1310
+ it 'uses the correct msgSerial from the old connection for the queued messages' do
1311
+ msg_serial, recovery_key, connection_id = nil, nil, nil
1312
+
1313
+ channel.attach do
1314
+ expect(connection.send(:client_msg_serial)).to eql(-1) # no messages published yet
1315
+ connection_id = client.connection.id
1316
+ connection.transport.__incoming_protocol_msgbus__
1317
+ channel.subscribe('event') do |message|
1318
+ expect(message.data).to eql('message-1')
1319
+ msg_serial = connection.send(:client_msg_serial)
1320
+ expect(msg_serial).to eql(0)
1321
+ recovery_key = client.connection.recovery_key
1322
+ connection.transition_state_machine! :failed
1323
+ end
1324
+ channel.publish('event', 'message-1')
1325
+ end
1326
+
1327
+ connection.on(:failed) do
1328
+ recover_client = auto_close Ably::Realtime::Client.new(default_options.merge(recover: recovery_key))
1329
+ recover_client_channel = recover_client.channel(channel_name)
1330
+ expect(recover_client.connection.send(:client_msg_serial)).to eql(msg_serial)
1331
+
1332
+ recover_client.connection.once(:connecting) do
1333
+ recover_client_channel.publish('event', 'message-2')
1334
+ expect(recover_client.connection.send(:client_msg_serial)).to eql(msg_serial + 1)
1335
+ end
1336
+
1337
+ recover_client_channel.attach do
1338
+ expect(recover_client.connection.id).to eql(connection_id)
1339
+
1340
+ recover_client_channel.subscribe do |message|
1341
+ expect(message.data).to eql('message-2')
1342
+ EventMachine.add_timer(2) do
1343
+ stop_reactor
1344
+ end
1345
+ end
1346
+ end
1347
+ end
1348
+ end
1349
+ end
1350
+ end
1240
1351
  end
1241
1352
 
1242
1353
  context 'with :recover option' do
@@ -1250,7 +1361,7 @@ describe Ably::Realtime::Connection, :event_machine do
1250
1361
  end
1251
1362
 
1252
1363
  context 'with invalid formatted value sent to server' do
1253
- let(:client_options) { default_options.merge(recover: 'not-a-valid-connection-key:1', log_level: :none) }
1364
+ let(:client_options) { default_options.merge(recover: 'not-a-valid-connection-key:1:0', log_level: :none) }
1254
1365
 
1255
1366
  it 'sets the #error_reason and moves the connection to FAILED' do
1256
1367
  connection.once(:failed) do |state_change|
@@ -1265,7 +1376,7 @@ describe Ably::Realtime::Connection, :event_machine do
1265
1376
  end
1266
1377
 
1267
1378
  context 'with expired (missing) value sent to server' do
1268
- let(:client_options) { default_options.merge(recover: 'wVIsgTHAB1UvXh7z-1991d8586:0', log_level: :fatal) }
1379
+ let(:client_options) { default_options.merge(recover: 'wVIsgTHAB1UvXh7z-1991d8586:0:0', log_level: :fatal) }
1269
1380
 
1270
1381
  it 'connects but sets the error reason and includes the reason in the state change' do
1271
1382
  connection.once(:connected) do |state_change|