github.com/cli/cli@v1.14.1-0.20210902173923-1af6a669e342/pkg/cmd/secret/set/set_test.go (about) 1 package set 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io/ioutil" 8 "net/http" 9 "testing" 10 11 "github.com/cli/cli/internal/config" 12 "github.com/cli/cli/internal/ghrepo" 13 "github.com/cli/cli/pkg/cmd/secret/shared" 14 "github.com/cli/cli/pkg/cmdutil" 15 "github.com/cli/cli/pkg/httpmock" 16 "github.com/cli/cli/pkg/iostreams" 17 "github.com/cli/cli/pkg/prompt" 18 "github.com/google/shlex" 19 "github.com/stretchr/testify/assert" 20 ) 21 22 func TestNewCmdSet(t *testing.T) { 23 tests := []struct { 24 name string 25 cli string 26 wants SetOptions 27 stdinTTY bool 28 wantsErr bool 29 }{ 30 { 31 name: "invalid visibility", 32 cli: "cool_secret --org coolOrg -v'mistyVeil'", 33 wantsErr: true, 34 }, 35 { 36 name: "invalid visibility", 37 cli: "cool_secret --org coolOrg -v'selected'", 38 wantsErr: true, 39 }, 40 { 41 name: "repos with wrong vis", 42 cli: "cool_secret --org coolOrg -v'private' -rcoolRepo", 43 wantsErr: true, 44 }, 45 { 46 name: "no name", 47 cli: "", 48 wantsErr: true, 49 }, 50 { 51 name: "multiple names", 52 cli: "cool_secret good_secret", 53 wantsErr: true, 54 }, 55 { 56 name: "visibility without org", 57 cli: "cool_secret -vall", 58 wantsErr: true, 59 }, 60 { 61 name: "repos without vis", 62 cli: "cool_secret -bs --org coolOrg -rcoolRepo", 63 wants: SetOptions{ 64 SecretName: "cool_secret", 65 Visibility: shared.Selected, 66 RepositoryNames: []string{"coolRepo"}, 67 Body: "s", 68 OrgName: "coolOrg", 69 }, 70 }, 71 { 72 name: "org with selected repo", 73 cli: "-ocoolOrg -bs -vselected -rcoolRepo cool_secret", 74 wants: SetOptions{ 75 SecretName: "cool_secret", 76 Visibility: shared.Selected, 77 RepositoryNames: []string{"coolRepo"}, 78 Body: "s", 79 OrgName: "coolOrg", 80 }, 81 }, 82 { 83 name: "org with selected repos", 84 cli: `--org=coolOrg -bs -vselected -r="coolRepo,radRepo,goodRepo" cool_secret`, 85 wants: SetOptions{ 86 SecretName: "cool_secret", 87 Visibility: shared.Selected, 88 RepositoryNames: []string{"coolRepo", "goodRepo", "radRepo"}, 89 Body: "s", 90 OrgName: "coolOrg", 91 }, 92 }, 93 { 94 name: "repo", 95 cli: `cool_secret -b"a secret"`, 96 wants: SetOptions{ 97 SecretName: "cool_secret", 98 Visibility: shared.Private, 99 Body: "a secret", 100 OrgName: "", 101 }, 102 }, 103 { 104 name: "env", 105 cli: `cool_secret -b"a secret" -eRelease`, 106 wants: SetOptions{ 107 SecretName: "cool_secret", 108 Visibility: shared.Private, 109 Body: "a secret", 110 OrgName: "", 111 EnvName: "Release", 112 }, 113 }, 114 { 115 name: "vis all", 116 cli: `cool_secret --org coolOrg -b"cool" -vall`, 117 wants: SetOptions{ 118 SecretName: "cool_secret", 119 Visibility: shared.All, 120 Body: "cool", 121 OrgName: "coolOrg", 122 }, 123 }, 124 { 125 name: "bad name prefix", 126 cli: `GITHUB_SECRET -b"cool"`, 127 wantsErr: true, 128 }, 129 { 130 name: "leading numbers in name", 131 cli: `123_SECRET -b"cool"`, 132 wantsErr: true, 133 }, 134 { 135 name: "invalid characters in name", 136 cli: `BAD-SECRET -b"cool"`, 137 wantsErr: true, 138 }, 139 } 140 141 for _, tt := range tests { 142 t.Run(tt.name, func(t *testing.T) { 143 io, _, _, _ := iostreams.Test() 144 f := &cmdutil.Factory{ 145 IOStreams: io, 146 } 147 148 io.SetStdinTTY(tt.stdinTTY) 149 150 argv, err := shlex.Split(tt.cli) 151 assert.NoError(t, err) 152 153 var gotOpts *SetOptions 154 cmd := NewCmdSet(f, func(opts *SetOptions) error { 155 gotOpts = opts 156 return nil 157 }) 158 cmd.SetArgs(argv) 159 cmd.SetIn(&bytes.Buffer{}) 160 cmd.SetOut(&bytes.Buffer{}) 161 cmd.SetErr(&bytes.Buffer{}) 162 163 _, err = cmd.ExecuteC() 164 if tt.wantsErr { 165 assert.Error(t, err) 166 return 167 } 168 assert.NoError(t, err) 169 170 assert.Equal(t, tt.wants.SecretName, gotOpts.SecretName) 171 assert.Equal(t, tt.wants.Body, gotOpts.Body) 172 assert.Equal(t, tt.wants.Visibility, gotOpts.Visibility) 173 assert.Equal(t, tt.wants.OrgName, gotOpts.OrgName) 174 assert.Equal(t, tt.wants.EnvName, gotOpts.EnvName) 175 assert.ElementsMatch(t, tt.wants.RepositoryNames, gotOpts.RepositoryNames) 176 }) 177 } 178 } 179 180 func Test_setRun_repo(t *testing.T) { 181 reg := &httpmock.Registry{} 182 183 reg.Register(httpmock.REST("GET", "repos/owner/repo/actions/secrets/public-key"), 184 httpmock.JSONResponse(PubKey{ID: "123", Key: "CDjXqf7AJBXWhMczcy+Fs7JlACEptgceysutztHaFQI="})) 185 186 reg.Register(httpmock.REST("PUT", "repos/owner/repo/actions/secrets/cool_secret"), httpmock.StatusStringResponse(201, `{}`)) 187 188 io, _, _, _ := iostreams.Test() 189 190 opts := &SetOptions{ 191 HttpClient: func() (*http.Client, error) { 192 return &http.Client{Transport: reg}, nil 193 }, 194 Config: func() (config.Config, error) { return config.NewBlankConfig(), nil }, 195 BaseRepo: func() (ghrepo.Interface, error) { 196 return ghrepo.FromFullName("owner/repo") 197 }, 198 IO: io, 199 SecretName: "cool_secret", 200 Body: "a secret", 201 // Cribbed from https://github.com/golang/crypto/commit/becbf705a91575484002d598f87d74f0002801e7 202 RandomOverride: bytes.NewReader([]byte{5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5}), 203 } 204 205 err := setRun(opts) 206 assert.NoError(t, err) 207 208 reg.Verify(t) 209 210 data, err := ioutil.ReadAll(reg.Requests[1].Body) 211 assert.NoError(t, err) 212 var payload SecretPayload 213 err = json.Unmarshal(data, &payload) 214 assert.NoError(t, err) 215 assert.Equal(t, payload.KeyID, "123") 216 assert.Equal(t, payload.EncryptedValue, "UKYUCbHd0DJemxa3AOcZ6XcsBwALG9d4bpB8ZT0gSV39vl3BHiGSgj8zJapDxgB2BwqNqRhpjC4=") 217 } 218 219 func Test_setRun_env(t *testing.T) { 220 reg := &httpmock.Registry{} 221 222 reg.Register(httpmock.REST("GET", "repos/owner/repo/environments/development/secrets/public-key"), 223 httpmock.JSONResponse(PubKey{ID: "123", Key: "CDjXqf7AJBXWhMczcy+Fs7JlACEptgceysutztHaFQI="})) 224 225 reg.Register(httpmock.REST("PUT", "repos/owner/repo/environments/development/secrets/cool_secret"), httpmock.StatusStringResponse(201, `{}`)) 226 227 io, _, _, _ := iostreams.Test() 228 229 opts := &SetOptions{ 230 HttpClient: func() (*http.Client, error) { 231 return &http.Client{Transport: reg}, nil 232 }, 233 Config: func() (config.Config, error) { return config.NewBlankConfig(), nil }, 234 BaseRepo: func() (ghrepo.Interface, error) { 235 return ghrepo.FromFullName("owner/repo") 236 }, 237 EnvName: "development", 238 IO: io, 239 SecretName: "cool_secret", 240 Body: "a secret", 241 // Cribbed from https://github.com/golang/crypto/commit/becbf705a91575484002d598f87d74f0002801e7 242 RandomOverride: bytes.NewReader([]byte{5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5}), 243 } 244 245 err := setRun(opts) 246 assert.NoError(t, err) 247 248 reg.Verify(t) 249 250 data, err := ioutil.ReadAll(reg.Requests[1].Body) 251 assert.NoError(t, err) 252 var payload SecretPayload 253 err = json.Unmarshal(data, &payload) 254 assert.NoError(t, err) 255 assert.Equal(t, payload.KeyID, "123") 256 assert.Equal(t, payload.EncryptedValue, "UKYUCbHd0DJemxa3AOcZ6XcsBwALG9d4bpB8ZT0gSV39vl3BHiGSgj8zJapDxgB2BwqNqRhpjC4=") 257 } 258 259 func Test_setRun_org(t *testing.T) { 260 tests := []struct { 261 name string 262 opts *SetOptions 263 wantVisibility shared.Visibility 264 wantRepositories []int 265 }{ 266 { 267 name: "all vis", 268 opts: &SetOptions{ 269 OrgName: "UmbrellaCorporation", 270 Visibility: shared.All, 271 }, 272 }, 273 { 274 name: "selected visibility", 275 opts: &SetOptions{ 276 OrgName: "UmbrellaCorporation", 277 Visibility: shared.Selected, 278 RepositoryNames: []string{"birkin", "wesker"}, 279 }, 280 wantRepositories: []int{1, 2}, 281 }, 282 } 283 284 for _, tt := range tests { 285 t.Run(tt.name, func(t *testing.T) { 286 reg := &httpmock.Registry{} 287 288 orgName := tt.opts.OrgName 289 290 reg.Register(httpmock.REST("GET", 291 fmt.Sprintf("orgs/%s/actions/secrets/public-key", orgName)), 292 httpmock.JSONResponse(PubKey{ID: "123", Key: "CDjXqf7AJBXWhMczcy+Fs7JlACEptgceysutztHaFQI="})) 293 294 reg.Register(httpmock.REST("PUT", 295 fmt.Sprintf("orgs/%s/actions/secrets/cool_secret", orgName)), 296 httpmock.StatusStringResponse(201, `{}`)) 297 298 if len(tt.opts.RepositoryNames) > 0 { 299 reg.Register(httpmock.GraphQL(`query MapRepositoryNames\b`), 300 httpmock.StringResponse(`{"data":{"birkin":{"databaseId":1},"wesker":{"databaseId":2}}}`)) 301 } 302 303 io, _, _, _ := iostreams.Test() 304 305 tt.opts.BaseRepo = func() (ghrepo.Interface, error) { 306 return ghrepo.FromFullName("owner/repo") 307 } 308 tt.opts.HttpClient = func() (*http.Client, error) { 309 return &http.Client{Transport: reg}, nil 310 } 311 tt.opts.Config = func() (config.Config, error) { 312 return config.NewBlankConfig(), nil 313 } 314 tt.opts.IO = io 315 tt.opts.SecretName = "cool_secret" 316 tt.opts.Body = "a secret" 317 // Cribbed from https://github.com/golang/crypto/commit/becbf705a91575484002d598f87d74f0002801e7 318 tt.opts.RandomOverride = bytes.NewReader([]byte{5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5}) 319 320 err := setRun(tt.opts) 321 assert.NoError(t, err) 322 323 reg.Verify(t) 324 325 data, err := ioutil.ReadAll(reg.Requests[len(reg.Requests)-1].Body) 326 assert.NoError(t, err) 327 var payload SecretPayload 328 err = json.Unmarshal(data, &payload) 329 assert.NoError(t, err) 330 assert.Equal(t, payload.KeyID, "123") 331 assert.Equal(t, payload.EncryptedValue, "UKYUCbHd0DJemxa3AOcZ6XcsBwALG9d4bpB8ZT0gSV39vl3BHiGSgj8zJapDxgB2BwqNqRhpjC4=") 332 assert.Equal(t, payload.Visibility, tt.opts.Visibility) 333 assert.ElementsMatch(t, payload.Repositories, tt.wantRepositories) 334 }) 335 } 336 } 337 338 func Test_getBody(t *testing.T) { 339 tests := []struct { 340 name string 341 bodyArg string 342 want string 343 stdin string 344 }{ 345 { 346 name: "literal value", 347 bodyArg: "a secret", 348 want: "a secret", 349 }, 350 { 351 name: "from stdin", 352 want: "a secret", 353 stdin: "a secret", 354 }, 355 } 356 357 for _, tt := range tests { 358 t.Run(tt.name, func(t *testing.T) { 359 io, stdin, _, _ := iostreams.Test() 360 361 io.SetStdinTTY(false) 362 363 _, err := stdin.WriteString(tt.stdin) 364 assert.NoError(t, err) 365 366 body, err := getBody(&SetOptions{ 367 Body: tt.bodyArg, 368 IO: io, 369 }) 370 assert.NoError(t, err) 371 372 assert.Equal(t, string(body), tt.want) 373 }) 374 } 375 } 376 377 func Test_getBodyPrompt(t *testing.T) { 378 io, _, _, _ := iostreams.Test() 379 380 io.SetStdinTTY(true) 381 io.SetStdoutTTY(true) 382 383 as, teardown := prompt.InitAskStubber() 384 defer teardown() 385 386 as.StubOne("cool secret") 387 388 body, err := getBody(&SetOptions{ 389 IO: io, 390 }) 391 assert.NoError(t, err) 392 assert.Equal(t, string(body), "cool secret") 393 }