|
- #!/usr/bin/env python
-
- from klein import Klein
- from klein.interfaces import IKleinRequest
- from twisted.internet.defer import Deferred
- from twisted.trial import unittest
- from twisted.web.http_headers import Headers
- from zope.interface import implements
- from StringIO import StringIO
- from requests.structures import CaseInsensitiveDict
-
- __all__ = [ 'FakeRequests', ]
-
- class FakeHTTPRequest(object):
- # https://github.com/twisted/twisted/blob/twisted-19.7.0/src/twisted/web/http.py#L664
- implements(IKleinRequest)
-
- code = 200
-
- def __init__(self, meth, uri, data):
-
- #self.requestHeaders = Headers()
- self.responseHeaders = Headers()
-
- self.content = StringIO(data)
-
- self.path = uri
- self.prepath = []
- self.postpath = uri.split('/')
- self.method = meth
- self.notifications = []
- self.finished = False
-
- def setHeader(self, name, value):
- self.responseHeaders.setRawHeaders(name, [value])
-
- def setResponseCode(self, code, message=None):
- self.code = code
-
- def getRequestHostname(self):
- return ''
-
- def getHost(self):
- return ''
-
- def isSecure(self):
- return False
-
- def processingFailed(self, failure):
- self.setResponseCode(500, 'Internal Server Error')
-
- print 'f:', `failure`
- print 'b:', failure.getTraceback()
-
- def _cleanup(self):
- for d in self.notifications:
- d.callback(None)
- self.notifications = []
-
- def finish(self):
- if self.finished: # pragma: no cover
- warnings.warn('Warning! request.finish called twice.', stacklevel=2)
-
- self.finished = True
-
- self._cleanup()
-
- def notifyFinish(self):
- self.notifications.append(Deferred())
- return self.notifications[-1]
-
- class FakeRequestsResponse(object):
- def __init__(self, req):
- self._req = req
- self._io = StringIO()
- req.write = self.write
-
- def _finished(self, arg):
- if arg is not None: # pragma: no cover
- raise NotImplementedError('cannot handle exceptions yet')
-
- self.status_code = self._req.code
-
- self.text = self._io.getvalue()
- self.headers = CaseInsensitiveDict((k.lower(), v[-1]) for k, v in self._req.responseHeaders.getAllRawHeaders())
-
- def write(self, data):
- self._io.write(data)
-
- class FakeRequests(object):
- '''This class wraps a Klein app into a calling interface that is similar
- to the requests module for testing apps.
-
- Example test:
- ```
- app = Klein()
-
- @app.route('/')
- def home(request):
- return 'hello'
-
- class TestFakeRequests(unittest.TestCase):
- def setUp(self):
- self.requests = FakeRequests(app)
-
- def test_basic(self):
- r = self.requests.get('/')
- self.assertEqual(r.status_code, 200)
- self.assertEqual(r.text, 'hello')
- ```
- '''
-
- def __init__(self, app):
- '''Wrap the passed in app as if it was a server for a requests
- like interface. The URLs expected will not be complete urls.'''
-
- self._app = app
- self._res = app.resource()
-
- def _makerequest(self, method, url, data=''):
- if url[0] != '/':
- raise ValueError('url must be absolute (start w/ a slash)')
-
- req = FakeHTTPRequest('GET', url, data)
- resp = FakeRequestsResponse(req)
-
- req.notifyFinish().addBoth(resp._finished)
-
- r = self._res.render(req)
-
- return resp
-
- def get(self, url):
- '''Return a response for the passed in url.'''
-
- return self._makerequest('GET', url)
-
- def put(self, url, data=''):
- '''Make a put request to the provied URL w/ the body of data.'''
-
- return self._makerequest('PUT', url, data)
-
- class TestFakeRequests(unittest.TestCase):
- def setUp(self):
- self.putdata = []
-
- app = Klein()
-
- @app.route('/')
- def home(request):
- request.setHeader('x-testing', 'value')
-
- return 'hello'
-
- @app.route('/500')
- def causeerror(request):
- raise ValueError('random exception')
-
- @app.route('/put')
- def putreq(request):
- self.putdata.append(request.content.read())
-
- request.setResponseCode(201)
- return ''
-
- @app.route('/404')
- def notfound(request):
- request.setResponseCode(404)
- return 'not found'
-
- self.requests = FakeRequests(app)
-
- def test_bad(self):
- self.assertRaises(ValueError, self.requests.get, 'foobar')
-
- def test_basic(self):
- r = self.requests.get('/')
- self.assertEqual(r.status_code, 200)
- self.assertEqual(r.text, 'hello')
- self.assertEqual(r.headers['X-testing'], 'value')
-
- r = self.requests.get('/404')
- self.assertEqual(r.status_code, 404)
-
- r = self.requests.get('/nonexistent')
- self.assertEqual(r.status_code, 404)
-
- r = self.requests.get('/500')
- self.assertEqual(r.status_code, 500)
-
- body = 'body'
- r = self.requests.put('/put', data=body)
- self.assertEqual(r.status_code, 201)
- self.assertEqual(r.text, '')
- self.assertEqual(''.join(self.putdata), body)
|