Skip to content
This repository has been archived by the owner on Oct 11, 2024. It is now read-only.

Commit

Permalink
Test helper functions for selector creation (#1163)
Browse files Browse the repository at this point in the history
## 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
  • Loading branch information
ashmrtn authored Oct 14, 2022
1 parent 42b9b13 commit 29015df
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 117 deletions.
159 changes: 146 additions & 13 deletions src/internal/connector/graph_connector_helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 {
Expand Down
106 changes: 2 additions & 104 deletions src/internal/connector/graph_connector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}{
{
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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
},
},
}

Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 29015df

Please sign in to comment.