shingara-blather 0.4.9 → 0.4.14

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.
@@ -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