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.
Files changed (150) hide show
  1. data/.autotest +13 -0
  2. data/.gemtest +0 -0
  3. data/.gitignore +19 -0
  4. data/.rspec +3 -0
  5. data/.travis.yml +8 -0
  6. data/CHANGELOG.md +249 -0
  7. data/Gemfile +4 -0
  8. data/Guardfile +5 -0
  9. data/LICENSE +22 -0
  10. data/README.md +413 -0
  11. data/Rakefile +20 -0
  12. data/TODO.md +2 -0
  13. data/blather.gemspec +51 -0
  14. data/examples/certs/README +20 -0
  15. data/examples/certs/ca-bundle.crt +3987 -0
  16. data/examples/echo.rb +19 -0
  17. data/examples/execute.rb +17 -0
  18. data/examples/ping_pong.rb +38 -0
  19. data/examples/print_hierarchy.rb +77 -0
  20. data/examples/rosterprint.rb +15 -0
  21. data/examples/stream_only.rb +28 -0
  22. data/examples/trusted_echo.rb +21 -0
  23. data/examples/xmpp4r/echo.rb +36 -0
  24. data/lib/blather.rb +112 -0
  25. data/lib/blather/cert_store.rb +53 -0
  26. data/lib/blather/client.rb +95 -0
  27. data/lib/blather/client/client.rb +345 -0
  28. data/lib/blather/client/dsl.rb +320 -0
  29. data/lib/blather/client/dsl/pubsub.rb +174 -0
  30. data/lib/blather/core_ext/eventmachine.rb +125 -0
  31. data/lib/blather/core_ext/ipaddr.rb +20 -0
  32. data/lib/blather/errors.rb +69 -0
  33. data/lib/blather/errors/sasl_error.rb +44 -0
  34. data/lib/blather/errors/stanza_error.rb +110 -0
  35. data/lib/blather/errors/stream_error.rb +84 -0
  36. data/lib/blather/file_transfer.rb +107 -0
  37. data/lib/blather/file_transfer/ibb.rb +68 -0
  38. data/lib/blather/file_transfer/s5b.rb +114 -0
  39. data/lib/blather/jid.rb +141 -0
  40. data/lib/blather/roster.rb +118 -0
  41. data/lib/blather/roster_item.rb +146 -0
  42. data/lib/blather/stanza.rb +167 -0
  43. data/lib/blather/stanza/disco.rb +32 -0
  44. data/lib/blather/stanza/disco/capabilities.rb +161 -0
  45. data/lib/blather/stanza/disco/disco_info.rb +205 -0
  46. data/lib/blather/stanza/disco/disco_items.rb +134 -0
  47. data/lib/blather/stanza/iq.rb +144 -0
  48. data/lib/blather/stanza/iq/command.rb +339 -0
  49. data/lib/blather/stanza/iq/ibb.rb +86 -0
  50. data/lib/blather/stanza/iq/ping.rb +50 -0
  51. data/lib/blather/stanza/iq/query.rb +53 -0
  52. data/lib/blather/stanza/iq/roster.rb +185 -0
  53. data/lib/blather/stanza/iq/s5b.rb +208 -0
  54. data/lib/blather/stanza/iq/si.rb +415 -0
  55. data/lib/blather/stanza/iq/vcard.rb +149 -0
  56. data/lib/blather/stanza/message.rb +428 -0
  57. data/lib/blather/stanza/message/muc_user.rb +119 -0
  58. data/lib/blather/stanza/muc/muc_user_base.rb +54 -0
  59. data/lib/blather/stanza/presence.rb +172 -0
  60. data/lib/blather/stanza/presence/c.rb +100 -0
  61. data/lib/blather/stanza/presence/muc.rb +35 -0
  62. data/lib/blather/stanza/presence/muc_user.rb +147 -0
  63. data/lib/blather/stanza/presence/status.rb +218 -0
  64. data/lib/blather/stanza/presence/subscription.rb +100 -0
  65. data/lib/blather/stanza/pubsub.rb +119 -0
  66. data/lib/blather/stanza/pubsub/affiliations.rb +79 -0
  67. data/lib/blather/stanza/pubsub/create.rb +65 -0
  68. data/lib/blather/stanza/pubsub/errors.rb +18 -0
  69. data/lib/blather/stanza/pubsub/event.rb +139 -0
  70. data/lib/blather/stanza/pubsub/items.rb +103 -0
  71. data/lib/blather/stanza/pubsub/publish.rb +103 -0
  72. data/lib/blather/stanza/pubsub/retract.rb +92 -0
  73. data/lib/blather/stanza/pubsub/subscribe.rb +68 -0
  74. data/lib/blather/stanza/pubsub/subscription.rb +135 -0
  75. data/lib/blather/stanza/pubsub/subscriptions.rb +83 -0
  76. data/lib/blather/stanza/pubsub/unsubscribe.rb +84 -0
  77. data/lib/blather/stanza/pubsub_owner.rb +51 -0
  78. data/lib/blather/stanza/pubsub_owner/delete.rb +52 -0
  79. data/lib/blather/stanza/pubsub_owner/purge.rb +52 -0
  80. data/lib/blather/stanza/x.rb +416 -0
  81. data/lib/blather/stream.rb +266 -0
  82. data/lib/blather/stream/client.rb +32 -0
  83. data/lib/blather/stream/component.rb +39 -0
  84. data/lib/blather/stream/features.rb +70 -0
  85. data/lib/blather/stream/features/register.rb +38 -0
  86. data/lib/blather/stream/features/resource.rb +63 -0
  87. data/lib/blather/stream/features/sasl.rb +190 -0
  88. data/lib/blather/stream/features/session.rb +45 -0
  89. data/lib/blather/stream/features/tls.rb +29 -0
  90. data/lib/blather/stream/parser.rb +102 -0
  91. data/lib/blather/version.rb +3 -0
  92. data/lib/blather/xmpp_node.rb +94 -0
  93. data/spec/blather/client/client_spec.rb +687 -0
  94. data/spec/blather/client/dsl/pubsub_spec.rb +492 -0
  95. data/spec/blather/client/dsl_spec.rb +266 -0
  96. data/spec/blather/errors/sasl_error_spec.rb +33 -0
  97. data/spec/blather/errors/stanza_error_spec.rb +129 -0
  98. data/spec/blather/errors/stream_error_spec.rb +108 -0
  99. data/spec/blather/errors_spec.rb +33 -0
  100. data/spec/blather/file_transfer_spec.rb +135 -0
  101. data/spec/blather/jid_spec.rb +87 -0
  102. data/spec/blather/roster_item_spec.rb +134 -0
  103. data/spec/blather/roster_spec.rb +107 -0
  104. data/spec/blather/stanza/discos/disco_info_spec.rb +247 -0
  105. data/spec/blather/stanza/discos/disco_items_spec.rb +154 -0
  106. data/spec/blather/stanza/iq/command_spec.rb +206 -0
  107. data/spec/blather/stanza/iq/ibb_spec.rb +124 -0
  108. data/spec/blather/stanza/iq/ping_spec.rb +45 -0
  109. data/spec/blather/stanza/iq/query_spec.rb +64 -0
  110. data/spec/blather/stanza/iq/roster_spec.rb +139 -0
  111. data/spec/blather/stanza/iq/s5b_spec.rb +57 -0
  112. data/spec/blather/stanza/iq/si_spec.rb +98 -0
  113. data/spec/blather/stanza/iq/vcard_spec.rb +93 -0
  114. data/spec/blather/stanza/iq_spec.rb +61 -0
  115. data/spec/blather/stanza/message/muc_user_spec.rb +152 -0
  116. data/spec/blather/stanza/message_spec.rb +282 -0
  117. data/spec/blather/stanza/presence/c_spec.rb +56 -0
  118. data/spec/blather/stanza/presence/muc_spec.rb +37 -0
  119. data/spec/blather/stanza/presence/muc_user_spec.rb +83 -0
  120. data/spec/blather/stanza/presence/status_spec.rb +144 -0
  121. data/spec/blather/stanza/presence/subscription_spec.rb +102 -0
  122. data/spec/blather/stanza/presence_spec.rb +125 -0
  123. data/spec/blather/stanza/pubsub/affiliations_spec.rb +57 -0
  124. data/spec/blather/stanza/pubsub/create_spec.rb +56 -0
  125. data/spec/blather/stanza/pubsub/event_spec.rb +98 -0
  126. data/spec/blather/stanza/pubsub/items_spec.rb +79 -0
  127. data/spec/blather/stanza/pubsub/publish_spec.rb +83 -0
  128. data/spec/blather/stanza/pubsub/retract_spec.rb +75 -0
  129. data/spec/blather/stanza/pubsub/subscribe_spec.rb +61 -0
  130. data/spec/blather/stanza/pubsub/subscription_spec.rb +97 -0
  131. data/spec/blather/stanza/pubsub/subscriptions_spec.rb +59 -0
  132. data/spec/blather/stanza/pubsub/unsubscribe_spec.rb +74 -0
  133. data/spec/blather/stanza/pubsub_owner/delete_spec.rb +50 -0
  134. data/spec/blather/stanza/pubsub_owner/purge_spec.rb +50 -0
  135. data/spec/blather/stanza/pubsub_owner_spec.rb +27 -0
  136. data/spec/blather/stanza/pubsub_spec.rb +68 -0
  137. data/spec/blather/stanza/x_spec.rb +231 -0
  138. data/spec/blather/stanza_spec.rb +134 -0
  139. data/spec/blather/stream/client_spec.rb +1090 -0
  140. data/spec/blather/stream/component_spec.rb +108 -0
  141. data/spec/blather/stream/parser_spec.rb +152 -0
  142. data/spec/blather/stream/ssl_spec.rb +32 -0
  143. data/spec/blather/xmpp_node_spec.rb +47 -0
  144. data/spec/blather_spec.rb +34 -0
  145. data/spec/fixtures/pubsub.rb +311 -0
  146. data/spec/spec_helper.rb +17 -0
  147. data/yard/templates/default/class/html/handlers.erb +18 -0
  148. data/yard/templates/default/class/setup.rb +10 -0
  149. data/yard/templates/default/class/text/handlers.erb +1 -0
  150. metadata +459 -0
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe Blather::BlatherError do
4
+ it 'is handled by :error' do
5
+ Blather::BlatherError.new.handler_hierarchy.should == [:error]
6
+ end
7
+ end
8
+
9
+ describe 'Blather::ParseError' do
10
+ before { @error = Blather::ParseError.new('</generate-parse-error>"') }
11
+
12
+ it 'is registers with the handler hierarchy' do
13
+ @error.handler_hierarchy.should == [:parse_error, :error]
14
+ end
15
+
16
+ it 'contains the error message' do
17
+ @error.should respond_to :message
18
+ @error.message.should == '</generate-parse-error>"'
19
+ end
20
+ end
21
+
22
+ describe 'Blather::UnknownResponse' do
23
+ before { @error = Blather::UnknownResponse.new(Blather::XMPPNode.new('foo-bar')) }
24
+
25
+ it 'is registers with the handler hierarchy' do
26
+ @error.handler_hierarchy.should == [:unknown_response_error, :error]
27
+ end
28
+
29
+ it 'holds on to a copy of the failure node' do
30
+ @error.should respond_to :node
31
+ @error.node.element_name.should == 'foo-bar'
32
+ end
33
+ end
@@ -0,0 +1,135 @@
1
+ require 'spec_helper'
2
+ require 'blather/client/dsl'
3
+
4
+ module MockFileReceiver
5
+ def post_init
6
+ end
7
+ def receive_data(data)
8
+ end
9
+ def unbind
10
+ end
11
+ def send(data, params)
12
+ end
13
+ end
14
+
15
+ def si_xml
16
+ <<-XML
17
+ <iq type='set' id='offer1' to='juliet@capulet.com/balcony' from='romeo@montague.net/orchard'>
18
+ <si xmlns='http://jabber.org/protocol/si'
19
+ id='a0'
20
+ mime-type='text/plain'
21
+ profile='http://jabber.org/protocol/si/profile/file-transfer'>
22
+ <file xmlns='http://jabber.org/protocol/si/profile/file-transfer'
23
+ name='test.txt'
24
+ size='1022'>
25
+ <range/>
26
+ </file>
27
+ <feature xmlns='http://jabber.org/protocol/feature-neg'>
28
+ <x xmlns='jabber:x:data' type='form'>
29
+ <field var='stream-method' type='list-single'>
30
+ <option><value>http://jabber.org/protocol/bytestreams</value></option>
31
+ <option><value>http://jabber.org/protocol/ibb</value></option>
32
+ </field>
33
+ </x>
34
+ </feature>
35
+ </si>
36
+ </iq>
37
+ XML
38
+ end
39
+
40
+ describe Blather::FileTransfer do
41
+ before do
42
+ @host = 'host.name'
43
+ @client = Blather::Client.setup Blather::JID.new('n@d/r'), 'pass'
44
+ end
45
+
46
+ it 'can select ibb' do
47
+ iq = Blather::XMPPNode.parse(si_xml)
48
+
49
+ @client.stubs(:write).with do |answer|
50
+ answer.si.feature.x.field('stream-method').value.should == Blather::Stanza::Iq::Ibb::NS_IBB
51
+ true
52
+ end
53
+
54
+ transfer = Blather::FileTransfer.new(@client, iq)
55
+ transfer.allow_s5b = false
56
+ transfer.allow_ibb = true
57
+ transfer.accept(MockFileReceiver)
58
+ end
59
+
60
+ it 'can select s5b' do
61
+ iq = Blather::XMPPNode.parse(si_xml)
62
+
63
+ @client.stubs(:write).with do |answer|
64
+ answer.si.feature.x.field('stream-method').value.should == Blather::Stanza::Iq::S5b::NS_S5B
65
+ true
66
+ end
67
+
68
+ transfer = Blather::FileTransfer.new(@client, iq)
69
+ transfer.allow_s5b = true
70
+ transfer.allow_ibb = false
71
+ transfer.accept(MockFileReceiver)
72
+ end
73
+
74
+ it 'can allow s5b private ips' do
75
+ iq = Blather::XMPPNode.parse(si_xml)
76
+
77
+ @client.stubs(:write).with do |answer|
78
+ answer.si.feature.x.field('stream-method').value.should == Blather::Stanza::Iq::S5b::NS_S5B
79
+ true
80
+ end
81
+
82
+ transfer = Blather::FileTransfer.new(@client, iq)
83
+ transfer.allow_s5b = true
84
+ transfer.allow_private_ips = true
85
+ transfer.allow_ibb = false
86
+ transfer.accept(MockFileReceiver)
87
+ end
88
+
89
+ it 'can response no-valid-streams' do
90
+ iq = Blather::XMPPNode.parse(si_xml)
91
+
92
+ @client.stubs(:write).with do |answer|
93
+ answer.find_first('error')['type'].should == "cancel"
94
+ answer.find_first('.//ns:no-valid-streams', :ns => 'http://jabber.org/protocol/si').should_not be_nil
95
+ true
96
+ end
97
+
98
+ transfer = Blather::FileTransfer.new(@client, iq)
99
+ transfer.allow_s5b = false
100
+ transfer.allow_ibb = false
101
+ transfer.accept(MockFileReceiver)
102
+ end
103
+
104
+ it 'can decline transfer' do
105
+ iq = Blather::XMPPNode.parse(si_xml)
106
+
107
+ @client.stubs(:write).with do |answer|
108
+ answer.find_first('error')['type'].should == "cancel"
109
+ answer.find_first('.//ns:forbidden', :ns => 'urn:ietf:params:xml:ns:xmpp-stanzas').should_not be_nil
110
+ answer.find_first('.//ns:text', :ns => 'urn:ietf:params:xml:ns:xmpp-stanzas').content.should == "Offer declined"
111
+ true
112
+ end
113
+
114
+ transfer = Blather::FileTransfer.new(@client, iq)
115
+ transfer.decline
116
+ end
117
+
118
+ it 'can s5b post_init include the handler' do
119
+ class TestS5B < Blather::FileTransfer::S5b::SocketConnection
120
+ def initialize()
121
+ super("0.0.0.0", 1, MockFileReceiver, nil)
122
+ restore_methods
123
+ self.post_init()
124
+ end
125
+
126
+ def self.new(*args)
127
+ allocate.instance_eval do
128
+ initialize(*args)
129
+ self
130
+ end
131
+ end
132
+ end
133
+ lambda { TestS5B.new }.should_not raise_error
134
+ end
135
+ end
@@ -0,0 +1,87 @@
1
+ require 'spec_helper'
2
+
3
+ describe Blather::JID do
4
+ it 'does nothing if creaded from Blather::JID' do
5
+ jid = Blather::JID.new 'n@d/r'
6
+ Blather::JID.new(jid).object_id.should == jid.object_id
7
+ end
8
+
9
+ it 'creates a new Blather::JID from (n,d,r)' do
10
+ jid = Blather::JID.new('n', 'd', 'r')
11
+ jid.node.should == 'n'
12
+ jid.domain.should == 'd'
13
+ jid.resource.should == 'r'
14
+ end
15
+
16
+ it 'creates a new Blather::JID from (n,d)' do
17
+ jid = Blather::JID.new('n', 'd')
18
+ jid.node.should == 'n'
19
+ jid.domain.should == 'd'
20
+ end
21
+
22
+ it 'creates a new Blather::JID from (n@d)' do
23
+ jid = Blather::JID.new('n@d')
24
+ jid.node.should == 'n'
25
+ jid.domain.should == 'd'
26
+ end
27
+
28
+ it 'creates a new Blather::JID from (n@d/r)' do
29
+ jid = Blather::JID.new('n@d/r')
30
+ jid.node.should == 'n'
31
+ jid.domain.should == 'd'
32
+ jid.resource.should == 'r'
33
+ end
34
+
35
+ it 'requires at least a node' do
36
+ proc { Blather::JID.new }.should raise_error ::ArgumentError
37
+ end
38
+
39
+ it 'ensures length of node is no more than 1023 characters' do
40
+ proc { Blather::JID.new('n'*1024) }.should raise_error Blather::ArgumentError
41
+ end
42
+
43
+ it 'ensures length of domain is no more than 1023 characters' do
44
+ proc { Blather::JID.new('n', 'd'*1024) }.should raise_error Blather::ArgumentError
45
+ end
46
+
47
+ it 'ensures length of resource is no more than 1023 characters' do
48
+ proc { Blather::JID.new('n', 'd', 'r'*1024) }.should raise_error Blather::ArgumentError
49
+ end
50
+
51
+ it 'compares Blather::JIDs' do
52
+ (Blather::JID.new('a@b/c') <=> Blather::JID.new('d@e/f')).should == -1
53
+ (Blather::JID.new('a@b/c') <=> Blather::JID.new('a@b/c')).should == 0
54
+ (Blather::JID.new('d@e/f') <=> Blather::JID.new('a@b/c')).should == 1
55
+ end
56
+
57
+ it 'checks for equality' do
58
+ (Blather::JID.new('n@d/r') == Blather::JID.new('n@d/r')).should == true
59
+ Blather::JID.new('n@d/r').eql?(Blather::JID.new('n@d/r')).should == true
60
+ end
61
+
62
+ it 'will strip' do
63
+ jid = Blather::JID.new('n@d/r')
64
+ jid.stripped.should == Blather::JID.new('n@d')
65
+ jid.should == Blather::JID.new('n@d/r')
66
+ end
67
+
68
+ it 'will strip itself' do
69
+ jid = Blather::JID.new('n@d/r')
70
+ jid.strip!
71
+ jid.should == Blather::JID.new('n@d')
72
+ end
73
+
74
+ it 'has a string representation' do
75
+ Blather::JID.new('n@d/r').to_s.should == 'n@d/r'
76
+ Blather::JID.new('n', 'd', 'r').to_s.should == 'n@d/r'
77
+ Blather::JID.new('n', 'd').to_s.should == 'n@d'
78
+ end
79
+
80
+ it 'provides a #stripped? helper' do
81
+ jid = Blather::JID.new 'a@b/c'
82
+ jid.should respond_to :stripped?
83
+ jid.stripped?.should_not equal true
84
+ jid.strip!
85
+ jid.stripped?.should == true
86
+ end
87
+ end
@@ -0,0 +1,134 @@
1
+ require 'spec_helper'
2
+
3
+ describe Blather::RosterItem do
4
+ it 'can be initialized with Blather::JID' do
5
+ jid = Blather::JID.new(jid)
6
+ i = Blather::RosterItem.new jid
7
+ i.jid.should == jid
8
+ end
9
+
10
+ it 'can be initialized with an Iq::RosterItem' do
11
+ jid = 'n@d/r'
12
+ i = Blather::RosterItem.new Blather::Stanza::Iq::Roster::RosterItem.new(jid)
13
+ i.jid.should == Blather::JID.new(jid).stripped
14
+ end
15
+
16
+ it 'can be initialized with a string' do
17
+ jid = 'n@d/r'
18
+ i = Blather::RosterItem.new jid
19
+ i.jid.should == Blather::JID.new(jid).stripped
20
+ end
21
+
22
+ it 'returns the same object when intialized with a Blather::RosterItem' do
23
+ control = Blather::RosterItem.new 'n@d/r'
24
+ Blather::RosterItem.new(control).should be control
25
+ end
26
+
27
+ it 'has a Blather::JID setter that strips the Blather::JID' do
28
+ jid = Blather::JID.new('n@d/r')
29
+ i = Blather::RosterItem.new nil
30
+ i.jid = jid
31
+ i.jid.should == jid.stripped
32
+ end
33
+
34
+ it 'has a subscription setter that forces a symbol' do
35
+ i = Blather::RosterItem.new nil
36
+ i.subscription = 'remove'
37
+ i.subscription.should == :remove
38
+ end
39
+
40
+ it 'forces the type of subscription' do
41
+ proc { Blather::RosterItem.new(nil).subscription = 'foo' }.should raise_error Blather::ArgumentError
42
+ end
43
+
44
+ it 'returns :none if the subscription field is blank' do
45
+ Blather::RosterItem.new(nil).subscription.should == :none
46
+ end
47
+
48
+ it 'ensure #ask is a symbol' do
49
+ i = Blather::RosterItem.new(nil)
50
+ i.ask = 'subscribe'
51
+ i.ask.should == :subscribe
52
+ end
53
+
54
+ it 'forces #ask to be :subscribe or nothing at all' do
55
+ proc { Blather::RosterItem.new(nil).ask = 'foo' }.should raise_error Blather::ArgumentError
56
+ end
57
+
58
+ it 'generates a stanza with #to_stanza' do
59
+ jid = Blather::JID.new('n@d/r')
60
+ i = Blather::RosterItem.new jid
61
+ s = i.to_stanza
62
+ s.should be_kind_of Blather::Stanza::Iq::Roster
63
+ s.items.first.jid.should == jid.stripped
64
+ end
65
+
66
+ it 'returns status based on priority' do
67
+ setup_item_with_presences
68
+ @i.status.should == @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.should == @p3
81
+ end
82
+
83
+ it 'returns status based on resource' do
84
+ setup_item_with_presences
85
+ @i.status('a').should == @p
86
+ end
87
+
88
+ def setup_item_with_presences
89
+ @jid = Blather::JID.new('n@d/r')
90
+ @i = Blather::RosterItem.new @jid
91
+
92
+ @p = Blather::Stanza::Presence::Status.new(:away)
93
+ @p.from = 'n@d/a'
94
+ @p.priority = 0
95
+
96
+ @p2 = Blather::Stanza::Presence::Status.new(:dnd)
97
+ @p2.from = 'n@d/b'
98
+ @p2.priority = -1
99
+
100
+ @p3 = Blather::Stanza::Presence::Status.new(:dnd)
101
+ @p3.from = 'n@d/c'
102
+ @p3.priority = 10
103
+
104
+ @i.status = @p
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.should == 4
120
+ end
121
+
122
+ it 'initializes groups to [nil] if the item is not part of a group' do
123
+ i = Blather::RosterItem.new 'n@d'
124
+ i.groups.should == [nil]
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).should == true
133
+ end
134
+ end
@@ -0,0 +1,107 @@
1
+ require 'spec_helper'
2
+
3
+ describe Blather::Roster do
4
+ before do
5
+ @stream = mock()
6
+ @stream.stubs(:write)
7
+
8
+ @stanza = mock()
9
+ items = []; 4.times { |n| items << Blather::JID.new("n@d/#{n}r") }
10
+ @stanza.stubs(:items).returns(items)
11
+
12
+ @roster = Blather::Roster.new(@stream, @stanza)
13
+ end
14
+
15
+ it 'initializes with items' do
16
+ @roster.items.map { |_,i| i.jid.to_s }.should == (@stanza.items.map { |i| i.stripped.to_s }.uniq)
17
+ end
18
+
19
+ it 'processes @stanzas with remove requests' do
20
+ s = @roster['n@d/0r']
21
+ s.subscription = :remove
22
+ proc { @roster.process(s.to_stanza) }.should change(@roster, :length).by -1
23
+ end
24
+
25
+ it 'processes @stanzas with add requests' do
26
+ s = Blather::Stanza::Iq::Roster::RosterItem.new('a@b/c').to_stanza
27
+ proc { @roster.process(s) }.should change(@roster, :length).by 1
28
+ end
29
+
30
+ it 'allows a jid to be pushed' do
31
+ jid = 'a@b/c'
32
+ proc { @roster.push(jid) }.should change(@roster, :length).by 1
33
+ @roster[jid].should_not be_nil
34
+ end
35
+
36
+ it 'allows an item to be pushed' do
37
+ jid = 'a@b/c'
38
+ item = Blather::RosterItem.new(Blather::JID.new(jid))
39
+ proc { @roster.push(item) }.should change(@roster, :length).by 1
40
+ @roster[jid].should_not be_nil
41
+ end
42
+
43
+ it 'aliases #<< to #push and returns self to allow for chaining' do
44
+ jid = 'a@b/c'
45
+ item = Blather::RosterItem.new(Blather::JID.new(jid))
46
+ jid2 = 'd@e/f'
47
+ item2 = Blather::RosterItem.new(Blather::JID.new(jid2))
48
+ proc { @roster << item << item2 }.should change(@roster, :length).by 2
49
+ @roster[jid].should_not be_nil
50
+ @roster[jid2].should_not be_nil
51
+ end
52
+
53
+ it 'sends a @roster addition over the wire' do
54
+ client = mock(:write => nil)
55
+ roster = Blather::Roster.new client, @stanza
56
+ roster.push('a@b/c')
57
+ end
58
+
59
+ it 'removes a Blather::JID' do
60
+ proc { @roster.delete 'n@d' }.should change(@roster, :length).by -1
61
+ end
62
+
63
+ it 'sends a @roster removal over the wire' do
64
+ client = mock(:write => nil)
65
+ roster = Blather::Roster.new client, @stanza
66
+ roster.delete('a@b/c')
67
+ end
68
+
69
+ it 'returns an item through []' do
70
+ item = @roster['n@d']
71
+ item.should be_kind_of Blather::RosterItem
72
+ item.jid.should == Blather::JID.new('n@d')
73
+ end
74
+
75
+ it 'responds to #each' do
76
+ @roster.should respond_to :each
77
+ end
78
+
79
+ it 'cycles through all the items using #each' do
80
+ @roster.map { |i| i }.sort.should ==(@roster.items.values.sort)
81
+ end
82
+
83
+ it 'yields RosterItems from #each' do
84
+ @roster.map { |i| i.should be_kind_of Blather::RosterItem }
85
+ end
86
+
87
+ it 'returns a duplicate of items through #items' do
88
+ items = @roster.items
89
+ items.delete 'n@d'
90
+ items.should_not equal @roster.items
91
+ end
92
+
93
+ it 'will group roster items' do
94
+ @roster.delete 'n@d'
95
+ item1 = Blather::RosterItem.new("n1@d")
96
+ item1.groups = ['group1', 'group2']
97
+ item2 = Blather::RosterItem.new("n2@d")
98
+ item2.groups = ['group1', 'group3']
99
+ @roster << item1 << item2
100
+
101
+ @roster.grouped.should ==({
102
+ 'group1' => [item1, item2],
103
+ 'group2' => [item1],
104
+ 'group3' => [item2]
105
+ })
106
+ end
107
+ end