ably 0.8.15 → 1.0.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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +6 -4
  3. data/CHANGELOG.md +6 -2
  4. data/README.md +5 -1
  5. data/SPEC.md +1473 -852
  6. data/ably.gemspec +11 -8
  7. data/lib/ably/auth.rb +90 -53
  8. data/lib/ably/exceptions.rb +37 -8
  9. data/lib/ably/logger.rb +10 -1
  10. data/lib/ably/models/auth_details.rb +42 -0
  11. data/lib/ably/models/channel_state_change.rb +18 -4
  12. data/lib/ably/models/connection_details.rb +6 -3
  13. data/lib/ably/models/connection_state_change.rb +4 -3
  14. data/lib/ably/models/error_info.rb +1 -1
  15. data/lib/ably/models/message.rb +17 -1
  16. data/lib/ably/models/message_encoders/base.rb +103 -82
  17. data/lib/ably/models/message_encoders/base64.rb +1 -1
  18. data/lib/ably/models/presence_message.rb +16 -1
  19. data/lib/ably/models/protocol_message.rb +20 -3
  20. data/lib/ably/models/token_details.rb +11 -1
  21. data/lib/ably/models/token_request.rb +16 -6
  22. data/lib/ably/modules/async_wrapper.rb +7 -3
  23. data/lib/ably/modules/encodeable.rb +51 -12
  24. data/lib/ably/modules/enum.rb +17 -7
  25. data/lib/ably/modules/event_emitter.rb +29 -14
  26. data/lib/ably/modules/model_common.rb +13 -21
  27. data/lib/ably/modules/state_emitter.rb +7 -4
  28. data/lib/ably/modules/state_machine.rb +2 -4
  29. data/lib/ably/modules/uses_state_machine.rb +7 -3
  30. data/lib/ably/realtime.rb +2 -0
  31. data/lib/ably/realtime/auth.rb +102 -42
  32. data/lib/ably/realtime/channel.rb +68 -26
  33. data/lib/ably/realtime/channel/channel_manager.rb +154 -65
  34. data/lib/ably/realtime/channel/channel_state_machine.rb +14 -15
  35. data/lib/ably/realtime/client.rb +18 -3
  36. data/lib/ably/realtime/client/incoming_message_dispatcher.rb +38 -29
  37. data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +6 -1
  38. data/lib/ably/realtime/connection.rb +108 -49
  39. data/lib/ably/realtime/connection/connection_manager.rb +167 -61
  40. data/lib/ably/realtime/connection/connection_state_machine.rb +22 -3
  41. data/lib/ably/realtime/connection/websocket_transport.rb +19 -10
  42. data/lib/ably/realtime/presence.rb +70 -45
  43. data/lib/ably/realtime/presence/members_map.rb +201 -36
  44. data/lib/ably/realtime/presence/presence_manager.rb +30 -6
  45. data/lib/ably/realtime/presence/presence_state_machine.rb +5 -12
  46. data/lib/ably/rest.rb +2 -2
  47. data/lib/ably/rest/channel.rb +5 -5
  48. data/lib/ably/rest/client.rb +31 -27
  49. data/lib/ably/rest/middleware/exceptions.rb +1 -3
  50. data/lib/ably/rest/middleware/logger.rb +2 -2
  51. data/lib/ably/rest/presence.rb +2 -2
  52. data/lib/ably/util/pub_sub.rb +1 -1
  53. data/lib/ably/util/safe_deferrable.rb +26 -0
  54. data/lib/ably/version.rb +2 -2
  55. data/spec/acceptance/realtime/auth_spec.rb +470 -111
  56. data/spec/acceptance/realtime/channel_history_spec.rb +5 -3
  57. data/spec/acceptance/realtime/channel_spec.rb +1017 -168
  58. data/spec/acceptance/realtime/client_spec.rb +6 -6
  59. data/spec/acceptance/realtime/connection_failures_spec.rb +458 -27
  60. data/spec/acceptance/realtime/connection_spec.rb +424 -105
  61. data/spec/acceptance/realtime/message_spec.rb +52 -23
  62. data/spec/acceptance/realtime/presence_history_spec.rb +5 -3
  63. data/spec/acceptance/realtime/presence_spec.rb +1110 -96
  64. data/spec/acceptance/rest/auth_spec.rb +222 -59
  65. data/spec/acceptance/rest/base_spec.rb +1 -1
  66. data/spec/acceptance/rest/channel_spec.rb +1 -2
  67. data/spec/acceptance/rest/client_spec.rb +104 -48
  68. data/spec/acceptance/rest/message_spec.rb +42 -15
  69. data/spec/acceptance/rest/presence_spec.rb +4 -11
  70. data/spec/rspec_config.rb +2 -1
  71. data/spec/shared/client_initializer_behaviour.rb +2 -2
  72. data/spec/shared/safe_deferrable_behaviour.rb +6 -2
  73. data/spec/spec_helper.rb +4 -2
  74. data/spec/support/debug_failure_helper.rb +20 -4
  75. data/spec/support/event_machine_helper.rb +32 -1
  76. data/spec/unit/auth_spec.rb +4 -11
  77. data/spec/unit/logger_spec.rb +28 -2
  78. data/spec/unit/models/auth_details_spec.rb +49 -0
  79. data/spec/unit/models/channel_state_change_spec.rb +23 -3
  80. data/spec/unit/models/connection_details_spec.rb +12 -1
  81. data/spec/unit/models/connection_state_change_spec.rb +15 -4
  82. data/spec/unit/models/message_encoders/base64_spec.rb +2 -1
  83. data/spec/unit/models/message_spec.rb +153 -0
  84. data/spec/unit/models/presence_message_spec.rb +192 -0
  85. data/spec/unit/models/protocol_message_spec.rb +64 -6
  86. data/spec/unit/models/token_details_spec.rb +75 -0
  87. data/spec/unit/models/token_request_spec.rb +74 -0
  88. data/spec/unit/modules/async_wrapper_spec.rb +2 -1
  89. data/spec/unit/modules/enum_spec.rb +69 -0
  90. data/spec/unit/modules/event_emitter_spec.rb +149 -22
  91. data/spec/unit/modules/state_emitter_spec.rb +9 -3
  92. data/spec/unit/realtime/client_spec.rb +1 -1
  93. data/spec/unit/realtime/connection_spec.rb +8 -5
  94. data/spec/unit/realtime/incoming_message_dispatcher_spec.rb +1 -1
  95. data/spec/unit/realtime/presence_spec.rb +4 -3
  96. data/spec/unit/rest/client_spec.rb +1 -1
  97. data/spec/unit/util/crypto_spec.rb +3 -3
  98. metadata +22 -19
