ably 1.1.8 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -0
  3. data/README.md +1 -1
  4. data/UPDATING.md +30 -0
  5. data/lib/ably/auth.rb +3 -3
  6. data/lib/ably/models/channel_options.rb +97 -0
  7. data/lib/ably/models/idiomatic_ruby_wrapper.rb +4 -0
  8. data/lib/ably/models/message.rb +4 -4
  9. data/lib/ably/models/protocol_message.rb +17 -5
  10. data/lib/ably/models/token_details.rb +7 -2
  11. data/lib/ably/modules/channels_collection.rb +22 -2
  12. data/lib/ably/modules/conversions.rb +34 -0
  13. data/lib/ably/realtime/channel/channel_manager.rb +16 -4
  14. data/lib/ably/realtime/channel/channel_state_machine.rb +5 -0
  15. data/lib/ably/realtime/channel.rb +54 -24
  16. data/lib/ably/realtime/channels.rb +1 -1
  17. data/lib/ably/rest/channel.rb +26 -34
  18. data/lib/ably/rest/client.rb +4 -1
  19. data/lib/ably/util/crypto.rb +1 -1
  20. data/lib/ably/version.rb +2 -2
  21. data/spec/acceptance/realtime/channel_spec.rb +247 -21
  22. data/spec/acceptance/realtime/channels_spec.rb +59 -7
  23. data/spec/acceptance/realtime/connection_spec.rb +1 -1
  24. data/spec/acceptance/realtime/message_spec.rb +77 -0
  25. data/spec/acceptance/rest/auth_spec.rb +18 -0
  26. data/spec/acceptance/rest/channel_spec.rb +1 -1
  27. data/spec/acceptance/rest/channels_spec.rb +22 -5
  28. data/spec/acceptance/rest/client_spec.rb +2 -2
  29. data/spec/acceptance/rest/message_spec.rb +61 -3
  30. data/spec/lib/unit/models/channel_options_spec.rb +52 -0
  31. data/spec/unit/models/message_spec.rb +14 -0
  32. data/spec/unit/models/protocol_message_spec.rb +53 -7
  33. data/spec/unit/models/token_details_spec.rb +14 -0
  34. data/spec/unit/realtime/channels_spec.rb +52 -14
  35. data/spec/unit/rest/channels_spec.rb +81 -14
  36. metadata +21 -3
@@ -1165,6 +1165,24 @@ describe Ably::Auth do
1165
1165
  end
1166
1166
  end
1167
1167
 
1168
+ context 'when token does not expire' do
1169
+ let(:client_options) { default_options.merge(use_token_auth: true, key: api_key, query_time: true) }
1170
+ let(:channel) { client.channels.get(random_str) }
1171
+
1172
+ context 'for the next 2 hours' do
1173
+ let(:local_time) { Time.now - 2 * 60 * 60 }
1174
+
1175
+ before { allow(Time).to receive(:now).and_return(local_time) }
1176
+
1177
+ it 'should not request for the new token (#RSA4b1)' do
1178
+ expect { channel.publish 'event' }.to change { auth.current_token_details }
1179
+ expect do
1180
+ expect { channel.publish 'event' }.not_to change { auth.current_token_details }
1181
+ end.not_to change { auth.current_token_details.expires }
1182
+ end
1183
+ end
1184
+ end
1185
+
1168
1186
  context 'when :client_id is provided in a token' do
1169
1187
  let(:client_id) { '123' }
1170
1188
  let(:token) do
@@ -5,7 +5,7 @@ describe Ably::Rest::Channel do
5
5
  include Ably::Modules::Conversions
6
6
 
7
7
  vary_by_protocol do
8
- let(:default_options) { { key: api_key, environment: environment, protocol: protocol, max_frame_size: max_frame_size, max_message_size: max_message_size } }
8
+ let(:default_options) { { key: api_key, environment: environment, protocol: protocol, max_frame_size: max_frame_size, max_message_size: max_message_size, idempotent_rest_publishing: false } }
9
9
  let(:client_options) { default_options }
10
10
  let(:client) do
11
11
  Ably::Rest::Client.new(client_options)
@@ -5,11 +5,11 @@ describe Ably::Rest::Channels do
5
5
  shared_examples 'a channel' do
6
6
  it 'returns a channel object' do
7
7
  expect(channel).to be_a Ably::Rest::Channel
8
- expect(channel.name).to eql(channel_name)
8
+ expect(channel.name).to eq(channel_name)
9
9
  end
10
10
 
11
11
  it 'returns channel object and passes the provided options' do
12
- expect(channel_with_options.options).to eql(options)
12
+ expect(channel_with_options.options.to_h).to eq(options)
13
13
  end
