ably 1.1.7 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/check.yml +1 -1
  3. data/CHANGELOG.md +99 -0
  4. data/COPYRIGHT +1 -1
  5. data/README.md +2 -2
  6. data/SPEC.md +0 -7
  7. data/UPDATING.md +30 -0
  8. data/ably.gemspec +11 -24
  9. data/lib/ably/auth.rb +8 -8
  10. data/lib/ably/logger.rb +4 -4
  11. data/lib/ably/models/channel_options.rb +97 -0
  12. data/lib/ably/models/connection_details.rb +8 -2
  13. data/lib/ably/models/delta_extras.rb +29 -0
  14. data/lib/ably/models/device_details.rb +1 -1
  15. data/lib/ably/models/error_info.rb +6 -2
  16. data/lib/ably/models/idiomatic_ruby_wrapper.rb +4 -0
  17. data/lib/ably/models/message.rb +14 -3
  18. data/lib/ably/models/protocol_message.rb +23 -14
  19. data/lib/ably/models/token_details.rb +7 -2
  20. data/lib/ably/models/token_request.rb +1 -1
  21. data/lib/ably/modules/ably.rb +1 -1
  22. data/lib/ably/modules/channels_collection.rb +22 -2
  23. data/lib/ably/modules/conversions.rb +34 -0
  24. data/lib/ably/realtime/auth.rb +2 -2
  25. data/lib/ably/realtime/channel/channel_manager.rb +16 -4
  26. data/lib/ably/realtime/channel/channel_state_machine.rb +10 -1
  27. data/lib/ably/realtime/channel/publisher.rb +3 -2
  28. data/lib/ably/realtime/channel.rb +54 -22
  29. data/lib/ably/realtime/channels.rb +1 -1
  30. data/lib/ably/realtime/connection/connection_manager.rb +13 -4
  31. data/lib/ably/realtime/connection/connection_state_machine.rb +4 -0
  32. data/lib/ably/realtime/connection.rb +0 -3
  33. data/lib/ably/rest/channel.rb +28 -35
  34. data/lib/ably/rest/client.rb +23 -8
  35. data/lib/ably/rest/middleware/encoder.rb +1 -1
  36. data/lib/ably/rest/middleware/exceptions.rb +1 -1
  37. data/lib/ably/rest/middleware/external_exceptions.rb +1 -1
  38. data/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +1 -1
  39. data/lib/ably/rest/middleware/logger.rb +1 -1
  40. data/lib/ably/rest/middleware/parse_json.rb +1 -1
  41. data/lib/ably/rest/middleware/parse_message_pack.rb +1 -1
  42. data/lib/ably/util/crypto.rb +1 -1
  43. data/lib/ably/version.rb +2 -2
  44. data/spec/acceptance/realtime/channel_spec.rb +458 -27
  45. data/spec/acceptance/realtime/channels_spec.rb +59 -7
  46. data/spec/acceptance/realtime/connection_failures_spec.rb +56 -1
  47. data/spec/acceptance/realtime/connection_spec.rb +270 -1
  48. data/spec/acceptance/realtime/message_spec.rb +77 -0
  49. data/spec/acceptance/realtime/presence_spec.rb +18 -1
  50. data/spec/acceptance/rest/auth_spec.rb +18 -0
  51. data/spec/acceptance/rest/channel_spec.rb +73 -11
  52. data/spec/acceptance/rest/channels_spec.rb +23 -6
  53. data/spec/acceptance/rest/client_spec.rb +3 -3
  54. data/spec/acceptance/rest/message_spec.rb +61 -3
  55. data/spec/lib/unit/models/channel_options_spec.rb +52 -0
  56. data/spec/run_parallel_tests +2 -7
  57. data/spec/support/test_app.rb +1 -1
  58. data/spec/unit/logger_spec.rb +6 -14
  59. data/spec/unit/models/delta_extras_spec.rb +14 -0
  60. data/spec/unit/models/error_info_spec.rb +17 -1
  61. data/spec/unit/models/message_spec.rb +38 -0
  62. data/spec/unit/models/protocol_message_spec.rb +77 -27
  63. data/spec/unit/models/token_details_spec.rb +14 -0
  64. data/spec/unit/realtime/channel_spec.rb +2 -1
  65. data/spec/unit/realtime/channels_spec.rb +53 -15
  66. data/spec/unit/rest/channel_spec.rb +40 -7
  67. data/spec/unit/rest/channels_spec.rb +81 -14
  68. data/spec/unit/rest/client_spec.rb +27 -0
  69. metadata +46 -11
