source: wokkel/test/test_pubsub.py @ 183:f175d5c3fabf

Last change on this file since 183:f175d5c3fabf was 183:f175d5c3fabf, checked in by Ralph Meijer <ralphm@…>, 9 years ago

Fix bad test comparing the empty string by identity.

  • Property exe set to *
File size: 139.4 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_getOptions(self):
844        def cb(form):
845            self.assertEqual('form', form.formType)
846            self.assertEqual(NS_PUBSUB_SUBSCRIBE_OPTIONS, form.formNamespace)
847            field = form.fields['pubsub#deliver']
848            self.assertEqual('boolean', field.fieldType)
849            self.assertIdentical(True, field.value)
850            self.assertEqual('Enable delivery?', field.label)
851
852        d = self.protocol.getOptions(JID('pubsub.example.org'), 'test',
853                                     JID('user@example.org'),
854                                     sender=JID('user@example.org'))
855        d.addCallback(cb)
856
857        iq = self.stub.output[-1]
858        self.assertEqual('pubsub.example.org', iq.getAttribute('to'))
859        self.assertEqual('get', iq.getAttribute('type'))
860        self.assertEqual('pubsub', iq.pubsub.name)
861        self.assertEqual(NS_PUBSUB, iq.pubsub.uri)
862        children = list(domish.generateElementsQNamed(iq.pubsub.children,
863                                                      'options', NS_PUBSUB))
864        self.assertEqual(1, len(children))
865        child = children[0]
866        self.assertEqual('test', child['node'])
867
868        self.assertEqual(0, len(child.children))
869
870        # Send response
871        form = data_form.Form('form', formNamespace=NS_PUBSUB_SUBSCRIBE_OPTIONS)
872        form.addField(data_form.Field('boolean', var='pubsub#deliver',
873                                                 label='Enable delivery?',
874                                                 value=True))
875        response = toResponse(iq, 'result')
876        response.addElement((NS_PUBSUB, 'pubsub'))
877        response.pubsub.addElement('options')
878        response.pubsub.options.addChild(form.toElement())
879        self.stub.send(response)
880
881        return d
882
883
884    def test_getOptionsWithSubscriptionIdentifier(self):
885        """
886        Getting options with a subid should have the subid in the request.
887        """
888
889        d = self.protocol.getOptions(JID('pubsub.example.org'), 'test',
890                                     JID('user@example.org'),
891                                     sender=JID('user@example.org'),
892                                     subscriptionIdentifier='1234')
893
894        iq = self.stub.output[-1]
895        child = iq.pubsub.options
896        self.assertEqual('1234', child['subid'])
897
898        # Send response
899        form = data_form.Form('form', formNamespace=NS_PUBSUB_SUBSCRIBE_OPTIONS)
900        form.addField(data_form.Field('boolean', var='pubsub#deliver',
901                                                 label='Enable delivery?',
902                                                 value=True))
903        response = toResponse(iq, 'result')
904        response.addElement((NS_PUBSUB, 'pubsub'))
905        response.pubsub.addElement('options')
906        response.pubsub.options.addChild(form.toElement())
907        self.stub.send(response)
908
909        return d
910
911
912    def test_setOptions(self):
913        """
914        setOptions should send out a options-set request.
915        """
916        options = {'pubsub#deliver': False}
917
918        d = self.protocol.setOptions(JID('pubsub.example.org'), 'test',
919                                     JID('user@example.org'),
920                                     options,
921                                     sender=JID('user@example.org'))
922
923        iq = self.stub.output[-1]
924        self.assertEqual('pubsub.example.org', iq.getAttribute('to'))
925        self.assertEqual('set', iq.getAttribute('type'))
926        self.assertEqual('pubsub', iq.pubsub.name)
927        self.assertEqual(NS_PUBSUB, iq.pubsub.uri)
928        children = list(domish.generateElementsQNamed(iq.pubsub.children,
929                                                      'options', NS_PUBSUB))
930        self.assertEqual(1, len(children))
931        child = children[0]
932        self.assertEqual('test', child['node'])
933
934        form = data_form.findForm(child, NS_PUBSUB_SUBSCRIBE_OPTIONS)
935        self.assertEqual('submit', form.formType)
936        form.typeCheck({'pubsub#deliver': {'type': 'boolean'}})
937        self.assertEqual(options, form.getValues())
938
939        response = toResponse(iq, 'result')
940        self.stub.send(response)
941
942        return d
943
944
945    def test_setOptionsWithSubscriptionIdentifier(self):
946        """
947        setOptions should send out a options-set request with subid.
948        """
949        options = {'pubsub#deliver': False}
950
951        d = self.protocol.setOptions(JID('pubsub.example.org'), 'test',
952                                     JID('user@example.org'),
953                                     options,
954                                     subscriptionIdentifier='1234',
955                                     sender=JID('user@example.org'))
956
957        iq = self.stub.output[-1]
958        child = iq.pubsub.options
959        self.assertEqual('1234', child['subid'])
960
961        form = data_form.findForm(child, NS_PUBSUB_SUBSCRIBE_OPTIONS)
962        self.assertEqual('submit', form.formType)
963        form.typeCheck({'pubsub#deliver': {'type': 'boolean'}})
964        self.assertEqual(options, form.getValues())
965
966        response = toResponse(iq, 'result')
967        self.stub.send(response)
968
969        return d
970
971
972class PubSubRequestTest(unittest.TestCase):
973
974    def test_fromElementUnknown(self):
975        """
976        An unknown verb raises NotImplementedError.
977        """
978
979        xml = """
980        <iq type='set' to='pubsub.example.org'
981                       from='user@example.org'>
982          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
983            <non-existing-verb/>
984          </pubsub>
985        </iq>
986        """
987
988        self.assertRaises(NotImplementedError,
989                          pubsub.PubSubRequest.fromElement, parseXml(xml))
990
991
992    def test_fromElementKnownBadCombination(self):
993        """
994        Multiple verbs in an unknown configuration raises NotImplementedError.
995        """
996
997        xml = """
998        <iq type='set' to='pubsub.example.org'
999                       from='user@example.org'>
1000          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1001             <publish/>
1002             <create/>
1003          </pubsub>
1004        </iq>
1005        """
1006
1007        self.assertRaises(NotImplementedError,
1008                          pubsub.PubSubRequest.fromElement, parseXml(xml))
1009
1010    def test_fromElementPublish(self):
1011        """
1012        Test parsing a publish request.
1013        """
1014
1015        xml = """
1016        <iq type='set' to='pubsub.example.org'
1017                       from='user@example.org'>
1018          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1019            <publish node='test'/>
1020          </pubsub>
1021        </iq>
1022        """
1023
1024        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1025        self.assertEqual('publish', request.verb)
1026        self.assertEqual(JID('user@example.org'), request.sender)
1027        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1028        self.assertEqual('test', request.nodeIdentifier)
1029        self.assertEqual([], request.items)
1030
1031
1032    def test_fromElementPublishItems(self):
1033        """
1034        Test parsing a publish request with items.
1035        """
1036
1037        xml = """
1038        <iq type='set' to='pubsub.example.org'
1039                       from='user@example.org'>
1040          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1041            <publish node='test'>
1042              <item id="item1"/>
1043              <item id="item2"/>
1044            </publish>
1045          </pubsub>
1046        </iq>
1047        """
1048
1049        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1050        self.assertEqual(2, len(request.items))
1051        self.assertEqual(u'item1', request.items[0]["id"])
1052        self.assertEqual(u'item2', request.items[1]["id"])
1053
1054
1055    def test_fromElementPublishItemsOptions(self):
1056        """
1057        Test parsing a publish request with items and options.
1058
1059        Note that publishing options are not supported, but passing them
1060        shouldn't affect processing of the publish request itself.
1061        """
1062
1063        xml = """
1064        <iq type='set' to='pubsub.example.org'
1065                       from='user@example.org'>
1066          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1067            <publish node='test'>
1068              <item id="item1"/>
1069              <item id="item2"/>
1070            </publish>
1071            <publish-options/>
1072          </pubsub>
1073        </iq>
1074        """
1075
1076        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1077        self.assertEqual(2, len(request.items))
1078        self.assertEqual(u'item1', request.items[0]["id"])
1079        self.assertEqual(u'item2', request.items[1]["id"])
1080
1081    def test_fromElementPublishNoNode(self):
1082        """
1083        A publish request to the root node should raise an exception.
1084        """
1085        xml = """
1086        <iq type='set' to='pubsub.example.org'
1087                       from='user@example.org'>
1088          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1089            <publish/>
1090          </pubsub>
1091        </iq>
1092        """
1093
1094        err = self.assertRaises(error.StanzaError,
1095                                pubsub.PubSubRequest.fromElement,
1096                                parseXml(xml))
1097        self.assertEqual('bad-request', err.condition)
1098        self.assertEqual(NS_PUBSUB_ERRORS, err.appCondition.uri)
1099        self.assertEqual('nodeid-required', err.appCondition.name)
1100
1101
1102    def test_fromElementSubscribe(self):
1103        """
1104        Test parsing a subscription request.
1105        """
1106
1107        xml = """
1108        <iq type='set' to='pubsub.example.org'
1109                       from='user@example.org'>
1110          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1111            <subscribe node='test' jid='user@example.org/Home'/>
1112          </pubsub>
1113        </iq>
1114        """
1115
1116        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1117        self.assertEqual('subscribe', request.verb)
1118        self.assertEqual(JID('user@example.org'), request.sender)
1119        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1120        self.assertEqual('test', request.nodeIdentifier)
1121        self.assertEqual(JID('user@example.org/Home'), request.subscriber)
1122
1123
1124    def test_fromElementSubscribeEmptyNode(self):
1125        """
1126        Test parsing a subscription request to the root node.
1127        """
1128
1129        xml = """
1130        <iq type='set' to='pubsub.example.org'
1131                       from='user@example.org'>
1132          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1133            <subscribe jid='user@example.org/Home'/>
1134          </pubsub>
1135        </iq>
1136        """
1137
1138        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1139        self.assertEqual('', request.nodeIdentifier)
1140
1141
1142    def test_fromElementSubscribeNoJID(self):
1143        """
1144        Subscribe requests without a JID should raise a bad-request exception.
1145        """
1146        xml = """
1147        <iq type='set' to='pubsub.example.org'
1148                       from='user@example.org'>
1149          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1150            <subscribe node='test'/>
1151          </pubsub>
1152        </iq>
1153        """
1154        err = self.assertRaises(error.StanzaError,
1155                                pubsub.PubSubRequest.fromElement,
1156                                parseXml(xml))
1157        self.assertEqual('bad-request', err.condition)
1158        self.assertEqual(NS_PUBSUB_ERRORS, err.appCondition.uri)
1159        self.assertEqual('jid-required', err.appCondition.name)
1160
1161
1162    def test_fromElementSubscribeWithOptions(self):
1163        """
1164        Test parsing a subscription request.
1165        """
1166
1167        xml = """
1168        <iq type='set' to='pubsub.example.org'
1169                       from='user@example.org'>
1170          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1171            <subscribe node='test' jid='user@example.org/Home'/>
1172            <options>
1173              <x xmlns="jabber:x:data" type='submit'>
1174                <field var='FORM_TYPE' type='hidden'>
1175                  <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
1176                </field>
1177                <field var='pubsub#deliver' type='boolean'
1178                       label='Enable delivery?'>
1179                  <value>1</value>
1180                </field>
1181              </x>
1182            </options>
1183          </pubsub>
1184        </iq>
1185        """
1186
1187        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1188        self.assertEqual('subscribe', request.verb)
1189        request.options.typeCheck({'pubsub#deliver': {'type': 'boolean'}})
1190        self.assertEqual({'pubsub#deliver': True}, request.options.getValues())
1191
1192
1193    def test_fromElementSubscribeWithOptionsBadFormType(self):
1194        """
1195        The options form should have the right type.
1196        """
1197
1198        xml = """
1199        <iq type='set' to='pubsub.example.org'
1200                       from='user@example.org'>
1201          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1202            <subscribe node='test' jid='user@example.org/Home'/>
1203            <options>
1204              <x xmlns="jabber:x:data" type='result'>
1205                <field var='FORM_TYPE' type='hidden'>
1206                  <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
1207                </field>
1208                <field var='pubsub#deliver' type='boolean'
1209                       label='Enable delivery?'>
1210                  <value>1</value>
1211                </field>
1212              </x>
1213            </options>
1214          </pubsub>
1215        </iq>
1216        """
1217
1218        err = self.assertRaises(error.StanzaError,
1219                                pubsub.PubSubRequest.fromElement,
1220                                parseXml(xml))
1221        self.assertEqual('bad-request', err.condition)
1222        self.assertEqual("Unexpected form type 'result'", err.text)
1223        self.assertEqual(None, err.appCondition)
1224
1225
1226    def test_fromElementSubscribeWithOptionsEmpty(self):
1227        """
1228        When no (suitable) form is found, the options are empty.
1229        """
1230
1231        xml = """
1232        <iq type='set' to='pubsub.example.org'
1233                       from='user@example.org'>
1234          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1235            <subscribe node='test' jid='user@example.org/Home'/>
1236            <options/>
1237          </pubsub>
1238        </iq>
1239        """
1240
1241        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1242        self.assertEqual('subscribe', request.verb)
1243        self.assertEqual({}, request.options.getValues())
1244
1245
1246    def test_fromElementUnsubscribe(self):
1247        """
1248        Test parsing an unsubscription request.
1249        """
1250
1251        xml = """
1252        <iq type='set' to='pubsub.example.org'
1253                       from='user@example.org'>
1254          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1255            <unsubscribe node='test' jid='user@example.org/Home'/>
1256          </pubsub>
1257        </iq>
1258        """
1259
1260        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1261        self.assertEqual('unsubscribe', request.verb)
1262        self.assertEqual(JID('user@example.org'), request.sender)
1263        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1264        self.assertEqual('test', request.nodeIdentifier)
1265        self.assertEqual(JID('user@example.org/Home'), request.subscriber)
1266
1267
1268    def test_fromElementUnsubscribeWithSubscriptionIdentifier(self):
1269        """
1270        Test parsing an unsubscription request with subscription identifier.
1271        """
1272
1273        xml = """
1274        <iq type='set' to='pubsub.example.org'
1275                       from='user@example.org'>
1276          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1277            <unsubscribe node='test' jid='user@example.org/Home'
1278                         subid='1234'/>
1279          </pubsub>
1280        </iq>
1281        """
1282
1283        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1284        self.assertEqual('1234', request.subscriptionIdentifier)
1285
1286
1287    def test_fromElementUnsubscribeNoJID(self):
1288        """
1289        Unsubscribe requests without a JID should raise a bad-request exception.
1290        """
1291        xml = """
1292        <iq type='set' to='pubsub.example.org'
1293                       from='user@example.org'>
1294          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1295            <unsubscribe node='test'/>
1296          </pubsub>
1297        </iq>
1298        """
1299        err = self.assertRaises(error.StanzaError,
1300                                pubsub.PubSubRequest.fromElement,
1301                                parseXml(xml))
1302        self.assertEqual('bad-request', err.condition)
1303        self.assertEqual(NS_PUBSUB_ERRORS, err.appCondition.uri)
1304        self.assertEqual('jid-required', err.appCondition.name)
1305
1306
1307    def test_fromElementOptionsGet(self):
1308        """
1309        Test parsing a request for getting subscription options.
1310        """
1311
1312        xml = """
1313        <iq type='get' to='pubsub.example.org'
1314                       from='user@example.org'>
1315          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1316            <options node='test' jid='user@example.org/Home'/>
1317          </pubsub>
1318        </iq>
1319        """
1320
1321        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1322        self.assertEqual('optionsGet', request.verb)
1323        self.assertEqual(JID('user@example.org'), request.sender)
1324        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1325        self.assertEqual('test', request.nodeIdentifier)
1326        self.assertEqual(JID('user@example.org/Home'), request.subscriber)
1327
1328
1329    def test_fromElementOptionsGetWithSubscriptionIdentifier(self):
1330        """
1331        Test parsing a request for getting subscription options with subid.
1332        """
1333
1334        xml = """
1335        <iq type='get' to='pubsub.example.org'
1336                       from='user@example.org'>
1337          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1338            <options node='test' jid='user@example.org/Home'
1339                     subid='1234'/>
1340          </pubsub>
1341        </iq>
1342        """
1343
1344        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1345        self.assertEqual('1234', request.subscriptionIdentifier)
1346
1347
1348    def test_fromElementOptionsSet(self):
1349        """
1350        Test parsing a request for setting subscription options.
1351        """
1352
1353        xml = """
1354        <iq type='set' 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              <x xmlns='jabber:x:data' type='submit'>
1359                <field var='FORM_TYPE' type='hidden'>
1360                  <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
1361                </field>
1362                <field var='pubsub#deliver'><value>1</value></field>
1363              </x>
1364            </options>
1365          </pubsub>
1366        </iq>
1367        """
1368
1369        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1370        self.assertEqual('optionsSet', request.verb)
1371        self.assertEqual(JID('user@example.org'), request.sender)
1372        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1373        self.assertEqual('test', request.nodeIdentifier)
1374        self.assertEqual(JID('user@example.org/Home'), request.subscriber)
1375        self.assertEqual({'pubsub#deliver': '1'}, request.options.getValues())
1376
1377
1378    def test_fromElementOptionsSetWithSubscriptionIdentifier(self):
1379        """
1380        Test parsing a request for setting subscription options with subid.
1381        """
1382
1383        xml = """
1384        <iq type='set' to='pubsub.example.org'
1385                       from='user@example.org'>
1386          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1387            <options node='test' jid='user@example.org/Home'
1388                     subid='1234'>
1389              <x xmlns='jabber:x:data' type='submit'>
1390                <field var='FORM_TYPE' type='hidden'>
1391                  <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
1392                </field>
1393                <field var='pubsub#deliver'><value>1</value></field>
1394              </x>
1395            </options>
1396          </pubsub>
1397        </iq>
1398        """
1399
1400        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1401        self.assertEqual('1234', request.subscriptionIdentifier)
1402
1403
1404    def test_fromElementOptionsSetCancel(self):
1405        """
1406        Test parsing a request for cancelling setting subscription options.
1407        """
1408
1409        xml = """
1410        <iq type='set' to='pubsub.example.org'
1411                       from='user@example.org'>
1412          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1413            <options node='test' jid='user@example.org/Home'>
1414              <x xmlns='jabber:x:data' type='cancel'/>
1415            </options>
1416          </pubsub>
1417        </iq>
1418        """
1419
1420        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1421        self.assertEqual('cancel', request.options.formType)
1422
1423
1424    def test_fromElementOptionsSetBadFormType(self):
1425        """
1426        On a options set request unknown fields should be ignored.
1427        """
1428
1429        xml = """
1430        <iq type='set' to='pubsub.example.org'
1431                       from='user@example.org'>
1432          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1433            <options node='test' jid='user@example.org/Home'>
1434              <x xmlns='jabber:x:data' type='result'>
1435                <field var='FORM_TYPE' type='hidden'>
1436                  <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
1437                </field>
1438                <field var='pubsub#deliver'><value>1</value></field>
1439              </x>
1440            </options>
1441          </pubsub>
1442        </iq>
1443        """
1444
1445        err = self.assertRaises(error.StanzaError,
1446                                pubsub.PubSubRequest.fromElement,
1447                                parseXml(xml))
1448        self.assertEqual('bad-request', err.condition)
1449        self.assertEqual("Unexpected form type 'result'", err.text)
1450        self.assertEqual(None, err.appCondition)
1451
1452
1453    def test_fromElementOptionsSetNoForm(self):
1454        """
1455        On a options set request a form is required.
1456        """
1457
1458        xml = """
1459        <iq type='set' to='pubsub.example.org'
1460                       from='user@example.org'>
1461          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1462            <options node='test' jid='user@example.org/Home'/>
1463          </pubsub>
1464        </iq>
1465        """
1466        err = self.assertRaises(error.StanzaError,
1467                                pubsub.PubSubRequest.fromElement,
1468                                parseXml(xml))
1469        self.assertEqual('bad-request', err.condition)
1470        self.assertEqual(None, err.appCondition)
1471
1472
1473    def test_fromElementSubscriptions(self):
1474        """
1475        Test parsing a request for all subscriptions.
1476        """
1477
1478        xml = """
1479        <iq type='get' to='pubsub.example.org'
1480                       from='user@example.org'>
1481          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1482            <subscriptions/>
1483          </pubsub>
1484        </iq>
1485        """
1486
1487        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1488        self.assertEqual('subscriptions', request.verb)
1489        self.assertEqual(JID('user@example.org'), request.sender)
1490        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1491
1492
1493    def test_fromElementAffiliations(self):
1494        """
1495        Test parsing a request for all affiliations.
1496        """
1497
1498        xml = """
1499        <iq type='get' to='pubsub.example.org'
1500                       from='user@example.org'>
1501          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1502            <affiliations/>
1503          </pubsub>
1504        </iq>
1505        """
1506
1507        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1508        self.assertEqual('affiliations', request.verb)
1509        self.assertEqual(JID('user@example.org'), request.sender)
1510        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1511
1512
1513    def test_fromElementCreate(self):
1514        """
1515        Test parsing a request to create a node.
1516        """
1517
1518        xml = """
1519        <iq type='set' to='pubsub.example.org'
1520                       from='user@example.org'>
1521          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1522            <create node='mynode'/>
1523          </pubsub>
1524        </iq>
1525        """
1526
1527        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1528        self.assertEqual('create', request.verb)
1529        self.assertEqual(JID('user@example.org'), request.sender)
1530        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1531        self.assertEqual('mynode', request.nodeIdentifier)
1532        self.assertIdentical(None, request.options)
1533
1534
1535    def test_fromElementCreateInstant(self):
1536        """
1537        Test parsing a request to create an instant node.
1538        """
1539
1540        xml = """
1541        <iq type='set' to='pubsub.example.org'
1542                       from='user@example.org'>
1543          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1544            <create/>
1545          </pubsub>
1546        </iq>
1547        """
1548
1549        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1550        self.assertIdentical(None, request.nodeIdentifier)
1551
1552
1553    def test_fromElementCreateConfigureEmpty(self):
1554        """
1555        Test parsing a request to create a node with an empty configuration.
1556        """
1557
1558        xml = """
1559        <iq type='set' to='pubsub.example.org'
1560                       from='user@example.org'>
1561          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1562            <create node='mynode'/>
1563            <configure/>
1564          </pubsub>
1565        </iq>
1566        """
1567
1568        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1569        self.assertEqual({}, request.options.getValues())
1570        self.assertEqual(u'mynode', request.nodeIdentifier)
1571
1572
1573    def test_fromElementCreateConfigureEmptyWrongOrder(self):
1574        """
1575        Test parsing a request to create a node and configure, wrong order.
1576
1577        The C{configure} element should come after the C{create} request,
1578        but we should accept both orders.
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            <configure/>
1586            <create node='mynode'/>
1587          </pubsub>
1588        </iq>
1589        """
1590
1591        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1592        self.assertEqual({}, request.options.getValues())
1593        self.assertEqual(u'mynode', request.nodeIdentifier)
1594
1595
1596    def test_fromElementCreateConfigure(self):
1597        """
1598        Test parsing a request to create a node.
1599        """
1600
1601        xml = """
1602        <iq type='set' to='pubsub.example.org'
1603                       from='user@example.org'>
1604          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1605            <create node='mynode'/>
1606            <configure>
1607              <x xmlns='jabber:x:data' type='submit'>
1608                <field var='FORM_TYPE' type='hidden'>
1609                  <value>http://jabber.org/protocol/pubsub#node_config</value>
1610                </field>
1611                <field var='pubsub#access_model'><value>open</value></field>
1612                <field var='pubsub#persist_items'><value>0</value></field>
1613              </x>
1614            </configure>
1615          </pubsub>
1616        </iq>
1617        """
1618
1619        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1620        values = request.options
1621        self.assertIn('pubsub#access_model', values)
1622        self.assertEqual(u'open', values['pubsub#access_model'])
1623        self.assertIn('pubsub#persist_items', values)
1624        self.assertEqual(u'0', values['pubsub#persist_items'])
1625
1626
1627    def test_fromElementCreateConfigureBadFormType(self):
1628        """
1629        The form of a node creation request should have the right type.
1630        """
1631
1632        xml = """
1633        <iq type='set' to='pubsub.example.org'
1634                       from='user@example.org'>
1635          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1636            <create node='mynode'/>
1637            <configure>
1638              <x xmlns='jabber:x:data' type='result'>
1639                <field var='FORM_TYPE' type='hidden'>
1640                  <value>http://jabber.org/protocol/pubsub#node_config</value>
1641                </field>
1642                <field var='pubsub#access_model'><value>open</value></field>
1643                <field var='pubsub#persist_items'><value>0</value></field>
1644              </x>
1645            </configure>
1646          </pubsub>
1647        </iq>
1648        """
1649
1650        err = self.assertRaises(error.StanzaError,
1651                                pubsub.PubSubRequest.fromElement,
1652                                parseXml(xml))
1653        self.assertEqual('bad-request', err.condition)
1654        self.assertEqual("Unexpected form type 'result'", err.text)
1655        self.assertEqual(None, err.appCondition)
1656
1657
1658    def test_fromElementDefault(self):
1659        """
1660        Parsing default node configuration request sets required attributes.
1661
1662        Besides C{verb}, C{sender} and C{recipient}, we expect C{nodeType}
1663        to be set. If not passed it receives the default C{u'leaf'}.
1664        """
1665
1666        xml = """
1667        <iq type='get' to='pubsub.example.org'
1668                       from='user@example.org'>
1669          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
1670            <default/>
1671          </pubsub>
1672        </iq>
1673        """
1674
1675        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1676        self.assertEquals(u'default', request.verb)
1677        self.assertEquals(JID('user@example.org'), request.sender)
1678        self.assertEquals(JID('pubsub.example.org'), request.recipient)
1679        self.assertEquals(u'leaf', request.nodeType)
1680
1681
1682    def test_fromElementDefaultCollection(self):
1683        """
1684        Parsing default request for collection sets nodeType to collection.
1685        """
1686
1687        xml = """
1688        <iq type='get' to='pubsub.example.org'
1689                       from='user@example.org'>
1690          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
1691            <default>
1692              <x xmlns='jabber:x:data' type='submit'>
1693                <field var='FORM_TYPE' type='hidden'>
1694                  <value>http://jabber.org/protocol/pubsub#node_config</value>
1695                </field>
1696                <field var='pubsub#node_type'>
1697                  <value>collection</value>
1698                </field>
1699              </x>
1700            </default>
1701
1702          </pubsub>
1703        </iq>
1704        """
1705
1706        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1707        self.assertEquals('collection', request.nodeType)
1708
1709
1710    def test_fromElementConfigureGet(self):
1711        """
1712        Test parsing a node configuration get request.
1713        """
1714
1715        xml = """
1716        <iq type='get' to='pubsub.example.org'
1717                       from='user@example.org'>
1718          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
1719            <configure node='test'/>
1720          </pubsub>
1721        </iq>
1722        """
1723
1724        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1725        self.assertEqual('configureGet', request.verb)
1726        self.assertEqual(JID('user@example.org'), request.sender)
1727        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1728        self.assertEqual('test', request.nodeIdentifier)
1729
1730
1731    def test_fromElementConfigureSet(self):
1732        """
1733        On a node configuration set request the Data Form is parsed.
1734        """
1735
1736        xml = """
1737        <iq type='set' to='pubsub.example.org'
1738                       from='user@example.org'>
1739          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
1740            <configure node='test'>
1741              <x xmlns='jabber:x:data' type='submit'>
1742                <field var='FORM_TYPE' type='hidden'>
1743                  <value>http://jabber.org/protocol/pubsub#node_config</value>
1744                </field>
1745                <field var='pubsub#deliver_payloads'><value>0</value></field>
1746                <field var='pubsub#persist_items'><value>1</value></field>
1747              </x>
1748            </configure>
1749          </pubsub>
1750        </iq>
1751        """
1752
1753        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1754        self.assertEqual('configureSet', request.verb)
1755        self.assertEqual(JID('user@example.org'), request.sender)
1756        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1757        self.assertEqual('test', request.nodeIdentifier)
1758        self.assertEqual({'pubsub#deliver_payloads': '0',
1759                          'pubsub#persist_items': '1'},
1760                         request.options.getValues())
1761
1762
1763    def test_fromElementConfigureSetCancel(self):
1764        """
1765        The node configuration is cancelled, so no options.
1766        """
1767
1768        xml = """
1769        <iq type='set' to='pubsub.example.org'
1770                       from='user@example.org'>
1771          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
1772            <configure node='test'>
1773              <x xmlns='jabber:x:data' type='cancel'/>
1774            </configure>
1775          </pubsub>
1776        </iq>
1777        """
1778
1779        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1780        self.assertEqual('cancel', request.options.formType)
1781
1782
1783    def test_fromElementConfigureSetBadFormType(self):
1784        """
1785        The form of a node configuraton set request should have the right type.
1786        """
1787
1788        xml = """
1789        <iq type='set' to='pubsub.example.org'
1790                       from='user@example.org'>
1791          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
1792            <configure node='test'>
1793              <x xmlns='jabber:x:data' type='result'>
1794                <field var='FORM_TYPE' type='hidden'>
1795                  <value>http://jabber.org/protocol/pubsub#node_config</value>
1796                </field>
1797                <field var='pubsub#deliver_payloads'><value>0</value></field>
1798                <field var='x-myfield'><value>1</value></field>
1799              </x>
1800            </configure>
1801          </pubsub>
1802        </iq>
1803        """
1804
1805        err = self.assertRaises(error.StanzaError,
1806                                pubsub.PubSubRequest.fromElement,
1807                                parseXml(xml))
1808        self.assertEqual('bad-request', err.condition)
1809        self.assertEqual("Unexpected form type 'result'", err.text)
1810        self.assertEqual(None, err.appCondition)
1811
1812
1813    def test_fromElementConfigureSetNoForm(self):
1814        """
1815        On a node configuration set request a form is required.
1816        """
1817
1818        xml = """
1819        <iq type='set' to='pubsub.example.org'
1820                       from='user@example.org'>
1821          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
1822            <configure node='test'/>
1823          </pubsub>
1824        </iq>
1825        """
1826        err = self.assertRaises(error.StanzaError,
1827                                pubsub.PubSubRequest.fromElement,
1828                                parseXml(xml))
1829        self.assertEqual('bad-request', err.condition)
1830        self.assertEqual(None, err.appCondition)
1831
1832
1833    def test_fromElementItems(self):
1834        """
1835        Test parsing an items request.
1836        """
1837        xml = """
1838        <iq type='get' to='pubsub.example.org'
1839                       from='user@example.org'>
1840          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1841            <items node='test'/>
1842          </pubsub>
1843        </iq>
1844        """
1845
1846        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1847        self.assertEqual('items', request.verb)
1848        self.assertEqual(JID('user@example.org'), request.sender)
1849        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1850        self.assertEqual('test', request.nodeIdentifier)
1851        self.assertIdentical(None, request.maxItems)
1852        self.assertIdentical(None, request.subscriptionIdentifier)
1853        self.assertEqual([], request.itemIdentifiers)
1854
1855
1856    def test_fromElementItemsSubscriptionIdentifier(self):
1857        """
1858        Test parsing an items request with subscription identifier.
1859        """
1860        xml = """
1861        <iq type='get' to='pubsub.example.org'
1862                       from='user@example.org'>
1863          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1864            <items node='test' subid='1234'/>
1865          </pubsub>
1866        </iq>
1867        """
1868
1869        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1870        self.assertEqual('1234', request.subscriptionIdentifier)
1871
1872
1873    def test_fromElementRetract(self):
1874        """
1875        Test parsing a retract request.
1876        """
1877
1878        xml = """
1879        <iq type='set' to='pubsub.example.org'
1880                       from='user@example.org'>
1881          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
1882            <retract node='test'>
1883              <item id='item1'/>
1884              <item id='item2'/>
1885            </retract>
1886          </pubsub>
1887        </iq>
1888        """
1889
1890        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1891        self.assertEqual('retract', request.verb)
1892        self.assertEqual(JID('user@example.org'), request.sender)
1893        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1894        self.assertEqual('test', request.nodeIdentifier)
1895        self.assertEqual(['item1', 'item2'], request.itemIdentifiers)
1896
1897
1898    def test_fromElementPurge(self):
1899        """
1900        Test parsing a purge request.
1901        """
1902
1903        xml = """
1904        <iq type='set' to='pubsub.example.org'
1905                       from='user@example.org'>
1906          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
1907            <purge node='test'/>
1908          </pubsub>
1909        </iq>
1910        """
1911
1912        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1913        self.assertEqual('purge', request.verb)
1914        self.assertEqual(JID('user@example.org'), request.sender)
1915        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1916        self.assertEqual('test', request.nodeIdentifier)
1917
1918
1919    def test_fromElementDelete(self):
1920        """
1921        Test parsing a delete request.
1922        """
1923
1924        xml = """
1925        <iq type='set' to='pubsub.example.org'
1926                       from='user@example.org'>
1927          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
1928            <delete node='test'/>
1929          </pubsub>
1930        </iq>
1931        """
1932
1933        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
1934        self.assertEqual('delete', request.verb)
1935        self.assertEqual(JID('user@example.org'), request.sender)
1936        self.assertEqual(JID('pubsub.example.org'), request.recipient)
1937        self.assertEqual('test', request.nodeIdentifier)
1938
1939
1940
1941class PubSubServiceTest(unittest.TestCase, TestableRequestHandlerMixin):
1942    """
1943    Tests for L{pubsub.PubSubService}.
1944    """
1945
1946    def setUp(self):
1947        self.stub = XmlStreamStub()
1948        self.resource = pubsub.PubSubResource()
1949        self.service = pubsub.PubSubService(self.resource)
1950        self.service.send = self.stub.xmlstream.send
1951
1952    def test_interface(self):
1953        """
1954        Do instances of L{pubsub.PubSubService} provide L{iwokkel.IPubSubService}?
1955        """
1956        verify.verifyObject(iwokkel.IPubSubService, self.service)
1957
1958
1959    def test_interfaceIDisco(self):
1960        """
1961        Do instances of L{pubsub.PubSubService} provide L{iwokkel.IDisco}?
1962        """
1963        verify.verifyObject(iwokkel.IDisco, self.service)
1964
1965
1966    def test_connectionMade(self):
1967        """
1968        Verify setup of observers in L{pubsub.connectionMade}.
1969        """
1970        requests = []
1971
1972        def handleRequest(iq):
1973            requests.append(iq)
1974
1975        self.service.xmlstream = self.stub.xmlstream
1976        self.service.handleRequest = handleRequest
1977        self.service.connectionMade()
1978
1979        for namespace in (NS_PUBSUB, NS_PUBSUB_OWNER):
1980            for stanzaType in ('get', 'set'):
1981                iq = domish.Element((None, 'iq'))
1982                iq['type'] = stanzaType
1983                iq.addElement((namespace, 'pubsub'))
1984                self.stub.xmlstream.dispatch(iq)
1985
1986        self.assertEqual(4, len(requests))
1987
1988
1989    def test_getDiscoInfo(self):
1990        """
1991        Test getDiscoInfo calls getNodeInfo and returns some minimal info.
1992        """
1993        def cb(info):
1994            discoInfo = disco.DiscoInfo()
1995            for item in info:
1996                discoInfo.append(item)
1997            self.assertIn(('pubsub', 'service'), discoInfo.identities)
1998            self.assertIn(disco.NS_DISCO_ITEMS, discoInfo.features)
1999
2000        d = self.service.getDiscoInfo(JID('user@example.org/home'),
2001                                      JID('pubsub.example.org'), '')
2002        d.addCallback(cb)
2003        return d
2004
2005
2006    def test_getDiscoInfoNodeType(self):
2007        """
2008        Test getDiscoInfo with node type.
2009        """
2010        def cb(info):
2011            discoInfo = disco.DiscoInfo()
2012            for item in info:
2013                discoInfo.append(item)
2014            self.assertIn(('pubsub', 'collection'), discoInfo.identities)
2015
2016        def getInfo(requestor, target, nodeIdentifier):
2017            return defer.succeed({'type': 'collection',
2018                                  'meta-data': {}})
2019
2020        self.resource.getInfo = getInfo
2021        d = self.service.getDiscoInfo(JID('user@example.org/home'),
2022                                      JID('pubsub.example.org'), '')
2023        d.addCallback(cb)
2024        return d
2025
2026
2027    def test_getDiscoInfoMetaData(self):
2028        """
2029        Test getDiscoInfo with returned meta data.
2030        """
2031        def cb(info):
2032            discoInfo = disco.DiscoInfo()
2033            for item in info:
2034                discoInfo.append(item)
2035
2036            self.assertIn(('pubsub', 'leaf'), discoInfo.identities)
2037            self.assertIn(NS_PUBSUB_META_DATA, discoInfo.extensions)
2038            form = discoInfo.extensions[NS_PUBSUB_META_DATA]
2039            self.assertIn('pubsub#node_type', form.fields)
2040
2041        def getInfo(requestor, target, nodeIdentifier):
2042            metaData = [{'var': 'pubsub#persist_items',
2043                         'label': 'Persist items to storage',
2044                         'value': True}]
2045            return defer.succeed({'type': 'leaf', 'meta-data': metaData})
2046
2047        self.resource.getInfo = getInfo
2048        d = self.service.getDiscoInfo(JID('user@example.org/home'),
2049                                      JID('pubsub.example.org'), '')
2050        d.addCallback(cb)
2051        return d
2052
2053
2054    def test_getDiscoInfoResourceFeatures(self):
2055        """
2056        Test getDiscoInfo with the resource features.
2057        """
2058        def cb(info):
2059            discoInfo = disco.DiscoInfo()
2060            for item in info:
2061                discoInfo.append(item)
2062            self.assertIn('http://jabber.org/protocol/pubsub#publish',
2063                          discoInfo.features)
2064
2065        self.resource.features = ['publish']
2066        d = self.service.getDiscoInfo(JID('user@example.org/home'),
2067                                      JID('pubsub.example.org'), '')
2068        d.addCallback(cb)
2069        return d
2070
2071
2072    def test_getDiscoInfoBadResponse(self):
2073        """
2074        If getInfo returns invalid response, it should be logged, then ignored.
2075        """
2076        def cb(info):
2077            self.assertEquals([], info)
2078            self.assertEqual(1, len(self.flushLoggedErrors(TypeError)))
2079
2080        def getInfo(requestor, target, nodeIdentifier):
2081            return defer.succeed('bad response')
2082
2083        self.resource.getInfo = getInfo
2084        d = self.service.getDiscoInfo(JID('user@example.org/home'),
2085                                      JID('pubsub.example.org'), 'test')
2086        d.addCallback(cb)
2087        return d
2088
2089
2090    def test_getDiscoInfoException(self):
2091        """
2092        If getInfo returns invalid response, it should be logged, then ignored.
2093        """
2094        def cb(info):
2095            self.assertEquals([], info)
2096            self.assertEqual(1, len(self.flushLoggedErrors(NotImplementedError)))
2097
2098        def getInfo(requestor, target, nodeIdentifier):
2099            return defer.fail(NotImplementedError())
2100
2101        self.resource.getInfo = getInfo
2102        d = self.service.getDiscoInfo(JID('user@example.org/home'),
2103                                      JID('pubsub.example.org'), 'test')
2104        d.addCallback(cb)
2105        return d
2106
2107
2108    def test_getDiscoItemsRoot(self):
2109        """
2110        Test getDiscoItems on the root node.
2111        """
2112        def getNodes(requestor, service, nodeIdentifier):
2113            return defer.succeed(['node1', 'node2'])
2114
2115        def cb(items):
2116            self.assertEqual(2, len(items))
2117            item1, item2 = items
2118
2119            self.assertEqual(JID('pubsub.example.org'), item1.entity)
2120            self.assertEqual('node1', item1.nodeIdentifier)
2121
2122            self.assertEqual(JID('pubsub.example.org'), item2.entity)
2123            self.assertEqual('node2', item2.nodeIdentifier)
2124
2125        self.resource.getNodes = getNodes
2126        d = self.service.getDiscoItems(JID('user@example.org/home'),
2127                                       JID('pubsub.example.org'),
2128                                       '')
2129        d.addCallback(cb)
2130        return d
2131
2132
2133    def test_getDiscoItemsRootHideNodes(self):
2134        """
2135        Test getDiscoItems on the root node.
2136        """
2137        def getNodes(requestor, service, nodeIdentifier):
2138            raise Exception("Unexpected call to getNodes")
2139
2140        def cb(items):
2141            self.assertEqual([], items)
2142
2143        self.service.hideNodes = True
2144        self.resource.getNodes = getNodes
2145        d = self.service.getDiscoItems(JID('user@example.org/home'),
2146                                       JID('pubsub.example.org'),
2147                                       '')
2148        d.addCallback(cb)
2149        return d
2150
2151
2152    def test_getDiscoItemsNonRoot(self):
2153        """
2154        Test getDiscoItems on a non-root node.
2155        """
2156        def getNodes(requestor, service, nodeIdentifier):
2157            return defer.succeed(['node1', 'node2'])
2158
2159        def cb(items):
2160            self.assertEqual(2, len(items))
2161
2162        self.resource.getNodes = getNodes
2163        d = self.service.getDiscoItems(JID('user@example.org/home'),
2164                                       JID('pubsub.example.org'),
2165                                       'test')
2166        d.addCallback(cb)
2167        return d
2168
2169
2170    def test_on_publish(self):
2171        """
2172        A publish request should result in L{PubSubService.publish} being
2173        called.
2174        """
2175
2176        xml = """
2177        <iq type='set' to='pubsub.example.org'
2178                       from='user@example.org'>
2179          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2180            <publish node='test'/>
2181          </pubsub>
2182        </iq>
2183        """
2184
2185        def publish(request):
2186            return defer.succeed(None)
2187
2188        self.resource.publish = publish
2189        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2190        return self.handleRequest(xml)
2191
2192
2193    def test_on_subscribe(self):
2194        """
2195        A successful subscription should return the current subscription.
2196        """
2197
2198        xml = """
2199        <iq type='set' to='pubsub.example.org'
2200                       from='user@example.org'>
2201          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2202            <subscribe node='test' jid='user@example.org/Home'/>
2203          </pubsub>
2204        </iq>
2205        """
2206
2207        def subscribe(request):
2208            return defer.succeed(pubsub.Subscription(request.nodeIdentifier,
2209                                                     request.subscriber,
2210                                                     'subscribed'))
2211
2212        def cb(element):
2213            self.assertEqual('pubsub', element.name)
2214            self.assertEqual(NS_PUBSUB, element.uri)
2215            subscription = element.subscription
2216            self.assertEqual(NS_PUBSUB, subscription.uri)
2217            self.assertEqual('test', subscription['node'])
2218            self.assertEqual('user@example.org/Home', subscription['jid'])
2219            self.assertEqual('subscribed', subscription['subscription'])
2220
2221        self.resource.subscribe = subscribe
2222        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2223        d = self.handleRequest(xml)
2224        d.addCallback(cb)
2225        return d
2226
2227
2228    def test_on_subscribeEmptyNode(self):
2229        """
2230        A successful subscription on root node should return no node attribute.
2231        """
2232
2233        xml = """
2234        <iq type='set' to='pubsub.example.org'
2235                       from='user@example.org'>
2236          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2237            <subscribe jid='user@example.org/Home'/>
2238          </pubsub>
2239        </iq>
2240        """
2241
2242        def subscribe(request):
2243            return defer.succeed(pubsub.Subscription(request.nodeIdentifier,
2244                                                     request.subscriber,
2245                                                     'subscribed'))
2246
2247        def cb(element):
2248            self.assertFalse(element.subscription.hasAttribute('node'))
2249
2250        self.resource.subscribe = subscribe
2251        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2252        d = self.handleRequest(xml)
2253        d.addCallback(cb)
2254        return d
2255
2256
2257    def test_on_subscribeSubscriptionIdentifier(self):
2258        """
2259        If a subscription returns a subid, this should be available.
2260        """
2261
2262        xml = """
2263        <iq type='set' to='pubsub.example.org'
2264                       from='user@example.org'>
2265          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2266            <subscribe node='test' jid='user@example.org/Home'/>
2267          </pubsub>
2268        </iq>
2269        """
2270
2271        def subscribe(request):
2272            subscription = pubsub.Subscription(request.nodeIdentifier,
2273                                               request.subscriber,
2274                                               'subscribed',
2275                                               subscriptionIdentifier='1234')
2276            return defer.succeed(subscription)
2277
2278        def cb(element):
2279            self.assertEqual('1234', element.subscription.getAttribute('subid'))
2280
2281        self.resource.subscribe = subscribe
2282        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2283        d = self.handleRequest(xml)
2284        d.addCallback(cb)
2285        return d
2286
2287
2288    def test_on_unsubscribe(self):
2289        """
2290        A successful unsubscription should return an empty response.
2291        """
2292
2293        xml = """
2294        <iq type='set' to='pubsub.example.org'
2295                       from='user@example.org'>
2296          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2297            <unsubscribe node='test' jid='user@example.org/Home'/>
2298          </pubsub>
2299        </iq>
2300        """
2301
2302        def unsubscribe(request):
2303            return defer.succeed(None)
2304
2305        def cb(element):
2306            self.assertIdentical(None, element)
2307
2308        self.resource.unsubscribe = unsubscribe
2309        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2310        d = self.handleRequest(xml)
2311        d.addCallback(cb)
2312        return d
2313
2314
2315    def test_on_unsubscribeSubscriptionIdentifier(self):
2316        """
2317        A successful unsubscription with subid should return an empty response.
2318        """
2319
2320        xml = """
2321        <iq type='set' to='pubsub.example.org'
2322                       from='user@example.org'>
2323          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2324            <unsubscribe node='test' jid='user@example.org/Home' subid='1234'/>
2325          </pubsub>
2326        </iq>
2327        """
2328
2329        def unsubscribe(request):
2330            self.assertEqual('1234', request.subscriptionIdentifier)
2331            return defer.succeed(None)
2332
2333        def cb(element):
2334            self.assertIdentical(None, element)
2335
2336        self.resource.unsubscribe = unsubscribe
2337        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2338        d = self.handleRequest(xml)
2339        d.addCallback(cb)
2340        return d
2341
2342
2343    def test_on_optionsGet(self):
2344        """
2345        Getting subscription options is not supported.
2346        """
2347
2348        xml = """
2349        <iq type='get' to='pubsub.example.org'
2350                       from='user@example.org'>
2351          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2352            <options node='test' jid='user@example.org/Home'/>
2353          </pubsub>
2354        </iq>
2355        """
2356
2357        def cb(result):
2358            self.assertEquals('feature-not-implemented', result.condition)
2359            self.assertEquals('unsupported', result.appCondition.name)
2360            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
2361
2362        d = self.handleRequest(xml)
2363        self.assertFailure(d, error.StanzaError)
2364        d.addCallback(cb)
2365        return d
2366
2367
2368    def test_on_optionsSet(self):
2369        """
2370        Setting subscription options is not supported.
2371        """
2372
2373        xml = """
2374        <iq type='set' to='pubsub.example.org'
2375                       from='user@example.org'>
2376          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2377            <options node='test' jid='user@example.org/Home'>
2378              <x xmlns='jabber:x:data' type='submit'>
2379                <field var='FORM_TYPE' type='hidden'>
2380                  <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
2381                </field>
2382                <field var='pubsub#deliver'><value>1</value></field>
2383              </x>
2384            </options>
2385          </pubsub>
2386        </iq>
2387        """
2388
2389        def cb(result):
2390            self.assertEquals('feature-not-implemented', result.condition)
2391            self.assertEquals('unsupported', result.appCondition.name)
2392            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
2393
2394        d = self.handleRequest(xml)
2395        self.assertFailure(d, error.StanzaError)
2396        d.addCallback(cb)
2397        return d
2398
2399
2400    def test_on_subscriptions(self):
2401        """
2402        A subscriptions request should result in
2403        L{PubSubService.subscriptions} being called and the result prepared
2404        for the response.
2405        """
2406
2407        xml = """
2408        <iq type='get' to='pubsub.example.org'
2409                       from='user@example.org'>
2410          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2411            <subscriptions/>
2412          </pubsub>
2413        </iq>
2414        """
2415
2416        def subscriptions(request):
2417            subscription = pubsub.Subscription('test', JID('user@example.org'),
2418                                               'subscribed')
2419            return defer.succeed([subscription])
2420
2421        def cb(element):
2422            self.assertEqual('pubsub', element.name)
2423            self.assertEqual(NS_PUBSUB, element.uri)
2424            self.assertEqual(NS_PUBSUB, element.subscriptions.uri)
2425            children = list(element.subscriptions.elements())
2426            self.assertEqual(1, len(children))
2427            subscription = children[0]
2428            self.assertEqual('subscription', subscription.name)
2429            self.assertEqual(NS_PUBSUB, subscription.uri, NS_PUBSUB)
2430            self.assertEqual('user@example.org', subscription['jid'])
2431            self.assertEqual('test', subscription['node'])
2432            self.assertEqual('subscribed', subscription['subscription'])
2433
2434        self.resource.subscriptions = subscriptions
2435        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2436        d = self.handleRequest(xml)
2437        d.addCallback(cb)
2438        return d
2439
2440
2441    def test_on_subscriptionsWithSubscriptionIdentifier(self):
2442        """
2443        A subscriptions request response should include subids, if set.
2444        """
2445
2446        xml = """
2447        <iq type='get' to='pubsub.example.org'
2448                       from='user@example.org'>
2449          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2450            <subscriptions/>
2451          </pubsub>
2452        </iq>
2453        """
2454
2455        def subscriptions(request):
2456            subscription = pubsub.Subscription('test', JID('user@example.org'),
2457                                               'subscribed',
2458                                               subscriptionIdentifier='1234')
2459            return defer.succeed([subscription])
2460
2461        def cb(element):
2462            subscription = element.subscriptions.subscription
2463            self.assertEqual('1234', subscription['subid'])
2464
2465        self.resource.subscriptions = subscriptions
2466        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2467        d = self.handleRequest(xml)
2468        d.addCallback(cb)
2469        return d
2470
2471
2472    def test_on_affiliations(self):
2473        """
2474        A subscriptions request should result in
2475        L{PubSubService.affiliations} being called and the result prepared
2476        for the response.
2477        """
2478
2479        xml = """
2480        <iq type='get' to='pubsub.example.org'
2481                       from='user@example.org'>
2482          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2483            <affiliations/>
2484          </pubsub>
2485        </iq>
2486        """
2487
2488        def affiliations(request):
2489            affiliation = ('test', 'owner')
2490            return defer.succeed([affiliation])
2491
2492        def cb(element):
2493            self.assertEqual('pubsub', element.name)
2494            self.assertEqual(NS_PUBSUB, element.uri)
2495            self.assertEqual(NS_PUBSUB, element.affiliations.uri)
2496            children = list(element.affiliations.elements())
2497            self.assertEqual(1, len(children))
2498            affiliation = children[0]
2499            self.assertEqual('affiliation', affiliation.name)
2500            self.assertEqual(NS_PUBSUB, affiliation.uri)
2501            self.assertEqual('test', affiliation['node'])
2502            self.assertEqual('owner', affiliation['affiliation'])
2503
2504        self.resource.affiliations = affiliations
2505        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2506        d = self.handleRequest(xml)
2507        d.addCallback(cb)
2508        return d
2509
2510
2511    def test_on_create(self):
2512        """
2513        Replies to create node requests don't return the created node.
2514        """
2515
2516        xml = """
2517        <iq type='set' to='pubsub.example.org'
2518                       from='user@example.org'>
2519          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2520            <create node='mynode'/>
2521          </pubsub>
2522        </iq>
2523        """
2524
2525        def create(request):
2526            return defer.succeed(request.nodeIdentifier)
2527
2528        def cb(element):
2529            self.assertIdentical(None, element)
2530
2531        self.resource.create = create
2532        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2533        d = self.handleRequest(xml)
2534        d.addCallback(cb)
2535        return d
2536
2537
2538    def test_on_createChanged(self):
2539        """
2540        Replies to create node requests return the created node if changed.
2541        """
2542
2543        xml = """
2544        <iq type='set' to='pubsub.example.org'
2545                       from='user@example.org'>
2546          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2547            <create node='mynode'/>
2548          </pubsub>
2549        </iq>
2550        """
2551
2552        def create(request):
2553            return defer.succeed(u'myrenamednode')
2554
2555        def cb(element):
2556            self.assertEqual('pubsub', element.name)
2557            self.assertEqual(NS_PUBSUB, element.uri)
2558            self.assertEqual(NS_PUBSUB, element.create.uri)
2559            self.assertEqual(u'myrenamednode',
2560                             element.create.getAttribute('node'))
2561
2562        self.resource.create = create
2563        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2564        d = self.handleRequest(xml)
2565        d.addCallback(cb)
2566        return d
2567
2568
2569    def test_on_createInstant(self):
2570        """
2571        Replies to create instant node requests return the created node.
2572        """
2573
2574        xml = """
2575        <iq type='set' to='pubsub.example.org'
2576                       from='user@example.org'>
2577          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2578            <create/>
2579          </pubsub>
2580        </iq>
2581        """
2582
2583        def create(request):
2584            return defer.succeed(u'random')
2585
2586        def cb(element):
2587            self.assertEqual('pubsub', element.name)
2588            self.assertEqual(NS_PUBSUB, element.uri)
2589            self.assertEqual(NS_PUBSUB, element.create.uri)
2590            self.assertEqual(u'random', element.create.getAttribute('node'))
2591
2592        self.resource.create = create
2593        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2594        d = self.handleRequest(xml)
2595        d.addCallback(cb)
2596        return d
2597
2598
2599    def test_on_createWithConfig(self):
2600        """
2601        On a node create with configuration request the Data Form is parsed and
2602        L{PubSubResource.create} is called with the passed options.
2603        """
2604
2605        xml = """
2606        <iq type='set' to='pubsub.example.org'
2607                       from='user@example.org'>
2608          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2609            <create node='mynode'/>
2610            <configure>
2611              <x xmlns='jabber:x:data' type='submit'>
2612                <field var='FORM_TYPE' type='hidden'>
2613                  <value>http://jabber.org/protocol/pubsub#node_config</value>
2614                </field>
2615                <field var='pubsub#deliver_payloads'><value>0</value></field>
2616                <field var='pubsub#persist_items'><value>1</value></field>
2617              </x>
2618            </configure>
2619          </pubsub>
2620        </iq>
2621        """
2622
2623        def getConfigurationOptions():
2624            return {
2625                "pubsub#persist_items":
2626                    {"type": "boolean",
2627                     "label": "Persist items to storage"},
2628                "pubsub#deliver_payloads":
2629                    {"type": "boolean",
2630                     "label": "Deliver payloads with event notifications"}
2631                }
2632
2633        def create(request):
2634            self.assertEqual({'pubsub#deliver_payloads': False,
2635                              'pubsub#persist_items': True},
2636                             request.options.getValues())
2637            return defer.succeed(None)
2638
2639        self.resource.getConfigurationOptions = getConfigurationOptions
2640        self.resource.create = create
2641        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2642        return self.handleRequest(xml)
2643
2644
2645    def test_on_default(self):
2646        """
2647        A default request returns default options filtered by available fields.
2648        """
2649
2650        xml = """
2651        <iq type='get' to='pubsub.example.org'
2652                       from='user@example.org'>
2653          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
2654            <default/>
2655          </pubsub>
2656        </iq>
2657        """
2658        fieldDefs = {
2659                "pubsub#persist_items":
2660                    {"type": "boolean",
2661                     "label": "Persist items to storage"},
2662                "pubsub#deliver_payloads":
2663                    {"type": "boolean",
2664                     "label": "Deliver payloads with event notifications"}
2665                }
2666
2667        def getConfigurationOptions():
2668            return fieldDefs
2669
2670        def default(request):
2671            return defer.succeed({'pubsub#persist_items': 'false',
2672                                  'x-myfield': '1'})
2673
2674        def cb(element):
2675            self.assertEquals('pubsub', element.name)
2676            self.assertEquals(NS_PUBSUB_OWNER, element.uri)
2677            self.assertEquals(NS_PUBSUB_OWNER, element.default.uri)
2678            form = data_form.Form.fromElement(element.default.x)
2679            self.assertEquals(NS_PUBSUB_NODE_CONFIG, form.formNamespace)
2680            form.typeCheck(fieldDefs)
2681            self.assertIn('pubsub#persist_items', form.fields)
2682            self.assertFalse(form.fields['pubsub#persist_items'].value)
2683            self.assertNotIn('x-myfield', form.fields)
2684
2685        self.resource.getConfigurationOptions = getConfigurationOptions
2686        self.resource.default = default
2687        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2688        d = self.handleRequest(xml)
2689        d.addCallback(cb)
2690        return d
2691
2692
2693    def test_on_defaultUnknownNodeType(self):
2694        """
2695        Unknown node types yield non-acceptable.
2696
2697        Both C{getConfigurationOptions} and C{default} must not be called.
2698        """
2699
2700        xml = """
2701        <iq type='get' to='pubsub.example.org'
2702                       from='user@example.org'>
2703          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
2704            <default>
2705              <x xmlns='jabber:x:data' type='submit'>
2706                <field var='FORM_TYPE' type='hidden'>
2707                  <value>http://jabber.org/protocol/pubsub#node_config</value>
2708                </field>
2709                <field var='pubsub#node_type'>
2710                  <value>unknown</value>
2711                </field>
2712              </x>
2713            </default>
2714
2715          </pubsub>
2716        </iq>
2717        """
2718
2719        def getConfigurationOptions():
2720            self.fail("Unexpected call to getConfigurationOptions")
2721
2722        def default(request):
2723            self.fail("Unexpected call to default")
2724
2725        def cb(result):
2726            self.assertEquals('not-acceptable', result.condition)
2727
2728        self.resource.getConfigurationOptions = getConfigurationOptions
2729        self.resource.default = default
2730        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2731        d = self.handleRequest(xml)
2732        self.assertFailure(d, error.StanzaError)
2733        d.addCallback(cb)
2734        return d
2735
2736
2737    def test_on_configureGet(self):
2738        """
2739        On a node configuration get
2740        requestL{PubSubResource.configureGet} is called and results in a
2741        data form with the configuration.
2742        """
2743
2744        xml = """
2745        <iq type='get' to='pubsub.example.org'
2746                       from='user@example.org'>
2747          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
2748            <configure node='test'/>
2749          </pubsub>
2750        </iq>
2751        """
2752
2753        def getConfigurationOptions():
2754            return {
2755                "pubsub#persist_items":
2756                    {"type": "boolean",
2757                     "label": "Persist items to storage"},
2758                "pubsub#deliver_payloads":
2759                    {"type": "boolean",
2760                     "label": "Deliver payloads with event notifications"},
2761                "pubsub#owner":
2762                    {"type": "jid-single",
2763                     "label": "Owner of the node"}
2764                }
2765
2766        def configureGet(request):
2767            return defer.succeed({'pubsub#deliver_payloads': '0',
2768                                  'pubsub#persist_items': '1',
2769                                  'pubsub#owner': JID('user@example.org'),
2770                                  'x-myfield': 'a'})
2771
2772        def cb(element):
2773            self.assertEqual('pubsub', element.name)
2774            self.assertEqual(NS_PUBSUB_OWNER, element.uri)
2775            self.assertEqual(NS_PUBSUB_OWNER, element.configure.uri)
2776            form = data_form.Form.fromElement(element.configure.x)
2777            self.assertEqual(NS_PUBSUB_NODE_CONFIG, form.formNamespace)
2778            fields = form.fields
2779
2780            self.assertIn('pubsub#deliver_payloads', fields)
2781            field = fields['pubsub#deliver_payloads']
2782            self.assertEqual('boolean', field.fieldType)
2783            field.typeCheck()
2784            self.assertEqual(False, field.value)
2785
2786            self.assertIn('pubsub#persist_items', fields)
2787            field = fields['pubsub#persist_items']
2788            self.assertEqual('boolean', field.fieldType)
2789            field.typeCheck()
2790            self.assertEqual(True, field.value)
2791
2792            self.assertIn('pubsub#owner', fields)
2793            field = fields['pubsub#owner']
2794            self.assertEqual('jid-single', field.fieldType)
2795            field.typeCheck()
2796            self.assertEqual(JID('user@example.org'), field.value)
2797
2798            self.assertNotIn('x-myfield', fields)
2799
2800        self.resource.getConfigurationOptions = getConfigurationOptions
2801        self.resource.configureGet = configureGet
2802        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2803        d = self.handleRequest(xml)
2804        d.addCallback(cb)
2805        return d
2806
2807
2808    def test_on_configureSet(self):
2809        """
2810        On a node configuration set request the Data Form is parsed and
2811        L{PubSubResource.configureSet} is called with the passed options.
2812        """
2813
2814        xml = """
2815        <iq type='set' to='pubsub.example.org'
2816                       from='user@example.org'>
2817          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
2818            <configure node='test'>
2819              <x xmlns='jabber:x:data' type='submit'>
2820                <field var='FORM_TYPE' type='hidden'>
2821                  <value>http://jabber.org/protocol/pubsub#node_config</value>
2822                </field>
2823                <field var='pubsub#deliver_payloads'><value>0</value></field>
2824                <field var='pubsub#persist_items'><value>1</value></field>
2825              </x>
2826            </configure>
2827          </pubsub>
2828        </iq>
2829        """
2830
2831        def getConfigurationOptions():
2832            return {
2833                "pubsub#persist_items":
2834                    {"type": "boolean",
2835                     "label": "Persist items to storage"},
2836                "pubsub#deliver_payloads":
2837                    {"type": "boolean",
2838                     "label": "Deliver payloads with event notifications"}
2839                }
2840
2841        def configureSet(request):
2842            self.assertEqual({'pubsub#deliver_payloads': False,
2843                              'pubsub#persist_items': True},
2844                             request.options.getValues())
2845            return defer.succeed(None)
2846
2847        self.resource.getConfigurationOptions = getConfigurationOptions
2848        self.resource.configureSet = configureSet
2849        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2850        return self.handleRequest(xml)
2851
2852
2853    def test_on_configureSetCancel(self):
2854        """
2855        The node configuration is cancelled,
2856        L{PubSubResource.configureSet} not called.
2857        """
2858
2859        xml = """
2860        <iq type='set' to='pubsub.example.org'
2861                       from='user@example.org'>
2862          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
2863            <configure node='test'>
2864              <x xmlns='jabber:x:data' type='cancel'>
2865                <field var='FORM_TYPE' type='hidden'>
2866                  <value>http://jabber.org/protocol/pubsub#node_config</value>
2867                </field>
2868              </x>
2869            </configure>
2870          </pubsub>
2871        </iq>
2872        """
2873
2874        def configureSet(request):
2875            self.fail("Unexpected call to setConfiguration")
2876
2877        self.resource.configureSet = configureSet
2878        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2879        return self.handleRequest(xml)
2880
2881
2882    def test_on_configureSetIgnoreUnknown(self):
2883        """
2884        On a node configuration set request unknown fields should be ignored.
2885        """
2886
2887        xml = """
2888        <iq type='set' to='pubsub.example.org'
2889                       from='user@example.org'>
2890          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
2891            <configure node='test'>
2892              <x xmlns='jabber:x:data' type='submit'>
2893                <field var='FORM_TYPE' type='hidden'>
2894                  <value>http://jabber.org/protocol/pubsub#node_config</value>
2895                </field>
2896                <field var='pubsub#deliver_payloads'><value>0</value></field>
2897                <field var='x-myfield'><value>1</value></field>
2898              </x>
2899            </configure>
2900          </pubsub>
2901        </iq>
2902        """
2903
2904        def getConfigurationOptions():
2905            return {
2906                "pubsub#persist_items":
2907                    {"type": "boolean",
2908                     "label": "Persist items to storage"},
2909                "pubsub#deliver_payloads":
2910                    {"type": "boolean",
2911                     "label": "Deliver payloads with event notifications"}
2912                }
2913
2914        def configureSet(request):
2915            self.assertEquals(['pubsub#deliver_payloads'],
2916                              request.options.keys())
2917
2918        self.resource.getConfigurationOptions = getConfigurationOptions
2919        self.resource.configureSet = configureSet
2920        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2921        return self.handleRequest(xml)
2922
2923
2924    def test_on_configureSetBadFormType(self):
2925        """
2926        On a node configuration set request unknown fields should be ignored.
2927        """
2928
2929        xml = """
2930        <iq type='set' to='pubsub.example.org'
2931                       from='user@example.org'>
2932          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
2933            <configure node='test'>
2934              <x xmlns='jabber:x:data' type='result'>
2935                <field var='FORM_TYPE' type='hidden'>
2936                  <value>http://jabber.org/protocol/pubsub#node_config</value>
2937                </field>
2938                <field var='pubsub#deliver_payloads'><value>0</value></field>
2939                <field var='x-myfield'><value>1</value></field>
2940              </x>
2941            </configure>
2942          </pubsub>
2943        </iq>
2944        """
2945
2946        def cb(result):
2947            self.assertEquals('bad-request', result.condition)
2948            self.assertEqual("Unexpected form type 'result'", result.text)
2949
2950        d = self.handleRequest(xml)
2951        self.assertFailure(d, error.StanzaError)
2952        d.addCallback(cb)
2953        return d
2954
2955
2956    def test_on_items(self):
2957        """
2958        On a items request, return all items for the given node.
2959        """
2960        xml = """
2961        <iq type='get' to='pubsub.example.org'
2962                       from='user@example.org'>
2963          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2964            <items node='test'/>
2965          </pubsub>
2966        </iq>
2967        """
2968
2969        def items(request):
2970            return defer.succeed([pubsub.Item('current')])
2971
2972        def cb(element):
2973            self.assertEqual(NS_PUBSUB, element.uri)
2974            self.assertEqual(NS_PUBSUB, element.items.uri)
2975            self.assertEqual(1, len(element.items.children))
2976            item = element.items.children[-1]
2977            self.assertTrue(domish.IElement.providedBy(item))
2978            self.assertEqual('item', item.name)
2979            self.assertEqual(NS_PUBSUB, item.uri)
2980            self.assertEqual('current', item['id'])
2981
2982        self.resource.items = items
2983        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
2984        d = self.handleRequest(xml)
2985        d.addCallback(cb)
2986        return d
2987
2988
2989    def test_on_retract(self):
2990        """
2991        A retract request should result in L{PubSubResource.retract}
2992        being called.
2993        """
2994
2995        xml = """
2996        <iq type='set' to='pubsub.example.org'
2997                       from='user@example.org'>
2998          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
2999            <retract node='test'>
3000              <item id='item1'/>
3001              <item id='item2'/>
3002            </retract>
3003          </pubsub>
3004        </iq>
3005        """
3006
3007        def retract(request):
3008            return defer.succeed(None)
3009
3010        self.resource.retract = retract
3011        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
3012        return self.handleRequest(xml)
3013
3014
3015    def test_on_purge(self):
3016        """
3017        A purge request should result in L{PubSubResource.purge} being
3018        called.
3019        """
3020
3021        xml = """
3022        <iq type='set' to='pubsub.example.org'
3023                       from='user@example.org'>
3024          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3025            <purge node='test'/>
3026          </pubsub>
3027        </iq>
3028        """
3029
3030        def purge(request):
3031            return defer.succeed(None)
3032
3033        self.resource.purge = purge
3034        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
3035        return self.handleRequest(xml)
3036
3037
3038    def test_on_delete(self):
3039        """
3040        A delete request should result in L{PubSubResource.delete} being
3041        called.
3042        """
3043
3044        xml = """
3045        <iq type='set' to='pubsub.example.org'
3046                       from='user@example.org'>
3047          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3048            <delete node='test'/>
3049          </pubsub>
3050        </iq>
3051        """
3052
3053        def delete(request):
3054            return defer.succeed(None)
3055
3056        self.resource.delete = delete
3057        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
3058        return self.handleRequest(xml)
3059
3060
3061    def test_notifyPublish(self):
3062        """
3063        Publish notifications are sent to the subscribers.
3064        """
3065        subscriber = JID('user@example.org')
3066        subscriptions = [pubsub.Subscription('test', subscriber, 'subscribed')]
3067        items = [pubsub.Item('current')]
3068        notifications = [(subscriber, subscriptions, items)]
3069        self.service.notifyPublish(JID('pubsub.example.org'), 'test',
3070                                   notifications)
3071        message = self.stub.output[-1]
3072
3073        self.assertEquals('message', message.name)
3074        self.assertIdentical(None, message.uri)
3075        self.assertEquals('user@example.org', message['to'])
3076        self.assertEquals('pubsub.example.org', message['from'])
3077        self.assertTrue(message.event)
3078        self.assertEquals(NS_PUBSUB_EVENT, message.event.uri)
3079        self.assertTrue(message.event.items)
3080        self.assertEquals(NS_PUBSUB_EVENT, message.event.items.uri)
3081        self.assertTrue(message.event.items.hasAttribute('node'))
3082        self.assertEquals('test', message.event.items['node'])
3083        itemElements = list(domish.generateElementsQNamed(
3084            message.event.items.children, 'item', NS_PUBSUB_EVENT))
3085        self.assertEquals(1, len(itemElements))
3086        self.assertEquals('current', itemElements[0].getAttribute('id'))
3087
3088
3089    def test_notifyPublishCollection(self):
3090        """
3091        Publish notifications are sent to the subscribers of collections.
3092
3093        The node the item was published to is on the C{items} element, while
3094        the subscribed-to node is in the C{'Collections'} SHIM header.
3095        """
3096        subscriber = JID('user@example.org')
3097        subscriptions = [pubsub.Subscription('', subscriber, 'subscribed')]
3098        items = [pubsub.Item('current')]
3099        notifications = [(subscriber, subscriptions, items)]
3100        self.service.notifyPublish(JID('pubsub.example.org'), 'test',
3101                                   notifications)
3102        message = self.stub.output[-1]
3103
3104        self.assertTrue(message.event.items.hasAttribute('node'))
3105        self.assertEquals('test', message.event.items['node'])
3106        headers = shim.extractHeaders(message)
3107        self.assertIn('Collection', headers)
3108        self.assertIn('', headers['Collection'])
3109
3110
3111    def test_notifyDelete(self):
3112        """
3113        Subscribers should be sent a delete notification.
3114        """
3115        subscriptions = [JID('user@example.org')]
3116        self.service.notifyDelete(JID('pubsub.example.org'), 'test',
3117                                  subscriptions)
3118        message = self.stub.output[-1]
3119
3120        self.assertEquals('message', message.name)
3121        self.assertIdentical(None, message.uri)
3122        self.assertEquals('user@example.org', message['to'])
3123        self.assertEquals('pubsub.example.org', message['from'])
3124        self.assertTrue(message.event)
3125        self.assertEqual(NS_PUBSUB_EVENT, message.event.uri)
3126        self.assertTrue(message.event.delete)
3127        self.assertEqual(NS_PUBSUB_EVENT, message.event.delete.uri)
3128        self.assertTrue(message.event.delete.hasAttribute('node'))
3129        self.assertEqual('test', message.event.delete['node'])
3130
3131
3132    def test_notifyDeleteRedirect(self):
3133        """
3134        Subscribers should be sent a delete notification with redirect.
3135        """
3136        redirectURI = 'xmpp:pubsub.example.org?;node=test2'
3137        subscriptions = [JID('user@example.org')]
3138        self.service.notifyDelete(JID('pubsub.example.org'), 'test',
3139                                  subscriptions, redirectURI)
3140        message = self.stub.output[-1]
3141
3142        self.assertEquals('message', message.name)
3143        self.assertIdentical(None, message.uri)
3144        self.assertEquals('user@example.org', message['to'])
3145        self.assertEquals('pubsub.example.org', message['from'])
3146        self.assertTrue(message.event)
3147        self.assertEqual(NS_PUBSUB_EVENT, message.event.uri)
3148        self.assertTrue(message.event.delete)
3149        self.assertEqual(NS_PUBSUB_EVENT, message.event.delete.uri)
3150        self.assertTrue(message.event.delete.hasAttribute('node'))
3151        self.assertEqual('test', message.event.delete['node'])
3152        self.assertTrue(message.event.delete.redirect)
3153        self.assertEqual(NS_PUBSUB_EVENT, message.event.delete.redirect.uri)
3154        self.assertTrue(message.event.delete.redirect.hasAttribute('uri'))
3155        self.assertEqual(redirectURI, message.event.delete.redirect['uri'])
3156
3157
3158    def test_on_subscriptionsGet(self):
3159        """
3160        Getting subscription options is not supported.
3161        """
3162
3163        xml = """
3164        <iq type='get' to='pubsub.example.org'
3165                       from='user@example.org'>
3166          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3167            <subscriptions/>
3168          </pubsub>
3169        </iq>
3170        """
3171
3172        def cb(result):
3173            self.assertEquals('feature-not-implemented', result.condition)
3174            self.assertEquals('unsupported', result.appCondition.name)
3175            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3176            self.assertEquals('manage-subscriptions',
3177                              result.appCondition['feature'])
3178
3179        d = self.handleRequest(xml)
3180        self.assertFailure(d, error.StanzaError)
3181        d.addCallback(cb)
3182        return d
3183
3184
3185    def test_on_subscriptionsSet(self):
3186        """
3187        Setting subscription options is not supported.
3188        """
3189
3190        xml = """
3191        <iq type='set' to='pubsub.example.org'
3192                       from='user@example.org'>
3193          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3194            <subscriptions/>
3195          </pubsub>
3196        </iq>
3197        """
3198
3199        def cb(result):
3200            self.assertEquals('feature-not-implemented', result.condition)
3201            self.assertEquals('unsupported', result.appCondition.name)
3202            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3203            self.assertEquals('manage-subscriptions',
3204                              result.appCondition['feature'])
3205
3206        d = self.handleRequest(xml)
3207        self.assertFailure(d, error.StanzaError)
3208        d.addCallback(cb)
3209        return d
3210
3211
3212    def test_on_affiliationsGet(self):
3213        """
3214        Getting node affiliations should have.
3215        """
3216
3217        xml = """
3218        <iq type='get' to='pubsub.example.org'
3219                       from='user@example.org'>
3220          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3221            <affiliations node='test'/>
3222          </pubsub>
3223        </iq>
3224        """
3225
3226        def affiliationsGet(request):
3227            self.assertEquals('test', request.nodeIdentifier)
3228            return defer.succeed({JID('user@example.org'): 'owner'})
3229
3230        def cb(element):
3231            self.assertEquals(u'pubsub', element.name)
3232            self.assertEquals(NS_PUBSUB_OWNER, element.uri)
3233            self.assertEquals(NS_PUBSUB_OWNER, element.affiliations.uri)
3234            self.assertEquals(u'test', element.affiliations[u'node'])
3235            children = list(element.affiliations.elements())
3236            self.assertEquals(1, len(children))
3237            affiliation = children[0]
3238            self.assertEquals(u'affiliation', affiliation.name)
3239            self.assertEquals(NS_PUBSUB_OWNER, affiliation.uri)
3240            self.assertEquals(u'user@example.org', affiliation[u'jid'])
3241            self.assertEquals(u'owner', affiliation[u'affiliation'])
3242
3243        self.resource.affiliationsGet = affiliationsGet
3244        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
3245        d = self.handleRequest(xml)
3246        d.addCallback(cb)
3247        return d
3248
3249
3250    def test_on_affiliationsGetEmptyNode(self):
3251        """
3252        Getting node affiliations without node should assume empty node.
3253        """
3254
3255        xml = """
3256        <iq type='get' to='pubsub.example.org'
3257                       from='user@example.org'>
3258          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3259            <affiliations/>
3260          </pubsub>
3261        </iq>
3262        """
3263
3264        def affiliationsGet(request):
3265            self.assertEqual('', request.nodeIdentifier)
3266            return defer.succeed({})
3267
3268        def cb(element):
3269            self.assertFalse(element.affiliations.hasAttribute(u'node'))
3270
3271        self.resource.affiliationsGet = affiliationsGet
3272        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
3273        d = self.handleRequest(xml)
3274        d.addCallback(cb)
3275        return d
3276
3277
3278    def test_on_affiliationsSet(self):
3279        """
3280        Setting node affiliations has the affiliations to be modified.
3281        """
3282
3283        xml = """
3284        <iq type='set' to='pubsub.example.org'
3285                       from='user@example.org'>
3286          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3287            <affiliations node='test'>
3288              <affiliation jid='other@example.org' affiliation='publisher'/>
3289            </affiliations>
3290          </pubsub>
3291        </iq>
3292        """
3293
3294        def affiliationsSet(request):
3295            self.assertEquals(u'test', request.nodeIdentifier)
3296            otherJID = JID(u'other@example.org')
3297            self.assertIn(otherJID, request.affiliations)
3298            self.assertEquals(u'publisher', request.affiliations[otherJID])
3299
3300        self.resource.affiliationsSet = affiliationsSet
3301        return self.handleRequest(xml)
3302
3303
3304    def test_on_affiliationsSetBareJID(self):
3305        """
3306        Affiliations are always on the bare JID.
3307        """
3308
3309        xml = """
3310        <iq type='set' to='pubsub.example.org'
3311                       from='user@example.org'>
3312          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3313            <affiliations node='test'>
3314              <affiliation jid='other@example.org/Home'
3315                           affiliation='publisher'/>
3316            </affiliations>
3317          </pubsub>
3318        </iq>
3319        """
3320
3321        def affiliationsSet(request):
3322            otherJID = JID(u'other@example.org')
3323            self.assertIn(otherJID, request.affiliations)
3324
3325        self.resource.affiliationsSet = affiliationsSet
3326        return self.handleRequest(xml)
3327
3328
3329    def test_on_affiliationsSetMultipleForSameEntity(self):
3330        """
3331        Setting node affiliations can only have one item per entity.
3332        """
3333
3334        xml = """
3335        <iq type='set' to='pubsub.example.org'
3336                       from='user@example.org'>
3337          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3338            <affiliations node='test'>
3339              <affiliation jid='other@example.org' affiliation='publisher'/>
3340              <affiliation jid='other@example.org' affiliation='owner'/>
3341            </affiliations>
3342          </pubsub>
3343        </iq>
3344        """
3345
3346        def cb(result):
3347            self.assertEquals('bad-request', result.condition)
3348
3349        d = self.handleRequest(xml)
3350        self.assertFailure(d, error.StanzaError)
3351        d.addCallback(cb)
3352        return d
3353
3354
3355    def test_on_affiliationsSetMissingJID(self):
3356        """
3357        Setting node affiliations must include a JID per affiliation.
3358        """
3359
3360        xml = """
3361        <iq type='set' to='pubsub.example.org'
3362                       from='user@example.org'>
3363          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3364            <affiliations node='test'>
3365              <affiliation affiliation='publisher'/>
3366            </affiliations>
3367          </pubsub>
3368        </iq>
3369        """
3370
3371        def cb(result):
3372            self.assertEquals('bad-request', result.condition)
3373
3374        d = self.handleRequest(xml)
3375        self.assertFailure(d, error.StanzaError)
3376        d.addCallback(cb)
3377        return d
3378
3379
3380    def test_on_affiliationsSetMissingAffiliation(self):
3381        """
3382        Setting node affiliations must include an affiliation.
3383        """
3384
3385        xml = """
3386        <iq type='set' to='pubsub.example.org'
3387                       from='user@example.org'>
3388          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3389            <affiliations node='test'>
3390              <affiliation jid='other@example.org'/>
3391            </affiliations>
3392          </pubsub>
3393        </iq>
3394        """
3395
3396        def cb(result):
3397            self.assertEquals('bad-request', result.condition)
3398
3399        d = self.handleRequest(xml)
3400        self.assertFailure(d, error.StanzaError)
3401        d.addCallback(cb)
3402        return d
3403
3404
3405
3406class PubSubServiceWithoutResourceTest(unittest.TestCase, TestableRequestHandlerMixin):
3407
3408    def setUp(self):
3409        self.stub = XmlStreamStub()
3410        self.service = pubsub.PubSubService()
3411        self.service.send = self.stub.xmlstream.send
3412
3413
3414    def test_getDiscoInfo(self):
3415        """
3416        Test getDiscoInfo calls getNodeInfo and returns some minimal info.
3417        """
3418        def cb(info):
3419            discoInfo = disco.DiscoInfo()
3420            for item in info:
3421                discoInfo.append(item)
3422            self.assertIn(('pubsub', 'service'), discoInfo.identities)
3423            self.assertIn(disco.NS_DISCO_ITEMS, discoInfo.features)
3424
3425        d = self.service.getDiscoInfo(JID('user@example.org/home'),
3426                                      JID('pubsub.example.org'), '')
3427        d.addCallback(cb)
3428        return d
3429
3430
3431    def test_publish(self):
3432        """
3433        Non-overridden L{PubSubService.publish} yields unsupported error.
3434        """
3435
3436        xml = """
3437        <iq type='set' to='pubsub.example.org'
3438                       from='user@example.org'>
3439          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
3440            <publish node='mynode'/>
3441          </pubsub>
3442        </iq>
3443        """
3444
3445        def cb(result):
3446            self.assertEquals('feature-not-implemented', result.condition)
3447            self.assertEquals('unsupported', result.appCondition.name)
3448            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3449            self.assertEquals('publish', result.appCondition['feature'])
3450
3451        d = self.handleRequest(xml)
3452        self.assertFailure(d, error.StanzaError)
3453        d.addCallback(cb)
3454        return d
3455
3456
3457    def test_subscribe(self):
3458        """
3459        Non-overridden L{PubSubService.subscribe} yields unsupported error.
3460        """
3461
3462        xml = """
3463        <iq type='set' to='pubsub.example.org'
3464                       from='user@example.org'>
3465          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
3466            <subscribe node='test' jid='user@example.org/Home'/>
3467          </pubsub>
3468        </iq>
3469        """
3470
3471        def cb(result):
3472            self.assertEquals('feature-not-implemented', result.condition)
3473            self.assertEquals('unsupported', result.appCondition.name)
3474            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3475            self.assertEquals('subscribe', result.appCondition['feature'])
3476
3477        d = self.handleRequest(xml)
3478        self.assertFailure(d, error.StanzaError)
3479        d.addCallback(cb)
3480        return d
3481
3482
3483    def test_unsubscribe(self):
3484        """
3485        Non-overridden L{PubSubService.unsubscribe} yields unsupported error.
3486        """
3487
3488        xml = """
3489        <iq type='set' to='pubsub.example.org'
3490                       from='user@example.org'>
3491          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
3492            <unsubscribe node='test' jid='user@example.org/Home'/>
3493          </pubsub>
3494        </iq>
3495        """
3496
3497        def cb(result):
3498            self.assertEquals('feature-not-implemented', result.condition)
3499            self.assertEquals('unsupported', result.appCondition.name)
3500            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3501            self.assertEquals('subscribe', result.appCondition['feature'])
3502
3503        d = self.handleRequest(xml)
3504        self.assertFailure(d, error.StanzaError)
3505        d.addCallback(cb)
3506        return d
3507
3508
3509    def test_subscriptions(self):
3510        """
3511        Non-overridden L{PubSubService.subscriptions} yields unsupported error.
3512        """
3513
3514        xml = """
3515        <iq type='get' to='pubsub.example.org'
3516                       from='user@example.org'>
3517          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
3518            <subscriptions/>
3519          </pubsub>
3520        </iq>
3521        """
3522
3523        def cb(result):
3524            self.assertEquals('feature-not-implemented', result.condition)
3525            self.assertEquals('unsupported', result.appCondition.name)
3526            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3527            self.assertEquals('retrieve-subscriptions',
3528                              result.appCondition['feature'])
3529
3530        d = self.handleRequest(xml)
3531        self.assertFailure(d, error.StanzaError)
3532        d.addCallback(cb)
3533        return d
3534
3535
3536    def test_affiliations(self):
3537        """
3538        Non-overridden L{PubSubService.affiliations} yields unsupported error.
3539        """
3540
3541        xml = """
3542        <iq type='get' to='pubsub.example.org'
3543                       from='user@example.org'>
3544          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
3545            <affiliations/>
3546          </pubsub>
3547        </iq>
3548        """
3549
3550        def cb(result):
3551            self.assertEquals('feature-not-implemented', result.condition)
3552            self.assertEquals('unsupported', result.appCondition.name)
3553            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3554            self.assertEquals('retrieve-affiliations',
3555                              result.appCondition['feature'])
3556
3557        d = self.handleRequest(xml)
3558        self.assertFailure(d, error.StanzaError)
3559        d.addCallback(cb)
3560        return d
3561
3562
3563    def test_create(self):
3564        """
3565        Non-overridden L{PubSubService.create} yields unsupported error.
3566        """
3567
3568        xml = """
3569        <iq type='set' to='pubsub.example.org'
3570                       from='user@example.org'>
3571          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
3572            <create node='mynode'/>
3573          </pubsub>
3574        </iq>
3575        """
3576
3577        def cb(result):
3578            self.assertEquals('feature-not-implemented', result.condition)
3579            self.assertEquals('unsupported', result.appCondition.name)
3580            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3581            self.assertEquals('create-nodes', result.appCondition['feature'])
3582
3583        d = self.handleRequest(xml)
3584        self.assertFailure(d, error.StanzaError)
3585        d.addCallback(cb)
3586        return d
3587
3588
3589    def test_getDefaultConfiguration(self):
3590        """
3591        Non-overridden L{PubSubService.getDefaultConfiguration} yields
3592        unsupported error.
3593        """
3594
3595        xml = """
3596        <iq type='get' to='pubsub.example.org'
3597                       from='user@example.org'>
3598          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3599            <default/>
3600          </pubsub>
3601        </iq>
3602        """
3603
3604        def cb(result):
3605            self.assertEquals('feature-not-implemented', result.condition)
3606            self.assertEquals('unsupported', result.appCondition.name)
3607            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3608            self.assertEquals('retrieve-default', result.appCondition['feature'])
3609
3610        d = self.handleRequest(xml)
3611        self.assertFailure(d, error.StanzaError)
3612        d.addCallback(cb)
3613        return d
3614
3615
3616    def test_getConfiguration(self):
3617        """
3618        Non-overridden L{PubSubService.getConfiguration} yields unsupported
3619        error.
3620        """
3621
3622        xml = """
3623        <iq type='get' to='pubsub.example.org'
3624                       from='user@example.org'>
3625          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3626            <configure/>
3627          </pubsub>
3628        </iq>
3629        """
3630
3631        def cb(result):
3632            self.assertEquals('feature-not-implemented', result.condition)
3633            self.assertEquals('unsupported', result.appCondition.name)
3634            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3635            self.assertEquals('config-node', result.appCondition['feature'])
3636
3637        d = self.handleRequest(xml)
3638        self.assertFailure(d, error.StanzaError)
3639        d.addCallback(cb)
3640        return d
3641
3642
3643    def test_setConfiguration(self):
3644        """
3645        Non-overridden L{PubSubService.setConfiguration} yields unsupported
3646        error.
3647        """
3648
3649        xml = """
3650        <iq type='set' to='pubsub.example.org'
3651                       from='user@example.org'>
3652          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3653            <configure node='test'>
3654              <x xmlns='jabber:x:data' type='submit'>
3655                <field var='FORM_TYPE' type='hidden'>
3656                  <value>http://jabber.org/protocol/pubsub#node_config</value>
3657                </field>
3658                <field var='pubsub#deliver_payloads'><value>0</value></field>
3659                <field var='pubsub#persist_items'><value>1</value></field>
3660              </x>
3661            </configure>
3662          </pubsub>
3663        </iq>
3664        """
3665
3666        def cb(result):
3667            self.assertEquals('feature-not-implemented', result.condition)
3668            self.assertEquals('unsupported', result.appCondition.name)
3669            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3670            self.assertEquals('config-node', result.appCondition['feature'])
3671
3672        d = self.handleRequest(xml)
3673        self.assertFailure(d, error.StanzaError)
3674        d.addCallback(cb)
3675        return d
3676
3677
3678    def test_setConfigurationOptionsDict(self):
3679        """
3680        Options should be passed as a dictionary, not a form.
3681        """
3682
3683        xml = """
3684        <iq type='set' to='pubsub.example.org'
3685                       from='user@example.org'>
3686          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3687            <configure node='test'>
3688              <x xmlns='jabber:x:data' type='submit'>
3689                <field var='FORM_TYPE' type='hidden'>
3690                  <value>http://jabber.org/protocol/pubsub#node_config</value>
3691                </field>
3692                <field var='pubsub#deliver_payloads'><value>0</value></field>
3693                <field var='pubsub#persist_items'><value>1</value></field>
3694              </x>
3695            </configure>
3696          </pubsub>
3697        </iq>
3698        """
3699
3700        def getConfigurationOptions():
3701            return {
3702                "pubsub#persist_items":
3703                    {"type": "boolean",
3704                     "label": "Persist items to storage"},
3705                "pubsub#deliver_payloads":
3706                    {"type": "boolean",
3707                     "label": "Deliver payloads with event notifications"}
3708                }
3709
3710        def setConfiguration(requestor, service, nodeIdentifier, options):
3711            self.assertIn('pubsub#deliver_payloads', options)
3712            self.assertFalse(options['pubsub#deliver_payloads'])
3713            self.assertIn('pubsub#persist_items', options)
3714            self.assertTrue(options['pubsub#persist_items'])
3715
3716        self.service.getConfigurationOptions = getConfigurationOptions
3717        self.service.setConfiguration = setConfiguration
3718        return self.handleRequest(xml)
3719
3720
3721    def test_items(self):
3722        """
3723        Non-overridden L{PubSubService.items} yields unsupported error.
3724        """
3725        xml = """
3726        <iq type='get' to='pubsub.example.org'
3727                       from='user@example.org'>
3728          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
3729            <items node='test'/>
3730          </pubsub>
3731        </iq>
3732        """
3733
3734        def cb(result):
3735            self.assertEquals('feature-not-implemented', result.condition)
3736            self.assertEquals('unsupported', result.appCondition.name)
3737            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3738            self.assertEquals('retrieve-items', result.appCondition['feature'])
3739
3740        d = self.handleRequest(xml)
3741        self.assertFailure(d, error.StanzaError)
3742        d.addCallback(cb)
3743        return d
3744
3745
3746    def test_retract(self):
3747        """
3748        Non-overridden L{PubSubService.retract} yields unsupported error.
3749        """
3750        xml = """
3751        <iq type='set' to='pubsub.example.org'
3752                       from='user@example.org'>
3753          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
3754            <retract node='test'>
3755              <item id='item1'/>
3756              <item id='item2'/>
3757            </retract>
3758          </pubsub>
3759        </iq>
3760        """
3761
3762        def cb(result):
3763            self.assertEquals('feature-not-implemented', result.condition)
3764            self.assertEquals('unsupported', result.appCondition.name)
3765            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3766            self.assertEquals('retract-items', result.appCondition['feature'])
3767
3768        d = self.handleRequest(xml)
3769        self.assertFailure(d, error.StanzaError)
3770        d.addCallback(cb)
3771        return d
3772
3773
3774    def test_purge(self):
3775        """
3776        Non-overridden L{PubSubService.purge} yields unsupported error.
3777        """
3778        xml = """
3779        <iq type='set' to='pubsub.example.org'
3780                       from='user@example.org'>
3781          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3782            <purge node='test'/>
3783          </pubsub>
3784        </iq>
3785        """
3786
3787        def cb(result):
3788            self.assertEquals('feature-not-implemented', result.condition)
3789            self.assertEquals('unsupported', result.appCondition.name)
3790            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3791            self.assertEquals('purge-nodes', result.appCondition['feature'])
3792
3793        d = self.handleRequest(xml)
3794        self.assertFailure(d, error.StanzaError)
3795        d.addCallback(cb)
3796        return d
3797
3798
3799    def test_delete(self):
3800        """
3801        Non-overridden L{PubSubService.delete} yields unsupported error.
3802        """
3803        xml = """
3804        <iq type='set' to='pubsub.example.org'
3805                       from='user@example.org'>
3806          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3807            <delete node='test'/>
3808          </pubsub>
3809        </iq>
3810        """
3811
3812        def cb(result):
3813            self.assertEquals('feature-not-implemented', result.condition)
3814            self.assertEquals('unsupported', result.appCondition.name)
3815            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3816            self.assertEquals('delete-nodes', result.appCondition['feature'])
3817
3818        d = self.handleRequest(xml)
3819        self.assertFailure(d, error.StanzaError)
3820        d.addCallback(cb)
3821        return d
3822
3823
3824    def test_unknown(self):
3825        """
3826        Unknown verb yields unsupported error.
3827        """
3828        xml = """
3829        <iq type='get' to='pubsub.example.org'
3830                       from='user@example.org'>
3831          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
3832            <affiliations node='test'/>
3833          </pubsub>
3834        </iq>
3835        """
3836
3837        def cb(result):
3838            self.assertEquals('feature-not-implemented', result.condition)
3839            self.assertEquals('unsupported', result.appCondition.name)
3840            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3841
3842        d = self.handleRequest(xml)
3843        self.assertFailure(d, error.StanzaError)
3844        d.addCallback(cb)
3845        return d
3846
3847
3848
3849class PubSubResourceTest(unittest.TestCase):
3850
3851    def setUp(self):
3852        self.resource = pubsub.PubSubResource()
3853
3854
3855    def test_interface(self):
3856        """
3857        Do instances of L{pubsub.PubSubResource} provide L{iwokkel.IPubSubResource}?
3858        """
3859        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
3860
3861
3862    def test_getNodes(self):
3863        """
3864        Default getNodes returns an empty list.
3865        """
3866        def cb(nodes):
3867            self.assertEquals([], nodes)
3868
3869        d = self.resource.getNodes(JID('user@example.org/home'),
3870                                   JID('pubsub.example.org'),
3871                                   '')
3872        d.addCallback(cb)
3873        return d
3874
3875
3876    def test_publish(self):
3877        """
3878        Non-overridden L{PubSubResource.publish} yields unsupported
3879        error.
3880        """
3881
3882        def cb(result):
3883            self.assertEquals('feature-not-implemented', result.condition)
3884            self.assertEquals('unsupported', result.appCondition.name)
3885            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3886            self.assertEquals('publish', result.appCondition['feature'])
3887
3888        d = self.resource.publish(pubsub.PubSubRequest())
3889        self.assertFailure(d, error.StanzaError)
3890        d.addCallback(cb)
3891        return d
3892
3893
3894    def test_subscribe(self):
3895        """
3896        Non-overridden subscriptions yields unsupported error.
3897        """
3898
3899        def cb(result):
3900            self.assertEquals('feature-not-implemented', result.condition)
3901            self.assertEquals('unsupported', result.appCondition.name)
3902            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3903            self.assertEquals('subscribe', result.appCondition['feature'])
3904
3905        d = self.resource.subscribe(pubsub.PubSubRequest())
3906        self.assertFailure(d, error.StanzaError)
3907        d.addCallback(cb)
3908        return d
3909
3910
3911    def test_unsubscribe(self):
3912        """
3913        Non-overridden unsubscribe yields unsupported error.
3914        """
3915
3916        def cb(result):
3917            self.assertEquals('feature-not-implemented', result.condition)
3918            self.assertEquals('unsupported', result.appCondition.name)
3919            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3920            self.assertEquals('subscribe', result.appCondition['feature'])
3921
3922        d = self.resource.unsubscribe(pubsub.PubSubRequest())
3923        self.assertFailure(d, error.StanzaError)
3924        d.addCallback(cb)
3925        return d
3926
3927
3928    def test_subscriptions(self):
3929        """
3930        Non-overridden subscriptions yields unsupported error.
3931        """
3932
3933        def cb(result):
3934            self.assertEquals('feature-not-implemented', result.condition)
3935            self.assertEquals('unsupported', result.appCondition.name)
3936            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3937            self.assertEquals('retrieve-subscriptions',
3938                              result.appCondition['feature'])
3939
3940        d = self.resource.subscriptions(pubsub.PubSubRequest())
3941        self.assertFailure(d, error.StanzaError)
3942        d.addCallback(cb)
3943        return d
3944
3945
3946    def test_affiliations(self):
3947        """
3948        Non-overridden affiliations yields unsupported error.
3949        """
3950
3951        def cb(result):
3952            self.assertEquals('feature-not-implemented', result.condition)
3953            self.assertEquals('unsupported', result.appCondition.name)
3954            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3955            self.assertEquals('retrieve-affiliations',
3956                              result.appCondition['feature'])
3957
3958        d = self.resource.affiliations(pubsub.PubSubRequest())
3959        self.assertFailure(d, error.StanzaError)
3960        d.addCallback(cb)
3961        return d
3962
3963
3964    def test_create(self):
3965        """
3966        Non-overridden create yields unsupported error.
3967        """
3968
3969        def cb(result):
3970            self.assertEquals('feature-not-implemented', result.condition)
3971            self.assertEquals('unsupported', result.appCondition.name)
3972            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3973            self.assertEquals('create-nodes', result.appCondition['feature'])
3974
3975        d = self.resource.create(pubsub.PubSubRequest())
3976        self.assertFailure(d, error.StanzaError)
3977        d.addCallback(cb)
3978        return d
3979
3980
3981    def test_default(self):
3982        """
3983        Non-overridden default yields unsupported error.
3984        """
3985
3986        def cb(result):
3987            self.assertEquals('feature-not-implemented', result.condition)
3988            self.assertEquals('unsupported', result.appCondition.name)
3989            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
3990            self.assertEquals('retrieve-default',
3991                              result.appCondition['feature'])
3992
3993        d = self.resource.default(pubsub.PubSubRequest())
3994        self.assertFailure(d, error.StanzaError)
3995        d.addCallback(cb)
3996        return d
3997
3998
3999    def test_configureGet(self):
4000        """
4001        Non-overridden configureGet yields unsupported
4002        error.
4003        """
4004
4005        def cb(result):
4006            self.assertEquals('feature-not-implemented', result.condition)
4007            self.assertEquals('unsupported', result.appCondition.name)
4008            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
4009            self.assertEquals('config-node', result.appCondition['feature'])
4010
4011        d = self.resource.configureGet(pubsub.PubSubRequest())
4012        self.assertFailure(d, error.StanzaError)
4013        d.addCallback(cb)
4014        return d
4015
4016
4017    def test_configureSet(self):
4018        """
4019        Non-overridden configureSet yields unsupported error.
4020        """
4021
4022        def cb(result):
4023            self.assertEquals('feature-not-implemented', result.condition)
4024            self.assertEquals('unsupported', result.appCondition.name)
4025            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
4026            self.assertEquals('config-node', result.appCondition['feature'])
4027
4028        d = self.resource.configureSet(pubsub.PubSubRequest())
4029        self.assertFailure(d, error.StanzaError)
4030        d.addCallback(cb)
4031        return d
4032
4033
4034    def test_items(self):
4035        """
4036        Non-overridden items yields unsupported error.
4037        """
4038
4039        def cb(result):
4040            self.assertEquals('feature-not-implemented', result.condition)
4041            self.assertEquals('unsupported', result.appCondition.name)
4042            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
4043            self.assertEquals('retrieve-items', result.appCondition['feature'])
4044
4045        d = self.resource.items(pubsub.PubSubRequest())
4046        self.assertFailure(d, error.StanzaError)
4047        d.addCallback(cb)
4048        return d
4049
4050
4051    def test_retract(self):
4052        """
4053        Non-overridden retract yields unsupported error.
4054        """
4055
4056        def cb(result):
4057            self.assertEquals('feature-not-implemented', result.condition)
4058            self.assertEquals('unsupported', result.appCondition.name)
4059            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
4060            self.assertEquals('retract-items', result.appCondition['feature'])
4061
4062        d = self.resource.retract(pubsub.PubSubRequest())
4063        self.assertFailure(d, error.StanzaError)
4064        d.addCallback(cb)
4065        return d
4066
4067
4068    def test_purge(self):
4069        """
4070        Non-overridden purge yields unsupported error.
4071        """
4072
4073        def cb(result):
4074            self.assertEquals('feature-not-implemented', result.condition)
4075            self.assertEquals('unsupported', result.appCondition.name)
4076            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
4077            self.assertEquals('purge-nodes', result.appCondition['feature'])
4078
4079        d = self.resource.purge(pubsub.PubSubRequest())
4080        self.assertFailure(d, error.StanzaError)
4081        d.addCallback(cb)
4082        return d
4083
4084
4085    def test_delete(self):
4086        """
4087        Non-overridden delete yields unsupported error.
4088        """
4089
4090        def cb(result):
4091            self.assertEquals('feature-not-implemented', result.condition)
4092            self.assertEquals('unsupported', result.appCondition.name)
4093            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
4094            self.assertEquals('delete-nodes', result.appCondition['feature'])
4095
4096        d = self.resource.delete(pubsub.PubSubRequest())
4097        self.assertFailure(d, error.StanzaError)
4098        d.addCallback(cb)
4099        return d
4100
4101
4102    def test_affiliationsGet(self):
4103        """
4104        Non-overridden owner affiliations get yields unsupported error.
4105        """
4106
4107        def cb(result):
4108            self.assertEquals('feature-not-implemented', result.condition)
4109            self.assertEquals('unsupported', result.appCondition.name)
4110            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
4111            self.assertEquals('modify-affiliations',
4112                              result.appCondition['feature'])
4113
4114        d = self.resource.affiliationsGet(pubsub.PubSubRequest())
4115        self.assertFailure(d, error.StanzaError)
4116        d.addCallback(cb)
4117        return d
4118
4119
4120    def test_affiliationsSet(self):
4121        """
4122        Non-overridden owner affiliations set yields unsupported error.
4123        """
4124
4125        def cb(result):
4126            self.assertEquals('feature-not-implemented', result.condition)
4127            self.assertEquals('unsupported', result.appCondition.name)
4128            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
4129            self.assertEquals('modify-affiliations',
4130                              result.appCondition['feature'])
4131
4132        d = self.resource.affiliationsSet(pubsub.PubSubRequest())
4133        self.assertFailure(d, error.StanzaError)
4134        d.addCallback(cb)
4135        return d
Note: See TracBrowser for help on using the repository browser.