onstomp 1.0.0 → 1.0.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.
- 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')
|