@@ -570,7 +570,7 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
570
570
  end
571
571
 
572
572
  context 'when DISCONNECTED ProtocolMessage received from the server' do
573
- it 'reconnects automatically and immediately' do
573
+ it 'reconnects automatically and immediately (#RTN15a)' do
574
574
  fail_if_suspended_or_failed
575
575
 
576
576
  connection.once(:connected) do
@@ -590,6 +590,61 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
590
590
  end
591
591
  end
592
592
 
593
+ context 'when protocolMessage contains token error' do
594
+ context "library does not have a means to renew the token (#RTN15h1)" do
595
+ let(:auth_url) { 'https://echo.ably.io/createJWT' }
596
+ let(:token) { Faraday.get("#{auth_url}?keyName=#{key_name}&keySecret=#{key_secret}").body }
597
+ let(:client_options) { default_options.merge(token: token, log_level: :none) }
598
+
599
+ let(:error_message) { 'error_message' }
600
+
601
+ it 'moves connection state to failed' do
602
+ connection.on(:failed) do |connection_state_change|
603
+ expect(connection.error_reason.message).to eq(error_message)
604
+ stop_reactor
605
+ end
606
+
607
+ connection.on(:connected) do
608
+ protocol_message = Ably::Models::ProtocolMessage.new(action: Ably::Models::ProtocolMessage::ACTION.Disconnected.to_i, error: { code: 40140, message: error_message })
609
+ connection.__incoming_protocol_msgbus__.publish :protocol_message, protocol_message
610
+ end
611
+
612
+ connection.connect
613
+ end
614
+ end
615
+
616
+ context "library have a means to renew the token (#RTN15h2)" do
617
+ let(:client_options) { default_options.merge(log_level: :none) }
618
+ let(:error_message) { 'error_message' }
619
+
620
+ def send_disconnect_message
621
+ protocol_message = Ably::Models::ProtocolMessage.new(action: Ably::Models::ProtocolMessage::ACTION.Disconnected.to_i, error: { code: 40140, message: error_message })
622
+ connection.__incoming_protocol_msgbus__.publish :protocol_message, protocol_message
623
+ end
624
+
625
+ it 'attempts to reconnect' do
626
+ connection.on(:failed) do |connection_state_change|
627
+ raise "Connection shouldn't be failed"
628
+ end
629
+
630
+ connection.on(:connected) do
631
+ connection.once(:connecting) do
632
+ connection.once(:disconnected) do
633
+ expect(connection.error_reason.message).to eq(error_message)
634
+ stop_reactor
635
+ end
636
+
637
+ send_disconnect_message
638
+ end
639
+
640
+ send_disconnect_message
641
+ end
642
+
643
+ connection.connect
644
+ end
645
+ end
646
+ end
647
+
593
648
  context 'connection state freshness is monitored' do
594
649
  it 'resumes connections when disconnected within the connection_state_ttl period (#RTN15g)' do
595
650
  connection.once(:connected) do
