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