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 }