@@ -74,7 +74,6 @@ describe Ably::Rest::Presence do
74
74
  let!(:get_stub) {
75
75
  query_params = query_options.map { |k, v| "#{k}=#{v}" }.join('&')
76
76
  stub_request(:get, "#{endpoint}/channels/#{Addressable::URI.encode(channel_name)}/presence?#{query_params}").
77
- with(basic_auth: [key_name, key_secret]).
78
77
  to_return(:body => '{}', :headers => { 'Content-Type' => 'application/json' })
79
78
  }
80
79
  let(:channel_name) { random_str }
@@ -196,7 +195,6 @@ describe Ably::Rest::Presence do
196
195
  let!(:history_stub) {
197
196
  query_params = history_options.map { |k, v| "#{k}=#{v}" }.join('&')
198
197
  stub_request(:get, "#{endpoint}/channels/#{Addressable::URI.encode(channel_name)}/presence/history?#{query_params}").
199
- with(basic_auth: [user, secret]).
200
198
  to_return(:body => '{}', :headers => { 'Content-Type' => 'application/json' })
201
199
  }
202
200
 
@@ -237,7 +235,6 @@ describe Ably::Rest::Presence do
237
235
  let!(:history_stub) {
238
236
  query_params = history_options.map { |k, v| "#{k}=#{v}" }.join('&')
239
237
  stub_request(:get, "#{endpoint}/channels/#{Addressable::URI.encode(channel_name)}/presence/history?#{query_params}").
240
- with(basic_auth: [user, secret]).
241
238
  to_return(:body => '{}', :headers => { 'Content-Type' => 'application/json' })
242
239
  }
