ably 1.0.6 → 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +14 -0
  3. data/.travis.yml +10 -8
  4. data/CHANGELOG.md +75 -3
  5. data/LICENSE +1 -3
  6. data/README.md +12 -7
  7. data/Rakefile +32 -0
  8. data/SPEC.md +1277 -835
  9. data/ably.gemspec +14 -9
  10. data/lib/ably/auth.rb +30 -4
  11. data/lib/ably/exceptions.rb +10 -4
  12. data/lib/ably/logger.rb +7 -1
  13. data/lib/ably/models/channel_state_change.rb +1 -1
  14. data/lib/ably/models/connection_state_change.rb +1 -1
  15. data/lib/ably/models/device_details.rb +87 -0
  16. data/lib/ably/models/device_push_details.rb +86 -0
  17. data/lib/ably/models/error_info.rb +23 -2
  18. data/lib/ably/models/idiomatic_ruby_wrapper.rb +4 -4
  19. data/lib/ably/models/protocol_message.rb +32 -2
  20. data/lib/ably/models/push_channel_subscription.rb +89 -0
  21. data/lib/ably/modules/conversions.rb +1 -1
  22. data/lib/ably/modules/encodeable.rb +1 -1
  23. data/lib/ably/modules/exception_codes.rb +128 -0
  24. data/lib/ably/modules/model_common.rb +15 -2
  25. data/lib/ably/modules/state_machine.rb +2 -2
  26. data/lib/ably/realtime.rb +1 -0
  27. data/lib/ably/realtime/auth.rb +1 -1
  28. data/lib/ably/realtime/channel.rb +24 -102
  29. data/lib/ably/realtime/channel/channel_manager.rb +2 -6
  30. data/lib/ably/realtime/channel/channel_state_machine.rb +2 -2
  31. data/lib/ably/realtime/channel/publisher.rb +74 -0
  32. data/lib/ably/realtime/channel/push_channel.rb +62 -0
  33. data/lib/ably/realtime/client.rb +91 -3
  34. data/lib/ably/realtime/client/incoming_message_dispatcher.rb +6 -2
  35. data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +1 -1
  36. data/lib/ably/realtime/connection.rb +34 -20
  37. data/lib/ably/realtime/connection/connection_manager.rb +25 -9
  38. data/lib/ably/realtime/connection/websocket_transport.rb +1 -1
  39. data/lib/ably/realtime/presence.rb +4 -4
  40. data/lib/ably/realtime/presence/members_map.rb +3 -3
  41. data/lib/ably/realtime/push.rb +40 -0
  42. data/lib/ably/realtime/push/admin.rb +61 -0
  43. data/lib/ably/realtime/push/channel_subscriptions.rb +108 -0
  44. data/lib/ably/realtime/push/device_registrations.rb +105 -0
  45. data/lib/ably/rest.rb +1 -0
  46. data/lib/ably/rest/channel.rb +53 -17
  47. data/lib/ably/rest/channel/push_channel.rb +62 -0
  48. data/lib/ably/rest/client.rb +154 -32
  49. data/lib/ably/rest/middleware/parse_message_pack.rb +17 -1
  50. data/lib/ably/rest/presence.rb +1 -0
  51. data/lib/ably/rest/push.rb +42 -0
  52. data/lib/ably/rest/push/admin.rb +54 -0
  53. data/lib/ably/rest/push/channel_subscriptions.rb +121 -0
  54. data/lib/ably/rest/push/device_registrations.rb +103 -0
  55. data/lib/ably/version.rb +7 -2
  56. data/spec/acceptance/realtime/auth_spec.rb +245 -17
  57. data/spec/acceptance/realtime/channel_history_spec.rb +26 -20
  58. data/spec/acceptance/realtime/channel_spec.rb +177 -59
  59. data/spec/acceptance/realtime/client_spec.rb +153 -0
  60. data/spec/acceptance/realtime/connection_failures_spec.rb +72 -6
  61. data/spec/acceptance/realtime/connection_spec.rb +129 -18
  62. data/spec/acceptance/realtime/message_spec.rb +36 -34
  63. data/spec/acceptance/realtime/presence_spec.rb +201 -167
  64. data/spec/acceptance/realtime/push_admin_spec.rb +736 -0
  65. data/spec/acceptance/realtime/push_spec.rb +27 -0
  66. data/spec/acceptance/rest/auth_spec.rb +41 -3
  67. data/spec/acceptance/rest/base_spec.rb +2 -2
  68. data/spec/acceptance/rest/channel_spec.rb +79 -4
  69. data/spec/acceptance/rest/channels_spec.rb +6 -0
  70. data/spec/acceptance/rest/client_spec.rb +129 -10
  71. data/spec/acceptance/rest/message_spec.rb +158 -6
  72. data/spec/acceptance/rest/push_admin_spec.rb +952 -0
  73. data/spec/acceptance/rest/push_spec.rb +25 -0
  74. data/spec/acceptance/rest/time_spec.rb +1 -1
  75. data/spec/run_parallel_tests +33 -0
  76. data/spec/spec_helper.rb +1 -1
  77. data/spec/support/debug_failure_helper.rb +9 -5
  78. data/spec/support/test_app.rb +2 -2
  79. data/spec/unit/logger_spec.rb +10 -3
  80. data/spec/unit/models/device_details_spec.rb +102 -0
  81. data/spec/unit/models/device_push_details_spec.rb +101 -0
  82. data/spec/unit/models/error_info_spec.rb +51 -3
  83. data/spec/unit/models/message_spec.rb +17 -2
  84. data/spec/unit/models/presence_message_spec.rb +1 -1
  85. data/spec/unit/models/push_channel_subscription_spec.rb +86 -0
  86. data/spec/unit/modules/enum_spec.rb +1 -1
  87. data/spec/unit/realtime/client_spec.rb +13 -1
  88. data/spec/unit/realtime/connection_spec.rb +1 -1
  89. data/spec/unit/realtime/push_channel_spec.rb +36 -0
  90. data/spec/unit/rest/channel_spec.rb +8 -1
  91. data/spec/unit/rest/client_spec.rb +30 -0
  92. data/spec/unit/rest/push_channel_spec.rb +36 -0
  93. metadata +95 -26
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe Ably::Realtime::Push, :event_machine do
5
+ vary_by_protocol do
6
+ let(:default_options) { { key: api_key, environment: environment, protocol: protocol} }
7
+ let(:client_options) { default_options }
8
+ let(:client) do
9
+ Ably::Realtime::Client.new(client_options)
10
+ end
11
+ subject { client.push }
12
+
13
+ describe '#activate' do
14
+ it 'raises an unsupported exception' do
15
+ expect { subject.activate('foo') }.to raise_error(Ably::Exceptions::PushNotificationsNotSupported)
16
+ stop_reactor
17
+ end
18
+ end
19
+
20
+ describe '#deactivate' do
21
+ it 'raises an unsupported exception' do
22
+ expect { subject.deactivate('foo') }.to raise_error(Ably::Exceptions::PushNotificationsNotSupported)
23
+ stop_reactor
24
+ end
25
+ end
26
+ end
27
+ end
@@ -758,9 +758,10 @@ describe Ably::Auth do
758
758
  end
