ably-rest 0.9.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/ably-rest.gemspec +2 -1
  3. data/lib/submodules/ably-ruby/.travis.yml +6 -4
  4. data/lib/submodules/ably-ruby/CHANGELOG.md +52 -61
  5. data/lib/submodules/ably-ruby/README.md +10 -0
  6. data/lib/submodules/ably-ruby/SPEC.md +1473 -852
  7. data/lib/submodules/ably-ruby/ably.gemspec +2 -1
  8. data/lib/submodules/ably-ruby/lib/ably/auth.rb +57 -25
  9. data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +34 -8
  10. data/lib/submodules/ably-ruby/lib/ably/logger.rb +10 -1
  11. data/lib/submodules/ably-ruby/lib/ably/models/auth_details.rb +42 -0
  12. data/lib/submodules/ably-ruby/lib/ably/models/channel_state_change.rb +18 -4
  13. data/lib/submodules/ably-ruby/lib/ably/models/connection_details.rb +6 -3
  14. data/lib/submodules/ably-ruby/lib/ably/models/connection_state_change.rb +4 -3
  15. data/lib/submodules/ably-ruby/lib/ably/models/error_info.rb +1 -1
  16. data/lib/submodules/ably-ruby/lib/ably/models/message.rb +12 -1
  17. data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/base.rb +101 -97
  18. data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +13 -1
  19. data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +20 -3
  20. data/lib/submodules/ably-ruby/lib/ably/modules/async_wrapper.rb +7 -3
  21. data/lib/submodules/ably-ruby/lib/ably/modules/enum.rb +17 -7
  22. data/lib/submodules/ably-ruby/lib/ably/modules/event_emitter.rb +29 -14
  23. data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +7 -4
  24. data/lib/submodules/ably-ruby/lib/ably/modules/state_machine.rb +2 -4
  25. data/lib/submodules/ably-ruby/lib/ably/modules/uses_state_machine.rb +7 -3
  26. data/lib/submodules/ably-ruby/lib/ably/realtime.rb +2 -0
  27. data/lib/submodules/ably-ruby/lib/ably/realtime/auth.rb +79 -31
  28. data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +62 -26
  29. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +154 -65
  30. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_state_machine.rb +14 -15
  31. data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +16 -3
  32. data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +38 -29
  33. data/lib/submodules/ably-ruby/lib/ably/realtime/client/outgoing_message_dispatcher.rb +6 -1
  34. data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +108 -49
  35. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +165 -59
  36. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_state_machine.rb +22 -3
  37. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/websocket_transport.rb +19 -10
  38. data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +67 -45
  39. data/lib/submodules/ably-ruby/lib/ably/realtime/presence/members_map.rb +198 -36
  40. data/lib/submodules/ably-ruby/lib/ably/realtime/presence/presence_manager.rb +30 -6
  41. data/lib/submodules/ably-ruby/lib/ably/realtime/presence/presence_state_machine.rb +5 -12
  42. data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +3 -3
  43. data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +21 -8
  44. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/exceptions.rb +1 -3
  45. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/logger.rb +2 -2
  46. data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +1 -1
  47. data/lib/submodules/ably-ruby/lib/ably/util/pub_sub.rb +1 -1
  48. data/lib/submodules/ably-ruby/lib/ably/util/safe_deferrable.rb +26 -0
  49. data/lib/submodules/ably-ruby/lib/ably/version.rb +2 -2
  50. data/lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb +416 -99
  51. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +5 -3
  52. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +1011 -160
  53. data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +2 -2
  54. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +458 -27
  55. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +436 -97
  56. data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +52 -23
  57. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +5 -3
  58. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +1160 -105
  59. data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +151 -22
  60. data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +1 -1
  61. data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +88 -27
  62. data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +42 -15
  63. data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +4 -4
  64. data/lib/submodules/ably-ruby/spec/rspec_config.rb +2 -1
  65. data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +2 -2
  66. data/lib/submodules/ably-ruby/spec/shared/safe_deferrable_behaviour.rb +6 -2
  67. data/lib/submodules/ably-ruby/spec/support/debug_failure_helper.rb +20 -4
  68. data/lib/submodules/ably-ruby/spec/support/event_machine_helper.rb +32 -1
  69. data/lib/submodules/ably-ruby/spec/unit/auth_spec.rb +4 -11
  70. data/lib/submodules/ably-ruby/spec/unit/logger_spec.rb +28 -2
  71. data/lib/submodules/ably-ruby/spec/unit/models/auth_details_spec.rb +49 -0
  72. data/lib/submodules/ably-ruby/spec/unit/models/channel_state_change_spec.rb +23 -3
  73. data/lib/submodules/ably-ruby/spec/unit/models/connection_details_spec.rb +12 -1
  74. data/lib/submodules/ably-ruby/spec/unit/models/connection_state_change_spec.rb +15 -4
  75. data/lib/submodules/ably-ruby/spec/unit/models/message_spec.rb +34 -2
  76. data/lib/submodules/ably-ruby/spec/unit/models/presence_message_spec.rb +73 -2
  77. data/lib/submodules/ably-ruby/spec/unit/models/protocol_message_spec.rb +64 -6
  78. data/lib/submodules/ably-ruby/spec/unit/models/token_details_spec.rb +1 -1
  79. data/lib/submodules/ably-ruby/spec/unit/models/token_request_spec.rb +1 -1
  80. data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +2 -1
  81. data/lib/submodules/ably-ruby/spec/unit/modules/enum_spec.rb +69 -0
  82. data/lib/submodules/ably-ruby/spec/unit/modules/event_emitter_spec.rb +149 -22
  83. data/lib/submodules/ably-ruby/spec/unit/modules/state_emitter_spec.rb +9 -3
  84. data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +1 -1
  85. data/lib/submodules/ably-ruby/spec/unit/realtime/connection_spec.rb +8 -5
  86. data/lib/submodules/ably-ruby/spec/unit/realtime/incoming_message_dispatcher_spec.rb +1 -1
  87. data/lib/submodules/ably-ruby/spec/unit/realtime/presence_spec.rb +4 -3
  88. data/lib/submodules/ably-ruby/spec/unit/rest/client_spec.rb +1 -1
  89. data/lib/submodules/ably-ruby/spec/unit/util/crypto_spec.rb +3 -3
  90. metadata +7 -5
