ably 1.1.8 → 1.2.2

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/check.yml +14 -5
  3. data/CHANGELOG.md +48 -0
  4. data/README.md +2 -2
  5. data/UPDATING.md +30 -0
  6. data/ably.gemspec +12 -24
  7. data/lib/ably/auth.rb +8 -8
  8. data/lib/ably/logger.rb +4 -4
  9. data/lib/ably/models/channel_details.rb +59 -0
  10. data/lib/ably/models/channel_metrics.rb +84 -0
  11. data/lib/ably/models/channel_occupancy.rb +43 -0
  12. data/lib/ably/models/channel_options.rb +97 -0
  13. data/lib/ably/models/channel_status.rb +53 -0
  14. data/lib/ably/models/device_details.rb +1 -1
  15. data/lib/ably/models/idiomatic_ruby_wrapper.rb +4 -0
  16. data/lib/ably/models/message.rb +4 -4
  17. data/lib/ably/models/protocol_message.rb +19 -7
  18. data/lib/ably/models/token_details.rb +7 -2
  19. data/lib/ably/models/token_request.rb +1 -1
  20. data/lib/ably/modules/ably.rb +1 -1
  21. data/lib/ably/modules/channels_collection.rb +22 -2
  22. data/lib/ably/modules/conversions.rb +34 -0
  23. data/lib/ably/realtime/auth.rb +2 -2
  24. data/lib/ably/realtime/channel/channel_manager.rb +16 -4
  25. data/lib/ably/realtime/channel/channel_state_machine.rb +5 -0
  26. data/lib/ably/realtime/channel.rb +54 -24
  27. data/lib/ably/realtime/channels.rb +1 -1
  28. data/lib/ably/rest/channel.rb +33 -34
  29. data/lib/ably/rest/client.rb +8 -5
  30. data/lib/ably/rest/middleware/encoder.rb +1 -1
  31. data/lib/ably/rest/middleware/exceptions.rb +1 -1
  32. data/lib/ably/rest/middleware/external_exceptions.rb +1 -1
  33. data/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +1 -1
  34. data/lib/ably/rest/middleware/logger.rb +1 -1
  35. data/lib/ably/rest/middleware/parse_json.rb +1 -1
  36. data/lib/ably/rest/middleware/parse_message_pack.rb +1 -1
  37. data/lib/ably/util/crypto.rb +1 -1
  38. data/lib/ably/version.rb +2 -2
  39. data/spec/acceptance/realtime/channel_spec.rb +247 -21
  40. data/spec/acceptance/realtime/channels_spec.rb +59 -7
  41. data/spec/acceptance/realtime/connection_spec.rb +21 -1
  42. data/spec/acceptance/realtime/message_spec.rb +77 -0
  43. data/spec/acceptance/rest/auth_spec.rb +18 -0
  44. data/spec/acceptance/rest/channel_spec.rb +19 -1
  45. data/spec/acceptance/rest/channels_spec.rb +22 -5
  46. data/spec/acceptance/rest/client_spec.rb +3 -3
  47. data/spec/acceptance/rest/message_spec.rb +61 -3
  48. data/spec/lib/unit/models/channel_options_spec.rb +52 -0
  49. data/spec/run_parallel_tests +2 -7
  50. data/spec/unit/logger_spec.rb +6 -14
  51. data/spec/unit/models/channel_details_spec.rb +30 -0
  52. data/spec/unit/models/channel_metrics_spec.rb +42 -0
  53. data/spec/unit/models/channel_occupancy_spec.rb +17 -0
  54. data/spec/unit/models/channel_status_spec.rb +36 -0
  55. data/spec/unit/models/message_spec.rb +14 -0
  56. data/spec/unit/models/protocol_message_spec.rb +53 -7
  57. data/spec/unit/models/token_details_spec.rb +14 -0
  58. data/spec/unit/realtime/channels_spec.rb +52 -14
  59. data/spec/unit/rest/channels_spec.rb +81 -14
  60. metadata +69 -11
@@ -5,7 +5,7 @@ module Ably
5
5
  module Middleware
6
6
  # HTTP exceptions raised due to a status code error on a 3rd party site
7
7
  # Used by auth calls