759
759
 
760
760
  it 'updates the persisted auth options that are then used for subsequent authorize requests' do
761
- expect(auth.options[:authUrl]).to be_nil
762
- auth.authorize({}, authUrl: 'http://foo.com')
763
- expect(auth.options[:authUrl]).to eql('http://foo.com')
761
+ auth_url = "https://echo.ably.io/?type=text&body=#{auth.request_token.token}"
762
+ expect(auth.options[:auth_url]).to be_nil
763
+ auth.authorize({}, auth_url: auth_url)
764
+ expect(auth.options[:auth_url]).to eql(auth_url)
764
765
  end
765
766
 
766
767
  context 'with a lambda for the :auth_callback option' do
@@ -1338,5 +1339,42 @@ describe Ably::Auth do
1338
1339
  expect(response).to be_a(Ably::Models::TokenDetails)
1339
1340
  end
1340
1341
  end
1342
+
1343
+ # RSC1, RSC1a, RSA3c, RSA3d
1344
+ context 'when using JWT' do
1345
+ let(:auth_url) { 'https://echo.ably.io/createJWT' }
1346
+ let(:token) { Faraday.get("#{auth_url}?keyName=#{key_name}&keySecret=#{key_secret}").body }
1347
+ let(:client) { Ably::Rest::Client.new(token: token, environment: environment, protocol: protocol) }
1348
+
1349
+ it 'authenticates correctly using the JWT token generated by the echo server' do
1350
+ expect(client.stats).to_not be_nil()
1351
+ end
1352
+
1353
+ context 'when the JWT embeds an Ably token' do
1354
+ let(:token) { Faraday.post(auth_url, { keyName: key_name, keySecret: key_secret, jwtType: :embedded }).body }
1355
+
1356
+ it 'authenticates correctly using the embedded token' do
1357
+ expect(client.stats).to_not be_nil()
1358
+ end
1359
+
1360
+ context 'and the requested token is encrypted' do
1361
+ let(:token) { Faraday.post(auth_url, { keyName: key_name, keySecret: key_secret, jwtType: :embedded, encrypted: 1 }).body }
1362
+
1363
+ it 'authenticates correctly using the embedded token' do
1364
+ expect(client.stats).to_not be_nil()
1365
+ end
1366
+ end
1367
+ end
1368
+
1369
+ # RSA4f, RSA8c
1370
+ context 'when the token requested is returned with application/jwt content type' do
1371
+ let(:auth_rest_client) { Ably::Rest::Client.new(default_options.merge(key: api_key)) }
1372
+ let(:auth_params) { { keyName: key_name, keySecret: key_secret, returnType: 'jwt' } }
1373
+ let(:token) { auth_rest_client.auth.request_token({ }, { auth_url: auth_url, auth_params: auth_params }).token }
1374
+ it 'authenticates correctly and pulls stats' do
1375
+ expect(client.stats).to_not be_nil()
1376
+ end
1377
+ end
1378
+ end
1341
1379
  end
