seems like a lot of code, but this is 95% boilerplate additions copied from groups collections packages, with a find-replace for names. Some noteworthy differences: * teamsChats does not handle metadata, so all metadata, delta, and previous path handling was removed * teamsChats does not produce tombstones * chats are never deleted, so no "removed" items are tracked * all chats gets stored at the prefix root, so no "containers" are iterated, and therefore only one collection is ever produced. This means that, overall, while the boilerplate here is still the same, it's much reduced compared to similar packages. --- #### Does this PR need a docs update or release note? - [x] ⛔ No #### Type of change - [x] 🌻 Feature #### Issue(s) * #5062 #### 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.NewBackup().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.NewBackup().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())
|
|
}
|