test_pagure_flask_rebase.py 48 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178
  1. # -*- coding: utf-8 -*-
  2. """
  3. (c) 2018 - Copyright Red Hat Inc
  4. Authors:
  5. Pierre-Yves Chibon <pingou@pingoured.fr>
  6. """
  7. from __future__ import unicode_literals, absolute_import
  8. import datetime
  9. import unittest
  10. import shutil
  11. import sys
  12. import os
  13. import json
  14. import pagure_messages
  15. import pygit2
  16. from fedora_messaging import api, testing
  17. from mock import ANY, patch, MagicMock
  18. sys.path.insert(
  19. 0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")
  20. )
  21. import pagure.lib.query
  22. import pagure.lib.tasks
  23. import tests
  24. class PagureRebaseBasetests(tests.Modeltests):
  25. """Tests rebasing pull-request in pagure"""
  26. maxDiff = None
  27. config_values = {"authbackend": "pagure"}
  28. @patch("pagure.lib.notify.send_email", MagicMock(return_value=True))
  29. def setUp(self):
  30. """Set up the environnment, ran before every tests."""
  31. super(PagureRebaseBasetests, self).setUp()
  32. pagure.config.config["REQUESTS_FOLDER"] = None
  33. tests.create_projects(self.session)
  34. tests.create_projects_git(os.path.join(self.path, "repos"), bare=True)
  35. tests.create_projects_git(
  36. os.path.join(self.path, "requests"), bare=True
  37. )
  38. tests.add_content_to_git(
  39. os.path.join(self.path, "repos", "test.git"),
  40. branch="master",
  41. content="foobarbaz",
  42. filename="testfile",
  43. )
  44. project = pagure.lib.query.get_authorized_project(self.session, "test")
  45. # Fork the project
  46. task = pagure.lib.query.fork_project(
  47. session=self.session, user="foo", repo=project
  48. )
  49. self.session.commit()
  50. self.assertEqual(
  51. task.get(),
  52. {
  53. "endpoint": "ui_ns.view_repo",
  54. "repo": "test",
  55. "username": "foo",
  56. "namespace": None,
  57. },
  58. )
  59. tests.add_content_to_git(
  60. os.path.join(self.path, "repos", "forks", "foo", "test.git"),
  61. branch="test",
  62. content="foobar",
  63. filename="sources",
  64. )
  65. fork_repo = pagure.lib.query.get_authorized_project(
  66. self.session, "test", user="foo"
  67. )
  68. tests.add_readme_git_repo(os.path.join(self.path, "repos", "test.git"))
  69. # Create a PR for these changes
  70. req = pagure.lib.query.new_pull_request(
  71. session=self.session,
  72. repo_from=fork_repo,
  73. branch_from="test",
  74. repo_to=project,
  75. branch_to="master",
  76. title="PR from the test branch",
  77. user="foo",
  78. allow_rebase=True,
  79. )
  80. self.session.commit()
  81. self.assertEqual(req.id, 1)
  82. self.assertEqual(req.title, "PR from the test branch")
  83. self.project = pagure.lib.query.get_authorized_project(
  84. self.session, "test"
  85. )
  86. self.assertEqual(len(project.requests), 1)
  87. self.request = self.project.requests[0]
  88. class PagureRebasetests(PagureRebaseBasetests):
  89. """Tests rebasing pull-request in pagure"""
  90. def test_merge_status_merge(self):
  91. """Test that the PR can be merged with a merge commit."""
  92. user = tests.FakeUser(username="pingou")
  93. with tests.user_set(self.app.application, user):
  94. data = {
  95. "requestid": self.request.uid,
  96. "csrf_token": self.get_csrf(),
  97. }
  98. output = self.app.post("/pv/pull-request/merge", data=data)
  99. self.assertEqual(output.status_code, 200)
  100. data = json.loads(output.get_data(as_text=True))
  101. self.assertEqual(
  102. data,
  103. {
  104. "code": "MERGE",
  105. "message": "The pull-request can be merged with a "
  106. "merge commit",
  107. "short_code": "With merge",
  108. },
  109. )
  110. def test_merge_status_needsrebase(self):
  111. """Test that the PR is marked as needing a rebase if the project
  112. disables non-fast-forward merges."""
  113. self.project = pagure.lib.query.get_authorized_project(
  114. self.session, "test"
  115. )
  116. settings = self.project.settings
  117. settings["disable_non_fast-forward_merges"] = True
  118. self.project.settings = settings
  119. self.session.add(self.project)
  120. self.session.commit()
  121. user = tests.FakeUser(username="pingou")
  122. with tests.user_set(self.app.application, user):
  123. data = {
  124. "requestid": self.request.uid,
  125. "csrf_token": self.get_csrf(),
  126. }
  127. output = self.app.post("/pv/pull-request/merge", data=data)
  128. self.assertEqual(output.status_code, 200)
  129. data = json.loads(output.get_data(as_text=True))
  130. self.assertEqual(
  131. data,
  132. {
  133. "code": "NEEDSREBASE",
  134. "message": "The pull-request must be rebased before "
  135. "merging",
  136. "short_code": "Needs rebase",
  137. },
  138. )
  139. def test_rebase_task(self):
  140. """Test the rebase PR task and its outcome."""
  141. pagure.lib.tasks.rebase_pull_request(
  142. "test",
  143. namespace=None,
  144. user=None,
  145. requestid=self.request.id,
  146. user_rebaser="pingou",
  147. )
  148. user = tests.FakeUser(username="pingou")
  149. with tests.user_set(self.app.application, user):
  150. data = {
  151. "requestid": self.request.uid,
  152. "csrf_token": self.get_csrf(),
  153. }
  154. output = self.app.post("/pv/pull-request/merge", data=data)
  155. self.assertEqual(output.status_code, 200)
  156. data = json.loads(output.get_data(as_text=True))
  157. self.assertEqual(
  158. data,
  159. {
  160. "code": "FFORWARD",
  161. "message": "The pull-request can be merged and "
  162. "fast-forwarded",
  163. "short_code": "Ok",
  164. },
  165. )
  166. def test_rebase_api_ui_logged_in_different_user(self):
  167. """Test the rebase PR API endpoint when logged in from the UI and
  168. its outcome."""
  169. # Add 'bar' to the project 'test' so 'bar' can rebase the PR
  170. item = pagure.lib.model.User(
  171. user="bar",
  172. fullname="bar foo",
  173. password=b"foo",
  174. default_email="bar@foo.com",
  175. )
  176. self.session.add(item)
  177. item = pagure.lib.model.UserEmail(user_id=2, email="bar@foo.com")
  178. self.session.add(item)
  179. self.session.commit()
  180. repo = pagure.lib.query._get_project(self.session, "test")
  181. msg = pagure.lib.query.add_user_to_project(
  182. session=self.session, project=repo, new_user="bar", user="pingou"
  183. )
  184. self.session.commit()
  185. self.assertEqual(msg, "User added")
  186. user = tests.FakeUser(username="bar")
  187. with tests.user_set(self.app.application, user):
  188. # Get the merge status first so it's cached and can be refreshed
  189. csrf_token = self.get_csrf()
  190. data = {"requestid": self.request.uid, "csrf_token": csrf_token}
  191. output = self.app.post("/pv/pull-request/merge", data=data)
  192. self.assertEqual(output.status_code, 200)
  193. data = json.loads(output.get_data(as_text=True))
  194. self.assertEqual(
  195. data,
  196. {
  197. "code": "MERGE",
  198. "message": "The pull-request can be merged with "
  199. "a merge commit",
  200. "short_code": "With merge",
  201. },
  202. )
  203. output = self.app.post("/api/0/test/pull-request/1/rebase")
  204. self.assertEqual(output.status_code, 200)
  205. data = json.loads(output.get_data(as_text=True))
  206. self.assertEqual(data, {"message": "Pull-request rebased"})
  207. data = {"requestid": self.request.uid, "csrf_token": csrf_token}
  208. output = self.app.post("/pv/pull-request/merge", data=data)
  209. self.assertEqual(output.status_code, 200)
  210. data = json.loads(output.get_data(as_text=True))
  211. self.assertEqual(
  212. data,
  213. {
  214. "code": "FFORWARD",
  215. "message": "The pull-request can be merged and "
  216. "fast-forwarded",
  217. "short_code": "Ok",
  218. },
  219. )
  220. output = self.app.get("/test/pull-request/1")
  221. self.assertEqual(output.status_code, 200)
  222. output_text = output.get_data(as_text=True)
  223. orig_repo_obj = pygit2.Repository(
  224. os.path.join(self.path, "repos", "test.git")
  225. )
  226. orig_commit = orig_repo_obj.lookup_branch("master").peel().hex
  227. expected = f'rebased onto <a href="/test/c/{orig_commit}"'
  228. self.assertIn(expected, output_text)
  229. repo = pagure.lib.query._get_project(self.session, "test")
  230. self.assertEqual(repo.requests[0].comments[0].user.username, "bar")
  231. def test_rebase_api_ui_logged_in_pull_request_author(self):
  232. """Test the rebase PR API endpoint when logged in from the UI and
  233. its outcome."""
  234. user = tests.FakeUser(username="foo")
  235. with tests.user_set(self.app.application, user):
  236. # Get the merge status first so it's cached and can be refreshed
  237. csrf_token = self.get_csrf()
  238. data = {"requestid": self.request.uid, "csrf_token": csrf_token}
  239. output = self.app.post("/pv/pull-request/merge", data=data)
  240. self.assertEqual(output.status_code, 200)
  241. data = json.loads(output.get_data(as_text=True))
  242. self.assertEqual(
  243. data,
  244. {
  245. "code": "MERGE",
  246. "message": "The pull-request can be merged with "
  247. "a merge commit",
  248. "short_code": "With merge",
  249. },
  250. )
  251. output = self.app.post("/api/0/test/pull-request/1/rebase")
  252. self.assertEqual(output.status_code, 200)
  253. data = json.loads(output.get_data(as_text=True))
  254. self.assertEqual(data, {"message": "Pull-request rebased"})
  255. data = {"requestid": self.request.uid, "csrf_token": csrf_token}
  256. output = self.app.post("/pv/pull-request/merge", data=data)
  257. self.assertEqual(output.status_code, 200)
  258. data = json.loads(output.get_data(as_text=True))
  259. self.assertEqual(
  260. data,
  261. {
  262. "code": "FFORWARD",
  263. "message": "The pull-request can be merged and "
  264. "fast-forwarded",
  265. "short_code": "Ok",
  266. },
  267. )
  268. output = self.app.get("/test/pull-request/1")
  269. self.assertEqual(output.status_code, 200)
  270. output_text = output.get_data(as_text=True)
  271. self.assertIn("rebased onto", output_text)
  272. repo = pagure.lib.query._get_project(self.session, "test")
  273. self.assertEqual(repo.requests[0].comments[0].user.username, "foo")
  274. def test_rebase_api_api_logged_in(self):
  275. """Test the rebase PR API endpoint when using an API token and
  276. its outcome."""
  277. tests.create_tokens(self.session)
  278. tests.create_tokens_acl(self.session)
  279. headers = {"Authorization": "token aaabbbcccddd"}
  280. output = self.app.post(
  281. "/api/0/test/pull-request/1/rebase", headers=headers
  282. )
  283. self.assertEqual(output.status_code, 200)
  284. data = json.loads(output.get_data(as_text=True))
  285. self.assertEqual(data, {"message": "Pull-request rebased"})
  286. user = tests.FakeUser(username="pingou")
  287. with tests.user_set(self.app.application, user):
  288. data = {
  289. "requestid": self.request.uid,
  290. "csrf_token": self.get_csrf(),
  291. }
  292. output = self.app.post("/pv/pull-request/merge", data=data)
  293. self.assertEqual(output.status_code, 200)
  294. data = json.loads(output.get_data(as_text=True))
  295. self.assertEqual(
  296. data,
  297. {
  298. "code": "FFORWARD",
  299. "message": "The pull-request can be merged and "
  300. "fast-forwarded",
  301. "short_code": "Ok",
  302. },
  303. )
  304. def test_rebase_api_conflicts(self):
  305. """Test the rebase PR API endpoint when logged in from the UI and
  306. its outcome."""
  307. tests.add_content_to_git(
  308. os.path.join(self.path, "repos", "test.git"),
  309. branch="master",
  310. content="foobar baz",
  311. )
  312. user = tests.FakeUser(username="pingou")
  313. with tests.user_set(self.app.application, user):
  314. output = self.app.post("/api/0/test/pull-request/1/rebase")
  315. self.assertEqual(output.status_code, 400)
  316. data = json.loads(output.get_data(as_text=True))
  317. self.assertEqual(
  318. data,
  319. {
  320. "error": "Did not manage to rebase this pull-request",
  321. "error_code": "ENOCODE",
  322. },
  323. )
  324. data = {
  325. "requestid": self.request.uid,
  326. "csrf_token": self.get_csrf(),
  327. }
  328. output = self.app.post("/pv/pull-request/merge", data=data)
  329. self.assertEqual(output.status_code, 200)
  330. data = json.loads(output.get_data(as_text=True))
  331. self.assertEqual(
  332. data,
  333. {
  334. "code": "CONFLICTS",
  335. "message": "The pull-request cannot be merged due "
  336. "to conflicts",
  337. "short_code": "Conflicts",
  338. },
  339. )
  340. def test_rebase_api_api_logged_in_unknown_project(self):
  341. """Test the rebase PR API endpoint when the project doesn't exist"""
  342. tests.create_tokens(self.session)
  343. tests.create_tokens_acl(self.session)
  344. headers = {"Authorization": "token aaabbbcccddd"}
  345. output = self.app.post(
  346. "/api/0/unknown/pull-request/1/rebase", headers=headers
  347. )
  348. self.assertEqual(output.status_code, 404)
  349. data = json.loads(output.get_data(as_text=True))
  350. self.assertEqual(
  351. data, {"error": "Project not found", "error_code": "ENOPROJECT"}
  352. )
  353. def test_rebase_api_api_logged_in_unknown_pr(self):
  354. """Test the rebase PR API endpoint when the PR doesn't exist"""
  355. tests.create_tokens(self.session)
  356. tests.create_tokens_acl(self.session)
  357. headers = {"Authorization": "token aaabbbcccddd"}
  358. output = self.app.post(
  359. "/api/0/test/pull-request/404/rebase", headers=headers
  360. )
  361. self.assertEqual(output.status_code, 404)
  362. data = json.loads(output.get_data(as_text=True))
  363. self.assertEqual(
  364. data, {"error": "Pull-Request not found", "error_code": "ENOREQ"}
  365. )
  366. def test_rebase_api_api_logged_in_unknown_token(self):
  367. """Test the rebase PR API endpoint with an invalid API token"""
  368. tests.create_tokens(self.session)
  369. tests.create_tokens_acl(self.session)
  370. headers = {"Authorization": "token unknown"}
  371. output = self.app.post(
  372. "/api/0/test/pull-request/1/rebase", headers=headers
  373. )
  374. self.assertEqual(output.status_code, 401)
  375. data = json.loads(output.get_data(as_text=True))
  376. self.assertEqual(
  377. data,
  378. {
  379. "error": "Invalid or expired token. Please visit "
  380. "http://localhost.localdomain/settings#nav-api-tab to get "
  381. "or renew your API token.",
  382. "error_code": "EINVALIDTOK",
  383. "errors": "Invalid token",
  384. },
  385. )
  386. class PagureRebaseNoHooktests(PagureRebaseBasetests):
  387. """Tests rebasing pull-request in pagure"""
  388. config_values = {"authbackend": "pagure", "nogithooks": True}
  389. @patch.dict(
  390. "pagure.config.config",
  391. {
  392. "FEDORA_MESSAGING_NOTIFICATIONS": True,
  393. },
  394. )
  395. def test_rebase_api_ui_logged_in(self):
  396. """Test the rebase PR API endpoint when logged in from the UI and
  397. its outcome."""
  398. user = tests.FakeUser(username="pingou")
  399. with tests.user_set(self.app.application, user):
  400. # Get the merge status first so it's cached and can be refreshed
  401. csrf_token = self.get_csrf()
  402. data = {"requestid": self.request.uid, "csrf_token": csrf_token}
  403. output = self.app.post("/pv/pull-request/merge", data=data)
  404. self.assertEqual(output.status_code, 200)
  405. data = json.loads(output.get_data(as_text=True))
  406. self.assertEqual(
  407. data,
  408. {
  409. "code": "MERGE",
  410. "message": "The pull-request can be merged with "
  411. "a merge commit",
  412. "short_code": "With merge",
  413. },
  414. )
  415. with testing.mock_sends(
  416. pagure_messages.PullRequestRebasedV1(
  417. topic="pagure.pull-request.rebased",
  418. body={
  419. "pullrequest": {
  420. "id": 1,
  421. "uid": ANY,
  422. "title": "PR from the test branch",
  423. "full_url": "http://localhost.localdomain/test/pull-request/1",
  424. "branch": "master",
  425. "project": {
  426. "id": 1,
  427. "name": "test",
  428. "fullname": "test",
  429. "url_path": "test",
  430. "full_url": "http://localhost.localdomain/test",
  431. "description": "test project #1",
  432. "namespace": None,
  433. "parent": None,
  434. "date_created": ANY,
  435. "date_modified": ANY,
  436. "user": {
  437. "name": "pingou",
  438. "fullname": "PY C",
  439. "url_path": "user/pingou",
  440. "full_url": "http://localhost.localdomain/user/pingou",
  441. },
  442. "access_users": {
  443. "owner": ["pingou"],
  444. "admin": [],
  445. "commit": [],
  446. "collaborator": [],
  447. "ticket": [],
  448. },
  449. "access_groups": {
  450. "admin": [],
  451. "commit": [],
  452. "collaborator": [],
  453. "ticket": [],
  454. },
  455. "tags": [],
  456. "priorities": {},
  457. "custom_keys": [],
  458. "close_status": [
  459. "Invalid",
  460. "Insufficient data",
  461. "Fixed",
  462. "Duplicate",
  463. ],
  464. "milestones": {},
  465. },
  466. "branch_from": "test",
  467. "repo_from": {
  468. "id": 4,
  469. "name": "test",
  470. "fullname": "forks/foo/test",
  471. "url_path": "fork/foo/test",
  472. "full_url": "http://localhost.localdomain/fork/foo/test",
  473. "description": "test project #1",
  474. "namespace": None,
  475. "parent": {
  476. "id": 1,
  477. "name": "test",
  478. "fullname": "test",
  479. "url_path": "test",
  480. "full_url": "http://localhost.localdomain/test",
  481. "description": "test project #1",
  482. "namespace": None,
  483. "parent": None,
  484. "date_created": ANY,
  485. "date_modified": ANY,
  486. "user": {
  487. "name": "pingou",
  488. "fullname": "PY C",
  489. "url_path": "user/pingou",
  490. "full_url": "http://localhost.localdomain/user/pingou",
  491. },
  492. "access_users": {
  493. "owner": ["pingou"],
  494. "admin": [],
  495. "commit": [],
  496. "collaborator": [],
  497. "ticket": [],
  498. },
  499. "access_groups": {
  500. "admin": [],
  501. "commit": [],
  502. "collaborator": [],
  503. "ticket": [],
  504. },
  505. "tags": [],
  506. "priorities": {},
  507. "custom_keys": [],
  508. "close_status": [
  509. "Invalid",
  510. "Insufficient data",
  511. "Fixed",
  512. "Duplicate",
  513. ],
  514. "milestones": {},
  515. },
  516. "date_created": ANY,
  517. "date_modified": ANY,
  518. "user": {
  519. "name": "foo",
  520. "fullname": "foo bar",
  521. "url_path": "user/foo",
  522. "full_url": "http://localhost.localdomain/user/foo",
  523. },
  524. "access_users": {
  525. "owner": ["foo"],
  526. "admin": [],
  527. "commit": [],
  528. "collaborator": [],
  529. "ticket": [],
  530. },
  531. "access_groups": {
  532. "admin": [],
  533. "commit": [],
  534. "collaborator": [],
  535. "ticket": [],
  536. },
  537. "tags": [],
  538. "priorities": {},
  539. "custom_keys": [],
  540. "close_status": [],
  541. "milestones": {},
  542. },
  543. "remote_git": None,
  544. "date_created": ANY,
  545. "updated_on": ANY,
  546. "last_updated": ANY,
  547. "closed_at": None,
  548. "user": {
  549. "name": "foo",
  550. "fullname": "foo bar",
  551. "url_path": "user/foo",
  552. "full_url": "http://localhost.localdomain/user/foo",
  553. },
  554. "assignee": None,
  555. "status": "Open",
  556. "commit_start": ANY,
  557. "commit_stop": ANY,
  558. "closed_by": None,
  559. "initial_comment": None,
  560. "cached_merge_status": "unknown",
  561. "threshold_reached": None,
  562. "tags": [],
  563. "comments": [],
  564. },
  565. "agent": "pagure",
  566. },
  567. ),
  568. pagure_messages.PullRequestCommentAddedV1(
  569. topic="pagure.pull-request.comment.added",
  570. body={
  571. "pullrequest": {
  572. "id": 1,
  573. "uid": ANY,
  574. "title": "PR from the test branch",
  575. "full_url": "http://localhost.localdomain/test/pull-request/1",
  576. "branch": "master",
  577. "project": {
  578. "id": 1,
  579. "name": "test",
  580. "fullname": "test",
  581. "url_path": "test",
  582. "full_url": "http://localhost.localdomain/test",
  583. "description": "test project #1",
  584. "namespace": None,
  585. "parent": None,
  586. "date_created": ANY,
  587. "date_modified": ANY,
  588. "user": {
  589. "name": "pingou",
  590. "fullname": "PY C",
  591. "url_path": "user/pingou",
  592. "full_url": "http://localhost.localdomain/user/pingou",
  593. },
  594. "access_users": {
  595. "owner": ["pingou"],
  596. "admin": [],
  597. "commit": [],
  598. "collaborator": [],
  599. "ticket": [],
  600. },
  601. "access_groups": {
  602. "admin": [],
  603. "commit": [],
  604. "collaborator": [],
  605. "ticket": [],
  606. },
  607. "tags": [],
  608. "priorities": {},
  609. "custom_keys": [],
  610. "close_status": [
  611. "Invalid",
  612. "Insufficient data",
  613. "Fixed",
  614. "Duplicate",
  615. ],
  616. "milestones": {},
  617. },
  618. "branch_from": "test",
  619. "repo_from": {
  620. "id": 4,
  621. "name": "test",
  622. "fullname": "forks/foo/test",
  623. "url_path": "fork/foo/test",
  624. "full_url": "http://localhost.localdomain/fork/foo/test",
  625. "description": "test project #1",
  626. "namespace": None,
  627. "parent": {
  628. "id": 1,
  629. "name": "test",
  630. "fullname": "test",
  631. "url_path": "test",
  632. "full_url": "http://localhost.localdomain/test",
  633. "description": "test project #1",
  634. "namespace": None,
  635. "parent": None,
  636. "date_created": ANY,
  637. "date_modified": ANY,
  638. "user": {
  639. "name": "pingou",
  640. "fullname": "PY C",
  641. "url_path": "user/pingou",
  642. "full_url": "http://localhost.localdomain/user/pingou",
  643. },
  644. "access_users": {
  645. "owner": ["pingou"],
  646. "admin": [],
  647. "commit": [],
  648. "collaborator": [],
  649. "ticket": [],
  650. },
  651. "access_groups": {
  652. "admin": [],
  653. "commit": [],
  654. "collaborator": [],
  655. "ticket": [],
  656. },
  657. "tags": [],
  658. "priorities": {},
  659. "custom_keys": [],
  660. "close_status": [
  661. "Invalid",
  662. "Insufficient data",
  663. "Fixed",
  664. "Duplicate",
  665. ],
  666. "milestones": {},
  667. },
  668. "date_created": ANY,
  669. "date_modified": ANY,
  670. "user": {
  671. "name": "foo",
  672. "fullname": "foo bar",
  673. "url_path": "user/foo",
  674. "full_url": "http://localhost.localdomain/user/foo",
  675. },
  676. "access_users": {
  677. "owner": ["foo"],
  678. "admin": [],
  679. "commit": [],
  680. "collaborator": [],
  681. "ticket": [],
  682. },
  683. "access_groups": {
  684. "admin": [],
  685. "commit": [],
  686. "collaborator": [],
  687. "ticket": [],
  688. },
  689. "tags": [],
  690. "priorities": {},
  691. "custom_keys": [],
  692. "close_status": [],
  693. "milestones": {},
  694. },
  695. "remote_git": None,
  696. "date_created": ANY,
  697. "updated_on": ANY,
  698. "last_updated": ANY,
  699. "closed_at": None,
  700. "user": {
  701. "name": "foo",
  702. "fullname": "foo bar",
  703. "url_path": "user/foo",
  704. "full_url": "http://localhost.localdomain/user/foo",
  705. },
  706. "assignee": None,
  707. "status": "Open",
  708. "commit_start": ANY,
  709. "commit_stop": ANY,
  710. "closed_by": None,
  711. "initial_comment": None,
  712. "cached_merge_status": "unknown",
  713. "threshold_reached": None,
  714. "tags": [],
  715. "comments": [
  716. {
  717. "id": 1,
  718. "commit": None,
  719. "tree": None,
  720. "filename": None,
  721. "line": None,
  722. "comment": ANY,
  723. "parent": None,
  724. "date_created": ANY,
  725. "user": {
  726. "name": "pingou",
  727. "fullname": "PY C",
  728. "url_path": "user/pingou",
  729. "full_url": "http://localhost.localdomain/user/pingou",
  730. },
  731. "edited_on": None,
  732. "editor": None,
  733. "notification": True,
  734. "reactions": {},
  735. }
  736. ],
  737. },
  738. "agent": "pingou",
  739. },
  740. ),
  741. ):
  742. output = self.app.post("/api/0/test/pull-request/1/rebase")
  743. self.assertEqual(output.status_code, 200)
  744. data = json.loads(output.get_data(as_text=True))
  745. self.assertEqual(data, {"message": "Pull-request rebased"})
  746. data = {"requestid": self.request.uid, "csrf_token": csrf_token}
  747. output = self.app.post("/pv/pull-request/merge", data=data)
  748. self.assertEqual(output.status_code, 200)
  749. data = json.loads(output.get_data(as_text=True))
  750. self.assertEqual(
  751. data,
  752. {
  753. "code": "FFORWARD",
  754. "message": "The pull-request can be merged and "
  755. "fast-forwarded",
  756. "short_code": "Ok",
  757. },
  758. )
  759. output = self.app.get("/test/pull-request/1")
  760. self.assertEqual(output.status_code, 200)
  761. output_text = output.get_data(as_text=True)
  762. self.assertIn("rebased onto", output_text)
  763. repo = pagure.lib.query._get_project(self.session, "test")
  764. self.assertEqual(
  765. repo.requests[0].comments[0].user.username, "pingou"
  766. )
  767. class PagureRebaseNotAllowedtests(tests.Modeltests):
  768. """Tests rebasing pull-request in pagure"""
  769. maxDiff = None
  770. @patch("pagure.lib.notify.send_email", MagicMock(return_value=True))
  771. def setUp(self):
  772. """Set up the environnment, ran before every tests."""
  773. super(PagureRebaseNotAllowedtests, self).setUp()
  774. pagure.config.config["REQUESTS_FOLDER"] = None
  775. tests.create_projects(self.session)
  776. tests.create_projects_git(os.path.join(self.path, "repos"), bare=True)
  777. tests.create_projects_git(
  778. os.path.join(self.path, "requests"), bare=True
  779. )
  780. tests.add_content_to_git(
  781. os.path.join(self.path, "repos", "test.git"),
  782. branch="master",
  783. content="foobarbaz",
  784. filename="testfile",
  785. )
  786. project = pagure.lib.query.get_authorized_project(self.session, "test")
  787. # Fork the project
  788. task = pagure.lib.query.fork_project(
  789. session=self.session, user="foo", repo=project
  790. )
  791. self.session.commit()
  792. self.assertEqual(
  793. task.get(),
  794. {
  795. "endpoint": "ui_ns.view_repo",
  796. "repo": "test",
  797. "username": "foo",
  798. "namespace": None,
  799. },
  800. )
  801. tests.add_content_to_git(
  802. os.path.join(self.path, "repos", "forks", "foo", "test.git"),
  803. branch="test",
  804. content="foobar",
  805. filename="sources",
  806. )
  807. fork_repo = pagure.lib.query.get_authorized_project(
  808. self.session, "test", user="foo"
  809. )
  810. tests.add_readme_git_repo(os.path.join(self.path, "repos", "test.git"))
  811. # Create a PR for these changes
  812. project = pagure.lib.query.get_authorized_project(self.session, "test")
  813. req = pagure.lib.query.new_pull_request(
  814. session=self.session,
  815. repo_from=fork_repo,
  816. branch_from="test",
  817. repo_to=project,
  818. branch_to="master",
  819. title="PR from the test branch",
  820. user="foo",
  821. allow_rebase=False,
  822. )
  823. self.session.commit()
  824. self.assertEqual(req.id, 1)
  825. self.assertEqual(req.title, "PR from the test branch")
  826. self.project = pagure.lib.query.get_authorized_project(
  827. self.session, "test"
  828. )
  829. self.assertEqual(len(project.requests), 1)
  830. self.request = self.project.requests[0]
  831. def test_rebase_api_ui_logged_in(self):
  832. """Test the rebase PR API endpoint when logged in from the UI and
  833. its outcome."""
  834. user = tests.FakeUser(username="pingou")
  835. with tests.user_set(self.app.application, user):
  836. # Get the merge status first so it's cached and can be refreshed
  837. csrf_token = self.get_csrf()
  838. data = {"requestid": self.request.uid, "csrf_token": csrf_token}
  839. output = self.app.post("/pv/pull-request/merge", data=data)
  840. self.assertEqual(output.status_code, 200)
  841. data = json.loads(output.get_data(as_text=True))
  842. self.assertEqual(
  843. data,
  844. {
  845. "code": "MERGE",
  846. "message": "The pull-request can be merged with "
  847. "a merge commit",
  848. "short_code": "With merge",
  849. },
  850. )
  851. output = self.app.post("/api/0/test/pull-request/1/rebase")
  852. self.assertEqual(output.status_code, 403)
  853. data = json.loads(output.get_data(as_text=True))
  854. self.assertEqual(
  855. data,
  856. {
  857. "error": "You are not authorized to rebase this pull-request",
  858. "error_code": "EREBASENOTALLOWED",
  859. },
  860. )
  861. # Add pingou to fork repo so he can rebase while allow_rebase = False
  862. fork = pagure.lib.query.get_authorized_project(
  863. self.session, "test", user="foo"
  864. )
  865. msg = pagure.lib.query.add_user_to_project(
  866. session=self.session,
  867. project=fork,
  868. new_user="pingou",
  869. user="foo",
  870. )
  871. self.session.commit()
  872. self.assertEqual(msg, "User added")
  873. output = self.app.post("/api/0/test/pull-request/1/rebase")
  874. self.assertEqual(output.status_code, 200)
  875. data = json.loads(output.get_data(as_text=True))
  876. self.assertEqual(data, {"message": "Pull-request rebased"})
  877. data = {"requestid": self.request.uid, "csrf_token": csrf_token}
  878. output = self.app.post("/pv/pull-request/merge", data=data)
  879. self.assertEqual(output.status_code, 200)
  880. data = json.loads(output.get_data(as_text=True))
  881. self.assertEqual(
  882. data,
  883. {
  884. "code": "FFORWARD",
  885. "message": "The pull-request can be merged and "
  886. "fast-forwarded",
  887. "short_code": "Ok",
  888. },
  889. )
  890. output = self.app.get("/test/pull-request/1")
  891. self.assertEqual(output.status_code, 200)
  892. output_text = output.get_data(as_text=True)
  893. self.assertIn("rebased onto", output_text)
  894. repo = pagure.lib.query._get_project(self.session, "test")
  895. self.assertEqual(
  896. repo.requests[0].comments[0].user.username, "pingou"
  897. )
  898. def test_rebase_api_ui_logged_in_different_user(self):
  899. """Test the rebase PR API endpoint when logged in from the UI and
  900. its outcome."""
  901. # Add 'bar' to the project 'test' so 'bar' can rebase the PR
  902. item = pagure.lib.model.User(
  903. user="bar",
  904. fullname="bar foo",
  905. password=b"foo",
  906. default_email="bar@foo.com",
  907. )
  908. self.session.add(item)
  909. item = pagure.lib.model.UserEmail(user_id=2, email="bar@foo.com")
  910. self.session.add(item)
  911. self.session.commit()
  912. repo = pagure.lib.query._get_project(self.session, "test")
  913. msg = pagure.lib.query.add_user_to_project(
  914. session=self.session, project=repo, new_user="bar", user="pingou"
  915. )
  916. self.session.commit()
  917. self.assertEqual(msg, "User added")
  918. user = tests.FakeUser(username="bar")
  919. with tests.user_set(self.app.application, user):
  920. # Get the merge status first so it's cached and can be refreshed
  921. csrf_token = self.get_csrf()
  922. data = {"requestid": self.request.uid, "csrf_token": csrf_token}
  923. output = self.app.post("/pv/pull-request/merge", data=data)
  924. self.assertEqual(output.status_code, 200)
  925. data = json.loads(output.get_data(as_text=True))
  926. self.assertEqual(
  927. data,
  928. {
  929. "code": "MERGE",
  930. "message": "The pull-request can be merged with "
  931. "a merge commit",
  932. "short_code": "With merge",
  933. },
  934. )
  935. output = self.app.post("/api/0/test/pull-request/1/rebase")
  936. self.assertEqual(output.status_code, 403)
  937. data = json.loads(output.get_data(as_text=True))
  938. self.assertEqual(
  939. data,
  940. {
  941. "error": "You are not authorized to rebase this pull-request",
  942. "error_code": "EREBASENOTALLOWED",
  943. },
  944. )
  945. # Add bar to fork repo so he can rebase while allow_rebase = False
  946. fork = pagure.lib.query.get_authorized_project(
  947. self.session, "test", user="foo"
  948. )
  949. msg = pagure.lib.query.add_user_to_project(
  950. session=self.session, project=fork, new_user="bar", user="foo"
  951. )
  952. self.session.commit()
  953. self.assertEqual(msg, "User added")
  954. output = self.app.post("/api/0/test/pull-request/1/rebase")
  955. self.assertEqual(output.status_code, 200)
  956. data = json.loads(output.get_data(as_text=True))
  957. self.assertEqual(data, {"message": "Pull-request rebased"})
  958. data = {"requestid": self.request.uid, "csrf_token": csrf_token}
  959. output = self.app.post("/pv/pull-request/merge", data=data)
  960. self.assertEqual(output.status_code, 200)
  961. data = json.loads(output.get_data(as_text=True))
  962. self.assertEqual(
  963. data,
  964. {
  965. "code": "FFORWARD",
  966. "message": "The pull-request can be merged and "
  967. "fast-forwarded",
  968. "short_code": "Ok",
  969. },
  970. )
  971. output = self.app.get("/test/pull-request/1")
  972. self.assertEqual(output.status_code, 200)
  973. output_text = output.get_data(as_text=True)
  974. self.assertIn("rebased onto", output_text)
  975. repo = pagure.lib.query._get_project(self.session, "test")
  976. self.assertEqual(repo.requests[0].comments[0].user.username, "bar")
  977. def test_rebase_api_api_logged_in(self):
  978. """Test the rebase PR API endpoint when using an API token and
  979. its outcome."""
  980. tests.create_tokens(self.session)
  981. tests.create_tokens_acl(self.session)
  982. headers = {"Authorization": "token aaabbbcccddd"}
  983. output = self.app.post(
  984. "/api/0/test/pull-request/1/rebase", headers=headers
  985. )
  986. self.assertEqual(output.status_code, 403)
  987. data = json.loads(output.get_data(as_text=True))
  988. self.assertEqual(
  989. data,
  990. {
  991. "error": "You are not authorized to rebase this pull-request",
  992. "error_code": "EREBASENOTALLOWED",
  993. },
  994. )
  995. # Add pingou to fork repo so he can rebase while allow_rebase = False
  996. fork = pagure.lib.query.get_authorized_project(
  997. self.session, "test", user="foo"
  998. )
  999. msg = pagure.lib.query.add_user_to_project(
  1000. session=self.session, project=fork, new_user="pingou", user="foo"
  1001. )
  1002. self.session.commit()
  1003. self.assertEqual(msg, "User added")
  1004. output = self.app.post(
  1005. "/api/0/test/pull-request/1/rebase", headers=headers
  1006. )
  1007. self.assertEqual(output.status_code, 200)
  1008. data = json.loads(output.get_data(as_text=True))
  1009. self.assertEqual(data, {"message": "Pull-request rebased"})
  1010. user = tests.FakeUser(username="pingou")
  1011. with tests.user_set(self.app.application, user):
  1012. data = {
  1013. "requestid": self.request.uid,
  1014. "csrf_token": self.get_csrf(),
  1015. }
  1016. output = self.app.post("/pv/pull-request/merge", data=data)
  1017. self.assertEqual(output.status_code, 200)
  1018. data = json.loads(output.get_data(as_text=True))
  1019. self.assertEqual(
  1020. data,
  1021. {
  1022. "code": "FFORWARD",
  1023. "message": "The pull-request can be merged and "
  1024. "fast-forwarded",
  1025. "short_code": "Ok",
  1026. },
  1027. )
  1028. def test_rebase_api_ui_logged_in_pull_request_author(self):
  1029. """Test the rebase PR API endpoint when logged in from the UI and
  1030. its outcome."""
  1031. user = tests.FakeUser(username="foo")
  1032. with tests.user_set(self.app.application, user):
  1033. # Get the merge status first so it's cached and can be refreshed
  1034. csrf_token = self.get_csrf()
  1035. data = {"requestid": self.request.uid, "csrf_token": csrf_token}
  1036. output = self.app.post("/pv/pull-request/merge", data=data)
  1037. self.assertEqual(output.status_code, 200)
  1038. data = json.loads(output.get_data(as_text=True))
  1039. self.assertEqual(
  1040. data,
  1041. {
  1042. "code": "MERGE",
  1043. "message": "The pull-request can be merged with "
  1044. "a merge commit",
  1045. "short_code": "With merge",
  1046. },
  1047. )
  1048. output = self.app.post("/api/0/test/pull-request/1/rebase")
  1049. self.assertEqual(output.status_code, 200)
  1050. data = json.loads(output.get_data(as_text=True))
  1051. self.assertEqual(data, {"message": "Pull-request rebased"})
  1052. data = {"requestid": self.request.uid, "csrf_token": csrf_token}
  1053. output = self.app.post("/pv/pull-request/merge", data=data)
  1054. self.assertEqual(output.status_code, 200)
  1055. data = json.loads(output.get_data(as_text=True))
  1056. self.assertEqual(
  1057. data,
  1058. {
  1059. "code": "FFORWARD",
  1060. "message": "The pull-request can be merged and "
  1061. "fast-forwarded",
  1062. "short_code": "Ok",
  1063. },
  1064. )
  1065. output = self.app.get("/test/pull-request/1")
  1066. self.assertEqual(output.status_code, 200)
  1067. output_text = output.get_data(as_text=True)
  1068. self.assertIn("rebased onto", output_text)
  1069. repo = pagure.lib.query._get_project(self.session, "test")
  1070. self.assertEqual(repo.requests[0].comments[0].user.username, "foo")
  1071. if __name__ == "__main__":
  1072. unittest.main(verbosity=2)