onstomp 1.0.0pre1
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/.autotest +2 -0
- data/.gitignore +6 -0
- data/.rspec +2 -0
- data/.yardopts +5 -0
- data/CHANGELOG.md +4 -0
- data/DeveloperNarrative.md +15 -0
- data/Gemfile +4 -0
- data/LICENSE.md +221 -0
- data/README.md +73 -0
- data/Rakefile +6 -0
- data/UserNarrative.md +8 -0
- data/examples/basic.rb +40 -0
- data/examples/events.rb +72 -0
- data/lib/onstomp/client.rb +152 -0
- data/lib/onstomp/components/frame.rb +108 -0
- data/lib/onstomp/components/frame_headers.rb +212 -0
- data/lib/onstomp/components/nil_processor.rb +20 -0
- data/lib/onstomp/components/scopes/header_scope.rb +25 -0
- data/lib/onstomp/components/scopes/receipt_scope.rb +25 -0
- data/lib/onstomp/components/scopes/transaction_scope.rb +191 -0
- data/lib/onstomp/components/scopes.rb +45 -0
- data/lib/onstomp/components/subscription.rb +30 -0
- data/lib/onstomp/components/threaded_processor.rb +62 -0
- data/lib/onstomp/components/uri.rb +30 -0
- data/lib/onstomp/components.rb +13 -0
- data/lib/onstomp/connections/base.rb +208 -0
- data/lib/onstomp/connections/heartbeating.rb +82 -0
- data/lib/onstomp/connections/serializers/stomp_1.rb +166 -0
- data/lib/onstomp/connections/serializers/stomp_1_0.rb +41 -0
- data/lib/onstomp/connections/serializers/stomp_1_1.rb +134 -0
- data/lib/onstomp/connections/serializers.rb +9 -0
- data/lib/onstomp/connections/stomp_1.rb +69 -0
- data/lib/onstomp/connections/stomp_1_0.rb +28 -0
- data/lib/onstomp/connections/stomp_1_1.rb +65 -0
- data/lib/onstomp/connections.rb +119 -0
- data/lib/onstomp/interfaces/client_configurable.rb +55 -0
- data/lib/onstomp/interfaces/client_events.rb +168 -0
- data/lib/onstomp/interfaces/connection_events.rb +62 -0
- data/lib/onstomp/interfaces/event_manager.rb +69 -0
- data/lib/onstomp/interfaces/frame_methods.rb +190 -0
- data/lib/onstomp/interfaces/receipt_manager.rb +33 -0
- data/lib/onstomp/interfaces/subscription_manager.rb +48 -0
- data/lib/onstomp/interfaces/uri_configurable.rb +106 -0
- data/lib/onstomp/interfaces.rb +14 -0
- data/lib/onstomp/version.rb +13 -0
- data/lib/onstomp.rb +147 -0
- data/onstomp.gemspec +29 -0
- data/spec/onstomp/client_spec.rb +265 -0
- data/spec/onstomp/components/frame_headers_spec.rb +163 -0
- data/spec/onstomp/components/frame_spec.rb +144 -0
- data/spec/onstomp/components/nil_processor_spec.rb +32 -0
- data/spec/onstomp/components/scopes/header_scope_spec.rb +27 -0
- data/spec/onstomp/components/scopes/receipt_scope_spec.rb +33 -0
- data/spec/onstomp/components/scopes/transaction_scope_spec.rb +227 -0
- data/spec/onstomp/components/scopes_spec.rb +63 -0
- data/spec/onstomp/components/subscription_spec.rb +58 -0
- data/spec/onstomp/components/threaded_processor_spec.rb +92 -0
- data/spec/onstomp/components/uri_spec.rb +33 -0
- data/spec/onstomp/connections/base_spec.rb +349 -0
- data/spec/onstomp/connections/heartbeating_spec.rb +132 -0
- data/spec/onstomp/connections/serializers/stomp_1_0_spec.rb +50 -0
- data/spec/onstomp/connections/serializers/stomp_1_1_spec.rb +99 -0
- data/spec/onstomp/connections/serializers/stomp_1_spec.rb +104 -0
- data/spec/onstomp/connections/stomp_1_0_spec.rb +54 -0
- data/spec/onstomp/connections/stomp_1_1_spec.rb +137 -0
- data/spec/onstomp/connections/stomp_1_spec.rb +113 -0
- data/spec/onstomp/connections_spec.rb +135 -0
- data/spec/onstomp/interfaces/client_events_spec.rb +108 -0
- data/spec/onstomp/interfaces/connection_events_spec.rb +55 -0
- data/spec/onstomp/interfaces/event_manager_spec.rb +72 -0
- data/spec/onstomp/interfaces/frame_methods_spec.rb +109 -0
- data/spec/onstomp/interfaces/receipt_manager_spec.rb +53 -0
- data/spec/onstomp/interfaces/subscription_manager_spec.rb +64 -0
- data/spec/onstomp_spec.rb +15 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/support/custom_argument_matchers.rb +51 -0
- data/spec/support/frame_matchers.rb +88 -0
- data/spec/support/shared_frame_method_examples.rb +116 -0
- data/yard_extensions.rb +32 -0
- metadata +219 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
|
|
4
|
+
module OnStomp::Connections::Serializers
|
|
5
|
+
describe Stomp_1 do
|
|
6
|
+
let(:serializer) {
|
|
7
|
+
mock('serializer').tap do |m|
|
|
8
|
+
m.extend Stomp_1
|
|
9
|
+
end
|
|
10
|
+
}
|
|
11
|
+
describe ".frame_to_bytes" do
|
|
12
|
+
let(:frame) {
|
|
13
|
+
mock('frame')
|
|
14
|
+
}
|
|
15
|
+
it "should call frame_to_string and encode the result to ASCII-8BIT" do
|
|
16
|
+
serializer.stub(:frame_to_string).with(frame).and_return('SERIALIZED FRAME')
|
|
17
|
+
ser = serializer.frame_to_bytes(frame)
|
|
18
|
+
ser.should == 'SERIALIZED FRAME'
|
|
19
|
+
if RUBY_VERSION >= '1.9'
|
|
20
|
+
ser.encoding.name.should == 'ASCII-8BIT'
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe ".frame_to_string_base" do
|
|
26
|
+
let(:frame) {
|
|
27
|
+
OnStomp::Components::Frame.new('COMMAND', {}, 'body of frame').tap do |f|
|
|
28
|
+
f[:heaDer1] = 'Value 1'
|
|
29
|
+
f[:headeR2] = 'Value 2'
|
|
30
|
+
f['HEADER3'] = 'Value 3'
|
|
31
|
+
end
|
|
32
|
+
}
|
|
33
|
+
it "should treat a frame with no command as a heartbeat" do
|
|
34
|
+
frame.command = nil
|
|
35
|
+
serializer.frame_to_string_base(frame).should == "\n"
|
|
36
|
+
end
|
|
37
|
+
it "should serialize a frame as a string, yielding headers to the supplied block" do
|
|
38
|
+
serializer.frame_to_string_base(frame) do |k,v|
|
|
39
|
+
"#{k.downcase}:#{v.upcase}\n"
|
|
40
|
+
end.should == "COMMAND\nheader1:VALUE 1\nheader2:VALUE 2\nheader3:VALUE 3\ncontent-length:13\n\nbody of frame\000"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
describe ".bytes_to_frame" do
|
|
45
|
+
let(:buffer) {
|
|
46
|
+
[ "COMMAND1\nheader", " 1:Test value 1", "\nHeader 2:",
|
|
47
|
+
"Test value 2\n", "content-leng", "th:1",
|
|
48
|
+
"6\n\ntesting \000his guy", "\000\n\nCOMMAND4",
|
|
49
|
+
"\nNext Header: some value \nMore headers:another value\n",
|
|
50
|
+
"\nbody of the frame without content-length\000\nCOMMAND6",
|
|
51
|
+
"\nheader:and its value\n\n\000", "COMMAND7\nheader",
|
|
52
|
+
" for command 7:another value\n\nyet another body\000",
|
|
53
|
+
"\n\nCOMMAND10\nlast Header:last Header ValuE!\n\nthis is a",
|
|
54
|
+
"nother body for yet another ", "frame", "\000" ]
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
before(:each) do
|
|
58
|
+
serializer.reset_parser
|
|
59
|
+
if RUBY_VERSION >= '1.9'
|
|
60
|
+
buffer.each do |b|
|
|
61
|
+
b.force_encoding('ASCII-8BIT')
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
it "should parse and yield the frames contained in the buffers" do
|
|
66
|
+
serializer.should_receive(:split_header).exactly(8).times.and_return do |str|
|
|
67
|
+
str.split(':')
|
|
68
|
+
end
|
|
69
|
+
serializer.should_receive(:prepare_parsed_frame).exactly(5).times
|
|
70
|
+
yielded = []
|
|
71
|
+
serializer.bytes_to_frame(buffer) { |f| yielded << f }
|
|
72
|
+
yielded.size.should == 10
|
|
73
|
+
yielded[0].should be_an_onstomp_frame('COMMAND1', {
|
|
74
|
+
:'header 1' => 'Test value 1', :'Header 2' => 'Test value 2',
|
|
75
|
+
:'content-length' => '16'
|
|
76
|
+
}, "testing \000his guy")
|
|
77
|
+
yielded[1].should be_an_onstomp_frame(nil, {}, nil)
|
|
78
|
+
yielded[2].should be_an_onstomp_frame(nil, {}, nil)
|
|
79
|
+
yielded[3].should be_an_onstomp_frame('COMMAND4', {
|
|
80
|
+
:'Next Header' => ' some value ', :'More headers' => 'another value',
|
|
81
|
+
}, 'body of the frame without content-length')
|
|
82
|
+
yielded[4].should be_an_onstomp_frame(nil, {}, nil)
|
|
83
|
+
# Is this what you really want?
|
|
84
|
+
yielded[5].should be_an_onstomp_frame('COMMAND6', {
|
|
85
|
+
:header => 'and its value' }, '')
|
|
86
|
+
yielded[6].should be_an_onstomp_frame('COMMAND7', {
|
|
87
|
+
:'header for command 7' => 'another value' }, 'yet another body')
|
|
88
|
+
yielded[7].should be_an_onstomp_frame(nil, {}, nil)
|
|
89
|
+
yielded[8].should be_an_onstomp_frame(nil, {}, nil)
|
|
90
|
+
yielded[9].should be_an_onstomp_frame('COMMAND10', {
|
|
91
|
+
:'last Header' => 'last Header ValuE!'
|
|
92
|
+
}, 'this is another body for yet another frame')
|
|
93
|
+
end
|
|
94
|
+
it "should raise a malformed frame error if the content-length is a lie" do
|
|
95
|
+
serializer.stub(:split_header).and_return do |str|
|
|
96
|
+
str.split(':')
|
|
97
|
+
end
|
|
98
|
+
lambda {
|
|
99
|
+
serializer.bytes_to_frame(["COMMAND\ncontent-length:5\n\nmore than 5\000"])
|
|
100
|
+
}.should raise_error(OnStomp::MalformedFrameError)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
|
|
4
|
+
module OnStomp::Connections
|
|
5
|
+
describe Stomp_1_0 do
|
|
6
|
+
let(:io) {
|
|
7
|
+
mock('io')
|
|
8
|
+
}
|
|
9
|
+
let(:client) {
|
|
10
|
+
mock('client')
|
|
11
|
+
}
|
|
12
|
+
let(:connection) {
|
|
13
|
+
Stomp_1_0.new(io, client)
|
|
14
|
+
}
|
|
15
|
+
describe "ancestors" do
|
|
16
|
+
it "should be a kind of Base connection" do
|
|
17
|
+
connection.should be_a_kind_of(OnStomp::Connections::Base)
|
|
18
|
+
end
|
|
19
|
+
it "should be a kind of Stomp_1 connection" do
|
|
20
|
+
connection.should be_a_kind_of(OnStomp::Connections::Stomp_1)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe ".serializer" do
|
|
25
|
+
it "should use a Stomp_1_0 serializer" do
|
|
26
|
+
connection.serializer.should be_a_kind_of(OnStomp::Connections::Serializers::Stomp_1_0)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe ".ack_frame" do
|
|
31
|
+
let(:message_frame) {
|
|
32
|
+
OnStomp::Components::Frame.new('MESSAGE', :'message-id' => 'm-1234')
|
|
33
|
+
}
|
|
34
|
+
it "should create an ack frame for a MESSAGE frame" do
|
|
35
|
+
connection.ack_frame(message_frame).should be_an_onstomp_frame('ACK',
|
|
36
|
+
{:'message-id' => 'm-1234'}, nil)
|
|
37
|
+
end
|
|
38
|
+
it "should create an ack frame for a message id" do
|
|
39
|
+
connection.ack_frame('m-5678').should be_an_onstomp_frame('ACK',
|
|
40
|
+
{:'message-id' => 'm-5678'}, nil)
|
|
41
|
+
end
|
|
42
|
+
it "should override the supplied message id with a 'message-id' header" do
|
|
43
|
+
connection.ack_frame('m-5678', {
|
|
44
|
+
:'message-id' => 'm-1234'}
|
|
45
|
+
).should be_an_onstomp_frame('ACK', {:'message-id' => 'm-1234'}, nil)
|
|
46
|
+
end
|
|
47
|
+
it "should raise an exception if a message-id cannot be inferred" do
|
|
48
|
+
lambda {
|
|
49
|
+
connection.ack_frame(nil, {:'message-id' => ''})
|
|
50
|
+
}.should raise_error(ArgumentError)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
|
|
4
|
+
module OnStomp::Connections
|
|
5
|
+
describe Stomp_1_1 do
|
|
6
|
+
let(:io) {
|
|
7
|
+
mock('io')
|
|
8
|
+
}
|
|
9
|
+
let(:client) {
|
|
10
|
+
mock('client')
|
|
11
|
+
}
|
|
12
|
+
let(:connection) {
|
|
13
|
+
Stomp_1_1.new(io, client)
|
|
14
|
+
}
|
|
15
|
+
describe "ancestors" do
|
|
16
|
+
it "should be a kind of Base connection" do
|
|
17
|
+
connection.should be_a_kind_of(OnStomp::Connections::Base)
|
|
18
|
+
end
|
|
19
|
+
it "should be a kind of Stomp_1 connection" do
|
|
20
|
+
connection.should be_a_kind_of(OnStomp::Connections::Stomp_1)
|
|
21
|
+
end
|
|
22
|
+
it "should be a kind of Heartbeating connection" do
|
|
23
|
+
connection.should be_a_kind_of(OnStomp::Connections::Heartbeating)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
describe ".serializer" do
|
|
28
|
+
it "should use a Stomp_1_1 serializer" do
|
|
29
|
+
connection.serializer.should be_a_kind_of(OnStomp::Connections::Serializers::Stomp_1_1)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe ".ack_frame" do
|
|
34
|
+
let(:message_frame) {
|
|
35
|
+
OnStomp::Components::Frame.new('MESSAGE',
|
|
36
|
+
:'message-id' => 'm-1234', :subscription => 's-5678')
|
|
37
|
+
}
|
|
38
|
+
it "should create an ACK frame for a MESSAGE frame" do
|
|
39
|
+
connection.ack_frame(message_frame).should be_an_onstomp_frame('ACK',
|
|
40
|
+
{:'message-id' => 'm-1234', :subscription => 's-5678'}, nil)
|
|
41
|
+
end
|
|
42
|
+
it "should create an ACK frame for a message id and subscription" do
|
|
43
|
+
connection.ack_frame('m-5678', 's-1234').should be_an_onstomp_frame('ACK',
|
|
44
|
+
{:'message-id' => 'm-5678', :subscription => 's-1234'}, nil)
|
|
45
|
+
end
|
|
46
|
+
it "should override the message id and subscription with headers" do
|
|
47
|
+
connection.ack_frame('m-5678', 's-1234', {
|
|
48
|
+
:'message-id' => 'm-1234', :subscription => 's-5678'}
|
|
49
|
+
).should be_an_onstomp_frame('ACK', {:'message-id' => 'm-1234',
|
|
50
|
+
:subscription => 's-5678'}, nil)
|
|
51
|
+
end
|
|
52
|
+
it "should raise an error if a message-id cannot be inferred" do
|
|
53
|
+
lambda {
|
|
54
|
+
connection.ack_frame(nil, {:'message-id' => '', :subscription => 's-1234'})
|
|
55
|
+
}.should raise_error(ArgumentError)
|
|
56
|
+
end
|
|
57
|
+
it "should raise an error if a subscription cannot be inferred" do
|
|
58
|
+
lambda {
|
|
59
|
+
connection.ack_frame('m-1234', nil, { :subscription => ''})
|
|
60
|
+
}.should raise_error(ArgumentError)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
describe ".connected?" do
|
|
65
|
+
it "should be connected if Base is connected and it has a pulse" do
|
|
66
|
+
io.stub(:closed? => false)
|
|
67
|
+
connection.stub(:pulse? => true)
|
|
68
|
+
connection.connected?.should be_true
|
|
69
|
+
end
|
|
70
|
+
it "should not be connected if Base is not connected" do
|
|
71
|
+
io.stub(:closed? => true)
|
|
72
|
+
connection.stub(:pulse? => true)
|
|
73
|
+
connection.connected?.should be_false
|
|
74
|
+
end
|
|
75
|
+
it "should not be connected if it has no pulse" do
|
|
76
|
+
io.stub(:closed? => false)
|
|
77
|
+
connection.stub(:pulse? => false)
|
|
78
|
+
connection.connected?.should be_false
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
describe ".configure" do
|
|
83
|
+
let(:client_beats) { mock('client beats') }
|
|
84
|
+
let(:broker_beats) { mock('broker beats') }
|
|
85
|
+
let(:connected_frame) {
|
|
86
|
+
mock('connected frame', :heart_beat => broker_beats).tap do |m|
|
|
87
|
+
m.stub(:header?).with(:version).and_return(true)
|
|
88
|
+
m.stub(:[]).with(:version).and_return('1.1')
|
|
89
|
+
end
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
it "should configure heartbeating" do
|
|
93
|
+
client.stub(:heartbeats => client_beats)
|
|
94
|
+
connection.should_receive(:configure_heartbeating).
|
|
95
|
+
with(client_beats, broker_beats)
|
|
96
|
+
connection.configure(connected_frame, {})
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
describe ".nack_frame" do
|
|
101
|
+
let(:message_frame) {
|
|
102
|
+
OnStomp::Components::Frame.new('MESSAGE',
|
|
103
|
+
:'message-id' => 'm-1234', :subscription => 's-5678')
|
|
104
|
+
}
|
|
105
|
+
it "should create a NACK frame for a MESSAGE frame" do
|
|
106
|
+
connection.nack_frame(message_frame).should be_an_onstomp_frame('NACK',
|
|
107
|
+
{:'message-id' => 'm-1234', :subscription => 's-5678'}, nil)
|
|
108
|
+
end
|
|
109
|
+
it "should create a NACK frame for a message id and subscription" do
|
|
110
|
+
connection.nack_frame('m-5678', 's-1234').should be_an_onstomp_frame('NACK',
|
|
111
|
+
{:'message-id' => 'm-5678', :subscription => 's-1234'}, nil)
|
|
112
|
+
end
|
|
113
|
+
it "should override the message id and subscription with headers" do
|
|
114
|
+
connection.nack_frame('m-5678', 's-1234', {
|
|
115
|
+
:'message-id' => 'm-1234', :subscription => 's-5678'}
|
|
116
|
+
).should be_an_onstomp_frame('NACK', {:'message-id' => 'm-1234',
|
|
117
|
+
:subscription => 's-5678'}, nil)
|
|
118
|
+
end
|
|
119
|
+
it "should raise an error if a message-id cannot be inferred" do
|
|
120
|
+
lambda {
|
|
121
|
+
connection.nack_frame(nil, {:'message-id' => '', :subscription => 's-1234'})
|
|
122
|
+
}.should raise_error(ArgumentError)
|
|
123
|
+
end
|
|
124
|
+
it "should raise an error if a subscription cannot be inferred" do
|
|
125
|
+
lambda {
|
|
126
|
+
connection.nack_frame('m-1234', nil, { :subscription => ''})
|
|
127
|
+
}.should raise_error(ArgumentError)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
describe ".heartbeat_frame" do
|
|
132
|
+
it "should create a heartbeat frame (frame with no command)" do
|
|
133
|
+
connection.heartbeat_frame.should be_an_onstomp_frame(nil, {}, nil)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
|
|
4
|
+
module OnStomp::Connections
|
|
5
|
+
describe Stomp_1 do
|
|
6
|
+
let(:connection) {
|
|
7
|
+
mock('connection').tap do |m|
|
|
8
|
+
m.extend Stomp_1
|
|
9
|
+
end
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe ".connect_frame" do
|
|
13
|
+
it "should build a CONNECT frame" do
|
|
14
|
+
connection.connect_frame({:header1 => 'value 1',
|
|
15
|
+
:header2 => 'value 2'}, {:header1 => '', :header2 => 'value 22',
|
|
16
|
+
:header3 => 'value 3'}
|
|
17
|
+
).should be_an_onstomp_frame('CONNECT', {:header1 => 'value 1',
|
|
18
|
+
:header2 => 'value 22', :header3 => 'value 3'}, nil)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe ".send_frame" do
|
|
23
|
+
it "should build a SEND frame" do
|
|
24
|
+
connection.send_frame('/queue/test', 'body of message',
|
|
25
|
+
{:header1 => 'value 1', :destination => '/queue/not-test'}
|
|
26
|
+
).should be_an_onstomp_frame('SEND', {:header1 => 'value 1',
|
|
27
|
+
:destination => '/queue/test'}, 'body of message')
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
describe ".begin_frame" do
|
|
32
|
+
it "should build a BEGIN frame" do
|
|
33
|
+
connection.begin_frame('tx-1234', {:transaction => 'tx-5678',
|
|
34
|
+
:header1 => 'value 1'}
|
|
35
|
+
).should be_an_onstomp_frame('BEGIN', {:header1 => 'value 1',
|
|
36
|
+
:transaction => 'tx-1234'}, nil)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
describe ".commit_frame" do
|
|
41
|
+
it "should build a COMMIT frame" do
|
|
42
|
+
connection.commit_frame('tx-1234', {:transaction => 'tx-5678',
|
|
43
|
+
:header1 => 'value 1'}
|
|
44
|
+
).should be_an_onstomp_frame('COMMIT', {:header1 => 'value 1',
|
|
45
|
+
:transaction => 'tx-1234'}, nil)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
describe ".abort_frame" do
|
|
50
|
+
it "should build an ABORT frame" do
|
|
51
|
+
connection.abort_frame('tx-1234', {:transaction => 'tx-5678',
|
|
52
|
+
:header1 => 'value 1'}
|
|
53
|
+
).should be_an_onstomp_frame('ABORT', {:header1 => 'value 1',
|
|
54
|
+
:transaction => 'tx-1234'}, nil)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
describe ".disconnect_frame" do
|
|
59
|
+
it "should build a DISCONNECT frame" do
|
|
60
|
+
connection.disconnect_frame({:receipt => 'r-5678',
|
|
61
|
+
:header1 => 'value 1'}
|
|
62
|
+
).should be_an_onstomp_frame('DISCONNECT', {:header1 => 'value 1',
|
|
63
|
+
:receipt => 'r-5678'}, nil)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
describe ".subscribe_frame" do
|
|
68
|
+
it "should automatically generate an 'id' header if one is not supplied" do
|
|
69
|
+
frame = connection.subscribe_frame('/queue/test', :ack => 'client',
|
|
70
|
+
:destination => '/queue/not-test')
|
|
71
|
+
frame.should be_an_onstomp_frame('SUBSCRIBE', {:ack => 'client',
|
|
72
|
+
:destination => '/queue/test'}, nil)
|
|
73
|
+
frame.header?(:id).should be_true
|
|
74
|
+
end
|
|
75
|
+
it "should build a SUBSCRIBE frame" do
|
|
76
|
+
connection.subscribe_frame('/queue/test', :ack => 'auto',
|
|
77
|
+
:destination => '/queue/not-test', :id => 's-1234'
|
|
78
|
+
).should be_an_onstomp_frame('SUBSCRIBE', {:ack => 'auto',
|
|
79
|
+
:destination => '/queue/test', :id => 's-1234'}, nil)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
describe ".unsubscribe_frame" do
|
|
84
|
+
it "should build an UNSUBSCRIBE frame from a SUBSCRIBE frame" do
|
|
85
|
+
subscribe_frame = connection.subscribe_frame('/queue/test',
|
|
86
|
+
:ack => 'auto',
|
|
87
|
+
:destination => '/queue/not-test', :id => 's-1234')
|
|
88
|
+
unsubscribe_frame = connection.unsubscribe_frame(subscribe_frame,
|
|
89
|
+
:header1 => 'value 1')
|
|
90
|
+
unsubscribe_frame.should be_an_onstomp_frame('UNSUBSCRIBE',
|
|
91
|
+
{:header1 => 'value 1', :id => 's-1234'}, nil)
|
|
92
|
+
end
|
|
93
|
+
it "should build an UNSUBSCRIBE frame from an id" do
|
|
94
|
+
connection.unsubscribe_frame('s-1234',
|
|
95
|
+
:header1 => 'value 1'
|
|
96
|
+
).should be_an_onstomp_frame('UNSUBSCRIBE',
|
|
97
|
+
{:header1 => 'value 1', :id => 's-1234'}, nil)
|
|
98
|
+
end
|
|
99
|
+
it "should use a supplied ID header over the supplied id" do
|
|
100
|
+
connection.unsubscribe_frame('s-1234',
|
|
101
|
+
:header1 => 'value 1', :id => 's-5678'
|
|
102
|
+
).should be_an_onstomp_frame('UNSUBSCRIBE',
|
|
103
|
+
{:header1 => 'value 1', :id => 's-5678'}, nil)
|
|
104
|
+
end
|
|
105
|
+
it "should raise an error if no id header can be inferred" do
|
|
106
|
+
lambda {
|
|
107
|
+
connection.unsubscribe_frame(nil,
|
|
108
|
+
:header1 => 'value 1', :id => '')
|
|
109
|
+
}.should raise_error(ArgumentError)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
|
|
4
|
+
module OnStomp
|
|
5
|
+
describe Connections do
|
|
6
|
+
describe ".supported" do
|
|
7
|
+
it "should be 1.0 and 1.1" do
|
|
8
|
+
Connections.supported.should == ['1.0', '1.1']
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe ".select_supported" do
|
|
13
|
+
it "should filter out any unsupported version" do
|
|
14
|
+
Connections.select_supported(['1.9', '1.0', '2.x']).should ==
|
|
15
|
+
['1.0']
|
|
16
|
+
end
|
|
17
|
+
it "should be empty if none are supported" do
|
|
18
|
+
Connections.select_supported(['1.5', '1.9', '1.x']).should be_empty
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe ".connect" do
|
|
23
|
+
let(:client_uri) { mock('uri', :host => 'host.domain.tld', :port => 10101) }
|
|
24
|
+
let(:stomp_1_0_class) { mock('stomp 1.0 class') }
|
|
25
|
+
let(:stomp_1_1_class) { mock('stomp 1.1 class') }
|
|
26
|
+
let(:stomp_1_0) {
|
|
27
|
+
mock('stomp 1.0', :is_a? => false).tap do |m|
|
|
28
|
+
m.stub(:is_a?).with(stomp_1_0_class).and_return(true)
|
|
29
|
+
end
|
|
30
|
+
}
|
|
31
|
+
let(:stomp_1_1) {
|
|
32
|
+
mock('stomp 1.1', :is_a? => false).tap do |m|
|
|
33
|
+
m.stub(:is_a?).with(stomp_1_1_class).and_return(true)
|
|
34
|
+
end
|
|
35
|
+
}
|
|
36
|
+
let(:tcp_socket) { mock('tcp socket') }
|
|
37
|
+
let(:ssl_socket) { mock('ssl socket', :sync_close= => nil, :connect => nil) }
|
|
38
|
+
let(:ssl_context) { mock('ssl context') }
|
|
39
|
+
let(:ssl_options) {
|
|
40
|
+
{
|
|
41
|
+
:post_connection_check => 'some hostname',
|
|
42
|
+
:ca_file => '/path/to/ca_file.pem',
|
|
43
|
+
:ca_path => '/path/to/ca_files/',
|
|
44
|
+
:cert => '/path/to/client/cert.pem',
|
|
45
|
+
:key => '/path/to/client/key.pem',
|
|
46
|
+
:verify_mode => 'super_duper_mode'
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
let(:connected_frame) { mock('CONNECTED frame') }
|
|
50
|
+
let(:client) {
|
|
51
|
+
mock('client', :ssl => nil, :uri => client_uri)
|
|
52
|
+
}
|
|
53
|
+
let(:user_headers) {
|
|
54
|
+
{}
|
|
55
|
+
}
|
|
56
|
+
let(:connect_headers) {
|
|
57
|
+
{}
|
|
58
|
+
}
|
|
59
|
+
let(:pend_events) { mock('pending events') }
|
|
60
|
+
before(:each) do
|
|
61
|
+
::TCPSocket.stub(:new => nil)
|
|
62
|
+
::OpenSSL::SSL::SSLSocket.stub(:new => nil)
|
|
63
|
+
Connections::PROTOCOL_VERSIONS['1.0'] = stomp_1_0_class
|
|
64
|
+
Connections::PROTOCOL_VERSIONS['1.1'] = stomp_1_1_class
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
describe "TCP connections" do
|
|
68
|
+
before(:each) do
|
|
69
|
+
::TCPSocket.should_receive(:new).with('host.domain.tld', 10101).and_return(tcp_socket)
|
|
70
|
+
stomp_1_0_class.should_receive(:new).with(tcp_socket, client).and_return(stomp_1_0)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "should create a 1.0 connection" do
|
|
74
|
+
stomp_1_0.should_receive(:connect).and_return(['1.0', connected_frame])
|
|
75
|
+
stomp_1_0.should_receive(:configure).with(connected_frame, pend_events)
|
|
76
|
+
Connections.connect client, user_headers, connect_headers, pend_events
|
|
77
|
+
end
|
|
78
|
+
it "should create a 1.1 connection" do
|
|
79
|
+
stomp_1_0.should_receive(:connect).and_return(['1.1', connected_frame])
|
|
80
|
+
stomp_1_0.should_receive(:socket).and_return(tcp_socket)
|
|
81
|
+
stomp_1_1_class.should_receive(:new).with(tcp_socket, client).and_return(stomp_1_1)
|
|
82
|
+
stomp_1_1.should_receive(:configure).with(connected_frame, pend_events)
|
|
83
|
+
Connections.connect client, user_headers, connect_headers, pend_events
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
describe "SSL connections" do
|
|
88
|
+
before(:each) do
|
|
89
|
+
::TCPSocket.should_receive(:new).with('host.domain.tld', 10101).and_return(tcp_socket)
|
|
90
|
+
::OpenSSL::SSL::SSLSocket.should_receive(:new).with(tcp_socket, ssl_context).and_return(ssl_socket)
|
|
91
|
+
::OpenSSL::SSL::SSLContext.should_receive(:new).and_return(ssl_context)
|
|
92
|
+
stomp_1_0_class.should_receive(:new).with(ssl_socket, client).and_return(stomp_1_0)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "should create a 1.0 connection" do
|
|
96
|
+
client.stub(:ssl => ssl_options)
|
|
97
|
+
stomp_1_0.should_receive(:connect).and_return(['1.0', connected_frame])
|
|
98
|
+
stomp_1_0.should_receive(:configure).with(connected_frame, pend_events)
|
|
99
|
+
ssl_options.each do |k, v|
|
|
100
|
+
next if k == :post_connection_check
|
|
101
|
+
ssl_context.should_receive(:"#{k}=").with(v)
|
|
102
|
+
end
|
|
103
|
+
ssl_socket.should_receive(:post_connection_check).with(ssl_options[:post_connection_check])
|
|
104
|
+
Connections.connect client, user_headers, connect_headers, pend_events
|
|
105
|
+
end
|
|
106
|
+
it "should create a 1.1 connection" do
|
|
107
|
+
client.stub(:ssl => nil)
|
|
108
|
+
client_uri.stub(:onstomp_socket_type => :ssl)
|
|
109
|
+
stomp_1_0.should_receive(:connect).and_return(['1.1', connected_frame])
|
|
110
|
+
Connections::DEFAULT_SSL_OPTIONS.each do |k, v|
|
|
111
|
+
next if k == :post_connection_check
|
|
112
|
+
ssl_context.should_receive(:"#{k}=").with(v)
|
|
113
|
+
end
|
|
114
|
+
ssl_socket.should_receive(:post_connection_check).with(client_uri.host)
|
|
115
|
+
stomp_1_0.should_receive(:socket).and_return(ssl_socket)
|
|
116
|
+
stomp_1_1_class.should_receive(:new).with(ssl_socket, client).and_return(stomp_1_1)
|
|
117
|
+
stomp_1_1.should_receive(:configure).with(connected_frame, pend_events)
|
|
118
|
+
Connections.connect client, user_headers, connect_headers, pend_events
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
describe "raising an OnStompError while negotiating connection" do
|
|
123
|
+
it "should close the base connection in a blocking fashion" do
|
|
124
|
+
Connections.stub(:create_connection).and_return(stomp_1_0)
|
|
125
|
+
stomp_1_0.stub(:connect => ['1.0', connected_frame])
|
|
126
|
+
Connections.stub(:negotiate_connection).and_raise OnStomp::OnStompError
|
|
127
|
+
stomp_1_0.should_receive(:close).with(true)
|
|
128
|
+
lambda {
|
|
129
|
+
Connections.connect client, user_headers, connect_headers, pend_events
|
|
130
|
+
}.should raise_error(OnStomp::OnStompError)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
|
|
4
|
+
module OnStomp::Interfaces
|
|
5
|
+
describe ClientEvents do
|
|
6
|
+
let(:connection) { mock('connection') }
|
|
7
|
+
let(:eventable) {
|
|
8
|
+
mock('eventable').tap do |m|
|
|
9
|
+
m.extend ClientEvents
|
|
10
|
+
end
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
describe "client frame events" do
|
|
14
|
+
[ :ack, :nack, :begin, :abort, :commit, :send, :subscribe,
|
|
15
|
+
:unsubscribe, :disconnect, :client_beat ].each do |ftype|
|
|
16
|
+
it "should provide an 'on_#{ftype}' event" do
|
|
17
|
+
frame = if ftype == :client_beat
|
|
18
|
+
OnStomp::Components::Frame.new
|
|
19
|
+
else
|
|
20
|
+
OnStomp::Components::Frame.new(ftype.to_s.upcase)
|
|
21
|
+
end
|
|
22
|
+
triggered = false
|
|
23
|
+
eventable.__send__(:"on_#{ftype}") { triggered = true }
|
|
24
|
+
eventable.trigger_after_transmitting frame
|
|
25
|
+
triggered.should be_true
|
|
26
|
+
end
|
|
27
|
+
it "should provide a 'before_#{ftype}' event" do
|
|
28
|
+
frame = if ftype == :client_beat
|
|
29
|
+
OnStomp::Components::Frame.new
|
|
30
|
+
else
|
|
31
|
+
OnStomp::Components::Frame.new(ftype.to_s.upcase)
|
|
32
|
+
end
|
|
33
|
+
triggered = false
|
|
34
|
+
eventable.__send__(:"before_#{ftype}") { triggered = true }
|
|
35
|
+
eventable.trigger_before_transmitting frame
|
|
36
|
+
triggered.should be_true
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe "broker frame events" do
|
|
42
|
+
[ :error, :message, :receipt, :broker_beat ].each do |ftype|
|
|
43
|
+
it "should provide an 'on_#{ftype}' event" do
|
|
44
|
+
frame = if ftype == :broker_beat
|
|
45
|
+
OnStomp::Components::Frame.new
|
|
46
|
+
else
|
|
47
|
+
OnStomp::Components::Frame.new(ftype.to_s.upcase)
|
|
48
|
+
end
|
|
49
|
+
triggered = false
|
|
50
|
+
eventable.__send__(:"on_#{ftype}") { triggered = true }
|
|
51
|
+
eventable.trigger_after_receiving frame
|
|
52
|
+
triggered.should be_true
|
|
53
|
+
end
|
|
54
|
+
it "should provide a 'before_#{ftype}' event" do
|
|
55
|
+
frame = if ftype == :broker_beat
|
|
56
|
+
OnStomp::Components::Frame.new
|
|
57
|
+
else
|
|
58
|
+
OnStomp::Components::Frame.new(ftype.to_s.upcase)
|
|
59
|
+
end
|
|
60
|
+
triggered = false
|
|
61
|
+
eventable.__send__(:"before_#{ftype}") { triggered = true }
|
|
62
|
+
eventable.trigger_before_receiving frame
|
|
63
|
+
triggered.should be_true
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
describe "frame io events" do
|
|
69
|
+
let(:frame) {
|
|
70
|
+
OnStomp::Components::Frame.new
|
|
71
|
+
}
|
|
72
|
+
[:transmitting, :receiving].each do |evtype|
|
|
73
|
+
it "should provide a 'before_#{evtype}' event" do
|
|
74
|
+
triggered = false
|
|
75
|
+
eventable.__send__(:"before_#{evtype}") { triggered = true }
|
|
76
|
+
eventable.__send__(:"trigger_before_#{evtype}", frame)
|
|
77
|
+
triggered.should be_true
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "should provide an 'after_#{evtype}' event" do
|
|
81
|
+
triggered = false
|
|
82
|
+
eventable.__send__(:"after_#{evtype}") { triggered = true }
|
|
83
|
+
eventable.__send__(:"trigger_after_#{evtype}", frame)
|
|
84
|
+
triggered.should be_true
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
describe "connection events" do
|
|
90
|
+
[:established, :terminated, :died, :closed].each do |ctype|
|
|
91
|
+
it "should store in pending connection events if there is no connection" do
|
|
92
|
+
triggered = nil
|
|
93
|
+
eventable.stub(:connection => nil)
|
|
94
|
+
eventable.__send__(:"on_connection_#{ctype}") { |ct| triggered = ct }
|
|
95
|
+
eventable.pending_connection_events[:"on_#{ctype}"].first.call(ctype)
|
|
96
|
+
triggered.should == ctype
|
|
97
|
+
end
|
|
98
|
+
it "should pass on to connection if there is a connection" do
|
|
99
|
+
triggered = nil
|
|
100
|
+
eventable.stub(:connection => connection)
|
|
101
|
+
connection.should_receive(:"on_#{ctype}").and_yield(ctype)
|
|
102
|
+
eventable.__send__(:"on_connection_#{ctype}") { |ct| triggered = ct }
|
|
103
|
+
triggered.should == ctype
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|