test_pagure_flask_api_project.py 132 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784
  1. # -*- coding: utf-8 -*-
  2. """
  3. (c) 2015-2018 - Copyright Red Hat Inc
  4. Authors:
  5. Pierre-Yves Chibon <pingou@pingoured.fr>
  6. Karsten Hopp <karsten@redhat.com>
  7. """
  8. from __future__ import unicode_literals
  9. __requires__ = ['SQLAlchemy >= 0.8']
  10. import pkg_resources
  11. import datetime
  12. import json
  13. import unittest
  14. import shutil
  15. import sys
  16. import tempfile
  17. import os
  18. import pygit2
  19. from celery.result import EagerResult
  20. from mock import patch, Mock
  21. sys.path.insert(0, os.path.join(os.path.dirname(
  22. os.path.abspath(__file__)), '..'))
  23. import pagure.flask_app
  24. import pagure.lib
  25. import tests
  26. from pagure.lib.repo import PagureRepo
  27. class PagureFlaskApiProjecttests(tests.Modeltests):
  28. """ Tests for the flask API of pagure for issue """
  29. def setUp(self):
  30. super(PagureFlaskApiProjecttests, self).setUp()
  31. self.gga_patcher = patch(
  32. 'pagure.lib.tasks.generate_gitolite_acls.delay')
  33. self.mock_gen_acls = self.gga_patcher.start()
  34. task_result = EagerResult('abc-1234', True, "SUCCESS")
  35. self.mock_gen_acls.return_value = task_result
  36. def tearDown(self):
  37. self.gga_patcher.stop()
  38. super(PagureFlaskApiProjecttests, self).tearDown()
  39. def test_api_git_tags(self):
  40. """ Test the api_git_tags method of the flask api. """
  41. tests.create_projects(self.session)
  42. # Create a git repo to play with
  43. gitrepo = os.path.join(self.path, 'repos', 'test.git')
  44. repo = pygit2.init_repository(gitrepo, bare=True)
  45. newpath = tempfile.mkdtemp(prefix='pagure-fork-test')
  46. repopath = os.path.join(newpath, 'test')
  47. clone_repo = pygit2.clone_repository(gitrepo, repopath)
  48. # Create a file in that git repo
  49. with open(os.path.join(repopath, 'sources'), 'w') as stream:
  50. stream.write('foo\n bar')
  51. clone_repo.index.add('sources')
  52. clone_repo.index.write()
  53. # Commits the files added
  54. tree = clone_repo.index.write_tree()
  55. author = pygit2.Signature(
  56. 'Alice Author', 'alice@authors.tld')
  57. committer = pygit2.Signature(
  58. 'Cecil Committer', 'cecil@committers.tld')
  59. clone_repo.create_commit(
  60. 'refs/heads/master', # the name of the reference to update
  61. author,
  62. committer,
  63. 'Add sources file for testing',
  64. # binary string representing the tree object ID
  65. tree,
  66. # list of binary strings representing parents of the new commit
  67. []
  68. )
  69. refname = 'refs/heads/master:refs/heads/master'
  70. ori_remote = clone_repo.remotes[0]
  71. PagureRepo.push(ori_remote, refname)
  72. # Tag our first commit
  73. first_commit = repo.revparse_single('HEAD')
  74. tagger = pygit2.Signature('Alice Doe', 'adoe@example.com', 12347, 0)
  75. repo.create_tag(
  76. "0.0.1", first_commit.oid.hex, pygit2.GIT_OBJ_COMMIT, tagger,
  77. "Release 0.0.1")
  78. # Check tags
  79. output = self.app.get('/api/0/test/git/tags')
  80. self.assertEqual(output.status_code, 200)
  81. data = json.loads(output.get_data(as_text=True))
  82. self.assertDictEqual(
  83. data,
  84. {'tags': ['0.0.1'], 'total_tags': 1}
  85. )
  86. # Check tags with commits
  87. output = self.app.get('/api/0/test/git/tags?with_commits=True')
  88. self.assertEqual(output.status_code, 200)
  89. data = json.loads(output.get_data(as_text=True))
  90. data['tags']['0.0.1'] = 'bb8fa2aa199da08d6085e1c9badc3d83d188d38c'
  91. self.assertDictEqual(
  92. data,
  93. {
  94. 'tags': {'0.0.1': 'bb8fa2aa199da08d6085e1c9badc3d83d188d38c'},
  95. 'total_tags': 1}
  96. )
  97. shutil.rmtree(newpath)
  98. def test_api_git_branches(self):
  99. """ Test the api_git_branches method of the flask api. """
  100. # Create a git repo to add branches to
  101. tests.create_projects(self.session)
  102. repo_path = os.path.join(self.path, 'repos', 'test.git')
  103. tests.add_content_git_repo(repo_path)
  104. new_repo_path = tempfile.mkdtemp(prefix='pagure-api-git-branches-test')
  105. clone_repo = pygit2.clone_repository(repo_path, new_repo_path)
  106. # Create two other branches based on master
  107. for branch in ['pats-win-49', 'pats-win-51']:
  108. clone_repo.create_branch(branch, clone_repo.head.get_object())
  109. refname = 'refs/heads/{0}:refs/heads/{0}'.format(branch)
  110. PagureRepo.push(clone_repo.remotes[0], refname)
  111. # Check that the branches show up on the API
  112. output = self.app.get('/api/0/test/git/branches')
  113. # Delete the cloned git repo after the API call
  114. shutil.rmtree(new_repo_path)
  115. # Verify the API data
  116. self.assertEqual(output.status_code, 200)
  117. data = json.loads(output.get_data(as_text=True))
  118. self.assertDictEqual(
  119. data,
  120. {
  121. 'branches': ['master', 'pats-win-49', 'pats-win-51'],
  122. 'total_branches': 3
  123. }
  124. )
  125. def test_api_git_branches_empty_repo(self):
  126. """ Test the api_git_branches method of the flask api when the repo is
  127. empty.
  128. """
  129. # Create a git repo without any branches
  130. tests.create_projects(self.session)
  131. repo_base_path = os.path.join(self.path, 'repos')
  132. tests.create_projects_git(repo_base_path)
  133. # Check that no branches show up on the API
  134. output = self.app.get('/api/0/test/git/branches')
  135. self.assertEqual(output.status_code, 200)
  136. data = json.loads(output.get_data(as_text=True))
  137. self.assertDictEqual(
  138. data,
  139. {
  140. 'branches': [],
  141. 'total_branches': 0
  142. }
  143. )
  144. def test_api_git_branches_no_repo(self):
  145. """ Test the api_git_branches method of the flask api when there is no
  146. repo on a project.
  147. """
  148. tests.create_projects(self.session)
  149. output = self.app.get('/api/0/test/git/branches')
  150. self.assertEqual(output.status_code, 404)
  151. def test_api_git_urls(self):
  152. """ Test the api_project_git_urls method of the flask api.
  153. """
  154. tests.create_projects(self.session)
  155. output = self.app.get('/api/0/test/git/urls')
  156. self.assertEqual(output.status_code, 200)
  157. expected_rv = {
  158. 'urls': {
  159. 'git': 'git://localhost.localdomain/test.git',
  160. 'ssh': 'ssh://git@localhost.localdomain/test.git'
  161. },
  162. 'total_urls': 2
  163. }
  164. data = json.loads(output.get_data(as_text=True))
  165. self.assertDictEqual(data, expected_rv)
  166. def test_api_git_urls_no_project(self):
  167. """ Test the api_project_git_urls method of the flask api when there is
  168. no project.
  169. """
  170. output = self.app.get('/api/0/test1234/git/urls')
  171. self.assertEqual(output.status_code, 404)
  172. expected_rv = {
  173. 'error': 'Project not found',
  174. 'error_code': 'ENOPROJECT'
  175. }
  176. data = json.loads(output.get_data(as_text=True))
  177. self.assertDictEqual(data, expected_rv)
  178. @patch.dict('pagure.config.config', {'PRIVATE_PROJECTS': True})
  179. def test_api_git_urls_private_project(self):
  180. """ Test the api_project_git_urls method of the flask api when the
  181. project is private.
  182. """
  183. tests.create_projects(self.session)
  184. tests.create_tokens(self.session)
  185. tests.create_tokens_acl(self.session, 'aaabbbcccddd')
  186. headers = {'Authorization': 'token aaabbbcccddd'}
  187. test_project = pagure.lib._get_project(self.session, 'test')
  188. test_project.private = True
  189. self.session.add(test_project)
  190. self.session.commit()
  191. output = self.app.get('/api/0/test/git/urls', headers=headers)
  192. self.assertEqual(output.status_code, 200)
  193. expected_rv = {
  194. 'urls': {
  195. 'git': 'git://localhost.localdomain/test.git',
  196. 'ssh': 'ssh://git@localhost.localdomain/test.git'
  197. },
  198. 'total_urls': 2
  199. }
  200. data = json.loads(output.get_data(as_text=True))
  201. self.assertDictEqual(data, expected_rv)
  202. @patch.dict('pagure.config.config', {'PRIVATE_PROJECTS': True})
  203. def test_api_git_urls_private_project_no_login(self):
  204. """ Test the api_project_git_urls method of the flask api when the
  205. project is private and the user is not logged in.
  206. """
  207. tests.create_projects(self.session)
  208. test_project = pagure.lib._get_project(self.session, 'test')
  209. test_project.private = True
  210. self.session.add(test_project)
  211. self.session.commit()
  212. output = self.app.get('/api/0/test/git/urls')
  213. self.assertEqual(output.status_code, 404)
  214. expected_rv = {
  215. 'error': 'Project not found',
  216. 'error_code': 'ENOPROJECT'
  217. }
  218. data = json.loads(output.get_data(as_text=True))
  219. self.assertDictEqual(data, expected_rv)
  220. def test_api_projects_pattern(self):
  221. """ Test the api_projects method of the flask api. """
  222. tests.create_projects(self.session)
  223. output = self.app.get('/api/0/projects?pattern=test')
  224. self.assertEqual(output.status_code, 200)
  225. data = json.loads(output.get_data(as_text=True))
  226. data['projects'][0]['date_created'] = "1436527638"
  227. data['projects'][0]['date_modified'] = "1436527638"
  228. del data['pagination']
  229. expected_data = {
  230. "args": {
  231. "fork": None,
  232. "namespace": None,
  233. "owner": None,
  234. "page": 1,
  235. "pattern": "test",
  236. "per_page": 20,
  237. "short": False,
  238. "tags": [],
  239. "username": None
  240. },
  241. "projects": [
  242. {
  243. "access_groups": {
  244. "admin": [],
  245. "commit": [],
  246. "ticket": []
  247. },
  248. "access_users": {
  249. "admin": [],
  250. "commit": [],
  251. "owner": [
  252. "pingou"
  253. ],
  254. "ticket": []
  255. },
  256. "close_status": [
  257. "Invalid",
  258. "Insufficient data",
  259. "Fixed",
  260. "Duplicate"
  261. ],
  262. "custom_keys": [],
  263. "date_created": "1436527638",
  264. "date_modified": "1436527638",
  265. "description": "test project #1",
  266. "fullname": "test",
  267. "url_path": "test",
  268. "id": 1,
  269. "milestones": {},
  270. "name": "test",
  271. "namespace": None,
  272. "parent": None,
  273. "priorities": {},
  274. "tags": [],
  275. "user": {
  276. "fullname": "PY C",
  277. "name": "pingou"
  278. }
  279. }
  280. ],
  281. "total_projects": 1
  282. }
  283. self.assertDictEqual(data, expected_data)
  284. def test_api_projects_pattern_short(self):
  285. """ Test the api_projects method of the flask api. """
  286. tests.create_projects(self.session)
  287. output = self.app.get('/api/0/projects?pattern=te*&short=1')
  288. self.assertEqual(output.status_code, 200)
  289. data = json.loads(output.get_data(as_text=True))
  290. del data['pagination']
  291. expected_data = {
  292. "args": {
  293. "fork": None,
  294. "namespace": None,
  295. "owner": None,
  296. "page": 1,
  297. "pattern": "te*",
  298. "per_page": 20,
  299. "short": True,
  300. "tags": [],
  301. "username": None
  302. },
  303. "projects": [
  304. {
  305. "description": "test project #1",
  306. "fullname": "test",
  307. "name": "test",
  308. "namespace": None
  309. },
  310. {
  311. "description": "test project #2",
  312. "fullname": "test2",
  313. "name": "test2",
  314. "namespace": None
  315. },
  316. {
  317. "description": "namespaced test project",
  318. "fullname": "somenamespace/test3",
  319. "name": "test3",
  320. "namespace": "somenamespace"
  321. }
  322. ],
  323. "total_projects": 3
  324. }
  325. self.maxDiff = None
  326. self.assertDictEqual(data, expected_data)
  327. def test_api_projects(self):
  328. """ Test the api_projects method of the flask api. """
  329. tests.create_projects(self.session)
  330. # Check before adding
  331. repo = pagure.lib.get_authorized_project(self.session, 'test')
  332. self.assertEqual(repo.tags, [])
  333. # Adding a tag
  334. output = pagure.lib.update_tags(
  335. self.session, repo, 'infra', 'pingou')
  336. self.assertEqual(output, ['Project tagged with: infra'])
  337. # Check after adding
  338. repo = pagure.lib.get_authorized_project(self.session, 'test')
  339. self.assertEqual(len(repo.tags), 1)
  340. self.assertEqual(repo.tags_text, ['infra'])
  341. # Check the API
  342. output = self.app.get('/api/0/projects?tags=inf')
  343. self.assertEqual(output.status_code, 200)
  344. data = json.loads(output.get_data(as_text=True))
  345. null = None
  346. del data['pagination']
  347. self.assertDictEqual(
  348. data,
  349. {
  350. "total_projects": 0,
  351. "projects": [],
  352. "args": {
  353. "fork": None,
  354. "namespace": None,
  355. "owner": None,
  356. "page": 1,
  357. "pattern": None,
  358. "per_page": 20,
  359. "short": False,
  360. "tags": ["inf"],
  361. "username": None
  362. },
  363. }
  364. )
  365. output = self.app.get('/api/0/projects?tags=infra')
  366. self.assertEqual(output.status_code, 200)
  367. data = json.loads(output.get_data(as_text=True))
  368. data['projects'][0]['date_created'] = "1436527638"
  369. data['projects'][0]['date_modified'] = "1436527638"
  370. del data['pagination']
  371. expected_data = {
  372. "args": {
  373. "fork": None,
  374. "namespace": None,
  375. "owner": None,
  376. "page": 1,
  377. "pattern": None,
  378. "per_page": 20,
  379. "short": False,
  380. "tags": ["infra"],
  381. "username": None
  382. },
  383. "projects": [{
  384. "access_groups": {
  385. "admin": [],
  386. "commit": [],
  387. "ticket": []},
  388. "access_users": {
  389. "admin": [],
  390. "commit": [],
  391. "owner": ["pingou"],
  392. "ticket": []},
  393. "close_status": [
  394. "Invalid",
  395. "Insufficient data",
  396. "Fixed",
  397. "Duplicate"
  398. ],
  399. "custom_keys": [],
  400. "date_created": "1436527638",
  401. "date_modified": "1436527638",
  402. "description": "test project #1",
  403. "fullname": "test",
  404. "url_path": "test",
  405. "id": 1,
  406. "milestones": {},
  407. "name": "test",
  408. "namespace": None,
  409. "parent": None,
  410. "priorities": {},
  411. "tags": ["infra"],
  412. "user": {
  413. "fullname": "PY C",
  414. "name": "pingou"
  415. }
  416. }],
  417. "total_projects": 1
  418. }
  419. self.assertDictEqual(data, expected_data)
  420. output = self.app.get('/api/0/projects?owner=pingou')
  421. self.assertEqual(output.status_code, 200)
  422. data = json.loads(output.get_data(as_text=True))
  423. data['projects'][0]['date_created'] = "1436527638"
  424. data['projects'][0]['date_modified'] = "1436527638"
  425. data['projects'][1]['date_created'] = "1436527638"
  426. data['projects'][1]['date_modified'] = "1436527638"
  427. data['projects'][2]['date_created'] = "1436527638"
  428. data['projects'][2]['date_modified'] = "1436527638"
  429. del data['pagination']
  430. expected_data = {
  431. "args": {
  432. "fork": None,
  433. "namespace": None,
  434. "owner": "pingou",
  435. "page": 1,
  436. "pattern": None,
  437. "per_page": 20,
  438. "short": False,
  439. "tags": [],
  440. "username": None
  441. },
  442. "projects": [
  443. {
  444. "access_groups": {
  445. "admin": [],
  446. "commit": [],
  447. "ticket": []
  448. },
  449. "access_users": {
  450. "admin": [],
  451. "commit": [],
  452. "owner": ["pingou"],
  453. "ticket": []
  454. },
  455. "close_status": [
  456. "Invalid",
  457. "Insufficient data",
  458. "Fixed",
  459. "Duplicate"
  460. ],
  461. "custom_keys": [],
  462. "date_created": "1436527638",
  463. "date_modified": "1436527638",
  464. "description": "test project #1",
  465. "fullname": "test",
  466. "url_path": "test",
  467. "id": 1,
  468. "milestones": {},
  469. "name": "test",
  470. "namespace": None,
  471. "parent": None,
  472. "priorities": {},
  473. "tags": ["infra"],
  474. "user": {
  475. "fullname": "PY C",
  476. "name": "pingou"
  477. }
  478. },
  479. {
  480. "access_groups": {
  481. "admin": [],
  482. "commit": [],
  483. "ticket": []
  484. },
  485. "access_users": {
  486. "admin": [],
  487. "commit": [],
  488. "owner": ["pingou"],
  489. "ticket": []
  490. },
  491. "close_status": [
  492. "Invalid",
  493. "Insufficient data",
  494. "Fixed",
  495. "Duplicate"
  496. ],
  497. "custom_keys": [],
  498. "date_created": "1436527638",
  499. "date_modified": "1436527638",
  500. "description": "test project #2",
  501. "fullname": "test2",
  502. "url_path": "test2",
  503. "id": 2,
  504. "milestones": {},
  505. "name": "test2",
  506. "namespace": None,
  507. "parent": None,
  508. "priorities": {},
  509. "tags": [],
  510. "user": {
  511. "fullname": "PY C",
  512. "name": "pingou"
  513. }
  514. },
  515. {
  516. "access_groups": {
  517. "admin": [],
  518. "commit": [],
  519. "ticket": []
  520. },
  521. "access_users": {
  522. "admin": [],
  523. "commit": [],
  524. "owner": ["pingou"],
  525. "ticket": []
  526. },
  527. "close_status": [
  528. "Invalid",
  529. "Insufficient data",
  530. "Fixed",
  531. "Duplicate"
  532. ],
  533. "custom_keys": [],
  534. "date_created": "1436527638",
  535. "date_modified": "1436527638",
  536. "description": "namespaced test project",
  537. "fullname": "somenamespace/test3",
  538. "url_path": "somenamespace/test3",
  539. "id": 3,
  540. "milestones": {},
  541. "name": "test3",
  542. "namespace": "somenamespace",
  543. "parent": None,
  544. "priorities": {},
  545. "tags": [],
  546. "user": {
  547. "fullname": "PY C",
  548. "name": "pingou"
  549. }
  550. }
  551. ],
  552. "total_projects": 3
  553. }
  554. self.assertDictEqual(data, expected_data)
  555. output = self.app.get('/api/0/projects?username=pingou')
  556. self.assertEqual(output.status_code, 200)
  557. data = json.loads(output.get_data(as_text=True))
  558. data['projects'][0]['date_created'] = "1436527638"
  559. data['projects'][0]['date_modified'] = "1436527638"
  560. data['projects'][1]['date_created'] = "1436527638"
  561. data['projects'][1]['date_modified'] = "1436527638"
  562. data['projects'][2]['date_created'] = "1436527638"
  563. data['projects'][2]['date_modified'] = "1436527638"
  564. del data['pagination']
  565. expected_data = {
  566. "args": {
  567. "fork": None,
  568. "namespace": None,
  569. "owner": None,
  570. "page": 1,
  571. "pattern": None,
  572. "per_page": 20,
  573. "short": False,
  574. "tags": [],
  575. "username": "pingou"
  576. },
  577. "projects": [
  578. {
  579. "access_groups": {
  580. "admin": [],
  581. "commit": [],
  582. "ticket": []},
  583. "access_users": {
  584. "admin": [],
  585. "commit": [],
  586. "owner": ["pingou"],
  587. "ticket": []
  588. },
  589. "close_status": [
  590. "Invalid",
  591. "Insufficient data",
  592. "Fixed",
  593. "Duplicate"
  594. ],
  595. "custom_keys": [],
  596. "date_created": "1436527638",
  597. "date_modified": "1436527638",
  598. "description": "test project #1",
  599. "fullname": "test",
  600. "url_path": "test",
  601. "id": 1,
  602. "milestones": {},
  603. "name": "test",
  604. "namespace": None,
  605. "parent": None,
  606. "priorities": {},
  607. "tags": ["infra"],
  608. "user": {
  609. "fullname": "PY C",
  610. "name": "pingou"
  611. }
  612. },
  613. {
  614. "access_groups": {
  615. "admin": [],
  616. "commit": [],
  617. "ticket": []
  618. },
  619. "access_users": {
  620. "admin": [],
  621. "commit": [],
  622. "owner": ["pingou"],
  623. "ticket": []
  624. },
  625. "close_status": [
  626. "Invalid",
  627. "Insufficient data",
  628. "Fixed",
  629. "Duplicate"
  630. ],
  631. "custom_keys": [],
  632. "date_created": "1436527638",
  633. "date_modified": "1436527638",
  634. "description": "test project #2",
  635. "fullname": "test2",
  636. "url_path": "test2",
  637. "id": 2,
  638. "milestones": {},
  639. "name": "test2",
  640. "namespace": None,
  641. "parent": None,
  642. "priorities": {},
  643. "tags": [],
  644. "user": {
  645. "fullname": "PY C",
  646. "name": "pingou"
  647. }
  648. },
  649. {
  650. "access_groups": {
  651. "admin": [],
  652. "commit": [],
  653. "ticket": []},
  654. "access_users": {
  655. "admin": [],
  656. "commit": [],
  657. "owner": ["pingou"],
  658. "ticket": []},
  659. "close_status": [
  660. "Invalid",
  661. "Insufficient data",
  662. "Fixed",
  663. "Duplicate"
  664. ],
  665. "custom_keys": [],
  666. "date_created": "1436527638",
  667. "date_modified": "1436527638",
  668. "description": "namespaced test project",
  669. "fullname": "somenamespace/test3",
  670. "url_path": "somenamespace/test3",
  671. "id": 3,
  672. "milestones": {},
  673. "name": "test3",
  674. "namespace": "somenamespace",
  675. "parent": None,
  676. "priorities": {},
  677. "tags": [],
  678. "user": {
  679. "fullname": "PY C",
  680. "name": "pingou"
  681. }
  682. }
  683. ],
  684. "total_projects": 3
  685. }
  686. self.assertDictEqual(data, expected_data)
  687. output = self.app.get('/api/0/projects?username=pingou&tags=infra')
  688. self.assertEqual(output.status_code, 200)
  689. data = json.loads(output.get_data(as_text=True))
  690. data['projects'][0]['date_created'] = "1436527638"
  691. data['projects'][0]['date_modified'] = "1436527638"
  692. del data['pagination']
  693. expected_data = {
  694. "args": {
  695. "fork": None,
  696. "namespace": None,
  697. "owner": None,
  698. "page": 1,
  699. "pattern": None,
  700. "per_page": 20,
  701. "short": False,
  702. "tags": ["infra"],
  703. "username": "pingou",
  704. },
  705. "projects": [{
  706. "access_groups": {
  707. "admin": [],
  708. "commit": [],
  709. "ticket": []
  710. },
  711. "access_users": {
  712. "admin": [],
  713. "commit": [],
  714. "owner": ["pingou"],
  715. "ticket": []},
  716. "close_status": [
  717. "Invalid",
  718. "Insufficient data",
  719. "Fixed",
  720. "Duplicate"],
  721. "custom_keys": [],
  722. "date_created": "1436527638",
  723. "date_modified": "1436527638",
  724. "description": "test project #1",
  725. "fullname": "test",
  726. "url_path": "test",
  727. "id": 1,
  728. "milestones": {},
  729. "name": "test",
  730. "namespace": None,
  731. "parent": None,
  732. "priorities": {},
  733. "tags": ["infra"],
  734. "user": {
  735. "fullname": "PY C",
  736. "name": "pingou"
  737. }
  738. }],
  739. "total_projects": 1
  740. }
  741. self.assertDictEqual(data, expected_data)
  742. output = self.app.get('/api/0/projects?namespace=somenamespace')
  743. self.assertEqual(output.status_code, 200)
  744. data = json.loads(output.get_data(as_text=True))
  745. data['projects'][0]['date_created'] = "1436527638"
  746. data['projects'][0]['date_modified'] = "1436527638"
  747. del data['pagination']
  748. expected_data = {
  749. "args": {
  750. "fork": None,
  751. "owner": None,
  752. "page": 1,
  753. "namespace": "somenamespace",
  754. "per_page": 20,
  755. "pattern": None,
  756. "short": False,
  757. "tags": [],
  758. "username": None
  759. },
  760. "projects": [
  761. {
  762. "access_groups": {
  763. "admin": [],
  764. "commit": [],
  765. "ticket": []},
  766. "access_users": {
  767. "admin": [],
  768. "commit": [],
  769. "owner": ["pingou"],
  770. "ticket": []},
  771. "close_status": [
  772. "Invalid",
  773. "Insufficient data",
  774. "Fixed",
  775. "Duplicate"
  776. ],
  777. "custom_keys": [],
  778. "date_created": "1436527638",
  779. "date_modified": "1436527638",
  780. "description": "namespaced test project",
  781. "fullname": "somenamespace/test3",
  782. "url_path": "somenamespace/test3",
  783. "id": 3,
  784. "milestones": {},
  785. "name": "test3",
  786. "namespace": "somenamespace",
  787. "parent": None,
  788. "priorities": {},
  789. "tags": [],
  790. "user": {
  791. "fullname": "PY C",
  792. "name": "pingou"
  793. }
  794. }
  795. ],
  796. "total_projects": 1
  797. }
  798. self.assertDictEqual(data, expected_data)
  799. def test_api_project(self):
  800. """ Test the api_project method of the flask api. """
  801. tests.create_projects(self.session)
  802. # Check before adding
  803. repo = pagure.lib.get_authorized_project(self.session, 'test')
  804. self.assertEqual(repo.tags, [])
  805. # Adding a tag
  806. output = pagure.lib.update_tags(
  807. self.session, repo, 'infra', 'pingou')
  808. self.assertEqual(output, ['Project tagged with: infra'])
  809. # Check after adding
  810. repo = pagure.lib.get_authorized_project(self.session, 'test')
  811. self.assertEqual(len(repo.tags), 1)
  812. self.assertEqual(repo.tags_text, ['infra'])
  813. # Check the API
  814. # Non-existing project
  815. output = self.app.get('/api/0/random')
  816. self.assertEqual(output.status_code, 404)
  817. data = json.loads(output.get_data(as_text=True))
  818. self.assertDictEqual(
  819. data,
  820. {'error_code': 'ENOPROJECT', 'error': 'Project not found'}
  821. )
  822. # Existing project
  823. output = self.app.get('/api/0/test')
  824. self.assertEqual(output.status_code, 200)
  825. data = json.loads(output.get_data(as_text=True))
  826. data['date_created'] = "1436527638"
  827. data['date_modified'] = "1436527638"
  828. expected_data ={
  829. "access_groups": {
  830. "admin": [],
  831. "commit": [],
  832. "ticket": []
  833. },
  834. "access_users": {
  835. "admin": [],
  836. "commit": [],
  837. "owner": ["pingou"],
  838. "ticket": []},
  839. "close_status": [
  840. "Invalid",
  841. "Insufficient data",
  842. "Fixed",
  843. "Duplicate"
  844. ],
  845. "custom_keys": [],
  846. "date_created": "1436527638",
  847. "date_modified": "1436527638",
  848. "description": "test project #1",
  849. "fullname": "test",
  850. "url_path": "test",
  851. "id": 1,
  852. "milestones": {},
  853. "name": "test",
  854. "namespace": None,
  855. "parent": None,
  856. "priorities": {},
  857. "tags": ["infra"],
  858. "user": {
  859. "fullname": "PY C",
  860. "name": "pingou"
  861. }
  862. }
  863. self.assertDictEqual(data, expected_data)
  864. def test_api_project_group(self):
  865. """ Test the api_project method of the flask api. """
  866. tests.create_projects(self.session)
  867. repo = pagure.lib.get_authorized_project(self.session, 'test')
  868. # Adding a tag
  869. output = pagure.lib.update_tags(
  870. self.session, repo, 'infra', 'pingou')
  871. self.assertEqual(output, ['Project tagged with: infra'])
  872. # Check after adding
  873. repo = pagure.lib.get_authorized_project(self.session, 'test')
  874. self.assertEqual(len(repo.tags), 1)
  875. self.assertEqual(repo.tags_text, ['infra'])
  876. # Add a group to the project
  877. msg = pagure.lib.add_group(
  878. self.session,
  879. group_name='some_group',
  880. display_name='Some Group',
  881. description=None,
  882. group_type='bar',
  883. user='foo',
  884. is_admin=False,
  885. blacklist=[],
  886. )
  887. self.session.commit()
  888. project = pagure.lib.get_authorized_project(self.session, 'test')
  889. group = pagure.lib.search_groups(
  890. self.session, group_name='some_group')
  891. pagure.lib.add_group_to_project(
  892. self.session,
  893. project,
  894. new_group='some_group',
  895. user='pingou',
  896. access='commit',
  897. create=False,
  898. is_admin=True
  899. )
  900. self.session.commit()
  901. # Check the API
  902. # Existing project
  903. output = self.app.get('/api/0/test?expand_group=1')
  904. self.assertEqual(output.status_code, 200)
  905. data = json.loads(output.get_data(as_text=True))
  906. data['date_created'] = "1436527638"
  907. data['date_modified'] = "1436527638"
  908. expected_data ={
  909. "access_groups": {
  910. "admin": [],
  911. "commit": ["some_group"],
  912. "ticket": []
  913. },
  914. "access_users": {
  915. "admin": [],
  916. "commit": [],
  917. "owner": ["pingou"],
  918. "ticket": []},
  919. "close_status": [
  920. "Invalid",
  921. "Insufficient data",
  922. "Fixed",
  923. "Duplicate"
  924. ],
  925. "custom_keys": [],
  926. "date_created": "1436527638",
  927. "date_modified": "1436527638",
  928. "description": "test project #1",
  929. "fullname": "test",
  930. "url_path": "test",
  931. "group_details": {
  932. "some_group": [
  933. "foo"
  934. ]
  935. },
  936. "id": 1,
  937. "milestones": {},
  938. "name": "test",
  939. "namespace": None,
  940. "parent": None,
  941. "priorities": {},
  942. "tags": ["infra"],
  943. "user": {
  944. "fullname": "PY C",
  945. "name": "pingou"
  946. }
  947. }
  948. self.assertDictEqual(data, expected_data)
  949. def test_api_project_group_but_no_group(self):
  950. """ Test the api_project method of the flask api when asking for
  951. group details while there are none associated.
  952. """
  953. tests.create_projects(self.session)
  954. repo = pagure.lib.get_authorized_project(self.session, 'test')
  955. # Adding a tag
  956. output = pagure.lib.update_tags(
  957. self.session, repo, 'infra', 'pingou')
  958. self.assertEqual(output, ['Project tagged with: infra'])
  959. # Check after adding
  960. repo = pagure.lib.get_authorized_project(self.session, 'test')
  961. self.assertEqual(len(repo.tags), 1)
  962. self.assertEqual(repo.tags_text, ['infra'])
  963. # Check the API
  964. # Existing project
  965. output = self.app.get('/api/0/test?expand_group=0')
  966. self.assertEqual(output.status_code, 200)
  967. data = json.loads(output.get_data(as_text=True))
  968. data['date_created'] = "1436527638"
  969. data['date_modified'] = "1436527638"
  970. expected_data ={
  971. "access_groups": {
  972. "admin": [],
  973. "commit": [],
  974. "ticket": []
  975. },
  976. "access_users": {
  977. "admin": [],
  978. "commit": [],
  979. "owner": ["pingou"],
  980. "ticket": []},
  981. "close_status": [
  982. "Invalid",
  983. "Insufficient data",
  984. "Fixed",
  985. "Duplicate"
  986. ],
  987. "custom_keys": [],
  988. "date_created": "1436527638",
  989. "date_modified": "1436527638",
  990. "description": "test project #1",
  991. "fullname": "test",
  992. "url_path": "test",
  993. "id": 1,
  994. "milestones": {},
  995. "name": "test",
  996. "namespace": None,
  997. "parent": None,
  998. "priorities": {},
  999. "tags": ["infra"],
  1000. "user": {
  1001. "fullname": "PY C",
  1002. "name": "pingou"
  1003. }
  1004. }
  1005. self.assertDictEqual(data, expected_data)
  1006. def test_api_projects_pagination(self):
  1007. """ Test the api_projects method of the flask api with pagination. """
  1008. tests.create_projects(self.session)
  1009. output = self.app.get('/api/0/projects?page=1')
  1010. self.assertEqual(output.status_code, 200)
  1011. data = json.loads(output.get_data(as_text=True))
  1012. for i in range(3):
  1013. data['projects'][i]['date_created'] = "1436527638"
  1014. data['projects'][i]['date_modified'] = "1436527638"
  1015. expected_data = {
  1016. "args": {
  1017. "fork": None,
  1018. "namespace": None,
  1019. "owner": None,
  1020. "page": 1,
  1021. "per_page": 20,
  1022. "pattern": None,
  1023. "short": False,
  1024. "tags": [],
  1025. "username": None
  1026. },
  1027. "pagination": {
  1028. "next": None,
  1029. "page": 1,
  1030. "pages": 1,
  1031. "per_page": 20,
  1032. "prev": None
  1033. },
  1034. "projects": [
  1035. {
  1036. "access_groups": {
  1037. "admin": [],
  1038. "commit": [],
  1039. "ticket": []},
  1040. "access_users": {
  1041. "admin": [],
  1042. "commit": [],
  1043. "owner": ["pingou"],
  1044. "ticket": []
  1045. },
  1046. "close_status": [
  1047. "Invalid",
  1048. "Insufficient data",
  1049. "Fixed",
  1050. "Duplicate"
  1051. ],
  1052. "custom_keys": [],
  1053. "date_created": "1436527638",
  1054. "date_modified": "1436527638",
  1055. "description": "test project #1",
  1056. "fullname": "test",
  1057. "url_path": "test",
  1058. "id": 1,
  1059. "milestones": {},
  1060. "name": "test",
  1061. "namespace": None,
  1062. "parent": None,
  1063. "priorities": {},
  1064. "tags": [],
  1065. "user": {
  1066. "fullname": "PY C",
  1067. "name": "pingou"
  1068. }
  1069. },
  1070. {
  1071. "access_groups": {
  1072. "admin": [],
  1073. "commit": [],
  1074. "ticket": []
  1075. },
  1076. "access_users": {
  1077. "admin": [],
  1078. "commit": [],
  1079. "owner": ["pingou"],
  1080. "ticket": []
  1081. },
  1082. "close_status": [
  1083. "Invalid",
  1084. "Insufficient data",
  1085. "Fixed",
  1086. "Duplicate"
  1087. ],
  1088. "custom_keys": [],
  1089. "date_created": "1436527638",
  1090. "date_modified": "1436527638",
  1091. "description": "test project #2",
  1092. "fullname": "test2",
  1093. "url_path": "test2",
  1094. "id": 2,
  1095. "milestones": {},
  1096. "name": "test2",
  1097. "namespace": None,
  1098. "parent": None,
  1099. "priorities": {},
  1100. "tags": [],
  1101. "user": {
  1102. "fullname": "PY C",
  1103. "name": "pingou"
  1104. }
  1105. },
  1106. {
  1107. "access_groups": {
  1108. "admin": [],
  1109. "commit": [],
  1110. "ticket": []},
  1111. "access_users": {
  1112. "admin": [],
  1113. "commit": [],
  1114. "owner": ["pingou"],
  1115. "ticket": []},
  1116. "close_status": [
  1117. "Invalid",
  1118. "Insufficient data",
  1119. "Fixed",
  1120. "Duplicate"
  1121. ],
  1122. "custom_keys": [],
  1123. "date_created": "1436527638",
  1124. "date_modified": "1436527638",
  1125. "description": "namespaced test project",
  1126. "fullname": "somenamespace/test3",
  1127. "url_path": "somenamespace/test3",
  1128. "id": 3,
  1129. "milestones": {},
  1130. "name": "test3",
  1131. "namespace": "somenamespace",
  1132. "parent": None,
  1133. "priorities": {},
  1134. "tags": [],
  1135. "user": {
  1136. "fullname": "PY C",
  1137. "name": "pingou"
  1138. }
  1139. }
  1140. ],
  1141. "total_projects": 3
  1142. }
  1143. # Test URLs
  1144. self.assertURLEqual(
  1145. data["pagination"].pop("first"),
  1146. "http://localhost/api/0/projects?per_page=20&page=1",
  1147. )
  1148. self.assertURLEqual(
  1149. data["pagination"].pop("last"),
  1150. "http://localhost/api/0/projects?per_page=20&page=1",
  1151. )
  1152. self.assertDictEqual(data, expected_data)
  1153. def test_api_projects_pagination_per_page(self):
  1154. """ Test the api_projects method of the flask api with pagination and
  1155. the `per_page` argument set. """
  1156. tests.create_projects(self.session)
  1157. output = self.app.get('/api/0/projects?page=2&per_page=2')
  1158. self.assertEqual(output.status_code, 200)
  1159. data = json.loads(output.get_data(as_text=True))
  1160. data['projects'][0]['date_created'] = "1436527638"
  1161. data['projects'][0]['date_modified'] = "1436527638"
  1162. expected_data = {
  1163. "args": {
  1164. "fork": None,
  1165. "namespace": None,
  1166. "owner": None,
  1167. "page": 2,
  1168. "per_page": 2,
  1169. "pattern": None,
  1170. "short": False,
  1171. "tags": [],
  1172. "username": None
  1173. },
  1174. "pagination": {
  1175. "next": None,
  1176. "page": 2,
  1177. "pages": 2,
  1178. "per_page": 2,
  1179. },
  1180. "projects": [
  1181. {
  1182. "access_groups": {
  1183. "admin": [],
  1184. "commit": [],
  1185. "ticket": []
  1186. },
  1187. "access_users": {
  1188. "admin": [],
  1189. "commit": [],
  1190. "owner": ["pingou"],
  1191. "ticket": []
  1192. },
  1193. "close_status": [
  1194. "Invalid",
  1195. "Insufficient data",
  1196. "Fixed",
  1197. "Duplicate"
  1198. ],
  1199. "custom_keys": [],
  1200. "date_created": "1436527638",
  1201. "date_modified": "1436527638",
  1202. "description": "namespaced test project",
  1203. "fullname": "somenamespace/test3",
  1204. "url_path": "somenamespace/test3",
  1205. "id": 3,
  1206. "milestones": {},
  1207. "name": "test3",
  1208. "namespace": "somenamespace",
  1209. "parent": None,
  1210. "priorities": {},
  1211. "tags": [],
  1212. "user": {
  1213. "fullname": "PY C",
  1214. "name": "pingou"
  1215. }
  1216. }
  1217. ],
  1218. "total_projects": 3
  1219. }
  1220. self.assertURLEqual(
  1221. data["pagination"].pop("first"),
  1222. "http://localhost/api/0/projects?per_page=2&page=1",
  1223. )
  1224. self.assertURLEqual(
  1225. data["pagination"].pop("prev"),
  1226. "http://localhost/api/0/projects?per_page=2&page=1",
  1227. )
  1228. self.assertURLEqual(
  1229. data["pagination"].pop("last"),
  1230. "http://localhost/api/0/projects?per_page=2&page=2",
  1231. )
  1232. self.assertDictEqual(data, expected_data)
  1233. def test_api_projects_pagination_invalid_page(self):
  1234. """ Test the api_projects method of the flask api when an invalid page
  1235. value is entered. """
  1236. tests.create_projects(self.session)
  1237. output = self.app.get('/api/0/projects?page=-3')
  1238. self.assertEqual(output.status_code, 400)
  1239. def test_api_projects_pagination_invalid_page_str(self):
  1240. """ Test the api_projects method of the flask api when an invalid type
  1241. for the page value is entered. """
  1242. tests.create_projects(self.session)
  1243. output = self.app.get('/api/0/projects?page=abcd')
  1244. self.assertEqual(output.status_code, 400)
  1245. def test_api_projects_pagination_invalid_per_page_too_low(self):
  1246. """ Test the api_projects method of the flask api when a per_page
  1247. value is below 1. """
  1248. tests.create_projects(self.session)
  1249. output = self.app.get('/api/0/projects?page=1&per_page=0')
  1250. self.assertEqual(output.status_code, 400)
  1251. error = json.loads(output.get_data(as_text=True))
  1252. self.assertEqual(
  1253. error['error'], 'The per_page value must be between 1 and 100')
  1254. def test_api_projects_pagination_invalid_per_page_too_high(self):
  1255. """ Test the api_projects method of the flask api when a per_page
  1256. value is above 100. """
  1257. tests.create_projects(self.session)
  1258. output = self.app.get('/api/0/projects?page=1&per_page=101')
  1259. self.assertEqual(output.status_code, 400)
  1260. error = json.loads(output.get_data(as_text=True))
  1261. self.assertEqual(
  1262. error['error'], 'The per_page value must be between 1 and 100')
  1263. def test_api_projects_pagination_invalid_per_page_str(self):
  1264. """ Test the api_projects method of the flask api when an invalid type
  1265. for the per_page value is entered. """
  1266. tests.create_projects(self.session)
  1267. output = self.app.get('/api/0/projects?page=1&per_page=abcd')
  1268. self.assertEqual(output.status_code, 400)
  1269. def test_api_projects_pagination_beyond_last_page(self):
  1270. """ Test the api_projects method of the flask api when a page value
  1271. that is larger than the last page is entered. """
  1272. tests.create_projects(self.session)
  1273. output = self.app.get('/api/0/projects?page=99999')
  1274. self.assertEqual(output.status_code, 200)
  1275. data = json.loads(output.get_data(as_text=True))
  1276. self.assertURLEqual(
  1277. data["pagination"].pop("first"),
  1278. "http://localhost/api/0/projects?per_page=20&page=1",
  1279. )
  1280. self.assertURLEqual(
  1281. data["pagination"].pop("last"),
  1282. "http://localhost/api/0/projects?per_page=20&page=1",
  1283. )
  1284. self.assertURLEqual(
  1285. data["pagination"].pop("prev"),
  1286. "http://localhost/api/0/projects?per_page=20&page=99998",
  1287. )
  1288. self.assertEqual(
  1289. data,
  1290. {
  1291. "args": {
  1292. "fork": None,
  1293. "namespace": None,
  1294. "owner": None,
  1295. "page": 99999,
  1296. "pattern": None,
  1297. "per_page": 20,
  1298. "short": False,
  1299. "tags": [],
  1300. "username": None
  1301. },
  1302. "pagination": {
  1303. "next": None,
  1304. "page": 99999,
  1305. "pages": 1,
  1306. "per_page": 20,
  1307. },
  1308. "projects": [],
  1309. "total_projects": 3
  1310. }
  1311. )
  1312. def test_api_modify_project_main_admin(self):
  1313. """ Test the api_modify_project method of the flask api when the
  1314. request is to change the main_admin of the project. """
  1315. tests.create_projects(self.session)
  1316. tests.create_tokens(self.session, project_id=None)
  1317. tests.create_tokens_acl(self.session, 'aaabbbcccddd', 'modify_project')
  1318. headers = {'Authorization': 'token aaabbbcccddd'}
  1319. output = self.app.patch(
  1320. '/api/0/test', headers=headers,
  1321. data={'main_admin': 'foo'})
  1322. self.assertEqual(output.status_code, 200)
  1323. data = json.loads(output.get_data(as_text=True))
  1324. data['date_created'] = '1496338274'
  1325. data['date_modified'] = '1496338274'
  1326. expected_output = {
  1327. "access_groups": {
  1328. "admin": [],
  1329. "commit": [],
  1330. "ticket": []
  1331. },
  1332. "access_users": {
  1333. "admin": [],
  1334. "commit": [],
  1335. "owner": [
  1336. "foo"
  1337. ],
  1338. "ticket": []
  1339. },
  1340. "close_status": [
  1341. "Invalid",
  1342. "Insufficient data",
  1343. "Fixed",
  1344. "Duplicate"
  1345. ],
  1346. "custom_keys": [],
  1347. "date_created": "1496338274",
  1348. "date_modified": "1496338274",
  1349. "description": "test project #1",
  1350. "fullname": "test",
  1351. "url_path": "test",
  1352. "id": 1,
  1353. "milestones": {},
  1354. "name": "test",
  1355. "namespace": None,
  1356. "parent": None,
  1357. "priorities": {},
  1358. "tags": [],
  1359. "user": {
  1360. "default_email": "foo@bar.com",
  1361. "emails": [
  1362. "foo@bar.com"
  1363. ],
  1364. "fullname": "foo bar",
  1365. "name": "foo"
  1366. }
  1367. }
  1368. self.assertEqual(data, expected_output)
  1369. def test_api_modify_project_main_admin_retain_access(self):
  1370. """ Test the api_modify_project method of the flask api when the
  1371. request is to change the main_admin of the project and retain_access
  1372. is true. """
  1373. tests.create_projects(self.session)
  1374. tests.create_tokens(self.session, project_id=None)
  1375. tests.create_tokens_acl(self.session, 'aaabbbcccddd', 'modify_project')
  1376. headers = {'Authorization': 'token aaabbbcccddd'}
  1377. output = self.app.patch(
  1378. '/api/0/test', headers=headers,
  1379. data={'main_admin': 'foo', 'retain_access': True})
  1380. self.assertEqual(output.status_code, 200)
  1381. data = json.loads(output.get_data(as_text=True))
  1382. data['date_created'] = '1496338274'
  1383. data['date_modified'] = '1496338274'
  1384. expected_output = {
  1385. "access_groups": {
  1386. "admin": [],
  1387. "commit": [],
  1388. "ticket": []
  1389. },
  1390. "access_users": {
  1391. "admin": [
  1392. "pingou"
  1393. ],
  1394. "commit": [],
  1395. "owner": [
  1396. "foo"
  1397. ],
  1398. "ticket": []
  1399. },
  1400. "close_status": [
  1401. "Invalid",
  1402. "Insufficient data",
  1403. "Fixed",
  1404. "Duplicate"
  1405. ],
  1406. "custom_keys": [],
  1407. "date_created": "1496338274",
  1408. "date_modified": "1496338274",
  1409. "description": "test project #1",
  1410. "fullname": "test",
  1411. "url_path": "test",
  1412. "id": 1,
  1413. "milestones": {},
  1414. "name": "test",
  1415. "namespace": None,
  1416. "parent": None,
  1417. "priorities": {},
  1418. "tags": [],
  1419. "user": {
  1420. "default_email": "foo@bar.com",
  1421. "emails": [
  1422. "foo@bar.com"
  1423. ],
  1424. "fullname": "foo bar",
  1425. "name": "foo"
  1426. }
  1427. }
  1428. self.assertEqual(data, expected_output)
  1429. def test_api_modify_project_main_admin_retain_access_already_user(self):
  1430. """ Test the api_modify_project method of the flask api when the
  1431. request is to change the main_admin of the project and retain_access
  1432. is true and the user becoming the main_admin already has access. """
  1433. tests.create_projects(self.session)
  1434. tests.create_tokens(self.session, project_id=None)
  1435. tests.create_tokens_acl(self.session, 'aaabbbcccddd', 'modify_project')
  1436. headers = {'Authorization': 'token aaabbbcccddd'}
  1437. project = pagure.lib._get_project(self.session, 'test')
  1438. pagure.lib.add_user_to_project(
  1439. self.session, project,
  1440. new_user='foo',
  1441. user='pingou',
  1442. access='commit'
  1443. )
  1444. self.session.commit()
  1445. output = self.app.patch(
  1446. '/api/0/test', headers=headers,
  1447. data={'main_admin': 'foo', 'retain_access': True})
  1448. self.assertEqual(output.status_code, 200)
  1449. data = json.loads(output.get_data(as_text=True))
  1450. data['date_created'] = '1496338274'
  1451. data['date_modified'] = '1496338274'
  1452. expected_output = {
  1453. "access_groups": {
  1454. "admin": [],
  1455. "commit": [],
  1456. "ticket": []
  1457. },
  1458. "access_users": {
  1459. "admin": [
  1460. "pingou"
  1461. ],
  1462. "commit": [],
  1463. "owner": [
  1464. "foo"
  1465. ],
  1466. "ticket": []
  1467. },
  1468. "close_status": [
  1469. "Invalid",
  1470. "Insufficient data",
  1471. "Fixed",
  1472. "Duplicate"
  1473. ],
  1474. "custom_keys": [],
  1475. "date_created": "1496338274",
  1476. "date_modified": "1496338274",
  1477. "description": "test project #1",
  1478. "fullname": "test",
  1479. "url_path": "test",
  1480. "id": 1,
  1481. "milestones": {},
  1482. "name": "test",
  1483. "namespace": None,
  1484. "parent": None,
  1485. "priorities": {},
  1486. "tags": [],
  1487. "user": {
  1488. "default_email": "foo@bar.com",
  1489. "emails": [
  1490. "foo@bar.com"
  1491. ],
  1492. "fullname": "foo bar",
  1493. "name": "foo"
  1494. }
  1495. }
  1496. self.assertEqual(data, expected_output)
  1497. def test_api_modify_project_main_admin_json(self):
  1498. """ Test the api_modify_project method of the flask api when the
  1499. request is to change the main_admin of the project using JSON. """
  1500. tests.create_projects(self.session)
  1501. tests.create_tokens(self.session, project_id=None)
  1502. tests.create_tokens_acl(self.session, 'aaabbbcccddd', 'modify_project')
  1503. headers = {'Authorization': 'token aaabbbcccddd',
  1504. 'Content-Type': 'application/json'}
  1505. output = self.app.patch(
  1506. '/api/0/test', headers=headers,
  1507. data=json.dumps({'main_admin': 'foo'}))
  1508. self.assertEqual(output.status_code, 200)
  1509. data = json.loads(output.get_data(as_text=True))
  1510. data['date_created'] = '1496338274'
  1511. data['date_modified'] = '1496338274'
  1512. expected_output = {
  1513. "access_groups": {
  1514. "admin": [],
  1515. "commit": [],
  1516. "ticket": []
  1517. },
  1518. "access_users": {
  1519. "admin": [],
  1520. "commit": [],
  1521. "owner": [
  1522. "foo"
  1523. ],
  1524. "ticket": []
  1525. },
  1526. "close_status": [
  1527. "Invalid",
  1528. "Insufficient data",
  1529. "Fixed",
  1530. "Duplicate"
  1531. ],
  1532. "custom_keys": [],
  1533. "date_created": "1496338274",
  1534. "date_modified": "1496338274",
  1535. "description": "test project #1",
  1536. "fullname": "test",
  1537. "url_path": "test",
  1538. "id": 1,
  1539. "milestones": {},
  1540. "name": "test",
  1541. "namespace": None,
  1542. "parent": None,
  1543. "priorities": {},
  1544. "tags": [],
  1545. "user": {
  1546. "default_email": "foo@bar.com",
  1547. "emails": [
  1548. "foo@bar.com"
  1549. ],
  1550. "fullname": "foo bar",
  1551. "name": "foo"
  1552. }
  1553. }
  1554. self.assertEqual(data, expected_output)
  1555. @patch.dict('pagure.config.config', {'PAGURE_ADMIN_USERS': 'foo'})
  1556. def test_api_modify_project_main_admin_as_site_admin(self):
  1557. """ Test the api_modify_project method of the flask api when the
  1558. request is to change the main_admin of the project and the user is a
  1559. Pagure site admin. """
  1560. tests.create_projects(self.session)
  1561. tests.create_tokens(self.session, user_id=2, project_id=None)
  1562. tests.create_tokens_acl(self.session, 'aaabbbcccddd', 'modify_project')
  1563. headers = {'Authorization': 'token aaabbbcccddd'}
  1564. output = self.app.patch(
  1565. '/api/0/test', headers=headers,
  1566. data={'main_admin': 'foo'})
  1567. self.assertEqual(output.status_code, 200)
  1568. data = json.loads(output.get_data(as_text=True))
  1569. data['date_created'] = '1496338274'
  1570. data['date_modified'] = '1496338274'
  1571. expected_output = {
  1572. "access_groups": {
  1573. "admin": [],
  1574. "commit": [],
  1575. "ticket": []
  1576. },
  1577. "access_users": {
  1578. "admin": [],
  1579. "commit": [],
  1580. "owner": [
  1581. "foo"
  1582. ],
  1583. "ticket": []
  1584. },
  1585. "close_status": [
  1586. "Invalid",
  1587. "Insufficient data",
  1588. "Fixed",
  1589. "Duplicate"
  1590. ],
  1591. "custom_keys": [],
  1592. "date_created": "1496338274",
  1593. "date_modified": "1496338274",
  1594. "description": "test project #1",
  1595. "fullname": "test",
  1596. "url_path": "test",
  1597. "id": 1,
  1598. "milestones": {},
  1599. "name": "test",
  1600. "namespace": None,
  1601. "parent": None,
  1602. "priorities": {},
  1603. "tags": [],
  1604. "user": {
  1605. "default_email": "foo@bar.com",
  1606. "emails": [
  1607. "foo@bar.com"
  1608. ],
  1609. "fullname": "foo bar",
  1610. "name": "foo"
  1611. }
  1612. }
  1613. self.assertEqual(data, expected_output)
  1614. def test_api_modify_project_main_admin_not_main_admin(self):
  1615. """ Test the api_modify_project method of the flask api when the
  1616. requester is not the main_admin of the project and requests to change
  1617. the main_admin.
  1618. """
  1619. tests.create_projects(self.session)
  1620. project_user = pagure.lib.model.ProjectUser(
  1621. project_id=1,
  1622. user_id=2,
  1623. access='admin',
  1624. )
  1625. self.session.add(project_user)
  1626. self.session.commit()
  1627. tests.create_tokens(self.session, project_id=None, user_id=2)
  1628. tests.create_tokens_acl(self.session, 'aaabbbcccddd', 'modify_project')
  1629. headers = {'Authorization': 'token aaabbbcccddd'}
  1630. output = self.app.patch(
  1631. '/api/0/test', headers=headers,
  1632. data={'main_admin': 'foo'})
  1633. self.assertEqual(output.status_code, 401)
  1634. expected_error = {
  1635. 'error': ('Only the main admin can set the main admin of a '
  1636. 'project'),
  1637. 'error_code': 'ENOTMAINADMIN'
  1638. }
  1639. self.assertEqual(
  1640. json.loads(output.get_data(as_text=True)), expected_error)
  1641. def test_api_modify_project_not_admin(self):
  1642. """ Test the api_modify_project method of the flask api when the
  1643. requester is not an admin of the project.
  1644. """
  1645. tests.create_projects(self.session)
  1646. tests.create_tokens(self.session, project_id=None, user_id=2)
  1647. tests.create_tokens_acl(self.session, 'aaabbbcccddd', 'modify_project')
  1648. headers = {'Authorization': 'token aaabbbcccddd'}
  1649. output = self.app.patch(
  1650. '/api/0/test', headers=headers,
  1651. data={'main_admin': 'foo'})
  1652. self.assertEqual(output.status_code, 401)
  1653. expected_error = {
  1654. 'error': 'You are not allowed to modify this project',
  1655. 'error_code': 'EMODIFYPROJECTNOTALLOWED'
  1656. }
  1657. self.assertEqual(
  1658. json.loads(output.get_data(as_text=True)), expected_error)
  1659. def test_api_modify_project_invalid_request(self):
  1660. """ Test the api_modify_project method of the flask api when the
  1661. request data is invalid.
  1662. """
  1663. tests.create_projects(self.session)
  1664. tests.create_tokens(self.session, project_id=None)
  1665. tests.create_tokens_acl(self.session, 'aaabbbcccddd', 'modify_project')
  1666. headers = {'Authorization': 'token aaabbbcccddd'}
  1667. output = self.app.patch(
  1668. '/api/0/test', headers=headers,
  1669. data='invalid')
  1670. self.assertEqual(output.status_code, 400)
  1671. expected_error = {
  1672. 'error': 'Invalid or incomplete input submitted',
  1673. 'error_code': 'EINVALIDREQ'
  1674. }
  1675. self.assertEqual(
  1676. json.loads(output.get_data(as_text=True)), expected_error)
  1677. def test_api_modify_project_invalid_keys(self):
  1678. """ Test the api_modify_project method of the flask api when the
  1679. request data contains an invalid key.
  1680. """
  1681. tests.create_projects(self.session)
  1682. tests.create_tokens(self.session, project_id=None)
  1683. tests.create_tokens_acl(self.session, 'aaabbbcccddd', 'modify_project')
  1684. headers = {'Authorization': 'token aaabbbcccddd'}
  1685. output = self.app.patch(
  1686. '/api/0/test', headers=headers,
  1687. data={'invalid': 'invalid'})
  1688. self.assertEqual(output.status_code, 400)
  1689. expected_error = {
  1690. 'error': 'Invalid or incomplete input submitted',
  1691. 'error_code': 'EINVALIDREQ'
  1692. }
  1693. self.assertEqual(
  1694. json.loads(output.get_data(as_text=True)), expected_error)
  1695. def test_api_modify_project_invalid_new_main_admin(self):
  1696. """ Test the api_modify_project method of the flask api when the
  1697. request is to change the main_admin of the project to a main_admin
  1698. that doesn't exist.
  1699. """
  1700. tests.create_projects(self.session)
  1701. tests.create_tokens(self.session, project_id=None)
  1702. tests.create_tokens_acl(self.session, 'aaabbbcccddd', 'modify_project')
  1703. headers = {'Authorization': 'token aaabbbcccddd'}
  1704. output = self.app.patch(
  1705. '/api/0/test', headers=headers,
  1706. data={'main_admin': 'tbrady'})
  1707. self.assertEqual(output.status_code, 400)
  1708. expected_error = {
  1709. 'error': 'No such user found',
  1710. 'error_code': 'ENOUSER'
  1711. }
  1712. self.assertEqual(
  1713. json.loads(output.get_data(as_text=True)), expected_error)
  1714. def test_api_project_watchers(self):
  1715. """ Test the api_project_watchers method of the flask api. """
  1716. tests.create_projects(self.session)
  1717. # The user is not logged in and the owner is watching issues implicitly
  1718. output = self.app.get('/api/0/test/watchers')
  1719. self.assertEqual(output.status_code, 200)
  1720. expected_data = {
  1721. "total_watchers": 1,
  1722. "watchers": {
  1723. "pingou": [
  1724. "issues"
  1725. ]
  1726. }
  1727. }
  1728. self.assertDictEqual(json.loads(output.get_data(as_text=True)), expected_data)
  1729. user = tests.FakeUser(username='pingou')
  1730. with tests.user_set(self.app.application, user):
  1731. # Non-existing project
  1732. output = self.app.get('/api/0/random/watchers')
  1733. self.assertEqual(output.status_code, 404)
  1734. data = json.loads(output.get_data(as_text=True))
  1735. self.assertDictEqual(
  1736. data,
  1737. {'error_code': 'ENOPROJECT', 'error': 'Project not found'}
  1738. )
  1739. # The owner is watching issues implicitly
  1740. output = self.app.get('/api/0/test/watchers')
  1741. self.assertEqual(output.status_code, 200)
  1742. expected_data = {
  1743. "total_watchers": 1,
  1744. "watchers": {
  1745. "pingou": [
  1746. "issues"
  1747. ]
  1748. }
  1749. }
  1750. self.assertDictEqual(json.loads(output.get_data(as_text=True)), expected_data)
  1751. project = pagure.lib.get_authorized_project(self.session, 'test')
  1752. # The owner is watching issues and commits explicitly
  1753. pagure.lib.update_watch_status(
  1754. self.session, project, 'pingou', '3')
  1755. self.session.commit()
  1756. output = self.app.get('/api/0/test/watchers')
  1757. self.assertEqual(output.status_code, 200)
  1758. expected_data = {
  1759. "total_watchers": 1,
  1760. "watchers": {
  1761. "pingou": [
  1762. "issues",
  1763. "commits"
  1764. ]
  1765. }
  1766. }
  1767. self.assertDictEqual(json.loads(output.get_data(as_text=True)), expected_data)
  1768. # The owner is watching issues explicitly
  1769. pagure.lib.update_watch_status(
  1770. self.session, project, 'pingou', '1')
  1771. self.session.commit()
  1772. output = self.app.get('/api/0/test/watchers')
  1773. self.assertEqual(output.status_code, 200)
  1774. expected_data = {
  1775. "total_watchers": 1,
  1776. "watchers": {
  1777. "pingou": [
  1778. "issues"
  1779. ]
  1780. }
  1781. }
  1782. self.assertDictEqual(json.loads(output.get_data(as_text=True)), expected_data)
  1783. # The owner is watching commits explicitly
  1784. pagure.lib.update_watch_status(
  1785. self.session, project, 'pingou', '2')
  1786. self.session.commit()
  1787. output = self.app.get('/api/0/test/watchers')
  1788. self.assertEqual(output.status_code, 200)
  1789. expected_data = {
  1790. "total_watchers": 1,
  1791. "watchers": {
  1792. "pingou": [
  1793. "commits"
  1794. ]
  1795. }
  1796. }
  1797. self.assertDictEqual(json.loads(output.get_data(as_text=True)), expected_data)
  1798. # The owner is watching commits explicitly and foo is watching
  1799. # issues implicitly
  1800. project_user = pagure.lib.model.ProjectUser(
  1801. project_id=project.id,
  1802. user_id=2,
  1803. access='commit',
  1804. )
  1805. pagure.lib.update_watch_status(
  1806. self.session, project, 'pingou', '2')
  1807. self.session.add(project_user)
  1808. self.session.commit()
  1809. output = self.app.get('/api/0/test/watchers')
  1810. self.assertEqual(output.status_code, 200)
  1811. expected_data = {
  1812. "total_watchers": 2,
  1813. "watchers": {
  1814. "foo": ["issues"],
  1815. "pingou": ["commits"]
  1816. }
  1817. }
  1818. self.assertDictEqual(json.loads(output.get_data(as_text=True)), expected_data)
  1819. # The owner and foo are watching issues implicitly
  1820. pagure.lib.update_watch_status(
  1821. self.session, project, 'pingou', '-1')
  1822. self.session.commit()
  1823. output = self.app.get('/api/0/test/watchers')
  1824. self.assertEqual(output.status_code, 200)
  1825. expected_data = {
  1826. "total_watchers": 2,
  1827. "watchers": {
  1828. "foo": ["issues"],
  1829. "pingou": ["issues"]
  1830. }
  1831. }
  1832. self.assertDictEqual(json.loads(output.get_data(as_text=True)), expected_data)
  1833. # The owner and foo through group membership are watching issues
  1834. # implicitly
  1835. pagure.lib.update_watch_status(
  1836. self.session, project, 'pingou', '-1')
  1837. project_membership = self.session.query(
  1838. pagure.lib.model.ProjectUser).filter_by(
  1839. user_id=2, project_id=project.id).one()
  1840. self.session.delete(project_membership)
  1841. self.session.commit()
  1842. msg = pagure.lib.add_group(
  1843. self.session,
  1844. group_name='some_group',
  1845. display_name='Some Group',
  1846. description=None,
  1847. group_type='bar',
  1848. user='pingou',
  1849. is_admin=False,
  1850. blacklist=[],
  1851. )
  1852. self.session.commit()
  1853. project = pagure.lib.get_authorized_project(self.session, 'test')
  1854. group = pagure.lib.search_groups(
  1855. self.session, group_name='some_group')
  1856. pagure.lib.add_user_to_group(
  1857. self.session, 'foo', group, 'pingou', False)
  1858. pagure.lib.add_group_to_project(
  1859. self.session,
  1860. project,
  1861. new_group='some_group',
  1862. user='pingou',
  1863. access='commit',
  1864. create=False,
  1865. is_admin=True
  1866. )
  1867. self.session.commit()
  1868. output = self.app.get('/api/0/test/watchers')
  1869. self.assertEqual(output.status_code, 200)
  1870. expected_data = {
  1871. "total_watchers": 2,
  1872. "watchers": {
  1873. "@some_group": ["issues"],
  1874. "pingou": ["issues"]
  1875. }
  1876. }
  1877. self.assertDictEqual(json.loads(output.get_data(as_text=True)), expected_data)
  1878. # The owner is watching issues implicitly and foo will be watching
  1879. # commits explicitly but is in a group with commit access
  1880. pagure.lib.update_watch_status(
  1881. self.session, project, 'pingou', '-1')
  1882. pagure.lib.update_watch_status(
  1883. self.session, project, 'foo', '2')
  1884. self.session.commit()
  1885. output = self.app.get('/api/0/test/watchers')
  1886. self.assertEqual(output.status_code, 200)
  1887. expected_data = {
  1888. "total_watchers": 3,
  1889. "watchers": {
  1890. "@some_group": ["issues"],
  1891. "foo": ["commits"],
  1892. "pingou": ["issues"]
  1893. }
  1894. }
  1895. self.assertDictEqual(json.loads(output.get_data(as_text=True)), expected_data)
  1896. def test_api_new_project(self):
  1897. """ Test the api_new_project method of the flask api. """
  1898. tests.create_projects(self.session)
  1899. tests.create_projects_git(os.path.join(self.path, 'tickets'))
  1900. tests.create_tokens(self.session)
  1901. tests.create_tokens_acl(self.session)
  1902. headers = {'Authorization': 'token foo_token'}
  1903. # Invalid token
  1904. output = self.app.post('/api/0/new', headers=headers)
  1905. self.assertEqual(output.status_code, 401)
  1906. data = json.loads(output.get_data(as_text=True))
  1907. self.assertEqual(sorted(data.keys()), ['error', 'error_code'])
  1908. self.assertEqual(
  1909. pagure.api.APIERROR.EINVALIDTOK.value, data['error'])
  1910. self.assertEqual(
  1911. pagure.api.APIERROR.EINVALIDTOK.name, data['error_code'])
  1912. headers = {'Authorization': 'token aaabbbcccddd'}
  1913. # No input
  1914. output = self.app.post('/api/0/new', headers=headers)
  1915. self.assertEqual(output.status_code, 400)
  1916. data = json.loads(output.get_data(as_text=True))
  1917. self.assertDictEqual(
  1918. data,
  1919. {
  1920. "error": "Invalid or incomplete input submitted",
  1921. "error_code": "EINVALIDREQ",
  1922. "errors": {
  1923. "name": ["This field is required."],
  1924. "description": ["This field is required."]
  1925. }
  1926. }
  1927. )
  1928. data = {
  1929. 'name': 'test',
  1930. }
  1931. # Incomplete request
  1932. output = self.app.post(
  1933. '/api/0/new', data=data, headers=headers)
  1934. self.assertEqual(output.status_code, 400)
  1935. data = json.loads(output.get_data(as_text=True))
  1936. self.assertDictEqual(
  1937. data,
  1938. {
  1939. "error": "Invalid or incomplete input submitted",
  1940. "error_code": "EINVALIDREQ",
  1941. "errors": {"description": ["This field is required."]}
  1942. }
  1943. )
  1944. data = {
  1945. 'name': 'test',
  1946. 'description': 'Just a small test project',
  1947. }
  1948. # Valid request but repo already exists
  1949. output = self.app.post(
  1950. '/api/0/new/', data=data, headers=headers)
  1951. self.assertEqual(output.status_code, 400)
  1952. data = json.loads(output.get_data(as_text=True))
  1953. self.assertDictEqual(
  1954. data,
  1955. {
  1956. "error": "It is not possible to create the repo \"test\"",
  1957. "error_code": "ENOCODE"
  1958. }
  1959. )
  1960. data = {
  1961. 'name': 'test_42',
  1962. 'description': 'Just another small test project',
  1963. }
  1964. # Valid request
  1965. output = self.app.post(
  1966. '/api/0/new/', data=data, headers=headers)
  1967. self.assertEqual(output.status_code, 200)
  1968. data = json.loads(output.get_data(as_text=True))
  1969. self.assertDictEqual(
  1970. data,
  1971. {'message': 'Project "test_42" created'}
  1972. )
  1973. @patch.dict('pagure.config.config', {'PAGURE_ADMIN_USERS': ['pingou'],
  1974. 'ALLOW_ADMIN_IGNORE_EXISTING_REPOS': True})
  1975. def test_adopt_repos(self):
  1976. """ Test the new_project endpoint with existing git repo. """
  1977. # Before
  1978. projects = pagure.lib.search_projects(self.session)
  1979. self.assertEqual(len(projects), 0)
  1980. tests.create_projects_git(os.path.join(self.path, 'repos'), bare=True)
  1981. tests.add_content_git_repo(os.path.join(self.path, 'repos', 'test.git'))
  1982. item = pagure.lib.model.Token(
  1983. id='aaabbbcccddd',
  1984. user_id=1,
  1985. project_id=None,
  1986. expiration=datetime.datetime.utcnow() + datetime.timedelta(days=10)
  1987. )
  1988. self.session.add(item)
  1989. self.session.commit()
  1990. tests.create_tokens_acl(self.session)
  1991. headers = {'Authorization': 'token aaabbbcccddd'}
  1992. user = tests.FakeUser(username='pingou')
  1993. with tests.user_set(self.app.application, user):
  1994. input_data = {
  1995. 'name': 'test',
  1996. 'description': 'Project #1',
  1997. }
  1998. # Valid request
  1999. output = self.app.post(
  2000. '/api/0/new/', data=input_data, headers=headers)
  2001. self.assertEqual(output.status_code, 400)
  2002. data = json.loads(output.get_data(as_text=True))
  2003. self.assertDictEqual(
  2004. data,
  2005. {
  2006. 'error': 'The main repo test.git already exists',
  2007. 'error_code': 'ENOCODE'
  2008. }
  2009. )
  2010. input_data['ignore_existing_repos'] = 'y'
  2011. # Valid request
  2012. output = self.app.post(
  2013. '/api/0/new/', data=input_data, headers=headers)
  2014. self.assertEqual(output.status_code, 200)
  2015. data = json.loads(output.get_data(as_text=True))
  2016. self.assertDictEqual(
  2017. data,
  2018. {'message': 'Project "test" created'}
  2019. )
  2020. @patch.dict('pagure.config.config', {'PRIVATE_PROJECTS': True})
  2021. def test_api_new_project_private(self):
  2022. """ Test the api_new_project method of the flask api to create
  2023. a private project. """
  2024. tests.create_projects(self.session)
  2025. tests.create_projects_git(os.path.join(self.path, 'tickets'))
  2026. tests.create_tokens(self.session)
  2027. tests.create_tokens_acl(self.session)
  2028. headers = {'Authorization': 'token aaabbbcccddd'}
  2029. data = {
  2030. 'name': 'test',
  2031. 'description': 'Just a small test project',
  2032. 'private': True,
  2033. }
  2034. # Valid request
  2035. output = self.app.post(
  2036. '/api/0/new/', data=data, headers=headers)
  2037. self.assertEqual(output.status_code, 200)
  2038. data = json.loads(output.get_data(as_text=True))
  2039. self.assertDictEqual(
  2040. data,
  2041. {'message': 'Project "pingou/test" created'}
  2042. )
  2043. def test_api_new_project_user_token(self):
  2044. """ Test the api_new_project method of the flask api. """
  2045. tests.create_projects(self.session)
  2046. tests.create_projects_git(os.path.join(self.path, 'tickets'))
  2047. tests.create_tokens(self.session, project_id=None)
  2048. tests.create_tokens_acl(self.session)
  2049. headers = {'Authorization': 'token foo_token'}
  2050. # Invalid token
  2051. output = self.app.post('/api/0/new', headers=headers)
  2052. self.assertEqual(output.status_code, 401)
  2053. data = json.loads(output.get_data(as_text=True))
  2054. self.assertEqual(sorted(data.keys()), ['error', 'error_code'])
  2055. self.assertEqual(
  2056. pagure.api.APIERROR.EINVALIDTOK.value, data['error'])
  2057. self.assertEqual(
  2058. pagure.api.APIERROR.EINVALIDTOK.name, data['error_code'])
  2059. headers = {'Authorization': 'token aaabbbcccddd'}
  2060. # No input
  2061. output = self.app.post('/api/0/new', headers=headers)
  2062. self.assertEqual(output.status_code, 400)
  2063. data = json.loads(output.get_data(as_text=True))
  2064. self.assertDictEqual(
  2065. data,
  2066. {
  2067. "error": "Invalid or incomplete input submitted",
  2068. "error_code": "EINVALIDREQ",
  2069. "errors": {
  2070. "name": ["This field is required."],
  2071. "description": ["This field is required."]
  2072. }
  2073. }
  2074. )
  2075. data = {
  2076. 'name': 'test',
  2077. }
  2078. # Incomplete request
  2079. output = self.app.post(
  2080. '/api/0/new', data=data, headers=headers)
  2081. self.assertEqual(output.status_code, 400)
  2082. data = json.loads(output.get_data(as_text=True))
  2083. self.assertDictEqual(
  2084. data,
  2085. {
  2086. "error": "Invalid or incomplete input submitted",
  2087. "error_code": "EINVALIDREQ",
  2088. "errors": {"description": ["This field is required."]}
  2089. }
  2090. )
  2091. data = {
  2092. 'name': 'test',
  2093. 'description': 'Just a small test project',
  2094. }
  2095. # Valid request but repo already exists
  2096. output = self.app.post(
  2097. '/api/0/new/', data=data, headers=headers)
  2098. self.assertEqual(output.status_code, 400)
  2099. data = json.loads(output.get_data(as_text=True))
  2100. self.assertDictEqual(
  2101. data,
  2102. {
  2103. "error": "It is not possible to create the repo \"test\"",
  2104. "error_code": "ENOCODE"
  2105. }
  2106. )
  2107. data = {
  2108. 'name': 'test_42',
  2109. 'description': 'Just another small test project',
  2110. }
  2111. # Valid request
  2112. output = self.app.post(
  2113. '/api/0/new/', data=data, headers=headers)
  2114. self.assertEqual(output.status_code, 200)
  2115. data = json.loads(output.get_data(as_text=True))
  2116. self.assertDictEqual(
  2117. data,
  2118. {'message': 'Project "test_42" created'}
  2119. )
  2120. # Project with a namespace
  2121. pagure.config.config['ALLOWED_PREFIX'] = ['rpms']
  2122. data = {
  2123. 'name': 'test_42',
  2124. 'namespace': 'pingou',
  2125. 'description': 'Just another small test project',
  2126. }
  2127. # Invalid namespace
  2128. output = self.app.post(
  2129. '/api/0/new/', data=data, headers=headers)
  2130. self.assertEqual(output.status_code, 400)
  2131. data = json.loads(output.get_data(as_text=True))
  2132. self.assertDictEqual(
  2133. data,
  2134. {
  2135. "error": "Invalid or incomplete input submitted",
  2136. "error_code": "EINVALIDREQ",
  2137. "errors": {
  2138. "namespace": [
  2139. "Not a valid choice"
  2140. ]
  2141. }
  2142. }
  2143. )
  2144. data = {
  2145. 'name': 'test_42',
  2146. 'namespace': 'rpms',
  2147. 'description': 'Just another small test project',
  2148. }
  2149. # All good
  2150. output = self.app.post(
  2151. '/api/0/new/', data=data, headers=headers)
  2152. self.assertEqual(output.status_code, 200)
  2153. data = json.loads(output.get_data(as_text=True))
  2154. self.assertDictEqual(
  2155. data,
  2156. {'message': 'Project "rpms/test_42" created'}
  2157. )
  2158. @patch.dict('pagure.config.config', {'USER_NAMESPACE': True})
  2159. def test_api_new_project_user_ns(self):
  2160. """ Test the api_new_project method of the flask api. """
  2161. tests.create_projects(self.session)
  2162. tests.create_projects_git(os.path.join(self.path, 'tickets'))
  2163. tests.create_tokens(self.session)
  2164. tests.create_tokens_acl(self.session)
  2165. headers = {'Authorization': 'token aaabbbcccddd'}
  2166. # Create a project with the user namespace feature on
  2167. data = {
  2168. 'name': 'testproject',
  2169. 'description': 'Just another small test project',
  2170. }
  2171. # Valid request
  2172. output = self.app.post(
  2173. '/api/0/new/', data=data, headers=headers)
  2174. self.assertEqual(output.status_code, 200)
  2175. data = json.loads(output.get_data(as_text=True))
  2176. self.assertDictEqual(
  2177. data,
  2178. {'message': 'Project "pingou/testproject" created'}
  2179. )
  2180. # Create a project with a namespace and the user namespace feature on
  2181. data = {
  2182. 'name': 'testproject2',
  2183. 'namespace': 'testns',
  2184. 'description': 'Just another small test project',
  2185. }
  2186. # Valid request
  2187. with patch.dict('pagure.config.config', {'ALLOWED_PREFIX': ['testns']}):
  2188. output = self.app.post(
  2189. '/api/0/new/', data=data, headers=headers)
  2190. self.assertEqual(output.status_code, 200)
  2191. data = json.loads(output.get_data(as_text=True))
  2192. self.assertDictEqual(
  2193. data,
  2194. {'message': 'Project "testns/testproject2" created'}
  2195. )
  2196. def test_api_fork_project(self):
  2197. """ Test the api_fork_project method of the flask api. """
  2198. tests.create_projects(self.session)
  2199. for folder in ['docs', 'tickets', 'requests', 'repos']:
  2200. tests.create_projects_git(
  2201. os.path.join(self.path, folder), bare=True)
  2202. tests.create_tokens(self.session)
  2203. tests.create_tokens_acl(self.session)
  2204. headers = {'Authorization': 'token foo_token'}
  2205. # Invalid token
  2206. output = self.app.post('/api/0/fork', headers=headers)
  2207. self.assertEqual(output.status_code, 401)
  2208. data = json.loads(output.get_data(as_text=True))
  2209. self.assertEqual(sorted(data.keys()), ['error', 'error_code'])
  2210. self.assertEqual(
  2211. pagure.api.APIERROR.EINVALIDTOK.value, data['error'])
  2212. self.assertEqual(
  2213. pagure.api.APIERROR.EINVALIDTOK.name, data['error_code'])
  2214. headers = {'Authorization': 'token aaabbbcccddd'}
  2215. # No input
  2216. output = self.app.post('/api/0/fork', headers=headers)
  2217. self.assertEqual(output.status_code, 400)
  2218. data = json.loads(output.get_data(as_text=True))
  2219. self.assertDictEqual(
  2220. data,
  2221. {
  2222. "error": "Invalid or incomplete input submitted",
  2223. "error_code": "EINVALIDREQ",
  2224. "errors": {"repo": ["This field is required."]}
  2225. }
  2226. )
  2227. data = {
  2228. 'name': 'test',
  2229. }
  2230. # Incomplete request
  2231. output = self.app.post(
  2232. '/api/0/fork', data=data, headers=headers)
  2233. self.assertEqual(output.status_code, 400)
  2234. data = json.loads(output.get_data(as_text=True))
  2235. self.assertDictEqual(
  2236. data,
  2237. {
  2238. "error": "Invalid or incomplete input submitted",
  2239. "error_code": "EINVALIDREQ",
  2240. "errors": {"repo": ["This field is required."]}
  2241. }
  2242. )
  2243. data = {
  2244. 'repo': 'test',
  2245. }
  2246. # Valid request
  2247. output = self.app.post(
  2248. '/api/0/fork/', data=data, headers=headers)
  2249. self.assertEqual(output.status_code, 200)
  2250. data = json.loads(output.get_data(as_text=True))
  2251. self.assertDictEqual(
  2252. data,
  2253. {
  2254. "message": "Repo \"test\" cloned to \"pingou/test\""
  2255. }
  2256. )
  2257. data = {
  2258. 'repo': 'test',
  2259. }
  2260. # project already forked
  2261. output = self.app.post(
  2262. '/api/0/fork/', data=data, headers=headers)
  2263. self.assertEqual(output.status_code, 400)
  2264. data = json.loads(output.get_data(as_text=True))
  2265. self.assertDictEqual(
  2266. data,
  2267. {
  2268. "error": "Repo \"forks/pingou/test\" already exists",
  2269. "error_code": "ENOCODE"
  2270. }
  2271. )
  2272. data = {
  2273. 'repo': 'test',
  2274. 'username': 'pingou',
  2275. }
  2276. # Fork already exists
  2277. output = self.app.post(
  2278. '/api/0/fork/', data=data, headers=headers)
  2279. self.assertEqual(output.status_code, 400)
  2280. data = json.loads(output.get_data(as_text=True))
  2281. self.assertDictEqual(
  2282. data,
  2283. {
  2284. "error": "Repo \"forks/pingou/test\" already exists",
  2285. "error_code": "ENOCODE"
  2286. }
  2287. )
  2288. data = {
  2289. 'repo': 'test',
  2290. 'namespace': 'pingou',
  2291. }
  2292. # Repo does not exists
  2293. output = self.app.post(
  2294. '/api/0/fork/', data=data, headers=headers)
  2295. self.assertEqual(output.status_code, 404)
  2296. data = json.loads(output.get_data(as_text=True))
  2297. self.assertDictEqual(
  2298. data,
  2299. {
  2300. "error": "Project not found",
  2301. "error_code": "ENOPROJECT"
  2302. }
  2303. )
  2304. def test_api_fork_project_user_token(self):
  2305. """ Test the api_fork_project method of the flask api. """
  2306. tests.create_projects(self.session)
  2307. for folder in ['docs', 'tickets', 'requests', 'repos']:
  2308. tests.create_projects_git(
  2309. os.path.join(self.path, folder), bare=True)
  2310. tests.create_tokens(self.session, project_id=None)
  2311. tests.create_tokens_acl(self.session)
  2312. headers = {'Authorization': 'token foo_token'}
  2313. # Invalid token
  2314. output = self.app.post('/api/0/fork', headers=headers)
  2315. self.assertEqual(output.status_code, 401)
  2316. data = json.loads(output.get_data(as_text=True))
  2317. self.assertEqual(sorted(data.keys()), ['error', 'error_code'])
  2318. self.assertEqual(
  2319. pagure.api.APIERROR.EINVALIDTOK.value, data['error'])
  2320. self.assertEqual(
  2321. pagure.api.APIERROR.EINVALIDTOK.name, data['error_code'])
  2322. headers = {'Authorization': 'token aaabbbcccddd'}
  2323. # No input
  2324. output = self.app.post('/api/0/fork', headers=headers)
  2325. self.assertEqual(output.status_code, 400)
  2326. data = json.loads(output.get_data(as_text=True))
  2327. self.assertDictEqual(
  2328. data,
  2329. {
  2330. "error": "Invalid or incomplete input submitted",
  2331. "error_code": "EINVALIDREQ",
  2332. "errors": {"repo": ["This field is required."]}
  2333. }
  2334. )
  2335. data = {
  2336. 'name': 'test',
  2337. }
  2338. # Incomplete request
  2339. output = self.app.post(
  2340. '/api/0/fork', data=data, headers=headers)
  2341. self.assertEqual(output.status_code, 400)
  2342. data = json.loads(output.get_data(as_text=True))
  2343. self.assertDictEqual(
  2344. data,
  2345. {
  2346. "error": "Invalid or incomplete input submitted",
  2347. "error_code": "EINVALIDREQ",
  2348. "errors": {"repo": ["This field is required."]}
  2349. }
  2350. )
  2351. data = {
  2352. 'repo': 'test',
  2353. }
  2354. # Valid request
  2355. output = self.app.post(
  2356. '/api/0/fork/', data=data, headers=headers)
  2357. self.assertEqual(output.status_code, 200)
  2358. data = json.loads(output.get_data(as_text=True))
  2359. self.assertDictEqual(
  2360. data,
  2361. {
  2362. "message": "Repo \"test\" cloned to \"pingou/test\""
  2363. }
  2364. )
  2365. data = {
  2366. 'repo': 'test',
  2367. }
  2368. # project already forked
  2369. output = self.app.post(
  2370. '/api/0/fork/', data=data, headers=headers)
  2371. self.assertEqual(output.status_code, 400)
  2372. data = json.loads(output.get_data(as_text=True))
  2373. self.assertDictEqual(
  2374. data,
  2375. {
  2376. "error": "Repo \"forks/pingou/test\" already exists",
  2377. "error_code": "ENOCODE"
  2378. }
  2379. )
  2380. data = {
  2381. 'repo': 'test',
  2382. 'username': 'pingou',
  2383. }
  2384. # Fork already exists
  2385. output = self.app.post(
  2386. '/api/0/fork/', data=data, headers=headers)
  2387. self.assertEqual(output.status_code, 400)
  2388. data = json.loads(output.get_data(as_text=True))
  2389. self.assertDictEqual(
  2390. data,
  2391. {
  2392. "error": "Repo \"forks/pingou/test\" already exists",
  2393. "error_code": "ENOCODE"
  2394. }
  2395. )
  2396. data = {
  2397. 'repo': 'test',
  2398. 'namespace': 'pingou',
  2399. }
  2400. # Repo does not exists
  2401. output = self.app.post(
  2402. '/api/0/fork/', data=data, headers=headers)
  2403. self.assertEqual(output.status_code, 404)
  2404. data = json.loads(output.get_data(as_text=True))
  2405. self.assertDictEqual(
  2406. data,
  2407. {
  2408. "error": "Project not found",
  2409. "error_code": "ENOPROJECT"
  2410. }
  2411. )
  2412. def test_api_generate_acls(self):
  2413. """ Test the api_generate_acls method of the flask api """
  2414. tests.create_projects(self.session)
  2415. tests.create_tokens(self.session, project_id=None)
  2416. tests.create_tokens_acl(
  2417. self.session, 'aaabbbcccddd', 'generate_acls_project')
  2418. headers = {'Authorization': 'token aaabbbcccddd'}
  2419. user = pagure.lib.get_user(self.session, 'pingou')
  2420. output = self.app.post(
  2421. '/api/0/test/git/generateacls', headers=headers,
  2422. data={'wait': False})
  2423. self.assertEqual(output.status_code, 200)
  2424. data = json.loads(output.get_data(as_text=True))
  2425. expected_output = {
  2426. 'message': 'Project ACL generation queued',
  2427. 'taskid': 'abc-1234'
  2428. }
  2429. self.assertEqual(data, expected_output)
  2430. self.mock_gen_acls.assert_called_once_with(
  2431. name='test', namespace=None, user=None, group=None)
  2432. def test_api_generate_acls_json(self):
  2433. """ Test the api_generate_acls method of the flask api using JSON """
  2434. tests.create_projects(self.session)
  2435. tests.create_tokens(self.session, project_id=None)
  2436. tests.create_tokens_acl(
  2437. self.session, 'aaabbbcccddd', 'generate_acls_project')
  2438. headers = {'Authorization': 'token aaabbbcccddd',
  2439. 'Content-Type': 'application/json'}
  2440. user = pagure.lib.get_user(self.session, 'pingou')
  2441. output = self.app.post(
  2442. '/api/0/test/git/generateacls', headers=headers,
  2443. data=json.dumps({'wait': False}))
  2444. self.assertEqual(output.status_code, 200)
  2445. data = json.loads(output.get_data(as_text=True))
  2446. expected_output = {
  2447. 'message': 'Project ACL generation queued',
  2448. 'taskid': 'abc-1234'
  2449. }
  2450. self.assertEqual(data, expected_output)
  2451. self.mock_gen_acls.assert_called_once_with(
  2452. name='test', namespace=None, user=None, group=None)
  2453. def test_api_generate_acls_wait_true(self):
  2454. """ Test the api_generate_acls method of the flask api when wait is
  2455. set to True """
  2456. tests.create_projects(self.session)
  2457. tests.create_tokens(self.session, project_id=None)
  2458. tests.create_tokens_acl(
  2459. self.session, 'aaabbbcccddd', 'generate_acls_project')
  2460. headers = {'Authorization': 'token aaabbbcccddd'}
  2461. task_result = Mock()
  2462. task_result.id = 'abc-1234'
  2463. self.mock_gen_acls.return_value = task_result
  2464. user = pagure.lib.get_user(self.session, 'pingou')
  2465. output = self.app.post(
  2466. '/api/0/test/git/generateacls', headers=headers,
  2467. data={'wait': True})
  2468. self.assertEqual(output.status_code, 200)
  2469. data = json.loads(output.get_data(as_text=True))
  2470. expected_output = {
  2471. 'message': 'Project ACLs generated',
  2472. }
  2473. self.assertEqual(data, expected_output)
  2474. self.mock_gen_acls.assert_called_once_with(
  2475. name='test', namespace=None, user=None, group=None)
  2476. self.assertTrue(task_result.get.called)
  2477. def test_api_generate_acls_no_project(self):
  2478. """ Test the api_generate_acls method of the flask api when the project
  2479. doesn't exist """
  2480. tests.create_projects(self.session)
  2481. tests.create_tokens(self.session, project_id=None)
  2482. tests.create_tokens_acl(
  2483. self.session, 'aaabbbcccddd', 'generate_acls_project')
  2484. headers = {'Authorization': 'token aaabbbcccddd'}
  2485. user = pagure.lib.get_user(self.session, 'pingou')
  2486. output = self.app.post(
  2487. '/api/0/test12345123/git/generateacls', headers=headers,
  2488. data={'wait': False})
  2489. self.assertEqual(output.status_code, 404)
  2490. data = json.loads(output.get_data(as_text=True))
  2491. expected_output = {
  2492. 'error_code': 'ENOPROJECT',
  2493. 'error': 'Project not found'
  2494. }
  2495. self.assertEqual(data, expected_output)
  2496. def test_api_new_git_branch(self):
  2497. """ Test the api_new_branch method of the flask api """
  2498. tests.create_projects(self.session)
  2499. repo_path = os.path.join(self.path, 'repos')
  2500. tests.create_projects_git(repo_path, bare=True)
  2501. tests.add_content_git_repo(os.path.join(repo_path, 'test.git'))
  2502. tests.create_tokens(self.session, project_id=None)
  2503. tests.create_tokens_acl(
  2504. self.session, 'aaabbbcccddd', 'create_branch')
  2505. headers = {'Authorization': 'token aaabbbcccddd'}
  2506. args = {'branch': 'test123'}
  2507. output = self.app.post('/api/0/test/git/branch', headers=headers,
  2508. data=args)
  2509. self.assertEqual(output.status_code, 200)
  2510. data = json.loads(output.get_data(as_text=True))
  2511. expected_output = {
  2512. 'message': 'Project branch was created',
  2513. }
  2514. self.assertEqual(data, expected_output)
  2515. git_path = os.path.join(self.path, 'repos', 'test.git')
  2516. repo_obj = pygit2.Repository(git_path)
  2517. self.assertIn('test123', repo_obj.listall_branches())
  2518. def test_api_new_git_branch_json(self):
  2519. """ Test the api_new_branch method of the flask api """
  2520. tests.create_projects(self.session)
  2521. repo_path = os.path.join(self.path, 'repos')
  2522. tests.create_projects_git(repo_path, bare=True)
  2523. tests.add_content_git_repo(os.path.join(repo_path, 'test.git'))
  2524. tests.create_tokens(self.session, project_id=None)
  2525. tests.create_tokens_acl(
  2526. self.session, 'aaabbbcccddd', 'create_branch')
  2527. headers = {'Authorization': 'token aaabbbcccddd',
  2528. 'Content-Type': 'application/json'}
  2529. args = {'branch': 'test123'}
  2530. output = self.app.post('/api/0/test/git/branch', headers=headers,
  2531. data=json.dumps(args))
  2532. self.assertEqual(output.status_code, 200)
  2533. data = json.loads(output.get_data(as_text=True))
  2534. expected_output = {
  2535. 'message': 'Project branch was created',
  2536. }
  2537. self.assertEqual(data, expected_output)
  2538. git_path = os.path.join(self.path, 'repos', 'test.git')
  2539. repo_obj = pygit2.Repository(git_path)
  2540. self.assertIn('test123', repo_obj.listall_branches())
  2541. def test_api_new_git_branch_from_branch(self):
  2542. """ Test the api_new_branch method of the flask api """
  2543. tests.create_projects(self.session)
  2544. repo_path = os.path.join(self.path, 'repos')
  2545. tests.create_projects_git(repo_path, bare=True)
  2546. tests.add_content_git_repo(os.path.join(repo_path, 'test.git'))
  2547. tests.create_tokens(self.session, project_id=None)
  2548. tests.create_tokens_acl(
  2549. self.session, 'aaabbbcccddd', 'create_branch')
  2550. git_path = os.path.join(self.path, 'repos', 'test.git')
  2551. repo_obj = pygit2.Repository(git_path)
  2552. parent = pagure.lib.git.get_branch_ref(repo_obj, 'master').get_object()
  2553. repo_obj.create_branch('dev123', parent)
  2554. headers = {'Authorization': 'token aaabbbcccddd'}
  2555. args = {'branch': 'test123', 'from_branch': 'dev123'}
  2556. output = self.app.post('/api/0/test/git/branch', headers=headers,
  2557. data=args)
  2558. self.assertEqual(output.status_code, 200)
  2559. data = json.loads(output.get_data(as_text=True))
  2560. expected_output = {
  2561. 'message': 'Project branch was created',
  2562. }
  2563. self.assertEqual(data, expected_output)
  2564. self.assertIn('test123', repo_obj.listall_branches())
  2565. def test_api_new_git_branch_already_exists(self):
  2566. """ Test the api_new_branch method of the flask api when branch already
  2567. exists """
  2568. tests.create_projects(self.session)
  2569. repo_path = os.path.join(self.path, 'repos')
  2570. tests.create_projects_git(repo_path, bare=True)
  2571. tests.add_content_git_repo(os.path.join(repo_path, 'test.git'))
  2572. tests.create_tokens(self.session, project_id=None)
  2573. tests.create_tokens_acl(
  2574. self.session, 'aaabbbcccddd', 'create_branch')
  2575. headers = {'Authorization': 'token aaabbbcccddd'}
  2576. args = {'branch': 'master'}
  2577. output = self.app.post('/api/0/test/git/branch', headers=headers,
  2578. data=args)
  2579. self.assertEqual(output.status_code, 400)
  2580. data = json.loads(output.get_data(as_text=True))
  2581. expected_output = {
  2582. 'error': 'The branch "master" already exists',
  2583. 'error_code': 'ENOCODE'
  2584. }
  2585. self.assertEqual(data, expected_output)
  2586. def test_api_new_git_branch_from_commit(self):
  2587. """ Test the api_new_branch method of the flask api """
  2588. tests.create_projects(self.session)
  2589. repos_path = os.path.join(self.path, 'repos')
  2590. tests.create_projects_git(repos_path, bare=True)
  2591. git_path = os.path.join(repos_path, 'test.git')
  2592. tests.add_content_git_repo(git_path)
  2593. tests.create_tokens(self.session, project_id=None)
  2594. tests.create_tokens_acl(
  2595. self.session, 'aaabbbcccddd', 'create_branch')
  2596. repo_obj = pygit2.Repository(git_path)
  2597. from_commit = repo_obj.revparse_single('HEAD').oid.hex
  2598. headers = {'Authorization': 'token aaabbbcccddd'}
  2599. args = {'branch': 'test123', 'from_commit': from_commit}
  2600. output = self.app.post('/api/0/test/git/branch', headers=headers,
  2601. data=args)
  2602. self.assertEqual(output.status_code, 200)
  2603. data = json.loads(output.get_data(as_text=True))
  2604. expected_output = {
  2605. 'message': 'Project branch was created',
  2606. }
  2607. self.assertEqual(data, expected_output)
  2608. self.assertIn('test123', repo_obj.listall_branches())
  2609. class PagureFlaskApiProjectFlagtests(tests.Modeltests):
  2610. """ Tests for the flask API of pagure for flagging commit in project
  2611. """
  2612. def setUp(self):
  2613. """ Set up the environnment, ran before every tests. """
  2614. super(PagureFlaskApiProjectFlagtests, self).setUp()
  2615. tests.create_projects(self.session)
  2616. repo_path = os.path.join(self.path, 'repos')
  2617. self.git_path = os.path.join(repo_path, 'test.git')
  2618. tests.create_projects_git(repo_path, bare=True)
  2619. tests.add_content_git_repo(self.git_path)
  2620. tests.create_tokens(self.session, project_id=None)
  2621. tests.create_tokens_acl(
  2622. self.session, 'aaabbbcccddd', 'commit_flag')
  2623. def test_flag_commit_missing_status(self):
  2624. """ Test flagging a commit with missing precentage. """
  2625. repo_obj = pygit2.Repository(self.git_path)
  2626. commit = repo_obj.revparse_single('HEAD')
  2627. headers = {'Authorization': 'token aaabbbcccddd'}
  2628. data = {
  2629. 'username': 'Jenkins',
  2630. 'comment': 'Tests passed',
  2631. 'url': 'http://jenkins.cloud.fedoraproject.org/',
  2632. 'uid': 'jenkins_build_pagure_100+seed',
  2633. }
  2634. output = self.app.post(
  2635. '/api/0/test/c/%s/flag' % commit.oid.hex,
  2636. headers=headers, data=data)
  2637. self.assertEqual(output.status_code, 400)
  2638. data = json.loads(output.get_data(as_text=True))
  2639. expected_output = {
  2640. "error": "Invalid or incomplete input submitted",
  2641. "error_code": "EINVALIDREQ",
  2642. "errors": {
  2643. "status": [
  2644. "Not a valid choice"
  2645. ]
  2646. }
  2647. }
  2648. self.assertEqual(data, expected_output)
  2649. def test_flag_commit_missing_username(self):
  2650. """ Test flagging a commit with missing username. """
  2651. repo_obj = pygit2.Repository(self.git_path)
  2652. commit = repo_obj.revparse_single('HEAD')
  2653. headers = {'Authorization': 'token aaabbbcccddd'}
  2654. data = {
  2655. 'percent': 100,
  2656. 'comment': 'Tests passed',
  2657. 'url': 'http://jenkins.cloud.fedoraproject.org/',
  2658. 'uid': 'jenkins_build_pagure_100+seed',
  2659. 'status': 'success',
  2660. }
  2661. output = self.app.post(
  2662. '/api/0/test/c/%s/flag' % commit.oid.hex,
  2663. headers=headers, data=data)
  2664. self.assertEqual(output.status_code, 400)
  2665. data = json.loads(output.get_data(as_text=True))
  2666. expected_output = {
  2667. "error": "Invalid or incomplete input submitted",
  2668. "error_code": "EINVALIDREQ",
  2669. "errors": {
  2670. "username": [
  2671. "This field is required."
  2672. ]
  2673. }
  2674. }
  2675. self.assertEqual(data, expected_output)
  2676. def test_flag_commit_missing_comment(self):
  2677. """ Test flagging a commit with missing comment. """
  2678. repo_obj = pygit2.Repository(self.git_path)
  2679. commit = repo_obj.revparse_single('HEAD')
  2680. headers = {'Authorization': 'token aaabbbcccddd'}
  2681. data = {
  2682. 'username': 'Jenkins',
  2683. 'percent': 100,
  2684. 'url': 'http://jenkins.cloud.fedoraproject.org/',
  2685. 'uid': 'jenkins_build_pagure_100+seed',
  2686. 'status': 'success',
  2687. }
  2688. output = self.app.post(
  2689. '/api/0/test/c/%s/flag' % commit.oid.hex,
  2690. headers=headers, data=data)
  2691. self.assertEqual(output.status_code, 400)
  2692. data = json.loads(output.get_data(as_text=True))
  2693. expected_output = {
  2694. "error": "Invalid or incomplete input submitted",
  2695. "error_code": "EINVALIDREQ",
  2696. "errors": {
  2697. "comment": [
  2698. "This field is required."
  2699. ]
  2700. }
  2701. }
  2702. self.assertEqual(data, expected_output)
  2703. def test_flag_commit_missing_url(self):
  2704. """ Test flagging a commit with missing url. """
  2705. repo_obj = pygit2.Repository(self.git_path)
  2706. commit = repo_obj.revparse_single('HEAD')
  2707. headers = {'Authorization': 'token aaabbbcccddd'}
  2708. data = {
  2709. 'username': 'Jenkins',
  2710. 'percent': 100,
  2711. 'comment': 'Tests passed',
  2712. 'uid': 'jenkins_build_pagure_100+seed',
  2713. 'status': 'success',
  2714. }
  2715. output = self.app.post(
  2716. '/api/0/test/c/%s/flag' % commit.oid.hex,
  2717. headers=headers, data=data)
  2718. self.assertEqual(output.status_code, 400)
  2719. data = json.loads(output.get_data(as_text=True))
  2720. expected_output = {
  2721. "error": "Invalid or incomplete input submitted",
  2722. "error_code": "EINVALIDREQ",
  2723. "errors": {
  2724. "url": [
  2725. "This field is required."
  2726. ]
  2727. }
  2728. }
  2729. self.assertEqual(data, expected_output)
  2730. def test_flag_commit_invalid_token(self):
  2731. """ Test flagging a commit with missing info. """
  2732. repo_obj = pygit2.Repository(self.git_path)
  2733. commit = repo_obj.revparse_single('HEAD')
  2734. headers = {'Authorization': 'token 123'}
  2735. data = {
  2736. 'username': 'Jenkins',
  2737. 'percent': 100,
  2738. 'comment': 'Tests passed',
  2739. 'url': 'http://jenkins.cloud.fedoraproject.org/',
  2740. 'uid': 'jenkins_build_pagure_100+seed',
  2741. }
  2742. output = self.app.post(
  2743. '/api/0/test/c/%s/flag' % commit.oid.hex,
  2744. headers=headers, data=data)
  2745. self.assertEqual(output.status_code, 401)
  2746. data = json.loads(output.get_data(as_text=True))
  2747. self.assertEqual(sorted(data.keys()), ['error', 'error_code'])
  2748. self.assertEqual(
  2749. pagure.api.APIERROR.EINVALIDTOK.value, data['error'])
  2750. self.assertEqual(
  2751. pagure.api.APIERROR.EINVALIDTOK.name, data['error_code'])
  2752. def test_flag_commit_invalid_status(self):
  2753. """ Test flagging a commit with an invalid status. """
  2754. repo_obj = pygit2.Repository(self.git_path)
  2755. commit = repo_obj.revparse_single('HEAD')
  2756. headers = {'Authorization': 'token aaabbbcccddd'}
  2757. data = {
  2758. 'username': 'Jenkins',
  2759. 'percent': 100,
  2760. 'comment': 'Tests passed',
  2761. 'url': 'http://jenkins.cloud.fedoraproject.org/',
  2762. 'status': 'foobar',
  2763. }
  2764. output = self.app.post(
  2765. '/api/0/test/c/%s/flag' % commit.oid.hex,
  2766. headers=headers, data=data)
  2767. self.assertEqual(output.status_code, 400)
  2768. data = json.loads(output.get_data(as_text=True))
  2769. self.assertEqual(
  2770. data,
  2771. {
  2772. 'errors': {'status': ['Not a valid choice']},
  2773. 'error_code': 'EINVALIDREQ',
  2774. 'error': 'Invalid or incomplete input submitted'
  2775. }
  2776. )
  2777. def test_flag_commit_with_uid(self):
  2778. """ Test flagging a commit with provided uid. """
  2779. repo_obj = pygit2.Repository(self.git_path)
  2780. commit = repo_obj.revparse_single('HEAD')
  2781. headers = {'Authorization': 'token aaabbbcccddd'}
  2782. data = {
  2783. 'username': 'Jenkins',
  2784. 'percent': 100,
  2785. 'comment': 'Tests passed',
  2786. 'url': 'http://jenkins.cloud.fedoraproject.org/',
  2787. 'uid': 'jenkins_build_pagure_100+seed',
  2788. 'status': 'success',
  2789. }
  2790. output = self.app.post(
  2791. '/api/0/test/c/%s/flag' % commit.oid.hex,
  2792. headers=headers, data=data)
  2793. self.assertEqual(output.status_code, 200)
  2794. data = json.loads(output.get_data(as_text=True))
  2795. data['flag']['date_created'] = '1510742565'
  2796. data['flag']['commit_hash'] = '62b49f00d489452994de5010565fab81'
  2797. expected_output = {
  2798. 'flag': {
  2799. 'comment': 'Tests passed',
  2800. 'commit_hash': '62b49f00d489452994de5010565fab81',
  2801. 'date_created': '1510742565',
  2802. 'percent': 100,
  2803. 'status': 'success',
  2804. 'url': 'http://jenkins.cloud.fedoraproject.org/',
  2805. 'user': {
  2806. 'default_email': 'bar@pingou.com',
  2807. 'emails': ['bar@pingou.com', 'foo@pingou.com'],
  2808. 'fullname': 'PY C',
  2809. 'name': 'pingou'},
  2810. 'username': 'Jenkins'
  2811. },
  2812. 'message': 'Flag added',
  2813. 'uid': 'jenkins_build_pagure_100+seed'
  2814. }
  2815. self.assertEqual(data, expected_output)
  2816. @patch('pagure.lib.notify.send_email')
  2817. def test_flag_commit_without_uid(self, mock_email):
  2818. """ Test flagging a commit with missing info.
  2819. Also ensure notifications aren't sent when they are not asked for.
  2820. """
  2821. repo_obj = pygit2.Repository(self.git_path)
  2822. commit = repo_obj.revparse_single('HEAD')
  2823. headers = {'Authorization': 'token aaabbbcccddd'}
  2824. data = {
  2825. 'username': 'Jenkins',
  2826. 'percent': 100,
  2827. 'comment': 'Tests passed',
  2828. 'url': 'http://jenkins.cloud.fedoraproject.org/',
  2829. 'status': 'success',
  2830. }
  2831. output = self.app.post(
  2832. '/api/0/test/c/%s/flag' % commit.oid.hex,
  2833. headers=headers, data=data)
  2834. self.assertEqual(output.status_code, 200)
  2835. data = json.loads(output.get_data(as_text=True))
  2836. self.assertNotEqual(
  2837. data['uid'],
  2838. 'jenkins_build_pagure_100+seed'
  2839. )
  2840. data['flag']['date_created'] = '1510742565'
  2841. data['uid'] = 'b1de8f80defd4a81afe2e09f39678087'
  2842. expected_output = {
  2843. 'flag': {
  2844. 'comment': 'Tests passed',
  2845. 'commit_hash': commit.oid.hex,
  2846. 'date_created': '1510742565',
  2847. 'percent': 100,
  2848. 'status': 'success',
  2849. 'url': 'http://jenkins.cloud.fedoraproject.org/',
  2850. 'user': {
  2851. 'default_email': 'bar@pingou.com',
  2852. 'emails': ['bar@pingou.com', 'foo@pingou.com'],
  2853. 'fullname': 'PY C',
  2854. 'name': 'pingou'},
  2855. 'username': 'Jenkins'
  2856. },
  2857. 'message': 'Flag added',
  2858. 'uid': 'b1de8f80defd4a81afe2e09f39678087'
  2859. }
  2860. self.assertEqual(data, expected_output)
  2861. mock_email.assert_not_called()
  2862. @patch('pagure.lib.notify.send_email')
  2863. def test_flag_commit_with_notification(self, mock_email):
  2864. """ Test flagging a commit with notification enabled. """
  2865. # Enable commit notifications
  2866. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2867. settings = repo.settings
  2868. settings['notify_on_commit_flag'] = True
  2869. repo.settings = settings
  2870. self.session.add(repo)
  2871. self.session.commit()
  2872. repo_obj = pygit2.Repository(self.git_path)
  2873. commit = repo_obj.revparse_single('HEAD')
  2874. headers = {'Authorization': 'token aaabbbcccddd'}
  2875. data = {
  2876. 'username': 'Jenkins',
  2877. 'percent': 100,
  2878. 'comment': 'Tests passed',
  2879. 'url': 'http://jenkins.cloud.fedoraproject.org/',
  2880. 'status': 'success',
  2881. }
  2882. output = self.app.post(
  2883. '/api/0/test/c/%s/flag' % commit.oid.hex,
  2884. headers=headers, data=data)
  2885. self.assertEqual(output.status_code, 200)
  2886. data = json.loads(output.get_data(as_text=True))
  2887. self.assertNotEqual(
  2888. data['uid'],
  2889. 'jenkins_build_pagure_100+seed'
  2890. )
  2891. data['flag']['date_created'] = '1510742565'
  2892. data['uid'] = 'b1de8f80defd4a81afe2e09f39678087'
  2893. expected_output = {
  2894. 'flag': {
  2895. 'comment': 'Tests passed',
  2896. 'commit_hash': commit.oid.hex,
  2897. 'date_created': '1510742565',
  2898. 'percent': 100,
  2899. 'status': 'success',
  2900. 'url': 'http://jenkins.cloud.fedoraproject.org/',
  2901. 'user': {
  2902. 'default_email': 'bar@pingou.com',
  2903. 'emails': ['bar@pingou.com', 'foo@pingou.com'],
  2904. 'fullname': 'PY C',
  2905. 'name': 'pingou'},
  2906. 'username': 'Jenkins'
  2907. },
  2908. 'message': 'Flag added',
  2909. 'uid': 'b1de8f80defd4a81afe2e09f39678087'
  2910. }
  2911. self.assertEqual(data, expected_output)
  2912. mock_email.assert_called_once_with(
  2913. '\nJenkins flagged the commit '
  2914. '`' + commit.oid.hex + '` as success: '
  2915. 'Tests passed\n\n'
  2916. 'http://localhost.localdomain/test/c/' + commit.oid.hex + '\n',
  2917. 'Coommit #' + commit.oid.hex + ' - Jenkins: success',
  2918. 'bar@pingou.com',
  2919. in_reply_to='test-project-1',
  2920. mail_id='test-commit-1-1',
  2921. project_name='test',
  2922. user_from='Jenkins'
  2923. )
  2924. @patch.dict('pagure.config.config',
  2925. {
  2926. 'FLAG_STATUSES_LABELS':
  2927. {
  2928. 'pend!': 'label-info',
  2929. 'succeed!': 'label-success',
  2930. 'fail!': 'label-danger',
  2931. 'what?': 'label-warning',
  2932. },
  2933. 'FLAG_PENDING': 'pend!',
  2934. 'FLAG_SUCCESS': 'succeed!',
  2935. 'FLAG_FAILURE': 'fail!',
  2936. })
  2937. def test_flag_commit_with_custom_flags(self):
  2938. """ Test flagging when custom flags are set up
  2939. """
  2940. repo_obj = pygit2.Repository(self.git_path)
  2941. commit = repo_obj.revparse_single('HEAD')
  2942. headers = {'Authorization': 'token aaabbbcccddd'}
  2943. send_data = {
  2944. 'username': 'Jenkins',
  2945. 'percent': 100,
  2946. 'comment': 'Tests passed',
  2947. 'url': 'http://jenkins.cloud.fedoraproject.org/',
  2948. 'status': 'succeed!',
  2949. }
  2950. output = self.app.post(
  2951. '/api/0/test/c/%s/flag' % commit.oid.hex,
  2952. headers=headers, data=send_data)
  2953. self.assertEqual(output.status_code, 200)
  2954. data = json.loads(output.get_data(as_text=True))
  2955. self.assertEqual(data['flag']['status'], 'succeed!')
  2956. # Try invalid flag status
  2957. send_data['status'] = 'nooooo....'
  2958. output = self.app.post(
  2959. '/api/0/test/c/%s/flag' % commit.oid.hex,
  2960. headers=headers, data=send_data)
  2961. self.assertEqual(output.status_code, 400)
  2962. data = json.loads(output.get_data(as_text=True))
  2963. self.assertEqual(
  2964. data,
  2965. {
  2966. 'errors': {'status': ['Not a valid choice']},
  2967. 'error_code': 'EINVALIDREQ',
  2968. 'error': 'Invalid or incomplete input submitted'
  2969. }
  2970. )
  2971. def test_commit_flags(self):
  2972. """ Test retrieving commit flags. """
  2973. repo = pagure.lib.get_authorized_project(self.session, 'test')
  2974. repo_obj = pygit2.Repository(self.git_path)
  2975. commit = repo_obj.revparse_single('HEAD')
  2976. # test with no flags
  2977. output = self.app.get('/api/0/test/c/%s/flag' % commit.oid.hex)
  2978. self.assertEqual(json.loads(output.get_data(as_text=True)), {'total_flags': 0, 'flags': []})
  2979. self.assertEqual(output.status_code, 200)
  2980. # add some flags and retrieve them
  2981. pagure.lib.add_commit_flag(
  2982. session=self.session,
  2983. repo=repo,
  2984. commit_hash=commit.oid.hex,
  2985. username='simple-koji-ci',
  2986. status='pending',
  2987. percent=None,
  2988. comment='Build is running',
  2989. url='https://koji.fp.o/koji...',
  2990. uid='uid',
  2991. user='foo',
  2992. token='aaabbbcccddd'
  2993. )
  2994. pagure.lib.add_commit_flag(
  2995. session=self.session,
  2996. repo=repo,
  2997. commit_hash=commit.oid.hex,
  2998. username='complex-koji-ci',
  2999. status='success',
  3000. percent=None,
  3001. comment='Build succeeded',
  3002. url='https://koji.fp.o/koji...',
  3003. uid='uid2',
  3004. user='foo',
  3005. token='aaabbbcccddd'
  3006. )
  3007. self.session.commit()
  3008. output = self.app.get('/api/0/test/c/%s/flag' % commit.oid.hex)
  3009. data = json.loads(output.get_data(as_text=True))
  3010. for f in data['flags']:
  3011. f['date_created'] = '1510742565'
  3012. f['commit_hash'] = '62b49f00d489452994de5010565fab81'
  3013. expected_output = {
  3014. "flags": [
  3015. {
  3016. "comment": "Build is running",
  3017. "commit_hash": "62b49f00d489452994de5010565fab81",
  3018. "date_created": "1510742565",
  3019. "percent": None,
  3020. "status": "pending",
  3021. "url": "https://koji.fp.o/koji...",
  3022. "user": {
  3023. "fullname": "foo bar",
  3024. "name": "foo"
  3025. },
  3026. "username": "simple-koji-ci"
  3027. },
  3028. {
  3029. "comment": "Build succeeded",
  3030. "commit_hash": "62b49f00d489452994de5010565fab81",
  3031. "date_created": "1510742565",
  3032. "percent": None,
  3033. "status": "success",
  3034. "url": "https://koji.fp.o/koji...",
  3035. "user": {
  3036. "fullname": "foo bar",
  3037. "name": "foo"
  3038. },
  3039. "username": "complex-koji-ci"
  3040. }
  3041. ],
  3042. "total_flags": 2
  3043. }
  3044. self.assertEqual(data, expected_output)
  3045. class PagureFlaskApiProjectModifyAclTests(tests.Modeltests):
  3046. """ Tests for the flask API of pagure for modifying ACLs in a project
  3047. """
  3048. maxDiff = None
  3049. def setUp(self):
  3050. """ Set up the environnment, ran before every tests. """
  3051. super(PagureFlaskApiProjectModifyAclTests, self).setUp()
  3052. tests.create_projects(self.session)
  3053. tests.create_tokens(self.session, project_id=None)
  3054. tests.create_tokens_acl(
  3055. self.session, 'aaabbbcccddd', 'modify_project')
  3056. project = pagure.lib._get_project(self.session, 'test')
  3057. self.assertEquals(
  3058. project.access_users,
  3059. {u'admin': [], u'commit': [], u'ticket': []}
  3060. )
  3061. def test_api_modify_acls_no_project(self):
  3062. """ Test the api_modify_acls method of the flask api when the project
  3063. doesn't exist """
  3064. headers = {'Authorization': 'token aaabbbcccddd'}
  3065. data = {
  3066. 'user_type': 'user',
  3067. 'name': 'bar',
  3068. 'acl': 'commit'
  3069. }
  3070. output = self.app.post(
  3071. '/api/0/test12345123/git/modifyacls',
  3072. headers=headers, data=data)
  3073. self.assertEqual(output.status_code, 404)
  3074. data = json.loads(output.get_data(as_text=True))
  3075. expected_output = {
  3076. 'error_code': 'ENOPROJECT',
  3077. 'error': 'Project not found'
  3078. }
  3079. self.assertEqual(data, expected_output)
  3080. def test_api_modify_acls_no_user(self):
  3081. """ Test the api_modify_acls method of the flask api when the user
  3082. doesn't exist """
  3083. headers = {'Authorization': 'token aaabbbcccddd'}
  3084. data = {
  3085. 'user_type': 'user',
  3086. 'name': 'nosuchuser',
  3087. 'acl': 'commit'
  3088. }
  3089. output = self.app.post(
  3090. '/api/0/test/git/modifyacls',
  3091. headers=headers, data=data)
  3092. self.assertEqual(output.status_code, 404)
  3093. data = json.loads(output.get_data(as_text=True))
  3094. expected_output = {
  3095. 'error': 'No such user found',
  3096. 'error_code': u'ENOUSER'
  3097. }
  3098. self.assertEqual(data, expected_output)
  3099. def test_api_modify_acls_no_group(self):
  3100. """ Test the api_modify_acls method of the flask api when the group
  3101. doesn't exist """
  3102. headers = {'Authorization': 'token aaabbbcccddd'}
  3103. data = {
  3104. 'user_type': 'group',
  3105. 'name': 'nosuchgroup',
  3106. 'acl': 'commit'
  3107. }
  3108. output = self.app.post(
  3109. '/api/0/test/git/modifyacls',
  3110. headers=headers, data=data)
  3111. self.assertEqual(output.status_code, 404)
  3112. data = json.loads(output.get_data(as_text=True))
  3113. expected_output = {
  3114. 'error': 'Group not found',
  3115. 'error_code': 'ENOGROUP'
  3116. }
  3117. self.assertEqual(data, expected_output)
  3118. def test_api_modify_acls_no_permission(self):
  3119. """ Test the api_modify_acls method of the flask api when the user
  3120. doesn't have permissions """
  3121. item = pagure.lib.model.Token(
  3122. id='foo_token2',
  3123. user_id=2,
  3124. project_id=None,
  3125. expiration=datetime.datetime.utcnow()
  3126. + datetime.timedelta(days=30)
  3127. )
  3128. self.session.add(item)
  3129. self.session.commit()
  3130. tests.create_tokens_acl(
  3131. self.session, 'foo_token2', 'modify_project')
  3132. headers = {'Authorization': 'token foo_token2'}
  3133. data = {
  3134. 'user_type': 'user',
  3135. 'name': 'foo',
  3136. 'acl': 'commit'
  3137. }
  3138. output = self.app.post(
  3139. '/api/0/test/git/modifyacls',
  3140. headers=headers, data=data)
  3141. self.assertEqual(output.status_code, 401)
  3142. data = json.loads(output.get_data(as_text=True))
  3143. expected_output = {
  3144. 'error': 'You are not allowed to modify this project',
  3145. 'error_code': 'EMODIFYPROJECTNOTALLOWED'
  3146. }
  3147. self.assertEqual(data, expected_output)
  3148. def test_api_modify_acls_neither_user_nor_group(self):
  3149. """ Test the api_modify_acls method of the flask api when neither
  3150. user nor group was set """
  3151. headers = {'Authorization': 'token aaabbbcccddd'}
  3152. data = {
  3153. 'acl': 'commit'
  3154. }
  3155. output = self.app.post(
  3156. '/api/0/test/git/modifyacls',
  3157. headers=headers, data=data)
  3158. self.assertEqual(output.status_code, 400)
  3159. data = json.loads(output.get_data(as_text=True))
  3160. expected_output = {
  3161. 'error': 'Invalid or incomplete input submitted',
  3162. 'error_code': 'EINVALIDREQ',
  3163. 'errors': {'name': ['This field is required.'],
  3164. 'user_type': ['Not a valid choice']}
  3165. }
  3166. self.assertEqual(data, expected_output)
  3167. def test_api_modify_acls_invalid_acl(self):
  3168. """ Test the api_modify_acls method of the flask api when the ACL
  3169. doesn't exist. Must be one of ticket, commit or admin. """
  3170. headers = {'Authorization': 'token aaabbbcccddd'}
  3171. data = {
  3172. 'user_type': 'user',
  3173. 'name': 'bar',
  3174. 'acl': 'invalidacl'
  3175. }
  3176. output = self.app.post(
  3177. '/api/0/test/git/modifyacls',
  3178. headers=headers, data=data)
  3179. self.assertEqual(output.status_code, 400)
  3180. data = json.loads(output.get_data(as_text=True))
  3181. expected_output = {
  3182. 'error': 'Invalid or incomplete input submitted',
  3183. 'error_code': 'EINVALIDREQ',
  3184. 'errors': {
  3185. 'acl': ['Not a valid choice']
  3186. }
  3187. }
  3188. self.assertEqual(data, expected_output)
  3189. def test_api_modify_acls_user(self):
  3190. """ Test the api_modify_acls method of the flask api for
  3191. setting an ACL for a user. """
  3192. headers = {'Authorization': 'token aaabbbcccddd'}
  3193. data = {
  3194. 'user_type': 'user',
  3195. 'name': 'foo',
  3196. 'acl': 'commit'
  3197. }
  3198. output = self.app.post(
  3199. '/api/0/test/git/modifyacls',
  3200. headers=headers, data=data)
  3201. self.assertEqual(output.status_code, 200)
  3202. data = json.loads(output.get_data(as_text=True))
  3203. data['date_created'] = '1510742565'
  3204. data['date_modified'] = '1510742566'
  3205. expected_output = {
  3206. 'access_groups': {'admin': [], 'commit': [], 'ticket': []},
  3207. 'access_users': {'admin': [],
  3208. 'commit': ['foo'],
  3209. 'owner': ['pingou'],
  3210. 'ticket': []},
  3211. 'close_status':
  3212. ['Invalid', 'Insufficient data', 'Fixed', 'Duplicate'],
  3213. 'custom_keys': [],
  3214. 'date_created': '1510742565',
  3215. 'date_modified': '1510742566',
  3216. 'description': 'test project #1',
  3217. 'fullname': 'test',
  3218. 'id': 1,
  3219. 'milestones': {},
  3220. 'name': 'test',
  3221. 'namespace': None,
  3222. 'parent': None,
  3223. 'priorities': {},
  3224. 'tags': [],
  3225. 'url_path': 'test',
  3226. 'user': {'fullname': 'PY C', 'name': 'pingou'}
  3227. }
  3228. self.assertEqual(data, expected_output)
  3229. def test_api_modify_acls_group(self):
  3230. """ Test the api_modify_acls method of the flask api for
  3231. setting an ACL for a group. """
  3232. headers = {'Authorization': 'token aaabbbcccddd'}
  3233. # Create a group
  3234. msg = pagure.lib.add_group(
  3235. self.session,
  3236. group_name='baz',
  3237. display_name='baz group',
  3238. description=None,
  3239. group_type='bar',
  3240. user='foo',
  3241. is_admin=False,
  3242. blacklist=[],
  3243. )
  3244. self.session.commit()
  3245. self.assertEqual(msg, 'User `foo` added to the group `baz`.')
  3246. data = {
  3247. 'user_type': 'group',
  3248. 'name': 'baz',
  3249. 'acl': 'ticket'
  3250. }
  3251. output = self.app.post(
  3252. '/api/0/test/git/modifyacls',
  3253. headers=headers, data=data)
  3254. self.assertEqual(output.status_code, 200)
  3255. data = json.loads(output.get_data(as_text=True))
  3256. data['date_created'] = '1510742565'
  3257. data['date_modified'] = '1510742566'
  3258. expected_output = {
  3259. 'access_groups': {
  3260. 'admin': [],
  3261. 'commit': [],
  3262. 'ticket': ['baz']
  3263. },
  3264. 'access_users': {
  3265. 'admin': [],
  3266. 'commit': [],
  3267. 'owner': ['pingou'],
  3268. 'ticket': []
  3269. },
  3270. 'close_status': [
  3271. 'Invalid',
  3272. 'Insufficient data',
  3273. 'Fixed',
  3274. 'Duplicate'
  3275. ],
  3276. 'custom_keys': [],
  3277. 'date_created': '1510742565',
  3278. 'date_modified': '1510742566',
  3279. 'description': 'test project #1',
  3280. 'fullname': 'test',
  3281. 'id': 1,
  3282. 'milestones': {},
  3283. 'name': 'test',
  3284. 'namespace': None,
  3285. 'parent': None,
  3286. 'priorities': {},
  3287. 'tags': [],
  3288. 'url_path': 'test',
  3289. 'user': {'fullname': 'PY C', 'name': 'pingou'}
  3290. }
  3291. self.assertEqual(data, expected_output)
  3292. def test_api_modify_acls_no_acl(self):
  3293. """ Test the api_modify_acls method of the flask api when no ACL
  3294. are specified. """
  3295. headers = {'Authorization': 'token aaabbbcccddd'}
  3296. project = pagure.lib._get_project(self.session, 'test')
  3297. self.assertEquals(
  3298. project.access_users,
  3299. {u'admin': [], u'commit': [], u'ticket': []}
  3300. )
  3301. data = {
  3302. 'user_type': 'user',
  3303. 'name': 'foo',
  3304. }
  3305. output = self.app.post(
  3306. '/api/0/test/git/modifyacls', headers=headers, data=data)
  3307. self.assertEqual(output.status_code, 400)
  3308. data = json.loads(output.get_data(as_text=True))
  3309. expected_output = {
  3310. "error": "Invalid or incomplete input submitted",
  3311. "error_code": "EINVALIDREQ",
  3312. "errors": "User does not have any access on the repo"
  3313. }
  3314. self.assertEqual(data, expected_output)
  3315. def test_api_modify_acls_remove_own_acl_no_access(self):
  3316. """ Test the api_modify_acls method of the flask api when no ACL
  3317. are specified, so the user tries to remove their own access but the
  3318. user is the project owner. """
  3319. headers = {'Authorization': 'token aaabbbcccddd'}
  3320. data = {
  3321. 'user_type': 'user',
  3322. 'name': 'pingou',
  3323. }
  3324. output = self.app.post(
  3325. '/api/0/test/git/modifyacls', headers=headers, data=data)
  3326. self.assertEqual(output.status_code, 400)
  3327. data = json.loads(output.get_data(as_text=True))
  3328. expected_output = {
  3329. "error": "Invalid or incomplete input submitted",
  3330. "error_code": "EINVALIDREQ",
  3331. "errors": "User does not have any access on the repo"
  3332. }
  3333. self.assertEqual(data, expected_output)
  3334. def test_api_modify_acls_remove_own_acl_(self):
  3335. """ Test the api_modify_acls method of the flask api when no ACL
  3336. are specified, so the user tries to remove their own access but the
  3337. user is the project owner. """
  3338. # Add the user `foo` to the project
  3339. self.test_api_modify_acls_user()
  3340. # Ensure `foo` was properly added:
  3341. project = pagure.lib._get_project(self.session, 'test')
  3342. user_foo = pagure.lib.search_user(self.session, username='foo')
  3343. self.assertEquals(
  3344. project.access_users,
  3345. {u'admin': [], u'commit': [user_foo], u'ticket': []}
  3346. )
  3347. # Create an API token for `foo` for the project `test`
  3348. item = pagure.lib.model.Token(
  3349. id='foo_test_token',
  3350. user_id=2, # foo
  3351. project_id=1, # test
  3352. expiration=datetime.datetime.utcnow() + datetime.timedelta(days=10)
  3353. )
  3354. self.session.add(item)
  3355. self.session.commit()
  3356. tests.create_tokens_acl(
  3357. self.session, 'foo_test_token', 'modify_project')
  3358. headers = {'Authorization': 'token foo_test_token'}
  3359. data = {
  3360. 'user_type': 'user',
  3361. 'name': 'foo',
  3362. }
  3363. output = self.app.post(
  3364. '/api/0/test/git/modifyacls', headers=headers, data=data)
  3365. self.assertEqual(output.status_code, 200)
  3366. data = json.loads(output.get_data(as_text=True))
  3367. data['date_created'] = '1510742565'
  3368. data['date_modified'] = '1510742566'
  3369. expected_output = {
  3370. "access_groups": {
  3371. "admin": [],
  3372. "commit": [],
  3373. "ticket": []
  3374. },
  3375. "access_users": {
  3376. "admin": [],
  3377. "commit": [],
  3378. "owner": [
  3379. "pingou"
  3380. ],
  3381. "ticket": []
  3382. },
  3383. "close_status": [
  3384. "Invalid",
  3385. "Insufficient data",
  3386. "Fixed",
  3387. "Duplicate"
  3388. ],
  3389. "custom_keys": [],
  3390. "date_created": "1510742565",
  3391. "date_modified": "1510742566",
  3392. "description": "test project #1",
  3393. "fullname": "test",
  3394. "id": 1,
  3395. "milestones": {},
  3396. "name": "test",
  3397. "namespace": None,
  3398. "parent": None,
  3399. "priorities": {},
  3400. "tags": [],
  3401. "url_path": "test",
  3402. "user": {
  3403. "fullname": "PY C",
  3404. "name": "pingou"
  3405. }
  3406. }
  3407. self.assertEqual(data, expected_output)
  3408. # Ensure `foo` was properly removed
  3409. self.session = pagure.lib.create_session(self.dbpath)
  3410. project = pagure.lib._get_project(self.session, 'test')
  3411. self.assertEquals(
  3412. project.access_users,
  3413. {u'admin': [], u'commit': [], u'ticket': []}
  3414. )
  3415. def test_api_modify_acls_remove_someone_else_acl(self):
  3416. """ Test the api_modify_acls method of the flask api an admin tries
  3417. to remove access from someone else. """
  3418. # Add the user `foo` to the project
  3419. self.test_api_modify_acls_user()
  3420. # Ensure `foo` was properly added:
  3421. project = pagure.lib._get_project(self.session, 'test')
  3422. user_foo = pagure.lib.search_user(self.session, username='foo')
  3423. self.assertEquals(
  3424. project.access_users,
  3425. {u'admin': [], u'commit': [user_foo], u'ticket': []}
  3426. )
  3427. headers = {'Authorization': 'token aaabbbcccddd'}
  3428. data = {
  3429. 'user_type': 'user',
  3430. 'name': 'foo',
  3431. }
  3432. output = self.app.post(
  3433. '/api/0/test/git/modifyacls', headers=headers, data=data)
  3434. self.assertEqual(output.status_code, 200)
  3435. data = json.loads(output.get_data(as_text=True))
  3436. data['date_created'] = '1510742565'
  3437. data['date_modified'] = '1510742566'
  3438. expected_output = {
  3439. "access_groups": {
  3440. "admin": [],
  3441. "commit": [],
  3442. "ticket": []
  3443. },
  3444. "access_users": {
  3445. "admin": [],
  3446. "commit": [],
  3447. "owner": [
  3448. "pingou"
  3449. ],
  3450. "ticket": []
  3451. },
  3452. "close_status": [
  3453. "Invalid",
  3454. "Insufficient data",
  3455. "Fixed",
  3456. "Duplicate"
  3457. ],
  3458. "custom_keys": [],
  3459. "date_created": "1510742565",
  3460. "date_modified": "1510742566",
  3461. "description": "test project #1",
  3462. "fullname": "test",
  3463. "id": 1,
  3464. "milestones": {},
  3465. "name": "test",
  3466. "namespace": None,
  3467. "parent": None,
  3468. "priorities": {},
  3469. "tags": [],
  3470. "url_path": "test",
  3471. "user": {
  3472. "fullname": "PY C",
  3473. "name": "pingou"
  3474. }
  3475. }
  3476. self.assertEqual(data, expected_output)
  3477. # Ensure `foo` was properly removed
  3478. self.session = pagure.lib.create_session(self.dbpath)
  3479. project = pagure.lib._get_project(self.session, 'test')
  3480. self.assertEquals(
  3481. project.access_users,
  3482. {u'admin': [], u'commit': [], u'ticket': []}
  3483. )
  3484. if __name__ == '__main__':
  3485. unittest.main(verbosity=2)