code.gitea.io/gitea@v1.22.3/tests/integration/pull_review_test.go (about) 1 // Copyright 2019 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package integration 5 6 import ( 7 "net/http" 8 "net/http/httptest" 9 "net/url" 10 "path" 11 "strings" 12 "testing" 13 14 "code.gitea.io/gitea/models/db" 15 issues_model "code.gitea.io/gitea/models/issues" 16 repo_model "code.gitea.io/gitea/models/repo" 17 "code.gitea.io/gitea/models/unittest" 18 user_model "code.gitea.io/gitea/models/user" 19 "code.gitea.io/gitea/modules/git" 20 "code.gitea.io/gitea/modules/test" 21 issue_service "code.gitea.io/gitea/services/issue" 22 repo_service "code.gitea.io/gitea/services/repository" 23 files_service "code.gitea.io/gitea/services/repository/files" 24 "code.gitea.io/gitea/tests" 25 26 "github.com/stretchr/testify/assert" 27 ) 28 29 func TestPullView_ReviewerMissed(t *testing.T) { 30 defer tests.PrepareTestEnv(t)() 31 session := loginUser(t, "user1") 32 33 req := NewRequest(t, "GET", "/pulls") 34 resp := session.MakeRequest(t, req, http.StatusOK) 35 assert.True(t, test.IsNormalPageCompleted(resp.Body.String())) 36 37 req = NewRequest(t, "GET", "/user2/repo1/pulls/3") 38 resp = session.MakeRequest(t, req, http.StatusOK) 39 assert.True(t, test.IsNormalPageCompleted(resp.Body.String())) 40 41 // if some reviews are missing, the page shouldn't fail 42 err := db.TruncateBeans(db.DefaultContext, &issues_model.Review{}) 43 assert.NoError(t, err) 44 req = NewRequest(t, "GET", "/user2/repo1/pulls/2") 45 resp = session.MakeRequest(t, req, http.StatusOK) 46 assert.True(t, test.IsNormalPageCompleted(resp.Body.String())) 47 } 48 49 func TestPullView_CodeOwner(t *testing.T) { 50 onGiteaRun(t, func(t *testing.T, u *url.URL) { 51 user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) 52 53 // Create the repo. 54 repo, err := repo_service.CreateRepositoryDirectly(db.DefaultContext, user2, user2, repo_service.CreateRepoOptions{ 55 Name: "test_codeowner", 56 Readme: "Default", 57 AutoInit: true, 58 ObjectFormatName: git.Sha1ObjectFormat.Name(), 59 DefaultBranch: "master", 60 }) 61 assert.NoError(t, err) 62 63 // add CODEOWNERS to default branch 64 _, err = files_service.ChangeRepoFiles(db.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{ 65 OldBranch: repo.DefaultBranch, 66 Files: []*files_service.ChangeRepoFile{ 67 { 68 Operation: "create", 69 TreePath: "CODEOWNERS", 70 ContentReader: strings.NewReader("README.md @user5\n"), 71 }, 72 }, 73 }) 74 assert.NoError(t, err) 75 76 t.Run("First Pull Request", func(t *testing.T) { 77 // create a new branch to prepare for pull request 78 _, err = files_service.ChangeRepoFiles(db.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{ 79 NewBranch: "codeowner-basebranch", 80 Files: []*files_service.ChangeRepoFile{ 81 { 82 Operation: "update", 83 TreePath: "README.md", 84 ContentReader: strings.NewReader("# This is a new project\n"), 85 }, 86 }, 87 }) 88 assert.NoError(t, err) 89 90 // Create a pull request. 91 session := loginUser(t, "user2") 92 testPullCreate(t, session, "user2", "test_codeowner", false, repo.DefaultBranch, "codeowner-basebranch", "Test Pull Request") 93 94 pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadRepoID: repo.ID, HeadBranch: "codeowner-basebranch"}) 95 unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5}) 96 assert.NoError(t, pr.LoadIssue(db.DefaultContext)) 97 98 err := issue_service.ChangeTitle(db.DefaultContext, pr.Issue, user2, "[WIP] Test Pull Request") 99 assert.NoError(t, err) 100 prUpdated1 := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID}) 101 assert.NoError(t, prUpdated1.LoadIssue(db.DefaultContext)) 102 assert.EqualValues(t, "[WIP] Test Pull Request", prUpdated1.Issue.Title) 103 104 err = issue_service.ChangeTitle(db.DefaultContext, prUpdated1.Issue, user2, "Test Pull Request2") 105 assert.NoError(t, err) 106 prUpdated2 := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID}) 107 assert.NoError(t, prUpdated2.LoadIssue(db.DefaultContext)) 108 assert.EqualValues(t, "Test Pull Request2", prUpdated2.Issue.Title) 109 }) 110 111 // change the default branch CODEOWNERS file to change README.md's codeowner 112 _, err = files_service.ChangeRepoFiles(db.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{ 113 Files: []*files_service.ChangeRepoFile{ 114 { 115 Operation: "update", 116 TreePath: "CODEOWNERS", 117 ContentReader: strings.NewReader("README.md @user8\n"), 118 }, 119 }, 120 }) 121 assert.NoError(t, err) 122 123 t.Run("Second Pull Request", func(t *testing.T) { 124 // create a new branch to prepare for pull request 125 _, err = files_service.ChangeRepoFiles(db.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{ 126 NewBranch: "codeowner-basebranch2", 127 Files: []*files_service.ChangeRepoFile{ 128 { 129 Operation: "update", 130 TreePath: "README.md", 131 ContentReader: strings.NewReader("# This is a new project2\n"), 132 }, 133 }, 134 }) 135 assert.NoError(t, err) 136 137 // Create a pull request. 138 session := loginUser(t, "user2") 139 testPullCreate(t, session, "user2", "test_codeowner", false, repo.DefaultBranch, "codeowner-basebranch2", "Test Pull Request2") 140 141 pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "codeowner-basebranch2"}) 142 unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8}) 143 }) 144 145 t.Run("Forked Repo Pull Request", func(t *testing.T) { 146 user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}) 147 forkedRepo, err := repo_service.ForkRepository(db.DefaultContext, user2, user5, repo_service.ForkRepoOptions{ 148 BaseRepo: repo, 149 Name: "test_codeowner", 150 }) 151 assert.NoError(t, err) 152 153 // create a new branch to prepare for pull request 154 _, err = files_service.ChangeRepoFiles(db.DefaultContext, forkedRepo, user5, &files_service.ChangeRepoFilesOptions{ 155 NewBranch: "codeowner-basebranch-forked", 156 Files: []*files_service.ChangeRepoFile{ 157 { 158 Operation: "update", 159 TreePath: "README.md", 160 ContentReader: strings.NewReader("# This is a new forked project\n"), 161 }, 162 }, 163 }) 164 assert.NoError(t, err) 165 166 session := loginUser(t, "user5") 167 168 // create a pull request on the forked repository, code reviewers should not be mentioned 169 testPullCreateDirectly(t, session, "user5", "test_codeowner", forkedRepo.DefaultBranch, "", "", "codeowner-basebranch-forked", "Test Pull Request on Forked Repository") 170 171 pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: forkedRepo.ID, HeadBranch: "codeowner-basebranch-forked"}) 172 unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8}) 173 174 // create a pull request to base repository, code reviewers should be mentioned 175 testPullCreateDirectly(t, session, repo.OwnerName, repo.Name, repo.DefaultBranch, forkedRepo.OwnerName, forkedRepo.Name, "codeowner-basebranch-forked", "Test Pull Request3") 176 177 pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadRepoID: forkedRepo.ID, HeadBranch: "codeowner-basebranch-forked"}) 178 unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8}) 179 }) 180 }) 181 } 182 183 func TestPullView_GivenApproveOrRejectReviewOnClosedPR(t *testing.T) { 184 onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { 185 user1Session := loginUser(t, "user1") 186 user2Session := loginUser(t, "user2") 187 188 // Have user1 create a fork of repo1. 189 testRepoFork(t, user1Session, "user2", "repo1", "user1", "repo1", "") 190 191 t.Run("Submit approve/reject review on merged PR", func(t *testing.T) { 192 // Create a merged PR (made by user1) in the upstream repo1. 193 testEditFile(t, user1Session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") 194 resp := testPullCreate(t, user1Session, "user1", "repo1", false, "master", "master", "This is a pull title") 195 elem := strings.Split(test.RedirectURL(resp), "/") 196 assert.EqualValues(t, "pulls", elem[3]) 197 testPullMerge(t, user1Session, elem[1], elem[2], elem[4], repo_model.MergeStyleMerge, false) 198 199 // Grab the CSRF token. 200 req := NewRequest(t, "GET", path.Join(elem[1], elem[2], "pulls", elem[4])) 201 resp = user2Session.MakeRequest(t, req, http.StatusOK) 202 htmlDoc := NewHTMLParser(t, resp.Body) 203 204 // Submit an approve review on the PR. 205 testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "", "approve", http.StatusUnprocessableEntity) 206 207 // Submit a reject review on the PR. 208 testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "", "reject", http.StatusUnprocessableEntity) 209 }) 210 211 t.Run("Submit approve/reject review on closed PR", func(t *testing.T) { 212 // Created a closed PR (made by user1) in the upstream repo1. 213 testEditFileToNewBranch(t, user1Session, "user1", "repo1", "master", "a-test-branch", "README.md", "Hello, World (Editied...again)\n") 214 resp := testPullCreate(t, user1Session, "user1", "repo1", false, "master", "a-test-branch", "This is a pull title") 215 elem := strings.Split(test.RedirectURL(resp), "/") 216 assert.EqualValues(t, "pulls", elem[3]) 217 testIssueClose(t, user1Session, elem[1], elem[2], elem[4]) 218 219 // Grab the CSRF token. 220 req := NewRequest(t, "GET", path.Join(elem[1], elem[2], "pulls", elem[4])) 221 resp = user2Session.MakeRequest(t, req, http.StatusOK) 222 htmlDoc := NewHTMLParser(t, resp.Body) 223 224 // Submit an approve review on the PR. 225 testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "", "approve", http.StatusUnprocessableEntity) 226 227 // Submit a reject review on the PR. 228 testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "", "reject", http.StatusUnprocessableEntity) 229 }) 230 }) 231 } 232 233 func testSubmitReview(t *testing.T, session *TestSession, csrf, owner, repo, pullNumber, commitID, reviewType string, expectedSubmitStatus int) *httptest.ResponseRecorder { 234 options := map[string]string{ 235 "_csrf": csrf, 236 "commit_id": commitID, 237 "content": "test", 238 "type": reviewType, 239 } 240 241 submitURL := path.Join(owner, repo, "pulls", pullNumber, "files", "reviews", "submit") 242 req := NewRequestWithValues(t, "POST", submitURL, options) 243 return session.MakeRequest(t, req, expectedSubmitStatus) 244 } 245 246 func testIssueClose(t *testing.T, session *TestSession, owner, repo, issueNumber string) *httptest.ResponseRecorder { 247 req := NewRequest(t, "GET", path.Join(owner, repo, "pulls", issueNumber)) 248 resp := session.MakeRequest(t, req, http.StatusOK) 249 250 htmlDoc := NewHTMLParser(t, resp.Body) 251 closeURL := path.Join(owner, repo, "issues", issueNumber, "comments") 252 253 options := map[string]string{ 254 "_csrf": htmlDoc.GetCSRF(), 255 "status": "close", 256 } 257 258 req = NewRequestWithValues(t, "POST", closeURL, options) 259 return session.MakeRequest(t, req, http.StatusOK) 260 }