ably-rest 0.8.2 → 0.8.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -43
  3. data/SPEC.md +707 -580
  4. data/lib/submodules/ably-ruby/.travis.yml +1 -0
  5. data/lib/submodules/ably-ruby/CHANGELOG.md +143 -3
  6. data/lib/submodules/ably-ruby/README.md +1 -1
  7. data/lib/submodules/ably-ruby/SPEC.md +842 -520
  8. data/lib/submodules/ably-ruby/ably.gemspec +1 -1
  9. data/lib/submodules/ably-ruby/lib/ably/auth.rb +114 -87
  10. data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +40 -14
  11. data/lib/submodules/ably-ruby/lib/ably/models/message.rb +3 -5
  12. data/lib/submodules/ably-ruby/lib/ably/models/paginated_result.rb +3 -12
  13. data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +8 -2
  14. data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +15 -3
  15. data/lib/submodules/ably-ruby/lib/ably/models/stat.rb +1 -1
  16. data/lib/submodules/ably-ruby/lib/ably/models/token_details.rb +1 -1
  17. data/lib/submodules/ably-ruby/lib/ably/modules/channels_collection.rb +7 -1
  18. data/lib/submodules/ably-ruby/lib/ably/modules/conversions.rb +1 -1
  19. data/lib/submodules/ably-ruby/lib/ably/modules/encodeable.rb +6 -3
  20. data/lib/submodules/ably-ruby/lib/ably/modules/message_pack.rb +2 -2
  21. data/lib/submodules/ably-ruby/lib/ably/modules/model_common.rb +1 -1
  22. data/lib/submodules/ably-ruby/lib/ably/modules/state_machine.rb +2 -2
  23. data/lib/submodules/ably-ruby/lib/ably/realtime.rb +1 -0
  24. data/lib/submodules/ably-ruby/lib/ably/realtime/auth.rb +191 -0
  25. data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +97 -25
  26. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +11 -3
  27. data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +22 -6
  28. data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +73 -40
  29. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +48 -33
  30. data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +17 -3
  31. data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +43 -16
  32. data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +57 -26
  33. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/exceptions.rb +3 -1
  34. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +4 -2
  35. data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +1 -0
  36. data/lib/submodules/ably-ruby/lib/ably/version.rb +1 -1
  37. data/lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb +242 -0
  38. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +277 -5
  39. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channels_spec.rb +64 -0
  40. data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +26 -5
  41. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +23 -6
  42. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +167 -16
  43. data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +9 -8
  44. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +1 -0
  45. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +121 -10
  46. data/lib/submodules/ably-ruby/spec/acceptance/realtime/stats_spec.rb +13 -1
  47. data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +161 -79
  48. data/lib/submodules/ably-ruby/spec/acceptance/rest/base_spec.rb +3 -3
  49. data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +142 -15
  50. data/lib/submodules/ably-ruby/spec/acceptance/rest/channels_spec.rb +23 -0
  51. data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +180 -18
  52. data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +8 -8
  53. data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +136 -25
  54. data/lib/submodules/ably-ruby/spec/acceptance/rest/stats_spec.rb +60 -4
  55. data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +54 -3
  56. data/lib/submodules/ably-ruby/spec/unit/auth_spec.rb +7 -6
  57. data/lib/submodules/ably-ruby/spec/unit/models/message_spec.rb +1 -9
  58. data/lib/submodules/ably-ruby/spec/unit/models/paginated_result_spec.rb +1 -18
  59. data/lib/submodules/ably-ruby/spec/unit/models/presence_message_spec.rb +1 -1
  60. data/lib/submodules/ably-ruby/spec/unit/models/protocol_message_spec.rb +21 -1
  61. data/lib/submodules/ably-ruby/spec/unit/realtime/channel_spec.rb +10 -3
  62. data/lib/submodules/ably-ruby/spec/unit/realtime/channels_spec.rb +27 -8
  63. data/lib/submodules/ably-ruby/spec/unit/rest/channel_spec.rb +0 -8
  64. data/lib/submodules/ably-ruby/spec/unit/rest/client_spec.rb +7 -7
  65. metadata +5 -2
