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:
parent
42b9b133af
commit
29015df8ef
@ -660,6 +660,151 @@ func checkCollections(
|
|||||||
assert.Equal(t, expectedItems, gotItems, "expected items")
|
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(
|
func collectionsForInfo(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
service path.ServiceType,
|
service path.ServiceType,
|
||||||
@ -682,19 +827,7 @@ func collectionsForInfo(
|
|||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
c := mockconnector.NewMockExchangeCollection(pth, len(info.items))
|
c := mockconnector.NewMockExchangeCollection(pth, len(info.items))
|
||||||
|
baseDestPath := backupOutputPathFromRestore(t, dest, pth)
|
||||||
// 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,
|
|
||||||
)
|
|
||||||
|
|
||||||
baseExpected := expectedData[baseDestPath.String()]
|
baseExpected := expectedData[baseDestPath.String()]
|
||||||
if baseExpected == nil {
|
if baseExpected == nil {
|
||||||
|
|||||||
@ -457,7 +457,6 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() {
|
|||||||
name string
|
name string
|
||||||
service path.ServiceType
|
service path.ServiceType
|
||||||
collections []colInfo
|
collections []colInfo
|
||||||
backupSelFunc func(dest control.RestoreDestination, backupUser string) selectors.Selector
|
|
||||||
expectedRestoreFolders int
|
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",
|
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",
|
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",
|
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",
|
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",
|
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.
|
// Run a backup and compare its output with what we put in.
|
||||||
|
|
||||||
backupGC := loadConnector(ctx, t)
|
backupGC := loadConnector(ctx, t)
|
||||||
backupSel := test.backupSelFunc(dest, suite.user)
|
backupSel := backupSelectorForExpected(t, expectedData)
|
||||||
t.Logf("Selective backup of %s\n", backupSel)
|
t.Logf("Selective backup of %s\n", backupSel)
|
||||||
|
|
||||||
dcs, err := backupGC.DataCollections(ctx, 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."
|
bodyText := "This email has some text. However, all the text is on the same line."
|
||||||
subjectText := "Test message for restore"
|
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 {
|
table := []struct {
|
||||||
name string
|
name string
|
||||||
service path.ServiceType
|
service path.ServiceType
|
||||||
@ -994,7 +892,7 @@ func (suite *GraphConnectorIntegrationSuite) TestMultiFolderBackupDifferentNames
|
|||||||
// Run a backup and compare its output with what we put in.
|
// Run a backup and compare its output with what we put in.
|
||||||
|
|
||||||
backupGC := loadConnector(ctx, t)
|
backupGC := loadConnector(ctx, t)
|
||||||
backupSel := backupSelFunc(dests, test.category, suite.user)
|
backupSel := backupSelectorForExpected(t, allExpectedData)
|
||||||
t.Logf("Selective backup of %s\n", backupSel)
|
t.Logf("Selective backup of %s\n", backupSel)
|
||||||
|
|
||||||
dcs, err := backupGC.DataCollections(ctx, backupSel)
|
dcs, err := backupGC.DataCollections(ctx, backupSel)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user