8
- class ExternalExceptions < Faraday::Response::Middleware
8
+ class ExternalExceptions < Faraday::Middleware
9
9
  def on_complete(env)
10
10
  if env.status >= 400
11
11
  error_status_code = env.status
@@ -4,7 +4,7 @@ require 'json'
4
4
  module Ably
5
5
  module Rest
6
6
  module Middleware
7
- class FailIfUnsupportedMimeType < Faraday::Response::Middleware
7
+ class FailIfUnsupportedMimeType < Faraday::Middleware
8
8
  def on_complete(env)
9
9
  unless env.response_headers['Ably-Middleware-Parsed'] == true
10
10
  # Ignore empty body with success status code for no body response
@@ -3,7 +3,7 @@ require 'faraday'
3
3
  module Ably
4
4
  module Rest
5
5
  module Middleware
6
- class Logger < Faraday::Response::Middleware
6
+ class Logger < Faraday::Middleware
7
7
  extend Forwardable
8
8
 
9
9
  def initialize(app, logger = nil)
@@ -4,7 +4,7 @@ require 'json'
4
4
  module Ably
5
5
  module Rest
6
6
  module Middleware
7
- class ParseJson < Faraday::Response::Middleware
7
+ class ParseJson < Faraday::Middleware
8
8
  def on_complete(env)
9
9
  if env.response_headers['Content-Type'] == 'application/json'
10
10
  env.body = parse(env.body) unless env.response_headers['Ably-Middleware-Parsed'] == true
@@ -4,7 +4,7 @@ require 'msgpack'
4
4
  module Ably
5
5
  module Rest
6
6
  module Middleware
7
- class ParseMessagePack < Faraday::Response::Middleware
7
+ class ParseMessagePack < Faraday::Middleware
8
8
  def on_complete(env)
9
9
  if env.response_headers['Content-Type'] == 'application/x-msgpack'
10
10
  env.body = parse(env.body) unless env.response_headers['Ably-Middleware-Parsed'] == true
@@ -30,7 +30,7 @@ module Ably::Util
30
30
  # crypto.decrypt(decrypted) # => 'secret text'
31
31
  #
32
32
  def initialize(params)
33
- @fixed_iv = params.delete(:fixed_iv) if params.kind_of?(Hash)
33
+ @fixed_iv = params[:fixed_iv]
34
34
  @cipher_params = Ably::Models::CipherParams(params)
35
35
  end
36
36
 
data/lib/ably/version.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Ably
2
- VERSION = '1.1.8'
3
- PROTOCOL_VERSION = '1.1'
2
+ VERSION = '1.2.2'
3
+ PROTOCOL_VERSION = '1.2'
4
4
 
5
5
  # @api private
6
6
  def self.major_minor_version_numeric
@@ -117,6 +117,94 @@ describe Ably::Realtime::Channel, :event_machine do
117
117
  end
118
118
  end
119
119
 
