From 03e8c972d06a7a2749f932cb819c6a933016bb50 Mon Sep 17 00:00:00 2001 From: Abin Simon Date: Tue, 25 Oct 2022 02:03:51 +0530 Subject: [PATCH] Show message when no backups or backup deleted (#1192) ## Description Show messages for specific user interactions which previously had none. ## Type of change - [ ] :sunflower: Feature - [ ] :bug: Bugfix - [ ] :world_map: Documentation - [ ] :robot: Test - [ ] :computer: CI/Deployment - [x] :hamster: Trivial/Minor ## Issue(s) * fixes https://github.com/alcionai/corso/issues/1177 ## Test Plan - [x] :muscle: Manual - [ ] :zap: Unit test - [x] :green_heart: E2E --- src/cli/backup/exchange.go | 2 + src/cli/backup/exchange_integration_test.go | 90 ++++++++ src/cli/backup/onedrive.go | 2 + src/cli/backup/onedrive_integration_test.go | 235 ++++++++++++++++++++ src/pkg/backup/backup.go | 5 + 5 files changed, 334 insertions(+) create mode 100644 src/cli/backup/onedrive_integration_test.go diff --git a/src/cli/backup/exchange.go b/src/cli/backup/exchange.go index bd7462747..b99fcc1ef 100644 --- a/src/cli/backup/exchange.go +++ b/src/cli/backup/exchange.go @@ -524,5 +524,7 @@ func deleteExchangeCmd(cmd *cobra.Command, args []string) error { return Only(ctx, errors.Wrapf(err, "Deleting backup %s", backupID)) } + Info(ctx, "Deleted Exchange backup ", backupID) + return nil } diff --git a/src/cli/backup/exchange_integration_test.go b/src/cli/backup/exchange_integration_test.go index 39e888971..1f500187e 100644 --- a/src/cli/backup/exchange_integration_test.go +++ b/src/cli/backup/exchange_integration_test.go @@ -34,6 +34,96 @@ var ( var backupDataSets = []path.CategoryType{email, contacts, events} +// --------------------------------------------------------------------------- +// tests with no backups +// --------------------------------------------------------------------------- + +type NoBackupExchangeIntegrationSuite struct { + suite.Suite + acct account.Account + st storage.Storage + vpr *viper.Viper + cfgFP string + repo repository.Repository + m365UserID string + recorder strings.Builder +} + +func TestNoBackupExchangeIntegrationSuite(t *testing.T) { + if err := tester.RunOnAny( + tester.CorsoCITests, + tester.CorsoCLITests, + tester.CorsoCLIBackupTests, + ); err != nil { + t.Skip(err) + } + + suite.Run(t, new(NoBackupExchangeIntegrationSuite)) +} + +func (suite *NoBackupExchangeIntegrationSuite) SetupSuite() { + t := suite.T() + ctx, flush := tester.NewContext() + + defer flush() + + _, err := tester.GetRequiredEnvSls( + tester.AWSStorageCredEnvs, + tester.M365AcctCredEnvs) + require.NoError(t, err) + + // prepare common details + suite.acct = tester.NewM365Account(t) + suite.st = tester.NewPrefixedS3Storage(t) + suite.recorder = strings.Builder{} + + cfg, err := suite.st.S3Config() + require.NoError(t, err) + + force := map[string]string{ + tester.TestCfgAccountProvider: "M365", + tester.TestCfgStorageProvider: "S3", + tester.TestCfgPrefix: cfg.Prefix, + } + + suite.vpr, suite.cfgFP, err = tester.MakeTempTestConfigClone(t, force) + require.NoError(t, err) + + ctx = config.SetViper(ctx, suite.vpr) + suite.m365UserID = tester.M365UserID(t) + + // init the repo first + suite.repo, err = repository.Initialize(ctx, suite.acct, suite.st, control.Options{}) + require.NoError(t, err) +} + +func (suite *NoBackupExchangeIntegrationSuite) TestExchangeBackupListCmd_empty() { + t := suite.T() + ctx, flush := tester.NewContext() + ctx = config.SetViper(ctx, suite.vpr) + + defer flush() + + suite.recorder.Reset() + + cmd := tester.StubRootCmd( + "backup", "list", "exchange", + "--config-file", suite.cfgFP) + cli.BuildCommandTree(cmd) + + cmd.SetErr(&suite.recorder) + + ctx = print.SetRootCmd(ctx, cmd) + + // run the command + require.NoError(t, cmd.ExecuteContext(ctx)) + + result := suite.recorder.String() + + // as an offhand check: the result should contain the m365 user id + assert.Equal(t, "No backups available\n", result) +} + // --------------------------------------------------------------------------- // tests with no prior backup // --------------------------------------------------------------------------- diff --git a/src/cli/backup/onedrive.go b/src/cli/backup/onedrive.go index cb9ee95d9..7a853f853 100644 --- a/src/cli/backup/onedrive.go +++ b/src/cli/backup/onedrive.go @@ -412,5 +412,7 @@ func deleteOneDriveCmd(cmd *cobra.Command, args []string) error { return Only(ctx, errors.Wrapf(err, "Deleting backup %s", backupID)) } + Info(ctx, "Deleted OneDrive backup ", backupID) + return nil } diff --git a/src/cli/backup/onedrive_integration_test.go b/src/cli/backup/onedrive_integration_test.go new file mode 100644 index 000000000..956f9f075 --- /dev/null +++ b/src/cli/backup/onedrive_integration_test.go @@ -0,0 +1,235 @@ +package backup_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/google/uuid" + "github.com/spf13/viper" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + "github.com/alcionai/corso/src/cli" + "github.com/alcionai/corso/src/cli/config" + "github.com/alcionai/corso/src/cli/print" + "github.com/alcionai/corso/src/cli/utils" + "github.com/alcionai/corso/src/internal/operations" + "github.com/alcionai/corso/src/internal/tester" + "github.com/alcionai/corso/src/pkg/account" + "github.com/alcionai/corso/src/pkg/control" + "github.com/alcionai/corso/src/pkg/repository" + "github.com/alcionai/corso/src/pkg/selectors" + "github.com/alcionai/corso/src/pkg/storage" +) + +// --------------------------------------------------------------------------- +// tests with no prior backup +// --------------------------------------------------------------------------- + +type NoBackupOneDriveIntegrationSuite struct { + suite.Suite + acct account.Account + st storage.Storage + vpr *viper.Viper + cfgFP string + repo repository.Repository + m365UserID string + recorder strings.Builder +} + +func TestNoBackupOneDriveIntegrationSuite(t *testing.T) { + if err := tester.RunOnAny( + tester.CorsoCITests, + tester.CorsoCLITests, + tester.CorsoCLIBackupTests, + ); err != nil { + t.Skip(err) + } + + suite.Run(t, new(NoBackupOneDriveIntegrationSuite)) +} + +func (suite *NoBackupOneDriveIntegrationSuite) SetupSuite() { + t := suite.T() + ctx, flush := tester.NewContext() + + defer flush() + + _, err := tester.GetRequiredEnvSls( + tester.AWSStorageCredEnvs, + tester.M365AcctCredEnvs) + require.NoError(t, err) + + // prepare common details + suite.acct = tester.NewM365Account(t) + suite.st = tester.NewPrefixedS3Storage(t) + + cfg, err := suite.st.S3Config() + require.NoError(t, err) + + force := map[string]string{ + tester.TestCfgAccountProvider: "M365", + tester.TestCfgStorageProvider: "S3", + tester.TestCfgPrefix: cfg.Prefix, + } + + suite.vpr, suite.cfgFP, err = tester.MakeTempTestConfigClone(t, force) + require.NoError(t, err) + + ctx = config.SetViper(ctx, suite.vpr) + suite.m365UserID = tester.M365UserID(t) + + // init the repo first + suite.repo, err = repository.Initialize(ctx, suite.acct, suite.st, control.Options{}) + require.NoError(t, err) +} + +func (suite *NoBackupOneDriveIntegrationSuite) TestOneDriveBackupListCmd_empty() { + t := suite.T() + ctx, flush := tester.NewContext() + ctx = config.SetViper(ctx, suite.vpr) + + defer flush() + + suite.recorder.Reset() + + cmd := tester.StubRootCmd( + "backup", "list", "onedrive", + "--config-file", suite.cfgFP) + cli.BuildCommandTree(cmd) + + cmd.SetErr(&suite.recorder) + + ctx = print.SetRootCmd(ctx, cmd) + + // run the command + require.NoError(t, cmd.ExecuteContext(ctx)) + + result := suite.recorder.String() + + // as an offhand check: the result should contain the m365 user id + assert.Equal(t, "No backups available\n", result) +} + +// --------------------------------------------------------------------------- +// tests for deleting backups +// --------------------------------------------------------------------------- + +type BackupDeleteOneDriveIntegrationSuite struct { + suite.Suite + acct account.Account + st storage.Storage + vpr *viper.Viper + cfgFP string + repo repository.Repository + backupOp operations.BackupOperation + recorder strings.Builder +} + +func TestBackupDeleteOneDriveIntegrationSuite(t *testing.T) { + if err := tester.RunOnAny( + tester.CorsoCITests, + tester.CorsoCLITests, + tester.CorsoCLIBackupTests, + ); err != nil { + t.Skip(err) + } + + suite.Run(t, new(BackupDeleteOneDriveIntegrationSuite)) +} + +func (suite *BackupDeleteOneDriveIntegrationSuite) SetupSuite() { + t := suite.T() + _, err := tester.GetRequiredEnvSls( + tester.AWSStorageCredEnvs, + tester.M365AcctCredEnvs) + require.NoError(t, err) + + // prepare common details + suite.acct = tester.NewM365Account(t) + suite.st = tester.NewPrefixedS3Storage(t) + + cfg, err := suite.st.S3Config() + require.NoError(t, err) + + force := map[string]string{ + tester.TestCfgAccountProvider: "M365", + tester.TestCfgStorageProvider: "S3", + tester.TestCfgPrefix: cfg.Prefix, + } + suite.vpr, suite.cfgFP, err = tester.MakeTempTestConfigClone(t, force) + require.NoError(t, err) + + ctx, flush := tester.NewContext() + ctx = config.SetViper(ctx, suite.vpr) + + defer flush() + + // init the repo first + suite.repo, err = repository.Initialize(ctx, suite.acct, suite.st, control.Options{}) + require.NoError(t, err) + + m365UserID := tester.M365UserID(t) + + // some tests require an existing backup + sel := selectors.NewOneDriveBackup() + sel.Include(sel.Folders([]string{m365UserID}, selectors.Any())) + + suite.backupOp, err = suite.repo.NewBackup(ctx, sel.Selector) + require.NoError(t, suite.backupOp.Run(ctx)) + require.NoError(t, err) +} + +func (suite *BackupDeleteOneDriveIntegrationSuite) TestOneDriveBackupDeleteCmd() { + t := suite.T() + ctx, flush := tester.NewContext() + ctx = config.SetViper(ctx, suite.vpr) + + defer flush() + + suite.recorder.Reset() + + cmd := tester.StubRootCmd( + "backup", "delete", "onedrive", + "--config-file", suite.cfgFP, + "--"+utils.BackupFN, string(suite.backupOp.Results.BackupID)) + cli.BuildCommandTree(cmd) + cmd.SetErr(&suite.recorder) + + ctx = print.SetRootCmd(ctx, cmd) + + // run the command + require.NoError(t, cmd.ExecuteContext(ctx)) + + result := suite.recorder.String() + + assert.Equal(t, fmt.Sprintf("Deleted OneDrive backup %s\n", string(suite.backupOp.Results.BackupID)), result) + + // a follow-up details call should fail, due to the backup ID being deleted + cmd = tester.StubRootCmd( + "backup", "details", "onedrive", + "--config-file", suite.cfgFP, + "--backup", string(suite.backupOp.Results.BackupID)) + cli.BuildCommandTree(cmd) + + require.Error(t, cmd.ExecuteContext(ctx)) +} + +func (suite *BackupDeleteOneDriveIntegrationSuite) TestOneDriveBackupDeleteCmd_unknownID() { + t := suite.T() + ctx, flush := tester.NewContext() + ctx = config.SetViper(ctx, suite.vpr) + + defer flush() + + cmd := tester.StubRootCmd( + "backup", "delete", "onedrive", + "--config-file", suite.cfgFP, + "--"+utils.BackupFN, uuid.NewString()) + cli.BuildCommandTree(cmd) + + // unknown backupIDs should error since the modelStore can't find the backup + require.Error(t, cmd.ExecuteContext(ctx)) +} diff --git a/src/pkg/backup/backup.go b/src/pkg/backup/backup.go index 97b02a373..9f841cc29 100644 --- a/src/pkg/backup/backup.go +++ b/src/pkg/backup/backup.go @@ -75,6 +75,11 @@ func (b Backup) Print(ctx context.Context) { // PrintAll writes the slice of Backups to StdOut, in the format requested by the caller. func PrintAll(ctx context.Context, bs []Backup) { + if len(bs) == 0 { + print.Info(ctx, "No backups available") + return + } + ps := []print.Printable{} for _, b := range bs { ps = append(ps, print.Printable(b))