@@ -167,12 +167,12 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
167
167
 
168
168
  context 'when connection state is :failed' do
169
169
  describe '#close' do
170
- it 'will not transition state to :close and raises a StateChangeError exception' do
170
+ it 'will not transition state to :close and raises a InvalidStateChange exception' do
171
171
  connection.on(:connected) { raise 'Connection should not have reached :connected state' }
172
172
 
173
173
  connection.once(:failed) do
174
174
  expect(connection.state).to eq(:failed)
175
- expect { connection.close }.to raise_error Ably::Exceptions::StateChangeError, /Unable to transition from failed => closing/
175
+ expect { connection.close }.to raise_error Ably::Exceptions::InvalidStateChange, /Unable to transition from failed => closing/
176
176
  stop_reactor
177
177
  end
178
178
  end
@@ -228,6 +228,8 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
228
228
  end
229
229
 
230
230
  context 'connection opening times out' do
231
+ let(:client_options) { client_failure_options }
232
+
231
233
  it 'attempts to reconnect' do
232
234
  started_at = Time.now
233
235
 
@@ -249,8 +251,6 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
249
251
  end
250
252
 
251
253
  context 'when retry intervals are stubbed to attempt reconnection quickly' do
252
- let(:client_options) { client_failure_options }
253
-
254
254
  before do
255
255
  # Reconfigure client library retry periods and timeouts so that tests run quickly
256
256
  stub_const 'Ably::Realtime::Connection::ConnectionManager::CONNECT_RETRY_CONFIG',
@@ -488,7 +488,8 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
488
488
  when_all(*channels.map(&:attach)) do
489
489
  detached_channels = []
490
490
  channels.each do |channel|
491
- channel.on(:detached) do
491
+ channel.on(:detached) do |error|
492
+ expect(error.message).to match(/Invalid connection key/i)
492
493
  detached_channels << channel
493
494
  next unless detached_channels.count == channel_count
494
495
  expect(detached_channels.count).to eql(channel_count)
@@ -537,7 +538,7 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
537
538
 
538
539
  context 'with custom realtime websocket host option' do
539
540
  let(:expected_host) { 'this.host.does.not.exist' }
540
- let(:client_options) { default_options.merge(realtime_host: expected_host, log_level: :none) }
541
+ let(:client_options) { default_options.merge(realtime_host: expected_host, :environment => :production, log_level: :none) }
541
542
 
542
543
  it 'never uses a fallback host' do
543
544
  expect(EventMachine).to receive(:connect).exactly(retry_count_for_all_states).times do |host|
@@ -551,6 +552,22 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
551
552
  end
552
553
  end
553
554
 
555
+ context 'with custom realtime websocket port option' do
556
+ let(:custom_port) { 666}
557
+ let(:client_options) { default_options.merge(tls_port: custom_port, :environment => :production, log_level: :none) }
558
+
559
+ it 'never uses a fallback host' do
560
+ expect(EventMachine).to receive(:connect).exactly(retry_count_for_all_states).times do |host, port|
561
+ expect(port).to eql(custom_port)
562
+ raise EventMachine::ConnectionError
563
+ end
564
+
565
+ connection.on(:failed) do
566
+ stop_reactor
567
+ end
568
+ end
569
+ end
570
+
554
571
  context 'with non-production environment' do
555
572
  let(:environment) { 'sandbox' }
556
573
  let(:expected_host) { "#{environment}-#{Ably::Realtime::Client::DOMAIN}" }
@@ -52,9 +52,12 @@ describe Ably::Realtime::Connection, :event_machine do
52
52
  before do
53
53
  # Reduce token expiry buffer to zero so that a token expired? predicate is exact
54
54
  # Normally there is a buffer so that a token expiring soon is considered expired
55
+ @original_token_expiry_buffer = Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER
55
56
  stub_const 'Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER', 0
56
57
  end
57
58
 
59
+ let(:original_token_expiry_buffer) { @original_token_expiry_buffer }
60
+
58
61
  context 'for renewable tokens' do
59
62
  context 'that are valid for the duration of the test' do