1342
1380
  end
@@ -7,7 +7,7 @@ describe Ably::Rest do
7
7
 
8
8
  let(:client_options) { {} }
9
9
  let(:client) do
10
- Ably::Rest::Client.new(client_options.merge(key: 'appid.keyuid:keysecret'))
10
+ Ably::Rest::Client.new(client_options.merge(key: 'appid.keyuid:keysecret', log_retries_as_info: true))
11
11
  end
12
12
 
13
13
  let(:now) { Time.now - 1000 }
@@ -67,7 +67,7 @@ describe Ably::Rest do
67
67
 
68
68
  vary_by_protocol do
69
69
  let(:client) do
70
- Ably::Rest::Client.new(key: api_key, environment: environment, protocol: protocol)
70
+ Ably::Rest::Client.new(key: api_key, environment: environment, protocol: protocol, log_retries_as_info: true)
71
71
  end
72
72
 
73
73
  describe 'failed requests' do
@@ -40,7 +40,7 @@ describe Ably::Rest::Channel do
40
40
 
41
41
  it 'publishes the message without a client_id' do
42
42
  expect(client).to receive(:post).
43
- with("/channels/#{channel_name}/publish", hash_excluding(client_id: client_id)).
43
+ with("/channels/#{channel_name}/publish", hash_excluding(client_id: client_id), {}).
44
44
  and_return(double('response', status: 201))
45
45
 
46
46
  expect(channel.publish(name, data)).to eql(true)
@@ -82,6 +82,44 @@ describe Ably::Rest::Channel do
82
82
  end
83
83
  end
84
84
 