@@ -182,7 +182,7 @@ describe Ably::Auth do
182
182
  context 'with :query_time option' do
183
183
  let(:options) { { query_time: true } }
184
184
 
185
- it 'queries the server for the time' do
185
+ it 'queries the server for the time (#RSA10k)' do
186
186
  expect(client).to receive(:time).and_call_original
187
187
  auth.request_token({}, options)
188
188
  end
@@ -404,7 +404,7 @@ describe Ably::Auth do
404
404
  end
405
405
 
406
406
  it 'raises ServerError' do
407
- expect { auth.request_token({}, auth_options) }.to raise_error(Ably::Exceptions::ServerError)
407
+ expect { auth.request_token({}, auth_options) }.to raise_error(Ably::Exceptions::AuthenticationFailed)
408
408
  end
409
409
  end
410
410
 
@@ -415,7 +415,7 @@ describe Ably::Auth do
415
415
  end
416
416
 
417
417
  it 'raises InvalidResponseBody' do
418
- expect { auth.request_token({}, auth_options) }.to raise_error(Ably::Exceptions::InvalidResponseBody)
418
+ expect { auth.request_token({}, auth_options) }.to raise_error(Ably::Exceptions::AuthenticationFailed, /Content Type.*not supported/)
419
419
  end
420
420
  end
421
421
  end
@@ -612,7 +612,7 @@ describe Ably::Auth do
612
612
  end
613
613
  end
614
614
 
615
- context 'query_time: true' do
615
+ context 'query_time: true with authorize' do
616
616
  let(:local_time) { @now - 60 }
617
617
  let(:server_time) { @now }
618
618
 
@@ -621,7 +621,7 @@ describe Ably::Auth do
621
621
  allow(Time).to receive(:now).and_return(local_time)
622
622
  end
623
623
 
624
- it 'only queries the server time once and then works out the offset, query_time option is never persisted' do
624
+ it 'only queries the server time once and then works out the offset, query_time option is never persisted (#RSA10k)' do
625
625
  expect(client).to receive(:time).once.and_return(server_time)
626
626
 
627
627
  auth.authorize({}, query_time: true)
@@ -630,6 +630,27 @@ describe Ably::Auth do
630
630
  end
631
631
  end
632
632
 