243
240
 
@@ -342,7 +339,6 @@ describe Ably::Rest::Presence do
342
339
  context '#get' do
343
340
  let!(:get_stub) {
344
341
  stub_request(:get, "#{endpoint}/channels/#{Addressable::URI.encode(channel_name)}/presence?limit=100").
345
- with(basic_auth: [user, secret]).
346
342
  to_return(:body => serialized_encoded_message, :headers => { 'Content-Type' => content_type })
347
343
  }
348
344
 
@@ -360,7 +356,6 @@ describe Ably::Rest::Presence do
360
356
  context '#history' do
361
357
  let!(:history_stub) {
362
358
  stub_request(:get, "#{endpoint}/channels/#{Addressable::URI.encode(channel_name)}/presence/history?direction=backwards&limit=100").
363
- with(basic_auth: [user, secret]).
364
359
  to_return(:body => serialized_encoded_message, :headers => { 'Content-Type' => content_type })
365
360
  }
366
361
 
@@ -391,7 +386,6 @@ describe Ably::Rest::Presence do
391
386
  let(:client_options) { default_options.merge(log_level: :fatal) }
392
387
  let!(:get_stub) {
393
388
  stub_request(:get, "#{endpoint}/channels/#{Addressable::URI.encode(channel_name)}/presence?limit=100").
394
- with(basic_auth: [user, secret]).
395
389
  to_return(:body => serialized_encoded_message_with_invalid_encoding, :headers => { 'Content-Type' => content_type })
396
390
  }
397
391
  let(:presence_message) { presence.get.items.first }
@@ -405,8 +399,8 @@ describe Ably::Rest::Presence do
405
399
  end
406
400
 
407
401
  it 'logs a cipher error' do
408
- expect(client.logger).to receive(:error) do |message|
409
- expect(message).to match(/Cipher algorithm [\w-]+ does not match/)
402
+ expect(client.logger).to receive(:error) do |*args, &block|
403
+ expect(args.concat([block ? block.call : nil]).join(',')).to match(/Cipher algorithm [\w-]+ does not match/)
410
404
  end
411
405
  presence.get
412
406
  end
@@ -416,7 +410,6 @@ describe Ably::Rest::Presence do
416
410
  let(:client_options) { default_options.merge(log_level: :fatal) }
417
411
  let!(:history_stub) {
418
412
  stub_request(:get, "#{endpoint}/channels/#{Addressable::URI.encode(channel_name)}/presence/history?direction=backwards&limit=100").
419
- with(basic_auth: [user, secret]).
420
413
  to_return(:body => serialized_encoded_message_with_invalid_encoding, :headers => { 'Content-Type' => content_type })
421
414
  }
422
415
  let(:presence_message) { presence.history.items.first }
@@ -430,8 +423,8 @@ describe Ably::Rest::Presence do
430
423
  end
431
424
 
432
425
  it 'logs a cipher error' do
433
- expect(client.logger).to receive(:error) do |message|
434
- expect(message).to match(/Cipher algorithm [\w-]+ does not match/)
426
+ expect(client.logger).to receive(:error) do |*args, &block|
427
+ expect(args.concat([block ? block.call : nil]).join(',')).to match(/Cipher algorithm [\w-]+ does not match/)
435
428
  end
436
429
  presence.history
437
430
  end
data/spec/rspec_config.rb CHANGED
@@ -41,7 +41,7 @@ RSpec.configure do |config|
41
41
  end
42
42
 
43
43
  if defined?(EventMachine)
