ably 1.1.6 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/check.yml +15 -1
- data/CHANGELOG.md +131 -0
- data/COPYRIGHT +1 -1
- data/README.md +14 -2
- data/SPEC.md +0 -7
- data/UPDATING.md +30 -0
- data/ably.gemspec +12 -7
- data/lib/ably/agent.rb +3 -0
- data/lib/ably/auth.rb +3 -3
- data/lib/ably/exceptions.rb +6 -0
- data/lib/ably/models/channel_options.rb +97 -0
- data/lib/ably/models/connection_details.rb +8 -0
- data/lib/ably/models/delta_extras.rb +29 -0
- data/lib/ably/models/error_info.rb +6 -2
- data/lib/ably/models/idiomatic_ruby_wrapper.rb +4 -0
- data/lib/ably/models/message.rb +28 -3
- data/lib/ably/models/presence_message.rb +14 -0
- data/lib/ably/models/protocol_message.rb +29 -12
- data/lib/ably/models/token_details.rb +7 -2
- data/lib/ably/modules/channels_collection.rb +22 -2
- data/lib/ably/modules/conversions.rb +34 -0
- data/lib/ably/realtime/channel/channel_manager.rb +18 -6
- data/lib/ably/realtime/channel/channel_state_machine.rb +10 -1
- data/lib/ably/realtime/channel/publisher.rb +6 -0
- data/lib/ably/realtime/channel.rb +54 -22
- data/lib/ably/realtime/channels.rb +1 -1
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +14 -6
- data/lib/ably/realtime/connection/connection_manager.rb +13 -4
- data/lib/ably/realtime/connection/connection_state_machine.rb +4 -0
- data/lib/ably/realtime/connection.rb +2 -2
- data/lib/ably/rest/channel.rb +31 -31
- data/lib/ably/rest/client.rb +27 -12
- data/lib/ably/util/crypto.rb +1 -1
- data/lib/ably/version.rb +2 -14
- data/lib/ably.rb +1 -0
- data/spec/acceptance/realtime/auth_spec.rb +1 -1
- data/spec/acceptance/realtime/channel_history_spec.rb +25 -0
- data/spec/acceptance/realtime/channel_spec.rb +466 -21
- data/spec/acceptance/realtime/channels_spec.rb +59 -7
- data/spec/acceptance/realtime/connection_failures_spec.rb +59 -2
- data/spec/acceptance/realtime/connection_spec.rb +256 -28
- data/spec/acceptance/realtime/message_spec.rb +77 -0
- data/spec/acceptance/realtime/presence_history_spec.rb +3 -1
- data/spec/acceptance/realtime/presence_spec.rb +31 -159
- data/spec/acceptance/rest/auth_spec.rb +18 -0
- data/spec/acceptance/rest/channel_spec.rb +84 -9
- data/spec/acceptance/rest/channels_spec.rb +23 -6
- data/spec/acceptance/rest/client_spec.rb +25 -21
- data/spec/acceptance/rest/message_spec.rb +61 -3
- data/spec/lib/unit/models/channel_options_spec.rb +52 -0
- data/spec/shared/model_behaviour.rb +1 -1
- data/spec/spec_helper.rb +11 -2
- data/spec/support/test_app.rb +1 -1
- data/spec/unit/models/delta_extras_spec.rb +14 -0
- data/spec/unit/models/error_info_spec.rb +17 -1
- data/spec/unit/models/message_spec.rb +97 -0
- data/spec/unit/models/presence_message_spec.rb +49 -0
- data/spec/unit/models/protocol_message_spec.rb +125 -27
- data/spec/unit/models/token_details_spec.rb +14 -0
- data/spec/unit/realtime/channel_spec.rb +3 -2
- data/spec/unit/realtime/channels_spec.rb +53 -15
- data/spec/unit/realtime/incoming_message_dispatcher_spec.rb +38 -0
- data/spec/unit/rest/channel_spec.rb +44 -1
- data/spec/unit/rest/channels_spec.rb +81 -14
- data/spec/unit/rest/client_spec.rb +47 -0
- metadata +60 -24
@@ -498,7 +498,9 @@ describe Ably::Realtime::Presence, :event_machine do
|
|
498
498
|
end
|
499
499
|
|
500
500
|
presence_client_one.enter do
|
501
|
-
|
501
|
+
EventMachine.add_timer(0.5) do
|
502
|
+
presence_client_one.leave
|
503
|
+
end
|
502
504
|
end
|
503
505
|
end
|
504
506
|
end
|
@@ -705,7 +707,7 @@ describe Ably::Realtime::Presence, :event_machine do
|
|
705
707
|
|
706
708
|
context '#sync_complete? and SYNC flags (#RTP1)' do
|
707
709
|
context 'when attaching to a channel without any members present' do
|
708
|
-
|
710
|
+
xit 'sync_complete? is true, there is no presence flag, and the presence channel is considered synced immediately (#RTP1)' do
|
709
711
|
flag_checked = false
|
710
712
|
|
711
713
|
anonymous_client.connection.__incoming_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
|
@@ -1211,10 +1213,11 @@ describe Ably::Realtime::Presence, :event_machine do
|
|
1211
1213
|
channel_client_one.attach do
|
1212
1214
|
presence_client_one.subscribe(:enter) do
|
1213
1215
|
presence_client_one.unsubscribe :enter
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
1217
|
-
|
1216
|
+
EventMachine.add_timer(0.5) do
|
1217
|
+
expect(presence_client_one.members).to be_in_sync
|
1218
|
+
expect(presence_client_one.members.send(:members).count).to eql(1)
|
1219
|
+
presence_client_one.leave data
|
1220
|
+
end
|
1218
1221
|
end
|
1219
1222
|
|
1220
1223
|
presence_client_one.enter(enter_data) do
|
@@ -2137,16 +2140,18 @@ describe Ably::Realtime::Presence, :event_machine do
|
|
2137
2140
|
end
|
2138
2141
|
|
2139
2142
|
it 'emits an error when cipher does not match and presence data cannot be decoded' do
|
2140
|
-
incompatible_encrypted_channel.
|
2143
|
+
incompatible_encrypted_channel.once(:attached) do
|
2141
2144
|
expect(client_two.logger).to receive(:error) do |*args, &block|
|
2142
2145
|
expect(args.concat([block ? block.call : nil]).join(',')).to match(/Cipher algorithm AES-128-CBC does not match/)
|
2143
2146
|
stop_reactor
|
2144
|
-
end
|
2147
|
+
end.at_least(:once)
|
2145
2148
|
|
2146
2149
|
encrypted_channel.attach do
|
2147
2150
|
encrypted_channel.presence.enter data
|
2148
2151
|
end
|
2149
2152
|
end
|
2153
|
+
|
2154
|
+
incompatible_encrypted_channel.attach
|
2150
2155
|
end
|
2151
2156
|
end
|
2152
2157
|
end
|
@@ -2522,156 +2527,6 @@ describe Ably::Realtime::Presence, :event_machine do
|
|
2522
2527
|
client_one.connection.transport.__outgoing_protocol_msgbus__.unsubscribe
|
2523
2528
|
end
|
2524
2529
|
|
2525
|
-
context 'and the resume flag is true' do
|
2526
|
-
context 'and the presence flag is false' do
|
2527
|
-
it 'does not send any presence events as the PresenceMap is in sync (#RTP5c1)' do
|
2528
|
-
presence_client_one.enter
|
2529
|
-
presence_client_one.subscribe(:enter) do
|
2530
|
-
presence_client_one.unsubscribe :enter
|
2531
|
-
|
2532
|
-
client_one.connection.transport.__outgoing_protocol_msgbus__.subscribe do |message|
|
2533
|
-
raise "No presence state updates to Ably are expected. Message sent: #{message.to_json}" if client_one.connection.connected?
|
2534
|
-
end
|
2535
|
-
|
2536
|
-
cripple_websocket_transport
|
2537
|
-
|
2538
|
-
fabricate_incoming_protocol_message Ably::Models::ProtocolMessage.new(
|
2539
|
-
action: attached_action,
|
2540
|
-
channel: channel_name,
|
2541
|
-
flags: resume_flag
|
2542
|
-
)
|
2543
|
-
|
2544
|
-
EventMachine.add_timer(1) do
|
2545
|
-
presence_client_one.get do |members|
|
2546
|
-
expect(members.length).to eql(1)
|
2547
|
-
expect(presence_client_one.members.local_members.length).to eql(1)
|
2548
|
-
stop_reactor
|
2549
|
-
end
|
2550
|
-
end
|
2551
|
-
end
|
2552
|
-
end
|
2553
|
-
end
|
2554
|
-
|
2555
|
-
context 'and the presence flag is true' do
|
2556
|
-
context 'and following the SYNC all local MemberMap members are present in the PresenceMap' do
|
2557
|
-
it 'does nothing as MemberMap is in sync (#RTP5c2)' do
|
2558
|
-
presence_client_one.enter
|
2559
|
-
presence_client_one.subscribe(:enter) do
|
2560
|
-
presence_client_one.unsubscribe :enter
|
2561
|
-
|
2562
|
-
expect(presence_client_one.members.length).to eql(1)
|
2563
|
-
expect(presence_client_one.members.local_members.length).to eql(1)
|
2564
|
-
|
2565
|
-
presence_client_one.members.once(:in_sync) do
|
2566
|
-
presence_client_one.get do |members|
|
2567
|
-
expect(members.length).to eql(1)
|
2568
|
-
expect(presence_client_one.members.local_members.length).to eql(1)
|
2569
|
-
stop_reactor
|
2570
|
-
end
|
2571
|
-
end
|
2572
|
-
|
2573
|
-
client_one.connection.transport.__outgoing_protocol_msgbus__.subscribe do |message|
|
2574
|
-
raise "No presence state updates to Ably are expected. Message sent: #{message.to_json}" if client_one.connection.connected?
|
2575
|
-
end
|
2576
|
-
|
2577
|
-
cripple_websocket_transport
|
2578
|
-
|
2579
|
-
fabricate_incoming_protocol_message Ably::Models::ProtocolMessage.new(
|
2580
|
-
action: attached_action,
|
2581
|
-
channel: channel_name,
|
2582
|
-
flags: resume_flag + presence_flag
|
2583
|
-
)
|
2584
|
-
|
2585
|
-
fabricate_incoming_protocol_message Ably::Models::ProtocolMessage.new(
|
2586
|
-
action: sync_action,
|
2587
|
-
channel: channel_name,
|
2588
|
-
presence: presence_client_one.members.map(&:shallow_clone).map(&:as_json),
|
2589
|
-
channelSerial: nil # no further SYNC messages expected
|
2590
|
-
)
|
2591
|
-
end
|
2592
|
-
end
|
2593
|
-
end
|
2594
|
-
|
2595
|
-
context 'and following the SYNC a local MemberMap member is not present in the PresenceMap' do
|
2596
|
-
it 're-enters the missing members automatically (#RTP5c2)' do
|
2597
|
-
sync_check_completed = false
|
2598
|
-
|
2599
|
-
presence_client_one.enter
|
2600
|
-
presence_client_one.subscribe(:enter) do
|
2601
|
-
presence_client_one.unsubscribe :enter
|
2602
|
-
|
2603
|
-
expect(presence_client_one.members.length).to eql(1)
|
2604
|
-
expect(presence_client_one.members.local_members.length).to eql(1)
|
2605
|
-
|
2606
|
-
client_one.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |message|
|
2607
|
-
next if message.action == :close # ignore finalization of connection
|
2608
|
-
|
2609
|
-
expect(message.action).to eq(:presence)
|
2610
|
-
presence_message = message.presence.first
|
2611
|
-
expect(presence_message.action).to eq(:enter)
|
2612
|
-
expect(presence_message.client_id).to eq(client_one.auth.client_id)
|
2613
|
-
|
2614
|
-
presence_client_one.subscribe(:enter) do |message|
|
2615
|
-
expect(message.connection_id).to eql(client_one.connection.id)
|
2616
|
-
expect(message.client_id).to eq(client_one.auth.client_id)
|
2617
|
-
|
2618
|
-
EventMachine.next_tick do
|
2619
|
-
expect(presence_client_one.members.length).to eql(2)
|
2620
|
-
expect(presence_client_one.members.local_members.length).to eql(1)
|
2621
|
-
expect(sync_check_completed).to be_truthy
|
2622
|
-
stop_reactor
|
2623
|
-
end
|
2624
|
-
end
|
2625
|
-
|
2626
|
-
# Fabricate Ably sending back the Enter PresenceMessage to the client a short while after
|
2627
|
-
# ensuring the PresenceMap for a short period does not have this member as to be expected in reality
|
2628
|
-
EventMachine.add_timer(0.2) do
|
2629
|
-
connection_id = random_str
|
2630
|
-
fabricate_incoming_protocol_message Ably::Models::ProtocolMessage.new(
|
2631
|
-
action: presence_action,
|
2632
|
-
channel: channel_name,
|
2633
|
-
connectionId: client_one.connection.id,
|
2634
|
-
connectionSerial: 50,
|
2635
|
-
timestamp: as_since_epoch(Time.now),
|
2636
|
-
presence: [presence_message.shallow_clone(id: "#{client_one.connection.id}:0:0", timestamp: as_since_epoch(Time.now)).as_json]
|
2637
|
-
)
|
2638
|
-
end
|
2639
|
-
end
|
2640
|
-
|
2641
|
-
presence_client_one.members.once(:in_sync) do
|
2642
|
-
# For a brief period, the client will have re-entered the missing members from the local_members
|
2643
|
-
# but the enter from Ably will have not been received, so at this point the local_members will be empty
|
2644
|
-
presence_client_one.get do |members|
|
2645
|
-
expect(members.length).to eql(1)
|
2646
|
-
expect(members.first.connection_id).to_not eql(client_one.connection.id)
|
2647
|
-
expect(presence_client_one.members.local_members.length).to eql(0)
|
2648
|
-
sync_check_completed = true
|
2649
|
-
end
|
2650
|
-
end
|
2651
|
-
|
2652
|
-
cripple_websocket_transport
|
2653
|
-
|
2654
|
-
fabricate_incoming_protocol_message Ably::Models::ProtocolMessage.new(
|
2655
|
-
action: attached_action,
|
2656
|
-
channel: channel_name,
|
2657
|
-
flags: resume_flag + presence_flag
|
2658
|
-
)
|
2659
|
-
|
2660
|
-
# Complete the SYNC but without the member who was entered by this client
|
2661
|
-
connection_id = random_str
|
2662
|
-
fabricate_incoming_protocol_message Ably::Models::ProtocolMessage.new(
|
2663
|
-
action: sync_action,
|
2664
|
-
channel: channel_name,
|
2665
|
-
timestamp: as_since_epoch(Time.now),
|
2666
|
-
presence: [{ id: "#{connection_id}:0:0", action: present_action, connection_id: connection_id, client_id: random_str }],
|
2667
|
-
chanenlSerial: nil # no further SYNC messages expected
|
2668
|
-
)
|
2669
|
-
end
|
2670
|
-
end
|
2671
|
-
end
|
2672
|
-
end
|
2673
|
-
end
|
2674
|
-
|
2675
2530
|
context 'and the resume flag is false' do
|
2676
2531
|
context 'and the presence flag is false' do
|
2677
2532
|
let(:member_data) { random_str }
|
@@ -2779,7 +2634,24 @@ describe Ably::Realtime::Presence, :event_machine do
|
|
2779
2634
|
end
|
2780
2635
|
end
|
2781
2636
|
|
2782
|
-
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
|
+
|
2783
2655
|
context 'channel transitions to the FAILED state' do
|
2784
2656
|
let(:anonymous_client) { auto_close Ably::Realtime::Client.new(client_options.merge(log_level: :fatal)) }
|
2785
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,6 +62,9 @@ describe Ably::Rest::Channel do
|
|
60
62
|
end
|
61
63
|
|
62
64
|
it 'publishes an array of messages in one HTTP request' do
|
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)
|
67
|
+
|
63
68
|
expect(client).to receive(:post).once.and_call_original
|
64
69
|
expect(channel.publish(messages)).to eql(true)
|
65
70
|
expect(channel.history.items.map(&:name)).to match_array(messages.map { |message| message[:name] })
|
@@ -68,17 +73,78 @@ describe Ably::Rest::Channel do
|
|
68
73
|
end
|
69
74
|
|
70
75
|
context 'with an array of Message objects' do
|
71
|
-
|
72
|
-
|
73
|
-
Ably::
|
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
|
74
109
|
end
|
75
110
|
end
|
76
111
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
112
|
+
context 'when max_message_size is 655 bytes' do
|
113
|
+
let(:max_message_size) { 655 }
|
114
|
+
|
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
|
82
148
|
end
|
83
149
|
end
|
84
150
|
|
@@ -350,6 +416,15 @@ describe Ably::Rest::Channel do
|
|
350
416
|
end
|
351
417
|
end
|
352
418
|
end
|
419
|
+
|
420
|
+
context 'message size is exceeded (#TO3l8)' do
|
421
|
+
let(:data) { 101.times.map { { data: 'x' * 655 } } }
|
422
|
+
|
423
|
+
it 'should raise Ably::Exceptions::MaxMessageSizeExceeded exception' do
|
424
|
+
expect { channel.publish([ data: data ]) }.to \
|
425
|
+
raise_error(Ably::Exceptions::MaxMessageSizeExceeded)
|
426
|
+
end
|
427
|
+
end
|
353
428
|
end
|
354
429
|
|
355
430
|
describe '#history' do
|
@@ -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
|
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
|
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
|
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
|
71
|
+
expect(original_channel.options.to_h).to eq(options)
|
55
72
|
end
|
56
73
|
end
|
57
74
|
|
@@ -1081,29 +1081,15 @@ describe Ably::Rest::Client do
|
|
1081
1081
|
end
|
1082
1082
|
|
1083
1083
|
context 'version headers', :webmock do
|
1084
|
-
[nil, '
|
1085
|
-
context "with
|
1086
|
-
|
1087
|
-
before do
|
1088
|
-
Ably.lib_variant = variant
|
1089
|
-
end
|
1084
|
+
[nil, 'ably-ruby/1.1.1 ruby/1.9.3'].each do |agent|
|
1085
|
+
context "with #{agent ? "custom #{agent}" : 'default'} agent" do
|
1086
|
+
let(:client_options) { default_options.merge(key: api_key, agent: agent) }
|
1090
1087
|
|
1091
|
-
after do
|
1092
|
-
Ably.lib_variant = nil
|
1093
|
-
end
|
1094
|
-
end
|
1095
|
-
|
1096
|
-
let(:client_options) { default_options.merge(key: api_key) }
|
1097
1088
|
let!(:publish_message_stub) do
|
1098
|
-
lib = ['ruby']
|
1099
|
-
lib << variant if variant
|
1100
|
-
lib << Ably::VERSION
|
1101
|
-
|
1102
|
-
|
1103
1089
|
stub_request(:post, "#{client.endpoint}/channels/foo/publish").
|
1104
1090
|
with(headers: {
|
1105
1091
|
'X-Ably-Version' => Ably::PROTOCOL_VERSION,
|
1106
|
-
'
|
1092
|
+
'Ably-Agent' => agent || Ably::AGENT
|
1107
1093
|
}).
|
1108
1094
|
to_return(status: 201, body: '{}', headers: { 'Content-Type' => 'application/json' })
|
1109
1095
|
end
|
@@ -1111,13 +1097,13 @@ describe Ably::Rest::Client do
|
|
1111
1097
|
it 'sends a protocol version and lib version header (#G4, #RSC7a, #RSC7b)' do
|
1112
1098
|
client.channels.get('foo').publish("event")
|
1113
1099
|
expect(publish_message_stub).to have_been_requested
|
1114
|
-
expect(Ably::PROTOCOL_VERSION).to eql('1.
|
1100
|
+
expect(Ably::PROTOCOL_VERSION).to eql('1.2')
|
1115
1101
|
end
|
1116
1102
|
end
|
1117
1103
|
end
|
1118
1104
|
end
|
1119
1105
|
|
1120
|
-
context '#request (#RSC19
|
1106
|
+
context '#request (#RSC19*, #TO3l9)' do
|
1121
1107
|
let(:client_options) { default_options.merge(key: api_key) }
|
1122
1108
|
let(:device_id) { random_str }
|
1123
1109
|
let(:endpoint) { client.endpoint }
|
@@ -1172,6 +1158,12 @@ describe Ably::Rest::Client do
|
|
1172
1158
|
|
1173
1159
|
expect(response).to be_success
|
1174
1160
|
end
|
1161
|
+
|
1162
|
+
it 'raises an exception once body size in bytes exceeded' do
|
1163
|
+
expect {
|
1164
|
+
client.request(:post, endpoint, {}, { content: 'x' * Ably::Rest::Client::MAX_FRAME_SIZE })
|
1165
|
+
}.to raise_error(Ably::Exceptions::MaxFrameSizeExceeded)
|
1166
|
+
end
|
1175
1167
|
end
|
1176
1168
|
|
1177
1169
|
context 'delete', :webmock do
|
@@ -1201,6 +1193,12 @@ describe Ably::Rest::Client do
|
|
1201
1193
|
|
1202
1194
|
expect(response).to be_success
|
1203
1195
|
end
|
1196
|
+
|
1197
|
+
it 'raises an exception once body size in bytes exceeded' do
|
1198
|
+
expect {
|
1199
|
+
client.request(:patch, endpoint, {}, { content: 'x' * Ably::Rest::Client::MAX_FRAME_SIZE })
|
1200
|
+
}.to raise_error(Ably::Exceptions::MaxFrameSizeExceeded)
|
1201
|
+
end
|
1204
1202
|
end
|
1205
1203
|
|
1206
1204
|
context 'put', :webmock do
|
@@ -1224,10 +1222,16 @@ describe Ably::Rest::Client do
|
|
1224
1222
|
|
1225
1223
|
expect(response).to be_success
|
1226
1224
|
end
|
1225
|
+
|
1226
|
+
it 'raises an exception once body size in bytes exceeded' do
|
1227
|
+
expect {
|
1228
|
+
client.request(:put, endpoint, {}, { content: 'x' * Ably::Rest::Client::MAX_FRAME_SIZE })
|
1229
|
+
}.to raise_error(Ably::Exceptions::MaxFrameSizeExceeded)
|
1230
|
+
end
|
1227
1231
|
end
|
1228
1232
|
end
|
1229
1233
|
|
1230
|
-
context 'request_id generation' do
|
1234
|
+
context 'request_id generation (#RSC7c)' do
|
1231
1235
|
context 'Timeout error' do
|
1232
1236
|
context 'with option add_request_ids: true and no fallback hosts', :webmock, :prevent_log_stubbing do
|
1233
1237
|
let(:custom_logger_object) { TestLogger.new }
|
@@ -24,6 +24,57 @@ describe Ably::Rest::Channel, 'messages' do
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
+
context 'a single Message object (#RSL1a)' do
|
28
|
+
let(:name) { random_str }
|
29
|
+
let(:data) { random_str }
|
30
|
+
let(:message) { Ably::Models::Message.new(name: name, data: data) }
|
31
|
+
|
32
|
+
it 'publishes the message' do
|
33
|
+
channel.publish(message)
|
34
|
+
expect(channel.history.items.length).to eql(1)
|
35
|
+
message = channel.history.items.first
|
36
|
+
expect(message.name).to eq(name)
|
37
|
+
expect(message.data).to eq(data)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'an array of Message objects (#RSL1a)' do
|
42
|
+
let(:data) { random_str }
|
43
|
+
let(:message1) { Ably::Models::Message.new(name: random_str, data: data) }
|
44
|
+
let(:message2) { Ably::Models::Message.new(name: random_str, data: data) }
|
45
|
+
let(:message3) { Ably::Models::Message.new(name: random_str, data: data) }
|
46
|
+
|
47
|
+
it 'publishes three messages' do
|
48
|
+
channel.publish([message1, message2, message3])
|
49
|
+
expect(channel.history.items.length).to eql(3)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'an array of hashes (#RSL1a)' do
|
54
|
+
let(:data) { random_str }
|
55
|
+
let(:message1) { { name: random_str, data: data } }
|
56
|
+
let(:message2) { { name: random_str, data: data } }
|
57
|
+
let(:message3) { { name: random_str, data: data } }
|
58
|
+
|
59
|
+
it 'publishes three messages' do
|
60
|
+
channel.publish([message1, message2, message3])
|
61
|
+
expect(channel.history.items.length).to eql(3)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'a name with data payload (#RSL1a, #RSL1b)' do
|
66
|
+
let(:name) { random_str }
|
67
|
+
let(:data) { random_str }
|
68
|
+
|
69
|
+
it 'publishes the message' do
|
70
|
+
channel.publish(name, data)
|
71
|
+
expect(channel.history.items.length).to eql(1)
|
72
|
+
message = channel.history.items.first
|
73
|
+
expect(message.name).to eq(name)
|
74
|
+
expect(message.data).to eq(data)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
27
78
|
context 'with supported data payload content type' do
|
28
79
|
context 'JSON Object (Hash)' do
|
29
80
|
let(:data) { { 'Hash' => 'true' } }
|
@@ -153,13 +204,20 @@ describe Ably::Rest::Channel, 'messages' do
|
|
153
204
|
end
|
154
205
|
end
|
155
206
|
|
156
|
-
specify 'idempotent publishing is disabled by default with 1.1 (#TO3n)' do
|
207
|
+
specify 'idempotent publishing is disabled by default with <= 1.1 (#TO3n)' do
|
208
|
+
stub_const 'Ably::PROTOCOL_VERSION', '1.0'
|
209
|
+
client = Ably::Rest::Client.new(key: api_key, protocol: protocol)
|
210
|
+
expect(client.idempotent_rest_publishing).to be_falsey
|
211
|
+
stub_const 'Ably::PROTOCOL_VERSION', '1.1'
|
157
212
|
client = Ably::Rest::Client.new(key: api_key, protocol: protocol)
|
158
213
|
expect(client.idempotent_rest_publishing).to be_falsey
|
159
214
|
end
|
160
215
|
|
161
|
-
specify 'idempotent publishing is enabled by default with 1.2 (#TO3n)' do
|
162
|
-
stub_const 'Ably::
|
216
|
+
specify 'idempotent publishing is enabled by default with >= 1.2 (#TO3n)' do
|
217
|
+
stub_const 'Ably::PROTOCOL_VERSION', '1.2'
|
218
|
+
client = Ably::Rest::Client.new(key: api_key, protocol: protocol)
|
219
|
+
expect(client.idempotent_rest_publishing).to be_truthy
|
220
|
+
stub_const 'Ably::PROTOCOL_VERSION', '1.3'
|
163
221
|
client = Ably::Rest::Client.new(key: api_key, protocol: protocol)
|
164
222
|
expect(client.idempotent_rest_publishing).to be_truthy
|
165
223
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Ably::Models::ChannelOptions do
|
6
|
+
let(:modes) { nil }
|
7
|
+
let(:params) { {} }
|
8
|
+
let(:options) { described_class.new(modes: modes, params: params) }
|
9
|
+
|
10
|
+
describe '#modes_to_flags' do
|
11
|
+
let(:modes) { %w[publish subscribe presence_subscribe] }
|
12
|
+
|
13
|
+
subject(:protocol_message) do
|
14
|
+
Ably::Models::ProtocolMessage.new(action: Ably::Models::ProtocolMessage::ACTION.Attach, flags: options.modes_to_flags)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'converts modes to ProtocolMessage#flags correctly' do
|
18
|
+
expect(protocol_message.has_attach_publish_flag?).to eq(true)
|
19
|
+
expect(protocol_message.has_attach_subscribe_flag?).to eq(true)
|
20
|
+
expect(protocol_message.has_attach_presence_subscribe_flag?).to eq(true)
|
21
|
+
|
22
|
+
expect(protocol_message.has_attach_resume_flag?).to eq(false)
|
23
|
+
expect(protocol_message.has_attach_presence_flag?).to eq(false)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#set_modes_from_flags' do
|
28
|
+
let(:subscribe_flag) { 262144 }
|
29
|
+
|
30
|
+
it 'converts flags to ChannelOptions#modes correctly' do
|
31
|
+
result = options.set_modes_from_flags(subscribe_flag)
|
32
|
+
|
33
|
+
expect(result).to eq(options.modes)
|
34
|
+
expect(options.modes.map(&:to_sym)).to eq(%i[subscribe])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#set_params' do
|
39
|
+
let(:previous_params) { { example_attribute: 1 } }
|
40
|
+
let(:new_params) { { new_attribute: 1 } }
|
41
|
+
let(:params) { previous_params }
|
42
|
+
|
43
|
+
it 'should be able to overwrite attributes' do
|
44
|
+
expect { options.set_params(new_params) }.to \
|
45
|
+
change { options.params }.from(previous_params).to(new_params)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should be able to make params empty' do # (1)
|
49
|
+
expect { options.set_params({}) }.to change { options.params }.from(previous_params).to({})
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|