633
+ context 'query_time: true ClientOption when instanced' do
634
+ let(:local_time) { @now - 60 }
635
+ let(:server_time) { @now }
636
+
637
+ let(:client_options) { default_options.merge(key: api_key, query_time: true) }
638
+
639
+ before do
640
+ @now = Time.now
641
+ allow(Time).to receive(:now).and_return(local_time)
642
+ end
643
+
644
+ it 'only queries the server time once and then works out the offset, query_time option is never persisted (#RSA10k)' do
645
+ expect(client).to receive(:time).once.and_return(server_time)
646
+
647
+ auth.authorize({})
648
+ auth.authorize({})
649
+ auth.authorize({})
650
+ expect(auth.auth_options).to_not have_key(:query_time)
651
+ end
652
+ end
653
+
633
654
  context 'TokenParams argument' do
634
655
  let(:default_token_params) { { ttl: 23 } }
635
656
 
@@ -644,11 +665,11 @@ describe Ably::Auth do
644
665
  expect(auth.token_params[:ttl]).to eql(23)
645
666
  end
646
667
 
647
- it 'updates defaults when present and all previous configured TokenParams are discarded' do
668
+ it 'updates defaults when present and all previous configured TokenParams are discarded (#RSA10g)' do
648
669
  old_token = auth.current_token_details
649
670
  auth.authorize({ client_id: 'bob' })
650
671
  expect(old_token).to_not eql(auth.current_token_details)
651
- expect(auth.token_params[:ttl]).to_not eql(23)
672
+ expect(auth.token_params[:ttl]).to_not eq(23)
652
673
  expect(auth.token_params[:client_id]).to eql('bob')
653
674
  end
654
675
 
@@ -656,6 +677,14 @@ describe Ably::Auth do
656
677
  auth.authorize({ client_id: 'bob' })
657
678
  expect { auth.token_params['key_name'] = 'new_name' }.to raise_error RuntimeError, /can't modify frozen.*Hash/
658
679
  end
680
+
681
+ it 'uses TokenParams#timestamp for this request but obtains a new timestamp for subsequence requests (#RSA10g)' do
682
+ timestamp = Time.now.to_i
683
+ expect(auth).to receive(:create_token_request).with({ timestamp: Time.now.to_i }, {}).once.and_call_original
684
+ expect(auth).to receive(:create_token_request).with({}, {}).once.and_call_original
685
+ auth.authorize(timestamp: Time.now.to_i)
686
+ auth.authorize
687
+ end
659
688
  end
660
689
 
661
690
  context 'AuthOptions argument' do
@@ -680,7 +709,7 @@ describe Ably::Auth do
680
709
  expect(auth.options[:auth_callback]).to eql(auth_callback)
681
710
  end
682
711
 
683
- it 'updates defaults when present and all previous configured AuthOptions are discarded' do
712
+ it 'updates defaults when present and all previous configured AuthOptions are discarded (#RSA10g)' do
684
713
  auth.authorize(nil, auth_method: :post)
685
714
  expect(@old_token).to_not eql(auth.current_token_details)
686
715
  expect(auth.options[:auth_callback]).to be_nil
@@ -691,6 +720,18 @@ describe Ably::Auth do
691
720
  auth.authorize(nil, auth_callback: Proc.new { '1231232.12321:12321312' })
692
721
  expect { auth.options['key_name'] = 'new_name' }.to raise_error RuntimeError, /can't modify frozen.*Hash/
693
722
  end
723
+
724
+ it 'uses AuthOptions#query_time for this request and will not query_time for subsequent requests (#RSA10g)' do
725
+ expect(client).to receive(:time).once.and_call_original
726
+ auth.authorize({}, query_time: true)
727
+ auth.authorize
728
+ end
729
+
730
+ it 'uses AuthOptions#query_time for this request and will query_time again if provided subsequently' do
731
+ expect(client).to receive(:time).twice.and_call_original
732
+ auth.authorize({}, query_time: true)
733
+ auth.authorize({}, query_time: true)
734
+ end
694
735
  end
695
736
 
696
737
  context 'with previous authorisation' do
@@ -768,7 +809,7 @@ describe Ably::Auth do
768
809
  @block_called = 0
769
810
  end
770
811
 
