def test_POST_after_body_read_and_stream_read(self): """ POST should be populated even if body is read first, and then the stream is read second. """ for method in HTTP_METHODS_WITH_BODY: payload = FakePayload('{"name": "佚名"}') request = WSGIRequest({ 'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': 'application/json', 'CONTENT_LENGTH': len(payload), 'wsgi.input': payload }) request.body # evaluate self.assertEqual(request.read(1), b'{') self.assertEqual(request.POST, {'name': '佚名'}) self.assertEqual(request.data, {'name': '佚名'})
def patch(self, uri, format='json', data=None, authentication=None, **kwargs): """ Performs a simulated ``PATCH`` request to the provided URI. Optionally accepts a ``data`` kwarg. **Unlike** ``GET``, in ``PATCH`` the ``data`` gets serialized & sent as the body instead of becoming part of the URI. Example:: from tastypie.test import TestApiClient client = TestApiClient() response = client.patch('/api/v1/entry/1/', data={ 'created': '2012-05-01T20:02:36', 'slug': 'another-post', 'title': 'Another Post', 'user': '******', }) Optionally accepts an ``authentication`` kwarg, which should be an HTTP header with the correct authentication data already setup. All other ``**kwargs`` passed in get passed through to the Django ``TestClient``. See https://docs.djangoproject.com/en/dev/topics/testing/#module-django.test.client for details. """ content_type = self.get_content_type(format) kwargs['content_type'] = content_type if data is not None: kwargs['data'] = self.serializer.serialize(data, format=content_type) if authentication is not None: kwargs['HTTP_AUTHORIZATION'] = authentication # This hurts because Django doesn't support PATCH natively. parsed = urlparse(uri) r = { 'CONTENT_LENGTH': len(kwargs['data']), 'CONTENT_TYPE': content_type, 'PATH_INFO': self.client._get_path(parsed), 'QUERY_STRING': parsed[4], 'REQUEST_METHOD': 'PATCH', 'wsgi.input': FakePayload(str(kwargs['data'])), } r.update(kwargs) return self.client.request(**r)
def generic(self, method, path, data='', content_type='application/octet-stream', **extra): parsed = _urlparse(path) data = force_bytes_or_smart_bytes(data, settings.DEFAULT_CHARSET) r = { 'PATH_INFO': self._get_path(parsed), 'QUERY_STRING': force_text(parsed[4]), 'REQUEST_METHOD': six.text_type(method), } if data: r.update({ 'CONTENT_LENGTH': len(data), 'CONTENT_TYPE': six.text_type(content_type), 'wsgi.input': FakePayload(data), }) r.update(extra) return self.request(**r)
def test_patch(self): """PATCH should edit some attributes.""" ci_count_before = CI.objects.count() ci_data = json.dumps({ 'business_owners': ['/api/v0.9/ciowners/{0}/'.format(self.owner1.id)], 'technical_owners': ['/api/v0.9/ciowners/{0}/'.format(self.owner2.id)], 'attributes': [ { 'name': 'SLA value', 'value': 0.7, }, { 'name': 'Documentation Link', 'value': 'http://www.gutenberg.org/files/27827/' '27827-h/27827-h.htm', }, ], }) req_data = { 'CONTENT_LENGTH': len(ci_data), 'CONTENT_TYPE': 'application/json', 'PATH_INFO': '/api/v0.9/ci/{0}/'.format(self.ci1.id), 'REQUEST_METHOD': 'PATCH', 'wsgi.input': FakePayload(ci_data), } req_data.update(self.headers) self.client.request(**req_data) self.assertEqual(CI.objects.count(), ci_count_before) edited = CI.objects.get(pk=self.ci1.id) self.assertEqual(edited.name, 'ciname1') self.assertEqual(edited.uid, 'uid-ci1') self.assertSetEqual( set(edited.business_owners.all()), {self.owner1}, ) self.assertSetEqual( set(av.value for av in edited.ciattributevalue_set.all()), { 0.7, 'http://www.gutenberg.org/files/27827/27827-h/27827-h.htm', }, )
def test_value_after_read(self): """ Construction of POST or body is not allowed after reading from request. """ for method in HTTP_METHODS_WITH_BODY: payload = FakePayload('{"name": "value"}') request = WSGIRequest({ 'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': 'application/json', 'CONTENT_LENGTH': len(payload), 'wsgi.input': payload }) self.assertEqual(request.read(2), b'{"') with self.assertRaises(RawPostDataException): request.body self.assertEqual(request.data, {})
def test_file_passes(self): payload = FakePayload("\r\n".join([ "--boundary", 'Content-Disposition: form-data; name="file1"; ' 'filename="test.file"', "", "value", "--boundary--", ])) request = WSGIRequest({ "REQUEST_METHOD": "POST", "CONTENT_TYPE": "multipart/form-data; boundary=boundary", "CONTENT_LENGTH": len(payload), "wsgi.input": payload, }) with self.settings(DATA_UPLOAD_MAX_MEMORY_SIZE=1): request._load_post_and_files() self.assertIn("file1", request.FILES, "Upload file not present")
def setUp(self): payload = FakePayload("\r\n".join([ "--boundary", 'Content-Disposition: form-data; name="name1"', "", "value1", "--boundary", 'Content-Disposition: form-data; name="name2"', "", "value2", "--boundary--", ])) self.request = WSGIRequest({ "REQUEST_METHOD": "POST", "CONTENT_TYPE": "multipart/form-data; boundary=boundary", "CONTENT_LENGTH": len(payload), "wsgi.input": payload, })
def test_json_resonse_mixin(): class ViewClass(JsonRequestMixin, View): def post(self, request): return self.data() view = ViewClass.as_view() data = u'\N{SNOWMAN}' encoded_data = json.dumps(data).encode('utf-16') # BBB: Just use RequestFactory.generic in Django >= 1.5 params = { 'wsgi.input': FakePayload(encoded_data), 'CONTENT_TYPE': 'application/json', 'CONTENT_LENGTH': len(encoded_data), } request = RequestFactory().post('/', **params) request.encoding = 'utf-16' response = view(request) assert response == data
def patch(self, path, data={}, content_type=MULTIPART_CONTENT, **extra): "Construct a PATCH request." warn( "This custom patch method here is deprecated in Django 1.5 by the ``generic`` method." ) post_data = self._encode_data(data, content_type) parsed = urlparse(path) r = { 'CONTENT_LENGTH': len(post_data), 'CONTENT_TYPE': content_type, 'PATH_INFO': self._get_path(parsed), 'QUERY_STRING': parsed[4], 'REQUEST_METHOD': 'PATCH', 'wsgi.input': FakePayload(post_data), } r.update(extra) return self.request(**r)
def test_POST_immutable_for_multipart(self): """ MultiPartParser.parse() leaves request.POST immutable. """ payload = FakePayload("\r\n".join([ '--boundary', 'Content-Disposition: form-data; name="name"', '', 'value', '--boundary--', ])) request = WSGIRequest({ 'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': 'multipart/form-data; boundary=boundary', 'CONTENT_LENGTH': len(payload), 'wsgi.input': payload, }) self.assertFalse(request.POST._mutable)
def test_POST_immutable_for_multipart(self): """ MultiPartParser.parse() leaves request.POST immutable. """ payload = FakePayload("\r\n".join([ "--boundary", 'Content-Disposition: form-data; name="name"', "", "value", "--boundary--", ])) request = WSGIRequest({ "REQUEST_METHOD": "POST", "CONTENT_TYPE": "multipart/form-data; boundary=boundary", "CONTENT_LENGTH": len(payload), "wsgi.input": payload, }) self.assertFalse(request.POST._mutable)
def get_render_from_response(request, url, get_forwarding=False): path, query = url, '' if '?' in url: path, query = url.split('?', 1) query = query.encode('utf-8') if request and request.GET: try: requestpath = request.get_full_path() path2, query2 = requestpath.split('?', 1) query2 = query2.encode('utf-8') if query: query += '&' + query2 else: query = query2 except: pass if request: meta = dict(request.META, PATH_INFO=path, QUERY_STRING=query) if not get_forwarding: meta = dict(request.META, PATH_INFO=path, QUERY_STRING='') else: meta = dict(PATH_INFO=path) # this is because django 1.3 now checking wsgi.input attribute in # request https://code.djangoproject.com/changeset/14453 meta['wsgi.input'] = FakePayload('') # XXX maybe not needed since 1.4? response = Client().request(**meta) if isinstance(response, http.HttpResponseRedirect): url = response['Location'] host = request.get_host() if host in url: url = url.split(host)[1] else: raise http.HttpResponseBadRequest( 'Cross domain includes not allowed! %s' % response['Location']) return get_render_from_response( request, url, get_forwarding=get_forwarding) return response.content.decode('utf-8')
def move(self, path, data={}, content_type=MULTIPART_CONTENT, follow=False, **extra): """ Send a resource to the server using MOVE. """ parsed = urlparse(path) r = { 'CONTENT_TYPE': 'text/html; charset=utf-8', 'PATH_INFO': self._get_path(parsed), 'QUERY_STRING': urlencode(data, doseq=True) or parsed[4], 'REQUEST_METHOD': 'MOVE', 'wsgi.input': FakePayload('') } r.update(extra) response = self.request(**r) if follow: response = self._handle_redirects(response, **extra) return response
def test_body_after_POST_multipart_form_data(self): """ Reading body after parsing multipart/form-data is not allowed """ # Because multipart is used for large amounts fo data i.e. file uploads, # we don't want the data held in memory twice, and we don't want to # silence the error by setting body = '' either. payload = FakePayload("\r\n".join([ '--boundary', 'Content-Disposition: form-data; name="name"', '', 'value', '--boundary--' ''])) request = WSGIRequest({'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': 'multipart/form-data; boundary=boundary', 'CONTENT_LENGTH': len(payload), 'wsgi.input': payload}) self.assertEqual(request.POST, {'name': ['value']}) self.assertRaises(RawPostDataException, lambda: request.body)
def test_POST_multipart_with_content_length_zero(self): """ Multipart POST requests with Content-Length >= 0 are valid and need to be handled. """ # According to: # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13 # Every request.POST with Content-Length >= 0 is a valid request, # this test ensures that we handle Content-Length == 0. payload = FakePayload("\r\n".join([ '--boundary', 'Content-Disposition: form-data; name="name"', '', 'value', '--boundary--' ''])) request = WSGIRequest({'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': 'multipart/form-data; boundary=boundary', 'CONTENT_LENGTH': 0, 'wsgi.input': payload}) self.assertEqual(request.POST, {})
def test_POST_after_body_read_and_stream_read_multipart(self): """ POST should be populated even if body is read first, and then the stream is read second. Using multipart/form-data instead of urlencoded. """ payload = FakePayload("\r\n".join([ '--boundary', 'Content-Disposition: form-data; name="name"', '', 'value', '--boundary--' ''])) request = WSGIRequest({'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': 'multipart/form-data; boundary=boundary', 'CONTENT_LENGTH': len(payload), 'wsgi.input': payload}) request.body # evaluate # Consume enough data to mess up the parsing: self.assertEqual(request.read(13), b'--boundary\r\nC') self.assertEqual(request.POST, {'name': ['value']})
def get_fake_request(meta): '''Retrieves a fake request using the given request.META. This allows celery tasks to have a "request" to use in code.''' # if the body was cached in the meta, put it back as the wsgi.input if BODY_KEY in meta: meta['wsgi.input'] = FakePayload(meta[BODY_KEY]) # create a basic request using the Django testing framework request = RequestFactory().request(**meta) # run middleware on it handler = BaseHandler() handler.load_middleware() for middleware_method in handler._request_middleware: response = middleware_method(request) if response: raise Exception( "Middleware cannot return a response with a FakeRequest.") # return the request return request
def request(self, **request): """ Similar to parent class, but returns the request object as soon as it has created it. """ environ = { 'HTTP_COOKIE': self.cookies.output(header='', sep='; '), 'PATH_INFO': '/', 'QUERY_STRING': '', 'REQUEST_METHOD': 'GET', 'SCRIPT_NAME': '', 'SERVER_NAME': 'testserver', 'SERVER_PORT': 80, 'SERVER_PROTOCOL': 'HTTP/1.1', 'wsgi.input': FakePayload(''), } environ.update(self.defaults) environ.update(request) return WSGIRequest(environ)
def request(self, **request): environ = { 'HTTP_COOKIE': self.cookies.output(header='', sep='; '), 'PATH_INFO': str('/'), 'REMOTE_ADDR': str('127.0.0.1'), 'REQUEST_METHOD': str('GET'), 'SCRIPT_NAME': str(''), 'SERVER_NAME': str('testserver'), 'SERVER_PORT': str('80'), 'SERVER_PROTOCOL': str('HTTP/1.1'), 'wsgi.version': (1, 0), 'wsgi.url_scheme': str('http'), 'wsgi.input': FakePayload(b''), 'wsgi.errors': self.errors, 'wsgi.multiprocess': True, 'wsgi.multithread': False, 'wsgi.run_once': False, } environ.update(self.defaults) environ.update(request) return WSGIRequest(environ)
def post_raw_data(self, path, post_data): """ The built-in test client's post() method assumes the data you pass is a dictionary and encodes it. If we just want to pass the data unmodified, we need our own version of post(). """ parsed = urlparse(path) r = { 'CONTENT_LENGTH': len(post_data), 'CONTENT_TYPE': "text/plain", 'PATH_INFO': self.client._get_path(parsed), 'QUERY_STRING': force_str(parsed[4]), 'REQUEST_METHOD': str('POST'), 'wsgi.input': FakePayload(post_data), } # Add the request signature to the headers being sent. r.update(self.get_signature(path, method='POST', body=post_data)) # Make the actual request. return self.client.request(**r)
def patch(self, path, data={}, content_type=MULTIPART_CONTENT, **extra): """ Construct a PATCH request." :param path: :param data: :param content_type: :param extra: :return: """ patch_data = self._encode_data(data, content_type) parsed = urlparse(path) r = { 'CONTENT_LENGTH': len(patch_data), 'CONTENT_TYPE': content_type, 'PATH_INFO': self._get_path(parsed), 'QUERY_STRING': parsed[4], 'REQUEST_METHOD': 'PATCH', 'wsgi.input': FakePayload(patch_data), } r.update(extra) return self.request(**r)
def setUp(self): """initialize a basic djagno wsgi request.""" # copied from django.test.client self.environ = { 'HTTP_COOKIE': '', 'PATH_INFO': '/', 'REMOTE_ADDR': '127.0.0.1', 'REQUEST_METHOD': 'GET', 'SCRIPT_NAME': '', 'SERVER_NAME': 'testserver', 'SERVER_PORT': '80', 'SERVER_PROTOCOL': 'HTTP/1.1', 'wsgi.version': (1, 0), 'wsgi.url_scheme': 'http', 'wsgi.input': FakePayload(b''), 'wsgi.errors': '', 'wsgi.multiprocess': True, 'wsgi.multithread': False, 'wsgi.run_once': False, } self.request = WSGIRequest(self.environ)
def test_body_after_POST_multipart_related(self): """ Reading body after parsing multipart that isn't form-data is allowed """ # Ticket #9054 # There are cases in which the multipart data is related instead of # being a binary upload, in which case it should still be accessible # via body. payload_data = b"\r\n".join([ b'--boundary', b'Content-ID: id; name="name"', b'', b'value', b'--boundary--' b'']) payload = FakePayload(payload_data) request = WSGIRequest({'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': 'multipart/related; boundary=boundary', 'CONTENT_LENGTH': len(payload), 'wsgi.input': payload}) self.assertEqual(request.POST, {}) self.assertEqual(request.body, payload_data)
def put(self, url, consumer=None, token=None, callback=False, verifier=None, data={}, content_type=MULTIPART_CONTENT, **kwargs): """ Send a resource to the server using PUT. """ # If data has come from JSON remove unicode keys. data = dict([(str(k), v) for k, v in data.items()]) url = get_absolute_url(url) params = _get_args(consumer, callback=callback, verifier=verifier) params.update(data_keys(data)) req = oauth.Request(method='PUT', url=url, parameters=params) req.sign_request(self.signature_method, consumer, token) post_data = encode_multipart(BOUNDARY, data) parsed = urlparse.urlparse(url) query_string = urllib.urlencode(req, doseq=True) r = { 'CONTENT_LENGTH': len(post_data), 'CONTENT_TYPE': content_type, 'PATH_INFO': urllib.unquote(parsed[2]), 'QUERY_STRING': query_string, 'REQUEST_METHOD': 'PUT', 'wsgi.input': FakePayload(post_data), 'HTTP_HOST': 'api', 'HTTP_AUTHORIZATION': 'OAuth realm=""', } r.update(req) response = self.request(**r) return response
def test_POST_after_body_read_and_stream_read_multipart(self): """ POST should be populated even if body is read first, and then the stream is read second. Using multipart/form-data instead of urlencoded. """ payload = FakePayload("\r\n".join([ "--boundary", 'Content-Disposition: form-data; name="name"', "", "value", "--boundary--" "", ])) request = WSGIRequest({ "REQUEST_METHOD": "POST", "CONTENT_TYPE": "multipart/form-data; boundary=boundary", "CONTENT_LENGTH": len(payload), "wsgi.input": payload, }) request.body # evaluate # Consume enough data to mess up the parsing: self.assertEqual(request.read(13), b"--boundary\r\nC") self.assertEqual(request.POST, {"name": ["value"]})
def test_POST_multipart_with_content_length_zero(self): """ Multipart POST requests with Content-Length >= 0 are valid and need to be handled. """ # According to: # https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13 # Every request.POST with Content-Length >= 0 is a valid request, # this test ensures that we handle Content-Length == 0. payload = FakePayload("\r\n".join([ "--boundary", 'Content-Disposition: form-data; name="name"', "", "value", "--boundary--", ])) request = WSGIRequest({ "REQUEST_METHOD": "POST", "CONTENT_TYPE": "multipart/form-data; boundary=boundary", "CONTENT_LENGTH": 0, "wsgi.input": payload, }) self.assertEqual(request.POST, {})
def request(self, method, path, data='', content_type=None, status=OK): if not path.startswith('/'): path = '/' + path if not isinstance(data, str): data = json.dumps(data) content_type = content_type or 'application/json' parsed = urlparse(path) request = { 'REQUEST_METHOD': method, 'PATH_INFO': urllib.unquote(parsed[2]), 'QUERY_STRING': parsed[4], 'wsgi.input': FakePayload(data), 'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest', } if data: request['CONTENT_LENGTH'] = len(data) if content_type: request['CONTENT_TYPE'] = content_type response = self.client.request(**request) self.assertEqual(response.status_code, status, response.content) return (json.loads(response.content) if response['Content-Type'].startswith('application/json') else response.content)
def test_body_after_POST_multipart_form_data(self): """ Reading body after parsing multipart/form-data is not allowed """ # Because multipart is used for large amounts of data i.e. file uploads, # we don't want the data held in memory twice, and we don't want to # silence the error by setting body = '' either. payload = FakePayload("\r\n".join([ "--boundary", 'Content-Disposition: form-data; name="name"', "", "value", "--boundary--", ])) request = WSGIRequest({ "REQUEST_METHOD": "POST", "CONTENT_TYPE": "multipart/form-data; boundary=boundary", "CONTENT_LENGTH": len(payload), "wsgi.input": payload, }) self.assertEqual(request.POST, {"name": ["value"]}) with self.assertRaises(RawPostDataException): request.body
def request_factory_patch(self, path, data=None, content_type=MULTIPART_CONTENT, **extra): """ Construct a PATCH request. """ # pylint: disable=invalid-name patch_data = self._encode_data(data or {}, content_type) parsed = urlparse(path) r = { 'CONTENT_LENGTH': len(patch_data), 'CONTENT_TYPE': content_type, 'PATH_INFO': self._get_path(parsed), 'QUERY_STRING': parsed[4], 'REQUEST_METHOD': 'PATCH', 'wsgi.input': FakePayload(patch_data), } r.update(extra) return self.request(**r)
def test_invalid_multipart_content_length(self): data = 'data' boundary = 'boundary' parsed = urlparse('/path/') environ = self.client._base_environ(**{ 'CONTENT_TYPE': 'multipart/form-data; boundary=%s' % boundary, 'CONTENT_LENGTH': -1, 'PATH_INFO': self.client._get_path(parsed), 'QUERY_STRING': parsed[4], 'REQUEST_METHOD': 'POST', 'wsgi.input': FakePayload(data), }) request = WSGIRequest(environ) for content_type in self.serializer.content_types.keys(): if not content_type.startswith('multipart/'): continue self.assertRaises( ValueError, self.serializer.deserialize_request, request, request.META['CONTENT_TYPE'] )
def test_write_after_read(self): payload = FakePayload() payload.read() msg = "Unable to write a payload after it's been read" with self.assertRaisesMessage(ValueError, msg): payload.write(b'abc')