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,54 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
module Stomper::Frames
|
4
|
+
describe Headers do
|
5
|
+
before(:each) do
|
6
|
+
@headers = Headers.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should set a header by a string key, accessible by all" do
|
10
|
+
@test_value = "a test"
|
11
|
+
@headers['testing'] = @test_value
|
12
|
+
@headers['testing'].should == @test_value
|
13
|
+
@headers[:testing].should == @test_value
|
14
|
+
@headers.testing.should == @test_value
|
15
|
+
@headers.send(:testing).should == @test_value
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should set a header by a symbol key, accessible by all" do
|
19
|
+
@test_value = "another test"
|
20
|
+
@headers[:some_key] = @test_value
|
21
|
+
@headers['some_key'].should == @test_value
|
22
|
+
@headers[:some_key].should == @test_value
|
23
|
+
@headers.some_key.should == @test_value
|
24
|
+
@headers.send(:some_key).should == @test_value
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should set a header by a method, accessible by all" do
|
28
|
+
@test_value = "yet more testing"
|
29
|
+
@headers.another_key = @test_value
|
30
|
+
@headers['another_key'].should == @test_value
|
31
|
+
@headers[:another_key].should == @test_value
|
32
|
+
@headers.another_key.should == @test_value
|
33
|
+
@headers.send(:another_key).should == @test_value
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should override the default id getter and provide a setter" do
|
37
|
+
@test_value = "my id"
|
38
|
+
@headers.id = @test_value
|
39
|
+
@headers.id.should == @test_value
|
40
|
+
@headers['id'].should == @test_value
|
41
|
+
@headers[:id].should == @test_value
|
42
|
+
@headers.send(:id).should == @test_value
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should provide method to convert to stomp compatible headers" do
|
46
|
+
@headers.to_stomp.should be_empty
|
47
|
+
@headers.ack = 'auto'
|
48
|
+
@headers.destination = '/queue/test/1'
|
49
|
+
@headers.to_stomp.should == "ack:auto\ndestination:/queue/test/1\n"
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
module Stomper
|
4
|
+
module Frames
|
5
|
+
describe ServerFrame do
|
6
|
+
before(:each) do
|
7
|
+
@server_frame = ServerFrame.new("SERVER COMMAND")
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should provide a method for subclasses to register the command they handle" do
|
11
|
+
ServerFrame.should respond_to(:factory_for)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should build an appropriate object for a given server frame" do
|
15
|
+
@built_frame = ServerFrame.build("MESSAGE", {'message-id' => 'msg-001' ,'transaction' => 'tx-test', 'subscription' => 'sub-test'}, "message body")
|
16
|
+
@built_frame.should be_an_instance_of(Message)
|
17
|
+
@built_frame.headers.transaction.should == "tx-test"
|
18
|
+
@built_frame.headers.subscription.should == "sub-test"
|
19
|
+
@built_frame.headers[:'message-id'].should == "msg-001"
|
20
|
+
@built_frame.body.should == "message body"
|
21
|
+
@built_frame = ServerFrame.build("AN UNKNOWN COMMAND", {:a_header => "test"}, "a body")
|
22
|
+
@built_frame.should be_an_instance_of(ServerFrame)
|
23
|
+
@built_frame.command.should == "AN UNKNOWN COMMAND"
|
24
|
+
@built_frame.headers.a_header.should == "test"
|
25
|
+
@built_frame.body.should == "a body"
|
26
|
+
class MockServerFrame < ServerFrame
|
27
|
+
factory_for :testing
|
28
|
+
def initialize(headers, body)
|
29
|
+
super('TESTING', headers, body)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
@built_frame = ServerFrame.build("TESTING", {:a_header => "test"}, "a body")
|
33
|
+
@built_frame.should be_an_instance_of(MockServerFrame)
|
34
|
+
@built_frame.headers.a_header.should == "test"
|
35
|
+
@built_frame.body.should == "a body"
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "server frames" do
|
39
|
+
describe Connected do
|
40
|
+
it "should be registered" do
|
41
|
+
@server_frame = ServerFrame.build("CONNECTED", {:a_header => 'test'}, "test body")
|
42
|
+
@server_frame.should be_an_instance_of(Connected)
|
43
|
+
@server_frame.headers.a_header.should == "test"
|
44
|
+
@server_frame.body.should == "test body"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
describe Error do
|
48
|
+
it "should be registered" do
|
49
|
+
@server_frame = ServerFrame.build("ERROR", {:a_header => 'test'}, "test body")
|
50
|
+
@server_frame.should be_an_instance_of(Error)
|
51
|
+
@server_frame.headers.a_header.should == "test"
|
52
|
+
@server_frame.body.should == "test body"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
describe Message do
|
56
|
+
it "should be registered" do
|
57
|
+
@server_frame = ServerFrame.build("MESSAGE", {:a_header => 'test'}, "test body")
|
58
|
+
@server_frame.should be_an_instance_of(Message)
|
59
|
+
@server_frame.headers.a_header.should == "test"
|
60
|
+
@server_frame.body.should == "test body"
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should provide the convenience attributes" do
|
64
|
+
@message = Message.new({:destination => '/queue/testing', :subscription => 'sub-001', :'message-id' => 'msg-001'}, "test body")
|
65
|
+
@message.id.should == "msg-001"
|
66
|
+
@message.destination.should == "/queue/testing"
|
67
|
+
@message.subscription.should == "sub-001"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
describe Receipt do
|
71
|
+
it "should be registered" do
|
72
|
+
@server_frame = ServerFrame.build("RECEIPT", {:a_header => 'test'}, "test body")
|
73
|
+
@server_frame.should be_an_instance_of(Receipt)
|
74
|
+
@server_frame.headers.a_header.should == "test"
|
75
|
+
@server_frame.body.should == "test body"
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should provide the convenience attributes" do
|
79
|
+
@receipt = Receipt.new({:'receipt-id' => 'who the receipt is for'}, "test body")
|
80
|
+
@receipt.for.should == "who the receipt is for"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
shared_examples_for "All Client Connections" do
|
2
|
+
describe "connection initializers" do
|
3
|
+
describe "from uri" do
|
4
|
+
it "should accept the stomp:/// uri (no host specified)" do
|
5
|
+
lambda { @connection.class.new("stomp:///") }.should_not raise_error
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should accept a uri specifying just the host" do
|
9
|
+
lambda { @connection.class.new("stomp://localhost/") }.should_not raise_error
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should accept a uri specifying host and port" do
|
13
|
+
lambda { @connection.class.new("stomp://localhost:61613/") }.should_not raise_error
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should accept a uri specifying host, port and credentials" do
|
17
|
+
lambda { @connection.class.new("stomp://test_user:s3cr3tz@localhost:61613/") }.should_not raise_error
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should accept a uri specifying a secure connection" do
|
21
|
+
lambda { @connection.class.new("stomp+ssl://localhost") }.should_not raise_error
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should not accept a bogus URI" do
|
25
|
+
lambda { @connection.class.new("stomp://localhost:garbage") }.should raise_error
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "connection control" do
|
32
|
+
it "should provide the appropriate all connection control and status methods" do
|
33
|
+
@connection.should respond_to(:connect)
|
34
|
+
@connection.should respond_to(:disconnect)
|
35
|
+
@connection.should respond_to(:close)
|
36
|
+
@connection.should respond_to(:connected?)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should not report it is connected after close is called" do
|
40
|
+
@connection.connect
|
41
|
+
@connection.connected?.should be_true
|
42
|
+
@connection.close
|
43
|
+
@connection.connected?.should be_false
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should not report it is connected after disconnect is called" do
|
47
|
+
@connection.connect
|
48
|
+
@connection.connected?.should be_true
|
49
|
+
@connection.disconnect
|
50
|
+
@connection.connected?.should be_false
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "connection IO" do
|
55
|
+
it "should provide methods for receiving frames and writing frames" do
|
56
|
+
@connection.should respond_to(:transmit)
|
57
|
+
@connection.should respond_to(:receive)
|
58
|
+
end
|
59
|
+
it "should transmit frames" do
|
60
|
+
@connection.connect
|
61
|
+
@frame = nil
|
62
|
+
@connection.transmit(Stomper::Frames::Subscribe.new("/topic/test_topic"))
|
63
|
+
@connection.transmit(Stomper::Frames::Send.new("/topic/test_topic", "hello"))
|
64
|
+
@frame = @connection.receive while @frame.nil?
|
65
|
+
@frame.should be_an_instance_of(Stomper::Frames::Message)
|
66
|
+
@frame.body.should == "hello"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "secure connection" do
|
71
|
+
before(:each) do
|
72
|
+
@secure_connection = @connection.class.new("stomp+ssl:///")
|
73
|
+
end
|
74
|
+
it "should transmit frames" do
|
75
|
+
@connection.connect
|
76
|
+
@frame = nil
|
77
|
+
@connection.transmit(Stomper::Frames::Subscribe.new("/topic/test_topic"))
|
78
|
+
@connection.transmit(Stomper::Frames::Send.new("/topic/test_topic", "hello"))
|
79
|
+
@frame = @connection.receive while @frame.nil?
|
80
|
+
@frame.should be_an_instance_of(Stomper::Frames::Message)
|
81
|
+
@frame.body.should == "hello"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
2
|
+
|
3
|
+
module Stomper
|
4
|
+
describe Subscription do
|
5
|
+
before(:each) do
|
6
|
+
@subscription = Subscription.new("/queue/test/1", 'subscription-1')
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "basic expectations" do
|
10
|
+
it "should be constructable from a parameter list, providing sensible defaults for omitted arguments" do
|
11
|
+
@subscription.destination.should == "/queue/test/1"
|
12
|
+
@subscription.ack.should == :auto
|
13
|
+
@subscription.selector.should be_nil
|
14
|
+
|
15
|
+
@subscription = Subscription.new("/queue/test/2", 'subscription-1', 'client', 'a > 3')
|
16
|
+
@subscription.destination.should == "/queue/test/2"
|
17
|
+
@subscription.ack.should == :client
|
18
|
+
@subscription.selector.should == 'a > 3'
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should be constructable from a hash, providing sensible defaults for omitted arguments" do
|
22
|
+
@subscription = Subscription.new({ :destination => '/queue/test/2' })
|
23
|
+
@subscription.destination.should == "/queue/test/2"
|
24
|
+
@subscription.ack.should == :auto
|
25
|
+
|
26
|
+
@subscription = Subscription.new({:destination => "/queue/test/3", :ack => :client, :id => 'subscription-3', :selector => 'b < 10' })
|
27
|
+
@subscription.destination.should == "/queue/test/3"
|
28
|
+
@subscription.ack.should == :client
|
29
|
+
@subscription.id.should_not be_nil
|
30
|
+
@subscription.selector.should == "b < 10"
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should use the specified ID for subscribing and unsubscribing" do
|
34
|
+
@subscription.id.should_not be_nil
|
35
|
+
@subscription.id.should_not be_empty
|
36
|
+
@subscription.id.should == @subscription.to_subscribe.id
|
37
|
+
@subscription.id.should == @subscription.to_unsubscribe.id
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should provide a meaningful ID when the ack mode is not auto" do
|
41
|
+
@client_ack_subscription = Subscription.new("/queue/test/1",nil,'client')
|
42
|
+
@client_ack_subscription.id.should_not be_nil
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should provide a meaningful ID when the selector is specified" do
|
46
|
+
@selector_subscription = Subscription.new("/queue/test/1",nil,'auto',"a > 3.5")
|
47
|
+
@selector_subscription.id.should_not be_nil
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should provide a SUBSCRIBE frame" do
|
51
|
+
@subscription.should respond_to(:to_subscribe)
|
52
|
+
@frame = @subscription.to_subscribe
|
53
|
+
@frame.destination.should == "/queue/test/1"
|
54
|
+
@frame.id.should_not be_nil
|
55
|
+
@frame.ack.should == "auto"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "message acceptance" do
|
60
|
+
it "should accept a message without a subscription header only when the subscription is 'naive'" do
|
61
|
+
@matching_subscription_1 = Subscription.new("/queue/test/1")
|
62
|
+
@matching_subscription_2 = Subscription.new("/queue/test/1", nil, :auto)
|
63
|
+
@message = Stomper::Frames::Message.new({'destination' => '/queue/test/1'},"test message")
|
64
|
+
@subscription.accepts?(@message).should be_false
|
65
|
+
@matching_subscription_1.accepts?(@message).should be_true
|
66
|
+
@matching_subscription_2.accepts?(@message).should be_true
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should be able to accept messages by destination" do
|
70
|
+
@non_matching_subscription = Subscription.new("/queue/test/another")
|
71
|
+
@message = Stomper::Frames::Message.new({'destination' => '/queue/test/1', 'subscription' => @subscription.id},"test message")
|
72
|
+
@subscription.accepts?(@message).should be_true
|
73
|
+
@non_matching_subscription.accepts?(@message).should be_false
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should accept messages only if the same destination and subscription" do
|
77
|
+
@alternate_subscription = Subscription.new("/queue/test/1", 'subscription-2')
|
78
|
+
@message = Stomper::Frames::Message.new({'destination' => '/queue/test/1', 'subscription' => @subscription.id},"test message")
|
79
|
+
@subscription.accepts?(@message).should be_true
|
80
|
+
@alternate_subscription.accepts?(@message).should be_false
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should accept messages by the subscription id if the message has a subscription" do
|
84
|
+
@non_matching_subscription = Subscription.new("/queue/test/1")
|
85
|
+
@message = Stomper::Frames::Message.new({'destination' => '/queue/test/1', 'subscription' => @subscription.id},"test message")
|
86
|
+
@subscription.accepts?(@message).should be_true
|
87
|
+
@non_matching_subscription.accepts?(@message).should be_false
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should not accept messages with the same destination when its ack mode is not 'auto' and no subscription header was specified" do
|
91
|
+
@non_matching_subscription = Subscription.new("/queue/test/1", nil, 'client')
|
92
|
+
@message = Stomper::Frames::Message.new({'destination' => '/queue/test/1'},"test message")
|
93
|
+
@non_matching_subscription.accepts?(@message).should be_false
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should not accept messages with the same destination when it has a selector and no subscription header was specified" do
|
97
|
+
@non_matching_subscription = Subscription.new("/queue/test/1", nil, 'auto', 'rating > 3.0')
|
98
|
+
@message = Stomper::Frames::Message.new({'destination' => '/queue/test/1'},"test message")
|
99
|
+
@non_matching_subscription.accepts?(@message).should be_false
|
100
|
+
end
|
101
|
+
|
102
|
+
# Either insist that the #id method return something meaningful in non-trivial situations
|
103
|
+
# as part of the expected behavior of the interface, or change this test!
|
104
|
+
# We now insist on it, so this test is valid.
|
105
|
+
it "should accept messages when it has a selector and the subscription header was specified" do
|
106
|
+
@non_matching_subscription = Subscription.new("/queue/test/1", nil, 'auto', 'rating > 3.0')
|
107
|
+
# To test this without insisting that the #id field be equivalent to the subscribe frame's header
|
108
|
+
# go this way:
|
109
|
+
|
110
|
+
@message = Stomper::Frames::Message.new({'destination' => '/queue/test/1', 'subscription' => @non_matching_subscription.id},"test message")
|
111
|
+
@non_matching_subscription.accepts?(@message).should be_true
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe "message delivery" do
|
116
|
+
it "should call its callback when an applicable message arrives for its destination" do
|
117
|
+
called_back = false
|
118
|
+
@subscription_with_block = Subscription.new("/queue/test/1", 'subscription-test') do |msg|
|
119
|
+
called_back = (msg == @message)
|
120
|
+
end
|
121
|
+
@message = Stomper::Frames::Message.new({'destination' => '/queue/test/1', 'subscription' => @subscription_with_block.id},"test message")
|
122
|
+
@subscription_with_block.perform(@message)
|
123
|
+
called_back.should be_true
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should not call its callback when given a message for a different destination" do
|
127
|
+
called_back = false
|
128
|
+
@message = Stomper::Frames::Message.new({'destination' => '/queue/test/1'},"test message")
|
129
|
+
@subscription_with_block = Subscription.new("/queue/test/another", 'subscription-test') do |msg|
|
130
|
+
called_back = (msg == @message)
|
131
|
+
end
|
132
|
+
@subscription_with_block.perform(@message)
|
133
|
+
called_back.should be_false
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should call its callback when a message arrives for its subscription id" do
|
137
|
+
called_back = false
|
138
|
+
@subscription_with_block = Subscription.new("/queue/test/another", 'subscription-test') do |msg|
|
139
|
+
called_back = (msg == @message)
|
140
|
+
end
|
141
|
+
@message = Stomper::Frames::Message.new({'destination' => '/queue/test/1', 'subscription' => @subscription_with_block.id},"test message")
|
142
|
+
@subscription_with_block.perform(@message)
|
143
|
+
called_back.should be_true
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should not call its callback when a message arrives for its subscription id, even on the same " do
|
147
|
+
called_back = false
|
148
|
+
@message = Stomper::Frames::Message.new({'destination' => '/queue/test/1', 'subscription' => 'subscription-not-test'},"test message")
|
149
|
+
@subscription_with_block = Subscription.new("/queue/test/another", 'subscription-test') do |msg|
|
150
|
+
called_back = (msg == @message)
|
151
|
+
end
|
152
|
+
@subscription_with_block.perform(@message)
|
153
|
+
called_back.should be_false
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
2
|
+
|
3
|
+
module Stomper
|
4
|
+
describe Subscriptions do
|
5
|
+
before(:each) do
|
6
|
+
@subscriptions = Subscriptions.new
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "adding subscriptions" do
|
10
|
+
it "should add a subscription given a subscription object" do
|
11
|
+
@subscriptions << Subscription.new('/queue/test/3', 'subscription-3', nil, 'b < 10')
|
12
|
+
@subscriptions.size.should == 1
|
13
|
+
@subscriptions.first.destination.should == "/queue/test/3"
|
14
|
+
@subscriptions.first.ack.should == :auto
|
15
|
+
@subscriptions.first.selector.should == 'b < 10'
|
16
|
+
@subscriptions.first.id.should_not be_nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "enumerable" do
|
21
|
+
it "should provide an each method" do
|
22
|
+
@subscriptions.should respond_to(:each)
|
23
|
+
end
|
24
|
+
it "should be enumerable" do
|
25
|
+
@subscriptions.is_a?(Enumerable).should be_true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "thread safe" do
|
30
|
+
# How do you test this?
|
31
|
+
it "should synchronize adding subscriptions" do
|
32
|
+
@subscriptions << Subscription.new("/queue/test/1")
|
33
|
+
@subscriptions << Subscription.new("/queue/test/2")
|
34
|
+
@subscriptions << Subscription.new("/queue/test/3")
|
35
|
+
Thread.new { sleep(0.1); @subscriptions << Subscription.new("/queue/test/4") }
|
36
|
+
# In general, this next step should execute before the thread has a chance to
|
37
|
+
# but the sleep in the map should mean that our thread gets woken up before
|
38
|
+
# map finishes. However, destinations should NEVER contain "/queue/test/4"
|
39
|
+
# because map should be synchronized.
|
40
|
+
destinations = @subscriptions.map { |sub| sleep(0.1); sub.destination }
|
41
|
+
destinations.size.should == 3
|
42
|
+
destinations.should == ['/queue/test/1', '/queue/test/2', '/queue/test/3']
|
43
|
+
@subscriptions.size.should == 4
|
44
|
+
@subscriptions.last.destination.should == "/queue/test/4"
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should synchronize removing subscriptions" do
|
48
|
+
@subscriptions << Subscription.new("/queue/test/1")
|
49
|
+
@subscriptions << Subscription.new("/queue/test/2")
|
50
|
+
@subscriptions << Subscription.new("/queue/test/3")
|
51
|
+
Thread.new { sleep(0.1); @subscriptions.remove("/queue/test/1") }
|
52
|
+
# In general, this next step should execute before the thread has a chance to
|
53
|
+
# but the sleep in the map should mean that our thread gets woken up before
|
54
|
+
# map finishes. However, destinations should NEVER contain "/queue/test/4"
|
55
|
+
# because map should be synchronized.
|
56
|
+
destinations = @subscriptions.map { |sub| sleep(0.1); sub.destination }
|
57
|
+
destinations.size.should == 3
|
58
|
+
destinations.should == ['/queue/test/1', '/queue/test/2', '/queue/test/3']
|
59
|
+
@subscriptions.size.should == 2
|
60
|
+
@subscriptions.first.destination.should == "/queue/test/2"
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "message delivery" do
|
66
|
+
it "should deliver messages to matching subscriptions" do
|
67
|
+
received_1 = false
|
68
|
+
received_2 = false
|
69
|
+
received_3 = false
|
70
|
+
@subscriptions << Subscription.new("/queue/test/1") { |msg| received_1 = true }
|
71
|
+
@subscriptions << Subscription.new("/queue/test/1", 'subscription-2') { |msg| received_2 = true }
|
72
|
+
@subscriptions << Subscription.new("/queue/test/2") { |msg| received_3 = true }
|
73
|
+
@message = Stomper::Frames::Message.new({'destination' => '/queue/test/1'},"test message")
|
74
|
+
@subscriptions.perform(@message)
|
75
|
+
received_1.should be_true
|
76
|
+
received_2.should be_false
|
77
|
+
received_3.should be_false
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "unsubscribing" do
|
82
|
+
it "should remove all naive subscriptions when unsubscribing with a string destination" do
|
83
|
+
@removed_subscriptions = [Subscription.new("/queue/test/1"), Subscription.new("/queue/test/1")]
|
84
|
+
@removed_subscriptions.each { |sub| @subscriptions << sub }
|
85
|
+
@subscriptions << Subscription.new("/queue/test/2")
|
86
|
+
@subscriptions << Subscription.new("/queue/test/1", 'subscription-4')
|
87
|
+
@to_remove = @subscriptions.remove("/queue/test/1")
|
88
|
+
@subscriptions.size.should == 2
|
89
|
+
@subscriptions.should_not include(@removed_subscriptions.first)
|
90
|
+
@subscriptions.should_not include(@removed_subscriptions.last)
|
91
|
+
@to_remove.size.should == 2
|
92
|
+
@to_remove.should include(@removed_subscriptions.first)
|
93
|
+
@to_remove.should include(@removed_subscriptions.last)
|
94
|
+
end
|
95
|
+
it "should remove all subscriptions that accept messages for a supplied subscription ID" do
|
96
|
+
@removed_subscription = Subscription.new("/queue/test/1", 'subscription-4')
|
97
|
+
@subscriptions << Subscription.new("/queue/test/1")
|
98
|
+
@subscriptions << Subscription.new("/queue/test/1")
|
99
|
+
@subscriptions << Subscription.new("/queue/test/2")
|
100
|
+
@subscriptions << @removed_subscription
|
101
|
+
@to_remove = @subscriptions.remove(nil, @removed_subscription.id)
|
102
|
+
@subscriptions.size.should == 3
|
103
|
+
@subscriptions.should_not include(@removed_subscription)
|
104
|
+
@to_remove.size.should == 1
|
105
|
+
@to_remove.should include(@removed_subscription)
|
106
|
+
end
|
107
|
+
it "should remove subscriptions as expected when parameter is a hash specifying the ID" do
|
108
|
+
@removed_subscription = Subscription.new("/queue/test/1", 'subscription-4')
|
109
|
+
@subscriptions << Subscription.new("/queue/test/1")
|
110
|
+
@subscriptions << Subscription.new("/queue/test/1")
|
111
|
+
@subscriptions << Subscription.new("/queue/test/2")
|
112
|
+
@subscriptions << @removed_subscription
|
113
|
+
@to_remove = @subscriptions.remove({ :id => @removed_subscription.id })
|
114
|
+
@subscriptions.size.should == 3
|
115
|
+
@subscriptions.should_not include(@removed_subscription)
|
116
|
+
@to_remove.size.should == 1
|
117
|
+
@to_remove.should include(@removed_subscription)
|
118
|
+
end
|
119
|
+
it "should remove subscriptions as expected when parameter is a hash specifying the destination" do
|
120
|
+
@removed_subscriptions = [Subscription.new("/queue/test/1"), Subscription.new("/queue/test/1")]
|
121
|
+
@removed_subscriptions.each { |sub| @subscriptions << sub }
|
122
|
+
@subscriptions << Subscription.new("/queue/test/2")
|
123
|
+
@subscriptions << Subscription.new("/queue/test/1", 'subscription-4')
|
124
|
+
@to_remove = @subscriptions.remove( { :destination => "/queue/test/1" })
|
125
|
+
@subscriptions.size.should == 2
|
126
|
+
@subscriptions.should_not include(@removed_subscriptions.first)
|
127
|
+
@subscriptions.should_not include(@removed_subscriptions.last)
|
128
|
+
@to_remove.size.should == 2
|
129
|
+
@to_remove.should include(@removed_subscriptions.first)
|
130
|
+
@to_remove.should include(@removed_subscriptions.last)
|
131
|
+
end
|
132
|
+
it "should remove subscriptions as expected when parameter is a Subscription" do
|
133
|
+
@removed_subscription = Subscription.new("/queue/test/1", 'subscription-4')
|
134
|
+
@subscriptions << Subscription.new("/queue/test/1")
|
135
|
+
@subscriptions << Subscription.new("/queue/test/1")
|
136
|
+
@subscriptions << Subscription.new("/queue/test/2")
|
137
|
+
@subscriptions << @removed_subscription
|
138
|
+
@to_remove = @subscriptions.remove(@removed_subscription)
|
139
|
+
@to_remove.size.should == 1
|
140
|
+
@to_remove.should include(@removed_subscription)
|
141
|
+
@to_remove = @subscriptions.remove(Subscription.new("/queue/test/1", nil, :client))
|
142
|
+
@to_remove.should be_empty
|
143
|
+
@subscriptions.size.should == 3
|
144
|
+
@subscriptions.should_not include(@removed_subscription)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|