ably 0.1.6 → 0.2.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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.travis.yml +9 -0
  4. data/LICENSE.txt +1 -1
  5. data/README.md +8 -1
  6. data/Rakefile +10 -0
  7. data/ably.gemspec +18 -18
  8. data/lib/ably.rb +6 -5
  9. data/lib/ably/auth.rb +11 -14
  10. data/lib/ably/exceptions.rb +18 -15
  11. data/lib/ably/logger.rb +102 -0
  12. data/lib/ably/models/error_info.rb +1 -1
  13. data/lib/ably/models/message.rb +19 -5
  14. data/lib/ably/models/message_encoders/base.rb +107 -0
  15. data/lib/ably/models/message_encoders/base64.rb +39 -0
  16. data/lib/ably/models/message_encoders/cipher.rb +80 -0
  17. data/lib/ably/models/message_encoders/json.rb +33 -0
  18. data/lib/ably/models/message_encoders/utf8.rb +33 -0
  19. data/lib/ably/models/paginated_resource.rb +23 -6
  20. data/lib/ably/models/presence_message.rb +19 -7
  21. data/lib/ably/models/protocol_message.rb +5 -4
  22. data/lib/ably/models/token.rb +2 -2
  23. data/lib/ably/modules/channels_collection.rb +0 -3
  24. data/lib/ably/modules/conversions.rb +3 -3
  25. data/lib/ably/modules/encodeable.rb +68 -0
  26. data/lib/ably/modules/event_emitter.rb +10 -4
  27. data/lib/ably/modules/event_machine_helpers.rb +6 -4
  28. data/lib/ably/modules/http_helpers.rb +7 -2
  29. data/lib/ably/modules/model_common.rb +2 -0
  30. data/lib/ably/modules/state_emitter.rb +10 -1
  31. data/lib/ably/realtime.rb +19 -12
  32. data/lib/ably/realtime/channel.rb +26 -13
  33. data/lib/ably/realtime/client.rb +31 -7
  34. data/lib/ably/realtime/client/incoming_message_dispatcher.rb +14 -3
  35. data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +13 -4
  36. data/lib/ably/realtime/connection.rb +152 -46
  37. data/lib/ably/realtime/connection/connection_manager.rb +168 -0
  38. data/lib/ably/realtime/connection/connection_state_machine.rb +56 -33
  39. data/lib/ably/realtime/connection/websocket_transport.rb +56 -29
  40. data/lib/ably/{models → realtime/models}/nil_channel.rb +1 -1
  41. data/lib/ably/realtime/presence.rb +38 -13
  42. data/lib/ably/rest.rb +7 -5
  43. data/lib/ably/rest/channel.rb +24 -3
  44. data/lib/ably/rest/client.rb +56 -17
  45. data/lib/ably/rest/middleware/encoder.rb +49 -0
  46. data/lib/ably/rest/middleware/exceptions.rb +3 -2
  47. data/lib/ably/rest/middleware/logger.rb +37 -0
  48. data/lib/ably/rest/presence.rb +10 -2
  49. data/lib/ably/util/crypto.rb +57 -29
  50. data/lib/ably/util/pub_sub.rb +11 -0
  51. data/lib/ably/version.rb +1 -1
  52. data/spec/acceptance/realtime/channel_spec.rb +65 -7
  53. data/spec/acceptance/realtime/connection_spec.rb +123 -27
  54. data/spec/acceptance/realtime/message_spec.rb +319 -34
  55. data/spec/acceptance/realtime/presence_history_spec.rb +58 -0
  56. data/spec/acceptance/realtime/presence_spec.rb +160 -18
  57. data/spec/acceptance/rest/auth_spec.rb +93 -49
  58. data/spec/acceptance/rest/base_spec.rb +10 -10
  59. data/spec/acceptance/rest/channel_spec.rb +35 -19
  60. data/spec/acceptance/rest/channels_spec.rb +8 -8
  61. data/spec/acceptance/rest/message_spec.rb +224 -0
  62. data/spec/acceptance/rest/presence_spec.rb +159 -23
  63. data/spec/acceptance/rest/stats_spec.rb +5 -5
  64. data/spec/acceptance/rest/time_spec.rb +4 -4
  65. data/spec/integration/rest/auth.rb +1 -1
  66. data/spec/resources/crypto-data-128.json +56 -0
  67. data/spec/resources/crypto-data-256.json +56 -0
  68. data/spec/rspec_config.rb +39 -0
  69. data/spec/spec_helper.rb +4 -42
  70. data/spec/support/api_helper.rb +1 -1
  71. data/spec/support/event_machine_helper.rb +0 -5
  72. data/spec/support/protocol_msgbus_helper.rb +3 -3
  73. data/spec/support/test_app.rb +3 -3
  74. data/spec/unit/logger_spec.rb +135 -0
  75. data/spec/unit/models/message_encoders/base64_spec.rb +181 -0
  76. data/spec/unit/models/message_encoders/cipher_spec.rb +260 -0
  77. data/spec/unit/models/message_encoders/json_spec.rb +135 -0
  78. data/spec/unit/models/message_encoders/utf8_spec.rb +100 -0
  79. data/spec/unit/models/message_spec.rb +16 -1
  80. data/spec/unit/models/paginated_resource_spec.rb +46 -0
  81. data/spec/unit/models/presence_message_spec.rb +18 -5
  82. data/spec/unit/models/token_spec.rb +1 -1
  83. data/spec/unit/modules/event_emitter_spec.rb +24 -10
  84. data/spec/unit/realtime/channel_spec.rb +3 -3
  85. data/spec/unit/realtime/channels_spec.rb +1 -1
  86. data/spec/unit/realtime/client_spec.rb +44 -2
  87. data/spec/unit/realtime/connection_spec.rb +2 -2
  88. data/spec/unit/realtime/incoming_message_dispatcher_spec.rb +4 -4
  89. data/spec/unit/realtime/presence_spec.rb +1 -1
  90. data/spec/unit/realtime/realtime_spec.rb +3 -3
  91. data/spec/unit/realtime/websocket_transport_spec.rb +24 -0
  92. data/spec/unit/rest/channels_spec.rb +1 -1
  93. data/spec/unit/rest/client_spec.rb +45 -10
  94. data/spec/unit/util/crypto_spec.rb +82 -0
  95. data/spec/unit/{modules → util}/pub_sub_spec.rb +13 -1
  96. metadata +43 -12
  97. data/spec/acceptance/crypto.rb +0 -63
