github.com/fastly/cli@v1.7.2-0.20240304164155-9d0f1d77c3bf/pkg/commands/logging/ftp/ftp_integration_test.go (about) 1 package ftp_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 TestFTPCreate(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 ftp create --service-id 123 --version 1 --name log --address example.com --user anonymous --password foo@example.com --compression-codec zstd --autoclone"), 28 api: mock.API{ 29 ListVersionsFn: testutil.ListVersions, 30 CloneVersionFn: testutil.CloneVersionResult(4), 31 CreateFTPFn: createFTPOK, 32 }, 33 wantOutput: "Created FTP logging endpoint log (service 123 version 4)", 34 }, 35 { 36 args: args("logging ftp create --service-id 123 --version 1 --name log --address example.com --user anonymous --password foo@example.com --autoclone"), 37 api: mock.API{ 38 ListVersionsFn: testutil.ListVersions, 39 CloneVersionFn: testutil.CloneVersionResult(4), 40 CreateFTPFn: createFTPError, 41 }, 42 wantError: errTest.Error(), 43 }, 44 { 45 args: args("logging ftp create --service-id 123 --version 1 --name log --address example.com --user anonymous --password foo@example.com --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 TestFTPList(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 ftp list --service-id 123 --version 1"), 79 api: mock.API{ 80 ListVersionsFn: testutil.ListVersions, 81 ListFTPsFn: listFTPsOK, 82 }, 83 wantOutput: listFTPsShortOutput, 84 }, 85 { 86 args: args("logging ftp list --service-id 123 --version 1 --verbose"), 87 api: mock.API{ 88 ListVersionsFn: testutil.ListVersions, 89 ListFTPsFn: listFTPsOK, 90 }, 91 wantOutput: listFTPsVerboseOutput, 92 }, 93 { 94 args: args("logging ftp list --service-id 123 --version 1 -v"), 95 api: mock.API{ 96 ListVersionsFn: testutil.ListVersions, 97 ListFTPsFn: listFTPsOK, 98 }, 99 wantOutput: listFTPsVerboseOutput, 100 }, 101 { 102 args: args("logging ftp --verbose list --service-id 123 --version 1"), 103 api: mock.API{ 104 ListVersionsFn: testutil.ListVersions, 105 ListFTPsFn: listFTPsOK, 106 }, 107 wantOutput: listFTPsVerboseOutput, 108 }, 109 { 110 args: args("logging -v ftp list --service-id 123 --version 1"), 111 api: mock.API{ 112 ListVersionsFn: testutil.ListVersions, 113 ListFTPsFn: listFTPsOK, 114 }, 115 wantOutput: listFTPsVerboseOutput, 116 }, 117 { 118 args: args("logging ftp list --service-id 123 --version 1"), 119 api: mock.API{ 120 ListVersionsFn: testutil.ListVersions, 121 ListFTPsFn: listFTPsError, 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 TestFTPDescribe(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 ftp describe --service-id 123 --version 1"), 152 wantError: "error parsing arguments: required flag --name not provided", 153 }, 154 { 155 args: args("logging ftp describe --service-id 123 --version 1 --name logs"), 156 api: mock.API{ 157 ListVersionsFn: testutil.ListVersions, 158 GetFTPFn: getFTPError, 159 }, 160 wantError: errTest.Error(), 161 }, 162 { 163 args: args("logging ftp describe --service-id 123 --version 1 --name logs"), 164 api: mock.API{ 165 ListVersionsFn: testutil.ListVersions, 166 GetFTPFn: getFTPOK, 167 }, 168 wantOutput: describeFTPOutput, 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 TestFTPUpdate(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 ftp 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 ftp 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 UpdateFTPFn: updateFTPError, 205 }, 206 wantError: errTest.Error(), 207 }, 208 { 209 args: args("logging ftp 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 UpdateFTPFn: updateFTPOK, 214 }, 215 wantOutput: "Updated FTP 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 TestFTPDelete(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 ftp delete --service-id 123 --version 1"), 244 wantError: "error parsing arguments: required flag --name not provided", 245 }, 246 { 247 args: args("logging ftp delete --service-id 123 --version 1 --name logs --autoclone"), 248 api: mock.API{ 249 ListVersionsFn: testutil.ListVersions, 250 CloneVersionFn: testutil.CloneVersionResult(4), 251 DeleteFTPFn: deleteFTPError, 252 }, 253 wantError: errTest.Error(), 254 }, 255 { 256 args: args("logging ftp delete --service-id 123 --version 1 --name logs --autoclone"), 257 api: mock.API{ 258 ListVersionsFn: testutil.ListVersions, 259 CloneVersionFn: testutil.CloneVersionResult(4), 260 DeleteFTPFn: deleteFTPOK, 261 }, 262 wantOutput: "Deleted FTP 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 createFTPOK(i *fastly.CreateFTPInput) (*fastly.FTP, error) { 284 return &fastly.FTP{ 285 ServiceID: fastly.ToPointer(i.ServiceID), 286 ServiceVersion: fastly.ToPointer(i.ServiceVersion), 287 Name: i.Name, 288 CompressionCodec: i.CompressionCodec, 289 }, nil 290 } 291 292 func createFTPError(_ *fastly.CreateFTPInput) (*fastly.FTP, error) { 293 return nil, errTest 294 } 295 296 func listFTPsOK(i *fastly.ListFTPsInput) ([]*fastly.FTP, error) { 297 return []*fastly.FTP{ 298 { 299 ServiceID: fastly.ToPointer(i.ServiceID), 300 ServiceVersion: fastly.ToPointer(i.ServiceVersion), 301 Name: fastly.ToPointer("logs"), 302 Address: fastly.ToPointer("example.com"), 303 Port: fastly.ToPointer(123), 304 Username: fastly.ToPointer("anonymous"), 305 Password: fastly.ToPointer("foo@example.com"), 306 PublicKey: fastly.ToPointer(pgpPublicKey()), 307 Path: fastly.ToPointer("logs/"), 308 Period: fastly.ToPointer(3600), 309 GzipLevel: fastly.ToPointer(9), 310 Format: fastly.ToPointer(`%h %l %u %t "%r" %>s %b`), 311 FormatVersion: fastly.ToPointer(2), 312 ResponseCondition: fastly.ToPointer("Prevent default logging"), 313 TimestampFormat: fastly.ToPointer("%Y-%m-%dT%H:%M:%S.000"), 314 Placement: fastly.ToPointer("none"), 315 CompressionCodec: fastly.ToPointer("zstd"), 316 }, 317 { 318 ServiceID: fastly.ToPointer(i.ServiceID), 319 ServiceVersion: fastly.ToPointer(i.ServiceVersion), 320 Name: fastly.ToPointer("analytics"), 321 Address: fastly.ToPointer("127.0.0.1"), 322 Port: fastly.ToPointer(456), 323 Username: fastly.ToPointer("foo"), 324 Password: fastly.ToPointer("password"), 325 PublicKey: fastly.ToPointer(pgpPublicKey()), 326 Path: fastly.ToPointer("logs/"), 327 Period: fastly.ToPointer(86400), 328 GzipLevel: fastly.ToPointer(9), 329 Format: fastly.ToPointer(`%h %l %u %t "%r" %>s %b`), 330 FormatVersion: fastly.ToPointer(2), 331 ResponseCondition: fastly.ToPointer("Prevent default logging"), 332 TimestampFormat: fastly.ToPointer("%Y-%m-%dT%H:%M:%S.000"), 333 Placement: fastly.ToPointer("none"), 334 CompressionCodec: fastly.ToPointer("zstd"), 335 }, 336 }, nil 337 } 338 339 func listFTPsError(_ *fastly.ListFTPsInput) ([]*fastly.FTP, error) { 340 return nil, errTest 341 } 342 343 var listFTPsShortOutput = strings.TrimSpace(` 344 SERVICE VERSION NAME 345 123 1 logs 346 123 1 analytics 347 `) + "\n" 348 349 var listFTPsVerboseOutput = strings.TrimSpace(` 350 Fastly API endpoint: https://api.fastly.com 351 Fastly API token provided via config file (profile: user) 352 353 Service ID (via --service-id): 123 354 355 Version: 1 356 FTP 1/2 357 Service ID: 123 358 Version: 1 359 Name: logs 360 Address: example.com 361 Port: 123 362 Username: anonymous 363 Password: foo@example.com 364 Public key: `+pgpPublicKey()+` 365 Path: logs/ 366 Period: 3600 367 GZip level: 9 368 Format: %h %l %u %t "%r" %>s %b 369 Format version: 2 370 Response condition: Prevent default logging 371 Timestamp format: %Y-%m-%dT%H:%M:%S.000 372 Placement: none 373 Compression codec: zstd 374 FTP 2/2 375 Service ID: 123 376 Version: 1 377 Name: analytics 378 Address: 127.0.0.1 379 Port: 456 380 Username: foo 381 Password: password 382 Public key: `+pgpPublicKey()+` 383 Path: logs/ 384 Period: 86400 385 GZip level: 9 386 Format: %h %l %u %t "%r" %>s %b 387 Format version: 2 388 Response condition: Prevent default logging 389 Timestamp format: %Y-%m-%dT%H:%M:%S.000 390 Placement: none 391 Compression codec: zstd 392 `) + "\n\n" 393 394 func getFTPOK(i *fastly.GetFTPInput) (*fastly.FTP, error) { 395 return &fastly.FTP{ 396 ServiceID: fastly.ToPointer(i.ServiceID), 397 ServiceVersion: fastly.ToPointer(i.ServiceVersion), 398 Name: fastly.ToPointer("logs"), 399 Address: fastly.ToPointer("example.com"), 400 Port: fastly.ToPointer(123), 401 Username: fastly.ToPointer("anonymous"), 402 Password: fastly.ToPointer("foo@example.com"), 403 PublicKey: fastly.ToPointer(pgpPublicKey()), 404 Path: fastly.ToPointer("logs/"), 405 Period: fastly.ToPointer(3600), 406 GzipLevel: fastly.ToPointer(9), 407 Format: fastly.ToPointer(`%h %l %u %t "%r" %>s %b`), 408 FormatVersion: fastly.ToPointer(2), 409 ResponseCondition: fastly.ToPointer("Prevent default logging"), 410 TimestampFormat: fastly.ToPointer("%Y-%m-%dT%H:%M:%S.000"), 411 Placement: fastly.ToPointer("none"), 412 CompressionCodec: fastly.ToPointer("zstd"), 413 }, nil 414 } 415 416 func getFTPError(_ *fastly.GetFTPInput) (*fastly.FTP, error) { 417 return nil, errTest 418 } 419 420 var describeFTPOutput = "\n" + strings.TrimSpace(` 421 Address: example.com 422 Compression codec: zstd 423 Format: %h %l %u %t "%r" %>s %b 424 Format version: 2 425 GZip level: 9 426 Name: logs 427 Password: foo@example.com 428 Path: logs/ 429 Period: 3600 430 Placement: none 431 Port: 123 432 Public key: `+pgpPublicKey()+` 433 Response condition: Prevent default logging 434 Service ID: 123 435 Timestamp format: %Y-%m-%dT%H:%M:%S.000 436 Username: anonymous 437 Version: 1 438 `) + "\n" 439 440 func updateFTPOK(i *fastly.UpdateFTPInput) (*fastly.FTP, error) { 441 return &fastly.FTP{ 442 ServiceID: fastly.ToPointer(i.ServiceID), 443 ServiceVersion: fastly.ToPointer(i.ServiceVersion), 444 Name: fastly.ToPointer("log"), 445 Address: fastly.ToPointer("example.com"), 446 Port: fastly.ToPointer(123), 447 Username: fastly.ToPointer("anonymous"), 448 Password: fastly.ToPointer("foo@example.com"), 449 PublicKey: fastly.ToPointer(pgpPublicKey()), 450 Path: fastly.ToPointer("logs/"), 451 Period: fastly.ToPointer(3600), 452 GzipLevel: fastly.ToPointer(9), 453 Format: fastly.ToPointer(`%h %l %u %t "%r" %>s %b`), 454 FormatVersion: fastly.ToPointer(2), 455 ResponseCondition: fastly.ToPointer("Prevent default logging"), 456 TimestampFormat: fastly.ToPointer("%Y-%m-%dT%H:%M:%S.000"), 457 Placement: fastly.ToPointer("none"), 458 CompressionCodec: fastly.ToPointer("zstd"), 459 }, nil 460 } 461 462 func updateFTPError(_ *fastly.UpdateFTPInput) (*fastly.FTP, error) { 463 return nil, errTest 464 } 465 466 func deleteFTPOK(_ *fastly.DeleteFTPInput) error { 467 return nil 468 } 469 470 func deleteFTPError(_ *fastly.DeleteFTPInput) error { 471 return errTest 472 } 473 474 // pgpPublicKey returns a PEM encoded PGP public key suitable for testing. 475 func pgpPublicKey() string { 476 return strings.TrimSpace(`-----BEGIN PGP PUBLIC KEY BLOCK----- 477 mQENBFyUD8sBCACyFnB39AuuTygseek+eA4fo0cgwva6/FSjnWq7riouQee8GgQ/ 478 ibXTRyv4iVlwI12GswvMTIy7zNvs1R54i0qvsLr+IZ4GVGJqs6ZJnvQcqe3xPoR4 479 8AnBfw90o32r/LuHf6QCJXi+AEu35koNlNAvLJ2B+KACaNB7N0EeWmqpV/1V2k9p 480 lDYk+th7LcCuaFNGqKS/PrMnnMqR6VDLCjHhNx4KR79b0Twm/2qp6an3hyNRu8Gn 481 dwxpf1/BUu3JWf+LqkN4Y3mbOmSUL3MaJNvyQguUzTfS0P0uGuBDHrJCVkMZCzDB 482 89ag55jCPHyGeHBTd02gHMWzsg3WMBWvCsrzABEBAAG0JXRlcnJhZm9ybSAodGVz 483 dCkgPHRlc3RAdGVycmFmb3JtLmNvbT6JAU4EEwEIADgWIQSHYyc6Kj9l6HzQsau6 484 vFFc9jxV/wUCXJQPywIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRC6vFFc 485 9jxV/815CAClb32OxV7wG01yF97TzlyTl8TnvjMtoG29Mw4nSyg+mjM3b8N7iXm9 486 OLX59fbDAWtBSldSZE22RXd3CvlFOG/EnKBXSjBtEqfyxYSnyOPkMPBYWGL/ApkX 487 SvPYJ4LKdvipYToKFh3y9kk2gk1DcDBDyaaHvR+3rv1u3aoy7/s2EltAfDS3ZQIq 488 7/cWTLJml/lleeB/Y6rPj8xqeCYhE5ahw9gsV/Mdqatl24V9Tks30iijx0Hhw+Gx 489 kATUikMGr2GDVqoIRga5kXI7CzYff4rkc0Twn47fMHHHe/KY9M2yVnMHUXmAZwbG 490 M1cMI/NH1DjevCKdGBLcRJlhuLPKF/anuQENBFyUD8sBCADIpd7r7GuPd6n/Ikxe 491 u6h7umV6IIPoAm88xCYpTbSZiaK30Svh6Ywra9jfE2KlU9o6Y/art8ip0VJ3m07L 492 4RSfSpnzqgSwdjSq5hNour2Fo/BzYhK7yaz2AzVSbe33R0+RYhb4b/6N+bKbjwGF 493 ftCsqVFMH+PyvYkLbvxyQrHlA9woAZaNThI1ztO5rGSnGUR8xt84eup28WIFKg0K 494 UEGUcTzz+8QGAwAra+0ewPXo/AkO+8BvZjDidP417u6gpBHOJ9qYIcO9FxHeqFyu 495 YrjlrxowEgXn5wO8xuNz6Vu1vhHGDHGDsRbZF8pv1d5O+0F1G7ttZ2GRRgVBZPwi 496 kiyRABEBAAGJATYEGAEIACAWIQSHYyc6Kj9l6HzQsau6vFFc9jxV/wUCXJQPywIb 497 DAAKCRC6vFFc9jxV/9YOCACe8qmOSnKQpQfW+PqYOqo3dt7JyweTs3FkD6NT8Zml 498 dYy/vkstbTjPpX6aTvUZjkb46BVi7AOneVHpD5GBqvRsZ9iVgDYHaehmLCdKiG5L 499 3Tp90NN+QY5WDbsGmsyk6+6ZMYejb4qYfweQeduOj27aavCJdLkCYMoRKfcFYI8c 500 FaNmEfKKy/r1PO20NXEG6t9t05K/frHy6ZG8bCNYdpagfFVot47r9JaQqWlTNtIR 501 5+zkkSq/eG9BEtRij3a6cTdQbktdBzx2KBeI0PYc1vlZR0LpuFKZqY9vlE6vTGLR 502 wMfrTEOvx0NxUM3rpaCgEmuWbB1G1Hu371oyr4srrr+N 503 =28dr 504 -----END PGP PUBLIC KEY BLOCK----- 505 `) 506 }