ably 1.1.1 → 1.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/check.yml +27 -0
  3. data/CHANGELOG.md +60 -2
  4. data/COPYRIGHT +1 -0
  5. data/LICENSE +172 -11
  6. data/MAINTAINERS.md +1 -0
  7. data/README.md +2 -14
  8. data/SPEC.md +1020 -922
  9. data/ably.gemspec +5 -5
  10. data/lib/ably/auth.rb +12 -2
  11. data/lib/ably/exceptions.rb +2 -2
  12. data/lib/ably/logger.rb +7 -1
  13. data/lib/ably/modules/ably.rb +11 -1
  14. data/lib/ably/modules/state_machine.rb +1 -1
  15. data/lib/ably/realtime/channel.rb +7 -11
  16. data/lib/ably/realtime/channel/channel_manager.rb +2 -2
  17. data/lib/ably/realtime/channel/channel_properties.rb +24 -0
  18. data/lib/ably/realtime/client.rb +9 -0
  19. data/lib/ably/realtime/connection.rb +7 -4
  20. data/lib/ably/realtime/connection/connection_manager.rb +19 -1
  21. data/lib/ably/realtime/connection/websocket_transport.rb +67 -1
  22. data/lib/ably/realtime/presence.rb +0 -14
  23. data/lib/ably/rest/channel.rb +25 -17
  24. data/lib/ably/rest/client.rb +35 -17
  25. data/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +4 -1
  26. data/lib/ably/version.rb +1 -1
  27. data/spec/acceptance/realtime/auth_spec.rb +5 -4
  28. data/spec/acceptance/realtime/channel_spec.rb +21 -8
  29. data/spec/acceptance/realtime/client_spec.rb +80 -20
  30. data/spec/acceptance/realtime/connection_failures_spec.rb +90 -9
  31. data/spec/acceptance/realtime/connection_spec.rb +47 -19
  32. data/spec/acceptance/realtime/message_spec.rb +2 -4
  33. data/spec/acceptance/realtime/presence_history_spec.rb +0 -58
  34. data/spec/acceptance/realtime/presence_spec.rb +54 -0
  35. data/spec/acceptance/realtime/push_admin_spec.rb +43 -21
  36. data/spec/acceptance/rest/auth_spec.rb +6 -75
  37. data/spec/acceptance/rest/base_spec.rb +8 -4
  38. data/spec/acceptance/rest/channel_spec.rb +42 -4
  39. data/spec/acceptance/rest/client_spec.rb +121 -26
  40. data/spec/acceptance/rest/message_spec.rb +1 -2
  41. data/spec/acceptance/rest/push_admin_spec.rb +67 -27
  42. data/spec/shared/client_initializer_behaviour.rb +131 -8
  43. data/spec/spec_helper.rb +1 -0
  44. data/spec/support/debug_failure_helper.rb +9 -5
  45. data/spec/support/serialization_helper.rb +21 -0
  46. data/spec/support/test_app.rb +2 -2
  47. data/spec/unit/modules/enum_spec.rb +1 -1
  48. data/spec/unit/realtime/client_spec.rb +20 -7
  49. data/spec/unit/realtime/connection_spec.rb +1 -1
  50. metadata +22 -17
  51. data/.travis.yml +0 -19
@@ -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,82 @@ 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
+
164
+ stub_request(:get, 'https://internet-up.ably-realtime.com/is-the-internet-up.txt')
165
+ .with(
166
+ headers: {
167
+ 'Accept-Encoding' => 'gzip, compressed',
168
+ 'Connection' => 'close',
169
+ 'Host' => 'internet-up.ably-realtime.com',
170
+ 'User-Agent' => 'EventMachine HttpClient'
171
+ }
172
+ ).to_return(status: 200, body: 'yes\n', headers: { 'Content-Type' => 'text/plain' })
173
+ end
174
+
175
+ specify 'the connection moves to the disconnected state and tries again, returning again to the disconnected state (#RSA4c, #RSA4c1, #RSA4c2)' do
176
+ states = Hash.new { |hash, key| hash[key] = [] }
177
+
178
+ connection.once(:connected) do
179
+ expect(states[:disconnected].count).to eql(1)
180
+ expect(states[:connecting].count).to eql(2)
181
+ stop_reactor
182
+ end
183
+
184
+ connection.on do |connection_state|
185
+ states[connection_state.current.to_sym] << Time.now
186
+ end
187
+ end
188
+ end
113
189
  end
