source: wokkel/test/test_pubsub.py @ 225:58dd11c3ddd5

Last change on this file since 225:58dd11c3ddd5 was 225:58dd11c3ddd5, checked in by souliane <souliane@…>, 4 years ago

implement item retract + notification

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