85
+ context 'with a Message object' do
86
+ let(:name) { random_str }
87
+
88
+ let(:message) do
89
+ Ably::Models::Message(name: name, data: data)
90
+ end
91
+
92
+ it 'publishes the message' do
93
+ expect(client).to receive(:post).once.and_call_original
94
+ expect(channel.publish(message)).to eql(true)
95
+ expect(channel.history.items.first.name).to eql(name)
96
+ end
97
+ end
98
+
99
+ context 'with a Message object and query params' do
100
+ let(:message) do
101
+ Ably::Models::Message(name: name, data: data)
102
+ end
103
+
104
+ it 'should fail to publish the message (RSL1l1)' do
105
+ expect(client).to receive(:post).once.and_call_original
106
+ expect { channel.publish(message, { _forceNack: 'true' }) }.to raise_error(Ably::Exceptions::InvalidRequest, /40099/)
107
+ end
108
+ end
109
+
110
+ context 'with Messages and query params' do
111
+ let(:messages) do
112
+ 10.times.map do |index|
113
+ { name: index.to_s, data: { "index" => index + 10 } }
114
+ end
115
+ end
116
+
117
+ it 'should fail to publish the message (RSL1l1)' do
118
+ expect(client).to receive(:post).once.and_call_original
119
+ expect { channel.publish(messages, { _forceNack: 'true' }) }.to raise_error(Ably::Exceptions::InvalidRequest, /40099/)
120
+ end
121
+ end
122
+
85
123
  context 'without adequate permissions on the channel' do
86
124
  let(:capability) { { onlyChannel: ['subscribe'] } }
87
125
  let(:client_options) { default_options.merge(use_token_auth: true, default_token_params: { capability: capability }) }
@@ -96,7 +134,7 @@ describe Ably::Rest::Channel do
96
134
  let(:data) { random_str }
97
135
 
98
136
  it 'publishes the message without a name attribute in the payload' do
99
- expect(client).to receive(:post).with(anything, { "data" => data }).once.and_call_original
137
+ expect(client).to receive(:post).with(anything, { "data" => data }, {}).once.and_call_original
100
138
  expect(channel.publish(nil, data)).to eql(true)
101
139
  expect(channel.history.items.first.name).to be_nil
102
140
  expect(channel.history.items.first.data).to eql(data)
@@ -107,7 +145,7 @@ describe Ably::Rest::Channel do
107
145
  let(:name) { random_str }
108
146
 
109
147
  it 'publishes the message without a data attribute in the payload' do
110
- expect(client).to receive(:post).with(anything, { "name" => name }).once.and_call_original
148
+ expect(client).to receive(:post).with(anything, { "name" => name }, {}).once.and_call_original
111
149
  expect(channel.publish(name)).to eql(true)
112
150
  expect(channel.history.items.first.name).to eql(name)
113
151
  expect(channel.history.items.first.data).to be_nil
@@ -118,7 +156,7 @@ describe Ably::Rest::Channel do
118
156
  let(:name) { random_str }
119
157
 
120
158
  it 'publishes the message without any attributes in the payload' do
121
- expect(client).to receive(:post).with(anything, {}).once.and_call_original
159
+ expect(client).to receive(:post).with(anything, {}, {}).once.and_call_original
122
160
  expect(channel.publish(nil)).to eql(true)
123
161
  expect(channel.history.items.first.name).to be_nil
124
162
  expect(channel.history.items.first.data).to be_nil
@@ -275,6 +313,43 @@ describe Ably::Rest::Channel do
275
313
  end
276
314
  end
277
315
  end
