diff --git a/src/internal/operations/backup_integration_test.go b/src/internal/operations/backup_e2e_test.go similarity index 97% rename from src/internal/operations/backup_integration_test.go rename to src/internal/operations/backup_e2e_test.go index 4876bb3d3..dfe004d08 100644 --- a/src/internal/operations/backup_integration_test.go +++ b/src/internal/operations/backup_e2e_test.go @@ -447,27 +447,30 @@ func toDataLayerPath( } // --------------------------------------------------------------------------- -// integration tests +// e2e tests // --------------------------------------------------------------------------- -type BackupOpIntegrationSuite struct { - suite.Suite +type BackupOpE2ESuite struct { + tester.Suite user, site string } -func TestBackupOpIntegrationSuite(t *testing.T) { - tester.RunOnAny(t, tester.CorsoCITests) - suite.Run(t, new(BackupOpIntegrationSuite)) +func TestBackupOpE2ESuite(t *testing.T) { + suite.Run(t, &BackupOpE2ESuite{ + Suite: tester.NewE2ESuite( + t, + [][]string{tester.AWSStorageCredEnvs, tester.M365AcctCredEnvs}, + tester.CorsoCITests, + ), + }) } -func (suite *BackupOpIntegrationSuite) SetupSuite() { - tester.MustGetEnvSets(suite.T(), tester.AWSStorageCredEnvs, tester.M365AcctCredEnvs) - +func (suite *BackupOpE2ESuite) SetupSuite() { suite.user = tester.M365UserID(suite.T()) suite.site = tester.M365SiteID(suite.T()) } -func (suite *BackupOpIntegrationSuite) TestNewBackupOperation() { +func (suite *BackupOpE2ESuite) TestNewBackupOperation() { kw := &kopia.Wrapper{} sw := &store.Wrapper{} acct := tester.NewM365Account(suite.T()) @@ -509,7 +512,7 @@ func (suite *BackupOpIntegrationSuite) TestNewBackupOperation() { // TestBackup_Run ensures that Integration Testing works // for the following scopes: Contacts, Events, and Mail -func (suite *BackupOpIntegrationSuite) TestBackup_Run_exchange() { +func (suite *BackupOpE2ESuite) TestBackup_Run_exchange() { ctx, flush := tester.NewContext() defer flush() @@ -637,7 +640,7 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_exchange() { // TestBackup_Run ensures that Integration Testing works // for the following scopes: Contacts, Events, and Mail -func (suite *BackupOpIntegrationSuite) TestBackup_Run_exchangeIncrementals() { +func (suite *BackupOpE2ESuite) TestBackup_Run_exchangeIncrementals() { ctx, flush := tester.NewContext() defer flush() @@ -1069,7 +1072,7 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_exchangeIncrementals() { // OneDrive // --------------------------------------------------------------------------- -func (suite *BackupOpIntegrationSuite) TestBackup_Run_oneDrive() { +func (suite *BackupOpE2ESuite) TestBackup_Run_oneDrive() { ctx, flush := tester.NewContext() defer flush() @@ -1092,7 +1095,7 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_oneDrive() { // SharePoint // --------------------------------------------------------------------------- -func (suite *BackupOpIntegrationSuite) TestBackup_Run_sharePoint() { +func (suite *BackupOpE2ESuite) TestBackup_Run_sharePoint() { ctx, flush := tester.NewContext() defer flush() diff --git a/src/internal/operations/restore_e2e_test.go b/src/internal/operations/restore_e2e_test.go new file mode 100644 index 000000000..84370fb39 --- /dev/null +++ b/src/internal/operations/restore_e2e_test.go @@ -0,0 +1,236 @@ +package operations + +import ( + "context" + "testing" + + "github.com/alcionai/corso/src/internal/connector/exchange" + "github.com/alcionai/corso/src/internal/events" + evmock "github.com/alcionai/corso/src/internal/events/mock" + "github.com/alcionai/corso/src/internal/kopia" + "github.com/alcionai/corso/src/internal/model" + "github.com/alcionai/corso/src/internal/tester" + "github.com/alcionai/corso/src/pkg/account" + "github.com/alcionai/corso/src/pkg/control" + "github.com/alcionai/corso/src/pkg/selectors" + "github.com/alcionai/corso/src/pkg/store" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +type RestoreOpE2ESuite struct { + tester.Suite + + backupID model.StableID + numItems int + kopiaCloser func(ctx context.Context) + kw *kopia.Wrapper + sw *store.Wrapper + ms *kopia.ModelStore +} + +func TestRestoreOpE2ESuite(t *testing.T) { + suite.Run(t, &RestoreOpE2ESuite{ + Suite: tester.NewE2ESuite( + t, + [][]string{tester.AWSStorageCredEnvs, tester.M365AcctCredEnvs}, + tester.CorsoCITests, + ), + }) +} + +func (suite *RestoreOpE2ESuite) SetupSuite() { + ctx, flush := tester.NewContext() + defer flush() + + t := suite.T() + + m365UserID := tester.M365UserID(t) + acct := tester.NewM365Account(t) + + // need to initialize the repository before we can test connecting to it. + st := tester.NewPrefixedS3Storage(t) + + k := kopia.NewConn(st) + require.NoError(t, k.Initialize(ctx)) + + suite.kopiaCloser = func(ctx context.Context) { + k.Close(ctx) + } + + kw, err := kopia.NewWrapper(k) + require.NoError(t, err) + + suite.kw = kw + + ms, err := kopia.NewModelStore(k) + require.NoError(t, err) + + suite.ms = ms + + sw := store.NewKopiaStore(ms) + suite.sw = sw + + users := []string{m365UserID} + + bsel := selectors.NewExchangeBackup(users) + bsel.DiscreteOwner = m365UserID + bsel.Include( + bsel.MailFolders([]string{exchange.DefaultMailFolder}, selectors.PrefixMatch()), + bsel.ContactFolders([]string{exchange.DefaultContactFolder}, selectors.PrefixMatch()), + bsel.EventCalendars([]string{exchange.DefaultCalendar}, selectors.PrefixMatch()), + ) + + bo, err := NewBackupOperation( + ctx, + control.Options{}, + kw, + sw, + acct, + bsel.Selector, + evmock.NewBus()) + require.NoError(t, err) + require.NoError(t, bo.Run(ctx)) + require.NotEmpty(t, bo.Results.BackupID) + + suite.backupID = bo.Results.BackupID + // Discount metadata files (3 paths, 3 deltas) as + // they are not part of the data restored. + suite.numItems = bo.Results.ItemsWritten - 6 +} + +func (suite *RestoreOpE2ESuite) TearDownSuite() { + ctx, flush := tester.NewContext() + defer flush() + + if suite.ms != nil { + suite.ms.Close(ctx) + } + + if suite.kw != nil { + suite.kw.Close(ctx) + } + + if suite.kopiaCloser != nil { + suite.kopiaCloser(ctx) + } +} + +func (suite *RestoreOpE2ESuite) TestNewRestoreOperation() { + kw := &kopia.Wrapper{} + sw := &store.Wrapper{} + acct := tester.NewM365Account(suite.T()) + dest := tester.DefaultTestRestoreDestination() + + table := []struct { + name string + opts control.Options + kw *kopia.Wrapper + sw *store.Wrapper + acct account.Account + targets []string + errCheck assert.ErrorAssertionFunc + }{ + {"good", control.Options{}, kw, sw, acct, nil, assert.NoError}, + {"missing kopia", control.Options{}, nil, sw, acct, nil, assert.Error}, + {"missing modelstore", control.Options{}, kw, nil, acct, nil, assert.Error}, + } + for _, test := range table { + suite.T().Run(test.name, func(t *testing.T) { + ctx, flush := tester.NewContext() + defer flush() + + _, err := NewRestoreOperation( + ctx, + test.opts, + test.kw, + test.sw, + test.acct, + "backup-id", + selectors.Selector{DiscreteOwner: "test"}, + dest, + evmock.NewBus()) + test.errCheck(t, err) + }) + } +} + +func (suite *RestoreOpE2ESuite) TestRestore_Run() { + ctx, flush := tester.NewContext() + defer flush() + + t := suite.T() + users := []string{tester.M365UserID(t)} + + rsel := selectors.NewExchangeRestore(users) + rsel.Include(rsel.AllData()) + + dest := tester.DefaultTestRestoreDestination() + mb := evmock.NewBus() + + ro, err := NewRestoreOperation( + ctx, + control.Options{}, + suite.kw, + suite.sw, + tester.NewM365Account(t), + suite.backupID, + rsel.Selector, + dest, + mb) + require.NoError(t, err) + + ds, err := ro.Run(ctx) + + require.NoError(t, err, "restoreOp.Run()") + require.Empty(t, ro.Errors.Errs(), "restoreOp.Run() recoverable errors") + require.NotEmpty(t, ro.Results, "restoreOp results") + require.NotNil(t, ds, "restored details") + assert.Equal(t, ro.Status, Completed, "restoreOp status") + assert.Equal(t, ro.Results.ItemsWritten, len(ds.Entries), "count of items written matches restored entries in details") + assert.Less(t, 0, ro.Results.ItemsRead, "restore items read") + assert.Less(t, 0, ro.Results.ItemsWritten, "restored items written") + assert.Less(t, int64(0), ro.Results.BytesRead, "bytes read") + assert.Equal(t, 1, ro.Results.ResourceOwners, "resource Owners") + assert.NoError(t, ro.Errors.Err(), "non-recoverable error") + assert.Empty(t, ro.Errors.Errs(), "recoverable errors") + assert.NoError(t, ro.Results.ReadErrors, "errors while reading restore data") + assert.NoError(t, ro.Results.WriteErrors, "errors while writing restore data") + assert.Equal(t, suite.numItems, ro.Results.ItemsWritten, "backup and restore wrote the same num of items") + assert.Equal(t, 1, mb.TimesCalled[events.RestoreStart], "restore-start events") + assert.Equal(t, 1, mb.TimesCalled[events.RestoreEnd], "restore-end events") +} + +func (suite *RestoreOpE2ESuite) TestRestore_Run_ErrorNoResults() { + ctx, flush := tester.NewContext() + defer flush() + + t := suite.T() + + rsel := selectors.NewExchangeRestore(selectors.None()) + rsel.Include(rsel.AllData()) + + dest := tester.DefaultTestRestoreDestination() + mb := evmock.NewBus() + + ro, err := NewRestoreOperation( + ctx, + control.Options{}, + suite.kw, + suite.sw, + tester.NewM365Account(t), + suite.backupID, + rsel.Selector, + dest, + mb) + require.NoError(t, err) + + ds, err := ro.Run(ctx) + require.Error(t, err, "restoreOp.Run() should have errored") + require.Nil(t, ds, "restoreOp.Run() should not produce details") + assert.Zero(t, ro.Results.ResourceOwners, "resource owners") + assert.Zero(t, ro.Results.BytesRead, "bytes read") + assert.Equal(t, 1, mb.TimesCalled[events.RestoreStart], "restore-start events") + assert.Zero(t, mb.TimesCalled[events.RestoreEnd], "restore-end events") +} diff --git a/src/internal/operations/restore_test.go b/src/internal/operations/restore_test.go index db9f84986..68f177b79 100644 --- a/src/internal/operations/restore_test.go +++ b/src/internal/operations/restore_test.go @@ -1,7 +1,6 @@ package operations import ( - "context" "testing" "time" @@ -9,14 +8,11 @@ import ( "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" - "github.com/alcionai/corso/src/internal/connector/exchange" "github.com/alcionai/corso/src/internal/connector/mockconnector" "github.com/alcionai/corso/src/internal/connector/support" "github.com/alcionai/corso/src/internal/data" - "github.com/alcionai/corso/src/internal/events" evmock "github.com/alcionai/corso/src/internal/events/mock" "github.com/alcionai/corso/src/internal/kopia" - "github.com/alcionai/corso/src/internal/model" "github.com/alcionai/corso/src/internal/stats" "github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/pkg/account" @@ -119,221 +115,3 @@ func (suite *RestoreOpSuite) TestRestoreOperation_PersistResults() { }) } } - -// --------------------------------------------------------------------------- -// integration -// --------------------------------------------------------------------------- - -type RestoreOpIntegrationSuite struct { - suite.Suite - - backupID model.StableID - numItems int - kopiaCloser func(ctx context.Context) - kw *kopia.Wrapper - sw *store.Wrapper - ms *kopia.ModelStore -} - -func TestRestoreOpIntegrationSuite(t *testing.T) { - tester.RunOnAny(t, tester.CorsoCITests) - - suite.Run(t, new(RestoreOpIntegrationSuite)) -} - -func (suite *RestoreOpIntegrationSuite) SetupSuite() { - ctx, flush := tester.NewContext() - defer flush() - - tester.MustGetEnvSets(suite.T(), tester.M365AcctCredEnvs) - - t := suite.T() - - m365UserID := tester.M365UserID(t) - acct := tester.NewM365Account(t) - - // need to initialize the repository before we can test connecting to it. - st := tester.NewPrefixedS3Storage(t) - - k := kopia.NewConn(st) - require.NoError(t, k.Initialize(ctx)) - - suite.kopiaCloser = func(ctx context.Context) { - k.Close(ctx) - } - - kw, err := kopia.NewWrapper(k) - require.NoError(t, err) - - suite.kw = kw - - ms, err := kopia.NewModelStore(k) - require.NoError(t, err) - - suite.ms = ms - - sw := store.NewKopiaStore(ms) - suite.sw = sw - - users := []string{m365UserID} - - bsel := selectors.NewExchangeBackup(users) - bsel.DiscreteOwner = m365UserID - bsel.Include( - bsel.MailFolders([]string{exchange.DefaultMailFolder}, selectors.PrefixMatch()), - bsel.ContactFolders([]string{exchange.DefaultContactFolder}, selectors.PrefixMatch()), - bsel.EventCalendars([]string{exchange.DefaultCalendar}, selectors.PrefixMatch()), - ) - - bo, err := NewBackupOperation( - ctx, - control.Options{}, - kw, - sw, - acct, - bsel.Selector, - evmock.NewBus()) - require.NoError(t, err) - require.NoError(t, bo.Run(ctx)) - require.NotEmpty(t, bo.Results.BackupID) - - suite.backupID = bo.Results.BackupID - // Discount metadata files (3 paths, 3 deltas) as - // they are not part of the data restored. - suite.numItems = bo.Results.ItemsWritten - 6 -} - -func (suite *RestoreOpIntegrationSuite) TearDownSuite() { - ctx, flush := tester.NewContext() - defer flush() - - if suite.ms != nil { - suite.ms.Close(ctx) - } - - if suite.kw != nil { - suite.kw.Close(ctx) - } - - if suite.kopiaCloser != nil { - suite.kopiaCloser(ctx) - } -} - -func (suite *RestoreOpIntegrationSuite) TestNewRestoreOperation() { - kw := &kopia.Wrapper{} - sw := &store.Wrapper{} - acct := tester.NewM365Account(suite.T()) - dest := tester.DefaultTestRestoreDestination() - - table := []struct { - name string - opts control.Options - kw *kopia.Wrapper - sw *store.Wrapper - acct account.Account - targets []string - errCheck assert.ErrorAssertionFunc - }{ - {"good", control.Options{}, kw, sw, acct, nil, assert.NoError}, - {"missing kopia", control.Options{}, nil, sw, acct, nil, assert.Error}, - {"missing modelstore", control.Options{}, kw, nil, acct, nil, assert.Error}, - } - for _, test := range table { - suite.T().Run(test.name, func(t *testing.T) { - ctx, flush := tester.NewContext() - defer flush() - - _, err := NewRestoreOperation( - ctx, - test.opts, - test.kw, - test.sw, - test.acct, - "backup-id", - selectors.Selector{DiscreteOwner: "test"}, - dest, - evmock.NewBus()) - test.errCheck(t, err) - }) - } -} - -func (suite *RestoreOpIntegrationSuite) TestRestore_Run() { - ctx, flush := tester.NewContext() - defer flush() - - t := suite.T() - users := []string{tester.M365UserID(t)} - - rsel := selectors.NewExchangeRestore(users) - rsel.Include(rsel.AllData()) - - dest := tester.DefaultTestRestoreDestination() - mb := evmock.NewBus() - - ro, err := NewRestoreOperation( - ctx, - control.Options{}, - suite.kw, - suite.sw, - tester.NewM365Account(t), - suite.backupID, - rsel.Selector, - dest, - mb) - require.NoError(t, err) - - ds, err := ro.Run(ctx) - - require.NoError(t, err, "restoreOp.Run()") - require.Empty(t, ro.Errors.Errs(), "restoreOp.Run() recoverable errors") - require.NotEmpty(t, ro.Results, "restoreOp results") - require.NotNil(t, ds, "restored details") - assert.Equal(t, ro.Status, Completed, "restoreOp status") - assert.Equal(t, ro.Results.ItemsWritten, len(ds.Entries), "count of items written matches restored entries in details") - assert.Less(t, 0, ro.Results.ItemsRead, "restore items read") - assert.Less(t, 0, ro.Results.ItemsWritten, "restored items written") - assert.Less(t, int64(0), ro.Results.BytesRead, "bytes read") - assert.Equal(t, 1, ro.Results.ResourceOwners, "resource Owners") - assert.NoError(t, ro.Errors.Err(), "non-recoverable error") - assert.Empty(t, ro.Errors.Errs(), "recoverable errors") - assert.NoError(t, ro.Results.ReadErrors, "errors while reading restore data") - assert.NoError(t, ro.Results.WriteErrors, "errors while writing restore data") - assert.Equal(t, suite.numItems, ro.Results.ItemsWritten, "backup and restore wrote the same num of items") - assert.Equal(t, 1, mb.TimesCalled[events.RestoreStart], "restore-start events") - assert.Equal(t, 1, mb.TimesCalled[events.RestoreEnd], "restore-end events") -} - -func (suite *RestoreOpIntegrationSuite) TestRestore_Run_ErrorNoResults() { - ctx, flush := tester.NewContext() - defer flush() - - t := suite.T() - - rsel := selectors.NewExchangeRestore(selectors.None()) - rsel.Include(rsel.AllData()) - - dest := tester.DefaultTestRestoreDestination() - mb := evmock.NewBus() - - ro, err := NewRestoreOperation( - ctx, - control.Options{}, - suite.kw, - suite.sw, - tester.NewM365Account(t), - suite.backupID, - rsel.Selector, - dest, - mb) - require.NoError(t, err) - - ds, err := ro.Run(ctx) - require.Error(t, err, "restoreOp.Run() should have errored") - require.Nil(t, ds, "restoreOp.Run() should not produce details") - assert.Zero(t, ro.Results.ResourceOwners, "resource owners") - assert.Zero(t, ro.Results.BytesRead, "bytes read") - assert.Equal(t, 1, mb.TimesCalled[events.RestoreStart], "restore-start events") - assert.Zero(t, mb.TimesCalled[events.RestoreEnd], "restore-end events") -}