14
14
  end
15
15
 
@@ -32,12 +32,29 @@ describe Ably::Rest::Channels do
32
32
  it_behaves_like 'a channel'
33
33
  end
34
34
 
35
+ describe '#set_options (#RTL16)' do
36
+ let(:channel) { client.channel(channel_name) }
37
+
38
+ it "updates channel's options" do
39
+ expect { channel.options = options }.to change { channel.options.to_h }.from({}).to(options)
40
+ end
41
+
42
+ context 'when providing Ably::Models::ChannelOptions object' do
43
+ let(:options_object) { Ably::Models::ChannelOptions.new(options) }
44
+
45
+ it "updates channel's options" do
46
+ expect { channel.options = options_object}.to change { channel.options.to_h }.from({}).to(options)
47
+ end
48
+ end
49
+ end
50
+
35
51
  describe 'accessing an existing channel object with different options' do
36
52
  let(:new_channel_options) { { encrypted: true } }
37
53
  let(:original_channel) { client.channels.get(channel_name, options) }
38
54
 
39
55
  it 'overrides the existing channel options and returns the channel object (RSN3c)' do
40
- expect(original_channel.options).to_not include(:encrypted)
56
+ expect(original_channel.options.to_h).to_not include(:encrypted)
57
+
41
58
  new_channel = client.channels.get(channel_name, new_channel_options)
42
59
  expect(new_channel).to be_a(Ably::Rest::Channel)
43
60
  expect(new_channel.options[:encrypted]).to eql(true)
@@ -48,10 +65,10 @@ describe Ably::Rest::Channels do
48
65
  let(:original_channel) { client.channels.get(channel_name, options) }
49
66
 
50
67
  it 'returns the existing channel without modifying the channel options' do
51
- expect(original_channel.options).to eql(options)
68
+ expect(original_channel.options.to_h).to eq(options)
52
69
  new_channel = client.channels.get(channel_name)
53
70
  expect(new_channel).to be_a(Ably::Rest::Channel)
54
- expect(original_channel.options).to eql(options)
71
+ expect(original_channel.options.to_h).to eq(options)
55
72
  end
56
73
  end
57
74
 
@@ -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
@@ -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 }
@@ -223,6 +223,24 @@ describe Ably::Models::ProtocolMessage do
223
223
  end
224
224
  end
225
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
+
226
244
  context '#has_connection_serial?' do
227
245
  context 'without connection_serial' do
228
246
  let(:protocol_message) { new_protocol_message({}) }
@@ -330,6 +348,26 @@ describe Ably::Models::ProtocolMessage do
330
348
  end
331
349
  end
332
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
+
333
371
  context '#presence (#TR4l)' do
334
372
  let(:protocol_message) { new_protocol_message(presence: [{ action: 1, data: 'test' }]) }
335
373
 
@@ -443,19 +481,23 @@ describe Ably::Models::ProtocolMessage do
443
481
 
444
482
  context '#to_json', :api_private do
445
483
  let(:json_object) { JSON.parse(model.to_json) }
446
- 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) } }
447
487
  let(:attached_action) { Ably::Models::ProtocolMessage::ACTION.Attached }
448
488
  let(:message_action) { Ably::Models::ProtocolMessage::ACTION.Message }
449
489
 
450
490
  context 'with valid data' do
451
- 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] }) }
452
492
 
453
493
  it 'converts the attribute back to Java mixedCase notation using string keys' do
454
494
  expect(json_object["channelSerial"]).to eql('unique')
455
495
  end
456
496
 
457
497
  it 'populates the messages' do
458
- 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)
459
501
  end
460
502
  end
461
503
 
@@ -468,7 +510,7 @@ describe Ably::Models::ProtocolMessage do
468
510
  end
469
511
 
470
512
  context 'is aliased by #to_s' do
471
- 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) }) }
472
514
 
473
515
  specify do
474
516
  expect(json_object).to eql(JSON.parse("#{model}"))
@@ -477,14 +519,18 @@ describe Ably::Models::ProtocolMessage do
477
519
  end
478
520
 
479
521
  context '#to_msgpack', :api_private do
480
- let(:model) { new_protocol_message({ :connectionSerial => 'unique', messages: [message] }) }
481
- 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) } }
482
526
  let(:packed) { model.to_msgpack }
483
527
  let(:unpacked) { MessagePack.unpack(packed) }
484
528
 
485
529
  it 'returns a unpackable msgpack object' do
486
530
  expect(unpacked['connectionSerial']).to eq('unique')
487
- 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')
488
534
  end
489
535
  end
490
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,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 (RSN3a)' 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 (RSN3a)' 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
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
@@ -2,30 +2,97 @@
2
2
  require 'spec_helper'
