github.com/fastly/cli@v1.7.2-0.20240304164155-9d0f1d77c3bf/pkg/commands/secretstore/secretstore_test.go (about)

     1  package secretstore_test
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/fastly/go-fastly/v9/fastly"
    12  
    13  	"github.com/fastly/cli/pkg/app"
    14  	"github.com/fastly/cli/pkg/commands/secretstore"
    15  	fstfmt "github.com/fastly/cli/pkg/fmt"
    16  	"github.com/fastly/cli/pkg/global"
    17  	"github.com/fastly/cli/pkg/mock"
    18  	"github.com/fastly/cli/pkg/testutil"
    19  )
    20  
    21  func TestCreateStoreCommand(t *testing.T) {
    22  	const (
    23  		storeName = "test123"
    24  		storeID   = "store-id-123"
    25  	)
    26  	now := time.Now()
    27  
    28  	scenarios := []struct {
    29  		args           string
    30  		api            mock.API
    31  		wantAPIInvoked bool
    32  		wantError      string
    33  		wantOutput     string
    34  	}{
    35  		{
    36  			args:      "create",
    37  			wantError: "error parsing arguments: required flag --name not provided",
    38  		},
    39  		{
    40  			args: fmt.Sprintf("create --name %s", storeName),
    41  			api: mock.API{
    42  				CreateSecretStoreFn: func(i *fastly.CreateSecretStoreInput) (*fastly.SecretStore, error) {
    43  					return nil, errors.New("invalid request")
    44  				},
    45  			},
    46  			wantAPIInvoked: true,
    47  			wantError:      "invalid request",
    48  		},
    49  		{
    50  			args: fmt.Sprintf("create --name %s", storeName),
    51  			api: mock.API{
    52  				CreateSecretStoreFn: func(i *fastly.CreateSecretStoreInput) (*fastly.SecretStore, error) {
    53  					return &fastly.SecretStore{
    54  						StoreID: storeID,
    55  						Name:    i.Name,
    56  					}, nil
    57  				},
    58  			},
    59  			wantAPIInvoked: true,
    60  			wantOutput:     fstfmt.Success("Created Secret Store '%s' (%s)", storeName, storeID),
    61  		},
    62  		{
    63  			args: fmt.Sprintf("create --name %s --json", storeName),
    64  			api: mock.API{
    65  				CreateSecretStoreFn: func(i *fastly.CreateSecretStoreInput) (*fastly.SecretStore, error) {
    66  					return &fastly.SecretStore{
    67  						StoreID:   storeID,
    68  						Name:      i.Name,
    69  						CreatedAt: now,
    70  					}, nil
    71  				},
    72  			},
    73  			wantAPIInvoked: true,
    74  			wantOutput:     fstfmt.JSON(`{"created_at": %q, "name": %q, "id": %q}`, now.Format(time.RFC3339Nano), storeName, storeID),
    75  		},
    76  	}
    77  
    78  	for _, testcase := range scenarios {
    79  		testcase := testcase
    80  		t.Run(testcase.args, func(t *testing.T) {
    81  			var stdout bytes.Buffer
    82  			args := testutil.Args(secretstore.RootNameStore + " " + testcase.args)
    83  			opts := testutil.MockGlobalData(args, &stdout)
    84  
    85  			f := testcase.api.CreateSecretStoreFn
    86  			var apiInvoked bool
    87  			testcase.api.CreateSecretStoreFn = func(i *fastly.CreateSecretStoreInput) (*fastly.SecretStore, error) {
    88  				apiInvoked = true
    89  				return f(i)
    90  			}
    91  
    92  			app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
    93  				opts.APIClientFactory = mock.APIClient(testcase.api)
    94  				return opts, nil
    95  			}
    96  			err := app.Run(args, nil)
    97  
    98  			testutil.AssertErrorContains(t, err, testcase.wantError)
    99  			testutil.AssertString(t, testcase.wantOutput, stdout.String())
   100  			if apiInvoked != testcase.wantAPIInvoked {
   101  				t.Fatalf("API CreateSecretStore invoked = %v, want %v", apiInvoked, testcase.wantAPIInvoked)
   102  			}
   103  		})
   104  	}
   105  }
   106  
   107  func TestDeleteStoreCommand(t *testing.T) {
   108  	const storeID = "test123"
   109  	errStoreNotFound := errors.New("store not found")
   110  
   111  	scenarios := []struct {
   112  		args           string
   113  		api            mock.API
   114  		wantAPIInvoked bool
   115  		wantError      string
   116  		wantOutput     string
   117  	}{
   118  		{
   119  			args:      "delete",
   120  			wantError: "error parsing arguments: required flag --store-id not provided",
   121  		},
   122  		{
   123  			args: "delete --store-id DOES-NOT-EXIST",
   124  			api: mock.API{
   125  				DeleteSecretStoreFn: func(i *fastly.DeleteSecretStoreInput) error {
   126  					if i.StoreID != storeID {
   127  						return errStoreNotFound
   128  					}
   129  					return nil
   130  				},
   131  			},
   132  			wantAPIInvoked: true,
   133  			wantError:      errStoreNotFound.Error(),
   134  		},
   135  		{
   136  			args: fmt.Sprintf("delete --store-id %s", storeID),
   137  			api: mock.API{
   138  				DeleteSecretStoreFn: func(i *fastly.DeleteSecretStoreInput) error {
   139  					if i.StoreID != storeID {
   140  						return errStoreNotFound
   141  					}
   142  					return nil
   143  				},
   144  			},
   145  			wantAPIInvoked: true,
   146  			wantOutput:     fstfmt.Success("Deleted Secret Store '%s'\n", storeID),
   147  		},
   148  		{
   149  			args: fmt.Sprintf("delete --store-id %s --json", storeID),
   150  			api: mock.API{
   151  				DeleteSecretStoreFn: func(i *fastly.DeleteSecretStoreInput) error {
   152  					if i.StoreID != storeID {
   153  						return errStoreNotFound
   154  					}
   155  					return nil
   156  				},
   157  			},
   158  			wantAPIInvoked: true,
   159  			wantOutput:     fstfmt.JSON(`{"id": %q, "deleted": true}`, storeID),
   160  		},
   161  	}
   162  
   163  	for _, testcase := range scenarios {
   164  		testcase := testcase
   165  		t.Run(testcase.args, func(t *testing.T) {
   166  			var stdout bytes.Buffer
   167  			args := testutil.Args(secretstore.RootNameStore + " " + testcase.args)
   168  			opts := testutil.MockGlobalData(args, &stdout)
   169  
   170  			f := testcase.api.DeleteSecretStoreFn
   171  			var apiInvoked bool
   172  			testcase.api.DeleteSecretStoreFn = func(i *fastly.DeleteSecretStoreInput) error {
   173  				apiInvoked = true
   174  				return f(i)
   175  			}
   176  
   177  			app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
   178  				opts.APIClientFactory = mock.APIClient(testcase.api)
   179  				return opts, nil
   180  			}
   181  			err := app.Run(args, nil)
   182  
   183  			testutil.AssertErrorContains(t, err, testcase.wantError)
   184  			testutil.AssertString(t, testcase.wantOutput, stdout.String())
   185  			if apiInvoked != testcase.wantAPIInvoked {
   186  				t.Fatalf("API DeleteSecretStore invoked = %v, want %v", apiInvoked, testcase.wantAPIInvoked)
   187  			}
   188  		})
   189  	}
   190  }
   191  
   192  func TestDescribeStoreCommand(t *testing.T) {
   193  	const (
   194  		storeName = "test123"
   195  		storeID   = "store-id-123"
   196  	)
   197  
   198  	scenarios := []struct {
   199  		args           string
   200  		api            mock.API
   201  		wantAPIInvoked bool
   202  		wantError      string
   203  		wantOutput     string
   204  	}{
   205  		{
   206  			args:      "get",
   207  			wantError: "error parsing arguments: required flag --store-id not provided",
   208  		},
   209  		{
   210  			args: fmt.Sprintf("get --store-id %s", storeID),
   211  			api: mock.API{
   212  				GetSecretStoreFn: func(i *fastly.GetSecretStoreInput) (*fastly.SecretStore, error) {
   213  					return nil, errors.New("invalid request")
   214  				},
   215  			},
   216  			wantAPIInvoked: true,
   217  			wantError:      "invalid request",
   218  		},
   219  		{
   220  			args: fmt.Sprintf("get --store-id %s", storeID),
   221  			api: mock.API{
   222  				GetSecretStoreFn: func(i *fastly.GetSecretStoreInput) (*fastly.SecretStore, error) {
   223  					return &fastly.SecretStore{
   224  						StoreID: i.StoreID,
   225  						Name:    storeName,
   226  					}, nil
   227  				},
   228  			},
   229  			wantAPIInvoked: true,
   230  			wantOutput: fmtStore(&fastly.SecretStore{
   231  				StoreID: storeID,
   232  				Name:    storeName,
   233  			}),
   234  		},
   235  		{
   236  			args: fmt.Sprintf("get --store-id %s --json", storeID),
   237  			api: mock.API{
   238  				GetSecretStoreFn: func(i *fastly.GetSecretStoreInput) (*fastly.SecretStore, error) {
   239  					return &fastly.SecretStore{
   240  						StoreID: i.StoreID,
   241  						Name:    storeName,
   242  					}, nil
   243  				},
   244  			},
   245  			wantAPIInvoked: true,
   246  			wantOutput: fstfmt.EncodeJSON(&fastly.SecretStore{
   247  				StoreID: storeID,
   248  				Name:    storeName,
   249  			}),
   250  		},
   251  	}
   252  
   253  	for _, testcase := range scenarios {
   254  		testcase := testcase
   255  		t.Run(testcase.args, func(t *testing.T) {
   256  			var stdout bytes.Buffer
   257  			args := testutil.Args(secretstore.RootNameStore + " " + testcase.args)
   258  			opts := testutil.MockGlobalData(args, &stdout)
   259  
   260  			f := testcase.api.GetSecretStoreFn
   261  			var apiInvoked bool
   262  			testcase.api.GetSecretStoreFn = func(i *fastly.GetSecretStoreInput) (*fastly.SecretStore, error) {
   263  				apiInvoked = true
   264  				return f(i)
   265  			}
   266  
   267  			app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
   268  				opts.APIClientFactory = mock.APIClient(testcase.api)
   269  				return opts, nil
   270  			}
   271  			err := app.Run(args, nil)
   272  
   273  			testutil.AssertErrorContains(t, err, testcase.wantError)
   274  			testutil.AssertString(t, testcase.wantOutput, stdout.String())
   275  			if apiInvoked != testcase.wantAPIInvoked {
   276  				t.Fatalf("API GetSecretStore invoked = %v, want %v", apiInvoked, testcase.wantAPIInvoked)
   277  			}
   278  		})
   279  	}
   280  }
   281  
   282  func TestListStoresCommand(t *testing.T) {
   283  	const (
   284  		storeName = "test123"
   285  		storeID   = "store-id-123"
   286  	)
   287  
   288  	stores := &fastly.SecretStores{
   289  		Meta: fastly.SecretStoreMeta{
   290  			Limit: 123,
   291  		},
   292  		Data: []fastly.SecretStore{
   293  			{StoreID: storeID, Name: storeName},
   294  		},
   295  	}
   296  
   297  	scenarios := []struct {
   298  		args           string
   299  		api            mock.API
   300  		wantAPIInvoked bool
   301  		wantError      string
   302  		wantOutput     string
   303  	}{
   304  		{
   305  			args: "list",
   306  			api: mock.API{
   307  				ListSecretStoresFn: func(i *fastly.ListSecretStoresInput) (*fastly.SecretStores, error) {
   308  					return nil, nil
   309  				},
   310  			},
   311  			wantAPIInvoked: true,
   312  		},
   313  		{
   314  			args: "list",
   315  			api: mock.API{
   316  				ListSecretStoresFn: func(i *fastly.ListSecretStoresInput) (*fastly.SecretStores, error) {
   317  					return nil, errors.New("unknown error")
   318  				},
   319  			},
   320  			wantAPIInvoked: true,
   321  			wantError:      "unknown error",
   322  		},
   323  		{
   324  			args: "list",
   325  			api: mock.API{
   326  				ListSecretStoresFn: func(i *fastly.ListSecretStoresInput) (*fastly.SecretStores, error) {
   327  					return stores, nil
   328  				},
   329  			},
   330  			wantAPIInvoked: true,
   331  			wantOutput:     fmtStores(stores.Data),
   332  		},
   333  		{
   334  			args: "list --json",
   335  			api: mock.API{
   336  				ListSecretStoresFn: func(i *fastly.ListSecretStoresInput) (*fastly.SecretStores, error) {
   337  					return stores, nil
   338  				},
   339  			},
   340  			wantAPIInvoked: true,
   341  			wantOutput:     fstfmt.EncodeJSON([]fastly.SecretStore{stores.Data[0]}),
   342  		},
   343  	}
   344  
   345  	for _, testcase := range scenarios {
   346  		testcase := testcase
   347  		t.Run(testcase.args, func(t *testing.T) {
   348  			var stdout bytes.Buffer
   349  			args := testutil.Args(secretstore.RootNameStore + " " + testcase.args)
   350  			opts := testutil.MockGlobalData(args, &stdout)
   351  
   352  			f := testcase.api.ListSecretStoresFn
   353  			var apiInvoked bool
   354  			testcase.api.ListSecretStoresFn = func(i *fastly.ListSecretStoresInput) (*fastly.SecretStores, error) {
   355  				apiInvoked = true
   356  				return f(i)
   357  			}
   358  
   359  			app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
   360  				opts.APIClientFactory = mock.APIClient(testcase.api)
   361  				return opts, nil
   362  			}
   363  			err := app.Run(args, nil)
   364  
   365  			testutil.AssertErrorContains(t, err, testcase.wantError)
   366  			testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput)
   367  			if apiInvoked != testcase.wantAPIInvoked {
   368  				t.Fatalf("API ListSecretStores invoked = %v, want %v", apiInvoked, testcase.wantAPIInvoked)
   369  			}
   370  		})
   371  	}
   372  }