Changes in [133:2ea562934152:134:250a1041a437]
- Files:
-
- 11 added
- 32 edited
Legend:
- Unmodified
- Added
- Removed
-
LICENSE
r53 r96 1 Copyright (c) 2003-20 09 Ralph Meijer1 Copyright (c) 2003-2011 Ralph Meijer. 2 2 3 3 Permission is hereby granted, free of charge, to any person obtaining -
README
r73 r96 31 31 ====================== 32 32 33 The code in this distribution is Copyright (c) 2003-2009Ralph Meijer, unless34 ex cplicitely specified otherwise.33 The code in this distribution is Copyright (c) Ralph Meijer, unless 34 explicitly specified otherwise. 35 35 36 36 Wokkel is made available under the MIT License. The included LICENSE file -
setup.py
- Property exe set to *
r73 r96 1 1 #!/usr/bin/env python 2 2 3 # Copyright (c) 2003-2009 Ralph Meijer3 # Copyright (c) Ralph Meijer. 4 4 # See LICENSE for details. 5 5 … … 18 18 'wokkel', 19 19 'wokkel.test', 20 'twisted.plugins', 20 21 ], 22 package_data={'twisted.plugins': ['twisted/plugins/server.py']}, 21 23 ) -
wokkel/__init__.py
r1 r96 1 # Copyright (c) 2003-2007 Ralph Meijer1 # Copyright (c) Ralph Meijer. 2 2 # See LICENSE for details 3 3 -
wokkel/client.py
- Property exe set to *
r73 r96 1 1 # -*- test-case-name: wokkel.test.test_client -*- 2 2 # 3 # Copyright (c) 2003-2009 Ralph Meijer3 # Copyright (c) Ralph Meijer. 4 4 # See LICENSE for details. 5 5 -
wokkel/compat.py
- Property exe set to *
r63 r96 1 1 # -*- test-case-name: wokkel.test.test_compat -*- 2 2 # 3 # Copyright (c) 2001-2009Twisted Matrix Laboratories.3 # Copyright (c) Twisted Matrix Laboratories. 4 4 # See LICENSE for details. 5 5 -
wokkel/component.py
- Property exe set to *
r54 r96 1 1 # -*- test-case-name: wokkel.test.test_component -*- 2 2 # 3 # Copyright (c) 2003-2008 Ralph Meijer3 # Copyright (c) Ralph Meijer. 4 4 # See LICENSE for details. 5 5 … … 158 158 159 159 @ivar secret: The shared used to authorized incoming component connections. 160 @type secret: C{ str}.160 @type secret: C{unicode}. 161 161 """ 162 162 … … 237 237 be exchanged. 238 238 """ 239 calculatedHash = xmlstream.hashPassword(self.xmlstream.sid, self.secret) 239 calculatedHash = xmlstream.hashPassword(self.xmlstream.sid, 240 unicode(self.secret)) 240 241 if handshake != calculatedHash: 241 242 exc = error.StreamError('not-authorized', text='Invalid hash') -
wokkel/data_form.py
- Property exe set to *
r56 r96 1 1 # -*- test-case-name: wokkel.test.test_data_form -*- 2 2 # 3 # Copyright (c) 2003-2009 Ralph Meijer3 # Copyright (c) Ralph Meijer. 4 4 # See LICENSE for details. 5 5 … … 76 76 option['label'] = self.label 77 77 return option 78 78 79 79 80 @staticmethod … … 130 131 self.fieldType = fieldType 131 132 self.var = var 133 132 134 if value is not None: 133 135 self.value = value 134 136 else: 135 137 self.values = values or [] 138 139 self.label = label 136 140 137 141 try: … … 141 145 self.options = options or [] 142 146 143 self.label = label144 147 self.desc = desc 145 148 self.required = required … … 213 216 if self.values: 214 217 if (self.fieldType not in ('hidden', 'jid-multi', 'list-multi', 215 'text-multi') and218 'text-multi', None) and 216 219 len(self.values) > 1): 217 220 raise TooManyValuesError() … … 234 237 self.values = newValues 235 238 239 236 240 def toElement(self, asForm=False): 237 241 """ … … 245 249 field = domish.Element((NS_X_DATA, 'field')) 246 250 247 if asForm or self.fieldType != 'text-single':251 if self.fieldType: 248 252 field['type'] = self.fieldType 249 253 … … 252 256 253 257 for value in self.values: 254 if self.fieldType == 'boolean':258 if isinstance(value, bool): 255 259 value = unicode(value).lower() 256 el if self.fieldType in ('jid-single', 'jid-multi'):257 value = value.full()260 else: 261 value = unicode(value) 258 262 259 263 field.addElement('value', content=value) … … 323 327 324 328 @staticmethod 325 def fromDict(dictionary): 326 kwargs = dictionary.copy() 327 328 if 'type' in dictionary: 329 kwargs['fieldType'] = dictionary['type'] 329 def fromDict(fieldDict): 330 """ 331 Create a field from a dictionary. 332 333 This is a short hand for passing arguments directly on Field object 334 creation. The field type is represented by the C{'type'} key. For 335 C{'options'} the value is not a list of L{Option}s, but a dictionary 336 keyed by value, with an optional label as value. 337 """ 338 kwargs = fieldDict.copy() 339 340 if 'type' in fieldDict: 341 kwargs['fieldType'] = fieldDict['type'] 330 342 del kwargs['type'] 331 343 332 if 'options' in dictionary:344 if 'options' in fieldDict: 333 345 options = [] 334 for value, label in dictionary['options'].iteritems():346 for value, label in fieldDict['options'].iteritems(): 335 347 options.append(Option(value, label)) 336 348 kwargs['options'] = options … … 344 356 Data Form. 345 357 346 There are two similarly named properties of forms. The L{formType} is the358 There are two similarly named properties of forms. The C{formType} is the 347 359 the so-called type of the form, and is set as the C{'type'} attribute 348 360 on the form's root element. … … 352 364 special hidden field named C{'FORM_TYPE'}, to put the names of all 353 365 other fields in the namespace of the value of that field. This namespace 354 is recorded in the L{formNamespace} instance variable.366 is recorded in the C{formNamespace} instance variable. 355 367 356 368 @ivar formType: Type of form. One of C{'form'}, C{'submit'}, {'cancel'}, 357 369 or {'result'}. 358 @type formType: C{str}. 370 @type formType: C{str} 371 372 @ivar title: Natural language title of the form. 373 @type title: C{unicode} 374 375 @ivar instructions: Natural language instructions as a list of C{unicode} 376 strings without line breaks. 377 @type instructions: C{list} 378 359 379 @ivar formNamespace: The optional namespace of the field names for this 360 form. This goes in the special field named 361 C{'FORM_TYPE'}, if set. 380 form. This goes in the special field named C{'FORM_TYPE'}, if set. 362 381 @type formNamespace: C{str}. 363 @ivar fields: Dictionary of fields that have a name. Note that this is 364 meant to be used for reading, only. One should use 365 L{addField} for adding fields. 382 383 @ivar fields: Dictionary of named fields. Note that this is meant to be 384 used for reading, only. One should use L{addField} or L{makeFields} and 385 L{removeField} for adding and removing fields. 366 386 @type fields: C{dict} 387 388 @ivar fieldList: List of all fields, in the order they are added. Like 389 C{fields}, this is meant to be used for reading, only. 390 @type fieldList: C{list} 367 391 """ 368 392 … … 393 417 r.append(", formNamespace=") 394 418 r.append(repr(self.formNamespace)) 395 if self.field s:419 if self.fieldList: 396 420 r.append(", fields=") 397 421 r.append(repr(self.fieldList)) … … 404 428 Add a field to this form. 405 429 406 Fields are added in order, and L{fields} is a dictionary of the430 Fields are added in order, and C{fields} is a dictionary of the 407 431 named fields, that is kept in sync only if this method is used for 408 432 adding new fields. Multiple fields with the same name are disallowed. … … 417 441 418 442 443 def removeField(self, field): 444 """ 445 Remove a field from this form. 446 """ 447 self.fieldList.remove(field) 448 449 if field.var is not None: 450 del self.fields[field.var] 451 452 453 def makeFields(self, values, fieldDefs=None, filterUnknown=True): 454 """ 455 Create fields from values and add them to this form. 456 457 This creates fields from a mapping of name to value(s) and adds them to 458 this form. It is typically used for generating outgoing forms. 459 460 If C{fieldDefs} is not C{None}, this is used to fill in 461 additional properties of fields, like the field types, labels and 462 possible options. 463 464 If C{filterUnknown} is C{True} and C{fieldDefs} is not C{None}, fields 465 will only be created from C{values} with a corresponding entry in 466 C{fieldDefs}. 467 468 If the field type is unknown, the field type is C{None}. When the form 469 is rendered using L{toElement}, these fields will have no C{'type'} 470 attribute, and it is up to the receiving party to interpret the values 471 properly (e.g. by knowing about the FORM_TYPE in L{formNamespace} and 472 the field name). 473 474 @param values: Values to create fields from. 475 @type values: C{dict} 476 477 @param fieldDefs: Field definitions as a dictionary. See 478 L{wokkel.iwokkel.IPubSubService.getConfigurationOptions} 479 @type fieldDefs: C{dict} 480 481 @param filterUnknown: If C{True}, ignore fields that are not in 482 C{fieldDefs}. 483 @type filterUnknown: C{bool} 484 """ 485 for name, value in values.iteritems(): 486 fieldDict = {'var': name, 487 'type': None} 488 489 if fieldDefs is not None: 490 if name in fieldDefs: 491 fieldDict.update(fieldDefs[name]) 492 elif filterUnknown: 493 continue 494 495 if isinstance(value, list): 496 fieldDict['values'] = value 497 else: 498 fieldDict['value'] = value 499 500 self.addField(Field.fromDict(fieldDict)) 501 502 419 503 def toElement(self): 504 """ 505 Return the DOM representation of this Form. 506 507 @rtype: L{domish.Element} 508 """ 420 509 form = domish.Element((NS_X_DATA, 'x')) 421 510 form['type'] = self.formType … … 425 514 426 515 for instruction in self.instructions: 427 form.addElement('instruction ', content=instruction)516 form.addElement('instructions', content=instruction) 428 517 429 518 if self.formNamespace is not None: … … 478 567 return form 479 568 569 480 570 def getValues(self): 571 """ 572 Extract values from the named form fields. 573 574 For all named fields, the corresponding value or values are 575 returned in a dictionary keyed by the field name. For multi-value 576 fields, the dictionary value is a list, otherwise a single value. 577 578 If a field has no type, and the field has multiple values, the value of 579 the dictionary entry is the list of values. Otherwise, it will be a 580 single value. 581 582 @rtype: C{dict} 583 """ 481 584 values = {} 482 585 483 586 for name, field in self.fields.iteritems(): 484 if len(field.values) > 1: 587 if (field.fieldType in ('jid-multi', 'list-multi', 'text-multi') or 588 (field.fieldType is None and len(field.values) > 1)): 485 589 value = field.values 486 590 else: … … 490 594 491 595 return values 596 597 598 def typeCheck(self, fieldDefs=None, filterUnknown=False): 599 """ 600 Check values of fields according to the field definition. 601 602 This method walks all named fields to check their values against their 603 type, and is typically used for forms received from other entities. The 604 field definition in C{fieldDefs} is used to check the field type. 605 606 If C{filterUnknown} is C{True}, fields that are not present in 607 C{fieldDefs} are removed from the form. 608 609 If the field type is C{None} (when not set by the sending entity), 610 the type from the field definitition is used, or C{'text-single'} if 611 that is not set. 612 613 If C{fieldDefs} is None, an empty dictionary is assumed. This is 614 useful for coercing boolean and JID values on forms with type 615 C{'form'}. 616 617 @param fieldDefs: Field definitions as a dictionary. See 618 L{wokkel.iwokkel.IPubSubService.getConfigurationOptions} 619 @type fieldDefs: C{dict} 620 621 @param filterUnknown: If C{True}, remove fields that are not in 622 C{fieldDefs}. 623 @type filterUnknown: C{bool} 624 """ 625 626 if fieldDefs is None: 627 fieldDefs = {} 628 629 filtered = [] 630 631 for name, field in self.fields.iteritems(): 632 if name in fieldDefs: 633 fieldDef = fieldDefs[name] 634 if 'type' not in fieldDef: 635 fieldDef['type'] = 'text-single' 636 637 if field.fieldType is None: 638 field.fieldType = fieldDef['type'] 639 elif field.fieldType != fieldDef['type']: 640 raise TypeError("Field type for %r is %r, expected %r" % 641 (name, 642 field.fieldType, 643 fieldDef['type'])) 644 else: 645 # Field type is correct 646 pass 647 field.typeCheck() 648 elif filterUnknown: 649 filtered.append(field) 650 elif field.fieldType is not None: 651 field.typeCheck() 652 else: 653 # Unknown field without type, no checking, no filtering 654 pass 655 656 for field in filtered: 657 self.removeField(field) 658 659 660 661 def findForm(element, formNamespace): 662 """ 663 Find a Data Form. 664 665 Look for an element that represents a Data Form with the specified 666 form namespace as a child element of the given element. 667 """ 668 if not element: 669 return None 670 671 for child in element.elements(): 672 if (child.uri, child.name) == ((NS_X_DATA, 'x')): 673 form = Form.fromElement(child) 674 675 if (form.formNamespace == formNamespace or 676 not form.formNamespace and form.formType=='cancel'): 677 return form 678 679 return None -
wokkel/disco.py
- Property exe set to *
r133 r134 1 1 # -*- test-case-name: wokkel.test.test_disco -*- 2 2 # 3 # Copyright (c) 2003-2009 Ralph Meijer3 # Copyright (c) Ralph Meijer. 4 4 # See LICENSE for details. 5 5 … … 15 15 from twisted.words.xish import domish 16 16 17 from wokkel import data_form 18 from wokkel.compat import IQ 17 from wokkel import data_form, generic 19 18 from wokkel.iwokkel import IDisco 20 19 from wokkel.subprotocols import IQHandlerMixin, XMPPHandler … … 120 119 @type nodeIdentifier: C{unicode} 121 120 @ivar features: Features as L{DiscoFeature}. 122 @type features: C{set )121 @type features: C{set} 123 122 @ivar identities: Identities as a mapping from (category, type) to name, 124 123 all C{unicode}. … … 347 346 348 347 349 class _DiscoRequest(IQ): 350 """ 351 Element representing an XMPP service discovery request. 352 """ 353 354 def __init__(self, xs, namespace, nodeIdentifier=''): 355 """ 356 Initialize the request. 357 358 @param xs: XML Stream the request should go out on. 359 @type xs: L{xmlstream.XmlStream} 360 @param namespace: Request namespace. 361 @type namespace: C{str} 362 @param nodeIdentifier: Node to request info from. 363 @type nodeIdentifier: C{unicode} 364 """ 365 IQ.__init__(self, xs, "get") 366 query = self.addElement((namespace, 'query')) 367 if nodeIdentifier: 368 query['node'] = nodeIdentifier 348 class _DiscoRequest(generic.Request): 349 """ 350 A Service Discovery request. 351 352 @ivar verb: Type of request: C{'info'} or C{'items'}. 353 @type verb: C{str} 354 @ivar nodeIdentifier: Optional node to request info for. 355 @type nodeIdentifier: C{unicode} 356 """ 357 358 verb = None 359 nodeIdentifier = '' 360 361 _requestVerbMap = { 362 NS_DISCO_INFO: 'info', 363 NS_DISCO_ITEMS: 'items', 364 } 365 366 _verbRequestMap = dict(((v, k) for k, v in _requestVerbMap.iteritems())) 367 368 def __init__(self, verb=None, nodeIdentifier='', 369 recipient=None, sender=None): 370 generic.Request.__init__(self, recipient=recipient, sender=sender, 371 stanzaType='get') 372 self.verb = verb 373 self.nodeIdentifier = nodeIdentifier 374 375 376 def parseElement(self, element): 377 generic.Request.parseElement(self, element) 378 379 verbElement = None 380 for child in element.elements(): 381 if child.name == 'query' and child.uri in self._requestVerbMap: 382 self.verb = self._requestVerbMap[child.uri] 383 verbElement = child 384 385 if verbElement: 386 self.nodeIdentifier = verbElement.getAttribute('node', '') 387 388 389 def toElement(self): 390 element = generic.Request.toElement(self) 391 392 childURI = self._verbRequestMap[self.verb] 393 query = element.addElement((childURI, 'query')) 394 395 if self.nodeIdentifier: 396 query['node'] = self.nodeIdentifier 397 398 return element 369 399 370 400 … … 389 419 """ 390 420 391 request = _DiscoRequest( self.xmlstream, NS_DISCO_INFO, nodeIdentifier)392 if sender is not None:393 request['from'] = unicode(sender)394 395 d = request.send(entity.full())421 request = _DiscoRequest('info', nodeIdentifier) 422 request.sender = sender 423 request.recipient = entity 424 425 d = self.request(request) 396 426 d.addCallback(lambda iq: DiscoInfo.fromElement(iq.query)) 397 427 return d … … 412 442 """ 413 443 414 request = _DiscoRequest( self.xmlstream, NS_DISCO_ITEMS, nodeIdentifier)415 if sender is not None:416 request['from'] = unicode(sender)417 418 d = request.send(entity.full())444 request = _DiscoRequest('items', nodeIdentifier) 445 request.sender = sender 446 request.recipient = entity 447 448 d = self.request(request) 419 449 d.addCallback(lambda iq: DiscoItems.fromElement(iq.query)) 420 450 return d … … 446 476 @type iq: L{Element<twisted.words.xish.domish.Element>} 447 477 """ 448 requestor = jid.internJID(iq["from"]) 449 target = jid.internJID(iq["to"]) 450 nodeIdentifier = iq.query.getAttribute("node", '') 478 request = _DiscoRequest.fromElement(iq) 451 479 452 480 def toResponse(info): 453 if nodeIdentifier and not info:481 if request.nodeIdentifier and not info: 454 482 raise error.StanzaError('item-not-found') 455 483 else: 456 484 response = DiscoInfo() 457 response.nodeIdentifier = nodeIdentifier485 response.nodeIdentifier = request.nodeIdentifier 458 486 459 487 for item in info: … … 462 490 return response.toElement() 463 491 464 d = self.info(requestor, target, nodeIdentifier) 492 d = self.info(request.sender, request.recipient, 493 request.nodeIdentifier) 465 494 d.addCallback(toResponse) 466 495 return d … … 474 503 @type iq: L{Element<twisted.words.xish.domish.Element>} 475 504 """ 476 requestor = jid.internJID(iq["from"]) 477 target = jid.internJID(iq["to"]) 478 nodeIdentifier = iq.query.getAttribute("node", '') 505 request = _DiscoRequest.fromElement(iq) 479 506 480 507 def toResponse(items): 481 508 response = DiscoItems() 482 response.nodeIdentifier = nodeIdentifier509 response.nodeIdentifier = request.nodeIdentifier 483 510 484 511 for item in items: … … 487 514 return response.toElement() 488 515 489 d = self.items(requestor, target, nodeIdentifier) 516 d = self.items(request.sender, request.recipient, 517 request.nodeIdentifier) 490 518 d.addCallback(toResponse) 491 519 return d -
wokkel/formats.py
- Property exe set to *
r12 r96 1 # Copyright (c) 2003-2008 Ralph Meijer1 # Copyright (c) Ralph Meijer. 2 2 # See LICENSE for details. 3 3 -
wokkel/generic.py
- Property exe set to *
r68 r103 1 1 # -*- test-case-name: wokkel.test.test_generic -*- 2 2 # 3 # Copyright (c) 2003-2009 Ralph Meijer3 # Copyright (c) Ralph Meijer. 4 4 # See LICENSE for details. 5 5 … … 21 21 from wokkel.compat import BootstrapMixin 22 22 23 from wokkel import disco24 23 from wokkel.iwokkel import IDisco 25 24 from wokkel.subprotocols import XMPPHandler … … 121 120 122 121 if not node: 122 from wokkel import disco 123 123 info.add(disco.DiscoFeature(NS_VERSION)) 124 124 … … 175 175 """ 176 176 177 recipient = None 178 sender = None 177 179 stanzaKind = None 178 180 stanzaID = None … … 237 239 238 240 241 class Request(Stanza): 242 """ 243 IQ request stanza. 244 245 This is a base class for IQ get or set stanzas, to be used with 246 L{wokkel.subprotocols.StreamManager.request}. 247 """ 248 249 stanzaKind = 'iq' 250 stanzaType = 'get' 251 timeout = None 252 253 def __init__(self, recipient=None, sender=None, stanzaType='get'): 254 Stanza.__init__(self, recipient=recipient, sender=sender) 255 self.stanzaType = stanzaType 256 257 258 def toElement(self): 259 element = Stanza.toElement(self) 260 261 if not self.stanzaID: 262 element.addUniqueId() 263 self.stanzaID = element['id'] 264 265 return element 266 267 268 239 269 class DeferredXmlStreamFactory(BootstrapMixin, protocol.ClientFactory): 240 270 protocol = xmlstream.XmlStream -
wokkel/iwokkel.py
- Property exe set to *
-
wokkel/ping.py
- Property exe set to *
r65 r96 1 1 # -*- test-case-name: wokkel.test.test_ping -*- 2 2 # 3 # Copyright (c) 2003-2009 Ralph Meijer3 # Copyright (c) Ralph Meijer. 4 4 # See LICENSE for details. 5 5 … … 95 95 response = toResponse(iq, 'result') 96 96 self.xmlstream.send(response) 97 iq.handled = True 97 98 98 99 -
wokkel/pubsub.py
- Property exe set to *
r63 r97 1 1 # -*- test-case-name: wokkel.test.test_pubsub -*- 2 2 # 3 # Copyright (c) 2003-2009 Ralph Meijer3 # Copyright (c) Ralph Meijer. 4 4 # See LICENSE for details. 5 5 … … 103 103 A subscription to a node. 104 104 105 @ivar nodeIdentifier: The identifier of the node subscribed to. 106 The root node is denoted by C{None}. 105 @ivar nodeIdentifier: The identifier of the node subscribed to. The root 106 node is denoted by C{None}. 107 @type nodeIdentifier: C{unicode} 108 107 109 @ivar subscriber: The subscribing entity. 110 @type subscriber: L{jid.JID} 111 108 112 @ivar state: The subscription state. One of C{'subscribed'}, C{'pending'}, 109 113 C{'unconfigured'}. 114 @type state: C{unicode} 115 110 116 @ivar options: Optional list of subscription options. 111 @type options: C{dict}. 112 """ 113 114 def __init__(self, nodeIdentifier, subscriber, state, options=None): 117 @type options: C{dict} 118 119 @ivar subscriptionIdentifier: Optional subscription identifier. 120 @type subscriptionIdentifier: C{unicode} 121 """ 122 123 def __init__(self, nodeIdentifier, subscriber, state, options=None, 124 subscriptionIdentifier=None): 115 125 self.nodeIdentifier = nodeIdentifier 116 126 self.subscriber = subscriber 117 127 self.state = state 118 128 self.options = options or {} 129 self.subscriptionIdentifier = subscriptionIdentifier 130 131 132 @staticmethod 133 def fromElement(element): 134 return Subscription( 135 element.getAttribute('node'), 136 jid.JID(element.getAttribute('jid')), 137 element.getAttribute('subscription'), 138 subscriptionIdentifier=element.getAttribute('subid')) 139 140 141 def toElement(self, defaultUri=None): 142 """ 143 Return the DOM representation of this subscription. 144 145 @rtype: L{domish.Element} 146 """ 147 element = domish.Element((defaultUri, 'subscription')) 148 if self.nodeIdentifier: 149 element['node'] = self.nodeIdentifier 150 element['jid'] = unicode(self.subscriber) 151 element['subscription'] = self.state 152 if self.subscriptionIdentifier: 153 element['subid'] = self.subscriptionIdentifier 154 return element 119 155 120 156 … … 139 175 """ 140 176 141 domish.Element.__init__(self, (N S_PUBSUB, 'item'))177 domish.Element.__init__(self, (None, 'item')) 142 178 if id is not None: 143 179 self['id'] = id … … 185 221 L{Subscription}. 186 222 @type subscriptions: C{set} 223 @ivar affiliations: Affiliations to be modified, as a dictionary of entity 224 (L{JID} to affiliation (C{unicode}). 225 @type affiliations: C{dict} 187 226 """ 188 227 … … 199 238 subscriptionIdentifier = None 200 239 subscriptions = None 240 affiliations = None 201 241 202 242 # Map request iq type and subelement name to request verb … … 229 269 _parameters = { 230 270 'publish': ['node', 'items'], 231 'subscribe': ['nodeOrEmpty', 'jid' ],232 'unsubscribe': ['nodeOrEmpty', 'jid' ],233 'optionsGet': ['nodeOrEmpty', 'jid' ],234 'optionsSet': ['nodeOrEmpty', 'jid', 'options' ],271 'subscribe': ['nodeOrEmpty', 'jid', 'optionsWithSubscribe'], 272 'unsubscribe': ['nodeOrEmpty', 'jid', 'subidOrNone'], 273 'optionsGet': ['nodeOrEmpty', 'jid', 'subidOrNone'], 274 'optionsSet': ['nodeOrEmpty', 'jid', 'options', 'subidOrNone'], 235 275 'subscriptions': [], 236 276 'affiliations': [], 237 'create': ['nodeOrNone' ],277 'create': ['nodeOrNone', 'configureOrNone'], 238 278 'default': ['default'], 239 279 'configureGet': ['nodeOrEmpty'], 240 280 'configureSet': ['nodeOrEmpty', 'configure'], 241 'items': ['node', 'maxItems', 'itemIdentifiers' ],281 'items': ['node', 'maxItems', 'itemIdentifiers', 'subidOrNone'], 242 282 'retract': ['node', 'itemIdentifiers'], 243 283 'purge': ['node'], 244 284 'delete': ['node'], 245 285 'affiliationsGet': ['nodeOrEmpty'], 246 'affiliationsSet': [ ],286 'affiliationsSet': ['nodeOrEmpty', 'affiliations'], 247 287 'subscriptionsGet': ['nodeOrEmpty'], 248 288 'subscriptionsSet': [], … … 251 291 def __init__(self, verb=None): 252 292 self.verb = verb 253 254 255 @staticmethod256 def _findForm(element, formNamespace):257 """258 Find a Data Form.259 260 Look for an element that represents a Data Form with the specified261 form namespace as a child element of the given element.262 """263 if not element:264 return None265 266 form = None267 for child in element.elements():268 try:269 form = data_form.Form.fromElement(child)270 except data_form.Error:271 continue272 273 if form.formNamespace != NS_PUBSUB_NODE_CONFIG:274 continue275 276 return form277 293 278 294 … … 343 359 if self.items: 344 360 for item in self.items: 361 item.uri = NS_PUBSUB 345 362 verbElement.addChild(item) 346 363 … … 367 384 Parse node type out of a request for the default node configuration. 368 385 """ 369 form = PubSubRequest._findForm(verbElement, NS_PUBSUB_NODE_CONFIG)386 form = data_form.findForm(verbElement, NS_PUBSUB_NODE_CONFIG) 370 387 if form and form.formType == 'submit': 371 388 values = form.getValues() … … 379 396 Parse options out of a request for setting the node configuration. 380 397 """ 381 form = PubSubRequest._findForm(verbElement, NS_PUBSUB_NODE_CONFIG)398 form = data_form.findForm(verbElement, NS_PUBSUB_NODE_CONFIG) 382 399 if form: 383 if form.formType == 'submit': 384 self.options = form.getValues() 385 elif form.formType == 'cancel': 386 self.options = {} 400 if form.formType in ('submit', 'cancel'): 401 self.options = form 387 402 else: 388 raise BadRequest(text= "Unexpected form type %r" % form.formType)403 raise BadRequest(text=u"Unexpected form type '%s'" % form.formType) 389 404 else: 390 405 raise BadRequest(text="Missing configuration form") 391 406 407 408 def _parse_configureOrNone(self, verbElement): 409 """ 410 Parse optional node configuration form in create request. 411 """ 412 for element in verbElement.parent.elements(): 413 if element.uri == NS_PUBSUB and element.name == 'configure': 414 form = data_form.findForm(element, NS_PUBSUB_NODE_CONFIG) 415 if form: 416 if form.formType != 'submit': 417 raise BadRequest(text=u"Unexpected form type '%s'" % 418 form.formType) 419 else: 420 form = data_form.Form('submit', 421 formNamespace=NS_PUBSUB_NODE_CONFIG) 422 self.options = form 423 424 425 def _render_configureOrNone(self, verbElement): 426 """ 427 Render optional node configuration form in create request. 428 """ 429 if self.options is not None: 430 configure = verbElement.parent.addElement('configure') 431 configure.addChild(self.options.toElement()) 392 432 393 433 … … 431 471 def _render_maxItems(self, verbElement): 432 472 """ 433 Parsemaximum items into an items request.473 Render maximum items into an items request. 434 474 """ 435 475 if self.maxItems: … … 437 477 438 478 479 def _parse_subidOrNone(self, verbElement): 480 """ 481 Parse subscription identifier out of a request. 482 """ 483 self.subscriptionIdentifier = verbElement.getAttribute("subid") 484 485 486 def _render_subidOrNone(self, verbElement): 487 """ 488 Render subscription identifier into a request. 489 """ 490 if self.subscriptionIdentifier: 491 verbElement['subid'] = self.subscriptionIdentifier 492 493 439 494 def _parse_options(self, verbElement): 440 form = PubSubRequest._findForm(verbElement, NS_PUBSUB_SUBSCRIBE_OPTIONS) 495 """ 496 Parse options form out of a subscription options request. 497 """ 498 form = data_form.findForm(verbElement, NS_PUBSUB_SUBSCRIBE_OPTIONS) 441 499 if form: 442 if form.formType == 'submit': 443 self.options = form.getValues() 444 elif form.formType == 'cancel': 445 self.options = {} 500 if form.formType in ('submit', 'cancel'): 501 self.options = form 446 502 else: 447 raise BadRequest(text= "Unexpected form type %r" % form.formType)503 raise BadRequest(text=u"Unexpected form type '%s'" % form.formType) 448 504 else: 449 505 raise BadRequest(text="Missing options form") 450 506 507 508 509 def _render_options(self, verbElement): 510 verbElement.addChild(self.options.toElement()) 511 512 513 def _parse_optionsWithSubscribe(self, verbElement): 514 for element in verbElement.parent.elements(): 515 if element.name == 'options' and element.uri == NS_PUBSUB: 516 form = data_form.findForm(element, 517 NS_PUBSUB_SUBSCRIBE_OPTIONS) 518 if form: 519 if form.formType != 'submit': 520 raise BadRequest(text=u"Unexpected form type '%s'" % 521 form.formType) 522 else: 523 form = data_form.Form('submit', 524 formNamespace=NS_PUBSUB_SUBSCRIBE_OPTIONS) 525 self.options = form 526 527 528 def _render_optionsWithSubscribe(self, verbElement): 529 if self.options: 530 optionsElement = verbElement.parent.addElement('options') 531 self._render_options(optionsElement) 532 533 534 def _parse_affiliations(self, verbElement): 535 self.affiliations = {} 536 for element in verbElement.elements(): 537 if (element.uri == NS_PUBSUB_OWNER and 538 element.name == 'affiliation'): 539 try: 540 entity = jid.internJID(element['jid']).userhostJID() 541 except KeyError: 542 raise BadRequest(text='Missing jid attribute') 543 544 if entity in self.affiliations: 545 raise BadRequest(text='Multiple affiliations for an entity') 546 547 try: 548 affiliation = element['affiliation'] 549 except KeyError: 550 raise BadRequest(text='Missing affiliation attribute') 551 552 self.affiliations[entity] = affiliation 553 554 451 555 def parseElement(self, element): 452 556 """ … … 455 559 generic.Stanza.parseElement(self, element) 456 560 561 verbs = [] 562 verbElements = [] 457 563 for child in element.pubsub.elements(): 458 564 key = (self.stanzaType, child.uri, child.name) … … 461 567 except KeyError: 462 568 continue 569 570 verbs.append(verb) 571 verbElements.append(child) 572 573 if not verbs: 574 raise NotImplementedError() 575 576 if len(verbs) > 1: 577 if 'optionsSet' in verbs and 'subscribe' in verbs: 578 self.verb = 'subscribe' 579 verbElement = verbElements[verbs.index('subscribe')] 463 580 else: 464 self.verb = verb465 break466 467 if not self.verb:468 raise NotImplementedError() 469 470 for parameter in self._parameters[verb]:471 getattr(self, '_parse_%s' % parameter)(child) 581 raise NotImplementedError() 582 else: 583 self.verb = verbs[0] 584 verbElement = verbElements[0] 585 586 for parameter in self._parameters[self.verb]: 587 getattr(self, '_parse_%s' % parameter)(verbElement) 588 472 589 473 590 … … 574 691 575 692 def _onEvent(self, message): 693 if message.getAttribute('type') == 'error': 694 return 695 576 696 try: 577 697 sender = jid.JID(message["from"]) … … 632 752 633 753 634 def createNode(self, service, nodeIdentifier=None, sender=None): 754 def createNode(self, service, nodeIdentifier=None, options=None, 755 sender=None): 635 756 """ 636 757 Create a publish subscribe node. … … 640 761 @param nodeIdentifier: Optional suggestion for the id of the node. 641 762 @type nodeIdentifier: C{unicode} 763 @param options: Optional node configuration options. 764 @type options: C{dict} 642 765 """ 643 766 request = PubSubRequest('create') … … 645 768 request.nodeIdentifier = nodeIdentifier 646 769 request.sender = sender 770 771 if options: 772 form = data_form.Form(formType='submit', 773 formNamespace=NS_PUBSUB_NODE_CONFIG) 774 form.makeFields(options) 775 request.options = form 647 776 648 777 def cb(iq): … … 675 804 676 805 677 def subscribe(self, service, nodeIdentifier, subscriber, sender=None): 806 def subscribe(self, service, nodeIdentifier, subscriber, 807 options=None, sender=None): 678 808 """ 679 809 Subscribe to a publish subscribe node. … … 681 811 @param service: The publish subscribe service that keeps the node. 682 812 @type service: L{JID} 813 683 814 @param nodeIdentifier: The identifier of the node. 684 815 @type nodeIdentifier: C{unicode} 816 685 817 @param subscriber: The entity to subscribe to the node. This entity 686 818 will get notifications of new published items. 687 819 @type subscriber: L{JID} 820 821 @param options: Subscription options. 822 @type options: C{dict} 823 824 @return: Deferred that fires with L{Subscription} or errbacks with 825 L{SubscriptionPending} or L{SubscriptionUnconfigured}. 826 @rtype: L{defer.Deferred} 688 827 """ 689 828 request = PubSubRequest('subscribe') … … 693 832 request.sender = sender 694 833 834 if options: 835 form = data_form.Form(formType='submit', 836 formNamespace=NS_PUBSUB_SUBSCRIBE_OPTIONS) 837 form.makeFields(options) 838 request.options = form 839 695 840 def cb(iq): 696 subscription = iq.pubsub.subscription["subscription"]697 698 if subscription == 'pending':699 raise SubscriptionPending 700 elif subscription == 'unconfigured':701 raise SubscriptionUnconfigured 841 subscription = Subscription.fromElement(iq.pubsub.subscription) 842 843 if subscription.state == 'pending': 844 raise SubscriptionPending() 845 elif subscription.state == 'unconfigured': 846 raise SubscriptionUnconfigured() 702 847 else: 703 848 # we assume subscription == 'subscribed' 704 849 # any other value would be invalid, but that should have 705 850 # yielded a stanza error. 706 return None851 return subscription 707 852 708 853 d = request.send(self.xmlstream) … … 711 856 712 857 713 def unsubscribe(self, service, nodeIdentifier, subscriber, sender=None): 858 def unsubscribe(self, service, nodeIdentifier, subscriber, 859 subscriptionIdentifier=None, sender=None): 714 860 """ 715 861 Unsubscribe from a publish subscribe node. … … 717 863 @param service: The publish subscribe service that keeps the node. 718 864 @type service: L{JID} 865 719 866 @param nodeIdentifier: The identifier of the node. 720 867 @type nodeIdentifier: C{unicode} 868 721 869 @param subscriber: The entity to unsubscribe from the node. 722 870 @type subscriber: L{JID} 871 872 @param subscriptionIdentifier: Optional subscription identifier. 873 @type subscriptionIdentifier: C{unicode} 723 874 """ 724 875 request = PubSubRequest('unsubscribe') … … 726 877 request.nodeIdentifier = nodeIdentifier 727 878 request.subscriber = subscriber 879 request.subscriptionIdentifier = subscriptionIdentifier 728 880 request.sender = sender 729 881 return request.send(self.xmlstream) … … 749 901 750 902 751 def items(self, service, nodeIdentifier, maxItems=None, sender=None): 903 def items(self, service, nodeIdentifier, maxItems=None, 904 subscriptionIdentifier=None, sender=None): 752 905 """ 753 906 Retrieve previously published items from a publish subscribe node. … … 755 908 @param service: The publish subscribe service that keeps the node. 756 909 @type service: L{JID} 910 757 911 @param nodeIdentifier: The identifier of the node. 758 912 @type nodeIdentifier: C{unicode} 913 759 914 @param maxItems: Optional limit on the number of retrieved items. 760 915 @type maxItems: C{int} 916 917 @param subscriptionIdentifier: Optional subscription identifier. In 918 case the node has been subscribed to multiple times, this narrows 919 the results to the specific subscription. 920 @type subscriptionIdentifier: C{unicode} 761 921 """ 762 922 request = PubSubRequest('items') … … 765 925 if maxItems: 766 926 request.maxItems = str(int(maxItems)) 927 request.subscriptionIdentifier = subscriptionIdentifier 767 928 request.sender = sender 768 929 … … 779 940 780 941 942 def getOptions(self, service, nodeIdentifier, subscriber, 943 subscriptionIdentifier=None, sender=None): 944 """ 945 Get subscription options. 946 947 @param service: The publish subscribe service that keeps the node. 948 @type service: L{JID} 949 950 @param nodeIdentifier: The identifier of the node. 951 @type nodeIdentifier: C{unicode} 952 953 @param subscriber: The entity subscribed to the node. 954 @type subscriber: L{JID} 955 956 @param subscriptionIdentifier: Optional subscription identifier. 957 @type subscriptionIdentifier: C{unicode} 958 959 @rtype: L{data_form.Form} 960 """ 961 request = PubSubRequest('optionsGet') 962 request.recipient = service 963 request.nodeIdentifier = nodeIdentifier 964 request.subscriber = subscriber 965 request.subscriptionIdentifier = subscriptionIdentifier 966 request.sender = sender 967 968 def cb(iq): 969 form = data_form.findForm(iq.pubsub.options, 970 NS_PUBSUB_SUBSCRIBE_OPTIONS) 971 form.typeCheck() 972 return form 973 974 d = request.send(self.xmlstream) 975 d.addCallback(cb) 976 return d 977 978 979 def setOptions(self, service, nodeIdentifier, subscriber, 980 options, subscriptionIdentifier=None, sender=None): 981 """ 982 Set subscription options. 983 984 @param service: The publish subscribe service that keeps the node. 985 @type service: L{JID} 986 987 @param nodeIdentifier: The identifier of the node. 988 @type nodeIdentifier: C{unicode} 989 990 @param subscriber: The entity subscribed to the node. 991 @type subscriber: L{JID} 992 993 @param options: Subscription options. 994 @type options: C{dict}. 995 996 @param subscriptionIdentifier: Optional subscription identifier. 997 @type subscriptionIdentifier: C{unicode} 998 """ 999 request = PubSubRequest('optionsSet') 1000 request.recipient = service 1001 request.nodeIdentifier = nodeIdentifier 1002 request.subscriber = subscriber 1003 request.subscriptionIdentifier = subscriptionIdentifier 1004 request.sender = sender 1005 1006 form = data_form.Form(formType='submit', 1007 formNamespace=NS_PUBSUB_SUBSCRIBE_OPTIONS) 1008 form.makeFields(options) 1009 request.options = form 1010 1011 d = request.send(self.xmlstream) 1012 return d 1013 1014 781 1015 782 1016 class PubSubService(XMPPHandler, IQHandlerMixin): … … 808 1042 """ 809 1043 810 implements(IPubSubService )1044 implements(IPubSubService, disco.IDisco) 811 1045 812 1046 iqHandlers = { … … 844 1078 self.resource = resource 845 1079 self.discoIdentity = {'category': 'pubsub', 846 'type': ' generic',1080 'type': 'service', 847 1081 'name': 'Generic Publish-Subscribe Service'} 848 1082 … … 854 1088 855 1089 856 def getDiscoInfo(self, requestor, target, nodeIdentifier ):857 def toInfo(nodeInfo , info):1090 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): 1091 def toInfo(nodeInfo): 858 1092 if not nodeInfo: 859 return info1093 return 860 1094 861 1095 (nodeType, metaData) = nodeInfo['type'], nodeInfo['meta-data'] … … 877 1111 info.append(form) 878 1112 879 return info1113 return 880 1114 881 1115 info = [] … … 889 1123 getInfo = resource.getInfo 890 1124 else: 891 category, idType, name = self.discoIdentity 1125 category = self.discoIdentity['category'] 1126 idType = self.discoIdentity['type'] 1127 name = self.discoIdentity['name'] 892 1128 identity = disco.DiscoIdentity(category, idType, name) 893 1129 features = self.pubSubFeatures … … 900 1136 for feature in features]) 901 1137 902 d = getInfo(requestor, target, nodeIdentifier or '')903 d.addCallback(toInfo , info)1138 d = defer.maybeDeferred(getInfo, requestor, target, nodeIdentifier or '') 1139 d.addCallback(toInfo) 904 1140 d.addErrback(log.err) 1141 d.addCallback(lambda _: info) 905 1142 return d 906 1143 907 1144 908 def getDiscoItems(self, requestor, target, nodeIdentifier ):1145 def getDiscoItems(self, requestor, target, nodeIdentifier=''): 909 1146 if self.hideNodes: 910 1147 d = defer.succeed([]) … … 917 1154 else: 918 1155 d = defer.succeed([]) 919 920 921 1156 922 1157 d.addCallback(lambda nodes: [disco.DiscoItem(target, node) … … 948 1183 handler = getattr(resource, request.verb) 949 1184 except AttributeError: 950 # fix lookup feature951 1185 text = "Request verb: %s" % request.verb 952 1186 return defer.fail(Unsupported('', text)) … … 954 1188 d = handler(request) 955 1189 else: 956 handlerName, argNames = self._legacyHandlers[request.verb] 1190 try: 1191 handlerName, argNames = self._legacyHandlers[request.verb] 1192 except KeyError: 1193 text = "Request verb: %s" % request.verb 1194 return defer.fail(Unsupported('', text)) 1195 957 1196 handler = getattr(self, handlerName) 1197 958 1198 args = [getattr(request, arg) for arg in argNames] 1199 if 'options' in argNames: 1200 args[argNames.index('options')] = request.options.getValues() 1201 959 1202 d = handler(*args) 960 1203 … … 972 1215 def _toResponse_subscribe(self, result, resource, request): 973 1216 response = domish.Element((NS_PUBSUB, "pubsub")) 974 subscription = response.addElement("subscription") 975 if result.nodeIdentifier: 976 subscription["node"] = result.nodeIdentifier 977 subscription["jid"] = result.subscriber.full() 978 subscription["subscription"] = result.state 1217 response.addChild(result.toElement(NS_PUBSUB)) 979 1218 return response 980 1219 … … 984 1223 subscriptions = response.addElement('subscriptions') 985 1224 for subscription in result: 986 item = subscriptions.addElement('subscription') 987 item['node'] = subscription.nodeIdentifier 988 item['jid'] = subscription.subscriber.full() 989 item['subscription'] = subscription.state 1225 subscriptions.addChild(subscription.toElement(NS_PUBSUB)) 990 1226 return response 991 1227 … … 1013 1249 1014 1250 1015 def _makeFields(self, options, values):1016 fields = []1017 for name, value in values.iteritems():1018 if name not in options:1019 continue1020 1021 option = {'var': name}1022 option.update(options[name])1023 if isinstance(value, list):1024 option['values'] = value1025 else:1026 option['value'] = value1027 fields.append(data_form.Field.fromDict(option))1028 return fields1029 1030 1031 1251 def _formFromConfiguration(self, resource, values): 1032 options = resource.getConfigurationOptions() 1033 fields = self._makeFields(options, values) 1252 fieldDefs = resource.getConfigurationOptions() 1034 1253 form = data_form.Form(formType="form", 1035 formNamespace=NS_PUBSUB_NODE_CONFIG, 1036 fields=fields) 1037 1254 formNamespace=NS_PUBSUB_NODE_CONFIG) 1255 form.makeFields(values, fieldDefs) 1038 1256 return form 1039 1257 1040 1258 1041 def _checkConfiguration(self, resource, values): 1042 options = resource.getConfigurationOptions() 1043 processedValues = {} 1044 1045 for key, value in values.iteritems(): 1046 if key not in options: 1047 continue 1048 1049 option = {'var': key} 1050 option.update(options[key]) 1051 field = data_form.Field.fromDict(option) 1052 if isinstance(value, list): 1053 field.values = value 1054 else: 1055 field.value = value 1056 field.typeCheck() 1057 1058 if isinstance(value, list): 1059 processedValues[key] = field.values 1060 else: 1061 processedValues[key] = field.value 1062 1063 return processedValues 1259 def _checkConfiguration(self, resource, form): 1260 fieldDefs = resource.getConfigurationOptions() 1261 form.typeCheck(fieldDefs, filterUnknown=True) 1262 1263 1264 def _preProcess_create(self, resource, request): 1265 if request.options: 1266 self._checkConfiguration(resource, request.options) 1267 return request 1064 1268 1065 1269 … … 1092 1296 1093 1297 def _preProcess_configureSet(self, resource, request): 1094 if request.options: 1095 request.options = self._checkConfiguration(resource, 1096 request.options) 1298 if request.options.formType == 'cancel': 1299 return None 1300 else: 1301 self._checkConfiguration(resource, request.options) 1097 1302 return request 1098 else:1099 return None1100 1303 1101 1304 … … 1106 1309 1107 1310 for item in result: 1311 item.uri = NS_PUBSUB 1108 1312 items.addChild(item) 1109 1313 … … 1132 1336 1133 1337 return message 1338 1339 1340 def _toResponse_affiliationsGet(self, result, resource, request): 1341 response = domish.Element((NS_PUBSUB_OWNER, 'pubsub')) 1342 affiliations = response.addElement('affiliations') 1343 1344 if request.nodeIdentifier: 1345 affiliations['node'] = request.nodeIdentifier 1346 1347 for entity, affiliation in result.iteritems(): 1348 item = affiliations.addElement('affiliation') 1349 item['jid'] = entity.full() 1350 item['affiliation'] = affiliation 1351 1352 return response 1353 1134 1354 1135 1355 # public methods … … 1140 1360 nodeIdentifier, subscriber, 1141 1361 subscriptions) 1142 message.event.items.children = items 1362 for item in items: 1363 item.uri = NS_PUBSUB_EVENT 1364 message.event.items.addChild(item) 1143 1365 self.send(message) 1144 1366 -
wokkel/server.py
- Property exe set to *
r55 r96 1 1 # -*- test-case-name: wokkel.test.test_server -*- 2 2 # 3 # Copyright (c) 2003-2008 Ralph Meijer3 # Copyright (c) Ralph Meijer. 4 4 # See LICENSE for details. 5 5 -
wokkel/shim.py
- Property exe set to *
r27 r96 1 1 # -*- test-case-name: wokkel.test.test_shim -*- 2 2 # 3 # Copyright (c) 2003-2008 Ralph Meijer3 # Copyright (c) Ralph Meijer. 4 4 # See LICENSE for details. 5 5 -
wokkel/subprotocols.py
- Property exe set to *
r53 r101 1 1 # -*- test-case-name: wokkel.test.test_subprotocols -*- 2 2 # 3 # Copyright (c) 2001-2009Twisted Matrix Laboratories.3 # Copyright (c) Twisted Matrix Laboratories. 4 4 # See LICENSE for details. 5 5 … … 11 11 12 12 from twisted.internet import defer 13 from twisted.python import log 13 from twisted.internet.error import ConnectionDone 14 from twisted.python import failure, log 14 15 from twisted.words.protocols.jabber import error, xmlstream 15 16 from twisted.words.protocols.jabber.xmlstream import toResponse … … 92 93 """ 93 94 self.parent.send(obj) 95 96 97 def request(self, request): 98 """ 99 Send an IQ request and track the response. 100 101 This passes the request to the parent for sending and response 102 tracking. 103 104 @see: L{StreamManager.request}. 105 """ 106 return self.parent.request(request) 94 107 95 108 … … 156 169 @ivar _packetQueue: internal buffer of unsent data. See L{send} for details. 157 170 @type _packetQueue: L{list} 158 """ 171 @ivar timeout: Default IQ request timeout in seconds. 172 @type timeout: C{int} 173 @ivar _reactor: A provider of L{IReactorTime} to track timeouts. 174 """ 175 timeout = None 176 _reactor = None 159 177 160 178 logTraffic = False 161 179 162 def __init__(self, factory): 180 def __init__(self, factory, reactor=None): 181 """ 182 Construct a stream manager. 183 184 @param factory: The stream factory to connect with. 185 @param reactor: A provider of L{IReactorTime} to track timeouts. 186 If not provided, the global reactor will be used. 187 """ 163 188 XMPPHandlerCollection.__init__(self) 164 189 self.xmlstream = None … … 173 198 self.factory = factory 174 199 200 if reactor is None: 201 from twisted.internet import reactor 202 self._reactor = reactor 203 204 # Set up IQ response tracking 205 self._iqDeferreds = {} 206 175 207 176 208 def addHandler(self, handler): … … 185 217 # get protocol handler up to speed when a connection has already 186 218 # been established 187 if self.xmlstream and self._initialized:219 if self.xmlstream: 188 220 handler.makeConnection(self.xmlstream) 221 if self._initialized: 189 222 handler.connectionInitialized() 190 223 … … 210 243 self.xmlstream = xs 211 244 212 for e in self:245 for e in list(self): 213 246 e.makeConnection(xs) 214 247 … … 221 254 C{connectionInitialized} method. 222 255 """ 256 257 xs.addObserver('/iq[@type="result"]', self._onIQResponse) 258 xs.addObserver('/iq[@type="error"]', self._onIQResponse) 259 223 260 # Flush all pending packets 224 261 for p in self._packetQueue: … … 229 266 # Notify all child services which implement 230 267 # the IService interface 231 for e in self:268 for e in list(self): 232 269 e.connectionInitialized() 233 270 … … 247 284 248 285 249 def _disconnected(self, _):286 def _disconnected(self, reason): 250 287 """ 251 288 Called when the stream has been closed. … … 258 295 self._initialized = False 259 296 297 # Twisted versions before 11.0 passed an XmlStream here. 298 if not hasattr(reason, 'trap'): 299 reason = failure.Failure(ConnectionDone()) 300 260 301 # Notify all child services which implement 261 302 # the IService interface 262 for e in self: 263 e.connectionLost(None) 303 for e in list(self): 304 e.connectionLost(reason) 305 306 # This errbacks all deferreds of iq's for which no response has 307 # been received with a L{ConnectionLost} failure. Otherwise, the 308 # deferreds will never be fired. 309 iqDeferreds = self._iqDeferreds 310 self._iqDeferreds = {} 311 for d in iqDeferreds.itervalues(): 312 d.errback(reason) 313 314 315 def _onIQResponse(self, iq): 316 """ 317 Handle iq response by firing associated deferred. 318 """ 319 try: 320 d = self._iqDeferreds[iq["id"]] 321 except KeyError: 322 return 323 324 del self._iqDeferreds[iq["id"]] 325 iq.handled = True 326 if iq['type'] == 'error': 327 d.errback(error.exceptionFromStanza(iq)) 328 else: 329 d.callback(iq) 264 330 265 331 … … 278 344 else: 279 345 self._packetQueue.append(obj) 346 347 348 def request(self, request): 349 """ 350 Send an IQ request and track the response. 351 352 A request is an IQ L{generic.Stanza} of type C{'get'} or C{'set'}. It 353 will have its C{toElement} called to render to a L{domish.Element} 354 which is then sent out over the current stream. If there is no such 355 stream (yet), it is queued and sent whenever a connection is 356 established and initialized, just like L{send}. 357 358 If the request doesn't have an identifier, it will be assigned a fresh 359 one, so the response can be tracked. 360 361 The deferred that is returned will fire with the L{domish.Element} 362 representation of the response if it is a result iq. If the response 363 is an error iq, a corresponding L{error.StanzaError} will be errbacked. 364 365 If the connection is closed before a response was received, the deferred 366 will be errbacked with the reason failure. 367 368 A request may also have a timeout, either by setting a default timeout 369 in L{StreamManager.timeout} or on the C{timeout} attribute of the 370 request. 371 372 @param request: The IQ request. 373 @type request: L{generic.Request} 374 """ 375 if (request.stanzaKind != 'iq' or 376 request.stanzaType not in ('get', 'set')): 377 return defer.fail(ValueError("Not a request")) 378 379 element = request.toElement() 380 381 # Make sure we have a trackable id on the stanza 382 if not request.stanzaID: 383 element.addUniqueId() 384 request.stanzaID = element['id'] 385 386 # Set up iq response tracking 387 d = defer.Deferred() 388 self._iqDeferreds[element['id']] = d 389 390 timeout = getattr(request, 'timeout', self.timeout) 391 392 if timeout is not None: 393 def onTimeout(): 394 del self._iqDeferreds[element['id']] 395 d.errback(xmlstream.TimeoutError("IQ timed out")) 396 397 call = self._reactor.callLater(timeout, onTimeout) 398 399 def cancelTimeout(result): 400 if call.active(): 401 call.cancel() 402 403 return result 404 405 d.addBoth(cancelTimeout) 406 self.send(element) 407 return d 280 408 281 409 -
wokkel/test/__init__.py
- Property exe set to *
r1 r96 1 # Copyright (c) 2003-2007 Ralph Meijer1 # Copyright (c) Ralph Meijer. 2 2 # See LICENSE for details. 3 3 -
wokkel/test/helpers.py
- Property exe set to *
r46 r96 1 # Copyright (c) 2003-2008 Ralph Meijer1 # Copyright (c) Ralph Meijer. 2 2 # See LICENSE for details. 3 3 -
wokkel/test/test_client.py
- Property exe set to *
r73 r96 1 # Copyright (c) 2003-2009 Ralph Meijer1 # Copyright (c) Ralph Meijer. 2 2 # See LICENSE for details. 3 3 -
wokkel/test/test_compat.py
- Property exe set to *
r63 r96 1 1 # Copyright (c) 2001-2008 Twisted Matrix Laboratories. 2 # Copyright (c) 2008-2009 Ralph Meijer2 # Copyright (c) Ralph Meijer. 3 3 # See LICENSE for details. 4 4 -
wokkel/test/test_component.py
- Property exe set to *
r54 r96 1 # Copyright (c) 2003-2008 Ralph Meijer1 # Copyright (c) Ralph Meijer. 2 2 # See LICENSE for details. 3 3 … … 369 369 xs = self.xmlstream 370 370 xs.addOnetimeObserver(xmlstream.STREAM_AUTHD_EVENT, authenticated) 371 xs.sid = '1234'371 xs.sid = u'1234' 372 372 theHash = '32532c0f7dbf1253c095b18b18e36d38d94c1256' 373 373 xs.authenticator.onHandshake(theHash) … … 390 390 xs.sendStreamError = streamErrors.append 391 391 392 xs.sid = '1234'392 xs.sid = u'1234' 393 393 theHash = '1234' 394 394 xs.authenticator.onHandshake(theHash) -
wokkel/test/test_data_form.py
- Property exe set to *
r56 r96 1 # Copyright (c) 2003-2009 Ralph Meijer1 # Copyright (c) Ralph Meijer. 2 2 # See LICENSE for details. 3 3 … … 20 20 21 21 def test_toElement(self): 22 """ 23 An option is an option element with a value child with the option value. 24 """ 25 option = data_form.Option('value') 26 element = option.toElement() 27 28 self.assertEqual('option', element.name) 29 self.assertEqual(NS_X_DATA, element.uri) 30 self.assertEqual(NS_X_DATA, element.value.uri) 31 self.assertEqual('value', unicode(element.value)) 32 self.assertFalse(element.hasAttribute('label')) 33 34 35 def test_toElementLabel(self): 36 """ 37 A label is rendered as an attribute on the option element. 38 """ 22 39 option = data_form.Option('value', 'label') 23 40 element = option.toElement() 24 self.assertEquals('option', element.name) 25 self.assertEquals(NS_X_DATA, element.uri) 26 self.assertEquals('label', element['label']) 27 self.assertEquals('value', element.value.name) 28 self.assertEquals(NS_X_DATA, element.value.uri) 29 self.assertEquals('value', unicode(element.value)) 41 42 self.assertEqual('option', element.name) 43 self.assertEqual(NS_X_DATA, element.uri) 44 self.assertEqual(NS_X_DATA, element.value.uri) 45 self.assertEqual('value', unicode(element.value)) 46 self.assertEqual('label', element['label']) 47 48 49 def test_fromElement(self): 50 """ 51 An option has a child element with the option value. 52 """ 53 element = domish.Element((NS_X_DATA, 'option')) 54 element.addElement('value', content='value') 55 option = data_form.Option.fromElement(element) 56 57 self.assertEqual('value', option.value) 58 self.assertIdentical(None, option.label) 59 60 61 def test_fromElementLabel(self): 62 """ 63 An option label is an attribute on the option element. 64 """ 65 66 element = domish.Element((NS_X_DATA, 'option')) 67 element.addElement('value', content='value') 68 element['label'] = 'label' 69 option = data_form.Option.fromElement(element) 70 71 self.assertEqual('label', option.label) 72 73 74 def test_fromElementNoValue(self): 75 """ 76 An option MUST have a value. 77 """ 78 element = domish.Element((NS_X_DATA, 'option')) 79 self.assertRaises(data_form.Error, 80 data_form.Option.fromElement, element) 81 82 83 def test_repr(self): 84 """ 85 The representation of an Option is equal to how it is created. 86 """ 87 option = data_form.Option('value', 'label') 88 self.assertEqual("""Option('value', 'label')""", repr(option)) 30 89 31 90 … … 43 102 self.assertEqual('text-single', field.fieldType) 44 103 self.assertEqual('test', field.var) 104 105 106 def test_labelAndOptions(self): 107 """ 108 The label should be set, even if there are options with labels as dict. 109 """ 110 field = data_form.Field(label='test', 111 options={'test2': 'test 2', 'test3': 'test 3'}) 112 self.assertEqual('test', field.label) 113 114 115 def test_repr(self): 116 """ 117 The repr of a field should be equal to its initialization. 118 """ 119 field = data_form.Field('list-single', var='test', label='label', 120 desc='desc', required=True, value='test', 121 options=[data_form.Option('test')]) 122 self.assertEqual("""Field(fieldType='list-single', """ 123 """var='test', label='label', """ 124 """desc='desc', required=True, """ 125 """values=['test'], """ 126 """options=[Option('test')])""", 127 repr(field)) 45 128 46 129 … … 61 144 62 145 63 def test_toElementTypeNot ListSingle(self):64 """ 65 Always render the field type, if different from list-single.146 def test_toElementTypeNotTextSingle(self): 147 """ 148 Always render the field type, if different from text-single. 66 149 """ 67 150 field = data_form.Field('hidden', var='test') … … 69 152 70 153 self.assertEquals('hidden', element.getAttribute('type')) 154 155 156 def test_toElementSingleValue(self): 157 """ 158 A single value should yield only one value element. 159 """ 160 field = data_form.Field('list-multi', var='test', value='test') 161 element = field.toElement() 162 163 children = list(element.elements()) 164 self.assertEqual(1, len(children)) 165 166 167 def test_toElementMultipleValues(self): 168 """ 169 A field with no type and multiple values should render all values. 170 """ 171 field = data_form.Field('list-multi', var='test', 172 values=['test', 'test2']) 173 element = field.toElement() 174 175 children = list(element.elements()) 176 self.assertEqual(2, len(children)) 71 177 72 178 … … 131 237 132 238 def test_toElementJID(self): 239 """ 240 A JID value should render to text. 241 """ 133 242 field = data_form.Field(fieldType='jid-single', var='test', 134 243 value=jid.JID(u'test@example.org')) 135 244 element = field.toElement() 136 245 self.assertEqual(u'test@example.org', unicode(element.value)) 246 247 248 def test_toElementJIDTextSingle(self): 249 """ 250 A JID value should render to text if field type is text-single. 251 """ 252 field = data_form.Field(fieldType='text-single', var='test', 253 value=jid.JID(u'test@example.org')) 254 element = field.toElement() 255 self.assertEqual(u'test@example.org', unicode(element.value)) 256 257 258 def test_toElementBoolean(self): 259 """ 260 A boolean value should render to text. 261 """ 262 field = data_form.Field(fieldType='boolean', var='test', 263 value=True) 264 element = field.toElement() 265 self.assertEqual(u'true', unicode(element.value)) 266 267 268 def test_toElementBooleanTextSingle(self): 269 """ 270 A boolean value should render to text if the field type is text-single. 271 """ 272 field = data_form.Field(var='test', value=True) 273 element = field.toElement() 274 self.assertEqual(u'true', unicode(element.value)) 275 276 277 def test_toElementNoType(self): 278 """ 279 A field with no type should not have a type attribute. 280 """ 281 field = data_form.Field(None, var='test', value='test') 282 element = field.toElement() 283 self.assertFalse(element.hasAttribute('type')) 284 285 286 def test_toElementNoTypeMultipleValues(self): 287 """ 288 A field with no type and multiple values should render all values. 289 """ 290 field = data_form.Field(None, var='test', values=['test', 'test2']) 291 element = field.toElement() 292 293 self.assertFalse(element.hasAttribute('type')) 294 children = list(element.elements()) 295 self.assertEqual(2, len(children)) 137 296 138 297 … … 251 410 self.assertEquals(u'user@example.org', field.value) 252 411 412 253 413 def test_fromElementValueJIDMalformed(self): 254 414 """ … … 276 436 277 437 438 def test_fromElementDesc(self): 439 """ 440 Field descriptions are in a desc child element. 441 """ 442 element = domish.Element((NS_X_DATA, 'field')) 443 element.addElement('desc', content=u'My description') 444 field = data_form.Field.fromElement(element) 445 self.assertEqual(u'My description', field.desc) 446 447 448 def test_fromElementOption(self): 449 """ 450 Field descriptions are in a desc child element. 451 """ 452 element = domish.Element((NS_X_DATA, 'field')) 453 element.addElement('option').addElement('value', content=u'option1') 454 element.addElement('option').addElement('value', content=u'option2') 455 field = data_form.Field.fromElement(element) 456 self.assertEqual(2, len(field.options)) 457 458 459 def test_fromElementRequired(self): 460 """ 461 Required fields have a required child element. 462 """ 463 element = domish.Element((NS_X_DATA, 'field')) 464 element.addElement('required') 465 field = data_form.Field.fromElement(element) 466 self.assertTrue(field.required) 467 468 469 def test_fromElementChildOtherNamespace(self): 470 """ 471 Child elements from another namespace are ignored. 472 """ 473 element = domish.Element((NS_X_DATA, 'field')) 474 element['var'] = 'test' 475 child = element.addElement(('myns', 'value')) 476 field = data_form.Field.fromElement(element) 477 478 self.assertIdentical(None, field.value) 479 480 481 def test_fromDict(self): 482 """ 483 A named field with a value can be created by providing a dictionary. 484 """ 485 fieldDict = {'var': 'test', 'value': 'text'} 486 field = data_form.Field.fromDict(fieldDict) 487 self.assertEqual('test', field.var) 488 self.assertEqual('text', field.value) 489 490 491 def test_fromDictFieldType(self): 492 """ 493 The field type is set using the key 'type'. 494 """ 495 fieldDict = {'type': 'boolean'} 496 field = data_form.Field.fromDict(fieldDict) 497 self.assertEqual('boolean', field.fieldType) 498 499 500 def test_fromDictOptions(self): 501 """ 502 The field options are set using the key 'options'. 503 504 The options are represented as a dictionary keyed by option, 505 with the optional label as value. 506 """ 507 fieldDict = {'options': {'value1': 'label1', 508 'value2': 'label2'}} 509 field = data_form.Field.fromDict(fieldDict) 510 self.assertEqual(2, len(field.options)) 511 options = {} 512 for option in field.options: 513 options[option.value] = option.label 514 515 self.assertEqual(options, fieldDict['options']) 516 278 517 279 518 class FormTest(unittest.TestCase): … … 289 528 form = data_form.Form('result') 290 529 self.assertEqual('result', form.formType) 530 291 531 292 532 def test_toElement(self): … … 304 544 305 545 546 def test_toElementTitle(self): 547 """ 548 A title is rendered as a child element with the title as CDATA. 549 """ 550 form = data_form.Form('form', title='Bot configuration') 551 element = form.toElement() 552 553 elements = list(element.elements()) 554 self.assertEqual(1, len(elements)) 555 title = elements[0] 556 self.assertEqual('title', title.name) 557 self.assertEqual(NS_X_DATA, title.uri) 558 self.assertEqual('Bot configuration', unicode(title)) 559 560 561 def test_toElementInstructions(self): 562 """ 563 Instructions are rendered as child elements with CDATA. 564 """ 565 form = data_form.Form('form', instructions=['Fill out this form!']) 566 element = form.toElement() 567 568 elements = list(element.elements()) 569 self.assertEqual(1, len(elements)) 570 instructions = elements[0] 571 self.assertEqual('instructions', instructions.name) 572 self.assertEqual(NS_X_DATA, instructions.uri) 573 self.assertEqual('Fill out this form!', unicode(instructions)) 574 575 576 def test_toElementInstructionsMultiple(self): 577 """ 578 Instructions render as one element per instruction, in order. 579 """ 580 form = data_form.Form('form', instructions=['Fill out this form!', 581 'no really']) 582 element = form.toElement() 583 584 elements = list(element.elements()) 585 self.assertEqual(2, len(elements)) 586 instructions1 = elements[0] 587 instructions2 = elements[1] 588 self.assertEqual('instructions', instructions1.name) 589 self.assertEqual(NS_X_DATA, instructions1.uri) 590 self.assertEqual('Fill out this form!', unicode(instructions1)) 591 self.assertEqual('instructions', instructions2.name) 592 self.assertEqual(NS_X_DATA, instructions2.uri) 593 self.assertEqual('no really', unicode(instructions2)) 594 595 596 def test_toElementFormType(self): 597 """ 598 The form type is rendered as a hidden field with name FORM_TYPE. 599 """ 600 form = data_form.Form('form', formNamespace='jabber:bot') 601 element = form.toElement() 602 603 elements = list(element.elements()) 604 self.assertEqual(1, len(elements)) 605 formTypeField = elements[0] 606 self.assertEqual('field', formTypeField.name) 607 self.assertEqual(NS_X_DATA, formTypeField.uri) 608 self.assertEqual('FORM_TYPE', formTypeField['var']) 609 self.assertEqual('hidden', formTypeField['type']) 610 self.assertEqual('jabber:bot', unicode(formTypeField.value)) 611 612 613 def test_toElementFields(self): 614 """ 615 Fields are rendered as child elements, in order. 616 """ 617 fields = [data_form.Field('fixed', value='Section 1'), 618 data_form.Field('text-single', 619 var='botname', 620 label='The name of your bot'), 621 data_form.Field('text-multi', 622 var='description', 623 label='Helpful description of your bot'), 624 data_form.Field('boolean', 625 var='public', 626 label='Public bot?', 627 required=True) 628 ] 629 form = data_form.Form('form', fields=fields) 630 element = form.toElement() 631 632 elements = list(element.elements()) 633 self.assertEqual(4, len(elements)) 634 for field in elements: 635 self.assertEqual('field', field.name) 636 self.assertEqual(NS_X_DATA, field.uri) 637 638 # Check order 639 self.assertEqual('fixed', elements[0]['type']) 640 self.assertEqual('botname', elements[1]['var']) 641 self.assertEqual('description', elements[2]['var']) 642 self.assertEqual('public', elements[3]['var']) 643 644 306 645 def test_fromElement(self): 307 646 """ … … 348 687 349 688 self.assertEquals(['instruction'], form.instructions) 689 350 690 351 691 def test_fromElementInstructions2(self): … … 378 718 self.assertIn('field2', form.fields) 379 719 self.assertEquals('field2', form.fieldList[1].var) 720 721 722 def test_fromElementFormType(self): 723 """ 724 The form type is a hidden field named FORM_TYPE. 725 """ 726 element = domish.Element((NS_X_DATA, 'x')) 727 field = element.addElement('field') 728 field['var'] = 'FORM_TYPE' 729 field['type'] = 'hidden' 730 field.addElement('value', content='myns') 731 form = data_form.Form.fromElement(element) 732 733 self.assertNotIn('FORM_TYPE', form.fields) 734 self.assertEqual('myns', form.formNamespace) 735 736 def test_fromElementFormTypeNotHidden(self): 737 """ 738 A non-hidden field named FORM_TYPE does not set the form type. 739 """ 740 element = domish.Element((NS_X_DATA, 'x')) 741 field = element.addElement('field') 742 field['var'] = 'FORM_TYPE' 743 field.addElement('value', content='myns') 744 form = data_form.Form.fromElement(element) 745 746 self.assertIn('FORM_TYPE', form.fields) 747 self.assertIdentical(None, form.formNamespace) 748 749 750 def test_fromElementChildOtherNamespace(self): 751 """ 752 Child elements from another namespace are ignored. 753 """ 754 element = domish.Element((NS_X_DATA, 'x')) 755 element['type'] = 'result' 756 field = element.addElement(('myns', 'field')) 757 field['var'] = 'test' 758 form = data_form.Form.fromElement(element) 759 760 self.assertEqual(0, len(form.fields)) 761 762 763 def test_repr(self): 764 """ 765 The repr of a form should be equal to its initialization. 766 """ 767 form = data_form.Form('form', title='title', instructions=['instr'], 768 formNamespace='myns', 769 fields=[data_form.Field('fixed', 770 value='test')]) 771 self.assertEqual("""Form(formType='form', title='title', """ 772 """instructions=['instr'], formNamespace='myns', """ 773 """fields=[Field(fieldType='fixed', """ 774 """values=['test'])])""", 775 repr(form)) 776 777 778 def test_addField(self): 779 """ 780 A field should occur in fieldList. 781 """ 782 form = data_form.Form('result') 783 field = data_form.Field('fixed', value='Section 1') 784 form.addField(field) 785 self.assertEqual([field], form.fieldList) 786 787 788 def test_addFieldTwice(self): 789 """ 790 Fields occur in fieldList in the order they were added. 791 """ 792 form = data_form.Form('result') 793 field1 = data_form.Field('fixed', value='Section 1') 794 field2 = data_form.Field('fixed', value='Section 2') 795 form.addField(field1) 796 form.addField(field2) 797 self.assertEqual([field1, field2], form.fieldList) 798 799 800 def test_addFieldNotNamed(self): 801 """ 802 A non-named field should not occur in fields. 803 """ 804 form = data_form.Form('result') 805 field = data_form.Field('fixed', value='Section 1') 806 form.addField(field) 807 self.assertEqual({}, form.fields) 808 809 810 def test_addFieldNamed(self): 811 """ 812 A named field should occur in fields. 813 """ 814 form = data_form.Form('result') 815 field = data_form.Field(var='test') 816 form.addField(field) 817 self.assertEqual({'test': field}, form.fields) 818 819 820 def test_addFieldTwiceNamed(self): 821 """ 822 A second named field should occur in fields. 823 """ 824 form = data_form.Form('result') 825 field1 = data_form.Field(var='test') 826 field2 = data_form.Field(var='test2') 827 form.addField(field2) 828 form.addField(field1) 829 self.assertEqual({'test': field1, 'test2': field2}, form.fields) 830 831 832 def test_addFieldSameName(self): 833 """ 834 A named field cannot occur twice. 835 """ 836 form = data_form.Form('result') 837 field1 = data_form.Field(var='test', value='value') 838 field2 = data_form.Field(var='test', value='value2') 839 form.addField(field1) 840 self.assertRaises(data_form.Error, form.addField, field2) 841 842 843 def test_removeField(self): 844 """ 845 A removed field should not occur in fieldList. 846 """ 847 form = data_form.Form('result') 848 field = data_form.Field('fixed', value='Section 1') 849 form.addField(field) 850 form.removeField(field) 851 self.assertNotIn(field, form.fieldList) 852 853 854 def test_removeFieldNamed(self): 855 """ 856 A removed named field should not occur in fields. 857 """ 858 form = data_form.Form('result') 859 field = data_form.Field(var='test', value='test1') 860 form.addField(field) 861 form.removeField(field) 862 self.assertNotIn('test', form.fields) 863 864 865 def test_makeField(self): 866 """ 867 Fields can be created from a dict of values and a dict of field defs. 868 """ 869 fieldDefs = { 870 "pubsub#persist_items": 871 {"type": "boolean", 872 "label": "Persist items to storage"}, 873 "pubsub#deliver_payloads": 874 {"type": "boolean", 875 "label": "Deliver payloads with event notifications"}, 876 "pubsub#creator": 877 {"type": "jid-single", 878 "label": "The JID of the node creator"}, 879 "pubsub#description": 880 {"type": "text-single", 881 "label": "A description of the node"}, 882 "pubsub#owner": 883 {"type": "jid-single", 884 "label": "Owner of the node"}, 885 } 886 values = {'pubsub#deliver_payloads': '0', 887 'pubsub#persist_items': True, 888 'pubsub#description': 'a great node', 889 'pubsub#owner': jid.JID('user@example.org'), 890 'x-myfield': ['a', 'b']} 891 892 form = data_form.Form('submit') 893 form.makeFields(values, fieldDefs) 894 895 # Check that the expected fields have been created 896 self.assertIn('pubsub#deliver_payloads', form.fields) 897 self.assertIn('pubsub#persist_items', form.fields) 898 self.assertIn('pubsub#description', form.fields) 899 self.assertIn('pubsub#owner', form.fields) 900 901 # This field is not created because there is no value for it. 902 self.assertNotIn('pubsub#creator', form.fields) 903 904 # This field is not created because it does not appear in fieldDefs 905 # and filterUnknown defaults to True 906 self.assertNotIn('x-myfield', form.fields) 907 908 # Check properties the created fields 909 self.assertEqual('boolean', 910 form.fields['pubsub#deliver_payloads'].fieldType) 911 self.assertEqual('0', 912 form.fields['pubsub#deliver_payloads'].value) 913 self.assertEqual('Deliver payloads with event notifications', 914 form.fields['pubsub#deliver_payloads'].label) 915 self.assertEqual(True, 916 form.fields['pubsub#persist_items'].value) 917 918 919 def test_makeFieldNotFilterUnknown(self): 920 """ 921 Fields can be created from a dict of values and a dict of field defs. 922 """ 923 fieldDefs = { 924 "pubsub#persist_items": 925 {"type": "boolean", 926 "label": "Persist items to storage"}, 927 } 928 values = {'x-myfield': ['a', 'b']} 929 930 form = data_form.Form('submit') 931 form.makeFields(values, fieldDefs, filterUnknown=False) 932 933 field = form.fields['x-myfield'] 934 self.assertEqual(None, field.fieldType) 935 self.assertEqual(values, form.getValues()) 936 937 938 def test_makeFieldsUnknownTypeJID(self): 939 """ 940 Without type, a single JID value sets field type jid-single. 941 """ 942 values = {'pubsub#creator': jid.JID('user@example.org')} 943 form = data_form.Form('result') 944 form.makeFields(values) 945 946 field = form.fields['pubsub#creator'] 947 self.assertEqual(None, field.fieldType) 948 self.assertEqual(values, form.getValues()) 949 950 951 def test_makeFieldsUnknownTypeJIDMulti(self): 952 """ 953 Without type, multiple JID values sets field type jid-multi. 954 """ 955 values = {'pubsub#contact': [jid.JID('user@example.org'), 956 jid.JID('other@example.org')]} 957 form = data_form.Form('result') 958 form.makeFields(values) 959 960 field = form.fields['pubsub#contact'] 961 self.assertEqual(None, field.fieldType) 962 self.assertEqual(values, form.getValues()) 963 964 965 def test_makeFieldsUnknownTypeBoolean(self): 966 """ 967 Without type, a boolean value sets field type boolean. 968 """ 969 values = {'pubsub#persist_items': True} 970 form = data_form.Form('result') 971 form.makeFields(values) 972 973 field = form.fields['pubsub#persist_items'] 974 self.assertEqual(None, field.fieldType) 975 self.assertEqual(values, form.getValues()) 976 977 978 def test_makeFieldsUnknownTypeListMulti(self): 979 """ 980 Without type, multiple values sets field type list-multi. 981 """ 982 values = {'pubsub#show-values': ['chat', 'online', 'away']} 983 form = data_form.Form('result') 984 form.makeFields(values) 985 986 field = form.fields['pubsub#show-values'] 987 self.assertEqual(None, field.fieldType) 988 self.assertEqual(values, form.getValues()) 989 990 991 def test_getValues(self): 992 """ 993 Each named field is represented in the values, keyed by name. 994 """ 995 fields = [data_form.Field(var='botname', value='The Jabber Bot'), 996 data_form.Field('boolean', var='public', value=True), 997 data_form.Field('list-multi', var='features', 998 values=['news', 'search'])] 999 form = data_form.Form('submit', fields=fields) 1000 values = form.getValues() 1001 self.assertEqual({'botname': 'The Jabber Bot', 1002 'public': True, 1003 'features': ['news', 'search']}, 1004 values) 1005 1006 1007 def test_getValuesOneValueTypeMulti(self): 1008 """ 1009 A single value for a multi-value field type is returned in a list. 1010 """ 1011 fields = [data_form.Field('list-multi', var='features', 1012 values=['news'])] 1013 form = data_form.Form('submit', fields=fields) 1014 values = form.getValues() 1015 self.assertEqual({'features': ['news']}, values) 1016 1017 1018 def test_getValuesMultipleValuesNoType(self): 1019 """ 1020 Multiple values for a field without type are returned in a list. 1021 """ 1022 fields = [data_form.Field(None, var='features', 1023 values=['news', 'search'])] 1024 form = data_form.Form('submit', fields=fields) 1025 values = form.getValues() 1026 self.assertEqual({'features': ['news', 'search']}, values) 1027 1028 1029 def test_getValuesMultipleValuesTypeSingle(self): 1030 """ 1031 Multiple values for a single-value field type returns the first value. 1032 """ 1033 fields = [data_form.Field('text-single', var='features', 1034 values=['news', 'search'])] 1035 form = data_form.Form('submit', fields=fields) 1036 values = form.getValues() 1037 self.assertEqual({'features': 'news'}, values) 1038 1039 1040 def test_typeCheckKnownFieldChecked(self): 1041 """ 1042 Known fields are type checked. 1043 """ 1044 checked = [] 1045 fieldDefs = {"pubsub#description": 1046 {"type": "text-single", 1047 "label": "A description of the node"}} 1048 form = data_form.Form('submit') 1049 form.addField(data_form.Field(var='pubsub#description', 1050 value='a node')) 1051 field = form.fields['pubsub#description'] 1052 field.typeCheck = lambda : checked.append(None) 1053 form.typeCheck(fieldDefs) 1054 1055 self.assertEqual([None], checked) 1056 1057 1058 def test_typeCheckKnownFieldNoType(self): 1059 """ 1060 Known fields without a type get the type of the field definition. 1061 """ 1062 checked = [] 1063 fieldDefs = {"pubsub#description": 1064 {"type": "text-single", 1065 "label": "A description of the node"}} 1066 form = data_form.Form('submit') 1067 form.addField(data_form.Field(None, var='pubsub#description', 1068 value='a node')) 1069 field = form.fields['pubsub#description'] 1070 field.typeCheck = lambda : checked.append(None) 1071 form.typeCheck(fieldDefs) 1072 1073 self.assertEqual('text-single', field.fieldType) 1074 self.assertEqual([None], checked) 1075 1076 1077 def test_typeCheckWrongFieldType(self): 1078 """ 1079 A field should have the same type as the field definition. 1080 """ 1081 checked = [] 1082 fieldDefs = {"pubsub#description": 1083 {"type": "text-single", 1084 "label": "A description of the node"}} 1085 form = data_form.Form('submit') 1086 form.addField(data_form.Field('list-single', var='pubsub#description', 1087 value='a node')) 1088 field = form.fields['pubsub#description'] 1089 field.typeCheck = lambda : checked.append(None) 1090 1091 self.assertRaises(TypeError, form.typeCheck, fieldDefs) 1092 self.assertEqual([], checked) 1093 1094 1095 def test_typeCheckDefaultTextSingle(self): 1096 """ 1097 If a field definition has no type, use text-single. 1098 """ 1099 checked = [] 1100 fieldDefs = {"pubsub#description": 1101 {"label": "A description of the node"}} 1102 form = data_form.Form('submit') 1103 form.addField(data_form.Field('text-single', var='pubsub#description', 1104 value='a node')) 1105 field = form.fields['pubsub#description'] 1106 field.typeCheck = lambda : checked.append(None) 1107 form.typeCheck(fieldDefs) 1108 1109 self.assertEqual([None], checked) 1110 1111 1112 def test_typeCheckUnknown(self): 1113 """ 1114 Unknown fields are checked, not removed if filterUnknown False. 1115 """ 1116 checked = [] 1117 fieldDefs = {} 1118 form = data_form.Form('submit') 1119 form.addField(data_form.Field('list-single', var='pubsub#description', 1120 value='a node')) 1121 field = form.fields['pubsub#description'] 1122 field.typeCheck = lambda : checked.append(None) 1123 form.typeCheck(fieldDefs, filterUnknown=False) 1124 1125 self.assertIn('pubsub#description', form.fields) 1126 self.assertEqual([None], checked) 1127 1128 1129 def test_typeCheckUnknownNoType(self): 1130 """ 1131 Unknown fields without type are not checked. 1132 """ 1133 checked = [] 1134 fieldDefs = {} 1135 form = data_form.Form('submit') 1136 form.addField(data_form.Field(None, var='pubsub#description', 1137 value='a node')) 1138 field = form.fields['pubsub#description'] 1139 field.typeCheck = lambda : checked.append(None) 1140 form.typeCheck(fieldDefs, filterUnknown=False) 1141 1142 self.assertIn('pubsub#description', form.fields) 1143 self.assertEqual([], checked) 1144 1145 1146 def test_typeCheckUnknownRemoved(self): 1147 """ 1148 Unknown fields are not checked, and removed if filterUnknown True. 1149 """ 1150 checked = [] 1151 fieldDefs = {} 1152 form = data_form.Form('submit') 1153 form.addField(data_form.Field('list-single', var='pubsub#description', 1154 value='a node')) 1155 field = form.fields['pubsub#description'] 1156 field.typeCheck = lambda : checked.append(None) 1157 form.typeCheck(fieldDefs, filterUnknown=True) 1158 1159 self.assertNotIn('pubsub#description', form.fields) 1160 self.assertEqual([], checked) 1161 1162 1163 1164 class FindFormTest(unittest.TestCase): 1165 """ 1166 Tests for L{data_form.findForm}. 1167 """ 1168 1169 def test_findForm(self): 1170 element = domish.Element((None, 'test')) 1171 theForm = data_form.Form('submit', formNamespace='myns') 1172 element.addChild(theForm.toElement()) 1173 form = data_form.findForm(element, 'myns') 1174 self.assertEqual('myns', form.formNamespace) 1175 1176 1177 def test_noFormType(self): 1178 element = domish.Element((None, 'test')) 1179 otherForm = data_form.Form('submit') 1180 element.addChild(otherForm.toElement()) 1181 form = data_form.findForm(element, 'myns') 1182 self.assertIdentical(None, form) 1183 1184 1185 def test_noFormTypeCancel(self): 1186 """ 1187 Cancelled forms don't have a FORM_TYPE field, the first is returned. 1188 """ 1189 element = domish.Element((None, 'test')) 1190 cancelledForm = data_form.Form('cancel') 1191 element.addChild(cancelledForm.toElement()) 1192 form = data_form.findForm(element, 'myns') 1193 self.assertEqual('cancel', form.formType) 1194 1195 1196 def test_otherFormType(self): 1197 """ 1198 Forms with other FORM_TYPEs are ignored. 1199 """ 1200 element = domish.Element((None, 'test')) 1201 otherForm = data_form.Form('submit', formNamespace='otherns') 1202 element.addChild(otherForm.toElement()) 1203 form = data_form.findForm(element, 'myns') 1204 self.assertIdentical(None, form) 1205 1206 1207 def test_otherFormTypeCancel(self): 1208 """ 1209 Cancelled forms with another FORM_TYPE are ignored. 1210 """ 1211 element = domish.Element((None, 'test')) 1212 cancelledForm = data_form.Form('cancel', formNamespace='otherns') 1213 element.addChild(cancelledForm.toElement()) 1214 form = data_form.findForm(element, 'myns') 1215 self.assertIdentical(None, form) 1216 1217 1218 def test_noElement(self): 1219 """ 1220 When None is passed as element, None is returned. 1221 """ 1222 element = None 1223 form = data_form.findForm(element, 'myns') 1224 self.assertIdentical(None, form) 1225 1226 1227 def test_noForm(self): 1228 """ 1229 When no child element is a form, None is returned. 1230 """ 1231 element = domish.Element((None, 'test')) 1232 form = data_form.findForm(element, 'myns') 1233 self.assertIdentical(None, form) 1234 def test_typeCheckNoFieldDefs(self): 1235 """ 1236 If there are no field defs, an empty dictionary is assumed. 1237 """ 1238 checked = [] 1239 form = data_form.Form('submit') 1240 form.addField(data_form.Field('list-single', var='pubsub#description', 1241 value='a node')) 1242 field = form.fields['pubsub#description'] 1243 field.typeCheck = lambda : checked.append(None) 1244 form.typeCheck() 1245 1246 self.assertIn('pubsub#description', form.fields) 1247 self.assertEqual([None], checked) -
wokkel/test/test_disco.py
- Property exe set to *
r67 r103 1 # Copyright (c) 2003-2009 Ralph Meijer1 # Copyright (c) Ralph Meijer. 2 2 # See LICENSE for details. 3 3 … … 10 10 from twisted.internet import defer 11 11 from twisted.trial import unittest 12 from twisted.words.protocols.jabber.error import StanzaError 12 13 from twisted.words.protocols.jabber.jid import JID 13 14 from twisted.words.protocols.jabber.xmlstream import toResponse 14 from twisted.words.xish import domish 15 from twisted.words.xish import domish, utility 15 16 16 17 from wokkel import data_form, disco … … 477 478 """ 478 479 self.stub = XmlStreamStub() 480 self.patch(XMPPHandler, 'request', self.request) 479 481 self.protocol = disco.DiscoClientProtocol() 480 self.protocol.xmlstream = self.stub.xmlstream 481 self.protocol.connectionInitialized() 482 483 484 def request(self, request): 485 element = request.toElement() 486 self.stub.xmlstream.send(element) 487 return defer.Deferred() 482 488 483 489 … … 511 517 element[u'jid'] = u"test2.example.org" 512 518 513 self.stub.send(response)519 d.callback(response) 514 520 return d 515 521 … … 527 533 response = toResponse(iq, u'result') 528 534 response.addElement((NS_DISCO_ITEMS, u'query')) 529 self.stub.send(response) 530 535 536 d.callback(response) 531 537 return d 532 538 … … 566 572 element[u'var'] = u'http://jabber.org/protocol/muc' 567 573 568 self.stub.send(response)574 d.callback(response) 569 575 return d 570 576 … … 575 581 """ 576 582 d = self.protocol.requestInfo(JID(u'example.org'), 577 583 sender=JID(u'test.example.org')) 578 584 579 585 iq = self.stub.output[-1] … … 582 588 response = toResponse(iq, u'result') 583 589 response.addElement((NS_DISCO_INFO, u'query')) 584 self.stub.send(response) 585 590 591 d.callback(response) 586 592 return d 587 593 … … 595 601 def setUp(self): 596 602 self.service = disco.DiscoHandler() 603 604 605 def test_connectionInitializedObserveInfo(self): 606 """ 607 An observer for Disco Info requests is setup on stream initialization. 608 """ 609 xml = """<iq from='test@example.com' to='example.com' 610 type='get'> 611 <query xmlns='%s'/> 612 </iq>""" % NS_DISCO_INFO 613 614 def handleRequest(iq): 615 called.append(iq) 616 617 called = [] 618 self.service.xmlstream = utility.EventDispatcher() 619 self.service.handleRequest = handleRequest 620 self.service.connectionInitialized() 621 self.service.xmlstream.dispatch(parseXml(xml)) 622 self.assertEqual(1, len(called)) 623 624 625 def test_connectionInitializedObserveItems(self): 626 """ 627 An observer for Disco Items requests is setup on stream initialization. 628 """ 629 xml = """<iq from='test@example.com' to='example.com' 630 type='get'> 631 <query xmlns='%s'/> 632 </iq>""" % NS_DISCO_ITEMS 633 634 def handleRequest(iq): 635 called.append(iq) 636 637 called = [] 638 self.service.xmlstream = utility.EventDispatcher() 639 self.service.handleRequest = handleRequest 640 self.service.connectionInitialized() 641 self.service.xmlstream.dispatch(parseXml(xml)) 642 self.assertEqual(1, len(called)) 597 643 598 644 … … 636 682 637 683 684 def test_onDiscoInfoWithNoFromAttribute(self): 685 """ 686 Disco info request without a from attribute has requestor None. 687 """ 688 xml = """<iq to='example.com' 689 type='get'> 690 <query xmlns='%s'/> 691 </iq>""" % NS_DISCO_INFO 692 693 def info(requestor, target, nodeIdentifier): 694 self.assertEqual(None, requestor) 695 696 return defer.succeed([ 697 disco.DiscoIdentity('dummy', 'generic', 'Generic Dummy Entity'), 698 disco.DiscoFeature('jabber:iq:version') 699 ]) 700 701 self.service.info = info 702 d = self.handleRequest(xml) 703 return d 704 705 706 def test_onDiscoInfoWithNoToAttribute(self): 707 """ 708 Disco info request without a to attribute has target None. 709 """ 710 xml = """<iq from='test@example.com' 711 type='get'> 712 <query xmlns='%s'/> 713 </iq>""" % NS_DISCO_INFO 714 715 def info(requestor, target, nodeIdentifier): 716 self.assertEqual(JID('test@example.com'), requestor) 717 718 return defer.succeed([ 719 disco.DiscoIdentity('dummy', 'generic', 'Generic Dummy Entity'), 720 disco.DiscoFeature('jabber:iq:version') 721 ]) 722 723 self.service.info = info 724 d = self.handleRequest(xml) 725 return d 726 727 638 728 def test_onDiscoInfoWithNode(self): 639 729 """ … … 658 748 self.service.info = info 659 749 d = self.handleRequest(xml) 750 d.addCallback(cb) 751 return d 752 753 754 def test_onDiscoInfoWithNodeNoResults(self): 755 """ 756 An info request for a node with no results returns items-not-found. 757 """ 758 xml = """<iq from='test@example.com' to='example.com' 759 type='get'> 760 <query xmlns='%s' node='test'/> 761 </iq>""" % NS_DISCO_INFO 762 763 def cb(exc): 764 self.assertEquals('item-not-found', exc.condition) 765 766 def info(requestor, target, nodeIdentifier): 767 self.assertEqual('test', nodeIdentifier) 768 769 return defer.succeed([]) 770 771 self.service.info = info 772 d = self.handleRequest(xml) 773 self.assertFailure(d, StanzaError) 660 774 d.addCallback(cb) 661 775 return d -
wokkel/test/test_generic.py
- Property exe set to *
r35 r102 1 # Copyright (c) 2003-2008 Ralph Meijer1 # Copyright (c) Ralph Meijer. 2 2 # See LICENSE for details. 3 3 … … 8 8 from twisted.trial import unittest 9 9 from twisted.words.xish import domish 10 from twisted.words.protocols.jabber.jid import JID 10 11 11 12 from wokkel import generic … … 87 88 self.pipe.sink.send(element) 88 89 self.assertEquals([element], called) 90 91 92 93 class RequestTest(unittest.TestCase): 94 """ 95 Tests for L{generic.Request}. 96 """ 97 98 def setUp(self): 99 self.request = generic.Request() 100 101 102 def test_toElementStanzaKind(self): 103 """ 104 A request is an iq stanza. 105 """ 106 element = self.request.toElement() 107 self.assertIdentical(None, element.uri) 108 self.assertEquals('iq', element.name) 109 110 111 def test_toElementStanzaType(self): 112 """ 113 The request has type 'get'. 114 """ 115 self.assertEquals('get', self.request.stanzaType) 116 element = self.request.toElement() 117 self.assertEquals('get', element.getAttribute('type')) 118 119 120 def test_toElementStanzaTypeSet(self): 121 """ 122 The request has type 'set'. 123 """ 124 self.request.stanzaType = 'set' 125 element = self.request.toElement() 126 self.assertEquals('set', element.getAttribute('type')) 127 128 129 def test_toElementStanzaID(self): 130 """ 131 A request, when rendered, has an identifier. 132 """ 133 element = self.request.toElement() 134 self.assertNotIdentical(None, self.request.stanzaID) 135 self.assertEquals(self.request.stanzaID, element.getAttribute('id')) 136 137 138 def test_toElementRecipient(self): 139 """ 140 A request without recipient, has no 'to' attribute. 141 """ 142 self.request = generic.Request(recipient=JID('other@example.org')) 143 self.assertEquals(JID('other@example.org'), self.request.recipient) 144 element = self.request.toElement() 145 self.assertEquals(u'other@example.org', element.getAttribute('to')) 146 147 148 def test_toElementRecipientNone(self): 149 """ 150 A request without recipient, has no 'to' attribute. 151 """ 152 element = self.request.toElement() 153 self.assertFalse(element.hasAttribute('to')) 154 155 156 def test_toElementSender(self): 157 """ 158 A request with sender, has a 'from' attribute. 159 """ 160 self.request = generic.Request(sender=JID('user@example.org')) 161 self.assertEquals(JID('user@example.org'), self.request.sender) 162 element = self.request.toElement() 163 self.assertEquals(u'user@example.org', element.getAttribute('from')) 164 165 166 def test_toElementSenderNone(self): 167 """ 168 A request without sender, has no 'from' attribute. 169 """ 170 element = self.request.toElement() 171 self.assertFalse(element.hasAttribute('from')) 172 173 174 def test_timeoutDefault(self): 175 """ 176 The default is no timeout. 177 """ 178 self.assertIdentical(None, self.request.timeout) -
wokkel/test/test_ping.py
- Property exe set to *
r65 r96 1 # Copyright (c) 2003-2009 Ralph Meijer1 # Copyright (c) Ralph Meijer. 2 2 # See LICENSE for details. 3 3 … … 134 134 135 135 136 def test_onPingHandled(self): 137 """ 138 The ping handler should mark the stanza as handled. 139 """ 140 xml = """<iq from='test@example.com' to='example.com' type='get'> 141 <ping xmlns='urn:xmpp:ping'/> 142 </iq>""" 143 iq = parseXml(xml) 144 self.stub.send(iq) 145 146 self.assertTrue(iq.handled) 147 148 136 149 def test_interfaceIDisco(self): 137 150 """ -
wokkel/test/test_pubsub.py
- Property exe set to *
r59 r97 1 # Copyright (c) 2003-2009 Ralph Meijer1 # Copyright (c) Ralph Meijer. 2 2 # See LICENSE for details. 3 3 … … 20 20 21 21 NS_PUBSUB = 'http://jabber.org/protocol/pubsub' 22 NS_PUBSUB_ CONFIG = 'http://jabber.org/protocol/pubsub#node_config'22 NS_PUBSUB_NODE_CONFIG = 'http://jabber.org/protocol/pubsub#node_config' 23 23 NS_PUBSUB_ERRORS = 'http://jabber.org/protocol/pubsub#errors' 24 24 NS_PUBSUB_EVENT = 'http://jabber.org/protocol/pubsub#event' 25 25 NS_PUBSUB_OWNER = 'http://jabber.org/protocol/pubsub#owner' 26 26 NS_PUBSUB_META_DATA = 'http://jabber.org/protocol/pubsub#meta-data' 27 NS_PUBSUB_SUBSCRIBE_OPTIONS = 'http://jabber.org/protocol/pubsub#subscribe_options' 27 28 28 29 def calledAsync(fn): … … 41 42 42 43 return d, func 44 45 46 class SubscriptionTest(unittest.TestCase): 47 """ 48 Tests for L{pubsub.Subscription}. 49 """ 50 51 def test_fromElement(self): 52 """ 53 fromElement parses a subscription from XML DOM. 54 """ 55 xml = """ 56 <subscription node='test' jid='user@example.org/Home' 57 subscription='pending'/> 58 """ 59 subscription = pubsub.Subscription.fromElement(parseXml(xml)) 60 self.assertEqual('test', subscription.nodeIdentifier) 61 self.assertEqual(JID('user@example.org/Home'), subscription.subscriber) 62 self.assertEqual('pending', subscription.state) 63 self.assertIdentical(None, subscription.subscriptionIdentifier) 64 65 66 def test_fromElementWithSubscriptionIdentifier(self): 67 """ 68 A subscription identifier in the subscription should be parsed, too. 69 """ 70 xml = """ 71 <subscription node='test' jid='user@example.org/Home' subid='1234' 72 subscription='pending'/> 73 """ 74 subscription = pubsub.Subscription.fromElement(parseXml(xml)) 75 self.assertEqual('1234', subscription.subscriptionIdentifier) 76 77 78 def test_toElement(self): 79 """ 80 Rendering a Subscription should yield the proper attributes. 81 """ 82 subscription = pubsub.Subscription('test', 83 JID('user@example.org/Home'), 84 'pending') 85 element = subscription.toElement() 86 self.assertEqual('subscription', element.name) 87 self.assertEqual(None, element.uri) 88 self.assertEqual('test', element.getAttribute('node')) 89 self.assertEqual('user@example.org/Home', element.getAttribute('jid')) 90 self.assertEqual('pending', element.getAttribute('subscription')) 91 self.assertFalse(element.hasAttribute('subid')) 92 93 94 def test_toElementEmptyNodeIdentifier(self): 95 """ 96 The empty node identifier should not yield a node attribute. 97 """ 98 subscription = pubsub.Subscription('', 99 JID('user@example.org/Home'), 100 'pending') 101 element = subscription.toElement() 102 self.assertFalse(element.hasAttribute('node')) 103 104 105 def test_toElementWithSubscriptionIdentifier(self): 106 """ 107 The subscription identifier, if set, is in the subid attribute. 108 """ 109 subscription = pubsub.Subscription('test', 110 JID('user@example.org/Home'), 111 'pending', 112 subscriptionIdentifier='1234') 113 element = subscription.toElement() 114 self.assertEqual('1234', element.getAttribute('subid')) 115 43 116 44 117 … … 113 186 114 187 188 def test_eventItemsError(self): 189 """ 190 An error message with embedded event should not be handled. 191 192 This test uses an items event, which should not result in itemsReceived 193 being called. In general message.handled should be False. 194 """ 195 message = domish.Element((None, 'message')) 196 message['from'] = 'pubsub.example.org' 197 message['to'] = 'user@example.org/home' 198 message['type'] = 'error' 199 event = message.addElement((NS_PUBSUB_EVENT, 'event')) 200 items = event.addElement('items') 201 items['node'] = 'test' 202 203 class UnexpectedCall(Exception): 204 pass 205 206 def itemsReceived(event): 207 raise UnexpectedCall("Unexpected call to itemsReceived") 208 209 self.protocol.itemsReceived = itemsReceived 210 self.stub.send(message) 211 self.assertFalse(message.handled) 212 213 115 214 def test_eventDelete(self): 116 215 """ … … 266 365 iq = self.stub.output[-1] 267 366 self.assertEquals('user@example.org', iq['from']) 367 368 response = toResponse(iq, 'result') 369 self.stub.send(response) 370 return d 371 372 373 def test_createNodeWithConfig(self): 374 """ 375 Test sending create request with configuration options 376 """ 377 378 options = { 379 'pubsub#title': 'Princely Musings (Atom)', 380 'pubsub#deliver_payloads': True, 381 'pubsub#persist_items': '1', 382 'pubsub#max_items': '10', 383 'pubsub#access_model': 'open', 384 'pubsub#type': 'http://www.w3.org/2005/Atom', 385 } 386 387 d = self.protocol.createNode(JID('pubsub.example.org'), 'test', 388 sender=JID('user@example.org'), 389 options=options) 390 391 iq = self.stub.output[-1] 392 393 # check if there is exactly one configure element 394 children = list(domish.generateElementsQNamed(iq.pubsub.children, 395 'configure', NS_PUBSUB)) 396 self.assertEqual(1, len(children)) 397 398 # check that it has a configuration form 399 form = data_form.findForm(children[0], NS_PUBSUB_NODE_CONFIG) 400 self.assertEqual('submit', form.formType) 401 268 402 269 403 response = toResponse(iq, 'result') … … 408 542 409 543 544 def test_subscribeReturnsSubscription(self): 545 """ 546 A successful subscription should return a Subscription instance. 547 """ 548 def cb(subscription): 549 self.assertEqual(JID('user@example.org'), subscription.subscriber) 550 551 d = self.protocol.subscribe(JID('pubsub.example.org'), 'test', 552 JID('user@example.org')) 553 d.addCallback(cb) 554 555 iq = self.stub.output[-1] 556 557 response = toResponse(iq, 'result') 558 pubsub = response.addElement((NS_PUBSUB, 'pubsub')) 559 subscription = pubsub.addElement('subscription') 560 subscription['node'] = 'test' 561 subscription['jid'] = 'user@example.org' 562 subscription['subscription'] = 'subscribed' 563 self.stub.send(response) 564 return d 565 566 410 567 def test_subscribePending(self): 411 568 """ … … 448 605 449 606 450 def test_subscribeWithSender(self): 451 """ 452 Test sending subscription request from a specific JID. 453 """ 607 def test_subscribeWithOptions(self): 608 options = {'pubsub#deliver': False} 609 454 610 d = self.protocol.subscribe(JID('pubsub.example.org'), 'test', 455 JID('user@example.org'), 456 sender=JID('user@example.org')) 457 611 JID('user@example.org'), 612 options=options) 458 613 iq = self.stub.output[-1] 459 self.assertEquals('user@example.org', iq['from']) 460 614 615 # Check options present 616 childNames = [] 617 for element in iq.pubsub.elements(): 618 if element.uri == NS_PUBSUB: 619 childNames.append(element.name) 620 621 self.assertEqual(['subscribe', 'options'], childNames) 622 form = data_form.findForm(iq.pubsub.options, 623 NS_PUBSUB_SUBSCRIBE_OPTIONS) 624 self.assertEqual('submit', form.formType) 625 form.typeCheck({'pubsub#deliver': {'type': 'boolean'}}) 626 self.assertEqual(options, form.getValues()) 627 628 # Send response 461 629 response = toResponse(iq, 'result') 462 630 pubsub = response.addElement((NS_PUBSUB, 'pubsub')) … … 465 633 subscription['jid'] = 'user@example.org' 466 634 subscription['subscription'] = 'subscribed' 635 self.stub.send(response) 636 637 return d 638 639 640 def test_subscribeWithSender(self): 641 """ 642 Test sending subscription request from a specific JID. 643 """ 644 d = self.protocol.subscribe(JID('pubsub.example.org'), 'test', 645 JID('user@example.org'), 646 sender=JID('user@example.org')) 647 648 iq = self.stub.output[-1] 649 self.assertEquals('user@example.org', iq['from']) 650 651 response = toResponse(iq, 'result') 652 pubsub = response.addElement((NS_PUBSUB, 'pubsub')) 653 subscription = pubsub.addElement('subscription') 654 subscription['node'] = 'test' 655 subscription['jid'] = 'user@example.org' 656 subscription['subscription'] = 'subscribed' 657 self.stub.send(response) 658 return d 659 660 661 def test_subscribeReturningSubscriptionIdentifier(self): 662 """ 663 Test sending subscription request with subscription identifier. 664 """ 665 def cb(subscription): 666 self.assertEqual('1234', subscription.subscriptionIdentifier) 667 668 d = self.protocol.subscribe(JID('pubsub.example.org'), 'test', 669 JID('user@example.org')) 670 d.addCallback(cb) 671 672 iq = self.stub.output[-1] 673 674 response = toResponse(iq, 'result') 675 pubsub = response.addElement((NS_PUBSUB, 'pubsub')) 676 subscription = pubsub.addElement('subscription') 677 subscription['node'] = 'test' 678 subscription['jid'] = 'user@example.org' 679 subscription['subscription'] = 'subscribed' 680 subscription['subid'] = '1234' 467 681 self.stub.send(response) 468 682 return d … … 506 720 507 721 722 def test_unsubscribeWithSubscriptionIdentifier(self): 723 """ 724 Test sending unsubscription request with subscription identifier. 725 """ 726 d = self.protocol.unsubscribe(JID('pubsub.example.org'), 'test', 727 JID('user@example.org'), 728 subscriptionIdentifier='1234') 729 730 iq = self.stub.output[-1] 731 child = iq.pubsub.unsubscribe 732 self.assertEquals('1234', child['subid']) 733 734 self.stub.send(toResponse(iq, 'result')) 735 return d 736 737 508 738 def test_items(self): 509 739 """ … … 572 802 573 803 574 def test_itemsWithS ender(self):575 """ 576 Test sending items request from a specific JID.804 def test_itemsWithSubscriptionIdentifier(self): 805 """ 806 Test sending items request with a subscription identifier. 577 807 """ 578 808 579 809 d = self.protocol.items(JID('pubsub.example.org'), 'test', 580 s ender=JID('user@example.org'))810 subscriptionIdentifier='1234') 581 811 582 812 iq = self.stub.output[-1] 583 self.assertEquals('user@example.org', iq['from']) 813 child = iq.pubsub.items 814 self.assertEquals('1234', child['subid']) 584 815 585 816 response = toResponse(iq, 'result') … … 591 822 592 823 824 def test_itemsWithSender(self): 825 """ 826 Test sending items request from a specific JID. 827 """ 828 829 d = self.protocol.items(JID('pubsub.example.org'), 'test', 830 sender=JID('user@example.org')) 831 832 iq = self.stub.output[-1] 833 self.assertEquals('user@example.org', iq['from']) 834 835 response = toResponse(iq, 'result') 836 items = response.addElement((NS_PUBSUB, 'pubsub')).addElement('items') 837 items['node'] = 'test' 838 839 self.stub.send(response) 840 return d 841 842 843 def test_getOptions(self): 844 def cb(form): 845 self.assertEqual('form', form.formType) 846 self.assertEqual(NS_PUBSUB_SUBSCRIBE_OPTIONS, form.formNamespace) 847 field = form.fields['pubsub#deliver'] 848 self.assertEqual('boolean', field.fieldType) 849 self.assertIdentical(True, field.value) 850 self.assertEqual('Enable delivery?', field.label) 851 852 d = self.protocol.getOptions(JID('pubsub.example.org'), 'test', 853 JID('user@example.org'), 854 sender=JID('user@example.org')) 855 d.addCallback(cb) 856 857 iq = self.stub.output[-1] 858 self.assertEqual('pubsub.example.org', iq.getAttribute('to')) 859 self.assertEqual('get', iq.getAttribute('type')) 860 self.assertEqual('pubsub', iq.pubsub.name) 861 self.assertEqual(NS_PUBSUB, iq.pubsub.uri) 862 children = list(domish.generateElementsQNamed(iq.pubsub.children, 863 'options', NS_PUBSUB)) 864 self.assertEqual(1, len(children)) 865 child = children[0] 866 self.assertEqual('test', child['node']) 867 868 self.assertEqual(0, len(child.children)) 869 870 # Send response 871 form = data_form.Form('form', formNamespace=NS_PUBSUB_SUBSCRIBE_OPTIONS) 872 form.addField(data_form.Field('boolean', var='pubsub#deliver', 873 label='Enable delivery?', 874 value=True)) 875 response = toResponse(iq, 'result') 876 response.addElement((NS_PUBSUB, 'pubsub')) 877 response.pubsub.addElement('options') 878 response.pubsub.options.addChild(form.toElement()) 879 self.stub.send(response) 880 881 return d 882 883 884 def test_getOptionsWithSubscriptionIdentifier(self): 885 """ 886 Getting options with a subid should have the subid in the request. 887 """ 888 889 d = self.protocol.getOptions(JID('pubsub.example.org'), 'test', 890 JID('user@example.org'), 891 sender=JID('user@example.org'), 892 subscriptionIdentifier='1234') 893 894 iq = self.stub.output[-1] 895 child = iq.pubsub.options 896 self.assertEqual('1234', child['subid']) 897 898 # Send response 899 form = data_form.Form('form', formNamespace=NS_PUBSUB_SUBSCRIBE_OPTIONS) 900 form.addField(data_form.Field('boolean', var='pubsub#deliver', 901 label='Enable delivery?', 902 value=True)) 903 response = toResponse(iq, 'result') 904 response.addElement((NS_PUBSUB, 'pubsub')) 905 response.pubsub.addElement('options') 906 response.pubsub.options.addChild(form.toElement()) 907 self.stub.send(response) 908 909 return d 910 911 912 def test_setOptions(self): 913 """ 914 setOptions should send out a options-set request. 915 """ 916 options = {'pubsub#deliver': False} 917 918 d = self.protocol.setOptions(JID('pubsub.example.org'), 'test', 919 JID('user@example.org'), 920 options, 921 sender=JID('user@example.org')) 922 923 iq = self.stub.output[-1] 924 self.assertEqual('pubsub.example.org', iq.getAttribute('to')) 925 self.assertEqual('set', iq.getAttribute('type')) 926 self.assertEqual('pubsub', iq.pubsub.name) 927 self.assertEqual(NS_PUBSUB, iq.pubsub.uri) 928 children = list(domish.generateElementsQNamed(iq.pubsub.children, 929 'options', NS_PUBSUB)) 930 self.assertEqual(1, len(children)) 931 child = children[0] 932 self.assertEqual('test', child['node']) 933 934 form = data_form.findForm(child, NS_PUBSUB_SUBSCRIBE_OPTIONS) 935 self.assertEqual('submit', form.formType) 936 form.typeCheck({'pubsub#deliver': {'type': 'boolean'}}) 937 self.assertEqual(options, form.getValues()) 938 939 response = toResponse(iq, 'result') 940 self.stub.send(response) 941 942 return d 943 944 945 def test_setOptionsWithSubscriptionIdentifier(self): 946 """ 947 setOptions should send out a options-set request with subid. 948 """ 949 options = {'pubsub#deliver': False} 950 951 d = self.protocol.setOptions(JID('pubsub.example.org'), 'test', 952 JID('user@example.org'), 953 options, 954 subscriptionIdentifier='1234', 955 sender=JID('user@example.org')) 956 957 iq = self.stub.output[-1] 958 child = iq.pubsub.options 959 self.assertEqual('1234', child['subid']) 960 961 form = data_form.findForm(child, NS_PUBSUB_SUBSCRIBE_OPTIONS) 962 self.assertEqual('submit', form.formType) 963 form.typeCheck({'pubsub#deliver': {'type': 'boolean'}}) 964 self.assertEqual(options, form.getValues()) 965 966 response = toResponse(iq, 'result') 967 self.stub.send(response) 968 969 return d 970 593 971 594 972 class PubSubRequestTest(unittest.TestCase): 973 974 def test_fromElementUnknown(self): 975 """ 976 An unknown verb raises NotImplementedError. 977 """ 978 979 xml = """ 980 <iq type='set' to='pubsub.example.org' 981 from='user@example.org'> 982 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 983 <non-existing-verb/> 984 </pubsub> 985 </iq> 986 """ 987 988 self.assertRaises(NotImplementedError, 989 pubsub.PubSubRequest.fromElement, parseXml(xml)) 990 991 992 def test_fromElementKnownBadCombination(self): 993 """ 994 Multiple verbs in an unknown configuration raises NotImplementedError. 995 """ 996 997 xml = """ 998 <iq type='set' to='pubsub.example.org' 999 from='user@example.org'> 1000 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1001 <publish/> 1002 <create/> 1003 </pubsub> 1004 </iq> 1005 """ 1006 1007 self.assertRaises(NotImplementedError, 1008 pubsub.PubSubRequest.fromElement, parseXml(xml)) 595 1009 596 1010 def test_fromElementPublish(self): … … 639 1053 640 1054 1055 def test_fromElementPublishItemsOptions(self): 1056 """ 1057 Test parsing a publish request with items and options. 1058 1059 Note that publishing options are not supported, but passing them 1060 shouldn't affect processing of the publish request itself. 1061 """ 1062 1063 xml = """ 1064 <iq type='set' to='pubsub.example.org' 1065 from='user@example.org'> 1066 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1067 <publish node='test'> 1068 <item id="item1"/> 1069 <item id="item2"/> 1070 </publish> 1071 <publish-options/> 1072 </pubsub> 1073 </iq> 1074 """ 1075 1076 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 1077 self.assertEqual(2, len(request.items)) 1078 self.assertEqual(u'item1', request.items[0]["id"]) 1079 self.assertEqual(u'item2', request.items[1]["id"]) 1080 641 1081 def test_fromElementPublishNoNode(self): 642 1082 """ … … 719 1159 self.assertEqual('jid-required', err.appCondition.name) 720 1160 1161 1162 def test_fromElementSubscribeWithOptions(self): 1163 """ 1164 Test parsing a subscription request. 1165 """ 1166 1167 xml = """ 1168 <iq type='set' to='pubsub.example.org' 1169 from='user@example.org'> 1170 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1171 <subscribe node='test' jid='user@example.org/Home'/> 1172 <options> 1173 <x xmlns="jabber:x:data" type='submit'> 1174 <field var='FORM_TYPE' type='hidden'> 1175 <value>http://jabber.org/protocol/pubsub#subscribe_options</value> 1176 </field> 1177 <field var='pubsub#deliver' type='boolean' 1178 label='Enable delivery?'> 1179 <value>1</value> 1180 </field> 1181 </x> 1182 </options> 1183 </pubsub> 1184 </iq> 1185 """ 1186 1187 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 1188 self.assertEqual('subscribe', request.verb) 1189 request.options.typeCheck({'pubsub#deliver': {'type': 'boolean'}}) 1190 self.assertEqual({'pubsub#deliver': True}, request.options.getValues()) 1191 1192 1193 def test_fromElementSubscribeWithOptionsBadFormType(self): 1194 """ 1195 The options form should have the right type. 1196 """ 1197 1198 xml = """ 1199 <iq type='set' to='pubsub.example.org' 1200 from='user@example.org'> 1201 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1202 <subscribe node='test' jid='user@example.org/Home'/> 1203 <options> 1204 <x xmlns="jabber:x:data" type='result'> 1205 <field var='FORM_TYPE' type='hidden'> 1206 <value>http://jabber.org/protocol/pubsub#subscribe_options</value> 1207 </field> 1208 <field var='pubsub#deliver' type='boolean' 1209 label='Enable delivery?'> 1210 <value>1</value> 1211 </field> 1212 </x> 1213 </options> 1214 </pubsub> 1215 </iq> 1216 """ 1217 1218 err = self.assertRaises(error.StanzaError, 1219 pubsub.PubSubRequest.fromElement, 1220 parseXml(xml)) 1221 self.assertEqual('bad-request', err.condition) 1222 self.assertEqual("Unexpected form type 'result'", err.text) 1223 self.assertEqual(None, err.appCondition) 1224 1225 1226 def test_fromElementSubscribeWithOptionsEmpty(self): 1227 """ 1228 When no (suitable) form is found, the options are empty. 1229 """ 1230 1231 xml = """ 1232 <iq type='set' to='pubsub.example.org' 1233 from='user@example.org'> 1234 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1235 <subscribe node='test' jid='user@example.org/Home'/> 1236 <options/> 1237 </pubsub> 1238 </iq> 1239 """ 1240 1241 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 1242 self.assertEqual('subscribe', request.verb) 1243 self.assertEqual({}, request.options.getValues()) 1244 1245 721 1246 def test_fromElementUnsubscribe(self): 722 1247 """ … … 741 1266 742 1267 1268 def test_fromElementUnsubscribeWithSubscriptionIdentifier(self): 1269 """ 1270 Test parsing an unsubscription request with subscription identifier. 1271 """ 1272 1273 xml = """ 1274 <iq type='set' to='pubsub.example.org' 1275 from='user@example.org'> 1276 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1277 <unsubscribe node='test' jid='user@example.org/Home' 1278 subid='1234'/> 1279 </pubsub> 1280 </iq> 1281 """ 1282 1283 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 1284 self.assertEqual('1234', request.subscriptionIdentifier) 1285 1286 743 1287 def test_fromElementUnsubscribeNoJID(self): 744 1288 """ … … 777 1321 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 778 1322 self.assertEqual('optionsGet', request.verb) 1323 self.assertEqual(JID('user@example.org'), request.sender) 1324 self.assertEqual(JID('pubsub.example.org'), request.recipient) 1325 self.assertEqual('test', request.nodeIdentifier) 1326 self.assertEqual(JID('user@example.org/Home'), request.subscriber) 1327 1328 1329 def test_fromElementOptionsGetWithSubscriptionIdentifier(self): 1330 """ 1331 Test parsing a request for getting subscription options with subid. 1332 """ 1333 1334 xml = """ 1335 <iq type='get' to='pubsub.example.org' 1336 from='user@example.org'> 1337 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1338 <options node='test' jid='user@example.org/Home' 1339 subid='1234'/> 1340 </pubsub> 1341 </iq> 1342 """ 1343 1344 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 1345 self.assertEqual('1234', request.subscriptionIdentifier) 779 1346 780 1347 … … 806 1373 self.assertEqual('test', request.nodeIdentifier) 807 1374 self.assertEqual(JID('user@example.org/Home'), request.subscriber) 808 self.assertEqual({'pubsub#deliver': '1'}, request.options) 809 810 811 def test_fromElementOptionsSetCancel(self): 812 """ 813 Test parsing a request for cancelling setting subscription options. 814 """ 815 816 xml = """ 817 <iq type='set' to='pubsub.example.org' 818 from='user@example.org'> 819 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 820 <options node='test' jid='user@example.org/Home'> 821 <x xmlns='jabber:x:data' type='cancel'/> 822 </options> 823 </pubsub> 824 </iq> 825 """ 826 827 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 828 self.assertEqual({}, request.options) 829 830 831 def test_fromElementOptionsSetBadFormType(self): 832 """ 833 On a options set request unknown fields should be ignored. 834 """ 835 836 xml = """ 837 <iq type='set' to='pubsub.example.org' 838 from='user@example.org'> 839 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 840 <options node='test' jid='user@example.org/Home'> 841 <x xmlns='jabber:x:data' type='result'> 1375 self.assertEqual({'pubsub#deliver': '1'}, request.options.getValues()) 1376 1377 1378 def test_fromElementOptionsSetWithSubscriptionIdentifier(self): 1379 """ 1380 Test parsing a request for setting subscription options with subid. 1381 """ 1382 1383 xml = """ 1384 <iq type='set' to='pubsub.example.org' 1385 from='user@example.org'> 1386 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1387 <options node='test' jid='user@example.org/Home' 1388 subid='1234'> 1389 <x xmlns='jabber:x:data' type='submit'> 842 1390 <field var='FORM_TYPE' type='hidden'> 843 <value>http://jabber.org/protocol/pubsub# node_config</value>1391 <value>http://jabber.org/protocol/pubsub#subscribe_options</value> 844 1392 </field> 845 1393 <field var='pubsub#deliver'><value>1</value></field> … … 850 1398 """ 851 1399 1400 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 1401 self.assertEqual('1234', request.subscriptionIdentifier) 1402 1403 1404 def test_fromElementOptionsSetCancel(self): 1405 """ 1406 Test parsing a request for cancelling setting subscription options. 1407 """ 1408 1409 xml = """ 1410 <iq type='set' to='pubsub.example.org' 1411 from='user@example.org'> 1412 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1413 <options node='test' jid='user@example.org/Home'> 1414 <x xmlns='jabber:x:data' type='cancel'/> 1415 </options> 1416 </pubsub> 1417 </iq> 1418 """ 1419 1420 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 1421 self.assertEqual('cancel', request.options.formType) 1422 1423 1424 def test_fromElementOptionsSetBadFormType(self): 1425 """ 1426 On a options set request unknown fields should be ignored. 1427 """ 1428 1429 xml = """ 1430 <iq type='set' to='pubsub.example.org' 1431 from='user@example.org'> 1432 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1433 <options node='test' jid='user@example.org/Home'> 1434 <x xmlns='jabber:x:data' type='result'> 1435 <field var='FORM_TYPE' type='hidden'> 1436 <value>http://jabber.org/protocol/pubsub#subscribe_options</value> 1437 </field> 1438 <field var='pubsub#deliver'><value>1</value></field> 1439 </x> 1440 </options> 1441 </pubsub> 1442 </iq> 1443 """ 1444 1445 err = self.assertRaises(error.StanzaError, 1446 pubsub.PubSubRequest.fromElement, 1447 parseXml(xml)) 1448 self.assertEqual('bad-request', err.condition) 1449 self.assertEqual("Unexpected form type 'result'", err.text) 1450 self.assertEqual(None, err.appCondition) 1451 1452 1453 def test_fromElementOptionsSetNoForm(self): 1454 """ 1455 On a options set request a form is required. 1456 """ 1457 1458 xml = """ 1459 <iq type='set' to='pubsub.example.org' 1460 from='user@example.org'> 1461 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1462 <options node='test' jid='user@example.org/Home'/> 1463 </pubsub> 1464 </iq> 1465 """ 852 1466 err = self.assertRaises(error.StanzaError, 853 1467 pubsub.PubSubRequest.fromElement, … … 857 1471 858 1472 859 def test_fromElementOptionsSetNoForm(self):860 """861 On a options set request a form is required.862 """863 864 xml = """865 <iq type='set' to='pubsub.example.org'866 from='user@example.org'>867 <pubsub xmlns='http://jabber.org/protocol/pubsub'>868 <options node='test' jid='user@example.org/Home'/>869 </pubsub>870 </iq>871 """872 err = self.assertRaises(error.StanzaError,873 pubsub.PubSubRequest.fromElement,874 parseXml(xml))875 self.assertEqual('bad-request', err.condition)876 self.assertEqual(None, err.appCondition)877 878 879 1473 def test_fromElementSubscriptions(self): 880 1474 """ … … 936 1530 self.assertEqual(JID('pubsub.example.org'), request.recipient) 937 1531 self.assertEqual('mynode', request.nodeIdentifier) 1532 self.assertIdentical(None, request.options) 938 1533 939 1534 … … 956 1551 957 1552 1553 def test_fromElementCreateConfigureEmpty(self): 1554 """ 1555 Test parsing a request to create a node with an empty configuration. 1556 """ 1557 1558 xml = """ 1559 <iq type='set' to='pubsub.example.org' 1560 from='user@example.org'> 1561 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1562 <create node='mynode'/> 1563 <configure/> 1564 </pubsub> 1565 </iq> 1566 """ 1567 1568 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 1569 self.assertEqual({}, request.options.getValues()) 1570 self.assertEqual(u'mynode', request.nodeIdentifier) 1571 1572 1573 def test_fromElementCreateConfigureEmptyWrongOrder(self): 1574 """ 1575 Test parsing a request to create a node and configure, wrong order. 1576 1577 The C{configure} element should come after the C{create} request, 1578 but we should accept both orders. 1579 """ 1580 1581 xml = """ 1582 <iq type='set' to='pubsub.example.org' 1583 from='user@example.org'> 1584 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1585 <configure/> 1586 <create node='mynode'/> 1587 </pubsub> 1588 </iq> 1589 """ 1590 1591 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 1592 self.assertEqual({}, request.options.getValues()) 1593 self.assertEqual(u'mynode', request.nodeIdentifier) 1594 1595 1596 def test_fromElementCreateConfigure(self): 1597 """ 1598 Test parsing a request to create a node. 1599 """ 1600 1601 xml = """ 1602 <iq type='set' to='pubsub.example.org' 1603 from='user@example.org'> 1604 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1605 <create node='mynode'/> 1606 <configure> 1607 <x xmlns='jabber:x:data' type='submit'> 1608 <field var='FORM_TYPE' type='hidden'> 1609 <value>http://jabber.org/protocol/pubsub#node_config</value> 1610 </field> 1611 <field var='pubsub#access_model'><value>open</value></field> 1612 <field var='pubsub#persist_items'><value>0</value></field> 1613 </x> 1614 </configure> 1615 </pubsub> 1616 </iq> 1617 """ 1618 1619 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 1620 values = request.options.getValues() 1621 self.assertIn('pubsub#access_model', values) 1622 self.assertEqual(u'open', values['pubsub#access_model']) 1623 self.assertIn('pubsub#persist_items', values) 1624 self.assertEqual(u'0', values['pubsub#persist_items']) 1625 1626 1627 def test_fromElementCreateConfigureBadFormType(self): 1628 """ 1629 The form of a node creation request should have the right type. 1630 """ 1631 1632 xml = """ 1633 <iq type='set' to='pubsub.example.org' 1634 from='user@example.org'> 1635 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1636 <create node='mynode'/> 1637 <configure> 1638 <x xmlns='jabber:x:data' type='result'> 1639 <field var='FORM_TYPE' type='hidden'> 1640 <value>http://jabber.org/protocol/pubsub#node_config</value> 1641 </field> 1642 <field var='pubsub#access_model'><value>open</value></field> 1643 <field var='pubsub#persist_items'><value>0</value></field> 1644 </x> 1645 </configure> 1646 </pubsub> 1647 </iq> 1648 """ 1649 1650 err = self.assertRaises(error.StanzaError, 1651 pubsub.PubSubRequest.fromElement, 1652 parseXml(xml)) 1653 self.assertEqual('bad-request', err.condition) 1654 self.assertEqual("Unexpected form type 'result'", err.text) 1655 self.assertEqual(None, err.appCondition) 1656 1657 958 1658 def test_fromElementDefault(self): 959 1659 """ 960 Test parsing a request for the default node configuration. 1660 Parsing default node configuration request sets required attributes. 1661 1662 Besides C{verb}, C{sender} and C{recipient}, we expect C{nodeType} 1663 to be set. If not passed it receives the default C{u'leaf'}. 961 1664 """ 962 1665 … … 971 1674 972 1675 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 973 self.assertEqual ('default', request.verb)974 self.assertEqual (JID('user@example.org'), request.sender)975 self.assertEqual (JID('pubsub.example.org'), request.recipient)976 self.assertEqual ('leaf', request.nodeType)1676 self.assertEquals(u'default', request.verb) 1677 self.assertEquals(JID('user@example.org'), request.sender) 1678 self.assertEquals(JID('pubsub.example.org'), request.recipient) 1679 self.assertEquals(u'leaf', request.nodeType) 977 1680 978 1681 979 1682 def test_fromElementDefaultCollection(self): 980 1683 """ 981 Parsing a request for the default configuration extracts the node type.1684 Parsing default request for collection sets nodeType to collection. 982 1685 """ 983 1686 … … 1002 1705 1003 1706 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 1004 self.assertEqual ('collection', request.nodeType)1707 self.assertEquals('collection', request.nodeType) 1005 1708 1006 1709 … … 1054 1757 self.assertEqual('test', request.nodeIdentifier) 1055 1758 self.assertEqual({'pubsub#deliver_payloads': '0', 1056 'pubsub#persist_items': '1'}, request.options) 1759 'pubsub#persist_items': '1'}, 1760 request.options.getValues()) 1057 1761 1058 1762 … … 1074 1778 1075 1779 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 1076 self.assertEqual( {}, request.options)1780 self.assertEqual('cancel', request.options.formType) 1077 1781 1078 1782 1079 1783 def test_fromElementConfigureSetBadFormType(self): 1080 1784 """ 1081 On a node configuration set request unknown fields should be ignored.1785 The form of a node configuraton set request should have the right type. 1082 1786 """ 1083 1787 … … 1103 1807 parseXml(xml)) 1104 1808 self.assertEqual('bad-request', err.condition) 1809 self.assertEqual("Unexpected form type 'result'", err.text) 1105 1810 self.assertEqual(None, err.appCondition) 1106 1811 … … 1145 1850 self.assertEqual('test', request.nodeIdentifier) 1146 1851 self.assertIdentical(None, request.maxItems) 1852 self.assertIdentical(None, request.subscriptionIdentifier) 1147 1853 self.assertEqual([], request.itemIdentifiers) 1854 1855 1856 def test_fromElementItemsSubscriptionIdentifier(self): 1857 """ 1858 Test parsing an items request with subscription identifier. 1859 """ 1860 xml = """ 1861 <iq type='get' to='pubsub.example.org' 1862 from='user@example.org'> 1863 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1864 <items node='test' subid='1234'/> 1865 </pubsub> 1866 </iq> 1867 """ 1868 1869 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 1870 self.assertEqual('1234', request.subscriptionIdentifier) 1148 1871 1149 1872 … … 1234 1957 1235 1958 1959 def test_interfaceIDisco(self): 1960 """ 1961 Do instances of L{pubsub.PubSubService} provide L{iwokkel.IDisco}? 1962 """ 1963 verify.verifyObject(iwokkel.IDisco, self.service) 1964 1965 1236 1966 def test_connectionMade(self): 1237 1967 """ … … 1336 2066 d = self.service.getDiscoInfo(JID('user@example.org/home'), 1337 2067 JID('pubsub.example.org'), '') 2068 d.addCallback(cb) 2069 return d 2070 2071 2072 def test_getDiscoInfoBadResponse(self): 2073 """ 2074 If getInfo returns invalid response, it should be logged, then ignored. 2075 """ 2076 def cb(info): 2077 self.assertEquals([], info) 2078 self.assertEqual(1, len(self.flushLoggedErrors(TypeError))) 2079 2080 def getInfo(requestor, target, nodeIdentifier): 2081 return defer.succeed('bad response') 2082 2083 self.resource.getInfo = getInfo 2084 d = self.service.getDiscoInfo(JID('user@example.org/home'), 2085 JID('pubsub.example.org'), 'test') 2086 d.addCallback(cb) 2087 return d 2088 2089 2090 def test_getDiscoInfoException(self): 2091 """ 2092 If getInfo returns invalid response, it should be logged, then ignored. 2093 """ 2094 def cb(info): 2095 self.assertEquals([], info) 2096 self.assertEqual(1, len(self.flushLoggedErrors(NotImplementedError))) 2097 2098 def getInfo(requestor, target, nodeIdentifier): 2099 return defer.fail(NotImplementedError()) 2100 2101 self.resource.getInfo = getInfo 2102 d = self.service.getDiscoInfo(JID('user@example.org/home'), 2103 JID('pubsub.example.org'), 'test') 1338 2104 d.addCallback(cb) 1339 2105 return d … … 1489 2255 1490 2256 2257 def test_on_subscribeSubscriptionIdentifier(self): 2258 """ 2259 If a subscription returns a subid, this should be available. 2260 """ 2261 2262 xml = """ 2263 <iq type='set' to='pubsub.example.org' 2264 from='user@example.org'> 2265 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 2266 <subscribe node='test' jid='user@example.org/Home'/> 2267 </pubsub> 2268 </iq> 2269 """ 2270 2271 def subscribe(request): 2272 subscription = pubsub.Subscription(request.nodeIdentifier, 2273 request.subscriber, 2274 'subscribed', 2275 subscriptionIdentifier='1234') 2276 return defer.succeed(subscription) 2277 2278 def cb(element): 2279 self.assertEqual('1234', element.subscription.getAttribute('subid')) 2280 2281 self.resource.subscribe = subscribe 2282 verify.verifyObject(iwokkel.IPubSubResource, self.resource) 2283 d = self.handleRequest(xml) 2284 d.addCallback(cb) 2285 return d 2286 2287 1491 2288 def test_on_unsubscribe(self): 1492 2289 """ … … 1504 2301 1505 2302 def unsubscribe(request): 2303 return defer.succeed(None) 2304 2305 def cb(element): 2306 self.assertIdentical(None, element) 2307 2308 self.resource.unsubscribe = unsubscribe 2309 verify.verifyObject(iwokkel.IPubSubResource, self.resource) 2310 d = self.handleRequest(xml) 2311 d.addCallback(cb) 2312 return d 2313 2314 2315 def test_on_unsubscribeSubscriptionIdentifier(self): 2316 """ 2317 A successful unsubscription with subid should return an empty response. 2318 """ 2319 2320 xml = """ 2321 <iq type='set' to='pubsub.example.org' 2322 from='user@example.org'> 2323 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 2324 <unsubscribe node='test' jid='user@example.org/Home' subid='1234'/> 2325 </pubsub> 2326 </iq> 2327 """ 2328 2329 def unsubscribe(request): 2330 self.assertEqual('1234', request.subscriptionIdentifier) 1506 2331 return defer.succeed(None) 1507 2332 … … 1602 2427 subscription = children[0] 1603 2428 self.assertEqual('subscription', subscription.name) 1604 self.assertEqual(NS_PUBSUB, subscription.uri )2429 self.assertEqual(NS_PUBSUB, subscription.uri, NS_PUBSUB) 1605 2430 self.assertEqual('user@example.org', subscription['jid']) 1606 2431 self.assertEqual('test', subscription['node']) 1607 2432 self.assertEqual('subscribed', subscription['subscription']) 2433 2434 self.resource.subscriptions = subscriptions 2435 verify.verifyObject(iwokkel.IPubSubResource, self.resource) 2436 d = self.handleRequest(xml) 2437 d.addCallback(cb) 2438 return d 2439 2440 2441 def test_on_subscriptionsWithSubscriptionIdentifier(self): 2442 """ 2443 A subscriptions request response should include subids, if set. 2444 """ 2445 2446 xml = """ 2447 <iq type='get' to='pubsub.example.org' 2448 from='user@example.org'> 2449 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 2450 <subscriptions/> 2451 </pubsub> 2452 </iq> 2453 """ 2454 2455 def subscriptions(request): 2456 subscription = pubsub.Subscription('test', JID('user@example.org'), 2457 'subscribed', 2458 subscriptionIdentifier='1234') 2459 return defer.succeed([subscription]) 2460 2461 def cb(element): 2462 subscription = element.subscriptions.subscription 2463 self.assertEqual('1234', subscription['subid']) 1608 2464 1609 2465 self.resource.subscriptions = subscriptions … … 1741 2597 1742 2598 1743 def test_on_default(self): 1744 """ 1745 A default request should result in 1746 L{PubSubService.getDefaultConfiguration} being called. 1747 """ 1748 1749 xml = """ 1750 <iq type='get' to='pubsub.example.org' 1751 from='user@example.org'> 1752 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 1753 <default/> 2599 def test_on_createWithConfig(self): 2600 """ 2601 On a node create with configuration request the Data Form is parsed and 2602 L{PubSubResource.create} is called with the passed options. 2603 """ 2604 2605 xml = """ 2606 <iq type='set' to='pubsub.example.org' 2607 from='user@example.org'> 2608 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 2609 <create node='mynode'/> 2610 <configure> 2611 <x xmlns='jabber:x:data' type='submit'> 2612 <field var='FORM_TYPE' type='hidden'> 2613 <value>http://jabber.org/protocol/pubsub#node_config</value> 2614 </field> 2615 <field var='pubsub#deliver_payloads'><value>0</value></field> 2616 <field var='pubsub#persist_items'><value>1</value></field> 2617 </x> 2618 </configure> 1754 2619 </pubsub> 1755 2620 </iq> … … 1766 2631 } 1767 2632 2633 def create(request): 2634 self.assertEqual({'pubsub#deliver_payloads': False, 2635 'pubsub#persist_items': True}, 2636 request.options.getValues()) 2637 return defer.succeed(None) 2638 2639 self.resource.getConfigurationOptions = getConfigurationOptions 2640 self.resource.create = create 2641 verify.verifyObject(iwokkel.IPubSubResource, self.resource) 2642 return self.handleRequest(xml) 2643 2644 2645 def test_on_default(self): 2646 """ 2647 A default request returns default options filtered by available fields. 2648 """ 2649 2650 xml = """ 2651 <iq type='get' to='pubsub.example.org' 2652 from='user@example.org'> 2653 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 2654 <default/> 2655 </pubsub> 2656 </iq> 2657 """ 2658 fieldDefs = { 2659 "pubsub#persist_items": 2660 {"type": "boolean", 2661 "label": "Persist items to storage"}, 2662 "pubsub#deliver_payloads": 2663 {"type": "boolean", 2664 "label": "Deliver payloads with event notifications"} 2665 } 2666 2667 def getConfigurationOptions(): 2668 return fieldDefs 2669 1768 2670 def default(request): 1769 return defer.succeed({}) 2671 return defer.succeed({'pubsub#persist_items': 'false', 2672 'x-myfield': '1'}) 1770 2673 1771 2674 def cb(element): 1772 self.assertEqual ('pubsub', element.name)1773 self.assertEqual (NS_PUBSUB_OWNER, element.uri)1774 self.assertEqual (NS_PUBSUB_OWNER, element.default.uri)2675 self.assertEquals('pubsub', element.name) 2676 self.assertEquals(NS_PUBSUB_OWNER, element.uri) 2677 self.assertEquals(NS_PUBSUB_OWNER, element.default.uri) 1775 2678 form = data_form.Form.fromElement(element.default.x) 1776 self.assertEqual(NS_PUBSUB_CONFIG, form.formNamespace) 2679 self.assertEquals(NS_PUBSUB_NODE_CONFIG, form.formNamespace) 2680 form.typeCheck(fieldDefs) 2681 self.assertIn('pubsub#persist_items', form.fields) 2682 self.assertFalse(form.fields['pubsub#persist_items'].value) 2683 self.assertNotIn('x-myfield', form.fields) 1777 2684 1778 2685 self.resource.getConfigurationOptions = getConfigurationOptions … … 1784 2691 1785 2692 1786 def test_on_defaultCollection(self):1787 """1788 Responses to default requests should depend on passed node type.1789 """1790 1791 xml = """1792 <iq type='get' to='pubsub.example.org'1793 from='user@example.org'>1794 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>1795 <default>1796 <x xmlns='jabber:x:data' type='submit'>1797 <field var='FORM_TYPE' type='hidden'>1798 <value>http://jabber.org/protocol/pubsub#node_config</value>1799 </field>1800 <field var='pubsub#node_type'>1801 <value>collection</value>1802 </field>1803 </x>1804 </default>1805 1806 </pubsub>1807 </iq>1808 """1809 1810 def getConfigurationOptions():1811 return {1812 "pubsub#deliver_payloads":1813 {"type": "boolean",1814 "label": "Deliver payloads with event notifications"}1815 }1816 1817 def default(request):1818 return defer.succeed({})1819 1820 self.resource.getConfigurationOptions = getConfigurationOptions1821 self.resource.default = default1822 verify.verifyObject(iwokkel.IPubSubResource, self.resource)1823 return self.handleRequest(xml)1824 1825 1826 2693 def test_on_defaultUnknownNodeType(self): 1827 2694 """ 1828 A default request should result in 1829 L{PubSubResource.default} being called. 2695 Unknown node types yield non-acceptable. 2696 2697 Both C{getConfigurationOptions} and C{default} must not be called. 1830 2698 """ 1831 2699 … … 1849 2717 """ 1850 2718 2719 def getConfigurationOptions(): 2720 self.fail("Unexpected call to getConfigurationOptions") 2721 1851 2722 def default(request): 1852 self.fail("Unexpected call to getConfiguration")2723 self.fail("Unexpected call to default") 1853 2724 1854 2725 def cb(result): 1855 2726 self.assertEquals('not-acceptable', result.condition) 1856 2727 2728 self.resource.getConfigurationOptions = getConfigurationOptions 1857 2729 self.resource.default = default 1858 2730 verify.verifyObject(iwokkel.IPubSubResource, self.resource) … … 1896 2768 'pubsub#persist_items': '1', 1897 2769 'pubsub#owner': JID('user@example.org'), 1898 'x-myfield': ['a', 'b']})2770 'x-myfield': 'a'}) 1899 2771 1900 2772 def cb(element): … … 1903 2775 self.assertEqual(NS_PUBSUB_OWNER, element.configure.uri) 1904 2776 form = data_form.Form.fromElement(element.configure.x) 1905 self.assertEqual(NS_PUBSUB_ CONFIG, form.formNamespace)2777 self.assertEqual(NS_PUBSUB_NODE_CONFIG, form.formNamespace) 1906 2778 fields = form.fields 1907 2779 … … 1969 2841 def configureSet(request): 1970 2842 self.assertEqual({'pubsub#deliver_payloads': False, 1971 'pubsub#persist_items': True}, request.options) 2843 'pubsub#persist_items': True}, 2844 request.options.getValues()) 1972 2845 return defer.succeed(None) 1973 2846 … … 2041 2914 def configureSet(request): 2042 2915 self.assertEquals(['pubsub#deliver_payloads'], 2043 request.options. keys())2916 request.options.fields.keys()) 2044 2917 2045 2918 self.resource.getConfigurationOptions = getConfigurationOptions … … 2073 2946 def cb(result): 2074 2947 self.assertEquals('bad-request', result.condition) 2948 self.assertEqual("Unexpected form type 'result'", result.text) 2075 2949 2076 2950 d = self.handleRequest(xml) … … 2183 3057 verify.verifyObject(iwokkel.IPubSubResource, self.resource) 2184 3058 return self.handleRequest(xml) 3059 3060 3061 def test_notifyPublish(self): 3062 """ 3063 Publish notifications are sent to the subscribers. 3064 """ 3065 subscriber = JID('user@example.org') 3066 subscriptions = [pubsub.Subscription('test', subscriber, 'subscribed')] 3067 items = [pubsub.Item('current')] 3068 notifications = [(subscriber, subscriptions, items)] 3069 self.service.notifyPublish(JID('pubsub.example.org'), 'test', 3070 notifications) 3071 message = self.stub.output[-1] 3072 3073 self.assertEquals('message', message.name) 3074 self.assertIdentical(None, message.uri) 3075 self.assertEquals('user@example.org', message['to']) 3076 self.assertEquals('pubsub.example.org', message['from']) 3077 self.assertTrue(message.event) 3078 self.assertEquals(NS_PUBSUB_EVENT, message.event.uri) 3079 self.assertTrue(message.event.items) 3080 self.assertEquals(NS_PUBSUB_EVENT, message.event.items.uri) 3081 self.assertTrue(message.event.items.hasAttribute('node')) 3082 self.assertEquals('test', message.event.items['node']) 3083 itemElements = list(domish.generateElementsQNamed( 3084 message.event.items.children, 'item', NS_PUBSUB_EVENT)) 3085 self.assertEquals(1, len(itemElements)) 3086 self.assertEquals('current', itemElements[0].getAttribute('id')) 3087 3088 3089 def test_notifyPublishCollection(self): 3090 """ 3091 Publish notifications are sent to the subscribers of collections. 3092 3093 The node the item was published to is on the C{items} element, while 3094 the subscribed-to node is in the C{'Collections'} SHIM header. 3095 """ 3096 subscriber = JID('user@example.org') 3097 subscriptions = [pubsub.Subscription('', subscriber, 'subscribed')] 3098 items = [pubsub.Item('current')] 3099 notifications = [(subscriber, subscriptions, items)] 3100 self.service.notifyPublish(JID('pubsub.example.org'), 'test', 3101 notifications) 3102 message = self.stub.output[-1] 3103 3104 self.assertTrue(message.event.items.hasAttribute('node')) 3105 self.assertEquals('test', message.event.items['node']) 3106 headers = shim.extractHeaders(message) 3107 self.assertIn('Collection', headers) 3108 self.assertIn('', headers['Collection']) 2185 3109 2186 3110 … … 2288 3212 def test_on_affiliationsGet(self): 2289 3213 """ 2290 Getting subscription options is not supported. 3214 Getting node affiliations should have. 3215 """ 3216 3217 xml = """ 3218 <iq type='get' to='pubsub.example.org' 3219 from='user@example.org'> 3220 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 3221 <affiliations node='test'/> 3222 </pubsub> 3223 </iq> 3224 """ 3225 3226 def affiliationsGet(request): 3227 self.assertEquals('test', request.nodeIdentifier) 3228 return defer.succeed({JID('user@example.org'): 'owner'}) 3229 3230 def cb(element): 3231 self.assertEquals(u'pubsub', element.name) 3232 self.assertEquals(NS_PUBSUB_OWNER, element.uri) 3233 self.assertEquals(NS_PUBSUB_OWNER, element.affiliations.uri) 3234 self.assertEquals(u'test', element.affiliations[u'node']) 3235 children = list(element.affiliations.elements()) 3236 self.assertEquals(1, len(children)) 3237 affiliation = children[0] 3238 self.assertEquals(u'affiliation', affiliation.name) 3239 self.assertEquals(NS_PUBSUB_OWNER, affiliation.uri) 3240 self.assertEquals(u'user@example.org', affiliation[u'jid']) 3241 self.assertEquals(u'owner', affiliation[u'affiliation']) 3242 3243 self.resource.affiliationsGet = affiliationsGet 3244 verify.verifyObject(iwokkel.IPubSubResource, self.resource) 3245 d = self.handleRequest(xml) 3246 d.addCallback(cb) 3247 return d 3248 3249 3250 def test_on_affiliationsGetEmptyNode(self): 3251 """ 3252 Getting node affiliations without node should assume empty node. 2291 3253 """ 2292 3254 … … 2300 3262 """ 2301 3263 3264 def affiliationsGet(request): 3265 self.assertIdentical('', request.nodeIdentifier) 3266 return defer.succeed({}) 3267 3268 def cb(element): 3269 self.assertFalse(element.affiliations.hasAttribute(u'node')) 3270 3271 self.resource.affiliationsGet = affiliationsGet 3272 verify.verifyObject(iwokkel.IPubSubResource, self.resource) 3273 d = self.handleRequest(xml) 3274 d.addCallback(cb) 3275 return d 3276 3277 3278 def test_on_affiliationsSet(self): 3279 """ 3280 Setting node affiliations has the affiliations to be modified. 3281 """ 3282 3283 xml = """ 3284 <iq type='set' to='pubsub.example.org' 3285 from='user@example.org'> 3286 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 3287 <affiliations node='test'> 3288 <affiliation jid='other@example.org' affiliation='publisher'/> 3289 </affiliations> 3290 </pubsub> 3291 </iq> 3292 """ 3293 3294 def affiliationsSet(request): 3295 self.assertEquals(u'test', request.nodeIdentifier) 3296 otherJID = JID(u'other@example.org') 3297 self.assertIn(otherJID, request.affiliations) 3298 self.assertEquals(u'publisher', request.affiliations[otherJID]) 3299 3300 self.resource.affiliationsSet = affiliationsSet 3301 return self.handleRequest(xml) 3302 3303 3304 def test_on_affiliationsSetBareJID(self): 3305 """ 3306 Affiliations are always on the bare JID. 3307 """ 3308 3309 xml = """ 3310 <iq type='set' to='pubsub.example.org' 3311 from='user@example.org'> 3312 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 3313 <affiliations node='test'> 3314 <affiliation jid='other@example.org/Home' 3315 affiliation='publisher'/> 3316 </affiliations> 3317 </pubsub> 3318 </iq> 3319 """ 3320 3321 def affiliationsSet(request): 3322 otherJID = JID(u'other@example.org') 3323 self.assertIn(otherJID, request.affiliations) 3324 3325 self.resource.affiliationsSet = affiliationsSet 3326 return self.handleRequest(xml) 3327 3328 3329 def test_on_affiliationsSetMultipleForSameEntity(self): 3330 """ 3331 Setting node affiliations can only have one item per entity. 3332 """ 3333 3334 xml = """ 3335 <iq type='set' to='pubsub.example.org' 3336 from='user@example.org'> 3337 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 3338 <affiliations node='test'> 3339 <affiliation jid='other@example.org' affiliation='publisher'/> 3340 <affiliation jid='other@example.org' affiliation='owner'/> 3341 </affiliations> 3342 </pubsub> 3343 </iq> 3344 """ 3345 2302 3346 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('modify-affiliations', 2307 result.appCondition['feature']) 3347 self.assertEquals('bad-request', result.condition) 2308 3348 2309 3349 d = self.handleRequest(xml) … … 2313 3353 2314 3354 2315 def test_on_affiliationsSet (self):2316 """ 2317 Setting subscription options is not supported.3355 def test_on_affiliationsSetMissingJID(self): 3356 """ 3357 Setting node affiliations must include a JID per affiliation. 2318 3358 """ 2319 3359 … … 2322 3362 from='user@example.org'> 2323 3363 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 2324 <affiliations/> 3364 <affiliations node='test'> 3365 <affiliation affiliation='publisher'/> 3366 </affiliations> 2325 3367 </pubsub> 2326 3368 </iq> … … 2328 3370 2329 3371 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('modify-affiliations', 2334 result.appCondition['feature']) 3372 self.assertEquals('bad-request', result.condition) 3373 3374 d = self.handleRequest(xml) 3375 self.assertFailure(d, error.StanzaError) 3376 d.addCallback(cb) 3377 return d 3378 3379 3380 def test_on_affiliationsSetMissingAffiliation(self): 3381 """ 3382 Setting node affiliations must include an affiliation. 3383 """ 3384 3385 xml = """ 3386 <iq type='set' to='pubsub.example.org' 3387 from='user@example.org'> 3388 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 3389 <affiliations node='test'> 3390 <affiliation jid='other@example.org'/> 3391 </affiliations> 3392 </pubsub> 3393 </iq> 3394 """ 3395 3396 def cb(result): 3397 self.assertEquals('bad-request', result.condition) 2335 3398 2336 3399 d = self.handleRequest(xml) … … 2347 3410 self.service = pubsub.PubSubService() 2348 3411 self.service.send = self.stub.xmlstream.send 3412 3413 3414 def test_getDiscoInfo(self): 3415 """ 3416 Test getDiscoInfo calls getNodeInfo and returns some minimal info. 3417 """ 3418 def cb(info): 3419 discoInfo = disco.DiscoInfo() 3420 for item in info: 3421 discoInfo.append(item) 3422 self.assertIn(('pubsub', 'service'), discoInfo.identities) 3423 self.assertIn(disco.NS_DISCO_ITEMS, discoInfo.features) 3424 3425 d = self.service.getDiscoInfo(JID('user@example.org/home'), 3426 JID('pubsub.example.org'), '') 3427 d.addCallback(cb) 3428 return d 2349 3429 2350 3430 … … 2596 3676 2597 3677 3678 def test_setConfigurationOptionsDict(self): 3679 """ 3680 Options should be passed as a dictionary, not a form. 3681 """ 3682 3683 xml = """ 3684 <iq type='set' to='pubsub.example.org' 3685 from='user@example.org'> 3686 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 3687 <configure node='test'> 3688 <x xmlns='jabber:x:data' type='submit'> 3689 <field var='FORM_TYPE' type='hidden'> 3690 <value>http://jabber.org/protocol/pubsub#node_config</value> 3691 </field> 3692 <field var='pubsub#deliver_payloads'><value>0</value></field> 3693 <field var='pubsub#persist_items'><value>1</value></field> 3694 </x> 3695 </configure> 3696 </pubsub> 3697 </iq> 3698 """ 3699 3700 def getConfigurationOptions(): 3701 return { 3702 "pubsub#persist_items": 3703 {"type": "boolean", 3704 "label": "Persist items to storage"}, 3705 "pubsub#deliver_payloads": 3706 {"type": "boolean", 3707 "label": "Deliver payloads with event notifications"} 3708 } 3709 3710 def setConfiguration(requestor, service, nodeIdentifier, options): 3711 self.assertEquals({'pubsub#deliver_payloads': False, 3712 'pubsub#persist_items': True}, options) 3713 3714 3715 self.service.getConfigurationOptions = getConfigurationOptions 3716 self.service.setConfiguration = setConfiguration 3717 return self.handleRequest(xml) 3718 3719 2598 3720 def test_items(self): 2599 3721 """ … … 2692 3814 self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri) 2693 3815 self.assertEquals('delete-nodes', result.appCondition['feature']) 3816 3817 d = self.handleRequest(xml) 3818 self.assertFailure(d, error.StanzaError) 3819 d.addCallback(cb) 3820 return d 3821 3822 3823 def test_unknown(self): 3824 """ 3825 Unknown verb yields unsupported error. 3826 """ 3827 xml = """ 3828 <iq type='get' to='pubsub.example.org' 3829 from='user@example.org'> 3830 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 3831 <affiliations node='test'/> 3832 </pubsub> 3833 </iq> 3834 """ 3835 3836 def cb(result): 3837 self.assertEquals('feature-not-implemented', result.condition) 3838 self.assertEquals('unsupported', result.appCondition.name) 3839 self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri) 2694 3840 2695 3841 d = self.handleRequest(xml) … … 2951 4097 d.addCallback(cb) 2952 4098 return d 4099 4100 4101 def test_affiliationsGet(self): 4102 """ 4103 Non-overridden owner affiliations get yields unsupported error. 4104 """ 4105 4106 def cb(result): 4107 self.assertEquals('feature-not-implemented', result.condition) 4108 self.assertEquals('unsupported', result.appCondition.name) 4109 self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri) 4110 self.assertEquals('modify-affiliations', 4111 result.appCondition['feature']) 4112 4113 d = self.resource.affiliationsGet(pubsub.PubSubRequest()) 4114 self.assertFailure(d, error.StanzaError) 4115 d.addCallback(cb) 4116 return d 4117 4118 4119 def test_affiliationsSet(self): 4120 """ 4121 Non-overridden owner affiliations set yields unsupported error. 4122 """ 4123 4124 def cb(result): 4125 self.assertEquals('feature-not-implemented', result.condition) 4126 self.assertEquals('unsupported', result.appCondition.name) 4127 self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri) 4128 self.assertEquals('modify-affiliations', 4129 result.appCondition['feature']) 4130 4131 d = self.resource.affiliationsSet(pubsub.PubSubRequest()) 4132 self.assertFailure(d, error.StanzaError) 4133 d.addCallback(cb) 4134 return d -
wokkel/test/test_server.py
- Property exe set to *
r55 r96 1 # Copyright (c) 2003-2008 Ralph Meijer1 # Copyright (c) Ralph Meijer. 2 2 # See LICENSE for details. 3 3 -
wokkel/test/test_shim.py
- Property exe set to *
r27 r96 1 1 # -*- test-case-name: wokkel.test.test_shim -*- 2 2 # 3 # Copyright (c) 2003-2008 Ralph Meijer3 # Copyright (c) Ralph Meijer. 4 4 # See LICENSE for details. 5 5 -
wokkel/test/test_subprotocols.py
- Property exe set to *
r45 r101 1 # Copyright (c) 2003-2007 Ralph Meijer1 # Copyright (c) Ralph Meijer. 2 2 # See LICENSE for details. 3 3 … … 10 10 from twisted.trial import unittest 11 11 from twisted.test import proto_helpers 12 from twisted.internet import defer 12 from twisted.internet import defer, task 13 from twisted.internet.error import ConnectionDone 14 from twisted.python import failure 13 15 from twisted.words.xish import domish 14 16 from twisted.words.protocols.jabber import error, xmlstream 15 17 16 from wokkel import iwokkel, subprotocols18 from wokkel import generic, iwokkel, subprotocols 17 19 18 20 class DummyFactory(object): … … 56 58 57 59 60 class FailureReasonXMPPHandler(subprotocols.XMPPHandler): 61 """ 62 Dummy handler specifically for failure Reason tests. 63 """ 64 def __init__(self): 65 self.gotFailureReason = False 66 67 68 def connectionLost(self, reason): 69 if isinstance(reason, failure.Failure): 70 self.gotFailureReason = True 71 72 73 74 class IQGetStanza(generic.Stanza): 75 timeout = None 76 77 stanzaKind = 'iq' 78 stanzaType = 'get' 79 stanzaID = 'test' 80 81 82 58 83 class XMPPHandlerTest(unittest.TestCase): 59 84 """ … … 111 136 112 137 138 def test_request(self): 139 """ 140 A request is passed up to the stream manager. 141 """ 142 class DummyStreamManager(object): 143 def __init__(self): 144 self.requests = [] 145 146 def request(self, request): 147 self.requests.append(request) 148 return defer.succeed(None) 149 150 handler = subprotocols.XMPPHandler() 151 handler.parent = DummyStreamManager() 152 request = IQGetStanza() 153 d = handler.request(request) 154 self.assertEquals(1, len(handler.parent.requests)) 155 self.assertIdentical(request, handler.parent.requests[-1]) 156 return d 157 158 113 159 114 160 class XMPPHandlerCollectionTest(unittest.TestCase): … … 156 202 157 203 def setUp(self): 204 factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator()) 205 self.clock = task.Clock() 206 self.streamManager = subprotocols.StreamManager(factory, self.clock) 207 self.xmlstream = factory.buildProtocol(None) 208 self.transport = proto_helpers.StringTransport() 209 self.xmlstream.transport = self.transport 210 211 self.request = IQGetStanza() 212 213 def _streamStarted(self): 214 """ 215 Bring the test stream to the initialized state. 216 """ 217 self.xmlstream.connectionMade() 218 self.xmlstream.dataReceived( 219 "<stream:stream xmlns='jabber:client' " 220 "xmlns:stream='http://etherx.jabber.org/streams' " 221 "from='example.com' id='12345'>") 222 self.xmlstream.dispatch(self.xmlstream, "//event/stream/authd") 223 224 225 def test_basic(self): 226 """ 227 Test correct initialization and setup of factory observers. 228 """ 158 229 factory = DummyFactory() 159 self.streamManager = subprotocols.StreamManager(factory) 160 161 def test_basic(self): 162 """ 163 Test correct initialization and setup of factory observers. 164 """ 165 sm = self.streamManager 230 sm = subprotocols.StreamManager(factory) 166 231 self.assertIdentical(None, sm.xmlstream) 167 232 self.assertEquals([], sm.handlers) … … 235 300 def test_disconnected(self): 236 301 """ 237 Test that protocol handlers have their connectionLost method 238 called when the XML stream is disconnected. 239 """ 240 sm = self.streamManager 241 handler = DummyXMPPHandler() 242 handler.setHandlerParent(sm) 243 xs = xmlstream.XmlStream(xmlstream.Authenticator()) 244 sm._disconnected(xs) 302 Protocol handlers have connectionLost called on stream disconnect. 303 """ 304 sm = self.streamManager 305 handler = DummyXMPPHandler() 306 handler.setHandlerParent(sm) 307 sm._disconnected(None) 245 308 self.assertEquals(0, handler.doneMade) 246 309 self.assertEquals(0, handler.doneInitialized) … … 248 311 249 312 313 def test_disconnectedReason(self): 314 """ 315 A L{STREAM_END_EVENT} results in L{StreamManager} firing the handlers 316 L{connectionLost} methods, passing a L{failure.Failure} reason. 317 """ 318 sm = self.streamManager 319 handler = FailureReasonXMPPHandler() 320 handler.setHandlerParent(sm) 321 xs = xmlstream.XmlStream(xmlstream.Authenticator()) 322 sm._disconnected(failure.Failure(Exception("no reason"))) 323 self.assertEquals(True, handler.gotFailureReason) 324 325 250 326 def test_addHandler(self): 251 327 """ … … 261 337 262 338 339 def test_addHandlerConnected(self): 340 """ 341 Adding a handler when connected doesn't call connectionInitialized. 342 """ 343 sm = self.streamManager 344 xs = xmlstream.XmlStream(xmlstream.Authenticator()) 345 sm._connected(xs) 346 handler = DummyXMPPHandler() 347 handler.setHandlerParent(sm) 348 349 self.assertEquals(1, handler.doneMade) 350 self.assertEquals(0, handler.doneInitialized) 351 self.assertEquals(0, handler.doneLost) 352 353 354 def test_addHandlerConnectedNested(self): 355 """ 356 Adding a handler in connectionMade doesn't cause 2nd call. 357 """ 358 class NestingHandler(DummyXMPPHandler): 359 nestedHandler = None 360 361 def connectionMade(self): 362 DummyXMPPHandler.connectionMade(self) 363 self.nestedHandler = DummyXMPPHandler() 364 self.nestedHandler.setHandlerParent(self.parent) 365 366 sm = self.streamManager 367 xs = xmlstream.XmlStream(xmlstream.Authenticator()) 368 handler = NestingHandler() 369 handler.setHandlerParent(sm) 370 sm._connected(xs) 371 372 self.assertEquals(1, handler.doneMade) 373 self.assertEquals(0, handler.doneInitialized) 374 self.assertEquals(0, handler.doneLost) 375 376 self.assertEquals(1, handler.nestedHandler.doneMade) 377 self.assertEquals(0, handler.nestedHandler.doneInitialized) 378 self.assertEquals(0, handler.nestedHandler.doneLost) 379 380 381 263 382 def test_addHandlerInitialized(self): 264 383 """ … … 281 400 self.assertEquals(0, handler.doneLost) 282 401 402 403 def test_addHandlerInitializedNested(self): 404 """ 405 Adding a handler in connectionInitialized doesn't cause 2nd call. 406 """ 407 class NestingHandler(DummyXMPPHandler): 408 nestedHandler = None 409 410 def connectionInitialized(self): 411 DummyXMPPHandler.connectionInitialized(self) 412 self.nestedHandler = DummyXMPPHandler() 413 self.nestedHandler.setHandlerParent(self.parent) 414 415 sm = self.streamManager 416 xs = xmlstream.XmlStream(xmlstream.Authenticator()) 417 handler = NestingHandler() 418 handler.setHandlerParent(sm) 419 sm._connected(xs) 420 sm._authd(xs) 421 422 self.assertEquals(1, handler.doneMade) 423 self.assertEquals(1, handler.doneInitialized) 424 self.assertEquals(0, handler.doneLost) 425 426 self.assertEquals(1, handler.nestedHandler.doneMade) 427 self.assertEquals(1, handler.nestedHandler.doneInitialized) 428 self.assertEquals(0, handler.nestedHandler.doneLost) 429 430 431 def test_addHandlerConnectionLostNested(self): 432 """ 433 Adding a handler in connectionLost doesn't call connectionLost there. 434 """ 435 class NestingHandler(DummyXMPPHandler): 436 nestedHandler = None 437 438 def connectionLost(self, reason): 439 DummyXMPPHandler.connectionLost(self, reason) 440 self.nestedHandler = DummyXMPPHandler() 441 self.nestedHandler.setHandlerParent(self.parent) 442 443 sm = self.streamManager 444 xs = xmlstream.XmlStream(xmlstream.Authenticator()) 445 handler = NestingHandler() 446 handler.setHandlerParent(sm) 447 sm._connected(xs) 448 sm._authd(xs) 449 sm._disconnected(xs) 450 451 self.assertEquals(1, handler.doneMade) 452 self.assertEquals(1, handler.doneInitialized) 453 self.assertEquals(1, handler.doneLost) 454 455 self.assertEquals(0, handler.nestedHandler.doneMade) 456 self.assertEquals(0, handler.nestedHandler.doneInitialized) 457 self.assertEquals(0, handler.nestedHandler.doneLost) 458 459 460 283 461 def test_removeHandler(self): 284 462 """ … … 291 469 self.assertNotIn(handler, sm) 292 470 self.assertIdentical(None, handler.parent) 471 293 472 294 473 def test_sendInitialized(self): … … 381 560 self.assertEquals("", xs.transport.value()) 382 561 self.assertEquals("<presence/>", sm._packetQueue[0]) 562 563 564 def test_requestSendInitialized(self): 565 """ 566 A request is sent out over the wire when the stream is initialized. 567 """ 568 self._streamStarted() 569 570 self.streamManager.request(self.request) 571 expected = u"<iq type='get' id='%s'/>" % self.request.stanzaID 572 self.assertEquals(expected, self.transport.value()) 573 574 575 def test_requestSendInitializedFreshID(self): 576 """ 577 A request without an ID gets a fresh one upon send. 578 """ 579 self._streamStarted() 580 581 self.request.stanzaID = None 582 self.streamManager.request(self.request) 583 self.assertNotIdentical(None, self.request.stanzaID) 584 expected = u"<iq type='get' id='%s'/>" % self.request.stanzaID 585 self.assertEquals(expected, self.transport.value()) 586 587 588 def test_requestSendNotConnected(self): 589 """ 590 A request is queued until a stream is initialized. 591 """ 592 handler = DummyXMPPHandler() 593 self.streamManager.addHandler(handler) 594 595 self.streamManager.request(self.request) 596 expected = u"<iq type='get' id='test'/>" 597 598 xs = self.xmlstream 599 self.assertEquals("", xs.transport.value()) 600 601 xs.connectionMade() 602 self.assertEquals("", xs.transport.value()) 603 604 xs.dataReceived("<stream:stream xmlns='jabber:client' " 605 "xmlns:stream='http://etherx.jabber.org/streams' " 606 "from='example.com' id='12345'>") 607 xs.dispatch(xs, "//event/stream/authd") 608 609 self.assertEquals(expected, xs.transport.value()) 610 self.assertFalse(self.streamManager._packetQueue) 611 612 613 def test_requestResultResponse(self): 614 """ 615 A result response gets the request deferred fired with the response. 616 """ 617 def cb(result): 618 self.assertEquals(result['type'], 'result') 619 620 self._streamStarted() 621 d = self.streamManager.request(self.request) 622 d.addCallback(cb) 623 624 xs = self.xmlstream 625 xs.dataReceived("<iq type='result' id='test'/>") 626 return d 627 628 629 def test_requestErrorResponse(self): 630 """ 631 An error response gets the request deferred fired with a failure. 632 """ 633 self._streamStarted() 634 d = self.streamManager.request(self.request) 635 self.assertFailure(d, error.StanzaError) 636 637 xs = self.xmlstream 638 xs.dataReceived("<iq type='error' id='test'/>") 639 return d 640 641 642 def test_requestNonTrackedResponse(self): 643 """ 644 Test that untracked iq responses don't trigger any action. 645 646 Untracked means that the id of the incoming response iq is not 647 in the stream's C{iqDeferreds} dictionary. 648 """ 649 # Set up a fallback handler that checks the stanza's handled attribute. 650 # If that is set to True, the iq tracker claims to have handled the 651 # response. 652 dispatched = [] 653 def cb(iq): 654 dispatched.append(iq) 655 656 self._streamStarted() 657 self.xmlstream.addObserver("/iq", cb, -1) 658 659 # Receive an untracked iq response 660 self.xmlstream.dataReceived("<iq type='result' id='other'/>") 661 self.assertEquals(1, len(dispatched)) 662 self.assertFalse(getattr(dispatched[-1], 'handled', False)) 663 664 665 def test_requestCleanup(self): 666 """ 667 Test if the deferred associated with an iq request is removed 668 from the list kept in the L{XmlStream} object after it has 669 been fired. 670 """ 671 self._streamStarted() 672 d = self.streamManager.request(self.request) 673 xs = self.xmlstream 674 xs.dataReceived("<iq type='result' id='test'/>") 675 self.assertNotIn('test', self.streamManager._iqDeferreds) 676 return d 677 678 679 def test_requestDisconnectCleanup(self): 680 """ 681 Test if deferreds for iq's that haven't yet received a response 682 have their errback called on stream disconnect. 683 """ 684 d = self.streamManager.request(self.request) 685 xs = self.xmlstream 686 xs.connectionLost(failure.Failure(ConnectionDone())) 687 self.assertFailure(d, ConnectionDone) 688 return d 689 690 691 def test_requestNoModifyingDict(self): 692 """ 693 Test to make sure the errbacks cannot cause the iteration of the 694 iqDeferreds to blow up in our face. 695 """ 696 697 def eb(failure): 698 d = xmlstream.IQ(self.xmlstream).send() 699 d.addErrback(eb) 700 701 d = self.streamManager.request(self.request) 702 d.addErrback(eb) 703 self.xmlstream.connectionLost(failure.Failure(ConnectionDone())) 704 return d 705 706 707 def test_requestTimingOut(self): 708 """ 709 Test that an iq request with a defined timeout times out. 710 """ 711 self.request.timeout = 60 712 d = self.streamManager.request(self.request) 713 self.assertFailure(d, xmlstream.TimeoutError) 714 715 self.clock.pump([1, 60]) 716 self.assertFalse(self.clock.calls) 717 self.assertFalse(self.streamManager._iqDeferreds) 718 return d 719 720 721 def test_requestNotTimingOut(self): 722 """ 723 Test that an iq request with a defined timeout does not time out 724 when a response was received before the timeout period elapsed. 725 """ 726 self._streamStarted() 727 self.request.timeout = 60 728 d = self.streamManager.request(self.request) 729 self.clock.callLater(1, self.xmlstream.dataReceived, 730 "<iq type='result' id='test'/>") 731 self.clock.pump([1, 1]) 732 self.assertFalse(self.clock.calls) 733 return d 734 735 736 def test_requestDisconnectTimeoutCancellation(self): 737 """ 738 Test if timeouts for iq's that haven't yet received a response 739 are cancelled on stream disconnect. 740 """ 741 742 self.request.timeout = 60 743 d = self.streamManager.request(self.request) 744 745 self.xmlstream.connectionLost(failure.Failure(ConnectionDone())) 746 self.assertFailure(d, ConnectionDone) 747 self.assertFalse(self.clock.calls) 748 return d 749 750 751 def test_requestNotIQ(self): 752 """ 753 The request stanza must be an iq. 754 """ 755 stanza = generic.Stanza() 756 stanza.stanzaKind = 'message' 757 758 d = self.streamManager.request(stanza) 759 self.assertFailure(d, ValueError) 760 761 762 def test_requestNotResult(self): 763 """ 764 The request stanza cannot be of type 'result'. 765 """ 766 stanza = generic.Stanza() 767 stanza.stanzaKind = 'iq' 768 stanza.stanzaType = 'result' 769 770 d = self.streamManager.request(stanza) 771 self.assertFailure(d, ValueError) 772 773 774 def test_requestNotError(self): 775 """ 776 The request stanza cannot be of type 'error'. 777 """ 778 stanza = generic.Stanza() 779 stanza.stanzaKind = 'iq' 780 stanza.stanzaType = 'error' 781 782 d = self.streamManager.request(stanza) 783 self.assertFailure(d, ValueError) 383 784 384 785 -
wokkel/test/test_xmppim.py
r68 r96 1 # Copyright (c) 2003-2009 Ralph Meijer1 # Copyright (c) Ralph Meijer. 2 2 # See LICENSE for details 3 3 -
wokkel/xmppim.py
- Property exe set to *
r68 r96 1 1 # -*- test-case-name: wokkel.test.test_xmppim -*- 2 2 # 3 # Copyright (c) 2003-2009 Ralph Meijer3 # Copyright (c) Ralph Meijer. 4 4 # See LICENSE for details. 5 5
Note: See TracChangeset
for help on using the changeset viewer.