github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/secret/list/list_test.go (about)

     1  package list
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"net/http"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/ungtb10d/cli/v2/internal/config"
    13  	"github.com/ungtb10d/cli/v2/internal/ghrepo"
    14  	"github.com/ungtb10d/cli/v2/pkg/cmd/secret/shared"
    15  	"github.com/ungtb10d/cli/v2/pkg/cmdutil"
    16  	"github.com/ungtb10d/cli/v2/pkg/httpmock"
    17  	"github.com/ungtb10d/cli/v2/pkg/iostreams"
    18  	"github.com/ungtb10d/cli/v2/test"
    19  	"github.com/google/shlex"
    20  	"github.com/stretchr/testify/assert"
    21  )
    22  
    23  func Test_NewCmdList(t *testing.T) {
    24  	tests := []struct {
    25  		name  string
    26  		cli   string
    27  		wants ListOptions
    28  	}{
    29  		{
    30  			name: "repo",
    31  			cli:  "",
    32  			wants: ListOptions{
    33  				OrgName: "",
    34  			},
    35  		},
    36  		{
    37  			name: "org",
    38  			cli:  "-oUmbrellaCorporation",
    39  			wants: ListOptions{
    40  				OrgName: "UmbrellaCorporation",
    41  			},
    42  		},
    43  		{
    44  			name: "env",
    45  			cli:  "-eDevelopment",
    46  			wants: ListOptions{
    47  				EnvName: "Development",
    48  			},
    49  		},
    50  		{
    51  			name: "user",
    52  			cli:  "-u",
    53  			wants: ListOptions{
    54  				UserSecrets: true,
    55  			},
    56  		},
    57  		{
    58  			name: "Dependabot repo",
    59  			cli:  "--app Dependabot",
    60  			wants: ListOptions{
    61  				Application: "Dependabot",
    62  			},
    63  		},
    64  		{
    65  			name: "Dependabot org",
    66  			cli:  "--app Dependabot --org UmbrellaCorporation",
    67  			wants: ListOptions{
    68  				Application: "Dependabot",
    69  				OrgName:     "UmbrellaCorporation",
    70  			},
    71  		},
    72  	}
    73  
    74  	for _, tt := range tests {
    75  		t.Run(tt.name, func(t *testing.T) {
    76  			ios, _, _, _ := iostreams.Test()
    77  			f := &cmdutil.Factory{
    78  				IOStreams: ios,
    79  			}
    80  
    81  			argv, err := shlex.Split(tt.cli)
    82  			assert.NoError(t, err)
    83  
    84  			var gotOpts *ListOptions
    85  			cmd := NewCmdList(f, func(opts *ListOptions) error {
    86  				gotOpts = opts
    87  				return nil
    88  			})
    89  			cmd.SetArgs(argv)
    90  			cmd.SetIn(&bytes.Buffer{})
    91  			cmd.SetOut(&bytes.Buffer{})
    92  			cmd.SetErr(&bytes.Buffer{})
    93  
    94  			_, err = cmd.ExecuteC()
    95  			assert.NoError(t, err)
    96  
    97  			assert.Equal(t, tt.wants.OrgName, gotOpts.OrgName)
    98  			assert.Equal(t, tt.wants.EnvName, gotOpts.EnvName)
    99  		})
   100  	}
   101  }
   102  
   103  func Test_listRun(t *testing.T) {
   104  	tests := []struct {
   105  		name    string
   106  		tty     bool
   107  		opts    *ListOptions
   108  		wantOut []string
   109  	}{
   110  		{
   111  			name: "repo tty",
   112  			tty:  true,
   113  			opts: &ListOptions{},
   114  			wantOut: []string{
   115  				"SECRET_ONE.*Updated 1988-10-11",
   116  				"SECRET_TWO.*Updated 2020-12-04",
   117  				"SECRET_THREE.*Updated 1975-11-30",
   118  			},
   119  		},
   120  		{
   121  			name: "repo not tty",
   122  			tty:  false,
   123  			opts: &ListOptions{},
   124  			wantOut: []string{
   125  				"SECRET_ONE\t1988-10-11",
   126  				"SECRET_TWO\t2020-12-04",
   127  				"SECRET_THREE\t1975-11-30",
   128  			},
   129  		},
   130  		{
   131  			name: "org tty",
   132  			tty:  true,
   133  			opts: &ListOptions{
   134  				OrgName: "UmbrellaCorporation",
   135  			},
   136  			wantOut: []string{
   137  				"SECRET_ONE.*Updated 1988-10-11.*Visible to all repositories",
   138  				"SECRET_TWO.*Updated 2020-12-04.*Visible to private repositories",
   139  				"SECRET_THREE.*Updated 1975-11-30.*Visible to 2 selected repositories",
   140  			},
   141  		},
   142  		{
   143  			name: "org not tty",
   144  			tty:  false,
   145  			opts: &ListOptions{
   146  				OrgName: "UmbrellaCorporation",
   147  			},
   148  			wantOut: []string{
   149  				"SECRET_ONE\t1988-10-11\tALL",
   150  				"SECRET_TWO\t2020-12-04\tPRIVATE",
   151  				"SECRET_THREE\t1975-11-30\tSELECTED",
   152  			},
   153  		},
   154  		{
   155  			name: "env tty",
   156  			tty:  true,
   157  			opts: &ListOptions{
   158  				EnvName: "Development",
   159  			},
   160  			wantOut: []string{
   161  				"SECRET_ONE.*Updated 1988-10-11",
   162  				"SECRET_TWO.*Updated 2020-12-04",
   163  				"SECRET_THREE.*Updated 1975-11-30",
   164  			},
   165  		},
   166  		{
   167  			name: "env not tty",
   168  			tty:  false,
   169  			opts: &ListOptions{
   170  				EnvName: "Development",
   171  			},
   172  			wantOut: []string{
   173  				"SECRET_ONE\t1988-10-11",
   174  				"SECRET_TWO\t2020-12-04",
   175  				"SECRET_THREE\t1975-11-30",
   176  			},
   177  		},
   178  		{
   179  			name: "user tty",
   180  			tty:  true,
   181  			opts: &ListOptions{
   182  				UserSecrets: true,
   183  			},
   184  			wantOut: []string{
   185  				"SECRET_ONE.*Updated 1988-10-11.*Visible to 1 selected repository",
   186  				"SECRET_TWO.*Updated 2020-12-04.*Visible to 2 selected repositories",
   187  				"SECRET_THREE.*Updated 1975-11-30.*Visible to 3 selected repositories",
   188  			},
   189  		},
   190  		{
   191  			name: "user not tty",
   192  			tty:  false,
   193  			opts: &ListOptions{
   194  				UserSecrets: true,
   195  			},
   196  			wantOut: []string{
   197  				"SECRET_ONE\t1988-10-11\t",
   198  				"SECRET_TWO\t2020-12-04\t",
   199  				"SECRET_THREE\t1975-11-30\t",
   200  			},
   201  		},
   202  		{
   203  			name: "Dependabot repo tty",
   204  			tty:  true,
   205  			opts: &ListOptions{
   206  				Application: "Dependabot",
   207  			},
   208  			wantOut: []string{
   209  				"SECRET_ONE.*Updated 1988-10-11",
   210  				"SECRET_TWO.*Updated 2020-12-04",
   211  				"SECRET_THREE.*Updated 1975-11-30",
   212  			},
   213  		},
   214  		{
   215  			name: "Dependabot repo not tty",
   216  			tty:  false,
   217  			opts: &ListOptions{
   218  				Application: "Dependabot",
   219  			},
   220  			wantOut: []string{
   221  				"SECRET_ONE\t1988-10-11",
   222  				"SECRET_TWO\t2020-12-04",
   223  				"SECRET_THREE\t1975-11-30",
   224  			},
   225  		},
   226  		{
   227  			name: "Dependabot org tty",
   228  			tty:  true,
   229  			opts: &ListOptions{
   230  				Application: "Dependabot",
   231  				OrgName:     "UmbrellaCorporation",
   232  			},
   233  			wantOut: []string{
   234  				"SECRET_ONE.*Updated 1988-10-11.*Visible to all repositories",
   235  				"SECRET_TWO.*Updated 2020-12-04.*Visible to private repositories",
   236  				"SECRET_THREE.*Updated 1975-11-30.*Visible to 2 selected repositories",
   237  			},
   238  		},
   239  		{
   240  			name: "Dependabot org not tty",
   241  			tty:  false,
   242  			opts: &ListOptions{
   243  				Application: "Dependabot",
   244  				OrgName:     "UmbrellaCorporation",
   245  			},
   246  			wantOut: []string{
   247  				"SECRET_ONE\t1988-10-11\tALL",
   248  				"SECRET_TWO\t2020-12-04\tPRIVATE",
   249  				"SECRET_THREE\t1975-11-30\tSELECTED",
   250  			},
   251  		},
   252  	}
   253  
   254  	for _, tt := range tests {
   255  		t.Run(tt.name, func(t *testing.T) {
   256  			reg := &httpmock.Registry{}
   257  
   258  			path := "repos/owner/repo/actions/secrets"
   259  			if tt.opts.EnvName != "" {
   260  				path = fmt.Sprintf("repos/owner/repo/environments/%s/secrets", tt.opts.EnvName)
   261  			}
   262  
   263  			t0, _ := time.Parse("2006-01-02", "1988-10-11")
   264  			t1, _ := time.Parse("2006-01-02", "2020-12-04")
   265  			t2, _ := time.Parse("2006-01-02", "1975-11-30")
   266  			payload := secretsPayload{}
   267  			payload.Secrets = []*Secret{
   268  				{
   269  					Name:      "SECRET_ONE",
   270  					UpdatedAt: t0,
   271  				},
   272  				{
   273  					Name:      "SECRET_TWO",
   274  					UpdatedAt: t1,
   275  				},
   276  				{
   277  					Name:      "SECRET_THREE",
   278  					UpdatedAt: t2,
   279  				},
   280  			}
   281  			if tt.opts.OrgName != "" {
   282  				payload.Secrets = []*Secret{
   283  					{
   284  						Name:       "SECRET_ONE",
   285  						UpdatedAt:  t0,
   286  						Visibility: shared.All,
   287  					},
   288  					{
   289  						Name:       "SECRET_TWO",
   290  						UpdatedAt:  t1,
   291  						Visibility: shared.Private,
   292  					},
   293  					{
   294  						Name:             "SECRET_THREE",
   295  						UpdatedAt:        t2,
   296  						Visibility:       shared.Selected,
   297  						SelectedReposURL: fmt.Sprintf("https://api.github.com/orgs/%s/actions/secrets/SECRET_THREE/repositories", tt.opts.OrgName),
   298  					},
   299  				}
   300  				path = fmt.Sprintf("orgs/%s/actions/secrets", tt.opts.OrgName)
   301  
   302  				if tt.tty {
   303  					reg.Register(
   304  						httpmock.REST("GET", fmt.Sprintf("orgs/%s/actions/secrets/SECRET_THREE/repositories", tt.opts.OrgName)),
   305  						httpmock.JSONResponse(struct {
   306  							TotalCount int `json:"total_count"`
   307  						}{2}))
   308  				}
   309  			}
   310  
   311  			if tt.opts.UserSecrets {
   312  				payload.Secrets = []*Secret{
   313  					{
   314  						Name:             "SECRET_ONE",
   315  						UpdatedAt:        t0,
   316  						Visibility:       shared.Selected,
   317  						SelectedReposURL: "https://api.github.com/user/codespaces/secrets/SECRET_ONE/repositories",
   318  					},
   319  					{
   320  						Name:             "SECRET_TWO",
   321  						UpdatedAt:        t1,
   322  						Visibility:       shared.Selected,
   323  						SelectedReposURL: "https://api.github.com/user/codespaces/secrets/SECRET_TWO/repositories",
   324  					},
   325  					{
   326  						Name:             "SECRET_THREE",
   327  						UpdatedAt:        t2,
   328  						Visibility:       shared.Selected,
   329  						SelectedReposURL: "https://api.github.com/user/codespaces/secrets/SECRET_THREE/repositories",
   330  					},
   331  				}
   332  
   333  				path = "user/codespaces/secrets"
   334  				if tt.tty {
   335  					for i, secret := range payload.Secrets {
   336  						hostLen := len("https://api.github.com/")
   337  						path := secret.SelectedReposURL[hostLen:len(secret.SelectedReposURL)]
   338  						repositoryCount := i + 1
   339  						reg.Register(
   340  							httpmock.REST("GET", path),
   341  							httpmock.JSONResponse(struct {
   342  								TotalCount int `json:"total_count"`
   343  							}{repositoryCount}))
   344  					}
   345  				}
   346  			}
   347  
   348  			if tt.opts.Application == "Dependabot" {
   349  				path = strings.Replace(path, "actions", "dependabot", 1)
   350  			}
   351  
   352  			reg.Register(httpmock.REST("GET", path), httpmock.JSONResponse(payload))
   353  
   354  			ios, _, stdout, _ := iostreams.Test()
   355  
   356  			ios.SetStdoutTTY(tt.tty)
   357  
   358  			tt.opts.IO = ios
   359  			tt.opts.BaseRepo = func() (ghrepo.Interface, error) {
   360  				return ghrepo.FromFullName("owner/repo")
   361  			}
   362  			tt.opts.HttpClient = func() (*http.Client, error) {
   363  				return &http.Client{Transport: reg}, nil
   364  			}
   365  			tt.opts.Config = func() (config.Config, error) {
   366  				return config.NewBlankConfig(), nil
   367  			}
   368  
   369  			err := listRun(tt.opts)
   370  			assert.NoError(t, err)
   371  
   372  			reg.Verify(t)
   373  
   374  			//nolint:staticcheck // prefer exact matchers over ExpectLines
   375  			test.ExpectLines(t, stdout.String(), tt.wantOut...)
   376  		})
   377  	}
   378  }
   379  
   380  func Test_getSecrets_pagination(t *testing.T) {
   381  	var requests []*http.Request
   382  	var client testClient = func(req *http.Request) (*http.Response, error) {
   383  		header := make(map[string][]string)
   384  		if len(requests) == 0 {
   385  			header["Link"] = []string{`<http://example.com/page/0>; rel="previous", <http://example.com/page/2>; rel="next"`}
   386  		}
   387  		requests = append(requests, req)
   388  		return &http.Response{
   389  			Request: req,
   390  			Body:    io.NopCloser(strings.NewReader(`{"secrets":[{},{}]}`)),
   391  			Header:  header,
   392  		}, nil
   393  	}
   394  
   395  	secrets, err := getSecrets(client, "github.com", "path/to")
   396  	assert.NoError(t, err)
   397  	assert.Equal(t, 2, len(requests))
   398  	assert.Equal(t, 4, len(secrets))
   399  	assert.Equal(t, "https://api.github.com/path/to?per_page=100", requests[0].URL.String())
   400  	assert.Equal(t, "http://example.com/page/2", requests[1].URL.String())
   401  }
   402  
   403  type testClient func(*http.Request) (*http.Response, error)
   404  
   405  func (c testClient) Do(req *http.Request) (*http.Response, error) {
   406  	return c(req)
   407  }