@@ -482,6 +482,138 @@ 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
+
505
+ context 'when explicitly reconnecting disconnected/suspended connection in retry (#RTN11c)' do
506
+ let(:close_connection_proc) do
507
+ lambda do
508
+ EventMachine.add_timer(0.001) do
509
+ if connection.transport.nil?
510
+ close_connection_proc.call
511
+ else
512
+ connection.transport.close_connection_after_writing
513
+ end
514
+ end
515
+ end
516
+ end
517
+
518
+ context 'when suspended' do
519
+ let(:suspended_retry_timeout) { 60 }
520
+ let(:client_options) do
521
+ default_options.merge(
522
+ disconnected_retry_timeout: 0.02,
523
+ suspended_retry_timeout: suspended_retry_timeout,
524
+ connection_state_ttl: 0
525
+ )
526
+ end
527
+
528
+ it 'reconnects immediately' do
529
+ connection.once(:connecting) { close_connection_proc.call }
530
+
531
+ connection.on(:suspended) do |connection_state_change|
532
+ if connection_state_change.retry_in.zero?
533
+ # Exhausting immediate retries
534
+ connection.once(:connecting) { close_connection_proc.call }
535
+ else
536
+ suspended_at = Time.now.to_f
537
+ connection.on(:connected) do
538
+ expect(connection_state_change.retry_in).to eq(suspended_retry_timeout)
539
+ expect(connection.state).to eq(:connected)
540
+ expect(Time.now.to_f).to be_within(4).of(suspended_at)
541
+ stop_reactor
542
+ end
543
+ end
544
+
545
+ connection.connect
546
+ end
547
+
548
+ connection.connect
549
+ end
550
+ end
551
+
552
+ context 'when disconnected' do
553
+ let(:retry_timeout) { 60 }
554
+ let(:client_options) do
555
+ default_options.merge(
556
+ disconnected_retry_timeout: retry_timeout,
557
+ suspended_retry_timeout: retry_timeout,
558
+ connection_state_ttl: 0
559
+ )
560
+ end
561
+
562
+ it 'reconnects immediately' do
563
+ connection.once(:connected) do
564
+ connection.on(:disconnected) do |connection_state_change|
565
+ disconnected_at = Time.now.to_f
566
+ connection.on(:connected) do
567
+ if connection_state_change.retry_in.zero?
568
+ # Exhausting immediate retries
569
+ close_connection_proc.call
570
+ else
571
+ expect(connection_state_change.retry_in).to eq(retry_timeout)
572
+ expect(connection.state).to eq(:connected)
573
+ expect(Time.now.to_f).to be_within(4).of(disconnected_at)
574
+ stop_reactor
575
+ end
576
+ end
577
+ connection.connect
578
+ end
579
+
580
+ close_connection_proc.call
581
+ end
582
+
583
+ connection.connect
584
+ end
585
+ end
586
+ end
587
+
588
+ context 'when reconnecting a failed connection' do
589
+ let(:channel) { client.channel(random_str) }
590
+ let(:client_options) { default_options.merge(log_level: :none) }
591
+
592
+ it 'transitions all channels state to initialized and cleares error_reason' do
593
+ connection.on(:failed) do |connection_state_change|
594
+ expect(connection.error_reason).to be_a(Ably::Exceptions::BaseAblyException)
595
+ expect(channel.error_reason).to be_a(Ably::Exceptions::BaseAblyException)
596
+ expect(channel).to be_failed
597
+
598
+ connection.on(:connected) do
599
+ expect(connection.error_reason).to eq(nil)
600
+ expect(channel).to be_initialized
601
+ expect(channel.error_reason).to eq(nil)
602
+ stop_reactor
603
+ end
604
+
605
+ connection.connect
606
+ end
607
+
608
+ connection.connect do
609
+ channel.attach do
610
+ error = Ably::Exceptions::ConnectionFailed.new('forced failure', 500, 50000)
611
+ client.connection.manager.error_received_from_server error
612
+ end
613
+ end
614
+ end
615
+ end
616
+
485
617
  context 'with invalid auth details' do
486
618
  let(:client_options) { default_options.merge(key: 'this.is:invalid', log_level: :none) }
