ably 1.1.7 → 1.2.1

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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/check.yml +1 -1
  3. data/CHANGELOG.md +99 -0
  4. data/COPYRIGHT +1 -1
  5. data/README.md +2 -2
  6. data/SPEC.md +0 -7
  7. data/UPDATING.md +30 -0
  8. data/ably.gemspec +11 -24
  9. data/lib/ably/auth.rb +8 -8
  10. data/lib/ably/logger.rb +4 -4
  11. data/lib/ably/models/channel_options.rb +97 -0
  12. data/lib/ably/models/connection_details.rb +8 -2
  13. data/lib/ably/models/delta_extras.rb +29 -0
  14. data/lib/ably/models/device_details.rb +1 -1
  15. data/lib/ably/models/error_info.rb +6 -2
  16. data/lib/ably/models/idiomatic_ruby_wrapper.rb +4 -0
  17. data/lib/ably/models/message.rb +14 -3
  18. data/lib/ably/models/protocol_message.rb +23 -14
  19. data/lib/ably/models/token_details.rb +7 -2
  20. data/lib/ably/models/token_request.rb +1 -1
  21. data/lib/ably/modules/ably.rb +1 -1
  22. data/lib/ably/modules/channels_collection.rb +22 -2
  23. data/lib/ably/modules/conversions.rb +34 -0
  24. data/lib/ably/realtime/auth.rb +2 -2
  25. data/lib/ably/realtime/channel/channel_manager.rb +16 -4
  26. data/lib/ably/realtime/channel/channel_state_machine.rb +10 -1
  27. data/lib/ably/realtime/channel/publisher.rb +3 -2
  28. data/lib/ably/realtime/channel.rb +54 -22
  29. data/lib/ably/realtime/channels.rb +1 -1
  30. data/lib/ably/realtime/connection/connection_manager.rb +13 -4
  31. data/lib/ably/realtime/connection/connection_state_machine.rb +4 -0
  32. data/lib/ably/realtime/connection.rb +0 -3
  33. data/lib/ably/rest/channel.rb +28 -35
  34. data/lib/ably/rest/client.rb +23 -8
  35. data/lib/ably/rest/middleware/encoder.rb +1 -1
  36. data/lib/ably/rest/middleware/exceptions.rb +1 -1
  37. data/lib/ably/rest/middleware/external_exceptions.rb +1 -1
  38. data/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +1 -1
  39. data/lib/ably/rest/middleware/logger.rb +1 -1
  40. data/lib/ably/rest/middleware/parse_json.rb +1 -1
  41. data/lib/ably/rest/middleware/parse_message_pack.rb +1 -1
  42. data/lib/ably/util/crypto.rb +1 -1
  43. data/lib/ably/version.rb +2 -2
  44. data/spec/acceptance/realtime/channel_spec.rb +458 -27
  45. data/spec/acceptance/realtime/channels_spec.rb +59 -7
  46. data/spec/acceptance/realtime/connection_failures_spec.rb +56 -1
  47. data/spec/acceptance/realtime/connection_spec.rb +270 -1
  48. data/spec/acceptance/realtime/message_spec.rb +77 -0
  49. data/spec/acceptance/realtime/presence_spec.rb +18 -1
  50. data/spec/acceptance/rest/auth_spec.rb +18 -0
  51. data/spec/acceptance/rest/channel_spec.rb +73 -11
  52. data/spec/acceptance/rest/channels_spec.rb +23 -6
  53. data/spec/acceptance/rest/client_spec.rb +3 -3
  54. data/spec/acceptance/rest/message_spec.rb +61 -3
  55. data/spec/lib/unit/models/channel_options_spec.rb +52 -0
  56. data/spec/run_parallel_tests +2 -7
  57. data/spec/support/test_app.rb +1 -1
  58. data/spec/unit/logger_spec.rb +6 -14
  59. data/spec/unit/models/delta_extras_spec.rb +14 -0
  60. data/spec/unit/models/error_info_spec.rb +17 -1
  61. data/spec/unit/models/message_spec.rb +38 -0
  62. data/spec/unit/models/protocol_message_spec.rb +77 -27
  63. data/spec/unit/models/token_details_spec.rb +14 -0
  64. data/spec/unit/realtime/channel_spec.rb +2 -1
  65. data/spec/unit/realtime/channels_spec.rb +53 -15
  66. data/spec/unit/rest/channel_spec.rb +40 -7
  67. data/spec/unit/rest/channels_spec.rb +81 -14
  68. data/spec/unit/rest/client_spec.rb +27 -0
  69. metadata +46 -11
