github.com/fastly/cli@v1.7.2-0.20240304164155-9d0f1d77c3bf/pkg/commands/logging/cloudfiles/cloudfiles_integration_test.go (about) 1 package cloudfiles_test 2 3 import ( 4 "bytes" 5 "errors" 6 "io" 7 "strings" 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 TestCloudfilesCreate(t *testing.T) { 19 args := testutil.Args 20 scenarios := []struct { 21 args []string 22 api mock.API 23 wantError string 24 wantOutput string 25 }{ 26 { 27 args: args("logging cloudfiles create --service-id 123 --version 1 --name log --user username --bucket log --access-key foo --autoclone"), 28 api: mock.API{ 29 ListVersionsFn: testutil.ListVersions, 30 CloneVersionFn: testutil.CloneVersionResult(4), 31 CreateCloudfilesFn: createCloudfilesOK, 32 }, 33 wantOutput: "Created Cloudfiles logging endpoint log (service 123 version 4)", 34 }, 35 { 36 args: args("logging cloudfiles create --service-id 123 --version 1 --name log --user username --bucket log --access-key foo --autoclone"), 37 api: mock.API{ 38 ListVersionsFn: testutil.ListVersions, 39 CloneVersionFn: testutil.CloneVersionResult(4), 40 CreateCloudfilesFn: createCloudfilesError, 41 }, 42 wantError: errTest.Error(), 43 }, 44 { 45 args: args("logging cloudfiles create --service-id 123 --version 1 --name log --user username --bucket log --access-key foo --compression-codec zstd --gzip-level 9 --autoclone"), 46 api: mock.API{ 47 ListVersionsFn: testutil.ListVersions, 48 CloneVersionFn: testutil.CloneVersionResult(4), 49 }, 50 wantError: "error parsing arguments: the --compression-codec flag is mutually exclusive with the --gzip-level flag", 51 }, 52 } 53 for testcaseIdx := range scenarios { 54 testcase := &scenarios[testcaseIdx] 55 t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { 56 var stdout bytes.Buffer 57 app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { 58 opts := testutil.MockGlobalData(testcase.args, &stdout) 59 opts.APIClientFactory = mock.APIClient(testcase.api) 60 return opts, nil 61 } 62 err := app.Run(testcase.args, nil) 63 testutil.AssertErrorContains(t, err, testcase.wantError) 64 testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) 65 }) 66 } 67 } 68 69 func TestCloudfilesList(t *testing.T) { 70 args := testutil.Args 71 scenarios := []struct { 72 args []string 73 api mock.API 74 wantError string 75 wantOutput string 76 }{ 77 { 78 args: args("logging cloudfiles list --service-id 123 --version 1"), 79 api: mock.API{ 80 ListVersionsFn: testutil.ListVersions, 81 ListCloudfilesFn: listCloudfilesOK, 82 }, 83 wantOutput: listCloudfilesShortOutput, 84 }, 85 { 86 args: args("logging cloudfiles list --service-id 123 --version 1 --verbose"), 87 api: mock.API{ 88 ListVersionsFn: testutil.ListVersions, 89 ListCloudfilesFn: listCloudfilesOK, 90 }, 91 wantOutput: listCloudfilesVerboseOutput, 92 }, 93 { 94 args: args("logging cloudfiles list --service-id 123 --version 1 -v"), 95 api: mock.API{ 96 ListVersionsFn: testutil.ListVersions, 97 ListCloudfilesFn: listCloudfilesOK, 98 }, 99 wantOutput: listCloudfilesVerboseOutput, 100 }, 101 { 102 args: args("logging cloudfiles --verbose list --service-id 123 --version 1"), 103 api: mock.API{ 104 ListVersionsFn: testutil.ListVersions, 105 ListCloudfilesFn: listCloudfilesOK, 106 }, 107 wantOutput: listCloudfilesVerboseOutput, 108 }, 109 { 110 args: args("logging -v cloudfiles list --service-id 123 --version 1"), 111 api: mock.API{ 112 ListVersionsFn: testutil.ListVersions, 113 ListCloudfilesFn: listCloudfilesOK, 114 }, 115 wantOutput: listCloudfilesVerboseOutput, 116 }, 117 { 118 args: args("logging cloudfiles list --service-id 123 --version 1"), 119 api: mock.API{ 120 ListVersionsFn: testutil.ListVersions, 121 ListCloudfilesFn: listCloudfilesError, 122 }, 123 wantError: errTest.Error(), 124 }, 125 } 126 for testcaseIdx := range scenarios { 127 testcase := &scenarios[testcaseIdx] 128 t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { 129 var stdout bytes.Buffer 130 app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { 131 opts := testutil.MockGlobalData(testcase.args, &stdout) 132 opts.APIClientFactory = mock.APIClient(testcase.api) 133 return opts, nil 134 } 135 err := app.Run(testcase.args, nil) 136 testutil.AssertErrorContains(t, err, testcase.wantError) 137 testutil.AssertString(t, testcase.wantOutput, stdout.String()) 138 }) 139 } 140 } 141 142 func TestCloudfilesDescribe(t *testing.T) { 143 args := testutil.Args 144 scenarios := []struct { 145 args []string 146 api mock.API 147 wantError string 148 wantOutput string 149 }{ 150 { 151 args: args("logging cloudfiles describe --service-id 123 --version 1"), 152 wantError: "error parsing arguments: required flag --name not provided", 153 }, 154 { 155 args: args("logging cloudfiles describe --service-id 123 --version 1 --name logs"), 156 api: mock.API{ 157 ListVersionsFn: testutil.ListVersions, 158 GetCloudfilesFn: getCloudfilesError, 159 }, 160 wantError: errTest.Error(), 161 }, 162 { 163 args: args("logging cloudfiles describe --service-id 123 --version 1 --name logs"), 164 api: mock.API{ 165 ListVersionsFn: testutil.ListVersions, 166 GetCloudfilesFn: getCloudfilesOK, 167 }, 168 wantOutput: describeCloudfilesOutput, 169 }, 170 } 171 for testcaseIdx := range scenarios { 172 testcase := &scenarios[testcaseIdx] 173 t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { 174 var stdout bytes.Buffer 175 app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { 176 opts := testutil.MockGlobalData(testcase.args, &stdout) 177 opts.APIClientFactory = mock.APIClient(testcase.api) 178 return opts, nil 179 } 180 err := app.Run(testcase.args, nil) 181 testutil.AssertErrorContains(t, err, testcase.wantError) 182 testutil.AssertString(t, testcase.wantOutput, stdout.String()) 183 }) 184 } 185 } 186 187 func TestCloudfilesUpdate(t *testing.T) { 188 args := testutil.Args 189 scenarios := []struct { 190 args []string 191 api mock.API 192 wantError string 193 wantOutput string 194 }{ 195 { 196 args: args("logging cloudfiles update --service-id 123 --version 1 --new-name log"), 197 wantError: "error parsing arguments: required flag --name not provided", 198 }, 199 { 200 args: args("logging cloudfiles update --service-id 123 --version 1 --name logs --new-name log --autoclone"), 201 api: mock.API{ 202 ListVersionsFn: testutil.ListVersions, 203 CloneVersionFn: testutil.CloneVersionResult(4), 204 UpdateCloudfilesFn: updateCloudfilesError, 205 }, 206 wantError: errTest.Error(), 207 }, 208 { 209 args: args("logging cloudfiles update --service-id 123 --version 1 --name logs --new-name log --autoclone"), 210 api: mock.API{ 211 ListVersionsFn: testutil.ListVersions, 212 CloneVersionFn: testutil.CloneVersionResult(4), 213 UpdateCloudfilesFn: updateCloudfilesOK, 214 }, 215 wantOutput: "Updated Cloudfiles logging endpoint log (service 123 version 4)", 216 }, 217 } 218 for testcaseIdx := range scenarios { 219 testcase := &scenarios[testcaseIdx] 220 t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { 221 var stdout bytes.Buffer 222 app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { 223 opts := testutil.MockGlobalData(testcase.args, &stdout) 224 opts.APIClientFactory = mock.APIClient(testcase.api) 225 return opts, nil 226 } 227 err := app.Run(testcase.args, nil) 228 testutil.AssertErrorContains(t, err, testcase.wantError) 229 testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) 230 }) 231 } 232 } 233 234 func TestCloudfilesDelete(t *testing.T) { 235 args := testutil.Args 236 scenarios := []struct { 237 args []string 238 api mock.API 239 wantError string 240 wantOutput string 241 }{ 242 { 243 args: args("logging cloudfiles delete --service-id 123 --version 1"), 244 wantError: "error parsing arguments: required flag --name not provided", 245 }, 246 { 247 args: args("logging cloudfiles delete --service-id 123 --version 1 --name logs --autoclone"), 248 api: mock.API{ 249 ListVersionsFn: testutil.ListVersions, 250 CloneVersionFn: testutil.CloneVersionResult(4), 251 DeleteCloudfilesFn: deleteCloudfilesError, 252 }, 253 wantError: errTest.Error(), 254 }, 255 { 256 args: args("logging cloudfiles delete --service-id 123 --version 1 --name logs --autoclone"), 257 api: mock.API{ 258 ListVersionsFn: testutil.ListVersions, 259 CloneVersionFn: testutil.CloneVersionResult(4), 260 DeleteCloudfilesFn: deleteCloudfilesOK, 261 }, 262 wantOutput: "Deleted Cloudfiles logging endpoint logs (service 123 version 4)", 263 }, 264 } 265 for testcaseIdx := range scenarios { 266 testcase := &scenarios[testcaseIdx] 267 t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { 268 var stdout bytes.Buffer 269 app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { 270 opts := testutil.MockGlobalData(testcase.args, &stdout) 271 opts.APIClientFactory = mock.APIClient(testcase.api) 272 return opts, nil 273 } 274 err := app.Run(testcase.args, nil) 275 testutil.AssertErrorContains(t, err, testcase.wantError) 276 testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) 277 }) 278 } 279 } 280 281 var errTest = errors.New("fixture error") 282 283 func createCloudfilesOK(i *fastly.CreateCloudfilesInput) (*fastly.Cloudfiles, error) { 284 s := fastly.Cloudfiles{ 285 ServiceID: fastly.ToPointer(i.ServiceID), 286 ServiceVersion: fastly.ToPointer(i.ServiceVersion), 287 } 288 289 if i.Name != nil { 290 s.Name = i.Name 291 } 292 293 return &s, nil 294 } 295 296 func createCloudfilesError(_ *fastly.CreateCloudfilesInput) (*fastly.Cloudfiles, error) { 297 return nil, errTest 298 } 299 300 func listCloudfilesOK(i *fastly.ListCloudfilesInput) ([]*fastly.Cloudfiles, error) { 301 return []*fastly.Cloudfiles{ 302 { 303 ServiceID: fastly.ToPointer(i.ServiceID), 304 ServiceVersion: fastly.ToPointer(i.ServiceVersion), 305 Name: fastly.ToPointer("logs"), 306 User: fastly.ToPointer("username"), 307 AccessKey: fastly.ToPointer("1234"), 308 BucketName: fastly.ToPointer("my-logs"), 309 Path: fastly.ToPointer("logs/"), 310 Region: fastly.ToPointer("ORD"), 311 Placement: fastly.ToPointer("none"), 312 Period: fastly.ToPointer(3600), 313 GzipLevel: fastly.ToPointer(9), 314 Format: fastly.ToPointer(`%h %l %u %t "%r" %>s %b`), 315 FormatVersion: fastly.ToPointer(2), 316 ResponseCondition: fastly.ToPointer("Prevent default logging"), 317 MessageType: fastly.ToPointer("classic"), 318 TimestampFormat: fastly.ToPointer("%Y-%m-%dT%H:%M:%S.000"), 319 PublicKey: fastly.ToPointer(pgpPublicKey()), 320 }, 321 { 322 ServiceID: fastly.ToPointer(i.ServiceID), 323 ServiceVersion: fastly.ToPointer(i.ServiceVersion), 324 Name: fastly.ToPointer("analytics"), 325 User: fastly.ToPointer("username"), 326 AccessKey: fastly.ToPointer("1234"), 327 BucketName: fastly.ToPointer("analytics"), 328 Path: fastly.ToPointer("logs/"), 329 Region: fastly.ToPointer("ORD"), 330 Placement: fastly.ToPointer("none"), 331 Period: fastly.ToPointer(86400), 332 GzipLevel: fastly.ToPointer(9), 333 Format: fastly.ToPointer(`%h %l %u %t "%r" %>s %b`), 334 FormatVersion: fastly.ToPointer(2), 335 ResponseCondition: fastly.ToPointer("Prevent default logging"), 336 MessageType: fastly.ToPointer("classic"), 337 TimestampFormat: fastly.ToPointer("%Y-%m-%dT%H:%M:%S.000"), 338 PublicKey: fastly.ToPointer(pgpPublicKey()), 339 }, 340 }, nil 341 } 342 343 func listCloudfilesError(_ *fastly.ListCloudfilesInput) ([]*fastly.Cloudfiles, error) { 344 return nil, errTest 345 } 346 347 var listCloudfilesShortOutput = strings.TrimSpace(` 348 SERVICE VERSION NAME 349 123 1 logs 350 123 1 analytics 351 `) + "\n" 352 353 var listCloudfilesVerboseOutput = strings.TrimSpace(` 354 Fastly API endpoint: https://api.fastly.com 355 Fastly API token provided via config file (profile: user) 356 357 Service ID (via --service-id): 123 358 359 Version: 1 360 Cloudfiles 1/2 361 Service ID: 123 362 Version: 1 363 Name: logs 364 User: username 365 Access key: 1234 366 Bucket: my-logs 367 Path: logs/ 368 Region: ORD 369 Placement: none 370 Period: 3600 371 GZip level: 9 372 Format: %h %l %u %t "%r" %>s %b 373 Format version: 2 374 Response condition: Prevent default logging 375 Message type: classic 376 Timestamp format: %Y-%m-%dT%H:%M:%S.000 377 Public key: `+pgpPublicKey()+` 378 Cloudfiles 2/2 379 Service ID: 123 380 Version: 1 381 Name: analytics 382 User: username 383 Access key: 1234 384 Bucket: analytics 385 Path: logs/ 386 Region: ORD 387 Placement: none 388 Period: 86400 389 GZip level: 9 390 Format: %h %l %u %t "%r" %>s %b 391 Format version: 2 392 Response condition: Prevent default logging 393 Message type: classic 394 Timestamp format: %Y-%m-%dT%H:%M:%S.000 395 Public key: `+pgpPublicKey()+` 396 `) + "\n\n" 397 398 func getCloudfilesOK(i *fastly.GetCloudfilesInput) (*fastly.Cloudfiles, error) { 399 return &fastly.Cloudfiles{ 400 ServiceID: fastly.ToPointer(i.ServiceID), 401 ServiceVersion: fastly.ToPointer(i.ServiceVersion), 402 Name: fastly.ToPointer("logs"), 403 User: fastly.ToPointer("username"), 404 AccessKey: fastly.ToPointer("1234"), 405 BucketName: fastly.ToPointer("my-logs"), 406 Path: fastly.ToPointer("logs/"), 407 Region: fastly.ToPointer("ORD"), 408 Placement: fastly.ToPointer("none"), 409 Period: fastly.ToPointer(3600), 410 GzipLevel: fastly.ToPointer(9), 411 Format: fastly.ToPointer(`%h %l %u %t "%r" %>s %b`), 412 FormatVersion: fastly.ToPointer(2), 413 ResponseCondition: fastly.ToPointer("Prevent default logging"), 414 MessageType: fastly.ToPointer("classic"), 415 TimestampFormat: fastly.ToPointer("%Y-%m-%dT%H:%M:%S.000"), 416 PublicKey: fastly.ToPointer(pgpPublicKey()), 417 }, nil 418 } 419 420 func getCloudfilesError(_ *fastly.GetCloudfilesInput) (*fastly.Cloudfiles, error) { 421 return nil, errTest 422 } 423 424 var describeCloudfilesOutput = "\n" + strings.TrimSpace(` 425 Access key: 1234 426 Bucket: my-logs 427 Format: %h %l %u %t "%r" %>s %b 428 Format version: 2 429 GZip level: 9 430 Message type: classic 431 Name: logs 432 Path: logs/ 433 Period: 3600 434 Placement: none 435 Public key: `+pgpPublicKey()+` 436 Region: ORD 437 Response condition: Prevent default logging 438 Service ID: 123 439 Timestamp format: %Y-%m-%dT%H:%M:%S.000 440 User: username 441 Version: 1 442 `) + "\n" 443 444 func updateCloudfilesOK(i *fastly.UpdateCloudfilesInput) (*fastly.Cloudfiles, error) { 445 return &fastly.Cloudfiles{ 446 ServiceID: fastly.ToPointer(i.ServiceID), 447 ServiceVersion: fastly.ToPointer(i.ServiceVersion), 448 Name: fastly.ToPointer("log"), 449 User: fastly.ToPointer("username"), 450 AccessKey: fastly.ToPointer("1234"), 451 BucketName: fastly.ToPointer("my-logs"), 452 Path: fastly.ToPointer("logs/"), 453 Region: fastly.ToPointer("ORD"), 454 Placement: fastly.ToPointer("none"), 455 Period: fastly.ToPointer(3600), 456 GzipLevel: fastly.ToPointer(9), 457 Format: fastly.ToPointer(`%h %l %u %t "%r" %>s %b`), 458 FormatVersion: fastly.ToPointer(2), 459 ResponseCondition: fastly.ToPointer("Prevent default logging"), 460 MessageType: fastly.ToPointer("classic"), 461 TimestampFormat: fastly.ToPointer("%Y-%m-%dT%H:%M:%S.000"), 462 PublicKey: fastly.ToPointer(pgpPublicKey()), 463 }, nil 464 } 465 466 func updateCloudfilesError(_ *fastly.UpdateCloudfilesInput) (*fastly.Cloudfiles, error) { 467 return nil, errTest 468 } 469 470 func deleteCloudfilesOK(_ *fastly.DeleteCloudfilesInput) error { 471 return nil 472 } 473 474 func deleteCloudfilesError(_ *fastly.DeleteCloudfilesInput) error { 475 return errTest 476 } 477 478 // pgpPublicKey returns a PEM encoded PGP public key suitable for testing. 479 func pgpPublicKey() string { 480 return strings.TrimSpace(`-----BEGIN PGP PUBLIC KEY BLOCK----- 481 mQENBFyUD8sBCACyFnB39AuuTygseek+eA4fo0cgwva6/FSjnWq7riouQee8GgQ/ 482 ibXTRyv4iVlwI12GswvMTIy7zNvs1R54i0qvsLr+IZ4GVGJqs6ZJnvQcqe3xPoR4 483 8AnBfw90o32r/LuHf6QCJXi+AEu35koNlNAvLJ2B+KACaNB7N0EeWmqpV/1V2k9p 484 lDYk+th7LcCuaFNGqKS/PrMnnMqR6VDLCjHhNx4KR79b0Twm/2qp6an3hyNRu8Gn 485 dwxpf1/BUu3JWf+LqkN4Y3mbOmSUL3MaJNvyQguUzTfS0P0uGuBDHrJCVkMZCzDB 486 89ag55jCPHyGeHBTd02gHMWzsg3WMBWvCsrzABEBAAG0JXRlcnJhZm9ybSAodGVz 487 dCkgPHRlc3RAdGVycmFmb3JtLmNvbT6JAU4EEwEIADgWIQSHYyc6Kj9l6HzQsau6 488 vFFc9jxV/wUCXJQPywIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRC6vFFc 489 9jxV/815CAClb32OxV7wG01yF97TzlyTl8TnvjMtoG29Mw4nSyg+mjM3b8N7iXm9 490 OLX59fbDAWtBSldSZE22RXd3CvlFOG/EnKBXSjBtEqfyxYSnyOPkMPBYWGL/ApkX 491 SvPYJ4LKdvipYToKFh3y9kk2gk1DcDBDyaaHvR+3rv1u3aoy7/s2EltAfDS3ZQIq 492 7/cWTLJml/lleeB/Y6rPj8xqeCYhE5ahw9gsV/Mdqatl24V9Tks30iijx0Hhw+Gx 493 kATUikMGr2GDVqoIRga5kXI7CzYff4rkc0Twn47fMHHHe/KY9M2yVnMHUXmAZwbG 494 M1cMI/NH1DjevCKdGBLcRJlhuLPKF/anuQENBFyUD8sBCADIpd7r7GuPd6n/Ikxe 495 u6h7umV6IIPoAm88xCYpTbSZiaK30Svh6Ywra9jfE2KlU9o6Y/art8ip0VJ3m07L 496 4RSfSpnzqgSwdjSq5hNour2Fo/BzYhK7yaz2AzVSbe33R0+RYhb4b/6N+bKbjwGF 497 ftCsqVFMH+PyvYkLbvxyQrHlA9woAZaNThI1ztO5rGSnGUR8xt84eup28WIFKg0K 498 UEGUcTzz+8QGAwAra+0ewPXo/AkO+8BvZjDidP417u6gpBHOJ9qYIcO9FxHeqFyu 499 YrjlrxowEgXn5wO8xuNz6Vu1vhHGDHGDsRbZF8pv1d5O+0F1G7ttZ2GRRgVBZPwi 500 kiyRABEBAAGJATYEGAEIACAWIQSHYyc6Kj9l6HzQsau6vFFc9jxV/wUCXJQPywIb 501 DAAKCRC6vFFc9jxV/9YOCACe8qmOSnKQpQfW+PqYOqo3dt7JyweTs3FkD6NT8Zml 502 dYy/vkstbTjPpX6aTvUZjkb46BVi7AOneVHpD5GBqvRsZ9iVgDYHaehmLCdKiG5L 503 3Tp90NN+QY5WDbsGmsyk6+6ZMYejb4qYfweQeduOj27aavCJdLkCYMoRKfcFYI8c 504 FaNmEfKKy/r1PO20NXEG6t9t05K/frHy6ZG8bCNYdpagfFVot47r9JaQqWlTNtIR 505 5+zkkSq/eG9BEtRij3a6cTdQbktdBzx2KBeI0PYc1vlZR0LpuFKZqY9vlE6vTGLR 506 wMfrTEOvx0NxUM3rpaCgEmuWbB1G1Hu371oyr4srrr+N 507 =28dr 508 -----END PGP PUBLIC KEY BLOCK----- 509 `) 510 }