771
- let(:token_client) { Ably::Rest::Client.new(default_options.merge(key: api_key, token_params: { ttl: 3 })) }
812
+ let(:token_client) { Ably::Rest::Client.new(default_options.merge(key: api_key, default_token_params: { ttl: 3 })) }
772
813
  let(:client_options) {
773
814
  default_options.merge(token: token_client.auth.request_token.token, auth_callback: Proc.new do
774
815
  @block_called += 1
@@ -840,21 +881,22 @@ describe Ably::Auth do
840
881
  expect(subject['keyName']).to eql(key_name)
841
882
  end
842
883
 
843
- it 'uses the default TTL' do
844
- expect(subject['ttl']).to eql(Ably::Auth::TOKEN_DEFAULTS.fetch(:ttl) * 1000)
884
+ it 'specifies no TTL (#RSA5)' do
885
+ expect(subject['ttl']).to be_nil
845
886
  end
846
887
 
847
888
  context 'with a :ttl option below the Token expiry buffer that ensures tokens are renewed 15s before they expire as they are considered expired' do
848
- let(:ttl) { 1 }
889
+ let(:ttl) { 1 }
890
+ let(:token_params) { { ttl: ttl } }
849
891
 
850
892
  it 'uses the Token expiry buffer default + 10s to allow for a token request in flight' do
851
- expect(subject.ttl).to be > 1
852
- expect(subject.ttl).to be > Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER
893
+ expect(subject['ttl']).to be > 1
894
+ expect(subject['ttl']).to be > Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER
853
895
  end
854
896
  end
855
897
 
856
- it 'uses the default capability' do
857
- expect(subject['capability']).to eql(Ably::Auth::TOKEN_DEFAULTS.fetch(:capability).to_json)
898
+ it 'specifies no capability (#RSA6)' do
899
+ expect(subject['capability']).to be_nil
858
900
  end
859
901
 
860
902
  context 'the nonce' do
@@ -1048,6 +1090,42 @@ describe Ably::Auth do
1048
1090
  it 'cannot be renewed automatically' do
1049
1091
  expect(token_auth_client.auth).to_not be_token_renewable
1050
1092
  end
1093
+
1094
+ context 'and the token expires' do
1095
+ let(:ttl) { 1 }
1096
+
1097
+ before do
1098
+ stub_const 'Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER', 0 # allow token to be used even if about to expire
1099
+ stub_const 'Ably::Auth::TOKEN_DEFAULTS', Ably::Auth::TOKEN_DEFAULTS.merge(renew_token_buffer: 0) # Ensure tokens issued expire immediately after issue
1100
+
1101
+ @token = auth.request_token(ttl: ttl)
1102
+ WebMock.enable!
1103
+ WebMock.disable_net_connect!
1104
+
1105
+ token_expired = {
1106
+ "error" => {
1107
+ "statusCode" => 401,
1108
+ "code" => 40140,
1109
+ "message" => "Token expired"
1110
+ }
1111
+ }
1112
+
1113
+ stub_request(:post, "https://#{environment}-rest.ably.io/channels/foo/publish").
1114
+ to_return(status: 401, body: token_expired.to_json, headers: { 'Content-Type' => 'application/json' })
1115
+ end
1116
+
1117
+ after do
1118
+ WebMock.allow_net_connect!
1119
+ WebMock.disable!
1120
+ end
1121
+
1122
+ let(:token) { @token.token }
1123
+
1124
+ it 'should indicate an error and not retry the request (#RSA4a)' do
1125
+ sleep ttl + 1
1126
+ expect { token_auth_client.channels.get('foo').publish 'event' }.to raise_error(Ably::Exceptions::TokenExpired)
1127
+ end
1128
+ end
1051
1129
  end
1052
1130
 
1053
1131
  context 'when implicit as a result of using :client_id' do
@@ -1090,14 +1168,14 @@ describe Ably::Auth do
1090
1168
  expect(client.channel('foo').publish('event', 'data')).to be_truthy
1091
1169
  end
1092
1170
 
1093
- it 'with capability and TTL defaults' do
1171
+ it 'with capability and TTL defaults (#TK2a, #TK2b)' do
1094
1172
  client.channel('foo').publish('event', 'data')
1095
1173
 
1096
1174
  expect(token).to be_a(Ably::Models::TokenDetails)
1097
- capability_with_str_key = Ably::Auth::TOKEN_DEFAULTS.fetch(:capability)
1175
+ capability_with_str_key = { "*" => ["*"] } # Ably default is all capabilities
1098
1176
  capability = Hash[capability_with_str_key.keys.map(&:to_s).zip(capability_with_str_key.values)]
1099
1177
  expect(token.capability).to eq(capability)
1100
- expect(token.expires.to_i).to be_within(2).of(Time.now.to_i + Ably::Auth::TOKEN_DEFAULTS.fetch(:ttl))
1178
+ expect(token.expires.to_i).to be_within(2).of(Time.now.to_i + 60 * 60) # Ably default is 1hr
1101
1179
  expect(token.client_id).to eq(client_id)
1102
1180
  end
1103
1181
 
@@ -1107,6 +1185,54 @@ describe Ably::Auth do
1107
1185
  end
1108
1186
  end
1109
1187
 
1188
+ context 'when token expires' do
1189
+ before do
1190
+ stub_const 'Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER', 0 # allow token to be used even if about to expire
1191
+ stub_const 'Ably::Auth::TOKEN_DEFAULTS', Ably::Auth::TOKEN_DEFAULTS.merge(renew_token_buffer: 0) # Ensure tokens issued expire immediately after issue
1192
+ end
1193
+
1194
+ after do
1195
+ WebMock.allow_net_connect!
1196
+ WebMock.disable!
1197
+ end
1198
+
1199
+ let(:client_options) { default_options.merge(use_token_auth: true, key: api_key, query_time: true, default_token_params: { ttl: 2 }) }
1200
+ let(:channel) { client.channels.get(random_str) }
1201
+ let(:token_expired_response) do
1202
+ {
1203
+ "error" => {
1204
+ "statusCode" => 401,
1205
+ "code" => 40140,
1206
+ "message" => "Token expired"
1207
+ }
1208
+ }
1209
+ end
1210
+
1211
+ it 'automatically renews the token (#RSA4b)' do
1212
+ expect(auth.current_token_details).to be_nil
1213
+ channel.publish 'event'
1214
+ token = auth.current_token_details
1215
+ expect(token).to_not be_nil
1216
+ sleep 2.5
1217
+ channel.publish 'event'
1218
+ expect(auth.current_token_details).to_not eql(token)
1219
+ end
1220
+
1221
+ it 'fails if the token renewal fails (#RSA4b)' do
1222
+ expect(auth.current_token_details).to be_nil
1223
+ channel.publish 'event'
1224
+ token = auth.current_token_details
1225
+ expect(token).to_not be_nil
1226
+ sleep 2.5
1227
+ WebMock.enable!
1228
+ WebMock.disable_net_connect!
1229
+ stub_request(:post, "https://#{environment}-rest.ably.io/keys/#{TestApp.instance.key_name}/requestToken").
1230
+ to_return(status: 401, body: token_expired_response.to_json, headers: { 'Content-Type' => 'application/json' })
1231
+ expect { channel.publish 'event' }.to raise_error Ably::Exceptions::TokenExpired
1232
+ expect(auth.current_token_details).to eql(token)
1233
+ end
1234
+ end
1235
+
1110
1236
  context 'when :client_id is provided in a token' do
1111
1237
  let(:client_id) { '123' }
1112
1238
  let(:token) do
@@ -1199,7 +1325,7 @@ describe Ably::Auth do
1199
1325
  end
1200
1326
 
1201
1327
  context 'deprecated #authorise' do
1202
- let(:client_options) { default_options.merge(key: api_key, logger: custom_logger_object) }
1328
+ let(:client_options) { default_options.merge(key: api_key, logger: custom_logger_object, use_token_auth: true) }
1203
1329
  let(:custom_logger) do
1204
1330
  Class.new do
1205
1331
  def initialize
@@ -1207,8 +1333,11 @@ describe Ably::Auth do
1207
1333
  end
1208
1334
 
1209
1335
  [:fatal, :error, :warn, :info, :debug].each do |severity|
1210
- define_method severity do |message|
1211
- @messages << [severity, message]
1336
+ define_method severity do |message, &block|
1337
+ message_val = [message]
1338
+ message_val << block.call if block
1339
+
1340
+ @messages << [severity, message_val.compact.join(' ')]
1212
1341
  end
1213
1342
  end
1214
1343
 
@@ -84,7 +84,7 @@ describe Ably::Rest::Channel do
84
84
 
85
85
  context 'without adequate permissions on the channel' do
86
86
  let(:capability) { { onlyChannel: ['subscribe'] } }
87
- let(:client_options) { default_options.merge(use_token_auth: true, token_params: { capability: capability }) }
87
+ let(:client_options) { default_options.merge(use_token_auth: true, default_token_params: { capability: capability }) }
88
88
 
89
89
  it 'raises a permission error when publishing' do
90
90
  expect { channel.publish(name, data) }.to raise_error(Ably::Exceptions::UnauthorizedRequest, /not permitted/)
@@ -51,6 +51,14 @@ describe Ably::Rest::Client do
51
51
  end
52
52
  end
53
53
 
54
+ context 'with a non string :client_id' do
55
+ let(:client) { Ably::Rest::Client.new(client_options.merge(key: api_key, client_id: 1)) }
56
+
57
+ it 'raises an ArgumentError' do
58
+ expect { client.auth }.to raise_error ArgumentError, /client_id.*String/
59
+ end
60
+ end
61
+
54
62
  context 'with an invalid wildcard "*" :client_id' do
55
63
  it 'raises an exception' do
56
64
  expect { Ably::Rest::Client.new(client_options.merge(key: api_key, client_id: '*')) }.to raise_error ArgumentError
@@ -70,6 +78,21 @@ describe Ably::Rest::Client do
70
78
  end
71
79
  end
72
80
 
81
+ context 'with :default_token_params' do
82
+ let(:client) do
83
+ Ably::Rest::Client.new(client_options.merge(
84
+ default_token_params: { client_id: 'bob' },
85
+ use_token_auth: true,
86
+ key: api_key
87
+ ))
88
+ end
89
+
90
+ it 'overides the default token params (#TO3j11)' do
91
+ client.auth.authorize
92
+ expect(client.auth.client_id).to eql('bob')
93
+ end
94
+ end
95
+
73
96
  context 'with an :auth_callback Proc (clientId provided in library options instead of as a token_request param)' do
74
97
  let(:client) { Ably::Rest::Client.new(client_options.merge(client_id: client_id, auth_callback: Proc.new { token_request })) }
75
98
  let(:token_request) { client.auth.create_token_request({}, key_name: key_name, key_secret: key_secret) }
@@ -272,7 +295,7 @@ describe Ably::Rest::Client do
272
295
  context 'configured' do
273
296
  let(:client_options) { default_options.merge(key: api_key, environment: 'production') }
274
297
 
275
- it 'should make connection attempts to A.ably-realtime.com, B.ably-realtime.com, C.ably-realtime.com, D.ably-realtime.com, E.ably-realtime.com' do
298
+ it 'should make connection attempts to A.ably-realtime.com, B.ably-realtime.com, C.ably-realtime.com, D.ably-realtime.com, E.ably-realtime.com (#RSC15a)' do
276
299
  hosts = []
277
300
  5.times do
278
301
  hosts << client.fallback_connection.host
@@ -281,7 +304,7 @@ describe Ably::Rest::Client do
281
304
  end
282
305
  end
283
306
 
284
- context 'when environment is NOT production' do
307
+ context 'when environment is NOT production (#RSC15b)' do
285
308
  let(:client_options) { default_options.merge(environment: 'sandbox', key: api_key) }
286
309
  let!(:default_host_request_stub) do
287
310
  stub_request(:post, "https://#{environment}-#{Ably::Rest::Client::DOMAIN}#{path}").to_return do
@@ -304,7 +327,8 @@ describe Ably::Rest::Client do
304
327
  environment: nil,
305
328
  key: api_key,
306
329
  http_max_retry_duration: max_retry_duration,
307
- http_max_retry_count: max_retry_count
330
+ http_max_retry_count: max_retry_count,
331
+ log_level: :error
308
332
  )
309
333
  end
310
334
 
@@ -327,7 +351,7 @@ describe Ably::Rest::Client do
327
351
  end
328
352
  end
329
353
 
330
- it "tries fallback hosts #{http_defaults.fetch(:max_retry_count)} times" do
354
+ it "tries fallback hosts #{http_defaults.fetch(:max_retry_count)} times (#RSC15b, #RSC15b)" do
331
355
  expect { publish_block.call }.to raise_error Ably::Exceptions::ConnectionError, /ssl error message/
332
356
  expect(default_host_request_stub).to have_been_requested
333
357
  expect(first_fallback_request_stub).to have_been_requested
@@ -366,6 +390,43 @@ describe Ably::Rest::Client do
366
390
  end
367
391
  end
368
392
 
393
+ context 'and first request to primary endpoint fails' do
394
+ let(:client_options) do
395
+ default_options.merge(
396
+ environment: nil,
397
+ key: api_key,
398
+ http_max_retry_duration: max_retry_duration,
399
+ http_max_retry_count: max_retry_count,
400
+ log_level: :error
401
+ )
402
+ end
403
+ let(:requests) { [] }
404
+ let!(:default_host_request_stub) do
405
+ stub_request(:post, "https://#{Ably::Rest::Client::DOMAIN}#{path}").to_return do
406
+ requests << true
407
+ if requests.count == 1
408
+ raise Faraday::ConnectionFailed.new('connection failure error message')
409
+ else
410
+ {
411
+ headers: { 'Content-Type' => 'application/json' },
412
+ status: 200,
413
+ body: {}.to_json
414
+ }
415
+ end
416
+ end
417
+ end
418
+
419
+ it "tries a fallback host, and for the next request tries the primary endpoint again (#RSC15e)" do
420
+ expect { publish_block.call }.to raise_error Ably::Exceptions::ConnectionError, /ssl error message/
421
+ expect(default_host_request_stub).to have_been_requested
422
+ expect(first_fallback_request_stub).to have_been_requested
423
+ expect(requests.count).to eql(1)
424
+
425
+ publish_block.call
426
+ expect(requests.count).to eql(2)
427
+ end
428
+ end
429
+
369
430
  context 'and basic authentication fails' do
370
431
  let(:status) { 401 }
371
432
  let!(:default_host_request_stub) do
@@ -404,7 +465,7 @@ describe Ably::Rest::Client do
404
465
  stub_request(:post, "https://#{Ably::Rest::Client::DOMAIN}#{path}").to_return(&fallback_block)
405
466
  end
406
467
 
407
- it 'attempts the fallback hosts as this is an authentication failure' do
468
+ it 'attempts the fallback hosts as this is an authentication failure (#RSC15d)' do
408
469
  expect { publish_block.call }.to raise_error(Ably::Exceptions::ServerError)
409
470
  expect(default_host_request_stub).to have_been_requested
410
471
  expect(first_fallback_request_stub).to have_been_requested
@@ -450,10 +511,10 @@ describe Ably::Rest::Client do
450
511
  end
451
512
 
452
513
  let(:client_options) {
453
- production_options.merge(fallback_hosts: custom_hosts)
514
+ production_options.merge(fallback_hosts: custom_hosts, log_level: :error)
454
515
  }
455
516
 
456
- it 'attempts the fallback hosts as this is an authentication failure (#RSC15b, #TO3k6)' do
517
+ it 'attempts the fallback hosts as this is an authentication failure (#RSC15b, #RSC15a, #TO3k6)' do
457
518
  expect { publish_block.call }.to raise_error(Ably::Exceptions::ServerError)
458
519
  expect(default_host_request_stub).to have_been_requested
459
520
  expect(first_fallback_request_stub).to have_been_requested
@@ -461,7 +522,7 @@ describe Ably::Rest::Client do
461
522
  end
462
523
  end
463
524
 
464
- context 'with an empty array of fallback hosts provided (#RSC15b, #TO3k6)' do
525
+ context 'with an empty array of fallback hosts provided (#RSC15b, #RSC15a, #TO3k6)' do
465
526
  let(:client_options) {
466
527
  production_options.merge(fallback_hosts: [])
467
528
  }
@@ -485,7 +546,7 @@ describe Ably::Rest::Client do
485
546
 
486
547
  context 'and timing out the primary host' do
487
548
  before do
488
- @web_server = WEBrick::HTTPServer.new(:Port => port, :SSLEnable => false)
549
+ @web_server = WEBrick::HTTPServer.new(:Port => port, :SSLEnable => false, :AccessLog => [], Logger: WEBrick::Log.new("/dev/null"))
489
550
  @web_server.mount_proc "/channels/#{channel_name}/publish" do |req, res|
490
551
  if req.header["host"].first.include?(primary_host)
491
552
  @primary_host_requested = true
@@ -516,12 +577,13 @@ describe Ably::Rest::Client do
516
577
  port: port,
517
578
  tls: false,
518
579
  http_request_timeout: request_timeout,
519
- max_retry_duration: request_timeout * 3
580
+ max_retry_duration: request_timeout * 3,
581
+ log_level: :error
520
582
  )