316
+
317
+ context 'with a frozen message event name' do
318
+ let(:event_name) { random_str.freeze }
319
+
320
+ it 'succeeds and publishes with an implicit client_id' do
321
+ channel.publish([name: event_name])
322
+ channel.publish(event_name)
323
+
324
+ if !(RUBY_VERSION.match(/^1\./) || RUBY_VERSION.match(/^2\.[012]/))
325
+ channel.publish(+'foo-bar') # new style freeze, see https://github.com/ably/ably-ruby/issues/132
326
+ else
327
+ channel.publish('foo-bar'.freeze) # new + style not supported until Ruby 2.3
328
+ end
329
+
330
+ channel.history do |messages|
331
+ expect(messages.length).to eql(3)
332
+ expect(messages.first.name).to eql(event_name)
333
+ expect(messages[1].name).to eql(event_name)
334
+ expect(messages.last.name).to eql('foo-bar')
335
+ end
336
+ end
337
+ end
338
+
339
+ context 'with a frozen payload' do
340
+ let(:payload) { { foo: random_str.freeze }.freeze }
341
+
342
+ it 'succeeds and publishes with an implicit client_id' do
343
+ channel.publish([data: payload])
344
+ channel.publish(nil, payload)
345
+
346
+ channel.history do |messages|
347
+ expect(messages.length).to eql(2)
348
+ expect(messages.first.data).to eql(payload)
349
+ expect(messages.last.data).to eql(payload)
350
+ end
351
+ end
352
+ end
278
353
  end
279
354
 
280
355
  describe '#history' do
@@ -60,5 +60,11 @@ describe Ably::Rest::Channels do
60
60
  let(:channel_with_options) { client.channels[channel_name, options] }
61
61
  it_behaves_like 'a channel'
62
62
  end
63
+
64
+ describe 'using a frozen channel name' do
65
+ let(:channel) { client.channels[channel_name.freeze] }
66
+ let(:channel_with_options) { client.channels[channel_name.freeze, options] }
67
+ it_behaves_like 'a channel'
68
+ end
63
69
  end
64
70
  end
@@ -4,7 +4,7 @@ require 'webrick'
4
4
 
5
5
  describe Ably::Rest::Client do
6
6
  vary_by_protocol do
7
- let(:default_options) { { environment: environment, protocol: protocol } }
7
+ let(:default_options) { { environment: environment, protocol: protocol, log_retries_as_info: true } }
8
8
  let(:client_options) { default_options }
9
9
 
10
10
  let(:client) { Ably::Rest::Client.new(client_options) }
@@ -27,6 +27,19 @@ describe Ably::Rest::Client do
27
27
  end
28
28
  end
29
29
 
