github.com/andrewhsu/cli/v2@v2.0.1-0.20210910131313-d4b4061f5b89/pkg/cmd/run/download/download_test.go (about) 1 package download 2 3 import ( 4 "bytes" 5 "io/ioutil" 6 "net/http" 7 "path/filepath" 8 "testing" 9 10 "github.com/andrewhsu/cli/v2/internal/ghrepo" 11 "github.com/andrewhsu/cli/v2/pkg/cmd/run/shared" 12 "github.com/andrewhsu/cli/v2/pkg/cmdutil" 13 "github.com/andrewhsu/cli/v2/pkg/iostreams" 14 "github.com/google/shlex" 15 "github.com/stretchr/testify/assert" 16 "github.com/stretchr/testify/mock" 17 "github.com/stretchr/testify/require" 18 ) 19 20 func Test_NewCmdDownload(t *testing.T) { 21 tests := []struct { 22 name string 23 args string 24 isTTY bool 25 want DownloadOptions 26 wantErr string 27 }{ 28 { 29 name: "empty", 30 args: "", 31 isTTY: true, 32 want: DownloadOptions{ 33 RunID: "", 34 DoPrompt: true, 35 Names: []string(nil), 36 DestinationDir: ".", 37 }, 38 }, 39 { 40 name: "with run ID", 41 args: "2345", 42 isTTY: true, 43 want: DownloadOptions{ 44 RunID: "2345", 45 DoPrompt: false, 46 Names: []string(nil), 47 DestinationDir: ".", 48 }, 49 }, 50 { 51 name: "to destination", 52 args: "2345 -D tmp/dest", 53 isTTY: true, 54 want: DownloadOptions{ 55 RunID: "2345", 56 DoPrompt: false, 57 Names: []string(nil), 58 DestinationDir: "tmp/dest", 59 }, 60 }, 61 { 62 name: "repo level with names", 63 args: "-n one -n two", 64 isTTY: true, 65 want: DownloadOptions{ 66 RunID: "", 67 DoPrompt: false, 68 Names: []string{"one", "two"}, 69 DestinationDir: ".", 70 }, 71 }, 72 } 73 for _, tt := range tests { 74 t.Run(tt.name, func(t *testing.T) { 75 io, _, _, _ := iostreams.Test() 76 io.SetStdoutTTY(tt.isTTY) 77 io.SetStdinTTY(tt.isTTY) 78 io.SetStderrTTY(tt.isTTY) 79 80 f := &cmdutil.Factory{ 81 IOStreams: io, 82 HttpClient: func() (*http.Client, error) { 83 return nil, nil 84 }, 85 BaseRepo: func() (ghrepo.Interface, error) { 86 return nil, nil 87 }, 88 } 89 90 var opts *DownloadOptions 91 cmd := NewCmdDownload(f, func(o *DownloadOptions) error { 92 opts = o 93 return nil 94 }) 95 cmd.PersistentFlags().StringP("repo", "R", "", "") 96 97 argv, err := shlex.Split(tt.args) 98 require.NoError(t, err) 99 cmd.SetArgs(argv) 100 101 cmd.SetIn(&bytes.Buffer{}) 102 cmd.SetOut(ioutil.Discard) 103 cmd.SetErr(ioutil.Discard) 104 105 _, err = cmd.ExecuteC() 106 if tt.wantErr != "" { 107 require.EqualError(t, err, tt.wantErr) 108 return 109 } else { 110 require.NoError(t, err) 111 } 112 113 assert.Equal(t, tt.want.RunID, opts.RunID) 114 assert.Equal(t, tt.want.Names, opts.Names) 115 assert.Equal(t, tt.want.DestinationDir, opts.DestinationDir) 116 assert.Equal(t, tt.want.DoPrompt, opts.DoPrompt) 117 }) 118 } 119 } 120 121 func Test_runDownload(t *testing.T) { 122 tests := []struct { 123 name string 124 opts DownloadOptions 125 mockAPI func(*mockPlatform) 126 mockPrompt func(*mockPrompter) 127 wantErr string 128 }{ 129 { 130 name: "download non-expired", 131 opts: DownloadOptions{ 132 RunID: "2345", 133 DestinationDir: "./tmp", 134 Names: []string(nil), 135 }, 136 mockAPI: func(p *mockPlatform) { 137 p.On("List", "2345").Return([]shared.Artifact{ 138 { 139 Name: "artifact-1", 140 DownloadURL: "http://download.com/artifact1.zip", 141 Expired: false, 142 }, 143 { 144 Name: "expired-artifact", 145 DownloadURL: "http://download.com/expired.zip", 146 Expired: true, 147 }, 148 { 149 Name: "artifact-2", 150 DownloadURL: "http://download.com/artifact2.zip", 151 Expired: false, 152 }, 153 }, nil) 154 p.On("Download", "http://download.com/artifact1.zip", filepath.FromSlash("tmp/artifact-1")).Return(nil) 155 p.On("Download", "http://download.com/artifact2.zip", filepath.FromSlash("tmp/artifact-2")).Return(nil) 156 }, 157 }, 158 { 159 name: "no valid artifacts", 160 opts: DownloadOptions{ 161 RunID: "2345", 162 DestinationDir: ".", 163 Names: []string(nil), 164 }, 165 mockAPI: func(p *mockPlatform) { 166 p.On("List", "2345").Return([]shared.Artifact{ 167 { 168 Name: "artifact-1", 169 DownloadURL: "http://download.com/artifact1.zip", 170 Expired: true, 171 }, 172 { 173 Name: "artifact-2", 174 DownloadURL: "http://download.com/artifact2.zip", 175 Expired: true, 176 }, 177 }, nil) 178 }, 179 wantErr: "no valid artifacts found to download", 180 }, 181 { 182 name: "no matches", 183 opts: DownloadOptions{ 184 RunID: "2345", 185 DestinationDir: ".", 186 Names: []string{"artifact-3"}, 187 }, 188 mockAPI: func(p *mockPlatform) { 189 p.On("List", "2345").Return([]shared.Artifact{ 190 { 191 Name: "artifact-1", 192 DownloadURL: "http://download.com/artifact1.zip", 193 Expired: false, 194 }, 195 { 196 Name: "artifact-2", 197 DownloadURL: "http://download.com/artifact2.zip", 198 Expired: false, 199 }, 200 }, nil) 201 }, 202 wantErr: "no artifact matches any of the names provided", 203 }, 204 { 205 name: "prompt to select artifact", 206 opts: DownloadOptions{ 207 RunID: "", 208 DoPrompt: true, 209 DestinationDir: ".", 210 Names: []string(nil), 211 }, 212 mockAPI: func(p *mockPlatform) { 213 p.On("List", "").Return([]shared.Artifact{ 214 { 215 Name: "artifact-1", 216 DownloadURL: "http://download.com/artifact1.zip", 217 Expired: false, 218 }, 219 { 220 Name: "expired-artifact", 221 DownloadURL: "http://download.com/expired.zip", 222 Expired: true, 223 }, 224 { 225 Name: "artifact-2", 226 DownloadURL: "http://download.com/artifact2.zip", 227 Expired: false, 228 }, 229 { 230 Name: "artifact-2", 231 DownloadURL: "http://download.com/artifact2.also.zip", 232 Expired: false, 233 }, 234 }, nil) 235 p.On("Download", "http://download.com/artifact2.zip", ".").Return(nil) 236 }, 237 mockPrompt: func(p *mockPrompter) { 238 p.On("Prompt", "Select artifacts to download:", []string{"artifact-1", "artifact-2"}, mock.AnythingOfType("*[]string")). 239 Run(func(args mock.Arguments) { 240 result := args.Get(2).(*[]string) 241 *result = []string{"artifact-2"} 242 }). 243 Return(nil) 244 }, 245 }, 246 } 247 for _, tt := range tests { 248 t.Run(tt.name, func(t *testing.T) { 249 opts := &tt.opts 250 io, _, stdout, stderr := iostreams.Test() 251 opts.IO = io 252 opts.Platform = newMockPlatform(t, tt.mockAPI) 253 opts.Prompter = newMockPrompter(t, tt.mockPrompt) 254 255 err := runDownload(opts) 256 if tt.wantErr != "" { 257 require.EqualError(t, err, tt.wantErr) 258 } else { 259 require.NoError(t, err) 260 } 261 262 assert.Equal(t, "", stdout.String()) 263 assert.Equal(t, "", stderr.String()) 264 }) 265 } 266 } 267 268 type mockPlatform struct { 269 mock.Mock 270 } 271 272 func newMockPlatform(t *testing.T, config func(*mockPlatform)) *mockPlatform { 273 m := &mockPlatform{} 274 m.Test(t) 275 t.Cleanup(func() { 276 m.AssertExpectations(t) 277 }) 278 if config != nil { 279 config(m) 280 } 281 return m 282 } 283 284 func (p *mockPlatform) List(runID string) ([]shared.Artifact, error) { 285 args := p.Called(runID) 286 return args.Get(0).([]shared.Artifact), args.Error(1) 287 } 288 289 func (p *mockPlatform) Download(url string, dir string) error { 290 args := p.Called(url, dir) 291 return args.Error(0) 292 } 293 294 type mockPrompter struct { 295 mock.Mock 296 } 297 298 func newMockPrompter(t *testing.T, config func(*mockPrompter)) *mockPrompter { 299 m := &mockPrompter{} 300 m.Test(t) 301 t.Cleanup(func() { 302 m.AssertExpectations(t) 303 }) 304 if config != nil { 305 config(m) 306 } 307 return m 308 } 309 310 func (p *mockPrompter) Prompt(msg string, opts []string, res interface{}) error { 311 args := p.Called(msg, opts, res) 312 return args.Error(0) 313 }