521
583
  end
522
584
  let(:fail_fallback_request_count) { 1 }
523
585
 
524
- it 'tries one of the fallback hosts' do
586
+ it 'tries one of the fallback hosts (#RSC15d)' do
525
587
  client.channel(channel_name).publish('event', 'data')
526
588
  expect(@primary_host_requested).to be_truthy
527
589
  expect(@fallback_request_count).to eql(2)
@@ -537,12 +599,13 @@ describe Ably::Rest::Client do
537
599
  port: port,
538
600
  tls: false,
539
601
  http_request_timeout: request_timeout,
540
- max_retry_duration: request_timeout / 2
602
+ max_retry_duration: request_timeout / 2,
603
+ log_level: :error
541
604
  )
542
605
  end
543
606
  let(:fail_fallback_request_count) { 0 }
544
607
 
545
- it 'tries one of the fallback hosts' do
608
+ it 'tries one of the fallback hosts (#RSC15d)' do
546
609
  client.channel(channel_name).publish('event', 'data')
547
610
  expect(@primary_host_requested).to be_truthy
548
611
  expect(@fallback_request_count).to eql(1)
@@ -552,7 +615,7 @@ describe Ably::Rest::Client do
552
615
 
553
616
  context 'and failing the primary host' do
554
617
  before do
