You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

api_pull_review_test.go 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. // Copyright 2020 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package integrations
  5. import (
  6. "fmt"
  7. "net/http"
  8. "testing"
  9. "code.gitea.io/gitea/models"
  10. "code.gitea.io/gitea/models/unittest"
  11. "code.gitea.io/gitea/modules/json"
  12. api "code.gitea.io/gitea/modules/structs"
  13. "github.com/stretchr/testify/assert"
  14. )
  15. func TestAPIPullReview(t *testing.T) {
  16. defer prepareTestEnv(t)()
  17. pullIssue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 3}).(*models.Issue)
  18. assert.NoError(t, pullIssue.LoadAttributes())
  19. repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: pullIssue.RepoID}).(*models.Repository)
  20. // test ListPullReviews
  21. session := loginUser(t, "user2")
  22. token := getTokenForLoggedInUser(t, session)
  23. req := NewRequestf(t, http.MethodGet, "/api/v1/repos/%s/%s/pulls/%d/reviews?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token)
  24. resp := session.MakeRequest(t, req, http.StatusOK)
  25. var reviews []*api.PullReview
  26. DecodeJSON(t, resp, &reviews)
  27. if !assert.Len(t, reviews, 6) {
  28. return
  29. }
  30. for _, r := range reviews {
  31. assert.EqualValues(t, pullIssue.HTMLURL(), r.HTMLPullURL)
  32. }
  33. assert.EqualValues(t, 8, reviews[3].ID)
  34. assert.EqualValues(t, "APPROVED", reviews[3].State)
  35. assert.EqualValues(t, 0, reviews[3].CodeCommentsCount)
  36. assert.True(t, reviews[3].Stale)
  37. assert.False(t, reviews[3].Official)
  38. assert.EqualValues(t, 10, reviews[5].ID)
  39. assert.EqualValues(t, "REQUEST_CHANGES", reviews[5].State)
  40. assert.EqualValues(t, 1, reviews[5].CodeCommentsCount)
  41. assert.EqualValues(t, -1, reviews[5].Reviewer.ID) // ghost user
  42. assert.False(t, reviews[5].Stale)
  43. assert.True(t, reviews[5].Official)
  44. // test GetPullReview
  45. req = NewRequestf(t, http.MethodGet, "/api/v1/repos/%s/%s/pulls/%d/reviews/%d?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, reviews[3].ID, token)
  46. resp = session.MakeRequest(t, req, http.StatusOK)
  47. var review api.PullReview
  48. DecodeJSON(t, resp, &review)
  49. assert.EqualValues(t, *reviews[3], review)
  50. req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/pulls/%d/reviews/%d?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, reviews[5].ID, token)
  51. resp = session.MakeRequest(t, req, http.StatusOK)
  52. DecodeJSON(t, resp, &review)
  53. assert.EqualValues(t, *reviews[5], review)
  54. // test GetPullReviewComments
  55. comment := unittest.AssertExistsAndLoadBean(t, &models.Comment{ID: 7}).(*models.Comment)
  56. req = NewRequestf(t, http.MethodGet, "/api/v1/repos/%s/%s/pulls/%d/reviews/%d/comments?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, 10, token)
  57. resp = session.MakeRequest(t, req, http.StatusOK)
  58. var reviewComments []*api.PullReviewComment
  59. DecodeJSON(t, resp, &reviewComments)
  60. assert.Len(t, reviewComments, 1)
  61. assert.EqualValues(t, "Ghost", reviewComments[0].Poster.UserName)
  62. assert.EqualValues(t, "a review from a deleted user", reviewComments[0].Body)
  63. assert.EqualValues(t, comment.ID, reviewComments[0].ID)
  64. assert.EqualValues(t, comment.UpdatedUnix, reviewComments[0].Updated.Unix())
  65. assert.EqualValues(t, comment.HTMLURL(), reviewComments[0].HTMLURL)
  66. // test CreatePullReview
  67. req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token), &api.CreatePullReviewOptions{
  68. Body: "body1",
  69. // Event: "" # will result in PENDING
  70. Comments: []api.CreatePullReviewComment{{
  71. Path: "README.md",
  72. Body: "first new line",
  73. OldLineNum: 0,
  74. NewLineNum: 1,
  75. }, {
  76. Path: "README.md",
  77. Body: "first old line",
  78. OldLineNum: 1,
  79. NewLineNum: 0,
  80. }, {
  81. Path: "iso-8859-1.txt",
  82. Body: "this line contains a non-utf-8 character",
  83. OldLineNum: 0,
  84. NewLineNum: 1,
  85. },
  86. },
  87. })
  88. resp = session.MakeRequest(t, req, http.StatusOK)
  89. DecodeJSON(t, resp, &review)
  90. assert.EqualValues(t, 6, review.ID)
  91. assert.EqualValues(t, "PENDING", review.State)
  92. assert.EqualValues(t, 3, review.CodeCommentsCount)
  93. // test SubmitPullReview
  94. req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews/%d?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, review.ID, token), &api.SubmitPullReviewOptions{
  95. Event: "APPROVED",
  96. Body: "just two nits",
  97. })
  98. resp = session.MakeRequest(t, req, http.StatusOK)
  99. DecodeJSON(t, resp, &review)
  100. assert.EqualValues(t, 6, review.ID)
  101. assert.EqualValues(t, "APPROVED", review.State)
  102. assert.EqualValues(t, 3, review.CodeCommentsCount)
  103. // test dismiss review
  104. req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews/%d/dismissals?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, review.ID, token), &api.DismissPullReviewOptions{
  105. Message: "test",
  106. })
  107. resp = session.MakeRequest(t, req, http.StatusOK)
  108. DecodeJSON(t, resp, &review)
  109. assert.EqualValues(t, 6, review.ID)
  110. assert.True(t, review.Dismissed)
  111. // test dismiss review
  112. req = NewRequest(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews/%d/undismissals?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, review.ID, token))
  113. resp = session.MakeRequest(t, req, http.StatusOK)
  114. DecodeJSON(t, resp, &review)
  115. assert.EqualValues(t, 6, review.ID)
  116. assert.False(t, review.Dismissed)
  117. // test DeletePullReview
  118. req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token), &api.CreatePullReviewOptions{
  119. Body: "just a comment",
  120. Event: "COMMENT",
  121. })
  122. resp = session.MakeRequest(t, req, http.StatusOK)
  123. DecodeJSON(t, resp, &review)
  124. assert.EqualValues(t, "COMMENT", review.State)
  125. assert.EqualValues(t, 0, review.CodeCommentsCount)
  126. req = NewRequestf(t, http.MethodDelete, "/api/v1/repos/%s/%s/pulls/%d/reviews/%d?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, review.ID, token)
  127. session.MakeRequest(t, req, http.StatusNoContent)
  128. // test CreatePullReview Comment without body but with comments
  129. req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token), &api.CreatePullReviewOptions{
  130. // Body: "",
  131. Event: "COMMENT",
  132. Comments: []api.CreatePullReviewComment{{
  133. Path: "README.md",
  134. Body: "first new line",
  135. OldLineNum: 0,
  136. NewLineNum: 1,
  137. }, {
  138. Path: "README.md",
  139. Body: "first old line",
  140. OldLineNum: 1,
  141. NewLineNum: 0,
  142. },
  143. },
  144. })
  145. var commentReview api.PullReview
  146. resp = session.MakeRequest(t, req, http.StatusOK)
  147. DecodeJSON(t, resp, &commentReview)
  148. assert.EqualValues(t, "COMMENT", commentReview.State)
  149. assert.EqualValues(t, 2, commentReview.CodeCommentsCount)
  150. assert.EqualValues(t, "", commentReview.Body)
  151. assert.EqualValues(t, false, commentReview.Dismissed)
  152. // test CreatePullReview Comment with body but without comments
  153. commentBody := "This is a body of the comment."
  154. req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token), &api.CreatePullReviewOptions{
  155. Body: commentBody,
  156. Event: "COMMENT",
  157. Comments: []api.CreatePullReviewComment{},
  158. })
  159. resp = session.MakeRequest(t, req, http.StatusOK)
  160. DecodeJSON(t, resp, &commentReview)
  161. assert.EqualValues(t, "COMMENT", commentReview.State)
  162. assert.EqualValues(t, 0, commentReview.CodeCommentsCount)
  163. assert.EqualValues(t, commentBody, commentReview.Body)
  164. assert.EqualValues(t, false, commentReview.Dismissed)
  165. // test CreatePullReview Comment without body and no comments
  166. req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token), &api.CreatePullReviewOptions{
  167. Body: "",
  168. Event: "COMMENT",
  169. Comments: []api.CreatePullReviewComment{},
  170. })
  171. resp = session.MakeRequest(t, req, http.StatusUnprocessableEntity)
  172. errMap := make(map[string]interface{})
  173. json.Unmarshal(resp.Body.Bytes(), &errMap)
  174. assert.EqualValues(t, "review event COMMENT requires a body or a comment", errMap["message"].(string))
  175. // test get review requests
  176. // to make it simple, use same api with get review
  177. pullIssue12 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 12}).(*models.Issue)
  178. assert.NoError(t, pullIssue12.LoadAttributes())
  179. repo3 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: pullIssue12.RepoID}).(*models.Repository)
  180. req = NewRequestf(t, http.MethodGet, "/api/v1/repos/%s/%s/pulls/%d/reviews?token=%s", repo3.OwnerName, repo3.Name, pullIssue12.Index, token)
  181. resp = session.MakeRequest(t, req, http.StatusOK)
  182. DecodeJSON(t, resp, &reviews)
  183. assert.EqualValues(t, 11, reviews[0].ID)
  184. assert.EqualValues(t, "REQUEST_REVIEW", reviews[0].State)
  185. assert.EqualValues(t, 0, reviews[0].CodeCommentsCount)
  186. assert.False(t, reviews[0].Stale)
  187. assert.True(t, reviews[0].Official)
  188. assert.EqualValues(t, "test_team", reviews[0].ReviewerTeam.Name)
  189. assert.EqualValues(t, 12, reviews[1].ID)
  190. assert.EqualValues(t, "REQUEST_REVIEW", reviews[1].State)
  191. assert.EqualValues(t, 0, reviews[0].CodeCommentsCount)
  192. assert.False(t, reviews[1].Stale)
  193. assert.True(t, reviews[1].Official)
  194. assert.EqualValues(t, 1, reviews[1].Reviewer.ID)
  195. }
  196. func TestAPIPullReviewRequest(t *testing.T) {
  197. defer prepareTestEnv(t)()
  198. pullIssue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 3}).(*models.Issue)
  199. assert.NoError(t, pullIssue.LoadAttributes())
  200. repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: pullIssue.RepoID}).(*models.Repository)
  201. // Test add Review Request
  202. session := loginUser(t, "user2")
  203. token := getTokenForLoggedInUser(t, session)
  204. req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token), &api.PullReviewRequestOptions{
  205. Reviewers: []string{"user4@example.com", "user8"},
  206. })
  207. session.MakeRequest(t, req, http.StatusCreated)
  208. // poster of pr can't be reviewer
  209. req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token), &api.PullReviewRequestOptions{
  210. Reviewers: []string{"user1"},
  211. })
  212. session.MakeRequest(t, req, http.StatusUnprocessableEntity)
  213. // test user not exist
  214. req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token), &api.PullReviewRequestOptions{
  215. Reviewers: []string{"testOther"},
  216. })
  217. session.MakeRequest(t, req, http.StatusNotFound)
  218. // Test Remove Review Request
  219. session2 := loginUser(t, "user4")
  220. token2 := getTokenForLoggedInUser(t, session2)
  221. req = NewRequestWithJSON(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token2), &api.PullReviewRequestOptions{
  222. Reviewers: []string{"user4"},
  223. })
  224. session.MakeRequest(t, req, http.StatusNoContent)
  225. // doer is not admin
  226. req = NewRequestWithJSON(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token2), &api.PullReviewRequestOptions{
  227. Reviewers: []string{"user8"},
  228. })
  229. session.MakeRequest(t, req, http.StatusUnprocessableEntity)
  230. req = NewRequestWithJSON(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token), &api.PullReviewRequestOptions{
  231. Reviewers: []string{"user8"},
  232. })
  233. session.MakeRequest(t, req, http.StatusNoContent)
  234. // Test team review request
  235. pullIssue12 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 12}).(*models.Issue)
  236. assert.NoError(t, pullIssue12.LoadAttributes())
  237. repo3 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: pullIssue12.RepoID}).(*models.Repository)
  238. // Test add Team Review Request
  239. req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers?token=%s", repo3.OwnerName, repo3.Name, pullIssue12.Index, token), &api.PullReviewRequestOptions{
  240. TeamReviewers: []string{"team1", "owners"},
  241. })
  242. session.MakeRequest(t, req, http.StatusCreated)
  243. // Test add Team Review Request to not allowned
  244. req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers?token=%s", repo3.OwnerName, repo3.Name, pullIssue12.Index, token), &api.PullReviewRequestOptions{
  245. TeamReviewers: []string{"test_team"},
  246. })
  247. session.MakeRequest(t, req, http.StatusUnprocessableEntity)
  248. // Test add Team Review Request to not exist
  249. req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers?token=%s", repo3.OwnerName, repo3.Name, pullIssue12.Index, token), &api.PullReviewRequestOptions{
  250. TeamReviewers: []string{"not_exist_team"},
  251. })
  252. session.MakeRequest(t, req, http.StatusNotFound)
  253. // Test Remove team Review Request
  254. req = NewRequestWithJSON(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers?token=%s", repo3.OwnerName, repo3.Name, pullIssue12.Index, token), &api.PullReviewRequestOptions{
  255. TeamReviewers: []string{"team1"},
  256. })
  257. session.MakeRequest(t, req, http.StatusNoContent)
  258. // empty request test
  259. req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers?token=%s", repo3.OwnerName, repo3.Name, pullIssue12.Index, token), &api.PullReviewRequestOptions{})
  260. session.MakeRequest(t, req, http.StatusCreated)
  261. req = NewRequestWithJSON(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers?token=%s", repo3.OwnerName, repo3.Name, pullIssue12.Index, token), &api.PullReviewRequestOptions{})
  262. session.MakeRequest(t, req, http.StatusNoContent)
  263. }