ably 1.0.6 → 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +14 -0
  3. data/.travis.yml +10 -8
  4. data/CHANGELOG.md +75 -3
  5. data/LICENSE +1 -3
  6. data/README.md +12 -7
  7. data/Rakefile +32 -0
  8. data/SPEC.md +1277 -835
  9. data/ably.gemspec +14 -9
  10. data/lib/ably/auth.rb +30 -4
  11. data/lib/ably/exceptions.rb +10 -4
  12. data/lib/ably/logger.rb +7 -1
  13. data/lib/ably/models/channel_state_change.rb +1 -1
  14. data/lib/ably/models/connection_state_change.rb +1 -1
  15. data/lib/ably/models/device_details.rb +87 -0
  16. data/lib/ably/models/device_push_details.rb +86 -0
  17. data/lib/ably/models/error_info.rb +23 -2
  18. data/lib/ably/models/idiomatic_ruby_wrapper.rb +4 -4
  19. data/lib/ably/models/protocol_message.rb +32 -2
  20. data/lib/ably/models/push_channel_subscription.rb +89 -0
  21. data/lib/ably/modules/conversions.rb +1 -1
  22. data/lib/ably/modules/encodeable.rb +1 -1
  23. data/lib/ably/modules/exception_codes.rb +128 -0
  24. data/lib/ably/modules/model_common.rb +15 -2
  25. data/lib/ably/modules/state_machine.rb +2 -2
  26. data/lib/ably/realtime.rb +1 -0
  27. data/lib/ably/realtime/auth.rb +1 -1
  28. data/lib/ably/realtime/channel.rb +24 -102
  29. data/lib/ably/realtime/channel/channel_manager.rb +2 -6
  30. data/lib/ably/realtime/channel/channel_state_machine.rb +2 -2
  31. data/lib/ably/realtime/channel/publisher.rb +74 -0
  32. data/lib/ably/realtime/channel/push_channel.rb +62 -0
  33. data/lib/ably/realtime/client.rb +91 -3
  34. data/lib/ably/realtime/client/incoming_message_dispatcher.rb +6 -2
  35. data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +1 -1
  36. data/lib/ably/realtime/connection.rb +34 -20
  37. data/lib/ably/realtime/connection/connection_manager.rb +25 -9
  38. data/lib/ably/realtime/connection/websocket_transport.rb +1 -1
  39. data/lib/ably/realtime/presence.rb +4 -4
  40. data/lib/ably/realtime/presence/members_map.rb +3 -3
  41. data/lib/ably/realtime/push.rb +40 -0
  42. data/lib/ably/realtime/push/admin.rb +61 -0
  43. data/lib/ably/realtime/push/channel_subscriptions.rb +108 -0
  44. data/lib/ably/realtime/push/device_registrations.rb +105 -0
  45. data/lib/ably/rest.rb +1 -0
  46. data/lib/ably/rest/channel.rb +53 -17
  47. data/lib/ably/rest/channel/push_channel.rb +62 -0
  48. data/lib/ably/rest/client.rb +154 -32
  49. data/lib/ably/rest/middleware/parse_message_pack.rb +17 -1
  50. data/lib/ably/rest/presence.rb +1 -0
  51. data/lib/ably/rest/push.rb +42 -0
  52. data/lib/ably/rest/push/admin.rb +54 -0
  53. data/lib/ably/rest/push/channel_subscriptions.rb +121 -0
  54. data/lib/ably/rest/push/device_registrations.rb +103 -0
  55. data/lib/ably/version.rb +7 -2
  56. data/spec/acceptance/realtime/auth_spec.rb +245 -17
  57. data/spec/acceptance/realtime/channel_history_spec.rb +26 -20
  58. data/spec/acceptance/realtime/channel_spec.rb +177 -59
  59. data/spec/acceptance/realtime/client_spec.rb +153 -0
  60. data/spec/acceptance/realtime/connection_failures_spec.rb +72 -6
  61. data/spec/acceptance/realtime/connection_spec.rb +129 -18
  62. data/spec/acceptance/realtime/message_spec.rb +36 -34
  63. data/spec/acceptance/realtime/presence_spec.rb +201 -167
  64. data/spec/acceptance/realtime/push_admin_spec.rb +736 -0
  65. data/spec/acceptance/realtime/push_spec.rb +27 -0
  66. data/spec/acceptance/rest/auth_spec.rb +41 -3
  67. data/spec/acceptance/rest/base_spec.rb +2 -2
  68. data/spec/acceptance/rest/channel_spec.rb +79 -4
  69. data/spec/acceptance/rest/channels_spec.rb +6 -0
  70. data/spec/acceptance/rest/client_spec.rb +129 -10
  71. data/spec/acceptance/rest/message_spec.rb +158 -6
  72. data/spec/acceptance/rest/push_admin_spec.rb +952 -0
  73. data/spec/acceptance/rest/push_spec.rb +25 -0
  74. data/spec/acceptance/rest/time_spec.rb +1 -1
  75. data/spec/run_parallel_tests +33 -0
  76. data/spec/spec_helper.rb +1 -1
  77. data/spec/support/debug_failure_helper.rb +9 -5
  78. data/spec/support/test_app.rb +2 -2
  79. data/spec/unit/logger_spec.rb +10 -3
  80. data/spec/unit/models/device_details_spec.rb +102 -0
  81. data/spec/unit/models/device_push_details_spec.rb +101 -0
  82. data/spec/unit/models/error_info_spec.rb +51 -3
  83. data/spec/unit/models/message_spec.rb +17 -2
  84. data/spec/unit/models/presence_message_spec.rb +1 -1
  85. data/spec/unit/models/push_channel_subscription_spec.rb +86 -0
  86. data/spec/unit/modules/enum_spec.rb +1 -1
  87. data/spec/unit/realtime/client_spec.rb +13 -1
  88. data/spec/unit/realtime/connection_spec.rb +1 -1
  89. data/spec/unit/realtime/push_channel_spec.rb +36 -0
  90. data/spec/unit/rest/channel_spec.rb +8 -1
  91. data/spec/unit/rest/client_spec.rb +30 -0
  92. data/spec/unit/rest/push_channel_spec.rb +36 -0
  93. metadata +95 -26
