From 14bd16f97885ac7ea59849951958e3f10c2f5dbe Mon Sep 17 00:00:00 2001 From: ashmrtn Date: Mon, 24 Oct 2022 11:36:26 -0700 Subject: [PATCH] Exchange multiuser restore and backup tests (#1195) ## Description Basic smoke tests to ensure multi-user restore and backup work properly. Will need updated to add OneDrive tests later on once the OneDrive test pattern is merged ## Type of change - [ ] :sunflower: Feature - [ ] :bug: Bugfix - [ ] :world_map: Documentation - [x] :robot: Test - [ ] :computer: CI/Deployment - [ ] :hamster: Trivial/Minor ## Issue(s) * #913 merge after * #1186 ## Test Plan - [ ] :muscle: Manual - [x] :zap: Unit test - [ ] :green_heart: E2E --- .../connector/graph_connector_test.go | 245 ++++++++++++++---- 1 file changed, 195 insertions(+), 50 deletions(-) diff --git a/src/internal/connector/graph_connector_test.go b/src/internal/connector/graph_connector_test.go index 6f8480bef..7c193ce68 100644 --- a/src/internal/connector/graph_connector_test.go +++ b/src/internal/connector/graph_connector_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -469,6 +470,89 @@ func (suite *GraphConnectorIntegrationSuite) TestEmptyCollections() { } } +func runRestoreBackupTest( + t *testing.T, + test restoreBackupInfo, + tenant string, + users []string, +) { + var ( + collections []data.Collection + expectedData = map[string]map[string][]byte{} + totalItems = 0 + // Get a dest per test so they're independent. + dest = tester.DefaultTestRestoreDestination() + ) + + ctx, flush := tester.NewContext() + defer flush() + + for _, user := range users { + numItems, userCollections, userExpectedData := collectionsForInfo( + t, + test.service, + tenant, + user, + dest, + test.collections, + ) + + collections = append(collections, userCollections...) + totalItems += numItems + + for k, v := range userExpectedData { + expectedData[k] = v + } + } + + t.Logf( + "Restoring collections to %s for user(s) %v\n", + dest.ContainerName, + users, + ) + + start := time.Now() + + restoreGC := loadConnector(ctx, t) + restoreSel := getSelectorWith(test.service) + deets, err := restoreGC.RestoreDataCollections(ctx, restoreSel, dest, collections) + require.NoError(t, err) + assert.NotNil(t, deets) + + status := restoreGC.AwaitStatus() + runTime := time.Now().Sub(start) + + assert.Equal(t, totalItems, status.ObjectCount, "status.ObjectCount") + assert.Equal(t, totalItems, status.Successful, "status.Successful") + assert.Len( + t, + deets.Entries, + totalItems, + "details entries contains same item count as total successful items restored") + + t.Logf("Restore complete in %v\n", runTime) + + // Run a backup and compare its output with what we put in. + + backupGC := loadConnector(ctx, t) + backupSel := backupSelectorForExpected(t, expectedData) + t.Logf("Selective backup of %s\n", backupSel) + + start = time.Now() + dcs, err := backupGC.DataCollections(ctx, backupSel) + require.NoError(t, err) + + t.Logf("Backup enumeration complete in %v\n", time.Now().Sub(start)) + + // Pull the data prior to waiting for the status as otherwise it will + // deadlock. + checkCollections(t, totalItems, expectedData, dcs) + + status = backupGC.AwaitStatus() + assert.Equal(t, totalItems, status.ObjectCount, "status.ObjectCount") + assert.Equal(t, totalItems, status.Successful, "status.Successful") +} + func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() { bodyText := "This email has some text. However, all the text is on the same line." subjectText := "Test message for restore" @@ -689,56 +773,7 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() { for _, test := range table { suite.T().Run(test.name, func(t *testing.T) { - ctx, flush := tester.NewContext() - defer flush() - - // Get a dest per test so they're independent. - dest := tester.DefaultTestRestoreDestination() - - totalItems, collections, expectedData := collectionsForInfo( - t, - test.service, - suite.connector.tenant, - suite.user, - dest, - test.collections, - ) - - t.Logf("Restoring collections to %s\n", dest.ContainerName) - - restoreGC := loadConnector(ctx, t) - restoreSel := getSelectorWith(test.service) - deets, err := restoreGC.RestoreDataCollections(ctx, restoreSel, dest, collections) - require.NoError(t, err) - assert.NotNil(t, deets) - - status := restoreGC.AwaitStatus() - assert.Equal(t, totalItems, status.ObjectCount, "status.ObjectCount") - assert.Equal(t, totalItems, status.Successful, "status.Successful") - assert.Equal( - t, totalItems, len(deets.Entries), - "details entries contains same item count as total successful items restored") - - t.Logf("Restore complete\n") - - // Run a backup and compare its output with what we put in. - - backupGC := loadConnector(ctx, t) - backupSel := backupSelectorForExpected(t, expectedData) - t.Logf("Selective backup of %s\n", backupSel) - - dcs, err := backupGC.DataCollections(ctx, backupSel) - require.NoError(t, err) - - t.Logf("Backup enumeration complete\n") - - // Pull the data prior to waiting for the status as otherwise it will - // deadlock. - checkCollections(t, totalItems, expectedData, dcs) - - status = backupGC.AwaitStatus() - assert.Equal(t, totalItems, status.ObjectCount, "status.ObjectCount") - assert.Equal(t, totalItems, status.Successful, "status.Successful") + runRestoreBackupTest(t, test, suite.connector.tenant, []string{suite.user}) }) } } @@ -876,3 +911,113 @@ func (suite *GraphConnectorIntegrationSuite) TestMultiFolderBackupDifferentNames }) } } + +func (suite *GraphConnectorIntegrationSuite) TestMultiuserRestoreAndBackup() { + bodyText := "This email has some text. However, all the text is on the same line." + subjectText := "Test message for restore" + + users := []string{ + suite.user, + tester.SecondaryM365UserID(suite.T()), + } + table := []restoreBackupInfo{ + { + name: "Email", + service: path.ExchangeService, + collections: []colInfo{ + { + pathElements: []string{"Inbox"}, + category: path.EmailCategory, + items: []itemInfo{ + { + name: "someencodeditemID", + data: mockconnector.GetMockMessageWithBodyBytes( + subjectText+"-1", + bodyText+" 1.", + bodyText+" 1.", + ), + lookupKey: subjectText + "-1", + }, + }, + }, + { + pathElements: []string{"Archive"}, + category: path.EmailCategory, + items: []itemInfo{ + { + name: "someencodeditemID2", + data: mockconnector.GetMockMessageWithBodyBytes( + subjectText+"-2", + bodyText+" 2.", + bodyText+" 2.", + ), + lookupKey: subjectText + "-2", + }, + }, + }, + }, + }, + { + name: "Contacts", + service: path.ExchangeService, + collections: []colInfo{ + { + pathElements: []string{"Work"}, + category: path.ContactsCategory, + items: []itemInfo{ + { + name: "someencodeditemID", + data: mockconnector.GetMockContactBytes("Ghimley"), + lookupKey: "Ghimley", + }, + }, + }, + { + pathElements: []string{"Personal"}, + category: path.ContactsCategory, + items: []itemInfo{ + { + name: "someencodeditemID2", + data: mockconnector.GetMockContactBytes("Irgot"), + lookupKey: "Irgot", + }, + }, + }, + }, + }, + { + name: "Events", + service: path.ExchangeService, + collections: []colInfo{ + { + pathElements: []string{"Work"}, + category: path.EventsCategory, + items: []itemInfo{ + { + name: "someencodeditemID", + data: mockconnector.GetMockEventWithSubjectBytes("Ghimley"), + lookupKey: "Ghimley", + }, + }, + }, + { + pathElements: []string{"Personal"}, + category: path.EventsCategory, + items: []itemInfo{ + { + name: "someencodeditemID2", + data: mockconnector.GetMockEventWithSubjectBytes("Irgot"), + lookupKey: "Irgot", + }, + }, + }, + }, + }, + } + + for _, test := range table { + suite.T().Run(test.name, func(t *testing.T) { + runRestoreBackupTest(t, test, suite.connector.tenant, users) + }) + } +}