60
63
  context 'with valid pre authorised token expiring in the future' do
@@ -71,7 +74,7 @@ describe Ably::Realtime::Connection, :event_machine do
71
74
  let(:client_options) { default_options.merge(client_id: 'force_token_auth') }
72
75
 
73
76
  it 'uses the token created by the implicit authorisation' do
74
- expect(client.auth).to receive(:request_token).once.and_call_original
77
+ expect(client.rest_client.auth).to receive(:request_token).once.and_call_original
75
78
 
76
79
  connection.once(:connected) do
77
80
  stop_reactor
@@ -84,20 +87,36 @@ describe Ably::Realtime::Connection, :event_machine do
84
87
  let(:client_options) { default_options.merge(log_level: :none) }
85
88
 
86
89
  before do
87
- client.auth.authorise(ttl: ttl)
90
+ expect(client.rest_client.time.to_f).to be_within(2).of(Time.now.to_i), "Local clock is out of sync with Ably"
91
+ end
92
+
93
+ before do
94
+ # Ensure tokens issued expire immediately after issue
95
+ @original_renew_token_buffer = Ably::Auth::TOKEN_DEFAULTS.fetch(:renew_token_buffer)
96
+ stub_const 'Ably::Auth::TOKEN_DEFAULTS', Ably::Auth::TOKEN_DEFAULTS.merge(renew_token_buffer: 0)
97
+
98
+ # Authorise synchronously to ensure token has been issued
99
+ client.auth.authorise_sync(token_params: { ttl: ttl })
88
100
  end
89
101
 
102
+ let(:original_renew_token_buffer) { @original_renew_token_buffer }
103
+
90
104
  context 'opening a new connection' do
91
105
  context 'with recently expired token' do
92
106
  let(:ttl) { 2 }
93
107
 
94
- it 'renews the token on connect' do
95
- sleep ttl + 0.1
96
- expect(client.auth.current_token_details).to be_expired
97
- expect(client.auth).to receive(:authorise).at_least(:once).and_call_original
98
- connection.once(:connected) do
99
- expect(client.auth.current_token_details).to_not be_expired
100
- stop_reactor
108
+ it 'renews the token on connect without changing connection state' do
109
+ connection.once(:connecting) do
110
+ sleep ttl + 0.1
111
+ expect(client.auth.current_token_details).to be_expired
112
+ expect(client.rest_client.auth).to receive(:authorise).at_least(:once).and_call_original
113
+ connection.once(:connected) do
114
+ expect(client.auth.current_token_details).to_not be_expired
115
+ stop_reactor
116
+ end
117
+ connection.once_state_changed do
118
+ raise "Invalid state #{connection.state}" unless connection.state == :connected
119
+ end
101
120
  end
102
121
  end
103
122
  end
@@ -106,7 +125,7 @@ describe Ably::Realtime::Connection, :event_machine do
106
125
  let(:ttl) { 0.001 }
107
126
 
108
127
  it 'renews the token on connect, and only makes one subsequent attempt to obtain a new token' do
109
- expect(client.auth).to receive(:authorise).at_least(:twice).and_call_original
128
+ expect(client.rest_client.auth).to receive(:authorise).at_least(:twice).and_call_original
110
129
  connection.once(:disconnected) do
111
130
  connection.once(:failed) do |error|
112
131
  expect(error.code).to eql(40140) # token expired
@@ -149,6 +168,12 @@ describe Ably::Realtime::Connection, :event_machine do
149
168
  connection.once(:connected) do
150
169
  started_at = Time.now
151
170
  connection.once(:disconnected) do |error|
171
+ expect(error.code).to eq(40140) # Token expired
172
+
173
+ # Token has expired, so now ensure it is not used again
174
+ stub_const 'Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER', original_token_expiry_buffer
175
+ stub_const 'Ably::Auth::TOKEN_DEFAULTS', Ably::Auth::TOKEN_DEFAULTS.merge(renew_token_buffer: original_renew_token_buffer)
176
+
152
177
  connection.once(:connected) do
153
178
  expect(client.auth.current_token_details).to_not be_expired
154
179
  expect(Time.now - started_at >= ttl)