555
- @web_server = WEBrick::HTTPServer.new(:Port => port, :SSLEnable => false)
618
+ @web_server = WEBrick::HTTPServer.new(:Port => port, :SSLEnable => false, :AccessLog => [], Logger: WEBrick::Log.new("/dev/null"))
556
619
  @web_server.mount_proc "/channels/#{channel_name}/publish" do |req, res|
557
620
  if req.header["host"].first.include?(primary_host)
558
621
  @primary_host_requested = true
@@ -580,7 +643,8 @@ describe Ably::Rest::Client do
580
643
  fallback_hosts: fallbacks,
581
644
  token: 'fake.token',
582
645
  port: port,
583
- tls: false
646
+ tls: false,
647
+ log_level: :error
584
648
  )
585
649
  end
586
650
  let(:fail_fallback_request_count) { 1 }
@@ -632,10 +696,10 @@ describe Ably::Rest::Client do
632
696
  end
633
697
 
634
698
  let(:client_options) {
635
- production_options.merge(fallback_hosts: custom_hosts)
699
+ production_options.merge(fallback_hosts: custom_hosts, log_level: :error)
636
700
  }
637
701
 
638
- it 'attempts the fallback hosts as this is an authentication failure' do
702
+ it 'attempts the fallback hosts as this is not an authentication failure' do
639
703
  expect { publish_block.call }.to raise_error(Ably::Exceptions::ServerError)