@@ -1081,7 +1081,7 @@ describe Ably::Rest::Client do
1081
1081
  end
1082
1082
 
1083
1083
  context 'version headers', :webmock do
1084
- [nil, 'ably-ruby/1.1.1 ruby/1.9.3'].each do |agent|
1084
+ [nil, 'ably-ruby/1.1.1 ruby/3.1.1'].each do |agent|
1085
1085
  context "with #{agent ? "custom #{agent}" : 'default'} agent" do
1086
1086
  let(:client_options) { default_options.merge(key: api_key, agent: agent) }
1087
1087
 
@@ -1097,7 +1097,7 @@ describe Ably::Rest::Client do
1097
1097
  it 'sends a protocol version and lib version header (#G4, #RSC7a, #RSC7b)' do
1098
1098
  client.channels.get('foo').publish("event")
1099
1099
  expect(publish_message_stub).to have_been_requested
1100
- expect(Ably::PROTOCOL_VERSION).to eql('1.1')
1100
+ expect(Ably::PROTOCOL_VERSION).to eql('1.2')
1101
1101
  end
1102
1102
  end
1103
1103
  end
@@ -1231,7 +1231,7 @@ describe Ably::Rest::Client do
1231
1231
  end
1232
1232
  end
1233
1233
 
1234
- context 'request_id generation' do
1234
+ context 'request_id generation (#RSC7c)' do
1235
1235
  context 'Timeout error' do
1236
1236
  context 'with option add_request_ids: true and no fallback hosts', :webmock, :prevent_log_stubbing do
1237
1237
  let(:custom_logger_object) { TestLogger.new }
@@ -24,6 +24,57 @@ describe Ably::Rest::Channel, 'messages' do
24
24
  end
25
25
  end
26
26
 
27
+ context 'a single Message object (#RSL1a)' do
28
+ let(:name) { random_str }
29
+ let(:data) { random_str }
30
+ let(:message) { Ably::Models::Message.new(name: name, data: data) }
31
+
32
+ it 'publishes the message' do
33
+ channel.publish(message)
34
+ expect(channel.history.items.length).to eql(1)
35
+ message = channel.history.items.first
36
+ expect(message.name).to eq(name)
37
+ expect(message.data).to eq(data)
38
+ end
39
+ end
40
+
41
+ context 'an array of Message objects (#RSL1a)' do
42
+ let(:data) { random_str }
43
+ let(:message1) { Ably::Models::Message.new(name: random_str, data: data) }
44
+ let(:message2) { Ably::Models::Message.new(name: random_str, data: data) }
45
+ let(:message3) { Ably::Models::Message.new(name: random_str, data: data) }
46
+
47
+ it 'publishes three messages' do
48
+ channel.publish([message1, message2, message3])
49
+ expect(channel.history.items.length).to eql(3)
50
+ end
51
+ end
52
+
53
+ context 'an array of hashes (#RSL1a)' do
54
+ let(:data) { random_str }
55
+ let(:message1) { { name: random_str, data: data } }
56
+ let(:message2) { { name: random_str, data: data } }
57
+ let(:message3) { { name: random_str, data: data } }
58
+
59
+ it 'publishes three messages' do
60
+ channel.publish([message1, message2, message3])
61
+ expect(channel.history.items.length).to eql(3)
62
+ end
63
+ end
64
+
65
+ context 'a name with data payload (#RSL1a, #RSL1b)' do
66
+ let(:name) { random_str }
67
+ let(:data) { random_str }
68
+
69
+ it 'publishes the message' do
70
+ channel.publish(name, data)
71
+ expect(channel.history.items.length).to eql(1)
72
+ message = channel.history.items.first
73
+ expect(message.name).to eq(name)
74
+ expect(message.data).to eq(data)
75
+ end
76
+ end
77
+
27
78
  context 'with supported data payload content type' do
