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:
parent
6464a66b46
commit
192c69c68f
75
src/cli/backup/exchange_integration_test.go
Normal file
75
src/cli/backup/exchange_integration_test.go
Normal 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))
|
||||||
|
}
|
||||||
@ -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
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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 ""
|
|
||||||
}
|
|
||||||
|
|||||||
@ -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 ""
|
||||||
|
}
|
||||||
|
|||||||
@ -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"),
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user