3
3
 
4
4
  describe Ably::Rest::Channels do
5
- let(:client) { instance_double('Ably::Rest::Client') }
5
+ let(:client) { instance_double('Ably::Rest::Client', logger: double('logger').as_null_object) }
6
6
  let(:channel_name) { 'unique'.encode(Encoding::UTF_8) }
7
- let(:options) { { 'bizarre' => 'value' } }
7
+ let(:options) do
8
+ { params: { 'bizarre' => 'value' } }
9
+ end
8
10
 
9
11
  subject { Ably::Rest::Channels.new(client) }
10
12
 
11
- context 'creating channels' do
12
- it '#get creates a channel' do
13
- expect(Ably::Rest::Channel).to receive(:new).with(client, channel_name, options)
14
- subject.get(channel_name, options)
15
- end
13
+ describe '#get' do
14
+ context "when channel doesn't exist" do
15
+ shared_examples 'creates a channel' do
16
+ it 'creates a channel (RSN3a)' do
17
+ expect(Ably::Rest::Channel).to receive(:new).with(client, channel_name, options)
18
+ subject.get(channel_name, options)
19
+ end
20
+ end
16
21
 
17
- it '#get will reuse the channel object' do
18
- channel = subject.get(channel_name, options)
19
- expect(channel).to be_a(Ably::Rest::Channel)
20
- expect(subject.get(channel_name, options).object_id).to eql(channel.object_id)
22
+ describe 'hash' do
23
+ let(:channel_options) { options }
24
+ it { expect(channel_options).to be_a(Hash) }
25
+
26
+ include_examples 'creates a channel'
27
+ end
28
+
29
+ describe 'ChannelOptions object' do
30
+ let(:channel_options) { Ably::Models::ChannelOptions.new(options) }
31
+ it { expect(channel_options).to be_a(Ably::Models::ChannelOptions) }
32
+
33
+ include_examples 'creates a channel'
34
+ end
21
35
  end
22
36
 
23
- it '[] creates a channel' do
24
- expect(Ably::Rest::Channel).to receive(:new).with(client, channel_name, options)
25
- subject.get(channel_name, options)
37
+ context 'when an existing channel exists' do
38
+ shared_examples 'reuse a channel object if it exists' do
39
+ it 'will reuse a channel object if it exists (RSN3a)' do
40
+ channel = subject.get(channel_name, channel_options)
41
+ expect(channel).to be_a(Ably::Rest::Channel)
42
+ expect(subject.get(channel_name, channel_options).object_id).to eql(channel.object_id)
43
+ end
44
+ end
45
+
46
+ describe 'hash' do
47
+ let(:channel_options) { options }
48
+ it { expect(channel_options).to be_a(Hash) }
49
+
50
+ include_examples 'reuse a channel object if it exists'
51
+ end
52
+
53
+ describe 'ChannelOptions object' do
54
+ let(:channel_options) { Ably::Models::ChannelOptions.new(options) }
55
+ it { expect(channel_options).to be_a(Ably::Models::ChannelOptions) }
56
+
57
+ include_examples 'reuse a channel object if it exists'
58
+ end
59
+
60
+ context 'with new channel_options modes' do
61
+ shared_examples 'update channel with provided options :modes' do
62
+ it 'will update channel with provided options modes (RSN3c)' do
63
+ channel = subject.get(channel_name, channel_options)
64
+ expect(channel.options.modes).to eq(modes)
65
+
66
+ subject.get(channel_name, channel_options)
67
+ expect(channel.options.modes).to eq(modes)
68
+ end
69
+ end
70
+
71
+ let(:modes) { %i[subscribe] }
72
+ let(:new_options) { { modes: modes } }
73
+
74
+ describe 'hash' do
75
+ let(:channel_options) { new_options }
76
+ it { expect(channel_options).to be_a(Hash) }
77
+
78
+ include_examples 'update channel with provided options :modes'
79
+ end
80
+
81
+ describe 'ChannelOptions object' do
82
+ let(:channel_options) { Ably::Models::ChannelOptions.new(new_options) }
83
+ it { expect(channel_options).to be_a(Ably::Models::ChannelOptions) }
84
+
85
+ include_examples 'update channel with provided options :modes'
86
+ end
87
+ end
26
88
  end
27
89
  end
28
90
 
91
+ it '[] creates a channel' do
92
+ expect(Ably::Rest::Channel).to receive(:new).with(client, channel_name, options)
93
+ subject.get(channel_name, options)
94
+ end
95
+
29
96
  context '#fetch' do
30
97
  it 'retrieves a channel if it exists' do
31
98
  channel = subject.get(channel_name, options)