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.
Files changed (80) hide show
  1. data/.autotest +2 -0
  2. data/.gitignore +6 -0
  3. data/.rspec +2 -0
  4. data/.yardopts +5 -0
  5. data/CHANGELOG.md +4 -0
  6. data/DeveloperNarrative.md +15 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.md +221 -0
  9. data/README.md +73 -0
  10. data/Rakefile +6 -0
  11. data/UserNarrative.md +8 -0
  12. data/examples/basic.rb +40 -0
  13. data/examples/events.rb +72 -0
  14. data/lib/onstomp/client.rb +152 -0
  15. data/lib/onstomp/components/frame.rb +108 -0
  16. data/lib/onstomp/components/frame_headers.rb +212 -0
  17. data/lib/onstomp/components/nil_processor.rb +20 -0
  18. data/lib/onstomp/components/scopes/header_scope.rb +25 -0
  19. data/lib/onstomp/components/scopes/receipt_scope.rb +25 -0
  20. data/lib/onstomp/components/scopes/transaction_scope.rb +191 -0
  21. data/lib/onstomp/components/scopes.rb +45 -0
  22. data/lib/onstomp/components/subscription.rb +30 -0
  23. data/lib/onstomp/components/threaded_processor.rb +62 -0
  24. data/lib/onstomp/components/uri.rb +30 -0
  25. data/lib/onstomp/components.rb +13 -0
  26. data/lib/onstomp/connections/base.rb +208 -0
  27. data/lib/onstomp/connections/heartbeating.rb +82 -0
  28. data/lib/onstomp/connections/serializers/stomp_1.rb +166 -0
  29. data/lib/onstomp/connections/serializers/stomp_1_0.rb +41 -0
  30. data/lib/onstomp/connections/serializers/stomp_1_1.rb +134 -0
  31. data/lib/onstomp/connections/serializers.rb +9 -0
  32. data/lib/onstomp/connections/stomp_1.rb +69 -0
  33. data/lib/onstomp/connections/stomp_1_0.rb +28 -0
  34. data/lib/onstomp/connections/stomp_1_1.rb +65 -0
  35. data/lib/onstomp/connections.rb +119 -0
  36. data/lib/onstomp/interfaces/client_configurable.rb +55 -0
  37. data/lib/onstomp/interfaces/client_events.rb +168 -0
  38. data/lib/onstomp/interfaces/connection_events.rb +62 -0
  39. data/lib/onstomp/interfaces/event_manager.rb +69 -0
  40. data/lib/onstomp/interfaces/frame_methods.rb +190 -0
  41. data/lib/onstomp/interfaces/receipt_manager.rb +33 -0
  42. data/lib/onstomp/interfaces/subscription_manager.rb +48 -0
  43. data/lib/onstomp/interfaces/uri_configurable.rb +106 -0
  44. data/lib/onstomp/interfaces.rb +14 -0
  45. data/lib/onstomp/version.rb +13 -0
  46. data/lib/onstomp.rb +147 -0
  47. data/onstomp.gemspec +29 -0
  48. data/spec/onstomp/client_spec.rb +265 -0
  49. data/spec/onstomp/components/frame_headers_spec.rb +163 -0
  50. data/spec/onstomp/components/frame_spec.rb +144 -0
  51. data/spec/onstomp/components/nil_processor_spec.rb +32 -0
  52. data/spec/onstomp/components/scopes/header_scope_spec.rb +27 -0
  53. data/spec/onstomp/components/scopes/receipt_scope_spec.rb +33 -0
  54. data/spec/onstomp/components/scopes/transaction_scope_spec.rb +227 -0
  55. data/spec/onstomp/components/scopes_spec.rb +63 -0
  56. data/spec/onstomp/components/subscription_spec.rb +58 -0
  57. data/spec/onstomp/components/threaded_processor_spec.rb +92 -0
  58. data/spec/onstomp/components/uri_spec.rb +33 -0
  59. data/spec/onstomp/connections/base_spec.rb +349 -0
  60. data/spec/onstomp/connections/heartbeating_spec.rb +132 -0
  61. data/spec/onstomp/connections/serializers/stomp_1_0_spec.rb +50 -0
  62. data/spec/onstomp/connections/serializers/stomp_1_1_spec.rb +99 -0
  63. data/spec/onstomp/connections/serializers/stomp_1_spec.rb +104 -0
  64. data/spec/onstomp/connections/stomp_1_0_spec.rb +54 -0
  65. data/spec/onstomp/connections/stomp_1_1_spec.rb +137 -0
  66. data/spec/onstomp/connections/stomp_1_spec.rb +113 -0
  67. data/spec/onstomp/connections_spec.rb +135 -0
  68. data/spec/onstomp/interfaces/client_events_spec.rb +108 -0
  69. data/spec/onstomp/interfaces/connection_events_spec.rb +55 -0
  70. data/spec/onstomp/interfaces/event_manager_spec.rb +72 -0
  71. data/spec/onstomp/interfaces/frame_methods_spec.rb +109 -0
  72. data/spec/onstomp/interfaces/receipt_manager_spec.rb +53 -0
  73. data/spec/onstomp/interfaces/subscription_manager_spec.rb +64 -0
  74. data/spec/onstomp_spec.rb +15 -0
  75. data/spec/spec_helper.rb +12 -0
  76. data/spec/support/custom_argument_matchers.rb +51 -0
  77. data/spec/support/frame_matchers.rb +88 -0
  78. data/spec/support/shared_frame_method_examples.rb +116 -0
  79. data/yard_extensions.rb +32 -0
  80. 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