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  }