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