Mercurial > p > roundup > code
comparison test/rest_common.py @ 5653:ba67e397f063
Fix string/bytes issues under python 3.
1) cgi/client.py: override cgi.FieldStorage's make_file so that file
is always created in binary/byte mode. This means that json (and
xml) are bytes not strings.
2) rest.py: try harder to find dicttoxml in roundup directory or on
sys.path. This just worked under python 2 but python 3 only
searches sys.path by default and does not search relative like
python 2.
3) rest.py: replace headers.getheader call removed from python 3 with
equivalent code.
4) rest.py: make value returned from dispatch into bytes not string.
5) test/caseinsensitivedict.py, test/test_CaseInsensitiveDict.py:
get code from stackoverflow that implements a case insensitive key
dict. So dict['foo'], dict['Foo'] are the same entry. Used for
looking up headers in mocked http rewuset header array.
6) test/rest_common.py: rework tests for etags and rest to properly
supply bytes to the called routines. Calls to s2b and b2s and use
of BytesIO and overriding make_file in cgi.FieldStorage to try to
make sure it works under python 3.
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Sun, 17 Mar 2019 19:28:26 -0400 |
| parents | 095db27e8064 |
| children | 207e0f5d551c |
comparison
equal
deleted
inserted
replaced
| 5652:9689d1bf9bb0 | 5653:ba67e397f063 |
|---|---|
| 6 from roundup.cgi.exceptions import * | 6 from roundup.cgi.exceptions import * |
| 7 from roundup import password, hyperdb | 7 from roundup import password, hyperdb |
| 8 from roundup.rest import RestfulInstance, calculate_etag | 8 from roundup.rest import RestfulInstance, calculate_etag |
| 9 from roundup.backends import list_backends | 9 from roundup.backends import list_backends |
| 10 from roundup.cgi import client | 10 from roundup.cgi import client |
| 11 from roundup.anypy.strings import s2b, b2s | |
| 11 import random | 12 import random |
| 12 | 13 |
| 13 from .db_test_base import setupTracker | 14 from .db_test_base import setupTracker |
| 14 | 15 |
| 15 from .mocknull import MockNull | 16 from .mocknull import MockNull |
| 16 | 17 |
| 17 from io import StringIO | 18 from io import BytesIO |
| 18 import json | 19 import json |
| 20 | |
| 21 from .caseinsensitivedict import CaseInsensitiveDict | |
| 19 | 22 |
| 20 NEEDS_INSTANCE = 1 | 23 NEEDS_INSTANCE = 1 |
| 21 | 24 |
| 22 | 25 |
| 23 class TestCase(): | 26 class TestCase(): |
| 66 'PATH_INFO': 'http://localhost/rounduptest/rest/', | 69 'PATH_INFO': 'http://localhost/rounduptest/rest/', |
| 67 'HTTP_HOST': 'localhost', | 70 'HTTP_HOST': 'localhost', |
| 68 'TRACKER_NAME': 'rounduptest' | 71 'TRACKER_NAME': 'rounduptest' |
| 69 } | 72 } |
| 70 self.dummy_client = client.Client(self.instance, MockNull(), env, [], None) | 73 self.dummy_client = client.Client(self.instance, MockNull(), env, [], None) |
| 71 self.dummy_client.request.headers.getheader = self.get_header | 74 self.dummy_client.request.headers = CaseInsensitiveDict() |
| 72 self.empty_form = cgi.FieldStorage() | 75 self.empty_form = cgi.FieldStorage() |
| 73 | 76 |
| 74 self.server = RestfulInstance(self.dummy_client, self.db) | 77 self.server = RestfulInstance(self.dummy_client, self.db) |
| 75 | 78 |
| 76 def tearDown(self): | 79 def tearDown(self): |
| 78 try: | 81 try: |
| 79 shutil.rmtree(self.dirname) | 82 shutil.rmtree(self.dirname) |
| 80 except OSError as error: | 83 except OSError as error: |
| 81 if error.errno not in (errno.ENOENT, errno.ESRCH): | 84 if error.errno not in (errno.ENOENT, errno.ESRCH): |
| 82 raise | 85 raise |
| 83 | |
| 84 def get_header (self, header, not_found=None): | |
| 85 try: | |
| 86 return self.headers[header.lower()] | |
| 87 except (AttributeError, KeyError): | |
| 88 return not_found | |
| 89 | 86 |
| 90 def testGet(self): | 87 def testGet(self): |
| 91 """ | 88 """ |
| 92 Retrieve all three users | 89 Retrieve all three users |
| 93 obtain data for 'joe' | 90 obtain data for 'joe' |
| 349 Both will be checked if availble. If either one | 346 Both will be checked if availble. If either one |
| 350 fails, the etag check will fail. | 347 fails, the etag check will fail. |
| 351 | 348 |
| 352 Run over header only, etag in form only, both, | 349 Run over header only, etag in form only, both, |
| 353 each one broke and no etag. Use the put command | 350 each one broke and no etag. Use the put command |
| 354 to triger the etag checking code. | 351 to trigger the etag checking code. |
| 355 ''' | 352 ''' |
| 356 for mode in ('header', 'etag', 'both', | 353 for mode in ('header', 'etag', 'both', |
| 357 'brokenheader', 'brokenetag', 'none'): | 354 'brokenheader', 'brokenetag', 'none'): |
| 358 try: | 355 try: |
| 359 # clean up any old header | 356 # use lower case for key to delete. Probably |
| 360 del(self.headers) | 357 # a bug. |
| 361 except AttributeError: | 358 del(self.dummy_client.request.headers['etag']) |
| 359 except (AttributeError,KeyError): | |
| 362 pass | 360 pass |
| 363 | 361 |
| 364 form = cgi.FieldStorage() | 362 form = cgi.FieldStorage() |
| 365 etag = calculate_etag(self.db.user.getnode(self.joeid)) | 363 etag = calculate_etag(self.db.user.getnode(self.joeid)) |
| 366 form.list = [ | 364 form.list = [ |
| 367 cgi.MiniFieldStorage('data', 'Joe Doe Doe'), | 365 cgi.MiniFieldStorage('data', 'Joe Doe Doe'), |
| 368 ] | 366 ] |
| 369 | 367 |
| 370 if mode == 'header': | 368 if mode == 'header': |
| 371 print("Mode = %s"%mode) | 369 print("Mode = %s"%mode) |
| 372 self.headers = {'etag': etag} | 370 self.dummy_client.request.headers['Etag'] = etag |
| 373 elif mode == 'etag': | 371 elif mode == 'etag': |
| 374 print("Mode = %s"%mode) | 372 print("Mode = %s"%mode) |
| 375 form.list.append(cgi.MiniFieldStorage('@etag', etag)) | 373 form.list.append(cgi.MiniFieldStorage('@etag', etag)) |
| 376 elif mode == 'both': | 374 elif mode == 'both': |
| 377 print("Mode = %s"%mode) | 375 print("Mode = %s"%mode) |
| 378 self.headers = {'etag': etag} | 376 self.dummy_client.request.headers['Etag'] = etag |
| 379 form.list.append(cgi.MiniFieldStorage('@etag', etag)) | 377 form.list.append(cgi.MiniFieldStorage('@etag', etag)) |
| 380 elif mode == 'brokenheader': | 378 elif mode == 'brokenheader': |
| 381 print("Mode = %s"%mode) | 379 print("Mode = %s"%mode) |
| 382 self.headers = {'etag': 'bad'} | 380 self.dummy_client.request.headers['Etag'] = 'bad' |
| 383 form.list.append(cgi.MiniFieldStorage('@etag', etag)) | 381 form.list.append(cgi.MiniFieldStorage('@etag', etag)) |
| 384 elif mode == 'brokenetag': | 382 elif mode == 'brokenetag': |
| 385 print("Mode = %s"%mode) | 383 print("Mode = %s"%mode) |
| 386 self.headers = {'etag': etag} | 384 self.dummy_client.request.headers['Etag'] = etag |
| 387 form.list.append(cgi.MiniFieldStorage('@etag', 'bad')) | 385 form.list.append(cgi.MiniFieldStorage('@etag', 'bad')) |
| 388 elif mode == 'none': | 386 elif mode == 'none': |
| 389 print( "Mode = %s"%mode) | 387 print( "Mode = %s"%mode) |
| 390 else: | 388 else: |
| 391 self.fail("unknown mode found") | 389 self.fail("unknown mode found") |
| 396 if mode not in ('brokenheader', 'brokenetag', 'none'): | 394 if mode not in ('brokenheader', 'brokenetag', 'none'): |
| 397 self.assertEqual(self.dummy_client.response_code, 200) | 395 self.assertEqual(self.dummy_client.response_code, 200) |
| 398 else: | 396 else: |
| 399 self.assertEqual(self.dummy_client.response_code, 412) | 397 self.assertEqual(self.dummy_client.response_code, 412) |
| 400 | 398 |
| 399 def make_file(self): | |
| 400 import tempfile | |
| 401 return tempfile.TemporaryFile("wb+") | |
| 402 | |
| 401 def testDispatch(self): | 403 def testDispatch(self): |
| 402 """ | 404 """ |
| 403 run changes through rest dispatch(). This also tests | 405 run changes through rest dispatch(). This also tests |
| 404 sending json payload through code as dispatch is the | 406 sending json payload through code as dispatch is the |
| 405 code that changes json payload into something we can | 407 code that changes json payload into something we can |
| 406 process. | 408 process. |
| 407 """ | 409 """ |
| 408 # Set joe's 'realname' using json data. | 410 |
| 411 # Override the make_file so it is always set to binary | |
| 412 # read mode. This is needed so we can send a json | |
| 413 # body. | |
| 414 saved_make_file = cgi.FieldStorage.make_file | |
| 415 cgi.FieldStorage.make_file = self.make_file | |
| 416 | |
| 417 | |
| 418 # TEST #1 | |
| 419 # PUT: joe's 'realname' using json data. | |
| 409 # simulate: /rest/data/user/<id>/realname | 420 # simulate: /rest/data/user/<id>/realname |
| 410 # use etag in header | 421 # use etag in header |
| 411 etag = calculate_etag(self.db.user.getnode(self.joeid)) | 422 etag = calculate_etag(self.db.user.getnode(self.joeid)) |
| 412 body=u'{ "data": "Joe Doe 1" }' | 423 body='{ "data": "Joe Doe 1" }' |
| 413 env = { "CONTENT_TYPE": "application/json", | 424 env = { "CONTENT_TYPE": "application/json", |
| 414 "CONTENT_LENGTH": len(body), | 425 "CONTENT_LENGTH": len(body), |
| 415 "REQUEST_METHOD": "PUT" | 426 "REQUEST_METHOD": "PUT" |
| 416 } | 427 } |
| 417 headers={"accept": "application/json", | 428 headers={"accept": "application/json", |
| 418 "content-type": env['CONTENT_TYPE'], | 429 "content-type": env['CONTENT_TYPE'], |
| 419 "etag": etag | 430 "etag": etag |
| 420 } | 431 } |
| 421 self.headers=headers | |
| 422 # we need to generate a FieldStorage the looks like | 432 # we need to generate a FieldStorage the looks like |
| 423 # FieldStorage(None, None, 'string') rather than | 433 # FieldStorage(None, None, 'string') rather than |
| 424 # FieldStorage(None, None, []) | 434 # FieldStorage(None, None, []) |
| 425 body_file=StringIO(body) # FieldStorage needs a file | 435 body_file=BytesIO(s2b(body)) # FieldStorage needs a file |
| 436 cgi.FieldStorage.make_file = self.make_file | |
| 426 form = cgi.FieldStorage(body_file, | 437 form = cgi.FieldStorage(body_file, |
| 427 headers=headers, | 438 headers=headers, |
| 428 environ=env) | 439 environ=env) |
| 429 self.server.client.request.headers.getheader=self.get_header | 440 self.server.client.request.headers.update(headers) # set headers |
| 430 results = self.server.dispatch('PUT', | 441 results = self.server.dispatch('PUT', |
| 431 "/rest/data/user/%s/realname"%self.joeid, | 442 "/rest/data/user/%s/realname"%self.joeid, |
| 432 form) | 443 form) |
| 433 | |
| 434 self.assertEqual(self.server.client.response_code, 200) | 444 self.assertEqual(self.server.client.response_code, 200) |
| 435 results = self.server.get_element('user', self.joeid, self.empty_form) | 445 results = self.server.get_element('user', self.joeid, self.empty_form) |
| 436 self.assertEqual(self.dummy_client.response_code, 200) | 446 self.assertEqual(self.dummy_client.response_code, 200) |
| 437 self.assertEqual(results['data']['attributes']['realname'], | 447 self.assertEqual(results['data']['attributes']['realname'], |
| 438 'Joe Doe 1') | 448 'Joe Doe 1') |
| 439 del(self.headers) | 449 self.server.client.request.headers.clear() # set headers |
| 440 | 450 |
| 451 # TEST #2 | |
| 441 # Set joe's 'realname' using json data. | 452 # Set joe's 'realname' using json data. |
| 442 # simulate: /rest/data/user/<id>/realname | 453 # simulate: /rest/data/user/<id>/realname |
| 443 # use etag in payload | 454 # use etag in payload |
| 444 etag = calculate_etag(self.db.user.getnode(self.joeid)) | 455 etag = calculate_etag(self.db.user.getnode(self.joeid)) |
| 445 body=u'{ "@etag": "%s", "data": "Joe Doe 2" }'%etag | 456 body='{ "@etag": "%s", "data": "Joe Doe 2" }'%etag |
| 446 env = { "CONTENT_TYPE": "application/json", | 457 env = { "CONTENT_TYPE": "application/json", |
| 447 "CONTENT_LENGTH": len(body), | 458 "CONTENT_LENGTH": len(body), |
| 448 "REQUEST_METHOD": "PUT" | 459 "REQUEST_METHOD": "PUT" |
| 449 } | 460 } |
| 450 headers={"accept": "application/json", | 461 headers={"accept": "application/json", |
| 451 "content-type": env['CONTENT_TYPE'] | 462 "content-type": env['CONTENT_TYPE'] |
| 452 } | 463 } |
| 453 self.headers=headers | 464 body_file=BytesIO(s2b(body)) # FieldStorage needs a file |
| 454 body_file=StringIO(body) # FieldStorage needs a file | |
| 455 form = cgi.FieldStorage(body_file, | 465 form = cgi.FieldStorage(body_file, |
| 456 headers=headers, | 466 headers=headers, |
| 457 environ=env) | 467 environ=env) |
| 458 self.server.client.request.headers.getheader=self.get_header | 468 self.server.client.request.headers.update(headers) # set headers |
| 459 results = self.server.dispatch('PUT', | 469 results = self.server.dispatch('PUT', |
| 460 "/rest/data/user/%s/realname"%self.joeid, | 470 "/rest/data/user/%s/realname"%self.joeid, |
| 461 form) | 471 form) |
| 462 | 472 |
| 463 self.assertEqual(self.server.client.response_code, 200) | 473 self.assertEqual(self.server.client.response_code, 200) |
| 464 results = self.server.get_element('user', self.joeid, self.empty_form) | 474 results = self.server.get_element('user', self.joeid, self.empty_form) |
| 465 self.assertEqual(self.dummy_client.response_code, 200) | 475 self.assertEqual(self.dummy_client.response_code, 200) |
| 466 self.assertEqual(results['data']['attributes']['realname'], | 476 self.assertEqual(results['data']['attributes']['realname'], |
| 467 'Joe Doe 2') | 477 'Joe Doe 2') |
| 468 del(self.headers) | 478 self.server.client.request.headers.clear() |
| 469 | 479 |
| 480 # TEST #3 | |
| 470 # change Joe's realname via a normal web form | 481 # change Joe's realname via a normal web form |
| 471 # This generates a FieldStorage that looks like: | |
| 472 # FieldStorage(None, None, []) | |
| 473 # use etag from header | 482 # use etag from header |
| 474 # | 483 # |
| 475 # also use a GET on the uri via the dispatch to get | 484 # Also do a GET using dispatch to get the results from the db. |
| 476 # the results from the db. | |
| 477 etag = calculate_etag(self.db.user.getnode(self.joeid)) | 485 etag = calculate_etag(self.db.user.getnode(self.joeid)) |
| 478 headers={"etag": etag, | 486 headers={"etag": etag, |
| 479 "accept": "application/json", | 487 "accept": "application/json", |
| 480 } | 488 } |
| 481 form = cgi.FieldStorage() | 489 form = cgi.FieldStorage() |
| 482 form.list = [ | 490 form.list = [ |
| 483 cgi.MiniFieldStorage('data', 'Joe Doe'), | 491 cgi.MiniFieldStorage('data', 'Joe Doe'), |
| 484 ] | 492 ] |
| 485 self.headers = headers | 493 self.server.client.request.headers.update(headers) # set headers |
| 486 self.server.client.request.headers.getheader = self.get_header | |
| 487 results = self.server.dispatch('PUT', | 494 results = self.server.dispatch('PUT', |
| 488 "/rest/data/user/%s/realname"%self.joeid, | 495 "/rest/data/user/%s/realname"%self.joeid, |
| 489 form) | 496 form) |
| 490 self.assertEqual(self.dummy_client.response_code, 200) | 497 self.assertEqual(self.dummy_client.response_code, 200) |
| 491 results = self.server.dispatch('GET', | 498 results = self.server.dispatch('GET', |
| 492 "/rest/data/user/%s/realname"%self.joeid, | 499 "/rest/data/user/%s/realname"%self.joeid, |
| 493 self.empty_form) | 500 self.empty_form) |
| 494 self.assertEqual(self.dummy_client.response_code, 200) | 501 self.assertEqual(self.dummy_client.response_code, 200) |
| 495 json_dict = json.loads(results) | 502 |
| 496 | 503 json_dict = json.loads(b2s(results)) |
| 497 self.assertEqual(json_dict['data']['data'], 'Joe Doe') | 504 self.assertEqual(json_dict['data']['data'], 'Joe Doe') |
| 498 self.assertEqual(json_dict['data']['link'], | 505 self.assertEqual(json_dict['data']['link'], |
| 499 "http://tracker.example/cgi-bin/" | 506 "http://tracker.example/cgi-bin/" |
| 500 "roundup.cgi/bugs/rest/data/user/3/realname") | 507 "roundup.cgi/bugs/rest/data/user/3/realname") |
| 501 self.assertEqual(json_dict['data']['type'], "<type 'str'>") | 508 self.assertIn(json_dict['data']['type'], ("<class 'str'>", |
| 509 "<type 'str'>")) | |
| 502 self.assertEqual(json_dict['data']["id"], "3") | 510 self.assertEqual(json_dict['data']["id"], "3") |
| 503 del(self.headers) | 511 self.server.client.request.headers.clear() |
| 504 | 512 |
| 505 | 513 |
| 506 # PATCH joe's email address with json | 514 # TEST #4 |
| 515 # PATCH: joe's email address with json | |
| 507 # save address so we can use it later | 516 # save address so we can use it later |
| 508 stored_results = self.server.get_element('user', self.joeid, | 517 stored_results = self.server.get_element('user', self.joeid, |
| 509 self.empty_form) | 518 self.empty_form) |
| 510 self.assertEqual(self.dummy_client.response_code, 200) | 519 self.assertEqual(self.dummy_client.response_code, 200) |
| 511 | 520 |
| 512 etag = calculate_etag(self.db.user.getnode(self.joeid)) | 521 etag = calculate_etag(self.db.user.getnode(self.joeid)) |
| 513 body=u'{ "address": "demo2@example.com", "@etag": "%s"}'%etag | 522 body='{ "address": "demo2@example.com", "@etag": "%s"}'%etag |
| 514 env = { "CONTENT_TYPE": "application/json", | 523 env = { "CONTENT_TYPE": "application/json", |
| 515 "CONTENT_LENGTH": len(body), | 524 "CONTENT_LENGTH": len(body), |
| 516 "REQUEST_METHOD": "PATCH" | 525 "REQUEST_METHOD": "PATCH" |
| 517 } | 526 } |
| 518 headers={"accept": "application/json", | 527 headers={"accept": "application/json", |
| 519 "content-type": env['CONTENT_TYPE'] | 528 "content-type": env['CONTENT_TYPE'] |
| 520 } | 529 } |
| 521 self.headers=headers | 530 body_file=BytesIO(s2b(body)) # FieldStorage needs a file |
| 522 body_file=StringIO(body) # FieldStorage needs a file | |
| 523 form = cgi.FieldStorage(body_file, | 531 form = cgi.FieldStorage(body_file, |
| 524 headers=headers, | 532 headers=headers, |
| 525 environ=env) | 533 environ=env) |
| 526 self.server.client.request.headers.getheader=self.get_header | 534 self.server.client.request.headers.update(headers) # set headers |
| 527 results = self.server.dispatch('PATCH', | 535 results = self.server.dispatch('PATCH', |
| 528 "/rest/data/user/%s"%self.joeid, | 536 "/rest/data/user/%s"%self.joeid, |
| 529 form) | 537 form) |
| 530 | 538 |
| 531 self.assertEqual(self.server.client.response_code, 200) | 539 self.assertEqual(self.server.client.response_code, 200) |
| 532 results = self.server.get_element('user', self.joeid, self.empty_form) | 540 results = self.server.get_element('user', self.joeid, self.empty_form) |
| 533 self.assertEqual(self.dummy_client.response_code, 200) | 541 self.assertEqual(self.dummy_client.response_code, 200) |
| 534 self.assertEqual(results['data']['attributes']['address'], | 542 self.assertEqual(results['data']['attributes']['address'], |
| 535 'demo2@example.com') | 543 'demo2@example.com') |
| 536 | 544 # and set it back reusing env and headers from last test |
| 537 # and set it back | |
| 538 etag = calculate_etag(self.db.user.getnode(self.joeid)) | 545 etag = calculate_etag(self.db.user.getnode(self.joeid)) |
| 539 body=u'{ "address": "%s", "@etag": "%s"}'%( | 546 body='{ "address": "%s", "@etag": "%s"}'%( |
| 540 stored_results['data']['attributes']['address'], | 547 stored_results['data']['attributes']['address'], |
| 541 etag) | 548 etag) |
| 542 # reuse env and headers from prior test. | 549 # reuse env and headers from prior test. |
| 543 body_file=StringIO(body) # FieldStorage needs a file | 550 body_file=BytesIO(s2b(body)) # FieldStorage needs a file |
| 544 form = cgi.FieldStorage(body_file, | 551 form = cgi.FieldStorage(body_file, |
| 545 headers=headers, | 552 headers=headers, |
| 546 environ=env) | 553 environ=env) |
| 547 self.server.client.request.headers.getheader=self.get_header | |
| 548 results = self.server.dispatch('PATCH', | 554 results = self.server.dispatch('PATCH', |
| 549 "/rest/data/user/%s"%self.joeid, | 555 "/rest/data/user/%s"%self.joeid, |
| 550 form) | 556 form) |
| 551 | 557 |
| 552 self.assertEqual(self.server.client.response_code, 200) | 558 self.assertEqual(self.server.client.response_code, 200) |
| 553 results = self.server.get_element('user', self.joeid, self.empty_form) | 559 results = self.server.get_element('user', self.joeid, self.empty_form) |
| 554 self.assertEqual(self.dummy_client.response_code, 200) | 560 self.assertEqual(self.dummy_client.response_code, 200) |
| 555 self.assertEqual(results['data']['attributes']['address'], | 561 self.assertEqual(results['data']['attributes']['address'], |
| 556 'random@home.org') | 562 'random@home.org') |
| 557 del(self.headers) | 563 self.server.client.request.headers.clear() |
| 558 | 564 |
| 559 # POST to create new issue | 565 |
| 560 body=u'{ "title": "foo bar", "priority": "critical" }' | 566 # TEST #5 |
| 561 | 567 # POST: create new issue |
| 568 # no etag needed | |
| 569 # FIXME at some point we probably want to implement | |
| 570 # Post Once Only, so we need to add a Post Once Exactly | |
| 571 # test and a resubmit as well. | |
| 572 etag = "not needed" | |
| 573 body='{ "title": "foo bar", "priority": "critical" }' | |
| 562 env = { "CONTENT_TYPE": "application/json", | 574 env = { "CONTENT_TYPE": "application/json", |
| 563 "CONTENT_LENGTH": len(body), | 575 "CONTENT_LENGTH": len(body), |
| 564 "REQUEST_METHOD": "POST" | 576 "REQUEST_METHOD": "POST" |
| 565 } | 577 } |
| 566 headers={"accept": "application/json", | 578 headers={"accept": "application/json", |
| 567 "content-type": env['CONTENT_TYPE'] | 579 "content-type": env['CONTENT_TYPE'] |
| 568 } | 580 } |
| 569 self.headers=headers | 581 body_file=BytesIO(s2b(body)) # FieldStorage needs a file |
| 570 body_file=StringIO(body) # FieldStorage needs a file | |
| 571 form = cgi.FieldStorage(body_file, | 582 form = cgi.FieldStorage(body_file, |
| 572 headers=headers, | 583 headers=headers, |
| 573 environ=env) | 584 environ=env) |
| 574 self.server.client.request.headers.getheader=self.get_header | 585 self.server.client.request.headers.update(headers) # set headers |
| 575 results = self.server.dispatch('POST', | 586 results = self.server.dispatch('POST', |
| 576 "/rest/data/issue", | 587 "/rest/data/issue", |
| 577 form) | 588 form) |
| 578 | |
| 579 self.assertEqual(self.server.client.response_code, 201) | 589 self.assertEqual(self.server.client.response_code, 201) |
| 580 json_dict = json.loads(results) | 590 json_dict = json.loads(b2s(results)) |
| 581 issue_id=json_dict['data']['id'] | 591 issue_id=json_dict['data']['id'] |
| 582 results = self.server.get_element('issue', | 592 results = self.server.get_element('issue', |
| 583 str(issue_id), # must be a string not unicode | 593 str(issue_id), # must be a string not unicode |
| 584 self.empty_form) | 594 self.empty_form) |
| 585 self.assertEqual(self.dummy_client.response_code, 200) | 595 self.assertEqual(self.dummy_client.response_code, 200) |
| 586 self.assertEqual(results['data']['attributes']['title'], | 596 self.assertEqual(results['data']['attributes']['title'], |
| 587 'foo bar') | 597 'foo bar') |
| 588 del(self.headers) | 598 self.server.client.request.headers.clear() |
| 599 | |
| 600 # reset the make_file method in the class | |
| 601 cgi.FieldStorage.make_file = saved_make_file | |
| 589 | 602 |
| 590 def testPut(self): | 603 def testPut(self): |
| 591 """ | 604 """ |
| 592 Change joe's 'realname' | 605 Change joe's 'realname' |
| 593 Check if we can't change admin's detail | 606 Check if we can't change admin's detail |
| 613 etag = calculate_etag(self.db.user.getnode(self.joeid)) | 626 etag = calculate_etag(self.db.user.getnode(self.joeid)) |
| 614 form.list = [ | 627 form.list = [ |
| 615 cgi.MiniFieldStorage('data', 'Joe Doe Doe'), | 628 cgi.MiniFieldStorage('data', 'Joe Doe Doe'), |
| 616 ] | 629 ] |
| 617 | 630 |
| 618 self.headers = {'etag': etag } # use etag in header | 631 self.dummy_client.request.headers['ETag'] = etag # use etag in header |
| 619 results = self.server.put_attribute( | 632 results = self.server.put_attribute( |
| 620 'user', self.joeid, 'realname', form | 633 'user', self.joeid, 'realname', form |
| 621 ) | 634 ) |
| 622 self.assertEqual(self.dummy_client.response_code, 200) | 635 self.assertEqual(self.dummy_client.response_code, 200) |
| 623 results = self.server.get_attribute( | 636 results = self.server.get_attribute( |
| 624 'user', self.joeid, 'realname', self.empty_form | 637 'user', self.joeid, 'realname', self.empty_form |
| 625 ) | 638 ) |
| 626 self.assertEqual(self.dummy_client.response_code, 200) | 639 self.assertEqual(self.dummy_client.response_code, 200) |
| 627 self.assertEqual(results['data']['data'], 'Joe Doe Doe') | 640 self.assertEqual(results['data']['data'], 'Joe Doe Doe') |
| 628 del(self.headers) | 641 del(self.dummy_client.request.headers['etag']) |
| 629 | 642 |
| 630 # Reset joe's 'realname'. etag in body | 643 # Reset joe's 'realname'. etag in body |
| 631 form = cgi.FieldStorage() | 644 form = cgi.FieldStorage() |
| 632 etag = calculate_etag(self.db.user.getnode(self.joeid)) | 645 etag = calculate_etag(self.db.user.getnode(self.joeid)) |
| 633 form.list = [ | 646 form.list = [ |
