Mercurial > p > roundup > code
diff test/rest_common.py @ 6311:be8d5a8e090a
Fix uncaught error when parsing rest headers, document
Started this work as better docs for rest response format. But I found
406 error response was not being tested. Also there was no error for
bad Content-Type.
In rest.py fix uncaught exceptions due to invalid Accept or
Content-Type headers. If Content-type is valid but not
application/json return code 415.
Document use of accept header (was only shown in examples) and support
for q parameter. Describe using .xml and .json extensions to select
return format for testing from browser (where setting accept header is
a problem). Document 406 error code return. Document 415 error code
return and acceptable content types. Previously only doc was in
examples.
Set up tests for 406 and 415 error codes.
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Fri, 01 Jan 2021 14:14:34 -0500 |
| parents | 29c6dc8ed004 |
| children | 6ef7b66774b4 |
line wrap: on
line diff
--- a/test/rest_common.py Sun Dec 27 15:04:02 2020 -0500 +++ b/test/rest_common.py Fri Jan 01 14:14:34 2021 -0500 @@ -1321,6 +1321,204 @@ ['assignedto']['link'], "http://tracker.example/cgi-bin/roundup.cgi/bugs/rest/data/user/2") + def testDispatchBadContent(self): + """ + runthrough rest dispatch() with bad content_type patterns. + """ + + # simulate: /rest/data/issue + body=b'{ "title": "Joe Doe has problems", \ + "nosy": [ "1", "3" ], \ + "assignedto": "2", \ + "abool": true, \ + "afloat": 2.3, \ + "anint": 567890 \ + }' + env = { "CONTENT_TYPE": "application/jzot", + "CONTENT_LENGTH": len(body), + "REQUEST_METHOD": "POST" + } + + headers={"accept": "application/json; version=1", + "content-type": env['CONTENT_TYPE'], + "content-length": env['CONTENT_LENGTH'], + } + + self.headers=headers + # we need to generate a FieldStorage the looks like + # FieldStorage(None, None, 'string') rather than + # FieldStorage(None, None, []) + body_file=BytesIO(body) # FieldStorage needs a file + form = client.BinaryFieldStorage(body_file, + headers=headers, + environ=env) + self.server.client.request.headers.get=self.get_header + results = self.server.dispatch(env["REQUEST_METHOD"], + "/rest/data/issue", + form) + + print(results) + self.assertEqual(self.server.client.response_code, 415) + json_dict = json.loads(b2s(results)) + self.assertEqual(json_dict['error']['msg'], + "Unable to process input of type application/jzot") + + # Test GET as well. I am not sure if this should pass or not. + # Arguably GET doesn't use any form/json input but.... + results = self.server.dispatch('GET', + "/rest/data/issue", + form) + print(results) + self.assertEqual(self.server.client.response_code, 415) + + + + def testDispatchBadAccept(self): + # simulate: /rest/data/issue expect failure unknown accept settings + body=b'{ "title": "Joe Doe has problems", \ + "nosy": [ "1", "3" ], \ + "assignedto": "2", \ + "abool": true, \ + "afloat": 2.3, \ + "anint": 567890 \ + }' + env = { "CONTENT_TYPE": "application/json", + "CONTENT_LENGTH": len(body), + "REQUEST_METHOD": "POST" + } + + headers={"accept": "application/zot; version=1; q=0.5", + "content-type": env['CONTENT_TYPE'], + "content-length": env['CONTENT_LENGTH'], + } + + self.headers=headers + # we need to generate a FieldStorage the looks like + # FieldStorage(None, None, 'string') rather than + # FieldStorage(None, None, []) + body_file=BytesIO(body) # FieldStorage needs a file + form = client.BinaryFieldStorage(body_file, + headers=headers, + environ=env) + self.server.client.request.headers.get=self.get_header + results = self.server.dispatch(env["REQUEST_METHOD"], + "/rest/data/issue", + form) + + print(results) + self.assertEqual(self.server.client.response_code, 406) + self.assertIn(b"Requested content type 'application/zot; version=1; q=0.5' is not available.\nAcceptable types: */*, application/json,", results) + + # simulate: /rest/data/issue works, multiple acceptable output, one + # is valid + env = { "CONTENT_TYPE": "application/json", + "CONTENT_LENGTH": len(body), + "REQUEST_METHOD": "POST" + } + + headers={"accept": "application/zot; version=1; q=0.75, " + "application/json; version=1; q=0.5", + "content-type": env['CONTENT_TYPE'], + "content-length": env['CONTENT_LENGTH'], + } + + self.headers=headers + # we need to generate a FieldStorage the looks like + # FieldStorage(None, None, 'string') rather than + # FieldStorage(None, None, []) + body_file=BytesIO(body) # FieldStorage needs a file + form = client.BinaryFieldStorage(body_file, + headers=headers, + environ=env) + self.server.client.request.headers.get=self.get_header + results = self.server.dispatch(env["REQUEST_METHOD"], + "/rest/data/issue", + form) + + print(results) + self.assertEqual(self.server.client.response_code, 201) + json_dict = json.loads(b2s(results)) + # ERROR this should be 1. What's happening is that the code + # for handling 406 error code runs through everything and creates + # the item. Then it throws a 406 after the work is done when it + # realizes it can't respond as requested. So the 406 post above + # creates issue 1 and this one creates issue 2. + self.assertEqual(json_dict['data']['id'], "2") + + + # test 3 accept is empty. This triggers */* so passes + headers={"accept": "", + "content-type": env['CONTENT_TYPE'], + "content-length": env['CONTENT_LENGTH'], + } + + self.headers=headers + # we need to generate a FieldStorage the looks like + # FieldStorage(None, None, 'string') rather than + # FieldStorage(None, None, []) + body_file=BytesIO(body) # FieldStorage needs a file + form = client.BinaryFieldStorage(body_file, + headers=headers, + environ=env) + self.server.client.request.headers.get=self.get_header + results = self.server.dispatch(env["REQUEST_METHOD"], + "/rest/data/issue", + form) + + print(results) + self.assertEqual(self.server.client.response_code, 201) + json_dict = json.loads(b2s(results)) + # This is one more than above. Will need to be fixed + # When error above is fixed. + self.assertEqual(json_dict['data']['id'], "3") + + # test 4 accept is random junk. + headers={"accept": "Xyzzy I am not a mime, type;", + "content-type": env['CONTENT_TYPE'], + "content-length": env['CONTENT_LENGTH'], + } + + self.headers=headers + # we need to generate a FieldStorage the looks like + # FieldStorage(None, None, 'string') rather than + # FieldStorage(None, None, []) + body_file=BytesIO(body) # FieldStorage needs a file + form = client.BinaryFieldStorage(body_file, + headers=headers, + environ=env) + self.server.client.request.headers.get=self.get_header + results = self.server.dispatch(env["REQUEST_METHOD"], + "/rest/data/issue", + form) + + print(results) + self.assertEqual(self.server.client.response_code, 406) + json_dict = json.loads(b2s(results)) + self.assertIn('Unable to parse Accept Header. Invalid media type: Xyzzy I am not a mime. Acceptable types: */* application/json', json_dict['error']['msg']) + + # test 5 accept mimetype is ok, param is not + headers={"accept": "*/*; foo", + "content-type": env['CONTENT_TYPE'], + "content-length": env['CONTENT_LENGTH'], + } + + self.headers=headers + # we need to generate a FieldStorage the looks like + # FieldStorage(None, None, 'string') rather than + # FieldStorage(None, None, []) + body_file=BytesIO(body) # FieldStorage needs a file + form = client.BinaryFieldStorage(body_file, + headers=headers, + environ=env) + self.server.client.request.headers.get=self.get_header + results = self.server.dispatch(env["REQUEST_METHOD"], + "/rest/data/issue", + form) + + print(results) + self.assertEqual(self.server.client.response_code, 406) + json_dict = json.loads(b2s(results)) + self.assertIn('Unable to parse Accept Header. Invalid param: foo. Acceptable types: */* application/json', json_dict['error']['msg']) def testStatsGen(self): # check stats being returned by put and get ops
