github.com/vipcoin-gold/reviewdog@v1.0.2/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/v49/github" 14 "github.com/kylelemons/godebug/pretty" 15 "golang.org/x/oauth2" 16 17 "github.com/vipcoin-gold/reviewdog" 18 "github.com/vipcoin-gold/reviewdog/filter" 19 "github.com/vipcoin-gold/reviewdog/proto/rdf" 20 "github.com/vipcoin-gold/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/reviewdog/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/reviewdog/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/reviewdog/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/reviewdog/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/reviewdog/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 Path: github.String("reviewdog.go"), 439 Side: github.String("RIGHT"), 440 StartSide: github.String("RIGHT"), 441 StartLine: github.Int(15), 442 Line: github.Int(16), 443 Body: github.String(commentutil.BodyPrefix + strings.Join([]string{ 444 "multiline suggestion comment including a code fence block", 445 "````suggestion", 446 "```", 447 "some code", 448 "```", 449 "````", 450 }, "\n") + "\n"), 451 }, 452 { 453 Path: github.String("reviewdog.go"), 454 Side: github.String("RIGHT"), 455 Line: github.Int(15), 456 Body: github.String(commentutil.BodyPrefix + strings.Join([]string{ 457 "singleline suggestion comment including a code fence block", 458 "````suggestion", 459 "```", 460 "some code", 461 "```", 462 "````", 463 }, "\n") + "\n"), 464 }, 465 { 466 Path: github.String("reviewdog.go"), 467 Side: github.String("RIGHT"), 468 StartSide: github.String("RIGHT"), 469 StartLine: github.Int(15), 470 Line: github.Int(16), 471 Body: github.String(commentutil.BodyPrefix + strings.Join([]string{ 472 "multiline suggestion comment including an empty code fence block", 473 "``````suggestion", 474 "```", 475 "`````", 476 "``````", 477 }, "\n") + "\n"), 478 }, 479 } 480 if diff := pretty.Compare(want, req.Comments); diff != "" { 481 t.Errorf("req.Comments diff: (-got +want)\n%s", diff) 482 } 483 }) 484 ts := httptest.NewServer(mux) 485 defer ts.Close() 486 487 cli := github.NewClient(nil) 488 cli.BaseURL, _ = url.Parse(ts.URL + "/") 489 g, err := NewGitHubPullRequest(cli, "o", "r", 14, "sha") 490 if err != nil { 491 t.Fatal(err) 492 } 493 comments := []*reviewdog.Comment{ 494 { 495 Result: &filter.FilteredDiagnostic{ 496 Diagnostic: &rdf.Diagnostic{ 497 Location: &rdf.Location{ 498 Path: "reviewdog.go", 499 Range: &rdf.Range{ 500 Start: &rdf.Position{ 501 Line: 2, 502 }, 503 }, 504 }, 505 Message: "already commented", 506 }, 507 InDiffContext: true, 508 }, 509 }, 510 { 511 Result: &filter.FilteredDiagnostic{ 512 Diagnostic: &rdf.Diagnostic{ 513 Location: &rdf.Location{ 514 Path: "reviewdog.go", 515 Range: &rdf.Range{ 516 Start: &rdf.Position{ 517 Line: 15, 518 }, 519 }, 520 }, 521 Message: "already commented 2", 522 }, 523 InDiffContext: true, 524 }, 525 }, 526 { 527 Result: &filter.FilteredDiagnostic{ 528 Diagnostic: &rdf.Diagnostic{ 529 Location: &rdf.Location{ 530 Path: "reviewdog.go", 531 Range: &rdf.Range{ 532 Start: &rdf.Position{ 533 Line: 15, 534 }, 535 }, 536 }, 537 Message: "new comment", 538 }, 539 InDiffContext: true, 540 }, 541 }, 542 { 543 Result: &filter.FilteredDiagnostic{ 544 Diagnostic: &rdf.Diagnostic{ 545 Location: &rdf.Location{ 546 Path: "reviewdog.go", 547 Range: &rdf.Range{ 548 Start: &rdf.Position{ 549 Line: 15, 550 }, 551 End: &rdf.Position{ 552 Line: 16, 553 }, 554 }, 555 }, 556 Message: "multiline existing comment", 557 }, 558 InDiffContext: true, 559 }, 560 }, 561 { 562 Result: &filter.FilteredDiagnostic{ 563 Diagnostic: &rdf.Diagnostic{ 564 Location: &rdf.Location{ 565 Path: "reviewdog.go", 566 Range: &rdf.Range{ 567 Start: &rdf.Position{ 568 Line: 15, 569 Column: 1, 570 }, 571 End: &rdf.Position{ 572 Line: 17, 573 Column: 1, 574 }, 575 }, 576 }, 577 Message: "multiline existing comment (line-break)", 578 }, 579 InDiffContext: true, 580 }, 581 }, 582 { 583 Result: &filter.FilteredDiagnostic{ 584 Diagnostic: &rdf.Diagnostic{ 585 Location: &rdf.Location{ 586 Path: "reviewdog.go", 587 Range: &rdf.Range{ 588 Start: &rdf.Position{ 589 Line: 15, 590 }, 591 End: &rdf.Position{ 592 Line: 16, 593 }, 594 }, 595 }, 596 Message: "multiline new comment", 597 }, 598 InDiffContext: true, 599 }, 600 }, 601 { 602 Result: &filter.FilteredDiagnostic{ 603 Diagnostic: &rdf.Diagnostic{ 604 Location: &rdf.Location{ 605 Path: "reviewdog.go", 606 // No Line 607 }, 608 Message: "should not be reported via GitHub Review API", 609 }, 610 }, 611 }, 612 { 613 Result: &filter.FilteredDiagnostic{ 614 Diagnostic: &rdf.Diagnostic{ 615 Location: &rdf.Location{ 616 Path: "reviewdog.go", 617 Range: &rdf.Range{ 618 Start: &rdf.Position{ 619 Line: 15, 620 }, 621 End: &rdf.Position{ 622 Line: 16, 623 }, 624 }, 625 }, 626 Suggestions: []*rdf.Suggestion{ 627 { 628 Range: &rdf.Range{ 629 Start: &rdf.Position{ 630 Line: 15, 631 }, 632 End: &rdf.Position{ 633 Line: 16, 634 }, 635 }, 636 Text: "line1\nline2\nline3", 637 }, 638 }, 639 Message: "multiline suggestion comment", 640 }, 641 InDiffContext: true, 642 }, 643 }, 644 { 645 Result: &filter.FilteredDiagnostic{ 646 Diagnostic: &rdf.Diagnostic{ 647 Location: &rdf.Location{ 648 Path: "reviewdog.go", 649 Range: &rdf.Range{ 650 Start: &rdf.Position{ 651 Line: 15, 652 }, 653 }, 654 }, 655 Suggestions: []*rdf.Suggestion{ 656 { 657 Range: &rdf.Range{ 658 Start: &rdf.Position{ 659 Line: 15, 660 }, 661 }, 662 Text: "line1\nline2", 663 }, 664 }, 665 Message: "singleline suggestion comment", 666 }, 667 InDiffContext: true, 668 }, 669 }, 670 { 671 Result: &filter.FilteredDiagnostic{ 672 Diagnostic: &rdf.Diagnostic{ 673 Location: &rdf.Location{ 674 Path: "reviewdog.go", 675 Range: &rdf.Range{ 676 Start: &rdf.Position{ 677 Line: 15, 678 }, 679 End: &rdf.Position{ 680 Line: 16, 681 }, 682 }, 683 }, 684 Suggestions: []*rdf.Suggestion{ 685 { 686 Range: &rdf.Range{ 687 Start: &rdf.Position{ 688 Line: 16, 689 }, 690 End: &rdf.Position{ 691 Line: 17, 692 }, 693 }, 694 Text: "line1\nline2\nline3", 695 }, 696 }, 697 Message: "invalid lines suggestion comment", 698 }, 699 InDiffContext: true, 700 FirstSuggestionInDiffContext: false, 701 }, 702 }, 703 { 704 Result: &filter.FilteredDiagnostic{ 705 SourceLines: map[int]string{ 706 14: "line 14 before", 707 15: "line 15 before", 708 16: "line 16 before", 709 }, 710 Diagnostic: &rdf.Diagnostic{ 711 Location: &rdf.Location{ 712 Path: "reviewdog.go", 713 Range: &rdf.Range{ 714 Start: &rdf.Position{ 715 Line: 15, 716 }, 717 }, 718 }, 719 Suggestions: []*rdf.Suggestion{ 720 { 721 Range: &rdf.Range{ 722 Start: &rdf.Position{ 723 Line: 14, 724 }, 725 End: &rdf.Position{ 726 Line: 16, 727 }, 728 }, 729 Text: "line1\nline2\nline3", 730 }, 731 }, 732 Message: "Use suggestion range as GitHub comment range if the suggestion is in diff context", 733 }, 734 InDiffContext: true, 735 FirstSuggestionInDiffContext: true, 736 }, 737 }, 738 { 739 Result: &filter.FilteredDiagnostic{ 740 SourceLines: map[int]string{ 741 14: "line 14 before", 742 15: "line 15 before", 743 16: "line 16 before", 744 }, 745 Diagnostic: &rdf.Diagnostic{ 746 Location: &rdf.Location{ 747 Path: "reviewdog.go", 748 Range: &rdf.Range{ 749 Start: &rdf.Position{ 750 Line: 15, 751 }, 752 }, 753 }, 754 Suggestions: []*rdf.Suggestion{ 755 { 756 Range: &rdf.Range{ 757 Start: &rdf.Position{ 758 Line: 14, 759 }, 760 End: &rdf.Position{ 761 Line: 16, 762 }, 763 }, 764 Text: "line1\nline2\nline3", 765 }, 766 { 767 Range: &rdf.Range{ 768 Start: &rdf.Position{ 769 Line: 14, 770 }, 771 End: &rdf.Position{ 772 Line: 14, 773 }, 774 }, 775 Text: "line1\nline2", 776 }, 777 }, 778 Message: "Partially invalid suggestions", 779 }, 780 InDiffContext: true, 781 FirstSuggestionInDiffContext: true, 782 }, 783 }, 784 { 785 Result: &filter.FilteredDiagnostic{ 786 Diagnostic: &rdf.Diagnostic{ 787 Location: &rdf.Location{ 788 Path: "reviewdog.go", 789 Range: &rdf.Range{ 790 Start: &rdf.Position{ 791 Line: 15, 792 }, 793 End: &rdf.Position{ 794 Line: 16, 795 }, 796 }, 797 }, 798 Suggestions: []*rdf.Suggestion{ 799 { 800 Range: &rdf.Range{ 801 Start: &rdf.Position{ 802 Line: 15, 803 Column: 5, 804 }, 805 End: &rdf.Position{ 806 Line: 16, 807 Column: 7, 808 }, 809 }, 810 Text: "replacement", 811 }, 812 }, 813 Message: "non-line based suggestion comment (no source lines)", 814 }, 815 InDiffContext: true, 816 }, 817 }, 818 { 819 Result: &filter.FilteredDiagnostic{ 820 SourceLines: map[int]string{15: "haya15busa"}, 821 Diagnostic: &rdf.Diagnostic{ 822 Location: &rdf.Location{ 823 Path: "reviewdog.go", 824 Range: &rdf.Range{ 825 Start: &rdf.Position{Line: 15, Column: 5}, 826 End: &rdf.Position{Line: 15, Column: 7}, 827 }, 828 }, 829 Suggestions: []*rdf.Suggestion{ 830 { 831 Range: &rdf.Range{ 832 Start: &rdf.Position{Line: 15, Column: 5}, 833 End: &rdf.Position{Line: 15, Column: 7}, 834 }, 835 Text: "14", 836 }, 837 }, 838 Message: "range suggestion (single line)", 839 }, 840 InDiffContext: true, 841 }, 842 }, 843 { 844 Result: &filter.FilteredDiagnostic{ 845 SourceLines: map[int]string{ 846 15: "haya???", 847 16: "???busa (multi-line)", 848 }, 849 Diagnostic: &rdf.Diagnostic{ 850 Location: &rdf.Location{ 851 Path: "reviewdog.go", 852 Range: &rdf.Range{ 853 Start: &rdf.Position{Line: 15, Column: 5}, 854 End: &rdf.Position{Line: 16, Column: 4}, 855 }, 856 }, 857 Suggestions: []*rdf.Suggestion{ 858 { 859 Range: &rdf.Range{ 860 Start: &rdf.Position{Line: 15, Column: 5}, 861 End: &rdf.Position{Line: 16, Column: 4}, 862 }, 863 Text: "14", 864 }, 865 }, 866 Message: "range suggestion (multi-line)", 867 }, 868 InDiffContext: true, 869 }, 870 }, 871 { 872 Result: &filter.FilteredDiagnostic{ 873 SourceLines: map[int]string{ 874 15: "line 15 xxx", 875 16: "line 16", 876 17: "(content at line 15)", 877 }, 878 Diagnostic: &rdf.Diagnostic{ 879 Location: &rdf.Location{ 880 Path: "reviewdog.go", 881 Range: &rdf.Range{ 882 Start: &rdf.Position{Line: 15, Column: 9}, 883 End: &rdf.Position{Line: 17, Column: 1}, 884 }, 885 }, 886 Suggestions: []*rdf.Suggestion{ 887 { 888 Range: &rdf.Range{ 889 Start: &rdf.Position{Line: 15, Column: 9}, 890 End: &rdf.Position{Line: 17, Column: 1}, 891 }, 892 Text: "", 893 }, 894 }, 895 Message: "range suggestion (line-break, remove)", 896 }, 897 InDiffContext: true, 898 }, 899 }, 900 { 901 Result: &filter.FilteredDiagnostic{ 902 SourceLines: map[int]string{ 903 15: "hayabusa", 904 }, 905 Diagnostic: &rdf.Diagnostic{ 906 Location: &rdf.Location{ 907 Path: "reviewdog.go", 908 Range: &rdf.Range{ 909 Start: &rdf.Position{Line: 15, Column: 5}, 910 End: &rdf.Position{Line: 15, Column: 5}, 911 }, 912 }, 913 Suggestions: []*rdf.Suggestion{ 914 { 915 Range: &rdf.Range{ 916 Start: &rdf.Position{Line: 15, Column: 5}, 917 End: &rdf.Position{Line: 15, Column: 5}, 918 }, 919 Text: "14", 920 }, 921 }, 922 Message: "range suggestion (insert)", 923 }, 924 InDiffContext: true, 925 }, 926 }, 927 { 928 Result: &filter.FilteredDiagnostic{ 929 SourceLines: map[int]string{15: "haya??busa"}, 930 Diagnostic: &rdf.Diagnostic{ 931 Location: &rdf.Location{ 932 Path: "reviewdog.go", 933 Range: &rdf.Range{ 934 Start: &rdf.Position{Line: 15, Column: 5}, 935 End: &rdf.Position{Line: 15, Column: 7}, 936 }, 937 }, 938 Suggestions: []*rdf.Suggestion{ 939 { 940 Range: &rdf.Range{ 941 Start: &rdf.Position{Line: 15, Column: 5}, 942 End: &rdf.Position{Line: 15, Column: 7}, 943 }, 944 Text: "1", 945 }, 946 { 947 Range: &rdf.Range{ 948 Start: &rdf.Position{Line: 15, Column: 5}, 949 End: &rdf.Position{Line: 15, Column: 7}, 950 }, 951 Text: "4", 952 }, 953 { 954 Range: &rdf.Range{ 955 Start: &rdf.Position{Line: 15, Column: 5}, 956 End: &rdf.Position{Line: 15, Column: 7}, 957 }, 958 Text: "14", 959 }, 960 }, 961 Message: "multiple suggestions", 962 }, 963 InDiffContext: true, 964 }, 965 }, 966 { 967 Result: &filter.FilteredDiagnostic{ 968 SourceLines: map[int]string{15: "haya15busa"}, 969 Diagnostic: &rdf.Diagnostic{ 970 Location: &rdf.Location{ 971 Path: "reviewdog.go", 972 Range: &rdf.Range{ 973 Start: &rdf.Position{Line: 15, Column: 5}, 974 }, 975 }, 976 Suggestions: []*rdf.Suggestion{ 977 { 978 Range: &rdf.Range{ 979 Start: &rdf.Position{Line: 15, Column: 5}, 980 End: &rdf.Position{Line: 15, Column: 7}, 981 }, 982 Text: "14", 983 }, 984 }, 985 Message: "range suggestion with start only location", 986 }, 987 InDiffContext: true, 988 }, 989 }, 990 { 991 Result: &filter.FilteredDiagnostic{ 992 Diagnostic: &rdf.Diagnostic{ 993 Location: &rdf.Location{ 994 Path: "reviewdog.go", 995 Range: &rdf.Range{ 996 Start: &rdf.Position{ 997 Line: 15, 998 }, 999 End: &rdf.Position{ 1000 Line: 16, 1001 }, 1002 }, 1003 }, 1004 Suggestions: []*rdf.Suggestion{ 1005 { 1006 Range: &rdf.Range{ 1007 Start: &rdf.Position{ 1008 Line: 15, 1009 }, 1010 End: &rdf.Position{ 1011 Line: 16, 1012 }, 1013 }, 1014 Text: "```\nsome code\n```", 1015 }, 1016 }, 1017 Message: "multiline suggestion comment including a code fence block", 1018 }, 1019 InDiffContext: true, 1020 }, 1021 }, 1022 { 1023 Result: &filter.FilteredDiagnostic{ 1024 Diagnostic: &rdf.Diagnostic{ 1025 Location: &rdf.Location{ 1026 Path: "reviewdog.go", 1027 Range: &rdf.Range{ 1028 Start: &rdf.Position{ 1029 Line: 15, 1030 }, 1031 }, 1032 }, 1033 Suggestions: []*rdf.Suggestion{ 1034 { 1035 Range: &rdf.Range{ 1036 Start: &rdf.Position{ 1037 Line: 15, 1038 }, 1039 }, 1040 Text: "```\nsome code\n```", 1041 }, 1042 }, 1043 Message: "singleline suggestion comment including a code fence block", 1044 }, 1045 InDiffContext: true, 1046 }, 1047 }, 1048 { 1049 Result: &filter.FilteredDiagnostic{ 1050 Diagnostic: &rdf.Diagnostic{ 1051 Location: &rdf.Location{ 1052 Path: "reviewdog.go", 1053 Range: &rdf.Range{ 1054 Start: &rdf.Position{ 1055 Line: 15, 1056 }, 1057 End: &rdf.Position{ 1058 Line: 16, 1059 }, 1060 }, 1061 }, 1062 Suggestions: []*rdf.Suggestion{ 1063 { 1064 Range: &rdf.Range{ 1065 Start: &rdf.Position{ 1066 Line: 15, 1067 }, 1068 End: &rdf.Position{ 1069 Line: 16, 1070 }, 1071 }, 1072 Text: "```\n`````", 1073 }, 1074 }, 1075 Message: "multiline suggestion comment including an empty code fence block", 1076 }, 1077 InDiffContext: true, 1078 }, 1079 }, 1080 } 1081 for _, c := range comments { 1082 if err := g.Post(context.Background(), c); err != nil { 1083 t.Error(err) 1084 } 1085 } 1086 if err := g.Flush(context.Background()); err != nil { 1087 t.Error(err) 1088 } 1089 if listCommentsAPICalled != 2 { 1090 t.Errorf("GitHub List PullRequest comments API called %v times, want 2 times", listCommentsAPICalled) 1091 } 1092 if postCommentsAPICalled != 1 { 1093 t.Errorf("GitHub post PullRequest comments API called %v times, want 1 times", postCommentsAPICalled) 1094 } 1095 } 1096 1097 func TestGitHubPullRequest_Post_toomany(t *testing.T) { 1098 cwd, _ := os.Getwd() 1099 defer os.Chdir(cwd) 1100 moveToRootDir() 1101 defer setupEnvs()() 1102 1103 listCommentsAPICalled := 0 1104 postCommentsAPICalled := 0 1105 1106 mux := http.NewServeMux() 1107 mux.HandleFunc("/repos/o/r/pulls/14/comments", func(w http.ResponseWriter, r *http.Request) { 1108 listCommentsAPICalled++ 1109 if err := json.NewEncoder(w).Encode([]*github.PullRequestComment{}); err != nil { 1110 t.Fatal(err) 1111 } 1112 }) 1113 mux.HandleFunc("/repos/o/r/pulls/14/reviews", func(w http.ResponseWriter, r *http.Request) { 1114 postCommentsAPICalled++ 1115 var req github.PullRequestReviewRequest 1116 if err := json.NewDecoder(r.Body).Decode(&req); err != nil { 1117 t.Error(err) 1118 } 1119 if req.GetBody() == "" { 1120 t.Errorf("PullRequestReviewRequest.Body is empty but want some summary text") 1121 } 1122 }) 1123 ts := httptest.NewServer(mux) 1124 defer ts.Close() 1125 1126 cli := github.NewClient(nil) 1127 cli.BaseURL, _ = url.Parse(ts.URL + "/") 1128 g, err := NewGitHubPullRequest(cli, "o", "r", 14, "sha") 1129 if err != nil { 1130 t.Fatal(err) 1131 } 1132 var comments []*reviewdog.Comment 1133 for i := 0; i < 100; i++ { 1134 comments = append(comments, &reviewdog.Comment{ 1135 Result: &filter.FilteredDiagnostic{ 1136 Diagnostic: &rdf.Diagnostic{ 1137 Location: &rdf.Location{ 1138 Path: "reviewdog.go", 1139 Range: &rdf.Range{Start: &rdf.Position{ 1140 Line: int32(i), 1141 }}, 1142 }, 1143 Message: "comment", 1144 }, 1145 InDiffContext: true, 1146 }, 1147 ToolName: "tool", 1148 }) 1149 } 1150 for _, c := range comments { 1151 if err := g.Post(context.Background(), c); err != nil { 1152 t.Error(err) 1153 } 1154 } 1155 if err := g.Flush(context.Background()); err != nil { 1156 t.Error(err) 1157 } 1158 if want := 1; listCommentsAPICalled != want { 1159 t.Errorf("GitHub List PullRequest comments API called %v times, want %d times", listCommentsAPICalled, want) 1160 } 1161 if want := 1; postCommentsAPICalled != want { 1162 t.Errorf("GitHub post PullRequest comments API called %v times, want %d times", postCommentsAPICalled, want) 1163 } 1164 } 1165 1166 func TestGitHubPullRequest_workdir(t *testing.T) { 1167 cwd, _ := os.Getwd() 1168 defer os.Chdir(cwd) 1169 moveToRootDir() 1170 defer setupEnvs()() 1171 1172 g, err := NewGitHubPullRequest(nil, "", "", 0, "") 1173 if err != nil { 1174 t.Fatal(err) 1175 } 1176 if g.wd != "" { 1177 t.Fatalf("g.wd = %q, want empty", g.wd) 1178 } 1179 ctx := context.Background() 1180 want := "a/b/c" 1181 g.Post(ctx, &reviewdog.Comment{Result: &filter.FilteredDiagnostic{ 1182 Diagnostic: &rdf.Diagnostic{Location: &rdf.Location{Path: want}}}}) 1183 if got := g.postComments[0].Result.Diagnostic.GetLocation().GetPath(); got != want { 1184 t.Errorf("wd=%q path=%q, want %q", g.wd, got, want) 1185 } 1186 1187 subDir := "cmd/" 1188 if err := os.Chdir(subDir); err != nil { 1189 t.Fatal(err) 1190 } 1191 g, _ = NewGitHubPullRequest(nil, "", "", 0, "") 1192 if g.wd != subDir { 1193 t.Fatalf("gitRelWorkdir() = %q, want %q", g.wd, subDir) 1194 } 1195 path := "a/b/c" 1196 wantPath := "cmd/" + path 1197 g.Post(ctx, &reviewdog.Comment{Result: &filter.FilteredDiagnostic{ 1198 Diagnostic: &rdf.Diagnostic{Location: &rdf.Location{Path: want}}}}) 1199 if got := g.postComments[0].Result.Diagnostic.GetLocation().GetPath(); got != wantPath { 1200 t.Errorf("wd=%q path=%q, want %q", g.wd, got, wantPath) 1201 } 1202 } 1203 1204 func TestGitHubPullRequest_Diff_fake(t *testing.T) { 1205 apiCalled := 0 1206 mux := http.NewServeMux() 1207 mux.HandleFunc("/repos/o/r/pulls/14", func(w http.ResponseWriter, r *http.Request) { 1208 apiCalled++ 1209 if r.Method != http.MethodGet { 1210 t.Errorf("unexpected access: %v %v", r.Method, r.URL) 1211 } 1212 if accept := r.Header.Get("Accept"); !strings.Contains(accept, "diff") { 1213 t.Errorf("Accept header doesn't contain 'diff': %v", accept) 1214 } 1215 w.Write([]byte("Pull Request diff")) 1216 }) 1217 ts := httptest.NewServer(mux) 1218 defer ts.Close() 1219 1220 cli := github.NewClient(nil) 1221 cli.BaseURL, _ = url.Parse(ts.URL + "/") 1222 g, err := NewGitHubPullRequest(cli, "o", "r", 14, "sha") 1223 if err != nil { 1224 t.Fatal(err) 1225 } 1226 if _, err := g.Diff(context.Background()); err != nil { 1227 t.Fatal(err) 1228 } 1229 if apiCalled != 1 { 1230 t.Errorf("GitHub API should be called once; called %v times", apiCalled) 1231 } 1232 }