diaspora-vines 0.1.2

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 (174) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +3 -0
  3. data/LICENSE +19 -0
  4. data/README.md +7 -0
  5. data/Rakefile +23 -0
  6. data/bin/vines +4 -0
  7. data/conf/certs/README +39 -0
  8. data/conf/certs/ca-bundle.crt +3895 -0
  9. data/conf/config.rb +42 -0
  10. data/lib/vines/cli.rb +132 -0
  11. data/lib/vines/cluster/connection.rb +26 -0
  12. data/lib/vines/cluster/publisher.rb +55 -0
  13. data/lib/vines/cluster/pubsub.rb +92 -0
  14. data/lib/vines/cluster/sessions.rb +125 -0
  15. data/lib/vines/cluster/subscriber.rb +108 -0
  16. data/lib/vines/cluster.rb +246 -0
  17. data/lib/vines/command/bcrypt.rb +12 -0
  18. data/lib/vines/command/cert.rb +50 -0
  19. data/lib/vines/command/init.rb +68 -0
  20. data/lib/vines/command/ldap.rb +38 -0
  21. data/lib/vines/command/restart.rb +12 -0
  22. data/lib/vines/command/schema.rb +24 -0
  23. data/lib/vines/command/start.rb +28 -0
  24. data/lib/vines/command/stop.rb +18 -0
  25. data/lib/vines/config/host.rb +125 -0
  26. data/lib/vines/config/port.rb +132 -0
  27. data/lib/vines/config/pubsub.rb +108 -0
  28. data/lib/vines/config.rb +223 -0
  29. data/lib/vines/contact.rb +111 -0
  30. data/lib/vines/daemon.rb +78 -0
  31. data/lib/vines/error.rb +150 -0
  32. data/lib/vines/jid.rb +95 -0
  33. data/lib/vines/kit.rb +23 -0
  34. data/lib/vines/log.rb +24 -0
  35. data/lib/vines/router.rb +179 -0
  36. data/lib/vines/stanza/iq/auth.rb +18 -0
  37. data/lib/vines/stanza/iq/disco_info.rb +45 -0
  38. data/lib/vines/stanza/iq/disco_items.rb +29 -0
  39. data/lib/vines/stanza/iq/error.rb +16 -0
  40. data/lib/vines/stanza/iq/ping.rb +16 -0
  41. data/lib/vines/stanza/iq/private_storage.rb +83 -0
  42. data/lib/vines/stanza/iq/query.rb +10 -0
  43. data/lib/vines/stanza/iq/result.rb +16 -0
  44. data/lib/vines/stanza/iq/roster.rb +140 -0
  45. data/lib/vines/stanza/iq/session.rb +17 -0
  46. data/lib/vines/stanza/iq/vcard.rb +56 -0
  47. data/lib/vines/stanza/iq/version.rb +25 -0
  48. data/lib/vines/stanza/iq.rb +48 -0
  49. data/lib/vines/stanza/message.rb +40 -0
  50. data/lib/vines/stanza/presence/error.rb +23 -0
  51. data/lib/vines/stanza/presence/probe.rb +37 -0
  52. data/lib/vines/stanza/presence/subscribe.rb +42 -0
  53. data/lib/vines/stanza/presence/subscribed.rb +51 -0
  54. data/lib/vines/stanza/presence/unavailable.rb +15 -0
  55. data/lib/vines/stanza/presence/unsubscribe.rb +38 -0
  56. data/lib/vines/stanza/presence/unsubscribed.rb +38 -0
  57. data/lib/vines/stanza/presence.rb +141 -0
  58. data/lib/vines/stanza/pubsub/create.rb +39 -0
  59. data/lib/vines/stanza/pubsub/delete.rb +41 -0
  60. data/lib/vines/stanza/pubsub/publish.rb +66 -0
  61. data/lib/vines/stanza/pubsub/subscribe.rb +44 -0
  62. data/lib/vines/stanza/pubsub/unsubscribe.rb +30 -0
  63. data/lib/vines/stanza/pubsub.rb +22 -0
  64. data/lib/vines/stanza.rb +175 -0
  65. data/lib/vines/storage/ldap.rb +71 -0
  66. data/lib/vines/storage/local.rb +139 -0
  67. data/lib/vines/storage/null.rb +39 -0
  68. data/lib/vines/storage/sql.rb +138 -0
  69. data/lib/vines/storage.rb +239 -0
  70. data/lib/vines/store.rb +110 -0
  71. data/lib/vines/stream/client/auth.rb +74 -0
  72. data/lib/vines/stream/client/auth_restart.rb +29 -0
  73. data/lib/vines/stream/client/bind.rb +72 -0
  74. data/lib/vines/stream/client/bind_restart.rb +24 -0
  75. data/lib/vines/stream/client/closed.rb +13 -0
  76. data/lib/vines/stream/client/ready.rb +17 -0
  77. data/lib/vines/stream/client/session.rb +210 -0
  78. data/lib/vines/stream/client/start.rb +27 -0
  79. data/lib/vines/stream/client/tls.rb +38 -0
  80. data/lib/vines/stream/client.rb +84 -0
  81. data/lib/vines/stream/component/handshake.rb +26 -0
  82. data/lib/vines/stream/component/ready.rb +23 -0
  83. data/lib/vines/stream/component/start.rb +19 -0
  84. data/lib/vines/stream/component.rb +58 -0
  85. data/lib/vines/stream/http/auth.rb +22 -0
  86. data/lib/vines/stream/http/bind.rb +32 -0
  87. data/lib/vines/stream/http/bind_restart.rb +37 -0
  88. data/lib/vines/stream/http/ready.rb +29 -0
  89. data/lib/vines/stream/http/request.rb +172 -0
  90. data/lib/vines/stream/http/session.rb +120 -0
  91. data/lib/vines/stream/http/sessions.rb +65 -0
  92. data/lib/vines/stream/http/start.rb +23 -0
  93. data/lib/vines/stream/http.rb +157 -0
  94. data/lib/vines/stream/parser.rb +79 -0
  95. data/lib/vines/stream/sasl.rb +128 -0
  96. data/lib/vines/stream/server/auth.rb +13 -0
  97. data/lib/vines/stream/server/auth_restart.rb +13 -0
  98. data/lib/vines/stream/server/final_restart.rb +21 -0
  99. data/lib/vines/stream/server/outbound/auth.rb +31 -0
  100. data/lib/vines/stream/server/outbound/auth_restart.rb +20 -0
  101. data/lib/vines/stream/server/outbound/auth_result.rb +32 -0
  102. data/lib/vines/stream/server/outbound/final_features.rb +28 -0
  103. data/lib/vines/stream/server/outbound/final_restart.rb +20 -0
  104. data/lib/vines/stream/server/outbound/start.rb +20 -0
  105. data/lib/vines/stream/server/outbound/tls.rb +30 -0
  106. data/lib/vines/stream/server/outbound/tls_result.rb +34 -0
  107. data/lib/vines/stream/server/ready.rb +24 -0
  108. data/lib/vines/stream/server/start.rb +13 -0
  109. data/lib/vines/stream/server/tls.rb +13 -0
  110. data/lib/vines/stream/server.rb +150 -0
  111. data/lib/vines/stream/state.rb +60 -0
  112. data/lib/vines/stream.rb +247 -0
  113. data/lib/vines/token_bucket.rb +55 -0
  114. data/lib/vines/user.rb +123 -0
  115. data/lib/vines/version.rb +6 -0
  116. data/lib/vines/xmpp_server.rb +25 -0
  117. data/lib/vines.rb +203 -0
  118. data/test/cluster/publisher_test.rb +57 -0
  119. data/test/cluster/sessions_test.rb +47 -0
  120. data/test/cluster/subscriber_test.rb +109 -0
  121. data/test/config/host_test.rb +369 -0
  122. data/test/config/pubsub_test.rb +187 -0
  123. data/test/config_test.rb +732 -0
  124. data/test/contact_test.rb +102 -0
  125. data/test/error_test.rb +58 -0
  126. data/test/ext/nokogiri.rb +14 -0
  127. data/test/jid_test.rb +147 -0
  128. data/test/kit_test.rb +31 -0
  129. data/test/router_test.rb +243 -0
  130. data/test/stanza/iq/disco_info_test.rb +78 -0
  131. data/test/stanza/iq/disco_items_test.rb +49 -0
  132. data/test/stanza/iq/private_storage_test.rb +184 -0
  133. data/test/stanza/iq/roster_test.rb +229 -0
  134. data/test/stanza/iq/session_test.rb +25 -0
  135. data/test/stanza/iq/vcard_test.rb +146 -0
  136. data/test/stanza/iq/version_test.rb +64 -0
  137. data/test/stanza/iq_test.rb +70 -0
  138. data/test/stanza/message_test.rb +126 -0
  139. data/test/stanza/presence/probe_test.rb +50 -0
  140. data/test/stanza/presence/subscribe_test.rb +83 -0
  141. data/test/stanza/pubsub/create_test.rb +116 -0
  142. data/test/stanza/pubsub/delete_test.rb +169 -0
  143. data/test/stanza/pubsub/publish_test.rb +309 -0
  144. data/test/stanza/pubsub/subscribe_test.rb +205 -0
  145. data/test/stanza/pubsub/unsubscribe_test.rb +148 -0
  146. data/test/stanza_test.rb +85 -0
  147. data/test/storage/ldap_test.rb +201 -0
  148. data/test/storage/local_test.rb +59 -0
  149. data/test/storage/mock_redis.rb +97 -0
  150. data/test/storage/null_test.rb +29 -0
  151. data/test/storage/storage_tests.rb +182 -0
  152. data/test/storage_test.rb +85 -0
  153. data/test/store_test.rb +130 -0
  154. data/test/stream/client/auth_test.rb +137 -0
  155. data/test/stream/client/ready_test.rb +47 -0
  156. data/test/stream/client/session_test.rb +27 -0
  157. data/test/stream/component/handshake_test.rb +52 -0
  158. data/test/stream/component/ready_test.rb +103 -0
  159. data/test/stream/component/start_test.rb +39 -0
  160. data/test/stream/http/auth_test.rb +70 -0
  161. data/test/stream/http/ready_test.rb +86 -0
  162. data/test/stream/http/request_test.rb +209 -0
  163. data/test/stream/http/sessions_test.rb +49 -0
  164. data/test/stream/http/start_test.rb +50 -0
  165. data/test/stream/parser_test.rb +122 -0
  166. data/test/stream/sasl_test.rb +195 -0
  167. data/test/stream/server/auth_test.rb +61 -0
  168. data/test/stream/server/outbound/auth_test.rb +75 -0
  169. data/test/stream/server/ready_test.rb +98 -0
  170. data/test/test_helper.rb +42 -0
  171. data/test/token_bucket_test.rb +44 -0
  172. data/test/user_test.rb +96 -0
  173. data/vines.gemspec +30 -0
  174. metadata +387 -0
