test_pagure_flask_api_boards.py 54 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664
  1. # -*- coding: utf-8 -*-
  2. """
  3. (c) 2015 - 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 pygit2
  14. from mock import patch, MagicMock
  15. sys.path.insert(
  16. 0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")
  17. )
  18. import pagure.api
  19. import pagure.flask_app
  20. import pagure.lib.query
  21. import tests
  22. def set_projects_up(self):
  23. tests.create_projects(self.session)
  24. tests.create_projects_git(os.path.join(self.path, "repos"), bare=True)
  25. tests.add_content_git_repo(os.path.join(self.path, "repos", "test.git"))
  26. tests.create_tokens(self.session)
  27. tests.create_tokens_acl(self.session)
  28. tag = pagure.lib.model.TagColored(
  29. tag="dev", tag_color="DeepBlueSky", project_id=1
  30. )
  31. self.session.add(tag)
  32. tag = pagure.lib.model.TagColored(
  33. tag="infra", tag_color="DeepGreen", project_id=1
  34. )
  35. self.session.add(tag)
  36. self.session.commit()
  37. def set_up_board(self):
  38. headers = {
  39. "Authorization": "token aaabbbcccddd",
  40. "Content-Type": "application/json",
  41. }
  42. data = json.dumps({"dev": {"active": True, "tag": "dev"}})
  43. output = self.app.post("/api/0/test/boards", headers=headers, data=data)
  44. self.assertEqual(output.status_code, 200)
  45. data = json.loads(output.get_data(as_text=True))
  46. self.assertDictEqual(
  47. data,
  48. {
  49. "boards": [
  50. {
  51. "active": True,
  52. "full_url": "http://localhost.localdomain/test/boards/dev",
  53. "name": "dev",
  54. "status": [],
  55. "tag": {
  56. "tag": "dev",
  57. "tag_color": "DeepBlueSky",
  58. "tag_description": "",
  59. },
  60. }
  61. ]
  62. },
  63. )
  64. class PagureFlaskApiBoardstests(tests.SimplePagureTest):
  65. """Tests for flask API Boards controller of pagure"""
  66. maxDiff = None
  67. def setUp(self):
  68. super(PagureFlaskApiBoardstests, self).setUp()
  69. set_projects_up(self)
  70. def test_api_boards_view_no_project(self):
  71. output = self.app.get("/api/0/invalid/boards")
  72. self.assertEqual(output.status_code, 404)
  73. data = json.loads(output.get_data(as_text=True))
  74. self.assertDictEqual(
  75. data, {"error": "Project not found", "error_code": "ENOPROJECT"}
  76. )
  77. def test_api_boards_view_empty(self):
  78. output = self.app.get("/api/0/test/boards")
  79. self.assertEqual(output.status_code, 200)
  80. data = json.loads(output.get_data(as_text=True))
  81. self.assertDictEqual(data, {"boards": [], "total_requests": 0})
  82. def test_api_board_create_no_token(self):
  83. headers = {}
  84. data = {}
  85. output = self.app.post(
  86. "/api/0/test/boards", headers=headers, data=data
  87. )
  88. self.assertEqual(output.status_code, 401)
  89. data = json.loads(output.get_data(as_text=True))
  90. self.assertDictEqual(
  91. data,
  92. {
  93. "error": "Invalid or expired token. Please visit "
  94. "http://localhost.localdomain/settings#nav-api-tab to get or renew "
  95. "your API token.",
  96. "error_code": "EINVALIDTOK",
  97. "errors": "Invalid token",
  98. },
  99. )
  100. def test_api_board_create_expired_token(self):
  101. headers = {"Authorization": "token expired_token"}
  102. data = {}
  103. output = self.app.post(
  104. "/api/0/test/boards", headers=headers, data=data
  105. )
  106. self.assertEqual(output.status_code, 401)
  107. data = json.loads(output.get_data(as_text=True))
  108. self.assertDictEqual(
  109. data,
  110. {
  111. "error": "Invalid or expired token. Please visit "
  112. "http://localhost.localdomain/settings#nav-api-tab to get or renew "
  113. "your API token.",
  114. "error_code": "EINVALIDTOK",
  115. "errors": "Expired token",
  116. },
  117. )
  118. def test_api_board_create_invalid_token_project(self):
  119. headers = {"Authorization": "token aaabbbcccddd"}
  120. data = {}
  121. output = self.app.post(
  122. "/api/0/test2/boards", headers=headers, data=data
  123. )
  124. self.assertEqual(output.status_code, 401)
  125. data = json.loads(output.get_data(as_text=True))
  126. self.assertDictEqual(
  127. data,
  128. {
  129. "error": "Invalid or expired token. Please visit "
  130. "http://localhost.localdomain/settings#nav-api-tab to get or renew "
  131. "your API token.",
  132. "error_code": "EINVALIDTOK",
  133. },
  134. )
  135. def test_api_board_create_no_data(self):
  136. headers = {
  137. "Authorization": "token aaabbbcccddd",
  138. }
  139. data = {}
  140. output = self.app.post(
  141. "/api/0/test/boards", headers=headers, data=data
  142. )
  143. self.assertEqual(output.status_code, 400)
  144. data = json.loads(output.get_data(as_text=True))
  145. self.assertDictEqual(
  146. data,
  147. {
  148. "error": "Invalid or incomplete input submitted",
  149. "error_code": "EINVALIDREQ",
  150. "errors": "No (JSON) data provided",
  151. },
  152. )
  153. def test_api_board_create_no_contenttype(self):
  154. headers = {"Authorization": "token aaabbbcccddd"}
  155. data = {"name": "board", "active": True, "Tag": "not found"}
  156. output = self.app.post(
  157. "/api/0/test/boards", headers=headers, data=data
  158. )
  159. self.assertEqual(output.status_code, 400)
  160. data = json.loads(output.get_data(as_text=True))
  161. self.assertDictEqual(
  162. data,
  163. {
  164. "error": "Invalid or incomplete input submitted",
  165. "error_code": "EINVALIDREQ",
  166. "errors": "No (JSON) data provided",
  167. },
  168. )
  169. def test_api_board_create_html_data(self):
  170. headers = {
  171. "Authorization": "token aaabbbcccddd",
  172. "Content-Type": "application/json",
  173. }
  174. data = {"name": "board", "active": True, "tag": "not found"}
  175. output = self.app.post(
  176. "/api/0/test/boards", headers=headers, data=data
  177. )
  178. self.assertEqual(output.status_code, 400)
  179. self.assertIn(
  180. "The browser (or proxy) sent a request that this server could not understand.",
  181. output.get_data(as_text=True),
  182. )
  183. def test_api_board_create_invalid_json(self):
  184. headers = {
  185. "Authorization": "token aaabbbcccddd",
  186. "Content-Type": "application/json",
  187. }
  188. data = json.dumps(
  189. {"name": "board", "active": True, "tag": "not found"}
  190. )
  191. output = self.app.post(
  192. "/api/0/test/boards", headers=headers, data=data
  193. )
  194. self.assertEqual(output.status_code, 400)
  195. data = json.loads(output.get_data(as_text=True))
  196. self.assertDictEqual(
  197. data,
  198. {
  199. "error": "Invalid or incomplete input submitted",
  200. "error_code": "EINVALIDREQ",
  201. "errors": "No tag associated with at least one of the boards",
  202. },
  203. )
  204. def test_api_board_create_invalid_tag(self):
  205. headers = {
  206. "Authorization": "token aaabbbcccddd",
  207. "Content-Type": "application/json",
  208. }
  209. data = json.dumps({"dev": {"active": True, "tag": "not found"}})
  210. output = self.app.post(
  211. "/api/0/test/boards", headers=headers, data=data
  212. )
  213. self.assertEqual(output.status_code, 400)
  214. data = json.loads(output.get_data(as_text=True))
  215. self.assertDictEqual(
  216. data,
  217. {
  218. "error": "No tag found with the name not found",
  219. "error_code": "ENOCODE",
  220. },
  221. )
  222. def test_api_board_create(self):
  223. headers = {
  224. "Authorization": "token aaabbbcccddd",
  225. "Content-Type": "application/json",
  226. }
  227. data = json.dumps({"dev": {"active": True, "tag": "dev"}})
  228. output = self.app.post(
  229. "/api/0/test/boards", headers=headers, data=data
  230. )
  231. self.assertEqual(output.status_code, 200)
  232. data = json.loads(output.get_data(as_text=True))
  233. self.assertDictEqual(
  234. data,
  235. {
  236. "boards": [
  237. {
  238. "active": True,
  239. "full_url": "http://localhost.localdomain/test/boards/dev",
  240. "name": "dev",
  241. "status": [],
  242. "tag": {
  243. "tag": "dev",
  244. "tag_color": "DeepBlueSky",
  245. "tag_description": "",
  246. },
  247. }
  248. ]
  249. },
  250. )
  251. def test_api_board_edit_board_delete_board(self):
  252. headers = {
  253. "Authorization": "token aaabbbcccddd",
  254. "Content-Type": "application/json",
  255. }
  256. # Add 2 boards to the project
  257. data = json.dumps(
  258. {
  259. "dev": {"active": True, "tag": "dev"},
  260. "infra": {"active": True, "tag": "infra"},
  261. }
  262. )
  263. output = self.app.post(
  264. "/api/0/test/boards", headers=headers, data=data
  265. )
  266. self.assertEqual(output.status_code, 200)
  267. data = json.loads(output.get_data(as_text=True))
  268. self.assertDictEqual(
  269. data,
  270. {
  271. "boards": [
  272. {
  273. "active": True,
  274. "full_url": "http://localhost.localdomain/test/boards/dev",
  275. "name": "dev",
  276. "status": [],
  277. "tag": {
  278. "tag": "dev",
  279. "tag_color": "DeepBlueSky",
  280. "tag_description": "",
  281. },
  282. },
  283. {
  284. "active": True,
  285. "full_url": "http://localhost.localdomain/test/boards/infra",
  286. "name": "infra",
  287. "status": [],
  288. "tag": {
  289. "tag": "infra",
  290. "tag_color": "DeepGreen",
  291. "tag_description": "",
  292. },
  293. },
  294. ]
  295. },
  296. )
  297. # Remove one of the 2 boards
  298. data = json.dumps(
  299. {
  300. "dev": {"active": True, "tag": "dev"},
  301. }
  302. )
  303. output = self.app.post(
  304. "/api/0/test/boards", headers=headers, data=data
  305. )
  306. self.assertEqual(output.status_code, 200)
  307. data = json.loads(output.get_data(as_text=True))
  308. self.assertDictEqual(
  309. data,
  310. {
  311. "boards": [
  312. {
  313. "active": True,
  314. "full_url": "http://localhost.localdomain/test/boards/dev",
  315. "name": "dev",
  316. "status": [],
  317. "tag": {
  318. "tag": "dev",
  319. "tag_color": "DeepBlueSky",
  320. "tag_description": "",
  321. },
  322. }
  323. ]
  324. },
  325. )
  326. # Removing the last board doesn't work (no JSON data provided)
  327. data = json.dumps({})
  328. output = self.app.post(
  329. "/api/0/test/boards", headers=headers, data=data
  330. )
  331. self.assertEqual(output.status_code, 400)
  332. def test_api_board_delete_board(self):
  333. headers = {
  334. "Authorization": "token aaabbbcccddd",
  335. "Content-Type": "application/json",
  336. }
  337. data = json.dumps({"name": ["dev"]})
  338. output = self.app.post(
  339. "/api/0/test/boards/delete", headers=headers, data=data
  340. )
  341. self.assertEqual(output.status_code, 200)
  342. data = json.loads(output.get_data(as_text=True))
  343. self.assertDictEqual(data, {"boards": []})
  344. def test_api_board_api_board_status_no_board(self):
  345. headers = {
  346. "Authorization": "token aaabbbcccddd",
  347. "Content-Type": "application/json",
  348. }
  349. data = json.dumps({"foo": "bar"})
  350. output = self.app.post(
  351. "/api/0/test/boards/invalid/status", headers=headers, data=data
  352. )
  353. self.assertEqual(output.status_code, 404)
  354. data = json.loads(output.get_data(as_text=True))
  355. self.assertDictEqual(
  356. data,
  357. {
  358. "error": "Invalid or incomplete input submitted",
  359. "error_code": "EINVALIDREQ",
  360. "errors": "Board not found",
  361. },
  362. )
  363. class PagureFlaskApiBoardsWithBoardtests(tests.SimplePagureTest):
  364. """Tests for flask API Boards controller of pagure for the tests
  365. requiring a pre-existing board.
  366. """
  367. maxDiff = None
  368. def setUp(self):
  369. super(PagureFlaskApiBoardsWithBoardtests, self).setUp()
  370. set_projects_up(self)
  371. set_up_board(self)
  372. def test_api_board_edit_board(self):
  373. headers = {
  374. "Authorization": "token aaabbbcccddd",
  375. "Content-Type": "application/json",
  376. }
  377. # Make the board inactive
  378. data = json.dumps({"dev": {"active": False, "tag": "dev"}})
  379. output = self.app.post(
  380. "/api/0/test/boards", headers=headers, data=data
  381. )
  382. self.assertEqual(output.status_code, 200)
  383. data = json.loads(output.get_data(as_text=True))
  384. self.assertDictEqual(
  385. data,
  386. {
  387. "boards": [
  388. {
  389. "active": False,
  390. "full_url": "http://localhost.localdomain/test/boards/dev",
  391. "name": "dev",
  392. "status": [],
  393. "tag": {
  394. "tag": "dev",
  395. "tag_color": "DeepBlueSky",
  396. "tag_description": "",
  397. },
  398. }
  399. ]
  400. },
  401. )
  402. def test_api_board_edit_board_invalid_tag(self):
  403. headers = {
  404. "Authorization": "token aaabbbcccddd",
  405. "Content-Type": "application/json",
  406. }
  407. # Associate the existing board with an invalid tag
  408. data = json.dumps(
  409. {
  410. "dev": {"active": True, "tag": "invalid"},
  411. }
  412. )
  413. output = self.app.post(
  414. "/api/0/test/boards", headers=headers, data=data
  415. )
  416. self.assertEqual(output.status_code, 400)
  417. data = json.loads(output.get_data(as_text=True))
  418. self.assertDictEqual(
  419. data,
  420. {
  421. "error": "No tag found with the name invalid",
  422. "error_code": "ENOCODE",
  423. },
  424. )
  425. def test_api_board_delete_invalid_json_input(self):
  426. headers = {
  427. "Authorization": "token aaabbbcccddd",
  428. "Content-Type": "application/json",
  429. }
  430. # Remove this board
  431. data = json.dumps({})
  432. output = self.app.post(
  433. "/api/0/test/boards/delete", headers=headers, data=data
  434. )
  435. self.assertEqual(output.status_code, 400)
  436. data = json.loads(output.get_data(as_text=True))
  437. self.assertDictEqual(
  438. data,
  439. {
  440. "error": "Invalid or incomplete input submitted",
  441. "error_code": "EINVALIDREQ",
  442. "errors": {"name": ["This field is required"]},
  443. },
  444. )
  445. def test_api_board_delete_invalid_html_input(self):
  446. headers = {
  447. "Authorization": "token aaabbbcccddd",
  448. }
  449. # Remove the board
  450. data = {}
  451. output = self.app.post(
  452. "/api/0/test/boards/delete", headers=headers, data=data
  453. )
  454. self.assertEqual(output.status_code, 400)
  455. data = json.loads(output.get_data(as_text=True))
  456. self.assertDictEqual(
  457. data,
  458. {
  459. "error": "Invalid or incomplete input submitted",
  460. "error_code": "EINVALIDREQ",
  461. "errors": {"name": ["This field is required"]},
  462. },
  463. )
  464. def test_api_board_delete_json_input(self):
  465. headers = {
  466. "Authorization": "token aaabbbcccddd",
  467. "Content-Type": "application/json",
  468. }
  469. # Remove the board
  470. data = json.dumps({"name": ["dev"]})
  471. output = self.app.post(
  472. "/api/0/test/boards/delete", headers=headers, data=data
  473. )
  474. self.assertEqual(output.status_code, 200)
  475. data = json.loads(output.get_data(as_text=True))
  476. self.assertDictEqual(
  477. data,
  478. {"boards": []},
  479. )
  480. def test_api_board_delete_html_input(self):
  481. headers = {
  482. "Authorization": "token aaabbbcccddd",
  483. }
  484. # Remove the board
  485. data = {"name": ["dev"]}
  486. output = self.app.post(
  487. "/api/0/test/boards/delete", headers=headers, data=data
  488. )
  489. self.assertEqual(output.status_code, 200)
  490. data = json.loads(output.get_data(as_text=True))
  491. self.assertDictEqual(
  492. data,
  493. {"boards": []},
  494. )
  495. def test_api_board_api_board_status_no_data(self):
  496. headers = {
  497. "Authorization": "token aaabbbcccddd",
  498. "Content-Type": "application/json",
  499. }
  500. data = json.dumps({})
  501. output = self.app.post(
  502. "/api/0/test/boards/dev/status", headers=headers, data=data
  503. )
  504. self.assertEqual(output.status_code, 400)
  505. data = json.loads(output.get_data(as_text=True))
  506. self.assertDictEqual(
  507. data,
  508. {
  509. "error": "Invalid or incomplete input submitted",
  510. "error_code": "EINVALIDREQ",
  511. "errors": "No (JSON) data provided",
  512. },
  513. )
  514. def test_api_board_api_board_status_invalid_tag_name(self):
  515. headers = {
  516. "Authorization": "token aaabbbcccddd",
  517. "Content-Type": "application/json",
  518. }
  519. data = json.dumps(
  520. {
  521. "-Triaged": {
  522. "close": False,
  523. "close_status": "",
  524. "bg_color": "#ca0dcd",
  525. "default": True,
  526. "rank": 1,
  527. }
  528. }
  529. )
  530. output = self.app.post(
  531. "/api/0/test/boards/dev/status", headers=headers, data=data
  532. )
  533. self.assertEqual(output.status_code, 400)
  534. data = json.loads(output.get_data(as_text=True))
  535. self.assertDictEqual(
  536. data,
  537. {
  538. "error": "Invalid or incomplete input submitted",
  539. "error_code": "EINVALIDREQ",
  540. "errors": {
  541. "name": [
  542. "Invalid status name provided, it should match: "
  543. "^[a-zA-Z0-9][a-zA-Z0-9-_ .:]+$."
  544. ]
  545. },
  546. },
  547. )
  548. def test_api_board_api_board_status_missing_rank(self):
  549. headers = {
  550. "Authorization": "token aaabbbcccddd",
  551. "Content-Type": "application/json",
  552. }
  553. data = json.dumps(
  554. {
  555. "Triaged": {
  556. "close": False,
  557. "close_status": "",
  558. "bg_color": "#ca0dcd",
  559. "default": True,
  560. }
  561. }
  562. )
  563. output = self.app.post(
  564. "/api/0/test/boards/dev/status", headers=headers, data=data
  565. )
  566. self.assertEqual(output.status_code, 400)
  567. data = json.loads(output.get_data(as_text=True))
  568. self.assertDictEqual(
  569. data,
  570. {
  571. "error": "Invalid or incomplete input submitted",
  572. "error_code": "EINVALIDREQ",
  573. "errors": "The 'rank' and 'default' fields are mandatory.",
  574. },
  575. )
  576. def test_api_board_api_board_status_missing_default(self):
  577. headers = {
  578. "Authorization": "token aaabbbcccddd",
  579. "Content-Type": "application/json",
  580. }
  581. data = json.dumps(
  582. {
  583. "Triaged": {
  584. "close": False,
  585. "close_status": "",
  586. "bg_color": "#ca0dcd",
  587. "rank": 1,
  588. }
  589. }
  590. )
  591. output = self.app.post(
  592. "/api/0/test/boards/dev/status", headers=headers, data=data
  593. )
  594. self.assertEqual(output.status_code, 400)
  595. data = json.loads(output.get_data(as_text=True))
  596. self.assertDictEqual(
  597. data,
  598. {
  599. "error": "Invalid or incomplete input submitted",
  600. "error_code": "EINVALIDREQ",
  601. "errors": "The 'rank' and 'default' fields are mandatory.",
  602. },
  603. )
  604. def test_api_board_api_board_status_no_default(self):
  605. headers = {
  606. "Authorization": "token aaabbbcccddd",
  607. "Content-Type": "application/json",
  608. }
  609. data = json.dumps(
  610. {
  611. "Triaged": {
  612. "close": False,
  613. "close_status": "",
  614. "bg_color": "#ca0dcd",
  615. "default": False,
  616. "rank": 1,
  617. }
  618. }
  619. )
  620. output = self.app.post(
  621. "/api/0/test/boards/dev/status", headers=headers, data=data
  622. )
  623. self.assertEqual(output.status_code, 400)
  624. data = json.loads(output.get_data(as_text=True))
  625. self.assertDictEqual(
  626. data,
  627. {
  628. "error": "Invalid or incomplete input submitted",
  629. "error_code": "EINVALIDREQ",
  630. "errors": "There must be one and only one default.",
  631. },
  632. )
  633. def test_api_board_api_board_status_multiple_default(self):
  634. headers = {
  635. "Authorization": "token aaabbbcccddd",
  636. "Content-Type": "application/json",
  637. }
  638. data = json.dumps(
  639. {
  640. "Backlog": {
  641. "close": False,
  642. "close_status": "",
  643. "bg_color": "#FFB300",
  644. "default": True,
  645. "rank": 1,
  646. },
  647. "Triaged": {
  648. "close": False,
  649. "close_status": "",
  650. "bg_color": "#ca0dcd",
  651. "default": True,
  652. "rank": 1,
  653. },
  654. }
  655. )
  656. output = self.app.post(
  657. "/api/0/test/boards/dev/status", headers=headers, data=data
  658. )
  659. self.assertEqual(output.status_code, 400)
  660. data = json.loads(output.get_data(as_text=True))
  661. self.assertDictEqual(
  662. data,
  663. {
  664. "error": "Invalid or incomplete input submitted",
  665. "error_code": "EINVALIDREQ",
  666. "errors": "There must be one and only one default.",
  667. },
  668. )
  669. def test_api_board_api_board_status(self):
  670. headers = {
  671. "Authorization": "token aaabbbcccddd",
  672. "Content-Type": "application/json",
  673. }
  674. data = json.dumps(
  675. {
  676. "Backlog": {
  677. "close": False,
  678. "close_status": "",
  679. "bg_color": "#FFB300",
  680. "default": True,
  681. "rank": 1,
  682. },
  683. "Triaged": {
  684. "close": False,
  685. "close_status": "",
  686. "bg_color": "#ca0dcd",
  687. "default": False,
  688. "rank": 2,
  689. },
  690. "Done": {
  691. "close": True,
  692. "close_status": "Fixed",
  693. "bg_color": "#34d240",
  694. "default": False,
  695. "rank": 4,
  696. },
  697. " ": {
  698. "close": True,
  699. "close_status": "Fixed",
  700. "bg_color": "#34d240",
  701. "default": False,
  702. "rank": 5,
  703. },
  704. }
  705. )
  706. output = self.app.post(
  707. "/api/0/test/boards/dev/status", headers=headers, data=data
  708. )
  709. self.assertEqual(output.status_code, 200)
  710. data = json.loads(output.get_data(as_text=True))
  711. self.assertDictEqual(
  712. data,
  713. {
  714. "board": {
  715. "active": True,
  716. "full_url": "http://localhost.localdomain/test/boards/dev",
  717. "name": "dev",
  718. "status": [
  719. {
  720. "bg_color": "#FFB300",
  721. "close": False,
  722. "close_status": None,
  723. "default": True,
  724. "name": "Backlog",
  725. },
  726. {
  727. "bg_color": "#ca0dcd",
  728. "close": False,
  729. "close_status": None,
  730. "default": False,
  731. "name": "Triaged",
  732. },
  733. {
  734. "name": "Done",
  735. "close": True,
  736. "close_status": "Fixed",
  737. "default": False,
  738. "bg_color": "#34d240",
  739. },
  740. ],
  741. "tag": {
  742. "tag": "dev",
  743. "tag_color": "DeepBlueSky",
  744. "tag_description": "",
  745. },
  746. }
  747. },
  748. )
  749. def test_api_board_api_board_status_no_close_status(self):
  750. headers = {
  751. "Authorization": "token aaabbbcccddd",
  752. "Content-Type": "application/json",
  753. }
  754. data = json.dumps(
  755. {
  756. "Backlog": {
  757. "close": False,
  758. "close_status": None,
  759. "bg_color": "#FFB300",
  760. "default": True,
  761. "rank": 1,
  762. },
  763. "Triaged": {
  764. "close": False,
  765. "close_status": None,
  766. "bg_color": "#ca0dcd",
  767. "default": False,
  768. "rank": 2,
  769. },
  770. "Done": {
  771. "close": True,
  772. "close_status": None,
  773. "bg_color": "#34d240",
  774. "default": False,
  775. "rank": 4,
  776. },
  777. " ": {
  778. "close": True,
  779. "close_status": None,
  780. "bg_color": "#34d240",
  781. "default": False,
  782. "rank": 5,
  783. },
  784. }
  785. )
  786. output = self.app.post(
  787. "/api/0/test/boards/dev/status", headers=headers, data=data
  788. )
  789. self.assertEqual(output.status_code, 200)
  790. data = json.loads(output.get_data(as_text=True))
  791. self.assertDictEqual(
  792. data,
  793. {
  794. "board": {
  795. "active": True,
  796. "full_url": "http://localhost.localdomain/test/boards/dev",
  797. "name": "dev",
  798. "status": [
  799. {
  800. "bg_color": "#FFB300",
  801. "close": False,
  802. "close_status": None,
  803. "default": True,
  804. "name": "Backlog",
  805. },
  806. {
  807. "bg_color": "#ca0dcd",
  808. "close": False,
  809. "close_status": None,
  810. "default": False,
  811. "name": "Triaged",
  812. },
  813. {
  814. "name": "Done",
  815. "close": True,
  816. "close_status": None,
  817. "default": False,
  818. "bg_color": "#34d240",
  819. },
  820. ],
  821. "tag": {
  822. "tag": "dev",
  823. "tag_color": "DeepBlueSky",
  824. "tag_description": "",
  825. },
  826. }
  827. },
  828. )
  829. def test_api_board_api_board_status_adding_removing(self):
  830. headers = {
  831. "Authorization": "token aaabbbcccddd",
  832. "Content-Type": "application/json",
  833. }
  834. data = json.dumps(
  835. {
  836. "Backlog": {
  837. "close": False,
  838. "close_status": "",
  839. "bg_color": "#FFB300",
  840. "default": True,
  841. "rank": 1,
  842. },
  843. "Triaged": {
  844. "close": False,
  845. "close_status": "",
  846. "bg_color": "#ca0dcd",
  847. "default": False,
  848. "rank": 2,
  849. },
  850. "Done": {
  851. "close": True,
  852. "close_status": "Fixed",
  853. "bg_color": "#34d240",
  854. "default": False,
  855. "rank": 4,
  856. },
  857. " ": {
  858. "close": True,
  859. "close_status": "Fixed",
  860. "bg_color": "#34d240",
  861. "default": False,
  862. "rank": 5,
  863. },
  864. }
  865. )
  866. output = self.app.post(
  867. "/api/0/test/boards/dev/status", headers=headers, data=data
  868. )
  869. self.assertEqual(output.status_code, 200)
  870. data = json.loads(output.get_data(as_text=True))
  871. self.assertDictEqual(
  872. data,
  873. {
  874. "board": {
  875. "active": True,
  876. "full_url": "http://localhost.localdomain/test/boards/dev",
  877. "name": "dev",
  878. "status": [
  879. {
  880. "bg_color": "#FFB300",
  881. "close": False,
  882. "close_status": None,
  883. "default": True,
  884. "name": "Backlog",
  885. },
  886. {
  887. "bg_color": "#ca0dcd",
  888. "close": False,
  889. "close_status": None,
  890. "default": False,
  891. "name": "Triaged",
  892. },
  893. {
  894. "name": "Done",
  895. "close": True,
  896. "close_status": "Fixed",
  897. "default": False,
  898. "bg_color": "#34d240",
  899. },
  900. ],
  901. "tag": {
  902. "tag": "dev",
  903. "tag_color": "DeepBlueSky",
  904. "tag_description": "",
  905. },
  906. }
  907. },
  908. )
  909. data = json.dumps(
  910. {
  911. "Backlog": {
  912. "close": False,
  913. "close_status": "",
  914. "bg_color": "#FFB300",
  915. "default": True,
  916. "rank": 1,
  917. },
  918. "In Progress": {
  919. "close": False,
  920. "close_status": "",
  921. "bg_color": "#ca0eef",
  922. "default": False,
  923. "rank": 2,
  924. },
  925. "Done": {
  926. "close": True,
  927. "close_status": "Fixed",
  928. "bg_color": "#34d240",
  929. "default": False,
  930. "rank": 4,
  931. },
  932. }
  933. )
  934. output = self.app.post(
  935. "/api/0/test/boards/dev/status", headers=headers, data=data
  936. )
  937. self.assertEqual(output.status_code, 200)
  938. data = json.loads(output.get_data(as_text=True))
  939. self.assertDictEqual(
  940. data,
  941. {
  942. "board": {
  943. "active": True,
  944. "full_url": "http://localhost.localdomain/test/boards/dev",
  945. "name": "dev",
  946. "status": [
  947. {
  948. "bg_color": "#FFB300",
  949. "close": False,
  950. "close_status": None,
  951. "default": True,
  952. "name": "Backlog",
  953. },
  954. {
  955. "bg_color": "#ca0eef",
  956. "close": False,
  957. "close_status": None,
  958. "default": False,
  959. "name": "In Progress",
  960. },
  961. {
  962. "name": "Done",
  963. "close": True,
  964. "close_status": "Fixed",
  965. "default": False,
  966. "bg_color": "#34d240",
  967. },
  968. ],
  969. "tag": {
  970. "tag": "dev",
  971. "tag_color": "DeepBlueSky",
  972. "tag_description": "",
  973. },
  974. }
  975. },
  976. )
  977. class PagureFlaskApiBoardsWithBoardAndIssuetests(tests.SimplePagureTest):
  978. """Tests for flask API Boards controller of pagure for the tests
  979. requiring a pre-existing board and issues.
  980. """
  981. maxDiff = None
  982. def setUp(self):
  983. super(PagureFlaskApiBoardsWithBoardAndIssuetests, self).setUp()
  984. set_projects_up(self)
  985. set_up_board(self)
  986. # Set up the ticket repo
  987. tests.create_projects_git(
  988. os.path.join(self.path, "repos", "tickets"), bare=True
  989. )
  990. # Set up some status to the board
  991. headers = {
  992. "Authorization": "token aaabbbcccddd",
  993. "Content-Type": "application/json",
  994. }
  995. data = json.dumps(
  996. {
  997. "Backlog": {
  998. "close": False,
  999. "close_status": "",
  1000. "bg_color": "#FFB300",
  1001. "default": True,
  1002. "rank": 1,
  1003. },
  1004. "In Progress": {
  1005. "close": False,
  1006. "close_status": "",
  1007. "bg_color": "#ca0eef",
  1008. "default": False,
  1009. "rank": 2,
  1010. },
  1011. "Done": {
  1012. "close": True,
  1013. "close_status": "Fixed",
  1014. "bg_color": "#34d240",
  1015. "default": False,
  1016. "rank": 4,
  1017. },
  1018. }
  1019. )
  1020. output = self.app.post(
  1021. "/api/0/test/boards/dev/status", headers=headers, data=data
  1022. )
  1023. self.assertEqual(output.status_code, 200)
  1024. # Create two issues to play with
  1025. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  1026. msg = pagure.lib.query.new_issue(
  1027. session=self.session,
  1028. repo=repo,
  1029. title="Test issue",
  1030. content="We should work on this",
  1031. user="pingou",
  1032. )
  1033. self.session.commit()
  1034. self.assertEqual(msg.title, "Test issue")
  1035. self.assertEqual(repo.open_tickets, 1)
  1036. self.assertEqual(repo.open_tickets_public, 1)
  1037. msg = pagure.lib.query.new_issue(
  1038. session=self.session,
  1039. repo=repo,
  1040. title="Test issue #2",
  1041. content="We should work on this for the second time",
  1042. user="foo",
  1043. status="Open",
  1044. )
  1045. self.session.commit()
  1046. self.assertEqual(msg.title, "Test issue #2")
  1047. self.assertEqual(repo.open_tickets, 2)
  1048. self.assertEqual(repo.open_tickets_public, 2)
  1049. self.tickets_uid = [t.uid for t in repo.issues]
  1050. def test_api_board_ticket_add_status_invalid_board(self):
  1051. headers = {
  1052. "Authorization": "token aaabbbcccddd",
  1053. "Content-Type": "application/json",
  1054. }
  1055. data = json.dumps(
  1056. {
  1057. "12": {
  1058. "status": "In Progress",
  1059. "rank": 2,
  1060. }
  1061. }
  1062. )
  1063. output = self.app.post(
  1064. "/api/0/test/boards/invalid/add_issue", headers=headers, data=data
  1065. )
  1066. self.assertEqual(output.status_code, 404)
  1067. data = json.loads(output.get_data(as_text=True))
  1068. self.assertDictEqual(
  1069. data,
  1070. {
  1071. "error": "Invalid or incomplete input submitted",
  1072. "error_code": "EINVALIDREQ",
  1073. "errors": "Board not found",
  1074. },
  1075. )
  1076. def test_api_board_ticket_add_status_no_data(self):
  1077. headers = {
  1078. "Authorization": "token aaabbbcccddd",
  1079. "Content-Type": "application/json",
  1080. }
  1081. data = json.dumps({})
  1082. output = self.app.post(
  1083. "/api/0/test/boards/dev/add_issue", headers=headers, data=data
  1084. )
  1085. self.assertEqual(output.status_code, 400)
  1086. data = json.loads(output.get_data(as_text=True))
  1087. self.assertDictEqual(
  1088. data,
  1089. {
  1090. "error": "Invalid or incomplete input submitted",
  1091. "error_code": "EINVALIDREQ",
  1092. "errors": "No (JSON) data provided",
  1093. },
  1094. )
  1095. def test_api_board_ticket_add_status_no_rank(self):
  1096. headers = {
  1097. "Authorization": "token aaabbbcccddd",
  1098. "Content-Type": "application/json",
  1099. }
  1100. data = json.dumps({"12": {"status": "In Progress"}})
  1101. output = self.app.post(
  1102. "/api/0/test/boards/dev/add_issue", headers=headers, data=data
  1103. )
  1104. self.assertEqual(output.status_code, 400)
  1105. data = json.loads(output.get_data(as_text=True))
  1106. self.assertDictEqual(
  1107. data,
  1108. {
  1109. "error": "Invalid or incomplete input submitted",
  1110. "error_code": "EINVALIDREQ",
  1111. "errors": "The 'rank' and 'status' fields are mandatory.",
  1112. },
  1113. )
  1114. def test_api_board_ticket_add_status_no_status(self):
  1115. headers = {
  1116. "Authorization": "token aaabbbcccddd",
  1117. "Content-Type": "application/json",
  1118. }
  1119. data = json.dumps(
  1120. {
  1121. "12": {
  1122. "rank": 2,
  1123. }
  1124. }
  1125. )
  1126. output = self.app.post(
  1127. "/api/0/test/boards/dev/add_issue", headers=headers, data=data
  1128. )
  1129. self.assertEqual(output.status_code, 400)
  1130. data = json.loads(output.get_data(as_text=True))
  1131. self.assertDictEqual(
  1132. data,
  1133. {
  1134. "error": "Invalid or incomplete input submitted",
  1135. "error_code": "EINVALIDREQ",
  1136. "errors": "The 'rank' and 'status' fields are mandatory.",
  1137. },
  1138. )
  1139. def test_api_board_ticket_add_status_invalid_ticket_id(self):
  1140. headers = {
  1141. "Authorization": "token aaabbbcccddd",
  1142. "Content-Type": "application/json",
  1143. }
  1144. data = json.dumps({"12": {"status": "In Progress", "rank": 2}})
  1145. output = self.app.post(
  1146. "/api/0/test/boards/dev/add_issue", headers=headers, data=data
  1147. )
  1148. self.assertEqual(output.status_code, 400)
  1149. data = json.loads(output.get_data(as_text=True))
  1150. self.assertDictEqual(
  1151. data,
  1152. {
  1153. "error": "No ticket found with this identifier",
  1154. "error_code": "ENOCODE",
  1155. },
  1156. )
  1157. def test_api_board_ticket_add_status(self):
  1158. headers = {
  1159. "Authorization": "token aaabbbcccddd",
  1160. "Content-Type": "application/json",
  1161. }
  1162. data = json.dumps(
  1163. {
  1164. "2": {"status": "In Progress", "rank": 2},
  1165. " ": {"status": "In Progress", "rank": 4},
  1166. }
  1167. )
  1168. output = self.app.post(
  1169. "/api/0/test/boards/dev/add_issue", headers=headers, data=data
  1170. )
  1171. self.assertEqual(output.status_code, 200)
  1172. data = json.loads(output.get_data(as_text=True))
  1173. self.assertDictEqual(
  1174. data,
  1175. {
  1176. "board": {
  1177. "active": True,
  1178. "full_url": "http://localhost.localdomain/test/boards/dev",
  1179. "name": "dev",
  1180. "status": [
  1181. {
  1182. "bg_color": "#FFB300",
  1183. "close": False,
  1184. "close_status": None,
  1185. "default": True,
  1186. "name": "Backlog",
  1187. },
  1188. {
  1189. "bg_color": "#ca0eef",
  1190. "close": False,
  1191. "close_status": None,
  1192. "default": False,
  1193. "name": "In Progress",
  1194. },
  1195. {
  1196. "bg_color": "#34d240",
  1197. "close": True,
  1198. "close_status": "Fixed",
  1199. "default": False,
  1200. "name": "Done",
  1201. },
  1202. ],
  1203. "tag": {
  1204. "tag": "dev",
  1205. "tag_color": "DeepBlueSky",
  1206. "tag_description": "",
  1207. },
  1208. }
  1209. },
  1210. )
  1211. def test_api_board_ticket_update_status_invalid_board(self):
  1212. headers = {
  1213. "Authorization": "token aaabbbcccddd",
  1214. "Content-Type": "application/json",
  1215. }
  1216. data = json.dumps(
  1217. {
  1218. "12": {
  1219. "status": "In Progress",
  1220. "rank": 2,
  1221. }
  1222. }
  1223. )
  1224. output = self.app.post(
  1225. "/api/0/test/boards/invalid/update_issue",
  1226. headers=headers,
  1227. data=data,
  1228. )
  1229. self.assertEqual(output.status_code, 404)
  1230. data = json.loads(output.get_data(as_text=True))
  1231. self.assertDictEqual(
  1232. data,
  1233. {
  1234. "error": "Invalid or incomplete input submitted",
  1235. "error_code": "EINVALIDREQ",
  1236. "errors": "Board not found",
  1237. },
  1238. )
  1239. def test_api_board_ticket_update_status_no_data(self):
  1240. headers = {
  1241. "Authorization": "token aaabbbcccddd",
  1242. "Content-Type": "application/json",
  1243. }
  1244. data = json.dumps({})
  1245. output = self.app.post(
  1246. "/api/0/test/boards/dev/update_issue", headers=headers, data=data
  1247. )
  1248. self.assertEqual(output.status_code, 400)
  1249. data = json.loads(output.get_data(as_text=True))
  1250. self.assertDictEqual(
  1251. data,
  1252. {
  1253. "error": "Invalid or incomplete input submitted",
  1254. "error_code": "EINVALIDREQ",
  1255. "errors": "No (JSON) data provided",
  1256. },
  1257. )
  1258. def test_api_board_ticket_update_status_no_rank(self):
  1259. headers = {
  1260. "Authorization": "token aaabbbcccddd",
  1261. "Content-Type": "application/json",
  1262. }
  1263. data = json.dumps({"12": {"status": "In Progress"}})
  1264. output = self.app.post(
  1265. "/api/0/test/boards/dev/update_issue", headers=headers, data=data
  1266. )
  1267. self.assertEqual(output.status_code, 400)
  1268. data = json.loads(output.get_data(as_text=True))
  1269. self.assertDictEqual(
  1270. data,
  1271. {
  1272. "error": "Invalid or incomplete input submitted",
  1273. "error_code": "EINVALIDREQ",
  1274. "errors": "The 'rank' and 'status' fields are mandatory.",
  1275. },
  1276. )
  1277. def test_api_board_ticket_update_status_no_status(self):
  1278. headers = {
  1279. "Authorization": "token aaabbbcccddd",
  1280. "Content-Type": "application/json",
  1281. }
  1282. data = json.dumps(
  1283. {
  1284. "12": {
  1285. "rank": 2,
  1286. }
  1287. }
  1288. )
  1289. output = self.app.post(
  1290. "/api/0/test/boards/dev/add_issue", headers=headers, data=data
  1291. )
  1292. self.assertEqual(output.status_code, 400)
  1293. data = json.loads(output.get_data(as_text=True))
  1294. self.assertDictEqual(
  1295. data,
  1296. {
  1297. "error": "Invalid or incomplete input submitted",
  1298. "error_code": "EINVALIDREQ",
  1299. "errors": "The 'rank' and 'status' fields are mandatory.",
  1300. },
  1301. )
  1302. def test_api_board_ticket_update_status_invalid_ticket_id(self):
  1303. headers = {
  1304. "Authorization": "token aaabbbcccddd",
  1305. "Content-Type": "application/json",
  1306. }
  1307. data = json.dumps({"12": {"status": "In Progress", "rank": 2}})
  1308. output = self.app.post(
  1309. "/api/0/test/boards/dev/update_issue", headers=headers, data=data
  1310. )
  1311. self.assertEqual(output.status_code, 400)
  1312. data = json.loads(output.get_data(as_text=True))
  1313. self.assertDictEqual(
  1314. data,
  1315. {
  1316. "error": "No ticket found with this identifier",
  1317. "error_code": "ENOCODE",
  1318. },
  1319. )
  1320. def test_api_board_ticket_update_status(self):
  1321. headers = {
  1322. "Authorization": "token aaabbbcccddd",
  1323. "Content-Type": "application/json",
  1324. }
  1325. data = json.dumps(
  1326. {
  1327. self.tickets_uid[1]: {"status": "In Progress", "rank": 2},
  1328. " ": {"status": "In Progress", "rank": 4},
  1329. }
  1330. )
  1331. output = self.app.post(
  1332. "/api/0/test/boards/dev/update_issue", headers=headers, data=data
  1333. )
  1334. self.assertEqual(output.status_code, 200)
  1335. data = json.loads(output.get_data(as_text=True))
  1336. self.assertDictEqual(
  1337. data,
  1338. {
  1339. "board": {
  1340. "active": True,
  1341. "full_url": "http://localhost.localdomain/test/boards/dev",
  1342. "name": "dev",
  1343. "status": [
  1344. {
  1345. "bg_color": "#FFB300",
  1346. "close": False,
  1347. "close_status": None,
  1348. "default": True,
  1349. "name": "Backlog",
  1350. },
  1351. {
  1352. "bg_color": "#ca0eef",
  1353. "close": False,
  1354. "close_status": None,
  1355. "default": False,
  1356. "name": "In Progress",
  1357. },
  1358. {
  1359. "bg_color": "#34d240",
  1360. "close": True,
  1361. "close_status": "Fixed",
  1362. "default": False,
  1363. "name": "Done",
  1364. },
  1365. ],
  1366. "tag": {
  1367. "tag": "dev",
  1368. "tag_color": "DeepBlueSky",
  1369. "tag_description": "",
  1370. },
  1371. }
  1372. },
  1373. )
  1374. def test_api_board_ticket_update_status_close_re_opend(self):
  1375. headers = {
  1376. "Authorization": "token aaabbbcccddd",
  1377. "Content-Type": "application/json",
  1378. }
  1379. data = json.dumps(
  1380. {
  1381. self.tickets_uid[1]: {"status": "Done", "rank": 2},
  1382. }
  1383. )
  1384. output = self.app.post(
  1385. "/api/0/test/boards/dev/update_issue", headers=headers, data=data
  1386. )
  1387. self.assertEqual(output.status_code, 200)
  1388. data = json.loads(output.get_data(as_text=True))
  1389. self.session = pagure.lib.query.create_session(self.dbpath)
  1390. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  1391. self.assertEqual(repo.issues[1].status, "Closed")
  1392. self.assertEqual(repo.issues[1].close_status, "Fixed")
  1393. data = json.dumps(
  1394. {
  1395. self.tickets_uid[1]: {"status": "In Progress", "rank": 2},
  1396. }
  1397. )
  1398. output = self.app.post(
  1399. "/api/0/test/boards/dev/update_issue", headers=headers, data=data
  1400. )
  1401. self.assertEqual(output.status_code, 200)
  1402. data = json.loads(output.get_data(as_text=True))
  1403. self.session = pagure.lib.query.create_session(self.dbpath)
  1404. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  1405. self.assertEqual(repo.issues[0].status, "Open")
  1406. self.assertEqual(repo.issues[1].status, "Open")
  1407. self.assertEqual(repo.issues[1].close_status, None)
  1408. @patch("pagure.lib.notify.send_email", new=MagicMock(return_value=True))
  1409. def test_ticket_representation_in_git(self):
  1410. headers = {
  1411. "Authorization": "token aaabbbcccddd",
  1412. "Content-Type": "application/json",
  1413. }
  1414. data = json.dumps(
  1415. {
  1416. "2": {"status": "In Progress", "rank": 2},
  1417. }
  1418. )
  1419. output = self.app.post(
  1420. "/api/0/test/boards/dev/add_issue", headers=headers, data=data
  1421. )
  1422. self.assertEqual(output.status_code, 200)
  1423. self.session = pagure.lib.query.create_session(self.dbpath)
  1424. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  1425. self.assertEqual(repo.issues[1].status, "Open")
  1426. self.assertEqual(repo.issues[1].close_status, None)
  1427. # Clone the repo so it isn't a bare repo
  1428. pygit2.clone_repository(
  1429. os.path.join(self.path, "repos", "tickets", "test.git"),
  1430. os.path.join(self.path, "repos", "tickets", "test"),
  1431. )
  1432. exp = {
  1433. "assignee": None,
  1434. "blocks": [],
  1435. "boards": [
  1436. {
  1437. "board": {
  1438. "active": True,
  1439. "full_url": "http://localhost.localdomain/test/boards/dev",
  1440. "name": "dev",
  1441. "status": [
  1442. {
  1443. "bg_color": "#FFB300",
  1444. "close": False,
  1445. "close_status": None,
  1446. "default": True,
  1447. "name": "Backlog",
  1448. },
  1449. {
  1450. "bg_color": "#ca0eef",
  1451. "close": False,
  1452. "close_status": None,
  1453. "default": False,
  1454. "name": "In Progress",
  1455. },
  1456. {
  1457. "bg_color": "#34d240",
  1458. "close": True,
  1459. "close_status": "Fixed",
  1460. "default": False,
  1461. "name": "Done",
  1462. },
  1463. ],
  1464. "tag": {
  1465. "tag": "dev",
  1466. "tag_color": "DeepBlueSky",
  1467. "tag_description": "",
  1468. },
  1469. },
  1470. "rank": 2,
  1471. "status": {
  1472. "bg_color": "#ca0eef",
  1473. "close": False,
  1474. "close_status": None,
  1475. "default": False,
  1476. "name": "In Progress",
  1477. },
  1478. }
  1479. ],
  1480. "close_status": None,
  1481. "closed_at": None,
  1482. "closed_by": None,
  1483. "comments": [
  1484. {
  1485. "comment": "Issue tagged with: dev",
  1486. "date_created": "1594654596",
  1487. "edited_on": None,
  1488. "editor": None,
  1489. "id": 1,
  1490. "notification": True,
  1491. "parent": None,
  1492. "reactions": {},
  1493. "user": {
  1494. "default_email": "bar@pingou.com",
  1495. "emails": ["bar@pingou.com", "foo@pingou.com"],
  1496. "full_url": "http://localhost.localdomain/user/pingou",
  1497. "fullname": "PY C",
  1498. "name": "pingou",
  1499. "url_path": "user/pingou",
  1500. },
  1501. }
  1502. ],
  1503. "content": "We should work on this for the second time",
  1504. "custom_fields": [],
  1505. "date_created": "1594654596",
  1506. "depends": [],
  1507. "full_url": "http://localhost.localdomain/test/issue/2",
  1508. "id": 2,
  1509. "last_updated": "1594654596",
  1510. "milestone": None,
  1511. "priority": None,
  1512. "private": False,
  1513. "related_prs": [],
  1514. "status": "Open",
  1515. "tags": ["dev"],
  1516. "title": "Test issue #2",
  1517. "user": {
  1518. "default_email": "foo@bar.com",
  1519. "emails": ["foo@bar.com"],
  1520. "full_url": "http://localhost.localdomain/user/foo",
  1521. "fullname": "foo bar",
  1522. "name": "foo",
  1523. "url_path": "user/foo",
  1524. },
  1525. }
  1526. with open(
  1527. os.path.join(
  1528. self.path, "repos", "tickets", "test", repo.issues[1].uid
  1529. )
  1530. ) as stream:
  1531. data = json.load(stream)
  1532. # Make the date fix
  1533. for idx, com in enumerate(data["comments"]):
  1534. com["date_created"] = "1594654596"
  1535. data["comments"][idx] = com
  1536. data["date_created"] = "1594654596"
  1537. data["last_updated"] = "1594654596"
  1538. self.assertDictEqual(data, exp)
  1539. if __name__ == "__main__":
  1540. unittest.main(verbosity=2)