28
79
  context 'JSON Object (Hash)' do
29
80
  let(:data) { { 'Hash' => 'true' } }
@@ -153,13 +204,20 @@ describe Ably::Rest::Channel, 'messages' do
153
204
  end
154
205
  end
155
206
 
156
- specify 'idempotent publishing is disabled by default with 1.1 (#TO3n)' do
207
+ specify 'idempotent publishing is disabled by default with <= 1.1 (#TO3n)' do
208
+ stub_const 'Ably::PROTOCOL_VERSION', '1.0'
209
+ client = Ably::Rest::Client.new(key: api_key, protocol: protocol)
210
+ expect(client.idempotent_rest_publishing).to be_falsey
211
+ stub_const 'Ably::PROTOCOL_VERSION', '1.1'
157
212
  client = Ably::Rest::Client.new(key: api_key, protocol: protocol)
158
213
  expect(client.idempotent_rest_publishing).to be_falsey
159
214
  end
160
215
 
161
- specify 'idempotent publishing is enabled by default with 1.2 (#TO3n)' do
162
- stub_const 'Ably::VERSION', '1.2.0'
216
+ specify 'idempotent publishing is enabled by default with >= 1.2 (#TO3n)' do
217
+ stub_const 'Ably::PROTOCOL_VERSION', '1.2'
218
+ client = Ably::Rest::Client.new(key: api_key, protocol: protocol)
219
+ expect(client.idempotent_rest_publishing).to be_truthy
220
+ stub_const 'Ably::PROTOCOL_VERSION', '1.3'
163
221
  client = Ably::Rest::Client.new(key: api_key, protocol: protocol)
164
222
  expect(client.idempotent_rest_publishing).to be_truthy
165
223
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Ably::Models::ChannelOptions do
6
+ let(:modes) { nil }
7
+ let(:params) { {} }
8
+ let(:options) { described_class.new(modes: modes, params: params) }
9
+
10
+ describe '#modes_to_flags' do
11
+ let(:modes) { %w[publish subscribe presence_subscribe] }
12
+
13
+ subject(:protocol_message) do
14
+ Ably::Models::ProtocolMessage.new(action: Ably::Models::ProtocolMessage::ACTION.Attach, flags: options.modes_to_flags)
15
+ end
16
+
17
+ it 'converts modes to ProtocolMessage#flags correctly' do
18
+ expect(protocol_message.has_attach_publish_flag?).to eq(true)
19
+ expect(protocol_message.has_attach_subscribe_flag?).to eq(true)
20
+ expect(protocol_message.has_attach_presence_subscribe_flag?).to eq(true)
21
+
22
+ expect(protocol_message.has_attach_resume_flag?).to eq(false)
23
+ expect(protocol_message.has_attach_presence_flag?).to eq(false)
24
+ end
25
+ end
26
+
27
+ describe '#set_modes_from_flags' do
28
+ let(:subscribe_flag) { 262144 }
29
+
30
+ it 'converts flags to ChannelOptions#modes correctly' do
31
+ result = options.set_modes_from_flags(subscribe_flag)
32
+
33
+ expect(result).to eq(options.modes)
34
+ expect(options.modes.map(&:to_sym)).to eq(%i[subscribe])
35
+ end
36
+ end
37
+
38
+ describe '#set_params' do
39
+ let(:previous_params) { { example_attribute: 1 } }
40
+ let(:new_params) { { new_attribute: 1 } }
41
+ let(:params) { previous_params }
42
+
43
+ it 'should be able to overwrite attributes' do
44
+ expect { options.set_params(new_params) }.to \
45
+ change { options.params }.from(previous_params).to(new_params)
46
+ end
47
+
48
+ it 'should be able to make params empty' do # (1)
49
+ expect { options.set_params({}) }.to change { options.params }.from(previous_params).to({})
50
+ end
51
+ end
52
+ end
@@ -11,13 +11,8 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
11
11
  bundle exec rspec "${DIR}/unit"
12
12
  unit_status=$?
13
13
 