@@ -159,6 +184,8 @@ describe Ably::Realtime::Connection, :event_machine do
159
184
  end
160
185
  end
161
186
 
187
+ connection.unsafe_once(:failed) { |error| fail error.inspect }
188
+
162
189
  channel.attach
163
190
  end
164
191
  end
@@ -172,8 +199,13 @@ describe Ably::Realtime::Connection, :event_machine do
172
199
 
173
200
  context 'for non-renewable tokens' do
174
201
  context 'that are expired' do
202
+ before do
203
+ stub_const 'Ably::Auth::TOKEN_DEFAULTS', Ably::Auth::TOKEN_DEFAULTS.merge(renew_token_buffer: 0)
204
+ end
205
+
175
206
  let!(:expired_token_details) do
176
- Ably::Realtime::Client.new(default_options).auth.request_token(ttl: 0.01)
207
+ # Request a token synchronously
208
+ Ably::Realtime::Client.new(default_options).auth.request_token_sync(token_params: { ttl: 0.01 })
177
209
  end
178
210
 
179
211
  context 'opening a new connection' do
@@ -260,6 +292,20 @@ describe Ably::Realtime::Connection, :event_machine do
260
292
  end
261
293
  end
262
294
 
295
+ describe 'connection#id' do
296
+ it 'is null before connecting' do
297
+ expect(connection.id).to be_nil
298
+ stop_reactor
299
+ end
300
+ end
301
+
302
+ describe 'connection#key' do
303
+ it 'is null before connecting' do
304
+ expect(connection.key).to be_nil
305
+ stop_reactor
306
+ end
307
+ end
308
+
263
309
  describe 'once connected' do
264
310
  let(:connection2) { Ably::Realtime::Client.new(client_options).connection }
265
311
 
@@ -326,6 +372,18 @@ describe Ably::Realtime::Connection, :event_machine do
326
372
  end
327
373
  end
328
374
  end
375
+
376
+ context 'when closing' do
377
+ it 'raises an exception before the connection is closed' do
378
+ connection.connect do
379
+ connection.once(:closing) do
380
+ expect { connection.connect }.to raise_error Ably::Exceptions::InvalidStateChange
381
+ stop_reactor
382
+ end
383
+ connection.close
384
+ end
385
+ end
386
+ end
329
387
  end
330
388
 
331
389
  describe '#serial connection serial' do
@@ -339,7 +397,6 @@ describe Ably::Realtime::Connection, :event_machine do
339
397
  end
340
398
 
341
399
  context 'when a message is sent but the ACK has not yet been received' do
342
-
343
400
  it 'the sent message msgSerial is 0 but the connection serial remains at -1' do
344
401
  channel.attach do
345
402
  connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
@@ -732,13 +789,13 @@ describe Ably::Realtime::Connection, :event_machine do
732
789
  context 'when a state transition is unsupported' do
733
790
  let(:client_options) { default_options.merge(log_level: :none) } # silence FATAL errors
734
791
 
735
- it 'emits a StateChangeError' do
792
+ it 'emits a InvalidStateChange' do
736
793
  connection.connect do
737
794
  connection.transition_state_machine :initialized
738
795
  end
739
796
 
740
797
  connection.on(:error) do |error|
741
- expect(error).to be_a(Ably::Exceptions::StateChangeError)
798
+ expect(error).to be_a(Ably::Exceptions::InvalidStateChange)
742
799
  stop_reactor
743
800
  end
744
801
  end
@@ -794,16 +851,41 @@ describe Ably::Realtime::Connection, :event_machine do
794
851
  end
795
852
 
796
853
  context 'when the Internet is up' do