487
619
 
@@ -693,6 +825,18 @@ describe Ably::Realtime::Connection, :event_machine do
693
825
  end
694
826
 
695
827
  context '#close' do
828
+ let(:close_connection_proc) do
829
+ lambda do
830
+ EventMachine.add_timer(0.001) do
831
+ if connection.transport.nil?
832
+ close_connection_proc.call
833
+ else
834
+ connection.transport.close_connection_after_writing
835
+ end
836
+ end
837
+ end
838
+ end
839
+
696
840
  it 'returns a SafeDeferrable that catches exceptions in callbacks and logs them' do
697
841
  connection.connect do
698
842
  expect(connection.close).to be_a(Ably::Util::SafeDeferrable)
@@ -754,6 +898,56 @@ describe Ably::Realtime::Connection, :event_machine do
754
898
  end
755
899
  end
756
900
 
901
+ context ':connecting RTN12f' do
902
+ context ":connected does not arrive when trying to close" do
903
+ it 'moves to closed' do
904
+ connection.on(:closed) do |state_change|
905
+ state_changes = connection.state_history.map { |t| t[:state].to_sym }
906
+
907
+ expect(state_changes).to eq([:connecting, :closing, :closed])
908
+ stop_reactor
909
+ end
910
+
911
+ connection.on(:connecting) do
912
+ connection.close
913
+ connection.__outgoing_protocol_msgbus__.unsubscribe
914
+ end
915
+
916
+ connection.connect
917
+ end
918
+ end
919
+
920
+ context ":connected arrive when trying to close" do
921
+ let(:protocol_message_attributes) do
922
+ {
923
+ action: Ably::Models::ProtocolMessage::ACTION.Connected.to_i,
924
+ connection_serial: 55,
925
+ connection_details: {
926
+ max_idle_interval: 2 * 1000
927
+ }
928
+ }
929
+ end
930
+
931
+ it 'moves to connected and then to closed' do
932
+ connection.on(:closed) do |state_change|
933
+ state_changes = connection.state_history.map { |t| t[:state].to_sym }
934
+
935
+ expect(state_changes).to eq([:connecting, :connected, :closing, :closed])
936
+ stop_reactor
937
+ end
938
+
939
+ connection.on(:connecting) do
940
+ connection.__outgoing_protocol_msgbus__.unsubscribe
941
+
942
+ connection.__incoming_protocol_msgbus__.publish :protocol_message, Ably::Models::ProtocolMessage.new(protocol_message_attributes)
943
+ connection.close
944
+ end
945
+
946
+ connection.connect
947
+ end
948
+ end
949
+ end
950
+
757
951
  context ':connected' do
758
952
  it 'changes the connection state to :closing and waits for the server to confirm connection is :closed with a ProtocolMessage' do
759
953
  connection.on(:connected) do
@@ -800,6 +994,81 @@ describe Ably::Realtime::Connection, :event_machine do
800
994
  end
801
995
  end
802
996
  end