30
+ context 'with an invalid API key' do
31
+ let(:client) { Ably::Rest::Client.new(client_options.merge(key: 'app.key:secret', log_level: :fatal)) }
32
+
33
+ it 'logs an entry with a help href url matching the code #TI5' do
34
+ begin
35
+ client.channels.get('foo').publish('test')
36
+ raise 'Expected Ably::Exceptions::ResourceMissing'
37
+ rescue Ably::Exceptions::ResourceMissing => err
38
+ expect err.to_s.match(%r{https://help.ably.io/error/40400})
39
+ end
40
+ end
41
+ end
42
+
30
43
  context 'with an explicit string :token' do
31
44
  let(:client) { Ably::Rest::Client.new(client_options.merge(token: random_str)) }
32
45
 
@@ -710,6 +723,109 @@ describe Ably::Rest::Client do
710
723
  expect(@fallback_request_count).to eql(2)
711
724
  end
712
725
  end
726
+
727
+ context 'to fail the primary host, allow a fallback to succeed, then later trigger a fallback to the primary host (#RSC15f)' do
728
+ before do
729
+ @request_count = 0
730
+ @primary_host_request_count = 0
731
+ @web_server = WEBrick::HTTPServer.new(:Port => port, :SSLEnable => false, :AccessLog => [], Logger: WEBrick::Log.new("/dev/null"))
732
+ @web_server.mount_proc "/channels/#{channel_name}/publish" do |req, res|
733
+ @request_count += 1
734
+ if req.header["host"].first.include?(primary_host)
735
+ @primary_host_request_count += 1
736
+ # Fail all requests to the primary host so that a fallback is used
737
+ # Except request 6 which should suceed and clear the fallback host preference
738
+ if @request_count == 6
739
+ res.status = 200
740
+ res['Content-Type'] = 'application/json'
741
+ res.body = '{}'
742
+ else
743
+ res.status = 500
744
+ end
745
+ else
746
+ # Fail the second request (first failed fallback of first request)
747
+ # Fail the third request on the previously succeeded fallback host to trigger an attempt on the primary host
748
+ if [2, 5].include?(@request_count)
749
+ res.status = 500
750
+ else
751
+ res.status = 200
752
+ res['Content-Type'] = 'application/json'
753
+ res.body = '{}'
754
+ end
755
+ end
756
+ end
757
+
758
+ Thread.new do
759
+ @web_server.start
760
+ end
761
+ end
762
+
763
+ let(:client_options) do
764
+ default_options.merge(
765
+ rest_host: primary_host,
766
+ fallback_hosts: fallbacks,
767
+ token: 'fake.token',
768
+ port: port,
769
+ tls: false,
770
+ log_level: :error
771
+ ).merge(additional_client_options)
772
+ end
773
+
774
+ let (:additional_client_options) { {} }
775
+
776
+ it 'succeeds and remembers fallback host preferences across requests' do
777
+ # Send a request, expect primary endpoint to fail, one fallback to fail, second fallback to succeed
778
+ client.channel(channel_name).publish('event', 'data')
779
+ expect(@request_count).to eql(3)
780
+ expect(fallbacks).to include(client.using_preferred_fallback_host?)
781
+ successfull_fallback = client.using_preferred_fallback_host?
782
+ expect(@primary_host_request_count).to eql(1)
783
+
784
+ # Send another request, which should go straight to the fallback as it succeeded previously
785
+ client.channel(channel_name).publish('event', 'data')
786
+ expect(@request_count).to eql(4)
787
+ expect(successfull_fallback).to eql(client.using_preferred_fallback_host?)
788
+ expect(@primary_host_request_count).to eql(1)
789
+
790
+ # A subsequent request should fail to the fallback, go the primary host and succeed
791
+ client.channel(channel_name).publish('event', 'data')
792
+ expect(@request_count).to eql(6)
793
+ expect(client.using_preferred_fallback_host?).to be_falsey
794
+ expect(@primary_host_request_count).to eql(2)
795
+
796
+ # A subsequent request will fail on the primary endpoint, and we expect the fallback to be used again
797
+ client.channel(channel_name).publish('event', 'data')
798
+ expect(@request_count).to eql(8)
799
+ expect(fallbacks).to include(client.using_preferred_fallback_host?)
800
+ successfull_fallback = client.using_preferred_fallback_host?
801
+ expect(@primary_host_request_count).to eql(3)
802
+
803
+ # Send another request, which should go straight to the fallback as it succeeded previously
804
+ client.channel(channel_name).publish('event', 'data')
805
+ expect(@request_count).to eql(9)
806
+ expect(successfull_fallback).to eql(client.using_preferred_fallback_host?)
807
+ expect(@primary_host_request_count).to eql(3)
808
+ end
809
+
810
+ context 'with custom :fallback_retry_timeout' do
811
+ let (:additional_client_options) { { fallback_retry_timeout: 5 } }
812
+
813
+ it 'stops using the preferred fallback after this time' do
814
+ # Send a request, expect primary endpoint to fail, one fallback to fail, second fallback to succeed
815
+ client.channel(channel_name).publish('event', 'data')
816
+ expect(@request_count).to eql(3)
817
+ expect(fallbacks).to include(client.using_preferred_fallback_host?)
818
+ expect(@primary_host_request_count).to eql(1)
819
+
820
+ # Wait for the preferred fallback cache to expire
821
+ sleep 5
822
+
823
+ # Send another request, which should go straight to the primary host again as fallback host is expired
824
+ client.channel(channel_name).publish('event', 'data')
825
+ expect(@primary_host_request_count).to eql(2)
826
+ end
827
+ end
828
+ end
713
829
  end
714
830
  end
715
831
 
@@ -724,7 +840,8 @@ describe Ably::Rest::Client do
724
840
  environment: env,
725
841
  key: api_key,
726
842
  http_max_retry_duration: max_retry_duration,
727
- http_max_retry_count: max_retry_count
843
+ http_max_retry_count: max_retry_count,
844
+ log_level: :fatal,
728
845
  )
729
846
  end
730
847
 
@@ -751,7 +868,7 @@ describe Ably::Rest::Client do
751
868
  end
752
869
 
753
870
  let(:client_options) {
754
- production_options.merge(fallback_hosts: custom_hosts, log_level: :error)
871
+ production_options.merge(fallback_hosts: custom_hosts, log_level: :fatal)
755
872
  }
756
873
 
757
874
  it 'attempts the fallback hosts as this is not an authentication failure' do
@@ -764,7 +881,7 @@ describe Ably::Rest::Client do
764
881
 
765
882
  context 'with an empty array of fallback hosts provided (#RSC15b, #TO3k6)' do
766
883
  let(:client_options) {
767
- production_options.merge(fallback_hosts: [])
884
+ production_options.merge(fallback_hosts: [], log_level: :fatal)
768
885
  }
769
886
 
770
887
  it 'does not attempt the fallback hosts as this is an authentication failure' do
@@ -789,7 +906,7 @@ describe Ably::Rest::Client do
789
906
  end
790
907
 
791
908
  let(:client_options) {
792
- production_options.merge(fallback_hosts: custom_hosts, log_level: :error)
909
+ production_options.merge(fallback_hosts: custom_hosts, log_level: :fatal)
793
910
  }
794
911
 
795
912
  it 'attempts the default fallback hosts as this is an authentication failure' do
@@ -966,7 +1083,7 @@ describe Ably::Rest::Client do
966
1083
  it 'sends a protocol version and lib version header (#G4, #RSC7a, #RSC7b)' do
967
1084
  client.channels.get('foo').publish("event")
968
1085
  expect(publish_message_stub).to have_been_requested
969
- expect(Ably::PROTOCOL_VERSION).to eql('1.0')
1086
+ expect(Ably::PROTOCOL_VERSION).to eql('1.1')
970
1087
  end
971
1088
  end
972
1089
  end
@@ -1084,7 +1201,7 @@ describe Ably::Rest::Client do
1084
1201
  end
1085
1202
 
1086
1203
  context 'option add_request_ids: true and specified fallback hosts', :webmock do
1087
- let(:client_options) { { key: api_key, fallback_hosts_use_default: true, add_request_ids: true, log_level: :error } }
1204
+ let(:client_options) { { key: api_key, fallback_hosts_use_default: true, add_request_ids: true, log_level: :error, log_retries_as_info: true } }
1088
1205
  let(:requests) { [] }
1089
1206
 
1090
1207
  before do
@@ -1140,7 +1257,7 @@ describe Ably::Rest::Client do
1140
1257
 
1141
1258
  context 'failed request logging', :prevent_log_stubbing do
1142
1259
  let(:custom_logger) { TestLogger.new }
1143
- let(:client_options) { default_options.merge(key: api_key, logger: custom_logger) }
1260
+ let(:client_options) { default_options.merge(key: api_key, logger: custom_logger, log_retries_as_info: false) }
1144
1261
 
1145
1262
  it 'is absent when requests do not fail' do
1146
1263
  client.time
@@ -1153,7 +1270,8 @@ describe Ably::Rest::Client do
1153
1270
  rest_host: 'non.existent.domain.local',
1154
1271
  fallback_hosts: [[environment, Ably::Rest::Client::DOMAIN].join('-')],
1155
1272
  key: api_key,
1156
- logger: custom_logger)
1273
+ logger: custom_logger,
1274
+ log_retries_as_info: false)
1157
1275
  end
1158
1276
 
1159
1277
  it 'is present with success message when requests do not actually fail' do
@@ -1169,7 +1287,8 @@ describe Ably::Rest::Client do
1169
1287
  rest_host: 'non.existent.domain.local',
1170
1288
  fallback_hosts: ['non2.existent.domain.local'],
1171
1289
  key: api_key,
1172
- logger: custom_logger)
1290
+ logger: custom_logger,
1291
+ log_retries_as_info: false)
1173
1292
  end
1174
1293
 
1175
1294
  it 'is present when all requests fail' do