120
+ context 'context when channel options contain modes' do
121
+ before do
122
+ channel.options = { modes: %i[publish] }
123
+ end
124
+
125
+ it 'sends an ATTACH with options as flags (#RTL4l)' do
126
+ connection.once(:connected) do
127
+ client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
128
+ next if protocol_message.action != :attach
129
+
130
+ expect(protocol_message.has_attach_publish_flag?).to eq(true)
131
+ stop_reactor
132
+ end
133
+
134
+ channel.attach
135
+ end
136
+ end
137
+
138
+ context 'when channel is reattaching' do
139
+ it 'sends ATTACH_RESUME flag along with other modes (RTL4j)' do
140
+ channel.attach do
141
+ channel.on(:suspended) do
142
+ client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
143
+ next if protocol_message.action != :attach
144
+
145
+ expect(protocol_message.has_attach_resume_flag?).to eq(true)
146
+ expect(protocol_message.has_attach_publish_flag?).to eq(true)
147
+ stop_reactor
148
+ end
149
+
150
+ client.connection.connect
151
+ end
152
+ client.connection.transition_state_machine :suspended
153
+ end
154
+ end
155
+ end
156
+ end
157
+
158
+ context 'context when channel options contain params' do
159
+ let(:params) do
160
+ { foo: 'foo', bar: 'bar'}
161
+ end
162
+
163
+ before do
164
+ channel.options = { params: params }
165
+ end
166
+
167
+ it 'sends an ATTACH with params (#RTL4k)' do
168
+ connection.once(:connected) do
169
+ client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
170
+ next if protocol_message.action != :attach
171
+
172
+ expect(protocol_message.params).to eq(params)
173
+ stop_reactor
174
+ end
175
+
176
+ channel.attach
177
+ end
178
+ end
179
+ end
180
+
181
+ context 'when received attached' do
182
+ it 'decodes flags and sets it as modes on channel options (#RTL4m)'do
183
+ channel.on(:attached) do
184
+ expect(channel.options.modes.map(&:to_sym)).to eq(%i[subscribe])
185
+ stop_reactor
186
+ end
187
+
188
+ channel.transition_state_machine(:attaching)
189
+ attached_message = Ably::Models::ProtocolMessage.new(action: 11, channel: channel_name, flags: 262144)
190
+ client.connection.__incoming_protocol_msgbus__.publish :protocol_message, attached_message
191
+ end
192
+
193
+ it 'set params as channel options params (#RTL4k1)' do
194
+ params = { param: :something }
195
+
196
+ channel.on(:attached) do
197
+ expect(channel.params).to eq(channel.options.params)
198
+ expect(channel.params).to eq(params)
199
+ stop_reactor
200
+ end
201
+
202
+ channel.transition_state_machine(:attaching)
203
+ attached_message = Ably::Models::ProtocolMessage.new(action: 11, channel: channel_name, params: params)
204
+ client.connection.__incoming_protocol_msgbus__.publish :protocol_message, attached_message
205
+ end
206
+ end
207
+
120
208
  it 'implicitly attaches the channel (#RTL7c)' do
121
209
  expect(channel).to be_initialized
122
210
  channel.subscribe { |message| }
@@ -368,6 +456,42 @@ describe Ably::Realtime::Channel, :event_machine do
368
456
  end
369
457
  end
370
458
  end
459
+
460
+ describe 'clean attach (RTL4j)' do
461
+ context "when channel wasn't previously attached" do
462
+ it "doesn't send ATTACH_RESUME" do
463
+ client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
464
+ next if protocol_message.action != :attach
465
+
466
+ expect(protocol_message.has_attach_resume_flag?).to eq(false)
467
+ stop_reactor
468
+ end
469
+
470
+ channel.attach
471
+ end
472
+ end
473
+
474
+ context "when channel was explicitly detached" do
475
+ it "doesn't send ATTACH_RESUME" do
476
+ channel.once(:detached) do
477
+ client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
478
+ next if protocol_message.action != :attach
479
+
480
+ expect(protocol_message.has_attach_resume_flag?).to eq(false)
481
+ stop_reactor
482
+ end
483
+
484
+ channel.attach
485
+ end
486
+
487
+ channel.once(:attached) do
488
+ channel.detach
489
+ end
490
+
491
+ channel.attach
492
+ end
493
+ end
494
+ end
371
495
  end
372
496
 
373
497
  describe '#detach' do
@@ -1936,15 +2060,33 @@ describe Ably::Realtime::Channel, :event_machine do
1936
2060
  end
1937
2061
  end
1938
2062
 
1939
- it 'transitions state automatically to :attaching once the connection is re-established (#RTN15c3)' do
1940
- channel.attach do
1941
- channel.on(:suspended) do
1942
- client.connection.connect
1943
- channel.once(:attached) do
1944
- stop_reactor
2063
+ describe 'reattaching (#RTN15c3)' do
2064
+ it 'transitions state automatically to :attaching once the connection is re-established ' do
2065
+ channel.attach do
2066
+ channel.on(:suspended) do
2067
+ client.connection.connect
2068
+ channel.once(:attached) do
2069
+ stop_reactor
2070
+ end
1945
2071
  end
