add backup cli integration test (#517)

Adds the basic cli-based backup integration test.
Due to discovering some corner cases about panic
conditions and other error handling in bad runtime
state, updates many other packages for safety.
This commit is contained in:
Keepers 2022-08-12 14:04:44 -06:00 committed by GitHub
parent 6464a66b46
commit 192c69c68f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 99 additions and 35 deletions

View File

@ -0,0 +1,75 @@
package backup_test
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/alcionai/corso/cli"
"github.com/alcionai/corso/cli/config"
"github.com/alcionai/corso/internal/tester"
"github.com/alcionai/corso/pkg/repository"
)
type ExchangeIntegrationSuite struct {
suite.Suite
}
func TestExchangeIntegrationSuite(t *testing.T) {
if err := tester.RunOnAny(
tester.CorsoCITests,
tester.CorsoCLITests,
tester.CorsoCLIBackupTests,
); err != nil {
t.Skip(err)
}
suite.Run(t, new(ExchangeIntegrationSuite))
}
func (suite *ExchangeIntegrationSuite) SetupSuite() {
_, err := tester.GetRequiredEnvVars(
append(
tester.AWSStorageCredEnvs,
tester.M365AcctCredEnvs...,
)...,
)
require.NoError(suite.T(), err)
}
func (suite *ExchangeIntegrationSuite) TestExchangeBackupCmd() {
ctx := tester.NewContext()
t := suite.T()
acct := tester.NewM365Account(t)
st := tester.NewPrefixedS3Storage(t)
cfg, err := st.S3Config()
require.NoError(t, err)
force := map[string]string{
tester.TestCfgAccountProvider: "M365",
tester.TestCfgStorageProvider: "S3",
tester.TestCfgPrefix: cfg.Prefix,
}
vpr, configFP, err := tester.MakeTempTestConfigClone(t, force)
require.NoError(t, err)
ctx = config.SetViper(ctx, vpr)
// init the repo first
_, err = repository.Initialize(ctx, acct, st)
require.NoError(t, err)
m365UserID := tester.M365UserID(t)
// then test it
cmd := tester.StubRootCmd(
"backup", "create", "exchange",
"--config-file", configFP,
"--user", m365UserID,
"--data", "email",
)
cli.BuildCommandTree(cmd)
// run the command
require.NoError(t, cmd.ExecuteContext(ctx))
}

View File

