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,5 +1,5 @@
1
1
  require 'spec_helper'
2
- require 'support/model_helper'
2
+ require 'shared/model_behaviour'
3
3
 
4
4
  describe Ably::Models::ErrorInfo do
5
5
  subject { Ably::Models::ErrorInfo }
@@ -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) { SecureRandom.hex }
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) { SecureRandom.hex(32) }
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: ''.force_encoding(Encoding::ASCII_8BIT), encoding: nil } }
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) { SecureRandom.hex(64) }
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) { SecureRandom.hex(32) }
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'.force_encoding(Encoding::ASCII_8BIT) }
7
- let(:string_utf8) { 'string'.force_encoding(Encoding::UTF_8) }
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 'support/model_helper'
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
- it 'retrieves attribute :timestamp from ProtocolMessage' do
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 'Java naming' do
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 '#to_json' do
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(SecureRandom.hex(32)) }
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) { SecureRandom.hex }
68
- let(:message_time) { Time.now + 60 }
69
- let(:timestamp) { as_since_epoch(message_time) }
70
- let(:model) { subject.new(id: id, timestamp: timestamp) }
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) { SecureRandom.random_number(1_000_000) }
96
- let(:connection_id) { SecureRandom.hex }
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) { SecureRandom.hex }
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
- context 'with option async_blocking_operations: true' do
118
- include RSpec::EventMachine
117
+ if defined?(EventMachine)
118
+ context 'with option async_blocking_operations: true' do
119
+ include RSpec::EventMachine
119
120
 
120
- subject do
121
- paginated_resource_class.new(http_response, full_url, paged_client, async_blocking_operations: true)
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
- it 'allows a success callback block to be added' do
133
- run_reactor do
134
- subject.next_page do |paginated_resource|
135
- expect(paginated_resource).to be_a(Ably::Models::PaginatedResource)
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
- context '#first_page' do
143
- it 'calls the errback callback when first page headers are missing' do
144
- run_reactor do
145
- subject.next_page do |paginated_resource|
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 'support/model_helper'
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 member_id data encoding) do
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
- let(:model) { subject.new({ action: 0 }, protocol_message) }
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
- it 'provides action as an Enum' do
35
- expect(model.action).to eq(:enter)
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 '#to_json' do
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(SecureRandom.hex(32)) }
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) { SecureRandom.hex }
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) { SecureRandom.random_number(1_000_000) }
110
- let(:connection_id) { SecureRandom.hex }
252
+ let(:message_serial) { random_int_str(1_000_000) }
253
+ let(:connection_id) { random_str }
111
254
 
112
- let(:presence_0_payload) { SecureRandom.hex(8) }
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) { SecureRandom.hex(8) }
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) { SecureRandom.hex }
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