@@ -112,22 +112,26 @@ describe Ably::Realtime::Channel, '#history', :event_machine do
112
112
 
113
113
  context 'in multiple ProtocolMessages', em_timeout: (30 / 10) + 5 do
114
114
  it 'retrieves limited history forwards with pagination' do
115
- messages_sent.times do |index|
116
- EventMachine.add_timer(index.to_f / rate_per_second) do
117
- channel.publish('event', "history#{index}") do
118
- next unless index == messages_sent - 1
119
- ensure_message_history_direction_and_paging_is_correct :forwards
115
+ channel.attach do
116
+ messages_sent.times do |index|
117
+ EventMachine.add_timer(index.to_f / rate_per_second) do
118
+ channel.publish('event', "history#{index}") do
119
+ next unless index == messages_sent - 1
120
+ ensure_message_history_direction_and_paging_is_correct :forwards
121
+ end
120
122
  end
121
123
  end
122
124
  end
123
125
  end
124
126
 
125
127
  it 'retrieves limited history backwards with pagination' do
126
- messages_sent.times.to_a.reverse.each do |index|
127
- EventMachine.add_timer((messages_sent - index).to_f / rate_per_second) do
128
- channel.publish('event', "history#{index}") do
129
- next unless index == 0
130
- ensure_message_history_direction_and_paging_is_correct :backwards if index == 0
128
+ channel.attach do
129
+ messages_sent.times.to_a.reverse.each do |index|
130
+ EventMachine.add_timer((messages_sent - index).to_f / rate_per_second) do
131
+ channel.publish('event', "history#{index}") do
132
+ next unless index == 0
133
+ ensure_message_history_direction_and_paging_is_correct :backwards if index == 0
134
+ end
131
135
  end
132
136
  end
133
137
  end
@@ -139,18 +143,20 @@ describe Ably::Realtime::Channel, '#history', :event_machine do
139
143
  let(:messages_per_batch) { 10 }
140
144
 
141
145
  it 'return the same results with unique matching message IDs' do
142
- batches.times do |batch|
143
- EventMachine.add_timer(batch.to_f / batches.to_f) do
144
- messages_per_batch.times { channel.publish('event', 'data') }
146
+ channel.attach do
147
+ batches.times do |batch|
148
+ EventMachine.add_timer(batch.to_f / batches.to_f) do
149
+ messages_per_batch.times { |index| channel.publish('event') }
150
+ end
145
151
  end
