test_error.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. HTTP errors.
  5. """
  6. from __future__ import division, absolute_import
  7. import re
  8. import sys
  9. import traceback
  10. from twisted.trial import unittest
  11. from twisted.python.compat import nativeString, _PY3
  12. from twisted.web import error
  13. from twisted.web.template import Tag
  14. class CodeToMessageTests(unittest.TestCase):
  15. """
  16. L{_codeToMessages} inverts L{_responses.RESPONSES}
  17. """
  18. def test_validCode(self):
  19. m = error._codeToMessage(b"302")
  20. self.assertEqual(m, b"Found")
  21. def test_invalidCode(self):
  22. m = error._codeToMessage(b"987")
  23. self.assertEqual(m, None)
  24. def test_nonintegerCode(self):
  25. m = error._codeToMessage(b"InvalidCode")
  26. self.assertEqual(m, None)
  27. class ErrorTests(unittest.TestCase):
  28. """
  29. Tests for how L{Error} attributes are initialized.
  30. """
  31. def test_noMessageValidStatus(self):
  32. """
  33. If no C{message} argument is passed to the L{Error} constructor and the
  34. C{code} argument is a valid HTTP status code, C{code} is mapped to a
  35. descriptive string to which C{message} is assigned.
  36. """
  37. e = error.Error(b"200")
  38. self.assertEqual(e.message, b"OK")
  39. def test_noMessageInvalidStatus(self):
  40. """
  41. If no C{message} argument is passed to the L{Error} constructor and
  42. C{code} isn't a valid HTTP status code, C{message} stays L{None}.
  43. """
  44. e = error.Error(b"InvalidCode")
  45. self.assertEqual(e.message, None)
  46. def test_messageExists(self):
  47. """
  48. If a C{message} argument is passed to the L{Error} constructor, the
  49. C{message} isn't affected by the value of C{status}.
  50. """
  51. e = error.Error(b"200", b"My own message")
  52. self.assertEqual(e.message, b"My own message")
  53. def test_str(self):
  54. """
  55. C{str()} on an L{Error} returns the code and message it was
  56. instantiated with.
  57. """
  58. # Bytestring status
  59. e = error.Error(b"200", b"OK")
  60. self.assertEqual(str(e), "200 OK")
  61. # int status
  62. e = error.Error(200, b"OK")
  63. self.assertEqual(str(e), "200 OK")
  64. class PageRedirectTests(unittest.TestCase):
  65. """
  66. Tests for how L{PageRedirect} attributes are initialized.
  67. """
  68. def test_noMessageValidStatus(self):
  69. """
  70. If no C{message} argument is passed to the L{PageRedirect} constructor
  71. and the C{code} argument is a valid HTTP status code, C{code} is mapped
  72. to a descriptive string to which C{message} is assigned.
  73. """
  74. e = error.PageRedirect(b"200", location=b"/foo")
  75. self.assertEqual(e.message, b"OK to /foo")
  76. def test_noMessageValidStatusNoLocation(self):
  77. """
  78. If no C{message} argument is passed to the L{PageRedirect} constructor
  79. and C{location} is also empty and the C{code} argument is a valid HTTP
  80. status code, C{code} is mapped to a descriptive string to which
  81. C{message} is assigned without trying to include an empty location.
  82. """
  83. e = error.PageRedirect(b"200")
  84. self.assertEqual(e.message, b"OK")
  85. def test_noMessageInvalidStatusLocationExists(self):
  86. """
  87. If no C{message} argument is passed to the L{PageRedirect} constructor
  88. and C{code} isn't a valid HTTP status code, C{message} stays L{None}.
  89. """
  90. e = error.PageRedirect(b"InvalidCode", location=b"/foo")
  91. self.assertEqual(e.message, None)
  92. def test_messageExistsLocationExists(self):
  93. """
  94. If a C{message} argument is passed to the L{PageRedirect} constructor,
  95. the C{message} isn't affected by the value of C{status}.
  96. """
  97. e = error.PageRedirect(b"200", b"My own message", location=b"/foo")
  98. self.assertEqual(e.message, b"My own message to /foo")
  99. def test_messageExistsNoLocation(self):
  100. """
  101. If a C{message} argument is passed to the L{PageRedirect} constructor
  102. and no location is provided, C{message} doesn't try to include the
  103. empty location.
  104. """
  105. e = error.PageRedirect(b"200", b"My own message")
  106. self.assertEqual(e.message, b"My own message")
  107. class InfiniteRedirectionTests(unittest.TestCase):
  108. """
  109. Tests for how L{InfiniteRedirection} attributes are initialized.
  110. """
  111. def test_noMessageValidStatus(self):
  112. """
  113. If no C{message} argument is passed to the L{InfiniteRedirection}
  114. constructor and the C{code} argument is a valid HTTP status code,
  115. C{code} is mapped to a descriptive string to which C{message} is
  116. assigned.
  117. """
  118. e = error.InfiniteRedirection(b"200", location=b"/foo")
  119. self.assertEqual(e.message, b"OK to /foo")
  120. def test_noMessageValidStatusNoLocation(self):
  121. """
  122. If no C{message} argument is passed to the L{InfiniteRedirection}
  123. constructor and C{location} is also empty and the C{code} argument is a
  124. valid HTTP status code, C{code} is mapped to a descriptive string to
  125. which C{message} is assigned without trying to include an empty
  126. location.
  127. """
  128. e = error.InfiniteRedirection(b"200")
  129. self.assertEqual(e.message, b"OK")
  130. def test_noMessageInvalidStatusLocationExists(self):
  131. """
  132. If no C{message} argument is passed to the L{InfiniteRedirection}
  133. constructor and C{code} isn't a valid HTTP status code, C{message} stays
  134. L{None}.
  135. """
  136. e = error.InfiniteRedirection(b"InvalidCode", location=b"/foo")
  137. self.assertEqual(e.message, None)
  138. def test_messageExistsLocationExists(self):
  139. """
  140. If a C{message} argument is passed to the L{InfiniteRedirection}
  141. constructor, the C{message} isn't affected by the value of C{status}.
  142. """
  143. e = error.InfiniteRedirection(b"200", b"My own message",
  144. location=b"/foo")
  145. self.assertEqual(e.message, b"My own message to /foo")
  146. def test_messageExistsNoLocation(self):
  147. """
  148. If a C{message} argument is passed to the L{InfiniteRedirection}
  149. constructor and no location is provided, C{message} doesn't try to
  150. include the empty location.
  151. """
  152. e = error.InfiniteRedirection(b"200", b"My own message")
  153. self.assertEqual(e.message, b"My own message")
  154. class RedirectWithNoLocationTests(unittest.TestCase):
  155. """
  156. L{RedirectWithNoLocation} is a subclass of L{Error} which sets
  157. a custom message in the constructor.
  158. """
  159. def test_validMessage(self):
  160. """
  161. When C{code}, C{message}, and C{uri} are passed to the
  162. L{RedirectWithNoLocation} constructor, the C{message} and C{uri}
  163. attributes are set, respectively.
  164. """
  165. e = error.RedirectWithNoLocation(b"302", b"REDIRECT",
  166. b"https://example.com")
  167. self.assertEqual(e.message, b"REDIRECT to https://example.com")
  168. self.assertEqual(e.uri, b"https://example.com")
  169. class MissingRenderMethodTests(unittest.TestCase):
  170. """
  171. Tests for how L{MissingRenderMethod} exceptions are initialized and
  172. displayed.
  173. """
  174. def test_constructor(self):
  175. """
  176. Given C{element} and C{renderName} arguments, the
  177. L{MissingRenderMethod} constructor assigns the values to the
  178. corresponding attributes.
  179. """
  180. elt = object()
  181. e = error.MissingRenderMethod(elt, 'renderThing')
  182. self.assertIs(e.element, elt)
  183. self.assertIs(e.renderName, 'renderThing')
  184. def test_repr(self):
  185. """
  186. A L{MissingRenderMethod} is represented using a custom string
  187. containing the element's representation and the method name.
  188. """
  189. elt = object()
  190. e = error.MissingRenderMethod(elt, 'renderThing')
  191. self.assertEqual(
  192. repr(e),
  193. ("'MissingRenderMethod': "
  194. "%r had no render method named 'renderThing'") % elt)
  195. class MissingTemplateLoaderTests(unittest.TestCase):
  196. """
  197. Tests for how L{MissingTemplateLoader} exceptions are initialized and
  198. displayed.
  199. """
  200. def test_constructor(self):
  201. """
  202. Given an C{element} argument, the L{MissingTemplateLoader} constructor
  203. assigns the value to the corresponding attribute.
  204. """
  205. elt = object()
  206. e = error.MissingTemplateLoader(elt)
  207. self.assertIs(e.element, elt)
  208. def test_repr(self):
  209. """
  210. A L{MissingTemplateLoader} is represented using a custom string
  211. containing the element's representation and the method name.
  212. """
  213. elt = object()
  214. e = error.MissingTemplateLoader(elt)
  215. self.assertEqual(
  216. repr(e),
  217. "'MissingTemplateLoader': %r had no loader" % elt)
  218. class FlattenerErrorTests(unittest.TestCase):
  219. """
  220. Tests for L{FlattenerError}.
  221. """
  222. def makeFlattenerError(self, roots=[]):
  223. try:
  224. raise RuntimeError("oh noes")
  225. except Exception as e:
  226. tb = traceback.extract_tb(sys.exc_info()[2])
  227. return error.FlattenerError(e, roots, tb)
  228. def fakeFormatRoot(self, obj):
  229. return 'R(%s)' % obj
  230. def test_constructor(self):
  231. """
  232. Given C{exception}, C{roots}, and C{traceback} arguments, the
  233. L{FlattenerError} constructor assigns the roots to the C{_roots}
  234. attribute.
  235. """
  236. e = self.makeFlattenerError(roots=['a', 'b'])
  237. self.assertEqual(e._roots, ['a', 'b'])
  238. def test_str(self):
  239. """
  240. The string form of a L{FlattenerError} is identical to its
  241. representation.
  242. """
  243. e = self.makeFlattenerError()
  244. self.assertEqual(str(e), repr(e))
  245. def test_reprWithRootsAndWithTraceback(self):
  246. """
  247. The representation of a L{FlattenerError} initialized with roots and a
  248. traceback contains a formatted representation of those roots (using
  249. C{_formatRoot}) and a formatted traceback.
  250. """
  251. e = self.makeFlattenerError(['a', 'b'])
  252. e._formatRoot = self.fakeFormatRoot
  253. self.assertTrue(
  254. re.match('Exception while flattening:\n'
  255. ' R\(a\)\n'
  256. ' R\(b\)\n'
  257. ' File "[^"]*", line [0-9]*, in makeFlattenerError\n'
  258. ' raise RuntimeError\("oh noes"\)\n'
  259. 'RuntimeError: oh noes\n$',
  260. repr(e), re.M | re.S),
  261. repr(e))
  262. def test_reprWithoutRootsAndWithTraceback(self):
  263. """
  264. The representation of a L{FlattenerError} initialized without roots but
  265. with a traceback contains a formatted traceback but no roots.
  266. """
  267. e = self.makeFlattenerError([])
  268. self.assertTrue(
  269. re.match('Exception while flattening:\n'
  270. ' File "[^"]*", line [0-9]*, in makeFlattenerError\n'
  271. ' raise RuntimeError\("oh noes"\)\n'
  272. 'RuntimeError: oh noes\n$',
  273. repr(e), re.M | re.S),
  274. repr(e))
  275. def test_reprWithoutRootsAndWithoutTraceback(self):
  276. """
  277. The representation of a L{FlattenerError} initialized without roots but
  278. with a traceback contains a formatted traceback but no roots.
  279. """
  280. e = error.FlattenerError(RuntimeError("oh noes"), [], None)
  281. self.assertTrue(
  282. re.match('Exception while flattening:\n'
  283. 'RuntimeError: oh noes\n$',
  284. repr(e), re.M | re.S),
  285. repr(e))
  286. def test_formatRootShortUnicodeString(self):
  287. """
  288. The C{_formatRoot} method formats a short unicode string using the
  289. built-in repr.
  290. """
  291. e = self.makeFlattenerError()
  292. self.assertEqual(e._formatRoot(nativeString('abcd')), repr('abcd'))
  293. def test_formatRootLongUnicodeString(self):
  294. """
  295. The C{_formatRoot} method formats a long unicode string using the
  296. built-in repr with an ellipsis.
  297. """
  298. e = self.makeFlattenerError()
  299. longString = nativeString('abcde-' * 20)
  300. self.assertEqual(e._formatRoot(longString),
  301. repr('abcde-abcde-abcde-ab<...>e-abcde-abcde-abcde-'))
  302. def test_formatRootShortByteString(self):
  303. """
  304. The C{_formatRoot} method formats a short byte string using the
  305. built-in repr.
  306. """
  307. e = self.makeFlattenerError()
  308. self.assertEqual(e._formatRoot(b'abcd'), repr(b'abcd'))
  309. def test_formatRootLongByteString(self):
  310. """
  311. The C{_formatRoot} method formats a long byte string using the
  312. built-in repr with an ellipsis.
  313. """
  314. e = self.makeFlattenerError()
  315. longString = b'abcde-' * 20
  316. self.assertEqual(e._formatRoot(longString),
  317. repr(b'abcde-abcde-abcde-ab<...>e-abcde-abcde-abcde-'))
  318. def test_formatRootTagNoFilename(self):
  319. """
  320. The C{_formatRoot} method formats a C{Tag} with no filename information
  321. as 'Tag <tagName>'.
  322. """
  323. e = self.makeFlattenerError()
  324. self.assertEqual(e._formatRoot(Tag('a-tag')), 'Tag <a-tag>')
  325. def test_formatRootTagWithFilename(self):
  326. """
  327. The C{_formatRoot} method formats a C{Tag} with filename information
  328. using the filename, line, column, and tag information
  329. """
  330. e = self.makeFlattenerError()
  331. t = Tag('a-tag', filename='tpl.py', lineNumber=10, columnNumber=20)
  332. self.assertEqual(e._formatRoot(t),
  333. 'File "tpl.py", line 10, column 20, in "a-tag"')
  334. def test_string(self):
  335. """
  336. If a L{FlattenerError} is created with a string root, up to around 40
  337. bytes from that string are included in the string representation of the
  338. exception.
  339. """
  340. self.assertEqual(
  341. str(error.FlattenerError(RuntimeError("reason"),
  342. ['abc123xyz'], [])),
  343. "Exception while flattening:\n"
  344. " 'abc123xyz'\n"
  345. "RuntimeError: reason\n")
  346. self.assertEqual(
  347. str(error.FlattenerError(
  348. RuntimeError("reason"), ['0123456789' * 10], [])),
  349. "Exception while flattening:\n"
  350. " '01234567890123456789"
  351. "<...>01234567890123456789'\n" # TODO: re-add 0
  352. "RuntimeError: reason\n")
  353. def test_unicode(self):
  354. """
  355. If a L{FlattenerError} is created with a unicode root, up to around 40
  356. characters from that string are included in the string representation
  357. of the exception.
  358. """
  359. # the response includes the output of repr(), which differs between
  360. # Python 2 and 3
  361. u = {'u': ''} if _PY3 else {'u': 'u'}
  362. self.assertEqual(
  363. str(error.FlattenerError(
  364. RuntimeError("reason"), [u'abc\N{SNOWMAN}xyz'], [])),
  365. "Exception while flattening:\n"
  366. " %(u)s'abc\\u2603xyz'\n" # Codepoint for SNOWMAN
  367. "RuntimeError: reason\n" % u)
  368. self.assertEqual(
  369. str(error.FlattenerError(
  370. RuntimeError("reason"), [u'01234567\N{SNOWMAN}9' * 10],
  371. [])),
  372. "Exception while flattening:\n"
  373. " %(u)s'01234567\\u2603901234567\\u26039"
  374. "<...>01234567\\u2603901234567"
  375. "\\u26039'\n"
  376. "RuntimeError: reason\n" % u)
  377. class UnsupportedMethodTests(unittest.SynchronousTestCase):
  378. """
  379. Tests for L{UnsupportedMethod}.
  380. """
  381. def test_str(self):
  382. """
  383. The C{__str__} for L{UnsupportedMethod} makes it clear that what it
  384. shows is a list of the supported methods, not the method that was
  385. unsupported.
  386. """
  387. b = "b" if _PY3 else ""
  388. e = error.UnsupportedMethod([b"HEAD", b"PATCH"])
  389. self.assertEqual(
  390. str(e), "Expected one of [{b}'HEAD', {b}'PATCH']".format(b=b),
  391. )