114
190
 
115
191
  context 'existing CONNECTED connection' do
@@ -425,7 +501,7 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
425
501
  let(:client_options) do
426
502
  default_options.merge(
427
503
  log_level: :none,
428
- realtime_request_timeout: timeout
504
+ realtime_request_timeout: timeout,
429
505
  )
430
506
  end
431
507
 
@@ -1357,14 +1433,19 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
1357
1433
  let(:expected_host) { "#{environment}-#{Ably::Realtime::Client::DOMAIN}" }
1358
1434
  let(:client_options) { timeout_options.merge(environment: environment) }
1359
1435
 
1360
- it 'does not use a fallback host by default' do
1361
- expect(connection).to receive(:create_transport).exactly(retry_count_for_all_states).times do |host|
1362
- expect(host).to eql(expected_host)
1363
- raise EventMachine::ConnectionError
1364
- end
1436
+ context ':fallback_hosts_use_default is unset' do
1437
+ let(:max_time_in_state_for_tests) { 8 }
1438
+ let(:expected_hosts) { Ably::CUSTOM_ENVIRONMENT_FALLBACKS_SUFFIXES.map { |suffix| "#{environment}#{suffix}" } + [expected_host] }
1439
+ let(:fallback_hosts_used) { Array.new }
1440
+
1441
+ it 'uses fallback hosts by default' do
1442
+ allow(connection).to receive(:create_transport) do |host|
1443
+ fallback_hosts_used << host
1444
+ raise EventMachine::ConnectionError
1445
+ end
1365
1446
 
1366
- connection.once(:suspended) do
1367
1447
  connection.once(:suspended) do
1448
+ expect(fallback_hosts_used.uniq).to match_array(expected_hosts)
1368
1449
  stop_reactor
1369
1450
  end
1370
1451
  end
@@ -1442,7 +1523,7 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
1442
1523
  end
1443
1524
 
1444
1525
  context 'with production environment' do
1445
- let(:custom_hosts) { %w(A.ably-realtime.com B.ably-realtime.com) }
1526
+ let(:custom_hosts) { %w(a.ably-realtime.com b.ably-realtime.com) }
1446
1527
  before do
1447
1528
  stub_const 'Ably::FALLBACK_HOSTS', custom_hosts
1448
1529
  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))
@@ -70,18 +77,6 @@ describe Ably::Realtime::Connection, :event_machine do
70
77
  end
71
78
  end
72
79
  end
73
-
74
- context 'with implicit authorisation' do
75
- let(:client_options) { default_options.merge(client_id: 'force_token_auth') }
76
-
77
- it 'uses the token created by the implicit authorisation' do
78
- expect(client.rest_client.auth).to receive(:request_token).once.and_call_original
79
-
80
- connection.once(:connected) do
81
- stop_reactor
82
- end
83
- end
84
- end
85
80
  end
86
81
 
87
82
  context 'that expire' do
@@ -127,7 +122,7 @@ describe Ably::Realtime::Connection, :event_machine do
127
122
  end
128
123
  end
129
124
 
130
- context 'with immediately expired token' do
125
+ context 'with immediately expired token and no fallback hosts' do
131
126
  let(:ttl) { 0.001 }
132
127
  let(:auth_requests) { [] }
133
128
  let(:token_callback) do
@@ -136,7 +131,7 @@ describe Ably::Realtime::Connection, :event_machine do
136
131
  Ably::Rest::Client.new(default_options).auth.request_token(ttl: ttl).token
137
132
  end
138
133
  end
139
- let(:client_options) { default_options.merge(auth_callback: token_callback) }
134
+ let(:client_options) { default_options.merge(auth_callback: token_callback, fallback_hosts: []) }
140
135
 
141
136
  it 'renews the token on connect, and makes one immediate subsequent attempt to obtain a new token (#RSA4b)' do