146
- end
147
152
 
148
- channel.subscribe('event') do |message|
149
- messages << message
150
- if messages.count == batches * messages_per_batch
151
- channel.history do |page|
152
- expect(page.items.map(&:id).sort).to eql(messages.map(&:id).sort)
153
- stop_reactor
153
+ channel.subscribe('event') do |message|
154
+ messages << message
155
+ if messages.count == batches * messages_per_batch
156
+ channel.history do |page|
157
+ expect(page.items.map(&:id).sort).to eql(messages.map(&:id).sort)
158
+ stop_reactor
159
+ end
154
160
  end
155
161
  end
156
162
  end
@@ -13,6 +13,9 @@ describe Ably::Realtime::Channel, :event_machine do
13
13
  let(:channel) { client.channel(channel_name) }
14
14
  let(:messages) { [] }
15
15
 
16
+ let(:sub_client) { auto_close Ably::Realtime::Client.new(client_options) }
17
+ let(:sub_channel) { sub_client.channel(channel_name) }
18
+
16
19
  def disconnect_transport
17
20
  connection.transport.unbind
18
21
  end
@@ -276,8 +279,7 @@ describe Ably::Realtime::Channel, :event_machine do
276
279
  key: restricted_api_key,
277
280
  log_level: :fatal,
278
281
  use_token_auth: true,
279
- # TODO: Use wildcard / default when intersection issue resolved, realtime#780
280
- default_token_params: { capability: { "canpublish:foo" => ["publish"] } }
282
+ default_token_params: { capability: { "canpublish:foo" => ["*"] } }
281
283
  )
282
284
  end
283
285
  let(:restricted_client) do
@@ -726,7 +728,7 @@ describe Ably::Realtime::Channel, :event_machine do
726
728
  let(:name) { random_str }
727
729
  let(:data) { random_str }
728
730
 
729
- context 'when attached' do
731
+ context 'when channel is attached (#RTL6c1)' do
730
732
  it 'publishes messages' do
731
733
  channel.attach do
732
734
  3.times { channel.publish('event', payload) }
@@ -738,81 +740,173 @@ describe Ably::Realtime::Channel, :event_machine do
738
740
  end
739
741
  end
740
742
 
741
- context 'when not yet attached' do
742
- it 'publishes queued messages once attached' do
743
- 3.times { channel.publish('event', random_str) }
744
- channel.subscribe do |message|
745
- messages << message if message.name == 'event'
746
- stop_reactor if messages.count == 3
743
+ context 'when channel is not attached in state Initializing (#RTL6c1)' do
744
+ it 'publishes messages immediately and does not implicitly attach (#RTL6c1)' do
745
+ sub_channel.attach do
746
+ sub_channel.subscribe do |message|
747
+ messages << message if message.name == 'event'
748
+ if messages.count == 3
749
+ EventMachine.add_timer(1) do
750
+ expect(channel.state).to eq(:initialized)
751
+ stop_reactor
752
+ end
753
+ end
754
+ end
755
+ 3.times { channel.publish('event', random_str) }
747
756
  end
748
757
  end
758
+ end
749
759
 
750
- it 'publishes queued messages within a single protocol message' do
751
- 3.times { channel.publish('event', random_str) }
752
- channel.subscribe do |message|
753
- messages << message if message.name == 'event'
754
- next unless messages.length == 3
755
-
756
- # All 3 messages should be batched into a single Protocol Message by the client library
757
- # message.id = "{protocol_message.id}:{protocol_message_index}"
758
- # Check that all messages share the same protocol_message.id
759
- message_id = messages.map { |msg| msg.id.split(':')[0...-1].join(':') }
760
- expect(message_id.uniq.count).to eql(1)
761
-
762
- # Check that messages use index 0,1,2 in the ID
763
- message_indexes = messages.map { |msg| msg.id.split(':').last }
764
- expect(message_indexes).to include("0", "1", "2")
765
- stop_reactor
760
+ context 'when channel is Attaching (#RTL6c1)' do
761
+ it 'publishes messages immediately (#RTL6c1)' do
762
+ sub_channel.attach do
763
+ channel.once(:attaching) do
764
+ outgoing_message_count = 0
765
+ client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
766
+ if protocol_message.action == :message
767
+ raise "Expected channel state to be attaching when publishing messages, not #{channel.state}" unless channel.attaching?
768
+ outgoing_message_count += protocol_message.messages.count
769
+ end
770
+ end
771
+ sub_channel.subscribe do |message|
772
+ messages << message if message.name == 'event'
773
+ if messages.count == 3
774
+ expect(outgoing_message_count).to eql(3)
775
+ stop_reactor
776
+ end
777
+ end
778
+ 3.times { channel.publish('event', random_str) }
779
+ end
780
+ channel.attach
766
781
  end