14
- if ruby -v | grep -e "1.9"; then
15
- # Output with test ID is not supported with this old version of RSpec
16
- # So it will be jumbled sadly for 1.9.*
17
- bundle exec parallel_rspec "${DIR}/acceptance"
18
- else
19
- bundle exec parallel_rspec "${DIR}/acceptance" --prefix-output-with-test-env-number
20
- fi
14
+ bundle exec parallel_rspec "${DIR}/acceptance" --prefix-output-with-test-env-number
15
+
21
16
  acceptance_status=$?
22
17
 
23
18
  if [ $unit_status -ne 0 ]; then
@@ -59,7 +59,7 @@ class TestApp
59
59
 
60
60
  url = "#{sandbox_client.endpoint}/apps/#{app_id}"
61
61
 
62
- basic_auth = Base64.encode64(api_key).chomp
62
+ basic_auth = Base64.urlsafe_encode64(api_key).chomp
63
63
  headers = { "Authorization" => "Basic #{basic_auth}" }
64
64
 
65
65
  Faraday.delete(url, nil, headers)
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Ably::Logger do
3
+ describe Ably::Logger, :prevent_log_stubbing do
4
4
  let(:rest_client) do
5
5
  instance_double('Ably::Rest::Client')
6
6
  end
@@ -18,14 +18,10 @@ describe Ably::Logger do
18
18
 
19
19
  context 'internals', :api_private do
20
20
  it 'delegates to the default Logger object' do
21
- received = false
22
21
  expect(subject.logger).to be_a(::Logger)
23
- allow_any_instance_of(::Logger).to receive(:warn) do |*args, &block|
24
- expect(args.concat([block ? block.call : nil]).join(',')).to match(/message/)
25
- received = true
26
- end
22
+ expect(subject.logger).to receive(:warn).with('message')
23
+
27
24
  subject.warn 'message'
28
- expect(received).to be_truthy
29
25
  end
30
26
 
31
27
  context 'formatter' do
@@ -136,18 +132,14 @@ describe Ably::Logger do
136
132
  end
137
133
 
138
134
  it 'delegates log messages to logger', :api_private do
139
- received = false
140
- allow(custom_logger_object).to receive(:fatal) do |*args, &block|
141
- expect(args.concat([block ? block.call : nil]).join(',')).to match(/message/)
142
- received = true
143
- end
135
+ expect(custom_logger_object).to receive(:fatal).with('message')
136
+
144
137
  subject.fatal 'message'
145
- expect(received).to be_truthy
146
138
  end
147
139
  end
148
140
  end
149
141
 
150
- context 'with blocks', :prevent_log_stubbing do
142
+ context 'with blocks' do
151
143
  it 'does not call the block unless the log level is met' do
152
144
  log_level_blocks = []
153
145
  subject.warn { log_level_blocks << :warn }
@@ -0,0 +1,14 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe Ably::Models::DeltaExtras do
5
+ subject { described_class.new({ format: 'vcdiff', from: '1234-4567-8910-1001-1111'}) }
6
+
7
+ it 'should have `from` attribute' do
8
+ expect(subject.from).to eq('1234-4567-8910-1001-1111')
9
+ end
10
+
11
+ it 'should have `format` attribute' do
12
+ expect(subject.format).to eq('vcdiff')
13
+ end
14
+ end
@@ -5,7 +5,7 @@ describe Ably::Models::ErrorInfo do
5
5
  subject { Ably::Models::ErrorInfo }
6
6
 
7
7
  context '#TI1, #TI4' do
8
- it_behaves_like 'a model', with_simple_attributes: %w(code status_code href message) do
8
+ it_behaves_like 'a model', with_simple_attributes: %w(code status_code href message request_id cause) do
9
9
  let(:model_args) { [] }
10
10
  end
11
11
  end
@@ -18,6 +18,22 @@ describe Ably::Models::ErrorInfo do
18
18
  end
19
19
  end
20
20
 
21
+ context '#request_id #RSC7c' do
22
+ subject { Ably::Models::ErrorInfo.new('request_id' => '123-456-789-001') }
23
+
24
+ it 'should return request ID' do
25
+ expect(subject.request_id).to eql('123-456-789-001')
26
+ end
27
+ end
28
+
29
+ context '#cause #TI1' do
30
+ subject { Ably::Models::ErrorInfo.new('cause' => Ably::Models::ErrorInfo.new({})) }
31
+
32
+ it 'should return cause attribute' do
33
+ expect(subject.cause).to be_kind_of(Ably::Models::ErrorInfo)
34
+ end
35
+ end
36
+
21
37
  context 'log entries container help link #TI5' do
