go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/bisection/internal/gerrit/utils_test.go (about) 1 // Copyright 2022 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package gerrit 16 17 import ( 18 "context" 19 "testing" 20 "time" 21 22 . "github.com/smartystreets/goconvey/convey" 23 "google.golang.org/protobuf/types/known/timestamppb" 24 25 "go.chromium.org/luci/common/clock" 26 "go.chromium.org/luci/common/clock/testclock" 27 gerritpb "go.chromium.org/luci/common/proto/gerrit" 28 . "go.chromium.org/luci/common/testing/assertions" 29 "go.chromium.org/luci/gae/impl/memory" 30 ) 31 32 func TestGetHost(t *testing.T) { 33 t.Parallel() 34 ctx := context.Background() 35 36 Convey("Empty string returns error", t, func() { 37 host, err := GetHost(ctx, "") 38 So(err, ShouldErrLike, "could not find Gerrit host") 39 So(host, ShouldEqual, "") 40 }) 41 42 Convey("Non-URL format returns error", t, func() { 43 host, err := GetHost(ctx, "[Test] This is a review title, not a URL") 44 So(err, ShouldErrLike, "could not find Gerrit host") 45 So(host, ShouldEqual, "") 46 }) 47 48 Convey("Gets host from review URL", t, func() { 49 host, err := GetHost(ctx, " https://chromium-test-review.googlesource.com/c/proj/+/1200003\n") 50 So(err, ShouldBeNil) 51 So(host, ShouldEqual, "chromium-test-review.googlesource.com") 52 }) 53 } 54 55 func TestHasLUCIBisectionComment(t *testing.T) { 56 t.Parallel() 57 ctx := memory.Use(context.Background()) 58 59 Convey("No comments", t, func() { 60 hasLBComment, err := HasLUCIBisectionComment(ctx, &gerritpb.ChangeInfo{}) 61 So(err, ShouldBeNil) 62 So(hasLBComment, ShouldEqual, false) 63 }) 64 65 Convey("No LUCI Bisection comment", t, func() { 66 change := &gerritpb.ChangeInfo{ 67 Messages: []*gerritpb.ChangeMessageInfo{ 68 { 69 Id: "19283", 70 Message: "Automated message from Gerrit with no author", 71 Tag: "autogenerated:gerrit:test", 72 }, 73 { 74 Id: "19284", 75 Author: &gerritpb.AccountInfo{ 76 Name: "John Doe", 77 Email: "jdoe@example.com", 78 Username: "jdoe", 79 AccountId: 100001, 80 }, 81 }, 82 }, 83 } 84 hasLBComment, err := HasLUCIBisectionComment(ctx, change) 85 So(err, ShouldBeNil) 86 So(hasLBComment, ShouldEqual, false) 87 }) 88 89 Convey("LUCI Bisection has commented", t, func() { 90 // Get the service account email value in this test context 91 testEmail, err := ServiceAccountEmail(ctx) 92 So(err, ShouldBeNil) 93 94 change := &gerritpb.ChangeInfo{ 95 Messages: []*gerritpb.ChangeMessageInfo{ 96 { 97 Id: "19285", 98 Message: "Automated message from Gerrit with no author", 99 Tag: "autogenerated:gerrit:test", 100 }, 101 { 102 Id: "19286", 103 Author: &gerritpb.AccountInfo{ 104 Name: "LUCI Bisection", 105 Email: testEmail, 106 Username: "gae_service_account", 107 AccountId: 900009, 108 }, 109 }, 110 { 111 Id: "19287", 112 Author: &gerritpb.AccountInfo{ 113 Name: "John Doe", 114 Email: "jdoe@example.com", 115 Username: "jdoe", 116 AccountId: 100001, 117 }, 118 }, 119 }, 120 } 121 hasLBComment, err := HasLUCIBisectionComment(ctx, change) 122 So(err, ShouldBeNil) 123 So(hasLBComment, ShouldEqual, true) 124 }) 125 } 126 127 func TestIsOwnedByLUCIBisection(t *testing.T) { 128 t.Parallel() 129 ctx := memory.Use(context.Background()) 130 131 Convey("No owner", t, func() { 132 change := &gerritpb.ChangeInfo{ 133 Project: "chromium/test/src", 134 Number: 615243, 135 } 136 lbOwned, err := IsOwnedByLUCIBisection(ctx, change) 137 So(err, ShouldBeNil) 138 So(lbOwned, ShouldEqual, false) 139 }) 140 141 Convey("Change is not owned by LUCI Bisection", t, func() { 142 change := &gerritpb.ChangeInfo{ 143 Project: "chromium/test/src", 144 Number: 615243, 145 Owner: &gerritpb.AccountInfo{ 146 Name: "John Doe", 147 Email: "jdoe@example.com", 148 Username: "jdoe", 149 AccountId: 100001, 150 }, 151 } 152 lbOwned, err := IsOwnedByLUCIBisection(ctx, change) 153 So(err, ShouldBeNil) 154 So(lbOwned, ShouldEqual, false) 155 }) 156 157 Convey("Change is owned by LUCI Bisection", t, func() { 158 // Get the service account email value in this test context 159 testEmail, err := ServiceAccountEmail(ctx) 160 So(err, ShouldBeNil) 161 162 change := &gerritpb.ChangeInfo{ 163 Project: "chromium/test/src", 164 Number: 615243, 165 Owner: &gerritpb.AccountInfo{ 166 Name: "LUCI Bisection", 167 Email: testEmail, 168 Username: "gae_service_account", 169 AccountId: 900009, 170 }, 171 } 172 lbOwned, err := IsOwnedByLUCIBisection(ctx, change) 173 So(err, ShouldBeNil) 174 So(lbOwned, ShouldEqual, true) 175 }) 176 } 177 178 func TestIsRecentSubmit(t *testing.T) { 179 t.Parallel() 180 ctx := context.Background() 181 182 // Set test clock 183 cl := testclock.New(testclock.TestTimeUTC) 184 ctx = clock.Set(ctx, cl) 185 186 Convey("IsRecentSubmit", t, func() { 187 change := &gerritpb.ChangeInfo{ 188 Project: "chromium/test/src", 189 Number: 234567, 190 } 191 maxAge := time.Duration(6) * time.Hour 192 193 Convey("change submitted now is recent", func() { 194 change.Submitted = timestamppb.New(clock.Now(ctx)) 195 So(IsRecentSubmit(ctx, change, maxAge), ShouldEqual, true) 196 }) 197 198 Convey("change submitted at threshold time is recent", func() { 199 change.Submitted = timestamppb.New(clock.Now(ctx).Add(-time.Hour * 6)) 200 So(IsRecentSubmit(ctx, change, maxAge), ShouldEqual, true) 201 }) 202 203 Convey("change submitted a while ago is not recent", func() { 204 change.Submitted = timestamppb.New(clock.Now(ctx).Add(-time.Hour * 30)) 205 So(IsRecentSubmit(ctx, change, maxAge), ShouldEqual, false) 206 }) 207 }) 208 } 209 210 func TestHasAutoRevertOffFlagSet(t *testing.T) { 211 t.Parallel() 212 ctx := context.Background() 213 214 Convey("change does not have enough information", t, func() { 215 change := &gerritpb.ChangeInfo{ 216 Project: "chromium/test/src", 217 Number: 234567, 218 } 219 _, err := HasAutoRevertOffFlagSet(ctx, change) 220 So(err, ShouldErrLike, "could not get", "info") 221 }) 222 223 Convey("change does not mention flag", t, func() { 224 change := &gerritpb.ChangeInfo{ 225 Project: "chromium/test/src", 226 Number: 234567, 227 CurrentRevision: "deadbeef", 228 Revisions: map[string]*gerritpb.RevisionInfo{ 229 "deadbeef": { 230 Number: 1, 231 Kind: gerritpb.RevisionInfo_REWORK, 232 Uploader: &gerritpb.AccountInfo{ 233 AccountId: 1000096, 234 Name: "John Doe", 235 Email: "jdoe@example.com", 236 SecondaryEmails: []string{"johndoe@chromium.org"}, 237 Username: "jdoe", 238 }, 239 Ref: "refs/changes/123", 240 Description: "first upload", 241 Files: map[string]*gerritpb.FileInfo{ 242 "go/to/file.go": { 243 LinesInserted: 32, 244 LinesDeleted: 44, 245 SizeDelta: -567, 246 Size: 11984, 247 }, 248 }, 249 Commit: &gerritpb.CommitInfo{ 250 Id: "", 251 Message: "Title.\n\nBody is here.\n\nChange-Id: I100deadbeef", 252 Parents: []*gerritpb.CommitInfo_Parent{ 253 {Id: "deadbeef00"}, 254 }, 255 }, 256 }, 257 }, 258 } 259 260 hasFlag, err := HasAutoRevertOffFlagSet(ctx, change) 261 So(err, ShouldBeNil) 262 So(hasFlag, ShouldEqual, false) 263 }) 264 265 Convey("change has flag set", t, func() { 266 change := &gerritpb.ChangeInfo{ 267 Project: "chromium/test/src", 268 Number: 234567, 269 CurrentRevision: "deadbeef", 270 Revisions: map[string]*gerritpb.RevisionInfo{ 271 "deadbeef": { 272 Number: 1, 273 Kind: gerritpb.RevisionInfo_REWORK, 274 Uploader: &gerritpb.AccountInfo{ 275 AccountId: 1000096, 276 Name: "John Doe", 277 Email: "jdoe@example.com", 278 SecondaryEmails: []string{"johndoe@chromium.org"}, 279 Username: "jdoe", 280 }, 281 Ref: "refs/changes/123", 282 Description: "first upload", 283 Files: map[string]*gerritpb.FileInfo{ 284 "go/to/file.go": { 285 LinesInserted: 32, 286 LinesDeleted: 44, 287 SizeDelta: -567, 288 Size: 11984, 289 }, 290 }, 291 Commit: &gerritpb.CommitInfo{ 292 Id: "", 293 Message: "Title.\n\nBody is here.\n\nNOAUTOREVERT=true\n\nChange-Id: I100deadbeef", 294 Parents: []*gerritpb.CommitInfo_Parent{ 295 {Id: "deadbeef00"}, 296 }, 297 }, 298 }, 299 }, 300 } 301 302 hasFlag, err := HasAutoRevertOffFlagSet(ctx, change) 303 So(err, ShouldBeNil) 304 So(hasFlag, ShouldEqual, true) 305 }) 306 307 Convey("change has flag set with extra whitespace", t, func() { 308 change := &gerritpb.ChangeInfo{ 309 Project: "chromium/test/src", 310 Number: 234567, 311 CurrentRevision: "deadbeef", 312 Revisions: map[string]*gerritpb.RevisionInfo{ 313 "deadbeef": { 314 Number: 1, 315 Kind: gerritpb.RevisionInfo_REWORK, 316 Uploader: &gerritpb.AccountInfo{ 317 AccountId: 1000096, 318 Name: "John Doe", 319 Email: "jdoe@example.com", 320 SecondaryEmails: []string{"johndoe@chromium.org"}, 321 Username: "jdoe", 322 }, 323 Ref: "refs/changes/123", 324 Description: "first upload", 325 Files: map[string]*gerritpb.FileInfo{ 326 "go/to/file.go": { 327 LinesInserted: 32, 328 LinesDeleted: 44, 329 SizeDelta: -567, 330 Size: 11984, 331 }, 332 }, 333 Commit: &gerritpb.CommitInfo{ 334 Id: "", 335 Message: "Title.\n\nBody is here.\n\nNOAUTOREVERT\t= true\n\nChange-Id: I100deadbeef", 336 Parents: []*gerritpb.CommitInfo_Parent{ 337 {Id: "deadbeef00"}, 338 }, 339 }, 340 }, 341 }, 342 } 343 344 hasFlag, err := HasAutoRevertOffFlagSet(ctx, change) 345 So(err, ShouldBeNil) 346 So(hasFlag, ShouldEqual, true) 347 }) 348 349 Convey("change has flag set to false", t, func() { 350 change := &gerritpb.ChangeInfo{ 351 Project: "chromium/test/src", 352 Number: 234567, 353 CurrentRevision: "deadbeef", 354 Revisions: map[string]*gerritpb.RevisionInfo{ 355 "deadbeef": { 356 Number: 1, 357 Kind: gerritpb.RevisionInfo_REWORK, 358 Uploader: &gerritpb.AccountInfo{ 359 AccountId: 1000096, 360 Name: "John Doe", 361 Email: "jdoe@example.com", 362 SecondaryEmails: []string{"johndoe@chromium.org"}, 363 Username: "jdoe", 364 }, 365 Ref: "refs/changes/123", 366 Description: "first upload", 367 Files: map[string]*gerritpb.FileInfo{ 368 "go/to/file.go": { 369 LinesInserted: 32, 370 LinesDeleted: 44, 371 SizeDelta: -567, 372 Size: 11984, 373 }, 374 }, 375 Commit: &gerritpb.CommitInfo{ 376 Id: "", 377 Message: "Title.\n\nBody is here.\n\nNOAUTOREVERT=false SOMEOTHERFLAG=true\n\nChange-Id: I100deadbeef", 378 Parents: []*gerritpb.CommitInfo_Parent{ 379 {Id: "deadbeef00"}, 380 }, 381 }, 382 }, 383 }, 384 } 385 386 hasFlag, err := HasAutoRevertOffFlagSet(ctx, change) 387 So(err, ShouldBeNil) 388 So(hasFlag, ShouldEqual, false) 389 }) 390 } 391 392 func TestAuthorEmail(t *testing.T) { 393 t.Parallel() 394 ctx := context.Background() 395 396 Convey("change does not have enough information", t, func() { 397 change := &gerritpb.ChangeInfo{ 398 Project: "chromium/test/src", 399 Number: 234567, 400 } 401 _, err := AuthorEmail(ctx, change) 402 So(err, ShouldErrLike, "could not get", "info") 403 }) 404 405 Convey("change does not have an author", t, func() { 406 change := &gerritpb.ChangeInfo{ 407 Project: "chromium/test/src", 408 Number: 234567, 409 CurrentRevision: "deadbeef", 410 Revisions: map[string]*gerritpb.RevisionInfo{ 411 "deadbeef": { 412 Number: 1, 413 Kind: gerritpb.RevisionInfo_REWORK, 414 Uploader: &gerritpb.AccountInfo{ 415 AccountId: 1000096, 416 Name: "John Doe", 417 Email: "jdoe@example.com", 418 SecondaryEmails: []string{"johndoe@chromium.org"}, 419 Username: "jdoe", 420 }, 421 Ref: "refs/changes/123", 422 Description: "first upload", 423 Files: map[string]*gerritpb.FileInfo{ 424 "go/to/file.go": { 425 LinesInserted: 32, 426 LinesDeleted: 44, 427 SizeDelta: -567, 428 Size: 11984, 429 }, 430 }, 431 Commit: &gerritpb.CommitInfo{ 432 Id: "", 433 Message: "Title.\n\nBody is here.\n\nChange-Id: I100deadbeef", 434 Parents: []*gerritpb.CommitInfo_Parent{ 435 {Id: "deadbeef00"}, 436 }, 437 }, 438 }, 439 }, 440 } 441 442 _, err := AuthorEmail(ctx, change) 443 So(err, ShouldErrLike, "no author in commit info") 444 }) 445 446 Convey("author email is returned", t, func() { 447 change := &gerritpb.ChangeInfo{ 448 Project: "chromium/test/src", 449 Number: 234567, 450 CurrentRevision: "deadbeef", 451 Revisions: map[string]*gerritpb.RevisionInfo{ 452 "deadbeef": { 453 Number: 1, 454 Kind: gerritpb.RevisionInfo_REWORK, 455 Uploader: &gerritpb.AccountInfo{ 456 AccountId: 1000096, 457 Name: "John Doe", 458 Email: "jdoe@example.com", 459 SecondaryEmails: []string{"johndoe@chromium.org"}, 460 Username: "jdoe", 461 }, 462 Ref: "refs/changes/123", 463 Description: "first upload", 464 Files: map[string]*gerritpb.FileInfo{ 465 "go/to/file.go": { 466 LinesInserted: 32, 467 LinesDeleted: 44, 468 SizeDelta: -567, 469 Size: 11984, 470 }, 471 }, 472 Commit: &gerritpb.CommitInfo{ 473 Id: "", 474 Message: "Title.\n\nBody is here.\n\nNOAUTOREVERT=true\n\nChange-Id: I100deadbeef", 475 Parents: []*gerritpb.CommitInfo_Parent{ 476 {Id: "deadbeef00"}, 477 }, 478 Author: &gerritpb.GitPersonInfo{ 479 Name: "John Doe", 480 Email: "jdoe@example.com", 481 }, 482 }, 483 }, 484 }, 485 } 486 487 author, err := AuthorEmail(ctx, change) 488 So(err, ShouldBeNil) 489 So(author, ShouldEqual, "jdoe@example.com") 490 }) 491 } 492 493 func TestCommitMessage(t *testing.T) { 494 t.Parallel() 495 ctx := context.Background() 496 497 Convey("change does not have enough information", t, func() { 498 change := &gerritpb.ChangeInfo{ 499 Project: "chromium/test/src", 500 Number: 234567, 501 } 502 _, err := CommitMessage(ctx, change) 503 So(err, ShouldErrLike, "could not get", "info") 504 }) 505 506 Convey("commit message is returned", t, func() { 507 expectedMessage := "Title.\n\nBody is here.\n\nNOAUTOREVERT=true\n\nChange-Id: I100deadbeef" 508 change := &gerritpb.ChangeInfo{ 509 Project: "chromium/test/src", 510 Number: 234567, 511 CurrentRevision: "deadbeef", 512 Revisions: map[string]*gerritpb.RevisionInfo{ 513 "deadbeef": { 514 Number: 1, 515 Kind: gerritpb.RevisionInfo_REWORK, 516 Uploader: &gerritpb.AccountInfo{ 517 AccountId: 1000096, 518 Name: "John Doe", 519 Email: "jdoe@example.com", 520 SecondaryEmails: []string{"johndoe@chromium.org"}, 521 Username: "jdoe", 522 }, 523 Ref: "refs/changes/123", 524 Description: "first upload", 525 Files: map[string]*gerritpb.FileInfo{ 526 "go/to/file.go": { 527 LinesInserted: 32, 528 LinesDeleted: 44, 529 SizeDelta: -567, 530 Size: 11984, 531 }, 532 }, 533 Commit: &gerritpb.CommitInfo{ 534 Id: "", 535 Message: expectedMessage, 536 Parents: []*gerritpb.CommitInfo_Parent{ 537 {Id: "deadbeef00"}, 538 }, 539 Author: &gerritpb.GitPersonInfo{ 540 Name: "John Doe", 541 Email: "jdoe@example.com", 542 }, 543 }, 544 }, 545 }, 546 } 547 548 message, err := CommitMessage(ctx, change) 549 So(err, ShouldBeNil) 550 So(message, ShouldEqual, expectedMessage) 551 }) 552 }