shingara-blather 0.4.9 → 0.4.14

Sign up to get free protection for your applications and to get access to all the features.
@@ -113,8 +113,9 @@ module Blather
113
113
  #
114
114
  # @param [#to_xml, #to_s] stanza the stanza to send over the wire
115
115
  def send(stanza)
116
- Blather.logger.debug "SENDING: (#{caller[1]}) #{stanza}"
117
- send_data stanza.respond_to?(:to_xml) ? stanza.to_xml : stanza.to_s
116
+ data = stanza.respond_to?(:to_xml) ? stanza.to_xml(:save_with => Nokogiri::XML::Node::SaveOptions::AS_XML) : stanza.to_s
117
+ Blather.logger.debug "SENDING: (#{caller[1]}) #{data}"
118
+ send_data data
118
119
  end
119
120
 
120
121
  # Called by EM.connect to initialize stream variables
@@ -16,6 +16,7 @@ class Stream # :nodoc:
16
16
  @namespaces = {}
17
17
  @namespace_definitions = []
18
18
  @parser = Nokogiri::XML::SAX::PushParser.new self
19
+ @parser.options = Nokogiri::XML::ParseOptions::DEFAULT_XML
19
20
  end
20
21
 
21
22
  def receive_data(string)
@@ -37,11 +38,6 @@ class Stream # :nodoc:
37
38
  node[attr.localname] = attr.value
38
39
  end
39
40
 
40
- if !@receiver.stopped?
41
- @current << node if @current
42
- @current = node
43
- end
44
-
45
41
  ns_keys = namespaces.map { |pre, href| pre }
46
42
  namespaces.delete_if { |pre, href| NS_TO_IGNORE.include? href }
47
43
  @namespace_definitions.push []
@@ -53,6 +49,11 @@ class Stream # :nodoc:
53
49
  @namespaces[[prefix, uri]] ||= node.add_namespace(prefix, uri) if prefix && !ns_keys.include?(prefix)
54
50
  node.namespace = @namespaces[[prefix, uri]]
55
51
 
52
+ if !@receiver.stopped?
53
+ @current << node if @current
54
+ @current = node
55
+ end
56
+
56
57
  deliver(node) if elem == 'stream'
57
58
 
58
59
  # $stderr.puts "\n\n"
@@ -33,7 +33,7 @@ module Blather
33
33
  # @return [Class, nil] the class appropriate for the name/ns combination
34
34
  def self.class_from_registration(name, ns = nil)
35
35
  name = name.to_s
36
- @@registrations[[name, ns]] || @@registrations[[name, nil]]
36
+ @@registrations[[name, ns]]
37
37
  end
38
38
 
39
39
  # Import an XML::Node to the appropriate class
@@ -65,7 +65,19 @@ describe Blather::RosterItem do
65
65
 
66
66
  it 'returns status based on priority' do
67
67
  setup_item_with_presences
68
- @i.status.must_equal @p2
68
+ @i.status.must_equal @p3
69
+ end
70
+
71
+ it 'returns status based on priority and state' do
72
+ setup_item_with_presences
73
+
74
+ @p4 = Blather::Stanza::Presence::Status.new
75
+ @p4.type = :unavailable
76
+ @p4.from = 'n@d/d'
77
+ @p4.priority = 15
78
+ @i.status = @p4
79
+
80
+ @i.status.must_equal @p3
69
81
  end
70
82
 
71
83
  it 'returns status based on resource' do
@@ -85,12 +97,38 @@ describe Blather::RosterItem do
85
97
  @p2.from = 'n@d/b'
86
98
  @p2.priority = -1
87
99
 
100
+ @p3 = Blather::Stanza::Presence::Status.new(:dnd)
101
+ @p3.from = 'n@d/c'
102
+ @p3.priority = 10
103
+
88
104
  @i.status = @p
89
105
  @i.status = @p2
106
+ @i.status = @p3
107
+ end
108
+
109
+ it 'removes old unavailable presences' do
110
+ setup_item_with_presences
111
+
112
+ 50.times do |i|
113
+ p = Blather::Stanza::Presence::Status.new
114
+ p.type = :unavailable
115
+ p.from = "n@d/#{i}"
116
+ @i.status = p
117
+ end
118
+
119
+ @i.statuses.size.must_equal 4
90
120
  end
91
121
 
92
122
  it 'initializes groups to [nil] if the item is not part of a group' do