854
+ let(:client_options) { default_options.merge(tls: false, use_token_auth: true) }
855
+
856
+ context 'with a TLS connection' do
857
+ let(:client_options) { default_options.merge(tls: true) }
858
+
859
+ it 'checks the Internet up URL over TLS' do
860
+ expect(EventMachine::HttpRequest).to receive(:new).with("https:#{Ably::INTERNET_CHECK.fetch(:url)}").and_return(double('request', get: EventMachine::DefaultDeferrable.new))
861
+ connection.internet_up?
862
+ stop_reactor
863
+ end
864
+ end
865
+
866
+ context 'with a non-TLS connection' do
867
+ let(:client_options) { default_options.merge(tls: false, use_token_auth: true) }
868
+
869
+ it 'checks the Internet up URL over TLS' do
870
+ expect(EventMachine::HttpRequest).to receive(:new).with("http:#{Ably::INTERNET_CHECK.fetch(:url)}").and_return(double('request', get: EventMachine::DefaultDeferrable.new))
871
+ connection.internet_up?
872
+ stop_reactor
873
+ end
874
+ end
875
+
797
876
  it 'calls the block with true' do
798
877
  connection.internet_up? do |result|
799
878
  expect(result).to be_truthy
800
- stop_reactor
879
+ EventMachine.add_timer(0.2) { stop_reactor }
801
880
  end
802
881
  end
803
882
 
804
883
  it 'calls the success callback of the Deferrable' do
805
884
  connection.internet_up?.callback do
806
- stop_reactor
885
+ EventMachine.add_timer(0.2) { stop_reactor }
886
+ end
887
+ connection.internet_up?.errback do |error|
888
+ raise "Could not perform the Internet up check. Are you connected to the Internet? #{error}"
807
889
  end
808
890
  end
809
891
  end
@@ -828,5 +910,74 @@ describe Ably::Realtime::Connection, :event_machine do
828
910
  end
829
911
  end
830
912
  end
913
+
914
+ describe 'state change side effects' do
915
+ let(:channel) { client.channels.get(random_str) }
916
+ let(:client_options) { default_options.merge(:log_level => :error) }
917
+
918
+ context 'when connection enters the :disconnected state' do
919
+ it 'queues messages to be sent and all channels remain attached' do
920
+ channel.attach do
921
+ connection.once(:disconnected) do
922
+ expect(connection.__outgoing_message_queue__).to be_empty
923
+ channel.publish 'test'
924
+
925
+ EventMachine.add_timer(0.02) do
926
+ expect(connection.__outgoing_message_queue__).to_not be_empty
927
+ end
928
+
929
+ connection.once(:connected) do
930
+ EventMachine.add_timer(0.02) do
931
+ expect(connection.__outgoing_message_queue__).to be_empty
932
+ stop_reactor
933
+ end
934
+ end
935
+ end
936
+
937
+ connection.transport.close_connection_after_writing
938
+ end
939
+ end
940
+ end
941
+
942
+ context 'when connection enters the :suspended state' do
943
+ before do
944
+ # Reconfigure client library retry periods so that client stays in suspended state
945
+ stub_const 'Ably::Realtime::Connection::ConnectionManager::CONNECT_RETRY_CONFIG',
946
+ Ably::Realtime::Connection::ConnectionManager::CONNECT_RETRY_CONFIG.merge(
947
+ disconnected: { retry_every: 0.01, max_time_in_state: 0.05 },
948
+ suspended: { retry_every: 60, max_time_in_state: 60 }
949
+ )
950
+ end
951
+
952
+ it 'detaches the channels and prevents publishing of messages on those channels' do
953
+ channel.attach do
954
+ channel.once(:detached) do
955
+ expect { channel.publish 'test' }.to raise_error(Ably::Exceptions::ChannelInactive)
956
+ stop_reactor
957
+ end
958
+
959
+ # Keep disconnecting the websocket transport after it attempts reconnection
960
+ connection.transport.close_connection_after_writing
961
+ connection.on(:connecting) do
962
+ EventMachine.add_timer(0.03) do
963
+ connection.transport.close_connection_after_writing
964
+ end
965
+ end
966
+ end
967
+ end
968
+ end
969
+
970
+ context 'when connection enters the :failed state' do
971
+ let(:client_options) { default_options.merge(:key => 'will.not:authenticate', log_level: :none) }
972
+
973
+ it 'sets all channels to failed and prevents publishing of messages on those channels' do
974
+ channel.attach
975
+ channel.once(:failed) do
976
+ expect { channel.publish 'test' }.to raise_error(Ably::Exceptions::ChannelInactive)
977
+ stop_reactor
978
+ end
979
+ end
980
+ end
981
+ end
831
982
  end
