tp-blather 0.8.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.
- data/.autotest +13 -0
- data/.gemtest +0 -0
- data/.gitignore +19 -0
- data/.rspec +3 -0
- data/.travis.yml +8 -0
- data/CHANGELOG.md +249 -0
- data/Gemfile +4 -0
- data/Guardfile +5 -0
- data/LICENSE +22 -0
- data/README.md +413 -0
- data/Rakefile +20 -0
- data/TODO.md +2 -0
- data/blather.gemspec +51 -0
- data/examples/certs/README +20 -0
- data/examples/certs/ca-bundle.crt +3987 -0
- data/examples/echo.rb +19 -0
- data/examples/execute.rb +17 -0
- data/examples/ping_pong.rb +38 -0
- data/examples/print_hierarchy.rb +77 -0
- data/examples/rosterprint.rb +15 -0
- data/examples/stream_only.rb +28 -0
- data/examples/trusted_echo.rb +21 -0
- data/examples/xmpp4r/echo.rb +36 -0
- data/lib/blather.rb +112 -0
- data/lib/blather/cert_store.rb +53 -0
- data/lib/blather/client.rb +95 -0
- data/lib/blather/client/client.rb +345 -0
- data/lib/blather/client/dsl.rb +320 -0
- data/lib/blather/client/dsl/pubsub.rb +174 -0
- data/lib/blather/core_ext/eventmachine.rb +125 -0
- data/lib/blather/core_ext/ipaddr.rb +20 -0
- data/lib/blather/errors.rb +69 -0
- data/lib/blather/errors/sasl_error.rb +44 -0
- data/lib/blather/errors/stanza_error.rb +110 -0
- data/lib/blather/errors/stream_error.rb +84 -0
- data/lib/blather/file_transfer.rb +107 -0
- data/lib/blather/file_transfer/ibb.rb +68 -0
- data/lib/blather/file_transfer/s5b.rb +114 -0
- data/lib/blather/jid.rb +141 -0
- data/lib/blather/roster.rb +118 -0
- data/lib/blather/roster_item.rb +146 -0
- data/lib/blather/stanza.rb +167 -0
- data/lib/blather/stanza/disco.rb +32 -0
- data/lib/blather/stanza/disco/capabilities.rb +161 -0
- data/lib/blather/stanza/disco/disco_info.rb +205 -0
- data/lib/blather/stanza/disco/disco_items.rb +134 -0
- data/lib/blather/stanza/iq.rb +144 -0
- data/lib/blather/stanza/iq/command.rb +339 -0
- data/lib/blather/stanza/iq/ibb.rb +86 -0
- data/lib/blather/stanza/iq/ping.rb +50 -0
- data/lib/blather/stanza/iq/query.rb +53 -0
- data/lib/blather/stanza/iq/roster.rb +185 -0
- data/lib/blather/stanza/iq/s5b.rb +208 -0
- data/lib/blather/stanza/iq/si.rb +415 -0
- data/lib/blather/stanza/iq/vcard.rb +149 -0
- data/lib/blather/stanza/message.rb +428 -0
- data/lib/blather/stanza/message/muc_user.rb +119 -0
- data/lib/blather/stanza/muc/muc_user_base.rb +54 -0
- data/lib/blather/stanza/presence.rb +172 -0
- data/lib/blather/stanza/presence/c.rb +100 -0
- data/lib/blather/stanza/presence/muc.rb +35 -0
- data/lib/blather/stanza/presence/muc_user.rb +147 -0
- data/lib/blather/stanza/presence/status.rb +218 -0
- data/lib/blather/stanza/presence/subscription.rb +100 -0
- data/lib/blather/stanza/pubsub.rb +119 -0
- data/lib/blather/stanza/pubsub/affiliations.rb +79 -0
- data/lib/blather/stanza/pubsub/create.rb +65 -0
- data/lib/blather/stanza/pubsub/errors.rb +18 -0
- data/lib/blather/stanza/pubsub/event.rb +139 -0
- data/lib/blather/stanza/pubsub/items.rb +103 -0
- data/lib/blather/stanza/pubsub/publish.rb +103 -0
- data/lib/blather/stanza/pubsub/retract.rb +92 -0
- data/lib/blather/stanza/pubsub/subscribe.rb +68 -0
- data/lib/blather/stanza/pubsub/subscription.rb +135 -0
- data/lib/blather/stanza/pubsub/subscriptions.rb +83 -0
- data/lib/blather/stanza/pubsub/unsubscribe.rb +84 -0
- data/lib/blather/stanza/pubsub_owner.rb +51 -0
- data/lib/blather/stanza/pubsub_owner/delete.rb +52 -0
- data/lib/blather/stanza/pubsub_owner/purge.rb +52 -0
- data/lib/blather/stanza/x.rb +416 -0
- data/lib/blather/stream.rb +266 -0
- data/lib/blather/stream/client.rb +32 -0
- data/lib/blather/stream/component.rb +39 -0
- data/lib/blather/stream/features.rb +70 -0
- data/lib/blather/stream/features/register.rb +38 -0
- data/lib/blather/stream/features/resource.rb +63 -0
- data/lib/blather/stream/features/sasl.rb +190 -0
- data/lib/blather/stream/features/session.rb +45 -0
- data/lib/blather/stream/features/tls.rb +29 -0
- data/lib/blather/stream/parser.rb +102 -0
- data/lib/blather/version.rb +3 -0
- data/lib/blather/xmpp_node.rb +94 -0
- data/spec/blather/client/client_spec.rb +687 -0
- data/spec/blather/client/dsl/pubsub_spec.rb +492 -0
- data/spec/blather/client/dsl_spec.rb +266 -0
- data/spec/blather/errors/sasl_error_spec.rb +33 -0
- data/spec/blather/errors/stanza_error_spec.rb +129 -0
- data/spec/blather/errors/stream_error_spec.rb +108 -0
- data/spec/blather/errors_spec.rb +33 -0
- data/spec/blather/file_transfer_spec.rb +135 -0
- data/spec/blather/jid_spec.rb +87 -0
- data/spec/blather/roster_item_spec.rb +134 -0
- data/spec/blather/roster_spec.rb +107 -0
- data/spec/blather/stanza/discos/disco_info_spec.rb +247 -0
- data/spec/blather/stanza/discos/disco_items_spec.rb +154 -0
- data/spec/blather/stanza/iq/command_spec.rb +206 -0
- data/spec/blather/stanza/iq/ibb_spec.rb +124 -0
- data/spec/blather/stanza/iq/ping_spec.rb +45 -0
- data/spec/blather/stanza/iq/query_spec.rb +64 -0
- data/spec/blather/stanza/iq/roster_spec.rb +139 -0
- data/spec/blather/stanza/iq/s5b_spec.rb +57 -0
- data/spec/blather/stanza/iq/si_spec.rb +98 -0
- data/spec/blather/stanza/iq/vcard_spec.rb +93 -0
- data/spec/blather/stanza/iq_spec.rb +61 -0
- data/spec/blather/stanza/message/muc_user_spec.rb +152 -0
- data/spec/blather/stanza/message_spec.rb +282 -0
- data/spec/blather/stanza/presence/c_spec.rb +56 -0
- data/spec/blather/stanza/presence/muc_spec.rb +37 -0
- data/spec/blather/stanza/presence/muc_user_spec.rb +83 -0
- data/spec/blather/stanza/presence/status_spec.rb +144 -0
- data/spec/blather/stanza/presence/subscription_spec.rb +102 -0
- data/spec/blather/stanza/presence_spec.rb +125 -0
- data/spec/blather/stanza/pubsub/affiliations_spec.rb +57 -0
- data/spec/blather/stanza/pubsub/create_spec.rb +56 -0
- data/spec/blather/stanza/pubsub/event_spec.rb +98 -0
- data/spec/blather/stanza/pubsub/items_spec.rb +79 -0
- data/spec/blather/stanza/pubsub/publish_spec.rb +83 -0
- data/spec/blather/stanza/pubsub/retract_spec.rb +75 -0
- data/spec/blather/stanza/pubsub/subscribe_spec.rb +61 -0
- data/spec/blather/stanza/pubsub/subscription_spec.rb +97 -0
- data/spec/blather/stanza/pubsub/subscriptions_spec.rb +59 -0
- data/spec/blather/stanza/pubsub/unsubscribe_spec.rb +74 -0
- data/spec/blather/stanza/pubsub_owner/delete_spec.rb +50 -0
- data/spec/blather/stanza/pubsub_owner/purge_spec.rb +50 -0
- data/spec/blather/stanza/pubsub_owner_spec.rb +27 -0
- data/spec/blather/stanza/pubsub_spec.rb +68 -0
- data/spec/blather/stanza/x_spec.rb +231 -0
- data/spec/blather/stanza_spec.rb +134 -0
- data/spec/blather/stream/client_spec.rb +1090 -0
- data/spec/blather/stream/component_spec.rb +108 -0
- data/spec/blather/stream/parser_spec.rb +152 -0
- data/spec/blather/stream/ssl_spec.rb +32 -0
- data/spec/blather/xmpp_node_spec.rb +47 -0
- data/spec/blather_spec.rb +34 -0
- data/spec/fixtures/pubsub.rb +311 -0
- data/spec/spec_helper.rb +17 -0
- data/yard/templates/default/class/html/handlers.erb +18 -0
- data/yard/templates/default/class/setup.rb +10 -0
- data/yard/templates/default/class/text/handlers.erb +1 -0
- metadata +459 -0
|
@@ -0,0 +1,687 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'blather/client/client'
|
|
3
|
+
|
|
4
|
+
describe Blather::Client do
|
|
5
|
+
before do
|
|
6
|
+
@client = Blather::Client.new
|
|
7
|
+
@stream = mock
|
|
8
|
+
@stream.stubs(:send)
|
|
9
|
+
@jid = Blather::JID.new('n@d/r')
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it 'provides a Blather::JID reader' do
|
|
13
|
+
@client.post_init @stream, @jid
|
|
14
|
+
@client.should respond_to :jid
|
|
15
|
+
@client.jid.should == @jid
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'provides a reader for the roster' do
|
|
19
|
+
@client.should respond_to :roster
|
|
20
|
+
@client.roster.should be_kind_of Blather::Roster
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'provides a status reader' do
|
|
24
|
+
@client.post_init @stream, @jid
|
|
25
|
+
@client.should respond_to :status
|
|
26
|
+
@client.status = :away
|
|
27
|
+
@client.status.should == :away
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'should have a caps handler' do
|
|
31
|
+
@client.should respond_to :caps
|
|
32
|
+
@client.caps.should be_kind_of Blather::Stanza::Capabilities
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'can be setup' do
|
|
36
|
+
@client.should respond_to :setup
|
|
37
|
+
@client.setup('me@me.com', 'pass').should == @client
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'knows if it has been setup' do
|
|
41
|
+
@client.should respond_to :setup?
|
|
42
|
+
@client.setup?.should == false
|
|
43
|
+
@client.setup 'me@me.com', 'pass'
|
|
44
|
+
@client.setup?.should == true
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'cannot be run before being setup' do
|
|
48
|
+
lambda { @client.run }.should raise_error RuntimeError
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'starts up a Component connection when setup without a node' do
|
|
52
|
+
setup = 'pubsub.jabber.local', 'secret'
|
|
53
|
+
@client.setup *setup
|
|
54
|
+
Blather::Stream::Component.expects(:start).with @client, *setup + [nil, nil, nil, nil]
|
|
55
|
+
@client.run
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'starts up a Client connection when setup with a node' do
|
|
59
|
+
setup = 'test@jabber.local', 'secret'
|
|
60
|
+
@client.setup *setup
|
|
61
|
+
Blather::Stream::Client.expects(:start).with @client, *setup + [nil, nil, nil, nil]
|
|
62
|
+
@client.run
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it 'knows if it is disconnected' do
|
|
66
|
+
@client.should respond_to :connected?
|
|
67
|
+
@client.connected?.should == false
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it 'knows if it is connected' do
|
|
71
|
+
stream = mock
|
|
72
|
+
stream.expects(:stopped?).returns false
|
|
73
|
+
@client.setup('me.com', 'secret')
|
|
74
|
+
@client.post_init stream, Blather::JID.new('me.com')
|
|
75
|
+
@client.connected?.should == true
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
describe 'if it has been setup but not connected yet' do
|
|
79
|
+
it 'should consider itself disconnected' do
|
|
80
|
+
@client.setup('me.com', 'secret')
|
|
81
|
+
@client.connected?.should == false
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it 'writes to the connection the closes when #close is called' do
|
|
86
|
+
stream = mock()
|
|
87
|
+
stream.expects(:close_connection_after_writing)
|
|
88
|
+
@client.setup('me.com', 'secret')
|
|
89
|
+
@client.post_init stream, Blather::JID.new('me.com')
|
|
90
|
+
@client.close
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it 'shuts down EM when #unbind is called if it is running' do
|
|
94
|
+
EM.expects(:reactor_running?).returns true
|
|
95
|
+
EM.expects(:stop)
|
|
96
|
+
@client.unbind
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it 'does nothing when #unbind is called and EM is not running' do
|
|
100
|
+
EM.expects(:reactor_running?).returns false
|
|
101
|
+
EM.expects(:stop).never
|
|
102
|
+
@client.unbind
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it 'calls the :disconnected handler with #unbind is called' do
|
|
106
|
+
EM.expects(:reactor_running?).returns false
|
|
107
|
+
disconnected = mock()
|
|
108
|
+
disconnected.expects(:call)
|
|
109
|
+
@client.register_handler(:disconnected) { disconnected.call }
|
|
110
|
+
@client.unbind
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it 'does not call EM.stop on #unbind if a handler returns positive' do
|
|
114
|
+
EM.expects(:reactor_running?).never
|
|
115
|
+
EM.expects(:stop).never
|
|
116
|
+
disconnected = mock()
|
|
117
|
+
disconnected.expects(:call).returns true
|
|
118
|
+
@client.register_handler(:disconnected) { disconnected.call }
|
|
119
|
+
@client.unbind
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it 'calls EM.stop on #unbind if a handler returns negative' do
|
|
123
|
+
EM.expects(:reactor_running?).returns true
|
|
124
|
+
EM.expects(:stop)
|
|
125
|
+
disconnected = mock()
|
|
126
|
+
disconnected.expects(:call).returns false
|
|
127
|
+
@client.register_handler(:disconnected) { disconnected.call }
|
|
128
|
+
@client.unbind
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it 'can register a temporary handler based on stanza ID' do
|
|
132
|
+
stanza = Blather::Stanza::Iq.new
|
|
133
|
+
response = mock()
|
|
134
|
+
response.expects(:call)
|
|
135
|
+
@client.register_tmp_handler(stanza.id) { |_| response.call }
|
|
136
|
+
@client.receive_data stanza
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it 'removes a tmp handler as soon as it is used' do
|
|
140
|
+
stanza = Blather::Stanza::Iq.new
|
|
141
|
+
response = mock()
|
|
142
|
+
response.expects(:call)
|
|
143
|
+
@client.register_tmp_handler(stanza.id) { |_| response.call }
|
|
144
|
+
@client.receive_data stanza
|
|
145
|
+
@client.receive_data stanza
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
it 'will create a handler then write the stanza' do
|
|
149
|
+
stanza = Blather::Stanza::Iq.new
|
|
150
|
+
response = mock()
|
|
151
|
+
response.expects(:call)
|
|
152
|
+
@client.expects(:write).with do |s|
|
|
153
|
+
@client.receive_data stanza
|
|
154
|
+
s.should == stanza
|
|
155
|
+
end
|
|
156
|
+
@client.write_with_handler(stanza) { |_| response.call }
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
it 'can register a handler' do
|
|
160
|
+
stanza = Blather::Stanza::Iq.new
|
|
161
|
+
response = mock()
|
|
162
|
+
response.expects(:call).times(2)
|
|
163
|
+
@client.register_handler(:iq) { |_| response.call }
|
|
164
|
+
@client.receive_data stanza
|
|
165
|
+
@client.receive_data stanza
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
it 'allows for breaking out of handlers' do
|
|
169
|
+
stanza = Blather::Stanza::Iq.new
|
|
170
|
+
response = mock(:iq => nil)
|
|
171
|
+
@client.register_handler(:iq) do |_|
|
|
172
|
+
response.iq
|
|
173
|
+
throw :halt
|
|
174
|
+
response.fail
|
|
175
|
+
end
|
|
176
|
+
@client.receive_data stanza
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
it 'allows for passing to the next handler of the same type' do
|
|
180
|
+
stanza = Blather::Stanza::Iq.new
|
|
181
|
+
response = mock(:iq1 => nil, :iq2 => nil)
|
|
182
|
+
@client.register_handler(:iq) do |_|
|
|
183
|
+
response.iq1
|
|
184
|
+
throw :pass
|
|
185
|
+
response.fail
|
|
186
|
+
end
|
|
187
|
+
@client.register_handler(:iq) do |_|
|
|
188
|
+
response.iq2
|
|
189
|
+
end
|
|
190
|
+
@client.receive_data stanza
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
it 'allows for passing to the next handler in the hierarchy' do
|
|
194
|
+
stanza = Blather::Stanza::Iq::Query.new
|
|
195
|
+
response = mock(:query => nil, :iq => nil)
|
|
196
|
+
@client.register_handler(:query) do |_|
|
|
197
|
+
response.query
|
|
198
|
+
throw :pass
|
|
199
|
+
response.fail
|
|
200
|
+
end
|
|
201
|
+
@client.register_handler(:iq) { |_| response.iq }
|
|
202
|
+
@client.receive_data stanza
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
it 'can clear handlers' do
|
|
206
|
+
stanza = Blather::Stanza::Message.new
|
|
207
|
+
stanza.expects(:chat?).returns true
|
|
208
|
+
|
|
209
|
+
response = mock
|
|
210
|
+
response.expects(:call).once
|
|
211
|
+
|
|
212
|
+
@client.register_handler(:message, :chat?) { |_| response.call }
|
|
213
|
+
@client.receive_data stanza
|
|
214
|
+
|
|
215
|
+
@client.clear_handlers(:message, :chat?)
|
|
216
|
+
@client.receive_data stanza
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
describe 'Blather::Client#write' do
|
|
221
|
+
before do
|
|
222
|
+
@client = Blather::Client.new
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
it 'writes to the stream' do
|
|
226
|
+
stanza = Blather::Stanza::Iq.new
|
|
227
|
+
stream = mock()
|
|
228
|
+
stream.expects(:send).with stanza
|
|
229
|
+
@client.setup('me@me.com', 'me')
|
|
230
|
+
@client.post_init stream, Blather::JID.new('me.com')
|
|
231
|
+
@client.write stanza
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
describe 'Blather::Client#status=' do
|
|
236
|
+
before do
|
|
237
|
+
@client = Blather::Client.new
|
|
238
|
+
@stream = mock()
|
|
239
|
+
@stream.stubs(:send)
|
|
240
|
+
@client.post_init @stream, Blather::JID.new('n@d/r')
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
it 'updates the state when not sending to a Blather::JID' do
|
|
244
|
+
@stream.stubs(:write)
|
|
245
|
+
@client.status.should_not equal :away
|
|
246
|
+
@client.status = :away, 'message'
|
|
247
|
+
@client.status.should == :away
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
it 'does not update the state when sending to a Blather::JID' do
|
|
251
|
+
@stream.stubs(:write)
|
|
252
|
+
@client.status.should_not equal :away
|
|
253
|
+
@client.status = :away, 'message', 'me@me.com'
|
|
254
|
+
@client.status.should_not equal :away
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
it 'writes the new status to the stream' do
|
|
258
|
+
Blather::Stanza::Presence::Status.stubs(:next_id).returns 0
|
|
259
|
+
status = [:away, 'message']
|
|
260
|
+
@stream.expects(:send).with do |s|
|
|
261
|
+
s.should be_kind_of Blather::Stanza::Presence::Status
|
|
262
|
+
s.to_s.should == Blather::Stanza::Presence::Status.new(*status).to_s
|
|
263
|
+
end
|
|
264
|
+
@client.status = status
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
describe 'Blather::Client default handlers' do
|
|
269
|
+
before do
|
|
270
|
+
@client = Blather::Client.new
|
|
271
|
+
@stream = mock()
|
|
272
|
+
@stream.stubs(:send)
|
|
273
|
+
@client.post_init @stream, Blather::JID.new('n@d/r')
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
it 're-raises errors' do
|
|
277
|
+
err = Blather::BlatherError.new
|
|
278
|
+
lambda { @client.receive_data err }.should raise_error Blather::BlatherError
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# it 'responds to iq:get with a "service-unavailable" error' do
|
|
282
|
+
# get = Blather::Stanza::Iq.new :get
|
|
283
|
+
# err = Blather::StanzaError.new(get, 'service-unavailable', :cancel).to_node
|
|
284
|
+
# @client.expects(:write).with err
|
|
285
|
+
# @client.receive_data get
|
|
286
|
+
# end
|
|
287
|
+
|
|
288
|
+
# it 'responds to iq:get with a "service-unavailable" error' do
|
|
289
|
+
# get = Blather::Stanza::Iq.new :get
|
|
290
|
+
# err = Blather::StanzaError.new(get, 'service-unavailable', :cancel).to_node
|
|
291
|
+
# @client.expects(:write).with { |n| n.to_s.should == err.to_s }
|
|
292
|
+
# @client.receive_data get
|
|
293
|
+
# end
|
|
294
|
+
|
|
295
|
+
# it 'responds to iq:set with a "service-unavailable" error' do
|
|
296
|
+
# get = Blather::Stanza::Iq.new :set
|
|
297
|
+
# err = Blather::StanzaError.new(get, 'service-unavailable', :cancel).to_node
|
|
298
|
+
# @client.expects(:write).with { |n| n.to_s.should == err.to_s }
|
|
299
|
+
# @client.receive_data get
|
|
300
|
+
# end
|
|
301
|
+
|
|
302
|
+
it 'responds to s2c pings with a pong' do
|
|
303
|
+
ping = Blather::Stanza::Iq::Ping.new :get
|
|
304
|
+
pong = ping.reply
|
|
305
|
+
@client.expects(:write).with { |n| n.to_s.should == pong.to_s }
|
|
306
|
+
@client.receive_data ping
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
it 'handles status changes by updating the roster if the status is from a Blather::JID in the roster' do
|
|
310
|
+
jid = 'friend@jabber.local'
|
|
311
|
+
status = Blather::Stanza::Presence::Status.new :away
|
|
312
|
+
status.stubs(:from).returns jid
|
|
313
|
+
roster_item = mock()
|
|
314
|
+
roster_item.expects(:status=).with status
|
|
315
|
+
@client.stubs(:roster).returns({status.from => roster_item})
|
|
316
|
+
@client.receive_data status
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
it 'lets status stanzas fall through to other handlers' do
|
|
320
|
+
jid = 'friend@jabber.local'
|
|
321
|
+
status = Blather::Stanza::Presence::Status.new :away
|
|
322
|
+
status.stubs(:from).returns jid
|
|
323
|
+
roster_item = mock()
|
|
324
|
+
roster_item.expects(:status=).with status
|
|
325
|
+
@client.stubs(:roster).returns({status.from => roster_item})
|
|
326
|
+
|
|
327
|
+
response = mock()
|
|
328
|
+
response.expects(:call).with jid
|
|
329
|
+
@client.register_handler(:status) { |s| response.call s.from.to_s }
|
|
330
|
+
@client.receive_data status
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
it 'handles an incoming roster node by processing it through the roster' do
|
|
334
|
+
roster = Blather::Stanza::Iq::Roster.new
|
|
335
|
+
client_roster = mock()
|
|
336
|
+
client_roster.expects(:process).with roster
|
|
337
|
+
@client.stubs(:roster).returns client_roster
|
|
338
|
+
@client.receive_data roster
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
it 'handles an incoming roster node by processing it through the roster' do
|
|
342
|
+
roster = Blather::Stanza::Iq::Roster.new
|
|
343
|
+
client_roster = mock()
|
|
344
|
+
client_roster.expects(:process).with roster
|
|
345
|
+
@client.stubs(:roster).returns client_roster
|
|
346
|
+
|
|
347
|
+
response = mock()
|
|
348
|
+
response.expects(:call)
|
|
349
|
+
@client.register_handler(:roster) { |_| response.call }
|
|
350
|
+
@client.receive_data roster
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
describe 'Blather::Client with a Component stream' do
|
|
355
|
+
before do
|
|
356
|
+
class MockComponent < Blather::Stream::Component; def initialize(); end; end
|
|
357
|
+
@stream = MockComponent.new('')
|
|
358
|
+
@stream.stubs(:send_data)
|
|
359
|
+
@client = Blather::Client.new
|
|
360
|
+
@client.setup('me.com', 'secret')
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
it 'calls the ready handler when sent post_init' do
|
|
364
|
+
ready = mock()
|
|
365
|
+
ready.expects(:call)
|
|
366
|
+
@client.register_handler(:ready) { ready.call }
|
|
367
|
+
@client.post_init @stream
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
describe 'Blather::Client with a Client stream' do
|
|
372
|
+
before do
|
|
373
|
+
class MockClientStream < Blather::Stream::Client; def initialize(); end; end
|
|
374
|
+
@stream = MockClientStream.new('')
|
|
375
|
+
@client = Blather::Client.new
|
|
376
|
+
Blather::Stream::Client.stubs(:start).returns @stream
|
|
377
|
+
@client.setup('me@me.com', 'secret').run
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
it 'sends a request for the roster when post_init is called' do
|
|
381
|
+
@stream.expects(:send).with { |stanza| stanza.should be_kind_of Blather::Stanza::Iq::Roster }
|
|
382
|
+
@client.post_init @stream, Blather::JID.new('n@d/r')
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
it 'calls the ready handler after post_init and roster is received' do
|
|
386
|
+
result_roster = Blather::Stanza::Iq::Roster.new :result
|
|
387
|
+
@stream.stubs(:send).with { |s| result_roster.id = s.id; @client.receive_data result_roster; true }
|
|
388
|
+
|
|
389
|
+
ready = mock()
|
|
390
|
+
ready.expects(:call)
|
|
391
|
+
@client.register_handler(:ready) { ready.call }
|
|
392
|
+
@client.post_init @stream, Blather::JID.new('n@d/r')
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
describe 'Blather::Client filters' do
|
|
397
|
+
before do
|
|
398
|
+
@client = Blather::Client.new
|
|
399
|
+
@stream = mock()
|
|
400
|
+
@stream.stubs(:send)
|
|
401
|
+
@client.post_init @stream, Blather::JID.new('n@d/r')
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
it 'raises an error when an invalid filter type is registered' do
|
|
405
|
+
lambda { @client.register_filter(:invalid) {} }.should raise_error RuntimeError
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
it 'can be guarded' do
|
|
409
|
+
stanza = Blather::Stanza::Iq.new
|
|
410
|
+
ready = mock()
|
|
411
|
+
ready.expects(:call).once
|
|
412
|
+
@client.register_filter(:before, :iq, :id => stanza.id) { |_| ready.call }
|
|
413
|
+
@client.register_filter(:before, :iq, :id => 'not-id') { |_| ready.call }
|
|
414
|
+
@client.receive_data stanza
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
it 'can pass to the next handler' do
|
|
418
|
+
stanza = Blather::Stanza::Iq.new
|
|
419
|
+
ready = mock()
|
|
420
|
+
ready.expects(:call).once
|
|
421
|
+
@client.register_filter(:before) { |_| throw :pass; ready.call }
|
|
422
|
+
@client.register_filter(:before) { |_| ready.call }
|
|
423
|
+
@client.receive_data stanza
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
it 'runs them in order' do
|
|
427
|
+
stanza = Blather::Stanza::Iq.new
|
|
428
|
+
count = 0
|
|
429
|
+
@client.register_filter(:before) { |_| count.should == 0; count = 1 }
|
|
430
|
+
@client.register_filter(:before) { |_| count.should == 1; count = 2 }
|
|
431
|
+
@client.register_handler(:iq) { |_| count.should == 2; count = 3 }
|
|
432
|
+
@client.register_filter(:after) { |_| count.should == 3; count = 4 }
|
|
433
|
+
@client.register_filter(:after) { |_| count.should == 4 }
|
|
434
|
+
@client.receive_data stanza
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
it 'can modify the stanza' do
|
|
438
|
+
stanza = Blather::Stanza::Iq.new
|
|
439
|
+
stanza.from = 'from@test.local'
|
|
440
|
+
new_jid = 'before@filter.local'
|
|
441
|
+
ready = mock()
|
|
442
|
+
ready.expects(:call).with new_jid
|
|
443
|
+
@client.register_filter(:before) { |s| s.from = new_jid }
|
|
444
|
+
@client.register_handler(:iq) { |s| ready.call s.from.to_s }
|
|
445
|
+
@client.receive_data stanza
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
it 'can halt the handler chain' do
|
|
449
|
+
stanza = Blather::Stanza::Iq.new
|
|
450
|
+
ready = mock()
|
|
451
|
+
ready.expects(:call).never
|
|
452
|
+
@client.register_filter(:before) { |_| throw :halt }
|
|
453
|
+
@client.register_handler(:iq) { |_| ready.call }
|
|
454
|
+
@client.receive_data stanza
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
it 'can be specific to a handler' do
|
|
458
|
+
stanza = Blather::Stanza::Iq.new
|
|
459
|
+
ready = mock()
|
|
460
|
+
ready.expects(:call).once
|
|
461
|
+
@client.register_filter(:before, :iq) { |_| ready.call }
|
|
462
|
+
@client.register_filter(:before, :message) { |_| ready.call }
|
|
463
|
+
@client.receive_data stanza
|
|
464
|
+
end
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
describe 'Blather::Client guards' do
|
|
468
|
+
before do
|
|
469
|
+
stream = mock()
|
|
470
|
+
stream.stubs(:send)
|
|
471
|
+
@client = Blather::Client.new
|
|
472
|
+
@client.post_init stream, Blather::JID.new('n@d/r')
|
|
473
|
+
@stanza = Blather::Stanza::Iq.new
|
|
474
|
+
@response = mock()
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
it 'can be a symbol' do
|
|
478
|
+
@response.expects :call
|
|
479
|
+
@client.register_handler(:iq, :chat?) { |_| @response.call }
|
|
480
|
+
|
|
481
|
+
@stanza.expects(:chat?).returns true
|
|
482
|
+
@client.receive_data @stanza
|
|
483
|
+
|
|
484
|
+
@stanza.expects(:chat?).returns false
|
|
485
|
+
@client.receive_data @stanza
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
it 'can be a hash with string match' do
|
|
489
|
+
@response.expects :call
|
|
490
|
+
@client.register_handler(:iq, :body => 'exit') { |_| @response.call }
|
|
491
|
+
|
|
492
|
+
@stanza.expects(:body).returns 'exit'
|
|
493
|
+
@client.receive_data @stanza
|
|
494
|
+
|
|
495
|
+
@stanza.expects(:body).returns 'not-exit'
|
|
496
|
+
@client.receive_data @stanza
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
it 'can be a hash with a value' do
|
|
500
|
+
@response.expects :call
|
|
501
|
+
@client.register_handler(:iq, :number => 0) { |_| @response.call }
|
|
502
|
+
|
|
503
|
+
@stanza.expects(:number).returns 0
|
|
504
|
+
@client.receive_data @stanza
|
|
505
|
+
|
|
506
|
+
@stanza.expects(:number).returns 1
|
|
507
|
+
@client.receive_data @stanza
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
it 'can be a hash with a regexp' do
|
|
511
|
+
@response.expects :call
|
|
512
|
+
@client.register_handler(:iq, :body => /exit/) { |_| @response.call }
|
|
513
|
+
|
|
514
|
+
@stanza.expects(:body).returns 'more than just exit, but exit still'
|
|
515
|
+
@client.receive_data @stanza
|
|
516
|
+
|
|
517
|
+
@stanza.expects(:body).returns 'keyword not found'
|
|
518
|
+
@client.receive_data @stanza
|
|
519
|
+
|
|
520
|
+
@stanza.expects(:body).returns nil
|
|
521
|
+
@client.receive_data @stanza
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
it 'can be a hash with an array' do
|
|
525
|
+
@response.expects(:call).times(2)
|
|
526
|
+
@client.register_handler(:iq, :type => [:result, :error]) { |_| @response.call }
|
|
527
|
+
|
|
528
|
+
stanza = Blather::Stanza::Iq.new
|
|
529
|
+
stanza.expects(:type).at_least_once.returns :result
|
|
530
|
+
@client.receive_data stanza
|
|
531
|
+
|
|
532
|
+
stanza = Blather::Stanza::Iq.new
|
|
533
|
+
stanza.expects(:type).at_least_once.returns :error
|
|
534
|
+
@client.receive_data stanza
|
|
535
|
+
|
|
536
|
+
stanza = Blather::Stanza::Iq.new
|
|
537
|
+
stanza.expects(:type).at_least_once.returns :get
|
|
538
|
+
@client.receive_data stanza
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
it 'chained are treated like andand (short circuited)' do
|
|
542
|
+
@response.expects :call
|
|
543
|
+
@client.register_handler(:iq, :type => :get, :body => 'test') { |_| @response.call }
|
|
544
|
+
|
|
545
|
+
stanza = Blather::Stanza::Iq.new
|
|
546
|
+
stanza.expects(:type).at_least_once.returns :get
|
|
547
|
+
stanza.expects(:body).returns 'test'
|
|
548
|
+
@client.receive_data stanza
|
|
549
|
+
|
|
550
|
+
stanza = Blather::Stanza::Iq.new
|
|
551
|
+
stanza.expects(:type).at_least_once.returns :set
|
|
552
|
+
stanza.expects(:body).never
|
|
553
|
+
@client.receive_data stanza
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
it 'within an Array are treated as oror (short circuited)' do
|
|
557
|
+
@response.expects(:call).times 2
|
|
558
|
+
@client.register_handler(:iq, [{:type => :get}, {:body => 'test'}]) { |_| @response.call }
|
|
559
|
+
|
|
560
|
+
stanza = Blather::Stanza::Iq.new
|
|
561
|
+
stanza.expects(:type).at_least_once.returns :set
|
|
562
|
+
stanza.expects(:body).returns 'test'
|
|
563
|
+
@client.receive_data stanza
|
|
564
|
+
|
|
565
|
+
stanza = Blather::Stanza::Iq.new
|
|
566
|
+
stanza.stubs(:type).at_least_once.returns :get
|
|
567
|
+
stanza.expects(:body).never
|
|
568
|
+
@client.receive_data stanza
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
it 'can be a lambda' do
|
|
572
|
+
@response.expects :call
|
|
573
|
+
@client.register_handler(:iq, lambda { |s| s.number % 3 == 0 }) { |_| @response.call }
|
|
574
|
+
|
|
575
|
+
@stanza.expects(:number).at_least_once.returns 3
|
|
576
|
+
@client.receive_data @stanza
|
|
577
|
+
|
|
578
|
+
@stanza.expects(:number).at_least_once.returns 2
|
|
579
|
+
@client.receive_data @stanza
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
it 'can be an xpath and will send the result to the handler' do
|
|
583
|
+
@response.expects(:call).with do |stanza, xpath|
|
|
584
|
+
xpath.should be_instance_of Nokogiri::XML::NodeSet
|
|
585
|
+
xpath.should_not be_empty
|
|
586
|
+
stanza.should == @stanza
|
|
587
|
+
end
|
|
588
|
+
@client.register_handler(:iq, "/iq[@id='#{@stanza.id}']") { |stanza, xpath| @response.call stanza, xpath }
|
|
589
|
+
@client.receive_data @stanza
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
it 'can be an xpath with namespaces and will send the result to the handler' do
|
|
593
|
+
@stanza = Blather::Stanza.parse('<message><foo xmlns="http://bar.com"></message>')
|
|
594
|
+
@response.expects(:call).with do |stanza, xpath|
|
|
595
|
+
xpath.should be_instance_of Nokogiri::XML::NodeSet
|
|
596
|
+
xpath.should_not be_empty
|
|
597
|
+
stanza.should == @stanza
|
|
598
|
+
end
|
|
599
|
+
@client.register_handler(:message, "/message/bar:foo", :bar => 'http://bar.com') { |stanza, xpath| @response.call stanza, xpath }
|
|
600
|
+
@client.receive_data @stanza
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
it 'raises an error when a bad guard is tried' do
|
|
604
|
+
lambda { @client.register_handler(:iq, 0) {} }.should raise_error RuntimeError
|
|
605
|
+
end
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
describe 'Blather::Client::Caps' do
|
|
609
|
+
before do
|
|
610
|
+
@client = Blather::Client.new
|
|
611
|
+
@stream = mock()
|
|
612
|
+
@stream.stubs(:send)
|
|
613
|
+
@client.post_init @stream, Blather::JID.new('n@d/r')
|
|
614
|
+
@caps = @client.caps
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
it 'must be of type result' do
|
|
618
|
+
@caps.should respond_to :type
|
|
619
|
+
@caps.type.should == :result
|
|
620
|
+
end
|
|
621
|
+
|
|
622
|
+
it 'can have a client node set' do
|
|
623
|
+
@caps.should respond_to :node=
|
|
624
|
+
@caps.node = "somenode"
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
it 'provides a client node reader' do
|
|
628
|
+
@caps.should respond_to :node
|
|
629
|
+
@caps.node = "somenode"
|
|
630
|
+
@caps.node.should == "somenode##{@caps.ver}"
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
it 'can have identities set' do
|
|
634
|
+
@caps.should respond_to :identities=
|
|
635
|
+
@caps.identities = [{:name => "name", :type => "type", :category => "cat"}]
|
|
636
|
+
end
|
|
637
|
+
|
|
638
|
+
it 'provides an identities reader' do
|
|
639
|
+
@caps.should respond_to :identities
|
|
640
|
+
@caps.identities = [{:name => "name", :type => "type", :category => "cat"}]
|
|
641
|
+
@caps.identities.should == [Blather::Stanza::Iq::DiscoInfo::Identity.new({:name => "name", :type => "type", :category => "cat"})]
|
|
642
|
+
end
|
|
643
|
+
|
|
644
|
+
it 'can have features set' do
|
|
645
|
+
@caps.should respond_to :features=
|
|
646
|
+
@caps.features.size.should == 0
|
|
647
|
+
@caps.features = ["feature1"]
|
|
648
|
+
@caps.features.size.should == 1
|
|
649
|
+
@caps.features += [Blather::Stanza::Iq::DiscoInfo::Feature.new("feature2")]
|
|
650
|
+
@caps.features.size.should == 2
|
|
651
|
+
@caps.features = nil
|
|
652
|
+
@caps.features.size.should == 0
|
|
653
|
+
end
|
|
654
|
+
|
|
655
|
+
it 'provides a features reader' do
|
|
656
|
+
@caps.should respond_to :features
|
|
657
|
+
@caps.features = %w{feature1 feature2}
|
|
658
|
+
@caps.features.should == [Blather::Stanza::Iq::DiscoInfo::Feature.new("feature1"), Blather::Stanza::Iq::DiscoInfo::Feature.new("feature2")]
|
|
659
|
+
end
|
|
660
|
+
|
|
661
|
+
it 'provides a client ver reader' do
|
|
662
|
+
@caps.should respond_to :ver
|
|
663
|
+
@caps.node = 'http://code.google.com/p/exodus'
|
|
664
|
+
@caps.identities = [Blather::Stanza::Iq::DiscoInfo::Identity.new({:name => 'Exodus 0.9.1', :type => 'pc', :category => 'client'})]
|
|
665
|
+
@caps.features = %w{
|
|
666
|
+
http://jabber.org/protocol/caps
|
|
667
|
+
http://jabber.org/protocol/disco#info
|
|
668
|
+
http://jabber.org/protocol/disco#items
|
|
669
|
+
http://jabber.org/protocol/muc
|
|
670
|
+
}
|
|
671
|
+
@caps.ver.should == 'QgayPKawpkPSDYmwT/WM94uAlu0='
|
|
672
|
+
@caps.node.should == "http://code.google.com/p/exodus#QgayPKawpkPSDYmwT/WM94uAlu0="
|
|
673
|
+
end
|
|
674
|
+
|
|
675
|
+
it 'can construct caps presence correctly' do
|
|
676
|
+
@caps.should respond_to :c
|
|
677
|
+
@caps.node = 'http://code.google.com/p/exodus'
|
|
678
|
+
@caps.identities = [Blather::Stanza::Iq::DiscoInfo::Identity.new({:name => 'Exodus 0.9.1', :type => 'pc', :category => 'client'})]
|
|
679
|
+
@caps.features = %w{
|
|
680
|
+
http://jabber.org/protocol/caps
|
|
681
|
+
http://jabber.org/protocol/disco#info
|
|
682
|
+
http://jabber.org/protocol/disco#items
|
|
683
|
+
http://jabber.org/protocol/muc
|
|
684
|
+
}
|
|
685
|
+
Nokogiri::XML(@caps.c.to_xml).to_s.should == Nokogiri::XML("<presence><c xmlns=\"http://jabber.org/protocol/caps\" hash=\"sha-1\" node=\"http://code.google.com/p/exodus\" ver=\"QgayPKawpkPSDYmwT/WM94uAlu0=\"/></presence>").to_s
|
|
686
|
+
end
|
|
687
|
+
end
|