tests.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. import datetime
  2. import json
  3. import os
  4. import re
  5. import warnings
  6. from io import BytesIO
  7. from PIL import Image
  8. from testfixtures import LogCapture
  9. import django
  10. from django.core import management
  11. from django.core.exceptions import ImproperlyConfigured
  12. from django.test import TestCase, override_settings
  13. from django.urls import reverse
  14. from django.utils import timezone
  15. from django.utils.translation import gettext_lazy
  16. from captcha.conf import settings
  17. from captcha.fields import CaptchaField, CaptchaTextInput
  18. from captcha.models import CaptchaStore
  19. @override_settings(ROOT_URLCONF="captcha.tests.urls")
  20. class CaptchaCase(TestCase):
  21. def setUp(self):
  22. self.stores = {}
  23. self.__current_settings_output_format = settings.CAPTCHA_OUTPUT_FORMAT
  24. self.__current_settings_dictionary = settings.CAPTCHA_WORDS_DICTIONARY
  25. self.__current_settings_punctuation = settings.CAPTCHA_PUNCTUATION
  26. tested_helpers = [
  27. "captcha.helpers.math_challenge",
  28. "captcha.helpers.random_char_challenge",
  29. "captcha.helpers.unicode_challenge",
  30. ]
  31. if os.path.exists("/usr/share/dict/words"):
  32. settings.CAPTCHA_WORDS_DICTIONARY = "/usr/share/dict/words"
  33. settings.CAPTCHA_PUNCTUATION = ";-,."
  34. tested_helpers.append("captcha.helpers.word_challenge")
  35. tested_helpers.append(
  36. "captcha.helpers.huge_words_and_punctuation_challenge"
  37. )
  38. for helper in tested_helpers:
  39. challenge, response = settings._callable_from_string(helper)()
  40. (
  41. self.stores[helper.rsplit(".", 1)[-1].replace("_challenge", "_store")],
  42. _,
  43. ) = CaptchaStore.objects.get_or_create(
  44. challenge=challenge, response=response
  45. )
  46. challenge, response = settings.get_challenge()()
  47. self.stores["default_store"], _ = CaptchaStore.objects.get_or_create(
  48. challenge=challenge, response=response
  49. )
  50. self.default_store = self.stores["default_store"]
  51. def tearDown(self):
  52. settings.CAPTCHA_OUTPUT_FORMAT = self.__current_settings_output_format
  53. settings.CAPTCHA_WORDS_DICTIONARY = self.__current_settings_dictionary
  54. settings.CAPTCHA_PUNCTUATION = self.__current_settings_punctuation
  55. def _assertFormError(self, response, form_name, *args, **kwargs):
  56. if django.VERSION >= (4, 1):
  57. self.assertFormError(response.context.get(form_name), *args, **kwargs)
  58. else:
  59. self.assertFormError(response, form_name, *args, **kwargs)
  60. def __extract_hash_and_response(self, r):
  61. hash_ = re.findall(r'value="([0-9a-f]+)"', str(r.content))[0]
  62. response = CaptchaStore.objects.get(hashkey=hash_).response
  63. return hash_, response
  64. def test_image(self):
  65. for key in [store.hashkey for store in self.stores.values()]:
  66. response = self.client.get(reverse("captcha-image", kwargs=dict(key=key)))
  67. self.assertEqual(response.status_code, 200)
  68. self.assertTrue(response.has_header("content-type"))
  69. self.assertEqual(response["content-type"], "image/png")
  70. def test_audio(self):
  71. if not settings.CAPTCHA_FLITE_PATH:
  72. return
  73. for key in (
  74. self.stores.get("math_store").hashkey,
  75. self.stores.get("math_store").hashkey,
  76. self.default_store.hashkey,
  77. ):
  78. response = self.client.get(reverse("captcha-audio", kwargs=dict(key=key)))
  79. self.assertEqual(response.status_code, 200)
  80. self.assertTrue(response.ranged_file.size > 1024)
  81. self.assertTrue(response.has_header("content-type"))
  82. self.assertEqual(response["content-type"], "audio/wav")
  83. def test_form_submit(self):
  84. r = self.client.get(reverse("captcha-test"))
  85. self.assertEqual(r.status_code, 200)
  86. hash_, response = self.__extract_hash_and_response(r)
  87. r = self.client.post(
  88. reverse("captcha-test"),
  89. dict(
  90. captcha_0=hash_,
  91. captcha_1=response,
  92. subject="xxx",
  93. sender="asasd@asdasd.com",
  94. ),
  95. )
  96. self.assertEqual(r.status_code, 200)
  97. self.assertTrue(str(r.content).find("Form validated") > 0)
  98. r = self.client.post(
  99. reverse("captcha-test"),
  100. dict(
  101. captcha_0=hash_,
  102. captcha_1=response,
  103. subject="xxx",
  104. sender="asasd@asdasd.com",
  105. ),
  106. )
  107. self.assertEqual(r.status_code, 200)
  108. self.assertFalse(str(r.content).find("Form validated") > 0)
  109. def test_modelform(self):
  110. r = self.client.get(reverse("captcha-test-model-form"))
  111. self.assertEqual(r.status_code, 200)
  112. hash_, response = self.__extract_hash_and_response(r)
  113. r = self.client.post(
  114. reverse("captcha-test-model-form"),
  115. dict(
  116. captcha_0=hash_,
  117. captcha_1=response,
  118. subject="xxx",
  119. sender="asasd@asdasd.com",
  120. ),
  121. )
  122. self.assertEqual(r.status_code, 200)
  123. self.assertTrue(str(r.content).find("Form validated") > 0)
  124. r = self.client.post(
  125. reverse("captcha-test-model-form"),
  126. dict(
  127. captcha_0=hash_,
  128. captcha_1=response,
  129. subject="xxx",
  130. sender="asasd@asdasd.com",
  131. ),
  132. )
  133. self.assertEqual(r.status_code, 200)
  134. self.assertFalse(str(r.content).find("Form validated") > 0)
  135. def test_wrong_submit(self):
  136. for urlname in ("captcha-test", "captcha-test-model-form"):
  137. r = self.client.get(reverse(urlname))
  138. self.assertEqual(r.status_code, 200)
  139. r = self.client.post(
  140. reverse(urlname),
  141. dict(
  142. captcha_0="abc",
  143. captcha_1="wrong response",
  144. subject="xxx",
  145. sender="asasd@asdasd.com",
  146. ),
  147. )
  148. self._assertFormError(r, "form", "captcha", gettext_lazy("Invalid CAPTCHA"))
  149. def test_deleted_expired(self):
  150. self.default_store.expiration = timezone.now() - datetime.timedelta(minutes=5)
  151. self.default_store.save()
  152. hash_ = self.default_store.hashkey
  153. r = self.client.post(
  154. reverse("captcha-test"),
  155. dict(
  156. captcha_0=hash_,
  157. captcha_1=self.default_store.response,
  158. subject="xxx",
  159. sender="asasd@asdasd.com",
  160. ),
  161. )
  162. self.assertEqual(r.status_code, 200)
  163. self.assertFalse("Form validated" in str(r.content))
  164. # expired -> deleted
  165. try:
  166. CaptchaStore.objects.get(hashkey=hash_)
  167. self.fail()
  168. except Exception:
  169. pass
  170. def test_custom_error_message(self):
  171. r = self.client.get(reverse("captcha-test-custom-error-message"))
  172. self.assertEqual(r.status_code, 200)
  173. # Wrong answer
  174. r = self.client.post(
  175. reverse("captcha-test-custom-error-message"),
  176. dict(captcha_0="abc", captcha_1="wrong response"),
  177. )
  178. self._assertFormError(r, "form", "captcha", "TEST CUSTOM ERROR MESSAGE")
  179. # empty answer
  180. r = self.client.post(
  181. reverse("captcha-test-custom-error-message"),
  182. dict(captcha_0="abc", captcha_1=""),
  183. )
  184. self._assertFormError(
  185. r, "form", "captcha", gettext_lazy("This field is required.")
  186. )
  187. def test_repeated_challenge(self):
  188. CaptchaStore.objects.create(challenge="xxx", response="xxx")
  189. try:
  190. CaptchaStore.objects.create(challenge="xxx", response="xxx")
  191. except Exception:
  192. self.fail()
  193. def test_repeated_challenge_form_submit(self):
  194. __current_challange_function = settings.CAPTCHA_CHALLENGE_FUNCT
  195. for urlname in ("captcha-test", "captcha-test-model-form"):
  196. settings.CAPTCHA_CHALLENGE_FUNCT = "captcha.tests.trivial_challenge"
  197. r1 = self.client.get(reverse(urlname))
  198. r2 = self.client.get(reverse(urlname))
  199. self.assertEqual(r1.status_code, 200)
  200. self.assertEqual(r2.status_code, 200)
  201. if re.findall(r'value="([0-9a-f]+)"', str(r1.content)):
  202. hash_1 = re.findall(r'value="([0-9a-f]+)"', str(r1.content))[0]
  203. else:
  204. self.fail()
  205. if re.findall(r'value="([0-9a-f]+)"', str(r2.content)):
  206. hash_2 = re.findall(r'value="([0-9a-f]+)"', str(r2.content))[0]
  207. else:
  208. self.fail()
  209. try:
  210. store_1 = CaptchaStore.objects.get(hashkey=hash_1)
  211. store_2 = CaptchaStore.objects.get(hashkey=hash_2)
  212. except Exception:
  213. self.fail()
  214. self.assertTrue(store_1.pk != store_2.pk)
  215. self.assertTrue(store_1.response == store_2.response)
  216. self.assertTrue(hash_1 != hash_2)
  217. r1 = self.client.post(
  218. reverse(urlname),
  219. dict(
  220. captcha_0=hash_1,
  221. captcha_1=store_1.response,
  222. subject="xxx",
  223. sender="asasd@asdasd.com",
  224. ),
  225. )
  226. self.assertEqual(r1.status_code, 200)
  227. self.assertTrue(str(r1.content).find("Form validated") > 0)
  228. try:
  229. store_2 = CaptchaStore.objects.get(hashkey=hash_2)
  230. except Exception:
  231. self.fail()
  232. r2 = self.client.post(
  233. reverse(urlname),
  234. dict(
  235. captcha_0=hash_2,
  236. captcha_1=store_2.response,
  237. subject="xxx",
  238. sender="asasd@asdasd.com",
  239. ),
  240. )
  241. self.assertEqual(r2.status_code, 200)
  242. self.assertTrue(str(r2.content).find("Form validated") > 0)
  243. settings.CAPTCHA_CHALLENGE_FUNCT = __current_challange_function
  244. def test_output_format(self):
  245. for urlname in ("captcha-test", "captcha-test-model-form"):
  246. settings.CAPTCHA_OUTPUT_FORMAT = (
  247. "%(image)s<p>Hello, captcha world</p>%(hidden_field)s%(text_field)s"
  248. )
  249. r = self.client.get(reverse(urlname))
  250. self.assertEqual(r.status_code, 200)
  251. self.assertTrue("<p>Hello, captcha world</p>" in str(r.content))
  252. def test_invalid_output_format(self):
  253. for urlname in ("captcha-test", "captcha-test-model-form"):
  254. settings.CAPTCHA_OUTPUT_FORMAT = "%(image)s"
  255. try:
  256. with warnings.catch_warnings(record=True) as w:
  257. self.client.get(reverse(urlname))
  258. assert len(w) == 1
  259. self.assertTrue("CAPTCHA_OUTPUT_FORMAT" in str(w[-1].message))
  260. self.fail()
  261. except ImproperlyConfigured as e:
  262. self.assertTrue("CAPTCHA_OUTPUT_FORMAT" in str(e))
  263. def test_per_form_format(self):
  264. settings.CAPTCHA_OUTPUT_FORMAT = (
  265. "%(image)s testCustomFormatString %(hidden_field)s %(text_field)s"
  266. )
  267. r = self.client.get(reverse("captcha-test"))
  268. self.assertTrue("testCustomFormatString" in str(r.content))
  269. r = self.client.get(reverse("test_per_form_format"))
  270. self.assertTrue("testPerFieldCustomFormatString" in str(r.content))
  271. def test_custom_generator(self):
  272. r = self.client.get(reverse("test_custom_generator"))
  273. hash_, response = self.__extract_hash_and_response(r)
  274. self.assertEqual(response, "111111")
  275. def test_issue31_proper_abel(self):
  276. settings.CAPTCHA_OUTPUT_FORMAT = "%(image)s %(hidden_field)s %(text_field)s"
  277. r = self.client.get(reverse("captcha-test"))
  278. self.assertTrue('<label for="id_captcha_1"' in str(r.content))
  279. def test_refresh_view(self):
  280. r = self.client.get(
  281. reverse("captcha-refresh"), HTTP_X_REQUESTED_WITH="XMLHttpRequest"
  282. )
  283. try:
  284. new_data = json.loads(str(r.content, encoding="ascii"))
  285. self.assertTrue("image_url" in new_data)
  286. self.assertTrue("audio_url" in new_data)
  287. except Exception:
  288. self.fail()
  289. def test_content_length(self):
  290. for key in [store.hashkey for store in self.stores.values()]:
  291. response = self.client.get(reverse("captcha-image", kwargs=dict(key=key)))
  292. self.assertTrue(response.has_header("content-length"))
  293. self.assertTrue(response["content-length"].isdigit())
  294. self.assertTrue(int(response["content-length"]))
  295. def test_issue12_proper_instantiation(self):
  296. """
  297. This test covers a default django field and widget behavior
  298. It not assert anything. If something is wrong it will raise a error!
  299. """
  300. settings.CAPTCHA_OUTPUT_FORMAT = "%(image)s %(hidden_field)s %(text_field)s"
  301. widget = CaptchaTextInput(attrs={"class": "required"})
  302. CaptchaField(widget=widget)
  303. def test_test_mode_issue15(self):
  304. __current_test_mode_setting = settings.CAPTCHA_TEST_MODE
  305. settings.CAPTCHA_TEST_MODE = False
  306. r = self.client.get(reverse("captcha-test"))
  307. self.assertEqual(r.status_code, 200)
  308. r = self.client.post(
  309. reverse("captcha-test"),
  310. dict(
  311. captcha_0="abc",
  312. captcha_1="wrong response",
  313. subject="xxx",
  314. sender="asasd@asdasd.com",
  315. ),
  316. )
  317. self._assertFormError(r, "form", "captcha", gettext_lazy("Invalid CAPTCHA"))
  318. settings.CAPTCHA_TEST_MODE = True
  319. # Test mode, only 'PASSED' is accepted
  320. r = self.client.get(reverse("captcha-test"))
  321. self.assertEqual(r.status_code, 200)
  322. r = self.client.post(
  323. reverse("captcha-test"),
  324. dict(
  325. captcha_0="abc",
  326. captcha_1="wrong response",
  327. subject="xxx",
  328. sender="asasd@asdasd.com",
  329. ),
  330. )
  331. self._assertFormError(r, "form", "captcha", gettext_lazy("Invalid CAPTCHA"))
  332. r = self.client.get(reverse("captcha-test"))
  333. self.assertEqual(r.status_code, 200)
  334. r = self.client.post(
  335. reverse("captcha-test"),
  336. dict(
  337. captcha_0="abc",
  338. captcha_1="passed",
  339. subject="xxx",
  340. sender="asasd@asdasd.com",
  341. ),
  342. )
  343. self.assertTrue(str(r.content).find("Form validated") > 0)
  344. settings.CAPTCHA_TEST_MODE = __current_test_mode_setting
  345. def test_get_version(self):
  346. import captcha
  347. captcha.get_version()
  348. def test_missing_value(self):
  349. r = self.client.get(reverse("captcha-test-non-required"))
  350. self.assertEqual(r.status_code, 200)
  351. hash_, response = self.__extract_hash_and_response(r)
  352. # Empty response is okay when required is False
  353. r = self.client.post(
  354. reverse("captcha-test-non-required"),
  355. dict(subject="xxx", sender="asasd@asdasd.com"),
  356. )
  357. self.assertEqual(r.status_code, 200)
  358. self.assertTrue(str(r.content).find("Form validated") > 0)
  359. # But a valid response is okay, too
  360. r = self.client.get(reverse("captcha-test-non-required"))
  361. self.assertEqual(r.status_code, 200)
  362. hash_, response = self.__extract_hash_and_response(r)
  363. r = self.client.post(
  364. reverse("captcha-test-non-required"),
  365. dict(
  366. captcha_0=hash_,
  367. captcha_1=response,
  368. subject="xxx",
  369. sender="asasd@asdasd.com",
  370. ),
  371. )
  372. self.assertEqual(r.status_code, 200)
  373. self.assertTrue(str(r.content).find("Form validated") > 0)
  374. def test_autocomplete_off(self):
  375. r = self.client.get(reverse("captcha-test"))
  376. captcha_input = (
  377. '<input type="text" name="captcha_1" autocomplete="off" spellcheck="false" autocorrect="off" '
  378. 'autocapitalize="off" id="id_captcha_1" required />'
  379. )
  380. self.assertContains(r, captcha_input, html=True)
  381. def test_issue201_autocomplete_off_on_hiddeninput(self):
  382. r = self.client.get(reverse("captcha-test"))
  383. # Inspect the response context to find out the captcha key.
  384. key = r.context["form"]["captcha"].field.widget._key
  385. # Assety that autocomplete=off is set on the hidden captcha field.
  386. self.assertInHTML(
  387. '<input type="hidden" name="captcha_0" value="{}" id="id_captcha_0" autocomplete="off" required />'.format(
  388. key
  389. ),
  390. str(r.content),
  391. )
  392. def test_transparent_background(self):
  393. __current_test_mode_setting = settings.CAPTCHA_BACKGROUND_COLOR
  394. settings.CAPTCHA_BACKGROUND_COLOR = "transparent"
  395. for key in [store.hashkey for store in self.stores.values()]:
  396. response = self.client.get(reverse("captcha-image", kwargs=dict(key=key)))
  397. self.assertEqual(response.status_code, 200)
  398. self.assertTrue(response.has_header("content-type"))
  399. self.assertEqual(response["content-type"], "image/png")
  400. settings.CAPTCHA_BACKGROUND_COLOR = __current_test_mode_setting
  401. def test_expired_captcha_returns_410(self):
  402. for key in [store.hashkey for store in self.stores.values()]:
  403. response = self.client.get(reverse("captcha-image", kwargs=dict(key=key)))
  404. self.assertEqual(response.status_code, 200)
  405. CaptchaStore.objects.filter(hashkey=key).delete()
  406. response = self.client.get(reverse("captcha-image", kwargs=dict(key=key)))
  407. self.assertEqual(response.status_code, 410)
  408. def test_id_prefix(self):
  409. r = self.client.get(reverse("captcha-test-id-prefix"))
  410. self.assertTrue(
  411. '<label for="form1_id_captcha1_1">Captcha1:</label>' in str(r.content)
  412. )
  413. self.assertTrue('id="form1_id_captcha1_1"' in str(r.content))
  414. self.assertTrue(
  415. '<label for="form2_id_captcha2_1">Captcha2:</label>' in str(r.content)
  416. )
  417. self.assertTrue('id="form2_id_captcha2_1"' in str(r.content))
  418. def test_image_size(self):
  419. __current_test_mode_setting = settings.CAPTCHA_IMAGE_SIZE
  420. for key in [store.hashkey for store in self.stores.values()]:
  421. settings.CAPTCHA_IMAGE_SIZE = (201, 97)
  422. response = self.client.get(reverse("captcha-image", kwargs=dict(key=key)))
  423. self.assertEqual(response.status_code, 200)
  424. self.assertTrue(response.has_header("content-type"))
  425. self.assertEqual(response["content-type"], "image/png")
  426. self.assertEqual(Image.open(BytesIO(response.content)).size, (201, 97))
  427. settings.CAPTCHA_IMAGE_SIZE = __current_test_mode_setting
  428. def test_multiple_fonts(self):
  429. vera = os.path.join(os.path.dirname(__file__), "..", "fonts", "Vera.ttf")
  430. __current_test_mode_setting = settings.CAPTCHA_FONT_PATH
  431. settings.CAPTCHA_FONT_PATH = vera
  432. for key in [store.hashkey for store in self.stores.values()]:
  433. response = self.client.get(reverse("captcha-image", kwargs=dict(key=key)))
  434. self.assertEqual(response.status_code, 200)
  435. self.assertEqual(response["content-type"], "image/png")
  436. settings.CAPTCHA_FONT_PATH = [vera, vera, vera]
  437. for key in [store.hashkey for store in self.stores.values()]:
  438. response = self.client.get(reverse("captcha-image", kwargs=dict(key=key)))
  439. self.assertEqual(response.status_code, 200)
  440. self.assertEqual(response["content-type"], "image/png")
  441. settings.CAPTCHA_FONT_PATH = False
  442. for key in [store.hashkey for store in self.stores.values()]:
  443. try:
  444. response = self.client.get(
  445. reverse("captcha-image", kwargs=dict(key=key))
  446. )
  447. self.fail()
  448. except ImproperlyConfigured:
  449. pass
  450. settings.CAPTCHA_FONT_PATH = __current_test_mode_setting
  451. def test_template_overrides(self):
  452. __current_test_mode_setting = settings.CAPTCHA_IMAGE_TEMPLATE
  453. __current_field_template = settings.CAPTCHA_FIELD_TEMPLATE
  454. settings.CAPTCHA_IMAGE_TEMPLATE = "captcha_test/image.html"
  455. settings.CAPTCHA_FIELD_TEMPLATE = "captcha/field.html"
  456. for urlname in ("captcha-test", "captcha-test-model-form"):
  457. settings.CAPTCHA_CHALLENGE_FUNCT = "captcha.tests.trivial_challenge"
  458. r = self.client.get(reverse(urlname))
  459. self.assertTrue("captcha-template-test" in str(r.content))
  460. settings.CAPTCHA_IMAGE_TEMPLATE = __current_test_mode_setting
  461. settings.CAPTCHA_FIELD_TEMPLATE = __current_field_template
  462. def test_math_challenge(self):
  463. __current_test_mode_setting = settings.CAPTCHA_MATH_CHALLENGE_OPERATOR
  464. settings.CAPTCHA_MATH_CHALLENGE_OPERATOR = "~"
  465. helper = "captcha.helpers.math_challenge"
  466. challenge, response = settings._callable_from_string(helper)()
  467. while settings.CAPTCHA_MATH_CHALLENGE_OPERATOR not in challenge:
  468. challenge, response = settings._callable_from_string(helper)()
  469. self.assertEqual(
  470. response,
  471. str(
  472. eval(
  473. challenge.replace(settings.CAPTCHA_MATH_CHALLENGE_OPERATOR, "*")[
  474. :-1
  475. ]
  476. )
  477. ),
  478. )
  479. settings.CAPTCHA_MATH_CHALLENGE_OPERATOR = __current_test_mode_setting
  480. def test_get_from_pool(self):
  481. __current_test_get_from_pool_setting = settings.CAPTCHA_GET_FROM_POOL
  482. __current_test_get_from_pool_timeout_setting = (
  483. settings.CAPTCHA_GET_FROM_POOL_TIMEOUT
  484. )
  485. __current_test_timeout_setting = settings.CAPTCHA_TIMEOUT
  486. settings.CAPTCHA_GET_FROM_POOL = True
  487. settings.CAPTCHA_GET_FROM_POOL_TIMEOUT = 5
  488. settings.CAPTCHA_TIMEOUT = 90
  489. CaptchaStore.objects.all().delete() # Delete objects created during SetUp
  490. POOL_SIZE = 10
  491. CaptchaStore.create_pool(count=POOL_SIZE)
  492. self.assertEqual(CaptchaStore.objects.count(), POOL_SIZE)
  493. pool = CaptchaStore.objects.values_list("hashkey", flat=True)
  494. random_pick = CaptchaStore.pick()
  495. self.assertIn(random_pick, pool)
  496. # pick() should not create any extra captcha
  497. self.assertEqual(CaptchaStore.objects.count(), POOL_SIZE)
  498. settings.CAPTCHA_GET_FROM_POOL = __current_test_get_from_pool_setting
  499. settings.CAPTCHA_GET_FROM_POOL_TIMEOUT = (
  500. __current_test_get_from_pool_timeout_setting
  501. )
  502. settings.CAPTCHA_TIMEOUT = __current_test_timeout_setting
  503. def test_captcha_create_pool(self):
  504. CaptchaStore.objects.all().delete() # Delete objects created during SetUp
  505. POOL_SIZE = 10
  506. management.call_command("captcha_create_pool", pool_size=POOL_SIZE, verbosity=0)
  507. self.assertEqual(CaptchaStore.objects.count(), POOL_SIZE)
  508. def test_empty_pool_fallback(self):
  509. __current_test_get_from_pool_setting = settings.CAPTCHA_GET_FROM_POOL
  510. settings.CAPTCHA_GET_FROM_POOL = True
  511. CaptchaStore.objects.all().delete() # Delete objects created during SetUp
  512. with LogCapture() as log:
  513. CaptchaStore.pick()
  514. log.check(
  515. ("captcha.models", "ERROR", "Couldn't get a captcha from pool, generating")
  516. )
  517. self.assertEqual(CaptchaStore.objects.count(), 1)
  518. settings.CAPTCHA_GET_FROM_POOL = __current_test_get_from_pool_setting
  519. def trivial_challenge():
  520. return "trivial", "trivial"