640
704
  expect(default_host_request_stub).to have_been_requested
641
705
  expect(first_fallback_request_stub).to have_been_requested
@@ -661,10 +725,6 @@ describe Ably::Rest::Client do
661
725
  stub_const 'Ably::FALLBACK_HOSTS', custom_hosts
662
726
  end
663
727
 
664
- let(:client_options) {
665
- production_options.merge(fallback_hosts_use_default: true)
666
- }
667
-
668
728
  let!(:first_fallback_request_stub) do
669
729
  stub_request(:post, "https://#{Ably::FALLBACK_HOSTS[0]}#{path}").to_return(&fallback_block)
670
730
  end
@@ -674,7 +734,7 @@ describe Ably::Rest::Client do
674
734
  end
675
735
 
676
736
  let(:client_options) {
677
- production_options.merge(fallback_hosts: custom_hosts)
737
+ production_options.merge(fallback_hosts: custom_hosts, log_level: :error)
678
738
  }
679
739
 
680
740
  it 'attempts the default fallback hosts as this is an authentication failure' do
@@ -758,16 +818,16 @@ describe Ably::Rest::Client do
758
818
  expect(client.http_defaults[:open_timeout]).to eql(4)
759
819
  end
760
820
 
761
- specify '#http_request_timeout is 15s' do
762
- expect(client.http_defaults[:request_timeout]).to eql(15)
821
+ specify '#http_request_timeout is 10s' do
822
+ expect(client.http_defaults[:request_timeout]).to eql(10)
763
823
  end
764
824
 
765
825
  specify '#http_max_retry_count is 3' do
766
826
  expect(client.http_defaults[:max_retry_count]).to eql(3)
767
827
  end
768
828
 
769
- specify '#http_max_retry_duration is 10s' do
770
- expect(client.http_defaults[:max_retry_duration]).to eql(10)
829
+ specify '#http_max_retry_duration is 15s' do
830
+ expect(client.http_defaults[:max_retry_duration]).to eql(15)
771
831
  end
772
832
  end
773
833
 
@@ -848,9 +908,10 @@ describe Ably::Rest::Client do
848
908
  to_return(status: 201, body: '{}', headers: { 'Content-Type' => 'application/json' })
849
909
  end
850
910
 
851
- it 'sends a protocol version and lib version header' do
911
+ it 'sends a protocol version and lib version header (#G4, #RSC7a, #RSC7b)' do
852
912
  client.channels.get('foo').publish("event")
853
913
  expect(publish_message_stub).to have_been_requested
914
+ expect(Ably::PROTOCOL_VERSION).to eql('1.0')
854
915
  end
855
916
  end
856
917
  end