44
- config.before(:example) do
44
+ config.before(:example, :event_machine) do
45
45
  # Ensure EventMachine shutdown hooks are deregistered for every test
46
46
  EventMachine.instance_variable_set '@tails', []
47
47
  end
@@ -52,6 +52,7 @@ RSpec.configure do |config|
52
52
  if ENV['RSPEC_RETRY']
53
53
  puts 'Running tests using RSpec retry'
54
54
  config.verbose_retry = true # show retry status in spec process
55
+ config.display_try_failure_messages = true # show exception that triggered the try
55
56
  config.default_retry_count = 3
56
57
  config.default_sleep_interval = 2
57
58
  end
@@ -154,9 +154,9 @@ shared_examples 'a client initializer' do
154
154
  end
155
155
 
156
156
  context 'with token_params' do
157
- let(:client_options) { { token_params: { ttl: 777, client_id: 'john' }, token: 'token', auto_connect: false } }
157
+ let(:client_options) { { default_token_params: { ttl: 777, client_id: 'john' }, token: 'token', auto_connect: false } }
158
158
 
159
- it 'configures the default token_params' do
159
+ it 'configures default_token_params' do
160
160
  expect(subject.auth.token_params.fetch(:ttl)).to eql(777)
161
161
  expect(subject.auth.token_params.fetch(:client_id)).to eql('john')
162
162
  end
@@ -20,7 +20,9 @@ shared_examples 'a safe Deferrable' do
20
20
  end
21
21
 
22
22
  it 'catches exceptions in the callback and logs the error to the logger' do
