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.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -0
  3. data/.ruby-version.old +1 -0
  4. data/.travis.yml +0 -2
  5. data/Rakefile +22 -4
  6. data/SPEC.md +1676 -0
  7. data/ably.gemspec +1 -1
  8. data/lib/ably.rb +0 -8
  9. data/lib/ably/auth.rb +54 -46
  10. data/lib/ably/exceptions.rb +19 -5
  11. data/lib/ably/logger.rb +1 -1
  12. data/lib/ably/models/error_info.rb +1 -1
  13. data/lib/ably/models/idiomatic_ruby_wrapper.rb +11 -9
  14. data/lib/ably/models/message.rb +15 -12
  15. data/lib/ably/models/message_encoders/base.rb +6 -5
  16. data/lib/ably/models/message_encoders/base64.rb +1 -0
  17. data/lib/ably/models/message_encoders/cipher.rb +6 -3
  18. data/lib/ably/models/message_encoders/json.rb +1 -0
  19. data/lib/ably/models/message_encoders/utf8.rb +2 -9
  20. data/lib/ably/models/nil_logger.rb +20 -0
  21. data/lib/ably/models/paginated_resource.rb +5 -2
  22. data/lib/ably/models/presence_message.rb +21 -12
  23. data/lib/ably/models/protocol_message.rb +22 -6
  24. data/lib/ably/modules/ably.rb +11 -0
  25. data/lib/ably/modules/async_wrapper.rb +2 -0
  26. data/lib/ably/modules/conversions.rb +23 -3
  27. data/lib/ably/modules/encodeable.rb +2 -1
  28. data/lib/ably/modules/enum.rb +2 -0
  29. data/lib/ably/modules/event_emitter.rb +7 -1
  30. data/lib/ably/modules/event_machine_helpers.rb +2 -0
  31. data/lib/ably/modules/http_helpers.rb +2 -0
  32. data/lib/ably/modules/model_common.rb +12 -2
  33. data/lib/ably/modules/state_emitter.rb +76 -0
  34. data/lib/ably/modules/state_machine.rb +53 -0
  35. data/lib/ably/modules/statesman_monkey_patch.rb +33 -0
  36. data/lib/ably/modules/uses_state_machine.rb +74 -0
  37. data/lib/ably/realtime.rb +4 -2
  38. data/lib/ably/realtime/channel.rb +51 -58
  39. data/lib/ably/realtime/channel/channel_manager.rb +91 -0
  40. data/lib/ably/realtime/channel/channel_state_machine.rb +68 -0
  41. data/lib/ably/realtime/client.rb +70 -26
  42. data/lib/ably/realtime/client/incoming_message_dispatcher.rb +31 -13
  43. data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +1 -1
  44. data/lib/ably/realtime/connection.rb +135 -92
  45. data/lib/ably/realtime/connection/connection_manager.rb +216 -33
  46. data/lib/ably/realtime/connection/connection_state_machine.rb +30 -73
  47. data/lib/ably/realtime/models/nil_channel.rb +10 -1
  48. data/lib/ably/realtime/presence.rb +336 -92
  49. data/lib/ably/rest.rb +2 -2
  50. data/lib/ably/rest/channel.rb +13 -4
  51. data/lib/ably/rest/client.rb +138 -38
  52. data/lib/ably/rest/middleware/logger.rb +24 -3
  53. data/lib/ably/rest/presence.rb +12 -7
  54. data/lib/ably/version.rb +1 -1
  55. data/spec/acceptance/realtime/channel_history_spec.rb +101 -85
  56. data/spec/acceptance/realtime/channel_spec.rb +461 -120
  57. data/spec/acceptance/realtime/client_spec.rb +119 -0
  58. data/spec/acceptance/realtime/connection_failures_spec.rb +499 -0
  59. data/spec/acceptance/realtime/connection_spec.rb +571 -97
  60. data/spec/acceptance/realtime/message_spec.rb +347 -333
  61. data/spec/acceptance/realtime/presence_history_spec.rb +35 -40
  62. data/spec/acceptance/realtime/presence_spec.rb +769 -239
  63. data/spec/acceptance/realtime/stats_spec.rb +14 -22
  64. data/spec/acceptance/realtime/time_spec.rb +16 -20
  65. data/spec/acceptance/rest/auth_spec.rb +425 -364
  66. data/spec/acceptance/rest/base_spec.rb +108 -176
  67. data/spec/acceptance/rest/channel_spec.rb +89 -89
  68. data/spec/acceptance/rest/channels_spec.rb +30 -32
  69. data/spec/acceptance/rest/client_spec.rb +273 -0
  70. data/spec/acceptance/rest/encoders_spec.rb +185 -0
  71. data/spec/acceptance/rest/message_spec.rb +186 -163
  72. data/spec/acceptance/rest/presence_spec.rb +150 -111
  73. data/spec/acceptance/rest/stats_spec.rb +45 -40
  74. data/spec/acceptance/rest/time_spec.rb +8 -10
  75. data/spec/rspec_config.rb +10 -1
  76. data/spec/shared/client_initializer_behaviour.rb +212 -0
  77. data/spec/{support/model_helper.rb → shared/model_behaviour.rb} +6 -6
  78. data/spec/{support/protocol_msgbus_helper.rb → shared/protocol_msgbus_behaviour.rb} +1 -1
  79. data/spec/spec_helper.rb +9 -0
  80. data/spec/support/api_helper.rb +11 -0
  81. data/spec/support/event_machine_helper.rb +101 -3
  82. data/spec/support/markdown_spec_formatter.rb +90 -0
  83. data/spec/support/private_api_formatter.rb +36 -0
  84. data/spec/support/protocol_helper.rb +32 -0
  85. data/spec/support/random_helper.rb +15 -0
  86. data/spec/support/test_app.rb +4 -0
  87. data/spec/unit/auth_spec.rb +68 -0
  88. data/spec/unit/logger_spec.rb +77 -66
  89. data/spec/unit/models/error_info_spec.rb +1 -1
  90. data/spec/unit/models/idiomatic_ruby_wrapper_spec.rb +2 -3
  91. data/spec/unit/models/message_encoders/base64_spec.rb +2 -2
  92. data/spec/unit/models/message_encoders/cipher_spec.rb +2 -2
  93. data/spec/unit/models/message_encoders/utf8_spec.rb +2 -46
  94. data/spec/unit/models/message_spec.rb +160 -15
  95. data/spec/unit/models/paginated_resource_spec.rb +29 -27
  96. data/spec/unit/models/presence_message_spec.rb +163 -20
  97. data/spec/unit/models/protocol_message_spec.rb +43 -8
  98. data/spec/unit/modules/async_wrapper_spec.rb +2 -3
  99. data/spec/unit/modules/conversions_spec.rb +1 -1
  100. data/spec/unit/modules/enum_spec.rb +2 -3
  101. data/spec/unit/modules/event_emitter_spec.rb +62 -5
  102. data/spec/unit/modules/state_emitter_spec.rb +283 -0
  103. data/spec/unit/realtime/channel_spec.rb +107 -2
  104. data/spec/unit/realtime/channels_spec.rb +1 -0
  105. data/spec/unit/realtime/client_spec.rb +8 -48
  106. data/spec/unit/realtime/connection_spec.rb +3 -3
  107. data/spec/unit/realtime/incoming_message_dispatcher_spec.rb +2 -2
  108. data/spec/unit/realtime/presence_spec.rb +13 -4
  109. data/spec/unit/realtime/realtime_spec.rb +0 -11
  110. data/spec/unit/realtime/websocket_transport_spec.rb +2 -2
  111. data/spec/unit/rest/channel_spec.rb +109 -0
  112. data/spec/unit/rest/channels_spec.rb +4 -3
  113. data/spec/unit/rest/client_spec.rb +30 -125
  114. data/spec/unit/rest/rest_spec.rb +10 -0
  115. data/spec/unit/util/crypto_spec.rb +10 -5
  116. data/spec/unit/util/pub_sub_spec.rb +5 -5
  117. metadata +44 -12
  118. data/spec/integration/modules/state_emitter_spec.rb +0 -80
  119. 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
- [:msgpack, :json].each do |protocol|
6
- context "over #{protocol}" do
7
- let(:client) do
8
- Ably::Rest::Client.new(api_key: api_key, environment: environment, protocol: protocol)
9
- end
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
- shared_examples 'a channel' do
14
- it 'should access a channel' do
15
- expect(channel).to be_a Ably::Rest::Channel
16
- expect(channel.name).to eql(channel_name)
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
- it 'should allow options to be set on a channel' do
20
- expect(channel_with_options.options).to eql(options)
21
- end
22
- end
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
- describe 'using shortcut method on client' do
25
- let(:channel) { client.channel(channel_name) }
26
- let(:channel_with_options) { client.channel(channel_name, options) }
27
- it_behaves_like 'a channel'
28
- end
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
- describe 'using documented .get method on client.channels' do
31
- let(:channel) { client.channels.get(channel_name) }
32
- let(:channel_with_options) { client.channels.get(channel_name, options) }
33
- it_behaves_like 'a channel'
34
- end
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
- describe 'using undocumented [] method on client.channels' do
37
- let(:channel) { client.channels[channel_name] }
38
- let(:channel_with_options) { client.channels[channel_name, options] }
39
- it_behaves_like 'a channel'
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