142
137
  started_at = Time.now.to_f
@@ -151,7 +146,7 @@ describe Ably::Realtime::Connection, :event_machine do
151
146
  end
152
147
 
153
148
  context 'when disconnected_retry_timeout is 0.5 seconds' do
154
- let(:client_options) { default_options.merge(disconnected_retry_timeout: 0.5, auth_callback: token_callback) }
149
+ let(:client_options) { default_options.merge(disconnected_retry_timeout: 0.5, auth_callback: token_callback, fallback_hosts: []) }
155
150
 
156
151
  it 'renews the token on connect, and continues to attempt renew based on the retry schedule' do
157
152
  disconnect_count = 0
@@ -177,7 +172,7 @@ describe Ably::Realtime::Connection, :event_machine do
177
172
  end
178
173
 
179
174
  context 'using implicit token auth' do
180
- let(:client_options) { default_options.merge(use_token_auth: true, default_token_params: { ttl: ttl }) }
175
+ let(:client_options) { default_options.merge(use_token_auth: true, default_token_params: { ttl: ttl }, fallback_hosts: []) }
181
176
 
182
177
  before do
183
178
  stub_const 'Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER', -10 # ensure client lib thinks token is still valid
@@ -446,7 +441,9 @@ describe Ably::Realtime::Connection, :event_machine do
446
441
  end
447
442
  end
448
443
 
449
- context '#connect' do
444
+ context '#connect with no fallbacks' do
445
+ let(:client_options) { default_options.merge(fallback_hosts: []) }
446
+
450
447
  it 'returns a SafeDeferrable that catches exceptions in callbacks and logs them' do
451
448
  expect(connection.connect).to be_a(Ably::Util::SafeDeferrable)
452
449
  stop_reactor
@@ -1172,6 +1169,7 @@ describe Ably::Realtime::Connection, :event_machine do
1172
1169
  host: 'this.host.does.not.exist.com'
1173
1170
  )
1174
1171
  )
1172
+ allow(client).to receive(:fallback_hosts).and_return([])
1175
1173
 
1176
1174
  connection.transition_state_machine! :disconnected
1177
1175
  end
@@ -1307,12 +1305,14 @@ describe Ably::Realtime::Connection, :event_machine do
1307
1305
  expect(connection.send(:client_msg_serial)).to eql(-1) # no messages published yet
1308
1306
  connection_id = client.connection.id
1309
1307
  connection.transport.__incoming_protocol_msgbus__
1310
- channel.publish('event', 'message-1') do
1308
+ channel.subscribe('event') do |message|
1309
+ expect(message.data).to eql('message-1')
1311
1310
  msg_serial = connection.send(:client_msg_serial)
1312
1311
  expect(msg_serial).to eql(0)
1313
1312
  recovery_key = client.connection.recovery_key
1314
1313
  connection.transition_state_machine! :failed
1315
1314
  end
1315
+ channel.publish('event', 'message-1')
1316
1316
  end
1317
1317
 
1318
1318
  connection.on(:failed) do
@@ -1329,7 +1329,7 @@ describe Ably::Realtime::Connection, :event_machine do
1329
1329
  expect(recover_client.connection.id).to eql(connection_id)
1330
1330
 
1331
1331
  recover_client_channel.subscribe do |message|
1332
- raise "Unexpected message #{message}" if message.data != 'message-2'
1332
+ expect(message.data).to eql('message-2')
1333
1333
  EventMachine.add_timer(2) do
1334
1334
  stop_reactor
1335
1335
  end
@@ -1870,5 +1870,33 @@ describe Ably::Realtime::Connection, :event_machine do
1870
1870
  end
1871
1871
  end
1872
1872
  end