93
123
  i = Blather::RosterItem.new 'n@d'
94
124
  i.groups.must_equal [nil]
95
125
  end
126
+
127
+ it 'can determine equality' do
128
+ item1 = Blather::RosterItem.new 'n@d'
129
+ item2 = Blather::RosterItem.new 'n@d'
130
+ item1.groups = %w[group1 group2]
131
+ item2.groups = %w[group1 group2]
132
+ (item1 == item2).must_equal true
133
+ end
96
134
  end
@@ -54,6 +54,29 @@ describe Blather::Stanza::Iq::DiscoInfo do
54
54
  n.to = 'to@jid.com'
55
55
  n.find("/iq[@to='to@jid.com' and @type='get' and @id='#{n.id}']/ns:query[@node='/path/to/node']", :ns => Blather::Stanza::Iq::DiscoInfo.registered_ns).wont_be_empty
56
56
  end
57
+
58
+ it 'allows adding of identities' do
59
+ di = Blather::Stanza::Iq::DiscoInfo.new
60
+ di.identities.size.must_equal 0
61
+ di.identities = [{:name => 'name', :type => 'type', :category => 'category'}]
62
+ di.identities.size.must_equal 1
63
+ di.identities += [Blather::Stanza::Iq::DiscoInfo::Identity.new(*%w[name type category])]
64
+ di.identities.size.must_equal 2
65
+ di.identities = nil
66
+ di.identities.size.must_equal 0
67
+ end
68
+
69
+ it 'allows adding of features' do
70
+ di = Blather::Stanza::Iq::DiscoInfo.new
71
+ di.features.size.must_equal 0
72
+ di.features = ["feature1"]
73
+ di.features.size.must_equal 1
74
+ di.features += [Blather::Stanza::Iq::DiscoInfo::Feature.new("feature2")]
75
+ di.features.size.must_equal 2
76
+ di.features = nil
77
+ di.features.size.must_equal 0
78
+ end
79
+
57
80
  end
58
81
 
59
82
  describe 'Blather::Stanza::Iq::DiscoInfo identities' do
@@ -102,6 +102,17 @@ describe Blather::Stanza::Iq::DiscoItems do
102
102
  di.items.size.must_equal 2
103
103
  di.items.each { |i| control.include?(i).must_equal true }
104
104
  end
105
+
106
+ it 'allows adding of items' do
107
+ di = Blather::Stanza::Iq::DiscoItems.new
108
+ di.items.size.must_equal 0
109
+ di.items = [{:jid => 'foo@bar/baz', :node => 'node', :name => 'name'}]
110
+ di.items.size.must_equal 1
111
+ di.items += [Blather::Stanza::Iq::DiscoItems::Item.new(*%w[foo@bar/baz node name])]
112
+ di.items.size.must_equal 2
113
+ di.items = nil
114
+ di.items.size.must_equal 0
115
+ end
105
116
  end
106
117
 
107
118
  describe Blather::Stanza::Iq::DiscoItems::Item do
