ably 0.6.2 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,7 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'securerandom'
|
3
2
|
|
4
|
-
describe Ably::Models::IdiomaticRubyWrapper do
|
3
|
+
describe Ably::Models::IdiomaticRubyWrapper, :api_private do
|
5
4
|
include Ably::Modules::Conversions
|
6
5
|
|
7
6
|
let(:mixed_case_data) do
|
@@ -107,7 +106,7 @@ describe Ably::Models::IdiomaticRubyWrapper do
|
|
107
106
|
'lowercasestring' => 'lowercasestringValue'
|
108
107
|
}
|
109
108
|
end
|
110
|
-
let(:unique_value) {
|
109
|
+
let(:unique_value) { random_str }
|
111
110
|
|
112
111
|
subject { Ably::Models::IdiomaticRubyWrapper.new(data) }
|
113
112
|
|
@@ -5,7 +5,7 @@ require 'msgpack'
|
|
5
5
|
require 'ably/models/message_encoders/base64'
|
6
6
|
|
7
7
|
describe Ably::Models::MessageEncoders::Base64 do
|
8
|
-
let(:decoded_data) {
|
8
|
+
let(:decoded_data) { random_str(32) }
|
9
9
|
let(:base64_data) { Base64.encode64(decoded_data) }
|
10
10
|
let(:binary_data) { MessagePack.pack(decoded_data) }
|
11
11
|
let(:base64_binary_data) { Base64.encode64(binary_data) }
|
@@ -111,7 +111,7 @@ describe Ably::Models::MessageEncoders::Base64 do
|
|
111
111
|
end
|
112
112
|
|
113
113
|
context 'message with empty binary string payload' do
|
114
|
-
let(:message) { { data: ''.
|
114
|
+
let(:message) { { data: ''.encode(Encoding::ASCII_8BIT), encoding: nil } }
|
115
115
|
|
116
116
|
it 'leaves the message data intact' do
|
117
117
|
expect(message[:data]).to eql('')
|
@@ -3,11 +3,11 @@ require 'ably/models/message_encoders/cipher'
|
|
3
3
|
require 'msgpack'
|
4
4
|
|
5
5
|
describe Ably::Models::MessageEncoders::Cipher do
|
6
|
-
let(:secret_key) {
|
6
|
+
let(:secret_key) { random_str(64) }
|
7
7
|
let(:crypto_options) { { key: secret_key, algorithm: 'AES', mode: 'CBC', key_length: 128 } }
|
8
8
|
let(:crypto) { Ably::Util::Crypto.new(cipher_params) }
|
9
9
|
|
10
|
-
let(:decoded_data) {
|
10
|
+
let(:decoded_data) { random_str(32) }
|
11
11
|
let(:cipher_data) { crypto.encrypt(decoded_data) }
|
12
12
|
|
13
13
|
let(:binary_data) { MessagePack.pack(decoded_data) }
|
@@ -3,8 +3,8 @@ require 'spec_helper'
|
|
3
3
|
require 'ably/models/message_encoders/utf8'
|
4
4
|
|
5
5
|
describe Ably::Models::MessageEncoders::Utf8 do
|
6
|
-
let(:string_ascii) { 'string'.
|
7
|
-
let(:string_utf8) { 'string'.
|
6
|
+
let(:string_ascii) { 'string'.encode(Encoding::ASCII_8BIT) }
|
7
|
+
let(:string_utf8) { 'string'.encode(Encoding::UTF_8) }
|
8
8
|
|
9
9
|
let(:client) { instance_double('Ably::Realtime::Client') }
|
10
10
|
|
@@ -53,48 +53,4 @@ describe Ably::Models::MessageEncoders::Utf8 do
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
end
|
56
|
-
|
57
|
-
context '#encode' do
|
58
|
-
before do
|
59
|
-
subject.encode message, {}
|
60
|
-
end
|
61
|
-
|
62
|
-
context 'message with json payload' do
|
63
|
-
let(:message) { { data: string_ascii, encoding: 'json' } }
|
64
|
-
|
65
|
-
it 'sets the cencoding' do
|
66
|
-
expect(message[:data]).to eql(string_utf8)
|
67
|
-
expect(message[:data].encoding).to eql(Encoding::UTF_8)
|
68
|
-
end
|
69
|
-
|
70
|
-
it 'adds the encoding' do
|
71
|
-
expect(message[:encoding]).to eql('json/utf-8')
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
|
76
|
-
context 'message with string payload and no encoding' do
|
77
|
-
let(:message) { { data: string_ascii, encoding: nil } }
|
78
|
-
|
79
|
-
it 'leaves the message data intact' do
|
80
|
-
expect(message[:data]).to eql(string_ascii)
|
81
|
-
end
|
82
|
-
|
83
|
-
it 'leaves the encoding intact' do
|
84
|
-
expect(message[:encoding]).to eql(nil)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
context 'message with string payload and UTF-8 encoding' do
|
89
|
-
let(:message) { { data: string_ascii, encoding: 'utf-8' } }
|
90
|
-
|
91
|
-
it 'leaves the message data intact' do
|
92
|
-
expect(message[:data]).to eql(string_ascii)
|
93
|
-
end
|
94
|
-
|
95
|
-
it 'leaves the encoding intact' do
|
96
|
-
expect(message[:encoding]).to eql('utf-8')
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
56
|
end
|
@@ -1,5 +1,6 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require 'spec_helper'
|
2
|
-
require '
|
3
|
+
require 'shared/model_behaviour'
|
3
4
|
require 'base64'
|
4
5
|
require 'msgpack'
|
5
6
|
|
@@ -16,13 +17,56 @@ describe Ably::Models::Message do
|
|
16
17
|
|
17
18
|
context '#timestamp' do
|
18
19
|
let(:model) { subject.new({}, protocol_message) }
|
19
|
-
|
20
|
+
|
21
|
+
it 'retrieves attribute :timestamp as Time object from ProtocolMessage' do
|
20
22
|
expect(model.timestamp).to be_a(Time)
|
21
23
|
expect(model.timestamp.to_i).to be_within(1).of(Time.now.to_i)
|
22
24
|
end
|
23
25
|
end
|
24
26
|
|
25
|
-
context '
|
27
|
+
context '#connection_id attribute' do
|
28
|
+
let(:protocol_connection_id) { random_str }
|
29
|
+
let(:protocol_message) { Ably::Models::ProtocolMessage.new('connectionId' => protocol_connection_id, action: 1, timestamp: protocol_message_timestamp) }
|
30
|
+
let(:model_connection_id) { random_str }
|
31
|
+
|
32
|
+
context 'when this model has a connectionId attribute' do
|
33
|
+
context 'but no protocol message' do
|
34
|
+
let(:model) { subject.new('connectionId' => model_connection_id ) }
|
35
|
+
|
36
|
+
it 'uses the model value' do
|
37
|
+
expect(model.connection_id).to eql(model_connection_id)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'with a protocol message with a different connectionId' do
|
42
|
+
let(:model) { subject.new({ 'connectionId' => model_connection_id }, protocol_message) }
|
43
|
+
|
44
|
+
it 'uses the model value' do
|
45
|
+
expect(model.connection_id).to eql(model_connection_id)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'when this model has no connectionId attribute' do
|
51
|
+
context 'and no protocol message' do
|
52
|
+
let(:model) { subject.new({ }) }
|
53
|
+
|
54
|
+
it 'uses the model value' do
|
55
|
+
expect(model.connection_id).to be_nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'with a protocol message with a connectionId' do
|
60
|
+
let(:model) { subject.new({ }, protocol_message) }
|
61
|
+
|
62
|
+
it 'uses the model value' do
|
63
|
+
expect(model.connection_id).to eql(protocol_connection_id)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'Java naming', :api_private do
|
26
70
|
let(:model) { subject.new({ clientId: 'joe' }, protocol_message) }
|
27
71
|
|
28
72
|
it 'converts the attribute to ruby symbol naming convention' do
|
@@ -30,7 +74,71 @@ describe Ably::Models::Message do
|
|
30
74
|
end
|
31
75
|
end
|
32
76
|
|
33
|
-
context '
|
77
|
+
context 'initialized with' do
|
78
|
+
%w(name client_id encoding).each do |attribute|
|
79
|
+
context ":#{attribute}" do
|
80
|
+
let(:encoded_value) { value.encode(encoding) }
|
81
|
+
let(:value) { random_str }
|
82
|
+
let(:options) { { attribute.to_sym => encoded_value } }
|
83
|
+
let(:model) { subject.new(options, protocol_message) }
|
84
|
+
let(:model_attribute) { model.public_send(attribute) }
|
85
|
+
|
86
|
+
context 'as UTF_8 string' do
|
87
|
+
let(:encoding) { Encoding::UTF_8 }
|
88
|
+
|
89
|
+
it 'is permitted' do
|
90
|
+
expect(model_attribute).to eql(encoded_value)
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'remains as UTF-8' do
|
94
|
+
expect(model_attribute.encoding).to eql(encoding)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'as SHIFT_JIS string' do
|
99
|
+
let(:encoding) { Encoding::SHIFT_JIS }
|
100
|
+
|
101
|
+
it 'gets converted to UTF-8' do
|
102
|
+
expect(model_attribute.encoding).to eql(Encoding::UTF_8)
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'is compatible with original encoding' do
|
106
|
+
expect(model_attribute.encode(encoding)).to eql(encoded_value)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context 'as ASCII_8BIT string' do
|
111
|
+
let(:encoding) { Encoding::ASCII_8BIT }
|
112
|
+
|
113
|
+
it 'gets converted to UTF-8' do
|
114
|
+
expect(model_attribute.encoding).to eql(Encoding::UTF_8)
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'is compatible with original encoding' do
|
118
|
+
expect(model_attribute.encode(encoding)).to eql(encoded_value)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context 'as Integer' do
|
123
|
+
let(:encoded_value) { 1 }
|
124
|
+
|
125
|
+
it 'raises an argument error' do
|
126
|
+
expect { model_attribute }.to raise_error ArgumentError, /must be a String/
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'as Nil' do
|
131
|
+
let(:encoded_value) { nil }
|
132
|
+
|
133
|
+
it 'is permitted' do
|
134
|
+
expect(model_attribute).to be_nil
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
context '#to_json', :api_private do
|
34
142
|
let(:json_object) { JSON.parse(model.to_json) }
|
35
143
|
|
36
144
|
context 'with valid data' do
|
@@ -50,7 +158,7 @@ describe Ably::Models::Message do
|
|
50
158
|
end
|
51
159
|
|
52
160
|
context 'with binary data' do
|
53
|
-
let(:data) { MessagePack.pack(
|
161
|
+
let(:data) { MessagePack.pack(random_str(32)) }
|
54
162
|
let(:model) { subject.new({ name: 'test', data: data }, protocol_message) }
|
55
163
|
|
56
164
|
it 'encodes as Base64 so that it can be converted to UTF-8 automatically by JSON#dump' do
|
@@ -63,13 +171,25 @@ describe Ably::Models::Message do
|
|
63
171
|
end
|
64
172
|
end
|
65
173
|
|
66
|
-
context 'from REST request with embedded fields' do
|
67
|
-
let(:id)
|
68
|
-
let(:
|
69
|
-
let(:
|
70
|
-
let(:
|
174
|
+
context 'from REST request with embedded fields', :api_private do
|
175
|
+
let(:id) { random_str }
|
176
|
+
let(:protocol_message_id) { random_str }
|
177
|
+
let(:message_time) { Time.now + 60 }
|
178
|
+
let(:message_timestamp) { as_since_epoch(message_time) }
|
179
|
+
let(:protocol_time) { Time.now }
|
180
|
+
let(:protocol_timestamp) { as_since_epoch(protocol_time) }
|
181
|
+
|
182
|
+
let(:protocol_message) do
|
183
|
+
Ably::Models::ProtocolMessage.new({
|
184
|
+
action: :message,
|
185
|
+
timestamp: protocol_timestamp,
|
186
|
+
id: protocol_message_id
|
187
|
+
})
|
188
|
+
end
|
71
189
|
|
72
190
|
context 'with protocol message' do
|
191
|
+
let(:model) { subject.new({ id: id, timestamp: message_timestamp }, protocol_message) }
|
192
|
+
|
73
193
|
specify '#id prefers embedded ID' do
|
74
194
|
expect(model.id).to eql(id)
|
75
195
|
end
|
@@ -80,6 +200,8 @@ describe Ably::Models::Message do
|
|
80
200
|
end
|
81
201
|
|
82
202
|
context 'without protocol message' do
|
203
|
+
let(:model) { subject.new(id: id, timestamp: message_timestamp) }
|
204
|
+
|
83
205
|
specify '#id uses embedded ID' do
|
84
206
|
expect(model.id).to eql(id)
|
85
207
|
end
|
@@ -90,10 +212,10 @@ describe Ably::Models::Message do
|
|
90
212
|
end
|
91
213
|
end
|
92
214
|
|
93
|
-
context 'part of ProtocolMessage' do
|
215
|
+
context 'part of ProtocolMessage', :api_private do
|
94
216
|
let(:ably_time) { Time.now + 5 }
|
95
|
-
let(:message_serial) {
|
96
|
-
let(:connection_id) {
|
217
|
+
let(:message_serial) { random_int_str(1_000_000) }
|
218
|
+
let(:connection_id) { random_str }
|
97
219
|
|
98
220
|
let(:message_0_payload) do
|
99
221
|
{
|
@@ -117,7 +239,7 @@ describe Ably::Models::Message do
|
|
117
239
|
}
|
118
240
|
end
|
119
241
|
|
120
|
-
let(:protocol_message_id) {
|
242
|
+
let(:protocol_message_id) { random_str }
|
121
243
|
let(:protocol_message) do
|
122
244
|
Ably::Models::ProtocolMessage.new({
|
123
245
|
action: :message,
|
@@ -150,9 +272,32 @@ describe Ably::Models::Message do
|
|
150
272
|
it 'should not allow changes to the payload' do
|
151
273
|
expect { message_0.data["test"] = true }.to raise_error RuntimeError, /can't modify frozen Hash/
|
152
274
|
end
|
275
|
+
|
276
|
+
context 'with identical message objects' do
|
277
|
+
let(:protocol_message) do
|
278
|
+
Ably::Models::ProtocolMessage.new({
|
279
|
+
action: :message,
|
280
|
+
timestamp: ably_time.to_i,
|
281
|
+
msg_serial: message_serial,
|
282
|
+
id: protocol_message_id,
|
283
|
+
messages: [
|
284
|
+
message_0_json, message_0_json, message_0_json
|
285
|
+
]
|
286
|
+
})
|
287
|
+
end
|
288
|
+
|
289
|
+
it 'provide a unique ID:index' do
|
290
|
+
expect(protocol_message.messages.map(&:id).uniq.count).to eql(3)
|
291
|
+
end
|
292
|
+
|
293
|
+
it 'recognises the index based on the object ID as opposed to message payload' do
|
294
|
+
expect(protocol_message.messages.first.id).to match(/0$/)
|
295
|
+
expect(protocol_message.messages.last.id).to match(/2$/)
|
296
|
+
end
|
297
|
+
end
|
153
298
|
end
|
154
299
|
|
155
|
-
context 'Message conversion method' do
|
300
|
+
context 'Message conversion method', :api_private do
|
156
301
|
let(:json) { { name: 'test', data: 'conversion' } }
|
157
302
|
|
158
303
|
context 'with JSON' do
|
@@ -58,7 +58,7 @@ describe Ably::Models::PaginatedResource do
|
|
58
58
|
expect(subject.last[:id]).to eql(body[1][:id])
|
59
59
|
end
|
60
60
|
|
61
|
-
context 'with coercion' do
|
61
|
+
context 'with coercion', :api_private do
|
62
62
|
let(:paginated_resource_options) { { coerce_into: 'OpenStruct' } }
|
63
63
|
|
64
64
|
it 'returns coerced objects' do
|
@@ -67,7 +67,7 @@ describe Ably::Models::PaginatedResource do
|
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
-
context 'paged transformations' do
|
70
|
+
context 'paged transformations', :api_private do
|
71
71
|
let(:headers) do
|
72
72
|
{
|
73
73
|
'link' => [
|
@@ -114,43 +114,45 @@ describe Ably::Models::PaginatedResource do
|
|
114
114
|
end
|
115
115
|
end
|
116
116
|
|
117
|
-
|
118
|
-
|
117
|
+
if defined?(EventMachine)
|
118
|
+
context 'with option async_blocking_operations: true' do
|
119
|
+
include RSpec::EventMachine
|
119
120
|
|
120
|
-
|
121
|
-
|
122
|
-
end
|
123
|
-
|
124
|
-
context '#next_page' do
|
125
|
-
it 'returns a deferrable object' do
|
126
|
-
run_reactor do
|
127
|
-
expect(subject.next_page).to be_a(EventMachine::Deferrable)
|
128
|
-
stop_reactor
|
129
|
-
end
|
121
|
+
subject do
|
122
|
+
paginated_resource_class.new(http_response, full_url, paged_client, async_blocking_operations: true)
|
130
123
|
end
|
131
124
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
expect(
|
125
|
+
context '#next_page' do
|
126
|
+
it 'returns a deferrable object' do
|
127
|
+
run_reactor do
|
128
|
+
expect(subject.next_page).to be_a(EventMachine::Deferrable)
|
136
129
|
stop_reactor
|
137
130
|
end
|
138
131
|
end
|
139
|
-
end
|
140
|
-
end
|
141
132
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
deferrable = subject.first_page
|
147
|
-
deferrable.errback do |error|
|
148
|
-
expect(error).to be_a(Ably::Exceptions::InvalidPageError)
|
133
|
+
it 'allows a success callback block to be added' do
|
134
|
+
run_reactor do
|
135
|
+
subject.next_page do |paginated_resource|
|
136
|
+
expect(paginated_resource).to be_a(Ably::Models::PaginatedResource)
|
149
137
|
stop_reactor
|
150
138
|
end
|
151
139
|
end
|
152
140
|
end
|
153
141
|
end
|
142
|
+
|
143
|
+
context '#first_page' do
|
144
|
+
it 'calls the errback callback when first page headers are missing' do
|
145
|
+
run_reactor do
|
146
|
+
subject.next_page do |paginated_resource|
|
147
|
+
deferrable = subject.first_page
|
148
|
+
deferrable.errback do |error|
|
149
|
+
expect(error).to be_a(Ably::Exceptions::InvalidPageError)
|
150
|
+
stop_reactor
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
154
156
|
end
|
155
157
|
end
|
156
158
|
end
|
@@ -1,5 +1,6 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require 'spec_helper'
|
2
|
-
require '
|
3
|
+
require 'shared/model_behaviour'
|
3
4
|
|
4
5
|
describe Ably::Models::PresenceMessage do
|
5
6
|
include Ably::Modules::Conversions
|
@@ -8,19 +9,87 @@ describe Ably::Models::PresenceMessage do
|
|
8
9
|
let(:protocol_message_timestamp) { as_since_epoch(Time.now) }
|
9
10
|
let(:protocol_message) { Ably::Models::ProtocolMessage.new(action: 1, timestamp: protocol_message_timestamp) }
|
10
11
|
|
11
|
-
it_behaves_like 'a model', with_simple_attributes: %w(client_id
|
12
|
+
it_behaves_like 'a model', with_simple_attributes: %w(client_id data encoding) do
|
12
13
|
let(:model_args) { [protocol_message] }
|
13
14
|
end
|
14
15
|
|
16
|
+
context '#connection_id attribute' do
|
17
|
+
let(:protocol_connection_id) { random_str }
|
18
|
+
let(:protocol_message) { Ably::Models::ProtocolMessage.new('connectionId' => protocol_connection_id, action: 1, timestamp: protocol_message_timestamp) }
|
19
|
+
let(:model_connection_id) { random_str }
|
20
|
+
|
21
|
+
context 'when this model has a connectionId attribute' do
|
22
|
+
context 'but no protocol message' do
|
23
|
+
let(:model) { subject.new('connectionId' => model_connection_id ) }
|
24
|
+
|
25
|
+
it 'uses the model value' do
|
26
|
+
expect(model.connection_id).to eql(model_connection_id)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'with a protocol message with a different connectionId' do
|
31
|
+
let(:model) { subject.new({ 'connectionId' => model_connection_id }, protocol_message) }
|
32
|
+
|
33
|
+
it 'uses the model value' do
|
34
|
+
expect(model.connection_id).to eql(model_connection_id)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'when this model has no connectionId attribute' do
|
40
|
+
context 'and no protocol message' do
|
41
|
+
let(:model) { subject.new({ }) }
|
42
|
+
|
43
|
+
it 'uses the model value' do
|
44
|
+
expect(model.connection_id).to be_nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'with a protocol message with a connectionId' do
|
49
|
+
let(:model) { subject.new({ }, protocol_message) }
|
50
|
+
|
51
|
+
it 'uses the model value' do
|
52
|
+
expect(model.connection_id).to eql(protocol_connection_id)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context '#member_key attribute' do
|
59
|
+
let(:model) { subject.new(client_id: 'client_id', connection_id: 'connection_id') }
|
60
|
+
|
61
|
+
it 'is string in format connection_id:client_id' do
|
62
|
+
expect(model.member_key).to eql('connection_id:client_id')
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'with the same client id across multiple connections' do
|
66
|
+
let(:connection_1) { subject.new({ client_id: 'same', connection_id: 'unique' }, protocol_message) }
|
67
|
+
let(:connection_2) { subject.new({ client_id: 'same', connection_id: 'different' }, protocol_message) }
|
68
|
+
|
69
|
+
it 'is unique' do
|
70
|
+
expect(connection_1.member_key).to_not eql(connection_2.member_key)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'with a single connection and different client_ids' do
|
75
|
+
let(:client_1) { subject.new({ client_id: 'unique', connection_id: 'same' }, protocol_message) }
|
76
|
+
let(:client_2) { subject.new({ client_id: 'different', connection_id: 'same' }, protocol_message) }
|
77
|
+
|
78
|
+
it 'is unique' do
|
79
|
+
expect(client_1.member_key).to_not eql(client_2.member_key)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
15
84
|
context '#timestamp' do
|
16
85
|
let(:model) { subject.new({}, protocol_message) }
|
17
|
-
it 'retrieves attribute :timestamp from ProtocolMessage' do
|
86
|
+
it 'retrieves attribute :timestamp as a Time object from ProtocolMessage' do
|
18
87
|
expect(model.timestamp).to be_a(Time)
|
19
88
|
expect(model.timestamp.to_i).to be_within(1).of(Time.now.to_i)
|
20
89
|
end
|
21
90
|
end
|
22
91
|
|
23
|
-
context 'Java naming' do
|
92
|
+
context 'Java naming', :api_private do
|
24
93
|
let(:model) { subject.new({ clientId: 'joe' }, protocol_message) }
|
25
94
|
|
26
95
|
it 'converts the attribute to ruby symbol naming convention' do
|
@@ -28,15 +97,25 @@ describe Ably::Models::PresenceMessage do
|
|
28
97
|
end
|
29
98
|
end
|
30
99
|
|
31
|
-
context 'with action' do
|
32
|
-
|
100
|
+
context 'with action', :api_private do
|
101
|
+
context 'absent' do
|
102
|
+
let(:model) { subject.new({ action: 0 }, protocol_message) }
|
103
|
+
|
104
|
+
it 'provides action as an Enum' do
|
105
|
+
expect(model.action).to eq(:absent)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context 'enter' do
|
110
|
+
let(:model) { subject.new({ action: 2 }, protocol_message) }
|
33
111
|
|
34
|
-
|
35
|
-
|
112
|
+
it 'provides action as an Enum' do
|
113
|
+
expect(model.action).to eq(:enter)
|
114
|
+
end
|
36
115
|
end
|
37
116
|
end
|
38
117
|
|
39
|
-
context 'without action' do
|
118
|
+
context 'without action', :api_private do
|
40
119
|
let(:model) { subject.new({}, protocol_message) }
|
41
120
|
|
42
121
|
it 'raises an exception when accessed' do
|
@@ -44,7 +123,71 @@ describe Ably::Models::PresenceMessage do
|
|
44
123
|
end
|
45
124
|
end
|
46
125
|
|
47
|
-
context '
|
126
|
+
context 'initialized with' do
|
127
|
+
%w(client_id connection_id encoding).each do |attribute|
|
128
|
+
context ":#{attribute}" do
|
129
|
+
let(:encoded_value) { value.encode(encoding) }
|
130
|
+
let(:value) { random_str }
|
131
|
+
let(:options) { { attribute.to_sym => encoded_value } }
|
132
|
+
let(:model) { subject.new(options, protocol_message) }
|
133
|
+
let(:model_attribute) { model.public_send(attribute) }
|
134
|
+
|
135
|
+
context 'as UTF_8 string' do
|
136
|
+
let(:encoding) { Encoding::UTF_8 }
|
137
|
+
|
138
|
+
it 'is permitted' do
|
139
|
+
expect(model_attribute).to eql(encoded_value)
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'remains as UTF-8' do
|
143
|
+
expect(model_attribute.encoding).to eql(Encoding::UTF_8)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
context 'as SHIFT_JIS string' do
|
148
|
+
let(:encoding) { Encoding::SHIFT_JIS }
|
149
|
+
|
150
|
+
it 'gets converted to UTF-8' do
|
151
|
+
expect(model_attribute.encoding).to eql(Encoding::UTF_8)
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'is compatible with original encoding' do
|
155
|
+
expect(model_attribute.encode(encoding)).to eql(encoded_value)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
context 'as ASCII_8BIT string' do
|
160
|
+
let(:encoding) { Encoding::ASCII_8BIT }
|
161
|
+
|
162
|
+
it 'gets converted to UTF-8' do
|
163
|
+
expect(model_attribute.encoding).to eql(Encoding::UTF_8)
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'is compatible with original encoding' do
|
167
|
+
expect(model_attribute.encode(encoding)).to eql(encoded_value)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
context 'as Integer' do
|
172
|
+
let(:encoded_value) { 1 }
|
173
|
+
|
174
|
+
it 'raises an argument error' do
|
175
|
+
expect { model_attribute }.to raise_error ArgumentError, /must be a String/
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
context 'as Nil' do
|
180
|
+
let(:encoded_value) { nil }
|
181
|
+
|
182
|
+
it 'is permitted' do
|
183
|
+
expect(model_attribute).to be_nil
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
context '#to_json', :api_private do
|
48
191
|
let(:json_object) { JSON.parse(model.to_json) }
|
49
192
|
|
50
193
|
context 'with valid data' do
|
@@ -64,7 +207,7 @@ describe Ably::Models::PresenceMessage do
|
|
64
207
|
end
|
65
208
|
|
66
209
|
context 'with binary data' do
|
67
|
-
let(:data) { MessagePack.pack(
|
210
|
+
let(:data) { MessagePack.pack(random_str(32)) }
|
68
211
|
let(:model) { subject.new({ action: 'enter', data: data }, protocol_message) }
|
69
212
|
|
70
213
|
it 'encodes as Base64 so that it can be converted to UTF-8 automatically by JSON#dump' do
|
@@ -77,8 +220,8 @@ describe Ably::Models::PresenceMessage do
|
|
77
220
|
end
|
78
221
|
end
|
79
222
|
|
80
|
-
context 'from REST request with embedded fields' do
|
81
|
-
let(:id) {
|
223
|
+
context 'from REST request with embedded fields', :api_private do
|
224
|
+
let(:id) { random_str }
|
82
225
|
let(:message_time) { Time.now + 60 }
|
83
226
|
let(:timestamp) { as_since_epoch(message_time) }
|
84
227
|
let(:model) { subject.new(id: id, timestamp: timestamp) }
|
@@ -104,19 +247,19 @@ describe Ably::Models::PresenceMessage do
|
|
104
247
|
end
|
105
248
|
end
|
106
249
|
|
107
|
-
context 'part of ProtocolMessage' do
|
250
|
+
context 'part of ProtocolMessage', :api_private do
|
108
251
|
let(:ably_time) { Time.now + 5 }
|
109
|
-
let(:message_serial) {
|
110
|
-
let(:connection_id) {
|
252
|
+
let(:message_serial) { random_int_str(1_000_000) }
|
253
|
+
let(:connection_id) { random_str }
|
111
254
|
|
112
|
-
let(:presence_0_payload) {
|
255
|
+
let(:presence_0_payload) { random_str(8) }
|
113
256
|
let(:presence_0_json) do
|
114
257
|
{
|
115
258
|
client_id: 'zero',
|
116
259
|
data: presence_0_payload
|
117
260
|
}
|
118
261
|
end
|
119
|
-
let(:presence_1_payload) {
|
262
|
+
let(:presence_1_payload) { random_str(8) }
|
120
263
|
let(:presence_1_json) do
|
121
264
|
{
|
122
265
|
client_id: 'one',
|
@@ -124,7 +267,7 @@ describe Ably::Models::PresenceMessage do
|
|
124
267
|
}
|
125
268
|
end
|
126
269
|
|
127
|
-
let(:protocol_message_id) {
|
270
|
+
let(:protocol_message_id) { random_str }
|
128
271
|
let(:protocol_message) do
|
129
272
|
Ably::Models::ProtocolMessage.new({
|
130
273
|
action: :message,
|
@@ -151,7 +294,7 @@ describe Ably::Models::PresenceMessage do
|
|
151
294
|
end
|
152
295
|
end
|
153
296
|
|
154
|
-
context 'PresenceMessage conversion method' do
|
297
|
+
context 'PresenceMessage conversion method', :api_private do
|
155
298
|
let(:json) { { client_id: 'test' } }
|
156
299
|
|
157
300
|
context 'with JSON' do
|