2072
+ client.connection.transition_state_machine :suspended
2073
+ end
2074
+ end
2075
+
2076
+ it 'sends ATTACH_RESUME flag when reattaching (RTL4j)' do
2077
+ channel.attach do
2078
+ channel.on(:suspended) do
2079
+ client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
2080
+ next if protocol_message.action != :attach
2081
+
2082
+ expect(protocol_message.has_attach_resume_flag?).to eq(true)
2083
+ stop_reactor
2084
+ end
2085
+
2086
+ client.connection.connect
2087
+ end
2088
+ client.connection.transition_state_machine :suspended
1946
2089
  end
1947
- client.connection.transition_state_machine :suspended
1948
2090
  end
1949
2091
  end
1950
2092
  end
@@ -2134,6 +2276,68 @@ describe Ably::Realtime::Channel, :event_machine do
2134
2276
  end
2135
2277
  end
2136
2278
 
2279
+ describe '#set_options (#RTL16a)' do
2280
+ let(:modes) { %i[subscribe] }
2281
+ let(:channel_options) do
2282
+ { modes: modes }
2283
+ end
2284
+
2285
+ def self.build_flags(flags)
2286
+ flags.map { |flag| Ably::Models::ProtocolMessage::ATTACH_FLAGS_MAPPING[flag] }.reduce(:|)
2287
+ end
2288
+
2289
+ shared_examples 'an update that sends ATTACH message' do |state, flags|
2290
+ it 'sends an ATTACH message on options change' do
2291
+ attach_sent = nil
2292
+
2293
+ client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
2294
+ if protocol_message.action == :attach && protocol_message.flags.nonzero?
2295
+ attach_sent = true
2296
+ expect(protocol_message.flags).to eq(flags)
2297
+ end
2298
+ end
2299
+
2300
+ channel.once(state) do
2301
+ channel.options = channel_options
2302
+ end
2303
+
2304
+ channel.on(:attached) do
2305
+ client.connection.__incoming_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
2306
+ next if protocol_message.action != :attached
2307
+
2308
+ expect(attach_sent).to eq(true)
2309
+ stop_reactor
2310
+ end
2311
+ end
2312
+
2313
+ channel.attach
2314
+ end
2315
+ end
2316
+
2317
+ context 'when channel is attaching' do
2318
+ it_behaves_like 'an update that sends ATTACH message', :attaching, build_flags(%i[subscribe])
2319
+ end
2320
+
2321
+ context 'when channel is attaching' do
2322
+ it_behaves_like 'an update that sends ATTACH message', :attached, build_flags(%i[resume subscribe])
2323
+ end
2324
+
2325
+ context 'when channel is initialized' do
2326
+ it "doesn't send ATTACH message" do
2327
+ client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
2328
+ raise "Unexpected message" if protocol_message.action == :attach
2329
+ end
2330
+
2331
+ channel.options = channel_options
2332
+ expect(channel.options.modes.map(&:to_sym)).to eq(modes)
2333
+
2334
+ EventMachine.next_tick do
2335
+ stop_reactor
2336
+ end
2337
+ end
2338
+ end
2339
+ end
2340
+
2137
2341
  context 'channel state change' do
2138
2342
  it 'emits a ChannelStateChange object' do
2139
2343
  channel.on(:attached) do |channel_state_change|
@@ -2375,11 +2579,20 @@ describe Ably::Realtime::Channel, :event_machine do
2375
2579
  end
2376
2580
 
2377
2581
  context 'and channel is attached' do
2378
- it 'reattaches immediately (#RTL13a)' do
2379
- channel.attach do
2582
+ it 'reattaches immediately (#RTL13a) with ATTACH_RESUME flag(RTL4j)' do
2583
+ resume_flag = false
2584
+
2585
+ channel.once(:attached) do
2586
+ client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
2587
+ next if protocol_message.action != :attach
2588
+
2589
+ resume_flag = protocol_message.has_attach_resume_flag?
2590
+ end
2591
+
2380
2592
  channel.once(:attaching) do |state_change|
2381
2593
  expect(state_change.reason.code).to eql(50505)
2382
2594
  channel.once(:attached) do
2595
+ expect(resume_flag).to eq(true)
2383
2596
  stop_reactor
2384
2597
  end
2385
2598
  end
@@ -2387,26 +2600,39 @@ describe Ably::Realtime::Channel, :event_machine do
2387
2600
  detach_message = Ably::Models::ProtocolMessage.new(action: detached_action, channel: channel_name, error: { code: 50505 })
