stomper 0.3.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/AUTHORS +21 -0
- data/CHANGELOG +3 -0
- data/LICENSE +202 -0
- data/README.rdoc +68 -0
- data/lib/stomper.rb +15 -0
- data/lib/stomper/client.rb +300 -0
- data/lib/stomper/connection.rb +176 -0
- data/lib/stomper/frames.rb +24 -0
- data/lib/stomper/frames/abort.rb +14 -0
- data/lib/stomper/frames/ack.rb +29 -0
- data/lib/stomper/frames/begin.rb +14 -0
- data/lib/stomper/frames/client_frame.rb +86 -0
- data/lib/stomper/frames/commit.rb +14 -0
- data/lib/stomper/frames/connect.rb +15 -0
- data/lib/stomper/frames/connected.rb +27 -0
- data/lib/stomper/frames/disconnect.rb +13 -0
- data/lib/stomper/frames/error.rb +26 -0
- data/lib/stomper/frames/headers.rb +68 -0
- data/lib/stomper/frames/message.rb +44 -0
- data/lib/stomper/frames/receipt.rb +24 -0
- data/lib/stomper/frames/send.rb +14 -0
- data/lib/stomper/frames/server_frame.rb +48 -0
- data/lib/stomper/frames/subscribe.rb +47 -0
- data/lib/stomper/frames/unsubscribe.rb +23 -0
- data/lib/stomper/subscription.rb +128 -0
- data/lib/stomper/subscriptions.rb +95 -0
- data/lib/stomper/transaction.rb +180 -0
- data/spec/client_spec.rb +167 -0
- data/spec/connection_spec.rb +12 -0
- data/spec/frames/client_frame_spec.rb +142 -0
- data/spec/frames/headers_spec.rb +54 -0
- data/spec/frames/server_frame_spec.rb +86 -0
- data/spec/shared_connection_examples.rb +84 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/subscription_spec.rb +157 -0
- data/spec/subscriptions_spec.rb +148 -0
- data/spec/transaction_spec.rb +139 -0
- metadata +121 -0
@@ -0,0 +1,180 @@
|
|
1
|
+
module Stomper
|
2
|
+
# An exception raised whenever a Transaction object has been aborted
|
3
|
+
# due to an unhandled exception generated by its supplied block, or when
|
4
|
+
# the block explicitly aborts the transaction.
|
5
|
+
#
|
6
|
+
# See also: Stomper::Transaction#perform
|
7
|
+
class TransactionAborted < RuntimeError; end
|
8
|
+
|
9
|
+
# An encapsulation of a stomp transaction. Manually managing transactions
|
10
|
+
# is possible through the use of Stomper::Client#begin, Stomper::Client#commit,
|
11
|
+
# and Stomper::Client#abort.
|
12
|
+
#
|
13
|
+
# === Example Usage
|
14
|
+
#
|
15
|
+
# When the transaction is passed to the block:
|
16
|
+
#
|
17
|
+
# client.transaction do |t|
|
18
|
+
# t.send("/queue/target", "doing some work")
|
19
|
+
#
|
20
|
+
# # do something that might raise an exception, indicating that any
|
21
|
+
# # messages and acknowledgements we have sent should be "undone"
|
22
|
+
#
|
23
|
+
# t.send("/queue/target", "completed work")
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# When the block is evaluated within the transaction:
|
27
|
+
#
|
28
|
+
# client.transaction do
|
29
|
+
# send("/queue/target", "doing some work")
|
30
|
+
#
|
31
|
+
# # ...
|
32
|
+
#
|
33
|
+
# send("/queue/target", "completed work")
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# Nesting transactions:
|
37
|
+
#
|
38
|
+
# client.transaction do |t|
|
39
|
+
# t.transaction do |nt|
|
40
|
+
# nt.send("/queue/target", ...)
|
41
|
+
#
|
42
|
+
# nt.transaction do |nnt|
|
43
|
+
# nnt.send("/queue/target", ...)
|
44
|
+
#
|
45
|
+
# # do something with potentially exceptional results
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# nt.send("/queue/target", ...)
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# t.send("/queue/target", ...)
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# See also: Stomper::Client#transaction
|
55
|
+
#
|
56
|
+
class Transaction
|
57
|
+
# The id of this transaction, used to reference the transaction with the stomp broker.
|
58
|
+
attr_reader :id
|
59
|
+
|
60
|
+
# Creates a new Transaction instance. The +client+ parameter
|
61
|
+
# is an instance of Stomper::Client and is required so that the Transaction
|
62
|
+
# instance has somewhere to forward +begin+, +ack+ and +abort+ methods
|
63
|
+
# to. If the +trans_id+ parameter is not specified, an id is automatically
|
64
|
+
# generated of the form "tx-{Time.now.to_f}". This name can be accessed
|
65
|
+
# through the +id+ attribute and is used in naming the transaction to
|
66
|
+
# the stomp broker. If +block+ is given, the Transaction instance immediately
|
67
|
+
# calls its perform method with the supplied +block+.
|
68
|
+
def initialize(client, trans_id=nil, &block)
|
69
|
+
@client = client
|
70
|
+
@id = trans_id || "tx-#{Time.now.to_f}"
|
71
|
+
@committed = false
|
72
|
+
@aborted = false
|
73
|
+
perform(&block) if block_given?
|
74
|
+
end
|
75
|
+
|
76
|
+
# Invokes the given +block+. If the +block+ executes normally, the
|
77
|
+
# transaction is committed, otherwise it is aborted.
|
78
|
+
# If +block+ accepts a parameter, this method yields itself to the block,
|
79
|
+
# otherwise, +block+ is evaluated within the context of this instance through
|
80
|
+
# +instance_eval+.
|
81
|
+
#
|
82
|
+
# If a call to +abort+ is issued within the block, the transaction is aborted
|
83
|
+
# as demanded, and no attempt is made to commit it; however, no code after the
|
84
|
+
# call to +abort+ will be evaluated, as +abort+ raises a TransactionAborted
|
85
|
+
# exception.
|
86
|
+
#
|
87
|
+
# If a call to +commit+ is issued within the block, the transaction is committed
|
88
|
+
# as demanded, and no attempt is made to commit it after +block+ has finished
|
89
|
+
# executing. As +commit+ does not raise an excpetion, all code after the call
|
90
|
+
# to commit will be evaluated.
|
91
|
+
#
|
92
|
+
# If you are using Transaction objects directly, and not relying on their
|
93
|
+
# generation through Stomper::Client#transaction, be warned that this method
|
94
|
+
# will raise a TransactionAborted exception if the +block+ evaluation fails.
|
95
|
+
# This behavior allows for nesting transactions and ensuring that if a nested
|
96
|
+
# transaction fails, so do all of its ancestors.
|
97
|
+
def perform(&block) #:yields: transaction
|
98
|
+
begin
|
99
|
+
@client.begin(@id)
|
100
|
+
if block.arity == 1
|
101
|
+
yield self
|
102
|
+
else
|
103
|
+
instance_eval(&block)
|
104
|
+
end
|
105
|
+
commit
|
106
|
+
rescue => err
|
107
|
+
_abort
|
108
|
+
raise TransactionAborted, "aborted transaction '#{@id}' originator: #{err.to_s}"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Returns true if the Transaction object has already been committed, false
|
113
|
+
# otherwise.
|
114
|
+
def committed?
|
115
|
+
@committed
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns true if the Transaction object has already been aborted, false
|
119
|
+
# otherwise.
|
120
|
+
def aborted?
|
121
|
+
@aborted
|
122
|
+
end
|
123
|
+
|
124
|
+
# Similar to Stomper::Client#transaction, this method creates a new
|
125
|
+
# Transaction object, nested inside of this one. To prevent name
|
126
|
+
# collisions, this method automatically generates a transaction id,
|
127
|
+
# if one is not specified, of the form "#{parent_transaction_id}-#{Time.now.to_f}.
|
128
|
+
def transaction(transaction_id=nil,&block)
|
129
|
+
# To get a transaction name guaranteed to not collide with this one
|
130
|
+
# we will supply an explicit id to the constructor unless an id was
|
131
|
+
# provided
|
132
|
+
transaction_id ||= "#{@id}-#{Time.now.to_f}"
|
133
|
+
self.class.new(@client, transaction_id, &block)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Wraps the Stomper::Client#send method, injecting a "transaction" header
|
137
|
+
# into the +headers+ hash, thus informing the stomp broker that the message
|
138
|
+
# generated here is part of this transaction.
|
139
|
+
def send(destination, body, headers={})
|
140
|
+
headers['transaction'] = @id
|
141
|
+
@client.send(destination, body, headers)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Wraps the Stomper::Client#ack method, injecting a "transaction" header
|
145
|
+
# into the +headers+ hash, thus informing the stomp broker that the message
|
146
|
+
# acknowledgement is part of this transaction.
|
147
|
+
def ack(message_or_id, headers={})
|
148
|
+
headers['transaction'] = @id
|
149
|
+
@client.ack(message_or_id, headers)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Aborts this transaction if it has not already been committed or aborted.
|
153
|
+
# Note that it does so by raising a TransactionAborted exception, allowing
|
154
|
+
# the +abort+ call to force any ancestral transactions to also fail.
|
155
|
+
#
|
156
|
+
# See also: commit, committed?, aborted?
|
157
|
+
def abort
|
158
|
+
raise TransactionAborted, "transaction '#{@id}' aborted explicitly" if _abort
|
159
|
+
end
|
160
|
+
|
161
|
+
# Commits this transaction unless it has already been committed or aborted.
|
162
|
+
#
|
163
|
+
# See also: abort, committed?, aborted?
|
164
|
+
def commit
|
165
|
+
# Guard against sending multiple commit messages to the server for a
|
166
|
+
# single transaction.
|
167
|
+
@client.commit(@id) unless committed? || aborted?
|
168
|
+
@committed = true
|
169
|
+
end
|
170
|
+
|
171
|
+
private
|
172
|
+
def _abort
|
173
|
+
# Guard against sending multiple abort messages to the server for a
|
174
|
+
# single transaction.
|
175
|
+
return false if committed? || aborted?
|
176
|
+
@client.abort(@id)
|
177
|
+
@aborted = true
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
2
|
+
|
3
|
+
module Stomper
|
4
|
+
describe Client do
|
5
|
+
before(:each) do
|
6
|
+
# For the client, we want to mock the underlying connection
|
7
|
+
@mock_connection = mock("connection")
|
8
|
+
@mock_connection.should_receive(:disconnect).with(no_args()).at_most(:once).and_return(nil)
|
9
|
+
Stomper::Connection.stub!(:new).and_return(@mock_connection)
|
10
|
+
@client = Client.new("stomp:///")
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "expected interface" do
|
14
|
+
it "should provide a send method" do
|
15
|
+
@client.should respond_to(:send)
|
16
|
+
@mock_connection.should_receive(:transmit).with(an_instance_of(Stomper::Frames::Send)).twice.and_return(nil)
|
17
|
+
@client.send("/queue/to", "message body", {:additional => 'header'})
|
18
|
+
@client.send("/queue/to", "message body")
|
19
|
+
end
|
20
|
+
it "should provide a subscribe method" do
|
21
|
+
@client.should respond_to(:subscribe)
|
22
|
+
@mock_connection.should_receive(:transmit).with(an_instance_of(Stomper::Frames::Subscribe)).twice.and_return(nil)
|
23
|
+
@client.subscribe("/queue/to", {:additional => 'header'})
|
24
|
+
@client.subscribe("/queue/to")
|
25
|
+
end
|
26
|
+
it "should provide an unsubscribe method" do
|
27
|
+
@client.should respond_to(:unsubscribe)
|
28
|
+
@mock_connection.should_receive(:transmit).with(an_instance_of(Stomper::Frames::Subscribe)).twice.and_return(nil)
|
29
|
+
@client.subscribe("/queue/to", {:id => 'subscription-id'})
|
30
|
+
@client.subscribe("/queue/to")
|
31
|
+
@mock_connection.should_receive(:transmit).with(an_instance_of(Stomper::Frames::Unsubscribe)).twice.and_return(nil)
|
32
|
+
@client.unsubscribe("/queue/to", 'subscription-id')
|
33
|
+
@client.unsubscribe("/queue/to")
|
34
|
+
end
|
35
|
+
it "should provide an ack method" do
|
36
|
+
@client.should respond_to(:ack)
|
37
|
+
@mock_connection.should_receive(:transmit).with(an_instance_of(Stomper::Frames::Ack)).exactly(3).times.and_return(nil)
|
38
|
+
@client.ack("message-id", {:additional => "header"})
|
39
|
+
@client.ack("message-id")
|
40
|
+
@client.ack(Stomper::Frames::Message.new({:'message-id' => 'msg-001'}, "body"))
|
41
|
+
end
|
42
|
+
it "should provide a begin method" do
|
43
|
+
@client.should respond_to(:begin)
|
44
|
+
@mock_connection.should_receive(:transmit).with(an_instance_of(Stomper::Frames::Begin)).once.and_return(nil)
|
45
|
+
@client.begin("tx-001")
|
46
|
+
end
|
47
|
+
it "should proivde an abort method" do
|
48
|
+
@client.should respond_to(:abort)
|
49
|
+
@mock_connection.should_receive(:transmit).with(an_instance_of(Stomper::Frames::Abort)).once.and_return(nil)
|
50
|
+
@client.abort("tx-001")
|
51
|
+
end
|
52
|
+
it "should provide a commit method" do
|
53
|
+
@client.should respond_to(:commit)
|
54
|
+
@mock_connection.should_receive(:transmit).with(an_instance_of(Stomper::Frames::Commit)).once.and_return(nil)
|
55
|
+
@client.commit("tx-001")
|
56
|
+
end
|
57
|
+
it "should provide a recieve method" do
|
58
|
+
@client.should respond_to(:receive)
|
59
|
+
end
|
60
|
+
it "should provide a disconnect method" do
|
61
|
+
@client.should respond_to(:disconnect)
|
62
|
+
end
|
63
|
+
it "should provide a close method" do
|
64
|
+
@client.should respond_to(:close)
|
65
|
+
end
|
66
|
+
it "should provide a connectivity test" do
|
67
|
+
@client.should respond_to(:connected?)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "threaded receiver" do
|
72
|
+
it "should respond to start and stop" do
|
73
|
+
@client.should respond_to(:start)
|
74
|
+
@client.should respond_to(:stop)
|
75
|
+
@client.should respond_to(:receiving?)
|
76
|
+
end
|
77
|
+
it "should only be receiving when it is started" do
|
78
|
+
@mock_connection.stub!(:receive).and_return(nil)
|
79
|
+
@mock_connection.should_receive(:connected?).any_number_of_times.and_return(true)
|
80
|
+
@client.receiving?.should be_false
|
81
|
+
@client.start
|
82
|
+
@client.receiving?.should be_true
|
83
|
+
@client.stop
|
84
|
+
@client.receiving?.should be_false
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe "subscribing to queue" do
|
89
|
+
before(:each) do
|
90
|
+
@message_sent = Stomper::Frames::Message.new({'destination' => "/queue/test"}, "test message")
|
91
|
+
@mock_connection.should_receive(:connected?).any_number_of_times.and_return(true)
|
92
|
+
@mock_connection.should_receive(:transmit).with(duck_type(:to_stomp)).at_least(:once).and_return(nil)
|
93
|
+
@mock_connection.should_receive(:receive).any_number_of_times.and_return(@message_sent)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should subscribe to a destination with a block" do
|
97
|
+
wait_for_message = true
|
98
|
+
@message_received = nil
|
99
|
+
@client.start
|
100
|
+
@client.subscribe("/queue/test") do |msg|
|
101
|
+
@message_received = msg
|
102
|
+
wait_for_message = false
|
103
|
+
end
|
104
|
+
true while wait_for_message
|
105
|
+
@client.stop
|
106
|
+
@message_received.should == @message_sent
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should not unsubscribe from all destinations when a subscription id is provided" do
|
110
|
+
@client.subscribe("/queue/test", { 'id' => 'subscription-1' }) do |msg|
|
111
|
+
@message_received = msg
|
112
|
+
end
|
113
|
+
@client.subscribe("/queue/test") do |msg|
|
114
|
+
@message_received = msg
|
115
|
+
end
|
116
|
+
@client.subscribe("/queue/test", :id => 'subscription-2') do |msg|
|
117
|
+
@message_received = msg
|
118
|
+
end
|
119
|
+
@client.unsubscribe("/queue/test", 'subscription-1')
|
120
|
+
@client.subscriptions.size.should == 2
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should not unsubscribe from non-naive subscriptions when only a destination is supplied" do
|
124
|
+
@client.subscribe("/queue/test", { 'id' => 'subscription-1' }) do |msg|
|
125
|
+
@message_received = msg
|
126
|
+
end
|
127
|
+
@client.subscribe("/queue/test") do |msg|
|
128
|
+
@message_received = msg
|
129
|
+
end
|
130
|
+
@client.subscribe("/queue/test") do |msg|
|
131
|
+
@message_received = msg
|
132
|
+
end
|
133
|
+
@client.unsubscribe("/queue/test")
|
134
|
+
@client.subscriptions.size.should == 1
|
135
|
+
end
|
136
|
+
|
137
|
+
# Due to the receiver running in a separate thread, this may not be correct?
|
138
|
+
it "should unsubscribe from a destination and receive no more messages" do
|
139
|
+
@mutex = Mutex.new
|
140
|
+
@last_message_received = nil
|
141
|
+
@client.start
|
142
|
+
@client.subscribe("/queue/test") do |msg|
|
143
|
+
@last_message_received = Time.now
|
144
|
+
end
|
145
|
+
true until @last_message_received
|
146
|
+
@client.unsubscribe("/queue/test")
|
147
|
+
@unsubscribed_at = Time.now
|
148
|
+
@client.stop
|
149
|
+
(@last_message_received < @unsubscribed_at).should be_true
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe "transactions" do
|
154
|
+
before(:each) do
|
155
|
+
@mock_connection.should_receive(:transmit).with(duck_type(:to_stomp)).at_least(:once).and_return(nil)
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should provide a transaction method that generates a new Transaction" do
|
159
|
+
@evaluated = false
|
160
|
+
@client.transaction do |t|
|
161
|
+
@evaluated = true
|
162
|
+
end
|
163
|
+
@evaluated.should be_true
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'shared_connection_examples'))
|
3
|
+
|
4
|
+
module Stomper
|
5
|
+
describe Connection do
|
6
|
+
before(:each) do
|
7
|
+
@connection = Connection.new("stomp:///")
|
8
|
+
end
|
9
|
+
|
10
|
+
it_should_behave_like "All Client Connections"
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
module Stomper::Frames
|
4
|
+
describe ClientFrame do
|
5
|
+
before(:each) do
|
6
|
+
ClientFrame.generate_content_length = true
|
7
|
+
@client_frame = ClientFrame.new('COMMAND')
|
8
|
+
end
|
9
|
+
|
10
|
+
def str_size(str)
|
11
|
+
str.respond_to?(:bytesize) ? str.bytesize : str.size
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should be provide a headers as an instance of Headers" do
|
15
|
+
@client_frame.headers.should be_an_instance_of(Stomper::Frames::Headers)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should be convertable into a stomp frame" do
|
19
|
+
@client_frame.to_stomp.should == "COMMAND\n\n\0"
|
20
|
+
@client_frame.headers.destination = "/queue/test/1"
|
21
|
+
@client_frame.headers['transaction-id'] = '2'
|
22
|
+
@client_frame.headers[:ack] = 'client'
|
23
|
+
@client_frame.to_stomp.should == "COMMAND\nack:client\ndestination:/queue/test/1\ntransaction-id:2\n\n\0"
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "generating content-length header" do
|
27
|
+
it "should provide the header by default, overriding any existing header" do
|
28
|
+
@frame_body = 'testing'
|
29
|
+
@client_frame = ClientFrame.new('COMMAND', {'content-length' => 1}, @frame_body)
|
30
|
+
@client_frame.to_stomp.should == "COMMAND\ncontent-length:#{str_size(@frame_body)}\n\n#{@frame_body}\0"
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should not provide the header if the class option is set to false, unless explicitly set on the frame in particular" do
|
34
|
+
ClientFrame.generate_content_length = false
|
35
|
+
@frame_body = 'testing'
|
36
|
+
@client_frame = ClientFrame.new('COMMAND', {}, @frame_body)
|
37
|
+
@client_frame.to_stomp.should == "COMMAND\n\n#{@frame_body}\0"
|
38
|
+
@client_frame = ClientFrame.new('COMMAND', {}, @frame_body)
|
39
|
+
@client_frame.generate_content_length = true
|
40
|
+
@client_frame.to_stomp.should == "COMMAND\ncontent-length:#{str_size(@frame_body)}\n\n#{@frame_body}\0"
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should not provide the header if instance option is set false, when the class option is true" do
|
44
|
+
@frame_body = 'testing'
|
45
|
+
@client_frame = ClientFrame.new('COMMAND', {}, @frame_body)
|
46
|
+
@client_frame.generate_content_length = false
|
47
|
+
@client_frame.to_stomp.should == "COMMAND\n\n#{@frame_body}\0"
|
48
|
+
@client_frame = ClientFrame.new('COMMAND', {:generate_content_length => false}, @frame_body)
|
49
|
+
@client_frame.to_stomp.should == "COMMAND\n\n#{@frame_body}\0"
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should not overwrite an explicit content-length header when option is off at class or instance level" do
|
53
|
+
@frame_body = 'testing'
|
54
|
+
@client_frame = ClientFrame.new('COMMAND', { 'content-length' => 4}, @frame_body)
|
55
|
+
@client_frame.generate_content_length = false
|
56
|
+
@client_frame.to_stomp.should == "COMMAND\ncontent-length:4\n\n#{@frame_body}\0"
|
57
|
+
ClientFrame.generate_content_length = false
|
58
|
+
@client_frame = ClientFrame.new('COMMAND', {'content-length' => 2}, @frame_body)
|
59
|
+
@client_frame.to_stomp.should == "COMMAND\ncontent-length:2\n\n#{@frame_body}\0"
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should the class option should be scoped to the class it is set on" do
|
63
|
+
@frame_body = 'testing'
|
64
|
+
Send.generate_content_length = false
|
65
|
+
@send_frame = Send.new('/queue/test/1', @frame_body)
|
66
|
+
@client_frame = ClientFrame.new('COMMAND', {}, @frame_body)
|
67
|
+
@client_frame.to_stomp.should == "COMMAND\ncontent-length:#{str_size(@frame_body)}\n\n#{@frame_body}\0"
|
68
|
+
@send_frame.to_stomp.should == "SEND\ndestination:#{@send_frame.headers.destination}\n\n#{@frame_body}\0"
|
69
|
+
Send.generate_content_length = true
|
70
|
+
ClientFrame.generate_content_length = false
|
71
|
+
@send_frame = Send.new('/queue/test/1', @frame_body)
|
72
|
+
@client_frame = ClientFrame.new('COMMAND', {}, @frame_body)
|
73
|
+
@client_frame.to_stomp.should == "COMMAND\n\n#{@frame_body}\0"
|
74
|
+
@send_frame.to_stomp.should == "SEND\ncontent-length:#{str_size(@frame_body)}\ndestination:#{@send_frame.headers.destination}\n\n#{@frame_body}\0"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
describe "client frames" do
|
78
|
+
describe Abort do
|
79
|
+
it "should produce a proper stomp message" do
|
80
|
+
@abort = Abort.new("transaction-test", { :a_header => 'test'})
|
81
|
+
@abort.to_stomp.should == "ABORT\na_header:test\ntransaction:transaction-test\n\n\0"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
describe Ack do
|
85
|
+
it "should produce a proper stomp message" do
|
86
|
+
@ack = Ack.new("message-test", { :a_header => 'test'})
|
87
|
+
@ack.to_stomp.should == "ACK\na_header:test\nmessage-id:message-test\n\n\0"
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should provide an Ack for a given message frame" do
|
91
|
+
@ack = Ack.ack_for(Message.new({'message-id' => 'test'}, "a body"))
|
92
|
+
@ack.to_stomp.should == "ACK\nmessage-id:test\n\n\0"
|
93
|
+
@ack = Ack.ack_for(Message.new({'message-id' => 'test', 'transaction' => 'tx-test'}, "a body"))
|
94
|
+
@ack.to_stomp.should == "ACK\nmessage-id:test\ntransaction:tx-test\n\n\0"
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
describe Begin do
|
99
|
+
it "should produce a proper stomp message" do
|
100
|
+
@begin = Begin.new("transaction-test", { :a_header => 'test'})
|
101
|
+
@begin.to_stomp.should == "BEGIN\na_header:test\ntransaction:transaction-test\n\n\0"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
describe Commit do
|
105
|
+
it "should produce a proper stomp message" do
|
106
|
+
@commit = Commit.new("transaction-test", { :a_header => 'test'})
|
107
|
+
@commit.to_stomp.should == "COMMIT\na_header:test\ntransaction:transaction-test\n\n\0"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
describe Connect do
|
111
|
+
it "should produce a proper stomp message" do
|
112
|
+
@connect = Connect.new('uzer','s3cr3t', { :a_header => 'test' })
|
113
|
+
@connect.to_stomp.should == "CONNECT\na_header:test\nlogin:uzer\npasscode:s3cr3t\n\n\0"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
describe Disconnect do
|
117
|
+
it "should produce a proper stomp message" do
|
118
|
+
@disconnect = Disconnect.new({ :a_header => 'test'})
|
119
|
+
@disconnect.to_stomp.should == "DISCONNECT\na_header:test\n\n\0"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
describe Send do
|
123
|
+
it "should produce a proper stomp message" do
|
124
|
+
@send = Send.new("/queue/a/target", "a body", { :a_header => 'test'})
|
125
|
+
@send.to_stomp.should == "SEND\na_header:test\ncontent-length:6\ndestination:/queue/a/target\n\na body\0"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
describe Subscribe do
|
129
|
+
it "should produce a proper stomp message" do
|
130
|
+
@subscribe = Subscribe.new("/topic/some/target", { :a_header => 'test'})
|
131
|
+
@subscribe.to_stomp.should == "SUBSCRIBE\na_header:test\nack:auto\ndestination:/topic/some/target\n\n\0"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
describe Unsubscribe do
|
135
|
+
it "should produce a proper stomp message" do
|
136
|
+
@unsubscribe = Unsubscribe.new("/topic/target.name.path", { :a_header => 'test'})
|
137
|
+
@unsubscribe.to_stomp.should == "UNSUBSCRIBE\na_header:test\ndestination:/topic/target.name.path\n\n\0"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|