@ -7,6 +7,7 @@ import (
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/alcionai/corso/cli/utils" "github.com/alcionai/corso/cli/utils"
"github.com/alcionai/corso/internal/common"
"github.com/alcionai/corso/pkg/account" "github.com/alcionai/corso/pkg/account"
"github.com/alcionai/corso/pkg/credentials" "github.com/alcionai/corso/pkg/credentials"
) )
@ -63,7 +64,7 @@ func configureAccount(
m365Cfg = account.M365Config{ m365Cfg = account.M365Config{
M365: m365, M365: m365,
TenantID: first(overrides[account.TenantID], m365Cfg.TenantID, os.Getenv(account.TenantID)), TenantID: common.First(overrides[account.TenantID], m365Cfg.TenantID, os.Getenv(account.TenantID)),
} }
// ensure required properties are present // ensure required properties are present

View File

@ -220,16 +220,6 @@ func getStorageAndAccountWithViper(
// Helper funcs // Helper funcs
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// returns the first non-zero valued string
func first(vs ...string) string {
for _, v := range vs {
if len(v) > 0 {
return v
}
}
return ""
}
var constToTomlKeyMap = map[string]string{ var constToTomlKeyMap = map[string]string{
account.TenantID: TenantIDKey, account.TenantID: TenantIDKey,
AccountProviderTypeKey: AccountProviderTypeKey, AccountProviderTypeKey: AccountProviderTypeKey,

View File

@ -7,6 +7,7 @@ import (
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/alcionai/corso/cli/utils" "github.com/alcionai/corso/cli/utils"
"github.com/alcionai/corso/internal/common"
"github.com/alcionai/corso/pkg/credentials" "github.com/alcionai/corso/pkg/credentials"
"github.com/alcionai/corso/pkg/storage" "github.com/alcionai/corso/pkg/storage"
) )
@ -66,9 +67,9 @@ func configureStorage(
s3Cfg = storage.S3Config{ s3Cfg = storage.S3Config{
AWS: aws, AWS: aws,
Bucket: first(overrides[storage.Bucket], s3Cfg.Bucket, os.Getenv(storage.BucketKey)), Bucket: common.First(overrides[storage.Bucket], s3Cfg.Bucket, os.Getenv(storage.BucketKey)),
Endpoint: first(overrides[storage.Endpoint], s3Cfg.Endpoint, os.Getenv(storage.EndpointKey)), Endpoint: common.First(overrides[storage.Endpoint], s3Cfg.Endpoint, os.Getenv(storage.EndpointKey)),
Prefix: first(overrides[storage.Prefix], s3Cfg.Prefix, os.Getenv(storage.PrefixKey)), Prefix: common.First(overrides[storage.Prefix], s3Cfg.Prefix, os.Getenv(storage.PrefixKey)),
} }
// compose the common config and credentials // compose the common config and credentials

View File

@ -13,10 +13,6 @@ import (
"github.com/alcionai/corso/pkg/repository" "github.com/alcionai/corso/pkg/repository"
) )
// ---------------------------------------------------------------------------------------------------------
// Integration
// ---------------------------------------------------------------------------------------------------------
type S3IntegrationSuite struct { type S3IntegrationSuite struct {
suite.Suite suite.Suite
} }
@ -24,6 +20,7 @@ type S3IntegrationSuite struct {
func TestS3IntegrationSuite(t *testing.T) { func TestS3IntegrationSuite(t *testing.T) {
if err := tester.RunOnAny( if err := tester.RunOnAny(
tester.CorsoCITests, tester.CorsoCITests,
tester.CorsoCLITests,
tester.CorsoCLIRepoTests, tester.CorsoCLIRepoTests,
); err != nil { ); err != nil {
t.Skip(err) t.Skip(err)
@ -104,7 +101,7 @@ func (suite *S3IntegrationSuite) TestConnectS3Cmd() {
_, err = repository.Initialize(ctx, account.Account{}, st) _, err = repository.Initialize(ctx, account.Account{}, st)
require.NoError(t, err) require.NoError(t, err)
// then connect to it // then test it
cmd := tester.StubRootCmd( cmd := tester.StubRootCmd(
"repo", "connect", "s3", "repo", "connect", "s3",
"--config-file", configFP, "--config-file", configFP,

View File

@ -37,7 +37,7 @@ func doFolderPurge(cmd *cobra.Command, args []string) error {
// get account info // get account info
m365Cfg := account.M365Config{ m365Cfg := account.M365Config{
M365: credentials.GetM365(), M365: credentials.GetM365(),
TenantID: first(tenant, os.Getenv(account.TenantID)), TenantID: common.First(tenant, os.Getenv(account.TenantID)),
} }
acct, err := account.NewAccount(account.ProviderM365, m365Cfg) acct, err := account.NewAccount(account.ProviderM365, m365Cfg)
if err != nil { if err != nil {
@ -110,13 +110,3 @@ func main() {
os.Exit(1) os.Exit(1)
} }
} }
// returns the first non-zero valued string
func first(vs ...string) string {
for _, v := range vs {
if len(v) > 0 {
return v
}
}
return ""
}

View File

@ -8,3 +8,13 @@ func ContainsString(super []string, sub string) bool {
} }
return false return false
} }
// First returns the first non-zero valued string
func First(vs ...string) string {
for _, v := range vs {
if len(v) > 0 {
return v
}
}
return ""
}

View File

@ -78,7 +78,6 @@ type restoreStats struct {
} }
// Run begins a synchronous restore operation. // Run begins a synchronous restore operation.
// todo (keepers): return stats block in first param.
func (op *RestoreOperation) Run(ctx context.Context) (err error) { func (op *RestoreOperation) Run(ctx context.Context) (err error) {
// TODO: persist initial state of restoreOperation in modelstore // TODO: persist initial state of restoreOperation in modelstore
@ -157,7 +156,6 @@ func (op *RestoreOperation) persistResults(
op.Status = Completed op.Status = Completed
if !opStats.started { if !opStats.started {
op.Status = Failed
op.Status = Failed op.Status = Failed
return multierror.Append( return multierror.Append(
errors.New("errors prevented the operation from processing"), errors.New("errors prevented the operation from processing"),

View File

@ -10,8 +10,10 @@ import (
const ( const (
CorsoCITests = "CORSO_CI_TESTS" CorsoCITests = "CORSO_CI_TESTS"
CorsoCLIConfigTests = "CORSO_CLI_CONFIG_TESTS" CorsoCLIBackupTests = "CORSO_COMMAND_LINE_BACKUP_TESTS"
CorsoCLIRepoTests = "CORSO_CLI_REPO_TESTS" CorsoCLIConfigTests = "CORSO_COMMAND_LINE_CONFIG_TESTS"
CorsoCLIRepoTests = "CORSO_COMMAND_LINE_REPO_TESTS"
CorsoCLITests = "CORSO_COMMAND_LINE_TESTS"
CorsoGraphConnectorTests = "CORSO_GRAPH_CONNECTOR_TESTS" CorsoGraphConnectorTests = "CORSO_GRAPH_CONNECTOR_TESTS"
CorsoKopiaWrapperTests = "CORSO_KOPIA_WRAPPER_TESTS" CorsoKopiaWrapperTests = "CORSO_KOPIA_WRAPPER_TESTS"
CorsoModelStoreTests = "CORSO_MODEL_STORE_TESTS" CorsoModelStoreTests = "CORSO_MODEL_STORE_TESTS"
@ -42,7 +44,7 @@ func RunOnAny(tests ...string) error {
// LogTimeOfTest logs the test name and the time that it was run. // LogTimeOfTest logs the test name and the time that it was run.
func LogTimeOfTest(t *testing.T) string { func LogTimeOfTest(t *testing.T) string {
now := time.Now().UTC().Format("2006-01-02T15:04:05.0000") now := time.Now().UTC().Format(time.RFC3339Nano)
name := t.Name() name := t.Name()
if name == "" { if name == "" {
t.Logf("Test run at %s.", now) t.Logf("Test run at %s.", now)