1873
+
1874
+ context 'transport_params (#RTC1f)' do
1875
+ let(:client_options) { default_options.merge(transport_params: { 'extra_param' => 'extra_param' }) }
1876
+
1877
+ it 'pases transport_params to query' do
1878
+ expect(EventMachine).to receive(:connect) do |host, port, transport, object, url|
1879
+ uri = URI.parse(url)
1880
+ expect(CGI::parse(uri.query)['extra_param'][0]).to eq('extra_param')
1881
+ stop_reactor
1882
+ end
1883
+
1884
+ client
1885
+ end
1886
+
1887
+ context 'when changing default param' do
1888
+ let(:client_options) { default_options.merge(transport_params: { v: '1.0' }) }
1889
+
1890
+ it 'overrides default param (#RTC1f1)' do
1891
+ expect(EventMachine).to receive(:connect) do |host, port, transport, object, url|
1892
+ uri = URI.parse(url)
1893
+ expect(CGI::parse(uri.query)['v'][0]).to eq('1.0')
1894
+ stop_reactor
1895
+ end
1896
+
1897
+ client
1898
+ end
1899
+ end
1900
+ end
1873
1901
  end
1874
1902
  end
@@ -96,8 +96,7 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
96
96
  end
97
97
 
98
98
  context 'JSON Array' do
99
- # TODO: Add nil type back in
100
- let(:data) { { 'push' => { 'data' => { 'key' => [ true, false, 55, 'string', { 'Hash' => true }, ['array'] ] } } } }
99
+ let(:data) { { 'push' => { 'data' => { 'key' => [ true, false, 55, nil, 'string', { 'Hash' => true }, ['array'] ] } } } }
101
100
 
102
101
  it 'is encoded and decoded to the same Array' do
103
102
  publish_and_check_extras data
@@ -658,8 +657,7 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
658
657
  expect(message_state).to be_empty
659
658
  EventMachine.add_timer(2) do
660
659
  expect(message_state).to contain_exactly(:delivered)
661
- # TODO: Uncomment once issue realtime#42 is resolved
662
- # expect(msgs_received.length).to eql(1)
660
+ expect(msgs_received.length).to eql(1)
663
661
  stop_reactor
664
662
  end
665
663
  end
@@ -52,63 +52,5 @@ describe Ably::Realtime::Presence, 'history', :event_machine do
52
52
 
53
53
  presence_client_one.enter(data)
54
54
  end
55
-
56
- context 'with option until_attach: true' do
57
- let(:event) { random_str }
58
- let(:presence_data_before_attach) { random_str }
59
- let(:presence_data_after_attach) { random_str }
60
-
61
- it 'retrieves all presence messages before channel was attached' do
62
- presence_client_two.enter(presence_data_before_attach) do
63
- presence_client_one.enter(presence_data_after_attach) do
64
- presence_client_one.history(until_attach: true) do |presence_page|
65
- expect(presence_page.items.count).to eql(1)
66
- expect(presence_page.items.first.data).to eql(presence_data_before_attach)
67
- stop_reactor
68
- end
69
- end
70
- end
71
- end
72
-
73
- context 'and two pages of messages' do
74
- let(:wildcard_token) { lambda { |token_params| Ably::Rest::Client.new(default_options).auth.request_token(client_id: '*') } }
75
- let(:client_one) { auto_close Ably::Realtime::Client.new(default_options.merge(auth_callback: wildcard_token)) }
76
- let(:client_two) { auto_close Ably::Realtime::Client.new(default_options.merge(auth_callback: wildcard_token)) }
77
-
78
- # TODO: Remove retry logic when presence history regression fixed
79
- # https://github.com/ably/realtime/issues/1707
80
- #
81
- it 'retrieves two pages of messages before channel was attached', retry: 10, :retry_wait => 5 do
82
- when_all(*10.times.map { |i| presence_client_two.enter_client("client:#{i}", presence_data_before_attach) }) do
83
- when_all(*10.times.map { |i| presence_client_one.enter_client("client:#{i}", presence_data_after_attach) }) do
84
- presence_client_one.history(until_attach: true, limit: 5) do |presence_page|
85
- expect(presence_page.items.count).to eql(5)
86
- expect(presence_page.items.map(&:data).uniq.first).to eql(presence_data_before_attach)
87
-
88
- presence_page.next do |presence_next_page|
89
- expect(presence_next_page.items.count).to eql(5)
90
- expect(presence_next_page.items.map(&:data).uniq.first).to eql(presence_data_before_attach)
91
- if presence_next_page.has_next?
92
- presence_next_page.next do |last|
93
- expect(last.items.count).to eql(0)
94
- end
95
- else
96
- expect(presence_next_page).to be_last
97
- end
98
- stop_reactor
99
- end
100
- end
101
- end
102
- end
103
- end
104
- end
105
-
106
- it 'fails with an exception unless state is attached' do
107
- presence_client_one.history(until_attach: true).errback do |error|
108
- expect(error.message).to match(/not attached/)
109
- stop_reactor
110
- end
111
- end
112
- end
113
55
  end