23
- expect(subject.send(:logger)).to receive(:error).with(/#{exception.message}/)
23
+ expect(subject.send(:logger)).to receive(:error) do |*args, &block|
24
+ expect(args.concat([block ? block.call : nil]).join(',')).to match(/#{exception.message}/)
25
+ end
24
26
  subject.errback do
25
27
  raise exception
26
28
  end
@@ -49,7 +51,9 @@ shared_examples 'a safe Deferrable' do
49
51
  end
50
52
 
51
53
  it 'catches exceptions in the callback and logs the error to the logger' do
52
- expect(subject.send(:logger)).to receive(:error).with(/#{exception.message}/)
54
+ expect(subject.send(:logger)).to receive(:error) do |*args, &block|
55
+ expect(args.concat([block ? block.call : nil]).join(',')).to match(/#{exception.message}/)
56
+ end
53
57
  subject.callback do
54
58
  raise exception
55
59
  end
data/spec/spec_helper.rb CHANGED
@@ -4,8 +4,10 @@ def console(message)
4
4
  puts "\033[31m[#{Time.now.strftime('%H:%M:%S.%L')}]\033[0m \033[33m#{message}\033[0m"
5
5
  end
6
6
 
7
- require 'coveralls'
8
- Coveralls.wear!
7
+ unless RUBY_VERSION.match(/^1/)
8
+ require 'coveralls'
9
+ Coveralls.wear!
10
+ end
9
11
 
10
12
  require 'webmock/rspec'
11
13
 
@@ -1,15 +1,31 @@
1
1
  RSpec.configure do |config|
2
- config.before(:example) do
2
+ config.before(:example) do |example|
3
+ next if example.metadata[:prevent_log_stubbing]
4
+
3
5
  @log_output = []
4
6
  %w(fatal error warn info debug).each do |method_name|
5
- allow_any_instance_of(Ably::Logger).to receive(method_name.to_sym).and_wrap_original do |method, *args|
6
- @log_output << "#{Time.now.strftime('%H:%M:%S.%L')} [\e[33m#{method_name}\e[0m] #{args[0]}"
7
- method.call(*args)
7
+ allow_any_instance_of(Ably::Logger).to receive(method_name.to_sym).and_wrap_original do |method, *args, &block|
8
+ # Don't log shutdown sequence to keep log noise to a minimum
9
+ next if RSpec.const_defined?(:EventMachine) && RSpec::EventMachine.reactor_stopping?
10
+
11
+ prefix = "#{Time.now.strftime('%H:%M:%S.%L')} [\e[33m#{method_name}\e[0m] "
12
+
13
+ begin
14
+ args << block.call unless block.nil?
15
+ @log_output << "#{prefix}#{args.compact.join(' ')}"
16
+ rescue StandardError => e
17
+ @log_output << "#{prefix}Failed to log block - #{e.class}: #{e.message}\n#{e.backtrace.join("\n")}}"
18
+ end
19
+
20
+ # Call original
21
+ method.call(*args, &block)
8
22
  end
9
23
  end
10
24
  end
11
25
 
12
26
  config.after(:example) do |example|
27
+ next if example.metadata[:prevent_log_stubbing]
28
+
13
29
  exception = example.exception
14
30
  puts "\n#{'-'*34}\n\e[36mVerbose Ably log from test failure\e[0m\n#{'-'*34}\n#{@log_output.join("\n")}\n\n" if exception
15
31
  end
@@ -9,6 +9,8 @@ module RSpec
9
9
  DEFAULT_TIMEOUT = 15
10
10
 
11
11
  def run_reactor(timeout = DEFAULT_TIMEOUT)
12
+ @reactor_stopping = false
13
+
12
14
  Timeout::timeout(timeout + 0.5) do
13
15
  ::EventMachine.run do
14
16
  yield
@@ -16,7 +18,13 @@ module RSpec
16
18
  end
17
19
  end
18
20
 
21
+ def reactor_stopping?
22
+ @reactor_stopping
23
+ end
24
+
19
25
  def stop_reactor
26
+ mark_reactor_stopping
27
+
20
28
  unless realtime_clients.empty?
21
29
  realtime_clients.shift.tap do |client|
22
30
  # Ensure close appens outside of the caller as this can cause errbacks on Deferrables
@@ -45,6 +53,10 @@ module RSpec
45
53
  @realtime_clients ||= []
46
54
  end
47
55
 
56
+ def mark_reactor_stopping
57
+ @reactor_stopping = true
58
+ end
59
+
48
60
  # Allows multiple Deferrables to be passed in and calls the provided block when
49
61
  # all success callbacks have completed
50
62
  def when_all(*deferrables)
@@ -123,8 +135,27 @@ RSpec.configure do |config|
123
135
  example.run
124
136
  end
125
137
 
126
- config.before(:example) do
138
+ config.before(:example, :event_machine) do
127
139
  # Ensure EventMachine shutdown hooks are deregistered for every test
128
140
  EventMachine.instance_variable_set '@tails', []
129
141
  end
130
142
  end
143
+
144
+ module RSpec
145
+ module Expectations
146
+ module ExpectationHelper
147
+ class << self
148
+ # This is very hacky and ties into the internals of RSpec which is likely to break in future versions
149
+ # However, this is just a convenience to reduce log noise when the reactor is stopping
150
+ # i.e. debug_failure_helper logs the verbose messages generated by the libraries, however it also often
151
+ # catches all the shutdown messages which is unnecessary
152
+ alias_method :orig_handle_failure, :handle_failure
153
+
154
+ def handle_failure(*args, &block)
155
+ RSpec::EventMachine.mark_reactor_stopping
156
+ orig_handle_failure(*args, &block)
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
@@ -68,19 +68,12 @@ describe Ably::Auth do
68
68
  end
69
69
 
70
70
  context 'defaults' do
71
- let(:one_hour) { 60 * 60 }
72
- let(:all_capabilities) { { "*" => ["*"] } }
73
-
74
- it 'should default TTL to 1 hour' do
75
- expect(Ably::Auth::TOKEN_DEFAULTS.fetch(:ttl)).to eql(one_hour)
76
- end
77
-
78
- it 'should default capability to all' do
79
- expect(Ably::Auth::TOKEN_DEFAULTS.fetch(:capability)).to eql(all_capabilities)
71
+ it 'should have no default TTL' do
72
+ expect(Ably::Auth::TOKEN_DEFAULTS[:ttl]).to be_nil
80
73
  end
81
74
 
82
- it 'should have defaults for :ttl and :capability' do
83
- expect(Ably::Auth::TOKEN_DEFAULTS.keys).to include(:ttl, :capability)
75
+ it 'should have no default capability' do
76
+ expect(Ably::Auth::TOKEN_DEFAULTS[:capability]).to be_nil
84
77
  end
85
78
  end
86
79
  end
@@ -18,7 +18,9 @@ describe Ably::Logger do
18
18
 
19
19
  context 'internals', :api_private do
20
20
  it 'delegates to the logger object' do
21
- expect(subject.logger).to receive(:warn).with('message')
21
+ expect(subject.logger).to receive(:warn) do |*args, &block|
22
+ expect(args.concat([block ? block.call : nil]).join(',')).to match(/message/)
23
+ end
22
24
  subject.warn 'message'
23
25
  end
24
26
 
@@ -138,9 +140,33 @@ describe Ably::Logger do
138
140
  end
139
141
 
140
142
  it 'delegates log messages to logger', :api_private do
141
- expect(custom_logger_object).to receive(:fatal).with('message')
143
+ expect(custom_logger_object).to receive(:fatal) do |*args, &block|
144
+ expect(args.concat([block ? block.call : nil]).join(',')).to match(/message/)
145
+ end
142
146
  subject.fatal 'message'
143
147
  end
144
148
  end
145
149
  end
150
+
151
+ context 'with blocks', :prevent_log_stubbing do
152
+ it 'does not call the block unless the log level is met' do
153
+ log_level_blocks = []
154
+ subject.warn { log_level_blocks << :warn }
155
+ subject.info { log_level_blocks << :info }
156
+ subject.debug { log_level_blocks << :debug }
157
+ expect(log_level_blocks).to contain_exactly(:warn, :info)
158
+ end
159
+
160
+ context 'with an exception in the logger block' do
161
+ before do
162
+ expect(subject.logger).to receive(:error) do |*args, &block|
163
+ expect(args.concat([block ? block.call : nil]).join(',')).to match(/Raise an error in the block/)
164
+ end
165
+ end
166
+
167
+ it 'catches the error and continues' do
168
+ subject.info { raise "Raise an error in the block" }
169
+ end
170
+ end
171
+ end
146
172
  end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+ require 'shared/model_behaviour'
3
+
4
+ describe Ably::Models::AuthDetails do
5
+ include Ably::Modules::Conversions
6
+
7
+ subject { Ably::Models::AuthDetails }
8
+
9
+ # Spec model items AD2*
10
+ it_behaves_like 'a model', with_simple_attributes: %w(access_token) do
11
+ let(:model_args) { [] }
12
+ end
13
+
14
+ context '==' do
15
+ let(:attributes) { { access_token: 'unique' } }
16
+
17
+ it 'is true when attributes are the same' do
18
+ auth_details = -> { Ably::Models::AuthDetails.new(attributes) }
19
+ expect(auth_details.call).to eq(auth_details.call)
20
+ end
21
+
22
+ it 'is false when attributes are not the same' do
23
+ expect(Ably::Models::AuthDetails.new(access_token: '1')).to_not eq(Ably::Models::AuthDetails.new(access_token: '2'))
24
+ end
25
+
26
+ it 'is false when class type differs' do
27
+ expect(Ably::Models::AuthDetails.new(access_token: '1')).to_not eq(nil)
28
+ end
29
+ end
30
+
31
+ context 'AuthDetails conversion methods', :api_private do
32
+ context 'with a AuthDetails object' do
33
+ let(:details) { Ably::Models::AuthDetails.new(access_token: random_str) }
34
+
35
+ it 'returns the AuthDetails object' do
36
+ expect(Ably::Models::AuthDetails(details)).to eql(details)
37
+ end
38
+ end
39
+
40
+ context 'with a JSON object' do
41
+ let(:access_token) { random_str }
42
+ let(:details_json) { { access_token: access_token } }
43
+
44
+ it 'returns a new AuthDetails object from the JSON' do
45
+ expect(Ably::Models::AuthDetails(details_json).access_token).to eql(access_token)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -6,7 +6,7 @@ describe Ably::Models::ChannelStateChange do
6
6
 
7
7
  subject { Ably::Models::ChannelStateChange }
8
8
 
9
- context '#current' do
9
+ context '#current (#TH1)' do
10
10
  it 'is required' do
11
11
  expect { subject.new(previous: true) }.to raise_error ArgumentError
12
12
  end
@@ -16,7 +16,7 @@ describe Ably::Models::ChannelStateChange do
16
16
  end
17
17
  end
18
18
 
19
- context '#previous' do
19
+ context '#previous (#TH2)' do
20
20
  it 'is required' do
21
21
  expect { subject.new(current: true) }.to raise_error ArgumentError
22
22
  end
@@ -26,7 +26,17 @@ describe Ably::Models::ChannelStateChange do
26
26
  end
27
27
  end
28
28
 
29
- context '#reason' do
29
+ context '#event (#TH5)' do
30
+ it 'is not required' do
31
+ expect { subject.new(previous: true, current: true) }.to_not raise_error
32
+ end
33
+
34
+ it 'is an attribute' do
35
+ expect(subject.new(event: unique, previous: unique, current: true).event).to eql(unique)
36
+ end
37
+ end
38
+
39
+ context '#reason (#TH3)' do
30
40
  it 'is not required' do
31
41
  expect { subject.new(previous: true, current: true) }.to_not raise_error
32
42
  end
@@ -36,6 +46,16 @@ describe Ably::Models::ChannelStateChange do
36
46
  end
37
47
  end
38
48
 
49
+ context '#resumed (#TH4)' do
50
+ it 'is false when ommitted' do
51
+ expect(subject.new(previous: true, current: true).resumed).to be_falsey
52
+ end
53
+
54
+ it 'is true when provided' do
55
+ expect(subject.new(previous: true, current: true, resumed: true).resumed).to be_truthy
56
+ end
57
+ end
58
+
39
59
  context 'invalid attributes' do
40
60
  it 'raises an argument error' do
41
61
  expect { subject.new(invalid: true, current: true, previous: true) }.to raise_error ArgumentError
@@ -6,6 +6,7 @@ describe Ably::Models::ConnectionDetails do
6
6
 
7
7
  subject { Ably::Models::ConnectionDetails }
8
8
 
9
+ # Spec model items CD2*
9
10
  it_behaves_like 'a model', with_simple_attributes: %w(client_id connection_key max_message_size max_frame_size max_inbound_rate) do
10
11
  let(:model_args) { [] }
11
12
  end
@@ -13,13 +14,23 @@ describe Ably::Models::ConnectionDetails do
13
14
  context 'attributes' do
14
15
  let(:connection_state_ttl_ms) { 5_000 }
15
16
 
16
- context '#connection_state_ttl' do
17
+ context '#connection_state_ttl (#CD2f)' do
17
18
  subject { Ably::Models::ConnectionDetails.new({ connection_state_ttl: connection_state_ttl_ms }) }
18
19
 
19
20
  it 'retrieves attribute :connection_state_ttl and converts it from ms to s' do
20
21
  expect(subject.connection_state_ttl).to eql(connection_state_ttl_ms / 1000)
21
22
  end
22
23
  end
24
+
25
+ let(:max_idle_interval) { 6_000 }
26
+
27
+ context '#max_idle_interval (#CD2h)' do
28
+ subject { Ably::Models::ConnectionDetails.new({ max_idle_interval: max_idle_interval }) }
29
+
30
+ it 'retrieves attribute :max_idle_interval and converts it from ms to s' do
31
+ expect(subject.max_idle_interval).to eql(max_idle_interval / 1000)
32
+ end
33
+ end
23
34
  end
24
35
 
25
36
  context '==' do