ably-rest 0.8.5 → 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/SPEC.md +1380 -631
- data/ably-rest.gemspec +11 -5
- data/lib/submodules/ably-ruby/.travis.yml +1 -1
- data/lib/submodules/ably-ruby/CHANGELOG.md +42 -48
- data/lib/submodules/ably-ruby/ably.gemspec +7 -1
- data/lib/submodules/ably-ruby/lib/ably.rb +2 -0
- data/lib/submodules/ably-ruby/lib/ably/auth.rb +155 -47
- data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +2 -0
- data/lib/submodules/ably-ruby/lib/ably/models/channel_state_change.rb +2 -3
- data/lib/submodules/ably-ruby/lib/ably/models/connection_details.rb +54 -0
- data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +14 -4
- data/lib/submodules/ably-ruby/lib/ably/models/token_details.rb +13 -7
- data/lib/submodules/ably-ruby/lib/ably/models/token_request.rb +1 -2
- data/lib/submodules/ably-ruby/lib/ably/modules/ably.rb +3 -2
- data/lib/submodules/ably-ruby/lib/ably/modules/message_emitter.rb +1 -3
- data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/realtime/auth.rb +6 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +15 -4
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +2 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +10 -3
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +11 -1
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +62 -6
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +58 -54
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +18 -5
- data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +9 -1
- data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +32 -14
- data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/version.rb +1 -1
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb +251 -11
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +12 -2
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +316 -24
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +93 -1
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +177 -86
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +284 -60
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +45 -6
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +4 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +181 -49
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/time_spec.rb +13 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +222 -4
- data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +132 -1
- data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +129 -28
- data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +7 -7
- data/lib/submodules/ably-ruby/spec/acceptance/rest/time_spec.rb +10 -0
- data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +41 -17
- data/lib/submodules/ably-ruby/spec/spec_helper.rb +1 -0
- data/lib/submodules/ably-ruby/spec/support/debug_failure_helper.rb +16 -0
- data/lib/submodules/ably-ruby/spec/unit/models/connection_details_spec.rb +60 -0
- data/lib/submodules/ably-ruby/spec/unit/models/protocol_message_spec.rb +45 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/event_emitter_spec.rb +3 -1
- data/lib/submodules/ably-ruby/spec/unit/realtime/channel_spec.rb +6 -5
- data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +5 -1
- data/lib/submodules/ably-ruby/spec/unit/realtime/connection_spec.rb +5 -1
- data/lib/submodules/ably-ruby/spec/unit/realtime/realtime_spec.rb +5 -1
- metadata +57 -13
@@ -22,6 +22,19 @@ describe Ably::Realtime::Client, '#time', :event_machine do
|
|
22
22
|
stop_reactor
|
23
23
|
end
|
24
24
|
end
|
25
|
+
|
26
|
+
context 'with reconfigured HTTP timeout' do
|
27
|
+
let(:client) do
|
28
|
+
auto_close Ably::Realtime::Client.new(http_request_timeout: 0.0001, key: api_key, environment: environment, protocol: protocol, log_level: :fatal)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should raise a timeout exception' do
|
32
|
+
client.time.errback do |error|
|
33
|
+
expect(error).to be_a Ably::Exceptions::ConnectionTimeout
|
34
|
+
stop_reactor
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
25
38
|
end
|
26
39
|
end
|
27
40
|
end
|
@@ -197,6 +197,85 @@ describe Ably::Auth do
|
|
197
197
|
end
|
198
198
|
end
|
199
199
|
|
200
|
+
context 'with :auth_url option merging', :webmock do
|
201
|
+
context 'with existing configured auth options' do
|
202
|
+
let(:client_id) { random_str }
|
203
|
+
let(:auth_url) { "https://www.fictitious.com/#{random_str}" }
|
204
|
+
let(:auth_method) { :get }
|
205
|
+
let(:auth_params) { { key: 'val', client_id: 'isOverridenByClient' } }
|
206
|
+
let(:auth_headers) { { 'Header-X' => 'val1', 'Header-Y' => 'val2' } }
|
207
|
+
|
208
|
+
let(:base_options) do
|
209
|
+
default_options.merge(
|
210
|
+
client_id: client_id,
|
211
|
+
auth_url: auth_url,
|
212
|
+
auth_params: auth_params,
|
213
|
+
auth_headers: auth_headers
|
214
|
+
)
|
215
|
+
end
|
216
|
+
let(:client_options) { base_options }
|
217
|
+
|
218
|
+
let!(:auth_request) do
|
219
|
+
stub_request(auth_method, auth_url).to_return(
|
220
|
+
:status => 201,
|
221
|
+
:body => '123123.12312321321312321', # token string
|
222
|
+
:headers => { 'Content-Type' => 'text/plain' }
|
223
|
+
)
|
224
|
+
end
|
225
|
+
|
226
|
+
let(:request_token_auth_options) { Hash.new }
|
227
|
+
let(:request_token_token_params) { Hash.new }
|
228
|
+
after do
|
229
|
+
client.auth.request_token(request_token_token_params, request_token_auth_options)
|
230
|
+
expect(auth_request).to have_been_requested
|
231
|
+
end
|
232
|
+
|
233
|
+
context 'using unspecified :auth_method' do
|
234
|
+
it 'requests a token using a GET request with provided headers, and merges client_id into auth_params' do
|
235
|
+
auth_request.with(headers: auth_headers)
|
236
|
+
auth_request.with(query: auth_params.merge(client_id: client_id))
|
237
|
+
end
|
238
|
+
|
239
|
+
context 'with provided token_params' do
|
240
|
+
let(:request_token_token_params) { { client_id: 'custom', key2: 'val2' } }
|
241
|
+
|
242
|
+
it 'merges provided token_params with existing auth_params and client_id' do
|
243
|
+
auth_request.with(query: auth_params.merge(client_id: client_id).merge(request_token_token_params))
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
context 'with provided auth option auth_params and auth_headers' do
|
248
|
+
let(:request_token_auth_options) { { auth_params: {}, auth_headers: {} } }
|
249
|
+
|
250
|
+
it 'replaces any preconfigured auth_params' do
|
251
|
+
auth_request.with(query: {}.merge(client_id: client_id))
|
252
|
+
auth_request.with(headers: { 'Accept'=>'*/*' }) # mock library needs at least one header, accept is default
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
context 'using :get :auth_method and query params in the URL' do
|
258
|
+
let(:auth_method) { :get }
|
259
|
+
let(:client_options) { base_options.merge(auth_method: :get, auth_url: "#{auth_url}?urlparam=true") }
|
260
|
+
|
261
|
+
it 'requests a token using a GET request with provided headers, and merges client_id into auth_params and existing URL querystring into new URL querystring' do
|
262
|
+
auth_request.with(headers: auth_headers)
|
263
|
+
auth_request.with(query: auth_params.merge(client_id: client_id).merge(urlparam: 'true'))
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
context 'using :post :auth_method' do
|
268
|
+
let(:auth_method) { :post }
|
269
|
+
let(:client_options) { base_options.merge(auth_method: :post) }
|
270
|
+
|
271
|
+
it 'requests a token using a POST request with provided headers, and merges client_id into auth_params as form-encoded post data' do
|
272
|
+
auth_request.with(headers: auth_headers)
|
273
|
+
auth_request.with(body: auth_params.merge(client_id: client_id))
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
200
279
|
context 'with :auth_url option', :webmock do
|
201
280
|
let(:auth_url) { 'https://www.fictitious.com/get_token' }
|
202
281
|
let(:auth_url_response) { { keyName: key_name }.to_json }
|
@@ -365,6 +444,18 @@ describe Ably::Auth do
|
|
365
444
|
it 'uses the token request returned from the callback when requesting a new token' do
|
366
445
|
expect(request_token.client_id).to eql(client_id)
|
367
446
|
end
|
447
|
+
|
448
|
+
context 'when authorised' do
|
449
|
+
before { auth.authorise(token_params, auth_callback: auth_callback) }
|
450
|
+
|
451
|
+
it "sets Auth#client_id to the new token's client_id" do
|
452
|
+
expect(auth.client_id).to eql(client_id)
|
453
|
+
end
|
454
|
+
|
455
|
+
it "sets Client#client_id to the new token's client_id" do
|
456
|
+
expect(client.client_id).to eql(client_id)
|
457
|
+
end
|
458
|
+
end
|
368
459
|
end
|
369
460
|
|
370
461
|
context 'that returns a TokenDetails JSON object' do
|
@@ -377,7 +468,11 @@ describe Ably::Auth do
|
|
377
468
|
let(:capability_str) { JSON.dump(capability) }
|
378
469
|
|
379
470
|
let!(:token_details) do
|
380
|
-
auth.request_token(token_params, auth_callback:
|
471
|
+
auth.request_token(token_params, auth_callback: auth_callback)
|
472
|
+
end
|
473
|
+
|
474
|
+
let(:auth_callback) do
|
475
|
+
Proc.new do |token_params_arg|
|
381
476
|
@block_called = true
|
382
477
|
@block_params = token_params_arg
|
383
478
|
{
|
@@ -388,7 +483,7 @@ describe Ably::Auth do
|
|
388
483
|
'expires' => expires.to_i * 1000,
|
389
484
|
'capability'=> capability_str
|
390
485
|
}
|
391
|
-
end
|
486
|
+
end
|
392
487
|
end
|
393
488
|
|
394
489
|
it 'calls the Proc when authenticating to obtain the request token' do
|
@@ -404,6 +499,18 @@ describe Ably::Auth do
|
|
404
499
|
expect(token_details.issued).to be_within(1).of(issued)
|
405
500
|
expect(token_details.capability).to eql(capability)
|
406
501
|
end
|
502
|
+
|
503
|
+
context 'when authorised' do
|
504
|
+
before { auth.authorise(token_params, auth_callback: auth_callback) }
|
505
|
+
|
506
|
+
it "sets Auth#client_id to the new token's client_id" do
|
507
|
+
expect(auth.client_id).to eql(client_id)
|
508
|
+
end
|
509
|
+
|
510
|
+
it "sets Client#client_id to the new token's client_id" do
|
511
|
+
expect(client.client_id).to eql(client_id)
|
512
|
+
end
|
513
|
+
end
|
407
514
|
end
|
408
515
|
|
409
516
|
context 'that returns a TokenDetails object' do
|
@@ -602,6 +709,37 @@ describe Ably::Auth do
|
|
602
709
|
end
|
603
710
|
end
|
604
711
|
end
|
712
|
+
|
713
|
+
context 'with an explicit ClientOptions client_id' do
|
714
|
+
let(:client_id) { random_str }
|
715
|
+
let(:client_options) { default_options.merge(auth_callback: Proc.new { auth_token_object }, client_id: client_id) }
|
716
|
+
let(:auth_client) { Ably::Rest::Client.new(default_options.merge(key: api_key, client_id: 'invalid')) }
|
717
|
+
|
718
|
+
context 'and an incompatible client_id in a TokenDetails object passed to the auth callback' do
|
719
|
+
let(:auth_token_object) { auth_client.auth.request_token }
|
720
|
+
|
721
|
+
it 'rejects a TokenDetails object with an incompatible client_id and raises an exception' do
|
722
|
+
expect { client.auth.authorise({}, force: true) }.to raise_error Ably::Exceptions::IncompatibleClientId
|
723
|
+
end
|
724
|
+
end
|
725
|
+
|
726
|
+
context 'and an incompatible client_id in a TokenRequest object passed to the auth callback and raises an exception' do
|
727
|
+
let(:auth_token_object) { auth_client.auth.create_token_request }
|
728
|
+
|
729
|
+
it 'rejects a TokenRequests object with an incompatible client_id and raises an exception' do
|
730
|
+
expect { client.auth.authorise({}, force: true) }.to raise_error Ably::Exceptions::IncompatibleClientId
|
731
|
+
end
|
732
|
+
end
|
733
|
+
|
734
|
+
context 'and a token string without any retrievable client_id' do
|
735
|
+
let(:auth_token_object) { auth_client.auth.request_token(client_id: 'different').token }
|
736
|
+
|
737
|
+
it 'rejects a TokenRequests object with an incompatible client_id and raises an exception' do
|
738
|
+
client.auth.authorise({}, force: true)
|
739
|
+
expect(client.client_id).to eql(client_id)
|
740
|
+
end
|
741
|
+
end
|
742
|
+
end
|
605
743
|
end
|
606
744
|
|
607
745
|
describe '#create_token_request' do
|
@@ -799,7 +937,7 @@ describe Ably::Auth do
|
|
799
937
|
end
|
800
938
|
end
|
801
939
|
|
802
|
-
context 'when implicit as a result of using :
|
940
|
+
context 'when implicit as a result of using :client_id' do
|
803
941
|
let(:client_id) { '999' }
|
804
942
|
let(:client) do
|
805
943
|
Ably::Rest::Client.new(key: api_key, client_id: client_id, environment: environment, protocol: protocol)
|
@@ -849,11 +987,91 @@ describe Ably::Auth do
|
|
849
987
|
expect(token.expires.to_i).to be_within(2).of(Time.now.to_i + Ably::Auth::TOKEN_DEFAULTS.fetch(:ttl))
|
850
988
|
expect(token.client_id).to eq(client_id)
|
851
989
|
end
|
990
|
+
|
991
|
+
specify '#client_id contains the client_id' do
|
992
|
+
expect(client.auth.client_id).to eql(client_id)
|
993
|
+
end
|
994
|
+
end
|
995
|
+
end
|
996
|
+
|
997
|
+
context 'when :client_id is provided in a token' do
|
998
|
+
let(:client_id) { '123' }
|
999
|
+
let(:token) do
|
1000
|
+
Ably::Rest::Client.new(key: api_key, environment: environment, protocol: protocol).auth.request_token(client_id: client_id)
|
1001
|
+
end
|
1002
|
+
let(:client) do
|
1003
|
+
Ably::Rest::Client.new(token: token, environment: environment, protocol: protocol)
|
1004
|
+
end
|
1005
|
+
|
1006
|
+
specify '#client_id contains the client_id' do
|
1007
|
+
expect(client.auth.client_id).to eql(client_id)
|
1008
|
+
end
|
1009
|
+
end
|
1010
|
+
end
|
1011
|
+
|
1012
|
+
describe '#client_id_validated?' do
|
1013
|
+
let(:auth) { Ably::Rest::Client.new(default_options.merge(key: api_key)).auth }
|
1014
|
+
|
1015
|
+
context 'when using basic auth' do
|
1016
|
+
let(:client_options) { default_options.merge(key: api_key) }
|
1017
|
+
|
1018
|
+
it 'is false as basic auth users do not have an identity' do
|
1019
|
+
expect(client.auth).to_not be_client_id_validated
|
1020
|
+
end
|
1021
|
+
end
|
1022
|
+
|
1023
|
+
context 'when using a token auth string for a token with a client_id' do
|
1024
|
+
let(:client_options) { default_options.merge(token: auth.request_token(client_id: 'present').token) }
|
1025
|
+
|
1026
|
+
it 'is false as identification is not possible from an opaque token string' do
|
1027
|
+
expect(client.auth).to_not be_client_id_validated
|
1028
|
+
end
|
1029
|
+
end
|
1030
|
+
|
1031
|
+
context 'when using a token' do
|
1032
|
+
context 'with a client_id' do
|
1033
|
+
let(:client_options) { default_options.merge(token: auth.request_token(client_id: 'present')) }
|
1034
|
+
|
1035
|
+
it 'is true' do
|
1036
|
+
expect(client.auth).to be_client_id_validated
|
1037
|
+
end
|
1038
|
+
end
|
1039
|
+
|
1040
|
+
context 'with no client_id (anonymous)' do
|
1041
|
+
let(:client_options) { default_options.merge(token: auth.request_token(client_id: nil)) }
|
1042
|
+
|
1043
|
+
it 'is true' do
|
1044
|
+
expect(client.auth).to be_client_id_validated
|
1045
|
+
end
|
1046
|
+
end
|
1047
|
+
|
1048
|
+
context 'with a wildcard client_id (anonymous)' do
|
1049
|
+
let(:client_options) { default_options.merge(token: auth.request_token(client_id: '*')) }
|
1050
|
+
|
1051
|
+
it 'is false' do
|
1052
|
+
expect(client.auth).to be_client_id_validated
|
1053
|
+
end
|
1054
|
+
end
|
1055
|
+
end
|
1056
|
+
|
1057
|
+
context 'when using a token request with a client_id' do
|
1058
|
+
let(:client_options) { default_options.merge(token: auth.create_token_request(client_id: 'present')) }
|
1059
|
+
|
1060
|
+
it 'is not true as identification is not confirmed until authenticated' do
|
1061
|
+
expect(client.auth).to_not be_client_id_validated
|
1062
|
+
end
|
1063
|
+
|
1064
|
+
context 'after authentication' do
|
1065
|
+
before { client.channel('test').publish('a') }
|
1066
|
+
|
1067
|
+
it 'is true as identification is completed during implicit authentication' do
|
1068
|
+
expect(client.auth).to be_client_id_validated
|
1069
|
+
end
|
852
1070
|
end
|
853
1071
|
end
|
854
1072
|
end
|
855
1073
|
|
856
|
-
context 'when using
|
1074
|
+
context 'when using a :key and basic auth' do
|
857
1075
|
specify '#using_token_auth? is false' do
|
858
1076
|
expect(auth).to_not be_using_token_auth
|
859
1077
|
end
|
@@ -125,6 +125,137 @@ describe Ably::Rest::Channel do
|
|
125
125
|
end
|
126
126
|
end
|
127
127
|
end
|
128
|
+
|
129
|
+
context 'identified clients' do
|
130
|
+
context 'when authenticated with a wildcard client_id' do
|
131
|
+
let(:token) { Ably::Rest::Client.new(default_options).auth.request_token(client_id: '*') }
|
132
|
+
let(:client_options) { default_options.merge(key: nil, token: token) }
|
133
|
+
let(:client) { Ably::Rest::Client.new(client_options) }
|
134
|
+
let(:channel) { client.channels.get(channel_name) }
|
135
|
+
|
136
|
+
context 'with a valid client_id in the message' do
|
137
|
+
it 'succeeds' do
|
138
|
+
channel.publish([name: 'event', client_id: 'valid'])
|
139
|
+
channel.history do |messages|
|
140
|
+
expect(messages.first.client_id).to eql('valid')
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context 'with a wildcard client_id in the message' do
|
146
|
+
it 'throws an exception' do
|
147
|
+
expect { channel.publish([name: 'event', client_id: '*']) }.to raise_error Ably::Exceptions::IncompatibleClientId
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
context 'with an empty client_id in the message' do
|
152
|
+
it 'succeeds and publishes without a client_id' do
|
153
|
+
channel.publish([name: 'event', client_id: nil])
|
154
|
+
channel.history do |messages|
|
155
|
+
expect(messages.first.client_id).to eql('valid')
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
context 'when authenticated with a Token string with an implicit client_id' do
|
162
|
+
let(:token) { Ably::Rest::Client.new(default_options).auth.request_token(client_id: 'valid').token }
|
163
|
+
let(:client_options) { default_options.merge(key: nil, token: token) }
|
164
|
+
let(:client) { Ably::Rest::Client.new(client_options) }
|
165
|
+
let(:channel) { client.channels.get(channel_name) }
|
166
|
+
|
167
|
+
context 'without having a confirmed identity' do
|
168
|
+
context 'with a valid client_id in the message' do
|
169
|
+
it 'succeeds' do
|
170
|
+
channel.publish([name: 'event', client_id: 'valid'])
|
171
|
+
channel.history do |messages|
|
172
|
+
expect(messages.first.client_id).to eql('valid')
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
context 'with an invalid client_id in the message' do
|
178
|
+
it 'succeeds in the client library but then fails when published to Ably' do
|
179
|
+
expect { channel.publish([name: 'event', client_id: 'invalid']) }.to raise_error Ably::Exceptions::InvalidRequest, /mismatched clientId/
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
context 'with an empty client_id in the message' do
|
184
|
+
it 'succeeds and publishes with an implicit client_id' do
|
185
|
+
channel.publish([name: 'event', client_id: nil])
|
186
|
+
channel.history do |messages|
|
187
|
+
expect(messages.first.client_id).to eql('valid')
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
context 'when authenticated with TokenDetails with a valid client_id' do
|
195
|
+
let(:token) { Ably::Rest::Client.new(default_options).auth.request_token(client_id: 'valid') }
|
196
|
+
let(:client_options) { default_options.merge(key: nil, token: token) }
|
197
|
+
let(:client) { Ably::Rest::Client.new(client_options) }
|
198
|
+
let(:channel) { client.channels.get(channel_name) }
|
199
|
+
|
200
|
+
context 'with a valid client_id in the message' do
|
201
|
+
it 'succeeds' do
|
202
|
+
channel.publish([name: 'event', client_id: 'valid'])
|
203
|
+
channel.history do |messages|
|
204
|
+
expect(messages.first.client_id).to eql('valid')
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
context 'with a wildcard client_id in the message' do
|
210
|
+
it 'throws an exception' do
|
211
|
+
expect { channel.publish([name: 'event', client_id: '*']) }.to raise_error Ably::Exceptions::IncompatibleClientId
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
context 'with an invalid client_id in the message' do
|
216
|
+
it 'throws an exception' do
|
217
|
+
expect { channel.publish([name: 'event', client_id: 'invalid']) }.to raise_error Ably::Exceptions::IncompatibleClientId
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
context 'with an empty client_id in the message' do
|
222
|
+
it 'succeeds and publishes with an implicit client_id' do
|
223
|
+
channel.publish([name: 'event', client_id: nil])
|
224
|
+
channel.history do |messages|
|
225
|
+
expect(messages.first.client_id).to eql('valid')
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
context 'when anonymous and no client_id' do
|
232
|
+
let(:token) { Ably::Rest::Client.new(default_options).auth.request_token(client_id: nil) }
|
233
|
+
let(:client_options) { default_options.merge(key: nil, token: token) }
|
234
|
+
let(:client) { Ably::Rest::Client.new(client_options) }
|
235
|
+
let(:channel) { client.channels.get(channel_name) }
|
236
|
+
|
237
|
+
context 'with a client_id in the message' do
|
238
|
+
it 'throws an exception' do
|
239
|
+
expect { channel.publish([name: 'event', client_id: '*']) }.to raise_error Ably::Exceptions::IncompatibleClientId
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
context 'with a wildcard client_id in the message' do
|
244
|
+
it 'throws an exception' do
|
245
|
+
expect { channel.publish([name: 'event', client_id: '*']) }.to raise_error Ably::Exceptions::IncompatibleClientId
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
context 'with an empty client_id in the message' do
|
250
|
+
it 'succeeds and publishes with an implicit client_id' do
|
251
|
+
channel.publish([name: 'event', client_id: nil])
|
252
|
+
channel.history do |messages|
|
253
|
+
expect(messages.first.client_id).to be_nil
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
128
259
|
end
|
129
260
|
|
130
261
|
describe '#history' do
|
@@ -246,7 +377,7 @@ describe Ably::Rest::Channel do
|
|
246
377
|
let!(:history_stub) {
|
247
378
|
query_params = default_history_options
|
248
379
|
.merge(option => milliseconds).map { |k, v| "#{k}=#{v}" }.join('&')
|
249
|
-
stub_request(:get, "#{endpoint}/channels/#{
|
380
|
+
stub_request(:get, "#{endpoint}/channels/#{Addressable::URI.encode(channel_name)}/messages?#{query_params}").
|
250
381
|
to_return(:body => '{}', :headers => { 'Content-Type' => 'application/json' })
|
251
382
|
}
|
252
383
|
|
@@ -8,7 +8,7 @@ describe Ably::Rest::Client do
|
|
8
8
|
|
9
9
|
let(:client) { Ably::Rest::Client.new(client_options) }
|
10
10
|
|
11
|
-
|
11
|
+
http_defaults = Ably::Rest::Client::HTTP_DEFAULTS
|
12
12
|
|
13
13
|
def encode64(text)
|
14
14
|
Base64.encode64(text).gsub("\n", '')
|
@@ -50,6 +50,12 @@ describe Ably::Rest::Client do
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
+
context 'with an invalid wildcard "*" :client_id' do
|
54
|
+
it 'raises an exception' do
|
55
|
+
expect { Ably::Rest::Client.new(client_options.merge(key: api_key, client_id: '*')) }.to raise_error ArgumentError
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
53
59
|
context 'with an :auth_callback Proc' do
|
54
60
|
let(:client) { Ably::Rest::Client.new(client_options.merge(auth_callback: Proc.new { token_request })) }
|
55
61
|
|
@@ -83,7 +89,7 @@ describe Ably::Rest::Client do
|
|
83
89
|
|
84
90
|
context 'before any REST request' do
|
85
91
|
before do
|
86
|
-
expect(client.auth).to receive(:token_request_from_auth_url).with(token_request_url, hash_including(:auth_method => :get)).once do
|
92
|
+
expect(client.auth).to receive(:token_request_from_auth_url).with(token_request_url, hash_including(:auth_method => :get), anything).once do
|
87
93
|
client.auth.create_token_request(client_id: client_id)
|
88
94
|
end
|
89
95
|
end
|
@@ -154,8 +160,10 @@ describe Ably::Rest::Client do
|
|
154
160
|
send("token_request_#{@request_index > 2 ? 'next' : @request_index}")
|
155
161
|
end))
|
156
162
|
end
|
157
|
-
let(:
|
158
|
-
let(:
|
163
|
+
let(:client_id) { random_str }
|
164
|
+
let(:client_id_2) { client_id }
|
165
|
+
let(:token_request_1) { client.auth.create_token_request({}, token_request_options.merge(client_id: client_id)) }
|
166
|
+
let(:token_request_2) { client.auth.create_token_request({}, token_request_options.merge(client_id: client_id_2)) }
|
159
167
|
|
160
168
|
# If token expires against whilst runnig tests in a slower CI environment then use this token
|
161
169
|
let(:token_request_next) { client.auth.create_token_request({}, token_request_options.merge(client_id: random_str)) }
|
@@ -177,6 +185,16 @@ describe Ably::Rest::Client do
|
|
177
185
|
expect { client.channel('channel_name').publish('event', 'message') }.to change { client.auth.current_token_details }
|
178
186
|
expect(client.auth.current_token_details.client_id).to eql(token_request_2.client_id)
|
179
187
|
end
|
188
|
+
|
189
|
+
context 'with a different client_id in the subsequent token' do
|
190
|
+
let(:client_id_2) { random_str }
|
191
|
+
|
192
|
+
it 'fails to authenticate and raises an exception' do
|
193
|
+
client.channel('channel_name').publish('event', 'message')
|
194
|
+
sleep 1
|
195
|
+
expect { client.channel('channel_name').publish('event', 'message') }.to raise_error(Ably::Exceptions::IncompatibleClientId)
|
196
|
+
end
|
197
|
+
end
|
180
198
|
end
|
181
199
|
|
182
200
|
context 'when token has not expired' do
|
@@ -195,25 +213,53 @@ describe Ably::Rest::Client do
|
|
195
213
|
end
|
196
214
|
|
197
215
|
context 'connection transport' do
|
198
|
-
|
216
|
+
context 'defaults' do
|
217
|
+
let(:client_options) { default_options.merge(key: api_key) }
|
218
|
+
|
219
|
+
context 'for default host' do
|
220
|
+
it "is configured to timeout connection opening in #{http_defaults.fetch(:open_timeout)} seconds" do
|
221
|
+
expect(client.connection.options.open_timeout).to eql(http_defaults.fetch(:open_timeout))
|
222
|
+
end
|
199
223
|
|
200
|
-
|
201
|
-
|
202
|
-
|
224
|
+
it "is configured to timeout connection requests in #{http_defaults.fetch(:request_timeout)} seconds" do
|
225
|
+
expect(client.connection.options.timeout).to eql(http_defaults.fetch(:request_timeout))
|
226
|
+
end
|
203
227
|
end
|
204
228
|
|
205
|
-
|
206
|
-
|
229
|
+
context 'for the fallback hosts' do
|
230
|
+
it "is configured to timeout connection opening in #{http_defaults.fetch(:open_timeout)} seconds" do
|
231
|
+
expect(client.fallback_connection.options.open_timeout).to eql(http_defaults.fetch(:open_timeout))
|
232
|
+
end
|
233
|
+
|
234
|
+
it "is configured to timeout connection requests in #{http_defaults.fetch(:request_timeout)} seconds" do
|
235
|
+
expect(client.fallback_connection.options.timeout).to eql(http_defaults.fetch(:request_timeout))
|
236
|
+
end
|
207
237
|
end
|
208
238
|
end
|
209
239
|
|
210
|
-
context '
|
211
|
-
|
212
|
-
|
240
|
+
context 'with custom http_open_timeout and http_request_timeout options' do
|
241
|
+
let(:http_open_timeout) { 999 }
|
242
|
+
let(:http_request_timeout) { 666 }
|
243
|
+
let(:client_options) { default_options.merge(key: api_key, http_open_timeout: http_open_timeout, http_request_timeout: http_request_timeout) }
|
244
|
+
|
245
|
+
context 'for default host' do
|
246
|
+
it 'is configured to use custom open timeout' do
|
247
|
+
expect(client.connection.options.open_timeout).to eql(http_open_timeout)
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'is configured to use custom request timeout' do
|
251
|
+
expect(client.connection.options.timeout).to eql(http_request_timeout)
|
252
|
+
end
|
213
253
|
end
|
214
254
|
|
215
|
-
|
216
|
-
|
255
|
+
context 'for the fallback hosts' do
|
256
|
+
it "is configured to timeout connection opening in #{http_defaults.fetch(:open_timeout)} seconds" do
|
257
|
+
expect(client.fallback_connection.options.open_timeout).to eql(http_open_timeout)
|
258
|
+
end
|
259
|
+
|
260
|
+
it "is configured to timeout connection requests in #{http_defaults.fetch(:request_timeout)} seconds" do
|
261
|
+
expect(client.fallback_connection.options.timeout).to eql(http_request_timeout)
|
262
|
+
end
|
217
263
|
end
|
218
264
|
end
|
219
265
|
end
|
@@ -249,19 +295,20 @@ describe Ably::Rest::Client do
|
|
249
295
|
|
250
296
|
context 'when environment is production' do
|
251
297
|
let(:custom_hosts) { %w(A.ably-realtime.com B.ably-realtime.com) }
|
252
|
-
let(:
|
253
|
-
let(:
|
254
|
-
let(:client_options) { default_options.merge(environment: nil, key: api_key) }
|
298
|
+
let(:max_retry_count) { 2 }
|
299
|
+
let(:max_retry_duration) { 0.5 }
|
255
300
|
let(:fallback_block) { Proc.new { raise Faraday::SSLError.new('ssl error message') } }
|
301
|
+
let(:client_options) do
|
302
|
+
default_options.merge(
|
303
|
+
environment: nil,
|
304
|
+
key: api_key,
|
305
|
+
http_max_retry_duration: max_retry_duration,
|
306
|
+
http_max_retry_count: max_retry_count
|
307
|
+
)
|
308
|
+
end
|
256
309
|
|
257
310
|
before do
|
258
311
|
stub_const 'Ably::FALLBACK_HOSTS', custom_hosts
|
259
|
-
stub_const 'Ably::Rest::Client::CONNECTION_RETRY', {
|
260
|
-
single_request_open_timeout: 4,
|
261
|
-
single_request_timeout: 15,
|
262
|
-
cumulative_request_open_timeout: cumulative_timeout,
|
263
|
-
max_retry_attempts: max_attempts
|
264
|
-
}
|
265
312
|
end
|
266
313
|
|
267
314
|
let!(:first_fallback_request_stub) do
|
@@ -279,17 +326,17 @@ describe Ably::Rest::Client do
|
|
279
326
|
end
|
280
327
|
end
|
281
328
|
|
282
|
-
it "tries fallback hosts #{
|
329
|
+
it "tries fallback hosts #{http_defaults.fetch(:max_retry_count)} times" do
|
283
330
|
expect { publish_block.call }.to raise_error Ably::Exceptions::ConnectionError, /ssl error message/
|
284
331
|
expect(default_host_request_stub).to have_been_requested
|
285
332
|
expect(first_fallback_request_stub).to have_been_requested
|
286
333
|
expect(second_fallback_request_stub).to have_been_requested
|
287
334
|
end
|
288
335
|
|
289
|
-
context "and the total request time exeeds #{
|
336
|
+
context "and the total request time exeeds #{http_defaults.fetch(:max_retry_duration)} seconds" do
|
290
337
|
let!(:default_host_request_stub) do
|
291
338
|
stub_request(:post, "https://#{api_key}@#{Ably::Rest::Client::DOMAIN}#{path}").to_return do
|
292
|
-
sleep
|
339
|
+
sleep max_retry_duration * 1.5
|
293
340
|
raise Faraday::TimeoutError.new('timeout error message')
|
294
341
|
end
|
295
342
|
end
|
@@ -310,7 +357,7 @@ describe Ably::Rest::Client do
|
|
310
357
|
end
|
311
358
|
end
|
312
359
|
|
313
|
-
it "tries fallback hosts #{
|
360
|
+
it "tries fallback hosts #{http_defaults.fetch(:max_retry_count)} times" do
|
314
361
|
expect { publish_block.call }.to raise_error Ably::Exceptions::ConnectionError, /ssl error message/
|
315
362
|
expect(default_host_request_stub).to have_been_requested
|
316
363
|
expect(first_fallback_request_stub).to have_been_requested
|
@@ -429,6 +476,60 @@ describe Ably::Rest::Client do
|
|
429
476
|
end
|
430
477
|
end
|
431
478
|
|
479
|
+
context 'HTTP configuration options' do
|
480
|
+
let(:client_options) { default_options.merge(key: api_key) }
|
481
|
+
|
482
|
+
context 'defaults' do
|
483
|
+
specify '#http_open_timeout is 4s' do
|
484
|
+
expect(client.http_defaults[:open_timeout]).to eql(4)
|
485
|
+
end
|
486
|
+
|
487
|
+
specify '#http_request_timeout is 15s' do
|
488
|
+
expect(client.http_defaults[:request_timeout]).to eql(15)
|
489
|
+
end
|
490
|
+
|
491
|
+
specify '#http_max_retry_count is 3' do
|
492
|
+
expect(client.http_defaults[:max_retry_count]).to eql(3)
|
493
|
+
end
|
494
|
+
|
495
|
+
specify '#http_max_retry_duration is 10s' do
|
496
|
+
expect(client.http_defaults[:max_retry_duration]).to eql(10)
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
context 'configured' do
|
501
|
+
let(:client_options) do
|
502
|
+
default_options.merge(
|
503
|
+
key: api_key,
|
504
|
+
http_open_timeout: 1,
|
505
|
+
http_request_timeout: 2,
|
506
|
+
http_max_retry_count: 33,
|
507
|
+
http_max_retry_duration: 4
|
508
|
+
)
|
509
|
+
end
|
510
|
+
|
511
|
+
specify '#http_open_timeout uses provided value' do
|
512
|
+
expect(client.http_defaults[:open_timeout]).to eql(1)
|
513
|
+
end
|
514
|
+
|
515
|
+
specify '#http_request_timeout uses provided value' do
|
516
|
+
expect(client.http_defaults[:request_timeout]).to eql(2)
|
517
|
+
end
|
518
|
+
|
519
|
+
specify '#http_max_retry_count uses provided value' do
|
520
|
+
expect(client.http_defaults[:max_retry_count]).to eql(33)
|
521
|
+
end
|
522
|
+
|
523
|
+
specify '#http_max_retry_duration uses provided value' do
|
524
|
+
expect(client.http_defaults[:max_retry_duration]).to eql(4)
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
it 'is frozen' do
|
529
|
+
expect(client.http_defaults).to be_frozen
|
530
|
+
end
|
531
|
+
end
|
532
|
+
|
432
533
|
context '#auth' do
|
433
534
|
let(:dummy_auth_url) { 'http://dummy.url' }
|
434
535
|
let(:unique_ttl) { 1234 }
|