22
38
  context 'without an error code' do
23
39
  subject { Ably::Models::ErrorInfo.new('statusCode' => 401) }
@@ -270,6 +270,20 @@ describe Ably::Models::Message do
270
270
  end
271
271
  end
272
272
 
273
+ describe '#protocol_message_index (#RTL21)' do
274
+ let(:messages) { [{ name: 'test1' }, { name: 'test2' }, { name: 'test3' }] }
275
+
276
+ let(:protocol_message) do
277
+ Ably::Models::ProtocolMessage.new({ action: 1 }.merge(messages: messages))
278
+ end
279
+
280
+ it 'should return correct protocol_message_index' do
281
+ expect(protocol_message.messages[0].protocol_message_index).to eq(0)
282
+ expect(protocol_message.messages[1].protocol_message_index).to eq(1)
283
+ expect(protocol_message.messages[2].protocol_message_index).to eq(2)
284
+ end
285
+ end
286
+
273
287
  context 'from REST request with embedded fields', :api_private do
274
288
  let(:id) { random_str }
275
289
  let(:protocol_message_id) { random_str }
@@ -606,4 +620,28 @@ describe Ably::Models::Message do
606
620
  end
607
621
  end
608
622
  end
623
+
624
+ context '#delta_extras (TM2i)' do
625
+ let(:delta_extras) { message.delta_extras }
626
+
627
+ context 'when delta' do
628
+ let(:message) { subject.new({ extras: { delta: { from: '1234-1234-5678-9009', format: 'vcdiff' } } }) }
629
+
630
+ it 'should return vcdiff format' do
631
+ expect(delta_extras.format).to eq('vcdiff')
632
+ end
633
+
634
+ it 'should return 1234-1234-5678-9009 message id' do
635
+ expect(delta_extras.from).to eq('1234-1234-5678-9009')
636
+ end
637
+ end
638
+
639
+ context 'when no delta' do
640
+ let(:message) { subject.new({ extras: {} }) }
641
+
642
+ it 'should return nil' do
643
+ expect(delta_extras).to eq(nil)
644
+ end
645
+ end
646
+ end
609
647
  end
@@ -10,9 +10,9 @@ describe Ably::Models::ProtocolMessage do
10
10
  subject.new({ action: 1 }.merge(options))
11
11
  end
12
12
 
13
- # TR4n, TR4b, TR4c, TR4d, TR4e
13
+ # TR4n, TR4b, TR4c, TR4d
14
14
  it_behaves_like 'a model',
15
- with_simple_attributes: %w(id channel channel_serial connection_id connection_key),
15
+ with_simple_attributes: %w(id channel channel_serial connection_id),
16
16
  base_model_options: { action: 1 } do
17
17
 
18
18
  let(:model_args) { [] }
@@ -176,6 +176,28 @@ describe Ably::Models::ProtocolMessage do
176
176
  end
177
177
  end
178
178
 
179
+ context 'when attach resumed flag' do
180
+ context 'flags is 34' do
181
+ let(:protocol_message) { new_protocol_message(flags: 34) }
182
+
183
+ it '#has_attach_resume_flag? is true' do
184
+ expect(protocol_message.has_attach_resume_flag?).to be_truthy
185
+ end
186
+
187
+ it '#has_attach_presence_flag? is false' do
188
+ expect(protocol_message.has_attach_presence_flag?).to be_falsey
189
+ end
190
+ end
191
+
192
+ context 'flags is 0' do
193
+ let(:protocol_message) { new_protocol_message(flags: 0) }
194
+
195
+ it 'should raise an exception if flags is a float number' do
196
+ expect(protocol_message.has_attach_resume_flag?).to be_falsy
197
+ end
198
+ end
199
+ end
200
+
179
201
  context 'when channel resumed and presence flags present' do
180
202
  let(:protocol_message) { new_protocol_message(flags: 5) }
181
203
 
@@ -201,6 +223,24 @@ describe Ably::Models::ProtocolMessage do
201
223
  end
