stomper 1.0.0 → 2.0.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.
- data/.gitignore +5 -0
- data/{spec/spec.opts → .rspec} +0 -2
- data/Gemfile +4 -0
- data/LICENSE +201 -201
- data/README.md +130 -0
- data/Rakefile +5 -0
- data/examples/basic.rb +38 -0
- data/examples/events.rb +54 -0
- data/features/acking_messages.feature +147 -0
- data/features/disconnecting.feature +12 -0
- data/features/establish_connection.feature +44 -0
- data/features/protocol_version_negotiation.feature +61 -0
- data/features/receipts.feature +72 -0
- data/features/scopes.feature +32 -0
- data/features/secure_connections.feature +38 -0
- data/features/send_and_message.feature +28 -0
- data/features/steps/acking_messages_steps.rb +39 -0
- data/features/steps/disconnecting_steps.rb +8 -0
- data/features/steps/establish_connection_steps.rb +74 -0
- data/features/steps/frame_transmission_steps.rb +35 -0
- data/features/steps/protocol_version_negotiation_steps.rb +15 -0
- data/features/steps/receipts_steps.rb +79 -0
- data/features/steps/scopes_steps.rb +52 -0
- data/features/steps/secure_connections_steps.rb +41 -0
- data/features/steps/send_and_message_steps.rb +35 -0
- data/features/steps/subscribing_steps.rb +36 -0
- data/features/steps/threaded_receiver_steps.rb +8 -0
- data/features/steps/transactions_steps.rb +0 -0
- data/features/subscribing.feature +151 -0
- data/features/support/env.rb +11 -0
- data/features/support/header_helpers.rb +12 -0
- data/features/support/ssl/README +6 -0
- data/features/support/ssl/broker_cert.csr +17 -0
- data/features/support/ssl/broker_cert.pem +72 -0
- data/features/support/ssl/broker_key.pem +27 -0
- data/features/support/ssl/client_cert.csr +17 -0
- data/features/support/ssl/client_cert.pem +72 -0
- data/features/support/ssl/client_key.pem +27 -0
- data/features/support/ssl/demoCA/cacert.pem +17 -0
- data/features/support/ssl/demoCA/index.txt +2 -0
- data/features/support/ssl/demoCA/index.txt.attr +1 -0
- data/features/support/ssl/demoCA/index.txt.attr.old +1 -0
- data/features/support/ssl/demoCA/index.txt.old +1 -0
- data/features/support/ssl/demoCA/newcerts/01.pem +72 -0
- data/features/support/ssl/demoCA/newcerts/02.pem +72 -0
- data/features/support/ssl/demoCA/private/cakey.pem +17 -0
- data/features/support/ssl/demoCA/serial +1 -0
- data/features/support/ssl/demoCA/serial.old +1 -0
- data/features/support/test_stomp_server.rb +150 -0
- data/features/threaded_receiver.feature +11 -0
- data/features/transactions.feature +66 -0
- data/lib/stomper.rb +30 -20
- data/lib/stomper/connection.rb +442 -102
- data/lib/stomper/errors.rb +59 -0
- data/lib/stomper/extensions.rb +10 -0
- data/lib/stomper/extensions/common.rb +258 -0
- data/lib/stomper/extensions/events.rb +213 -0
- data/lib/stomper/extensions/heartbeat.rb +101 -0
- data/lib/stomper/extensions/scoping.rb +56 -0
- data/lib/stomper/frame.rb +54 -0
- data/lib/stomper/frame_serializer.rb +217 -0
- data/lib/stomper/headers.rb +15 -0
- data/lib/stomper/receipt_manager.rb +36 -0
- data/lib/stomper/receivers.rb +7 -0
- data/lib/stomper/receivers/threaded.rb +71 -0
- data/lib/stomper/scopes.rb +9 -0
- data/lib/stomper/scopes/header_scope.rb +49 -0
- data/lib/stomper/scopes/receipt_scope.rb +44 -0
- data/lib/stomper/scopes/transaction_scope.rb +109 -0
- data/lib/stomper/sockets.rb +66 -28
- data/lib/stomper/subscription_manager.rb +79 -0
- data/lib/stomper/support.rb +68 -0
- data/lib/stomper/support/1.8/frame_serializer.rb +53 -0
- data/lib/stomper/support/1.8/headers.rb +183 -0
- data/lib/stomper/support/1.9/frame_serializer.rb +64 -0
- data/lib/stomper/support/1.9/headers.rb +172 -0
- data/lib/stomper/support/ruby.rb +13 -0
- data/lib/stomper/uris.rb +49 -0
- data/lib/stomper/version.rb +7 -0
- data/spec/spec_helper.rb +13 -9
- data/spec/stomper/connection_spec.rb +712 -0
- data/spec/stomper/extensions/common_spec.rb +187 -0
- data/spec/stomper/extensions/events_spec.rb +78 -0
- data/spec/stomper/extensions/heartbeat_spec.rb +103 -0
- data/spec/stomper/extensions/scoping_spec.rb +21 -0
- data/spec/stomper/frame_serializer_1.8_spec.rb +318 -0
- data/spec/stomper/frame_serializer_spec.rb +316 -0
- data/spec/stomper/frame_spec.rb +36 -0
- data/spec/stomper/headers_spec.rb +224 -0
- data/spec/stomper/receipt_manager_spec.rb +91 -0
- data/spec/stomper/receivers/threaded_spec.rb +116 -0
- data/spec/stomper/scopes/header_scope_spec.rb +42 -0
- data/spec/stomper/scopes/receipt_scope_spec.rb +51 -0
- data/spec/stomper/scopes/transaction_scope_spec.rb +183 -0
- data/spec/stomper/sockets_spec.rb +113 -0
- data/spec/stomper/subscription_manager_spec.rb +107 -0
- data/spec/stomper/support_spec.rb +69 -0
- data/spec/stomper/uris_spec.rb +54 -0
- data/spec/stomper_spec.rb +9 -0
- data/spec/support/custom_argument_matchers.rb +57 -0
- data/spec/support/existential_frame_matchers.rb +19 -0
- data/spec/support/frame_header_matchers.rb +10 -0
- data/stomper.gemspec +30 -0
- metadata +272 -97
- data/AUTHORS +0 -21
- data/CHANGELOG +0 -20
- data/README.rdoc +0 -120
- data/lib/stomper/client.rb +0 -34
- data/lib/stomper/frame_reader.rb +0 -73
- data/lib/stomper/frame_writer.rb +0 -21
- data/lib/stomper/frames.rb +0 -39
- data/lib/stomper/frames/abort.rb +0 -10
- data/lib/stomper/frames/ack.rb +0 -25
- data/lib/stomper/frames/begin.rb +0 -11
- data/lib/stomper/frames/client_frame.rb +0 -89
- data/lib/stomper/frames/commit.rb +0 -10
- data/lib/stomper/frames/connect.rb +0 -10
- data/lib/stomper/frames/connected.rb +0 -30
- data/lib/stomper/frames/disconnect.rb +0 -10
- data/lib/stomper/frames/error.rb +0 -21
- data/lib/stomper/frames/message.rb +0 -48
- data/lib/stomper/frames/receipt.rb +0 -19
- data/lib/stomper/frames/send.rb +0 -10
- data/lib/stomper/frames/server_frame.rb +0 -38
- data/lib/stomper/frames/subscribe.rb +0 -42
- data/lib/stomper/frames/unsubscribe.rb +0 -19
- data/lib/stomper/open_uri_interface.rb +0 -41
- data/lib/stomper/receipt_handlers.rb +0 -23
- data/lib/stomper/receiptor.rb +0 -38
- data/lib/stomper/subscriber.rb +0 -76
- data/lib/stomper/subscription.rb +0 -128
- data/lib/stomper/subscriptions.rb +0 -95
- data/lib/stomper/threaded_receiver.rb +0 -59
- data/lib/stomper/transaction.rb +0 -185
- data/lib/stomper/transactor.rb +0 -50
- data/lib/stomper/uri.rb +0 -55
- data/spec/client_spec.rb +0 -29
- data/spec/connection_spec.rb +0 -22
- data/spec/frame_reader_spec.rb +0 -37
- data/spec/frame_writer_spec.rb +0 -27
- data/spec/frames/client_frame_spec.rb +0 -66
- data/spec/frames/indirect_frame_spec.rb +0 -45
- data/spec/frames/server_frame_spec.rb +0 -85
- data/spec/open_uri_interface_spec.rb +0 -132
- data/spec/receiptor_spec.rb +0 -35
- data/spec/shared_connection_examples.rb +0 -79
- data/spec/subscriber_spec.rb +0 -77
- data/spec/subscription_spec.rb +0 -157
- data/spec/subscriptions_spec.rb +0 -145
- data/spec/threaded_receiver_spec.rb +0 -33
- data/spec/transaction_spec.rb +0 -139
- data/spec/transactor_spec.rb +0 -46
@@ -0,0 +1,91 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module Stomper
|
5
|
+
describe ReceiptManager do
|
6
|
+
before(:each) do
|
7
|
+
@connection = mock("connection")
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should bind to the on_receipt event handler" do
|
11
|
+
receipt = mock('receipt')
|
12
|
+
@connection.should_receive(:on_receipt)
|
13
|
+
@connection.should_receive(:before_disconnect)
|
14
|
+
@receipt_manager = ReceiptManager.new(@connection)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "usage" do
|
18
|
+
before(:each) do
|
19
|
+
@connection.extend ::Stomper::Extensions::Events
|
20
|
+
@receipt_manager = ReceiptManager.new(@connection)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should add callbacks that are invoked by a matching receipt" do
|
24
|
+
triggered = false
|
25
|
+
@receipt_manager.add('1234', lambda { |r| triggered = true })
|
26
|
+
@connection.__send__(:trigger_received_frame, ::Stomper::Frame.new('RECEIPT', { :'receipt-id' => '1234' }))
|
27
|
+
triggered.should be_true
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should not invoke callbacks that don't match the receipt" do
|
31
|
+
triggered = false
|
32
|
+
@receipt_manager.add('1234', lambda { |r| triggered = true })
|
33
|
+
@receipt_manager.add('5678', lambda { |r| triggered = true })
|
34
|
+
@connection.__send__(:trigger_received_frame, ::Stomper::Frame.new('RECEIPT', { :'receipt-id' => '12345' }))
|
35
|
+
triggered.should be_false
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should not invoke the same callback more than once" do
|
39
|
+
triggered = 0
|
40
|
+
@receipt_manager.add('1234', lambda { |r| triggered += 1 })
|
41
|
+
@connection.__send__(:trigger_received_frame, ::Stomper::Frame.new('RECEIPT', { :'receipt-id' => '1234' }))
|
42
|
+
@connection.__send__(:trigger_received_frame, ::Stomper::Frame.new('RECEIPT', { :'receipt-id' => '1234' }))
|
43
|
+
triggered.should == 1
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should allow a receipt handler to be registered within a callback" do
|
47
|
+
triggered = [false, false]
|
48
|
+
callback = lambda do |r|
|
49
|
+
triggered[0] = true
|
50
|
+
@receipt_manager.add('4567', lambda { |r| triggered[1] = true })
|
51
|
+
end
|
52
|
+
@receipt_manager.add('1234', callback)
|
53
|
+
@connection.__send__(:trigger_received_frame, ::Stomper::Frame.new('RECEIPT', { :'receipt-id' => '1234' }))
|
54
|
+
@connection.__send__(:trigger_received_frame, ::Stomper::Frame.new('RECEIPT', { :'receipt-id' => '4567' }))
|
55
|
+
triggered.should == [true, true]
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should allow a receipt handler to be registered within a callback in separate threads" do
|
59
|
+
triggered = [false, false]
|
60
|
+
started_r1 = false
|
61
|
+
callback = lambda do |r|
|
62
|
+
triggered[0] = true
|
63
|
+
Thread.stop
|
64
|
+
end
|
65
|
+
@receipt_manager.add('1234', callback)
|
66
|
+
r_1 = Thread.new(@connection) do |c|
|
67
|
+
started_r1 = true
|
68
|
+
Thread.stop
|
69
|
+
c.__send__(:trigger_received_frame, ::Stomper::Frame.new('RECEIPT', { :'receipt-id' => '1234' }))
|
70
|
+
end
|
71
|
+
r_2 = Thread.new(@connection) do |c|
|
72
|
+
Thread.pass until triggered[0]
|
73
|
+
@receipt_manager.add('4567', lambda { |r| triggered[1] = true })
|
74
|
+
r_1.run
|
75
|
+
c.__send__(:trigger_received_frame, ::Stomper::Frame.new('RECEIPT', { :'receipt-id' => '4567' }))
|
76
|
+
end
|
77
|
+
Thread.pass until started_r1
|
78
|
+
r_1.run
|
79
|
+
r_1.join
|
80
|
+
r_2.join
|
81
|
+
triggered.should == [true, true]
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should close a connection when a RECEIPT for the DISCONNECT frame is received" do
|
85
|
+
@connection.__send__(:trigger_before_transmitted_frame, ::Stomper::Frame.new('DISCONNECT', {:receipt => 'd-1234'}))
|
86
|
+
@connection.should_receive(:close)
|
87
|
+
@connection.__send__(:trigger_received_frame, ::Stomper::Frame.new('RECEIPT', { :'receipt-id' => 'd-1234' }))
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module Stomper::Receivers
|
5
|
+
describe Threaded do
|
6
|
+
before(:each) do
|
7
|
+
@connection = mock("connection")
|
8
|
+
@receiver = Threaded.new(@connection)
|
9
|
+
@receive_called = false
|
10
|
+
@received_frame = mock('frame')
|
11
|
+
end
|
12
|
+
|
13
|
+
def mock_receive_call
|
14
|
+
@connection.stub!(:receive).and_return(@received_frame)
|
15
|
+
end
|
16
|
+
|
17
|
+
def expect_receive_call
|
18
|
+
@connection.should_receive(:receive).at_least(:once).and_return do
|
19
|
+
@receive_called = true
|
20
|
+
@received_frame
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def wait_until_receive_called(reset=true)
|
25
|
+
@receive_called = false if reset
|
26
|
+
Thread.pass until @receive_called
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should start running when :start is called, creating a new thread" do
|
30
|
+
expect_receive_call
|
31
|
+
initial_threads = Thread.list.size
|
32
|
+
@receiver.start
|
33
|
+
wait_until_receive_called
|
34
|
+
@receiver.running?.should be_true
|
35
|
+
Thread.list.size.should == (initial_threads + 1)
|
36
|
+
@receiver.stop
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should stop running when :stop is called, joining its run thread" do
|
40
|
+
mock_receive_call
|
41
|
+
initial_threads = Thread.list.size
|
42
|
+
@receiver.start
|
43
|
+
@receiver.stop
|
44
|
+
@receiver.running?.should be_false
|
45
|
+
Thread.list.size.should == initial_threads
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should receive frames from its connection until it is stopped" do
|
49
|
+
thread_check = false
|
50
|
+
@connection.should_receive(:receive).twice.and_return do
|
51
|
+
Thread.pass until thread_check
|
52
|
+
thread_check = false
|
53
|
+
@received_frame
|
54
|
+
end
|
55
|
+
@receiver.start
|
56
|
+
thread_check = true
|
57
|
+
Thread.pass while thread_check
|
58
|
+
thread_check = true
|
59
|
+
@receiver.stop
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should not create more than one thread if :start is invoked repeatedly" do
|
63
|
+
expect_receive_call
|
64
|
+
initial_threads = Thread.list.size
|
65
|
+
running_threads = initial_threads+1
|
66
|
+
|
67
|
+
@receiver.start
|
68
|
+
wait_until_receive_called
|
69
|
+
Thread.list.size.should == running_threads
|
70
|
+
|
71
|
+
@receiver.start
|
72
|
+
wait_until_receive_called
|
73
|
+
Thread.list.size.should == running_threads
|
74
|
+
|
75
|
+
@receiver.start
|
76
|
+
wait_until_receive_called
|
77
|
+
Thread.list.size.should == running_threads
|
78
|
+
|
79
|
+
@receiver.stop
|
80
|
+
Thread.list.size.should == initial_threads
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should not raise an error if :stop is invoked repeatedly" do
|
84
|
+
expect_receive_call
|
85
|
+
@receiver.start
|
86
|
+
wait_until_receive_called
|
87
|
+
|
88
|
+
@receiver.stop
|
89
|
+
lambda { @receiver.stop }.should_not raise_error
|
90
|
+
lambda { @receiver.stop }.should_not raise_error
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should stop itself if receiving a frame raises any error" do
|
94
|
+
@connection.should_receive(:receive).and_raise('stopping the receiver')
|
95
|
+
@receiver.start
|
96
|
+
Thread.pass while @receiver.running?
|
97
|
+
lambda { @receiver.stop }.should raise_error('stopping the receiver')
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should propegate an IOError if the connection is still connected" do
|
101
|
+
@connection.should_receive(:connected?).and_return(true)
|
102
|
+
@connection.should_receive(:receive).and_raise(IOError.new('stopping the receiver'))
|
103
|
+
@receiver.start
|
104
|
+
Thread.pass while @receiver.running?
|
105
|
+
lambda { @receiver.stop }.should raise_error(IOError)
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should not propegate an IOError if the connection is not connected" do
|
109
|
+
@connection.should_receive(:connected?).and_return(false)
|
110
|
+
@connection.should_receive(:receive).and_raise(IOError.new('stopping the receiver'))
|
111
|
+
@receiver.start
|
112
|
+
Thread.pass while @receiver.running?
|
113
|
+
lambda { @receiver.stop }.should_not raise_error
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module Stomper::Scopes
|
5
|
+
describe HeaderScope do
|
6
|
+
before(:each) do
|
7
|
+
@connection = mock("connection", :is_a? => true, :version => '1.1')
|
8
|
+
@headers = { :global_1 => 'turbo', 'global_2' => 'is me', :persistent => true }
|
9
|
+
@connection.stub!(:subscription_manager).and_return(mock('subscription manager', {
|
10
|
+
:subscribed_id? => true
|
11
|
+
}))
|
12
|
+
@scope = HeaderScope.new(@connection, @headers)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should apply the headers to any frame generated on its Common interface" do
|
16
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'SEND'))
|
17
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'BEGIN'))
|
18
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'UNSUBSCRIBE'))
|
19
|
+
@scope.send("/queue/test", "body of message", { :local_1 => 'my header' })
|
20
|
+
@scope.begin("transaction-1234", { :local_2 => 'other header'})
|
21
|
+
@scope.unsubscribe('no-real-destination')
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should evaluate a proc through itself if one is provided" do
|
25
|
+
scope_block = lambda do |h|
|
26
|
+
h.abort('transaction-1234')
|
27
|
+
h.subscribe('/queue/test')
|
28
|
+
h.commit('transaction-1234')
|
29
|
+
end
|
30
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'ABORT'))
|
31
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'SUBSCRIBE'))
|
32
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'COMMIT'))
|
33
|
+
@scope.apply_to(scope_block)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should override its headers with those passed through the frame methods" do
|
37
|
+
overridden_headers = @headers.merge(:persistent => 'false')
|
38
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(overridden_headers, 'SEND'))
|
39
|
+
@scope.send('/queue/test', 'body of message', { :persistent => false })
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module Stomper::Scopes
|
5
|
+
describe ReceiptScope do
|
6
|
+
before(:each) do
|
7
|
+
@receipt_manager = mock("receipt manager")
|
8
|
+
@connection = mock("connection", :is_a? => true, :version => '1.1')
|
9
|
+
@connection.stub!(:receipt_manager => @receipt_manager)
|
10
|
+
@connection.stub!(:subscription_manager).and_return(mock('subscription manager', {
|
11
|
+
:subscribed_id? => true
|
12
|
+
}))
|
13
|
+
@scope = ReceiptScope.new(@connection, {})
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should add entries to the connection's receipt manager" do
|
17
|
+
scope_block = lambda do |r|
|
18
|
+
end
|
19
|
+
@scope.apply_to(scope_block)
|
20
|
+
|
21
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers({ :destination => '/queue/test', :receipt => 'receipt-1234' }, 'SEND'))
|
22
|
+
@receipt_manager.should_receive(:add).with('receipt-1234', an_instance_of(Proc)).once
|
23
|
+
@scope.send('/queue/test', 'body of message', { :receipt => 'receipt-1234' })
|
24
|
+
|
25
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers({ :destination => '/queue/test', :receipt => 'receipt-4567' }, 'SUBSCRIBE'))
|
26
|
+
@receipt_manager.should_receive(:add).with('receipt-4567', an_instance_of(Proc)).once
|
27
|
+
@scope.subscribe('/queue/test', { :receipt => 'receipt-4567' })
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should set up receipt ids automatically when none are specified in the headers" do
|
31
|
+
scope_block = lambda do |r|
|
32
|
+
end
|
33
|
+
@connection.stub!(:transmit) { |f| f }
|
34
|
+
@scope.apply_to(scope_block)
|
35
|
+
|
36
|
+
frames = []
|
37
|
+
@receipt_manager.should_receive(:add).with('receipt-1234', an_instance_of(Proc)).once
|
38
|
+
frames << @scope.send('/queue/test', 'body of message', { :receipt => 'receipt-1234' })
|
39
|
+
frames.last[:receipt].should == 'receipt-1234'
|
40
|
+
|
41
|
+
@receipt_manager.should_receive(:add).with(an_instance_of(String), an_instance_of(Proc)).twice
|
42
|
+
frames << @scope.unsubscribe('/queue/test')
|
43
|
+
frames.last[:receipt].should_not be_empty
|
44
|
+
|
45
|
+
frames << @scope.ack('msg-1234', 'sub-5678')
|
46
|
+
frames.last[:receipt].should_not be_empty
|
47
|
+
|
48
|
+
frames.map { |r| r[:receipt] }.uniq.should == frames.map { |r| r[:receipt] }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module Stomper::Scopes
|
5
|
+
describe TransactionScope do
|
6
|
+
before(:each) do
|
7
|
+
@connection = mock("connection", :is_a? => true, :version => '1.1')
|
8
|
+
@headers = { :transaction => 'tx-1234' }
|
9
|
+
@connection.stub!(:subscription_manager).and_return(mock('subscription manager', {
|
10
|
+
:subscribed_id? => true
|
11
|
+
}))
|
12
|
+
@scope = TransactionScope.new(@connection, @headers)
|
13
|
+
@connection.should_receive(:transmit).at_most(:once).with(stomper_frame_with_headers(@headers, 'BEGIN'))
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should generate a transaction ID if one was not provided" do
|
17
|
+
auto_scope = TransactionScope.new(@connection, {})
|
18
|
+
auto_scope_name = auto_scope.transaction
|
19
|
+
auto_scope_name.should_not be_empty
|
20
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers({:transaction => auto_scope_name}, 'BEGIN'))
|
21
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers({:transaction => auto_scope_name}, 'SEND'))
|
22
|
+
auto_scope.send('/queue/test', 'body of message')
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should apply a transaction header to SEND" do
|
26
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'SEND'))
|
27
|
+
@scope.send('/queue/test', 'body of message')
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should apply a transaction header to ACK" do
|
31
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'ACK'))
|
32
|
+
@scope.ack('msg-001', 'sub-001')
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should apply a transaction header to COMMIT" do
|
36
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'COMMIT'))
|
37
|
+
@scope.commit
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should apply a transaction header to ABORT" do
|
41
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'ABORT'))
|
42
|
+
@scope.abort
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should apply a transaction header to COMMIT" do
|
46
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'COMMIT'))
|
47
|
+
@scope.commit
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should apply a transaction header to NACK" do
|
51
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'NACK'))
|
52
|
+
@scope.nack('msg-001', 'sub-001')
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should not apply a transaction header to SUBSCRIBE, UNSUBSCRIBE" do
|
56
|
+
@connection.stub!(:transmit).and_return { |f| f }
|
57
|
+
subscribe = @scope.subscribe('/queue/test')
|
58
|
+
unsubscribe = @scope.unsubscribe(subscribe)
|
59
|
+
subscribe.headers.has?(:transaction).should be_false
|
60
|
+
unsubscribe.headers.has?(:transaction).should be_false
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should raise an error when beginning a transaction that has already begun" do
|
64
|
+
@connection.stub!(:transmit).and_return { |f| f }
|
65
|
+
@scope.send('/queue/test', 'body of message')
|
66
|
+
lambda { @scope.begin }.should raise_error
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should raise an error when aborting a transaction that has already aborted" do
|
70
|
+
@connection.stub!(:transmit).and_return { |f| f }
|
71
|
+
@scope.abort
|
72
|
+
lambda { @scope.abort }.should raise_error
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should raise an error when aborting a transaction that has already committed" do
|
76
|
+
@connection.stub!(:transmit).and_return { |f| f }
|
77
|
+
@scope.commit
|
78
|
+
lambda { @scope.abort }.should raise_error
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should raise an error when committing a transaction that has already aborted" do
|
82
|
+
@connection.stub!(:transmit).and_return { |f| f }
|
83
|
+
@scope.abort
|
84
|
+
lambda { @scope.commit }.should raise_error
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should raise an error when committing a transaction that has already committed" do
|
88
|
+
@connection.stub!(:transmit).and_return { |f| f }
|
89
|
+
@scope.commit
|
90
|
+
lambda { @scope.commit }.should raise_error
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should evaluate a block as a transaction and commit it if the block does not raise an error" do
|
94
|
+
scope_block = lambda do |t|
|
95
|
+
t.send('/queue/test', 'body of message')
|
96
|
+
t.ack('msg-1234', 'sub-4321')
|
97
|
+
t.nack('msg-5678', 'sub-8765')
|
98
|
+
end
|
99
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'SEND'))
|
100
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'ACK'))
|
101
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'NACK'))
|
102
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'COMMIT'))
|
103
|
+
@scope.apply_to(scope_block)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should evaluate a block as a transaction but do nothing if the transaction never started" do
|
107
|
+
scope_block = lambda do |t|
|
108
|
+
end
|
109
|
+
@connection.should_not_receive(:transmit).with(stomper_frame(nil, {}, 'BEGIN'))
|
110
|
+
@connection.should_not_receive(:transmit).with(stomper_frame(nil, {}, 'COMMIT'))
|
111
|
+
@scope.apply_to(scope_block)
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should evaluate a block as a transaction and abort the transaction and raise an exception if an exception is raised" do
|
115
|
+
scope_block = lambda do |t|
|
116
|
+
t.send('/queue/test', 'body of message')
|
117
|
+
t.ack('msg-1234', 'sub-4321')
|
118
|
+
raise "Time to abort!"
|
119
|
+
t.nack('msg-5678', 'sub-8765')
|
120
|
+
end
|
121
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'SEND'))
|
122
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'ACK'))
|
123
|
+
@connection.should_not_receive(:transmit).with(stomper_frame(nil, {}, 'NACK'))
|
124
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'ABORT'))
|
125
|
+
@connection.should_not_receive(:transmit).with(stomper_frame(nil, {}, 'COMMIT'))
|
126
|
+
lambda { @scope.apply_to(scope_block) }.should raise_error('Time to abort!')
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should not commit a transaction block that was manually aborted" do
|
130
|
+
scope_block = lambda do |t|
|
131
|
+
t.send('/queue/test', 'body of message')
|
132
|
+
t.ack('msg-1234', 'sub-4321')
|
133
|
+
t.abort
|
134
|
+
end
|
135
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'SEND'))
|
136
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'ACK'))
|
137
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'ABORT'))
|
138
|
+
@connection.should_not_receive(:transmit).with(stomper_frame(nil, {}, 'COMMIT'))
|
139
|
+
lambda { @scope.apply_to(scope_block) }.should_not raise_error
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should not re-commit a transaction block that was manually committed" do
|
143
|
+
scope_block = lambda do |t|
|
144
|
+
t.send('/queue/test', 'body of message')
|
145
|
+
t.ack('msg-1234', 'sub-4321')
|
146
|
+
t.commit
|
147
|
+
end
|
148
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'SEND'))
|
149
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'ACK'))
|
150
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'COMMIT'))
|
151
|
+
lambda { @scope.apply_to(scope_block) }.should_not raise_error
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should raise an error if further transactionable frames are sent after the transaction has been aborted" do
|
155
|
+
scope_block = lambda do |t|
|
156
|
+
t.send('/queue/test', 'body of message')
|
157
|
+
t.ack('msg-1234', 'sub-4321')
|
158
|
+
t.abort
|
159
|
+
t.nack('msg-5678', 'sub-8765')
|
160
|
+
end
|
161
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'SEND'))
|
162
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'ACK'))
|
163
|
+
@connection.should_not_receive(:transmit).with(stomper_frame(nil, {}, 'NACK'))
|
164
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'ABORT'))
|
165
|
+
@connection.should_not_receive(:transmit).with(stomper_frame(nil, {}, 'COMMIT'))
|
166
|
+
lambda { @scope.apply_to(scope_block) }.should raise_error(::Stomper::Errors::TransactionFinalizedError)
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should raise an error if further transactionable frames are sent after the transaction has been committed" do
|
170
|
+
scope_block = lambda do |t|
|
171
|
+
t.send('/queue/test', 'body of message')
|
172
|
+
t.ack('msg-1234', 'sub-4321')
|
173
|
+
t.commit
|
174
|
+
t.nack('msg-5678', 'sub-8765')
|
175
|
+
end
|
176
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'SEND'))
|
177
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'ACK'))
|
178
|
+
@connection.should_not_receive(:transmit).with(stomper_frame(nil, {}, 'NACK'))
|
179
|
+
@connection.should_receive(:transmit).with(stomper_frame_with_headers(@headers, 'COMMIT'))
|
180
|
+
lambda { @scope.apply_to(scope_block) }.should raise_error(::Stomper::Errors::TransactionFinalizedError)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|