test_pagure_flask_api_fork_update.py 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002
  1. # -*- coding: utf-8 -*-
  2. """
  3. (c) 2019 - Copyright Red Hat Inc
  4. Authors:
  5. Pierre-Yves Chibon <pingou@pingoured.fr>
  6. """
  7. from __future__ import unicode_literals, absolute_import
  8. import arrow
  9. import copy
  10. import datetime
  11. import unittest
  12. import shutil
  13. import sys
  14. import time
  15. import os
  16. import flask
  17. import json
  18. import munch
  19. from mock import patch, MagicMock
  20. from sqlalchemy.exc import SQLAlchemyError
  21. sys.path.insert(
  22. 0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")
  23. )
  24. import pagure.lib.query
  25. import tests
  26. class PagureFlaskApiForkUpdatetests(tests.SimplePagureTest):
  27. """Tests for the flask API of pagure for updating a PR"""
  28. maxDiff = None
  29. @patch("pagure.lib.git.update_git", MagicMock(return_value=True))
  30. @patch("pagure.lib.notify.send_email", MagicMock(return_value=True))
  31. def setUp(self):
  32. """Set up the environnment, ran before every tests."""
  33. super(PagureFlaskApiForkUpdatetests, self).setUp()
  34. tests.create_projects(self.session)
  35. tests.add_content_git_repo(
  36. os.path.join(self.path, "repos", "test.git")
  37. )
  38. # Fork
  39. project = pagure.lib.query.get_authorized_project(self.session, "test")
  40. task = pagure.lib.query.fork_project(
  41. session=self.session, user="pingou", repo=project
  42. )
  43. self.session.commit()
  44. self.assertEqual(
  45. task.get(),
  46. {
  47. "endpoint": "ui_ns.view_repo",
  48. "repo": "test",
  49. "namespace": None,
  50. "username": "pingou",
  51. },
  52. )
  53. tests.add_readme_git_repo(
  54. os.path.join(self.path, "repos", "forks", "pingou", "test.git")
  55. )
  56. project = pagure.lib.query.get_authorized_project(self.session, "test")
  57. fork = pagure.lib.query.get_authorized_project(
  58. self.session, "test", user="pingou"
  59. )
  60. tests.create_tokens(self.session)
  61. tests.create_tokens_acl(self.session)
  62. req = pagure.lib.query.new_pull_request(
  63. session=self.session,
  64. repo_from=fork,
  65. branch_from="master",
  66. repo_to=project,
  67. branch_to="master",
  68. title="test pull-request",
  69. user="pingou",
  70. )
  71. self.session.commit()
  72. self.assertEqual(req.id, 1)
  73. self.assertEqual(req.title, "test pull-request")
  74. # Assert the PR is open
  75. self.session = pagure.lib.query.create_session(self.dbpath)
  76. project = pagure.lib.query.get_authorized_project(self.session, "test")
  77. self.assertEqual(len(project.requests), 1)
  78. self.assertEqual(project.requests[0].status, "Open")
  79. # Check how the PR renders in the API and the UI
  80. output = self.app.get("/api/0/test/pull-request/1")
  81. self.assertEqual(output.status_code, 200)
  82. output = self.app.get("/test/pull-request/1")
  83. self.assertEqual(output.status_code, 200)
  84. def test_api_pull_request_update_invalid_project_namespace(self):
  85. """Test api_pull_request_update method when the project doesn't exist."""
  86. headers = {"Authorization": "token aaabbbcccddd"}
  87. # Valid token, wrong project
  88. output = self.app.post(
  89. "/api/0/somenamespace/test3/pull-request/1", headers=headers
  90. )
  91. self.assertEqual(output.status_code, 401)
  92. data = json.loads(output.get_data(as_text=True))
  93. self.assertDictEqual(
  94. data,
  95. {
  96. "error": "Invalid or expired token. Please visit "
  97. "http://localhost.localdomain/settings#nav-api-tab to get or renew your "
  98. "API token.",
  99. "error_code": "EINVALIDTOK",
  100. },
  101. )
  102. def test_api_pull_request_update_invalid_project(self):
  103. """Test api_pull_request_update method when the project doesn't exist."""
  104. headers = {"Authorization": "token aaabbbcccddd"}
  105. # Invalid project
  106. output = self.app.post("/api/0/foo/pull-request/1", headers=headers)
  107. self.assertEqual(output.status_code, 404)
  108. data = json.loads(output.get_data(as_text=True))
  109. self.assertDictEqual(
  110. data, {"error": "Project not found", "error_code": "ENOPROJECT"}
  111. )
  112. def test_api_pull_request_update_invalid_project_token(self):
  113. """Test api_pull_request_update method when the token doesn't correspond
  114. to the project.
  115. """
  116. headers = {"Authorization": "token aaabbbcccddd"}
  117. # Valid token, wrong project
  118. output = self.app.post("/api/0/test2/pull-request/1", headers=headers)
  119. self.assertEqual(output.status_code, 401)
  120. data = json.loads(output.get_data(as_text=True))
  121. self.assertEqual(sorted(data.keys()), ["error", "error_code"])
  122. self.assertEqual(pagure.api.APIERROR.EINVALIDTOK.value, data["error"])
  123. self.assertEqual(
  124. pagure.api.APIERROR.EINVALIDTOK.name, data["error_code"]
  125. )
  126. def test_api_pull_request_update_invalid_pr(self):
  127. """Test api_assign_pull_request method when asking for an invalid PR"""
  128. headers = {"Authorization": "token aaabbbcccddd"}
  129. # Invalid PR id
  130. output = self.app.post("/api/0/test/pull-request/404", headers=headers)
  131. self.assertEqual(output.status_code, 404)
  132. data = json.loads(output.get_data(as_text=True))
  133. self.assertDictEqual(
  134. data, {"error": "Pull-Request not found", "error_code": "ENOREQ"}
  135. )
  136. def test_api_pull_request_update_no_input(self):
  137. """Test api_assign_pull_request method when no input is specified"""
  138. headers = {"Authorization": "token aaabbbcccddd"}
  139. # No input
  140. output = self.app.post("/api/0/test/pull-request/1", headers=headers)
  141. self.assertEqual(output.status_code, 400)
  142. data = json.loads(output.get_data(as_text=True))
  143. self.assertDictEqual(
  144. data,
  145. {
  146. "error": "Invalid or incomplete input submitted",
  147. "error_code": "EINVALIDREQ",
  148. "errors": {"title": ["This field is required."]},
  149. },
  150. )
  151. def test_api_pull_request_update_insufficient_input(self):
  152. """Test api_assign_pull_request method when no input is specified"""
  153. headers = {"Authorization": "token aaabbbcccddd"}
  154. data = {"initial_comment": "will not work"}
  155. # Missing the required title field
  156. output = self.app.post(
  157. "/api/0/test/pull-request/1", data=data, headers=headers
  158. )
  159. self.assertEqual(output.status_code, 400)
  160. data = json.loads(output.get_data(as_text=True))
  161. self.assertDictEqual(
  162. data,
  163. {
  164. "error": "Invalid or incomplete input submitted",
  165. "error_code": "EINVALIDREQ",
  166. "errors": {"title": ["This field is required."]},
  167. },
  168. )
  169. def test_api_pull_request_update_edited(self):
  170. """Test api_assign_pull_request method when with valid input"""
  171. headers = {"Authorization": "token aaabbbcccddd"}
  172. data = {
  173. "title": "edited test PR",
  174. "initial_comment": "Edited initial comment",
  175. }
  176. # Valid request
  177. output = self.app.post(
  178. "/api/0/test/pull-request/1", data=data, headers=headers
  179. )
  180. self.assertEqual(output.status_code, 200)
  181. data = json.loads(output.get_data(as_text=True))
  182. # Hard-code all the values that will change from a test to another
  183. # because either random or time-based
  184. data["date_created"] = "1551276260"
  185. data["last_updated"] = "1551276261"
  186. data["updated_on"] = "1551276260"
  187. data["commit_start"] = "5f5d609db65d447f77ba00e25afd17ba5053344b"
  188. data["commit_stop"] = "5f5d609db65d447f77ba00e25afd17ba5053344b"
  189. data["project"]["date_created"] = "1551276259"
  190. data["project"]["date_modified"] = "1551276259"
  191. data["repo_from"]["date_created"] = "1551276259"
  192. data["repo_from"]["date_modified"] = "1551276259"
  193. data["repo_from"]["parent"]["date_created"] = "1551276259"
  194. data["repo_from"]["parent"]["date_modified"] = "1551276259"
  195. data["uid"] = "a2bddecc8ea548e88c22a0df77670092"
  196. self.assertDictEqual(
  197. data,
  198. {
  199. "assignee": None,
  200. "branch": "master",
  201. "branch_from": "master",
  202. "cached_merge_status": "unknown",
  203. "closed_at": None,
  204. "closed_by": None,
  205. "comments": [],
  206. "commit_start": "5f5d609db65d447f77ba00e25afd17ba5053344b",
  207. "commit_stop": "5f5d609db65d447f77ba00e25afd17ba5053344b",
  208. "date_created": "1551276260",
  209. "full_url": "http://localhost.localdomain/test/pull-request/1",
  210. "id": 1,
  211. "initial_comment": "Edited initial comment",
  212. "last_updated": "1551276261",
  213. "project": {
  214. "access_groups": {
  215. "admin": [],
  216. "collaborator": [],
  217. "commit": [],
  218. "ticket": [],
  219. },
  220. "access_users": {
  221. "admin": [],
  222. "collaborator": [],
  223. "commit": [],
  224. "owner": ["pingou"],
  225. "ticket": [],
  226. },
  227. "close_status": [
  228. "Invalid",
  229. "Insufficient data",
  230. "Fixed",
  231. "Duplicate",
  232. ],
  233. "custom_keys": [],
  234. "date_created": "1551276259",
  235. "date_modified": "1551276259",
  236. "description": "test project #1",
  237. "full_url": "http://localhost.localdomain/test",
  238. "fullname": "test",
  239. "id": 1,
  240. "milestones": {},
  241. "name": "test",
  242. "namespace": None,
  243. "parent": None,
  244. "priorities": {},
  245. "tags": [],
  246. "url_path": "test",
  247. "user": {
  248. "fullname": "PY C",
  249. "full_url": "http://localhost.localdomain/user/pingou",
  250. "name": "pingou",
  251. "url_path": "user/pingou",
  252. },
  253. },
  254. "remote_git": None,
  255. "repo_from": {
  256. "access_groups": {
  257. "admin": [],
  258. "collaborator": [],
  259. "commit": [],
  260. "ticket": [],
  261. },
  262. "access_users": {
  263. "admin": [],
  264. "collaborator": [],
  265. "commit": [],
  266. "owner": ["pingou"],
  267. "ticket": [],
  268. },
  269. "close_status": [],
  270. "custom_keys": [],
  271. "date_created": "1551276259",
  272. "date_modified": "1551276259",
  273. "description": "test project #1",
  274. "full_url": "http://localhost.localdomain/fork/pingou/test",
  275. "fullname": "forks/pingou/test",
  276. "id": 4,
  277. "milestones": {},
  278. "name": "test",
  279. "namespace": None,
  280. "parent": {
  281. "access_groups": {
  282. "admin": [],
  283. "collaborator": [],
  284. "commit": [],
  285. "ticket": [],
  286. },
  287. "access_users": {
  288. "admin": [],
  289. "collaborator": [],
  290. "commit": [],
  291. "owner": ["pingou"],
  292. "ticket": [],
  293. },
  294. "close_status": [
  295. "Invalid",
  296. "Insufficient data",
  297. "Fixed",
  298. "Duplicate",
  299. ],
  300. "custom_keys": [],
  301. "date_created": "1551276259",
  302. "date_modified": "1551276259",
  303. "description": "test project #1",
  304. "full_url": "http://localhost.localdomain/test",
  305. "fullname": "test",
  306. "id": 1,
  307. "milestones": {},
  308. "name": "test",
  309. "namespace": None,
  310. "parent": None,
  311. "priorities": {},
  312. "tags": [],
  313. "url_path": "test",
  314. "user": {
  315. "fullname": "PY C",
  316. "full_url": "http://localhost.localdomain/user/pingou",
  317. "name": "pingou",
  318. "url_path": "user/pingou",
  319. },
  320. },
  321. "priorities": {},
  322. "tags": [],
  323. "url_path": "fork/pingou/test",
  324. "user": {
  325. "fullname": "PY C",
  326. "full_url": "http://localhost.localdomain/user/pingou",
  327. "name": "pingou",
  328. "url_path": "user/pingou",
  329. },
  330. },
  331. "status": "Open",
  332. "tags": [],
  333. "threshold_reached": None,
  334. "title": "edited test PR",
  335. "uid": "a2bddecc8ea548e88c22a0df77670092",
  336. "updated_on": "1551276260",
  337. "user": {
  338. "fullname": "PY C",
  339. "full_url": "http://localhost.localdomain/user/pingou",
  340. "name": "pingou",
  341. "url_path": "user/pingou",
  342. },
  343. },
  344. )
  345. def test_api_pull_request_update_edited_no_comment(self):
  346. """Test api_assign_pull_request method when with valid input"""
  347. headers = {"Authorization": "token aaabbbcccddd"}
  348. data = {"title": "edited test PR"}
  349. # Valid request
  350. output = self.app.post(
  351. "/api/0/test/pull-request/1", data=data, headers=headers
  352. )
  353. self.assertEqual(output.status_code, 200)
  354. data = json.loads(output.get_data(as_text=True))
  355. # Hard-code all the values that will change from a test to another
  356. # because either random or time-based
  357. data["date_created"] = "1551276260"
  358. data["last_updated"] = "1551276261"
  359. data["updated_on"] = "1551276260"
  360. data["commit_start"] = "5f5d609db65d447f77ba00e25afd17ba5053344b"
  361. data["commit_stop"] = "5f5d609db65d447f77ba00e25afd17ba5053344b"
  362. data["project"]["date_created"] = "1551276259"
  363. data["project"]["date_modified"] = "1551276259"
  364. data["repo_from"]["date_created"] = "1551276259"
  365. data["repo_from"]["date_modified"] = "1551276259"
  366. data["repo_from"]["parent"]["date_created"] = "1551276259"
  367. data["repo_from"]["parent"]["date_modified"] = "1551276259"
  368. data["uid"] = "a2bddecc8ea548e88c22a0df77670092"
  369. self.assertDictEqual(
  370. data,
  371. {
  372. "assignee": None,
  373. "branch": "master",
  374. "branch_from": "master",
  375. "cached_merge_status": "unknown",
  376. "closed_at": None,
  377. "closed_by": None,
  378. "comments": [],
  379. "commit_start": "5f5d609db65d447f77ba00e25afd17ba5053344b",
  380. "commit_stop": "5f5d609db65d447f77ba00e25afd17ba5053344b",
  381. "date_created": "1551276260",
  382. "full_url": "http://localhost.localdomain/test/pull-request/1",
  383. "id": 1,
  384. "initial_comment": "",
  385. "last_updated": "1551276261",
  386. "project": {
  387. "access_groups": {
  388. "admin": [],
  389. "collaborator": [],
  390. "commit": [],
  391. "ticket": [],
  392. },
  393. "access_users": {
  394. "admin": [],
  395. "collaborator": [],
  396. "commit": [],
  397. "owner": ["pingou"],
  398. "ticket": [],
  399. },
  400. "close_status": [
  401. "Invalid",
  402. "Insufficient data",
  403. "Fixed",
  404. "Duplicate",
  405. ],
  406. "custom_keys": [],
  407. "date_created": "1551276259",
  408. "date_modified": "1551276259",
  409. "description": "test project #1",
  410. "full_url": "http://localhost.localdomain/test",
  411. "fullname": "test",
  412. "id": 1,
  413. "milestones": {},
  414. "name": "test",
  415. "namespace": None,
  416. "parent": None,
  417. "priorities": {},
  418. "tags": [],
  419. "url_path": "test",
  420. "user": {
  421. "fullname": "PY C",
  422. "full_url": "http://localhost.localdomain/user/pingou",
  423. "name": "pingou",
  424. "url_path": "user/pingou",
  425. },
  426. },
  427. "remote_git": None,
  428. "repo_from": {
  429. "access_groups": {
  430. "admin": [],
  431. "collaborator": [],
  432. "commit": [],
  433. "ticket": [],
  434. },
  435. "access_users": {
  436. "admin": [],
  437. "collaborator": [],
  438. "commit": [],
  439. "owner": ["pingou"],
  440. "ticket": [],
  441. },
  442. "close_status": [],
  443. "custom_keys": [],
  444. "date_created": "1551276259",
  445. "date_modified": "1551276259",
  446. "description": "test project #1",
  447. "full_url": "http://localhost.localdomain/fork/pingou/test",
  448. "fullname": "forks/pingou/test",
  449. "id": 4,
  450. "milestones": {},
  451. "name": "test",
  452. "namespace": None,
  453. "parent": {
  454. "access_groups": {
  455. "admin": [],
  456. "collaborator": [],
  457. "commit": [],
  458. "ticket": [],
  459. },
  460. "access_users": {
  461. "admin": [],
  462. "collaborator": [],
  463. "commit": [],
  464. "owner": ["pingou"],
  465. "ticket": [],
  466. },
  467. "close_status": [
  468. "Invalid",
  469. "Insufficient data",
  470. "Fixed",
  471. "Duplicate",
  472. ],
  473. "custom_keys": [],
  474. "date_created": "1551276259",
  475. "date_modified": "1551276259",
  476. "description": "test project #1",
  477. "full_url": "http://localhost.localdomain/test",
  478. "fullname": "test",
  479. "id": 1,
  480. "milestones": {},
  481. "name": "test",
  482. "namespace": None,
  483. "parent": None,
  484. "priorities": {},
  485. "tags": [],
  486. "url_path": "test",
  487. "user": {
  488. "fullname": "PY C",
  489. "full_url": "http://localhost.localdomain/user/pingou",
  490. "name": "pingou",
  491. "url_path": "user/pingou",
  492. },
  493. },
  494. "priorities": {},
  495. "tags": [],
  496. "url_path": "fork/pingou/test",
  497. "user": {
  498. "fullname": "PY C",
  499. "full_url": "http://localhost.localdomain/user/pingou",
  500. "name": "pingou",
  501. "url_path": "user/pingou",
  502. },
  503. },
  504. "status": "Open",
  505. "tags": [],
  506. "threshold_reached": None,
  507. "title": "edited test PR",
  508. "uid": "a2bddecc8ea548e88c22a0df77670092",
  509. "updated_on": "1551276260",
  510. "user": {
  511. "fullname": "PY C",
  512. "full_url": "http://localhost.localdomain/user/pingou",
  513. "name": "pingou",
  514. "url_path": "user/pingou",
  515. },
  516. },
  517. )
  518. def test_api_pull_request_update_edited_linked(self):
  519. """Test api_assign_pull_request method when with valid input"""
  520. project = pagure.lib.query.get_authorized_project(self.session, "test")
  521. self.assertEqual(len(project.requests), 1)
  522. self.assertEqual(len(project.requests[0].related_issues), 0)
  523. self.assertEqual(len(project.issues), 0)
  524. # Create issues to link to
  525. msg = pagure.lib.query.new_issue(
  526. session=self.session,
  527. repo=project,
  528. title="tést íssüé",
  529. content="We should work on this",
  530. user="pingou",
  531. )
  532. self.session.commit()
  533. self.assertEqual(msg.title, "tést íssüé")
  534. headers = {"Authorization": "token aaabbbcccddd"}
  535. data = {
  536. "title": "edited test PR",
  537. "initial_comment": "Edited initial comment\n\n"
  538. "this PR fixes #2 \n\nThanks",
  539. }
  540. # Valid request
  541. output = self.app.post(
  542. "/api/0/test/pull-request/1", data=data, headers=headers
  543. )
  544. self.assertEqual(output.status_code, 200)
  545. data = json.loads(output.get_data(as_text=True))
  546. # Hard-code all the values that will change from a test to another
  547. # because either random or time-based
  548. data["date_created"] = "1551276260"
  549. data["last_updated"] = "1551276261"
  550. data["updated_on"] = "1551276260"
  551. data["commit_start"] = "5f5d609db65d447f77ba00e25afd17ba5053344b"
  552. data["commit_stop"] = "5f5d609db65d447f77ba00e25afd17ba5053344b"
  553. data["project"]["date_created"] = "1551276259"
  554. data["project"]["date_modified"] = "1551276259"
  555. data["repo_from"]["date_created"] = "1551276259"
  556. data["repo_from"]["date_modified"] = "1551276259"
  557. data["repo_from"]["parent"]["date_created"] = "1551276259"
  558. data["repo_from"]["parent"]["date_modified"] = "1551276259"
  559. data["uid"] = "a2bddecc8ea548e88c22a0df77670092"
  560. self.assertDictEqual(
  561. data,
  562. {
  563. "assignee": None,
  564. "branch": "master",
  565. "branch_from": "master",
  566. "cached_merge_status": "unknown",
  567. "closed_at": None,
  568. "closed_by": None,
  569. "comments": [],
  570. "commit_start": "5f5d609db65d447f77ba00e25afd17ba5053344b",
  571. "commit_stop": "5f5d609db65d447f77ba00e25afd17ba5053344b",
  572. "date_created": "1551276260",
  573. "full_url": "http://localhost.localdomain/test/pull-request/1",
  574. "id": 1,
  575. "initial_comment": "Edited initial comment\n\nthis PR "
  576. "fixes #2 \n\nThanks",
  577. "last_updated": "1551276261",
  578. "project": {
  579. "access_groups": {
  580. "admin": [],
  581. "collaborator": [],
  582. "commit": [],
  583. "ticket": [],
  584. },
  585. "access_users": {
  586. "admin": [],
  587. "collaborator": [],
  588. "commit": [],
  589. "owner": ["pingou"],
  590. "ticket": [],
  591. },
  592. "close_status": [
  593. "Invalid",
  594. "Insufficient data",
  595. "Fixed",
  596. "Duplicate",
  597. ],
  598. "custom_keys": [],
  599. "date_created": "1551276259",
  600. "date_modified": "1551276259",
  601. "description": "test project #1",
  602. "full_url": "http://localhost.localdomain/test",
  603. "fullname": "test",
  604. "id": 1,
  605. "milestones": {},
  606. "name": "test",
  607. "namespace": None,
  608. "parent": None,
  609. "priorities": {},
  610. "tags": [],
  611. "url_path": "test",
  612. "user": {
  613. "fullname": "PY C",
  614. "full_url": "http://localhost.localdomain/user/pingou",
  615. "name": "pingou",
  616. "url_path": "user/pingou",
  617. },
  618. },
  619. "remote_git": None,
  620. "repo_from": {
  621. "access_groups": {
  622. "admin": [],
  623. "collaborator": [],
  624. "commit": [],
  625. "ticket": [],
  626. },
  627. "access_users": {
  628. "admin": [],
  629. "collaborator": [],
  630. "commit": [],
  631. "owner": ["pingou"],
  632. "ticket": [],
  633. },
  634. "close_status": [],
  635. "custom_keys": [],
  636. "date_created": "1551276259",
  637. "date_modified": "1551276259",
  638. "description": "test project #1",
  639. "full_url": "http://localhost.localdomain/fork/pingou/test",
  640. "fullname": "forks/pingou/test",
  641. "id": 4,
  642. "milestones": {},
  643. "name": "test",
  644. "namespace": None,
  645. "parent": {
  646. "access_groups": {
  647. "admin": [],
  648. "collaborator": [],
  649. "commit": [],
  650. "ticket": [],
  651. },
  652. "access_users": {
  653. "admin": [],
  654. "collaborator": [],
  655. "commit": [],
  656. "owner": ["pingou"],
  657. "ticket": [],
  658. },
  659. "close_status": [
  660. "Invalid",
  661. "Insufficient data",
  662. "Fixed",
  663. "Duplicate",
  664. ],
  665. "custom_keys": [],
  666. "date_created": "1551276259",
  667. "date_modified": "1551276259",
  668. "description": "test project #1",
  669. "full_url": "http://localhost.localdomain/test",
  670. "fullname": "test",
  671. "id": 1,
  672. "milestones": {},
  673. "name": "test",
  674. "namespace": None,
  675. "parent": None,
  676. "priorities": {},
  677. "tags": [],
  678. "url_path": "test",
  679. "user": {
  680. "fullname": "PY C",
  681. "full_url": "http://localhost.localdomain/user/pingou",
  682. "name": "pingou",
  683. "url_path": "user/pingou",
  684. },
  685. },
  686. "priorities": {},
  687. "tags": [],
  688. "url_path": "fork/pingou/test",
  689. "user": {
  690. "fullname": "PY C",
  691. "full_url": "http://localhost.localdomain/user/pingou",
  692. "name": "pingou",
  693. "url_path": "user/pingou",
  694. },
  695. },
  696. "status": "Open",
  697. "tags": [],
  698. "threshold_reached": None,
  699. "title": "edited test PR",
  700. "uid": "a2bddecc8ea548e88c22a0df77670092",
  701. "updated_on": "1551276260",
  702. "user": {
  703. "fullname": "PY C",
  704. "full_url": "http://localhost.localdomain/user/pingou",
  705. "name": "pingou",
  706. "url_path": "user/pingou",
  707. },
  708. },
  709. )
  710. project = pagure.lib.query.get_authorized_project(self.session, "test")
  711. self.assertEqual(len(project.requests), 1)
  712. self.assertEqual(len(project.requests[0].related_issues), 1)
  713. self.assertEqual(len(project.issues), 1)
  714. self.assertEqual(len(project.issues[0].related_prs), 1)
  715. class PagureFlaskApiForkUpdateNoCommitterTests(tests.SimplePagureTest):
  716. """Tests for the flask API of pagure for updating a PR
  717. when the PR owner is not a committer in the target project
  718. but he is the owner of the PR.
  719. """
  720. maxDiff = None
  721. @patch("pagure.lib.git.update_git", MagicMock(return_value=True))
  722. @patch("pagure.lib.notify.send_email", MagicMock(return_value=True))
  723. def setUp(self):
  724. """Set up the environnment, ran before every tests.
  725. Pingou is the owner of the target project (test).
  726. Maja has a fork of the test project and creates
  727. a PR against Pingou parent project.
  728. She should be able to update her own PR.
  729. """
  730. super(PagureFlaskApiForkUpdateNoCommitterTests, self).setUp()
  731. tests.create_projects(self.session)
  732. tests.add_content_git_repo(
  733. os.path.join(self.path, "repos", "test.git")
  734. )
  735. # Fork
  736. tests.create_user(self.session, "maja", "Maja M.", ["mm@f.com"])
  737. project = pagure.lib.query.get_authorized_project(self.session, "test")
  738. task = pagure.lib.query.fork_project(
  739. session=self.session, user="maja", repo=project
  740. )
  741. self.session.commit()
  742. self.assertEqual(
  743. task.get(),
  744. {
  745. "endpoint": "ui_ns.view_repo",
  746. "repo": "test",
  747. "namespace": None,
  748. "username": "maja",
  749. },
  750. )
  751. tests.add_readme_git_repo(
  752. os.path.join(self.path, "repos", "forks", "maja", "test.git")
  753. )
  754. project = pagure.lib.query.get_authorized_project(self.session, "test")
  755. fork = pagure.lib.query.get_authorized_project(
  756. self.session, "test", user="maja"
  757. )
  758. tests.create_tokens(self.session)
  759. tests.create_tokens_acl(self.session)
  760. req = pagure.lib.query.new_pull_request(
  761. session=self.session,
  762. repo_from=fork,
  763. branch_from="master",
  764. repo_to=project,
  765. branch_to="master",
  766. title="test pull-request",
  767. user="maja",
  768. )
  769. self.session.commit()
  770. self.assertEqual(req.id, 1)
  771. self.assertEqual(req.title, "test pull-request")
  772. # Assert the PR is open
  773. self.session = pagure.lib.query.create_session(self.dbpath)
  774. project = pagure.lib.query.get_authorized_project(self.session, "test")
  775. self.assertEqual(len(project.requests), 1)
  776. self.assertEqual(project.requests[0].status, "Open")
  777. # Check how the PR renders in the API and the UI
  778. output = self.app.get("/api/0/test/pull-request/1")
  779. self.assertEqual(output.status_code, 200)
  780. output = self.app.get("/test/pull-request/1")
  781. self.assertEqual(output.status_code, 200)
  782. def test_api_pull_request_updated_by_owner(self):
  783. """Owners of PRs can update their own PRs."""
  784. headers = {"Authorization": "token aaabbbcccddd"}
  785. data = {
  786. "title": "edited test PR",
  787. "initial_comment": "Edited initial comment",
  788. }
  789. user = tests.FakeUser()
  790. user.username = "maja"
  791. with tests.user_set(self.app.application, user):
  792. output = self.app.post(
  793. "/api/0/test/pull-request/1", data=data, headers=headers
  794. )
  795. self.assertEqual(output.status_code, 200)
  796. def test_api_pull_request_not_updated_by_other_user(self):
  797. """No repo committers or PR owners are allowed to update PR."""
  798. headers = {"Authorization": "token aaabbbcccddd"}
  799. data = {
  800. "title": "edited test PR",
  801. "initial_comment": "Edited initial comment",
  802. }
  803. tests.create_user(self.session, "other", "Another User", ["au@rh.com"])
  804. user = tests.FakeUser()
  805. user.username = "other"
  806. with tests.user_set(self.app.application, user):
  807. output = self.app.post(
  808. "/api/0/test/pull-request/1", data=data, headers=headers
  809. )
  810. self.assertEqual(output.status_code, 403)
  811. class PagureFlaskApiForkUpdateNoCommitterTests(tests.SimplePagureTest):
  812. """Tests for the flask API of pagure for updating a PR
  813. when the PR owner is not a committer in the target project
  814. but he is the owner of the PR.
  815. """
  816. maxDiff = None
  817. @patch("pagure.lib.git.update_git", MagicMock(return_value=True))
  818. @patch("pagure.lib.notify.send_email", MagicMock(return_value=True))
  819. def setUp(self):
  820. """Set up the environnment, ran before every tests.
  821. Pingou is the owner of the target project (test).
  822. Maja has a fork of the test project and creates
  823. a PR against Pingou parent project.
  824. She should be able to update her own PR.
  825. """
  826. super(PagureFlaskApiForkUpdateNoCommitterTests, self).setUp()
  827. tests.create_projects(self.session)
  828. tests.add_content_git_repo(
  829. os.path.join(self.path, "repos", "test.git")
  830. )
  831. # Fork
  832. tests.create_user(self.session, "maja", "Maja M.", ["mm@f.com"])
  833. project = pagure.lib.query.get_authorized_project(self.session, "test")
  834. task = pagure.lib.query.fork_project(
  835. session=self.session, user="maja", repo=project
  836. )
  837. self.session.commit()
  838. self.assertEqual(
  839. task.get(),
  840. {
  841. "endpoint": "ui_ns.view_repo",
  842. "repo": "test",
  843. "namespace": None,
  844. "username": "maja",
  845. },
  846. )
  847. tests.add_readme_git_repo(
  848. os.path.join(self.path, "repos", "forks", "maja", "test.git")
  849. )
  850. project = pagure.lib.query.get_authorized_project(self.session, "test")
  851. fork = pagure.lib.query.get_authorized_project(
  852. self.session, "test", user="maja"
  853. )
  854. tests.create_tokens(self.session)
  855. tests.create_tokens_acl(self.session)
  856. req = pagure.lib.query.new_pull_request(
  857. session=self.session,
  858. repo_from=fork,
  859. branch_from="master",
  860. repo_to=project,
  861. branch_to="master",
  862. title="test pull-request",
  863. user="maja",
  864. )
  865. self.session.commit()
  866. self.assertEqual(req.id, 1)
  867. self.assertEqual(req.title, "test pull-request")
  868. # Assert the PR is open
  869. self.session = pagure.lib.query.create_session(self.dbpath)
  870. project = pagure.lib.query.get_authorized_project(self.session, "test")
  871. self.assertEqual(len(project.requests), 1)
  872. self.assertEqual(project.requests[0].status, "Open")
  873. # Check how the PR renders in the API and the UI
  874. output = self.app.get("/api/0/test/pull-request/1")
  875. self.assertEqual(output.status_code, 200)
  876. output = self.app.get("/test/pull-request/1")
  877. self.assertEqual(output.status_code, 200)
  878. def test_api_pull_request_updated_by_owner(self):
  879. """Owners of PRs can update their own PRs."""
  880. headers = {"Authorization": "token aaabbbcccddd"}
  881. data = {
  882. "title": "edited test PR",
  883. "initial_comment": "Edited initial comment",
  884. }
  885. user = tests.FakeUser()
  886. user.username = "maja"
  887. with tests.user_set(self.app.application, user):
  888. output = self.app.post(
  889. "/api/0/test/pull-request/1", data=data, headers=headers
  890. )
  891. self.assertEqual(output.status_code, 200)
  892. def test_api_pull_request_not_updated_by_other_user(self):
  893. """No repo committers or PR owners are allowed to update PR."""
  894. headers = {"Authorization": "token aaabbbcccddd"}
  895. data = {
  896. "title": "edited test PR",
  897. "initial_comment": "Edited initial comment",
  898. }
  899. tests.create_user(self.session, "other", "Another User", ["au@rh.com"])
  900. user = tests.FakeUser()
  901. user.username = "other"
  902. with tests.user_set(self.app.application, user):
  903. output = self.app.post(
  904. "/api/0/test/pull-request/1", data=data, headers=headers
  905. )
  906. self.assertEqual(output.status_code, 403)
  907. if __name__ == "__main__":
  908. unittest.main(verbosity=2)