test_pagure_flask_api_project.py 160 KB

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