code.gitea.io/gitea@v1.22.3/tests/integration/api_pull_review_test.go (about) 1 // Copyright 2020 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package integration 5 6 import ( 7 "fmt" 8 "net/http" 9 "testing" 10 11 auth_model "code.gitea.io/gitea/models/auth" 12 "code.gitea.io/gitea/models/db" 13 issues_model "code.gitea.io/gitea/models/issues" 14 repo_model "code.gitea.io/gitea/models/repo" 15 "code.gitea.io/gitea/models/unittest" 16 user_model "code.gitea.io/gitea/models/user" 17 "code.gitea.io/gitea/modules/json" 18 api "code.gitea.io/gitea/modules/structs" 19 issue_service "code.gitea.io/gitea/services/issue" 20 "code.gitea.io/gitea/tests" 21 22 "github.com/stretchr/testify/assert" 23 "xorm.io/builder" 24 ) 25 26 func TestAPIPullReview(t *testing.T) { 27 defer tests.PrepareTestEnv(t)() 28 pullIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3}) 29 assert.NoError(t, pullIssue.LoadAttributes(db.DefaultContext)) 30 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue.RepoID}) 31 32 // test ListPullReviews 33 session := loginUser(t, "user2") 34 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) 35 req := NewRequestf(t, http.MethodGet, "/api/v1/repos/%s/%s/pulls/%d/reviews", repo.OwnerName, repo.Name, pullIssue.Index). 36 AddTokenAuth(token) 37 resp := MakeRequest(t, req, http.StatusOK) 38 39 var reviews []*api.PullReview 40 DecodeJSON(t, resp, &reviews) 41 if !assert.Len(t, reviews, 8) { 42 return 43 } 44 for _, r := range reviews { 45 assert.EqualValues(t, pullIssue.HTMLURL(), r.HTMLPullURL) 46 } 47 assert.EqualValues(t, 8, reviews[3].ID) 48 assert.EqualValues(t, "APPROVED", reviews[3].State) 49 assert.EqualValues(t, 0, reviews[3].CodeCommentsCount) 50 assert.True(t, reviews[3].Stale) 51 assert.False(t, reviews[3].Official) 52 53 assert.EqualValues(t, 10, reviews[5].ID) 54 assert.EqualValues(t, "REQUEST_CHANGES", reviews[5].State) 55 assert.EqualValues(t, 1, reviews[5].CodeCommentsCount) 56 assert.EqualValues(t, -1, reviews[5].Reviewer.ID) // ghost user 57 assert.False(t, reviews[5].Stale) 58 assert.True(t, reviews[5].Official) 59 60 // test GetPullReview 61 req = NewRequestf(t, http.MethodGet, "/api/v1/repos/%s/%s/pulls/%d/reviews/%d", repo.OwnerName, repo.Name, pullIssue.Index, reviews[3].ID). 62 AddTokenAuth(token) 63 resp = MakeRequest(t, req, http.StatusOK) 64 var review api.PullReview 65 DecodeJSON(t, resp, &review) 66 assert.EqualValues(t, *reviews[3], review) 67 68 req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/pulls/%d/reviews/%d", repo.OwnerName, repo.Name, pullIssue.Index, reviews[5].ID). 69 AddTokenAuth(token) 70 resp = MakeRequest(t, req, http.StatusOK) 71 DecodeJSON(t, resp, &review) 72 assert.EqualValues(t, *reviews[5], review) 73 74 // test GetPullReviewComments 75 comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 7}) 76 req = NewRequestf(t, http.MethodGet, "/api/v1/repos/%s/%s/pulls/%d/reviews/%d/comments", repo.OwnerName, repo.Name, pullIssue.Index, 10). 77 AddTokenAuth(token) 78 resp = MakeRequest(t, req, http.StatusOK) 79 var reviewComments []*api.PullReviewComment 80 DecodeJSON(t, resp, &reviewComments) 81 assert.Len(t, reviewComments, 1) 82 assert.EqualValues(t, "Ghost", reviewComments[0].Poster.UserName) 83 assert.EqualValues(t, "a review from a deleted user", reviewComments[0].Body) 84 assert.EqualValues(t, comment.ID, reviewComments[0].ID) 85 assert.EqualValues(t, comment.UpdatedUnix, reviewComments[0].Updated.Unix()) 86 assert.EqualValues(t, comment.HTMLURL(db.DefaultContext), reviewComments[0].HTMLURL) 87 88 // test CreatePullReview 89 req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews", repo.OwnerName, repo.Name, pullIssue.Index), &api.CreatePullReviewOptions{ 90 Body: "body1", 91 // Event: "" # will result in PENDING 92 Comments: []api.CreatePullReviewComment{ 93 { 94 Path: "README.md", 95 Body: "first new line", 96 OldLineNum: 0, 97 NewLineNum: 1, 98 }, { 99 Path: "README.md", 100 Body: "first old line", 101 OldLineNum: 1, 102 NewLineNum: 0, 103 }, { 104 Path: "iso-8859-1.txt", 105 Body: "this line contains a non-utf-8 character", 106 OldLineNum: 0, 107 NewLineNum: 1, 108 }, 109 }, 110 }).AddTokenAuth(token) 111 resp = MakeRequest(t, req, http.StatusOK) 112 DecodeJSON(t, resp, &review) 113 assert.EqualValues(t, 6, review.ID) 114 assert.EqualValues(t, "PENDING", review.State) 115 assert.EqualValues(t, 3, review.CodeCommentsCount) 116 117 // test SubmitPullReview 118 req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews/%d", repo.OwnerName, repo.Name, pullIssue.Index, review.ID), &api.SubmitPullReviewOptions{ 119 Event: "APPROVED", 120 Body: "just two nits", 121 }).AddTokenAuth(token) 122 resp = MakeRequest(t, req, http.StatusOK) 123 DecodeJSON(t, resp, &review) 124 assert.EqualValues(t, 6, review.ID) 125 assert.EqualValues(t, "APPROVED", review.State) 126 assert.EqualValues(t, 3, review.CodeCommentsCount) 127 128 // test dismiss review 129 req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews/%d/dismissals", repo.OwnerName, repo.Name, pullIssue.Index, review.ID), &api.DismissPullReviewOptions{ 130 Message: "test", 131 }).AddTokenAuth(token) 132 resp = MakeRequest(t, req, http.StatusOK) 133 DecodeJSON(t, resp, &review) 134 assert.EqualValues(t, 6, review.ID) 135 assert.True(t, review.Dismissed) 136 137 // test dismiss review 138 req = NewRequest(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews/%d/undismissals", repo.OwnerName, repo.Name, pullIssue.Index, review.ID)). 139 AddTokenAuth(token) 140 resp = MakeRequest(t, req, http.StatusOK) 141 DecodeJSON(t, resp, &review) 142 assert.EqualValues(t, 6, review.ID) 143 assert.False(t, review.Dismissed) 144 145 // test DeletePullReview 146 req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews", repo.OwnerName, repo.Name, pullIssue.Index), &api.CreatePullReviewOptions{ 147 Body: "just a comment", 148 Event: "COMMENT", 149 }).AddTokenAuth(token) 150 resp = MakeRequest(t, req, http.StatusOK) 151 DecodeJSON(t, resp, &review) 152 assert.EqualValues(t, "COMMENT", review.State) 153 assert.EqualValues(t, 0, review.CodeCommentsCount) 154 req = NewRequestf(t, http.MethodDelete, "/api/v1/repos/%s/%s/pulls/%d/reviews/%d", repo.OwnerName, repo.Name, pullIssue.Index, review.ID). 155 AddTokenAuth(token) 156 MakeRequest(t, req, http.StatusNoContent) 157 158 // test CreatePullReview Comment without body but with comments 159 req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews", repo.OwnerName, repo.Name, pullIssue.Index), &api.CreatePullReviewOptions{ 160 // Body: "", 161 Event: "COMMENT", 162 Comments: []api.CreatePullReviewComment{ 163 { 164 Path: "README.md", 165 Body: "first new line", 166 OldLineNum: 0, 167 NewLineNum: 1, 168 }, { 169 Path: "README.md", 170 Body: "first old line", 171 OldLineNum: 1, 172 NewLineNum: 0, 173 }, 174 }, 175 }).AddTokenAuth(token) 176 var commentReview api.PullReview 177 178 resp = MakeRequest(t, req, http.StatusOK) 179 DecodeJSON(t, resp, &commentReview) 180 assert.EqualValues(t, "COMMENT", commentReview.State) 181 assert.EqualValues(t, 2, commentReview.CodeCommentsCount) 182 assert.Empty(t, commentReview.Body) 183 assert.False(t, commentReview.Dismissed) 184 185 // test CreatePullReview Comment with body but without comments 186 commentBody := "This is a body of the comment." 187 req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews", repo.OwnerName, repo.Name, pullIssue.Index), &api.CreatePullReviewOptions{ 188 Body: commentBody, 189 Event: "COMMENT", 190 Comments: []api.CreatePullReviewComment{}, 191 }).AddTokenAuth(token) 192 193 resp = MakeRequest(t, req, http.StatusOK) 194 DecodeJSON(t, resp, &commentReview) 195 assert.EqualValues(t, "COMMENT", commentReview.State) 196 assert.EqualValues(t, 0, commentReview.CodeCommentsCount) 197 assert.EqualValues(t, commentBody, commentReview.Body) 198 assert.False(t, commentReview.Dismissed) 199 200 // test CreatePullReview Comment without body and no comments 201 req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews", repo.OwnerName, repo.Name, pullIssue.Index), &api.CreatePullReviewOptions{ 202 Body: "", 203 Event: "COMMENT", 204 Comments: []api.CreatePullReviewComment{}, 205 }).AddTokenAuth(token) 206 resp = MakeRequest(t, req, http.StatusUnprocessableEntity) 207 errMap := make(map[string]any) 208 json.Unmarshal(resp.Body.Bytes(), &errMap) 209 assert.EqualValues(t, "review event COMMENT requires a body or a comment", errMap["message"].(string)) 210 211 // test get review requests 212 // to make it simple, use same api with get review 213 pullIssue12 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 12}) 214 assert.NoError(t, pullIssue12.LoadAttributes(db.DefaultContext)) 215 repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue12.RepoID}) 216 217 req = NewRequestf(t, http.MethodGet, "/api/v1/repos/%s/%s/pulls/%d/reviews", repo3.OwnerName, repo3.Name, pullIssue12.Index). 218 AddTokenAuth(token) 219 resp = MakeRequest(t, req, http.StatusOK) 220 DecodeJSON(t, resp, &reviews) 221 assert.EqualValues(t, 11, reviews[0].ID) 222 assert.EqualValues(t, "REQUEST_REVIEW", reviews[0].State) 223 assert.EqualValues(t, 0, reviews[0].CodeCommentsCount) 224 assert.False(t, reviews[0].Stale) 225 assert.True(t, reviews[0].Official) 226 assert.EqualValues(t, "test_team", reviews[0].ReviewerTeam.Name) 227 228 assert.EqualValues(t, 12, reviews[1].ID) 229 assert.EqualValues(t, "REQUEST_REVIEW", reviews[1].State) 230 assert.EqualValues(t, 0, reviews[0].CodeCommentsCount) 231 assert.False(t, reviews[1].Stale) 232 assert.True(t, reviews[1].Official) 233 assert.EqualValues(t, 1, reviews[1].Reviewer.ID) 234 } 235 236 func TestAPIPullReviewRequest(t *testing.T) { 237 defer tests.PrepareTestEnv(t)() 238 pullIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3}) 239 assert.NoError(t, pullIssue.LoadAttributes(db.DefaultContext)) 240 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue.RepoID}) 241 242 // Test add Review Request 243 session := loginUser(t, "user2") 244 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) 245 req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers", repo.OwnerName, repo.Name, pullIssue.Index), &api.PullReviewRequestOptions{ 246 Reviewers: []string{"user4@example.com", "user8"}, 247 }).AddTokenAuth(token) 248 MakeRequest(t, req, http.StatusCreated) 249 250 // poster of pr can't be reviewer 251 req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers", repo.OwnerName, repo.Name, pullIssue.Index), &api.PullReviewRequestOptions{ 252 Reviewers: []string{"user1"}, 253 }).AddTokenAuth(token) 254 MakeRequest(t, req, http.StatusUnprocessableEntity) 255 256 // test user not exist 257 req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers", repo.OwnerName, repo.Name, pullIssue.Index), &api.PullReviewRequestOptions{ 258 Reviewers: []string{"testOther"}, 259 }).AddTokenAuth(token) 260 MakeRequest(t, req, http.StatusNotFound) 261 262 // Test Remove Review Request 263 session2 := loginUser(t, "user4") 264 token2 := getTokenForLoggedInUser(t, session2, auth_model.AccessTokenScopeWriteRepository) 265 266 req = NewRequestWithJSON(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers", repo.OwnerName, repo.Name, pullIssue.Index), &api.PullReviewRequestOptions{ 267 Reviewers: []string{"user4"}, 268 }).AddTokenAuth(token2) 269 MakeRequest(t, req, http.StatusNoContent) 270 271 // doer is not admin 272 req = NewRequestWithJSON(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers", repo.OwnerName, repo.Name, pullIssue.Index), &api.PullReviewRequestOptions{ 273 Reviewers: []string{"user8"}, 274 }).AddTokenAuth(token2) 275 MakeRequest(t, req, http.StatusUnprocessableEntity) 276 277 req = NewRequestWithJSON(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers", repo.OwnerName, repo.Name, pullIssue.Index), &api.PullReviewRequestOptions{ 278 Reviewers: []string{"user8"}, 279 }).AddTokenAuth(token) 280 MakeRequest(t, req, http.StatusNoContent) 281 282 // a collaborator can add/remove a review request 283 pullIssue21 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 21}) 284 assert.NoError(t, pullIssue21.LoadAttributes(db.DefaultContext)) 285 pull21Repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue21.RepoID}) // repo60 286 user38Session := loginUser(t, "user38") 287 user38Token := getTokenForLoggedInUser(t, user38Session, auth_model.AccessTokenScopeWriteRepository) 288 req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers", pull21Repo.OwnerName, pull21Repo.Name, pullIssue21.Index), &api.PullReviewRequestOptions{ 289 Reviewers: []string{"user4@example.com"}, 290 }).AddTokenAuth(user38Token) 291 MakeRequest(t, req, http.StatusCreated) 292 293 req = NewRequestWithJSON(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers", pull21Repo.OwnerName, pull21Repo.Name, pullIssue21.Index), &api.PullReviewRequestOptions{ 294 Reviewers: []string{"user4@example.com"}, 295 }).AddTokenAuth(user38Token) 296 MakeRequest(t, req, http.StatusNoContent) 297 298 // the poster of the PR can add/remove a review request 299 user39Session := loginUser(t, "user39") 300 user39Token := getTokenForLoggedInUser(t, user39Session, auth_model.AccessTokenScopeWriteRepository) 301 req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers", pull21Repo.OwnerName, pull21Repo.Name, pullIssue21.Index), &api.PullReviewRequestOptions{ 302 Reviewers: []string{"user8"}, 303 }).AddTokenAuth(user39Token) 304 MakeRequest(t, req, http.StatusCreated) 305 306 req = NewRequestWithJSON(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers", pull21Repo.OwnerName, pull21Repo.Name, pullIssue21.Index), &api.PullReviewRequestOptions{ 307 Reviewers: []string{"user8"}, 308 }).AddTokenAuth(user39Token) 309 MakeRequest(t, req, http.StatusNoContent) 310 311 // user with read permission on pull requests unit can add/remove a review request 312 pullIssue22 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 22}) 313 assert.NoError(t, pullIssue22.LoadAttributes(db.DefaultContext)) 314 pull22Repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue22.RepoID}) // repo61 315 req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers", pull22Repo.OwnerName, pull22Repo.Name, pullIssue22.Index), &api.PullReviewRequestOptions{ 316 Reviewers: []string{"user38"}, 317 }).AddTokenAuth(user39Token) // user39 is from a team with read permission on pull requests unit 318 MakeRequest(t, req, http.StatusCreated) 319 320 req = NewRequestWithJSON(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers", pull22Repo.OwnerName, pull22Repo.Name, pullIssue22.Index), &api.PullReviewRequestOptions{ 321 Reviewers: []string{"user38"}, 322 }).AddTokenAuth(user39Token) // user39 is from a team with read permission on pull requests unit 323 MakeRequest(t, req, http.StatusNoContent) 324 325 // Test team review request 326 pullIssue12 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 12}) 327 assert.NoError(t, pullIssue12.LoadAttributes(db.DefaultContext)) 328 repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue12.RepoID}) 329 330 // Test add Team Review Request 331 req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers", repo3.OwnerName, repo3.Name, pullIssue12.Index), &api.PullReviewRequestOptions{ 332 TeamReviewers: []string{"team1", "owners"}, 333 }).AddTokenAuth(token) 334 MakeRequest(t, req, http.StatusCreated) 335 336 // Test add Team Review Request to not allowned 337 req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers", repo3.OwnerName, repo3.Name, pullIssue12.Index), &api.PullReviewRequestOptions{ 338 TeamReviewers: []string{"test_team"}, 339 }).AddTokenAuth(token) 340 MakeRequest(t, req, http.StatusUnprocessableEntity) 341 342 // Test add Team Review Request to not exist 343 req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers", repo3.OwnerName, repo3.Name, pullIssue12.Index), &api.PullReviewRequestOptions{ 344 TeamReviewers: []string{"not_exist_team"}, 345 }).AddTokenAuth(token) 346 MakeRequest(t, req, http.StatusNotFound) 347 348 // Test Remove team Review Request 349 req = NewRequestWithJSON(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers", repo3.OwnerName, repo3.Name, pullIssue12.Index), &api.PullReviewRequestOptions{ 350 TeamReviewers: []string{"team1"}, 351 }).AddTokenAuth(token) 352 MakeRequest(t, req, http.StatusNoContent) 353 354 // empty request test 355 req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers", repo3.OwnerName, repo3.Name, pullIssue12.Index), &api.PullReviewRequestOptions{}). 356 AddTokenAuth(token) 357 MakeRequest(t, req, http.StatusCreated) 358 359 req = NewRequestWithJSON(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers", repo3.OwnerName, repo3.Name, pullIssue12.Index), &api.PullReviewRequestOptions{}). 360 AddTokenAuth(token) 361 MakeRequest(t, req, http.StatusNoContent) 362 } 363 364 func TestAPIPullReviewStayDismissed(t *testing.T) { 365 // This test against issue https://github.com/go-gitea/gitea/issues/28542 366 // where old reviews surface after a review request got dismissed. 367 defer tests.PrepareTestEnv(t)() 368 pullIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3}) 369 assert.NoError(t, pullIssue.LoadAttributes(db.DefaultContext)) 370 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue.RepoID}) 371 user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) 372 session2 := loginUser(t, user2.LoginName) 373 token2 := getTokenForLoggedInUser(t, session2, auth_model.AccessTokenScopeWriteRepository) 374 user8 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 8}) 375 session8 := loginUser(t, user8.LoginName) 376 token8 := getTokenForLoggedInUser(t, session8, auth_model.AccessTokenScopeWriteRepository) 377 378 // user2 request user8 379 req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers", repo.OwnerName, repo.Name, pullIssue.Index), &api.PullReviewRequestOptions{ 380 Reviewers: []string{user8.LoginName}, 381 }).AddTokenAuth(token2) 382 MakeRequest(t, req, http.StatusCreated) 383 384 reviewsCountCheck(t, 385 "check we have only one review request", 386 pullIssue.ID, user8.ID, 0, 1, 1, false) 387 388 // user2 request user8 again, it is expected to be ignored 389 req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers", repo.OwnerName, repo.Name, pullIssue.Index), &api.PullReviewRequestOptions{ 390 Reviewers: []string{user8.LoginName}, 391 }).AddTokenAuth(token2) 392 MakeRequest(t, req, http.StatusCreated) 393 394 reviewsCountCheck(t, 395 "check we have only one review request, even after re-request it again", 396 pullIssue.ID, user8.ID, 0, 1, 1, false) 397 398 // user8 reviews it as accept 399 req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews", repo.OwnerName, repo.Name, pullIssue.Index), &api.CreatePullReviewOptions{ 400 Event: "APPROVED", 401 Body: "lgtm", 402 }).AddTokenAuth(token8) 403 MakeRequest(t, req, http.StatusOK) 404 405 reviewsCountCheck(t, 406 "check we have one valid approval", 407 pullIssue.ID, user8.ID, 0, 0, 1, true) 408 409 // emulate of auto-dismiss lgtm on a protected branch that where a pull just got an update 410 _, err := db.GetEngine(db.DefaultContext).Where("issue_id = ? AND reviewer_id = ?", pullIssue.ID, user8.ID). 411 Cols("dismissed").Update(&issues_model.Review{Dismissed: true}) 412 assert.NoError(t, err) 413 414 // user2 request user8 again 415 req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers", repo.OwnerName, repo.Name, pullIssue.Index), &api.PullReviewRequestOptions{ 416 Reviewers: []string{user8.LoginName}, 417 }).AddTokenAuth(token2) 418 MakeRequest(t, req, http.StatusCreated) 419 420 reviewsCountCheck(t, 421 "check we have no valid approval and one review request", 422 pullIssue.ID, user8.ID, 1, 1, 2, false) 423 424 // user8 dismiss review 425 _, err = issue_service.ReviewRequest(db.DefaultContext, pullIssue, user8, user8, false) 426 assert.NoError(t, err) 427 428 reviewsCountCheck(t, 429 "check new review request is now dismissed", 430 pullIssue.ID, user8.ID, 1, 0, 1, false) 431 432 // add a new valid approval 433 req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews", repo.OwnerName, repo.Name, pullIssue.Index), &api.CreatePullReviewOptions{ 434 Event: "APPROVED", 435 Body: "lgtm", 436 }).AddTokenAuth(token8) 437 MakeRequest(t, req, http.StatusOK) 438 439 reviewsCountCheck(t, 440 "check that old reviews requests are deleted", 441 pullIssue.ID, user8.ID, 1, 0, 2, true) 442 443 // now add a change request witch should dismiss the approval 444 req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews", repo.OwnerName, repo.Name, pullIssue.Index), &api.CreatePullReviewOptions{ 445 Event: "REQUEST_CHANGES", 446 Body: "please change XYZ", 447 }).AddTokenAuth(token8) 448 MakeRequest(t, req, http.StatusOK) 449 450 reviewsCountCheck(t, 451 "check that old reviews are dismissed", 452 pullIssue.ID, user8.ID, 2, 0, 3, false) 453 } 454 455 func reviewsCountCheck(t *testing.T, name string, issueID, reviewerID int64, expectedDismissed, expectedRequested, expectedTotal int, expectApproval bool) { 456 t.Run(name, func(t *testing.T) { 457 unittest.AssertCountByCond(t, "review", builder.Eq{ 458 "issue_id": issueID, 459 "reviewer_id": reviewerID, 460 "dismissed": true, 461 }, expectedDismissed) 462 463 unittest.AssertCountByCond(t, "review", builder.Eq{ 464 "issue_id": issueID, 465 "reviewer_id": reviewerID, 466 }, expectedTotal) 467 468 unittest.AssertCountByCond(t, "review", builder.Eq{ 469 "issue_id": issueID, 470 "reviewer_id": reviewerID, 471 "type": issues_model.ReviewTypeRequest, 472 }, expectedRequested) 473 474 approvalCount := 0 475 if expectApproval { 476 approvalCount = 1 477 } 478 unittest.AssertCountByCond(t, "review", builder.Eq{ 479 "issue_id": issueID, 480 "reviewer_id": reviewerID, 481 "type": issues_model.ReviewTypeApprove, 482 "dismissed": false, 483 }, approvalCount) 484 }) 485 }