832
983
  end
@@ -79,8 +79,8 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
79
79
  context 'Integer' do
80
80
  let(:data) { 1 }
81
81
 
82
- it 'is raises an UnsupportedDataTypeError 40011 exception' do
83
- expect { channel.publish 'event', data }.to raise_error(Ably::Exceptions::UnsupportedDataTypeError)
82
+ it 'is raises an UnsupportedDataType 40011 exception' do
83
+ expect { channel.publish 'event', data }.to raise_error(Ably::Exceptions::UnsupportedDataType)
84
84
  stop_reactor
85
85
  end
86
86
  end
@@ -88,8 +88,8 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
88
88
  context 'Float' do
89
89
  let(:data) { 1.1 }
90
90
 
91
- it 'is raises an UnsupportedDataTypeError 40011 exception' do
92
- expect { channel.publish 'event', data }.to raise_error(Ably::Exceptions::UnsupportedDataTypeError)
91
+ it 'is raises an UnsupportedDataType 40011 exception' do
92
+ expect { channel.publish 'event', data }.to raise_error(Ably::Exceptions::UnsupportedDataType)
93
93
  stop_reactor
94
94
  end
95
95
  end
@@ -97,8 +97,8 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
97
97
  context 'Boolean' do
98
98
  let(:data) { true }
99
99
 
100
- it 'is raises an UnsupportedDataTypeError 40011 exception' do
101
- expect { channel.publish 'event', data }.to raise_error(Ably::Exceptions::UnsupportedDataTypeError)
100
+ it 'is raises an UnsupportedDataType 40011 exception' do
101
+ expect { channel.publish 'event', data }.to raise_error(Ably::Exceptions::UnsupportedDataType)
102
102
  stop_reactor
103
103
  end
104
104
  end
@@ -106,8 +106,8 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
106
106
  context 'False' do
107
107
  let(:data) { false }
108
108
 
109
- it 'is raises an UnsupportedDataTypeError 40011 exception' do
110
- expect { channel.publish 'event', data }.to raise_error(Ably::Exceptions::UnsupportedDataTypeError)
109
+ it 'is raises an UnsupportedDataType 40011 exception' do
110
+ expect { channel.publish 'event', data }.to raise_error(Ably::Exceptions::UnsupportedDataType)
111
111
  stop_reactor
112
112
  end
113
113
  end
@@ -132,6 +132,7 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
132
132
  let(:client_options) { default_options.merge(client_id: client_id) }
133
133
 
134
134
  it 'contains a #client_id attribute' do
135
+ skip 'Waiting for issue #256 to be resolved'
135
136
  when_all(channel.attach, other_client_channel.attach) do
136
137
  other_client_channel.subscribe('event') do |message|
137
138
  expect(message.client_id).to eql(client_id)
@@ -22,6 +22,7 @@ describe Ably::Realtime::Presence, 'history', :event_machine do
22
22
  presence_client_one.enter(data: data) do
23
23
  presence_client_one.leave(data: leave_data) do
24
24
  presence_client_one.history do |history_page|
25
+ expect(history_page).to be_a(Ably::Models::PaginatedResult)
25
26
  expect(history_page.items.count).to eql(2)
26
27
 
27
28
  expect(history_page.items[1].action).to eq(:enter)
@@ -45,7 +45,62 @@ describe Ably::Realtime::Presence, :event_machine do
45
45
  setup_test(method_name, args, options) do
46
46
  channel_client_one.attach do
47
47
  channel_client_one.change_state state.to_sym
