Mercurial > libervia-pubsub
comparison sat_pubsub/test/test_gateway.py @ 273:6ba0d6def7f5
Use twisted.web instead of web2, initial tests.
author | Ralph Meijer <ralphm@ik.nu> |
---|---|
date | Sun, 20 Jan 2013 13:38:41 +0100 |
parents | d55620ceafed |
children | 6641ea7990ee |
comparison
equal
deleted
inserted
replaced
272:558a43366c9f | 273:6ba0d6def7f5 |
---|---|
57 | 57 |
58 Note that some tests are functional tests that require a running idavoll | 58 Note that some tests are functional tests that require a running idavoll |
59 service. | 59 service. |
60 """ | 60 """ |
61 | 61 |
62 from StringIO import StringIO | |
63 | |
64 import simplejson | |
65 | |
62 from twisted.internet import defer | 66 from twisted.internet import defer |
63 from twisted.trial import unittest | 67 from twisted.trial import unittest |
64 from twisted.web import error | 68 from twisted.web import error, http, http_headers, server |
69 from twisted.web.test import requesthelper | |
65 from twisted.words.xish import domish | 70 from twisted.words.xish import domish |
71 from twisted.words.protocols.jabber.jid import JID | |
66 | 72 |
67 from sat_pubsub import gateway | 73 from sat_pubsub import gateway |
74 from sat_pubsub.backend import BackendService | |
75 from sat_pubsub.memory_storage import Storage | |
68 | 76 |
69 AGENT = "Idavoll Test Script" | 77 AGENT = "Idavoll Test Script" |
70 NS_ATOM = "http://www.w3.org/2005/Atom" | 78 NS_ATOM = "http://www.w3.org/2005/Atom" |
71 | 79 |
72 TEST_ENTRY = domish.Element((NS_ATOM, 'entry')) | 80 TEST_ENTRY = domish.Element((NS_ATOM, 'entry')) |
75 TEST_ENTRY.addElement("title", content="Atom-Powered Robots Run Amok") | 83 TEST_ENTRY.addElement("title", content="Atom-Powered Robots Run Amok") |
76 TEST_ENTRY.addElement("author").addElement("name", content="John Doe") | 84 TEST_ENTRY.addElement("author").addElement("name", content="John Doe") |
77 TEST_ENTRY.addElement("content", content="Some text.") | 85 TEST_ENTRY.addElement("content", content="Some text.") |
78 | 86 |
79 baseURI = "http://localhost:8086/" | 87 baseURI = "http://localhost:8086/" |
80 componentJID = "pubsub" | 88 component = "pubsub" |
89 componentJID = JID(component) | |
90 ownerJID = JID('owner@example.org') | |
91 | |
92 def _render(resource, request): | |
93 result = resource.render(request) | |
94 if isinstance(result, str): | |
95 request.write(result) | |
96 request.finish() | |
97 return defer.succeed(None) | |
98 elif result is server.NOT_DONE_YET: | |
99 if request.finished: | |
100 return defer.succeed(None) | |
101 else: | |
102 return request.notifyFinish() | |
103 else: | |
104 raise ValueError("Unexpected return value: %r" % (result,)) | |
105 | |
106 | |
107 class DummyRequest(requesthelper.DummyRequest): | |
108 | |
109 def __init__(self, *args, **kwargs): | |
110 requesthelper.DummyRequest.__init__(self, *args, **kwargs) | |
111 self.requestHeaders = http_headers.Headers() | |
112 | |
113 | |
114 | |
115 class GetServiceAndNodeTest(unittest.TestCase): | |
116 """ | |
117 Tests for {gateway.getServiceAndNode}. | |
118 """ | |
119 | |
120 def test_basic(self): | |
121 """ | |
122 getServiceAndNode parses an XMPP URI with node parameter. | |
123 """ | |
124 uri = b'xmpp:pubsub.example.org?;node=test' | |
125 service, nodeIdentifier = gateway.getServiceAndNode(uri) | |
126 self.assertEqual(JID(u'pubsub.example.org'), service) | |
127 self.assertEqual(u'test', nodeIdentifier) | |
128 | |
129 | |
130 def test_schemeEmpty(self): | |
131 """ | |
132 If the URI scheme is empty, an exception is raised. | |
133 """ | |
134 uri = b'pubsub.example.org' | |
135 self.assertRaises(gateway.XMPPURIParseError, | |
136 gateway.getServiceAndNode, uri) | |
137 | |
138 | |
139 def test_schemeNotXMPP(self): | |
140 """ | |
141 If the URI scheme is not 'xmpp', an exception is raised. | |
142 """ | |
143 uri = b'mailto:test@example.org' | |
144 self.assertRaises(gateway.XMPPURIParseError, | |
145 gateway.getServiceAndNode, uri) | |
146 | |
147 | |
148 def test_authorityPresent(self): | |
149 """ | |
150 If the URI has an authority component, an exception is raised. | |
151 """ | |
152 uri = b'xmpp://pubsub.example.org/' | |
153 self.assertRaises(gateway.XMPPURIParseError, | |
154 gateway.getServiceAndNode, uri) | |
155 | |
156 | |
157 def test_queryEmpty(self): | |
158 """ | |
159 If there is no query component, the nodeIdentifier is empty. | |
160 """ | |
161 uri = b'xmpp:pubsub.example.org' | |
162 service, nodeIdentifier = gateway.getServiceAndNode(uri) | |
163 | |
164 self.assertEqual(JID(u'pubsub.example.org'), service) | |
165 self.assertEqual(u'', nodeIdentifier) | |
166 | |
167 | |
168 def test_jidInvalid(self): | |
169 """ | |
170 If the JID from the path component is invalid, an exception is raised. | |
171 """ | |
172 uri = b'xmpp:@@pubsub.example.org?;node=test' | |
173 self.assertRaises(gateway.XMPPURIParseError, | |
174 gateway.getServiceAndNode, uri) | |
175 | |
176 | |
177 def test_pathEmpty(self): | |
178 """ | |
179 If there is no path component, an exception is raised. | |
180 """ | |
181 uri = b'xmpp:?node=test' | |
182 self.assertRaises(gateway.XMPPURIParseError, | |
183 gateway.getServiceAndNode, uri) | |
184 | |
185 | |
186 def test_nodeAbsent(self): | |
187 """ | |
188 If the node parameter is missing, the nodeIdentifier is empty. | |
189 """ | |
190 uri = b'xmpp:pubsub.example.org?' | |
191 service, nodeIdentifier = gateway.getServiceAndNode(uri) | |
192 | |
193 self.assertEqual(JID(u'pubsub.example.org'), service) | |
194 self.assertEqual(u'', nodeIdentifier) | |
195 | |
196 | |
197 | |
198 class GetXMPPURITest(unittest.TestCase): | |
199 """ | |
200 Tests for L{gateway.getXMPPURITest}. | |
201 """ | |
202 | |
203 def test_basic(self): | |
204 uri = gateway.getXMPPURI(JID(u'pubsub.example.org'), u'test') | |
205 self.assertEqual('xmpp:pubsub.example.org?;node=test', uri) | |
206 | |
207 | |
208 class CreateResourceTest(unittest.TestCase): | |
209 """ | |
210 Tests for L{gateway.CreateResource}. | |
211 """ | |
212 | |
213 def setUp(self): | |
214 self.backend = BackendService(Storage()) | |
215 self.resource = gateway.CreateResource(self.backend, componentJID, | |
216 ownerJID) | |
217 | |
218 | |
219 def test_get(self): | |
220 """ | |
221 The method GET is not supported. | |
222 """ | |
223 request = DummyRequest([b'']) | |
224 self.assertRaises(error.UnsupportedMethod, | |
225 _render, self.resource, request) | |
226 | |
227 | |
228 def test_post(self): | |
229 """ | |
230 Upon a POST, a new node is created and the URI returned. | |
231 """ | |
232 request = DummyRequest([b'']) | |
233 request.method = 'POST' | |
234 | |
235 def gotNodes(nodeIdentifiers, uri): | |
236 service, nodeIdentifier = gateway.getServiceAndNode(uri) | |
237 self.assertIn(nodeIdentifier, nodeIdentifiers) | |
238 | |
239 def rendered(result): | |
240 self.assertEqual('application/json', | |
241 request.outgoingHeaders['content-type']) | |
242 payload = simplejson.loads(b''.join(request.written)) | |
243 self.assertIn('uri', payload) | |
244 d = self.backend.getNodes() | |
245 d.addCallback(gotNodes, payload['uri']) | |
246 return d | |
247 | |
248 d = _render(self.resource, request) | |
249 d.addCallback(rendered) | |
250 return d | |
251 | |
252 | |
253 | |
254 class DeleteResourceTest(unittest.TestCase): | |
255 """ | |
256 Tests for L{gateway.DeleteResource}. | |
257 """ | |
258 | |
259 def setUp(self): | |
260 self.backend = BackendService(Storage()) | |
261 self.resource = gateway.DeleteResource(self.backend, componentJID, | |
262 ownerJID) | |
263 | |
264 | |
265 def test_get(self): | |
266 """ | |
267 The method GET is not supported. | |
268 """ | |
269 request = DummyRequest([b'']) | |
270 self.assertRaises(error.UnsupportedMethod, | |
271 _render, self.resource, request) | |
272 | |
273 | |
274 def test_post(self): | |
275 """ | |
276 Upon a POST, a new node is created and the URI returned. | |
277 """ | |
278 request = DummyRequest([b'']) | |
279 request.method = b'POST' | |
280 | |
281 def rendered(result): | |
282 self.assertEqual(http.NO_CONTENT, request.responseCode) | |
283 | |
284 def nodeCreated(nodeIdentifier): | |
285 uri = gateway.getXMPPURI(componentJID, nodeIdentifier) | |
286 request.args[b'uri'] = [uri] | |
287 request.content = StringIO(b'') | |
288 | |
289 return _render(self.resource, request) | |
290 | |
291 d = self.backend.createNode(u'test', ownerJID) | |
292 d.addCallback(nodeCreated) | |
293 d.addCallback(rendered) | |
294 return d | |
295 | |
296 | |
297 def test_postWithRedirect(self): | |
298 """ | |
299 Upon a POST, a new node is created and the URI returned. | |
300 """ | |
301 request = DummyRequest([b'']) | |
302 request.method = b'POST' | |
303 otherNodeURI = b'xmpp:pubsub.example.org?node=other' | |
304 | |
305 def rendered(result): | |
306 self.assertEqual(http.NO_CONTENT, request.responseCode) | |
307 self.assertEqual(1, len(deletes)) | |
308 nodeIdentifier, owner, redirectURI = deletes[-1] | |
309 self.assertEqual(otherNodeURI, redirectURI) | |
310 | |
311 def nodeCreated(nodeIdentifier): | |
312 uri = gateway.getXMPPURI(componentJID, nodeIdentifier) | |
313 request.args[b'uri'] = [uri] | |
314 payload = {b'redirect_uri': otherNodeURI} | |
315 body = simplejson.dumps(payload) | |
316 request.content = StringIO(body) | |
317 return _render(self.resource, request) | |
318 | |
319 def deleteNode(nodeIdentifier, owner, redirectURI): | |
320 deletes.append((nodeIdentifier, owner, redirectURI)) | |
321 return defer.succeed(nodeIdentifier) | |
322 | |
323 deletes = [] | |
324 self.patch(self.backend, 'deleteNode', deleteNode) | |
325 d = self.backend.createNode(u'test', ownerJID) | |
326 d.addCallback(nodeCreated) | |
327 d.addCallback(rendered) | |
328 return d | |
329 | |
330 | |
331 def test_postUnknownNode(self): | |
332 """ | |
333 If the node to be deleted is unknown, 404 Not Found is returned. | |
334 """ | |
335 request = DummyRequest([b'']) | |
336 request.method = b'POST' | |
337 | |
338 def rendered(result): | |
339 self.assertEqual(http.NOT_FOUND, request.responseCode) | |
340 | |
341 uri = gateway.getXMPPURI(componentJID, u'unknown') | |
342 request.args[b'uri'] = [uri] | |
343 request.content = StringIO(b'') | |
344 | |
345 d = _render(self.resource, request) | |
346 d.addCallback(rendered) | |
347 return d | |
348 | |
349 | |
350 def test_postURIMissing(self): | |
351 """ | |
352 If no URI is passed, 400 Bad Request is returned. | |
353 """ | |
354 request = DummyRequest([b'']) | |
355 request.method = b'POST' | |
356 | |
357 def rendered(result): | |
358 self.assertEqual(http.BAD_REQUEST, request.responseCode) | |
359 | |
360 request.content = StringIO(b'') | |
361 | |
362 d = _render(self.resource, request) | |
363 d.addCallback(rendered) | |
364 return d | |
365 | |
366 | |
367 class CallbackResourceTest(unittest.TestCase): | |
368 """ | |
369 Tests for L{gateway.CallbackResource}. | |
370 """ | |
371 | |
372 def setUp(self): | |
373 self.callbackEvents = [] | |
374 self.resource = gateway.CallbackResource(self._callback) | |
375 | |
376 | |
377 def _callback(self, payload, headers): | |
378 self.callbackEvents.append((payload, headers)) | |
379 | |
380 | |
381 def test_get(self): | |
382 """ | |
383 The method GET is not supported. | |
384 """ | |
385 request = DummyRequest([b'']) | |
386 self.assertRaises(error.UnsupportedMethod, | |
387 _render, self.resource, request) | |
388 | |
389 | |
390 def test_post(self): | |
391 """ | |
392 The body posted is passed to the callback. | |
393 """ | |
394 request = DummyRequest([b'']) | |
395 request.method = 'POST' | |
396 request.content = StringIO(b'<root><child/></root>') | |
397 | |
398 def rendered(result): | |
399 self.assertEqual(1, len(self.callbackEvents)) | |
400 payload, headers = self.callbackEvents[-1] | |
401 self.assertEqual('root', payload.name) | |
402 | |
403 self.assertEqual(http.NO_CONTENT, request.responseCode) | |
404 self.assertFalse(b''.join(request.written)) | |
405 | |
406 d = _render(self.resource, request) | |
407 d.addCallback(rendered) | |
408 return d | |
409 | |
410 | |
411 def test_postEvent(self): | |
412 """ | |
413 If the Event header is set, the payload is empty and the header passed. | |
414 """ | |
415 request = DummyRequest([b'']) | |
416 request.method = 'POST' | |
417 request.requestHeaders.addRawHeader(b'Event', b'DELETE') | |
418 request.content = StringIO(b'') | |
419 | |
420 def rendered(result): | |
421 self.assertEqual(1, len(self.callbackEvents)) | |
422 payload, headers = self.callbackEvents[-1] | |
423 self.assertIdentical(None, payload) | |
424 self.assertEqual(['DELETE'], headers.getRawHeaders(b'Event')) | |
425 self.assertFalse(b''.join(request.written)) | |
426 | |
427 d = _render(self.resource, request) | |
428 d.addCallback(rendered) | |
429 return d | |
430 | |
431 | |
81 | 432 |
82 class GatewayTest(unittest.TestCase): | 433 class GatewayTest(unittest.TestCase): |
83 timeout = 2 | 434 timeout = 2 |
84 | 435 |
85 def setUp(self): | 436 def setUp(self): |
141 | 492 |
142 def test_publishNonExisting(self): | 493 def test_publishNonExisting(self): |
143 def cb(err): | 494 def cb(err): |
144 self.assertEqual('404', err.status) | 495 self.assertEqual('404', err.status) |
145 | 496 |
146 d = self.client.publish(TEST_ENTRY, 'xmpp:%s?node=test' % componentJID) | 497 d = self.client.publish(TEST_ENTRY, 'xmpp:%s?node=test' % component) |
147 self.assertFailure(d, error.Error) | 498 self.assertFailure(d, error.Error) |
148 d.addCallback(cb) | 499 d.addCallback(cb) |
149 return d | 500 return d |
150 | 501 |
151 def test_delete(self): | 502 def test_delete(self): |
159 return d | 510 return d |
160 | 511 |
161 def test_deleteWithRedirect(self): | 512 def test_deleteWithRedirect(self): |
162 def cb(response): | 513 def cb(response): |
163 xmppURI = response['uri'] | 514 xmppURI = response['uri'] |
164 redirectURI = 'xmpp:%s?node=test' % componentJID | 515 redirectURI = 'xmpp:%s?node=test' % component |
165 d = self.client.delete(xmppURI, redirectURI) | 516 d = self.client.delete(xmppURI, redirectURI) |
166 return d | 517 return d |
167 | 518 |
168 d = self.client.create() | 519 d = self.client.create() |
169 d.addCallback(cb) | 520 d.addCallback(cb) |
196 d.addCallback(cb) | 547 d.addCallback(cb) |
197 d.addCallback(cb2) | 548 d.addCallback(cb2) |
198 return defer.gatherResults([d, self.client.deferred]) | 549 return defer.gatherResults([d, self.client.deferred]) |
199 | 550 |
200 def test_deleteNotificationWithRedirect(self): | 551 def test_deleteNotificationWithRedirect(self): |
201 redirectURI = 'xmpp:%s?node=test' % componentJID | 552 redirectURI = 'xmpp:%s?node=test' % component |
202 | 553 |
203 def onNotification(data, headers): | 554 def onNotification(data, headers): |
204 try: | 555 try: |
205 self.assertTrue(headers.hasHeader('Event')) | 556 self.assertTrue(headers.hasHeader('Event')) |
206 self.assertEquals(['DELETED'], headers.getRawHeaders('Event')) | 557 self.assertEquals(['DELETED'], headers.getRawHeaders('Event')) |
383 | 734 |
384 def test_subscribeNonExisting(self): | 735 def test_subscribeNonExisting(self): |
385 def cb(err): | 736 def cb(err): |
386 self.assertEqual('403', err.status) | 737 self.assertEqual('403', err.status) |
387 | 738 |
388 d = self.client.subscribe('xmpp:%s?node=test' % componentJID) | 739 d = self.client.subscribe('xmpp:%s?node=test' % component) |
389 self.assertFailure(d, error.Error) | 740 self.assertFailure(d, error.Error) |
390 d.addCallback(cb) | 741 d.addCallback(cb) |
391 return d | 742 return d |
392 | 743 |
393 | 744 |
394 def test_subscribeRootGetNotification(self): | 745 def test_subscribeRootGetNotification(self): |
746 | |
747 def clean(rootNode): | |
748 return self.client.unsubscribe(rootNode) | |
395 | 749 |
396 def onNotification(data, headers): | 750 def onNotification(data, headers): |
397 self.client.deferred.callback(None) | 751 self.client.deferred.callback(None) |
398 | 752 |
399 def cb(response): | 753 def cb(response): |
400 xmppURI = response['uri'] | 754 xmppURI = response['uri'] |
401 jid, nodeIdentifier = gateway.getServiceAndNode(xmppURI) | 755 jid, nodeIdentifier = gateway.getServiceAndNode(xmppURI) |
402 rootNode = gateway.getXMPPURI(jid, '') | 756 rootNode = gateway.getXMPPURI(jid, '') |
403 | 757 |
404 d = self.client.subscribe(rootNode) | 758 d = self.client.subscribe(rootNode) |
759 d.addCallback(lambda _: self.addCleanup(clean, rootNode)) | |
405 d.addCallback(lambda _: xmppURI) | 760 d.addCallback(lambda _: xmppURI) |
406 return d | 761 return d |
407 | 762 |
408 def cb2(xmppURI): | 763 def cb2(xmppURI): |
409 return self.client.publish(TEST_ENTRY, xmppURI) | 764 return self.client.publish(TEST_ENTRY, xmppURI) |
419 | 774 |
420 def test_unsubscribeNonExisting(self): | 775 def test_unsubscribeNonExisting(self): |
421 def cb(err): | 776 def cb(err): |
422 self.assertEqual('403', err.status) | 777 self.assertEqual('403', err.status) |
423 | 778 |
424 d = self.client.unsubscribe('xmpp:%s?node=test' % componentJID) | 779 d = self.client.unsubscribe('xmpp:%s?node=test' % component) |
425 self.assertFailure(d, error.Error) | 780 self.assertFailure(d, error.Error) |
426 d.addCallback(cb) | 781 d.addCallback(cb) |
427 return d | 782 return d |
428 | 783 |
429 | 784 |