114
56
  end
@@ -2449,6 +2449,60 @@ describe Ably::Realtime::Presence, :event_machine do
2449
2449
  end
2450
2450
  end
2451
2451
 
2452
+ describe '#RTP17b' do
2453
+ let(:leave_action) { Ably::Models::PresenceMessage::ACTION.Leave }
2454
+
2455
+ it 'updates presence members on leave' do
2456
+ presence_client_two.subscribe(:enter) do
2457
+ channel_anonymous_client.attach do
2458
+ channel_anonymous_client.presence.get do |members|
2459
+ presence_client_two.subscribe(:leave) do
2460
+ expect(presence_client_two.members.local_members).to be_empty
2461
+ stop_reactor
2462
+ end
2463
+
2464
+ leave_message = Ably::Models::PresenceMessage.new(
2465
+ 'id' => "#{client_two.connection.id}:#{presence_client_two.client_id}:1",
2466
+ 'clientId' => presence_client_two.client_id,
2467
+ 'connectionId' => client_two.connection.id,
2468
+ 'timestamp' => as_since_epoch(Time.now),
2469
+ 'action' => leave_action
2470
+ )
2471
+
2472
+ presence_client_two.__incoming_msgbus__.publish :presence, leave_message
2473
+ end
2474
+ end
2475
+ end
2476
+
2477
+ presence_client_two.enter
2478
+ end
2479
+
2480
+ it 'does no update presence members on fabricated leave' do
2481
+ presence_client_two.subscribe(:enter) do
2482
+ channel_anonymous_client.attach do
2483
+ channel_anonymous_client.presence.get do |members|
2484
+ presence_client_two.subscribe(:leave) do
2485
+ expect(presence_client_two.members.local_members).to_not be_empty
2486
+ stop_reactor
2487
+ end
2488
+
2489
+ fabricated_leave_message = Ably::Models::PresenceMessage.new(
2490
+ 'id' => "#{client_two.connection.id}:#{presence_client_two.client_id}:1",
2491
+ 'clientId' => presence_client_two.client_id,
2492
+ 'connectionId' => "fabricated:#{presence_client_two.client_id}:0",
2493
+ 'timestamp' => as_since_epoch(Time.now),
2494
+ 'action' => leave_action
2495
+ )
2496
+
2497
+ presence_client_two.__incoming_msgbus__.publish :presence, fabricated_leave_message
2498
+ end
2499
+ end
2500
+ end
2501
+
2502
+ presence_client_two.enter
2503
+ end
2504
+ end
2505
+
2452
2506
  context 'when a channel becomes attached again' do
2453
2507
  let(:attached_action) { Ably::Models::ProtocolMessage::ACTION.Attached.to_i }
2454
2508
  let(:sync_action) { Ably::Models::ProtocolMessage::ACTION.Sync.to_i }
@@ -101,31 +101,15 @@ describe Ably::Realtime::Push::Admin, :event_machine do
101
101
  end
102
102
  end
103
103
 
104
- def request_body(request, protocol)
105
- if protocol == :msgpack
106
- MessagePack.unpack(request.body)
107
- else
108
- JSON.parse(request.body)
109
- end
110
- end
111
-
112
- def serialize(object, protocol)
113
- if protocol == :msgpack
114
- MessagePack.pack(object)
115
- else
116
- JSON.dump(object)
117
- end
118
- end
119
-
120
104
  let!(:publish_stub) do
121
105
  stub_request(:post, "#{client.rest_client.endpoint}/push/publish").
122
106
  with do |request|
