github.com/friedemannf/reviewdog@v0.14.0/service/github/github_test.go (about) 1 package github 2 3 import ( 4 "context" 5 "encoding/json" 6 "net/http" 7 "net/http/httptest" 8 "net/url" 9 "os" 10 "strings" 11 "testing" 12 13 "github.com/google/go-github/v37/github" 14 "github.com/kylelemons/godebug/pretty" 15 "golang.org/x/oauth2" 16 17 "github.com/friedemannf/reviewdog" 18 "github.com/friedemannf/reviewdog/filter" 19 "github.com/friedemannf/reviewdog/proto/rdf" 20 "github.com/friedemannf/reviewdog/service/commentutil" 21 ) 22 23 const notokenSkipTestMes = "skipping test (requires actual Personal access tokens. export REVIEWDOG_TEST_GITHUB_API_TOKEN=<GitHub Personal Access Token>)" 24 25 func setupGitHubClient() *github.Client { 26 token := os.Getenv("REVIEWDOG_TEST_GITHUB_API_TOKEN") 27 if token == "" { 28 return nil 29 } 30 ts := oauth2.StaticTokenSource( 31 &oauth2.Token{AccessToken: token}, 32 ) 33 tc := oauth2.NewClient(context.TODO(), ts) 34 return github.NewClient(tc) 35 } 36 37 func setupEnvs() (cleanup func()) { 38 var cleanEnvs = []string{ 39 "GITHUB_ACTIONS", 40 } 41 saveEnvs := make(map[string]string) 42 for _, key := range cleanEnvs { 43 saveEnvs[key] = os.Getenv(key) 44 os.Unsetenv(key) 45 } 46 return func() { 47 for key, value := range saveEnvs { 48 os.Setenv(key, value) 49 } 50 } 51 } 52 53 func moveToRootDir() { 54 os.Chdir("../..") 55 } 56 57 func TestGitHubPullRequest_Post(t *testing.T) { 58 t.Skip("skipping test which post comments actually") 59 client := setupGitHubClient() 60 if client == nil { 61 t.Skip(notokenSkipTestMes) 62 } 63 64 // https://github.com/friedemannf/reviewdog/pull/2 65 owner := "haya14busa" 66 repo := "reviewdog" 67 pr := 2 68 sha := "cce89afa9ac5519a7f5b1734db2e3aa776b138a7" 69 70 g, err := NewGitHubPullRequest(client, owner, repo, pr, sha) 71 if err != nil { 72 t.Fatal(err) 73 } 74 comment := &reviewdog.Comment{ 75 Result: &filter.FilteredDiagnostic{ 76 Diagnostic: &rdf.Diagnostic{ 77 Location: &rdf.Location{ 78 Path: "watchdogs.go", 79 }, 80 Message: "[reviewdog] test", 81 }, 82 InDiffContext: true, 83 }, 84 } 85 // https://github.com/friedemannf/reviewdog/pull/2/files#diff-ed1d019a10f54464cfaeaf6a736b7d27L20 86 if err := g.Post(context.Background(), comment); err != nil { 87 t.Error(err) 88 } 89 if err := g.Flush(context.Background()); err != nil { 90 t.Error(err) 91 } 92 } 93 94 func TestGitHubPullRequest_Diff(t *testing.T) { 95 if testing.Short() { 96 t.Skip("skipping test which contains actual API requests in short mode") 97 } 98 client := setupGitHubClient() 99 if client == nil { 100 t.Skip(notokenSkipTestMes) 101 } 102 103 want := `diff --git a/diff.go b/diff.go 104 index b380b67..6abc0f1 100644 105 --- a/diff.go 106 +++ b/diff.go 107 @@ -4,6 +4,9 @@ import ( 108 "os/exec" 109 ) 110 111 +func TestNewExportedFunc() { 112 +} 113 + 114 var _ DiffService = &DiffString{} 115 116 type DiffString struct { 117 diff --git a/reviewdog.go b/reviewdog.go 118 index 61450f3..f63f149 100644 119 --- a/reviewdog.go 120 +++ b/reviewdog.go 121 @@ -10,18 +10,18 @@ import ( 122 "github.com/friedemannf/reviewdog/diff" 123 ) 124 125 +var TestExportedVarWithoutComment = 1 126 + 127 +func NewReviewdog(p Parser, c CommentService, d DiffService) *Reviewdog { 128 + return &Reviewdog{p: p, c: c, d: d} 129 +} 130 + 131 type Reviewdog struct { 132 p Parser 133 c CommentService 134 d DiffService 135 } 136 137 -func NewReviewdog(p Parser, c CommentService, d DiffService) *Reviewdog { 138 - return &Reviewdog{p: p, c: c, d: d} 139 -} 140 - 141 -// CheckResult represents a checked result of static analysis tools. 142 -// :h error-file-format 143 type CheckResult struct { 144 Path string // file path 145 Lnum int // line number 146 ` 147 148 // https://github.com/friedemannf/reviewdog/pull/2 149 owner := "haya14busa" 150 repo := "reviewdog" 151 pr := 2 152 g, err := NewGitHubPullRequest(client, owner, repo, pr, "") 153 if err != nil { 154 t.Fatal(err) 155 } 156 b, err := g.Diff(context.Background()) 157 if err != nil { 158 t.Fatal(err) 159 } 160 if got := string(b); got != want { 161 t.Errorf("got:\n%v\nwant:\n%v", got, want) 162 } 163 } 164 165 func TestGitHubPullRequest_comment(t *testing.T) { 166 if testing.Short() { 167 t.Skip("skipping test which contains actual API requests in short mode") 168 } 169 client := setupGitHubClient() 170 if client == nil { 171 t.Skip(notokenSkipTestMes) 172 } 173 // https://github.com/friedemannf/reviewdog/pull/2 174 owner := "haya14busa" 175 repo := "reviewdog" 176 pr := 2 177 g, err := NewGitHubPullRequest(client, owner, repo, pr, "") 178 if err != nil { 179 t.Fatal(err) 180 } 181 comments, err := g.comment(context.Background()) 182 if err != nil { 183 t.Fatal(err) 184 } 185 for _, c := range comments { 186 t.Log("---") 187 t.Log(*c.Body) 188 t.Log(*c.Path) 189 if c.Position != nil { 190 t.Log(*c.Position) 191 } 192 t.Log(*c.CommitID) 193 } 194 } 195 196 func TestGitHubPullRequest_Post_Flush_review_api(t *testing.T) { 197 cwd, _ := os.Getwd() 198 defer os.Chdir(cwd) 199 moveToRootDir() 200 defer setupEnvs()() 201 202 listCommentsAPICalled := 0 203 postCommentsAPICalled := 0 204 mux := http.NewServeMux() 205 mux.HandleFunc("/repos/o/r/pulls/14/comments", func(w http.ResponseWriter, r *http.Request) { 206 listCommentsAPICalled++ 207 if r.Method != http.MethodGet { 208 t.Errorf("unexpected access: %v %v", r.Method, r.URL) 209 } 210 switch r.URL.Query().Get("page") { 211 default: 212 cs := []*github.PullRequestComment{ 213 { 214 Path: github.String("reviewdog.go"), 215 Line: github.Int(2), 216 Body: github.String(commentutil.BodyPrefix + "already commented"), 217 }, 218 } 219 w.Header().Add("Link", `<https://api.github.com/repos/o/r/pulls/14/comments?page=2>; rel="next"`) 220 if err := json.NewEncoder(w).Encode(cs); err != nil { 221 t.Fatal(err) 222 } 223 case "2": 224 cs := []*github.PullRequestComment{ 225 { 226 Path: github.String("reviewdog.go"), 227 Line: github.Int(15), 228 Body: github.String(commentutil.BodyPrefix + "already commented 2"), 229 }, 230 { 231 Path: github.String("reviewdog.go"), 232 StartLine: github.Int(15), 233 Line: github.Int(16), 234 Body: github.String(commentutil.BodyPrefix + "multiline existing comment"), 235 }, 236 { 237 Path: github.String("reviewdog.go"), 238 StartLine: github.Int(15), 239 Line: github.Int(17), 240 Body: github.String(commentutil.BodyPrefix + "multiline existing comment (line-break)"), 241 }, 242 } 243 if err := json.NewEncoder(w).Encode(cs); err != nil { 244 t.Fatal(err) 245 } 246 } 247 }) 248 mux.HandleFunc("/repos/o/r/pulls/14/reviews", func(w http.ResponseWriter, r *http.Request) { 249 postCommentsAPICalled++ 250 if r.Method != http.MethodPost { 251 t.Errorf("unexpected access: %v %v", r.Method, r.URL) 252 } 253 var req github.PullRequestReviewRequest 254 if err := json.NewDecoder(r.Body).Decode(&req); err != nil { 255 t.Error(err) 256 } 257 if *req.Event != "COMMENT" { 258 t.Errorf("PullRequestReviewRequest.Event = %v, want COMMENT", *req.Event) 259 } 260 if req.Body != nil && *req.Body != "" { 261 t.Errorf("PullRequestReviewRequest.Body = %v, want empty", *req.Body) 262 } 263 if *req.CommitID != "sha" { 264 t.Errorf("PullRequestReviewRequest.Body = %v, want empty", *req.Body) 265 } 266 want := []*github.DraftReviewComment{ 267 { 268 Path: github.String("reviewdog.go"), 269 Side: github.String("RIGHT"), 270 Line: github.Int(15), 271 Body: github.String(commentutil.BodyPrefix + "new comment"), 272 }, 273 { 274 Path: github.String("reviewdog.go"), 275 Side: github.String("RIGHT"), 276 StartSide: github.String("RIGHT"), 277 StartLine: github.Int(15), 278 Line: github.Int(16), 279 Body: github.String(commentutil.BodyPrefix + "multiline new comment"), 280 }, 281 { 282 Path: github.String("reviewdog.go"), 283 Side: github.String("RIGHT"), 284 StartSide: github.String("RIGHT"), 285 StartLine: github.Int(15), 286 Line: github.Int(16), 287 Body: github.String(commentutil.BodyPrefix + strings.Join([]string{ 288 "multiline suggestion comment", 289 "```suggestion", 290 "line1", 291 "line2", 292 "line3", 293 "```", 294 }, "\n") + "\n"), 295 }, 296 { 297 Path: github.String("reviewdog.go"), 298 Side: github.String("RIGHT"), 299 Line: github.Int(15), 300 Body: github.String(commentutil.BodyPrefix + strings.Join([]string{ 301 "singleline suggestion comment", 302 "```suggestion", 303 "line1", 304 "line2", 305 "```", 306 }, "\n") + "\n"), 307 }, 308 { 309 Path: github.String("reviewdog.go"), 310 Side: github.String("RIGHT"), 311 StartSide: github.String("RIGHT"), 312 StartLine: github.Int(15), 313 Line: github.Int(16), 314 Body: github.String(commentutil.BodyPrefix + strings.Join([]string{ 315 "invalid lines suggestion comment", 316 invalidSuggestionPre + "GitHub comment range and suggestion line range must be same. L15-L16 v.s. L16-L17" + invalidSuggestionPost, 317 }, "\n") + "\n"), 318 }, 319 { 320 Path: github.String("reviewdog.go"), 321 Side: github.String("RIGHT"), 322 StartSide: github.String("RIGHT"), 323 StartLine: github.Int(14), 324 Line: github.Int(16), 325 Body: github.String(commentutil.BodyPrefix + strings.Join([]string{ 326 "Use suggestion range as GitHub comment range if the suggestion is in diff context", 327 "```suggestion", 328 "line1", 329 "line2", 330 "line3", 331 "```", 332 }, "\n") + "\n"), 333 }, 334 { 335 Path: github.String("reviewdog.go"), 336 Side: github.String("RIGHT"), 337 StartSide: github.String("RIGHT"), 338 StartLine: github.Int(14), 339 Line: github.Int(16), 340 Body: github.String(commentutil.BodyPrefix + strings.Join([]string{ 341 "Partially invalid suggestions", 342 "```suggestion", 343 "line1", 344 "line2", 345 "line3", 346 "```", 347 invalidSuggestionPre + "GitHub comment range and suggestion line range must be same. L14-L16 v.s. L14-L14" + invalidSuggestionPost, 348 }, "\n") + "\n"), 349 }, 350 { 351 Path: github.String("reviewdog.go"), 352 Side: github.String("RIGHT"), 353 StartSide: github.String("RIGHT"), 354 StartLine: github.Int(15), 355 Line: github.Int(16), 356 Body: github.String(commentutil.BodyPrefix + strings.Join([]string{ 357 "non-line based suggestion comment (no source lines)", 358 invalidSuggestionPre + "source lines are not available" + invalidSuggestionPost, 359 }, "\n") + "\n"), 360 }, 361 { 362 Path: github.String("reviewdog.go"), 363 Side: github.String("RIGHT"), 364 Line: github.Int(15), 365 Body: github.String(commentutil.BodyPrefix + strings.Join([]string{ 366 "range suggestion (single line)", 367 "```suggestion", 368 "haya14busa", 369 "```", 370 }, "\n") + "\n"), 371 }, 372 { 373 Path: github.String("reviewdog.go"), 374 Side: github.String("RIGHT"), 375 StartSide: github.String("RIGHT"), 376 StartLine: github.Int(15), 377 Line: github.Int(16), 378 Body: github.String(commentutil.BodyPrefix + strings.Join([]string{ 379 "range suggestion (multi-line)", 380 "```suggestion", 381 "haya14busa (multi-line)", 382 "```", 383 }, "\n") + "\n"), 384 }, 385 { 386 Path: github.String("reviewdog.go"), 387 Side: github.String("RIGHT"), 388 StartSide: github.String("RIGHT"), 389 StartLine: github.Int(15), 390 Line: github.Int(17), 391 Body: github.String(commentutil.BodyPrefix + strings.Join([]string{ 392 "range suggestion (line-break, remove)", 393 "```suggestion", 394 "line 15 (content at line 15)", 395 "```", 396 }, "\n") + "\n"), 397 }, 398 { 399 Path: github.String("reviewdog.go"), 400 Side: github.String("RIGHT"), 401 Line: github.Int(15), 402 Body: github.String(commentutil.BodyPrefix + strings.Join([]string{ 403 "range suggestion (insert)", 404 "```suggestion", 405 "haya14busa", 406 "```", 407 }, "\n") + "\n"), 408 }, 409 { 410 Path: github.String("reviewdog.go"), 411 Side: github.String("RIGHT"), 412 Line: github.Int(15), 413 Body: github.String(commentutil.BodyPrefix + strings.Join([]string{ 414 "multiple suggestions", 415 "```suggestion", 416 "haya1busa", 417 "```", 418 "```suggestion", 419 "haya4busa", 420 "```", 421 "```suggestion", 422 "haya14busa", 423 "```", 424 }, "\n") + "\n"), 425 }, 426 { 427 Path: github.String("reviewdog.go"), 428 Side: github.String("RIGHT"), 429 Line: github.Int(15), 430 Body: github.String(commentutil.BodyPrefix + strings.Join([]string{ 431 "range suggestion with start only location", 432 "```suggestion", 433 "haya14busa", 434 "```", 435 }, "\n") + "\n"), 436 }, 437 } 438 if diff := pretty.Compare(want, req.Comments); diff != "" { 439 t.Errorf("req.Comments diff: (-got +want)\n%s", diff) 440 } 441 }) 442 ts := httptest.NewServer(mux) 443 defer ts.Close() 444 445 cli := github.NewClient(nil) 446 cli.BaseURL, _ = url.Parse(ts.URL + "/") 447 g, err := NewGitHubPullRequest(cli, "o", "r", 14, "sha") 448 if err != nil { 449 t.Fatal(err) 450 } 451 comments := []*reviewdog.Comment{ 452 { 453 Result: &filter.FilteredDiagnostic{ 454 Diagnostic: &rdf.Diagnostic{ 455 Location: &rdf.Location{ 456 Path: "reviewdog.go", 457 Range: &rdf.Range{ 458 Start: &rdf.Position{ 459 Line: 2, 460 }, 461 }, 462 }, 463 Message: "already commented", 464 }, 465 InDiffContext: true, 466 }, 467 }, 468 { 469 Result: &filter.FilteredDiagnostic{ 470 Diagnostic: &rdf.Diagnostic{ 471 Location: &rdf.Location{ 472 Path: "reviewdog.go", 473 Range: &rdf.Range{ 474 Start: &rdf.Position{ 475 Line: 15, 476 }, 477 }, 478 }, 479 Message: "already commented 2", 480 }, 481 InDiffContext: true, 482 }, 483 }, 484 { 485 Result: &filter.FilteredDiagnostic{ 486 Diagnostic: &rdf.Diagnostic{ 487 Location: &rdf.Location{ 488 Path: "reviewdog.go", 489 Range: &rdf.Range{ 490 Start: &rdf.Position{ 491 Line: 15, 492 }, 493 }, 494 }, 495 Message: "new comment", 496 }, 497 InDiffContext: true, 498 }, 499 }, 500 { 501 Result: &filter.FilteredDiagnostic{ 502 Diagnostic: &rdf.Diagnostic{ 503 Location: &rdf.Location{ 504 Path: "reviewdog.go", 505 Range: &rdf.Range{ 506 Start: &rdf.Position{ 507 Line: 15, 508 }, 509 End: &rdf.Position{ 510 Line: 16, 511 }, 512 }, 513 }, 514 Message: "multiline existing comment", 515 }, 516 InDiffContext: true, 517 }, 518 }, 519 { 520 Result: &filter.FilteredDiagnostic{ 521 Diagnostic: &rdf.Diagnostic{ 522 Location: &rdf.Location{ 523 Path: "reviewdog.go", 524 Range: &rdf.Range{ 525 Start: &rdf.Position{ 526 Line: 15, 527 Column: 1, 528 }, 529 End: &rdf.Position{ 530 Line: 17, 531 Column: 1, 532 }, 533 }, 534 }, 535 Message: "multiline existing comment (line-break)", 536 }, 537 InDiffContext: true, 538 }, 539 }, 540 { 541 Result: &filter.FilteredDiagnostic{ 542 Diagnostic: &rdf.Diagnostic{ 543 Location: &rdf.Location{ 544 Path: "reviewdog.go", 545 Range: &rdf.Range{ 546 Start: &rdf.Position{ 547 Line: 15, 548 }, 549 End: &rdf.Position{ 550 Line: 16, 551 }, 552 }, 553 }, 554 Message: "multiline new comment", 555 }, 556 InDiffContext: true, 557 }, 558 }, 559 { 560 Result: &filter.FilteredDiagnostic{ 561 Diagnostic: &rdf.Diagnostic{ 562 Location: &rdf.Location{ 563 Path: "reviewdog.go", 564 // No Line 565 }, 566 Message: "should not be reported via GitHub Review API", 567 }, 568 }, 569 }, 570 { 571 Result: &filter.FilteredDiagnostic{ 572 Diagnostic: &rdf.Diagnostic{ 573 Location: &rdf.Location{ 574 Path: "reviewdog.go", 575 Range: &rdf.Range{ 576 Start: &rdf.Position{ 577 Line: 15, 578 }, 579 End: &rdf.Position{ 580 Line: 16, 581 }, 582 }, 583 }, 584 Suggestions: []*rdf.Suggestion{ 585 { 586 Range: &rdf.Range{ 587 Start: &rdf.Position{ 588 Line: 15, 589 }, 590 End: &rdf.Position{ 591 Line: 16, 592 }, 593 }, 594 Text: "line1\nline2\nline3", 595 }, 596 }, 597 Message: "multiline suggestion comment", 598 }, 599 InDiffContext: true, 600 }, 601 }, 602 { 603 Result: &filter.FilteredDiagnostic{ 604 Diagnostic: &rdf.Diagnostic{ 605 Location: &rdf.Location{ 606 Path: "reviewdog.go", 607 Range: &rdf.Range{ 608 Start: &rdf.Position{ 609 Line: 15, 610 }, 611 }, 612 }, 613 Suggestions: []*rdf.Suggestion{ 614 { 615 Range: &rdf.Range{ 616 Start: &rdf.Position{ 617 Line: 15, 618 }, 619 }, 620 Text: "line1\nline2", 621 }, 622 }, 623 Message: "singleline suggestion comment", 624 }, 625 InDiffContext: true, 626 }, 627 }, 628 { 629 Result: &filter.FilteredDiagnostic{ 630 Diagnostic: &rdf.Diagnostic{ 631 Location: &rdf.Location{ 632 Path: "reviewdog.go", 633 Range: &rdf.Range{ 634 Start: &rdf.Position{ 635 Line: 15, 636 }, 637 End: &rdf.Position{ 638 Line: 16, 639 }, 640 }, 641 }, 642 Suggestions: []*rdf.Suggestion{ 643 { 644 Range: &rdf.Range{ 645 Start: &rdf.Position{ 646 Line: 16, 647 }, 648 End: &rdf.Position{ 649 Line: 17, 650 }, 651 }, 652 Text: "line1\nline2\nline3", 653 }, 654 }, 655 Message: "invalid lines suggestion comment", 656 }, 657 InDiffContext: true, 658 FirstSuggestionInDiffContext: false, 659 }, 660 }, 661 { 662 Result: &filter.FilteredDiagnostic{ 663 SourceLines: map[int]string{ 664 14: "line 14 before", 665 15: "line 15 before", 666 16: "line 16 before", 667 }, 668 Diagnostic: &rdf.Diagnostic{ 669 Location: &rdf.Location{ 670 Path: "reviewdog.go", 671 Range: &rdf.Range{ 672 Start: &rdf.Position{ 673 Line: 15, 674 }, 675 }, 676 }, 677 Suggestions: []*rdf.Suggestion{ 678 { 679 Range: &rdf.Range{ 680 Start: &rdf.Position{ 681 Line: 14, 682 }, 683 End: &rdf.Position{ 684 Line: 16, 685 }, 686 }, 687 Text: "line1\nline2\nline3", 688 }, 689 }, 690 Message: "Use suggestion range as GitHub comment range if the suggestion is in diff context", 691 }, 692 InDiffContext: true, 693 FirstSuggestionInDiffContext: true, 694 }, 695 }, 696 { 697 Result: &filter.FilteredDiagnostic{ 698 SourceLines: map[int]string{ 699 14: "line 14 before", 700 15: "line 15 before", 701 16: "line 16 before", 702 }, 703 Diagnostic: &rdf.Diagnostic{ 704 Location: &rdf.Location{ 705 Path: "reviewdog.go", 706 Range: &rdf.Range{ 707 Start: &rdf.Position{ 708 Line: 15, 709 }, 710 }, 711 }, 712 Suggestions: []*rdf.Suggestion{ 713 { 714 Range: &rdf.Range{ 715 Start: &rdf.Position{ 716 Line: 14, 717 }, 718 End: &rdf.Position{ 719 Line: 16, 720 }, 721 }, 722 Text: "line1\nline2\nline3", 723 }, 724 { 725 Range: &rdf.Range{ 726 Start: &rdf.Position{ 727 Line: 14, 728 }, 729 End: &rdf.Position{ 730 Line: 14, 731 }, 732 }, 733 Text: "line1\nline2", 734 }, 735 }, 736 Message: "Partially invalid suggestions", 737 }, 738 InDiffContext: true, 739 FirstSuggestionInDiffContext: true, 740 }, 741 }, 742 { 743 Result: &filter.FilteredDiagnostic{ 744 Diagnostic: &rdf.Diagnostic{ 745 Location: &rdf.Location{ 746 Path: "reviewdog.go", 747 Range: &rdf.Range{ 748 Start: &rdf.Position{ 749 Line: 15, 750 }, 751 End: &rdf.Position{ 752 Line: 16, 753 }, 754 }, 755 }, 756 Suggestions: []*rdf.Suggestion{ 757 { 758 Range: &rdf.Range{ 759 Start: &rdf.Position{ 760 Line: 15, 761 Column: 5, 762 }, 763 End: &rdf.Position{ 764 Line: 16, 765 Column: 7, 766 }, 767 }, 768 Text: "replacement", 769 }, 770 }, 771 Message: "non-line based suggestion comment (no source lines)", 772 }, 773 InDiffContext: true, 774 }, 775 }, 776 { 777 Result: &filter.FilteredDiagnostic{ 778 SourceLines: map[int]string{15: "haya15busa"}, 779 Diagnostic: &rdf.Diagnostic{ 780 Location: &rdf.Location{ 781 Path: "reviewdog.go", 782 Range: &rdf.Range{ 783 Start: &rdf.Position{Line: 15, Column: 5}, 784 End: &rdf.Position{Line: 15, Column: 7}, 785 }, 786 }, 787 Suggestions: []*rdf.Suggestion{ 788 { 789 Range: &rdf.Range{ 790 Start: &rdf.Position{Line: 15, Column: 5}, 791 End: &rdf.Position{Line: 15, Column: 7}, 792 }, 793 Text: "14", 794 }, 795 }, 796 Message: "range suggestion (single line)", 797 }, 798 InDiffContext: true, 799 }, 800 }, 801 { 802 Result: &filter.FilteredDiagnostic{ 803 SourceLines: map[int]string{ 804 15: "haya???", 805 16: "???busa (multi-line)", 806 }, 807 Diagnostic: &rdf.Diagnostic{ 808 Location: &rdf.Location{ 809 Path: "reviewdog.go", 810 Range: &rdf.Range{ 811 Start: &rdf.Position{Line: 15, Column: 5}, 812 End: &rdf.Position{Line: 16, Column: 4}, 813 }, 814 }, 815 Suggestions: []*rdf.Suggestion{ 816 { 817 Range: &rdf.Range{ 818 Start: &rdf.Position{Line: 15, Column: 5}, 819 End: &rdf.Position{Line: 16, Column: 4}, 820 }, 821 Text: "14", 822 }, 823 }, 824 Message: "range suggestion (multi-line)", 825 }, 826 InDiffContext: true, 827 }, 828 }, 829 { 830 Result: &filter.FilteredDiagnostic{ 831 SourceLines: map[int]string{ 832 15: "line 15 xxx", 833 16: "line 16", 834 17: "(content at line 15)", 835 }, 836 Diagnostic: &rdf.Diagnostic{ 837 Location: &rdf.Location{ 838 Path: "reviewdog.go", 839 Range: &rdf.Range{ 840 Start: &rdf.Position{Line: 15, Column: 9}, 841 End: &rdf.Position{Line: 17, Column: 1}, 842 }, 843 }, 844 Suggestions: []*rdf.Suggestion{ 845 { 846 Range: &rdf.Range{ 847 Start: &rdf.Position{Line: 15, Column: 9}, 848 End: &rdf.Position{Line: 17, Column: 1}, 849 }, 850 Text: "", 851 }, 852 }, 853 Message: "range suggestion (line-break, remove)", 854 }, 855 InDiffContext: true, 856 }, 857 }, 858 { 859 Result: &filter.FilteredDiagnostic{ 860 SourceLines: map[int]string{ 861 15: "hayabusa", 862 }, 863 Diagnostic: &rdf.Diagnostic{ 864 Location: &rdf.Location{ 865 Path: "reviewdog.go", 866 Range: &rdf.Range{ 867 Start: &rdf.Position{Line: 15, Column: 5}, 868 End: &rdf.Position{Line: 15, Column: 5}, 869 }, 870 }, 871 Suggestions: []*rdf.Suggestion{ 872 { 873 Range: &rdf.Range{ 874 Start: &rdf.Position{Line: 15, Column: 5}, 875 End: &rdf.Position{Line: 15, Column: 5}, 876 }, 877 Text: "14", 878 }, 879 }, 880 Message: "range suggestion (insert)", 881 }, 882 InDiffContext: true, 883 }, 884 }, 885 { 886 Result: &filter.FilteredDiagnostic{ 887 SourceLines: map[int]string{15: "haya??busa"}, 888 Diagnostic: &rdf.Diagnostic{ 889 Location: &rdf.Location{ 890 Path: "reviewdog.go", 891 Range: &rdf.Range{ 892 Start: &rdf.Position{Line: 15, Column: 5}, 893 End: &rdf.Position{Line: 15, Column: 7}, 894 }, 895 }, 896 Suggestions: []*rdf.Suggestion{ 897 { 898 Range: &rdf.Range{ 899 Start: &rdf.Position{Line: 15, Column: 5}, 900 End: &rdf.Position{Line: 15, Column: 7}, 901 }, 902 Text: "1", 903 }, 904 { 905 Range: &rdf.Range{ 906 Start: &rdf.Position{Line: 15, Column: 5}, 907 End: &rdf.Position{Line: 15, Column: 7}, 908 }, 909 Text: "4", 910 }, 911 { 912 Range: &rdf.Range{ 913 Start: &rdf.Position{Line: 15, Column: 5}, 914 End: &rdf.Position{Line: 15, Column: 7}, 915 }, 916 Text: "14", 917 }, 918 }, 919 Message: "multiple suggestions", 920 }, 921 InDiffContext: true, 922 }, 923 }, 924 { 925 Result: &filter.FilteredDiagnostic{ 926 SourceLines: map[int]string{15: "haya15busa"}, 927 Diagnostic: &rdf.Diagnostic{ 928 Location: &rdf.Location{ 929 Path: "reviewdog.go", 930 Range: &rdf.Range{ 931 Start: &rdf.Position{Line: 15, Column: 5}, 932 }, 933 }, 934 Suggestions: []*rdf.Suggestion{ 935 { 936 Range: &rdf.Range{ 937 Start: &rdf.Position{Line: 15, Column: 5}, 938 End: &rdf.Position{Line: 15, Column: 7}, 939 }, 940 Text: "14", 941 }, 942 }, 943 Message: "range suggestion with start only location", 944 }, 945 InDiffContext: true, 946 }, 947 }, 948 } 949 for _, c := range comments { 950 if err := g.Post(context.Background(), c); err != nil { 951 t.Error(err) 952 } 953 } 954 if err := g.Flush(context.Background()); err != nil { 955 t.Error(err) 956 } 957 if listCommentsAPICalled != 2 { 958 t.Errorf("GitHub List PullRequest comments API called %v times, want 2 times", listCommentsAPICalled) 959 } 960 if postCommentsAPICalled != 1 { 961 t.Errorf("GitHub post PullRequest comments API called %v times, want 1 times", postCommentsAPICalled) 962 } 963 } 964 965 func TestGitHubPullRequest_Post_toomany(t *testing.T) { 966 cwd, _ := os.Getwd() 967 defer os.Chdir(cwd) 968 moveToRootDir() 969 defer setupEnvs()() 970 971 listCommentsAPICalled := 0 972 postCommentsAPICalled := 0 973 974 mux := http.NewServeMux() 975 mux.HandleFunc("/repos/o/r/pulls/14/comments", func(w http.ResponseWriter, r *http.Request) { 976 listCommentsAPICalled++ 977 if err := json.NewEncoder(w).Encode([]*github.PullRequestComment{}); err != nil { 978 t.Fatal(err) 979 } 980 }) 981 mux.HandleFunc("/repos/o/r/pulls/14/reviews", func(w http.ResponseWriter, r *http.Request) { 982 postCommentsAPICalled++ 983 var req github.PullRequestReviewRequest 984 if err := json.NewDecoder(r.Body).Decode(&req); err != nil { 985 t.Error(err) 986 } 987 if req.GetBody() == "" { 988 t.Errorf("PullRequestReviewRequest.Body is empty but want some summary text") 989 } 990 }) 991 ts := httptest.NewServer(mux) 992 defer ts.Close() 993 994 cli := github.NewClient(nil) 995 cli.BaseURL, _ = url.Parse(ts.URL + "/") 996 g, err := NewGitHubPullRequest(cli, "o", "r", 14, "sha") 997 if err != nil { 998 t.Fatal(err) 999 } 1000 var comments []*reviewdog.Comment 1001 for i := 0; i < 100; i++ { 1002 comments = append(comments, &reviewdog.Comment{ 1003 Result: &filter.FilteredDiagnostic{ 1004 Diagnostic: &rdf.Diagnostic{ 1005 Location: &rdf.Location{ 1006 Path: "reviewdog.go", 1007 Range: &rdf.Range{Start: &rdf.Position{ 1008 Line: int32(i), 1009 }}, 1010 }, 1011 Message: "comment", 1012 }, 1013 InDiffContext: true, 1014 }, 1015 ToolName: "tool", 1016 }) 1017 } 1018 for _, c := range comments { 1019 if err := g.Post(context.Background(), c); err != nil { 1020 t.Error(err) 1021 } 1022 } 1023 if err := g.Flush(context.Background()); err != nil { 1024 t.Error(err) 1025 } 1026 if want := 1; listCommentsAPICalled != want { 1027 t.Errorf("GitHub List PullRequest comments API called %v times, want %d times", listCommentsAPICalled, want) 1028 } 1029 if want := 1; postCommentsAPICalled != want { 1030 t.Errorf("GitHub post PullRequest comments API called %v times, want %d times", postCommentsAPICalled, want) 1031 } 1032 } 1033 1034 func TestGitHubPullRequest_workdir(t *testing.T) { 1035 cwd, _ := os.Getwd() 1036 defer os.Chdir(cwd) 1037 moveToRootDir() 1038 defer setupEnvs()() 1039 1040 g, err := NewGitHubPullRequest(nil, "", "", 0, "") 1041 if err != nil { 1042 t.Fatal(err) 1043 } 1044 if g.wd != "" { 1045 t.Fatalf("g.wd = %q, want empty", g.wd) 1046 } 1047 ctx := context.Background() 1048 want := "a/b/c" 1049 g.Post(ctx, &reviewdog.Comment{Result: &filter.FilteredDiagnostic{ 1050 Diagnostic: &rdf.Diagnostic{Location: &rdf.Location{Path: want}}}}) 1051 if got := g.postComments[0].Result.Diagnostic.GetLocation().GetPath(); got != want { 1052 t.Errorf("wd=%q path=%q, want %q", g.wd, got, want) 1053 } 1054 1055 subDir := "cmd/" 1056 if err := os.Chdir(subDir); err != nil { 1057 t.Fatal(err) 1058 } 1059 g, _ = NewGitHubPullRequest(nil, "", "", 0, "") 1060 if g.wd != subDir { 1061 t.Fatalf("gitRelWorkdir() = %q, want %q", g.wd, subDir) 1062 } 1063 path := "a/b/c" 1064 wantPath := "cmd/" + path 1065 g.Post(ctx, &reviewdog.Comment{Result: &filter.FilteredDiagnostic{ 1066 Diagnostic: &rdf.Diagnostic{Location: &rdf.Location{Path: want}}}}) 1067 if got := g.postComments[0].Result.Diagnostic.GetLocation().GetPath(); got != wantPath { 1068 t.Errorf("wd=%q path=%q, want %q", g.wd, got, wantPath) 1069 } 1070 } 1071 1072 func TestGitHubPullRequest_Diff_fake(t *testing.T) { 1073 apiCalled := 0 1074 mux := http.NewServeMux() 1075 mux.HandleFunc("/repos/o/r/pulls/14", func(w http.ResponseWriter, r *http.Request) { 1076 apiCalled++ 1077 if r.Method != http.MethodGet { 1078 t.Errorf("unexpected access: %v %v", r.Method, r.URL) 1079 } 1080 if accept := r.Header.Get("Accept"); !strings.Contains(accept, "diff") { 1081 t.Errorf("Accept header doesn't contain 'diff': %v", accept) 1082 } 1083 w.Write([]byte("Pull Request diff")) 1084 }) 1085 ts := httptest.NewServer(mux) 1086 defer ts.Close() 1087 1088 cli := github.NewClient(nil) 1089 cli.BaseURL, _ = url.Parse(ts.URL + "/") 1090 g, err := NewGitHubPullRequest(cli, "o", "r", 14, "sha") 1091 if err != nil { 1092 t.Fatal(err) 1093 } 1094 if _, err := g.Diff(context.Background()); err != nil { 1095 t.Fatal(err) 1096 } 1097 if apiCalled != 1 { 1098 t.Errorf("GitHub API should be called once; called %v times", apiCalled) 1099 } 1100 }