From 6a15f699aac241349b46aa03b08240f76556d988 Mon Sep 17 00:00:00 2001 From: Keepers Date: Tue, 12 Sep 2023 17:17:05 -0600 Subject: [PATCH] use alias for teams cli (#4225) #### Does this PR need a docs update or release note? - [x] :no_entry: No #### Type of change - [x] :broom: Tech Debt/Cleanup #### Issue(s) * #3988 #### Test Plan - [x] :zap: Unit test - [x] :green_heart: E2E --- .github/workflows/sanity-test.yaml | 11 - src/cli/backup/backup.go | 1 - src/cli/backup/groups.go | 10 +- src/cli/backup/teams.go | 287 -------------- src/cli/backup/teams_e2e_test.go | 606 ----------------------------- src/cli/backup/teams_test.go | 139 ------- src/cli/export/export.go | 1 - src/cli/export/groups.go | 8 +- src/cli/export/teams.go | 96 ----- src/cli/export/teams_test.go | 93 ----- src/cli/restore/groups.go | 2 + src/cli/restore/restore.go | 1 - src/cli/restore/teams.go | 81 ---- src/cli/restore/teams_test.go | 108 ----- 14 files changed, 13 insertions(+), 1431 deletions(-) delete mode 100644 src/cli/backup/teams.go delete mode 100644 src/cli/backup/teams_e2e_test.go delete mode 100644 src/cli/backup/teams_test.go delete mode 100644 src/cli/export/teams.go delete mode 100644 src/cli/export/teams_test.go delete mode 100644 src/cli/restore/teams.go delete mode 100644 src/cli/restore/teams_test.go diff --git a/.github/workflows/sanity-test.yaml b/.github/workflows/sanity-test.yaml index 1c155a255..2e7487be7 100644 --- a/.github/workflows/sanity-test.yaml +++ b/.github/workflows/sanity-test.yaml @@ -355,17 +355,6 @@ jobs: test-folder: '${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-groups.outputs.result }}' log-dir: ${{ env.CORSO_LOG_DIR }} -# Since it will be alias, will reenable if required - # - name: Teams - Backup - # id: teams-backup - # uses: ./.github/actions/backup-restore-test - # with: - # service: teams - # kind: initial - # backup-args: '--group "${{ vars.CORSO_M365_TEST_TEAM_ID }}"' - # test-folder: '${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-groups.outputs.result }}' - # log-dir: ${{ env.CORSO_LOG_DIR }} - # TODO: incrementals ########################################################################################################################################## diff --git a/src/cli/backup/backup.go b/src/cli/backup/backup.go index 6727abcd7..93fcce74f 100644 --- a/src/cli/backup/backup.go +++ b/src/cli/backup/backup.go @@ -61,7 +61,6 @@ func AddCommands(cmd *cobra.Command) { // delete after release if len(os.Getenv("CORSO_ENABLE_GROUPS")) > 0 { addGroupsCommands(subCommand) - addTeamsCommands(subCommand) } } } diff --git a/src/cli/backup/groups.go b/src/cli/backup/groups.go index db9410c64..9f7f928c8 100644 --- a/src/cli/backup/groups.go +++ b/src/cli/backup/groups.go @@ -31,6 +31,7 @@ import ( const ( groupsServiceCommand = "groups" + teamsServiceCommand = "teams" groupsServiceCommandCreateUseSuffix = "--group | '" + flags.Wildcard + "'" groupsServiceCommandDeleteUseSuffix = "--backup " groupsServiceCommandDetailsUseSuffix = "--backup " @@ -133,10 +134,11 @@ func addGroupsCommands(cmd *cobra.Command) *cobra.Command { // `corso backup create groups [...]` func groupsCreateCmd() *cobra.Command { return &cobra.Command{ - Use: groupsServiceCommand, - Short: "Backup M365 Group service data", - RunE: createGroupsCmd, - Args: cobra.NoArgs, + Use: groupsServiceCommand, + Aliases: []string{teamsServiceCommand}, + Short: "Backup M365 Group service data", + RunE: createGroupsCmd, + Args: cobra.NoArgs, } } diff --git a/src/cli/backup/teams.go b/src/cli/backup/teams.go deleted file mode 100644 index 33bd6c035..000000000 --- a/src/cli/backup/teams.go +++ /dev/null @@ -1,287 +0,0 @@ -package backup - -import ( - "context" - "fmt" - - "github.com/alcionai/clues" - "github.com/spf13/cobra" - "github.com/spf13/pflag" - "golang.org/x/exp/slices" - - "github.com/alcionai/corso/src/cli/flags" - . "github.com/alcionai/corso/src/cli/print" - "github.com/alcionai/corso/src/cli/repo" - "github.com/alcionai/corso/src/cli/utils" - "github.com/alcionai/corso/src/internal/common/idname" - "github.com/alcionai/corso/src/pkg/fault" - "github.com/alcionai/corso/src/pkg/filters" - "github.com/alcionai/corso/src/pkg/path" - "github.com/alcionai/corso/src/pkg/selectors" - "github.com/alcionai/corso/src/pkg/services/m365" -) - -// ------------------------------------------------------------------------------------------------ -// setup and globals -// ------------------------------------------------------------------------------------------------ - -const ( - teamsServiceCommand = "teams" - teamsServiceCommandCreateUseSuffix = "--team | '" + flags.Wildcard + "'" - teamsServiceCommandDeleteUseSuffix = "--backup " - teamsServiceCommandDetailsUseSuffix = "--backup " -) - -// TODO: correct examples -const ( - teamsServiceCommandCreateExamples = `# Backup all Teams data for Alice -corso backup create teams --team alice@example.com - -# Backup only Teams contacts for Alice and Bob -corso backup create teams --team engineering,sales --data contacts - -# Backup all Teams data for all M365 users -corso backup create teams --team '*'` - - teamsServiceCommandDeleteExamples = `# Delete Teams backup with ID 1234abcd-12ab-cd34-56de-1234abcd -corso backup delete teams --backup 1234abcd-12ab-cd34-56de-1234abcd` - - teamsServiceCommandDetailsExamples = `# Explore items in Alice's latest backup (1234abcd...) -corso backup details teams --backup 1234abcd-12ab-cd34-56de-1234abcd - -# Explore calendar events occurring after start of 2022 -corso backup details teams --backup 1234abcd-12ab-cd34-56de-1234abcd \ - --event-starts-after 2022-01-01T00:00:00` -) - -// called by backup.go to map subcommands to provider-specific handling. -func addTeamsCommands(cmd *cobra.Command) *cobra.Command { - var ( - c *cobra.Command - fs *pflag.FlagSet - ) - - switch cmd.Use { - case createCommand: - c, fs = utils.AddCommand(cmd, teamsCreateCmd(), utils.MarkPreReleaseCommand()) - fs.SortFlags = false - - c.Use = c.Use + " " + teamsServiceCommandCreateUseSuffix - c.Example = teamsServiceCommandCreateExamples - - // Flags addition ordering should follow the order we want them to appear in help and docs: - flags.AddTeamFlag(c) - flags.AddDataFlag(c, []string{dataEmail, dataContacts, dataEvents}, false) - flags.AddCorsoPassphaseFlags(c) - flags.AddAWSCredsFlags(c) - flags.AddAzureCredsFlags(c) - flags.AddFetchParallelismFlag(c) - flags.AddFailFastFlag(c) - - case listCommand: - c, fs = utils.AddCommand(cmd, teamsListCmd(), utils.MarkPreReleaseCommand()) - fs.SortFlags = false - - flags.AddBackupIDFlag(c, false) - flags.AddCorsoPassphaseFlags(c) - flags.AddAWSCredsFlags(c) - flags.AddAzureCredsFlags(c) - addFailedItemsFN(c) - addSkippedItemsFN(c) - addRecoveredErrorsFN(c) - - case detailsCommand: - c, fs = utils.AddCommand(cmd, teamsDetailsCmd(), utils.MarkPreReleaseCommand()) - fs.SortFlags = false - - c.Use = c.Use + " " + teamsServiceCommandDetailsUseSuffix - c.Example = teamsServiceCommandDetailsExamples - - flags.AddSkipReduceFlag(c) - - // Flags addition ordering should follow the order we want them to appear in help and docs: - // More generic (ex: --user) and more frequently used flags take precedence. - flags.AddBackupIDFlag(c, true) - flags.AddCorsoPassphaseFlags(c) - flags.AddAWSCredsFlags(c) - flags.AddAzureCredsFlags(c) - - case deleteCommand: - c, fs = utils.AddCommand(cmd, teamsDeleteCmd(), utils.MarkPreReleaseCommand()) - fs.SortFlags = false - - c.Use = c.Use + " " + teamsServiceCommandDeleteUseSuffix - c.Example = teamsServiceCommandDeleteExamples - - flags.AddBackupIDFlag(c, true) - flags.AddCorsoPassphaseFlags(c) - flags.AddAWSCredsFlags(c) - flags.AddAzureCredsFlags(c) - } - - return c -} - -// ------------------------------------------------------------------------------------------------ -// backup create -// ------------------------------------------------------------------------------------------------ - -// `corso backup create teams [...]` -func teamsCreateCmd() *cobra.Command { - return &cobra.Command{ - Use: teamsServiceCommand, - Short: "Backup M365 Team service data", - RunE: createTeamsCmd, - Args: cobra.NoArgs, - } -} - -// processes a teams service backup. -func createTeamsCmd(cmd *cobra.Command, args []string) error { - ctx := cmd.Context() - - if utils.HasNoFlagsAndShownHelp(cmd) { - return nil - } - - if err := validateTeamsBackupCreateFlags(flags.TeamFV, flags.CategoryDataFV); err != nil { - return err - } - - r, acct, err := utils.AccountConnectAndWriteRepoConfig(ctx, path.GroupsService, repo.S3Overrides(cmd)) - if err != nil { - return Only(ctx, err) - } - - defer utils.CloseRepo(ctx, r) - - // TODO: log/print recoverable errors - errs := fault.New(false) - - ins, err := m365.GroupsMap(ctx, *acct, errs) - if err != nil { - return Only(ctx, clues.Wrap(err, "Failed to retrieve M365 teams")) - } - - sel := teamsBackupCreateSelectors(ctx, ins, flags.TeamFV, flags.CategoryDataFV) - selectorSet := []selectors.Selector{} - - for _, discSel := range sel.SplitByResourceOwner(ins.IDs()) { - selectorSet = append(selectorSet, discSel.Selector) - } - - return runBackups( - ctx, - r, - "Group", - selectorSet, - ins) -} - -// ------------------------------------------------------------------------------------------------ -// backup list -// ------------------------------------------------------------------------------------------------ - -// `corso backup list teams [...]` -func teamsListCmd() *cobra.Command { - return &cobra.Command{ - Use: teamsServiceCommand, - Short: "List the history of M365 Teams service backups", - RunE: listTeamsCmd, - Args: cobra.NoArgs, - } -} - -// lists the history of backup operations -func listTeamsCmd(cmd *cobra.Command, args []string) error { - return genericListCommand(cmd, flags.BackupIDFV, path.TeamsService, args) -} - -// ------------------------------------------------------------------------------------------------ -// backup details -// ------------------------------------------------------------------------------------------------ - -// `corso backup details teams [...]` -func teamsDetailsCmd() *cobra.Command { - return &cobra.Command{ - Use: teamsServiceCommand, - Short: "Shows the details of a M365 Teams service backup", - RunE: detailsTeamsCmd, - Args: cobra.NoArgs, - } -} - -// processes a teams service backup. -func detailsTeamsCmd(cmd *cobra.Command, args []string) error { - ctx := cmd.Context() - - if utils.HasNoFlagsAndShownHelp(cmd) { - return nil - } - - return Only(ctx, utils.ErrNotYetImplemented) -} - -// ------------------------------------------------------------------------------------------------ -// backup delete -// ------------------------------------------------------------------------------------------------ - -// `corso backup delete teams [...]` -func teamsDeleteCmd() *cobra.Command { - return &cobra.Command{ - Use: teamsServiceCommand, - Short: "Delete backed-up M365 Teams service data", - RunE: deleteTeamsCmd, - Args: cobra.NoArgs, - } -} - -// deletes an teams service backup. -func deleteTeamsCmd(cmd *cobra.Command, args []string) error { - return genericDeleteCommand(cmd, path.TeamsService, flags.BackupIDFV, "Teams", args) -} - -// --------------------------------------------------------------------------- -// helpers -// --------------------------------------------------------------------------- - -func validateTeamsBackupCreateFlags(teams, cats []string) error { - if len(teams) == 0 { - return clues.New( - "requires one or more --" + - flags.TeamFN + " ids, or the wildcard --" + - flags.TeamFN + " *") - } - - msg := fmt.Sprintf( - " is an unrecognized data type; only %s and %s are supported", - flags.DataLibraries, flags.DataMessages) - - allowedCats := utils.GroupsAllowedCategories() - - for _, d := range cats { - if _, ok := allowedCats[d]; !ok { - return clues.New(d + msg) - } - } - - return nil -} - -func teamsBackupCreateSelectors( - ctx context.Context, - ins idname.Cacher, - team, cats []string, -) *selectors.GroupsBackup { - if filters.PathContains(team).Compare(flags.Wildcard) { - return includeAllTeamWithCategories(ins, cats) - } - - sel := selectors.NewGroupsBackup(slices.Clone(team)) - - return utils.AddGroupsCategories(sel, cats) -} - -func includeAllTeamWithCategories(ins idname.Cacher, categories []string) *selectors.GroupsBackup { - return utils.AddGroupsCategories(selectors.NewGroupsBackup(ins.IDs()), categories) -} diff --git a/src/cli/backup/teams_e2e_test.go b/src/cli/backup/teams_e2e_test.go deleted file mode 100644 index 13bcd82b8..000000000 --- a/src/cli/backup/teams_e2e_test.go +++ /dev/null @@ -1,606 +0,0 @@ -package backup_test - -import ( - "context" - "fmt" - "strings" - "testing" - - "github.com/alcionai/clues" - "github.com/google/uuid" - "github.com/spf13/cobra" - "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/flags" - "github.com/alcionai/corso/src/cli/print" - cliTD "github.com/alcionai/corso/src/cli/testdata" - "github.com/alcionai/corso/src/internal/common/idname" - "github.com/alcionai/corso/src/internal/operations" - "github.com/alcionai/corso/src/internal/tester" - "github.com/alcionai/corso/src/internal/tester/tconfig" - "github.com/alcionai/corso/src/pkg/path" - "github.com/alcionai/corso/src/pkg/selectors" - selTD "github.com/alcionai/corso/src/pkg/selectors/testdata" - storeTD "github.com/alcionai/corso/src/pkg/storage/testdata" -) - -// --------------------------------------------------------------------------- -// tests that require no existing backups -// --------------------------------------------------------------------------- - -type NoBackupTeamsE2ESuite struct { - tester.Suite - dpnd dependencies - its intgTesterSetup -} - -func TestNoBackupTeamsE2ESuite(t *testing.T) { - suite.Run(t, &BackupTeamsE2ESuite{Suite: tester.NewE2ESuite( - t, - [][]string{storeTD.AWSStorageCredEnvs, tconfig.M365AcctCredEnvs})}) -} - -func (suite *NoBackupTeamsE2ESuite) SetupSuite() { - t := suite.T() - - ctx, flush := tester.NewContext(t) - defer flush() - - suite.its = newIntegrationTesterSetup(t) - suite.dpnd = prepM365Test(t, ctx) -} - -func (suite *NoBackupTeamsE2ESuite) TestTeamsBackupListCmd_noBackups() { - t := suite.T() - ctx, flush := tester.NewContext(t) - ctx = config.SetViper(ctx, suite.dpnd.vpr) - - defer flush() - - suite.dpnd.recorder.Reset() - - cmd := cliTD.StubRootCmd( - "backup", "list", "teams", - "--config-file", suite.dpnd.configFilePath) - cli.BuildCommandTree(cmd) - - cmd.SetErr(&suite.dpnd.recorder) - - ctx = print.SetRootCmd(ctx, cmd) - - // run the command - err := cmd.ExecuteContext(ctx) - require.NoError(t, err, clues.ToCore(err)) - - result := suite.dpnd.recorder.String() - - // as an offhand check: the result should contain the m365 team id - assert.True(t, strings.HasSuffix(result, "No backups available\n")) -} - -// --------------------------------------------------------------------------- -// tests with no prior backup -// --------------------------------------------------------------------------- - -type BackupTeamsE2ESuite struct { - tester.Suite - dpnd dependencies - its intgTesterSetup -} - -func TestBackupTeamsE2ESuite(t *testing.T) { - suite.Run(t, &BackupTeamsE2ESuite{Suite: tester.NewE2ESuite( - t, - [][]string{storeTD.AWSStorageCredEnvs, tconfig.M365AcctCredEnvs})}) -} - -func (suite *BackupTeamsE2ESuite) SetupSuite() { - t := suite.T() - - ctx, flush := tester.NewContext(t) - defer flush() - - suite.its = newIntegrationTesterSetup(t) - suite.dpnd = prepM365Test(t, ctx) -} - -func (suite *BackupTeamsE2ESuite) TestTeamsBackupCmd_channelMessages() { - runTeamsBackupCategoryTest(suite, channelMessages) -} - -func (suite *BackupTeamsE2ESuite) TestTeamsBackupCmd_libraries() { - runTeamsBackupCategoryTest(suite, libraries) -} - -func runTeamsBackupCategoryTest(suite *BackupTeamsE2ESuite, category path.CategoryType) { - recorder := strings.Builder{} - recorder.Reset() - - t := suite.T() - - ctx, flush := tester.NewContext(t) - ctx = config.SetViper(ctx, suite.dpnd.vpr) - - defer flush() - - cmd, ctx := buildTeamsBackupCmd( - ctx, - suite.dpnd.configFilePath, - suite.its.team.ID, - category.String(), - &recorder) - - // run the command - err := cmd.ExecuteContext(ctx) - require.NoError(t, err, clues.ToCore(err)) - - result := recorder.String() - t.Log("backup results", result) - - // as an offhand check: the result should contain the m365 team id - assert.Contains(t, result, suite.its.team.ID) -} - -func (suite *BackupTeamsE2ESuite) TestTeamsBackupCmd_teamNotFound_channelMessages() { - runTeamsBackupTeamNotFoundTest(suite, channelMessages) -} - -func (suite *BackupTeamsE2ESuite) TestTeamsBackupCmd_teamNotFound_libraries() { - runTeamsBackupTeamNotFoundTest(suite, libraries) -} - -func runTeamsBackupTeamNotFoundTest(suite *BackupTeamsE2ESuite, category path.CategoryType) { - recorder := strings.Builder{} - recorder.Reset() - - t := suite.T() - - ctx, flush := tester.NewContext(t) - ctx = config.SetViper(ctx, suite.dpnd.vpr) - - defer flush() - - cmd, ctx := buildTeamsBackupCmd( - ctx, - suite.dpnd.configFilePath, - "foo@not-there.com", - category.String(), - &recorder) - - // run the command - err := cmd.ExecuteContext(ctx) - require.Error(t, err, clues.ToCore(err)) - assert.Contains( - t, - err.Error(), - "not found in tenant", "error missing team not found") - assert.NotContains(t, err.Error(), "runtime error", "panic happened") - - t.Logf("backup error message: %s", err.Error()) - - result := recorder.String() - t.Log("backup results", result) -} - -func (suite *BackupTeamsE2ESuite) TestBackupCreateTeams_badAzureClientIDFlag() { - t := suite.T() - ctx, flush := tester.NewContext(t) - - defer flush() - - suite.dpnd.recorder.Reset() - - cmd := cliTD.StubRootCmd( - "backup", "create", "teams", - "--team", suite.its.team.ID, - "--azure-client-id", "invalid-value") - cli.BuildCommandTree(cmd) - - cmd.SetErr(&suite.dpnd.recorder) - - ctx = print.SetRootCmd(ctx, cmd) - - // run the command - err := cmd.ExecuteContext(ctx) - require.Error(t, err, clues.ToCore(err)) -} - -func (suite *BackupTeamsE2ESuite) TestBackupCreateTeams_fromConfigFile() { - t := suite.T() - ctx, flush := tester.NewContext(t) - ctx = config.SetViper(ctx, suite.dpnd.vpr) - - defer flush() - - suite.dpnd.recorder.Reset() - - cmd := cliTD.StubRootCmd( - "backup", "create", "teams", - "--team", suite.its.team.ID, - "--config-file", suite.dpnd.configFilePath) - cli.BuildCommandTree(cmd) - - cmd.SetOut(&suite.dpnd.recorder) - - ctx = print.SetRootCmd(ctx, cmd) - - // run the command - err := cmd.ExecuteContext(ctx) - require.NoError(t, err, clues.ToCore(err)) - - result := suite.dpnd.recorder.String() - t.Log("backup results", result) - - // as an offhand check: the result should contain the m365 team id - assert.Contains(t, result, suite.its.team.ID) -} - -// AWS flags -func (suite *BackupTeamsE2ESuite) TestBackupCreateTeams_badAWSFlags() { - t := suite.T() - ctx, flush := tester.NewContext(t) - - defer flush() - - suite.dpnd.recorder.Reset() - - cmd := cliTD.StubRootCmd( - "backup", "create", "teams", - "--team", suite.its.team.ID, - "--aws-access-key", "invalid-value", - "--aws-secret-access-key", "some-invalid-value") - cli.BuildCommandTree(cmd) - - cmd.SetOut(&suite.dpnd.recorder) - - ctx = print.SetRootCmd(ctx, cmd) - - // run the command - err := cmd.ExecuteContext(ctx) - // since invalid aws creds are explicitly set, should see a failure - require.Error(t, err, clues.ToCore(err)) -} - -// --------------------------------------------------------------------------- -// tests prepared with a previous backup -// --------------------------------------------------------------------------- - -type PreparedBackupTeamsE2ESuite struct { - tester.Suite - dpnd dependencies - backupOps map[path.CategoryType]string - its intgTesterSetup -} - -func TestPreparedBackupTeamsE2ESuite(t *testing.T) { - suite.Run(t, &PreparedBackupTeamsE2ESuite{ - Suite: tester.NewE2ESuite( - t, - [][]string{storeTD.AWSStorageCredEnvs, tconfig.M365AcctCredEnvs}), - }) -} - -func (suite *PreparedBackupTeamsE2ESuite) SetupSuite() { - t := suite.T() - - ctx, flush := tester.NewContext(t) - defer flush() - - suite.its = newIntegrationTesterSetup(t) - suite.dpnd = prepM365Test(t, ctx) - suite.backupOps = make(map[path.CategoryType]string) - - var ( - teams = []string{suite.its.team.ID} - ins = idname.NewCache(map[string]string{suite.its.team.ID: suite.its.team.ID}) - ) - - for _, set := range []path.CategoryType{channelMessages, libraries} { - var ( - sel = selectors.NewGroupsBackup(teams) - scopes []selectors.GroupsScope - ) - - switch set { - case channelMessages: - scopes = selTD.GroupsBackupChannelScope(sel) - - case libraries: - scopes = selTD.GroupsBackupLibraryFolderScope(sel) - } - - sel.Include(scopes) - - bop, err := suite.dpnd.repo.NewBackupWithLookup(ctx, sel.Selector, ins) - require.NoError(t, err, clues.ToCore(err)) - - err = bop.Run(ctx) - require.NoError(t, err, clues.ToCore(err)) - - bIDs := string(bop.Results.BackupID) - - // sanity check, ensure we can find the backup and its details immediately - b, err := suite.dpnd.repo.Backup(ctx, string(bop.Results.BackupID)) - require.NoError(t, err, "retrieving recent backup by ID") - require.Equal(t, bIDs, string(b.ID), "repo backup matches results id") - - _, b, errs := suite.dpnd.repo.GetBackupDetails(ctx, bIDs) - require.NoError(t, errs.Failure(), "retrieving recent backup details by ID") - require.Empty(t, errs.Recovered(), "retrieving recent backup details by ID") - require.Equal(t, bIDs, string(b.ID), "repo details matches results id") - - suite.backupOps[set] = string(b.ID) - } -} - -func (suite *PreparedBackupTeamsE2ESuite) TestTeamsListCmd_channelMessages() { - runTeamsListCmdTest(suite, channelMessages) -} - -func (suite *PreparedBackupTeamsE2ESuite) TestTeamsListCmd_libraries() { - runTeamsListCmdTest(suite, libraries) -} - -func runTeamsListCmdTest(suite *PreparedBackupTeamsE2ESuite, category path.CategoryType) { - suite.dpnd.recorder.Reset() - - t := suite.T() - - ctx, flush := tester.NewContext(t) - ctx = config.SetViper(ctx, suite.dpnd.vpr) - - defer flush() - - cmd := cliTD.StubRootCmd( - "backup", "list", "teams", - "--config-file", suite.dpnd.configFilePath) - cli.BuildCommandTree(cmd) - cmd.SetOut(&suite.dpnd.recorder) - - ctx = print.SetRootCmd(ctx, cmd) - - // run the command - err := cmd.ExecuteContext(ctx) - require.NoError(t, err, clues.ToCore(err)) - - // compare the output - result := suite.dpnd.recorder.String() - assert.Contains(t, result, suite.backupOps[category]) -} - -func (suite *PreparedBackupTeamsE2ESuite) TestTeamsListCmd_singleID_channelMessages() { - runTeamsListSingleCmdTest(suite, channelMessages) -} - -func (suite *PreparedBackupTeamsE2ESuite) TestTeamsListCmd_singleID_libraries() { - runTeamsListSingleCmdTest(suite, libraries) -} - -func runTeamsListSingleCmdTest(suite *PreparedBackupTeamsE2ESuite, category path.CategoryType) { - suite.dpnd.recorder.Reset() - - t := suite.T() - - ctx, flush := tester.NewContext(t) - ctx = config.SetViper(ctx, suite.dpnd.vpr) - - defer flush() - - bID := suite.backupOps[category] - - cmd := cliTD.StubRootCmd( - "backup", "list", "teams", - "--config-file", suite.dpnd.configFilePath, - "--backup", string(bID)) - cli.BuildCommandTree(cmd) - - cmd.SetOut(&suite.dpnd.recorder) - - ctx = print.SetRootCmd(ctx, cmd) - - // run the command - err := cmd.ExecuteContext(ctx) - require.NoError(t, err, clues.ToCore(err)) - - // compare the output - result := suite.dpnd.recorder.String() - assert.Contains(t, result, bID) -} - -func (suite *PreparedBackupTeamsE2ESuite) TestTeamsListCmd_badID() { - t := suite.T() - - ctx, flush := tester.NewContext(t) - ctx = config.SetViper(ctx, suite.dpnd.vpr) - - defer flush() - - cmd := cliTD.StubRootCmd( - "backup", "list", "teams", - "--config-file", suite.dpnd.configFilePath, - "--backup", "smarfs") - cli.BuildCommandTree(cmd) - - ctx = print.SetRootCmd(ctx, cmd) - - // run the command - err := cmd.ExecuteContext(ctx) - require.Error(t, err, clues.ToCore(err)) -} - -func (suite *PreparedBackupTeamsE2ESuite) TestTeamsDetailsCmd_channelMessages() { - runTeamsDetailsCmdTest(suite, channelMessages) -} - -func (suite *PreparedBackupTeamsE2ESuite) TestTeamsDetailsCmd_libraries() { - runTeamsDetailsCmdTest(suite, libraries) -} - -func runTeamsDetailsCmdTest(suite *PreparedBackupTeamsE2ESuite, category path.CategoryType) { - suite.dpnd.recorder.Reset() - - t := suite.T() - - ctx, flush := tester.NewContext(t) - ctx = config.SetViper(ctx, suite.dpnd.vpr) - - defer flush() - - bID := suite.backupOps[category] - - // fetch the details from the repo first - deets, _, errs := suite.dpnd.repo.GetBackupDetails(ctx, string(bID)) - require.NoError(t, errs.Failure(), clues.ToCore(errs.Failure())) - require.Empty(t, errs.Recovered()) - - cmd := cliTD.StubRootCmd( - "backup", "details", "teams", - "--config-file", suite.dpnd.configFilePath, - "--"+flags.BackupFN, string(bID)) - cli.BuildCommandTree(cmd) - cmd.SetOut(&suite.dpnd.recorder) - - ctx = print.SetRootCmd(ctx, cmd) - - // run the command - err := cmd.ExecuteContext(ctx) - require.NoError(t, err, clues.ToCore(err)) - - // compare the output - result := suite.dpnd.recorder.String() - - i := 0 - foundFolders := 0 - - for _, ent := range deets.Entries { - // Skip folders as they don't mean anything to the end team. - if ent.Folder != nil { - foundFolders++ - continue - } - - suite.Run(fmt.Sprintf("detail %d", i), func() { - assert.Contains(suite.T(), result, ent.ShortRef) - }) - - i++ - } - - // We only backup the default folder for each category so there should be at - // least that folder (we don't make details entries for prefix folders). - assert.GreaterOrEqual(t, foundFolders, 1) -} - -// --------------------------------------------------------------------------- -// tests for deleting backups -// --------------------------------------------------------------------------- - -type BackupDeleteTeamsE2ESuite struct { - tester.Suite - dpnd dependencies - backupOp operations.BackupOperation -} - -func TestBackupDeleteTeamsE2ESuite(t *testing.T) { - suite.Run(t, &BackupDeleteTeamsE2ESuite{ - Suite: tester.NewE2ESuite( - t, - [][]string{storeTD.AWSStorageCredEnvs, tconfig.M365AcctCredEnvs}), - }) -} - -func (suite *BackupDeleteTeamsE2ESuite) SetupSuite() { - t := suite.T() - - ctx, flush := tester.NewContext(t) - defer flush() - - suite.dpnd = prepM365Test(t, ctx) - - m365TeamID := tconfig.M365TeamID(t) - teams := []string{m365TeamID} - - // some tests require an existing backup - sel := selectors.NewGroupsBackup(teams) - sel.Include(selTD.GroupsBackupChannelScope(sel)) - - backupOp, err := suite.dpnd.repo.NewBackup(ctx, sel.Selector) - require.NoError(t, err, clues.ToCore(err)) - - suite.backupOp = backupOp - - err = suite.backupOp.Run(ctx) - require.NoError(t, err, clues.ToCore(err)) -} - -func (suite *BackupDeleteTeamsE2ESuite) TestTeamsBackupDeleteCmd() { - t := suite.T() - - ctx, flush := tester.NewContext(t) - ctx = config.SetViper(ctx, suite.dpnd.vpr) - - defer flush() - - cmd := cliTD.StubRootCmd( - "backup", "delete", "teams", - "--config-file", suite.dpnd.configFilePath, - "--"+flags.BackupFN, string(suite.backupOp.Results.BackupID)) - cli.BuildCommandTree(cmd) - - // run the command - err := cmd.ExecuteContext(ctx) - require.NoError(t, err, clues.ToCore(err)) - - // a follow-up details call should fail, due to the backup ID being deleted - cmd = cliTD.StubRootCmd( - "backup", "details", "teams", - "--config-file", suite.dpnd.configFilePath, - "--backup", string(suite.backupOp.Results.BackupID)) - cli.BuildCommandTree(cmd) - - err = cmd.ExecuteContext(ctx) - require.Error(t, err, clues.ToCore(err)) -} - -func (suite *BackupDeleteTeamsE2ESuite) TestTeamsBackupDeleteCmd_UnknownID() { - t := suite.T() - - ctx, flush := tester.NewContext(t) - ctx = config.SetViper(ctx, suite.dpnd.vpr) - - defer flush() - - cmd := cliTD.StubRootCmd( - "backup", "delete", "teams", - "--config-file", suite.dpnd.configFilePath, - "--"+flags.BackupFN, uuid.NewString()) - cli.BuildCommandTree(cmd) - - // unknown backupIDs should error since the modelStore can't find the backup - err := cmd.ExecuteContext(ctx) - require.Error(t, err, clues.ToCore(err)) -} - -// --------------------------------------------------------------------------- -// helpers -// --------------------------------------------------------------------------- - -func buildTeamsBackupCmd( - ctx context.Context, - configFile, team, category string, - recorder *strings.Builder, -) (*cobra.Command, context.Context) { - cmd := cliTD.StubRootCmd( - "backup", "create", "teams", - "--config-file", configFile, - "--"+flags.TeamFN, team, - "--"+flags.CategoryDataFN, category) - cli.BuildCommandTree(cmd) - cmd.SetOut(recorder) - - return cmd, print.SetRootCmd(ctx, cmd) -} diff --git a/src/cli/backup/teams_test.go b/src/cli/backup/teams_test.go deleted file mode 100644 index 60b209db2..000000000 --- a/src/cli/backup/teams_test.go +++ /dev/null @@ -1,139 +0,0 @@ -package backup - -import ( - "testing" - - "github.com/alcionai/clues" - "github.com/spf13/cobra" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - - "github.com/alcionai/corso/src/cli/flags" - "github.com/alcionai/corso/src/internal/tester" -) - -type TeamsUnitSuite struct { - tester.Suite -} - -func TestTeamsUnitSuite(t *testing.T) { - suite.Run(t, &TeamsUnitSuite{Suite: tester.NewUnitSuite(t)}) -} - -func (suite *TeamsUnitSuite) TestAddTeamsCommands() { - expectUse := teamsServiceCommand - - table := []struct { - name string - use string - expectUse string - expectShort string - flags []string - expectRunE func(*cobra.Command, []string) error - }{ - { - "create teams", - createCommand, - expectUse + " " + teamsServiceCommandCreateUseSuffix, - teamsCreateCmd().Short, - []string{ - flags.CategoryDataFN, - flags.FailFastFN, - flags.FetchParallelismFN, - flags.SkipReduceFN, - flags.NoStatsFN, - }, - createTeamsCmd, - }, - { - "list teams", - listCommand, - expectUse, - teamsListCmd().Short, - []string{ - flags.BackupFN, - flags.FailedItemsFN, - flags.SkippedItemsFN, - flags.RecoveredErrorsFN, - }, - listTeamsCmd, - }, - { - "details teams", - detailsCommand, - expectUse + " " + teamsServiceCommandDetailsUseSuffix, - teamsDetailsCmd().Short, - []string{ - flags.BackupFN, - }, - detailsTeamsCmd, - }, - { - "delete teams", - deleteCommand, - expectUse + " " + teamsServiceCommandDeleteUseSuffix, - teamsDeleteCmd().Short, - []string{flags.BackupFN}, - deleteTeamsCmd, - }, - } - for _, test := range table { - suite.Run(test.name, func() { - t := suite.T() - - cmd := &cobra.Command{Use: test.use} - - c := addTeamsCommands(cmd) - require.NotNil(t, c) - - cmds := cmd.Commands() - require.Len(t, cmds, 1) - - child := cmds[0] - assert.Equal(t, test.expectUse, child.Use) - assert.Equal(t, test.expectShort, child.Short) - tester.AreSameFunc(t, test.expectRunE, child.RunE) - }) - } -} - -func (suite *TeamsUnitSuite) TestValidateTeamsBackupCreateFlags() { - table := []struct { - name string - cats []string - expect assert.ErrorAssertionFunc - }{ - { - name: "none", - cats: []string{}, - expect: assert.NoError, - }, - { - name: "libraries", - cats: []string{flags.DataLibraries}, - expect: assert.NoError, - }, - { - name: "messages", - cats: []string{flags.DataMessages}, - expect: assert.NoError, - }, - { - name: "all allowed", - cats: []string{flags.DataLibraries, flags.DataMessages}, - expect: assert.NoError, - }, - { - name: "bad inputs", - cats: []string{"foo"}, - expect: assert.Error, - }, - } - for _, test := range table { - suite.Run(test.name, func() { - err := validateTeamsBackupCreateFlags([]string{"*"}, test.cats) - test.expect(suite.T(), err, clues.ToCore(err)) - }) - } -} diff --git a/src/cli/export/export.go b/src/cli/export/export.go index 20dd0f155..89a2111fc 100644 --- a/src/cli/export/export.go +++ b/src/cli/export/export.go @@ -39,7 +39,6 @@ func AddCommands(cmd *cobra.Command) { // delete after release if len(os.Getenv("CORSO_ENABLE_GROUPS")) > 0 { addGroupsCommands(exportC) - addTeamsCommands(exportC) } } diff --git a/src/cli/export/groups.go b/src/cli/export/groups.go index a3439e27c..130f3bd66 100644 --- a/src/cli/export/groups.go +++ b/src/cli/export/groups.go @@ -39,6 +39,7 @@ func addGroupsCommands(cmd *cobra.Command) *cobra.Command { // TODO: correct examples const ( groupsServiceCommand = "groups" + teamsServiceCommand = "teams" groupsServiceCommandUseSuffix = " --backup " //nolint:lll @@ -57,9 +58,10 @@ corso export groups my-exports --backup 1234abcd-12ab-cd34-56de-1234abcd // `corso export groups [...] ` func groupsExportCmd() *cobra.Command { return &cobra.Command{ - Use: groupsServiceCommand, - Short: "Export M365 Groups service data", - RunE: exportGroupsCmd, + Use: groupsServiceCommand, + Aliases: []string{teamsServiceCommand}, + Short: "Export M365 Groups service data", + RunE: exportGroupsCmd, Args: func(cmd *cobra.Command, args []string) error { if len(args) != 1 { return errors.New("missing export destination") diff --git a/src/cli/export/teams.go b/src/cli/export/teams.go deleted file mode 100644 index 51a017534..000000000 --- a/src/cli/export/teams.go +++ /dev/null @@ -1,96 +0,0 @@ -package export - -import ( - "github.com/pkg/errors" - "github.com/spf13/cobra" - "github.com/spf13/pflag" - - "github.com/alcionai/corso/src/cli/flags" - "github.com/alcionai/corso/src/cli/utils" -) - -// called by export.go to map subcommands to provider-specific handling. -func addTeamsCommands(cmd *cobra.Command) *cobra.Command { - var ( - c *cobra.Command - fs *pflag.FlagSet - ) - - switch cmd.Use { - case exportCommand: - c, fs = utils.AddCommand(cmd, teamsExportCmd(), utils.MarkPreReleaseCommand()) - - c.Use = c.Use + " " + teamsServiceCommandUseSuffix - - // Flags addition ordering should follow the order we want them to appear in help and docs: - // More generic (ex: --user) and more frequently used flags take precedence. - fs.SortFlags = false - - flags.AddBackupIDFlag(c, true) - flags.AddExportConfigFlags(c) - flags.AddFailFastFlag(c) - flags.AddCorsoPassphaseFlags(c) - flags.AddAWSCredsFlags(c) - } - - return c -} - -// TODO: correct examples -const ( - teamsServiceCommand = "teams" - teamsServiceCommandUseSuffix = " --backup " - - //nolint:lll - teamsServiceCommandExportExamples = `# Export file with ID 98765abcdef in Bob's last backup (1234abcd...) to my-exports directory -corso export teams my-exports --backup 1234abcd-12ab-cd34-56de-1234abcd --file 98765abcdef - -# Export files named "FY2021 Planning.xlsx" in "Documents/Finance Reports" to current directory -corso export teams . --backup 1234abcd-12ab-cd34-56de-1234abcd \ - --file "FY2021 Planning.xlsx" --folder "Documents/Finance Reports" - -# Export all files and folders in folder "Documents/Finance Reports" that were created before 2020 to my-exports -corso export teams my-exports --backup 1234abcd-12ab-cd34-56de-1234abcd - --folder "Documents/Finance Reports" --file-created-before 2020-01-01T00:00:00` -) - -// `corso export teams [...] ` -func teamsExportCmd() *cobra.Command { - return &cobra.Command{ - Use: teamsServiceCommand, - Short: "Export M365 Teams service data", - RunE: exportTeamsCmd, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) != 1 { - return errors.New("missing export destination") - } - - return nil - }, - Example: teamsServiceCommandExportExamples, - } -} - -// processes an teams service export. -func exportTeamsCmd(cmd *cobra.Command, args []string) error { - ctx := cmd.Context() - - if utils.HasNoFlagsAndShownHelp(cmd) { - return nil - } - - opts := utils.MakeGroupsOpts(cmd) - - if flags.RunModeFV == flags.RunModeFlagTest { - return nil - } - - if err := utils.ValidateGroupsRestoreFlags(flags.BackupIDFV, opts); err != nil { - return err - } - - sel := utils.IncludeGroupsRestoreDataSelectors(ctx, opts) - utils.FilterGroupsRestoreInfoSelectors(sel, opts) - - return runExport(ctx, cmd, args, opts.ExportCfg, sel.Selector, flags.BackupIDFV, "Teams") -} diff --git a/src/cli/export/teams_test.go b/src/cli/export/teams_test.go deleted file mode 100644 index 11ab2baf8..000000000 --- a/src/cli/export/teams_test.go +++ /dev/null @@ -1,93 +0,0 @@ -package export - -import ( - "bytes" - "testing" - - "github.com/alcionai/clues" - "github.com/spf13/cobra" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - - "github.com/alcionai/corso/src/cli/flags" - "github.com/alcionai/corso/src/cli/utils" - "github.com/alcionai/corso/src/cli/utils/testdata" - "github.com/alcionai/corso/src/internal/tester" -) - -type TeamsUnitSuite struct { - tester.Suite -} - -func TestTeamsUnitSuite(t *testing.T) { - suite.Run(t, &TeamsUnitSuite{Suite: tester.NewUnitSuite(t)}) -} - -func (suite *TeamsUnitSuite) TestAddTeamsCommands() { - expectUse := teamsServiceCommand + " " + teamsServiceCommandUseSuffix - - table := []struct { - name string - use string - expectUse string - expectShort string - expectRunE func(*cobra.Command, []string) error - }{ - {"export teams", exportCommand, expectUse, teamsExportCmd().Short, exportTeamsCmd}, - } - for _, test := range table { - suite.Run(test.name, func() { - t := suite.T() - - cmd := &cobra.Command{Use: test.use} - - // normally a persistent flag from the root. - // required to ensure a dry run. - flags.AddRunModeFlag(cmd, true) - - c := addTeamsCommands(cmd) - require.NotNil(t, c) - - cmds := cmd.Commands() - require.Len(t, cmds, 1) - - child := cmds[0] - assert.Equal(t, test.expectUse, child.Use) - assert.Equal(t, test.expectShort, child.Short) - tester.AreSameFunc(t, test.expectRunE, child.RunE) - - cmd.SetArgs([]string{ - "teams", - testdata.RestoreDestination, - "--" + flags.RunModeFN, flags.RunModeFlagTest, - "--" + flags.BackupFN, testdata.BackupInput, - - "--" + flags.AWSAccessKeyFN, testdata.AWSAccessKeyID, - "--" + flags.AWSSecretAccessKeyFN, testdata.AWSSecretAccessKey, - "--" + flags.AWSSessionTokenFN, testdata.AWSSessionToken, - - "--" + flags.CorsoPassphraseFN, testdata.CorsoPassphrase, - - // bool flags - "--" + flags.ArchiveFN, - }) - - cmd.SetOut(new(bytes.Buffer)) // drop output - cmd.SetErr(new(bytes.Buffer)) // drop output - err := cmd.Execute() - assert.NoError(t, err, clues.ToCore(err)) - - opts := utils.MakeGroupsOpts(cmd) - assert.Equal(t, testdata.BackupInput, flags.BackupIDFV) - - assert.Equal(t, testdata.Archive, opts.ExportCfg.Archive) - - assert.Equal(t, testdata.AWSAccessKeyID, flags.AWSAccessKeyFV) - assert.Equal(t, testdata.AWSSecretAccessKey, flags.AWSSecretAccessKeyFV) - assert.Equal(t, testdata.AWSSessionToken, flags.AWSSessionTokenFV) - - assert.Equal(t, testdata.CorsoPassphrase, flags.CorsoPassphraseFV) - }) - } -} diff --git a/src/cli/restore/groups.go b/src/cli/restore/groups.go index 3907b17d0..fd4927685 100644 --- a/src/cli/restore/groups.go +++ b/src/cli/restore/groups.go @@ -41,6 +41,7 @@ func addGroupsCommands(cmd *cobra.Command) *cobra.Command { // TODO: correct examples const ( groupsServiceCommand = "groups" + teamsServiceCommand = "teams" groupsServiceCommandUseSuffix = "--backup " groupsServiceCommandRestoreExamples = `# Restore file with ID 98765abcdef in Bob's last backup (1234abcd...) @@ -62,6 +63,7 @@ corso restore groups --backup 1234abcd-12ab-cd34-56de-1234abcd func groupsRestoreCmd() *cobra.Command { return &cobra.Command{ Use: groupsServiceCommand, + Aliases: []string{teamsServiceCommand}, Short: "Restore M365 Groups service data", RunE: restoreGroupsCmd, Args: cobra.NoArgs, diff --git a/src/cli/restore/restore.go b/src/cli/restore/restore.go index 444f713a5..3f62ab0ae 100644 --- a/src/cli/restore/restore.go +++ b/src/cli/restore/restore.go @@ -38,7 +38,6 @@ func AddCommands(cmd *cobra.Command) { // delete after release if len(os.Getenv("CORSO_ENABLE_GROUPS")) > 0 { addGroupsCommands(restoreC) - addTeamsCommands(restoreC) } } diff --git a/src/cli/restore/teams.go b/src/cli/restore/teams.go deleted file mode 100644 index 059c2182a..000000000 --- a/src/cli/restore/teams.go +++ /dev/null @@ -1,81 +0,0 @@ -package restore - -import ( - "github.com/spf13/cobra" - "github.com/spf13/pflag" - - "github.com/alcionai/corso/src/cli/flags" - . "github.com/alcionai/corso/src/cli/print" - "github.com/alcionai/corso/src/cli/utils" -) - -// called by restore.go to map subcommands to provider-specific handling. -func addTeamsCommands(cmd *cobra.Command) *cobra.Command { - var ( - c *cobra.Command - fs *pflag.FlagSet - ) - - switch cmd.Use { - case restoreCommand: - c, fs = utils.AddCommand(cmd, teamsRestoreCmd(), utils.MarkPreReleaseCommand()) - - c.Use = c.Use + " " + teamsServiceCommandUseSuffix - - // Flags addition ordering should follow the order we want them to appear in help and docs: - // More generic (ex: --user) and more frequently used flags take precedence. - fs.SortFlags = false - - flags.AddBackupIDFlag(c, true) - flags.AddRestorePermissionsFlag(c) - flags.AddRestoreConfigFlags(c) - flags.AddFailFastFlag(c) - flags.AddCorsoPassphaseFlags(c) - flags.AddAWSCredsFlags(c) - flags.AddAzureCredsFlags(c) - } - - return c -} - -// TODO: correct examples -const ( - teamsServiceCommand = "teams" - teamsServiceCommandUseSuffix = "--backup " - - teamsServiceCommandRestoreExamples = `# Restore file with ID 98765abcdef in Bob's last backup (1234abcd...) -corso restore teams --backup 1234abcd-12ab-cd34-56de-1234abcd --file 98765abcdef - -# Restore the file with ID 98765abcdef along with its associated permissions -corso restore teams --backup 1234abcd-12ab-cd34-56de-1234abcd --file 98765abcdef --restore-permissions - -# Restore files named "FY2021 Planning.xlsx" in "Documents/Finance Reports" -corso restore teams --backup 1234abcd-12ab-cd34-56de-1234abcd \ - --file "FY2021 Planning.xlsx" --folder "Documents/Finance Reports" - -# Restore all files and folders in folder "Documents/Finance Reports" that were created before 2020 -corso restore teams --backup 1234abcd-12ab-cd34-56de-1234abcd - --folder "Documents/Finance Reports" --file-created-before 2020-01-01T00:00:00` -) - -// `corso restore teams [...]` -func teamsRestoreCmd() *cobra.Command { - return &cobra.Command{ - Use: teamsServiceCommand, - Short: "Restore M365 Teams service data", - RunE: restoreTeamsCmd, - Args: cobra.NoArgs, - Example: teamsServiceCommandRestoreExamples, - } -} - -// processes an teams service restore. -func restoreTeamsCmd(cmd *cobra.Command, args []string) error { - ctx := cmd.Context() - - if utils.HasNoFlagsAndShownHelp(cmd) { - return nil - } - - return Only(ctx, utils.ErrNotYetImplemented) -} diff --git a/src/cli/restore/teams_test.go b/src/cli/restore/teams_test.go deleted file mode 100644 index c24e9f439..000000000 --- a/src/cli/restore/teams_test.go +++ /dev/null @@ -1,108 +0,0 @@ -package restore - -import ( - "bytes" - "testing" - - "github.com/alcionai/clues" - "github.com/spf13/cobra" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - - "github.com/alcionai/corso/src/cli/flags" - "github.com/alcionai/corso/src/cli/utils" - "github.com/alcionai/corso/src/cli/utils/testdata" - "github.com/alcionai/corso/src/internal/tester" -) - -type TeamsUnitSuite struct { - tester.Suite -} - -func TestTeamsUnitSuite(t *testing.T) { - suite.Run(t, &TeamsUnitSuite{Suite: tester.NewUnitSuite(t)}) -} - -func (suite *TeamsUnitSuite) TestAddTeamsCommands() { - expectUse := teamsServiceCommand + " " + teamsServiceCommandUseSuffix - - table := []struct { - name string - use string - expectUse string - expectShort string - expectRunE func(*cobra.Command, []string) error - }{ - {"restore teams", restoreCommand, expectUse, teamsRestoreCmd().Short, restoreTeamsCmd}, - } - for _, test := range table { - suite.Run(test.name, func() { - t := suite.T() - - cmd := &cobra.Command{Use: test.use} - - // normally a persistent flag from the root. - // required to ensure a dry run. - flags.AddRunModeFlag(cmd, true) - - c := addTeamsCommands(cmd) - require.NotNil(t, c) - - cmds := cmd.Commands() - require.Len(t, cmds, 1) - - child := cmds[0] - assert.Equal(t, test.expectUse, child.Use) - assert.Equal(t, test.expectShort, child.Short) - tester.AreSameFunc(t, test.expectRunE, child.RunE) - - cmd.SetArgs([]string{ - "teams", - "--" + flags.RunModeFN, flags.RunModeFlagTest, - "--" + flags.BackupFN, testdata.BackupInput, - - "--" + flags.CollisionsFN, testdata.Collisions, - "--" + flags.DestinationFN, testdata.Destination, - "--" + flags.ToResourceFN, testdata.ToResource, - - "--" + flags.AWSAccessKeyFN, testdata.AWSAccessKeyID, - "--" + flags.AWSSecretAccessKeyFN, testdata.AWSSecretAccessKey, - "--" + flags.AWSSessionTokenFN, testdata.AWSSessionToken, - - "--" + flags.AzureClientIDFN, testdata.AzureClientID, - "--" + flags.AzureClientTenantFN, testdata.AzureTenantID, - "--" + flags.AzureClientSecretFN, testdata.AzureClientSecret, - - "--" + flags.CorsoPassphraseFN, testdata.CorsoPassphrase, - - // bool flags - "--" + flags.RestorePermissionsFN, - }) - - cmd.SetOut(new(bytes.Buffer)) // drop output - cmd.SetErr(new(bytes.Buffer)) // drop output - err := cmd.Execute() - // assert.NoError(t, err, clues.ToCore(err)) - assert.ErrorIs(t, err, utils.ErrNotYetImplemented, clues.ToCore(err)) - - opts := utils.MakeGroupsOpts(cmd) - assert.Equal(t, testdata.BackupInput, flags.BackupIDFV) - - assert.Equal(t, testdata.Collisions, opts.RestoreCfg.Collisions) - assert.Equal(t, testdata.Destination, opts.RestoreCfg.Destination) - assert.Equal(t, testdata.ToResource, opts.RestoreCfg.ProtectedResource) - - assert.Equal(t, testdata.AWSAccessKeyID, flags.AWSAccessKeyFV) - assert.Equal(t, testdata.AWSSecretAccessKey, flags.AWSSecretAccessKeyFV) - assert.Equal(t, testdata.AWSSessionToken, flags.AWSSessionTokenFV) - - assert.Equal(t, testdata.AzureClientID, flags.AzureClientIDFV) - assert.Equal(t, testdata.AzureTenantID, flags.AzureClientTenantFV) - assert.Equal(t, testdata.AzureClientSecret, flags.AzureClientSecretFV) - - assert.Equal(t, testdata.CorsoPassphrase, flags.CorsoPassphraseFV) - assert.True(t, flags.RestorePermissionsFV) - }) - } -}