github.com/mistwind/reviewdog@v0.0.0-20230322024206-9cfa11856d58/cmd/reviewdog/doghouse_test.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/google/go-cmp/cmp"
    13  	"golang.org/x/oauth2"
    14  	"google.golang.org/protobuf/testing/protocmp"
    15  
    16  	"github.com/mistwind/reviewdog"
    17  	"github.com/mistwind/reviewdog/cienv"
    18  	"github.com/mistwind/reviewdog/doghouse"
    19  	"github.com/mistwind/reviewdog/doghouse/client"
    20  	"github.com/mistwind/reviewdog/filter"
    21  	"github.com/mistwind/reviewdog/project"
    22  	"github.com/mistwind/reviewdog/proto/rdf"
    23  )
    24  
    25  func setupEnvs(testEnvs map[string]string) (cleanup func()) {
    26  	saveEnvs := make(map[string]string)
    27  	for key, value := range testEnvs {
    28  		saveEnvs[key] = os.Getenv(key)
    29  		if value == "" {
    30  			os.Unsetenv(key)
    31  		} else {
    32  			os.Setenv(key, value)
    33  		}
    34  	}
    35  	return func() {
    36  		for key, value := range saveEnvs {
    37  			os.Setenv(key, value)
    38  		}
    39  	}
    40  }
    41  
    42  func TestNewDoghouseCli_returnGitHubClient(t *testing.T) {
    43  	cleanup := setupEnvs(map[string]string{
    44  		"REVIEWDOG_TOKEN":            "",
    45  		"GITHUB_ACTIONS":             "xxx",
    46  		"REVIEWDOG_GITHUB_API_TOKEN": "xxx",
    47  	})
    48  	defer cleanup()
    49  	cli, err := newDoghouseCli(context.Background())
    50  	if err != nil {
    51  		t.Fatalf("failed to create new client: %v", err)
    52  	}
    53  	if _, ok := cli.(*client.GitHubClient); !ok {
    54  		t.Errorf("got %T client, want *client.GitHubClient client", cli)
    55  	}
    56  }
    57  
    58  func TestNewDoghouseCli_returnErrorForGitHubClient(t *testing.T) {
    59  	cleanup := setupEnvs(map[string]string{
    60  		"REVIEWDOG_TOKEN":            "",
    61  		"GITHUB_ACTIONS":             "true",
    62  		"REVIEWDOG_GITHUB_API_TOKEN": "", // missing
    63  	})
    64  	defer cleanup()
    65  	if _, err := newDoghouseCli(context.Background()); err == nil {
    66  		t.Error("got no error but want REVIEWDOG_GITHUB_API_TOKEN missing error")
    67  	}
    68  }
    69  
    70  func TestNewDoghouseCli_returnDogHouseClientWithReviewdogToken(t *testing.T) {
    71  	cleanup := setupEnvs(map[string]string{
    72  		"REVIEWDOG_TOKEN":            "xxx",
    73  		"GITHUB_ACTIONS":             "true",
    74  		"REVIEWDOG_GITHUB_API_TOKEN": "xxx",
    75  	})
    76  	defer cleanup()
    77  	cli, err := newDoghouseCli(context.Background())
    78  	if err != nil {
    79  		t.Fatalf("failed to create new client: %v", err)
    80  	}
    81  	if _, ok := cli.(*client.DogHouseClient); !ok {
    82  		t.Errorf("got %T client, want *client.DogHouseClient client", cli)
    83  	}
    84  }
    85  
    86  func TestNewDoghouseCli_returnDogHouseClient(t *testing.T) {
    87  	cleanup := setupEnvs(map[string]string{
    88  		"REVIEWDOG_TOKEN":            "",
    89  		"GITHUB_ACTIONS":             "",
    90  		"REVIEWDOG_GITHUB_API_TOKEN": "",
    91  	})
    92  	defer cleanup()
    93  	cli, err := newDoghouseCli(context.Background())
    94  	if err != nil {
    95  		t.Fatalf("failed to create new client: %v", err)
    96  	}
    97  	if _, ok := cli.(*client.DogHouseClient); !ok {
    98  		t.Errorf("got %T client, want *client.DogHouseClient client", cli)
    99  	}
   100  }
   101  
   102  func TestNewDoghouseServerCli(t *testing.T) {
   103  	if _, ok := newDoghouseServerCli(context.Background()).Client.Transport.(*oauth2.Transport); ok {
   104  		t.Error("got oauth2 http client, want default client")
   105  	}
   106  
   107  	cleanup := setupEnvs(map[string]string{
   108  		"REVIEWDOG_TOKEN": "xxx",
   109  	})
   110  	defer cleanup()
   111  
   112  	if _, ok := newDoghouseServerCli(context.Background()).Client.Transport.(*oauth2.Transport); !ok {
   113  		t.Error("w/ TOKEN: got unexpected http client, want oauth client")
   114  	}
   115  }
   116  
   117  func TestDiagnosticResultSet_Project(t *testing.T) {
   118  	defer func(f func(ctx context.Context, conf *project.Config, runners map[string]bool, level string, tee bool) (*reviewdog.ResultMap, error)) {
   119  		projectRunAndParse = f
   120  	}(projectRunAndParse)
   121  
   122  	var wantDiagnosticResult reviewdog.ResultMap
   123  	wantDiagnosticResult.Store("name1", &reviewdog.Result{Diagnostics: []*rdf.Diagnostic{
   124  		{
   125  			Location: &rdf.Location{
   126  				Range: &rdf.Range{Start: &rdf.Position{
   127  					Line:   1,
   128  					Column: 14,
   129  				}},
   130  				Path: "reviewdog.go",
   131  			},
   132  			Message: "msg",
   133  		},
   134  	}})
   135  
   136  	projectRunAndParse = func(ctx context.Context, conf *project.Config, runners map[string]bool, level string, tee bool) (*reviewdog.ResultMap, error) {
   137  		return &wantDiagnosticResult, nil
   138  	}
   139  
   140  	tmp, err := os.CreateTemp("", "")
   141  	if err != nil {
   142  		t.Fatal(err)
   143  	}
   144  	defer os.Remove(tmp.Name())
   145  
   146  	got, err := checkResultSet(context.Background(), nil, &option{conf: tmp.Name()}, true)
   147  	if err != nil {
   148  		t.Fatal(err)
   149  	}
   150  
   151  	if got.Len() != wantDiagnosticResult.Len() {
   152  		t.Errorf("length of results is different. got = %d, want = %d\n", got.Len(), wantDiagnosticResult.Len())
   153  	}
   154  	got.Range(func(k string, r *reviewdog.Result) {
   155  		w, _ := wantDiagnosticResult.Load(k)
   156  		if diff := cmp.Diff(r, w, protocmp.Transform()); diff != "" {
   157  			t.Errorf("result has diff:\n%s", diff)
   158  		}
   159  	})
   160  }
   161  
   162  func TestDiagnosticResultSet_NonProject(t *testing.T) {
   163  	opt := &option{
   164  		f: "golint",
   165  	}
   166  	input := `reviewdog.go:14:14: test message`
   167  	got, err := checkResultSet(context.Background(), strings.NewReader(input), opt, false)
   168  	if err != nil {
   169  		t.Fatal(err)
   170  	}
   171  	var want reviewdog.ResultMap
   172  	want.Store("golint", &reviewdog.Result{Diagnostics: []*rdf.Diagnostic{
   173  		{
   174  			Location: &rdf.Location{
   175  				Range: &rdf.Range{Start: &rdf.Position{
   176  					Line:   14,
   177  					Column: 14,
   178  				}},
   179  				Path: "reviewdog.go",
   180  			},
   181  			Message:        "test message",
   182  			OriginalOutput: input,
   183  		},
   184  	}})
   185  
   186  	if got.Len() != want.Len() {
   187  		t.Errorf("length of results is different. got = %d, want = %d\n", got.Len(), want.Len())
   188  	}
   189  	got.Range(func(k string, r *reviewdog.Result) {
   190  		w, _ := want.Load(k)
   191  		if diff := cmp.Diff(r, w, protocmp.Transform()); diff != "" {
   192  			t.Errorf("result has diff:\n%s", diff)
   193  		}
   194  	})
   195  }
   196  
   197  type fakeDoghouseServerCli struct {
   198  	client.DogHouseClientInterface
   199  	FakeCheck func(context.Context, *doghouse.CheckRequest) (*doghouse.CheckResponse, error)
   200  }
   201  
   202  func (f *fakeDoghouseServerCli) Check(ctx context.Context, req *doghouse.CheckRequest) (*doghouse.CheckResponse, error) {
   203  	return f.FakeCheck(ctx, req)
   204  }
   205  
   206  func TestPostResultSet_withReportURL(t *testing.T) {
   207  	const (
   208  		owner = "haya14busa"
   209  		repo  = "reviewdog"
   210  		prNum = 14
   211  		sha   = "1414"
   212  	)
   213  
   214  	fakeCli := &fakeDoghouseServerCli{}
   215  	fakeCli.FakeCheck = func(ctx context.Context, req *doghouse.CheckRequest) (*doghouse.CheckResponse, error) {
   216  		if req.Owner != owner {
   217  			t.Errorf("req.Owner = %q, want %q", req.Owner, owner)
   218  		}
   219  		if req.Repo != repo {
   220  			t.Errorf("req.Repo = %q, want %q", req.Repo, repo)
   221  		}
   222  		if req.SHA != sha {
   223  			t.Errorf("req.SHA = %q, want %q", req.SHA, sha)
   224  		}
   225  		if req.PullRequest != prNum {
   226  			t.Errorf("req.PullRequest = %d, want %d", req.PullRequest, prNum)
   227  		}
   228  		switch req.Name {
   229  		case "name1":
   230  			if diff := cmp.Diff(req.Annotations, []*doghouse.Annotation{
   231  				{
   232  					Diagnostic: &rdf.Diagnostic{
   233  						Message: "name1: test 1",
   234  						Location: &rdf.Location{
   235  							Path: "cmd/mistwind/reviewdog.go",
   236  							Range: &rdf.Range{
   237  								Start: &rdf.Position{Line: 14},
   238  							},
   239  						},
   240  						OriginalOutput: "L1\nL2",
   241  					},
   242  				},
   243  				{
   244  					Diagnostic: &rdf.Diagnostic{
   245  						Message: "name1: test 2",
   246  						Location: &rdf.Location{
   247  							Path: "cmd/mistwind/reviewdog.go",
   248  						},
   249  					},
   250  				},
   251  			}, protocmp.Transform()); diff != "" {
   252  				t.Errorf("%s: req.Annotation have diff:\n%s", req.Name, diff)
   253  			}
   254  		case "name2":
   255  			if diff := cmp.Diff(req.Annotations, []*doghouse.Annotation{
   256  				{
   257  					Diagnostic: &rdf.Diagnostic{
   258  						Message: "name2: test 1",
   259  						Location: &rdf.Location{
   260  							Path: "cmd/reviewdog/doghouse.go",
   261  							Range: &rdf.Range{
   262  								Start: &rdf.Position{Line: 14},
   263  							},
   264  						},
   265  					},
   266  				},
   267  			}, protocmp.Transform()); diff != "" {
   268  				t.Errorf("%s: req.Annotation have diff:\n%s", req.Name, diff)
   269  			}
   270  		default:
   271  			t.Errorf("unexpected req.Name: %s", req.Name)
   272  		}
   273  		return &doghouse.CheckResponse{ReportURL: "xxx"}, nil
   274  	}
   275  
   276  	// It assumes the current dir is ./cmd/reviewdog/
   277  	var resultSet reviewdog.ResultMap
   278  	resultSet.Store("name1", &reviewdog.Result{Diagnostics: []*rdf.Diagnostic{
   279  		{
   280  			Location: &rdf.Location{
   281  				Range: &rdf.Range{Start: &rdf.Position{
   282  					Line: 14,
   283  				}},
   284  				Path: "reviewdog.go", // test relative path
   285  			},
   286  			Message:        "name1: test 1",
   287  			OriginalOutput: "L1\nL2",
   288  		},
   289  		{
   290  			Location: &rdf.Location{
   291  				Path: absPath(t, "reviewdog.go"), // test abs path
   292  			},
   293  			Message: "name1: test 2",
   294  		},
   295  	}})
   296  	resultSet.Store("name2", &reviewdog.Result{Diagnostics: []*rdf.Diagnostic{
   297  		{
   298  			Location: &rdf.Location{
   299  				Range: &rdf.Range{Start: &rdf.Position{
   300  					Line: 14,
   301  				}},
   302  				Path: "doghouse.go",
   303  			},
   304  			Message: "name2: test 1",
   305  		},
   306  	}})
   307  
   308  	ghInfo := &cienv.BuildInfo{
   309  		Owner:       owner,
   310  		Repo:        repo,
   311  		PullRequest: prNum,
   312  		SHA:         sha,
   313  	}
   314  
   315  	opt := &option{filterMode: filter.ModeAdded}
   316  	if _, err := postResultSet(context.Background(), &resultSet, ghInfo, fakeCli, opt); err != nil {
   317  		t.Fatal(err)
   318  	}
   319  }
   320  
   321  func TestPostResultSet_withoutReportURL(t *testing.T) {
   322  	const (
   323  		owner = "haya14busa"
   324  		repo  = "reviewdog"
   325  		prNum = 14
   326  		sha   = "1414"
   327  	)
   328  
   329  	wantResults := []*filter.FilteredDiagnostic{{ShouldReport: true}}
   330  	fakeCli := &fakeDoghouseServerCli{}
   331  	fakeCli.FakeCheck = func(ctx context.Context, req *doghouse.CheckRequest) (*doghouse.CheckResponse, error) {
   332  		return &doghouse.CheckResponse{CheckedResults: wantResults}, nil
   333  	}
   334  
   335  	var resultSet reviewdog.ResultMap
   336  	resultSet.Store("name1", &reviewdog.Result{Diagnostics: []*rdf.Diagnostic{}})
   337  
   338  	ghInfo := &cienv.BuildInfo{Owner: owner, Repo: repo, PullRequest: prNum, SHA: sha}
   339  
   340  	opt := &option{filterMode: filter.ModeAdded}
   341  	resp, err := postResultSet(context.Background(), &resultSet, ghInfo, fakeCli, opt)
   342  	if err != nil {
   343  		t.Fatal(err)
   344  	}
   345  	if resp.Len() == 0 {
   346  		t.Fatal("result should not be empty")
   347  	}
   348  	results, err := resp.Load("name1")
   349  	if err != nil {
   350  		t.Fatalf("should have result for name1: %v", err)
   351  	}
   352  	if diff := cmp.Diff(results.FilteredDiagnostic, wantResults, protocmp.Transform()); diff != "" {
   353  		t.Errorf("results has diff:\n%s", diff)
   354  	}
   355  }
   356  
   357  func TestPostResultSet_conclusion(t *testing.T) {
   358  	const (
   359  		owner = "haya14busa"
   360  		repo  = "reviewdog"
   361  		prNum = 14
   362  		sha   = "1414"
   363  	)
   364  
   365  	fakeCli := &fakeDoghouseServerCli{}
   366  	var resultSet reviewdog.ResultMap
   367  	resultSet.Store("name1", &reviewdog.Result{Diagnostics: []*rdf.Diagnostic{}})
   368  	ghInfo := &cienv.BuildInfo{Owner: owner, Repo: repo, PullRequest: prNum, SHA: sha}
   369  
   370  	tests := []struct {
   371  		conclusion  string
   372  		failOnError bool
   373  		wantErr     bool
   374  	}{
   375  		{conclusion: "failure", failOnError: true, wantErr: true},
   376  		{conclusion: "neutral", failOnError: true, wantErr: false},
   377  		{conclusion: "success", failOnError: true, wantErr: false},
   378  		{conclusion: "", failOnError: true, wantErr: false},
   379  		{conclusion: "failure", failOnError: false, wantErr: false},
   380  	}
   381  
   382  	for _, tt := range tests {
   383  		tt := tt
   384  		fakeCli.FakeCheck = func(ctx context.Context, req *doghouse.CheckRequest) (*doghouse.CheckResponse, error) {
   385  			return &doghouse.CheckResponse{ReportURL: "xxx", Conclusion: tt.conclusion}, nil
   386  		}
   387  		opt := &option{filterMode: filter.ModeAdded, failOnError: tt.failOnError}
   388  		id := fmt.Sprintf("[conclusion=%s, failOnError=%v]", tt.conclusion, tt.failOnError)
   389  		_, err := postResultSet(context.Background(), &resultSet, ghInfo, fakeCli, opt)
   390  		if tt.wantErr && err == nil {
   391  			t.Errorf("[%s] want err, but got nil.", id)
   392  		} else if !tt.wantErr && err != nil {
   393  			t.Errorf("[%s] got unexpected error: %v", id, err)
   394  		}
   395  	}
   396  }
   397  
   398  func TestPostResultSet_withEmptyResponse(t *testing.T) {
   399  	const (
   400  		owner = "haya14busa"
   401  		repo  = "reviewdog"
   402  		prNum = 14
   403  		sha   = "1414"
   404  	)
   405  
   406  	fakeCli := &fakeDoghouseServerCli{}
   407  	fakeCli.FakeCheck = func(ctx context.Context, req *doghouse.CheckRequest) (*doghouse.CheckResponse, error) {
   408  		return &doghouse.CheckResponse{}, nil
   409  	}
   410  
   411  	var resultSet reviewdog.ResultMap
   412  	resultSet.Store("name1", &reviewdog.Result{Diagnostics: []*rdf.Diagnostic{}})
   413  
   414  	ghInfo := &cienv.BuildInfo{Owner: owner, Repo: repo, PullRequest: prNum, SHA: sha}
   415  
   416  	opt := &option{filterMode: filter.ModeAdded}
   417  	if _, err := postResultSet(context.Background(), &resultSet, ghInfo, fakeCli, opt); err == nil {
   418  		t.Error("got no error but want report missing error")
   419  	}
   420  }
   421  
   422  func TestReportResults(t *testing.T) {
   423  	cleanup := setupEnvs(map[string]string{
   424  		"GITHUB_ACTIONS":    "",
   425  		"GITHUB_EVENT_PATH": "",
   426  	})
   427  	defer cleanup()
   428  	filteredResultSet := new(reviewdog.FilteredResultMap)
   429  	filteredResultSet.Store("name1", &reviewdog.FilteredResult{
   430  		FilteredDiagnostic: []*filter.FilteredDiagnostic{
   431  			{
   432  				Diagnostic: &rdf.Diagnostic{
   433  					OriginalOutput: "name1-L1\nname1-L2",
   434  				},
   435  				ShouldReport: true,
   436  			},
   437  			{
   438  				Diagnostic: &rdf.Diagnostic{
   439  					OriginalOutput: "name1.2-L1\nname1.2-L2",
   440  				},
   441  				ShouldReport: false,
   442  			},
   443  		},
   444  	})
   445  	filteredResultSet.Store("name2", &reviewdog.FilteredResult{
   446  		FilteredDiagnostic: []*filter.FilteredDiagnostic{
   447  			{
   448  				Diagnostic: &rdf.Diagnostic{
   449  					OriginalOutput: "name1-L1\nname1-L2",
   450  				},
   451  				ShouldReport: false,
   452  			},
   453  		},
   454  	})
   455  	stdout := new(bytes.Buffer)
   456  	foundResultShouldReport := reportResults(stdout, filteredResultSet)
   457  	if !foundResultShouldReport {
   458  		t.Errorf("foundResultShouldReport = %v, want true", foundResultShouldReport)
   459  	}
   460  	want := `reviewdog: Reporting results for "name1"
   461  name1-L1
   462  name1-L2
   463  reviewdog: Reporting results for "name2"
   464  reviewdog: No results found for "name2". 1 results found outside diff.
   465  `
   466  	if got := stdout.String(); got != want {
   467  		t.Errorf("diff found for report:\ngot:\n%s\nwant:\n%s", got, want)
   468  	}
   469  }
   470  
   471  func TestReportResults_inGitHubAction(t *testing.T) {
   472  	cleanup := setupEnvs(map[string]string{
   473  		"GITHUB_ACTIONS":    "true",
   474  		"GITHUB_EVENT_PATH": "",
   475  	})
   476  	defer cleanup()
   477  	filteredResultSet := new(reviewdog.FilteredResultMap)
   478  	filteredResultSet.Store("name1", &reviewdog.FilteredResult{
   479  		FilteredDiagnostic: []*filter.FilteredDiagnostic{
   480  			{
   481  				Diagnostic: &rdf.Diagnostic{
   482  					OriginalOutput: "name1-L1\nname1-L2",
   483  				},
   484  				ShouldReport: true,
   485  			},
   486  		},
   487  	})
   488  	stdout := new(bytes.Buffer)
   489  	_ = reportResults(stdout, filteredResultSet)
   490  	want := `reviewdog: Reporting results for "name1"
   491  `
   492  	if got := stdout.String(); got != want {
   493  		t.Errorf("diff found for report:\ngot:\n%s\nwant:\n%s", got, want)
   494  	}
   495  }
   496  
   497  func TestReportResults_noResultsShouldReport(t *testing.T) {
   498  	cleanup := setupEnvs(map[string]string{
   499  		"GITHUB_ACTIONS":    "",
   500  		"GITHUB_EVENT_PATH": "",
   501  	})
   502  	defer cleanup()
   503  	filteredResultSet := new(reviewdog.FilteredResultMap)
   504  	filteredResultSet.Store("name1", &reviewdog.FilteredResult{
   505  		FilteredDiagnostic: []*filter.FilteredDiagnostic{
   506  			{
   507  				Diagnostic: &rdf.Diagnostic{
   508  					OriginalOutput: "name1-L1\nname1-L2",
   509  				},
   510  				ShouldReport: false,
   511  			},
   512  			{
   513  				Diagnostic: &rdf.Diagnostic{
   514  					OriginalOutput: "name1.2-L1\nname1.2-L2",
   515  				},
   516  				ShouldReport: false,
   517  			},
   518  		},
   519  	})
   520  	filteredResultSet.Store("name2", &reviewdog.FilteredResult{
   521  		FilteredDiagnostic: []*filter.FilteredDiagnostic{
   522  			{
   523  				Diagnostic: &rdf.Diagnostic{
   524  					OriginalOutput: "name1-L1\nname1-L2",
   525  				},
   526  				ShouldReport: false,
   527  			},
   528  		},
   529  	})
   530  	stdout := new(bytes.Buffer)
   531  	foundResultShouldReport := reportResults(stdout, filteredResultSet)
   532  	if foundResultShouldReport {
   533  		t.Errorf("foundResultShouldReport = %v, want false", foundResultShouldReport)
   534  	}
   535  	want := `reviewdog: Reporting results for "name1"
   536  reviewdog: No results found for "name1". 2 results found outside diff.
   537  reviewdog: Reporting results for "name2"
   538  reviewdog: No results found for "name2". 1 results found outside diff.
   539  `
   540  	if got := stdout.String(); got != want {
   541  		t.Errorf("diff found for report:\ngot:\n%s\nwant:\n%s", got, want)
   542  	}
   543  }
   544  
   545  func absPath(t *testing.T, path string) string {
   546  	p, err := filepath.Abs(path)
   547  	if err != nil {
   548  		t.Errorf("filepath.Abs(%q) failed: %v", path, err)
   549  	}
   550  	return p
   551  }