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,187 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module Stomper::Extensions
|
5
|
+
describe Common do
|
6
|
+
before(:each) do
|
7
|
+
@common = mock("common", :version => '1.0')
|
8
|
+
@common.extend Common
|
9
|
+
@subscription_manager = mock('subscription manager')
|
10
|
+
@common.stub!(:subscription_manager).and_return(@subscription_manager)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "Shared default interface" do
|
14
|
+
before(:each) do
|
15
|
+
@common.stub!(:is_a?).with(::Stomper::Connection).and_return(true)
|
16
|
+
end
|
17
|
+
it "should transmit an ABORT frame" do
|
18
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'transaction' => 'tx-1234'}, 'ABORT'))
|
19
|
+
@common.abort('tx-1234')
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should transmit a BEGIN frame" do
|
23
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'transaction' => 'tx-3124'}, 'BEGIN'))
|
24
|
+
@common.begin('tx-3124')
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should transmit a COMMIT frame" do
|
28
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'transaction' => 'tx-4321'}, 'COMMIT'))
|
29
|
+
@common.commit('tx-4321')
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should transmit a SEND frame with :send" do
|
33
|
+
@common.should_receive(:transmit).with(stomper_frame('testing :send', {'destination' => '/queue/test_send'}, 'SEND'))
|
34
|
+
@common.send('/queue/test_send', 'testing :send')
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should transmit a SEND frame with :puts" do
|
38
|
+
@common.should_receive(:transmit).with(stomper_frame('testing :puts', {'destination' => '/queue/test_puts'}, 'SEND'))
|
39
|
+
@common.puts('/queue/test_puts', 'testing :puts')
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should transmit a SUBSCRIBE frame" do
|
43
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'destination' => '/queue/test_subscribe'}, 'SUBSCRIBE'))
|
44
|
+
@common.subscribe('/queue/test_subscribe')
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should transmit an UNSUBSCRIBE frame for a given subscription ID" do
|
48
|
+
@subscription_manager.should_receive(:subscribed_id?).with('subscription-1234').and_return(true)
|
49
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'id' => 'subscription-1234'}, 'UNSUBSCRIBE'))
|
50
|
+
@common.unsubscribe('subscription-1234')
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should transmit an UNSUBSCRIBE frame for a given SUBSCRIBE frame" do
|
54
|
+
@subscription_manager.should_receive(:subscribed_id?).with('id-in-frame-4321').and_return(true)
|
55
|
+
subscribe = ::Stomper::Frame.new('SUBSCRIBE', { :id => 'id-in-frame-4321' })
|
56
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'id' => 'id-in-frame-4321'}, 'UNSUBSCRIBE'))
|
57
|
+
@common.unsubscribe(subscribe)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should raise an error on :nack" do
|
61
|
+
lambda { @common.nack("msg-001", "sub-001") }.should raise_error
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should transmit an ACK for a message-id given MESSAGE frame" do
|
65
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'message-id' => 'msg-001'}, 'ACK'))
|
66
|
+
@common.ack(Stomper::Frame.new('MESSAGE', { :'message-id' => 'msg-001' }, 'some body'))
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should transmit an ACK for a message-id given a message-id" do
|
70
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'message-id' => 'msg-123'}, 'ACK'))
|
71
|
+
@common.ack('msg-123')
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should raise an error when the message-id cannot be inferred" do
|
75
|
+
lambda { @common.ack('') }.should raise_error(ArgumentError)
|
76
|
+
lambda { @common.ack(Stomper::Frame.new('MESSAGE', { :id => '' }, 'some body')) }.should raise_error(ArgumentError)
|
77
|
+
lambda { @common.ack(nil) }.should raise_error(ArgumentError)
|
78
|
+
lambda { @common.ack(Stomper::Frame.new('MESSAGE', {}, 'some body')) }.should raise_error(ArgumentError)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "subscription handling" do
|
83
|
+
it "should add a callback to the subscription manager on :subscribe" do
|
84
|
+
# In this tests, we do not care about the particulars of the generated
|
85
|
+
# frame. Our only interest is the interaction with the subscription
|
86
|
+
# manager
|
87
|
+
@common.stub!(:transmit).and_return { |f| f }
|
88
|
+
@subscription_manager.should_receive(:add).with(stomper_frame_with_headers({}, 'SUBSCRIBE'), an_instance_of(Proc))
|
89
|
+
@common.subscribe('/queue/test') { |m| true }
|
90
|
+
end
|
91
|
+
it "should unsubscribe by destination" do
|
92
|
+
# As this invocation of unsubscribe will result in multiple UNSUBSCRIBE
|
93
|
+
# frames being generated, we do care about the actual frames generated
|
94
|
+
# as well.
|
95
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({:id => '1234'}, 'UNSUBSCRIBE'))
|
96
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({:id => '4567'}, 'UNSUBSCRIBE'))
|
97
|
+
@subscription_manager.should_receive(:subscribed_id?).with('/queue/test').and_return(false)
|
98
|
+
@subscription_manager.should_receive(:subscribed_destination?).with('/queue/test').and_return(true)
|
99
|
+
@subscription_manager.should_receive(:ids_for_destination).with('/queue/test').and_return(['1234', '4567'])
|
100
|
+
@common.unsubscribe("/queue/test")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "SEND receipt handling" do
|
105
|
+
it "should build a receipt scope when a block is passed to :send" do
|
106
|
+
receipt_scope = mock('receipt scope')
|
107
|
+
@common.should_receive(:with_receipt).and_return(receipt_scope)
|
108
|
+
receipt_scope.should_receive(:transmit).with(stomper_frame('my message', { :destination => '/topic/testing' }, 'SEND'))
|
109
|
+
@common.send('/topic/testing', 'my message') { |r| true }
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should build a receipt scope when a block is passed to :puts" do
|
113
|
+
receipt_scope = mock('receipt scope')
|
114
|
+
@common.should_receive(:with_receipt).and_return(receipt_scope)
|
115
|
+
receipt_scope.should_receive(:transmit).with(stomper_frame('my message', { :destination => '/topic/testing' }, 'SEND'))
|
116
|
+
@common.puts('/topic/testing', 'my message') { |r| true }
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "1.1 Protocol Extensions" do
|
121
|
+
before(:each) do
|
122
|
+
@common.stub!(:version).and_return('1.1')
|
123
|
+
Common.extend_by_protocol_version(@common, '1.1')
|
124
|
+
end
|
125
|
+
it "should include V1_1 module" do
|
126
|
+
@common.should be_a_kind_of(Common::V1_1)
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should transmit a NACK for a message-id and subscription given MESSAGE frame" do
|
130
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'message-id' => 'msg-456', 'subscription' => 'sub-123'}, 'NACK'))
|
131
|
+
@common.nack(Stomper::Frame.new('MESSAGE', { :'message-id' => 'msg-456', :subscription => 'sub-123' }, 'some body'))
|
132
|
+
end
|
133
|
+
it "should transmit a NACK for a message-id and subscription given MESSAGE frame w/o subscription and subscription-id" do
|
134
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'message-id' => 'msg-456', 'subscription' => 'sub-123'}, 'NACK'))
|
135
|
+
@common.nack(Stomper::Frame.new('MESSAGE', { :'message-id' => 'msg-456', :subscription => '' }, 'some body'), 'sub-123')
|
136
|
+
end
|
137
|
+
it "should transmit a NACK for a message-id and subscription given message-id and subscription-id" do
|
138
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'message-id' => 'msg-456', 'subscription' => 'sub-123'}, 'NACK'))
|
139
|
+
@common.nack('msg-456', 'sub-123')
|
140
|
+
end
|
141
|
+
it "should raise an error when the subscription ID cannot be inferred" do
|
142
|
+
lambda { @common.nack('msg-456') }.should raise_error(ArgumentError)
|
143
|
+
lambda { @common.nack(Stomper::Frame.new('MESSAGE', { :'message-id' => 'msg-456' }, 'some body')) }.should raise_error(ArgumentError)
|
144
|
+
lambda { @common.nack('msg-456', '') }.should raise_error(ArgumentError)
|
145
|
+
lambda { @common.nack(Stomper::Frame.new('MESSAGE', { :'message-id' => 'msg-456' }, 'some body'), '') }.should raise_error(ArgumentError)
|
146
|
+
lambda { @common.nack('msg-456', { :subscription => ''}) }.should raise_error(ArgumentError)
|
147
|
+
lambda { @common.nack(Stomper::Frame.new('MESSAGE', { :'message-id' => 'msg-456' }, 'some body'), '', { :subscription => nil }) }.should raise_error(ArgumentError)
|
148
|
+
lambda { @common.nack('msg-456', { :subscription => 'sub-123'}) }.should raise_error(ArgumentError)
|
149
|
+
end
|
150
|
+
it "should raise an error when the message-id cannot be inferred" do
|
151
|
+
lambda { @common.nack('', 'sub-123') }.should raise_error(ArgumentError)
|
152
|
+
lambda { @common.nack(Stomper::Frame.new('MESSAGE', { :'message-id' => '' }, 'some body'), 'sub-123') }.should raise_error(ArgumentError)
|
153
|
+
lambda { @common.nack(nil, 'sub-123') }.should raise_error(ArgumentError)
|
154
|
+
lambda { @common.nack(Stomper::Frame.new('MESSAGE', {}, 'some body'), 'sub-123') }.should raise_error(ArgumentError)
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should transmit an ACK for a message-id and subscription given MESSAGE frame" do
|
158
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'message-id' => 'msg-456', 'subscription' => 'sub-123'}, 'ACK'))
|
159
|
+
@common.ack(Stomper::Frame.new('MESSAGE', { :'message-id' => 'msg-456', :subscription => 'sub-123' }, 'some body'))
|
160
|
+
end
|
161
|
+
it "should transmit an ACK for a message-id and subscription given MESSAGE frame w/o subscription and subscription-id" do
|
162
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'message-id' => 'msg-456', 'subscription' => 'sub-123'}, 'ACK'))
|
163
|
+
@common.ack(Stomper::Frame.new('MESSAGE', { :'message-id' => 'msg-456', :subscription => '' }, 'some body'), 'sub-123')
|
164
|
+
end
|
165
|
+
it "should transmit an ACK for a message-id and subscription given message-id and subscription-id" do
|
166
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'message-id' => 'msg-456', 'subscription' => 'sub-123'}, 'ACK'))
|
167
|
+
@common.ack('msg-456', 'sub-123')
|
168
|
+
end
|
169
|
+
it "should raise an error when the subscription ID cannot be inferred" do
|
170
|
+
lambda { @common.ack('msg-456') }.should raise_error(ArgumentError)
|
171
|
+
lambda { @common.ack(Stomper::Frame.new('MESSAGE', { :'message-id' => 'msg-456' }, 'some body')) }.should raise_error(ArgumentError)
|
172
|
+
lambda { @common.ack('msg-456', '') }.should raise_error(ArgumentError)
|
173
|
+
lambda { @common.ack(Stomper::Frame.new('MESSAGE', { :'message-id' => 'msg-456' }, 'some body'), '') }.should raise_error(ArgumentError)
|
174
|
+
lambda { @common.ack('msg-456', { :subscription => ''}) }.should raise_error(ArgumentError)
|
175
|
+
lambda { @common.ack(Stomper::Frame.new('MESSAGE', { :'message-id' => 'msg-456' }, 'some body'), '', { :subscription => nil }) }.should raise_error(ArgumentError)
|
176
|
+
lambda { @common.ack('msg-456', { :subscription => 'sub-123'}) }.should raise_error(ArgumentError)
|
177
|
+
end
|
178
|
+
it "should raise an error when the message-id cannot be inferred" do
|
179
|
+
lambda { @common.ack('', 'sub-123') }.should raise_error(ArgumentError)
|
180
|
+
lambda { @common.ack(Stomper::Frame.new('MESSAGE', { :'message-id' => '' }, 'some body'), 'sub-123') }.should raise_error(ArgumentError)
|
181
|
+
lambda { @common.ack(nil, 'sub-123') }.should raise_error(ArgumentError)
|
182
|
+
lambda { @common.ack(Stomper::Frame.new('MESSAGE', {}, 'some body'), 'sub-123') }.should raise_error(ArgumentError)
|
183
|
+
lambda { @common.ack('', { :'message-id' => 'msg-456', :subscription => 'sub-123'}) }.should raise_error(ArgumentError)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module Stomper::Extensions
|
5
|
+
describe Events do
|
6
|
+
before(:each) do
|
7
|
+
@events = mock('events')
|
8
|
+
@events.extend Events
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "basic event callbacks" do
|
12
|
+
[:on_abort, :on_ack, :on_begin, :on_commit, :on_connect, :on_stomp,
|
13
|
+
:on_connected, :on_disconnect, :on_error, :on_message, :on_nack,
|
14
|
+
:on_receipt, :on_send, :on_subscribe, :on_unsubscribe, :on_client_beat,
|
15
|
+
:on_broker_beat, :on_connection_established, :on_connection_closed,
|
16
|
+
:on_connection_terminated, :on_connection_disconnected,
|
17
|
+
:on_connection_died, :before_transmitting, :after_transmitting,
|
18
|
+
:before_receiving, :after_receiving].each do |event_name|
|
19
|
+
|
20
|
+
it "should register a callback for #{event_name} and trigger appropriately" do
|
21
|
+
triggered = false
|
22
|
+
@events.__send__(event_name) do
|
23
|
+
triggered = true
|
24
|
+
end
|
25
|
+
@events.__send__(:trigger_event, event_name)
|
26
|
+
triggered.should be_true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "frame event callbacks" do
|
32
|
+
[ :on_connected, :on_error, :on_message, :on_receipt].each do |event_name|
|
33
|
+
|
34
|
+
it "should register a callback for #{event_name} and trigger when the frame is received" do
|
35
|
+
command_name = event_name.to_s.split('_').last.upcase
|
36
|
+
triggered = false
|
37
|
+
@events.__send__(event_name) do
|
38
|
+
triggered = true
|
39
|
+
end
|
40
|
+
@events.__send__(:trigger_received_frame, ::Stomper::Frame.new(command_name))
|
41
|
+
triggered.should be_true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
[:on_abort, :on_ack, :on_begin, :on_commit, :on_connect, :on_stomp,
|
46
|
+
:on_disconnect, :on_nack, :on_send, :on_subscribe, :on_unsubscribe].each do |event_name|
|
47
|
+
|
48
|
+
it "should register a callback for #{event_name} and trigger when the frame is transmitted" do
|
49
|
+
command_name = event_name.to_s.split('_').last.upcase
|
50
|
+
triggered = false
|
51
|
+
@events.__send__(event_name) do
|
52
|
+
triggered = true
|
53
|
+
end
|
54
|
+
@events.__send__(:trigger_transmitted_frame, ::Stomper::Frame.new(command_name))
|
55
|
+
triggered.should be_true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should trigger a broker beat when receiving a frame with no command" do
|
60
|
+
triggered = false
|
61
|
+
@events.on_broker_beat do
|
62
|
+
triggered = true
|
63
|
+
end
|
64
|
+
@events.__send__(:trigger_received_frame, ::Stomper::Frame.new)
|
65
|
+
triggered.should be_true
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should trigger a client beat when receiving a frame with no command" do
|
69
|
+
triggered = false
|
70
|
+
@events.on_client_beat do
|
71
|
+
triggered = true
|
72
|
+
end
|
73
|
+
@events.__send__(:trigger_transmitted_frame, ::Stomper::Frame.new)
|
74
|
+
triggered.should be_true
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module Stomper::Extensions
|
5
|
+
describe Heartbeat do
|
6
|
+
before(:each) do
|
7
|
+
@heartbeats = mock('heartbeats')
|
8
|
+
@heartbeats.extend Heartbeat
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "Shared default interface" do
|
12
|
+
it "should be dead? when it is not alive?" do
|
13
|
+
@heartbeats.stub(:alive? => true)
|
14
|
+
@heartbeats.dead?.should be_false
|
15
|
+
|
16
|
+
@heartbeats.stub(:alive? => false)
|
17
|
+
@heartbeats.dead?.should be_true
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should do nothing when :beat is called" do
|
21
|
+
@heartbeats.beat.should be_nil
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should be alive if the connection is connected?" do
|
25
|
+
@heartbeats.should_receive(:connected?).and_return(true)
|
26
|
+
@heartbeats.alive?.should be_true
|
27
|
+
@heartbeats.should_receive(:connected?).and_return(false)
|
28
|
+
@heartbeats.alive?.should be_false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "1.1 Protocol Extensions" do
|
33
|
+
before(:each) do
|
34
|
+
Heartbeat.extend_by_protocol_version(@heartbeats, '1.1')
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should include the 1.1 extensions" do
|
38
|
+
@heartbeats.should be_a_kind_of(Heartbeat::V1_1)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should transmit a heartbeat frame through :beat" do
|
42
|
+
@heartbeats.should_receive(:transmit).with(stomper_heartbeat_frame)
|
43
|
+
@heartbeats.beat
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should be alive if client and broker are alive and connected" do
|
47
|
+
@heartbeats.stub(:client_alive? => true, :broker_alive? => true, :connected? => true)
|
48
|
+
@heartbeats.alive?.should be_true
|
49
|
+
@heartbeats.stub(:connected? => false)
|
50
|
+
@heartbeats.alive?.should be_false
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
it "should be dead if connected and broker is alive but client is not" do
|
55
|
+
@heartbeats.stub(:client_alive? => false, :broker_alive? => true, :connected? => true)
|
56
|
+
@heartbeats.alive?.should be_false
|
57
|
+
@heartbeats.stub(:client_alive? => true)
|
58
|
+
@heartbeats.alive?.should be_true
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should be dead if connected and client is alive but broker is not" do
|
62
|
+
@heartbeats.stub(:client_alive? => true, :broker_alive? => false, :connected? => true)
|
63
|
+
@heartbeats.alive?.should be_false
|
64
|
+
@heartbeats.stub(:broker_alive? => true)
|
65
|
+
@heartbeats.alive?.should be_true
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should have a living client if client beats are disabled" do
|
69
|
+
@heartbeats.stub(:heartbeating => [0, 10], :connected? => true)
|
70
|
+
@heartbeats.client_alive?.should be_true
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should have a living broker if broker beats are disabled" do
|
74
|
+
@heartbeats.stub(:heartbeating => [10, 0], :connected? => true)
|
75
|
+
@heartbeats.broker_alive?.should be_true
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should have a living client if beating is enabled and transmitted within a marin of error" do
|
79
|
+
@heartbeats.stub(:heartbeating => [1_000, 0], :connected? => true)
|
80
|
+
@heartbeats.stub(:duration_since_transmitted => 50)
|
81
|
+
@heartbeats.client_alive?.should be_true
|
82
|
+
@heartbeats.stub(:duration_since_transmitted => 1_000)
|
83
|
+
@heartbeats.client_alive?.should be_true
|
84
|
+
@heartbeats.stub(:duration_since_transmitted => 1_100)
|
85
|
+
@heartbeats.client_alive?.should be_true
|
86
|
+
@heartbeats.stub(:duration_since_transmitted => 1_200)
|
87
|
+
@heartbeats.client_alive?.should be_false
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should have a living broker if beating is enabled and received within a marin of error" do
|
91
|
+
@heartbeats.stub(:heartbeating => [0, 5_000], :connected? => true)
|
92
|
+
@heartbeats.stub(:duration_since_received => 100)
|
93
|
+
@heartbeats.broker_alive?.should be_true
|
94
|
+
@heartbeats.stub(:duration_since_received => 5_000)
|
95
|
+
@heartbeats.broker_alive?.should be_true
|
96
|
+
@heartbeats.stub(:duration_since_received => 5_500)
|
97
|
+
@heartbeats.broker_alive?.should be_true
|
98
|
+
@heartbeats.stub(:duration_since_received => 5_600)
|
99
|
+
@heartbeats.broker_alive?.should be_false
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module Stomper::Extensions
|
5
|
+
describe Scoping do
|
6
|
+
before(:each) do
|
7
|
+
@scoping = mock("scoping", :version => '1.0')
|
8
|
+
@scoping.extend Scoping
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should return a new HeaderScope" do
|
12
|
+
@scoping.with_headers({}).should be_an_instance_of(::Stomper::Scopes::HeaderScope)
|
13
|
+
end
|
14
|
+
it "should return a new TransactionScope" do
|
15
|
+
@scoping.with_transaction.should be_an_instance_of(::Stomper::Scopes::TransactionScope)
|
16
|
+
end
|
17
|
+
it "should return a new ReceiptScope" do
|
18
|
+
@scoping.with_receipt.should be_an_instance_of(::Stomper::Scopes::ReceiptScope)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,318 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module Stomper
|
5
|
+
if RUBY_VERSION < '1.9'
|
6
|
+
describe FrameSerializer do
|
7
|
+
before(:each) do
|
8
|
+
@messages = {
|
9
|
+
:content_type_and_charset => "MESSAGE\ncontent-type:text/plain; charset=ISO-8859-1\ncontent-length:6\na-header: padded \n\nh\xEBllo!\000",
|
10
|
+
:escaped_headers => "MESSAGE\ncontent-type:text/plain;charset=UTF-8\ncontent-length:7\na\\nspecial\\chead\\\\cer: padded\\c and using\\nspecial\\\\\\\\\\\\ncharacters \n\nh\xC3\xABllo!\000",
|
11
|
+
:no_content_length => "MESSAGE\ncontent-type:text/plain\n\nh\xC3\xABllo!\000",
|
12
|
+
:repeated_headers => "MESSAGE\ncontent-type:text/plain\nrepeated header:a value\nrepeated header:alternate value\n\nh\xC3\xABllo!\000",
|
13
|
+
:non_text_content_type => "MESSAGE\ncontent-type:not-text/other\n\nh\xC3\xABllo!\000",
|
14
|
+
:no_content_type => "MESSAGE\n\nh\xC3\xABllo!\000",
|
15
|
+
:invalid_content_length => "MESSAGE\ncontent-length:4\n\n12345\000",
|
16
|
+
:invalid_header_character => "MESSAGE\ngrandpa:he was:anti\n\n12345\000",
|
17
|
+
:invalid_header_sequence => "MESSAGE\ngrandpa:he was\\ranti\n\n12345\000",
|
18
|
+
:malformed_header => "MESSAGE\nearth_below_us\nfloating:weightless\n\n12345\000",
|
19
|
+
:dangling_header_sequence => "MESSAGE\ngrandpa:he was anti\\\n\n12345\000",
|
20
|
+
}
|
21
|
+
|
22
|
+
@frames = {
|
23
|
+
:common => ::Stomper::Frame.new('FRAME', {}, 'body of message'),
|
24
|
+
:no_headers => ::Stomper::Frame.new('FRAME', {}, 'body of message'),
|
25
|
+
:no_body => ::Stomper::Frame.new('FRAME'),
|
26
|
+
:no_command => ::Stomper::Frame.new,
|
27
|
+
:header_name_with_linefeed => ::Stomper::Frame.new('FRAME', { "a\ntest\nheader" => "va\\lue : is\n\nme"}),
|
28
|
+
:header_name_with_colon => ::Stomper::Frame.new('FRAME', { "a:test:header" => "va\\lue : is\n\nme"}),
|
29
|
+
:header_name_with_backslash => ::Stomper::Frame.new('FRAME', { "a\\test\\header" => "va\\lue : is\n\nme"}),
|
30
|
+
:binary_body_no_content_type => ::Stomper::Frame.new('FRAME', {}, 'body of message'),
|
31
|
+
:charset_header_text_body => ::Stomper::Frame.new('FRAME', {:'content-type' => 'text/plain; param="value";charset=ISO-8859-1'}, 'body of message'),
|
32
|
+
:charset_header_binary_body => ::Stomper::Frame.new('FRAME', {:'content-type' => 'application/pdf; param="value";charset=ISO-8859-1'}, 'body of message')
|
33
|
+
}
|
34
|
+
#{ :header_1 => 'value 1', :header_2 => '3', :header_3 => '', :'content-type' => 'text/plain'}
|
35
|
+
@frames[:common][:header_1] = 'value 1'
|
36
|
+
@frames[:common][:header_2] = '3'
|
37
|
+
@frames[:common][:header_3] = ''
|
38
|
+
@frames[:common][:'content-type'] = 'text/plain'
|
39
|
+
# :header_1 => 'val', :musical => '', :offering => '4'
|
40
|
+
@frames[:no_body][:header_1] = 'val'
|
41
|
+
@frames[:no_body][:musical] = ''
|
42
|
+
@frames[:no_body][:offering] = '4'
|
43
|
+
@frame_io = StringIO.new
|
44
|
+
@frame_serializer = FrameSerializer.new(@frame_io)
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "thread safety" do
|
48
|
+
before(:each) do
|
49
|
+
@frame_serializer = FrameSerializer.new(mock('frame io'))
|
50
|
+
end
|
51
|
+
it "should synchronize writing to the underlying IO" do
|
52
|
+
first_called = false
|
53
|
+
call_next = false
|
54
|
+
ordered = []
|
55
|
+
@frame_serializer.stub!(:__write_frame__).and_return do |f|
|
56
|
+
first_called = true
|
57
|
+
ordered << 1
|
58
|
+
Thread.stop
|
59
|
+
ordered << 2
|
60
|
+
f
|
61
|
+
end
|
62
|
+
|
63
|
+
thread_1 = Thread.new do
|
64
|
+
@frame_serializer.write_frame(mock('frame'))
|
65
|
+
end
|
66
|
+
thread_2 = Thread.new do
|
67
|
+
Thread.pass until call_next
|
68
|
+
Thread.pass
|
69
|
+
thread_1.run
|
70
|
+
end
|
71
|
+
Thread.pass until first_called
|
72
|
+
call_next = true
|
73
|
+
|
74
|
+
@frame_serializer.stub!(:__write_frame__).and_return do |f|
|
75
|
+
ordered << 3
|
76
|
+
f
|
77
|
+
end
|
78
|
+
@frame_serializer.write_frame(mock('frame'))
|
79
|
+
thread_1.join
|
80
|
+
thread_2.join
|
81
|
+
ordered.should == [1, 2, 3]
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should synchronize reading from the underlying IO" do
|
85
|
+
first_called = false
|
86
|
+
call_next = false
|
87
|
+
ordered = []
|
88
|
+
@frame_serializer.stub!(:__read_frame__).and_return do
|
89
|
+
first_called = true
|
90
|
+
ordered << 1
|
91
|
+
Thread.stop
|
92
|
+
ordered << 2
|
93
|
+
mock('frame 1')
|
94
|
+
end
|
95
|
+
|
96
|
+
thread_1 = Thread.new do
|
97
|
+
@frame_serializer.read_frame
|
98
|
+
end
|
99
|
+
thread_2 = Thread.new do
|
100
|
+
Thread.pass until call_next
|
101
|
+
Thread.pass
|
102
|
+
thread_1.run
|
103
|
+
end
|
104
|
+
Thread.pass until first_called
|
105
|
+
call_next = true
|
106
|
+
|
107
|
+
@frame_serializer.stub!(:__read_frame__).and_return do
|
108
|
+
ordered << 3
|
109
|
+
mock('frame 2')
|
110
|
+
end
|
111
|
+
@frame_serializer.read_frame
|
112
|
+
thread_1.join
|
113
|
+
thread_2.join
|
114
|
+
ordered.should == [1, 2, 3]
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should not make reading and writing mutually exclusive" do
|
118
|
+
first_called = false
|
119
|
+
call_next = false
|
120
|
+
ordered = []
|
121
|
+
@frame_serializer.stub!(:__write_frame__).and_return do |f|
|
122
|
+
first_called = true
|
123
|
+
ordered << 1
|
124
|
+
Thread.stop
|
125
|
+
ordered << 2
|
126
|
+
f
|
127
|
+
end
|
128
|
+
@frame_serializer.stub!(:__read_frame__).and_return do
|
129
|
+
ordered << 3
|
130
|
+
mock('frame 2')
|
131
|
+
end
|
132
|
+
|
133
|
+
thread_1 = Thread.new do
|
134
|
+
@frame_serializer.write_frame(mock('frame'))
|
135
|
+
end
|
136
|
+
thread_2 = Thread.new do
|
137
|
+
Thread.pass until call_next
|
138
|
+
Thread.pass
|
139
|
+
thread_1.run
|
140
|
+
end
|
141
|
+
Thread.pass until first_called
|
142
|
+
call_next = true
|
143
|
+
@frame_serializer.read_frame
|
144
|
+
thread_1.join
|
145
|
+
thread_2.join
|
146
|
+
ordered.should == [1, 3, 2]
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe "Protocol 1.0" do
|
151
|
+
it "should not have extended the V1_1 mixin" do
|
152
|
+
::Stomper::FrameSerializer::EXTEND_BY_VERSION['1.1'].each do |mod|
|
153
|
+
@frame_serializer.should_not be_a_kind_of(mod)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe "writing frames" do
|
158
|
+
it "should properly serialize a common frame" do
|
159
|
+
@frame_serializer.write_frame(@frames[:common])
|
160
|
+
@frame_io.string.should == "FRAME\nheader_1:value 1\nheader_2:3\nheader_3:\ncontent-type:text/plain;charset=UTF-8\ncontent-length:15\n\nbody of message\000"
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should properly serialize a frame without headers - content type cannot be inferred in Ruby 1.8.7" do
|
164
|
+
@frame_serializer.write_frame(@frames[:no_headers])
|
165
|
+
@frame_io.string.should == "FRAME\ncontent-length:15\n\nbody of message\000"
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should properly serialize a frame without a body" do
|
169
|
+
@frame_serializer.write_frame(@frames[:no_body])
|
170
|
+
@frame_io.string.should == "FRAME\nheader_1:val\nmusical:\noffering:4\n\n\000"
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should properly serialize a frame without a command as a new line" do
|
174
|
+
@frame_serializer.write_frame(@frames[:no_command])
|
175
|
+
@frame_io.string.should == "\n"
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should properly drop LF from header names and values" do
|
179
|
+
@frame_serializer.write_frame(@frames[:header_name_with_linefeed])
|
180
|
+
@frame_io.string.should == "FRAME\natestheader:va\\lue : isme\n\n\000"
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should not escape backslash characters in header names or values" do
|
184
|
+
@frame_serializer.write_frame(@frames[:header_name_with_backslash])
|
185
|
+
@frame_io.string.should == "FRAME\na\\test\\header:va\\lue : isme\n\n\000"
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should drop colons in header names, but leave them alone in values" do
|
189
|
+
@frame_serializer.write_frame(@frames[:header_name_with_colon])
|
190
|
+
@frame_io.string.should == "FRAME\natestheader:va\\lue : isme\n\n\000"
|
191
|
+
end
|
192
|
+
|
193
|
+
it "should not generate a content-type header if the encoding is binary" do
|
194
|
+
@frame_serializer.write_frame(@frames[:binary_body_no_content_type])
|
195
|
+
@frame_io.string.should == "FRAME\ncontent-length:15\n\nbody of message\000"
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should preserve the charset parameter of text bodies, because we have nothing else to work with in Ruby 1.8.7" do
|
199
|
+
@frame_serializer.write_frame(@frames[:charset_header_text_body])
|
200
|
+
@frame_io.string.should == "FRAME\ncontent-type:text/plain; param=\"value\";charset=ISO-8859-1\ncontent-length:15\n\nbody of message\000"
|
201
|
+
end
|
202
|
+
|
203
|
+
it "should preserve the charset parameter of binary bodies, because we have nothing else to work with in Ruby 1.8.7" do
|
204
|
+
@frame_serializer.write_frame(@frames[:charset_header_binary_body])
|
205
|
+
@frame_io.string.should == "FRAME\ncontent-type:application/pdf; param=\"value\";charset=ISO-8859-1\ncontent-length:15\n\nbody of message\000"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
describe "Protocol 1.1" do
|
211
|
+
before(:each) do
|
212
|
+
@frame_serializer.extend_for_protocol '1.1'
|
213
|
+
end
|
214
|
+
|
215
|
+
it "should have extended the V1_1 mixin" do
|
216
|
+
::Stomper::FrameSerializer::EXTEND_BY_VERSION['1.1'].each do |mod|
|
217
|
+
@frame_serializer.should be_a_kind_of(mod)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
describe "writing frames" do
|
222
|
+
it "should properly serialize a common frame" do
|
223
|
+
@frame_serializer.write_frame(@frames[:common])
|
224
|
+
@frame_io.string.should == "FRAME\nheader_1:value 1\nheader_2:3\nheader_3:\ncontent-type:text/plain;charset=UTF-8\ncontent-length:15\n\nbody of message\000"
|
225
|
+
end
|
226
|
+
|
227
|
+
it "should properly serialize a frame without headers - content type cannot be inferred in Ruby 1.8.7" do
|
228
|
+
@frame_serializer.write_frame(@frames[:no_headers])
|
229
|
+
@frame_io.string.should == "FRAME\ncontent-length:15\n\nbody of message\000"
|
230
|
+
end
|
231
|
+
|
232
|
+
it "should properly serialize a frame without a body" do
|
233
|
+
@frame_serializer.write_frame(@frames[:no_body])
|
234
|
+
@frame_io.string.should == "FRAME\nheader_1:val\nmusical:\noffering:4\n\n\000"
|
235
|
+
end
|
236
|
+
|
237
|
+
it "should properly serialize a frame without a command as a new line" do
|
238
|
+
@frame_serializer.write_frame(@frames[:no_command])
|
239
|
+
@frame_io.string.should == "\n"
|
240
|
+
end
|
241
|
+
|
242
|
+
it "should escape LF in header names and values" do
|
243
|
+
@frame_serializer.write_frame(@frames[:header_name_with_linefeed])
|
244
|
+
@frame_io.string.should == "FRAME\na\\ntest\\nheader:va\\\\lue \\c is\\n\\nme\n\n\000"
|
245
|
+
end
|
246
|
+
|
247
|
+
it "should escape backslashes in header names and values" do
|
248
|
+
@frame_serializer.write_frame(@frames[:header_name_with_backslash])
|
249
|
+
@frame_io.string.should == "FRAME\na\\\\test\\\\header:va\\\\lue \\c is\\n\\nme\n\n\000"
|
250
|
+
end
|
251
|
+
|
252
|
+
it "should escape colons in header names and values" do
|
253
|
+
@frame_serializer.write_frame(@frames[:header_name_with_colon])
|
254
|
+
@frame_io.string.should == "FRAME\na\\ctest\\cheader:va\\\\lue \\c is\\n\\nme\n\n\000"
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
describe "reading frames" do
|
259
|
+
it "should properly de-serialize a simple frame" do
|
260
|
+
@frame_io.string = @messages[:content_type_and_charset]
|
261
|
+
frame = @frame_serializer.read_frame
|
262
|
+
frame.command.should == "MESSAGE"
|
263
|
+
frame.headers.sort { |a, b| a.first <=> b.first }.should == [
|
264
|
+
['a-header', ' padded '], ['content-length', '6'],
|
265
|
+
['content-type', 'text/plain; charset=ISO-8859-1']
|
266
|
+
]
|
267
|
+
frame.body.should == "h\xEBllo!"
|
268
|
+
end
|
269
|
+
it "should properly read a frame with special characters in its header" do
|
270
|
+
@frame_io.string = @messages[:escaped_headers]
|
271
|
+
frame = @frame_serializer.read_frame
|
272
|
+
frame["a\nspecial:head\\cer"].should == " padded: and using\nspecial\\\\\\ncharacters "
|
273
|
+
end
|
274
|
+
it "should properly read a frame with a body and no content-length" do
|
275
|
+
@frame_io.string = @messages[:no_content_length]
|
276
|
+
frame = @frame_serializer.read_frame
|
277
|
+
frame.body.should == "hëllo!"
|
278
|
+
end
|
279
|
+
it "should assume a binary charset if none is set and the content-type does not match text/*" do
|
280
|
+
@frame_io.string = @messages[:non_text_content_type]
|
281
|
+
frame = @frame_serializer.read_frame
|
282
|
+
end
|
283
|
+
it "should assume a binary charset if the content-type header is not specified" do
|
284
|
+
@frame_io.string = @messages[:no_content_type]
|
285
|
+
frame = @frame_serializer.read_frame
|
286
|
+
end
|
287
|
+
it "should set the value of a header to the first occurrence" do
|
288
|
+
@frame_io.string = @messages[:repeated_headers]
|
289
|
+
frame = @frame_serializer.read_frame
|
290
|
+
end
|
291
|
+
it "should raise a malformed frame error if the frame is not properly terminated" do
|
292
|
+
@frame_io.string = @messages[:invalid_content_length]
|
293
|
+
lambda { @frame_serializer.read_frame }.should raise_error(::Stomper::Errors::MalformedFrameError)
|
294
|
+
end
|
295
|
+
# While the spec suggests that all ":" chars be replaced with "\c", ActiveMQ 5.3.2 sends
|
296
|
+
# a "session" header with a value that contains ":" chars. So, we are NOT going to
|
297
|
+
# freak out if we receive more than one ":" on a header line.
|
298
|
+
it "should not raise an error if the frame contains a header value with a raw ':'" do
|
299
|
+
@frame_io.string = @messages[:invalid_header_character]
|
300
|
+
lambda { @frame_serializer.read_frame }.should_not raise_error
|
301
|
+
end
|
302
|
+
it "should raise an invalid header esacape sequence error if the frame contains a header with an invalid escape sequence" do
|
303
|
+
@frame_io.string = @messages[:invalid_header_sequence]
|
304
|
+
lambda { @frame_serializer.read_frame }.should raise_error(::Stomper::Errors::InvalidHeaderEscapeSequenceError)
|
305
|
+
end
|
306
|
+
it "should raise an malfored header error if the frame contains an incomplete header" do
|
307
|
+
@frame_io.string = @messages[:malformed_header]
|
308
|
+
lambda { @frame_serializer.read_frame }.should raise_error(::Stomper::Errors::MalformedHeaderError)
|
309
|
+
end
|
310
|
+
it "should raise an invalid header esacape sequence error if the frame contains a header with a dangling escape sequence" do
|
311
|
+
@frame_io.string = @messages[:dangling_header_sequence]
|
312
|
+
lambda { @frame_serializer.read_frame }.should raise_error(::Stomper::Errors::InvalidHeaderEscapeSequenceError)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|