Mercurial > p > roundup > code
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 6310:68d83479747b | 6311:be8d5a8e090a |
|---|---|
| 1319 self.assertEqual(len(json_dict['data']['attributes']['nosy']), 3) | 1319 self.assertEqual(len(json_dict['data']['attributes']['nosy']), 3) |
| 1320 self.assertEqual(json_dict['data']['attributes']\ | 1320 self.assertEqual(json_dict['data']['attributes']\ |
| 1321 ['assignedto']['link'], | 1321 ['assignedto']['link'], |
| 1322 "http://tracker.example/cgi-bin/roundup.cgi/bugs/rest/data/user/2") | 1322 "http://tracker.example/cgi-bin/roundup.cgi/bugs/rest/data/user/2") |
| 1323 | 1323 |
| 1324 def testDispatchBadContent(self): | |
| 1325 """ | |
| 1326 runthrough rest dispatch() with bad content_type patterns. | |
| 1327 """ | |
| 1328 | |
| 1329 # simulate: /rest/data/issue | |
| 1330 body=b'{ "title": "Joe Doe has problems", \ | |
| 1331 "nosy": [ "1", "3" ], \ | |
| 1332 "assignedto": "2", \ | |
| 1333 "abool": true, \ | |
| 1334 "afloat": 2.3, \ | |
| 1335 "anint": 567890 \ | |
| 1336 }' | |
| 1337 env = { "CONTENT_TYPE": "application/jzot", | |
| 1338 "CONTENT_LENGTH": len(body), | |
| 1339 "REQUEST_METHOD": "POST" | |
| 1340 } | |
| 1341 | |
| 1342 headers={"accept": "application/json; version=1", | |
| 1343 "content-type": env['CONTENT_TYPE'], | |
| 1344 "content-length": env['CONTENT_LENGTH'], | |
| 1345 } | |
| 1346 | |
| 1347 self.headers=headers | |
| 1348 # we need to generate a FieldStorage the looks like | |
| 1349 # FieldStorage(None, None, 'string') rather than | |
| 1350 # FieldStorage(None, None, []) | |
| 1351 body_file=BytesIO(body) # FieldStorage needs a file | |
| 1352 form = client.BinaryFieldStorage(body_file, | |
| 1353 headers=headers, | |
| 1354 environ=env) | |
| 1355 self.server.client.request.headers.get=self.get_header | |
| 1356 results = self.server.dispatch(env["REQUEST_METHOD"], | |
| 1357 "/rest/data/issue", | |
| 1358 form) | |
| 1359 | |
| 1360 print(results) | |
| 1361 self.assertEqual(self.server.client.response_code, 415) | |
| 1362 json_dict = json.loads(b2s(results)) | |
| 1363 self.assertEqual(json_dict['error']['msg'], | |
| 1364 "Unable to process input of type application/jzot") | |
| 1365 | |
| 1366 # Test GET as well. I am not sure if this should pass or not. | |
| 1367 # Arguably GET doesn't use any form/json input but.... | |
| 1368 results = self.server.dispatch('GET', | |
| 1369 "/rest/data/issue", | |
| 1370 form) | |
| 1371 print(results) | |
| 1372 self.assertEqual(self.server.client.response_code, 415) | |
| 1373 | |
| 1374 | |
| 1375 | |
| 1376 def testDispatchBadAccept(self): | |
| 1377 # simulate: /rest/data/issue expect failure unknown accept settings | |
| 1378 body=b'{ "title": "Joe Doe has problems", \ | |
| 1379 "nosy": [ "1", "3" ], \ | |
| 1380 "assignedto": "2", \ | |
| 1381 "abool": true, \ | |
| 1382 "afloat": 2.3, \ | |
| 1383 "anint": 567890 \ | |
| 1384 }' | |
| 1385 env = { "CONTENT_TYPE": "application/json", | |
| 1386 "CONTENT_LENGTH": len(body), | |
| 1387 "REQUEST_METHOD": "POST" | |
| 1388 } | |
| 1389 | |
| 1390 headers={"accept": "application/zot; version=1; q=0.5", | |
| 1391 "content-type": env['CONTENT_TYPE'], | |
| 1392 "content-length": env['CONTENT_LENGTH'], | |
| 1393 } | |
| 1394 | |
| 1395 self.headers=headers | |
| 1396 # we need to generate a FieldStorage the looks like | |
| 1397 # FieldStorage(None, None, 'string') rather than | |
| 1398 # FieldStorage(None, None, []) | |
| 1399 body_file=BytesIO(body) # FieldStorage needs a file | |
| 1400 form = client.BinaryFieldStorage(body_file, | |
| 1401 headers=headers, | |
| 1402 environ=env) | |
| 1403 self.server.client.request.headers.get=self.get_header | |
| 1404 results = self.server.dispatch(env["REQUEST_METHOD"], | |
| 1405 "/rest/data/issue", | |
| 1406 form) | |
| 1407 | |
| 1408 print(results) | |
| 1409 self.assertEqual(self.server.client.response_code, 406) | |
| 1410 self.assertIn(b"Requested content type 'application/zot; version=1; q=0.5' is not available.\nAcceptable types: */*, application/json,", results) | |
| 1411 | |
| 1412 # simulate: /rest/data/issue works, multiple acceptable output, one | |
| 1413 # is valid | |
| 1414 env = { "CONTENT_TYPE": "application/json", | |
| 1415 "CONTENT_LENGTH": len(body), | |
| 1416 "REQUEST_METHOD": "POST" | |
| 1417 } | |
| 1418 | |
| 1419 headers={"accept": "application/zot; version=1; q=0.75, " | |
| 1420 "application/json; version=1; q=0.5", | |
| 1421 "content-type": env['CONTENT_TYPE'], | |
| 1422 "content-length": env['CONTENT_LENGTH'], | |
| 1423 } | |
| 1424 | |
| 1425 self.headers=headers | |
| 1426 # we need to generate a FieldStorage the looks like | |
| 1427 # FieldStorage(None, None, 'string') rather than | |
| 1428 # FieldStorage(None, None, []) | |
| 1429 body_file=BytesIO(body) # FieldStorage needs a file | |
| 1430 form = client.BinaryFieldStorage(body_file, | |
| 1431 headers=headers, | |
| 1432 environ=env) | |
| 1433 self.server.client.request.headers.get=self.get_header | |
| 1434 results = self.server.dispatch(env["REQUEST_METHOD"], | |
| 1435 "/rest/data/issue", | |
| 1436 form) | |
| 1437 | |
| 1438 print(results) | |
| 1439 self.assertEqual(self.server.client.response_code, 201) | |
| 1440 json_dict = json.loads(b2s(results)) | |
| 1441 # ERROR this should be 1. What's happening is that the code | |
| 1442 # for handling 406 error code runs through everything and creates | |
| 1443 # the item. Then it throws a 406 after the work is done when it | |
| 1444 # realizes it can't respond as requested. So the 406 post above | |
| 1445 # creates issue 1 and this one creates issue 2. | |
| 1446 self.assertEqual(json_dict['data']['id'], "2") | |
| 1447 | |
| 1448 | |
| 1449 # test 3 accept is empty. This triggers */* so passes | |
| 1450 headers={"accept": "", | |
| 1451 "content-type": env['CONTENT_TYPE'], | |
| 1452 "content-length": env['CONTENT_LENGTH'], | |
| 1453 } | |
| 1454 | |
| 1455 self.headers=headers | |
| 1456 # we need to generate a FieldStorage the looks like | |
| 1457 # FieldStorage(None, None, 'string') rather than | |
| 1458 # FieldStorage(None, None, []) | |
| 1459 body_file=BytesIO(body) # FieldStorage needs a file | |
| 1460 form = client.BinaryFieldStorage(body_file, | |
| 1461 headers=headers, | |
| 1462 environ=env) | |
| 1463 self.server.client.request.headers.get=self.get_header | |
| 1464 results = self.server.dispatch(env["REQUEST_METHOD"], | |
| 1465 "/rest/data/issue", | |
| 1466 form) | |
| 1467 | |
| 1468 print(results) | |
| 1469 self.assertEqual(self.server.client.response_code, 201) | |
| 1470 json_dict = json.loads(b2s(results)) | |
| 1471 # This is one more than above. Will need to be fixed | |
| 1472 # When error above is fixed. | |
| 1473 self.assertEqual(json_dict['data']['id'], "3") | |
| 1474 | |
| 1475 # test 4 accept is random junk. | |
| 1476 headers={"accept": "Xyzzy I am not a mime, type;", | |
| 1477 "content-type": env['CONTENT_TYPE'], | |
| 1478 "content-length": env['CONTENT_LENGTH'], | |
| 1479 } | |
| 1480 | |
| 1481 self.headers=headers | |
| 1482 # we need to generate a FieldStorage the looks like | |
| 1483 # FieldStorage(None, None, 'string') rather than | |
| 1484 # FieldStorage(None, None, []) | |
| 1485 body_file=BytesIO(body) # FieldStorage needs a file | |
| 1486 form = client.BinaryFieldStorage(body_file, | |
| 1487 headers=headers, | |
| 1488 environ=env) | |
| 1489 self.server.client.request.headers.get=self.get_header | |
| 1490 results = self.server.dispatch(env["REQUEST_METHOD"], | |
| 1491 "/rest/data/issue", | |
| 1492 form) | |
| 1493 | |
| 1494 print(results) | |
| 1495 self.assertEqual(self.server.client.response_code, 406) | |
| 1496 json_dict = json.loads(b2s(results)) | |
| 1497 self.assertIn('Unable to parse Accept Header. Invalid media type: Xyzzy I am not a mime. Acceptable types: */* application/json', json_dict['error']['msg']) | |
| 1498 | |
| 1499 # test 5 accept mimetype is ok, param is not | |
| 1500 headers={"accept": "*/*; foo", | |
| 1501 "content-type": env['CONTENT_TYPE'], | |
| 1502 "content-length": env['CONTENT_LENGTH'], | |
| 1503 } | |
| 1504 | |
| 1505 self.headers=headers | |
| 1506 # we need to generate a FieldStorage the looks like | |
| 1507 # FieldStorage(None, None, 'string') rather than | |
| 1508 # FieldStorage(None, None, []) | |
| 1509 body_file=BytesIO(body) # FieldStorage needs a file | |
| 1510 form = client.BinaryFieldStorage(body_file, | |
| 1511 headers=headers, | |
| 1512 environ=env) | |
| 1513 self.server.client.request.headers.get=self.get_header | |
| 1514 results = self.server.dispatch(env["REQUEST_METHOD"], | |
| 1515 "/rest/data/issue", | |
| 1516 form) | |
| 1517 | |
| 1518 print(results) | |
| 1519 self.assertEqual(self.server.client.response_code, 406) | |
| 1520 json_dict = json.loads(b2s(results)) | |
| 1521 self.assertIn('Unable to parse Accept Header. Invalid param: foo. Acceptable types: */* application/json', json_dict['error']['msg']) | |
| 1324 | 1522 |
| 1325 def testStatsGen(self): | 1523 def testStatsGen(self): |
| 1326 # check stats being returned by put and get ops | 1524 # check stats being returned by put and get ops |
| 1327 # using dispatch which parses the @stats query param | 1525 # using dispatch which parses the @stats query param |
| 1328 | 1526 |
