test_pagure_flask_ui_fork.py 317 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969
  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.remotes.create("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. .replace("&#x27;", "'")
  5862. .replace("&#39;", "'"),
  5863. )
  5864. @patch("pagure.lib.notify.send_email")
  5865. def test_new_request_pull_empty_repo(self, send_email):
  5866. """Test the new_request_pull endpoint against an empty repo."""
  5867. send_email.return_value = True
  5868. self.test_fork_project()
  5869. tests.create_projects_git(
  5870. os.path.join(self.path, "requests"), bare=True
  5871. )
  5872. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  5873. fork = pagure.lib.query.get_authorized_project(
  5874. self.session, "test", user="foo"
  5875. )
  5876. # Create a git repo to play with
  5877. gitrepo = os.path.join(self.path, "repos", "test.git")
  5878. repo = pygit2.init_repository(gitrepo, bare=True)
  5879. # Create a fork of this repo
  5880. newpath = tempfile.mkdtemp(prefix="pagure-fork-test")
  5881. gitrepo = os.path.join(self.path, "repos", "forks", "foo", "test.git")
  5882. new_repo = pygit2.clone_repository(gitrepo, newpath)
  5883. user = tests.FakeUser()
  5884. user.username = "foo"
  5885. with tests.user_set(self.app.application, user):
  5886. output = self.app.get(
  5887. "/fork/foo/test/diff/master..feature", follow_redirects=True
  5888. )
  5889. self.assertEqual(output.status_code, 400)
  5890. self.assertIn(
  5891. "<p>Fork is empty, there are no commits to create a pull "
  5892. "request with</p>",
  5893. output.get_data(as_text=True),
  5894. )
  5895. output = self.app.get("/test/new_issue")
  5896. csrf_token = self.get_csrf(output=output)
  5897. data = {"csrf_token": csrf_token, "title": "foo bar PR"}
  5898. output = self.app.post(
  5899. "/test/diff/master..feature", data=data, follow_redirects=True
  5900. )
  5901. self.assertEqual(output.status_code, 400)
  5902. self.assertIn(
  5903. "<p>Fork is empty, there are no commits to create a pull "
  5904. "request with</p>",
  5905. output.get_data(as_text=True),
  5906. )
  5907. shutil.rmtree(newpath)
  5908. @patch("pagure.lib.notify.send_email")
  5909. def test_new_request_pull_empty_fork(self, send_email):
  5910. """Test the new_request_pull endpoint against an empty repo."""
  5911. send_email.return_value = True
  5912. self.test_fork_project()
  5913. tests.create_projects_git(
  5914. os.path.join(self.path, "requests"), bare=True
  5915. )
  5916. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  5917. fork = pagure.lib.query.get_authorized_project(
  5918. self.session, "test", user="foo"
  5919. )
  5920. # Create a git repo to play with
  5921. gitrepo = os.path.join(self.path, "repos", "test.git")
  5922. repo = pygit2.init_repository(gitrepo, bare=True)
  5923. # Create a fork of this repo
  5924. newpath = tempfile.mkdtemp(prefix="pagure-fork-test")
  5925. gitrepo = os.path.join(self.path, "repos", "forks", "foo", "test.git")
  5926. new_repo = pygit2.clone_repository(gitrepo, newpath)
  5927. user = tests.FakeUser()
  5928. user.username = "foo"
  5929. with tests.user_set(self.app.application, user):
  5930. output = self.app.get(
  5931. "/fork/foo/test/diff/master..master", follow_redirects=True
  5932. )
  5933. self.assertEqual(output.status_code, 400)
  5934. self.assertIn(
  5935. "<p>Fork is empty, there are no commits to create a pull "
  5936. "request with</p>",
  5937. output.get_data(as_text=True),
  5938. )
  5939. shutil.rmtree(newpath)
  5940. @patch.dict(
  5941. "pagure.config.config", {"FEDORA_MESSAGING_NOTIFICATIONS": True}
  5942. )
  5943. @patch("pagure.lib.notify.send_email")
  5944. def test_pull_request_add_comment(self, send_email):
  5945. """Test the pull_request_add_comment endpoint."""
  5946. send_email.return_value = True
  5947. self.test_request_pull()
  5948. user = tests.FakeUser()
  5949. user.username = "pingou"
  5950. with tests.user_set(self.app.application, user):
  5951. output = self.app.post("/foo/pull-request/1/comment")
  5952. self.assertEqual(output.status_code, 404)
  5953. output = self.app.post("/test/pull-request/100/comment")
  5954. self.assertEqual(output.status_code, 404)
  5955. output = self.app.post("/test/pull-request/1/comment")
  5956. self.assertEqual(output.status_code, 200)
  5957. self.assertTrue(
  5958. output.get_data(as_text=True).startswith(
  5959. '\n<section class="add_comment">'
  5960. )
  5961. )
  5962. csrf_token = self.get_csrf(output=output)
  5963. data = {
  5964. "csrf_token": csrf_token,
  5965. "comment": "This look alright but we can do better",
  5966. }
  5967. with testing.mock_sends(
  5968. pagure_messages.PullRequestCommentAddedV1(
  5969. topic="pagure.pull-request.comment.added",
  5970. body={
  5971. "pullrequest": {
  5972. "id": 1,
  5973. "full_url": "http://localhost.localdomain/test/pull-request/1",
  5974. "uid": ANY,
  5975. "title": "PR from the feature branch",
  5976. "branch": "master",
  5977. "project": {
  5978. "id": 1,
  5979. "name": "test",
  5980. "fullname": "test",
  5981. "url_path": "test",
  5982. "full_url": "http://localhost.localdomain/test",
  5983. "description": "test project #1",
  5984. "namespace": None,
  5985. "parent": None,
  5986. "date_created": ANY,
  5987. "date_modified": ANY,
  5988. "user": {
  5989. "name": "pingou",
  5990. "fullname": "PY C",
  5991. "url_path": "user/pingou",
  5992. "full_url": "http://localhost.localdomain/user/pingou",
  5993. },
  5994. "access_users": {
  5995. "owner": ["pingou"],
  5996. "admin": [],
  5997. "commit": [],
  5998. "collaborator": [],
  5999. "ticket": [],
  6000. },
  6001. "access_groups": {
  6002. "admin": [],
  6003. "commit": [],
  6004. "collaborator": [],
  6005. "ticket": [],
  6006. },
  6007. "tags": [],
  6008. "priorities": {},
  6009. "custom_keys": [],
  6010. "close_status": [
  6011. "Invalid",
  6012. "Insufficient data",
  6013. "Fixed",
  6014. "Duplicate",
  6015. ],
  6016. "milestones": {},
  6017. },
  6018. "branch_from": "feature",
  6019. "repo_from": {
  6020. "id": 1,
  6021. "name": "test",
  6022. "fullname": "test",
  6023. "url_path": "test",
  6024. "full_url": "http://localhost.localdomain/test",
  6025. "description": "test project #1",
  6026. "namespace": None,
  6027. "parent": None,
  6028. "date_created": ANY,
  6029. "date_modified": ANY,
  6030. "user": {
  6031. "name": "pingou",
  6032. "fullname": "PY C",
  6033. "url_path": "user/pingou",
  6034. "full_url": "http://localhost.localdomain/user/pingou",
  6035. },
  6036. "access_users": {
  6037. "owner": ["pingou"],
  6038. "admin": [],
  6039. "commit": [],
  6040. "collaborator": [],
  6041. "ticket": [],
  6042. },
  6043. "access_groups": {
  6044. "admin": [],
  6045. "commit": [],
  6046. "collaborator": [],
  6047. "ticket": [],
  6048. },
  6049. "tags": [],
  6050. "priorities": {},
  6051. "custom_keys": [],
  6052. "close_status": [
  6053. "Invalid",
  6054. "Insufficient data",
  6055. "Fixed",
  6056. "Duplicate",
  6057. ],
  6058. "milestones": {},
  6059. },
  6060. "remote_git": None,
  6061. "date_created": ANY,
  6062. "updated_on": ANY,
  6063. "last_updated": ANY,
  6064. "closed_at": None,
  6065. "user": {
  6066. "name": "pingou",
  6067. "fullname": "PY C",
  6068. "url_path": "user/pingou",
  6069. "full_url": "http://localhost.localdomain/user/pingou",
  6070. },
  6071. "assignee": None,
  6072. "status": "Open",
  6073. "commit_start": ANY,
  6074. "commit_stop": ANY,
  6075. "closed_by": None,
  6076. "initial_comment": None,
  6077. "cached_merge_status": "unknown",
  6078. "threshold_reached": None,
  6079. "tags": [],
  6080. "comments": [
  6081. {
  6082. "id": 1,
  6083. "commit": None,
  6084. "tree": None,
  6085. "filename": None,
  6086. "line": None,
  6087. "comment": "This look alright but we can do better",
  6088. "parent": None,
  6089. "date_created": ANY,
  6090. "user": {
  6091. "name": "pingou",
  6092. "fullname": "PY C",
  6093. "url_path": "user/pingou",
  6094. "full_url": "http://localhost.localdomain/user/pingou",
  6095. },
  6096. "edited_on": None,
  6097. "editor": None,
  6098. "notification": False,
  6099. "reactions": {},
  6100. }
  6101. ],
  6102. },
  6103. "agent": "pingou",
  6104. },
  6105. )
  6106. ):
  6107. output = self.app.post(
  6108. "/test/pull-request/1/comment",
  6109. data=data,
  6110. follow_redirects=True,
  6111. )
  6112. self.assertEqual(output.status_code, 200)
  6113. output_text = output.get_data(as_text=True)
  6114. self.assertIn(
  6115. "<title>PR#1: PR from the feature branch - test\n - "
  6116. "Pagure</title>",
  6117. output_text,
  6118. )
  6119. self.assertIn("Comment added", output_text)
  6120. self.assertEqual(output_text.count('title="PY C (pingou)"'), 2)
  6121. # Project w/o pull-request
  6122. repo = pagure.lib.query.get_authorized_project(
  6123. self.session, "test"
  6124. )
  6125. settings = repo.settings
  6126. settings["pull_requests"] = False
  6127. repo.settings = settings
  6128. self.session.add(repo)
  6129. self.session.commit()
  6130. output = self.app.post(
  6131. "/test/pull-request/1/comment",
  6132. data=data,
  6133. follow_redirects=True,
  6134. )
  6135. self.assertEqual(output.status_code, 404)
  6136. @patch("pagure.lib.notify.send_email")
  6137. def test_pull_request_drop_comment(self, send_email):
  6138. """Test the pull_request_drop_comment endpoint."""
  6139. send_email.return_value = True
  6140. self.test_pull_request_add_comment()
  6141. # Project w/ pull-request
  6142. repo = pagure.lib.query.get_authorized_project(self.session, "test")
  6143. settings = repo.settings
  6144. settings["pull_requests"] = True
  6145. repo.settings = settings
  6146. self.session.add(repo)
  6147. self.session.commit()
  6148. user = tests.FakeUser()
  6149. user.username = "foo"
  6150. with tests.user_set(self.app.application, user):
  6151. output = self.app.post("/foo/pull-request/1/comment/drop")
  6152. self.assertEqual(output.status_code, 404)
  6153. output = self.app.post("/test/pull-request/100/comment/drop")
  6154. self.assertEqual(output.status_code, 404)
  6155. output = self.app.post(
  6156. "/test/pull-request/1/comment/drop", follow_redirects=True
  6157. )
  6158. self.assertEqual(output.status_code, 200)
  6159. output_text = output.get_data(as_text=True)
  6160. self.assertIn(
  6161. '<h4 class="ml-1">\n <div>\n '
  6162. '<span class="fa fa-fw text-success fa-arrow-circle-down pt-1"></span>\n '
  6163. '<span class="text-success '
  6164. 'font-weight-bold">#1</span>\n '
  6165. '<span class="font-weight-bold">\n '
  6166. "PR from the feature branch\n",
  6167. output_text,
  6168. )
  6169. # self.assertIn('href="#comment-1">¶</a>', output_text)
  6170. self.assertIn(
  6171. "<p>This look alright but we can do better</p>", output_text
  6172. )
  6173. csrf_token = self.get_csrf(output=output)
  6174. # Invalid comment id
  6175. data = {"csrf_token": csrf_token, "drop_comment": "10"}
  6176. output = self.app.post(
  6177. "/test/pull-request/1/comment/drop",
  6178. data=data,
  6179. follow_redirects=True,
  6180. )
  6181. self.assertEqual(output.status_code, 404)
  6182. data["drop_comment"] = "1"
  6183. output = self.app.post(
  6184. "/test/pull-request/1/comment/drop",
  6185. data=data,
  6186. follow_redirects=True,
  6187. )
  6188. self.assertEqual(output.status_code, 403)
  6189. user.username = "pingou"
  6190. with tests.user_set(self.app.application, user):
  6191. # Drop comment
  6192. output = self.app.post(
  6193. "/test/pull-request/1/comment/drop",
  6194. data=data,
  6195. follow_redirects=True,
  6196. )
  6197. self.assertEqual(output.status_code, 200)
  6198. output_text = output.get_data(as_text=True)
  6199. self.assertIn(
  6200. '<h4 class="ml-1">\n <div>\n '
  6201. '<span class="fa fa-fw text-success fa-arrow-circle-down pt-1"></span>\n '
  6202. '<span class="text-success '
  6203. 'font-weight-bold">#1</span>\n '
  6204. '<span class="font-weight-bold">\n '
  6205. "PR from the feature branch\n",
  6206. output_text,
  6207. )
  6208. self.assertIn("Comment removed", output_text)
  6209. # Project w/o pull-request
  6210. repo = pagure.lib.query.get_authorized_project(
  6211. self.session, "test"
  6212. )
  6213. settings = repo.settings
  6214. settings["pull_requests"] = False
  6215. repo.settings = settings
  6216. self.session.add(repo)
  6217. self.session.commit()
  6218. output = self.app.post(
  6219. "/test/pull-request/1/comment/drop",
  6220. data=data,
  6221. follow_redirects=True,
  6222. )
  6223. self.assertEqual(output.status_code, 404)
  6224. @patch.dict(
  6225. "pagure.config.config", {"FEDORA_MESSAGING_NOTIFICATIONS": True}
  6226. )
  6227. @patch("pagure.lib.notify.send_email")
  6228. def test_pull_request_edit_comment(self, send_email):
  6229. """Test the pull request edit comment endpoint"""
  6230. send_email.return_value = True
  6231. self.test_request_pull()
  6232. user = tests.FakeUser()
  6233. user.username = "pingou"
  6234. with tests.user_set(self.app.application, user):
  6235. # Repo 'foo' does not exist so it is verifying that condition
  6236. output = self.app.post("/foo/pull-request/1/comment/1/edit")
  6237. self.assertEqual(output.status_code, 404)
  6238. # Here no comment is present in the PR so its verifying that condition
  6239. output = self.app.post("/test/pull-request/100/comment/100/edit")
  6240. self.assertEqual(output.status_code, 404)
  6241. output = self.app.post("/test/pull-request/1/comment")
  6242. self.assertEqual(output.status_code, 200)
  6243. # Creating comment to play with
  6244. self.assertTrue(
  6245. output.get_data(as_text=True).startswith(
  6246. '\n<section class="add_comment">'
  6247. )
  6248. )
  6249. csrf_token = self.get_csrf(output=output)
  6250. data = {
  6251. "csrf_token": csrf_token,
  6252. "comment": "This look alright but we can do better",
  6253. }
  6254. output = self.app.post(
  6255. "/test/pull-request/1/comment",
  6256. data=data,
  6257. follow_redirects=True,
  6258. )
  6259. self.assertEqual(output.status_code, 200)
  6260. output_text = output.get_data(as_text=True)
  6261. self.assertIn(
  6262. '<h4 class="ml-1">\n <div>\n '
  6263. '<span class="fa fa-fw text-success fa-arrow-circle-down pt-1"></span>\n '
  6264. '<span class="text-success '
  6265. 'font-weight-bold">#1</span>\n '
  6266. '<span class="font-weight-bold">\n '
  6267. "PR from the feature branch\n",
  6268. output_text,
  6269. )
  6270. self.assertIn("Comment added", output_text)
  6271. # Check if the comment is there
  6272. self.assertIn(
  6273. "<p>This look alright but we can do better</p>", output_text
  6274. )
  6275. output = self.app.get("/test/pull-request/1/comment/1/edit")
  6276. self.assertEqual(output.status_code, 200)
  6277. output_text = output.get_data(as_text=True)
  6278. self.assertIn('<section class="edit_comment">', output_text)
  6279. # Checking if the comment is there in the update page
  6280. self.assertIn(
  6281. "This look alright but we can do better</textarea>",
  6282. output_text,
  6283. )
  6284. csrf_token = self.get_csrf(output=output)
  6285. data = {
  6286. "csrf_token": csrf_token,
  6287. "update_comment": "This look alright but we can do better than this.",
  6288. }
  6289. with testing.mock_sends(
  6290. pagure_messages.PullRequestCommentEditedV1(
  6291. topic="pagure.pull-request.comment.edited",
  6292. body={
  6293. "pullrequest": {
  6294. "id": 1,
  6295. "full_url": "http://localhost.localdomain/test/pull-request/1",
  6296. "uid": ANY,
  6297. "title": "PR from the feature branch",
  6298. "branch": "master",
  6299. "project": {
  6300. "id": 1,
  6301. "name": "test",
  6302. "fullname": "test",
  6303. "url_path": "test",
  6304. "full_url": "http://localhost.localdomain/test",
  6305. "description": "test project #1",
  6306. "namespace": None,
  6307. "parent": None,
  6308. "date_created": ANY,
  6309. "date_modified": ANY,
  6310. "user": {
  6311. "name": "pingou",
  6312. "fullname": "PY C",
  6313. "url_path": "user/pingou",
  6314. "full_url": "http://localhost.localdomain/user/pingou",
  6315. },
  6316. "access_users": {
  6317. "owner": ["pingou"],
  6318. "admin": [],
  6319. "commit": [],
  6320. "collaborator": [],
  6321. "ticket": [],
  6322. },
  6323. "access_groups": {
  6324. "admin": [],
  6325. "commit": [],
  6326. "collaborator": [],
  6327. "ticket": [],
  6328. },
  6329. "tags": [],
  6330. "priorities": {},
  6331. "custom_keys": [],
  6332. "close_status": [
  6333. "Invalid",
  6334. "Insufficient data",
  6335. "Fixed",
  6336. "Duplicate",
  6337. ],
  6338. "milestones": {},
  6339. },
  6340. "branch_from": "feature",
  6341. "repo_from": {
  6342. "id": 1,
  6343. "name": "test",
  6344. "fullname": "test",
  6345. "full_url": "http://localhost.localdomain/test",
  6346. "url_path": "test",
  6347. "description": "test project #1",
  6348. "namespace": None,
  6349. "parent": None,
  6350. "date_created": ANY,
  6351. "date_modified": ANY,
  6352. "user": {
  6353. "name": "pingou",
  6354. "fullname": "PY C",
  6355. "url_path": "user/pingou",
  6356. "full_url": "http://localhost.localdomain/user/pingou",
  6357. },
  6358. "access_users": {
  6359. "owner": ["pingou"],
  6360. "admin": [],
  6361. "commit": [],
  6362. "collaborator": [],
  6363. "ticket": [],
  6364. },
  6365. "access_groups": {
  6366. "admin": [],
  6367. "commit": [],
  6368. "collaborator": [],
  6369. "ticket": [],
  6370. },
  6371. "tags": [],
  6372. "priorities": {},
  6373. "custom_keys": [],
  6374. "close_status": [
  6375. "Invalid",
  6376. "Insufficient data",
  6377. "Fixed",
  6378. "Duplicate",
  6379. ],
  6380. "milestones": {},
  6381. },
  6382. "remote_git": None,
  6383. "date_created": ANY,
  6384. "updated_on": ANY,
  6385. "last_updated": ANY,
  6386. "closed_at": None,
  6387. "user": {
  6388. "name": "pingou",
  6389. "fullname": "PY C",
  6390. "url_path": "user/pingou",
  6391. "full_url": "http://localhost.localdomain/user/pingou",
  6392. },
  6393. "assignee": None,
  6394. "status": "Open",
  6395. "commit_start": ANY,
  6396. "commit_stop": ANY,
  6397. "closed_by": None,
  6398. "initial_comment": None,
  6399. "cached_merge_status": "unknown",
  6400. "threshold_reached": None,
  6401. "tags": [],
  6402. "comments": [],
  6403. },
  6404. "project": {
  6405. "id": 1,
  6406. "name": "test",
  6407. "fullname": "test",
  6408. "url_path": "test",
  6409. "full_url": "http://localhost.localdomain/test",
  6410. "description": "test project #1",
  6411. "namespace": None,
  6412. "parent": None,
  6413. "date_created": ANY,
  6414. "date_modified": ANY,
  6415. "user": {
  6416. "name": "pingou",
  6417. "fullname": "PY C",
  6418. "url_path": "user/pingou",
  6419. "full_url": "http://localhost.localdomain/user/pingou",
  6420. },
  6421. "access_users": {
  6422. "owner": ["pingou"],
  6423. "admin": [],
  6424. "commit": [],
  6425. "collaborator": [],
  6426. "ticket": [],
  6427. },
  6428. "access_groups": {
  6429. "admin": [],
  6430. "commit": [],
  6431. "collaborator": [],
  6432. "ticket": [],
  6433. },
  6434. "tags": [],
  6435. "priorities": {},
  6436. "custom_keys": [],
  6437. "close_status": [
  6438. "Invalid",
  6439. "Insufficient data",
  6440. "Fixed",
  6441. "Duplicate",
  6442. ],
  6443. "milestones": {},
  6444. },
  6445. "comment": {
  6446. "id": 1,
  6447. "commit": None,
  6448. "tree": None,
  6449. "filename": None,
  6450. "line": None,
  6451. "comment": "This look alright but we can do better than this.",
  6452. "parent": None,
  6453. "date_created": ANY,
  6454. "user": {
  6455. "name": "pingou",
  6456. "fullname": "PY C",
  6457. "url_path": "user/pingou",
  6458. "full_url": "http://localhost.localdomain/user/pingou",
  6459. },
  6460. "edited_on": ANY,
  6461. "editor": {
  6462. "name": "pingou",
  6463. "fullname": "PY C",
  6464. "url_path": "user/pingou",
  6465. "full_url": "http://localhost.localdomain/user/pingou",
  6466. },
  6467. "notification": False,
  6468. "reactions": {},
  6469. },
  6470. "agent": "pingou",
  6471. },
  6472. )
  6473. ):
  6474. output = self.app.post(
  6475. "/test/pull-request/1/comment/1/edit",
  6476. data=data,
  6477. follow_redirects=True,
  6478. )
  6479. output_text = output.get_data(as_text=True)
  6480. # Checking if the comment is updated in the main page
  6481. self.assertIn(
  6482. "<p>This look alright but we can do better than this.</p>",
  6483. output_text,
  6484. )
  6485. self.assertIn(
  6486. '<h4 class="ml-1">\n <div>\n '
  6487. '<span class="fa fa-fw text-success fa-arrow-circle-down pt-1"></span>\n '
  6488. '<span class="text-success '
  6489. 'font-weight-bold">#1</span>\n '
  6490. '<span class="font-weight-bold">\n '
  6491. "PR from the feature branch\n",
  6492. output_text,
  6493. )
  6494. # Checking if Edited by User is there or not
  6495. pattern = (
  6496. re.escape("<small>Edited ")
  6497. + '<span title="[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:'
  6498. + '[0-9]{2} UTC" data-toggle="tooltip">(just now|seconds ago)</span>'
  6499. + re.escape(" by pingou </small>")
  6500. )
  6501. self.assertIsNotNone(re.search(pattern, output_text))
  6502. self.assertIn("Comment updated", output_text)
  6503. # Project w/o pull-request
  6504. repo = pagure.lib.query.get_authorized_project(
  6505. self.session, "test"
  6506. )
  6507. settings = repo.settings
  6508. settings["pull_requests"] = False
  6509. repo.settings = settings
  6510. self.session.add(repo)
  6511. self.session.commit()
  6512. output = self.app.post(
  6513. "/test/pull-request/1/comment/edit/1",
  6514. data=data,
  6515. follow_redirects=True,
  6516. )
  6517. self.assertEqual(output.status_code, 404)
  6518. @patch("pagure.lib.notify.send_email")
  6519. def test_merge_request_pull_FF_w_merge_commit(self, send_email):
  6520. """Test the merge_request_pull endpoint with a FF PR but with a
  6521. merge commit.
  6522. """
  6523. send_email.return_value = True
  6524. self.test_request_pull()
  6525. user = tests.FakeUser()
  6526. with tests.user_set(self.app.application, user):
  6527. output = self.app.get("/test/pull-request/1")
  6528. self.assertEqual(output.status_code, 200)
  6529. csrf_token = self.get_csrf(output=output)
  6530. # No CSRF
  6531. output = self.app.post(
  6532. "/test/pull-request/1/merge", data={}, follow_redirects=True
  6533. )
  6534. self.assertEqual(output.status_code, 200)
  6535. output_text = output.get_data(as_text=True)
  6536. self.assertIn(
  6537. "<title>PR#1: PR from the feature branch - test\n - "
  6538. "Pagure</title>",
  6539. output_text,
  6540. )
  6541. self.assertIn(
  6542. '<h4 class="ml-1">\n <div>\n '
  6543. '<span class="fa fa-fw text-success fa-arrow-circle-down pt-1"></span>\n '
  6544. '<span class="text-success '
  6545. 'font-weight-bold">#1</span>\n '
  6546. '<span class="font-weight-bold">\n '
  6547. "PR from the feature branch\n",
  6548. output_text,
  6549. )
  6550. self.assertIn(
  6551. 'title="View file as of 2a552bb">sources</a>', output_text
  6552. )
  6553. # Wrong project
  6554. data = {"csrf_token": csrf_token}
  6555. output = self.app.post(
  6556. "/foobar/pull-request/100/merge",
  6557. data=data,
  6558. follow_redirects=True,
  6559. )
  6560. self.assertEqual(output.status_code, 404)
  6561. # Wrong project
  6562. data = {"csrf_token": csrf_token}
  6563. output = self.app.post(
  6564. "/test/pull-request/1/merge", data=data, follow_redirects=True
  6565. )
  6566. self.assertEqual(output.status_code, 403)
  6567. user.username = "pingou"
  6568. with tests.user_set(self.app.application, user):
  6569. # Wrong request id
  6570. data = {"csrf_token": csrf_token}
  6571. output = self.app.post(
  6572. "/test/pull-request/100/merge",
  6573. data=data,
  6574. follow_redirects=True,
  6575. )
  6576. self.assertEqual(output.status_code, 404)
  6577. # Project requiring a merge commit
  6578. repo = pagure.lib.query.get_authorized_project(
  6579. self.session, "test"
  6580. )
  6581. settings = repo.settings
  6582. settings["always_merge"] = True
  6583. repo.settings = settings
  6584. self.session.add(repo)
  6585. self.session.commit()
  6586. # Merge
  6587. output = self.app.post(
  6588. "/test/pull-request/1/merge", data=data, follow_redirects=True
  6589. )
  6590. self.assertEqual(output.status_code, 200)
  6591. output = self.app.get("/test/commits")
  6592. output_text = output.get_data(as_text=True)
  6593. self.assertIn(
  6594. "<title>Commits - test - Pagure</title>", output_text
  6595. )
  6596. self.assertIn("Merge #1 `PR from the feature branch`", output_text)
  6597. self.assertIn("A commit on branch feature", output_text)
  6598. # Check if the closing notification was added
  6599. output = self.app.get("/test/pull-request/1")
  6600. output_text = output.get_data(as_text=True)
  6601. self.assertIsNotNone(re.search(MERGED_PATTERN, output_text))
  6602. @patch("pagure.lib.notify.send_email")
  6603. def test_internal_endpoint_main_ahead(self, send_email):
  6604. """Test the new_request_pull endpoint when the main repo is ahead
  6605. of the fork.
  6606. """
  6607. send_email.return_value = True
  6608. tests.create_projects(self.session)
  6609. tests.create_projects_git(
  6610. os.path.join(self.path, "requests"), bare=True
  6611. )
  6612. set_up_git_repo(
  6613. self.session, self.path, new_project=None, branch_from="feature"
  6614. )
  6615. gitrepo = os.path.join(self.path, "repos", "test.git")
  6616. repo = pygit2.init_repository(gitrepo, bare=True)
  6617. # Make the main repo be ahead of the fork
  6618. # First commit
  6619. newpath = tempfile.mkdtemp(prefix="pagure-test")
  6620. repopath = os.path.join(newpath, "test")
  6621. clone_repo = pygit2.clone_repository(gitrepo, repopath)
  6622. # Create a file in that git repo
  6623. with open(os.path.join(repopath, "testfile"), "w") as stream:
  6624. stream.write("foo\n bar")
  6625. clone_repo.index.add("testfile")
  6626. clone_repo.index.write()
  6627. # Commits the files added
  6628. last_commit = clone_repo.revparse_single("HEAD")
  6629. tree = clone_repo.index.write_tree()
  6630. author = pygit2.Signature("Alice Author", "alice@authors.tld")
  6631. committer = pygit2.Signature("Cecil Committer", "cecil@committers.tld")
  6632. clone_repo.create_commit(
  6633. "refs/heads/master", # the name of the reference to update
  6634. author,
  6635. committer,
  6636. "Add testfile file for testing",
  6637. # binary string representing the tree object ID
  6638. tree,
  6639. # list of binary strings representing parents of the new commit
  6640. [last_commit.oid.hex],
  6641. )
  6642. # Second commit
  6643. with open(os.path.join(repopath, "testfile"), "a") as stream:
  6644. stream.write("\nfoo2\n bar2")
  6645. clone_repo.index.add("testfile")
  6646. clone_repo.index.write()
  6647. # Commits the files added
  6648. last_commit = clone_repo.revparse_single("HEAD")
  6649. tree = clone_repo.index.write_tree()
  6650. author = pygit2.Signature("Alice Author", "alice@authors.tld")
  6651. committer = pygit2.Signature("Cecil Committer", "cecil@committers.tld")
  6652. clone_repo.create_commit(
  6653. "refs/heads/master", # the name of the reference to update
  6654. author,
  6655. committer,
  6656. "Add a second commit to testfile for testing",
  6657. # binary string representing the tree object ID
  6658. tree,
  6659. # list of binary strings representing parents of the new commit
  6660. [last_commit.oid.hex],
  6661. )
  6662. # Third commit
  6663. with open(os.path.join(repopath, "testfile"), "a") as stream:
  6664. stream.write("\nfoo3\n bar3")
  6665. clone_repo.index.add("testfile")
  6666. clone_repo.index.write()
  6667. # Commits the files added
  6668. last_commit = clone_repo.revparse_single("HEAD")
  6669. tree = clone_repo.index.write_tree()
  6670. author = pygit2.Signature("Alice Author", "alice@authors.tld")
  6671. committer = pygit2.Signature("Cecil Committer", "cecil@committers.tld")
  6672. clone_repo.create_commit(
  6673. "refs/heads/master", # the name of the reference to update
  6674. author,
  6675. committer,
  6676. "Add a third commit to testfile for testing",
  6677. # binary string representing the tree object ID
  6678. tree,
  6679. # list of binary strings representing parents of the new commit
  6680. [last_commit.oid.hex],
  6681. )
  6682. refname = "refs/heads/master:refs/heads/master"
  6683. ori_remote = clone_repo.remotes[0]
  6684. PagureRepo.push(ori_remote, refname)
  6685. shutil.rmtree(newpath)
  6686. user = tests.FakeUser()
  6687. user.username = "foo"
  6688. with tests.user_set(self.app.application, user):
  6689. csrf_token = self.get_csrf()
  6690. output = self.app.post(
  6691. "/pv/pull-request/ready",
  6692. data={"repo": "test", "csrf_token": csrf_token},
  6693. )
  6694. self.assertEqual(output.status_code, 200)
  6695. data = json.loads(output.get_data(as_text=True))
  6696. self.assertEqual(sorted(data.keys()), ["code", "task"])
  6697. self.assertEqual(data["code"], "OK")
  6698. @patch("pagure.lib.notify.send_email")
  6699. def test_fork_edit_file(self, send_email):
  6700. """Test the fork_edit file endpoint."""
  6701. send_email.return_value = True
  6702. # Git repo not found
  6703. output = self.app.post("fork_edit/test/edit/master/f/sources")
  6704. self.assertEqual(output.status_code, 404)
  6705. tests.create_projects(self.session)
  6706. for folder in ["docs", "tickets", "requests", "repos"]:
  6707. tests.create_projects_git(
  6708. os.path.join(self.path, folder), bare=True
  6709. )
  6710. # User not logged in
  6711. output = self.app.post("fork_edit/test/edit/master/f/sources")
  6712. self.assertEqual(output.status_code, 302)
  6713. user = tests.FakeUser()
  6714. user.username = "pingou"
  6715. with tests.user_set(self.app.application, user):
  6716. # Invalid request
  6717. output = self.app.post("fork_edit/test/edit/master/f/source")
  6718. self.assertEqual(output.status_code, 400)
  6719. output = self.app.get("/new/")
  6720. self.assertEqual(output.status_code, 200)
  6721. self.assertIn(
  6722. "<strong>Create new Project</strong>",
  6723. output.get_data(as_text=True),
  6724. )
  6725. csrf_token = self.get_csrf(output=output)
  6726. data = {"csrf_token": csrf_token}
  6727. # No files can be found since they are not added
  6728. output = self.app.post(
  6729. "fork_edit/test/edit/master/f/sources",
  6730. data=data,
  6731. follow_redirects=True,
  6732. )
  6733. self.assertEqual(output.status_code, 404)
  6734. user = tests.FakeUser()
  6735. user.username = "foo"
  6736. with tests.user_set(self.app.application, user):
  6737. data = {"csrf_token": csrf_token}
  6738. # Invalid request
  6739. output = self.app.post(
  6740. "fork_edit/test/edit/master/f/sources", follow_redirects=True
  6741. )
  6742. self.assertEqual(output.status_code, 400)
  6743. # Add content to the repo
  6744. tests.add_content_git_repo(
  6745. os.path.join(pagure.config.config["GIT_FOLDER"], "test.git")
  6746. )
  6747. tests.add_readme_git_repo(
  6748. os.path.join(pagure.config.config["GIT_FOLDER"], "test.git")
  6749. )
  6750. tests.add_binary_git_repo(
  6751. os.path.join(pagure.config.config["GIT_FOLDER"], "test.git"),
  6752. "test.jpg",
  6753. )
  6754. # Check if button exists
  6755. output = self.app.get("/test/blob/master/f/sources")
  6756. self.assertEqual(output.status_code, 200)
  6757. self.assertIn(
  6758. "Fork and Edit\n </button>\n",
  6759. output.get_data(as_text=True),
  6760. )
  6761. # Check fork-edit doesn't show for binary files
  6762. output = self.app.get("/test/blob/master/f/test.jpg")
  6763. self.assertEqual(output.status_code, 200)
  6764. self.assertNotIn(b"<html", output.data)
  6765. # Check for edit panel
  6766. output = self.app.post(
  6767. "fork_edit/test/edit/master/f/sources",
  6768. data=data,
  6769. follow_redirects=True,
  6770. )
  6771. self.assertEqual(output.status_code, 200)
  6772. output_text = output.get_data(as_text=True)
  6773. self.assertIn(
  6774. '<li><a href="/fork/foo/test/tree/master">'
  6775. '<span class="fa fa-random"></span>&nbsp; master</a>'
  6776. '</li><li class="active"><span class="fa fa-file">'
  6777. "</span>&nbsp; sources</li>",
  6778. output_text,
  6779. )
  6780. self.assertIn(
  6781. '<textarea id="textareaCode" name="content">foo\n bar</textarea>',
  6782. output_text,
  6783. )
  6784. # Check for edit panel- Fork already done
  6785. output = self.app.post(
  6786. "fork_edit/test/edit/master/f/sources",
  6787. data=data,
  6788. follow_redirects=True,
  6789. )
  6790. self.assertEqual(output.status_code, 200)
  6791. output_text = output.get_data(as_text=True)
  6792. self.assertIn("<title>Edit - test - Pagure</title>", output_text)
  6793. self.assertIn(
  6794. "You had already forked " "this project", output_text
  6795. )
  6796. self.assertIn(
  6797. '<i class="fa fa-code-fork fa-fw"></i> View Upstream',
  6798. output_text,
  6799. )
  6800. self.assertIn(
  6801. '<li><a href="/fork/foo/test/tree/master">'
  6802. '<span class="fa fa-random"></span>&nbsp; master</a>'
  6803. '</li><li class="active"><span class="fa fa-file">'
  6804. "</span>&nbsp; sources</li>",
  6805. output_text,
  6806. )
  6807. self.assertIn(
  6808. '<textarea id="textareaCode" name="content">foo\n bar</textarea>',
  6809. output_text,
  6810. )
  6811. # View what's supposed to be an image
  6812. output = self.app.post(
  6813. "fork_edit/test/edit/master/f/test.jpg",
  6814. data=data,
  6815. follow_redirects=True,
  6816. )
  6817. self.assertEqual(output.status_code, 400)
  6818. self.assertIn(
  6819. b"<p>Cannot edit binary files</p>",
  6820. output.data,
  6821. )
  6822. # Check fork-edit shows when user is not logged in
  6823. output = self.app.get("/test/blob/master/f/sources")
  6824. self.assertEqual(output.status_code, 200)
  6825. self.assertIn(
  6826. "Fork and Edit\n </button>\n",
  6827. output.get_data(as_text=True),
  6828. )
  6829. # Check if fork-edit shows for different user
  6830. user.username = "pingou"
  6831. with tests.user_set(self.app.application, user):
  6832. # Check if button exists
  6833. output = self.app.get("/test/blob/master/f/sources")
  6834. self.assertEqual(output.status_code, 200)
  6835. self.assertIn(
  6836. "Edit in your fork\n </button>\n",
  6837. output.get_data(as_text=True),
  6838. )
  6839. # Check fork-edit doesn't show for binary
  6840. output = self.app.get("/test/blob/master/f/test.jpg")
  6841. self.assertEqual(output.status_code, 200)
  6842. self.assertNotIn(b"<html", output.data)
  6843. @patch("pagure.lib.notify.send_email", MagicMock(return_value=True))
  6844. def test_fork_edit_file_namespace(self):
  6845. """Test the fork_edit file endpoint on a namespaced project."""
  6846. tests.create_projects(self.session)
  6847. for folder in ["docs", "tickets", "requests", "repos"]:
  6848. tests.create_projects_git(
  6849. os.path.join(self.path, folder), bare=True
  6850. )
  6851. # User not logged in
  6852. output = self.app.post(
  6853. "fork_edit/somenamespace/test3/edit/master/f/sources"
  6854. )
  6855. self.assertEqual(output.status_code, 302)
  6856. user = tests.FakeUser()
  6857. user.username = "pingou"
  6858. with tests.user_set(self.app.application, user):
  6859. # Invalid request
  6860. output = self.app.post(
  6861. "fork_edit/somenamespace/test3/edit/master/f/sources"
  6862. )
  6863. self.assertEqual(output.status_code, 400)
  6864. output = self.app.get("/new/")
  6865. self.assertEqual(output.status_code, 200)
  6866. self.assertIn(
  6867. "<strong>Create new Project</strong>",
  6868. output.get_data(as_text=True),
  6869. )
  6870. csrf_token = self.get_csrf(output=output)
  6871. data = {"csrf_token": csrf_token}
  6872. # No files can be found since they are not added
  6873. output = self.app.post(
  6874. "fork_edit/somenamespace/test3/edit/master/f/sources",
  6875. data=data,
  6876. follow_redirects=True,
  6877. )
  6878. self.assertEqual(output.status_code, 404)
  6879. user = tests.FakeUser()
  6880. user.username = "foo"
  6881. with tests.user_set(self.app.application, user):
  6882. data = {"csrf_token": csrf_token}
  6883. # Invalid request
  6884. output = self.app.post(
  6885. "fork_edit/somenamespace/test3/edit/master/f/sources",
  6886. follow_redirects=True,
  6887. )
  6888. self.assertEqual(output.status_code, 400)
  6889. # Add content to the repo
  6890. tests.add_content_git_repo(
  6891. os.path.join(
  6892. pagure.config.config["GIT_FOLDER"],
  6893. "somenamespace",
  6894. "test3.git",
  6895. )
  6896. )
  6897. tests.add_readme_git_repo(
  6898. os.path.join(
  6899. pagure.config.config["GIT_FOLDER"],
  6900. "somenamespace",
  6901. "test3.git",
  6902. )
  6903. )
  6904. tests.add_binary_git_repo(
  6905. os.path.join(
  6906. pagure.config.config["GIT_FOLDER"],
  6907. "somenamespace",
  6908. "test3.git",
  6909. ),
  6910. "test.jpg",
  6911. )
  6912. # Check if button exists
  6913. output = self.app.get("/somenamespace/test3/blob/master/f/sources")
  6914. self.assertEqual(output.status_code, 200)
  6915. self.assertIn(
  6916. "Fork and Edit\n </button>\n",
  6917. output.get_data(as_text=True),
  6918. )
  6919. # Check fork-edit doesn't show for binary files
  6920. output = self.app.get(
  6921. "/somenamespace/test3/blob/master/f/test.jpg"
  6922. )
  6923. self.assertEqual(output.status_code, 200)
  6924. self.assertNotIn(b"<html", output.data)
  6925. # Check for edit panel
  6926. output = self.app.post(
  6927. "fork_edit/somenamespace/test3/edit/master/f/sources",
  6928. data=data,
  6929. follow_redirects=True,
  6930. )
  6931. self.assertEqual(output.status_code, 200)
  6932. output_text = output.get_data(as_text=True)
  6933. self.assertIn(
  6934. "<title>Edit - somenamespace/test3 - Pagure</title>",
  6935. output_text,
  6936. )
  6937. self.assertIn(
  6938. '<i class="fa fa-code-fork fa-fw"></i> View Upstream',
  6939. output_text,
  6940. )
  6941. self.assertIn(
  6942. '<li><a href="/fork/foo/somenamespace/test3/tree/master">'
  6943. '<span class="fa fa-random"></span>&nbsp; master</a>'
  6944. '</li><li class="active"><span class="fa fa-file">'
  6945. "</span>&nbsp; sources</li>",
  6946. output_text,
  6947. )
  6948. self.assertIn(
  6949. '<textarea id="textareaCode" name="content">foo\n bar</textarea>',
  6950. output_text,
  6951. )
  6952. # Check for edit panel - while the project was already forked
  6953. output = self.app.post(
  6954. "fork_edit/somenamespace/test3/edit/master/f/sources",
  6955. data=data,
  6956. follow_redirects=True,
  6957. )
  6958. self.assertEqual(output.status_code, 200)
  6959. output_text = output.get_data(as_text=True)
  6960. self.assertIn(
  6961. "<title>Edit - somenamespace/test3 - Pagure</title>",
  6962. output_text,
  6963. )
  6964. self.assertIn(
  6965. "You had already forked " "this project", output_text
  6966. )
  6967. self.assertIn(
  6968. '<i class="fa fa-code-fork fa-fw"></i> View Upstream',
  6969. output_text,
  6970. )
  6971. self.assertIn(
  6972. '<li><a href="/fork/foo/somenamespace/test3/tree/master">'
  6973. '<span class="fa fa-random"></span>&nbsp; master</a>'
  6974. '</li><li class="active"><span class="fa fa-file">'
  6975. "</span>&nbsp; sources</li>",
  6976. output_text,
  6977. )
  6978. self.assertIn(
  6979. '<textarea id="textareaCode" name="content">foo\n bar</textarea>',
  6980. output_text,
  6981. )
  6982. @patch("pagure.lib.notify.send_email")
  6983. def test_fork_without_main_repo(self, send_email):
  6984. """Test the fork without the main repo."""
  6985. send_email.return_value = True
  6986. tests.create_projects(self.session)
  6987. # Create a fork with no parent i.e parent_id = None
  6988. item = pagure.lib.model.Project(
  6989. user_id=2, # foo
  6990. name="test",
  6991. description="test project #1",
  6992. hook_token="aaabbb",
  6993. is_fork=True,
  6994. parent_id=None,
  6995. )
  6996. self.session.add(item)
  6997. self.session.commit()
  6998. # Get fork project
  6999. project = pagure.lib.query._get_project(self.session, "test", "foo")
  7000. # Pull-requests and issue-trackers are off for forks
  7001. # lib function is not used here so mannually turning them off
  7002. project_settings = project.settings
  7003. project_settings["pull_requests"] = False
  7004. project_settings["issue_tracker"] = False
  7005. project.settings = project_settings
  7006. self.session.add(project)
  7007. self.session.commit()
  7008. tests.create_projects_git(
  7009. os.path.join(self.path, "repos", "forks", "foo"), bare=True
  7010. )
  7011. # Create a git repo to play with
  7012. gitrepo = os.path.join(self.path, "repos", "test.git")
  7013. self.assertFalse(os.path.exists(gitrepo))
  7014. os.makedirs(gitrepo)
  7015. repo = pygit2.init_repository(gitrepo, bare=True)
  7016. # Create a fork of this repo
  7017. newpath = tempfile.mkdtemp(prefix="pagure-fork-test")
  7018. gitrepo = os.path.join(self.path, "repos", "forks", "foo", "test.git")
  7019. new_repo = pygit2.clone_repository(gitrepo, newpath)
  7020. tests.add_content_git_repo(gitrepo)
  7021. # UI test for deleted main
  7022. output = self.app.get("/fork/foo/test")
  7023. self.assertEqual(output.status_code, 200)
  7024. self.assertIn(
  7025. "Forked from a deleted repository", output.get_data(as_text=True)
  7026. )
  7027. # Testing commit endpoint
  7028. output = self.app.get("/fork/foo/test/commits/master")
  7029. self.assertEqual(output.status_code, 200)
  7030. self.assertIn(
  7031. 'Commits <span class="badge badge-secondary"> 2</span>\n',
  7032. output.get_data(as_text=True),
  7033. )
  7034. # Test pull-request endpoint
  7035. output = self.app.get("/fork/foo/test/pull-requests")
  7036. self.assertEqual(output.status_code, 404)
  7037. # Test issue-tracker endpoint
  7038. output = self.app.get("/fork/foo/test/issues")
  7039. self.assertEqual(output.status_code, 404)
  7040. shutil.rmtree(newpath)
  7041. def _set_up_for_reaction_test(self):
  7042. self.session.add(
  7043. pagure.lib.model.User(
  7044. user="jdoe",
  7045. fullname="John Doe",
  7046. password=b"password",
  7047. default_email="jdoe@example.com",
  7048. )
  7049. )
  7050. self.session.commit()
  7051. tests.create_projects(self.session)
  7052. tests.create_projects_git(
  7053. os.path.join(self.path, "requests"), bare=True
  7054. )
  7055. set_up_git_repo(
  7056. self.session, self.path, new_project=None, branch_from="feature"
  7057. )
  7058. pagure.lib.query.get_authorized_project(self.session, "test")
  7059. request = pagure.lib.query.search_pull_requests(
  7060. self.session, requestid=1, project_id=1
  7061. )
  7062. pagure.lib.query.add_pull_request_comment(
  7063. self.session,
  7064. request=request,
  7065. commit=None,
  7066. tree_id=None,
  7067. filename=None,
  7068. row=None,
  7069. comment="Hello",
  7070. user="jdoe",
  7071. )
  7072. self.session.commit()
  7073. @patch("pagure.lib.notify.send_email")
  7074. def test_add_reaction(self, send_email):
  7075. """Test the request_pull endpoint."""
  7076. send_email.return_value = True
  7077. self._set_up_for_reaction_test()
  7078. user = tests.FakeUser()
  7079. user.username = "pingou"
  7080. with tests.user_set(self.app.application, user):
  7081. output = self.app.get("/test/pull-request/1")
  7082. self.assertEqual(output.status_code, 200)
  7083. data = {
  7084. "csrf_token": self.get_csrf(output=output),
  7085. "reaction": "Thumbs up",
  7086. }
  7087. output = self.app.post(
  7088. "/test/pull-request/1/comment/1/react",
  7089. data=data,
  7090. follow_redirects=True,
  7091. )
  7092. self.assertEqual(output.status_code, 200)
  7093. # Load the page and check reaction is added.
  7094. output = self.app.get("/test/pull-request/1")
  7095. self.assertEqual(output.status_code, 200)
  7096. self.assertIn(
  7097. "Thumbs up sent by pingou", output.get_data(as_text=True)
  7098. )
  7099. @patch("pagure.lib.notify.send_email")
  7100. def test_add_reaction_unauthenticated(self, send_email):
  7101. """Test the request_pull endpoint."""
  7102. send_email.return_value = True
  7103. self._set_up_for_reaction_test()
  7104. output = self.app.get("/test/pull-request/1")
  7105. self.assertEqual(output.status_code, 200)
  7106. data = {
  7107. "csrf_token": self.get_csrf(output=output),
  7108. "reaction": "Thumbs down",
  7109. }
  7110. output = self.app.post(
  7111. "/test/pull-request/1/comment/1/react",
  7112. data=data,
  7113. follow_redirects=False,
  7114. )
  7115. # Redirect to login page
  7116. self.assertEqual(output.status_code, 302)
  7117. self.assertIn("/login/", output.headers["Location"])
  7118. class TestTicketAccessEditPRMetadata(tests.Modeltests):
  7119. """Tests that people with ticket access on a project can edit the
  7120. meta-data of a PR"""
  7121. def setUp(self):
  7122. """Set up the environnment, ran before every tests."""
  7123. super(TestTicketAccessEditPRMetadata, self).setUp()
  7124. tests.create_projects(self.session)
  7125. tests.create_projects_git(
  7126. os.path.join(self.path, "requests"), bare=True
  7127. )
  7128. set_up_git_repo(
  7129. self.session, self.path, new_project=None, branch_from="feature"
  7130. )
  7131. # Add user "foo" to the project "test"
  7132. repo = pagure.lib.query._get_project(self.session, "test")
  7133. msg = pagure.lib.query.add_user_to_project(
  7134. session=self.session,
  7135. project=repo,
  7136. new_user="foo",
  7137. user="pingou",
  7138. access="ticket",
  7139. )
  7140. self.session.commit()
  7141. self.assertEqual(msg, "User added")
  7142. def test_unauth_cannot_view_edit_metadata_ui(self):
  7143. """Test that unauthenticated users cannot view the edit the
  7144. metadata fields in the UI."""
  7145. output = self.app.get("/test/pull-request/1")
  7146. self.assertEqual(output.status_code, 200)
  7147. output_text = output.get_data(as_text=True)
  7148. self.assertIn(
  7149. "<title>PR#1: PR from the feature branch - test\n"
  7150. " - Pagure</title>",
  7151. output_text,
  7152. )
  7153. self.assertIn(
  7154. '<i class="fa fa-fw fa-pencil"></i></a>',
  7155. '<a class="btn btn-outline-primary border-0 btn-sm '
  7156. "issue-metadata-display editmetadatatoggle pointer inline-block"
  7157. '"><i class="fa fa-fw fa-pencil"></i></a>',
  7158. output_text,
  7159. )
  7160. self.assertNotIn(
  7161. '<form method="POST" action="/test/pull-request/1/update">',
  7162. output_text,
  7163. )
  7164. def test_admin_can_view_edit_metadata_ui(self):
  7165. """Test that admin users can view the edit the metadata fields in
  7166. the UI."""
  7167. user = tests.FakeUser(username="pingou")
  7168. with tests.user_set(self.app.application, user):
  7169. output = self.app.get("/test/pull-request/1")
  7170. self.assertEqual(output.status_code, 200)
  7171. output_text = output.get_data(as_text=True)
  7172. self.assertIn(
  7173. "<title>PR#1: PR from the feature branch - test\n"
  7174. " - Pagure</title>",
  7175. output_text,
  7176. )
  7177. self.assertIn(
  7178. '<i class="fa fa-fw fa-pencil"></i></a>',
  7179. '<a class="btn btn-outline-primary border-0 btn-sm '
  7180. "issue-metadata-display editmetadatatoggle pointer inline-block"
  7181. '"><i class="fa fa-fw fa-pencil"></i></a>',
  7182. output_text,
  7183. )
  7184. self.assertIn(
  7185. '<form method="POST" action="/test/pull-request/1/update">',
  7186. output_text,
  7187. )
  7188. def test_admin_can_edit_metadata_ui(self):
  7189. """Test that admin users can edit the metadata in the UI."""
  7190. user = tests.FakeUser(username="pingou")
  7191. with tests.user_set(self.app.application, user):
  7192. data = {"csrf_token": self.get_csrf(), "user": "foo"}
  7193. output = self.app.post(
  7194. "/test/pull-request/1/update", data=data, follow_redirects=True
  7195. )
  7196. self.assertEqual(output.status_code, 200)
  7197. output_text = output.get_data(as_text=True)
  7198. self.assertIn(
  7199. "<title>PR#1: PR from the feature branch - test\n"
  7200. " - Pagure</title>",
  7201. output_text,
  7202. )
  7203. self.assertIn(
  7204. '<i class="fa fa-fw fa-pencil"></i></a>',
  7205. '<a class="btn btn-outline-primary border-0 btn-sm '
  7206. "issue-metadata-display editmetadatatoggle pointer inline-block"
  7207. '"><i class="fa fa-fw fa-pencil"></i></a>',
  7208. output_text,
  7209. )
  7210. self.assertIn(
  7211. '<form method="POST" action="/test/pull-request/1/update">',
  7212. output_text,
  7213. )
  7214. self.assertIn(
  7215. '<input value="foo"\n name="user" '
  7216. 'id="assignee" placeholder="username" >',
  7217. output_text,
  7218. )
  7219. def test_ticket_can_view_edit_metadata_ui(self):
  7220. """Test that users with ticket access can view the edit the
  7221. metadata fields in the UI."""
  7222. user = tests.FakeUser(username="foo")
  7223. with tests.user_set(self.app.application, user):
  7224. output = self.app.get("/test/pull-request/1")
  7225. self.assertEqual(output.status_code, 200)
  7226. output_text = output.get_data(as_text=True)
  7227. self.assertIn(
  7228. "<title>PR#1: PR from the feature branch - test\n"
  7229. " - Pagure</title>",
  7230. output_text,
  7231. )
  7232. self.assertIn(
  7233. '<i class="fa fa-fw fa-pencil"></i></a>',
  7234. '<a class="btn btn-outline-primary border-0 btn-sm '
  7235. "issue-metadata-display editmetadatatoggle pointer inline-block"
  7236. '"><i class="fa fa-fw fa-pencil"></i></a>',
  7237. output_text,
  7238. )
  7239. self.assertIn(
  7240. '<form method="POST" action="/test/pull-request/1/update">',
  7241. output_text,
  7242. )
  7243. def test_ticket_can_edit_metadata_ui(self):
  7244. """Test that users with ticket access can edit the metadata in the
  7245. UI."""
  7246. user = tests.FakeUser(username="foo")
  7247. with tests.user_set(self.app.application, user):
  7248. data = {"csrf_token": self.get_csrf(), "user": "pingou"}
  7249. output = self.app.post(
  7250. "/test/pull-request/1/update", data=data, follow_redirects=True
  7251. )
  7252. self.assertEqual(output.status_code, 200)
  7253. output_text = output.get_data(as_text=True)
  7254. self.assertIn(
  7255. "<title>PR#1: PR from the feature branch - test\n"
  7256. " - Pagure</title>",
  7257. output_text,
  7258. )
  7259. self.assertIn(
  7260. '<i class="fa fa-fw fa-pencil"></i></a>',
  7261. '<a class="btn btn-outline-primary border-0 btn-sm '
  7262. "issue-metadata-display editmetadatatoggle pointer inline-block"
  7263. '"><i class="fa fa-fw fa-pencil"></i></a>',
  7264. output_text,
  7265. )
  7266. self.assertIn(
  7267. '<form method="POST" action="/test/pull-request/1/update">',
  7268. output_text,
  7269. )
  7270. self.assertIn(
  7271. '<input value="pingou"\n name="user" '
  7272. 'id="assignee" placeholder="username" >',
  7273. output_text,
  7274. )
  7275. if __name__ == "__main__":
  7276. unittest.main(verbosity=2)