test_pagure_flask_api_issue.py 143 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292
  1. # -*- coding: utf-8 -*-
  2. """
  3. (c) 2015 - Copyright Red Hat Inc
  4. Authors:
  5. Pierre-Yves Chibon <pingou@pingoured.fr>
  6. """
  7. from __future__ import unicode_literals
  8. __requires__ = ['SQLAlchemy >= 0.8']
  9. import pkg_resources
  10. import arrow
  11. import copy
  12. import datetime
  13. import unittest
  14. import shutil
  15. import sys
  16. import time
  17. import os
  18. import flask
  19. import json
  20. import munch
  21. from mock import patch, MagicMock
  22. from sqlalchemy.exc import SQLAlchemyError
  23. sys.path.insert(0, os.path.join(os.path.dirname(
  24. os.path.abspath(__file__)), '..'))
  25. import pagure.lib.query
  26. import tests
  27. FULL_ISSUE_LIST = [
  28. {
  29. "assignee": None,
  30. "blocks": [],
  31. "close_status": None,
  32. "closed_at": None,
  33. "closed_by": None,
  34. "comments": [],
  35. "content": "We should work on this",
  36. "custom_fields": [],
  37. "date_created": "1431414800",
  38. "depends": [],
  39. "id": 2,
  40. "last_updated": "1431414800",
  41. "milestone": None,
  42. "priority": None,
  43. "private": True,
  44. "status": "Closed",
  45. "tags": [],
  46. "title": "Test issue",
  47. "user": {
  48. "fullname": "PY C",
  49. "name": "pingou"
  50. }
  51. },
  52. {
  53. "assignee": {'fullname': 'foo bar', 'name': 'foo'},
  54. "blocks": [],
  55. "close_status": None,
  56. "closed_at": None,
  57. "closed_by": None,
  58. "comments": [],
  59. "content": "This issue needs attention",
  60. "custom_fields": [],
  61. "date_created": "1431414800",
  62. "depends": [],
  63. "id": 8,
  64. "last_updated": "1431414800",
  65. "milestone": None,
  66. "priority": None,
  67. "private": True,
  68. "status": "Open",
  69. "tags": [],
  70. "title": "test issue1",
  71. "user": {
  72. "fullname": "PY C",
  73. "name": "pingou"
  74. }
  75. },
  76. {
  77. "assignee": None,
  78. "blocks": [],
  79. "close_status": None,
  80. "closed_at": None,
  81. "closed_by": None,
  82. "comments": [],
  83. "content": "This issue needs attention",
  84. "custom_fields": [],
  85. "date_created": "1431414800",
  86. "depends": [],
  87. "id": 7,
  88. "last_updated": "1431414800",
  89. "milestone": None,
  90. "priority": None,
  91. "private": True,
  92. "status": "Open",
  93. "tags": [],
  94. "title": "test issue",
  95. "user": {
  96. "fullname": "PY C",
  97. "name": "pingou"
  98. }
  99. },
  100. {
  101. "assignee": None,
  102. "blocks": [],
  103. "close_status": None,
  104. "closed_at": None,
  105. "closed_by": None,
  106. "comments": [],
  107. "content": "This issue needs attention",
  108. "custom_fields": [],
  109. "date_created": "1431414800",
  110. "depends": [],
  111. "id": 6,
  112. "last_updated": "1431414800",
  113. "milestone": None,
  114. "priority": None,
  115. "private": False,
  116. "status": "Open",
  117. "tags": [],
  118. "title": "test issue",
  119. "user": {
  120. "fullname": "PY C",
  121. "name": "pingou"
  122. }
  123. },
  124. {
  125. "assignee": None,
  126. "blocks": [],
  127. "close_status": None,
  128. "closed_at": None,
  129. "closed_by": None,
  130. "comments": [],
  131. "content": "This issue needs attention",
  132. "custom_fields": [],
  133. "date_created": "1431414800",
  134. "depends": [],
  135. "id": 5,
  136. "last_updated": "1431414800",
  137. "milestone": None,
  138. "priority": None,
  139. "private": False,
  140. "status": "Open",
  141. "tags": [],
  142. "title": "test issue",
  143. "user": {
  144. "fullname": "PY C",
  145. "name": "pingou"
  146. }
  147. },
  148. {
  149. "assignee": None,
  150. "blocks": [],
  151. "close_status": None,
  152. "closed_at": None,
  153. "closed_by": None,
  154. "comments": [],
  155. "content": "This issue needs attention",
  156. "custom_fields": [],
  157. "date_created": "1431414800",
  158. "depends": [],
  159. "id": 4,
  160. "last_updated": "1431414800",
  161. "milestone": None,
  162. "priority": None,
  163. "private": False,
  164. "status": "Open",
  165. "tags": [],
  166. "title": "test issue",
  167. "user": {
  168. "fullname": "PY C",
  169. "name": "pingou"
  170. }
  171. },
  172. {
  173. "assignee": None,
  174. "blocks": [],
  175. "close_status": None,
  176. "closed_at": None,
  177. "closed_by": None,
  178. "comments": [],
  179. "content": "This issue needs attention",
  180. "custom_fields": [],
  181. "date_created": "1431414800",
  182. "depends": [],
  183. "id": 3,
  184. "last_updated": "1431414800",
  185. "milestone": None,
  186. "priority": None,
  187. "private": False,
  188. "status": "Open",
  189. "tags": [],
  190. "title": "test issue",
  191. "user": {
  192. "fullname": "PY C",
  193. "name": "pingou"
  194. }
  195. },
  196. {
  197. "assignee": None,
  198. "blocks": [],
  199. "close_status": None,
  200. "closed_at": None,
  201. "closed_by": None,
  202. "comments": [],
  203. "content": "This issue needs attention",
  204. "custom_fields": [],
  205. "date_created": "1431414800",
  206. "depends": [],
  207. "id": 2,
  208. "last_updated": "1431414800",
  209. "milestone": "milestone-1.0",
  210. "priority": None,
  211. "private": False,
  212. "status": "Open",
  213. "tags": [],
  214. "title": "test issue",
  215. "user": {
  216. "fullname": "PY C",
  217. "name": "pingou"
  218. }
  219. },
  220. {
  221. "assignee": None,
  222. "blocks": [],
  223. "close_status": None,
  224. "closed_at": None,
  225. "closed_by": None,
  226. "comments": [],
  227. "content": "This issue needs attention",
  228. "custom_fields": [],
  229. "date_created": "1431414800",
  230. "depends": [],
  231. "id": 1,
  232. "last_updated": "1431414800",
  233. "milestone": None,
  234. "priority": None,
  235. "private": False,
  236. "status": "Open",
  237. "tags": [],
  238. "title": "test issue",
  239. "user": {
  240. "fullname": "PY C",
  241. "name": "pingou"
  242. }
  243. }
  244. ]
  245. LCL_ISSUES = [
  246. {
  247. 'assignee': None,
  248. 'blocks': [],
  249. 'close_status': None,
  250. 'closed_at': None,
  251. "closed_by": None,
  252. 'comments': [],
  253. 'content': 'Description',
  254. 'custom_fields': [],
  255. 'date_created': '1431414800',
  256. 'depends': [],
  257. 'id': 2,
  258. 'last_updated': '1431414800',
  259. 'milestone': None,
  260. 'priority': None,
  261. 'private': False,
  262. 'status': 'Open',
  263. 'tags': [],
  264. 'title': 'Issue #2',
  265. 'user': {'fullname': 'PY C', 'name': 'pingou'}
  266. },
  267. {
  268. 'assignee': None,
  269. 'blocks': [],
  270. 'close_status': None,
  271. 'closed_at': None,
  272. "closed_by": None,
  273. 'comments': [],
  274. 'content': 'Description',
  275. 'custom_fields': [],
  276. 'date_created': '1431414800',
  277. 'depends': [],
  278. 'id': 1,
  279. 'last_updated': '1431414800',
  280. 'milestone': None,
  281. 'priority': None,
  282. 'private': False,
  283. 'status': 'Open',
  284. 'tags': [],
  285. 'title': 'Issue #1',
  286. 'user': {'fullname': 'PY C', 'name': 'pingou'}
  287. }
  288. ]
  289. class PagureFlaskApiIssuetests(tests.SimplePagureTest):
  290. """ Tests for the flask API of pagure for issue """
  291. maxDiff = None
  292. def setUp(self):
  293. """ Set up the environnment, ran before every tests. """
  294. super(PagureFlaskApiIssuetests, self).setUp()
  295. pagure.config.config['TICKETS_FOLDER'] = None
  296. def test_api_new_issue_wrong_token(self):
  297. """ Test the api_new_issue method of the flask api. """
  298. tests.create_projects(self.session)
  299. tests.create_projects_git(
  300. os.path.join(self.path, 'tickets'), bare=True)
  301. tests.create_tokens(self.session)
  302. tests.create_tokens_acl(self.session)
  303. headers = {'Authorization': 'token aaabbbcccddd'}
  304. # Valid token, wrong project
  305. output = self.app.post('/api/0/test2/new_issue', headers=headers)
  306. self.assertEqual(output.status_code, 401)
  307. data = json.loads(output.get_data(as_text=True))
  308. self.assertEqual(sorted(data.keys()), ['error', 'error_code'])
  309. self.assertEqual(
  310. pagure.api.APIERROR.EINVALIDTOK.value, data['error'])
  311. self.assertEqual(
  312. pagure.api.APIERROR.EINVALIDTOK.name, data['error_code'])
  313. def test_api_new_issue_no_input(self):
  314. """ Test the api_new_issue method of the flask api. """
  315. tests.create_projects(self.session)
  316. tests.create_projects_git(
  317. os.path.join(self.path, 'tickets'), bare=True)
  318. tests.create_tokens(self.session)
  319. tests.create_tokens_acl(self.session)
  320. headers = {'Authorization': 'token aaabbbcccddd'}
  321. # No input
  322. output = self.app.post('/api/0/test/new_issue', headers=headers)
  323. self.assertEqual(output.status_code, 400)
  324. data = json.loads(output.get_data(as_text=True))
  325. self.assertDictEqual(
  326. data,
  327. {
  328. "error": "Invalid or incomplete input submitted",
  329. "error_code": "EINVALIDREQ",
  330. "errors": {
  331. "issue_content": ["This field is required."],
  332. "title": ["This field is required."],
  333. }
  334. }
  335. )
  336. def test_api_new_issue_invalid_repo(self):
  337. """ Test the api_new_issue method of the flask api. """
  338. tests.create_projects(self.session)
  339. tests.create_projects_git(
  340. os.path.join(self.path, 'tickets'), bare=True)
  341. tests.create_tokens(self.session)
  342. tests.create_tokens_acl(self.session)
  343. headers = {'Authorization': 'token aaabbbcccddd'}
  344. data = {
  345. 'title': 'test issue'
  346. }
  347. # Invalid repo
  348. output = self.app.post(
  349. '/api/0/foo/new_issue', data=data, headers=headers)
  350. self.assertEqual(output.status_code, 404)
  351. data = json.loads(output.get_data(as_text=True))
  352. self.assertDictEqual(
  353. data,
  354. {
  355. "error": "Project not found",
  356. "error_code": "ENOPROJECT",
  357. }
  358. )
  359. def test_api_new_issue_invalid_request(self):
  360. """ Test the api_new_issue method of the flask api. """
  361. tests.create_projects(self.session)
  362. tests.create_projects_git(
  363. os.path.join(self.path, 'tickets'), bare=True)
  364. tests.create_tokens(self.session)
  365. tests.create_tokens_acl(self.session)
  366. headers = {'Authorization': 'token aaabbbcccddd'}
  367. # Incomplete request
  368. output = self.app.post(
  369. '/api/0/test/new_issue', data={}, headers=headers)
  370. self.assertEqual(output.status_code, 400)
  371. data = json.loads(output.get_data(as_text=True))
  372. self.assertDictEqual(
  373. data,
  374. {
  375. "error": "Invalid or incomplete input submitted",
  376. "error_code": "EINVALIDREQ",
  377. "errors": {
  378. "issue_content": ["This field is required."],
  379. "title": ["This field is required."]
  380. }
  381. }
  382. )
  383. def test_api_new_issue(self):
  384. """ Test the api_new_issue method of the flask api. """
  385. tests.create_projects(self.session)
  386. tests.create_projects_git(
  387. os.path.join(self.path, 'tickets'), bare=True)
  388. tests.create_tokens(self.session)
  389. tests.create_tokens_acl(self.session)
  390. headers = {'Authorization': 'token aaabbbcccddd'}
  391. data = {
  392. 'title': 'test issue',
  393. 'issue_content': 'This issue needs attention',
  394. }
  395. # Valid request
  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.get_data(as_text=True))
  400. data['issue']['date_created'] = '1431414800'
  401. data['issue']['last_updated'] = '1431414800'
  402. self.assertDictEqual(
  403. data,
  404. {
  405. "issue": FULL_ISSUE_LIST[8],
  406. "message": "Issue created"
  407. }
  408. )
  409. def test_api_new_issue_img(self):
  410. """ Test the api_new_issue method of the flask api. """
  411. tests.create_projects(self.session)
  412. tests.create_projects_git(
  413. os.path.join(self.path, 'tickets'), bare=True)
  414. tests.create_tokens(self.session)
  415. tests.create_tokens_acl(self.session)
  416. headers = {'Authorization': 'token aaabbbcccddd'}
  417. with open(os.path.join(tests.HERE, 'placebo.png'), 'rb') as stream:
  418. data = {
  419. 'title': 'test issue',
  420. 'issue_content': 'This issue needs attention <!!image>',
  421. 'filestream': stream,
  422. }
  423. # Valid request
  424. output = self.app.post(
  425. '/api/0/test/new_issue', data=data, headers=headers)
  426. self.assertEqual(output.status_code, 200)
  427. data = json.loads(output.get_data(as_text=True))
  428. data['issue']['date_created'] = '1431414800'
  429. data['issue']['last_updated'] = '1431414800'
  430. issue = copy.deepcopy(FULL_ISSUE_LIST[8])
  431. issue['id'] = 1
  432. self.assertIn(
  433. 'pagure_tests_placebo.png)](/test/issue/raw/files/'
  434. '8a06845923010b27bfd8e7e75acff7badc40d1021b4994e01f5e11ca'
  435. '40bc3abe', data['issue']['content']
  436. )
  437. data['issue']['content'] = 'This issue needs attention'
  438. self.assertDictEqual(
  439. data,
  440. {
  441. "issue": issue,
  442. "message": "Issue created"
  443. }
  444. )
  445. def test_api_new_issue_invalid_milestone(self):
  446. """ Test the api_new_issue method of the flask api. """
  447. tests.create_projects(self.session)
  448. tests.create_projects_git(
  449. os.path.join(self.path, 'tickets'), bare=True)
  450. tests.create_tokens(self.session)
  451. tests.create_tokens_acl(self.session)
  452. headers = {'Authorization': 'token aaabbbcccddd'}
  453. # Valid request but invalid milestone
  454. data = {
  455. 'title': 'test issue',
  456. 'issue_content': 'This issue needs attention',
  457. 'milestone': ['milestone-1.0'],
  458. }
  459. output = self.app.post(
  460. '/api/0/test/new_issue', data=data, headers=headers)
  461. self.assertEqual(output.status_code, 400)
  462. data = json.loads(output.get_data(as_text=True))
  463. self.assertDictEqual(
  464. data,
  465. {
  466. "error": "Invalid or incomplete input submitted",
  467. "error_code": "EINVALIDREQ",
  468. "errors": {
  469. "milestone": [
  470. "Not a valid choice"
  471. ]
  472. }
  473. }
  474. )
  475. def test_api_new_issue_milestone(self):
  476. """ Test the api_new_issue method of the flask api. """
  477. tests.create_projects(self.session)
  478. tests.create_projects_git(
  479. os.path.join(self.path, 'tickets'), bare=True)
  480. tests.create_tokens(self.session)
  481. tests.create_tokens_acl(self.session)
  482. headers = {'Authorization': 'token aaabbbcccddd'}
  483. # Set some milestones
  484. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  485. repo.milestones = {'milestone-1.0': '', 'milestone-2.0': 'Tomorrow!'}
  486. self.session.add(repo)
  487. self.session.commit()
  488. # Valid request with milestone
  489. data = {
  490. 'title': 'test issue',
  491. 'issue_content': 'This issue needs attention',
  492. 'milestone': ['milestone-1.0'],
  493. }
  494. output = self.app.post(
  495. '/api/0/test/new_issue', data=data, headers=headers)
  496. self.assertEqual(output.status_code, 200)
  497. data = json.loads(output.get_data(as_text=True))
  498. data['issue']['date_created'] = '1431414800'
  499. data['issue']['last_updated'] = '1431414800'
  500. issue = copy.deepcopy(FULL_ISSUE_LIST[7])
  501. issue['id'] = 1
  502. self.assertDictEqual(
  503. data,
  504. {
  505. "issue": issue,
  506. "message": "Issue created"
  507. }
  508. )
  509. def test_api_new_issue_public(self):
  510. """ Test the api_new_issue method of the flask api. """
  511. tests.create_projects(self.session)
  512. tests.create_projects_git(
  513. os.path.join(self.path, 'tickets'), bare=True)
  514. tests.create_tokens(self.session)
  515. tests.create_tokens_acl(self.session)
  516. headers = {'Authorization': 'token aaabbbcccddd'}
  517. # Valid request, with private='false'
  518. data = {
  519. 'title': 'test issue',
  520. 'issue_content': 'This issue needs attention',
  521. 'private': 'false',
  522. }
  523. output = self.app.post(
  524. '/api/0/test/new_issue', data=data, headers=headers)
  525. self.assertEqual(output.status_code, 200)
  526. data = json.loads(output.get_data(as_text=True))
  527. data['issue']['date_created'] = '1431414800'
  528. data['issue']['last_updated'] = '1431414800'
  529. issue = copy.deepcopy(FULL_ISSUE_LIST[6])
  530. issue['id'] = 1
  531. self.assertDictEqual(
  532. data,
  533. {
  534. "issue": issue,
  535. "message": "Issue created"
  536. }
  537. )
  538. # Valid request, with private=False
  539. data = {
  540. 'title': 'test issue',
  541. 'issue_content': 'This issue needs attention',
  542. 'private': False
  543. }
  544. output = self.app.post(
  545. '/api/0/test/new_issue', data=data, headers=headers)
  546. self.assertEqual(output.status_code, 200)
  547. data = json.loads(output.get_data(as_text=True))
  548. data['issue']['date_created'] = '1431414800'
  549. data['issue']['last_updated'] = '1431414800'
  550. issue = copy.deepcopy(FULL_ISSUE_LIST[5])
  551. issue['id'] = 2
  552. self.assertDictEqual(
  553. data,
  554. {
  555. "issue": issue,
  556. "message": "Issue created"
  557. }
  558. )
  559. # Valid request, with private='False'
  560. data = {
  561. 'title': 'test issue',
  562. 'issue_content': 'This issue needs attention',
  563. 'private': 'False'
  564. }
  565. output = self.app.post(
  566. '/api/0/test/new_issue', data=data, headers=headers)
  567. self.assertEqual(output.status_code, 200)
  568. data = json.loads(output.get_data(as_text=True))
  569. data['issue']['date_created'] = '1431414800'
  570. data['issue']['last_updated'] = '1431414800'
  571. issue = copy.deepcopy(FULL_ISSUE_LIST[4])
  572. issue['id'] = 3
  573. self.assertDictEqual(
  574. data,
  575. {
  576. "issue": issue,
  577. "message": "Issue created"
  578. }
  579. )
  580. # Valid request, with private=0
  581. data = {
  582. 'title': 'test issue',
  583. 'issue_content': 'This issue needs attention',
  584. 'private': 0
  585. }
  586. output = self.app.post(
  587. '/api/0/test/new_issue', data=data, headers=headers)
  588. self.assertEqual(output.status_code, 200)
  589. data = json.loads(output.get_data(as_text=True))
  590. data['issue']['date_created'] = '1431414800'
  591. data['issue']['last_updated'] = '1431414800'
  592. issue = copy.deepcopy(FULL_ISSUE_LIST[3])
  593. issue['id'] = 4
  594. self.assertDictEqual(
  595. data,
  596. {
  597. "issue": issue,
  598. "message": "Issue created"
  599. }
  600. )
  601. def test_api_new_issue_private(self):
  602. """ Test the api_new_issue method of the flask api. """
  603. tests.create_projects(self.session)
  604. tests.create_projects_git(
  605. os.path.join(self.path, 'tickets'), bare=True)
  606. tests.create_tokens(self.session)
  607. tests.create_tokens_acl(self.session)
  608. headers = {'Authorization': 'token aaabbbcccddd'}
  609. # Private issue: True
  610. data = {
  611. 'title': 'test issue',
  612. 'issue_content': 'This issue needs attention',
  613. 'private': True,
  614. }
  615. output = self.app.post(
  616. '/api/0/test/new_issue', data=data, headers=headers)
  617. self.assertEqual(output.status_code, 200)
  618. data = json.loads(output.get_data(as_text=True))
  619. data['issue']['date_created'] = '1431414800'
  620. data['issue']['last_updated'] = '1431414800'
  621. issue = copy.deepcopy(FULL_ISSUE_LIST[2])
  622. issue['id'] = 1
  623. self.assertDictEqual(
  624. data,
  625. {
  626. "issue": issue,
  627. "message": "Issue created"
  628. }
  629. )
  630. # Private issue: 1
  631. data = {
  632. 'title': 'test issue1',
  633. 'issue_content': 'This issue needs attention',
  634. 'private': 1,
  635. 'assignee': 'foo'
  636. }
  637. output = self.app.post(
  638. '/api/0/test/new_issue', data=data, headers=headers)
  639. self.assertEqual(output.status_code, 200)
  640. data = json.loads(output.get_data(as_text=True))
  641. data['issue']['date_created'] = '1431414800'
  642. data['issue']['last_updated'] = '1431414800'
  643. exp = copy.deepcopy(FULL_ISSUE_LIST[1])
  644. exp['id'] = 2
  645. self.assertDictEqual(
  646. data,
  647. {
  648. "issue": exp,
  649. "message": "Issue created"
  650. }
  651. )
  652. @patch('pagure.utils.check_api_acls', MagicMock(return_value=None))
  653. def test_api_new_issue_raise_db_error(self):
  654. """ Test the api_new_issue method of the flask api. """
  655. tests.create_projects(self.session)
  656. tests.create_projects_git(
  657. os.path.join(self.path, 'tickets'), bare=True)
  658. tests.create_tokens(self.session)
  659. tests.create_tokens_acl(self.session)
  660. headers = {'Authorization': 'token aaabbbcccddd'}
  661. data = {
  662. 'title': 'test issue',
  663. 'issue_content': 'This issue needs attention',
  664. }
  665. with self._app.test_request_context('/') as ctx:
  666. flask.g.session = self.session
  667. flask.g.fas_user = tests.FakeUser(username='foo')
  668. with patch(
  669. 'flask.g.session.commit',
  670. MagicMock(side_effect=SQLAlchemyError('DB error'))):
  671. output = self.app.post(
  672. '/api/0/test/new_issue', data=data, headers=headers)
  673. self.assertEqual(output.status_code, 400)
  674. data = json.loads(output.get_data(as_text=True))
  675. self.assertDictEqual(
  676. data,
  677. {
  678. u'error': u'An error occurred at the database '
  679. 'level and prevent the action from reaching '
  680. 'completion',
  681. u'error_code': u'EDBERROR'
  682. }
  683. )
  684. def test_api_new_issue_user_token_no_input(self):
  685. """ Test the api_new_issue method of the flask api. """
  686. tests.create_projects(self.session)
  687. tests.create_projects_git(
  688. os.path.join(self.path, 'tickets'), bare=True)
  689. tests.create_tokens(self.session, project_id=None)
  690. tests.create_tokens_acl(self.session)
  691. headers = {'Authorization': 'token aaabbbcccddd'}
  692. # Valid token, invalid request - No input
  693. output = self.app.post('/api/0/test2/new_issue', headers=headers)
  694. self.assertEqual(output.status_code, 400)
  695. data = json.loads(output.get_data(as_text=True))
  696. self.assertDictEqual(
  697. data,
  698. {
  699. "error": "Invalid or incomplete input submitted",
  700. "error_code": "EINVALIDREQ",
  701. "errors": {
  702. "issue_content": ["This field is required."],
  703. "title": ["This field is required."],
  704. }
  705. }
  706. )
  707. def test_api_new_issue_user_token_invalid_user(self):
  708. """ Test the api_new_issue method of the flask api. """
  709. tests.create_projects(self.session)
  710. tests.create_projects_git(
  711. os.path.join(self.path, 'tickets'), bare=True)
  712. tests.create_tokens(self.session, project_id=None)
  713. tests.create_tokens_acl(self.session)
  714. headers = {'Authorization': 'token aaabbbcccddd'}
  715. # Another project, still an invalid request - No input
  716. output = self.app.post('/api/0/test/new_issue', headers=headers)
  717. self.assertEqual(output.status_code, 400)
  718. data = json.loads(output.get_data(as_text=True))
  719. self.assertDictEqual(
  720. data,
  721. {
  722. "error": "Invalid or incomplete input submitted",
  723. "error_code": "EINVALIDREQ",
  724. "errors": {
  725. "issue_content": ["This field is required."],
  726. "title": ["This field is required."],
  727. }
  728. }
  729. )
  730. def test_api_new_issue_user_token_invalid_repo(self):
  731. """ Test the api_new_issue method of the flask api. """
  732. tests.create_projects(self.session)
  733. tests.create_projects_git(
  734. os.path.join(self.path, 'tickets'), bare=True)
  735. tests.create_tokens(self.session, project_id=None)
  736. tests.create_tokens_acl(self.session)
  737. headers = {'Authorization': 'token aaabbbcccddd'}
  738. data = {
  739. 'title': 'test issue'
  740. }
  741. # Invalid repo
  742. output = self.app.post(
  743. '/api/0/foo/new_issue', data=data, headers=headers)
  744. self.assertEqual(output.status_code, 404)
  745. data = json.loads(output.get_data(as_text=True))
  746. self.assertDictEqual(
  747. data,
  748. {
  749. "error": "Project not found",
  750. "error_code": "ENOPROJECT",
  751. }
  752. )
  753. def test_api_new_issue_user_token_invalid_request(self):
  754. """ Test the api_new_issue method of the flask api. """
  755. tests.create_projects(self.session)
  756. tests.create_projects_git(
  757. os.path.join(self.path, 'tickets'), bare=True)
  758. tests.create_tokens(self.session, project_id=None)
  759. tests.create_tokens_acl(self.session)
  760. headers = {'Authorization': 'token aaabbbcccddd'}
  761. # Incomplete request
  762. output = self.app.post(
  763. '/api/0/test/new_issue', data={}, headers=headers)
  764. self.assertEqual(output.status_code, 400)
  765. data = json.loads(output.get_data(as_text=True))
  766. self.assertDictEqual(
  767. data,
  768. {
  769. "error": "Invalid or incomplete input submitted",
  770. "error_code": "EINVALIDREQ",
  771. "errors": {
  772. "issue_content": ["This field is required."],
  773. "title": ["This field is required."]
  774. }
  775. }
  776. )
  777. def test_api_new_issue_user_token(self):
  778. """ Test the api_new_issue method of the flask api. """
  779. tests.create_projects(self.session)
  780. tests.create_projects_git(
  781. os.path.join(self.path, 'tickets'), bare=True)
  782. tests.create_tokens(self.session, project_id=None)
  783. tests.create_tokens_acl(self.session)
  784. headers = {'Authorization': 'token aaabbbcccddd'}
  785. data = {
  786. 'title': 'test issue',
  787. 'issue_content': 'This issue needs attention',
  788. }
  789. # Valid request
  790. output = self.app.post(
  791. '/api/0/test/new_issue', data=data, headers=headers)
  792. self.assertEqual(output.status_code, 200)
  793. data = json.loads(output.get_data(as_text=True))
  794. data['issue']['date_created'] = '1431414800'
  795. data['issue']['last_updated'] = '1431414800'
  796. self.assertDictEqual(
  797. data,
  798. {
  799. "issue": FULL_ISSUE_LIST[8],
  800. "message": "Issue created"
  801. }
  802. )
  803. def test_api_new_issue_user_token_milestone(self):
  804. """ Test the api_new_issue method of the flask api. """
  805. tests.create_projects(self.session)
  806. tests.create_projects_git(
  807. os.path.join(self.path, 'tickets'), bare=True)
  808. tests.create_tokens(self.session, project_id=None)
  809. tests.create_tokens_acl(self.session)
  810. headers = {'Authorization': 'token aaabbbcccddd'}
  811. # Set some milestones
  812. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  813. repo.milestones = {'milestone-1.0': '', 'milestone-2.0': 'Tomorrow!'}
  814. self.session.add(repo)
  815. self.session.commit()
  816. # Valid request with milestone
  817. data = {
  818. 'title': 'test issue',
  819. 'issue_content': 'This issue needs attention',
  820. 'milestone': ['milestone-1.0'],
  821. }
  822. output = self.app.post(
  823. '/api/0/test/new_issue', data=data, headers=headers)
  824. self.assertEqual(output.status_code, 200)
  825. data = json.loads(output.get_data(as_text=True))
  826. data['issue']['date_created'] = '1431414800'
  827. data['issue']['last_updated'] = '1431414800'
  828. issue = copy.deepcopy(FULL_ISSUE_LIST[7])
  829. issue['id'] = 1
  830. self.assertDictEqual(
  831. data,
  832. {
  833. "issue": issue,
  834. "message": "Issue created"
  835. }
  836. )
  837. def test_api_new_issue_user_token_public(self):
  838. """ Test the api_new_issue method of the flask api. """
  839. tests.create_projects(self.session)
  840. tests.create_projects_git(
  841. os.path.join(self.path, 'tickets'), bare=True)
  842. tests.create_tokens(self.session, project_id=None)
  843. tests.create_tokens_acl(self.session)
  844. headers = {'Authorization': 'token aaabbbcccddd'}
  845. # Valid request, with private='false'
  846. data = {
  847. 'title': 'test issue',
  848. 'issue_content': 'This issue needs attention',
  849. 'private': 'false',
  850. }
  851. output = self.app.post(
  852. '/api/0/test/new_issue', data=data, headers=headers)
  853. self.assertEqual(output.status_code, 200)
  854. data = json.loads(output.get_data(as_text=True))
  855. data['issue']['date_created'] = '1431414800'
  856. data['issue']['last_updated'] = '1431414800'
  857. issue = copy.deepcopy(FULL_ISSUE_LIST[6])
  858. issue['id'] = 1
  859. self.assertDictEqual(
  860. data,
  861. {
  862. "issue": issue,
  863. "message": "Issue created"
  864. }
  865. )
  866. # Valid request, with private=False
  867. data = {
  868. 'title': 'test issue',
  869. 'issue_content': 'This issue needs attention',
  870. 'private': False
  871. }
  872. output = self.app.post(
  873. '/api/0/test/new_issue', data=data, headers=headers)
  874. self.assertEqual(output.status_code, 200)
  875. data = json.loads(output.get_data(as_text=True))
  876. data['issue']['date_created'] = '1431414800'
  877. data['issue']['last_updated'] = '1431414800'
  878. issue = copy.deepcopy(FULL_ISSUE_LIST[5])
  879. issue['id'] = 2
  880. self.assertDictEqual(
  881. data,
  882. {
  883. "issue": issue,
  884. "message": "Issue created"
  885. }
  886. )
  887. # Valid request, with private='False'
  888. data = {
  889. 'title': 'test issue',
  890. 'issue_content': 'This issue needs attention',
  891. 'private': 'False'
  892. }
  893. output = self.app.post(
  894. '/api/0/test/new_issue', data=data, headers=headers)
  895. self.assertEqual(output.status_code, 200)
  896. data = json.loads(output.get_data(as_text=True))
  897. data['issue']['date_created'] = '1431414800'
  898. data['issue']['last_updated'] = '1431414800'
  899. issue = copy.deepcopy(FULL_ISSUE_LIST[4])
  900. issue['id'] = 3
  901. self.assertDictEqual(
  902. data,
  903. {
  904. "issue": issue,
  905. "message": "Issue created"
  906. }
  907. )
  908. # Valid request, with private=0
  909. data = {
  910. 'title': 'test issue',
  911. 'issue_content': 'This issue needs attention',
  912. 'private': 0
  913. }
  914. output = self.app.post(
  915. '/api/0/test/new_issue', data=data, headers=headers)
  916. self.assertEqual(output.status_code, 200)
  917. data = json.loads(output.get_data(as_text=True))
  918. data['issue']['date_created'] = '1431414800'
  919. data['issue']['last_updated'] = '1431414800'
  920. issue = copy.deepcopy(FULL_ISSUE_LIST[4])
  921. issue['id'] = 4
  922. self.assertDictEqual(
  923. data,
  924. {
  925. "issue": issue,
  926. "message": "Issue created"
  927. }
  928. )
  929. def test_api_new_issue_user_token_private(self):
  930. """ Test the api_new_issue method of the flask api. """
  931. tests.create_projects(self.session)
  932. tests.create_projects_git(
  933. os.path.join(self.path, 'tickets'), bare=True)
  934. tests.create_tokens(self.session, project_id=None)
  935. tests.create_tokens_acl(self.session)
  936. headers = {'Authorization': 'token aaabbbcccddd'}
  937. # Private issue: True
  938. data = {
  939. 'title': 'test issue',
  940. 'issue_content': 'This issue needs attention',
  941. 'private': True,
  942. }
  943. output = self.app.post(
  944. '/api/0/test/new_issue', data=data, headers=headers)
  945. self.assertEqual(output.status_code, 200)
  946. data = json.loads(output.get_data(as_text=True))
  947. data['issue']['date_created'] = '1431414800'
  948. data['issue']['last_updated'] = '1431414800'
  949. issue = copy.deepcopy(FULL_ISSUE_LIST[2])
  950. issue['id'] = 1
  951. self.assertDictEqual(
  952. data,
  953. {
  954. "issue": issue,
  955. "message": "Issue created"
  956. }
  957. )
  958. # Private issue: 1
  959. data = {
  960. 'title': 'test issue1',
  961. 'issue_content': 'This issue needs attention',
  962. 'private': 1,
  963. 'assignee': 'foo'
  964. }
  965. output = self.app.post(
  966. '/api/0/test/new_issue', data=data, headers=headers)
  967. self.assertEqual(output.status_code, 200)
  968. data = json.loads(output.get_data(as_text=True))
  969. data['issue']['date_created'] = '1431414800'
  970. data['issue']['last_updated'] = '1431414800'
  971. issue = copy.deepcopy(FULL_ISSUE_LIST[1])
  972. issue['id'] = 2
  973. self.assertDictEqual(
  974. data,
  975. {
  976. "issue": issue,
  977. "message": "Issue created"
  978. }
  979. )
  980. # Private issue: 'true'
  981. data = {
  982. 'title': 'test issue1',
  983. 'issue_content': 'This issue needs attention',
  984. 'private': 'true',
  985. }
  986. output = self.app.post(
  987. '/api/0/test/new_issue', data=data, headers=headers)
  988. self.assertEqual(output.status_code, 200)
  989. data = json.loads(output.get_data(as_text=True))
  990. data['issue']['date_created'] = '1431414800'
  991. data['issue']['last_updated'] = '1431414800'
  992. exp = copy.deepcopy(FULL_ISSUE_LIST[1])
  993. exp['id'] = 3
  994. exp['assignee'] = None
  995. self.assertDictEqual(
  996. data,
  997. {
  998. "issue": exp,
  999. "message": "Issue created"
  1000. }
  1001. )
  1002. def test_api_view_issues(self):
  1003. """ Test the api_view_issues method of the flask api. """
  1004. self.test_api_new_issue()
  1005. # Invalid repo
  1006. output = self.app.get('/api/0/foo/issues')
  1007. self.assertEqual(output.status_code, 404)
  1008. data = json.loads(output.get_data(as_text=True))
  1009. self.assertDictEqual(
  1010. data,
  1011. {
  1012. "error": "Project not found",
  1013. "error_code": "ENOPROJECT",
  1014. }
  1015. )
  1016. # List all opened issues
  1017. output = self.app.get('/api/0/test/issues')
  1018. self.assertEqual(output.status_code, 200)
  1019. data = json.loads(output.get_data(as_text=True))
  1020. for idx in range(len(data['issues'])):
  1021. data['issues'][idx]['date_created'] = '1431414800'
  1022. data['issues'][idx]['last_updated'] = '1431414800'
  1023. for k in ['first', 'last']:
  1024. self.assertIsNotNone(data['pagination'][k])
  1025. data['pagination'][k] = 'http://localhost...'
  1026. self.assertDictEqual(
  1027. data,
  1028. {
  1029. "args": {
  1030. "assignee": None,
  1031. "author": None,
  1032. 'milestones': [],
  1033. 'no_stones': None,
  1034. 'order': None,
  1035. 'priority': None,
  1036. "since": None,
  1037. "status": None,
  1038. "tags": [],
  1039. },
  1040. "issues": [FULL_ISSUE_LIST[8]],
  1041. u'pagination': {
  1042. u'first': u'http://localhost...',
  1043. u'last': u'http://localhost...',
  1044. u'next': None,
  1045. u'page': 1,
  1046. u'pages': 1,
  1047. u'per_page': 20,
  1048. u'prev': None
  1049. },
  1050. "total_issues": 1
  1051. }
  1052. )
  1053. # Create private issue
  1054. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  1055. msg = pagure.lib.query.new_issue(
  1056. session=self.session,
  1057. repo=repo,
  1058. title='Test issue',
  1059. content='We should work on this',
  1060. user='pingou',
  1061. private=True,
  1062. status="Closed"
  1063. )
  1064. self.session.commit()
  1065. self.assertEqual(msg.title, 'Test issue')
  1066. # Access issues un-authenticated
  1067. output = self.app.get('/api/0/test/issues')
  1068. self.assertEqual(output.status_code, 200)
  1069. data = json.loads(output.get_data(as_text=True))
  1070. for idx in range(len(data['issues'])):
  1071. data['issues'][idx]['date_created'] = '1431414800'
  1072. data['issues'][idx]['last_updated'] = '1431414800'
  1073. for k in ['first', 'last']:
  1074. self.assertIsNotNone(data['pagination'][k])
  1075. data['pagination'][k] = 'http://localhost...'
  1076. self.assertDictEqual(
  1077. data,
  1078. {
  1079. "args": {
  1080. "assignee": None,
  1081. "author": None,
  1082. 'milestones': [],
  1083. 'no_stones': None,
  1084. 'order': None,
  1085. 'priority': None,
  1086. "since": None,
  1087. "status": None,
  1088. "tags": []
  1089. },
  1090. "issues": [FULL_ISSUE_LIST[8]],
  1091. u'pagination': {
  1092. u'first': u'http://localhost...',
  1093. u'last': u'http://localhost...',
  1094. u'next': None,
  1095. u'page': 1,
  1096. u'pages': 1,
  1097. u'per_page': 20,
  1098. u'prev': None
  1099. },
  1100. "total_issues": 1
  1101. }
  1102. )
  1103. headers = {'Authorization': 'token aaabbbccc'}
  1104. # Access issues authenticated but non-existing token
  1105. output = self.app.get('/api/0/test/issues', headers=headers)
  1106. self.assertEqual(output.status_code, 401)
  1107. # Create a new token for another user
  1108. item = pagure.lib.model.Token(
  1109. id='bar_token',
  1110. user_id=2,
  1111. project_id=1,
  1112. expiration=datetime.datetime.utcnow() + datetime.timedelta(
  1113. days=30)
  1114. )
  1115. self.session.add(item)
  1116. self.session.commit()
  1117. headers = {'Authorization': 'token bar_token'}
  1118. # Access issues authenticated but wrong token
  1119. output = self.app.get('/api/0/test/issues', headers=headers)
  1120. self.assertEqual(output.status_code, 200)
  1121. data = json.loads(output.get_data(as_text=True))
  1122. for idx in range(len(data['issues'])):
  1123. data['issues'][idx]['date_created'] = '1431414800'
  1124. data['issues'][idx]['last_updated'] = '1431414800'
  1125. for k in ['first', 'last']:
  1126. self.assertIsNotNone(data['pagination'][k])
  1127. data['pagination'][k] = 'http://localhost...'
  1128. self.assertDictEqual(
  1129. data,
  1130. {
  1131. "args": {
  1132. "assignee": None,
  1133. "author": None,
  1134. 'milestones': [],
  1135. 'no_stones': None,
  1136. 'order': None,
  1137. 'priority': None,
  1138. "since": None,
  1139. "status": None,
  1140. "tags": []
  1141. },
  1142. "issues": [FULL_ISSUE_LIST[8]],
  1143. u'pagination': {
  1144. u'first': u'http://localhost...',
  1145. u'last': u'http://localhost...',
  1146. u'next': None,
  1147. u'page': 1,
  1148. u'pages': 1,
  1149. u'per_page': 20,
  1150. u'prev': None
  1151. },
  1152. "total_issues": 1
  1153. }
  1154. )
  1155. headers = {'Authorization': 'token aaabbbcccddd'}
  1156. # Access issues authenticated correctly
  1157. output = self.app.get('/api/0/test/issues', headers=headers)
  1158. self.assertEqual(output.status_code, 200)
  1159. data = json.loads(output.get_data(as_text=True))
  1160. for idx in range(len(data['issues'])):
  1161. data['issues'][idx]['date_created'] = '1431414800'
  1162. data['issues'][idx]['last_updated'] = '1431414800'
  1163. for k in ['first', 'last']:
  1164. self.assertIsNotNone(data['pagination'][k])
  1165. data['pagination'][k] = 'http://localhost...'
  1166. self.assertDictEqual(
  1167. data,
  1168. {
  1169. "args": {
  1170. "assignee": None,
  1171. "author": None,
  1172. 'milestones': [],
  1173. 'no_stones': None,
  1174. 'order': None,
  1175. 'priority': None,
  1176. "since": None,
  1177. "status": None,
  1178. "tags": []
  1179. },
  1180. "issues": [FULL_ISSUE_LIST[8]],
  1181. u'pagination': {
  1182. u'first': u'http://localhost...',
  1183. u'last': u'http://localhost...',
  1184. u'next': None,
  1185. u'page': 1,
  1186. u'pages': 1,
  1187. u'per_page': 20,
  1188. u'prev': None
  1189. },
  1190. "total_issues": 1
  1191. }
  1192. )
  1193. headers = {'Authorization': 'token aaabbbccc'}
  1194. # Access issues authenticated but non-existing token
  1195. output = self.app.get('/api/0/test/issues', headers=headers)
  1196. self.assertEqual(output.status_code, 401)
  1197. # Create a new token for another user
  1198. item = pagure.lib.model.Token(
  1199. id='bar_token_foo',
  1200. user_id=2,
  1201. project_id=1,
  1202. expiration=datetime.datetime.utcnow() + datetime.timedelta(
  1203. days=30)
  1204. )
  1205. self.session.add(item)
  1206. self.session.commit()
  1207. headers = {'Authorization': 'token bar_token_foo'}
  1208. # Access issues authenticated but wrong token
  1209. output = self.app.get('/api/0/test/issues', headers=headers)
  1210. self.assertEqual(output.status_code, 200)
  1211. data = json.loads(output.get_data(as_text=True))
  1212. for idx in range(len(data['issues'])):
  1213. data['issues'][idx]['date_created'] = '1431414800'
  1214. data['issues'][idx]['last_updated'] = '1431414800'
  1215. for k in ['first', 'last']:
  1216. self.assertIsNotNone(data['pagination'][k])
  1217. data['pagination'][k] = 'http://localhost...'
  1218. self.assertDictEqual(
  1219. data,
  1220. {
  1221. "args": {
  1222. "assignee": None,
  1223. "author": None,
  1224. 'milestones': [],
  1225. 'no_stones': None,
  1226. 'order': None,
  1227. 'priority': None,
  1228. "since": None,
  1229. "status": None,
  1230. "tags": []
  1231. },
  1232. "issues": [FULL_ISSUE_LIST[8]],
  1233. u'pagination': {
  1234. u'first': u'http://localhost...',
  1235. u'last': u'http://localhost...',
  1236. u'next': None,
  1237. u'page': 1,
  1238. u'pages': 1,
  1239. u'per_page': 20,
  1240. u'prev': None
  1241. },
  1242. "total_issues": 1
  1243. }
  1244. )
  1245. headers = {'Authorization': 'token aaabbbcccddd'}
  1246. # Access issues authenticated correctly
  1247. output = self.app.get('/api/0/test/issues', headers=headers)
  1248. self.assertEqual(output.status_code, 200)
  1249. data = json.loads(output.get_data(as_text=True))
  1250. for idx in range(len(data['issues'])):
  1251. data['issues'][idx]['date_created'] = '1431414800'
  1252. data['issues'][idx]['last_updated'] = '1431414800'
  1253. for k in ['first', 'last']:
  1254. self.assertIsNotNone(data['pagination'][k])
  1255. data['pagination'][k] = 'http://localhost...'
  1256. self.assertDictEqual(
  1257. data,
  1258. {
  1259. "args": {
  1260. "assignee": None,
  1261. "author": None,
  1262. 'milestones': [],
  1263. 'no_stones': None,
  1264. 'order': None,
  1265. 'priority': None,
  1266. "since": None,
  1267. "status": None,
  1268. "tags": []
  1269. },
  1270. "issues": [FULL_ISSUE_LIST[8]],
  1271. u'pagination': {
  1272. u'first': u'http://localhost...',
  1273. u'last': u'http://localhost...',
  1274. u'next': None,
  1275. u'page': 1,
  1276. u'pages': 1,
  1277. u'per_page': 20,
  1278. u'prev': None
  1279. },
  1280. "total_issues": 1
  1281. }
  1282. )
  1283. # List closed issue
  1284. output = self.app.get('/api/0/test/issues?status=Closed', headers=headers)
  1285. self.assertEqual(output.status_code, 200)
  1286. data = json.loads(output.get_data(as_text=True))
  1287. data['issues'][0]['date_created'] = '1431414800'
  1288. data['issues'][0]['last_updated'] = '1431414800'
  1289. for k in ['first', 'last']:
  1290. self.assertIsNotNone(data['pagination'][k])
  1291. data['pagination'][k] = 'http://localhost...'
  1292. self.assertDictEqual(
  1293. data,
  1294. {
  1295. "args": {
  1296. "assignee": None,
  1297. "author": None,
  1298. "milestones": [],
  1299. "no_stones": None,
  1300. 'order': None,
  1301. "priority": None,
  1302. "since": None,
  1303. "status": "Closed",
  1304. "tags": []
  1305. },
  1306. "issues": [FULL_ISSUE_LIST[0]],
  1307. u'pagination': {
  1308. u'first': u'http://localhost...',
  1309. u'last': u'http://localhost...',
  1310. u'next': None,
  1311. u'page': 1,
  1312. u'pages': 1,
  1313. u'per_page': 20,
  1314. u'prev': None
  1315. },
  1316. "total_issues": 1,
  1317. }
  1318. )
  1319. # List closed issue
  1320. output = self.app.get('/api/0/test/issues?status=Invalid', headers=headers)
  1321. self.assertEqual(output.status_code, 200)
  1322. data = json.loads(output.get_data(as_text=True))
  1323. for k in ['first', 'last']:
  1324. self.assertIsNotNone(data['pagination'][k])
  1325. data['pagination'][k] = 'http://localhost...'
  1326. self.assertDictEqual(
  1327. data,
  1328. {
  1329. "args": {
  1330. "assignee": None,
  1331. "author": None,
  1332. 'milestones': [],
  1333. 'no_stones': None,
  1334. 'order': None,
  1335. 'priority': None,
  1336. "since": None,
  1337. "status": "Invalid",
  1338. "tags": []
  1339. },
  1340. "issues": [],
  1341. u'pagination': {
  1342. u'first': u'http://localhost...',
  1343. u'last': u'http://localhost...',
  1344. u'next': None,
  1345. u'page': 1,
  1346. u'pages': 0,
  1347. u'per_page': 20,
  1348. u'prev': None
  1349. },
  1350. "total_issues": 0,
  1351. }
  1352. )
  1353. # List all issues
  1354. output = self.app.get('/api/0/test/issues?status=All', headers=headers)
  1355. self.assertEqual(output.status_code, 200)
  1356. data = json.loads(output.get_data(as_text=True))
  1357. for idx in range(len(data['issues'])):
  1358. data['issues'][idx]['last_updated'] = '1431414800'
  1359. data['issues'][idx]['date_created'] = '1431414800'
  1360. for k in ['first', 'last']:
  1361. self.assertIsNotNone(data['pagination'][k])
  1362. data['pagination'][k] = 'http://localhost...'
  1363. self.assertDictEqual(
  1364. data,
  1365. {
  1366. "args": {
  1367. "assignee": None,
  1368. "author": None,
  1369. 'milestones': [],
  1370. 'no_stones': None,
  1371. 'order': None,
  1372. 'priority': None,
  1373. "since": None,
  1374. "status": "All",
  1375. "tags": []
  1376. },
  1377. "issues": [FULL_ISSUE_LIST[0], FULL_ISSUE_LIST[8]],
  1378. u'pagination': {
  1379. u'first': u'http://localhost...',
  1380. u'last': u'http://localhost...',
  1381. u'next': None,
  1382. u'page': 1,
  1383. u'pages': 1,
  1384. u'per_page': 20,
  1385. u'prev': None
  1386. },
  1387. "total_issues": 2
  1388. }
  1389. )
  1390. def test_api_view_issues_since_invalid_format(self):
  1391. """ Test the api_view_issues method of the flask api. """
  1392. self.test_api_new_issue()
  1393. # Invalid repo
  1394. output = self.app.get('/api/0/test/issues?since=12-13')
  1395. self.assertEqual(output.status_code, 400)
  1396. data = json.loads(output.get_data(as_text=True))
  1397. self.assertDictEqual(
  1398. data,
  1399. {
  1400. u'error': u'Invalid datetime format',
  1401. u'error_code': u'EDATETIME'
  1402. }
  1403. )
  1404. def test_api_view_issues_since_invalid_timestamp(self):
  1405. """ Test the api_view_issues method of the flask api. """
  1406. self.test_api_new_issue()
  1407. # Invalid repo
  1408. output = self.app.get('/api/0/test/issues?since=100000000000000')
  1409. self.assertEqual(output.status_code, 400)
  1410. data = json.loads(output.get_data(as_text=True))
  1411. self.assertDictEqual(
  1412. data,
  1413. {
  1414. u'error': u'Invalid timestamp format',
  1415. u'error_code': u'ETIMESTAMP'
  1416. }
  1417. )
  1418. def test_api_view_issues_reversed(self):
  1419. """ Test the api_view_issues method of the flask api. in reversed
  1420. order.
  1421. """
  1422. self.test_api_new_issue()
  1423. headers = {'Authorization': 'token aaabbbcccddd'}
  1424. # List issues in reverse order
  1425. output = self.app.get('/api/0/test/issues?order=asc', headers=headers)
  1426. self.assertEqual(output.status_code, 200)
  1427. data = json.loads(output.get_data(as_text=True))
  1428. for idx in range(len(data['issues'])):
  1429. data['issues'][idx]['last_updated'] = '1431414800'
  1430. data['issues'][idx]['date_created'] = '1431414800'
  1431. for k in ['first', 'last']:
  1432. self.assertIsNotNone(data['pagination'][k])
  1433. data['pagination'][k] = 'http://localhost...'
  1434. expected = {
  1435. "args": {
  1436. "assignee": None,
  1437. "author": None,
  1438. 'milestones': [],
  1439. 'no_stones': None,
  1440. 'order': 'asc',
  1441. 'priority': None,
  1442. "since": None,
  1443. "status": None,
  1444. "tags": []
  1445. },
  1446. "issues": [FULL_ISSUE_LIST[8]],
  1447. u'pagination': {
  1448. u'first': u'http://localhost...',
  1449. u'last': u'http://localhost...',
  1450. u'next': None,
  1451. u'page': 1,
  1452. u'pages': 1,
  1453. u'per_page': 20,
  1454. u'prev': None
  1455. },
  1456. "total_issues": 1
  1457. }
  1458. self.assertDictEqual(data, expected)
  1459. def test_api_view_issues_milestone(self):
  1460. """ Test the api_view_issues method of the flask api when filtering
  1461. for a milestone.
  1462. """
  1463. tests.create_projects(self.session)
  1464. tests.create_projects_git(
  1465. os.path.join(self.path, 'tickets'), bare=True)
  1466. tests.create_tokens(self.session)
  1467. tests.create_tokens_acl(self.session)
  1468. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  1469. # Create 2 tickets but only 1 has a milestone
  1470. start = arrow.utcnow().timestamp
  1471. issue = pagure.lib.model.Issue(
  1472. id=pagure.lib.query.get_next_id(self.session, repo.id),
  1473. project_id=repo.id,
  1474. title='Issue #1',
  1475. content='Description',
  1476. user_id=1, # pingou
  1477. uid='issue#1',
  1478. private=False,
  1479. )
  1480. self.session.add(issue)
  1481. self.session.commit()
  1482. issue = pagure.lib.model.Issue(
  1483. id=pagure.lib.query.get_next_id(self.session, repo.id),
  1484. project_id=repo.id,
  1485. title='Issue #2',
  1486. content='Description',
  1487. user_id=1, # pingou
  1488. uid='issue#2',
  1489. private=False,
  1490. milestone='v1.0',
  1491. )
  1492. self.session.add(issue)
  1493. self.session.commit()
  1494. # List all opened issues
  1495. output = self.app.get('/api/0/test/issues')
  1496. self.assertEqual(output.status_code, 200)
  1497. data = json.loads(output.get_data(as_text=True))
  1498. for idx in range(len(data['issues'])):
  1499. data['issues'][idx]['date_created'] = '1431414800'
  1500. data['issues'][idx]['last_updated'] = '1431414800'
  1501. for k in ['first', 'last']:
  1502. self.assertIsNotNone(data['pagination'][k])
  1503. data['pagination'][k] = 'http://localhost...'
  1504. lcl_issues = copy.deepcopy(LCL_ISSUES)
  1505. lcl_issues[0]['milestone'] = 'v1.0'
  1506. self.assertDictEqual(
  1507. data,
  1508. {
  1509. "args": {
  1510. "assignee": None,
  1511. "author": None,
  1512. 'milestones': [],
  1513. 'no_stones': None,
  1514. 'order': None,
  1515. 'priority': None,
  1516. "since": None,
  1517. "status": None,
  1518. "tags": [],
  1519. },
  1520. "issues": lcl_issues,
  1521. u'pagination': {
  1522. u'first': u'http://localhost...',
  1523. u'last': u'http://localhost...',
  1524. u'next': None,
  1525. u'page': 1,
  1526. u'pages': 1,
  1527. u'per_page': 20,
  1528. u'prev': None
  1529. },
  1530. "total_issues": 2
  1531. }
  1532. )
  1533. # List all issues of the milestone v1.0
  1534. output = self.app.get('/api/0/test/issues?milestones=v1.0')
  1535. self.assertEqual(output.status_code, 200)
  1536. data = json.loads(output.get_data(as_text=True))
  1537. for idx in range(len(data['issues'])):
  1538. data['issues'][idx]['date_created'] = '1431414800'
  1539. data['issues'][idx]['last_updated'] = '1431414800'
  1540. for k in ['first', 'last']:
  1541. self.assertIsNotNone(data['pagination'][k])
  1542. data['pagination'][k] = 'http://localhost...'
  1543. self.assertDictEqual(
  1544. data,
  1545. {
  1546. "args": {
  1547. "assignee": None,
  1548. "author": None,
  1549. 'milestones': ['v1.0'],
  1550. 'no_stones': None,
  1551. 'order': None,
  1552. 'priority': None,
  1553. "since": None,
  1554. "status": None,
  1555. "tags": [],
  1556. },
  1557. "issues": [lcl_issues[0]],
  1558. u'pagination': {
  1559. u'first': u'http://localhost...',
  1560. u'last': u'http://localhost...',
  1561. u'next': None,
  1562. u'page': 1,
  1563. u'pages': 1,
  1564. u'per_page': 20,
  1565. u'prev': None
  1566. },
  1567. "total_issues": 1
  1568. }
  1569. )
  1570. def test_api_view_issues_priority(self):
  1571. """ Test the api_view_issues method of the flask api when filtering
  1572. for a priority.
  1573. """
  1574. tests.create_projects(self.session)
  1575. tests.create_projects_git(
  1576. os.path.join(self.path, 'tickets'), bare=True)
  1577. tests.create_tokens(self.session)
  1578. tests.create_tokens_acl(self.session)
  1579. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  1580. # Create 2 tickets but only 1 has a priority
  1581. start = arrow.utcnow().timestamp
  1582. issue = pagure.lib.model.Issue(
  1583. id=pagure.lib.query.get_next_id(self.session, repo.id),
  1584. project_id=repo.id,
  1585. title='Issue #1',
  1586. content='Description',
  1587. user_id=1, # pingou
  1588. uid='issue#1',
  1589. private=False,
  1590. )
  1591. self.session.add(issue)
  1592. self.session.commit()
  1593. issue = pagure.lib.model.Issue(
  1594. id=pagure.lib.query.get_next_id(self.session, repo.id),
  1595. project_id=repo.id,
  1596. title='Issue #2',
  1597. content='Description',
  1598. user_id=1, # pingou
  1599. uid='issue#2',
  1600. private=False,
  1601. priority=1,
  1602. )
  1603. self.session.add(issue)
  1604. self.session.commit()
  1605. # Set some priorities to the project
  1606. repo.priorities = {'1': 'High', '2': 'Normal'}
  1607. self.session.add(repo)
  1608. self.session.commit()
  1609. # List all opened issues
  1610. output = self.app.get('/api/0/test/issues')
  1611. self.assertEqual(output.status_code, 200)
  1612. data = json.loads(output.get_data(as_text=True))
  1613. for idx in range(len(data['issues'])):
  1614. data['issues'][idx]['date_created'] = '1431414800'
  1615. data['issues'][idx]['last_updated'] = '1431414800'
  1616. for k in ['first', 'last']:
  1617. self.assertIsNotNone(data['pagination'][k])
  1618. data['pagination'][k] = 'http://localhost...'
  1619. lcl_issues = copy.deepcopy(LCL_ISSUES)
  1620. lcl_issues[0]['priority'] = 1
  1621. self.assertDictEqual(
  1622. data,
  1623. {
  1624. "args": {
  1625. "assignee": None,
  1626. "author": None,
  1627. 'milestones': [],
  1628. 'no_stones': None,
  1629. 'order': None,
  1630. 'priority': None,
  1631. "since": None,
  1632. "status": None,
  1633. "tags": [],
  1634. },
  1635. "issues": lcl_issues,
  1636. u'pagination': {
  1637. u'first': u'http://localhost...',
  1638. u'last': u'http://localhost...',
  1639. u'next': None,
  1640. u'page': 1,
  1641. u'pages': 1,
  1642. u'per_page': 20,
  1643. u'prev': None
  1644. },
  1645. "total_issues": 2
  1646. }
  1647. )
  1648. # List all issues of the priority high (ie: 1)
  1649. output = self.app.get('/api/0/test/issues?priority=high')
  1650. self.assertEqual(output.status_code, 200)
  1651. data = json.loads(output.get_data(as_text=True))
  1652. for idx in range(len(data['issues'])):
  1653. data['issues'][idx]['date_created'] = '1431414800'
  1654. data['issues'][idx]['last_updated'] = '1431414800'
  1655. for k in ['first', 'last']:
  1656. self.assertIsNotNone(data['pagination'][k])
  1657. data['pagination'][k] = 'http://localhost...'
  1658. self.assertDictEqual(
  1659. data,
  1660. {
  1661. "args": {
  1662. "assignee": None,
  1663. "author": None,
  1664. 'milestones': [],
  1665. 'no_stones': None,
  1666. 'order': None,
  1667. 'priority': 'high',
  1668. "since": None,
  1669. "status": None,
  1670. "tags": [],
  1671. },
  1672. "issues": [lcl_issues[0]],
  1673. u'pagination': {
  1674. u'first': u'http://localhost...',
  1675. u'last': u'http://localhost...',
  1676. u'next': None,
  1677. u'page': 1,
  1678. u'pages': 1,
  1679. u'per_page': 20,
  1680. u'prev': None
  1681. },
  1682. "total_issues": 1
  1683. }
  1684. )
  1685. output = self.app.get('/api/0/test/issues?priority=1')
  1686. self.assertEqual(output.status_code, 200)
  1687. data = json.loads(output.get_data(as_text=True))
  1688. for idx in range(len(data['issues'])):
  1689. data['issues'][idx]['date_created'] = '1431414800'
  1690. data['issues'][idx]['last_updated'] = '1431414800'
  1691. for k in ['first', 'last']:
  1692. self.assertIsNotNone(data['pagination'][k])
  1693. data['pagination'][k] = 'http://localhost...'
  1694. self.assertDictEqual(
  1695. data,
  1696. {
  1697. "args": {
  1698. "assignee": None,
  1699. "author": None,
  1700. 'milestones': [],
  1701. 'no_stones': None,
  1702. 'order': None,
  1703. 'priority': '1',
  1704. "since": None,
  1705. "status": None,
  1706. "tags": [],
  1707. },
  1708. "issues": [lcl_issues[0]],
  1709. u'pagination': {
  1710. u'first': u'http://localhost...',
  1711. u'last': u'http://localhost...',
  1712. u'next': None,
  1713. u'page': 1,
  1714. u'pages': 1,
  1715. u'per_page': 20,
  1716. u'prev': None
  1717. },
  1718. "total_issues": 1
  1719. }
  1720. )
  1721. def test_api_view_issues_priority_invalid(self):
  1722. """ Test the api_view_issues method of the flask api when filtering
  1723. for an invalid priority.
  1724. """
  1725. tests.create_projects(self.session)
  1726. tests.create_projects_git(
  1727. os.path.join(self.path, 'tickets'), bare=True)
  1728. tests.create_tokens(self.session)
  1729. tests.create_tokens_acl(self.session)
  1730. # Try getting issues with an invalid priority
  1731. output = self.app.get('/api/0/test/issues?priority=foobar')
  1732. self.assertEqual(output.status_code, 400)
  1733. data = json.loads(output.get_data(as_text=True))
  1734. self.assertDictEqual(
  1735. data,
  1736. {
  1737. "error": "Invalid priority submitted",
  1738. "error_code": "EINVALIDPRIORITY"
  1739. }
  1740. )
  1741. def test_api_view_issues_no_stones(self):
  1742. """ Test the api_view_issues method of the flask api when filtering
  1743. with no_stones.
  1744. """
  1745. tests.create_projects(self.session)
  1746. tests.create_projects_git(
  1747. os.path.join(self.path, 'tickets'), bare=True)
  1748. tests.create_tokens(self.session)
  1749. tests.create_tokens_acl(self.session)
  1750. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  1751. # Create 2 tickets but only 1 has a milestone
  1752. start = arrow.utcnow().timestamp
  1753. issue = pagure.lib.model.Issue(
  1754. id=pagure.lib.query.get_next_id(self.session, repo.id),
  1755. project_id=repo.id,
  1756. title='Issue #1',
  1757. content='Description',
  1758. user_id=1, # pingou
  1759. uid='issue#1',
  1760. private=False,
  1761. )
  1762. self.session.add(issue)
  1763. self.session.commit()
  1764. issue = pagure.lib.model.Issue(
  1765. id=pagure.lib.query.get_next_id(self.session, repo.id),
  1766. project_id=repo.id,
  1767. title='Issue #2',
  1768. content='Description',
  1769. user_id=1, # pingou
  1770. uid='issue#2',
  1771. private=False,
  1772. milestone='v1.0',
  1773. )
  1774. self.session.add(issue)
  1775. self.session.commit()
  1776. # List all opened issues
  1777. output = self.app.get('/api/0/test/issues')
  1778. self.assertEqual(output.status_code, 200)
  1779. data = json.loads(output.get_data(as_text=True))
  1780. for idx in range(len(data['issues'])):
  1781. data['issues'][idx]['date_created'] = '1431414800'
  1782. data['issues'][idx]['last_updated'] = '1431414800'
  1783. for k in ['first', 'last']:
  1784. self.assertIsNotNone(data['pagination'][k])
  1785. data['pagination'][k] = 'http://localhost...'
  1786. lcl_issues = copy.deepcopy(LCL_ISSUES)
  1787. lcl_issues[0]['milestone'] = 'v1.0'
  1788. self.assertDictEqual(
  1789. data,
  1790. {
  1791. "args": {
  1792. "assignee": None,
  1793. "author": None,
  1794. 'milestones': [],
  1795. 'no_stones': None,
  1796. 'order': None,
  1797. 'priority': None,
  1798. "since": None,
  1799. "status": None,
  1800. "tags": [],
  1801. },
  1802. "issues": lcl_issues,
  1803. u'pagination': {
  1804. u'first': u'http://localhost...',
  1805. u'last': u'http://localhost...',
  1806. u'next': None,
  1807. u'page': 1,
  1808. u'pages': 1,
  1809. u'per_page': 20,
  1810. u'prev': None
  1811. },
  1812. "total_issues": 2
  1813. }
  1814. )
  1815. # List all issues with no milestone
  1816. output = self.app.get('/api/0/test/issues?no_stones=1')
  1817. self.assertEqual(output.status_code, 200)
  1818. data = json.loads(output.get_data(as_text=True))
  1819. for idx in range(len(data['issues'])):
  1820. data['issues'][idx]['date_created'] = '1431414800'
  1821. data['issues'][idx]['last_updated'] = '1431414800'
  1822. for k in ['first', 'last']:
  1823. self.assertIsNotNone(data['pagination'][k])
  1824. data['pagination'][k] = 'http://localhost...'
  1825. self.assertDictEqual(
  1826. data,
  1827. {
  1828. "args": {
  1829. "assignee": None,
  1830. "author": None,
  1831. 'milestones': [],
  1832. 'no_stones': True,
  1833. 'order': None,
  1834. 'priority': None,
  1835. "since": None,
  1836. "status": None,
  1837. "tags": [],
  1838. },
  1839. "issues": [lcl_issues[1]],
  1840. u'pagination': {
  1841. u'first': u'http://localhost...',
  1842. u'last': u'http://localhost...',
  1843. u'next': None,
  1844. u'page': 1,
  1845. u'pages': 1,
  1846. u'per_page': 20,
  1847. u'prev': None
  1848. },
  1849. "total_issues": 1
  1850. }
  1851. )
  1852. # List all issues with a milestone
  1853. output = self.app.get('/api/0/test/issues?no_stones=0')
  1854. self.assertEqual(output.status_code, 200)
  1855. data = json.loads(output.get_data(as_text=True))
  1856. for idx in range(len(data['issues'])):
  1857. data['issues'][idx]['date_created'] = '1431414800'
  1858. data['issues'][idx]['last_updated'] = '1431414800'
  1859. for k in ['first', 'last']:
  1860. self.assertIsNotNone(data['pagination'][k])
  1861. data['pagination'][k] = 'http://localhost...'
  1862. self.assertDictEqual(
  1863. data,
  1864. {
  1865. "args": {
  1866. "assignee": None,
  1867. "author": None,
  1868. 'milestones': [],
  1869. 'no_stones': False,
  1870. 'order': None,
  1871. 'priority': None,
  1872. "since": None,
  1873. "status": None,
  1874. "tags": [],
  1875. },
  1876. "issues": [lcl_issues[0]],
  1877. u'pagination': {
  1878. u'first': u'http://localhost...',
  1879. u'last': u'http://localhost...',
  1880. u'next': None,
  1881. u'page': 1,
  1882. u'pages': 1,
  1883. u'per_page': 20,
  1884. u'prev': None
  1885. },
  1886. "total_issues": 1
  1887. }
  1888. )
  1889. def test_api_view_issues_since(self):
  1890. """ Test the api_view_issues method of the flask api for since option """
  1891. tests.create_projects(self.session)
  1892. tests.create_projects_git(
  1893. os.path.join(self.path, 'tickets'), bare=True)
  1894. tests.create_tokens(self.session)
  1895. tests.create_tokens_acl(self.session)
  1896. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  1897. # Create 1st tickets
  1898. start = arrow.utcnow().timestamp
  1899. issue = pagure.lib.model.Issue(
  1900. id=pagure.lib.query.get_next_id(self.session, repo.id),
  1901. project_id=repo.id,
  1902. title='Issue #1',
  1903. content='Description',
  1904. user_id=1, # pingou
  1905. uid='issue#1',
  1906. private=False,
  1907. )
  1908. self.session.add(issue)
  1909. self.session.commit()
  1910. time.sleep(1)
  1911. middle = arrow.utcnow().timestamp
  1912. # Create 2nd tickets
  1913. issue = pagure.lib.model.Issue(
  1914. id=pagure.lib.query.get_next_id(self.session, repo.id),
  1915. project_id=repo.id,
  1916. title='Issue #2',
  1917. content='Description',
  1918. user_id=1, # pingou
  1919. uid='issue#2',
  1920. private=False,
  1921. )
  1922. self.session.add(issue)
  1923. self.session.commit()
  1924. time.sleep(1)
  1925. final = arrow.utcnow().timestamp
  1926. # Create private issue
  1927. issue = pagure.lib.model.Issue(
  1928. id=pagure.lib.query.get_next_id(self.session, repo.id),
  1929. project_id=repo.id,
  1930. title='Issue #3',
  1931. content='Description',
  1932. user_id=1, # pingou
  1933. uid='issue#3',
  1934. private=True,
  1935. )
  1936. self.session.add(issue)
  1937. self.session.commit()
  1938. # Invalid repo
  1939. output = self.app.get('/api/0/foo/issues')
  1940. self.assertEqual(output.status_code, 404)
  1941. data = json.loads(output.get_data(as_text=True))
  1942. self.assertDictEqual(
  1943. data,
  1944. {
  1945. "error": "Project not found",
  1946. "error_code": "ENOPROJECT",
  1947. }
  1948. )
  1949. # List all opened issues
  1950. output = self.app.get('/api/0/test/issues')
  1951. self.assertEqual(output.status_code, 200)
  1952. data = json.loads(output.get_data(as_text=True))
  1953. for idx in range(len(data['issues'])):
  1954. data['issues'][idx]['date_created'] = '1431414800'
  1955. data['issues'][idx]['last_updated'] = '1431414800'
  1956. for k in ['first', 'last']:
  1957. self.assertIsNotNone(data['pagination'][k])
  1958. data['pagination'][k] = 'http://localhost...'
  1959. self.assertDictEqual(
  1960. data,
  1961. {
  1962. "args": {
  1963. "assignee": None,
  1964. "author": None,
  1965. 'milestones': [],
  1966. 'no_stones': None,
  1967. 'order': None,
  1968. 'priority': None,
  1969. "since": None,
  1970. "status": None,
  1971. "tags": []
  1972. },
  1973. "issues": LCL_ISSUES,
  1974. u'pagination': {
  1975. u'first': u'http://localhost...',
  1976. u'last': u'http://localhost...',
  1977. u'next': None,
  1978. u'page': 1,
  1979. u'pages': 1,
  1980. u'per_page': 20,
  1981. u'prev': None
  1982. },
  1983. "total_issues": 2
  1984. }
  1985. )
  1986. time.sleep(1)
  1987. late = arrow.utcnow().timestamp
  1988. # List all opened issues from the start
  1989. output = self.app.get('/api/0/test/issues?since=%s' % start)
  1990. self.assertEqual(output.status_code, 200)
  1991. data = json.loads(output.get_data(as_text=True))
  1992. for idx in range(len(data['issues'])):
  1993. data['issues'][idx]['date_created'] = '1431414800'
  1994. data['issues'][idx]['last_updated'] = '1431414800'
  1995. for k in ['first', 'last']:
  1996. self.assertIsNotNone(data['pagination'][k])
  1997. data['pagination'][k] = 'http://localhost...'
  1998. self.assertDictEqual(
  1999. data,
  2000. {
  2001. "args": {
  2002. "assignee": None,
  2003. "author": None,
  2004. 'milestones': [],
  2005. 'no_stones': None,
  2006. 'order': None,
  2007. 'priority': None,
  2008. "since": str(start),
  2009. "status": None,
  2010. "tags": []
  2011. },
  2012. "issues": LCL_ISSUES,
  2013. u'pagination': {
  2014. u'first': u'http://localhost...',
  2015. u'last': u'http://localhost...',
  2016. u'next': None,
  2017. u'page': 1,
  2018. u'pages': 1,
  2019. u'per_page': 20,
  2020. u'prev': None
  2021. },
  2022. "total_issues": 2
  2023. }
  2024. )
  2025. # List all opened issues from the middle
  2026. output = self.app.get('/api/0/test/issues?since=%s' % middle)
  2027. self.assertEqual(output.status_code, 200)
  2028. data = json.loads(output.get_data(as_text=True))
  2029. for idx in range(len(data['issues'])):
  2030. data['issues'][idx]['date_created'] = '1431414800'
  2031. data['issues'][idx]['last_updated'] = '1431414800'
  2032. for k in ['first', 'last']:
  2033. self.assertIsNotNone(data['pagination'][k])
  2034. data['pagination'][k] = 'http://localhost...'
  2035. self.assertDictEqual(
  2036. data,
  2037. {
  2038. "args": {
  2039. "assignee": None,
  2040. "author": None,
  2041. 'milestones': [],
  2042. 'no_stones': None,
  2043. 'order': None,
  2044. 'priority': None,
  2045. "since": str(middle),
  2046. "status": None,
  2047. "tags": []
  2048. },
  2049. "issues": LCL_ISSUES[:1],
  2050. u'pagination': {
  2051. u'first': u'http://localhost...',
  2052. u'last': u'http://localhost...',
  2053. u'next': None,
  2054. u'page': 1,
  2055. u'pages': 1,
  2056. u'per_page': 20,
  2057. u'prev': None
  2058. },
  2059. "total_issues": 1
  2060. }
  2061. )
  2062. # List all opened issues at the end
  2063. output = self.app.get('/api/0/test/issues?since=%s' % final)
  2064. self.assertEqual(output.status_code, 200)
  2065. data = json.loads(output.get_data(as_text=True))
  2066. for idx in range(len(data['issues'])):
  2067. data['issues'][idx]['date_created'] = '1431414800'
  2068. data['issues'][idx]['last_updated'] = '1431414800'
  2069. for k in ['first', 'last']:
  2070. self.assertIsNotNone(data['pagination'][k])
  2071. data['pagination'][k] = 'http://localhost...'
  2072. self.assertDictEqual(
  2073. data,
  2074. {
  2075. "args": {
  2076. "assignee": None,
  2077. "author": None,
  2078. 'milestones': [],
  2079. 'no_stones': None,
  2080. 'order': None,
  2081. 'priority': None,
  2082. "since": str(final),
  2083. "status": None,
  2084. "tags": []
  2085. },
  2086. "issues": [],
  2087. u'pagination': {
  2088. u'first': u'http://localhost...',
  2089. u'last': u'http://localhost...',
  2090. u'next': None,
  2091. u'page': 1,
  2092. u'pages': 0,
  2093. u'per_page': 20,
  2094. u'prev': None
  2095. },
  2096. "total_issues": 0
  2097. }
  2098. )
  2099. headers = {'Authorization': 'token aaabbbcccddd'}
  2100. # Test since for a value before creation of issues
  2101. output = self.app.get(
  2102. '/api/0/test/issues?since=%s' % final, headers=headers)
  2103. self.assertEqual(output.status_code, 200)
  2104. data = json.loads(output.get_data(as_text=True))
  2105. for idx in range(len(data['issues'])):
  2106. data['issues'][idx]['last_updated'] = '1431414800'
  2107. data['issues'][idx]['date_created'] = '1431414800'
  2108. for k in ['first', 'last']:
  2109. self.assertIsNotNone(data['pagination'][k])
  2110. data['pagination'][k] = 'http://localhost...'
  2111. self.assertDictEqual(
  2112. data,
  2113. {
  2114. "args": {
  2115. "assignee": None,
  2116. "author": None,
  2117. 'milestones': [],
  2118. 'no_stones': None,
  2119. 'order': None,
  2120. 'priority': None,
  2121. "since": str(final),
  2122. "status": None,
  2123. "tags": []
  2124. },
  2125. "issues": [{
  2126. 'assignee': None,
  2127. 'blocks': [],
  2128. 'close_status': None,
  2129. 'closed_at': None,
  2130. 'closed_by': None,
  2131. 'comments': [],
  2132. 'content': 'Description',
  2133. 'custom_fields': [],
  2134. 'date_created': '1431414800',
  2135. 'depends': [],
  2136. 'id': 3,
  2137. 'last_updated': '1431414800',
  2138. 'milestone': None,
  2139. 'priority': None,
  2140. 'private': True,
  2141. 'status': 'Open',
  2142. 'tags': [],
  2143. 'title': 'Issue #3',
  2144. 'user': {'fullname': 'PY C', 'name': 'pingou'}}
  2145. ],
  2146. u'pagination': {
  2147. u'first': u'http://localhost...',
  2148. u'last': u'http://localhost...',
  2149. u'next': None,
  2150. u'page': 1,
  2151. u'pages': 1,
  2152. u'per_page': 20,
  2153. u'prev': None
  2154. },
  2155. "total_issues": 1
  2156. }
  2157. )
  2158. def test_api_view_issue(self):
  2159. """ Test the api_view_issue method of the flask api. """
  2160. self.test_api_new_issue()
  2161. # Invalid repo
  2162. output = self.app.get('/api/0/foo/issue/1')
  2163. self.assertEqual(output.status_code, 404)
  2164. data = json.loads(output.get_data(as_text=True))
  2165. self.assertDictEqual(
  2166. data,
  2167. {
  2168. "error": "Project not found",
  2169. "error_code": "ENOPROJECT",
  2170. }
  2171. )
  2172. # Invalid issue for this repo
  2173. output = self.app.get('/api/0/test2/issue/1')
  2174. self.assertEqual(output.status_code, 404)
  2175. data = json.loads(output.get_data(as_text=True))
  2176. self.assertDictEqual(
  2177. data,
  2178. {
  2179. "error": "Issue not found",
  2180. "error_code": "ENOISSUE",
  2181. }
  2182. )
  2183. # Valid issue
  2184. output = self.app.get('/api/0/test/issue/1')
  2185. self.assertEqual(output.status_code, 200)
  2186. data = json.loads(output.get_data(as_text=True))
  2187. data['date_created'] = '1431414800'
  2188. data['last_updated'] = '1431414800'
  2189. self.assertDictEqual(
  2190. data,
  2191. {
  2192. "assignee": None,
  2193. "blocks": [],
  2194. "comments": [],
  2195. "content": "This issue needs attention",
  2196. "custom_fields": [],
  2197. "date_created": "1431414800",
  2198. "close_status": None,
  2199. "closed_at": None,
  2200. "closed_by": None,
  2201. "depends": [],
  2202. "id": 1,
  2203. "last_updated": "1431414800",
  2204. "milestone": None,
  2205. "priority": None,
  2206. "private": False,
  2207. "status": "Open",
  2208. "tags": [],
  2209. "title": "test issue",
  2210. "user": {
  2211. "fullname": "PY C",
  2212. "name": "pingou"
  2213. }
  2214. }
  2215. )
  2216. # Create private issue
  2217. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2218. msg = pagure.lib.query.new_issue(
  2219. session=self.session,
  2220. repo=repo,
  2221. title='Test issue',
  2222. content='We should work on this',
  2223. user='pingou',
  2224. private=True,
  2225. issue_uid='aaabbbccc',
  2226. )
  2227. self.session.commit()
  2228. self.assertEqual(msg.title, 'Test issue')
  2229. # Access private issue un-authenticated
  2230. output = self.app.get('/api/0/test/issue/2')
  2231. self.assertEqual(output.status_code, 403)
  2232. data = json.loads(output.get_data(as_text=True))
  2233. self.assertDictEqual(
  2234. data,
  2235. {
  2236. "error": "You are not allowed to view this issue",
  2237. "error_code": "EISSUENOTALLOWED",
  2238. }
  2239. )
  2240. headers = {'Authorization': 'token aaabbbccc'}
  2241. # Access private issue authenticated but non-existing token
  2242. output = self.app.get('/api/0/test/issue/2', headers=headers)
  2243. self.assertEqual(output.status_code, 401)
  2244. data = json.loads(output.get_data(as_text=True))
  2245. self.assertEqual(sorted(data.keys()), ['error', 'error_code'])
  2246. self.assertEqual(
  2247. pagure.api.APIERROR.EINVALIDTOK.value, data['error'])
  2248. self.assertEqual(
  2249. pagure.api.APIERROR.EINVALIDTOK.name, data['error_code'])
  2250. # Create a new token for another user
  2251. item = pagure.lib.model.Token(
  2252. id='bar_token',
  2253. user_id=2,
  2254. project_id=1,
  2255. expiration=datetime.datetime.utcnow() + datetime.timedelta(
  2256. days=30)
  2257. )
  2258. self.session.add(item)
  2259. self.session.commit()
  2260. headers = {'Authorization': 'token bar_token'}
  2261. # Access private issue authenticated but wrong token
  2262. output = self.app.get('/api/0/test/issue/2', headers=headers)
  2263. self.assertEqual(output.status_code, 403)
  2264. data = json.loads(output.get_data(as_text=True))
  2265. self.assertDictEqual(
  2266. data,
  2267. {
  2268. "error": "You are not allowed to view this issue",
  2269. "error_code": "EISSUENOTALLOWED",
  2270. }
  2271. )
  2272. headers = {'Authorization': 'token aaabbbcccddd'}
  2273. # Access private issue authenticated correctly
  2274. output = self.app.get('/api/0/test/issue/2', headers=headers)
  2275. self.assertEqual(output.status_code, 200)
  2276. data = json.loads(output.get_data(as_text=True))
  2277. data['date_created'] = '1431414800'
  2278. data['last_updated'] = '1431414800'
  2279. self.assertDictEqual(
  2280. data,
  2281. {
  2282. "assignee": None,
  2283. "blocks": [],
  2284. "comments": [],
  2285. "content": "We should work on this",
  2286. "custom_fields": [],
  2287. "date_created": "1431414800",
  2288. "close_status": None,
  2289. "closed_at": None,
  2290. "closed_by": None,
  2291. "depends": [],
  2292. "id": 2,
  2293. "last_updated": "1431414800",
  2294. "milestone": None,
  2295. "priority": None,
  2296. "private": True,
  2297. "status": "Open",
  2298. "tags": [],
  2299. "title": "Test issue",
  2300. "user": {
  2301. "fullname": "PY C",
  2302. "name": "pingou"
  2303. }
  2304. }
  2305. )
  2306. # Access private issue authenticated correctly using the issue's uid
  2307. output = self.app.get('/api/0/test/issue/aaabbbccc', headers=headers)
  2308. self.assertEqual(output.status_code, 200)
  2309. data = json.loads(output.get_data(as_text=True))
  2310. data['date_created'] = '1431414800'
  2311. data['last_updated'] = '1431414800'
  2312. self.assertDictEqual(
  2313. data,
  2314. {
  2315. "assignee": None,
  2316. "blocks": [],
  2317. "comments": [],
  2318. "content": "We should work on this",
  2319. "custom_fields": [],
  2320. "date_created": "1431414800",
  2321. "close_status": None,
  2322. "closed_at": None,
  2323. "closed_by": None,
  2324. "depends": [],
  2325. "id": 2,
  2326. "last_updated": "1431414800",
  2327. "milestone": None,
  2328. "priority": None,
  2329. "private": True,
  2330. "status": "Open",
  2331. "tags": [],
  2332. "title": "Test issue",
  2333. "user": {
  2334. "fullname": "PY C",
  2335. "name": "pingou"
  2336. }
  2337. }
  2338. )
  2339. def test_api_change_milestone_issue_invalid_project(self):
  2340. """ Test the api_change_milestone_issue method of the flask api. """
  2341. tests.create_projects(self.session)
  2342. tests.create_projects_git(os.path.join(self.path, 'tickets'))
  2343. tests.create_tokens(self.session)
  2344. tests.create_tokens_acl(self.session)
  2345. # Set some milestones to the project
  2346. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2347. repo.milestones = {'v1.0': None, 'v2.0': 'Soon'}
  2348. self.session.add(repo)
  2349. self.session.commit()
  2350. headers = {'Authorization': 'token aaabbbcccddd'}
  2351. # Invalid project
  2352. output = self.app.post('/api/0/foo/issue/1/milestone', headers=headers)
  2353. self.assertEqual(output.status_code, 404)
  2354. data = json.loads(output.get_data(as_text=True))
  2355. self.assertDictEqual(
  2356. data,
  2357. {
  2358. "error": "Project not found",
  2359. "error_code": "ENOPROJECT",
  2360. }
  2361. )
  2362. def test_api_change_milestone_issue_wrong_token(self):
  2363. """ Test the api_change_milestone_issue method of the flask api. """
  2364. tests.create_projects(self.session)
  2365. tests.create_projects_git(os.path.join(self.path, 'tickets'))
  2366. tests.create_tokens(self.session)
  2367. tests.create_tokens_acl(self.session)
  2368. # Set some milestones to the project
  2369. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2370. repo.milestones = {'v1.0': None, 'v2.0': 'Soon'}
  2371. self.session.add(repo)
  2372. self.session.commit()
  2373. headers = {'Authorization': 'token aaabbbcccddd'}
  2374. # Valid token, wrong project
  2375. output = self.app.post('/api/0/test2/issue/1/milestone', headers=headers)
  2376. self.assertEqual(output.status_code, 401)
  2377. data = json.loads(output.get_data(as_text=True))
  2378. self.assertEqual(sorted(data.keys()), ['error', 'error_code'])
  2379. self.assertEqual(
  2380. pagure.api.APIERROR.EINVALIDTOK.value, data['error'])
  2381. self.assertEqual(
  2382. pagure.api.APIERROR.EINVALIDTOK.name, data['error_code'])
  2383. def test_api_change_milestone_issue_no_issue(self):
  2384. """ Test the api_change_milestone_issue method of the flask api. """
  2385. tests.create_projects(self.session)
  2386. tests.create_projects_git(os.path.join(self.path, 'tickets'))
  2387. tests.create_tokens(self.session)
  2388. tests.create_tokens_acl(self.session)
  2389. # Set some milestones to the project
  2390. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2391. repo.milestones = {'v1.0': None, 'v2.0': 'Soon'}
  2392. self.session.add(repo)
  2393. self.session.commit()
  2394. headers = {'Authorization': 'token aaabbbcccddd'}
  2395. # No issue
  2396. output = self.app.post('/api/0/test/issue/1/milestone', headers=headers)
  2397. self.assertEqual(output.status_code, 404)
  2398. data = json.loads(output.get_data(as_text=True))
  2399. self.assertDictEqual(
  2400. data,
  2401. {
  2402. "error": "Issue not found",
  2403. "error_code": "ENOISSUE",
  2404. }
  2405. )
  2406. def test_api_change_milestone_issue_no_milestone(self):
  2407. """ Test the api_change_milestone_issue method of the flask api. """
  2408. tests.create_projects(self.session)
  2409. tests.create_projects_git(os.path.join(self.path, 'tickets'))
  2410. tests.create_tokens(self.session)
  2411. tests.create_tokens_acl(self.session)
  2412. # Set some milestones to the project
  2413. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2414. repo.milestones = {'v1.0': None, 'v2.0': 'Soon'}
  2415. self.session.add(repo)
  2416. self.session.commit()
  2417. headers = {'Authorization': 'token aaabbbcccddd'}
  2418. # Create normal issue
  2419. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2420. msg = pagure.lib.query.new_issue(
  2421. session=self.session,
  2422. repo=repo,
  2423. title='Test issue #1',
  2424. content='We should work on this',
  2425. user='pingou',
  2426. private=False,
  2427. )
  2428. self.session.commit()
  2429. self.assertEqual(msg.title, 'Test issue #1')
  2430. # Check milestone before
  2431. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2432. issue = pagure.lib.query.search_issues(self.session, repo, issueid=1)
  2433. self.assertEqual(issue.milestone, None)
  2434. data = {
  2435. 'milestone': '',
  2436. }
  2437. # Valid request but no milestone specified
  2438. output = self.app.post(
  2439. '/api/0/test/issue/1/milestone', data=data, headers=headers)
  2440. self.assertEqual(output.status_code, 200)
  2441. data = json.loads(output.get_data(as_text=True))
  2442. self.assertDictEqual(
  2443. data,
  2444. {'message': 'No changes'}
  2445. )
  2446. # No change
  2447. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2448. issue = pagure.lib.query.search_issues(self.session, repo, issueid=1)
  2449. self.assertEqual(issue.milestone, None)
  2450. def test_api_change_milestone_issue_invalid_milestone(self):
  2451. """ Test the api_change_milestone_issue method of the flask api. """
  2452. tests.create_projects(self.session)
  2453. tests.create_projects_git(os.path.join(self.path, 'tickets'))
  2454. tests.create_tokens(self.session)
  2455. tests.create_tokens_acl(self.session)
  2456. # Set some milestones to the project
  2457. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2458. repo.milestones = {'v1.0': None, 'v2.0': 'Soon'}
  2459. self.session.add(repo)
  2460. self.session.commit()
  2461. headers = {'Authorization': 'token aaabbbcccddd'}
  2462. # Create normal issue
  2463. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2464. msg = pagure.lib.query.new_issue(
  2465. session=self.session,
  2466. repo=repo,
  2467. title='Test issue #1',
  2468. content='We should work on this',
  2469. user='pingou',
  2470. private=False,
  2471. )
  2472. self.session.commit()
  2473. self.assertEqual(msg.title, 'Test issue #1')
  2474. # Check milestone before
  2475. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2476. issue = pagure.lib.query.search_issues(self.session, repo, issueid=1)
  2477. self.assertEqual(issue.milestone, None)
  2478. data = {
  2479. 'milestone': 'milestone-1-0',
  2480. }
  2481. # Invalid milestone specified
  2482. output = self.app.post(
  2483. '/api/0/test/issue/1/milestone', data=data, headers=headers)
  2484. self.assertEqual(output.status_code, 400)
  2485. data = json.loads(output.get_data(as_text=True))
  2486. self.assertDictEqual(
  2487. data,
  2488. {
  2489. "error": "Invalid or incomplete input submitted",
  2490. "error_code": "EINVALIDREQ",
  2491. "errors": {
  2492. "milestone": [
  2493. "Not a valid choice"
  2494. ]
  2495. }
  2496. }
  2497. )
  2498. def test_api_change_milestone_issue(self):
  2499. """ Test the api_change_milestone_issue method of the flask api. """
  2500. tests.create_projects(self.session)
  2501. tests.create_projects_git(os.path.join(self.path, 'tickets'))
  2502. tests.create_tokens(self.session)
  2503. tests.create_tokens_acl(self.session)
  2504. # Set some milestones to the project
  2505. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2506. repo.milestones = {'v1.0': None, 'v2.0': 'Soon'}
  2507. self.session.add(repo)
  2508. self.session.commit()
  2509. headers = {'Authorization': 'token aaabbbcccddd'}
  2510. # Create normal issue
  2511. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2512. msg = pagure.lib.query.new_issue(
  2513. session=self.session,
  2514. repo=repo,
  2515. title='Test issue #1',
  2516. content='We should work on this',
  2517. user='pingou',
  2518. private=False,
  2519. )
  2520. self.session.commit()
  2521. self.assertEqual(msg.title, 'Test issue #1')
  2522. # Check milestone before
  2523. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2524. issue = pagure.lib.query.search_issues(self.session, repo, issueid=1)
  2525. self.assertEqual(issue.milestone, None)
  2526. data = {
  2527. 'milestone': 'v1.0',
  2528. }
  2529. # Valid requests
  2530. output = self.app.post(
  2531. '/api/0/test/issue/1/milestone', data=data, headers=headers)
  2532. self.assertEqual(output.status_code, 200)
  2533. data = json.loads(output.get_data(as_text=True))
  2534. self.assertDictEqual(
  2535. data,
  2536. {
  2537. "message": [
  2538. "Issue set to the milestone: v1.0"
  2539. ]
  2540. }
  2541. )
  2542. def test_api_change_milestone_issue_remove_milestone(self):
  2543. """ Test the api_change_milestone_issue method of the flask api. """
  2544. tests.create_projects(self.session)
  2545. tests.create_projects_git(os.path.join(self.path, 'tickets'))
  2546. tests.create_tokens(self.session)
  2547. tests.create_tokens_acl(self.session)
  2548. # Set some milestones to the project
  2549. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2550. repo.milestones = {'v1.0': None, 'v2.0': 'Soon'}
  2551. self.session.add(repo)
  2552. self.session.commit()
  2553. headers = {'Authorization': 'token aaabbbcccddd'}
  2554. # Create normal issue
  2555. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2556. msg = pagure.lib.query.new_issue(
  2557. session=self.session,
  2558. repo=repo,
  2559. title='Test issue #1',
  2560. content='We should work on this',
  2561. user='pingou',
  2562. private=False,
  2563. )
  2564. self.session.commit()
  2565. self.assertEqual(msg.title, 'Test issue #1')
  2566. # Check milestone before
  2567. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2568. issue = pagure.lib.query.search_issues(self.session, repo, issueid=1)
  2569. self.assertEqual(issue.milestone, None)
  2570. data = {
  2571. 'milestone': 'v1.0',
  2572. }
  2573. # Valid requests
  2574. output = self.app.post(
  2575. '/api/0/test/issue/1/milestone', data=data, headers=headers)
  2576. self.assertEqual(output.status_code, 200)
  2577. data = json.loads(output.get_data(as_text=True))
  2578. self.assertDictEqual(
  2579. data,
  2580. {
  2581. "message": [
  2582. "Issue set to the milestone: v1.0"
  2583. ]
  2584. }
  2585. )
  2586. # remove milestone
  2587. data = {
  2588. 'milestone': '',
  2589. }
  2590. # Valid requests
  2591. output = self.app.post(
  2592. '/api/0/test/issue/1/milestone', data=data, headers=headers)
  2593. self.assertEqual(output.status_code, 200)
  2594. data = json.loads(output.get_data(as_text=True))
  2595. self.assertDictEqual(
  2596. data,
  2597. {
  2598. "message": [
  2599. "Issue set to the milestone: None (was: v1.0)"
  2600. ]
  2601. }
  2602. )
  2603. # Change recorded
  2604. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2605. issue = pagure.lib.query.search_issues(self.session, repo, issueid=1)
  2606. self.assertEqual(issue.milestone, None)
  2607. def test_api_change_milestone_issue_remove_milestone2(self):
  2608. """ Test the api_change_milestone_issue method of the flask api. """
  2609. tests.create_projects(self.session)
  2610. tests.create_projects_git(os.path.join(self.path, 'tickets'))
  2611. tests.create_tokens(self.session)
  2612. tests.create_tokens_acl(self.session)
  2613. # Set some milestones to the project
  2614. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2615. repo.milestones = {'v1.0': None, 'v2.0': 'Soon'}
  2616. self.session.add(repo)
  2617. self.session.commit()
  2618. headers = {'Authorization': 'token aaabbbcccddd'}
  2619. # Create normal issue
  2620. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2621. msg = pagure.lib.query.new_issue(
  2622. session=self.session,
  2623. repo=repo,
  2624. title='Test issue #1',
  2625. content='We should work on this',
  2626. user='pingou',
  2627. private=False,
  2628. )
  2629. self.session.commit()
  2630. self.assertEqual(msg.title, 'Test issue #1')
  2631. # Check milestone before
  2632. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2633. issue = pagure.lib.query.search_issues(self.session, repo, issueid=1)
  2634. self.assertEqual(issue.milestone, None)
  2635. data = {
  2636. 'milestone': 'v1.0',
  2637. }
  2638. # Valid requests
  2639. output = self.app.post(
  2640. '/api/0/test/issue/1/milestone', data=data, headers=headers)
  2641. self.assertEqual(output.status_code, 200)
  2642. data = json.loads(output.get_data(as_text=True))
  2643. self.assertDictEqual(
  2644. data,
  2645. {
  2646. "message": [
  2647. "Issue set to the milestone: v1.0"
  2648. ]
  2649. }
  2650. )
  2651. # remove milestone by using no milestone in JSON
  2652. data = {}
  2653. # Valid requests
  2654. output = self.app.post(
  2655. '/api/0/test/issue/1/milestone', data=data, headers=headers)
  2656. self.assertEqual(output.status_code, 200)
  2657. data = json.loads(output.get_data(as_text=True))
  2658. self.assertDictEqual(
  2659. data,
  2660. {
  2661. "message": [
  2662. "Issue set to the milestone: None (was: v1.0)"
  2663. ]
  2664. }
  2665. )
  2666. # Change recorded
  2667. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2668. issue = pagure.lib.query.search_issues(self.session, repo, issueid=1)
  2669. self.assertEqual(issue.milestone, None)
  2670. def test_api_change_milestone_issue_unauthorized(self):
  2671. """ Test the api_change_milestone_issue method of the flask api. """
  2672. tests.create_projects(self.session)
  2673. tests.create_projects_git(os.path.join(self.path, 'tickets'))
  2674. tests.create_tokens(self.session)
  2675. tests.create_tokens_acl(self.session)
  2676. # Set some milestones to the project
  2677. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2678. repo.milestones = {'v1.0': None, 'v2.0': 'Soon'}
  2679. self.session.add(repo)
  2680. self.session.commit()
  2681. headers = {'Authorization': 'token aaabbbcccddd'}
  2682. # Create normal issue
  2683. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2684. msg = pagure.lib.query.new_issue(
  2685. session=self.session,
  2686. repo=repo,
  2687. title='Test issue #1',
  2688. content='We should work on this',
  2689. user='pingou',
  2690. private=False,
  2691. )
  2692. self.session.commit()
  2693. self.assertEqual(msg.title, 'Test issue #1')
  2694. headers = {'Authorization': 'token pingou_foo'}
  2695. data = {'milestone': 'v1.0',}
  2696. # Un-authorized issue
  2697. output = self.app.post(
  2698. '/api/0/foo/issue/1/milestone', data={}, headers=headers)
  2699. self.assertEqual(output.status_code, 401)
  2700. data = json.loads(output.get_data(as_text=True))
  2701. self.assertEqual(sorted(data.keys()), ['error', 'error_code'])
  2702. self.assertEqual(
  2703. pagure.api.APIERROR.EINVALIDTOK.value, data['error'])
  2704. self.assertEqual(
  2705. pagure.api.APIERROR.EINVALIDTOK.name, data['error_code'])
  2706. @patch('pagure.lib.notify.send_email', MagicMock(return_value=True))
  2707. @patch(
  2708. 'pagure.lib.query.add_metadata_update_notif',
  2709. MagicMock(side_effect=pagure.exceptions.PagureException('error')))
  2710. def test_api_change_milestone_issue_raises_exception(self):
  2711. """ Test the api_change_milestone_issue method of the flask api. """
  2712. tests.create_projects(self.session)
  2713. tests.create_projects_git(os.path.join(self.path, 'tickets'))
  2714. tests.create_tokens(self.session)
  2715. tests.create_tokens_acl(self.session)
  2716. # Set some milestones to the project
  2717. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2718. repo.milestones = {'v1.0': None, 'v2.0': 'Soon'}
  2719. self.session.add(repo)
  2720. self.session.commit()
  2721. headers = {'Authorization': 'token aaabbbcccddd'}
  2722. # Create normal issue
  2723. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2724. msg = pagure.lib.query.new_issue(
  2725. session=self.session,
  2726. repo=repo,
  2727. title='Test issue #1',
  2728. content='We should work on this',
  2729. user='pingou',
  2730. private=False,
  2731. )
  2732. self.session.commit()
  2733. self.assertEqual(msg.title, 'Test issue #1')
  2734. data = {
  2735. 'milestone': 'v1.0',
  2736. }
  2737. # Valid requests
  2738. output = self.app.post(
  2739. '/api/0/test/issue/1/milestone', data=data, headers=headers)
  2740. self.assertEqual(output.status_code, 400)
  2741. data = json.loads(output.get_data(as_text=True))
  2742. self.assertDictEqual(
  2743. data,
  2744. {u'error': u'error', u'error_code': u'ENOCODE'}
  2745. )
  2746. @patch('pagure.lib.git.update_git')
  2747. @patch('pagure.lib.notify.send_email')
  2748. def test_api_view_issue_comment(self, p_send_email, p_ugt):
  2749. """ Test the api_view_issue_comment endpoint. """
  2750. p_send_email.return_value = True
  2751. p_ugt.return_value = True
  2752. tests.create_projects(self.session)
  2753. tests.create_projects_git(os.path.join(self.path, 'tickets'))
  2754. tests.create_tokens(self.session)
  2755. tests.create_tokens_acl(self.session)
  2756. # Create normal issue in test
  2757. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2758. msg = pagure.lib.query.new_issue(
  2759. session=self.session,
  2760. repo=repo,
  2761. title='Test issue #1',
  2762. content='We should work on this',
  2763. user='pingou',
  2764. private=False,
  2765. issue_uid='aaabbbccc1',
  2766. )
  2767. self.session.commit()
  2768. self.assertEqual(msg.title, 'Test issue #1')
  2769. headers = {'Authorization': 'token aaabbbcccddd'}
  2770. data = {
  2771. 'comment': 'This is a very interesting question',
  2772. }
  2773. # Valid request
  2774. output = self.app.post(
  2775. '/api/0/test/issue/1/comment', data=data, headers=headers)
  2776. self.assertEqual(output.status_code, 200)
  2777. data = json.loads(output.get_data(as_text=True))
  2778. data["avatar_url"] = "https://seccdn.libravatar.org/avatar/..."
  2779. self.assertDictEqual(
  2780. data,
  2781. {'message': 'Comment added',
  2782. 'avatar_url': 'https://seccdn.libravatar.org/avatar/...',
  2783. 'user': 'pingou'}
  2784. )
  2785. # One comment added
  2786. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2787. issue = pagure.lib.query.search_issues(self.session, repo, issueid=1)
  2788. self.assertEqual(len(issue.comments), 1)
  2789. # View a comment that does not exist
  2790. output = self.app.get('/api/0/foo/issue/100/comment/2')
  2791. self.assertEqual(output.status_code, 404)
  2792. # Issue exists but not the comment
  2793. output = self.app.get('/api/0/test/issue/1/comment/2')
  2794. self.assertEqual(output.status_code, 404)
  2795. # Issue and comment exists
  2796. output = self.app.get('/api/0/test/issue/1/comment/1')
  2797. self.assertEqual(output.status_code, 200)
  2798. data = json.loads(output.get_data(as_text=True))
  2799. data['date_created'] = '1435821770'
  2800. data["comment_date"] = "2015-07-02 09:22"
  2801. data["avatar_url"] = "https://seccdn.libravatar.org/avatar/..."
  2802. self.assertDictEqual(
  2803. data,
  2804. {
  2805. "avatar_url": "https://seccdn.libravatar.org/avatar/...",
  2806. "comment": "This is a very interesting question",
  2807. "comment_date": "2015-07-02 09:22",
  2808. "date_created": "1435821770",
  2809. "edited_on": None,
  2810. "editor": None,
  2811. "notification": False,
  2812. "id": 1,
  2813. "parent": None,
  2814. "reactions": {},
  2815. "user": {
  2816. "fullname": "PY C",
  2817. "name": "pingou"
  2818. }
  2819. }
  2820. )
  2821. # Issue and comment exists, using UID
  2822. output = self.app.get('/api/0/test/issue/aaabbbccc1/comment/1')
  2823. self.assertEqual(output.status_code, 200)
  2824. data = json.loads(output.get_data(as_text=True))
  2825. data['date_created'] = '1435821770'
  2826. data["comment_date"] = "2015-07-02 09:22"
  2827. data["avatar_url"] = "https://seccdn.libravatar.org/avatar/..."
  2828. self.assertDictEqual(
  2829. data,
  2830. {
  2831. "avatar_url": "https://seccdn.libravatar.org/avatar/...",
  2832. "comment": "This is a very interesting question",
  2833. "comment_date": "2015-07-02 09:22",
  2834. "date_created": "1435821770",
  2835. "edited_on": None,
  2836. "editor": None,
  2837. "notification": False,
  2838. "id": 1,
  2839. "parent": None,
  2840. "reactions": {},
  2841. "user": {
  2842. "fullname": "PY C",
  2843. "name": "pingou"
  2844. }
  2845. }
  2846. )
  2847. @patch('pagure.lib.git.update_git')
  2848. @patch('pagure.lib.notify.send_email')
  2849. def test_api_view_issue_comment_private(self, p_send_email, p_ugt):
  2850. """ Test the api_view_issue_comment endpoint. """
  2851. p_send_email.return_value = True
  2852. p_ugt.return_value = True
  2853. tests.create_projects(self.session)
  2854. tests.create_projects_git(os.path.join(self.path, 'tickets'))
  2855. tests.create_tokens(self.session)
  2856. tests.create_tokens_acl(self.session)
  2857. # Create normal issue in test
  2858. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2859. msg = pagure.lib.query.new_issue(
  2860. session=self.session,
  2861. repo=repo,
  2862. title='Test issue #1',
  2863. content='We should work on this',
  2864. user='foo',
  2865. private=True,
  2866. issue_uid='aaabbbccc1',
  2867. )
  2868. self.session.commit()
  2869. self.assertEqual(msg.title, 'Test issue #1')
  2870. # Create a token for another user
  2871. item = pagure.lib.model.Token(
  2872. id='foo_token_2',
  2873. user_id=2,
  2874. project_id=1,
  2875. expiration=datetime.datetime.utcnow()
  2876. + datetime.timedelta(days=30)
  2877. )
  2878. self.session.add(item)
  2879. self.session.commit()
  2880. tests.create_tokens_acl(self.session, token_id='foo_token_2')
  2881. # Add a comment to that issue
  2882. data = {
  2883. 'comment': 'This is a very interesting question',
  2884. }
  2885. headers = {'Authorization': 'token foo_token_2'}
  2886. output = self.app.post(
  2887. '/api/0/test/issue/1/comment', data=data, headers=headers)
  2888. self.assertEqual(output.status_code, 200)
  2889. data = json.loads(output.get_data(as_text=True))
  2890. data["avatar_url"] = "https://seccdn.libravatar.org/avatar/..."
  2891. self.assertDictEqual(
  2892. data,
  2893. {'message': 'Comment added',
  2894. 'avatar_url': 'https://seccdn.libravatar.org/avatar/...',
  2895. 'user': 'foo'}
  2896. )
  2897. # Private issue - no auth
  2898. output = self.app.get('/api/0/test/issue/1/comment/2')
  2899. self.assertEqual(output.status_code, 403)
  2900. # Private issue - Auth - Invalid token
  2901. headers = {'Authorization': 'token aaabbbcccdddee'}
  2902. output = self.app.get('/api/0/test/issue/1/comment/2', headers=headers)
  2903. self.assertEqual(output.status_code, 401)
  2904. # Private issue - Auth - valid token - unknown comment
  2905. headers = {'Authorization': 'token foo_token_2'}
  2906. output = self.app.get('/api/0/test/issue/1/comment/3', headers=headers)
  2907. self.assertEqual(output.status_code, 404)
  2908. # Private issue - Auth - valid token - known comment
  2909. headers = {'Authorization': 'token foo_token_2'}
  2910. output = self.app.get('/api/0/test/issue/1/comment/1', headers=headers)
  2911. self.assertEqual(output.status_code, 200)
  2912. data = json.loads(output.get_data(as_text=True))
  2913. data['date_created'] = '1435821770'
  2914. data["comment_date"] = "2015-07-02 09:22"
  2915. data["avatar_url"] = "https://seccdn.libravatar.org/avatar/..."
  2916. self.assertDictEqual(
  2917. data,
  2918. {
  2919. "avatar_url": "https://seccdn.libravatar.org/avatar/...",
  2920. "comment": "This is a very interesting question",
  2921. "comment_date": "2015-07-02 09:22",
  2922. "date_created": "1435821770",
  2923. "edited_on": None,
  2924. "editor": None,
  2925. "notification": False,
  2926. "id": 1,
  2927. "parent": None,
  2928. "reactions": {},
  2929. "user": {
  2930. "fullname": "foo bar",
  2931. "name": "foo"
  2932. }
  2933. }
  2934. )
  2935. @patch('pagure.lib.git.update_git')
  2936. @patch('pagure.lib.notify.send_email')
  2937. def test_api_assign_issue(self, p_send_email, p_ugt):
  2938. """ Test the api_assign_issue method of the flask api. """
  2939. p_send_email.return_value = True
  2940. p_ugt.return_value = True
  2941. tests.create_projects(self.session)
  2942. tests.create_tokens(self.session)
  2943. tests.create_tokens_acl(self.session)
  2944. headers = {'Authorization': 'token aaabbbcccddd'}
  2945. # Invalid project
  2946. output = self.app.post('/api/0/foo/issue/1/assign', headers=headers)
  2947. self.assertEqual(output.status_code, 404)
  2948. data = json.loads(output.get_data(as_text=True))
  2949. self.assertDictEqual(
  2950. data,
  2951. {
  2952. "error": "Project not found",
  2953. "error_code": "ENOPROJECT",
  2954. }
  2955. )
  2956. # Valid token, wrong project
  2957. output = self.app.post('/api/0/test2/issue/1/assign', headers=headers)
  2958. self.assertEqual(output.status_code, 401)
  2959. data = json.loads(output.get_data(as_text=True))
  2960. self.assertEqual(sorted(data.keys()), ['error', 'error_code'])
  2961. self.assertEqual(
  2962. pagure.api.APIERROR.EINVALIDTOK.value, data['error'])
  2963. self.assertEqual(
  2964. pagure.api.APIERROR.EINVALIDTOK.name, data['error_code'])
  2965. # No input
  2966. output = self.app.post('/api/0/test/issue/1/assign', headers=headers)
  2967. self.assertEqual(output.status_code, 404)
  2968. data = json.loads(output.get_data(as_text=True))
  2969. self.assertDictEqual(
  2970. data,
  2971. {
  2972. "error": "Issue not found",
  2973. "error_code": "ENOISSUE",
  2974. }
  2975. )
  2976. # Create normal issue
  2977. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2978. msg = pagure.lib.query.new_issue(
  2979. session=self.session,
  2980. repo=repo,
  2981. title='Test issue #1',
  2982. content='We should work on this',
  2983. user='pingou',
  2984. private=False,
  2985. issue_uid='aaabbbccc1',
  2986. )
  2987. self.session.commit()
  2988. self.assertEqual(msg.title, 'Test issue #1')
  2989. # Check comments before
  2990. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2991. issue = pagure.lib.query.search_issues(self.session, repo, issueid=1)
  2992. self.assertEqual(len(issue.comments), 0)
  2993. # No change
  2994. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2995. issue = pagure.lib.query.search_issues(self.session, repo, issueid=1)
  2996. self.assertEqual(issue.status, 'Open')
  2997. data = {
  2998. 'assignee': 'pingou',
  2999. }
  3000. # Valid request
  3001. output = self.app.post(
  3002. '/api/0/test/issue/1/assign', data=data, headers=headers)
  3003. self.assertEqual(output.status_code, 200)
  3004. data = json.loads(output.get_data(as_text=True))
  3005. self.assertDictEqual(
  3006. data,
  3007. {'message': 'Issue assigned to pingou'}
  3008. )
  3009. # Un-assign
  3010. output = self.app.post(
  3011. '/api/0/test/issue/1/assign', data=data, headers=headers)
  3012. self.assertEqual(output.status_code, 200)
  3013. data = json.loads(output.get_data(as_text=True))
  3014. self.assertDictEqual(
  3015. data,
  3016. {'message': 'Assignee reset'}
  3017. )
  3018. # No change
  3019. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  3020. issue = pagure.lib.query.search_issues(self.session, repo, issueid=1)
  3021. self.assertEqual(issue.assignee, None)
  3022. # Un-assign
  3023. data = {'assignee': None}
  3024. output = self.app.post(
  3025. '/api/0/test/issue/1/assign', data=data, headers=headers)
  3026. self.assertEqual(output.status_code, 200)
  3027. data = json.loads(output.get_data(as_text=True))
  3028. self.assertDictEqual(
  3029. data,
  3030. {'message': 'Nothing to change'}
  3031. )
  3032. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  3033. issue = pagure.lib.query.search_issues(self.session, repo, issueid=1)
  3034. self.assertEqual(issue.assignee, None)
  3035. # Re-assign for the rest of the tests
  3036. data = {'assignee': 'pingou'}
  3037. output = self.app.post(
  3038. '/api/0/test/issue/1/assign', data=data, headers=headers)
  3039. self.assertEqual(output.status_code, 200)
  3040. data = json.loads(output.get_data(as_text=True))
  3041. self.assertDictEqual(
  3042. data,
  3043. {'message': 'Issue assigned to pingou'}
  3044. )
  3045. # Un-assign
  3046. data = {'assignee': ''}
  3047. output = self.app.post(
  3048. '/api/0/test/issue/1/assign', data=data, headers=headers)
  3049. self.assertEqual(output.status_code, 200)
  3050. data = json.loads(output.get_data(as_text=True))
  3051. self.assertDictEqual(
  3052. data,
  3053. {'message': 'Assignee reset'}
  3054. )
  3055. # Re-assign for the rest of the tests
  3056. data = {'assignee': 'pingou'}
  3057. output = self.app.post(
  3058. '/api/0/test/issue/1/assign', data=data, headers=headers)
  3059. self.assertEqual(output.status_code, 200)
  3060. data = json.loads(output.get_data(as_text=True))
  3061. self.assertDictEqual(
  3062. data,
  3063. {'message': 'Issue assigned to pingou'}
  3064. )
  3065. # One comment added
  3066. self.session.commit()
  3067. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  3068. issue = pagure.lib.query.search_issues(self.session, repo, issueid=1)
  3069. self.assertEqual(issue.assignee.user, 'pingou')
  3070. # Create another project
  3071. item = pagure.lib.model.Project(
  3072. user_id=2, # foo
  3073. name='foo',
  3074. description='test project #3',
  3075. hook_token='aaabbbdddeee',
  3076. )
  3077. self.session.add(item)
  3078. self.session.commit()
  3079. # Create a token for pingou for this project
  3080. item = pagure.lib.model.Token(
  3081. id='pingou_foo',
  3082. user_id=1,
  3083. project_id=4,
  3084. expiration=datetime.datetime.utcnow() + datetime.timedelta(
  3085. days=30)
  3086. )
  3087. self.session.add(item)
  3088. self.session.commit()
  3089. # Give `issue_change_status` to this token when `issue_comment`
  3090. # is required
  3091. item = pagure.lib.model.TokenAcl(
  3092. token_id='pingou_foo',
  3093. acl_id=8,
  3094. )
  3095. self.session.add(item)
  3096. self.session.commit()
  3097. repo = pagure.lib.query.get_authorized_project(self.session, 'foo')
  3098. # Create private issue
  3099. msg = pagure.lib.query.new_issue(
  3100. session=self.session,
  3101. repo=repo,
  3102. title='Test issue',
  3103. content='We should work on this',
  3104. user='foo',
  3105. private=True,
  3106. issue_uid='aaabbbccc#2',
  3107. )
  3108. self.session.commit()
  3109. self.assertEqual(msg.title, 'Test issue')
  3110. # Check before
  3111. repo = pagure.lib.query.get_authorized_project(self.session, 'foo')
  3112. issue = pagure.lib.query.search_issues(self.session, repo, issueid=1)
  3113. self.assertEqual(len(issue.comments), 0)
  3114. data = {
  3115. 'assignee': 'pingou',
  3116. }
  3117. headers = {'Authorization': 'token pingou_foo'}
  3118. # Valid request but un-authorized
  3119. output = self.app.post(
  3120. '/api/0/foo/issue/1/assign', data=data, headers=headers)
  3121. self.assertEqual(output.status_code, 401)
  3122. data = json.loads(output.get_data(as_text=True))
  3123. self.assertEqual(sorted(data.keys()), ['error', 'error_code'])
  3124. self.assertEqual(
  3125. pagure.api.APIERROR.EINVALIDTOK.value, data['error'])
  3126. self.assertEqual(
  3127. pagure.api.APIERROR.EINVALIDTOK.name, data['error_code'])
  3128. # No comment added
  3129. repo = pagure.lib.query.get_authorized_project(self.session, 'foo')
  3130. issue = pagure.lib.query.search_issues(self.session, repo, issueid=1)
  3131. self.assertEqual(len(issue.comments), 0)
  3132. # Create token for user foo
  3133. item = pagure.lib.model.Token(
  3134. id='foo_token2',
  3135. user_id=2,
  3136. project_id=4,
  3137. expiration=datetime.datetime.utcnow() + datetime.timedelta(days=30)
  3138. )
  3139. self.session.add(item)
  3140. self.session.commit()
  3141. tests.create_tokens_acl(self.session, token_id='foo_token2')
  3142. data = {
  3143. 'assignee': 'pingou',
  3144. }
  3145. headers = {'Authorization': 'token foo_token2'}
  3146. # Valid request and authorized
  3147. output = self.app.post(
  3148. '/api/0/foo/issue/1/assign', data=data, headers=headers)
  3149. self.assertEqual(output.status_code, 200)
  3150. data = json.loads(output.get_data(as_text=True))
  3151. self.assertDictEqual(
  3152. data,
  3153. {'message': 'Issue assigned to pingou'}
  3154. )
  3155. @patch('pagure.lib.git.update_git')
  3156. @patch('pagure.lib.notify.send_email')
  3157. def test_api_assign_issue_issuer(self, p_send_email, p_ugt):
  3158. """ Test the api_assign_issue method of the flask api. """
  3159. p_send_email.return_value = True
  3160. p_ugt.return_value = True
  3161. tests.create_projects(self.session)
  3162. tests.create_tokens(self.session, user_id=2)
  3163. tests.create_tokens_acl(self.session)
  3164. headers = {'Authorization': 'token aaabbbcccddd'}
  3165. # Create normal issue
  3166. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  3167. msg = pagure.lib.query.new_issue(
  3168. session=self.session,
  3169. repo=repo,
  3170. title='Test issue #1',
  3171. content='We should work on this',
  3172. user='pingou',
  3173. private=False,
  3174. issue_uid='aaabbbccc1',
  3175. assignee='foo',
  3176. )
  3177. self.session.commit()
  3178. self.assertEqual(msg.title, 'Test issue #1')
  3179. # Check comments before
  3180. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  3181. issue = pagure.lib.query.search_issues(self.session, repo, issueid=1)
  3182. self.assertEqual(len(issue.comments), 0)
  3183. # Un-assign
  3184. data = {'assignee': None}
  3185. output = self.app.post(
  3186. '/api/0/test/issue/1/assign', data={}, headers=headers)
  3187. self.assertEqual(output.status_code, 200)
  3188. data = json.loads(output.get_data(as_text=True))
  3189. self.assertDictEqual(
  3190. data,
  3191. {'message': 'Assignee reset'}
  3192. )
  3193. # No longer allowed to self-assign since no access
  3194. data = {
  3195. 'assignee': 'foo',
  3196. }
  3197. output = self.app.post(
  3198. '/api/0/test/issue/1/assign', data=data, headers=headers)
  3199. self.assertEqual(output.status_code, 403)
  3200. data = json.loads(output.get_data(as_text=True))
  3201. self.assertDictEqual(
  3202. data,
  3203. {
  3204. 'error': 'You are not allowed to view this issue',
  3205. 'error_code': 'EISSUENOTALLOWED'
  3206. }
  3207. )
  3208. @patch('pagure.lib.git.update_git')
  3209. @patch('pagure.lib.notify.send_email')
  3210. def test_api_subscribe_issue(self, p_send_email, p_ugt):
  3211. """ Test the api_subscribe_issue method of the flask api. """
  3212. p_send_email.return_value = True
  3213. p_ugt.return_value = True
  3214. item = pagure.lib.model.User(
  3215. user='bar',
  3216. fullname='bar foo',
  3217. password='foo',
  3218. default_email='bar@bar.com',
  3219. )
  3220. self.session.add(item)
  3221. item = pagure.lib.model.UserEmail(
  3222. user_id=3,
  3223. email='bar@bar.com')
  3224. self.session.add(item)
  3225. self.session.commit()
  3226. tests.create_projects(self.session)
  3227. tests.create_tokens(self.session, user_id=3)
  3228. tests.create_tokens_acl(self.session)
  3229. headers = {'Authorization': 'token aaabbbcccddd'}
  3230. # Invalid project
  3231. output = self.app.post(
  3232. '/api/0/foo/issue/1/subscribe', headers=headers)
  3233. self.assertEqual(output.status_code, 404)
  3234. data = json.loads(output.get_data(as_text=True))
  3235. self.assertDictEqual(
  3236. data,
  3237. {
  3238. "error": "Project not found",
  3239. "error_code": "ENOPROJECT",
  3240. }
  3241. )
  3242. # Valid token, wrong project
  3243. output = self.app.post(
  3244. '/api/0/test2/issue/1/subscribe', headers=headers)
  3245. self.assertEqual(output.status_code, 401)
  3246. data = json.loads(output.get_data(as_text=True))
  3247. self.assertEqual(sorted(data.keys()), ['error', 'error_code'])
  3248. self.assertEqual(
  3249. pagure.api.APIERROR.EINVALIDTOK.value, data['error'])
  3250. self.assertEqual(
  3251. pagure.api.APIERROR.EINVALIDTOK.name, data['error_code'])
  3252. # No input
  3253. output = self.app.post(
  3254. '/api/0/test/issue/1/subscribe', headers=headers)
  3255. self.assertEqual(output.status_code, 404)
  3256. data = json.loads(output.get_data(as_text=True))
  3257. self.assertDictEqual(
  3258. data,
  3259. {
  3260. "error": "Issue not found",
  3261. "error_code": "ENOISSUE",
  3262. }
  3263. )
  3264. # Create normal issue
  3265. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  3266. msg = pagure.lib.query.new_issue(
  3267. session=self.session,
  3268. repo=repo,
  3269. title='Test issue #1',
  3270. content='We should work on this',
  3271. user='foo',
  3272. private=False,
  3273. issue_uid='aaabbbccc1',
  3274. )
  3275. self.session.commit()
  3276. self.assertEqual(msg.title, 'Test issue #1')
  3277. # Check subscribtion before
  3278. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  3279. issue = pagure.lib.query.search_issues(self.session, repo, issueid=1)
  3280. self.assertEqual(
  3281. pagure.lib.query.get_watch_list(self.session, issue),
  3282. set(['pingou', 'foo']))
  3283. # Unsubscribe - no changes
  3284. data = {}
  3285. output = self.app.post(
  3286. '/api/0/test/issue/1/subscribe', data=data, headers=headers)
  3287. self.assertEqual(output.status_code, 200)
  3288. data = json.loads(output.get_data(as_text=True))
  3289. data["avatar_url"] = "https://seccdn.libravatar.org/avatar/..."
  3290. self.assertDictEqual(
  3291. data,
  3292. {'message': 'You are no longer watching this issue',
  3293. 'avatar_url': 'https://seccdn.libravatar.org/avatar/...',
  3294. 'user': 'bar'}
  3295. )
  3296. data = {}
  3297. output = self.app.post(
  3298. '/api/0/test/issue/1/subscribe', data=data, headers=headers)
  3299. self.assertEqual(output.status_code, 200)
  3300. data = json.loads(output.get_data(as_text=True))
  3301. data["avatar_url"] = "https://seccdn.libravatar.org/avatar/..."
  3302. self.assertDictEqual(
  3303. data,
  3304. {'message': 'You are no longer watching this issue',
  3305. 'avatar_url': 'https://seccdn.libravatar.org/avatar/...',
  3306. 'user': 'bar'}
  3307. )
  3308. # No change
  3309. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  3310. issue = pagure.lib.query.search_issues(self.session, repo, issueid=1)
  3311. self.assertEqual(
  3312. pagure.lib.query.get_watch_list(self.session, issue),
  3313. set(['pingou', 'foo']))
  3314. # Subscribe
  3315. data = {'status': True}
  3316. output = self.app.post(
  3317. '/api/0/test/issue/1/subscribe', data=data, headers=headers)
  3318. self.assertEqual(output.status_code, 200)
  3319. data = json.loads(output.get_data(as_text=True))
  3320. data["avatar_url"] = "https://seccdn.libravatar.org/avatar/..."
  3321. self.assertDictEqual(
  3322. data,
  3323. {'message': 'You are now watching this issue',
  3324. 'avatar_url': 'https://seccdn.libravatar.org/avatar/...',
  3325. 'user': 'bar'}
  3326. )
  3327. # Subscribe - no changes
  3328. data = {'status': True}
  3329. output = self.app.post(
  3330. '/api/0/test/issue/1/subscribe', data=data, headers=headers)
  3331. self.assertEqual(output.status_code, 200)
  3332. data = json.loads(output.get_data(as_text=True))
  3333. data["avatar_url"] = "https://seccdn.libravatar.org/avatar/..."
  3334. self.assertDictEqual(
  3335. data,
  3336. {'message': 'You are now watching this issue',
  3337. 'avatar_url': 'https://seccdn.libravatar.org/avatar/...',
  3338. 'user': 'bar'}
  3339. )
  3340. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  3341. issue = pagure.lib.query.search_issues(self.session, repo, issueid=1)
  3342. self.assertEqual(
  3343. pagure.lib.query.get_watch_list(self.session, issue),
  3344. set(['pingou', 'foo', 'bar']))
  3345. # Unsubscribe
  3346. data = {}
  3347. output = self.app.post(
  3348. '/api/0/test/issue/1/subscribe', data=data, headers=headers)
  3349. self.assertEqual(output.status_code, 200)
  3350. data = json.loads(output.get_data(as_text=True))
  3351. data["avatar_url"] = "https://seccdn.libravatar.org/avatar/..."
  3352. self.assertDictEqual(
  3353. data,
  3354. {'message': 'You are no longer watching this issue',
  3355. 'avatar_url': 'https://seccdn.libravatar.org/avatar/...',
  3356. 'user': 'bar'}
  3357. )
  3358. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  3359. issue = pagure.lib.query.search_issues(self.session, repo, issueid=1)
  3360. self.assertEqual(
  3361. pagure.lib.query.get_watch_list(self.session, issue),
  3362. set(['pingou', 'foo']))
  3363. def test_api_update_custom_field(self):
  3364. """ Test the api_update_custom_field method of the flask api. """
  3365. tests.create_projects(self.session)
  3366. tests.create_projects_git(os.path.join(self.path, 'tickets'))
  3367. tests.create_tokens(self.session)
  3368. tests.create_tokens_acl(self.session)
  3369. headers = {'Authorization': 'token aaabbbcccddd'}
  3370. # Invalid project
  3371. output = self.app.post(
  3372. '/api/0/foo/issue/1/custom/bugzilla', headers=headers)
  3373. self.assertEqual(output.status_code, 404)
  3374. data = json.loads(output.get_data(as_text=True))
  3375. self.assertDictEqual(
  3376. data,
  3377. {
  3378. "error": "Project not found",
  3379. "error_code": "ENOPROJECT",
  3380. }
  3381. )
  3382. # Valid token, wrong project
  3383. output = self.app.post(
  3384. '/api/0/test2/issue/1/custom/bugzilla', headers=headers)
  3385. self.assertEqual(output.status_code, 401)
  3386. data = json.loads(output.get_data(as_text=True))
  3387. self.assertEqual(sorted(data.keys()), ['error', 'error_code'])
  3388. self.assertEqual(
  3389. pagure.api.APIERROR.EINVALIDTOK.value, data['error'])
  3390. self.assertEqual(
  3391. pagure.api.APIERROR.EINVALIDTOK.name, data['error_code'])
  3392. # No issue
  3393. output = self.app.post(
  3394. '/api/0/test/issue/1/custom/bugzilla', headers=headers)
  3395. self.assertEqual(output.status_code, 404)
  3396. data = json.loads(output.get_data(as_text=True))
  3397. self.assertDictEqual(
  3398. data,
  3399. {
  3400. "error": "Issue not found",
  3401. "error_code": "ENOISSUE",
  3402. }
  3403. )
  3404. # Create normal issue
  3405. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  3406. msg = pagure.lib.query.new_issue(
  3407. session=self.session,
  3408. repo=repo,
  3409. title='Test issue #1',
  3410. content='We should work on this',
  3411. user='pingou',
  3412. private=False,
  3413. )
  3414. self.session.commit()
  3415. self.assertEqual(msg.title, 'Test issue #1')
  3416. # Project does not have this custom field
  3417. output = self.app.post(
  3418. '/api/0/test/issue/1/custom/bugzilla', headers=headers)
  3419. self.assertEqual(output.status_code, 400)
  3420. data = json.loads(output.get_data(as_text=True))
  3421. self.assertDictEqual(
  3422. data,
  3423. {
  3424. "error": "Invalid custom field submitted",
  3425. "error_code": "EINVALIDISSUEFIELD",
  3426. }
  3427. )
  3428. # Check the behavior if the project disabled the issue tracker
  3429. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  3430. settings = repo.settings
  3431. settings['issue_tracker'] = False
  3432. repo.settings = settings
  3433. self.session.add(repo)
  3434. self.session.commit()
  3435. output = self.app.post(
  3436. '/api/0/test/issue/1/custom/bugzilla', headers=headers)
  3437. self.assertEqual(output.status_code, 404)
  3438. data = json.loads(output.get_data(as_text=True))
  3439. self.assertDictEqual(
  3440. data,
  3441. {
  3442. "error": "Issue tracker disabled for this project",
  3443. "error_code": "ETRACKERDISABLED",
  3444. }
  3445. )
  3446. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  3447. settings = repo.settings
  3448. settings['issue_tracker'] = True
  3449. repo.settings = settings
  3450. self.session.add(repo)
  3451. self.session.commit()
  3452. # Invalid API token
  3453. headers = {'Authorization': 'token foobar'}
  3454. output = self.app.post(
  3455. '/api/0/test/issue/1/custom/bugzilla', headers=headers)
  3456. self.assertEqual(output.status_code, 401)
  3457. data = json.loads(output.get_data(as_text=True))
  3458. self.assertEqual(sorted(data.keys()), ['error', 'error_code'])
  3459. self.assertEqual(
  3460. pagure.api.APIERROR.EINVALIDTOK.value, data['error'])
  3461. self.assertEqual(
  3462. pagure.api.APIERROR.EINVALIDTOK.name, data['error_code'])
  3463. headers = {'Authorization': 'token aaabbbcccddd'}
  3464. # Set some custom fields
  3465. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  3466. msg = pagure.lib.query.set_custom_key_fields(
  3467. self.session, repo,
  3468. ['bugzilla', 'upstream', 'reviewstatus', 'duedate'],
  3469. ['link', 'boolean', 'list', 'date'],
  3470. ['', '', 'ack, nack , needs review', '2018-10-10'],
  3471. [None, None, None, None])
  3472. self.session.commit()
  3473. self.assertEqual(msg, 'List of custom fields updated')
  3474. # Check the project custom fields were correctly set
  3475. for key in repo.issue_keys:
  3476. # Check that the bugzilla field correctly had its data removed
  3477. if key.name == "bugzilla":
  3478. self.assertIsNone(key.data)
  3479. # Check that the reviewstatus list field still has its list
  3480. if key.name == "reviewstatus":
  3481. self.assertEqual(
  3482. sorted(key.data), ['ack', 'nack', 'needs review'])
  3483. # Check that the duedate date field still has its date
  3484. if key.name == "duedate":
  3485. self.assertEqual(key.data, '2018-10-10')
  3486. # Check that not setting the value on a non-existing custom field
  3487. # changes nothing
  3488. output = self.app.post(
  3489. '/api/0/test/issue/1/custom/bugzilla', headers=headers)
  3490. self.assertEqual(output.status_code, 200)
  3491. data = json.loads(output.get_data(as_text=True))
  3492. self.assertDictEqual(
  3493. data,
  3494. {
  3495. 'message': 'No changes'
  3496. }
  3497. )
  3498. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  3499. issue = pagure.lib.query.search_issues(self.session, repo, issueid=1)
  3500. self.assertEqual(issue.other_fields, [])
  3501. self.assertEqual(len(issue.other_fields), 0)
  3502. # Invalid value
  3503. output = self.app.post(
  3504. '/api/0/test/issue/1/custom/bugzilla', headers=headers,
  3505. data={'value': 'foobar'})
  3506. self.assertEqual(output.status_code, 400)
  3507. data = json.loads(output.get_data(as_text=True))
  3508. self.assertDictEqual(
  3509. data,
  3510. {
  3511. "error": "Invalid custom field submitted, the value is not "
  3512. "a link",
  3513. "error_code": "EINVALIDISSUEFIELD_LINK",
  3514. }
  3515. )
  3516. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  3517. issue = pagure.lib.query.search_issues(self.session, repo, issueid=1)
  3518. self.assertEqual(issue.other_fields, [])
  3519. self.assertEqual(len(issue.other_fields), 0)
  3520. # All good
  3521. output = self.app.post(
  3522. '/api/0/test/issue/1/custom/bugzilla', headers=headers,
  3523. data={'value': 'https://bugzilla.redhat.com/1234'})
  3524. self.assertEqual(output.status_code, 200)
  3525. data = json.loads(output.get_data(as_text=True))
  3526. self.assertDictEqual(
  3527. data,
  3528. {
  3529. "message": "Custom field bugzilla adjusted to "
  3530. "https://bugzilla.redhat.com/1234"
  3531. }
  3532. )
  3533. self.session.commit()
  3534. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  3535. issue = pagure.lib.query.search_issues(self.session, repo, issueid=1)
  3536. self.assertEqual(len(issue.other_fields), 1)
  3537. self.assertEqual(issue.other_fields[0].key.name, 'bugzilla')
  3538. self.assertEqual(
  3539. issue.other_fields[0].value,
  3540. 'https://bugzilla.redhat.com/1234')
  3541. # Reset the value
  3542. output = self.app.post(
  3543. '/api/0/test/issue/1/custom/bugzilla', headers=headers,
  3544. data={'value': ''})
  3545. self.assertEqual(output.status_code, 200)
  3546. data = json.loads(output.get_data(as_text=True))
  3547. self.assertDictEqual(
  3548. data,
  3549. {
  3550. "message": "Custom field bugzilla reset "
  3551. "(from https://bugzilla.redhat.com/1234)"
  3552. }
  3553. )
  3554. self.session.commit()
  3555. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  3556. issue = pagure.lib.query.search_issues(self.session, repo, issueid=1)
  3557. self.assertEqual(len(issue.other_fields), 0)
  3558. @patch(
  3559. 'pagure.lib.query.set_custom_key_value',
  3560. MagicMock(side_effect=pagure.exceptions.PagureException('error')))
  3561. def test_api_update_custom_field_raises_error(self):
  3562. """ Test the api_update_custom_field method of the flask api. """
  3563. tests.create_projects(self.session)
  3564. tests.create_projects_git(os.path.join(self.path, 'tickets'))
  3565. tests.create_tokens(self.session)
  3566. tests.create_tokens_acl(self.session)
  3567. headers = {'Authorization': 'token aaabbbcccddd'}
  3568. # Create normal issue
  3569. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  3570. msg = pagure.lib.query.new_issue(
  3571. session=self.session,
  3572. repo=repo,
  3573. title='Test issue #1',
  3574. content='We should work on this',
  3575. user='pingou',
  3576. private=False,
  3577. )
  3578. self.session.commit()
  3579. self.assertEqual(msg.title, 'Test issue #1')
  3580. # Set some custom fields
  3581. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  3582. msg = pagure.lib.query.set_custom_key_fields(
  3583. self.session, repo,
  3584. ['bugzilla', 'upstream', 'reviewstatus'],
  3585. ['link', 'boolean', 'list'],
  3586. ['unused data for non-list type', '', 'ack, nack , needs review'],
  3587. [None, None, None])
  3588. self.session.commit()
  3589. self.assertEqual(msg, 'List of custom fields updated')
  3590. # Check the project custom fields were correctly set
  3591. for key in repo.issue_keys:
  3592. # Check that the bugzilla field correctly had its data removed
  3593. if key.name == "bugzilla":
  3594. self.assertIsNone(key.data)
  3595. # Check that the reviewstatus list field still has its list
  3596. elif key.name == "reviewstatus":
  3597. self.assertEqual(
  3598. sorted(key.data), ['ack', 'nack', 'needs review'])
  3599. # Should work but raises an exception
  3600. output = self.app.post(
  3601. '/api/0/test/issue/1/custom/bugzilla', headers=headers,
  3602. data={'value': 'https://bugzilla.redhat.com/1234'})
  3603. self.assertEqual(output.status_code, 400)
  3604. data = json.loads(output.get_data(as_text=True))
  3605. self.assertDictEqual(
  3606. data,
  3607. {u'error': u'error', u'error_code': u'ENOCODE'}
  3608. )
  3609. def test_api_view_issues_history_stats(self):
  3610. """ Test the api_view_issues_history_stats method of the flask api. """
  3611. self.test_api_new_issue()
  3612. # Create private issue
  3613. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  3614. msg = pagure.lib.query.new_issue(
  3615. session=self.session,
  3616. repo=repo,
  3617. title='Test issue',
  3618. content='We should work on this',
  3619. user='pingou',
  3620. private=True,
  3621. status="Closed"
  3622. )
  3623. self.session.commit()
  3624. self.assertEqual(msg.title, 'Test issue')
  3625. output = self.app.get('/api/0/test/issues/history/stats')
  3626. self.assertEqual(output.status_code, 200)
  3627. data = json.loads(output.get_data(as_text=True))
  3628. self.assertEqual(len(data), 1)
  3629. self.assertEqual(len(data['stats']), 53)
  3630. last_key = sorted(data['stats'].keys())[-1]
  3631. self.assertEqual(data['stats'][last_key], 0)
  3632. for k in sorted(data['stats'].keys())[:-1]:
  3633. self.assertEqual(data['stats'][k], 0)
  3634. def test_api_view_user_issues_pingou(self):
  3635. """ Test the api_view_user_issues method of the flask api for pingou.
  3636. """
  3637. self.test_api_new_issue()
  3638. # Create private issue
  3639. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  3640. msg = pagure.lib.query.new_issue(
  3641. session=self.session,
  3642. repo=repo,
  3643. title='Test issue',
  3644. content='We should work on this',
  3645. user='pingou',
  3646. private=True,
  3647. status="Closed"
  3648. )
  3649. self.session.commit()
  3650. self.assertEqual(msg.title, 'Test issue')
  3651. output = self.app.get('/api/0/user/pingou/issues')
  3652. self.assertEqual(output.status_code, 200)
  3653. data = json.loads(output.get_data(as_text=True))
  3654. args = {
  3655. "assignee": True,
  3656. "author": True,
  3657. "milestones": [],
  3658. "no_stones": None,
  3659. "order": None,
  3660. "order_key": None,
  3661. "page": 1,
  3662. "since": None,
  3663. "status": None,
  3664. "tags": []
  3665. }
  3666. self.assertEqual(data['args'], args)
  3667. self.assertEqual(data['issues_assigned'], [])
  3668. self.assertEqual(len(data['issues_created']), 1)
  3669. self.assertEqual(data['total_issues_assigned'], 0)
  3670. self.assertEqual(data['total_issues_created'], 1)
  3671. self.assertEqual(data['total_issues_assigned_pages'], 1)
  3672. self.assertEqual(data['total_issues_created_pages'], 1)
  3673. # Restrict to a certain, fake milestone
  3674. output = self.app.get('/api/0/user/pingou/issues?milestones=v1.0')
  3675. self.assertEqual(output.status_code, 200)
  3676. data = json.loads(output.get_data(as_text=True))
  3677. args = {
  3678. "assignee": True,
  3679. "author": True,
  3680. "milestones": ['v1.0'],
  3681. "no_stones": None,
  3682. "order": None,
  3683. "order_key": None,
  3684. "page": 1,
  3685. "since": None,
  3686. "status": None,
  3687. "tags": []
  3688. }
  3689. self.assertEqual(data['args'], args)
  3690. self.assertEqual(data['issues_assigned'], [])
  3691. self.assertEqual(data['issues_created'], [])
  3692. self.assertEqual(data['total_issues_assigned'], 0)
  3693. self.assertEqual(data['total_issues_created'], 0)
  3694. self.assertEqual(data['total_issues_assigned_pages'], 1)
  3695. self.assertEqual(data['total_issues_created_pages'], 1)
  3696. # Restrict to a certain status
  3697. output = self.app.get('/api/0/user/pingou/issues?status=closed')
  3698. self.assertEqual(output.status_code, 200)
  3699. data = json.loads(output.get_data(as_text=True))
  3700. args = {
  3701. "assignee": True,
  3702. "author": True,
  3703. "milestones": [],
  3704. "no_stones": None,
  3705. "order": None,
  3706. "order_key": None,
  3707. "page": 1,
  3708. "since": None,
  3709. "status": 'closed',
  3710. "tags": []
  3711. }
  3712. self.assertEqual(data['args'], args)
  3713. self.assertEqual(data['issues_assigned'], [])
  3714. self.assertEqual(len(data['issues_created']), 1)
  3715. self.assertEqual(data['total_issues_assigned'], 0)
  3716. self.assertEqual(data['total_issues_created'], 1)
  3717. self.assertEqual(data['total_issues_assigned_pages'], 1)
  3718. self.assertEqual(data['total_issues_created_pages'], 1)
  3719. # Restrict to a certain status
  3720. output = self.app.get('/api/0/user/pingou/issues?status=all')
  3721. self.assertEqual(output.status_code, 200)
  3722. data = json.loads(output.get_data(as_text=True))
  3723. args = {
  3724. "assignee": True,
  3725. "author": True,
  3726. "milestones": [],
  3727. "no_stones": None,
  3728. "order": None,
  3729. "order_key": None,
  3730. "page": 1,
  3731. "since": None,
  3732. "status": 'all',
  3733. "tags": []
  3734. }
  3735. self.assertEqual(data['args'], args)
  3736. self.assertEqual(data['issues_assigned'], [])
  3737. self.assertEqual(len(data['issues_created']), 2)
  3738. self.assertEqual(data['total_issues_assigned'], 0)
  3739. self.assertEqual(data['total_issues_created'], 2)
  3740. self.assertEqual(data['total_issues_assigned_pages'], 1)
  3741. self.assertEqual(data['total_issues_created_pages'], 1)
  3742. def test_api_view_user_issues_foo(self):
  3743. """ Test the api_view_user_issues method of the flask api for foo.
  3744. """
  3745. self.test_api_new_issue()
  3746. # Create private issue
  3747. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  3748. msg = pagure.lib.query.new_issue(
  3749. session=self.session,
  3750. repo=repo,
  3751. title='Test issue',
  3752. content='We should work on this',
  3753. user='pingou',
  3754. private=True,
  3755. status="Closed"
  3756. )
  3757. self.session.commit()
  3758. self.assertEqual(msg.title, 'Test issue')
  3759. output = self.app.get('/api/0/user/foo/issues')
  3760. self.assertEqual(output.status_code, 200)
  3761. data = json.loads(output.get_data(as_text=True))
  3762. args = {
  3763. "assignee": True,
  3764. "author": True,
  3765. "milestones": [],
  3766. "no_stones": None,
  3767. "order": None,
  3768. "order_key": None,
  3769. "page": 1,
  3770. "since": None,
  3771. "status": None,
  3772. "tags": [],
  3773. }
  3774. self.assertEqual(data['args'], args)
  3775. self.assertEqual(len(data['issues_assigned']), 0)
  3776. self.assertEqual(data['issues_created'], [])
  3777. self.assertEqual(data['total_issues_assigned'], 0)
  3778. self.assertEqual(data['total_issues_created'], 0)
  3779. self.assertEqual(data['total_issues_assigned_pages'], 1)
  3780. self.assertEqual(data['total_issues_created_pages'], 1)
  3781. def test_api_view_user_issues_foo_invalid_page(self):
  3782. """ Test the api_view_user_issues method of the flask api for foo.
  3783. """
  3784. self.test_api_new_issue()
  3785. output = self.app.get('/api/0/user/foo/issues?page=0')
  3786. self.assertEqual(output.status_code, 400)
  3787. data = json.loads(output.get_data(as_text=True))
  3788. self.assertEqual(
  3789. data,
  3790. {
  3791. u'error': u'Invalid or incomplete input submitted',
  3792. u'error_code': u'EINVALIDREQ'
  3793. }
  3794. )
  3795. output = self.app.get('/api/0/user/foo/issues?page=abc')
  3796. self.assertEqual(output.status_code, 400)
  3797. data = json.loads(output.get_data(as_text=True))
  3798. self.assertEqual(
  3799. data,
  3800. {
  3801. u'error': u'Invalid or incomplete input submitted',
  3802. u'error_code': u'EINVALIDREQ'
  3803. }
  3804. )
  3805. def test_api_view_user_issues_foo_no_assignee(self):
  3806. """ Test the api_view_user_issues method of the flask api for foo.
  3807. """
  3808. self.test_api_new_issue()
  3809. output = self.app.get('/api/0/user/foo/issues?assignee=0')
  3810. self.assertEqual(output.status_code, 200)
  3811. data = json.loads(output.get_data(as_text=True))
  3812. args = {
  3813. "assignee": False,
  3814. "author": True,
  3815. "milestones": [],
  3816. "no_stones": None,
  3817. "order": None,
  3818. "order_key": None,
  3819. "page": 1,
  3820. "since": None,
  3821. "status": None,
  3822. "tags": [],
  3823. }
  3824. self.assertEqual(data['args'], args)
  3825. self.assertEqual(data['issues_assigned'], [])
  3826. self.assertEqual(data['issues_created'], [])
  3827. self.assertEqual(data['total_issues_assigned'], 0)
  3828. self.assertEqual(data['total_issues_created'], 0)
  3829. self.assertEqual(data['total_issues_assigned_pages'], 1)
  3830. self.assertEqual(data['total_issues_created_pages'], 1)
  3831. def test_api_view_user_issues_pingou_no_author(self):
  3832. """ Test the api_view_user_issues method of the flask api for pingou.
  3833. """
  3834. self.test_api_new_issue()
  3835. output = self.app.get('/api/0/user/pingou/issues?author=0')
  3836. self.assertEqual(output.status_code, 200)
  3837. data = json.loads(output.get_data(as_text=True))
  3838. args = {
  3839. "assignee": True,
  3840. "author": False,
  3841. "milestones": [],
  3842. "no_stones": None,
  3843. "order": None,
  3844. "order_key": None,
  3845. "page": 1,
  3846. "since": None,
  3847. "status": None,
  3848. "tags": []
  3849. }
  3850. self.assertEqual(data['args'], args)
  3851. self.assertEqual(data['issues_assigned'], [])
  3852. self.assertEqual(data['issues_created'], [])
  3853. self.assertEqual(data['total_issues_assigned'], 0)
  3854. self.assertEqual(data['total_issues_created'], 0)
  3855. self.assertEqual(data['total_issues_assigned_pages'], 1)
  3856. self.assertEqual(data['total_issues_created_pages'], 1)
  3857. if __name__ == '__main__':
  3858. SUITE = unittest.TestLoader().loadTestsFromTestCase(
  3859. PagureFlaskApiIssuetests)
  3860. unittest.TextTestRunner(verbosity=2).run(SUITE)