source: wokkel/test/test_pubsub.py @ 212:af4ef4a0fd7f

Last change on this file since 212:af4ef4a0fd7f was 212:af4ef4a0fd7f, checked in by Ralph Meijer <ralphm@…>, 4 years ago

Replace basestring with StringType and add tests.

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