onstomp 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/.yardopts +2 -1
- data/Rakefile +147 -0
- data/extra_doc/API.md +491 -0
- data/extra_doc/API.md.erb +33 -0
- data/extra_doc/DeveloperNarrative.md +123 -0
- data/extra_doc/UserNarrative.md +511 -0
- data/lib/onstomp.rb +5 -5
- data/lib/onstomp/client.rb +6 -1
- data/lib/onstomp/components/frame.rb +6 -6
- data/lib/onstomp/components/frame_headers.rb +18 -18
- data/lib/onstomp/components/scopes/transaction_scope.rb +11 -11
- data/lib/onstomp/components/subscription.rb +2 -2
- data/lib/onstomp/components/threaded_processor.rb +1 -1
- data/lib/onstomp/components/uri.rb +1 -1
- data/lib/onstomp/connections/base.rb +5 -5
- data/lib/onstomp/connections/heartbeating.rb +2 -2
- data/lib/onstomp/connections/serializers/stomp_1.rb +6 -6
- data/lib/onstomp/connections/serializers/stomp_1_1.rb +2 -2
- data/lib/onstomp/connections/stomp_1_0.rb +1 -1
- data/lib/onstomp/connections/stomp_1_1.rb +1 -1
- data/lib/onstomp/failover.rb +4 -0
- data/lib/onstomp/failover/buffers.rb +1 -0
- data/lib/onstomp/failover/buffers/receipts.rb +101 -0
- data/lib/onstomp/failover/buffers/written.rb +2 -2
- data/lib/onstomp/failover/client.rb +15 -12
- data/lib/onstomp/failover/failover_configurable.rb +3 -3
- data/lib/onstomp/failover/pools/base.rb +1 -1
- data/lib/onstomp/failover/uri.rb +41 -16
- data/lib/onstomp/interfaces/client_configurable.rb +1 -1
- data/lib/onstomp/interfaces/client_events.rb +30 -11
- data/lib/onstomp/interfaces/connection_events.rb +5 -1
- data/lib/onstomp/interfaces/event_manager.rb +2 -2
- data/lib/onstomp/interfaces/frame_methods.rb +169 -8
- data/lib/onstomp/interfaces/uri_configurable.rb +3 -3
- data/lib/onstomp/open-uri/client_extensions.rb +4 -4
- data/lib/onstomp/version.rb +2 -2
- data/onstomp.gemspec +0 -1
- data/spec/onstomp/components/threaded_processor_spec.rb +21 -0
- data/spec/onstomp/connections/base_spec.rb +15 -0
- data/spec/onstomp/failover/buffers/receipts_spec.rb +189 -0
- data/spec/onstomp/failover/buffers/written_spec.rb +167 -1
- data/spec/onstomp/failover/client_spec.rb +70 -1
- data/spec/onstomp/failover/failover_events_spec.rb +1 -2
- data/spec/onstomp/failover/uri_spec.rb +37 -4
- data/spec/onstomp/full_stacks/failover_spec.rb +76 -25
- data/spec/onstomp/full_stacks/onstomp_spec.rb +52 -8
- data/spec/onstomp/full_stacks/onstomp_ssh_spec.rb +83 -0
- data/spec/onstomp/full_stacks/test_broker.rb +45 -29
- metadata +11 -15
- data/DeveloperNarrative.md +0 -15
- data/UserNarrative.md +0 -8
@@ -66,6 +66,27 @@ module OnStomp::Components
|
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
|
+
describe ".prepare_to_close" do
|
70
|
+
it "should do nothing special if it is not running" do
|
71
|
+
processor.stub(:running? => false)
|
72
|
+
processor.prepare_to_close
|
73
|
+
end
|
74
|
+
it "should stop its worker thread, flush the connection's buffer then restart the thread" do
|
75
|
+
# ugg....
|
76
|
+
def processor.stopped?
|
77
|
+
@run_thread.stop?
|
78
|
+
end
|
79
|
+
client.stub(:connected? => true)
|
80
|
+
processor.start
|
81
|
+
connection.should_receive(:flush_write_buffer).and_return do
|
82
|
+
processor.stopped?.should be_true
|
83
|
+
nil
|
84
|
+
end
|
85
|
+
processor.prepare_to_close
|
86
|
+
processor.stopped?.should be_false
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
69
90
|
describe ".join" do
|
70
91
|
it "should block the current thread until connection is no longer alive" do
|
71
92
|
joined_properly = false
|
@@ -94,6 +94,21 @@ module OnStomp::Connections
|
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
97
|
+
describe ".flush_write_buffer" do
|
98
|
+
it "should run until the buffer is empty" do
|
99
|
+
connection.stub(:connected? => true)
|
100
|
+
IO.stub(:select => true)
|
101
|
+
connection.push_write_buffer 'FRAME_SERIALIZED', frame
|
102
|
+
connection.push_write_buffer 'FRAME_SERIALIZED', frame
|
103
|
+
connection.push_write_buffer 'FRAME_SERIALIZED', frame
|
104
|
+
io.should_receive(:write_nonblock).exactly(6).times.and_return do |d|
|
105
|
+
8
|
106
|
+
end
|
107
|
+
connection.flush_write_buffer
|
108
|
+
connection.shift_write_buffer.should be_nil
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
97
112
|
describe ".io_process_write" do
|
98
113
|
it "should not write if the buffer is empty" do
|
99
114
|
io.should_not_receive :write_nonblock
|
@@ -0,0 +1,189 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module OnStomp::Failover::Buffers
|
5
|
+
describe Receipts, :failover => true do
|
6
|
+
let(:clients) {
|
7
|
+
['client 1', 'client 2', 'client 3'].map do |c|
|
8
|
+
mock(c).tap do |m|
|
9
|
+
m.extend OnStomp::Interfaces::ClientEvents
|
10
|
+
end
|
11
|
+
end
|
12
|
+
}
|
13
|
+
let(:active_client) { clients.first }
|
14
|
+
let(:failover) {
|
15
|
+
mock('failover client', :client_pool => clients,
|
16
|
+
:active_client => active_client).tap do |m|
|
17
|
+
m.extend OnStomp::Failover::FailoverEvents
|
18
|
+
end
|
19
|
+
}
|
20
|
+
let(:buffer) {
|
21
|
+
Receipts.new failover
|
22
|
+
}
|
23
|
+
let(:partial_transaction) {
|
24
|
+
[
|
25
|
+
OnStomp::Components::Frame.new('BEGIN', :transaction => 't-1234'),
|
26
|
+
OnStomp::Components::Frame.new('SEND', {:transaction => 't-1234'}, 'message 1'),
|
27
|
+
OnStomp::Components::Frame.new('SEND', {:transaction => 't-1234'}, 'message 2'),
|
28
|
+
OnStomp::Components::Frame.new('ACK', {:transaction => 't-1234',
|
29
|
+
:'message-id' => 'm-99991'}),
|
30
|
+
OnStomp::Components::Frame.new('SEND', {:transaction => 't-1234'}, 'message 3'),
|
31
|
+
]
|
32
|
+
}
|
33
|
+
|
34
|
+
def receipt_for frame
|
35
|
+
raise "frame does not have a receipt header" unless frame.header?(:receipt)
|
36
|
+
OnStomp::Components::Frame.new 'RECEIPT', :'receipt-id' => frame[:receipt]
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "basic replaying" do
|
40
|
+
before(:each) do
|
41
|
+
buffer
|
42
|
+
end
|
43
|
+
['ACK', 'NACK'].each do |command|
|
44
|
+
it "should not replay #{command} frames" do
|
45
|
+
frame = OnStomp::Components::Frame.new command
|
46
|
+
active_client.trigger_before_transmitting frame
|
47
|
+
active_client.should_not_receive(:transmit)
|
48
|
+
failover.trigger_failover_event :connected, clients.first
|
49
|
+
end
|
50
|
+
end
|
51
|
+
it "should attach a receipt header to frames lacking one" do
|
52
|
+
frame = OnStomp::Components::Frame.new 'SEND',
|
53
|
+
{ :destination => '/queue/test' }, 'body of message'
|
54
|
+
active_client.trigger_before_transmitting frame
|
55
|
+
frame.header?(:receipt).should be_true
|
56
|
+
end
|
57
|
+
it "should not molest existing receipt headeres" do
|
58
|
+
frame = OnStomp::Components::Frame.new 'SUBSCRIBE',
|
59
|
+
{ :destination => '/queue/test', :receipt => 'r-1234' }
|
60
|
+
active_client.trigger_before_transmitting frame
|
61
|
+
frame[:receipt].should == 'r-1234'
|
62
|
+
end
|
63
|
+
it "should replay SEND frames" do
|
64
|
+
frame = OnStomp::Components::Frame.new 'SEND',
|
65
|
+
{ :destination => '/queue/test' }, 'body of message'
|
66
|
+
active_client.trigger_before_transmitting frame
|
67
|
+
active_client.should_receive(:transmit).with(an_onstomp_frame(
|
68
|
+
'SEND', {:destination => '/queue/test'}, 'body of message'))
|
69
|
+
failover.trigger_failover_event :connected, :on, clients.first
|
70
|
+
end
|
71
|
+
it "should debuffer non-transactional SEND frames" do
|
72
|
+
frame = OnStomp::Components::Frame.new 'SEND',
|
73
|
+
{ :destination => '/queue/test' }, 'body of message'
|
74
|
+
active_client.trigger_before_transmitting frame
|
75
|
+
active_client.trigger_after_receiving receipt_for(frame)
|
76
|
+
active_client.should_not_receive(:transmit)
|
77
|
+
failover.trigger_failover_event :connected, :on, clients.first
|
78
|
+
end
|
79
|
+
it "should replay SUBSCRIBE frames, even receipted ones" do
|
80
|
+
frame = OnStomp::Components::Frame.new 'SUBSCRIBE',
|
81
|
+
{ :destination => '/queue/test', :id => 's-1234' }
|
82
|
+
active_client.trigger_before_transmitting frame
|
83
|
+
active_client.trigger_after_receiving receipt_for(frame)
|
84
|
+
active_client.should_receive(:transmit).with(an_onstomp_frame(
|
85
|
+
'SUBSCRIBE', {:destination => '/queue/test', :id => 's-1234'}))
|
86
|
+
failover.trigger_failover_event :connected, :on, clients.first
|
87
|
+
end
|
88
|
+
it "should debuffer SUBSCRIBE frames as soon as UNSUBSCRIBE is in the write buffer (does not need to be receipted)" do
|
89
|
+
frame = OnStomp::Components::Frame.new 'SUBSCRIBE',
|
90
|
+
{ :destination => '/queue/test', :id => 's-1234' }
|
91
|
+
active_client.trigger_before_transmitting frame
|
92
|
+
active_client.trigger_after_transmitting frame
|
93
|
+
frame = OnStomp::Components::Frame.new 'UNSUBSCRIBE', :id => 's-1234'
|
94
|
+
active_client.trigger_before_transmitting frame
|
95
|
+
active_client.should_not_receive :transmit
|
96
|
+
failover.trigger_failover_event :connected, :on, clients.first
|
97
|
+
end
|
98
|
+
it "should not debuffer incomplete transactions" do
|
99
|
+
replayed = []
|
100
|
+
partial_transaction.each do |f|
|
101
|
+
active_client.trigger_before_transmitting f
|
102
|
+
active_client.trigger_after_transmitting f
|
103
|
+
end
|
104
|
+
active_client.stub(:transmit).and_return { |f| replayed << f; f }
|
105
|
+
failover.trigger_failover_event :connected, :on, clients.first
|
106
|
+
replayed.should == partial_transaction.reject { |f| ['ACK', 'NACK'].include? f.command }
|
107
|
+
end
|
108
|
+
['ABORT', 'COMMIT'].each do |trans_fin|
|
109
|
+
it "should not debuffer transactions when #{trans_fin} is only buffered" do
|
110
|
+
fin_frame = OnStomp::Components::Frame.new trans_fin, {:transaction => 't-1234'}
|
111
|
+
replayed = []
|
112
|
+
partial_transaction.each do |f|
|
113
|
+
active_client.trigger_before_transmitting f
|
114
|
+
active_client.trigger_after_transmitting f
|
115
|
+
end
|
116
|
+
active_client.trigger_before_transmitting fin_frame
|
117
|
+
active_client.stub(:transmit).and_return { |f| replayed << f; f }
|
118
|
+
failover.trigger_failover_event :connected, :on, clients.first
|
119
|
+
replayed.should ==
|
120
|
+
(partial_transaction.reject { |f| ['ACK', 'NACK'].include? f.command } +
|
121
|
+
[fin_frame])
|
122
|
+
end
|
123
|
+
it "should debuffer a transaction when #{trans_fin} has been written" do
|
124
|
+
fin_frame = OnStomp::Components::Frame.new trans_fin, {:transaction => 't-1234'}
|
125
|
+
replayed = []
|
126
|
+
partial_transaction.each do |f|
|
127
|
+
active_client.trigger_before_transmitting f
|
128
|
+
active_client.trigger_after_transmitting f
|
129
|
+
end
|
130
|
+
active_client.trigger_before_transmitting fin_frame
|
131
|
+
active_client.trigger_after_receiving receipt_for(fin_frame)
|
132
|
+
active_client.stub(:transmit).and_return { |f| replayed << f; f }
|
133
|
+
failover.trigger_failover_event :connected, :on, clients.first
|
134
|
+
replayed.should be_empty
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe "replaying while you replay (Xzibit Approved)" do
|
140
|
+
let(:frame_list) {
|
141
|
+
[
|
142
|
+
OnStomp::Components::Frame.new('SEND', {:destination => '/queue/test'}, 'message 1'),
|
143
|
+
OnStomp::Components::Frame.new('SUBSCRIBE', :id => 's-1234', :destination => '/queue/test'),
|
144
|
+
OnStomp::Components::Frame.new('BEGIN', :transaction => 't-1234'),
|
145
|
+
OnStomp::Components::Frame.new('NACK', :'message-id' => 'm-99992'),
|
146
|
+
OnStomp::Components::Frame.new('SEND', {:transaction => 't-1234'}, 'message 2'),
|
147
|
+
OnStomp::Components::Frame.new('ACK', :'message-id' => 'm-99993'),
|
148
|
+
OnStomp::Components::Frame.new('SEND', {:transaction => 't-1234'}, 'message 3'),
|
149
|
+
OnStomp::Components::Frame.new('UNSUBSCRIBE', :id => 's-1234'),
|
150
|
+
OnStomp::Components::Frame.new('SUBSCRIBE', :id => 's-5678', :destination => '/queue/test'),
|
151
|
+
OnStomp::Components::Frame.new('ACK', {:transaction => 't-1234',
|
152
|
+
:'message-id' => 'm-99991'}),
|
153
|
+
OnStomp::Components::Frame.new('SEND', {:transaction => 't-1234'}, 'message 4'),
|
154
|
+
]
|
155
|
+
}
|
156
|
+
let(:expected_replay) {
|
157
|
+
frame_list.reject do |f|
|
158
|
+
['ACK', 'NACK'].include?(f.command) || f[:id] == 's-1234'
|
159
|
+
end
|
160
|
+
}
|
161
|
+
let(:client1_frames) { [] }
|
162
|
+
let(:client3_frames) { [] }
|
163
|
+
before(:each) do
|
164
|
+
buffer
|
165
|
+
frame_list.each do |f|
|
166
|
+
active_client.trigger_before_transmitting f
|
167
|
+
end
|
168
|
+
end
|
169
|
+
it "should replay properly if failover reconnects while replaying" do
|
170
|
+
extra_frame = OnStomp::Components::Frame.new 'SEND',
|
171
|
+
{:destination => '/queue/test'}, 'yet another freaking message'
|
172
|
+
clients.last.stub(:transmit).and_return { |f| client3_frames << f; f }
|
173
|
+
active_client.stub(:transmit).and_return do |f|
|
174
|
+
client1_frames << f
|
175
|
+
if f.command == 'BEGIN'
|
176
|
+
# Add a fresh frame to the buffer
|
177
|
+
active_client.trigger_before_transmitting extra_frame
|
178
|
+
# Signal the a reconnect in the midst of replaying.
|
179
|
+
failover.trigger_failover_event :connected, :on, clients.last
|
180
|
+
end
|
181
|
+
f
|
182
|
+
end
|
183
|
+
failover.trigger_failover_event :connected, :on, active_client
|
184
|
+
client1_frames.should == expected_replay
|
185
|
+
client3_frames.should == expected_replay + [extra_frame]
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
@@ -3,6 +3,172 @@ require 'spec_helper'
|
|
3
3
|
|
4
4
|
module OnStomp::Failover::Buffers
|
5
5
|
describe Written, :failover => true do
|
6
|
-
|
6
|
+
let(:clients) {
|
7
|
+
['client 1', 'client 2', 'client 3'].map do |c|
|
8
|
+
mock(c).tap do |m|
|
9
|
+
m.extend OnStomp::Interfaces::ClientEvents
|
10
|
+
end
|
11
|
+
end
|
12
|
+
}
|
13
|
+
let(:active_client) { clients.first }
|
14
|
+
let(:failover) {
|
15
|
+
mock('failover client', :client_pool => clients,
|
16
|
+
:active_client => active_client).tap do |m|
|
17
|
+
m.extend OnStomp::Failover::FailoverEvents
|
18
|
+
end
|
19
|
+
}
|
20
|
+
let(:buffer) {
|
21
|
+
Written.new failover
|
22
|
+
}
|
23
|
+
let(:partial_transaction) {
|
24
|
+
[
|
25
|
+
OnStomp::Components::Frame.new('BEGIN', :transaction => 't-1234'),
|
26
|
+
OnStomp::Components::Frame.new('SEND', {:transaction => 't-1234'}, 'message 1'),
|
27
|
+
OnStomp::Components::Frame.new('SEND', {:transaction => 't-1234'}, 'message 2'),
|
28
|
+
OnStomp::Components::Frame.new('ACK', {:transaction => 't-1234',
|
29
|
+
:'message-id' => 'm-99991'}),
|
30
|
+
OnStomp::Components::Frame.new('SEND', {:transaction => 't-1234'}, 'message 3'),
|
31
|
+
]
|
32
|
+
}
|
33
|
+
|
34
|
+
describe "basic replaying" do
|
35
|
+
before(:each) do
|
36
|
+
buffer
|
37
|
+
end
|
38
|
+
['ACK', 'NACK'].each do |command|
|
39
|
+
it "should not replay #{command} frames" do
|
40
|
+
frame = OnStomp::Components::Frame.new command
|
41
|
+
active_client.trigger_before_transmitting frame
|
42
|
+
active_client.should_not_receive(:transmit)
|
43
|
+
failover.trigger_failover_event :connected, clients.first
|
44
|
+
end
|
45
|
+
end
|
46
|
+
it "should replay SEND frames" do
|
47
|
+
frame = OnStomp::Components::Frame.new 'SEND',
|
48
|
+
{ :destination => '/queue/test' }, 'body of message'
|
49
|
+
active_client.trigger_before_transmitting frame
|
50
|
+
active_client.should_receive(:transmit).with(an_onstomp_frame(
|
51
|
+
'SEND', {:destination => '/queue/test'}, 'body of message'))
|
52
|
+
failover.trigger_failover_event :connected, :on, clients.first
|
53
|
+
end
|
54
|
+
it "should debuffer non-transactional SEND frames" do
|
55
|
+
frame = OnStomp::Components::Frame.new 'SEND',
|
56
|
+
{ :destination => '/queue/test' }, 'body of message'
|
57
|
+
active_client.trigger_before_transmitting frame
|
58
|
+
active_client.trigger_after_transmitting frame
|
59
|
+
active_client.should_not_receive(:transmit)
|
60
|
+
failover.trigger_failover_event :connected, :on, clients.first
|
61
|
+
end
|
62
|
+
it "should replay SUBSCRIBE frames, even fully written ones" do
|
63
|
+
frame = OnStomp::Components::Frame.new 'SUBSCRIBE',
|
64
|
+
{ :destination => '/queue/test', :id => 's-1234' }
|
65
|
+
active_client.trigger_before_transmitting frame
|
66
|
+
active_client.trigger_after_transmitting frame
|
67
|
+
active_client.should_receive(:transmit).with(an_onstomp_frame(
|
68
|
+
'SUBSCRIBE', {:destination => '/queue/test', :id => 's-1234'}))
|
69
|
+
failover.trigger_failover_event :connected, :on, clients.first
|
70
|
+
end
|
71
|
+
it "should debuffer SUBSCRIBE frames as soon as UNSUBSCRIBE is in the write buffer (does not need to be fully written)" do
|
72
|
+
frame = OnStomp::Components::Frame.new 'SUBSCRIBE',
|
73
|
+
{ :destination => '/queue/test', :id => 's-1234' }
|
74
|
+
active_client.trigger_before_transmitting frame
|
75
|
+
active_client.trigger_after_transmitting frame
|
76
|
+
|
77
|
+
frame = OnStomp::Components::Frame.new 'UNSUBSCRIBE', :id => 's-1234'
|
78
|
+
|
79
|
+
active_client.trigger_before_transmitting frame
|
80
|
+
active_client.should_not_receive :transmit
|
81
|
+
failover.trigger_failover_event :connected, :on, clients.first
|
82
|
+
end
|
83
|
+
it "should not debuffer incomplete transactions" do
|
84
|
+
replayed = []
|
85
|
+
partial_transaction.each do |f|
|
86
|
+
active_client.trigger_before_transmitting f
|
87
|
+
active_client.trigger_after_transmitting f
|
88
|
+
end
|
89
|
+
active_client.stub(:transmit).and_return { |f| replayed << f; f }
|
90
|
+
failover.trigger_failover_event :connected, :on, clients.first
|
91
|
+
replayed.should == partial_transaction.reject { |f| ['ACK', 'NACK'].include? f.command }
|
92
|
+
end
|
93
|
+
['ABORT', 'COMMIT'].each do |trans_fin|
|
94
|
+
it "should not debuffer transactions when #{trans_fin} is only buffered" do
|
95
|
+
fin_frame = OnStomp::Components::Frame.new trans_fin, {:transaction => 't-1234'}
|
96
|
+
replayed = []
|
97
|
+
partial_transaction.each do |f|
|
98
|
+
active_client.trigger_before_transmitting f
|
99
|
+
active_client.trigger_after_transmitting f
|
100
|
+
end
|
101
|
+
active_client.trigger_before_transmitting fin_frame
|
102
|
+
active_client.stub(:transmit).and_return { |f| replayed << f; f }
|
103
|
+
failover.trigger_failover_event :connected, :on, clients.first
|
104
|
+
replayed.should ==
|
105
|
+
(partial_transaction.reject { |f| ['ACK', 'NACK'].include? f.command } +
|
106
|
+
[fin_frame])
|
107
|
+
end
|
108
|
+
it "should debuffer a transaction when #{trans_fin} has been written" do
|
109
|
+
fin_frame = OnStomp::Components::Frame.new trans_fin, {:transaction => 't-1234'}
|
110
|
+
replayed = []
|
111
|
+
partial_transaction.each do |f|
|
112
|
+
active_client.trigger_before_transmitting f
|
113
|
+
active_client.trigger_after_transmitting f
|
114
|
+
end
|
115
|
+
active_client.trigger_before_transmitting fin_frame
|
116
|
+
active_client.trigger_after_transmitting fin_frame
|
117
|
+
active_client.stub(:transmit).and_return { |f| replayed << f; f }
|
118
|
+
failover.trigger_failover_event :connected, :on, clients.first
|
119
|
+
replayed.should be_empty
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "replaying while you replay (Xzibit Approved)" do
|
125
|
+
let(:frame_list) {
|
126
|
+
[
|
127
|
+
OnStomp::Components::Frame.new('SEND', {:destination => '/queue/test'}, 'message 1'),
|
128
|
+
OnStomp::Components::Frame.new('SUBSCRIBE', :id => 's-1234', :destination => '/queue/test'),
|
129
|
+
OnStomp::Components::Frame.new('BEGIN', :transaction => 't-1234'),
|
130
|
+
OnStomp::Components::Frame.new('NACK', :'message-id' => 'm-99992'),
|
131
|
+
OnStomp::Components::Frame.new('SEND', {:transaction => 't-1234'}, 'message 2'),
|
132
|
+
OnStomp::Components::Frame.new('ACK', :'message-id' => 'm-99993'),
|
133
|
+
OnStomp::Components::Frame.new('SEND', {:transaction => 't-1234'}, 'message 3'),
|
134
|
+
OnStomp::Components::Frame.new('UNSUBSCRIBE', :id => 's-1234'),
|
135
|
+
OnStomp::Components::Frame.new('SUBSCRIBE', :id => 's-5678', :destination => '/queue/test'),
|
136
|
+
OnStomp::Components::Frame.new('ACK', {:transaction => 't-1234',
|
137
|
+
:'message-id' => 'm-99991'}),
|
138
|
+
OnStomp::Components::Frame.new('SEND', {:transaction => 't-1234'}, 'message 4'),
|
139
|
+
]
|
140
|
+
}
|
141
|
+
let(:expected_replay) {
|
142
|
+
frame_list.reject do |f|
|
143
|
+
['ACK', 'NACK'].include?(f.command) || f[:id] == 's-1234'
|
144
|
+
end
|
145
|
+
}
|
146
|
+
let(:client1_frames) { [] }
|
147
|
+
let(:client3_frames) { [] }
|
148
|
+
before(:each) do
|
149
|
+
buffer
|
150
|
+
frame_list.each do |f|
|
151
|
+
active_client.trigger_before_transmitting f
|
152
|
+
end
|
153
|
+
end
|
154
|
+
it "should replay properly if failover reconnects while replaying" do
|
155
|
+
extra_frame = OnStomp::Components::Frame.new 'SEND',
|
156
|
+
{:destination => '/queue/test'}, 'yet another freaking message'
|
157
|
+
clients.last.stub(:transmit).and_return { |f| client3_frames << f; f }
|
158
|
+
active_client.stub(:transmit).and_return do |f|
|
159
|
+
client1_frames << f
|
160
|
+
if f.command == 'BEGIN'
|
161
|
+
# Add a fresh frame to the buffer
|
162
|
+
active_client.trigger_before_transmitting extra_frame
|
163
|
+
# Signal the a reconnect in the midst of replaying.
|
164
|
+
failover.trigger_failover_event :connected, :on, clients.last
|
165
|
+
end
|
166
|
+
f
|
167
|
+
end
|
168
|
+
failover.trigger_failover_event :connected, :on, active_client
|
169
|
+
client1_frames.should == expected_replay
|
170
|
+
client3_frames.should == expected_replay + [extra_frame]
|
171
|
+
end
|
172
|
+
end
|
7
173
|
end
|
8
174
|
end
|
@@ -4,7 +4,9 @@ require 'spec_helper'
|
|
4
4
|
module OnStomp::Failover
|
5
5
|
describe Client, :failover => true do
|
6
6
|
let(:active_client) {
|
7
|
-
mock('active client')
|
7
|
+
mock('active client').tap do |m|
|
8
|
+
m.extend OnStomp::Interfaces::ClientEvents
|
9
|
+
end
|
8
10
|
}
|
9
11
|
let(:client) {
|
10
12
|
Client.new('failover:(stomp:///,stomp+ssl:///)').tap do |c|
|
@@ -26,6 +28,73 @@ module OnStomp::Failover
|
|
26
28
|
end
|
27
29
|
end
|
28
30
|
|
31
|
+
describe ".connect" do
|
32
|
+
it "should call reconnect" do
|
33
|
+
client.should_receive(:reconnect).and_return(true)
|
34
|
+
client.connect.should == client
|
35
|
+
end
|
36
|
+
it "should raise an maximum retries error if reconnect is false" do
|
37
|
+
client.stub(:reconnect => false)
|
38
|
+
lambda {
|
39
|
+
client.connect
|
40
|
+
}.should raise_error(OnStomp::Failover::MaximumRetriesExceededError)
|
41
|
+
end
|
42
|
+
it "should trigger :on_failover_connect_failure if connecting raises an exception" do
|
43
|
+
triggered = false
|
44
|
+
client.on_failover_connect_failure { |*_| triggered = true }
|
45
|
+
active_client.stub(:connected? => false)
|
46
|
+
active_client.should_receive(:connect).and_return do
|
47
|
+
active_client.stub(:connected? => true)
|
48
|
+
raise "find yourself a big lady"
|
49
|
+
end
|
50
|
+
client.connect
|
51
|
+
triggered.should be_true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe ".disconnect" do
|
56
|
+
let(:connection) {
|
57
|
+
mock('connection').tap do |m|
|
58
|
+
m.extend OnStomp::Interfaces::ConnectionEvents
|
59
|
+
end
|
60
|
+
}
|
61
|
+
let(:client_pool) {
|
62
|
+
[active_client].tap do |m|
|
63
|
+
m.stub(:next_client => active_client)
|
64
|
+
end
|
65
|
+
}
|
66
|
+
before(:each) do
|
67
|
+
# Get the hooks installed on our mocks
|
68
|
+
active_client.stub(:connection => connection)
|
69
|
+
client.stub(:client_pool => client_pool)
|
70
|
+
client.__send__ :create_client_pool
|
71
|
+
end
|
72
|
+
it "should do nothing special if there is no active client" do
|
73
|
+
client.stub(:active_client => nil)
|
74
|
+
client.disconnect
|
75
|
+
end
|
76
|
+
it "should wait until the active client is ready, then call its disconnect method" do
|
77
|
+
active_client.stub(:connected? => false)
|
78
|
+
active_client.stub(:connect).and_return do
|
79
|
+
active_client.stub(:connected? => true)
|
80
|
+
end
|
81
|
+
client.connect
|
82
|
+
actual_disconnect = client.method(:disconnect)
|
83
|
+
client.stub(:disconnect).and_return do |*args|
|
84
|
+
active_client.stub(:connected? => false)
|
85
|
+
# Fire this off in a separate thread, as would be the real case
|
86
|
+
t = Thread.new do
|
87
|
+
connection.trigger_event :on_closed, active_client, connection
|
88
|
+
end
|
89
|
+
Thread.pass while t.alive?
|
90
|
+
actual_disconnect.call *args
|
91
|
+
end
|
92
|
+
|
93
|
+
active_client.should_receive(:disconnect).with(:header1 => 'value 1')
|
94
|
+
client.disconnect :header1 => 'value 1'
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
29
98
|
describe ".transmit" do
|
30
99
|
it "should transmit on the active client if there is one" do
|
31
100
|
active_client.should_receive(:transmit).with('test', :coming => 'home')
|