123
- expect(request_body(request, protocol)['recipient']['camelCase']['secondLevelCamelCase']).to eql('val')
124
- expect(request_body(request, protocol)['recipient']).to_not have_key('camel_case')
107
+ expect(deserialize_body(request.body, protocol)['recipient']['camelCase']['secondLevelCamelCase']).to eql('val')
108
+ expect(deserialize_body(request.body, protocol)['recipient']).to_not have_key('camel_case')
125
109
  true
126
110
  end.to_return(
127
111
  :status => 201,
128
- :body => serialize({}, protocol),
112
+ :body => serialize_body({}, protocol),
129
113
  :headers => { 'Content-Type' => content_type }
130
114
  )
131
115
  end
@@ -217,6 +201,12 @@ describe Ably::Realtime::Push::Admin, :event_machine do
217
201
  let(:client_id) { random_str }
218
202
  let(:fixture_count) { 6 }
219
203
 
204
+ before(:all) do
205
+ # As push tests often use the global scope (devices),
206
+ # we need to ensure tests cannot conflict
207
+ reload_test_app
208
+ end
209
+
220
210
  before do
221
211
  fixture_count.times.map do |index|
222
212
  Thread.new do # Parallelise the setup
@@ -274,6 +264,12 @@ describe Ably::Realtime::Push::Admin, :event_machine do
274
264
  let(:fixture_count) { 2 }
275
265
  let(:client_id) { random_str }
276
266
 
267
+ before(:all) do
268
+ # As push tests often use the global scope (devices),
269
+ # we need to ensure tests cannot conflict
270
+ reload_test_app
271
+ end
272
+
277
273
  before do
278
274
  fixture_count.times.map do |index|
279
275
  Thread.new do # Parallelise the setup
@@ -353,6 +349,12 @@ describe Ably::Realtime::Push::Admin, :event_machine do
353
349
  }
354
350
  end
355
351
 
352
+ before(:all) do
353
+ # As push tests often use the global scope (devices),
354
+ # we need to ensure tests cannot conflict
355
+ reload_test_app
356
+ end
357
+
356
358
  after do
357
359
  rest_device_registrations.remove_where client_id: client_id
358
360
  end
@@ -388,6 +390,12 @@ describe Ably::Realtime::Push::Admin, :event_machine do
388
390
  let(:device_id) { random_str }
389
391
  let(:client_id) { random_str }
390
392
 
393
+ before(:all) do
394
+ # As push tests often use the global scope (devices),
395
+ # we need to ensure tests cannot conflict
396
+ reload_test_app
397
+ end
398
+
391
399
  before do
392
400
  rest_device_registrations.save({
393
401
  id: "device-#{client_id}-0",
@@ -421,6 +429,12 @@ describe Ably::Realtime::Push::Admin, :event_machine do
421
429
  let(:device_id) { random_str }
422
430
  let(:client_id) { random_str }
423
431
 
432
+ before(:all) do
433
+ # As push tests often use the global scope (devices),
434
+ # we need to ensure tests cannot conflict
435
+ reload_test_app
436
+ end
437
+
424
438
  before do
425
439
  rest_device_registrations.save({
426
440
  id: "device-#{client_id}-0",
@@ -481,6 +495,12 @@ describe Ably::Realtime::Push::Admin, :event_machine do
481
495
  client.push.admin.channel_subscriptions
482
496
  }
483
497
 
498
+ before(:all) do
499
+ # As push tests often use the global scope (devices),
500
+ # we need to ensure tests cannot conflict
501
+ reload_test_app
502
+ end
503
+
484
504
  # Set up 2 devices with the same client_id
485
505
  # and two device with the unique device_id and no client_id
486
506
  before do
@@ -543,8 +563,10 @@ describe Ably::Realtime::Push::Admin, :event_machine do
543
563
  describe '#list_channels' do
544
564
  let(:fixture_count) { 6 }
545
565
 
546
- before(:context) do
547
- reload_test_app # TODO: Review if necessary later, currently other tests may affect list_channels
566
+ before(:all) do
567
+ # As push tests often use the global scope (devices),
568
+ # we need to ensure tests cannot conflict
569
+ reload_test_app
548
570
  end
549
571
 
550
572
  before do