997
+
998
+ context ':suspended RTN12d' do
999
+ let(:suspended_retry_timeout) { 60 }
1000
+ let(:client_options) do
1001
+ default_options.merge(
1002
+ disconnected_retry_timeout: 0.02,
1003
+ suspended_retry_timeout: suspended_retry_timeout,
1004
+ connection_state_ttl: 0
1005
+ )
1006
+ end
1007
+
1008
+ it 'immediatly closes connection' do
1009
+ connection.on(:connecting) { close_connection_proc.call }
1010
+ connection.on(:suspended) do |connection_state_change|
1011
+ if connection_state_change.retry_in.zero?
1012
+ # Exhausting immediate retries
1013
+ connection.once(:connecting) { close_connection_proc.call }
1014
+ else
1015
+ suspended_at = Time.now.to_f
1016
+ connection.on(:closed) do
1017
+ expect(connection_state_change.retry_in).to eq(suspended_retry_timeout)
1018
+ expect(connection.state).to eq(:closed)
1019
+ expect(Time.now.to_f).to be_within(4).of(suspended_at)
1020
+ stop_reactor
1021
+ end
1022
+
1023
+ connection.close
1024
+ end
1025
+
1026
+ connection.connect
1027
+ end
1028
+
1029
+ connection.connect
1030
+ end
1031
+ end
1032
+
1033
+ context ':disconnected RTN12d' do
1034
+ let(:retry_timeout) { 60 }
1035
+ let(:client_options) do
1036
+ default_options.merge(
1037
+ disconnected_retry_timeout: retry_timeout,
1038
+ suspended_retry_timeout: retry_timeout,
1039
+ connection_state_ttl: 0
1040
+ )
1041
+ end
1042
+
1043
+ it 'immediatly closes connection' do
1044
+ connection.once(:connected) do
1045
+ connection.on(:disconnected) do |connection_state_change|
1046
+ disconnected_at = Time.now.to_f
1047
+ connection.on(:connected) do
1048
+ if connection_state_change.retry_in.zero?
1049
+ # Exhausting immediate retries
1050
+ close_connection_proc.call
1051
+ else
1052
+ connection.once(:closed) do
1053
+ expect(connection_state_change.retry_in).to eq(retry_timeout)
1054
+ expect(connection.state).to eq(:closed)
1055
+ expect(Time.now.to_f).to be_within(4).of(disconnected_at)
1056
+ stop_reactor
1057
+ end
1058
+
1059
+ connection.close
1060
+ end
1061
+ end
1062
+
1063
+ connection.connect
1064
+ end
1065
+
1066
+ close_connection_proc.call
1067
+ end
1068
+
1069
+ connection.connect
1070
+ end
1071
+ end
803
1072
  end
804
1073
  end
805
1074
 
@@ -1834,7 +2103,7 @@ describe Ably::Realtime::Connection, :event_machine do
1834
2103
  it 'sends the protocol version param v (#G4, #RTN2f)' do
1835
2104
  expect(EventMachine).to receive(:connect) do |host, port, transport, object, url|
1836
2105
  uri = URI.parse(url)
1837
- expect(CGI::parse(uri.query)['v'][0]).to eql('1.1')
2106
+ expect(CGI::parse(uri.query)['v'][0]).to eql('1.2')
1838
2107
  stop_reactor
1839
2108
  end
1840
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
 
@@ -2634,7 +2634,24 @@ describe Ably::Realtime::Presence, :event_machine do
2634
2634
  end
2635
2635
  end
2636
2636
 
2637
- context 'channel state side effects' do
2637
+ context 'channel state side effects (RTP5)' do
2638
+ context 'channel transitions to the ATTACHED state (RTP5b)' do
2639
+ it 'all queued presence messages are sent' do
2640
+ channel_client_one.on(:attached) do
2641
+ client_one.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
2642
+ if protocol_message.action == :presence
2643
+ expect(protocol_message.action).to eq(:presence)
2644
+ stop_reactor
2645
+ end
2646
+ end
2647
+ end
2648
+
2649
+ presence_client_one.enter do
2650
+ channel_client_one.attach
2651
+ end
2652
+ end
2653
+ end
2654
+
2638
2655
  context 'channel transitions to the FAILED state' do
2639
2656
  let(:anonymous_client) { auto_close Ably::Realtime::Client.new(client_options.merge(log_level: :fatal)) }
2640
2657
  let(:client_one) { auto_close Ably::Realtime::Client.new(client_options.merge(client_id: client_one_id, log_level: :fatal)) }
@@ -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,11 +5,13 @@ 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} }
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)
12
12
  end
13
+ let(:max_message_size) { nil }
14
+ let(:max_frame_size) { nil }
13
15
 
14
16
  describe '#publish' do
