GC: e2e: SharePoint.Libraries (#2666)
<!-- Insert PR description--> E2E Testing for GC: `SharePoint.Libraries`. Covers Restore and Backup tests within the operations package --- #### Does this PR need a docs update or release note? - [x] ⛔ No #### Type of change - [x] 🤖 Test - [x] 💻 CI/Deployment #### Issue(s) <!-- Can reference multiple issues. Use one of the following "magic words" - "closes, fixes" to auto-close the Github issue. --> * closes #2525<issue> #### Test Plan - [x] ⚡ Unit test - [x] 💚 E2E
This commit is contained in:
parent
aef90f8b86
commit
5c65638721
@ -10,6 +10,7 @@ import (
|
||||
mssites "github.com/microsoftgraph/msgraph-sdk-go/sites"
|
||||
msusers "github.com/microsoftgraph/msgraph-sdk-go/users"
|
||||
|
||||
"github.com/alcionai/corso/src/internal/common/ptr"
|
||||
"github.com/alcionai/corso/src/internal/connector/graph"
|
||||
"github.com/alcionai/corso/src/internal/connector/graph/api"
|
||||
)
|
||||
@ -137,6 +138,11 @@ type siteDrivePager struct {
|
||||
options *mssites.ItemDrivesRequestBuilderGetRequestConfiguration
|
||||
}
|
||||
|
||||
// NewSiteDrivePager is a constructor for creating a siteDrivePager
|
||||
// fields are the associated site drive fields that are desired to be returned
|
||||
// in a query. NOTE: Fields are case-sensitive. Incorrect field settings will
|
||||
// cause errors during later paging.
|
||||
// Available fields: https://learn.microsoft.com/en-us/graph/api/resources/drive?view=graph-rest-1.0
|
||||
func NewSiteDrivePager(
|
||||
gs graph.Servicer,
|
||||
siteID string,
|
||||
@ -178,3 +184,72 @@ func (p *siteDrivePager) SetNext(link string) {
|
||||
func (p *siteDrivePager) ValuesIn(l api.PageLinker) ([]models.Driveable, error) {
|
||||
return getValues[models.Driveable](l)
|
||||
}
|
||||
|
||||
// GetDriveIDByName is a helper function to retrieve the M365ID of a site drive.
|
||||
// Returns "" if the folder is not within the drive.
|
||||
// Dependency: Requires "name" and "id" to be part of the given options
|
||||
func (p *siteDrivePager) GetDriveIDByName(ctx context.Context, driveName string) (string, error) {
|
||||
var empty string
|
||||
|
||||
for {
|
||||
resp, err := p.builder.Get(ctx, p.options)
|
||||
if err != nil {
|
||||
return empty, clues.Stack(err).WithClues(ctx).With(graph.ErrData(err)...)
|
||||
}
|
||||
|
||||
for _, entry := range resp.GetValue() {
|
||||
if ptr.Val(entry.GetName()) == driveName {
|
||||
return ptr.Val(entry.GetId()), nil
|
||||
}
|
||||
}
|
||||
|
||||
link, ok := ptr.ValOK(resp.GetOdataNextLink())
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
p.builder = mssites.NewItemDrivesRequestBuilder(link, p.gs.Adapter())
|
||||
}
|
||||
|
||||
return empty, nil
|
||||
}
|
||||
|
||||
// GetFolderIDByName is a helper function to retrieve the M365ID of a folder within a site document library.
|
||||
// Returns "" if the folder is not within the drive
|
||||
func (p *siteDrivePager) GetFolderIDByName(ctx context.Context, driveID, folderName string) (string, error) {
|
||||
var empty string
|
||||
|
||||
// *msdrives.ItemRootChildrenRequestBuilder
|
||||
builder := p.gs.Client().DrivesById(driveID).Root().Children()
|
||||
option := &msdrives.ItemRootChildrenRequestBuilderGetRequestConfiguration{
|
||||
QueryParameters: &msdrives.ItemRootChildrenRequestBuilderGetQueryParameters{
|
||||
Select: []string{"id", "name", "folder"},
|
||||
},
|
||||
}
|
||||
|
||||
for {
|
||||
resp, err := builder.Get(ctx, option)
|
||||
if err != nil {
|
||||
return empty, clues.Stack(err).WithClues(ctx).With(graph.ErrData(err)...)
|
||||
}
|
||||
|
||||
for _, entry := range resp.GetValue() {
|
||||
if entry.GetFolder() == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if ptr.Val(entry.GetName()) == folderName {
|
||||
return ptr.Val(entry.GetId()), nil
|
||||
}
|
||||
}
|
||||
|
||||
link, ok := ptr.ValOK(resp.GetOdataNextLink())
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
builder = msdrives.NewItemRootChildrenRequestBuilder(link, p.gs.Adapter())
|
||||
}
|
||||
|
||||
return empty, nil
|
||||
}
|
||||
|
||||
81
src/internal/connector/onedrive/api/drive_test.go
Normal file
81
src/internal/connector/onedrive/api/drive_test.go
Normal file
@ -0,0 +1,81 @@
|
||||
package api_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/alcionai/corso/src/internal/connector/graph"
|
||||
"github.com/alcionai/corso/src/internal/connector/onedrive/api"
|
||||
"github.com/alcionai/corso/src/internal/tester"
|
||||
"github.com/alcionai/corso/src/pkg/account"
|
||||
)
|
||||
|
||||
type OneDriveAPISuite struct {
|
||||
tester.Suite
|
||||
creds account.M365Config
|
||||
service graph.Servicer
|
||||
}
|
||||
|
||||
func (suite *OneDriveAPISuite) SetupSuite() {
|
||||
t := suite.T()
|
||||
a := tester.NewM365Account(t)
|
||||
m365, err := a.M365Config()
|
||||
require.NoError(t, err)
|
||||
|
||||
suite.creds = m365
|
||||
adpt, err := graph.CreateAdapter(m365.AzureTenantID, m365.AzureClientID, m365.AzureClientSecret)
|
||||
require.NoError(t, err)
|
||||
|
||||
suite.service = graph.NewService(adpt)
|
||||
}
|
||||
|
||||
func TestOneDriveAPIs(t *testing.T) {
|
||||
suite.Run(t, &OneDriveAPISuite{
|
||||
Suite: tester.NewIntegrationSuite(
|
||||
t,
|
||||
[][]string{tester.M365AcctCredEnvs},
|
||||
tester.CorsoGraphConnectorOneDriveTests),
|
||||
})
|
||||
}
|
||||
|
||||
func (suite *OneDriveAPISuite) TestCreatePagerAndGetPage() {
|
||||
ctx, flush := tester.NewContext()
|
||||
defer flush()
|
||||
|
||||
t := suite.T()
|
||||
siteID := tester.M365SiteID(t)
|
||||
pager := api.NewSiteDrivePager(suite.service, siteID, []string{"name"})
|
||||
a, err := pager.GetPage(ctx)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, a)
|
||||
}
|
||||
|
||||
func (suite *OneDriveAPISuite) TestGetDriveIDByName() {
|
||||
ctx, flush := tester.NewContext()
|
||||
defer flush()
|
||||
|
||||
t := suite.T()
|
||||
siteID := tester.M365SiteID(t)
|
||||
pager := api.NewSiteDrivePager(suite.service, siteID, []string{"id", "name"})
|
||||
id, err := pager.GetDriveIDByName(ctx, "Documents")
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, id)
|
||||
}
|
||||
|
||||
func (suite *OneDriveAPISuite) TestGetDriveFolderByName() {
|
||||
ctx, flush := tester.NewContext()
|
||||
defer flush()
|
||||
|
||||
t := suite.T()
|
||||
siteID := tester.M365SiteID(t)
|
||||
pager := api.NewSiteDrivePager(suite.service, siteID, []string{"id", "name"})
|
||||
id, err := pager.GetDriveIDByName(ctx, "Documents")
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, id)
|
||||
|
||||
_, err = pager.GetFolderIDByName(ctx, id, "folder")
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@ -1107,10 +1107,11 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_sharePoint() {
|
||||
sel = selectors.NewSharePointBackup([]string{suite.site})
|
||||
)
|
||||
|
||||
sel.Include(sel.AllData())
|
||||
sel.Include(sel.Libraries(selectors.Any()))
|
||||
|
||||
bo, _, _, _, closer := prepNewTestBackupOp(t, ctx, mb, sel.Selector, control.Toggles{})
|
||||
bo, _, kw, _, closer := prepNewTestBackupOp(t, ctx, mb, sel.Selector, control.Toggles{})
|
||||
defer closer()
|
||||
|
||||
runAndCheckBackup(t, ctx, &bo, mb)
|
||||
checkBackupIsInManifests(t, ctx, kw, &bo, sel.Selector, suite.site, path.LibrariesCategory)
|
||||
}
|
||||
|
||||
@ -9,8 +9,12 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/alcionai/corso/src/internal/common"
|
||||
"github.com/alcionai/corso/src/internal/connector/exchange"
|
||||
"github.com/alcionai/corso/src/internal/connector/graph"
|
||||
"github.com/alcionai/corso/src/internal/connector/mockconnector"
|
||||
"github.com/alcionai/corso/src/internal/connector/onedrive"
|
||||
"github.com/alcionai/corso/src/internal/connector/onedrive/api"
|
||||
"github.com/alcionai/corso/src/internal/connector/support"
|
||||
"github.com/alcionai/corso/src/internal/data"
|
||||
"github.com/alcionai/corso/src/internal/events"
|
||||
@ -133,12 +137,14 @@ func (suite *RestoreOpSuite) TestRestoreOperation_PersistResults() {
|
||||
type RestoreOpIntegrationSuite struct {
|
||||
tester.Suite
|
||||
|
||||
backupID model.StableID
|
||||
numItems int
|
||||
kopiaCloser func(ctx context.Context)
|
||||
kw *kopia.Wrapper
|
||||
sw *store.Wrapper
|
||||
ms *kopia.ModelStore
|
||||
backupID model.StableID
|
||||
sharepointID model.StableID
|
||||
shareItems int
|
||||
numItems int
|
||||
kopiaCloser func(ctx context.Context)
|
||||
kw *kopia.Wrapper
|
||||
sw *store.Wrapper
|
||||
ms *kopia.ModelStore
|
||||
}
|
||||
|
||||
func TestRestoreOpIntegrationSuite(t *testing.T) {
|
||||
@ -208,6 +214,30 @@ func (suite *RestoreOpIntegrationSuite) SetupSuite() {
|
||||
// Discount metadata files (3 paths, 3 deltas) as
|
||||
// they are not part of the data restored.
|
||||
suite.numItems = bo.Results.ItemsWritten - 6
|
||||
|
||||
siteID := tester.M365SiteID(t)
|
||||
sites := []string{siteID}
|
||||
csel := selectors.NewSharePointBackup(sites)
|
||||
csel.DiscreteOwner = siteID
|
||||
csel.Include(
|
||||
csel.Libraries(selectors.Any()),
|
||||
)
|
||||
|
||||
bo, err = NewBackupOperation(
|
||||
ctx,
|
||||
control.Options{},
|
||||
kw,
|
||||
sw,
|
||||
acct,
|
||||
csel.Selector,
|
||||
evmock.NewBus(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, bo.Run(ctx))
|
||||
require.NotEmpty(t, bo.Results.BackupID)
|
||||
suite.sharepointID = bo.Results.BackupID
|
||||
// Discount MetaData files (1 path, 1 delta)
|
||||
suite.shareItems = bo.Results.ItemsWritten - 2
|
||||
}
|
||||
|
||||
func (suite *RestoreOpIntegrationSuite) TearDownSuite() {
|
||||
@ -266,49 +296,106 @@ func (suite *RestoreOpIntegrationSuite) TestNewRestoreOperation() {
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:lll
|
||||
func (suite *RestoreOpIntegrationSuite) TestRestore_Run() {
|
||||
ctx, flush := tester.NewContext()
|
||||
defer flush()
|
||||
|
||||
t := suite.T()
|
||||
users := []string{tester.M365UserID(t)}
|
||||
tables := []struct {
|
||||
name string
|
||||
bID model.StableID
|
||||
expectedItems int
|
||||
dest control.RestoreDestination
|
||||
getSelector func(t *testing.T) selectors.Selector
|
||||
cleanup func(t *testing.T, dest string)
|
||||
}{
|
||||
{
|
||||
name: "Exchange_Restore",
|
||||
bID: suite.backupID,
|
||||
expectedItems: suite.numItems,
|
||||
dest: tester.DefaultTestRestoreDestination(),
|
||||
getSelector: func(t *testing.T) selectors.Selector {
|
||||
users := []string{tester.M365UserID(t)}
|
||||
rsel := selectors.NewExchangeRestore(users)
|
||||
rsel.Include(rsel.AllData())
|
||||
|
||||
rsel := selectors.NewExchangeRestore(users)
|
||||
rsel.Include(rsel.AllData())
|
||||
return rsel.Selector
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "SharePoint_Restore",
|
||||
bID: suite.sharepointID,
|
||||
expectedItems: suite.shareItems,
|
||||
dest: control.DefaultRestoreDestination(common.SimpleDateTimeOneDrive),
|
||||
getSelector: func(t *testing.T) selectors.Selector {
|
||||
bsel := selectors.NewSharePointRestore([]string{tester.M365SiteID(t)})
|
||||
bsel.Include(bsel.AllData())
|
||||
|
||||
dest := tester.DefaultTestRestoreDestination()
|
||||
mb := evmock.NewBus()
|
||||
return bsel.Selector
|
||||
},
|
||||
cleanup: func(t *testing.T, dest string) {
|
||||
act := tester.NewM365Account(t)
|
||||
m365, err := act.M365Config()
|
||||
require.NoError(t, err)
|
||||
|
||||
ro, err := NewRestoreOperation(
|
||||
ctx,
|
||||
control.Options{},
|
||||
suite.kw,
|
||||
suite.sw,
|
||||
tester.NewM365Account(t),
|
||||
suite.backupID,
|
||||
rsel.Selector,
|
||||
dest,
|
||||
mb)
|
||||
require.NoError(t, err)
|
||||
adpt, err := graph.CreateAdapter(m365.AzureTenantID, m365.AzureClientID, m365.AzureClientSecret)
|
||||
require.NoError(t, err)
|
||||
service := graph.NewService(adpt)
|
||||
pager := api.NewSiteDrivePager(service, tester.M365SiteID(t), []string{"id", "name"})
|
||||
driveID, err := pager.GetDriveIDByName(ctx, "Documents")
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, driveID)
|
||||
|
||||
ds, err := ro.Run(ctx)
|
||||
folderID, err := pager.GetFolderIDByName(ctx, driveID, dest)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, folderID)
|
||||
|
||||
require.NoError(t, err, "restoreOp.Run()")
|
||||
require.NotEmpty(t, ro.Results, "restoreOp results")
|
||||
require.NotNil(t, ds, "restored details")
|
||||
assert.Equal(t, ro.Status, Completed, "restoreOp status")
|
||||
assert.Equal(t, ro.Results.ItemsWritten, len(ds.Entries), "count of items written matches restored entries in details")
|
||||
assert.Less(t, 0, ro.Results.ItemsRead, "restore items read")
|
||||
assert.Less(t, 0, ro.Results.ItemsWritten, "restored items written")
|
||||
assert.Less(t, int64(0), ro.Results.BytesRead, "bytes read")
|
||||
assert.Equal(t, 1, ro.Results.ResourceOwners, "resource Owners")
|
||||
assert.NoError(t, ro.Errors.Failure(), "non-recoverable error")
|
||||
assert.Empty(t, ro.Errors.Recovered(), "recoverable errors")
|
||||
assert.NoError(t, ro.Results.ReadErrors, "errors while reading restore data")
|
||||
assert.NoError(t, ro.Results.WriteErrors, "errors while writing restore data")
|
||||
assert.Equal(t, suite.numItems, ro.Results.ItemsWritten, "backup and restore wrote the same num of items")
|
||||
assert.Equal(t, 1, mb.TimesCalled[events.RestoreStart], "restore-start events")
|
||||
assert.Equal(t, 1, mb.TimesCalled[events.RestoreEnd], "restore-end events")
|
||||
err = onedrive.DeleteItem(ctx, service, driveID, folderID)
|
||||
assert.NoError(t, err, "failed to delete restore folder: operations_SharePoint_Restore")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tables {
|
||||
suite.T().Run(test.name, func(t *testing.T) {
|
||||
mb := evmock.NewBus()
|
||||
ro, err := NewRestoreOperation(
|
||||
ctx,
|
||||
control.Options{FailFast: true},
|
||||
suite.kw,
|
||||
suite.sw,
|
||||
tester.NewM365Account(t),
|
||||
test.bID,
|
||||
test.getSelector(t),
|
||||
test.dest,
|
||||
mb)
|
||||
require.NoError(t, err)
|
||||
|
||||
ds, err := ro.Run(ctx)
|
||||
|
||||
require.NoError(t, err, "restoreOp.Run()")
|
||||
require.NotEmpty(t, ro.Results, "restoreOp results")
|
||||
require.NotNil(t, ds, "restored details")
|
||||
assert.Equal(t, ro.Status, Completed, "restoreOp status")
|
||||
assert.Equal(t, ro.Results.ItemsWritten, len(ds.Entries), "count of items written matches restored entries in details")
|
||||
assert.Less(t, 0, ro.Results.ItemsRead, "restore items read")
|
||||
assert.Less(t, 0, ro.Results.ItemsWritten, "restored items written")
|
||||
assert.Less(t, int64(0), ro.Results.BytesRead, "bytes read")
|
||||
assert.Equal(t, 1, ro.Results.ResourceOwners, "resource Owners")
|
||||
assert.NoError(t, ro.Errors.Failure(), "non-recoverable error")
|
||||
assert.Empty(t, ro.Errors.Recovered(), "recoverable errors")
|
||||
assert.NoError(t, ro.Results.ReadErrors, "errors while reading restore data")
|
||||
assert.NoError(t, ro.Results.WriteErrors, "errors while writing restore data")
|
||||
assert.Equal(t, test.expectedItems, ro.Results.ItemsWritten, "backup and restore wrote the same num of items")
|
||||
assert.Equal(t, 1, mb.TimesCalled[events.RestoreStart], "restore-start events")
|
||||
assert.Equal(t, 1, mb.TimesCalled[events.RestoreEnd], "restore-end events")
|
||||
|
||||
// clean up
|
||||
if test.cleanup != nil {
|
||||
test.cleanup(t, test.dest.ContainerName)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *RestoreOpIntegrationSuite) TestRestore_Run_ErrorNoResults() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user