ably 0.6.2 → 0.7.0
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/.rspec +1 -0
- data/.ruby-version.old +1 -0
- data/.travis.yml +0 -2
- data/Rakefile +22 -4
- data/SPEC.md +1676 -0
- data/ably.gemspec +1 -1
- data/lib/ably.rb +0 -8
- data/lib/ably/auth.rb +54 -46
- data/lib/ably/exceptions.rb +19 -5
- data/lib/ably/logger.rb +1 -1
- data/lib/ably/models/error_info.rb +1 -1
- data/lib/ably/models/idiomatic_ruby_wrapper.rb +11 -9
- data/lib/ably/models/message.rb +15 -12
- data/lib/ably/models/message_encoders/base.rb +6 -5
- data/lib/ably/models/message_encoders/base64.rb +1 -0
- data/lib/ably/models/message_encoders/cipher.rb +6 -3
- data/lib/ably/models/message_encoders/json.rb +1 -0
- data/lib/ably/models/message_encoders/utf8.rb +2 -9
- data/lib/ably/models/nil_logger.rb +20 -0
- data/lib/ably/models/paginated_resource.rb +5 -2
- data/lib/ably/models/presence_message.rb +21 -12
- data/lib/ably/models/protocol_message.rb +22 -6
- data/lib/ably/modules/ably.rb +11 -0
- data/lib/ably/modules/async_wrapper.rb +2 -0
- data/lib/ably/modules/conversions.rb +23 -3
- data/lib/ably/modules/encodeable.rb +2 -1
- data/lib/ably/modules/enum.rb +2 -0
- data/lib/ably/modules/event_emitter.rb +7 -1
- data/lib/ably/modules/event_machine_helpers.rb +2 -0
- data/lib/ably/modules/http_helpers.rb +2 -0
- data/lib/ably/modules/model_common.rb +12 -2
- data/lib/ably/modules/state_emitter.rb +76 -0
- data/lib/ably/modules/state_machine.rb +53 -0
- data/lib/ably/modules/statesman_monkey_patch.rb +33 -0
- data/lib/ably/modules/uses_state_machine.rb +74 -0
- data/lib/ably/realtime.rb +4 -2
- data/lib/ably/realtime/channel.rb +51 -58
- data/lib/ably/realtime/channel/channel_manager.rb +91 -0
- data/lib/ably/realtime/channel/channel_state_machine.rb +68 -0
- data/lib/ably/realtime/client.rb +70 -26
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +31 -13
- data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +1 -1
- data/lib/ably/realtime/connection.rb +135 -92
- data/lib/ably/realtime/connection/connection_manager.rb +216 -33
- data/lib/ably/realtime/connection/connection_state_machine.rb +30 -73
- data/lib/ably/realtime/models/nil_channel.rb +10 -1
- data/lib/ably/realtime/presence.rb +336 -92
- data/lib/ably/rest.rb +2 -2
- data/lib/ably/rest/channel.rb +13 -4
- data/lib/ably/rest/client.rb +138 -38
- data/lib/ably/rest/middleware/logger.rb +24 -3
- data/lib/ably/rest/presence.rb +12 -7
- data/lib/ably/version.rb +1 -1
- data/spec/acceptance/realtime/channel_history_spec.rb +101 -85
- data/spec/acceptance/realtime/channel_spec.rb +461 -120
- data/spec/acceptance/realtime/client_spec.rb +119 -0
- data/spec/acceptance/realtime/connection_failures_spec.rb +499 -0
- data/spec/acceptance/realtime/connection_spec.rb +571 -97
- data/spec/acceptance/realtime/message_spec.rb +347 -333
- data/spec/acceptance/realtime/presence_history_spec.rb +35 -40
- data/spec/acceptance/realtime/presence_spec.rb +769 -239
- data/spec/acceptance/realtime/stats_spec.rb +14 -22
- data/spec/acceptance/realtime/time_spec.rb +16 -20
- data/spec/acceptance/rest/auth_spec.rb +425 -364
- data/spec/acceptance/rest/base_spec.rb +108 -176
- data/spec/acceptance/rest/channel_spec.rb +89 -89
- data/spec/acceptance/rest/channels_spec.rb +30 -32
- data/spec/acceptance/rest/client_spec.rb +273 -0
- data/spec/acceptance/rest/encoders_spec.rb +185 -0
- data/spec/acceptance/rest/message_spec.rb +186 -163
- data/spec/acceptance/rest/presence_spec.rb +150 -111
- data/spec/acceptance/rest/stats_spec.rb +45 -40
- data/spec/acceptance/rest/time_spec.rb +8 -10
- data/spec/rspec_config.rb +10 -1
- data/spec/shared/client_initializer_behaviour.rb +212 -0
- data/spec/{support/model_helper.rb → shared/model_behaviour.rb} +6 -6
- data/spec/{support/protocol_msgbus_helper.rb → shared/protocol_msgbus_behaviour.rb} +1 -1
- data/spec/spec_helper.rb +9 -0
- data/spec/support/api_helper.rb +11 -0
- data/spec/support/event_machine_helper.rb +101 -3
- data/spec/support/markdown_spec_formatter.rb +90 -0
- data/spec/support/private_api_formatter.rb +36 -0
- data/spec/support/protocol_helper.rb +32 -0
- data/spec/support/random_helper.rb +15 -0
- data/spec/support/test_app.rb +4 -0
- data/spec/unit/auth_spec.rb +68 -0
- data/spec/unit/logger_spec.rb +77 -66
- data/spec/unit/models/error_info_spec.rb +1 -1
- data/spec/unit/models/idiomatic_ruby_wrapper_spec.rb +2 -3
- data/spec/unit/models/message_encoders/base64_spec.rb +2 -2
- data/spec/unit/models/message_encoders/cipher_spec.rb +2 -2
- data/spec/unit/models/message_encoders/utf8_spec.rb +2 -46
- data/spec/unit/models/message_spec.rb +160 -15
- data/spec/unit/models/paginated_resource_spec.rb +29 -27
- data/spec/unit/models/presence_message_spec.rb +163 -20
- data/spec/unit/models/protocol_message_spec.rb +43 -8
- data/spec/unit/modules/async_wrapper_spec.rb +2 -3
- data/spec/unit/modules/conversions_spec.rb +1 -1
- data/spec/unit/modules/enum_spec.rb +2 -3
- data/spec/unit/modules/event_emitter_spec.rb +62 -5
- data/spec/unit/modules/state_emitter_spec.rb +283 -0
- data/spec/unit/realtime/channel_spec.rb +107 -2
- data/spec/unit/realtime/channels_spec.rb +1 -0
- data/spec/unit/realtime/client_spec.rb +8 -48
- data/spec/unit/realtime/connection_spec.rb +3 -3
- data/spec/unit/realtime/incoming_message_dispatcher_spec.rb +2 -2
- data/spec/unit/realtime/presence_spec.rb +13 -4
- data/spec/unit/realtime/realtime_spec.rb +0 -11
- data/spec/unit/realtime/websocket_transport_spec.rb +2 -2
- data/spec/unit/rest/channel_spec.rb +109 -0
- data/spec/unit/rest/channels_spec.rb +4 -3
- data/spec/unit/rest/client_spec.rb +30 -125
- data/spec/unit/rest/rest_spec.rb +10 -0
- data/spec/unit/util/crypto_spec.rb +10 -5
- data/spec/unit/util/pub_sub_spec.rb +5 -5
- metadata +44 -12
- data/spec/integration/modules/state_emitter_spec.rb +0 -80
- data/spec/integration/rest/auth.rb +0 -9
@@ -1,43 +1,41 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require 'spec_helper'
|
2
|
-
require 'securerandom'
|
3
3
|
|
4
4
|
describe Ably::Rest::Channels do
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
let(:channel_name) { SecureRandom.hex }
|
11
|
-
let(:options) { { key: 'value' } }
|
5
|
+
shared_examples 'a channel' do
|
6
|
+
it 'returns a channel object' do
|
7
|
+
expect(channel).to be_a Ably::Rest::Channel
|
8
|
+
expect(channel.name).to eql(channel_name)
|
9
|
+
end
|
12
10
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
end
|
11
|
+
it 'returns channel object and passes the provided options' do
|
12
|
+
expect(channel_with_options.options).to eql(options)
|
13
|
+
end
|
14
|
+
end
|
18
15
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
16
|
+
vary_by_protocol do
|
17
|
+
let(:client) do
|
18
|
+
Ably::Rest::Client.new(api_key: api_key, environment: environment, protocol: protocol)
|
19
|
+
end
|
20
|
+
let(:channel_name) { random_str }
|
21
|
+
let(:options) { { key: 'value' } }
|
23
22
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
23
|
+
describe 'using shortcut method #channel on the client object' do
|
24
|
+
let(:channel) { client.channel(channel_name) }
|
25
|
+
let(:channel_with_options) { client.channel(channel_name, options) }
|
26
|
+
it_behaves_like 'a channel'
|
27
|
+
end
|
29
28
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
29
|
+
describe 'using #get method on client#channels' do
|
30
|
+
let(:channel) { client.channels.get(channel_name) }
|
31
|
+
let(:channel_with_options) { client.channels.get(channel_name, options) }
|
32
|
+
it_behaves_like 'a channel'
|
33
|
+
end
|
35
34
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
end
|
35
|
+
describe 'using undocumented array accessor [] method on client#channels' do
|
36
|
+
let(:channel) { client.channels[channel_name] }
|
37
|
+
let(:channel_with_options) { client.channels[channel_name, options] }
|
38
|
+
it_behaves_like 'a channel'
|
41
39
|
end
|
42
40
|
end
|
43
41
|
end
|
@@ -0,0 +1,273 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Ably::Rest::Client do
|
5
|
+
vary_by_protocol do
|
6
|
+
let(:default_options) { { environment: environment, protocol: protocol } }
|
7
|
+
let(:client_options) { default_options }
|
8
|
+
|
9
|
+
let(:client) { Ably::Rest::Client.new(client_options) }
|
10
|
+
|
11
|
+
connection_retry = Ably::Rest::Client::CONNECTION_RETRY
|
12
|
+
|
13
|
+
context '#initialize' do
|
14
|
+
let(:client_id) { random_str }
|
15
|
+
let(:token_request) { client.auth.create_token_request(key_id: key_id, key_secret: key_secret, client_id: client_id) }
|
16
|
+
|
17
|
+
context 'with an auth block' do
|
18
|
+
let(:client) { Ably::Rest::Client.new(client_options) { token_request } }
|
19
|
+
|
20
|
+
it 'calls the block to get a new token' do
|
21
|
+
expect { client.channel('channel_name').publish('event', 'message') }.to change { client.auth.current_token }
|
22
|
+
expect(client.auth.current_token.client_id).to eql(client_id)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'with an auth URL' do
|
27
|
+
let(:client_options) { default_options.merge(auth_url: token_request_url, auth_method: :get) }
|
28
|
+
let(:token_request_url) { 'http://get.token.request.com/' }
|
29
|
+
|
30
|
+
before do
|
31
|
+
allow(client.auth).to receive(:token_request_from_auth_url).with(token_request_url, :auth_method => :get).and_return(token_request)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'sends an HTTP request to the provided URL to get a new token' do
|
35
|
+
expect { client.channel('channel_name').publish('event', 'message') }.to change { client.auth.current_token }
|
36
|
+
expect(client.auth.current_token.client_id).to eql(client_id)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'using tokens' do
|
42
|
+
let(:client) do
|
43
|
+
Ably::Rest::Client.new(client_options) do
|
44
|
+
@request_index ||= 0
|
45
|
+
@request_index += 1
|
46
|
+
send("token_request_#{@request_index}")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
let(:token_request_1) { client.auth.create_token_request(token_request_options.merge(client_id: random_str)) }
|
50
|
+
let(:token_request_2) { client.auth.create_token_request(token_request_options.merge(client_id: random_str)) }
|
51
|
+
|
52
|
+
context 'when expired' do
|
53
|
+
let(:token_request_options) { { key_id: key_id, key_secret: key_secret, ttl: Ably::Models::Token::TOKEN_EXPIRY_BUFFER } }
|
54
|
+
|
55
|
+
it 'creates a new token automatically when the old token expires' do
|
56
|
+
expect { client.channel('channel_name').publish('event', 'message') }.to change { client.auth.current_token }
|
57
|
+
expect(client.auth.current_token.client_id).to eql(token_request_1[:client_id])
|
58
|
+
|
59
|
+
sleep 1
|
60
|
+
|
61
|
+
expect { client.channel('channel_name').publish('event', 'message') }.to change { client.auth.current_token }
|
62
|
+
expect(client.auth.current_token.client_id).to eql(token_request_2[:client_id])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'when token has not expired' do
|
67
|
+
let(:token_request_options) { { key_id: key_id, key_secret: key_secret, ttl: 3600 } }
|
68
|
+
|
69
|
+
it 'reuses the existing token for every request' do
|
70
|
+
expect { client.channel('channel_name').publish('event', 'message') }.to change { client.auth.current_token }
|
71
|
+
expect(client.auth.current_token.client_id).to eql(token_request_1[:client_id])
|
72
|
+
|
73
|
+
sleep 1
|
74
|
+
|
75
|
+
expect { client.channel('channel_name').publish('event', 'message') }.to_not change { client.auth.current_token }
|
76
|
+
expect(client.auth.current_token.client_id).to eql(token_request_1[:client_id])
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'connection transport' do
|
82
|
+
let(:client_options) { default_options.merge(api_key: api_key) }
|
83
|
+
|
84
|
+
context 'for default host' do
|
85
|
+
it "is configured to timeout connection opening in #{connection_retry.fetch(:single_request_open_timeout)} seconds" do
|
86
|
+
expect(client.connection.options.open_timeout).to eql(connection_retry.fetch(:single_request_open_timeout))
|
87
|
+
end
|
88
|
+
|
89
|
+
it "is configured to timeout connection requests in #{connection_retry.fetch(:single_request_timeout)} seconds" do
|
90
|
+
expect(client.connection.options.timeout).to eql(connection_retry.fetch(:single_request_timeout))
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'for the fallback hosts' do
|
95
|
+
it "is configured to timeout connection opening in #{connection_retry.fetch(:single_request_open_timeout)} seconds" do
|
96
|
+
expect(client.fallback_connection.options.open_timeout).to eql(connection_retry.fetch(:single_request_open_timeout))
|
97
|
+
end
|
98
|
+
|
99
|
+
it "is configured to timeout connection requests in #{connection_retry.fetch(:single_request_timeout)} seconds" do
|
100
|
+
expect(client.fallback_connection.options.timeout).to eql(connection_retry.fetch(:single_request_timeout))
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'fallback hosts', :webmock do
|
106
|
+
let(:path) { '/channels/test/publish' }
|
107
|
+
let(:publish_block) { proc { client.channel('test').publish('event', 'data') } }
|
108
|
+
|
109
|
+
context 'configured' do
|
110
|
+
let(:client_options) { default_options.merge(api_key: api_key) }
|
111
|
+
|
112
|
+
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
|
113
|
+
hosts = []
|
114
|
+
5.times do
|
115
|
+
hosts << client.fallback_connection.host
|
116
|
+
end
|
117
|
+
expect(hosts).to match_array(%w(A.ably-realtime.com B.ably-realtime.com C.ably-realtime.com D.ably-realtime.com E.ably-realtime.com))
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'when environment is NOT production' do
|
122
|
+
let(:client_options) { default_options.merge(environment: 'sandbox', api_key: api_key) }
|
123
|
+
let!(:default_host_request_stub) do
|
124
|
+
stub_request(:post, "https://#{api_key}@#{environment}-#{Ably::Rest::Client::DOMAIN}#{path}").to_return do
|
125
|
+
raise Faraday::TimeoutError.new('timeout error message')
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'does not retry failed requests with fallback hosts when there is a connection error' do
|
130
|
+
expect { publish_block.call }.to raise_error Ably::Exceptions::ConnectionTimeoutError
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
context 'when environment is production' do
|
135
|
+
let(:custom_hosts) { %w(A.ably-realtime.com B.ably-realtime.com) }
|
136
|
+
let(:max_attempts) { 2 }
|
137
|
+
let(:cumulative_timeout) { 0.5 }
|
138
|
+
let(:client_options) { default_options.merge(environment: nil, api_key: api_key) }
|
139
|
+
|
140
|
+
before do
|
141
|
+
stub_const 'Ably::FALLBACK_HOSTS', custom_hosts
|
142
|
+
stub_const 'Ably::Rest::Client::CONNECTION_RETRY', {
|
143
|
+
single_request_open_timeout: 4,
|
144
|
+
single_request_timeout: 15,
|
145
|
+
cumulative_request_open_timeout: cumulative_timeout,
|
146
|
+
max_retry_attempts: max_attempts
|
147
|
+
}
|
148
|
+
end
|
149
|
+
|
150
|
+
let!(:first_fallback_request_stub) do
|
151
|
+
stub_request(:post, "https://#{api_key}@#{custom_hosts[0]}#{path}").to_return do
|
152
|
+
raise Faraday::SSLError.new('ssl error message')
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
let!(:second_fallback_request_stub) do
|
157
|
+
stub_request(:post, "https://#{api_key}@#{custom_hosts[1]}#{path}").to_return do
|
158
|
+
raise Faraday::SSLError.new('ssl error message')
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context 'and connection times out' do
|
163
|
+
let!(:default_host_request_stub) do
|
164
|
+
stub_request(:post, "https://#{api_key}@#{Ably::Rest::Client::DOMAIN}#{path}").to_return do
|
165
|
+
raise Faraday::TimeoutError.new('timeout error message')
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
it "tries fallback hosts #{connection_retry[:max_retry_attempts]} times" do
|
170
|
+
expect { publish_block.call }.to raise_error Ably::Exceptions::ConnectionError, /ssl error message/
|
171
|
+
expect(default_host_request_stub).to have_been_requested
|
172
|
+
expect(first_fallback_request_stub).to have_been_requested
|
173
|
+
expect(second_fallback_request_stub).to have_been_requested
|
174
|
+
end
|
175
|
+
|
176
|
+
context "and the total request time exeeds #{connection_retry[:cumulative_request_open_timeout]} seconds" do
|
177
|
+
let!(:default_host_request_stub) do
|
178
|
+
stub_request(:post, "https://#{api_key}@#{Ably::Rest::Client::DOMAIN}#{path}").to_return do
|
179
|
+
sleep cumulative_timeout * 1.5
|
180
|
+
raise Faraday::TimeoutError.new('timeout error message')
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'makes no further attempts to any fallback hosts' do
|
185
|
+
expect { publish_block.call }.to raise_error Ably::Exceptions::ConnectionTimeoutError
|
186
|
+
expect(default_host_request_stub).to have_been_requested
|
187
|
+
expect(first_fallback_request_stub).to_not have_been_requested
|
188
|
+
expect(second_fallback_request_stub).to_not have_been_requested
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
context 'and connection fails' do
|
194
|
+
let!(:default_host_request_stub) do
|
195
|
+
stub_request(:post, "https://#{api_key}@#{Ably::Rest::Client::DOMAIN}#{path}").to_return do
|
196
|
+
raise Faraday::ConnectionFailed.new('connection failure error message')
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
it "tries fallback hosts #{connection_retry[:max_retry_attempts]} times" do
|
201
|
+
expect { publish_block.call }.to raise_error Ably::Exceptions::ConnectionError, /ssl error message/
|
202
|
+
expect(default_host_request_stub).to have_been_requested
|
203
|
+
expect(first_fallback_request_stub).to have_been_requested
|
204
|
+
expect(second_fallback_request_stub).to have_been_requested
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
context 'with a custom host' do
|
211
|
+
let(:custom_host) { 'host.does.not.exist' }
|
212
|
+
let(:client_options) { default_options.merge(api_key: api_key, rest_host: custom_host) }
|
213
|
+
let(:capability) { { :foo => ["publish"] } }
|
214
|
+
|
215
|
+
context 'that does not exist' do
|
216
|
+
it 'fails immediately and raises a Faraday Error' do
|
217
|
+
expect { client.channel('test').publish('event', 'data') }.to raise_error Ably::Exceptions::ConnectionError
|
218
|
+
end
|
219
|
+
|
220
|
+
context 'fallback hosts', :webmock do
|
221
|
+
let(:path) { '/channels/test/publish' }
|
222
|
+
|
223
|
+
let!(:custom_host_request_stub) do
|
224
|
+
stub_request(:post, "https://#{api_key}@#{custom_host}#{path}").to_return do
|
225
|
+
raise Faraday::ConnectionFailed.new('connection failure error message')
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
before do
|
230
|
+
Ably::FALLBACK_HOSTS.each do |host|
|
231
|
+
stub_request(:post, "https://#{host}#{path}").to_return do
|
232
|
+
raise 'Fallbacks should not be used with custom hosts'
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
specify 'are never used' do
|
238
|
+
expect { client.channel('test').publish('event', 'data') }.to raise_error Ably::Exceptions::ConnectionError
|
239
|
+
expect(custom_host_request_stub).to have_been_requested
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
context 'that times out', :webmock do
|
245
|
+
let(:path) { '/keys/app_id.key_id/requestToken' }
|
246
|
+
let!(:custom_host_request_stub) do
|
247
|
+
stub_request(:post, "https://#{custom_host}#{path}").to_return do
|
248
|
+
raise Faraday::TimeoutError.new('timeout error message')
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'fails immediately and raises a Faraday Error' do
|
253
|
+
expect { client.auth.request_token }.to raise_error Ably::Exceptions::ConnectionTimeoutError
|
254
|
+
end
|
255
|
+
|
256
|
+
context 'fallback hosts' do
|
257
|
+
before do
|
258
|
+
Ably::FALLBACK_HOSTS.each do |host|
|
259
|
+
stub_request(:post, "https://#{host}#{path}").to_return do
|
260
|
+
raise 'Fallbacks should not be used with custom hosts'
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
specify 'are never used' do
|
266
|
+
expect { client.auth.request_token }.to raise_error Ably::Exceptions::ConnectionTimeoutError
|
267
|
+
expect(custom_host_request_stub).to have_been_requested
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'base64'
|
4
|
+
|
5
|
+
describe Ably::Models::MessageEncoders do
|
6
|
+
let(:default_client_options) { { api_key: api_key, environment: environment } }
|
7
|
+
let(:client) { Ably::Rest::Client.new(default_client_options.merge(protocol: protocol)) }
|
8
|
+
let(:channel_options) { {} }
|
9
|
+
let(:channel) { client.channel('test', channel_options) }
|
10
|
+
let(:response) { instance_double('Faraday::Response', status: 201) }
|
11
|
+
|
12
|
+
let(:cipher_params) { { key: random_str, algorithm: 'aes', mode: 'cbc', key_length: 128 } }
|
13
|
+
let(:crypto) { Ably::Util::Crypto.new(cipher_params) }
|
14
|
+
|
15
|
+
let(:utf_8_data) { random_str.encode(Encoding::UTF_8) }
|
16
|
+
let(:binary_data) { MessagePack.pack(random_str).encode(Encoding::ASCII_8BIT) }
|
17
|
+
let(:json_data) { { 'key' => random_str } }
|
18
|
+
|
19
|
+
after do
|
20
|
+
channel.publish 'event', published_data
|
21
|
+
end
|
22
|
+
|
23
|
+
def on_publish
|
24
|
+
expect(client).to receive(:post) do |url, message|
|
25
|
+
yield(message['encoding'], message['data'])
|
26
|
+
end.and_return(response)
|
27
|
+
end
|
28
|
+
|
29
|
+
def decrypted(payload, options = {})
|
30
|
+
payload = Base64.decode64(payload) if options[:base64]
|
31
|
+
crypto.decrypt(payload)
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'with binary transport protocol' do
|
35
|
+
let(:protocol) { :msgpack }
|
36
|
+
|
37
|
+
context 'without encryption' do
|
38
|
+
context 'with UTF-8 data' do
|
39
|
+
let(:published_data) { utf_8_data }
|
40
|
+
|
41
|
+
it 'does not apply any encoding' do
|
42
|
+
on_publish do |encoding, encoded_data|
|
43
|
+
expect(encoding).to be_nil
|
44
|
+
expect(encoded_data).to eql(published_data)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'with binary data' do
|
50
|
+
let(:published_data) { binary_data }
|
51
|
+
|
52
|
+
it 'does not apply any encoding' do
|
53
|
+
on_publish do |encoding, encoded_data|
|
54
|
+
expect(encoding).to be_nil
|
55
|
+
expect(encoded_data).to eql(published_data)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'with JSON data' do
|
61
|
+
let(:published_data) { json_data }
|
62
|
+
|
63
|
+
it 'stringifies the JSON and sets the json encoding type' do
|
64
|
+
on_publish do |encoding, encoded_data|
|
65
|
+
expect(encoding).to eql('json')
|
66
|
+
expect(encoded_data).to eql(JSON.dump(published_data))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'with encryption' do
|
73
|
+
let(:channel_options) { { encrypted: true, cipher_params: cipher_params } }
|
74
|
+
|
75
|
+
context 'with UTF-8 data' do
|
76
|
+
let(:published_data) { utf_8_data }
|
77
|
+
|
78
|
+
it 'applies utf-8 and cipher encoding' do
|
79
|
+
on_publish do |encoding, encoded_data|
|
80
|
+
expect(encoding).to eql('utf-8/cipher+aes-128-cbc')
|
81
|
+
expect(decrypted(encoded_data)).to eql(published_data)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'with binary data' do
|
87
|
+
let(:published_data) { binary_data }
|
88
|
+
|
89
|
+
it 'applies cipher encoding' do
|
90
|
+
on_publish do |encoding, encoded_data|
|
91
|
+
expect(encoding).to eql('cipher+aes-128-cbc')
|
92
|
+
expect(decrypted(encoded_data)).to eql(published_data)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'with JSON data' do
|
98
|
+
let(:published_data) { json_data }
|
99
|
+
|
100
|
+
it 'applies json, utf-8 and cipher encoding' do
|
101
|
+
on_publish do |encoding, encoded_data|
|
102
|
+
expect(encoding).to eql('json/utf-8/cipher+aes-128-cbc')
|
103
|
+
expect(decrypted(encoded_data)).to eql(JSON.dump(published_data))
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context 'with text transport protocol' do
|
111
|
+
let(:protocol) { :json }
|
112
|
+
|
113
|
+
context 'without encryption' do
|
114
|
+
context 'with UTF-8 data' do
|
115
|
+
let(:published_data) { utf_8_data }
|
116
|
+
|
117
|
+
it 'does not apply any encoding' do
|
118
|
+
on_publish do |encoding, encoded_data|
|
119
|
+
expect(encoding).to be_nil
|
120
|
+
expect(encoded_data).to eql(published_data)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context 'with binary data' do
|
126
|
+
let(:published_data) { binary_data }
|
127
|
+
|
128
|
+
it 'applies a base64 encoding' do
|
129
|
+
on_publish do |encoding, encoded_data|
|
130
|
+
expect(encoding).to eql('base64')
|
131
|
+
expect(Base64.decode64(encoded_data)).to eql(published_data)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context 'with JSON data' do
|
137
|
+
let(:published_data) { json_data }
|
138
|
+
|
139
|
+
it 'stringifies the JSON and sets the json encoding type' do
|
140
|
+
on_publish do |encoding, encoded_data|
|
141
|
+
expect(encoding).to eql('json')
|
142
|
+
expect(encoded_data).to eql(JSON.dump(published_data))
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context 'with encryption' do
|
149
|
+
let(:channel_options) { { encrypted: true, cipher_params: cipher_params } }
|
150
|
+
|
151
|
+
context 'with UTF-8 data' do
|
152
|
+
let(:published_data) { utf_8_data }
|
153
|
+
|
154
|
+
it 'applies utf-8, cipher and base64 encodings' do
|
155
|
+
on_publish do |encoding, encoded_data|
|
156
|
+
expect(encoding).to eql('utf-8/cipher+aes-128-cbc/base64')
|
157
|
+
expect(decrypted(encoded_data, base64: true)).to eql(published_data)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context 'with binary data' do
|
163
|
+
let(:published_data) { binary_data }
|
164
|
+
|
165
|
+
it 'applies cipher and base64 encoding' do
|
166
|
+
on_publish do |encoding, encoded_data|
|
167
|
+
expect(encoding).to eql('cipher+aes-128-cbc/base64')
|
168
|
+
expect(decrypted(encoded_data, base64: true)).to eql(published_data)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
context 'with JSON data' do
|
174
|
+
let(:published_data) { json_data }
|
175
|
+
|
176
|
+
it 'applies json, utf-8, cipher and base64 encoding' do
|
177
|
+
on_publish do |encoding, encoded_data|
|
178
|
+
expect(encoding).to eql('json/utf-8/cipher+aes-128-cbc/base64')
|
179
|
+
expect(decrypted(encoded_data, base64: true)).to eql(JSON.dump(published_data))
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|