ably 1.1.7 → 1.2.1

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 (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