767
782
  end
783
+ end
768
784
 
769
- context 'with :queue_messages client option set to false' do
770
- let(:client_options) { default_options.merge(queue_messages: false) }
771
-
772
- context 'and connection state initialized' do
773
- it 'fails the deferrable' do
774
- expect(client.connection).to be_initialized
775
- channel.publish('event').errback do |error|
776
- expect(error).to be_a(Ably::Exceptions::MessageQueueingDisabled)
777
- stop_reactor
785
+ context 'when channel is Detaching (#RTL6c1)' do
786
+ it 'publishes messages immediately (#RTL6c1)' do
787
+ sub_channel.attach do
788
+ channel.attach do
789
+ channel.once(:detaching) do
790
+ outgoing_message_count = 0
791
+ client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
792
+ if protocol_message.action == :message
793
+ raise "Expected channel state to be attaching when publishing messages, not #{channel.state}" unless channel.detaching?
794
+ outgoing_message_count += protocol_message.messages.count
795
+ end
796
+ end
797
+ sub_channel.subscribe do |message|
798
+ messages << message if message.name == 'event'
799
+ if messages.count == 3
800
+ expect(outgoing_message_count).to eql(3)
801
+ stop_reactor
802
+ end
803
+ end
804
+ 3.times { channel.publish('event', random_str) }
778
805
  end
806
+ channel.detach
779
807
  end
780
808
  end
809
+ end
810
+ end
781
811
 
782
- context 'and connection state connecting' do
783
- it 'fails the deferrable' do
784
- client.connect
785
- EventMachine.next_tick do
786
- expect(client.connection).to be_connecting
787
- channel.publish('event').errback do |error|
788
- expect(error).to be_a(Ably::Exceptions::MessageQueueingDisabled)
789
- stop_reactor
812
+ context 'when channel is Detached (#RTL6c1)' do
813
+ it 'publishes messages immediately (#RTL6c1)' do
814
+ sub_channel.attach do
815
+ channel.attach
816
+ channel.once(:attached) do
817
+ channel.once(:detached) do
818
+ outgoing_message_count = 0
819
+ client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
820
+ if protocol_message.action == :message
821
+ raise "Expected channel state to be attaching when publishing messages, not #{channel.state}" unless channel.detached?
822
+ outgoing_message_count += protocol_message.messages.count
823
+ end
824
+ end
825
+ sub_channel.subscribe do |message|
826
+ messages << message if message.name == 'event'
827
+ if messages.count == 3
828
+ expect(outgoing_message_count).to eql(3)
829
+ stop_reactor
830
+ end
790
831
  end
832
+ 3.times { channel.publish('event', random_str) }
833
+ end
834
+ channel.detach
835
+ end
836
+ end
837
+ end
838
+ end
839
+
840
+ context 'with :queue_messages client option set to false (#RTL6c4)' do
841
+ let(:client_options) { default_options.merge(queue_messages: false) }
842
+
843
+ context 'and connection state connected (#RTL6c4)' do
844
+ it 'publishes the message' do
845
+ client.connection.once(:connected) do
846
+ channel.publish('event')
847
+ stop_reactor
848
+ end
849
+ end
850
+ end
851
+
852
+ context 'and connection state initialized (#RTL6c4)' do
853
+ it 'fails the deferrable' do
854
+ expect(client.connection).to be_initialized
855
+ channel.publish('event').errback do |error|
856
+ expect(error).to be_a(Ably::Exceptions::MessageQueueingDisabled)
857
+ stop_reactor
858
+ end
859
+ end
860
+ end
861
+
862
+ context 'and connection state connecting (#RTL6c4)' do
863
+ it 'fails the deferrable' do
864
+ client.connect
865
+ EventMachine.next_tick do
866
+ expect(client.connection).to be_connecting
867
+ channel.publish('event').errback do |error|
868
+ expect(error).to be_a(Ably::Exceptions::MessageQueueingDisabled)
869
+ stop_reactor
791
870
  end
