github.com/fastly/cli@v1.7.2-0.20240304164155-9d0f1d77c3bf/pkg/commands/logging/gcs/gcs_integration_test.go (about) 1 package gcs_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 TestGCSCreate(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 gcs create --service-id 123 --version 1 --name log --bucket log --user foo@example.com --secret-key foo --period 86400 --autoclone"), 28 api: mock.API{ 29 ListVersionsFn: testutil.ListVersions, 30 CloneVersionFn: testutil.CloneVersionResult(4), 31 CreateGCSFn: createGCSOK, 32 }, 33 wantOutput: "Created GCS logging endpoint log (service 123 version 4)", 34 }, 35 { 36 args: args("logging gcs create --service-id 123 --version 1 --name log --bucket log --user foo@example.com --secret-key foo --period 86400 --autoclone"), 37 api: mock.API{ 38 ListVersionsFn: testutil.ListVersions, 39 CloneVersionFn: testutil.CloneVersionResult(4), 40 CreateGCSFn: createGCSError, 41 }, 42 wantError: errTest.Error(), 43 }, 44 { 45 args: args("logging gcs create --service-id 123 --version 1 --name log --bucket log --user foo@example.com --secret-key foo --period 86400 --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 TestGCSList(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 gcs list --service-id 123 --version 1"), 79 api: mock.API{ 80 ListVersionsFn: testutil.ListVersions, 81 ListGCSsFn: listGCSsOK, 82 }, 83 wantOutput: listGCSsShortOutput, 84 }, 85 { 86 args: args("logging gcs list --service-id 123 --version 1 --verbose"), 87 api: mock.API{ 88 ListVersionsFn: testutil.ListVersions, 89 ListGCSsFn: listGCSsOK, 90 }, 91 wantOutput: listGCSsVerboseOutput, 92 }, 93 { 94 args: args("logging gcs list --service-id 123 --version 1 -v"), 95 api: mock.API{ 96 ListVersionsFn: testutil.ListVersions, 97 ListGCSsFn: listGCSsOK, 98 }, 99 wantOutput: listGCSsVerboseOutput, 100 }, 101 { 102 args: args("logging gcs --verbose list --service-id 123 --version 1"), 103 api: mock.API{ 104 ListVersionsFn: testutil.ListVersions, 105 ListGCSsFn: listGCSsOK, 106 }, 107 wantOutput: listGCSsVerboseOutput, 108 }, 109 { 110 args: args("logging -v gcs list --service-id 123 --version 1"), 111 api: mock.API{ 112 ListVersionsFn: testutil.ListVersions, 113 ListGCSsFn: listGCSsOK, 114 }, 115 wantOutput: listGCSsVerboseOutput, 116 }, 117 { 118 args: args("logging gcs list --service-id 123 --version 1"), 119 api: mock.API{ 120 ListVersionsFn: testutil.ListVersions, 121 ListGCSsFn: listGCSsError, 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 TestGCSDescribe(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 gcs describe --service-id 123 --version 1"), 152 wantError: "error parsing arguments: required flag --name not provided", 153 }, 154 { 155 args: args("logging gcs describe --service-id 123 --version 1 --name logs"), 156 api: mock.API{ 157 ListVersionsFn: testutil.ListVersions, 158 GetGCSFn: getGCSError, 159 }, 160 wantError: errTest.Error(), 161 }, 162 { 163 args: args("logging gcs describe --service-id 123 --version 1 --name logs"), 164 api: mock.API{ 165 ListVersionsFn: testutil.ListVersions, 166 GetGCSFn: getGCSOK, 167 }, 168 wantOutput: describeGCSOutput, 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 TestGCSUpdate(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 gcs 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 gcs 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 UpdateGCSFn: updateGCSError, 205 }, 206 wantError: errTest.Error(), 207 }, 208 { 209 args: args("logging gcs 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 UpdateGCSFn: updateGCSOK, 214 }, 215 wantOutput: "Updated GCS 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 TestGCSDelete(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 gcs delete --service-id 123 --version 1"), 244 wantError: "error parsing arguments: required flag --name not provided", 245 }, 246 { 247 args: args("logging gcs delete --service-id 123 --version 1 --name logs --autoclone"), 248 api: mock.API{ 249 ListVersionsFn: testutil.ListVersions, 250 CloneVersionFn: testutil.CloneVersionResult(4), 251 DeleteGCSFn: deleteGCSError, 252 }, 253 wantError: errTest.Error(), 254 }, 255 { 256 args: args("logging gcs delete --service-id 123 --version 1 --name logs --autoclone"), 257 api: mock.API{ 258 ListVersionsFn: testutil.ListVersions, 259 CloneVersionFn: testutil.CloneVersionResult(4), 260 DeleteGCSFn: deleteGCSOK, 261 }, 262 wantOutput: "Deleted GCS 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 createGCSOK(i *fastly.CreateGCSInput) (*fastly.GCS, error) { 284 return &fastly.GCS{ 285 ServiceID: fastly.ToPointer(i.ServiceID), 286 ServiceVersion: fastly.ToPointer(i.ServiceVersion), 287 Name: i.Name, 288 }, nil 289 } 290 291 func createGCSError(_ *fastly.CreateGCSInput) (*fastly.GCS, error) { 292 return nil, errTest 293 } 294 295 func listGCSsOK(i *fastly.ListGCSsInput) ([]*fastly.GCS, error) { 296 return []*fastly.GCS{ 297 { 298 ServiceID: fastly.ToPointer(i.ServiceID), 299 ServiceVersion: fastly.ToPointer(i.ServiceVersion), 300 Name: fastly.ToPointer("logs"), 301 Bucket: fastly.ToPointer("my-logs"), 302 User: fastly.ToPointer("foo@example.com"), 303 AccountName: fastly.ToPointer("me@fastly.com"), 304 SecretKey: fastly.ToPointer("-----BEGIN RSA PRIVATE KEY-----foo"), 305 Path: fastly.ToPointer("logs/"), 306 Period: fastly.ToPointer(3600), 307 GzipLevel: fastly.ToPointer(0), 308 Format: fastly.ToPointer(`%h %l %u %t "%r" %>s %b`), 309 FormatVersion: fastly.ToPointer(2), 310 MessageType: fastly.ToPointer("classic"), 311 ResponseCondition: fastly.ToPointer("Prevent default logging"), 312 TimestampFormat: fastly.ToPointer("%Y-%m-%dT%H:%M:%S.000"), 313 Placement: fastly.ToPointer("none"), 314 CompressionCodec: fastly.ToPointer("zstd"), 315 }, 316 { 317 ServiceID: fastly.ToPointer(i.ServiceID), 318 ServiceVersion: fastly.ToPointer(i.ServiceVersion), 319 Name: fastly.ToPointer("analytics"), 320 Bucket: fastly.ToPointer("analytics"), 321 User: fastly.ToPointer("foo@example.com"), 322 AccountName: fastly.ToPointer("me@fastly.com"), 323 SecretKey: fastly.ToPointer("-----BEGIN RSA PRIVATE KEY-----foo"), 324 Path: fastly.ToPointer("logs/"), 325 Period: fastly.ToPointer(86400), 326 GzipLevel: fastly.ToPointer(0), 327 Format: fastly.ToPointer(`%h %l %u %t "%r" %>s %b`), 328 FormatVersion: fastly.ToPointer(2), 329 MessageType: fastly.ToPointer("classic"), 330 ResponseCondition: fastly.ToPointer("Prevent default logging"), 331 TimestampFormat: fastly.ToPointer("%Y-%m-%dT%H:%M:%S.000"), 332 Placement: fastly.ToPointer("none"), 333 CompressionCodec: fastly.ToPointer("zstd"), 334 }, 335 }, nil 336 } 337 338 func listGCSsError(_ *fastly.ListGCSsInput) ([]*fastly.GCS, error) { 339 return nil, errTest 340 } 341 342 var listGCSsShortOutput = strings.TrimSpace(` 343 SERVICE VERSION NAME 344 123 1 logs 345 123 1 analytics 346 `) + "\n" 347 348 var listGCSsVerboseOutput = strings.TrimSpace(` 349 Fastly API endpoint: https://api.fastly.com 350 Fastly API token provided via config file (profile: user) 351 352 Service ID (via --service-id): 123 353 354 Version: 1 355 GCS 1/2 356 Service ID: 123 357 Version: 1 358 Name: logs 359 Bucket: my-logs 360 User: foo@example.com 361 Account name: me@fastly.com 362 Secret key: -----BEGIN RSA PRIVATE KEY-----foo 363 Path: logs/ 364 Period: 3600 365 GZip level: 0 366 Format: %h %l %u %t "%r" %>s %b 367 Format version: 2 368 Response condition: Prevent default logging 369 Message type: classic 370 Timestamp format: %Y-%m-%dT%H:%M:%S.000 371 Placement: none 372 Compression codec: zstd 373 GCS 2/2 374 Service ID: 123 375 Version: 1 376 Name: analytics 377 Bucket: analytics 378 User: foo@example.com 379 Account name: me@fastly.com 380 Secret key: -----BEGIN RSA PRIVATE KEY-----foo 381 Path: logs/ 382 Period: 86400 383 GZip level: 0 384 Format: %h %l %u %t "%r" %>s %b 385 Format version: 2 386 Response condition: Prevent default logging 387 Message type: classic 388 Timestamp format: %Y-%m-%dT%H:%M:%S.000 389 Placement: none 390 Compression codec: zstd 391 `) + "\n\n" 392 393 func getGCSOK(i *fastly.GetGCSInput) (*fastly.GCS, error) { 394 return &fastly.GCS{ 395 ServiceID: fastly.ToPointer(i.ServiceID), 396 ServiceVersion: fastly.ToPointer(i.ServiceVersion), 397 Name: fastly.ToPointer("logs"), 398 Bucket: fastly.ToPointer("my-logs"), 399 User: fastly.ToPointer("foo@example.com"), 400 SecretKey: fastly.ToPointer("-----BEGIN RSA PRIVATE KEY-----foo"), 401 AccountName: fastly.ToPointer("me@fastly.com"), 402 Path: fastly.ToPointer("logs/"), 403 Period: fastly.ToPointer(3600), 404 GzipLevel: fastly.ToPointer(0), 405 Format: fastly.ToPointer(`%h %l %u %t "%r" %>s %b`), 406 FormatVersion: fastly.ToPointer(2), 407 MessageType: fastly.ToPointer("classic"), 408 ResponseCondition: fastly.ToPointer("Prevent default logging"), 409 TimestampFormat: fastly.ToPointer("%Y-%m-%dT%H:%M:%S.000"), 410 Placement: fastly.ToPointer("none"), 411 CompressionCodec: fastly.ToPointer("zstd"), 412 }, nil 413 } 414 415 func getGCSError(_ *fastly.GetGCSInput) (*fastly.GCS, error) { 416 return nil, errTest 417 } 418 419 var describeGCSOutput = "\n" + strings.TrimSpace(` 420 Account name: me@fastly.com 421 Bucket: my-logs 422 Compression codec: zstd 423 Format: %h %l %u %t "%r" %>s %b 424 Format version: 2 425 GZip level: 0 426 Message type: classic 427 Name: logs 428 Path: logs/ 429 Period: 3600 430 Placement: none 431 Response condition: Prevent default logging 432 Secret key: -----BEGIN RSA PRIVATE KEY-----foo 433 Service ID: 123 434 Timestamp format: %Y-%m-%dT%H:%M:%S.000 435 User: foo@example.com 436 Version: 1 437 `) + "\n" 438 439 func updateGCSOK(i *fastly.UpdateGCSInput) (*fastly.GCS, error) { 440 return &fastly.GCS{ 441 ServiceID: fastly.ToPointer(i.ServiceID), 442 ServiceVersion: fastly.ToPointer(i.ServiceVersion), 443 Name: fastly.ToPointer("log"), 444 Bucket: fastly.ToPointer("logs"), 445 User: fastly.ToPointer("foo@example.com"), 446 SecretKey: fastly.ToPointer("-----BEGIN RSA PRIVATE KEY-----foo"), 447 Path: fastly.ToPointer("logs/"), 448 Period: fastly.ToPointer(3600), 449 GzipLevel: fastly.ToPointer(0), 450 Format: fastly.ToPointer(`%h %l %u %t "%r" %>s %b`), 451 FormatVersion: fastly.ToPointer(2), 452 ResponseCondition: fastly.ToPointer("Prevent default logging"), 453 MessageType: fastly.ToPointer("classic"), 454 TimestampFormat: fastly.ToPointer("%Y-%m-%dT%H:%M:%S.000"), 455 Placement: fastly.ToPointer("none"), 456 CompressionCodec: fastly.ToPointer("zstd"), 457 }, nil 458 } 459 460 func updateGCSError(_ *fastly.UpdateGCSInput) (*fastly.GCS, error) { 461 return nil, errTest 462 } 463 464 func deleteGCSOK(_ *fastly.DeleteGCSInput) error { 465 return nil 466 } 467 468 func deleteGCSError(_ *fastly.DeleteGCSInput) error { 469 return errTest 470 }