github.com/quickfeed/quickfeed@v0.0.0-20240507093252-ed8ca812a09c/scm/github_test.go (about) 1 package scm_test 2 3 import ( 4 "context" 5 "os" 6 "testing" 7 8 "github.com/google/go-cmp/cmp" 9 "github.com/google/go-github/v45/github" 10 "github.com/quickfeed/quickfeed/kit/score" 11 "github.com/quickfeed/quickfeed/qf" 12 "github.com/quickfeed/quickfeed/scm" 13 ) 14 15 const ( 16 qf101Org = "qf101" 17 qf101OrdID = 77283363 18 ) 19 20 // To run this test, please see instructions in the developer guide (dev.md). 21 22 func TestGetOrganization(t *testing.T) { 23 qfTestOrg := scm.GetTestOrganization(t) 24 s, qfTestUser := scm.GetTestSCM(t) 25 org, err := s.GetOrganization(context.Background(), &scm.OrganizationOptions{ 26 Name: qfTestOrg, 27 Username: qfTestUser, 28 }) 29 if err != nil { 30 t.Fatal(err) 31 } 32 if qfTestOrg == qf101Org { 33 if org.GetScmOrganizationID() != qf101OrdID { 34 t.Errorf("GetOrganization(%q) = %d, expected %d", qfTestOrg, org.GetScmOrganizationID(), qf101OrdID) 35 } 36 } else { 37 // Otherwise, we just print the organization result 38 t.Logf("org: %v", org) 39 } 40 } 41 42 // Test case for Creating new Issue on a git Repository 43 func TestCreateIssue(t *testing.T) { 44 qfTestOrg := scm.GetTestOrganization(t) 45 s, qfTestUser := scm.GetTestSCM(t) 46 47 issue, cleanup := createIssue(t, s, qfTestOrg, qf.StudentRepoName(qfTestUser)) 48 defer cleanup() 49 50 if !(issue.Title == "Test Issue" && issue.Body == "Test Body") { 51 t.Errorf("scm.TestCreateIssue: issue: %v", issue) 52 } 53 } 54 55 func TestGetIssue(t *testing.T) { 56 qfTestOrg := scm.GetTestOrganization(t) 57 s, qfTestUser := scm.GetTestSCM(t) 58 59 opt := &scm.RepositoryOptions{ 60 Owner: qfTestOrg, 61 Path: qf.StudentRepoName(qfTestUser), 62 } 63 64 wantIssue, cleanup := createIssue(t, s, opt.Owner, opt.Path) 65 defer cleanup() 66 67 gotIssue, err := s.GetIssue(context.Background(), opt, wantIssue.Number) 68 if err != nil { 69 t.Fatal(err) 70 } 71 72 if diff := cmp.Diff(wantIssue, gotIssue); diff != "" { 73 t.Errorf("scm.TestGetIssue() mismatch (-wantIssue +gotIssue):\n%s", diff) 74 } 75 } 76 77 // Test case for Updating existing Issue in a git Repository 78 func TestUpdateIssue(t *testing.T) { 79 qfTestOrg := scm.GetTestOrganization(t) 80 s, qfTestUser := scm.GetTestSCM(t) 81 82 opt := &scm.IssueOptions{ 83 Organization: qfTestOrg, 84 Repository: qf.StudentRepoName(qfTestUser), 85 Title: "Updated Issue", 86 Body: "Updated Issue Body", 87 } 88 89 issue, cleanup := createIssue(t, s, opt.Organization, opt.Repository) 90 defer cleanup() 91 92 opt.Number = issue.Number 93 gotIssue, err := s.UpdateIssue(context.Background(), opt) 94 if err != nil { 95 t.Fatal(err) 96 } 97 98 if gotIssue.Title != opt.Title || gotIssue.Body != opt.Body { 99 t.Errorf("scm.TestUpdateIssue() want (title: %s, body: %s), got (title: %s, body: %s)", opt.Title, opt.Body, gotIssue.Title, gotIssue.Body) 100 } 101 } 102 103 // This test will delete all open and closed issues for the test user and organization. 104 // The test is skipped unless run with: SCM_TESTS=1 go test -v -run TestDeleteAllIssues 105 func TestDeleteAllIssues(t *testing.T) { 106 if os.Getenv("SCM_TESTS") == "" { 107 t.SkipNow() 108 } 109 qfTestOrg := scm.GetTestOrganization(t) 110 s, qfTestUser := scm.GetTestSCM(t) 111 112 opt := &scm.RepositoryOptions{ 113 Owner: qfTestOrg, 114 Path: qf.StudentRepoName(qfTestUser), 115 } 116 if err := s.DeleteIssues(context.Background(), opt); err != nil { 117 t.Fatal(err) 118 } 119 } 120 121 // TestRequestReviewers tests the ability to request reviewers for a pull request. 122 // It will first create a pull request, then request reviewers for it and then closes the pull request. 123 // 124 // Note: This test requires manual steps before execution: 125 // 1. Create branch test-request-reviewers on the qfTestUser repo 126 // 2. Make edits on the test-request-reviewers branch 127 // 3. Push the changes to the qfTestUser repo 128 // 129 // The test is skipped unless run with: SCM_TESTS=1 go test -v -run TestRequestReviewers 130 func TestRequestReviewers(t *testing.T) { 131 if os.Getenv("SCM_TESTS") == "" { 132 t.SkipNow() 133 } 134 qfTestOrg := scm.GetTestOrganization(t) 135 s, qfTestUser := scm.GetTestSCM(t) 136 repo := qf.StudentRepoName(qfTestUser) 137 138 testReqReviewersBranch := "test-request-reviewers" 139 140 ctx := context.Background() 141 pullReq, _, err := s.Client().PullRequests.Create(ctx, qfTestOrg, repo, &github.NewPullRequest{ 142 Title: github.String("Test Request Reviewers"), 143 Body: github.String("Test Request Reviewers Body"), 144 Head: github.String(testReqReviewersBranch), 145 Base: github.String("master"), 146 }) 147 if err != nil { 148 t.Fatal(err) 149 } 150 t.Logf("PullRequest %d opened", *pullReq.Number) 151 152 // Pick a reviewer that is not the current user (that created the PR above). 153 var reviewer string 154 for _, r := range []string{"meling", "JosteinLindhom"} { 155 if r != qfTestUser { 156 reviewer = r 157 break 158 } 159 } 160 161 opt := &scm.RequestReviewersOptions{ 162 Organization: qfTestOrg, 163 Repository: repo, 164 Number: *pullReq.Number, 165 Reviewers: []string{reviewer}, 166 } 167 if err := s.RequestReviewers(ctx, opt); err != nil { 168 t.Fatal(err) 169 } 170 t.Logf("PullRequest %d created with reviewer %v", *pullReq.Number, reviewer) 171 172 _, _, err = s.Client().PullRequests.Edit(ctx, qfTestOrg, repo, *pullReq.Number, &github.PullRequest{ 173 State: github.String("closed"), 174 }) 175 if err != nil { 176 t.Fatal(err) 177 } 178 t.Logf("PullRequest %d closed", *pullReq.Number) 179 } 180 181 func TestCreateIssueComment(t *testing.T) { 182 qfTestOrg := scm.GetTestOrganization(t) 183 s, qfTestUser := scm.GetTestSCM(t) 184 185 body := "Test" 186 opt := &scm.IssueCommentOptions{ 187 Organization: qfTestOrg, 188 Repository: qf.StudentRepoName(qfTestUser), 189 Body: body, 190 } 191 192 issue, cleanup := createIssue(t, s, opt.Organization, opt.Repository) 193 defer cleanup() 194 195 opt.Number = issue.Number 196 _, err := s.CreateIssueComment(context.Background(), opt) 197 if err != nil { 198 t.Fatal(err) 199 } 200 } 201 202 // TestFeedbackCommentFormat tests creating a feedback comment on a pull request, with the given result. 203 // Note: Manual step required to view the resulting comment: disable the cleanup() function. 204 // The test is skipped unless run with: SCM_TESTS=1 go test -v -run TestFeedbackCommentFormat 205 func TestFeedbackCommentFormat(t *testing.T) { 206 if os.Getenv("SCM_TESTS") == "" { 207 t.SkipNow() 208 } 209 qfTestOrg := scm.GetTestOrganization(t) 210 s, qfTestUser := scm.GetTestSCM(t) 211 212 opt := &scm.IssueCommentOptions{ 213 Organization: qfTestOrg, 214 Repository: qf.StudentRepoName(qfTestUser), 215 Body: "Some initial feedback", 216 } 217 // Using the IssueCommentOptions opt fields to create the issue; the opt will be used below. 218 issue, cleanup := createIssue(t, s, opt.Organization, opt.Repository) 219 defer cleanup() 220 221 opt.Number = issue.Number 222 // The created comment will be deleted when the parent issue is deleted. 223 commentID, err := s.CreateIssueComment(context.Background(), opt) 224 if err != nil { 225 t.Fatal(err) 226 } 227 228 results := &score.Results{ 229 Scores: []*score.Score{ 230 {TestName: "Test1", TaskName: "1", Score: 5, MaxScore: 7, Weight: 2}, 231 {TestName: "Test2", TaskName: "1", Score: 3, MaxScore: 9, Weight: 3}, 232 {TestName: "Test3", TaskName: "1", Score: 8, MaxScore: 8, Weight: 5}, 233 {TestName: "Test4", TaskName: "1", Score: 2, MaxScore: 5, Weight: 1}, 234 {TestName: "Test5", TaskName: "1", Score: 5, MaxScore: 7, Weight: 1}, 235 {TestName: "Test6", TaskName: "2", Score: 5, MaxScore: 7, Weight: 1}, 236 {TestName: "Test7", TaskName: "3", Score: 5, MaxScore: 7, Weight: 1}, 237 }, 238 } 239 body := results.MarkdownComment("1", 80) 240 opt.CommentID = commentID 241 opt.Body = body 242 if err := s.UpdateIssueComment(context.Background(), opt); err != nil { 243 t.Fatal(err) 244 } 245 } 246 247 // This test assumes that the test organization has an empty "info" repository 248 // and non-empty "tests" repository. 249 func TestEmptyRepo(t *testing.T) { 250 qfTestOrg := scm.GetTestOrganization(t) 251 s, _ := scm.GetTestSCM(t) 252 ctx := context.Background() 253 254 tests := []struct { 255 name string 256 opt *scm.RepositoryOptions 257 wantEmpty bool 258 }{ 259 { 260 "tests repo, assume not empty", 261 &scm.RepositoryOptions{ 262 Path: "tests", 263 Owner: qfTestOrg, 264 }, 265 false, 266 }, 267 { 268 "info repo, assume empty", 269 &scm.RepositoryOptions{ 270 Path: "info", 271 Owner: qfTestOrg, 272 }, 273 true, 274 }, 275 { 276 "non-existent repo, handle as empty", 277 &scm.RepositoryOptions{ 278 Path: "some-other-repo", 279 Owner: qfTestOrg, 280 }, 281 true, 282 }, 283 } 284 for _, tt := range tests { 285 if empty := s.RepositoryIsEmpty(ctx, tt.opt); empty != tt.wantEmpty { 286 t.Errorf("%s: expected empty repository: %v, got = %v, ", tt.name, tt.wantEmpty, empty) 287 } 288 } 289 } 290 291 // createIssue on the given repository; returns the issue and a cleanup function. 292 func createIssue(t *testing.T, s scm.SCM, org, repo string) (*scm.Issue, func()) { 293 t.Helper() 294 issue, err := s.CreateIssue(context.Background(), &scm.IssueOptions{ 295 Organization: org, 296 Repository: repo, 297 Title: "Test Issue", 298 Body: "Test Body", 299 }) 300 if err != nil { 301 t.Fatal(err) 302 } 303 304 return issue, func() { 305 if err := s.DeleteIssue(context.Background(), &scm.RepositoryOptions{ 306 Owner: org, Path: repo, 307 }, issue.Number); err != nil { 308 t.Fatal(err) 309 } 310 } 311 }