Mercurial > libervia-pubsub
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sat_pubsub/test/test_backend.py Thu May 17 12:48:14 2012 +0200 @@ -0,0 +1,611 @@ +# Copyright (c) 2003-2010 Ralph Meijer +# See LICENSE for details. + +""" +Tests for L{idavoll.backend}. +""" + +from zope.interface import implements +from zope.interface.verify import verifyObject + +from twisted.internet import defer +from twisted.trial import unittest +from twisted.words.protocols.jabber import jid +from twisted.words.protocols.jabber.error import StanzaError + +from wokkel import iwokkel, pubsub + +from idavoll import backend, error, iidavoll + +OWNER = jid.JID('owner@example.com') +OWNER_FULL = jid.JID('owner@example.com/home') +SERVICE = jid.JID('test.example.org') +NS_PUBSUB = 'http://jabber.org/protocol/pubsub' + +class BackendTest(unittest.TestCase): + + def test_interfaceIBackend(self): + self.assertTrue(verifyObject(iidavoll.IBackendService, + backend.BackendService(None))) + + + def test_deleteNode(self): + class TestNode: + nodeIdentifier = 'to-be-deleted' + def getAffiliation(self, entity): + if entity.userhostJID() == OWNER: + return defer.succeed('owner') + + class TestStorage: + def __init__(self): + self.deleteCalled = [] + + def getNode(self, nodeIdentifier): + return defer.succeed(TestNode()) + + def deleteNode(self, nodeIdentifier): + if nodeIdentifier in ['to-be-deleted']: + self.deleteCalled.append(nodeIdentifier) + return defer.succeed(None) + else: + return defer.fail(error.NodeNotFound()) + + def preDelete(data): + self.assertFalse(self.storage.deleteCalled) + preDeleteCalled.append(data) + return defer.succeed(None) + + def cb(result): + self.assertEquals(1, len(preDeleteCalled)) + data = preDeleteCalled[-1] + self.assertEquals('to-be-deleted', data['nodeIdentifier']) + self.assertTrue(self.storage.deleteCalled) + + self.storage = TestStorage() + self.backend = backend.BackendService(self.storage) + + preDeleteCalled = [] + + self.backend.registerPreDelete(preDelete) + d = self.backend.deleteNode('to-be-deleted', OWNER_FULL) + d.addCallback(cb) + return d + + + def test_deleteNodeRedirect(self): + uri = 'xmpp:%s?;node=test2' % (SERVICE.full(),) + + class TestNode: + nodeIdentifier = 'to-be-deleted' + def getAffiliation(self, entity): + if entity.userhostJID() == OWNER: + return defer.succeed('owner') + + class TestStorage: + def __init__(self): + self.deleteCalled = [] + + def getNode(self, nodeIdentifier): + return defer.succeed(TestNode()) + + def deleteNode(self, nodeIdentifier): + if nodeIdentifier in ['to-be-deleted']: + self.deleteCalled.append(nodeIdentifier) + return defer.succeed(None) + else: + return defer.fail(error.NodeNotFound()) + + def preDelete(data): + self.assertFalse(self.storage.deleteCalled) + preDeleteCalled.append(data) + return defer.succeed(None) + + def cb(result): + self.assertEquals(1, len(preDeleteCalled)) + data = preDeleteCalled[-1] + self.assertEquals('to-be-deleted', data['nodeIdentifier']) + self.assertEquals(uri, data['redirectURI']) + self.assertTrue(self.storage.deleteCalled) + + self.storage = TestStorage() + self.backend = backend.BackendService(self.storage) + + preDeleteCalled = [] + + self.backend.registerPreDelete(preDelete) + d = self.backend.deleteNode('to-be-deleted', OWNER, redirectURI=uri) + d.addCallback(cb) + return d + + + def test_createNodeNoID(self): + """ + Test creation of a node without a given node identifier. + """ + class TestStorage: + def getDefaultConfiguration(self, nodeType): + return {} + + def createNode(self, nodeIdentifier, requestor, config): + self.nodeIdentifier = nodeIdentifier + return defer.succeed(None) + + self.storage = TestStorage() + self.backend = backend.BackendService(self.storage) + self.storage.backend = self.backend + + def checkID(nodeIdentifier): + self.assertNotIdentical(None, nodeIdentifier) + self.assertIdentical(self.storage.nodeIdentifier, nodeIdentifier) + + d = self.backend.createNode(None, OWNER_FULL) + d.addCallback(checkID) + return d + + class NodeStore: + """ + I just store nodes to pose as an L{IStorage} implementation. + """ + def __init__(self, nodes): + self.nodes = nodes + + def getNode(self, nodeIdentifier): + try: + return defer.succeed(self.nodes[nodeIdentifier]) + except KeyError: + return defer.fail(error.NodeNotFound()) + + + def test_getNotifications(self): + """ + Ensure subscribers show up in the notification list. + """ + item = pubsub.Item() + sub = pubsub.Subscription('test', OWNER, 'subscribed') + + class TestNode: + def getSubscriptions(self, state=None): + return [sub] + + def cb(result): + self.assertEquals(1, len(result)) + subscriber, subscriptions, items = result[-1] + + self.assertEquals(OWNER, subscriber) + self.assertEquals(set([sub]), subscriptions) + self.assertEquals([item], items) + + self.storage = self.NodeStore({'test': TestNode()}) + self.backend = backend.BackendService(self.storage) + d = self.backend.getNotifications('test', [item]) + d.addCallback(cb) + return d + + def test_getNotificationsRoot(self): + """ + Ensure subscribers to the root node show up in the notification list + for leaf nodes. + + This assumes a flat node relationship model with exactly one collection + node: the root node. Each leaf node is automatically a child node + of the root node. + """ + item = pubsub.Item() + subRoot = pubsub.Subscription('', OWNER, 'subscribed') + + class TestNode: + def getSubscriptions(self, state=None): + return [] + + class TestRootNode: + def getSubscriptions(self, state=None): + return [subRoot] + + def cb(result): + self.assertEquals(1, len(result)) + subscriber, subscriptions, items = result[-1] + self.assertEquals(OWNER, subscriber) + self.assertEquals(set([subRoot]), subscriptions) + self.assertEquals([item], items) + + self.storage = self.NodeStore({'test': TestNode(), + '': TestRootNode()}) + self.backend = backend.BackendService(self.storage) + d = self.backend.getNotifications('test', [item]) + d.addCallback(cb) + return d + + + def test_getNotificationsMultipleNodes(self): + """ + Ensure that entities that subscribe to a leaf node as well as the + root node get exactly one notification. + """ + item = pubsub.Item() + sub = pubsub.Subscription('test', OWNER, 'subscribed') + subRoot = pubsub.Subscription('', OWNER, 'subscribed') + + class TestNode: + def getSubscriptions(self, state=None): + return [sub] + + class TestRootNode: + def getSubscriptions(self, state=None): + return [subRoot] + + def cb(result): + self.assertEquals(1, len(result)) + subscriber, subscriptions, items = result[-1] + + self.assertEquals(OWNER, subscriber) + self.assertEquals(set([sub, subRoot]), subscriptions) + self.assertEquals([item], items) + + self.storage = self.NodeStore({'test': TestNode(), + '': TestRootNode()}) + self.backend = backend.BackendService(self.storage) + d = self.backend.getNotifications('test', [item]) + d.addCallback(cb) + return d + + + def test_getDefaultConfiguration(self): + """ + L{backend.BackendService.getDefaultConfiguration} should return + a deferred that fires a dictionary with configuration values. + """ + + class TestStorage: + def getDefaultConfiguration(self, nodeType): + return { + "pubsub#persist_items": True, + "pubsub#deliver_payloads": True} + + def cb(options): + self.assertIn("pubsub#persist_items", options) + self.assertEqual(True, options["pubsub#persist_items"]) + + self.backend = backend.BackendService(TestStorage()) + d = self.backend.getDefaultConfiguration('leaf') + d.addCallback(cb) + return d + + + def test_getNodeConfiguration(self): + class testNode: + nodeIdentifier = 'node' + def getConfiguration(self): + return {'pubsub#deliver_payloads': True, + 'pubsub#persist_items': False} + + class testStorage: + def getNode(self, nodeIdentifier): + return defer.succeed(testNode()) + + def cb(options): + self.assertIn("pubsub#deliver_payloads", options) + self.assertEqual(True, options["pubsub#deliver_payloads"]) + self.assertIn("pubsub#persist_items", options) + self.assertEqual(False, options["pubsub#persist_items"]) + + self.storage = testStorage() + self.backend = backend.BackendService(self.storage) + self.storage.backend = self.backend + + d = self.backend.getNodeConfiguration('node') + d.addCallback(cb) + return d + + + def test_setNodeConfiguration(self): + class testNode: + nodeIdentifier = 'node' + def getAffiliation(self, entity): + if entity.userhostJID() == OWNER: + return defer.succeed('owner') + def setConfiguration(self, options): + self.options = options + + class testStorage: + def __init__(self): + self.nodes = {'node': testNode()} + def getNode(self, nodeIdentifier): + return defer.succeed(self.nodes[nodeIdentifier]) + + def checkOptions(node): + options = node.options + self.assertIn("pubsub#deliver_payloads", options) + self.assertEqual(True, options["pubsub#deliver_payloads"]) + self.assertIn("pubsub#persist_items", options) + self.assertEqual(False, options["pubsub#persist_items"]) + + def cb(result): + d = self.storage.getNode('node') + d.addCallback(checkOptions) + return d + + self.storage = testStorage() + self.backend = backend.BackendService(self.storage) + self.storage.backend = self.backend + + options = {'pubsub#deliver_payloads': True, + 'pubsub#persist_items': False} + + d = self.backend.setNodeConfiguration('node', options, OWNER_FULL) + d.addCallback(cb) + return d + + + def test_publishNoID(self): + """ + Test publish request with an item without a node identifier. + """ + class TestNode: + nodeType = 'leaf' + nodeIdentifier = 'node' + def getAffiliation(self, entity): + if entity.userhostJID() == OWNER: + return defer.succeed('owner') + def getConfiguration(self): + return {'pubsub#deliver_payloads': True, + 'pubsub#persist_items': False} + + class TestStorage: + def getNode(self, nodeIdentifier): + return defer.succeed(TestNode()) + + def checkID(notification): + self.assertNotIdentical(None, notification['items'][0]['id']) + + self.storage = TestStorage() + self.backend = backend.BackendService(self.storage) + self.storage.backend = self.backend + + self.backend.registerNotifier(checkID) + + items = [pubsub.Item()] + d = self.backend.publish('node', items, OWNER_FULL) + return d + + + def test_notifyOnSubscription(self): + """ + Test notification of last published item on subscription. + """ + ITEM = "<item xmlns='%s' id='1'/>" % NS_PUBSUB + + class TestNode: + implements(iidavoll.ILeafNode) + nodeIdentifier = 'node' + nodeType = 'leaf' + def getAffiliation(self, entity): + if entity is OWNER: + return defer.succeed('owner') + def getConfiguration(self): + return {'pubsub#deliver_payloads': True, + 'pubsub#persist_items': False, + 'pubsub#send_last_published_item': 'on_sub'} + def getItems(self, maxItems): + return [ITEM] + def addSubscription(self, subscriber, state, options): + self.subscription = pubsub.Subscription('node', subscriber, + state, options) + return defer.succeed(None) + def getSubscription(self, subscriber): + return defer.succeed(self.subscription) + + class TestStorage: + def getNode(self, nodeIdentifier): + return defer.succeed(TestNode()) + + def cb(data): + self.assertEquals('node', data['nodeIdentifier']) + self.assertEquals([ITEM], data['items']) + self.assertEquals(OWNER, data['subscription'].subscriber) + + self.storage = TestStorage() + self.backend = backend.BackendService(self.storage) + self.storage.backend = self.backend + + d1 = defer.Deferred() + d1.addCallback(cb) + self.backend.registerNotifier(d1.callback) + d2 = self.backend.subscribe('node', OWNER, OWNER_FULL) + return defer.gatherResults([d1, d2]) + + test_notifyOnSubscription.timeout = 2 + + + +class BaseTestBackend(object): + """ + Base class for backend stubs. + """ + + def supportsPublisherAffiliation(self): + return True + + + def supportsOutcastAffiliation(self): + return True + + + def supportsPersistentItems(self): + return True + + + def supportsInstantNodes(self): + return True + + + def registerNotifier(self, observerfn, *args, **kwargs): + return + + + def registerPreDelete(self, preDeleteFn): + return + + + +class PubSubResourceFromBackendTest(unittest.TestCase): + + def test_interface(self): + resource = backend.PubSubResourceFromBackend(BaseTestBackend()) + self.assertTrue(verifyObject(iwokkel.IPubSubResource, resource)) + + + def test_preDelete(self): + """ + Test pre-delete sending out notifications to subscribers. + """ + + class TestBackend(BaseTestBackend): + preDeleteFn = None + + def registerPreDelete(self, preDeleteFn): + self.preDeleteFn = preDeleteFn + + def getSubscribers(self, nodeIdentifier): + return defer.succeed([OWNER]) + + def notifyDelete(service, nodeIdentifier, subscribers, + redirectURI=None): + self.assertEqual(SERVICE, service) + self.assertEqual('test', nodeIdentifier) + self.assertEqual([OWNER], subscribers) + self.assertIdentical(None, redirectURI) + d1.callback(None) + + d1 = defer.Deferred() + resource = backend.PubSubResourceFromBackend(TestBackend()) + resource.serviceJID = SERVICE + resource.pubsubService = pubsub.PubSubService() + resource.pubsubService.notifyDelete = notifyDelete + self.assertTrue(verifyObject(iwokkel.IPubSubResource, resource)) + self.assertNotIdentical(None, resource.backend.preDeleteFn) + data = {'nodeIdentifier': 'test'} + d2 = resource.backend.preDeleteFn(data) + return defer.DeferredList([d1, d2], fireOnOneErrback=1) + + + def test_preDeleteRedirect(self): + """ + Test pre-delete sending out notifications to subscribers. + """ + + uri = 'xmpp:%s?;node=test2' % (SERVICE.full(),) + + class TestBackend(BaseTestBackend): + preDeleteFn = None + + def registerPreDelete(self, preDeleteFn): + self.preDeleteFn = preDeleteFn + + def getSubscribers(self, nodeIdentifier): + return defer.succeed([OWNER]) + + def notifyDelete(service, nodeIdentifier, subscribers, + redirectURI=None): + self.assertEqual(SERVICE, service) + self.assertEqual('test', nodeIdentifier) + self.assertEqual([OWNER], subscribers) + self.assertEqual(uri, redirectURI) + d1.callback(None) + + d1 = defer.Deferred() + resource = backend.PubSubResourceFromBackend(TestBackend()) + resource.serviceJID = SERVICE + resource.pubsubService = pubsub.PubSubService() + resource.pubsubService.notifyDelete = notifyDelete + self.assertTrue(verifyObject(iwokkel.IPubSubResource, resource)) + self.assertNotIdentical(None, resource.backend.preDeleteFn) + data = {'nodeIdentifier': 'test', + 'redirectURI': uri} + d2 = resource.backend.preDeleteFn(data) + return defer.DeferredList([d1, d2], fireOnOneErrback=1) + + + def test_unsubscribeNotSubscribed(self): + """ + Test unsubscription request when not subscribed. + """ + + class TestBackend(BaseTestBackend): + def unsubscribe(self, nodeIdentifier, subscriber, requestor): + return defer.fail(error.NotSubscribed()) + + def cb(e): + self.assertEquals('unexpected-request', e.condition) + + resource = backend.PubSubResourceFromBackend(TestBackend()) + request = pubsub.PubSubRequest() + request.sender = OWNER + request.recipient = SERVICE + request.nodeIdentifier = 'test' + request.subscriber = OWNER + d = resource.unsubscribe(request) + self.assertFailure(d, StanzaError) + d.addCallback(cb) + return d + + + def test_getInfo(self): + """ + Test retrieving node information. + """ + + class TestBackend(BaseTestBackend): + def getNodeType(self, nodeIdentifier): + return defer.succeed('leaf') + + def getNodeMetaData(self, nodeIdentifier): + return defer.succeed({'pubsub#persist_items': True}) + + def cb(info): + self.assertIn('type', info) + self.assertEquals('leaf', info['type']) + self.assertIn('meta-data', info) + self.assertEquals({'pubsub#persist_items': True}, info['meta-data']) + + resource = backend.PubSubResourceFromBackend(TestBackend()) + d = resource.getInfo(OWNER, SERVICE, 'test') + d.addCallback(cb) + return d + + + def test_getConfigurationOptions(self): + class TestBackend(BaseTestBackend): + nodeOptions = { + "pubsub#persist_items": + {"type": "boolean", + "label": "Persist items to storage"}, + "pubsub#deliver_payloads": + {"type": "boolean", + "label": "Deliver payloads with event notifications"} + } + + resource = backend.PubSubResourceFromBackend(TestBackend()) + options = resource.getConfigurationOptions() + self.assertIn("pubsub#persist_items", options) + + + def test_default(self): + class TestBackend(BaseTestBackend): + def getDefaultConfiguration(self, nodeType): + options = {"pubsub#persist_items": True, + "pubsub#deliver_payloads": True, + "pubsub#send_last_published_item": 'on_sub', + } + return defer.succeed(options) + + def cb(options): + self.assertEquals(True, options["pubsub#persist_items"]) + + resource = backend.PubSubResourceFromBackend(TestBackend()) + request = pubsub.PubSubRequest() + request.sender = OWNER + request.recipient = SERVICE + request.nodeType = 'leaf' + d = resource.default(request) + d.addCallback(cb) + return d