Compare commits

...

6 Commits

Author SHA1 Message Date
ryanfkeepers
72771087a6 fix action script mistakes 2023-08-24 12:32:42 -06:00
ryanfkeepers
467983127f fix up issues found in manual testing 2023-08-23 16:28:44 -06:00
ryanfkeepers
27f97b3856 add groups, teams e2e tests
add e2e test suites for groups and teams backups.
These tests are currently skipped and waiting for
completion of e2e functionality.
2023-08-23 16:25:00 -06:00
ryanfkeepers
8f236019ad fix up e2e tests
fixing failures discovered from manual runs
2023-08-23 16:24:02 -06:00
ryanfkeepers
8ba7d08dc0 cleanup backup e2e in prep for groups
some code shuffling and cleanup in preparation to add
groups and teams e2e tests.
2023-08-23 14:26:55 -06:00
ryanfkeepers
c8888ad8ed add sanity test boilerplate for groups 2023-08-23 12:16:02 -06:00
9 changed files with 1698 additions and 493 deletions

View File

@ -12,7 +12,7 @@ inputs:
required: false required: false
default: "" default: ""
restore-args: restore-args:
description: Arguments to pass for restore description: Arguments to pass for restore; restore is skipped when missing.
required: false required: false
default: "" default: ""
test-folder: test-folder:
@ -28,6 +28,10 @@ inputs:
description: Value for the --collisions flag description: Value for the --collisions flag
requried: false requried: false
default: "replace" default: "replace"
with-export:
description: Runs export tests when true
required: false
default: false
outputs: outputs:
backup-id: backup-id:
@ -52,6 +56,7 @@ runs:
tee $GITHUB_OUTPUT tee $GITHUB_OUTPUT
- name: Restore ${{ inputs.service }} ${{ inputs.kind }} - name: Restore ${{ inputs.service }} ${{ inputs.kind }}
if: ${{ inputs.restore-args != "" }}
id: restore id: restore
shell: bash shell: bash
working-directory: src working-directory: src
@ -73,6 +78,7 @@ runs:
cat /tmp/corsologs cat /tmp/corsologs
- name: Check restore ${{ inputs.service }} ${{ inputs.kind }} - name: Check restore ${{ inputs.service }} ${{ inputs.kind }}
if: ${{ inputs.restore != "" }}
shell: bash shell: bash
working-directory: src working-directory: src
env: env:
@ -86,10 +92,10 @@ runs:
./sanity-test ./sanity-test
- name: Export ${{ inputs.service }} ${{ inputs.kind }} - name: Export ${{ inputs.service }} ${{ inputs.kind }}
if: ${{ inputs.with-export == true }}
id: export id: export
shell: bash shell: bash
working-directory: src working-directory: src
if: ${{ inputs.service == 'onedrive' || inputs.service == 'sharepoint' }}
run: | run: |
set -euo pipefail set -euo pipefail
CORSO_LOG_FILE=${{ inputs.log-dir }}/gotest-restore-${{ inputs.service }}-${{inputs.kind }}.log CORSO_LOG_FILE=${{ inputs.log-dir }}/gotest-restore-${{ inputs.service }}-${{inputs.kind }}.log
@ -103,9 +109,9 @@ runs:
cat /tmp/corsologs cat /tmp/corsologs
- name: Check export ${{ inputs.service }} ${{ inputs.kind }} - name: Check export ${{ inputs.service }} ${{ inputs.kind }}
if: ${{ inputs.with-export == true }}
shell: bash shell: bash
working-directory: src working-directory: src
if: ${{ inputs.service == 'onedrive' || inputs.service == 'sharepoint' }}
env: env:
SANITY_TEST_KIND: export SANITY_TEST_KIND: export
SANITY_TEST_FOLDER: /tmp/export-${{ inputs.service }}-${{inputs.kind }} SANITY_TEST_FOLDER: /tmp/export-${{ inputs.service }}-${{inputs.kind }}
@ -117,10 +123,10 @@ runs:
./sanity-test ./sanity-test
- name: Export archive ${{ inputs.service }} ${{ inputs.kind }} - name: Export archive ${{ inputs.service }} ${{ inputs.kind }}
if: ${{ inputs.with-export == true }}
id: export-archive id: export-archive
shell: bash shell: bash
working-directory: src working-directory: src
if: ${{ inputs.service == 'onedrive' }} # Export only available for OneDrive
run: | run: |
set -euo pipefail set -euo pipefail
CORSO_LOG_FILE=${{ inputs.log-dir }}/gotest-restore-${{ inputs.service }}-${{inputs.kind }}.log CORSO_LOG_FILE=${{ inputs.log-dir }}/gotest-restore-${{ inputs.service }}-${{inputs.kind }}.log
@ -137,9 +143,9 @@ runs:
cat /tmp/corsologs cat /tmp/corsologs
- name: Check archive export ${{ inputs.service }} ${{ inputs.kind }} - name: Check archive export ${{ inputs.service }} ${{ inputs.kind }}
if: ${{ inputs.with-export == true }}
shell: bash shell: bash
working-directory: src working-directory: src
if: ${{ inputs.service == 'onedrive' }}
env: env:
SANITY_TEST_KIND: export SANITY_TEST_KIND: export
SANITY_TEST_FOLDER: /tmp/export-${{ inputs.service }}-${{inputs.kind }}-unzipped SANITY_TEST_FOLDER: /tmp/export-${{ inputs.service }}-${{inputs.kind }}-unzipped

View File

@ -173,6 +173,7 @@ jobs:
restore-args: '--email-folder ${{ env.RESTORE_DEST_PFX }}${{ steps.repo-init.outputs.result }}' restore-args: '--email-folder ${{ env.RESTORE_DEST_PFX }}${{ steps.repo-init.outputs.result }}'
test-folder: '${{ env.RESTORE_DEST_PFX }}${{ steps.repo-init.outputs.result }}' test-folder: '${{ env.RESTORE_DEST_PFX }}${{ steps.repo-init.outputs.result }}'
log-dir: ${{ env.CORSO_LOG_DIR }} log-dir: ${{ env.CORSO_LOG_DIR }}
run-e
- name: Exchange - Incremental backup - name: Exchange - Incremental backup
id: exchange-backup-incremental id: exchange-backup-incremental
@ -241,6 +242,7 @@ jobs:
restore-args: '--folder ${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-onedrive.outputs.result }} --restore-permissions' restore-args: '--folder ${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-onedrive.outputs.result }} --restore-permissions'
test-folder: '${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-onedrive.outputs.result }}' test-folder: '${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-onedrive.outputs.result }}'
log-dir: ${{ env.CORSO_LOG_DIR }} log-dir: ${{ env.CORSO_LOG_DIR }}
with-export: true
# generate some more enteries for incremental check # generate some more enteries for incremental check
- name: OneDrive - Create new data (for incremental) - name: OneDrive - Create new data (for incremental)
@ -263,6 +265,7 @@ jobs:
restore-args: '--folder ${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-onedrive.outputs.result }} --restore-permissions' restore-args: '--folder ${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-onedrive.outputs.result }} --restore-permissions'
test-folder: '${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-onedrive.outputs.result }}' test-folder: '${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-onedrive.outputs.result }}'
log-dir: ${{ env.CORSO_LOG_DIR }} log-dir: ${{ env.CORSO_LOG_DIR }}
with-export: true
########################################################################################################################################## ##########################################################################################################################################
@ -295,6 +298,7 @@ jobs:
restore-args: '--folder ${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-sharepoint.outputs.result }} --restore-permissions' restore-args: '--folder ${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-sharepoint.outputs.result }} --restore-permissions'
test-folder: '${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-sharepoint.outputs.result }}' test-folder: '${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-sharepoint.outputs.result }}'
log-dir: ${{ env.CORSO_LOG_DIR }} log-dir: ${{ env.CORSO_LOG_DIR }}
with-export: true
# generate some more enteries for incremental check # generate some more enteries for incremental check
- name: SharePoint - Create new data (for incremental) - name: SharePoint - Create new data (for incremental)
@ -318,6 +322,53 @@ jobs:
restore-args: '--folder ${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-sharepoint.outputs.result }} --restore-permissions' restore-args: '--folder ${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-sharepoint.outputs.result }} --restore-permissions'
test-folder: '${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-sharepoint.outputs.result }}' test-folder: '${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-sharepoint.outputs.result }}'
log-dir: ${{ env.CORSO_LOG_DIR }} log-dir: ${{ env.CORSO_LOG_DIR }}
with-export: true
##########################################################################################################################################
# Groups and Teams
# generate new entries for test
- name: Groups - Create new data
if: false # TODO: enable when ready
id: new-data-creation-groups
working-directory: ./src/cmd/factory
run: |
suffix=$(date +"%Y-%m-%d_%H-%M-%S")
go run . sharepoint files \
--site ${{ secrets.CORSO_M365_TEST_GROUPS_SITE_URL }} \
--user ${{ env.TEST_USER }} \
--secondaryuser ${{ env.CORSO_SECONDARY_M365_TEST_USER_ID }} \
--tenant ${{ secrets.TENANT_ID }} \
--destination ${{ env.RESTORE_DEST_PFX }}$suffix \
--count 4
echo result="${suffix}" >> $GITHUB_OUTPUT
- name: Groups - Backup
if: false # TODO: enable when ready
id: groups-backup
uses: ./.github/actions/backup-restore-test
with:
service: groups
kind: initial
backup-args: '--group "${{ secrets.CORSO_M365_TEST_TEAM_ID }}"'
test-folder: '${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-groups.outputs.result }}'
log-dir: ${{ env.CORSO_LOG_DIR }}
- name: Teams - Backup
if: false # TODO: enable when ready
id: teams-backup
uses: ./.github/actions/backup-restore-test
with:
service: teams
kind: initial
backup-args: '--group "${{ secrets.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
########################################################################################################################################## ##########################################################################################################################################

