github.com/fastly/cli@v1.7.2-0.20240304164155-9d0f1d77c3bf/pkg/commands/authtoken/authtoken_test.go (about) 1 package authtoken_test 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "os" 8 "testing" 9 10 "github.com/fastly/go-fastly/v9/fastly" 11 12 "github.com/fastly/cli/pkg/app" 13 "github.com/fastly/cli/pkg/global" 14 "github.com/fastly/cli/pkg/mock" 15 "github.com/fastly/cli/pkg/testutil" 16 ) 17 18 func TestAuthTokenCreate(t *testing.T) { 19 args := testutil.Args 20 scenarios := []testutil.TestScenario{ 21 { 22 Name: "validate missing --password flag", 23 Args: args("auth-token create"), 24 WantError: "error parsing arguments: required flag --password not provided", 25 }, 26 { 27 Name: "validate CreateToken API error", 28 API: mock.API{ 29 CreateTokenFn: func(i *fastly.CreateTokenInput) (*fastly.Token, error) { 30 return nil, testutil.Err 31 }, 32 }, 33 Args: args("auth-token create --password secure --token 123"), 34 WantError: testutil.Err.Error(), 35 }, 36 { 37 Name: "validate CreateToken API success with no flags", 38 API: mock.API{ 39 CreateTokenFn: func(i *fastly.CreateTokenInput) (*fastly.Token, error) { 40 return &fastly.Token{ 41 ExpiresAt: &testutil.Date, 42 TokenID: fastly.ToPointer("123"), 43 Name: fastly.ToPointer("Example"), 44 Scope: fastly.ToPointer(fastly.TokenScope("foobar")), 45 AccessToken: fastly.ToPointer("123abc"), 46 }, nil 47 }, 48 }, 49 Args: args("auth-token create --password secure --token 123"), 50 WantOutput: "Created token '123abc' (name: Example, id: 123, scope: foobar, expires: 2021-06-15 23:00:00 +0000 UTC)", 51 }, 52 { 53 Name: "validate CreateToken API success with all flags", 54 API: mock.API{ 55 CreateTokenFn: func(i *fastly.CreateTokenInput) (*fastly.Token, error) { 56 return &fastly.Token{ 57 ExpiresAt: i.ExpiresAt, 58 TokenID: fastly.ToPointer("123"), 59 Name: i.Name, 60 Scope: i.Scope, 61 AccessToken: fastly.ToPointer("123abc"), 62 }, nil 63 }, 64 }, 65 Args: args("auth-token create --expires 2021-09-15T23:00:00Z --name Testing --password secure --scope purge_all --scope global:read --services a,b,c --token 123"), 66 WantOutput: "Created token '123abc' (name: Testing, id: 123, scope: purge_all global:read, expires: 2021-09-15 23:00:00 +0000 UTC)", 67 }, 68 } 69 70 for testcaseIdx := range scenarios { 71 testcase := &scenarios[testcaseIdx] 72 t.Run(testcase.Name, func(t *testing.T) { 73 var stdout bytes.Buffer 74 app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { 75 opts := testutil.MockGlobalData(testcase.Args, &stdout) 76 opts.APIClientFactory = mock.APIClient(testcase.API) 77 return opts, nil 78 } 79 err := app.Run(testcase.Args, nil) 80 testutil.AssertErrorContains(t, err, testcase.WantError) 81 testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) 82 }) 83 } 84 } 85 86 func TestAuthTokenDelete(t *testing.T) { 87 args := testutil.Args 88 scenarios := []testutil.TestScenario{ 89 { 90 Name: "validate missing optional flags", 91 Args: args("auth-token delete --token 123"), 92 WantError: "error parsing arguments: must provide either the --current, --file or --id flag", 93 }, 94 { 95 Name: "validate DeleteTokenSelf API error with --current", 96 API: mock.API{ 97 DeleteTokenSelfFn: func() error { 98 return testutil.Err 99 }, 100 }, 101 Args: args("auth-token delete --current --token 123"), 102 WantError: testutil.Err.Error(), 103 }, 104 { 105 Name: "validate BatchDeleteTokens API error with --file", 106 API: mock.API{ 107 BatchDeleteTokensFn: func(i *fastly.BatchDeleteTokensInput) error { 108 return testutil.Err 109 }, 110 }, 111 Args: args("auth-token delete --file ./testdata/tokens --token 123"), 112 WantError: testutil.Err.Error(), 113 }, 114 { 115 Name: "validate DeleteToken API error with --id", 116 API: mock.API{ 117 DeleteTokenFn: func(i *fastly.DeleteTokenInput) error { 118 return testutil.Err 119 }, 120 }, 121 Args: args("auth-token delete --id 123 --token 123"), 122 WantError: testutil.Err.Error(), 123 }, 124 { 125 Name: "validate DeleteTokenSelf API success with --current", 126 API: mock.API{ 127 DeleteTokenSelfFn: func() error { 128 return nil 129 }, 130 }, 131 Args: args("auth-token delete --current --token 123"), 132 WantOutput: "Deleted current token", 133 }, 134 { 135 Name: "validate BatchDeleteTokens API success with --file", 136 API: mock.API{ 137 BatchDeleteTokensFn: func(i *fastly.BatchDeleteTokensInput) error { 138 return nil 139 }, 140 }, 141 Args: args("auth-token delete --file ./testdata/tokens --token 123"), 142 WantOutput: "Deleted tokens", 143 }, 144 { 145 Name: "validate BatchDeleteTokens API success with --file and --verbose", 146 API: mock.API{ 147 BatchDeleteTokensFn: func(i *fastly.BatchDeleteTokensInput) error { 148 return nil 149 }, 150 }, 151 Args: args("auth-token delete --file ./testdata/tokens --token 123 --verbose"), 152 WantOutput: fileTokensOutput(), 153 }, 154 { 155 Name: "validate DeleteToken API success with --id", 156 API: mock.API{ 157 DeleteTokenFn: func(i *fastly.DeleteTokenInput) error { 158 return nil 159 }, 160 }, 161 Args: args("auth-token delete --id 123 --token 123"), 162 WantOutput: "Deleted token '123'", 163 }, 164 } 165 166 for testcaseIdx := range scenarios { 167 testcase := &scenarios[testcaseIdx] 168 t.Run(testcase.Name, func(t *testing.T) { 169 var stdout bytes.Buffer 170 app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { 171 opts := testutil.MockGlobalData(testcase.Args, &stdout) 172 opts.APIClientFactory = mock.APIClient(testcase.API) 173 return opts, nil 174 } 175 err := app.Run(testcase.Args, nil) 176 testutil.AssertErrorContains(t, err, testcase.WantError) 177 testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) 178 }) 179 } 180 } 181 182 func TestAuthTokenDescribe(t *testing.T) { 183 args := testutil.Args 184 scenarios := []testutil.TestScenario{ 185 { 186 Name: "validate GetTokenSelf API error", 187 API: mock.API{ 188 GetTokenSelfFn: func() (*fastly.Token, error) { 189 return nil, testutil.Err 190 }, 191 }, 192 Args: args("auth-token describe --token 123"), 193 WantError: testutil.Err.Error(), 194 }, 195 { 196 Name: "validate GetTokenSelf API success", 197 API: mock.API{ 198 GetTokenSelfFn: getToken, 199 }, 200 Args: args("auth-token describe --token 123"), 201 WantOutput: describeTokenOutput(), 202 }, 203 } 204 205 for testcaseIdx := range scenarios { 206 testcase := &scenarios[testcaseIdx] 207 t.Run(testcase.Name, func(t *testing.T) { 208 var stdout bytes.Buffer 209 app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { 210 opts := testutil.MockGlobalData(testcase.Args, &stdout) 211 opts.APIClientFactory = mock.APIClient(testcase.API) 212 return opts, nil 213 } 214 err := app.Run(testcase.Args, nil) 215 testutil.AssertErrorContains(t, err, testcase.WantError) 216 testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) 217 }) 218 } 219 } 220 221 func TestAuthTokenList(t *testing.T) { 222 args := testutil.Args 223 type ts struct { 224 testutil.TestScenario 225 SetEnv bool 226 } 227 scenarios := []ts{ 228 { 229 TestScenario: testutil.TestScenario{ 230 Name: "validate ListTokens API error", 231 API: mock.API{ 232 ListTokensFn: func(_ *fastly.ListTokensInput) ([]*fastly.Token, error) { 233 return nil, testutil.Err 234 }, 235 }, 236 Args: args("auth-token list"), 237 WantError: testutil.Err.Error(), 238 }, 239 }, 240 { 241 TestScenario: testutil.TestScenario{ 242 Name: "validate ListCustomerTokens API error", 243 API: mock.API{ 244 ListCustomerTokensFn: func(i *fastly.ListCustomerTokensInput) ([]*fastly.Token, error) { 245 return nil, testutil.Err 246 }, 247 }, 248 Args: args("auth-token list --customer-id 123"), 249 WantError: testutil.Err.Error(), 250 }, 251 }, 252 { 253 TestScenario: testutil.TestScenario{ 254 Name: "validate ListTokens API success", 255 API: mock.API{ 256 ListTokensFn: listTokens, 257 }, 258 Args: args("auth-token list"), 259 WantOutput: listTokenOutputSummary(false), 260 }, 261 }, 262 { 263 TestScenario: testutil.TestScenario{ 264 Name: "validate ListCustomerTokens API success", 265 API: mock.API{ 266 ListCustomerTokensFn: listCustomerTokens, 267 }, 268 Args: args("auth-token list --customer-id 123"), 269 WantOutput: listTokenOutputSummary(false), 270 }, 271 }, 272 { 273 TestScenario: testutil.TestScenario{ 274 Name: "validate ListCustomerTokens API success with env var", 275 API: mock.API{ 276 ListCustomerTokensFn: listCustomerTokens, 277 }, 278 Args: args("auth-token list"), 279 WantOutput: listTokenOutputSummary(true), 280 }, 281 SetEnv: true, 282 }, 283 { 284 TestScenario: testutil.TestScenario{ 285 Name: "validate --verbose flag", 286 API: mock.API{ 287 ListTokensFn: listTokens, 288 }, 289 Args: args("auth-token list --verbose"), 290 WantOutput: listTokenOutputVerbose(), 291 }, 292 }, 293 } 294 295 for testcaseIdx := range scenarios { 296 testcase := &scenarios[testcaseIdx] 297 t.Run(testcase.Name, func(t *testing.T) { 298 if testcase.SetEnv { 299 if err := os.Setenv("FASTLY_CUSTOMER_ID", "123"); err != nil { 300 t.Fatal(err) 301 } 302 defer func() { 303 if err := os.Unsetenv("FASTLY_CUSTOMER_ID"); err != nil { 304 t.Fatal(err) 305 } 306 }() 307 } 308 var stdout bytes.Buffer 309 app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { 310 opts := testutil.MockGlobalData(testcase.Args, &stdout) 311 opts.APIClientFactory = mock.APIClient(testcase.API) 312 return opts, nil 313 } 314 err := app.Run(testcase.Args, nil) 315 testutil.AssertErrorContains(t, err, testcase.WantError) 316 testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) 317 }) 318 } 319 } 320 321 func getToken() (*fastly.Token, error) { 322 t := testutil.Date 323 324 return &fastly.Token{ 325 TokenID: fastly.ToPointer("123"), 326 Name: fastly.ToPointer("Foo"), 327 UserID: fastly.ToPointer("456"), 328 Services: []string{"a", "b"}, 329 Scope: fastly.ToPointer(fastly.TokenScope(fmt.Sprintf("%s %s", fastly.PurgeAllScope, fastly.GlobalReadScope))), 330 IP: fastly.ToPointer("127.0.0.1"), 331 CreatedAt: &t, 332 ExpiresAt: &t, 333 LastUsedAt: &t, 334 }, nil 335 } 336 337 func listTokens(_ *fastly.ListTokensInput) ([]*fastly.Token, error) { 338 t := testutil.Date 339 token, _ := getToken() 340 vs := []*fastly.Token{ 341 token, 342 { 343 TokenID: fastly.ToPointer("456"), 344 Name: fastly.ToPointer("Bar"), 345 UserID: fastly.ToPointer("789"), 346 Services: []string{"a", "b"}, 347 Scope: fastly.ToPointer(fastly.GlobalScope), 348 IP: fastly.ToPointer("127.0.0.2"), 349 CreatedAt: &t, 350 ExpiresAt: &t, 351 LastUsedAt: &t, 352 }, 353 } 354 return vs, nil 355 } 356 357 func listCustomerTokens(_ *fastly.ListCustomerTokensInput) ([]*fastly.Token, error) { 358 return listTokens(nil) 359 } 360 361 func fileTokensOutput() string { 362 return `Deleted tokens 363 364 TOKEN ID 365 abc 366 def 367 xyz` 368 } 369 370 func describeTokenOutput() string { 371 return ` 372 ID: 123 373 Name: Foo 374 User ID: 456 375 Services: a, b 376 Scope: purge_all global:read 377 IP: 127.0.0.1 378 379 Created at: 2021-06-15 23:00:00 +0000 UTC 380 Last used at: 2021-06-15 23:00:00 +0000 UTC 381 Expires at: 2021-06-15 23:00:00 +0000 UTC` 382 } 383 384 func listTokenOutputVerbose() string { 385 return `Fastly API endpoint: https://api.fastly.com 386 Fastly API token provided via config file (profile: user) 387 388 389 ID: 123 390 Name: Foo 391 User ID: 456 392 Services: a, b 393 Scope: purge_all global:read 394 IP: 127.0.0.1 395 396 Created at: 2021-06-15 23:00:00 +0000 UTC 397 Last used at: 2021-06-15 23:00:00 +0000 UTC 398 Expires at: 2021-06-15 23:00:00 +0000 UTC 399 400 ID: 456 401 Name: Bar 402 User ID: 789 403 Services: a, b 404 Scope: global 405 IP: 127.0.0.2 406 407 Created at: 2021-06-15 23:00:00 +0000 UTC 408 Last used at: 2021-06-15 23:00:00 +0000 UTC 409 Expires at: 2021-06-15 23:00:00 +0000 UTC 410 411 ` 412 } 413 414 func listTokenOutputSummary(env bool) string { 415 var msg string 416 if env { 417 msg = "INFO: Listing customer tokens for the FASTLY_CUSTOMER_ID environment variable\n\n" 418 } 419 return fmt.Sprintf(`%sNAME TOKEN ID USER ID SCOPE SERVICES 420 Foo 123 456 purge_all global:read a, b 421 Bar 456 789 global a, b`, msg) 422 }