2388
2601
  client.connection.__incoming_protocol_msgbus__.publish :protocol_message, detach_message
2389
2602
  end
2603
+
2604
+ channel.attach
2390
2605
  end
2391
2606
  end
2392
2607
 
2393
2608
  context 'and channel is suspended' do
2394
- it 'reattaches immediately (#RTL13a)' do
2395
- channel.attach do
2396
- channel.once(:suspended) do
2397
- channel.once(:attaching) do |state_change|
2398
- expect(state_change.reason.code).to eql(50505)
2399
- channel.once(:attached) do
2400
- stop_reactor
2401
- end
2402
- end
2609
+ it 'reattaches immediately (#RTL13a) with ATTACH_RESUME flag(RTL4j)' do
2610
+ resume_flag = false
2403
2611
 
2404
- detach_message = Ably::Models::ProtocolMessage.new(action: detached_action, channel: channel_name, error: { code: 50505 })
2405
- client.connection.__incoming_protocol_msgbus__.publish :protocol_message, detach_message
2612
+ channel.once(:attached) do
2613
+ channel.transition_state_machine! :suspended
2614
+ end
2615
+
2616
+ channel.once(:suspended) do
2617
+ client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
2618
+ next if protocol_message.action != :attach
2619
+
2620
+ resume_flag = protocol_message.has_attach_resume_flag?
2406
2621
  end
2407
2622
 
2408
- channel.transition_state_machine! :suspended
2623
+ channel.once(:attaching) do |state_change|
2624
+ expect(state_change.reason.code).to eql(50505)
2625
+ channel.once(:attached) do
2626
+ expect(resume_flag).to eq(true)
2627
+ stop_reactor
2628
+ end
2629
+ end
2630
+
2631
+ detach_message = Ably::Models::ProtocolMessage.new(action: detached_action, channel: channel_name, error: { code: 50505 })
2632
+ client.connection.__incoming_protocol_msgbus__.publish :protocol_message, detach_message
2409
2633
  end
2634
+
2635
+ channel.attach
2410
2636
  end
2411
2637
 
2412
2638
  context 'when connection is no longer connected' do
@@ -10,17 +10,56 @@ describe Ably::Realtime::Channels, :event_machine do
10
10
  end
11
11
 
12
12
  it 'returns channel object and passes the provided options' do
13
- expect(channel_with_options.options).to eql(options)
13
+ expect(channel_options).to be_a(Ably::Models::ChannelOptions)
14
+ expect(channel_options.to_h).to eq(options)
14
15
  stop_reactor
15
16
  end
16
17
  end
17
18
 
18
19
  vary_by_protocol do
20
+ let(:client_options) do
21
+ { key: api_key, environment: environment, protocol: protocol }
22
+ end
19
23
  let(:client) do
20
- auto_close Ably::Realtime::Client.new(key: api_key, environment: environment, protocol: protocol)
24
+ auto_close Ably::Realtime::Client.new(client_options)
21
25
  end
22
26
  let(:channel_name) { random_str }
23
- let(:options) { { key: 'value' } }
27
+ let(:options) do
28
+ { params: { key: 'value' } }
29
+ end
30
+
31
+ subject(:channel_options) { channel_with_options.options }
32
+
33
+ context 'when channel supposed to trigger reattachment per RTL16a (#RTS3c1)' do
34
+ it 'will raise an error' do
35
+ channel = client.channels.get(channel_name, options)
36
+
37
+ channel.on(:attached) do
38
+ expect { client.channels.get(channel_name, { modes: [] }) }.to raise_error ArgumentError, /use Channel#set_options directly/
39
+ stop_reactor
40
+ end
41
+
42
+ channel.attach
43
+ end
44
+
45
+ context 'params keys are the same but values are different' do
46
+ let(:options) do
47
+ { params: { x: '1' } }
48
+ end
49
+
50
+ it 'will raise an error' do
51
+ channel = client.channels.get(channel_name, options)
52
+
53
+ channel.on(:attached) do
54
+ expect { client.channels.get(channel_name, { params: { x: '2' } }) }.to raise_error ArgumentError, /use Channel#set_options directly/
55
+
56
+ stop_reactor
57
+ end
58
+
59
+ channel.attach
60
+ end
61
+ end
62
+ end
24
63
 
