test_pagure_flask_ui_roadmap.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  1. # -*- coding: utf-8 -*-
  2. """
  3. (c) 2016-2018 - Copyright Red Hat Inc
  4. Authors:
  5. Pierre-Yves Chibon <pingou@pingoured.fr>
  6. """
  7. from __future__ import unicode_literals, absolute_import
  8. import datetime
  9. import json
  10. import unittest
  11. import shutil
  12. import sys
  13. import tempfile
  14. import os
  15. import pygit2
  16. from mock import patch
  17. sys.path.insert(
  18. 0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")
  19. )
  20. import pagure.lib.query
  21. import tests
  22. from pagure.lib.repo import PagureRepo
  23. class PagureFlaskRoadmaptests(tests.Modeltests):
  24. """Tests for the pagure's roadmap"""
  25. @patch("pagure.lib.git.update_git")
  26. @patch("pagure.lib.notify.send_email")
  27. def test_ticket_with_no_roadmap(self, p_send_email, p_ugt):
  28. """Test creating a ticket without roadmap."""
  29. p_send_email.return_value = True
  30. p_ugt.return_value = True
  31. tests.create_projects(self.session)
  32. tests.create_projects_git(os.path.join(self.path, "repos"), bare=True)
  33. user = tests.FakeUser()
  34. user.username = "pingou"
  35. with tests.user_set(self.app.application, user):
  36. # Get the CSRF token
  37. output = self.app.get("/test/new_issue")
  38. self.assertEqual(output.status_code, 200)
  39. output_text = output.get_data(as_text=True)
  40. self.assertTrue(
  41. '<h4 class="font-weight-bold mb-4">New Issue</h4>\n'
  42. in output_text
  43. )
  44. csrf_token = output_text.split(
  45. 'name="csrf_token" type="hidden" value="'
  46. )[1].split('">')[0]
  47. data = {
  48. "title": "Test issue",
  49. "issue_content": "We really should improve on this issue",
  50. "status": "Open",
  51. "csrf_token": csrf_token,
  52. }
  53. # Create the issue
  54. output = self.app.post(
  55. "/test/new_issue", data=data, follow_redirects=True
  56. )
  57. self.assertEqual(output.status_code, 200)
  58. output_text = output.get_data(as_text=True)
  59. self.assertIn(
  60. "<title>Issue #1: Test issue - test - Pagure</title>",
  61. output_text,
  62. )
  63. self.assertIn(
  64. '<a class="btn btn-outline-secondary btn-sm border-0" '
  65. 'href="/test/issue/1/edit" title="Edit this issue">',
  66. output_text,
  67. )
  68. @patch("pagure.lib.git.update_git")
  69. @patch("pagure.lib.notify.send_email")
  70. def test_ticket_with_roadmap(self, p_send_email, p_ugt):
  71. """Test creating a ticket with roadmap."""
  72. p_send_email.return_value = True
  73. p_ugt.return_value = True
  74. tests.create_projects(self.session)
  75. tests.create_projects_git(os.path.join(self.path, "repos"), bare=True)
  76. # Set some milestone
  77. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  78. repo.milestone = {"v1.0": "", "v2.0": "Tomorrow!"}
  79. self.session.add(repo)
  80. self.session.commit()
  81. user = tests.FakeUser()
  82. user.username = "pingou"
  83. with tests.user_set(self.app.application, user):
  84. # Get the CSRF token
  85. output = self.app.get("/test/new_issue")
  86. self.assertEqual(output.status_code, 200)
  87. output_text = output.get_data(as_text=True)
  88. self.assertTrue(
  89. '<h4 class="font-weight-bold mb-4">New Issue</h4>\n'
  90. in output_text
  91. )
  92. csrf_token = output_text.split(
  93. 'name="csrf_token" type="hidden" value="'
  94. )[1].split('">')[0]
  95. data = {
  96. "title": "Test issue",
  97. "issue_content": "We really should improve on this issue",
  98. "status": "Open",
  99. "csrf_token": csrf_token,
  100. }
  101. # Create the issue
  102. output = self.app.post(
  103. "/test/new_issue", data=data, follow_redirects=True
  104. )
  105. self.assertEqual(output.status_code, 200)
  106. output_text = output.get_data(as_text=True)
  107. self.assertIn(
  108. "<title>Issue #1: Test issue - test - Pagure</title>",
  109. output_text,
  110. )
  111. self.assertIn(
  112. '<a class="btn btn-outline-secondary btn-sm border-0" '
  113. 'href="/test/issue/1/edit" title="Edit this issue">',
  114. output_text,
  115. )
  116. # Mark the ticket for the roadmap
  117. data = {"tag": "roadmap", "csrf_token": csrf_token}
  118. output = self.app.post(
  119. "/test/issue/1/update", data=data, follow_redirects=True
  120. )
  121. self.assertEqual(output.status_code, 200)
  122. output_text = output.get_data(as_text=True)
  123. self.assertIn(
  124. "<title>Issue #1: Test issue - test - Pagure</title>",
  125. output_text,
  126. )
  127. self.assertIn(
  128. '<a class="btn btn-outline-secondary btn-sm border-0" '
  129. 'href="/test/issue/1/edit" title="Edit this issue">',
  130. output_text,
  131. )
  132. def test_update_milestones(self):
  133. """Test updating milestones of a repo."""
  134. tests.create_projects(self.session)
  135. tests.create_projects_git(os.path.join(self.path, "repos"), bare=True)
  136. # Set some milestones
  137. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  138. self.assertEqual(repo.milestones, {})
  139. user = tests.FakeUser()
  140. user.username = "pingou"
  141. with tests.user_set(self.app.application, user):
  142. # Get the CSRF token
  143. output = self.app.get("/test/settings")
  144. self.assertEqual(output.status_code, 200)
  145. output_text = output.get_data(as_text=True)
  146. self.assertIn(
  147. "<title>Settings - test - Pagure</title>", output_text
  148. )
  149. self.assertIn(
  150. '<h5 class="pl-2 font-weight-bold text-muted">'
  151. "Project Settings</h5>\n",
  152. output_text,
  153. )
  154. csrf_token = output_text.split(
  155. 'name="csrf_token" type="hidden" value="'
  156. )[1].split('">')[0]
  157. data = {
  158. "milestones": 1,
  159. "milestone_1_name": "1",
  160. "milestone_1_date": "Tomorrow",
  161. }
  162. output = self.app.post(
  163. "/test/update/milestones", data=data, follow_redirects=True
  164. )
  165. self.assertEqual(output.status_code, 200)
  166. output_text = output.get_data(as_text=True)
  167. # Check the redirect
  168. self.assertIn(
  169. "<title>Settings - test - Pagure</title>", output_text
  170. )
  171. self.assertIn(
  172. '<h5 class="pl-2 font-weight-bold text-muted">'
  173. "Project Settings</h5>\n",
  174. output_text,
  175. )
  176. # Check the result of the action -- None, no CSRF
  177. repo = pagure.lib.query.get_authorized_project(
  178. self.session, "test"
  179. )
  180. self.assertEqual(repo.milestones, {})
  181. data = {
  182. "milestones": 1,
  183. "milestone_1_name": "1",
  184. "milestone_date": "",
  185. "csrf_token": csrf_token,
  186. }
  187. output = self.app.post(
  188. "/test/update/milestones", data=data, follow_redirects=True
  189. )
  190. self.assertEqual(output.status_code, 200)
  191. output_text = output.get_data(as_text=True)
  192. # Check the redirect
  193. self.assertIn(
  194. "<title>Settings - test - Pagure</title>", output_text
  195. )
  196. self.assertIn(
  197. '<h5 class="pl-2 font-weight-bold text-muted">'
  198. "Project Settings</h5>\n",
  199. output_text,
  200. )
  201. self.assertIn("Milestones updated", output_text)
  202. # Check the result of the action -- Milestones recorded
  203. self.session.commit()
  204. repo = pagure.lib.query.get_authorized_project(
  205. self.session, "test"
  206. )
  207. self.assertEqual(
  208. repo.milestones, {"1": {"active": False, "date": None}}
  209. )
  210. data = {
  211. "milestones": [1, 2],
  212. "milestone_1_name": "v1.0",
  213. "milestone_2_name": "v2.0",
  214. "milestone_1_date": "Tomorrow",
  215. "milestone_2_date": "",
  216. "csrf_token": csrf_token,
  217. }
  218. output = self.app.post(
  219. "/test/update/milestones", data=data, follow_redirects=True
  220. )
  221. self.assertEqual(output.status_code, 200)
  222. output_text = output.get_data(as_text=True)
  223. # Check the redirect
  224. self.assertIn(
  225. "<title>Settings - test - Pagure</title>", output_text
  226. )
  227. self.assertIn(
  228. '<h5 class="pl-2 font-weight-bold text-muted">'
  229. "Project Settings</h5>\n",
  230. output_text,
  231. )
  232. self.assertIn("Milestones updated", output_text)
  233. # Check the result of the action -- Milestones recorded
  234. self.session.commit()
  235. repo = pagure.lib.query.get_authorized_project(
  236. self.session, "test"
  237. )
  238. self.assertEqual(
  239. repo.milestones,
  240. {
  241. "v1.0": {"active": False, "date": "Tomorrow"},
  242. "v2.0": {"active": False, "date": None},
  243. },
  244. )
  245. # Check error - less milestones than dates
  246. data = {
  247. "milestones": [1, 2],
  248. "milestone_1_name": "v1.0",
  249. "milestone_2_name": "v2.0",
  250. "milestone_1_date": "Tomorrow",
  251. "milestone_2_date": "Next week",
  252. "milestone_3_date": "Next Year",
  253. "csrf_token": csrf_token,
  254. }
  255. output = self.app.post(
  256. "/test/update/milestones", data=data, follow_redirects=True
  257. )
  258. self.assertEqual(output.status_code, 200)
  259. output_text = output.get_data(as_text=True)
  260. # Check the redirect
  261. self.assertIn(
  262. "<title>Settings - test - Pagure</title>", output_text
  263. )
  264. self.assertIn(
  265. '<h5 class="pl-2 font-weight-bold text-muted">'
  266. "Project Settings</h5>\n",
  267. output_text,
  268. )
  269. # Check the result of the action -- Milestones un-changed
  270. self.session.commit()
  271. repo = pagure.lib.query.get_authorized_project(
  272. self.session, "test"
  273. )
  274. self.assertEqual(
  275. repo.milestones,
  276. {
  277. "v1.0": {"active": False, "date": "Tomorrow"},
  278. "v2.0": {"active": False, "date": "Next week"},
  279. },
  280. )
  281. # Check error - Twice the same milestone
  282. data = {
  283. "milestones": [1, 2, 3],
  284. "milestone_1_name": "v1.0",
  285. "milestone_2_name": "v2.0",
  286. "milestone_3_name": "v2.0",
  287. "milestone_1_date": "Tomorrow",
  288. "milestone_2_date": "Next week",
  289. "milestone_3_date": "Next Year",
  290. "csrf_token": csrf_token,
  291. }
  292. output = self.app.post(
  293. "/test/update/milestones", data=data, follow_redirects=True
  294. )
  295. self.assertEqual(output.status_code, 200)
  296. output_text = output.get_data(as_text=True)
  297. # Check the redirect
  298. self.assertIn(
  299. "<title>Settings - test - Pagure</title>", output_text
  300. )
  301. self.assertIn(
  302. '<h5 class="pl-2 font-weight-bold text-muted">'
  303. "Project Settings</h5>\n",
  304. output_text,
  305. )
  306. self.assertIn(
  307. "Milestone v2.0 is present multiple times",
  308. "Milestone v2.0 is present multiple times",
  309. output_text,
  310. )
  311. # Check the result of the action -- Milestones un-changed
  312. self.session.commit()
  313. repo = pagure.lib.query.get_authorized_project(
  314. self.session, "test"
  315. )
  316. self.assertEqual(
  317. repo.milestones,
  318. {
  319. "v1.0": {"active": False, "date": "Tomorrow"},
  320. "v2.0": {"active": False, "date": "Next week"},
  321. },
  322. )
  323. # Check error - Twice the same date
  324. data = {
  325. "milestones": [1, 2, 3],
  326. "milestone_1_name": "v1.0",
  327. "milestone_2_name": "v2.0",
  328. "milestone_3_name": "v3.0",
  329. "milestone_1_date": "Tomorrow",
  330. "milestone_2_date": "Next week",
  331. "milestone_3_date": "Next week",
  332. "csrf_token": csrf_token,
  333. }
  334. output = self.app.post(
  335. "/test/update/milestones", data=data, follow_redirects=True
  336. )
  337. self.assertEqual(output.status_code, 200)
  338. output_text = output.get_data(as_text=True)
  339. # Check the redirect
  340. self.assertIn(
  341. "<title>Settings - test - Pagure</title>", output_text
  342. )
  343. self.assertIn(
  344. '<h5 class="pl-2 font-weight-bold text-muted">'
  345. "Project Settings</h5>\n",
  346. output_text,
  347. )
  348. self.assertIn("Milestones updated", output_text)
  349. # Check the result of the action -- Milestones updated
  350. self.session.commit()
  351. repo = pagure.lib.query.get_authorized_project(
  352. self.session, "test"
  353. )
  354. self.assertEqual(
  355. repo.milestones,
  356. {
  357. "v1.0": {"active": False, "date": "Tomorrow"},
  358. "v2.0": {"active": False, "date": "Next week"},
  359. "v3.0": {"active": False, "date": "Next week"},
  360. },
  361. )
  362. # Check for an invalid project
  363. output = self.app.post("/foo/update/milestones", data=data)
  364. self.assertEqual(output.status_code, 404)
  365. # Check the behavior if the project disabled the issue tracker
  366. settings = repo.settings
  367. settings["issue_tracker"] = False
  368. repo.settings = settings
  369. self.session.add(repo)
  370. self.session.commit()
  371. output = self.app.post("/test/update/milestones", data=data)
  372. self.assertEqual(output.status_code, 404)
  373. # Check for a non-admin user
  374. settings = repo.settings
  375. settings["issue_tracker"] = True
  376. repo.settings = settings
  377. self.session.add(repo)
  378. self.session.commit()
  379. user.username = "ralph"
  380. with tests.user_set(self.app.application, user):
  381. output = self.app.post("/test/update/milestones", data=data)
  382. self.assertEqual(output.status_code, 403)
  383. @patch("pagure.lib.git.update_git")
  384. @patch("pagure.lib.notify.send_email")
  385. def test_milestones_without_dates(self, p_send_email, p_ugt):
  386. """Test creating two milestones with no dates."""
  387. tests.create_projects(self.session)
  388. tests.create_projects_git(os.path.join(self.path, "repos"), bare=True)
  389. user = tests.FakeUser()
  390. user.username = "pingou"
  391. with tests.user_set(self.app.application, user):
  392. # Get the CSRF token
  393. output = self.app.get("/test/settings")
  394. output_text = output.get_data(as_text=True)
  395. csrf_token = output_text.split(
  396. 'name="csrf_token" type="hidden" value="'
  397. )[1].split('">')[0]
  398. data = {
  399. "milestones": [1, 2],
  400. "milestone_1_name": "v1.0",
  401. "milestone_2_name": "v2.0",
  402. "milestone_1_date": "",
  403. "milestone_2_date": "",
  404. "csrf_token": csrf_token,
  405. }
  406. output = self.app.post(
  407. "/test/update/milestones", data=data, follow_redirects=True
  408. )
  409. self.assertEqual(output.status_code, 200)
  410. output_text = output.get_data(as_text=True)
  411. # Check the redirect
  412. self.assertIn(
  413. "<title>Settings - test - Pagure</title>", output_text
  414. )
  415. self.assertIn(
  416. '<h5 class="pl-2 font-weight-bold text-muted">'
  417. "Project Settings</h5>\n",
  418. output_text,
  419. )
  420. self.assertIn("Milestones updated", output_text)
  421. # Check the result of the action -- Milestones recorded
  422. self.session.commit()
  423. repo = pagure.lib.query.get_authorized_project(
  424. self.session, "test"
  425. )
  426. self.assertEqual(
  427. repo.milestones,
  428. {
  429. "v1.0": {"active": False, "date": None},
  430. "v2.0": {"active": False, "date": None},
  431. },
  432. )
  433. @patch("pagure.lib.git.update_git")
  434. @patch("pagure.lib.notify.send_email")
  435. def test_roadmap_ui(self, p_send_email, p_ugt):
  436. """Test viewing the roadmap of a repo."""
  437. p_send_email.return_value = True
  438. p_ugt.return_value = True
  439. self.test_update_milestones()
  440. user = tests.FakeUser()
  441. user.username = "pingou"
  442. with tests.user_set(self.app.application, user):
  443. # Get the CSRF token
  444. output = self.app.get("/test/new_issue")
  445. self.assertEqual(output.status_code, 200)
  446. output_text = output.get_data(as_text=True)
  447. self.assertTrue(
  448. '<h4 class="font-weight-bold mb-4">New Issue</h4>\n'
  449. in output_text
  450. )
  451. csrf_token = output_text.split(
  452. 'name="csrf_token" type="hidden" value="'
  453. )[1].split('">')[0]
  454. # Create an unplanned milestone
  455. data = {
  456. "milestones": [1, 2, 3],
  457. "milestone_1_name": "v1.0",
  458. "milestone_2_name": "v2.0",
  459. "milestone_3_name": "unplanned",
  460. "milestone_1_date": "Tomorrow",
  461. "milestone_2_date": "",
  462. "milestone_3_date": "",
  463. "milestone_1_active": True,
  464. "milestone_2_active": True,
  465. "milestone_3_active": True,
  466. "csrf_token": csrf_token,
  467. }
  468. output = self.app.post(
  469. "/test/update/milestones", data=data, follow_redirects=True
  470. )
  471. self.assertEqual(output.status_code, 200)
  472. output_text = output.get_data(as_text=True)
  473. # Check the redirect
  474. self.assertIn(
  475. "<title>Settings - test - Pagure</title>", output_text
  476. )
  477. self.assertIn(
  478. '<h5 class="pl-2 font-weight-bold text-muted">'
  479. "Project Settings</h5>\n",
  480. output_text,
  481. )
  482. self.assertIn("Milestones updated", output_text)
  483. # Check the result of the action -- Milestones recorded
  484. self.session.commit()
  485. repo = pagure.lib.query._get_project(self.session, "test")
  486. self.assertEqual(
  487. repo.milestones,
  488. {
  489. "unplanned": {"active": True, "date": None},
  490. "v1.0": {"active": True, "date": "Tomorrow"},
  491. "v2.0": {"active": True, "date": None},
  492. },
  493. )
  494. # Create the issues
  495. for cnt in range(6):
  496. cnt += 1
  497. data = {
  498. "title": "Test issue %s" % cnt,
  499. "issue_content": "We really should improve on this "
  500. "issue %s" % cnt,
  501. "csrf_token": csrf_token,
  502. }
  503. output = self.app.post(
  504. "/test/new_issue", data=data, follow_redirects=True
  505. )
  506. self.assertEqual(output.status_code, 200)
  507. output_text = output.get_data(as_text=True)
  508. self.assertIn(
  509. "<title>Issue #{0}: Test issue {0} - test - "
  510. "Pagure</title>".format(cnt),
  511. output_text,
  512. )
  513. self.assertIn(
  514. '<a class="btn btn-outline-secondary btn-sm border-0" '
  515. 'href="/test/issue/%s/edit" title="Edit this issue">'
  516. % cnt,
  517. output_text,
  518. )
  519. # Mark the ticket for the roadmap
  520. mstone = "v%s.0" % cnt
  521. if cnt >= 3:
  522. if (cnt % 3) == 0:
  523. mstone = "unplanned"
  524. else:
  525. mstone = "v%s.0" % (cnt % 3)
  526. data = {"milestone": mstone, "csrf_token": csrf_token}
  527. output = self.app.post(
  528. "/test/issue/%s/update" % cnt,
  529. data=data,
  530. follow_redirects=True,
  531. )
  532. self.assertEqual(output.status_code, 200)
  533. output_text = output.get_data(as_text=True)
  534. self.assertIn(
  535. "<title>Issue #{0}: Test issue {0} - test - "
  536. "Pagure</title>".format(cnt),
  537. output_text,
  538. )
  539. self.assertIn(
  540. '<a class="btn btn-outline-secondary btn-sm border-0" '
  541. 'href="/test/issue/%s/edit" title="Edit this issue">'
  542. % cnt,
  543. output_text,
  544. )
  545. self.assertIn(
  546. "Issue set to the milestone: %s" % mstone, output_text
  547. )
  548. self.assertIn(
  549. '<div class="ml-2" id="milestone_plain">', output_text
  550. )
  551. self.assertIn(
  552. '<a href="/test/roadmap/%s/">' % mstone, output_text
  553. )
  554. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  555. # Mark ticket #1 as Fixed
  556. for iid in [1, 4]:
  557. ticket = pagure.lib.query.search_issues(
  558. self.session, repo, issueid=iid
  559. )
  560. ticket.status = "Closed"
  561. ticket.close_status = "Fixed"
  562. self.session.add(ticket)
  563. self.session.commit()
  564. # test the roadmap view
  565. output = self.app.get("/test/roadmap")
  566. self.assertEqual(output.status_code, 200)
  567. output_text = output.get_data(as_text=True)
  568. self.assertIn(
  569. '<span class="fa fa-fw fa-map-signs"></span>\n'
  570. ' <span class="font'
  571. '-weight-bold">v1.0</span>',
  572. output_text,
  573. )
  574. self.assertIn(
  575. '<span class="fa fa-fw fa-map-signs"></span>\n'
  576. ' <span class="font'
  577. '-weight-bold">unplanned</span>',
  578. output_text,
  579. )
  580. self.assertIn(
  581. 'title="100% Completed | 2 Closed Issues | 0 Open Issues"\n',
  582. output_text,
  583. )
  584. self.assertIn(
  585. 'title="0% Completed | 0 Closed Issues | 2 Open Issues"\n',
  586. output_text,
  587. )
  588. self.assertIn(
  589. 'title="0% Completed | 0 Closed Issues | 2 Open Issues"\n',
  590. output_text,
  591. )
  592. # test the roadmap view for a specific milestone
  593. output = self.app.get("/test/roadmap/v2.0/")
  594. self.assertEqual(output.status_code, 200)
  595. output_text = output.get_data(as_text=True)
  596. self.assertIn(
  597. '<span class="fa fa-fw fa-exclamation-circle"></span> 2 Open\n',
  598. output_text,
  599. )
  600. self.assertIn(
  601. '<span class="fa fa-fw fa-exclamation-circle"></span> 0 Closed\n',
  602. output_text,
  603. )
  604. self.assertIn('<a class="notblue" href="/test/issue/2">', output_text)
  605. self.assertEqual(
  606. output_text.count('<a class="notblue" href="/test/issue/2">'), 1
  607. )
  608. # test the roadmap view for errors
  609. output = self.app.get("/foo/roadmap")
  610. self.assertEqual(output.status_code, 404)
  611. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  612. settings = repo.settings
  613. settings["issue_tracker"] = False
  614. repo.settings = settings
  615. self.session.add(repo)
  616. self.session.commit()
  617. output = self.app.get("/test/roadmap", data=data)
  618. self.assertEqual(output.status_code, 404)
  619. if __name__ == "__main__":
  620. unittest.main(verbosity=2)