test_pagure_flask_ui_groups.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  1. # -*- coding: utf-8 -*-
  2. """
  3. (c) 2015-2016 - Copyright Red Hat Inc
  4. Authors:
  5. Pierre-Yves Chibon <pingou@pingoured.fr>
  6. """
  7. from __future__ import unicode_literals, absolute_import
  8. import unittest
  9. import shutil
  10. import sys
  11. import os
  12. import json
  13. import pagure_messages
  14. from fedora_messaging import api, testing
  15. from mock import ANY, patch
  16. sys.path.insert(
  17. 0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")
  18. )
  19. import pagure.config
  20. import tests
  21. class PagureFlaskGroupstests(tests.Modeltests):
  22. """Tests for flask groups controller of pagure"""
  23. def test_group_lists(self):
  24. """Test the group_lists endpoint."""
  25. output = self.app.get("/groups")
  26. self.assertIn(
  27. '<h3 class="font-weight-bold">\n'
  28. ' Groups <span class="badge badge-secondary">0</span>',
  29. output.get_data(as_text=True),
  30. )
  31. def test_add_group(self):
  32. """Test the add_group endpoint."""
  33. output = self.app.get("/group/add")
  34. self.assertEqual(output.status_code, 302)
  35. user = tests.FakeUser()
  36. with tests.user_set(self.app.application, user):
  37. output = self.app.get("/group/add")
  38. self.assertEqual(output.status_code, 403)
  39. user.username = "pingou"
  40. with tests.user_set(self.app.application, user):
  41. output = self.app.get("/group/add")
  42. self.assertEqual(output.status_code, 200)
  43. self.assertIn(
  44. "<strong>Create new group</strong>",
  45. output.get_data(as_text=True),
  46. )
  47. self.assertNotIn(
  48. '<option value="admin">admin</option>',
  49. output.get_data(as_text=True),
  50. )
  51. csrf_token = (
  52. output.get_data(as_text=True)
  53. .split('name="csrf_token" type="hidden" value="')[1]
  54. .split('">')[0]
  55. )
  56. data = {}
  57. # Insufficient input
  58. output = self.app.post("/group/add", data=data)
  59. self.assertEqual(output.status_code, 200)
  60. self.assertIn(
  61. "<strong>Create new group</strong>",
  62. output.get_data(as_text=True),
  63. )
  64. self.assertEqual(
  65. output.get_data(as_text=True).count("This field is required."),
  66. 3,
  67. )
  68. data = {
  69. "group_name": "test_group",
  70. "display_name": "Test Group",
  71. "description": "This is a group for the tests",
  72. }
  73. # Missing CSRF
  74. output = self.app.post("/group/add", data=data)
  75. self.assertEqual(output.status_code, 200)
  76. self.assertIn(
  77. "<strong>Create new group</strong>",
  78. output.get_data(as_text=True),
  79. )
  80. self.assertEqual(
  81. output.get_data(as_text=True).count("This field is required."),
  82. 0,
  83. )
  84. data["csrf_token"] = csrf_token
  85. # All good
  86. output = self.app.post(
  87. "/group/add", data=data, follow_redirects=True
  88. )
  89. self.assertEqual(output.status_code, 200)
  90. self.assertIn(
  91. "User `pingou` added to " "the group `test_group`.",
  92. output.get_data(as_text=True),
  93. )
  94. self.assertIn(
  95. "Group `test_group` created.", output.get_data(as_text=True)
  96. )
  97. self.assertIn(
  98. '<h3 class="font-weight-bold">\n'
  99. ' Groups <span class="badge badge-secondary">1</span>',
  100. output.get_data(as_text=True),
  101. )
  102. user = tests.FakeUser(
  103. username="pingou", groups=pagure.config.config["ADMIN_GROUP"]
  104. )
  105. with tests.user_set(self.app.application, user):
  106. output = self.app.get("/group/add")
  107. self.assertEqual(output.status_code, 200)
  108. self.assertIn(
  109. "<strong>Create new group</strong>",
  110. output.get_data(as_text=True),
  111. )
  112. self.assertIn(
  113. '<option value="admin">admin</option>',
  114. output.get_data(as_text=True),
  115. )
  116. data = {
  117. "group_name": "test_admin_group",
  118. "group_type": "admin",
  119. "display_name": "Test Admin Group",
  120. "description": "This is another group for the tests",
  121. "csrf_token": csrf_token,
  122. }
  123. # All good
  124. output = self.app.post(
  125. "/group/add", data=data, follow_redirects=True
  126. )
  127. self.assertEqual(output.status_code, 200)
  128. self.assertIn(
  129. "User `pingou` added to " "the group `test_admin_group`.",
  130. output.get_data(as_text=True),
  131. )
  132. self.assertIn(
  133. "Group `test_admin_group` " "created.",
  134. output.get_data(as_text=True),
  135. )
  136. self.assertIn(
  137. '<h3 class="font-weight-bold">\n'
  138. ' Groups <span class="badge badge-secondary">2</span>',
  139. output.get_data(as_text=True),
  140. )
  141. @patch.dict(
  142. "pagure.config.config", {"FEDORA_MESSAGING_NOTIFICATIONS": True}
  143. )
  144. def test_edit_group(self):
  145. """Test the edit_group endpoint."""
  146. output = self.app.get("/group/test_group/edit")
  147. self.assertEqual(output.status_code, 302)
  148. user = tests.FakeUser()
  149. with tests.user_set(self.app.application, user):
  150. output = self.app.get("/group/test_group/edit")
  151. self.assertEqual(output.status_code, 404)
  152. self.assertIn(
  153. "<p>Group not found</p>", output.get_data(as_text=True)
  154. )
  155. self.test_add_group()
  156. user.username = "foo"
  157. with tests.user_set(self.app.application, user):
  158. output = self.app.get("/group/foo/edit")
  159. self.assertEqual(output.status_code, 404)
  160. self.assertIn(
  161. "<p>Group not found</p>", output.get_data(as_text=True)
  162. )
  163. output = self.app.get("/group/test_group/edit")
  164. self.assertEqual(output.status_code, 200)
  165. self.assertIn(
  166. "<title>Edit group: test_group - Pagure</title>",
  167. output.get_data(as_text=True),
  168. )
  169. self.assertIn(
  170. '<form action="/group/test_group/edit" method="post">',
  171. output.get_data(as_text=True),
  172. )
  173. self.assertIn(
  174. '<strong><label for="description">Description'
  175. "</label> </strong>",
  176. output.get_data(as_text=True),
  177. )
  178. csrf_token = (
  179. output.get_data(as_text=True)
  180. .split('name="csrf_token" type="hidden" value="')[1]
  181. .split('">')[0]
  182. )
  183. # Missing CSRF
  184. data = {
  185. "group_name": "test_group",
  186. "display_name": "Test Group edited",
  187. "description": "This is a group for the tests edited",
  188. }
  189. output = self.app.post(
  190. "/group/test_group/edit", data=data, follow_redirects=True
  191. )
  192. self.assertEqual(output.status_code, 200)
  193. self.assertIn(
  194. "<title>Edit group: test_group - Pagure</title>",
  195. output.get_data(as_text=True),
  196. )
  197. self.assertIn(
  198. '<form action="/group/test_group/edit" method="post">',
  199. output.get_data(as_text=True),
  200. )
  201. self.assertIn(
  202. '<strong><label for="description">Description'
  203. "</label> </strong>",
  204. output.get_data(as_text=True),
  205. )
  206. # User not allowed
  207. data["csrf_token"] = csrf_token
  208. output = self.app.post(
  209. "/group/test_group/edit", data=data, follow_redirects=True
  210. )
  211. self.assertEqual(output.status_code, 200)
  212. self.assertIn(
  213. "<title>Group test_group - Pagure</title>",
  214. output.get_data(as_text=True),
  215. )
  216. self.assertIn(
  217. "You are not allowed to edit this group",
  218. output.get_data(as_text=True),
  219. )
  220. self.assertIn(
  221. '<h3 class="mb-0 font-weight-bold">Test Group</h3>',
  222. output.get_data(as_text=True),
  223. )
  224. user.username = "pingou"
  225. with tests.user_set(self.app.application, user):
  226. # Invalid repo
  227. output = self.app.post(
  228. "/group/bar/edit", data=data, follow_redirects=True
  229. )
  230. self.assertEqual(output.status_code, 404)
  231. self.assertIn(
  232. "<p>Group not found</p>", output.get_data(as_text=True)
  233. )
  234. # All good
  235. with testing.mock_sends(
  236. pagure_messages.GroupEditV1(
  237. topic="pagure.group.edit",
  238. body={
  239. "group": {
  240. "name": "test_group",
  241. "display_name": "Test Group edited",
  242. "description": "This is a group for the tests edited",
  243. "group_type": "user",
  244. "creator": {
  245. "name": "pingou",
  246. "fullname": "PY C",
  247. "url_path": "user/pingou",
  248. "full_url": "http://localhost.localdomain/user/pingou",
  249. },
  250. "date_created": ANY,
  251. "members": ["pingou"],
  252. "full_url": "http://localhost.localdomain/group/test_group",
  253. },
  254. "fields": ["display_name", "description"],
  255. "agent": "pingou",
  256. },
  257. )
  258. ):
  259. output = self.app.post(
  260. "/group/test_group/edit", data=data, follow_redirects=True
  261. )
  262. self.assertEqual(output.status_code, 200)
  263. self.assertIn(
  264. "<title>Group test_group - Pagure</title>",
  265. output.get_data(as_text=True),
  266. )
  267. self.assertIn(
  268. '<h3 class="mb-0 font-weight-bold">Test Group edited</h3>',
  269. output.get_data(as_text=True),
  270. )
  271. self.assertIn(
  272. "Group &#34;Test Group edited&#34; (test_group) edited",
  273. output.get_data(as_text=True),
  274. )
  275. def test_give_group(self):
  276. """Test the give_group endpoint."""
  277. output = self.app.post("/group/test_group/give")
  278. self.assertEqual(output.status_code, 302)
  279. user = tests.FakeUser()
  280. with tests.user_set(self.app.application, user):
  281. output = self.app.post("/group/test_group/give")
  282. self.assertEqual(output.status_code, 404)
  283. self.assertIn(
  284. "<p>Group not found</p>", output.get_data(as_text=True)
  285. )
  286. self.test_add_group()
  287. user.username = "foo"
  288. with tests.user_set(self.app.application, user):
  289. output = self.app.post("/group/foo/give")
  290. self.assertEqual(output.status_code, 404)
  291. self.assertIn(
  292. "<p>Group not found</p>", output.get_data(as_text=True)
  293. )
  294. output = self.app.post("/group/test_group/give")
  295. self.assertEqual(output.status_code, 403)
  296. csrf_token = self.get_csrf()
  297. user.username = "pingou"
  298. with tests.user_set(self.app.application, user):
  299. # Missing CSRF
  300. data = {"username": "invalid"}
  301. output = self.app.post(
  302. "/group/test_group/give", data=data, follow_redirects=True
  303. )
  304. self.assertEqual(output.status_code, 200)
  305. output_text = output.get_data(as_text=True)
  306. self.assertIn(
  307. "<title>Group test_group - Pagure</title>", output_text
  308. )
  309. self.assertIn(
  310. 'administered by <a href="/user/pingou">pingou</a>',
  311. output_text,
  312. )
  313. # User not found
  314. data["csrf_token"] = csrf_token
  315. output = self.app.post(
  316. "/group/test_group/give", data=data, follow_redirects=True
  317. )
  318. self.assertEqual(output.status_code, 200)
  319. output_text = output.get_data(as_text=True)
  320. self.assertIn(
  321. "<title>Group test_group - Pagure</title>", output_text
  322. )
  323. self.assertIn(
  324. "</i> No user invalid found to give this group to</div>",
  325. output_text,
  326. )
  327. self.assertIn(
  328. 'administered by <a href="/user/pingou">pingou</a>',
  329. output_text,
  330. )
  331. # Working
  332. data["username"] = "foo"
  333. output = self.app.post(
  334. "/group/test_group/give", data=data, follow_redirects=True
  335. )
  336. self.assertEqual(output.status_code, 200)
  337. output_text = output.get_data(as_text=True)
  338. self.assertIn(
  339. "<title>Group test_group - Pagure</title>", output_text
  340. )
  341. self.assertIn("</i> Group given</div>", output_text)
  342. self.assertIn(
  343. 'administered by <a href="/user/foo">foo</a>', output_text
  344. )
  345. def test_group_delete(self):
  346. """Test the group_delete endpoint."""
  347. output = self.app.post("/group/foo/delete")
  348. self.assertEqual(output.status_code, 302)
  349. user = tests.FakeUser()
  350. with tests.user_set(self.app.application, user):
  351. output = self.app.post("/group/foo/delete", follow_redirects=True)
  352. self.assertEqual(output.status_code, 200)
  353. self.assertIn(
  354. "<p>No groups have been created on this pagure instance "
  355. "yet</p>",
  356. output.get_data(as_text=True),
  357. )
  358. self.assertIn(
  359. '<h3 class="font-weight-bold">\n'
  360. ' Groups <span class="badge badge-secondary">0</span>',
  361. output.get_data(as_text=True),
  362. )
  363. self.test_add_group()
  364. with tests.user_set(self.app.application, user):
  365. output = self.app.post("/group/foo/delete", follow_redirects=True)
  366. self.assertEqual(output.status_code, 200)
  367. self.assertIn(
  368. '<h3 class="font-weight-bold">\n'
  369. ' Groups <span class="badge badge-secondary">1</span>',
  370. output.get_data(as_text=True),
  371. )
  372. output = self.app.get("/new/")
  373. csrf_token = (
  374. output.get_data(as_text=True)
  375. .split('name="csrf_token" type="hidden" value="')[1]
  376. .split('">')[0]
  377. )
  378. user.username = "foo"
  379. with tests.user_set(self.app.application, user):
  380. data = {"csrf_token": csrf_token}
  381. output = self.app.post(
  382. "/group/bar/delete", data=data, follow_redirects=True
  383. )
  384. self.assertEqual(output.status_code, 200)
  385. self.assertIn(
  386. "No group `bar` found", output.get_data(as_text=True)
  387. )
  388. self.assertIn(
  389. '<h3 class="font-weight-bold">\n'
  390. ' Groups <span class="badge badge-secondary">1</span>',
  391. output.get_data(as_text=True),
  392. )
  393. output = self.app.post(
  394. "/group/test_group/delete", data=data, follow_redirects=True
  395. )
  396. self.assertEqual(output.status_code, 200)
  397. self.assertIn(
  398. "You are not allowed to " "delete the group test_group",
  399. output.get_data(as_text=True),
  400. )
  401. self.assertIn(
  402. '<h3 class="font-weight-bold">\n'
  403. ' Groups <span class="badge badge-secondary">1</span>',
  404. output.get_data(as_text=True),
  405. )
  406. user.username = "bar"
  407. with tests.user_set(self.app.application, user):
  408. output = self.app.post(
  409. "/group/test_group/delete", data=data, follow_redirects=True
  410. )
  411. self.assertEqual(output.status_code, 404)
  412. user.username = "pingou"
  413. with tests.user_set(self.app.application, user):
  414. output = self.app.post(
  415. "/group/test_group/delete", data=data, follow_redirects=True
  416. )
  417. self.assertEqual(output.status_code, 200)
  418. self.assertIn(
  419. "Group `test_group` has " "been deleted",
  420. output.get_data(as_text=True),
  421. )
  422. self.assertIn(
  423. '<h3 class="font-weight-bold">\n'
  424. ' Groups <span class="badge badge-secondary">0</span>',
  425. output.get_data(as_text=True),
  426. )
  427. def test_view_group(self):
  428. """Test the view_group endpoint."""
  429. output = self.app.get("/group/foo")
  430. self.assertEqual(output.status_code, 404)
  431. self.test_add_group()
  432. user = tests.FakeUser()
  433. with tests.user_set(self.app.application, user):
  434. output = self.app.get("/group/test_group")
  435. self.assertEqual(output.status_code, 200)
  436. self.assertIn(
  437. '<h3 class="mb-0 font-weight-bold">Test Group</h3>',
  438. output.get_data(as_text=True),
  439. )
  440. output = self.app.get("/group/test_admin_group")
  441. self.assertEqual(output.status_code, 404)
  442. user = tests.FakeUser(
  443. username="pingou", groups=pagure.config.config["ADMIN_GROUP"]
  444. )
  445. with tests.user_set(self.app.application, user):
  446. # Admin can see group of type admins
  447. output = self.app.get("/group/test_admin_group")
  448. self.assertEqual(output.status_code, 200)
  449. self.assertIn(
  450. '<h3 class="mb-0 font-weight-bold">Test Admin Group</h3>',
  451. output.get_data(as_text=True),
  452. )
  453. self.assertEqual(
  454. output.get_data(as_text=True).count('<a href="/user/'), 2
  455. )
  456. csrf_token = (
  457. output.get_data(as_text=True)
  458. .split('name="csrf_token" type="hidden" value="')[1]
  459. .split('">')[0]
  460. )
  461. # No CSRF
  462. data = {"user": "bar"}
  463. output = self.app.post("/group/test_admin_group", data=data)
  464. self.assertEqual(output.status_code, 200)
  465. self.assertIn(
  466. '<h3 class="mb-0 font-weight-bold">Test Admin Group</h3>',
  467. output.get_data(as_text=True),
  468. )
  469. self.assertEqual(
  470. output.get_data(as_text=True).count('<a href="/user/'), 2
  471. )
  472. # Invalid user
  473. data = {"user": "bar", "csrf_token": csrf_token}
  474. output = self.app.post(
  475. "/group/test_admin_group", data=data, follow_redirects=True
  476. )
  477. self.assertEqual(output.status_code, 200)
  478. self.assertIn("No user `bar` found", output.get_data(as_text=True))
  479. self.assertIn(
  480. '<h3 class="mb-0 font-weight-bold">Test Admin Group</h3>',
  481. output.get_data(as_text=True),
  482. )
  483. self.assertEqual(
  484. output.get_data(as_text=True).count('<a href="/user/'), 2
  485. )
  486. # All good
  487. data = {"user": "foo", "csrf_token": csrf_token}
  488. output = self.app.post("/group/test_admin_group", data=data)
  489. self.assertEqual(output.status_code, 200)
  490. self.assertIn(
  491. "User `foo` added to the " "group `test_admin_group`.",
  492. output.get_data(as_text=True),
  493. )
  494. self.assertIn(
  495. '<h3 class="mb-0 font-weight-bold">Test Admin Group</h3>',
  496. output.get_data(as_text=True),
  497. )
  498. self.assertEqual(
  499. output.get_data(as_text=True).count('<a href="/user/'), 3
  500. )
  501. def test_group_user_delete(self):
  502. """Test the group_user_delete endpoint."""
  503. output = self.app.post("/group/foo/bar/delete")
  504. self.assertEqual(output.status_code, 302)
  505. user = tests.FakeUser()
  506. with tests.user_set(self.app.application, user):
  507. output = self.app.post(
  508. "/group/foo/bar/delete", follow_redirects=True
  509. )
  510. self.assertEqual(output.status_code, 404)
  511. self.test_add_group()
  512. user = tests.FakeUser()
  513. with tests.user_set(self.app.application, user):
  514. output = self.app.post(
  515. "/group/test_group/bar/delete", follow_redirects=True
  516. )
  517. self.assertEqual(output.status_code, 200)
  518. self.assertIn(
  519. '<h3 class="mb-0 font-weight-bold">Test Group</h3>',
  520. output.get_data(as_text=True),
  521. )
  522. self.assertEqual(
  523. output.get_data(as_text=True).count('<a href="/user/'), 2
  524. )
  525. output = self.app.get("/new/")
  526. csrf_token = (
  527. output.get_data(as_text=True)
  528. .split('name="csrf_token" type="hidden" value="')[1]
  529. .split('">')[0]
  530. )
  531. data = {"csrf_token": csrf_token}
  532. output = self.app.post(
  533. "/group/test_group/bar/delete",
  534. data=data,
  535. follow_redirects=True,
  536. )
  537. self.assertEqual(output.status_code, 200)
  538. self.assertIn("No user `bar` found", output.get_data(as_text=True))
  539. self.assertIn(
  540. '<h3 class="mb-0 font-weight-bold">Test Group</h3>',
  541. output.get_data(as_text=True),
  542. )
  543. self.assertEqual(
  544. output.get_data(as_text=True).count('<a href="/user/'), 2
  545. )
  546. output = self.app.post(
  547. "/group/test_group/foo/delete",
  548. data=data,
  549. follow_redirects=True,
  550. )
  551. self.assertEqual(output.status_code, 200)
  552. self.assertIn(
  553. "Could not find user " "username",
  554. output.get_data(as_text=True),
  555. )
  556. self.assertIn(
  557. '<h3 class="mb-0 font-weight-bold">Test Group</h3>',
  558. output.get_data(as_text=True),
  559. )
  560. self.assertEqual(
  561. output.get_data(as_text=True).count('<a href="/user/'), 2
  562. )
  563. user.username = "pingou"
  564. with tests.user_set(self.app.application, user):
  565. # User not in the group
  566. output = self.app.post(
  567. "/group/test_group/foo/delete",
  568. data=data,
  569. follow_redirects=True,
  570. )
  571. self.assertEqual(output.status_code, 200)
  572. self.assertIn(
  573. "User `foo` could not be " "found in the group `test_group`",
  574. output.get_data(as_text=True),
  575. )
  576. self.assertIn(
  577. '<h3 class="mb-0 font-weight-bold">Test Group</h3>',
  578. output.get_data(as_text=True),
  579. )
  580. self.assertEqual(
  581. output.get_data(as_text=True).count('<a href="/user/'), 2
  582. )
  583. # Cannot delete creator
  584. output = self.app.post(
  585. "/group/test_group/foo/delete",
  586. data=data,
  587. follow_redirects=True,
  588. )
  589. self.assertEqual(output.status_code, 200)
  590. self.assertIn(
  591. "User `foo` could not be " "found in the group `test_group`",
  592. output.get_data(as_text=True),
  593. )
  594. self.assertIn(
  595. '<h3 class="mb-0 font-weight-bold">Test Group</h3>',
  596. output.get_data(as_text=True),
  597. )
  598. self.assertEqual(
  599. output.get_data(as_text=True).count('<a href="/user/'), 2
  600. )
  601. # Add user foo
  602. data = {"user": "foo", "csrf_token": csrf_token}
  603. output = self.app.post("/group/test_group", data=data)
  604. self.assertEqual(output.status_code, 200)
  605. self.assertIn(
  606. "User `foo` added to the " "group `test_group`.",
  607. output.get_data(as_text=True),
  608. )
  609. self.assertIn(
  610. '<h3 class="mb-0 font-weight-bold">Test Group</h3>',
  611. output.get_data(as_text=True),
  612. )
  613. self.assertEqual(
  614. output.get_data(as_text=True).count('<a href="/user/'), 3
  615. )
  616. output = self.app.post(
  617. "/group/test_group/foo/delete",
  618. data=data,
  619. follow_redirects=True,
  620. )
  621. self.assertEqual(output.status_code, 200)
  622. self.assertIn(
  623. "User `foo` removed from " "the group `test_group`",
  624. output.get_data(as_text=True),
  625. )
  626. self.assertIn(
  627. '<h3 class="mb-0 font-weight-bold">Test Group</h3>',
  628. output.get_data(as_text=True),
  629. )
  630. self.assertEqual(
  631. output.get_data(as_text=True).count('<a href="/user/'), 2
  632. )
  633. if __name__ == "__main__":
  634. unittest.main(verbosity=2)