792
871
  end
793
872
  end
873
+ end
794
874
 
795
- context 'and connection state disconnected' do
875
+ [:disconnected, :suspended, :closing, :closed].each do |invalid_connection_state|
876
+ context "and connection state #{invalid_connection_state} (#RTL6c4)" do
796
877
  let(:client_options) { default_options.merge(queue_messages: false) }
797
878
  it 'fails the deferrable' do
798
879
  client.connection.once(:connected) do
799
- client.connection.once(:disconnected) do
800
- expect(client.connection).to be_disconnected
880
+ client.connection.once(invalid_connection_state) do
881
+ expect(client.connection.state).to eq(invalid_connection_state)
801
882
  channel.publish('event').errback do |error|
802
883
  expect(error).to be_a(Ably::Exceptions::MessageQueueingDisabled)
803
884
  stop_reactor
804
885
  end
805
886
  end
806
- client.connection.transition_state_machine :disconnected
887
+ if invalid_connection_state == :closed
888
+ connection.close
889
+ else
890
+ client.connection.transition_state_machine invalid_connection_state
891
+ end
807
892
  end
808
893
  end
809
894
  end
895
+ end
810
896
 
811
- context 'and connection state connected' do
812
- it 'publishes the message' do
813
- client.connection.once(:connected) do
814
- channel.publish('event')
815
- stop_reactor
897
+ context 'and the channel state is failed (#RTL6c4)' do
898
+ let(:client_options) { default_options.merge(queue_messages: false) }
899
+ it 'fails the deferrable' do
900
+ client.connection.once(:connected) do
901
+ channel.attach
902
+ channel.once(:attached) do
903
+ channel.once(:failed) do
904
+ channel.publish('event').errback do |error|
905
+ expect(error).to be_a(Ably::Exceptions::ChannelInactive)
906
+ stop_reactor
907
+ end
908
+ end
909
+ channel.transition_state_machine(:failed)
816
910
  end
817
911
  end
818
912
  end
@@ -914,9 +1008,6 @@ describe Ably::Realtime::Channel, :event_machine do
914
1008
 
915
1009
  it 'publishes the message without a name attribute in the payload' do
916
1010
  published = false
917
- channel.publish(nil, data) do
918
- published = true
919
- end
920
1011
 
921
1012
  channel.subscribe do |message|
922
1013
  expect(message.name).to be_nil
@@ -929,6 +1020,10 @@ describe Ably::Realtime::Channel, :event_machine do
929
1020
  end
930
1021
  end
931
1022
  end
1023
+
1024
+ channel.publish(nil, data) do
1025
+ published = true
1026
+ end
932
1027
  end
933
1028
  end
934
1029
 
@@ -937,9 +1032,6 @@ describe Ably::Realtime::Channel, :event_machine do
937
1032
 
938
1033
  it 'publishes the message without a data attribute in the payload' do
939
1034
  published = false
940
- channel.publish(name, nil) do
941
- published = true
942
- end
943
1035
 
944
1036
  channel.subscribe do |message|
945
1037
  expect(message.data).to be_nil
@@ -952,6 +1044,10 @@ describe Ably::Realtime::Channel, :event_machine do
952
1044
  end
953
1045
  end
954
1046
  end
1047
+
1048
+ channel.publish(name, nil) do
1049
+ published = true
1050
+ end
955
1051
  end
956
1052
  end
957
1053
 
@@ -1074,6 +1170,21 @@ describe Ably::Realtime::Channel, :event_machine do
1074
1170
  end
1075
1171
  end
1076
1172
 
1173
+ context 'with more than allowed messages in a single publish' do
1174
+ let(:channel_name) { random_str }
1175
+
1176
+ it 'rejects the publish' do
1177
+ messages = (Ably::Realtime::Connection::MAX_PROTOCOL_MESSAGE_BATCH_SIZE + 1).times.map do
1178
+ { name: 'foo' }
1179
+ end
1180
+
1181
+ channel.publish(messages).errback do |error|
1182
+ expect(error).to be_kind_of(Ably::Exceptions::InvalidRequest)
1183
+ stop_reactor
1184
+ end
1185
+ end
1186
+ end
1187
+
1077
1188
  context 'identified clients' do
