Use prefix matching in GraphConnector tests (#1548)

## Description

Switch to using prefix matchers for restore+backup tests as selectors now support them by default. Not using prefix matchers could eventually lead to failed tests as some tests may have subfolders that could lead to repeated items depending on how the selector is constructed. For example, a selector with scopes matching on `["inbox", "inbox/Work"]` could lead to matching `inbox/Work` twice if only prefix matching is done and GraphConnector iterates over all scopes

## Type of change

<!--- Please check the type of change your PR introduces: --->
- [ ] 🌻 Feature
- [ ] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [x] 🤖 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-11-17 15:03:31 -08:00 committed by GitHub
parent 93ad16dc30
commit 9fe9cd1ce6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 66 additions and 65 deletions

View File

@ -653,11 +653,12 @@ func checkCollections(
expected map[string]map[string][]byte, expected map[string]map[string][]byte,
got []data.Collection, got []data.Collection,
) { ) {
checkHasCollections(t, expected, got) collectionsWithItems := []data.Collection{}
gotItems := 0 gotItems := 0
for _, returned := range got { for _, returned := range got {
startingItems := gotItems
service := returned.FullPath().Service() service := returned.FullPath().Service()
category := returned.FullPath().Category() category := returned.FullPath().Category()
expectedColData := expected[returned.FullPath().String()] expectedColData := expected[returned.FullPath().String()]
@ -674,44 +675,47 @@ func checkCollections(
compareItem(t, expectedColData, service, category, item) compareItem(t, expectedColData, service, category, item)
} }
if gotItems != startingItems {
collectionsWithItems = append(collectionsWithItems, returned)
}
} }
assert.Equal(t, expectedItems, gotItems, "expected items") assert.Equal(t, expectedItems, gotItems, "expected items")
checkHasCollections(t, expected, collectionsWithItems)
} }
func mustParsePath(t *testing.T, p string, isItem bool) path.Path { type destAndCats struct {
res, err := path.FromDataLayerPath(p, isItem) resourceOwner string
require.NoError(t, err) dest string
cats map[path.CategoryType]struct{}
return res
} }
func makeExchangeBackupSel( func makeExchangeBackupSel(
t *testing.T, t *testing.T,
expected map[string]map[string][]byte, dests []destAndCats,
) selectors.Selector { ) selectors.Selector {
sel := selectors.NewExchangeBackup() sel := selectors.NewExchangeBackup()
toInclude := [][]selectors.ExchangeScope{} toInclude := [][]selectors.ExchangeScope{}
for p := range expected { for _, d := range dests {
pth := mustParsePath(t, p, false) for c := range d.cats {
require.Equal(t, path.ExchangeService.String(), pth.Service().String()) builder := sel.MailFolders
builder := sel.MailFolders switch c {
case path.ContactsCategory:
builder = sel.ContactFolders
case path.EventsCategory:
builder = sel.EventCalendars
case path.EmailCategory: // already set
}
switch pth.Category() { toInclude = append(toInclude, builder(
case path.ContactsCategory: []string{d.resourceOwner},
builder = sel.ContactFolders []string{d.dest},
case path.EventsCategory: selectors.PrefixMatch(),
builder = sel.EventCalendars ))
case path.EmailCategory: // already set
} }
toInclude = append(toInclude, builder(
[]string{pth.ResourceOwner()},
[]string{backupInputFromPath(pth).String()},
selectors.PrefixMatch(),
))
} }
sel.Include(toInclude...) sel.Include(toInclude...)
@ -721,18 +725,16 @@ func makeExchangeBackupSel(
func makeOneDriveBackupSel( func makeOneDriveBackupSel(
t *testing.T, t *testing.T,
expected map[string]map[string][]byte, dests []destAndCats,
) selectors.Selector { ) selectors.Selector {
sel := selectors.NewOneDriveBackup() sel := selectors.NewOneDriveBackup()
toInclude := [][]selectors.OneDriveScope{} toInclude := [][]selectors.OneDriveScope{}
for p := range expected { for _, d := range dests {
pth := mustParsePath(t, p, false)
require.Equal(t, path.OneDriveService.String(), pth.Service().String())
toInclude = append(toInclude, sel.Folders( toInclude = append(toInclude, sel.Folders(
[]string{pth.ResourceOwner()}, []string{d.resourceOwner},
[]string{backupInputFromPath(pth).String()}, []string{d.dest},
selectors.PrefixMatch(),
)) ))
} }
@ -746,45 +748,26 @@ func makeOneDriveBackupSel(
// multiple services are in expected. // multiple services are in expected.
func backupSelectorForExpected( func backupSelectorForExpected(
t *testing.T, t *testing.T,
expected map[string]map[string][]byte, service path.ServiceType,
dests []destAndCats,
) selectors.Selector { ) selectors.Selector {
require.NotEmpty(t, expected) require.NotEmpty(t, dests)
// Sadly need to get the first item to figure out what sort of selector we switch service {
// need. case path.ExchangeService:
for p := range expected { return makeExchangeBackupSel(t, dests)
pth := mustParsePath(t, p, false)
switch pth.Service() { case path.OneDriveService:
case path.ExchangeService: return makeOneDriveBackupSel(t, dests)
return makeExchangeBackupSel(t, expected)
case path.OneDriveService: default:
return makeOneDriveBackupSel(t, expected) assert.FailNow(t, "unknown service type %s", service.String())
default:
assert.FailNowf(t, "bad serivce type %s", pth.Service().String())
}
} }
// Fix compile error about no return. Should not reach here. // Fix compile error about no return. Should not reach here.
return selectors.Selector{} 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 // backupOutputPathFromRestore returns a path.Path denoting the location in
// kopia the data will be placed at. The location is a data-type specific // 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 // combination of the location the data was recently restored to and where the

View File

@ -11,7 +11,6 @@ import (
"github.com/alcionai/corso/src/internal/connector/mockconnector" "github.com/alcionai/corso/src/internal/connector/mockconnector"
"github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/pkg/control"
"github.com/alcionai/corso/src/pkg/path" "github.com/alcionai/corso/src/pkg/path"
"github.com/alcionai/corso/src/pkg/selectors" "github.com/alcionai/corso/src/pkg/selectors"
) )
@ -215,9 +214,22 @@ func runRestoreBackupTest(
t.Logf("Restore complete in %v\n", runTime) t.Logf("Restore complete in %v\n", runTime)
// Run a backup and compare its output with what we put in. // Run a backup and compare its output with what we put in.
cats := make(map[path.CategoryType]struct{}, len(test.collections))
for _, c := range test.collections {
cats[c.category] = struct{}{}
}
expectedDests := make([]destAndCats, 0, len(users))
for _, u := range users {
expectedDests = append(expectedDests, destAndCats{
resourceOwner: u,
dest: dest.ContainerName,
cats: cats,
})
}
backupGC := loadConnector(ctx, t) backupGC := loadConnector(ctx, t)
backupSel := backupSelectorForExpected(t, expectedData) backupSel := backupSelectorForExpected(t, test.service, expectedDests)
t.Logf("Selective backup of %s\n", backupSel) t.Logf("Selective backup of %s\n", backupSel)
start = time.Now() start = time.Now()
@ -526,14 +538,20 @@ func (suite *GraphConnectorIntegrationSuite) TestMultiFolderBackupDifferentNames
defer flush() defer flush()
restoreSel := getSelectorWith(test.service) restoreSel := getSelectorWith(test.service)
dests := make([]control.RestoreDestination, 0, len(test.collections)) expectedDests := make([]destAndCats, 0, len(test.collections))
allItems := 0 allItems := 0
allExpectedData := map[string]map[string][]byte{} allExpectedData := map[string]map[string][]byte{}
for i, collection := range test.collections { for i, collection := range test.collections {
// Get a dest per collection so they're independent. // Get a dest per collection so they're independent.
dest := tester.DefaultTestRestoreDestination() dest := tester.DefaultTestRestoreDestination()
dests = append(dests, dest) expectedDests = append(expectedDests, destAndCats{
resourceOwner: suite.user,
dest: dest.ContainerName,
cats: map[path.CategoryType]struct{}{
collection.category: {},
},
})
totalItems, collections, expectedData := collectionsForInfo( totalItems, collections, expectedData := collectionsForInfo(
t, t,
@ -575,7 +593,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 := backupSelectorForExpected(t, allExpectedData) backupSel := backupSelectorForExpected(t, test.service, expectedDests)
t.Log("Selective backup of", backupSel) t.Log("Selective backup of", backupSel)
dcs, err := backupGC.DataCollections(ctx, backupSel) dcs, err := backupGC.DataCollections(ctx, backupSel)