@@ -0,0 +1,206 @@
1
+ require File.join(File.dirname(__FILE__), *%w[.. .. .. spec_helper])
2
+
3
+ def command_xml
4
+ <<-XML
5
+ <iq type='result'
6
+ from='catalog.shakespeare.lit'
7
+ to='romeo@montague.net/orchard'
8
+ id='form2'>
9
+ <command xmlns='http://jabber.org/protocol/commands'
10
+ node='node1'
11
+ sessionid='dqjiodmqlmakm'>
12
+ <x xmlns='jabber:x:data' type='form'>
13
+ <field var='field-name' type='text-single' label='description' />
14
+ </x>
15
+ </command>
16
+ </iq>
17
+ XML
18
+ end
19
+
20
+ describe Blather::Stanza::Iq::Command do
21
+ it 'registers itself' do
22
+ Blather::XMPPNode.class_from_registration(:command, 'http://jabber.org/protocol/commands').must_equal Blather::Stanza::Iq::Command
23
+ end
24
+
25
+ it 'must be importable' do
26
+ Blather::XMPPNode.import(parse_stanza(command_xml).root).must_be_instance_of Blather::Stanza::Iq::Command
27
+ end
28
+
29
+ it 'ensures a command node is present on create' do
30
+ c = Blather::Stanza::Iq::Command.new
31
+ c.xpath('xmlns:command', :xmlns => Blather::Stanza::Iq::Command.registered_ns).wont_be_empty
32
+ end
33
+
34
+ it 'ensures a command node exists when calling #command' do
35
+ c = Blather::Stanza::Iq::Command.new
36
+ c.remove_children :command
37
+ c.xpath('ns:command', :ns => Blather::Stanza::Iq::Command.registered_ns).must_be_empty
38
+
39
+ c.command.wont_be_nil
40
+ c.xpath('ns:command', :ns => Blather::Stanza::Iq::Command.registered_ns).wont_be_empty
41
+ end
42
+
43
+ Blather::Stanza::Iq::Command::VALID_ACTIONS.each do |valid_action|
44
+ it "provides a helper (#{valid_action}?) for action #{valid_action}" do
45
+ Blather::Stanza::Iq::Command.new.must_respond_to :"#{valid_action}?"
46
+ end
47
+ end
48
+
49
+ Blather::Stanza::Iq::Command::VALID_STATUS.each do |valid_status|
50
+ it "provides a helper (#{valid_status}?) for status #{valid_status}" do
51
+ Blather::Stanza::Iq::Command.new.must_respond_to :"#{valid_status}?"
52
+ end
53
+ end
54
+
55
+ Blather::Stanza::Iq::Command::VALID_NOTE_TYPES.each do |valid_note_type|
56
+ it "provides a helper (#{valid_note_type}?) for note_type #{valid_note_type}" do
57
+ Blather::Stanza::Iq::Command.new.must_respond_to :"#{valid_note_type}?"
58
+ end
59
+ end
60
+
61
+ [:cancel, :execute, :complete, :next, :prev].each do |action|
62
+ it "action can be set as \"#{action}\"" do
63
+ c = Blather::Stanza::Iq::Command.new nil, nil, action
64
+ c.action.must_equal action
65
+ end
66
+ end
67
+
68
+ [:get, :set, :result, :error].each do |type|
69
+ it "can be set as \"#{type}\"" do
70
+ c = Blather::Stanza::Iq::Command.new type
71
+ c.type.must_equal type
72
+ end
73
+ end
74
+
75
+ it 'sets type to "result" on reply' do
76
+ c = Blather::Stanza::Iq::Command.new
77
+ c.type.must_equal :set
78
+ reply = c.reply.type.must_equal :result
79
+ end
80
+
81
+ it 'sets type to "result" on reply!' do
82
+ c = Blather::Stanza::Iq::Command.new
83
+ c.type.must_equal :set
84
+ c.reply!
85
+ c.type.must_equal :result
86
+ end
87
+
88
+ it 'removes action on reply' do
89
+ c = Blather::XMPPNode.import parse_stanza(command_xml).root
90
+ c.action.must_equal :execute
91
+ c.reply.action.must_equal nil
92
+ end
93
+
94
+ it 'removes action on reply!' do
95
+ c = Blather::XMPPNode.import parse_stanza(command_xml).root
96
+ c.action.must_equal :execute
97
+ c.reply!
98
+ c.action.must_equal nil
99
+ end
100
+
101
+ it 'can be registered under a namespace' do
102
+ class CommandNs < Blather::Stanza::Iq::Command; register :command_ns, nil, 'command:ns'; end
103
+ Blather::XMPPNode.class_from_registration(:command, 'command:ns').must_equal CommandNs
104
+ c_ns = CommandNs.new
105
+ c_ns.xpath('command').must_be_empty
106
+ c_ns.xpath('ns:command', :ns => 'command:ns').size.must_equal 1
107
+
108
+ c_ns.command
109
+ c_ns.command
110
+ c_ns.xpath('ns:command', :ns => 'command:ns').size.must_equal 1
111
+ end
112
+
113
+ it 'is constructed properly' do
114
+ n = Blather::Stanza::Iq::Command.new :set, "node", :execute
115
+ n.to = 'to@jid.com'
116
+ n.find("/iq[@to='to@jid.com' and @type='set' and @id='#{n.id}']/ns:command[@node='node' and @action='execute']", :ns => Blather::Stanza::Iq::Command.registered_ns).wont_be_empty
117
+ end
118
+
119
+ it 'has an action attribute' do
120
+ n = Blather::Stanza::Iq::Command.new
121
+ n.action.must_equal :execute
122
+ n.action = :cancel
123
+ n.action.must_equal :cancel
124
+ end
125
+
126
+ it 'must default action to :execute on import' do
127
+ n = Blather::XMPPNode.import(parse_stanza(command_xml).root)
128
+ n.action.must_equal :execute
129
+ end
130
+
131
+ it 'has a status attribute' do
132
+ n = Blather::Stanza::Iq::Command.new
133
+ n.status.must_equal :executing
134
+ n.status = :completed
135
+ n.status.must_equal :completed
136
+ end
137
+
138
+ it 'has a sessionid attribute' do
139
+ n = Blather::Stanza::Iq::Command.new
140
+ n.sessionid.must_equal nil
141
+ n.sessionid = "somerandomstring"
142
+ n.sessionid.must_equal Digest::SHA1.hexdigest("somerandomstring")
143
+ end
144
+
145
+ it 'has a sessionid? attribute' do
146
+ n = Blather::Stanza::Iq::Command.new
147
+ n.sessionid?.must_equal false
148
+ n.new_sessionid!
149
+ n.sessionid?.must_equal true
150
+ end
151
+
152
+ it 'has an allowed_actions attribute' do
153
+ n = Blather::XMPPNode.import parse_stanza(command_xml).root
154
+ n.allowed_actions.must_equal [:execute]
155
+ n.allowed_actions = [:next, :prev]
156
+ (n.allowed_actions - [:next, :prev, :execute]).must_be_empty
157
+ n.remove_allowed_actions!
158
+ n.allowed_actions.must_equal [:execute]
159
+ n.allowed_actions += [:next]
160
+ (n.allowed_actions - [:next, :execute]).must_be_empty
161
+
162
+ r = Blather::Stanza::Iq::Command.new
163
+ r.allowed_actions.must_equal [:execute]
164
+ r.allowed_actions += [:prev]
165
+ (r.allowed_actions - [:prev, :execute]).must_be_empty
166
+ end
167
+
168
+ it 'has a primary_allowed_action attribute' do
169
+ n = Blather::XMPPNode.import parse_stanza(command_xml).root
170
+ n.primary_allowed_action.must_equal :execute
171
+ n.primary_allowed_action = :next
172
+ n.primary_allowed_action.must_equal :next
173
+ end
174
+
175
+ it 'has a note_type attribute' do
176
+ n = Blather::Stanza::Iq::Command.new
177
+ n.note_type.must_equal nil
178
+ n.note_type = :info
179
+ n.note_type.must_equal :info
180
+ end
181
+
182
+ it 'has a note_text attribute' do
183
+ n = Blather::Stanza::Iq::Command.new
184
+ n.note_text.must_equal nil
185
+ n.note_text = "Some text"
186
+ n.note_text.must_equal "Some text"
187
+ end
188
+
189
+ it 'makes a form child available' do
190
+ n = Blather::XMPPNode.import(parse_stanza(command_xml).root)
191
+ n.form.fields.size.must_equal 1
192
+ n.form.fields.map { |f| f.class }.uniq.must_equal [Blather::Stanza::X::Field]
193
+ n.form.must_be_instance_of Blather::Stanza::X
194
+
195
+ r = Blather::Stanza::Iq::Command.new
196
+ r.form.type = :form
197
+ r.form.type.must_equal :form
198
+ end
199
+
200
+ it 'ensures the form child is a child of command' do
201
+ r = Blather::Stanza::Iq::Command.new
202
+ r.form
203
+ r.command.xpath('ns:x', :ns => Blather::Stanza::X.registered_ns).wont_be_empty
204
+ r.xpath('ns:x', :ns => Blather::Stanza::X.registered_ns).must_be_empty
205
+ end
206
+ end
@@ -0,0 +1,96 @@
1
+ require File.join(File.dirname(__FILE__), *%w[.. .. .. spec_helper])
2
+
3
+ def vcard_xml
4
+ <<-XML
5
+ <iq type="result" id="blather0007" to="romeo@example.net">
6
+ <vCard xmlns="vcard-temp">
7
+ <NICKNAME>Romeo</NICKNAME>
8
+ </vCard>
9
+ </iq>
10
+ XML
11
+ end
12
+
13
+ describe Blather::Stanza::Iq::Vcard do
14
+ it 'registers itself' do
15
+ Blather::XMPPNode.class_from_registration(:vCard, 'vcard-temp').must_equal Blather::Stanza::Iq::Vcard
16
+ end
17
+
18
+ it 'can be imported' do
19
+ doc = parse_stanza vcard_xml
20
+ query = Blather::XMPPNode.import(doc.root)
21
+ query.must_be_instance_of Blather::Stanza::Iq::Vcard
22
+ query.vcard.must_be_instance_of Blather::Stanza::Iq::Vcard::Vcard
23
+ end
24
+
25
+ it 'ensures a vcard node is present on create' do
26
+ query = Blather::Stanza::Iq::Vcard.new
27
+ query.xpath('ns:vCard', :ns => 'vcard-temp').wont_be_empty
28
+ end
29
+
30
+ it 'ensures a vcard node exists when calling #vcard' do
31
+ query = Blather::Stanza::Iq::Vcard.new
32
+ query.vcard.remove
33
+ query.xpath('ns:vCard', :ns => 'vcard-temp').must_be_empty
34
+
35
+ query.vcard.wont_be_nil
36
+ query.xpath('ns:vCard', :ns => 'vcard-temp').wont_be_empty
37
+ end
38
+
39
+ it 'ensures a vcard node is replaced when calling #vcard=' do
40
+ doc = parse_stanza vcard_xml
41
+ query = Blather::XMPPNode.import(doc.root)
42
+
43
+ new_vcard = Blather::Stanza::Iq::Vcard::Vcard.new
44
+ new_vcard["NICKNAME"] = 'Mercutio'
45
+
46
+ query.vcard = new_vcard
47
+
48
+ query.xpath('ns:vCard', :ns => 'vcard-temp').size.must_equal 1
49
+ query.find_first('ns:vCard/ns:NICKNAME', :ns => 'vcard-temp').content.must_equal 'Mercutio'
50
+ end
51
+ end
52
+
53
+ describe Blather::Stanza::Iq::Vcard::Vcard do
54
+ it 'can set vcard elements' do
55
+ query = Blather::Stanza::Iq::Vcard.new :set
56
+ query.vcard['NICKNAME'] = 'Romeo'
57
+ query.find_first('ns:vCard/ns:NICKNAME', :ns => 'vcard-temp').content.must_equal 'Romeo'
58
+ end
59
+
60
+ it 'can set deep vcard elements' do
61
+ query = Blather::Stanza::Iq::Vcard.new :set
62
+ query.vcard['PHOTO/TYPE'] = 'image/png'
63
+ query.vcard['PHOTO/BINVAL'] = '===='
64
+ query.find_first('ns:vCard/ns:PHOTO', :ns => 'vcard-temp').children.size.must_equal 2
65
+ query.find_first('ns:vCard/ns:PHOTO', :ns => 'vcard-temp').children.detect { |n| n.element_name == 'TYPE' && n.content == 'image/png' }.wont_be_nil
66
+ query.find_first('ns:vCard/ns:PHOTO', :ns => 'vcard-temp').children.detect { |n| n.element_name == 'BINVAL' && n.content == '====' }.wont_be_nil
67
+ end
68
+
69
+ it 'can get vcard elements' do
70
+ query = Blather::Stanza::Iq::Vcard.new :set
71
+ query.vcard['NICKNAME'] = 'Romeo'
72
+ query.vcard['NICKNAME'].must_equal 'Romeo'
73
+ end
74
+
75
+ it 'can get deep vcard elements' do
76
+ query = Blather::Stanza::Iq::Vcard.new :set
77
+ query.vcard['PHOTO/TYPE'] = 'image/png'
78
+ query.vcard['PHOTO/BINVAL'] = '===='
79
+ query.vcard['PHOTO/TYPE'].must_equal 'image/png'
80
+ query.vcard['PHOTO/BINVAL'].must_equal '===='
81
+ end
82
+
83
+ it 'returns nil on vcard elements which does not exist' do
84
+ query = Blather::Stanza::Iq::Vcard.new :set
85
+ query.vcard['NICKNAME'] = 'Romeo'
86
+ query.vcard['FN'].must_be_nil
87
+ end
88
+
89
+ it 'can update vcard elements' do
90
+ doc = parse_stanza vcard_xml
91
+ query = Blather::XMPPNode.import(doc.root)
92
+ query.vcard['NICKNAME'].must_equal 'Romeo'
93
+ query.vcard['NICKNAME'] = 'Mercutio'
94
+ query.vcard['NICKNAME'].must_equal 'Mercutio'
95
+ end
96
+ end