test_pagure_flask_internal.py 109 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049
  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
  8. __requires__ = ['SQLAlchemy >= 0.8']
  9. import pkg_resources
  10. import datetime
  11. import json
  12. import unittest
  13. import shutil
  14. import sys
  15. import time
  16. import os
  17. import pygit2
  18. from mock import patch, MagicMock, Mock
  19. sys.path.insert(0, os.path.join(os.path.dirname(
  20. os.path.abspath(__file__)), '..'))
  21. import pagure.lib.query
  22. import tests
  23. from pagure.lib.repo import PagureRepo
  24. class PagureFlaskInternaltests(tests.Modeltests):
  25. """ Tests for flask Internal controller of pagure """
  26. maxDiff = None
  27. def setUp(self):
  28. """ Set up the environnment, ran before every tests. """
  29. super(PagureFlaskInternaltests, self).setUp()
  30. pagure.config.config['IP_ALLOWED_INTERNAL'] = list(set(
  31. pagure.config.config['IP_ALLOWED_INTERNAL'] + [None]))
  32. pagure.config.config['GIT_FOLDER'] = os.path.join(
  33. self.path, 'repos')
  34. @patch.dict('pagure.config.config', {'IP_ALLOWED_INTERNAL': []})
  35. def test_internal_access_only(self):
  36. output = self.app.post('/pv/ssh/lookupkey/')
  37. self.assertEqual(output.status_code, 403)
  38. # no internal IP addresses => will fail
  39. output = self.app.post('/pv/ssh/lookupkey/')
  40. self.assertEqual(output.status_code, 403)
  41. # wrong token => will fail
  42. output = self.app.post(
  43. '/pv/ssh/lookupkey/',
  44. headers={"Authorization": "token doesntexist"}
  45. )
  46. self.assertEqual(output.status_code, 401)
  47. # correct token => will work
  48. pagure.lib.query.add_token_to_user(
  49. self.session, None, ['internal_access'], 'pingou'
  50. )
  51. token = pagure.lib.query.search_token(
  52. self.session, acls=['internal_access'], user='pingou'
  53. )
  54. output = self.app.post(
  55. '/pv/ssh/lookupkey/',
  56. headers={"Authorization": "token %s" % token[0].id}
  57. )
  58. self.assertEqual(output.status_code, 400)
  59. @patch('pagure.lib.notify.send_email')
  60. def test_pull_request_add_comment(self, send_email):
  61. """ Test the pull_request_add_comment function. """
  62. send_email.return_value = True
  63. tests.create_projects(self.session)
  64. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  65. req = pagure.lib.query.new_pull_request(
  66. session=self.session,
  67. repo_from=repo,
  68. branch_from='feature',
  69. repo_to=repo,
  70. branch_to='master',
  71. title='PR from the feature branch',
  72. user='pingou',
  73. )
  74. self.session.commit()
  75. self.assertEqual(req.id, 1)
  76. self.assertEqual(req.title, 'PR from the feature branch')
  77. request = repo.requests[0]
  78. self.assertEqual(len(request.comments), 0)
  79. self.assertEqual(len(request.discussion), 0)
  80. data = {
  81. 'objid': 'foo',
  82. }
  83. # Wrong http request
  84. output = self.app.post('/pv/pull-request/comment/', data=data)
  85. self.assertEqual(output.status_code, 405)
  86. # Invalid request
  87. output = self.app.put('/pv/pull-request/comment/', data=data)
  88. self.assertEqual(output.status_code, 400)
  89. data = {
  90. 'objid': 'foo',
  91. 'useremail': 'foo@pingou.com',
  92. }
  93. # Invalid objid
  94. output = self.app.put('/pv/pull-request/comment/', data=data)
  95. self.assertEqual(output.status_code, 404)
  96. data = {
  97. 'objid': request.uid,
  98. 'useremail': 'foo@pingou.com',
  99. }
  100. # Valid objid, in-complete data for a comment
  101. output = self.app.put('/pv/pull-request/comment/', data=data)
  102. self.assertEqual(output.status_code, 400)
  103. data = {
  104. 'objid': request.uid,
  105. 'useremail': 'foo@pingou.com',
  106. 'comment': 'Looks good to me!',
  107. }
  108. # Add comment
  109. output = self.app.put('/pv/pull-request/comment/', data=data)
  110. self.assertEqual(output.status_code, 200)
  111. js_data = json.loads(output.get_data(as_text=True))
  112. self.assertDictEqual(js_data, {'message': 'Comment added'})
  113. self.session.commit()
  114. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  115. request = repo.requests[0]
  116. self.assertEqual(len(request.comments), 1)
  117. self.assertEqual(len(request.discussion), 1)
  118. # Check the @internal_access_only
  119. before = pagure.config.config['IP_ALLOWED_INTERNAL'][:]
  120. pagure.config.config['IP_ALLOWED_INTERNAL'] = []
  121. output = self.app.put('/pv/pull-request/comment/', data=data)
  122. self.assertEqual(output.status_code, 403)
  123. pagure.config.config['IP_ALLOWED_INTERNAL'] = before[:]
  124. @patch('pagure.lib.notify.send_email')
  125. def test_ticket_add_comment(self, send_email):
  126. """ Test the ticket_add_comment function. """
  127. send_email.return_value = True
  128. tests.create_projects(self.session)
  129. # Create issues to play with
  130. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  131. msg = pagure.lib.query.new_issue(
  132. session=self.session,
  133. repo=repo,
  134. title='Test issue',
  135. content='We should work on this',
  136. user='pingou',
  137. )
  138. self.session.commit()
  139. self.assertEqual(msg.title, 'Test issue')
  140. issue = repo.issues[0]
  141. self.assertEqual(len(issue.comments), 0)
  142. data = {
  143. 'objid': 'foo',
  144. }
  145. # Wrong http request
  146. output = self.app.post('/pv/ticket/comment/', data=data)
  147. self.assertEqual(output.status_code, 405)
  148. # Invalid request
  149. output = self.app.put('/pv/ticket/comment/', data=data)
  150. self.assertEqual(output.status_code, 400)
  151. data = {
  152. 'objid': 'foo',
  153. 'useremail': 'foo@pingou.com',
  154. }
  155. # Invalid objid
  156. output = self.app.put('/pv/ticket/comment/', data=data)
  157. self.assertEqual(output.status_code, 404)
  158. data = {
  159. 'objid': issue.uid,
  160. 'useremail': 'foo@pingou.com',
  161. }
  162. # Valid objid, in-complete data for a comment
  163. output = self.app.put('/pv/ticket/comment/', data=data)
  164. self.assertEqual(output.status_code, 400)
  165. data = {
  166. 'objid': issue.uid,
  167. 'useremail': 'foo@pingou.com',
  168. 'comment': 'Looks good to me!',
  169. }
  170. # Add comment
  171. output = self.app.put('/pv/ticket/comment/', data=data)
  172. self.assertEqual(output.status_code, 200)
  173. js_data = json.loads(output.get_data(as_text=True))
  174. self.assertDictEqual(js_data, {'message': 'Comment added'})
  175. self.session.commit()
  176. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  177. issue = repo.issues[0]
  178. self.assertEqual(len(issue.comments), 1)
  179. # Check the @internal_access_only
  180. pagure.config.config['IP_ALLOWED_INTERNAL'].remove(None)
  181. before = pagure.config.config['IP_ALLOWED_INTERNAL'][:]
  182. pagure.config.config['IP_ALLOWED_INTERNAL'] = []
  183. output = self.app.put('/pv/ticket/comment/', data=data)
  184. self.assertEqual(output.status_code, 403)
  185. pagure.config.config['IP_ALLOWED_INTERNAL'] = before[:]
  186. @patch('pagure.lib.notify.send_email')
  187. def test_private_ticket_add_comment(self, send_email):
  188. """ Test the ticket_add_comment function on a private ticket. """
  189. send_email.return_value = True
  190. tests.create_projects(self.session)
  191. # Create issues to play with
  192. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  193. msg = pagure.lib.query.new_issue(
  194. session=self.session,
  195. repo=repo,
  196. title='Test issue',
  197. content='We should work on this, really',
  198. user='pingou',
  199. private=True,
  200. )
  201. self.session.commit()
  202. self.assertEqual(msg.title, 'Test issue')
  203. issue = repo.issues[0]
  204. self.assertEqual(len(issue.comments), 0)
  205. data = {
  206. 'objid': 'foo',
  207. }
  208. # Wrong http request
  209. output = self.app.post('/pv/ticket/comment/', data=data)
  210. self.assertEqual(output.status_code, 405)
  211. # Invalid request
  212. output = self.app.put('/pv/ticket/comment/', data=data)
  213. self.assertEqual(output.status_code, 400)
  214. data = {
  215. 'objid': 'foo',
  216. 'useremail': 'foo@pingou.com',
  217. }
  218. # Invalid objid
  219. output = self.app.put('/pv/ticket/comment/', data=data)
  220. self.assertEqual(output.status_code, 404)
  221. data = {
  222. 'objid': issue.uid,
  223. 'useremail': 'foo@bar.com',
  224. }
  225. # Valid objid, un-allowed user for this (private) ticket
  226. output = self.app.put('/pv/ticket/comment/', data=data)
  227. self.assertEqual(output.status_code, 403)
  228. data = {
  229. 'objid': issue.uid,
  230. 'useremail': 'foo@pingou.com',
  231. }
  232. # Valid objid, un-allowed user for this (private) ticket
  233. output = self.app.put('/pv/ticket/comment/', data=data)
  234. self.assertEqual(output.status_code, 400)
  235. data = {
  236. 'objid': issue.uid,
  237. 'useremail': 'foo@pingou.com',
  238. 'comment': 'Looks good to me!',
  239. }
  240. # Add comment
  241. output = self.app.put('/pv/ticket/comment/', data=data)
  242. self.assertEqual(output.status_code, 200)
  243. js_data = json.loads(output.get_data(as_text=True))
  244. self.assertDictEqual(js_data, {'message': 'Comment added'})
  245. self.session.commit()
  246. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  247. issue = repo.issues[0]
  248. self.assertEqual(len(issue.comments), 1)
  249. # Check the @internal_access_only
  250. before = pagure.config.config['IP_ALLOWED_INTERNAL'][:]
  251. pagure.config.config['IP_ALLOWED_INTERNAL'] = []
  252. output = self.app.put('/pv/ticket/comment/', data=data)
  253. self.assertEqual(output.status_code, 403)
  254. pagure.config.config['IP_ALLOWED_INTERNAL'] = before[:]
  255. @patch('pagure.lib.notify.send_email')
  256. def test_private_ticket_add_comment_acl(self, send_email):
  257. """ Test the ticket_add_comment function on a private ticket. """
  258. send_email.return_value = True
  259. tests.create_projects(self.session)
  260. # Create issues to play with
  261. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  262. msg = pagure.lib.query.new_issue(
  263. session=self.session,
  264. repo=repo,
  265. title='Test issue',
  266. content='We should work on this, really',
  267. user='pingou',
  268. private=True,
  269. )
  270. self.session.commit()
  271. self.assertEqual(msg.title, 'Test issue')
  272. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  273. issue = repo.issues[0]
  274. self.assertEqual(len(issue.comments), 0)
  275. # Currently, he is just an average user,
  276. # He doesn't have any access in this repo
  277. data = {
  278. 'objid': issue.uid,
  279. 'useremail': 'foo@bar.com',
  280. 'comment': 'Looks good to me!',
  281. }
  282. # Valid objid, un-allowed user for this (private) ticket
  283. output = self.app.put('/pv/ticket/comment/', data=data)
  284. self.assertEqual(output.status_code, 403)
  285. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  286. # Let's promote him to be a ticketer
  287. # He shoudn't be able to comment even then though
  288. msg = pagure.lib.query.add_user_to_project(
  289. self.session,
  290. project=repo,
  291. new_user='foo',
  292. user='pingou',
  293. access='ticket'
  294. )
  295. self.session.commit()
  296. self.assertEqual(msg, 'User added')
  297. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  298. self.assertEqual(
  299. sorted([u.username for u in repo.users]), ['foo'])
  300. self.assertEqual(
  301. sorted([u.username for u in repo.committers]), [])
  302. self.assertEqual(
  303. sorted([u.username for u in repo.admins]), [])
  304. output = self.app.put('/pv/ticket/comment/', data=data)
  305. self.assertEqual(output.status_code, 403)
  306. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  307. # Let's promote him to be a committer
  308. # He should be able to comment
  309. msg = pagure.lib.query.add_user_to_project(
  310. self.session,
  311. project=repo,
  312. new_user='foo',
  313. user='pingou',
  314. access='commit'
  315. )
  316. self.session.commit()
  317. self.assertEqual(msg, 'User access updated')
  318. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  319. self.assertEqual(
  320. sorted([u.username for u in repo.users]), ['foo'])
  321. self.assertEqual(
  322. sorted([u.username for u in repo.committers]), ['foo'])
  323. self.assertEqual(
  324. sorted([u.username for u in repo.admins]), [])
  325. # Add comment
  326. output = self.app.put('/pv/ticket/comment/', data=data)
  327. self.assertEqual(output.status_code, 200)
  328. js_data = json.loads(output.get_data(as_text=True))
  329. self.assertDictEqual(js_data, {'message': 'Comment added'})
  330. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  331. issue = repo.issues[0]
  332. self.assertEqual(len(issue.comments), 1)
  333. # Let's promote him to be a admin
  334. # He should be able to comment
  335. msg = pagure.lib.query.add_user_to_project(
  336. self.session,
  337. project=repo,
  338. new_user='foo',
  339. user='pingou',
  340. access='admin'
  341. )
  342. self.session.commit()
  343. self.assertEqual(msg, 'User access updated')
  344. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  345. self.assertEqual(
  346. sorted([u.username for u in repo.users]), ['foo'])
  347. self.assertEqual(
  348. sorted([u.username for u in repo.committers]), ['foo'])
  349. self.assertEqual(
  350. sorted([u.username for u in repo.admins]), ['foo'])
  351. # Add comment
  352. output = self.app.put('/pv/ticket/comment/', data=data)
  353. self.assertEqual(output.status_code, 200)
  354. js_data = json.loads(output.get_data(as_text=True))
  355. self.assertDictEqual(js_data, {'message': 'Comment added'})
  356. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  357. issue = repo.issues[0]
  358. self.assertEqual(len(issue.comments), 2)
  359. # Check the @internal_access_only
  360. before = pagure.config.config['IP_ALLOWED_INTERNAL'][:]
  361. pagure.config.config['IP_ALLOWED_INTERNAL'] = []
  362. output = self.app.put('/pv/ticket/comment/', data=data)
  363. self.assertEqual(output.status_code, 403)
  364. pagure.config.config['IP_ALLOWED_INTERNAL'] = before[:]
  365. @patch('pagure.lib.notify.send_email')
  366. def test_mergeable_request_pull_FF(self, send_email):
  367. """ Test the mergeable_request_pull endpoint with a fast-forward
  368. merge.
  369. """
  370. send_email.return_value = True
  371. # Create a git repo to play with
  372. origgitrepo = os.path.join(self.path, 'repos', 'test.git')
  373. self.assertFalse(os.path.exists(origgitrepo))
  374. os.makedirs(origgitrepo)
  375. orig_repo = pygit2.init_repository(origgitrepo, bare=True)
  376. os.makedirs(os.path.join(self.path, 'repos_tmp'))
  377. gitrepo = os.path.join(self.path, 'repos_tmp', 'test.git')
  378. repo = pygit2.clone_repository(origgitrepo, gitrepo)
  379. # Create a file in that git repo
  380. with open(os.path.join(gitrepo, 'sources'), 'w') as stream:
  381. stream.write('foo\n bar')
  382. repo.index.add('sources')
  383. repo.index.write()
  384. # Commits the files added
  385. tree = repo.index.write_tree()
  386. author = pygit2.Signature(
  387. 'Alice Author', 'alice@authors.tld')
  388. committer = pygit2.Signature(
  389. 'Cecil Committer', 'cecil@committers.tld')
  390. repo.create_commit(
  391. 'refs/heads/master', # the name of the reference to update
  392. author,
  393. committer,
  394. 'Add sources file for testing',
  395. # binary string representing the tree object ID
  396. tree,
  397. # list of binary strings representing parents of the new commit
  398. []
  399. )
  400. first_commit = repo.revparse_single('HEAD')
  401. refname = 'refs/heads/master:refs/heads/master'
  402. ori_remote = repo.remotes[0]
  403. PagureRepo.push(ori_remote, refname)
  404. # Edit the sources file again
  405. with open(os.path.join(gitrepo, 'sources'), 'w') as stream:
  406. stream.write('foo\n bar\nbaz\n boose')
  407. repo.index.add('sources')
  408. repo.index.write()
  409. # Commits the files added
  410. tree = repo.index.write_tree()
  411. author = pygit2.Signature(
  412. 'Alice Author', 'alice@authors.tld')
  413. committer = pygit2.Signature(
  414. 'Cecil Committer', 'cecil@committers.tld')
  415. repo.create_commit(
  416. 'refs/heads/feature', # the name of the reference to update
  417. author,
  418. committer,
  419. 'Add baz and boose to the sources\n\n There are more objects to '
  420. 'consider',
  421. # binary string representing the tree object ID
  422. tree,
  423. # list of binary strings representing parents of the new commit
  424. [first_commit.oid.hex]
  425. )
  426. second_commit = repo.revparse_single('HEAD')
  427. refname = 'refs/heads/feature:refs/heads/feature'
  428. ori_remote = repo.remotes[0]
  429. PagureRepo.push(ori_remote, refname)
  430. # Create a PR for these changes
  431. tests.create_projects(self.session)
  432. project = pagure.lib.query.get_authorized_project(self.session, 'test')
  433. req = pagure.lib.query.new_pull_request(
  434. session=self.session,
  435. repo_from=project,
  436. branch_from='feature',
  437. repo_to=project,
  438. branch_to='master',
  439. title='PR from the feature branch',
  440. user='pingou',
  441. )
  442. self.session.commit()
  443. self.assertEqual(req.id, 1)
  444. self.assertEqual(req.title, 'PR from the feature branch')
  445. # Check if the PR can be merged
  446. data = {
  447. 'objid': 'blah',
  448. }
  449. # Missing CSRF
  450. output = self.app.post('/pv/pull-request/merge', data=data)
  451. self.assertEqual(output.status_code, 400)
  452. user = tests.FakeUser()
  453. user.username = 'pingou'
  454. with tests.user_set(self.app.application, user):
  455. output = self.app.get('/test/adduser')
  456. csrf_token = output.get_data(as_text=True).split(
  457. 'name="csrf_token" type="hidden" value="')[1].split('">')[0]
  458. # Missing request identifier
  459. data = {
  460. 'csrf_token': csrf_token,
  461. }
  462. output = self.app.post('/pv/pull-request/merge', data=data)
  463. self.assertEqual(output.status_code, 404)
  464. # With all the desired information
  465. project = pagure.lib.query.get_authorized_project(self.session, 'test')
  466. data = {
  467. 'csrf_token': csrf_token,
  468. 'requestid': project.requests[0].uid,
  469. }
  470. output = self.app.post('/pv/pull-request/merge', data=data)
  471. self.assertEqual(output.status_code, 200)
  472. exp = {
  473. "code": "FFORWARD",
  474. "message": "The pull-request can be merged and fast-forwarded",
  475. "short_code": "Ok"
  476. }
  477. js_data = json.loads(output.get_data(as_text=True))
  478. self.assertDictEqual(js_data, exp)
  479. @patch('pagure.lib.notify.send_email')
  480. def test_mergeable_request_pull_no_change(self, send_email):
  481. """ Test the mergeable_request_pull endpoint when there are no
  482. changes to merge.
  483. """
  484. send_email.return_value = True
  485. # Create a git repo to play with
  486. gitrepo = os.path.join(self.path, 'repos', 'test.git')
  487. self.assertFalse(os.path.exists(gitrepo))
  488. os.makedirs(gitrepo)
  489. repo = pygit2.init_repository(gitrepo)
  490. # Create a file in that git repo
  491. with open(os.path.join(gitrepo, 'sources'), 'w') as stream:
  492. stream.write('foo\n bar')
  493. repo.index.add('sources')
  494. repo.index.write()
  495. # Commits the files added
  496. tree = repo.index.write_tree()
  497. author = pygit2.Signature(
  498. 'Alice Author', 'alice@authors.tld')
  499. committer = pygit2.Signature(
  500. 'Cecil Committer', 'cecil@committers.tld')
  501. repo.create_commit(
  502. 'refs/heads/master', # the name of the reference to update
  503. author,
  504. committer,
  505. 'Add sources file for testing',
  506. # binary string representing the tree object ID
  507. tree,
  508. # list of binary strings representing parents of the new commit
  509. []
  510. )
  511. first_commit = repo.revparse_single('HEAD')
  512. # Edit the sources file again
  513. with open(os.path.join(gitrepo, 'sources'), 'w') as stream:
  514. stream.write('foo\n bar\nbaz\n boose')
  515. repo.index.add('sources')
  516. repo.index.write()
  517. # Commits the files added
  518. tree = repo.index.write_tree()
  519. author = pygit2.Signature(
  520. 'Alice Author', 'alice@authors.tld')
  521. committer = pygit2.Signature(
  522. 'Cecil Committer', 'cecil@committers.tld')
  523. repo.create_commit(
  524. 'refs/heads/master', # the name of the reference to update
  525. author,
  526. committer,
  527. 'Add baz and boose to the sources\n\n There are more objects to '
  528. 'consider',
  529. # binary string representing the tree object ID
  530. tree,
  531. # list of binary strings representing parents of the new commit
  532. [first_commit.oid.hex]
  533. )
  534. second_commit = repo.revparse_single('HEAD')
  535. # Create a PR for these changes
  536. tests.create_projects(self.session)
  537. project = pagure.lib.query.get_authorized_project(self.session, 'test')
  538. req = pagure.lib.query.new_pull_request(
  539. session=self.session,
  540. repo_from=project,
  541. branch_from='master',
  542. repo_to=project,
  543. branch_to='master',
  544. title='PR from the feature branch',
  545. user='pingou',
  546. )
  547. self.session.commit()
  548. self.assertEqual(req.id, 1)
  549. self.assertEqual(req.title, 'PR from the feature branch')
  550. # Check if the PR can be merged
  551. data = {
  552. 'objid': 'blah',
  553. }
  554. # Missing CSRF
  555. output = self.app.post('/pv/pull-request/merge', data=data)
  556. self.assertEqual(output.status_code, 400)
  557. user = tests.FakeUser()
  558. user.username = 'pingou'
  559. with tests.user_set(self.app.application, user):
  560. output = self.app.get('/test/adduser')
  561. csrf_token = output.get_data(as_text=True).split(
  562. 'name="csrf_token" type="hidden" value="')[1].split('">')[0]
  563. # Missing request identifier
  564. data = {
  565. 'csrf_token': csrf_token,
  566. }
  567. output = self.app.post('/pv/pull-request/merge', data=data)
  568. self.assertEqual(output.status_code, 404)
  569. # With all the desired information
  570. project = pagure.lib.query.get_authorized_project(self.session, 'test')
  571. data = {
  572. 'csrf_token': csrf_token,
  573. 'requestid': project.requests[0].uid,
  574. }
  575. output = self.app.post('/pv/pull-request/merge', data=data)
  576. self.assertEqual(output.status_code, 200)
  577. exp = {
  578. "code": "NO_CHANGE",
  579. "message": "Nothing to change, git is up to date",
  580. "short_code": "No changes"
  581. }
  582. js_data = json.loads(output.get_data(as_text=True))
  583. self.assertDictEqual(js_data, exp)
  584. @patch('pagure.lib.notify.send_email')
  585. def test_mergeable_request_pull_merge(self, send_email):
  586. """ Test the mergeable_request_pull endpoint when the changes can
  587. be merged with a merge commit.
  588. """
  589. send_email.return_value = True
  590. # Create a git repo to play with
  591. origgitrepo = os.path.join(self.path, 'repos', 'test.git')
  592. self.assertFalse(os.path.exists(origgitrepo))
  593. os.makedirs(origgitrepo)
  594. orig_repo = pygit2.init_repository(origgitrepo, bare=True)
  595. os.makedirs(os.path.join(self.path, 'repos_tmp'))
  596. gitrepo = os.path.join(self.path, 'repos_tmp', 'test.git')
  597. repo = pygit2.clone_repository(origgitrepo, gitrepo)
  598. # Create a file in that git repo
  599. with open(os.path.join(gitrepo, 'sources'), 'w') as stream:
  600. stream.write('foo\n bar')
  601. repo.index.add('sources')
  602. repo.index.write()
  603. # Commits the files added
  604. tree = repo.index.write_tree()
  605. author = pygit2.Signature(
  606. 'Alice Author', 'alice@authors.tld')
  607. committer = pygit2.Signature(
  608. 'Cecil Committer', 'cecil@committers.tld')
  609. repo.create_commit(
  610. 'refs/heads/master', # the name of the reference to update
  611. author,
  612. committer,
  613. 'Add sources file for testing',
  614. # binary string representing the tree object ID
  615. tree,
  616. # list of binary strings representing parents of the new commit
  617. []
  618. )
  619. first_commit = repo.revparse_single('HEAD')
  620. refname = 'refs/heads/master:refs/heads/master'
  621. ori_remote = repo.remotes[0]
  622. PagureRepo.push(ori_remote, refname)
  623. # Edit the sources file again
  624. with open(os.path.join(gitrepo, 'sources'), 'w') as stream:
  625. stream.write('foo\n bar\nbaz\n boose')
  626. repo.index.add('sources')
  627. repo.index.write()
  628. # Commits the files added
  629. tree = repo.index.write_tree()
  630. author = pygit2.Signature(
  631. 'Alice Author', 'alice@authors.tld')
  632. committer = pygit2.Signature(
  633. 'Cecil Committer', 'cecil@committers.tld')
  634. repo.create_commit(
  635. 'refs/heads/feature', # the name of the reference to update
  636. author,
  637. committer,
  638. 'Add baz and boose to the sources\n\n There are more objects to '
  639. 'consider',
  640. # binary string representing the tree object ID
  641. tree,
  642. # list of binary strings representing parents of the new commit
  643. [first_commit.oid.hex]
  644. )
  645. refname = 'refs/heads/feature:refs/heads/feature'
  646. ori_remote = repo.remotes[0]
  647. PagureRepo.push(ori_remote, refname)
  648. # Create another file in the master branch
  649. with open(os.path.join(gitrepo, '.gitignore'), 'w') as stream:
  650. stream.write('*~')
  651. repo.index.add('.gitignore')
  652. repo.index.write()
  653. # Commits the files added
  654. tree = repo.index.write_tree()
  655. author = pygit2.Signature(
  656. 'Alice Author', 'alice@authors.tld')
  657. committer = pygit2.Signature(
  658. 'Cecil Committer', 'cecil@committers.tld')
  659. repo.create_commit(
  660. 'refs/heads/master', # the name of the reference to update
  661. author,
  662. committer,
  663. 'Add .gitignore file for testing',
  664. # binary string representing the tree object ID
  665. tree,
  666. # list of binary strings representing parents of the new commit
  667. [first_commit.oid.hex]
  668. )
  669. refname = 'refs/heads/master:refs/heads/master'
  670. ori_remote = repo.remotes[0]
  671. PagureRepo.push(ori_remote, refname)
  672. # Create a PR for these changes
  673. tests.create_projects(self.session)
  674. project = pagure.lib.query.get_authorized_project(self.session, 'test')
  675. req = pagure.lib.query.new_pull_request(
  676. session=self.session,
  677. repo_from=project,
  678. branch_from='feature',
  679. repo_to=project,
  680. branch_to='master',
  681. title='PR from the feature branch',
  682. user='pingou',
  683. )
  684. self.session.commit()
  685. self.assertEqual(req.id, 1)
  686. self.assertEqual(req.title, 'PR from the feature branch')
  687. # Check if the PR can be merged
  688. data = {}
  689. # Missing CSRF
  690. output = self.app.post('/pv/pull-request/merge', data=data)
  691. self.assertEqual(output.status_code, 400)
  692. user = tests.FakeUser()
  693. user.username = 'pingou'
  694. with tests.user_set(self.app.application, user):
  695. output = self.app.get('/test/adduser')
  696. csrf_token = output.get_data(as_text=True).split(
  697. 'name="csrf_token" type="hidden" value="')[1].split('">')[0]
  698. # Missing request identifier
  699. data = {
  700. 'csrf_token': csrf_token,
  701. }
  702. output = self.app.post('/pv/pull-request/merge', data=data)
  703. self.assertEqual(output.status_code, 404)
  704. # With all the desired information
  705. project = pagure.lib.query.get_authorized_project(self.session, 'test')
  706. data = {
  707. 'csrf_token': csrf_token,
  708. 'requestid': project.requests[0].uid,
  709. 'force': True,
  710. }
  711. output = self.app.post('/pv/pull-request/merge', data=data)
  712. self.assertEqual(output.status_code, 200)
  713. exp = {
  714. "code": "MERGE",
  715. "message": "The pull-request can be merged with a merge commit",
  716. "short_code": "With merge"
  717. }
  718. js_data = json.loads(output.get_data(as_text=True))
  719. self.assertDictEqual(js_data, exp)
  720. # Asking a second time will trigger the cache
  721. data = {
  722. 'csrf_token': csrf_token,
  723. 'requestid': project.requests[0].uid,
  724. }
  725. output = self.app.post('/pv/pull-request/merge', data=data)
  726. self.assertEqual(output.status_code, 200)
  727. exp = {
  728. "code": "MERGE",
  729. "message": "The pull-request can be merged with a merge commit",
  730. "short_code": "With merge"
  731. }
  732. js_data = json.loads(output.get_data(as_text=True))
  733. self.assertDictEqual(js_data, exp)
  734. @patch('pagure.lib.notify.send_email')
  735. def test_mergeable_request_pull_conflicts(self, send_email):
  736. """ Test the mergeable_request_pull endpoint when the changes cannot
  737. be merged due to conflicts.
  738. """
  739. send_email.return_value = True
  740. # Create a git repo to play with
  741. origgitrepo = os.path.join(self.path, 'repos', 'test.git')
  742. self.assertFalse(os.path.exists(origgitrepo))
  743. os.makedirs(origgitrepo)
  744. orig_repo = pygit2.init_repository(origgitrepo, bare=True)
  745. os.makedirs(os.path.join(self.path, 'repos_tmp'))
  746. gitrepo = os.path.join(self.path, 'repos_tmp', 'test.git')
  747. repo = pygit2.clone_repository(origgitrepo, gitrepo)
  748. # Create a file in that git repo
  749. with open(os.path.join(gitrepo, 'sources'), 'w') as stream:
  750. stream.write('foo\n bar')
  751. repo.index.add('sources')
  752. repo.index.write()
  753. # Commits the files added
  754. tree = repo.index.write_tree()
  755. author = pygit2.Signature(
  756. 'Alice Author', 'alice@authors.tld')
  757. committer = pygit2.Signature(
  758. 'Cecil Committer', 'cecil@committers.tld')
  759. repo.create_commit(
  760. 'refs/heads/master', # the name of the reference to update
  761. author,
  762. committer,
  763. 'Add sources file for testing',
  764. # binary string representing the tree object ID
  765. tree,
  766. # list of binary strings representing parents of the new commit
  767. []
  768. )
  769. first_commit = repo.revparse_single('HEAD')
  770. refname = 'refs/heads/master:refs/heads/master'
  771. ori_remote = repo.remotes[0]
  772. PagureRepo.push(ori_remote, refname)
  773. # Edit the sources file again
  774. with open(os.path.join(gitrepo, 'sources'), 'w') as stream:
  775. stream.write('foo\n bar\nbaz\n boose')
  776. repo.index.add('sources')
  777. repo.index.write()
  778. # Commits the files added
  779. tree = repo.index.write_tree()
  780. author = pygit2.Signature(
  781. 'Alice Author', 'alice@authors.tld')
  782. committer = pygit2.Signature(
  783. 'Cecil Committer', 'cecil@committers.tld')
  784. repo.create_commit(
  785. 'refs/heads/feature', # the name of the reference to update
  786. author,
  787. committer,
  788. 'Add baz and boose to the sources\n\n There are more objects to '
  789. 'consider',
  790. # binary string representing the tree object ID
  791. tree,
  792. # list of binary strings representing parents of the new commit
  793. [first_commit.oid.hex]
  794. )
  795. refname = 'refs/heads/feature:refs/heads/feature'
  796. ori_remote = repo.remotes[0]
  797. PagureRepo.push(ori_remote, refname)
  798. # Create another file in the master branch
  799. with open(os.path.join(gitrepo, 'sources'), 'w') as stream:
  800. stream.write('foo\n bar\nbaz\n')
  801. repo.index.add('sources')
  802. repo.index.write()
  803. # Commits the files added
  804. tree = repo.index.write_tree()
  805. author = pygit2.Signature(
  806. 'Alice Author', 'alice@authors.tld')
  807. committer = pygit2.Signature(
  808. 'Cecil Committer', 'cecil@committers.tld')
  809. repo.create_commit(
  810. 'refs/heads/master', # the name of the reference to update
  811. author,
  812. committer,
  813. 'Add .gitignore file for testing',
  814. # binary string representing the tree object ID
  815. tree,
  816. # list of binary strings representing parents of the new commit
  817. [first_commit.oid.hex]
  818. )
  819. refname = 'refs/heads/master:refs/heads/master'
  820. ori_remote = repo.remotes[0]
  821. PagureRepo.push(ori_remote, refname)
  822. # Create a PR for these changes
  823. tests.create_projects(self.session)
  824. project = pagure.lib.query.get_authorized_project(self.session, 'test')
  825. req = pagure.lib.query.new_pull_request(
  826. session=self.session,
  827. repo_from=project,
  828. branch_from='feature',
  829. repo_to=project,
  830. branch_to='master',
  831. title='PR from the feature branch',
  832. user='pingou',
  833. )
  834. self.session.commit()
  835. self.assertEqual(req.id, 1)
  836. self.assertEqual(req.title, 'PR from the feature branch')
  837. # Check if the PR can be merged
  838. data = {}
  839. # Missing CSRF
  840. output = self.app.post('/pv/pull-request/merge', data=data)
  841. self.assertEqual(output.status_code, 400)
  842. user = tests.FakeUser()
  843. user.username = 'pingou'
  844. with tests.user_set(self.app.application, user):
  845. output = self.app.get('/test/adduser')
  846. csrf_token = output.get_data(as_text=True).split(
  847. 'name="csrf_token" type="hidden" value="')[1].split('">')[0]
  848. # Missing request identifier
  849. data = {
  850. 'csrf_token': csrf_token,
  851. }
  852. output = self.app.post('/pv/pull-request/merge', data=data)
  853. self.assertEqual(output.status_code, 404)
  854. # With all the desired information
  855. project = pagure.lib.query.get_authorized_project(self.session, 'test')
  856. data = {
  857. 'csrf_token': csrf_token,
  858. 'requestid': project.requests[0].uid,
  859. }
  860. output = self.app.post('/pv/pull-request/merge', data=data)
  861. self.assertEqual(output.status_code, 200)
  862. exp = {
  863. "code": "CONFLICTS",
  864. "message": "The pull-request cannot be merged due to conflicts",
  865. "short_code": "Conflicts"
  866. }
  867. js_data = json.loads(output.get_data(as_text=True))
  868. self.assertDictEqual(js_data, exp)
  869. @patch('pagure.lib.notify.send_email')
  870. def test_mergeable_request_pull_merge_no_nonff_merges(self, send_email):
  871. """ Test the mergeable_request_pull endpoint when the changes can
  872. be merged with a merge commit, but project settings prohibit this.
  873. """
  874. send_email.return_value = True
  875. # Create a git repo to play with
  876. origgitrepo = os.path.join(self.path, 'repos', 'test.git')
  877. self.assertFalse(os.path.exists(origgitrepo))
  878. os.makedirs(origgitrepo)
  879. orig_repo = pygit2.init_repository(origgitrepo, bare=True)
  880. os.makedirs(os.path.join(self.path, 'repos_tmp'))
  881. gitrepo = os.path.join(self.path, 'repos_tmp', 'test.git')
  882. repo = pygit2.clone_repository(origgitrepo, gitrepo)
  883. # Create a file in that git repo
  884. with open(os.path.join(gitrepo, 'sources'), 'w') as stream:
  885. stream.write('foo\n bar')
  886. repo.index.add('sources')
  887. repo.index.write()
  888. # Commits the files added
  889. tree = repo.index.write_tree()
  890. author = pygit2.Signature(
  891. 'Alice Author', 'alice@authors.tld')
  892. committer = pygit2.Signature(
  893. 'Cecil Committer', 'cecil@committers.tld')
  894. repo.create_commit(
  895. 'refs/heads/master', # the name of the reference to update
  896. author,
  897. committer,
  898. 'Add sources file for testing',
  899. # binary string representing the tree object ID
  900. tree,
  901. # list of binary strings representing parents of the new commit
  902. []
  903. )
  904. first_commit = repo.revparse_single('HEAD')
  905. refname = 'refs/heads/master:refs/heads/master'
  906. ori_remote = repo.remotes[0]
  907. PagureRepo.push(ori_remote, refname)
  908. # Edit the sources file again
  909. with open(os.path.join(gitrepo, 'sources'), 'w') as stream:
  910. stream.write('foo\n bar\nbaz\n boose')
  911. repo.index.add('sources')
  912. repo.index.write()
  913. # Commits the files added
  914. tree = repo.index.write_tree()
  915. author = pygit2.Signature(
  916. 'Alice Author', 'alice@authors.tld')
  917. committer = pygit2.Signature(
  918. 'Cecil Committer', 'cecil@committers.tld')
  919. repo.create_commit(
  920. 'refs/heads/feature', # the name of the reference to update
  921. author,
  922. committer,
  923. 'Add baz and boose to the sources\n\n There are more objects to '
  924. 'consider',
  925. # binary string representing the tree object ID
  926. tree,
  927. # list of binary strings representing parents of the new commit
  928. [first_commit.oid.hex]
  929. )
  930. refname = 'refs/heads/feature:refs/heads/feature'
  931. ori_remote = repo.remotes[0]
  932. PagureRepo.push(ori_remote, refname)
  933. # Create another file in the master branch
  934. with open(os.path.join(gitrepo, '.gitignore'), 'w') as stream:
  935. stream.write('*~')
  936. repo.index.add('.gitignore')
  937. repo.index.write()
  938. # Commits the files added
  939. tree = repo.index.write_tree()
  940. author = pygit2.Signature(
  941. 'Alice Author', 'alice@authors.tld')
  942. committer = pygit2.Signature(
  943. 'Cecil Committer', 'cecil@committers.tld')
  944. repo.create_commit(
  945. 'refs/heads/master', # the name of the reference to update
  946. author,
  947. committer,
  948. 'Add .gitignore file for testing',
  949. # binary string representing the tree object ID
  950. tree,
  951. # list of binary strings representing parents of the new commit
  952. [first_commit.oid.hex]
  953. )
  954. refname = 'refs/heads/master:refs/heads/master'
  955. ori_remote = repo.remotes[0]
  956. PagureRepo.push(ori_remote, refname)
  957. # Create a PR for these changes
  958. tests.create_projects(self.session)
  959. project = pagure.lib.query.get_authorized_project(self.session, 'test')
  960. req = pagure.lib.query.new_pull_request(
  961. session=self.session,
  962. repo_from=project,
  963. branch_from='feature',
  964. repo_to=project,
  965. branch_to='master',
  966. title='PR from the feature branch',
  967. user='pingou',
  968. )
  969. settings = {'disable_non_fast-forward_merges': True}
  970. project.settings = settings
  971. self.session.commit()
  972. self.assertEqual(req.id, 1)
  973. self.assertEqual(req.title, 'PR from the feature branch')
  974. # Check if the PR can be merged
  975. data = {}
  976. user = tests.FakeUser()
  977. user.username = 'pingou'
  978. with tests.user_set(self.app.application, user):
  979. output = self.app.get('/test/adduser')
  980. csrf_token = output.get_data(as_text=True).split(
  981. 'name="csrf_token" type="hidden" value="')[1].split('">')[0]
  982. # With all the desired information
  983. project = pagure.lib.query.get_authorized_project(self.session, 'test')
  984. data = {
  985. 'csrf_token': csrf_token,
  986. 'requestid': project.requests[0].uid,
  987. 'force': True,
  988. }
  989. output = self.app.post('/pv/pull-request/merge', data=data)
  990. self.assertEqual(output.status_code, 200)
  991. exp = {
  992. "code": "NEEDSREBASE",
  993. "message": "The pull-request must be rebased before merging",
  994. "short_code": "Needs rebase"
  995. }
  996. js_data = json.loads(output.get_data(as_text=True))
  997. self.assertDictEqual(js_data, exp)
  998. # Asking a second time will trigger the cache
  999. data = {
  1000. 'csrf_token': csrf_token,
  1001. 'requestid': project.requests[0].uid,
  1002. }
  1003. output = self.app.post('/pv/pull-request/merge', data=data)
  1004. self.assertEqual(output.status_code, 200)
  1005. exp = {
  1006. "code": "NEEDSREBASE",
  1007. "message": "The pull-request must be rebased before merging",
  1008. "short_code": "Needs rebase"
  1009. }
  1010. js_data = json.loads(output.get_data(as_text=True))
  1011. self.assertDictEqual(js_data, exp)
  1012. @patch('pagure.lib.notify.send_email')
  1013. def test_mergeable_request_pull_minimum_score(self, send_email):
  1014. """ Test the mergeable_request_pull endpoint when the changes can
  1015. be merged with a merge FF, but project settings enforces vote on
  1016. the PR.
  1017. """
  1018. send_email.return_value = True
  1019. # Create a git repo to play with
  1020. origgitrepo = os.path.join(self.path, 'repos', 'test.git')
  1021. self.assertFalse(os.path.exists(origgitrepo))
  1022. os.makedirs(origgitrepo)
  1023. orig_repo = pygit2.init_repository(origgitrepo, bare=True)
  1024. os.makedirs(os.path.join(self.path, 'repos_tmp'))
  1025. gitrepo = os.path.join(self.path, 'repos_tmp', 'test.git')
  1026. repo = pygit2.clone_repository(origgitrepo, gitrepo)
  1027. # Create a file in that git repo
  1028. with open(os.path.join(gitrepo, 'sources'), 'w') as stream:
  1029. stream.write('foo\n bar')
  1030. repo.index.add('sources')
  1031. repo.index.write()
  1032. # Commits the files added
  1033. tree = repo.index.write_tree()
  1034. author = pygit2.Signature(
  1035. 'Alice Author', 'alice@authors.tld')
  1036. committer = pygit2.Signature(
  1037. 'Cecil Committer', 'cecil@committers.tld')
  1038. repo.create_commit(
  1039. 'refs/heads/master', # the name of the reference to update
  1040. author,
  1041. committer,
  1042. 'Add sources file for testing',
  1043. # binary string representing the tree object ID
  1044. tree,
  1045. # list of binary strings representing parents of the new commit
  1046. []
  1047. )
  1048. first_commit = repo.revparse_single('HEAD')
  1049. refname = 'refs/heads/master:refs/heads/master'
  1050. ori_remote = repo.remotes[0]
  1051. PagureRepo.push(ori_remote, refname)
  1052. # Edit the sources file again
  1053. with open(os.path.join(gitrepo, 'sources'), 'w') as stream:
  1054. stream.write('foo\n bar\nbaz\n boose')
  1055. repo.index.add('sources')
  1056. repo.index.write()
  1057. # Commits the files added
  1058. tree = repo.index.write_tree()
  1059. author = pygit2.Signature(
  1060. 'Alice Author', 'alice@authors.tld')
  1061. committer = pygit2.Signature(
  1062. 'Cecil Committer', 'cecil@committers.tld')
  1063. repo.create_commit(
  1064. 'refs/heads/feature', # the name of the reference to update
  1065. author,
  1066. committer,
  1067. 'Add baz and boose to the sources\n\n There are more objects to '
  1068. 'consider',
  1069. # binary string representing the tree object ID
  1070. tree,
  1071. # list of binary strings representing parents of the new commit
  1072. [first_commit.oid.hex]
  1073. )
  1074. refname = 'refs/heads/feature:refs/heads/feature'
  1075. ori_remote = repo.remotes[0]
  1076. PagureRepo.push(ori_remote, refname)
  1077. # Create a PR for these changes
  1078. tests.create_projects(self.session)
  1079. project = pagure.lib.query.get_authorized_project(self.session, 'test')
  1080. req = pagure.lib.query.new_pull_request(
  1081. session=self.session,
  1082. repo_from=project,
  1083. branch_from='feature',
  1084. repo_to=project,
  1085. branch_to='master',
  1086. title='PR from the feature branch',
  1087. user='pingou',
  1088. )
  1089. settings = {'Minimum_score_to_merge_pull-request': 2}
  1090. project.settings = settings
  1091. self.session.commit()
  1092. self.assertEqual(req.id, 1)
  1093. self.assertEqual(req.title, 'PR from the feature branch')
  1094. # Check if the PR can be merged
  1095. data = {}
  1096. user = tests.FakeUser()
  1097. user.username = 'pingou'
  1098. with tests.user_set(self.app.application, user):
  1099. output = self.app.get('/test/adduser')
  1100. csrf_token = output.get_data(as_text=True).split(
  1101. 'name="csrf_token" type="hidden" value="')[1].split('">')[0]
  1102. # With all the desired information
  1103. project = pagure.lib.query.get_authorized_project(self.session, 'test')
  1104. data = {
  1105. 'csrf_token': csrf_token,
  1106. 'requestid': project.requests[0].uid,
  1107. 'force': True,
  1108. }
  1109. output = self.app.post('/pv/pull-request/merge', data=data)
  1110. self.assertEqual(output.status_code, 400)
  1111. exp = {
  1112. "code": "CONFLICTS",
  1113. "message": "Pull-Request does not meet the minimal number "
  1114. "of review required: 0/2"
  1115. }
  1116. js_data = json.loads(output.get_data(as_text=True))
  1117. self.assertDictEqual(js_data, exp)
  1118. # Verify we get a valid merge_status (not 'unknown')
  1119. pub_api_call = self.app.get('/api/0/test/pull-request/1')
  1120. data = json.loads(pub_api_call.get_data(as_text=True))
  1121. self.assertIn(
  1122. data['cached_merge_status'], ('MERGE', 'FFORWARD'))
  1123. # Asking a second time will trigger the cache
  1124. data = {
  1125. 'csrf_token': csrf_token,
  1126. 'requestid': project.requests[0].uid,
  1127. }
  1128. output = self.app.post('/pv/pull-request/merge', data=data)
  1129. self.assertEqual(output.status_code, 400)
  1130. exp = {
  1131. "code": "CONFLICTS",
  1132. "message": "Pull-Request does not meet the minimal number "
  1133. "of review required: 0/2"
  1134. }
  1135. js_data = json.loads(output.get_data(as_text=True))
  1136. self.assertDictEqual(js_data, exp)
  1137. @patch('pagure.lib.notify.send_email', MagicMock(return_value=True))
  1138. @patch(
  1139. 'pagure.lib.git.merge_pull_request',
  1140. MagicMock(side_effect=pagure.exceptions.PagureException('error')))
  1141. def test_mergeable_request_pull_merge_pagureerror(self):
  1142. """ Test the mergeable_request_pull endpoint when the backend
  1143. raises an GitError exception.
  1144. """
  1145. # Create a git repo to play with
  1146. origgitrepo = os.path.join(self.path, 'repos', 'test.git')
  1147. self.assertFalse(os.path.exists(origgitrepo))
  1148. os.makedirs(origgitrepo)
  1149. orig_repo = pygit2.init_repository(origgitrepo, bare=True)
  1150. os.makedirs(os.path.join(self.path, 'repos_tmp'))
  1151. gitrepo = os.path.join(self.path, 'repos_tmp', 'test.git')
  1152. repo = pygit2.clone_repository(origgitrepo, gitrepo)
  1153. # Create a file in that git repo
  1154. with open(os.path.join(gitrepo, 'sources'), 'w') as stream:
  1155. stream.write('foo\n bar')
  1156. repo.index.add('sources')
  1157. repo.index.write()
  1158. # Commits the files added
  1159. tree = repo.index.write_tree()
  1160. author = pygit2.Signature(
  1161. 'Alice Author', 'alice@authors.tld')
  1162. committer = pygit2.Signature(
  1163. 'Cecil Committer', 'cecil@committers.tld')
  1164. repo.create_commit(
  1165. 'refs/heads/master', # the name of the reference to update
  1166. author,
  1167. committer,
  1168. 'Add sources file for testing',
  1169. # binary string representing the tree object ID
  1170. tree,
  1171. # list of binary strings representing parents of the new commit
  1172. []
  1173. )
  1174. first_commit = repo.revparse_single('HEAD')
  1175. refname = 'refs/heads/master:refs/heads/master'
  1176. ori_remote = repo.remotes[0]
  1177. PagureRepo.push(ori_remote, refname)
  1178. # Edit the sources file again
  1179. with open(os.path.join(gitrepo, 'sources'), 'w') as stream:
  1180. stream.write('foo\n bar\nbaz\n boose')
  1181. repo.index.add('sources')
  1182. repo.index.write()
  1183. # Commits the files added
  1184. tree = repo.index.write_tree()
  1185. author = pygit2.Signature(
  1186. 'Alice Author', 'alice@authors.tld')
  1187. committer = pygit2.Signature(
  1188. 'Cecil Committer', 'cecil@committers.tld')
  1189. repo.create_commit(
  1190. 'refs/heads/feature', # the name of the reference to update
  1191. author,
  1192. committer,
  1193. 'Add baz and boose to the sources\n\n There are more objects to '
  1194. 'consider',
  1195. # binary string representing the tree object ID
  1196. tree,
  1197. # list of binary strings representing parents of the new commit
  1198. [first_commit.oid.hex]
  1199. )
  1200. refname = 'refs/heads/feature:refs/heads/feature'
  1201. ori_remote = repo.remotes[0]
  1202. PagureRepo.push(ori_remote, refname)
  1203. # Create another file in the master branch
  1204. with open(os.path.join(gitrepo, '.gitignore'), 'w') as stream:
  1205. stream.write('*~')
  1206. repo.index.add('.gitignore')
  1207. repo.index.write()
  1208. # Commits the files added
  1209. tree = repo.index.write_tree()
  1210. author = pygit2.Signature(
  1211. 'Alice Author', 'alice@authors.tld')
  1212. committer = pygit2.Signature(
  1213. 'Cecil Committer', 'cecil@committers.tld')
  1214. repo.create_commit(
  1215. 'refs/heads/master', # the name of the reference to update
  1216. author,
  1217. committer,
  1218. 'Add .gitignore file for testing',
  1219. # binary string representing the tree object ID
  1220. tree,
  1221. # list of binary strings representing parents of the new commit
  1222. [first_commit.oid.hex]
  1223. )
  1224. refname = 'refs/heads/master:refs/heads/master'
  1225. ori_remote = repo.remotes[0]
  1226. PagureRepo.push(ori_remote, refname)
  1227. # Create a PR for these changes
  1228. tests.create_projects(self.session)
  1229. project = pagure.lib.query.get_authorized_project(self.session, 'test')
  1230. req = pagure.lib.query.new_pull_request(
  1231. session=self.session,
  1232. repo_from=project,
  1233. branch_from='feature',
  1234. repo_to=project,
  1235. branch_to='master',
  1236. title='PR from the feature branch',
  1237. user='pingou',
  1238. )
  1239. self.session.commit()
  1240. self.assertEqual(req.id, 1)
  1241. self.assertEqual(req.title, 'PR from the feature branch')
  1242. # Check if the PR can be merged
  1243. data = {}
  1244. user = tests.FakeUser()
  1245. user.username = 'pingou'
  1246. with tests.user_set(self.app.application, user):
  1247. output = self.app.get('/test/adduser')
  1248. csrf_token = self.get_csrf(output=output)
  1249. # With all the desired information
  1250. project = pagure.lib.query.get_authorized_project(self.session, 'test')
  1251. data = {
  1252. 'csrf_token': csrf_token,
  1253. 'requestid': project.requests[0].uid,
  1254. 'force': True,
  1255. }
  1256. output = self.app.post('/pv/pull-request/merge', data=data)
  1257. self.assertEqual(output.status_code, 500)
  1258. exp = {u'code': u'CONFLICTS', u'message': u'error'}
  1259. js_data = json.loads(output.get_data(as_text=True))
  1260. self.assertDictEqual(js_data, exp)
  1261. @patch('pagure.lib.notify.send_email', MagicMock(return_value=True))
  1262. @patch(
  1263. 'pagure.lib.git.merge_pull_request',
  1264. MagicMock(side_effect=pygit2.GitError('git error')))
  1265. def test_mergeable_request_pull_merge_giterror(self):
  1266. """ Test the mergeable_request_pull endpoint when the backend
  1267. raises an GitError exception.
  1268. """
  1269. # Create a git repo to play with
  1270. origgitrepo = os.path.join(self.path, 'repos', 'test.git')
  1271. self.assertFalse(os.path.exists(origgitrepo))
  1272. os.makedirs(origgitrepo)
  1273. orig_repo = pygit2.init_repository(origgitrepo, bare=True)
  1274. os.makedirs(os.path.join(self.path, 'repos_tmp'))
  1275. gitrepo = os.path.join(self.path, 'repos_tmp', 'test.git')
  1276. repo = pygit2.clone_repository(origgitrepo, gitrepo)
  1277. # Create a file in that git repo
  1278. with open(os.path.join(gitrepo, 'sources'), 'w') as stream:
  1279. stream.write('foo\n bar')
  1280. repo.index.add('sources')
  1281. repo.index.write()
  1282. # Commits the files added
  1283. tree = repo.index.write_tree()
  1284. author = pygit2.Signature(
  1285. 'Alice Author', 'alice@authors.tld')
  1286. committer = pygit2.Signature(
  1287. 'Cecil Committer', 'cecil@committers.tld')
  1288. repo.create_commit(
  1289. 'refs/heads/master', # the name of the reference to update
  1290. author,
  1291. committer,
  1292. 'Add sources file for testing',
  1293. # binary string representing the tree object ID
  1294. tree,
  1295. # list of binary strings representing parents of the new commit
  1296. []
  1297. )
  1298. first_commit = repo.revparse_single('HEAD')
  1299. refname = 'refs/heads/master:refs/heads/master'
  1300. ori_remote = repo.remotes[0]
  1301. PagureRepo.push(ori_remote, refname)
  1302. # Edit the sources file again
  1303. with open(os.path.join(gitrepo, 'sources'), 'w') as stream:
  1304. stream.write('foo\n bar\nbaz\n boose')
  1305. repo.index.add('sources')
  1306. repo.index.write()
  1307. # Commits the files added
  1308. tree = repo.index.write_tree()
  1309. author = pygit2.Signature(
  1310. 'Alice Author', 'alice@authors.tld')
  1311. committer = pygit2.Signature(
  1312. 'Cecil Committer', 'cecil@committers.tld')
  1313. repo.create_commit(
  1314. 'refs/heads/feature', # the name of the reference to update
  1315. author,
  1316. committer,
  1317. 'Add baz and boose to the sources\n\n There are more objects to '
  1318. 'consider',
  1319. # binary string representing the tree object ID
  1320. tree,
  1321. # list of binary strings representing parents of the new commit
  1322. [first_commit.oid.hex]
  1323. )
  1324. refname = 'refs/heads/feature:refs/heads/feature'
  1325. ori_remote = repo.remotes[0]
  1326. PagureRepo.push(ori_remote, refname)
  1327. # Create another file in the master branch
  1328. with open(os.path.join(gitrepo, '.gitignore'), 'w') as stream:
  1329. stream.write('*~')
  1330. repo.index.add('.gitignore')
  1331. repo.index.write()
  1332. # Commits the files added
  1333. tree = repo.index.write_tree()
  1334. author = pygit2.Signature(
  1335. 'Alice Author', 'alice@authors.tld')
  1336. committer = pygit2.Signature(
  1337. 'Cecil Committer', 'cecil@committers.tld')
  1338. repo.create_commit(
  1339. 'refs/heads/master', # the name of the reference to update
  1340. author,
  1341. committer,
  1342. 'Add .gitignore file for testing',
  1343. # binary string representing the tree object ID
  1344. tree,
  1345. # list of binary strings representing parents of the new commit
  1346. [first_commit.oid.hex]
  1347. )
  1348. refname = 'refs/heads/master:refs/heads/master'
  1349. ori_remote = repo.remotes[0]
  1350. PagureRepo.push(ori_remote, refname)
  1351. # Create a PR for these changes
  1352. tests.create_projects(self.session)
  1353. project = pagure.lib.query.get_authorized_project(self.session, 'test')
  1354. req = pagure.lib.query.new_pull_request(
  1355. session=self.session,
  1356. repo_from=project,
  1357. branch_from='feature',
  1358. repo_to=project,
  1359. branch_to='master',
  1360. title='PR from the feature branch',
  1361. user='pingou',
  1362. )
  1363. self.session.commit()
  1364. self.assertEqual(req.id, 1)
  1365. self.assertEqual(req.title, 'PR from the feature branch')
  1366. # Check if the PR can be merged
  1367. data = {}
  1368. user = tests.FakeUser()
  1369. user.username = 'pingou'
  1370. with tests.user_set(self.app.application, user):
  1371. output = self.app.get('/test/adduser')
  1372. csrf_token = self.get_csrf(output=output)
  1373. # With all the desired information
  1374. project = pagure.lib.query.get_authorized_project(self.session, 'test')
  1375. data = {
  1376. 'csrf_token': csrf_token,
  1377. 'requestid': project.requests[0].uid,
  1378. 'force': True,
  1379. }
  1380. output = self.app.post('/pv/pull-request/merge', data=data)
  1381. self.assertEqual(output.status_code, 409)
  1382. exp = {u'code': u'CONFLICTS', u'message': u'git error'}
  1383. js_data = json.loads(output.get_data(as_text=True))
  1384. self.assertDictEqual(js_data, exp)
  1385. def test_get_branches_of_commit(self):
  1386. ''' Test the get_branches_of_commit from the internal API. '''
  1387. tests.create_projects(self.session)
  1388. tests.create_projects_git(os.path.join(self.path, 'repos'))
  1389. user = tests.FakeUser()
  1390. user.username = 'pingou'
  1391. with tests.user_set(self.app.application, user):
  1392. output = self.app.get('/test/adduser')
  1393. self.assertEqual(output.status_code, 200)
  1394. csrf_token = output.get_data(as_text=True).split(
  1395. 'name="csrf_token" type="hidden" value="')[1].split('">')[0]
  1396. # No CSRF token
  1397. data = {
  1398. 'repo': 'fakerepo',
  1399. 'commit_id': 'foo',
  1400. }
  1401. output = self.app.post('/pv/branches/commit/', data=data)
  1402. self.assertEqual(output.status_code, 400)
  1403. js_data = json.loads(output.get_data(as_text=True))
  1404. self.assertDictEqual(
  1405. js_data,
  1406. {'code': 'ERROR', 'message': 'Invalid input submitted'}
  1407. )
  1408. # Invalid repo
  1409. data = {
  1410. 'repo': 'fakerepo',
  1411. 'commit_id': 'foo',
  1412. 'csrf_token': csrf_token,
  1413. }
  1414. output = self.app.post('/pv/branches/commit/', data=data)
  1415. self.assertEqual(output.status_code, 404)
  1416. js_data = json.loads(output.get_data(as_text=True))
  1417. self.assertDictEqual(
  1418. js_data,
  1419. {
  1420. 'code': 'ERROR',
  1421. 'message': 'No repo found with the information provided'
  1422. }
  1423. )
  1424. # Rigth repo, no commit
  1425. data = {
  1426. 'repo': 'test',
  1427. 'csrf_token': csrf_token,
  1428. }
  1429. output = self.app.post('/pv/branches/commit/', data=data)
  1430. self.assertEqual(output.status_code, 400)
  1431. js_data = json.loads(output.get_data(as_text=True))
  1432. self.assertDictEqual(
  1433. js_data,
  1434. {'code': 'ERROR', 'message': 'No commit id submitted'}
  1435. )
  1436. # Request is fine, but git repo doesn't exist
  1437. item = pagure.lib.model.Project(
  1438. user_id=1, # pingou
  1439. name='test20',
  1440. description='test project #20',
  1441. hook_token='aaabbbhhh',
  1442. )
  1443. self.session.add(item)
  1444. self.session.commit()
  1445. data = {
  1446. 'repo': 'test20',
  1447. 'commit_id': 'foo',
  1448. 'csrf_token': csrf_token,
  1449. }
  1450. output = self.app.post('/pv/branches/commit/', data=data)
  1451. self.assertEqual(output.status_code, 404)
  1452. js_data = json.loads(output.get_data(as_text=True))
  1453. self.assertDictEqual(
  1454. js_data,
  1455. {
  1456. 'code': 'ERROR',
  1457. 'message': 'No git repo found with the information provided'
  1458. }
  1459. )
  1460. # Create a git repo to play with
  1461. gitrepo = os.path.join(self.path, 'repos', 'test.git')
  1462. self.assertTrue(os.path.exists(gitrepo))
  1463. repo = pygit2.Repository(gitrepo)
  1464. # Create a file in that git repo
  1465. with open(os.path.join(gitrepo, 'sources'), 'w') as stream:
  1466. stream.write('foo\n bar')
  1467. repo.index.add('sources')
  1468. repo.index.write()
  1469. # Commits the files added
  1470. tree = repo.index.write_tree()
  1471. author = pygit2.Signature(
  1472. 'Alice Author', 'alice@authors.tld')
  1473. committer = pygit2.Signature(
  1474. 'Cecil Committer', 'cecil@committers.tld')
  1475. repo.create_commit(
  1476. 'refs/heads/master', # the name of the reference to update
  1477. author,
  1478. committer,
  1479. 'Add sources file for testing',
  1480. # binary string representing the tree object ID
  1481. tree,
  1482. # list of binary strings representing parents of the new commit
  1483. []
  1484. )
  1485. first_commit = repo.revparse_single('HEAD')
  1486. # Edit the sources file again
  1487. with open(os.path.join(gitrepo, 'sources'), 'w') as stream:
  1488. stream.write('foo\n bar\nbaz\n boose')
  1489. repo.index.add('sources')
  1490. repo.index.write()
  1491. # Commits the files added
  1492. tree = repo.index.write_tree()
  1493. author = pygit2.Signature(
  1494. 'Alice Author', 'alice@authors.tld')
  1495. committer = pygit2.Signature(
  1496. 'Cecil Committer', 'cecil@committers.tld')
  1497. repo.create_commit(
  1498. 'refs/heads/feature', # the name of the reference to update
  1499. author,
  1500. committer,
  1501. 'Add baz and boose to the sources\n\n There are more objects to '
  1502. 'consider',
  1503. # binary string representing the tree object ID
  1504. tree,
  1505. # list of binary strings representing parents of the new commit
  1506. [first_commit.oid.hex]
  1507. )
  1508. # Create another file in the master branch
  1509. with open(os.path.join(gitrepo, '.gitignore'), 'w') as stream:
  1510. stream.write('*~')
  1511. repo.index.add('.gitignore')
  1512. repo.index.write()
  1513. # Commits the files added
  1514. tree = repo.index.write_tree()
  1515. author = pygit2.Signature(
  1516. 'Alice Author', 'alice@authors.tld')
  1517. committer = pygit2.Signature(
  1518. 'Cecil Committer', 'cecil@committers.tld')
  1519. commit_hash = repo.create_commit(
  1520. 'refs/heads/feature_branch', # the name of the reference to update
  1521. author,
  1522. committer,
  1523. 'Add .gitignore file for testing',
  1524. # binary string representing the tree object ID
  1525. tree,
  1526. # list of binary strings representing parents of the new commit
  1527. [first_commit.oid.hex]
  1528. )
  1529. # All good but the commit id
  1530. data = {
  1531. 'repo': 'test',
  1532. 'commit_id': 'foo',
  1533. 'csrf_token': csrf_token,
  1534. }
  1535. output = self.app.post('/pv/branches/commit/', data=data)
  1536. self.assertEqual(output.status_code, 404)
  1537. js_data = json.loads(output.get_data(as_text=True))
  1538. self.assertDictEqual(
  1539. js_data,
  1540. {
  1541. 'code': 'ERROR',
  1542. 'message': 'This commit could not be found in this repo'
  1543. }
  1544. )
  1545. # All good
  1546. data = {
  1547. 'repo': 'test',
  1548. 'commit_id': commit_hash,
  1549. 'csrf_token': csrf_token,
  1550. }
  1551. output = self.app.post('/pv/branches/commit/', data=data)
  1552. self.assertEqual(output.status_code, 200)
  1553. js_data = json.loads(output.get_data(as_text=True))
  1554. self.assertDictEqual(
  1555. js_data,
  1556. {
  1557. 'code': 'OK',
  1558. 'branches': ['feature_branch'],
  1559. }
  1560. )
  1561. def test_get_branches_of_commit_with_unrelated_branches(self):
  1562. ''' Test the get_branches_of_commit from the internal API. '''
  1563. tests.create_projects(self.session)
  1564. tests.create_projects_git(os.path.join(self.path, 'repos'))
  1565. user = tests.FakeUser(username='pingou')
  1566. with tests.user_set(self.app.application, user):
  1567. csrf_token = self.get_csrf()
  1568. # Create a git repo to play with
  1569. gitrepo = os.path.join(self.path, 'repos', 'test.git')
  1570. self.assertTrue(os.path.exists(gitrepo))
  1571. repo = pygit2.Repository(gitrepo)
  1572. # Create a file in that git repo
  1573. with open(os.path.join(gitrepo, 'sources'), 'w') as stream:
  1574. stream.write('foo\n bar')
  1575. repo.index.add('sources')
  1576. repo.index.write()
  1577. # Commits the files added
  1578. tree = repo.index.write_tree()
  1579. author = pygit2.Signature(
  1580. 'Alice Author', 'alice@authors.tld')
  1581. committer = pygit2.Signature(
  1582. 'Cecil Committer', 'cecil@committers.tld')
  1583. repo.create_commit(
  1584. 'refs/heads/master', # the name of the reference to update
  1585. author,
  1586. committer,
  1587. 'Add sources file for testing',
  1588. # binary string representing the tree object ID
  1589. tree,
  1590. # list of binary strings representing parents of the new commit
  1591. []
  1592. )
  1593. first_commit = repo.revparse_single('HEAD')
  1594. # Edit the sources file again
  1595. with open(os.path.join(gitrepo, 'sources'), 'w') as stream:
  1596. stream.write('foo\n bar\nbaz\n boose')
  1597. repo.index.add('sources')
  1598. repo.index.write()
  1599. # Commits the files added, but unrelated with the first commit
  1600. tree = repo.index.write_tree()
  1601. author = pygit2.Signature(
  1602. 'Alice Author', 'alice@authors.tld')
  1603. committer = pygit2.Signature(
  1604. 'Cecil Committer', 'cecil@committers.tld')
  1605. commit = repo.create_commit(
  1606. 'refs/heads/feature', # the name of the reference to update
  1607. author,
  1608. committer,
  1609. 'Add baz and boose to the sources\n\n There are more objects to '
  1610. 'consider',
  1611. # binary string representing the tree object ID
  1612. tree,
  1613. # list of binary strings representing parents of the new commit
  1614. []
  1615. )
  1616. commit_hash = commit.hex
  1617. # All good
  1618. data = {
  1619. 'repo': 'test',
  1620. 'commit_id': commit_hash,
  1621. 'csrf_token': csrf_token,
  1622. }
  1623. output = self.app.post('/pv/branches/commit/', data=data)
  1624. self.assertEqual(output.status_code, 200)
  1625. js_data = json.loads(output.get_data(as_text=True))
  1626. self.assertDictEqual(
  1627. js_data,
  1628. {
  1629. u'code': u'OK',
  1630. u'branches': ['feature'],
  1631. }
  1632. )
  1633. def test_get_branches_head(self):
  1634. ''' Test the get_branches_head from the internal API. '''
  1635. tests.create_projects(self.session)
  1636. tests.create_projects_git(os.path.join(self.path, 'repos'))
  1637. user = tests.FakeUser()
  1638. user.username = 'pingou'
  1639. with tests.user_set(self.app.application, user):
  1640. csrf_token = self.get_csrf()
  1641. # No CSRF token
  1642. data = {
  1643. 'repo': 'fakerepo',
  1644. }
  1645. output = self.app.post('/pv/branches/heads/', data=data)
  1646. self.assertEqual(output.status_code, 400)
  1647. js_data = json.loads(output.get_data(as_text=True))
  1648. self.assertDictEqual(
  1649. js_data,
  1650. {'code': 'ERROR', 'message': 'Invalid input submitted'}
  1651. )
  1652. # Invalid repo
  1653. data = {
  1654. 'repo': 'fakerepo',
  1655. 'commit_id': 'foo',
  1656. 'csrf_token': csrf_token,
  1657. }
  1658. output = self.app.post('/pv/branches/heads/', data=data)
  1659. self.assertEqual(output.status_code, 404)
  1660. js_data = json.loads(output.get_data(as_text=True))
  1661. self.assertDictEqual(
  1662. js_data,
  1663. {
  1664. 'code': 'ERROR',
  1665. 'message': 'No repo found with the information provided'
  1666. }
  1667. )
  1668. # Rigth repo, no commit
  1669. data = {
  1670. 'repo': 'test',
  1671. 'csrf_token': csrf_token,
  1672. }
  1673. output = self.app.post('/pv/branches/heads/', data=data)
  1674. self.assertEqual(output.status_code, 200)
  1675. js_data = json.loads(output.get_data(as_text=True))
  1676. self.assertDictEqual(
  1677. js_data,
  1678. {"branches": {}, "code": "OK", "heads": {}}
  1679. )
  1680. # Request is fine, but git repo doesn't exist
  1681. item = pagure.lib.model.Project(
  1682. user_id=1, # pingou
  1683. name='test20',
  1684. description='test project #20',
  1685. hook_token='aaabbbhhh',
  1686. )
  1687. self.session.add(item)
  1688. self.session.commit()
  1689. data = {
  1690. 'repo': 'test20',
  1691. 'csrf_token': csrf_token,
  1692. }
  1693. output = self.app.post('/pv/branches/heads/', data=data)
  1694. self.assertEqual(output.status_code, 404)
  1695. js_data = json.loads(output.get_data(as_text=True))
  1696. self.assertDictEqual(
  1697. js_data,
  1698. {
  1699. 'code': 'ERROR',
  1700. 'message': 'No git repo found with the information provided'
  1701. }
  1702. )
  1703. # Create a git repo to play with
  1704. gitrepo = os.path.join(self.path, 'repos', 'test.git')
  1705. self.assertTrue(os.path.exists(gitrepo))
  1706. repo = pygit2.Repository(gitrepo)
  1707. # Create a file in that git repo
  1708. with open(os.path.join(gitrepo, 'sources'), 'w') as stream:
  1709. stream.write('foo\n bar')
  1710. repo.index.add('sources')
  1711. repo.index.write()
  1712. # Commits the files added
  1713. tree = repo.index.write_tree()
  1714. author = pygit2.Signature(
  1715. 'Alice Author', 'alice@authors.tld')
  1716. committer = pygit2.Signature(
  1717. 'Cecil Committer', 'cecil@committers.tld')
  1718. repo.create_commit(
  1719. 'refs/heads/master', # the name of the reference to update
  1720. author,
  1721. committer,
  1722. 'Add sources file for testing',
  1723. # binary string representing the tree object ID
  1724. tree,
  1725. # list of binary strings representing parents of the new commit
  1726. []
  1727. )
  1728. first_commit = repo.revparse_single('HEAD')
  1729. # Edit the sources file again
  1730. with open(os.path.join(gitrepo, 'sources'), 'w') as stream:
  1731. stream.write('foo\n bar\nbaz\n boose')
  1732. repo.index.add('sources')
  1733. repo.index.write()
  1734. # Commits the files added
  1735. tree = repo.index.write_tree()
  1736. author = pygit2.Signature(
  1737. 'Alice Author', 'alice@authors.tld')
  1738. committer = pygit2.Signature(
  1739. 'Cecil Committer', 'cecil@committers.tld')
  1740. repo.create_commit(
  1741. 'refs/heads/feature', # the name of the reference to update
  1742. author,
  1743. committer,
  1744. 'Add baz and boose to the sources\n\n There are more objects to '
  1745. 'consider',
  1746. # binary string representing the tree object ID
  1747. tree,
  1748. # list of binary strings representing parents of the new commit
  1749. [first_commit.oid.hex]
  1750. )
  1751. # Create another file in the master branch
  1752. with open(os.path.join(gitrepo, '.gitignore'), 'w') as stream:
  1753. stream.write('*~')
  1754. repo.index.add('.gitignore')
  1755. repo.index.write()
  1756. # Commits the files added
  1757. tree = repo.index.write_tree()
  1758. author = pygit2.Signature(
  1759. 'Alice Author', 'alice@authors.tld')
  1760. committer = pygit2.Signature(
  1761. 'Cecil Committer', 'cecil@committers.tld')
  1762. commit_hash = repo.create_commit(
  1763. 'refs/heads/feature_branch', # the name of the reference to update
  1764. author,
  1765. committer,
  1766. 'Add .gitignore file for testing',
  1767. # binary string representing the tree object ID
  1768. tree,
  1769. # list of binary strings representing parents of the new commit
  1770. [first_commit.oid.hex]
  1771. )
  1772. # All good
  1773. data = {
  1774. 'repo': 'test',
  1775. 'csrf_token': csrf_token,
  1776. }
  1777. output = self.app.post('/pv/branches/heads/', data=data)
  1778. self.assertEqual(output.status_code, 200)
  1779. js_data = json.loads(output.get_data(as_text=True))
  1780. # We can't test the content since the commit hash will change all
  1781. # the time, so let's just check the structure
  1782. self.assertEqual(
  1783. sorted(js_data.keys()), ['branches', 'code', 'heads'])
  1784. self.assertEqual(js_data['code'], 'OK')
  1785. self.assertEqual(len(js_data['heads']), 3)
  1786. self.assertEqual(len(js_data['branches']), 3)
  1787. def test_get_stats_commits_no_token(self):
  1788. ''' Test the get_stats_commits from the internal API. '''
  1789. # No CSRF token
  1790. data = {
  1791. 'repo': 'fakerepo',
  1792. }
  1793. output = self.app.post('/pv/stats/commits/authors', data=data)
  1794. self.assertEqual(output.status_code, 400)
  1795. js_data = json.loads(output.get_data(as_text=True))
  1796. self.assertDictEqual(
  1797. js_data,
  1798. {'code': 'ERROR', 'message': 'Invalid input submitted'}
  1799. )
  1800. def test_get_stats_commits_invalid_repo(self):
  1801. ''' Test the get_stats_commits from the internal API. '''
  1802. user = tests.FakeUser()
  1803. user.username = 'pingou'
  1804. with tests.user_set(self.app.application, user):
  1805. csrf_token = self.get_csrf()
  1806. # Invalid repo
  1807. data = {
  1808. 'repo': 'fakerepo',
  1809. 'csrf_token': csrf_token,
  1810. }
  1811. output = self.app.post('/pv/stats/commits/authors', data=data)
  1812. self.assertEqual(output.status_code, 404)
  1813. js_data = json.loads(output.get_data(as_text=True))
  1814. self.assertDictEqual(
  1815. js_data,
  1816. {'code': 'ERROR',
  1817. 'message': 'No repo found with the information provided'}
  1818. )
  1819. def test_get_stats_commits_empty_git(self):
  1820. ''' Test the get_stats_commits from the internal API. '''
  1821. tests.create_projects(self.session)
  1822. tests.create_projects_git(os.path.join(self.path, 'repos'))
  1823. user = tests.FakeUser()
  1824. user.username = 'pingou'
  1825. with tests.user_set(self.app.application, user):
  1826. csrf_token = self.get_csrf()
  1827. # No content in git
  1828. data = {
  1829. 'repo': 'test',
  1830. 'csrf_token': csrf_token,
  1831. }
  1832. output = self.app.post('/pv/stats/commits/authors', data=data)
  1833. self.assertEqual(output.status_code, 200)
  1834. js_data = json.loads(output.get_data(as_text=True))
  1835. self.assertEqual(
  1836. sorted(js_data.keys()),
  1837. ['code', 'message', 'task_id', 'url']
  1838. )
  1839. self.assertEqual(js_data['code'], 'OK')
  1840. self.assertEqual(js_data['message'], 'Stats asked')
  1841. self.assertTrue(js_data['url'].startswith('/pv/task/'))
  1842. output = self.app.get(js_data['url'])
  1843. js_data2 = json.loads(output.get_data(as_text=True))
  1844. self.assertTrue(
  1845. js_data2 in [
  1846. {'results': "reference 'refs/heads/master' not found"},
  1847. {'results': "Reference 'refs/heads/master' not found"}
  1848. ]
  1849. )
  1850. def test_get_stats_commits_git_populated(self):
  1851. ''' Test the get_stats_commits from the internal API. '''
  1852. tests.create_projects(self.session)
  1853. tests.create_projects_git(
  1854. os.path.join(self.path, 'repos'), bare=True)
  1855. tests.add_content_git_repo(
  1856. os.path.join(self.path, 'repos', 'test.git'))
  1857. user = tests.FakeUser()
  1858. user.username = 'pingou'
  1859. with tests.user_set(self.app.application, user):
  1860. csrf_token = self.get_csrf()
  1861. # Content in git
  1862. data = {
  1863. 'repo': 'test',
  1864. 'csrf_token': csrf_token,
  1865. }
  1866. output = self.app.post('/pv/stats/commits/authors', data=data)
  1867. self.assertEqual(output.status_code, 200)
  1868. js_data = json.loads(output.get_data(as_text=True))
  1869. self.assertEqual(
  1870. sorted(js_data.keys()),
  1871. ['code', 'message', 'task_id', 'url']
  1872. )
  1873. self.assertEqual(js_data['code'], 'OK')
  1874. self.assertEqual(js_data['message'], 'Stats asked')
  1875. self.assertTrue(js_data['url'].startswith('/pv/task/'))
  1876. output = self.app.get(js_data['url'])
  1877. while output.status_code == 418:
  1878. time.sleep(0.5)
  1879. output = self.app.get(js_data['url'])
  1880. js_data2 = json.loads(output.get_data(as_text=True))
  1881. self.assertTrue(js_data2['results'][3] > 1509110062)
  1882. js_data2['results'][3] = 1509110062
  1883. self.assertTrue(
  1884. js_data2 in [
  1885. {
  1886. 'results': [
  1887. 2,
  1888. [[2, [[
  1889. 'Alice Author',
  1890. 'alice@authors.tld',
  1891. 'https://seccdn.libravatar.org/avatar/'
  1892. '96c52c78570ffc4cfefcdadf5f8e77aeebcb11e07225df11bbf2fce381cdb8bd'
  1893. '?s=32&d=retro'
  1894. ]]]],
  1895. 1,
  1896. 1509110062
  1897. ]
  1898. },
  1899. {
  1900. 'results': [
  1901. 2,
  1902. [[2, [[
  1903. 'Alice Author',
  1904. 'alice@authors.tld',
  1905. 'https://seccdn.libravatar.org/avatar/'
  1906. '96c52c78570ffc4cfefcdadf5f8e77aeebcb11e07225df11bbf2fce381cdb8bd'
  1907. '?d=retro&s=32'
  1908. ]]]],
  1909. 1,
  1910. 1509110062
  1911. ]
  1912. }
  1913. ]
  1914. )
  1915. def test_get_stats_commits_trend_no_token(self):
  1916. ''' Test the get_stats_commits_trend from the internal API. '''
  1917. # No CSRF token
  1918. data = {
  1919. 'repo': 'fakerepo',
  1920. }
  1921. output = self.app.post('/pv/stats/commits/trend', data=data)
  1922. self.assertEqual(output.status_code, 400)
  1923. js_data = json.loads(output.get_data(as_text=True))
  1924. self.assertDictEqual(
  1925. js_data,
  1926. {'code': 'ERROR', 'message': 'Invalid input submitted'}
  1927. )
  1928. def test_get_stats_commits_trend_invalid_repo(self):
  1929. """ Test the get_stats_commits_trend from the internal API. """
  1930. user = tests.FakeUser()
  1931. user.username = 'pingou'
  1932. with tests.user_set(self.app.application, user):
  1933. csrf_token = self.get_csrf()
  1934. # Invalid repo
  1935. data = {
  1936. 'repo': 'fakerepo',
  1937. 'csrf_token': csrf_token,
  1938. }
  1939. output = self.app.post('/pv/stats/commits/trend', data=data)
  1940. self.assertEqual(output.status_code, 404)
  1941. js_data = json.loads(output.get_data(as_text=True))
  1942. self.assertDictEqual(
  1943. js_data,
  1944. {'code': 'ERROR',
  1945. 'message': 'No repo found with the information provided'}
  1946. )
  1947. def test_get_stats_commits_trend_empty_git(self):
  1948. ''' Test the get_stats_commits_trend from the internal API. '''
  1949. tests.create_projects(self.session)
  1950. tests.create_projects_git(os.path.join(self.path, 'repos'))
  1951. user = tests.FakeUser()
  1952. user.username = 'pingou'
  1953. with tests.user_set(self.app.application, user):
  1954. csrf_token = self.get_csrf()
  1955. # No content in git
  1956. data = {
  1957. 'repo': 'test',
  1958. 'csrf_token': csrf_token,
  1959. }
  1960. output = self.app.post('/pv/stats/commits/trend', data=data)
  1961. self.assertEqual(output.status_code, 200)
  1962. js_data = json.loads(output.get_data(as_text=True))
  1963. self.assertEqual(
  1964. sorted(js_data.keys()),
  1965. ['code', 'message', 'task_id', 'url']
  1966. )
  1967. self.assertEqual(js_data['code'], 'OK')
  1968. self.assertEqual(js_data['message'], 'Stats asked')
  1969. self.assertTrue(js_data['url'].startswith('/pv/task/'))
  1970. output = self.app.get(js_data['url'])
  1971. js_data2 = json.loads(output.get_data(as_text=True))
  1972. self.assertTrue(
  1973. js_data2 in [
  1974. {'results': "reference 'refs/heads/master' not found"},
  1975. {'results': "Reference 'refs/heads/master' not found"}
  1976. ]
  1977. )
  1978. def test_get_stats_commits_trend_git_populated(self):
  1979. ''' Test the get_stats_commits_trend from the internal API. '''
  1980. tests.create_projects(self.session)
  1981. tests.create_projects_git(
  1982. os.path.join(self.path, 'repos'), bare=True)
  1983. tests.add_content_git_repo(
  1984. os.path.join(self.path, 'repos', 'test.git'))
  1985. user = tests.FakeUser()
  1986. user.username = 'pingou'
  1987. with tests.user_set(self.app.application, user):
  1988. csrf_token = self.get_csrf()
  1989. # Content in git
  1990. data = {
  1991. 'repo': 'test',
  1992. 'csrf_token': csrf_token,
  1993. }
  1994. output = self.app.post('/pv/stats/commits/trend', data=data)
  1995. self.assertEqual(output.status_code, 200)
  1996. js_data = json.loads(output.get_data(as_text=True))
  1997. self.assertEqual(
  1998. sorted(js_data.keys()),
  1999. ['code', 'message', 'task_id', 'url']
  2000. )
  2001. self.assertEqual(js_data['code'], 'OK')
  2002. self.assertEqual(js_data['message'], 'Stats asked')
  2003. self.assertTrue(js_data['url'].startswith('/pv/task/'))
  2004. output = self.app.get(js_data['url'])
  2005. js_data2 = json.loads(output.get_data(as_text=True))
  2006. today = datetime.datetime.utcnow().date()
  2007. self.assertDictEqual(
  2008. js_data2,
  2009. {'results': [[str(today), 2]]}
  2010. )
  2011. def test_get_project_family_no_project(self):
  2012. ''' Test the get_project_family from the internal API. '''
  2013. output = self.app.post('/pv/test/family')
  2014. self.assertEqual(output.status_code, 404)
  2015. def test_get_project_family_no_csrf(self):
  2016. ''' Test the get_project_family from the internal API. '''
  2017. tests.create_projects(self.session)
  2018. tests.create_projects_git(
  2019. os.path.join(self.path, 'repos'), bare=True)
  2020. tests.add_content_git_repo(
  2021. os.path.join(self.path, 'repos', 'test.git'))
  2022. output = self.app.post('/pv/test/family')
  2023. self.assertEqual(output.status_code, 400)
  2024. js_data = json.loads(output.get_data(as_text=True))
  2025. self.assertEqual(
  2026. sorted(js_data.keys()),
  2027. ['code', 'message']
  2028. )
  2029. self.assertEqual(js_data['code'], 'ERROR')
  2030. self.assertEqual(js_data['message'], 'Invalid input submitted')
  2031. def test_get_project_family(self):
  2032. ''' Test the get_project_family from the internal API. '''
  2033. tests.create_projects(self.session)
  2034. tests.create_projects_git(
  2035. os.path.join(self.path, 'repos'), bare=True)
  2036. tests.add_content_git_repo(
  2037. os.path.join(self.path, 'repos', 'test.git'))
  2038. user = tests.FakeUser()
  2039. user.username = 'pingou'
  2040. with tests.user_set(self.app.application, user):
  2041. csrf_token = self.get_csrf()
  2042. data = {
  2043. 'csrf_token': csrf_token,
  2044. }
  2045. output = self.app.post('/pv/test/family', data=data)
  2046. self.assertEqual(output.status_code, 200)
  2047. js_data = json.loads(output.get_data(as_text=True))
  2048. self.assertEqual(
  2049. sorted(js_data.keys()),
  2050. ['code', 'family']
  2051. )
  2052. self.assertEqual(js_data['code'], 'OK')
  2053. self.assertEqual(js_data['family'], ['test'])
  2054. def test_get_project_larger_family(self):
  2055. ''' Test the get_project_family from the internal API. '''
  2056. tests.create_projects(self.session)
  2057. tests.create_projects_git(
  2058. os.path.join(self.path, 'repos'), bare=True)
  2059. # Create a 3rd user
  2060. item = pagure.lib.model.User(
  2061. user='ralph',
  2062. fullname='Ralph bar',
  2063. password='ralph_foo',
  2064. default_email='ralph@bar.com',
  2065. )
  2066. self.session.add(item)
  2067. item = pagure.lib.model.UserEmail(
  2068. user_id=3,
  2069. email='ralph@bar.com')
  2070. self.session.add(item)
  2071. self.session.commit()
  2072. # Create a couple of forks of the test project
  2073. item = pagure.lib.model.Project(
  2074. user_id=2, # foo
  2075. name='test',
  2076. is_fork=True,
  2077. parent_id=1, # test
  2078. description='test project #1',
  2079. hook_token='aaabbbcccddd',
  2080. )
  2081. item.close_status = ['Invalid', 'Insufficient data', 'Fixed', 'Duplicate']
  2082. self.session.add(item)
  2083. item = pagure.lib.model.Project(
  2084. user_id=3, # Ralph
  2085. name='test',
  2086. is_fork=True,
  2087. parent_id=1, # test
  2088. description='test project #1',
  2089. hook_token='aaabbbccceee',
  2090. )
  2091. item.close_status = ['Invalid', 'Insufficient data', 'Fixed', 'Duplicate']
  2092. self.session.add(item)
  2093. self.session.commit()
  2094. # Get on with testing
  2095. user = tests.FakeUser()
  2096. user.username = 'pingou'
  2097. with tests.user_set(self.app.application, user):
  2098. csrf_token = self.get_csrf()
  2099. data = {
  2100. 'csrf_token': csrf_token,
  2101. }
  2102. output = self.app.post('/pv/test/family', data=data)
  2103. self.assertEqual(output.status_code, 200)
  2104. js_data = json.loads(output.get_data(as_text=True))
  2105. self.assertEqual(
  2106. sorted(js_data.keys()),
  2107. ['code', 'family']
  2108. )
  2109. self.assertEqual(js_data['code'], 'OK')
  2110. self.assertEqual(
  2111. js_data['family'],
  2112. ['test', 'fork/foo/test', 'fork/ralph/test'])
  2113. def test_get_project_larger_family_pr_only(self):
  2114. ''' Test the get_project_family from the internal API. '''
  2115. tests.create_projects(self.session)
  2116. tests.create_projects_git(
  2117. os.path.join(self.path, 'repos'), bare=True)
  2118. # Create a 3rd user
  2119. item = pagure.lib.model.User(
  2120. user='ralph',
  2121. fullname='Ralph bar',
  2122. password='ralph_foo',
  2123. default_email='ralph@bar.com',
  2124. )
  2125. self.session.add(item)
  2126. item = pagure.lib.model.UserEmail(
  2127. user_id=3,
  2128. email='ralph@bar.com')
  2129. self.session.add(item)
  2130. self.session.commit()
  2131. # Create a couple of forks of the test project
  2132. item = pagure.lib.model.Project(
  2133. user_id=2, # foo
  2134. name='test',
  2135. is_fork=True,
  2136. parent_id=1, # test
  2137. description='test project #1',
  2138. hook_token='aaabbbcccddd',
  2139. )
  2140. item.close_status = ['Invalid', 'Insufficient data', 'Fixed', 'Duplicate']
  2141. # disable issues in this fork
  2142. default_repo_settings = item.settings
  2143. default_repo_settings['issue_tracker'] = False
  2144. item.settings = default_repo_settings
  2145. self.session.add(item)
  2146. item = pagure.lib.model.Project(
  2147. user_id=3, # Ralph
  2148. name='test',
  2149. is_fork=True,
  2150. parent_id=1, # test
  2151. description='test project #1',
  2152. hook_token='aaabbbccceee',
  2153. )
  2154. item.close_status = ['Invalid', 'Insufficient data', 'Fixed', 'Duplicate']
  2155. # disable PRs in this fork
  2156. default_repo_settings = item.settings
  2157. default_repo_settings['pull_requests'] = False
  2158. item.settings = default_repo_settings
  2159. self.session.add(item)
  2160. self.session.commit()
  2161. # Get on with testing
  2162. user = tests.FakeUser()
  2163. user.username = 'pingou'
  2164. with tests.user_set(self.app.application, user):
  2165. csrf_token = self.get_csrf()
  2166. data = {
  2167. 'csrf_token': csrf_token,
  2168. 'allows_pr': '1',
  2169. }
  2170. output = self.app.post('/pv/test/family', data=data)
  2171. self.assertEqual(output.status_code, 200)
  2172. js_data = json.loads(output.get_data(as_text=True))
  2173. self.assertEqual(
  2174. sorted(js_data.keys()),
  2175. ['code', 'family']
  2176. )
  2177. self.assertEqual(js_data['code'], 'OK')
  2178. self.assertEqual(
  2179. js_data['family'],
  2180. ['test', 'fork/foo/test'])
  2181. data = {
  2182. 'csrf_token': csrf_token,
  2183. 'allows_issues': '1',
  2184. }
  2185. output = self.app.post('/pv/test/family', data=data)
  2186. self.assertEqual(output.status_code, 200)
  2187. js_data = json.loads(output.get_data(as_text=True))
  2188. self.assertEqual(
  2189. sorted(js_data.keys()),
  2190. ['code', 'family']
  2191. )
  2192. self.assertEqual(js_data['code'], 'OK')
  2193. self.assertEqual(
  2194. js_data['family'],
  2195. ['test', 'fork/ralph/test'])
  2196. def test_get_pull_request_ready_branch_no_csrf(self):
  2197. '''Test the get_pull_request_ready_branch from the internal API
  2198. on the main repository
  2199. '''
  2200. tests.create_projects(self.session)
  2201. tests.create_projects_git(
  2202. os.path.join(self.path, 'repos'), bare=True)
  2203. # Query branches on the main repo
  2204. data = {
  2205. 'repo': 'test',
  2206. }
  2207. output = self.app.post('/pv/pull-request/ready', data=data)
  2208. self.assertEqual(output.status_code, 400)
  2209. js_data = json.loads(output.get_data(as_text=True))
  2210. self.assertEqual(
  2211. sorted(js_data.keys()),
  2212. ['code', 'message']
  2213. )
  2214. self.assertEqual(js_data['code'], 'ERROR')
  2215. self.assertEqual(
  2216. js_data['message'],
  2217. 'Invalid input submitted'
  2218. )
  2219. def test_get_pull_request_ready_branch_no_repo(self):
  2220. '''Test the get_pull_request_ready_branch from the internal API
  2221. on the main repository
  2222. '''
  2223. with tests.user_set(self.app.application, tests.FakeUser()):
  2224. csrf_token = self.get_csrf()
  2225. # Query branches on an invalid repo
  2226. data = {
  2227. 'repo': 'test',
  2228. 'namespace': 'fake',
  2229. 'csrf_token': csrf_token,
  2230. }
  2231. output = self.app.post('/pv/pull-request/ready', data=data)
  2232. self.assertEqual(output.status_code, 404)
  2233. js_data = json.loads(output.get_data(as_text=True))
  2234. self.assertEqual(
  2235. sorted(js_data.keys()),
  2236. ['code', 'message']
  2237. )
  2238. self.assertEqual(js_data['code'], 'ERROR')
  2239. self.assertEqual(
  2240. js_data['message'],
  2241. 'No repo found with the information provided'
  2242. )
  2243. def test_get_pull_request_ready_branch_main_repo_no_branch(self):
  2244. '''Test the get_pull_request_ready_branch from the internal API
  2245. on the main repository
  2246. '''
  2247. tests.create_projects(self.session)
  2248. tests.create_projects_git(
  2249. os.path.join(self.path, 'repos'), bare=True)
  2250. # Get on with testing
  2251. user = tests.FakeUser()
  2252. user.username = 'pingou'
  2253. with tests.user_set(self.app.application, user):
  2254. csrf_token = self.get_csrf()
  2255. # Query branches on the main repo
  2256. data = {
  2257. 'csrf_token': csrf_token,
  2258. 'repo': 'test',
  2259. }
  2260. output = self.app.post('/pv/pull-request/ready', data=data)
  2261. self.assertEqual(output.status_code, 200)
  2262. js_data = json.loads(output.get_data(as_text=True))
  2263. self.assertEqual(
  2264. sorted(js_data.keys()),
  2265. ['code', 'task']
  2266. )
  2267. self.assertEqual(js_data['code'], 'OK')
  2268. def test_get_pull_request_ready_branch_on_fork(self):
  2269. '''Test the get_pull_request_ready_branch from the internal API on
  2270. a fork
  2271. '''
  2272. tests.create_projects(self.session)
  2273. tests.create_projects_git(
  2274. os.path.join(self.path, 'repos'), bare=True)
  2275. tests.create_projects_git(
  2276. os.path.join(self.path, 'repos', 'forks', 'foo'), bare=True)
  2277. tests.add_content_git_repo(
  2278. os.path.join(self.path, 'repos', 'forks', 'foo', 'test.git'),
  2279. branch='feature')
  2280. # Create foo's fork of the test project
  2281. item = pagure.lib.model.Project(
  2282. user_id=2, # foo
  2283. name='test',
  2284. is_fork=True,
  2285. parent_id=1, # test
  2286. description='test project #1',
  2287. hook_token='aaabbbcccddd',
  2288. )
  2289. item.close_status = ['Invalid', 'Insufficient data', 'Fixed', 'Duplicate']
  2290. self.session.add(item)
  2291. self.session.commit()
  2292. # Get on with testing
  2293. user = tests.FakeUser()
  2294. user.username = 'pingou'
  2295. with tests.user_set(self.app.application, user):
  2296. csrf_token = self.get_csrf()
  2297. # Query branches on the Ralph's fork
  2298. data = {
  2299. 'csrf_token': csrf_token,
  2300. 'repo': 'test',
  2301. 'repouser': 'foo',
  2302. }
  2303. output = self.app.post('/pv/pull-request/ready', data=data)
  2304. self.assertEqual(output.status_code, 200)
  2305. js_data = json.loads(output.get_data(as_text=True))
  2306. self.assertEqual(
  2307. sorted(js_data.keys()),
  2308. ['code', 'task']
  2309. )
  2310. self.assertEqual(js_data['code'], 'OK')
  2311. output = self.app.get('/pv/task/' + js_data['task'])
  2312. self.assertEqual(output.status_code, 200)
  2313. js_data = json.loads(output.get_data(as_text=True))
  2314. self.assertEqual(
  2315. js_data,
  2316. {'results': {
  2317. 'branch_w_pr': {},
  2318. 'new_branch': {'feature':
  2319. {'commits': 2, 'target_branch': 'master'}}}}
  2320. )
  2321. def test_get_pull_request_ready_branch_on_fork_no_parent_no_pr(self):
  2322. '''Test the get_pull_request_ready_branch from the internal API on
  2323. a fork that has no parent repo (deleted) and doesn't allow PR
  2324. '''
  2325. tests.create_projects(self.session)
  2326. tests.create_projects_git(
  2327. os.path.join(self.path, 'repos'), bare=True)
  2328. tests.create_projects_git(
  2329. os.path.join(self.path, 'repos', 'forks', 'foo'), bare=True)
  2330. tests.add_content_git_repo(
  2331. os.path.join(self.path, 'repos', 'forks', 'foo', 'test.git'),
  2332. branch='feature')
  2333. # Create foo's fork of the test project
  2334. item = pagure.lib.model.Project(
  2335. user_id=2, # foo
  2336. name='test',
  2337. is_fork=True,
  2338. parent_id=1, # test
  2339. description='test project #1',
  2340. hook_token='aaabbbcccddd',
  2341. )
  2342. item.close_status = ['Invalid', 'Insufficient data', 'Fixed', 'Duplicate']
  2343. self.session.add(item)
  2344. self.session.commit()
  2345. settings = item.settings
  2346. settings['pull_requests'] = False
  2347. item.settings = settings
  2348. self.session.add(item)
  2349. self.session.commit()
  2350. # Delete the parent project
  2351. project = pagure.lib.query.get_authorized_project(self.session, 'test')
  2352. self.session.delete(project)
  2353. self.session.commit()
  2354. # Get on with testing
  2355. user = tests.FakeUser()
  2356. user.username = 'pingou'
  2357. with tests.user_set(self.app.application, user):
  2358. csrf_token = self.get_csrf()
  2359. # Query branches on the Ralph's fork
  2360. data = {
  2361. 'csrf_token': csrf_token,
  2362. 'repo': 'test',
  2363. 'repouser': 'foo',
  2364. }
  2365. output = self.app.post('/pv/pull-request/ready', data=data)
  2366. self.assertEqual(output.status_code, 400)
  2367. js_data = json.loads(output.get_data(as_text=True))
  2368. self.assertEqual(
  2369. sorted(js_data.keys()),
  2370. ['code', 'message']
  2371. )
  2372. self.assertEqual(js_data['code'], 'ERROR')
  2373. self.assertEqual(
  2374. js_data['message'],
  2375. 'Pull-request have been disabled for this repo')
  2376. def test_get_pull_request_ready_branch_on_fork_no_parent(self):
  2377. '''Test the get_pull_request_ready_branch from the internal API on
  2378. a fork that has no parent repo (deleted).
  2379. '''
  2380. tests.create_projects(self.session)
  2381. tests.create_projects_git(
  2382. os.path.join(self.path, 'repos'), bare=True)
  2383. tests.create_projects_git(
  2384. os.path.join(self.path, 'repos', 'forks', 'foo'), bare=True)
  2385. tests.add_content_git_repo(
  2386. os.path.join(self.path, 'repos', 'forks', 'foo', 'test.git'),
  2387. branch='feature')
  2388. # Create foo's fork of the test project
  2389. item = pagure.lib.model.Project(
  2390. user_id=2, # foo
  2391. name='test',
  2392. is_fork=True,
  2393. parent_id=1, # test
  2394. description='test project #1',
  2395. hook_token='aaabbbcccddd',
  2396. )
  2397. item.close_status = ['Invalid', 'Insufficient data', 'Fixed', 'Duplicate']
  2398. self.session.add(item)
  2399. self.session.commit()
  2400. settings = item.settings
  2401. settings['pull_requests'] = True
  2402. item.settings = settings
  2403. self.session.add(item)
  2404. self.session.commit()
  2405. # Delete the parent project
  2406. project = pagure.lib.query.get_authorized_project(self.session, 'test')
  2407. self.session.delete(project)
  2408. self.session.commit()
  2409. # Get on with testing
  2410. user = tests.FakeUser()
  2411. user.username = 'pingou'
  2412. with tests.user_set(self.app.application, user):
  2413. csrf_token = self.get_csrf()
  2414. # Query branches on the Ralph's fork
  2415. data = {
  2416. 'csrf_token': csrf_token,
  2417. 'repo': 'test',
  2418. 'repouser': 'foo',
  2419. }
  2420. output = self.app.post('/pv/pull-request/ready', data=data)
  2421. self.assertEqual(output.status_code, 200)
  2422. js_data = json.loads(output.get_data(as_text=True))
  2423. self.assertEqual(
  2424. sorted(js_data.keys()),
  2425. ['code', 'task']
  2426. )
  2427. self.assertEqual(js_data['code'], 'OK')
  2428. output = self.app.get('/pv/task/' + js_data['task'])
  2429. self.assertEqual(output.status_code, 200)
  2430. js_data = json.loads(output.get_data(as_text=True))
  2431. self.assertEqual(
  2432. js_data,
  2433. {'results': {
  2434. 'branch_w_pr': {},
  2435. 'new_branch': {'feature':
  2436. {'commits': 2, 'target_branch': 'master'}}}}
  2437. )
  2438. def test_get_pull_request_ready_branch_matching_target_off(self):
  2439. '''Test the get_pull_request_ready_branch from the internal API on
  2440. a fork while PR_TARGET_MATCHING_BRANCH is False
  2441. '''
  2442. tests.create_projects(self.session)
  2443. # make sure that head is not unborn
  2444. tests.add_content_git_repo(
  2445. os.path.join(self.path, 'repos', 'test.git'),
  2446. branch='master')
  2447. tests.add_content_git_repo(
  2448. os.path.join(self.path, 'repos', 'test.git'),
  2449. branch='feature')
  2450. tests.create_projects_git(
  2451. os.path.join(self.path, 'repos'), bare=True)
  2452. tests.create_projects_git(
  2453. os.path.join(self.path, 'repos', 'forks', 'foo'), bare=True)
  2454. tests.add_content_git_repo(
  2455. os.path.join(self.path, 'repos', 'forks', 'foo', 'test.git'),
  2456. branch='feature')
  2457. # Create foo's fork of the test project
  2458. item = pagure.lib.model.Project(
  2459. user_id=2, # foo
  2460. name='test',
  2461. is_fork=True,
  2462. parent_id=1, # test
  2463. description='test project #1',
  2464. hook_token='aaabbbcccddd',
  2465. )
  2466. item.close_status = ['Invalid', 'Insufficient data', 'Fixed', 'Duplicate']
  2467. self.session.add(item)
  2468. self.session.commit()
  2469. # Get on with testing
  2470. user = tests.FakeUser()
  2471. user.username = 'pingou'
  2472. with tests.user_set(self.app.application, user):
  2473. csrf_token = self.get_csrf()
  2474. # Query branches on the Ralph's fork
  2475. data = {
  2476. 'csrf_token': csrf_token,
  2477. 'repo': 'test',
  2478. 'repouser': 'foo',
  2479. }
  2480. output = self.app.post('/pv/pull-request/ready', data=data)
  2481. self.assertEqual(output.status_code, 200)
  2482. js_data = json.loads(output.get_data(as_text=True))
  2483. self.assertEqual(
  2484. sorted(js_data.keys()),
  2485. ['code', 'task']
  2486. )
  2487. self.assertEqual(js_data['code'], 'OK')
  2488. output = self.app.get('/pv/task/' + js_data['task'])
  2489. self.assertEqual(output.status_code, 200)
  2490. js_data = json.loads(output.get_data(as_text=True))
  2491. self.assertEqual(
  2492. js_data,
  2493. {'results': {
  2494. 'branch_w_pr': {},
  2495. 'new_branch': {'feature':
  2496. {'commits': 2, 'target_branch': 'master'}}}}
  2497. )
  2498. @patch.dict('pagure.config.config', {'PR_TARGET_MATCHING_BRANCH': True})
  2499. def test_get_pull_request_ready_branch_matching_target_on(self):
  2500. '''Test the get_pull_request_ready_branch from the internal API on
  2501. a fork while PR_TARGET_MATCHING_BRANCH is True
  2502. '''
  2503. tests.create_projects(self.session)
  2504. # make sure that head is not unborn
  2505. tests.add_content_git_repo(
  2506. os.path.join(self.path, 'repos', 'test.git'),
  2507. branch='master')
  2508. tests.add_content_git_repo(
  2509. os.path.join(self.path, 'repos', 'test.git'),
  2510. branch='feature')
  2511. tests.create_projects_git(
  2512. os.path.join(self.path, 'repos'), bare=True)
  2513. tests.create_projects_git(
  2514. os.path.join(self.path, 'repos', 'forks', 'foo'), bare=True)
  2515. tests.add_content_git_repo(
  2516. os.path.join(self.path, 'repos', 'forks', 'foo', 'test.git'),
  2517. append="testing from foo's fork",
  2518. branch='feature')
  2519. # Create foo's fork of the test project
  2520. item = pagure.lib.model.Project(
  2521. user_id=2, # foo
  2522. name='test',
  2523. is_fork=True,
  2524. parent_id=1, # test
  2525. description='test project #1',
  2526. hook_token='aaabbbcccddd',
  2527. )
  2528. item.close_status = ['Invalid', 'Insufficient data', 'Fixed', 'Duplicate']
  2529. self.session.add(item)
  2530. self.session.commit()
  2531. # Get on with testing
  2532. user = tests.FakeUser()
  2533. user.username = 'pingou'
  2534. with tests.user_set(self.app.application, user):
  2535. csrf_token = self.get_csrf()
  2536. # Query branches on the Ralph's fork
  2537. data = {
  2538. 'csrf_token': csrf_token,
  2539. 'repo': 'test',
  2540. 'repouser': 'foo',
  2541. }
  2542. output = self.app.post('/pv/pull-request/ready', data=data)
  2543. self.assertEqual(output.status_code, 200)
  2544. js_data = json.loads(output.get_data(as_text=True))
  2545. self.assertEqual(
  2546. sorted(js_data.keys()),
  2547. ['code', 'task']
  2548. )
  2549. self.assertEqual(js_data['code'], 'OK')
  2550. output = self.app.get('/pv/task/' + js_data['task'])
  2551. self.assertEqual(output.status_code, 200)
  2552. js_data = json.loads(output.get_data(as_text=True))
  2553. self.assertEqual(
  2554. js_data,
  2555. {'results': {
  2556. 'branch_w_pr': {},
  2557. 'new_branch': {'feature':
  2558. {'commits': 1, 'target_branch': 'feature'}}}}
  2559. )
  2560. def test_task_info_task_running(self):
  2561. """ Test the task_info internal API endpoint when the task isn't
  2562. ready.
  2563. """
  2564. task = MagicMock()
  2565. task.get = MagicMock(return_value='FAILED')
  2566. task.ready = MagicMock(return_value=False)
  2567. with patch('pagure.lib.tasks.get_result', MagicMock(return_value=task)):
  2568. output = self.app.get('/pv/task/2')
  2569. self.assertEqual(output.status_code, 418)
  2570. def test_task_info_task_passed(self):
  2571. """ Test the task_info internal API endpoint when the task failed.
  2572. """
  2573. task = MagicMock()
  2574. task.get = MagicMock(return_value='PASSED')
  2575. with patch('pagure.lib.tasks.get_result', MagicMock(return_value=task)):
  2576. output = self.app.get('/pv/task/2')
  2577. self.assertEqual(output.status_code, 200)
  2578. js_data = json.loads(output.get_data(as_text=True))
  2579. self.assertEqual(js_data, {u'results': u'PASSED'})
  2580. def test_task_info_task_failed(self):
  2581. """ Test the task_info internal API endpoint when the task failed.
  2582. """
  2583. task = MagicMock()
  2584. task.get = MagicMock(return_value=Exception('Random error'))
  2585. with patch('pagure.lib.tasks.get_result', MagicMock(return_value=task)):
  2586. output = self.app.get('/pv/task/2')
  2587. self.assertEqual(output.status_code, 200)
  2588. js_data = json.loads(output.get_data(as_text=True))
  2589. self.assertEqual(js_data, {u'results': u'Random error'})
  2590. def test_lookup_ssh_key(self):
  2591. """ Test the mergeable_request_pull endpoint when the backend
  2592. raises an GitError exception.
  2593. """
  2594. tests.create_projects(self.session)
  2595. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  2596. project_key = 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC4zmifEL8TLLZUZnjAuVL8495DAkpAAM2eBhwHwawBm'
  2597. project_key_fp = 'SHA256:ZSUQAqpPDWi90Fs6Ow8Epc8F3qiKVfU+H5ssvo7jiI0'
  2598. pingou = pagure.lib.query.get_user(self.session, 'pingou')
  2599. user_key = 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDTsfdTcXw4rlU1aQwOTbLOXqossLwpPIk27S/G17kUz'
  2600. user_key_fp = 'SHA256:jUJHzrq2Ct6Ubf7Y9rnB6tGnbHM9dMVsveyfPojm+i0'
  2601. pagure.lib.query.add_sshkey_to_project_or_user(
  2602. self.session,
  2603. user_key,
  2604. pushaccess=True,
  2605. creator=pingou,
  2606. user=pingou,
  2607. )
  2608. pagure.lib.query.add_sshkey_to_project_or_user(
  2609. self.session,
  2610. project_key,
  2611. pushaccess=True,
  2612. creator=pingou,
  2613. project=repo,
  2614. )
  2615. self.session.commit()
  2616. url = '/pv/ssh/lookupkey/'
  2617. output = self.app.post(
  2618. url,
  2619. data={'search_key': 'asdf'},
  2620. )
  2621. self.assertEqual(output.status_code, 200)
  2622. result = json.loads(output.get_data(as_text=True))
  2623. self.assertEqual(result['found'], False)
  2624. output = self.app.post(
  2625. url,
  2626. data={'search_key': user_key_fp},
  2627. )
  2628. self.assertEqual(output.status_code, 200)
  2629. result = json.loads(output.get_data(as_text=True))
  2630. self.assertEqual(result['found'], True)
  2631. self.assertEqual(result['username'], 'pingou')
  2632. self.assertEqual(result['public_key'], user_key)
  2633. output = self.app.post(
  2634. url,
  2635. data={'search_key': user_key_fp,
  2636. 'username': 'pingou'},
  2637. )
  2638. self.assertEqual(output.status_code, 200)
  2639. result = json.loads(output.get_data(as_text=True))
  2640. self.assertEqual(result['found'], True)
  2641. self.assertEqual(result['username'], 'pingou')
  2642. self.assertEqual(result['public_key'], user_key)
  2643. output = self.app.post(
  2644. url,
  2645. data={'search_key': user_key_fp,
  2646. 'username': 'foo'},
  2647. )
  2648. self.assertEqual(output.status_code, 200)
  2649. result = json.loads(output.get_data(as_text=True))
  2650. self.assertEqual(result['found'], False)
  2651. output = self.app.post(
  2652. url,
  2653. data={'search_key': project_key_fp},
  2654. )
  2655. self.assertEqual(output.status_code, 200)
  2656. result = json.loads(output.get_data(as_text=True))
  2657. self.assertEqual(result['found'], True)
  2658. self.assertEqual(result['username'], 'deploykey_test_2')
  2659. self.assertEqual(result['public_key'], project_key)
  2660. def test_check_ssh_access(self):
  2661. """ Test the SSH access check endpoint. """
  2662. tests.create_projects(self.session)
  2663. url = '/pv/ssh/checkaccess/'
  2664. def runtest(project, username, access):
  2665. output = self.app.post(
  2666. url,
  2667. data={
  2668. 'gitdir': project,
  2669. 'username': username,
  2670. }
  2671. )
  2672. self.assertEqual(output.status_code, 200)
  2673. result = json.loads(output.get_data(as_text=True))
  2674. self.assertEqual(result['access'], access)
  2675. return result
  2676. runtest('project.git', 'pingou', False)
  2677. i1 = runtest('test.git', 'pingou', True)
  2678. i2 = runtest('test.git', 'foo', True)
  2679. i3 = runtest('tickets/test.git', 'pingou', True)
  2680. runtest('tickets/test.git', 'foo', False)
  2681. self.assertEqual(i1['reponame'], 'test.git')
  2682. self.assertEqual(i1['repotype'], 'main')
  2683. self.assertEqual(i1['region'], None)
  2684. self.assertEqual(i1['project_name'], 'test')
  2685. self.assertEqual(i1['project_user'], None)
  2686. self.assertEqual(i1['project_namespace'], None)
  2687. self.assertEqual(i1, i2)
  2688. self.assertEqual(i3['reponame'], 'tickets/test.git')
  2689. self.assertEqual(i3['repotype'], 'tickets')
  2690. self.assertEqual(i3['region'], None)
  2691. self.assertEqual(i3['project_name'], 'test')
  2692. self.assertEqual(i3['project_user'], None)
  2693. self.assertEqual(i3['project_namespace'], None)
  2694. if __name__ == '__main__':
  2695. unittest.main(verbosity=2)