Mercurial > libervia-pubsub
comparison sat_pubsub/test/test_storage.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_storage.py@8540825f85e0 |
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.memory_storage} and L{idavoll.pgsql_storage}. | |
6 """ | |
7 | |
8 from zope.interface.verify import verifyObject | |
9 from twisted.trial import unittest | |
10 from twisted.words.protocols.jabber import jid | |
11 from twisted.internet import defer | |
12 from twisted.words.xish import domish | |
13 | |
14 from idavoll import error, iidavoll | |
15 | |
16 OWNER = jid.JID('owner@example.com/Work') | |
17 SUBSCRIBER = jid.JID('subscriber@example.com/Home') | |
18 SUBSCRIBER_NEW = jid.JID('new@example.com/Home') | |
19 SUBSCRIBER_TO_BE_DELETED = jid.JID('to_be_deleted@example.com/Home') | |
20 SUBSCRIBER_PENDING = jid.JID('pending@example.com/Home') | |
21 PUBLISHER = jid.JID('publisher@example.com') | |
22 ITEM = domish.Element((None, 'item')) | |
23 ITEM['id'] = 'current' | |
24 ITEM.addElement(('testns', 'test'), content=u'Test \u2083 item') | |
25 ITEM_NEW = domish.Element((None, 'item')) | |
26 ITEM_NEW['id'] = 'new' | |
27 ITEM_NEW.addElement(('testns', 'test'), content=u'Test \u2083 item') | |
28 ITEM_UPDATED = domish.Element((None, 'item')) | |
29 ITEM_UPDATED['id'] = 'current' | |
30 ITEM_UPDATED.addElement(('testns', 'test'), content=u'Test \u2084 item') | |
31 ITEM_TO_BE_DELETED = domish.Element((None, 'item')) | |
32 ITEM_TO_BE_DELETED['id'] = 'to-be-deleted' | |
33 ITEM_TO_BE_DELETED.addElement(('testns', 'test'), content=u'Test \u2083 item') | |
34 | |
35 def decode(object): | |
36 if isinstance(object, str): | |
37 object = object.decode('utf-8') | |
38 return object | |
39 | |
40 | |
41 | |
42 class StorageTests: | |
43 | |
44 def _assignTestNode(self, node): | |
45 self.node = node | |
46 | |
47 | |
48 def setUp(self): | |
49 d = self.s.getNode('pre-existing') | |
50 d.addCallback(self._assignTestNode) | |
51 return d | |
52 | |
53 | |
54 def test_interfaceIStorage(self): | |
55 self.assertTrue(verifyObject(iidavoll.IStorage, self.s)) | |
56 | |
57 | |
58 def test_interfaceINode(self): | |
59 self.assertTrue(verifyObject(iidavoll.INode, self.node)) | |
60 | |
61 | |
62 def test_interfaceILeafNode(self): | |
63 self.assertTrue(verifyObject(iidavoll.ILeafNode, self.node)) | |
64 | |
65 | |
66 def test_getNode(self): | |
67 return self.s.getNode('pre-existing') | |
68 | |
69 | |
70 def test_getNonExistingNode(self): | |
71 d = self.s.getNode('non-existing') | |
72 self.assertFailure(d, error.NodeNotFound) | |
73 return d | |
74 | |
75 | |
76 def test_getNodeIDs(self): | |
77 def cb(nodeIdentifiers): | |
78 self.assertIn('pre-existing', nodeIdentifiers) | |
79 self.assertNotIn('non-existing', nodeIdentifiers) | |
80 | |
81 return self.s.getNodeIds().addCallback(cb) | |
82 | |
83 | |
84 def test_createExistingNode(self): | |
85 config = self.s.getDefaultConfiguration('leaf') | |
86 config['pubsub#node_type'] = 'leaf' | |
87 d = self.s.createNode('pre-existing', OWNER, config) | |
88 self.assertFailure(d, error.NodeExists) | |
89 return d | |
90 | |
91 | |
92 def test_createNode(self): | |
93 def cb(void): | |
94 d = self.s.getNode('new 1') | |
95 return d | |
96 | |
97 config = self.s.getDefaultConfiguration('leaf') | |
98 config['pubsub#node_type'] = 'leaf' | |
99 d = self.s.createNode('new 1', OWNER, config) | |
100 d.addCallback(cb) | |
101 return d | |
102 | |
103 | |
104 def test_createNodeChangingConfig(self): | |
105 """ | |
106 The configuration passed to createNode must be free to be changed. | |
107 """ | |
108 def cb(result): | |
109 node1, node2 = result | |
110 self.assertTrue(node1.getConfiguration()['pubsub#persist_items']) | |
111 | |
112 config = { | |
113 "pubsub#persist_items": True, | |
114 "pubsub#deliver_payloads": True, | |
115 "pubsub#send_last_published_item": 'on_sub', | |
116 "pubsub#node_type": 'leaf', | |
117 } | |
118 | |
119 def unsetPersistItems(_): | |
120 config["pubsub#persist_items"] = False | |
121 | |
122 d = defer.succeed(None) | |
123 d.addCallback(lambda _: self.s.createNode('new 1', OWNER, config)) | |
124 d.addCallback(unsetPersistItems) | |
125 d.addCallback(lambda _: self.s.createNode('new 2', OWNER, config)) | |
126 d.addCallback(lambda _: defer.gatherResults([ | |
127 self.s.getNode('new 1'), | |
128 self.s.getNode('new 2')])) | |
129 d.addCallback(cb) | |
130 return d | |
131 | |
132 | |
133 def test_deleteNonExistingNode(self): | |
134 d = self.s.deleteNode('non-existing') | |
135 self.assertFailure(d, error.NodeNotFound) | |
136 return d | |
137 | |
138 | |
139 def test_deleteNode(self): | |
140 def cb(void): | |
141 d = self.s.getNode('to-be-deleted') | |
142 self.assertFailure(d, error.NodeNotFound) | |
143 return d | |
144 | |
145 d = self.s.deleteNode('to-be-deleted') | |
146 d.addCallback(cb) | |
147 return d | |
148 | |
149 | |
150 def test_getAffiliations(self): | |
151 def cb(affiliations): | |
152 self.assertIn(('pre-existing', 'owner'), affiliations) | |
153 | |
154 d = self.s.getAffiliations(OWNER) | |
155 d.addCallback(cb) | |
156 return d | |
157 | |
158 | |
159 def test_getSubscriptions(self): | |
160 def cb(subscriptions): | |
161 found = False | |
162 for subscription in subscriptions: | |
163 if (subscription.nodeIdentifier == 'pre-existing' and | |
164 subscription.subscriber == SUBSCRIBER and | |
165 subscription.state == 'subscribed'): | |
166 found = True | |
167 self.assertTrue(found) | |
168 | |
169 d = self.s.getSubscriptions(SUBSCRIBER) | |
170 d.addCallback(cb) | |
171 return d | |
172 | |
173 | |
174 # Node tests | |
175 | |
176 def test_getType(self): | |
177 self.assertEqual(self.node.getType(), 'leaf') | |
178 | |
179 | |
180 def test_getConfiguration(self): | |
181 config = self.node.getConfiguration() | |
182 self.assertIn('pubsub#persist_items', config.iterkeys()) | |
183 self.assertIn('pubsub#deliver_payloads', config.iterkeys()) | |
184 self.assertEqual(config['pubsub#persist_items'], True) | |
185 self.assertEqual(config['pubsub#deliver_payloads'], True) | |
186 | |
187 | |
188 def test_setConfiguration(self): | |
189 def getConfig(node): | |
190 d = node.setConfiguration({'pubsub#persist_items': False}) | |
191 d.addCallback(lambda _: node) | |
192 return d | |
193 | |
194 def checkObjectConfig(node): | |
195 config = node.getConfiguration() | |
196 self.assertEqual(config['pubsub#persist_items'], False) | |
197 | |
198 def getNode(void): | |
199 return self.s.getNode('to-be-reconfigured') | |
200 | |
201 def checkStorageConfig(node): | |
202 config = node.getConfiguration() | |
203 self.assertEqual(config['pubsub#persist_items'], False) | |
204 | |
205 d = self.s.getNode('to-be-reconfigured') | |
206 d.addCallback(getConfig) | |
207 d.addCallback(checkObjectConfig) | |
208 d.addCallback(getNode) | |
209 d.addCallback(checkStorageConfig) | |
210 return d | |
211 | |
212 | |
213 def test_getMetaData(self): | |
214 metaData = self.node.getMetaData() | |
215 for key, value in self.node.getConfiguration().iteritems(): | |
216 self.assertIn(key, metaData.iterkeys()) | |
217 self.assertEqual(value, metaData[key]) | |
218 self.assertIn('pubsub#node_type', metaData.iterkeys()) | |
219 self.assertEqual(metaData['pubsub#node_type'], 'leaf') | |
220 | |
221 | |
222 def test_getAffiliation(self): | |
223 def cb(affiliation): | |
224 self.assertEqual(affiliation, 'owner') | |
225 | |
226 d = self.node.getAffiliation(OWNER) | |
227 d.addCallback(cb) | |
228 return d | |
229 | |
230 | |
231 def test_getNonExistingAffiliation(self): | |
232 def cb(affiliation): | |
233 self.assertEqual(affiliation, None) | |
234 | |
235 d = self.node.getAffiliation(SUBSCRIBER) | |
236 d.addCallback(cb) | |
237 return d | |
238 | |
239 | |
240 def test_addSubscription(self): | |
241 def cb1(void): | |
242 return self.node.getSubscription(SUBSCRIBER_NEW) | |
243 | |
244 def cb2(subscription): | |
245 self.assertEqual(subscription.state, 'pending') | |
246 | |
247 d = self.node.addSubscription(SUBSCRIBER_NEW, 'pending', {}) | |
248 d.addCallback(cb1) | |
249 d.addCallback(cb2) | |
250 return d | |
251 | |
252 | |
253 def test_addExistingSubscription(self): | |
254 d = self.node.addSubscription(SUBSCRIBER, 'pending', {}) | |
255 self.assertFailure(d, error.SubscriptionExists) | |
256 return d | |
257 | |
258 | |
259 def test_getSubscription(self): | |
260 def cb(subscriptions): | |
261 self.assertEquals(subscriptions[0].state, 'subscribed') | |
262 self.assertEquals(subscriptions[1].state, 'pending') | |
263 self.assertEquals(subscriptions[2], None) | |
264 | |
265 d = defer.gatherResults([self.node.getSubscription(SUBSCRIBER), | |
266 self.node.getSubscription(SUBSCRIBER_PENDING), | |
267 self.node.getSubscription(OWNER)]) | |
268 d.addCallback(cb) | |
269 return d | |
270 | |
271 | |
272 def test_removeSubscription(self): | |
273 return self.node.removeSubscription(SUBSCRIBER_TO_BE_DELETED) | |
274 | |
275 | |
276 def test_removeNonExistingSubscription(self): | |
277 d = self.node.removeSubscription(OWNER) | |
278 self.assertFailure(d, error.NotSubscribed) | |
279 return d | |
280 | |
281 | |
282 def test_getNodeSubscriptions(self): | |
283 def extractSubscribers(subscriptions): | |
284 return [subscription.subscriber for subscription in subscriptions] | |
285 | |
286 def cb(subscribers): | |
287 self.assertIn(SUBSCRIBER, subscribers) | |
288 self.assertNotIn(SUBSCRIBER_PENDING, subscribers) | |
289 self.assertNotIn(OWNER, subscribers) | |
290 | |
291 d = self.node.getSubscriptions('subscribed') | |
292 d.addCallback(extractSubscribers) | |
293 d.addCallback(cb) | |
294 return d | |
295 | |
296 | |
297 def test_isSubscriber(self): | |
298 def cb(subscribed): | |
299 self.assertEquals(subscribed[0][1], True) | |
300 self.assertEquals(subscribed[1][1], True) | |
301 self.assertEquals(subscribed[2][1], False) | |
302 self.assertEquals(subscribed[3][1], False) | |
303 | |
304 d = defer.DeferredList([self.node.isSubscribed(SUBSCRIBER), | |
305 self.node.isSubscribed(SUBSCRIBER.userhostJID()), | |
306 self.node.isSubscribed(SUBSCRIBER_PENDING), | |
307 self.node.isSubscribed(OWNER)]) | |
308 d.addCallback(cb) | |
309 return d | |
310 | |
311 | |
312 def test_storeItems(self): | |
313 def cb1(void): | |
314 return self.node.getItemsById(['new']) | |
315 | |
316 def cb2(result): | |
317 self.assertEqual(ITEM_NEW.toXml(), result[0].toXml()) | |
318 | |
319 d = self.node.storeItems([ITEM_NEW], PUBLISHER) | |
320 d.addCallback(cb1) | |
321 d.addCallback(cb2) | |
322 return d | |
323 | |
324 | |
325 def test_storeUpdatedItems(self): | |
326 def cb1(void): | |
327 return self.node.getItemsById(['current']) | |
328 | |
329 def cb2(result): | |
330 self.assertEqual(ITEM_UPDATED.toXml(), result[0].toXml()) | |
331 | |
332 d = self.node.storeItems([ITEM_UPDATED], PUBLISHER) | |
333 d.addCallback(cb1) | |
334 d.addCallback(cb2) | |
335 return d | |
336 | |
337 | |
338 def test_removeItems(self): | |
339 def cb1(result): | |
340 self.assertEqual(['to-be-deleted'], result) | |
341 return self.node.getItemsById(['to-be-deleted']) | |
342 | |
343 def cb2(result): | |
344 self.assertEqual(0, len(result)) | |
345 | |
346 d = self.node.removeItems(['to-be-deleted']) | |
347 d.addCallback(cb1) | |
348 d.addCallback(cb2) | |
349 return d | |
350 | |
351 | |
352 def test_removeNonExistingItems(self): | |
353 def cb(result): | |
354 self.assertEqual([], result) | |
355 | |
356 d = self.node.removeItems(['non-existing']) | |
357 d.addCallback(cb) | |
358 return d | |
359 | |
360 | |
361 def test_getItems(self): | |
362 def cb(result): | |
363 items = [item.toXml() for item in result] | |
364 self.assertIn(ITEM.toXml(), items) | |
365 | |
366 d = self.node.getItems() | |
367 d.addCallback(cb) | |
368 return d | |
369 | |
370 | |
371 def test_lastItem(self): | |
372 def cb(result): | |
373 self.assertEqual(1, len(result)) | |
374 self.assertEqual(ITEM.toXml(), result[0].toXml()) | |
375 | |
376 d = self.node.getItems(1) | |
377 d.addCallback(cb) | |
378 return d | |
379 | |
380 | |
381 def test_getItemsById(self): | |
382 def cb(result): | |
383 self.assertEqual(1, len(result)) | |
384 | |
385 d = self.node.getItemsById(['current']) | |
386 d.addCallback(cb) | |
387 return d | |
388 | |
389 | |
390 def test_getNonExistingItemsById(self): | |
391 def cb(result): | |
392 self.assertEqual(0, len(result)) | |
393 | |
394 d = self.node.getItemsById(['non-existing']) | |
395 d.addCallback(cb) | |
396 return d | |
397 | |
398 | |
399 def test_purge(self): | |
400 def cb1(node): | |
401 d = node.purge() | |
402 d.addCallback(lambda _: node) | |
403 return d | |
404 | |
405 def cb2(node): | |
406 return node.getItems() | |
407 | |
408 def cb3(result): | |
409 self.assertEqual([], result) | |
410 | |
411 d = self.s.getNode('to-be-purged') | |
412 d.addCallback(cb1) | |
413 d.addCallback(cb2) | |
414 d.addCallback(cb3) | |
415 return d | |
416 | |
417 | |
418 def test_getNodeAffilatiations(self): | |
419 def cb1(node): | |
420 return node.getAffiliations() | |
421 | |
422 def cb2(affiliations): | |
423 affiliations = dict(((a[0].full(), a[1]) for a in affiliations)) | |
424 self.assertEquals(affiliations[OWNER.userhost()], 'owner') | |
425 | |
426 d = self.s.getNode('pre-existing') | |
427 d.addCallback(cb1) | |
428 d.addCallback(cb2) | |
429 return d | |
430 | |
431 | |
432 | |
433 class MemoryStorageStorageTestCase(unittest.TestCase, StorageTests): | |
434 | |
435 def setUp(self): | |
436 from idavoll.memory_storage import Storage, PublishedItem, LeafNode | |
437 from idavoll.memory_storage import Subscription | |
438 | |
439 defaultConfig = Storage.defaultConfig['leaf'] | |
440 | |
441 self.s = Storage() | |
442 self.s._nodes['pre-existing'] = \ | |
443 LeafNode('pre-existing', OWNER, defaultConfig) | |
444 self.s._nodes['to-be-deleted'] = \ | |
445 LeafNode('to-be-deleted', OWNER, None) | |
446 self.s._nodes['to-be-reconfigured'] = \ | |
447 LeafNode('to-be-reconfigured', OWNER, defaultConfig) | |
448 self.s._nodes['to-be-purged'] = \ | |
449 LeafNode('to-be-purged', OWNER, None) | |
450 | |
451 subscriptions = self.s._nodes['pre-existing']._subscriptions | |
452 subscriptions[SUBSCRIBER.full()] = Subscription('pre-existing', | |
453 SUBSCRIBER, | |
454 'subscribed') | |
455 subscriptions[SUBSCRIBER_TO_BE_DELETED.full()] = \ | |
456 Subscription('pre-existing', SUBSCRIBER_TO_BE_DELETED, | |
457 'subscribed') | |
458 subscriptions[SUBSCRIBER_PENDING.full()] = \ | |
459 Subscription('pre-existing', SUBSCRIBER_PENDING, | |
460 'pending') | |
461 | |
462 item = PublishedItem(ITEM_TO_BE_DELETED, PUBLISHER) | |
463 self.s._nodes['pre-existing']._items['to-be-deleted'] = item | |
464 self.s._nodes['pre-existing']._itemlist.append(item) | |
465 self.s._nodes['to-be-purged']._items['to-be-deleted'] = item | |
466 self.s._nodes['to-be-purged']._itemlist.append(item) | |
467 item = PublishedItem(ITEM, PUBLISHER) | |
468 self.s._nodes['pre-existing']._items['current'] = item | |
469 self.s._nodes['pre-existing']._itemlist.append(item) | |
470 | |
471 return StorageTests.setUp(self) | |
472 | |
473 | |
474 | |
475 class PgsqlStorageStorageTestCase(unittest.TestCase, StorageTests): | |
476 | |
477 dbpool = None | |
478 | |
479 def setUp(self): | |
480 from idavoll.pgsql_storage import Storage | |
481 from twisted.enterprise import adbapi | |
482 if self.dbpool is None: | |
483 self.__class__.dbpool = adbapi.ConnectionPool('psycopg2', | |
484 database='pubsub_test', | |
485 cp_reconnect=True, | |
486 client_encoding='utf-8', | |
487 ) | |
488 self.s = Storage(self.dbpool) | |
489 self.dbpool.start() | |
490 d = self.dbpool.runInteraction(self.init) | |
491 d.addCallback(lambda _: StorageTests.setUp(self)) | |
492 return d | |
493 | |
494 | |
495 def tearDown(self): | |
496 return self.dbpool.runInteraction(self.cleandb) | |
497 | |
498 | |
499 def init(self, cursor): | |
500 self.cleandb(cursor) | |
501 cursor.execute("""INSERT INTO nodes | |
502 (node, node_type, persist_items) | |
503 VALUES ('pre-existing', 'leaf', TRUE)""") | |
504 cursor.execute("""INSERT INTO nodes (node) VALUES ('to-be-deleted')""") | |
505 cursor.execute("""INSERT INTO nodes (node) VALUES ('to-be-reconfigured')""") | |
506 cursor.execute("""INSERT INTO nodes (node) VALUES ('to-be-purged')""") | |
507 cursor.execute("""INSERT INTO entities (jid) VALUES (%s)""", | |
508 (OWNER.userhost(),)) | |
509 cursor.execute("""INSERT INTO affiliations | |
510 (node_id, entity_id, affiliation) | |
511 SELECT node_id, entity_id, 'owner' | |
512 FROM nodes, entities | |
513 WHERE node='pre-existing' AND jid=%s""", | |
514 (OWNER.userhost(),)) | |
515 cursor.execute("""INSERT INTO entities (jid) VALUES (%s)""", | |
516 (SUBSCRIBER.userhost(),)) | |
517 cursor.execute("""INSERT INTO subscriptions | |
518 (node_id, entity_id, resource, state) | |
519 SELECT node_id, entity_id, %s, 'subscribed' | |
520 FROM nodes, entities | |
521 WHERE node='pre-existing' AND jid=%s""", | |
522 (SUBSCRIBER.resource, | |
523 SUBSCRIBER.userhost())) | |
524 cursor.execute("""INSERT INTO entities (jid) VALUES (%s)""", | |
525 (SUBSCRIBER_TO_BE_DELETED.userhost(),)) | |
526 cursor.execute("""INSERT INTO subscriptions | |
527 (node_id, entity_id, resource, state) | |
528 SELECT node_id, entity_id, %s, 'subscribed' | |
529 FROM nodes, entities | |
530 WHERE node='pre-existing' AND jid=%s""", | |
531 (SUBSCRIBER_TO_BE_DELETED.resource, | |
532 SUBSCRIBER_TO_BE_DELETED.userhost())) | |
533 cursor.execute("""INSERT INTO entities (jid) VALUES (%s)""", | |
534 (SUBSCRIBER_PENDING.userhost(),)) | |
535 cursor.execute("""INSERT INTO subscriptions | |
536 (node_id, entity_id, resource, state) | |
537 SELECT node_id, entity_id, %s, 'pending' | |
538 FROM nodes, entities | |
539 WHERE node='pre-existing' AND jid=%s""", | |
540 (SUBSCRIBER_PENDING.resource, | |
541 SUBSCRIBER_PENDING.userhost())) | |
542 cursor.execute("""INSERT INTO entities (jid) VALUES (%s)""", | |
543 (PUBLISHER.userhost(),)) | |
544 cursor.execute("""INSERT INTO items | |
545 (node_id, publisher, item, data, date) | |
546 SELECT node_id, %s, 'to-be-deleted', %s, | |
547 now() - interval '1 day' | |
548 FROM nodes | |
549 WHERE node='pre-existing'""", | |
550 (PUBLISHER.userhost(), | |
551 ITEM_TO_BE_DELETED.toXml())) | |
552 cursor.execute("""INSERT INTO items (node_id, publisher, item, data) | |
553 SELECT node_id, %s, 'to-be-deleted', %s | |
554 FROM nodes | |
555 WHERE node='to-be-purged'""", | |
556 (PUBLISHER.userhost(), | |
557 ITEM_TO_BE_DELETED.toXml())) | |
558 cursor.execute("""INSERT INTO items (node_id, publisher, item, data) | |
559 SELECT node_id, %s, 'current', %s | |
560 FROM nodes | |
561 WHERE node='pre-existing'""", | |
562 (PUBLISHER.userhost(), | |
563 ITEM.toXml())) | |
564 | |
565 | |
566 def cleandb(self, cursor): | |
567 cursor.execute("""DELETE FROM nodes WHERE node in | |
568 ('non-existing', 'pre-existing', 'to-be-deleted', | |
569 'new 1', 'new 2', 'new 3', 'to-be-reconfigured', | |
570 'to-be-purged')""") | |
571 cursor.execute("""DELETE FROM entities WHERE jid=%s""", | |
572 (OWNER.userhost(),)) | |
573 cursor.execute("""DELETE FROM entities WHERE jid=%s""", | |
574 (SUBSCRIBER.userhost(),)) | |
575 cursor.execute("""DELETE FROM entities WHERE jid=%s""", | |
576 (SUBSCRIBER_TO_BE_DELETED.userhost(),)) | |
577 cursor.execute("""DELETE FROM entities WHERE jid=%s""", | |
578 (SUBSCRIBER_PENDING.userhost(),)) | |
579 cursor.execute("""DELETE FROM entities WHERE jid=%s""", | |
580 (PUBLISHER.userhost(),)) | |
581 | |
582 try: | |
583 import psycopg2 | |
584 except ImportError: | |
585 PgsqlStorageStorageTestCase.skip = "Psycopg2 not available" |