source: wokkel/test/test_pubsub.py @ 222:e1dafcc79586

Last change on this file since 222:e1dafcc79586 was 222:e1dafcc79586, checked in by Ralph Meijer <ralphm@…>, 3 years ago

Merge branch 'master' into item-get-by-ids

  • Property exe set to *
File size: 141.6 KB
Line 
1# Copyright (c) Ralph Meijer.
2# See LICENSE for details.
3
4"""
5Tests for L{wokkel.pubsub}
6"""
7
8from __future__ import division, absolute_import
9
10from zope.interface import verify
11
12from twisted.trial import unittest
13from twisted.internet import defer
14from twisted.words.xish import domish
15from twisted.words.protocols.jabber import error
16from twisted.words.protocols.jabber.jid import JID
17from twisted.words.protocols.jabber.xmlstream import toResponse
18
19from wokkel import data_form, disco, iwokkel, pubsub, shim
20from wokkel.generic import parseXml
21from wokkel.test.helpers import TestableRequestHandlerMixin, XmlStreamStub
22
23NS_PUBSUB = 'http://jabber.org/protocol/pubsub'
24NS_PUBSUB_NODE_CONFIG = 'http://jabber.org/protocol/pubsub#node_config'
25NS_PUBSUB_ERRORS = 'http://jabber.org/protocol/pubsub#errors'
26NS_PUBSUB_EVENT = 'http://jabber.org/protocol/pubsub#event'
27NS_PUBSUB_OWNER = 'http://jabber.org/protocol/pubsub#owner'
28NS_PUBSUB_META_DATA = 'http://jabber.org/protocol/pubsub#meta-data'
29NS_PUBSUB_SUBSCRIBE_OPTIONS = 'http://jabber.org/protocol/pubsub#subscribe_options'
30
31def calledAsync(fn):
32    """
33    Function wrapper that fires a deferred upon calling the given function.
34    """
35    d = defer.Deferred()
36
37    def func(*args, **kwargs):
38        try:
39            result = fn(*args, **kwargs)
40        except:
41            d.errback()
42        else:
43            d.callback(result)
44
45    return d, func
46
47
48class SubscriptionTest(unittest.TestCase):
49    """
50    Tests for L{pubsub.Subscription}.
51    """
52
53    def test_fromElement(self):
54        """
55        fromElement parses a subscription from XML DOM.
56        """
57        xml = """
58        <subscription node='test' jid='user@example.org/Home'
59                      subscription='pending'/>
60        """
61        subscription = pubsub.Subscription.fromElement(parseXml(xml))
62        self.assertEqual('test', subscription.nodeIdentifier)
63        self.assertEqual(JID('user@example.org/Home'), subscription.subscriber)
64        self.assertEqual('pending', subscription.state)
65        self.assertIdentical(None, subscription.subscriptionIdentifier)
66
67
68    def test_fromElementWithSubscriptionIdentifier(self):
69        """
70        A subscription identifier in the subscription should be parsed, too.
71        """
72        xml = """
73        <subscription node='test' jid='user@example.org/Home' subid='1234'
74                      subscription='pending'/>
75        """
76        subscription = pubsub.Subscription.fromElement(parseXml(xml))
77        self.assertEqual('1234', subscription.subscriptionIdentifier)
78
79
80    def test_toElement(self):
81        """
82        Rendering a Subscription should yield the proper attributes.
83        """
84        subscription = pubsub.Subscription('test',
85                                           JID('user@example.org/Home'),
86                                           'pending')
87        element = subscription.toElement()
88        self.assertEqual('subscription', element.name)
89        self.assertEqual(None, element.uri)
90        self.assertEqual('test', element.getAttribute('node'))
91        self.assertEqual('user@example.org/Home', element.getAttribute('jid'))
92        self.assertEqual('pending', element.getAttribute('subscription'))
93        self.assertFalse(element.hasAttribute('subid'))
94
95
96    def test_toElementEmptyNodeIdentifier(self):
97        """
98        The empty node identifier should not yield a node attribute.
99        """
100        subscription = pubsub.Subscription('',
101                                           JID('user@example.org/Home'),
102                                           'pending')
103        element = subscription.toElement()
104        self.assertFalse(element.hasAttribute('node'))
105
106
107    def test_toElementWithSubscriptionIdentifier(self):
108        """
109        The subscription identifier, if set, is in the subid attribute.
110        """
111        subscription = pubsub.Subscription('test',
112                                           JID('user@example.org/Home'),
113                                           'pending',
114                                           subscriptionIdentifier='1234')
115        element = subscription.toElement()
116        self.assertEqual('1234', element.getAttribute('subid'))
117
118
119
120class ItemTests(unittest.TestCase):
121    """
122    Tests for L{pubsub.Item}.
123    """
124
125    def test_payloadRaw(self):
126        """
127        Adding a payload as a string assumes serialized XML.
128        """
129        payload = "<test xmlns='foo'/>"
130        item = pubsub.Item(payload=payload)
131        self.assertEqual(payload, item.children[0])
132
133
134    def test_payloadElement(self):
135        """
136        Adding a payload as an domish Element, just adds that element as child.
137        """
138        payload = domish.Element(('foo', 'test'))
139        item = pubsub.Item(payload=payload)
140        self.assertIs(payload, item.children[0])
141
142
143
144class PubSubClientTest(unittest.TestCase):
145    timeout = 2
146
147    def setUp(self):
148        self.stub = XmlStreamStub()
149        self.protocol = pubsub.PubSubClient()
150        self.protocol.xmlstream = self.stub.xmlstream
151        self.protocol.connectionInitialized()
152
153
154    def test_interface(self):
155        """
156        Do instances of L{pubsub.PubSubClient} provide L{iwokkel.IPubSubClient}?
157        """
158        verify.verifyObject(iwokkel.IPubSubClient, self.protocol)
159
160
161    def test_eventItems(self):
162        """
163        Test receiving an items event resulting in a call to itemsReceived.
164        """
165        message = domish.Element((None, 'message'))
166        message['from'] = 'pubsub.example.org'
167        message['to'] = 'user@example.org/home'
168        event = message.addElement((NS_PUBSUB_EVENT, 'event'))
169        items = event.addElement('items')
170        items['node'] = 'test'
171        item1 = items.addElement('item')
172        item1['id'] = 'item1'
173        item2 = items.addElement('retract')
174        item2['id'] = 'item2'
175        item3 = items.addElement('item')
176        item3['id'] = 'item3'
177
178        def itemsReceived(event):
179            self.assertEquals(JID('user@example.org/home'), event.recipient)
180            self.assertEquals(JID('pubsub.example.org'), event.sender)
181            self.assertEquals('test', event.nodeIdentifier)
182            self.assertEquals([item1, item2, item3], event.items)
183
184        d, self.protocol.itemsReceived = calledAsync(itemsReceived)
185        self.stub.send(message)
186        return d
187
188
189    def test_eventItemsCollection(self):
190        """
191        Test receiving an items event resulting in a call to itemsReceived.
192        """
193        message = domish.Element((None, 'message'))
194        message['from'] = 'pubsub.example.org'
195        message['to'] = 'user@example.org/home'
196        event = message.addElement((NS_PUBSUB_EVENT, 'event'))
197        items = event.addElement('items')
198        items['node'] = 'test'
199
200        headers = shim.Headers([('Collection', 'collection')])
201        message.addChild(headers)
202
203        def itemsReceived(event):
204            self.assertEquals(JID('user@example.org/home'), event.recipient)
205            self.assertEquals(JID('pubsub.example.org'), event.sender)
206            self.assertEquals('test', event.nodeIdentifier)
207            self.assertEquals({'Collection': ['collection']}, event.headers)
208
209        d, self.protocol.itemsReceived = calledAsync(itemsReceived)
210        self.stub.send(message)
211        return d
212
213
214    def test_eventItemsError(self):
215        """
216        An error message with embedded event should not be handled.
217
218        This test uses an items event, which should not result in itemsReceived
219        being called. In general message.handled should be False.
220        """
221        message = domish.Element((None, 'message'))
222        message['from'] = 'pubsub.example.org'
223        message['to'] = 'user@example.org/home'
224        message['type'] = 'error'
225        event = message.addElement((NS_PUBSUB_EVENT, 'event'))
226        items = event.addElement('items')
227        items['node'] = 'test'
228
229        class UnexpectedCall(Exception):
230            pass
231
232        def itemsReceived(event):
233            raise UnexpectedCall("Unexpected call to itemsReceived")
234
235        self.protocol.itemsReceived = itemsReceived
236        self.stub.send(message)
237        self.assertFalse(message.handled)
238
239
240    def test_eventDelete(self):
241        """
242        Test receiving a delete event resulting in a call to deleteReceived.
243        """
244        message = domish.Element((None, 'message'))
245        message['from'] = 'pubsub.example.org'
246        message['to'] = 'user@example.org/home'
247        event = message.addElement((NS_PUBSUB_EVENT, 'event'))
248        delete = event.addElement('delete')
249        delete['node'] = 'test'
250
251        def deleteReceived(event):
252            self.assertEquals(JID('user@example.org/home'), event.recipient)
253            self.assertEquals(JID('pubsub.example.org'), event.sender)
254            self.assertEquals('test', event.nodeIdentifier)
255
256        d, self.protocol.deleteReceived = calledAsync(deleteReceived)
257        self.stub.send(message)
258        return d
259
260
261    def test_eventDeleteRedirect(self):
262        """
263        Test receiving a delete event with a redirect URI.
264        """
265        message = domish.Element((None, 'message'))
266        message['from'] = 'pubsub.example.org'
267        message['to'] = 'user@example.org/home'
268        event = message.addElement((NS_PUBSUB_EVENT, 'event'))
269        delete = event.addElement('delete')
270        delete['node'] = 'test'
271        uri = 'xmpp:pubsub.example.org?;node=test2'
272        delete.addElement('redirect')['uri'] = uri
273
274        def deleteReceived(event):
275            self.assertEquals(JID('user@example.org/home'), event.recipient)
276            self.assertEquals(JID('pubsub.example.org'), event.sender)
277            self.assertEquals('test', event.nodeIdentifier)
278            self.assertEquals(uri, event.redirectURI)
279
280        d, self.protocol.deleteReceived = calledAsync(deleteReceived)
281        self.stub.send(message)
282        return d
283
284
285    def test_event_purge(self):
286        """
287        Test receiving a purge event resulting in a call to purgeReceived.
288        """
289        message = domish.Element((None, 'message'))
290        message['from'] = 'pubsub.example.org'
291        message['to'] = 'user@example.org/home'
292        event = message.addElement((NS_PUBSUB_EVENT, 'event'))
293        items = event.addElement('purge')
294        items['node'] = 'test'
295
296        def purgeReceived(event):
297            self.assertEquals(JID('user@example.org/home'), event.recipient)
298            self.assertEquals(JID('pubsub.example.org'), event.sender)
299            self.assertEquals('test', event.nodeIdentifier)
300
301        d, self.protocol.purgeReceived = calledAsync(purgeReceived)
302        self.stub.send(message)
303        return d
304
305
306    def test_createNode(self):
307        """
308        Test sending create request.
309        """
310
311        def cb(nodeIdentifier):
312            self.assertEquals('test', nodeIdentifier)
313
314        d = self.protocol.createNode(JID('pubsub.example.org'), 'test')
315        d.addCallback(cb)
316
317        iq = self.stub.output[-1]
318        self.assertEquals('pubsub.example.org', iq.getAttribute('to'))
319        self.assertEquals('set', iq.getAttribute('type'))
320        self.assertEquals('pubsub', iq.pubsub.name)
321        self.assertEquals(NS_PUBSUB, iq.pubsub.uri)
322        children = list(domish.generateElementsQNamed(iq.pubsub.children,
323                                                      'create', NS_PUBSUB))
324        self.assertEquals(1, len(children))
325        child = children[0]
326        self.assertEquals('test', child['node'])
327
328        response = toResponse(iq, 'result')
329        self.stub.send(response)
330        return d
331
332
333    def test_createNodeInstant(self):
334        """
335        Test sending create request resulting in an instant node.
336        """
337
338        def cb(nodeIdentifier):
339            self.assertEquals('test', nodeIdentifier)
340
341        d = self.protocol.createNode(JID('pubsub.example.org'))
342        d.addCallback(cb)
343
344        iq = self.stub.output[-1]
345        children = list(domish.generateElementsQNamed(iq.pubsub.children,
346                                                      'create', NS_PUBSUB))
347        child = children[0]
348        self.assertFalse(child.hasAttribute('node'))
349
350        response = toResponse(iq, 'result')
351        command = response.addElement((NS_PUBSUB, 'pubsub'))
352        create = command.addElement('create')
353        create['node'] = 'test'
354        self.stub.send(response)
355        return d
356
357
358    def test_createNodeRenamed(self):
359        """
360        Test sending create request resulting in renamed node.
361        """
362
363        def cb(nodeIdentifier):
364            self.assertEquals('test2', nodeIdentifier)
365
366        d = self.protocol.createNode(JID('pubsub.example.org'), 'test')
367        d.addCallback(cb)
368
369        iq = self.stub.output[-1]
370        children = list(domish.generateElementsQNamed(iq.pubsub.children,
371                                                      'create', NS_PUBSUB))
372        child = children[0]
373        self.assertEquals('test', child['node'])
374
375        response = toResponse(iq, 'result')
376        command = response.addElement((NS_PUBSUB, 'pubsub'))
377        create = command.addElement('create')
378        create['node'] = 'test2'
379        self.stub.send(response)
380        return d
381
382
383    def test_createNodeWithSender(self):
384        """
385        Test sending create request from a specific JID.
386        """
387
388        d = self.protocol.createNode(JID('pubsub.example.org'), 'test',
389                                     sender=JID('user@example.org'))
390
391        iq = self.stub.output[-1]
392        self.assertEquals('user@example.org', iq['from'])
393
394        response = toResponse(iq, 'result')
395        self.stub.send(response)
396        return d
397
398
399    def test_createNodeWithConfig(self):
400        """
401        Test sending create request with configuration options
402        """
403
404        options = {
405            'pubsub#title': 'Princely Musings (Atom)',
406            'pubsub#deliver_payloads': True,
407            'pubsub#persist_items': '1',
408            'pubsub#max_items': '10',
409            'pubsub#access_model': 'open',
410            'pubsub#type': 'http://www.w3.org/2005/Atom',
411        }
412
413        d = self.protocol.createNode(JID('pubsub.example.org'), 'test',
414                                     sender=JID('user@example.org'),
415                                     options=options)
416
417        iq = self.stub.output[-1]
418
419        # check if there is exactly one configure element
420        children = list(domish.generateElementsQNamed(iq.pubsub.children,
421                                                      'configure', NS_PUBSUB))
422        self.assertEqual(1, len(children))
423
424        # check that it has a configuration form
425        form = data_form.findForm(children[0], NS_PUBSUB_NODE_CONFIG)
426        self.assertEqual('submit', form.formType)
427
428
429        response = toResponse(iq, 'result')
430        self.stub.send(response)
431        return d
432
433
434    def test_deleteNode(self):
435        """
436        Test sending delete request.
437        """
438
439        d = self.protocol.deleteNode(JID('pubsub.example.org'), 'test')
440
441        iq = self.stub.output[-1]
442        self.assertEquals('pubsub.example.org', iq.getAttribute('to'))
443        self.assertEquals('set', iq.getAttribute('type'))
444        self.assertEquals('pubsub', iq.pubsub.name)
445        self.assertEquals(NS_PUBSUB_OWNER, iq.pubsub.uri)
446        children = list(domish.generateElementsQNamed(iq.pubsub.children,
447                                                      'delete', NS_PUBSUB_OWNER))
448        self.assertEquals(1, len(children))
449        child = children[0]
450        self.assertEquals('test', child['node'])
451
452        response = toResponse(iq, 'result')
453        self.stub.send(response)
454        return d
455
456
457    def test_deleteNodeWithSender(self):
458        """
459        Test sending delete request.
460        """
461
462        d = self.protocol.deleteNode(JID('pubsub.example.org'), 'test',
463                                     sender=JID('user@example.org'))
464
465        iq = self.stub.output[-1]
466        self.assertEquals('user@example.org', iq['from'])
467
468        response = toResponse(iq, 'result')
469        self.stub.send(response)
470        return d
471
472
473    def test_publish(self):
474        """
475        Test sending publish request.
476        """
477
478        item = pubsub.Item()
479        d = self.protocol.publish(JID('pubsub.example.org'), 'test', [item])
480
481        iq = self.stub.output[-1]
482        self.assertEquals('pubsub.example.org', iq.getAttribute('to'))
483        self.assertEquals('set', iq.getAttribute('type'))
484        self.assertEquals('pubsub', iq.pubsub.name)
485        self.assertEquals(NS_PUBSUB, iq.pubsub.uri)
486        children = list(domish.generateElementsQNamed(iq.pubsub.children,
487                                                      'publish', NS_PUBSUB))
488        self.assertEquals(1, len(children))
489        child = children[0]
490        self.assertEquals('test', child['node'])
491        items = list(domish.generateElementsQNamed(child.children,
492                                                   'item', NS_PUBSUB))
493        self.assertEquals(1, len(items))
494        self.assertIdentical(item, items[0])
495
496        response = toResponse(iq, 'result')
497        self.stub.send(response)
498        return d
499
500
501    def test_publishNoItems(self):
502        """
503        Test sending publish request without items.
504        """
505
506        d = self.protocol.publish(JID('pubsub.example.org'), 'test')
507
508        iq = self.stub.output[-1]
509        self.assertEquals('pubsub.example.org', iq.getAttribute('to'))
510        self.assertEquals('set', iq.getAttribute('type'))
511        self.assertEquals('pubsub', iq.pubsub.name)
512        self.assertEquals(NS_PUBSUB, iq.pubsub.uri)
513        children = list(domish.generateElementsQNamed(iq.pubsub.children,
514                                                      'publish', NS_PUBSUB))
515        self.assertEquals(1, len(children))
516        child = children[0]
517        self.assertEquals('test', child['node'])
518
519        response = toResponse(iq, 'result')
520        self.stub.send(response)
521        return d
522
523
524    def test_publishWithSender(self):
525        """
526        Test sending publish request from a specific JID.
527        """
528
529        item = pubsub.Item()
530        d = self.protocol.publish(JID('pubsub.example.org'), 'test', [item],
531                                  JID('user@example.org'))
532
533        iq = self.stub.output[-1]
534        self.assertEquals('user@example.org', iq['from'])
535
536        response = toResponse(iq, 'result')
537        self.stub.send(response)
538        return d
539
540
541    def test_subscribe(self):
542        """
543        Test sending subscription request.
544        """
545        d = self.protocol.subscribe(JID('pubsub.example.org'), 'test',
546                                      JID('user@example.org'))
547
548        iq = self.stub.output[-1]
549        self.assertEquals('pubsub.example.org', iq.getAttribute('to'))
550        self.assertEquals('set', iq.getAttribute('type'))
551        self.assertEquals('pubsub', iq.pubsub.name)
552        self.assertEquals(NS_PUBSUB, iq.pubsub.uri)
553        children = list(domish.generateElementsQNamed(iq.pubsub.children,
554                                                      'subscribe', NS_PUBSUB))
555        self.assertEquals(1, len(children))
556        child = children[0]
557        self.assertEquals('test', child['node'])
558        self.assertEquals('user@example.org', child['jid'])
559
560        response = toResponse(iq, 'result')
561        pubsub = response.addElement((NS_PUBSUB, 'pubsub'))
562        subscription = pubsub.addElement('subscription')
563        subscription['node'] = 'test'
564        subscription['jid'] = 'user@example.org'
565        subscription['subscription'] = 'subscribed'
566        self.stub.send(response)
567        return d
568
569
570    def test_subscribeReturnsSubscription(self):
571        """
572        A successful subscription should return a Subscription instance.
573        """
574        def cb(subscription):
575            self.assertEqual(JID('user@example.org'), subscription.subscriber)
576
577        d = self.protocol.subscribe(JID('pubsub.example.org'), 'test',
578                                      JID('user@example.org'))
579        d.addCallback(cb)
580
581        iq = self.stub.output[-1]
582
583        response = toResponse(iq, 'result')
584        pubsub = response.addElement((NS_PUBSUB, 'pubsub'))
585        subscription = pubsub.addElement('subscription')
586        subscription['node'] = 'test'
587        subscription['jid'] = 'user@example.org'
588        subscription['subscription'] = 'subscribed'
589        self.stub.send(response)
590        return d
591
592
593    def test_subscribePending(self):
594        """
595        Test sending subscription request that results in a pending
596        subscription.
597        """
598        d = self.protocol.subscribe(JID('pubsub.example.org'), 'test',
599                                      JID('user@example.org'))
600
601        iq = self.stub.output[-1]
602        response = toResponse(iq, 'result')
603        command = response.addElement((NS_PUBSUB, 'pubsub'))
604        subscription = command.addElement('subscription')
605        subscription['node'] = 'test'
606        subscription['jid'] = 'user@example.org'
607        subscription['subscription'] = 'pending'
608        self.stub.send(response)
609        self.assertFailure(d, pubsub.SubscriptionPending)
610        return d
611
612
613    def test_subscribeUnconfigured(self):
614        """
615        Test sending subscription request that results in an unconfigured
616        subscription.
617        """
618        d = self.protocol.subscribe(JID('pubsub.example.org'), 'test',
619                                      JID('user@example.org'))
620
621        iq = self.stub.output[-1]
622        response = toResponse(iq, 'result')
623        command = response.addElement((NS_PUBSUB, 'pubsub'))
624        subscription = command.addElement('subscription')
625        subscription['node'] = 'test'
626        subscription['jid'] = 'user@example.org'
627        subscription['subscription'] = 'unconfigured'
628        self.stub.send(response)
629        self.assertFailure(d, pubsub.SubscriptionUnconfigured)
630        return d
631
632
633    def test_subscribeWithOptions(self):
634        options = {'pubsub#deliver': False}
635
636        d = self.protocol.subscribe(JID('pubsub.example.org'), 'test',
637                                    JID('user@example.org'),
638                                    options=options)
639        iq = self.stub.output[-1]
640
641        # Check options present
642        childNames = []
643        for element in iq.pubsub.elements():
644            if element.uri == NS_PUBSUB:
645                childNames.append(element.name)
646
647        self.assertEqual(['subscribe', 'options'], childNames)
648        form = data_form.findForm(iq.pubsub.options,
649                                  NS_PUBSUB_SUBSCRIBE_OPTIONS)
650        self.assertEqual('submit', form.formType)
651        form.typeCheck({'pubsub#deliver': {'type': 'boolean'}})
652        self.assertEqual(options, form.getValues())
653
654        # Send response
655        response = toResponse(iq, 'result')
656        pubsub = response.addElement((NS_PUBSUB, 'pubsub'))
657        subscription = pubsub.addElement('subscription')
658        subscription['node'] = 'test'
659        subscription['jid'] = 'user@example.org'
660        subscription['subscription'] = 'subscribed'
661        self.stub.send(response)
662
663        return d
664
665
666    def test_subscribeWithSender(self):
667        """
668        Test sending subscription request from a specific JID.
669        """
670        d = self.protocol.subscribe(JID('pubsub.example.org'), 'test',
671                                      JID('user@example.org'),
672                                      sender=JID('user@example.org'))
673
674        iq = self.stub.output[-1]
675        self.assertEquals('user@example.org', iq['from'])
676
677        response = toResponse(iq, 'result')
678        pubsub = response.addElement((NS_PUBSUB, 'pubsub'))
679        subscription = pubsub.addElement('subscription')
680        subscription['node'] = 'test'
681        subscription['jid'] = 'user@example.org'
682        subscription['subscription'] = 'subscribed'
683        self.stub.send(response)
684        return d
685
686
687    def test_subscribeReturningSubscriptionIdentifier(self):
688        """
689        Test sending subscription request with subscription identifier.
690        """
691        def cb(subscription):
692            self.assertEqual('1234', subscription.subscriptionIdentifier)
693
694        d = self.protocol.subscribe(JID('pubsub.example.org'), 'test',
695                                      JID('user@example.org'))
696        d.addCallback(cb)
697
698        iq = self.stub.output[-1]
699
700        response = toResponse(iq, 'result')
701        pubsub = response.addElement((NS_PUBSUB, 'pubsub'))
702        subscription = pubsub.addElement('subscription')
703        subscription['node'] = 'test'
704        subscription['jid'] = 'user@example.org'
705        subscription['subscription'] = 'subscribed'
706        subscription['subid'] = '1234'
707        self.stub.send(response)
708        return d
709
710
711    def test_unsubscribe(self):
712        """
713        Test sending unsubscription request.
714        """
715        d = self.protocol.unsubscribe(JID('pubsub.example.org'), 'test',
716                                      JID('user@example.org'))
717
718        iq = self.stub.output[-1]
719        self.assertEquals('pubsub.example.org', iq.getAttribute('to'))
720        self.assertEquals('set', iq.getAttribute('type'))
721        self.assertEquals('pubsub', iq.pubsub.name)
722        self.assertEquals(NS_PUBSUB, iq.pubsub.uri)
723        children = list(domish.generateElementsQNamed(iq.pubsub.children,
724                                                      'unsubscribe', NS_PUBSUB))
725        self.assertEquals(1, len(children))
726        child = children[0]
727        self.assertEquals('test', child['node'])
728        self.assertEquals('user@example.org', child['jid'])
729
730        self.stub.send(toResponse(iq, 'result'))
731        return d
732
733
734    def test_unsubscribeWithSender(self):
735        """
736        Test sending unsubscription request from a specific JID.
737        """
738        d = self.protocol.unsubscribe(JID('pubsub.example.org'), 'test',
739                                      JID('user@example.org'),
740                                      sender=JID('user@example.org'))
741
742        iq = self.stub.output[-1]
743        self.assertEquals('user@example.org', iq['from'])
744        self.stub.send(toResponse(iq, 'result'))
745        return d
746
747
748    def test_unsubscribeWithSubscriptionIdentifier(self):
749        """
750        Test sending unsubscription request with subscription identifier.
751        """
752        d = self.protocol.unsubscribe(JID('pubsub.example.org'), 'test',
753                                      JID('user@example.org'),
754                                      subscriptionIdentifier='1234')
755
756        iq = self.stub.output[-1]
757        child = iq.pubsub.unsubscribe
758        self.assertEquals('1234', child['subid'])
759
760        self.stub.send(toResponse(iq, 'result'))
761        return d
762
763
764    def test_items(self):
765        """
766        Test sending items request.
767        """
768        def cb(items):
769            self.assertEquals([], items)
770
771        d = self.protocol.items(JID('pubsub.example.org'), 'test')
772        d.addCallback(cb)
773
774        iq = self.stub.output[-1]
775        self.assertEquals('pubsub.example.org', iq.getAttribute('to'))
776        self.assertEquals('get', iq.getAttribute('type'))
777        self.assertEquals('pubsub', iq.pubsub.name)
778        self.assertEquals(NS_PUBSUB, iq.pubsub.uri)
779        children = list(domish.generateElementsQNamed(iq.pubsub.children,
780                                                      'items', NS_PUBSUB))
781        self.assertEquals(1, len(children))
782        child = children[0]
783        self.assertEquals('test', child['node'])
784
785        response = toResponse(iq, 'result')
786        items = response.addElement((NS_PUBSUB, 'pubsub')).addElement('items')
787        items['node'] = 'test'
788
789        self.stub.send(response)
790
791        return d
792
793
794    def test_itemsMaxItems(self):
795        """
796        Test sending items request, with limit on the number of items.
797        """
798        def cb(items):
799            self.assertEquals(2, len(items))
800            self.assertEquals([item1, item2], items)
801
802        d = self.protocol.items(JID('pubsub.example.org'), 'test', maxItems=2)
803        d.addCallback(cb)
804
805        iq = self.stub.output[-1]
806        self.assertEquals('pubsub.example.org', iq.getAttribute('to'))
807        self.assertEquals('get', iq.getAttribute('type'))
808        self.assertEquals('pubsub', iq.pubsub.name)
809        self.assertEquals(NS_PUBSUB, iq.pubsub.uri)
810        children = list(domish.generateElementsQNamed(iq.pubsub.children,
811                                                      'items', NS_PUBSUB))
812        self.assertEquals(1, len(children))
813        child = children[0]
814        self.assertEquals('test', child['node'])
815        self.assertEquals('2', child['max_items'])
816
817        response = toResponse(iq, 'result')
818        items = response.addElement((NS_PUBSUB, 'pubsub')).addElement('items')
819        items['node'] = 'test'
820        item1 = items.addElement('item')
821        item1['id'] = 'item1'
822        item2 = items.addElement('item')
823        item2['id'] = 'item2'
824
825        self.stub.send(response)
826
827        return d
828
829
830    def test_itemsWithItemIdentifiers(self):
831        """
832        Test sending items request with item identifiers.
833        """
834        def cb(items):
835            self.assertEquals(2, len(items))
836            self.assertEquals([item1, item2], items)
837
838        d = self.protocol.items(JID('pubsub.example.org'), 'test',
839                                itemIdentifiers=['item1', 'item2'])
840        d.addCallback(cb)
841
842        iq = self.stub.output[-1]
843        self.assertEquals('pubsub.example.org', iq.getAttribute('to'))
844        self.assertEquals('get', iq.getAttribute('type'))
845        self.assertEquals('pubsub', iq.pubsub.name)
846        self.assertEquals(NS_PUBSUB, iq.pubsub.uri)
847        children = list(domish.generateElementsQNamed(iq.pubsub.children,
848                                                      'items', NS_PUBSUB))
849        self.assertEquals(1, len(children))
850        child = children[0]
851        self.assertEquals('test', child['node'])
852        itemIdentifiers = [item.getAttribute('id') for item in
853                           domish.generateElementsQNamed(child.children, 'item',
854                                                         NS_PUBSUB)]
855        self.assertEquals(['item1', 'item2'], itemIdentifiers)
856
857        response = toResponse(iq, 'result')
858        items = response.addElement((NS_PUBSUB, 'pubsub')).addElement('items')
859        items['node'] = 'test'
860        item1 = items.addElement('item')
861        item1['id'] = 'item1'
862        item2 = items.addElement('item')
863        item2['id'] = 'item2'
864
865        self.stub.send(response)
866
867        return d
868
869
870    def test_itemsWithSubscriptionIdentifier(self):
871        """
872        Test sending items request with a subscription identifier.
873        """
874
875        d = self.protocol.items(JID('pubsub.example.org'), 'test',
876                               subscriptionIdentifier='1234')
877
878        iq = self.stub.output[-1]
879        child = iq.pubsub.items
880        self.assertEquals('1234', child['subid'])
881
882        response = toResponse(iq, 'result')
883        items = response.addElement((NS_PUBSUB, 'pubsub')).addElement('items')
884        items['node'] = 'test'
885
886        self.stub.send(response)
887        return d
888
889
890    def test_itemsWithSender(self):
891        """
892        Test sending items request from a specific JID.
893        """
894
895        d = self.protocol.items(JID('pubsub.example.org'), 'test',
896                               sender=JID('user@example.org'))
897
898        iq = self.stub.output[-1]
899        self.assertEquals('user@example.org', iq['from'])
900
901        response = toResponse(iq, 'result')
902        items = response.addElement((NS_PUBSUB, 'pubsub')).addElement('items')
903        items['node'] = 'test'
904
905        self.stub.send(response)
906        return d
907
908
909    def test_getOptions(self):
910        def cb(form):
911            self.assertEqual('form', form.formType)
912            self.assertEqual(NS_PUBSUB_SUBSCRIBE_OPTIONS, form.formNamespace)
913            field = form.fields['pubsub#deliver']
914            self.assertEqual('boolean', field.fieldType)
915            self.assertIdentical(True, field.value)
916            self.assertEqual('Enable delivery?', field.label)
917
918        d = self.protocol.getOptions(JID('pubsub.example.org'), 'test',
919                                     JID('user@example.org'),
920                                     sender=JID('user@example.org'))
921        d.addCallback(cb)
922
923        iq = self.stub.output[-1]
924        self.assertEqual('pubsub.example.org', iq.getAttribute('to'))
925        self.assertEqual('get', iq.getAttribute('type'))
926        self.assertEqual('pubsub', iq.pubsub.name)
927        self.assertEqual(NS_PUBSUB, iq.pubsub.uri)
928        children = list(domish.generateElementsQNamed(iq.pubsub.children,
929                                                      'options', NS_PUBSUB))
930        self.assertEqual(1, len(children))
931        child = children[0]
932        self.assertEqual('test', child['node'])
933
934        self.assertEqual(0, len(child.children))
935
936        # Send response
937        form = data_form.Form('form', formNamespace=NS_PUBSUB_SUBSCRIBE_OPTIONS)
938        form.addField(data_form.Field('boolean', var='pubsub#deliver',
939                                                 label='Enable delivery?',
940                                                 value=True))
941        response = toResponse(iq, 'result')
942        response.addElement((NS_PUBSUB, 'pubsub'))
943        response.pubsub.addElement('options')
944        response.pubsub.options.addChild(form.toElement())
945        self.stub.send(response)
946
947        return d
948
949
950    def test_getOptionsWithSubscriptionIdentifier(self):
951        """
952        Getting options with a subid should have the subid in the request.
953        """
954
955        d = self.protocol.getOptions(JID('pubsub.example.org'), 'test',
956                                     JID('user@example.org'),
957                                     sender=JID('user@example.org'),
958                                     subscriptionIdentifier='1234')
959
960        iq = self.stub.output[-1]
961        child = iq.pubsub.options
962        self.assertEqual('1234', child['subid'])
963
964        # Send response
965        form = data_form.Form('form', formNamespace=NS_PUBSUB_SUBSCRIBE_OPTIONS)
966        form.addField(data_form.Field('boolean', var='pubsub#deliver',
967                                                 label='Enable delivery?',
968                                                 value=True))
969        response = toResponse(iq, 'result')
970        response.addElement((NS_PUBSUB, 'pubsub'))
971        response.pubsub.addElement('options')
972        response.pubsub.options.addChild(form.toElement())
973        self.stub.send(response)
974
975        return d
976
977
978    def test_setOptions(self):
979        """
980        setOptions should send out a options-set request.
981        """
982        options = {'pubsub#deliver': False}
983
984        d = self.protocol.setOptions(JID('pubsub.example.org'), 'test',
985                                     JID('user@example.org'),
986                                     options,
987                                     sender=JID('user@example.org'))
988
989        iq = self.stub.output[-1]
990        self.assertEqual('pubsub.example.org', iq.getAttribute('to'))
991        self.assertEqual('set', iq.getAttribute('type'))
992        self.assertEqual('pubsub', iq.pubsub.name)
993        self.assertEqual(NS_PUBSUB, iq.pubsub.uri)
994        children = list(domish.generateElementsQNamed(iq.pubsub.children,
995                                                      'options', NS_PUBSUB))
996        self.assertEqual(1, len(children))
997        child = children[0]
998        self.assertEqual('test', child['node'])
999
1000        form = data_form.findForm(child, NS_PUBSUB_SUBSCRIBE_OPTIONS)
1001        self.assertEqual('submit', form.formType)
1002        form.typeCheck({'pubsub#deliver': {'type': 'boolean'}})
1003        self.assertEqual(options, form.getValues())
1004
1005        response = toResponse(iq, 'result')
1006        self.stub.send(response)
1007
1008        return d
1009
1010
1011    def test_setOptionsWithSubscriptionIdentifier(self):
1012        """
1013        setOptions should send out a options-set request with subid.
1014        """
1015        options = {'pubsub#deliver': False}
1016
1017        d = self.protocol.setOptions(JID('pubsub.example.org'), 'test',
1018                                     JID('user@example.org'),
1019                                     options,
1020                                     subscriptionIdentifier='1234',
1021                                     sender=JID('user@example.org'))
1022
1023        iq = self.stub.output[-1]
1024        child = iq.pubsub.options
1025        self.assertEqual('1234', child['subid'])
1026
1027        form = data_form.findForm(child, NS_PUBSUB_SUBSCRIBE_OPTIONS)
1028        self.assertEqual('submit', form.formType)
1029        form.typeCheck({'pubsub#deliver': {'type': 'boolean'}})
1030        self.assertEqual(options, form.getValues())
1031
1032        response = toResponse(iq, 'result')
1033        self.stub.send(response)
1034
1035        return d
1036
1037
1038class PubSubRequestTest(unittest.TestCase):
1039
1040    def test_fromElementUnknown(self):
1041        """
1042        An unknown verb raises NotImplementedError.
1043        """
1044
1045        xml = """
1046        <iq type='set' to='pubsub.example.org'
1047                       from='user@example.org'>
1048          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1049            <non-existing-verb/>
1050          </pubsub>
1051        </iq>
1052        """
1053
1054        self.assertRaises(NotImplementedError,
1055                          pubsub.PubSubRequest.fromElement, parseXml(xml))
1056
1057
1058    def test_fromElementKnownBadCombination(self):
1059        """
1060        Multiple verbs in an unknown configuration raises NotImplementedError.
1061        """
1062
1063        xml = """
1064        <iq type='set' to='pubsub.example.org'
1065                       from='user@example.org'>
1066          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1067             <publish/>
1068             <create/>
1069          </pubsub>
1070        </iq>
1071        """
1072
1073        self.assertRaises(NotImplementedError,
1074                          pubsub.PubSubRequest.fromElement, parseXml(xml))
1075
1076    def test_fromElementPublish(self):
1077        """
1078        Test parsing a publish request.
1079        """
1080
1081        xml = """
1082        <iq type='set' to='pubsub.example.org'
1083                       from='user@example.org'>
1084          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1085            <publish node='test'/>
1086          </pubsub>
1087        </iq>
1088        """
1089
1090        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1091        self.assertEqual('publish', request.verb)
1092        self.assertEqual(JID('user@example.org'), request.sender)
1093        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1094        self.assertEqual('test', request.nodeIdentifier)
1095        self.assertEqual([], request.items)
1096
1097
1098    def test_fromElementPublishItems(self):
1099        """
1100        Test parsing a publish request with items.
1101        """
1102
1103        xml = """
1104        <iq type='set' to='pubsub.example.org'
1105                       from='user@example.org'>
1106          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1107            <publish node='test'>
1108              <item id="item1"/>
1109              <item id="item2"/>
1110            </publish>
1111          </pubsub>
1112        </iq>
1113        """
1114
1115        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1116        self.assertEqual(2, len(request.items))
1117        self.assertEqual(u'item1', request.items[0]["id"])
1118        self.assertEqual(u'item2', request.items[1]["id"])
1119
1120
1121    def test_fromElementPublishItemsOptions(self):
1122        """
1123        Test parsing a publish request with items and options.
1124
1125        Note that publishing options are not supported, but passing them
1126        shouldn't affect processing of the publish request itself.
1127        """
1128
1129        xml = """
1130        <iq type='set' to='pubsub.example.org'
1131                       from='user@example.org'>
1132          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1133            <publish node='test'>
1134              <item id="item1"/>
1135              <item id="item2"/>
1136            </publish>
1137            <publish-options/>
1138          </pubsub>
1139        </iq>
1140        """
1141
1142        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1143        self.assertEqual(2, len(request.items))
1144        self.assertEqual(u'item1', request.items[0]["id"])
1145        self.assertEqual(u'item2', request.items[1]["id"])
1146
1147    def test_fromElementPublishNoNode(self):
1148        """
1149        A publish request to the root node should raise an exception.
1150        """
1151        xml = """
1152        <iq type='set' to='pubsub.example.org'
1153                       from='user@example.org'>
1154          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1155            <publish/>
1156          </pubsub>
1157        </iq>
1158        """
1159
1160        err = self.assertRaises(error.StanzaError,
1161                                pubsub.PubSubRequest.fromElement,
1162                                parseXml(xml))
1163        self.assertEqual('bad-request', err.condition)
1164        self.assertEqual(NS_PUBSUB_ERRORS, err.appCondition.uri)
1165        self.assertEqual('nodeid-required', err.appCondition.name)
1166
1167
1168    def test_fromElementSubscribe(self):
1169        """
1170        Test parsing a subscription request.
1171        """
1172
1173        xml = """
1174        <iq type='set' to='pubsub.example.org'
1175                       from='user@example.org'>
1176          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1177            <subscribe node='test' jid='user@example.org/Home'/>
1178          </pubsub>
1179        </iq>
1180        """
1181
1182        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1183        self.assertEqual('subscribe', request.verb)
1184        self.assertEqual(JID('user@example.org'), request.sender)
1185        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1186        self.assertEqual('test', request.nodeIdentifier)
1187        self.assertEqual(JID('user@example.org/Home'), request.subscriber)
1188
1189
1190    def test_fromElementSubscribeEmptyNode(self):
1191        """
1192        Test parsing a subscription request to the root node.
1193        """
1194
1195        xml = """
1196        <iq type='set' to='pubsub.example.org'
1197                       from='user@example.org'>
1198          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1199            <subscribe jid='user@example.org/Home'/>
1200          </pubsub>
1201        </iq>
1202        """
1203
1204        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1205        self.assertEqual('', request.nodeIdentifier)
1206
1207
1208    def test_fromElementSubscribeNoJID(self):
1209        """
1210        Subscribe requests without a JID should raise a bad-request exception.
1211        """
1212        xml = """
1213        <iq type='set' to='pubsub.example.org'
1214                       from='user@example.org'>
1215          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1216            <subscribe node='test'/>
1217          </pubsub>
1218        </iq>
1219        """
1220        err = self.assertRaises(error.StanzaError,
1221                                pubsub.PubSubRequest.fromElement,
1222                                parseXml(xml))
1223        self.assertEqual('bad-request', err.condition)
1224        self.assertEqual(NS_PUBSUB_ERRORS, err.appCondition.uri)
1225        self.assertEqual('jid-required', err.appCondition.name)
1226
1227
1228    def test_fromElementSubscribeWithOptions(self):
1229        """
1230        Test parsing a subscription request.
1231        """
1232
1233        xml = """
1234        <iq type='set' to='pubsub.example.org'
1235                       from='user@example.org'>
1236          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1237            <subscribe node='test' jid='user@example.org/Home'/>
1238            <options>
1239              <x xmlns="jabber:x:data" type='submit'>
1240                <field var='FORM_TYPE' type='hidden'>
1241                  <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
1242                </field>
1243                <field var='pubsub#deliver' type='boolean'
1244                       label='Enable delivery?'>
1245                  <value>1</value>
1246                </field>
1247              </x>
1248            </options>
1249          </pubsub>
1250        </iq>
1251        """
1252
1253        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1254        self.assertEqual('subscribe', request.verb)
1255        request.options.typeCheck({'pubsub#deliver': {'type': 'boolean'}})
1256        self.assertEqual({'pubsub#deliver': True}, request.options.getValues())
1257
1258
1259    def test_fromElementSubscribeWithOptionsBadFormType(self):
1260        """
1261        The options form should have the right type.
1262        """
1263
1264        xml = """
1265        <iq type='set' to='pubsub.example.org'
1266                       from='user@example.org'>
1267          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1268            <subscribe node='test' jid='user@example.org/Home'/>
1269            <options>
1270              <x xmlns="jabber:x:data" type='result'>
1271                <field var='FORM_TYPE' type='hidden'>
1272                  <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
1273                </field>
1274                <field var='pubsub#deliver' type='boolean'
1275                       label='Enable delivery?'>
1276                  <value>1</value>
1277                </field>
1278              </x>
1279            </options>
1280          </pubsub>
1281        </iq>
1282        """
1283
1284        err = self.assertRaises(error.StanzaError,
1285                                pubsub.PubSubRequest.fromElement,
1286                                parseXml(xml))
1287        self.assertEqual('bad-request', err.condition)
1288        self.assertEqual("Unexpected form type 'result'", err.text)
1289        self.assertEqual(None, err.appCondition)
1290
1291
1292    def test_fromElementSubscribeWithOptionsEmpty(self):
1293        """
1294        When no (suitable) form is found, the options are empty.
1295        """
1296
1297        xml = """
1298        <iq type='set' to='pubsub.example.org'
1299                       from='user@example.org'>
1300          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1301            <subscribe node='test' jid='user@example.org/Home'/>
1302            <options/>
1303          </pubsub>
1304        </iq>
1305        """
1306
1307        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1308        self.assertEqual('subscribe', request.verb)
1309        self.assertEqual({}, request.options.getValues())
1310
1311
1312    def test_fromElementUnsubscribe(self):
1313        """
1314        Test parsing an unsubscription request.
1315        """
1316
1317        xml = """
1318        <iq type='set' to='pubsub.example.org'
1319                       from='user@example.org'>
1320          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1321            <unsubscribe node='test' jid='user@example.org/Home'/>
1322          </pubsub>
1323        </iq>
1324        """
1325
1326        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1327        self.assertEqual('unsubscribe', request.verb)
1328        self.assertEqual(JID('user@example.org'), request.sender)
1329        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1330        self.assertEqual('test', request.nodeIdentifier)
1331        self.assertEqual(JID('user@example.org/Home'), request.subscriber)
1332
1333
1334    def test_fromElementUnsubscribeWithSubscriptionIdentifier(self):
1335        """
1336        Test parsing an unsubscription request with subscription identifier.
1337        """
1338
1339        xml = """
1340        <iq type='set' to='pubsub.example.org'
1341                       from='user@example.org'>
1342          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1343            <unsubscribe node='test' jid='user@example.org/Home'
1344                         subid='1234'/>
1345          </pubsub>
1346        </iq>
1347        """
1348
1349        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1350        self.assertEqual('1234', request.subscriptionIdentifier)
1351
1352
1353    def test_fromElementUnsubscribeNoJID(self):
1354        """
1355        Unsubscribe requests without a JID should raise a bad-request exception.
1356        """
1357        xml = """
1358        <iq type='set' to='pubsub.example.org'
1359                       from='user@example.org'>
1360          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1361            <unsubscribe node='test'/>
1362          </pubsub>
1363        </iq>
1364        """
1365        err = self.assertRaises(error.StanzaError,
1366                                pubsub.PubSubRequest.fromElement,
1367                                parseXml(xml))
1368        self.assertEqual('bad-request', err.condition)
1369        self.assertEqual(NS_PUBSUB_ERRORS, err.appCondition.uri)
1370        self.assertEqual('jid-required', err.appCondition.name)
1371
1372
1373    def test_fromElementOptionsGet(self):
1374        """
1375        Test parsing a request for getting subscription options.
1376        """
1377
1378        xml = """
1379        <iq type='get' to='pubsub.example.org'
1380                       from='user@example.org'>
1381          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1382            <options node='test' jid='user@example.org/Home'/>
1383          </pubsub>
1384        </iq>
1385        """
1386
1387        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1388        self.assertEqual('optionsGet', request.verb)
1389        self.assertEqual(JID('user@example.org'), request.sender)
1390        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1391        self.assertEqual('test', request.nodeIdentifier)
1392        self.assertEqual(JID('user@example.org/Home'), request.subscriber)
1393
1394
1395    def test_fromElementOptionsGetWithSubscriptionIdentifier(self):
1396        """
1397        Test parsing a request for getting subscription options with subid.
1398        """
1399
1400        xml = """
1401        <iq type='get' to='pubsub.example.org'
1402                       from='user@example.org'>
1403          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1404            <options node='test' jid='user@example.org/Home'
1405                     subid='1234'/>
1406          </pubsub>
1407        </iq>
1408        """
1409
1410        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1411        self.assertEqual('1234', request.subscriptionIdentifier)
1412
1413
1414    def test_fromElementOptionsSet(self):
1415        """
1416        Test parsing a request for setting subscription options.
1417        """
1418
1419        xml = """
1420        <iq type='set' to='pubsub.example.org'
1421                       from='user@example.org'>
1422          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1423            <options node='test' jid='user@example.org/Home'>
1424              <x xmlns='jabber:x:data' type='submit'>
1425                <field var='FORM_TYPE' type='hidden'>
1426                  <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
1427                </field>
1428                <field var='pubsub#deliver'><value>1</value></field>
1429              </x>
1430            </options>
1431          </pubsub>
1432        </iq>
1433        """
1434
1435        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1436        self.assertEqual('optionsSet', request.verb)
1437        self.assertEqual(JID('user@example.org'), request.sender)
1438        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1439        self.assertEqual('test', request.nodeIdentifier)
1440        self.assertEqual(JID('user@example.org/Home'), request.subscriber)
1441        self.assertEqual({'pubsub#deliver': '1'}, request.options.getValues())
1442
1443
1444    def test_fromElementOptionsSetWithSubscriptionIdentifier(self):
1445        """
1446        Test parsing a request for setting subscription options with subid.
1447        """
1448
1449        xml = """
1450        <iq type='set' to='pubsub.example.org'
1451                       from='user@example.org'>
1452          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1453            <options node='test' jid='user@example.org/Home'
1454                     subid='1234'>
1455              <x xmlns='jabber:x:data' type='submit'>
1456                <field var='FORM_TYPE' type='hidden'>
1457                  <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
1458                </field>
1459                <field var='pubsub#deliver'><value>1</value></field>
1460              </x>
1461            </options>
1462          </pubsub>
1463        </iq>
1464        """
1465
1466        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1467        self.assertEqual('1234', request.subscriptionIdentifier)
1468
1469
1470    def test_fromElementOptionsSetCancel(self):
1471        """
1472        Test parsing a request for cancelling setting subscription options.
1473        """
1474
1475        xml = """
1476        <iq type='set' to='pubsub.example.org'
1477                       from='user@example.org'>
1478          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1479            <options node='test' jid='user@example.org/Home'>
1480              <x xmlns='jabber:x:data' type='cancel'/>
1481            </options>
1482          </pubsub>
1483        </iq>
1484        """
1485
1486        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1487        self.assertEqual('cancel', request.options.formType)
1488
1489
1490    def test_fromElementOptionsSetBadFormType(self):
1491        """
1492        On a options set request unknown fields should be ignored.
1493        """
1494
1495        xml = """
1496        <iq type='set' to='pubsub.example.org'
1497                       from='user@example.org'>
1498          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1499            <options node='test' jid='user@example.org/Home'>
1500              <x xmlns='jabber:x:data' type='result'>
1501                <field var='FORM_TYPE' type='hidden'>
1502                  <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
1503                </field>
1504                <field var='pubsub#deliver'><value>1</value></field>
1505              </x>
1506            </options>
1507          </pubsub>
1508        </iq>
1509        """
1510
1511        err = self.assertRaises(error.StanzaError,
1512                                pubsub.PubSubRequest.fromElement,
1513                                parseXml(xml))
1514        self.assertEqual('bad-request', err.condition)
1515        self.assertEqual("Unexpected form type 'result'", err.text)
1516        self.assertEqual(None, err.appCondition)
1517
1518
1519    def test_fromElementOptionsSetNoForm(self):
1520        """
1521        On a options set request a form is required.
1522        """
1523
1524        xml = """
1525        <iq type='set' to='pubsub.example.org'
1526                       from='user@example.org'>
1527          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1528            <options node='test' jid='user@example.org/Home'/>
1529          </pubsub>
1530        </iq>
1531        """
1532        err = self.assertRaises(error.StanzaError,
1533                                pubsub.PubSubRequest.fromElement,
1534                                parseXml(xml))
1535        self.assertEqual('bad-request', err.condition)
1536        self.assertEqual(None, err.appCondition)
1537
1538
1539    def test_fromElementSubscriptions(self):
1540        """
1541        Test parsing a request for all subscriptions.
1542        """
1543
1544        xml = """
1545        <iq type='get' to='pubsub.example.org'
1546                       from='user@example.org'>
1547          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1548            <subscriptions/>
1549          </pubsub>
1550        </iq>
1551        """
1552
1553        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1554        self.assertEqual('subscriptions', request.verb)
1555        self.assertEqual(JID('user@example.org'), request.sender)
1556        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1557
1558
1559    def test_fromElementAffiliations(self):
1560        """
1561        Test parsing a request for all affiliations.
1562        """
1563
1564        xml = """
1565        <iq type='get' to='pubsub.example.org'
1566                       from='user@example.org'>
1567          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1568            <affiliations/>
1569          </pubsub>
1570        </iq>
1571        """
1572
1573        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1574        self.assertEqual('affiliations', request.verb)
1575        self.assertEqual(JID('user@example.org'), request.sender)
1576        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1577
1578
1579    def test_fromElementCreate(self):
1580        """
1581        Test parsing a request to create a node.
1582        """
1583
1584        xml = """
1585        <iq type='set' to='pubsub.example.org'
1586                       from='user@example.org'>
1587          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1588            <create node='mynode'/>
1589          </pubsub>
1590        </iq>
1591        """
1592
1593        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1594        self.assertEqual('create', request.verb)
1595        self.assertEqual(JID('user@example.org'), request.sender)
1596        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1597        self.assertEqual('mynode', request.nodeIdentifier)
1598        self.assertIdentical(None, request.options)
1599
1600
1601    def test_fromElementCreateInstant(self):
1602        """
1603        Test parsing a request to create an instant node.
1604        """
1605
1606        xml = """
1607        <iq type='set' to='pubsub.example.org'
1608                       from='user@example.org'>
1609          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1610            <create/>
1611          </pubsub>
1612        </iq>
1613        """
1614
1615        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1616        self.assertIdentical(None, request.nodeIdentifier)
1617
1618
1619    def test_fromElementCreateConfigureEmpty(self):
1620        """
1621        Test parsing a request to create a node with an empty configuration.
1622        """
1623
1624        xml = """
1625        <iq type='set' to='pubsub.example.org'
1626                       from='user@example.org'>
1627          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1628            <create node='mynode'/>
1629            <configure/>
1630          </pubsub>
1631        </iq>
1632        """
1633
1634        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1635        self.assertEqual({}, request.options.getValues())
1636        self.assertEqual(u'mynode', request.nodeIdentifier)
1637
1638
1639    def test_fromElementCreateConfigureEmptyWrongOrder(self):
1640        """
1641        Test parsing a request to create a node and configure, wrong order.
1642
1643        The C{configure} element should come after the C{create} request,
1644        but we should accept both orders.
1645        """
1646
1647        xml = """
1648        <iq type='set' to='pubsub.example.org'
1649                       from='user@example.org'>
1650          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1651            <configure/>
1652            <create node='mynode'/>
1653          </pubsub>
1654        </iq>
1655        """
1656
1657        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1658        self.assertEqual({}, request.options.getValues())
1659        self.assertEqual(u'mynode', request.nodeIdentifier)
1660
1661
1662    def test_fromElementCreateConfigure(self):
1663        """
1664        Test parsing a request to create a node.
1665        """
1666
1667        xml = """
1668        <iq type='set' to='pubsub.example.org'
1669                       from='user@example.org'>
1670          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1671            <create node='mynode'/>
1672            <configure>
1673              <x xmlns='jabber:x:data' type='submit'>
1674                <field var='FORM_TYPE' type='hidden'>
1675                  <value>http://jabber.org/protocol/pubsub#node_config</value>
1676                </field>
1677                <field var='pubsub#access_model'><value>open</value></field>
1678                <field var='pubsub#persist_items'><value>0</value></field>
1679              </x>
1680            </configure>
1681          </pubsub>
1682        </iq>
1683        """
1684
1685        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1686        values = request.options
1687        self.assertIn('pubsub#access_model', values)
1688        self.assertEqual(u'open', values['pubsub#access_model'])
1689        self.assertIn('pubsub#persist_items', values)
1690        self.assertEqual(u'0', values['pubsub#persist_items'])
1691
1692
1693    def test_fromElementCreateConfigureBadFormType(self):
1694        """
1695        The form of a node creation request should have the right type.
1696        """
1697
1698        xml = """
1699        <iq type='set' to='pubsub.example.org'
1700                       from='user@example.org'>
1701          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1702            <create node='mynode'/>
1703            <configure>
1704              <x xmlns='jabber:x:data' type='result'>
1705                <field var='FORM_TYPE' type='hidden'>
1706                  <value>http://jabber.org/protocol/pubsub#node_config</value>
1707                </field>
1708                <field var='pubsub#access_model'><value>open</value></field>
1709                <field var='pubsub#persist_items'><value>0</value></field>
1710              </x>
1711            </configure>
1712          </pubsub>
1713        </iq>
1714        """
1715
1716        err = self.assertRaises(error.StanzaError,
1717                                pubsub.PubSubRequest.fromElement,
1718                                parseXml(xml))
1719        self.assertEqual('bad-request', err.condition)
1720        self.assertEqual("Unexpected form type 'result'", err.text)
1721        self.assertEqual(None, err.appCondition)
1722
1723
1724    def test_fromElementDefault(self):
1725        """
1726        Parsing default node configuration request sets required attributes.
1727
1728        Besides C{verb}, C{sender} and C{recipient}, we expect C{nodeType}
1729        to be set. If not passed it receives the default C{u'leaf'}.
1730        """
1731
1732        xml = """
1733        <iq type='get' to='pubsub.example.org'
1734                       from='user@example.org'>
1735          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
1736            <default/>
1737          </pubsub>
1738        </iq>
1739        """
1740
1741        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1742        self.assertEquals(u'default', request.verb)
1743        self.assertEquals(JID('user@example.org'), request.sender)
1744        self.assertEquals(JID('pubsub.example.org'), request.recipient)
1745        self.assertEquals(u'leaf', request.nodeType)
1746
1747
1748    def test_fromElementDefaultCollection(self):
1749        """
1750        Parsing default request for collection sets nodeType to collection.
1751        """
1752
1753        xml = """
1754        <iq type='get' to='pubsub.example.org'
1755                       from='user@example.org'>
1756          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
1757            <default>
1758              <x xmlns='jabber:x:data' type='submit'>
1759                <field var='FORM_TYPE' type='hidden'>
1760                  <value>http://jabber.org/protocol/pubsub#node_config</value>
1761                </field>
1762                <field var='pubsub#node_type'>
1763                  <value>collection</value>
1764                </field>
1765              </x>
1766            </default>
1767
1768          </pubsub>
1769        </iq>
1770        """
1771
1772        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1773        self.assertEquals('collection', request.nodeType)
1774
1775
1776    def test_fromElementConfigureGet(self):
1777        """
1778        Test parsing a node configuration get request.
1779        """
1780
1781        xml = """
1782        <iq type='get' to='pubsub.example.org'
1783                       from='user@example.org'>
1784          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
1785            <configure node='test'/>
1786          </pubsub>
1787        </iq>
1788        """
1789
1790        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1791        self.assertEqual('configureGet', request.verb)
1792        self.assertEqual(JID('user@example.org'), request.sender)
1793        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1794        self.assertEqual('test', request.nodeIdentifier)
1795
1796
1797    def test_fromElementConfigureSet(self):
1798        """
1799        On a node configuration set request the Data Form is parsed.
1800        """
1801
1802        xml = """
1803        <iq type='set' to='pubsub.example.org'
1804                       from='user@example.org'>
1805          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
1806            <configure node='test'>
1807              <x xmlns='jabber:x:data' type='submit'>
1808                <field var='FORM_TYPE' type='hidden'>
1809                  <value>http://jabber.org/protocol/pubsub#node_config</value>
1810                </field>
1811                <field var='pubsub#deliver_payloads'><value>0</value></field>
1812                <field var='pubsub#persist_items'><value>1</value></field>
1813              </x>
1814            </configure>
1815          </pubsub>
1816        </iq>
1817        """
1818
1819        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1820        self.assertEqual('configureSet', request.verb)
1821        self.assertEqual(JID('user@example.org'), request.sender)
1822        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1823        self.assertEqual('test', request.nodeIdentifier)
1824        self.assertEqual({'pubsub#deliver_payloads': '0',
1825                          'pubsub#persist_items': '1'},
1826                         request.options.getValues())
1827
1828
1829    def test_fromElementConfigureSetCancel(self):
1830        """
1831        The node configuration is cancelled, so no options.
1832        """
1833
1834        xml = """
1835        <iq type='set' to='pubsub.example.org'
1836                       from='user@example.org'>
1837          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
1838            <configure node='test'>
1839              <x xmlns='jabber:x:data' type='cancel'/>
1840            </configure>
1841          </pubsub>
1842        </iq>
1843        """
1844
1845        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1846        self.assertEqual('cancel', request.options.formType)
1847
1848
1849    def test_fromElementConfigureSetBadFormType(self):
1850        """
1851        The form of a node configuraton set request should have the right type.
1852        """
1853
1854        xml = """
1855        <iq type='set' to='pubsub.example.org'
1856                       from='user@example.org'>
1857          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
1858            <configure node='test'>
1859              <x xmlns='jabber:x:data' type='result'>
1860                <field var='FORM_TYPE' type='hidden'>
1861                  <value>http://jabber.org/protocol/pubsub#node_config</value>
1862                </field>
1863                <field var='pubsub#deliver_payloads'><value>0</value></field>
1864                <field var='x-myfield'><value>1</value></field>
1865              </x>
1866            </configure>
1867          </pubsub>
1868        </iq>
1869        """
1870
1871        err = self.assertRaises(error.StanzaError,
1872                                pubsub.PubSubRequest.fromElement,
1873                                parseXml(xml))
1874        self.assertEqual('bad-request', err.condition)
1875        self.assertEqual("Unexpected form type 'result'", err.text)
1876        self.assertEqual(None, err.appCondition)
1877
1878
1879    def test_fromElementConfigureSetNoForm(self):
1880        """
1881        On a node configuration set request a form is required.
1882        """
1883
1884        xml = """
1885        <iq type='set' to='pubsub.example.org'
1886                       from='user@example.org'>
1887          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
1888            <configure node='test'/>
1889          </pubsub>
1890        </iq>
1891        """
1892        err = self.assertRaises(error.StanzaError,
1893                                pubsub.PubSubRequest.fromElement,
1894                                parseXml(xml))
1895        self.assertEqual('bad-request', err.condition)
1896        self.assertEqual(None, err.appCondition)
1897
1898
1899    def test_fromElementItems(self):
1900        """
1901        Test parsing an items request.
1902        """
1903        xml = """
1904        <iq type='get' to='pubsub.example.org'
1905                       from='user@example.org'>
1906          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1907            <items node='test'/>
1908          </pubsub>
1909        </iq>
1910        """
1911
1912        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1913        self.assertEqual('items', request.verb)
1914        self.assertEqual(JID('user@example.org'), request.sender)
1915        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1916        self.assertEqual('test', request.nodeIdentifier)
1917        self.assertIdentical(None, request.maxItems)
1918        self.assertIdentical(None, request.subscriptionIdentifier)
1919        self.assertEqual([], request.itemIdentifiers)
1920
1921
1922    def test_fromElementItemsSubscriptionIdentifier(self):
1923        """
1924        Test parsing an items request with subscription identifier.
1925        """
1926        xml = """
1927        <iq type='get' to='pubsub.example.org'
1928                       from='user@example.org'>
1929          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1930            <items node='test' subid='1234'/>
1931          </pubsub>
1932        </iq>
1933        """
1934
1935        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1936        self.assertEqual('1234', request.subscriptionIdentifier)
1937
1938
1939    def test_fromElementRetract(self):
1940        """
1941        Test parsing a retract request.
1942        """
1943
1944        xml = """
1945        <iq type='set' to='pubsub.example.org'
1946                       from='user@example.org'>
1947          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1948            <retract node='test'>
1949              <item id='item1'/>
1950              <item id='item2'/>
1951            </retract>
1952          </pubsub>
1953        </iq>
1954        """
1955
1956        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1957        self.assertEqual('retract', request.verb)
1958        self.assertEqual(JID('user@example.org'), request.sender)
1959        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1960        self.assertEqual('test', request.nodeIdentifier)
1961        self.assertEqual(['item1', 'item2'], request.itemIdentifiers)
1962
1963
1964    def test_fromElementPurge(self):
1965        """
1966        Test parsing a purge request.
1967        """
1968
1969        xml = """
1970        <iq type='set' to='pubsub.example.org'
1971                       from='user@example.org'>
1972          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
1973            <purge node='test'/>
1974          </pubsub>
1975        </iq>
1976        """
1977
1978        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1979        self.assertEqual('purge', request.verb)
1980        self.assertEqual(JID('user@example.org'), request.sender)
1981        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1982        self.assertEqual('test', request.nodeIdentifier)
1983
1984
1985    def test_fromElementDelete(self):
1986        """
1987        Test parsing a delete request.
1988        """
1989
1990        xml = """
1991        <iq type='set' to='pubsub.example.org'
1992                       from='user@example.org'>
1993          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
1994            <delete node='test'/>
1995          </pubsub>
1996        </iq>
1997        """
1998
1999        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
2000        self.assertEqual('delete', request.verb)
2001        self.assertEqual(JID('user@example.org'), request.sender)
2002        self.assertEqual(JID('pubsub.example.org'), request.recipient)
2003        self.assertEqual('test', request.nodeIdentifier)
2004
2005
2006
2007class PubSubServiceTest(unittest.TestCase, TestableRequestHandlerMixin):
2008    """
2009    Tests for L{pubsub.PubSubService}.
2010    """
2011
2012    def setUp(self):
2013        self.stub = XmlStreamStub()
2014        self.resource = pubsub.PubSubResource()
2015        self.service = pubsub.PubSubService(self.resource)
2016        self.service.send = self.stub.xmlstream.send
2017
2018    def test_interface(self):
2019        """
2020        Do instances of L{pubsub.PubSubService} provide L{iwokkel.IPubSubService}?
2021        """
2022        verify.verifyObject(iwokkel.IPubSubService, self.service)
2023
2024
2025    def test_interfaceIDisco(self):
2026        """
2027        Do instances of L{pubsub.PubSubService} provide L{iwokkel.IDisco}?
2028        """
2029        verify.verifyObject(iwokkel.IDisco, self.service)
2030
2031
2032    def test_connectionMade(self):
2033        """
2034        Verify setup of observers in L{pubsub.connectionMade}.
2035        """
2036        requests = []
2037
2038        def handleRequest(iq):
2039            requests.append(iq)
2040
2041        self.service.xmlstream = self.stub.xmlstream
2042        self.service.handleRequest = handleRequest
2043        self.service.connectionMade()
2044
2045        for namespace in (NS_PUBSUB, NS_PUBSUB_OWNER):
2046            for stanzaType in ('get', 'set'):
2047                iq = domish.Element((None, 'iq'))
2048                iq['type'] = stanzaType
2049                iq.addElement((namespace, 'pubsub'))
2050                self.stub.xmlstream.dispatch(iq)
2051
2052        self.assertEqual(4, len(requests))
2053
2054
2055    def test_getDiscoInfo(self):
2056        """
2057        Test getDiscoInfo calls getNodeInfo and returns some minimal info.
2058        """
2059        def cb(info):
2060            discoInfo = disco.DiscoInfo()
2061            for item in info:
2062                discoInfo.append(item)
2063            self.assertIn(('pubsub', 'service'), discoInfo.identities)
2064            self.assertIn(disco.NS_DISCO_ITEMS, discoInfo.features)
2065
2066        d = self.service.getDiscoInfo(JID('user@example.org/home'),
2067                                      JID('pubsub.example.org'), '')
2068        d.addCallback(cb)
2069        return d
2070
2071
2072    def test_getDiscoInfoNodeType(self):
2073        """
2074        Test getDiscoInfo with node type.
2075        """
2076        def cb(info):
2077            discoInfo = disco.DiscoInfo()
2078            for item in info:
2079                discoInfo.append(item)
2080            self.assertIn(('pubsub', 'collection'), discoInfo.identities)
2081
2082        def getInfo(requestor, target, nodeIdentifier):
2083            return defer.succeed({'type': 'collection',
2084                                  'meta-data': {}})
2085
2086        self.resource.getInfo = getInfo
2087        d = self.service.getDiscoInfo(JID('user@example.org/home'),
2088                                      JID('pubsub.example.org'), '')
2089        d.addCallback(cb)
2090        return d
2091
2092
2093    def test_getDiscoInfoMetaData(self):
2094        """
2095        Test getDiscoInfo with returned meta data.
2096        """
2097        def cb(info):
2098            discoInfo = disco.DiscoInfo()
2099            for item in info:
2100                discoInfo.append(item)
2101
2102            self.assertIn(('pubsub', 'leaf'), discoInfo.identities)
2103            self.assertIn(NS_PUBSUB_META_DATA, discoInfo.extensions)
2104            form = discoInfo.extensions[NS_PUBSUB_META_DATA]
2105            self.assertIn('pubsub#node_type', form.fields)
2106
2107        def getInfo(requestor, target, nodeIdentifier):
2108            metaData = [{'var': 'pubsub#persist_items',
2109                         'label': 'Persist items to storage',
2110                         'value': True}]
2111            return defer.succeed({'type': 'leaf', 'meta-data': metaData})
2112
2113        self.resource.getInfo = getInfo
2114        d = self.service.getDiscoInfo(JID('user@example.org/home'),
2115                                      JID('pubsub.example.org'), '')
2116        d.addCallback(cb)
2117        return d
2118
2119
2120    def test_getDiscoInfoResourceFeatures(self):
2121        """
2122        Test getDiscoInfo with the resource features.
2123        """
2124        def cb(info):
2125            discoInfo = disco.DiscoInfo()
2126            for item in info:
2127                discoInfo.append(item)
2128            self.assertIn('http://jabber.org/protocol/pubsub#publish',
2129                          discoInfo.features)
2130
2131        self.resource.features = ['publish']
2132        d = self.service.getDiscoInfo(JID('user@example.org/home'),
2133                                      JID('pubsub.example.org'), '')
2134        d.addCallback(cb)
2135        return d
2136
2137
2138    def test_getDiscoInfoBadResponse(self):
2139        """
2140        If getInfo returns invalid response, it should be logged, then ignored.
2141        """
2142        def cb(info):
2143            self.assertEquals([], info)
2144            self.assertEqual(1, len(self.flushLoggedErrors(TypeError)))
2145
2146        def getInfo(requestor, target, nodeIdentifier):
2147            return defer.succeed('bad response')
2148
2149        self.resource.getInfo = getInfo
2150        d = self.service.getDiscoInfo(JID('user@example.org/home'),
2151                                      JID('pubsub.example.org'), 'test')
2152        d.addCallback(cb)
2153        return d
2154
2155
2156    def test_getDiscoInfoException(self):
2157        """
2158        If getInfo returns invalid response, it should be logged, then ignored.
2159        """
2160        def cb(info):
2161            self.assertEquals([], info)
2162            self.assertEqual(1, len(self.flushLoggedErrors(NotImplementedError)))
2163
2164        def getInfo(requestor, target, nodeIdentifier):
2165            return defer.fail(NotImplementedError())
2166
2167        self.resource.getInfo = getInfo
2168        d = self.service.getDiscoInfo(JID('user@example.org/home'),
2169                                      JID('pubsub.example.org'), 'test')
2170        d.addCallback(cb)
2171        return d
2172
2173
2174    def test_getDiscoItemsRoot(self):
2175        """
2176        Test getDiscoItems on the root node.
2177        """
2178        def getNodes(requestor, service, nodeIdentifier):
2179            return defer.succeed(['node1', 'node2'])
2180
2181        def cb(items):
2182            self.assertEqual(2, len(items))
2183            item1, item2 = items
2184
2185            self.assertEqual(JID('pubsub.example.org'), item1.entity)
2186            self.assertEqual('node1', item1.nodeIdentifier)
2187
2188            self.assertEqual(JID('pubsub.example.org'), item2.entity)
2189            self.assertEqual('node2', item2.nodeIdentifier)
2190
2191        self.resource.getNodes = getNodes
2192        d = self.service.getDiscoItems(JID('user@example.org/home'),
2193                                       JID('pubsub.example.org'),
2194                                       '')
2195        d.addCallback(cb)
2196        return d
2197
2198
2199    def test_getDiscoItemsRootHideNodes(self):
2200        """
2201        Test getDiscoItems on the root node.
2202        """
2203        def getNodes(requestor, service, nodeIdentifier):
2204            raise Exception("Unexpected call to getNodes")
2205
2206        def cb(items):
2207            self.assertEqual([], items)
2208
2209        self.service.hideNodes = True
2210        self.resource.getNodes = getNodes
2211        d = self.service.getDiscoItems(JID('user@example.org/home'),
2212                                       JID('pubsub.example.org'),
2213                                       '')
2214        d.addCallback(cb)
2215        return d
2216
2217
2218    def test_getDiscoItemsNonRoot(self):
2219        """
2220        Test getDiscoItems on a non-root node.
2221        """
2222        def getNodes(requestor, service, nodeIdentifier):
2223            return defer.succeed(['node1', 'node2'])
2224
2225        def cb(items):
2226            self.assertEqual(2, len(items))
2227
2228        self.resource.getNodes = getNodes
2229        d = self.service.getDiscoItems(JID('user@example.org/home'),
2230                                       JID('pubsub.example.org'),
2231                                       'test')
2232        d.addCallback(cb)
2233        return d
2234
2235
2236    def test_on_publish(self):
2237        """
2238        A publish request should result in L{PubSubService.publish} being
2239        called.
2240        """
2241
2242        xml = """
2243        <iq type='set' to='pubsub.example.org'
2244                       from='user@example.org'>
2245          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2246            <publish node='test'/>
2247          </pubsub>
2248        </iq>
2249        """
2250
2251        def publish(request):
2252            return defer.succeed(None)
2253
2254        self.resource.publish = publish
2255        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2256        return self.handleRequest(xml)
2257
2258
2259    def test_on_subscribe(self):
2260        """
2261        A successful subscription should return the current subscription.
2262        """
2263
2264        xml = """
2265        <iq type='set' to='pubsub.example.org'
2266                       from='user@example.org'>
2267          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2268            <subscribe node='test' jid='user@example.org/Home'/>
2269          </pubsub>
2270        </iq>
2271        """
2272
2273        def subscribe(request):
2274            return defer.succeed(pubsub.Subscription(request.nodeIdentifier,
2275                                                     request.subscriber,
2276                                                     'subscribed'))
2277
2278        def cb(element):
2279            self.assertEqual('pubsub', element.name)
2280            self.assertEqual(NS_PUBSUB, element.uri)
2281            subscription = element.subscription
2282            self.assertEqual(NS_PUBSUB, subscription.uri)
2283            self.assertEqual('test', subscription['node'])
2284            self.assertEqual('user@example.org/Home', subscription['jid'])
2285            self.assertEqual('subscribed', subscription['subscription'])
2286
2287        self.resource.subscribe = subscribe
2288        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2289        d = self.handleRequest(xml)
2290        d.addCallback(cb)
2291        return d
2292
2293
2294    def test_on_subscribeEmptyNode(self):
2295        """
2296        A successful subscription on root node should return no node attribute.
2297        """
2298
2299        xml = """
2300        <iq type='set' to='pubsub.example.org'
2301                       from='user@example.org'>
2302          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2303            <subscribe jid='user@example.org/Home'/>
2304          </pubsub>
2305        </iq>
2306        """
2307
2308        def subscribe(request):
2309            return defer.succeed(pubsub.Subscription(request.nodeIdentifier,
2310                                                     request.subscriber,
2311                                                     'subscribed'))
2312
2313        def cb(element):
2314            self.assertFalse(element.subscription.hasAttribute('node'))
2315
2316        self.resource.subscribe = subscribe
2317        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2318        d = self.handleRequest(xml)
2319        d.addCallback(cb)
2320        return d
2321
2322
2323    def test_on_subscribeSubscriptionIdentifier(self):
2324        """
2325        If a subscription returns a subid, this should be available.
2326        """
2327
2328        xml = """
2329        <iq type='set' to='pubsub.example.org'
2330                       from='user@example.org'>
2331          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2332            <subscribe node='test' jid='user@example.org/Home'/>
2333          </pubsub>
2334        </iq>
2335        """
2336
2337        def subscribe(request):
2338            subscription = pubsub.Subscription(request.nodeIdentifier,
2339                                               request.subscriber,
2340                                               'subscribed',
2341                                               subscriptionIdentifier='1234')
2342            return defer.succeed(subscription)
2343
2344        def cb(element):
2345            self.assertEqual('1234', element.subscription.getAttribute('subid'))
2346
2347        self.resource.subscribe = subscribe
2348        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2349        d = self.handleRequest(xml)
2350        d.addCallback(cb)
2351        return d
2352
2353
2354    def test_on_unsubscribe(self):
2355        """
2356        A successful unsubscription should return an empty response.
2357        """
2358
2359        xml = """
2360        <iq type='set' to='pubsub.example.org'
2361                       from='user@example.org'>
2362          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2363            <unsubscribe node='test' jid='user@example.org/Home'/>
2364          </pubsub>
2365        </iq>
2366        """
2367
2368        def unsubscribe(request):
2369            return defer.succeed(None)
2370
2371        def cb(element):
2372            self.assertIdentical(None, element)
2373
2374        self.resource.unsubscribe = unsubscribe
2375        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2376        d = self.handleRequest(xml)
2377        d.addCallback(cb)
2378        return d
2379
2380
2381    def test_on_unsubscribeSubscriptionIdentifier(self):
2382        """
2383        A successful unsubscription with subid should return an empty response.
2384        """
2385
2386        xml = """
2387        <iq type='set' to='pubsub.example.org'
2388                       from='user@example.org'>
2389          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2390            <unsubscribe node='test' jid='user@example.org/Home' subid='1234'/>
2391          </pubsub>
2392        </iq>
2393        """
2394
2395        def unsubscribe(request):
2396            self.assertEqual('1234', request.subscriptionIdentifier)
2397            return defer.succeed(None)
2398
2399        def cb(element):
2400            self.assertIdentical(None, element)
2401
2402        self.resource.unsubscribe = unsubscribe
2403        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2404        d = self.handleRequest(xml)
2405        d.addCallback(cb)
2406        return d
2407
2408
2409    def test_on_optionsGet(self):
2410        """
2411        Getting subscription options is not supported.
2412        """
2413
2414        xml = """
2415        <iq type='get' to='pubsub.example.org'
2416                       from='user@example.org'>
2417          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2418            <options node='test' jid='user@example.org/Home'/>
2419          </pubsub>
2420        </iq>
2421        """
2422
2423        def cb(result):
2424            self.assertEquals('feature-not-implemented', result.condition)
2425            self.assertEquals('unsupported', result.appCondition.name)
2426            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
2427
2428        d = self.handleRequest(xml)
2429        self.assertFailure(d, error.StanzaError)
2430        d.addCallback(cb)
2431        return d
2432
2433
2434    def test_on_optionsSet(self):
2435        """
2436        Setting subscription options is not supported.
2437        """
2438
2439        xml = """
2440        <iq type='set' to='pubsub.example.org'
2441                       from='user@example.org'>
2442          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2443            <options node='test' jid='user@example.org/Home'>
2444              <x xmlns='jabber:x:data' type='submit'>
2445                <field var='FORM_TYPE' type='hidden'>
2446                  <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
2447                </field>
2448                <field var='pubsub#deliver'><value>1</value></field>
2449              </x>
2450            </options>
2451          </pubsub>
2452        </iq>
2453        """
2454
2455        def cb(result):
2456            self.assertEquals('feature-not-implemented', result.condition)
2457            self.assertEquals('unsupported', result.appCondition.name)
2458            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
2459
2460        d = self.handleRequest(xml)
2461        self.assertFailure(d, error.StanzaError)
2462        d.addCallback(cb)
2463        return d
2464
2465
2466    def test_on_subscriptions(self):
2467        """
2468        A subscriptions request should result in
2469        L{PubSubService.subscriptions} being called and the result prepared
2470        for the response.
2471        """
2472
2473        xml = """
2474        <iq type='get' to='pubsub.example.org'
2475                       from='user@example.org'>
2476          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2477            <subscriptions/>
2478          </pubsub>
2479        </iq>
2480        """
2481
2482        def subscriptions(request):
2483            subscription = pubsub.Subscription('test', JID('user@example.org'),
2484                                               'subscribed')
2485            return defer.succeed([subscription])
2486
2487        def cb(element):
2488            self.assertEqual('pubsub', element.name)
2489            self.assertEqual(NS_PUBSUB, element.uri)
2490            self.assertEqual(NS_PUBSUB, element.subscriptions.uri)
2491            children = list(element.subscriptions.elements())
2492            self.assertEqual(1, len(children))
2493            subscription = children[0]
2494            self.assertEqual('subscription', subscription.name)
2495            self.assertEqual(NS_PUBSUB, subscription.uri, NS_PUBSUB)
2496            self.assertEqual('user@example.org', subscription['jid'])
2497            self.assertEqual('test', subscription['node'])
2498            self.assertEqual('subscribed', subscription['subscription'])
2499
2500        self.resource.subscriptions = subscriptions
2501        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2502        d = self.handleRequest(xml)
2503        d.addCallback(cb)
2504        return d
2505
2506
2507    def test_on_subscriptionsWithSubscriptionIdentifier(self):
2508        """
2509        A subscriptions request response should include subids, if set.
2510        """
2511
2512        xml = """
2513        <iq type='get' to='pubsub.example.org'
2514                       from='user@example.org'>
2515          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2516            <subscriptions/>
2517          </pubsub>
2518        </iq>
2519        """
2520
2521        def subscriptions(request):
2522            subscription = pubsub.Subscription('test', JID('user@example.org'),
2523                                               'subscribed',
2524                                               subscriptionIdentifier='1234')
2525            return defer.succeed([subscription])
2526
2527        def cb(element):
2528            subscription = element.subscriptions.subscription
2529            self.assertEqual('1234', subscription['subid'])
2530
2531        self.resource.subscriptions = subscriptions
2532        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2533        d = self.handleRequest(xml)
2534        d.addCallback(cb)
2535        return d
2536
2537
2538    def test_on_affiliations(self):
2539        """
2540        A subscriptions request should result in
2541        L{PubSubService.affiliations} being called and the result prepared
2542        for the response.
2543        """
2544
2545        xml = """
2546        <iq type='get' to='pubsub.example.org'
2547                       from='user@example.org'>
2548          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2549            <affiliations/>
2550          </pubsub>
2551        </iq>
2552        """
2553
2554        def affiliations(request):
2555            affiliation = ('test', 'owner')
2556            return defer.succeed([affiliation])
2557
2558        def cb(element):
2559            self.assertEqual('pubsub', element.name)
2560            self.assertEqual(NS_PUBSUB, element.uri)
2561            self.assertEqual(NS_PUBSUB, element.affiliations.uri)
2562            children = list(element.affiliations.elements())
2563            self.assertEqual(1, len(children))
2564            affiliation = children[0]
2565            self.assertEqual('affiliation', affiliation.name)
2566            self.assertEqual(NS_PUBSUB, affiliation.uri)
2567            self.assertEqual('test', affiliation['node'])
2568            self.assertEqual('owner', affiliation['affiliation'])
2569
2570        self.resource.affiliations = affiliations
2571        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2572        d = self.handleRequest(xml)
2573        d.addCallback(cb)
2574        return d
2575
2576
2577    def test_on_create(self):
2578        """
2579        Replies to create node requests don't return the created node.
2580        """
2581
2582        xml = """
2583        <iq type='set' to='pubsub.example.org'
2584                       from='user@example.org'>
2585          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2586            <create node='mynode'/>
2587          </pubsub>
2588        </iq>
2589        """
2590
2591        def create(request):
2592            return defer.succeed(request.nodeIdentifier)
2593
2594        def cb(element):
2595            self.assertIdentical(None, element)
2596
2597        self.resource.create = create
2598        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2599        d = self.handleRequest(xml)
2600        d.addCallback(cb)
2601        return d
2602
2603
2604    def test_on_createChanged(self):
2605        """
2606        Replies to create node requests return the created node if changed.
2607        """
2608
2609        xml = """
2610        <iq type='set' to='pubsub.example.org'
2611                       from='user@example.org'>
2612          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2613            <create node='mynode'/>
2614          </pubsub>
2615        </iq>
2616        """
2617
2618        def create(request):
2619            return defer.succeed(u'myrenamednode')
2620
2621        def cb(element):
2622            self.assertEqual('pubsub', element.name)
2623            self.assertEqual(NS_PUBSUB, element.uri)
2624            self.assertEqual(NS_PUBSUB, element.create.uri)
2625            self.assertEqual(u'myrenamednode',
2626                             element.create.getAttribute('node'))
2627
2628        self.resource.create = create
2629        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2630        d = self.handleRequest(xml)
2631        d.addCallback(cb)
2632        return d
2633
2634
2635    def test_on_createInstant(self):
2636        """
2637        Replies to create instant node requests return the created node.
2638        """
2639
2640        xml = """
2641        <iq type='set' to='pubsub.example.org'
2642                       from='user@example.org'>
2643          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2644            <create/>
2645          </pubsub>
2646        </iq>
2647        """
2648
2649        def create(request):
2650            return defer.succeed(u'random')
2651
2652        def cb(element):
2653            self.assertEqual('pubsub', element.name)
2654            self.assertEqual(NS_PUBSUB, element.uri)
2655            self.assertEqual(NS_PUBSUB, element.create.uri)
2656            self.assertEqual(u'random', element.create.getAttribute('node'))
2657
2658        self.resource.create = create
2659        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2660        d = self.handleRequest(xml)
2661        d.addCallback(cb)
2662        return d
2663
2664
2665    def test_on_createWithConfig(self):
2666        """
2667        On a node create with configuration request the Data Form is parsed and
2668        L{PubSubResource.create} is called with the passed options.
2669        """
2670
2671        xml = """
2672        <iq type='set' to='pubsub.example.org'
2673                       from='user@example.org'>
2674          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2675            <create node='mynode'/>
2676            <configure>
2677              <x xmlns='jabber:x:data' type='submit'>
2678                <field var='FORM_TYPE' type='hidden'>
2679                  <value>http://jabber.org/protocol/pubsub#node_config</value>
2680                </field>
2681                <field var='pubsub#deliver_payloads'><value>0</value></field>
2682                <field var='pubsub#persist_items'><value>1</value></field>
2683              </x>
2684            </configure>
2685          </pubsub>
2686        </iq>
2687        """
2688
2689        def getConfigurationOptions():
2690            return {
2691                "pubsub#persist_items":
2692                    {"type": "boolean",
2693                     "label": "Persist items to storage"},
2694                "pubsub#deliver_payloads":
2695                    {"type": "boolean",
2696                     "label": "Deliver payloads with event notifications"}
2697                }
2698
2699        def create(request):
2700            self.assertEqual({'pubsub#deliver_payloads': False,
2701                              'pubsub#persist_items': True},
2702                             request.options.getValues())
2703            return defer.succeed(None)
2704
2705        self.resource.getConfigurationOptions = getConfigurationOptions
2706        self.resource.create = create
2707        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2708        return self.handleRequest(xml)
2709
2710
2711    def test_on_default(self):
2712        """
2713        A default request returns default options filtered by available fields.
2714        """
2715
2716        xml = """
2717        <iq type='get' to='pubsub.example.org'
2718                       from='user@example.org'>
2719          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
2720            <default/>
2721          </pubsub>
2722        </iq>
2723        """
2724        fieldDefs = {
2725                "pubsub#persist_items":
2726                    {"type": "boolean",
2727                     "label": "Persist items to storage"},
2728                "pubsub#deliver_payloads":
2729                    {"type": "boolean",
2730                     "label": "Deliver payloads with event notifications"}
2731                }
2732
2733        def getConfigurationOptions():
2734            return fieldDefs
2735
2736        def default(request):
2737            return defer.succeed({'pubsub#persist_items': 'false',
2738                                  'x-myfield': '1'})
2739
2740        def cb(element):
2741            self.assertEquals('pubsub', element.name)
2742            self.assertEquals(NS_PUBSUB_OWNER, element.uri)
2743            self.assertEquals(NS_PUBSUB_OWNER, element.default.uri)
2744            form = data_form.Form.fromElement(element.default.x)
2745            self.assertEquals(NS_PUBSUB_NODE_CONFIG, form.formNamespace)
2746            form.typeCheck(fieldDefs)
2747            self.assertIn('pubsub#persist_items', form.fields)
2748            self.assertFalse(form.fields['pubsub#persist_items'].value)
2749            self.assertNotIn('x-myfield', form.fields)
2750
2751        self.resource.getConfigurationOptions = getConfigurationOptions
2752        self.resource.default = default
2753        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2754        d = self.handleRequest(xml)
2755        d.addCallback(cb)
2756        return d
2757
2758
2759    def test_on_defaultUnknownNodeType(self):
2760        """
2761        Unknown node types yield non-acceptable.
2762
2763        Both C{getConfigurationOptions} and C{default} must not be called.
2764        """
2765
2766        xml = """
2767        <iq type='get' to='pubsub.example.org'
2768                       from='user@example.org'>
2769          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
2770            <default>
2771              <x xmlns='jabber:x:data' type='submit'>
2772                <field var='FORM_TYPE' type='hidden'>
2773                  <value>http://jabber.org/protocol/pubsub#node_config</value>
2774                </field>
2775                <field var='pubsub#node_type'>
2776                  <value>unknown</value>
2777                </field>
2778              </x>
2779            </default>
2780
2781          </pubsub>
2782        </iq>
2783        """
2784
2785        def getConfigurationOptions():
2786            self.fail("Unexpected call to getConfigurationOptions")
2787
2788        def default(request):
2789            self.fail("Unexpected call to default")
2790
2791        def cb(result):
2792            self.assertEquals('not-acceptable', result.condition)
2793
2794        self.resource.getConfigurationOptions = getConfigurationOptions
2795        self.resource.default = default
2796        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2797        d = self.handleRequest(xml)
2798        self.assertFailure(d, error.StanzaError)
2799        d.addCallback(cb)
2800        return d
2801
2802
2803    def test_on_configureGet(self):
2804        """
2805        On a node configuration get
2806        requestL{PubSubResource.configureGet} is called and results in a
2807        data form with the configuration.
2808        """
2809
2810        xml = """
2811        <iq type='get' to='pubsub.example.org'
2812                       from='user@example.org'>
2813          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
2814            <configure node='test'/>
2815          </pubsub>
2816        </iq>
2817        """
2818
2819        def getConfigurationOptions():
2820            return {
2821                "pubsub#persist_items":
2822                    {"type": "boolean",
2823                     "label": "Persist items to storage"},
2824                "pubsub#deliver_payloads":
2825                    {"type": "boolean",
2826                     "label": "Deliver payloads with event notifications"},
2827                "pubsub#owner":
2828                    {"type": "jid-single",
2829                     "label": "Owner of the node"}
2830                }
2831
2832        def configureGet(request):
2833            return defer.succeed({'pubsub#deliver_payloads': '0',
2834                                  'pubsub#persist_items': '1',
2835                                  'pubsub#owner': JID('user@example.org'),
2836                                  'x-myfield': 'a'})
2837
2838        def cb(element):
2839            self.assertEqual('pubsub', element.name)
2840            self.assertEqual(NS_PUBSUB_OWNER, element.uri)
2841            self.assertEqual(NS_PUBSUB_OWNER, element.configure.uri)
2842            form = data_form.Form.fromElement(element.configure.x)
2843            self.assertEqual(NS_PUBSUB_NODE_CONFIG, form.formNamespace)
2844            fields = form.fields
2845
2846            self.assertIn('pubsub#deliver_payloads', fields)
2847            field = fields['pubsub#deliver_payloads']
2848            self.assertEqual('boolean', field.fieldType)
2849            field.typeCheck()
2850            self.assertEqual(False, field.value)
2851
2852            self.assertIn('pubsub#persist_items', fields)
2853            field = fields['pubsub#persist_items']
2854            self.assertEqual('boolean', field.fieldType)
2855            field.typeCheck()
2856            self.assertEqual(True, field.value)
2857
2858            self.assertIn('pubsub#owner', fields)
2859            field = fields['pubsub#owner']
2860            self.assertEqual('jid-single', field.fieldType)
2861            field.typeCheck()
2862            self.assertEqual(JID('user@example.org'), field.value)
2863
2864            self.assertNotIn('x-myfield', fields)
2865
2866        self.resource.getConfigurationOptions = getConfigurationOptions
2867        self.resource.configureGet = configureGet
2868        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2869        d = self.handleRequest(xml)
2870        d.addCallback(cb)
2871        return d
2872
2873
2874    def test_on_configureSet(self):
2875        """
2876        On a node configuration set request the Data Form is parsed and
2877        L{PubSubResource.configureSet} is called with the passed options.
2878        """
2879
2880        xml = """
2881        <iq type='set' to='pubsub.example.org'
2882                       from='user@example.org'>
2883          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
2884            <configure node='test'>
2885              <x xmlns='jabber:x:data' type='submit'>
2886                <field var='FORM_TYPE' type='hidden'>
2887                  <value>http://jabber.org/protocol/pubsub#node_config</value>
2888                </field>
2889                <field var='pubsub#deliver_payloads'><value>0</value></field>
2890                <field var='pubsub#persist_items'><value>1</value></field>
2891              </x>
2892            </configure>
2893          </pubsub>
2894        </iq>
2895        """
2896
2897        def getConfigurationOptions():
2898            return {
2899                "pubsub#persist_items":
2900                    {"type": "boolean",
2901                     "label": "Persist items to storage"},
2902                "pubsub#deliver_payloads":
2903                    {"type": "boolean",
2904                     "label": "Deliver payloads with event notifications"}
2905                }
2906
2907        def configureSet(request):
2908            self.assertEqual({'pubsub#deliver_payloads': False,
2909                              'pubsub#persist_items': True},
2910                             request.options.getValues())
2911            return defer.succeed(None)
2912
2913        self.resource.getConfigurationOptions = getConfigurationOptions
2914        self.resource.configureSet = configureSet
2915        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2916        return self.handleRequest(xml)
2917
2918
2919    def test_on_configureSetCancel(self):
2920        """
2921        The node configuration is cancelled,
2922        L{PubSubResource.configureSet} not called.
2923        """
2924
2925        xml = """
2926        <iq type='set' to='pubsub.example.org'
2927                       from='user@example.org'>
2928          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
2929            <configure node='test'>
2930              <x xmlns='jabber:x:data' type='cancel'>
2931                <field var='FORM_TYPE' type='hidden'>
2932                  <value>http://jabber.org/protocol/pubsub#node_config</value>
2933                </field>
2934              </x>
2935            </configure>
2936          </pubsub>
2937        </iq>
2938        """
2939
2940        def configureSet(request):
2941            self.fail("Unexpected call to setConfiguration")
2942
2943        self.resource.configureSet = configureSet
2944        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2945        return self.handleRequest(xml)
2946
2947
2948    def test_on_configureSetIgnoreUnknown(self):
2949        """
2950        On a node configuration set request unknown fields should be ignored.
2951        """
2952
2953        xml = """
2954        <iq type='set' to='pubsub.example.org'
2955                       from='user@example.org'>
2956          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
2957            <configure node='test'>
2958              <x xmlns='jabber:x:data' type='submit'>
2959                <field var='FORM_TYPE' type='hidden'>
2960                  <value>http://jabber.org/protocol/pubsub#node_config</value>
2961                </field>
2962                <field var='pubsub#deliver_payloads'><value>0</value></field>
2963                <field var='x-myfield'><value>1</value></field>
2964              </x>
2965            </configure>
2966          </pubsub>
2967        </iq>
2968        """
2969
2970        def getConfigurationOptions():
2971            return {
2972                "pubsub#persist_items":
2973                    {"type": "boolean",
2974                     "label": "Persist items to storage"},
2975                "pubsub#deliver_payloads":
2976                    {"type": "boolean",
2977                     "label": "Deliver payloads with event notifications"}
2978                }
2979
2980        def configureSet(request):
2981            self.assertEquals(['pubsub#deliver_payloads'],
2982                              list(request.options.keys()))
2983
2984        self.resource.getConfigurationOptions = getConfigurationOptions
2985        self.resource.configureSet = configureSet
2986        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2987        return self.handleRequest(xml)
2988
2989
2990    def test_on_configureSetBadFormType(self):
2991        """
2992        On a node configuration set request unknown fields should be ignored.
2993        """
2994
2995        xml = """
2996        <iq type='set' to='pubsub.example.org'
2997                       from='user@example.org'>
2998          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
2999            <configure node='test'>
3000              <x xmlns='jabber:x:data' type='result'>
3001                <field var='FORM_TYPE' type='hidden'>
3002                  <value>http://jabber.org/protocol/pubsub#node_config</value>
3003                </field>
3004                <field var='pubsub#deliver_payloads'><value>0</value></field>
3005                <field var='x-myfield'><value>1</value></field>
3006              </x>
3007            </configure>
3008          </pubsub>
3009        </iq>
3010        """
3011
3012        def cb(result):
3013            self.assertEquals('bad-request', result.condition)
3014            self.assertEqual("Unexpected form type 'result'", result.text)
3015
3016        d = self.handleRequest(xml)
3017        self.assertFailure(d, error.StanzaError)
3018        d.addCallback(cb)
3019        return d
3020
3021
3022    def test_on_items(self):
3023        """
3024        On a items request, return all items for the given node.
3025        """
3026        xml = """
3027        <iq type='get' to='pubsub.example.org'
3028                       from='user@example.org'>
3029          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
3030            <items node='test'/>
3031          </pubsub>
3032        </iq>
3033        """
3034
3035        def items(request):
3036            return defer.succeed([pubsub.Item('current')])
3037
3038        def cb(element):
3039            self.assertEqual(NS_PUBSUB, element.uri)
3040            self.assertEqual(NS_PUBSUB, element.items.uri)
3041            self.assertEqual(1, len(element.items.children))
3042            item = element.items.children[-1]
3043            self.assertTrue(domish.IElement.providedBy(item))
3044            self.assertEqual('item', item.name)
3045            self.assertEqual(NS_PUBSUB, item.uri)
3046            self.assertEqual('current', item['id'])
3047
3048        self.resource.items = items
3049        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
3050        d = self.handleRequest(xml)
3051        d.addCallback(cb)
3052        return d
3053
3054
3055    def test_on_retract(self):
3056        """
3057        A retract request should result in L{PubSubResource.retract}
3058        being called.
3059        """
3060
3061        xml = """
3062        <iq type='set' to='pubsub.example.org'
3063                       from='user@example.org'>
3064          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
3065            <retract node='test'>
3066              <item id='item1'/>
3067              <item id='item2'/>
3068            </retract>
3069          </pubsub>
3070        </iq>
3071        """
3072
3073        def retract(request):
3074            return defer.succeed(None)
3075
3076        self.resource.retract = retract
3077        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
3078        return self.handleRequest(xml)
3079
3080
3081    def test_on_purge(self):
3082        """
3083        A purge request should result in L{PubSubResource.purge} being
3084        called.
3085        """
3086
3087        xml = """
3088        <iq type='set' to='pubsub.example.org'
3089                       from='user@example.org'>
3090          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3091            <purge node='test'/>
3092          </pubsub>
3093        </iq>
3094        """
3095
3096        def purge(request):
3097            return defer.succeed(None)
3098
3099        self.resource.purge = purge
3100        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
3101        return self.handleRequest(xml)
3102
3103
3104    def test_on_delete(self):
3105        """
3106        A delete request should result in L{PubSubResource.delete} being
3107        called.
3108        """
3109
3110        xml = """
3111        <iq type='set' to='pubsub.example.org'
3112                       from='user@example.org'>
3113          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3114            <delete node='test'/>
3115          </pubsub>
3116        </iq>
3117        """
3118
3119        def delete(request):
3120            return defer.succeed(None)
3121
3122        self.resource.delete = delete
3123        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
3124        return self.handleRequest(xml)
3125
3126
3127    def test_notifyPublish(self):
3128        """
3129        Publish notifications are sent to the subscribers.
3130        """
3131        subscriber = JID('user@example.org')
3132        subscriptions = [pubsub.Subscription('test', subscriber, 'subscribed')]
3133        items = [pubsub.Item('current')]
3134        notifications = [(subscriber, subscriptions, items)]
3135        self.service.notifyPublish(JID('pubsub.example.org'), 'test',
3136                                   notifications)
3137        message = self.stub.output[-1]
3138
3139        self.assertEquals('message', message.name)
3140        self.assertIdentical(None, message.uri)
3141        self.assertEquals('user@example.org', message['to'])
3142        self.assertEquals('pubsub.example.org', message['from'])
3143        self.assertTrue(message.event)
3144        self.assertEquals(NS_PUBSUB_EVENT, message.event.uri)
3145        self.assertTrue(message.event.items)
3146        self.assertEquals(NS_PUBSUB_EVENT, message.event.items.uri)
3147        self.assertTrue(message.event.items.hasAttribute('node'))
3148        self.assertEquals('test', message.event.items['node'])
3149        itemElements = list(domish.generateElementsQNamed(
3150            message.event.items.children, 'item', NS_PUBSUB_EVENT))
3151        self.assertEquals(1, len(itemElements))
3152        self.assertEquals('current', itemElements[0].getAttribute('id'))
3153
3154
3155    def test_notifyPublishCollection(self):
3156        """
3157        Publish notifications are sent to the subscribers of collections.
3158
3159        The node the item was published to is on the C{items} element, while
3160        the subscribed-to node is in the C{'Collections'} SHIM header.
3161        """
3162        subscriber = JID('user@example.org')
3163        subscriptions = [pubsub.Subscription('', subscriber, 'subscribed')]
3164        items = [pubsub.Item('current')]
3165        notifications = [(subscriber, subscriptions, items)]
3166        self.service.notifyPublish(JID('pubsub.example.org'), 'test',
3167                                   notifications)
3168        message = self.stub.output[-1]
3169
3170        self.assertTrue(message.event.items.hasAttribute('node'))
3171        self.assertEquals('test', message.event.items['node'])
3172        headers = shim.extractHeaders(message)
3173        self.assertIn('Collection', headers)
3174        self.assertIn('', headers['Collection'])
3175
3176
3177    def test_notifyDelete(self):
3178        """
3179        Subscribers should be sent a delete notification.
3180        """
3181        subscriptions = [JID('user@example.org')]
3182        self.service.notifyDelete(JID('pubsub.example.org'), 'test',
3183                                  subscriptions)
3184        message = self.stub.output[-1]
3185
3186        self.assertEquals('message', message.name)
3187        self.assertIdentical(None, message.uri)
3188        self.assertEquals('user@example.org', message['to'])
3189        self.assertEquals('pubsub.example.org', message['from'])
3190        self.assertTrue(message.event)
3191        self.assertEqual(NS_PUBSUB_EVENT, message.event.uri)
3192        self.assertTrue(message.event.delete)
3193        self.assertEqual(NS_PUBSUB_EVENT, message.event.delete.uri)
3194        self.assertTrue(message.event.delete.hasAttribute('node'))
3195        self.assertEqual('test', message.event.delete['node'])
3196
3197
3198    def test_notifyDeleteRedirect(self):
3199        """
3200        Subscribers should be sent a delete notification with redirect.
3201        """
3202        redirectURI = 'xmpp:pubsub.example.org?;node=test2'
3203        subscriptions = [JID('user@example.org')]
3204        self.service.notifyDelete(JID('pubsub.example.org'), 'test',
3205                                  subscriptions, redirectURI)
3206        message = self.stub.output[-1]
3207
3208        self.assertEquals('message', message.name)
3209        self.assertIdentical(None, message.uri)
3210        self.assertEquals('user@example.org', message['to'])
3211        self.assertEquals('pubsub.example.org', message['from'])
3212        self.assertTrue(message.event)
3213        self.assertEqual(NS_PUBSUB_EVENT, message.event.uri)
3214        self.assertTrue(message.event.delete)
3215        self.assertEqual(NS_PUBSUB_EVENT, message.event.delete.uri)
3216        self.assertTrue(message.event.delete.hasAttribute('node'))
3217        self.assertEqual('test', message.event.delete['node'])
3218        self.assertTrue(message.event.delete.redirect)
3219        self.assertEqual(NS_PUBSUB_EVENT, message.event.delete.redirect.uri)
3220        self.assertTrue(message.event.delete.redirect.hasAttribute('uri'))
3221        self.assertEqual(redirectURI, message.event.delete.redirect['uri'])
3222
3223
3224    def test_on_subscriptionsGet(self):
3225        """
3226        Getting subscription options is not supported.
3227        """
3228
3229        xml = """
3230        <iq type='get' to='pubsub.example.org'
3231                       from='user@example.org'>
3232          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3233            <subscriptions/>
3234          </pubsub>
3235        </iq>
3236        """
3237
3238        def cb(result):
3239            self.assertEquals('feature-not-implemented', result.condition)
3240            self.assertEquals('unsupported', result.appCondition.name)
3241            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3242            self.assertEquals('manage-subscriptions',
3243                              result.appCondition['feature'])
3244
3245        d = self.handleRequest(xml)
3246        self.assertFailure(d, error.StanzaError)
3247        d.addCallback(cb)
3248        return d
3249
3250
3251    def test_on_subscriptionsSet(self):
3252        """
3253        Setting subscription options is not supported.
3254        """
3255
3256        xml = """
3257        <iq type='set' to='pubsub.example.org'
3258                       from='user@example.org'>
3259          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3260            <subscriptions/>
3261          </pubsub>
3262        </iq>
3263        """
3264
3265        def cb(result):
3266            self.assertEquals('feature-not-implemented', result.condition)
3267            self.assertEquals('unsupported', result.appCondition.name)
3268            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3269            self.assertEquals('manage-subscriptions',
3270                              result.appCondition['feature'])
3271
3272        d = self.handleRequest(xml)
3273        self.assertFailure(d, error.StanzaError)
3274        d.addCallback(cb)
3275        return d
3276
3277
3278    def test_on_affiliationsGet(self):
3279        """
3280        Getting node affiliations should have.
3281        """
3282
3283        xml = """
3284        <iq type='get' to='pubsub.example.org'
3285                       from='user@example.org'>
3286          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3287            <affiliations node='test'/>
3288          </pubsub>
3289        </iq>
3290        """
3291
3292        def affiliationsGet(request):
3293            self.assertEquals('test', request.nodeIdentifier)
3294            return defer.succeed({JID('user@example.org'): 'owner'})
3295
3296        def cb(element):
3297            self.assertEquals(u'pubsub', element.name)
3298            self.assertEquals(NS_PUBSUB_OWNER, element.uri)
3299            self.assertEquals(NS_PUBSUB_OWNER, element.affiliations.uri)
3300            self.assertEquals(u'test', element.affiliations[u'node'])
3301            children = list(element.affiliations.elements())
3302            self.assertEquals(1, len(children))
3303            affiliation = children[0]
3304            self.assertEquals(u'affiliation', affiliation.name)
3305            self.assertEquals(NS_PUBSUB_OWNER, affiliation.uri)
3306            self.assertEquals(u'user@example.org', affiliation[u'jid'])
3307            self.assertEquals(u'owner', affiliation[u'affiliation'])
3308
3309        self.resource.affiliationsGet = affiliationsGet
3310        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
3311        d = self.handleRequest(xml)
3312        d.addCallback(cb)
3313        return d
3314
3315
3316    def test_on_affiliationsGetEmptyNode(self):
3317        """
3318        Getting node affiliations without node should assume empty node.
3319        """
3320
3321        xml = """
3322        <iq type='get' to='pubsub.example.org'
3323                       from='user@example.org'>
3324          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3325            <affiliations/>
3326          </pubsub>
3327        </iq>
3328        """
3329
3330        def affiliationsGet(request):
3331            self.assertEqual('', request.nodeIdentifier)
3332            return defer.succeed({})
3333
3334        def cb(element):
3335            self.assertFalse(element.affiliations.hasAttribute(u'node'))
3336
3337        self.resource.affiliationsGet = affiliationsGet
3338        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
3339        d = self.handleRequest(xml)
3340        d.addCallback(cb)
3341        return d
3342
3343
3344    def test_on_affiliationsSet(self):
3345        """
3346        Setting node affiliations has the affiliations to be modified.
3347        """
3348
3349        xml = """
3350        <iq type='set' to='pubsub.example.org'
3351                       from='user@example.org'>
3352          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3353            <affiliations node='test'>
3354              <affiliation jid='other@example.org' affiliation='publisher'/>
3355            </affiliations>
3356          </pubsub>
3357        </iq>
3358        """
3359
3360        def affiliationsSet(request):
3361            self.assertEquals(u'test', request.nodeIdentifier)
3362            otherJID = JID(u'other@example.org')
3363            self.assertIn(otherJID, request.affiliations)
3364            self.assertEquals(u'publisher', request.affiliations[otherJID])
3365
3366        self.resource.affiliationsSet = affiliationsSet
3367        return self.handleRequest(xml)
3368
3369
3370    def test_on_affiliationsSetBareJID(self):
3371        """
3372        Affiliations are always on the bare JID.
3373        """
3374
3375        xml = """
3376        <iq type='set' to='pubsub.example.org'
3377                       from='user@example.org'>
3378          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3379            <affiliations node='test'>
3380              <affiliation jid='other@example.org/Home'
3381                           affiliation='publisher'/>
3382            </affiliations>
3383          </pubsub>
3384        </iq>
3385        """
3386
3387        def affiliationsSet(request):
3388            otherJID = JID(u'other@example.org')
3389            self.assertIn(otherJID, request.affiliations)
3390
3391        self.resource.affiliationsSet = affiliationsSet
3392        return self.handleRequest(xml)
3393
3394
3395    def test_on_affiliationsSetMultipleForSameEntity(self):
3396        """
3397        Setting node affiliations can only have one item per entity.
3398        """
3399
3400        xml = """
3401        <iq type='set' to='pubsub.example.org'
3402                       from='user@example.org'>
3403          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3404            <affiliations node='test'>
3405              <affiliation jid='other@example.org' affiliation='publisher'/>
3406              <affiliation jid='other@example.org' affiliation='owner'/>
3407            </affiliations>
3408          </pubsub>
3409        </iq>
3410        """
3411
3412        def cb(result):
3413            self.assertEquals('bad-request', result.condition)
3414
3415        d = self.handleRequest(xml)
3416        self.assertFailure(d, error.StanzaError)
3417        d.addCallback(cb)
3418        return d
3419
3420
3421    def test_on_affiliationsSetMissingJID(self):
3422        """
3423        Setting node affiliations must include a JID per affiliation.
3424        """
3425
3426        xml = """
3427        <iq type='set' to='pubsub.example.org'
3428                       from='user@example.org'>
3429          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3430            <affiliations node='test'>
3431              <affiliation affiliation='publisher'/>
3432            </affiliations>
3433          </pubsub>
3434        </iq>
3435        """
3436
3437        def cb(result):
3438            self.assertEquals('bad-request', result.condition)
3439
3440        d = self.handleRequest(xml)
3441        self.assertFailure(d, error.StanzaError)
3442        d.addCallback(cb)
3443        return d
3444
3445
3446    def test_on_affiliationsSetMissingAffiliation(self):
3447        """
3448        Setting node affiliations must include an affiliation.
3449        """
3450
3451        xml = """
3452        <iq type='set' to='pubsub.example.org'
3453                       from='user@example.org'>
3454          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3455            <affiliations node='test'>
3456              <affiliation jid='other@example.org'/>
3457            </affiliations>
3458          </pubsub>
3459        </iq>
3460        """
3461
3462        def cb(result):
3463            self.assertEquals('bad-request', result.condition)
3464
3465        d = self.handleRequest(xml)
3466        self.assertFailure(d, error.StanzaError)
3467        d.addCallback(cb)
3468        return d
3469
3470
3471
3472class PubSubServiceWithoutResourceTest(unittest.TestCase, TestableRequestHandlerMixin):
3473
3474    def setUp(self):
3475        self.stub = XmlStreamStub()
3476        self.service = pubsub.PubSubService()
3477        self.service.send = self.stub.xmlstream.send
3478
3479
3480    def test_getDiscoInfo(self):
3481        """
3482        Test getDiscoInfo calls getNodeInfo and returns some minimal info.
3483        """
3484        def cb(info):
3485            discoInfo = disco.DiscoInfo()
3486            for item in info:
3487                discoInfo.append(item)
3488            self.assertIn(('pubsub', 'service'), discoInfo.identities)
3489            self.assertIn(disco.NS_DISCO_ITEMS, discoInfo.features)
3490
3491        d = self.service.getDiscoInfo(JID('user@example.org/home'),
3492                                      JID('pubsub.example.org'), '')
3493        d.addCallback(cb)
3494        return d
3495
3496
3497    def test_publish(self):
3498        """
3499        Non-overridden L{PubSubService.publish} yields unsupported error.
3500        """
3501
3502        xml = """
3503        <iq type='set' to='pubsub.example.org'
3504                       from='user@example.org'>
3505          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
3506            <publish node='mynode'/>
3507          </pubsub>
3508        </iq>
3509        """
3510
3511        def cb(result):
3512            self.assertEquals('feature-not-implemented', result.condition)
3513            self.assertEquals('unsupported', result.appCondition.name)
3514            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3515            self.assertEquals('publish', result.appCondition['feature'])
3516
3517        d = self.handleRequest(xml)
3518        self.assertFailure(d, error.StanzaError)
3519        d.addCallback(cb)
3520        return d
3521
3522
3523    def test_subscribe(self):
3524        """
3525        Non-overridden L{PubSubService.subscribe} yields unsupported error.
3526        """
3527
3528        xml = """
3529        <iq type='set' to='pubsub.example.org'
3530                       from='user@example.org'>
3531          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
3532            <subscribe node='test' jid='user@example.org/Home'/>
3533          </pubsub>
3534        </iq>
3535        """
3536
3537        def cb(result):
3538            self.assertEquals('feature-not-implemented', result.condition)
3539            self.assertEquals('unsupported', result.appCondition.name)
3540            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3541            self.assertEquals('subscribe', result.appCondition['feature'])
3542
3543        d = self.handleRequest(xml)
3544        self.assertFailure(d, error.StanzaError)
3545        d.addCallback(cb)
3546        return d
3547
3548
3549    def test_unsubscribe(self):
3550        """
3551        Non-overridden L{PubSubService.unsubscribe} yields unsupported error.
3552        """
3553
3554        xml = """
3555        <iq type='set' to='pubsub.example.org'
3556                       from='user@example.org'>
3557          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
3558            <unsubscribe node='test' jid='user@example.org/Home'/>
3559          </pubsub>
3560        </iq>
3561        """
3562
3563        def cb(result):
3564            self.assertEquals('feature-not-implemented', result.condition)
3565            self.assertEquals('unsupported', result.appCondition.name)
3566            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3567            self.assertEquals('subscribe', result.appCondition['feature'])
3568
3569        d = self.handleRequest(xml)
3570        self.assertFailure(d, error.StanzaError)
3571        d.addCallback(cb)
3572        return d
3573
3574
3575    def test_subscriptions(self):
3576        """
3577        Non-overridden L{PubSubService.subscriptions} yields unsupported error.
3578        """
3579
3580        xml = """
3581        <iq type='get' to='pubsub.example.org'
3582                       from='user@example.org'>
3583          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
3584            <subscriptions/>
3585          </pubsub>
3586        </iq>
3587        """
3588
3589        def cb(result):
3590            self.assertEquals('feature-not-implemented', result.condition)
3591            self.assertEquals('unsupported', result.appCondition.name)
3592            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3593            self.assertEquals('retrieve-subscriptions',
3594                              result.appCondition['feature'])
3595
3596        d = self.handleRequest(xml)
3597        self.assertFailure(d, error.StanzaError)
3598        d.addCallback(cb)
3599        return d
3600
3601
3602    def test_affiliations(self):
3603        """
3604        Non-overridden L{PubSubService.affiliations} yields unsupported error.
3605        """
3606
3607        xml = """
3608        <iq type='get' to='pubsub.example.org'
3609                       from='user@example.org'>
3610          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
3611            <affiliations/>
3612          </pubsub>
3613        </iq>
3614        """
3615
3616        def cb(result):
3617            self.assertEquals('feature-not-implemented', result.condition)
3618            self.assertEquals('unsupported', result.appCondition.name)
3619            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3620            self.assertEquals('retrieve-affiliations',
3621                              result.appCondition['feature'])
3622
3623        d = self.handleRequest(xml)
3624        self.assertFailure(d, error.StanzaError)
3625        d.addCallback(cb)
3626        return d
3627
3628
3629    def test_create(self):
3630        """
3631        Non-overridden L{PubSubService.create} yields unsupported error.
3632        """
3633
3634        xml = """
3635        <iq type='set' to='pubsub.example.org'
3636                       from='user@example.org'>
3637          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
3638            <create node='mynode'/>
3639          </pubsub>
3640        </iq>
3641        """
3642
3643        def cb(result):
3644            self.assertEquals('feature-not-implemented', result.condition)
3645            self.assertEquals('unsupported', result.appCondition.name)
3646            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3647            self.assertEquals('create-nodes', result.appCondition['feature'])
3648
3649        d = self.handleRequest(xml)
3650        self.assertFailure(d, error.StanzaError)
3651        d.addCallback(cb)
3652        return d
3653
3654
3655    def test_getDefaultConfiguration(self):
3656        """
3657        Non-overridden L{PubSubService.getDefaultConfiguration} yields
3658        unsupported error.
3659        """
3660
3661        xml = """
3662        <iq type='get' to='pubsub.example.org'
3663                       from='user@example.org'>
3664          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3665            <default/>
3666          </pubsub>
3667        </iq>
3668        """
3669
3670        def cb(result):
3671            self.assertEquals('feature-not-implemented', result.condition)
3672            self.assertEquals('unsupported', result.appCondition.name)
3673            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3674            self.assertEquals('retrieve-default', result.appCondition['feature'])
3675
3676        d = self.handleRequest(xml)
3677        self.assertFailure(d, error.StanzaError)
3678        d.addCallback(cb)
3679        return d
3680
3681
3682    def test_getConfiguration(self):
3683        """
3684        Non-overridden L{PubSubService.getConfiguration} yields unsupported
3685        error.
3686        """
3687
3688        xml = """
3689        <iq type='get' to='pubsub.example.org'
3690                       from='user@example.org'>
3691          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3692            <configure/>
3693          </pubsub>
3694        </iq>
3695        """
3696
3697        def cb(result):
3698            self.assertEquals('feature-not-implemented', result.condition)
3699            self.assertEquals('unsupported', result.appCondition.name)
3700            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3701            self.assertEquals('config-node', result.appCondition['feature'])
3702
3703        d = self.handleRequest(xml)
3704        self.assertFailure(d, error.StanzaError)
3705        d.addCallback(cb)
3706        return d
3707
3708
3709    def test_setConfiguration(self):
3710        """
3711        Non-overridden L{PubSubService.setConfiguration} yields unsupported
3712        error.
3713        """
3714
3715        xml = """
3716        <iq type='set' to='pubsub.example.org'
3717                       from='user@example.org'>
3718          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3719            <configure node='test'>
3720              <x xmlns='jabber:x:data' type='submit'>
3721                <field var='FORM_TYPE' type='hidden'>
3722                  <value>http://jabber.org/protocol/pubsub#node_config</value>
3723                </field>
3724                <field var='pubsub#deliver_payloads'><value>0</value></field>
3725                <field var='pubsub#persist_items'><value>1</value></field>
3726              </x>
3727            </configure>
3728          </pubsub>
3729        </iq>
3730        """
3731
3732        def cb(result):
3733            self.assertEquals('feature-not-implemented', result.condition)
3734            self.assertEquals('unsupported', result.appCondition.name)
3735            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3736            self.assertEquals('config-node', result.appCondition['feature'])
3737
3738        d = self.handleRequest(xml)
3739        self.assertFailure(d, error.StanzaError)
3740        d.addCallback(cb)
3741        return d
3742
3743
3744    def test_setConfigurationOptionsDict(self):
3745        """
3746        Options should be passed as a dictionary, not a form.
3747        """
3748
3749        xml = """
3750        <iq type='set' to='pubsub.example.org'
3751                       from='user@example.org'>
3752          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3753            <configure node='test'>
3754              <x xmlns='jabber:x:data' type='submit'>
3755                <field var='FORM_TYPE' type='hidden'>
3756                  <value>http://jabber.org/protocol/pubsub#node_config</value>
3757                </field>
3758                <field var='pubsub#deliver_payloads'><value>0</value></field>
3759                <field var='pubsub#persist_items'><value>1</value></field>
3760              </x>
3761            </configure>
3762          </pubsub>
3763        </iq>
3764        """
3765
3766        def getConfigurationOptions():
3767            return {
3768                "pubsub#persist_items":
3769                    {"type": "boolean",
3770                     "label": "Persist items to storage"},
3771                "pubsub#deliver_payloads":
3772                    {"type": "boolean",
3773                     "label": "Deliver payloads with event notifications"}
3774                }
3775
3776        def setConfiguration(requestor, service, nodeIdentifier, options):
3777            self.assertIn('pubsub#deliver_payloads', options)
3778            self.assertFalse(options['pubsub#deliver_payloads'])
3779            self.assertIn('pubsub#persist_items', options)
3780            self.assertTrue(options['pubsub#persist_items'])
3781
3782        self.service.getConfigurationOptions = getConfigurationOptions
3783        self.service.setConfiguration = setConfiguration
3784        return self.handleRequest(xml)
3785
3786
3787    def test_items(self):
3788        """
3789        Non-overridden L{PubSubService.items} yields unsupported error.
3790        """
3791        xml = """
3792        <iq type='get' to='pubsub.example.org'
3793                       from='user@example.org'>
3794          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
3795            <items node='test'/>
3796          </pubsub>
3797        </iq>
3798        """
3799
3800        def cb(result):
3801            self.assertEquals('feature-not-implemented', result.condition)
3802            self.assertEquals('unsupported', result.appCondition.name)
3803            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3804            self.assertEquals('retrieve-items', result.appCondition['feature'])
3805
3806        d = self.handleRequest(xml)
3807        self.assertFailure(d, error.StanzaError)
3808        d.addCallback(cb)
3809        return d
3810
3811
3812    def test_retract(self):
3813        """
3814        Non-overridden L{PubSubService.retract} yields unsupported error.
3815        """
3816        xml = """
3817        <iq type='set' to='pubsub.example.org'
3818                       from='user@example.org'>
3819          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
3820            <retract node='test'>
3821              <item id='item1'/>
3822              <item id='item2'/>
3823            </retract>
3824          </pubsub>
3825        </iq>
3826        """
3827
3828        def cb(result):
3829            self.assertEquals('feature-not-implemented', result.condition)
3830            self.assertEquals('unsupported', result.appCondition.name)
3831            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3832            self.assertEquals('retract-items', result.appCondition['feature'])
3833
3834        d = self.handleRequest(xml)
3835        self.assertFailure(d, error.StanzaError)
3836        d.addCallback(cb)
3837        return d
3838
3839
3840    def test_purge(self):
3841        """
3842        Non-overridden L{PubSubService.purge} yields unsupported error.
3843        """
3844        xml = """
3845        <iq type='set' to='pubsub.example.org'
3846                       from='user@example.org'>
3847          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3848            <purge node='test'/>
3849          </pubsub>
3850        </iq>
3851        """
3852
3853        def cb(result):
3854            self.assertEquals('feature-not-implemented', result.condition)
3855            self.assertEquals('unsupported', result.appCondition.name)
3856            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3857            self.assertEquals('purge-nodes', result.appCondition['feature'])
3858
3859        d = self.handleRequest(xml)
3860        self.assertFailure(d, error.StanzaError)
3861        d.addCallback(cb)
3862        return d
3863
3864
3865    def test_delete(self):
3866        """
3867        Non-overridden L{PubSubService.delete} yields unsupported error.
3868        """
3869        xml = """
3870        <iq type='set' to='pubsub.example.org'
3871                       from='user@example.org'>
3872          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3873            <delete node='test'/>
3874          </pubsub>
3875        </iq>
3876        """
3877
3878        def cb(result):
3879            self.assertEquals('feature-not-implemented', result.condition)
3880            self.assertEquals('unsupported', result.appCondition.name)
3881            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3882            self.assertEquals('delete-nodes', result.appCondition['feature'])
3883
3884        d = self.handleRequest(xml)
3885        self.assertFailure(d, error.StanzaError)
3886        d.addCallback(cb)
3887        return d
3888
3889
3890    def test_unknown(self):
3891        """
3892        Unknown verb yields unsupported error.
3893        """
3894        xml = """
3895        <iq type='get' to='pubsub.example.org'
3896                       from='user@example.org'>
3897          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3898            <affiliations node='test'/>
3899          </pubsub>
3900        </iq>
3901        """
3902
3903        def cb(result):
3904            self.assertEquals('feature-not-implemented', result.condition)
3905            self.assertEquals('unsupported', result.appCondition.name)
3906            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3907
3908        d = self.handleRequest(xml)
3909        self.assertFailure(d, error.StanzaError)
3910        d.addCallback(cb)
3911        return d
3912
3913
3914
3915class PubSubResourceTest(unittest.TestCase):
3916
3917    def setUp(self):
3918        self.resource = pubsub.PubSubResource()
3919
3920
3921    def test_interface(self):
3922        """
3923        Do instances of L{pubsub.PubSubResource} provide L{iwokkel.IPubSubResource}?
3924        """
3925        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
3926
3927
3928    def test_getNodes(self):
3929        """
3930        Default getNodes returns an empty list.
3931        """
3932        def cb(nodes):
3933            self.assertEquals([], nodes)
3934
3935        d = self.resource.getNodes(JID('user@example.org/home'),
3936                                   JID('pubsub.example.org'),
3937                                   '')
3938        d.addCallback(cb)
3939        return d
3940
3941
3942    def test_publish(self):
3943        """
3944        Non-overridden L{PubSubResource.publish} yields unsupported
3945        error.
3946        """
3947
3948        def cb(result):
3949            self.assertEquals('feature-not-implemented', result.condition)
3950            self.assertEquals('unsupported', result.appCondition.name)
3951            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3952            self.assertEquals('publish', result.appCondition['feature'])
3953
3954        d = self.resource.publish(pubsub.PubSubRequest())
3955        self.assertFailure(d, error.StanzaError)
3956        d.addCallback(cb)
3957        return d
3958
3959
3960    def test_subscribe(self):
3961        """
3962        Non-overridden subscriptions yields unsupported error.
3963        """
3964
3965        def cb(result):
3966            self.assertEquals('feature-not-implemented', result.condition)
3967            self.assertEquals('unsupported', result.appCondition.name)
3968            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3969            self.assertEquals('subscribe', result.appCondition['feature'])
3970
3971        d = self.resource.subscribe(pubsub.PubSubRequest())
3972        self.assertFailure(d, error.StanzaError)
3973        d.addCallback(cb)
3974        return d
3975
3976
3977    def test_unsubscribe(self):
3978        """
3979        Non-overridden unsubscribe yields unsupported error.
3980        """
3981
3982        def cb(result):
3983            self.assertEquals('feature-not-implemented', result.condition)
3984            self.assertEquals('unsupported', result.appCondition.name)
3985            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3986            self.assertEquals('subscribe', result.appCondition['feature'])
3987
3988        d = self.resource.unsubscribe(pubsub.PubSubRequest())
3989        self.assertFailure(d, error.StanzaError)
3990        d.addCallback(cb)
3991        return d
3992
3993
3994    def test_subscriptions(self):
3995        """
3996        Non-overridden subscriptions yields unsupported error.
3997        """
3998
3999        def cb(result):
4000            self.assertEquals('feature-not-implemented', result.condition)
4001            self.assertEquals('unsupported', result.appCondition.name)
4002            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
4003            self.assertEquals('retrieve-subscriptions',
4004                              result.appCondition['feature'])
4005
4006        d = self.resource.subscriptions(pubsub.PubSubRequest())
4007        self.assertFailure(d, error.StanzaError)
4008        d.addCallback(cb)
4009        return d
4010
4011
4012    def test_affiliations(self):
4013        """
4014        Non-overridden affiliations yields unsupported error.
4015        """
4016
4017        def cb(result):
4018            self.assertEquals('feature-not-implemented', result.condition)
4019            self.assertEquals('unsupported', result.appCondition.name)
4020            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
4021            self.assertEquals('retrieve-affiliations',
4022                              result.appCondition['feature'])
4023
4024        d = self.resource.affiliations(pubsub.PubSubRequest())
4025        self.assertFailure(d, error.StanzaError)
4026        d.addCallback(cb)
4027        return d
4028
4029
4030    def test_create(self):
4031        """
4032        Non-overridden create yields unsupported error.
4033        """
4034
4035        def cb(result):
4036            self.assertEquals('feature-not-implemented', result.condition)
4037            self.assertEquals('unsupported', result.appCondition.name)
4038            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
4039            self.assertEquals('create-nodes', result.appCondition['feature'])
4040
4041        d = self.resource.create(pubsub.PubSubRequest())
4042        self.assertFailure(d, error.StanzaError)
4043        d.addCallback(cb)
4044        return d
4045
4046
4047    def test_default(self):
4048        """
4049        Non-overridden default yields unsupported error.
4050        """
4051
4052        def cb(result):
4053            self.assertEquals('feature-not-implemented', result.condition)
4054            self.assertEquals('unsupported', result.appCondition.name)
4055            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
4056            self.assertEquals('retrieve-default',
4057                              result.appCondition['feature'])
4058
4059        d = self.resource.default(pubsub.PubSubRequest())
4060        self.assertFailure(d, error.StanzaError)
4061        d.addCallback(cb)
4062        return d
4063
4064
4065    def test_configureGet(self):
4066        """
4067        Non-overridden configureGet yields unsupported
4068        error.
4069        """
4070
4071        def cb(result):
4072            self.assertEquals('feature-not-implemented', result.condition)
4073            self.assertEquals('unsupported', result.appCondition.name)
4074            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
4075            self.assertEquals('config-node', result.appCondition['feature'])
4076
4077        d = self.resource.configureGet(pubsub.PubSubRequest())
4078        self.assertFailure(d, error.StanzaError)
4079        d.addCallback(cb)
4080        return d
4081
4082
4083    def test_configureSet(self):
4084        """
4085        Non-overridden configureSet yields unsupported error.
4086        """
4087
4088        def cb(result):
4089            self.assertEquals('feature-not-implemented', result.condition)
4090            self.assertEquals('unsupported', result.appCondition.name)
4091            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
4092            self.assertEquals('config-node', result.appCondition['feature'])
4093
4094        d = self.resource.configureSet(pubsub.PubSubRequest())
4095        self.assertFailure(d, error.StanzaError)
4096        d.addCallback(cb)
4097        return d
4098
4099
4100    def test_items(self):
4101        """
4102        Non-overridden items yields unsupported error.
4103        """
4104
4105        def cb(result):
4106            self.assertEquals('feature-not-implemented', result.condition)
4107            self.assertEquals('unsupported', result.appCondition.name)
4108            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
4109            self.assertEquals('retrieve-items', result.appCondition['feature'])
4110
4111        d = self.resource.items(pubsub.PubSubRequest())
4112        self.assertFailure(d, error.StanzaError)
4113        d.addCallback(cb)
4114        return d
4115
4116
4117    def test_retract(self):
4118        """
4119        Non-overridden retract yields unsupported error.
4120        """
4121
4122        def cb(result):
4123            self.assertEquals('feature-not-implemented', result.condition)
4124            self.assertEquals('unsupported', result.appCondition.name)
4125            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
4126            self.assertEquals('retract-items', result.appCondition['feature'])
4127
4128        d = self.resource.retract(pubsub.PubSubRequest())
4129        self.assertFailure(d, error.StanzaError)
4130        d.addCallback(cb)
4131        return d
4132
4133
4134    def test_purge(self):
4135        """
4136        Non-overridden purge yields unsupported error.
4137        """
4138
4139        def cb(result):
4140            self.assertEquals('feature-not-implemented', result.condition)
4141            self.assertEquals('unsupported', result.appCondition.name)
4142            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
4143            self.assertEquals('purge-nodes', result.appCondition['feature'])
4144
4145        d = self.resource.purge(pubsub.PubSubRequest())
4146        self.assertFailure(d, error.StanzaError)
4147        d.addCallback(cb)
4148        return d
4149
4150
4151    def test_delete(self):
4152        """
4153        Non-overridden delete yields unsupported error.
4154        """
4155
4156        def cb(result):
4157            self.assertEquals('feature-not-implemented', result.condition)
4158            self.assertEquals('unsupported', result.appCondition.name)
4159            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
4160            self.assertEquals('delete-nodes', result.appCondition['feature'])
4161
4162        d = self.resource.delete(pubsub.PubSubRequest())
4163        self.assertFailure(d, error.StanzaError)
4164        d.addCallback(cb)
4165        return d
4166
4167
4168    def test_affiliationsGet(self):
4169        """
4170        Non-overridden owner affiliations get yields unsupported error.
4171        """
4172
4173        def cb(result):
4174            self.assertEquals('feature-not-implemented', result.condition)
4175            self.assertEquals('unsupported', result.appCondition.name)
4176            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
4177            self.assertEquals('modify-affiliations',
4178                              result.appCondition['feature'])
4179
4180        d = self.resource.affiliationsGet(pubsub.PubSubRequest())
4181        self.assertFailure(d, error.StanzaError)
4182        d.addCallback(cb)
4183        return d
4184
4185
4186    def test_affiliationsSet(self):
4187        """
4188        Non-overridden owner affiliations set yields unsupported error.
4189        """
4190
4191        def cb(result):
4192            self.assertEquals('feature-not-implemented', result.condition)
4193            self.assertEquals('unsupported', result.appCondition.name)
4194            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
4195            self.assertEquals('modify-affiliations',
4196                              result.appCondition['feature'])
4197
4198        d = self.resource.affiliationsSet(pubsub.PubSubRequest())
4199        self.assertFailure(d, error.StanzaError)
4200        d.addCallback(cb)
4201        return d
Note: See TracBrowser for help on using the repository browser.