github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/repo/list/list_test.go (about)

     1  package list
     2  
     3  import (
     4  	"bytes"
     5  	"io"
     6  	"net/http"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/MakeNowJust/heredoc"
    12  	"github.com/google/shlex"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  
    16  	"github.com/ungtb10d/cli/v2/internal/config"
    17  	fd "github.com/ungtb10d/cli/v2/internal/featuredetection"
    18  	"github.com/ungtb10d/cli/v2/pkg/cmdutil"
    19  	"github.com/ungtb10d/cli/v2/pkg/httpmock"
    20  	"github.com/ungtb10d/cli/v2/pkg/iostreams"
    21  	"github.com/ungtb10d/cli/v2/test"
    22  )
    23  
    24  func TestNewCmdList(t *testing.T) {
    25  	tests := []struct {
    26  		name     string
    27  		cli      string
    28  		wants    ListOptions
    29  		wantsErr string
    30  	}{
    31  		{
    32  			name: "no arguments",
    33  			cli:  "",
    34  			wants: ListOptions{
    35  				Limit:       30,
    36  				Owner:       "",
    37  				Visibility:  "",
    38  				Fork:        false,
    39  				Source:      false,
    40  				Language:    "",
    41  				Topic:       []string(nil),
    42  				Archived:    false,
    43  				NonArchived: false,
    44  			},
    45  		},
    46  		{
    47  			name: "with owner",
    48  			cli:  "monalisa",
    49  			wants: ListOptions{
    50  				Limit:       30,
    51  				Owner:       "monalisa",
    52  				Visibility:  "",
    53  				Fork:        false,
    54  				Source:      false,
    55  				Language:    "",
    56  				Topic:       []string(nil),
    57  				Archived:    false,
    58  				NonArchived: false,
    59  			},
    60  		},
    61  		{
    62  			name: "with limit",
    63  			cli:  "-L 101",
    64  			wants: ListOptions{
    65  				Limit:       101,
    66  				Owner:       "",
    67  				Visibility:  "",
    68  				Fork:        false,
    69  				Source:      false,
    70  				Language:    "",
    71  				Topic:       []string(nil),
    72  				Archived:    false,
    73  				NonArchived: false,
    74  			},
    75  		},
    76  		{
    77  			name: "only public",
    78  			cli:  "--visibility=public",
    79  			wants: ListOptions{
    80  				Limit:       30,
    81  				Owner:       "",
    82  				Visibility:  "public",
    83  				Fork:        false,
    84  				Source:      false,
    85  				Language:    "",
    86  				Topic:       []string(nil),
    87  				Archived:    false,
    88  				NonArchived: false,
    89  			},
    90  		},
    91  		{
    92  			name: "only private",
    93  			cli:  "--visibility=private",
    94  			wants: ListOptions{
    95  				Limit:       30,
    96  				Owner:       "",
    97  				Visibility:  "private",
    98  				Fork:        false,
    99  				Source:      false,
   100  				Language:    "",
   101  				Topic:       []string(nil),
   102  				Archived:    false,
   103  				NonArchived: false,
   104  			},
   105  		},
   106  		{
   107  			name: "only forks",
   108  			cli:  "--fork",
   109  			wants: ListOptions{
   110  				Limit:       30,
   111  				Owner:       "",
   112  				Visibility:  "",
   113  				Fork:        true,
   114  				Source:      false,
   115  				Language:    "",
   116  				Topic:       []string(nil),
   117  				Archived:    false,
   118  				NonArchived: false,
   119  			},
   120  		},
   121  		{
   122  			name: "only sources",
   123  			cli:  "--source",
   124  			wants: ListOptions{
   125  				Limit:       30,
   126  				Owner:       "",
   127  				Visibility:  "",
   128  				Fork:        false,
   129  				Source:      true,
   130  				Language:    "",
   131  				Topic:       []string(nil),
   132  				Archived:    false,
   133  				NonArchived: false,
   134  			},
   135  		},
   136  		{
   137  			name: "with language",
   138  			cli:  "-l go",
   139  			wants: ListOptions{
   140  				Limit:       30,
   141  				Owner:       "",
   142  				Visibility:  "",
   143  				Fork:        false,
   144  				Source:      false,
   145  				Language:    "go",
   146  				Topic:       []string(nil),
   147  				Archived:    false,
   148  				NonArchived: false,
   149  			},
   150  		},
   151  		{
   152  			name: "only archived",
   153  			cli:  "--archived",
   154  			wants: ListOptions{
   155  				Limit:       30,
   156  				Owner:       "",
   157  				Visibility:  "",
   158  				Fork:        false,
   159  				Source:      false,
   160  				Language:    "",
   161  				Topic:       []string(nil),
   162  				Archived:    true,
   163  				NonArchived: false,
   164  			},
   165  		},
   166  		{
   167  			name: "only non-archived",
   168  			cli:  "--no-archived",
   169  			wants: ListOptions{
   170  				Limit:       30,
   171  				Owner:       "",
   172  				Visibility:  "",
   173  				Fork:        false,
   174  				Source:      false,
   175  				Language:    "",
   176  				Topic:       []string(nil),
   177  				Archived:    false,
   178  				NonArchived: true,
   179  			},
   180  		},
   181  		{
   182  			name: "with topic",
   183  			cli:  "--topic cli",
   184  			wants: ListOptions{
   185  				Limit:       30,
   186  				Owner:       "",
   187  				Visibility:  "",
   188  				Fork:        false,
   189  				Source:      false,
   190  				Language:    "",
   191  				Topic:       []string{"cli"},
   192  				Archived:    false,
   193  				NonArchived: false,
   194  			},
   195  		},
   196  		{
   197  			name: "with multiple topic",
   198  			cli:  "--topic cli --topic multiple-topic",
   199  			wants: ListOptions{
   200  				Limit:       30,
   201  				Owner:       "",
   202  				Visibility:  "",
   203  				Fork:        false,
   204  				Source:      false,
   205  				Language:    "",
   206  				Topic:       []string{"cli", "multiple-topic"},
   207  				Archived:    false,
   208  				NonArchived: false,
   209  			},
   210  		},
   211  		{
   212  			name:     "invalid visibility",
   213  			cli:      "--visibility=bad",
   214  			wantsErr: "invalid argument \"bad\" for \"--visibility\" flag: valid values are {public|private|internal}",
   215  		},
   216  		{
   217  			name:     "no forks with sources",
   218  			cli:      "--fork --source",
   219  			wantsErr: "specify only one of `--source` or `--fork`",
   220  		},
   221  		{
   222  			name:     "conflicting archived",
   223  			cli:      "--archived --no-archived",
   224  			wantsErr: "specify only one of `--archived` or `--no-archived`",
   225  		},
   226  		{
   227  			name:     "too many arguments",
   228  			cli:      "monalisa hubot",
   229  			wantsErr: "accepts at most 1 arg(s), received 2",
   230  		},
   231  		{
   232  			name:     "invalid limit",
   233  			cli:      "-L 0",
   234  			wantsErr: "invalid limit: 0",
   235  		},
   236  	}
   237  
   238  	for _, tt := range tests {
   239  		t.Run(tt.name, func(t *testing.T) {
   240  			f := &cmdutil.Factory{}
   241  
   242  			argv, err := shlex.Split(tt.cli)
   243  			assert.NoError(t, err)
   244  
   245  			var gotOpts *ListOptions
   246  			cmd := NewCmdList(f, func(opts *ListOptions) error {
   247  				gotOpts = opts
   248  				return nil
   249  			})
   250  			cmd.SetArgs(argv)
   251  			cmd.SetIn(&bytes.Buffer{})
   252  			cmd.SetOut(&bytes.Buffer{})
   253  			cmd.SetErr(&bytes.Buffer{})
   254  
   255  			_, err = cmd.ExecuteC()
   256  			if tt.wantsErr != "" {
   257  				assert.EqualError(t, err, tt.wantsErr)
   258  				return
   259  			}
   260  			require.NoError(t, err)
   261  
   262  			assert.Equal(t, tt.wants.Limit, gotOpts.Limit)
   263  			assert.Equal(t, tt.wants.Owner, gotOpts.Owner)
   264  			assert.Equal(t, tt.wants.Visibility, gotOpts.Visibility)
   265  			assert.Equal(t, tt.wants.Fork, gotOpts.Fork)
   266  			assert.Equal(t, tt.wants.Topic, gotOpts.Topic)
   267  			assert.Equal(t, tt.wants.Source, gotOpts.Source)
   268  			assert.Equal(t, tt.wants.Archived, gotOpts.Archived)
   269  			assert.Equal(t, tt.wants.NonArchived, gotOpts.NonArchived)
   270  		})
   271  	}
   272  }
   273  
   274  func runCommand(rt http.RoundTripper, isTTY bool, cli string) (*test.CmdOut, error) {
   275  	ios, _, stdout, stderr := iostreams.Test()
   276  	ios.SetStdoutTTY(isTTY)
   277  	ios.SetStdinTTY(isTTY)
   278  	ios.SetStderrTTY(isTTY)
   279  
   280  	factory := &cmdutil.Factory{
   281  		IOStreams: ios,
   282  		HttpClient: func() (*http.Client, error) {
   283  			return &http.Client{Transport: rt}, nil
   284  		},
   285  		Config: func() (config.Config, error) {
   286  			return config.NewBlankConfig(), nil
   287  		},
   288  	}
   289  
   290  	cmd := NewCmdList(factory, nil)
   291  
   292  	argv, err := shlex.Split(cli)
   293  	if err != nil {
   294  		return nil, err
   295  	}
   296  	cmd.SetArgs(argv)
   297  
   298  	cmd.SetIn(&bytes.Buffer{})
   299  	cmd.SetOut(io.Discard)
   300  	cmd.SetErr(io.Discard)
   301  
   302  	_, err = cmd.ExecuteC()
   303  	return &test.CmdOut{
   304  		OutBuf: stdout,
   305  		ErrBuf: stderr,
   306  	}, err
   307  }
   308  
   309  func TestRepoList_nontty(t *testing.T) {
   310  	ios, _, stdout, stderr := iostreams.Test()
   311  	ios.SetStdoutTTY(false)
   312  	ios.SetStdinTTY(false)
   313  	ios.SetStderrTTY(false)
   314  
   315  	httpReg := &httpmock.Registry{}
   316  	defer httpReg.Verify(t)
   317  
   318  	httpReg.Register(
   319  		httpmock.GraphQL(`query RepositoryList\b`),
   320  		httpmock.FileResponse("./fixtures/repoList.json"),
   321  	)
   322  
   323  	opts := ListOptions{
   324  		IO: ios,
   325  		HttpClient: func() (*http.Client, error) {
   326  			return &http.Client{Transport: httpReg}, nil
   327  		},
   328  		Config: func() (config.Config, error) {
   329  			return config.NewBlankConfig(), nil
   330  		},
   331  		Now: func() time.Time {
   332  			t, _ := time.Parse(time.RFC822, "19 Feb 21 15:00 UTC")
   333  			return t
   334  		},
   335  		Limit: 30,
   336  	}
   337  
   338  	err := listRun(&opts)
   339  	assert.NoError(t, err)
   340  
   341  	assert.Equal(t, "", stderr.String())
   342  
   343  	assert.Equal(t, heredoc.Doc(`
   344  		octocat/hello-world	My first repository	public	2021-02-19T06:34:58Z
   345  		octocat/cli	GitHub CLI	public, fork	2021-02-19T06:06:06Z
   346  		octocat/testing		private	2021-02-11T22:32:05Z
   347  	`), stdout.String())
   348  }
   349  
   350  func TestRepoList_tty(t *testing.T) {
   351  	ios, _, stdout, stderr := iostreams.Test()
   352  	ios.SetStdoutTTY(true)
   353  	ios.SetStdinTTY(true)
   354  	ios.SetStderrTTY(true)
   355  
   356  	httpReg := &httpmock.Registry{}
   357  	defer httpReg.Verify(t)
   358  
   359  	httpReg.Register(
   360  		httpmock.GraphQL(`query RepositoryList\b`),
   361  		httpmock.FileResponse("./fixtures/repoList.json"),
   362  	)
   363  
   364  	opts := ListOptions{
   365  		IO: ios,
   366  		HttpClient: func() (*http.Client, error) {
   367  			return &http.Client{Transport: httpReg}, nil
   368  		},
   369  		Config: func() (config.Config, error) {
   370  			return config.NewBlankConfig(), nil
   371  		},
   372  		Now: func() time.Time {
   373  			t, _ := time.Parse(time.RFC822, "19 Feb 21 15:00 UTC")
   374  			return t
   375  		},
   376  		Limit: 30,
   377  	}
   378  
   379  	err := listRun(&opts)
   380  	assert.NoError(t, err)
   381  
   382  	assert.Equal(t, "", stderr.String())
   383  
   384  	assert.Equal(t, heredoc.Doc(`
   385  
   386  		Showing 3 of 3 repositories in @octocat
   387  
   388  		octocat/hello-world  My first repository  public        8h
   389  		octocat/cli          GitHub CLI           public, fork  8h
   390  		octocat/testing                           private       7d
   391  	`), stdout.String())
   392  }
   393  
   394  func TestRepoList_filtering(t *testing.T) {
   395  	http := &httpmock.Registry{}
   396  	defer http.Verify(t)
   397  
   398  	http.Register(
   399  		httpmock.GraphQL(`query RepositoryList\b`),
   400  		httpmock.GraphQLQuery(`{}`, func(_ string, params map[string]interface{}) {
   401  			assert.Equal(t, "PRIVATE", params["privacy"])
   402  			assert.Equal(t, float64(2), params["perPage"])
   403  		}),
   404  	)
   405  
   406  	output, err := runCommand(http, true, `--visibility=private --limit 2 `)
   407  	if err != nil {
   408  		t.Fatal(err)
   409  	}
   410  
   411  	assert.Equal(t, "", output.Stderr())
   412  	assert.Equal(t, "\nNo results match your search\n\n", output.String())
   413  }
   414  
   415  func TestRepoList_noVisibilityField(t *testing.T) {
   416  	ios, _, stdout, stderr := iostreams.Test()
   417  	ios.SetStdoutTTY(false)
   418  	ios.SetStdinTTY(false)
   419  	ios.SetStderrTTY(false)
   420  
   421  	reg := &httpmock.Registry{}
   422  	defer reg.Verify(t)
   423  
   424  	reg.Register(
   425  		httpmock.GraphQL(`query RepositoryList\b`),
   426  		httpmock.GraphQLQuery(`{"data":{"repositoryOwner":{"login":"octocat","repositories":{"totalCount":0}}}}`,
   427  			func(query string, params map[string]interface{}) {
   428  				assert.False(t, strings.Contains(query, "visibility"))
   429  			},
   430  		),
   431  	)
   432  
   433  	opts := ListOptions{
   434  		IO: ios,
   435  		HttpClient: func() (*http.Client, error) {
   436  			return &http.Client{Transport: reg}, nil
   437  		},
   438  		Config: func() (config.Config, error) {
   439  			return config.NewBlankConfig(), nil
   440  		},
   441  		Now: func() time.Time {
   442  			t, _ := time.Parse(time.RFC822, "19 Feb 21 15:00 UTC")
   443  			return t
   444  		},
   445  		Limit:    30,
   446  		Detector: &fd.DisabledDetectorMock{},
   447  	}
   448  
   449  	err := listRun(&opts)
   450  
   451  	assert.NoError(t, err)
   452  	assert.Equal(t, "", stderr.String())
   453  	assert.Equal(t, "", stdout.String())
   454  }