github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/search/repos/repos_test.go (about)

     1  package repos
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/ungtb10d/cli/v2/internal/browser"
    10  	"github.com/ungtb10d/cli/v2/pkg/cmdutil"
    11  	"github.com/ungtb10d/cli/v2/pkg/iostreams"
    12  	"github.com/ungtb10d/cli/v2/pkg/search"
    13  	"github.com/google/shlex"
    14  	"github.com/stretchr/testify/assert"
    15  )
    16  
    17  func TestNewCmdRepos(t *testing.T) {
    18  	var trueBool = true
    19  	tests := []struct {
    20  		name    string
    21  		input   string
    22  		output  ReposOptions
    23  		wantErr bool
    24  		errMsg  string
    25  	}{
    26  		{
    27  			name:    "no arguments",
    28  			input:   "",
    29  			wantErr: true,
    30  			errMsg:  "specify search keywords or flags",
    31  		},
    32  		{
    33  			name:  "keyword arguments",
    34  			input: "some search terms",
    35  			output: ReposOptions{
    36  				Query: search.Query{Keywords: []string{"some", "search", "terms"}, Kind: "repositories", Limit: 30},
    37  			},
    38  		},
    39  		{
    40  			name:  "web flag",
    41  			input: "--web",
    42  			output: ReposOptions{
    43  				Query:   search.Query{Keywords: []string{}, Kind: "repositories", Limit: 30},
    44  				WebMode: true,
    45  			},
    46  		},
    47  		{
    48  			name:   "limit flag",
    49  			input:  "--limit 10",
    50  			output: ReposOptions{Query: search.Query{Keywords: []string{}, Kind: "repositories", Limit: 10}},
    51  		},
    52  		{
    53  			name:    "invalid limit flag",
    54  			input:   "--limit 1001",
    55  			wantErr: true,
    56  			errMsg:  "`--limit` must be between 1 and 1000",
    57  		},
    58  		{
    59  			name:  "order flag",
    60  			input: "--order asc",
    61  			output: ReposOptions{
    62  				Query: search.Query{Keywords: []string{}, Kind: "repositories", Limit: 30, Order: "asc"},
    63  			},
    64  		},
    65  		{
    66  			name:    "invalid order flag",
    67  			input:   "--order invalid",
    68  			wantErr: true,
    69  			errMsg:  "invalid argument \"invalid\" for \"--order\" flag: valid values are {asc|desc}",
    70  		},
    71  		{
    72  			name: "qualifier flags",
    73  			input: `
    74        --archived
    75        --created=created
    76        --followers=1
    77        --include-forks=true
    78        --forks=2
    79        --good-first-issues=3
    80        --help-wanted-issues=4
    81        --match=description,readme
    82        --language=language
    83        --license=license
    84        --owner=owner
    85        --updated=updated
    86        --size=5
    87        --stars=6
    88        --topic=topic
    89        --number-topics=7
    90        --visibility=public
    91        `,
    92  			output: ReposOptions{
    93  				Query: search.Query{
    94  					Keywords: []string{},
    95  					Kind:     "repositories",
    96  					Limit:    30,
    97  					Qualifiers: search.Qualifiers{
    98  						Archived:         &trueBool,
    99  						Created:          "created",
   100  						Followers:        "1",
   101  						Fork:             "true",
   102  						Forks:            "2",
   103  						GoodFirstIssues:  "3",
   104  						HelpWantedIssues: "4",
   105  						In:               []string{"description", "readme"},
   106  						Language:         "language",
   107  						License:          []string{"license"},
   108  						Pushed:           "updated",
   109  						Size:             "5",
   110  						Stars:            "6",
   111  						Topic:            []string{"topic"},
   112  						Topics:           "7",
   113  						User:             "owner",
   114  						Is:               []string{"public"},
   115  					},
   116  				},
   117  			},
   118  		},
   119  	}
   120  	for _, tt := range tests {
   121  		t.Run(tt.name, func(t *testing.T) {
   122  			ios, _, _, _ := iostreams.Test()
   123  			f := &cmdutil.Factory{
   124  				IOStreams: ios,
   125  			}
   126  			argv, err := shlex.Split(tt.input)
   127  			assert.NoError(t, err)
   128  			var gotOpts *ReposOptions
   129  			cmd := NewCmdRepos(f, func(opts *ReposOptions) error {
   130  				gotOpts = opts
   131  				return nil
   132  			})
   133  			cmd.SetArgs(argv)
   134  			cmd.SetIn(&bytes.Buffer{})
   135  			cmd.SetOut(&bytes.Buffer{})
   136  			cmd.SetErr(&bytes.Buffer{})
   137  
   138  			_, err = cmd.ExecuteC()
   139  			if tt.wantErr {
   140  				assert.EqualError(t, err, tt.errMsg)
   141  				return
   142  			}
   143  
   144  			assert.NoError(t, err)
   145  			assert.Equal(t, tt.output.Query, gotOpts.Query)
   146  			assert.Equal(t, tt.output.WebMode, gotOpts.WebMode)
   147  		})
   148  	}
   149  }
   150  
   151  func TestReposRun(t *testing.T) {
   152  	var now = time.Date(2022, 2, 28, 12, 30, 0, 0, time.UTC)
   153  	var updatedAt = time.Date(2021, 2, 28, 12, 30, 0, 0, time.UTC)
   154  	var query = search.Query{
   155  		Keywords: []string{"cli"},
   156  		Kind:     "repositories",
   157  		Limit:    30,
   158  		Qualifiers: search.Qualifiers{
   159  			Stars: ">50",
   160  			Topic: []string{"golang"},
   161  		},
   162  	}
   163  	tests := []struct {
   164  		errMsg     string
   165  		name       string
   166  		opts       *ReposOptions
   167  		tty        bool
   168  		wantErr    bool
   169  		wantStderr string
   170  		wantStdout string
   171  	}{
   172  		{
   173  			name: "displays results tty",
   174  			opts: &ReposOptions{
   175  				Query: query,
   176  				Searcher: &search.SearcherMock{
   177  					RepositoriesFunc: func(query search.Query) (search.RepositoriesResult, error) {
   178  						return search.RepositoriesResult{
   179  							IncompleteResults: false,
   180  							Items: []search.Repository{
   181  								{FullName: "test/cli", Description: "of course", IsPrivate: true, IsArchived: true, UpdatedAt: updatedAt, Visibility: "private"},
   182  								{FullName: "test/cliing", Description: "wow", IsFork: true, UpdatedAt: updatedAt, Visibility: "public"},
   183  								{FullName: "ungtb10d/cli", Description: "so much", IsArchived: false, UpdatedAt: updatedAt, Visibility: "internal"},
   184  							},
   185  							Total: 300,
   186  						}, nil
   187  					},
   188  				},
   189  			},
   190  			tty:        true,
   191  			wantStdout: "\nShowing 3 of 300 repositories\n\ntest/cli     of course  private, archived  Feb 28, 2021\ntest/cliing  wow        public, fork       Feb 28, 2021\ncli/cli      so much    internal           Feb 28, 2021\n",
   192  		},
   193  		{
   194  			name: "displays results notty",
   195  			opts: &ReposOptions{
   196  				Query: query,
   197  				Searcher: &search.SearcherMock{
   198  					RepositoriesFunc: func(query search.Query) (search.RepositoriesResult, error) {
   199  						return search.RepositoriesResult{
   200  							IncompleteResults: false,
   201  							Items: []search.Repository{
   202  								{FullName: "test/cli", Description: "of course", IsPrivate: true, IsArchived: true, UpdatedAt: updatedAt, Visibility: "private"},
   203  								{FullName: "test/cliing", Description: "wow", IsFork: true, UpdatedAt: updatedAt, Visibility: "public"},
   204  								{FullName: "ungtb10d/cli", Description: "so much", IsArchived: false, UpdatedAt: updatedAt, Visibility: "internal"},
   205  							},
   206  							Total: 300,
   207  						}, nil
   208  					},
   209  				},
   210  			},
   211  			wantStdout: "test/cli\tof course\tprivate, archived\t2021-02-28T12:30:00Z\ntest/cliing\twow\tpublic, fork\t2021-02-28T12:30:00Z\ncli/cli\tso much\tinternal\t2021-02-28T12:30:00Z\n",
   212  		},
   213  		{
   214  			name: "displays no results",
   215  			opts: &ReposOptions{
   216  				Query: query,
   217  				Searcher: &search.SearcherMock{
   218  					RepositoriesFunc: func(query search.Query) (search.RepositoriesResult, error) {
   219  						return search.RepositoriesResult{}, nil
   220  					},
   221  				},
   222  			},
   223  			wantErr: true,
   224  			errMsg:  "no repositories matched your search",
   225  		},
   226  		{
   227  			name: "displays search error",
   228  			opts: &ReposOptions{
   229  				Query: query,
   230  				Searcher: &search.SearcherMock{
   231  					RepositoriesFunc: func(query search.Query) (search.RepositoriesResult, error) {
   232  						return search.RepositoriesResult{}, fmt.Errorf("error with query")
   233  					},
   234  				},
   235  			},
   236  			errMsg:  "error with query",
   237  			wantErr: true,
   238  		},
   239  		{
   240  			name: "opens browser for web mode tty",
   241  			opts: &ReposOptions{
   242  				Browser: &browser.Stub{},
   243  				Query:   query,
   244  				Searcher: &search.SearcherMock{
   245  					URLFunc: func(query search.Query) string {
   246  						return "https://github.com/search?type=repositories&q=cli"
   247  					},
   248  				},
   249  				WebMode: true,
   250  			},
   251  			tty:        true,
   252  			wantStderr: "Opening github.com/search in your browser.\n",
   253  		},
   254  		{
   255  			name: "opens browser for web mode notty",
   256  			opts: &ReposOptions{
   257  				Browser: &browser.Stub{},
   258  				Query:   query,
   259  				Searcher: &search.SearcherMock{
   260  					URLFunc: func(query search.Query) string {
   261  						return "https://github.com/search?type=repositories&q=cli"
   262  					},
   263  				},
   264  				WebMode: true,
   265  			},
   266  		},
   267  	}
   268  	for _, tt := range tests {
   269  		ios, _, stdout, stderr := iostreams.Test()
   270  		ios.SetStdinTTY(tt.tty)
   271  		ios.SetStdoutTTY(tt.tty)
   272  		ios.SetStderrTTY(tt.tty)
   273  		tt.opts.IO = ios
   274  		tt.opts.Now = now
   275  		t.Run(tt.name, func(t *testing.T) {
   276  			err := reposRun(tt.opts)
   277  			if tt.wantErr {
   278  				assert.EqualError(t, err, tt.errMsg)
   279  				return
   280  			} else if err != nil {
   281  				t.Fatalf("reposRun unexpected error: %v", err)
   282  			}
   283  			assert.Equal(t, tt.wantStdout, stdout.String())
   284  			assert.Equal(t, tt.wantStderr, stderr.String())
   285  		})
   286  	}
   287  }