cli/pkg/cmd/run/download/download_test.go
2023-05-23 00:12:44 -07:00

343 lines
8.1 KiB
Go

package download
import (
"bytes"
"io"
"net/http"
"path/filepath"
"testing"
"github.com/cli/cli/v2/internal/ghrepo"
"github.com/cli/cli/v2/internal/prompter"
"github.com/cli/cli/v2/pkg/cmd/run/shared"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/google/shlex"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
func Test_NewCmdDownload(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want DownloadOptions
wantErr string
}{
{
name: "empty",
args: "",
isTTY: true,
want: DownloadOptions{
RunID: "",
DoPrompt: true,
Names: []string(nil),
DestinationDir: ".",
},
},
{
name: "with run ID",
args: "2345",
isTTY: true,
want: DownloadOptions{
RunID: "2345",
DoPrompt: false,
Names: []string(nil),
DestinationDir: ".",
},
},
{
name: "to destination",
args: "2345 -D tmp/dest",
isTTY: true,
want: DownloadOptions{
RunID: "2345",
DoPrompt: false,
Names: []string(nil),
DestinationDir: "tmp/dest",
},
},
{
name: "repo level with names",
args: "-n one -n two",
isTTY: true,
want: DownloadOptions{
RunID: "",
DoPrompt: false,
Names: []string{"one", "two"},
DestinationDir: ".",
},
},
{
name: "repo level with patterns",
args: "-p o*e -p tw*",
isTTY: true,
want: DownloadOptions{
RunID: "",
DoPrompt: false,
FilePatterns: []string{"o*e", "tw*"},
DestinationDir: ".",
},
},
{
name: "repo level with names and patterns",
args: "-p o*e -p tw* -n three -n four",
isTTY: true,
want: DownloadOptions{
RunID: "",
DoPrompt: false,
Names: []string{"three", "four"},
FilePatterns: []string{"o*e", "tw*"},
DestinationDir: ".",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
ios.SetStdoutTTY(tt.isTTY)
ios.SetStdinTTY(tt.isTTY)
ios.SetStderrTTY(tt.isTTY)
f := &cmdutil.Factory{
IOStreams: ios,
HttpClient: func() (*http.Client, error) {
return nil, nil
},
BaseRepo: func() (ghrepo.Interface, error) {
return nil, nil
},
}
var opts *DownloadOptions
cmd := NewCmdDownload(f, func(o *DownloadOptions) error {
opts = o
return nil
})
cmd.PersistentFlags().StringP("repo", "R", "", "")
argv, err := shlex.Split(tt.args)
require.NoError(t, err)
cmd.SetArgs(argv)
cmd.SetIn(&bytes.Buffer{})
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
_, err = cmd.ExecuteC()
if tt.wantErr != "" {
require.EqualError(t, err, tt.wantErr)
return
} else {
require.NoError(t, err)
}
assert.Equal(t, tt.want.RunID, opts.RunID)
assert.Equal(t, tt.want.Names, opts.Names)
assert.Equal(t, tt.want.FilePatterns, opts.FilePatterns)
assert.Equal(t, tt.want.DestinationDir, opts.DestinationDir)
assert.Equal(t, tt.want.DoPrompt, opts.DoPrompt)
})
}
}
func Test_runDownload(t *testing.T) {
tests := []struct {
name string
opts DownloadOptions
mockAPI func(*mockPlatform)
promptStubs func(*prompter.MockPrompter)
wantErr string
}{
{
name: "download non-expired",
opts: DownloadOptions{
RunID: "2345",
DestinationDir: "./tmp",
Names: []string(nil),
},
mockAPI: func(p *mockPlatform) {
p.On("List", "2345").Return([]shared.Artifact{
{
Name: "artifact-1",
DownloadURL: "http://download.com/artifact1.zip",
Expired: false,
},
{
Name: "expired-artifact",
DownloadURL: "http://download.com/expired.zip",
Expired: true,
},
{
Name: "artifact-2",
DownloadURL: "http://download.com/artifact2.zip",
Expired: false,
},
}, nil)
p.On("Download", "http://download.com/artifact1.zip", filepath.FromSlash("tmp/artifact-1")).Return(nil)
p.On("Download", "http://download.com/artifact2.zip", filepath.FromSlash("tmp/artifact-2")).Return(nil)
},
},
{
name: "no valid artifacts",
opts: DownloadOptions{
RunID: "2345",
DestinationDir: ".",
Names: []string(nil),
},
mockAPI: func(p *mockPlatform) {
p.On("List", "2345").Return([]shared.Artifact{
{
Name: "artifact-1",
DownloadURL: "http://download.com/artifact1.zip",
Expired: true,
},
{
Name: "artifact-2",
DownloadURL: "http://download.com/artifact2.zip",
Expired: true,
},
}, nil)
},
wantErr: "no valid artifacts found to download",
},
{
name: "no name matches",
opts: DownloadOptions{
RunID: "2345",
DestinationDir: ".",
Names: []string{"artifact-3"},
},
mockAPI: func(p *mockPlatform) {
p.On("List", "2345").Return([]shared.Artifact{
{
Name: "artifact-1",
DownloadURL: "http://download.com/artifact1.zip",
Expired: false,
},
{
Name: "artifact-2",
DownloadURL: "http://download.com/artifact2.zip",
Expired: false,
},
}, nil)
},
wantErr: "no artifact matches any of the names or patterns provided",
},
{
name: "no pattern matches",
opts: DownloadOptions{
RunID: "2345",
DestinationDir: ".",
FilePatterns: []string{"artifiction-*"},
},
mockAPI: func(p *mockPlatform) {
p.On("List", "2345").Return([]shared.Artifact{
{
Name: "artifact-1",
DownloadURL: "http://download.com/artifact1.zip",
Expired: false,
},
{
Name: "artifact-2",
DownloadURL: "http://download.com/artifact2.zip",
Expired: false,
},
}, nil)
},
wantErr: "no artifact matches any of the names or patterns provided",
},
{
name: "prompt to select artifact",
opts: DownloadOptions{
RunID: "",
DoPrompt: true,
DestinationDir: ".",
Names: []string(nil),
},
mockAPI: func(p *mockPlatform) {
p.On("List", "").Return([]shared.Artifact{
{
Name: "artifact-1",
DownloadURL: "http://download.com/artifact1.zip",
Expired: false,
},
{
Name: "expired-artifact",
DownloadURL: "http://download.com/expired.zip",
Expired: true,
},
{
Name: "artifact-2",
DownloadURL: "http://download.com/artifact2.zip",
Expired: false,
},
{
Name: "artifact-2",
DownloadURL: "http://download.com/artifact2.also.zip",
Expired: false,
},
}, nil)
p.On("Download", "http://download.com/artifact2.zip", ".").Return(nil)
},
promptStubs: func(pm *prompter.MockPrompter) {
pm.RegisterMultiSelect("Select artifacts to download:", nil, []string{"artifact-1", "artifact-2"},
func(_ string, _, opts []string) ([]int, error) {
return []int{1}, nil
})
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
opts := &tt.opts
ios, _, stdout, stderr := iostreams.Test()
opts.IO = ios
opts.Platform = newMockPlatform(t, tt.mockAPI)
pm := prompter.NewMockPrompter(t)
opts.Prompter = pm
if tt.promptStubs != nil {
tt.promptStubs(pm)
}
err := runDownload(opts)
if tt.wantErr != "" {
require.EqualError(t, err, tt.wantErr)
} else {
require.NoError(t, err)
}
assert.Equal(t, "", stdout.String())
assert.Equal(t, "", stderr.String())
})
}
}
type mockPlatform struct {
mock.Mock
}
func newMockPlatform(t *testing.T, config func(*mockPlatform)) *mockPlatform {
m := &mockPlatform{}
m.Test(t)
t.Cleanup(func() {
m.AssertExpectations(t)
})
if config != nil {
config(m)
}
return m
}
func (p *mockPlatform) List(runID string) ([]shared.Artifact, error) {
args := p.Called(runID)
return args.Get(0).([]shared.Artifact), args.Error(1)
}
func (p *mockPlatform) Download(url string, dir string) error {
args := p.Called(url, dir)
return args.Error(0)
}