1078
1189
  context 'when authenticated with a wildcard client_id' do
1079
1190
  let(:token) { Ably::Rest::Client.new(default_options).auth.request_token(client_id: '*') }
@@ -1887,6 +1998,8 @@ describe Ably::Realtime::Channel, :event_machine do
1887
1998
  end
1888
1999
 
1889
2000
  context '#resume (#RTL2f)' do
2001
+ let(:client_options) { default_options.merge(log_level: :fatal) }
2002
+
1890
2003
  it 'is false when a channel first attaches' do
1891
2004
  channel.attach
1892
2005
  channel.on(:attached) do |channel_state_change|
@@ -1953,8 +2066,12 @@ describe Ably::Realtime::Channel, :event_machine do
1953
2066
  end
1954
2067
 
1955
2068
  context 'moves to' do
1956
- %w(suspended detached failed).each do |channel_state|
2069
+ %w(suspended failed).each do |channel_state|
1957
2070
  context(channel_state) do
2071
+ let(:client) do
2072
+ auto_close Ably::Realtime::Client.new(default_options.merge(log_level: :error))
2073
+ end
2074
+
1958
2075
  specify 'all queued messages fail with NACK (#RTL11)' do
1959
2076
  channel.attach do
1960
2077
  # Move to disconnected
@@ -1981,7 +2098,8 @@ describe Ably::Realtime::Channel, :event_machine do
1981
2098
  specify 'all published messages awaiting an ACK do nothing (#RTL11a)' do
1982
2099
  connection_been_disconnected = false
1983
2100
 
1984
- channel.attach do
2101
+ channel.attach
2102
+ channel.once(:attached) do
1985
2103
  deferrable = channel.publish("foo")
1986
2104
  deferrable.errback do |error|
1987
2105
  fail "Message publish should not fail"
@@ -12,6 +12,7 @@ describe Ably::Realtime::Client, :event_machine do
12
12
  let(:auth_params) { subject.auth.auth_params_sync }
13
13
 
14
14
  subject { auto_close Ably::Realtime::Client.new(client_options) }
15
+ let(:sub_client) { auto_close Ably::Realtime::Client.new(client_options) }
15
16
 
16
17
  context 'initialization' do
17
18
  context 'basic auth' do
@@ -24,6 +25,22 @@ describe Ably::Realtime::Client, :event_machine do
24
25
  end
25
26
  end
26
27
 