25
64
  describe 'using shortcut method #channel on the client object' do
26
65
  let(:channel) { client.channel(channel_name) }
@@ -35,26 +74,39 @@ describe Ably::Realtime::Channels, :event_machine do
35
74
  end
36
75
 
37
76
  describe 'accessing an existing channel object with different options' do
77
+ let(:client_options) { super().merge(logger: custom_logger_object) }
78
+ let(:custom_logger_object) { TestLogger.new }
38
79
  let(:new_channel_options) { { encrypted: true } }
39
- let(:original_channel) { client.channels.get(channel_name, options) }
80
+ let!(:original_channel) { client.channels.get(channel_name, options) }
40
81
 
41
82
  it 'overrides the existing channel options and returns the channel object' do
42
- expect(original_channel.options).to_not include(:encrypted)
83
+ expect(original_channel.options.to_h).to_not include(:encrypted)
43
84
  new_channel = client.channels.get(channel_name, new_channel_options)
44
85
  expect(new_channel).to be_a(Ably::Realtime::Channel)
45
86
  expect(new_channel.options[:encrypted]).to eql(true)
46
87
  stop_reactor
47
88
  end
89
+
90
+ it 'shows deprecation warning' do
91
+ client.channels.get(channel_name, new_channel_options)
92
+
93
+ warning = custom_logger_object.logs.find do |severity, message|
94
+ message.match(/Using this method to update channel options is deprecated and may be removed/)
95
+ end
96
+
97
+ expect(warning).to_not be_nil
98
+ stop_reactor
99
+ end
48
100
  end
49
101
 
50
102
  describe 'accessing an existing channel object without specifying any channel options' do
51
103
  let(:original_channel) { client.channels.get(channel_name, options) }
52
104
 
53
105
  it 'returns the existing channel without modifying the channel options' do
54
- expect(original_channel.options).to eql(options)
106
+ expect(original_channel.options.to_h).to eq(options)
55
107
  new_channel = client.channels.get(channel_name)
56
108
  expect(new_channel).to be_a(Ably::Realtime::Channel)
57
- expect(original_channel.options).to eql(options)
109
+ expect(original_channel.options.to_h).to eq(options)
58
110
  stop_reactor
59
111
  end
60
112
  end
@@ -482,6 +482,26 @@ describe Ably::Realtime::Connection, :event_machine do
482
482
  end
483
483
  end
484
484
 
485
+ context "when can't connect to host" do
486
+ let(:client_options) { super().merge(realtime_host: 'non-existent.ably.io') }
487
+
488
+ it 'logs error on failed connection attempt' do
489
+ logger_expectation = lambda do |*args, &block|
490
+ error_message = "Connection to non-existent.ably.io:443 failed"
491
+ expect(args.concat([block ? block.call : nil]).join(',')).to include(error_message)
492
+ stop_reactor
493
+ end
494
+
495
+ expect(connection.logger).to receive(:warn, &logger_expectation).at_least(:once)
496
+
497
+ connection.on(:connected) do
498
+ raise "Connection should not succeed"
499
+ end
500
+
501
+ connection.connect
502
+ end
503
+ end
504
+
485
505
  context 'when explicitly reconnecting disconnected/suspended connection in retry (#RTN11c)' do
486
506
  let(:close_connection_proc) do
487
507
  lambda do
@@ -2083,7 +2103,7 @@ describe Ably::Realtime::Connection, :event_machine do
2083
2103
  it 'sends the protocol version param v (#G4, #RTN2f)' do
2084
2104
  expect(EventMachine).to receive(:connect) do |host, port, transport, object, url|
2085
2105
  uri = URI.parse(url)
2086
- expect(CGI::parse(uri.query)['v'][0]).to eql('1.1')
2106
+ expect(CGI::parse(uri.query)['v'][0]).to eql('1.2')
2087
2107
  stop_reactor
2088
2108
  end
2089
2109
  client
@@ -75,6 +75,83 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
75
75
  end
76
76
  end
77
77
 