15
17
  let(:channel_name) { random_str }
@@ -60,7 +62,8 @@ describe Ably::Rest::Channel do
60
62
  end
61
63
 
62
64
  it 'publishes an array of messages in one HTTP request' do
63
- expect(messages.sum(&:size) < Ably::Rest::Channel::MAX_MESSAGE_SIZE).to eq(true)
65
+ expect(client.max_message_size).to eq(Ably::Rest::Client::MAX_MESSAGE_SIZE)
66
+ expect(messages.sum(&:size) < Ably::Rest::Client::MAX_MESSAGE_SIZE).to eq(true)
64
67
 
65
68
  expect(client).to receive(:post).once.and_call_original
66
69
  expect(channel.publish(messages)).to eql(true)
@@ -70,19 +73,78 @@ describe Ably::Rest::Channel do
70
73
  end
71
74
 
72
75
  context 'with an array of Message objects' do
73
- let(:messages) do
74
- 10.times.map do |index|
75
- Ably::Models::Message(name: index.to_s, data: { "index" => index + 10 })
76
+ context 'when max_message_size and max_frame_size is not set' do
77
+ before do
78
+ expect(client.max_message_size).to eq(Ably::Rest::Client::MAX_MESSAGE_SIZE)
79
+ expect(client.max_frame_size).to eq(Ably::Rest::Client::MAX_FRAME_SIZE)
80
+ end
81
+
82
+ context 'and messages size (130 bytes) is smaller than the max_message_size' do
83
+ let(:messages) do
84
+ 10.times.map do |index|
85
+ Ably::Models::Message(name: index.to_s, data: { "index" => index + 10 })
86
+ end
87
+ end
88
+
89
+ it 'publishes an array of messages in one HTTP request' do
90
+ expect(messages.sum &:size).to eq(130)
91
+ expect(client).to receive(:post).once.and_call_original
92
+ expect(channel.publish(messages)).to eql(true)
93
+ expect(channel.history.items.map(&:name)).to match_array(messages.map(&:name))
94
+ expect(channel.history.items.map(&:data)).to match_array(messages.map(&:data))
95
+ end
96
+ end
97
+
98
+ context 'and messages size (177784 bytes) is bigger than the max_message_size' do
99
+ let(:messages) do
100
+ 10000.times.map do |index|
101
+ Ably::Models::Message(name: index.to_s, data: { "index" => index + 1 })
102
+ end
103
+ end
104
+
105
+ it 'should not publish and raise Ably::Exceptions::MaxMessageSizeExceeded' do
106
+ expect(messages.sum &:size).to eq(177784)
107
+ expect { channel.publish(messages) }.to raise_error(Ably::Exceptions::MaxMessageSizeExceeded)
108
+ end
76
109
  end
77
110
  end
78
111
 
79
- it 'publishes an array of messages in one HTTP request' do
80
- expect(messages.sum(&:size) < Ably::Rest::Channel::MAX_MESSAGE_SIZE).to eq(true)
112
+ context 'when max_message_size is 655 bytes' do
113
+ let(:max_message_size) { 655 }
81
114
 