202
224
  end
203
225
 
226
+ context '#params (#RTL4k1)' do
227
+ let(:params) do
228
+ { foo: :bar }
229
+ end
230
+
231
+ context 'when present' do
232
+ specify do
233
+ expect(new_protocol_message({ params: params }).params).to eq(params)
234
+ end
235
+ end
236
+
237
+ context 'when empty' do
238
+ specify do
239
+ expect(new_protocol_message({}).params).to eq({})
240
+ end
241
+ end
242
+ end
243
+
204
244
  context '#has_connection_serial?' do
205
245
  context 'without connection_serial' do
206
246
  let(:protocol_message) { new_protocol_message({}) }
@@ -308,6 +348,26 @@ describe Ably::Models::ProtocolMessage do
308
348
  end
309
349
  end
310
350
 
351
+ context '#messages (#RTL21)' do
352
+ let(:protocol_message) do
353
+ new_protocol_message(messages: [{ name: 'test1' }, { name: 'test2' }, { name: 'test3' }])
354
+ end
355
+
356
+ before do
357
+ message = Ably::Models::Message(name: 'test4')
358
+ message.assign_to_protocol_message(protocol_message)
359
+ protocol_message.add_message(message)
360
+ end
361
+
362
+ it 'contains Message objects in ascending order' do
363
+ expect(protocol_message.messages.count).to eql(4)
364
+ protocol_message.messages.each_with_index do |message, index|
365
+ expect(message.protocol_message_index).to eql(index)
366
+ expect(message.name).to include('test')
367
+ end
368
+ end
369
+ end
370
+
311
371
  context '#presence (#TR4l)' do
312
372
  let(:protocol_message) { new_protocol_message(presence: [{ action: 1, data: 'test' }]) }
313
373
 
@@ -417,41 +477,27 @@ describe Ably::Models::ProtocolMessage do
417
477
  end
418
478
  end
419
479
  end
420
-
421
- context '#connection_key (#TR4e)' do
422
- context 'existing only in #connection_details.connection_key' do
423
- let(:protocol_message) { new_protocol_message(connectionDetails: { connectionKey: 'key' }) }
424
-
425
- it 'is returned' do
426
- expect(protocol_message.connection_key).to eql('key')
427
- end
428
- end
429
-
430
- context 'existing in both #connection_key and #connection_details.connection_key' do
431
- let(:protocol_message) { new_protocol_message(connectionKey: 'deprecated', connectionDetails: { connectionKey: 'key' }) }
432
-
433
- it 'returns #connection_details.connection_key as #connection_key will be deprecated > 0.8' do
434
- expect(protocol_message.connection_key).to eql('key')
435
- end
436
- end
437
- end
438
480
  end
439
481
 
440
482
  context '#to_json', :api_private do
441
483
  let(:json_object) { JSON.parse(model.to_json) }
442
- let(:message) { { 'name' => 'event', 'clientId' => 'joe', 'timestamp' => as_since_epoch(Time.now) } }
484
+ let(:message1) { { 'name' => 'event1', 'clientId' => 'joe', 'timestamp' => as_since_epoch(Time.now) } }
485
+ let(:message2) { { 'name' => 'event2', 'clientId' => 'joe', 'timestamp' => as_since_epoch(Time.now) } }
486
+ let(:message3) { { 'name' => 'event3', 'clientId' => 'joe', 'timestamp' => as_since_epoch(Time.now) } }
443
487
  let(:attached_action) { Ably::Models::ProtocolMessage::ACTION.Attached }
444
488
  let(:message_action) { Ably::Models::ProtocolMessage::ACTION.Message }
445
489
 
446
490
  context 'with valid data' do
447
- let(:model) { new_protocol_message({ :action => attached_action, :channelSerial => 'unique', messages: [message] }) }
491
+ let(:model) { new_protocol_message({ :action => attached_action, :channelSerial => 'unique', messages: [message1, message2, message3] }) }
448
492
 
449
493
  it 'converts the attribute back to Java mixedCase notation using string keys' do
450
494
  expect(json_object["channelSerial"]).to eql('unique')
451
495
  end
452
496
 
453
497
  it 'populates the messages' do