78
+ context 'a single Message object (#RSL1a)' do
79
+ let(:name) { random_str }
80
+ let(:data) { random_str }
81
+ let(:message) { Ably::Models::Message.new(name: name, data: data) }
82
+
83
+ it 'publishes the message' do
84
+ channel.attach
85
+ channel.publish(message)
86
+ channel.subscribe do |msg|
87
+ expect(msg.name).to eq(message.name)
88
+ expect(msg.data).to eq(message.data)
89
+ stop_reactor
90
+ end
91
+ end
92
+ end
93
+
94
+ context 'an array of Message objects (#RSL1a)' do
95
+ let(:data) { random_str }
96
+ let(:message1) { Ably::Models::Message.new(name: random_str, data: data) }
97
+ let(:message2) { Ably::Models::Message.new(name: random_str, data: data) }
98
+ let(:message3) { Ably::Models::Message.new(name: random_str, data: data) }
99
+
100
+ it 'publishes three messages' do
101
+ channel.attach
102
+ channel.publish([message1, message2, message3])
103
+ counter = 0
104
+ channel.subscribe do |message|
105
+ counter += 1
106
+ expect(message.data).to eq(data)
107
+ expect(message.name).to eq(message1.name) if counter == 1
108
+ expect(message.name).to eq(message2.name) if counter == 2
109
+ if counter == 3
110
+ expect(message.name).to eq(message3.name)
111
+ stop_reactor
112
+ end
113
+ end
114
+ end
115
+ end
116
+
117
+ context 'an array of hashes (#RSL1a)' do
118
+ let(:data) { random_str }
119
+ let(:message1) { { name: random_str, data: data } }
120
+ let(:message2) { { name: random_str, data: data } }
121
+ let(:message3) { { name: random_str, data: data } }
122
+
123
+ it 'publishes three messages' do
124
+ channel.attach
125
+ channel.publish([message1, message2, message3])
126
+ counter = 0
127
+ channel.subscribe do |message|
128
+ counter += 1
129
+ expect(message.data).to eq(data)
130
+ expect(message.name).to eq(message1[:name]) if counter == 1
131
+ expect(message.name).to eq(message2[:name]) if counter == 2
132
+ if counter == 3
133
+ expect(message.name).to eq(message3[:name])
134
+ stop_reactor
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ context 'a name with data payload (#RSL1a, #RSL1b)' do
141
+ let(:name) { random_str }
142
+ let(:data) { random_str }
143
+
144
+ it 'publishes a message' do
145
+ channel.attach
146
+ channel.publish(name, data)
147
+ channel.subscribe do |message|
148
+ expect(message.name).to eql(name)
149
+ expect(message.data).to eq(data)
150
+ stop_reactor
151
+ end
152
+ end
153
+ end
154
+
78
155
  context 'with supported extra payload content type (#RTL6h, #RSL6a2)' do
79
156
  let(:channel) { client.channel("pushenabled:#{random_str}") }
80
157
 
@@ -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)
@@ -595,5 +595,23 @@ describe Ably::Rest::Channel do
595
595
  expect(channel.presence).to be_a(Ably::Rest::Presence)
596
596
  end
597
597
  end
598
+
599
+ context '#status' do
600
+ let(:channel_name) { "persisted:#{random_str(4)}" }
601
+ let(:channel) { client.channel(channel_name) }
602
+ let(:channel_details) { channel.status }
603
+
604
+ it 'should return channel details status (#RSL8, #RSL8a)' do
605
+ expect(channel_details.channel_id).to eq(channel_name)
606
+ expect(channel_details.name).to eq(channel_name)
607
+ expect(channel_details.status).to be_a(Ably::Models::ChannelStatus)
608
+ expect(channel_details.status.is_active).to eq(true)
609
+ expect(channel_details.status.occupancy.metrics.publishers).to eq(0)
610
+ expect(channel_details.status.occupancy.metrics.subscribers).to eq(0)
611
+ expect(channel_details.status.occupancy.metrics.presence_connections).to eq(0)
612
+ expect(channel_details.status.occupancy.metrics.presence_members).to eq(0)
613
+ expect(channel_details.status.occupancy.metrics.presence_subscribers).to eq(0)
614
+ end
615
+ end
598
616
  end
599
617
  end