golang.org/x/build@v0.0.0-20240506185731-218518f32b70/maintner/github_test.go (about) 1 // Copyright 2017 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package maintner 6 7 import ( 8 "bytes" 9 "context" 10 "fmt" 11 "io" 12 "net/http" 13 "net/http/httptest" 14 "os" 15 "path/filepath" 16 "reflect" 17 "sort" 18 "strings" 19 "testing" 20 "time" 21 22 "github.com/golang/protobuf/ptypes" 23 "github.com/golang/protobuf/ptypes/timestamp" 24 "github.com/google/go-github/github" 25 26 "golang.org/x/build/maintner/maintpb" 27 ) 28 29 func TestParseGithubEvents(t *testing.T) { 30 tests := []struct { 31 name string // test 32 j string // JSON from Github API 33 e *GitHubIssueEvent // in-memory 34 p *maintpb.GithubIssueEvent // on disk 35 }{ 36 { 37 name: "labeled", 38 j: `{ 39 "id": 998144526, 40 "url": "https://api.github.com/repos/bradfitz/go-issue-mirror/issues/events/998144526", 41 "actor": { 42 "login": "bradfitz", 43 "id": 2621 44 }, 45 "event": "labeled", 46 "commit_id": null, 47 "commit_url": null, 48 "created_at": "2017-03-13T22:39:28Z", 49 "label": { 50 "name": "enhancement", 51 "color": "84b6eb" 52 } 53 } 54 `, 55 e: &GitHubIssueEvent{ 56 ID: 998144526, 57 Type: "labeled", 58 Created: t3339("2017-03-13T22:39:28Z"), 59 Actor: &GitHubUser{ 60 ID: 2621, 61 Login: "bradfitz", 62 }, 63 Label: "enhancement", 64 }, 65 p: &maintpb.GithubIssueEvent{ 66 Id: 998144526, 67 EventType: "labeled", 68 ActorId: 2621, 69 Created: p3339("2017-03-13T22:39:28Z"), 70 Label: &maintpb.GithubLabel{Name: "enhancement"}, 71 }, 72 }, 73 74 { 75 name: "unlabeled", 76 j: `{ 77 "id": 998144526, 78 "url": "https://api.github.com/repos/bradfitz/go-issue-mirror/issues/events/998144526", 79 "actor": { 80 "login": "bradfitz", 81 "id": 2621 82 }, 83 "event": "unlabeled", 84 "commit_id": null, 85 "commit_url": null, 86 "created_at": "2017-03-13T22:39:28Z", 87 "label": { 88 "name": "enhancement", 89 "color": "84b6eb" 90 } 91 } 92 `, 93 e: &GitHubIssueEvent{ 94 ID: 998144526, 95 Type: "unlabeled", 96 Created: t3339("2017-03-13T22:39:28Z"), 97 Actor: &GitHubUser{ 98 ID: 2621, 99 Login: "bradfitz", 100 }, 101 Label: "enhancement", 102 }, 103 p: &maintpb.GithubIssueEvent{ 104 Id: 998144526, 105 EventType: "unlabeled", 106 ActorId: 2621, 107 Created: p3339("2017-03-13T22:39:28Z"), 108 Label: &maintpb.GithubLabel{Name: "enhancement"}, 109 }, 110 }, 111 112 { 113 name: "milestoned", 114 j: `{ 115 "id": 998144529, 116 "url": "https://api.github.com/repos/bradfitz/go-issue-mirror/issues/events/998144529", 117 "actor": { 118 "login": "bradfitz", 119 "id": 2621 120 }, 121 "event": "milestoned", 122 "commit_id": null, 123 "commit_url": null, 124 "created_at": "2017-03-13T22:39:28Z", 125 "milestone": { 126 "title": "World Domination" 127 }}`, 128 e: &GitHubIssueEvent{ 129 ID: 998144529, 130 Type: "milestoned", 131 Created: t3339("2017-03-13T22:39:28Z"), 132 Actor: &GitHubUser{ 133 ID: 2621, 134 Login: "bradfitz", 135 }, 136 Milestone: "World Domination", 137 }, 138 p: &maintpb.GithubIssueEvent{ 139 Id: 998144529, 140 EventType: "milestoned", 141 ActorId: 2621, 142 Created: p3339("2017-03-13T22:39:28Z"), 143 Milestone: &maintpb.GithubMilestone{Title: "World Domination"}, 144 }, 145 }, 146 147 { 148 name: "demilestoned", 149 j: `{ 150 "id": 998144529, 151 "url": "https://api.github.com/repos/bradfitz/go-issue-mirror/issues/events/998144529", 152 "actor": { 153 "login": "bradfitz", 154 "id": 2621 155 }, 156 "event": "demilestoned", 157 "commit_id": null, 158 "commit_url": null, 159 "created_at": "2017-03-13T22:39:28Z", 160 "milestone": { 161 "title": "World Domination" 162 }}`, 163 e: &GitHubIssueEvent{ 164 ID: 998144529, 165 Type: "demilestoned", 166 Created: t3339("2017-03-13T22:39:28Z"), 167 Actor: &GitHubUser{ 168 ID: 2621, 169 Login: "bradfitz", 170 }, 171 Milestone: "World Domination", 172 }, 173 p: &maintpb.GithubIssueEvent{ 174 Id: 998144529, 175 EventType: "demilestoned", 176 ActorId: 2621, 177 Created: p3339("2017-03-13T22:39:28Z"), 178 Milestone: &maintpb.GithubMilestone{Title: "World Domination"}, 179 }, 180 }, 181 182 { 183 name: "assigned", 184 j: `{ 185 "id": 998144530, 186 "url": "https://api.github.com/repos/bradfitz/go-issue-mirror/issues/events/998144530", 187 "actor": { 188 "login": "bradfitz", 189 "id": 2621 190 }, 191 "event": "assigned", 192 "commit_id": null, 193 "commit_url": null, 194 "created_at": "2017-03-13T22:39:28Z", 195 "assignee": { 196 "login": "bradfitz", 197 "id": 2621 198 }, 199 "assigner": { 200 "login": "bradfitz", 201 "id": 2621 202 }}`, 203 e: &GitHubIssueEvent{ 204 ID: 998144530, 205 Type: "assigned", 206 Created: t3339("2017-03-13T22:39:28Z"), 207 Actor: &GitHubUser{ 208 ID: 2621, 209 Login: "bradfitz", 210 }, 211 Assignee: &GitHubUser{ 212 ID: 2621, 213 Login: "bradfitz", 214 }, 215 Assigner: &GitHubUser{ 216 ID: 2621, 217 Login: "bradfitz", 218 }, 219 }, 220 p: &maintpb.GithubIssueEvent{ 221 Id: 998144530, 222 EventType: "assigned", 223 ActorId: 2621, 224 Created: p3339("2017-03-13T22:39:28Z"), 225 AssigneeId: 2621, 226 AssignerId: 2621, 227 }, 228 }, 229 230 { 231 name: "unassigned", 232 j: `{ 233 "id": 1000077586, 234 "url": "https://api.github.com/repos/bradfitz/go-issue-mirror/issues/events/1000077586", 235 "actor": { 236 "login": "dmitshur", 237 "id": 1924134 238 }, 239 "event": "unassigned", 240 "commit_id": null, 241 "commit_url": null, 242 "created_at": "2017-03-15T00:31:42Z", 243 "assignee": { 244 "login": "dmitshur", 245 "id": 1924134 246 }, 247 "assigner": { 248 "login": "bradfitz", 249 "id": 2621 250 } 251 }`, 252 e: &GitHubIssueEvent{ 253 ID: 1000077586, 254 Type: "unassigned", 255 Created: t3339("2017-03-15T00:31:42Z"), 256 Actor: &GitHubUser{ 257 ID: 1924134, 258 Login: "dmitshur", 259 }, 260 Assignee: &GitHubUser{ 261 ID: 1924134, 262 Login: "dmitshur", 263 }, 264 Assigner: &GitHubUser{ 265 ID: 2621, 266 Login: "bradfitz", 267 }, 268 }, 269 p: &maintpb.GithubIssueEvent{ 270 Id: 1000077586, 271 EventType: "unassigned", 272 ActorId: 1924134, 273 Created: p3339("2017-03-15T00:31:42Z"), 274 AssigneeId: 1924134, 275 AssignerId: 2621, 276 }, 277 }, 278 279 { 280 name: "locked", 281 j: `{ 282 "id": 998144646, 283 "url": "https://api.github.com/repos/bradfitz/go-issue-mirror/issues/events/998144646", 284 "actor": { 285 "login": "bradfitz", 286 "id": 2621 287 }, 288 "event": "locked", 289 "commit_id": null, 290 "commit_url": null, 291 "created_at": "2017-03-13T22:39:36Z", 292 "lock_reason": "off-topic" 293 }`, 294 e: &GitHubIssueEvent{ 295 ID: 998144646, 296 Type: "locked", 297 Created: t3339("2017-03-13T22:39:36Z"), 298 Actor: &GitHubUser{ 299 ID: 2621, 300 Login: "bradfitz", 301 }, 302 }, 303 p: &maintpb.GithubIssueEvent{ 304 Id: 998144646, 305 EventType: "locked", 306 ActorId: 2621, 307 Created: p3339("2017-03-13T22:39:36Z"), 308 }, 309 }, 310 311 { 312 name: "unlocked", 313 j: `{ 314 "id": 1000014895, 315 "url": "https://api.github.com/repos/bradfitz/go-issue-mirror/issues/events/1000014895", 316 "actor": { 317 "login": "bradfitz", 318 "id": 2621 319 }, 320 "event": "unlocked", 321 "commit_id": null, 322 "commit_url": null, 323 "created_at": "2017-03-14T23:26:21Z" 324 }`, 325 e: &GitHubIssueEvent{ 326 ID: 1000014895, 327 Type: "unlocked", 328 Created: t3339("2017-03-14T23:26:21Z"), 329 Actor: &GitHubUser{ 330 ID: 2621, 331 Login: "bradfitz", 332 }, 333 }, 334 p: &maintpb.GithubIssueEvent{ 335 Id: 1000014895, 336 EventType: "unlocked", 337 ActorId: 2621, 338 Created: p3339("2017-03-14T23:26:21Z"), 339 }, 340 }, 341 342 { 343 name: "closed", 344 j: ` { 345 "id": 1006040931, 346 "url": "https://api.github.com/repos/bradfitz/go-issue-mirror/issues/events/1006040931", 347 "actor": { 348 "login": "bradfitz", 349 "id": 2621 350 }, 351 "event": "closed", 352 "commit_id": "e4d70f7e8892f024e4ed3e8b99ee6c5a9f16e126", 353 "commit_url": "https://api.github.com/repos/bradfitz/go-issue-mirror/commits/e4d70f7e8892f024e4ed3e8b99ee6c5a9f16e126", 354 "created_at": "2017-03-19T23:40:33Z" 355 }`, 356 e: &GitHubIssueEvent{ 357 ID: 1006040931, 358 Type: "closed", 359 Created: t3339("2017-03-19T23:40:33Z"), 360 Actor: &GitHubUser{ 361 ID: 2621, 362 Login: "bradfitz", 363 }, 364 CommitID: "e4d70f7e8892f024e4ed3e8b99ee6c5a9f16e126", 365 CommitURL: "https://api.github.com/repos/bradfitz/go-issue-mirror/commits/e4d70f7e8892f024e4ed3e8b99ee6c5a9f16e126", 366 }, 367 p: &maintpb.GithubIssueEvent{ 368 Id: 1006040931, 369 EventType: "closed", 370 ActorId: 2621, 371 Created: p3339("2017-03-19T23:40:33Z"), 372 Commit: &maintpb.GithubCommit{ 373 Owner: "bradfitz", 374 Repo: "go-issue-mirror", 375 CommitId: "e4d70f7e8892f024e4ed3e8b99ee6c5a9f16e126", 376 }, 377 }, 378 }, 379 380 { 381 name: "reopened", 382 j: `{ 383 "id": 1000014895, 384 "url": "https://api.github.com/repos/bradfitz/go-issue-mirror/issues/events/1000014895", 385 "actor": { 386 "login": "bradfitz", 387 "id": 2621 388 }, 389 "event": "reopened", 390 "commit_id": null, 391 "commit_url": null, 392 "created_at": "2017-03-14T23:26:21Z" 393 }`, 394 e: &GitHubIssueEvent{ 395 ID: 1000014895, 396 Type: "reopened", 397 Created: t3339("2017-03-14T23:26:21Z"), 398 Actor: &GitHubUser{ 399 ID: 2621, 400 Login: "bradfitz", 401 }, 402 }, 403 p: &maintpb.GithubIssueEvent{ 404 Id: 1000014895, 405 EventType: "reopened", 406 ActorId: 2621, 407 Created: p3339("2017-03-14T23:26:21Z"), 408 }, 409 }, 410 411 { 412 name: "referenced", 413 j: `{ 414 "id": 1006040930, 415 "url": "https://api.github.com/repos/bradfitz/go-issue-mirror/issues/events/1006040930", 416 "actor": { 417 "login": "bradfitz", 418 "id": 2621 419 }, 420 "event": "referenced", 421 "commit_id": "e4d70f7e8892f024e4ed3e8b99ee6c5a9f16e126", 422 "commit_url": "https://api.github.com/repos/bradfitz/go-issue-mirror/commits/e4d70f7e8892f024e4ed3e8b99ee6c5a9f16e126", 423 "created_at": "2017-03-19T23:40:32Z" 424 }`, 425 e: &GitHubIssueEvent{ 426 ID: 1006040930, 427 Type: "referenced", 428 Created: t3339("2017-03-19T23:40:32Z"), 429 Actor: &GitHubUser{ 430 ID: 2621, 431 Login: "bradfitz", 432 }, 433 CommitID: "e4d70f7e8892f024e4ed3e8b99ee6c5a9f16e126", 434 CommitURL: "https://api.github.com/repos/bradfitz/go-issue-mirror/commits/e4d70f7e8892f024e4ed3e8b99ee6c5a9f16e126", 435 }, 436 p: &maintpb.GithubIssueEvent{ 437 Id: 1006040930, 438 EventType: "referenced", 439 ActorId: 2621, 440 Created: p3339("2017-03-19T23:40:32Z"), 441 Commit: &maintpb.GithubCommit{ 442 Owner: "bradfitz", 443 Repo: "go-issue-mirror", 444 CommitId: "e4d70f7e8892f024e4ed3e8b99ee6c5a9f16e126", 445 }, 446 }, 447 }, 448 449 { 450 name: "renamed", 451 j: `{ 452 "id": 1006107803, 453 "url": "https://api.github.com/repos/bradfitz/go-issue-mirror/issues/events/1006107803", 454 "actor": { 455 "login": "bradfitz", 456 "id": 2621 457 }, 458 "event": "renamed", 459 "commit_id": null, 460 "commit_url": null, 461 "created_at": "2017-03-20T02:53:43Z", 462 "rename": { 463 "from": "test-2", 464 "to": "test-2 new name" 465 } 466 }`, 467 e: &GitHubIssueEvent{ 468 ID: 1006107803, 469 Type: "renamed", 470 Created: t3339("2017-03-20T02:53:43Z"), 471 Actor: &GitHubUser{ 472 ID: 2621, 473 Login: "bradfitz", 474 }, 475 From: "test-2", 476 To: "test-2 new name", 477 }, 478 p: &maintpb.GithubIssueEvent{ 479 Id: 1006107803, 480 EventType: "renamed", 481 ActorId: 2621, 482 Created: p3339("2017-03-20T02:53:43Z"), 483 RenameFrom: "test-2", 484 RenameTo: "test-2 new name", 485 }, 486 }, 487 488 { 489 name: "Extra Unknown JSON", 490 j: `{ 491 "id": 998144526, 492 "url": "https://api.github.com/repos/bradfitz/go-issue-mirror/issues/events/998144526", 493 "actor": { 494 "login": "bradfitz", 495 "id": 2621 496 }, 497 "event": "labeled", 498 "commit_id": null, 499 "commit_url": null, 500 "created_at": "2017-03-13T22:39:28Z", 501 "label": { 502 "name": "enhancement", 503 "color": "84b6eb" 504 }, 505 "random_new_field": "some new thing that GitHub API may add" 506 } 507 `, 508 e: &GitHubIssueEvent{ 509 ID: 998144526, 510 Type: "labeled", 511 Created: t3339("2017-03-13T22:39:28Z"), 512 Actor: &GitHubUser{ 513 ID: 2621, 514 Login: "bradfitz", 515 }, 516 Label: "enhancement", 517 OtherJSON: `{"random_new_field":"some new thing that GitHub API may add"}`, 518 }, 519 p: &maintpb.GithubIssueEvent{ 520 Id: 998144526, 521 EventType: "labeled", 522 ActorId: 2621, 523 Created: p3339("2017-03-13T22:39:28Z"), 524 Label: &maintpb.GithubLabel{Name: "enhancement"}, 525 OtherJson: []byte(`{"random_new_field":"some new thing that GitHub API may add"}`), 526 }, 527 }, 528 } 529 530 var eventTypes []string 531 532 for _, tt := range tests { 533 evts, err := parseGithubEvents(strings.NewReader("[" + tt.j + "]")) 534 if err != nil { 535 t.Errorf("%s: parse JSON: %v", tt.name, err) 536 continue 537 } 538 if len(evts) != 1 { 539 t.Errorf("%s: parse JSON = %v entries; want 1", tt.name, len(evts)) 540 continue 541 } 542 gote := evts[0] 543 if !reflect.DeepEqual(gote, tt.e) { 544 t.Errorf("%s: JSON -> githubEvent differs: %v", tt.name, DeepDiff(gote, tt.e)) 545 continue 546 } 547 eventTypes = append(eventTypes, gote.Type) 548 549 gotp := gote.Proto() 550 if !reflect.DeepEqual(gotp, tt.p) { 551 t.Errorf("%s: githubEvent -> proto differs: %v", tt.name, DeepDiff(gotp, tt.p)) 552 continue 553 } 554 555 var c Corpus 556 c.initGithub() 557 c.github.getOrCreateUserID(2621).Login = "bradfitz" 558 c.github.getOrCreateUserID(1924134).Login = "dmitshur" 559 gr := c.github.getOrCreateRepo("foowner", "bar") 560 e2 := gr.newGithubEvent(gotp) 561 562 if !reflect.DeepEqual(e2, tt.e) { 563 t.Errorf("%s: proto -> githubEvent differs: %v", tt.name, DeepDiff(e2, tt.e)) 564 continue 565 } 566 } 567 568 t.Logf("Tested event types: %q", eventTypes) 569 } 570 571 func TestParseMultipleGithubEvents(t *testing.T) { 572 content, err := os.ReadFile(filepath.Join("testdata", "TestParseMultipleGithubEvents.json")) 573 if err != nil { 574 t.Errorf("error while loading testdata: %s\n", err.Error()) 575 } 576 evts, err := parseGithubEvents(strings.NewReader(string(content))) 577 if err != nil { 578 t.Errorf("error was not expected: %s\n", err.Error()) 579 } 580 if len(evts) != 7 { 581 t.Errorf("there should have been three events. was: %d\n", len(evts)) 582 } 583 lastEvent := evts[len(evts)-1] 584 if lastEvent.Type != "closed" { 585 t.Errorf("the last event's type should have been closed. was: %s\n", lastEvent.Type) 586 } 587 } 588 589 func TestParseMultipleGithubEventsWithForeach(t *testing.T) { 590 issue := &GitHubIssue{ 591 PullRequest: true, 592 events: map[int64]*GitHubIssueEvent{ 593 0: &GitHubIssueEvent{ 594 Type: "labelled", 595 }, 596 1: &GitHubIssueEvent{ 597 Type: "milestone", 598 }, 599 2: &GitHubIssueEvent{ 600 Type: "closed", 601 }, 602 }, 603 } 604 eventTypes := []string{"closed", "labelled", "milestone"} 605 gatheredTypes := make([]string, 0) 606 issue.ForeachEvent(func(e *GitHubIssueEvent) error { 607 gatheredTypes = append(gatheredTypes, e.Type) 608 return nil 609 }) 610 sort.Strings(gatheredTypes) 611 if !reflect.DeepEqual(eventTypes, gatheredTypes) { 612 t.Fatalf("want event types: %v; got: %v\n", eventTypes, gatheredTypes) 613 } 614 } 615 616 type ClientMock struct { 617 err error 618 status string 619 statusCode int 620 testdata string 621 } 622 623 var timesDoWasCalled = 0 624 625 func (c *ClientMock) Do(req *http.Request) (*http.Response, error) { 626 if len(c.testdata) < 1 { 627 c.testdata = "TestParseMultipleGithubEvents.json" 628 } 629 timesDoWasCalled++ 630 content, _ := os.ReadFile(filepath.Join("testdata", c.testdata)) 631 headers := make(http.Header, 0) 632 t := time.Now() 633 var b []byte 634 headers["Date"] = []string{string(t.AppendFormat(b, "Mon Jan _2 15:04:05 2006"))} 635 return &http.Response{ 636 Body: io.NopCloser(bytes.NewReader(content)), 637 Status: c.status, 638 StatusCode: c.statusCode, 639 Header: headers, 640 }, c.err 641 } 642 643 type MockLogger struct { 644 } 645 646 var eventLog = make([]string, 0) 647 648 func (m *MockLogger) Log(mut *maintpb.Mutation) error { 649 for _, e := range mut.GithubIssue.Event { 650 eventLog = append(eventLog, e.EventType) 651 } 652 return nil 653 } 654 655 func TestSyncEvents(t *testing.T) { 656 var c Corpus 657 c.initGithub() 658 c.mutationLogger = &MockLogger{} 659 gr := c.github.getOrCreateRepo("foowner", "bar") 660 issue := &GitHubIssue{ 661 ID: 1001, 662 PullRequest: true, 663 events: map[int64]*GitHubIssueEvent{}, 664 } 665 gr.issues = map[int32]*GitHubIssue{ 666 1001: issue, 667 } 668 server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { 669 rw.Write([]byte("OK")) 670 })) 671 defer server.Close() 672 p := &githubRepoPoller{ 673 c: &c, 674 token: "foobar", 675 gr: gr, 676 githubDirect: github.NewClient(server.Client()), 677 githubCaching: github.NewClient(server.Client()), 678 } 679 t.Run("successful sync", func(t2 *testing.T) { 680 defer func() { eventLog = make([]string, 0) }() 681 timesDoWasCalled = 0 682 ctx := context.Background() 683 p.client = &ClientMock{ 684 status: "OK", 685 statusCode: http.StatusOK, 686 err: nil, 687 testdata: "TestParseMultipleGithubEvents.json", 688 } 689 err := p.syncEventsOnIssue(ctx, int32(issue.ID)) 690 if err != nil { 691 t2.Error(err) 692 } 693 want := []string{"labeled", "labeled", "labeled", "labeled", "labeled", "milestoned", "closed"} 694 if !reflect.DeepEqual(want, eventLog) { 695 t2.Errorf("want: %v; got: %v\n", want, eventLog) 696 } 697 698 wantTimesCalled := 1 699 if timesDoWasCalled != wantTimesCalled { 700 t.Errorf("client.Do should have been called %d times. got: %d\n", wantTimesCalled, timesDoWasCalled) 701 } 702 }) 703 t.Run("successful sync missing milestones", func(t2 *testing.T) { 704 defer func() { eventLog = make([]string, 0) }() 705 timesDoWasCalled = 0 706 ctx := context.Background() 707 p.client = &ClientMock{ 708 status: "OK", 709 statusCode: http.StatusOK, 710 err: nil, 711 testdata: "TestMissingMilestoneEvents.json", 712 } 713 err := p.syncEventsOnIssue(ctx, int32(issue.ID)) 714 if err != nil { 715 t2.Error(err) 716 } 717 want := []string{"mentioned", "subscribed", "mentioned", "subscribed", "assigned", "labeled", "labeled", "milestoned", "renamed", "demilestoned", "milestoned"} 718 sort.Strings(want) 719 sort.Strings(eventLog) 720 if !reflect.DeepEqual(want, eventLog) { 721 t2.Errorf("want: %v; got: %v\n", want, eventLog) 722 } 723 724 wantTimesCalled := 1 725 if timesDoWasCalled != wantTimesCalled { 726 t.Errorf("client.Do should have been called %d times. got: %d\n", wantTimesCalled, timesDoWasCalled) 727 } 728 }) 729 } 730 731 func TestSyncMultipleConsecutiveEvents(t *testing.T) { 732 var c Corpus 733 c.initGithub() 734 c.mutationLogger = &MockLogger{} 735 gr := c.github.getOrCreateRepo("foowner", "bar") 736 issue := &GitHubIssue{ 737 ID: 1001, 738 PullRequest: true, 739 events: map[int64]*GitHubIssueEvent{}, 740 } 741 gr.issues = map[int32]*GitHubIssue{ 742 1001: issue, 743 } 744 server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { 745 rw.Write([]byte("OK")) 746 })) 747 defer server.Close() 748 p := &githubRepoPoller{ 749 c: &c, 750 token: "foobar", 751 gr: gr, 752 githubDirect: github.NewClient(server.Client()), 753 githubCaching: github.NewClient(server.Client()), 754 } 755 t.Run("successful sync", func(t2 *testing.T) { 756 defer func() { eventLog = make([]string, 0) }() 757 timesDoWasCalled = 0 758 ctx := context.Background() 759 for i := 1; i < 5; i++ { 760 testdata := fmt.Sprintf("TestParseMultipleGithubEvents_p%d.json", i) 761 p.client = &ClientMock{ 762 status: "OK", 763 statusCode: http.StatusOK, 764 err: nil, 765 testdata: testdata, 766 } 767 err := p.syncEventsOnIssue(ctx, int32(issue.ID)) 768 if err != nil { 769 t2.Fatal(err) 770 } 771 } 772 773 want := []string{"labeled", "labeled", "labeled", "labeled", "labeled", "milestoned", "closed"} 774 if !reflect.DeepEqual(want, eventLog) { 775 t2.Errorf("want: %v; got: %v\n", want, eventLog) 776 } 777 778 wantTimesCalled := 4 779 if timesDoWasCalled != wantTimesCalled { 780 t.Errorf("client.Do should have been called %d times. got: %d\n", wantTimesCalled, timesDoWasCalled) 781 } 782 }) 783 } 784 785 func TestParseGitHubReviews(t *testing.T) { 786 tests := []struct { 787 name string // test 788 j string // JSON from Github API 789 e *GitHubReview // in-memory 790 p *maintpb.GithubReview // on disk 791 }{ 792 { 793 name: "Approved", 794 j: `{ 795 "id": 123456, 796 "node_id": "548913adsafas84asdf48a", 797 "user": { 798 "login": "bradfitz", 799 "id": 2621 800 }, 801 "body": "I approve this commit", 802 "state": "APPROVED", 803 "html_url": "https://github.com/bradfitz/go-issue-mirror/pull/21", 804 "pull_request_url": "https://github.com/bradfitz/go-issue-mirror/pull/21", 805 "author_association": "CONTRIBUTOR", 806 "_links":{ 807 "html":{ 808 "href": "https://github.com/bradfitz/go-issue-mirror/pull/21" 809 }, 810 "pull_request":{ 811 "href": "https://github.com/bradfitz/go-issue-mirror/pull/21" 812 } 813 }, 814 "submitted_at": "2018-03-22T00:26:48Z", 815 "commit_id" : "e4d70f7e8892f024e4ed3e8b99ee6c5a9f16e126" 816 }`, 817 e: &GitHubReview{ 818 ID: 123456, 819 Actor: &GitHubUser{ 820 ID: 2621, 821 Login: "bradfitz", 822 }, 823 Body: "I approve this commit", 824 State: "APPROVED", 825 CommitID: "e4d70f7e8892f024e4ed3e8b99ee6c5a9f16e126", 826 ActorAssociation: "CONTRIBUTOR", 827 Created: t3339("2018-03-22T00:26:48Z"), 828 }, 829 p: &maintpb.GithubReview{ 830 Id: 123456, 831 ActorId: 2621, 832 Body: "I approve this commit", 833 State: "APPROVED", 834 CommitId: "e4d70f7e8892f024e4ed3e8b99ee6c5a9f16e126", 835 ActorAssociation: "CONTRIBUTOR", 836 Created: p3339("2018-03-22T00:26:48Z"), 837 }, 838 }, 839 { 840 name: "Extra Unknown JSON", 841 j: `{ 842 "id": 123456, 843 "node_id": "548913adsafas84asdf48a", 844 "user": { 845 "login": "bradfitz", 846 "id": 2621 847 }, 848 "body": "I approve this commit", 849 "state": "APPROVED", 850 "html_url": "https://github.com/bradfitz/go-issue-mirror/pull/21", 851 "pull_request_url": "https://github.com/bradfitz/go-issue-mirror/pull/21", 852 "author_association": "CONTRIBUTOR", 853 "_links":{ 854 "html":{ 855 "href": "https://github.com/bradfitz/go-issue-mirror/pull/21" 856 }, 857 "pull_request":{ 858 "href": "https://github.com/bradfitz/go-issue-mirror/pull/21" 859 } 860 }, 861 "submitted_at": "2018-03-22T00:26:48Z", 862 "commit_id" : "e4d70f7e8892f024e4ed3e8b99ee6c5a9f16e126", 863 "random_new_field": "some new thing that GitHub API may add" 864 }`, 865 e: &GitHubReview{ 866 ID: 123456, 867 Actor: &GitHubUser{ 868 ID: 2621, 869 Login: "bradfitz", 870 }, 871 Body: "I approve this commit", 872 State: "APPROVED", 873 CommitID: "e4d70f7e8892f024e4ed3e8b99ee6c5a9f16e126", 874 ActorAssociation: "CONTRIBUTOR", 875 Created: t3339("2018-03-22T00:26:48Z"), 876 OtherJSON: `{"random_new_field":"some new thing that GitHub API may add"}`, 877 }, 878 p: &maintpb.GithubReview{ 879 Id: 123456, 880 ActorId: 2621, 881 Body: "I approve this commit", 882 State: "APPROVED", 883 CommitId: "e4d70f7e8892f024e4ed3e8b99ee6c5a9f16e126", 884 ActorAssociation: "CONTRIBUTOR", 885 Created: p3339("2018-03-22T00:26:48Z"), 886 OtherJson: []byte(`{"random_new_field":"some new thing that GitHub API may add"}`), 887 }, 888 }, 889 } 890 891 for _, tt := range tests { 892 evts, err := parseGithubReviews(strings.NewReader("[" + tt.j + "]")) 893 if err != nil { 894 t.Errorf("%s: parse JSON: %v", tt.name, err) 895 continue 896 } 897 if len(evts) != 1 { 898 t.Errorf("%s: parse JSON = %v entries; want 1", tt.name, len(evts)) 899 continue 900 } 901 gote := evts[0] 902 if !reflect.DeepEqual(gote, tt.e) { 903 t.Errorf("%s: JSON -> githubReviewEvent differs: %v", tt.name, DeepDiff(gote, tt.e)) 904 continue 905 } 906 907 gotp := gote.Proto() 908 if !reflect.DeepEqual(gotp, tt.p) { 909 t.Errorf("%s: githubReviewEvent -> proto differs: %v", tt.name, DeepDiff(gotp, tt.p)) 910 continue 911 } 912 913 var c Corpus 914 c.initGithub() 915 c.github.getOrCreateUserID(2621).Login = "bradfitz" 916 c.github.getOrCreateUserID(1924134).Login = "dmitshur" 917 gr := c.github.getOrCreateRepo("foowner", "bar") 918 e2 := gr.newGithubReview(gotp) 919 920 if !reflect.DeepEqual(e2, tt.e) { 921 t.Errorf("%s: proto -> githubReviewEvent differs: %v", tt.name, DeepDiff(e2, tt.e)) 922 continue 923 } 924 } 925 } 926 927 func TestForeachRepo(t *testing.T) { 928 tests := []struct { 929 name string 930 issue *GitHubIssue 931 want []string 932 wantErr error 933 }{ 934 { 935 name: "Skips non-PullRequests", 936 issue: &GitHubIssue{ 937 PullRequest: false, 938 }, 939 want: []string{}, 940 wantErr: nil, 941 }, 942 { 943 name: "Processes Multiple in Order", 944 issue: &GitHubIssue{ 945 PullRequest: true, 946 reviews: map[int64]*GitHubReview{ 947 0: &GitHubReview{ 948 Body: "Second", 949 Created: t3339("2018-04-22T00:26:48Z"), 950 }, 951 1: &GitHubReview{ 952 Body: "First", 953 Created: t3339("2018-03-22T00:26:48Z"), 954 }, 955 }, 956 }, 957 want: []string{"First", "Second"}, 958 wantErr: nil, 959 }, 960 { 961 name: "Will Error", 962 issue: &GitHubIssue{ 963 PullRequest: true, 964 reviews: map[int64]*GitHubReview{ 965 0: &GitHubReview{ 966 Body: "Fail", 967 }, 968 }, 969 }, 970 want: []string{}, 971 wantErr: fmt.Errorf("Planned Failure"), 972 }, 973 { 974 name: "Will Error Late", 975 issue: &GitHubIssue{ 976 PullRequest: true, 977 reviews: map[int64]*GitHubReview{ 978 0: &GitHubReview{ 979 Body: "First Event", 980 Created: t3339("2018-03-22T00:26:48Z"), 981 }, 982 1: &GitHubReview{ 983 Body: "Fail", 984 Created: t3339("2018-04-22T00:26:48Z"), 985 }, 986 2: &GitHubReview{ 987 Body: "Third Event", 988 Created: t3339("2018-05-22T00:26:48Z"), 989 }, 990 }, 991 }, 992 want: []string{"First Event"}, 993 wantErr: fmt.Errorf("Planned Failure"), 994 }} 995 996 for _, tt := range tests { 997 got := make([]string, 0) 998 999 err := tt.issue.ForeachReview(func(r *GitHubReview) error { 1000 if r.Body == "Fail" { 1001 return fmt.Errorf("Planned Failure") 1002 } 1003 got = append(got, r.Body) 1004 return nil 1005 }) 1006 1007 if !equalError(tt.wantErr, err) { 1008 t.Errorf("%s: ForeachReview errs differ. got: %s, want: %s", tt.name, err, tt.wantErr) 1009 } 1010 1011 if !reflect.DeepEqual(got, tt.want) { 1012 t.Errorf("%s: ForeachReview calls differ. got: %s want: %s", tt.name, got, tt.want) 1013 } 1014 } 1015 1016 t.Log("Tested Reviews") 1017 } 1018 1019 // equalError reports whether errors a and b are considered equal. 1020 // They're equal if both are nil, or both are not nil and a.Error() == b.Error(). 1021 func equalError(a, b error) bool { 1022 return a == nil && b == nil || a != nil && b != nil && a.Error() == b.Error() 1023 } 1024 1025 func TestCacheableURL(t *testing.T) { 1026 tests := []struct { 1027 v string 1028 want bool 1029 }{ 1030 {"https://api.github.com/repos/OWNER/RePO/milestones?page=1", true}, 1031 {"https://api.github.com/repos/OWNER/RePO/milestones?page=2", false}, 1032 {"https://api.github.com/repos/OWNER/RePO/milestones?", false}, 1033 {"https://api.github.com/repos/OWNER/RePO/milestones", false}, 1034 1035 {"https://api.github.com/repos/OWNER/RePO/labels?page=1", true}, 1036 {"https://api.github.com/repos/OWNER/RePO/labels?page=2", false}, 1037 {"https://api.github.com/repos/OWNER/RePO/labels?", false}, 1038 {"https://api.github.com/repos/OWNER/RePO/labels", false}, 1039 1040 {"https://api.github.com/repos/OWNER/RePO/foos?page=1", false}, 1041 1042 {"https://api.github.com/repos/OWNER/RePO/issues?page=1", false}, 1043 {"https://api.github.com/repos/OWNER/RePO/issues?page=1&sort=updated&direction=desc", true}, 1044 } 1045 1046 for _, tt := range tests { 1047 got := cacheableURL(tt.v) 1048 if got != tt.want { 1049 t.Errorf("cacheableURL(%q) = %v; want %v", tt.v, got, tt.want) 1050 } 1051 } 1052 } 1053 1054 func t3339(s string) time.Time { 1055 t, err := time.Parse(time.RFC3339, s) 1056 if err != nil { 1057 panic(err) 1058 } 1059 return t.UTC() 1060 } 1061 1062 func p3339(s string) *timestamp.Timestamp { 1063 tp, err := ptypes.TimestampProto(t3339(s)) 1064 if err != nil { 1065 panic(err) 1066 } 1067 return tp 1068 } 1069 1070 func TestParseGithubRefs(t *testing.T) { 1071 tests := []struct { 1072 gerritProj string // "go.googlesource.com/go", etc 1073 msg string 1074 want []string 1075 }{ 1076 {"go.googlesource.com/go", "\nFixes #1234\n", []string{"golang/go#1234"}}, 1077 {"go.googlesource.com/go", "Fixes #1234\n", []string{"golang/go#1234"}}, 1078 {"go.googlesource.com/go", "Fixes #1234", []string{"golang/go#1234"}}, 1079 {"go.googlesource.com/go", "Fixes golang/go#1234", []string{"golang/go#1234"}}, 1080 {"go.googlesource.com/go", "Fixes golang/go#1234\n", []string{"golang/go#1234"}}, 1081 {"go.googlesource.com/go", "Fixes golang/go#1234.", []string{"golang/go#1234"}}, 1082 {"go.googlesource.com/go", "Mention issue #1234 a second time.\n\nFixes #1234.", []string{"golang/go#1234"}}, 1083 {"go.googlesource.com/go", "Mention issue #1234 a second time.\n\nFixes #1234.\nUpdates #1235.", []string{"golang/go#1234", "golang/go#1235"}}, 1084 {"go.googlesource.com/net", "Fixes golang/go#1234.", []string{"golang/go#1234"}}, 1085 {"go.googlesource.com/net", "Fixes #1234", nil}, 1086 } 1087 for _, tt := range tests { 1088 c := new(Corpus) 1089 var got []string 1090 for _, ref := range c.parseGithubRefs(tt.gerritProj, tt.msg) { 1091 got = append(got, ref.String()) 1092 } 1093 if !reflect.DeepEqual(got, tt.want) { 1094 t.Errorf("parseGithubRefs(%q, %q) = %q; want %q", tt.gerritProj, tt.msg, got, tt.want) 1095 } 1096 } 1097 }