48
- expect { presence_client_one.public_send(method_name, args) }.to raise_error Ably::Exceptions::IncompatibleStateForOperation, /Operation is not allowed when channel is in STATE.#{state}/i
48
+ expect { presence_client_one.public_send(method_name, args) }.to raise_error Ably::Exceptions::InvalidStateChange, /Operation is not allowed when channel is in STATE.#{state}/i
49
+ stop_reactor
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ it 'implicitly attaches the channel' do
56
+ expect(channel_client_one).to_not be_attached
57
+ presence_client_one.public_send(method_name, args) do
58
+ expect(channel_client_one).to be_attached
59
+ stop_reactor
60
+ end
61
+ end
62
+
63
+ context 'when :queue_messages client option is false' do
64
+ let(:client_one) { Ably::Realtime::Client.new(default_options.merge(queue_messages: false, client_id: random_str)) }
65
+
66
+ context 'and connection state initialized' do
67
+ it 'raises an exception' do
68
+ expect { presence_client_one.public_send(method_name, args) }.to raise_error Ably::Exceptions::MessageQueueingDisabled
69
+ expect(client_one.connection).to be_initialized
70
+ stop_reactor
71
+ end
72
+ end
73
+
74
+ context 'and connection state connecting' do
75
+ it 'raises an exception' do
76
+ client_one.connect
77
+ EventMachine.next_tick do
78
+ expect { presence_client_one.public_send(method_name, args) }.to raise_error Ably::Exceptions::MessageQueueingDisabled
79
+ expect(client_one.connection).to be_connecting
80
+ stop_reactor
81
+ end
82
+ end
83
+ end
84
+
85
+ context 'and connection state disconnected' do
86
+ let(:client_one) { Ably::Realtime::Client.new(default_options.merge(queue_messages: false, client_id: random_str, :log_level => :error)) }
87
+
88
+ it 'raises an exception' do
89
+ client_one.connection.once(:connected) do
90
+ client_one.connection.once(:disconnected) do
91
+ expect { presence_client_one.public_send(method_name, args) }.to raise_error Ably::Exceptions::MessageQueueingDisabled
92
+ expect(client_one.connection).to be_disconnected
93
+ stop_reactor
94
+ end
95
+ client_one.connection.transition_state_machine :disconnected
96
+ end
97
+ end
98
+ end
99
+
100
+ context 'and connection state connected' do
101
+ it 'publishes the message' do
102
+ client_one.connection.once(:connected) do
103
+ presence_client_one.public_send(method_name, args)
49
104
  stop_reactor
50
105
  end
51
106
  end
@@ -120,8 +175,8 @@ describe Ably::Realtime::Presence, :event_machine do
120
175
  context 'Integer' do
121
176
  let(:data) { 1 }
122
177
 
123
- it 'raises an UnsupportedDataTypeError 40011 exception' do
124
- expect { presence_action(method_name, data) }.to raise_error(Ably::Exceptions::UnsupportedDataTypeError)
178
+ it 'raises an UnsupportedDataType 40011 exception' do
179
+ expect { presence_action(method_name, data) }.to raise_error(Ably::Exceptions::UnsupportedDataType)
125
180
  stop_reactor
126
181
  end
127
182
  end
@@ -129,8 +184,8 @@ describe Ably::Realtime::Presence, :event_machine do
129
184
  context 'Float' do
130
185
  let(:data) { 1.1 }
131
186
 
132
- it 'raises an UnsupportedDataTypeError 40011 exception' do
133
- expect { presence_action(method_name, data) }.to raise_error(Ably::Exceptions::UnsupportedDataTypeError)
187
+ it 'raises an UnsupportedDataType 40011 exception' do
188
+ expect { presence_action(method_name, data) }.to raise_error(Ably::Exceptions::UnsupportedDataType)
134
189
  stop_reactor
135
190
  end
136
191
  end
@@ -138,8 +193,8 @@ describe Ably::Realtime::Presence, :event_machine do
138
193
  context 'Boolean' do
139
194
  let(:data) { true }
140
195
 
141
- it 'raises an UnsupportedDataTypeError 40011 exception' do
142
- expect { presence_action(method_name, data) }.to raise_error(Ably::Exceptions::UnsupportedDataTypeError)
196
+ it 'raises an UnsupportedDataType 40011 exception' do
197
+ expect { presence_action(method_name, data) }.to raise_error(Ably::Exceptions::UnsupportedDataType)
143
198
  stop_reactor
144
199
  end
145
200
  end
@@ -147,8 +202,8 @@ describe Ably::Realtime::Presence, :event_machine do
147
202
  context 'False' do