@@ -0,0 +1,181 @@
1
+ require 'spec_helper'
2
+ require 'base64'
3
+ require 'msgpack'
4
+
5
+ require 'ably/models/message_encoders/base64'
6
+
7
+ describe Ably::Models::MessageEncoders::Base64 do
8
+ let(:decoded_data) { SecureRandom.hex(32) }
9
+ let(:base64_data) { Base64.encode64(decoded_data) }
10
+ let(:binary_data) { MessagePack.pack(decoded_data) }
11
+ let(:base64_binary_data) { Base64.encode64(binary_data) }
12
+ let(:client) { instance_double('Ably::Realtime::Client') }
13
+
14
+ subject { Ably::Models::MessageEncoders::Base64.new(client) }
15
+
16
+ context '#decode' do
17
+ before do
18
+ subject.decode message, {}
19
+ end
20
+
21
+ context 'message with base64 payload' do
22
+ let(:message) { { data: base64_data, encoding: 'base64' } }
23
+
24
+ it 'decodes base64' do
25
+ expect(message[:data]).to eql(decoded_data)
26
+ end
27
+
28
+ it 'strips the encoding' do
29
+ expect(message[:encoding]).to be_nil
30
+ end
31
+ end
32
+
33
+ context 'message with base64 payload before other payloads' do
34
+ let(:message) { { data: base64_data, encoding: 'utf-8/base64' } }
35
+
36
+ it 'decodes base64' do
37
+ expect(message[:data]).to eql(decoded_data)
38
+ end
39
+
40
+ it 'strips the encoding' do
41
+ expect(message[:encoding]).to eql('utf-8')
42
+ end
43
+ end
44
+
45
+ context 'message with another payload' do
46
+ let(:message) { { data: decoded_data, encoding: 'utf-8' } }
47
+
48
+ it 'leaves the message data intact' do
49
+ expect(message[:data]).to eql(decoded_data)
50
+ end
51
+
52
+ it 'leaves the encoding intact' do
53
+ expect(message[:encoding]).to eql('utf-8')
54
+ end
55
+ end
56
+ end
57
+
58
+ context '#encode' do
59
+ context 'over binary transport' do
60
+ before do
61
+ allow(client).to receive(:protocol_binary?).and_return(true)
62
+ subject.encode message, {}
63
+ end
64
+
65
+ context 'message with binary payload' do
66
+ let(:message) { { data: binary_data, encoding: nil } }
67
+
68
+ it 'leaves the message data intact as Base64 encoding is not necessary' do
69
+ expect(message[:data]).to eql(binary_data)
70
+ end
71
+
72
+ it 'leaves the encoding intact' do
73
+ expect(message[:encoding]).to eql(nil)
74
+ end
75
+ end
76
+
77
+ context 'already encoded message with binary payload' do
78
+ let(:message) { { data: binary_data, encoding: 'cipher' } }
79
+
80
+ it 'leaves the message data intact as Base64 encoding is not necessary' do
81
+ expect(message[:data]).to eql(binary_data)
82
+ end
83
+
84
+ it 'leaves the encoding intact' do
85
+ expect(message[:encoding]).to eql('cipher')
86
+ end
87
+ end
88
+
89
+ context 'message with UTF-8 payload' do
90
+ let(:message) { { data: decoded_data, encoding: nil } }
91
+
92
+ it 'leaves the data intact' do
93
+ expect(message[:data]).to eql(decoded_data)
94
+ end
95
+
96
+ it 'leaves the encoding intact' do
97
+ expect(message[:encoding]).to be_nil
98
+ end
99
+ end
100
+
101
+ context 'message with nil payload' do
102
+ let(:message) { { data: nil, encoding: nil } }
103
+
104
+ it 'leaves the message data intact' do
105
+ expect(message[:data]).to be_nil
106
+ end
107
+
108
+ it 'leaves the encoding intact' do
109
+ expect(message[:encoding]).to be_nil
110
+ end
111
+ end
112
+
113
+ context 'message with empty binary string payload' do
114
+ let(:message) { { data: ''.force_encoding(Encoding::ASCII_8BIT), encoding: nil } }
115
+
116
+ it 'leaves the message data intact' do
117
+ expect(message[:data]).to eql('')
118
+ end
119
+
120
+ it 'leaves the encoding intact' do
121
+ expect(message[:encoding]).to be_nil
122
+ end
123
+ end
124
+ end
125
+
126
+ context 'over text transport' do
127
+ before do
128
+ allow(client).to receive(:protocol_binary?).and_return(false)
129
+ subject.encode message, {}
130
+ end
131
+
132
+ context 'message with binary payload' do
133
+ let(:message) { { data: binary_data, encoding: nil } }
134
+
135
+ it 'encodes binary data as base64' do
136
+ expect(message[:data]).to eql(base64_binary_data)
137
+ end
138
+
139
+ it 'adds the encoding' do
140
+ expect(message[:encoding]).to eql('base64')
141
+ end
142
+ end
143
+
144
+ context 'already encoded message with binary payload' do
145
+ let(:message) { { data: binary_data, encoding: 'cipher' } }
146
+
147
+ it 'encodes binary data as base64' do
148
+ expect(message[:data]).to eql(base64_binary_data)
149
+ end
150
+
151
+ it 'adds the encoding' do
152
+ expect(message[:encoding]).to eql('cipher/base64')
153
+ end
154
+ end
155
+
156
+ context 'message with UTF-8 payload' do
157
+ let(:message) { { data: decoded_data, encoding: nil } }
158
+
159
+ it 'leaves the data intact' do
160
+ expect(message[:data]).to eql(decoded_data)
161
+ end
162
+
163
+ it 'leaves the encoding intact' do
164
+ expect(message[:encoding]).to be_nil
165
+ end
166
+ end
167
+
168
+ context 'message with nil payload' do
169
+ let(:message) { { data: nil, encoding: nil } }
170
+
171
+ it 'leaves the message data intact' do
172
+ expect(message[:data]).to be_nil
173
+ end
174
+
175
+ it 'leaves the encoding intact' do
176
+ expect(message[:encoding]).to be_nil
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,260 @@
1
+ require 'spec_helper'
2
+ require 'ably/models/message_encoders/cipher'
3
+ require 'msgpack'
4
+
5
+ describe Ably::Models::MessageEncoders::Cipher do
6
+ let(:secret_key) { SecureRandom.hex(64) }
7
+ let(:crypto_options) { { key: secret_key, algorithm: 'AES', mode: 'CBC', key_length: 128 } }
8
+ let(:crypto) { Ably::Util::Crypto.new(cipher_params) }
9
+
10
+ let(:decoded_data) { SecureRandom.hex(32) }
11
+ let(:cipher_data) { crypto.encrypt(decoded_data) }
12
+
13
+ let(:binary_data) { MessagePack.pack(decoded_data) }
14
+ let(:binary_cipher_data) { crypto.encrypt(binary_data) }
15
+
16
+ let(:client) { instance_double('Ably::Realtime::Client') }
17
+
18
+ subject { Ably::Models::MessageEncoders::Cipher.new(client) }
19
+
20
+ context '#decode' do
21
+ context 'with channel set up for AES-128-CBC' do
22
+ let(:cipher_params) { crypto_options }
23
+
24
+ context 'valid cipher data' do
25
+ before do
26
+ subject.decode message, { encrypted: true, cipher_params: cipher_params }
27
+ end
28
+
29
+ context 'message with cipher payload' do
30
+ let(:message) { { data: cipher_data, encoding: 'cipher+aes-128-cbc' } }
31
+
32
+ it 'decodes cipher' do
33
+ expect(message[:data]).to eql(decoded_data)
34
+ end
35
+
36
+ it 'strips the encoding' do
37
+ expect(message[:encoding]).to be_nil
38
+ end
39
+ end
40
+
41
+ context 'message with cipher payload before other payloads' do
42
+ let(:message) { { data: cipher_data, encoding: 'utf-8/cipher+aes-128-cbc' } }
43
+
44
+ it 'decodes cipher' do
45
+ expect(message[:data]).to eql(decoded_data)
46
+ end
47
+
48
+ it 'strips the encoding' do
49
+ expect(message[:encoding]).to eql('utf-8')
50
+ end
51
+ end
52
+
53
+ context 'message with binary payload' do
54
+ let(:message) { { data: binary_cipher_data, encoding: 'base64/cipher+aes-128-cbc' } }
55
+
56
+ it 'decodes cipher' do
57
+ expect(message[:data]).to eql(binary_data)
58
+ end
59
+
60
+ it 'strips the encoding' do
61
+ expect(message[:encoding]).to eql('base64')
62
+ end
63
+
64
+ it 'returns ASCII_8BIT encoded binary data' do
65
+ expect(message[:data].encoding).to eql(Encoding::ASCII_8BIT)
66
+ end
67
+ end
68
+
69
+ context 'message with another payload' do
70
+ let(:message) { { data: decoded_data, encoding: 'utf-8' } }
71
+
72
+ it 'leaves the message data intact' do
73
+ expect(message[:data]).to eql(decoded_data)
74
+ end
75
+
76
+ it 'leaves the encoding intact' do
77
+ expect(message[:encoding]).to eql('utf-8')
78
+ end
79
+ end
80
+ end
81
+
82
+ context 'with invalid channel_option cipher params' do
83
+ let(:message) { { data: decoded_data, encoding: 'cipher+aes-128-cbc' } }
84
+ let(:cipher_params) { crypto_options.merge(key_length: 256) }
85
+ let(:decode_method) { subject.decode message, { encrypted: true, cipher_params: cipher_params } }
86
+
87
+ it 'raise an exception' do
88
+ expect { decode_method }.to raise_error Ably::Exceptions::CipherError, /Cipher algorithm [\w\d-]+ does not match message cipher algorithm of AES-128-CBC/
89
+ end
90
+ end
91
+
92
+ context 'without any configured encryption' do
93
+ let(:message) { { data: decoded_data, encoding: 'cipher+aes-128-cbc' } }
94
+ let(:cipher_params) { crypto_options.merge(key_length: 256) }
95
+ let(:decode_method) { subject.decode message, {} }
96
+
97
+ it 'raise an exception' do
98
+ expect { decode_method }.to raise_error Ably::Exceptions::CipherError, /Message cannot be decrypted as the channel is not set up for encryption & decryption/
99
+ end
100
+ end
101
+
102
+ context 'with invalid cipher data' do
103
+ let(:message) { { data: decoded_data, encoding: 'cipher+aes-128-cbc' } }
104
+ let(:decode_method) { subject.decode(message, { encrypted: true, cipher_params: cipher_params }) }
105
+
106
+ it 'raise an exception' do
107
+ expect { decode_method }.to raise_error Ably::Exceptions::CipherError, /CipherError decrypting data/
108
+ end
109
+ end
110
+ end
111
+
112
+ context 'with AES-256-CBC' do
113
+ let(:cipher_params) { crypto_options.merge(key_length: 256) }
114
+
115
+ before do
116
+ subject.decode message, { encrypted: true, cipher_params: cipher_params }
117
+ end
118
+
119
+ context 'message with cipher payload' do
120
+ let(:message) { { data: cipher_data, encoding: 'cipher+aes-256-cbc' } }
121
+
122
+ it 'decodes cipher' do
123
+ expect(message[:data]).to eql(decoded_data)
124
+ end
125
+
126
+ it 'strips the encoding' do
127
+ expect(message[:encoding]).to be_nil
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ context '#encode' do
134
+ context 'with channel set up for AES-128-CBC' do
135
+ let(:cipher_params) { crypto_options }
136
+ let(:channel_options) { { encrypted: true, cipher_params: cipher_params } }
137
+
138
+ context 'with encrypted set to true' do
139
+ before do
140
+ subject.encode message, channel_options
141
+ end
142
+
143
+ context 'message with string payload' do
144
+ let(:message) { { data: decoded_data, encoding: nil } }
145
+
146
+ it 'encodes cipher' do
147
+ expect(message[:data]).to_not eql(decoded_data)
148
+ expect(crypto.decrypt(message[:data])).to eql(decoded_data)
149
+ end
150
+
151
+ it 'adds the encoding with utf-8' do
152
+ expect(message[:encoding]).to eql('utf-8/cipher+aes-128-cbc')
153
+ end
154
+ end
155
+
156
+ context 'message with binary payload' do
157
+ let(:message) { { data: binary_data, encoding: nil } }
158
+
159
+ it 'encodes cipher' do
160
+ expect(message[:data]).to_not eql(binary_data)
161
+ expect(crypto.decrypt(message[:data])).to eql(binary_data)
162
+ end
163
+
164
+ it 'adds the encoding without utf-8 prefixed' do
165
+ expect(message[:encoding]).to eql('cipher+aes-128-cbc')
166
+ end
167
+
168
+ it 'returns ASCII_8BIT encoded binary data' do
169
+ expect(message[:data].encoding).to eql(Encoding::ASCII_8BIT)
170
+ end
171
+ end
172
+
173
+ context 'message with json payload' do
174
+ let(:message) { { data: decoded_data, encoding: 'json' } }
175
+
176
+ it 'encodes cipher' do
177
+ expect(message[:data]).to_not eql(decoded_data)
178
+ expect(crypto.decrypt(message[:data])).to eql(decoded_data)
179
+ end
180
+
181
+ it 'adds the encoding with utf-8' do
182
+ expect(message[:encoding]).to eql('json/utf-8/cipher+aes-128-cbc')
183
+ end
184
+ end
185
+
186
+ context 'message with existing cipher encoding before' do
187
+ let(:message) { { data: decoded_data, encoding: 'utf-8/cipher+aes-128-cbc' } }
188
+
189
+ it 'leaves message intact as it is already encrypted' do
190
+ expect(message[:data]).to eql(decoded_data)
191
+ end
192
+
193
+ it 'leaves encoding intact' do
194
+ expect(message[:encoding]).to eql('utf-8/cipher+aes-128-cbc')
195
+ end
196
+ end
197
+
198
+ context 'with encryption set to to false' do
199
+ let(:message) { { data: decoded_data, encoding: 'utf-8' } }
200
+ let(:channel_options) { { encrypted: false, cipher_params: cipher_params } }
201
+
202
+ it 'leaves message intact as encryption is not enable' do
203
+ expect(message[:data]).to eql(decoded_data)
204
+ end
205
+
206
+ it 'leaves encoding intact' do
207
+ expect(message[:encoding]).to eql('utf-8')
208
+ end
209
+ end
210
+ end
211
+
212
+ context 'channel_option cipher params' do
213
+ let(:message) { { data: decoded_data, encoding: nil } }
214
+ let(:encode_method) { subject.encode message, { encrypted: true, cipher_params: cipher_params } }
215
+
216
+ context 'have invalid key length' do
217
+ let(:cipher_params) { crypto_options.merge(key_length: 1) }
218
+ it 'raise an exception' do
219
+ expect { encode_method }.to raise_error Ably::Exceptions::CipherError, /unsupported cipher algorithm/
220
+ end
221
+ end
222
+
223
+ context 'have invalid algorithm' do
224
+ let(:cipher_params) { crypto_options.merge(algorithm: 'does not exist') }
225
+ it 'raise an exception' do
226
+ expect { encode_method }.to raise_error Ably::Exceptions::CipherError, /unsupported cipher algorithm/
227
+ end
228
+ end
229
+
230
+ context 'have missing key' do
231
+ let(:cipher_params) { {} }
232
+ it 'raise an exception' do
233
+ expect { encode_method }.to raise_error Ably::Exceptions::CipherError, /:key is required/
234
+ end
235
+ end
236
+ end
237
+ end
238
+
239
+ context 'with AES-256-CBC' do
240
+ let(:cipher_params) { crypto_options.merge(key_length: 256) }
241
+
242
+ before do
243
+ subject.encode message, { encrypted: true, cipher_params: cipher_params }
244
+ end
245
+
246
+ context 'message with cipher payload' do
247
+ let(:message) { { data: decoded_data, encoding: 'utf-8' } }
248
+
249
+ it 'decodes cipher' do
250
+ expect(message[:data]).to_not eql(decoded_data)
251
+ expect(crypto.decrypt(message[:data])).to eql(decoded_data)
252
+ end
253
+
254
+ it 'strips the encoding' do
255
+ expect(message[:encoding]).to eql('utf-8/cipher+aes-256-cbc')
256
+ end
257
+ end
258
+ end
259
+ end
260
+ end
@@ -0,0 +1,135 @@
1
+ require 'spec_helper'
2
+ require 'json'
3
+
4
+ require 'ably/models/message_encoders/json'
5
+
6
+ describe Ably::Models::MessageEncoders::Json do
7
+ let(:hash_data) { { 'key' => 'value', 'key2' => 123 } }
8
+ let(:hash_string_data) { JSON.dump(hash_data) }
9
+ let(:array_data) { ['value', 123] }
10
+ let(:array_string_data) { JSON.dump(array_data) }
11
+
12
+ let(:client) { instance_double('Ably::Realtime::Client') }
13
+
14
+ subject { Ably::Models::MessageEncoders::Json.new(client) }
15
+
16
+ context '#decode' do
17
+ before do
18
+ subject.decode message, {}
19
+ end
20
+
21
+ context 'message with json payload' do
22
+ let(:message) { { data: hash_string_data, encoding: 'json' } }
23
+
24
+ it 'decodes json' do
25
+ expect(message[:data]).to eq(hash_data)
26
+ end
27
+
28
+ it 'strips the encoding' do
29
+ expect(message[:encoding]).to be_nil
30
+ end
31
+ end
32
+
33
+ context 'message with json payload before other payloads' do
34
+ let(:message) { { data: hash_string_data, encoding: 'utf-8/json' } }
35
+
36
+ it 'decodes json' do
37
+ expect(message[:data]).to eql(hash_data)
38
+ end
39
+
40
+ it 'strips the encoding' do
41
+ expect(message[:encoding]).to eql('utf-8')
42
+ end
43
+ end
44
+
45
+ context 'message with another payload' do
46
+ let(:message) { { data: hash_string_data, encoding: 'utf-8' } }
47
+
48
+ it 'leaves the message data intact' do
49
+ expect(message[:data]).to eql(hash_string_data)
50
+ end
51
+
52
+ it 'leaves the encoding intact' do
53
+ expect(message[:encoding]).to eql('utf-8')
54
+ end
55
+ end
56
+ end
57
+
58
+ context '#encode' do
59
+ before do
60
+ subject.encode message, {}
61
+ end
62
+
63
+ context 'message with hash payload' do
64
+ let(:message) { { data: hash_data, encoding: nil } }
65
+
66
+ it 'encodes hash payload data as json' do
67
+ expect(message[:data]).to eql(hash_string_data)
68
+ end
69
+
70
+ it 'adds the encoding' do
71
+ expect(message[:encoding]).to eql('json')
72
+ end
73
+ end
74
+
75
+ context 'already encoded message with hash payload' do
76
+ let(:message) { { data: hash_data, encoding: 'utf-8' } }
77
+
78
+ it 'encodes hash payload data as json' do
79
+ expect(message[:data]).to eql(hash_string_data)
80
+ end
81
+
82
+ it 'adds the encoding' do
83
+ expect(message[:encoding]).to eql('utf-8/json')
84
+ end
85
+ end
86
+
87
+ context 'message with Array payload' do
88
+ let(:message) { { data: array_data, encoding: nil } }
89
+
90
+ it 'encodes Array payload data as json' do
91
+ expect(message[:data]).to eql(array_string_data)
92
+ end
93
+
94
+ it 'adds the encoding' do
95
+ expect(message[:encoding]).to eql('json')
96
+ end
97
+ end
98
+
99
+ context 'message with UTF-8 payload' do
100
+ let(:message) { { data: hash_string_data, encoding: nil } }
101
+
102
+ it 'leaves the message data intact' do
103
+ expect(message[:data]).to eql(hash_string_data)
104
+ end
105
+
106
+ it 'leaves the encoding intact' do
107
+ expect(message[:encoding]).to be_nil
108
+ end
109
+ end
110
+
111
+ context 'message with nil payload' do
112
+ let(:message) { { data: nil, encoding: nil } }
113
+
114
+ it 'leaves the message data intact' do
115
+ expect(message[:data]).to be_nil
116
+ end
117
+
118
+ it 'leaves the encoding intact' do
119
+ expect(message[:encoding]).to be_nil
120
+ end
121
+ end
122
+
123
+ context 'message with no data payload' do
124
+ let(:message) { { encoding: nil } }
125
+
126
+ it 'leaves the message data intact' do
127
+ expect(message[:data]).to be_nil
128
+ end
129
+
130
+ it 'leaves the encoding intact' do
131
+ expect(message[:encoding]).to be_nil
132
+ end
133
+ end
134
+ end
135
+ end