Mercurial > libervia-pubsub
comparison sat_pubsub/test/test_backend.py @ 232:923281d4c5bc
renamed idavoll directory to sat_pubsub
author | Goffi <goffi@goffi.org> |
---|---|
date | Thu, 17 May 2012 12:48:14 +0200 |
parents | idavoll/test/test_backend.py@0eafdced5f24 |
children | 564ae55219e1 |
comparison
equal
deleted
inserted
replaced
231:d99047cd90f9 | 232:923281d4c5bc |
---|---|
1 # Copyright (c) 2003-2010 Ralph Meijer | |
2 # See LICENSE for details. | |
3 | |
4 """ | |
5 Tests for L{idavoll.backend}. | |
6 """ | |
7 | |
8 from zope.interface import implements | |
9 from zope.interface.verify import verifyObject | |
10 | |
11 from twisted.internet import defer | |
12 from twisted.trial import unittest | |
13 from twisted.words.protocols.jabber import jid | |
14 from twisted.words.protocols.jabber.error import StanzaError | |
15 | |
16 from wokkel import iwokkel, pubsub | |
17 | |
18 from idavoll import backend, error, iidavoll | |
19 | |
20 OWNER = jid.JID('owner@example.com') | |
21 OWNER_FULL = jid.JID('owner@example.com/home') | |
22 SERVICE = jid.JID('test.example.org') | |
23 NS_PUBSUB = 'http://jabber.org/protocol/pubsub' | |
24 | |
25 class BackendTest(unittest.TestCase): | |
26 | |
27 def test_interfaceIBackend(self): | |
28 self.assertTrue(verifyObject(iidavoll.IBackendService, | |
29 backend.BackendService(None))) | |
30 | |
31 | |
32 def test_deleteNode(self): | |
33 class TestNode: | |
34 nodeIdentifier = 'to-be-deleted' | |
35 def getAffiliation(self, entity): | |
36 if entity.userhostJID() == OWNER: | |
37 return defer.succeed('owner') | |
38 | |
39 class TestStorage: | |
40 def __init__(self): | |
41 self.deleteCalled = [] | |
42 | |
43 def getNode(self, nodeIdentifier): | |
44 return defer.succeed(TestNode()) | |
45 | |
46 def deleteNode(self, nodeIdentifier): | |
47 if nodeIdentifier in ['to-be-deleted']: | |
48 self.deleteCalled.append(nodeIdentifier) | |
49 return defer.succeed(None) | |
50 else: | |
51 return defer.fail(error.NodeNotFound()) | |
52 | |
53 def preDelete(data): | |
54 self.assertFalse(self.storage.deleteCalled) | |
55 preDeleteCalled.append(data) | |
56 return defer.succeed(None) | |
57 | |
58 def cb(result): | |
59 self.assertEquals(1, len(preDeleteCalled)) | |
60 data = preDeleteCalled[-1] | |
61 self.assertEquals('to-be-deleted', data['nodeIdentifier']) | |
62 self.assertTrue(self.storage.deleteCalled) | |
63 | |
64 self.storage = TestStorage() | |
65 self.backend = backend.BackendService(self.storage) | |
66 | |
67 preDeleteCalled = [] | |
68 | |
69 self.backend.registerPreDelete(preDelete) | |
70 d = self.backend.deleteNode('to-be-deleted', OWNER_FULL) | |
71 d.addCallback(cb) | |
72 return d | |
73 | |
74 | |
75 def test_deleteNodeRedirect(self): | |
76 uri = 'xmpp:%s?;node=test2' % (SERVICE.full(),) | |
77 | |
78 class TestNode: | |
79 nodeIdentifier = 'to-be-deleted' | |
80 def getAffiliation(self, entity): | |
81 if entity.userhostJID() == OWNER: | |
82 return defer.succeed('owner') | |
83 | |
84 class TestStorage: | |
85 def __init__(self): | |
86 self.deleteCalled = [] | |
87 | |
88 def getNode(self, nodeIdentifier): | |
89 return defer.succeed(TestNode()) | |
90 | |
91 def deleteNode(self, nodeIdentifier): | |
92 if nodeIdentifier in ['to-be-deleted']: | |
93 self.deleteCalled.append(nodeIdentifier) | |
94 return defer.succeed(None) | |
95 else: | |
96 return defer.fail(error.NodeNotFound()) | |
97 | |
98 def preDelete(data): | |
99 self.assertFalse(self.storage.deleteCalled) | |
100 preDeleteCalled.append(data) | |
101 return defer.succeed(None) | |
102 | |
103 def cb(result): | |
104 self.assertEquals(1, len(preDeleteCalled)) | |
105 data = preDeleteCalled[-1] | |
106 self.assertEquals('to-be-deleted', data['nodeIdentifier']) | |
107 self.assertEquals(uri, data['redirectURI']) | |
108 self.assertTrue(self.storage.deleteCalled) | |
109 | |
110 self.storage = TestStorage() | |
111 self.backend = backend.BackendService(self.storage) | |
112 | |
113 preDeleteCalled = [] | |
114 | |
115 self.backend.registerPreDelete(preDelete) | |
116 d = self.backend.deleteNode('to-be-deleted', OWNER, redirectURI=uri) | |
117 d.addCallback(cb) | |
118 return d | |
119 | |
120 | |
121 def test_createNodeNoID(self): | |
122 """ | |
123 Test creation of a node without a given node identifier. | |
124 """ | |
125 class TestStorage: | |
126 def getDefaultConfiguration(self, nodeType): | |
127 return {} | |
128 | |
129 def createNode(self, nodeIdentifier, requestor, config): | |
130 self.nodeIdentifier = nodeIdentifier | |
131 return defer.succeed(None) | |
132 | |
133 self.storage = TestStorage() | |
134 self.backend = backend.BackendService(self.storage) | |
135 self.storage.backend = self.backend | |
136 | |
137 def checkID(nodeIdentifier): | |
138 self.assertNotIdentical(None, nodeIdentifier) | |
139 self.assertIdentical(self.storage.nodeIdentifier, nodeIdentifier) | |
140 | |
141 d = self.backend.createNode(None, OWNER_FULL) | |
142 d.addCallback(checkID) | |
143 return d | |
144 | |
145 class NodeStore: | |
146 """ | |
147 I just store nodes to pose as an L{IStorage} implementation. | |
148 """ | |
149 def __init__(self, nodes): | |
150 self.nodes = nodes | |
151 | |
152 def getNode(self, nodeIdentifier): | |
153 try: | |
154 return defer.succeed(self.nodes[nodeIdentifier]) | |
155 except KeyError: | |
156 return defer.fail(error.NodeNotFound()) | |
157 | |
158 | |
159 def test_getNotifications(self): | |
160 """ | |
161 Ensure subscribers show up in the notification list. | |
162 """ | |
163 item = pubsub.Item() | |
164 sub = pubsub.Subscription('test', OWNER, 'subscribed') | |
165 | |
166 class TestNode: | |
167 def getSubscriptions(self, state=None): | |
168 return [sub] | |
169 | |
170 def cb(result): | |
171 self.assertEquals(1, len(result)) | |
172 subscriber, subscriptions, items = result[-1] | |
173 | |
174 self.assertEquals(OWNER, subscriber) | |
175 self.assertEquals(set([sub]), subscriptions) | |
176 self.assertEquals([item], items) | |
177 | |
178 self.storage = self.NodeStore({'test': TestNode()}) | |
179 self.backend = backend.BackendService(self.storage) | |
180 d = self.backend.getNotifications('test', [item]) | |
181 d.addCallback(cb) | |
182 return d | |
183 | |
184 def test_getNotificationsRoot(self): | |
185 """ | |
186 Ensure subscribers to the root node show up in the notification list | |
187 for leaf nodes. | |
188 | |
189 This assumes a flat node relationship model with exactly one collection | |
190 node: the root node. Each leaf node is automatically a child node | |
191 of the root node. | |
192 """ | |
193 item = pubsub.Item() | |
194 subRoot = pubsub.Subscription('', OWNER, 'subscribed') | |
195 | |
196 class TestNode: | |
197 def getSubscriptions(self, state=None): | |
198 return [] | |
199 | |
200 class TestRootNode: | |
201 def getSubscriptions(self, state=None): | |
202 return [subRoot] | |
203 | |
204 def cb(result): | |
205 self.assertEquals(1, len(result)) | |
206 subscriber, subscriptions, items = result[-1] | |
207 self.assertEquals(OWNER, subscriber) | |
208 self.assertEquals(set([subRoot]), subscriptions) | |
209 self.assertEquals([item], items) | |
210 | |
211 self.storage = self.NodeStore({'test': TestNode(), | |
212 '': TestRootNode()}) | |
213 self.backend = backend.BackendService(self.storage) | |
214 d = self.backend.getNotifications('test', [item]) | |
215 d.addCallback(cb) | |
216 return d | |
217 | |
218 | |
219 def test_getNotificationsMultipleNodes(self): | |
220 """ | |
221 Ensure that entities that subscribe to a leaf node as well as the | |
222 root node get exactly one notification. | |
223 """ | |
224 item = pubsub.Item() | |
225 sub = pubsub.Subscription('test', OWNER, 'subscribed') | |
226 subRoot = pubsub.Subscription('', OWNER, 'subscribed') | |
227 | |
228 class TestNode: | |
229 def getSubscriptions(self, state=None): | |
230 return [sub] | |
231 | |
232 class TestRootNode: | |
233 def getSubscriptions(self, state=None): | |
234 return [subRoot] | |
235 | |
236 def cb(result): | |
237 self.assertEquals(1, len(result)) | |
238 subscriber, subscriptions, items = result[-1] | |
239 | |
240 self.assertEquals(OWNER, subscriber) | |
241 self.assertEquals(set([sub, subRoot]), subscriptions) | |
242 self.assertEquals([item], items) | |
243 | |
244 self.storage = self.NodeStore({'test': TestNode(), | |
245 '': TestRootNode()}) | |
246 self.backend = backend.BackendService(self.storage) | |
247 d = self.backend.getNotifications('test', [item]) | |
248 d.addCallback(cb) | |
249 return d | |
250 | |
251 | |
252 def test_getDefaultConfiguration(self): | |
253 """ | |
254 L{backend.BackendService.getDefaultConfiguration} should return | |
255 a deferred that fires a dictionary with configuration values. | |
256 """ | |
257 | |
258 class TestStorage: | |
259 def getDefaultConfiguration(self, nodeType): | |
260 return { | |
261 "pubsub#persist_items": True, | |
262 "pubsub#deliver_payloads": True} | |
263 | |
264 def cb(options): | |
265 self.assertIn("pubsub#persist_items", options) | |
266 self.assertEqual(True, options["pubsub#persist_items"]) | |
267 | |
268 self.backend = backend.BackendService(TestStorage()) | |
269 d = self.backend.getDefaultConfiguration('leaf') | |
270 d.addCallback(cb) | |
271 return d | |
272 | |
273 | |
274 def test_getNodeConfiguration(self): | |
275 class testNode: | |
276 nodeIdentifier = 'node' | |
277 def getConfiguration(self): | |
278 return {'pubsub#deliver_payloads': True, | |
279 'pubsub#persist_items': False} | |
280 | |
281 class testStorage: | |
282 def getNode(self, nodeIdentifier): | |
283 return defer.succeed(testNode()) | |
284 | |
285 def cb(options): | |
286 self.assertIn("pubsub#deliver_payloads", options) | |
287 self.assertEqual(True, options["pubsub#deliver_payloads"]) | |
288 self.assertIn("pubsub#persist_items", options) | |
289 self.assertEqual(False, options["pubsub#persist_items"]) | |
290 | |
291 self.storage = testStorage() | |
292 self.backend = backend.BackendService(self.storage) | |
293 self.storage.backend = self.backend | |
294 | |
295 d = self.backend.getNodeConfiguration('node') | |
296 d.addCallback(cb) | |
297 return d | |
298 | |
299 | |
300 def test_setNodeConfiguration(self): | |
301 class testNode: | |
302 nodeIdentifier = 'node' | |
303 def getAffiliation(self, entity): | |
304 if entity.userhostJID() == OWNER: | |
305 return defer.succeed('owner') | |
306 def setConfiguration(self, options): | |
307 self.options = options | |
308 | |
309 class testStorage: | |
310 def __init__(self): | |
311 self.nodes = {'node': testNode()} | |
312 def getNode(self, nodeIdentifier): | |
313 return defer.succeed(self.nodes[nodeIdentifier]) | |
314 | |
315 def checkOptions(node): | |
316 options = node.options | |
317 self.assertIn("pubsub#deliver_payloads", options) | |
318 self.assertEqual(True, options["pubsub#deliver_payloads"]) | |
319 self.assertIn("pubsub#persist_items", options) | |
320 self.assertEqual(False, options["pubsub#persist_items"]) | |
321 | |
322 def cb(result): | |
323 d = self.storage.getNode('node') | |
324 d.addCallback(checkOptions) | |
325 return d | |
326 | |
327 self.storage = testStorage() | |
328 self.backend = backend.BackendService(self.storage) | |
329 self.storage.backend = self.backend | |
330 | |
331 options = {'pubsub#deliver_payloads': True, | |
332 'pubsub#persist_items': False} | |
333 | |
334 d = self.backend.setNodeConfiguration('node', options, OWNER_FULL) | |
335 d.addCallback(cb) | |
336 return d | |
337 | |
338 | |
339 def test_publishNoID(self): | |
340 """ | |
341 Test publish request with an item without a node identifier. | |
342 """ | |
343 class TestNode: | |
344 nodeType = 'leaf' | |
345 nodeIdentifier = 'node' | |
346 def getAffiliation(self, entity): | |
347 if entity.userhostJID() == OWNER: | |
348 return defer.succeed('owner') | |
349 def getConfiguration(self): | |
350 return {'pubsub#deliver_payloads': True, | |
351 'pubsub#persist_items': False} | |
352 | |
353 class TestStorage: | |
354 def getNode(self, nodeIdentifier): | |
355 return defer.succeed(TestNode()) | |
356 | |
357 def checkID(notification): | |
358 self.assertNotIdentical(None, notification['items'][0]['id']) | |
359 | |
360 self.storage = TestStorage() | |
361 self.backend = backend.BackendService(self.storage) | |
362 self.storage.backend = self.backend | |
363 | |
364 self.backend.registerNotifier(checkID) | |
365 | |
366 items = [pubsub.Item()] | |
367 d = self.backend.publish('node', items, OWNER_FULL) | |
368 return d | |
369 | |
370 | |
371 def test_notifyOnSubscription(self): | |
372 """ | |
373 Test notification of last published item on subscription. | |
374 """ | |
375 ITEM = "<item xmlns='%s' id='1'/>" % NS_PUBSUB | |
376 | |
377 class TestNode: | |
378 implements(iidavoll.ILeafNode) | |
379 nodeIdentifier = 'node' | |
380 nodeType = 'leaf' | |
381 def getAffiliation(self, entity): | |
382 if entity is OWNER: | |
383 return defer.succeed('owner') | |
384 def getConfiguration(self): | |
385 return {'pubsub#deliver_payloads': True, | |
386 'pubsub#persist_items': False, | |
387 'pubsub#send_last_published_item': 'on_sub'} | |
388 def getItems(self, maxItems): | |
389 return [ITEM] | |
390 def addSubscription(self, subscriber, state, options): | |
391 self.subscription = pubsub.Subscription('node', subscriber, | |
392 state, options) | |
393 return defer.succeed(None) | |
394 def getSubscription(self, subscriber): | |
395 return defer.succeed(self.subscription) | |
396 | |
397 class TestStorage: | |
398 def getNode(self, nodeIdentifier): | |
399 return defer.succeed(TestNode()) | |
400 | |
401 def cb(data): | |
402 self.assertEquals('node', data['nodeIdentifier']) | |
403 self.assertEquals([ITEM], data['items']) | |
404 self.assertEquals(OWNER, data['subscription'].subscriber) | |
405 | |
406 self.storage = TestStorage() | |
407 self.backend = backend.BackendService(self.storage) | |
408 self.storage.backend = self.backend | |
409 | |
410 d1 = defer.Deferred() | |
411 d1.addCallback(cb) | |
412 self.backend.registerNotifier(d1.callback) | |
413 d2 = self.backend.subscribe('node', OWNER, OWNER_FULL) | |
414 return defer.gatherResults([d1, d2]) | |
415 | |
416 test_notifyOnSubscription.timeout = 2 | |
417 | |
418 | |
419 | |
420 class BaseTestBackend(object): | |
421 """ | |
422 Base class for backend stubs. | |
423 """ | |
424 | |
425 def supportsPublisherAffiliation(self): | |
426 return True | |
427 | |
428 | |
429 def supportsOutcastAffiliation(self): | |
430 return True | |
431 | |
432 | |
433 def supportsPersistentItems(self): | |
434 return True | |
435 | |
436 | |
437 def supportsInstantNodes(self): | |
438 return True | |
439 | |
440 | |
441 def registerNotifier(self, observerfn, *args, **kwargs): | |
442 return | |
443 | |
444 | |
445 def registerPreDelete(self, preDeleteFn): | |
446 return | |
447 | |
448 | |
449 | |
450 class PubSubResourceFromBackendTest(unittest.TestCase): | |
451 | |
452 def test_interface(self): | |
453 resource = backend.PubSubResourceFromBackend(BaseTestBackend()) | |
454 self.assertTrue(verifyObject(iwokkel.IPubSubResource, resource)) | |
455 | |
456 | |
457 def test_preDelete(self): | |
458 """ | |
459 Test pre-delete sending out notifications to subscribers. | |
460 """ | |
461 | |
462 class TestBackend(BaseTestBackend): | |
463 preDeleteFn = None | |
464 | |
465 def registerPreDelete(self, preDeleteFn): | |
466 self.preDeleteFn = preDeleteFn | |
467 | |
468 def getSubscribers(self, nodeIdentifier): | |
469 return defer.succeed([OWNER]) | |
470 | |
471 def notifyDelete(service, nodeIdentifier, subscribers, | |
472 redirectURI=None): | |
473 self.assertEqual(SERVICE, service) | |
474 self.assertEqual('test', nodeIdentifier) | |
475 self.assertEqual([OWNER], subscribers) | |
476 self.assertIdentical(None, redirectURI) | |
477 d1.callback(None) | |
478 | |
479 d1 = defer.Deferred() | |
480 resource = backend.PubSubResourceFromBackend(TestBackend()) | |
481 resource.serviceJID = SERVICE | |
482 resource.pubsubService = pubsub.PubSubService() | |
483 resource.pubsubService.notifyDelete = notifyDelete | |
484 self.assertTrue(verifyObject(iwokkel.IPubSubResource, resource)) | |
485 self.assertNotIdentical(None, resource.backend.preDeleteFn) | |
486 data = {'nodeIdentifier': 'test'} | |
487 d2 = resource.backend.preDeleteFn(data) | |
488 return defer.DeferredList([d1, d2], fireOnOneErrback=1) | |
489 | |
490 | |
491 def test_preDeleteRedirect(self): | |
492 """ | |
493 Test pre-delete sending out notifications to subscribers. | |
494 """ | |
495 | |
496 uri = 'xmpp:%s?;node=test2' % (SERVICE.full(),) | |
497 | |
498 class TestBackend(BaseTestBackend): | |
499 preDeleteFn = None | |
500 | |
501 def registerPreDelete(self, preDeleteFn): | |
502 self.preDeleteFn = preDeleteFn | |
503 | |
504 def getSubscribers(self, nodeIdentifier): | |
505 return defer.succeed([OWNER]) | |
506 | |
507 def notifyDelete(service, nodeIdentifier, subscribers, | |
508 redirectURI=None): | |
509 self.assertEqual(SERVICE, service) | |
510 self.assertEqual('test', nodeIdentifier) | |
511 self.assertEqual([OWNER], subscribers) | |
512 self.assertEqual(uri, redirectURI) | |
513 d1.callback(None) | |
514 | |
515 d1 = defer.Deferred() | |
516 resource = backend.PubSubResourceFromBackend(TestBackend()) | |
517 resource.serviceJID = SERVICE | |
518 resource.pubsubService = pubsub.PubSubService() | |
519 resource.pubsubService.notifyDelete = notifyDelete | |
520 self.assertTrue(verifyObject(iwokkel.IPubSubResource, resource)) | |
521 self.assertNotIdentical(None, resource.backend.preDeleteFn) | |
522 data = {'nodeIdentifier': 'test', | |
523 'redirectURI': uri} | |
524 d2 = resource.backend.preDeleteFn(data) | |
525 return defer.DeferredList([d1, d2], fireOnOneErrback=1) | |
526 | |
527 | |
528 def test_unsubscribeNotSubscribed(self): | |
529 """ | |
530 Test unsubscription request when not subscribed. | |
531 """ | |
532 | |
533 class TestBackend(BaseTestBackend): | |
534 def unsubscribe(self, nodeIdentifier, subscriber, requestor): | |
535 return defer.fail(error.NotSubscribed()) | |
536 | |
537 def cb(e): | |
538 self.assertEquals('unexpected-request', e.condition) | |
539 | |
540 resource = backend.PubSubResourceFromBackend(TestBackend()) | |
541 request = pubsub.PubSubRequest() | |
542 request.sender = OWNER | |
543 request.recipient = SERVICE | |
544 request.nodeIdentifier = 'test' | |
545 request.subscriber = OWNER | |
546 d = resource.unsubscribe(request) | |
547 self.assertFailure(d, StanzaError) | |
548 d.addCallback(cb) | |
549 return d | |
550 | |
551 | |
552 def test_getInfo(self): | |
553 """ | |
554 Test retrieving node information. | |
555 """ | |
556 | |
557 class TestBackend(BaseTestBackend): | |
558 def getNodeType(self, nodeIdentifier): | |
559 return defer.succeed('leaf') | |
560 | |
561 def getNodeMetaData(self, nodeIdentifier): | |
562 return defer.succeed({'pubsub#persist_items': True}) | |
563 | |
564 def cb(info): | |
565 self.assertIn('type', info) | |
566 self.assertEquals('leaf', info['type']) | |
567 self.assertIn('meta-data', info) | |
568 self.assertEquals({'pubsub#persist_items': True}, info['meta-data']) | |
569 | |
570 resource = backend.PubSubResourceFromBackend(TestBackend()) | |
571 d = resource.getInfo(OWNER, SERVICE, 'test') | |
572 d.addCallback(cb) | |
573 return d | |
574 | |
575 | |
576 def test_getConfigurationOptions(self): | |
577 class TestBackend(BaseTestBackend): | |
578 nodeOptions = { | |
579 "pubsub#persist_items": | |
580 {"type": "boolean", | |
581 "label": "Persist items to storage"}, | |
582 "pubsub#deliver_payloads": | |
583 {"type": "boolean", | |
584 "label": "Deliver payloads with event notifications"} | |
585 } | |
586 | |
587 resource = backend.PubSubResourceFromBackend(TestBackend()) | |
588 options = resource.getConfigurationOptions() | |
589 self.assertIn("pubsub#persist_items", options) | |
590 | |
591 | |
592 def test_default(self): | |
593 class TestBackend(BaseTestBackend): | |
594 def getDefaultConfiguration(self, nodeType): | |
595 options = {"pubsub#persist_items": True, | |
596 "pubsub#deliver_payloads": True, | |
597 "pubsub#send_last_published_item": 'on_sub', | |
598 } | |
599 return defer.succeed(options) | |
600 | |
601 def cb(options): | |
602 self.assertEquals(True, options["pubsub#persist_items"]) | |
603 | |
604 resource = backend.PubSubResourceFromBackend(TestBackend()) | |
605 request = pubsub.PubSubRequest() | |
606 request.sender = OWNER | |
607 request.recipient = SERVICE | |
608 request.nodeType = 'leaf' | |
609 d = resource.default(request) | |
610 d.addCallback(cb) | |
611 return d |