148
203
  let(:data) { false }
149
204
 
150
- it 'raises an UnsupportedDataTypeError 40011 exception' do
151
- expect { presence_action(method_name, data) }.to raise_error(Ably::Exceptions::UnsupportedDataTypeError)
205
+ it 'raises an UnsupportedDataType 40011 exception' do
206
+ expect { presence_action(method_name, data) }.to raise_error(Ably::Exceptions::UnsupportedDataType)
152
207
  stop_reactor
153
208
  end
154
209
  end
@@ -161,6 +216,14 @@ describe Ably::Realtime::Presence, :event_machine do
161
216
  end
162
217
  end
163
218
 
219
+ it 'allows a block to be passed in that is executed upon success' do
220
+ setup_test(method_name, args, options) do
221
+ presence_client_one.public_send(method_name, args) do
222
+ stop_reactor
223
+ end
224
+ end
225
+ end
226
+
164
227
  it 'calls the Deferrable callback on success' do
165
228
  setup_test(method_name, args, options) do
166
229
  presence_client_one.public_send(method_name, args).callback do |presence|
@@ -1002,7 +1065,7 @@ describe Ably::Realtime::Presence, :event_machine do
1002
1065
  it "raise an exception if the channel is #{state}" do
1003
1066
  channel_client_one.attach do
1004
1067
  channel_client_one.change_state state.to_sym
1005
- expect { presence_client_one.get }.to raise_error Ably::Exceptions::IncompatibleStateForOperation, /Operation is not allowed when channel is in STATE.#{state}/i
1068
+ expect { presence_client_one.get }.to raise_error Ably::Exceptions::InvalidStateChange, /Operation is not allowed when channel is in STATE.#{state}/i
1006
1069
  stop_reactor
1007
1070
  end
1008
1071
  end
@@ -1218,6 +1281,36 @@ describe Ably::Realtime::Presence, :event_machine do
1218
1281
  end
1219
1282
  end
1220
1283
  end
1284
+
1285
+ context 'with event name' do
1286
+ it 'calls the callback for specified presence event' do
1287
+ when_all(channel_client_one.attach, channel_client_two.attach) do
1288
+ presence_client_two.subscribe(:leave) do |presence_message|
1289
+ messages << presence_message
1290
+ next unless messages.count == 1
1291
+
1292
+ expect(messages.map(&:action).map(&:to_sym)).to contain_exactly(:leave)
1293
+ stop_reactor
1294
+ end
1295
+
1296
+ presence_client_one.enter do
1297
+ presence_client_one.update do
1298
+ presence_client_one.leave
1299
+ end
1300
+ end
1301
+ end
1302
+ end
1303
+ end
1304
+
1305
+ it 'implicitly attaches' do
1306
+ expect(client_one.connection).to be_initialized
1307
+ presence_client_one.subscribe { true }
1308
+ channel_client_one.on(:attached) do
1309
+ expect(client_one.connection).to be_connected
1310
+ expect(channel_client_one).to be_attached
1311
+ stop_reactor
1312
+ end
1313
+ end
1221
1314
  end
1222
1315
 
1223
1316
  context '#unsubscribe' do
@@ -1238,6 +1331,24 @@ describe Ably::Realtime::Presence, :event_machine do
1238
1331
  end
1239
1332
  end
1240
1333
  end
1334
+
1335
+ context 'with event name' do
1336
+ it 'removes the callback for specified presence event' do
1337
+ when_all(channel_client_one.attach, channel_client_two.attach) do
1338
+ subscribe_callback = proc { raise 'Should not be called' }
1339
+ presence_client_two.subscribe :leave, &subscribe_callback
1340
+ presence_client_two.unsubscribe :leave, &subscribe_callback
1341
+
1342
+ presence_client_one.enter do
1343
+ presence_client_one.leave do
1344
+ EventMachine.add_timer(1) do
1345
+ stop_reactor
1346
+ end
1347
+ end
1348
+ end
1349
+ end
1350
+ end
1351
+ end
1241
1352
  end
1242
1353
 
1243
1354
  context 'REST #get' do