corso/src/pkg/selectors/sharepoint_test.go
Hitesh Pattanayak dfb4a73f56
modifies list restore handler and handles lists advanced restore (#5024)
- includes GetList in list restore handler
- includes GetListsByCollisionKey in list restore handler
- updates mock list restore handler


#### Does this PR need a docs update or release note?
- [x]  No

#### Type of change

<!--- Please check the type of change your PR introduces: --->
- [x] 🌻 Feature

#### Issue(s)
#4754 

#### Test Plan

<!-- How will this be tested prior to merging.-->
- [x] 💪 Manual
- [x]  Unit test
- [x] 💚 E2E
2024-01-23 16:18:47 +05:30

606 lines
17 KiB
Go

package selectors
import (
"strings"
"testing"
"time"
"github.com/alcionai/clues"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"golang.org/x/exp/slices"
odConsts "github.com/alcionai/corso/src/internal/m365/service/onedrive/consts"
"github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/pkg/backup/details"
"github.com/alcionai/corso/src/pkg/dttm"
"github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/path"
)
type SharePointSelectorSuite struct {
tester.Suite
}
func TestSharePointSelectorSuite(t *testing.T) {
suite.Run(t, &SharePointSelectorSuite{Suite: tester.NewUnitSuite(t)})
}
func (suite *SharePointSelectorSuite) TestNewSharePointBackup() {
t := suite.T()
ob := NewSharePointBackup(nil)
assert.Equal(t, ob.Service, ServiceSharePoint)
assert.NotZero(t, ob.Scopes())
}
func (suite *SharePointSelectorSuite) TestToSharePointBackup() {
t := suite.T()
ob := NewSharePointBackup(nil)
s := ob.Selector
ob, err := s.ToSharePointBackup()
require.NoError(t, err, clues.ToCore(err))
assert.Equal(t, ob.Service, ServiceSharePoint)
assert.NotZero(t, ob.Scopes())
}
func (suite *SharePointSelectorSuite) TestSharePointSelector_Include_WebURLs() {
t := suite.T()
const (
s1 = "s1"
s2 = "s2"
)
s12 := []string{s1, s2}
sel := NewSharePointRestore(s12)
sel.Include(sel.WebURL(s12))
scopes := sel.Includes
require.Len(t, scopes, 3)
for _, sc := range scopes {
scopeMustHave(
t,
SharePointScope(sc),
map[categorizer][]string{SharePointWebURL: s12})
}
}
func (suite *SharePointSelectorSuite) TestSharePointSelector_Include_WebURLs_anyNone() {
table := []struct {
name string
in []string
expect []string
}{
{
name: "any",
in: []string{AnyTgt},
expect: Any(),
},
{
name: "none",
in: []string{NoneTgt},
expect: None(),
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
sel := NewSharePointRestore(Any())
sel.Include(sel.WebURL(test.in))
scopes := sel.Includes
require.Len(t, scopes, 3)
for _, sc := range scopes {
scopeMustHave(
t,
SharePointScope(sc),
map[categorizer][]string{SharePointWebURL: test.expect})
}
})
}
}
func (suite *SharePointSelectorSuite) TestSharePointSelector_Exclude_WebURLs() {
t := suite.T()
const (
s1 = "s1"
s2 = "s2"
)
s12 := []string{s1, s2}
sel := NewSharePointRestore(s12)
sel.Exclude(sel.WebURL(s12))
scopes := sel.Excludes
require.Len(t, scopes, 3)
for _, sc := range scopes {
scopeMustHave(
t,
SharePointScope(sc),
map[categorizer][]string{SharePointWebURL: s12})
}
}
func (suite *SharePointSelectorSuite) TestNewSharePointRestore() {
t := suite.T()
or := NewSharePointRestore(nil)
assert.Equal(t, or.Service, ServiceSharePoint)
assert.NotZero(t, or.Scopes())
}
func (suite *SharePointSelectorSuite) TestToSharePointRestore() {
t := suite.T()
eb := NewSharePointRestore(nil)
s := eb.Selector
or, err := s.ToSharePointRestore()
require.NoError(t, err, clues.ToCore(err))
assert.Equal(t, or.Service, ServiceSharePoint)
assert.NotZero(t, or.Scopes())
}
func (suite *SharePointSelectorSuite) TestSharePointRestore_Reduce() {
toRR := func(cat path.CategoryType, siteID string, folders []string, item string) string {
folderElems := make([]string, 0, len(folders))
for _, f := range folders {
folderElems = append(folderElems, f+".d")
}
return stubRepoRef(
path.SharePointService,
cat,
siteID,
strings.Join(folderElems, "/"),
item)
}
var (
prefixElems = []string{
odConsts.DrivesPathDir,
"drive!id",
odConsts.RootPathDir,
}
itemElems1 = []string{"folderA", "folderB"}
itemElems2 = []string{"folderA", "folderC"}
itemElems3 = []string{"folderD", "folderE"}
pairAC = "folderA/folderC"
pairGH = "folderG/folderH"
listID1 = "list1"
listID2 = "list2"
item = toRR(
path.LibrariesCategory,
"sid",
append(slices.Clone(prefixElems), itemElems1...),
"item")
item2 = toRR(
path.LibrariesCategory,
"sid",
append(slices.Clone(prefixElems), itemElems2...),
"item2")
item3 = toRR(
path.LibrariesCategory,
"sid",
append(slices.Clone(prefixElems), itemElems3...),
"item3")
item4 = stubRepoRef(path.SharePointService, path.PagesCategory, "sid", pairGH, "item4")
item5 = stubRepoRef(path.SharePointService, path.PagesCategory, "sid", pairGH, "item5")
list1 = stubRepoRef(path.SharePointService, path.ListsCategory, "sid", listID1, listID1)
list2 = stubRepoRef(path.SharePointService, path.ListsCategory, "sid", listID2, listID2)
)
deets := &details.Details{
DetailsModel: details.DetailsModel{
Entries: []details.Entry{
{
RepoRef: item,
ItemRef: "item",
LocationRef: strings.Join(append([]string{odConsts.RootPathDir}, itemElems1...), "/"),
ItemInfo: details.ItemInfo{
SharePoint: &details.SharePointInfo{
ItemType: details.SharePointLibrary,
ItemName: "itemName",
ParentPath: strings.Join(itemElems1, "/"),
},
},
},
{
RepoRef: item2,
LocationRef: strings.Join(append([]string{odConsts.RootPathDir}, itemElems2...), "/"),
// ItemRef intentionally blank to test fallback case
ItemInfo: details.ItemInfo{
SharePoint: &details.SharePointInfo{
ItemType: details.SharePointLibrary,
ItemName: "itemName2",
ParentPath: strings.Join(itemElems2, "/"),
},
},
},
{
RepoRef: item3,
ItemRef: "item3",
LocationRef: strings.Join(append([]string{odConsts.RootPathDir}, itemElems3...), "/"),
ItemInfo: details.ItemInfo{
SharePoint: &details.SharePointInfo{
ItemType: details.SharePointLibrary,
ItemName: "itemName3",
ParentPath: strings.Join(itemElems3, "/"),
},
},
},
{
RepoRef: item4,
LocationRef: pairGH,
ItemRef: "item4",
ItemInfo: details.ItemInfo{
SharePoint: &details.SharePointInfo{
ItemType: details.SharePointPage,
ItemName: "itemName4",
ParentPath: pairGH,
},
},
},
{
RepoRef: item5,
LocationRef: pairGH,
// ItemRef intentionally blank to test fallback case
ItemInfo: details.ItemInfo{
SharePoint: &details.SharePointInfo{
ItemType: details.SharePointPage,
ItemName: "itemName5",
ParentPath: pairGH,
},
},
},
{
RepoRef: list1,
LocationRef: listID1,
ItemRef: "list1",
ItemInfo: details.ItemInfo{
SharePoint: &details.SharePointInfo{
ItemType: details.SharePointList,
List: &details.ListInfo{
Name: "listName1",
},
ParentPath: strings.Join([]string{listID1}, "/"),
},
},
},
{
RepoRef: list2,
LocationRef: listID2,
ItemRef: "list2",
ItemInfo: details.ItemInfo{
SharePoint: &details.SharePointInfo{
ItemType: details.SharePointList,
List: &details.ListInfo{
Name: "listName2",
},
ParentPath: strings.Join([]string{listID2}, "/"),
},
},
},
},
},
}
arr := func(s ...string) []string {
return s
}
table := []struct {
name string
makeSelector func() *SharePointRestore
expect []string
cfg Config
}{
{
name: "all",
makeSelector: func() *SharePointRestore {
odr := NewSharePointRestore(Any())
odr.Include(odr.AllData())
return odr
},
expect: arr(item, item2, item3, item4, item5, list1, list2),
},
{
name: "only match item",
makeSelector: func() *SharePointRestore {
odr := NewSharePointRestore(Any())
odr.Include(odr.LibraryItems(Any(), []string{"item2"}))
return odr
},
expect: arr(item2),
},
{
name: "id doesn't match name",
makeSelector: func() *SharePointRestore {
odr := NewSharePointRestore(Any())
odr.Include(odr.LibraryItems(Any(), []string{"item2"}))
return odr
},
expect: []string{},
cfg: Config{OnlyMatchItemNames: true},
},
{
name: "only match item name",
makeSelector: func() *SharePointRestore {
odr := NewSharePointRestore(Any())
odr.Include(odr.LibraryItems(Any(), []string{"itemName2"}))
return odr
},
expect: arr(item2),
cfg: Config{OnlyMatchItemNames: true},
},
{
name: "name doesn't match",
makeSelector: func() *SharePointRestore {
odr := NewSharePointRestore(Any())
odr.Include(odr.LibraryItems(Any(), []string{"itemName2"}))
return odr
},
expect: []string{},
},
{
name: "only match folder",
makeSelector: func() *SharePointRestore {
odr := NewSharePointRestore([]string{"sid"})
odr.Include(odr.LibraryFolders([]string{"folderA/folderB", pairAC}))
return odr
},
expect: arr(item, item2),
},
{
name: "pages match folder",
makeSelector: func() *SharePointRestore {
odr := NewSharePointRestore([]string{"sid"})
odr.Include(odr.Pages([]string{pairGH, pairAC}))
return odr
},
expect: arr(item4, item5),
},
{
name: "lists match by id",
makeSelector: func() *SharePointRestore {
odr := NewSharePointRestore([]string{"sid"})
odr.Include(odr.Lists([]string{listID1, listID2}))
return odr
},
expect: arr(list1, list2),
},
{
name: "lists match by name",
makeSelector: func() *SharePointRestore {
odr := NewSharePointRestore([]string{"sid"})
odr.Include(odr.ListItems(Any(), []string{"listName2"}))
return odr
},
expect: arr(list2),
cfg: Config{OnlyMatchItemNames: true},
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
sel := test.makeSelector()
sel.Configure(test.cfg)
results := sel.Reduce(ctx, deets, fault.New(true))
paths := results.Paths()
assert.Equal(t, test.expect, paths)
})
}
}
func (suite *SharePointSelectorSuite) TestSharePointCategory_PathValues() {
var (
itemName = "item"
itemID = "item-id"
shortRef = "short"
driveElems = []string{
odConsts.DrivesPathDir,
"drive!id",
odConsts.RootPathDir + ".d",
"dir1.d",
"dir2.d",
itemID,
}
elems = []string{itemID, itemID}
)
table := []struct {
name string
sc sharePointCategory
pathElems []string
locRef string
parentPath string
expected map[categorizer][]string
cfg Config
}{
{
name: "SharePoint Libraries",
sc: SharePointLibraryItem,
pathElems: driveElems,
locRef: "root:/dir1/dir2",
parentPath: "dir1/dir2",
expected: map[categorizer][]string{
SharePointLibraryFolder: {"dir1/dir2"},
SharePointLibraryItem: {itemID, shortRef},
},
cfg: Config{},
},
{
name: "SharePoint Libraries w/ name",
sc: SharePointLibraryItem,
pathElems: driveElems,
locRef: "root:/dir1/dir2",
parentPath: "dir1/dir2",
expected: map[categorizer][]string{
SharePointLibraryFolder: {"dir1/dir2"},
SharePointLibraryItem: {itemName, shortRef},
},
cfg: Config{OnlyMatchItemNames: true},
},
{
name: "SharePoint Lists",
sc: SharePointListItem,
pathElems: elems,
locRef: itemID,
expected: map[categorizer][]string{
SharePointList: {itemID},
SharePointListItem: {itemID, shortRef},
},
cfg: Config{},
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
itemPath, err := path.Build(
"tenant",
"site",
path.SharePointService,
test.sc.PathType(),
true,
test.pathElems...)
require.NoError(t, err, clues.ToCore(err))
di := details.ItemInfo{}
if test.sc.PathType() == path.LibrariesCategory {
di.SharePoint = &details.SharePointInfo{
ItemName: itemName,
ParentPath: test.parentPath,
}
} else if test.sc.PathType() == path.ListsCategory {
di.SharePoint = &details.SharePointInfo{
List: &details.ListInfo{
Name: itemName,
},
ParentPath: test.parentPath,
}
}
ent := details.Entry{
RepoRef: itemPath.String(),
ShortRef: shortRef,
ItemRef: itemPath.Item(),
LocationRef: test.locRef,
ItemInfo: di,
}
pv, err := test.sc.pathValues(itemPath, ent, test.cfg)
require.NoError(t, err)
assert.Equal(t, test.expected, pv)
})
}
}
func (suite *SharePointSelectorSuite) TestSharePointScope_MatchesInfo() {
var (
sel = NewSharePointRestore(Any())
host = "www.website.com"
pth = "/foo"
url = host + pth
epoch = time.Time{}
now = time.Now()
modification = now.Add(15 * time.Minute)
future = now.Add(45 * time.Minute)
)
table := []struct {
name string
infoURL string
scope []SharePointScope
expect assert.BoolAssertionFunc
}{
{"host match", host, sel.WebURL([]string{host}), assert.True},
{"url match", url, sel.WebURL([]string{url}), assert.True},
{"host suffixes host", host, sel.WebURL([]string{host}, SuffixMatch()), assert.True},
{"url does not suffix host", url, sel.WebURL([]string{host}, SuffixMatch()), assert.False},
{"url has path suffix", url, sel.WebURL([]string{pth}, SuffixMatch()), assert.True},
{"host does not contain substring", host, sel.WebURL([]string{"website"}), assert.False},
{"url does not suffix substring", url, sel.WebURL([]string{"oo"}, SuffixMatch()), assert.False},
{"host mismatch", host, sel.WebURL([]string{"www.google.com"}), assert.False},
{"file create after the epoch", host, sel.CreatedAfter(dttm.Format(epoch)), assert.True},
{"file create after now", host, sel.CreatedAfter(dttm.Format(now)), assert.False},
{"file create after later", url, sel.CreatedAfter(dttm.Format(future)), assert.False},
{"file create before future", host, sel.CreatedBefore(dttm.Format(future)), assert.True},
{"file create before now", host, sel.CreatedBefore(dttm.Format(now)), assert.False},
{"file create before modification", host, sel.CreatedBefore(dttm.Format(modification)), assert.True},
{"file create before epoch", host, sel.CreatedBefore(dttm.Format(now)), assert.False},
{"file modified after the epoch", host, sel.ModifiedAfter(dttm.Format(epoch)), assert.True},
{"file modified after now", host, sel.ModifiedAfter(dttm.Format(now)), assert.True},
{"file modified after later", host, sel.ModifiedAfter(dttm.Format(future)), assert.False},
{"file modified before future", host, sel.ModifiedBefore(dttm.Format(future)), assert.True},
{"file modified before now", host, sel.ModifiedBefore(dttm.Format(now)), assert.False},
{"file modified before epoch", host, sel.ModifiedBefore(dttm.Format(now)), assert.False},
{"list create after the epoch", host, sel.ListCreatedAfter(dttm.Format(epoch)), assert.True},
{"list create after now", host, sel.ListCreatedAfter(dttm.Format(now)), assert.False},
{"list create after later", url, sel.ListCreatedAfter(dttm.Format(future)), assert.False},
{"list create before future", host, sel.ListCreatedBefore(dttm.Format(future)), assert.True},
{"list create before now", host, sel.ListCreatedBefore(dttm.Format(now)), assert.False},
{"list create before modification", host, sel.ListCreatedBefore(dttm.Format(modification)), assert.True},
{"list create before epoch", host, sel.ListCreatedBefore(dttm.Format(now)), assert.False},
{"list modified after the epoch", host, sel.ListModifiedAfter(dttm.Format(epoch)), assert.True},
{"list modified after now", host, sel.ListModifiedAfter(dttm.Format(now)), assert.True},
{"list modified after later", host, sel.ListModifiedAfter(dttm.Format(future)), assert.False},
{"list modified before future", host, sel.ListModifiedBefore(dttm.Format(future)), assert.True},
{"list modified before now", host, sel.ListModifiedBefore(dttm.Format(now)), assert.False},
{"list modified before epoch", host, sel.ListModifiedBefore(dttm.Format(now)), assert.False},
{"in library", host, sel.Library("included-library"), assert.True},
{"not in library", host, sel.Library("not-included-library"), assert.False},
{"library id", host, sel.Library("1234"), assert.True},
{"not library id", host, sel.Library("abcd"), assert.False},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
itemInfo := details.ItemInfo{
SharePoint: &details.SharePointInfo{
ItemType: details.SharePointPage,
WebURL: test.infoURL,
Created: now,
Modified: modification,
DriveName: "included-library",
DriveID: "1234",
},
}
scopes := setScopesToDefault(test.scope)
for _, scope := range scopes {
test.expect(t, scope.matchesInfo(itemInfo))
}
})
}
}
func (suite *SharePointSelectorSuite) TestCategory_PathType() {
table := []struct {
cat sharePointCategory
pathType path.CategoryType
}{
{SharePointCategoryUnknown, path.UnknownCategory},
{SharePointWebURL, path.UnknownCategory},
{SharePointSite, path.UnknownCategory},
{SharePointLibraryFolder, path.LibrariesCategory},
{SharePointLibraryItem, path.LibrariesCategory},
{SharePointList, path.ListsCategory},
}
for _, test := range table {
suite.Run(test.cat.String(), func() {
assert.Equal(suite.T(), test.pathType, test.cat.PathType())
})
}
}