tp-blather 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
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