Test helper functions for selector creation (#1163)

## Description

Mostly path manipulations here to allow creating selectors for a backup and paths for the output of a backup given some information about the input. Capable of working for OneDrive and Exchange.

## Type of change

<!--- Please check the type of change your PR introduces: --->
- [x] 🌻 Feature
- [ ] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [ ] 🤖 Test
- [ ] 💻 CI/Deployment
- [ ] 🐹 Trivial/Minor

## Issue(s)

* #913 

## Test Plan

<!-- How will this be tested prior to merging.-->
- [ ] 💪 Manual
- [x]  Unit test
- [ ] 💚 E2E
This commit is contained in:
ashmrtn 2022-10-13 18:16:29 -07:00 committed by GitHub
parent 42b9b133af
commit 29015df8ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 148 additions and 117 deletions

View File

@ -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 {

View File

@ -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)