github.com/fastly/cli@v1.7.2-0.20240304164155-9d0f1d77c3bf/pkg/commands/vcl/custom/custom_test.go (about) 1 package custom_test 2 3 import ( 4 "bytes" 5 "io" 6 "testing" 7 8 "github.com/fastly/go-fastly/v9/fastly" 9 10 "github.com/fastly/cli/pkg/app" 11 "github.com/fastly/cli/pkg/global" 12 "github.com/fastly/cli/pkg/mock" 13 "github.com/fastly/cli/pkg/testutil" 14 ) 15 16 func TestVCLCustomCreate(t *testing.T) { 17 var content string 18 args := testutil.Args 19 scenarios := []testutil.TestScenario{ 20 { 21 Name: "validate missing --autoclone flag", 22 API: mock.API{ 23 ListVersionsFn: testutil.ListVersions, 24 }, 25 Args: args("vcl custom create --content ./testdata/example.vcl --name foo --service-id 123 --version 1"), 26 WantError: "service version 1 is not editable", 27 }, 28 { 29 Name: "validate CreateVCL API error", 30 API: mock.API{ 31 ListVersionsFn: testutil.ListVersions, 32 CreateVCLFn: func(i *fastly.CreateVCLInput) (*fastly.VCL, error) { 33 return nil, testutil.Err 34 }, 35 }, 36 Args: args("vcl custom create --content ./testdata/example.vcl --name foo --service-id 123 --version 3"), 37 WantError: testutil.Err.Error(), 38 }, 39 { 40 Name: "validate CreateVCL API success for non-main VCL", 41 API: mock.API{ 42 ListVersionsFn: testutil.ListVersions, 43 CreateVCLFn: func(i *fastly.CreateVCLInput) (*fastly.VCL, error) { 44 // Track the contents parsed 45 content = *i.Content 46 if i.Content == nil { 47 i.Content = fastly.ToPointer("") 48 } 49 if i.Main == nil { 50 b := false 51 i.Main = &b 52 } 53 if i.Name == nil { 54 i.Name = fastly.ToPointer("") 55 } 56 return &fastly.VCL{ 57 Content: i.Content, 58 Main: i.Main, 59 Name: i.Name, 60 ServiceID: fastly.ToPointer(i.ServiceID), 61 ServiceVersion: fastly.ToPointer(i.ServiceVersion), 62 }, nil 63 }, 64 }, 65 Args: args("vcl custom create --content ./testdata/example.vcl --name foo --service-id 123 --version 3"), 66 WantOutput: "Created custom VCL 'foo' (service: 123, version: 3, main: false)", 67 }, 68 { 69 Name: "validate CreateVCL API success for main VCL", 70 API: mock.API{ 71 ListVersionsFn: testutil.ListVersions, 72 CreateVCLFn: func(i *fastly.CreateVCLInput) (*fastly.VCL, error) { 73 // Track the contents parsed 74 // Track the contents parsed 75 content = *i.Content 76 if i.Content == nil { 77 i.Content = fastly.ToPointer("") 78 } 79 if i.Main == nil { 80 b := false 81 i.Main = &b 82 } 83 if i.Name == nil { 84 i.Name = fastly.ToPointer("") 85 } 86 return &fastly.VCL{ 87 Content: i.Content, 88 Main: i.Main, 89 Name: i.Name, 90 ServiceID: fastly.ToPointer(i.ServiceID), 91 ServiceVersion: fastly.ToPointer(i.ServiceVersion), 92 }, nil 93 }, 94 }, 95 Args: args("vcl custom create --content ./testdata/example.vcl --main --name foo --service-id 123 --version 3"), 96 WantOutput: "Created custom VCL 'foo' (service: 123, version: 3, main: true)", 97 }, 98 { 99 Name: "validate --autoclone results in cloned service version", 100 API: mock.API{ 101 ListVersionsFn: testutil.ListVersions, 102 CloneVersionFn: testutil.CloneVersionResult(4), 103 CreateVCLFn: func(i *fastly.CreateVCLInput) (*fastly.VCL, error) { 104 // Track the contents parsed 105 content = *i.Content 106 if i.Content == nil { 107 i.Content = fastly.ToPointer("") 108 } 109 if i.Main == nil { 110 b := false 111 i.Main = &b 112 } 113 if i.Name == nil { 114 i.Name = fastly.ToPointer("") 115 } 116 return &fastly.VCL{ 117 Content: i.Content, 118 Main: i.Main, 119 Name: i.Name, 120 ServiceID: fastly.ToPointer(i.ServiceID), 121 ServiceVersion: fastly.ToPointer(i.ServiceVersion), 122 }, nil 123 }, 124 }, 125 Args: args("vcl custom create --autoclone --content ./testdata/example.vcl --name foo --service-id 123 --version 1"), 126 WantOutput: "Created custom VCL 'foo' (service: 123, version: 4, main: false)", 127 }, 128 { 129 Name: "validate CreateVCL API success with inline VCL content", 130 API: mock.API{ 131 ListVersionsFn: testutil.ListVersions, 132 CreateVCLFn: func(i *fastly.CreateVCLInput) (*fastly.VCL, error) { 133 // Track the contents parsed 134 content = *i.Content 135 if i.Content == nil { 136 i.Content = fastly.ToPointer("") 137 } 138 if i.Main == nil { 139 b := false 140 i.Main = &b 141 } 142 if i.Name == nil { 143 i.Name = fastly.ToPointer("") 144 } 145 return &fastly.VCL{ 146 Content: i.Content, 147 Main: i.Main, 148 Name: i.Name, 149 ServiceID: fastly.ToPointer(i.ServiceID), 150 ServiceVersion: fastly.ToPointer(i.ServiceVersion), 151 }, nil 152 }, 153 }, 154 Args: args("vcl custom create --content inline_vcl --name foo --service-id 123 --version 3"), 155 WantOutput: "Created custom VCL 'foo' (service: 123, version: 3, main: false)", 156 }, 157 } 158 159 for testcaseIdx := range scenarios { 160 testcase := &scenarios[testcaseIdx] 161 t.Run(testcase.Name, func(t *testing.T) { 162 var stdout bytes.Buffer 163 app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { 164 opts := testutil.MockGlobalData(testcase.Args, &stdout) 165 opts.APIClientFactory = mock.APIClient(testcase.API) 166 return opts, nil 167 } 168 err := app.Run(testcase.Args, nil) 169 testutil.AssertErrorContains(t, err, testcase.WantError) 170 testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) 171 testutil.AssertPathContentFlag("content", testcase.WantError, testcase.Args, "example.vcl", content, t) 172 }) 173 } 174 } 175 176 func TestVCLCustomDelete(t *testing.T) { 177 args := testutil.Args 178 scenarios := []testutil.TestScenario{ 179 { 180 Name: "validate missing --name flag", 181 Args: args("vcl custom delete --version 3"), 182 WantError: "error parsing arguments: required flag --name not provided", 183 }, 184 { 185 Name: "validate missing --version flag", 186 Args: args("vcl custom delete --name foobar"), 187 WantError: "error parsing arguments: required flag --version not provided", 188 }, 189 { 190 Name: "validate missing --service-id flag", 191 Args: args("vcl custom delete --name foobar --version 3"), 192 WantError: "error reading service: no service ID found", 193 }, 194 { 195 Name: "validate missing --autoclone flag", 196 API: mock.API{ 197 ListVersionsFn: testutil.ListVersions, 198 }, 199 Args: args("vcl custom delete --name foobar --service-id 123 --version 1"), 200 WantError: "service version 1 is not editable", 201 }, 202 { 203 Name: "validate DeleteVCL API error", 204 API: mock.API{ 205 ListVersionsFn: testutil.ListVersions, 206 DeleteVCLFn: func(i *fastly.DeleteVCLInput) error { 207 return testutil.Err 208 }, 209 }, 210 Args: args("vcl custom delete --name foobar --service-id 123 --version 3"), 211 WantError: testutil.Err.Error(), 212 }, 213 { 214 Name: "validate DeleteVCL API success", 215 API: mock.API{ 216 ListVersionsFn: testutil.ListVersions, 217 DeleteVCLFn: func(i *fastly.DeleteVCLInput) error { 218 return nil 219 }, 220 }, 221 Args: args("vcl custom delete --name foobar --service-id 123 --version 3"), 222 WantOutput: "Deleted custom VCL 'foobar' (service: 123, version: 3)", 223 }, 224 { 225 Name: "validate --autoclone results in cloned service version", 226 API: mock.API{ 227 ListVersionsFn: testutil.ListVersions, 228 CloneVersionFn: testutil.CloneVersionResult(4), 229 DeleteVCLFn: func(i *fastly.DeleteVCLInput) error { 230 return nil 231 }, 232 }, 233 Args: args("vcl custom delete --autoclone --name foo --service-id 123 --version 1"), 234 WantOutput: "Deleted custom VCL 'foo' (service: 123, version: 4)", 235 }, 236 } 237 238 for testcaseIdx := range scenarios { 239 testcase := &scenarios[testcaseIdx] 240 t.Run(testcase.Name, func(t *testing.T) { 241 var stdout bytes.Buffer 242 app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { 243 opts := testutil.MockGlobalData(testcase.Args, &stdout) 244 opts.APIClientFactory = mock.APIClient(testcase.API) 245 return opts, nil 246 } 247 err := app.Run(testcase.Args, nil) 248 testutil.AssertErrorContains(t, err, testcase.WantError) 249 testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) 250 }) 251 } 252 } 253 254 func TestVCLCustomDescribe(t *testing.T) { 255 args := testutil.Args 256 scenarios := []testutil.TestScenario{ 257 { 258 Name: "validate missing --name flag", 259 Args: args("vcl custom describe --version 3"), 260 WantError: "error parsing arguments: required flag --name not provided", 261 }, 262 { 263 Name: "validate missing --version flag", 264 Args: args("vcl custom describe --name foobar"), 265 WantError: "error parsing arguments: required flag --version not provided", 266 }, 267 { 268 Name: "validate missing --service-id flag", 269 Args: args("vcl custom describe --name foobar --version 3"), 270 WantError: "error reading service: no service ID found", 271 }, 272 { 273 Name: "validate GetVCL API error", 274 API: mock.API{ 275 ListVersionsFn: testutil.ListVersions, 276 GetVCLFn: func(i *fastly.GetVCLInput) (*fastly.VCL, error) { 277 return nil, testutil.Err 278 }, 279 }, 280 Args: args("vcl custom describe --name foobar --service-id 123 --version 3"), 281 WantError: testutil.Err.Error(), 282 }, 283 { 284 Name: "validate GetVCL API success", 285 API: mock.API{ 286 ListVersionsFn: testutil.ListVersions, 287 GetVCLFn: getVCL, 288 }, 289 Args: args("vcl custom describe --name foobar --service-id 123 --version 3"), 290 WantOutput: "\nService ID: 123\nService Version: 3\n\nName: foobar\nMain: true\nContent: \n# some vcl content\n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n", 291 }, 292 { 293 Name: "validate missing --autoclone flag is OK", 294 API: mock.API{ 295 ListVersionsFn: testutil.ListVersions, 296 GetVCLFn: getVCL, 297 }, 298 Args: args("vcl custom describe --name foobar --service-id 123 --version 1"), 299 WantOutput: "\nService ID: 123\nService Version: 1\n\nName: foobar\nMain: true\nContent: \n# some vcl content\n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n", 300 }, 301 } 302 303 for testcaseIdx := range scenarios { 304 testcase := &scenarios[testcaseIdx] 305 t.Run(testcase.Name, func(t *testing.T) { 306 var stdout bytes.Buffer 307 app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { 308 opts := testutil.MockGlobalData(testcase.Args, &stdout) 309 opts.APIClientFactory = mock.APIClient(testcase.API) 310 return opts, nil 311 } 312 err := app.Run(testcase.Args, nil) 313 testutil.AssertErrorContains(t, err, testcase.WantError) 314 testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) 315 }) 316 } 317 } 318 319 func TestVCLCustomList(t *testing.T) { 320 args := testutil.Args 321 scenarios := []testutil.TestScenario{ 322 { 323 Name: "validate missing --version flag", 324 Args: args("vcl custom list"), 325 WantError: "error parsing arguments: required flag --version not provided", 326 }, 327 { 328 Name: "validate missing --service-id flag", 329 Args: args("vcl custom list --version 3"), 330 WantError: "error reading service: no service ID found", 331 }, 332 { 333 Name: "validate ListVCLs API error", 334 API: mock.API{ 335 ListVersionsFn: testutil.ListVersions, 336 ListVCLsFn: func(i *fastly.ListVCLsInput) ([]*fastly.VCL, error) { 337 return nil, testutil.Err 338 }, 339 }, 340 Args: args("vcl custom list --service-id 123 --version 3"), 341 WantError: testutil.Err.Error(), 342 }, 343 { 344 Name: "validate ListVCLs API success", 345 API: mock.API{ 346 ListVersionsFn: testutil.ListVersions, 347 ListVCLsFn: listVCLs, 348 }, 349 Args: args("vcl custom list --service-id 123 --version 3"), 350 WantOutput: "SERVICE ID VERSION NAME MAIN\n123 3 foo true\n123 3 bar false\n", 351 }, 352 { 353 Name: "validate missing --autoclone flag is OK", 354 API: mock.API{ 355 ListVersionsFn: testutil.ListVersions, 356 ListVCLsFn: listVCLs, 357 }, 358 Args: args("vcl custom list --service-id 123 --version 1"), 359 WantOutput: "SERVICE ID VERSION NAME MAIN\n123 1 foo true\n123 1 bar false\n", 360 }, 361 { 362 Name: "validate missing --verbose flag", 363 API: mock.API{ 364 ListVersionsFn: testutil.ListVersions, 365 ListVCLsFn: listVCLs, 366 }, 367 Args: args("vcl custom list --service-id 123 --verbose --version 1"), 368 WantOutput: "Fastly API endpoint: https://api.fastly.com\nFastly API token provided via config file (profile: user)\n\nService ID (via --service-id): 123\n\nService Version: 1\n\nName: foo\nMain: true\nContent: \n# some vcl content\n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n\nName: bar\nMain: false\nContent: \n# some vcl content\n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n", 369 }, 370 } 371 372 for testcaseIdx := range scenarios { 373 testcase := &scenarios[testcaseIdx] 374 t.Run(testcase.Name, func(t *testing.T) { 375 var stdout bytes.Buffer 376 app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { 377 opts := testutil.MockGlobalData(testcase.Args, &stdout) 378 opts.APIClientFactory = mock.APIClient(testcase.API) 379 return opts, nil 380 } 381 err := app.Run(testcase.Args, nil) 382 testutil.AssertErrorContains(t, err, testcase.WantError) 383 testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) 384 }) 385 } 386 } 387 388 func TestVCLCustomUpdate(t *testing.T) { 389 var content string 390 args := testutil.Args 391 scenarios := []testutil.TestScenario{ 392 { 393 Name: "validate missing --name flag", 394 Args: args("vcl custom update --version 3"), 395 WantError: "error parsing arguments: required flag --name not provided", 396 }, 397 { 398 Name: "validate missing --version flag", 399 Args: args("vcl custom update --name foobar"), 400 WantError: "error parsing arguments: required flag --version not provided", 401 }, 402 { 403 Name: "validate missing --service-id flag", 404 Args: args("vcl custom update --name foobar --version 3"), 405 WantError: "error reading service: no service ID found", 406 }, 407 { 408 Name: "validate missing --autoclone flag", 409 API: mock.API{ 410 ListVersionsFn: testutil.ListVersions, 411 }, 412 Args: args("vcl custom update --name foobar --service-id 123 --version 1"), 413 WantError: "service version 1 is not editable", 414 }, 415 { 416 Name: "validate UpdateVCL API error", 417 API: mock.API{ 418 ListVersionsFn: testutil.ListVersions, 419 UpdateVCLFn: func(i *fastly.UpdateVCLInput) (*fastly.VCL, error) { 420 return nil, testutil.Err 421 }, 422 }, 423 Args: args("vcl custom update --name foobar --new-name beepboop --service-id 123 --version 3"), 424 WantError: testutil.Err.Error(), 425 }, 426 { 427 Name: "validate UpdateVCL API success with --new-name", 428 API: mock.API{ 429 ListVersionsFn: testutil.ListVersions, 430 UpdateVCLFn: func(i *fastly.UpdateVCLInput) (*fastly.VCL, error) { 431 return &fastly.VCL{ 432 Content: fastly.ToPointer("# untouched"), 433 Main: fastly.ToPointer(true), 434 Name: i.NewName, 435 ServiceID: fastly.ToPointer(i.ServiceID), 436 ServiceVersion: fastly.ToPointer(i.ServiceVersion), 437 }, nil 438 }, 439 }, 440 Args: args("vcl custom update --name foobar --new-name beepboop --service-id 123 --version 3"), 441 WantOutput: "Updated custom VCL 'beepboop' (previously: 'foobar', service: 123, version: 3)", 442 }, 443 { 444 Name: "validate UpdateVCL API success with --content", 445 API: mock.API{ 446 ListVersionsFn: testutil.ListVersions, 447 UpdateVCLFn: func(i *fastly.UpdateVCLInput) (*fastly.VCL, error) { 448 // Track the contents parsed 449 content = *i.Content 450 451 return &fastly.VCL{ 452 Content: i.Content, 453 Main: fastly.ToPointer(true), 454 Name: fastly.ToPointer(i.Name), 455 ServiceID: fastly.ToPointer(i.ServiceID), 456 ServiceVersion: fastly.ToPointer(i.ServiceVersion), 457 }, nil 458 }, 459 }, 460 Args: args("vcl custom update --content updated --name foobar --service-id 123 --version 3"), 461 WantOutput: "Updated custom VCL 'foobar' (service: 123, version: 3)", 462 }, 463 { 464 Name: "validate --autoclone results in cloned service version", 465 API: mock.API{ 466 ListVersionsFn: testutil.ListVersions, 467 CloneVersionFn: testutil.CloneVersionResult(4), 468 UpdateVCLFn: func(i *fastly.UpdateVCLInput) (*fastly.VCL, error) { 469 // Track the contents parsed 470 content = *i.Content 471 472 return &fastly.VCL{ 473 Content: i.Content, 474 Main: fastly.ToPointer(true), 475 Name: fastly.ToPointer(i.Name), 476 ServiceID: fastly.ToPointer(i.ServiceID), 477 ServiceVersion: fastly.ToPointer(i.ServiceVersion), 478 }, nil 479 }, 480 }, 481 Args: args("vcl custom update --autoclone --content ./testdata/example.vcl --name foo --service-id 123 --version 1"), 482 WantOutput: "Updated custom VCL 'foo' (service: 123, version: 4)", 483 }, 484 } 485 486 for testcaseIdx := range scenarios { 487 testcase := &scenarios[testcaseIdx] 488 t.Run(testcase.Name, func(t *testing.T) { 489 var stdout bytes.Buffer 490 app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { 491 opts := testutil.MockGlobalData(testcase.Args, &stdout) 492 opts.APIClientFactory = mock.APIClient(testcase.API) 493 return opts, nil 494 } 495 err := app.Run(testcase.Args, nil) 496 testutil.AssertErrorContains(t, err, testcase.WantError) 497 testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) 498 testutil.AssertPathContentFlag("content", testcase.WantError, testcase.Args, "example.vcl", content, t) 499 }) 500 } 501 } 502 503 func getVCL(i *fastly.GetVCLInput) (*fastly.VCL, error) { 504 t := testutil.Date 505 506 return &fastly.VCL{ 507 Content: fastly.ToPointer("# some vcl content"), 508 Main: fastly.ToPointer(true), 509 Name: fastly.ToPointer(i.Name), 510 ServiceID: fastly.ToPointer(i.ServiceID), 511 ServiceVersion: fastly.ToPointer(i.ServiceVersion), 512 513 CreatedAt: &t, 514 DeletedAt: &t, 515 UpdatedAt: &t, 516 }, nil 517 } 518 519 func listVCLs(i *fastly.ListVCLsInput) ([]*fastly.VCL, error) { 520 t := testutil.Date 521 vs := []*fastly.VCL{ 522 { 523 Content: fastly.ToPointer("# some vcl content"), 524 Main: fastly.ToPointer(true), 525 Name: fastly.ToPointer("foo"), 526 ServiceID: fastly.ToPointer(i.ServiceID), 527 ServiceVersion: fastly.ToPointer(i.ServiceVersion), 528 529 CreatedAt: &t, 530 DeletedAt: &t, 531 UpdatedAt: &t, 532 }, 533 { 534 Content: fastly.ToPointer("# some vcl content"), 535 Main: fastly.ToPointer(false), 536 Name: fastly.ToPointer("bar"), 537 ServiceID: fastly.ToPointer(i.ServiceID), 538 ServiceVersion: fastly.ToPointer(i.ServiceVersion), 539 540 CreatedAt: &t, 541 DeletedAt: &t, 542 UpdatedAt: &t, 543 }, 544 } 545 return vs, nil 546 }