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 }