test_pagure_flask_api_issue.py 106 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329
  1. # -*- coding: utf-8 -*-
  2. """
  3. (c) 2015 - Copyright Red Hat Inc
  4. Authors:
  5. Pierre-Yves Chibon <pingou@pingoured.fr>
  6. """
  7. __requires__ = ['SQLAlchemy >= 0.8']
  8. import pkg_resources
  9. import arrow
  10. import copy
  11. import datetime
  12. import unittest
  13. import shutil
  14. import sys
  15. import time
  16. import os
  17. import json
  18. from mock import patch
  19. sys.path.insert(0, os.path.join(os.path.dirname(
  20. os.path.abspath(__file__)), '..'))
  21. import pagure
  22. import pagure.lib
  23. import tests
  24. FULL_ISSUE_LIST = [
  25. {
  26. "assignee": None,
  27. "blocks": [],
  28. "close_status": None,
  29. "closed_at": None,
  30. "comments": [],
  31. "content": "We should work on this",
  32. "custom_fields": [],
  33. "date_created": "1431414800",
  34. "depends": [],
  35. "id": 9,
  36. "last_updated": "1431414800",
  37. "milestone": None,
  38. "priority": None,
  39. "private": True,
  40. "status": "Closed",
  41. "tags": [],
  42. "title": "Test issue",
  43. "user": {
  44. "fullname": "PY C",
  45. "name": "pingou"
  46. }
  47. },
  48. {
  49. "assignee": {'fullname': 'foo bar', 'name': 'foo'},
  50. "blocks": [],
  51. "close_status": None,
  52. "closed_at": None,
  53. "comments": [],
  54. "content": "This issue needs attention",
  55. "custom_fields": [],
  56. "date_created": "1431414800",
  57. "depends": [],
  58. "id": 8,
  59. "last_updated": "1431414800",
  60. "milestone": None,
  61. "priority": None,
  62. "private": True,
  63. "status": "Open",
  64. "tags": [],
  65. "title": "test issue1",
  66. "user": {
  67. "fullname": "PY C",
  68. "name": "pingou"
  69. }
  70. },
  71. {
  72. "assignee": None,
  73. "blocks": [],
  74. "close_status": None,
  75. "closed_at": None,
  76. "comments": [],
  77. "content": "This issue needs attention",
  78. "custom_fields": [],
  79. "date_created": "1431414800",
  80. "depends": [],
  81. "id": 7,
  82. "last_updated": "1431414800",
  83. "milestone": None,
  84. "priority": None,
  85. "private": True,
  86. "status": "Open",
  87. "tags": [],
  88. "title": "test issue",
  89. "user": {
  90. "fullname": "PY C",
  91. "name": "pingou"
  92. }
  93. },
  94. {
  95. "assignee": None,
  96. "blocks": [],
  97. "close_status": None,
  98. "closed_at": None,
  99. "comments": [],
  100. "content": "This issue needs attention",
  101. "custom_fields": [],
  102. "date_created": "1431414800",
  103. "depends": [],
  104. "id": 6,
  105. "last_updated": "1431414800",
  106. "milestone": None,
  107. "priority": None,
  108. "private": False,
  109. "status": "Open",
  110. "tags": [],
  111. "title": "test issue",
  112. "user": {
  113. "fullname": "PY C",
  114. "name": "pingou"
  115. }
  116. },
  117. {
  118. "assignee": None,
  119. "blocks": [],
  120. "close_status": None,
  121. "closed_at": None,
  122. "comments": [],
  123. "content": "This issue needs attention",
  124. "custom_fields": [],
  125. "date_created": "1431414800",
  126. "depends": [],
  127. "id": 5,
  128. "last_updated": "1431414800",
  129. "milestone": None,
  130. "priority": None,
  131. "private": False,
  132. "status": "Open",
  133. "tags": [],
  134. "title": "test issue",
  135. "user": {
  136. "fullname": "PY C",
  137. "name": "pingou"
  138. }
  139. },
  140. {
  141. "assignee": None,
  142. "blocks": [],
  143. "close_status": None,
  144. "closed_at": None,
  145. "comments": [],
  146. "content": "This issue needs attention",
  147. "custom_fields": [],
  148. "date_created": "1431414800",
  149. "depends": [],
  150. "id": 4,
  151. "last_updated": "1431414800",
  152. "milestone": None,
  153. "priority": None,
  154. "private": False,
  155. "status": "Open",
  156. "tags": [],
  157. "title": "test issue",
  158. "user": {
  159. "fullname": "PY C",
  160. "name": "pingou"
  161. }
  162. },
  163. {
  164. "assignee": None,
  165. "blocks": [],
  166. "close_status": None,
  167. "closed_at": None,
  168. "comments": [],
  169. "content": "This issue needs attention",
  170. "custom_fields": [],
  171. "date_created": "1431414800",
  172. "depends": [],
  173. "id": 3,
  174. "last_updated": "1431414800",
  175. "milestone": None,
  176. "priority": None,
  177. "private": False,
  178. "status": "Open",
  179. "tags": [],
  180. "title": "test issue",
  181. "user": {
  182. "fullname": "PY C",
  183. "name": "pingou"
  184. }
  185. },
  186. {
  187. "assignee": None,
  188. "blocks": [],
  189. "close_status": None,
  190. "closed_at": None,
  191. "comments": [],
  192. "content": "This issue needs attention",
  193. "custom_fields": [],
  194. "date_created": "1431414800",
  195. "depends": [],
  196. "id": 2,
  197. "last_updated": "1431414800",
  198. "milestone": "milestone-1.0",
  199. "priority": None,
  200. "private": False,
  201. "status": "Open",
  202. "tags": [],
  203. "title": "test issue",
  204. "user": {
  205. "fullname": "PY C",
  206. "name": "pingou"
  207. }
  208. },
  209. {
  210. "assignee": None,
  211. "blocks": [],
  212. "close_status": None,
  213. "closed_at": None,
  214. "comments": [],
  215. "content": "This issue needs attention",
  216. "custom_fields": [],
  217. "date_created": "1431414800",
  218. "depends": [],
  219. "id": 1,
  220. "last_updated": "1431414800",
  221. "milestone": None,
  222. "priority": None,
  223. "private": False,
  224. "status": "Open",
  225. "tags": [],
  226. "title": "test issue",
  227. "user": {
  228. "fullname": "PY C",
  229. "name": "pingou"
  230. }
  231. }
  232. ]
  233. LCL_ISSUES = [
  234. {
  235. 'assignee': None,
  236. 'blocks': [],
  237. 'close_status': None,
  238. 'closed_at': None,
  239. 'comments': [],
  240. 'content': 'Description',
  241. 'custom_fields': [],
  242. 'date_created': '1431414800',
  243. 'depends': [],
  244. 'id': 2,
  245. 'last_updated': '1431414800',
  246. 'milestone': None,
  247. 'priority': None,
  248. 'private': False,
  249. 'status': 'Open',
  250. 'tags': [],
  251. 'title': 'Issue #2',
  252. 'user': {'fullname': 'PY C', 'name': 'pingou'}
  253. },
  254. {
  255. 'assignee': None,
  256. 'blocks': [],
  257. 'close_status': None,
  258. 'closed_at': None,
  259. 'comments': [],
  260. 'content': 'Description',
  261. 'custom_fields': [],
  262. 'date_created': '1431414800',
  263. 'depends': [],
  264. 'id': 1,
  265. 'last_updated': '1431414800',
  266. 'milestone': None,
  267. 'priority': None,
  268. 'private': False,
  269. 'status': 'Open',
  270. 'tags': [],
  271. 'title': 'Issue #1',
  272. 'user': {'fullname': 'PY C', 'name': 'pingou'}
  273. }
  274. ]
  275. class PagureFlaskApiIssuetests(tests.SimplePagureTest):
  276. """ Tests for the flask API of pagure for issue """
  277. maxDiff = None
  278. def setUp(self):
  279. """ Set up the environnment, ran before every tests. """
  280. super(PagureFlaskApiIssuetests, self).setUp()
  281. pagure.config.config['TICKETS_FOLDER'] = None
  282. def test_api_new_issue(self):
  283. """ Test the api_new_issue method of the flask api. """
  284. tests.create_projects(self.session)
  285. tests.create_projects_git(
  286. os.path.join(self.path, 'tickets'), bare=True)
  287. tests.create_tokens(self.session)
  288. tests.create_tokens_acl(self.session)
  289. headers = {'Authorization': 'token aaabbbcccddd'}
  290. # Valid token, wrong project
  291. output = self.app.post('/api/0/test2/new_issue', headers=headers)
  292. self.assertEqual(output.status_code, 401)
  293. data = json.loads(output.data)
  294. self.assertEqual(sorted(data.keys()), ['error', 'error_code'])
  295. self.assertEqual(
  296. pagure.api.APIERROR.EINVALIDTOK.value, data['error'])
  297. self.assertEqual(
  298. pagure.api.APIERROR.EINVALIDTOK.name, data['error_code'])
  299. # No input
  300. output = self.app.post('/api/0/test/new_issue', headers=headers)
  301. self.assertEqual(output.status_code, 400)
  302. data = json.loads(output.data)
  303. self.assertDictEqual(
  304. data,
  305. {
  306. "error": "Invalid or incomplete input submitted",
  307. "error_code": "EINVALIDREQ",
  308. "errors": {
  309. "issue_content": ["This field is required."],
  310. "title": ["This field is required."],
  311. }
  312. }
  313. )
  314. data = {
  315. 'title': 'test issue'
  316. }
  317. # Invalid repo
  318. output = self.app.post(
  319. '/api/0/foo/new_issue', data=data, headers=headers)
  320. self.assertEqual(output.status_code, 404)
  321. data = json.loads(output.data)
  322. self.assertDictEqual(
  323. data,
  324. {
  325. "error": "Project not found",
  326. "error_code": "ENOPROJECT",
  327. }
  328. )
  329. # Incomplete request
  330. output = self.app.post(
  331. '/api/0/test/new_issue', data=data, headers=headers)
  332. self.assertEqual(output.status_code, 400)
  333. data = json.loads(output.data)
  334. self.assertDictEqual(
  335. data,
  336. {
  337. "error": "Invalid or incomplete input submitted",
  338. "error_code": "EINVALIDREQ",
  339. "errors": {
  340. "issue_content": ["This field is required."],
  341. "title": ["This field is required."]
  342. }
  343. }
  344. )
  345. data = {
  346. 'title': 'test issue',
  347. 'issue_content': 'This issue needs attention',
  348. }
  349. # Valid request
  350. output = self.app.post(
  351. '/api/0/test/new_issue', data=data, headers=headers)
  352. self.assertEqual(output.status_code, 200)
  353. data = json.loads(output.data)
  354. data['issue']['date_created'] = '1431414800'
  355. data['issue']['last_updated'] = '1431414800'
  356. self.assertDictEqual(
  357. data,
  358. {
  359. "issue": FULL_ISSUE_LIST[8],
  360. "message": "Issue created"
  361. }
  362. )
  363. # Valid request but invalid milestone
  364. data = {
  365. 'title': 'test issue',
  366. 'issue_content': 'This issue needs attention',
  367. 'milestone': ['milestone-1.0'],
  368. }
  369. output = self.app.post(
  370. '/api/0/test/new_issue', data=data, headers=headers)
  371. self.assertEqual(output.status_code, 400)
  372. data = json.loads(output.data)
  373. self.assertDictEqual(
  374. data,
  375. {
  376. "error": "Invalid or incomplete input submitted",
  377. "error_code": "EINVALIDREQ",
  378. "errors": {
  379. "milestone": [
  380. "Not a valid choice"
  381. ]
  382. }
  383. }
  384. )
  385. # Set some milestones
  386. repo = pagure.lib.get_authorized_project(self.session, 'test')
  387. repo.milestones = {'milestone-1.0': '', 'milestone-2.0': 'Tomorrow!'}
  388. self.session.add(repo)
  389. self.session.commit()
  390. # Valid request with milestone
  391. data = {
  392. 'title': 'test issue',
  393. 'issue_content': 'This issue needs attention',
  394. 'milestone': ['milestone-1.0'],
  395. }
  396. output = self.app.post(
  397. '/api/0/test/new_issue', data=data, headers=headers)
  398. self.assertEqual(output.status_code, 200)
  399. data = json.loads(output.data)
  400. data['issue']['date_created'] = '1431414800'
  401. data['issue']['last_updated'] = '1431414800'
  402. self.assertDictEqual(
  403. data,
  404. {
  405. "issue": FULL_ISSUE_LIST[7],
  406. "message": "Issue created"
  407. }
  408. )
  409. # Valid request, with private='false'
  410. data = {
  411. 'title': 'test issue',
  412. 'issue_content': 'This issue needs attention',
  413. 'private': 'false',
  414. }
  415. output = self.app.post(
  416. '/api/0/test/new_issue', data=data, headers=headers)
  417. self.assertEqual(output.status_code, 200)
  418. data = json.loads(output.data)
  419. data['issue']['date_created'] = '1431414800'
  420. data['issue']['last_updated'] = '1431414800'
  421. self.assertDictEqual(
  422. data,
  423. {
  424. "issue": FULL_ISSUE_LIST[6],
  425. "message": "Issue created"
  426. }
  427. )
  428. # Valid request, with private=False
  429. data = {
  430. 'title': 'test issue',
  431. 'issue_content': 'This issue needs attention',
  432. 'private': False
  433. }
  434. output = self.app.post(
  435. '/api/0/test/new_issue', data=data, headers=headers)
  436. self.assertEqual(output.status_code, 200)
  437. data = json.loads(output.data)
  438. data['issue']['date_created'] = '1431414800'
  439. data['issue']['last_updated'] = '1431414800'
  440. self.assertDictEqual(
  441. data,
  442. {
  443. "issue": FULL_ISSUE_LIST[5],
  444. "message": "Issue created"
  445. }
  446. )
  447. # Valid request, with private='False'
  448. data = {
  449. 'title': 'test issue',
  450. 'issue_content': 'This issue needs attention',
  451. 'private': 'False'
  452. }
  453. output = self.app.post(
  454. '/api/0/test/new_issue', data=data, headers=headers)
  455. self.assertEqual(output.status_code, 200)
  456. data = json.loads(output.data)
  457. data['issue']['date_created'] = '1431414800'
  458. data['issue']['last_updated'] = '1431414800'
  459. self.assertDictEqual(
  460. data,
  461. {
  462. "issue": FULL_ISSUE_LIST[4],
  463. "message": "Issue created"
  464. }
  465. )
  466. # Valid request, with private=0
  467. data = {
  468. 'title': 'test issue',
  469. 'issue_content': 'This issue needs attention',
  470. 'private': 0
  471. }
  472. output = self.app.post(
  473. '/api/0/test/new_issue', data=data, headers=headers)
  474. self.assertEqual(output.status_code, 200)
  475. data = json.loads(output.data)
  476. data['issue']['date_created'] = '1431414800'
  477. data['issue']['last_updated'] = '1431414800'
  478. self.assertDictEqual(
  479. data,
  480. {
  481. "issue": FULL_ISSUE_LIST[3],
  482. "message": "Issue created"
  483. }
  484. )
  485. # Private issue: True
  486. data = {
  487. 'title': 'test issue',
  488. 'issue_content': 'This issue needs attention',
  489. 'private': True,
  490. }
  491. output = self.app.post(
  492. '/api/0/test/new_issue', data=data, headers=headers)
  493. self.assertEqual(output.status_code, 200)
  494. data = json.loads(output.data)
  495. data['issue']['date_created'] = '1431414800'
  496. data['issue']['last_updated'] = '1431414800'
  497. self.assertDictEqual(
  498. data,
  499. {
  500. "issue": FULL_ISSUE_LIST[2],
  501. "message": "Issue created"
  502. }
  503. )
  504. # Private issue: 1
  505. data = {
  506. 'title': 'test issue1',
  507. 'issue_content': 'This issue needs attention',
  508. 'private': 1,
  509. 'assignee': 'foo'
  510. }
  511. output = self.app.post(
  512. '/api/0/test/new_issue', data=data, headers=headers)
  513. self.assertEqual(output.status_code, 200)
  514. data = json.loads(output.data)
  515. data['issue']['date_created'] = '1431414800'
  516. data['issue']['last_updated'] = '1431414800'
  517. exp = copy.deepcopy(FULL_ISSUE_LIST[1])
  518. exp['id'] = 8
  519. self.assertDictEqual(
  520. data,
  521. {
  522. "issue": exp,
  523. "message": "Issue created"
  524. }
  525. )
  526. def test_api_new_issue_user_token(self):
  527. """ Test the api_new_issue method of the flask api. """
  528. tests.create_projects(self.session)
  529. tests.create_projects_git(
  530. os.path.join(self.path, 'tickets'), bare=True)
  531. tests.create_tokens(self.session, project_id=None)
  532. tests.create_tokens_acl(self.session)
  533. headers = {'Authorization': 'token aaabbbcccddd'}
  534. # Valid token, invalid request - No input
  535. output = self.app.post('/api/0/test2/new_issue', headers=headers)
  536. self.assertEqual(output.status_code, 400)
  537. data = json.loads(output.data)
  538. self.assertDictEqual(
  539. data,
  540. {
  541. "error": "Invalid or incomplete input submitted",
  542. "error_code": "EINVALIDREQ",
  543. "errors": {
  544. "issue_content": ["This field is required."],
  545. "title": ["This field is required."],
  546. }
  547. }
  548. )
  549. # Another project, still an invalid request - No input
  550. output = self.app.post('/api/0/test/new_issue', headers=headers)
  551. self.assertEqual(output.status_code, 400)
  552. data = json.loads(output.data)
  553. self.assertDictEqual(
  554. data,
  555. {
  556. "error": "Invalid or incomplete input submitted",
  557. "error_code": "EINVALIDREQ",
  558. "errors": {
  559. "issue_content": ["This field is required."],
  560. "title": ["This field is required."],
  561. }
  562. }
  563. )
  564. data = {
  565. 'title': 'test issue'
  566. }
  567. # Invalid repo
  568. output = self.app.post(
  569. '/api/0/foo/new_issue', data=data, headers=headers)
  570. self.assertEqual(output.status_code, 404)
  571. data = json.loads(output.data)
  572. self.assertDictEqual(
  573. data,
  574. {
  575. "error": "Project not found",
  576. "error_code": "ENOPROJECT",
  577. }
  578. )
  579. # Incomplete request
  580. output = self.app.post(
  581. '/api/0/test/new_issue', data=data, headers=headers)
  582. self.assertEqual(output.status_code, 400)
  583. data = json.loads(output.data)
  584. self.assertDictEqual(
  585. data,
  586. {
  587. "error": "Invalid or incomplete input submitted",
  588. "error_code": "EINVALIDREQ",
  589. "errors": {
  590. "issue_content": ["This field is required."],
  591. "title": ["This field is required."]
  592. }
  593. }
  594. )
  595. data = {
  596. 'title': 'test issue',
  597. 'issue_content': 'This issue needs attention',
  598. }
  599. # Valid request
  600. output = self.app.post(
  601. '/api/0/test/new_issue', data=data, headers=headers)
  602. self.assertEqual(output.status_code, 200)
  603. data = json.loads(output.data)
  604. data['issue']['date_created'] = '1431414800'
  605. data['issue']['last_updated'] = '1431414800'
  606. self.assertDictEqual(
  607. data,
  608. {
  609. "issue": FULL_ISSUE_LIST[8],
  610. "message": "Issue created"
  611. }
  612. )
  613. # Set some milestones
  614. repo = pagure.lib.get_authorized_project(self.session, 'test')
  615. repo.milestones = {'milestone-1.0': '', 'milestone-2.0': 'Tomorrow!'}
  616. self.session.add(repo)
  617. self.session.commit()
  618. # Valid request with milestone
  619. data = {
  620. 'title': 'test issue',
  621. 'issue_content': 'This issue needs attention',
  622. 'milestone': ['milestone-1.0'],
  623. }
  624. output = self.app.post(
  625. '/api/0/test/new_issue', data=data, headers=headers)
  626. self.assertEqual(output.status_code, 200)
  627. data = json.loads(output.data)
  628. data['issue']['date_created'] = '1431414800'
  629. data['issue']['last_updated'] = '1431414800'
  630. self.assertDictEqual(
  631. data,
  632. {
  633. "issue": FULL_ISSUE_LIST[7],
  634. "message": "Issue created"
  635. }
  636. )
  637. # Valid request, with private='false'
  638. data = {
  639. 'title': 'test issue',
  640. 'issue_content': 'This issue needs attention',
  641. 'private': 'false',
  642. }
  643. output = self.app.post(
  644. '/api/0/test/new_issue', data=data, headers=headers)
  645. self.assertEqual(output.status_code, 200)
  646. data = json.loads(output.data)
  647. data['issue']['date_created'] = '1431414800'
  648. data['issue']['last_updated'] = '1431414800'
  649. self.assertDictEqual(
  650. data,
  651. {
  652. "issue": FULL_ISSUE_LIST[6],
  653. "message": "Issue created"
  654. }
  655. )
  656. # Valid request, with private=False
  657. data = {
  658. 'title': 'test issue',
  659. 'issue_content': 'This issue needs attention',
  660. 'private': False
  661. }
  662. output = self.app.post(
  663. '/api/0/test/new_issue', data=data, headers=headers)
  664. self.assertEqual(output.status_code, 200)
  665. data = json.loads(output.data)
  666. data['issue']['date_created'] = '1431414800'
  667. data['issue']['last_updated'] = '1431414800'
  668. self.assertDictEqual(
  669. data,
  670. {
  671. "issue": FULL_ISSUE_LIST[5],
  672. "message": "Issue created"
  673. }
  674. )
  675. # Valid request, with private='False'
  676. data = {
  677. 'title': 'test issue',
  678. 'issue_content': 'This issue needs attention',
  679. 'private': 'False'
  680. }
  681. output = self.app.post(
  682. '/api/0/test/new_issue', data=data, headers=headers)
  683. self.assertEqual(output.status_code, 200)
  684. data = json.loads(output.data)
  685. data['issue']['date_created'] = '1431414800'
  686. data['issue']['last_updated'] = '1431414800'
  687. self.assertDictEqual(
  688. data,
  689. {
  690. "issue": FULL_ISSUE_LIST[4],
  691. "message": "Issue created"
  692. }
  693. )
  694. # Valid request, with private=0
  695. data = {
  696. 'title': 'test issue',
  697. 'issue_content': 'This issue needs attention',
  698. 'private': 0
  699. }
  700. output = self.app.post(
  701. '/api/0/test/new_issue', data=data, headers=headers)
  702. self.assertEqual(output.status_code, 200)
  703. data = json.loads(output.data)
  704. data['issue']['date_created'] = '1431414800'
  705. data['issue']['last_updated'] = '1431414800'
  706. self.assertDictEqual(
  707. data,
  708. {
  709. "issue": FULL_ISSUE_LIST[3],
  710. "message": "Issue created"
  711. }
  712. )
  713. # Private issue: True
  714. data = {
  715. 'title': 'test issue',
  716. 'issue_content': 'This issue needs attention',
  717. 'private': True,
  718. }
  719. output = self.app.post(
  720. '/api/0/test/new_issue', data=data, headers=headers)
  721. self.assertEqual(output.status_code, 200)
  722. data = json.loads(output.data)
  723. data['issue']['date_created'] = '1431414800'
  724. data['issue']['last_updated'] = '1431414800'
  725. self.assertDictEqual(
  726. data,
  727. {
  728. "issue": FULL_ISSUE_LIST[2],
  729. "message": "Issue created"
  730. }
  731. )
  732. # Private issue: 1
  733. data = {
  734. 'title': 'test issue1',
  735. 'issue_content': 'This issue needs attention',
  736. 'private': 1,
  737. 'assignee': 'foo'
  738. }
  739. output = self.app.post(
  740. '/api/0/test/new_issue', data=data, headers=headers)
  741. self.assertEqual(output.status_code, 200)
  742. data = json.loads(output.data)
  743. data['issue']['date_created'] = '1431414800'
  744. data['issue']['last_updated'] = '1431414800'
  745. self.assertDictEqual(
  746. data,
  747. {
  748. "issue": FULL_ISSUE_LIST[1],
  749. "message": "Issue created"
  750. }
  751. )
  752. # Private issue: 'true'
  753. data = {
  754. 'title': 'test issue1',
  755. 'issue_content': 'This issue needs attention',
  756. 'private': 'true',
  757. }
  758. output = self.app.post(
  759. '/api/0/test/new_issue', data=data, headers=headers)
  760. self.assertEqual(output.status_code, 200)
  761. data = json.loads(output.data)
  762. data['issue']['date_created'] = '1431414800'
  763. data['issue']['last_updated'] = '1431414800'
  764. exp = copy.deepcopy(FULL_ISSUE_LIST[1])
  765. exp['id'] = 9
  766. exp['assignee'] = None
  767. self.assertDictEqual(
  768. data,
  769. {
  770. "issue": exp,
  771. "message": "Issue created"
  772. }
  773. )
  774. def test_api_view_issues(self):
  775. """ Test the api_view_issues method of the flask api. """
  776. self.test_api_new_issue()
  777. # Invalid repo
  778. output = self.app.get('/api/0/foo/issues')
  779. self.assertEqual(output.status_code, 404)
  780. data = json.loads(output.data)
  781. self.assertDictEqual(
  782. data,
  783. {
  784. "error": "Project not found",
  785. "error_code": "ENOPROJECT",
  786. }
  787. )
  788. # List all opened issues
  789. output = self.app.get('/api/0/test/issues')
  790. self.assertEqual(output.status_code, 200)
  791. data = json.loads(output.data)
  792. for idx in range(len(data['issues'])):
  793. data['issues'][idx]['date_created'] = '1431414800'
  794. data['issues'][idx]['last_updated'] = '1431414800'
  795. self.assertDictEqual(
  796. data,
  797. {
  798. "args": {
  799. "assignee": None,
  800. "author": None,
  801. 'milestones': [],
  802. 'no_stones': None,
  803. 'order': None,
  804. 'priority': None,
  805. "since": None,
  806. "status": None,
  807. "tags": [],
  808. },
  809. "issues": FULL_ISSUE_LIST[3:],
  810. "total_issues": 6
  811. }
  812. )
  813. # Create private issue
  814. repo = pagure.lib.get_authorized_project(self.session, 'test')
  815. msg = pagure.lib.new_issue(
  816. session=self.session,
  817. repo=repo,
  818. title='Test issue',
  819. content='We should work on this',
  820. user='pingou',
  821. ticketfolder=None,
  822. private=True,
  823. status="Closed"
  824. )
  825. self.session.commit()
  826. self.assertEqual(msg.title, 'Test issue')
  827. # Access issues un-authenticated
  828. output = self.app.get('/api/0/test/issues')
  829. self.assertEqual(output.status_code, 200)
  830. data = json.loads(output.data)
  831. for idx in range(len(data['issues'])):
  832. data['issues'][idx]['date_created'] = '1431414800'
  833. data['issues'][idx]['last_updated'] = '1431414800'
  834. self.assertDictEqual(
  835. data,
  836. {
  837. "args": {
  838. "assignee": None,
  839. "author": None,
  840. 'milestones': [],
  841. 'no_stones': None,
  842. 'order': None,
  843. 'priority': None,
  844. "since": None,
  845. "status": None,
  846. "tags": []
  847. },
  848. "issues": FULL_ISSUE_LIST[3:],
  849. "total_issues": 6
  850. }
  851. )
  852. headers = {'Authorization': 'token aaabbbccc'}
  853. # Access issues authenticated but non-existing token
  854. output = self.app.get('/api/0/test/issues', headers=headers)
  855. self.assertEqual(output.status_code, 401)
  856. # Create a new token for another user
  857. item = pagure.lib.model.Token(
  858. id='bar_token',
  859. user_id=2,
  860. project_id=1,
  861. expiration=datetime.datetime.utcnow() + datetime.timedelta(
  862. days=30)
  863. )
  864. self.session.add(item)
  865. self.session.commit()
  866. headers = {'Authorization': 'token bar_token'}
  867. # Access issues authenticated but wrong token
  868. output = self.app.get('/api/0/test/issues', headers=headers)
  869. self.assertEqual(output.status_code, 200)
  870. data = json.loads(output.data)
  871. for idx in range(len(data['issues'])):
  872. data['issues'][idx]['date_created'] = '1431414800'
  873. data['issues'][idx]['last_updated'] = '1431414800'
  874. lcl_issues = copy.deepcopy(FULL_ISSUE_LIST[3:])
  875. lcl_issues.insert(0,FULL_ISSUE_LIST[1])
  876. self.assertDictEqual(
  877. data,
  878. {
  879. "args": {
  880. "assignee": None,
  881. "author": None,
  882. 'milestones': [],
  883. 'no_stones': None,
  884. 'order': None,
  885. 'priority': None,
  886. "since": None,
  887. "status": None,
  888. "tags": []
  889. },
  890. "issues": lcl_issues,
  891. "total_issues": 7
  892. }
  893. )
  894. headers = {'Authorization': 'token aaabbbcccddd'}
  895. # Access issues authenticated correctly
  896. output = self.app.get('/api/0/test/issues', headers=headers)
  897. self.assertEqual(output.status_code, 200)
  898. data = json.loads(output.data)
  899. for idx in range(len(data['issues'])):
  900. data['issues'][idx]['date_created'] = '1431414800'
  901. data['issues'][idx]['last_updated'] = '1431414800'
  902. exp = FULL_ISSUE_LIST[1]
  903. exp['id'] = 8
  904. self.assertDictEqual(
  905. data,
  906. {
  907. "args": {
  908. "assignee": None,
  909. "author": None,
  910. 'milestones': [],
  911. 'no_stones': None,
  912. 'order': None,
  913. 'priority': None,
  914. "since": None,
  915. "status": None,
  916. "tags": []
  917. },
  918. "issues": FULL_ISSUE_LIST[1:],
  919. "total_issues": 8
  920. }
  921. )
  922. headers = {'Authorization': 'token aaabbbccc'}
  923. # Access issues authenticated but non-existing token
  924. output = self.app.get('/api/0/test/issues', headers=headers)
  925. self.assertEqual(output.status_code, 401)
  926. # Create a new token for another user
  927. item = pagure.lib.model.Token(
  928. id='bar_token_foo',
  929. user_id=2,
  930. project_id=1,
  931. expiration=datetime.datetime.utcnow() + datetime.timedelta(
  932. days=30)
  933. )
  934. self.session.add(item)
  935. self.session.commit()
  936. headers = {'Authorization': 'token bar_token_foo'}
  937. # Access issues authenticated but wrong token
  938. output = self.app.get('/api/0/test/issues', headers=headers)
  939. self.assertEqual(output.status_code, 200)
  940. data = json.loads(output.data)
  941. for idx in range(len(data['issues'])):
  942. data['issues'][idx]['date_created'] = '1431414800'
  943. data['issues'][idx]['last_updated'] = '1431414800'
  944. lcl_issues = copy.deepcopy(FULL_ISSUE_LIST[3:])
  945. lcl_issues.insert(0,FULL_ISSUE_LIST[1])
  946. self.assertDictEqual(
  947. data,
  948. {
  949. "args": {
  950. "assignee": None,
  951. "author": None,
  952. 'milestones': [],
  953. 'no_stones': None,
  954. 'order': None,
  955. 'priority': None,
  956. "since": None,
  957. "status": None,
  958. "tags": []
  959. },
  960. "issues": lcl_issues,
  961. "total_issues": 7
  962. }
  963. )
  964. headers = {'Authorization': 'token aaabbbcccddd'}
  965. # Access issues authenticated correctly
  966. output = self.app.get('/api/0/test/issues', headers=headers)
  967. self.assertEqual(output.status_code, 200)
  968. data = json.loads(output.data)
  969. for idx in range(len(data['issues'])):
  970. data['issues'][idx]['date_created'] = '1431414800'
  971. data['issues'][idx]['last_updated'] = '1431414800'
  972. self.assertDictEqual(
  973. data,
  974. {
  975. "args": {
  976. "assignee": None,
  977. "author": None,
  978. 'milestones': [],
  979. 'no_stones': None,
  980. 'order': None,
  981. 'priority': None,
  982. "since": None,
  983. "status": None,
  984. "tags": []
  985. },
  986. "issues": FULL_ISSUE_LIST[1:],
  987. "total_issues": 8
  988. }
  989. )
  990. # List closed issue
  991. output = self.app.get('/api/0/test/issues?status=Closed', headers=headers)
  992. self.assertEqual(output.status_code, 200)
  993. data = json.loads(output.data)
  994. data['issues'][0]['date_created'] = '1431414800'
  995. data['issues'][0]['last_updated'] = '1431414800'
  996. self.assertDictEqual(
  997. data,
  998. {
  999. "args": {
  1000. "assignee": None,
  1001. "author": None,
  1002. "milestones": [],
  1003. "no_stones": None,
  1004. 'order': None,
  1005. "priority": None,
  1006. "since": None,
  1007. "status": "Closed",
  1008. "tags": []
  1009. },
  1010. "issues": [FULL_ISSUE_LIST[0]],
  1011. "total_issues": 1,
  1012. }
  1013. )
  1014. # List closed issue
  1015. output = self.app.get('/api/0/test/issues?status=Invalid', headers=headers)
  1016. self.assertEqual(output.status_code, 200)
  1017. data = json.loads(output.data)
  1018. self.assertDictEqual(
  1019. data,
  1020. {
  1021. "args": {
  1022. "assignee": None,
  1023. "author": None,
  1024. 'milestones': [],
  1025. 'no_stones': None,
  1026. 'order': None,
  1027. 'priority': None,
  1028. "since": None,
  1029. "status": "Invalid",
  1030. "tags": []
  1031. },
  1032. "total_issues": 0,
  1033. "issues": []
  1034. }
  1035. )
  1036. # List all issues
  1037. output = self.app.get('/api/0/test/issues?status=All', headers=headers)
  1038. self.assertEqual(output.status_code, 200)
  1039. data = json.loads(output.data)
  1040. for idx in range(len(data['issues'])):
  1041. data['issues'][idx]['last_updated'] = '1431414800'
  1042. data['issues'][idx]['date_created'] = '1431414800'
  1043. self.assertDictEqual(
  1044. data,
  1045. {
  1046. "args": {
  1047. "assignee": None,
  1048. "author": None,
  1049. 'milestones': [],
  1050. 'no_stones': None,
  1051. 'order': None,
  1052. 'priority': None,
  1053. "since": None,
  1054. "status": "All",
  1055. "tags": []
  1056. },
  1057. "issues": FULL_ISSUE_LIST,
  1058. "total_issues": 9
  1059. }
  1060. )
  1061. def test_api_view_issues_reversed(self):
  1062. """ Test the api_view_issues method of the flask api. in reversed
  1063. order.
  1064. """
  1065. self.test_api_new_issue()
  1066. headers = {'Authorization': 'token aaabbbcccddd'}
  1067. # List issues in reverse order
  1068. output = self.app.get('/api/0/test/issues?order=asc', headers=headers)
  1069. self.assertEqual(output.status_code, 200)
  1070. data = json.loads(output.data)
  1071. for idx in range(len(data['issues'])):
  1072. data['issues'][idx]['last_updated'] = '1431414800'
  1073. data['issues'][idx]['date_created'] = '1431414800'
  1074. expected = {
  1075. "args": {
  1076. "assignee": None,
  1077. "author": None,
  1078. 'milestones': [],
  1079. 'no_stones': None,
  1080. 'order': 'asc',
  1081. 'priority': None,
  1082. "since": None,
  1083. "status": None,
  1084. "tags": []
  1085. },
  1086. "issues": FULL_ISSUE_LIST[1:][::-1],
  1087. "total_issues": 8
  1088. }
  1089. self.assertDictEqual(data, expected)
  1090. def test_api_view_issues_milestone(self):
  1091. """ Test the api_view_issues method of the flask api when filtering
  1092. for a milestone.
  1093. """
  1094. tests.create_projects(self.session)
  1095. tests.create_projects_git(
  1096. os.path.join(self.path, 'tickets'), bare=True)
  1097. tests.create_tokens(self.session)
  1098. tests.create_tokens_acl(self.session)
  1099. repo = pagure.lib.get_authorized_project(self.session, 'test')
  1100. # Create 2 tickets but only 1 has a milestone
  1101. start = arrow.utcnow().timestamp
  1102. issue = pagure.lib.model.Issue(
  1103. id=pagure.lib.get_next_id(self.session, repo.id),
  1104. project_id=repo.id,
  1105. title='Issue #1',
  1106. content='Description',
  1107. user_id=1, # pingou
  1108. uid='issue#1',
  1109. private=False,
  1110. )
  1111. self.session.add(issue)
  1112. self.session.commit()
  1113. issue = pagure.lib.model.Issue(
  1114. id=pagure.lib.get_next_id(self.session, repo.id),
  1115. project_id=repo.id,
  1116. title='Issue #2',
  1117. content='Description',
  1118. user_id=1, # pingou
  1119. uid='issue#2',
  1120. private=False,
  1121. milestone='v1.0',
  1122. )
  1123. self.session.add(issue)
  1124. self.session.commit()
  1125. # List all opened issues
  1126. output = self.app.get('/api/0/test/issues')
  1127. self.assertEqual(output.status_code, 200)
  1128. data = json.loads(output.data)
  1129. for idx in range(len(data['issues'])):
  1130. data['issues'][idx]['date_created'] = '1431414800'
  1131. data['issues'][idx]['last_updated'] = '1431414800'
  1132. lcl_issues = copy.deepcopy(LCL_ISSUES)
  1133. lcl_issues[0]['milestone'] = 'v1.0'
  1134. self.assertDictEqual(
  1135. data,
  1136. {
  1137. "args": {
  1138. "assignee": None,
  1139. "author": None,
  1140. 'milestones': [],
  1141. 'no_stones': None,
  1142. 'order': None,
  1143. 'priority': None,
  1144. "since": None,
  1145. "status": None,
  1146. "tags": [],
  1147. },
  1148. "issues": lcl_issues,
  1149. "total_issues": 2
  1150. }
  1151. )
  1152. # List all issues of the milestone v1.0
  1153. output = self.app.get('/api/0/test/issues?milestones=v1.0')
  1154. self.assertEqual(output.status_code, 200)
  1155. data = json.loads(output.data)
  1156. for idx in range(len(data['issues'])):
  1157. data['issues'][idx]['date_created'] = '1431414800'
  1158. data['issues'][idx]['last_updated'] = '1431414800'
  1159. self.assertDictEqual(
  1160. data,
  1161. {
  1162. "args": {
  1163. "assignee": None,
  1164. "author": None,
  1165. 'milestones': ['v1.0'],
  1166. 'no_stones': None,
  1167. 'order': None,
  1168. 'priority': None,
  1169. "since": None,
  1170. "status": None,
  1171. "tags": [],
  1172. },
  1173. "issues": [lcl_issues[0]],
  1174. "total_issues": 1
  1175. }
  1176. )
  1177. def test_api_view_issues_priority(self):
  1178. """ Test the api_view_issues method of the flask api when filtering
  1179. for a priority.
  1180. """
  1181. tests.create_projects(self.session)
  1182. tests.create_projects_git(
  1183. os.path.join(self.path, 'tickets'), bare=True)
  1184. tests.create_tokens(self.session)
  1185. tests.create_tokens_acl(self.session)
  1186. repo = pagure.lib.get_authorized_project(self.session, 'test')
  1187. # Create 2 tickets but only 1 has a priority
  1188. start = arrow.utcnow().timestamp
  1189. issue = pagure.lib.model.Issue(
  1190. id=pagure.lib.get_next_id(self.session, repo.id),
  1191. project_id=repo.id,
  1192. title='Issue #1',
  1193. content='Description',
  1194. user_id=1, # pingou
  1195. uid='issue#1',
  1196. private=False,
  1197. )
  1198. self.session.add(issue)
  1199. self.session.commit()
  1200. issue = pagure.lib.model.Issue(
  1201. id=pagure.lib.get_next_id(self.session, repo.id),
  1202. project_id=repo.id,
  1203. title='Issue #2',
  1204. content='Description',
  1205. user_id=1, # pingou
  1206. uid='issue#2',
  1207. private=False,
  1208. priority=1,
  1209. )
  1210. self.session.add(issue)
  1211. self.session.commit()
  1212. # Set some priorities to the project
  1213. repo.priorities = {'1': 'High', '2': 'Normal'}
  1214. self.session.add(repo)
  1215. self.session.commit()
  1216. # List all opened issues
  1217. output = self.app.get('/api/0/test/issues')
  1218. self.assertEqual(output.status_code, 200)
  1219. data = json.loads(output.data)
  1220. for idx in range(len(data['issues'])):
  1221. data['issues'][idx]['date_created'] = '1431414800'
  1222. data['issues'][idx]['last_updated'] = '1431414800'
  1223. lcl_issues = copy.deepcopy(LCL_ISSUES)
  1224. lcl_issues[0]['priority'] = 1
  1225. self.assertDictEqual(
  1226. data,
  1227. {
  1228. "args": {
  1229. "assignee": None,
  1230. "author": None,
  1231. 'milestones': [],
  1232. 'no_stones': None,
  1233. 'order': None,
  1234. 'priority': None,
  1235. "since": None,
  1236. "status": None,
  1237. "tags": [],
  1238. },
  1239. "issues": lcl_issues,
  1240. "total_issues": 2
  1241. }
  1242. )
  1243. # List all issues of the priority high (ie: 1)
  1244. output = self.app.get('/api/0/test/issues?priority=high')
  1245. self.assertEqual(output.status_code, 200)
  1246. data = json.loads(output.data)
  1247. for idx in range(len(data['issues'])):
  1248. data['issues'][idx]['date_created'] = '1431414800'
  1249. data['issues'][idx]['last_updated'] = '1431414800'
  1250. self.assertDictEqual(
  1251. data,
  1252. {
  1253. "args": {
  1254. "assignee": None,
  1255. "author": None,
  1256. 'milestones': [],
  1257. 'no_stones': None,
  1258. 'order': None,
  1259. 'priority': 'high',
  1260. "since": None,
  1261. "status": None,
  1262. "tags": [],
  1263. },
  1264. "issues": [lcl_issues[0]],
  1265. "total_issues": 1
  1266. }
  1267. )
  1268. output = self.app.get('/api/0/test/issues?priority=1')
  1269. self.assertEqual(output.status_code, 200)
  1270. data = json.loads(output.data)
  1271. for idx in range(len(data['issues'])):
  1272. data['issues'][idx]['date_created'] = '1431414800'
  1273. data['issues'][idx]['last_updated'] = '1431414800'
  1274. self.assertDictEqual(
  1275. data,
  1276. {
  1277. "args": {
  1278. "assignee": None,
  1279. "author": None,
  1280. 'milestones': [],
  1281. 'no_stones': None,
  1282. 'order': None,
  1283. 'priority': '1',
  1284. "since": None,
  1285. "status": None,
  1286. "tags": [],
  1287. },
  1288. "issues": [lcl_issues[0]],
  1289. "total_issues": 1
  1290. }
  1291. )
  1292. def test_api_view_issues_priority_invalid(self):
  1293. """ Test the api_view_issues method of the flask api when filtering
  1294. for an invalid priority.
  1295. """
  1296. tests.create_projects(self.session)
  1297. tests.create_projects_git(
  1298. os.path.join(self.path, 'tickets'), bare=True)
  1299. tests.create_tokens(self.session)
  1300. tests.create_tokens_acl(self.session)
  1301. # Try getting issues with an invalid priority
  1302. output = self.app.get('/api/0/test/issues?priority=foobar')
  1303. self.assertEqual(output.status_code, 400)
  1304. data = json.loads(output.data)
  1305. self.assertDictEqual(
  1306. data,
  1307. {
  1308. "error": "Invalid priority submitted",
  1309. "error_code": "EINVALIDPRIORITY"
  1310. }
  1311. )
  1312. def test_api_view_issues_no_stones(self):
  1313. """ Test the api_view_issues method of the flask api when filtering
  1314. with no_stones.
  1315. """
  1316. tests.create_projects(self.session)
  1317. tests.create_projects_git(
  1318. os.path.join(self.path, 'tickets'), bare=True)
  1319. tests.create_tokens(self.session)
  1320. tests.create_tokens_acl(self.session)
  1321. repo = pagure.lib.get_authorized_project(self.session, 'test')
  1322. # Create 2 tickets but only 1 has a milestone
  1323. start = arrow.utcnow().timestamp
  1324. issue = pagure.lib.model.Issue(
  1325. id=pagure.lib.get_next_id(self.session, repo.id),
  1326. project_id=repo.id,
  1327. title='Issue #1',
  1328. content='Description',
  1329. user_id=1, # pingou
  1330. uid='issue#1',
  1331. private=False,
  1332. )
  1333. self.session.add(issue)
  1334. self.session.commit()
  1335. issue = pagure.lib.model.Issue(
  1336. id=pagure.lib.get_next_id(self.session, repo.id),
  1337. project_id=repo.id,
  1338. title='Issue #2',
  1339. content='Description',
  1340. user_id=1, # pingou
  1341. uid='issue#2',
  1342. private=False,
  1343. milestone='v1.0',
  1344. )
  1345. self.session.add(issue)
  1346. self.session.commit()
  1347. # List all opened issues
  1348. output = self.app.get('/api/0/test/issues')
  1349. self.assertEqual(output.status_code, 200)
  1350. data = json.loads(output.data)
  1351. for idx in range(len(data['issues'])):
  1352. data['issues'][idx]['date_created'] = '1431414800'
  1353. data['issues'][idx]['last_updated'] = '1431414800'
  1354. lcl_issues = copy.deepcopy(LCL_ISSUES)
  1355. lcl_issues[0]['milestone'] = 'v1.0'
  1356. self.assertDictEqual(
  1357. data,
  1358. {
  1359. "args": {
  1360. "assignee": None,
  1361. "author": None,
  1362. 'milestones': [],
  1363. 'no_stones': None,
  1364. 'order': None,
  1365. 'priority': None,
  1366. "since": None,
  1367. "status": None,
  1368. "tags": [],
  1369. },
  1370. "issues": lcl_issues,
  1371. "total_issues": 2
  1372. }
  1373. )
  1374. # List all issues with no milestone
  1375. output = self.app.get('/api/0/test/issues?no_stones=1')
  1376. self.assertEqual(output.status_code, 200)
  1377. data = json.loads(output.data)
  1378. for idx in range(len(data['issues'])):
  1379. data['issues'][idx]['date_created'] = '1431414800'
  1380. data['issues'][idx]['last_updated'] = '1431414800'
  1381. self.assertDictEqual(
  1382. data,
  1383. {
  1384. "args": {
  1385. "assignee": None,
  1386. "author": None,
  1387. 'milestones': [],
  1388. 'no_stones': True,
  1389. 'order': None,
  1390. 'priority': None,
  1391. "since": None,
  1392. "status": None,
  1393. "tags": [],
  1394. },
  1395. "issues": [lcl_issues[1]],
  1396. "total_issues": 1
  1397. }
  1398. )
  1399. # List all issues with a milestone
  1400. output = self.app.get('/api/0/test/issues?no_stones=0')
  1401. self.assertEqual(output.status_code, 200)
  1402. data = json.loads(output.data)
  1403. for idx in range(len(data['issues'])):
  1404. data['issues'][idx]['date_created'] = '1431414800'
  1405. data['issues'][idx]['last_updated'] = '1431414800'
  1406. self.assertDictEqual(
  1407. data,
  1408. {
  1409. "args": {
  1410. "assignee": None,
  1411. "author": None,
  1412. 'milestones': [],
  1413. 'no_stones': False,
  1414. 'order': None,
  1415. 'priority': None,
  1416. "since": None,
  1417. "status": None,
  1418. "tags": [],
  1419. },
  1420. "issues": [lcl_issues[0]],
  1421. "total_issues": 1
  1422. }
  1423. )
  1424. def test_api_view_issues_since(self):
  1425. """ Test the api_view_issues method of the flask api for since option """
  1426. tests.create_projects(self.session)
  1427. tests.create_projects_git(
  1428. os.path.join(self.path, 'tickets'), bare=True)
  1429. tests.create_tokens(self.session)
  1430. tests.create_tokens_acl(self.session)
  1431. repo = pagure.lib.get_authorized_project(self.session, 'test')
  1432. # Create 1st tickets
  1433. start = arrow.utcnow().timestamp
  1434. issue = pagure.lib.model.Issue(
  1435. id=pagure.lib.get_next_id(self.session, repo.id),
  1436. project_id=repo.id,
  1437. title='Issue #1',
  1438. content='Description',
  1439. user_id=1, # pingou
  1440. uid='issue#1',
  1441. private=False,
  1442. )
  1443. self.session.add(issue)
  1444. self.session.commit()
  1445. time.sleep(1)
  1446. middle = arrow.utcnow().timestamp
  1447. # Create 2nd tickets
  1448. issue = pagure.lib.model.Issue(
  1449. id=pagure.lib.get_next_id(self.session, repo.id),
  1450. project_id=repo.id,
  1451. title='Issue #2',
  1452. content='Description',
  1453. user_id=1, # pingou
  1454. uid='issue#2',
  1455. private=False,
  1456. )
  1457. self.session.add(issue)
  1458. self.session.commit()
  1459. time.sleep(1)
  1460. final = arrow.utcnow().timestamp
  1461. # Create private issue
  1462. issue = pagure.lib.model.Issue(
  1463. id=pagure.lib.get_next_id(self.session, repo.id),
  1464. project_id=repo.id,
  1465. title='Issue #3',
  1466. content='Description',
  1467. user_id=1, # pingou
  1468. uid='issue#3',
  1469. private=True,
  1470. )
  1471. self.session.add(issue)
  1472. self.session.commit()
  1473. # Invalid repo
  1474. output = self.app.get('/api/0/foo/issues')
  1475. self.assertEqual(output.status_code, 404)
  1476. data = json.loads(output.data)
  1477. self.assertDictEqual(
  1478. data,
  1479. {
  1480. "error": "Project not found",
  1481. "error_code": "ENOPROJECT",
  1482. }
  1483. )
  1484. # List all opened issues
  1485. output = self.app.get('/api/0/test/issues')
  1486. self.assertEqual(output.status_code, 200)
  1487. data = json.loads(output.data)
  1488. for idx in range(len(data['issues'])):
  1489. data['issues'][idx]['date_created'] = '1431414800'
  1490. data['issues'][idx]['last_updated'] = '1431414800'
  1491. self.assertDictEqual(
  1492. data,
  1493. {
  1494. "args": {
  1495. "assignee": None,
  1496. "author": None,
  1497. 'milestones': [],
  1498. 'no_stones': None,
  1499. 'order': None,
  1500. 'priority': None,
  1501. "since": None,
  1502. "status": None,
  1503. "tags": []
  1504. },
  1505. "issues": LCL_ISSUES,
  1506. "total_issues": 2
  1507. }
  1508. )
  1509. time.sleep(1)
  1510. late = arrow.utcnow().timestamp
  1511. # List all opened issues from the start
  1512. output = self.app.get('/api/0/test/issues?since=%s' % start)
  1513. self.assertEqual(output.status_code, 200)
  1514. data = json.loads(output.data)
  1515. for idx in range(len(data['issues'])):
  1516. data['issues'][idx]['date_created'] = '1431414800'
  1517. data['issues'][idx]['last_updated'] = '1431414800'
  1518. self.assertDictEqual(
  1519. data,
  1520. {
  1521. "args": {
  1522. "assignee": None,
  1523. "author": None,
  1524. 'milestones': [],
  1525. 'no_stones': None,
  1526. 'order': None,
  1527. 'priority': None,
  1528. "since": str(start),
  1529. "status": None,
  1530. "tags": []
  1531. },
  1532. "issues": LCL_ISSUES,
  1533. "total_issues": 2
  1534. }
  1535. )
  1536. # List all opened issues from the middle
  1537. output = self.app.get('/api/0/test/issues?since=%s' % middle)
  1538. self.assertEqual(output.status_code, 200)
  1539. data = json.loads(output.data)
  1540. for idx in range(len(data['issues'])):
  1541. data['issues'][idx]['date_created'] = '1431414800'
  1542. data['issues'][idx]['last_updated'] = '1431414800'
  1543. self.assertDictEqual(
  1544. data,
  1545. {
  1546. "args": {
  1547. "assignee": None,
  1548. "author": None,
  1549. 'milestones': [],
  1550. 'no_stones': None,
  1551. 'order': None,
  1552. 'priority': None,
  1553. "since": str(middle),
  1554. "status": None,
  1555. "tags": []
  1556. },
  1557. "issues": LCL_ISSUES[:1],
  1558. "total_issues": 1
  1559. }
  1560. )
  1561. # List all opened issues at the end
  1562. output = self.app.get('/api/0/test/issues?since=%s' % final)
  1563. self.assertEqual(output.status_code, 200)
  1564. data = json.loads(output.data)
  1565. for idx in range(len(data['issues'])):
  1566. data['issues'][idx]['date_created'] = '1431414800'
  1567. data['issues'][idx]['last_updated'] = '1431414800'
  1568. self.assertDictEqual(
  1569. data,
  1570. {
  1571. "args": {
  1572. "assignee": None,
  1573. "author": None,
  1574. 'milestones': [],
  1575. 'no_stones': None,
  1576. 'order': None,
  1577. 'priority': None,
  1578. "since": str(final),
  1579. "status": None,
  1580. "tags": []
  1581. },
  1582. "issues": [],
  1583. "total_issues": 0
  1584. }
  1585. )
  1586. headers = {'Authorization': 'token aaabbbcccddd'}
  1587. # Test since for a value before creation of issues
  1588. output = self.app.get(
  1589. '/api/0/test/issues?since=%s' % final, headers=headers)
  1590. self.assertEqual(output.status_code, 200)
  1591. data = json.loads(output.data)
  1592. for idx in range(len(data['issues'])):
  1593. data['issues'][idx]['last_updated'] = '1431414800'
  1594. data['issues'][idx]['date_created'] = '1431414800'
  1595. self.assertDictEqual(
  1596. data,
  1597. {
  1598. "args": {
  1599. "assignee": None,
  1600. "author": None,
  1601. 'milestones': [],
  1602. 'no_stones': None,
  1603. 'order': None,
  1604. 'priority': None,
  1605. "since": str(final),
  1606. "status": None,
  1607. "tags": []
  1608. },
  1609. "issues": [{
  1610. 'assignee': None,
  1611. 'blocks': [],
  1612. 'close_status': None,
  1613. 'closed_at': None,
  1614. 'comments': [],
  1615. 'content': 'Description',
  1616. 'custom_fields': [],
  1617. 'date_created': '1431414800',
  1618. 'depends': [],
  1619. 'id': 3,
  1620. 'last_updated': '1431414800',
  1621. 'milestone': None,
  1622. 'priority': None,
  1623. 'private': True,
  1624. 'status': 'Open',
  1625. 'tags': [],
  1626. 'title': 'Issue #3',
  1627. 'user': {'fullname': 'PY C', 'name': 'pingou'}}
  1628. ],
  1629. "total_issues": 1
  1630. }
  1631. )
  1632. def test_api_view_issue(self):
  1633. """ Test the api_view_issue method of the flask api. """
  1634. self.test_api_new_issue()
  1635. # Invalid repo
  1636. output = self.app.get('/api/0/foo/issue/1')
  1637. self.assertEqual(output.status_code, 404)
  1638. data = json.loads(output.data)
  1639. self.assertDictEqual(
  1640. data,
  1641. {
  1642. "error": "Project not found",
  1643. "error_code": "ENOPROJECT",
  1644. }
  1645. )
  1646. # Invalid issue for this repo
  1647. output = self.app.get('/api/0/test2/issue/1')
  1648. self.assertEqual(output.status_code, 404)
  1649. data = json.loads(output.data)
  1650. self.assertDictEqual(
  1651. data,
  1652. {
  1653. "error": "Issue not found",
  1654. "error_code": "ENOISSUE",
  1655. }
  1656. )
  1657. # Valid issue
  1658. output = self.app.get('/api/0/test/issue/1')
  1659. self.assertEqual(output.status_code, 200)
  1660. data = json.loads(output.data)
  1661. data['date_created'] = '1431414800'
  1662. data['last_updated'] = '1431414800'
  1663. self.assertDictEqual(
  1664. data,
  1665. {
  1666. "assignee": None,
  1667. "blocks": [],
  1668. "comments": [],
  1669. "content": "This issue needs attention",
  1670. "custom_fields": [],
  1671. "date_created": "1431414800",
  1672. "close_status": None,
  1673. "closed_at": None,
  1674. "depends": [],
  1675. "id": 1,
  1676. "last_updated": "1431414800",
  1677. "milestone": None,
  1678. "priority": None,
  1679. "private": False,
  1680. "status": "Open",
  1681. "tags": [],
  1682. "title": "test issue",
  1683. "user": {
  1684. "fullname": "PY C",
  1685. "name": "pingou"
  1686. }
  1687. }
  1688. )
  1689. # Create private issue
  1690. repo = pagure.lib.get_authorized_project(self.session, 'test')
  1691. msg = pagure.lib.new_issue(
  1692. session=self.session,
  1693. repo=repo,
  1694. title='Test issue',
  1695. content='We should work on this',
  1696. user='pingou',
  1697. ticketfolder=None,
  1698. private=True,
  1699. issue_uid='aaabbbccc',
  1700. )
  1701. self.session.commit()
  1702. self.assertEqual(msg.title, 'Test issue')
  1703. # Access private issue un-authenticated
  1704. output = self.app.get('/api/0/test/issue/7')
  1705. self.assertEqual(output.status_code, 403)
  1706. data = json.loads(output.data)
  1707. self.assertDictEqual(
  1708. data,
  1709. {
  1710. "error": "You are not allowed to view this issue",
  1711. "error_code": "EISSUENOTALLOWED",
  1712. }
  1713. )
  1714. headers = {'Authorization': 'token aaabbbccc'}
  1715. # Access private issue authenticated but non-existing token
  1716. output = self.app.get('/api/0/test/issue/6', headers=headers)
  1717. self.assertEqual(output.status_code, 401)
  1718. data = json.loads(output.data)
  1719. self.assertEqual(sorted(data.keys()), ['error', 'error_code'])
  1720. self.assertEqual(
  1721. pagure.api.APIERROR.EINVALIDTOK.value, data['error'])
  1722. self.assertEqual(
  1723. pagure.api.APIERROR.EINVALIDTOK.name, data['error_code'])
  1724. # Create a new token for another user
  1725. item = pagure.lib.model.Token(
  1726. id='bar_token',
  1727. user_id=2,
  1728. project_id=1,
  1729. expiration=datetime.datetime.utcnow() + datetime.timedelta(
  1730. days=30)
  1731. )
  1732. self.session.add(item)
  1733. self.session.commit()
  1734. headers = {'Authorization': 'token bar_token'}
  1735. # Access private issue authenticated but wrong token
  1736. output = self.app.get('/api/0/test/issue/7', headers=headers)
  1737. self.assertEqual(output.status_code, 403)
  1738. data = json.loads(output.data)
  1739. self.assertDictEqual(
  1740. data,
  1741. {
  1742. "error": "You are not allowed to view this issue",
  1743. "error_code": "EISSUENOTALLOWED",
  1744. }
  1745. )
  1746. headers = {'Authorization': 'token aaabbbcccddd'}
  1747. # Access private issue authenticated correctly
  1748. output = self.app.get('/api/0/test/issue/6', headers=headers)
  1749. self.assertEqual(output.status_code, 200)
  1750. data = json.loads(output.data)
  1751. data['date_created'] = '1431414800'
  1752. data['last_updated'] = '1431414800'
  1753. self.assertDictEqual(
  1754. data,
  1755. {
  1756. "assignee": None,
  1757. "blocks": [],
  1758. "comments": [],
  1759. "content": "This issue needs attention",
  1760. "custom_fields": [],
  1761. "date_created": "1431414800",
  1762. "close_status": None,
  1763. "closed_at": None,
  1764. "depends": [],
  1765. "id": 6,
  1766. "last_updated": "1431414800",
  1767. "milestone": None,
  1768. "priority": None,
  1769. "private": False,
  1770. "status": "Open",
  1771. "tags": [],
  1772. "title": "test issue",
  1773. "user": {
  1774. "fullname": "PY C",
  1775. "name": "pingou"
  1776. }
  1777. }
  1778. )
  1779. # Access private issue authenticated correctly using the issue's uid
  1780. output = self.app.get('/api/0/test/issue/aaabbbccc', headers=headers)
  1781. self.assertEqual(output.status_code, 200)
  1782. data = json.loads(output.data)
  1783. data['date_created'] = '1431414800'
  1784. data['last_updated'] = '1431414800'
  1785. self.assertDictEqual(
  1786. data,
  1787. {
  1788. "assignee": None,
  1789. "blocks": [],
  1790. "comments": [],
  1791. "content": "We should work on this",
  1792. "custom_fields": [],
  1793. "date_created": "1431414800",
  1794. "close_status": None,
  1795. "closed_at": None,
  1796. "depends": [],
  1797. "id": 9,
  1798. "last_updated": "1431414800",
  1799. "milestone": None,
  1800. "priority": None,
  1801. "private": True,
  1802. "status": "Open",
  1803. "tags": [],
  1804. "title": "Test issue",
  1805. "user": {
  1806. "fullname": "PY C",
  1807. "name": "pingou"
  1808. }
  1809. }
  1810. )
  1811. def test_api_change_milestone_issue(self):
  1812. """ Test the api_change_milestone_issue method of the flask api. """
  1813. tests.create_projects(self.session)
  1814. tests.create_projects_git(os.path.join(self.path, 'tickets'))
  1815. tests.create_tokens(self.session)
  1816. tests.create_tokens_acl(self.session)
  1817. # Set some milestones to the project
  1818. repo = pagure.lib.get_authorized_project(self.session, 'test')
  1819. repo.milestones = {'v1.0': None, 'v2.0': 'Soon'}
  1820. self.session.add(repo)
  1821. self.session.commit()
  1822. headers = {'Authorization': 'token aaabbbcccddd'}
  1823. # Invalid project
  1824. output = self.app.post('/api/0/foo/issue/1/milestone', headers=headers)
  1825. self.assertEqual(output.status_code, 404)
  1826. data = json.loads(output.data)
  1827. self.assertDictEqual(
  1828. data,
  1829. {
  1830. "error": "Project not found",
  1831. "error_code": "ENOPROJECT",
  1832. }
  1833. )
  1834. # Valid token, wrong project
  1835. output = self.app.post('/api/0/test2/issue/1/milestone', headers=headers)
  1836. self.assertEqual(output.status_code, 401)
  1837. data = json.loads(output.data)
  1838. self.assertEqual(sorted(data.keys()), ['error', 'error_code'])
  1839. self.assertEqual(
  1840. pagure.api.APIERROR.EINVALIDTOK.value, data['error'])
  1841. self.assertEqual(
  1842. pagure.api.APIERROR.EINVALIDTOK.name, data['error_code'])
  1843. # No issue
  1844. output = self.app.post('/api/0/test/issue/1/milestone', headers=headers)
  1845. self.assertEqual(output.status_code, 404)
  1846. data = json.loads(output.data)
  1847. self.assertDictEqual(
  1848. data,
  1849. {
  1850. "error": "Issue not found",
  1851. "error_code": "ENOISSUE",
  1852. }
  1853. )
  1854. # Create normal issue
  1855. repo = pagure.lib.get_authorized_project(self.session, 'test')
  1856. msg = pagure.lib.new_issue(
  1857. session=self.session,
  1858. repo=repo,
  1859. title='Test issue #1',
  1860. content='We should work on this',
  1861. user='pingou',
  1862. ticketfolder=None,
  1863. private=False,
  1864. )
  1865. self.session.commit()
  1866. self.assertEqual(msg.title, 'Test issue #1')
  1867. # Check milestone before
  1868. repo = pagure.lib.get_authorized_project(self.session, 'test')
  1869. issue = pagure.lib.search_issues(self.session, repo, issueid=1)
  1870. self.assertEqual(issue.milestone, None)
  1871. data = {
  1872. 'milestone': '',
  1873. }
  1874. # Valid request but no milestone specified
  1875. output = self.app.post(
  1876. '/api/0/test/issue/1/milestone', data=data, headers=headers)
  1877. self.assertEqual(output.status_code, 200)
  1878. data = json.loads(output.data)
  1879. self.assertDictEqual(
  1880. data,
  1881. {'message': 'No changes'}
  1882. )
  1883. # No change
  1884. repo = pagure.lib.get_authorized_project(self.session, 'test')
  1885. issue = pagure.lib.search_issues(self.session, repo, issueid=1)
  1886. self.assertEqual(issue.milestone, None)
  1887. data = {
  1888. 'milestone': 'milestone-1-0',
  1889. }
  1890. # Invalid milestone specified
  1891. output = self.app.post(
  1892. '/api/0/test/issue/1/milestone', data=data, headers=headers)
  1893. self.assertEqual(output.status_code, 400)
  1894. data = json.loads(output.data)
  1895. self.assertDictEqual(
  1896. data,
  1897. {
  1898. "error": "Invalid or incomplete input submitted",
  1899. "error_code": "EINVALIDREQ",
  1900. "errors": {
  1901. "milestone": [
  1902. "Not a valid choice"
  1903. ]
  1904. }
  1905. }
  1906. )
  1907. data = {
  1908. 'milestone': 'v1.0',
  1909. }
  1910. # Valid requests
  1911. output = self.app.post(
  1912. '/api/0/test/issue/1/milestone', data=data, headers=headers)
  1913. self.assertEqual(output.status_code, 200)
  1914. data = json.loads(output.data)
  1915. self.assertDictEqual(
  1916. data,
  1917. {
  1918. "message": [
  1919. "Issue set to the milestone: v1.0"
  1920. ]
  1921. }
  1922. )
  1923. # remove milestone
  1924. data = {
  1925. 'milestone': '',
  1926. }
  1927. # Valid requests
  1928. output = self.app.post(
  1929. '/api/0/test/issue/1/milestone', data=data, headers=headers)
  1930. self.assertEqual(output.status_code, 200)
  1931. data = json.loads(output.data)
  1932. self.assertDictEqual(
  1933. data,
  1934. {
  1935. "message": [
  1936. "Issue set to the milestone: None (was: v1.0)"
  1937. ]
  1938. }
  1939. )
  1940. # Change recorded
  1941. repo = pagure.lib.get_authorized_project(self.session, 'test')
  1942. issue = pagure.lib.search_issues(self.session, repo, issueid=1)
  1943. self.assertEqual(issue.milestone, None)
  1944. data = {
  1945. 'milestone': 'v1.0',
  1946. }
  1947. # Valid requests
  1948. output = self.app.post(
  1949. '/api/0/test/issue/1/milestone', data=data, headers=headers)
  1950. self.assertEqual(output.status_code, 200)
  1951. data = json.loads(output.data)
  1952. self.assertDictEqual(
  1953. data,
  1954. {
  1955. "message": [
  1956. "Issue set to the milestone: v1.0"
  1957. ]
  1958. }
  1959. )
  1960. # remove milestone by using no milestone in JSON
  1961. data = {}
  1962. # Valid requests
  1963. output = self.app.post(
  1964. '/api/0/test/issue/1/milestone', data=data, headers=headers)
  1965. self.assertEqual(output.status_code, 200)
  1966. data = json.loads(output.data)
  1967. self.assertDictEqual(
  1968. data,
  1969. {
  1970. "message": [
  1971. "Issue set to the milestone: None (was: v1.0)"
  1972. ]
  1973. }
  1974. )
  1975. # Change recorded
  1976. repo = pagure.lib.get_authorized_project(self.session, 'test')
  1977. issue = pagure.lib.search_issues(self.session, repo, issueid=1)
  1978. self.assertEqual(issue.milestone, None)
  1979. headers = {'Authorization': 'token pingou_foo'}
  1980. # Un-authorized issue
  1981. output = self.app.post(
  1982. '/api/0/foo/issue/1/milestone', data=data, headers=headers)
  1983. self.assertEqual(output.status_code, 401)
  1984. data = json.loads(output.data)
  1985. self.assertEqual(sorted(data.keys()), ['error', 'error_code'])
  1986. self.assertEqual(
  1987. pagure.api.APIERROR.EINVALIDTOK.value, data['error'])
  1988. self.assertEqual(
  1989. pagure.api.APIERROR.EINVALIDTOK.name, data['error_code'])
  1990. @patch('pagure.lib.git.update_git')
  1991. @patch('pagure.lib.notify.send_email')
  1992. def test_api_view_issue_comment(self, p_send_email, p_ugt):
  1993. """ Test the api_view_issue_comment endpoint. """
  1994. p_send_email.return_value = True
  1995. p_ugt.return_value = True
  1996. tests.create_projects(self.session)
  1997. tests.create_projects_git(os.path.join(self.path, 'tickets'))
  1998. tests.create_tokens(self.session)
  1999. tests.create_tokens_acl(self.session)
  2000. # Create normal issue in test
  2001. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2002. msg = pagure.lib.new_issue(
  2003. session=self.session,
  2004. repo=repo,
  2005. title='Test issue #1',
  2006. content='We should work on this',
  2007. user='pingou',
  2008. ticketfolder=None,
  2009. private=False,
  2010. issue_uid='aaabbbccc#1',
  2011. )
  2012. self.session.commit()
  2013. self.assertEqual(msg.title, 'Test issue #1')
  2014. headers = {'Authorization': 'token aaabbbcccddd'}
  2015. data = {
  2016. 'comment': 'This is a very interesting question',
  2017. }
  2018. # Valid request
  2019. output = self.app.post(
  2020. '/api/0/test/issue/1/comment', data=data, headers=headers)
  2021. self.assertEqual(output.status_code, 200)
  2022. data = json.loads(output.data)
  2023. self.assertDictEqual(
  2024. data,
  2025. {'message': 'Comment added'}
  2026. )
  2027. # One comment added
  2028. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2029. issue = pagure.lib.search_issues(self.session, repo, issueid=1)
  2030. self.assertEqual(len(issue.comments), 1)
  2031. # View a comment that does not exist
  2032. output = self.app.get('/api/0/foo/issue/100/comment/2')
  2033. self.assertEqual(output.status_code, 404)
  2034. # Issue exists but not the comment
  2035. output = self.app.get('/api/0/test/issue/1/comment/2')
  2036. self.assertEqual(output.status_code, 404)
  2037. # Issue and comment exists
  2038. output = self.app.get('/api/0/test/issue/1/comment/1')
  2039. self.assertEqual(output.status_code, 200)
  2040. data = json.loads(output.data)
  2041. data['date_created'] = '1435821770'
  2042. data["comment_date"] = "2015-07-02 09:22"
  2043. data["avatar_url"] = "https://seccdn.libravatar.org/avatar/..."
  2044. self.assertDictEqual(
  2045. data,
  2046. {
  2047. "avatar_url": "https://seccdn.libravatar.org/avatar/...",
  2048. "comment": "This is a very interesting question",
  2049. "comment_date": "2015-07-02 09:22",
  2050. "date_created": "1435821770",
  2051. "edited_on": None,
  2052. "editor": None,
  2053. "notification": False,
  2054. "id": 1,
  2055. "parent": None,
  2056. "user": {
  2057. "fullname": "PY C",
  2058. "name": "pingou"
  2059. }
  2060. }
  2061. )
  2062. # Issue and comment exists, using UID
  2063. output = self.app.get('/api/0/test/issue/aaabbbccc#1/comment/1')
  2064. self.assertEqual(output.status_code, 200)
  2065. data = json.loads(output.data)
  2066. data['date_created'] = '1435821770'
  2067. data["comment_date"] = "2015-07-02 09:22"
  2068. data["avatar_url"] = "https://seccdn.libravatar.org/avatar/..."
  2069. self.assertDictEqual(
  2070. data,
  2071. {
  2072. "avatar_url": "https://seccdn.libravatar.org/avatar/...",
  2073. "comment": "This is a very interesting question",
  2074. "comment_date": "2015-07-02 09:22",
  2075. "date_created": "1435821770",
  2076. "edited_on": None,
  2077. "editor": None,
  2078. "notification": False,
  2079. "id": 1,
  2080. "parent": None,
  2081. "user": {
  2082. "fullname": "PY C",
  2083. "name": "pingou"
  2084. }
  2085. }
  2086. )
  2087. @patch('pagure.lib.git.update_git')
  2088. @patch('pagure.lib.notify.send_email')
  2089. def test_api_view_issue_comment_private(self, p_send_email, p_ugt):
  2090. """ Test the api_view_issue_comment endpoint. """
  2091. p_send_email.return_value = True
  2092. p_ugt.return_value = True
  2093. tests.create_projects(self.session)
  2094. tests.create_projects_git(os.path.join(self.path, 'tickets'))
  2095. tests.create_tokens(self.session)
  2096. tests.create_tokens_acl(self.session)
  2097. # Create normal issue in test
  2098. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2099. msg = pagure.lib.new_issue(
  2100. session=self.session,
  2101. repo=repo,
  2102. title='Test issue #1',
  2103. content='We should work on this',
  2104. user='foo',
  2105. ticketfolder=None,
  2106. private=True,
  2107. issue_uid='aaabbbccc#1',
  2108. )
  2109. self.session.commit()
  2110. self.assertEqual(msg.title, 'Test issue #1')
  2111. # Create a token for another user
  2112. item = pagure.lib.model.Token(
  2113. id='foo_token_2',
  2114. user_id=2,
  2115. project_id=1,
  2116. expiration=datetime.datetime.utcnow()
  2117. + datetime.timedelta(days=30)
  2118. )
  2119. self.session.add(item)
  2120. self.session.commit()
  2121. tests.create_tokens_acl(self.session, token_id='foo_token_2')
  2122. # Add a comment to that issue
  2123. data = {
  2124. 'comment': 'This is a very interesting question',
  2125. }
  2126. headers = {'Authorization': 'token foo_token_2'}
  2127. output = self.app.post(
  2128. '/api/0/test/issue/1/comment', data=data, headers=headers)
  2129. self.assertEqual(output.status_code, 200)
  2130. data = json.loads(output.data)
  2131. self.assertDictEqual(
  2132. data,
  2133. {'message': 'Comment added'}
  2134. )
  2135. # Private issue - no auth
  2136. output = self.app.get('/api/0/test/issue/1/comment/2')
  2137. self.assertEqual(output.status_code, 403)
  2138. # Private issue - Auth - Invalid token
  2139. headers = {'Authorization': 'token aaabbbcccdddee'}
  2140. output = self.app.get('/api/0/test/issue/1/comment/2', headers=headers)
  2141. self.assertEqual(output.status_code, 401)
  2142. # Private issue - Auth - valid token - unknown comment
  2143. headers = {'Authorization': 'token foo_token_2'}
  2144. output = self.app.get('/api/0/test/issue/1/comment/3', headers=headers)
  2145. self.assertEqual(output.status_code, 404)
  2146. # Private issue - Auth - valid token - known comment
  2147. headers = {'Authorization': 'token foo_token_2'}
  2148. output = self.app.get('/api/0/test/issue/1/comment/1', headers=headers)
  2149. self.assertEqual(output.status_code, 200)
  2150. data = json.loads(output.data)
  2151. data['date_created'] = '1435821770'
  2152. data["comment_date"] = "2015-07-02 09:22"
  2153. data["avatar_url"] = "https://seccdn.libravatar.org/avatar/..."
  2154. self.assertDictEqual(
  2155. data,
  2156. {
  2157. "avatar_url": "https://seccdn.libravatar.org/avatar/...",
  2158. "comment": "This is a very interesting question",
  2159. "comment_date": "2015-07-02 09:22",
  2160. "date_created": "1435821770",
  2161. "edited_on": None,
  2162. "editor": None,
  2163. "notification": False,
  2164. "id": 1,
  2165. "parent": None,
  2166. "user": {
  2167. "fullname": "foo bar",
  2168. "name": "foo"
  2169. }
  2170. }
  2171. )
  2172. @patch('pagure.lib.git.update_git')
  2173. @patch('pagure.lib.notify.send_email')
  2174. def test_api_assign_issue(self, p_send_email, p_ugt):
  2175. """ Test the api_assign_issue method of the flask api. """
  2176. p_send_email.return_value = True
  2177. p_ugt.return_value = True
  2178. tests.create_projects(self.session)
  2179. tests.create_tokens(self.session)
  2180. tests.create_tokens_acl(self.session)
  2181. headers = {'Authorization': 'token aaabbbcccddd'}
  2182. # Invalid project
  2183. output = self.app.post('/api/0/foo/issue/1/assign', headers=headers)
  2184. self.assertEqual(output.status_code, 404)
  2185. data = json.loads(output.data)
  2186. self.assertDictEqual(
  2187. data,
  2188. {
  2189. "error": "Project not found",
  2190. "error_code": "ENOPROJECT",
  2191. }
  2192. )
  2193. # Valid token, wrong project
  2194. output = self.app.post('/api/0/test2/issue/1/assign', headers=headers)
  2195. self.assertEqual(output.status_code, 401)
  2196. data = json.loads(output.data)
  2197. self.assertEqual(sorted(data.keys()), ['error', 'error_code'])
  2198. self.assertEqual(
  2199. pagure.api.APIERROR.EINVALIDTOK.value, data['error'])
  2200. self.assertEqual(
  2201. pagure.api.APIERROR.EINVALIDTOK.name, data['error_code'])
  2202. # No input
  2203. output = self.app.post('/api/0/test/issue/1/assign', headers=headers)
  2204. self.assertEqual(output.status_code, 404)
  2205. data = json.loads(output.data)
  2206. self.assertDictEqual(
  2207. data,
  2208. {
  2209. "error": "Issue not found",
  2210. "error_code": "ENOISSUE",
  2211. }
  2212. )
  2213. # Create normal issue
  2214. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2215. msg = pagure.lib.new_issue(
  2216. session=self.session,
  2217. repo=repo,
  2218. title='Test issue #1',
  2219. content='We should work on this',
  2220. user='pingou',
  2221. ticketfolder=None,
  2222. private=False,
  2223. issue_uid='aaabbbccc#1',
  2224. )
  2225. self.session.commit()
  2226. self.assertEqual(msg.title, 'Test issue #1')
  2227. # Check comments before
  2228. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2229. issue = pagure.lib.search_issues(self.session, repo, issueid=1)
  2230. self.assertEqual(len(issue.comments), 0)
  2231. # No change
  2232. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2233. issue = pagure.lib.search_issues(self.session, repo, issueid=1)
  2234. self.assertEqual(issue.status, 'Open')
  2235. data = {
  2236. 'assignee': 'pingou',
  2237. }
  2238. # Valid request
  2239. output = self.app.post(
  2240. '/api/0/test/issue/1/assign', data=data, headers=headers)
  2241. self.assertEqual(output.status_code, 200)
  2242. data = json.loads(output.data)
  2243. self.assertDictEqual(
  2244. data,
  2245. {'message': 'Issue assigned to pingou'}
  2246. )
  2247. # Un-assign
  2248. output = self.app.post(
  2249. '/api/0/test/issue/1/assign', data=data, headers=headers)
  2250. self.assertEqual(output.status_code, 200)
  2251. data = json.loads(output.data)
  2252. self.assertDictEqual(
  2253. data,
  2254. {'message': 'Assignee reset'}
  2255. )
  2256. # No change
  2257. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2258. issue = pagure.lib.search_issues(self.session, repo, issueid=1)
  2259. self.assertEqual(issue.assignee, None)
  2260. # Un-assign
  2261. data = {'assignee': None}
  2262. output = self.app.post(
  2263. '/api/0/test/issue/1/assign', data=data, headers=headers)
  2264. self.assertEqual(output.status_code, 200)
  2265. data = json.loads(output.data)
  2266. self.assertDictEqual(
  2267. data,
  2268. {'message': 'Nothing to change'}
  2269. )
  2270. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2271. issue = pagure.lib.search_issues(self.session, repo, issueid=1)
  2272. self.assertEqual(issue.assignee, None)
  2273. # Re-assign for the rest of the tests
  2274. data = {'assignee': 'pingou'}
  2275. output = self.app.post(
  2276. '/api/0/test/issue/1/assign', data=data, headers=headers)
  2277. self.assertEqual(output.status_code, 200)
  2278. data = json.loads(output.data)
  2279. self.assertDictEqual(
  2280. data,
  2281. {'message': 'Issue assigned to pingou'}
  2282. )
  2283. # Un-assign
  2284. data = {'assignee': ''}
  2285. output = self.app.post(
  2286. '/api/0/test/issue/1/assign', data=data, headers=headers)
  2287. self.assertEqual(output.status_code, 200)
  2288. data = json.loads(output.data)
  2289. self.assertDictEqual(
  2290. data,
  2291. {'message': 'Assignee reset'}
  2292. )
  2293. # Re-assign for the rest of the tests
  2294. data = {'assignee': 'pingou'}
  2295. output = self.app.post(
  2296. '/api/0/test/issue/1/assign', data=data, headers=headers)
  2297. self.assertEqual(output.status_code, 200)
  2298. data = json.loads(output.data)
  2299. self.assertDictEqual(
  2300. data,
  2301. {'message': 'Issue assigned to pingou'}
  2302. )
  2303. # One comment added
  2304. self.session.commit()
  2305. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2306. issue = pagure.lib.search_issues(self.session, repo, issueid=1)
  2307. self.assertEqual(issue.assignee.user, 'pingou')
  2308. # Create another project
  2309. item = pagure.lib.model.Project(
  2310. user_id=2, # foo
  2311. name='foo',
  2312. description='test project #3',
  2313. hook_token='aaabbbdddeee',
  2314. )
  2315. self.session.add(item)
  2316. self.session.commit()
  2317. # Create a token for pingou for this project
  2318. item = pagure.lib.model.Token(
  2319. id='pingou_foo',
  2320. user_id=1,
  2321. project_id=4,
  2322. expiration=datetime.datetime.utcnow() + datetime.timedelta(
  2323. days=30)
  2324. )
  2325. self.session.add(item)
  2326. self.session.commit()
  2327. # Give `issue_change_status` to this token when `issue_comment`
  2328. # is required
  2329. item = pagure.lib.model.TokenAcl(
  2330. token_id='pingou_foo',
  2331. acl_id=7,
  2332. )
  2333. self.session.add(item)
  2334. self.session.commit()
  2335. repo = pagure.lib.get_authorized_project(self.session, 'foo')
  2336. # Create private issue
  2337. msg = pagure.lib.new_issue(
  2338. session=self.session,
  2339. repo=repo,
  2340. title='Test issue',
  2341. content='We should work on this',
  2342. user='foo',
  2343. ticketfolder=None,
  2344. private=True,
  2345. issue_uid='aaabbbccc#2',
  2346. )
  2347. self.session.commit()
  2348. self.assertEqual(msg.title, 'Test issue')
  2349. # Check before
  2350. repo = pagure.lib.get_authorized_project(self.session, 'foo')
  2351. issue = pagure.lib.search_issues(self.session, repo, issueid=1)
  2352. self.assertEqual(len(issue.comments), 0)
  2353. data = {
  2354. 'assignee': 'pingou',
  2355. }
  2356. headers = {'Authorization': 'token pingou_foo'}
  2357. # Valid request but un-authorized
  2358. output = self.app.post(
  2359. '/api/0/foo/issue/1/assign', data=data, headers=headers)
  2360. self.assertEqual(output.status_code, 401)
  2361. data = json.loads(output.data)
  2362. self.assertEqual(sorted(data.keys()), ['error', 'error_code'])
  2363. self.assertEqual(
  2364. pagure.api.APIERROR.EINVALIDTOK.value, data['error'])
  2365. self.assertEqual(
  2366. pagure.api.APIERROR.EINVALIDTOK.name, data['error_code'])
  2367. # No comment added
  2368. repo = pagure.lib.get_authorized_project(self.session, 'foo')
  2369. issue = pagure.lib.search_issues(self.session, repo, issueid=1)
  2370. self.assertEqual(len(issue.comments), 0)
  2371. # Create token for user foo
  2372. item = pagure.lib.model.Token(
  2373. id='foo_token2',
  2374. user_id=2,
  2375. project_id=4,
  2376. expiration=datetime.datetime.utcnow() + datetime.timedelta(days=30)
  2377. )
  2378. self.session.add(item)
  2379. self.session.commit()
  2380. tests.create_tokens_acl(self.session, token_id='foo_token2')
  2381. data = {
  2382. 'assignee': 'pingou',
  2383. }
  2384. headers = {'Authorization': 'token foo_token2'}
  2385. # Valid request and authorized
  2386. output = self.app.post(
  2387. '/api/0/foo/issue/1/assign', data=data, headers=headers)
  2388. self.assertEqual(output.status_code, 200)
  2389. data = json.loads(output.data)
  2390. self.assertDictEqual(
  2391. data,
  2392. {'message': 'Issue assigned to pingou'}
  2393. )
  2394. @patch('pagure.lib.git.update_git')
  2395. @patch('pagure.lib.notify.send_email')
  2396. def test_api_assign_issue_issuer(self, p_send_email, p_ugt):
  2397. """ Test the api_assign_issue method of the flask api. """
  2398. p_send_email.return_value = True
  2399. p_ugt.return_value = True
  2400. tests.create_projects(self.session)
  2401. tests.create_tokens(self.session, user_id=2)
  2402. tests.create_tokens_acl(self.session)
  2403. headers = {'Authorization': 'token aaabbbcccddd'}
  2404. # Create normal issue
  2405. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2406. msg = pagure.lib.new_issue(
  2407. session=self.session,
  2408. repo=repo,
  2409. title='Test issue #1',
  2410. content='We should work on this',
  2411. user='pingou',
  2412. ticketfolder=None,
  2413. private=False,
  2414. issue_uid='aaabbbccc#1',
  2415. assignee='foo',
  2416. )
  2417. self.session.commit()
  2418. self.assertEqual(msg.title, 'Test issue #1')
  2419. # Check comments before
  2420. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2421. issue = pagure.lib.search_issues(self.session, repo, issueid=1)
  2422. self.assertEqual(len(issue.comments), 0)
  2423. # Un-assign
  2424. data = {'assignee': None}
  2425. output = self.app.post(
  2426. '/api/0/test/issue/1/assign', data={}, headers=headers)
  2427. self.assertEqual(output.status_code, 200)
  2428. data = json.loads(output.data)
  2429. self.assertDictEqual(
  2430. data,
  2431. {'message': 'Assignee reset'}
  2432. )
  2433. # No longer allowed to self-assign since no access
  2434. data = {
  2435. 'assignee': 'foo',
  2436. }
  2437. output = self.app.post(
  2438. '/api/0/test/issue/1/assign', data=data, headers=headers)
  2439. self.assertEqual(output.status_code, 403)
  2440. data = json.loads(output.data)
  2441. self.assertDictEqual(
  2442. data,
  2443. {
  2444. u'error': u'You are not allowed to view this issue',
  2445. u'error_code': u'EISSUENOTALLOWED'
  2446. }
  2447. )
  2448. @patch('pagure.lib.git.update_git')
  2449. @patch('pagure.lib.notify.send_email')
  2450. def test_api_subscribe_issue(self, p_send_email, p_ugt):
  2451. """ Test the api_subscribe_issue method of the flask api. """
  2452. p_send_email.return_value = True
  2453. p_ugt.return_value = True
  2454. item = pagure.lib.model.User(
  2455. user='bar',
  2456. fullname='bar foo',
  2457. password='foo',
  2458. default_email='bar@bar.com',
  2459. )
  2460. self.session.add(item)
  2461. item = pagure.lib.model.UserEmail(
  2462. user_id=3,
  2463. email='bar@bar.com')
  2464. self.session.add(item)
  2465. self.session.commit()
  2466. tests.create_projects(self.session)
  2467. tests.create_tokens(self.session, user_id=3)
  2468. tests.create_tokens_acl(self.session)
  2469. headers = {'Authorization': 'token aaabbbcccddd'}
  2470. # Invalid project
  2471. output = self.app.post(
  2472. '/api/0/foo/issue/1/subscribe', headers=headers)
  2473. self.assertEqual(output.status_code, 404)
  2474. data = json.loads(output.data)
  2475. self.assertDictEqual(
  2476. data,
  2477. {
  2478. "error": "Project not found",
  2479. "error_code": "ENOPROJECT",
  2480. }
  2481. )
  2482. # Valid token, wrong project
  2483. output = self.app.post(
  2484. '/api/0/test2/issue/1/subscribe', headers=headers)
  2485. self.assertEqual(output.status_code, 401)
  2486. data = json.loads(output.data)
  2487. self.assertEqual(sorted(data.keys()), ['error', 'error_code'])
  2488. self.assertEqual(
  2489. pagure.api.APIERROR.EINVALIDTOK.value, data['error'])
  2490. self.assertEqual(
  2491. pagure.api.APIERROR.EINVALIDTOK.name, data['error_code'])
  2492. # No input
  2493. output = self.app.post(
  2494. '/api/0/test/issue/1/subscribe', headers=headers)
  2495. self.assertEqual(output.status_code, 404)
  2496. data = json.loads(output.data)
  2497. self.assertDictEqual(
  2498. data,
  2499. {
  2500. "error": "Issue not found",
  2501. "error_code": "ENOISSUE",
  2502. }
  2503. )
  2504. # Create normal issue
  2505. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2506. msg = pagure.lib.new_issue(
  2507. session=self.session,
  2508. repo=repo,
  2509. title='Test issue #1',
  2510. content='We should work on this',
  2511. user='foo',
  2512. ticketfolder=None,
  2513. private=False,
  2514. issue_uid='aaabbbccc#1',
  2515. )
  2516. self.session.commit()
  2517. self.assertEqual(msg.title, 'Test issue #1')
  2518. # Check subscribtion before
  2519. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2520. issue = pagure.lib.search_issues(self.session, repo, issueid=1)
  2521. self.assertEqual(
  2522. pagure.lib.get_watch_list(self.session, issue),
  2523. set(['pingou', 'foo']))
  2524. # Unsubscribe - no changes
  2525. data = {}
  2526. output = self.app.post(
  2527. '/api/0/test/issue/1/subscribe', data=data, headers=headers)
  2528. self.assertEqual(output.status_code, 200)
  2529. data = json.loads(output.data)
  2530. self.assertDictEqual(
  2531. data,
  2532. {'message': 'You are no longer watching this issue'}
  2533. )
  2534. data = {}
  2535. output = self.app.post(
  2536. '/api/0/test/issue/1/subscribe', data=data, headers=headers)
  2537. self.assertEqual(output.status_code, 200)
  2538. data = json.loads(output.data)
  2539. self.assertDictEqual(
  2540. data,
  2541. {'message': 'You are no longer watching this issue'}
  2542. )
  2543. # No change
  2544. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2545. issue = pagure.lib.search_issues(self.session, repo, issueid=1)
  2546. self.assertEqual(
  2547. pagure.lib.get_watch_list(self.session, issue),
  2548. set(['pingou', 'foo']))
  2549. # Subscribe
  2550. data = {'status': True}
  2551. output = self.app.post(
  2552. '/api/0/test/issue/1/subscribe', data=data, headers=headers)
  2553. self.assertEqual(output.status_code, 200)
  2554. data = json.loads(output.data)
  2555. self.assertDictEqual(
  2556. data,
  2557. {'message': 'You are now watching this issue'}
  2558. )
  2559. # Subscribe - no changes
  2560. data = {'status': True}
  2561. output = self.app.post(
  2562. '/api/0/test/issue/1/subscribe', data=data, headers=headers)
  2563. self.assertEqual(output.status_code, 200)
  2564. data = json.loads(output.data)
  2565. self.assertDictEqual(
  2566. data,
  2567. {'message': 'You are now watching this issue'}
  2568. )
  2569. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2570. issue = pagure.lib.search_issues(self.session, repo, issueid=1)
  2571. self.assertEqual(
  2572. pagure.lib.get_watch_list(self.session, issue),
  2573. set(['pingou', 'foo', 'bar']))
  2574. # Unsubscribe
  2575. data = {}
  2576. output = self.app.post(
  2577. '/api/0/test/issue/1/subscribe', data=data, headers=headers)
  2578. self.assertEqual(output.status_code, 200)
  2579. data = json.loads(output.data)
  2580. self.assertDictEqual(
  2581. data,
  2582. {'message': 'You are no longer watching this issue'}
  2583. )
  2584. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2585. issue = pagure.lib.search_issues(self.session, repo, issueid=1)
  2586. self.assertEqual(
  2587. pagure.lib.get_watch_list(self.session, issue),
  2588. set(['pingou', 'foo']))
  2589. def test_api_update_custom_field(self):
  2590. """ Test the api_update_custom_field method of the flask api. """
  2591. tests.create_projects(self.session)
  2592. tests.create_projects_git(os.path.join(self.path, 'tickets'))
  2593. tests.create_tokens(self.session)
  2594. tests.create_tokens_acl(self.session)
  2595. headers = {'Authorization': 'token aaabbbcccddd'}
  2596. # Invalid project
  2597. output = self.app.post(
  2598. '/api/0/foo/issue/1/custom/bugzilla', headers=headers)
  2599. self.assertEqual(output.status_code, 404)
  2600. data = json.loads(output.data)
  2601. self.assertDictEqual(
  2602. data,
  2603. {
  2604. "error": "Project not found",
  2605. "error_code": "ENOPROJECT",
  2606. }
  2607. )
  2608. # Valid token, wrong project
  2609. output = self.app.post(
  2610. '/api/0/test2/issue/1/custom/bugzilla', headers=headers)
  2611. self.assertEqual(output.status_code, 401)
  2612. data = json.loads(output.data)
  2613. self.assertEqual(sorted(data.keys()), ['error', 'error_code'])
  2614. self.assertEqual(
  2615. pagure.api.APIERROR.EINVALIDTOK.value, data['error'])
  2616. self.assertEqual(
  2617. pagure.api.APIERROR.EINVALIDTOK.name, data['error_code'])
  2618. # No issue
  2619. output = self.app.post(
  2620. '/api/0/test/issue/1/custom/bugzilla', headers=headers)
  2621. self.assertEqual(output.status_code, 404)
  2622. data = json.loads(output.data)
  2623. self.assertDictEqual(
  2624. data,
  2625. {
  2626. "error": "Issue not found",
  2627. "error_code": "ENOISSUE",
  2628. }
  2629. )
  2630. # Create normal issue
  2631. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2632. msg = pagure.lib.new_issue(
  2633. session=self.session,
  2634. repo=repo,
  2635. title='Test issue #1',
  2636. content='We should work on this',
  2637. user='pingou',
  2638. ticketfolder=None,
  2639. private=False,
  2640. )
  2641. self.session.commit()
  2642. self.assertEqual(msg.title, 'Test issue #1')
  2643. # Project does not have this custom field
  2644. output = self.app.post(
  2645. '/api/0/test/issue/1/custom/bugzilla', headers=headers)
  2646. self.assertEqual(output.status_code, 400)
  2647. data = json.loads(output.data)
  2648. self.assertDictEqual(
  2649. data,
  2650. {
  2651. "error": "Invalid custom field submitted",
  2652. "error_code": "EINVALIDISSUEFIELD",
  2653. }
  2654. )
  2655. # Check the behavior if the project disabled the issue tracker
  2656. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2657. settings = repo.settings
  2658. settings['issue_tracker'] = False
  2659. repo.settings = settings
  2660. self.session.add(repo)
  2661. self.session.commit()
  2662. output = self.app.post(
  2663. '/api/0/test/issue/1/custom/bugzilla', headers=headers)
  2664. self.assertEqual(output.status_code, 404)
  2665. data = json.loads(output.data)
  2666. self.assertDictEqual(
  2667. data,
  2668. {
  2669. "error": "Issue tracker disabled for this project",
  2670. "error_code": "ETRACKERDISABLED",
  2671. }
  2672. )
  2673. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2674. settings = repo.settings
  2675. settings['issue_tracker'] = True
  2676. repo.settings = settings
  2677. self.session.add(repo)
  2678. self.session.commit()
  2679. # Invalid API token
  2680. headers = {'Authorization': 'token foobar'}
  2681. output = self.app.post(
  2682. '/api/0/test/issue/1/custom/bugzilla', headers=headers)
  2683. self.assertEqual(output.status_code, 401)
  2684. data = json.loads(output.data)
  2685. self.assertEqual(sorted(data.keys()), ['error', 'error_code'])
  2686. self.assertEqual(
  2687. pagure.api.APIERROR.EINVALIDTOK.value, data['error'])
  2688. self.assertEqual(
  2689. pagure.api.APIERROR.EINVALIDTOK.name, data['error_code'])
  2690. headers = {'Authorization': 'token aaabbbcccddd'}
  2691. # Set some custom fields
  2692. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2693. msg = pagure.lib.set_custom_key_fields(
  2694. self.session, repo,
  2695. ['bugzilla', 'upstream', 'reviewstatus'],
  2696. ['link', 'boolean', 'list'],
  2697. ['unused data for non-list type', '', 'ack, nack , needs review'],
  2698. [None, None, None])
  2699. self.session.commit()
  2700. self.assertEqual(msg, 'List of custom fields updated')
  2701. # Check the project custom fields were correctly set
  2702. for key in repo.issue_keys:
  2703. # Check that the bugzilla field correctly had its data removed
  2704. if key.name == "bugzilla":
  2705. self.assertIsNone(key.data)
  2706. # Check that the reviewstatus list field still has its list
  2707. if key.name == "reviewstatus":
  2708. self.assertEqual(
  2709. sorted(key.data), ['ack', 'nack', 'needs review'])
  2710. # Check that not setting the value on a non-existing custom field
  2711. # changes nothing
  2712. output = self.app.post(
  2713. '/api/0/test/issue/1/custom/bugzilla', headers=headers)
  2714. self.assertEqual(output.status_code, 200)
  2715. data = json.loads(output.data)
  2716. self.assertDictEqual(
  2717. data,
  2718. {
  2719. 'message': 'No changes'
  2720. }
  2721. )
  2722. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2723. issue = pagure.lib.search_issues(self.session, repo, issueid=1)
  2724. self.assertEqual(issue.other_fields, [])
  2725. self.assertEqual(len(issue.other_fields), 0)
  2726. # Invalid value
  2727. output = self.app.post(
  2728. '/api/0/test/issue/1/custom/bugzilla', headers=headers,
  2729. data={'value': 'foobar'})
  2730. self.assertEqual(output.status_code, 400)
  2731. data = json.loads(output.data)
  2732. self.assertDictEqual(
  2733. data,
  2734. {
  2735. "error": "Invalid custom field submitted, the value is not "
  2736. "a link",
  2737. "error_code": "EINVALIDISSUEFIELD_LINK",
  2738. }
  2739. )
  2740. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2741. issue = pagure.lib.search_issues(self.session, repo, issueid=1)
  2742. self.assertEqual(issue.other_fields, [])
  2743. self.assertEqual(len(issue.other_fields), 0)
  2744. # All good
  2745. output = self.app.post(
  2746. '/api/0/test/issue/1/custom/bugzilla', headers=headers,
  2747. data={'value': 'https://bugzilla.redhat.com/1234'})
  2748. self.assertEqual(output.status_code, 200)
  2749. data = json.loads(output.data)
  2750. self.assertDictEqual(
  2751. data,
  2752. {
  2753. "message": "Custom field bugzilla adjusted to "
  2754. "https://bugzilla.redhat.com/1234"
  2755. }
  2756. )
  2757. self.session.commit()
  2758. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2759. issue = pagure.lib.search_issues(self.session, repo, issueid=1)
  2760. self.assertEqual(len(issue.other_fields), 1)
  2761. self.assertEqual(issue.other_fields[0].key.name, 'bugzilla')
  2762. self.assertEqual(
  2763. issue.other_fields[0].value,
  2764. 'https://bugzilla.redhat.com/1234')
  2765. # Reset the value
  2766. output = self.app.post(
  2767. '/api/0/test/issue/1/custom/bugzilla', headers=headers,
  2768. data={'value': ''})
  2769. self.assertEqual(output.status_code, 200)
  2770. data = json.loads(output.data)
  2771. self.assertDictEqual(
  2772. data,
  2773. {
  2774. "message": "Custom field bugzilla reset "
  2775. "(from https://bugzilla.redhat.com/1234)"
  2776. }
  2777. )
  2778. self.session.commit()
  2779. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2780. issue = pagure.lib.search_issues(self.session, repo, issueid=1)
  2781. self.assertEqual(len(issue.other_fields), 0)
  2782. def test_api_view_issues_history_stats(self):
  2783. """ Test the api_view_issues_history_stats method of the flask api. """
  2784. self.test_api_new_issue()
  2785. # Create private issue
  2786. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2787. msg = pagure.lib.new_issue(
  2788. session=self.session,
  2789. repo=repo,
  2790. title='Test issue',
  2791. content='We should work on this',
  2792. user='pingou',
  2793. ticketfolder=None,
  2794. private=True,
  2795. status="Closed"
  2796. )
  2797. self.session.commit()
  2798. self.assertEqual(msg.title, 'Test issue')
  2799. output = self.app.get('/api/0/test/issues/history/stats')
  2800. self.assertEqual(output.status_code, 200)
  2801. data = json.loads(output.data)
  2802. self.assertEqual(len(data), 1)
  2803. self.assertEqual(len(data['stats']), 53)
  2804. last_key = sorted(data['stats'].keys())[-1]
  2805. self.assertEqual(data['stats'][last_key], 7)
  2806. for k in sorted(data['stats'].keys())[:-1]:
  2807. self.assertEqual(data['stats'][k], 0)
  2808. def test_api_view_user_issues_pingou(self):
  2809. """ Test the api_view_user_issues method of the flask api for pingou.
  2810. """
  2811. self.test_api_new_issue()
  2812. # Create private issue
  2813. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2814. msg = pagure.lib.new_issue(
  2815. session=self.session,
  2816. repo=repo,
  2817. title='Test issue',
  2818. content='We should work on this',
  2819. user='pingou',
  2820. ticketfolder=None,
  2821. private=True,
  2822. status="Closed"
  2823. )
  2824. self.session.commit()
  2825. self.assertEqual(msg.title, 'Test issue')
  2826. output = self.app.get('/api/0/user/pingou/issues')
  2827. self.assertEqual(output.status_code, 200)
  2828. data = json.loads(output.data)
  2829. args = {
  2830. u"assignee": True,
  2831. u"author": True,
  2832. "milestones": [],
  2833. "no_stones": None,
  2834. "order": None,
  2835. "order_key": None,
  2836. "page": 1,
  2837. "since": None,
  2838. "status": None,
  2839. "tags": []
  2840. }
  2841. self.assertEqual(data['args'], args)
  2842. self.assertEqual(data['issues_assigned'], [])
  2843. self.assertEqual(len(data['issues_created']), 8)
  2844. self.assertEqual(data['total_issues_assigned'], 0)
  2845. self.assertEqual(data['total_issues_created'], 8)
  2846. self.assertEqual(data['total_issues_assigned_pages'], 1)
  2847. self.assertEqual(data['total_issues_created_pages'], 1)
  2848. # Restrict to a certain, fake milestone
  2849. output = self.app.get('/api/0/user/pingou/issues?milestones=v1.0')
  2850. self.assertEqual(output.status_code, 200)
  2851. data = json.loads(output.data)
  2852. args = {
  2853. u"assignee": True,
  2854. u"author": True,
  2855. "milestones": ['v1.0'],
  2856. "no_stones": None,
  2857. "order": None,
  2858. "order_key": None,
  2859. "page": 1,
  2860. "since": None,
  2861. "status": None,
  2862. "tags": []
  2863. }
  2864. self.assertEqual(data['args'], args)
  2865. self.assertEqual(data['issues_assigned'], [])
  2866. self.assertEqual(data['issues_created'], [])
  2867. self.assertEqual(data['total_issues_assigned'], 0)
  2868. self.assertEqual(data['total_issues_created'], 0)
  2869. self.assertEqual(data['total_issues_assigned_pages'], 1)
  2870. self.assertEqual(data['total_issues_created_pages'], 1)
  2871. # Restrict to a certain status
  2872. output = self.app.get('/api/0/user/pingou/issues?status=closed')
  2873. self.assertEqual(output.status_code, 200)
  2874. data = json.loads(output.data)
  2875. args = {
  2876. u"assignee": True,
  2877. u"author": True,
  2878. "milestones": [],
  2879. "no_stones": None,
  2880. "order": None,
  2881. "order_key": None,
  2882. "page": 1,
  2883. "since": None,
  2884. "status": 'closed',
  2885. "tags": []
  2886. }
  2887. self.assertEqual(data['args'], args)
  2888. self.assertEqual(data['issues_assigned'], [])
  2889. self.assertEqual(len(data['issues_created']), 1)
  2890. self.assertEqual(data['total_issues_assigned'], 0)
  2891. self.assertEqual(data['total_issues_created'], 1)
  2892. self.assertEqual(data['total_issues_assigned_pages'], 1)
  2893. self.assertEqual(data['total_issues_created_pages'], 1)
  2894. # Restrict to a certain status
  2895. output = self.app.get('/api/0/user/pingou/issues?status=all')
  2896. self.assertEqual(output.status_code, 200)
  2897. data = json.loads(output.data)
  2898. args = {
  2899. u"assignee": True,
  2900. u"author": True,
  2901. "milestones": [],
  2902. "no_stones": None,
  2903. "order": None,
  2904. "order_key": None,
  2905. "page": 1,
  2906. "since": None,
  2907. "status": 'all',
  2908. "tags": []
  2909. }
  2910. self.assertEqual(data['args'], args)
  2911. self.assertEqual(data['issues_assigned'], [])
  2912. self.assertEqual(len(data['issues_created']), 9)
  2913. self.assertEqual(data['total_issues_assigned'], 0)
  2914. self.assertEqual(data['total_issues_created'], 9)
  2915. self.assertEqual(data['total_issues_assigned_pages'], 1)
  2916. self.assertEqual(data['total_issues_created_pages'], 1)
  2917. def test_api_view_user_issues_foo(self):
  2918. """ Test the api_view_user_issues method of the flask api for foo.
  2919. """
  2920. self.test_api_new_issue()
  2921. # Create private issue
  2922. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2923. msg = pagure.lib.new_issue(
  2924. session=self.session,
  2925. repo=repo,
  2926. title='Test issue',
  2927. content='We should work on this',
  2928. user='pingou',
  2929. ticketfolder=None,
  2930. private=True,
  2931. status="Closed"
  2932. )
  2933. self.session.commit()
  2934. self.assertEqual(msg.title, 'Test issue')
  2935. output = self.app.get('/api/0/user/foo/issues')
  2936. self.assertEqual(output.status_code, 200)
  2937. data = json.loads(output.data)
  2938. args = {
  2939. u"assignee": True,
  2940. u"author": True,
  2941. u"milestones": [],
  2942. u"no_stones": None,
  2943. u"order": None,
  2944. u"order_key": None,
  2945. u"page": 1,
  2946. u"since": None,
  2947. u"status": None,
  2948. u"tags": [],
  2949. }
  2950. self.assertEqual(data['args'], args)
  2951. self.assertEqual(len(data['issues_assigned']), 1)
  2952. self.assertEqual(data['issues_created'], [])
  2953. self.assertEqual(data['total_issues_assigned'], 1)
  2954. self.assertEqual(data['total_issues_created'], 0)
  2955. self.assertEqual(data['total_issues_assigned_pages'], 1)
  2956. self.assertEqual(data['total_issues_created_pages'], 1)
  2957. def test_api_view_user_issues_foo_invalid_page(self):
  2958. """ Test the api_view_user_issues method of the flask api for foo.
  2959. """
  2960. self.test_api_new_issue()
  2961. output = self.app.get('/api/0/user/foo/issues?page=0')
  2962. self.assertEqual(output.status_code, 400)
  2963. data = json.loads(output.data)
  2964. self.assertEqual(
  2965. data,
  2966. {
  2967. u'error': u'Invalid page requested',
  2968. u'error_code': u'ENOCODE'
  2969. }
  2970. )
  2971. output = self.app.get('/api/0/user/foo/issues?page=abc')
  2972. self.assertEqual(output.status_code, 400)
  2973. data = json.loads(output.data)
  2974. self.assertEqual(
  2975. data,
  2976. {
  2977. u'error': u'Invalid page requested',
  2978. u'error_code': u'ENOCODE'
  2979. }
  2980. )
  2981. def test_api_view_user_issues_foo_no_assignee(self):
  2982. """ Test the api_view_user_issues method of the flask api for foo.
  2983. """
  2984. self.test_api_new_issue()
  2985. output = self.app.get('/api/0/user/foo/issues?assignee=0')
  2986. self.assertEqual(output.status_code, 200)
  2987. data = json.loads(output.data)
  2988. args = {
  2989. u"assignee": False,
  2990. u"author": True,
  2991. u"milestones": [],
  2992. u"no_stones": None,
  2993. u"order": None,
  2994. u"order_key": None,
  2995. u"page": 1,
  2996. u"since": None,
  2997. u"status": None,
  2998. u"tags": [],
  2999. }
  3000. self.assertEqual(data['args'], args)
  3001. self.assertEqual(data['issues_assigned'], [])
  3002. self.assertEqual(data['issues_created'], [])
  3003. self.assertEqual(data['total_issues_assigned'], 0)
  3004. self.assertEqual(data['total_issues_created'], 0)
  3005. self.assertEqual(data['total_issues_assigned_pages'], 1)
  3006. self.assertEqual(data['total_issues_created_pages'], 1)
  3007. def test_api_view_user_issues_pingou_no_author(self):
  3008. """ Test the api_view_user_issues method of the flask api for pingou.
  3009. """
  3010. self.test_api_new_issue()
  3011. output = self.app.get('/api/0/user/pingou/issues?author=0')
  3012. self.assertEqual(output.status_code, 200)
  3013. data = json.loads(output.data)
  3014. args = {
  3015. u"assignee": True,
  3016. u"author": False,
  3017. "milestones": [],
  3018. "no_stones": None,
  3019. "order": None,
  3020. "order_key": None,
  3021. "page": 1,
  3022. "since": None,
  3023. "status": None,
  3024. "tags": []
  3025. }
  3026. self.assertEqual(data['args'], args)
  3027. self.assertEqual(data['issues_assigned'], [])
  3028. self.assertEqual(data['issues_created'], [])
  3029. self.assertEqual(data['total_issues_assigned'], 0)
  3030. self.assertEqual(data['total_issues_created'], 0)
  3031. self.assertEqual(data['total_issues_assigned_pages'], 1)
  3032. self.assertEqual(data['total_issues_created_pages'], 1)
  3033. if __name__ == '__main__':
  3034. unittest.main(verbosity=2)