1import unittest 2 3from pyramid.compat import ( 4 bytes_, 5 text_, 6 ) 7 8class Test_exception_response(unittest.TestCase): 9 def _callFUT(self, *arg, **kw): 10 from pyramid.httpexceptions import exception_response 11 return exception_response(*arg, **kw) 12 13 def test_status_400(self): 14 from pyramid.httpexceptions import HTTPBadRequest 15 self.assertTrue(isinstance(self._callFUT(400), HTTPBadRequest)) 16 17 def test_status_404(self): 18 from pyramid.httpexceptions import HTTPNotFound 19 self.assertTrue(isinstance(self._callFUT(404), HTTPNotFound)) 20 21 def test_status_500(self): 22 from pyramid.httpexceptions import HTTPInternalServerError 23 self.assertTrue(isinstance(self._callFUT(500), 24 HTTPInternalServerError)) 25 26 def test_status_201(self): 27 from pyramid.httpexceptions import HTTPCreated 28 self.assertTrue(isinstance(self._callFUT(201), HTTPCreated)) 29 30 def test_extra_kw(self): 31 resp = self._callFUT(404, headers=[('abc', 'def')]) 32 self.assertEqual(resp.headers['abc'], 'def') 33 34class Test_default_exceptionresponse_view(unittest.TestCase): 35 def _callFUT(self, context, request): 36 from pyramid.httpexceptions import default_exceptionresponse_view 37 return default_exceptionresponse_view(context, request) 38 39 def test_call_with_exception(self): 40 context = Exception() 41 result = self._callFUT(context, None) 42 self.assertEqual(result, context) 43 44 def test_call_with_nonexception(self): 45 request = DummyRequest() 46 context = Exception() 47 request.exception = context 48 result = self._callFUT(None, request) 49 self.assertEqual(result, context) 50 51class Test__no_escape(unittest.TestCase): 52 def _callFUT(self, val): 53 from pyramid.httpexceptions import _no_escape 54 return _no_escape(val) 55 56 def test_null(self): 57 self.assertEqual(self._callFUT(None), '') 58 59 def test_not_basestring(self): 60 self.assertEqual(self._callFUT(42), '42') 61 62 def test_unicode(self): 63 class DummyUnicodeObject(object): 64 def __unicode__(self): 65 return text_('42') 66 duo = DummyUnicodeObject() 67 self.assertEqual(self._callFUT(duo), text_('42')) 68 69class TestHTTPException(unittest.TestCase): 70 def _getTargetClass(self): 71 from pyramid.httpexceptions import HTTPException 72 return HTTPException 73 74 def _getTargetSubclass(self, code='200', title='OK', 75 explanation='explanation', empty_body=False): 76 cls = self._getTargetClass() 77 class Subclass(cls): 78 pass 79 Subclass.empty_body = empty_body 80 Subclass.code = code 81 Subclass.title = title 82 Subclass.explanation = explanation 83 return Subclass 84 85 def _makeOne(self, *arg, **kw): 86 cls = self._getTargetClass() 87 return cls(*arg, **kw) 88 89 def test_implements_IResponse(self): 90 from pyramid.interfaces import IResponse 91 cls = self._getTargetClass() 92 self.assertTrue(IResponse.implementedBy(cls)) 93 94 def test_provides_IResponse(self): 95 from pyramid.interfaces import IResponse 96 inst = self._getTargetClass()() 97 self.assertTrue(IResponse.providedBy(inst)) 98 99 def test_implements_IExceptionResponse(self): 100 from pyramid.interfaces import IExceptionResponse 101 cls = self._getTargetClass() 102 self.assertTrue(IExceptionResponse.implementedBy(cls)) 103 104 def test_provides_IExceptionResponse(self): 105 from pyramid.interfaces import IExceptionResponse 106 inst = self._getTargetClass()() 107 self.assertTrue(IExceptionResponse.providedBy(inst)) 108 109 def test_ctor_sets_detail(self): 110 exc = self._makeOne('message') 111 self.assertEqual(exc.detail, 'message') 112 113 def test_ctor_sets_comment(self): 114 exc = self._makeOne(comment='comment') 115 self.assertEqual(exc.comment, 'comment') 116 117 def test_ctor_calls_Exception_ctor(self): 118 exc = self._makeOne('message') 119 self.assertEqual(exc.message, 'message') 120 121 def test_ctor_calls_Response_ctor(self): 122 exc = self._makeOne('message') 123 self.assertEqual(exc.status, '520 Unknown Error') 124 125 def test_ctor_extends_headers(self): 126 exc = self._makeOne(headers=[('X-Foo', 'foo')]) 127 self.assertEqual(exc.headers.get('X-Foo'), 'foo') 128 129 def test_ctor_sets_body_template_obj(self): 130 exc = self._makeOne(body_template='${foo}') 131 self.assertEqual( 132 exc.body_template_obj.substitute({'foo': 'foo'}), 'foo') 133 134 def test_ctor_with_empty_body(self): 135 cls = self._getTargetSubclass(empty_body=True) 136 exc = cls() 137 self.assertEqual(exc.content_type, None) 138 self.assertEqual(exc.content_length, None) 139 140 def test_ctor_with_body_doesnt_set_default_app_iter(self): 141 exc = self._makeOne(body=b'123') 142 self.assertEqual(exc.app_iter, [b'123']) 143 144 def test_ctor_with_unicode_body_doesnt_set_default_app_iter(self): 145 exc = self._makeOne(unicode_body=text_('123')) 146 self.assertEqual(exc.app_iter, [b'123']) 147 148 def test_ctor_with_app_iter_doesnt_set_default_app_iter(self): 149 exc = self._makeOne(app_iter=[b'123']) 150 self.assertEqual(exc.app_iter, [b'123']) 151 152 def test_ctor_with_body_sets_default_app_iter_html(self): 153 cls = self._getTargetSubclass() 154 exc = cls('detail') 155 environ = _makeEnviron() 156 environ['HTTP_ACCEPT'] = 'text/html' 157 start_response = DummyStartResponse() 158 body = list(exc(environ, start_response))[0] 159 self.assertTrue(body.startswith(b'<html')) 160 self.assertTrue(b'200 OK' in body) 161 self.assertTrue(b'explanation' in body) 162 self.assertTrue(b'detail' in body) 163 164 def test_ctor_with_body_sets_default_app_iter_text(self): 165 cls = self._getTargetSubclass() 166 exc = cls('detail') 167 environ = _makeEnviron() 168 start_response = DummyStartResponse() 169 body = list(exc(environ, start_response))[0] 170 self.assertEqual(body, b'200 OK\n\nexplanation\n\n\ndetail\n\n') 171 172 def test__str__detail(self): 173 exc = self._makeOne() 174 exc.detail = 'abc' 175 self.assertEqual(str(exc), 'abc') 176 177 def test__str__explanation(self): 178 exc = self._makeOne() 179 exc.explanation = 'def' 180 self.assertEqual(str(exc), 'def') 181 182 def test_wsgi_response(self): 183 exc = self._makeOne() 184 self.assertTrue(exc is exc.wsgi_response) 185 186 def test_exception(self): 187 exc = self._makeOne() 188 self.assertTrue(exc is exc.exception) 189 190 def test__calls_start_response(self): 191 cls = self._getTargetSubclass() 192 exc = cls() 193 environ = _makeEnviron() 194 start_response = DummyStartResponse() 195 exc(environ, start_response) 196 self.assertTrue(start_response.headerlist) 197 self.assertEqual(start_response.status, '200 OK') 198 199 def test_call_returns_same_body_called_twice(self): 200 # optimization 201 cls = self._getTargetSubclass() 202 exc = cls() 203 environ = _makeEnviron() 204 environ['HTTP_ACCEPT'] = '*/*' 205 start_response = DummyStartResponse() 206 app_iter = exc(environ, start_response) 207 self.assertEqual(app_iter[0], exc.body) 208 209 def test__default_app_iter_no_comment_plain(self): 210 cls = self._getTargetSubclass() 211 exc = cls() 212 environ = _makeEnviron() 213 start_response = DummyStartResponse() 214 body = list(exc(environ, start_response))[0] 215 for header in start_response.headerlist: 216 if header[0] == 'Content-Type': 217 self.assertEqual(header[1], 'text/plain; charset=UTF-8') 218 self.assertEqual(body, b'200 OK\n\nexplanation\n\n\n\n\n') 219 220 def test__default_app_iter_with_comment_plain(self): 221 cls = self._getTargetSubclass() 222 exc = cls(comment='comment') 223 environ = _makeEnviron() 224 start_response = DummyStartResponse() 225 body = list(exc(environ, start_response))[0] 226 for header in start_response.headerlist: 227 if header[0] == 'Content-Type': 228 self.assertEqual(header[1], 'text/plain; charset=UTF-8') 229 self.assertEqual(body, b'200 OK\n\nexplanation\n\n\n\ncomment\n') 230 231 def test__default_app_iter_no_comment_html(self): 232 cls = self._getTargetSubclass() 233 exc = cls() 234 environ = _makeEnviron() 235 start_response = DummyStartResponse() 236 body = list(exc(environ, start_response))[0] 237 for header in start_response.headerlist: 238 if header[0] == 'Content-Type': 239 self.assertEqual(header[1], 'text/plain; charset=UTF-8') 240 self.assertFalse(b'<!-- ' in body) 241 242 def test__content_type(self): 243 cls = self._getTargetSubclass() 244 exc = cls() 245 environ = _makeEnviron() 246 start_response = DummyStartResponse() 247 exc(environ, start_response) 248 for header in start_response.headerlist: 249 if header[0] == 'Content-Type': 250 self.assertEqual(header[1], 'text/plain; charset=UTF-8') 251 252 def test__content_type_default_is_html(self): 253 cls = self._getTargetSubclass() 254 exc = cls() 255 environ = _makeEnviron() 256 environ['HTTP_ACCEPT'] = '*/*' 257 start_response = DummyStartResponse() 258 exc(environ, start_response) 259 for header in start_response.headerlist: 260 if header[0] == 'Content-Type': 261 self.assertEqual(header[1], 'text/html; charset=UTF-8') 262 263 def test__content_type_text_html(self): 264 cls = self._getTargetSubclass() 265 exc = cls() 266 environ = _makeEnviron() 267 environ['HTTP_ACCEPT'] = 'text/html' 268 start_response = DummyStartResponse() 269 exc(environ, start_response) 270 for header in start_response.headerlist: 271 if header[0] == 'Content-Type': 272 self.assertEqual(header[1], 'text/html; charset=UTF-8') 273 274 def test__content_type_application_json(self): 275 cls = self._getTargetSubclass() 276 exc = cls() 277 environ = _makeEnviron() 278 environ['HTTP_ACCEPT'] = 'application/json' 279 start_response = DummyStartResponse() 280 exc(environ, start_response) 281 for header in start_response.headerlist: 282 if header[0] == 'Content-Type': 283 self.assertEqual(header[1], 'application/json') 284 285 def test__default_app_iter_with_comment_ampersand(self): 286 cls = self._getTargetSubclass() 287 exc = cls(comment='comment & comment') 288 environ = _makeEnviron() 289 environ['HTTP_ACCEPT'] = 'text/html' 290 start_response = DummyStartResponse() 291 body = list(exc(environ, start_response))[0] 292 for header in start_response.headerlist: 293 if header[0] == 'Content-Type': 294 self.assertEqual(header[1], 'text/html; charset=UTF-8') 295 self.assertTrue(b'<!-- comment & comment -->' in body) 296 297 def test__default_app_iter_with_comment_html(self): 298 cls = self._getTargetSubclass() 299 exc = cls(comment='comment & comment') 300 environ = _makeEnviron() 301 environ['HTTP_ACCEPT'] = 'text/html' 302 start_response = DummyStartResponse() 303 body = list(exc(environ, start_response))[0] 304 self.assertTrue(b'<!-- comment & comment -->' in body) 305 306 def test__default_app_iter_with_comment_json(self): 307 cls = self._getTargetSubclass() 308 exc = cls(comment='comment & comment') 309 environ = _makeEnviron() 310 environ['HTTP_ACCEPT'] = 'application/json' 311 start_response = DummyStartResponse() 312 body = list(exc(environ, start_response))[0] 313 import json 314 retval = json.loads(body.decode('UTF-8')) 315 self.assertEqual(retval['code'], '200 OK') 316 self.assertEqual(retval['title'], 'OK') 317 318 def test__default_app_iter_with_custom_json(self): 319 def json_formatter(status, body, title, environ): 320 return {'message': body, 321 'code': status, 322 'title': title, 323 'custom': environ['CUSTOM_VARIABLE'] 324 } 325 cls = self._getTargetSubclass() 326 exc = cls(comment='comment', json_formatter=json_formatter) 327 environ = _makeEnviron() 328 environ['HTTP_ACCEPT'] = 'application/json' 329 environ['CUSTOM_VARIABLE'] = 'custom!' 330 start_response = DummyStartResponse() 331 body = list(exc(environ, start_response))[0] 332 import json 333 retval = json.loads(body.decode('UTF-8')) 334 self.assertEqual(retval['code'], '200 OK') 335 self.assertEqual(retval['title'], 'OK') 336 self.assertEqual(retval['custom'], 'custom!') 337 338 def test_custom_body_template(self): 339 cls = self._getTargetSubclass() 340 exc = cls(body_template='${REQUEST_METHOD}') 341 environ = _makeEnviron() 342 start_response = DummyStartResponse() 343 body = list(exc(environ, start_response))[0] 344 self.assertEqual(body, b'200 OK\n\nGET') 345 346 def test_custom_body_template_with_custom_variable_doesnt_choke(self): 347 cls = self._getTargetSubclass() 348 exc = cls(body_template='${REQUEST_METHOD}') 349 environ = _makeEnviron() 350 class Choke(object): 351 def __str__(self): # pragma nocover 352 raise ValueError 353 environ['gardentheory.user'] = Choke() 354 start_response = DummyStartResponse() 355 body = list(exc(environ, start_response))[0] 356 self.assertEqual(body, b'200 OK\n\nGET') 357 358 def test_body_template_unicode(self): 359 cls = self._getTargetSubclass() 360 la = text_(b'/La Pe\xc3\xb1a', 'utf-8') 361 environ = _makeEnviron(unicodeval=la) 362 exc = cls(body_template='${unicodeval}') 363 start_response = DummyStartResponse() 364 body = list(exc(environ, start_response))[0] 365 self.assertEqual(body, b'200 OK\n\n/La Pe\xc3\xb1a') 366 367class TestRenderAllExceptionsWithoutArguments(unittest.TestCase): 368 def _doit(self, content_type): 369 from pyramid.httpexceptions import status_map 370 L = [] 371 self.assertTrue(status_map) 372 for v in status_map.values(): 373 environ = _makeEnviron() 374 start_response = DummyStartResponse() 375 exc = v() 376 exc.content_type = content_type 377 result = list(exc(environ, start_response))[0] 378 if exc.empty_body: 379 self.assertEqual(result, b'') 380 else: 381 self.assertTrue(bytes_(exc.status) in result) 382 L.append(result) 383 self.assertEqual(len(L), len(status_map)) 384 385 def test_it_plain(self): 386 self._doit('text/plain') 387 388 def test_it_html(self): 389 self._doit('text/html') 390 391class Test_HTTPMove(unittest.TestCase): 392 def _makeOne(self, *arg, **kw): 393 from pyramid.httpexceptions import _HTTPMove 394 return _HTTPMove(*arg, **kw) 395 396 def test_it_location_none_valueerrors(self): 397 # Constructing a HTTPMove instance with location=None should 398 # throw a ValueError from __init__ so that a more-confusing 399 # exception won't be thrown later from .prepare(environ) 400 self.assertRaises(ValueError, self._makeOne, location=None) 401 402 def test_it_location_not_passed(self): 403 exc = self._makeOne() 404 self.assertEqual(exc.location, '') 405 406 def test_it_location_passed(self): 407 exc = self._makeOne(location='foo') 408 self.assertEqual(exc.location, 'foo') 409 410 def test_it_location_firstarg(self): 411 exc = self._makeOne('foo') 412 self.assertEqual(exc.location, 'foo') 413 414 def test_it_call_with_default_body_tmpl(self): 415 exc = self._makeOne(location='foo') 416 environ = _makeEnviron() 417 start_response = DummyStartResponse() 418 app_iter = exc(environ, start_response) 419 self.assertEqual(app_iter[0], 420 (b'520 Unknown Error\n\nThe resource has been moved to foo; ' 421 b'you should be redirected automatically.\n\n')) 422 423class TestHTTPForbidden(unittest.TestCase): 424 def _makeOne(self, *arg, **kw): 425 from pyramid.httpexceptions import HTTPForbidden 426 return HTTPForbidden(*arg, **kw) 427 428 def test_it_result_not_passed(self): 429 exc = self._makeOne() 430 self.assertEqual(exc.result, None) 431 432 def test_it_result_passed(self): 433 exc = self._makeOne(result='foo') 434 self.assertEqual(exc.result, 'foo') 435 436class TestHTTPMethodNotAllowed(unittest.TestCase): 437 def _makeOne(self, *arg, **kw): 438 from pyramid.httpexceptions import HTTPMethodNotAllowed 439 return HTTPMethodNotAllowed(*arg, **kw) 440 441 def test_it_with_default_body_tmpl(self): 442 exc = self._makeOne() 443 environ = _makeEnviron() 444 start_response = DummyStartResponse() 445 app_iter = exc(environ, start_response) 446 self.assertEqual(app_iter[0], 447 (b'405 Method Not Allowed\n\nThe method GET is not ' 448 b'allowed for this resource. \n\n\n')) 449 450 451class DummyRequest(object): 452 exception = None 453 454class DummyStartResponse(object): 455 def __call__(self, status, headerlist): 456 self.status = status 457 self.headerlist = headerlist 458 459def _makeEnviron(**kw): 460 environ = {'REQUEST_METHOD': 'GET', 461 'wsgi.url_scheme': 'http', 462 'SERVER_NAME': 'localhost', 463 'SERVER_PORT': '80'} 464 environ.update(kw) 465 return environ 466