test_useradmin_views.py 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287
  1. from django.contrib.auth import get_user_model
  2. from django.core import mail
  3. from django.urls import reverse
  4. from misago.acl.models import Role
  5. from misago.admin.testutils import AdminTestCase
  6. from misago.categories.models import Category
  7. from misago.legal.models import Agreement
  8. from misago.legal.utils import save_user_agreement_acceptance
  9. from misago.threads.testutils import post_thread, reply_thread
  10. from misago.users.datadownloads import request_user_data_download
  11. from misago.users.models import Ban, DataDownload, Rank
  12. UserModel = get_user_model()
  13. class UserAdminViewsTests(AdminTestCase):
  14. AJAX_HEADER = {'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'}
  15. def test_link_registered(self):
  16. """admin index view contains users link"""
  17. response = self.client.get(reverse('misago:admin:index'))
  18. self.assertContains(response, reverse('misago:admin:users:accounts:index'))
  19. def test_list_view(self):
  20. """users list view returns 200"""
  21. response = self.client.get(reverse('misago:admin:users:accounts:index'))
  22. self.assertEqual(response.status_code, 302)
  23. response = self.client.get(response['location'])
  24. self.assertEqual(response.status_code, 200)
  25. self.assertContains(response, self.user.username)
  26. def test_list_search(self):
  27. """users list is searchable"""
  28. response = self.client.get(reverse('misago:admin:users:accounts:index'))
  29. self.assertEqual(response.status_code, 302)
  30. link_base = response['location']
  31. response = self.client.get(link_base)
  32. self.assertEqual(response.status_code, 200)
  33. user_a = UserModel.objects.create_user('Tyrael', 't123@test.com', 'pass123')
  34. user_b = UserModel.objects.create_user('Tyrion', 't321@test.com', 'pass123')
  35. user_c = UserModel.objects.create_user('Karen', 't432@test.com', 'pass123')
  36. # Search both
  37. response = self.client.get('%s&username=tyr' % link_base)
  38. self.assertEqual(response.status_code, 200)
  39. self.assertContains(response, user_a.username)
  40. self.assertContains(response, user_b.username)
  41. # Search tyrion
  42. response = self.client.get('%s&username=tyrion' % link_base)
  43. self.assertEqual(response.status_code, 200)
  44. self.assertNotContains(response, user_a.username)
  45. self.assertContains(response, user_b.username)
  46. # Search tyrael
  47. response = self.client.get('%s&email=t123@test.com' % link_base)
  48. self.assertEqual(response.status_code, 200)
  49. self.assertContains(response, user_a.username)
  50. self.assertNotContains(response, user_b.username)
  51. # Search disabled
  52. user_c.is_active = False
  53. user_c.save()
  54. response = self.client.get('%s&disabled=1' % link_base)
  55. self.assertEqual(response.status_code, 200)
  56. self.assertNotContains(response, user_a.username)
  57. self.assertNotContains(response, user_b.username)
  58. self.assertContains(response, '<del>%s</del>' % user_c.username)
  59. # Search requested own account delete
  60. user_c.is_deleting_account = True
  61. user_c.save()
  62. response = self.client.get('%s&is_deleting_account=1' % link_base)
  63. self.assertEqual(response.status_code, 200)
  64. self.assertNotContains(response, user_a.username)
  65. self.assertNotContains(response, user_b.username)
  66. self.assertContains(response, '<del>%s</del>' % user_c.username)
  67. response = self.client.get('%s&disabled=1' % link_base)
  68. self.assertEqual(response.status_code, 200)
  69. self.assertNotContains(response, user_a.username)
  70. self.assertNotContains(response, user_b.username)
  71. self.assertContains(response, '<del>%s</del>' % user_c.username)
  72. def test_mass_activation(self):
  73. """users list activates multiple users"""
  74. user_pks = []
  75. for i in range(10):
  76. test_user = UserModel.objects.create_user(
  77. 'Bob%s' % i,
  78. 'bob%s@test.com' % i,
  79. 'pass123',
  80. requires_activation=1,
  81. )
  82. user_pks.append(test_user.pk)
  83. response = self.client.post(
  84. reverse('misago:admin:users:accounts:index'),
  85. data={
  86. 'action': 'activate',
  87. 'selected_items': user_pks,
  88. }
  89. )
  90. self.assertEqual(response.status_code, 302)
  91. inactive_qs = UserModel.objects.filter(
  92. id__in=user_pks,
  93. requires_activation=1,
  94. )
  95. self.assertEqual(inactive_qs.count(), 0)
  96. self.assertIn("has been activated", mail.outbox[0].subject)
  97. def test_mass_ban(self):
  98. """users list bans multiple users"""
  99. user_pks = []
  100. for i in range(10):
  101. test_user = UserModel.objects.create_user(
  102. 'Bob%s' % i,
  103. 'bob%s@test.com' % i,
  104. 'pass123',
  105. requires_activation=1,
  106. )
  107. user_pks.append(test_user.pk)
  108. response = self.client.post(
  109. reverse('misago:admin:users:accounts:index'),
  110. data={
  111. 'action': 'ban',
  112. 'selected_items': user_pks,
  113. }
  114. )
  115. self.assertNotContains(response, 'value="ip"')
  116. self.assertNotContains(response, 'value="ip_first"')
  117. self.assertNotContains(response, 'value="ip_two"')
  118. response = self.client.post(
  119. reverse('misago:admin:users:accounts:index'),
  120. data={
  121. 'action': 'ban',
  122. 'selected_items': user_pks,
  123. 'ban_type': ['usernames', 'emails', 'domains'],
  124. 'finalize': '',
  125. },
  126. )
  127. self.assertEqual(response.status_code, 302)
  128. self.assertEqual(Ban.objects.count(), 21)
  129. def test_mass_ban_with_ips(self):
  130. """users list bans multiple users that also have ips"""
  131. user_pks = []
  132. for i in range(10):
  133. test_user = UserModel.objects.create_user(
  134. 'Bob%s' % i,
  135. 'bob%s@test.com' % i,
  136. 'pass123',
  137. joined_from_ip='73.95.67.27',
  138. requires_activation=1,
  139. )
  140. user_pks.append(test_user.pk)
  141. response = self.client.post(
  142. reverse('misago:admin:users:accounts:index'),
  143. data={
  144. 'action': 'ban',
  145. 'selected_items': user_pks,
  146. }
  147. )
  148. self.assertContains(response, 'value="ip"')
  149. self.assertContains(response, 'value="ip_first"')
  150. self.assertContains(response, 'value="ip_two"')
  151. response = self.client.post(
  152. reverse('misago:admin:users:accounts:index'),
  153. data={
  154. 'action': 'ban',
  155. 'selected_items': user_pks,
  156. 'ban_type': ['usernames', 'emails', 'domains', 'ip', 'ip_first', 'ip_two'],
  157. 'finalize': '',
  158. },
  159. )
  160. self.assertEqual(response.status_code, 302)
  161. self.assertEqual(Ban.objects.count(), 24)
  162. def test_mass_request_data_download(self):
  163. """users list requests data download for multiple users"""
  164. user_pks = []
  165. for i in range(10):
  166. test_user = UserModel.objects.create_user(
  167. 'Bob%s' % i,
  168. 'bob%s@test.com' % i,
  169. 'pass123',
  170. requires_activation=1,
  171. )
  172. user_pks.append(test_user.pk)
  173. response = self.client.post(
  174. reverse('misago:admin:users:accounts:index'),
  175. data={
  176. 'action': 'request_data_download',
  177. 'selected_items': user_pks,
  178. }
  179. )
  180. self.assertEqual(response.status_code, 302)
  181. self.assertEqual(DataDownload.objects.filter(user_id__in=user_pks).count(), len(user_pks))
  182. def test_mass_request_data_download_avoid_excessive_downloads(self):
  183. """users list avoids excessive data download requests for multiple users"""
  184. user_pks = []
  185. for i in range(10):
  186. test_user = UserModel.objects.create_user(
  187. 'Bob%s' % i,
  188. 'bob%s@test.com' % i,
  189. 'pass123',
  190. requires_activation=1,
  191. )
  192. request_user_data_download(test_user)
  193. user_pks.append(test_user.pk)
  194. response = self.client.post(
  195. reverse('misago:admin:users:accounts:index'),
  196. data={
  197. 'action': 'v',
  198. 'selected_items': user_pks,
  199. }
  200. )
  201. self.assertEqual(response.status_code, 302)
  202. self.assertEqual(DataDownload.objects.filter(user_id__in=user_pks).count(), len(user_pks))
  203. def test_mass_delete_accounts_self(self):
  204. """its impossible to delete oneself"""
  205. user_pks = [self.user.pk]
  206. response = self.client.post(
  207. reverse('misago:admin:users:accounts:index'),
  208. data={
  209. 'action': 'delete_accounts',
  210. 'selected_items': user_pks,
  211. }
  212. )
  213. self.assertEqual(response.status_code, 302)
  214. response = self.client.get(response['location'])
  215. self.assertContains(response, "delete yourself")
  216. def test_mass_delete_accounts_admin(self):
  217. """its impossible to delete admin account"""
  218. user_pks = []
  219. for i in range(10):
  220. test_user = UserModel.objects.create_user(
  221. 'Bob%s' % i,
  222. 'bob%s@test.com' % i,
  223. 'pass123',
  224. )
  225. user_pks.append(test_user.pk)
  226. test_user.is_staff = True
  227. test_user.save()
  228. response = self.client.post(
  229. reverse('misago:admin:users:accounts:index'),
  230. data={
  231. 'action': 'delete_accounts',
  232. 'selected_items': user_pks,
  233. }
  234. )
  235. self.assertEqual(response.status_code, 302)
  236. response = self.client.get(response['location'])
  237. self.assertContains(response, "is admin and can")
  238. self.assertContains(response, "be deleted.")
  239. self.assertEqual(UserModel.objects.count(), 11)
  240. def test_mass_delete_accounts_superadmin(self):
  241. """its impossible to delete superadmin account"""
  242. user_pks = []
  243. for i in range(10):
  244. test_user = UserModel.objects.create_user(
  245. 'Bob%s' % i,
  246. 'bob%s@test.com' % i,
  247. 'pass123',
  248. )
  249. user_pks.append(test_user.pk)
  250. test_user.is_superuser = True
  251. test_user.save()
  252. response = self.client.post(
  253. reverse('misago:admin:users:accounts:index'),
  254. data={
  255. 'action': 'delete_accounts',
  256. 'selected_items': user_pks,
  257. }
  258. )
  259. self.assertEqual(response.status_code, 302)
  260. response = self.client.get(response['location'])
  261. self.assertContains(response, "is admin and can")
  262. self.assertContains(response, "be deleted.")
  263. self.assertEqual(UserModel.objects.count(), 11)
  264. def test_mass_delete_accounts(self):
  265. """users list deletes users"""
  266. # create 10 users to delete
  267. user_pks = []
  268. for i in range(10):
  269. test_user = UserModel.objects.create_user(
  270. 'Bob%s' % i,
  271. 'bob%s@test.com' % i,
  272. 'pass123',
  273. requires_activation=0,
  274. )
  275. user_pks.append(test_user.pk)
  276. # create 10 more users that won't be deleted
  277. for i in range(10):
  278. test_user = UserModel.objects.create_user(
  279. 'Weebl%s' % i,
  280. 'weebl%s@test.com' % i,
  281. 'pass123',
  282. requires_activation=0,
  283. )
  284. response = self.client.post(
  285. reverse('misago:admin:users:accounts:index'),
  286. data={
  287. 'action': 'delete_accounts',
  288. 'selected_items': user_pks,
  289. }
  290. )
  291. self.assertEqual(response.status_code, 302)
  292. self.assertEqual(UserModel.objects.count(), 11)
  293. def test_mass_delete_all_self(self):
  294. """its impossible to delete oneself with content"""
  295. user_pks = [self.user.pk]
  296. response = self.client.post(
  297. reverse('misago:admin:users:accounts:index'),
  298. data={
  299. 'action': 'delete_all',
  300. 'selected_items': user_pks,
  301. }
  302. )
  303. self.assertEqual(response.status_code, 302)
  304. response = self.client.get(response['location'])
  305. self.assertContains(response, "delete yourself")
  306. def test_mass_delete_all_admin(self):
  307. """its impossible to delete admin account and content"""
  308. user_pks = []
  309. for i in range(10):
  310. test_user = UserModel.objects.create_user(
  311. 'Bob%s' % i,
  312. 'bob%s@test.com' % i,
  313. 'pass123',
  314. )
  315. user_pks.append(test_user.pk)
  316. test_user.is_staff = True
  317. test_user.save()
  318. response = self.client.post(
  319. reverse('misago:admin:users:accounts:index'),
  320. data={
  321. 'action': 'delete_all',
  322. 'selected_items': user_pks,
  323. }
  324. )
  325. self.assertEqual(response.status_code, 302)
  326. response = self.client.get(response['location'])
  327. self.assertContains(response, "is admin and can")
  328. self.assertContains(response, "be deleted.")
  329. self.assertEqual(UserModel.objects.count(), 11)
  330. def test_mass_delete_all_superadmin(self):
  331. """its impossible to delete superadmin account and content"""
  332. user_pks = []
  333. for i in range(10):
  334. test_user = UserModel.objects.create_user(
  335. 'Bob%s' % i,
  336. 'bob%s@test.com' % i,
  337. 'pass123',
  338. )
  339. user_pks.append(test_user.pk)
  340. test_user.is_superuser = True
  341. test_user.save()
  342. response = self.client.post(
  343. reverse('misago:admin:users:accounts:index'),
  344. data={
  345. 'action': 'delete_all',
  346. 'selected_items': user_pks,
  347. }
  348. )
  349. self.assertEqual(response.status_code, 302)
  350. response = self.client.get(response['location'])
  351. self.assertContains(response, "is admin and can")
  352. self.assertContains(response, "be deleted.")
  353. self.assertEqual(UserModel.objects.count(), 11)
  354. def test_mass_delete_all(self):
  355. """users list mass deleting view has no showstoppers"""
  356. user_pks = []
  357. for i in range(10):
  358. test_user = UserModel.objects.create_user(
  359. 'Bob%s' % i,
  360. 'bob%s@test.com' % i,
  361. 'pass123',
  362. requires_activation=1,
  363. )
  364. user_pks.append(test_user.pk)
  365. response = self.client.post(
  366. reverse('misago:admin:users:accounts:index'),
  367. data={
  368. 'action': 'delete_all',
  369. 'selected_items': user_pks,
  370. }
  371. )
  372. self.assertEqual(response.status_code, 200)
  373. # asser that no user has been deleted, because actuall deleting happens in
  374. # dedicated views called via ajax from JavaScript
  375. self.assertEqual(UserModel.objects.count(), 11)
  376. def test_new_view(self):
  377. """new user view creates account"""
  378. response = self.client.get(reverse('misago:admin:users:accounts:new'))
  379. self.assertEqual(response.status_code, 200)
  380. default_rank = Rank.objects.get_default()
  381. authenticated_role = Role.objects.get(special_role='authenticated')
  382. response = self.client.post(
  383. reverse('misago:admin:users:accounts:new'),
  384. data={
  385. 'username': 'Bawww',
  386. 'rank': str(default_rank.pk),
  387. 'roles': str(authenticated_role.pk),
  388. 'email': 'reg@stered.com',
  389. 'new_password': 'pass123',
  390. 'staff_level': '0',
  391. }
  392. )
  393. self.assertEqual(response.status_code, 302)
  394. UserModel.objects.get_by_username('Bawww')
  395. test_user = UserModel.objects.get_by_email('reg@stered.com')
  396. self.assertTrue(test_user.check_password('pass123'))
  397. def test_new_view_password_with_whitespaces(self):
  398. """new user view creates account with whitespaces password"""
  399. response = self.client.get(reverse('misago:admin:users:accounts:new'))
  400. self.assertEqual(response.status_code, 200)
  401. default_rank = Rank.objects.get_default()
  402. authenticated_role = Role.objects.get(special_role='authenticated')
  403. response = self.client.post(
  404. reverse('misago:admin:users:accounts:new'),
  405. data={
  406. 'username': 'Bawww',
  407. 'rank': str(default_rank.pk),
  408. 'roles': str(authenticated_role.pk),
  409. 'email': 'reg@stered.com',
  410. 'new_password': ' pass123 ',
  411. 'staff_level': '0',
  412. }
  413. )
  414. self.assertEqual(response.status_code, 302)
  415. UserModel.objects.get_by_username('Bawww')
  416. test_user = UserModel.objects.get_by_email('reg@stered.com')
  417. self.assertTrue(test_user.check_password(' pass123 '))
  418. def test_edit_view(self):
  419. """edit user view changes account"""
  420. test_user = UserModel.objects.create_user('Bob', 'bob@test.com', 'pass123')
  421. test_link = reverse(
  422. 'misago:admin:users:accounts:edit', kwargs={
  423. 'pk': test_user.pk,
  424. }
  425. )
  426. response = self.client.get(test_link)
  427. self.assertEqual(response.status_code, 200)
  428. response = self.client.post(
  429. test_link,
  430. data={
  431. 'username': 'Bawww',
  432. 'rank': str(test_user.rank_id),
  433. 'roles': str(test_user.roles.all()[0].pk),
  434. 'email': 'reg@stered.com',
  435. 'new_password': 'newpass123',
  436. 'staff_level': '0',
  437. 'signature': 'Hello world!',
  438. 'is_signature_locked': '1',
  439. 'is_hiding_presence': '0',
  440. 'limits_private_thread_invites_to': '0',
  441. 'signature_lock_staff_message': 'Staff message',
  442. 'signature_lock_user_message': 'User message',
  443. 'subscribe_to_started_threads': '2',
  444. 'subscribe_to_replied_threads': '2',
  445. }
  446. )
  447. self.assertEqual(response.status_code, 302)
  448. updated_user = UserModel.objects.get(pk=test_user.pk)
  449. self.assertTrue(updated_user.check_password('newpass123'))
  450. self.assertEqual(updated_user.username, 'Bawww')
  451. self.assertEqual(updated_user.slug, 'bawww')
  452. UserModel.objects.get_by_username('Bawww')
  453. UserModel.objects.get_by_email('reg@stered.com')
  454. def test_edit_dont_change_username(self):
  455. """
  456. If username wasn't changed, don't touch user's username, slug or history
  457. This is regression test for issue #640
  458. """
  459. test_user = UserModel.objects.create_user('Bob', 'bob@test.com', 'pass123')
  460. test_link = reverse(
  461. 'misago:admin:users:accounts:edit', kwargs={
  462. 'pk': test_user.pk,
  463. }
  464. )
  465. response = self.client.get(test_link)
  466. self.assertEqual(response.status_code, 200)
  467. response = self.client.post(
  468. test_link,
  469. data={
  470. 'username': 'Bob',
  471. 'rank': str(test_user.rank_id),
  472. 'roles': str(test_user.roles.all()[0].pk),
  473. 'email': 'reg@stered.com',
  474. 'new_password': 'pass123',
  475. 'signature': 'Hello world!',
  476. 'is_signature_locked': '1',
  477. 'is_hiding_presence': '0',
  478. 'limits_private_thread_invites_to': '0',
  479. 'signature_lock_staff_message': 'Staff message',
  480. 'signature_lock_user_message': 'User message',
  481. 'subscribe_to_started_threads': '2',
  482. 'subscribe_to_replied_threads': '2',
  483. }
  484. )
  485. self.assertEqual(response.status_code, 302)
  486. updated_user = UserModel.objects.get(pk=test_user.pk)
  487. self.assertEqual(updated_user.username, 'Bob')
  488. self.assertEqual(updated_user.slug, 'bob')
  489. self.assertEqual(updated_user.namechanges.count(), 0)
  490. def test_edit_change_password_whitespaces(self):
  491. """edit user view changes account password to include whitespaces"""
  492. test_user = UserModel.objects.create_user('Bob', 'bob@test.com', 'pass123')
  493. test_link = reverse(
  494. 'misago:admin:users:accounts:edit', kwargs={
  495. 'pk': test_user.pk,
  496. }
  497. )
  498. response = self.client.get(test_link)
  499. self.assertEqual(response.status_code, 200)
  500. response = self.client.post(
  501. test_link,
  502. data={
  503. 'username': 'Bawww',
  504. 'rank': str(test_user.rank_id),
  505. 'roles': str(test_user.roles.all()[0].pk),
  506. 'email': 'reg@stered.com',
  507. 'new_password': ' newpass123 ',
  508. 'staff_level': '0',
  509. 'signature': 'Hello world!',
  510. 'is_signature_locked': '1',
  511. 'is_hiding_presence': '0',
  512. 'limits_private_thread_invites_to': '0',
  513. 'signature_lock_staff_message': 'Staff message',
  514. 'signature_lock_user_message': 'User message',
  515. 'subscribe_to_started_threads': '2',
  516. 'subscribe_to_replied_threads': '2',
  517. }
  518. )
  519. self.assertEqual(response.status_code, 302)
  520. updated_user = UserModel.objects.get(pk=test_user.pk)
  521. self.assertTrue(updated_user.check_password(' newpass123 '))
  522. self.assertEqual(updated_user.username, 'Bawww')
  523. self.assertEqual(updated_user.slug, 'bawww')
  524. UserModel.objects.get_by_username('Bawww')
  525. UserModel.objects.get_by_email('reg@stered.com')
  526. def test_edit_make_admin(self):
  527. """edit user view allows super admin to make other user admin"""
  528. test_user = UserModel.objects.create_user('Bob', 'bob@test.com', 'pass123')
  529. test_link = reverse(
  530. 'misago:admin:users:accounts:edit', kwargs={
  531. 'pk': test_user.pk,
  532. }
  533. )
  534. response = self.client.get(test_link)
  535. self.assertContains(response, 'id="id_is_staff_1"')
  536. self.assertContains(response, 'id="id_is_superuser_1"')
  537. response = self.client.post(
  538. test_link,
  539. data={
  540. 'username': 'Bawww',
  541. 'rank': str(test_user.rank_id),
  542. 'roles': str(test_user.roles.all()[0].pk),
  543. 'email': 'reg@stered.com',
  544. 'new_password': 'pass123',
  545. 'is_staff': '1',
  546. 'is_superuser': '0',
  547. 'signature': 'Hello world!',
  548. 'is_signature_locked': '1',
  549. 'is_hiding_presence': '0',
  550. 'limits_private_thread_invites_to': '0',
  551. 'signature_lock_staff_message': 'Staff message',
  552. 'signature_lock_user_message': 'User message',
  553. 'subscribe_to_started_threads': '2',
  554. 'subscribe_to_replied_threads': '2',
  555. }
  556. )
  557. self.assertEqual(response.status_code, 302)
  558. updated_user = UserModel.objects.get(pk=test_user.pk)
  559. self.assertTrue(updated_user.is_staff)
  560. self.assertFalse(updated_user.is_superuser)
  561. def test_edit_make_superadmin_admin(self):
  562. """edit user view allows super admin to make other user super admin"""
  563. test_user = UserModel.objects.create_user('Bob', 'bob@test.com', 'pass123')
  564. test_link = reverse(
  565. 'misago:admin:users:accounts:edit', kwargs={
  566. 'pk': test_user.pk,
  567. }
  568. )
  569. response = self.client.get(test_link)
  570. self.assertContains(response, 'id="id_is_staff_1"')
  571. self.assertContains(response, 'id="id_is_superuser_1"')
  572. response = self.client.post(
  573. test_link,
  574. data={
  575. 'username': 'Bawww',
  576. 'rank': str(test_user.rank_id),
  577. 'roles': str(test_user.roles.all()[0].pk),
  578. 'email': 'reg@stered.com',
  579. 'new_password': 'pass123',
  580. 'is_staff': '0',
  581. 'is_superuser': '1',
  582. 'signature': 'Hello world!',
  583. 'is_signature_locked': '1',
  584. 'is_hiding_presence': '0',
  585. 'limits_private_thread_invites_to': '0',
  586. 'signature_lock_staff_message': 'Staff message',
  587. 'signature_lock_user_message': 'User message',
  588. 'subscribe_to_started_threads': '2',
  589. 'subscribe_to_replied_threads': '2',
  590. }
  591. )
  592. self.assertEqual(response.status_code, 302)
  593. updated_user = UserModel.objects.get(pk=test_user.pk)
  594. self.assertFalse(updated_user.is_staff)
  595. self.assertTrue(updated_user.is_superuser)
  596. def test_edit_denote_superadmin(self):
  597. """edit user view allows super admin to denote other super admin"""
  598. test_user = UserModel.objects.create_user(
  599. 'Bob',
  600. 'bob@test.com',
  601. 'pass123',
  602. is_staff=True,
  603. is_superuser=True,
  604. )
  605. test_link = reverse(
  606. 'misago:admin:users:accounts:edit', kwargs={
  607. 'pk': test_user.pk,
  608. }
  609. )
  610. response = self.client.get(test_link)
  611. self.assertContains(response, 'id="id_is_staff_1"')
  612. self.assertContains(response, 'id="id_is_superuser_1"')
  613. response = self.client.post(
  614. test_link,
  615. data={
  616. 'username': 'Bawww',
  617. 'rank': str(test_user.rank_id),
  618. 'roles': str(test_user.roles.all()[0].pk),
  619. 'email': 'reg@stered.com',
  620. 'new_password': 'pass123',
  621. 'is_staff': '0',
  622. 'is_superuser': '0',
  623. 'signature': 'Hello world!',
  624. 'is_signature_locked': '1',
  625. 'is_hiding_presence': '0',
  626. 'limits_private_thread_invites_to': '0',
  627. 'signature_lock_staff_message': 'Staff message',
  628. 'signature_lock_user_message': 'User message',
  629. 'subscribe_to_started_threads': '2',
  630. 'subscribe_to_replied_threads': '2',
  631. }
  632. )
  633. self.assertEqual(response.status_code, 302)
  634. updated_user = UserModel.objects.get(pk=test_user.pk)
  635. self.assertFalse(updated_user.is_staff)
  636. self.assertFalse(updated_user.is_superuser)
  637. def test_edit_cant_make_admin(self):
  638. """edit user view forbids admins from making other admins"""
  639. self.user.is_superuser = False
  640. self.user.save()
  641. test_user = UserModel.objects.create_user('Bob', 'bob@test.com', 'pass123')
  642. test_link = reverse(
  643. 'misago:admin:users:accounts:edit', kwargs={
  644. 'pk': test_user.pk,
  645. }
  646. )
  647. response = self.client.get(test_link)
  648. self.assertNotContains(response, 'id="id_is_staff_1"')
  649. self.assertNotContains(response, 'id="id_is_superuser_1"')
  650. response = self.client.post(
  651. test_link,
  652. data={
  653. 'username': 'Bawww',
  654. 'rank': str(test_user.rank_id),
  655. 'roles': str(test_user.roles.all()[0].pk),
  656. 'email': 'reg@stered.com',
  657. 'new_password': 'pass123',
  658. 'is_staff': '1',
  659. 'is_superuser': '1',
  660. 'signature': 'Hello world!',
  661. 'is_signature_locked': '1',
  662. 'is_hiding_presence': '0',
  663. 'limits_private_thread_invites_to': '0',
  664. 'signature_lock_staff_message': 'Staff message',
  665. 'signature_lock_user_message': 'User message',
  666. 'subscribe_to_started_threads': '2',
  667. 'subscribe_to_replied_threads': '2',
  668. }
  669. )
  670. self.assertEqual(response.status_code, 302)
  671. updated_user = UserModel.objects.get(pk=test_user.pk)
  672. self.assertFalse(updated_user.is_staff)
  673. self.assertFalse(updated_user.is_superuser)
  674. def test_edit_disable_user(self):
  675. """edit user view allows admin to disable non admin"""
  676. self.user.is_superuser = False
  677. self.user.save()
  678. test_user = UserModel.objects.create_user('Bob', 'bob@test.com', 'pass123')
  679. test_link = reverse(
  680. 'misago:admin:users:accounts:edit', kwargs={
  681. 'pk': test_user.pk,
  682. }
  683. )
  684. response = self.client.get(test_link)
  685. self.assertContains(response, 'id="id_is_active_1"')
  686. self.assertContains(response, 'id="id_is_active_staff_message"')
  687. response = self.client.post(
  688. test_link,
  689. data={
  690. 'username': 'Bawww',
  691. 'rank': str(test_user.rank_id),
  692. 'roles': str(test_user.roles.all()[0].pk),
  693. 'email': 'reg@stered.com',
  694. 'new_password': 'pass123',
  695. 'is_staff': '0',
  696. 'is_superuser': '0',
  697. 'signature': 'Hello world!',
  698. 'is_signature_locked': '1',
  699. 'is_hiding_presence': '0',
  700. 'limits_private_thread_invites_to': '0',
  701. 'signature_lock_staff_message': 'Staff message',
  702. 'signature_lock_user_message': 'User message',
  703. 'subscribe_to_started_threads': '2',
  704. 'subscribe_to_replied_threads': '2',
  705. 'is_active': '0',
  706. 'is_active_staff_message': "Disabled in test!"
  707. }
  708. )
  709. self.assertEqual(response.status_code, 302)
  710. updated_user = UserModel.objects.get(pk=test_user.pk)
  711. self.assertFalse(updated_user.is_active)
  712. self.assertEqual(updated_user.is_active_staff_message, "Disabled in test!")
  713. def test_edit_superuser_disable_admin(self):
  714. """edit user view allows admin to disable non admin"""
  715. self.user.is_superuser = True
  716. self.user.save()
  717. test_user = UserModel.objects.create_user('Bob', 'bob@test.com', 'pass123')
  718. test_user.is_staff = True
  719. test_user.save()
  720. test_link = reverse(
  721. 'misago:admin:users:accounts:edit', kwargs={
  722. 'pk': test_user.pk,
  723. }
  724. )
  725. response = self.client.get(test_link)
  726. self.assertContains(response, 'id="id_is_active_1"')
  727. self.assertContains(response, 'id="id_is_active_staff_message"')
  728. response = self.client.post(
  729. test_link,
  730. data={
  731. 'username': 'Bawww',
  732. 'rank': str(test_user.rank_id),
  733. 'roles': str(test_user.roles.all()[0].pk),
  734. 'email': 'reg@stered.com',
  735. 'new_password': 'pass123',
  736. 'is_staff': '1',
  737. 'is_superuser': '0',
  738. 'signature': 'Hello world!',
  739. 'is_signature_locked': '1',
  740. 'is_hiding_presence': '0',
  741. 'limits_private_thread_invites_to': '0',
  742. 'signature_lock_staff_message': 'Staff message',
  743. 'signature_lock_user_message': 'User message',
  744. 'subscribe_to_started_threads': '2',
  745. 'subscribe_to_replied_threads': '2',
  746. 'is_active': '0',
  747. 'is_active_staff_message': "Disabled in test!"
  748. }
  749. )
  750. self.assertEqual(response.status_code, 302)
  751. updated_user = UserModel.objects.get(pk=test_user.pk)
  752. self.assertFalse(updated_user.is_active)
  753. self.assertEqual(updated_user.is_active_staff_message, "Disabled in test!")
  754. def test_edit_admin_cant_disable_admin(self):
  755. """edit user view disallows admin to disable admin"""
  756. self.user.is_superuser = False
  757. self.user.save()
  758. test_user = UserModel.objects.create_user('Bob', 'bob@test.com', 'pass123')
  759. test_user.is_staff = True
  760. test_user.save()
  761. test_link = reverse(
  762. 'misago:admin:users:accounts:edit', kwargs={
  763. 'pk': test_user.pk,
  764. }
  765. )
  766. response = self.client.get(test_link)
  767. self.assertNotContains(response, 'id="id_is_active_1"')
  768. self.assertNotContains(response, 'id="id_is_active_staff_message"')
  769. response = self.client.post(
  770. test_link,
  771. data={
  772. 'username': 'Bawww',
  773. 'rank': str(test_user.rank_id),
  774. 'roles': str(test_user.roles.all()[0].pk),
  775. 'email': 'reg@stered.com',
  776. 'new_password': 'pass123',
  777. 'is_staff': '1',
  778. 'is_superuser': '0',
  779. 'signature': 'Hello world!',
  780. 'is_signature_locked': '1',
  781. 'is_hiding_presence': '0',
  782. 'limits_private_thread_invites_to': '0',
  783. 'signature_lock_staff_message': 'Staff message',
  784. 'signature_lock_user_message': 'User message',
  785. 'subscribe_to_started_threads': '2',
  786. 'subscribe_to_replied_threads': '2',
  787. 'is_active': '0',
  788. 'is_active_staff_message': "Disabled in test!"
  789. }
  790. )
  791. self.assertEqual(response.status_code, 302)
  792. updated_user = UserModel.objects.get(pk=test_user.pk)
  793. self.assertTrue(updated_user.is_active)
  794. self.assertFalse(updated_user.is_active_staff_message)
  795. def test_edit_is_deleting_account_cant_reactivate(self):
  796. """users deleting own accounts can't be reactivated"""
  797. test_user = UserModel.objects.create_user('Bob', 'bob@test.com', 'pass123')
  798. test_user.mark_for_delete()
  799. test_link = reverse(
  800. 'misago:admin:users:accounts:edit', kwargs={
  801. 'pk': test_user.pk,
  802. }
  803. )
  804. response = self.client.get(test_link)
  805. self.assertNotContains(response, 'id="id_is_active_1"')
  806. self.assertNotContains(response, 'id="id_is_active_staff_message"')
  807. response = self.client.post(
  808. test_link,
  809. data={
  810. 'username': 'Bawww',
  811. 'rank': str(test_user.rank_id),
  812. 'roles': str(test_user.roles.all()[0].pk),
  813. 'email': 'reg@stered.com',
  814. 'new_password': 'pass123',
  815. 'is_staff': '1',
  816. 'is_superuser': '0',
  817. 'signature': 'Hello world!',
  818. 'is_signature_locked': '1',
  819. 'is_hiding_presence': '0',
  820. 'limits_private_thread_invites_to': '0',
  821. 'signature_lock_staff_message': 'Staff message',
  822. 'signature_lock_user_message': 'User message',
  823. 'subscribe_to_started_threads': '2',
  824. 'subscribe_to_replied_threads': '2',
  825. 'is_active': '1',
  826. }
  827. )
  828. self.assertEqual(response.status_code, 302)
  829. updated_user = UserModel.objects.get(pk=test_user.pk)
  830. self.assertFalse(updated_user.is_active)
  831. self.assertTrue(updated_user.is_deleting_account)
  832. def test_edit_unusable_password(self):
  833. """admin edit form handles unusable passwords and lets setting new password"""
  834. test_user = UserModel.objects.create_user('Bob', 'bob@test.com')
  835. self.assertFalse(test_user.has_usable_password())
  836. test_link = reverse(
  837. 'misago:admin:users:accounts:edit', kwargs={
  838. 'pk': test_user.pk,
  839. }
  840. )
  841. response = self.client.get(test_link)
  842. self.assertContains(response, 'id="div_id_has_usable_password"')
  843. response = self.client.post(
  844. test_link,
  845. data={
  846. 'username': 'Bawww',
  847. 'rank': str(test_user.rank_id),
  848. 'roles': str(test_user.roles.all()[0].pk),
  849. 'email': 'reg@stered.com',
  850. 'new_password': 'pass123',
  851. 'is_staff': '1',
  852. 'is_superuser': '0',
  853. 'signature': 'Hello world!',
  854. 'is_signature_locked': '1',
  855. 'is_hiding_presence': '0',
  856. 'limits_private_thread_invites_to': '0',
  857. 'signature_lock_staff_message': 'Staff message',
  858. 'signature_lock_user_message': 'User message',
  859. 'subscribe_to_started_threads': '2',
  860. 'subscribe_to_replied_threads': '2',
  861. 'is_active': '1',
  862. }
  863. )
  864. self.assertEqual(response.status_code, 302)
  865. updated_user = UserModel.objects.get(pk=test_user.pk)
  866. self.assertTrue(updated_user.has_usable_password())
  867. def test_edit_keep_unusable_password(self):
  868. """admin edit form handles unusable passwords and lets admin leave them unchanged"""
  869. test_user = UserModel.objects.create_user('Bob', 'bob@test.com')
  870. self.assertFalse(test_user.has_usable_password())
  871. test_link = reverse(
  872. 'misago:admin:users:accounts:edit', kwargs={
  873. 'pk': test_user.pk,
  874. }
  875. )
  876. response = self.client.get(test_link)
  877. self.assertContains(response, 'id="div_id_has_usable_password"')
  878. response = self.client.post(
  879. test_link,
  880. data={
  881. 'username': 'Bawww',
  882. 'rank': str(test_user.rank_id),
  883. 'roles': str(test_user.roles.all()[0].pk),
  884. 'email': 'reg@stered.com',
  885. 'is_staff': '1',
  886. 'is_superuser': '0',
  887. 'signature': 'Hello world!',
  888. 'is_signature_locked': '1',
  889. 'is_hiding_presence': '0',
  890. 'limits_private_thread_invites_to': '0',
  891. 'signature_lock_staff_message': 'Staff message',
  892. 'signature_lock_user_message': 'User message',
  893. 'subscribe_to_started_threads': '2',
  894. 'subscribe_to_replied_threads': '2',
  895. 'is_active': '1',
  896. }
  897. )
  898. self.assertEqual(response.status_code, 302)
  899. updated_user = UserModel.objects.get(pk=test_user.pk)
  900. self.assertFalse(updated_user.has_usable_password())
  901. def test_edit_agreements_list(self):
  902. """edit view displays list of user's agreements"""
  903. test_user = UserModel.objects.create_user('Bob', 'bob@test.com', 'pass123')
  904. test_link = reverse(
  905. 'misago:admin:users:accounts:edit', kwargs={
  906. 'pk': test_user.pk,
  907. }
  908. )
  909. agreement = Agreement.objects.create(
  910. type=Agreement.TYPE_TOS,
  911. title="Test agreement!",
  912. text="Lorem ipsum!",
  913. is_active=True,
  914. )
  915. response = self.client.get(test_link)
  916. self.assertEqual(response.status_code, 200)
  917. self.assertNotContains(response, agreement.title)
  918. save_user_agreement_acceptance(test_user, agreement, commit=True)
  919. response = self.client.get(test_link)
  920. self.assertEqual(response.status_code, 200)
  921. self.assertContains(response, agreement.title)
  922. def test_delete_threads_view_self(self):
  923. """delete user threads view validates if user deletes self"""
  924. test_link = reverse(
  925. 'misago:admin:users:accounts:delete-threads', kwargs={
  926. 'pk': self.user.pk,
  927. }
  928. )
  929. response = self.client.post(test_link, **self.AJAX_HEADER)
  930. self.assertEqual(response.status_code, 302)
  931. response = self.client.get(reverse('misago:admin:index'))
  932. self.assertContains(response, "delete yourself");
  933. def test_delete_threads_view_staff(self):
  934. """delete user threads view validates if user deletes staff"""
  935. test_user = UserModel.objects.create_user('Bob', 'bob@test.com', 'pass123')
  936. test_user.is_staff = True
  937. test_user.save()
  938. test_link = reverse(
  939. 'misago:admin:users:accounts:delete-threads', kwargs={
  940. 'pk': test_user.pk,
  941. }
  942. )
  943. response = self.client.post(test_link, **self.AJAX_HEADER)
  944. self.assertEqual(response.status_code, 302)
  945. response = self.client.get(reverse('misago:admin:index'))
  946. self.assertContains(response, "is admin and");
  947. def test_delete_threads_view_superuser(self):
  948. """delete user threads view validates if user deletes superuser"""
  949. test_user = UserModel.objects.create_user('Bob', 'bob@test.com', 'pass123')
  950. test_user.is_superuser = True
  951. test_user.save()
  952. test_link = reverse(
  953. 'misago:admin:users:accounts:delete-threads', kwargs={
  954. 'pk': test_user.pk,
  955. }
  956. )
  957. response = self.client.post(test_link, **self.AJAX_HEADER)
  958. self.assertEqual(response.status_code, 302)
  959. response = self.client.get(reverse('misago:admin:index'))
  960. self.assertContains(response, "is admin and");
  961. def test_delete_threads_view(self):
  962. """delete user threads view deletes threads"""
  963. test_user = UserModel.objects.create_user('Bob', 'bob@test.com', 'pass123')
  964. test_link = reverse(
  965. 'misago:admin:users:accounts:delete-threads', kwargs={
  966. 'pk': test_user.pk,
  967. }
  968. )
  969. category = Category.objects.all_categories()[:1][0]
  970. [post_thread(category, poster=test_user) for _ in range(10)]
  971. response = self.client.post(test_link, **self.AJAX_HEADER)
  972. self.assertEqual(response.status_code, 200)
  973. response_dict = response.json()
  974. self.assertEqual(response_dict['deleted_count'], 10)
  975. self.assertFalse(response_dict['is_completed'])
  976. response = self.client.post(test_link, **self.AJAX_HEADER)
  977. self.assertEqual(response.status_code, 200)
  978. response_dict = response.json()
  979. self.assertEqual(response_dict['deleted_count'], 0)
  980. self.assertTrue(response_dict['is_completed'])
  981. def test_delete_posts_view_self(self):
  982. """delete user posts view validates if user deletes self"""
  983. test_link = reverse(
  984. 'misago:admin:users:accounts:delete-posts', kwargs={
  985. 'pk': self.user.pk,
  986. }
  987. )
  988. response = self.client.post(test_link, **self.AJAX_HEADER)
  989. self.assertEqual(response.status_code, 302)
  990. response = self.client.get(reverse('misago:admin:index'))
  991. self.assertContains(response, "delete yourself");
  992. def test_delete_posts_view_staff(self):
  993. """delete user posts view validates if user deletes staff"""
  994. test_user = UserModel.objects.create_user('Bob', 'bob@test.com', 'pass123')
  995. test_user.is_staff = True
  996. test_user.save()
  997. test_link = reverse(
  998. 'misago:admin:users:accounts:delete-posts', kwargs={
  999. 'pk': test_user.pk,
  1000. }
  1001. )
  1002. response = self.client.post(test_link, **self.AJAX_HEADER)
  1003. self.assertEqual(response.status_code, 302)
  1004. response = self.client.get(reverse('misago:admin:index'))
  1005. self.assertContains(response, "is admin and");
  1006. def test_delete_posts_view_superuser(self):
  1007. """delete user posts view validates if user deletes superuser"""
  1008. test_user = UserModel.objects.create_user('Bob', 'bob@test.com', 'pass123')
  1009. test_user.is_superuser = True
  1010. test_user.save()
  1011. test_link = reverse(
  1012. 'misago:admin:users:accounts:delete-posts', kwargs={
  1013. 'pk': test_user.pk,
  1014. }
  1015. )
  1016. response = self.client.post(test_link, **self.AJAX_HEADER)
  1017. self.assertEqual(response.status_code, 302)
  1018. response = self.client.get(reverse('misago:admin:index'))
  1019. self.assertContains(response, "is admin and");
  1020. def test_delete_posts_view(self):
  1021. """delete user posts view deletes posts"""
  1022. test_user = UserModel.objects.create_user('Bob', 'bob@test.com', 'pass123')
  1023. test_link = reverse(
  1024. 'misago:admin:users:accounts:delete-posts', kwargs={
  1025. 'pk': test_user.pk,
  1026. }
  1027. )
  1028. category = Category.objects.all_categories()[:1][0]
  1029. thread = post_thread(category)
  1030. [reply_thread(thread, poster=test_user) for _ in range(10)]
  1031. response = self.client.post(test_link, **self.AJAX_HEADER)
  1032. self.assertEqual(response.status_code, 200)
  1033. response_dict = response.json()
  1034. self.assertEqual(response_dict['deleted_count'], 10)
  1035. self.assertFalse(response_dict['is_completed'])
  1036. response = self.client.post(test_link, **self.AJAX_HEADER)
  1037. self.assertEqual(response.status_code, 200)
  1038. response_dict = response.json()
  1039. self.assertEqual(response_dict['deleted_count'], 0)
  1040. self.assertTrue(response_dict['is_completed'])
  1041. def test_delete_account_view_self(self):
  1042. """delete user account view validates if user deletes self"""
  1043. test_link = reverse(
  1044. 'misago:admin:users:accounts:delete-account', kwargs={
  1045. 'pk': self.user.pk,
  1046. }
  1047. )
  1048. response = self.client.post(test_link, **self.AJAX_HEADER)
  1049. self.assertEqual(response.status_code, 302)
  1050. response = self.client.get(reverse('misago:admin:index'))
  1051. self.assertContains(response, "delete yourself");
  1052. def test_delete_account_view_staff(self):
  1053. """delete user account view validates if user deletes staff"""
  1054. test_user = UserModel.objects.create_user('Bob', 'bob@test.com', 'pass123')
  1055. test_user.is_staff = True
  1056. test_user.save()
  1057. test_link = reverse(
  1058. 'misago:admin:users:accounts:delete-account', kwargs={
  1059. 'pk': test_user.pk,
  1060. }
  1061. )
  1062. response = self.client.post(test_link, **self.AJAX_HEADER)
  1063. self.assertEqual(response.status_code, 302)
  1064. response = self.client.get(reverse('misago:admin:index'))
  1065. self.assertContains(response, "is admin and");
  1066. def test_delete_account_view_superuser(self):
  1067. """delete user account view validates if user deletes superuser"""
  1068. test_user = UserModel.objects.create_user('Bob', 'bob@test.com', 'pass123')
  1069. test_user.is_superuser = True
  1070. test_user.save()
  1071. test_link = reverse(
  1072. 'misago:admin:users:accounts:delete-account', kwargs={
  1073. 'pk': test_user.pk,
  1074. }
  1075. )
  1076. response = self.client.post(test_link, **self.AJAX_HEADER)
  1077. self.assertEqual(response.status_code, 302)
  1078. response = self.client.get(reverse('misago:admin:index'))
  1079. self.assertContains(response, "is admin and");
  1080. def test_delete_account_view(self):
  1081. """delete user account view deletes user account"""
  1082. test_user = UserModel.objects.create_user('Bob', 'bob@test.com', 'pass123')
  1083. test_link = reverse(
  1084. 'misago:admin:users:accounts:delete-account', kwargs={
  1085. 'pk': test_user.pk,
  1086. }
  1087. )
  1088. response = self.client.post(test_link, **self.AJAX_HEADER)
  1089. self.assertEqual(response.status_code, 200)
  1090. response_dict = response.json()
  1091. self.assertTrue(response_dict['is_completed'])