test_pagure_flask_ui_fork.py 317 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967
  1. # -*- coding: utf-8 -*-
  2. """
  3. (c) 2015-2017 - Copyright Red Hat Inc
  4. Authors:
  5. Pierre-Yves Chibon <pingou@pingoured.fr>
  6. """
  7. from __future__ import unicode_literals, absolute_import
  8. import json
  9. import unittest
  10. import shutil
  11. import sys
  12. import tempfile
  13. import time
  14. import os
  15. import re
  16. import pagure_messages
  17. import pygit2
  18. import six
  19. from bs4 import BeautifulSoup
  20. from datetime import datetime, timedelta
  21. from fedora_messaging import api, testing
  22. from mock import ANY, patch, MagicMock
  23. sys.path.insert(
  24. 0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")
  25. )
  26. import pagure.lib.query
  27. import pagure.lib.tasks
  28. import tests
  29. from pagure.lib.repo import PagureRepo
  30. def _get_commits(output):
  31. """Returns the commits message in the output. All commits must have
  32. been made by `Alice Author` or `PY C` to be found.
  33. """
  34. commits = []
  35. save = False
  36. cnt = 0
  37. for row in output.split("\n"):
  38. if row.strip() in ["Alice Author", "Alice Äuthòr", "PY C"]:
  39. save = True
  40. if save:
  41. cnt += 1
  42. if cnt == 7:
  43. commits.append(row.strip())
  44. save = False
  45. cnt = 0
  46. return commits
  47. MERGED_PATTERN = (
  48. re.escape('<span class="text-info font-weight-bold">Merged</span> ')
  49. + "(just now|seconds ago)\n"
  50. + re.escape(
  51. " </span>\n by\n"
  52. ' <span title="PY C (pingou)">pingou.</span>\n'
  53. )
  54. )
  55. def set_up_git_repo(
  56. session,
  57. path,
  58. new_project=None,
  59. branch_from="feature",
  60. mtype="FF",
  61. prid=1,
  62. name_from="test",
  63. ):
  64. """Set up the git repo and create the corresponding PullRequest
  65. object.
  66. """
  67. # Create a git repo to play with
  68. gitrepo = os.path.join(path, "repos", "%s.git" % name_from)
  69. repo = pygit2.init_repository(gitrepo, bare=True)
  70. newpath = tempfile.mkdtemp(prefix="pagure-fork-test")
  71. repopath = os.path.join(newpath, "test")
  72. clone_repo = pygit2.clone_repository(gitrepo, repopath)
  73. # Create a file in that git repo
  74. with open(os.path.join(repopath, "sources"), "w") as stream:
  75. stream.write("foo\n bar")
  76. clone_repo.index.add("sources")
  77. clone_repo.index.write()
  78. try:
  79. com = repo.revparse_single("HEAD")
  80. prev_commit = [com.oid.hex]
  81. except:
  82. prev_commit = []
  83. # Commits the files added
  84. tree = clone_repo.index.write_tree()
  85. author = pygit2.Signature("Alice Author", "alice@authors.tld")
  86. committer = pygit2.Signature("Cecil Committer", "cecil@committers.tld")
  87. clone_repo.create_commit(
  88. "refs/heads/master", # the name of the reference to update
  89. author,
  90. committer,
  91. "Add sources file for testing",
  92. # binary string representing the tree object ID
  93. tree,
  94. # list of binary strings representing parents of the new commit
  95. prev_commit,
  96. )
  97. refname = "refs/heads/master:refs/heads/master"
  98. ori_remote = clone_repo.remotes[0]
  99. PagureRepo.push(ori_remote, refname)
  100. first_commit = repo.revparse_single("HEAD")
  101. def compatible_signature(name, email):
  102. if six.PY2:
  103. name = name.encode("utf-8")
  104. email = email.encode("utf-8")
  105. return pygit2.Signature(name, email)
  106. if mtype == "merge":
  107. with open(os.path.join(repopath, ".gitignore"), "w") as stream:
  108. stream.write("*~")
  109. clone_repo.index.add(".gitignore")
  110. clone_repo.index.write()
  111. # Commits the files added
  112. tree = clone_repo.index.write_tree()
  113. author = compatible_signature("Alice Äuthòr", "alice@äuthòrs.tld")
  114. comitter = compatible_signature(
  115. "Cecil Cõmmîttër", "cecil@cõmmîttërs.tld"
  116. )
  117. clone_repo.create_commit(
  118. "refs/heads/master",
  119. author,
  120. committer,
  121. "Add .gitignore file for testing",
  122. # binary string representing the tree object ID
  123. tree,
  124. # list of binary strings representing parents of the new commit
  125. [first_commit.oid.hex],
  126. )
  127. refname = "refs/heads/master:refs/heads/master"
  128. ori_remote = clone_repo.remotes[0]
  129. PagureRepo.push(ori_remote, refname)
  130. if mtype == "conflicts":
  131. with open(os.path.join(repopath, "sources"), "w") as stream:
  132. stream.write("foo\n bar\nbaz")
  133. clone_repo.index.add("sources")
  134. clone_repo.index.write()
  135. # Commits the files added
  136. tree = clone_repo.index.write_tree()
  137. author = pygit2.Signature("Alice Author", "alice@authors.tld")
  138. committer = pygit2.Signature("Cecil Committer", "cecil@committers.tld")
  139. clone_repo.create_commit(
  140. "refs/heads/master",
  141. author,
  142. committer,
  143. "Add sources conflicting",
  144. # binary string representing the tree object ID
  145. tree,
  146. # list of binary strings representing parents of the new commit
  147. [first_commit.oid.hex],
  148. )
  149. refname = "refs/heads/master:refs/heads/master"
  150. ori_remote = clone_repo.remotes[0]
  151. PagureRepo.push(ori_remote, refname)
  152. # Set the second repo
  153. new_gitrepo = repopath
  154. if new_project:
  155. # Create a new git repo to play with
  156. new_gitrepo = os.path.join(newpath, new_project.fullname)
  157. if not os.path.exists(new_gitrepo):
  158. os.makedirs(new_gitrepo)
  159. new_repo = pygit2.clone_repository(gitrepo, new_gitrepo)
  160. repo = pygit2.Repository(new_gitrepo)
  161. if mtype != "nochanges":
  162. # Edit the sources file again
  163. with open(os.path.join(new_gitrepo, "sources"), "w") as stream:
  164. stream.write("foo\n bar\nbaz\n boose")
  165. repo.index.add("sources")
  166. repo.index.write()
  167. # Commits the files added
  168. tree = repo.index.write_tree()
  169. author = pygit2.Signature("Alice Author", "alice@authors.tld")
  170. committer = pygit2.Signature("Cecil Committer", "cecil@committers.tld")
  171. repo.create_commit(
  172. "refs/heads/%s" % branch_from,
  173. author,
  174. committer,
  175. "A commit on branch %s\n\nMore information" % branch_from,
  176. tree,
  177. [first_commit.oid.hex],
  178. )
  179. refname = "refs/heads/%s" % (branch_from)
  180. ori_remote = repo.remotes[0]
  181. PagureRepo.push(ori_remote, refname)
  182. # Create a PR for these changes
  183. project = pagure.lib.query.get_authorized_project(session, "test")
  184. req = pagure.lib.query.new_pull_request(
  185. session=session,
  186. repo_from=project,
  187. branch_from=branch_from,
  188. repo_to=project,
  189. branch_to="master",
  190. title="PR from the %s branch" % branch_from,
  191. user="pingou",
  192. )
  193. session.commit()
  194. assert req.id == prid
  195. assert req.title == "PR from the %s branch" % branch_from
  196. shutil.rmtree(newpath)
  197. class PagureFlaskForktests(tests.Modeltests):
  198. """ Tests for flask fork controller of pagure """
  199. def test_request_pull_reference(self):
  200. """ Test if there is a reference created for a new PR. """
  201. tests.create_projects(self.session)
  202. tests.create_projects_git(
  203. os.path.join(self.path, "requests"), bare=True
  204. )
  205. set_up_git_repo(
  206. self.session, self.path, new_project=None, branch_from="feature"
  207. )
  208. project = pagure.lib.query.get_authorized_project(self.session, "test")
  209. self.assertEqual(len(project.requests), 1)
  210. # View the pull-request
  211. output = self.app.get("/test/pull-request/1")
  212. self.assertEqual(output.status_code, 200)
  213. gitrepo = os.path.join(self.path, "repos", "test.git")
  214. repo = pygit2.Repository(gitrepo)
  215. self.assertEqual(
  216. list(repo.listall_references()),
  217. ["refs/heads/feature", "refs/heads/master", "refs/pull/1/head"],
  218. )
  219. @patch("pagure.lib.notify.send_email")
  220. def test_request_pull(self, send_email):
  221. """ Test the request_pull endpoint. """
  222. send_email.return_value = True
  223. tests.create_projects(self.session)
  224. tests.create_projects_git(
  225. os.path.join(self.path, "requests"), bare=True
  226. )
  227. # Non-existant project
  228. output = self.app.get("/foobar/pull-request/1")
  229. self.assertEqual(output.status_code, 404)
  230. # Project has no PR
  231. output = self.app.get("/test/pull-request/1")
  232. self.assertEqual(output.status_code, 404)
  233. set_up_git_repo(
  234. self.session, self.path, new_project=None, branch_from="feature"
  235. )
  236. project = pagure.lib.query.get_authorized_project(self.session, "test")
  237. self.assertEqual(len(project.requests), 1)
  238. # View the pull-request
  239. output = self.app.get("/test/pull-request/1")
  240. self.assertEqual(output.status_code, 200)
  241. output_text = output.get_data(as_text=True)
  242. # self.assertIn(
  243. #'<h3><span class="label label-default">PR#1</span>\n'
  244. #' PR from the feature branch\n</h3>',
  245. # output_text)
  246. self.assertIn(
  247. 'title="View file as of 2a552bb">sources</a>', output_text
  248. )
  249. # Test if the `open changed file icon` is displayed.
  250. self.assertIn(
  251. 'class="open_changed_file_icon_wrap"><span '
  252. 'class="fa fa-file-code-o fa-fw" '
  253. 'alt="Open changed file" title="Open changed file"></span>'
  254. "</a>",
  255. output_text,
  256. )
  257. self.assertIn(
  258. '<span class="btn btn-success btn-sm font-weight-bold disabled'
  259. ' opacity-100">+3</span>',
  260. output_text,
  261. )
  262. self.assertIn(
  263. '<span class="btn btn-danger btn-sm font-weight-bold disabled '
  264. 'opacity-100">-1</span>',
  265. output_text,
  266. )
  267. # Test if hunk headline is rendered without line numbers
  268. self.assertIn(
  269. '<td class="cell1"></td><td class="prc border-right"></td>\n<td '
  270. 'class="cell2 stretch-table-column"> <pre class='
  271. '"text-muted"><code>@@ -1,2 +1,4 @@',
  272. output_text,
  273. )
  274. # Tests if line number 1 is displayed
  275. self.assertNotIn(
  276. '<td class="cell1"><a id="_1__1" href="#_1__1" data-line-number="1" data-file-number="1"></a></td>',
  277. output_text,
  278. )
  279. # Test if line number 2 is displayed
  280. self.assertIn(
  281. '<td class="cell1"><a id="_1__2" href="#_1__2" data-line-number="2" data-file-number="1"></a></td>',
  282. output_text,
  283. )
  284. @patch("pagure.lib.notify.send_email")
  285. def test_request_pull_delete_branch_button_no_auth(self, send_email):
  286. """ Test the request_pull endpoint. """
  287. send_email.return_value = True
  288. tests.create_projects(self.session)
  289. tests.create_projects_git(
  290. os.path.join(self.path, "requests"), bare=True
  291. )
  292. set_up_git_repo(
  293. self.session, self.path, new_project=None, branch_from="feature"
  294. )
  295. project = pagure.lib.query.get_authorized_project(self.session, "test")
  296. self.assertEqual(len(project.requests), 1)
  297. # View the pull-request
  298. output = self.app.get("/test/pull-request/1")
  299. self.assertEqual(output.status_code, 200)
  300. output_text = output.get_data(as_text=True)
  301. self.assertIn(
  302. "<title>PR#1: PR from the feature branch - test\n - "
  303. "Pagure</title>",
  304. output_text,
  305. )
  306. self.assertIn(
  307. 'title="View file as of 2a552bb">sources</a>', output_text
  308. )
  309. # Un-authenticated user cannot see this checkbox
  310. self.assertNotIn(
  311. '<input id="delete_branch" name="delete_branch" type="checkbox" '
  312. 'value="y"> <label for="delete_branch">Delete branch after '
  313. "merging</label>",
  314. output_text,
  315. )
  316. @patch("pagure.lib.notify.send_email")
  317. def test_request_pull_delete_branch_button(self, send_email):
  318. """ Test the request_pull endpoint. """
  319. send_email.return_value = True
  320. tests.create_projects(self.session)
  321. tests.create_projects_git(
  322. os.path.join(self.path, "requests"), bare=True
  323. )
  324. set_up_git_repo(
  325. self.session, self.path, new_project=None, branch_from="feature"
  326. )
  327. project = pagure.lib.query.get_authorized_project(self.session, "test")
  328. self.assertEqual(len(project.requests), 1)
  329. # View the pull-request
  330. user = tests.FakeUser()
  331. user.username = "pingou"
  332. with tests.user_set(self.app.application, user):
  333. output = self.app.get("/test/pull-request/1")
  334. self.assertEqual(output.status_code, 200)
  335. output_text = output.get_data(as_text=True)
  336. self.assertIn(
  337. "<title>PR#1: PR from the feature branch - test\n - "
  338. "Pagure</title>",
  339. output_text,
  340. )
  341. self.assertIn(
  342. 'title="View file as of 2a552bb">sources</a>', output_text
  343. )
  344. self.assertIn(
  345. '<input id="delete_branch" name="delete_branch" type="checkbox" '
  346. 'value="y"> <label for="delete_branch">Delete branch after '
  347. "merging</label>",
  348. output_text,
  349. )
  350. @patch("pagure.lib.notify.send_email")
  351. def test_request_pull_delete_branch_button_no_project_from(
  352. self, send_email
  353. ):
  354. """ Test the request_pull endpoint. """
  355. send_email.return_value = True
  356. tests.create_projects(self.session)
  357. tests.create_projects_git(
  358. os.path.join(self.path, "requests"), bare=True
  359. )
  360. set_up_git_repo(
  361. self.session, self.path, new_project=None, branch_from="feature"
  362. )
  363. project = pagure.lib.query.get_authorized_project(self.session, "test")
  364. self.assertEqual(len(project.requests), 1)
  365. project.requests[0].project_from = None
  366. self.session.add(project.requests[0])
  367. self.session.commit()
  368. # View the pull-request
  369. user = tests.FakeUser()
  370. user.username = "pingou"
  371. with tests.user_set(self.app.application, user):
  372. output = self.app.get("/test/pull-request/1")
  373. self.assertEqual(output.status_code, 200)
  374. output_text = output.get_data(as_text=True)
  375. self.assertIn(
  376. "<title>PR#1: PR from the feature branch - test\n - "
  377. "Pagure</title>",
  378. output_text,
  379. )
  380. self.assertIn(
  381. 'title="View file as of 2a552bb">sources</a>', output_text
  382. )
  383. self.assertIn(
  384. '<input id="delete_branch" name="delete_branch" type="checkbox" '
  385. 'value="y"> <label for="delete_branch">Delete branch after '
  386. "merging</label>",
  387. output_text,
  388. )
  389. @patch("pagure.lib.notify.send_email")
  390. def test_request_pull_delete_branch_button_no_project_from_no_acl(
  391. self, send_email
  392. ):
  393. """ Test the request_pull endpoint. """
  394. send_email.return_value = True
  395. tests.create_projects(self.session)
  396. tests.create_projects_git(
  397. os.path.join(self.path, "requests"), bare=True
  398. )
  399. set_up_git_repo(
  400. self.session, self.path, new_project=None, branch_from="feature"
  401. )
  402. project = pagure.lib.query.get_authorized_project(self.session, "test")
  403. self.assertEqual(len(project.requests), 1)
  404. project.requests[0].project_from = None
  405. self.session.add(project.requests[0])
  406. self.session.commit()
  407. # View the pull-request
  408. user = tests.FakeUser()
  409. user.username = "foo"
  410. with tests.user_set(self.app.application, user):
  411. output = self.app.get("/test/pull-request/1")
  412. self.assertEqual(output.status_code, 200)
  413. output_text = output.get_data(as_text=True)
  414. self.assertIn(
  415. "<title>PR#1: PR from the feature branch - test\n - "
  416. "Pagure</title>",
  417. output_text,
  418. )
  419. self.assertIn(
  420. 'title="View file as of 2a552bb">sources</a>', output_text
  421. )
  422. self.assertNotIn(
  423. '<input id="delete_branch" name="delete_branch" type="checkbox" '
  424. 'value="y"> <label for="delete_branch">Delete branch after '
  425. "merging</label>",
  426. output_text,
  427. )
  428. @patch("pagure.lib.notify.send_email")
  429. def test_task_update_request_pull(self, send_email):
  430. """ Test the task update_pull_request endpoint. """
  431. send_email.return_value = True
  432. tests.create_projects(self.session)
  433. tests.create_projects_git(
  434. os.path.join(self.path, "requests"), bare=True
  435. )
  436. set_up_git_repo(
  437. self.session, self.path, new_project=None, branch_from="feature"
  438. )
  439. self.session = pagure.lib.query.create_session(self.dbpath)
  440. project = pagure.lib.query.get_authorized_project(self.session, "test")
  441. self.assertEqual(len(project.requests), 1)
  442. request = project.requests[0]
  443. self.assertEqual(len(request.comments), 0)
  444. start_commit = request.commit_start
  445. stop_commit = request.commit_stop
  446. # View the pull-request
  447. output = self.app.get("/test/pull-request/1")
  448. self.assertEqual(output.status_code, 200)
  449. output_text = output.get_data(as_text=True)
  450. self.assertIn(
  451. "<title>PR#1: PR from the feature branch - test\n - Pagure</title>",
  452. output_text,
  453. )
  454. self.assertIn(
  455. 'title="View file as of 2a552bb">sources</a>', output_text
  456. )
  457. # Add a new commit on the repo from
  458. newpath = tempfile.mkdtemp(prefix="pagure-fork-test")
  459. gitrepo = os.path.join(self.path, "repos", "test.git")
  460. repopath = os.path.join(newpath, "test")
  461. clone_repo = pygit2.clone_repository(
  462. gitrepo, repopath, checkout_branch="feature"
  463. )
  464. def compatible_signature(name, email):
  465. if six.PY2:
  466. name = name.encode("utf-8")
  467. email = email.encode("utf-8")
  468. return pygit2.Signature(name, email)
  469. with open(os.path.join(repopath, ".gitignore"), "w") as stream:
  470. stream.write("*~")
  471. clone_repo.index.add(".gitignore")
  472. clone_repo.index.write()
  473. com = clone_repo.revparse_single("HEAD")
  474. prev_commit = [com.oid.hex]
  475. # Commits the files added
  476. tree = clone_repo.index.write_tree()
  477. author = compatible_signature("Alice Äuthòr", "alice@äuthòrs.tld")
  478. comitter = compatible_signature(
  479. "Cecil Cõmmîttër", "cecil@cõmmîttërs.tld"
  480. )
  481. clone_repo.create_commit(
  482. "refs/heads/feature",
  483. author,
  484. comitter,
  485. "Add .gitignore file for testing",
  486. # binary string representing the tree object ID
  487. tree,
  488. # list of binary strings representing parents of the new commit
  489. prev_commit,
  490. )
  491. refname = "refs/heads/feature:refs/heads/feature"
  492. ori_remote = clone_repo.remotes[0]
  493. PagureRepo.push(ori_remote, refname)
  494. shutil.rmtree(newpath)
  495. pagure.lib.tasks.update_pull_request(request.uid)
  496. self.session = pagure.lib.query.create_session(self.dbpath)
  497. project = pagure.lib.query.get_authorized_project(self.session, "test")
  498. self.assertEqual(len(project.requests), 1)
  499. request = project.requests[0]
  500. self.assertEqual(len(request.comments), 1)
  501. self.assertIsNotNone(request.commit_start)
  502. self.assertIsNotNone(request.commit_stop)
  503. self.assertNotEqual(start_commit, request.commit_start)
  504. self.assertNotEqual(stop_commit, request.commit_stop)
  505. @patch("pagure.lib.notify.send_email")
  506. def test_request_pull_ci_dropdown(self, send_email):
  507. """ Test presence of the "Rerun CI" dropdown with various settings. """
  508. send_email.return_value = True
  509. tests.create_projects(self.session)
  510. tests.create_projects_git(
  511. os.path.join(self.path, "requests"), bare=True
  512. )
  513. set_up_git_repo(
  514. self.session, self.path, new_project=None, branch_from="feature"
  515. )
  516. user = tests.FakeUser()
  517. user.username = "pingou"
  518. with tests.user_set(self.app.application, user):
  519. # old-style TRIGGER_CI list - test backwards compatibility
  520. with patch.dict(
  521. "pagure.config.config",
  522. {"TRIGGER_CI": ["old-style-trigger-ci"]},
  523. ):
  524. output = self.app.get("/test/pull-request/1")
  525. self.assertEqual(output.status_code, 200)
  526. output_text = output.get_data(as_text=True)
  527. self.assertNotIn("Rerun CI", output_text)
  528. # new-style TRIGGER_CI, but no button to show
  529. with patch.dict(
  530. "pagure.config.config", {"TRIGGER_CI": {"no-button": None}}
  531. ):
  532. output = self.app.get("/test/pull-request/1")
  533. self.assertEqual(output.status_code, 200)
  534. output_text = output.get_data(as_text=True)
  535. self.assertNotIn("Rerun CI", output_text)
  536. trigger_ci = {
  537. "foobar-ci": {
  538. "name": "foobar-ci-name",
  539. "description": "barfoo",
  540. },
  541. "spam-ci": {
  542. "name": "spam-ci-name",
  543. "description": "with beans and eggs",
  544. },
  545. "no-button-for-me-ci": None,
  546. }
  547. # new-style TRIGGER_CI, several buttons to show
  548. with patch.dict(
  549. "pagure.config.config", {"TRIGGER_CI": trigger_ci}
  550. ):
  551. output = self.app.get("/test/pull-request/1")
  552. self.assertEqual(output.status_code, 200)
  553. output_text = output.get_data(as_text=True)
  554. self.assertIn("Rerun CI", output_text)
  555. self.assertIn("foobar-ci-name", output_text)
  556. self.assertIn("spam-ci-name", output_text)
  557. self.assertNotIn("no-button-for-me-ci", output_text)
  558. trigger_ci = {
  559. "foobar-ci": {
  560. "name": "foobar-ci-name",
  561. "description": "barfoo",
  562. "requires_project_hook_attr": (
  563. "ci_hook",
  564. "active_pr",
  565. True,
  566. ),
  567. }
  568. }
  569. # new-style TRIGGER_CI with requires_project_hook_attr that is
  570. # not fulfilled by the project
  571. with patch.dict(
  572. "pagure.config.config", {"TRIGGER_CI": trigger_ci}
  573. ):
  574. output = self.app.get("/test/pull-request/1")
  575. self.assertEqual(output.status_code, 200)
  576. output_text = output.get_data(as_text=True)
  577. self.assertNotIn("Rerun CI", output_text)
  578. # now activate the hook and try again
  579. data = {
  580. "active_pr": "y",
  581. "ci_url": "https://jenkins.fedoraproject.org",
  582. "ci_job": "ci_job",
  583. "ci_type": "jenkins",
  584. "csrf_token": self.get_csrf(),
  585. }
  586. output = self.app.post(
  587. "/test/settings/Pagure CI", data=data, follow_redirects=True
  588. )
  589. self.assertEqual(output.status_code, 200)
  590. with patch.dict(
  591. "pagure.config.config", {"TRIGGER_CI": trigger_ci}
  592. ):
  593. output = self.app.get("/test/pull-request/1")
  594. self.assertEqual(output.status_code, 200)
  595. output_text = output.get_data(as_text=True)
  596. self.assertIn("Rerun CI", output_text)
  597. self.assertIn("foobar-ci-name", output_text)
  598. # shouldn't show up if user is not logged in
  599. with patch.dict("pagure.config.config", {"TRIGGER_CI": trigger_ci}):
  600. output = self.app.get("/test/pull-request/1")
  601. self.assertEqual(output.status_code, 200)
  602. output_text = output.get_data(as_text=True)
  603. self.assertNotIn("Rerun CI", output_text)
  604. @patch("pagure.lib.notify.send_email")
  605. @patch.dict(
  606. "pagure.config.config",
  607. {"TRIGGER_CI": {"CI1": {"name": "CI1", "description": "CI1!"}}},
  608. )
  609. def test_request_pull_ci_rerun(self, send_email):
  610. """ Test rerunning CI using button from the "Rerun CI" dropdown. """
  611. send_email.return_value = True
  612. tests.create_projects(self.session)
  613. tests.create_projects_git(
  614. os.path.join(self.path, "requests"), bare=True
  615. )
  616. set_up_git_repo(
  617. self.session, self.path, new_project=None, branch_from="feature"
  618. )
  619. user = tests.FakeUser()
  620. user.username = "pingou"
  621. project = pagure.lib.query.get_authorized_project(self.session, "test")
  622. request = project.requests[0]
  623. with tests.user_set(self.app.application, user):
  624. # no csrf token
  625. output = self.app.get("/test/pull-request/1")
  626. self.assertEqual(output.status_code, 200)
  627. output = self.app.post(
  628. "/test/pull-request/1/trigger-ci", follow_redirects=True
  629. )
  630. self.assertEqual(output.status_code, 200)
  631. self.assertIn("Invalid input", output.get_data(as_text=True))
  632. # no such PR
  633. output = self.app.get("/test/pull-request/1")
  634. self.assertEqual(output.status_code, 200)
  635. output = self.app.post(
  636. "/test/pull-request/2/trigger-ci", follow_redirects=True
  637. )
  638. self.assertEqual(output.status_code, 404)
  639. # wrong comment
  640. output = self.app.get("/test/pull-request/1")
  641. self.assertEqual(output.status_code, 200)
  642. csrf_token = self.get_csrf(output=output)
  643. data = {"csrf_token": csrf_token, "comment": "this doesnt exist"}
  644. output = self.app.post(
  645. "/test/pull-request/1/trigger-ci",
  646. data=data,
  647. follow_redirects=True,
  648. )
  649. self.assertEqual(output.status_code, 200)
  650. self.assertIn("Invalid input", output.get_data(as_text=True))
  651. # everything ok
  652. output = self.app.get("/test/pull-request/1")
  653. self.assertEqual(output.status_code, 200)
  654. csrf_token = self.get_csrf(output=output)
  655. data = {"csrf_token": csrf_token, "comment": "CI1"}
  656. output = self.app.post(
  657. "/test/pull-request/1/trigger-ci",
  658. data=data,
  659. follow_redirects=True,
  660. )
  661. output_text = output.get_data(as_text=True)
  662. self.assertEqual(output.status_code, 200)
  663. self.assertIn("<p>CI1</p>", output_text)
  664. comment = request.comments[0]
  665. self.assertTrue(comment.notification)
  666. self.assertEqual(comment.comment, "CI1")
  667. @patch("pagure.lib.notify.send_email")
  668. def test_merge_request_pull_FF(self, send_email):
  669. """ Test the merge_request_pull endpoint with a FF PR. """
  670. send_email.return_value = True
  671. self.test_request_pull()
  672. user = tests.FakeUser()
  673. with tests.user_set(self.app.application, user):
  674. output = self.app.get("/test/pull-request/1")
  675. self.assertEqual(output.status_code, 200)
  676. csrf_token = self.get_csrf(output=output)
  677. # No CSRF
  678. output = self.app.post(
  679. "/test/pull-request/1/merge", data={}, follow_redirects=True
  680. )
  681. self.assertEqual(output.status_code, 200)
  682. output_text = output.get_data(as_text=True)
  683. self.assertIn(
  684. "<title>PR#1: PR from the feature branch - test\n - "
  685. "Pagure</title>",
  686. output_text,
  687. )
  688. # self.assertIn(
  689. #'<h3><span class="label label-default">PR#1</span>\n'
  690. #' PR from the feature branch\n</h3>',
  691. # output_text)
  692. self.assertIn(
  693. 'title="View file as of 2a552bb">sources</a>', output_text
  694. )
  695. # Wrong project
  696. data = {"csrf_token": csrf_token}
  697. output = self.app.post(
  698. "/foobar/pull-request/100/merge",
  699. data=data,
  700. follow_redirects=True,
  701. )
  702. self.assertEqual(output.status_code, 404)
  703. # Wrong project
  704. data = {"csrf_token": csrf_token}
  705. output = self.app.post(
  706. "/test/pull-request/1/merge", data=data, follow_redirects=True
  707. )
  708. self.assertEqual(output.status_code, 403)
  709. user.username = "pingou"
  710. with tests.user_set(self.app.application, user):
  711. # Wrong request id
  712. data = {"csrf_token": csrf_token}
  713. output = self.app.post(
  714. "/test/pull-request/100/merge",
  715. data=data,
  716. follow_redirects=True,
  717. )
  718. self.assertEqual(output.status_code, 404)
  719. # Project w/o pull-request
  720. self.session.commit()
  721. repo = pagure.lib.query.get_authorized_project(
  722. self.session, "test"
  723. )
  724. settings = repo.settings
  725. settings["pull_requests"] = False
  726. repo.settings = settings
  727. self.session.add(repo)
  728. self.session.commit()
  729. # Pull-request disabled
  730. output = self.app.post(
  731. "/test/pull-request/1/merge", data=data, follow_redirects=True
  732. )
  733. self.assertEqual(output.status_code, 404)
  734. # Project w pull-request but only assignee can merge
  735. self.session.commit()
  736. repo = pagure.lib.query.get_authorized_project(
  737. self.session, "test"
  738. )
  739. settings["pull_requests"] = True
  740. settings["Only_assignee_can_merge_pull-request"] = True
  741. repo.settings = settings
  742. self.session.add(repo)
  743. self.session.commit()
  744. output = self.app.post(
  745. "/test/pull-request/1/merge", data=data, follow_redirects=True
  746. )
  747. self.assertEqual(output.status_code, 200)
  748. output_text = output.get_data(as_text=True)
  749. self.assertIn(
  750. "<title>PR#1: PR from the feature branch - test\n - "
  751. "Pagure</title>",
  752. output_text,
  753. )
  754. self.assertIn(
  755. '<h4 class="ml-1">\n <div>\n '
  756. '<span class="fa fa-fw text-success fa-arrow-circle-down pt-1"></span>\n '
  757. '<span class="text-success '
  758. 'font-weight-bold">#1</span>\n '
  759. '<span class="font-weight-bold">\n '
  760. "PR from the feature branch\n",
  761. output_text,
  762. )
  763. self.assertIn(
  764. "This request must be " "assigned to be merged", output_text
  765. )
  766. # PR assigned but not to this user
  767. self.session.commit()
  768. repo = pagure.lib.query.get_authorized_project(
  769. self.session, "test"
  770. )
  771. req = repo.requests[0]
  772. req.assignee_id = 2
  773. self.session.add(req)
  774. self.session.commit()
  775. output = self.app.post(
  776. "/test/pull-request/1/merge", data=data, follow_redirects=True
  777. )
  778. self.assertEqual(output.status_code, 200)
  779. output_text = output.get_data(as_text=True)
  780. self.assertIn(
  781. '<h4 class="ml-1">\n <div>\n '
  782. '<span class="fa fa-fw text-success fa-arrow-circle-down pt-1"></span>\n '
  783. '<span class="text-success '
  784. 'font-weight-bold">#1</span>\n '
  785. '<span class="font-weight-bold">\n '
  786. "PR from the feature branch\n",
  787. output_text,
  788. )
  789. self.assertIn(
  790. "Only the assignee can merge this request", output_text
  791. )
  792. # Project w/ minimal PR score
  793. self.session.commit()
  794. repo = pagure.lib.query.get_authorized_project(
  795. self.session, "test"
  796. )
  797. settings["Only_assignee_can_merge_pull-request"] = False
  798. settings["Minimum_score_to_merge_pull-request"] = 2
  799. repo.settings = settings
  800. self.session.add(repo)
  801. self.session.commit()
  802. output = self.app.post(
  803. "/test/pull-request/1/merge", data=data, follow_redirects=True
  804. )
  805. self.assertEqual(output.status_code, 200)
  806. output_text = output.get_data(as_text=True)
  807. self.assertIn(
  808. '<h4 class="ml-1">\n <div>\n '
  809. '<span class="fa fa-fw text-success fa-arrow-circle-down pt-1"></span>\n '
  810. '<span class="text-success '
  811. 'font-weight-bold">#1</span>\n '
  812. '<span class="font-weight-bold">\n '
  813. "PR from the feature branch\n",
  814. output_text,
  815. )
  816. self.assertIn(
  817. "This request does not "
  818. "have the minimum review score necessary to be merged",
  819. output_text,
  820. )
  821. # Merge
  822. self.session.commit()
  823. repo = pagure.lib.query.get_authorized_project(
  824. self.session, "test"
  825. )
  826. settings["Minimum_score_to_merge_pull-request"] = -1
  827. repo.settings = settings
  828. self.session.add(repo)
  829. self.session.commit()
  830. output = self.app.post(
  831. "/test/pull-request/1/merge", data=data, follow_redirects=True
  832. )
  833. self.assertEqual(output.status_code, 200)
  834. output = self.app.get("/test/commits")
  835. output_text = output.get_data(as_text=True)
  836. self.assertIn(
  837. "<title>Commits - test - Pagure</title>", output_text
  838. )
  839. self.assertIn("A commit on branch feature", output_text)
  840. self.assertNotIn(
  841. "Merge #1 `PR from the feature branch`", output_text
  842. )
  843. # Check if the closing notification was added
  844. output = self.app.get("/test/pull-request/1")
  845. self.assertIsNotNone(
  846. re.search(MERGED_PATTERN, output.get_data(as_text=True))
  847. )
  848. @patch("pagure.lib.notify.send_email")
  849. def test_merge_request_pull_merge(self, send_email):
  850. """ Test the merge_request_pull endpoint with a merge PR. """
  851. send_email.return_value = True
  852. tests.create_projects(self.session)
  853. tests.create_projects_git(
  854. os.path.join(self.path, "requests"), bare=True
  855. )
  856. set_up_git_repo(
  857. self.session,
  858. self.path,
  859. new_project=None,
  860. branch_from="feature",
  861. mtype="merge",
  862. )
  863. user = tests.FakeUser()
  864. user.username = "pingou"
  865. with tests.user_set(self.app.application, user):
  866. output = self.app.get("/test/pull-request/1")
  867. self.assertEqual(output.status_code, 200)
  868. csrf_token = self.get_csrf(output=output)
  869. data = {"csrf_token": csrf_token}
  870. # Merge
  871. output = self.app.post(
  872. "/test/pull-request/1/merge", data=data, follow_redirects=True
  873. )
  874. self.assertEqual(output.status_code, 200)
  875. self.assertIn(
  876. "<title>PR#1: PR from the feature branch - test\n - Pagure</title>",
  877. output.get_data(as_text=True),
  878. )
  879. # Check if the closing notification was added
  880. output = self.app.get("/test/pull-request/1")
  881. self.assertIsNotNone(
  882. re.search(MERGED_PATTERN, output.get_data(as_text=True))
  883. )
  884. @patch("pagure.lib.notify.send_email")
  885. def test_merge_request_pull_merge_with_comment(self, send_email):
  886. """ Test the merge_request_pull endpoint with a merge PR. """
  887. send_email.return_value = True
  888. tests.create_projects(self.session)
  889. tests.create_projects_git(
  890. os.path.join(self.path, "requests"), bare=True
  891. )
  892. set_up_git_repo(
  893. self.session,
  894. self.path,
  895. new_project=None,
  896. branch_from="feature",
  897. mtype="merge",
  898. )
  899. self.session = pagure.lib.query.create_session(self.dbpath)
  900. request = pagure.lib.query.search_pull_requests(
  901. self.session, project_id=1, requestid=1
  902. )
  903. self.assertEqual(len(request.comments), 0)
  904. user = tests.FakeUser()
  905. user.username = "pingou"
  906. with tests.user_set(self.app.application, user):
  907. output = self.app.get("/test/pull-request/1")
  908. self.assertEqual(output.status_code, 200)
  909. csrf_token = self.get_csrf(output=output)
  910. data = {
  911. "csrf_token": csrf_token,
  912. "comment": "Thanks for the review and the suggestions!",
  913. }
  914. # Merge
  915. output = self.app.post(
  916. "/test/pull-request/1/merge", data=data, follow_redirects=True
  917. )
  918. self.assertEqual(output.status_code, 200)
  919. self.assertIn(
  920. "<title>PR#1: PR from the feature branch - test\n - Pagure</title>",
  921. output.get_data(as_text=True),
  922. )
  923. # Check if the closing notification was added
  924. output = self.app.get("/test/pull-request/1")
  925. output_text = output.get_data(as_text=True)
  926. self.assertIsNotNone(re.search(MERGED_PATTERN, output_text))
  927. self.assertIn(
  928. "Thanks for the review and the suggestions!", output_text
  929. )
  930. self.session = pagure.lib.query.create_session(self.dbpath)
  931. request = pagure.lib.query.search_pull_requests(
  932. self.session, project_id=1, requestid=1
  933. )
  934. self.assertEqual(len(request.comments), 2)
  935. @patch("pagure.lib.notify.send_email")
  936. def test_merge_request_pull_merge_with_delete_branch(self, send_email):
  937. """ Test the merge_request_pull endpoint with a merge PR and delete source branch. """
  938. send_email.return_value = True
  939. tests.create_projects(self.session)
  940. tests.create_projects_git(
  941. os.path.join(self.path, "requests"), bare=True
  942. )
  943. set_up_git_repo(
  944. self.session,
  945. self.path,
  946. new_project=None,
  947. branch_from="feature-branch",
  948. mtype="merge",
  949. )
  950. user = tests.FakeUser()
  951. user.username = "pingou"
  952. with tests.user_set(self.app.application, user):
  953. output = self.app.get("/test/pull-request/1")
  954. self.assertEqual(output.status_code, 200)
  955. data = {
  956. "csrf_token": self.get_csrf(output=output),
  957. "delete_branch": True,
  958. }
  959. # Merge
  960. output = self.app.post(
  961. "/test/pull-request/1/merge", data=data, follow_redirects=True
  962. )
  963. self.assertEqual(output.status_code, 200)
  964. output_text = output.get_data(as_text=True)
  965. self.assertIn(
  966. "<title>PR#1: PR from the feature-branch branch - test\n - Pagure</title>",
  967. output_text,
  968. )
  969. # Check the branch is not mentioned
  970. self.assertNotIn(
  971. '<a class="" href="/test/branch/feature-branch"', output_text
  972. )
  973. @patch("pagure.lib.notify.send_email")
  974. def test_merge_request_pull_conflicts(self, send_email):
  975. """ Test the merge_request_pull endpoint with a conflicting PR. """
  976. send_email.return_value = True
  977. tests.create_projects(self.session)
  978. tests.create_projects_git(
  979. os.path.join(self.path, "requests"), bare=True
  980. )
  981. set_up_git_repo(
  982. self.session,
  983. self.path,
  984. new_project=None,
  985. branch_from="feature",
  986. mtype="conflicts",
  987. )
  988. user = tests.FakeUser()
  989. user.username = "pingou"
  990. with tests.user_set(self.app.application, user):
  991. output = self.app.get("/test/pull-request/1")
  992. self.assertEqual(output.status_code, 200)
  993. csrf_token = self.get_csrf(output=output)
  994. data = {"csrf_token": csrf_token}
  995. # Merge conflicts
  996. output = self.app.post(
  997. "/test/pull-request/1/merge", data=data, follow_redirects=True
  998. )
  999. self.assertEqual(output.status_code, 200)
  1000. output_text = output.get_data(as_text=True)
  1001. self.assertIn(
  1002. '<h4 class="ml-1">\n <div>\n '
  1003. '<span class="fa fa-fw text-success fa-arrow-circle-down pt-1"></span>\n '
  1004. '<span class="text-success '
  1005. 'font-weight-bold">#1</span>\n '
  1006. '<span class="font-weight-bold">\n '
  1007. "PR from the feature branch\n",
  1008. output_text,
  1009. )
  1010. self.assertIn("Merge conflicts!", output_text)
  1011. @patch("pagure.lib.notify.send_email")
  1012. def test_merge_request_pull_conflicts_with_delete_branch(self, send_email):
  1013. """ Test the merge_request_pull endpoint with a conflicting PR and request deletion of branch. """
  1014. send_email.return_value = True
  1015. tests.create_projects(self.session)
  1016. tests.create_projects_git(
  1017. os.path.join(self.path, "requests"), bare=True
  1018. )
  1019. set_up_git_repo(
  1020. self.session,
  1021. self.path,
  1022. new_project=None,
  1023. branch_from="feature-branch",
  1024. mtype="conflicts",
  1025. )
  1026. user = tests.FakeUser()
  1027. user.username = "pingou"
  1028. with tests.user_set(self.app.application, user):
  1029. output = self.app.get("/test/pull-request/1")
  1030. self.assertEqual(output.status_code, 200)
  1031. data = {
  1032. "csrf_token": self.get_csrf(output=output),
  1033. "delete_branch": True,
  1034. }
  1035. # Merge conflicts
  1036. output = self.app.post(
  1037. "/test/pull-request/1/merge", data=data, follow_redirects=True
  1038. )
  1039. self.assertEqual(output.status_code, 200)
  1040. output_text = output.get_data(as_text=True)
  1041. self.assertIn(
  1042. '<span class="fa fa-fw text-success fa-arrow-circle-down pt-1"></span>\n'
  1043. ' <span class="text-success font-weight-bold">#1</span>\n'
  1044. ' <span class="font-weight-bold">\n'
  1045. " PR from the feature-branch branch\n",
  1046. output_text,
  1047. )
  1048. self.assertIn("Merge conflicts!", output_text)
  1049. # Check the branch still exists
  1050. output = self.app.get("/test/branches")
  1051. self.assertIn("feature-branch", output.get_data(as_text=True))
  1052. @patch("pagure.lib.notify.send_email")
  1053. def test_merge_request_pull_nochange(self, send_email):
  1054. """ Test the merge_request_pull endpoint. """
  1055. send_email.return_value = True
  1056. tests.create_projects(self.session)
  1057. tests.create_projects_git(
  1058. os.path.join(self.path, "requests"), bare=True
  1059. )
  1060. set_up_git_repo(
  1061. self.session,
  1062. self.path,
  1063. new_project=None,
  1064. branch_from="master",
  1065. mtype="nochanges",
  1066. )
  1067. user = tests.FakeUser()
  1068. user.username = "pingou"
  1069. with tests.user_set(self.app.application, user):
  1070. output = self.app.get("/test/pull-request/1")
  1071. self.assertEqual(output.status_code, 200)
  1072. csrf_token = self.get_csrf(output=output)
  1073. data = {"csrf_token": csrf_token}
  1074. # Nothing to merge
  1075. output = self.app.post(
  1076. "/test/pull-request/1/merge", data=data, follow_redirects=True
  1077. )
  1078. self.assertEqual(output.status_code, 200)
  1079. output_text = output.get_data(as_text=True)
  1080. self.assertIn(
  1081. "Nothing to do, changes were already merged", output_text
  1082. )
  1083. # Check if the closing notification was added
  1084. output = self.app.get("/test/pull-request/1")
  1085. output_text = output.get_data(as_text=True)
  1086. self.assertIsNotNone(re.search(MERGED_PATTERN, output_text))
  1087. @patch("pagure.lib.notify.send_email")
  1088. def test_request_pull_close(self, send_email):
  1089. """ Test the request_pull endpoint with a closed PR. """
  1090. send_email.return_value = True
  1091. self.test_merge_request_pull_FF()
  1092. output = self.app.get("/test/pull-request/1")
  1093. self.assertEqual(output.status_code, 200)
  1094. output_text = output.get_data(as_text=True)
  1095. self.assertIsNotNone(re.search(MERGED_PATTERN, output_text))
  1096. self.assertIn(
  1097. 'title="View file as of 2a552bb">sources</a>', output_text
  1098. )
  1099. @patch("pagure.lib.notify.send_email")
  1100. def test_request_pull_disabled(self, send_email):
  1101. """ Test the request_pull endpoint with PR disabled. """
  1102. send_email.return_value = True
  1103. tests.create_projects(self.session)
  1104. tests.create_projects_git(
  1105. os.path.join(self.path, "requests"), bare=True
  1106. )
  1107. set_up_git_repo(
  1108. self.session, self.path, new_project=None, branch_from="feature"
  1109. )
  1110. # Project w/o pull-request
  1111. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  1112. settings = repo.settings
  1113. settings["pull_requests"] = False
  1114. repo.settings = settings
  1115. self.session.add(repo)
  1116. self.session.commit()
  1117. output = self.app.get("/test/pull-request/1")
  1118. self.assertEqual(output.status_code, 404)
  1119. @patch("pagure.lib.notify.send_email")
  1120. @patch("pagure.lib.git.update_pull_ref")
  1121. def test_request_pull_empty_repo(self, send_email, update_pull_ref):
  1122. """ Test the request_pull endpoint against an empty repo. """
  1123. # Mock update_pull_ref or the repo won't be empty anymore
  1124. # (the PR will have been pushed to refs/pull)
  1125. send_email.return_value = True
  1126. tests.create_projects(self.session)
  1127. item = pagure.lib.model.Project(
  1128. user_id=2, # foo
  1129. name="test",
  1130. description="test project #1",
  1131. hook_token="aaabbb",
  1132. is_fork=True,
  1133. parent_id=1,
  1134. )
  1135. self.session.add(item)
  1136. self.session.commit()
  1137. tests.create_projects_git(
  1138. os.path.join(self.path, "requests"), bare=True
  1139. )
  1140. tests.create_projects_git(
  1141. os.path.join(self.path, "repos", "forks", "foo"), bare=True
  1142. )
  1143. # Create a git repo to play with
  1144. gitrepo = os.path.join(self.path, "repos", "test.git")
  1145. self.assertFalse(os.path.exists(gitrepo))
  1146. os.makedirs(gitrepo)
  1147. repo = pygit2.init_repository(gitrepo, bare=True)
  1148. # Create a fork of this repo
  1149. newpath = tempfile.mkdtemp(prefix="pagure-fork-test")
  1150. gitrepo = os.path.join(self.path, "repos", "forks", "foo", "test.git")
  1151. new_repo = pygit2.clone_repository(gitrepo, newpath)
  1152. # Edit the sources file again
  1153. with open(os.path.join(newpath, "sources"), "w") as stream:
  1154. stream.write("foo\n bar\nbaz\n boose")
  1155. new_repo.index.add("sources")
  1156. new_repo.index.write()
  1157. # Commits the files added
  1158. tree = new_repo.index.write_tree()
  1159. author = pygit2.Signature("Alice Author", "alice@authors.tld")
  1160. committer = pygit2.Signature("Cecil Committer", "cecil@committers.tld")
  1161. new_repo.create_commit(
  1162. "refs/heads/feature",
  1163. author,
  1164. committer,
  1165. "A commit on branch feature",
  1166. tree,
  1167. [],
  1168. )
  1169. refname = "refs/heads/feature:refs/heads/feature"
  1170. ori_remote = new_repo.remotes[0]
  1171. PagureRepo.push(ori_remote, refname)
  1172. # Create a PR for these changes
  1173. project = pagure.lib.query.get_authorized_project(self.session, "test")
  1174. req = pagure.lib.query.new_pull_request(
  1175. session=self.session,
  1176. repo_from=item,
  1177. branch_from="feature",
  1178. repo_to=project,
  1179. branch_to="master",
  1180. title="PR from the feature branch",
  1181. user="pingou",
  1182. )
  1183. self.session.commit()
  1184. self.assertEqual(req.id, 1)
  1185. self.assertEqual(req.title, "PR from the feature branch")
  1186. output = self.app.get("/test/pull-request/1")
  1187. self.assertEqual(output.status_code, 200)
  1188. output_text = output.get_data(as_text=True)
  1189. self.assertIn(
  1190. '<h4 class="ml-1">\n <div>\n '
  1191. '<span class="fa fa-fw text-success fa-arrow-circle-down pt-1"></span>\n '
  1192. '<span class="text-success '
  1193. 'font-weight-bold">#1</span>\n '
  1194. '<span class="font-weight-bold">\n '
  1195. "PR from the feature branch\n",
  1196. output_text,
  1197. )
  1198. self.assertTrue(output_text.count('<span class="commitdate"'), 1)
  1199. self.assertTrue(update_pull_ref.called)
  1200. shutil.rmtree(newpath)
  1201. @patch("pagure.lib.notify.send_email")
  1202. def test_request_pull_empty_fork(self, send_email):
  1203. """ Test the request_pull endpoint from an empty fork. """
  1204. send_email.return_value = True
  1205. tests.create_projects(self.session)
  1206. item = pagure.lib.model.Project(
  1207. user_id=2, # foo
  1208. name="test",
  1209. description="test project #1",
  1210. hook_token="aaabbb",
  1211. is_fork=True,
  1212. parent_id=1,
  1213. )
  1214. self.session.add(item)
  1215. self.session.commit()
  1216. tests.create_projects_git(
  1217. os.path.join(self.path, "requests"), bare=True
  1218. )
  1219. tests.create_projects_git(
  1220. os.path.join(self.path, "repos", "forks", "foo"), bare=True
  1221. )
  1222. # Create a git repo to play with
  1223. gitrepo = os.path.join(self.path, "repos", "test.git")
  1224. self.assertFalse(os.path.exists(gitrepo))
  1225. os.makedirs(gitrepo)
  1226. repo = pygit2.init_repository(gitrepo, bare=True)
  1227. # Create a fork of this repo
  1228. newpath = tempfile.mkdtemp(prefix="pagure-fork-test")
  1229. gitrepo = os.path.join(self.path, "repos", "forks", "foo", "test.git")
  1230. new_repo = pygit2.clone_repository(gitrepo, newpath)
  1231. # Create a PR for these "changes" (there are none, both repos are
  1232. # empty)
  1233. project = pagure.lib.query.get_authorized_project(self.session, "test")
  1234. req = pagure.lib.query.new_pull_request(
  1235. session=self.session,
  1236. repo_from=item,
  1237. branch_from="feature",
  1238. repo_to=project,
  1239. branch_to="master",
  1240. title="PR from the feature branch",
  1241. user="pingou",
  1242. )
  1243. self.session.commit()
  1244. self.assertEqual(req.id, 1)
  1245. self.assertEqual(req.title, "PR from the feature branch")
  1246. output = self.app.get("/test/pull-request/1", follow_redirects=True)
  1247. self.assertEqual(output.status_code, 200)
  1248. output_text = output.get_data(as_text=True)
  1249. self.assertIn(
  1250. "<title>PR#1: PR from the feature branch - test\n - Pagure</title>",
  1251. output_text,
  1252. )
  1253. self.assertIn(
  1254. "Fork is empty, there are no "
  1255. "commits to create a pull request with",
  1256. output_text,
  1257. )
  1258. shutil.rmtree(newpath)
  1259. @patch("pagure.lib.notify.send_email")
  1260. def test_request_pulls_order(self, send_email):
  1261. """Test the request_pulls
  1262. i.e Make sure that the results are displayed
  1263. in the order required by the user"""
  1264. send_email.return_value = True
  1265. # Initially no project
  1266. output = self.app.get("/test/pull-requests")
  1267. self.assertEqual(output.status_code, 404)
  1268. tests.create_projects(self.session)
  1269. tests.create_projects_git(os.path.join(self.path, "repos"), bare=True)
  1270. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  1271. item = pagure.lib.model.Project(
  1272. user_id=2,
  1273. name="test",
  1274. description="test project #1",
  1275. hook_token="aaabbb",
  1276. is_fork=True,
  1277. parent_id=1,
  1278. )
  1279. self.session.add(item)
  1280. self.session.commit()
  1281. # create PR's to play with
  1282. # PR-1
  1283. req = pagure.lib.query.new_pull_request(
  1284. session=self.session,
  1285. repo_to=repo,
  1286. repo_from=item,
  1287. branch_from="feature",
  1288. branch_to="master",
  1289. title="PR from the feature branch",
  1290. user="pingou",
  1291. status="Open",
  1292. )
  1293. self.session.commit()
  1294. self.assertEqual(req.id, 1)
  1295. self.assertEqual(req.title, "PR from the feature branch")
  1296. # PR-2
  1297. req = pagure.lib.query.new_pull_request(
  1298. session=self.session,
  1299. repo_to=repo,
  1300. branch_to="master",
  1301. branch_from="feature",
  1302. repo_from=item,
  1303. title="test PR",
  1304. user="pingou",
  1305. status="Open",
  1306. )
  1307. self.session.commit()
  1308. self.assertEqual(req.title, "test PR")
  1309. # PR-3
  1310. req = pagure.lib.query.new_pull_request(
  1311. session=self.session,
  1312. repo_to=repo,
  1313. branch_from="feature",
  1314. branch_to="master",
  1315. repo_from=item,
  1316. title="test Invalid PR",
  1317. user="pingou",
  1318. status="Closed",
  1319. )
  1320. self.session.commit()
  1321. self.assertEqual(req.title, "test Invalid PR")
  1322. # PR-4
  1323. req = pagure.lib.query.new_pull_request(
  1324. session=self.session,
  1325. repo_to=repo,
  1326. branch_from="feature",
  1327. title="test PR for sort",
  1328. repo_from=item,
  1329. user="pingou",
  1330. branch_to="master",
  1331. status="Open",
  1332. )
  1333. self.session.commit()
  1334. self.assertEqual(req.title, "test PR for sort")
  1335. # sort by last_updated
  1336. output = self.app.get("/test/pull-requests?order_key=last_updated")
  1337. output_text = output.get_data(as_text=True)
  1338. tr_elements = re.findall(
  1339. '<div class="request-row list-group-item list-group-item-action ">(.*?)</div><!--end request-row-->',
  1340. output_text,
  1341. re.M | re.S,
  1342. )
  1343. self.assertEqual(output.status_code, 200)
  1344. # Make sure that issue four is first since it was modified last
  1345. self.assertIn('href="/test/pull-request/4"', tr_elements[0])
  1346. self.assertIn('href="/test/pull-request/2"', tr_elements[1])
  1347. self.assertIn('href="/test/pull-request/1"', tr_elements[2])
  1348. pr_one = pagure.lib.query.search_pull_requests(
  1349. self.session, project_id=1, requestid=1
  1350. )
  1351. pr_one.updated_on = datetime.utcnow() + timedelta(seconds=2)
  1352. self.session.add(pr_one)
  1353. self.session.commit()
  1354. # sort by last_updated
  1355. output = self.app.get("/test/pull-requests?order_key=last_updated")
  1356. output_text = output.get_data(as_text=True)
  1357. tr_elements = re.findall(
  1358. '<div class="request-row list-group-item list-group-item-action ">(.*?)</div><!--end request-row-->',
  1359. output_text,
  1360. re.M | re.S,
  1361. )
  1362. self.assertEqual(output.status_code, 200)
  1363. # Make sure that PR four is first since it was modified last
  1364. self.assertIn('href="/test/pull-request/1"', tr_elements[0])
  1365. # Make sure that PR two is second since it was modified second
  1366. self.assertIn('href="/test/pull-request/4"', tr_elements[1])
  1367. # Make sure that PR one is last since it was modified first
  1368. self.assertIn('href="/test/pull-request/2"', tr_elements[2])
  1369. # Now query so that the results are ascending
  1370. output = self.app.get(
  1371. "/test/pull-requests?" "order_key=last_updated&order=asc"
  1372. )
  1373. output_text = output.get_data(as_text=True)
  1374. tr_elements = re.findall(
  1375. '<div class="request-row list-group-item list-group-item-action ">(.*?)</div><!--end request-row-->',
  1376. output_text,
  1377. re.M | re.S,
  1378. )
  1379. self.assertIn('href="/test/pull-request/2"', tr_elements[0])
  1380. self.assertIn('href="/test/pull-request/4"', tr_elements[1])
  1381. self.assertIn('href="/test/pull-request/1"', tr_elements[2])
  1382. # check that search_pattern argument works
  1383. output = self.app.get("/test/pull-requests?search_pattern=feature")
  1384. output_text = output.get_data(as_text=True)
  1385. tr_elements = re.findall(
  1386. '<div class="request-row list-group-item list-group-item-action ">(.*?)</div><!--end request-row-->',
  1387. output_text,
  1388. re.M | re.S,
  1389. )
  1390. self.assertIn('href="/test/pull-request/1"', tr_elements[0])
  1391. self.assertEqual(len(tr_elements), 1)
  1392. output = self.app.get("/test/pull-requests?search_pattern=PR")
  1393. output_text = output.get_data(as_text=True)
  1394. tr_elements = re.findall(
  1395. '<div class="request-row list-group-item list-group-item-action ">(.*?)</div><!--end request-row-->',
  1396. output_text,
  1397. re.M | re.S,
  1398. )
  1399. self.assertIn('href="/test/pull-request/4"', tr_elements[0])
  1400. self.assertIn('href="/test/pull-request/2"', tr_elements[1])
  1401. self.assertIn('href="/test/pull-request/1"', tr_elements[2])
  1402. self.assertEqual(len(tr_elements), 3)
  1403. output = self.app.get("/test/pull-requests?search_pattern=*PR")
  1404. output_text = output.get_data(as_text=True)
  1405. tr_elements = re.findall(
  1406. '<div class="request-row list-group-item list-group-item-action ">(.*?)</div><!--end request-row-->',
  1407. output_text,
  1408. re.M | re.S,
  1409. )
  1410. self.assertEqual(len(tr_elements), 1)
  1411. self.assertIn('href="/test/pull-request/2"', tr_elements[0])
  1412. @patch("pagure.lib.notify.send_email")
  1413. def test_request_pulls(self, send_email):
  1414. """ Test the request_pulls endpoint. """
  1415. send_email.return_value = True
  1416. # No such project
  1417. output = self.app.get("/test/pull-requests")
  1418. self.assertEqual(output.status_code, 404)
  1419. tests.create_projects(self.session)
  1420. tests.create_projects_git(os.path.join(self.path, "repos"), bare=True)
  1421. output = self.app.get("/test/pull-requests")
  1422. self.assertEqual(output.status_code, 200)
  1423. output_text = output.get_data(as_text=True)
  1424. self.assertIn(
  1425. '<span class="fa fa-fw fa-arrow-circle-down"></span> 0 Open PRs\n',
  1426. output_text,
  1427. )
  1428. set_up_git_repo(
  1429. self.session, self.path, new_project=None, branch_from="feature"
  1430. )
  1431. output = self.app.get("/test/pull-requests")
  1432. self.assertEqual(output.status_code, 200)
  1433. output_text = output.get_data(as_text=True)
  1434. self.assertIn(
  1435. '<span class="fa fa-fw fa-arrow-circle-down"></span> 1 Open PRs\n',
  1436. output_text,
  1437. )
  1438. output = self.app.get("/test/pull-requests?status=1")
  1439. self.assertEqual(output.status_code, 200)
  1440. output_text = output.get_data(as_text=True)
  1441. self.assertIn(
  1442. '<span class="fa fa-fw fa-arrow-circle-down"></span> 1 Open PRs\n',
  1443. output_text,
  1444. )
  1445. output = self.app.get("/test/pull-requests?status=true")
  1446. self.assertEqual(output.status_code, 200)
  1447. output_text = output.get_data(as_text=True)
  1448. self.assertIn(
  1449. '<span class="fa fa-fw fa-arrow-circle-down"></span> 1 Open PRs\n',
  1450. output_text,
  1451. )
  1452. output = self.app.get("/test/pull-requests?status=Merged")
  1453. self.assertEqual(output.status_code, 200)
  1454. output_text = output.get_data(as_text=True)
  1455. self.assertIn(
  1456. '<span class="fa fa-fw fa-arrow-circle-down"></span> 0 Merged PRs\n',
  1457. output_text,
  1458. )
  1459. output = self.app.get("/test/pull-requests?status=0")
  1460. self.assertEqual(output.status_code, 200)
  1461. output_text = output.get_data(as_text=True)
  1462. self.assertIn(
  1463. '<span class="fa fa-fw fa-arrow-circle-down"></span> 0 Merged PRs\n',
  1464. output_text,
  1465. )
  1466. output = self.app.get("/test/pull-requests?status=Closed")
  1467. self.assertEqual(output.status_code, 200)
  1468. output_text = output.get_data(as_text=True)
  1469. self.assertIn(
  1470. '<span class="fa fa-fw fa-arrow-circle-down"></span> 0 Cancelled PRs\n',
  1471. output_text,
  1472. )
  1473. # Project w/o pull-request
  1474. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  1475. settings = repo.settings
  1476. settings["pull_requests"] = False
  1477. repo.settings = settings
  1478. self.session.add(repo)
  1479. self.session.commit()
  1480. output = self.app.get("/test/pull-requests")
  1481. self.assertEqual(output.status_code, 404)
  1482. @patch("pagure.lib.notify.send_email")
  1483. def test_request_pulls_filters_tags(self, send_email):
  1484. """Test the requests_pull
  1485. i.e Make sure that the results are filtered properly"""
  1486. send_email.return_value = True
  1487. tests.create_projects(self.session)
  1488. tests.create_projects_git(os.path.join(self.path, "repos"), bare=True)
  1489. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  1490. # Create some tags to play with
  1491. pagure.lib.query.new_tag(
  1492. self.session, "tag-1", "tag-1 descripcion", "#ff0000", repo.id
  1493. )
  1494. pagure.lib.query.new_tag(
  1495. self.session, "tag-2", "tag-2 description", "#00ff00", repo.id
  1496. )
  1497. pagure.lib.query.new_tag(
  1498. self.session, "tag-3", "tag-3 description", "#0000ff", repo.id
  1499. )
  1500. fork = pagure.lib.model.Project(
  1501. user_id=2,
  1502. name="test",
  1503. description="test project #1",
  1504. hook_token="aaabbb",
  1505. is_fork=True,
  1506. parent_id=1,
  1507. )
  1508. self.session.add(fork)
  1509. self.session.commit()
  1510. # Create PR's to play with
  1511. # PR-1, tags: tag-1, tag-3
  1512. req = pagure.lib.query.new_pull_request(
  1513. session=self.session,
  1514. repo_to=repo,
  1515. repo_from=fork,
  1516. branch_from="feature",
  1517. branch_to="master",
  1518. title="First PR",
  1519. user="pingou",
  1520. status="Open",
  1521. )
  1522. pagure.lib.query.update_tags(
  1523. self.session, obj=req, tags=["tag-1", "tag-3"], username="pingou"
  1524. )
  1525. self.session.commit()
  1526. # PR-2, tags: tag-2, tag-3
  1527. req = pagure.lib.query.new_pull_request(
  1528. session=self.session,
  1529. repo_to=repo,
  1530. repo_from=fork,
  1531. branch_from="feature",
  1532. branch_to="master",
  1533. title="Second PR",
  1534. user="pingou",
  1535. status="Open",
  1536. )
  1537. pagure.lib.query.update_tags(
  1538. self.session, obj=req, tags=["tag-2", "tag-3"], username="pingou"
  1539. )
  1540. self.session.commit()
  1541. # PR-3 closed, tags: tag-1, tag-3
  1542. req = pagure.lib.query.new_pull_request(
  1543. session=self.session,
  1544. repo_to=repo,
  1545. repo_from=fork,
  1546. branch_from="feature",
  1547. branch_to="master",
  1548. title="Third PR",
  1549. user="pingou",
  1550. status="Closed",
  1551. )
  1552. pagure.lib.query.update_tags(
  1553. self.session, obj=req, tags=["tag-1", "tag-3"], username="pingou"
  1554. )
  1555. self.session.commit()
  1556. # PR-4 closed, tags: tag-1, tag-2
  1557. req = pagure.lib.query.new_pull_request(
  1558. session=self.session,
  1559. repo_to=repo,
  1560. repo_from=fork,
  1561. branch_from="feature",
  1562. branch_to="master",
  1563. title="Fourth PR",
  1564. user="pingou",
  1565. status="Closed",
  1566. )
  1567. pagure.lib.query.update_tags(
  1568. self.session, obj=req, tags=["tag-1", "tag-2"], username="pingou"
  1569. )
  1570. self.session.commit()
  1571. # filter by 'tag-1'
  1572. output = self.app.get("/test/pull-requests?tags=tag-1")
  1573. self.assertEqual(output.status_code, 200)
  1574. output_text = output.get_data(as_text=True)
  1575. tr_elements = re.findall(
  1576. '<div class="request-row list-group-item list-group-item-action ">(.*?)</div><!--end request-row-->',
  1577. output_text,
  1578. re.M | re.S,
  1579. )
  1580. self.assertEqual(1, len(tr_elements))
  1581. self.assertIn('href="/test/pull-request/1', tr_elements[0])
  1582. # filter by '!tag-1'
  1583. output = self.app.get("/test/pull-requests?tags=!tag-1")
  1584. self.assertEqual(output.status_code, 200)
  1585. output_text = output.get_data(as_text=True)
  1586. tr_elements = re.findall(
  1587. '<div class="request-row list-group-item list-group-item-action ">(.*?)</div><!--end request-row-->',
  1588. output_text,
  1589. re.M | re.S,
  1590. )
  1591. self.assertEqual(1, len(tr_elements))
  1592. self.assertIn('href="/test/pull-request/2', tr_elements[0])
  1593. # filter by 'tag-2' and 'tag-3'
  1594. output = self.app.get("/test/pull-requests?tags=tag2&tags=tag-3")
  1595. self.assertEqual(output.status_code, 200)
  1596. output_text = output.get_data(as_text=True)
  1597. tr_elements = re.findall(
  1598. '<div class="request-row list-group-item list-group-item-action ">(.*?)</div><!--end request-row-->',
  1599. output_text,
  1600. re.M | re.S,
  1601. )
  1602. self.assertEqual(2, len(tr_elements))
  1603. self.assertIn('href="/test/pull-request/2', tr_elements[0])
  1604. self.assertIn('href="/test/pull-request/1', tr_elements[1])
  1605. # filter by '!tag-3'
  1606. output = self.app.get("/test/pull-requests?tags=!tag-3")
  1607. self.assertEqual(output.status_code, 200)
  1608. output_text = output.get_data(as_text=True)
  1609. tr_elements = re.findall(
  1610. '<div class="request-row list-group-item list-group-item-action ">(.*?)</div><!--end request-row-->',
  1611. output_text,
  1612. re.M | re.S,
  1613. )
  1614. self.assertEqual(0, len(tr_elements))
  1615. # filter by tag-2 on Closed prs
  1616. output = self.app.get("/test/pull-requests?status=Closed&tags=tag-2")
  1617. self.assertEqual(output.status_code, 200)
  1618. output_text = output.get_data(as_text=True)
  1619. tr_elements = re.findall(
  1620. '<div class="request-row list-group-item list-group-item-action ">(.*?)</div><!--end request-row-->',
  1621. output_text,
  1622. re.M | re.S,
  1623. )
  1624. self.assertEqual(1, len(tr_elements))
  1625. self.assertIn('href="/test/pull-request/4', tr_elements[0])
  1626. # filter by !tag-2 on Closed prs
  1627. output = self.app.get("/test/pull-requests?status=Closed&tags=!tag-2")
  1628. self.assertEqual(output.status_code, 200)
  1629. output_text = output.get_data(as_text=True)
  1630. tr_elements = re.findall(
  1631. '<div class="request-row list-group-item list-group-item-action ">(.*?)</div><!--end request-row-->',
  1632. output_text,
  1633. re.M | re.S,
  1634. )
  1635. self.assertEqual(1, len(tr_elements))
  1636. self.assertIn('href="/test/pull-request/3', tr_elements[0])
  1637. # filter by tag-2 on all the prs
  1638. output = self.app.get("/test/pull-requests?status=all&tags=tag-2")
  1639. self.assertEqual(output.status_code, 200)
  1640. output_text = output.get_data(as_text=True)
  1641. tr_elements = re.findall(
  1642. '<div class="request-row list-group-item list-group-item-action ">(.*?)</div><!--end request-row-->',
  1643. output_text,
  1644. re.M | re.S,
  1645. )
  1646. self.assertEqual(2, len(tr_elements))
  1647. self.assertIn('href="/test/pull-request/4', tr_elements[0])
  1648. self.assertIn('href="/test/pull-request/2', tr_elements[1])
  1649. @patch("pagure.lib.notify.send_email")
  1650. def test_request_pull_patch(self, send_email):
  1651. """ Test the request_pull_patch endpoint. """
  1652. send_email.return_value = True
  1653. output = self.app.get("/test/pull-request/1.patch")
  1654. self.assertEqual(output.status_code, 404)
  1655. tests.create_projects(self.session)
  1656. tests.create_projects_git(
  1657. os.path.join(self.path, "requests"), bare=True
  1658. )
  1659. set_up_git_repo(
  1660. self.session,
  1661. self.path,
  1662. new_project=None,
  1663. branch_from="feature",
  1664. mtype="merge",
  1665. )
  1666. output = self.app.get("/test/pull-request/100.patch")
  1667. self.assertEqual(output.status_code, 404)
  1668. output = self.app.get("/test/pull-request/1.patch")
  1669. self.assertEqual(output.status_code, 200)
  1670. npatch = []
  1671. for row in output.get_data(as_text=True).split("\n"):
  1672. if row.startswith("Date:"):
  1673. continue
  1674. if row.startswith("From "):
  1675. row = row.split(" ", 2)[2]
  1676. npatch.append(row)
  1677. exp = r"""Mon Sep 17 00:00:00 2001
  1678. From: Alice Author <alice@authors.tld>
  1679. Subject: A commit on branch feature
  1680. More information
  1681. ---
  1682. diff --git a/.gitignore b/.gitignore
  1683. new file mode 100644
  1684. index 0000000..e4e5f6c
  1685. --- /dev/null
  1686. +++ b/.gitignore
  1687. @@ -0,0 +1 @@
  1688. +*~
  1689. \ No newline at end of file
  1690. diff --git a/sources b/sources
  1691. index 9f44358..2a552bb 100644
  1692. --- a/sources
  1693. +++ b/sources
  1694. @@ -1,2 +1,4 @@
  1695. foo
  1696. - bar
  1697. \ No newline at end of file
  1698. + bar
  1699. +baz
  1700. + boose
  1701. \ No newline at end of file
  1702. """
  1703. patch = "\n".join(npatch)
  1704. # print patch
  1705. self.assertEqual(patch, exp)
  1706. # Project w/o pull-request
  1707. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  1708. settings = repo.settings
  1709. settings["pull_requests"] = False
  1710. repo.settings = settings
  1711. self.session.add(repo)
  1712. self.session.commit()
  1713. output = self.app.get("/test/pull-request/1.patch")
  1714. self.assertEqual(output.status_code, 404)
  1715. @patch("pagure.lib.notify.send_email")
  1716. def test_request_pull_diff(self, send_email):
  1717. """ Test the request_pull_patch endpoint. """
  1718. send_email.return_value = True
  1719. output = self.app.get("/test/pull-request/1.diff")
  1720. self.assertEqual(output.status_code, 404)
  1721. tests.create_projects(self.session)
  1722. tests.create_projects_git(
  1723. os.path.join(self.path, "requests"), bare=True
  1724. )
  1725. set_up_git_repo(
  1726. self.session,
  1727. self.path,
  1728. new_project=None,
  1729. branch_from="feature",
  1730. mtype="merge",
  1731. )
  1732. output = self.app.get("/test/pull-request/100.diff")
  1733. self.assertEqual(output.status_code, 404)
  1734. output = self.app.get("/test/pull-request/1.diff")
  1735. self.assertEqual(output.status_code, 200)
  1736. exp = r"""diff --git a/.gitignore b/.gitignore
  1737. new file mode 100644
  1738. index 0000000..e4e5f6c
  1739. --- /dev/null
  1740. +++ b/.gitignore
  1741. @@ -0,0 +1 @@
  1742. +*~
  1743. \ No newline at end of file
  1744. diff --git a/sources b/sources
  1745. index 9f44358..2a552bb 100644
  1746. --- a/sources
  1747. +++ b/sources
  1748. @@ -1,2 +1,4 @@
  1749. foo
  1750. - bar
  1751. \ No newline at end of file
  1752. + bar
  1753. +baz
  1754. + boose
  1755. \ No newline at end of file
  1756. """
  1757. self.assertEqual(output.get_data(as_text=True), exp)
  1758. # Project w/o pull-request
  1759. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  1760. settings = repo.settings
  1761. settings["pull_requests"] = False
  1762. repo.settings = settings
  1763. self.session.add(repo)
  1764. self.session.commit()
  1765. output = self.app.get("/test/pull-request/1.diff")
  1766. self.assertEqual(output.status_code, 404)
  1767. @patch("pagure.lib.notify.send_email")
  1768. def test_request_pull_patch_close(self, send_email):
  1769. """ Test the request_pull_patch endpoint with a closed PR. """
  1770. send_email.return_value = True
  1771. self.test_merge_request_pull_FF()
  1772. output = self.app.get("/test/pull-request/1.patch")
  1773. self.assertEqual(output.status_code, 200)
  1774. npatch = []
  1775. for row in output.get_data(as_text=True).split("\n"):
  1776. if row.startswith("Date:"):
  1777. continue
  1778. if row.startswith("From "):
  1779. row = row.split(" ", 2)[2]
  1780. npatch.append(row)
  1781. exp = r"""Mon Sep 17 00:00:00 2001
  1782. From: Alice Author <alice@authors.tld>
  1783. Subject: A commit on branch feature
  1784. More information
  1785. ---
  1786. diff --git a/sources b/sources
  1787. index 9f44358..2a552bb 100644
  1788. --- a/sources
  1789. +++ b/sources
  1790. @@ -1,2 +1,4 @@
  1791. foo
  1792. - bar
  1793. \ No newline at end of file
  1794. + bar
  1795. +baz
  1796. + boose
  1797. \ No newline at end of file
  1798. """
  1799. patch = "\n".join(npatch)
  1800. # print patch
  1801. self.assertEqual(patch, exp)
  1802. @patch("pagure.lib.notify.send_email")
  1803. @patch("pagure.lib.git.update_pull_ref")
  1804. def test_request_pull_patch_empty_repo(self, send_email, update_pull_ref):
  1805. """ Test the request_pull_patch endpoint against an empty repo. """
  1806. # Mock update_pull_ref or the repo won't be empty anymore
  1807. # (the PR will have been pushed to refs/pull)
  1808. send_email.return_value = True
  1809. tests.create_projects(self.session)
  1810. item = pagure.lib.model.Project(
  1811. user_id=2, # foo
  1812. name="test",
  1813. description="test project #1",
  1814. hook_token="aaabbb",
  1815. is_fork=True,
  1816. parent_id=1,
  1817. )
  1818. self.session.add(item)
  1819. self.session.commit()
  1820. tests.create_projects_git(
  1821. os.path.join(self.path, "requests"), bare=True
  1822. )
  1823. tests.create_projects_git(
  1824. os.path.join(self.path, "repos", "forks", "foo"), bare=True
  1825. )
  1826. # Create a git repo to play with
  1827. gitrepo = os.path.join(self.path, "repos", "test.git")
  1828. self.assertFalse(os.path.exists(gitrepo))
  1829. os.makedirs(gitrepo)
  1830. repo = pygit2.init_repository(gitrepo, bare=True)
  1831. # Create a fork of this repo
  1832. newpath = tempfile.mkdtemp(prefix="pagure-fork-test")
  1833. gitrepo = os.path.join(self.path, "repos", "forks", "foo", "test.git")
  1834. new_repo = pygit2.clone_repository(gitrepo, newpath)
  1835. # Edit the sources file again
  1836. with open(os.path.join(newpath, "sources"), "w") as stream:
  1837. stream.write("foo\n bar\nbaz\n boose")
  1838. new_repo.index.add("sources")
  1839. new_repo.index.write()
  1840. # Commits the files added
  1841. tree = new_repo.index.write_tree()
  1842. author = pygit2.Signature("Alice Author", "alice@authors.tld")
  1843. committer = pygit2.Signature("Cecil Committer", "cecil@committers.tld")
  1844. new_repo.create_commit(
  1845. "refs/heads/feature",
  1846. author,
  1847. committer,
  1848. "A commit on branch feature",
  1849. tree,
  1850. [],
  1851. )
  1852. refname = "refs/heads/feature:refs/heads/feature"
  1853. ori_remote = new_repo.remotes[0]
  1854. PagureRepo.push(ori_remote, refname)
  1855. # Create a PR for these "changes" (there are none, both repos are
  1856. # empty)
  1857. project = pagure.lib.query.get_authorized_project(self.session, "test")
  1858. req = pagure.lib.query.new_pull_request(
  1859. session=self.session,
  1860. repo_from=item,
  1861. branch_from="feature",
  1862. repo_to=project,
  1863. branch_to="master",
  1864. title="PR from the feature branch",
  1865. user="pingou",
  1866. )
  1867. self.session.commit()
  1868. self.assertEqual(req.id, 1)
  1869. self.assertEqual(req.title, "PR from the feature branch")
  1870. output = self.app.get(
  1871. "/test/pull-request/1.patch", follow_redirects=True
  1872. )
  1873. self.assertEqual(output.status_code, 200)
  1874. npatch = []
  1875. for row in output.get_data(as_text=True).split("\n"):
  1876. if row.startswith("Date:"):
  1877. continue
  1878. if row.startswith("From "):
  1879. row = row.split(" ", 2)[2]
  1880. npatch.append(row)
  1881. exp = r"""Mon Sep 17 00:00:00 2001
  1882. From: Alice Author <alice@authors.tld>
  1883. Subject: A commit on branch feature
  1884. ---
  1885. diff --git a/sources b/sources
  1886. new file mode 100644
  1887. index 0000000..2a552bb
  1888. --- /dev/null
  1889. +++ b/sources
  1890. @@ -0,0 +1,4 @@
  1891. +foo
  1892. + bar
  1893. +baz
  1894. + boose
  1895. \ No newline at end of file
  1896. """
  1897. patch = "\n".join(npatch)
  1898. # print patch
  1899. self.assertEqual(patch, exp)
  1900. shutil.rmtree(newpath)
  1901. @patch("pagure.lib.notify.send_email")
  1902. def test_request_pull_patch_empty_fork(self, send_email):
  1903. """ Test the request_pull_patch endpoint from an empty fork. """
  1904. send_email.return_value = True
  1905. tests.create_projects(self.session)
  1906. item = pagure.lib.model.Project(
  1907. user_id=2, # foo
  1908. name="test",
  1909. description="test project #1",
  1910. hook_token="aaabbb",
  1911. is_fork=True,
  1912. parent_id=1,
  1913. )
  1914. self.session.add(item)
  1915. self.session.commit()
  1916. tests.create_projects_git(
  1917. os.path.join(self.path, "requests"), bare=True
  1918. )
  1919. tests.create_projects_git(
  1920. os.path.join(self.path, "repos", "forks", "foo"), bare=True
  1921. )
  1922. # Create a git repo to play with
  1923. gitrepo = os.path.join(self.path, "repos", "test.git")
  1924. self.assertFalse(os.path.exists(gitrepo))
  1925. os.makedirs(gitrepo)
  1926. repo = pygit2.init_repository(gitrepo, bare=True)
  1927. # Create a fork of this repo
  1928. newpath = tempfile.mkdtemp(prefix="pagure-fork-test")
  1929. gitrepo = os.path.join(self.path, "repos", "forks", "foo", "test.git")
  1930. new_repo = pygit2.clone_repository(gitrepo, newpath)
  1931. # Create a PR for these "changes" (there are none, both repos are
  1932. # empty)
  1933. project = pagure.lib.query.get_authorized_project(self.session, "test")
  1934. req = pagure.lib.query.new_pull_request(
  1935. session=self.session,
  1936. repo_from=item,
  1937. branch_from="feature",
  1938. repo_to=project,
  1939. branch_to="master",
  1940. title="PR from the feature branch",
  1941. user="pingou",
  1942. )
  1943. self.session.commit()
  1944. self.assertEqual(req.id, 1)
  1945. self.assertEqual(req.title, "PR from the feature branch")
  1946. output = self.app.get(
  1947. "/test/pull-request/1.patch", follow_redirects=True
  1948. )
  1949. self.assertEqual(output.status_code, 200)
  1950. output_text = output.get_data(as_text=True)
  1951. self.assertIn("<title>Overview - test - Pagure</title>", output_text)
  1952. self.assertIn(
  1953. "Fork is empty, there are no "
  1954. "commits to create a pull request with",
  1955. output_text,
  1956. )
  1957. shutil.rmtree(newpath)
  1958. @patch("pagure.lib.notify.send_email")
  1959. def test_close_request_pull(self, send_email):
  1960. """ Test the close_request_pull endpoint. """
  1961. send_email.return_value = True
  1962. tests.create_projects(self.session)
  1963. tests.create_projects_git(
  1964. os.path.join(self.path, "requests"), bare=True
  1965. )
  1966. set_up_git_repo(
  1967. self.session,
  1968. self.path,
  1969. new_project=None,
  1970. branch_from="feature",
  1971. mtype="merge",
  1972. )
  1973. user = tests.FakeUser()
  1974. with tests.user_set(self.app.application, user):
  1975. output = self.app.post("/test/pull-request/close/1")
  1976. self.assertEqual(output.status_code, 302)
  1977. output = self.app.post(
  1978. "/test/pull-request/close/1", follow_redirects=True
  1979. )
  1980. self.assertEqual(output.status_code, 200)
  1981. output_text = output.get_data(as_text=True)
  1982. self.assertIn(
  1983. "<title>Overview - test - Pagure</title>", output_text
  1984. )
  1985. self.assertIn("Invalid input submitted", output_text)
  1986. output = self.app.get("/test/pull-request/1")
  1987. self.assertEqual(output.status_code, 200)
  1988. csrf_token = self.get_csrf(output=output)
  1989. data = {"csrf_token": csrf_token}
  1990. # Invalid project
  1991. output = self.app.post(
  1992. "/foo/pull-request/close/1", data=data, follow_redirects=True
  1993. )
  1994. self.assertEqual(output.status_code, 404)
  1995. # Invalid PR id
  1996. output = self.app.post(
  1997. "/test/pull-request/close/100",
  1998. data=data,
  1999. follow_redirects=True,
  2000. )
  2001. self.assertEqual(output.status_code, 404)
  2002. # Invalid user for this project
  2003. output = self.app.post(
  2004. "/test/pull-request/close/1", data=data, follow_redirects=True
  2005. )
  2006. self.assertEqual(output.status_code, 403)
  2007. user.username = "pingou"
  2008. with tests.user_set(self.app.application, user):
  2009. # Project w/o pull-request
  2010. repo = pagure.lib.query.get_authorized_project(
  2011. self.session, "test"
  2012. )
  2013. settings = repo.settings
  2014. settings["pull_requests"] = False
  2015. repo.settings = settings
  2016. self.session.add(repo)
  2017. self.session.commit()
  2018. output = self.app.post(
  2019. "/test/pull-request/close/1", data=data, follow_redirects=True
  2020. )
  2021. self.assertEqual(output.status_code, 404)
  2022. # Project w/ pull-request
  2023. repo = pagure.lib.query.get_authorized_project(
  2024. self.session, "test"
  2025. )
  2026. settings = repo.settings
  2027. settings["pull_requests"] = True
  2028. repo.settings = settings
  2029. self.session.add(repo)
  2030. self.session.commit()
  2031. output = self.app.post(
  2032. "/test/pull-request/close/1", data=data, follow_redirects=True
  2033. )
  2034. self.assertEqual(output.status_code, 200)
  2035. output_text = output.get_data(as_text=True)
  2036. self.assertIn(
  2037. "<title>Overview - test - Pagure</title>", output_text
  2038. )
  2039. self.assertIn("Pull request closed!", output_text)
  2040. @patch.dict(
  2041. "pagure.config.config", {"FEDORA_MESSAGING_NOTIFICATIONS": True}
  2042. )
  2043. @patch("pagure.lib.notify.send_email")
  2044. def test_reopen_request_pull(self, send_email):
  2045. """ Test the reopen_request_pull endpoint. """
  2046. send_email.return_value = True
  2047. tests.create_projects(self.session)
  2048. tests.create_projects_git(
  2049. os.path.join(self.path, "requests"), bare=True
  2050. )
  2051. set_up_git_repo(
  2052. self.session,
  2053. self.path,
  2054. new_project=None,
  2055. branch_from="feature",
  2056. mtype="merge",
  2057. )
  2058. user = tests.FakeUser()
  2059. with tests.user_set(self.app.application, user):
  2060. output = self.app.post("/test/pull-request/1/reopen")
  2061. self.assertEqual(output.status_code, 302)
  2062. output = self.app.post(
  2063. "/test/pull-request/1/reopen", follow_redirects=True
  2064. )
  2065. self.assertEqual(output.status_code, 200)
  2066. output_text = output.get_data(as_text=True)
  2067. self.assertIn(
  2068. "<title>PR#1: PR from the feature branch - test\n - Pagure</title>",
  2069. output_text,
  2070. )
  2071. self.assertIn(
  2072. #'Pull request reopened!',
  2073. 'return window.confirm("Are you sure you want to reopen this requested pull?")',
  2074. output_text,
  2075. )
  2076. output = self.app.get("/test/pull-request/1")
  2077. self.assertEqual(output.status_code, 200)
  2078. csrf_token = self.get_csrf(output=output)
  2079. data = {"csrf_token": csrf_token}
  2080. # Invalid project
  2081. output = self.app.post(
  2082. "/foo/pull-request/1/reopen", data=data, follow_redirects=True
  2083. )
  2084. self.assertEqual(output.status_code, 404)
  2085. # Invalid PR id
  2086. output = self.app.post(
  2087. "/test/pull-request/100/reopen",
  2088. data=data,
  2089. follow_redirects=True,
  2090. )
  2091. self.assertEqual(output.status_code, 404)
  2092. # Invalid user for this project
  2093. output = self.app.post(
  2094. "/test/pull-request/1/reopen", data=data, follow_redirects=True
  2095. )
  2096. self.assertEqual(output.status_code, 403)
  2097. user.username = "pingou"
  2098. with tests.user_set(self.app.application, user):
  2099. # Project w/o pull-request
  2100. repo = pagure.lib.query.get_authorized_project(
  2101. self.session, "test"
  2102. )
  2103. settings = repo.settings
  2104. settings["pull_requests"] = False
  2105. repo.settings = settings
  2106. self.session.add(repo)
  2107. self.session.commit()
  2108. output = self.app.post(
  2109. "/test/pull-request/1/reopen", data=data, follow_redirects=True
  2110. )
  2111. self.assertEqual(output.status_code, 404)
  2112. # Project w/ pull-request
  2113. repo = pagure.lib.query.get_authorized_project(
  2114. self.session, "test"
  2115. )
  2116. settings = repo.settings
  2117. settings["pull_requests"] = True
  2118. settings["fedmsg_notifications"] = True
  2119. repo.settings = settings
  2120. self.session.add(repo)
  2121. self.session.commit()
  2122. with testing.mock_sends(
  2123. pagure_messages.PullRequestCommentAddedV1(
  2124. topic="pagure.pull-request.comment.added",
  2125. body={
  2126. "pullrequest": {
  2127. "id": 1,
  2128. "uid": ANY,
  2129. "full_url": "http://localhost.localdomain/test/pull-request/1",
  2130. "title": "PR from the feature branch",
  2131. "branch": "master",
  2132. "project": {
  2133. "id": 1,
  2134. "name": "test",
  2135. "fullname": "test",
  2136. "full_url": "http://localhost.localdomain/test",
  2137. "url_path": "test",
  2138. "description": "test project #1",
  2139. "namespace": None,
  2140. "parent": None,
  2141. "date_created": ANY,
  2142. "date_modified": ANY,
  2143. "user": {
  2144. "name": "pingou",
  2145. "fullname": "PY C",
  2146. "url_path": "user/pingou",
  2147. "full_url": "http://localhost.localdomain/user/pingou",
  2148. },
  2149. "access_users": {
  2150. "owner": ["pingou"],
  2151. "admin": [],
  2152. "commit": [],
  2153. "collaborator": [],
  2154. "ticket": [],
  2155. },
  2156. "access_groups": {
  2157. "admin": [],
  2158. "commit": [],
  2159. "collaborator": [],
  2160. "ticket": [],
  2161. },
  2162. "tags": [],
  2163. "priorities": {},
  2164. "custom_keys": [],
  2165. "close_status": [
  2166. "Invalid",
  2167. "Insufficient data",
  2168. "Fixed",
  2169. "Duplicate",
  2170. ],
  2171. "milestones": {},
  2172. },
  2173. "branch_from": "feature",
  2174. "repo_from": {
  2175. "id": 1,
  2176. "name": "test",
  2177. "fullname": "test",
  2178. "url_path": "test",
  2179. "full_url": "http://localhost.localdomain/test",
  2180. "description": "test project #1",
  2181. "namespace": None,
  2182. "parent": None,
  2183. "date_created": ANY,
  2184. "date_modified": ANY,
  2185. "user": {
  2186. "name": "pingou",
  2187. "fullname": "PY C",
  2188. "url_path": "user/pingou",
  2189. "full_url": "http://localhost.localdomain/user/pingou",
  2190. },
  2191. "access_users": {
  2192. "owner": ["pingou"],
  2193. "admin": [],
  2194. "commit": [],
  2195. "collaborator": [],
  2196. "ticket": [],
  2197. },
  2198. "access_groups": {
  2199. "admin": [],
  2200. "commit": [],
  2201. "collaborator": [],
  2202. "ticket": [],
  2203. },
  2204. "tags": [],
  2205. "priorities": {},
  2206. "custom_keys": [],
  2207. "close_status": [
  2208. "Invalid",
  2209. "Insufficient data",
  2210. "Fixed",
  2211. "Duplicate",
  2212. ],
  2213. "milestones": {},
  2214. },
  2215. "remote_git": None,
  2216. "date_created": ANY,
  2217. "updated_on": ANY,
  2218. "last_updated": ANY,
  2219. "closed_at": ANY,
  2220. "user": {
  2221. "name": "pingou",
  2222. "fullname": "PY C",
  2223. "url_path": "user/pingou",
  2224. "full_url": "http://localhost.localdomain/user/pingou",
  2225. },
  2226. "assignee": None,
  2227. "status": "Closed",
  2228. "commit_start": ANY,
  2229. "commit_stop": ANY,
  2230. "closed_by": {
  2231. "name": "pingou",
  2232. "fullname": "PY C",
  2233. "url_path": "user/pingou",
  2234. "full_url": "http://localhost.localdomain/user/pingou",
  2235. },
  2236. "initial_comment": None,
  2237. "cached_merge_status": "unknown",
  2238. "threshold_reached": None,
  2239. "tags": [],
  2240. "comments": [
  2241. {
  2242. "id": 1,
  2243. "commit": None,
  2244. "tree": None,
  2245. "filename": None,
  2246. "line": None,
  2247. "comment": "Pull-Request has been closed by pingou",
  2248. "parent": None,
  2249. "date_created": ANY,
  2250. "user": {
  2251. "name": "pingou",
  2252. "fullname": "PY C",
  2253. "url_path": "user/pingou",
  2254. "full_url": "http://localhost.localdomain/user/pingou",
  2255. },
  2256. "edited_on": None,
  2257. "editor": None,
  2258. "notification": True,
  2259. "reactions": {},
  2260. }
  2261. ],
  2262. },
  2263. "agent": "pingou",
  2264. },
  2265. ),
  2266. pagure_messages.PullRequestClosedV1(
  2267. topic="pagure.pull-request.closed",
  2268. body={
  2269. "pullrequest": {
  2270. "id": 1,
  2271. "uid": ANY,
  2272. "title": "PR from the feature branch",
  2273. "full_url": "http://localhost.localdomain/test/pull-request/1",
  2274. "branch": "master",
  2275. "project": {
  2276. "id": 1,
  2277. "name": "test",
  2278. "fullname": "test",
  2279. "url_path": "test",
  2280. "full_url": "http://localhost.localdomain/test",
  2281. "description": "test project #1",
  2282. "namespace": None,
  2283. "parent": None,
  2284. "date_created": ANY,
  2285. "date_modified": ANY,
  2286. "user": {
  2287. "name": "pingou",
  2288. "fullname": "PY C",
  2289. "url_path": "user/pingou",
  2290. "full_url": "http://localhost.localdomain/user/pingou",
  2291. },
  2292. "access_users": {
  2293. "owner": ["pingou"],
  2294. "admin": [],
  2295. "commit": [],
  2296. "collaborator": [],
  2297. "ticket": [],
  2298. },
  2299. "access_groups": {
  2300. "admin": [],
  2301. "commit": [],
  2302. "collaborator": [],
  2303. "ticket": [],
  2304. },
  2305. "tags": [],
  2306. "priorities": {},
  2307. "custom_keys": [],
  2308. "close_status": [
  2309. "Invalid",
  2310. "Insufficient data",
  2311. "Fixed",
  2312. "Duplicate",
  2313. ],
  2314. "milestones": {},
  2315. },
  2316. "branch_from": "feature",
  2317. "repo_from": {
  2318. "id": 1,
  2319. "name": "test",
  2320. "fullname": "test",
  2321. "full_url": "http://localhost.localdomain/test",
  2322. "url_path": "test",
  2323. "description": "test project #1",
  2324. "namespace": None,
  2325. "parent": None,
  2326. "date_created": ANY,
  2327. "date_modified": ANY,
  2328. "user": {
  2329. "name": "pingou",
  2330. "fullname": "PY C",
  2331. "url_path": "user/pingou",
  2332. "full_url": "http://localhost.localdomain/user/pingou",
  2333. },
  2334. "access_users": {
  2335. "owner": ["pingou"],
  2336. "admin": [],
  2337. "commit": [],
  2338. "collaborator": [],
  2339. "ticket": [],
  2340. },
  2341. "access_groups": {
  2342. "admin": [],
  2343. "commit": [],
  2344. "collaborator": [],
  2345. "ticket": [],
  2346. },
  2347. "tags": [],
  2348. "priorities": {},
  2349. "custom_keys": [],
  2350. "close_status": [
  2351. "Invalid",
  2352. "Insufficient data",
  2353. "Fixed",
  2354. "Duplicate",
  2355. ],
  2356. "milestones": {},
  2357. },
  2358. "remote_git": None,
  2359. "date_created": ANY,
  2360. "updated_on": ANY,
  2361. "last_updated": ANY,
  2362. "closed_at": ANY,
  2363. "user": {
  2364. "name": "pingou",
  2365. "fullname": "PY C",
  2366. "url_path": "user/pingou",
  2367. "full_url": "http://localhost.localdomain/user/pingou",
  2368. },
  2369. "assignee": None,
  2370. "status": "Closed",
  2371. "commit_start": ANY,
  2372. "commit_stop": ANY,
  2373. "closed_by": {
  2374. "name": "pingou",
  2375. "fullname": "PY C",
  2376. "url_path": "user/pingou",
  2377. "full_url": "http://localhost.localdomain/user/pingou",
  2378. },
  2379. "initial_comment": None,
  2380. "cached_merge_status": "unknown",
  2381. "threshold_reached": None,
  2382. "tags": [],
  2383. "comments": [
  2384. {
  2385. "id": 1,
  2386. "commit": None,
  2387. "tree": None,
  2388. "filename": None,
  2389. "line": None,
  2390. "comment": "Pull-Request has been closed by pingou",
  2391. "parent": None,
  2392. "date_created": ANY,
  2393. "user": {
  2394. "name": "pingou",
  2395. "fullname": "PY C",
  2396. "url_path": "user/pingou",
  2397. "full_url": "http://localhost.localdomain/user/pingou",
  2398. },
  2399. "edited_on": None,
  2400. "editor": None,
  2401. "notification": True,
  2402. "reactions": {},
  2403. }
  2404. ],
  2405. },
  2406. "merged": False,
  2407. "agent": "pingou",
  2408. },
  2409. ),
  2410. ):
  2411. output = self.app.post(
  2412. "/test/pull-request/close/1",
  2413. data=data,
  2414. follow_redirects=True,
  2415. )
  2416. self.assertEqual(output.status_code, 200)
  2417. with testing.mock_sends(
  2418. pagure_messages.PullRequestCommentAddedV1(
  2419. topic="pagure.pull-request.comment.added",
  2420. body={
  2421. "pullrequest": {
  2422. "id": 1,
  2423. "uid": ANY,
  2424. "title": "PR from the feature branch",
  2425. "full_url": "http://localhost.localdomain/test/pull-request/1",
  2426. "branch": "master",
  2427. "project": {
  2428. "id": 1,
  2429. "name": "test",
  2430. "fullname": "test",
  2431. "url_path": "test",
  2432. "full_url": "http://localhost.localdomain/test",
  2433. "description": "test project #1",
  2434. "namespace": None,
  2435. "parent": None,
  2436. "date_created": ANY,
  2437. "date_modified": ANY,
  2438. "user": {
  2439. "name": "pingou",
  2440. "fullname": "PY C",
  2441. "url_path": "user/pingou",
  2442. "full_url": "http://localhost.localdomain/user/pingou",
  2443. },
  2444. "access_users": {
  2445. "owner": ["pingou"],
  2446. "admin": [],
  2447. "commit": [],
  2448. "collaborator": [],
  2449. "ticket": [],
  2450. },
  2451. "access_groups": {
  2452. "admin": [],
  2453. "commit": [],
  2454. "collaborator": [],
  2455. "ticket": [],
  2456. },
  2457. "tags": [],
  2458. "priorities": {},
  2459. "custom_keys": [],
  2460. "close_status": [
  2461. "Invalid",
  2462. "Insufficient data",
  2463. "Fixed",
  2464. "Duplicate",
  2465. ],
  2466. "milestones": {},
  2467. },
  2468. "branch_from": "feature",
  2469. "repo_from": {
  2470. "id": 1,
  2471. "name": "test",
  2472. "fullname": "test",
  2473. "url_path": "test",
  2474. "full_url": "http://localhost.localdomain/test",
  2475. "description": "test project #1",
  2476. "namespace": None,
  2477. "parent": None,
  2478. "date_created": ANY,
  2479. "date_modified": ANY,
  2480. "user": {
  2481. "name": "pingou",
  2482. "fullname": "PY C",
  2483. "url_path": "user/pingou",
  2484. "full_url": "http://localhost.localdomain/user/pingou",
  2485. },
  2486. "access_users": {
  2487. "owner": ["pingou"],
  2488. "admin": [],
  2489. "commit": [],
  2490. "collaborator": [],
  2491. "ticket": [],
  2492. },
  2493. "access_groups": {
  2494. "admin": [],
  2495. "commit": [],
  2496. "collaborator": [],
  2497. "ticket": [],
  2498. },
  2499. "tags": [],
  2500. "priorities": {},
  2501. "custom_keys": [],
  2502. "close_status": [
  2503. "Invalid",
  2504. "Insufficient data",
  2505. "Fixed",
  2506. "Duplicate",
  2507. ],
  2508. "milestones": {},
  2509. },
  2510. "remote_git": None,
  2511. "date_created": ANY,
  2512. "updated_on": ANY,
  2513. "last_updated": ANY,
  2514. "closed_at": ANY,
  2515. "user": {
  2516. "name": "pingou",
  2517. "fullname": "PY C",
  2518. "url_path": "user/pingou",
  2519. "full_url": "http://localhost.localdomain/user/pingou",
  2520. },
  2521. "assignee": None,
  2522. "status": "Open",
  2523. "commit_start": ANY,
  2524. "commit_stop": ANY,
  2525. "closed_by": {
  2526. "name": "pingou",
  2527. "fullname": "PY C",
  2528. "url_path": "user/pingou",
  2529. "full_url": "http://localhost.localdomain/user/pingou",
  2530. },
  2531. "initial_comment": None,
  2532. "cached_merge_status": "unknown",
  2533. "threshold_reached": None,
  2534. "tags": [],
  2535. "comments": [
  2536. {
  2537. "id": 1,
  2538. "commit": None,
  2539. "tree": None,
  2540. "filename": None,
  2541. "line": None,
  2542. "comment": "Pull-Request has been closed by pingou",
  2543. "parent": None,
  2544. "date_created": ANY,
  2545. "user": {
  2546. "name": "pingou",
  2547. "fullname": "PY C",
  2548. "url_path": "user/pingou",
  2549. "full_url": "http://localhost.localdomain/user/pingou",
  2550. },
  2551. "edited_on": None,
  2552. "editor": None,
  2553. "notification": True,
  2554. "reactions": {},
  2555. },
  2556. {
  2557. "id": 2,
  2558. "commit": None,
  2559. "tree": None,
  2560. "filename": None,
  2561. "line": None,
  2562. "comment": "Pull-Request has been reopened by pingou",
  2563. "parent": None,
  2564. "date_created": ANY,
  2565. "user": {
  2566. "name": "pingou",
  2567. "fullname": "PY C",
  2568. "url_path": "user/pingou",
  2569. "full_url": "http://localhost.localdomain/user/pingou",
  2570. },
  2571. "edited_on": None,
  2572. "editor": None,
  2573. "notification": True,
  2574. "reactions": {},
  2575. },
  2576. ],
  2577. },
  2578. "agent": "pingou",
  2579. },
  2580. ),
  2581. pagure_messages.PullRequestReopenedV1(
  2582. topic="pagure.pull-request.reopened",
  2583. body={
  2584. "pullrequest": {
  2585. "id": 1,
  2586. "uid": ANY,
  2587. "full_url": "http://localhost.localdomain/test/pull-request/1",
  2588. "title": "PR from the feature branch",
  2589. "branch": "master",
  2590. "project": {
  2591. "id": 1,
  2592. "name": "test",
  2593. "fullname": "test",
  2594. "url_path": "test",
  2595. "full_url": "http://localhost.localdomain/test",
  2596. "description": "test project #1",
  2597. "namespace": None,
  2598. "parent": None,
  2599. "date_created": ANY,
  2600. "date_modified": ANY,
  2601. "user": {
  2602. "name": "pingou",
  2603. "fullname": "PY C",
  2604. "url_path": "user/pingou",
  2605. "full_url": "http://localhost.localdomain/user/pingou",
  2606. },
  2607. "access_users": {
  2608. "owner": ["pingou"],
  2609. "admin": [],
  2610. "commit": [],
  2611. "collaborator": [],
  2612. "ticket": [],
  2613. },
  2614. "access_groups": {
  2615. "admin": [],
  2616. "commit": [],
  2617. "collaborator": [],
  2618. "ticket": [],
  2619. },
  2620. "tags": [],
  2621. "priorities": {},
  2622. "custom_keys": [],
  2623. "close_status": [
  2624. "Invalid",
  2625. "Insufficient data",
  2626. "Fixed",
  2627. "Duplicate",
  2628. ],
  2629. "milestones": {},
  2630. },
  2631. "branch_from": "feature",
  2632. "repo_from": {
  2633. "id": 1,
  2634. "name": "test",
  2635. "full_url": "http://localhost.localdomain/test",
  2636. "fullname": "test",
  2637. "url_path": "test",
  2638. "description": "test project #1",
  2639. "namespace": None,
  2640. "parent": None,
  2641. "date_created": ANY,
  2642. "date_modified": ANY,
  2643. "user": {
  2644. "name": "pingou",
  2645. "fullname": "PY C",
  2646. "url_path": "user/pingou",
  2647. "full_url": "http://localhost.localdomain/user/pingou",
  2648. },
  2649. "access_users": {
  2650. "owner": ["pingou"],
  2651. "admin": [],
  2652. "commit": [],
  2653. "collaborator": [],
  2654. "ticket": [],
  2655. },
  2656. "access_groups": {
  2657. "admin": [],
  2658. "commit": [],
  2659. "collaborator": [],
  2660. "ticket": [],
  2661. },
  2662. "tags": [],
  2663. "priorities": {},
  2664. "custom_keys": [],
  2665. "close_status": [
  2666. "Invalid",
  2667. "Insufficient data",
  2668. "Fixed",
  2669. "Duplicate",
  2670. ],
  2671. "milestones": {},
  2672. },
  2673. "remote_git": None,
  2674. "date_created": ANY,
  2675. "updated_on": ANY,
  2676. "last_updated": ANY,
  2677. "closed_at": ANY,
  2678. "user": {
  2679. "name": "pingou",
  2680. "fullname": "PY C",
  2681. "url_path": "user/pingou",
  2682. "full_url": "http://localhost.localdomain/user/pingou",
  2683. },
  2684. "assignee": None,
  2685. "status": "Open",
  2686. "commit_start": ANY,
  2687. "commit_stop": ANY,
  2688. "closed_by": {
  2689. "name": "pingou",
  2690. "fullname": "PY C",
  2691. "url_path": "user/pingou",
  2692. "full_url": "http://localhost.localdomain/user/pingou",
  2693. },
  2694. "initial_comment": None,
  2695. "cached_merge_status": "unknown",
  2696. "threshold_reached": None,
  2697. "tags": [],
  2698. "comments": [
  2699. {
  2700. "id": 1,
  2701. "commit": None,
  2702. "tree": None,
  2703. "filename": None,
  2704. "line": None,
  2705. "comment": "Pull-Request has been closed by pingou",
  2706. "parent": None,
  2707. "date_created": ANY,
  2708. "user": {
  2709. "name": "pingou",
  2710. "fullname": "PY C",
  2711. "url_path": "user/pingou",
  2712. "full_url": "http://localhost.localdomain/user/pingou",
  2713. },
  2714. "edited_on": None,
  2715. "editor": None,
  2716. "notification": True,
  2717. "reactions": {},
  2718. },
  2719. {
  2720. "id": 2,
  2721. "commit": None,
  2722. "tree": None,
  2723. "filename": None,
  2724. "line": None,
  2725. "comment": "Pull-Request has been reopened by pingou",
  2726. "parent": None,
  2727. "date_created": ANY,
  2728. "user": {
  2729. "name": "pingou",
  2730. "fullname": "PY C",
  2731. "url_path": "user/pingou",
  2732. "full_url": "http://localhost.localdomain/user/pingou",
  2733. },
  2734. "edited_on": None,
  2735. "editor": None,
  2736. "notification": True,
  2737. "reactions": {},
  2738. },
  2739. ],
  2740. },
  2741. "agent": "pingou",
  2742. },
  2743. ),
  2744. ):
  2745. output = self.app.post(
  2746. "/test/pull-request/1/reopen",
  2747. data=data,
  2748. follow_redirects=True,
  2749. )
  2750. self.assertEqual(output.status_code, 200)
  2751. output_text = output.get_data(as_text=True)
  2752. self.assertIn(
  2753. "<title>PR#1: PR from the feature branch - test\n - "
  2754. "Pagure</title>",
  2755. output_text,
  2756. )
  2757. self.assertIn(
  2758. 'return window.confirm("Are you sure you want to reopen this requested pull?")',
  2759. output_text,
  2760. )
  2761. @patch.dict(
  2762. "pagure.config.config", {"FEDORA_MESSAGING_NOTIFICATIONS": True}
  2763. )
  2764. @patch("pagure.lib.notify.send_email", MagicMock(return_value=True))
  2765. def test_update_pull_requests_assign(self):
  2766. """Test the update_pull_requests endpoint when assigning a PR."""
  2767. tests.create_projects(self.session)
  2768. tests.create_projects_git(
  2769. os.path.join(self.path, "requests"), bare=True
  2770. )
  2771. set_up_git_repo(
  2772. self.session, self.path, new_project=None, branch_from="feature"
  2773. )
  2774. user = tests.FakeUser()
  2775. user.username = "pingou"
  2776. with tests.user_set(self.app.application, user):
  2777. # No such project
  2778. output = self.app.post("/foo/pull-request/1/update")
  2779. self.assertEqual(output.status_code, 404)
  2780. output = self.app.post("/test/pull-request/100/update")
  2781. self.assertEqual(output.status_code, 404)
  2782. # Invalid input
  2783. output = self.app.post(
  2784. "/test/pull-request/1/update", follow_redirects=True
  2785. )
  2786. self.assertEqual(output.status_code, 200)
  2787. output_text = output.get_data(as_text=True)
  2788. self.assertIn(
  2789. "<title>PR#1: PR from the feature branch - test\n - "
  2790. "Pagure</title>",
  2791. output_text,
  2792. )
  2793. self.assertIn(
  2794. '<h4 class="ml-1">\n <div>\n '
  2795. '<span class="fa fa-fw text-success fa-arrow-circle-down pt-1"></span>\n '
  2796. '<span class="text-success '
  2797. 'font-weight-bold">#1</span>\n '
  2798. '<span class="font-weight-bold">\n '
  2799. "PR from the feature branch\n",
  2800. output_text,
  2801. )
  2802. self.assertNotIn("Request assigned", output_text)
  2803. output = self.app.get("/test/pull-request/1")
  2804. self.assertEqual(output.status_code, 200)
  2805. csrf_token = self.get_csrf(output=output)
  2806. data = {"user": "pingou"}
  2807. # No CSRF
  2808. output = self.app.post(
  2809. "/test/pull-request/1/update", data=data, follow_redirects=True
  2810. )
  2811. self.assertEqual(output.status_code, 200)
  2812. output_text = output.get_data(as_text=True)
  2813. self.assertIn(
  2814. "<title>PR#1: PR from the feature branch - test\n - "
  2815. "Pagure</title>",
  2816. output_text,
  2817. )
  2818. self.assertIn(
  2819. '<h4 class="ml-1">\n <div>\n '
  2820. '<span class="fa fa-fw text-success fa-arrow-circle-down pt-1"></span>\n '
  2821. '<span class="text-success '
  2822. 'font-weight-bold">#1</span>\n '
  2823. '<span class="font-weight-bold">\n '
  2824. "PR from the feature branch\n",
  2825. output_text,
  2826. )
  2827. self.assertNotIn("Request assigned", output_text)
  2828. # Invalid assignee
  2829. data = {"csrf_token": csrf_token, "user": "bar"}
  2830. output = self.app.post(
  2831. "/test/pull-request/1/update", data=data, follow_redirects=True
  2832. )
  2833. self.assertEqual(output.status_code, 200)
  2834. output_text = output.get_data(as_text=True)
  2835. self.assertIn(
  2836. "<title>PR#1: PR from the feature branch - test\n - "
  2837. "Pagure</title>",
  2838. output_text,
  2839. )
  2840. self.assertIn(
  2841. '<h4 class="ml-1">\n <div>\n '
  2842. '<span class="fa fa-fw text-success fa-arrow-circle-down pt-1"></span>\n '
  2843. '<span class="text-success '
  2844. 'font-weight-bold">#1</span>\n '
  2845. '<span class="font-weight-bold">\n '
  2846. "PR from the feature branch\n",
  2847. output_text,
  2848. )
  2849. self.assertIn("No user &#34;bar&#34; found", output_text)
  2850. # Assign the PR
  2851. data = {"csrf_token": csrf_token, "user": "pingou"}
  2852. user.username = "foo"
  2853. with tests.user_set(self.app.application, user):
  2854. output = self.app.post(
  2855. "/test/pull-request/1/update", data=data, follow_redirects=True
  2856. )
  2857. self.assertEqual(output.status_code, 403)
  2858. user.username = "pingou"
  2859. with tests.user_set(self.app.application, user):
  2860. with testing.mock_sends(
  2861. api.Message(
  2862. topic="pagure.request.assigned.added",
  2863. body={
  2864. "request": {
  2865. "id": 1,
  2866. "full_url": "http://localhost.localdomain/test/pull-request/1",
  2867. "uid": ANY,
  2868. "title": "PR from the feature branch",
  2869. "branch": "master",
  2870. "project": {
  2871. "id": 1,
  2872. "name": "test",
  2873. "fullname": "test",
  2874. "url_path": "test",
  2875. "full_url": "http://localhost.localdomain/test",
  2876. "description": "test project #1",
  2877. "namespace": None,
  2878. "parent": None,
  2879. "date_created": ANY,
  2880. "date_modified": ANY,
  2881. "user": {
  2882. "name": "pingou",
  2883. "fullname": "PY C",
  2884. "url_path": "user/pingou",
  2885. "full_url": "http://localhost.localdomain/user/pingou",
  2886. },
  2887. "access_users": {
  2888. "owner": ["pingou"],
  2889. "admin": [],
  2890. "commit": [],
  2891. "collaborator": [],
  2892. "ticket": [],
  2893. },
  2894. "access_groups": {
  2895. "admin": [],
  2896. "commit": [],
  2897. "collaborator": [],
  2898. "ticket": [],
  2899. },
  2900. "tags": [],
  2901. "priorities": {},
  2902. "custom_keys": [],
  2903. "close_status": [
  2904. "Invalid",
  2905. "Insufficient data",
  2906. "Fixed",
  2907. "Duplicate",
  2908. ],
  2909. "milestones": {},
  2910. },
  2911. "branch_from": "feature",
  2912. "repo_from": {
  2913. "id": 1,
  2914. "name": "test",
  2915. "fullname": "test",
  2916. "url_path": "test",
  2917. "full_url": "http://localhost.localdomain/test",
  2918. "description": "test project #1",
  2919. "namespace": None,
  2920. "parent": None,
  2921. "date_created": ANY,
  2922. "date_modified": ANY,
  2923. "user": {
  2924. "name": "pingou",
  2925. "fullname": "PY C",
  2926. "url_path": "user/pingou",
  2927. "full_url": "http://localhost.localdomain/user/pingou",
  2928. },
  2929. "access_users": {
  2930. "owner": ["pingou"],
  2931. "admin": [],
  2932. "commit": [],
  2933. "collaborator": [],
  2934. "ticket": [],
  2935. },
  2936. "access_groups": {
  2937. "admin": [],
  2938. "commit": [],
  2939. "collaborator": [],
  2940. "ticket": [],
  2941. },
  2942. "tags": [],
  2943. "priorities": {},
  2944. "custom_keys": [],
  2945. "close_status": [
  2946. "Invalid",
  2947. "Insufficient data",
  2948. "Fixed",
  2949. "Duplicate",
  2950. ],
  2951. "milestones": {},
  2952. },
  2953. "remote_git": None,
  2954. "date_created": ANY,
  2955. "updated_on": ANY,
  2956. "last_updated": ANY,
  2957. "closed_at": None,
  2958. "user": {
  2959. "name": "pingou",
  2960. "fullname": "PY C",
  2961. "url_path": "user/pingou",
  2962. "full_url": "http://localhost.localdomain/user/pingou",
  2963. },
  2964. "assignee": {
  2965. "name": "pingou",
  2966. "fullname": "PY C",
  2967. "full_url": "http://localhost.localdomain/user/pingou",
  2968. "url_path": "user/pingou",
  2969. },
  2970. "status": "Open",
  2971. "commit_start": ANY,
  2972. "commit_stop": ANY,
  2973. "closed_by": None,
  2974. "initial_comment": None,
  2975. "cached_merge_status": "unknown",
  2976. "threshold_reached": None,
  2977. "tags": [],
  2978. "comments": [],
  2979. },
  2980. "pullrequest": {
  2981. "id": 1,
  2982. "full_url": "http://localhost.localdomain/test/pull-request/1",
  2983. "uid": ANY,
  2984. "title": "PR from the feature branch",
  2985. "branch": "master",
  2986. "project": {
  2987. "id": 1,
  2988. "name": "test",
  2989. "fullname": "test",
  2990. "full_url": "http://localhost.localdomain/test",
  2991. "url_path": "test",
  2992. "description": "test project #1",
  2993. "namespace": None,
  2994. "parent": None,
  2995. "date_created": ANY,
  2996. "date_modified": ANY,
  2997. "user": {
  2998. "name": "pingou",
  2999. "fullname": "PY C",
  3000. "url_path": "user/pingou",
  3001. "full_url": "http://localhost.localdomain/user/pingou",
  3002. },
  3003. "access_users": {
  3004. "owner": ["pingou"],
  3005. "admin": [],
  3006. "commit": [],
  3007. "collaborator": [],
  3008. "ticket": [],
  3009. },
  3010. "access_groups": {
  3011. "admin": [],
  3012. "commit": [],
  3013. "collaborator": [],
  3014. "ticket": [],
  3015. },
  3016. "tags": [],
  3017. "priorities": {},
  3018. "custom_keys": [],
  3019. "close_status": [
  3020. "Invalid",
  3021. "Insufficient data",
  3022. "Fixed",
  3023. "Duplicate",
  3024. ],
  3025. "milestones": {},
  3026. },
  3027. "branch_from": "feature",
  3028. "repo_from": {
  3029. "id": 1,
  3030. "name": "test",
  3031. "fullname": "test",
  3032. "url_path": "test",
  3033. "full_url": "http://localhost.localdomain/test",
  3034. "description": "test project #1",
  3035. "namespace": None,
  3036. "parent": None,
  3037. "date_created": ANY,
  3038. "date_modified": ANY,
  3039. "user": {
  3040. "name": "pingou",
  3041. "fullname": "PY C",
  3042. "url_path": "user/pingou",
  3043. "full_url": "http://localhost.localdomain/user/pingou",
  3044. },
  3045. "access_users": {
  3046. "owner": ["pingou"],
  3047. "admin": [],
  3048. "commit": [],
  3049. "collaborator": [],
  3050. "ticket": [],
  3051. },
  3052. "access_groups": {
  3053. "admin": [],
  3054. "commit": [],
  3055. "collaborator": [],
  3056. "ticket": [],
  3057. },
  3058. "tags": [],
  3059. "priorities": {},
  3060. "custom_keys": [],
  3061. "close_status": [
  3062. "Invalid",
  3063. "Insufficient data",
  3064. "Fixed",
  3065. "Duplicate",
  3066. ],
  3067. "milestones": {},
  3068. },
  3069. "remote_git": None,
  3070. "date_created": ANY,
  3071. "updated_on": ANY,
  3072. "last_updated": ANY,
  3073. "closed_at": None,
  3074. "user": {
  3075. "name": "pingou",
  3076. "fullname": "PY C",
  3077. "url_path": "user/pingou",
  3078. "full_url": "http://localhost.localdomain/user/pingou",
  3079. },
  3080. "assignee": {
  3081. "name": "pingou",
  3082. "fullname": "PY C",
  3083. "url_path": "user/pingou",
  3084. "full_url": "http://localhost.localdomain/user/pingou",
  3085. },
  3086. "status": "Open",
  3087. "commit_start": ANY,
  3088. "commit_stop": ANY,
  3089. "closed_by": None,
  3090. "initial_comment": None,
  3091. "cached_merge_status": "unknown",
  3092. "threshold_reached": None,
  3093. "tags": [],
  3094. "comments": [],
  3095. },
  3096. "project": {
  3097. "id": 1,
  3098. "name": "test",
  3099. "fullname": "test",
  3100. "full_url": "http://localhost.localdomain/test",
  3101. "url_path": "test",
  3102. "description": "test project #1",
  3103. "namespace": None,
  3104. "parent": None,
  3105. "date_created": ANY,
  3106. "date_modified": ANY,
  3107. "user": {
  3108. "name": "pingou",
  3109. "fullname": "PY C",
  3110. "url_path": "user/pingou",
  3111. "full_url": "http://localhost.localdomain/user/pingou",
  3112. },
  3113. "access_users": {
  3114. "owner": ["pingou"],
  3115. "admin": [],
  3116. "commit": [],
  3117. "collaborator": [],
  3118. "ticket": [],
  3119. },
  3120. "access_groups": {
  3121. "admin": [],
  3122. "commit": [],
  3123. "collaborator": [],
  3124. "ticket": [],
  3125. },
  3126. "tags": [],
  3127. "priorities": {},
  3128. "custom_keys": [],
  3129. "close_status": [
  3130. "Invalid",
  3131. "Insufficient data",
  3132. "Fixed",
  3133. "Duplicate",
  3134. ],
  3135. "milestones": {},
  3136. },
  3137. "agent": "pingou",
  3138. },
  3139. ),
  3140. pagure_messages.PullRequestAssignedAddedV1(
  3141. topic="pagure.pull-request.assigned.added",
  3142. body={
  3143. "pullrequest": {
  3144. "id": 1,
  3145. "full_url": "http://localhost.localdomain/test/pull-request/1",
  3146. "uid": ANY,
  3147. "title": "PR from the feature branch",
  3148. "branch": "master",
  3149. "project": {
  3150. "id": 1,
  3151. "name": "test",
  3152. "fullname": "test",
  3153. "url_path": "test",
  3154. "full_url": "http://localhost.localdomain/test",
  3155. "description": "test project #1",
  3156. "namespace": None,
  3157. "parent": None,
  3158. "date_created": ANY,
  3159. "date_modified": ANY,
  3160. "user": {
  3161. "name": "pingou",
  3162. "fullname": "PY C",
  3163. "url_path": "user/pingou",
  3164. "full_url": "http://localhost.localdomain/user/pingou",
  3165. },
  3166. "access_users": {
  3167. "owner": ["pingou"],
  3168. "admin": [],
  3169. "commit": [],
  3170. "collaborator": [],
  3171. "ticket": [],
  3172. },
  3173. "access_groups": {
  3174. "admin": [],
  3175. "commit": [],
  3176. "collaborator": [],
  3177. "ticket": [],
  3178. },
  3179. "tags": [],
  3180. "priorities": {},
  3181. "custom_keys": [],
  3182. "close_status": [
  3183. "Invalid",
  3184. "Insufficient data",
  3185. "Fixed",
  3186. "Duplicate",
  3187. ],
  3188. "milestones": {},
  3189. },
  3190. "branch_from": "feature",
  3191. "repo_from": {
  3192. "id": 1,
  3193. "name": "test",
  3194. "fullname": "test",
  3195. "url_path": "test",
  3196. "full_url": "http://localhost.localdomain/test",
  3197. "description": "test project #1",
  3198. "namespace": None,
  3199. "parent": None,
  3200. "date_created": ANY,
  3201. "date_modified": ANY,
  3202. "user": {
  3203. "name": "pingou",
  3204. "fullname": "PY C",
  3205. "url_path": "user/pingou",
  3206. "full_url": "http://localhost.localdomain/user/pingou",
  3207. },
  3208. "access_users": {
  3209. "owner": ["pingou"],
  3210. "admin": [],
  3211. "commit": [],
  3212. "collaborator": [],
  3213. "ticket": [],
  3214. },
  3215. "access_groups": {
  3216. "admin": [],
  3217. "commit": [],
  3218. "collaborator": [],
  3219. "ticket": [],
  3220. },
  3221. "tags": [],
  3222. "priorities": {},
  3223. "custom_keys": [],
  3224. "close_status": [
  3225. "Invalid",
  3226. "Insufficient data",
  3227. "Fixed",
  3228. "Duplicate",
  3229. ],
  3230. "milestones": {},
  3231. },
  3232. "remote_git": None,
  3233. "date_created": ANY,
  3234. "updated_on": ANY,
  3235. "last_updated": ANY,
  3236. "closed_at": None,
  3237. "user": {
  3238. "name": "pingou",
  3239. "fullname": "PY C",
  3240. "url_path": "user/pingou",
  3241. "full_url": "http://localhost.localdomain/user/pingou",
  3242. },
  3243. "assignee": {
  3244. "name": "pingou",
  3245. "fullname": "PY C",
  3246. "url_path": "user/pingou",
  3247. "full_url": "http://localhost.localdomain/user/pingou",
  3248. },
  3249. "status": "Open",
  3250. "commit_start": ANY,
  3251. "commit_stop": ANY,
  3252. "closed_by": None,
  3253. "initial_comment": None,
  3254. "cached_merge_status": "unknown",
  3255. "threshold_reached": None,
  3256. "tags": [],
  3257. "comments": [],
  3258. },
  3259. "project": {
  3260. "id": 1,
  3261. "name": "test",
  3262. "fullname": "test",
  3263. "url_path": "test",
  3264. "full_url": "http://localhost.localdomain/test",
  3265. "description": "test project #1",
  3266. "namespace": None,
  3267. "parent": None,
  3268. "date_created": ANY,
  3269. "date_modified": ANY,
  3270. "user": {
  3271. "name": "pingou",
  3272. "fullname": "PY C",
  3273. "url_path": "user/pingou",
  3274. "full_url": "http://localhost.localdomain/user/pingou",
  3275. },
  3276. "access_users": {
  3277. "owner": ["pingou"],
  3278. "admin": [],
  3279. "commit": [],
  3280. "collaborator": [],
  3281. "ticket": [],
  3282. },
  3283. "access_groups": {
  3284. "admin": [],
  3285. "commit": [],
  3286. "collaborator": [],
  3287. "ticket": [],
  3288. },
  3289. "tags": [],
  3290. "priorities": {},
  3291. "custom_keys": [],
  3292. "close_status": [
  3293. "Invalid",
  3294. "Insufficient data",
  3295. "Fixed",
  3296. "Duplicate",
  3297. ],
  3298. "milestones": {},
  3299. },
  3300. "agent": "pingou",
  3301. },
  3302. ),
  3303. ):
  3304. output = self.app.post(
  3305. "/test/pull-request/1/update",
  3306. data=data,
  3307. follow_redirects=True,
  3308. )
  3309. self.assertEqual(output.status_code, 200)
  3310. output_text = output.get_data(as_text=True)
  3311. self.assertIn(
  3312. "<title>PR#1: PR from the feature branch - test\n - "
  3313. "Pagure</title>",
  3314. output_text,
  3315. )
  3316. self.assertIn(
  3317. '<h4 class="ml-1">\n <div>\n '
  3318. '<span class="fa fa-fw text-success fa-arrow-circle-down pt-1"></span>\n '
  3319. '<span class="text-success '
  3320. 'font-weight-bold">#1</span>\n '
  3321. '<span class="font-weight-bold">\n '
  3322. "PR from the feature branch\n",
  3323. output_text,
  3324. )
  3325. self.assertIn("Request assigned", output_text)
  3326. # Pull-Request closed - reset assignee
  3327. repo = pagure.lib.query.get_authorized_project(
  3328. self.session, "test"
  3329. )
  3330. req = repo.requests[0]
  3331. req.status = "Closed"
  3332. req.closed_by_in = 1
  3333. self.session.add(req)
  3334. self.session.commit()
  3335. data = {"csrf_token": csrf_token, "user": None}
  3336. with testing.mock_sends(
  3337. api.Message(
  3338. topic="pagure.request.assigned.reset",
  3339. body={
  3340. "request": {
  3341. "id": 1,
  3342. "full_url": "http://localhost.localdomain/test/pull-request/1",
  3343. "uid": ANY,
  3344. "title": "PR from the feature branch",
  3345. "branch": "master",
  3346. "project": {
  3347. "id": 1,
  3348. "name": "test",
  3349. "fullname": "test",
  3350. "url_path": "test",
  3351. "full_url": "http://localhost.localdomain/test",
  3352. "description": "test project #1",
  3353. "namespace": None,
  3354. "parent": None,
  3355. "date_created": ANY,
  3356. "date_modified": ANY,
  3357. "user": {
  3358. "name": "pingou",
  3359. "fullname": "PY C",
  3360. "url_path": "user/pingou",
  3361. "full_url": "http://localhost.localdomain/user/pingou",
  3362. },
  3363. "access_users": {
  3364. "owner": ["pingou"],
  3365. "admin": [],
  3366. "commit": [],
  3367. "collaborator": [],
  3368. "ticket": [],
  3369. },
  3370. "access_groups": {
  3371. "admin": [],
  3372. "commit": [],
  3373. "collaborator": [],
  3374. "ticket": [],
  3375. },
  3376. "tags": [],
  3377. "priorities": {},
  3378. "custom_keys": [],
  3379. "close_status": [
  3380. "Invalid",
  3381. "Insufficient data",
  3382. "Fixed",
  3383. "Duplicate",
  3384. ],
  3385. "milestones": {},
  3386. },
  3387. "branch_from": "feature",
  3388. "repo_from": {
  3389. "id": 1,
  3390. "name": "test",
  3391. "fullname": "test",
  3392. "url_path": "test",
  3393. "full_url": "http://localhost.localdomain/test",
  3394. "description": "test project #1",
  3395. "namespace": None,
  3396. "parent": None,
  3397. "date_created": ANY,
  3398. "date_modified": ANY,
  3399. "user": {
  3400. "name": "pingou",
  3401. "fullname": "PY C",
  3402. "url_path": "user/pingou",
  3403. "full_url": "http://localhost.localdomain/user/pingou",
  3404. },
  3405. "access_users": {
  3406. "owner": ["pingou"],
  3407. "admin": [],
  3408. "commit": [],
  3409. "collaborator": [],
  3410. "ticket": [],
  3411. },
  3412. "access_groups": {
  3413. "admin": [],
  3414. "commit": [],
  3415. "collaborator": [],
  3416. "ticket": [],
  3417. },
  3418. "tags": [],
  3419. "priorities": {},
  3420. "custom_keys": [],
  3421. "close_status": [
  3422. "Invalid",
  3423. "Insufficient data",
  3424. "Fixed",
  3425. "Duplicate",
  3426. ],
  3427. "milestones": {},
  3428. },
  3429. "remote_git": None,
  3430. "date_created": ANY,
  3431. "updated_on": ANY,
  3432. "last_updated": ANY,
  3433. "closed_at": None,
  3434. "user": {
  3435. "name": "pingou",
  3436. "fullname": "PY C",
  3437. "url_path": "user/pingou",
  3438. "full_url": "http://localhost.localdomain/user/pingou",
  3439. },
  3440. "assignee": None,
  3441. "status": "Closed",
  3442. "commit_start": ANY,
  3443. "commit_stop": ANY,
  3444. "closed_by": None,
  3445. "initial_comment": None,
  3446. "cached_merge_status": "unknown",
  3447. "threshold_reached": None,
  3448. "tags": [],
  3449. "comments": [
  3450. {
  3451. "id": 1,
  3452. "commit": None,
  3453. "tree": None,
  3454. "filename": None,
  3455. "line": None,
  3456. "comment": "**Metadata Update from @pingou**:\n- Request assigned",
  3457. "parent": None,
  3458. "date_created": ANY,
  3459. "user": {
  3460. "name": "pingou",
  3461. "fullname": "PY C",
  3462. "url_path": "user/pingou",
  3463. "full_url": "http://localhost.localdomain/user/pingou",
  3464. },
  3465. "edited_on": None,
  3466. "editor": None,
  3467. "notification": True,
  3468. "reactions": {},
  3469. }
  3470. ],
  3471. },
  3472. "pullrequest": {
  3473. "id": 1,
  3474. "uid": ANY,
  3475. "full_url": "http://localhost.localdomain/test/pull-request/1",
  3476. "title": "PR from the feature branch",
  3477. "branch": "master",
  3478. "project": {
  3479. "id": 1,
  3480. "name": "test",
  3481. "fullname": "test",
  3482. "full_url": "http://localhost.localdomain/test",
  3483. "url_path": "test",
  3484. "description": "test project #1",
  3485. "namespace": None,
  3486. "parent": None,
  3487. "date_created": ANY,
  3488. "date_modified": ANY,
  3489. "user": {
  3490. "name": "pingou",
  3491. "fullname": "PY C",
  3492. "url_path": "user/pingou",
  3493. "full_url": "http://localhost.localdomain/user/pingou",
  3494. },
  3495. "access_users": {
  3496. "owner": ["pingou"],
  3497. "admin": [],
  3498. "commit": [],
  3499. "collaborator": [],
  3500. "ticket": [],
  3501. },
  3502. "access_groups": {
  3503. "admin": [],
  3504. "commit": [],
  3505. "collaborator": [],
  3506. "ticket": [],
  3507. },
  3508. "tags": [],
  3509. "priorities": {},
  3510. "custom_keys": [],
  3511. "close_status": [
  3512. "Invalid",
  3513. "Insufficient data",
  3514. "Fixed",
  3515. "Duplicate",
  3516. ],
  3517. "milestones": {},
  3518. },
  3519. "branch_from": "feature",
  3520. "repo_from": {
  3521. "id": 1,
  3522. "name": "test",
  3523. "fullname": "test",
  3524. "url_path": "test",
  3525. "full_url": "http://localhost.localdomain/test",
  3526. "description": "test project #1",
  3527. "namespace": None,
  3528. "parent": None,
  3529. "date_created": ANY,
  3530. "date_modified": ANY,
  3531. "user": {
  3532. "name": "pingou",
  3533. "fullname": "PY C",
  3534. "url_path": "user/pingou",
  3535. "full_url": "http://localhost.localdomain/user/pingou",
  3536. },
  3537. "access_users": {
  3538. "owner": ["pingou"],
  3539. "admin": [],
  3540. "commit": [],
  3541. "collaborator": [],
  3542. "ticket": [],
  3543. },
  3544. "access_groups": {
  3545. "admin": [],
  3546. "commit": [],
  3547. "collaborator": [],
  3548. "ticket": [],
  3549. },
  3550. "tags": [],
  3551. "priorities": {},
  3552. "custom_keys": [],
  3553. "close_status": [
  3554. "Invalid",
  3555. "Insufficient data",
  3556. "Fixed",
  3557. "Duplicate",
  3558. ],
  3559. "milestones": {},
  3560. },
  3561. "remote_git": None,
  3562. "date_created": ANY,
  3563. "updated_on": ANY,
  3564. "last_updated": ANY,
  3565. "closed_at": None,
  3566. "user": {
  3567. "name": "pingou",
  3568. "fullname": "PY C",
  3569. "url_path": "user/pingou",
  3570. "full_url": "http://localhost.localdomain/user/pingou",
  3571. },
  3572. "assignee": None,
  3573. "status": "Closed",
  3574. "commit_start": ANY,
  3575. "commit_stop": ANY,
  3576. "closed_by": None,
  3577. "initial_comment": None,
  3578. "cached_merge_status": "unknown",
  3579. "threshold_reached": None,
  3580. "tags": [],
  3581. "comments": [
  3582. {
  3583. "id": 1,
  3584. "commit": None,
  3585. "tree": None,
  3586. "filename": None,
  3587. "line": None,
  3588. "comment": "**Metadata Update from @pingou**:\n- Request assigned",
  3589. "parent": None,
  3590. "date_created": ANY,
  3591. "user": {
  3592. "name": "pingou",
  3593. "fullname": "PY C",
  3594. "url_path": "user/pingou",
  3595. "full_url": "http://localhost.localdomain/user/pingou",
  3596. },
  3597. "edited_on": None,
  3598. "editor": None,
  3599. "notification": True,
  3600. "reactions": {},
  3601. }
  3602. ],
  3603. },
  3604. "project": {
  3605. "id": 1,
  3606. "name": "test",
  3607. "fullname": "test",
  3608. "url_path": "test",
  3609. "full_url": "http://localhost.localdomain/test",
  3610. "description": "test project #1",
  3611. "namespace": None,
  3612. "parent": None,
  3613. "date_created": ANY,
  3614. "date_modified": ANY,
  3615. "user": {
  3616. "name": "pingou",
  3617. "fullname": "PY C",
  3618. "url_path": "user/pingou",
  3619. "full_url": "http://localhost.localdomain/user/pingou",
  3620. },
  3621. "access_users": {
  3622. "owner": ["pingou"],
  3623. "admin": [],
  3624. "commit": [],
  3625. "collaborator": [],
  3626. "ticket": [],
  3627. },
  3628. "access_groups": {
  3629. "admin": [],
  3630. "commit": [],
  3631. "collaborator": [],
  3632. "ticket": [],
  3633. },
  3634. "tags": [],
  3635. "priorities": {},
  3636. "custom_keys": [],
  3637. "close_status": [
  3638. "Invalid",
  3639. "Insufficient data",
  3640. "Fixed",
  3641. "Duplicate",
  3642. ],
  3643. "milestones": {},
  3644. },
  3645. "agent": "pingou",
  3646. },
  3647. ),
  3648. pagure_messages.PullRequestAssignedResetV1(
  3649. topic="pagure.pull-request.assigned.reset",
  3650. body={
  3651. "pullrequest": {
  3652. "id": 1,
  3653. "uid": ANY,
  3654. "full_url": "http://localhost.localdomain/test/pull-request/1",
  3655. "title": "PR from the feature branch",
  3656. "branch": "master",
  3657. "project": {
  3658. "id": 1,
  3659. "name": "test",
  3660. "fullname": "test",
  3661. "url_path": "test",
  3662. "full_url": "http://localhost.localdomain/test",
  3663. "description": "test project #1",
  3664. "namespace": None,
  3665. "parent": None,
  3666. "date_created": ANY,
  3667. "date_modified": ANY,
  3668. "user": {
  3669. "name": "pingou",
  3670. "fullname": "PY C",
  3671. "url_path": "user/pingou",
  3672. "full_url": "http://localhost.localdomain/user/pingou",
  3673. },
  3674. "access_users": {
  3675. "owner": ["pingou"],
  3676. "admin": [],
  3677. "commit": [],
  3678. "collaborator": [],
  3679. "ticket": [],
  3680. },
  3681. "access_groups": {
  3682. "admin": [],
  3683. "commit": [],
  3684. "collaborator": [],
  3685. "ticket": [],
  3686. },
  3687. "tags": [],
  3688. "priorities": {},
  3689. "custom_keys": [],
  3690. "close_status": [
  3691. "Invalid",
  3692. "Insufficient data",
  3693. "Fixed",
  3694. "Duplicate",
  3695. ],
  3696. "milestones": {},
  3697. },
  3698. "branch_from": "feature",
  3699. "repo_from": {
  3700. "id": 1,
  3701. "name": "test",
  3702. "fullname": "test",
  3703. "url_path": "test",
  3704. "full_url": "http://localhost.localdomain/test",
  3705. "description": "test project #1",
  3706. "namespace": None,
  3707. "parent": None,
  3708. "date_created": ANY,
  3709. "date_modified": ANY,
  3710. "user": {
  3711. "name": "pingou",
  3712. "fullname": "PY C",
  3713. "url_path": "user/pingou",
  3714. "full_url": "http://localhost.localdomain/user/pingou",
  3715. },
  3716. "access_users": {
  3717. "owner": ["pingou"],
  3718. "admin": [],
  3719. "commit": [],
  3720. "collaborator": [],
  3721. "ticket": [],
  3722. },
  3723. "access_groups": {
  3724. "admin": [],
  3725. "commit": [],
  3726. "collaborator": [],
  3727. "ticket": [],
  3728. },
  3729. "tags": [],
  3730. "priorities": {},
  3731. "custom_keys": [],
  3732. "close_status": [
  3733. "Invalid",
  3734. "Insufficient data",
  3735. "Fixed",
  3736. "Duplicate",
  3737. ],
  3738. "milestones": {},
  3739. },
  3740. "remote_git": None,
  3741. "date_created": ANY,
  3742. "updated_on": ANY,
  3743. "last_updated": ANY,
  3744. "closed_at": None,
  3745. "user": {
  3746. "name": "pingou",
  3747. "fullname": "PY C",
  3748. "url_path": "user/pingou",
  3749. "full_url": "http://localhost.localdomain/user/pingou",
  3750. },
  3751. "assignee": None,
  3752. "status": "Closed",
  3753. "commit_start": ANY,
  3754. "commit_stop": ANY,
  3755. "closed_by": None,
  3756. "initial_comment": None,
  3757. "cached_merge_status": "unknown",
  3758. "threshold_reached": None,
  3759. "tags": [],
  3760. "comments": [
  3761. {
  3762. "id": 1,
  3763. "commit": None,
  3764. "tree": None,
  3765. "filename": None,
  3766. "line": None,
  3767. "comment": "**Metadata Update from @pingou**:\n- Request assigned",
  3768. "parent": None,
  3769. "date_created": ANY,
  3770. "user": {
  3771. "name": "pingou",
  3772. "fullname": "PY C",
  3773. "url_path": "user/pingou",
  3774. "full_url": "http://localhost.localdomain/user/pingou",
  3775. },
  3776. "edited_on": None,
  3777. "editor": None,
  3778. "notification": True,
  3779. "reactions": {},
  3780. }
  3781. ],
  3782. },
  3783. "project": {
  3784. "id": 1,
  3785. "name": "test",
  3786. "fullname": "test",
  3787. "url_path": "test",
  3788. "full_url": "http://localhost.localdomain/test",
  3789. "description": "test project #1",
  3790. "namespace": None,
  3791. "parent": None,
  3792. "date_created": ANY,
  3793. "date_modified": ANY,
  3794. "user": {
  3795. "name": "pingou",
  3796. "fullname": "PY C",
  3797. "url_path": "user/pingou",
  3798. "full_url": "http://localhost.localdomain/user/pingou",
  3799. },
  3800. "access_users": {
  3801. "owner": ["pingou"],
  3802. "admin": [],
  3803. "commit": [],
  3804. "collaborator": [],
  3805. "ticket": [],
  3806. },
  3807. "access_groups": {
  3808. "admin": [],
  3809. "commit": [],
  3810. "collaborator": [],
  3811. "ticket": [],
  3812. },
  3813. "tags": [],
  3814. "priorities": {},
  3815. "custom_keys": [],
  3816. "close_status": [
  3817. "Invalid",
  3818. "Insufficient data",
  3819. "Fixed",
  3820. "Duplicate",
  3821. ],
  3822. "milestones": {},
  3823. },
  3824. "agent": "pingou",
  3825. },
  3826. ),
  3827. ):
  3828. output = self.app.post(
  3829. "/test/pull-request/1/update",
  3830. data=data,
  3831. follow_redirects=True,
  3832. )
  3833. self.assertEqual(output.status_code, 200)
  3834. # Project w/o pull-request
  3835. repo = pagure.lib.query.get_authorized_project(
  3836. self.session, "test"
  3837. )
  3838. settings = repo.settings
  3839. settings["pull_requests"] = False
  3840. repo.settings = settings
  3841. self.session.add(repo)
  3842. self.session.commit()
  3843. output = self.app.post(
  3844. "/test/pull-request/1/update", data=data, follow_redirects=True
  3845. )
  3846. self.assertEqual(output.status_code, 404)
  3847. @patch.dict(
  3848. "pagure.config.config", {"FEDORA_MESSAGING_NOTIFICATIONS": True}
  3849. )
  3850. @patch("pagure.lib.notify.send_email", MagicMock(return_value=True))
  3851. def test_update_pull_requests_tag(self):
  3852. """Test the update_pull_requests endpoint when tagging a PR."""
  3853. tests.create_projects(self.session)
  3854. tests.create_projects_git(
  3855. os.path.join(self.path, "requests"), bare=True
  3856. )
  3857. set_up_git_repo(
  3858. self.session, self.path, new_project=None, branch_from="feature"
  3859. )
  3860. user = tests.FakeUser()
  3861. user.username = "pingou"
  3862. with tests.user_set(self.app.application, user):
  3863. output = self.app.get("/test/pull-request/1")
  3864. self.assertEqual(output.status_code, 200)
  3865. csrf_token = self.get_csrf(output=output)
  3866. data = {"tag": "black"}
  3867. # No CSRF
  3868. output = self.app.post(
  3869. "/test/pull-request/1/update", data=data, follow_redirects=True
  3870. )
  3871. self.assertEqual(output.status_code, 200)
  3872. output_text = output.get_data(as_text=True)
  3873. self.assertIn(
  3874. "<title>PR#1: PR from the feature branch - test\n - "
  3875. "Pagure</title>",
  3876. output_text,
  3877. )
  3878. self.assertIn(
  3879. '<h4 class="ml-1">\n <div>\n '
  3880. '<span class="fa fa-fw text-success fa-arrow-circle-down pt-1"></span>\n '
  3881. '<span class="text-success '
  3882. 'font-weight-bold">#1</span>\n '
  3883. '<span class="font-weight-bold">\n '
  3884. "PR from the feature branch\n",
  3885. output_text,
  3886. )
  3887. self.assertNotIn("Request assigned", output_text)
  3888. # Tag the PR
  3889. data = {"csrf_token": csrf_token, "tag": "black"}
  3890. with testing.mock_sends(
  3891. pagure_messages.PullRequestTagAddedV1(
  3892. topic="pagure.pull-request.tag.added",
  3893. body={
  3894. # This is field is for backward compatibility but we
  3895. # don't want to check it
  3896. "pull_request": ANY,
  3897. "pullrequest": {
  3898. "id": 1,
  3899. "uid": ANY,
  3900. "full_url": "http://localhost.localdomain/test/pull-request/1",
  3901. "title": "PR from the feature branch",
  3902. "branch": "master",
  3903. "project": {
  3904. "id": 1,
  3905. "name": "test",
  3906. "fullname": "test",
  3907. "url_path": "test",
  3908. "full_url": "http://localhost.localdomain/test",
  3909. "description": "test project #1",
  3910. "namespace": None,
  3911. "parent": None,
  3912. "date_created": ANY,
  3913. "date_modified": ANY,
  3914. "user": {
  3915. "name": "pingou",
  3916. "fullname": "PY C",
  3917. "url_path": "user/pingou",
  3918. "full_url": "http://localhost.localdomain/user/pingou",
  3919. },
  3920. "access_users": {
  3921. "owner": ["pingou"],
  3922. "admin": [],
  3923. "commit": [],
  3924. "collaborator": [],
  3925. "ticket": [],
  3926. },
  3927. "access_groups": {
  3928. "admin": [],
  3929. "commit": [],
  3930. "collaborator": [],
  3931. "ticket": [],
  3932. },
  3933. "tags": [],
  3934. "priorities": {},
  3935. "custom_keys": [],
  3936. "close_status": [
  3937. "Invalid",
  3938. "Insufficient data",
  3939. "Fixed",
  3940. "Duplicate",
  3941. ],
  3942. "milestones": {},
  3943. },
  3944. "branch_from": "feature",
  3945. "repo_from": {
  3946. "id": 1,
  3947. "name": "test",
  3948. "fullname": "test",
  3949. "url_path": "test",
  3950. "full_url": "http://localhost.localdomain/test",
  3951. "description": "test project #1",
  3952. "namespace": None,
  3953. "parent": None,
  3954. "date_created": ANY,
  3955. "date_modified": ANY,
  3956. "user": {
  3957. "name": "pingou",
  3958. "fullname": "PY C",
  3959. "url_path": "user/pingou",
  3960. "full_url": "http://localhost.localdomain/user/pingou",
  3961. },
  3962. "access_users": {
  3963. "owner": ["pingou"],
  3964. "admin": [],
  3965. "commit": [],
  3966. "collaborator": [],
  3967. "ticket": [],
  3968. },
  3969. "access_groups": {
  3970. "admin": [],
  3971. "commit": [],
  3972. "collaborator": [],
  3973. "ticket": [],
  3974. },
  3975. "tags": [],
  3976. "priorities": {},
  3977. "custom_keys": [],
  3978. "close_status": [
  3979. "Invalid",
  3980. "Insufficient data",
  3981. "Fixed",
  3982. "Duplicate",
  3983. ],
  3984. "milestones": {},
  3985. },
  3986. "remote_git": None,
  3987. "date_created": ANY,
  3988. "updated_on": ANY,
  3989. "last_updated": ANY,
  3990. "closed_at": None,
  3991. "user": {
  3992. "name": "pingou",
  3993. "fullname": "PY C",
  3994. "url_path": "user/pingou",
  3995. "full_url": "http://localhost.localdomain/user/pingou",
  3996. },
  3997. "assignee": None,
  3998. "status": "Open",
  3999. "commit_start": ANY,
  4000. "commit_stop": ANY,
  4001. "closed_by": None,
  4002. "initial_comment": None,
  4003. "cached_merge_status": "unknown",
  4004. "threshold_reached": None,
  4005. "tags": ["black"],
  4006. "comments": [],
  4007. },
  4008. "project": {
  4009. "id": 1,
  4010. "name": "test",
  4011. "fullname": "test",
  4012. "url_path": "test",
  4013. "full_url": "http://localhost.localdomain/test",
  4014. "description": "test project #1",
  4015. "namespace": None,
  4016. "parent": None,
  4017. "date_created": ANY,
  4018. "date_modified": ANY,
  4019. "user": {
  4020. "name": "pingou",
  4021. "fullname": "PY C",
  4022. "url_path": "user/pingou",
  4023. "full_url": "http://localhost.localdomain/user/pingou",
  4024. },
  4025. "access_users": {
  4026. "owner": ["pingou"],
  4027. "admin": [],
  4028. "commit": [],
  4029. "collaborator": [],
  4030. "ticket": [],
  4031. },
  4032. "access_groups": {
  4033. "admin": [],
  4034. "commit": [],
  4035. "collaborator": [],
  4036. "ticket": [],
  4037. },
  4038. "tags": [],
  4039. "priorities": {},
  4040. "custom_keys": [],
  4041. "close_status": [
  4042. "Invalid",
  4043. "Insufficient data",
  4044. "Fixed",
  4045. "Duplicate",
  4046. ],
  4047. "milestones": {},
  4048. },
  4049. "tags": ["black"],
  4050. "agent": "pingou",
  4051. },
  4052. )
  4053. ):
  4054. output = self.app.post(
  4055. "/test/pull-request/1/update",
  4056. data=data,
  4057. follow_redirects=True,
  4058. )
  4059. self.assertEqual(output.status_code, 200)
  4060. output_text = output.get_data(as_text=True)
  4061. self.assertIn(
  4062. "<title>PR#1: PR from the feature branch - test\n - "
  4063. "Pagure</title>",
  4064. output_text,
  4065. )
  4066. self.assertIn(
  4067. '<h4 class="ml-1">\n <div>\n '
  4068. '<span class="fa fa-fw text-success fa-arrow-circle-down pt-1"></span>\n '
  4069. '<span class="text-success '
  4070. 'font-weight-bold">#1</span>\n '
  4071. '<span class="font-weight-bold">\n '
  4072. "PR from the feature branch\n",
  4073. output_text,
  4074. )
  4075. self.assertIn("Pull-request tagged with: black", output_text)
  4076. self.assertIn(
  4077. 'title="comma separated list of tags"\n '
  4078. 'value="black" />',
  4079. output_text,
  4080. )
  4081. # Try as another user
  4082. user.username = "foo"
  4083. with tests.user_set(self.app.application, user):
  4084. # Tag the PR
  4085. data = {"csrf_token": csrf_token, "tag": "blue, yellow"}
  4086. output = self.app.post(
  4087. "/test/pull-request/1/update", data=data, follow_redirects=True
  4088. )
  4089. self.assertEqual(output.status_code, 403)
  4090. # Make the PR be from foo
  4091. repo = pagure.lib.query.get_authorized_project(
  4092. self.session, "test"
  4093. )
  4094. req = repo.requests[0]
  4095. req.user_id = 2
  4096. self.session.add(req)
  4097. self.session.commit()
  4098. # Re-try to tag the PR
  4099. data = {"csrf_token": csrf_token, "tag": "blue, yellow"}
  4100. with testing.mock_sends(
  4101. pagure_messages.PullRequestTagAddedV1(
  4102. topic="pagure.pull-request.tag.added",
  4103. body={
  4104. "pull_request": ANY,
  4105. "pullrequest": {
  4106. "id": 1,
  4107. "uid": ANY,
  4108. "full_url": "http://localhost.localdomain/test/pull-request/1",
  4109. "title": "PR from the feature branch",
  4110. "branch": "master",
  4111. "project": {
  4112. "id": 1,
  4113. "name": "test",
  4114. "fullname": "test",
  4115. "url_path": "test",
  4116. "full_url": "http://localhost.localdomain/test",
  4117. "description": "test project #1",
  4118. "namespace": None,
  4119. "parent": None,
  4120. "date_created": ANY,
  4121. "date_modified": ANY,
  4122. "user": {
  4123. "name": "pingou",
  4124. "fullname": "PY C",
  4125. "url_path": "user/pingou",
  4126. "full_url": "http://localhost.localdomain/user/pingou",
  4127. },
  4128. "access_users": {
  4129. "owner": ["pingou"],
  4130. "admin": [],
  4131. "commit": [],
  4132. "collaborator": [],
  4133. "ticket": [],
  4134. },
  4135. "access_groups": {
  4136. "admin": [],
  4137. "commit": [],
  4138. "collaborator": [],
  4139. "ticket": [],
  4140. },
  4141. "tags": [],
  4142. "priorities": {},
  4143. "custom_keys": [],
  4144. "close_status": [
  4145. "Invalid",
  4146. "Insufficient data",
  4147. "Fixed",
  4148. "Duplicate",
  4149. ],
  4150. "milestones": {},
  4151. },
  4152. "branch_from": "feature",
  4153. "repo_from": {
  4154. "id": 1,
  4155. "name": "test",
  4156. "fullname": "test",
  4157. "url_path": "test",
  4158. "full_url": "http://localhost.localdomain/test",
  4159. "description": "test project #1",
  4160. "namespace": None,
  4161. "parent": None,
  4162. "date_created": ANY,
  4163. "date_modified": ANY,
  4164. "user": {
  4165. "name": "pingou",
  4166. "fullname": "PY C",
  4167. "url_path": "user/pingou",
  4168. "full_url": "http://localhost.localdomain/user/pingou",
  4169. },
  4170. "access_users": {
  4171. "owner": ["pingou"],
  4172. "admin": [],
  4173. "commit": [],
  4174. "collaborator": [],
  4175. "ticket": [],
  4176. },
  4177. "access_groups": {
  4178. "admin": [],
  4179. "commit": [],
  4180. "collaborator": [],
  4181. "ticket": [],
  4182. },
  4183. "tags": [],
  4184. "priorities": {},
  4185. "custom_keys": [],
  4186. "close_status": [
  4187. "Invalid",
  4188. "Insufficient data",
  4189. "Fixed",
  4190. "Duplicate",
  4191. ],
  4192. "milestones": {},
  4193. },
  4194. "remote_git": None,
  4195. "date_created": ANY,
  4196. "updated_on": ANY,
  4197. "last_updated": ANY,
  4198. "closed_at": None,
  4199. "user": {
  4200. "name": "foo",
  4201. "fullname": "foo bar",
  4202. "url_path": "user/foo",
  4203. "full_url": "http://localhost.localdomain/user/foo",
  4204. },
  4205. "assignee": None,
  4206. "status": "Open",
  4207. "commit_start": ANY,
  4208. "commit_stop": ANY,
  4209. "closed_by": None,
  4210. "initial_comment": None,
  4211. "cached_merge_status": "unknown",
  4212. "threshold_reached": None,
  4213. "tags": ["black", "blue", "yellow"],
  4214. "comments": [
  4215. {
  4216. "id": 1,
  4217. "commit": None,
  4218. "tree": None,
  4219. "filename": None,
  4220. "line": None,
  4221. "comment": "**Metadata Update from "
  4222. "@pingou**:\n- Pull-request tagged "
  4223. "with: black",
  4224. "parent": None,
  4225. "date_created": ANY,
  4226. "user": {
  4227. "name": "pingou",
  4228. "fullname": "PY C",
  4229. "url_path": "user/pingou",
  4230. "full_url": "http://localhost.localdomain/user/pingou",
  4231. },
  4232. "edited_on": None,
  4233. "editor": None,
  4234. "notification": True,
  4235. "reactions": {},
  4236. }
  4237. ],
  4238. },
  4239. "project": {
  4240. "id": 1,
  4241. "name": "test",
  4242. "fullname": "test",
  4243. "url_path": "test",
  4244. "full_url": "http://localhost.localdomain/test",
  4245. "description": "test project #1",
  4246. "namespace": None,
  4247. "parent": None,
  4248. "date_created": ANY,
  4249. "date_modified": ANY,
  4250. "user": {
  4251. "name": "pingou",
  4252. "fullname": "PY C",
  4253. "url_path": "user/pingou",
  4254. "full_url": "http://localhost.localdomain/user/pingou",
  4255. },
  4256. "access_users": {
  4257. "owner": ["pingou"],
  4258. "admin": [],
  4259. "commit": [],
  4260. "collaborator": [],
  4261. "ticket": [],
  4262. },
  4263. "access_groups": {
  4264. "admin": [],
  4265. "commit": [],
  4266. "collaborator": [],
  4267. "ticket": [],
  4268. },
  4269. "tags": [],
  4270. "priorities": {},
  4271. "custom_keys": [],
  4272. "close_status": [
  4273. "Invalid",
  4274. "Insufficient data",
  4275. "Fixed",
  4276. "Duplicate",
  4277. ],
  4278. "milestones": {},
  4279. },
  4280. "tags": ["blue", "yellow"],
  4281. "agent": "foo",
  4282. },
  4283. ),
  4284. pagure_messages.PullRequestTagRemovedV1(
  4285. topic="pagure.pull-request.tag.removed",
  4286. body={
  4287. # This is field is for backward compatibility but we
  4288. # don't want to check it
  4289. "pull_request": ANY,
  4290. "pullrequest": {
  4291. "id": 1,
  4292. "uid": ANY,
  4293. "full_url": "http://localhost.localdomain/test/pull-request/1",
  4294. "title": "PR from the feature branch",
  4295. "branch": "master",
  4296. "project": {
  4297. "id": 1,
  4298. "name": "test",
  4299. "fullname": "test",
  4300. "url_path": "test",
  4301. "full_url": "http://localhost.localdomain/test",
  4302. "description": "test project #1",
  4303. "namespace": None,
  4304. "parent": None,
  4305. "date_created": ANY,
  4306. "date_modified": ANY,
  4307. "user": {
  4308. "name": "pingou",
  4309. "fullname": "PY C",
  4310. "url_path": "user/pingou",
  4311. "full_url": "http://localhost.localdomain/user/pingou",
  4312. },
  4313. "access_users": {
  4314. "owner": ["pingou"],
  4315. "admin": [],
  4316. "commit": [],
  4317. "collaborator": [],
  4318. "ticket": [],
  4319. },
  4320. "access_groups": {
  4321. "admin": [],
  4322. "commit": [],
  4323. "collaborator": [],
  4324. "ticket": [],
  4325. },
  4326. "tags": [],
  4327. "priorities": {},
  4328. "custom_keys": [],
  4329. "close_status": [
  4330. "Invalid",
  4331. "Insufficient data",
  4332. "Fixed",
  4333. "Duplicate",
  4334. ],
  4335. "milestones": {},
  4336. },
  4337. "branch_from": "feature",
  4338. "repo_from": {
  4339. "id": 1,
  4340. "name": "test",
  4341. "fullname": "test",
  4342. "url_path": "test",
  4343. "full_url": "http://localhost.localdomain/test",
  4344. "description": "test project #1",
  4345. "namespace": None,
  4346. "parent": None,
  4347. "date_created": ANY,
  4348. "date_modified": ANY,
  4349. "user": {
  4350. "name": "pingou",
  4351. "fullname": "PY C",
  4352. "url_path": "user/pingou",
  4353. "full_url": "http://localhost.localdomain/user/pingou",
  4354. },
  4355. "access_users": {
  4356. "owner": ["pingou"],
  4357. "admin": [],
  4358. "commit": [],
  4359. "collaborator": [],
  4360. "ticket": [],
  4361. },
  4362. "access_groups": {
  4363. "admin": [],
  4364. "commit": [],
  4365. "collaborator": [],
  4366. "ticket": [],
  4367. },
  4368. "tags": [],
  4369. "priorities": {},
  4370. "custom_keys": [],
  4371. "close_status": [
  4372. "Invalid",
  4373. "Insufficient data",
  4374. "Fixed",
  4375. "Duplicate",
  4376. ],
  4377. "milestones": {},
  4378. },
  4379. "remote_git": None,
  4380. "date_created": ANY,
  4381. "updated_on": ANY,
  4382. "last_updated": ANY,
  4383. "closed_at": None,
  4384. "user": {
  4385. "name": "foo",
  4386. "fullname": "foo bar",
  4387. "url_path": "user/foo",
  4388. "full_url": "http://localhost.localdomain/user/foo",
  4389. },
  4390. "assignee": None,
  4391. "status": "Open",
  4392. "commit_start": ANY,
  4393. "commit_stop": ANY,
  4394. "closed_by": None,
  4395. "initial_comment": None,
  4396. "cached_merge_status": "unknown",
  4397. "threshold_reached": None,
  4398. "tags": ["blue", "yellow"],
  4399. "comments": [
  4400. {
  4401. "id": 1,
  4402. "commit": None,
  4403. "tree": None,
  4404. "filename": None,
  4405. "line": None,
  4406. "comment": "**Metadata Update from "
  4407. "@pingou**:\n- Pull-request tagged "
  4408. "with: black",
  4409. "parent": None,
  4410. "date_created": ANY,
  4411. "user": {
  4412. "name": "pingou",
  4413. "fullname": "PY C",
  4414. "url_path": "user/pingou",
  4415. "full_url": "http://localhost.localdomain/user/pingou",
  4416. },
  4417. "edited_on": None,
  4418. "editor": None,
  4419. "notification": True,
  4420. "reactions": {},
  4421. }
  4422. ],
  4423. },
  4424. "project": {
  4425. "id": 1,
  4426. "name": "test",
  4427. "fullname": "test",
  4428. "full_url": "http://localhost.localdomain/test",
  4429. "url_path": "test",
  4430. "description": "test project #1",
  4431. "namespace": None,
  4432. "parent": None,
  4433. "date_created": ANY,
  4434. "date_modified": ANY,
  4435. "user": {
  4436. "name": "pingou",
  4437. "fullname": "PY C",
  4438. "url_path": "user/pingou",
  4439. "full_url": "http://localhost.localdomain/user/pingou",
  4440. },
  4441. "access_users": {
  4442. "owner": ["pingou"],
  4443. "admin": [],
  4444. "commit": [],
  4445. "collaborator": [],
  4446. "ticket": [],
  4447. },
  4448. "access_groups": {
  4449. "admin": [],
  4450. "commit": [],
  4451. "collaborator": [],
  4452. "ticket": [],
  4453. },
  4454. "tags": [],
  4455. "priorities": {},
  4456. "custom_keys": [],
  4457. "close_status": [
  4458. "Invalid",
  4459. "Insufficient data",
  4460. "Fixed",
  4461. "Duplicate",
  4462. ],
  4463. "milestones": {},
  4464. },
  4465. "tags": ["black"],
  4466. "agent": "foo",
  4467. },
  4468. ),
  4469. ):
  4470. output = self.app.post(
  4471. "/test/pull-request/1/update",
  4472. data=data,
  4473. follow_redirects=True,
  4474. )
  4475. self.assertEqual(output.status_code, 200)
  4476. soup = BeautifulSoup(output.get_data(as_text=True), "html.parser")
  4477. self.assertEqual(
  4478. soup.find("title").string,
  4479. "PR#1: PR from the feature branch - test\n - Pagure",
  4480. )
  4481. self.assertIn(
  4482. "Pull-request **un**tagged with: black",
  4483. output.get_data(as_text=True),
  4484. )
  4485. self.assertIn(
  4486. "Pull-request tagged with: blue, yellow",
  4487. output.get_data(as_text=True),
  4488. )
  4489. user.username = "pingou"
  4490. with tests.user_set(self.app.application, user):
  4491. # Pull-Request closed
  4492. repo = pagure.lib.query.get_authorized_project(
  4493. self.session, "test"
  4494. )
  4495. req = repo.requests[0]
  4496. req.status = "Closed"
  4497. req.closed_by_in = 1
  4498. self.session.add(req)
  4499. self.session.commit()
  4500. output = self.app.post(
  4501. "/test/pull-request/1/update", data=data, follow_redirects=True
  4502. )
  4503. self.assertEqual(output.status_code, 200)
  4504. # Project w/o pull-request
  4505. repo = pagure.lib.query.get_authorized_project(
  4506. self.session, "test"
  4507. )
  4508. settings = repo.settings
  4509. settings["pull_requests"] = False
  4510. repo.settings = settings
  4511. self.session.add(repo)
  4512. self.session.commit()
  4513. output = self.app.post(
  4514. "/test/pull-request/1/update", data=data, follow_redirects=True
  4515. )
  4516. self.assertEqual(output.status_code, 404)
  4517. @patch.dict(
  4518. "pagure.config.config", {"FEDORA_MESSAGING_NOTIFICATIONS": True}
  4519. )
  4520. @patch("pagure.lib.notify.send_email")
  4521. def test_fork_project(self, send_email):
  4522. """ Test the fork_project endpoint. """
  4523. send_email.return_value = True
  4524. tests.create_projects(self.session)
  4525. for folder in ["docs", "tickets", "requests", "repos"]:
  4526. tests.create_projects_git(
  4527. os.path.join(self.path, folder), bare=True
  4528. )
  4529. user = tests.FakeUser()
  4530. user.username = "pingou"
  4531. with tests.user_set(self.app.application, user):
  4532. output = self.app.post("/do_fork/test")
  4533. self.assertEqual(output.status_code, 400)
  4534. output = self.app.get("/new/")
  4535. self.assertEqual(output.status_code, 200)
  4536. self.assertIn(
  4537. "<strong>Create new Project</strong>",
  4538. output.get_data(as_text=True),
  4539. )
  4540. csrf_token = self.get_csrf(output=output)
  4541. data = {"csrf_token": csrf_token}
  4542. output = self.app.post(
  4543. "/do_fork/foo", data=data, follow_redirects=True
  4544. )
  4545. self.assertEqual(output.status_code, 404)
  4546. user.username = "foo"
  4547. with tests.user_set(self.app.application, user):
  4548. output = self.app.post("/do_fork/test")
  4549. self.assertEqual(output.status_code, 400)
  4550. data = {"csrf_token": csrf_token}
  4551. with testing.mock_sends(
  4552. pagure_messages.ProjectForkedV1(
  4553. topic="pagure.project.forked",
  4554. body={
  4555. "project": {
  4556. "id": 4,
  4557. "name": "test",
  4558. "fullname": "forks/foo/test",
  4559. "url_path": "fork/foo/test",
  4560. "full_url": "http://localhost.localdomain/fork/foo/test",
  4561. "description": "test project #1",
  4562. "namespace": None,
  4563. "parent": {
  4564. "id": 1,
  4565. "name": "test",
  4566. "fullname": "test",
  4567. "url_path": "test",
  4568. "full_url": "http://localhost.localdomain/test",
  4569. "description": "test project #1",
  4570. "namespace": None,
  4571. "parent": None,
  4572. "date_created": ANY,
  4573. "date_modified": ANY,
  4574. "user": {
  4575. "name": "pingou",
  4576. "fullname": "PY C",
  4577. "url_path": "user/pingou",
  4578. "full_url": "http://localhost.localdomain/user/pingou",
  4579. },
  4580. "access_users": {
  4581. "owner": ["pingou"],
  4582. "admin": [],
  4583. "commit": [],
  4584. "collaborator": [],
  4585. "ticket": [],
  4586. },
  4587. "access_groups": {
  4588. "admin": [],
  4589. "commit": [],
  4590. "collaborator": [],
  4591. "ticket": [],
  4592. },
  4593. "tags": [],
  4594. "priorities": {},
  4595. "custom_keys": [],
  4596. "close_status": [
  4597. "Invalid",
  4598. "Insufficient data",
  4599. "Fixed",
  4600. "Duplicate",
  4601. ],
  4602. "milestones": {},
  4603. },
  4604. "date_created": ANY,
  4605. "date_modified": ANY,
  4606. "user": {
  4607. "name": "foo",
  4608. "fullname": "foo bar",
  4609. "url_path": "user/foo",
  4610. "full_url": "http://localhost.localdomain/user/foo",
  4611. },
  4612. "access_users": {
  4613. "owner": ["foo"],
  4614. "admin": [],
  4615. "commit": [],
  4616. "collaborator": [],
  4617. "ticket": [],
  4618. },
  4619. "access_groups": {
  4620. "admin": [],
  4621. "commit": [],
  4622. "collaborator": [],
  4623. "ticket": [],
  4624. },
  4625. "tags": [],
  4626. "priorities": {},
  4627. "custom_keys": [],
  4628. "close_status": [],
  4629. "milestones": {},
  4630. },
  4631. "agent": "foo",
  4632. },
  4633. )
  4634. ):
  4635. output = self.app.post(
  4636. "/do_fork/test", data=data, follow_redirects=True
  4637. )
  4638. self.assertEqual(output.status_code, 200)
  4639. @patch("pagure.lib.notify.send_email", MagicMock(return_value=True))
  4640. def test_fork_project_non_master_default(self):
  4641. """Test the fork_project endpoint with a project whose default branch
  4642. is not master."""
  4643. tests.create_projects(self.session)
  4644. for folder in ["docs", "tickets", "requests", "repos"]:
  4645. tests.create_projects_git(
  4646. os.path.join(self.path, folder), bare=True
  4647. )
  4648. path = os.path.join(self.path, "repos", "test.git")
  4649. tests.add_content_git_repo(path)
  4650. project = pagure.lib.query.get_authorized_project(self.session, "test")
  4651. # Check before that the master branch is the default one - shown in the
  4652. # default page
  4653. output = self.app.get("/test")
  4654. self.assertEqual(output.status_code, 200)
  4655. output_text = output.get_data(as_text=True)
  4656. self.assertIn(
  4657. '<code class="py-1 px-2 font-weight-bold commit_branch">master</code><code',
  4658. output_text,
  4659. )
  4660. # Create the main branch with some content and make it the default branch
  4661. repo = pygit2.Repository(path)
  4662. branchname = "main"
  4663. repo.create_branch(branchname, repo.head.peel())
  4664. pagure.lib.git.git_set_ref_head(project=project, branch=branchname)
  4665. user = tests.FakeUser(username="foo")
  4666. with tests.user_set(self.app.application, user):
  4667. data = {"csrf_token": self.get_csrf()}
  4668. output = self.app.post(
  4669. "/do_fork/test", data=data, follow_redirects=True
  4670. )
  4671. self.assertEqual(output.status_code, 200)
  4672. output_text = output.get_data(as_text=True)
  4673. self.assertIn(
  4674. '<code class="py-1 px-2 font-weight-bold commit_branch">main</code><code',
  4675. output_text,
  4676. )
  4677. output = self.app.get("/fork/foo/test")
  4678. self.assertEqual(output.status_code, 200)
  4679. output_text = output.get_data(as_text=True)
  4680. self.assertIn(
  4681. '<code class="py-1 px-2 font-weight-bold commit_branch">main</code><code',
  4682. output_text,
  4683. )
  4684. @patch("pagure.lib.notify.send_email")
  4685. def test_new_request_pull_branch_space(self, send_email):
  4686. """ Test the new_request_pull endpoint. """
  4687. send_email.return_value = True
  4688. self.test_fork_project()
  4689. tests.create_projects_git(
  4690. os.path.join(self.path, "requests"), bare=True
  4691. )
  4692. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  4693. fork = pagure.lib.query.get_authorized_project(
  4694. self.session, "test", user="foo"
  4695. )
  4696. set_up_git_repo(
  4697. self.session,
  4698. self.path,
  4699. new_project=fork,
  4700. branch_from="feature",
  4701. mtype="FF",
  4702. )
  4703. user = tests.FakeUser(username="pingou")
  4704. with tests.user_set(self.app.application, user):
  4705. output = self.app.get("/test/diff/master..foo bar")
  4706. self.assertEqual(output.status_code, 400)
  4707. output_text = output.get_data(as_text=True)
  4708. self.assertIn("<p>Branch foo bar does not exist</p>", output_text)
  4709. @patch.dict(
  4710. "pagure.config.config", {"FEDORA_MESSAGING_NOTIFICATIONS": True}
  4711. )
  4712. @patch("pagure.lib.notify.send_email")
  4713. def test_new_request_pull(self, send_email):
  4714. """ Test the new_request_pull endpoint. """
  4715. send_email.return_value = True
  4716. self.test_fork_project()
  4717. tests.create_projects_git(
  4718. os.path.join(self.path, "requests"), bare=True
  4719. )
  4720. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  4721. fork = pagure.lib.query.get_authorized_project(
  4722. self.session, "test", user="foo"
  4723. )
  4724. set_up_git_repo(
  4725. self.session,
  4726. self.path,
  4727. new_project=fork,
  4728. branch_from="feature",
  4729. mtype="FF",
  4730. )
  4731. user = tests.FakeUser()
  4732. user.username = "foo"
  4733. with tests.user_set(self.app.application, user):
  4734. output = self.app.get("/foo/diff/master..feature")
  4735. self.assertEqual(output.status_code, 404)
  4736. output = self.app.get("/test/diff/master..foo")
  4737. self.assertEqual(output.status_code, 400)
  4738. output = self.app.get("/test/diff/foo..master")
  4739. self.assertEqual(output.status_code, 400)
  4740. output = self.app.get("/test/diff/feature..master")
  4741. self.assertEqual(output.status_code, 200)
  4742. output_text = output.get_data(as_text=True)
  4743. self.assertIn(
  4744. "<title>Diff from master to feature - test\n - "
  4745. "Pagure</title>",
  4746. output_text,
  4747. )
  4748. self.assertIn(
  4749. '<p class="error"> No commits found </p>', output_text
  4750. )
  4751. output = self.app.get("/test/diff/master..feature")
  4752. self.assertEqual(output.status_code, 200)
  4753. output_text = output.get_data(as_text=True)
  4754. self.assertIn(
  4755. "<title>Diff from feature to master - test\n - "
  4756. "Pagure</title>",
  4757. output_text,
  4758. )
  4759. self.assertNotIn(
  4760. '<input type="submit" class="submit positive button" '
  4761. 'value="Create">',
  4762. output_text,
  4763. )
  4764. user.username = "pingou"
  4765. with tests.user_set(self.app.application, user):
  4766. output = self.app.get("/test/diff/master..feature")
  4767. self.assertEqual(output.status_code, 200)
  4768. output_text = output.get_data(as_text=True)
  4769. self.assertIn(
  4770. "<title>Create new Pull Request for master - test\n - "
  4771. "Pagure</title>",
  4772. output_text,
  4773. )
  4774. self.assertIn(
  4775. '<input type="submit" class="btn btn-primary" value="Create Pull Request">\n',
  4776. output_text,
  4777. )
  4778. # Check that we prefilled the input fields as expected:
  4779. self.assertIn(
  4780. '<input class="form-control" id="title" name="title" '
  4781. 'placeholder="Pull Request Title" required="required" '
  4782. 'type="text" value="A commit on branch feature">',
  4783. output_text,
  4784. )
  4785. self.assertIn(
  4786. """<textarea class="form-control" rows=8 id="initial_comment" name="initial_comment"
  4787. placeholder="Describe your changes" tabindex=1>
  4788. More information</textarea>
  4789. <div id="preview" class="p-1">""",
  4790. output_text,
  4791. )
  4792. self.assertIn(
  4793. '<a class="dropdown-item branch_from_item pointer" '
  4794. 'data-value="master"><span class="fa fa-random">'
  4795. "</span> master</a>",
  4796. output_text,
  4797. )
  4798. csrf_token = self.get_csrf(output=output)
  4799. # Case 1 - Add an initial comment
  4800. data = {
  4801. "csrf_token": csrf_token,
  4802. "title": "foo bar PR",
  4803. "initial_comment": "Test Initial Comment",
  4804. }
  4805. with testing.mock_sends(
  4806. pagure_messages.PullRequestNewV1(
  4807. topic="pagure.pull-request.new",
  4808. body={
  4809. "pullrequest": {
  4810. "id": 2,
  4811. "full_url": "http://localhost.localdomain/test/pull-request/2",
  4812. "uid": ANY,
  4813. "title": "foo bar PR",
  4814. "branch": "master",
  4815. "project": {
  4816. "id": 1,
  4817. "name": "test",
  4818. "fullname": "test",
  4819. "url_path": "test",
  4820. "full_url": "http://localhost.localdomain/test",
  4821. "description": "test project #1",
  4822. "namespace": None,
  4823. "parent": None,
  4824. "date_created": ANY,
  4825. "date_modified": ANY,
  4826. "user": {
  4827. "name": "pingou",
  4828. "fullname": "PY C",
  4829. "url_path": "user/pingou",
  4830. "full_url": "http://localhost.localdomain/user/pingou",
  4831. },
  4832. "access_users": {
  4833. "owner": ["pingou"],
  4834. "admin": [],
  4835. "commit": [],
  4836. "collaborator": [],
  4837. "ticket": [],
  4838. },
  4839. "access_groups": {
  4840. "admin": [],
  4841. "commit": [],
  4842. "collaborator": [],
  4843. "ticket": [],
  4844. },
  4845. "tags": [],
  4846. "priorities": {},
  4847. "custom_keys": [],
  4848. "close_status": [
  4849. "Invalid",
  4850. "Insufficient data",
  4851. "Fixed",
  4852. "Duplicate",
  4853. ],
  4854. "milestones": {},
  4855. },
  4856. "branch_from": "feature",
  4857. "repo_from": {
  4858. "id": 1,
  4859. "name": "test",
  4860. "fullname": "test",
  4861. "full_url": "http://localhost.localdomain/test",
  4862. "url_path": "test",
  4863. "description": "test project #1",
  4864. "namespace": None,
  4865. "parent": None,
  4866. "date_created": ANY,
  4867. "date_modified": ANY,
  4868. "user": {
  4869. "name": "pingou",
  4870. "fullname": "PY C",
  4871. "url_path": "user/pingou",
  4872. "full_url": "http://localhost.localdomain/user/pingou",
  4873. },
  4874. "access_users": {
  4875. "owner": ["pingou"],
  4876. "admin": [],
  4877. "commit": [],
  4878. "collaborator": [],
  4879. "ticket": [],
  4880. },
  4881. "access_groups": {
  4882. "admin": [],
  4883. "commit": [],
  4884. "collaborator": [],
  4885. "ticket": [],
  4886. },
  4887. "tags": [],
  4888. "priorities": {},
  4889. "custom_keys": [],
  4890. "close_status": [
  4891. "Invalid",
  4892. "Insufficient data",
  4893. "Fixed",
  4894. "Duplicate",
  4895. ],
  4896. "milestones": {},
  4897. },
  4898. "remote_git": None,
  4899. "date_created": ANY,
  4900. "updated_on": ANY,
  4901. "last_updated": ANY,
  4902. "closed_at": None,
  4903. "user": {
  4904. "name": "pingou",
  4905. "fullname": "PY C",
  4906. "url_path": "user/pingou",
  4907. "full_url": "http://localhost.localdomain/user/pingou",
  4908. },
  4909. "assignee": None,
  4910. "status": "Open",
  4911. "commit_start": ANY,
  4912. "commit_stop": ANY,
  4913. "closed_by": None,
  4914. "initial_comment": "Test Initial Comment",
  4915. "cached_merge_status": "unknown",
  4916. "threshold_reached": None,
  4917. "tags": [],
  4918. "comments": [],
  4919. },
  4920. "agent": "pingou",
  4921. },
  4922. )
  4923. ):
  4924. output = self.app.post(
  4925. "/test/diff/master..feature",
  4926. data=data,
  4927. follow_redirects=True,
  4928. )
  4929. self.assertEqual(output.status_code, 200)
  4930. output_text = output.get_data(as_text=True)
  4931. self.assertIn(
  4932. "<title>PR#2: foo bar PR - test\n - Pagure</title>",
  4933. output_text,
  4934. )
  4935. self.assertIn("<p>Test Initial Comment</p>", output_text)
  4936. self.assertEqual(output_text.count('title="PY C (pingou)"'), 2)
  4937. # Test if the `open changed file icon` is displayed.
  4938. self.assertIn(
  4939. 'class="open_changed_file_icon_wrap"><span '
  4940. 'class="fa fa-file-code-o fa-fw" '
  4941. 'alt="Open changed file" title="Open changed file"></span>'
  4942. "</a>",
  4943. output_text,
  4944. )
  4945. # Case 2 - Add an empty initial comment
  4946. data = {
  4947. "csrf_token": csrf_token,
  4948. "title": "foo bar PR",
  4949. "initial_comment": "",
  4950. }
  4951. output = self.app.post(
  4952. "/test/diff/master..feature", data=data, follow_redirects=True
  4953. )
  4954. self.assertEqual(output.status_code, 200)
  4955. output_text = output.get_data(as_text=True)
  4956. self.assertIn(
  4957. "<title>PR#3: foo bar PR - test\n - Pagure</title>",
  4958. output_text,
  4959. )
  4960. self.assertNotIn('<div id="comment-', output_text)
  4961. @patch("pagure.lib.notify.send_email")
  4962. def test_new_request_pull_filename_unicode(self, send_email):
  4963. """ Test the new_request_pull endpoint. """
  4964. send_email.return_value = True
  4965. # Create the main project in the DB
  4966. item = pagure.lib.model.Project(
  4967. user_id=1, # pingou
  4968. name="test",
  4969. description="test project #1",
  4970. hook_token="aaabbbccc",
  4971. )
  4972. item.close_status = [
  4973. "Invalid",
  4974. "Insufficient data",
  4975. "Fixed",
  4976. "Duplicate",
  4977. ]
  4978. self.session.add(item)
  4979. self.session.commit()
  4980. # Create the fork
  4981. item = pagure.lib.model.Project(
  4982. user_id=1, # pingou
  4983. name="test",
  4984. description="test project #1",
  4985. hook_token="aaabbbcccdd",
  4986. parent_id=1,
  4987. is_fork=True,
  4988. )
  4989. item.close_status = [
  4990. "Invalid",
  4991. "Insufficient data",
  4992. "Fixed",
  4993. "Duplicate",
  4994. ]
  4995. self.session.add(item)
  4996. self.session.commit()
  4997. # Create two git repos, one has 6 commits, the other 4 of which only
  4998. # 1 isn't present in the first repo
  4999. gitrepo = os.path.join(self.path, "repos", "test.git")
  5000. pygit2.init_repository(gitrepo, bare=True)
  5001. gitrepo2 = os.path.join(
  5002. self.path, "repos", "forks", "pingou", "test.git"
  5003. )
  5004. pygit2.init_repository(gitrepo2, bare=True)
  5005. newpath = tempfile.mkdtemp(prefix="pagure-fork-test")
  5006. repopath = os.path.join(newpath, "test")
  5007. clone_repo = pygit2.clone_repository(gitrepo, repopath)
  5008. # Do 2 commits to the main repo
  5009. for i in range(2):
  5010. with open(os.path.join(repopath, "sources"), "w") as stream:
  5011. stream.write("foo%s\n bar%s\n" % (i, i))
  5012. clone_repo.index.add("sources")
  5013. clone_repo.index.write()
  5014. parents = []
  5015. try:
  5016. last_commit = clone_repo.revparse_single("HEAD")
  5017. parents = [last_commit.oid.hex]
  5018. except KeyError:
  5019. pass
  5020. # Commits the files added
  5021. tree = clone_repo.index.write_tree()
  5022. author = pygit2.Signature("Alice Author", "alice@authors.tld")
  5023. committer = pygit2.Signature(
  5024. "Cecil Committer", "cecil@committers.tld"
  5025. )
  5026. clone_repo.create_commit(
  5027. "refs/heads/master", # the name of the reference to update
  5028. author,
  5029. committer,
  5030. "Editing the file sources for testing #%s" % i,
  5031. # binary string representing the tree object ID
  5032. tree,
  5033. # list of binary strings representing parents of the new commit
  5034. parents,
  5035. )
  5036. # Push to the main repo
  5037. refname = "refs/heads/master:refs/heads/master"
  5038. ori_remote = clone_repo.remotes[0]
  5039. PagureRepo.push(ori_remote, refname)
  5040. # Push to the fork repo
  5041. remote = clone_repo.create_remote("pingou_fork", gitrepo2)
  5042. PagureRepo.push(remote, refname)
  5043. # Add 1 commits to the fork repo
  5044. repopath = os.path.join(newpath, "pingou_test")
  5045. clone_repo = pygit2.clone_repository(gitrepo2, repopath)
  5046. with open(os.path.join(repopath, "soürces"), "w") as stream:
  5047. stream.write("foo\n bar\n")
  5048. clone_repo.index.add("soürces")
  5049. clone_repo.index.write()
  5050. with open(os.path.join(repopath, "fóß"), "w") as stream:
  5051. stream.write("foo\n bar\n")
  5052. clone_repo.index.add("fóß")
  5053. clone_repo.index.write()
  5054. last_commit = clone_repo.revparse_single("HEAD")
  5055. # Commits the files added
  5056. tree = clone_repo.index.write_tree()
  5057. author = pygit2.Signature("Alice Author", "alice@authors.tld")
  5058. committer = pygit2.Signature("Cecil Committer", "cecil@committers.tld")
  5059. last_commit = clone_repo.create_commit(
  5060. "refs/heads/feature_foo", # the name of the reference to update
  5061. author,
  5062. committer,
  5063. "New edition on side branch of the file sources for testing",
  5064. # binary string representing the tree object ID
  5065. tree,
  5066. # list of binary strings representing parents of the new commit
  5067. [last_commit.oid.hex],
  5068. )
  5069. # Push to the fork repo
  5070. ori_remote = clone_repo.remotes[0]
  5071. refname = "refs/heads/feature_foo:refs/heads/feature_foo"
  5072. PagureRepo.push(ori_remote, refname)
  5073. shutil.rmtree(newpath)
  5074. # Create the PR between the two repos
  5075. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  5076. forked_repo = pagure.lib.query.get_authorized_project(
  5077. self.session, "test", user="pingou"
  5078. )
  5079. req = pagure.lib.query.new_pull_request(
  5080. session=self.session,
  5081. repo_from=forked_repo,
  5082. branch_from="feature_foo",
  5083. repo_to=repo,
  5084. branch_to="master",
  5085. title="test pull-request",
  5086. user="pingou",
  5087. )
  5088. self.assertEqual(req.id, 1)
  5089. self.assertEqual(req.title, "test pull-request")
  5090. user = tests.FakeUser(username="pingou")
  5091. with tests.user_set(self.app.application, user):
  5092. output = self.app.get("/fork/pingou/test/diff/master..feature_foo")
  5093. self.assertEqual(output.status_code, 200)
  5094. output_text = output.get_data(as_text=True)
  5095. self.assertIn(
  5096. "<title>Create new Pull Request for master - fork/pingou/test\n - "
  5097. "Pagure</title>",
  5098. output_text,
  5099. )
  5100. self.assertIn(
  5101. '<input type="submit" class="btn btn-primary" value="Create Pull Request">\n',
  5102. output_text,
  5103. )
  5104. # Check that we prefilled the input fields as expected:
  5105. self.assertIn(
  5106. '<input class="form-control" id="title" name="title" '
  5107. 'placeholder="Pull Request Title" required="required" '
  5108. 'type="text" value="New edition on side branch of the file '
  5109. 'sources for testing">',
  5110. output_text,
  5111. )
  5112. self.assertIn(
  5113. '<a class="dropdown-item branch_from_item pointer" '
  5114. 'data-value="master"><span class="fa fa-random">'
  5115. "</span> master</a>",
  5116. output_text,
  5117. )
  5118. csrf_token = self.get_csrf(output=output)
  5119. # Case 1 - Add an initial comment
  5120. data = {
  5121. "csrf_token": csrf_token,
  5122. "title": "foo bar PR",
  5123. "initial_comment": "Test Initial Comment",
  5124. }
  5125. output = self.app.post(
  5126. "/fork/pingou/test/diff/master..feature_foo",
  5127. data=data,
  5128. follow_redirects=True,
  5129. )
  5130. self.assertEqual(output.status_code, 200)
  5131. output_text = output.get_data(as_text=True)
  5132. self.assertIn(
  5133. "<title>PR#2: foo bar PR - test\n - Pagure</title>",
  5134. output_text,
  5135. )
  5136. self.assertIn("<p>Test Initial Comment</p>", output_text)
  5137. self.assertEqual(output_text.count('title="PY C (pingou)"'), 2)
  5138. @patch("pagure.lib.notify.send_email")
  5139. def test_new_request_pull_req_sign_off_view(self, send_email):
  5140. """ Test the new_request_pull endpoint. """
  5141. send_email.return_value = True
  5142. self.test_fork_project()
  5143. tests.create_projects_git(
  5144. os.path.join(self.path, "requests"), bare=True
  5145. )
  5146. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  5147. fork = pagure.lib.query.get_authorized_project(
  5148. self.session, "test", user="foo"
  5149. )
  5150. # Enforce Signed-of-by in the repo
  5151. settings = repo.settings
  5152. settings["Enforce_signed-off_commits_in_pull-request"] = True
  5153. repo.settings = settings
  5154. self.session.add(repo)
  5155. self.session.commit()
  5156. set_up_git_repo(
  5157. self.session,
  5158. self.path,
  5159. new_project=fork,
  5160. branch_from="feature",
  5161. mtype="FF",
  5162. )
  5163. user = tests.FakeUser()
  5164. user.username = "foo"
  5165. with tests.user_set(self.app.application, user):
  5166. output = self.app.get("/test/diff/master..feature")
  5167. self.assertEqual(output.status_code, 200)
  5168. output_text = output.get_data(as_text=True)
  5169. self.assertIn(
  5170. "<title>Diff from feature to master - test\n - "
  5171. "Pagure</title>",
  5172. output_text,
  5173. )
  5174. self.assertIn(
  5175. "This project enforces the "
  5176. "Signed-off-by statement on all commits",
  5177. output_text,
  5178. )
  5179. self.assertNotIn(
  5180. '<input type="submit" class="btn btn-primary" value="Create Pull Request">\n',
  5181. output_text,
  5182. )
  5183. self.assertNotIn(
  5184. "This repo enforces that "
  5185. "all commits are signed off by their author.",
  5186. output_text,
  5187. )
  5188. @patch("pagure.lib.notify.send_email")
  5189. def test_new_request_pull_req_sign_off_submit(self, send_email):
  5190. """ Test the new_request_pull endpoint. """
  5191. send_email.return_value = True
  5192. self.test_fork_project()
  5193. tests.create_projects_git(
  5194. os.path.join(self.path, "requests"), bare=True
  5195. )
  5196. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  5197. fork = pagure.lib.query.get_authorized_project(
  5198. self.session, "test", user="foo"
  5199. )
  5200. # Enforce Signed-of-by in the repo
  5201. settings = repo.settings
  5202. settings["Enforce_signed-off_commits_in_pull-request"] = True
  5203. repo.settings = settings
  5204. self.session.add(repo)
  5205. self.session.commit()
  5206. set_up_git_repo(
  5207. self.session,
  5208. self.path,
  5209. new_project=fork,
  5210. branch_from="feature",
  5211. mtype="FF",
  5212. )
  5213. user = tests.FakeUser()
  5214. user.username = "pingou"
  5215. with tests.user_set(self.app.application, user):
  5216. output = self.app.get("/test/diff/master..feature")
  5217. self.assertEqual(output.status_code, 200)
  5218. output_text = output.get_data(as_text=True)
  5219. self.assertIn(
  5220. "<title>Create new Pull Request for master - test\n - "
  5221. "Pagure</title>",
  5222. output_text,
  5223. )
  5224. self.assertIn(
  5225. "This project enforces the "
  5226. "Signed-off-by statement on all commits",
  5227. output_text,
  5228. )
  5229. self.assertIn(
  5230. '<input type="submit" class="btn btn-primary" value="Create Pull Request">\n',
  5231. output_text,
  5232. )
  5233. csrf_token = self.get_csrf(output=output)
  5234. # Try to create the PR
  5235. data = {
  5236. "csrf_token": csrf_token,
  5237. "title": "foo bar PR",
  5238. "initial_comment": "Test Initial Comment",
  5239. }
  5240. output = self.app.post(
  5241. "/test/diff/master..feature", data=data, follow_redirects=True
  5242. )
  5243. self.assertEqual(output.status_code, 200)
  5244. output_text = output.get_data(as_text=True)
  5245. self.assertIn(
  5246. "<title>Create new Pull Request for master - test\n - "
  5247. "Pagure</title>",
  5248. output_text,
  5249. )
  5250. # Flashed information message
  5251. self.assertIn(
  5252. "This project enforces the "
  5253. "Signed-off-by statement on all commits",
  5254. output_text,
  5255. )
  5256. # Flashed error message
  5257. self.assertIn(
  5258. "This repo enforces that "
  5259. "all commits are signed off by their author.",
  5260. output_text,
  5261. )
  5262. self.assertIn(
  5263. '<input type="submit" class="btn btn-primary" value="Create Pull Request">\n',
  5264. output_text,
  5265. )
  5266. @patch("pagure.lib.notify.send_email")
  5267. def test_request_pull_commit_start_stop(self, send_email):
  5268. """ Test the the commit start and stop of brand new PR. """
  5269. send_email.return_value = True
  5270. self.test_fork_project()
  5271. tests.create_projects_git(
  5272. os.path.join(self.path, "requests"), bare=True
  5273. )
  5274. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  5275. fork = pagure.lib.query.get_authorized_project(
  5276. self.session, "test", user="foo"
  5277. )
  5278. set_up_git_repo(
  5279. self.session,
  5280. self.path,
  5281. new_project=fork,
  5282. branch_from="feature",
  5283. mtype="FF",
  5284. )
  5285. user = tests.FakeUser()
  5286. user.username = "pingou"
  5287. with tests.user_set(self.app.application, user):
  5288. output = self.app.get("/test/diff/master..feature")
  5289. self.assertEqual(output.status_code, 200)
  5290. output_text = output.get_data(as_text=True)
  5291. self.assertIn(
  5292. "<title>Create new Pull Request for master - test\n - "
  5293. "Pagure</title>",
  5294. output_text,
  5295. )
  5296. self.assertIn(
  5297. '<input type="submit" class="btn btn-primary" value="Create Pull Request">\n',
  5298. output_text,
  5299. )
  5300. csrf_token = self.get_csrf(output=output)
  5301. # Case 1 - Add an initial comment
  5302. data = {
  5303. "csrf_token": csrf_token,
  5304. "title": "foo bar PR",
  5305. "initial_comment": "Test Initial Comment",
  5306. }
  5307. output = self.app.post(
  5308. "/test/diff/master..feature", data=data, follow_redirects=True
  5309. )
  5310. self.assertEqual(output.status_code, 200)
  5311. output_text = output.get_data(as_text=True)
  5312. self.assertIn(
  5313. "<title>PR#2: foo bar PR - test\n - Pagure</title>",
  5314. output_text,
  5315. )
  5316. self.assertIn("<p>Test Initial Comment</p>", output_text)
  5317. # Check if commit start and stop have been set for PR#2
  5318. request = pagure.lib.query.search_pull_requests(
  5319. self.session, project_id=1, requestid=2
  5320. )
  5321. self.assertIsNotNone(request.commit_start)
  5322. self.assertIsNotNone(request.commit_stop)
  5323. @patch("pagure.lib.notify.send_email")
  5324. def test_new_request_pull_from_fork_branch(self, send_email):
  5325. """ Test creating a fork to fork PR. """
  5326. send_email.return_value = True
  5327. # Create main repo with some content
  5328. tests.create_projects(self.session)
  5329. tests.create_projects_git(os.path.join(self.path, "repos"), bare=True)
  5330. tests.add_content_git_repo(
  5331. os.path.join(self.path, "repos", "test.git")
  5332. )
  5333. # Create fork repo with more content
  5334. tests.create_projects(
  5335. self.session, is_fork=True, hook_token_suffix="fork"
  5336. )
  5337. tests.create_projects_git(
  5338. os.path.join(self.path, "repos", "forks", "pingou"), bare=True
  5339. )
  5340. tests.add_content_git_repo(
  5341. os.path.join(self.path, "repos", "forks", "pingou", "test.git")
  5342. )
  5343. tests.add_readme_git_repo(
  5344. os.path.join(self.path, "repos", "forks", "pingou", "test.git"),
  5345. branch="feature",
  5346. )
  5347. tests.add_readme_git_repo(
  5348. os.path.join(self.path, "repos", "forks", "pingou", "test.git"),
  5349. branch="random_branch",
  5350. )
  5351. user = tests.FakeUser(username="pingou")
  5352. with tests.user_set(self.app.application, user):
  5353. data = {"csrf_token": self.get_csrf()}
  5354. output = self.app.post(
  5355. "/do_fork/test", data=data, follow_redirects=True
  5356. )
  5357. self.assertEqual(output.status_code, 200)
  5358. # Check that Ralph's fork do exist
  5359. output = self.app.get("/fork/pingou/test")
  5360. self.assertEqual(output.status_code, 200)
  5361. tests.create_projects_git(
  5362. os.path.join(self.path, "requests"), bare=True
  5363. )
  5364. fork = pagure.lib.query.get_authorized_project(
  5365. self.session, "test", user="ralph"
  5366. )
  5367. set_up_git_repo(
  5368. self.session,
  5369. self.path,
  5370. new_project=fork,
  5371. branch_from="feature",
  5372. mtype="FF",
  5373. )
  5374. # Try opening a pull-request
  5375. output = self.app.get("/fork/pingou/test/diff/master..feature")
  5376. self.assertEqual(output.status_code, 200)
  5377. output_text = output.get_data(as_text=True)
  5378. self.assertIn(
  5379. "<title>Create new Pull Request for master - "
  5380. "fork/pingou/test\n - Pagure</title>",
  5381. output_text,
  5382. )
  5383. self.assertIn(
  5384. '<input type="submit" class="btn btn-primary" value="Create Pull Request">\n',
  5385. output_text,
  5386. )
  5387. self.assertIn(
  5388. '<a class="dropdown-item branch_from_item pointer" '
  5389. 'data-value="master"><span class="fa fa-random">'
  5390. "</span> master</a>",
  5391. output_text,
  5392. )
  5393. self.assertIn(
  5394. '<a class="dropdown-item branch_from_item pointer" '
  5395. 'data-value="random_branch"><span class="fa fa-random">'
  5396. "</span> random_branch</a>",
  5397. output_text,
  5398. )
  5399. @patch("pagure.lib.notify.send_email", MagicMock(return_value=True))
  5400. def test_new_request_pull_from_fork_fixing_ticket(self):
  5401. """ Test creating a fork to fork PR fixing a ticket. """
  5402. # Create main repo with some content
  5403. tests.create_projects(self.session)
  5404. tests.create_projects_git(os.path.join(self.path, "repos"), bare=True)
  5405. tests.add_content_git_repo(
  5406. os.path.join(self.path, "repos", "test.git")
  5407. )
  5408. # Create fork repo with more content
  5409. tests.create_projects(
  5410. self.session, is_fork=True, hook_token_suffix="fork"
  5411. )
  5412. tests.create_projects_git(
  5413. os.path.join(self.path, "repos", "forks", "pingou"), bare=True
  5414. )
  5415. tests.add_content_git_repo(
  5416. os.path.join(self.path, "repos", "forks", "pingou", "test.git")
  5417. )
  5418. tests.add_readme_git_repo(
  5419. os.path.join(self.path, "repos", "forks", "pingou", "test.git"),
  5420. branch="feature",
  5421. )
  5422. tests.add_readme_git_repo(
  5423. os.path.join(self.path, "repos", "forks", "pingou", "test.git"),
  5424. branch="random_branch",
  5425. )
  5426. # Check relations before we create the PR
  5427. project = pagure.lib.query.get_authorized_project(self.session, "test")
  5428. self.assertEqual(len(project.requests), 0)
  5429. self.assertEqual(len(project.issues), 0)
  5430. # Create issues to link to
  5431. msg = pagure.lib.query.new_issue(
  5432. session=self.session,
  5433. repo=project,
  5434. title="tést íssüé",
  5435. content="We should work on this",
  5436. user="pingou",
  5437. )
  5438. self.session.commit()
  5439. self.assertEqual(msg.title, "tést íssüé")
  5440. user = tests.FakeUser(username="pingou")
  5441. with tests.user_set(self.app.application, user):
  5442. csrf_token = self.get_csrf()
  5443. data = {"csrf_token": csrf_token}
  5444. output = self.app.post(
  5445. "/do_fork/test", data=data, follow_redirects=True
  5446. )
  5447. self.assertEqual(output.status_code, 200)
  5448. # Check that pingou's fork do exist
  5449. output = self.app.get("/fork/pingou/test")
  5450. self.assertEqual(output.status_code, 200)
  5451. tests.create_projects_git(
  5452. os.path.join(self.path, "requests"), bare=True
  5453. )
  5454. fork = pagure.lib.query.get_authorized_project(
  5455. self.session, "test", user="ralph"
  5456. )
  5457. set_up_git_repo(
  5458. self.session,
  5459. self.path,
  5460. new_project=fork,
  5461. branch_from="feature",
  5462. mtype="FF",
  5463. prid=2,
  5464. )
  5465. # Try opening a pull-request
  5466. output = self.app.get("/fork/pingou/test/diff/master..feature")
  5467. self.assertEqual(output.status_code, 200)
  5468. output_text = output.get_data(as_text=True)
  5469. self.assertIn(
  5470. "<title>Create new Pull Request for master - "
  5471. "fork/pingou/test\n - Pagure</title>",
  5472. output_text,
  5473. )
  5474. self.assertIn(
  5475. '<input type="submit" class="btn btn-primary" value="Create Pull Request">\n',
  5476. output_text,
  5477. )
  5478. self.assertIn(
  5479. '<a class="dropdown-item branch_from_item pointer" '
  5480. 'data-value="master"><span class="fa fa-random">'
  5481. "</span> master</a>",
  5482. output_text,
  5483. )
  5484. self.assertIn(
  5485. '<a class="dropdown-item branch_from_item pointer" '
  5486. 'data-value="random_branch"><span class="fa fa-random">'
  5487. "</span> random_branch</a>",
  5488. output_text,
  5489. )
  5490. data = {
  5491. "csrf_token": csrf_token,
  5492. "title": "foo bar PR",
  5493. "initial_comment": "Test Initial Comment\n\nFixes #1",
  5494. }
  5495. output = self.app.post(
  5496. "/fork/pingou/test/diff/master..feature",
  5497. data=data,
  5498. follow_redirects=True,
  5499. )
  5500. self.assertEqual(output.status_code, 200)
  5501. output_text = output.get_data(as_text=True)
  5502. self.assertIn(
  5503. "<title>PR#3: foo bar PR - test\n - Pagure</title>",
  5504. output_text,
  5505. )
  5506. self.assertIn(
  5507. "<p>Test Initial Comment</p>\n<p>Fixes <a href", output_text
  5508. )
  5509. project = pagure.lib.query.get_authorized_project(self.session, "test")
  5510. self.assertEqual(len(project.requests), 2)
  5511. self.assertEqual(len(project.requests[0].related_issues), 0)
  5512. self.assertEqual(len(project.requests[1].related_issues), 1)
  5513. self.assertEqual(len(project.issues), 1)
  5514. self.assertEqual(len(project.issues[0].related_prs), 1)
  5515. @patch("pagure.lib.notify.send_email")
  5516. def test_new_request_pull_fork_to_fork_pr_disabled(self, send_email):
  5517. """ Test creating a fork to fork PR. """
  5518. send_email.return_value = True
  5519. self.test_fork_project()
  5520. # Create a 3rd user
  5521. item = pagure.lib.model.User(
  5522. user="ralph",
  5523. fullname="Ralph bar",
  5524. password="ralph_foo",
  5525. default_email="ralph@bar.com",
  5526. )
  5527. self.session.add(item)
  5528. item = pagure.lib.model.UserEmail(user_id=3, email="ralph@bar.com")
  5529. self.session.add(item)
  5530. self.session.commit()
  5531. user = tests.FakeUser()
  5532. user.username = "ralph"
  5533. with tests.user_set(self.app.application, user):
  5534. # Have Ralph fork, foo's fork of test
  5535. output = self.app.get("/fork/foo/test")
  5536. self.assertEqual(output.status_code, 200)
  5537. output = self.app.post("/do_fork/fork/foo/test")
  5538. self.assertEqual(output.status_code, 400)
  5539. csrf_token = self.get_csrf()
  5540. data = {"csrf_token": csrf_token}
  5541. output = self.app.post(
  5542. "/do_fork/fork/foo/test", data=data, follow_redirects=True
  5543. )
  5544. self.assertEqual(output.status_code, 200)
  5545. # Check that Ralph's fork do exist
  5546. output = self.app.get("/fork/ralph/test")
  5547. self.assertEqual(output.status_code, 200)
  5548. tests.create_projects_git(
  5549. os.path.join(self.path, "requests"), bare=True
  5550. )
  5551. fork = pagure.lib.query.get_authorized_project(
  5552. self.session, "test", user="ralph"
  5553. )
  5554. set_up_git_repo(
  5555. self.session,
  5556. self.path,
  5557. new_project=fork,
  5558. branch_from="feature",
  5559. mtype="FF",
  5560. )
  5561. # Try opening a pull-request
  5562. output = self.app.get("/fork/ralph/test/diff/master..feature")
  5563. self.assertEqual(output.status_code, 404)
  5564. self.assertIn(
  5565. "<p>No pull-request allowed on this project</p>",
  5566. output.get_data(as_text=True),
  5567. )
  5568. @patch("pagure.lib.notify.send_email")
  5569. def test_new_request_pull_fork_to_fork(self, send_email):
  5570. """ Test creating a fork to fork PR. """
  5571. send_email.return_value = True
  5572. self.test_fork_project()
  5573. # Create a 3rd user
  5574. item = pagure.lib.model.User(
  5575. user="ralph",
  5576. fullname="Ralph bar",
  5577. password="ralph_foo",
  5578. default_email="ralph@bar.com",
  5579. )
  5580. self.session.add(item)
  5581. item = pagure.lib.model.UserEmail(user_id=3, email="ralph@bar.com")
  5582. self.session.add(item)
  5583. self.session.commit()
  5584. user = tests.FakeUser()
  5585. user.username = "ralph"
  5586. with tests.user_set(self.app.application, user):
  5587. # Have Ralph fork, foo's fork of test
  5588. output = self.app.get("/fork/foo/test")
  5589. self.assertEqual(output.status_code, 200)
  5590. output = self.app.post("/do_fork/fork/foo/test")
  5591. self.assertEqual(output.status_code, 400)
  5592. csrf_token = self.get_csrf()
  5593. data = {"csrf_token": csrf_token}
  5594. output = self.app.post(
  5595. "/do_fork/fork/foo/test", data=data, follow_redirects=True
  5596. )
  5597. self.assertEqual(output.status_code, 200)
  5598. # Check that Ralph's fork do exist
  5599. output = self.app.get("/fork/ralph/test")
  5600. self.assertEqual(output.status_code, 200)
  5601. tests.create_projects_git(
  5602. os.path.join(self.path, "requests"), bare=True
  5603. )
  5604. # Turn on pull-request on the fork
  5605. repo = pagure.lib.query.get_authorized_project(
  5606. self.session, "test", user="foo"
  5607. )
  5608. settings = repo.settings
  5609. settings["pull_requests"] = True
  5610. repo.settings = settings
  5611. self.session.add(repo)
  5612. self.session.commit()
  5613. # Add some content to the parent
  5614. set_up_git_repo(
  5615. self.session,
  5616. self.path,
  5617. new_project=repo,
  5618. branch_from="master",
  5619. mtype="FF",
  5620. name_from=repo.fullname,
  5621. )
  5622. fork = pagure.lib.query.get_authorized_project(
  5623. self.session, "test", user="ralph"
  5624. )
  5625. set_up_git_repo(
  5626. self.session,
  5627. self.path,
  5628. new_project=fork,
  5629. branch_from="feature",
  5630. mtype="FF",
  5631. prid=2,
  5632. name_from=fork.fullname,
  5633. )
  5634. # Try opening a pull-request
  5635. output = self.app.get("/fork/ralph/test/diff/master..feature")
  5636. self.assertEqual(output.status_code, 200)
  5637. output_text = output.get_data(as_text=True)
  5638. self.assertIn(
  5639. "<title>Create new Pull Request for master - fork/ralph/test\n - "
  5640. "Pagure</title>",
  5641. output_text,
  5642. )
  5643. self.assertIn(
  5644. '<input type="submit" class="btn btn-primary" value="Create Pull Request">\n',
  5645. output_text,
  5646. )
  5647. csrf_token = self.get_csrf(output=output)
  5648. # Case 1 - Add an initial comment
  5649. data = {
  5650. "csrf_token": csrf_token,
  5651. "title": "foo bar PR",
  5652. "initial_comment": "Test Initial Comment",
  5653. }
  5654. output = self.app.post(
  5655. "/fork/ralph/test/diff/master..feature",
  5656. data=data,
  5657. follow_redirects=True,
  5658. )
  5659. self.assertEqual(output.status_code, 200)
  5660. output_text = output.get_data(as_text=True)
  5661. self.assertIn(
  5662. "<title>PR#1: foo bar PR - fork/foo/test\n - Pagure</title>",
  5663. output_text,
  5664. )
  5665. self.assertIn("<p>Test Initial Comment</p>", output_text)
  5666. @patch("pagure.lib.notify.send_email")
  5667. def test_new_request_pull_fork_to_other_fork(self, send_email):
  5668. """ Test creating a PR from fork to a fork of the same family. """
  5669. send_email.return_value = True
  5670. self.test_fork_project()
  5671. # Create a 3rd user
  5672. item = pagure.lib.model.User(
  5673. user="ralph",
  5674. fullname="Ralph bar",
  5675. password="ralph_foo",
  5676. default_email="ralph@bar.com",
  5677. )
  5678. self.session.add(item)
  5679. item = pagure.lib.model.UserEmail(user_id=3, email="ralph@bar.com")
  5680. self.session.add(item)
  5681. self.session.commit()
  5682. user = tests.FakeUser()
  5683. user.username = "ralph"
  5684. with tests.user_set(self.app.application, user):
  5685. csrf_token = self.get_csrf()
  5686. data = {"csrf_token": csrf_token}
  5687. output = self.app.post(
  5688. "/do_fork/test", data=data, follow_redirects=True
  5689. )
  5690. self.assertEqual(output.status_code, 200)
  5691. # Check that Ralph's fork do exist
  5692. output = self.app.get("/fork/ralph/test")
  5693. self.assertEqual(output.status_code, 200)
  5694. tests.create_projects_git(
  5695. os.path.join(self.path, "requests"), bare=True
  5696. )
  5697. # Turn on pull-request on the fork
  5698. repo = pagure.lib.query.get_authorized_project(
  5699. self.session, "test", user="foo"
  5700. )
  5701. settings = repo.settings
  5702. settings["pull_requests"] = True
  5703. repo.settings = settings
  5704. self.session.add(repo)
  5705. self.session.commit()
  5706. # Add some content to the parents
  5707. set_up_git_repo(
  5708. self.session,
  5709. self.path,
  5710. new_project=repo,
  5711. branch_from="master",
  5712. mtype="FF",
  5713. )
  5714. set_up_git_repo(
  5715. self.session,
  5716. self.path,
  5717. new_project=repo,
  5718. branch_from="master",
  5719. mtype="FF",
  5720. name_from=repo.fullname,
  5721. prid=2,
  5722. )
  5723. fork = pagure.lib.query.get_authorized_project(
  5724. self.session, "test", user="ralph"
  5725. )
  5726. set_up_git_repo(
  5727. self.session,
  5728. self.path,
  5729. new_project=fork,
  5730. branch_from="feature",
  5731. mtype="FF",
  5732. prid=3,
  5733. name_from=fork.fullname,
  5734. )
  5735. # Try opening a pull-request
  5736. output = self.app.get(
  5737. "/fork/ralph/test/diff/master..feature?project_to=fork/foo/test"
  5738. )
  5739. self.assertEqual(output.status_code, 200)
  5740. output_text = output.get_data(as_text=True)
  5741. self.assertIn(
  5742. "<title>Create new Pull Request for master - fork/ralph/test\n - "
  5743. "Pagure</title>",
  5744. output_text,
  5745. )
  5746. self.assertIn(
  5747. '<input type="submit" class="btn btn-primary" value="Create Pull Request">\n',
  5748. output_text,
  5749. )
  5750. csrf_token = self.get_csrf(output=output)
  5751. # Case 1 - Opening PR to fork/foo/test
  5752. data = {
  5753. "csrf_token": csrf_token,
  5754. "title": "foo bar PR",
  5755. "initial_comment": "Test Initial Comment",
  5756. }
  5757. output = self.app.post(
  5758. "/fork/ralph/test/diff/master..feature?project_to=fork/foo/test",
  5759. data=data,
  5760. follow_redirects=True,
  5761. )
  5762. self.assertEqual(output.status_code, 200)
  5763. output_text = output.get_data(as_text=True)
  5764. self.assertIn(
  5765. "<title>PR#1: foo bar PR - fork/foo/test\n - Pagure</title>",
  5766. output_text,
  5767. )
  5768. self.assertIn("<p>Test Initial Comment</p>", output_text)
  5769. # Case 1 - Opening PR to parent repo, shows project_to works
  5770. output = self.app.post(
  5771. "/fork/ralph/test/diff/master..feature",
  5772. data=data,
  5773. follow_redirects=True,
  5774. )
  5775. self.assertEqual(output.status_code, 200)
  5776. output_text = output.get_data(as_text=True)
  5777. self.assertIn(
  5778. "<title>PR#4: foo bar PR - test\n - Pagure</title>",
  5779. output_text,
  5780. )
  5781. self.assertIn("<p>Test Initial Comment</p>", output_text)
  5782. @patch("pagure.lib.notify.send_email")
  5783. def test_new_request_pull_fork_to_other_unrelated_fork(self, send_email):
  5784. """Test creating a PR from fork to fork that isn't from the same
  5785. family.
  5786. """
  5787. send_email.return_value = True
  5788. self.test_fork_project()
  5789. # Create a 3rd user
  5790. item = pagure.lib.model.User(
  5791. user="ralph",
  5792. fullname="Ralph bar",
  5793. password="ralph_foo",
  5794. default_email="ralph@bar.com",
  5795. )
  5796. self.session.add(item)
  5797. item = pagure.lib.model.UserEmail(user_id=3, email="ralph@bar.com")
  5798. self.session.add(item)
  5799. self.session.commit()
  5800. user = tests.FakeUser()
  5801. user.username = "ralph"
  5802. with tests.user_set(self.app.application, user):
  5803. csrf_token = self.get_csrf()
  5804. data = {"csrf_token": csrf_token}
  5805. output = self.app.post(
  5806. "/do_fork/test2", data=data, follow_redirects=True
  5807. )
  5808. self.assertEqual(output.status_code, 200)
  5809. # Check that Ralph's fork do exist
  5810. output = self.app.get("/fork/ralph/test2")
  5811. self.assertEqual(output.status_code, 200)
  5812. tests.create_projects_git(
  5813. os.path.join(self.path, "requests"), bare=True
  5814. )
  5815. # Turn on pull-request on the fork
  5816. repo = pagure.lib.query.get_authorized_project(
  5817. self.session, "test", user="foo"
  5818. )
  5819. settings = repo.settings
  5820. settings["pull_requests"] = True
  5821. repo.settings = settings
  5822. self.session.add(repo)
  5823. self.session.commit()
  5824. # Add some content to the parent
  5825. set_up_git_repo(
  5826. self.session,
  5827. self.path,
  5828. new_project=repo,
  5829. branch_from="master",
  5830. mtype="FF",
  5831. name_from=repo.fullname,
  5832. )
  5833. fork = pagure.lib.query.get_authorized_project(
  5834. self.session, "test2", user="ralph"
  5835. )
  5836. set_up_git_repo(
  5837. self.session,
  5838. self.path,
  5839. new_project=fork,
  5840. branch_from="feature",
  5841. mtype="FF",
  5842. prid=2,
  5843. name_from=fork.fullname,
  5844. )
  5845. # Case 1 - Opening PR to fork/foo/test
  5846. data = {
  5847. "csrf_token": csrf_token,
  5848. "title": "foo bar PR",
  5849. "initial_comment": "Test Initial Comment",
  5850. }
  5851. output = self.app.post(
  5852. "/fork/ralph/test2/diff/master..feature?project_to=fork/foo/test",
  5853. data=data,
  5854. follow_redirects=True,
  5855. )
  5856. self.assertEqual(output.status_code, 400)
  5857. self.assertIn(
  5858. "<p>fork/foo/test is not part of fork/ralph/test2's "
  5859. "family</p>",
  5860. output.get_data(as_text=True),
  5861. )
  5862. @patch("pagure.lib.notify.send_email")
  5863. def test_new_request_pull_empty_repo(self, send_email):
  5864. """ Test the new_request_pull endpoint against an empty repo. """
  5865. send_email.return_value = True
  5866. self.test_fork_project()
  5867. tests.create_projects_git(
  5868. os.path.join(self.path, "requests"), bare=True
  5869. )
  5870. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  5871. fork = pagure.lib.query.get_authorized_project(
  5872. self.session, "test", user="foo"
  5873. )
  5874. # Create a git repo to play with
  5875. gitrepo = os.path.join(self.path, "repos", "test.git")
  5876. repo = pygit2.init_repository(gitrepo, bare=True)
  5877. # Create a fork of this repo
  5878. newpath = tempfile.mkdtemp(prefix="pagure-fork-test")
  5879. gitrepo = os.path.join(self.path, "repos", "forks", "foo", "test.git")
  5880. new_repo = pygit2.clone_repository(gitrepo, newpath)
  5881. user = tests.FakeUser()
  5882. user.username = "foo"
  5883. with tests.user_set(self.app.application, user):
  5884. output = self.app.get(
  5885. "/fork/foo/test/diff/master..feature", follow_redirects=True
  5886. )
  5887. self.assertEqual(output.status_code, 400)
  5888. self.assertIn(
  5889. "<p>Fork is empty, there are no commits to create a pull "
  5890. "request with</p>",
  5891. output.get_data(as_text=True),
  5892. )
  5893. output = self.app.get("/test/new_issue")
  5894. csrf_token = self.get_csrf(output=output)
  5895. data = {"csrf_token": csrf_token, "title": "foo bar PR"}
  5896. output = self.app.post(
  5897. "/test/diff/master..feature", data=data, follow_redirects=True
  5898. )
  5899. self.assertEqual(output.status_code, 400)
  5900. self.assertIn(
  5901. "<p>Fork is empty, there are no commits to create a pull "
  5902. "request with</p>",
  5903. output.get_data(as_text=True),
  5904. )
  5905. shutil.rmtree(newpath)
  5906. @patch("pagure.lib.notify.send_email")
  5907. def test_new_request_pull_empty_fork(self, send_email):
  5908. """ Test the new_request_pull endpoint against an empty repo. """
  5909. send_email.return_value = True
  5910. self.test_fork_project()
  5911. tests.create_projects_git(
  5912. os.path.join(self.path, "requests"), bare=True
  5913. )
  5914. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  5915. fork = pagure.lib.query.get_authorized_project(
  5916. self.session, "test", user="foo"
  5917. )
  5918. # Create a git repo to play with
  5919. gitrepo = os.path.join(self.path, "repos", "test.git")
  5920. repo = pygit2.init_repository(gitrepo, bare=True)
  5921. # Create a fork of this repo
  5922. newpath = tempfile.mkdtemp(prefix="pagure-fork-test")
  5923. gitrepo = os.path.join(self.path, "repos", "forks", "foo", "test.git")
  5924. new_repo = pygit2.clone_repository(gitrepo, newpath)
  5925. user = tests.FakeUser()
  5926. user.username = "foo"
  5927. with tests.user_set(self.app.application, user):
  5928. output = self.app.get(
  5929. "/fork/foo/test/diff/master..master", follow_redirects=True
  5930. )
  5931. self.assertEqual(output.status_code, 400)
  5932. self.assertIn(
  5933. "<p>Fork is empty, there are no commits to create a pull "
  5934. "request with</p>",
  5935. output.get_data(as_text=True),
  5936. )
  5937. shutil.rmtree(newpath)
  5938. @patch.dict(
  5939. "pagure.config.config", {"FEDORA_MESSAGING_NOTIFICATIONS": True}
  5940. )
  5941. @patch("pagure.lib.notify.send_email")
  5942. def test_pull_request_add_comment(self, send_email):
  5943. """ Test the pull_request_add_comment endpoint. """
  5944. send_email.return_value = True
  5945. self.test_request_pull()
  5946. user = tests.FakeUser()
  5947. user.username = "pingou"
  5948. with tests.user_set(self.app.application, user):
  5949. output = self.app.post("/foo/pull-request/1/comment")
  5950. self.assertEqual(output.status_code, 404)
  5951. output = self.app.post("/test/pull-request/100/comment")
  5952. self.assertEqual(output.status_code, 404)
  5953. output = self.app.post("/test/pull-request/1/comment")
  5954. self.assertEqual(output.status_code, 200)
  5955. self.assertTrue(
  5956. output.get_data(as_text=True).startswith(
  5957. '\n<section class="add_comment">'
  5958. )
  5959. )
  5960. csrf_token = self.get_csrf(output=output)
  5961. data = {
  5962. "csrf_token": csrf_token,
  5963. "comment": "This look alright but we can do better",
  5964. }
  5965. with testing.mock_sends(
  5966. pagure_messages.PullRequestCommentAddedV1(
  5967. topic="pagure.pull-request.comment.added",
  5968. body={
  5969. "pullrequest": {
  5970. "id": 1,
  5971. "full_url": "http://localhost.localdomain/test/pull-request/1",
  5972. "uid": ANY,
  5973. "title": "PR from the feature branch",
  5974. "branch": "master",
  5975. "project": {
  5976. "id": 1,
  5977. "name": "test",
  5978. "fullname": "test",
  5979. "url_path": "test",
  5980. "full_url": "http://localhost.localdomain/test",
  5981. "description": "test project #1",
  5982. "namespace": None,
  5983. "parent": None,
  5984. "date_created": ANY,
  5985. "date_modified": ANY,
  5986. "user": {
  5987. "name": "pingou",
  5988. "fullname": "PY C",
  5989. "url_path": "user/pingou",
  5990. "full_url": "http://localhost.localdomain/user/pingou",
  5991. },
  5992. "access_users": {
  5993. "owner": ["pingou"],
  5994. "admin": [],
  5995. "commit": [],
  5996. "collaborator": [],
  5997. "ticket": [],
  5998. },
  5999. "access_groups": {
  6000. "admin": [],
  6001. "commit": [],
  6002. "collaborator": [],
  6003. "ticket": [],
  6004. },
  6005. "tags": [],
  6006. "priorities": {},
  6007. "custom_keys": [],
  6008. "close_status": [
  6009. "Invalid",
  6010. "Insufficient data",
  6011. "Fixed",
  6012. "Duplicate",
  6013. ],
  6014. "milestones": {},
  6015. },
  6016. "branch_from": "feature",
  6017. "repo_from": {
  6018. "id": 1,
  6019. "name": "test",
  6020. "fullname": "test",
  6021. "url_path": "test",
  6022. "full_url": "http://localhost.localdomain/test",
  6023. "description": "test project #1",
  6024. "namespace": None,
  6025. "parent": None,
  6026. "date_created": ANY,
  6027. "date_modified": ANY,
  6028. "user": {
  6029. "name": "pingou",
  6030. "fullname": "PY C",
  6031. "url_path": "user/pingou",
  6032. "full_url": "http://localhost.localdomain/user/pingou",
  6033. },
  6034. "access_users": {
  6035. "owner": ["pingou"],
  6036. "admin": [],
  6037. "commit": [],
  6038. "collaborator": [],
  6039. "ticket": [],
  6040. },
  6041. "access_groups": {
  6042. "admin": [],
  6043. "commit": [],
  6044. "collaborator": [],
  6045. "ticket": [],
  6046. },
  6047. "tags": [],
  6048. "priorities": {},
  6049. "custom_keys": [],
  6050. "close_status": [
  6051. "Invalid",
  6052. "Insufficient data",
  6053. "Fixed",
  6054. "Duplicate",
  6055. ],
  6056. "milestones": {},
  6057. },
  6058. "remote_git": None,
  6059. "date_created": ANY,
  6060. "updated_on": ANY,
  6061. "last_updated": ANY,
  6062. "closed_at": None,
  6063. "user": {
  6064. "name": "pingou",
  6065. "fullname": "PY C",
  6066. "url_path": "user/pingou",
  6067. "full_url": "http://localhost.localdomain/user/pingou",
  6068. },
  6069. "assignee": None,
  6070. "status": "Open",
  6071. "commit_start": ANY,
  6072. "commit_stop": ANY,
  6073. "closed_by": None,
  6074. "initial_comment": None,
  6075. "cached_merge_status": "unknown",
  6076. "threshold_reached": None,
  6077. "tags": [],
  6078. "comments": [
  6079. {
  6080. "id": 1,
  6081. "commit": None,
  6082. "tree": None,
  6083. "filename": None,
  6084. "line": None,
  6085. "comment": "This look alright but we can do better",
  6086. "parent": None,
  6087. "date_created": ANY,
  6088. "user": {
  6089. "name": "pingou",
  6090. "fullname": "PY C",
  6091. "url_path": "user/pingou",
  6092. "full_url": "http://localhost.localdomain/user/pingou",
  6093. },
  6094. "edited_on": None,
  6095. "editor": None,
  6096. "notification": False,
  6097. "reactions": {},
  6098. }
  6099. ],
  6100. },
  6101. "agent": "pingou",
  6102. },
  6103. )
  6104. ):
  6105. output = self.app.post(
  6106. "/test/pull-request/1/comment",
  6107. data=data,
  6108. follow_redirects=True,
  6109. )
  6110. self.assertEqual(output.status_code, 200)
  6111. output_text = output.get_data(as_text=True)
  6112. self.assertIn(
  6113. "<title>PR#1: PR from the feature branch - test\n - "
  6114. "Pagure</title>",
  6115. output_text,
  6116. )
  6117. self.assertIn("Comment added", output_text)
  6118. self.assertEqual(output_text.count('title="PY C (pingou)"'), 2)
  6119. # Project w/o pull-request
  6120. repo = pagure.lib.query.get_authorized_project(
  6121. self.session, "test"
  6122. )
  6123. settings = repo.settings
  6124. settings["pull_requests"] = False
  6125. repo.settings = settings
  6126. self.session.add(repo)
  6127. self.session.commit()
  6128. output = self.app.post(
  6129. "/test/pull-request/1/comment",
  6130. data=data,
  6131. follow_redirects=True,
  6132. )
  6133. self.assertEqual(output.status_code, 404)
  6134. @patch("pagure.lib.notify.send_email")
  6135. def test_pull_request_drop_comment(self, send_email):
  6136. """ Test the pull_request_drop_comment endpoint. """
  6137. send_email.return_value = True
  6138. self.test_pull_request_add_comment()
  6139. # Project w/ pull-request
  6140. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  6141. settings = repo.settings
  6142. settings["pull_requests"] = True
  6143. repo.settings = settings
  6144. self.session.add(repo)
  6145. self.session.commit()
  6146. user = tests.FakeUser()
  6147. user.username = "foo"
  6148. with tests.user_set(self.app.application, user):
  6149. output = self.app.post("/foo/pull-request/1/comment/drop")
  6150. self.assertEqual(output.status_code, 404)
  6151. output = self.app.post("/test/pull-request/100/comment/drop")
  6152. self.assertEqual(output.status_code, 404)
  6153. output = self.app.post(
  6154. "/test/pull-request/1/comment/drop", follow_redirects=True
  6155. )
  6156. self.assertEqual(output.status_code, 200)
  6157. output_text = output.get_data(as_text=True)
  6158. self.assertIn(
  6159. '<h4 class="ml-1">\n <div>\n '
  6160. '<span class="fa fa-fw text-success fa-arrow-circle-down pt-1"></span>\n '
  6161. '<span class="text-success '
  6162. 'font-weight-bold">#1</span>\n '
  6163. '<span class="font-weight-bold">\n '
  6164. "PR from the feature branch\n",
  6165. output_text,
  6166. )
  6167. # self.assertIn('href="#comment-1">¶</a>', output_text)
  6168. self.assertIn(
  6169. "<p>This look alright but we can do better</p>", output_text
  6170. )
  6171. csrf_token = self.get_csrf(output=output)
  6172. # Invalid comment id
  6173. data = {"csrf_token": csrf_token, "drop_comment": "10"}
  6174. output = self.app.post(
  6175. "/test/pull-request/1/comment/drop",
  6176. data=data,
  6177. follow_redirects=True,
  6178. )
  6179. self.assertEqual(output.status_code, 404)
  6180. data["drop_comment"] = "1"
  6181. output = self.app.post(
  6182. "/test/pull-request/1/comment/drop",
  6183. data=data,
  6184. follow_redirects=True,
  6185. )
  6186. self.assertEqual(output.status_code, 403)
  6187. user.username = "pingou"
  6188. with tests.user_set(self.app.application, user):
  6189. # Drop comment
  6190. output = self.app.post(
  6191. "/test/pull-request/1/comment/drop",
  6192. data=data,
  6193. follow_redirects=True,
  6194. )
  6195. self.assertEqual(output.status_code, 200)
  6196. output_text = output.get_data(as_text=True)
  6197. self.assertIn(
  6198. '<h4 class="ml-1">\n <div>\n '
  6199. '<span class="fa fa-fw text-success fa-arrow-circle-down pt-1"></span>\n '
  6200. '<span class="text-success '
  6201. 'font-weight-bold">#1</span>\n '
  6202. '<span class="font-weight-bold">\n '
  6203. "PR from the feature branch\n",
  6204. output_text,
  6205. )
  6206. self.assertIn("Comment removed", output_text)
  6207. # Project w/o pull-request
  6208. repo = pagure.lib.query.get_authorized_project(
  6209. self.session, "test"
  6210. )
  6211. settings = repo.settings
  6212. settings["pull_requests"] = False
  6213. repo.settings = settings
  6214. self.session.add(repo)
  6215. self.session.commit()
  6216. output = self.app.post(
  6217. "/test/pull-request/1/comment/drop",
  6218. data=data,
  6219. follow_redirects=True,
  6220. )
  6221. self.assertEqual(output.status_code, 404)
  6222. @patch.dict(
  6223. "pagure.config.config", {"FEDORA_MESSAGING_NOTIFICATIONS": True}
  6224. )
  6225. @patch("pagure.lib.notify.send_email")
  6226. def test_pull_request_edit_comment(self, send_email):
  6227. """ Test the pull request edit comment endpoint """
  6228. send_email.return_value = True
  6229. self.test_request_pull()
  6230. user = tests.FakeUser()
  6231. user.username = "pingou"
  6232. with tests.user_set(self.app.application, user):
  6233. # Repo 'foo' does not exist so it is verifying that condition
  6234. output = self.app.post("/foo/pull-request/1/comment/1/edit")
  6235. self.assertEqual(output.status_code, 404)
  6236. # Here no comment is present in the PR so its verifying that condition
  6237. output = self.app.post("/test/pull-request/100/comment/100/edit")
  6238. self.assertEqual(output.status_code, 404)
  6239. output = self.app.post("/test/pull-request/1/comment")
  6240. self.assertEqual(output.status_code, 200)
  6241. # Creating comment to play with
  6242. self.assertTrue(
  6243. output.get_data(as_text=True).startswith(
  6244. '\n<section class="add_comment">'
  6245. )
  6246. )
  6247. csrf_token = self.get_csrf(output=output)
  6248. data = {
  6249. "csrf_token": csrf_token,
  6250. "comment": "This look alright but we can do better",
  6251. }
  6252. output = self.app.post(
  6253. "/test/pull-request/1/comment",
  6254. data=data,
  6255. follow_redirects=True,
  6256. )
  6257. self.assertEqual(output.status_code, 200)
  6258. output_text = output.get_data(as_text=True)
  6259. self.assertIn(
  6260. '<h4 class="ml-1">\n <div>\n '
  6261. '<span class="fa fa-fw text-success fa-arrow-circle-down pt-1"></span>\n '
  6262. '<span class="text-success '
  6263. 'font-weight-bold">#1</span>\n '
  6264. '<span class="font-weight-bold">\n '
  6265. "PR from the feature branch\n",
  6266. output_text,
  6267. )
  6268. self.assertIn("Comment added", output_text)
  6269. # Check if the comment is there
  6270. self.assertIn(
  6271. "<p>This look alright but we can do better</p>", output_text
  6272. )
  6273. output = self.app.get("/test/pull-request/1/comment/1/edit")
  6274. self.assertEqual(output.status_code, 200)
  6275. output_text = output.get_data(as_text=True)
  6276. self.assertIn('<section class="edit_comment">', output_text)
  6277. # Checking if the comment is there in the update page
  6278. self.assertIn(
  6279. "This look alright but we can do better</textarea>",
  6280. output_text,
  6281. )
  6282. csrf_token = self.get_csrf(output=output)
  6283. data = {
  6284. "csrf_token": csrf_token,
  6285. "update_comment": "This look alright but we can do better than this.",
  6286. }
  6287. with testing.mock_sends(
  6288. pagure_messages.PullRequestCommentEditedV1(
  6289. topic="pagure.pull-request.comment.edited",
  6290. body={
  6291. "pullrequest": {
  6292. "id": 1,
  6293. "full_url": "http://localhost.localdomain/test/pull-request/1",
  6294. "uid": ANY,
  6295. "title": "PR from the feature branch",
  6296. "branch": "master",
  6297. "project": {
  6298. "id": 1,
  6299. "name": "test",
  6300. "fullname": "test",
  6301. "url_path": "test",
  6302. "full_url": "http://localhost.localdomain/test",
  6303. "description": "test project #1",
  6304. "namespace": None,
  6305. "parent": None,
  6306. "date_created": ANY,
  6307. "date_modified": ANY,
  6308. "user": {
  6309. "name": "pingou",
  6310. "fullname": "PY C",
  6311. "url_path": "user/pingou",
  6312. "full_url": "http://localhost.localdomain/user/pingou",
  6313. },
  6314. "access_users": {
  6315. "owner": ["pingou"],
  6316. "admin": [],
  6317. "commit": [],
  6318. "collaborator": [],
  6319. "ticket": [],
  6320. },
  6321. "access_groups": {
  6322. "admin": [],
  6323. "commit": [],
  6324. "collaborator": [],
  6325. "ticket": [],
  6326. },
  6327. "tags": [],
  6328. "priorities": {},
  6329. "custom_keys": [],
  6330. "close_status": [
  6331. "Invalid",
  6332. "Insufficient data",
  6333. "Fixed",
  6334. "Duplicate",
  6335. ],
  6336. "milestones": {},
  6337. },
  6338. "branch_from": "feature",
  6339. "repo_from": {
  6340. "id": 1,
  6341. "name": "test",
  6342. "fullname": "test",
  6343. "full_url": "http://localhost.localdomain/test",
  6344. "url_path": "test",
  6345. "description": "test project #1",
  6346. "namespace": None,
  6347. "parent": None,
  6348. "date_created": ANY,
  6349. "date_modified": ANY,
  6350. "user": {
  6351. "name": "pingou",
  6352. "fullname": "PY C",
  6353. "url_path": "user/pingou",
  6354. "full_url": "http://localhost.localdomain/user/pingou",
  6355. },
  6356. "access_users": {
  6357. "owner": ["pingou"],
  6358. "admin": [],
  6359. "commit": [],
  6360. "collaborator": [],
  6361. "ticket": [],
  6362. },
  6363. "access_groups": {
  6364. "admin": [],
  6365. "commit": [],
  6366. "collaborator": [],
  6367. "ticket": [],
  6368. },
  6369. "tags": [],
  6370. "priorities": {},
  6371. "custom_keys": [],
  6372. "close_status": [
  6373. "Invalid",
  6374. "Insufficient data",
  6375. "Fixed",
  6376. "Duplicate",
  6377. ],
  6378. "milestones": {},
  6379. },
  6380. "remote_git": None,
  6381. "date_created": ANY,
  6382. "updated_on": ANY,
  6383. "last_updated": ANY,
  6384. "closed_at": None,
  6385. "user": {
  6386. "name": "pingou",
  6387. "fullname": "PY C",
  6388. "url_path": "user/pingou",
  6389. "full_url": "http://localhost.localdomain/user/pingou",
  6390. },
  6391. "assignee": None,
  6392. "status": "Open",
  6393. "commit_start": ANY,
  6394. "commit_stop": ANY,
  6395. "closed_by": None,
  6396. "initial_comment": None,
  6397. "cached_merge_status": "unknown",
  6398. "threshold_reached": None,
  6399. "tags": [],
  6400. "comments": [],
  6401. },
  6402. "project": {
  6403. "id": 1,
  6404. "name": "test",
  6405. "fullname": "test",
  6406. "url_path": "test",
  6407. "full_url": "http://localhost.localdomain/test",
  6408. "description": "test project #1",
  6409. "namespace": None,
  6410. "parent": None,
  6411. "date_created": ANY,
  6412. "date_modified": ANY,
  6413. "user": {
  6414. "name": "pingou",
  6415. "fullname": "PY C",
  6416. "url_path": "user/pingou",
  6417. "full_url": "http://localhost.localdomain/user/pingou",
  6418. },
  6419. "access_users": {
  6420. "owner": ["pingou"],
  6421. "admin": [],
  6422. "commit": [],
  6423. "collaborator": [],
  6424. "ticket": [],
  6425. },
  6426. "access_groups": {
  6427. "admin": [],
  6428. "commit": [],
  6429. "collaborator": [],
  6430. "ticket": [],
  6431. },
  6432. "tags": [],
  6433. "priorities": {},
  6434. "custom_keys": [],
  6435. "close_status": [
  6436. "Invalid",
  6437. "Insufficient data",
  6438. "Fixed",
  6439. "Duplicate",
  6440. ],
  6441. "milestones": {},
  6442. },
  6443. "comment": {
  6444. "id": 1,
  6445. "commit": None,
  6446. "tree": None,
  6447. "filename": None,
  6448. "line": None,
  6449. "comment": "This look alright but we can do better than this.",
  6450. "parent": None,
  6451. "date_created": ANY,
  6452. "user": {
  6453. "name": "pingou",
  6454. "fullname": "PY C",
  6455. "url_path": "user/pingou",
  6456. "full_url": "http://localhost.localdomain/user/pingou",
  6457. },
  6458. "edited_on": ANY,
  6459. "editor": {
  6460. "name": "pingou",
  6461. "fullname": "PY C",
  6462. "url_path": "user/pingou",
  6463. "full_url": "http://localhost.localdomain/user/pingou",
  6464. },
  6465. "notification": False,
  6466. "reactions": {},
  6467. },
  6468. "agent": "pingou",
  6469. },
  6470. )
  6471. ):
  6472. output = self.app.post(
  6473. "/test/pull-request/1/comment/1/edit",
  6474. data=data,
  6475. follow_redirects=True,
  6476. )
  6477. output_text = output.get_data(as_text=True)
  6478. # Checking if the comment is updated in the main page
  6479. self.assertIn(
  6480. "<p>This look alright but we can do better than this.</p>",
  6481. output_text,
  6482. )
  6483. self.assertIn(
  6484. '<h4 class="ml-1">\n <div>\n '
  6485. '<span class="fa fa-fw text-success fa-arrow-circle-down pt-1"></span>\n '
  6486. '<span class="text-success '
  6487. 'font-weight-bold">#1</span>\n '
  6488. '<span class="font-weight-bold">\n '
  6489. "PR from the feature branch\n",
  6490. output_text,
  6491. )
  6492. # Checking if Edited by User is there or not
  6493. pattern = (
  6494. re.escape("<small>Edited ")
  6495. + '<span title="[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:'
  6496. + '[0-9]{2} UTC" data-toggle="tooltip">(just now|seconds ago)</span>'
  6497. + re.escape(" by pingou </small>")
  6498. )
  6499. self.assertIsNotNone(re.search(pattern, output_text))
  6500. self.assertIn("Comment updated", output_text)
  6501. # Project w/o pull-request
  6502. repo = pagure.lib.query.get_authorized_project(
  6503. self.session, "test"
  6504. )
  6505. settings = repo.settings
  6506. settings["pull_requests"] = False
  6507. repo.settings = settings
  6508. self.session.add(repo)
  6509. self.session.commit()
  6510. output = self.app.post(
  6511. "/test/pull-request/1/comment/edit/1",
  6512. data=data,
  6513. follow_redirects=True,
  6514. )
  6515. self.assertEqual(output.status_code, 404)
  6516. @patch("pagure.lib.notify.send_email")
  6517. def test_merge_request_pull_FF_w_merge_commit(self, send_email):
  6518. """Test the merge_request_pull endpoint with a FF PR but with a
  6519. merge commit.
  6520. """
  6521. send_email.return_value = True
  6522. self.test_request_pull()
  6523. user = tests.FakeUser()
  6524. with tests.user_set(self.app.application, user):
  6525. output = self.app.get("/test/pull-request/1")
  6526. self.assertEqual(output.status_code, 200)
  6527. csrf_token = self.get_csrf(output=output)
  6528. # No CSRF
  6529. output = self.app.post(
  6530. "/test/pull-request/1/merge", data={}, follow_redirects=True
  6531. )
  6532. self.assertEqual(output.status_code, 200)
  6533. output_text = output.get_data(as_text=True)
  6534. self.assertIn(
  6535. "<title>PR#1: PR from the feature branch - test\n - "
  6536. "Pagure</title>",
  6537. output_text,
  6538. )
  6539. self.assertIn(
  6540. '<h4 class="ml-1">\n <div>\n '
  6541. '<span class="fa fa-fw text-success fa-arrow-circle-down pt-1"></span>\n '
  6542. '<span class="text-success '
  6543. 'font-weight-bold">#1</span>\n '
  6544. '<span class="font-weight-bold">\n '
  6545. "PR from the feature branch\n",
  6546. output_text,
  6547. )
  6548. self.assertIn(
  6549. 'title="View file as of 2a552bb">sources</a>', output_text
  6550. )
  6551. # Wrong project
  6552. data = {"csrf_token": csrf_token}
  6553. output = self.app.post(
  6554. "/foobar/pull-request/100/merge",
  6555. data=data,
  6556. follow_redirects=True,
  6557. )
  6558. self.assertEqual(output.status_code, 404)
  6559. # Wrong project
  6560. data = {"csrf_token": csrf_token}
  6561. output = self.app.post(
  6562. "/test/pull-request/1/merge", data=data, follow_redirects=True
  6563. )
  6564. self.assertEqual(output.status_code, 403)
  6565. user.username = "pingou"
  6566. with tests.user_set(self.app.application, user):
  6567. # Wrong request id
  6568. data = {"csrf_token": csrf_token}
  6569. output = self.app.post(
  6570. "/test/pull-request/100/merge",
  6571. data=data,
  6572. follow_redirects=True,
  6573. )
  6574. self.assertEqual(output.status_code, 404)
  6575. # Project requiring a merge commit
  6576. repo = pagure.lib.query.get_authorized_project(
  6577. self.session, "test"
  6578. )
  6579. settings = repo.settings
  6580. settings["always_merge"] = True
  6581. repo.settings = settings
  6582. self.session.add(repo)
  6583. self.session.commit()
  6584. # Merge
  6585. output = self.app.post(
  6586. "/test/pull-request/1/merge", data=data, follow_redirects=True
  6587. )
  6588. self.assertEqual(output.status_code, 200)
  6589. output = self.app.get("/test/commits")
  6590. output_text = output.get_data(as_text=True)
  6591. self.assertIn(
  6592. "<title>Commits - test - Pagure</title>", output_text
  6593. )
  6594. self.assertIn("Merge #1 `PR from the feature branch`", output_text)
  6595. self.assertIn("A commit on branch feature", output_text)
  6596. # Check if the closing notification was added
  6597. output = self.app.get("/test/pull-request/1")
  6598. output_text = output.get_data(as_text=True)
  6599. self.assertIsNotNone(re.search(MERGED_PATTERN, output_text))
  6600. @patch("pagure.lib.notify.send_email")
  6601. def test_internal_endpoint_main_ahead(self, send_email):
  6602. """Test the new_request_pull endpoint when the main repo is ahead
  6603. of the fork.
  6604. """
  6605. send_email.return_value = True
  6606. tests.create_projects(self.session)
  6607. tests.create_projects_git(
  6608. os.path.join(self.path, "requests"), bare=True
  6609. )
  6610. set_up_git_repo(
  6611. self.session, self.path, new_project=None, branch_from="feature"
  6612. )
  6613. gitrepo = os.path.join(self.path, "repos", "test.git")
  6614. repo = pygit2.init_repository(gitrepo, bare=True)
  6615. # Make the main repo be ahead of the fork
  6616. # First commit
  6617. newpath = tempfile.mkdtemp(prefix="pagure-test")
  6618. repopath = os.path.join(newpath, "test")
  6619. clone_repo = pygit2.clone_repository(gitrepo, repopath)
  6620. # Create a file in that git repo
  6621. with open(os.path.join(repopath, "testfile"), "w") as stream:
  6622. stream.write("foo\n bar")
  6623. clone_repo.index.add("testfile")
  6624. clone_repo.index.write()
  6625. # Commits the files added
  6626. last_commit = clone_repo.revparse_single("HEAD")
  6627. tree = clone_repo.index.write_tree()
  6628. author = pygit2.Signature("Alice Author", "alice@authors.tld")
  6629. committer = pygit2.Signature("Cecil Committer", "cecil@committers.tld")
  6630. clone_repo.create_commit(
  6631. "refs/heads/master", # the name of the reference to update
  6632. author,
  6633. committer,
  6634. "Add testfile file for testing",
  6635. # binary string representing the tree object ID
  6636. tree,
  6637. # list of binary strings representing parents of the new commit
  6638. [last_commit.oid.hex],
  6639. )
  6640. # Second commit
  6641. with open(os.path.join(repopath, "testfile"), "a") as stream:
  6642. stream.write("\nfoo2\n bar2")
  6643. clone_repo.index.add("testfile")
  6644. clone_repo.index.write()
  6645. # Commits the files added
  6646. last_commit = clone_repo.revparse_single("HEAD")
  6647. tree = clone_repo.index.write_tree()
  6648. author = pygit2.Signature("Alice Author", "alice@authors.tld")
  6649. committer = pygit2.Signature("Cecil Committer", "cecil@committers.tld")
  6650. clone_repo.create_commit(
  6651. "refs/heads/master", # the name of the reference to update
  6652. author,
  6653. committer,
  6654. "Add a second commit to testfile for testing",
  6655. # binary string representing the tree object ID
  6656. tree,
  6657. # list of binary strings representing parents of the new commit
  6658. [last_commit.oid.hex],
  6659. )
  6660. # Third commit
  6661. with open(os.path.join(repopath, "testfile"), "a") as stream:
  6662. stream.write("\nfoo3\n bar3")
  6663. clone_repo.index.add("testfile")
  6664. clone_repo.index.write()
  6665. # Commits the files added
  6666. last_commit = clone_repo.revparse_single("HEAD")
  6667. tree = clone_repo.index.write_tree()
  6668. author = pygit2.Signature("Alice Author", "alice@authors.tld")
  6669. committer = pygit2.Signature("Cecil Committer", "cecil@committers.tld")
  6670. clone_repo.create_commit(
  6671. "refs/heads/master", # the name of the reference to update
  6672. author,
  6673. committer,
  6674. "Add a third commit to testfile for testing",
  6675. # binary string representing the tree object ID
  6676. tree,
  6677. # list of binary strings representing parents of the new commit
  6678. [last_commit.oid.hex],
  6679. )
  6680. refname = "refs/heads/master:refs/heads/master"
  6681. ori_remote = clone_repo.remotes[0]
  6682. PagureRepo.push(ori_remote, refname)
  6683. shutil.rmtree(newpath)
  6684. user = tests.FakeUser()
  6685. user.username = "foo"
  6686. with tests.user_set(self.app.application, user):
  6687. csrf_token = self.get_csrf()
  6688. output = self.app.post(
  6689. "/pv/pull-request/ready",
  6690. data={"repo": "test", "csrf_token": csrf_token},
  6691. )
  6692. self.assertEqual(output.status_code, 200)
  6693. data = json.loads(output.get_data(as_text=True))
  6694. self.assertEqual(sorted(data.keys()), ["code", "task"])
  6695. self.assertEqual(data["code"], "OK")
  6696. @patch("pagure.lib.notify.send_email")
  6697. def test_fork_edit_file(self, send_email):
  6698. """ Test the fork_edit file endpoint. """
  6699. send_email.return_value = True
  6700. # Git repo not found
  6701. output = self.app.post("fork_edit/test/edit/master/f/sources")
  6702. self.assertEqual(output.status_code, 404)
  6703. tests.create_projects(self.session)
  6704. for folder in ["docs", "tickets", "requests", "repos"]:
  6705. tests.create_projects_git(
  6706. os.path.join(self.path, folder), bare=True
  6707. )
  6708. # User not logged in
  6709. output = self.app.post("fork_edit/test/edit/master/f/sources")
  6710. self.assertEqual(output.status_code, 302)
  6711. user = tests.FakeUser()
  6712. user.username = "pingou"
  6713. with tests.user_set(self.app.application, user):
  6714. # Invalid request
  6715. output = self.app.post("fork_edit/test/edit/master/f/source")
  6716. self.assertEqual(output.status_code, 400)
  6717. output = self.app.get("/new/")
  6718. self.assertEqual(output.status_code, 200)
  6719. self.assertIn(
  6720. "<strong>Create new Project</strong>",
  6721. output.get_data(as_text=True),
  6722. )
  6723. csrf_token = self.get_csrf(output=output)
  6724. data = {"csrf_token": csrf_token}
  6725. # No files can be found since they are not added
  6726. output = self.app.post(
  6727. "fork_edit/test/edit/master/f/sources",
  6728. data=data,
  6729. follow_redirects=True,
  6730. )
  6731. self.assertEqual(output.status_code, 404)
  6732. user = tests.FakeUser()
  6733. user.username = "foo"
  6734. with tests.user_set(self.app.application, user):
  6735. data = {"csrf_token": csrf_token}
  6736. # Invalid request
  6737. output = self.app.post(
  6738. "fork_edit/test/edit/master/f/sources", follow_redirects=True
  6739. )
  6740. self.assertEqual(output.status_code, 400)
  6741. # Add content to the repo
  6742. tests.add_content_git_repo(
  6743. os.path.join(pagure.config.config["GIT_FOLDER"], "test.git")
  6744. )
  6745. tests.add_readme_git_repo(
  6746. os.path.join(pagure.config.config["GIT_FOLDER"], "test.git")
  6747. )
  6748. tests.add_binary_git_repo(
  6749. os.path.join(pagure.config.config["GIT_FOLDER"], "test.git"),
  6750. "test.jpg",
  6751. )
  6752. # Check if button exists
  6753. output = self.app.get("/test/blob/master/f/sources")
  6754. self.assertEqual(output.status_code, 200)
  6755. self.assertIn(
  6756. "Fork and Edit\n </button>\n",
  6757. output.get_data(as_text=True),
  6758. )
  6759. # Check fork-edit doesn't show for binary files
  6760. output = self.app.get("/test/blob/master/f/test.jpg")
  6761. self.assertEqual(output.status_code, 200)
  6762. self.assertNotIn(b"<html", output.data)
  6763. # Check for edit panel
  6764. output = self.app.post(
  6765. "fork_edit/test/edit/master/f/sources",
  6766. data=data,
  6767. follow_redirects=True,
  6768. )
  6769. self.assertEqual(output.status_code, 200)
  6770. output_text = output.get_data(as_text=True)
  6771. self.assertIn(
  6772. '<li><a href="/fork/foo/test/tree/master">'
  6773. '<span class="fa fa-random"></span>&nbsp; master</a>'
  6774. '</li><li class="active"><span class="fa fa-file">'
  6775. "</span>&nbsp; sources</li>",
  6776. output_text,
  6777. )
  6778. self.assertIn(
  6779. '<textarea id="textareaCode" name="content">foo\n bar</textarea>',
  6780. output_text,
  6781. )
  6782. # Check for edit panel- Fork already done
  6783. output = self.app.post(
  6784. "fork_edit/test/edit/master/f/sources",
  6785. data=data,
  6786. follow_redirects=True,
  6787. )
  6788. self.assertEqual(output.status_code, 200)
  6789. output_text = output.get_data(as_text=True)
  6790. self.assertIn("<title>Edit - test - Pagure</title>", output_text)
  6791. self.assertIn(
  6792. "You had already forked " "this project", output_text
  6793. )
  6794. self.assertIn(
  6795. '<i class="fa fa-code-fork fa-fw"></i> View Upstream',
  6796. output_text,
  6797. )
  6798. self.assertIn(
  6799. '<li><a href="/fork/foo/test/tree/master">'
  6800. '<span class="fa fa-random"></span>&nbsp; master</a>'
  6801. '</li><li class="active"><span class="fa fa-file">'
  6802. "</span>&nbsp; sources</li>",
  6803. output_text,
  6804. )
  6805. self.assertIn(
  6806. '<textarea id="textareaCode" name="content">foo\n bar</textarea>',
  6807. output_text,
  6808. )
  6809. # View what's supposed to be an image
  6810. output = self.app.post(
  6811. "fork_edit/test/edit/master/f/test.jpg",
  6812. data=data,
  6813. follow_redirects=True,
  6814. )
  6815. self.assertEqual(output.status_code, 400)
  6816. self.assertIn(
  6817. b"<p>Cannot edit binary files</p>",
  6818. output.data,
  6819. )
  6820. # Check fork-edit shows when user is not logged in
  6821. output = self.app.get("/test/blob/master/f/sources")
  6822. self.assertEqual(output.status_code, 200)
  6823. self.assertIn(
  6824. "Fork and Edit\n </button>\n",
  6825. output.get_data(as_text=True),
  6826. )
  6827. # Check if fork-edit shows for different user
  6828. user.username = "pingou"
  6829. with tests.user_set(self.app.application, user):
  6830. # Check if button exists
  6831. output = self.app.get("/test/blob/master/f/sources")
  6832. self.assertEqual(output.status_code, 200)
  6833. self.assertIn(
  6834. "Edit in your fork\n </button>\n",
  6835. output.get_data(as_text=True),
  6836. )
  6837. # Check fork-edit doesn't show for binary
  6838. output = self.app.get("/test/blob/master/f/test.jpg")
  6839. self.assertEqual(output.status_code, 200)
  6840. self.assertNotIn(b"<html", output.data)
  6841. @patch("pagure.lib.notify.send_email", MagicMock(return_value=True))
  6842. def test_fork_edit_file_namespace(self):
  6843. """ Test the fork_edit file endpoint on a namespaced project. """
  6844. tests.create_projects(self.session)
  6845. for folder in ["docs", "tickets", "requests", "repos"]:
  6846. tests.create_projects_git(
  6847. os.path.join(self.path, folder), bare=True
  6848. )
  6849. # User not logged in
  6850. output = self.app.post(
  6851. "fork_edit/somenamespace/test3/edit/master/f/sources"
  6852. )
  6853. self.assertEqual(output.status_code, 302)
  6854. user = tests.FakeUser()
  6855. user.username = "pingou"
  6856. with tests.user_set(self.app.application, user):
  6857. # Invalid request
  6858. output = self.app.post(
  6859. "fork_edit/somenamespace/test3/edit/master/f/sources"
  6860. )
  6861. self.assertEqual(output.status_code, 400)
  6862. output = self.app.get("/new/")
  6863. self.assertEqual(output.status_code, 200)
  6864. self.assertIn(
  6865. "<strong>Create new Project</strong>",
  6866. output.get_data(as_text=True),
  6867. )
  6868. csrf_token = self.get_csrf(output=output)
  6869. data = {"csrf_token": csrf_token}
  6870. # No files can be found since they are not added
  6871. output = self.app.post(
  6872. "fork_edit/somenamespace/test3/edit/master/f/sources",
  6873. data=data,
  6874. follow_redirects=True,
  6875. )
  6876. self.assertEqual(output.status_code, 404)
  6877. user = tests.FakeUser()
  6878. user.username = "foo"
  6879. with tests.user_set(self.app.application, user):
  6880. data = {"csrf_token": csrf_token}
  6881. # Invalid request
  6882. output = self.app.post(
  6883. "fork_edit/somenamespace/test3/edit/master/f/sources",
  6884. follow_redirects=True,
  6885. )
  6886. self.assertEqual(output.status_code, 400)
  6887. # Add content to the repo
  6888. tests.add_content_git_repo(
  6889. os.path.join(
  6890. pagure.config.config["GIT_FOLDER"],
  6891. "somenamespace",
  6892. "test3.git",
  6893. )
  6894. )
  6895. tests.add_readme_git_repo(
  6896. os.path.join(
  6897. pagure.config.config["GIT_FOLDER"],
  6898. "somenamespace",
  6899. "test3.git",
  6900. )
  6901. )
  6902. tests.add_binary_git_repo(
  6903. os.path.join(
  6904. pagure.config.config["GIT_FOLDER"],
  6905. "somenamespace",
  6906. "test3.git",
  6907. ),
  6908. "test.jpg",
  6909. )
  6910. # Check if button exists
  6911. output = self.app.get("/somenamespace/test3/blob/master/f/sources")
  6912. self.assertEqual(output.status_code, 200)
  6913. self.assertIn(
  6914. "Fork and Edit\n </button>\n",
  6915. output.get_data(as_text=True),
  6916. )
  6917. # Check fork-edit doesn't show for binary files
  6918. output = self.app.get(
  6919. "/somenamespace/test3/blob/master/f/test.jpg"
  6920. )
  6921. self.assertEqual(output.status_code, 200)
  6922. self.assertNotIn(b"<html", output.data)
  6923. # Check for edit panel
  6924. output = self.app.post(
  6925. "fork_edit/somenamespace/test3/edit/master/f/sources",
  6926. data=data,
  6927. follow_redirects=True,
  6928. )
  6929. self.assertEqual(output.status_code, 200)
  6930. output_text = output.get_data(as_text=True)
  6931. self.assertIn(
  6932. "<title>Edit - somenamespace/test3 - Pagure</title>",
  6933. output_text,
  6934. )
  6935. self.assertIn(
  6936. '<i class="fa fa-code-fork fa-fw"></i> View Upstream',
  6937. output_text,
  6938. )
  6939. self.assertIn(
  6940. '<li><a href="/fork/foo/somenamespace/test3/tree/master">'
  6941. '<span class="fa fa-random"></span>&nbsp; master</a>'
  6942. '</li><li class="active"><span class="fa fa-file">'
  6943. "</span>&nbsp; sources</li>",
  6944. output_text,
  6945. )
  6946. self.assertIn(
  6947. '<textarea id="textareaCode" name="content">foo\n bar</textarea>',
  6948. output_text,
  6949. )
  6950. # Check for edit panel - while the project was already forked
  6951. output = self.app.post(
  6952. "fork_edit/somenamespace/test3/edit/master/f/sources",
  6953. data=data,
  6954. follow_redirects=True,
  6955. )
  6956. self.assertEqual(output.status_code, 200)
  6957. output_text = output.get_data(as_text=True)
  6958. self.assertIn(
  6959. "<title>Edit - somenamespace/test3 - Pagure</title>",
  6960. output_text,
  6961. )
  6962. self.assertIn(
  6963. "You had already forked " "this project", output_text
  6964. )
  6965. self.assertIn(
  6966. '<i class="fa fa-code-fork fa-fw"></i> View Upstream',
  6967. output_text,
  6968. )
  6969. self.assertIn(
  6970. '<li><a href="/fork/foo/somenamespace/test3/tree/master">'
  6971. '<span class="fa fa-random"></span>&nbsp; master</a>'
  6972. '</li><li class="active"><span class="fa fa-file">'
  6973. "</span>&nbsp; sources</li>",
  6974. output_text,
  6975. )
  6976. self.assertIn(
  6977. '<textarea id="textareaCode" name="content">foo\n bar</textarea>',
  6978. output_text,
  6979. )
  6980. @patch("pagure.lib.notify.send_email")
  6981. def test_fork_without_main_repo(self, send_email):
  6982. """ Test the fork without the main repo. """
  6983. send_email.return_value = True
  6984. tests.create_projects(self.session)
  6985. # Create a fork with no parent i.e parent_id = None
  6986. item = pagure.lib.model.Project(
  6987. user_id=2, # foo
  6988. name="test",
  6989. description="test project #1",
  6990. hook_token="aaabbb",
  6991. is_fork=True,
  6992. parent_id=None,
  6993. )
  6994. self.session.add(item)
  6995. self.session.commit()
  6996. # Get fork project
  6997. project = pagure.lib.query._get_project(self.session, "test", "foo")
  6998. # Pull-requests and issue-trackers are off for forks
  6999. # lib function is not used here so mannually turning them off
  7000. project_settings = project.settings
  7001. project_settings["pull_requests"] = False
  7002. project_settings["issue_tracker"] = False
  7003. project.settings = project_settings
  7004. self.session.add(project)
  7005. self.session.commit()
  7006. tests.create_projects_git(
  7007. os.path.join(self.path, "repos", "forks", "foo"), bare=True
  7008. )
  7009. # Create a git repo to play with
  7010. gitrepo = os.path.join(self.path, "repos", "test.git")
  7011. self.assertFalse(os.path.exists(gitrepo))
  7012. os.makedirs(gitrepo)
  7013. repo = pygit2.init_repository(gitrepo, bare=True)
  7014. # Create a fork of this repo
  7015. newpath = tempfile.mkdtemp(prefix="pagure-fork-test")
  7016. gitrepo = os.path.join(self.path, "repos", "forks", "foo", "test.git")
  7017. new_repo = pygit2.clone_repository(gitrepo, newpath)
  7018. tests.add_content_git_repo(gitrepo)
  7019. # UI test for deleted main
  7020. output = self.app.get("/fork/foo/test")
  7021. self.assertEqual(output.status_code, 200)
  7022. self.assertIn(
  7023. "Forked from a deleted repository", output.get_data(as_text=True)
  7024. )
  7025. # Testing commit endpoint
  7026. output = self.app.get("/fork/foo/test/commits/master")
  7027. self.assertEqual(output.status_code, 200)
  7028. self.assertIn(
  7029. 'Commits <span class="badge badge-secondary"> 2</span>\n',
  7030. output.get_data(as_text=True),
  7031. )
  7032. # Test pull-request endpoint
  7033. output = self.app.get("/fork/foo/test/pull-requests")
  7034. self.assertEqual(output.status_code, 404)
  7035. # Test issue-tracker endpoint
  7036. output = self.app.get("/fork/foo/test/issues")
  7037. self.assertEqual(output.status_code, 404)
  7038. shutil.rmtree(newpath)
  7039. def _set_up_for_reaction_test(self):
  7040. self.session.add(
  7041. pagure.lib.model.User(
  7042. user="jdoe",
  7043. fullname="John Doe",
  7044. password=b"password",
  7045. default_email="jdoe@example.com",
  7046. )
  7047. )
  7048. self.session.commit()
  7049. tests.create_projects(self.session)
  7050. tests.create_projects_git(
  7051. os.path.join(self.path, "requests"), bare=True
  7052. )
  7053. set_up_git_repo(
  7054. self.session, self.path, new_project=None, branch_from="feature"
  7055. )
  7056. pagure.lib.query.get_authorized_project(self.session, "test")
  7057. request = pagure.lib.query.search_pull_requests(
  7058. self.session, requestid=1, project_id=1
  7059. )
  7060. pagure.lib.query.add_pull_request_comment(
  7061. self.session,
  7062. request=request,
  7063. commit=None,
  7064. tree_id=None,
  7065. filename=None,
  7066. row=None,
  7067. comment="Hello",
  7068. user="jdoe",
  7069. )
  7070. self.session.commit()
  7071. @patch("pagure.lib.notify.send_email")
  7072. def test_add_reaction(self, send_email):
  7073. """ Test the request_pull endpoint. """
  7074. send_email.return_value = True
  7075. self._set_up_for_reaction_test()
  7076. user = tests.FakeUser()
  7077. user.username = "pingou"
  7078. with tests.user_set(self.app.application, user):
  7079. output = self.app.get("/test/pull-request/1")
  7080. self.assertEqual(output.status_code, 200)
  7081. data = {
  7082. "csrf_token": self.get_csrf(output=output),
  7083. "reaction": "Thumbs up",
  7084. }
  7085. output = self.app.post(
  7086. "/test/pull-request/1/comment/1/react",
  7087. data=data,
  7088. follow_redirects=True,
  7089. )
  7090. self.assertEqual(output.status_code, 200)
  7091. # Load the page and check reaction is added.
  7092. output = self.app.get("/test/pull-request/1")
  7093. self.assertEqual(output.status_code, 200)
  7094. self.assertIn(
  7095. "Thumbs up sent by pingou", output.get_data(as_text=True)
  7096. )
  7097. @patch("pagure.lib.notify.send_email")
  7098. def test_add_reaction_unauthenticated(self, send_email):
  7099. """ Test the request_pull endpoint. """
  7100. send_email.return_value = True
  7101. self._set_up_for_reaction_test()
  7102. output = self.app.get("/test/pull-request/1")
  7103. self.assertEqual(output.status_code, 200)
  7104. data = {
  7105. "csrf_token": self.get_csrf(output=output),
  7106. "reaction": "Thumbs down",
  7107. }
  7108. output = self.app.post(
  7109. "/test/pull-request/1/comment/1/react",
  7110. data=data,
  7111. follow_redirects=False,
  7112. )
  7113. # Redirect to login page
  7114. self.assertEqual(output.status_code, 302)
  7115. self.assertIn("/login/", output.headers["Location"])
  7116. class TestTicketAccessEditPRMetadata(tests.Modeltests):
  7117. """Tests that people with ticket access on a project can edit the
  7118. meta-data of a PR"""
  7119. def setUp(self):
  7120. """ Set up the environnment, ran before every tests. """
  7121. super(TestTicketAccessEditPRMetadata, self).setUp()
  7122. tests.create_projects(self.session)
  7123. tests.create_projects_git(
  7124. os.path.join(self.path, "requests"), bare=True
  7125. )
  7126. set_up_git_repo(
  7127. self.session, self.path, new_project=None, branch_from="feature"
  7128. )
  7129. # Add user "foo" to the project "test"
  7130. repo = pagure.lib.query._get_project(self.session, "test")
  7131. msg = pagure.lib.query.add_user_to_project(
  7132. session=self.session,
  7133. project=repo,
  7134. new_user="foo",
  7135. user="pingou",
  7136. access="ticket",
  7137. )
  7138. self.session.commit()
  7139. self.assertEqual(msg, "User added")
  7140. def test_unauth_cannot_view_edit_metadata_ui(self):
  7141. """Test that unauthenticated users cannot view the edit the
  7142. metadata fields in the UI."""
  7143. output = self.app.get("/test/pull-request/1")
  7144. self.assertEqual(output.status_code, 200)
  7145. output_text = output.get_data(as_text=True)
  7146. self.assertIn(
  7147. "<title>PR#1: PR from the feature branch - test\n"
  7148. " - Pagure</title>",
  7149. output_text,
  7150. )
  7151. self.assertIn(
  7152. '<i class="fa fa-fw fa-pencil"></i></a>',
  7153. '<a class="btn btn-outline-primary border-0 btn-sm '
  7154. "issue-metadata-display editmetadatatoggle pointer inline-block"
  7155. '"><i class="fa fa-fw fa-pencil"></i></a>',
  7156. output_text,
  7157. )
  7158. self.assertNotIn(
  7159. '<form method="POST" action="/test/pull-request/1/update">',
  7160. output_text,
  7161. )
  7162. def test_admin_can_view_edit_metadata_ui(self):
  7163. """Test that admin users can view the edit the metadata fields in
  7164. the UI."""
  7165. user = tests.FakeUser(username="pingou")
  7166. with tests.user_set(self.app.application, user):
  7167. output = self.app.get("/test/pull-request/1")
  7168. self.assertEqual(output.status_code, 200)
  7169. output_text = output.get_data(as_text=True)
  7170. self.assertIn(
  7171. "<title>PR#1: PR from the feature branch - test\n"
  7172. " - Pagure</title>",
  7173. output_text,
  7174. )
  7175. self.assertIn(
  7176. '<i class="fa fa-fw fa-pencil"></i></a>',
  7177. '<a class="btn btn-outline-primary border-0 btn-sm '
  7178. "issue-metadata-display editmetadatatoggle pointer inline-block"
  7179. '"><i class="fa fa-fw fa-pencil"></i></a>',
  7180. output_text,
  7181. )
  7182. self.assertIn(
  7183. '<form method="POST" action="/test/pull-request/1/update">',
  7184. output_text,
  7185. )
  7186. def test_admin_can_edit_metadata_ui(self):
  7187. """ Test that admin users can edit the metadata in the UI. """
  7188. user = tests.FakeUser(username="pingou")
  7189. with tests.user_set(self.app.application, user):
  7190. data = {"csrf_token": self.get_csrf(), "user": "foo"}
  7191. output = self.app.post(
  7192. "/test/pull-request/1/update", data=data, follow_redirects=True
  7193. )
  7194. self.assertEqual(output.status_code, 200)
  7195. output_text = output.get_data(as_text=True)
  7196. self.assertIn(
  7197. "<title>PR#1: PR from the feature branch - test\n"
  7198. " - Pagure</title>",
  7199. output_text,
  7200. )
  7201. self.assertIn(
  7202. '<i class="fa fa-fw fa-pencil"></i></a>',
  7203. '<a class="btn btn-outline-primary border-0 btn-sm '
  7204. "issue-metadata-display editmetadatatoggle pointer inline-block"
  7205. '"><i class="fa fa-fw fa-pencil"></i></a>',
  7206. output_text,
  7207. )
  7208. self.assertIn(
  7209. '<form method="POST" action="/test/pull-request/1/update">',
  7210. output_text,
  7211. )
  7212. self.assertIn(
  7213. '<input value="foo"\n name="user" '
  7214. 'id="assignee" placeholder="username" >',
  7215. output_text,
  7216. )
  7217. def test_ticket_can_view_edit_metadata_ui(self):
  7218. """Test that users with ticket access can view the edit the
  7219. metadata fields in the UI."""
  7220. user = tests.FakeUser(username="foo")
  7221. with tests.user_set(self.app.application, user):
  7222. output = self.app.get("/test/pull-request/1")
  7223. self.assertEqual(output.status_code, 200)
  7224. output_text = output.get_data(as_text=True)
  7225. self.assertIn(
  7226. "<title>PR#1: PR from the feature branch - test\n"
  7227. " - Pagure</title>",
  7228. output_text,
  7229. )
  7230. self.assertIn(
  7231. '<i class="fa fa-fw fa-pencil"></i></a>',
  7232. '<a class="btn btn-outline-primary border-0 btn-sm '
  7233. "issue-metadata-display editmetadatatoggle pointer inline-block"
  7234. '"><i class="fa fa-fw fa-pencil"></i></a>',
  7235. output_text,
  7236. )
  7237. self.assertIn(
  7238. '<form method="POST" action="/test/pull-request/1/update">',
  7239. output_text,
  7240. )
  7241. def test_ticket_can_edit_metadata_ui(self):
  7242. """Test that users with ticket access can edit the metadata in the
  7243. UI."""
  7244. user = tests.FakeUser(username="foo")
  7245. with tests.user_set(self.app.application, user):
  7246. data = {"csrf_token": self.get_csrf(), "user": "pingou"}
  7247. output = self.app.post(
  7248. "/test/pull-request/1/update", data=data, follow_redirects=True
  7249. )
  7250. self.assertEqual(output.status_code, 200)
  7251. output_text = output.get_data(as_text=True)
  7252. self.assertIn(
  7253. "<title>PR#1: PR from the feature branch - test\n"
  7254. " - Pagure</title>",
  7255. output_text,
  7256. )
  7257. self.assertIn(
  7258. '<i class="fa fa-fw fa-pencil"></i></a>',
  7259. '<a class="btn btn-outline-primary border-0 btn-sm '
  7260. "issue-metadata-display editmetadatatoggle pointer inline-block"
  7261. '"><i class="fa fa-fw fa-pencil"></i></a>',
  7262. output_text,
  7263. )
  7264. self.assertIn(
  7265. '<form method="POST" action="/test/pull-request/1/update">',
  7266. output_text,
  7267. )
  7268. self.assertIn(
  7269. '<input value="pingou"\n name="user" '
  7270. 'id="assignee" placeholder="username" >',
  7271. output_text,
  7272. )
  7273. if __name__ == "__main__":
  7274. unittest.main(verbosity=2)