82
- expect(client).to receive(:post).once.and_call_original
83
- expect(channel.publish(messages)).to eql(true)
84
- expect(channel.history.items.map(&:name)).to match_array(messages.map(&:name))
85
- expect(channel.history.items.map(&:data)).to match_array(messages.map(&:data))
115
+ before do
116
+ expect(client.max_message_size).to eq(max_message_size)
117
+ expect(client.max_frame_size).to eq(Ably::Rest::Client::MAX_FRAME_SIZE)
118
+ end
119
+
120
+ context 'and messages size (130 bytes) is smaller than the max_message_size' do
121
+ let(:messages) do
122
+ 10.times.map do |index|
123
+ Ably::Models::Message(name: index.to_s, data: { "index" => index + 10 })
124
+ end
125
+ end
126
+
127
+ it 'publishes an array of messages in one HTTP request' do
128
+ expect(messages.sum &:size).to eq(130)
129
+ expect(client).to receive(:post).once.and_call_original
130
+ expect(channel.publish(messages)).to eql(true)
131
+ expect(channel.history.items.map(&:name)).to match_array(messages.map(&:name))
132
+ expect(channel.history.items.map(&:data)).to match_array(messages.map(&:data))
133
+ end
134
+ end
135
+
136
+ context 'and messages size (177784 bytes) is bigger than the max_message_size' do
137
+ let(:messages) do
138
+ 10000.times.map do |index|
139
+ Ably::Models::Message(name: index.to_s, data: { "index" => index + 1 })
140
+ end
141
+ end
142
+
143
+ it 'should not publish and raise Ably::Exceptions::MaxMessageSizeExceeded' do
144
+ expect(messages.sum &:size).to eq(177784)
145
+ expect { channel.publish(messages) }.to raise_error(Ably::Exceptions::MaxMessageSizeExceeded)
146
+ end
147
+ end
86
148
  end
87
149
  end
88
150
 
@@ -5,11 +5,11 @@ describe Ably::Rest::Channels do
5
5
  shared_examples 'a channel' do
6
6
  it 'returns a channel object' do
7
7
  expect(channel).to be_a Ably::Rest::Channel
8
- expect(channel.name).to eql(channel_name)
8
+ expect(channel.name).to eq(channel_name)
9
9
  end
10
10
 
11
11
  it 'returns channel object and passes the provided options' do
12
- expect(channel_with_options.options).to eql(options)
12
+ expect(channel_with_options.options.to_h).to eq(options)
13
13
  end
14
14
  end
15
15
 
@@ -32,12 +32,29 @@ describe Ably::Rest::Channels do
32
32
  it_behaves_like 'a channel'
33
33
  end
34
34
 
35
+ describe '#set_options (#RTL16)' do
36
+ let(:channel) { client.channel(channel_name) }
37
+
38
+ it "updates channel's options" do
39
+ expect { channel.options = options }.to change { channel.options.to_h }.from({}).to(options)
40
+ end
41
+
42
+ context 'when providing Ably::Models::ChannelOptions object' do
43
+ let(:options_object) { Ably::Models::ChannelOptions.new(options) }
44
+
45
+ it "updates channel's options" do
46
+ expect { channel.options = options_object}.to change { channel.options.to_h }.from({}).to(options)
47
+ end
48
+ end
49
+ end
50
+
35
51
  describe 'accessing an existing channel object with different options' do
36
52
  let(:new_channel_options) { { encrypted: true } }
37
53
  let(:original_channel) { client.channels.get(channel_name, options) }
38
54
 
39
- it 'overrides the existing channel options and returns the channel object' do
40
- expect(original_channel.options).to_not include(:encrypted)
55
+ it 'overrides the existing channel options and returns the channel object (RSN3c)' do
56
+ expect(original_channel.options.to_h).to_not include(:encrypted)
57
+
41
58
  new_channel = client.channels.get(channel_name, new_channel_options)
42
59
  expect(new_channel).to be_a(Ably::Rest::Channel)
43
60
  expect(new_channel.options[:encrypted]).to eql(true)
@@ -48,10 +65,10 @@ describe Ably::Rest::Channels do
48
65
  let(:original_channel) { client.channels.get(channel_name, options) }
49
66
 
50
67
  it 'returns the existing channel without modifying the channel options' do
51
- expect(original_channel.options).to eql(options)
68
+ expect(original_channel.options.to_h).to eq(options)
52
69
  new_channel = client.channels.get(channel_name)
53
70
  expect(new_channel).to be_a(Ably::Rest::Channel)
54
- expect(original_channel.options).to eql(options)
71
+ expect(original_channel.options.to_h).to eq(options)
55
72
  end
56
73
  end
57
74