28
+ context 'with an invalid API key' do
29
+ let(:custom_logger_object) { TestLogger.new }
30
+ let(:client) { Ably::Realtime::Client.new(client_options.merge(key: 'app.key:secret', logger: custom_logger_object)) }
31
+
32
+ it 'logs an entry with a help href url matching the code #TI5' do
33
+ client.connect
34
+ client.connection.once(:failed) do
35
+ expect(custom_logger_object.logs.find do |severity, message|
36
+ next unless %w(fatal error).include?(severity.to_s)
37
+ message.match(%r{https://help.ably.io/error/40400})
38
+ end).to_not be_nil
39
+ stop_reactor
40
+ end
41
+ end
42
+ end
43
+
27
44
  context ':tls option' do
28
45
  context 'set to false to force a plain-text connection' do
29
46
  let(:client_options) { default_options.merge(tls: false, log_level: :none) }
@@ -281,5 +298,141 @@ describe Ably::Realtime::Client, :event_machine do
281
298
  end
282
299
  end
283
300
  end
301
+
302
+ context '#publish (#TBC)' do
303
+ let(:channel_name) { random_str }
304
+ let(:channel) { subject.channel(channel_name) }
305
+ let(:sub_channel) { sub_client.channel(channel_name) }
306
+ let(:event_name) { random_str }
307
+ let(:data) { random_str }
308
+ let(:extras) { { 'push' => { 'notification' => { 'title' => 'Testing' } } } }
309
+ let(:message) { Ably::Models::Message.new(name: event_name, data: data) }
310
+
311
+ specify 'publishing a message implicity connects and publishes the message successfully on the provided channel' do
312
+ sub_channel.attach do
313
+ sub_channel.subscribe do |msg|
314
+ expect(msg.name).to eql(event_name)
315
+ expect(msg.data).to eql(data)
316
+ stop_reactor
317
+ end
318
+
319
+ subject.publish channel_name, event_name, data
320
+ end
321
+ end
322
+
323
+ specify 'publishing does not result in a channel being created' do
324
+ subject.publish channel_name, event_name, data
325
+ subject.channels.fetch(channel_name) do
326
+ # Block called if channel does not exist
327
+ EventMachine.add_timer(1) do
328
+ subject.channels.fetch(channel_name) do
329
+ # Block called if channel does not exist
330
+ stop_reactor
331
+ end
332
+ end
333
+ end
334
+ end
335
+
336
+ context 'with extras' do
337
+ let(:channel_name) { "pushenabled:#{random_str}" }
338
+
339
+ specify 'publishing supports extras' do
340
+ sub_channel.attach do
341
+ sub_channel.subscribe do |msg|
342
+ expect(msg.extras).to eql(extras)
343
+ stop_reactor
344
+ end
345
+
346
+ subject.publish channel_name, event_name, {}, extras: extras
347
+ end
348
+ end
349
+ end
350
+
351
+ specify 'publishing supports an array of Message objects' do
352
+ sub_channel.attach do
353
+ sub_channel.subscribe do |msg|
354
+ expect(msg.name).to eql(event_name)
355
+ expect(msg.data).to eql(data)
356
+ stop_reactor
357
+ end
358
+
359
+ subject.publish channel_name, [message]
360
+ end
361
+ end
362
+
363
+ specify 'publishing supports an array of Hash objects' do
364
+ sub_channel.attach do
365
+ sub_channel.subscribe do |msg|
366
+ expect(msg.name).to eql(event_name)
367
+ expect(msg.data).to eql(data)
368
+ stop_reactor
369
+ end
370
+
371
+ subject.publish channel_name, [name: event_name, data: data]
372
+ end
373
+ end
374
+
375
+ specify 'publishing on a closed connection fails' do
376
+ subject.connection.once(:connected) do
377
+ subject.connection.once(:closed) do
378
+ subject.publish(channel_name, name: event_name).errback do |error|
379
+ expect(error).to be_kind_of(Ably::Exceptions::MessageQueueingDisabled)
380
+ stop_reactor
381
+ end
382
+ end
383
+ connection.close
384
+ end
385
+ end
386
+
387
+ context 'queue_messages ClientOption' do
388
+ context 'when true' do
389
+ subject { auto_close Ably::Realtime::Client.new(client_options.merge(auto_connect: false)) }
390
+
391
+ it 'will queue messages whilst connecting and publish once connected' do
392
+ sub_channel.attach do
393
+ sub_channel.subscribe do |msg|
394
+ expect(msg.name).to eql(event_name)
395
+ stop_reactor
396
+ end
397
+ subject.connection.once(:connecting) do
398
+ subject.publish channel_name, event_name
399
+ end
400
+ subject.connection.connect
401
+ end
402
+ end
403
+ end
404
+
405
+ context 'when false' do
406
+ subject { auto_close Ably::Realtime::Client.new(client_options.merge(auto_connect: false, queue_messages: false)) }
407
+
408
+ it 'will reject messages on an initializing connection' do
409
+ sub_channel.attach do
410
+ subject.connection.once(:connecting) do
411
+ subject.publish(channel_name, event_name).errback do |error|
412
+ expect(error).to be_kind_of(Ably::Exceptions::MessageQueueingDisabled)
413
+ stop_reactor
414
+ end
415
+ end
416
+ subject.connection.connect
417
+ end
418
+ end
419
+ end
420
+ end
421
+
422
+ context 'with more than allowed messages in a single publish' do
423
+ let(:channel_name) { random_str }
424
+
425
+ it 'rejects the publish' do
426
+ messages = (Ably::Realtime::Connection::MAX_PROTOCOL_MESSAGE_BATCH_SIZE + 1).times.map do
427
+ { name: 'foo' }
428
+ end
429
+
430
+ subject.publish(channel_name, messages).errback do |error|
431
+ expect(error).to be_kind_of(Ably::Exceptions::InvalidRequest)
432
+ stop_reactor
433
+ end
434
+ end
435
+ end
436
+ end
284
437
  end
285
438
  end