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 }