reverses the drive backup process toggle by making the tree-based backup standard, and the old, linear enumeration the toggleable fallback. --- #### Does this PR need a docs update or release note? - [x] ⛔ No #### Type of change - [x] 🌻 Feature #### Issue(s) * #4690 #### Test Plan - [x] ⚡ Unit test - [x] 💚 E2E
731 lines
20 KiB
Go
731 lines
20 KiB
Go
package m365
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"testing"
|
|
|
|
"github.com/alcionai/clues"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/stretchr/testify/suite"
|
|
|
|
inMock "github.com/alcionai/corso/src/internal/common/idname/mock"
|
|
"github.com/alcionai/corso/src/internal/common/ptr"
|
|
"github.com/alcionai/corso/src/internal/data"
|
|
"github.com/alcionai/corso/src/internal/data/mock"
|
|
"github.com/alcionai/corso/src/internal/m365/service/exchange"
|
|
odConsts "github.com/alcionai/corso/src/internal/m365/service/onedrive/consts"
|
|
"github.com/alcionai/corso/src/internal/m365/service/sharepoint"
|
|
"github.com/alcionai/corso/src/internal/operations/inject"
|
|
"github.com/alcionai/corso/src/internal/tester"
|
|
"github.com/alcionai/corso/src/internal/tester/tconfig"
|
|
"github.com/alcionai/corso/src/internal/version"
|
|
"github.com/alcionai/corso/src/pkg/control"
|
|
"github.com/alcionai/corso/src/pkg/count"
|
|
"github.com/alcionai/corso/src/pkg/fault"
|
|
"github.com/alcionai/corso/src/pkg/path"
|
|
"github.com/alcionai/corso/src/pkg/selectors"
|
|
selTD "github.com/alcionai/corso/src/pkg/selectors/testdata"
|
|
"github.com/alcionai/corso/src/pkg/services/m365/api"
|
|
)
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// DataCollection tests
|
|
// ---------------------------------------------------------------------------
|
|
|
|
type DataCollectionIntgSuite struct {
|
|
tester.Suite
|
|
user string
|
|
site string
|
|
tenantID string
|
|
ac api.Client
|
|
}
|
|
|
|
func TestDataCollectionIntgSuite(t *testing.T) {
|
|
suite.Run(t, &DataCollectionIntgSuite{
|
|
Suite: tester.NewIntegrationSuite(
|
|
t,
|
|
[][]string{tconfig.M365AcctCredEnvs}),
|
|
})
|
|
}
|
|
|
|
func (suite *DataCollectionIntgSuite) SetupSuite() {
|
|
t := suite.T()
|
|
|
|
suite.user = tconfig.M365UserID(t)
|
|
suite.site = tconfig.M365SiteID(t)
|
|
|
|
acct := tconfig.NewM365Account(t)
|
|
creds, err := acct.M365Config()
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
suite.tenantID = creds.AzureTenantID
|
|
|
|
suite.ac, err = api.NewClient(
|
|
creds,
|
|
control.DefaultOptions(),
|
|
count.New())
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
}
|
|
|
|
func (suite *DataCollectionIntgSuite) TestExchangeDataCollection() {
|
|
ctx, flush := tester.NewContext(suite.T())
|
|
defer flush()
|
|
|
|
selUsers := []string{suite.user}
|
|
|
|
ctrl := newController(ctx, suite.T(), path.ExchangeService)
|
|
tests := []struct {
|
|
name string
|
|
getSelector func(t *testing.T) selectors.Selector
|
|
}{
|
|
{
|
|
name: "Email",
|
|
getSelector: func(t *testing.T) selectors.Selector {
|
|
sel := selectors.NewExchangeBackup(selUsers)
|
|
sel.Include(sel.MailFolders([]string{api.MailInbox}, selectors.PrefixMatch()))
|
|
sel.DiscreteOwner = suite.user
|
|
return sel.Selector
|
|
},
|
|
},
|
|
{
|
|
name: "Contacts",
|
|
getSelector: func(t *testing.T) selectors.Selector {
|
|
sel := selectors.NewExchangeBackup(selUsers)
|
|
sel.Include(sel.ContactFolders([]string{api.DefaultContacts}, selectors.PrefixMatch()))
|
|
sel.DiscreteOwner = suite.user
|
|
return sel.Selector
|
|
},
|
|
},
|
|
// {
|
|
// name: "Events",
|
|
// getSelector: func(t *testing.T) selectors.Selector {
|
|
// sel := selectors.NewExchangeBackup(selUsers)
|
|
// sel.Include(sel.EventCalendars([]string{exchange.DefaultCalendar}, selectors.PrefixMatch()))
|
|
// sel.DiscreteOwner = suite.user
|
|
// return sel.Selector
|
|
// },
|
|
// },
|
|
}
|
|
|
|
for _, test := range tests {
|
|
for _, canMakeDeltaQueries := range []bool{true, false} {
|
|
name := test.name
|
|
|
|
if canMakeDeltaQueries {
|
|
name += "-delta"
|
|
} else {
|
|
name += "-non-delta"
|
|
}
|
|
|
|
suite.Run(name, func() {
|
|
t := suite.T()
|
|
|
|
ctx, flush := tester.NewContext(t)
|
|
defer flush()
|
|
|
|
sel := test.getSelector(t)
|
|
uidn := inMock.NewProvider(sel.ID(), sel.Name())
|
|
|
|
ctrlOpts := control.DefaultOptions()
|
|
ctrlOpts.ToggleFeatures.DisableDelta = !canMakeDeltaQueries
|
|
|
|
bpc := inject.BackupProducerConfig{
|
|
// exchange doesn't have any changes based on backup version yet.
|
|
LastBackupVersion: version.NoBackup,
|
|
Options: ctrlOpts,
|
|
ProtectedResource: uidn,
|
|
Selector: sel,
|
|
}
|
|
|
|
collections, excludes, canUsePreviousBackup, err := exchange.ProduceBackupCollections(
|
|
ctx,
|
|
bpc,
|
|
suite.ac,
|
|
suite.ac.Credentials,
|
|
ctrl.UpdateStatus,
|
|
count.New(),
|
|
fault.New(true))
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
assert.True(t, canUsePreviousBackup, "can use previous backup")
|
|
assert.True(t, excludes.Empty())
|
|
|
|
for range collections {
|
|
ctrl.incrementAwaitingMessages()
|
|
}
|
|
|
|
// Categories with delta endpoints will produce a collection for metadata
|
|
// as well as the actual data pulled, and the "temp" root collection.
|
|
assert.LessOrEqual(t, 1, len(collections), "expected 1 <= num collections <= 3")
|
|
assert.GreaterOrEqual(t, 3, len(collections), "expected 1 <= num collections <= 3")
|
|
|
|
for _, col := range collections {
|
|
for object := range col.Items(ctx, fault.New(true)) {
|
|
buf := &bytes.Buffer{}
|
|
_, err := buf.ReadFrom(object.ToReader())
|
|
assert.NoError(t, err, "received a buf.Read error", clues.ToCore(err))
|
|
}
|
|
}
|
|
|
|
status := ctrl.Wait()
|
|
assert.NotZero(t, status.Successes)
|
|
t.Log(status.String())
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestInvalidUserForDataCollections ensures verification process for users
|
|
func (suite *DataCollectionIntgSuite) TestDataCollections_invalidResourceOwner() {
|
|
ctx, flush := tester.NewContext(suite.T())
|
|
defer flush()
|
|
|
|
owners := []string{"snuffleupagus"}
|
|
ctrl := newController(ctx, suite.T(), path.ExchangeService)
|
|
tests := []struct {
|
|
name string
|
|
getSelector func(t *testing.T) selectors.Selector
|
|
}{
|
|
{
|
|
name: "invalid exchange backup user",
|
|
getSelector: func(t *testing.T) selectors.Selector {
|
|
sel := selectors.NewExchangeBackup(owners)
|
|
sel.Include(sel.MailFolders(selectors.Any()))
|
|
return sel.Selector
|
|
},
|
|
},
|
|
{
|
|
name: "Invalid onedrive backup user",
|
|
getSelector: func(t *testing.T) selectors.Selector {
|
|
sel := selectors.NewOneDriveBackup(owners)
|
|
sel.Include(selTD.OneDriveBackupFolderScope(sel))
|
|
return sel.Selector
|
|
},
|
|
},
|
|
{
|
|
name: "Invalid sharepoint backup site",
|
|
getSelector: func(t *testing.T) selectors.Selector {
|
|
sel := selectors.NewSharePointBackup(owners)
|
|
sel.Include(selTD.SharePointBackupFolderScope(sel))
|
|
return sel.Selector
|
|
},
|
|
},
|
|
{
|
|
name: "missing exchange backup user",
|
|
getSelector: func(t *testing.T) selectors.Selector {
|
|
sel := selectors.NewExchangeBackup(owners)
|
|
sel.Include(sel.MailFolders(selectors.Any()))
|
|
sel.DiscreteOwner = ""
|
|
return sel.Selector
|
|
},
|
|
},
|
|
{
|
|
name: "missing onedrive backup user",
|
|
getSelector: func(t *testing.T) selectors.Selector {
|
|
sel := selectors.NewOneDriveBackup(owners)
|
|
sel.Include(selTD.OneDriveBackupFolderScope(sel))
|
|
sel.DiscreteOwner = ""
|
|
return sel.Selector
|
|
},
|
|
},
|
|
{
|
|
name: "missing sharepoint backup site",
|
|
getSelector: func(t *testing.T) selectors.Selector {
|
|
sel := selectors.NewSharePointBackup(owners)
|
|
sel.Include(selTD.SharePointBackupFolderScope(sel))
|
|
sel.DiscreteOwner = ""
|
|
return sel.Selector
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
suite.Run(test.name, func() {
|
|
t := suite.T()
|
|
|
|
ctx, flush := tester.NewContext(t)
|
|
defer flush()
|
|
|
|
bpc := inject.BackupProducerConfig{
|
|
LastBackupVersion: version.NoBackup,
|
|
Options: control.DefaultOptions(),
|
|
ProtectedResource: test.getSelector(t),
|
|
}
|
|
|
|
collections, excludes, canUsePreviousBackup, err := ctrl.ProduceBackupCollections(
|
|
ctx,
|
|
bpc,
|
|
count.New(),
|
|
fault.New(true))
|
|
assert.Error(t, err, clues.ToCore(err))
|
|
assert.False(t, canUsePreviousBackup, "can use previous backup")
|
|
assert.Empty(t, collections)
|
|
assert.Nil(t, excludes)
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *DataCollectionIntgSuite) TestSharePointDataCollection() {
|
|
ctx, flush := tester.NewContext(suite.T())
|
|
defer flush()
|
|
|
|
selSites := []string{suite.site}
|
|
ctrl := newController(ctx, suite.T(), path.SharePointService)
|
|
tests := []struct {
|
|
name string
|
|
getSelector func() selectors.Selector
|
|
}{
|
|
{
|
|
name: "Libraries",
|
|
getSelector: func() selectors.Selector {
|
|
sel := selectors.NewSharePointBackup(selSites)
|
|
sel.Include(selTD.SharePointBackupFolderScope(sel))
|
|
return sel.Selector
|
|
},
|
|
},
|
|
{
|
|
name: "Lists",
|
|
getSelector: func() selectors.Selector {
|
|
sel := selectors.NewSharePointBackup(selSites)
|
|
sel.Include(sel.Lists(selectors.Any()))
|
|
return sel.Selector
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
suite.Run(test.name, func() {
|
|
t := suite.T()
|
|
|
|
ctx, flush := tester.NewContext(t)
|
|
defer flush()
|
|
|
|
sel := test.getSelector()
|
|
|
|
bpc := inject.BackupProducerConfig{
|
|
Options: control.DefaultOptions(),
|
|
ProtectedResource: sel,
|
|
Selector: sel,
|
|
}
|
|
|
|
collections, excludes, canUsePreviousBackup, err := sharepoint.ProduceBackupCollections(
|
|
ctx,
|
|
bpc,
|
|
suite.ac,
|
|
ctrl.credentials,
|
|
ctrl.UpdateStatus,
|
|
count.New(),
|
|
fault.New(true))
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
assert.True(t, canUsePreviousBackup, "can use previous backup")
|
|
// Not expecting excludes as this isn't an incremental backup.
|
|
assert.True(t, excludes.Empty())
|
|
|
|
for range collections {
|
|
ctrl.incrementAwaitingMessages()
|
|
}
|
|
|
|
// we don't know an exact count of drives this will produce,
|
|
// but it should be more than zero.
|
|
assert.NotEmpty(t, collections)
|
|
|
|
for _, coll := range collections {
|
|
for object := range coll.Items(ctx, fault.New(true)) {
|
|
buf := &bytes.Buffer{}
|
|
_, err := buf.ReadFrom(object.ToReader())
|
|
assert.NoError(t, err, "reading item", clues.ToCore(err))
|
|
}
|
|
}
|
|
|
|
status := ctrl.Wait()
|
|
assert.NotZero(t, status.Successes)
|
|
t.Log(status.String())
|
|
})
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// CreateSharePointCollection tests
|
|
// ---------------------------------------------------------------------------
|
|
|
|
type SPCollectionIntgSuite struct {
|
|
tester.Suite
|
|
connector *Controller
|
|
user string
|
|
}
|
|
|
|
func TestSPCollectionIntgSuite(t *testing.T) {
|
|
suite.Run(t, &SPCollectionIntgSuite{
|
|
Suite: tester.NewIntegrationSuite(
|
|
t,
|
|
[][]string{tconfig.M365AcctCredEnvs}),
|
|
})
|
|
}
|
|
|
|
func (suite *SPCollectionIntgSuite) SetupSuite() {
|
|
ctx, flush := tester.NewContext(suite.T())
|
|
defer flush()
|
|
|
|
suite.connector = newController(ctx, suite.T(), path.SharePointService)
|
|
suite.user = tconfig.M365UserID(suite.T())
|
|
|
|
tester.LogTimeOfTest(suite.T())
|
|
}
|
|
|
|
func (suite *SPCollectionIntgSuite) TestCreateSharePointCollection_Libraries() {
|
|
t := suite.T()
|
|
|
|
ctx, flush := tester.NewContext(t)
|
|
defer flush()
|
|
|
|
var (
|
|
siteID = tconfig.M365SiteID(t)
|
|
ctrl = newController(ctx, t, path.SharePointService)
|
|
siteIDs = []string{siteID}
|
|
)
|
|
|
|
site, err := ctrl.PopulateProtectedResourceIDAndName(ctx, siteID, nil)
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
sel := selectors.NewSharePointBackup(siteIDs)
|
|
sel.Include(sel.LibraryFolders([]string{"foo"}, selectors.PrefixMatch()))
|
|
sel.Include(sel.Library("Documents"))
|
|
|
|
sel.SetDiscreteOwnerIDName(site.ID(), site.Name())
|
|
|
|
bpc := inject.BackupProducerConfig{
|
|
LastBackupVersion: version.NoBackup,
|
|
Options: control.DefaultOptions(),
|
|
ProtectedResource: site,
|
|
Selector: sel.Selector,
|
|
}
|
|
|
|
cols, excludes, canUsePreviousBackup, err := ctrl.ProduceBackupCollections(
|
|
ctx,
|
|
bpc,
|
|
count.New(),
|
|
fault.New(true))
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
assert.True(t, canUsePreviousBackup, "can use previous backup")
|
|
|
|
var (
|
|
hasDocumentsColl bool
|
|
hasMetadataColl bool
|
|
)
|
|
|
|
documentsColl, err := path.BuildPrefix(
|
|
suite.connector.tenant,
|
|
siteID,
|
|
path.SharePointService,
|
|
path.LibrariesCategory)
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
metadataColl, err := path.BuildMetadata(
|
|
suite.connector.tenant,
|
|
siteID,
|
|
path.SharePointService,
|
|
path.LibrariesCategory,
|
|
false)
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
for i, col := range cols {
|
|
fp := col.FullPath()
|
|
t.Logf("Collection %d: %s", i, fp)
|
|
|
|
hasDocumentsColl = hasDocumentsColl || fp.Equal(documentsColl)
|
|
hasMetadataColl = hasMetadataColl || fp.Equal(metadataColl)
|
|
}
|
|
|
|
require.Truef(t, hasDocumentsColl, "found documents collection %s", documentsColl)
|
|
require.Truef(t, hasMetadataColl, "found metadata collection %s", metadataColl)
|
|
|
|
// No excludes yet as this isn't an incremental backup.
|
|
assert.True(t, excludes.Empty())
|
|
}
|
|
|
|
func (suite *SPCollectionIntgSuite) TestCreateSharePointCollection_Lists() {
|
|
t := suite.T()
|
|
|
|
ctx, flush := tester.NewContext(t)
|
|
defer flush()
|
|
|
|
var (
|
|
siteID = tconfig.M365SiteID(t)
|
|
ctrl = newController(ctx, t, path.SharePointService)
|
|
siteIDs = []string{siteID}
|
|
)
|
|
|
|
site, err := ctrl.PopulateProtectedResourceIDAndName(ctx, siteID, nil)
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
sel := selectors.NewSharePointBackup(siteIDs)
|
|
sel.Include(sel.Lists(selectors.Any()))
|
|
|
|
sel.SetDiscreteOwnerIDName(site.ID(), site.Name())
|
|
|
|
bpc := inject.BackupProducerConfig{
|
|
LastBackupVersion: version.NoBackup,
|
|
Options: control.DefaultOptions(),
|
|
ProtectedResource: site,
|
|
Selector: sel.Selector,
|
|
}
|
|
|
|
cols, excludes, canUsePreviousBackup, err := ctrl.ProduceBackupCollections(
|
|
ctx,
|
|
bpc,
|
|
count.New(),
|
|
fault.New(true))
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
assert.True(t, canUsePreviousBackup, "can use previous backup")
|
|
assert.Less(t, 0, len(cols))
|
|
// No excludes yet as this isn't an incremental backup.
|
|
assert.True(t, excludes.Empty())
|
|
|
|
for _, collection := range cols {
|
|
assert.True(t, path.SharePointService == collection.FullPath().Service() ||
|
|
path.SharePointMetadataService == collection.FullPath().Service())
|
|
assert.Equal(t, path.ListsCategory, collection.FullPath().Category())
|
|
|
|
for item := range collection.Items(ctx, fault.New(true)) {
|
|
t.Log("File: " + item.ID())
|
|
|
|
_, err := io.ReadAll(item.ToReader())
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// CreateGroupsCollection tests
|
|
// ---------------------------------------------------------------------------
|
|
|
|
type GroupsCollectionIntgSuite struct {
|
|
tester.Suite
|
|
connector *Controller
|
|
tenantID string
|
|
user string
|
|
}
|
|
|
|
func TestGroupsCollectionIntgSuite(t *testing.T) {
|
|
suite.Run(t, &GroupsCollectionIntgSuite{
|
|
Suite: tester.NewIntegrationSuite(
|
|
t,
|
|
[][]string{tconfig.M365AcctCredEnvs}),
|
|
})
|
|
}
|
|
|
|
func (suite *GroupsCollectionIntgSuite) SetupSuite() {
|
|
t := suite.T()
|
|
|
|
ctx, flush := tester.NewContext(t)
|
|
defer flush()
|
|
|
|
suite.connector = newController(ctx, t, path.GroupsService)
|
|
suite.user = tconfig.M365UserID(t)
|
|
|
|
acct := tconfig.NewM365Account(t)
|
|
creds, err := acct.M365Config()
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
suite.tenantID = creds.AzureTenantID
|
|
|
|
tester.LogTimeOfTest(t)
|
|
}
|
|
|
|
func (suite *GroupsCollectionIntgSuite) TestCreateGroupsCollection_SharePoint() {
|
|
t := suite.T()
|
|
|
|
ctx, flush := tester.NewContext(t)
|
|
defer flush()
|
|
|
|
var (
|
|
groupID = tconfig.M365TeamID(t)
|
|
ctrl = newController(ctx, t, path.GroupsService)
|
|
groupIDs = []string{groupID}
|
|
)
|
|
|
|
group, err := ctrl.PopulateProtectedResourceIDAndName(ctx, groupID, nil)
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
sel := selectors.NewGroupsBackup(groupIDs)
|
|
sel.Include(sel.LibraryFolders([]string{"test"}, selectors.PrefixMatch()))
|
|
|
|
sel.SetDiscreteOwnerIDName(group.ID(), group.Name())
|
|
|
|
bpc := inject.BackupProducerConfig{
|
|
LastBackupVersion: version.NoBackup,
|
|
Options: control.DefaultOptions(),
|
|
ProtectedResource: group,
|
|
Selector: sel.Selector,
|
|
}
|
|
|
|
collections, excludes, canUsePreviousBackup, err := ctrl.ProduceBackupCollections(
|
|
ctx,
|
|
bpc,
|
|
count.New(),
|
|
fault.New(true))
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
assert.True(t, canUsePreviousBackup, "can use previous backup")
|
|
// No excludes yet as this isn't an incremental backup.
|
|
assert.True(t, excludes.Empty())
|
|
|
|
// we don't know an exact count of drives this will produce,
|
|
// but it should be more than one.
|
|
assert.Greater(t, len(collections), 1)
|
|
|
|
p, err := path.BuildMetadata(
|
|
suite.tenantID,
|
|
groupID,
|
|
path.GroupsService,
|
|
path.LibrariesCategory,
|
|
false)
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
p, err = p.Append(false, odConsts.SitesPathDir)
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
foundSitesMetadata := false
|
|
|
|
for _, coll := range collections {
|
|
sitesMetadataCollection := coll.FullPath().String() == p.String()
|
|
|
|
for object := range coll.Items(ctx, fault.New(true)) {
|
|
if object.ID() == "previouspath" && sitesMetadataCollection {
|
|
foundSitesMetadata = true
|
|
}
|
|
|
|
buf := &bytes.Buffer{}
|
|
_, err := buf.ReadFrom(object.ToReader())
|
|
assert.NoError(t, err, "reading item", clues.ToCore(err))
|
|
}
|
|
}
|
|
|
|
assert.True(t, foundSitesMetadata, "missing sites metadata")
|
|
|
|
status := ctrl.Wait()
|
|
assert.NotZero(t, status.Successes)
|
|
t.Log(status.String())
|
|
}
|
|
|
|
func (suite *GroupsCollectionIntgSuite) TestCreateGroupsCollection_SharePoint_InvalidMetadata() {
|
|
t := suite.T()
|
|
|
|
ctx, flush := tester.NewContext(t)
|
|
defer flush()
|
|
|
|
var (
|
|
groupID = tconfig.M365TeamID(t)
|
|
ctrl = newController(ctx, t, path.GroupsService)
|
|
groupIDs = []string{groupID}
|
|
)
|
|
|
|
group, err := ctrl.PopulateProtectedResourceIDAndName(ctx, groupID, nil)
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
sel := selectors.NewGroupsBackup(groupIDs)
|
|
sel.Include(sel.LibraryFolders([]string{"test"}, selectors.PrefixMatch()))
|
|
|
|
sel.SetDiscreteOwnerIDName(group.ID(), group.Name())
|
|
|
|
site, err := suite.connector.AC.Groups().GetRootSite(ctx, groupID)
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
pth, err := path.Build(
|
|
suite.tenantID,
|
|
groupID,
|
|
path.GroupsService,
|
|
path.LibrariesCategory,
|
|
true,
|
|
odConsts.SitesPathDir,
|
|
ptr.Val(site.GetId()))
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
mmc := []data.RestoreCollection{
|
|
mock.Collection{
|
|
Path: pth,
|
|
ItemData: []data.Item{
|
|
&mock.Item{
|
|
ItemID: "previouspath",
|
|
Reader: io.NopCloser(bytes.NewReader([]byte("invalid"))),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
bpc := inject.BackupProducerConfig{
|
|
LastBackupVersion: version.NoBackup,
|
|
Options: control.DefaultOptions(),
|
|
ProtectedResource: group,
|
|
Selector: sel.Selector,
|
|
MetadataCollections: mmc,
|
|
}
|
|
|
|
collections, excludes, canUsePreviousBackup, err := ctrl.ProduceBackupCollections(
|
|
ctx,
|
|
bpc,
|
|
count.New(),
|
|
fault.New(true))
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
assert.True(t, canUsePreviousBackup, "can use previous backup")
|
|
// No excludes yet as this isn't an incremental backup.
|
|
assert.True(t, excludes.Empty())
|
|
|
|
// we don't know an exact count of drives this will produce,
|
|
// but it should be more than one.
|
|
assert.Greater(t, len(collections), 1)
|
|
|
|
p, err := path.BuildMetadata(
|
|
suite.tenantID,
|
|
groupID,
|
|
path.GroupsService,
|
|
path.LibrariesCategory,
|
|
false)
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
p, err = p.Append(false, odConsts.SitesPathDir)
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
foundSitesMetadata := false
|
|
foundRootTombstone := false
|
|
|
|
sp, err := path.BuildPrefix(
|
|
suite.tenantID,
|
|
groupID,
|
|
path.GroupsService,
|
|
path.LibrariesCategory)
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
sp, err = sp.Append(false, odConsts.SitesPathDir, ptr.Val(site.GetId()))
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
for _, coll := range collections {
|
|
if coll.State() == data.DeletedState {
|
|
if coll.PreviousPath() != nil && coll.PreviousPath().String() == sp.String() {
|
|
foundRootTombstone = true
|
|
}
|
|
|
|
continue
|
|
}
|
|
|
|
sitesMetadataCollection := coll.FullPath().String() == p.String()
|
|
|
|
for object := range coll.Items(ctx, fault.New(true)) {
|
|
if object.ID() == "previouspath" && sitesMetadataCollection {
|
|
foundSitesMetadata = true
|
|
}
|
|
|
|
buf := &bytes.Buffer{}
|
|
_, err := buf.ReadFrom(object.ToReader())
|
|
assert.NoError(t, err, "reading item", clues.ToCore(err))
|
|
}
|
|
}
|
|
|
|
assert.True(t, foundSitesMetadata, "missing sites metadata")
|
|
assert.True(t, foundRootTombstone, "missing root tombstone")
|
|
|
|
status := ctrl.Wait()
|
|
assert.NotZero(t, status.Successes)
|
|
t.Log(status.String())
|
|
}
|