454
- expect(json_object["messages"].first).to include(message)
498
+ expect(json_object["messages"][0]).to include(message1)
499
+ expect(json_object["messages"][1]).to include(message2)
500
+ expect(json_object["messages"][2]).to include(message3)
455
501
  end
456
502
  end
457
503
 
@@ -464,7 +510,7 @@ describe Ably::Models::ProtocolMessage do
464
510
  end
465
511
 
466
512
  context 'is aliased by #to_s' do
467
- let(:model) { new_protocol_message({ :action => attached_action, :channelSerial => 'unique', messages: [message], :timestamp => as_since_epoch(Time.now) }) }
513
+ let(:model) { new_protocol_message({ :action => attached_action, :channelSerial => 'unique', messages: [message1, message2, message3], :timestamp => as_since_epoch(Time.now) }) }
468
514
 
469
515
  specify do
470
516
  expect(json_object).to eql(JSON.parse("#{model}"))
@@ -473,14 +519,18 @@ describe Ably::Models::ProtocolMessage do
473
519
  end
474
520
 
475
521
  context '#to_msgpack', :api_private do
476
- let(:model) { new_protocol_message({ :connectionSerial => 'unique', messages: [message] }) }
477
- let(:message) { { 'name' => 'event', 'clientId' => 'joe', 'timestamp' => as_since_epoch(Time.now) } }
522
+ let(:model) { new_protocol_message({ :connectionSerial => 'unique', messages: [message1, message2, message3] }) }
523
+ let(:message1) { { 'name' => 'event1', 'clientId' => 'joe', 'timestamp' => as_since_epoch(Time.now) } }
524
+ let(:message2) { { 'name' => 'event2', 'clientId' => 'joe', 'timestamp' => as_since_epoch(Time.now) } }
525
+ let(:message3) { { 'name' => 'event3', 'clientId' => 'joe', 'timestamp' => as_since_epoch(Time.now) } }
478
526
  let(:packed) { model.to_msgpack }
479
527
  let(:unpacked) { MessagePack.unpack(packed) }
480
528
 
481
529
  it 'returns a unpackable msgpack object' do
482
530
  expect(unpacked['connectionSerial']).to eq('unique')
483
- expect(unpacked['messages'][0]['name']).to eq('event')
531
+ expect(unpacked['messages'][0]['name']).to eq('event1')
532
+ expect(unpacked['messages'][1]['name']).to eq('event2')
533
+ expect(unpacked['messages'][2]['name']).to eq('event3')
484
534
  end
485
535
  end
486
536
  end
@@ -80,6 +80,20 @@ describe Ably::Models::TokenDetails do
80
80
  expect(subject.expired?).to eql(false)
81
81
  end
82
82
  end
83
+
84
+ context 'with :from attribute' do
85
+ subject { Ably::Models::TokenDetails.new(expires: expire_time) }
86
+
87
+ let(:server_offset_time) { 2 * 60 * 60 } # 2 hours
88
+
89
+ it 'is false' do
90
+ expect(subject.expired?(from: (Time.now - server_offset_time))).to eql(false)
91
+ end
92
+
93
+ it 'is true' do
94
+ expect(subject.expired?(from: Time.now)).to eql(true)
95
+ end
96
+ end
83
97
  end
84
98
  end
85
99
 
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  require 'shared/protocol_msgbus_behaviour'
4
4
 
5
5
  describe Ably::Realtime::Channel do
6
- let(:client) { double('client').as_null_object }
6
+ let(:client) { Ably::Realtime::Client.new(token: 'valid') }
7
7
  let(:channel_name) { 'test' }
8
8
 
9
9
  subject do
@@ -71,6 +71,7 @@ describe Ably::Realtime::Channel do
71
71
  let(:message) { instance_double('Ably::Models::Message', client_id: nil, size: 0) }
72
72
 
73
73
  before do
74
+ allow(subject).to receive(:enqueue_messages_on_connection).and_return(message)
74
75
  allow(subject).to receive(:create_message).and_return(message)
75
76
  allow(subject).to receive(:attach).and_return(:true)
76
77
  end
@@ -3,39 +3,77 @@ require 'spec_helper'
3
3
 
