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.
Files changed (152) hide show
  1. data/.gitignore +5 -0
  2. data/{spec/spec.opts → .rspec} +0 -2
  3. data/Gemfile +4 -0
  4. data/LICENSE +201 -201
  5. data/README.md +130 -0
  6. data/Rakefile +5 -0
  7. data/examples/basic.rb +38 -0
  8. data/examples/events.rb +54 -0
  9. data/features/acking_messages.feature +147 -0
  10. data/features/disconnecting.feature +12 -0
  11. data/features/establish_connection.feature +44 -0
  12. data/features/protocol_version_negotiation.feature +61 -0
  13. data/features/receipts.feature +72 -0
  14. data/features/scopes.feature +32 -0
  15. data/features/secure_connections.feature +38 -0
  16. data/features/send_and_message.feature +28 -0
  17. data/features/steps/acking_messages_steps.rb +39 -0
  18. data/features/steps/disconnecting_steps.rb +8 -0
  19. data/features/steps/establish_connection_steps.rb +74 -0
  20. data/features/steps/frame_transmission_steps.rb +35 -0
  21. data/features/steps/protocol_version_negotiation_steps.rb +15 -0
  22. data/features/steps/receipts_steps.rb +79 -0
  23. data/features/steps/scopes_steps.rb +52 -0
  24. data/features/steps/secure_connections_steps.rb +41 -0
  25. data/features/steps/send_and_message_steps.rb +35 -0
  26. data/features/steps/subscribing_steps.rb +36 -0
  27. data/features/steps/threaded_receiver_steps.rb +8 -0
  28. data/features/steps/transactions_steps.rb +0 -0
  29. data/features/subscribing.feature +151 -0
  30. data/features/support/env.rb +11 -0
  31. data/features/support/header_helpers.rb +12 -0
  32. data/features/support/ssl/README +6 -0
  33. data/features/support/ssl/broker_cert.csr +17 -0
  34. data/features/support/ssl/broker_cert.pem +72 -0
  35. data/features/support/ssl/broker_key.pem +27 -0
  36. data/features/support/ssl/client_cert.csr +17 -0
  37. data/features/support/ssl/client_cert.pem +72 -0
  38. data/features/support/ssl/client_key.pem +27 -0
  39. data/features/support/ssl/demoCA/cacert.pem +17 -0
  40. data/features/support/ssl/demoCA/index.txt +2 -0
  41. data/features/support/ssl/demoCA/index.txt.attr +1 -0
  42. data/features/support/ssl/demoCA/index.txt.attr.old +1 -0
  43. data/features/support/ssl/demoCA/index.txt.old +1 -0
  44. data/features/support/ssl/demoCA/newcerts/01.pem +72 -0
  45. data/features/support/ssl/demoCA/newcerts/02.pem +72 -0
  46. data/features/support/ssl/demoCA/private/cakey.pem +17 -0
  47. data/features/support/ssl/demoCA/serial +1 -0
  48. data/features/support/ssl/demoCA/serial.old +1 -0
  49. data/features/support/test_stomp_server.rb +150 -0
  50. data/features/threaded_receiver.feature +11 -0
  51. data/features/transactions.feature +66 -0
  52. data/lib/stomper.rb +30 -20
  53. data/lib/stomper/connection.rb +442 -102
  54. data/lib/stomper/errors.rb +59 -0
  55. data/lib/stomper/extensions.rb +10 -0
  56. data/lib/stomper/extensions/common.rb +258 -0
  57. data/lib/stomper/extensions/events.rb +213 -0
  58. data/lib/stomper/extensions/heartbeat.rb +101 -0
  59. data/lib/stomper/extensions/scoping.rb +56 -0
  60. data/lib/stomper/frame.rb +54 -0
  61. data/lib/stomper/frame_serializer.rb +217 -0
  62. data/lib/stomper/headers.rb +15 -0
  63. data/lib/stomper/receipt_manager.rb +36 -0
  64. data/lib/stomper/receivers.rb +7 -0
  65. data/lib/stomper/receivers/threaded.rb +71 -0
  66. data/lib/stomper/scopes.rb +9 -0
  67. data/lib/stomper/scopes/header_scope.rb +49 -0
  68. data/lib/stomper/scopes/receipt_scope.rb +44 -0
  69. data/lib/stomper/scopes/transaction_scope.rb +109 -0
  70. data/lib/stomper/sockets.rb +66 -28
  71. data/lib/stomper/subscription_manager.rb +79 -0
  72. data/lib/stomper/support.rb +68 -0
  73. data/lib/stomper/support/1.8/frame_serializer.rb +53 -0
  74. data/lib/stomper/support/1.8/headers.rb +183 -0
  75. data/lib/stomper/support/1.9/frame_serializer.rb +64 -0
  76. data/lib/stomper/support/1.9/headers.rb +172 -0
  77. data/lib/stomper/support/ruby.rb +13 -0
  78. data/lib/stomper/uris.rb +49 -0
  79. data/lib/stomper/version.rb +7 -0
  80. data/spec/spec_helper.rb +13 -9
  81. data/spec/stomper/connection_spec.rb +712 -0
  82. data/spec/stomper/extensions/common_spec.rb +187 -0
  83. data/spec/stomper/extensions/events_spec.rb +78 -0
  84. data/spec/stomper/extensions/heartbeat_spec.rb +103 -0
  85. data/spec/stomper/extensions/scoping_spec.rb +21 -0
  86. data/spec/stomper/frame_serializer_1.8_spec.rb +318 -0
  87. data/spec/stomper/frame_serializer_spec.rb +316 -0
  88. data/spec/stomper/frame_spec.rb +36 -0
  89. data/spec/stomper/headers_spec.rb +224 -0
  90. data/spec/stomper/receipt_manager_spec.rb +91 -0
  91. data/spec/stomper/receivers/threaded_spec.rb +116 -0
  92. data/spec/stomper/scopes/header_scope_spec.rb +42 -0
  93. data/spec/stomper/scopes/receipt_scope_spec.rb +51 -0
  94. data/spec/stomper/scopes/transaction_scope_spec.rb +183 -0
  95. data/spec/stomper/sockets_spec.rb +113 -0
  96. data/spec/stomper/subscription_manager_spec.rb +107 -0
  97. data/spec/stomper/support_spec.rb +69 -0
  98. data/spec/stomper/uris_spec.rb +54 -0
  99. data/spec/stomper_spec.rb +9 -0
  100. data/spec/support/custom_argument_matchers.rb +57 -0
  101. data/spec/support/existential_frame_matchers.rb +19 -0
  102. data/spec/support/frame_header_matchers.rb +10 -0
  103. data/stomper.gemspec +30 -0
  104. metadata +272 -97
  105. data/AUTHORS +0 -21
  106. data/CHANGELOG +0 -20
  107. data/README.rdoc +0 -120
  108. data/lib/stomper/client.rb +0 -34
  109. data/lib/stomper/frame_reader.rb +0 -73
  110. data/lib/stomper/frame_writer.rb +0 -21
  111. data/lib/stomper/frames.rb +0 -39
  112. data/lib/stomper/frames/abort.rb +0 -10
  113. data/lib/stomper/frames/ack.rb +0 -25
  114. data/lib/stomper/frames/begin.rb +0 -11
  115. data/lib/stomper/frames/client_frame.rb +0 -89
  116. data/lib/stomper/frames/commit.rb +0 -10
  117. data/lib/stomper/frames/connect.rb +0 -10
  118. data/lib/stomper/frames/connected.rb +0 -30
  119. data/lib/stomper/frames/disconnect.rb +0 -10
  120. data/lib/stomper/frames/error.rb +0 -21
  121. data/lib/stomper/frames/message.rb +0 -48
  122. data/lib/stomper/frames/receipt.rb +0 -19
  123. data/lib/stomper/frames/send.rb +0 -10
  124. data/lib/stomper/frames/server_frame.rb +0 -38
  125. data/lib/stomper/frames/subscribe.rb +0 -42
  126. data/lib/stomper/frames/unsubscribe.rb +0 -19
  127. data/lib/stomper/open_uri_interface.rb +0 -41
  128. data/lib/stomper/receipt_handlers.rb +0 -23
  129. data/lib/stomper/receiptor.rb +0 -38
  130. data/lib/stomper/subscriber.rb +0 -76
  131. data/lib/stomper/subscription.rb +0 -128
  132. data/lib/stomper/subscriptions.rb +0 -95
  133. data/lib/stomper/threaded_receiver.rb +0 -59
  134. data/lib/stomper/transaction.rb +0 -185
  135. data/lib/stomper/transactor.rb +0 -50
  136. data/lib/stomper/uri.rb +0 -55
  137. data/spec/client_spec.rb +0 -29
  138. data/spec/connection_spec.rb +0 -22
  139. data/spec/frame_reader_spec.rb +0 -37
  140. data/spec/frame_writer_spec.rb +0 -27
  141. data/spec/frames/client_frame_spec.rb +0 -66
  142. data/spec/frames/indirect_frame_spec.rb +0 -45
  143. data/spec/frames/server_frame_spec.rb +0 -85
  144. data/spec/open_uri_interface_spec.rb +0 -132
  145. data/spec/receiptor_spec.rb +0 -35
  146. data/spec/shared_connection_examples.rb +0 -79
  147. data/spec/subscriber_spec.rb +0 -77
  148. data/spec/subscription_spec.rb +0 -157
  149. data/spec/subscriptions_spec.rb +0 -145
  150. data/spec/threaded_receiver_spec.rb +0 -33
  151. data/spec/transaction_spec.rb +0 -139
  152. 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