View File

@ -1,15 +1,12 @@
package backup_test package backup_test
import ( import (
"context"
"fmt" "fmt"
"strings" "strings"
"testing" "testing"
"github.com/alcionai/clues" "github.com/alcionai/clues"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
@ -23,12 +20,9 @@ import (
"github.com/alcionai/corso/src/internal/operations" "github.com/alcionai/corso/src/internal/operations"
"github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/internal/tester/tconfig" "github.com/alcionai/corso/src/internal/tester/tconfig"
"github.com/alcionai/corso/src/pkg/account"
"github.com/alcionai/corso/src/pkg/path" "github.com/alcionai/corso/src/pkg/path"
"github.com/alcionai/corso/src/pkg/repository"
"github.com/alcionai/corso/src/pkg/selectors" "github.com/alcionai/corso/src/pkg/selectors"
"github.com/alcionai/corso/src/pkg/services/m365/api" "github.com/alcionai/corso/src/pkg/services/m365/api"
"github.com/alcionai/corso/src/pkg/storage"
storeTD "github.com/alcionai/corso/src/pkg/storage/testdata" storeTD "github.com/alcionai/corso/src/pkg/storage/testdata"
) )
@ -39,204 +33,17 @@ var (
) )
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// tests with azure flags in exchange create // tests that depend on no backups existing
// ---------------------------------------------------------------------------
type ExchangeCMDWithFlagsE2ESuite struct {
tester.Suite
acct account.Account
st storage.Storage
vpr *viper.Viper
cfgFP string
repo repository.Repository
m365UserID string
recorder strings.Builder
}
func TestExchangeCMDWithFlagsE2ESuite(t *testing.T) {
suite.Run(t, &ExchangeCMDWithFlagsE2ESuite{
Suite: tester.NewE2ESuite(
t,
[][]string{storeTD.AWSStorageCredEnvs, tconfig.M365AcctCredEnvs}),
})
}
func (suite *ExchangeCMDWithFlagsE2ESuite) SetupSuite() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
acct, st, repo, vpr, recorder, cfgFilePath := prepM365Test(t, ctx)
suite.acct = acct
suite.st = st
suite.repo = repo
suite.vpr = vpr
suite.recorder = recorder
suite.cfgFP = cfgFilePath
suite.m365UserID = tconfig.M365UserID(t)
}
func (suite *ExchangeCMDWithFlagsE2ESuite) TestBackupCreateExchange_badAzureClientID() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
suite.recorder.Reset()
cmd := cliTD.StubRootCmd(
"backup", "create", "exchange",
"--user", suite.m365UserID,
"--azure-client-id", "invalid-value",
)
cli.BuildCommandTree(cmd)
cmd.SetErr(&suite.recorder)
ctx = print.SetRootCmd(ctx, cmd)
// run the command
err := cmd.ExecuteContext(ctx)
require.Error(t, err, clues.ToCore(err))
}
func (suite *ExchangeCMDWithFlagsE2ESuite) TestBackupCreateExchange_azureIDFromConfigFile() {
t := suite.T()
ctx, flush := tester.NewContext(t)
ctx = config.SetViper(ctx, suite.vpr)
defer flush()
suite.recorder.Reset()
cmd := cliTD.StubRootCmd(
"backup", "create", "exchange",
"--user", suite.m365UserID,
"--config-file", suite.cfgFP)
cli.BuildCommandTree(cmd)
cmd.SetOut(&suite.recorder)
ctx = print.SetRootCmd(ctx, cmd)
// run the command
err := cmd.ExecuteContext(ctx)
require.NoError(t, err, clues.ToCore(err))
result := suite.recorder.String()
t.Log("backup results", result)
// as an offhand check: the result should contain the m365 user id
assert.Contains(t, result, suite.m365UserID)
}
func (suite *ExchangeCMDWithFlagsE2ESuite) TestExchangeBackupValueFromEnvCmd_empty() {
t := suite.T()
ctx, flush := tester.NewContext(t)
ctx = config.SetViper(ctx, suite.vpr)
defer flush()
suite.recorder.Reset()
cmd := cliTD.StubRootCmd(
"backup", "create", "exchange",
"--user", suite.m365UserID,
"--config-file", suite.cfgFP)
cli.BuildCommandTree(cmd)
cmd.SetOut(&suite.recorder)
ctx = print.SetRootCmd(ctx, cmd)
// run the command
err := cmd.ExecuteContext(ctx)
require.NoError(t, err, clues.ToCore(err))
result := suite.recorder.String()
t.Log("backup results", result)
// as an offhand check: the result should contain the m365 user id
assert.Contains(t, result, suite.m365UserID)
}
// AWS flags
func (suite *ExchangeCMDWithFlagsE2ESuite) TestExchangeBackupInvalidAWSClientIDCmd_empty() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
suite.recorder.Reset()
cmd := cliTD.StubRootCmd(
"backup", "create", "exchange",
"--user", suite.m365UserID,
"--aws-access-key", "invalid-value",
"--aws-secret-access-key", "some-invalid-value",
)
cli.BuildCommandTree(cmd)
cmd.SetOut(&suite.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))
}
func (suite *ExchangeCMDWithFlagsE2ESuite) TestExchangeBackupAWSValueFromEnvCmd_empty() {
t := suite.T()
ctx, flush := tester.NewContext(t)
ctx = config.SetViper(ctx, suite.vpr)
defer flush()
suite.recorder.Reset()
cmd := cliTD.StubRootCmd(
"backup", "create", "exchange",
"--user", suite.m365UserID,
"--config-file", suite.cfgFP)
cli.BuildCommandTree(cmd)
cmd.SetOut(&suite.recorder)
ctx = print.SetRootCmd(ctx, cmd)
// run the command
err := cmd.ExecuteContext(ctx)
require.NoError(t, err, clues.ToCore(err))
result := suite.recorder.String()
t.Log("backup results", result)
// as an offhand check: the result should contain the m365 user id
assert.Contains(t, result, suite.m365UserID)
}
// ---------------------------------------------------------------------------
// tests with no backups
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
type NoBackupExchangeE2ESuite struct { type NoBackupExchangeE2ESuite struct {
tester.Suite tester.Suite
acct account.Account dpnd dependencies
st storage.Storage its intgTesterSetup
vpr *viper.Viper
cfgFP string
repo repository.Repository
m365UserID string
recorder strings.Builder
} }
func TestNoBackupExchangeE2ESuite(t *testing.T) { func TestNoBackupExchangeE2ESuite(t *testing.T) {
suite.Run(t, &NoBackupExchangeE2ESuite{Suite: tester.NewE2ESuite( suite.Run(t, &BackupExchangeE2ESuite{Suite: tester.NewE2ESuite(
t, t,
[][]string{storeTD.AWSStorageCredEnvs, tconfig.M365AcctCredEnvs}, [][]string{storeTD.AWSStorageCredEnvs, tconfig.M365AcctCredEnvs},
)}) )})
@ -248,32 +55,25 @@ func (suite *NoBackupExchangeE2ESuite) SetupSuite() {
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
defer flush() defer flush()
acct, st, repo, vpr, recorder, cfgFilePath := prepM365Test(t, ctx) suite.its = newIntegrationTesterSetup(t)
suite.dpnd = prepM365Test(t, ctx)
suite.acct = acct
suite.st = st
suite.repo = repo
suite.vpr = vpr
suite.recorder = recorder
suite.cfgFP = cfgFilePath
suite.m365UserID = tconfig.M365UserID(t)
} }
func (suite *NoBackupExchangeE2ESuite) TestExchangeBackupListCmd_empty() { func (suite *NoBackupExchangeE2ESuite) TestExchangeBackupListCmd_noBackups() {
t := suite.T() t := suite.T()
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
ctx = config.SetViper(ctx, suite.vpr) ctx = config.SetViper(ctx, suite.dpnd.vpr)
defer flush() defer flush()
suite.recorder.Reset() suite.dpnd.recorder.Reset()
cmd := cliTD.StubRootCmd( cmd := cliTD.StubRootCmd(
"backup", "list", "exchange", "backup", "list", "exchange",
"--config-file", suite.cfgFP) "--config-file", suite.dpnd.configFilePath)
cli.BuildCommandTree(cmd) cli.BuildCommandTree(cmd)
cmd.SetErr(&suite.recorder) cmd.SetErr(&suite.dpnd.recorder)
ctx = print.SetRootCmd(ctx, cmd) ctx = print.SetRootCmd(ctx, cmd)
@ -281,7 +81,7 @@ func (suite *NoBackupExchangeE2ESuite) TestExchangeBackupListCmd_empty() {
err := cmd.ExecuteContext(ctx) err := cmd.ExecuteContext(ctx)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
result := suite.recorder.String() result := suite.dpnd.recorder.String()
// as an offhand check: the result should contain the m365 user id // as an offhand check: the result should contain the m365 user id
assert.True(t, strings.HasSuffix(result, "No backups available\n")) assert.True(t, strings.HasSuffix(result, "No backups available\n"))
@ -293,12 +93,8 @@ func (suite *NoBackupExchangeE2ESuite) TestExchangeBackupListCmd_empty() {
type BackupExchangeE2ESuite struct { type BackupExchangeE2ESuite struct {
tester.Suite tester.Suite
acct account.Account dpnd dependencies
st storage.Storage its intgTesterSetup
vpr *viper.Viper
cfgFP string
repo repository.Repository
m365UserID string
} }
func TestBackupExchangeE2ESuite(t *testing.T) { func TestBackupExchangeE2ESuite(t *testing.T) {
@ -314,40 +110,39 @@ func (suite *BackupExchangeE2ESuite) SetupSuite() {
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
defer flush() defer flush()
acct, st, repo, vpr, _, cfgFilePath := prepM365Test(t, ctx) suite.its = newIntegrationTesterSetup(t)
suite.dpnd = prepM365Test(t, ctx)
suite.acct = acct
suite.st = st
suite.repo = repo
suite.vpr = vpr
suite.cfgFP = cfgFilePath
suite.m365UserID = tconfig.M365UserID(t)
} }
func (suite *BackupExchangeE2ESuite) TestExchangeBackupCmd_email() { func (suite *BackupExchangeE2ESuite) TestExchangeBackupCmd_email() {
runExchangeBackupCategoryTest(suite, "email") runExchangeBackupCategoryTest(suite, email)
} }
func (suite *BackupExchangeE2ESuite) TestExchangeBackupCmd_contacts() { func (suite *BackupExchangeE2ESuite) TestExchangeBackupCmd_contacts() {
runExchangeBackupCategoryTest(suite, "contacts") runExchangeBackupCategoryTest(suite, contacts)
} }
func (suite *BackupExchangeE2ESuite) TestExchangeBackupCmd_events() { func (suite *BackupExchangeE2ESuite) TestExchangeBackupCmd_events() {
runExchangeBackupCategoryTest(suite, "events") runExchangeBackupCategoryTest(suite, events)
} }
func runExchangeBackupCategoryTest(suite *BackupExchangeE2ESuite, category string) { func runExchangeBackupCategoryTest(suite *BackupExchangeE2ESuite, category path.CategoryType) {
recorder := strings.Builder{} recorder := strings.Builder{}
recorder.Reset() recorder.Reset()
t := suite.T() t := suite.T()
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
ctx = config.SetViper(ctx, suite.vpr) ctx = config.SetViper(ctx, suite.dpnd.vpr)
defer flush() defer flush()
cmd, ctx := buildExchangeBackupCmd(ctx, suite.cfgFP, suite.m365UserID, category, &recorder) cmd, ctx := buildExchangeBackupCmd(
ctx,
suite.dpnd.configFilePath,
suite.its.user.ID,
category.String(),
&recorder)
// run the command // run the command
err := cmd.ExecuteContext(ctx) err := cmd.ExecuteContext(ctx)
@ -357,21 +152,21 @@ func runExchangeBackupCategoryTest(suite *BackupExchangeE2ESuite, category strin
t.Log("backup results", result) t.Log("backup results", result)
// as an offhand check: the result should contain the m365 user id // as an offhand check: the result should contain the m365 user id
assert.Contains(t, result, suite.m365UserID) assert.Contains(t, result, suite.its.user.ID)
} }
func (suite *BackupExchangeE2ESuite) TestExchangeBackupCmd_ServiceNotEnabled_email() { func (suite *BackupExchangeE2ESuite) TestExchangeBackupCmd_ServiceNotEnabled_email() {
runExchangeBackupServiceNotEnabledTest(suite, "email") runExchangeBackupServiceNotEnabledTest(suite, email)
} }
func runExchangeBackupServiceNotEnabledTest(suite *BackupExchangeE2ESuite, category string) { func runExchangeBackupServiceNotEnabledTest(suite *BackupExchangeE2ESuite, category path.CategoryType) {
recorder := strings.Builder{} recorder := strings.Builder{}
recorder.Reset() recorder.Reset()
t := suite.T() t := suite.T()
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
ctx = config.SetViper(ctx, suite.vpr) ctx = config.SetViper(ctx, suite.dpnd.vpr)
defer flush() defer flush()
@ -379,9 +174,9 @@ func runExchangeBackupServiceNotEnabledTest(suite *BackupExchangeE2ESuite, categ
cmd, ctx := buildExchangeBackupCmd( cmd, ctx := buildExchangeBackupCmd(
ctx, ctx,
suite.cfgFP, suite.dpnd.configFilePath,
fmt.Sprintf("%s,%s", tconfig.UnlicensedM365UserID(suite.T()), suite.m365UserID), fmt.Sprintf("%s,%s", tconfig.UnlicensedM365UserID(suite.T()), suite.its.user.ID),
category, category.String(),
&recorder) &recorder)
err := cmd.ExecuteContext(ctx) err := cmd.ExecuteContext(ctx)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
@ -390,33 +185,38 @@ func runExchangeBackupServiceNotEnabledTest(suite *BackupExchangeE2ESuite, categ
t.Log("backup results", result) t.Log("backup results", result)
// as an offhand check: the result should contain the m365 user id // as an offhand check: the result should contain the m365 user id
assert.Contains(t, result, suite.m365UserID) assert.Contains(t, result, suite.its.user.ID)
} }
func (suite *BackupExchangeE2ESuite) TestExchangeBackupCmd_userNotFound_email() { func (suite *BackupExchangeE2ESuite) TestExchangeBackupCmd_userNotFound_email() {
runExchangeBackupUserNotFoundTest(suite, "email") runExchangeBackupUserNotFoundTest(suite, email)
} }
func (suite *BackupExchangeE2ESuite) TestExchangeBackupCmd_userNotFound_contacts() { func (suite *BackupExchangeE2ESuite) TestExchangeBackupCmd_userNotFound_contacts() {
runExchangeBackupUserNotFoundTest(suite, "contacts") runExchangeBackupUserNotFoundTest(suite, contacts)
} }
func (suite *BackupExchangeE2ESuite) TestExchangeBackupCmd_userNotFound_events() { func (suite *BackupExchangeE2ESuite) TestExchangeBackupCmd_userNotFound_events() {
runExchangeBackupUserNotFoundTest(suite, "events") runExchangeBackupUserNotFoundTest(suite, events)
} }
func runExchangeBackupUserNotFoundTest(suite *BackupExchangeE2ESuite, category string) { func runExchangeBackupUserNotFoundTest(suite *BackupExchangeE2ESuite, category path.CategoryType) {
recorder := strings.Builder{} recorder := strings.Builder{}
recorder.Reset() recorder.Reset()
t := suite.T() t := suite.T()
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
ctx = config.SetViper(ctx, suite.vpr) ctx = config.SetViper(ctx, suite.dpnd.vpr)
defer flush() defer flush()
cmd, ctx := buildExchangeBackupCmd(ctx, suite.cfgFP, "foo@not-there.com", category, &recorder) cmd, ctx := buildExchangeBackupCmd(
ctx,
suite.dpnd.configFilePath,
"foo@not-there.com",
category.String(),
&recorder)
// run the command // run the command
err := cmd.ExecuteContext(ctx) err := cmd.ExecuteContext(ctx)
@ -433,27 +233,104 @@ func runExchangeBackupUserNotFoundTest(suite *BackupExchangeE2ESuite, category s
t.Log("backup results", result) t.Log("backup results", result)
} }
func (suite *BackupExchangeE2ESuite) TestBackupCreateExchange_badAzureClientIDFlag() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
suite.dpnd.recorder.Reset()
cmd := cliTD.StubRootCmd(
"backup", "create", "exchange",
"--user", suite.its.user.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 *BackupExchangeE2ESuite) TestBackupCreateExchange_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", "exchange",
"--user", suite.its.user.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 user id
assert.Contains(t, result, suite.its.user.ID)
}
// AWS flags
func (suite *BackupExchangeE2ESuite) TestBackupCreateExchange_badAWSFlags() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
suite.dpnd.recorder.Reset()
cmd := cliTD.StubRootCmd(
"backup", "create", "exchange",
"--user", suite.its.user.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 // tests prepared with a previous backup
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
type PreparedBackupExchangeE2ESuite struct { type PreparedBackupExchangeE2ESuite struct {
tester.Suite tester.Suite
acct account.Account dpnd dependencies
st storage.Storage backupOps map[path.CategoryType]string
vpr *viper.Viper its intgTesterSetup
cfgFP string
repo repository.Repository
m365UserID string
backupOps map[path.CategoryType]string
recorder strings.Builder
} }
func TestPreparedBackupExchangeE2ESuite(t *testing.T) { func TestPreparedBackupExchangeE2ESuite(t *testing.T) {
suite.Run(t, &PreparedBackupExchangeE2ESuite{Suite: tester.NewE2ESuite( suite.Run(t, &PreparedBackupExchangeE2ESuite{
t, Suite: tester.NewE2ESuite(
[][]string{storeTD.AWSStorageCredEnvs, tconfig.M365AcctCredEnvs}, t,
)}) [][]string{storeTD.AWSStorageCredEnvs, tconfig.M365AcctCredEnvs}),
})
} }
func (suite *PreparedBackupExchangeE2ESuite) SetupSuite() { func (suite *PreparedBackupExchangeE2ESuite) SetupSuite() {
@ -462,20 +339,13 @@ func (suite *PreparedBackupExchangeE2ESuite) SetupSuite() {
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
defer flush() defer flush()
acct, st, repo, vpr, recorder, cfgFilePath := prepM365Test(t, ctx) suite.its = newIntegrationTesterSetup(t)
suite.dpnd = prepM365Test(t, ctx)
suite.acct = acct
suite.st = st
suite.repo = repo
suite.vpr = vpr
suite.recorder = recorder
suite.cfgFP = cfgFilePath
suite.m365UserID = tconfig.M365UserID(t)
suite.backupOps = make(map[path.CategoryType]string) suite.backupOps = make(map[path.CategoryType]string)
var ( var (
users = []string{suite.m365UserID} users = []string{suite.its.user.ID}
ins = idname.NewCache(map[string]string{suite.m365UserID: suite.m365UserID}) ins = idname.NewCache(map[string]string{suite.its.user.ID: suite.its.user.ID})
) )
for _, set := range []path.CategoryType{email, contacts, events} { for _, set := range []path.CategoryType{email, contacts, events} {
@ -497,7 +367,7 @@ func (suite *PreparedBackupExchangeE2ESuite) SetupSuite() {
sel.Include(scopes) sel.Include(scopes)
bop, err := suite.repo.NewBackupWithLookup(ctx, sel.Selector, ins) bop, err := suite.dpnd.repo.NewBackupWithLookup(ctx, sel.Selector, ins)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
err = bop.Run(ctx) err = bop.Run(ctx)
@ -506,11 +376,11 @@ func (suite *PreparedBackupExchangeE2ESuite) SetupSuite() {
bIDs := string(bop.Results.BackupID) bIDs := string(bop.Results.BackupID)
// sanity check, ensure we can find the backup and its details immediately // sanity check, ensure we can find the backup and its details immediately
b, err := suite.repo.Backup(ctx, string(bop.Results.BackupID)) b, err := suite.dpnd.repo.Backup(ctx, string(bop.Results.BackupID))
require.NoError(t, err, "retrieving recent backup by ID") require.NoError(t, err, "retrieving recent backup by ID")
require.Equal(t, bIDs, string(b.ID), "repo backup matches results id") require.Equal(t, bIDs, string(b.ID), "repo backup matches results id")
_, b, errs := suite.repo.GetBackupDetails(ctx, bIDs) _, b, errs := suite.dpnd.repo.GetBackupDetails(ctx, bIDs)
require.NoError(t, errs.Failure(), "retrieving recent backup details by ID") require.NoError(t, errs.Failure(), "retrieving recent backup details by ID")
require.Empty(t, errs.Recovered(), "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") require.Equal(t, bIDs, string(b.ID), "repo details matches results id")
@ -532,20 +402,20 @@ func (suite *PreparedBackupExchangeE2ESuite) TestExchangeListCmd_events() {
} }
func runExchangeListCmdTest(suite *PreparedBackupExchangeE2ESuite, category path.CategoryType) { func runExchangeListCmdTest(suite *PreparedBackupExchangeE2ESuite, category path.CategoryType) {
suite.recorder.Reset() suite.dpnd.recorder.Reset()
t := suite.T() t := suite.T()
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
ctx = config.SetViper(ctx, suite.vpr) ctx = config.SetViper(ctx, suite.dpnd.vpr)
defer flush() defer flush()
cmd := cliTD.StubRootCmd( cmd := cliTD.StubRootCmd(
"backup", "list", "exchange", "backup", "list", "exchange",
"--config-file", suite.cfgFP) "--config-file", suite.dpnd.configFilePath)
cli.BuildCommandTree(cmd) cli.BuildCommandTree(cmd)
cmd.SetOut(&suite.recorder) cmd.SetOut(&suite.dpnd.recorder)
ctx = print.SetRootCmd(ctx, cmd) ctx = print.SetRootCmd(ctx, cmd)
@ -554,7 +424,7 @@ func runExchangeListCmdTest(suite *PreparedBackupExchangeE2ESuite, category path
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
// compare the output // compare the output
result := suite.recorder.String() result := suite.dpnd.recorder.String()
assert.Contains(t, result, suite.backupOps[category]) assert.Contains(t, result, suite.backupOps[category])
} }
@ -571,12 +441,12 @@ func (suite *PreparedBackupExchangeE2ESuite) TestExchangeListCmd_singleID_events
} }
func runExchangeListSingleCmdTest(suite *PreparedBackupExchangeE2ESuite, category path.CategoryType) { func runExchangeListSingleCmdTest(suite *PreparedBackupExchangeE2ESuite, category path.CategoryType) {
suite.recorder.Reset() suite.dpnd.recorder.Reset()
t := suite.T() t := suite.T()
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
ctx = config.SetViper(ctx, suite.vpr) ctx = config.SetViper(ctx, suite.dpnd.vpr)
defer flush() defer flush()
@ -584,11 +454,11 @@ func runExchangeListSingleCmdTest(suite *PreparedBackupExchangeE2ESuite, categor
cmd := cliTD.StubRootCmd( cmd := cliTD.StubRootCmd(
"backup", "list", "exchange", "backup", "list", "exchange",
"--config-file", suite.cfgFP, "--config-file", suite.dpnd.configFilePath,
"--backup", string(bID)) "--backup", string(bID))
cli.BuildCommandTree(cmd) cli.BuildCommandTree(cmd)
cmd.SetOut(&suite.recorder) cmd.SetOut(&suite.dpnd.recorder)
ctx = print.SetRootCmd(ctx, cmd) ctx = print.SetRootCmd(ctx, cmd)
@ -597,7 +467,7 @@ func runExchangeListSingleCmdTest(suite *PreparedBackupExchangeE2ESuite, categor
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
// compare the output // compare the output
result := suite.recorder.String() result := suite.dpnd.recorder.String()
assert.Contains(t, result, bID) assert.Contains(t, result, bID)
} }
@ -605,13 +475,13 @@ func (suite *PreparedBackupExchangeE2ESuite) TestExchangeListCmd_badID() {
t := suite.T() t := suite.T()
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
ctx = config.SetViper(ctx, suite.vpr) ctx = config.SetViper(ctx, suite.dpnd.vpr)
defer flush() defer flush()
cmd := cliTD.StubRootCmd( cmd := cliTD.StubRootCmd(
"backup", "list", "exchange", "backup", "list", "exchange",
"--config-file", suite.cfgFP, "--config-file", suite.dpnd.configFilePath,
"--backup", "smarfs") "--backup", "smarfs")
cli.BuildCommandTree(cmd) cli.BuildCommandTree(cmd)
@ -635,28 +505,28 @@ func (suite *PreparedBackupExchangeE2ESuite) TestExchangeDetailsCmd_events() {
} }
func runExchangeDetailsCmdTest(suite *PreparedBackupExchangeE2ESuite, category path.CategoryType) { func runExchangeDetailsCmdTest(suite *PreparedBackupExchangeE2ESuite, category path.CategoryType) {
suite.recorder.Reset() suite.dpnd.recorder.Reset()
t := suite.T() t := suite.T()
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
ctx = config.SetViper(ctx, suite.vpr) ctx = config.SetViper(ctx, suite.dpnd.vpr)
defer flush() defer flush()
bID := suite.backupOps[category] bID := suite.backupOps[category]
// fetch the details from the repo first // fetch the details from the repo first
deets, _, errs := suite.repo.GetBackupDetails(ctx, string(bID)) deets, _, errs := suite.dpnd.repo.GetBackupDetails(ctx, string(bID))
require.NoError(t, errs.Failure(), clues.ToCore(errs.Failure())) require.NoError(t, errs.Failure(), clues.ToCore(errs.Failure()))
require.Empty(t, errs.Recovered()) require.Empty(t, errs.Recovered())
cmd := cliTD.StubRootCmd( cmd := cliTD.StubRootCmd(
"backup", "details", "exchange", "backup", "details", "exchange",
"--config-file", suite.cfgFP, "--config-file", suite.dpnd.configFilePath,
"--"+flags.BackupFN, string(bID)) "--"+flags.BackupFN, string(bID))
cli.BuildCommandTree(cmd) cli.BuildCommandTree(cmd)
cmd.SetOut(&suite.recorder) cmd.SetOut(&suite.dpnd.recorder)
ctx = print.SetRootCmd(ctx, cmd) ctx = print.SetRootCmd(ctx, cmd)
@ -665,7 +535,7 @@ func runExchangeDetailsCmdTest(suite *PreparedBackupExchangeE2ESuite, category p
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
// compare the output // compare the output
result := suite.recorder.String() result := suite.dpnd.recorder.String()
i := 0 i := 0
foundFolders := 0 foundFolders := 0
@ -695,11 +565,7 @@ func runExchangeDetailsCmdTest(suite *PreparedBackupExchangeE2ESuite, category p
type BackupDeleteExchangeE2ESuite struct { type BackupDeleteExchangeE2ESuite struct {
tester.Suite tester.Suite
acct account.Account dpnd dependencies
st storage.Storage
vpr *viper.Viper
cfgFP string
repo repository.Repository
backupOp operations.BackupOperation backupOp operations.BackupOperation
} }
@ -707,8 +573,7 @@ func TestBackupDeleteExchangeE2ESuite(t *testing.T) {
suite.Run(t, &BackupDeleteExchangeE2ESuite{ suite.Run(t, &BackupDeleteExchangeE2ESuite{
Suite: tester.NewE2ESuite( Suite: tester.NewE2ESuite(
t, t,
[][]string{storeTD.AWSStorageCredEnvs, tconfig.M365AcctCredEnvs}, [][]string{storeTD.AWSStorageCredEnvs, tconfig.M365AcctCredEnvs}),
),
}) })
} }
@ -718,13 +583,7 @@ func (suite *BackupDeleteExchangeE2ESuite) SetupSuite() {
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
defer flush() defer flush()
acct, st, repo, vpr, _, cfgFilePath := prepM365Test(t, ctx) suite.dpnd = prepM365Test(t, ctx)
suite.acct = acct
suite.st = st
suite.repo = repo
suite.vpr = vpr
suite.cfgFP = cfgFilePath
m365UserID := tconfig.M365UserID(t) m365UserID := tconfig.M365UserID(t)
users := []string{m365UserID} users := []string{m365UserID}
@ -733,7 +592,7 @@ func (suite *BackupDeleteExchangeE2ESuite) SetupSuite() {
sel := selectors.NewExchangeBackup(users) sel := selectors.NewExchangeBackup(users)
sel.Include(sel.MailFolders([]string{api.MailInbox}, selectors.PrefixMatch())) sel.Include(sel.MailFolders([]string{api.MailInbox}, selectors.PrefixMatch()))
backupOp, err := suite.repo.NewBackup(ctx, sel.Selector) backupOp, err := suite.dpnd.repo.NewBackup(ctx, sel.Selector)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
suite.backupOp = backupOp suite.backupOp = backupOp
@ -746,13 +605,13 @@ func (suite *BackupDeleteExchangeE2ESuite) TestExchangeBackupDeleteCmd() {
t := suite.T() t := suite.T()
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
ctx = config.SetViper(ctx, suite.vpr) ctx = config.SetViper(ctx, suite.dpnd.vpr)
defer flush() defer flush()
cmd := cliTD.StubRootCmd( cmd := cliTD.StubRootCmd(
"backup", "delete", "exchange", "backup", "delete", "exchange",
"--config-file", suite.cfgFP, "--config-file", suite.dpnd.configFilePath,
"--"+flags.BackupFN, string(suite.backupOp.Results.BackupID)) "--"+flags.BackupFN, string(suite.backupOp.Results.BackupID))
cli.BuildCommandTree(cmd) cli.BuildCommandTree(cmd)
@ -763,7 +622,7 @@ func (suite *BackupDeleteExchangeE2ESuite) TestExchangeBackupDeleteCmd() {
// a follow-up details call should fail, due to the backup ID being deleted // a follow-up details call should fail, due to the backup ID being deleted
cmd = cliTD.StubRootCmd( cmd = cliTD.StubRootCmd(
"backup", "details", "exchange", "backup", "details", "exchange",
"--config-file", suite.cfgFP, "--config-file", suite.dpnd.configFilePath,
"--backup", string(suite.backupOp.Results.BackupID)) "--backup", string(suite.backupOp.Results.BackupID))
cli.BuildCommandTree(cmd) cli.BuildCommandTree(cmd)
@ -775,13 +634,13 @@ func (suite *BackupDeleteExchangeE2ESuite) TestExchangeBackupDeleteCmd_UnknownID
t := suite.T() t := suite.T()
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
ctx = config.SetViper(ctx, suite.vpr) ctx = config.SetViper(ctx, suite.dpnd.vpr)
defer flush() defer flush()
cmd := cliTD.StubRootCmd( cmd := cliTD.StubRootCmd(
"backup", "delete", "exchange", "backup", "delete", "exchange",
"--config-file", suite.cfgFP, "--config-file", suite.dpnd.configFilePath,
"--"+flags.BackupFN, uuid.NewString()) "--"+flags.BackupFN, uuid.NewString())
cli.BuildCommandTree(cmd) cli.BuildCommandTree(cmd)
@ -789,23 +648,3 @@ func (suite *BackupDeleteExchangeE2ESuite) TestExchangeBackupDeleteCmd_UnknownID
err := cmd.ExecuteContext(ctx) err := cmd.ExecuteContext(ctx)
require.Error(t, err, clues.ToCore(err)) require.Error(t, err, clues.ToCore(err))
} }
// ---------------------------------------------------------------------------
// helpers
// ---------------------------------------------------------------------------
func buildExchangeBackupCmd(
ctx context.Context,
configFile, user, category string,
recorder *strings.Builder,
) (*cobra.Command, context.Context) {
cmd := cliTD.StubRootCmd(
"backup", "create", "exchange",
"--config-file", configFile,
"--"+flags.UserFN, user,
"--"+flags.CategoryDataFN, category)
cli.BuildCommandTree(cmd)
cmd.SetOut(recorder)
return cmd, print.SetRootCmd(ctx, cmd)
}

View File

@ -0,0 +1,622 @@
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"
storeTD "github.com/alcionai/corso/src/pkg/storage/testdata"
)
var (
channelMessages = path.ChannelMessagesCategory
libraries = path.LibrariesCategory
)
// ---------------------------------------------------------------------------
// tests that require no existing backups
// ---------------------------------------------------------------------------
type NoBackupGroupsE2ESuite struct {
tester.Suite
dpnd dependencies
its intgTesterSetup
}
func TestNoBackupGroupsE2ESuite(t *testing.T) {
t.Skip("enable when e2e is complete for groups")
suite.Run(t, &BackupGroupsE2ESuite{Suite: tester.NewE2ESuite(
t,
[][]string{storeTD.AWSStorageCredEnvs, tconfig.M365AcctCredEnvs},
)})
}
func (suite *NoBackupGroupsE2ESuite) SetupSuite() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
suite.its = newIntegrationTesterSetup(t)
suite.dpnd = prepM365Test(t, ctx)
}
func (suite *NoBackupGroupsE2ESuite) TestGroupsBackupListCmd_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", "groups",
"--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 group id
assert.True(t, strings.HasSuffix(result, "No backups available\n"))
}
// ---------------------------------------------------------------------------
// tests with no prior backup
// ---------------------------------------------------------------------------
type BackupGroupsE2ESuite struct {
tester.Suite
dpnd dependencies
its intgTesterSetup
}
func TestBackupGroupsE2ESuite(t *testing.T) {
t.Skip("enable when e2e is complete for groups")
suite.Run(t, &BackupGroupsE2ESuite{Suite: tester.NewE2ESuite(
t,
[][]string{storeTD.AWSStorageCredEnvs, tconfig.M365AcctCredEnvs},
)})
}
func (suite *BackupGroupsE2ESuite) SetupSuite() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
suite.its = newIntegrationTesterSetup(t)
suite.dpnd = prepM365Test(t, ctx)
}
func (suite *BackupGroupsE2ESuite) TestGroupsBackupCmd_channelMessages() {
runGroupsBackupCategoryTest(suite, channelMessages)
}
func (suite *BackupGroupsE2ESuite) TestGroupsBackupCmd_libraries() {
runGroupsBackupCategoryTest(suite, libraries)
}
func runGroupsBackupCategoryTest(suite *BackupGroupsE2ESuite, 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 := buildGroupsBackupCmd(
ctx,
suite.dpnd.configFilePath,
suite.its.group.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 group id
assert.Contains(t, result, suite.its.group.ID)
}
func (suite *BackupGroupsE2ESuite) TestGroupsBackupCmd_groupNotFound_channelMessages() {
runGroupsBackupGroupNotFoundTest(suite, channelMessages)
}
func (suite *BackupGroupsE2ESuite) TestGroupsBackupCmd_groupNotFound_libraries() {
runGroupsBackupGroupNotFoundTest(suite, libraries)
}
func runGroupsBackupGroupNotFoundTest(suite *BackupGroupsE2ESuite, 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 := buildGroupsBackupCmd(
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 group 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 *BackupGroupsE2ESuite) TestBackupCreateGroups_badAzureClientIDFlag() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
suite.dpnd.recorder.Reset()
cmd := cliTD.StubRootCmd(
"backup", "create", "groups",
"--group", suite.its.group.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 *BackupGroupsE2ESuite) TestBackupCreateGroups_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", "groups",
"--group", suite.its.group.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 group id
assert.Contains(t, result, suite.its.group.ID)
}
// AWS flags
func (suite *BackupGroupsE2ESuite) TestBackupCreateGroups_badAWSFlags() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
suite.dpnd.recorder.Reset()
cmd := cliTD.StubRootCmd(
"backup", "create", "groups",
"--group", suite.its.group.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 PreparedBackupGroupsE2ESuite struct {
tester.Suite
dpnd dependencies
backupOps map[path.CategoryType]string
its intgTesterSetup
}
func TestPreparedBackupGroupsE2ESuite(t *testing.T) {
t.Skip("enable when e2e is complete for groups")
suite.Run(t, &PreparedBackupGroupsE2ESuite{
Suite: tester.NewE2ESuite(
t,
[][]string{storeTD.AWSStorageCredEnvs, tconfig.M365AcctCredEnvs}),
})
}
func (suite *PreparedBackupGroupsE2ESuite) 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 (
groups = []string{suite.its.group.ID}
ins = idname.NewCache(map[string]string{suite.its.group.ID: suite.its.group.ID})
)
for _, set := range []path.CategoryType{channelMessages, libraries} {
var (
sel = selectors.NewGroupsBackup(groups)
scopes []selectors.GroupsScope
)
switch set {
case channelMessages:
scopes = sel.Channel("TODO-test-channel-const")
case libraries:
scopes = sel.LibraryFolders([]string{"TODO-test-library-folder-const"}, selectors.PrefixMatch())
}
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 *PreparedBackupGroupsE2ESuite) TestGroupsListCmd_channelMessages() {
runGroupsListCmdTest(suite, channelMessages)
}
func (suite *PreparedBackupGroupsE2ESuite) TestGroupsListCmd_libraries() {
runGroupsListCmdTest(suite, libraries)
}
func runGroupsListCmdTest(suite *PreparedBackupGroupsE2ESuite, 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", "groups",
"--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 *PreparedBackupGroupsE2ESuite) TestGroupsListCmd_singleID_channelMessages() {
runGroupsListSingleCmdTest(suite, channelMessages)
}
func (suite *PreparedBackupGroupsE2ESuite) TestGroupsListCmd_singleID_libraries() {
runGroupsListSingleCmdTest(suite, libraries)
}
func runGroupsListSingleCmdTest(suite *PreparedBackupGroupsE2ESuite, 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", "groups",
"--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 *PreparedBackupGroupsE2ESuite) TestGroupsListCmd_badID() {
t := suite.T()
ctx, flush := tester.NewContext(t)
ctx = config.SetViper(ctx, suite.dpnd.vpr)
defer flush()
cmd := cliTD.StubRootCmd(
"backup", "list", "groups",
"--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 *PreparedBackupGroupsE2ESuite) TestGroupsDetailsCmd_channelMessages() {
runGroupsDetailsCmdTest(suite, channelMessages)
}
func (suite *PreparedBackupGroupsE2ESuite) TestGroupsDetailsCmd_libraries() {
runGroupsDetailsCmdTest(suite, libraries)
}
func runGroupsDetailsCmdTest(suite *PreparedBackupGroupsE2ESuite, 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", "groups",
"--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 group.
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 BackupDeleteGroupsE2ESuite struct {
tester.Suite
dpnd dependencies
backupOp operations.BackupOperation
}
func TestBackupDeleteGroupsE2ESuite(t *testing.T) {
t.Skip("enable when e2e is complete for groups")
suite.Run(t, &BackupDeleteGroupsE2ESuite{
Suite: tester.NewE2ESuite(
t,
[][]string{storeTD.AWSStorageCredEnvs, tconfig.M365AcctCredEnvs},
),
})
}
func (suite *BackupDeleteGroupsE2ESuite) SetupSuite() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
suite.dpnd = prepM365Test(t, ctx)
m365GroupID := tconfig.M365GroupID(t)
groups := []string{m365GroupID}
// some tests require an existing backup
sel := selectors.NewGroupsBackup(groups)
sel.Include(sel.Channel("TODO-test-channel-const"))
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 *BackupDeleteGroupsE2ESuite) TestGroupsBackupDeleteCmd() {
t := suite.T()
ctx, flush := tester.NewContext(t)
ctx = config.SetViper(ctx, suite.dpnd.vpr)
defer flush()
cmd := cliTD.StubRootCmd(
"backup", "delete", "groups",
"--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", "groups",
"--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 *BackupDeleteGroupsE2ESuite) TestGroupsBackupDeleteCmd_UnknownID() {
t := suite.T()
ctx, flush := tester.NewContext(t)
ctx = config.SetViper(ctx, suite.dpnd.vpr)
defer flush()
cmd := cliTD.StubRootCmd(
"backup", "delete", "groups",
"--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 buildGroupsBackupCmd(
ctx context.Context,
configFile, group, category string,
recorder *strings.Builder,
) (*cobra.Command, context.Context) {
cmd := cliTD.StubRootCmd(
"backup", "create", "groups",
"--config-file", configFile,
"--"+flags.GroupFN, group,
"--"+flags.CategoryDataFN, category)
cli.BuildCommandTree(cmd)
cmd.SetOut(recorder)
return cmd, print.SetRootCmd(ctx, cmd)
}

View File

@ -1,60 +0,0 @@
package backup_test
import (
"context"
"strings"
"testing"
"github.com/alcionai/clues"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
"github.com/alcionai/corso/src/cli/config"
"github.com/alcionai/corso/src/internal/tester/tconfig"
"github.com/alcionai/corso/src/pkg/account"
"github.com/alcionai/corso/src/pkg/control"
ctrlRepo "github.com/alcionai/corso/src/pkg/control/repository"
"github.com/alcionai/corso/src/pkg/repository"
"github.com/alcionai/corso/src/pkg/storage"
"github.com/alcionai/corso/src/pkg/storage/testdata"
)
func prepM365Test(
t *testing.T,
ctx context.Context, //revive:disable-line:context-as-argument
) (
account.Account,
storage.Storage,
repository.Repository,
*viper.Viper,
strings.Builder,
string,
) {
var (
acct = tconfig.NewM365Account(t)
st = testdata.NewPrefixedS3Storage(t)
recorder = strings.Builder{}
)
cfg, err := st.S3Config()
require.NoError(t, err, clues.ToCore(err))
force := map[string]string{
tconfig.TestCfgAccountProvider: "M365",
tconfig.TestCfgStorageProvider: "S3",
tconfig.TestCfgPrefix: cfg.Prefix,
}
vpr, cfgFP := tconfig.MakeTempTestConfigClone(t, force)
ctx = config.SetViper(ctx, vpr)
repo, err := repository.Initialize(
ctx,
acct,
st,
control.DefaultOptions(),
ctrlRepo.Retention{})
require.NoError(t, err, clues.ToCore(err))
return acct, st, repo, vpr, recorder, cfgFP
}

View File

@ -0,0 +1,190 @@
package backup_test
import (
"context"
"strings"
"testing"
"github.com/alcionai/clues"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
"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/ptr"
"github.com/alcionai/corso/src/internal/m365/graph"
"github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/internal/tester/tconfig"
"github.com/alcionai/corso/src/pkg/account"
"github.com/alcionai/corso/src/pkg/control"
ctrlRepo "github.com/alcionai/corso/src/pkg/control/repository"
"github.com/alcionai/corso/src/pkg/repository"
"github.com/alcionai/corso/src/pkg/services/m365/api"
"github.com/alcionai/corso/src/pkg/services/m365/api/mock"
"github.com/alcionai/corso/src/pkg/storage"
"github.com/alcionai/corso/src/pkg/storage/testdata"
)
// ---------------------------------------------------------------------------
// Suite Setup
// ---------------------------------------------------------------------------
type ids struct {
ID string
DriveID string
DriveRootFolderID string
}
type intgTesterSetup struct {
acct account.Account
ac api.Client
gockAC api.Client
user ids
site ids
group ids
team ids
}
func newIntegrationTesterSetup(t *testing.T) intgTesterSetup {
its := intgTesterSetup{}
ctx, flush := tester.NewContext(t)
defer flush()
graph.InitializeConcurrencyLimiter(ctx, true, 4)
its.acct = tconfig.NewM365Account(t)
creds, err := its.acct.M365Config()
require.NoError(t, err, clues.ToCore(err))
its.ac, err = api.NewClient(creds, control.DefaultOptions())
require.NoError(t, err, clues.ToCore(err))
its.gockAC, err = mock.NewClient(creds)
require.NoError(t, err, clues.ToCore(err))
// user drive
uids := ids{}
uids.ID = tconfig.M365UserID(t)
userDrive, err := its.ac.Users().GetDefaultDrive(ctx, uids.ID)
require.NoError(t, err, clues.ToCore(err))
uids.DriveID = ptr.Val(userDrive.GetId())
userDriveRootFolder, err := its.ac.Drives().GetRootFolder(ctx, uids.DriveID)
require.NoError(t, err, clues.ToCore(err))
uids.DriveRootFolderID = ptr.Val(userDriveRootFolder.GetId())
its.user = uids
// site
sids := ids{}
sids.ID = tconfig.M365SiteID(t)
siteDrive, err := its.ac.Sites().GetDefaultDrive(ctx, sids.ID)
require.NoError(t, err, clues.ToCore(err))
sids.DriveID = ptr.Val(siteDrive.GetId())
siteDriveRootFolder, err := its.ac.Drives().GetRootFolder(ctx, sids.DriveID)
require.NoError(t, err, clues.ToCore(err))
sids.DriveRootFolderID = ptr.Val(siteDriveRootFolder.GetId())
its.site = sids
// group
gids := ids{}
// use of the TeamID is intentional here, so that we are assured
// the group has full usage of the teams api.
gids.ID = tconfig.M365TeamID(t)
its.group = gids
// team
tids := ids{}
tids.ID = tconfig.M365TeamID(t)
its.team = tids
return its
}
type dependencies struct {
st storage.Storage
repo repository.Repository
vpr *viper.Viper
recorder strings.Builder
configFilePath string
}
func prepM365Test(
t *testing.T,
ctx context.Context, //revive:disable-line:context-as-argument
) dependencies {
var (
acct = tconfig.NewM365Account(t)
st = testdata.NewPrefixedS3Storage(t)
recorder = strings.Builder{}
)
cfg, err := st.S3Config()
require.NoError(t, err, clues.ToCore(err))
force := map[string]string{
tconfig.TestCfgAccountProvider: "M365",
tconfig.TestCfgStorageProvider: "S3",
tconfig.TestCfgPrefix: cfg.Prefix,
}
vpr, cfgFP := tconfig.MakeTempTestConfigClone(t, force)
ctx = config.SetViper(ctx, vpr)
repo, err := repository.Initialize(
ctx,
acct,
st,
control.DefaultOptions(),
ctrlRepo.Retention{})
require.NoError(t, err, clues.ToCore(err))
return dependencies{
st: st,
repo: repo,
vpr: vpr,
recorder: recorder,
configFilePath: cfgFP,
}
}
// ---------------------------------------------------------------------------
// funcs
// ---------------------------------------------------------------------------
func buildExchangeBackupCmd(
ctx context.Context,
configFile, user, category string,
recorder *strings.Builder,
) (*cobra.Command, context.Context) {
cmd := cliTD.StubRootCmd(
"backup", "create", "exchange",
"--config-file", configFile,
"--"+flags.UserFN, user,
"--"+flags.CategoryDataFN, category)
cli.BuildCommandTree(cmd)
cmd.SetOut(recorder)
return cmd, print.SetRootCmd(ctx, cmd)
}

View File

@ -7,7 +7,6 @@ import (
"github.com/alcionai/clues" "github.com/alcionai/clues"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
@ -21,11 +20,8 @@ import (
"github.com/alcionai/corso/src/internal/operations" "github.com/alcionai/corso/src/internal/operations"
"github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/internal/tester/tconfig" "github.com/alcionai/corso/src/internal/tester/tconfig"
"github.com/alcionai/corso/src/pkg/account"
"github.com/alcionai/corso/src/pkg/repository"
"github.com/alcionai/corso/src/pkg/selectors" "github.com/alcionai/corso/src/pkg/selectors"
selTD "github.com/alcionai/corso/src/pkg/selectors/testdata" selTD "github.com/alcionai/corso/src/pkg/selectors/testdata"
"github.com/alcionai/corso/src/pkg/storage"
storeTD "github.com/alcionai/corso/src/pkg/storage/testdata" storeTD "github.com/alcionai/corso/src/pkg/storage/testdata"
) )
@ -35,13 +31,7 @@ import (
type NoBackupOneDriveE2ESuite struct { type NoBackupOneDriveE2ESuite struct {
tester.Suite tester.Suite
acct account.Account dpnd dependencies
st storage.Storage
vpr *viper.Viper
cfgFP string
repo repository.Repository
m365UserID string
recorder strings.Builder
} }
func TestNoBackupOneDriveE2ESuite(t *testing.T) { func TestNoBackupOneDriveE2ESuite(t *testing.T) {
@ -58,33 +48,25 @@ func (suite *NoBackupOneDriveE2ESuite) SetupSuite() {
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
defer flush() defer flush()
acct, st, repo, vpr, recorder, cfgFilePath := prepM365Test(t, ctx) suite.dpnd = prepM365Test(t, ctx)
suite.acct = acct
suite.st = st
suite.repo = repo
suite.recorder = recorder
suite.vpr = vpr
suite.cfgFP = cfgFilePath
suite.m365UserID = tconfig.M365UserID(t)
} }
func (suite *NoBackupOneDriveE2ESuite) TestOneDriveBackupListCmd_empty() { func (suite *NoBackupOneDriveE2ESuite) TestOneDriveBackupListCmd_empty() {
t := suite.T() t := suite.T()
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
ctx = config.SetViper(ctx, suite.vpr) ctx = config.SetViper(ctx, suite.dpnd.vpr)
defer flush() defer flush()
suite.recorder.Reset() suite.dpnd.recorder.Reset()
cmd := cliTD.StubRootCmd( cmd := cliTD.StubRootCmd(
"backup", "list", "onedrive", "backup", "list", "onedrive",
"--config-file", suite.cfgFP) "--config-file", suite.dpnd.configFilePath)
cli.BuildCommandTree(cmd) cli.BuildCommandTree(cmd)
cmd.SetErr(&suite.recorder) cmd.SetErr(&suite.dpnd.recorder)
ctx = print.SetRootCmd(ctx, cmd) ctx = print.SetRootCmd(ctx, cmd)
@ -92,13 +74,13 @@ func (suite *NoBackupOneDriveE2ESuite) TestOneDriveBackupListCmd_empty() {
err := cmd.ExecuteContext(ctx) err := cmd.ExecuteContext(ctx)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
result := suite.recorder.String() result := suite.dpnd.recorder.String()
// as an offhand check: the result should contain the m365 user id // as an offhand check: the result should contain the m365 user id
assert.True(t, strings.HasSuffix(result, "No backups available\n")) assert.True(t, strings.HasSuffix(result, "No backups available\n"))
} }
func (suite *NoBackupOneDriveE2ESuite) TestOneDriveBackupCmd_UserNotInTenant() { func (suite *NoBackupOneDriveE2ESuite) TestOneDriveBackupCmd_userNotInTenant() {
recorder := strings.Builder{} recorder := strings.Builder{}
t := suite.T() t := suite.T()
@ -106,11 +88,11 @@ func (suite *NoBackupOneDriveE2ESuite) TestOneDriveBackupCmd_UserNotInTenant() {
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
defer flush() defer flush()
ctx = config.SetViper(ctx, suite.vpr) ctx = config.SetViper(ctx, suite.dpnd.vpr)
cmd := cliTD.StubRootCmd( cmd := cliTD.StubRootCmd(
"backup", "create", "onedrive", "backup", "create", "onedrive",
"--config-file", suite.cfgFP, "--config-file", suite.dpnd.configFilePath,
"--"+flags.UserFN, "foo@nothere.com") "--"+flags.UserFN, "foo@nothere.com")
cli.BuildCommandTree(cmd) cli.BuildCommandTree(cmd)
@ -139,13 +121,8 @@ func (suite *NoBackupOneDriveE2ESuite) TestOneDriveBackupCmd_UserNotInTenant() {
type BackupDeleteOneDriveE2ESuite struct { type BackupDeleteOneDriveE2ESuite struct {
tester.Suite tester.Suite
acct account.Account dpnd dependencies
st storage.Storage
vpr *viper.Viper
cfgFP string
repo repository.Repository
backupOp operations.BackupOperation backupOp operations.BackupOperation
recorder strings.Builder
} }
func TestBackupDeleteOneDriveE2ESuite(t *testing.T) { func TestBackupDeleteOneDriveE2ESuite(t *testing.T) {
@ -162,14 +139,7 @@ func (suite *BackupDeleteOneDriveE2ESuite) SetupSuite() {
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
defer flush() defer flush()
acct, st, repo, vpr, recorder, cfgFilePath := prepM365Test(t, ctx) suite.dpnd = prepM365Test(t, ctx)
suite.acct = acct
suite.st = st
suite.repo = repo
suite.recorder = recorder
suite.vpr = vpr
suite.cfgFP = cfgFilePath
var ( var (
m365UserID = tconfig.M365UserID(t) m365UserID = tconfig.M365UserID(t)
@ -181,7 +151,7 @@ func (suite *BackupDeleteOneDriveE2ESuite) SetupSuite() {
sel := selectors.NewOneDriveBackup(users) sel := selectors.NewOneDriveBackup(users)
sel.Include(selTD.OneDriveBackupFolderScope(sel)) sel.Include(selTD.OneDriveBackupFolderScope(sel))
backupOp, err := suite.repo.NewBackupWithLookup(ctx, sel.Selector, ins) backupOp, err := suite.dpnd.repo.NewBackupWithLookup(ctx, sel.Selector, ins)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
suite.backupOp = backupOp suite.backupOp = backupOp
@ -194,18 +164,18 @@ func (suite *BackupDeleteOneDriveE2ESuite) TestOneDriveBackupDeleteCmd() {
t := suite.T() t := suite.T()
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
ctx = config.SetViper(ctx, suite.vpr) ctx = config.SetViper(ctx, suite.dpnd.vpr)
defer flush() defer flush()
suite.recorder.Reset() suite.dpnd.recorder.Reset()
cmd := cliTD.StubRootCmd( cmd := cliTD.StubRootCmd(
"backup", "delete", "onedrive", "backup", "delete", "onedrive",
"--config-file", suite.cfgFP, "--config-file", suite.dpnd.configFilePath,
"--"+flags.BackupFN, string(suite.backupOp.Results.BackupID)) "--"+flags.BackupFN, string(suite.backupOp.Results.BackupID))
cli.BuildCommandTree(cmd) cli.BuildCommandTree(cmd)
cmd.SetErr(&suite.recorder) cmd.SetErr(&suite.dpnd.recorder)
ctx = print.SetRootCmd(ctx, cmd) ctx = print.SetRootCmd(ctx, cmd)
@ -213,7 +183,7 @@ func (suite *BackupDeleteOneDriveE2ESuite) TestOneDriveBackupDeleteCmd() {
err := cmd.ExecuteContext(ctx) err := cmd.ExecuteContext(ctx)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
result := suite.recorder.String() result := suite.dpnd.recorder.String()
assert.True(t, assert.True(t,
strings.HasSuffix( strings.HasSuffix(
result, result,
@ -224,7 +194,7 @@ func (suite *BackupDeleteOneDriveE2ESuite) TestOneDriveBackupDeleteCmd() {
// a follow-up details call should fail, due to the backup ID being deleted // a follow-up details call should fail, due to the backup ID being deleted
cmd = cliTD.StubRootCmd( cmd = cliTD.StubRootCmd(
"backup", "details", "onedrive", "backup", "details", "onedrive",
"--config-file", suite.cfgFP, "--config-file", suite.dpnd.configFilePath,
"--backup", string(suite.backupOp.Results.BackupID)) "--backup", string(suite.backupOp.Results.BackupID))
cli.BuildCommandTree(cmd) cli.BuildCommandTree(cmd)
@ -236,13 +206,13 @@ func (suite *BackupDeleteOneDriveE2ESuite) TestOneDriveBackupDeleteCmd_unknownID
t := suite.T() t := suite.T()
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
ctx = config.SetViper(ctx, suite.vpr) ctx = config.SetViper(ctx, suite.dpnd.vpr)
defer flush() defer flush()
cmd := cliTD.StubRootCmd( cmd := cliTD.StubRootCmd(
"backup", "delete", "onedrive", "backup", "delete", "onedrive",
"--config-file", suite.cfgFP, "--config-file", suite.dpnd.configFilePath,
"--"+flags.BackupFN, uuid.NewString()) "--"+flags.BackupFN, uuid.NewString())
cli.BuildCommandTree(cmd) cli.BuildCommandTree(cmd)

View File

@ -7,7 +7,6 @@ import (
"github.com/alcionai/clues" "github.com/alcionai/clues"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
@ -21,11 +20,8 @@ import (
"github.com/alcionai/corso/src/internal/operations" "github.com/alcionai/corso/src/internal/operations"
"github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/internal/tester/tconfig" "github.com/alcionai/corso/src/internal/tester/tconfig"
"github.com/alcionai/corso/src/pkg/account"
"github.com/alcionai/corso/src/pkg/repository"
"github.com/alcionai/corso/src/pkg/selectors" "github.com/alcionai/corso/src/pkg/selectors"
"github.com/alcionai/corso/src/pkg/selectors/testdata" "github.com/alcionai/corso/src/pkg/selectors/testdata"
"github.com/alcionai/corso/src/pkg/storage"
storeTD "github.com/alcionai/corso/src/pkg/storage/testdata" storeTD "github.com/alcionai/corso/src/pkg/storage/testdata"
) )
@ -35,13 +31,7 @@ import (
type NoBackupSharePointE2ESuite struct { type NoBackupSharePointE2ESuite struct {
tester.Suite tester.Suite
acct account.Account dpnd dependencies
st storage.Storage
vpr *viper.Viper
cfgFP string
repo repository.Repository
m365SiteID string
recorder strings.Builder
} }
func TestNoBackupSharePointE2ESuite(t *testing.T) { func TestNoBackupSharePointE2ESuite(t *testing.T) {
@ -57,33 +47,25 @@ func (suite *NoBackupSharePointE2ESuite) SetupSuite() {
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
defer flush() defer flush()
acct, st, repo, vpr, recorder, cfgFilePath := prepM365Test(t, ctx) suite.dpnd = prepM365Test(t, ctx)
suite.acct = acct
suite.st = st
suite.repo = repo
suite.vpr = vpr
suite.recorder = recorder
suite.cfgFP = cfgFilePath
suite.m365SiteID = tconfig.M365SiteID(t)
} }
func (suite *NoBackupSharePointE2ESuite) TestSharePointBackupListCmd_empty() { func (suite *NoBackupSharePointE2ESuite) TestSharePointBackupListCmd_empty() {
t := suite.T() t := suite.T()
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
ctx = config.SetViper(ctx, suite.vpr) ctx = config.SetViper(ctx, suite.dpnd.vpr)
defer flush() defer flush()
suite.recorder.Reset() suite.dpnd.recorder.Reset()
cmd := cliTD.StubRootCmd( cmd := cliTD.StubRootCmd(
"backup", "list", "sharepoint", "backup", "list", "sharepoint",
"--config-file", suite.cfgFP) "--config-file", suite.dpnd.configFilePath)
cli.BuildCommandTree(cmd) cli.BuildCommandTree(cmd)
cmd.SetErr(&suite.recorder) cmd.SetErr(&suite.dpnd.recorder)
ctx = print.SetRootCmd(ctx, cmd) ctx = print.SetRootCmd(ctx, cmd)
@ -91,7 +73,7 @@ func (suite *NoBackupSharePointE2ESuite) TestSharePointBackupListCmd_empty() {
err := cmd.ExecuteContext(ctx) err := cmd.ExecuteContext(ctx)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
result := suite.recorder.String() result := suite.dpnd.recorder.String()
// as an offhand check: the result should contain the m365 sitet id // as an offhand check: the result should contain the m365 sitet id
assert.True(t, strings.HasSuffix(result, "No backups available\n")) assert.True(t, strings.HasSuffix(result, "No backups available\n"))
@ -103,13 +85,8 @@ func (suite *NoBackupSharePointE2ESuite) TestSharePointBackupListCmd_empty() {
type BackupDeleteSharePointE2ESuite struct { type BackupDeleteSharePointE2ESuite struct {
tester.Suite tester.Suite
acct account.Account dpnd dependencies
st storage.Storage
vpr *viper.Viper
cfgFP string
repo repository.Repository
backupOp operations.BackupOperation backupOp operations.BackupOperation
recorder strings.Builder
} }
func TestBackupDeleteSharePointE2ESuite(t *testing.T) { func TestBackupDeleteSharePointE2ESuite(t *testing.T) {
@ -126,14 +103,7 @@ func (suite *BackupDeleteSharePointE2ESuite) SetupSuite() {
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
defer flush() defer flush()
acct, st, repo, vpr, recorder, cfgFilePath := prepM365Test(t, ctx) suite.dpnd = prepM365Test(t, ctx)
suite.acct = acct
suite.st = st
suite.repo = repo
suite.vpr = vpr
suite.recorder = recorder
suite.cfgFP = cfgFilePath
var ( var (
m365SiteID = tconfig.M365SiteID(t) m365SiteID = tconfig.M365SiteID(t)
@ -145,7 +115,7 @@ func (suite *BackupDeleteSharePointE2ESuite) SetupSuite() {
sel := selectors.NewSharePointBackup(sites) sel := selectors.NewSharePointBackup(sites)
sel.Include(testdata.SharePointBackupFolderScope(sel)) sel.Include(testdata.SharePointBackupFolderScope(sel))
backupOp, err := suite.repo.NewBackupWithLookup(ctx, sel.Selector, ins) backupOp, err := suite.dpnd.repo.NewBackupWithLookup(ctx, sel.Selector, ins)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
suite.backupOp = backupOp suite.backupOp = backupOp
@ -158,18 +128,18 @@ func (suite *BackupDeleteSharePointE2ESuite) TestSharePointBackupDeleteCmd() {
t := suite.T() t := suite.T()
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
ctx = config.SetViper(ctx, suite.vpr) ctx = config.SetViper(ctx, suite.dpnd.vpr)
defer flush() defer flush()
suite.recorder.Reset() suite.dpnd.recorder.Reset()
cmd := cliTD.StubRootCmd( cmd := cliTD.StubRootCmd(
"backup", "delete", "sharepoint", "backup", "delete", "sharepoint",
"--config-file", suite.cfgFP, "--config-file", suite.dpnd.configFilePath,
"--"+flags.BackupFN, string(suite.backupOp.Results.BackupID)) "--"+flags.BackupFN, string(suite.backupOp.Results.BackupID))
cli.BuildCommandTree(cmd) cli.BuildCommandTree(cmd)
cmd.SetErr(&suite.recorder) cmd.SetErr(&suite.dpnd.recorder)
ctx = print.SetRootCmd(ctx, cmd) ctx = print.SetRootCmd(ctx, cmd)
@ -177,7 +147,7 @@ func (suite *BackupDeleteSharePointE2ESuite) TestSharePointBackupDeleteCmd() {
err := cmd.ExecuteContext(ctx) err := cmd.ExecuteContext(ctx)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
result := suite.recorder.String() result := suite.dpnd.recorder.String()
assert.True(t, assert.True(t,
strings.HasSuffix( strings.HasSuffix(
result, result,
@ -201,13 +171,13 @@ func (suite *BackupDeleteSharePointE2ESuite) TestSharePointBackupDeleteCmd_unkno
t := suite.T() t := suite.T()
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
ctx = config.SetViper(ctx, suite.vpr) ctx = config.SetViper(ctx, suite.dpnd.vpr)
defer flush() defer flush()
cmd := cliTD.StubRootCmd( cmd := cliTD.StubRootCmd(
"backup", "delete", "sharepoint", "backup", "delete", "sharepoint",
"--config-file", suite.cfgFP, "--config-file", suite.dpnd.configFilePath,
"--"+flags.BackupFN, uuid.NewString()) "--"+flags.BackupFN, uuid.NewString())
cli.BuildCommandTree(cmd) cli.BuildCommandTree(cmd)

View File

@ -0,0 +1,617 @@
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"
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) {
t.Skip("enable when e2e is complete for teams")
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) {
t.Skip("enable when e2e is complete for teams")
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) {
t.Skip("enable when e2e is complete for teams")
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 = sel.Channel("TODO-test-channel-const")
case libraries:
scopes = sel.LibraryFolders([]string{"TODO-test-library-folder-const"}, selectors.PrefixMatch())
}
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) {
t.Skip("enable when e2e is complete for teams")
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(sel.Channel("TODO-test-channel-const"))
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)
}