4
4
  describe Ably::Realtime::Channels do
5
5
  let(:connection) { instance_double('Ably::Realtime::Connection', unsafe_on: true, on_resume: true) }
6
- let(:client) { instance_double('Ably::Realtime::Client', connection: connection, client_id: 'clientId') }
6
+ let(:client) do
7
+ instance_double('Ably::Realtime::Client', connection: connection, client_id: 'clientId', logger: double('logger').as_null_object)
8
+ end
7
9
  let(:channel_name) { 'unique' }
8
- let(:options) { { 'bizarre' => 'value' } }
10
+ let(:options) do
11
+ { params: { bizarre: 'value' } }
12
+ end
9
13
 
10
14
  subject { Ably::Realtime::Channels.new(client) }
11
15
 
12
16
  context 'creating channels' do
13
17
  context '#get' do
14
- it 'creates a channel if it does not exist' do
15
- expect(Ably::Realtime::Channel).to receive(:new).with(client, channel_name, options)
16
- subject.get(channel_name, options)
18
+ context "when channel doesn't exist" do
19
+ shared_examples 'creates a channel' do
20
+ it 'creates a channel (RTS3a)' do
21
+ expect(Ably::Realtime::Channel).to receive(:new).with(client, channel_name, channel_options)
22
+ subject.get(channel_name, channel_options)
23
+ end
24
+ end
25
+
26
+ describe 'hash' do
27
+ let(:channel_options) { options }
28
+ it { expect(channel_options).to be_a(Hash) }
29
+
30
+ include_examples 'creates a channel'
31
+ end
32
+
33
+ describe 'ChannelOptions object' do
34
+ let(:channel_options) { Ably::Models::ChannelOptions.new(options) }
35
+ it { expect(channel_options).to be_a(Ably::Models::ChannelOptions) }
36
+
37
+ include_examples 'creates a channel'
38
+ end
17
39
  end
18
40
 
19
41
  context 'when an existing channel exists' do
20
- it 'will reuse a channel object if it exists' do
21
- channel = subject.get(channel_name, options)
22
- expect(channel).to be_a(Ably::Realtime::Channel)
23
- expect(subject.get(channel_name, options).object_id).to eql(channel.object_id)
42
+ shared_examples 'reuse a channel object if it exists' do
43
+ it 'will reuse a channel object if it exists (RTS3a)' do
44
+ channel = subject.get(channel_name, channel_options)
45
+ expect(channel).to be_a(Ably::Realtime::Channel)
46
+ expect(subject.get(channel_name, channel_options).object_id).to eql(channel.object_id)
47
+ end
48
+ end
49
+
50
+ describe 'hash' do
51
+ let(:channel_options) { options }
52
+ it { expect(channel_options).to be_a(Hash) }
53
+
54
+ include_examples 'reuse a channel object if it exists'
55
+ end
56
+
57
+ describe 'ChannelOptions object' do
58
+ let(:channel_options) { Ably::Models::ChannelOptions.new(options) }
59
+ it { expect(channel_options).to be_a(Ably::Models::ChannelOptions) }
60
+
61
+ include_examples 'reuse a channel object if it exists'
24
62
  end
25
63
 
26
- it 'will update the options on the channel if provided' do
64
+ it 'will update the options on the channel if provided (RSN3c)' do
27
65
  channel = subject.get(channel_name, options)
28
- expect(channel.options).to eql(options)
29
- expect(channel.options).to_not include(:encrypted)
66
+ expect(channel.options.to_h).to eq(options)
67
+ expect(channel.options.to_h).to_not include(:encrypted)
30
68
  subject.get(channel_name, encrypted: true)
31
- expect(channel.options[:encrypted]).to eql(true)
69
+ expect(channel.options[:encrypted]).to eq(true)
32
70
  end
33
71
 
34
72
  it 'will leave the options intact on the channel if not provided' do
35
73
  channel = subject.get(channel_name, options)
36
- expect(channel.options).to eql(options)
74
+ expect(channel.options.to_h).to eq(options)
37
75
  subject.get(channel_name)
38
- expect(channel.options).to eql(options)
76
+ expect(channel.options.to_h).to eq(options)
39
77
  end
40
78
  end
41
79
  end