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:
parent
93ad16dc30
commit
9fe9cd1ce6
@ -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
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user