diff --git a/src/internal/connector/graph_connector_helper_test.go b/src/internal/connector/graph_connector_helper_test.go index 1089f67bf..5a50af8c2 100644 --- a/src/internal/connector/graph_connector_helper_test.go +++ b/src/internal/connector/graph_connector_helper_test.go @@ -660,6 +660,151 @@ func checkCollections( assert.Equal(t, expectedItems, gotItems, "expected items") } +func mustParsePath(t *testing.T, p string, isItem bool) path.Path { + res, err := path.FromDataLayerPath(p, isItem) + require.NoError(t, err) + + return res +} + +func makeExchangeBackupSel( + t *testing.T, + expected map[string]map[string][]byte, +) selectors.Selector { + sel := selectors.NewExchangeBackup() + toInclude := [][]selectors.ExchangeScope{} + + for p := range expected { + pth := mustParsePath(t, p, false) + require.Equal(t, path.ExchangeService.String(), pth.Service().String()) + + switch pth.Category() { + case path.EmailCategory: + toInclude = append(toInclude, sel.MailFolders( + []string{pth.ResourceOwner()}, + []string{backupInputFromPath(pth).String()}, + )) + case path.ContactsCategory: + toInclude = append(toInclude, sel.ContactFolders( + []string{pth.ResourceOwner()}, + []string{backupInputFromPath(pth).String()}, + )) + case path.EventsCategory: + toInclude = append(toInclude, sel.EventCalendars( + []string{pth.ResourceOwner()}, + []string{backupInputFromPath(pth).String()}, + )) + } + } + + sel.Include(toInclude...) + + return sel.Selector +} + +func makeOneDriveBackupSel( + t *testing.T, + expected map[string]map[string][]byte, +) selectors.Selector { + sel := selectors.NewOneDriveBackup() + toInclude := [][]selectors.OneDriveScope{} + + for p := range expected { + pth := mustParsePath(t, p, false) + require.Equal(t, path.OneDriveService.String(), pth.Service().String()) + + toInclude = append(toInclude, sel.Folders( + []string{pth.ResourceOwner()}, + []string{backupInputFromPath(pth).String()}, + )) + } + + sel.Include(toInclude...) + + return sel.Selector +} + +// backupSelectorForExpected creates a selector that can be used to backup the +// given items in expected based on the item paths. Fails the test if items from +// multiple services are in expected. +func backupSelectorForExpected( + t *testing.T, + expected map[string]map[string][]byte, +) selectors.Selector { + require.NotEmpty(t, expected) + + // Sadly need to get the first item to figure out what sort of selector we + // need. + for p := range expected { + pth := mustParsePath(t, p, false) + + switch pth.Service() { + case path.ExchangeService: + return makeExchangeBackupSel(t, expected) + case path.OneDriveService: + return makeOneDriveBackupSel(t, expected) + default: + assert.FailNowf(t, "bad serivce type %s", pth.Service().String()) + } + } + + // Fix compile error about no return. Should not reach here. + return selectors.Selector{} +} + +// backupInputFromPath returns a path.Builder with the path that a call to Backup +// can use to backup the given items. +func backupInputFromPath( + inputPath path.Path, +) *path.Builder { + startIdx := 0 + + if inputPath.Service() == path.OneDriveService { + // OneDrive has folders that are trimmed off in the app that are present in + // Corso. + startIdx = 3 + } + + return path.Builder{}.Append(inputPath.Folders()[startIdx:]...) +} + +// backupOutputPathFromRestore returns a path.Path denoting the location in +// kopia the data will be placed at. The location is a data-type specific +// combination of the location the data was recently restored to and where the +// data was originally in the hierarchy. +func backupOutputPathFromRestore( + t *testing.T, + restoreDest control.RestoreDestination, + inputPath path.Path, +) path.Path { + base := []string{restoreDest.ContainerName} + + // OneDrive has leading information like the drive ID. + if inputPath.Service() == path.OneDriveService { + folders := inputPath.Folders() + base = append(append([]string{}, folders[:3]...), restoreDest.ContainerName) + + if len(folders) > 3 { + base = append(base, folders[3:]...) + } + } + + // TODO(ashmrtn): Uncomment when exchange mail supports restoring to subfolders. + // if inputPath.Service == path.ExchangeService && inputPath.Category() == path.EmailCategory { + // base = append(base, inputPath.Folders()...) + // } + + return mustToDataLayerPath( + t, + inputPath.Service(), + inputPath.Tenant(), + inputPath.ResourceOwner(), + inputPath.Category(), + base, + false, + ) +} + func collectionsForInfo( t *testing.T, service path.ServiceType, @@ -682,19 +827,7 @@ func collectionsForInfo( false, ) c := mockconnector.NewMockExchangeCollection(pth, len(info.items)) - - // TODO(ashmrtn): This will need expanded/broken up by service/category - // depending on how restore for that service/category places data back in - // M365. - baseDestPath := mustToDataLayerPath( - t, - service, - tenant, - user, - info.category, - []string{dest.ContainerName}, - false, - ) + baseDestPath := backupOutputPathFromRestore(t, dest, pth) baseExpected := expectedData[baseDestPath.String()] if baseExpected == nil { diff --git a/src/internal/connector/graph_connector_test.go b/src/internal/connector/graph_connector_test.go index e6385b87a..693ed7bd7 100644 --- a/src/internal/connector/graph_connector_test.go +++ b/src/internal/connector/graph_connector_test.go @@ -457,7 +457,6 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() { name string service path.ServiceType collections []colInfo - backupSelFunc func(dest control.RestoreDestination, backupUser string) selectors.Selector expectedRestoreFolders int }{ { @@ -486,17 +485,6 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() { }, }, }, - // TODO(ashmrtn): Generalize this once we know the path transforms that - // occur during restore. - backupSelFunc: func(dest control.RestoreDestination, backupUser string) selectors.Selector { - backupSel := selectors.NewExchangeBackup() - backupSel.Include(backupSel.MailFolders( - []string{backupUser}, - []string{dest.ContainerName}, - )) - - return backupSel.Selector - }, }, { name: "MultipleEmailsSingleFolder", @@ -534,17 +522,6 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() { }, }, }, - // TODO(ashmrtn): Generalize this once we know the path transforms that - // occur during restore. - backupSelFunc: func(dest control.RestoreDestination, backupUser string) selectors.Selector { - backupSel := selectors.NewExchangeBackup() - backupSel.Include(backupSel.MailFolders( - []string{backupUser}, - []string{dest.ContainerName}, - )) - - return backupSel.Selector - }, }, { name: "MultipleContactsSingleFolder", @@ -573,17 +550,6 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() { }, }, }, - // TODO(ashmrtn): Generalize this once we know the path transforms that - // occur during restore. - backupSelFunc: func(dest control.RestoreDestination, backupUser string) selectors.Selector { - backupSel := selectors.NewExchangeBackup() - backupSel.Include(backupSel.ContactFolders( - []string{backupUser}, - []string{dest.ContainerName}, - )) - - return backupSel.Selector - }, }, { name: "MultipleContactsMutlipleFolders", @@ -628,17 +594,6 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() { }, }, }, - // TODO(ashmrtn): Generalize this once we know the path transforms that - // occur during restore. - backupSelFunc: func(dest control.RestoreDestination, backupUser string) selectors.Selector { - backupSel := selectors.NewExchangeBackup() - backupSel.Include(backupSel.ContactFolders( - []string{backupUser}, - []string{dest.ContainerName}, - )) - - return backupSel.Selector - }, }, { name: "MultipleEventsSingleCalendar", @@ -667,17 +622,6 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() { }, }, }, - // TODO(ashmrtn): Generalize this once we know the path transforms that - // occur during restore. - backupSelFunc: func(dest control.RestoreDestination, backupUser string) selectors.Selector { - backupSel := selectors.NewExchangeBackup() - backupSel.Include(backupSel.EventCalendars( - []string{backupUser}, - []string{dest.ContainerName}, - )) - - return backupSel.Selector - }, }, { name: "MultipleEventsMultipleCalendars", @@ -722,17 +666,6 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() { }, }, }, - // TODO(ashmrtn): Generalize this once we know the path transforms that - // occur during restore. - backupSelFunc: func(dest control.RestoreDestination, backupUser string) selectors.Selector { - backupSel := selectors.NewExchangeBackup() - backupSel.Include(backupSel.EventCalendars( - []string{backupUser}, - []string{dest.ContainerName}, - )) - - return backupSel.Selector - }, }, } @@ -774,7 +707,7 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() { // Run a backup and compare its output with what we put in. backupGC := loadConnector(ctx, t) - backupSel := test.backupSelFunc(dest, suite.user) + backupSel := backupSelectorForExpected(t, expectedData) t.Logf("Selective backup of %s\n", backupSel) dcs, err := backupGC.DataCollections(ctx, backupSel) @@ -800,41 +733,6 @@ func (suite *GraphConnectorIntegrationSuite) TestMultiFolderBackupDifferentNames bodyText := "This email has some text. However, all the text is on the same line." subjectText := "Test message for restore" - // TODO(ashmrtn): Update if we start mixing categories during backup/restore. - backupSelFunc := func( - dests []control.RestoreDestination, - category path.CategoryType, - backupUser string, - ) selectors.Selector { - destNames := make([]string, 0, len(dests)) - - for _, d := range dests { - destNames = append(destNames, d.ContainerName) - } - - backupSel := selectors.NewExchangeBackup() - - switch category { - case path.EmailCategory: - backupSel.Include(backupSel.MailFolders( - []string{backupUser}, - destNames, - )) - case path.ContactsCategory: - backupSel.Include(backupSel.ContactFolders( - []string{backupUser}, - destNames, - )) - case path.EventsCategory: - backupSel.Include(backupSel.EventCalendars( - []string{backupUser}, - destNames, - )) - } - - return backupSel.Selector - } - table := []struct { name string service path.ServiceType @@ -994,7 +892,7 @@ func (suite *GraphConnectorIntegrationSuite) TestMultiFolderBackupDifferentNames // Run a backup and compare its output with what we put in. backupGC := loadConnector(ctx, t) - backupSel := backupSelFunc(dests, test.category, suite.user) + backupSel := backupSelectorForExpected(t, allExpectedData) t.Logf("Selective backup of %s\n", backupSel) dcs, err := backupGC.DataCollections(ctx, backupSel)