source: ralphm-patches/pubsub_request.patch @ 13:42b22edb29bb

Last change on this file since 13:42b22edb29bb was 13:42b22edb29bb, checked in by Ralph Meijer <ralphm@…>, 12 years ago

Make these backwards compatible.

File size: 93.9 KB
  • wokkel/generic.py

    Add PubSubRequest that parses requests and replaces separate parameters.
    
    The publish-subscribe protocol has a large number of optional features. Adding
    support for a feature often means more information needs to be passed, and
    required changing the signature of the methods that need to be overridden by
    adaptors.
    
    This change adds the new PubSubRequest class that features a fromElement
    method that creates a new instance, parses a request and then fills the
    instance attributes where appropriate. This instance is then the only argument
    that is passed to the corresponding handler methods.
    
    diff -r e35e291de060 wokkel/generic.py
    a b  
    1010from zope.interface import implements
    1111
    1212from twisted.internet import defer, protocol
    13 from twisted.words.protocols.jabber import error, xmlstream
     13from twisted.words.protocols.jabber import error, jid, xmlstream
    1414from twisted.words.protocols.jabber.xmlstream import toResponse
    1515from twisted.words.xish import domish, utility
    1616
     
    162162        self.sink.send = lambda obj: self.source.dispatch(obj)
    163163
    164164
     165
     166class Stanza(object):
     167    """
     168    Abstract representation of a stanza.
     169
     170    @ivar sender: The sending entity.
     171    @type sender: L{jid.JID}
     172    @ivar recipient: The receiving entity.
     173    @type recipient: L{jid.JID}
     174    """
     175
     176    sender = None
     177    recipient = None
     178    stanzaType = None
     179
     180    @classmethod
     181    def fromElement(Class, element):
     182        stanza = Class()
     183        stanza.parseElement(element)
     184        return stanza
     185
     186
     187    def parseElement(self, element):
     188        self.sender = jid.internJID(element['from'])
     189        if element.hasAttribute('from'):
     190            self.sender = jid.internJID(element['from'])
     191        if element.hasAttribute('to'):
     192            self.recipient = jid.internJID(element['to'])
     193        self.stanzaType = element.getAttribute('type')
     194
     195
    165196class DeferredXmlStreamFactory(BootstrapMixin, protocol.ClientFactory):
    166197    protocol = xmlstream.XmlStream
    167198
  • wokkel/pubsub.py

    diff -r e35e291de060 wokkel/pubsub.py
    a b  
    1616from twisted.words.protocols.jabber import jid, error, xmlstream
    1717from twisted.words.xish import domish
    1818
    19 from wokkel import disco, data_form, shim
     19from wokkel import disco, data_form, generic, shim
    2020from wokkel.subprotocols import IQHandlerMixin, XMPPHandler
    2121from wokkel.iwokkel import IPubSubClient, IPubSubService
    2222
     
    3131NS_PUBSUB_OWNER = NS_PUBSUB + "#owner"
    3232NS_PUBSUB_NODE_CONFIG = NS_PUBSUB + "#node_config"
    3333NS_PUBSUB_META_DATA = NS_PUBSUB + "#meta-data"
     34NS_PUBSUB_SUBSCRIBE_OPTIONS = NS_PUBSUB + "#subscribe_options"
    3435
    35 # In publish-subscribe namespace XPath query selector.
    36 IN_NS_PUBSUB = '[@xmlns="' + NS_PUBSUB + '"]'
    37 IN_NS_PUBSUB_OWNER = '[@xmlns="' + NS_PUBSUB_OWNER + '"]'
    38 
    39 # Publish-subscribe XPath queries
    40 PUBSUB_ELEMENT = '/pubsub' + IN_NS_PUBSUB
    41 PUBSUB_OWNER_ELEMENT = '/pubsub' + IN_NS_PUBSUB_OWNER
    42 PUBSUB_GET = IQ_GET + PUBSUB_ELEMENT
    43 PUBSUB_SET = IQ_SET + PUBSUB_ELEMENT
    44 PUBSUB_OWNER_GET = IQ_GET + PUBSUB_OWNER_ELEMENT
    45 PUBSUB_OWNER_SET = IQ_SET + PUBSUB_OWNER_ELEMENT
    46 
    47 # Publish-subscribe command XPath queries
    48 PUBSUB_PUBLISH = PUBSUB_SET + '/publish' + IN_NS_PUBSUB
    49 PUBSUB_CREATE = PUBSUB_SET + '/create' + IN_NS_PUBSUB
    50 PUBSUB_SUBSCRIBE = PUBSUB_SET + '/subscribe' + IN_NS_PUBSUB
    51 PUBSUB_UNSUBSCRIBE = PUBSUB_SET + '/unsubscribe' + IN_NS_PUBSUB
    52 PUBSUB_OPTIONS_GET = PUBSUB_GET + '/options' + IN_NS_PUBSUB
    53 PUBSUB_OPTIONS_SET = PUBSUB_SET + '/options' + IN_NS_PUBSUB
    54 PUBSUB_DEFAULT = PUBSUB_OWNER_GET + '/default' + IN_NS_PUBSUB_OWNER
    55 PUBSUB_CONFIGURE_GET = PUBSUB_OWNER_GET + '/configure' + IN_NS_PUBSUB_OWNER
    56 PUBSUB_CONFIGURE_SET = PUBSUB_OWNER_SET + '/configure' + IN_NS_PUBSUB_OWNER
    57 PUBSUB_SUBSCRIPTIONS = PUBSUB_GET + '/subscriptions' + IN_NS_PUBSUB
    58 PUBSUB_AFFILIATIONS = PUBSUB_GET + '/affiliations' + IN_NS_PUBSUB
    59 PUBSUB_AFFILIATIONS_GET = PUBSUB_OWNER_GET + '/affiliations' + \
    60                           IN_NS_PUBSUB_OWNER
    61 PUBSUB_AFFILIATIONS_SET = PUBSUB_OWNER_SET + '/affiliations' + \
    62                           IN_NS_PUBSUB_OWNER
    63 PUBSUB_SUBSCRIPTIONS_GET = PUBSUB_OWNER_GET + '/subscriptions' + \
    64                           IN_NS_PUBSUB_OWNER
    65 PUBSUB_SUBSCRIPTIONS_SET = PUBSUB_OWNER_SET + '/subscriptions' + \
    66                           IN_NS_PUBSUB_OWNER
    67 PUBSUB_ITEMS = PUBSUB_GET + '/items' + IN_NS_PUBSUB
    68 PUBSUB_RETRACT = PUBSUB_SET + '/retract' + IN_NS_PUBSUB
    69 PUBSUB_PURGE = PUBSUB_OWNER_SET + '/purge' + IN_NS_PUBSUB_OWNER
    70 PUBSUB_DELETE = PUBSUB_OWNER_SET + '/delete' + IN_NS_PUBSUB_OWNER
     36# XPath to match pubsub requests
     37PUBSUB_REQUEST = '/iq[@type="get" or @type="set"]/' + \
     38                    'pubsub[@xmlns="' + NS_PUBSUB + '" or ' + \
     39                           '@xmlns="' + NS_PUBSUB_OWNER + '"]'
    7140
    7241class SubscriptionPending(Exception):
    7342    """
     
    9867
    9968
    10069
    101 class BadRequest(PubSubError):
     70class BadRequest(error.StanzaError):
    10271    """
    10372    Bad request stanza error.
    10473    """
    10574    def __init__(self, pubsubCondition=None, text=None):
    106         PubSubError.__init__(self, 'bad-request', pubsubCondition, text)
     75        if pubsubCondition:
     76            appCondition = domish.Element((NS_PUBSUB_ERRORS, pubsubCondition))
     77        else:
     78            appCondition = None
     79        error.StanzaError.__init__(self, 'bad-request',
     80                                         text=text,
     81                                         appCondition=appCondition)
    10782
    10883
    10984
     
    167142
    168143
    169144
    170 class _PubSubRequest(xmlstream.IQ):
     145class PubSubRequest(generic.Stanza):
    171146    """
    172     Publish subscribe request.
     147    A publish-subscribe request.
    173148
    174     @ivar verb: Request verb
    175     @type verb: C{str}
    176     @ivar namespace: Request namespace.
    177     @type namespace: C{str}
    178     @ivar method: Type attribute of the IQ request. Either C{'set'} or C{'get'}
    179     @type method: C{str}
    180     @ivar command: Command element of the request. This is the direct child of
    181                    the C{pubsub} element in the C{namespace} with the name
    182                    C{verb}.
     149    The set of instance variables used depends on the type of request. If
     150    a variable is not applicable or not passed in the request, its value is
     151    C{None}.
     152
     153    @ivar verb: The type of publish-subscribe request. See L{_requestVerbMap}.
     154    @type verb: C{str}.
     155
     156    @ivar affiliations: Affiliations to be modified.
     157    @type affiliations: C{set}
     158    @ivar items: The items to be published, as L{domish.Element}s.
     159    @type items: C{list}
     160    @ivar itemIdentifiers: Identifiers of the items to be retrieved or
     161                           retracted.
     162    @type itemIdentifiers: C{set}
     163    @ivar maxItems: Maximum number of items to retrieve.
     164    @type maxItems: C{int}.
     165    @ivar nodeIdentifier: Identifier of the node the request is about.
     166    @type nodeIdentifier: C{unicode}
     167    @ivar nodeType: The type of node that should be created, or for which the
     168                    configuration is retrieved. C{'leaf'} or C{'collection'}.
     169    @type nodeType: C{str}
     170    @ivar options: Configurations options for nodes, subscriptions and publish
     171                   requests.
     172    @type options: L{data_form.Form}
     173    @ivar subscriber: The subscribing entity.
     174    @type subscriber: L{JID}
     175    @ivar subscriptionIdentifier: Identifier for a specific subscription.
     176    @type subscriptionIdentifier: C{unicode}
     177    @ivar subscriptions: Subscriptions to be modified, as a set of
     178                         L{Subscription}.
     179    @type subscriptions: C{set}
    183180    """
    184181
    185     def __init__(self, xs, verb, namespace=NS_PUBSUB, method='set'):
    186         xmlstream.IQ.__init__(self, xs, method)
    187         self.addElement((namespace, 'pubsub'))
     182    verb = None
    188183
    189         self.command = self.pubsub.addElement(verb)
     184    affiliations = None
     185    items = None
     186    itemIdentifiers = None
     187    maxItems = None
     188    nodeIdentifier = None
     189    nodeType = None
     190    options = None
     191    subscriber = None
     192    subscriptionIdentifier = None
     193    subscriptions = None
    190194
     195    # Map request iq type and subelement name to request verb
     196    _requestVerbMap = {
     197        ('set', NS_PUBSUB, 'publish'): 'publish',
     198        ('set', NS_PUBSUB, 'subscribe'): 'subscribe',
     199        ('set', NS_PUBSUB, 'unsubscribe'): 'unsubscribe',
     200        ('get', NS_PUBSUB, 'options'): 'optionsGet',
     201        ('set', NS_PUBSUB, 'options'): 'optionsSet',
     202        ('get', NS_PUBSUB, 'subscriptions'): 'subscriptions',
     203        ('get', NS_PUBSUB, 'affiliations'): 'affiliations',
     204        ('set', NS_PUBSUB, 'create'): 'create',
     205        ('get', NS_PUBSUB_OWNER, 'default'): 'default',
     206        ('get', NS_PUBSUB_OWNER, 'configure'): 'configureGet',
     207        ('set', NS_PUBSUB_OWNER, 'configure'): 'configureSet',
     208        ('get', NS_PUBSUB, 'items'): 'items',
     209        ('set', NS_PUBSUB, 'retract'): 'retract',
     210        ('set', NS_PUBSUB_OWNER, 'purge'): 'purge',
     211        ('set', NS_PUBSUB_OWNER, 'delete'): 'delete',
     212        ('get', NS_PUBSUB_OWNER, 'affiliations'): 'affiliationsGet',
     213        ('set', NS_PUBSUB_OWNER, 'affiliations'): 'affiliationsSet',
     214        ('get', NS_PUBSUB_OWNER, 'subscriptions'): 'subscriptionsGet',
     215        ('set', NS_PUBSUB_OWNER, 'subscriptions'): 'subscriptionsSet',
     216    }
    191217
    192     def send(self, to):
     218    # Map request verb to request iq type and subelement name
     219    _verbRequestMap = dict(((v, k) for k, v in _requestVerbMap.iteritems()))
     220
     221    # Map request verb to parameter handler names
     222    _parameters = {
     223        'publish': ['node', 'items'],
     224        'subscribe': ['nodeOrEmpty', 'jid'],
     225        'unsubscribe': ['nodeOrEmpty', 'jid'],
     226        'optionsGet': ['nodeOrEmpty', 'jid'],
     227        'optionsSet': ['nodeOrEmpty', 'jid', 'options'],
     228        'subscriptions': [],
     229        'affiliations': [],
     230        'create': ['nodeOrNone'],
     231        'default': ['default'],
     232        'configureGet': ['nodeOrEmpty'],
     233        'configureSet': ['nodeOrEmpty', 'configure'],
     234        'items': ['node', 'maxItems', 'itemIdentifiers'],
     235        'retract': ['node', 'itemIdentifiers'],
     236        'purge': ['node'],
     237        'delete': ['node'],
     238        'affiliationsGet': [],
     239        'affiliationsSet': [],
     240        'subscriptionsGet': [],
     241        'subscriptionsSet': [],
     242    }
     243
     244    def __init__(self, verb=None):
     245        self.verb = verb
     246
     247
     248    @staticmethod
     249    def _findForm(element, formNamespace):
    193250        """
    194         Send out request.
     251        Find a Data Form.
    195252
    196         Extends L{xmlstream.IQ.send} by requiring the C{to} parameter to be
    197         a L{JID} instance.
     253        Look for an element that represents a Data Form with the specified
     254        form namespace as a child element of the given element.
     255        """
     256        if not element:
     257            return None
    198258
    199         @param to: Entity to send the request to.
    200         @type to: L{JID}
     259        form = None
     260        for child in element.elements():
     261            try:
     262                form = data_form.Form.fromElement(child)
     263            except data_form.Error:
     264                continue
     265
     266            if form.formNamespace != NS_PUBSUB_NODE_CONFIG:
     267                continue
     268
     269        return form
     270
     271
     272    def _parse_node(self, verbElement):
    201273        """
    202         destination = to.full()
    203         return xmlstream.IQ.send(self, destination)
     274        Parse the required node identifier out of the verbElement.
     275        """
     276        try:
     277            self.nodeIdentifier = verbElement["node"]
     278        except KeyError:
     279            raise BadRequest('nodeid-required')
     280
     281
     282    def _render_node(self, verbElement):
     283        """
     284        Render the required node identifier on the verbElement.
     285        """
     286        if not self.nodeIdentifier:
     287            raise Exception("Node identifier is required")
     288
     289        verbElement['node'] = self.nodeIdentifier
     290
     291
     292    def _parse_nodeOrEmpty(self, verbElement):
     293        """
     294        Parse the node identifier out of the verbElement. May be empty.
     295        """
     296        self.nodeIdentifier = verbElement.getAttribute("node", '')
     297
     298
     299    def _render_nodeOrEmpty(self, verbElement):
     300        """
     301        Render the node identifier on the verbElement. May be empty.
     302        """
     303        if self.nodeIdentifier:
     304            verbElement['node'] = self.nodeIdentifier
     305
     306
     307    def _parse_nodeOrNone(self, verbElement):
     308        """
     309        Parse the optional node identifier out of the verbElement.
     310        """
     311        self.nodeIdentifier = verbElement.getAttribute("node")
     312
     313
     314    def _render_nodeOrNone(self, verbElement):
     315        """
     316        Render the optional node identifier on the verbElement.
     317        """
     318        if self.nodeIdentifier:
     319            verbElement['node'] = self.nodeIdentifier
     320
     321
     322    def _parse_items(self, verbElement):
     323        """
     324        Parse items out of the verbElement for publish requests.
     325        """
     326        self.items = []
     327        for element in verbElement.elements():
     328            if element.uri == NS_PUBSUB and element.name == 'item':
     329                self.items.append(element)
     330
     331
     332    def _render_items(self, verbElement):
     333        """
     334        Render items into the verbElement for publish requests.
     335        """
     336        if self.items:
     337            for item in self.items:
     338                verbElement.addChild(item)
     339
     340
     341    def _parse_jid(self, verbElement):
     342        """
     343        Parse subscriber out of the verbElement for un-/subscribe requests.
     344        """
     345        try:
     346            self.subscriber = jid.internJID(verbElement["jid"])
     347        except KeyError:
     348            raise BadRequest('jid-required')
     349
     350
     351    def _render_jid(self, verbElement):
     352        """
     353        Render subscriber into the verbElement for un-/subscribe requests.
     354        """
     355        verbElement['jid'] = self.subscriber.full()
     356
     357
     358    def _parse_default(self, verbElement):
     359        """
     360        Parse node type out of a request for the default node configuration.
     361        """
     362        form = PubSubRequest._findForm(verbElement, NS_PUBSUB_NODE_CONFIG)
     363        if form and form.formType == 'submit':
     364            values = form.getValues()
     365            self.nodeType = values.get('pubsub#node_type', 'leaf')
     366        else:
     367            self.nodeType = 'leaf'
     368
     369
     370    def _parse_configure(self, verbElement):
     371        """
     372        Parse options out of a request for setting the node configuration.
     373        """
     374        form = PubSubRequest._findForm(verbElement, NS_PUBSUB_NODE_CONFIG)
     375        if form:
     376            if form.formType == 'submit':
     377                self.options = form.getValues()
     378            elif form.formType == 'cancel':
     379                self.options = {}
     380            else:
     381                raise BadRequest(text="Unexpected form type %r" % form.formType)
     382        else:
     383            raise BadRequest(text="Missing configuration form")
     384
     385
     386
     387    def _parse_itemIdentifiers(self, verbElement):
     388        """
     389        Parse item identifiers out of items and retract requests.
     390        """
     391        self.itemIdentifiers = []
     392        for element in verbElement.elements():
     393            if element.uri == NS_PUBSUB and element.name == 'item':
     394                try:
     395                    self.itemIdentifiers.append(element["id"])
     396                except KeyError:
     397                    raise BadRequest()
     398
     399
     400    def _render_itemIdentifiers(self, verbElement):
     401        """
     402        Render item identifiers into items and retract requests.
     403        """
     404        if self.itemIdentifiers:
     405            for itemIdentifier in self.itemIdentifiers:
     406                item = verbElement.addElement('item')
     407                item['id'] = itemIdentifier
     408
     409
     410    def _parse_maxItems(self, verbElement):
     411        """
     412        Parse maximum items out of an items request.
     413        """
     414        value = verbElement.getAttribute('max_items')
     415
     416        if value:
     417            try:
     418                self.maxItems = int(value)
     419            except ValueError:
     420                raise BadRequest(text="Field max_items requires a positive " +
     421                                      "integer value")
     422
     423
     424    def _render_maxItems(self, verbElement):
     425        """
     426        Parse maximum items into an items request.
     427        """
     428        if self.maxItems:
     429            verbElement['max_items'] = unicode(self.maxItems)
     430
     431
     432    def _parse_options(self, verbElement):
     433        form = PubSubRequest._findForm(verbElement, NS_PUBSUB_SUBSCRIBE_OPTIONS)
     434        if form:
     435            if form.formType == 'submit':
     436                self.options = form.getValues()
     437            elif form.formType == 'cancel':
     438                self.options = {}
     439            else:
     440                raise BadRequest(text="Unexpected form type %r" % form.formType)
     441        else:
     442            raise BadRequest(text="Missing options form")
     443
     444    def parseElement(self, element):
     445        """
     446        Parse the publish-subscribe verb and parameters out of a request.
     447        """
     448        generic.Stanza.parseElement(self, element)
     449
     450        for child in element.pubsub.elements():
     451            key = (self.stanzaType, child.uri, child.name)
     452            try:
     453                verb = self._requestVerbMap[key]
     454            except KeyError:
     455                continue
     456            else:
     457                self.verb = verb
     458                break
     459
     460        if not self.verb:
     461            raise NotImplementedError()
     462
     463        for parameter in self._parameters[verb]:
     464            getattr(self, '_parse_%s' % parameter)(child)
     465
     466
     467    def send(self, xs):
     468        """
     469        Send this request to its recipient.
     470
     471        This renders all of the relevant parameters for this specific
     472        requests into an L{xmlstream.IQ}, and invoke its C{send} method.
     473        This returns a deferred that fires upon reception of a response. See
     474        L{xmlstream.IQ} for details.
     475
     476        @param xs: The XML stream to send the request on.
     477        @type xs: L{xmlstream.XmlStream}
     478        @rtype: L{defer.Deferred}.
     479        """
     480
     481        try:
     482            (self.stanzaType,
     483             childURI,
     484             childName) = self._verbRequestMap[self.verb]
     485        except KeyError:
     486            raise NotImplementedError()
     487
     488        iq = xmlstream.IQ(xs, self.stanzaType)
     489        iq.addElement((childURI, 'pubsub'))
     490        verbElement = iq.pubsub.addElement(childName)
     491
     492        if self.sender:
     493            iq['from'] = self.sender.full()
     494        if self.recipient:
     495            iq['to'] = self.recipient.full()
     496
     497        for parameter in self._parameters[self.verb]:
     498            getattr(self, '_render_%s' % parameter)(verbElement)
     499
     500        return iq.send()
    204501
    205502
    206503
     
    336633        @param nodeIdentifier: Optional suggestion for the id of the node.
    337634        @type nodeIdentifier: C{unicode}
    338635        """
    339 
    340 
    341         request = _PubSubRequest(self.xmlstream, 'create')
    342         if nodeIdentifier:
    343             request.command['node'] = nodeIdentifier
     636        request = PubSubRequest('create')
     637        request.recipient = service
     638        request.nodeIdentifier = nodeIdentifier
    344639
    345640        def cb(iq):
    346641            try:
     
    350645                new_node = nodeIdentifier
    351646            return new_node
    352647
    353         return request.send(service).addCallback(cb)
     648        d = request.send(self.xmlstream)
     649        d.addCallback(cb)
     650        return d
    354651
    355652
    356653    def deleteNode(self, service, nodeIdentifier):
     
    362659        @param nodeIdentifier: The identifier of the node.
    363660        @type nodeIdentifier: C{unicode}
    364661        """
    365         request = _PubSubRequest(self.xmlstream, 'delete', NS_PUBSUB_OWNER)
    366         request.command['node'] = nodeIdentifier
    367         return request.send(service)
     662        request = PubSubRequest('delete')
     663        request.recipient = service
     664        request.nodeIdentifier = nodeIdentifier
     665        return request.send(self.xmlstream)
    368666
    369667
    370668    def subscribe(self, service, nodeIdentifier, subscriber):
     
    379677                           will get notifications of new published items.
    380678        @type subscriber: L{JID}
    381679        """
    382         request = _PubSubRequest(self.xmlstream, 'subscribe')
    383         if nodeIdentifier:
    384             request.command['node'] = nodeIdentifier
    385         request.command['jid'] = subscriber.full()
     680        request = PubSubRequest('subscribe')
     681        request.recipient = service
     682        request.nodeIdentifier = nodeIdentifier
     683        request.subscriber = subscriber
    386684
    387685        def cb(iq):
    388686            subscription = iq.pubsub.subscription["subscription"]
     
    397695                # yielded a stanza error.
    398696                return None
    399697
    400         return request.send(service).addCallback(cb)
     698        d = request.send(self.xmlstream)
     699        d.addCallback(cb)
     700        return d
    401701
    402702
    403703    def unsubscribe(self, service, nodeIdentifier, subscriber):
     
    411711        @param subscriber: The entity to unsubscribe from the node.
    412712        @type subscriber: L{JID}
    413713        """
    414         request = _PubSubRequest(self.xmlstream, 'unsubscribe')
    415         if nodeIdentifier:
    416             request.command['node'] = nodeIdentifier
    417         request.command['jid'] = subscriber.full()
    418         return request.send(service)
     714        request = PubSubRequest('unsubscribe')
     715        request.recipient = service
     716        request.nodeIdentifier = nodeIdentifier
     717        request.subscriber = subscriber
     718        return request.send(self.xmlstream)
    419719
    420720
    421721    def publish(self, service, nodeIdentifier, items=None):
     
    429729        @param items: Optional list of L{Item}s to publish.
    430730        @type items: C{list}
    431731        """
    432         request = _PubSubRequest(self.xmlstream, 'publish')
    433         request.command['node'] = nodeIdentifier
    434         if items:
    435             for item in items:
    436                 request.command.addChild(item)
    437 
    438         return request.send(service)
     732        request = PubSubRequest('publish')
     733        request.recipient = service
     734        request.nodeIdentifier = nodeIdentifier
     735        request.items = items
     736        return request.send(self.xmlstream)
    439737
    440738
    441739    def items(self, service, nodeIdentifier, maxItems=None):
     
    449747        @param maxItems: Optional limit on the number of retrieved items.
    450748        @type maxItems: C{int}
    451749        """
    452         request = _PubSubRequest(self.xmlstream, 'items', method='get')
    453         if nodeIdentifier:
    454             request.command['node'] = nodeIdentifier
     750        request = PubSubRequest('items')
     751        request.recipient = service
     752        request.nodeIdentifier = nodeIdentifier
    455753        if maxItems:
    456             request.command["max_items"] = str(int(maxItems))
     754            request.maxItems = str(int(maxItems))
    457755
    458756        def cb(iq):
    459757            items = []
     
    462760                    items.append(element)
    463761            return items
    464762
    465         return request.send(service).addCallback(cb)
     763        d = request.send(self.xmlstream)
     764        d.addCallback(cb)
     765        return d
    466766
    467767
    468768
     
    497797    implements(IPubSubService)
    498798
    499799    iqHandlers = {
    500             PUBSUB_PUBLISH: '_onPublish',
    501             PUBSUB_CREATE: '_onCreate',
    502             PUBSUB_SUBSCRIBE: '_onSubscribe',
    503             PUBSUB_OPTIONS_GET: '_onOptionsGet',
    504             PUBSUB_OPTIONS_SET: '_onOptionsSet',
    505             PUBSUB_AFFILIATIONS: '_onAffiliations',
    506             PUBSUB_ITEMS: '_onItems',
    507             PUBSUB_RETRACT: '_onRetract',
    508             PUBSUB_SUBSCRIPTIONS: '_onSubscriptions',
    509             PUBSUB_UNSUBSCRIBE: '_onUnsubscribe',
    510 
    511             PUBSUB_AFFILIATIONS_GET: '_onAffiliationsGet',
    512             PUBSUB_AFFILIATIONS_SET: '_onAffiliationsSet',
    513             PUBSUB_CONFIGURE_GET: '_onConfigureGet',
    514             PUBSUB_CONFIGURE_SET: '_onConfigureSet',
    515             PUBSUB_DEFAULT: '_onDefault',
    516             PUBSUB_PURGE: '_onPurge',
    517             PUBSUB_DELETE: '_onDelete',
    518             PUBSUB_SUBSCRIPTIONS_GET: '_onSubscriptionsGet',
    519             PUBSUB_SUBSCRIPTIONS_SET: '_onSubscriptionsSet',
    520 
     800            '/*': '_onPubSubRequest',
    521801            }
    522802
    523803
     
    530810
    531811
    532812    def connectionMade(self):
    533         self.xmlstream.addObserver(PUBSUB_GET, self.handleRequest)
    534         self.xmlstream.addObserver(PUBSUB_SET, self.handleRequest)
    535         self.xmlstream.addObserver(PUBSUB_OWNER_GET, self.handleRequest)
    536         self.xmlstream.addObserver(PUBSUB_OWNER_SET, self.handleRequest)
     813        self.xmlstream.addObserver(PUBSUB_REQUEST, self.handleRequest)
    537814
    538815
    539816    def getDiscoInfo(self, requestor, target, nodeIdentifier):
     
    585862        return d
    586863
    587864
    588     def _findForm(self, element, formNamespace):
    589         if not element:
    590             return None
     865    def _onPubSubRequest(self, iq):
     866        request = PubSubRequest.fromElement(iq)
     867        handler = getattr(self, '_on_%s' % request.verb)
     868        return handler(request)
    591869
    592         form = None
    593         for child in element.elements():
    594             try:
    595                 form = data_form.Form.fromElement(child)
    596             except data_form.Error:
    597                 continue
    598870
    599             if form.formNamespace != NS_PUBSUB_NODE_CONFIG:
    600                 continue
     871    def _on_publish(self, request):
     872        return self.publish(request.sender, request.recipient,
     873                            request.nodeIdentifier, request.items)
    601874
    602         return form
    603875
    604 
    605     def _getParameter_node(self, commandElement):
    606         try:
    607             return commandElement["node"]
    608         except KeyError:
    609             raise BadRequest('nodeid-required')
    610 
    611 
    612     def _getParameter_nodeOrEmpty(self, commandElement):
    613         return commandElement.getAttribute("node", '')
    614 
    615 
    616     def _getParameter_jid(self, commandElement):
    617         try:
    618             return jid.internJID(commandElement["jid"])
    619         except KeyError:
    620             raise BadRequest('jid-required')
    621 
    622 
    623     def _getParameter_max_items(self, commandElement):
    624         value = commandElement.getAttribute('max_items')
    625 
    626         if value:
    627             try:
    628                 return int(value)
    629             except ValueError:
    630                 raise BadRequest(text="Field max_items requires a positive " +
    631                                       "integer value")
    632         else:
    633             return None
    634 
    635 
    636     def _getParameters(self, iq, *names):
    637         requestor = jid.internJID(iq["from"]).userhostJID()
    638         service = jid.internJID(iq["to"])
    639 
    640         params = [requestor, service]
    641 
    642         if names:
    643             command = names[0]
    644             commandElement = getattr(iq.pubsub, command)
    645             if not commandElement:
    646                 raise Exception("Could not find command element %r" % command)
    647 
    648         for name in names[1:]:
    649             try:
    650                 getter = getattr(self, '_getParameter_' + name)
    651             except KeyError:
    652                 raise Exception("No parameter getter for this name")
    653 
    654             params.append(getter(commandElement))
    655 
    656         return params
    657 
    658 
    659     def _onPublish(self, iq):
    660         requestor, service, nodeIdentifier = self._getParameters(
    661                 iq, 'publish', 'node')
    662 
    663         items = []
    664         for element in iq.pubsub.publish.elements():
    665             if element.uri == NS_PUBSUB and element.name == 'item':
    666                 items.append(element)
    667 
    668         return self.publish(requestor, service, nodeIdentifier, items)
    669 
    670 
    671     def _onSubscribe(self, iq):
    672         requestor, service, nodeIdentifier, subscriber = self._getParameters(
    673                 iq, 'subscribe', 'nodeOrEmpty', 'jid')
     876    def _on_subscribe(self, request):
    674877
    675878        def toResponse(result):
    676879            response = domish.Element((NS_PUBSUB, "pubsub"))
     
    681884            subscription["subscription"] = result.state
    682885            return response
    683886
    684         d = self.subscribe(requestor, service, nodeIdentifier, subscriber)
     887        d = self.subscribe(request.sender, request.recipient,
     888                           request.nodeIdentifier, request.subscriber)
    685889        d.addCallback(toResponse)
    686890        return d
    687891
    688892
    689     def _onUnsubscribe(self, iq):
    690         requestor, service, nodeIdentifier, subscriber = self._getParameters(
    691                 iq, 'unsubscribe', 'nodeOrEmpty', 'jid')
     893    def _on_unsubscribe(self, request):
     894        return self.unsubscribe(request.sender, request.recipient,
     895                                request.nodeIdentifier, request.subscriber)
    692896
    693         return self.unsubscribe(requestor, service, nodeIdentifier, subscriber)
    694897
    695 
    696     def _onOptionsGet(self, iq):
     898    def _on_optionsGet(self, request):
    697899        raise Unsupported('subscription-options')
    698900
    699901
    700     def _onOptionsSet(self, iq):
     902    def _on_optionsSet(self, request):
    701903        raise Unsupported('subscription-options')
    702904
    703905
    704     def _onSubscriptions(self, iq):
    705         requestor, service = self._getParameters(iq)
     906    def _on_subscriptions(self, request):
    706907
    707908        def toResponse(result):
    708909            response = domish.Element((NS_PUBSUB, 'pubsub'))
     
    714915                item['subscription'] = subscription.state
    715916            return response
    716917
    717         d = self.subscriptions(requestor, service)
     918        d = self.subscriptions(request.sender, request.recipient)
    718919        d.addCallback(toResponse)
    719920        return d
    720921
    721922
    722     def _onAffiliations(self, iq):
    723         requestor, service = self._getParameters(iq)
     923    def _on_affiliations(self, request):
    724924
    725925        def toResponse(result):
    726926            response = domish.Element((NS_PUBSUB, 'pubsub'))
     
    733933
    734934            return response
    735935
    736         d = self.affiliations(requestor, service)
     936        d = self.affiliations(request.sender, request.recipient)
    737937        d.addCallback(toResponse)
    738938        return d
    739939
    740940
    741     def _onCreate(self, iq):
    742         requestor, service = self._getParameters(iq)
    743         nodeIdentifier = iq.pubsub.create.getAttribute("node")
     941    def _on_create(self, request):
    744942
    745943        def toResponse(result):
    746             if not nodeIdentifier or nodeIdentifier != result:
     944            if not request.nodeIdentifier or request.nodeIdentifier != result:
    747945                response = domish.Element((NS_PUBSUB, 'pubsub'))
    748946                create = response.addElement('create')
    749947                create['node'] = result
     
    751949            else:
    752950                return None
    753951
    754         d = self.create(requestor, service, nodeIdentifier)
     952        d = self.create(request.sender, request.recipient,
     953                        request.nodeIdentifier)
    755954        d.addCallback(toResponse)
    756955        return d
    757956
     
    771970            fields.append(data_form.Field.fromDict(option))
    772971        return fields
    773972
     973
    774974    def _formFromConfiguration(self, values):
    775975        options = self.getConfigurationOptions()
    776976        fields = self._makeFields(options, values)
     
    780980
    781981        return form
    782982
     983
    783984    def _checkConfiguration(self, values):
    784985        options = self.getConfigurationOptions()
    785986        processedValues = {}
     
    8051006        return processedValues
    8061007
    8071008
    808     def _onDefault(self, iq):
    809         requestor, service = self._getParameters(iq)
     1009    def _on_default(self, request):
    8101010
    8111011        def toResponse(options):
    8121012            response = domish.Element((NS_PUBSUB_OWNER, "pubsub"))
     
    8141014            default.addChild(self._formFromConfiguration(options).toElement())
    8151015            return response
    8161016
    817         form = self._findForm(iq.pubsub.config, NS_PUBSUB_NODE_CONFIG)
    818         values = form and form.formType == 'result' and form.getValues() or {}
    819         nodeType = values.get('pubsub#node_type', 'leaf')
    820 
    821         if nodeType not in ('leaf', 'collections'):
     1017        if request.nodeType not in ('leaf', 'collection'):
    8221018            return defer.fail(error.StanzaError('not-acceptable'))
    8231019
    824         d = self.getDefaultConfiguration(requestor, service, nodeType)
     1020        d = self.getDefaultConfiguration(request.sender, request.recipient,
     1021                                         request.nodeType)
    8251022        d.addCallback(toResponse)
    8261023        return d
    8271024
    8281025
    829     def _onConfigureGet(self, iq):
    830         requestor, service, nodeIdentifier = self._getParameters(
    831                 iq, 'configure', 'nodeOrEmpty')
    832 
     1026    def _on_configureGet(self, request):
    8331027        def toResponse(options):
    8341028            response = domish.Element((NS_PUBSUB_OWNER, "pubsub"))
    8351029            configure = response.addElement("configure")
    836             configure.addChild(self._formFromConfiguration(options).toElement())
     1030            form = self._formFromConfiguration(options)
     1031            configure.addChild(form.toElement())
    8371032
    838             if nodeIdentifier:
    839                 configure["node"] = nodeIdentifier
     1033            if request.nodeIdentifier:
     1034                configure["node"] = request.nodeIdentifier
    8401035
    8411036            return response
    8421037
    843         d = self.getConfiguration(requestor, service, nodeIdentifier)
     1038        d = self.getConfiguration(request.sender, request.recipient,
     1039                                  request.nodeIdentifier)
    8441040        d.addCallback(toResponse)
    8451041        return d
    8461042
    8471043
    848     def _onConfigureSet(self, iq):
    849         requestor, service, nodeIdentifier = self._getParameters(
    850                 iq, 'configure', 'nodeOrEmpty')
     1044    def _on_configureSet(self, request):
     1045        if request.options:
     1046            request.options = self._checkConfiguration(request.options)
     1047            return self.setConfiguration(request.sender, request.recipient,
     1048                                         request.nodeIdentifier,
     1049                                         request.options)
     1050        else:
     1051            return None
    8511052
    852         # Search configuration form with correct FORM_TYPE and process it
    8531053
    854         form = self._findForm(iq.pubsub.configure, NS_PUBSUB_NODE_CONFIG)
    8551054
    856         if form:
    857             if form.formType == 'submit':
    858                 options = self._checkConfiguration(form.getValues())
    859 
    860                 return self.setConfiguration(requestor, service,
    861                                              nodeIdentifier, options)
    862             elif form.formType == 'cancel':
    863                 return None
    864 
    865         raise BadRequest()
    866 
    867 
    868     def _onItems(self, iq):
    869         requestor, service, nodeIdentifier, maxItems = self._getParameters(
    870                 iq, 'items', 'nodeOrEmpty', 'max_items')
    871 
    872         itemIdentifiers = []
    873         for child in iq.pubsub.items.elements():
    874             if child.name == 'item' and child.uri == NS_PUBSUB:
    875                 try:
    876                     itemIdentifiers.append(child["id"])
    877                 except KeyError:
    878                     raise BadRequest()
     1055    def _on_items(self, request):
    8791056
    8801057        def toResponse(result):
    8811058            response = domish.Element((NS_PUBSUB, 'pubsub'))
    8821059            items = response.addElement('items')
    883             if nodeIdentifier:
    884                 items["node"] = nodeIdentifier
     1060            items["node"] = request.nodeIdentifier
    8851061
    8861062            for item in result:
    8871063                items.addChild(item)
    8881064
    8891065            return response
    8901066
    891         d = self.items(requestor, service, nodeIdentifier, maxItems,
    892                        itemIdentifiers)
     1067        d = self.items(request.sender, request.recipient,
     1068                       request.nodeIdentifier, request.maxItems,
     1069                       request.itemIdentifiers)
    8931070        d.addCallback(toResponse)
    8941071        return d
    8951072
    8961073
    897     def _onRetract(self, iq):
    898         requestor, service, nodeIdentifier = self._getParameters(
    899                 iq, 'retract', 'node')
     1074    def _on_retract(self, request):
     1075        return self.retract(request.sender, request.recipient,
     1076                            request.nodeIdentifier, request.itemIdentifiers)
    9001077
    901         itemIdentifiers = []
    902         for child in iq.pubsub.retract.elements():
    903             if child.uri == NS_PUBSUB and child.name == 'item':
    904                 try:
    905                     itemIdentifiers.append(child["id"])
    906                 except KeyError:
    907                     raise BadRequest()
    9081078
    909         return self.retract(requestor, service, nodeIdentifier,
    910                             itemIdentifiers)
     1079    def _on_purge(self, request):
     1080        return self.purge(request.sender, request.recipient,
     1081                          request.nodeIdentifier)
    9111082
    9121083
    913     def _onPurge(self, iq):
    914         requestor, service, nodeIdentifier = self._getParameters(
    915                 iq, 'purge', 'node')
    916         return self.purge(requestor, service, nodeIdentifier)
     1084    def _on_delete(self, request):
     1085        return self.delete(request.sender, request.recipient,
     1086                           request.nodeIdentifier)
    9171087
    9181088
    919     def _onDelete(self, iq):
    920         requestor, service, nodeIdentifier = self._getParameters(
    921                 iq, 'delete', 'node')
    922         return self.delete(requestor, service, nodeIdentifier)
    923 
    924 
    925     def _onAffiliationsGet(self, iq):
     1089    def _on_affiliationsGet(self, iq):
    9261090        raise Unsupported('modify-affiliations')
    9271091
    9281092
    929     def _onAffiliationsSet(self, iq):
     1093    def _on_affiliationsSet(self, iq):
    9301094        raise Unsupported('modify-affiliations')
    9311095
    9321096
    933     def _onSubscriptionsGet(self, iq):
     1097    def _on_subscriptionsGet(self, iq):
    9341098        raise Unsupported('manage-subscriptions')
    9351099
    9361100
    937     def _onSubscriptionsSet(self, iq):
     1101    def _on_subscriptionsSet(self, iq):
    9381102        raise Unsupported('manage-subscriptions')
    9391103
    9401104    # public methods
  • wokkel/test/test_pubsub.py

    diff -r e35e291de060 wokkel/test/test_pubsub.py
    a b  
    1515from twisted.words.protocols.jabber.xmlstream import toResponse
    1616
    1717from wokkel import data_form, disco, iwokkel, pubsub, shim
     18from wokkel.generic import parseXml
    1819from wokkel.test.helpers import TestableRequestHandlerMixin, XmlStreamStub
    1920
    2021NS_PUBSUB = 'http://jabber.org/protocol/pubsub'
     
    487488
    488489
    489490
     491class PubSubRequestTest(unittest.TestCase):
     492
     493    def test_fromElementPublish(self):
     494        """
     495        Test parsing a publish request.
     496        """
     497
     498        xml = """
     499        <iq type='set' to='pubsub.example.org'
     500                       from='user@example.org'>
     501          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     502            <publish node='test'/>
     503          </pubsub>
     504        </iq>
     505        """
     506
     507        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     508        self.assertEqual('publish', request.verb)
     509        self.assertEqual(JID('user@example.org'), request.sender)
     510        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     511        self.assertEqual('test', request.nodeIdentifier)
     512        self.assertEqual([], request.items)
     513
     514
     515    def test_fromElementPublishItems(self):
     516        """
     517        Test parsing a publish request with items.
     518        """
     519
     520        xml = """
     521        <iq type='set' to='pubsub.example.org'
     522                       from='user@example.org'>
     523          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     524            <publish node='test'>
     525              <item id="item1"/>
     526              <item id="item2"/>
     527            </publish>
     528          </pubsub>
     529        </iq>
     530        """
     531
     532        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     533        self.assertEqual(2, len(request.items))
     534        self.assertEqual(u'item1', request.items[0]["id"])
     535        self.assertEqual(u'item2', request.items[1]["id"])
     536
     537
     538    def test_fromElementPublishNoNode(self):
     539        """
     540        A publish request to the root node should raise an exception.
     541        """
     542        xml = """
     543        <iq type='set' to='pubsub.example.org'
     544                       from='user@example.org'>
     545          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     546            <publish/>
     547          </pubsub>
     548        </iq>
     549        """
     550
     551        err = self.assertRaises(error.StanzaError,
     552                                pubsub.PubSubRequest.fromElement,
     553                                parseXml(xml))
     554        self.assertEqual('bad-request', err.condition)
     555        self.assertEqual(NS_PUBSUB_ERRORS, err.appCondition.uri)
     556        self.assertEqual('nodeid-required', err.appCondition.name)
     557
     558
     559    def test_fromElementSubscribe(self):
     560        """
     561        Test parsing a subscription request.
     562        """
     563
     564        xml = """
     565        <iq type='set' to='pubsub.example.org'
     566                       from='user@example.org'>
     567          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     568            <subscribe node='test' jid='user@example.org/Home'/>
     569          </pubsub>
     570        </iq>
     571        """
     572
     573        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     574        self.assertEqual('subscribe', request.verb)
     575        self.assertEqual(JID('user@example.org'), request.sender)
     576        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     577        self.assertEqual('test', request.nodeIdentifier)
     578        self.assertEqual(JID('user@example.org/Home'), request.subscriber)
     579
     580
     581    def test_fromElementSubscribeEmptyNode(self):
     582        """
     583        Test parsing a subscription request to the root node.
     584        """
     585
     586        xml = """
     587        <iq type='set' to='pubsub.example.org'
     588                       from='user@example.org'>
     589          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     590            <subscribe jid='user@example.org/Home'/>
     591          </pubsub>
     592        </iq>
     593        """
     594
     595        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     596        self.assertEqual('', request.nodeIdentifier)
     597
     598
     599    def test_fromElementSubscribeNoJID(self):
     600        """
     601        Subscribe requests without a JID should raise a bad-request exception.
     602        """
     603        xml = """
     604        <iq type='set' to='pubsub.example.org'
     605                       from='user@example.org'>
     606          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     607            <subscribe node='test'/>
     608          </pubsub>
     609        </iq>
     610        """
     611        err = self.assertRaises(error.StanzaError,
     612                                pubsub.PubSubRequest.fromElement,
     613                                parseXml(xml))
     614        self.assertEqual('bad-request', err.condition)
     615        self.assertEqual(NS_PUBSUB_ERRORS, err.appCondition.uri)
     616        self.assertEqual('jid-required', err.appCondition.name)
     617
     618    def test_fromElementUnsubscribe(self):
     619        """
     620        Test parsing an unsubscription request.
     621        """
     622
     623        xml = """
     624        <iq type='set' to='pubsub.example.org'
     625                       from='user@example.org'>
     626          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     627            <unsubscribe node='test' jid='user@example.org/Home'/>
     628          </pubsub>
     629        </iq>
     630        """
     631
     632        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     633        self.assertEqual('unsubscribe', request.verb)
     634        self.assertEqual(JID('user@example.org'), request.sender)
     635        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     636        self.assertEqual('test', request.nodeIdentifier)
     637        self.assertEqual(JID('user@example.org/Home'), request.subscriber)
     638
     639
     640    def test_fromElementUnsubscribeNoJID(self):
     641        """
     642        Unsubscribe requests without a JID should raise a bad-request exception.
     643        """
     644        xml = """
     645        <iq type='set' to='pubsub.example.org'
     646                       from='user@example.org'>
     647          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     648            <unsubscribe node='test'/>
     649          </pubsub>
     650        </iq>
     651        """
     652        err = self.assertRaises(error.StanzaError,
     653                                pubsub.PubSubRequest.fromElement,
     654                                parseXml(xml))
     655        self.assertEqual('bad-request', err.condition)
     656        self.assertEqual(NS_PUBSUB_ERRORS, err.appCondition.uri)
     657        self.assertEqual('jid-required', err.appCondition.name)
     658
     659
     660    def test_fromElementOptionsGet(self):
     661        """
     662        Test parsing a request for getting subscription options.
     663        """
     664
     665        xml = """
     666        <iq type='get' to='pubsub.example.org'
     667                       from='user@example.org'>
     668          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     669            <options node='test' jid='user@example.org/Home'/>
     670          </pubsub>
     671        </iq>
     672        """
     673
     674        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     675        self.assertEqual('optionsGet', request.verb)
     676
     677
     678    def test_fromElementOptionsSet(self):
     679        """
     680        Test parsing a request for setting subscription options.
     681        """
     682
     683        xml = """
     684        <iq type='set' to='pubsub.example.org'
     685                       from='user@example.org'>
     686          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     687            <options node='test' jid='user@example.org/Home'>
     688              <x xmlns='jabber:x:data' type='submit'>
     689                <field var='FORM_TYPE' type='hidden'>
     690                  <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
     691                </field>
     692                <field var='pubsub#deliver'><value>1</value></field>
     693              </x>
     694            </options>
     695          </pubsub>
     696        </iq>
     697        """
     698
     699        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     700        self.assertEqual('optionsSet', request.verb)
     701        self.assertEqual(JID('user@example.org'), request.sender)
     702        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     703        self.assertEqual('test', request.nodeIdentifier)
     704        self.assertEqual(JID('user@example.org/Home'), request.subscriber)
     705        self.assertEqual({'pubsub#deliver': '1'}, request.options)
     706
     707
     708    def test_fromElementOptionsSetCancel(self):
     709        """
     710        Test parsing a request for cancelling setting subscription options.
     711        """
     712
     713        xml = """
     714        <iq type='set' to='pubsub.example.org'
     715                       from='user@example.org'>
     716          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     717            <options node='test' jid='user@example.org/Home'>
     718              <x xmlns='jabber:x:data' type='cancel'/>
     719            </options>
     720          </pubsub>
     721        </iq>
     722        """
     723
     724        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     725        self.assertEqual({}, request.options)
     726
     727
     728    def test_fromElementOptionsSetBadFormType(self):
     729        """
     730        On a options set request unknown fields should be ignored.
     731        """
     732
     733        xml = """
     734        <iq type='set' to='pubsub.example.org'
     735                       from='user@example.org'>
     736          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     737            <options node='test' jid='user@example.org/Home'>
     738              <x xmlns='jabber:x:data' type='result'>
     739                <field var='FORM_TYPE' type='hidden'>
     740                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     741                </field>
     742                <field var='pubsub#deliver'><value>1</value></field>
     743              </x>
     744            </options>
     745          </pubsub>
     746        </iq>
     747        """
     748
     749        err = self.assertRaises(error.StanzaError,
     750                                pubsub.PubSubRequest.fromElement,
     751                                parseXml(xml))
     752        self.assertEqual('bad-request', err.condition)
     753        self.assertEqual(None, err.appCondition)
     754
     755
     756    def test_fromElementOptionsSetNoForm(self):
     757        """
     758        On a options set request a form is required.
     759        """
     760
     761        xml = """
     762        <iq type='set' to='pubsub.example.org'
     763                       from='user@example.org'>
     764          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     765            <options node='test' jid='user@example.org/Home'/>
     766          </pubsub>
     767        </iq>
     768        """
     769        err = self.assertRaises(error.StanzaError,
     770                                pubsub.PubSubRequest.fromElement,
     771                                parseXml(xml))
     772        self.assertEqual('bad-request', err.condition)
     773        self.assertEqual(None, err.appCondition)
     774
     775
     776    def test_fromElementSubscriptions(self):
     777        """
     778        Test parsing a request for all subscriptions.
     779        """
     780
     781        xml = """
     782        <iq type='get' to='pubsub.example.org'
     783                       from='user@example.org'>
     784          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     785            <subscriptions/>
     786          </pubsub>
     787        </iq>
     788        """
     789
     790        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     791        self.assertEqual('subscriptions', request.verb)
     792        self.assertEqual(JID('user@example.org'), request.sender)
     793        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     794
     795
     796    def test_fromElementAffiliations(self):
     797        """
     798        Test parsing a request for all affiliations.
     799        """
     800
     801        xml = """
     802        <iq type='get' to='pubsub.example.org'
     803                       from='user@example.org'>
     804          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     805            <affiliations/>
     806          </pubsub>
     807        </iq>
     808        """
     809
     810        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     811        self.assertEqual('affiliations', request.verb)
     812        self.assertEqual(JID('user@example.org'), request.sender)
     813        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     814
     815
     816    def test_fromElementCreate(self):
     817        """
     818        Test parsing a request to create a node.
     819        """
     820
     821        xml = """
     822        <iq type='set' to='pubsub.example.org'
     823                       from='user@example.org'>
     824          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     825            <create node='mynode'/>
     826          </pubsub>
     827        </iq>
     828        """
     829
     830        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     831        self.assertEqual('create', request.verb)
     832        self.assertEqual(JID('user@example.org'), request.sender)
     833        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     834        self.assertEqual('mynode', request.nodeIdentifier)
     835
     836
     837    def test_fromElementCreateInstant(self):
     838        """
     839        Test parsing a request to create an instant node.
     840        """
     841
     842        xml = """
     843        <iq type='set' to='pubsub.example.org'
     844                       from='user@example.org'>
     845          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     846            <create/>
     847          </pubsub>
     848        </iq>
     849        """
     850
     851        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     852        self.assertIdentical(None, request.nodeIdentifier)
     853
     854
     855    def test_fromElementDefault(self):
     856        """
     857        Test parsing a request for the default node configuration.
     858        """
     859
     860        xml = """
     861        <iq type='get' to='pubsub.example.org'
     862                       from='user@example.org'>
     863          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     864            <default/>
     865          </pubsub>
     866        </iq>
     867        """
     868
     869        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     870        self.assertEqual('default', request.verb)
     871        self.assertEqual(JID('user@example.org'), request.sender)
     872        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     873        self.assertEqual('leaf', request.nodeType)
     874
     875
     876    def test_fromElementDefaultCollection(self):
     877        """
     878        Parsing a request for the default configuration extracts the node type.
     879        """
     880
     881        xml = """
     882        <iq type='get' to='pubsub.example.org'
     883                       from='user@example.org'>
     884          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     885            <default>
     886              <x xmlns='jabber:x:data' type='submit'>
     887                <field var='FORM_TYPE' type='hidden'>
     888                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     889                </field>
     890                <field var='pubsub#node_type'>
     891                  <value>collection</value>
     892                </field>
     893              </x>
     894            </default>
     895
     896          </pubsub>
     897        </iq>
     898        """
     899
     900        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     901        self.assertEqual('collection', request.nodeType)
     902
     903
     904    def test_fromElementConfigureGet(self):
     905        """
     906        Test parsing a node configuration get request.
     907        """
     908
     909        xml = """
     910        <iq type='get' to='pubsub.example.org'
     911                       from='user@example.org'>
     912          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     913            <configure node='test'/>
     914          </pubsub>
     915        </iq>
     916        """
     917
     918        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     919        self.assertEqual('configureGet', request.verb)
     920        self.assertEqual(JID('user@example.org'), request.sender)
     921        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     922        self.assertEqual('test', request.nodeIdentifier)
     923
     924
     925    def test_fromElementConfigureSet(self):
     926        """
     927        On a node configuration set request the Data Form is parsed.
     928        """
     929
     930        xml = """
     931        <iq type='set' to='pubsub.example.org'
     932                       from='user@example.org'>
     933          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     934            <configure node='test'>
     935              <x xmlns='jabber:x:data' type='submit'>
     936                <field var='FORM_TYPE' type='hidden'>
     937                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     938                </field>
     939                <field var='pubsub#deliver_payloads'><value>0</value></field>
     940                <field var='pubsub#persist_items'><value>1</value></field>
     941              </x>
     942            </configure>
     943          </pubsub>
     944        </iq>
     945        """
     946
     947        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     948        self.assertEqual('configureSet', request.verb)
     949        self.assertEqual(JID('user@example.org'), request.sender)
     950        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     951        self.assertEqual('test', request.nodeIdentifier)
     952        self.assertEqual({'pubsub#deliver_payloads': '0',
     953                          'pubsub#persist_items': '1'}, request.options)
     954
     955
     956    def test_fromElementConfigureSetCancel(self):
     957        """
     958        The node configuration is cancelled, so no options.
     959        """
     960
     961        xml = """
     962        <iq type='set' to='pubsub.example.org'
     963                       from='user@example.org'>
     964          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     965            <configure node='test'>
     966              <x xmlns='jabber:x:data' type='cancel'/>
     967            </configure>
     968          </pubsub>
     969        </iq>
     970        """
     971
     972        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     973        self.assertEqual({}, request.options)
     974
     975
     976    def test_fromElementConfigureSetBadFormType(self):
     977        """
     978        On a node configuration set request unknown fields should be ignored.
     979        """
     980
     981        xml = """
     982        <iq type='set' to='pubsub.example.org'
     983                       from='user@example.org'>
     984          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     985            <configure node='test'>
     986              <x xmlns='jabber:x:data' type='result'>
     987                <field var='FORM_TYPE' type='hidden'>
     988                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     989                </field>
     990                <field var='pubsub#deliver_payloads'><value>0</value></field>
     991                <field var='x-myfield'><value>1</value></field>
     992              </x>
     993            </configure>
     994          </pubsub>
     995        </iq>
     996        """
     997
     998        err = self.assertRaises(error.StanzaError,
     999                                pubsub.PubSubRequest.fromElement,
     1000                                parseXml(xml))
     1001        self.assertEqual('bad-request', err.condition)
     1002        self.assertEqual(None, err.appCondition)
     1003
     1004
     1005    def test_fromElementConfigureSetNoForm(self):
     1006        """
     1007        On a node configuration set request a form is required.
     1008        """
     1009
     1010        xml = """
     1011        <iq type='set' to='pubsub.example.org'
     1012                       from='user@example.org'>
     1013          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1014            <configure node='test'/>
     1015          </pubsub>
     1016        </iq>
     1017        """
     1018        err = self.assertRaises(error.StanzaError,
     1019                                pubsub.PubSubRequest.fromElement,
     1020                                parseXml(xml))
     1021        self.assertEqual('bad-request', err.condition)
     1022        self.assertEqual(None, err.appCondition)
     1023
     1024
     1025    def test_fromElementItems(self):
     1026        """
     1027        Test parsing an items request.
     1028        """
     1029        xml = """
     1030        <iq type='get' to='pubsub.example.org'
     1031                       from='user@example.org'>
     1032          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1033            <items node='test'/>
     1034          </pubsub>
     1035        </iq>
     1036        """
     1037
     1038        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     1039        self.assertEqual('items', request.verb)
     1040        self.assertEqual(JID('user@example.org'), request.sender)
     1041        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     1042        self.assertEqual('test', request.nodeIdentifier)
     1043        self.assertIdentical(None, request.maxItems)
     1044        self.assertEqual([], request.itemIdentifiers)
     1045
     1046
     1047    def test_fromElementRetract(self):
     1048        """
     1049        Test parsing a retract request.
     1050        """
     1051
     1052        xml = """
     1053        <iq type='set' to='pubsub.example.org'
     1054                       from='user@example.org'>
     1055          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1056            <retract node='test'>
     1057              <item id='item1'/>
     1058              <item id='item2'/>
     1059            </retract>
     1060          </pubsub>
     1061        </iq>
     1062        """
     1063
     1064        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     1065        self.assertEqual('retract', request.verb)
     1066        self.assertEqual(JID('user@example.org'), request.sender)
     1067        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     1068        self.assertEqual('test', request.nodeIdentifier)
     1069        self.assertEqual(['item1', 'item2'], request.itemIdentifiers)
     1070
     1071
     1072    def test_fromElementPurge(self):
     1073        """
     1074        Test parsing a purge request.
     1075        """
     1076
     1077        xml = """
     1078        <iq type='set' to='pubsub.example.org'
     1079                       from='user@example.org'>
     1080          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1081            <purge node='test'/>
     1082          </pubsub>
     1083        </iq>
     1084        """
     1085
     1086        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     1087        self.assertEqual('purge', request.verb)
     1088        self.assertEqual(JID('user@example.org'), request.sender)
     1089        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     1090        self.assertEqual('test', request.nodeIdentifier)
     1091
     1092
     1093    def test_fromElementDelete(self):
     1094        """
     1095        Test parsing a delete request.
     1096        """
     1097
     1098        xml = """
     1099        <iq type='set' to='pubsub.example.org'
     1100                       from='user@example.org'>
     1101          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1102            <delete node='test'/>
     1103          </pubsub>
     1104        </iq>
     1105        """
     1106
     1107        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     1108        self.assertEqual('delete', request.verb)
     1109        self.assertEqual(JID('user@example.org'), request.sender)
     1110        self.assertEqual(JID('pubsub.example.org'), request.recipient)
     1111        self.assertEqual('test', request.nodeIdentifier)
     1112
     1113
     1114
    4901115class PubSubServiceTest(unittest.TestCase, TestableRequestHandlerMixin):
    4911116    """
    4921117    Tests for L{pubsub.PubSubService}.
     
    5041129        verify.verifyObject(iwokkel.IPubSubService, self.service)
    5051130
    5061131
     1132    def test_connectionMade(self):
     1133        """
     1134        Verify setup of observers in L{pubsub.connectionMade}.
     1135        """
     1136        requests = []
     1137
     1138        def handleRequest(iq):
     1139            requests.append(iq)
     1140
     1141        self.service.xmlstream = self.stub.xmlstream
     1142        self.service.handleRequest = handleRequest
     1143        self.service.connectionMade()
     1144
     1145        for namespace in (NS_PUBSUB, NS_PUBSUB_OWNER):
     1146            for stanzaType in ('get', 'set'):
     1147                iq = domish.Element((None, 'iq'))
     1148                iq['type'] = stanzaType
     1149                iq.addElement((namespace, 'pubsub'))
     1150                self.stub.xmlstream.dispatch(iq)
     1151
     1152        self.assertEqual(4, len(requests))
     1153
     1154
    5071155    def test_getDiscoInfo(self):
    5081156        """
    5091157        Test getDiscoInfo calls getNodeInfo and returns some minimal info.
     
    5691217        return d
    5701218
    5711219
    572     def test_onPublishNoNode(self):
    573         """
    574         The root node is always a collection, publishing is a bad request.
    575         """
    576         xml = """
    577         <iq type='set' to='pubsub.example.org'
    578                        from='user@example.org'>
    579           <pubsub xmlns='http://jabber.org/protocol/pubsub'>
    580             <publish/>
    581           </pubsub>
    582         </iq>
    583         """
    584 
    585         def cb(result):
    586             self.assertEquals('bad-request', result.condition)
    587 
    588         d = self.handleRequest(xml)
    589         self.assertFailure(d, error.StanzaError)
    590         d.addCallback(cb)
    591         return d
    592 
    593 
    5941220    def test_onPublish(self):
    5951221        """
    5961222        A publish request should result in L{PubSubService.publish} being
     
    6071233        """
    6081234
    6091235        def publish(requestor, service, nodeIdentifier, items):
    610             self.assertEqual(JID('user@example.org'), requestor)
    611             self.assertEqual(JID('pubsub.example.org'), service)
    612             self.assertEqual('test', nodeIdentifier)
    613             self.assertEqual([], items)
    6141236            return defer.succeed(None)
    6151237
    6161238        self.service.publish = publish
     1239        verify.verifyObject(iwokkel.IPubSubService, self.service)
    6171240        return self.handleRequest(xml)
    6181241
    6191242
     1243    def test_onSubscribe(self):
     1244        """
     1245        A successful subscription should return the current subscription.
     1246        """
     1247
     1248        xml = """
     1249        <iq type='set' to='pubsub.example.org'
     1250                       from='user@example.org'>
     1251          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1252            <subscribe node='test' jid='user@example.org/Home'/>
     1253          </pubsub>
     1254        </iq>
     1255        """
     1256
     1257        def subscribe(requestor, service, nodeIdentifier, subscriber):
     1258            return defer.succeed(pubsub.Subscription(nodeIdentifier,
     1259                                                     subscriber,
     1260                                                     'subscribed'))
     1261
     1262        def cb(element):
     1263            self.assertEqual('pubsub', element.name)
     1264            self.assertEqual(NS_PUBSUB, element.uri)
     1265            subscription = element.subscription
     1266            self.assertEqual(NS_PUBSUB, subscription.uri)
     1267            self.assertEqual('test', subscription['node'])
     1268            self.assertEqual('user@example.org/Home', subscription['jid'])
     1269            self.assertEqual('subscribed', subscription['subscription'])
     1270
     1271        self.service.subscribe = subscribe
     1272        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1273        d = self.handleRequest(xml)
     1274        d.addCallback(cb)
     1275        return d
     1276
     1277
     1278    def test_onSubscribeEmptyNode(self):
     1279        """
     1280        A successful subscription on root node should return no node attribute.
     1281        """
     1282
     1283        xml = """
     1284        <iq type='set' to='pubsub.example.org'
     1285                       from='user@example.org'>
     1286          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1287            <subscribe jid='user@example.org/Home'/>
     1288          </pubsub>
     1289        </iq>
     1290        """
     1291
     1292        def subscribe(requestor, service, nodeIdentifier, subscriber):
     1293            return defer.succeed(pubsub.Subscription(nodeIdentifier,
     1294                                                     subscriber,
     1295                                                     'subscribed'))
     1296
     1297        def cb(element):
     1298            self.assertFalse(element.subscription.hasAttribute('node'))
     1299
     1300        self.service.subscribe = subscribe
     1301        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1302        d = self.handleRequest(xml)
     1303        d.addCallback(cb)
     1304        return d
     1305
     1306
     1307    def test_onUnsubscribe(self):
     1308        """
     1309        A successful unsubscription should return an empty response.
     1310        """
     1311
     1312        xml = """
     1313        <iq type='set' to='pubsub.example.org'
     1314                       from='user@example.org'>
     1315          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1316            <unsubscribe node='test' jid='user@example.org/Home'/>
     1317          </pubsub>
     1318        </iq>
     1319        """
     1320
     1321        def unsubscribe(requestor, service, nodeIdentifier, subscriber):
     1322            return defer.succeed(None)
     1323
     1324        def cb(element):
     1325            self.assertIdentical(None, element)
     1326
     1327        self.service.unsubscribe = unsubscribe
     1328        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1329        d = self.handleRequest(xml)
     1330        d.addCallback(cb)
     1331        return d
     1332
     1333
    6201334    def test_onOptionsGet(self):
    6211335        """
    622         Subscription options are not supported.
     1336        Getting subscription options is not supported.
    6231337        """
    6241338
    6251339        xml = """
    6261340        <iq type='get' to='pubsub.example.org'
    6271341                       from='user@example.org'>
    6281342          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
    629             <options/>
     1343            <options node='test' jid='user@example.org/Home'/>
     1344          </pubsub>
     1345        </iq>
     1346        """
     1347
     1348        def cb(result):
     1349            self.assertEquals('feature-not-implemented', result.condition)
     1350            self.assertEquals('unsupported', result.appCondition.name)
     1351            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     1352
     1353        d = self.handleRequest(xml)
     1354        self.assertFailure(d, error.StanzaError)
     1355        d.addCallback(cb)
     1356        return d
     1357
     1358
     1359    def test_onOptionsSet(self):
     1360        """
     1361        Setting subscription options is not supported.
     1362        """
     1363
     1364        xml = """
     1365        <iq type='set' to='pubsub.example.org'
     1366                       from='user@example.org'>
     1367          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1368            <options node='test' jid='user@example.org/Home'>
     1369              <x xmlns='jabber:x:data' type='submit'>
     1370                <field var='FORM_TYPE' type='hidden'>
     1371                  <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
     1372                </field>
     1373                <field var='pubsub#deliver'><value>1</value></field>
     1374              </x>
     1375            </options>
    6301376          </pubsub>
    6311377        </iq>
    6321378        """
     
    6731419
    6741420
    6751421        def subscriptions(requestor, service):
    676             self.assertEqual(JID('user@example.org'), requestor)
    677             self.assertEqual(JID('pubsub.example.org'), service)
    6781422            subscription = pubsub.Subscription('test', JID('user@example.org'),
    6791423                                               'subscribed')
    6801424            return defer.succeed([subscription])
    6811425
    6821426        self.service.subscriptions = subscriptions
     1427        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1428        d = self.handleRequest(xml)
     1429        d.addCallback(cb)
     1430        return d
     1431
     1432
     1433    def test_onAffiliations(self):
     1434        """
     1435        A subscriptions request should result in
     1436        L{PubSubService.affiliations} being called and the result prepared
     1437        for the response.
     1438        """
     1439
     1440        xml = """
     1441        <iq type='get' to='pubsub.example.org'
     1442                       from='user@example.org'>
     1443          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1444            <affiliations/>
     1445          </pubsub>
     1446        </iq>
     1447        """
     1448
     1449        def cb(element):
     1450            self.assertEqual('pubsub', element.name)
     1451            self.assertEqual(NS_PUBSUB, element.uri)
     1452            self.assertEqual(NS_PUBSUB, element.affiliations.uri)
     1453            children = list(element.affiliations.elements())
     1454            self.assertEqual(1, len(children))
     1455            affiliation = children[0]
     1456            self.assertEqual('affiliation', affiliation.name)
     1457            self.assertEqual(NS_PUBSUB, affiliation.uri)
     1458            self.assertEqual('test', affiliation['node'])
     1459            self.assertEqual('owner', affiliation['affiliation'])
     1460
     1461
     1462        def affiliations(requestor, service):
     1463            affiliation = ('test', 'owner')
     1464            return defer.succeed([affiliation])
     1465
     1466        self.service.affiliations = affiliations
     1467        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1468        d = self.handleRequest(xml)
     1469        d.addCallback(cb)
     1470        return d
     1471
     1472
     1473    def test_onCreate(self):
     1474        """
     1475        Replies to create node requests don't return the created node.
     1476        """
     1477
     1478        xml = """
     1479        <iq type='set' to='pubsub.example.org'
     1480                       from='user@example.org'>
     1481          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1482            <create node='mynode'/>
     1483          </pubsub>
     1484        </iq>
     1485        """
     1486
     1487        def create(requestor, service, nodeIdentifier):
     1488            return defer.succeed(nodeIdentifier)
     1489
     1490        def cb(element):
     1491            self.assertIdentical(None, element)
     1492
     1493        self.service.create = create
     1494        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1495        d = self.handleRequest(xml)
     1496        d.addCallback(cb)
     1497        return d
     1498
     1499
     1500    def test_onCreateChanged(self):
     1501        """
     1502        Replies to create node requests return the created node if changed.
     1503        """
     1504
     1505        xml = """
     1506        <iq type='set' to='pubsub.example.org'
     1507                       from='user@example.org'>
     1508          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1509            <create node='mynode'/>
     1510          </pubsub>
     1511        </iq>
     1512        """
     1513
     1514        def create(requestor, service, nodeIdentifier):
     1515            return defer.succeed(u'myrenamednode')
     1516
     1517        def cb(element):
     1518            self.assertEqual('pubsub', element.name)
     1519            self.assertEqual(NS_PUBSUB, element.uri)
     1520            self.assertEqual(NS_PUBSUB, element.create.uri)
     1521            self.assertEqual(u'myrenamednode',
     1522                             element.create.getAttribute('node'))
     1523
     1524        self.service.create = create
     1525        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1526        d = self.handleRequest(xml)
     1527        d.addCallback(cb)
     1528        return d
     1529
     1530
     1531    def test_onCreateInstant(self):
     1532        """
     1533        Replies to create instant node requests return the created node.
     1534        """
     1535
     1536        xml = """
     1537        <iq type='set' to='pubsub.example.org'
     1538                       from='user@example.org'>
     1539          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1540            <create/>
     1541          </pubsub>
     1542        </iq>
     1543        """
     1544
     1545        def create(requestor, service, nodeIdentifier):
     1546            return defer.succeed(u'random')
     1547
     1548        def cb(element):
     1549            self.assertEqual('pubsub', element.name)
     1550            self.assertEqual(NS_PUBSUB, element.uri)
     1551            self.assertEqual(NS_PUBSUB, element.create.uri)
     1552            self.assertEqual(u'random', element.create.getAttribute('node'))
     1553
     1554        self.service.create = create
     1555        verify.verifyObject(iwokkel.IPubSubService, self.service)
    6831556        d = self.handleRequest(xml)
    6841557        d.addCallback(cb)
    6851558        return d
     
    7111584                }
    7121585
    7131586        def getDefaultConfiguration(requestor, service, nodeType):
    714             self.assertEqual(JID('user@example.org'), requestor)
    715             self.assertEqual(JID('pubsub.example.org'), service)
    716             self.assertEqual('leaf', nodeType)
    7171587            return defer.succeed({})
    7181588
    7191589        def cb(element):
     
    7311601        return d
    7321602
    7331603
     1604    def test_onDefaultCollection(self):
     1605        """
     1606        Responses to default requests should depend on passed node type.
     1607        """
     1608
     1609        xml = """
     1610        <iq type='get' to='pubsub.example.org'
     1611                       from='user@example.org'>
     1612          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1613            <default>
     1614              <x xmlns='jabber:x:data' type='submit'>
     1615                <field var='FORM_TYPE' type='hidden'>
     1616                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     1617                </field>
     1618                <field var='pubsub#node_type'>
     1619                  <value>collection</value>
     1620                </field>
     1621              </x>
     1622            </default>
     1623
     1624          </pubsub>
     1625        </iq>
     1626        """
     1627
     1628        def getConfigurationOptions():
     1629            return {
     1630                "pubsub#deliver_payloads":
     1631                    {"type": "boolean",
     1632                     "label": "Deliver payloads with event notifications"}
     1633                }
     1634
     1635        def getDefaultConfiguration(requestor, service, nodeType):
     1636            return defer.succeed({})
     1637
     1638        self.service.getConfigurationOptions = getConfigurationOptions
     1639        self.service.getDefaultConfiguration = getDefaultConfiguration
     1640        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1641        return self.handleRequest(xml)
     1642
     1643
     1644    def test_onDefaultUnknownNodeType(self):
     1645        """
     1646        A default request should result in
     1647        L{PubSubService.getDefaultConfiguration} being called.
     1648        """
     1649
     1650        xml = """
     1651        <iq type='get' to='pubsub.example.org'
     1652                       from='user@example.org'>
     1653          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1654            <default>
     1655              <x xmlns='jabber:x:data' type='submit'>
     1656                <field var='FORM_TYPE' type='hidden'>
     1657                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     1658                </field>
     1659                <field var='pubsub#node_type'>
     1660                  <value>unknown</value>
     1661                </field>
     1662              </x>
     1663            </default>
     1664
     1665          </pubsub>
     1666        </iq>
     1667        """
     1668
     1669        def getDefaultConfiguration(requestor, service, nodeType):
     1670            self.fail("Unexpected call to getConfiguration")
     1671
     1672        def cb(result):
     1673            self.assertEquals('not-acceptable', result.condition)
     1674
     1675        self.service.getDefaultConfiguration = getDefaultConfiguration
     1676        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1677        d = self.handleRequest(xml)
     1678        self.assertFailure(d, error.StanzaError)
     1679        d.addCallback(cb)
     1680        return d
     1681
     1682
    7341683    def test_onConfigureGet(self):
    7351684        """
    7361685        On a node configuration get request L{PubSubService.getConfiguration}
     
    7601709                }
    7611710
    7621711        def getConfiguration(requestor, service, nodeIdentifier):
    763             self.assertEqual(JID('user@example.org'), requestor)
    764             self.assertEqual(JID('pubsub.example.org'), service)
    765             self.assertEqual('test', nodeIdentifier)
    766 
    7671712            return defer.succeed({'pubsub#deliver_payloads': '0',
    7681713                                  'pubsub#persist_items': '1',
    769                                   'pubsub#owner': JID('user@example.org')})
     1714                                  'pubsub#owner': JID('user@example.org'),
     1715                                  'x-myfield': ['a', 'b']})
    7701716
    7711717        def cb(element):
    7721718            self.assertEqual('pubsub', element.name)
     
    7941740            field.typeCheck()
    7951741            self.assertEqual(JID('user@example.org'), field.value)
    7961742
     1743            self.assertNotIn('x-myfield', fields)
     1744
     1745
    7971746        self.service.getConfigurationOptions = getConfigurationOptions
    7981747        self.service.getConfiguration = getConfiguration
     1748        verify.verifyObject(iwokkel.IPubSubService, self.service)
    7991749        d = self.handleRequest(xml)
    8001750        d.addCallback(cb)
    8011751        return d
     
    8351785                }
    8361786
    8371787        def setConfiguration(requestor, service, nodeIdentifier, options):
    838             self.assertEqual(JID('user@example.org'), requestor)
    839             self.assertEqual(JID('pubsub.example.org'), service)
    840             self.assertEqual('test', nodeIdentifier)
    8411788            self.assertEqual({'pubsub#deliver_payloads': False,
    8421789                              'pubsub#persist_items': True}, options)
    8431790            return defer.succeed(None)
    8441791
    8451792        self.service.getConfigurationOptions = getConfigurationOptions
    8461793        self.service.setConfiguration = setConfiguration
     1794        verify.verifyObject(iwokkel.IPubSubService, self.service)
    8471795        return self.handleRequest(xml)
    8481796
    8491797
     
    8721820            self.fail("Unexpected call to setConfiguration")
    8731821
    8741822        self.service.setConfiguration = setConfiguration
     1823        verify.verifyObject(iwokkel.IPubSubService, self.service)
    8751824        return self.handleRequest(xml)
    8761825
    8771826
     
    9121861
    9131862        self.service.getConfigurationOptions = getConfigurationOptions
    9141863        self.service.setConfiguration = setConfiguration
     1864        verify.verifyObject(iwokkel.IPubSubService, self.service)
    9151865        return self.handleRequest(xml)
    9161866
    9171867
     1868    def test_onConfigureSetBadFormType(self):
     1869        """
     1870        On a node configuration set request unknown fields should be ignored.
     1871        """
     1872
     1873        xml = """
     1874        <iq type='set' to='pubsub.example.org'
     1875                       from='user@example.org'>
     1876          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1877            <configure node='test'>
     1878              <x xmlns='jabber:x:data' type='result'>
     1879                <field var='FORM_TYPE' type='hidden'>
     1880                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     1881                </field>
     1882                <field var='pubsub#deliver_payloads'><value>0</value></field>
     1883                <field var='x-myfield'><value>1</value></field>
     1884              </x>
     1885            </configure>
     1886          </pubsub>
     1887        </iq>
     1888        """
     1889
     1890        def cb(result):
     1891            self.assertEquals('bad-request', result.condition)
     1892
     1893        d = self.handleRequest(xml)
     1894        self.assertFailure(d, error.StanzaError)
     1895        d.addCallback(cb)
     1896        return d
     1897
     1898
    9181899    def test_onItems(self):
    9191900        """
    9201901        On a items request, return all items for the given node.
     
    9281909        </iq>
    9291910        """
    9301911
    931         def items(requestor, service, nodeIdentifier, maxItems, items):
    932             self.assertEqual(JID('user@example.org'), requestor)
    933             self.assertEqual(JID('pubsub.example.org'), service)
    934             self.assertEqual('test', nodeIdentifier)
    935             self.assertIdentical(None, maxItems)
    936             self.assertEqual([], items)
     1912        def items(requestor, service, nodeIdentifier, maxItems,
     1913                  itemIdentifiers):
    9371914            return defer.succeed([pubsub.Item('current')])
    9381915
    9391916        def cb(element):
     
    9711948        """
    9721949
    9731950        def retract(requestor, service, nodeIdentifier, itemIdentifiers):
    974             self.assertEqual(JID('user@example.org'), requestor)
    975             self.assertEqual(JID('pubsub.example.org'), service)
    976             self.assertEqual('test', nodeIdentifier)
    977             self.assertEqual(['item1', 'item2'], itemIdentifiers)
    9781951            return defer.succeed(None)
    9791952
    9801953        self.service.retract = retract
     
    9971970        """
    9981971
    9991972        def purge(requestor, service, nodeIdentifier):
    1000             self.assertEqual(JID('user@example.org'), requestor)
    1001             self.assertEqual(JID('pubsub.example.org'), service)
    1002             self.assertEqual('test', nodeIdentifier)
    10031973            return defer.succeed(None)
    10041974
    10051975        self.service.purge = purge
     
    10221992        """
    10231993
    10241994        def delete(requestor, service, nodeIdentifier):
    1025             self.assertEqual(JID('user@example.org'), requestor)
    1026             self.assertEqual(JID('pubsub.example.org'), service)
    1027             self.assertEqual('test', nodeIdentifier)
    10281995            return defer.succeed(None)
    10291996
    10301997        self.service.delete = delete
     
    10762043        self.assertEqual(NS_PUBSUB_EVENT, message.event.delete.redirect.uri)
    10772044        self.assertTrue(message.event.delete.redirect.hasAttribute('uri'))
    10782045        self.assertEqual(redirectURI, message.event.delete.redirect['uri'])
     2046
     2047
     2048    def test_onSubscriptionsGet(self):
     2049        """
     2050        Getting subscription options is not supported.
     2051        """
     2052
     2053        xml = """
     2054        <iq type='get' to='pubsub.example.org'
     2055                       from='user@example.org'>
     2056          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2057            <subscriptions/>
     2058          </pubsub>
     2059        </iq>
     2060        """
     2061
     2062        def cb(result):
     2063            self.assertEquals('feature-not-implemented', result.condition)
     2064            self.assertEquals('unsupported', result.appCondition.name)
     2065            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2066            self.assertEquals('manage-subscriptions',
     2067                              result.appCondition['feature'])
     2068
     2069        d = self.handleRequest(xml)
     2070        self.assertFailure(d, error.StanzaError)
     2071        d.addCallback(cb)
     2072        return d
     2073
     2074
     2075    def test_onSubscriptionsSet(self):
     2076        """
     2077        Setting subscription options is not supported.
     2078        """
     2079
     2080        xml = """
     2081        <iq type='set' to='pubsub.example.org'
     2082                       from='user@example.org'>
     2083          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2084            <subscriptions/>
     2085          </pubsub>
     2086        </iq>
     2087        """
     2088
     2089        def cb(result):
     2090            self.assertEquals('feature-not-implemented', result.condition)
     2091            self.assertEquals('unsupported', result.appCondition.name)
     2092            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2093            self.assertEquals('manage-subscriptions',
     2094                              result.appCondition['feature'])
     2095
     2096        d = self.handleRequest(xml)
     2097        self.assertFailure(d, error.StanzaError)
     2098        d.addCallback(cb)
     2099        return d
     2100
     2101
     2102    def test_onAffiliationsGet(self):
     2103        """
     2104        Getting subscription options is not supported.
     2105        """
     2106
     2107        xml = """
     2108        <iq type='get' to='pubsub.example.org'
     2109                       from='user@example.org'>
     2110          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2111            <affiliations/>
     2112          </pubsub>
     2113        </iq>
     2114        """
     2115
     2116        def cb(result):
     2117            self.assertEquals('feature-not-implemented', result.condition)
     2118            self.assertEquals('unsupported', result.appCondition.name)
     2119            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2120            self.assertEquals('modify-affiliations',
     2121                              result.appCondition['feature'])
     2122
     2123        d = self.handleRequest(xml)
     2124        self.assertFailure(d, error.StanzaError)
     2125        d.addCallback(cb)
     2126        return d
     2127
     2128
     2129    def test_onAffiliationsSet(self):
     2130        """
     2131        Setting subscription options is not supported.
     2132        """
     2133
     2134        xml = """
     2135        <iq type='set' to='pubsub.example.org'
     2136                       from='user@example.org'>
     2137          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2138            <affiliations/>
     2139          </pubsub>
     2140        </iq>
     2141        """
     2142
     2143        def cb(result):
     2144            self.assertEquals('feature-not-implemented', result.condition)
     2145            self.assertEquals('unsupported', result.appCondition.name)
     2146            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2147            self.assertEquals('modify-affiliations',
     2148                              result.appCondition['feature'])
     2149
     2150        d = self.handleRequest(xml)
     2151        self.assertFailure(d, error.StanzaError)
     2152        d.addCallback(cb)
     2153        return d
     2154
     2155
     2156    def test_publish(self):
     2157        """
     2158        Non-overridden L{PubSubService.publish} yields unsupported error.
     2159        """
     2160
     2161        xml = """
     2162        <iq type='set' to='pubsub.example.org'
     2163                       from='user@example.org'>
     2164          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     2165            <publish node='mynode'/>
     2166          </pubsub>
     2167        </iq>
     2168        """
     2169
     2170        def cb(result):
     2171            self.assertEquals('feature-not-implemented', result.condition)
     2172            self.assertEquals('unsupported', result.appCondition.name)
     2173            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2174            self.assertEquals('publish', result.appCondition['feature'])
     2175
     2176        d = self.handleRequest(xml)
     2177        self.assertFailure(d, error.StanzaError)
     2178        d.addCallback(cb)
     2179        return d
     2180
     2181
     2182    def test_subscribe(self):
     2183        """
     2184        Non-overridden L{PubSubService.subscribe} yields unsupported error.
     2185        """
     2186
     2187        xml = """
     2188        <iq type='set' to='pubsub.example.org'
     2189                       from='user@example.org'>
     2190          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     2191            <subscribe node='test' jid='user@example.org/Home'/>
     2192          </pubsub>
     2193        </iq>
     2194        """
     2195
     2196        def cb(result):
     2197            self.assertEquals('feature-not-implemented', result.condition)
     2198            self.assertEquals('unsupported', result.appCondition.name)
     2199            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2200            self.assertEquals('subscribe', result.appCondition['feature'])
     2201
     2202        d = self.handleRequest(xml)
     2203        self.assertFailure(d, error.StanzaError)
     2204        d.addCallback(cb)
     2205        return d
     2206
     2207
     2208    def test_unsubscribe(self):
     2209        """
     2210        Non-overridden L{PubSubService.unsubscribe} yields unsupported error.
     2211        """
     2212
     2213        xml = """
     2214        <iq type='set' to='pubsub.example.org'
     2215                       from='user@example.org'>
     2216          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     2217            <unsubscribe node='test' jid='user@example.org/Home'/>
     2218          </pubsub>
     2219        </iq>
     2220        """
     2221
     2222        def cb(result):
     2223            self.assertEquals('feature-not-implemented', result.condition)
     2224            self.assertEquals('unsupported', result.appCondition.name)
     2225            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2226            self.assertEquals('subscribe', result.appCondition['feature'])
     2227
     2228        d = self.handleRequest(xml)
     2229        self.assertFailure(d, error.StanzaError)
     2230        d.addCallback(cb)
     2231        return d
     2232
     2233
     2234    def test_subscriptions(self):
     2235        """
     2236        Non-overridden L{PubSubService.subscriptions} yields unsupported error.
     2237        """
     2238
     2239        xml = """
     2240        <iq type='get' to='pubsub.example.org'
     2241                       from='user@example.org'>
     2242          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     2243            <subscriptions/>
     2244          </pubsub>
     2245        </iq>
     2246        """
     2247
     2248        def cb(result):
     2249            self.assertEquals('feature-not-implemented', result.condition)
     2250            self.assertEquals('unsupported', result.appCondition.name)
     2251            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2252            self.assertEquals('retrieve-subscriptions',
     2253                              result.appCondition['feature'])
     2254
     2255        d = self.handleRequest(xml)
     2256        self.assertFailure(d, error.StanzaError)
     2257        d.addCallback(cb)
     2258        return d
     2259
     2260
     2261    def test_affiliations(self):
     2262        """
     2263        Non-overridden L{PubSubService.affiliations} yields unsupported error.
     2264        """
     2265
     2266        xml = """
     2267        <iq type='get' to='pubsub.example.org'
     2268                       from='user@example.org'>
     2269          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     2270            <affiliations/>
     2271          </pubsub>
     2272        </iq>
     2273        """
     2274
     2275        def cb(result):
     2276            self.assertEquals('feature-not-implemented', result.condition)
     2277            self.assertEquals('unsupported', result.appCondition.name)
     2278            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2279            self.assertEquals('retrieve-affiliations',
     2280                              result.appCondition['feature'])
     2281
     2282        d = self.handleRequest(xml)
     2283        self.assertFailure(d, error.StanzaError)
     2284        d.addCallback(cb)
     2285        return d
     2286
     2287
     2288    def test_create(self):
     2289        """
     2290        Non-overridden L{PubSubService.create} yields unsupported error.
     2291        """
     2292
     2293        xml = """
     2294        <iq type='set' to='pubsub.example.org'
     2295                       from='user@example.org'>
     2296          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     2297            <create node='mynode'/>
     2298          </pubsub>
     2299        </iq>
     2300        """
     2301
     2302        def cb(result):
     2303            self.assertEquals('feature-not-implemented', result.condition)
     2304            self.assertEquals('unsupported', result.appCondition.name)
     2305            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2306            self.assertEquals('create-nodes', result.appCondition['feature'])
     2307
     2308        d = self.handleRequest(xml)
     2309        self.assertFailure(d, error.StanzaError)
     2310        d.addCallback(cb)
     2311        return d
     2312
     2313
     2314    def test_getDefaultConfiguration(self):
     2315        """
     2316        Non-overridden L{PubSubService.getDefaultConfiguration} yields
     2317        unsupported error.
     2318        """
     2319
     2320        xml = """
     2321        <iq type='get' to='pubsub.example.org'
     2322                       from='user@example.org'>
     2323          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2324            <default/>
     2325          </pubsub>
     2326        </iq>
     2327        """
     2328
     2329        def cb(result):
     2330            self.assertEquals('feature-not-implemented', result.condition)
     2331            self.assertEquals('unsupported', result.appCondition.name)
     2332            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2333            self.assertEquals('retrieve-default', result.appCondition['feature'])
     2334
     2335        d = self.handleRequest(xml)
     2336        self.assertFailure(d, error.StanzaError)
     2337        d.addCallback(cb)
     2338        return d
     2339
     2340
     2341    def test_getConfiguration(self):
     2342        """
     2343        Non-overridden L{PubSubService.getConfiguration} yields unsupported
     2344        error.
     2345        """
     2346
     2347        xml = """
     2348        <iq type='get' to='pubsub.example.org'
     2349                       from='user@example.org'>
     2350          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2351            <configure/>
     2352          </pubsub>
     2353        </iq>
     2354        """
     2355
     2356        def cb(result):
     2357            self.assertEquals('feature-not-implemented', result.condition)
     2358            self.assertEquals('unsupported', result.appCondition.name)
     2359            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2360            self.assertEquals('config-node', result.appCondition['feature'])
     2361
     2362        d = self.handleRequest(xml)
     2363        self.assertFailure(d, error.StanzaError)
     2364        d.addCallback(cb)
     2365        return d
     2366
     2367
     2368    def test_setConfiguration(self):
     2369        """
     2370        Non-overridden L{PubSubService.setConfiguration} yields unsupported
     2371        error.
     2372        """
     2373
     2374        xml = """
     2375        <iq type='set' to='pubsub.example.org'
     2376                       from='user@example.org'>
     2377          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2378            <configure node='test'>
     2379              <x xmlns='jabber:x:data' type='submit'>
     2380                <field var='FORM_TYPE' type='hidden'>
     2381                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     2382                </field>
     2383                <field var='pubsub#deliver_payloads'><value>0</value></field>
     2384                <field var='pubsub#persist_items'><value>1</value></field>
     2385              </x>
     2386            </configure>
     2387          </pubsub>
     2388        </iq>
     2389        """
     2390
     2391        def cb(result):
     2392            self.assertEquals('feature-not-implemented', result.condition)
     2393            self.assertEquals('unsupported', result.appCondition.name)
     2394            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2395            self.assertEquals('config-node', result.appCondition['feature'])
     2396
     2397        d = self.handleRequest(xml)
     2398        self.assertFailure(d, error.StanzaError)
     2399        d.addCallback(cb)
     2400        return d
     2401
     2402
     2403    def test_items(self):
     2404        """
     2405        Non-overridden L{PubSubService.items} yields unsupported error.
     2406        """
     2407        xml = """
     2408        <iq type='get' to='pubsub.example.org'
     2409                       from='user@example.org'>
     2410          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     2411            <items node='test'/>
     2412          </pubsub>
     2413        </iq>
     2414        """
     2415
     2416        def cb(result):
     2417            self.assertEquals('feature-not-implemented', result.condition)
     2418            self.assertEquals('unsupported', result.appCondition.name)
     2419            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2420            self.assertEquals('retrieve-items', result.appCondition['feature'])
     2421
     2422        d = self.handleRequest(xml)
     2423        self.assertFailure(d, error.StanzaError)
     2424        d.addCallback(cb)
     2425        return d
     2426
     2427
     2428    def test_retract(self):
     2429        """
     2430        Non-overridden L{PubSubService.retract} yields unsupported error.
     2431        """
     2432        xml = """
     2433        <iq type='set' to='pubsub.example.org'
     2434                       from='user@example.org'>
     2435          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     2436            <retract node='test'>
     2437              <item id='item1'/>
     2438              <item id='item2'/>
     2439            </retract>
     2440          </pubsub>
     2441        </iq>
     2442        """
     2443
     2444        def cb(result):
     2445            self.assertEquals('feature-not-implemented', result.condition)
     2446            self.assertEquals('unsupported', result.appCondition.name)
     2447            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2448            self.assertEquals('retract-items', result.appCondition['feature'])
     2449
     2450        d = self.handleRequest(xml)
     2451        self.assertFailure(d, error.StanzaError)
     2452        d.addCallback(cb)
     2453        return d
     2454
     2455
     2456    def test_purge(self):
     2457        """
     2458        Non-overridden L{PubSubService.purge} yields unsupported error.
     2459        """
     2460        xml = """
     2461        <iq type='set' to='pubsub.example.org'
     2462                       from='user@example.org'>
     2463          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2464            <purge node='test'/>
     2465          </pubsub>
     2466        </iq>
     2467        """
     2468
     2469        def cb(result):
     2470            self.assertEquals('feature-not-implemented', result.condition)
     2471            self.assertEquals('unsupported', result.appCondition.name)
     2472            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2473            self.assertEquals('purge-nodes', result.appCondition['feature'])
     2474
     2475        d = self.handleRequest(xml)
     2476        self.assertFailure(d, error.StanzaError)
     2477        d.addCallback(cb)
     2478        return d
     2479
     2480
     2481    def test_delete(self):
     2482        """
     2483        Non-overridden L{PubSubService.delete} yields unsupported error.
     2484        """
     2485        xml = """
     2486        <iq type='set' to='pubsub.example.org'
     2487                       from='user@example.org'>
     2488          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     2489            <delete node='test'/>
     2490          </pubsub>
     2491        </iq>
     2492        """
     2493
     2494        def cb(result):
     2495            self.assertEquals('feature-not-implemented', result.condition)
     2496            self.assertEquals('unsupported', result.appCondition.name)
     2497            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2498            self.assertEquals('delete-nodes', result.appCondition['feature'])
     2499
     2500        d = self.handleRequest(xml)
     2501        self.assertFailure(d, error.StanzaError)
     2502        d.addCallback(cb)
     2503        return d
Note: See TracBrowser for help on using the repository browser.