Package cherrypy :: Package test :: Module test_core
[hide private]
[frames] | no frames]

Source Code for Module cherrypy.test.test_core

  1  """Basic tests for the CherryPy core: request handling.""" 
  2   
  3  import os 
  4  localDir = os.path.dirname(__file__) 
  5  import sys 
  6  import types 
  7   
  8  import cherrypy 
  9  from cherrypy._cpcompat import IncompleteRead, itervalues, ntob 
 10  from cherrypy import _cptools, tools 
 11  from cherrypy.lib import httputil, static 
 12   
 13   
 14  favicon_path = os.path.join(os.getcwd(), localDir, "../favicon.ico") 
 15   
 16  #                             Client-side code                             # 
 17   
 18  from cherrypy.test import helper 
 19   
 20   
21 -class CoreRequestHandlingTest(helper.CPWebCase):
22
23 - def setup_server():
24 class Root: 25 26 def index(self): 27 return "hello"
28 index.exposed = True 29 30 favicon_ico = tools.staticfile.handler(filename=favicon_path) 31 32 def defct(self, newct): 33 newct = "text/%s" % newct 34 cherrypy.config.update({'tools.response_headers.on': True, 35 'tools.response_headers.headers': 36 [('Content-Type', newct)]})
37 defct.exposed = True 38 39 def baseurl(self, path_info, relative=None): 40 return cherrypy.url(path_info, relative=bool(relative)) 41 baseurl.exposed = True 42 43 root = Root() 44 45 if sys.version_info >= (2, 5): 46 from cherrypy.test._test_decorators import ExposeExamples 47 root.expose_dec = ExposeExamples() 48 49 class TestType(type): 50 51 """Metaclass which automatically exposes all functions in each 52 subclass, and adds an instance of the subclass as an attribute 53 of root. 54 """ 55 def __init__(cls, name, bases, dct): 56 type.__init__(cls, name, bases, dct) 57 for value in itervalues(dct): 58 if isinstance(value, types.FunctionType): 59 value.exposed = True 60 setattr(root, name.lower(), cls()) 61 Test = TestType('Test', (object, ), {}) 62 63 class URL(Test): 64 65 _cp_config = {'tools.trailing_slash.on': False} 66 67 def index(self, path_info, relative=None): 68 if relative != 'server': 69 relative = bool(relative) 70 return cherrypy.url(path_info, relative=relative) 71 72 def leaf(self, path_info, relative=None): 73 if relative != 'server': 74 relative = bool(relative) 75 return cherrypy.url(path_info, relative=relative) 76 77 def log_status(): 78 Status.statuses.append(cherrypy.response.status) 79 cherrypy.tools.log_status = cherrypy.Tool( 80 'on_end_resource', log_status) 81 82 class Status(Test): 83 84 def index(self): 85 return "normal" 86 87 def blank(self): 88 cherrypy.response.status = "" 89 90 # According to RFC 2616, new status codes are OK as long as they 91 # are between 100 and 599. 92 93 # Here is an illegal code... 94 def illegal(self): 95 cherrypy.response.status = 781 96 return "oops" 97 98 # ...and here is an unknown but legal code. 99 def unknown(self): 100 cherrypy.response.status = "431 My custom error" 101 return "funky" 102 103 # Non-numeric code 104 def bad(self): 105 cherrypy.response.status = "error" 106 return "bad news" 107 108 statuses = [] 109 110 def on_end_resource_stage(self): 111 return repr(self.statuses) 112 on_end_resource_stage._cp_config = {'tools.log_status.on': True} 113 114 class Redirect(Test): 115 116 class Error: 117 _cp_config = {"tools.err_redirect.on": True, 118 "tools.err_redirect.url": "/errpage", 119 "tools.err_redirect.internal": False, 120 } 121 122 def index(self): 123 raise NameError("redirect_test") 124 index.exposed = True 125 error = Error() 126 127 def index(self): 128 return "child" 129 130 def custom(self, url, code): 131 raise cherrypy.HTTPRedirect(url, code) 132 133 def by_code(self, code): 134 raise cherrypy.HTTPRedirect("somewhere%20else", code) 135 by_code._cp_config = {'tools.trailing_slash.extra': True} 136 137 def nomodify(self): 138 raise cherrypy.HTTPRedirect("", 304) 139 140 def proxy(self): 141 raise cherrypy.HTTPRedirect("proxy", 305) 142 143 def stringify(self): 144 return str(cherrypy.HTTPRedirect("/")) 145 146 def fragment(self, frag): 147 raise cherrypy.HTTPRedirect("/some/url#%s" % frag) 148 149 def url_with_quote(self): 150 raise cherrypy.HTTPRedirect("/some\"url/that'we/want") 151 152 def login_redir(): 153 if not getattr(cherrypy.request, "login", None): 154 raise cherrypy.InternalRedirect("/internalredirect/login") 155 tools.login_redir = _cptools.Tool('before_handler', login_redir) 156 157 def redir_custom(): 158 raise cherrypy.InternalRedirect("/internalredirect/custom_err") 159 160 class InternalRedirect(Test): 161 162 def index(self): 163 raise cherrypy.InternalRedirect("/") 164 165 def choke(self): 166 return 3 / 0 167 choke.exposed = True 168 choke._cp_config = {'hooks.before_error_response': redir_custom} 169 170 def relative(self, a, b): 171 raise cherrypy.InternalRedirect("cousin?t=6") 172 173 def cousin(self, t): 174 assert cherrypy.request.prev.closed 175 return cherrypy.request.prev.query_string 176 177 def petshop(self, user_id): 178 if user_id == "parrot": 179 # Trade it for a slug when redirecting 180 raise cherrypy.InternalRedirect( 181 '/image/getImagesByUser?user_id=slug') 182 elif user_id == "terrier": 183 # Trade it for a fish when redirecting 184 raise cherrypy.InternalRedirect( 185 '/image/getImagesByUser?user_id=fish') 186 else: 187 # This should pass the user_id through to getImagesByUser 188 raise cherrypy.InternalRedirect( 189 '/image/getImagesByUser?user_id=%s' % str(user_id)) 190 191 # We support Python 2.3, but the @-deco syntax would look like 192 # this: 193 # @tools.login_redir() 194 def secure(self): 195 return "Welcome!" 196 secure = tools.login_redir()(secure) 197 # Since calling the tool returns the same function you pass in, 198 # you could skip binding the return value, and just write: 199 # tools.login_redir()(secure) 200 201 def login(self): 202 return "Please log in" 203 204 def custom_err(self): 205 return "Something went horribly wrong." 206 207 def early_ir(self, arg): 208 return "whatever" 209 early_ir._cp_config = {'hooks.before_request_body': redir_custom} 210 211 class Image(Test): 212 213 def getImagesByUser(self, user_id): 214 return "0 images for %s" % user_id 215 216 class Flatten(Test): 217 218 def as_string(self): 219 return "content" 220 221 def as_list(self): 222 return ["con", "tent"] 223 224 def as_yield(self): 225 yield ntob("content") 226 227 def as_dblyield(self): 228 yield self.as_yield() 229 as_dblyield._cp_config = {'tools.flatten.on': True} 230 231 def as_refyield(self): 232 for chunk in self.as_yield(): 233 yield chunk 234 235 class Ranges(Test): 236 237 def get_ranges(self, bytes): 238 return repr(httputil.get_ranges('bytes=%s' % bytes, 8)) 239 240 def slice_file(self): 241 path = os.path.join(os.getcwd(), os.path.dirname(__file__)) 242 return static.serve_file( 243 os.path.join(path, "static/index.html")) 244 245 class Cookies(Test): 246 247 def single(self, name): 248 cookie = cherrypy.request.cookie[name] 249 # Python2's SimpleCookie.__setitem__ won't take unicode keys. 250 cherrypy.response.cookie[str(name)] = cookie.value 251 252 def multiple(self, names): 253 for name in names: 254 cookie = cherrypy.request.cookie[name] 255 # Python2's SimpleCookie.__setitem__ won't take unicode 256 # keys. 257 cherrypy.response.cookie[str(name)] = cookie.value 258 259 def append_headers(header_list, debug=False): 260 if debug: 261 cherrypy.log( 262 "Extending response headers with %s" % repr(header_list), 263 "TOOLS.APPEND_HEADERS") 264 cherrypy.serving.response.header_list.extend(header_list) 265 cherrypy.tools.append_headers = cherrypy.Tool( 266 'on_end_resource', append_headers) 267 268 class MultiHeader(Test): 269 270 def header_list(self): 271 pass 272 header_list = cherrypy.tools.append_headers(header_list=[ 273 (ntob('WWW-Authenticate'), ntob('Negotiate')), 274 (ntob('WWW-Authenticate'), ntob('Basic realm="foo"')), 275 ])(header_list) 276 277 def commas(self): 278 cherrypy.response.headers[ 279 'WWW-Authenticate'] = 'Negotiate,Basic realm="foo"' 280 281 cherrypy.tree.mount(root) 282 setup_server = staticmethod(setup_server) 283
284 - def testStatus(self):
285 self.getPage("/status/") 286 self.assertBody('normal') 287 self.assertStatus(200) 288 289 self.getPage("/status/blank") 290 self.assertBody('') 291 self.assertStatus(200) 292 293 self.getPage("/status/illegal") 294 self.assertStatus(500) 295 msg = "Illegal response status from server (781 is out of range)." 296 self.assertErrorPage(500, msg) 297 298 if not getattr(cherrypy.server, 'using_apache', False): 299 self.getPage("/status/unknown") 300 self.assertBody('funky') 301 self.assertStatus(431) 302 303 self.getPage("/status/bad") 304 self.assertStatus(500) 305 msg = "Illegal response status from server ('error' is non-numeric)." 306 self.assertErrorPage(500, msg)
307
308 - def test_on_end_resource_status(self):
309 self.getPage('/status/on_end_resource_stage') 310 self.assertBody('[]') 311 self.getPage('/status/on_end_resource_stage') 312 self.assertBody(repr(["200 OK"]))
313
314 - def testSlashes(self):
315 # Test that requests for index methods without a trailing slash 316 # get redirected to the same URI path with a trailing slash. 317 # Make sure GET params are preserved. 318 self.getPage("/redirect?id=3") 319 self.assertStatus(301) 320 self.assertMatchesBody('<a href=([\'"])%s/redirect/[?]id=3\\1>' 321 "%s/redirect/[?]id=3</a>" % (self.base(), self.base())) 322 323 if self.prefix(): 324 # Corner case: the "trailing slash" redirect could be tricky if 325 # we're using a virtual root and the URI is "/vroot" (no slash). 326 self.getPage("") 327 self.assertStatus(301) 328 self.assertMatchesBody("<a href=(['\"])%s/\\1>%s/</a>" % 329 (self.base(), self.base())) 330 331 # Test that requests for NON-index methods WITH a trailing slash 332 # get redirected to the same URI path WITHOUT a trailing slash. 333 # Make sure GET params are preserved. 334 self.getPage("/redirect/by_code/?code=307") 335 self.assertStatus(301) 336 self.assertMatchesBody("<a href=(['\"])%s/redirect/by_code[?]code=307\\1>" 337 "%s/redirect/by_code[?]code=307</a>" 338 % (self.base(), self.base())) 339 340 # If the trailing_slash tool is off, CP should just continue 341 # as if the slashes were correct. But it needs some help 342 # inside cherrypy.url to form correct output. 343 self.getPage('/url?path_info=page1') 344 self.assertBody('%s/url/page1' % self.base()) 345 self.getPage('/url/leaf/?path_info=page1') 346 self.assertBody('%s/url/page1' % self.base())
347
348 - def testRedirect(self):
349 self.getPage("/redirect/") 350 self.assertBody('child') 351 self.assertStatus(200) 352 353 self.getPage("/redirect/by_code?code=300") 354 self.assertMatchesBody( 355 r"<a href=(['\"])(.*)somewhere%20else\1>\2somewhere%20else</a>") 356 self.assertStatus(300) 357 358 self.getPage("/redirect/by_code?code=301") 359 self.assertMatchesBody( 360 r"<a href=(['\"])(.*)somewhere%20else\1>\2somewhere%20else</a>") 361 self.assertStatus(301) 362 363 self.getPage("/redirect/by_code?code=302") 364 self.assertMatchesBody( 365 r"<a href=(['\"])(.*)somewhere%20else\1>\2somewhere%20else</a>") 366 self.assertStatus(302) 367 368 self.getPage("/redirect/by_code?code=303") 369 self.assertMatchesBody( 370 r"<a href=(['\"])(.*)somewhere%20else\1>\2somewhere%20else</a>") 371 self.assertStatus(303) 372 373 self.getPage("/redirect/by_code?code=307") 374 self.assertMatchesBody( 375 r"<a href=(['\"])(.*)somewhere%20else\1>\2somewhere%20else</a>") 376 self.assertStatus(307) 377 378 self.getPage("/redirect/nomodify") 379 self.assertBody('') 380 self.assertStatus(304) 381 382 self.getPage("/redirect/proxy") 383 self.assertBody('') 384 self.assertStatus(305) 385 386 # HTTPRedirect on error 387 self.getPage("/redirect/error/") 388 self.assertStatus(('302 Found', '303 See Other')) 389 self.assertInBody('/errpage') 390 391 # Make sure str(HTTPRedirect()) works. 392 self.getPage("/redirect/stringify", protocol="HTTP/1.0") 393 self.assertStatus(200) 394 self.assertBody("(['%s/'], 302)" % self.base()) 395 if cherrypy.server.protocol_version == "HTTP/1.1": 396 self.getPage("/redirect/stringify", protocol="HTTP/1.1") 397 self.assertStatus(200) 398 self.assertBody("(['%s/'], 303)" % self.base()) 399 400 # check that #fragments are handled properly 401 # http://skrb.org/ietf/http_errata.html#location-fragments 402 frag = "foo" 403 self.getPage("/redirect/fragment/%s" % frag) 404 self.assertMatchesBody( 405 r"<a href=(['\"])(.*)\/some\/url\#%s\1>\2\/some\/url\#%s</a>" % ( 406 frag, frag)) 407 loc = self.assertHeader('Location') 408 assert loc.endswith("#%s" % frag) 409 self.assertStatus(('302 Found', '303 See Other')) 410 411 # check injection protection 412 # See https://bitbucket.org/cherrypy/cherrypy/issue/1003 413 self.getPage( 414 "/redirect/custom?" 415 "code=303&url=/foobar/%0d%0aSet-Cookie:%20somecookie=someval") 416 self.assertStatus(303) 417 loc = self.assertHeader('Location') 418 assert 'Set-Cookie' in loc 419 self.assertNoHeader('Set-Cookie') 420 421 def assertValidXHTML(): 422 from xml.etree import ElementTree 423 try: 424 ElementTree.fromstring('<html><body>%s</body></html>' % self.body) 425 except ElementTree.ParseError as e: 426 self._handlewebError('automatically generated redirect ' 427 'did not generate well-formed html')
428 429 # check redirects to URLs generated valid HTML - we check this 430 # by seeing if it appears as valid XHTML. 431 self.getPage("/redirect/by_code?code=303") 432 self.assertStatus(303) 433 assertValidXHTML() 434 435 # do the same with a url containing quote characters. 436 self.getPage("/redirect/url_with_quote") 437 self.assertStatus(303) 438 assertValidXHTML() 439
440 - def test_InternalRedirect(self):
441 # InternalRedirect 442 self.getPage("/internalredirect/") 443 self.assertBody('hello') 444 self.assertStatus(200) 445 446 # Test passthrough 447 self.getPage( 448 "/internalredirect/petshop?user_id=Sir-not-appearing-in-this-film") 449 self.assertBody('0 images for Sir-not-appearing-in-this-film') 450 self.assertStatus(200) 451 452 # Test args 453 self.getPage("/internalredirect/petshop?user_id=parrot") 454 self.assertBody('0 images for slug') 455 self.assertStatus(200) 456 457 # Test POST 458 self.getPage("/internalredirect/petshop", method="POST", 459 body="user_id=terrier") 460 self.assertBody('0 images for fish') 461 self.assertStatus(200) 462 463 # Test ir before body read 464 self.getPage("/internalredirect/early_ir", method="POST", 465 body="arg=aha!") 466 self.assertBody("Something went horribly wrong.") 467 self.assertStatus(200) 468 469 self.getPage("/internalredirect/secure") 470 self.assertBody('Please log in') 471 self.assertStatus(200) 472 473 # Relative path in InternalRedirect. 474 # Also tests request.prev. 475 self.getPage("/internalredirect/relative?a=3&b=5") 476 self.assertBody("a=3&b=5") 477 self.assertStatus(200) 478 479 # InternalRedirect on error 480 self.getPage("/internalredirect/choke") 481 self.assertStatus(200) 482 self.assertBody("Something went horribly wrong.")
483
484 - def testFlatten(self):
485 for url in ["/flatten/as_string", "/flatten/as_list", 486 "/flatten/as_yield", "/flatten/as_dblyield", 487 "/flatten/as_refyield"]: 488 self.getPage(url) 489 self.assertBody('content')
490
491 - def testRanges(self):
492 self.getPage("/ranges/get_ranges?bytes=3-6") 493 self.assertBody("[(3, 7)]") 494 495 # Test multiple ranges and a suffix-byte-range-spec, for good measure. 496 self.getPage("/ranges/get_ranges?bytes=2-4,-1") 497 self.assertBody("[(2, 5), (7, 8)]") 498 499 # Get a partial file. 500 if cherrypy.server.protocol_version == "HTTP/1.1": 501 self.getPage("/ranges/slice_file", [('Range', 'bytes=2-5')]) 502 self.assertStatus(206) 503 self.assertHeader("Content-Type", "text/html;charset=utf-8") 504 self.assertHeader("Content-Range", "bytes 2-5/14") 505 self.assertBody("llo,") 506 507 # What happens with overlapping ranges (and out of order, too)? 508 self.getPage("/ranges/slice_file", [('Range', 'bytes=4-6,2-5')]) 509 self.assertStatus(206) 510 ct = self.assertHeader("Content-Type") 511 expected_type = "multipart/byteranges; boundary=" 512 self.assert_(ct.startswith(expected_type)) 513 boundary = ct[len(expected_type):] 514 expected_body = ("\r\n--%s\r\n" 515 "Content-type: text/html\r\n" 516 "Content-range: bytes 4-6/14\r\n" 517 "\r\n" 518 "o, \r\n" 519 "--%s\r\n" 520 "Content-type: text/html\r\n" 521 "Content-range: bytes 2-5/14\r\n" 522 "\r\n" 523 "llo,\r\n" 524 "--%s--\r\n" % (boundary, boundary, boundary)) 525 self.assertBody(expected_body) 526 self.assertHeader("Content-Length") 527 528 # Test "416 Requested Range Not Satisfiable" 529 self.getPage("/ranges/slice_file", [('Range', 'bytes=2300-2900')]) 530 self.assertStatus(416) 531 # "When this status code is returned for a byte-range request, 532 # the response SHOULD include a Content-Range entity-header 533 # field specifying the current length of the selected resource" 534 self.assertHeader("Content-Range", "bytes */14") 535 elif cherrypy.server.protocol_version == "HTTP/1.0": 536 # Test Range behavior with HTTP/1.0 request 537 self.getPage("/ranges/slice_file", [('Range', 'bytes=2-5')]) 538 self.assertStatus(200) 539 self.assertBody("Hello, world\r\n")
540
541 - def testFavicon(self):
542 # favicon.ico is served by staticfile. 543 icofilename = os.path.join(localDir, "../favicon.ico") 544 icofile = open(icofilename, "rb") 545 data = icofile.read() 546 icofile.close() 547 548 self.getPage("/favicon.ico") 549 self.assertBody(data)
550
551 - def testCookies(self):
552 if sys.version_info >= (2, 5): 553 header_value = lambda x: x 554 else: 555 header_value = lambda x: x + ';' 556 557 self.getPage("/cookies/single?name=First", 558 [('Cookie', 'First=Dinsdale;')]) 559 self.assertHeader('Set-Cookie', header_value('First=Dinsdale')) 560 561 self.getPage("/cookies/multiple?names=First&names=Last", 562 [('Cookie', 'First=Dinsdale; Last=Piranha;'), 563 ]) 564 self.assertHeader('Set-Cookie', header_value('First=Dinsdale')) 565 self.assertHeader('Set-Cookie', header_value('Last=Piranha')) 566 567 self.getPage("/cookies/single?name=Something-With%2CComma", 568 [('Cookie', 'Something-With,Comma=some-value')]) 569 self.assertStatus(400)
570
571 - def testDefaultContentType(self):
572 self.getPage('/') 573 self.assertHeader('Content-Type', 'text/html;charset=utf-8') 574 self.getPage('/defct/plain') 575 self.getPage('/') 576 self.assertHeader('Content-Type', 'text/plain;charset=utf-8') 577 self.getPage('/defct/html')
578
579 - def test_multiple_headers(self):
580 self.getPage('/multiheader/header_list') 581 self.assertEqual( 582 [(k, v) for k, v in self.headers if k == 'WWW-Authenticate'], 583 [('WWW-Authenticate', 'Negotiate'), 584 ('WWW-Authenticate', 'Basic realm="foo"'), 585 ]) 586 self.getPage('/multiheader/commas') 587 self.assertHeader('WWW-Authenticate', 'Negotiate,Basic realm="foo"')
588
589 - def test_cherrypy_url(self):
590 # Input relative to current 591 self.getPage('/url/leaf?path_info=page1') 592 self.assertBody('%s/url/page1' % self.base()) 593 self.getPage('/url/?path_info=page1') 594 self.assertBody('%s/url/page1' % self.base()) 595 # Other host header 596 host = 'www.mydomain.example' 597 self.getPage('/url/leaf?path_info=page1', 598 headers=[('Host', host)]) 599 self.assertBody('%s://%s/url/page1' % (self.scheme, host)) 600 601 # Input is 'absolute'; that is, relative to script_name 602 self.getPage('/url/leaf?path_info=/page1') 603 self.assertBody('%s/page1' % self.base()) 604 self.getPage('/url/?path_info=/page1') 605 self.assertBody('%s/page1' % self.base()) 606 607 # Single dots 608 self.getPage('/url/leaf?path_info=./page1') 609 self.assertBody('%s/url/page1' % self.base()) 610 self.getPage('/url/leaf?path_info=other/./page1') 611 self.assertBody('%s/url/other/page1' % self.base()) 612 self.getPage('/url/?path_info=/other/./page1') 613 self.assertBody('%s/other/page1' % self.base()) 614 615 # Double dots 616 self.getPage('/url/leaf?path_info=../page1') 617 self.assertBody('%s/page1' % self.base()) 618 self.getPage('/url/leaf?path_info=other/../page1') 619 self.assertBody('%s/url/page1' % self.base()) 620 self.getPage('/url/leaf?path_info=/other/../page1') 621 self.assertBody('%s/page1' % self.base()) 622 623 # Output relative to current path or script_name 624 self.getPage('/url/?path_info=page1&relative=True') 625 self.assertBody('page1') 626 self.getPage('/url/leaf?path_info=/page1&relative=True') 627 self.assertBody('../page1') 628 self.getPage('/url/leaf?path_info=page1&relative=True') 629 self.assertBody('page1') 630 self.getPage('/url/leaf?path_info=leaf/page1&relative=True') 631 self.assertBody('leaf/page1') 632 self.getPage('/url/leaf?path_info=../page1&relative=True') 633 self.assertBody('../page1') 634 self.getPage('/url/?path_info=other/../page1&relative=True') 635 self.assertBody('page1') 636 637 # Output relative to / 638 self.getPage('/baseurl?path_info=ab&relative=True') 639 self.assertBody('ab') 640 # Output relative to / 641 self.getPage('/baseurl?path_info=/ab&relative=True') 642 self.assertBody('ab') 643 644 # absolute-path references ("server-relative") 645 # Input relative to current 646 self.getPage('/url/leaf?path_info=page1&relative=server') 647 self.assertBody('/url/page1') 648 self.getPage('/url/?path_info=page1&relative=server') 649 self.assertBody('/url/page1') 650 # Input is 'absolute'; that is, relative to script_name 651 self.getPage('/url/leaf?path_info=/page1&relative=server') 652 self.assertBody('/page1') 653 self.getPage('/url/?path_info=/page1&relative=server') 654 self.assertBody('/page1')
655
656 - def test_expose_decorator(self):
657 if not sys.version_info >= (2, 5): 658 return self.skip("skipped (Python 2.5+ only) ") 659 660 # Test @expose 661 self.getPage("/expose_dec/no_call") 662 self.assertStatus(200) 663 self.assertBody("Mr E. R. Bradshaw") 664 665 # Test @expose() 666 self.getPage("/expose_dec/call_empty") 667 self.assertStatus(200) 668 self.assertBody("Mrs. B.J. Smegma") 669 670 # Test @expose("alias") 671 self.getPage("/expose_dec/call_alias") 672 self.assertStatus(200) 673 self.assertBody("Mr Nesbitt") 674 # Does the original name work? 675 self.getPage("/expose_dec/nesbitt") 676 self.assertStatus(200) 677 self.assertBody("Mr Nesbitt") 678 679 # Test @expose(["alias1", "alias2"]) 680 self.getPage("/expose_dec/alias1") 681 self.assertStatus(200) 682 self.assertBody("Mr Ken Andrews") 683 self.getPage("/expose_dec/alias2") 684 self.assertStatus(200) 685 self.assertBody("Mr Ken Andrews") 686 # Does the original name work? 687 self.getPage("/expose_dec/andrews") 688 self.assertStatus(200) 689 self.assertBody("Mr Ken Andrews") 690 691 # Test @expose(alias="alias") 692 self.getPage("/expose_dec/alias3") 693 self.assertStatus(200) 694 self.assertBody("Mr. and Mrs. Watson")
695 696
697 -class ErrorTests(helper.CPWebCase):
698
699 - def setup_server():
700 def break_header(): 701 # Add a header after finalize that is invalid 702 cherrypy.serving.response.header_list.append((2, 3))
703 cherrypy.tools.break_header = cherrypy.Tool( 704 'on_end_resource', break_header) 705 706 class Root: 707 708 def index(self): 709 return "hello"
710 index.exposed = True 711 712 def start_response_error(self): 713 return "salud!" 714 start_response_error._cp_config = {'tools.break_header.on': True} 715 root = Root() 716 717 cherrypy.tree.mount(root) 718 setup_server = staticmethod(setup_server) 719
720 - def test_start_response_error(self):
721 self.getPage("/start_response_error") 722 self.assertStatus(500) 723 self.assertInBody( 724 "TypeError: response.header_list key 2 is not a byte string.")
725