ably 1.1.6 → 1.2.0
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.
- 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
|