github.com/secman-team/gh-api@v1.8.2/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/secman-team/gh-api/core/ghrepo"
    11  	"github.com/secman-team/gh-api/pkg/cmd/run/shared"
    12  	"github.com/secman-team/gh-api/pkg/cmdutil"
    13  	"github.com/secman-team/gh-api/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  }