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 &amp; 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 &amp; 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