@@ -0,0 +1,86 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Stream::Http::Ready do
6
+ subject { Vines::Stream::Http::Ready.new(stream, nil) }
7
+ let(:stream) { MiniTest::Mock.new }
8
+ let(:alice) { Vines::User.new(jid: 'alice@wonderland.lit') }
9
+ let(:hatter) { Vines::User.new(jid: 'hatter@wonderland.lit') }
10
+ let(:config) do
11
+ Vines::Config.new do
12
+ host 'wonderland.lit' do
13
+ storage(:fs) { dir Dir.tmpdir }
14
+ end
15
+ end
16
+ end
17
+
18
+ it "raises when body element is missing" do
19
+ node = node('<presence type="unavailable"/>')
20
+ stream.expect :valid_session?, true, [nil]
21
+ -> { subject.node(node) }.must_raise Vines::StreamErrors::NotAuthorized
22
+ end
23
+
24
+ it "raises when namespace is missing" do
25
+ node = node('<body rid="42" sid="12"/>')
26
+ stream.expect :valid_session?, true, ['12']
27
+ -> { subject.node(node) }.must_raise Vines::StreamErrors::NotAuthorized
28
+ end
29
+
30
+ it "raises when rid attribute is missing" do
31
+ node = node('<body xmlns="http://jabber.org/protocol/httpbind" sid="12"/>')
32
+ stream.expect :valid_session?, true, ['12']
33
+ -> { subject.node(node) }.must_raise Vines::StreamErrors::NotAuthorized
34
+ end
35
+
36
+ it "raises when session id is invalid" do
37
+ stream.expect :valid_session?, false, ['12']
38
+ node = node('<body xmlns="http://jabber.org/protocol/httpbind" rid="42" sid="12"/>')
39
+ -> { subject.node(node) }.must_raise Vines::StreamErrors::NotAuthorized
40
+ stream.verify
41
+ end
42
+
43
+ it "processes when body element is empty" do
44
+ node = node('<body xmlns="http://jabber.org/protocol/httpbind" rid="42" sid="12"/>')
45
+ stream.expect :valid_session?, true, ['12']
46
+ stream.expect :parse_body, [], [node]
47
+ subject.node(node)
48
+ stream.verify
49
+ end
50
+
51
+ describe 'when receiving multiple stanzas in one body element' do
52
+ let(:recipient) { MiniTest::Mock.new }
53
+ let(:bogus) { node('<message type="bogus">raises stanza error</message>') }
54
+ let(:ok) { node('<message to="hatter@wonderland.lit">but processes this message</message>') }
55
+ let(:xml) { node(%Q{<body xmlns="http://jabber.org/protocol/httpbind" rid="42" sid="12">#{bogus}#{ok}</body>}) }
56
+ let(:raises) { Vines::Stanza.from_node(bogus, stream) }
57
+ let(:processes) { Vines::Stanza.from_node(ok, stream) }
58
+
59
+ before do
60
+ recipient.expect :user, hatter
61
+ recipient.expect :write, nil, [Vines::Stanza::Message]
62
+
63
+ stream.expect :valid_session?, true, ['12']
64
+ stream.expect :parse_body, [raises, processes], [xml]
65
+ stream.expect :error, nil, [Vines::StanzaErrors::BadRequest]
66
+ stream.expect :config, config
67
+ stream.expect :user, alice
68
+ stream.expect :connected_resources, [recipient], [hatter.jid]
69
+ end
70
+
71
+ it 'processes all stanzas' do
72
+ subject.node(xml)
73
+ stream.verify
74
+ recipient.verify
75
+ end
76
+ end
77
+
78
+ it "terminates the session" do
79
+ node = node('<body xmlns="http://jabber.org/protocol/httpbind" rid="42" sid="12" type="terminate"/>')
80
+ stream.expect :valid_session?, true, ['12']
81
+ stream.expect :parse_body, [], [node]
82
+ stream.expect :terminate, nil
83
+ subject.node(node)
84
+ stream.verify
85
+ end
86
+ end
@@ -0,0 +1,209 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Stream::Http::Request do
6
+ PASSWORD = File.expand_path('../passwords')
7
+ INDEX = File.expand_path('index.html')
8
+
9
+ before do
10
+ File.open(PASSWORD, 'w') {|f| f.puts '/etc/passwd contents' }
11
+ File.open(INDEX, 'w') {|f| f.puts 'index.html contents' }
12
+
13
+ @stream = MiniTest::Mock.new
14
+ @parser = MiniTest::Mock.new
15
+ @parser.expect(:headers, {'Content-Type' => 'text/html', 'Host' => 'wonderland.lit'})
16
+ @parser.expect(:http_method, 'GET')
17
+ @parser.expect(:request_path, '/blogs/12')
18
+ @parser.expect(:request_url, '/blogs/12?ok=true')
19
+ @parser.expect(:query_string, 'ok=true')
20
+ end
21
+
22
+ after do
23
+ File.delete(PASSWORD)
24
+ File.delete(INDEX)
25
+ end
26
+
27
+ describe 'initialize' do
28
+ it 'copies request info from parser' do
29
+ request = Vines::Stream::Http::Request.new(@stream, @parser, '<html></html>')
30
+ assert_equal request.headers, {'Content-Type' => 'text/html', 'Host' => 'wonderland.lit'}
31
+ assert_equal request.method, 'GET'
32
+ assert_equal request.path, '/blogs/12'
33
+ assert_equal request.url, '/blogs/12?ok=true'
34
+ assert_equal request.query, 'ok=true'
35
+ assert_equal request.body, '<html></html>'
36
+ assert @stream.verify
37
+ assert @parser.verify
38
+ end
39
+ end
40
+
41
+ describe 'reply_with_file' do
42
+ it 'returns 404 file not found' do
43
+ request = Vines::Stream::Http::Request.new(@stream, @parser, '<html></html>')
44
+ headers = [
45
+ "HTTP/1.1 404 Not Found",
46
+ "Content-Length: 0"
47
+ ].join("\r\n")
48
+
49
+ @stream.expect(:stream_write, nil, ["#{headers}\r\n\r\n"])
50
+
51
+ request.reply_with_file(Dir.pwd)
52
+ assert @stream.verify
53
+ assert @parser.verify
54
+ end
55
+
56
+ it 'prevents directory traversal with 404 response' do
57
+ parser = MiniTest::Mock.new
58
+ parser.expect(:headers, {'Content-Type' => 'text/html', 'Host' => 'wonderland.lit'})
59
+ parser.expect(:http_method, 'GET')
60
+ parser.expect(:request_path, '/../passwords')
61
+ parser.expect(:request_url, '/../passwords')
62
+ parser.expect(:query_string, '')
63
+
64
+ request = Vines::Stream::Http::Request.new(@stream, parser, '<html></html>')
65
+
66
+ headers = [
67
+ "HTTP/1.1 404 Not Found",
68
+ "Content-Length: 0"
69
+ ].join("\r\n")
70
+
71
+ @stream.expect(:stream_write, nil, ["#{headers}\r\n\r\n"])
72
+
73
+ request.reply_with_file(Dir.pwd)
74
+ assert @stream.verify
75
+ assert parser.verify
76
+ end
77
+
78
+ it 'serves index.html for directory request' do
79
+ parser = MiniTest::Mock.new
80
+ parser.expect(:headers, {'Content-Type' => 'text/html', 'Host' => 'wonderland.lit'})
81
+ parser.expect(:http_method, 'GET')
82
+ parser.expect(:request_path, '/')
83
+ parser.expect(:request_url, '/?ok=true')
84
+ parser.expect(:query_string, 'ok=true')
85
+
86
+ request = Vines::Stream::Http::Request.new(@stream, parser, '<html></html>')
87
+
88
+ mtime = File.mtime(INDEX).utc.strftime('%a, %d %b %Y %H:%M:%S GMT')
89
+ headers = [
90
+ "HTTP/1.1 200 OK",
91
+ 'Content-Type: text/html; charset="utf-8"',
92
+ "Content-Length: 20",
93
+ "Last-Modified: #{mtime}"
94
+ ].join("\r\n")
95
+
96
+ @stream.expect(:stream_write, nil, ["#{headers}\r\n\r\n"])
97
+ @stream.expect(:stream_write, nil, ["index.html contents\n"])
98
+
99
+ request.reply_with_file(Dir.pwd)
100
+ assert @stream.verify
101
+ assert parser.verify
102
+ end
103
+
104
+ it 'redirects for missing trailing slash' do
105
+ parser = MiniTest::Mock.new
106
+ parser.expect(:headers, {'Content-Type' => 'text/html', 'Host' => 'wonderland.lit'})
107
+ parser.expect(:http_method, 'GET')
108
+ parser.expect(:request_path, '/http')
109
+ parser.expect(:request_url, '/http?ok=true')
110
+ parser.expect(:query_string, 'ok=true')
111
+
112
+ request = Vines::Stream::Http::Request.new(@stream, parser, '<html></html>')
113
+
114
+ headers = [
115
+ "HTTP/1.1 301 Moved Permanently",
116
+ "Content-Length: 0",
117
+ "Location: http://wonderland.lit/http/?ok=true"
118
+ ].join("\r\n")
119
+
120
+ @stream.expect(:stream_write, nil, ["#{headers}\r\n\r\n"])
121
+ # so the /http url above will work
122
+ request.reply_with_file(File.expand_path('../../', __FILE__))
123
+ assert @stream.verify
124
+ assert parser.verify
125
+ end
126
+ end
127
+
128
+ describe 'reply_to_options' do
129
+ it 'returns cors headers' do
130
+ parser = MiniTest::Mock.new
131
+ parser.expect(:headers, {
132
+ 'Content-Type' => 'text/xml',
133
+ 'Host' => 'wonderland.lit',
134
+ 'Origin' => 'remote.wonderland.lit',
135
+ 'Access-Control-Request-Headers' => 'Content-Type, Origin'})
136
+ parser.expect(:http_method, 'OPTIONS')
137
+ parser.expect(:request_path, '/xmpp')
138
+ parser.expect(:request_url, '/xmpp')
139
+ parser.expect(:query_string, '')
140
+
141
+ request = Vines::Stream::Http::Request.new(@stream, parser, '')
142
+
143
+ headers = [
144
+ "HTTP/1.1 200 OK",
145
+ "Content-Length: 0",
146
+ "Access-Control-Allow-Origin: *",
147
+ "Access-Control-Allow-Methods: POST, GET, OPTIONS",
148
+ "Access-Control-Allow-Headers: Content-Type, Origin",
149
+ "Access-Control-Max-Age: 2592000"
150
+ ].join("\r\n")
151
+
152
+ @stream.expect(:stream_write, nil, ["#{headers}\r\n\r\n"])
153
+ request.reply_to_options
154
+ assert @stream.verify
155
+ assert parser.verify
156
+ end
157
+ end
158
+
159
+ describe 'reply' do
160
+ it 'returns set-cookie header when vroute is defined' do
161
+ reply_with_cookie('v1')
162
+ end
163
+
164
+ it 'does not return set-cookie header when vroute is undefined' do
165
+ reply_with_cookie('')
166
+ end
167
+ end
168
+
169
+ private
170
+
171
+ def reply_with_cookie(cookie)
172
+ config = Vines::Config.new do
173
+ host 'wonderland.lit' do
174
+ storage(:fs) { dir Dir.tmpdir }
175
+ end
176
+ http '0.0.0.0', 5280 do
177
+ vroute cookie
178
+ end
179
+ end
180
+
181
+ parser = MiniTest::Mock.new
182
+ parser.expect(:headers, {
183
+ 'Content-Type' => 'text/xml',
184
+ 'Host' => 'wonderland.lit',
185
+ 'Origin' => 'remote.wonderland.lit'})
186
+ parser.expect(:http_method, 'POST')
187
+ parser.expect(:request_path, '/xmpp')
188
+ parser.expect(:request_url, '/xmpp')
189
+ parser.expect(:query_string, '')
190
+
191
+ request = Vines::Stream::Http::Request.new(@stream, parser, '')
192
+ message = '<message>hello</message>'
193
+
194
+ headers = [
195
+ "HTTP/1.1 200 OK",
196
+ "Access-Control-Allow-Origin: *",
197
+ "Content-Type: application/xml",
198
+ "Content-Length: 24",
199
+ ]
200
+ headers << "Set-Cookie: vroute=#{cookie}; path=/; HttpOnly" unless cookie.empty?
201
+ headers = headers.join("\r\n")
202
+
203
+ @stream.expect(:stream_write, nil, ["#{headers}\r\n\r\n#{message}"])
204
+ @stream.expect(:config, config)
205
+ request.reply(message, 'application/xml')
206
+ assert @stream.verify
207
+ assert parser.verify
208
+ end
209
+ end
@@ -0,0 +1,49 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Stream::Http::Sessions do
6
+ class MockSessions < Vines::Stream::Http::Sessions
7
+ def start_timer
8
+ # do nothing
9
+ end
10
+ end
11
+
12
+ def setup
13
+ @sessions = MockSessions.new
14
+ end
15
+
16
+ def test_session_add_and_delete
17
+ session = "session"
18
+ assert_nil @sessions['42']
19
+ @sessions['42'] = session
20
+ assert_equal session, @sessions['42']
21
+ @sessions.delete('42')
22
+ assert_nil @sessions['42']
23
+ end
24
+
25
+ def test_access_singleton_through_class_methods
26
+ session = "session"
27
+ assert_nil MockSessions['42']
28
+ MockSessions['42'] = session
29
+ assert_equal session, MockSessions['42']
30
+ MockSessions.delete('42')
31
+ assert_nil MockSessions['42']
32
+ end
33
+
34
+ def test_cleanup
35
+ live = MiniTest::Mock.new
36
+ live.expect(:expired?, false)
37
+
38
+ dead = MiniTest::Mock.new
39
+ dead.expect(:expired?, true)
40
+ dead.expect(:close, nil)
41
+
42
+ @sessions['live'] = live
43
+ @sessions['dead'] = dead
44
+
45
+ @sessions.send(:cleanup)
46
+ assert live.verify
47
+ assert dead.verify
48
+ end
49
+ end
@@ -0,0 +1,50 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Stream::Http::Start do
6
+ before do
7
+ @stream = MiniTest::Mock.new
8
+ @state = Vines::Stream::Http::Start.new(@stream)
9
+ end
10
+
11
+ def test_missing_body_raises_error
12
+ node = node('<presence type="unavailable"/>')
13
+ assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
14
+ end
15
+
16
+ def test_body_with_missing_namespace_raises_error
17
+ node = node('<body rid="42" sid="12"/>')
18
+ assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
19
+ end
20
+
21
+ def test_missing_session_starts_stream
22
+ EM.run do
23
+ node = node('<body xmlns="http://jabber.org/protocol/httpbind" rid="42" sid="12"/>')
24
+ @stream.expect(:start, nil, [node])
25
+ @stream.expect(:advance, nil, [Vines::Stream::Http::Auth.new(@stream)])
26
+ @state.node(node)
27
+ assert @stream.verify
28
+ EM.stop
29
+ end
30
+ end
31
+
32
+ def test_valid_session_resumes_stream
33
+ EM.run do
34
+ node = node('<body xmlns="http://jabber.org/protocol/httpbind" rid="42" sid="123"/>')
35
+ session = MiniTest::Mock.new
36
+ session.expect(:resume, nil, [@stream, node])
37
+ Vines::Stream::Http::Sessions['123'] = session
38
+ @state.node(node)
39
+ assert @stream.verify
40
+ assert session.verify
41
+ EM.stop
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def node(xml)
48
+ Nokogiri::XML(xml).root
49
+ end
50
+ end
@@ -0,0 +1,122 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ describe Vines::Stream::Parser do
6
+ STREAM_START = '<stream:stream to="wonderland.lit" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams">'.freeze
7
+
8
+ before do
9
+ @events = []
10
+ @parser = Vines::Stream::Parser.new.tap do |p|
11
+ p.stream_open {|el| @events << el }
12
+ p.stream_close { @events << :close }
13
+ p.stanza {|el| @events << el }
14
+ end
15
+ end
16
+
17
+ def test_xpath_to_subclass
18
+ expected = []
19
+ stanzas = [
20
+ ['<message></message>', Vines::Stanza::Message],
21
+ ['<presence/>', Vines::Stanza::Presence],
22
+ ['<presence type="bogus"/>', Vines::Stanza::Presence],
23
+ ['<presence type="error"/>', Vines::Stanza::Presence::Error],
24
+ ['<presence type="probe"/>', Vines::Stanza::Presence::Probe],
25
+ ['<presence type="subscribe"/>', Vines::Stanza::Presence::Subscribe],
26
+ ['<presence type="subscribed"/>', Vines::Stanza::Presence::Subscribed],
27
+ ['<presence type="unavailable"/>', Vines::Stanza::Presence::Unavailable],
28
+ ['<presence type="unsubscribe"/>', Vines::Stanza::Presence::Unsubscribe],
29
+ ['<presence type="unsubscribed"/>', Vines::Stanza::Presence::Unsubscribed],
30
+ ['<iq id="42" type="get"><query xmlns="http://jabber.org/protocol/disco#info"></query></iq>', Vines::Stanza::Iq::Query::DiscoInfo],
31
+ ['<iq id="42" type="get"><query xmlns="http://jabber.org/protocol/disco#items"></query></iq>', Vines::Stanza::Iq::Query::DiscoItems],
32
+ ['<iq id="42" type="error"></iq>', Vines::Stanza::Iq::Error],
33
+ ['<iq id="42" type="get"><query xmlns="jabber:iq:private"/></iq>', Vines::Stanza::Iq::PrivateStorage],
34
+ ['<iq id="42" type="set"><query xmlns="jabber:iq:private"/></iq>', Vines::Stanza::Iq::PrivateStorage],
35
+ ['<iq id="42" type="get"><ping xmlns="urn:xmpp:ping"/></iq>', Vines::Stanza::Iq::Ping],
36
+ ['<iq id="42" type="result"></iq>', Vines::Stanza::Iq::Result],
37
+ ['<iq id="42" type="get"><query xmlns="jabber:iq:roster"/></iq>', Vines::Stanza::Iq::Query::Roster],
38
+ ['<iq id="42" type="set"><query xmlns="jabber:iq:roster"/></iq>', Vines::Stanza::Iq::Query::Roster],
39
+ ['<iq id="42" type="set"><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></iq>', Vines::Stanza::Iq::Session],
40
+ ['<iq id="42" type="get"><vCard xmlns="vcard-temp"/></iq>', Vines::Stanza::Iq::Vcard],
41
+ ['<iq type="get"><vCard xmlns="vcard-temp"/></iq>', Vines::Stanza::Iq],
42
+ ['<iq id="42"><vCard xmlns="vcard-temp"/></iq>', Vines::Stanza::Iq],
43
+ ['<iq><vCard xmlns="vcard-temp"/></iq>', Vines::Stanza::Iq],
44
+ ['<bogus/>', NilClass],
45
+ ]
46
+ @parser << STREAM_START
47
+ stanzas.each do |stanza, klass|
48
+ @parser << stanza
49
+ expected << klass
50
+ end
51
+ @parser << '</stream:stream>'
52
+ assert_equal 'stream', @events.shift.name
53
+ assert_equal :close, @events.pop
54
+ assert_equal expected.size, @events.size
55
+ @events.each_with_index do |ev, ix|
56
+ assert_equal expected[ix], Vines::Stanza.from_node(ev, nil).class
57
+ end
58
+ end
59
+
60
+ def test_stream_namespace_with_default_prefix
61
+ @parser << STREAM_START
62
+ assert_equal 1, @events.size
63
+ stream = @events.shift
64
+ assert_equal 'stream', stream.name
65
+ refute_nil stream.namespace
66
+ assert_equal 'stream', stream.namespace.prefix
67
+ assert_equal 'http://etherx.jabber.org/streams', stream.namespace.href
68
+ expected = {'xmlns' => 'jabber:client', 'xmlns:stream' => 'http://etherx.jabber.org/streams'}
69
+ assert_equal expected, stream.namespaces
70
+ end
71
+
72
+ def test_stanzas_ignore_default_namespace
73
+ @parser << STREAM_START
74
+ @parser << '<message to="alice@wonderland.lit">hello!</message>'
75
+ assert_equal 2, @events.size
76
+ @events.shift # discard stream
77
+ msg = @events.shift
78
+ assert_equal 'message', msg.name
79
+ assert msg.namespaces.empty?
80
+ assert_nil msg.namespace
81
+ end
82
+
83
+ def test_nested_elements_have_namespace
84
+ @parser << STREAM_START
85
+ @parser << %q{
86
+ <iq from='alice@wonderland.lit/tea' id='42' type='set'>
87
+ <query xmlns='jabber:iq:roster'>
88
+ <item jid='hatter@wonderland.lit' name='Mad Hatter'>
89
+ <group>Tea Party</group>
90
+ </item>
91
+ </query>
92
+ </iq>
93
+ }
94
+ assert_equal 2, @events.size
95
+ @events.shift # discard stream
96
+ iq = @events.shift
97
+ assert_equal 'iq', iq.name
98
+ assert iq.namespaces.empty?
99
+ assert_nil iq.namespace
100
+
101
+ query = iq.elements.first
102
+ refute_nil query.namespace
103
+ assert_nil query.namespace.prefix
104
+ assert_equal 'jabber:iq:roster', query.namespace.href
105
+ expected = {'xmlns' => 'jabber:iq:roster'}
106
+ assert_equal expected, query.namespaces
107
+ end
108
+
109
+ def test_error_stanzas_have_stream_namespace
110
+ @parser << STREAM_START
111
+ @parser << '<stream:error><not-well-formed xmlns="urn:ietf:params:xml:ns:xmpp-streams"/></stream:error>'
112
+ assert_equal 2, @events.size
113
+ @events.shift # discard stream
114
+ error = @events.shift
115
+ assert_equal 'error', error.name
116
+ refute_nil error.namespace
117
+ assert_equal 'stream', error.namespace.prefix
118
+ assert_equal 'http://etherx.jabber.org/streams', error.namespace.href
119
+ expected = {'xmlns:stream' => 'http://etherx.jabber.org/streams'}
120
+ assert_equal expected, error.namespaces
121
+ end
122
+ end