corso/src/internal/operations/manifests_test.go
Abin Simon 636be4feed
Fxi issues with GetMetadataPaths (#4252)
<!-- PR description-->

---

#### Does this PR need a docs update or release note?

- [ ]  Yes, it's included
- [ ] 🕐 Yes, but in a later PR
- [x]  No

#### Type of change

<!--- Please check the type of change your PR introduces: --->
- [ ] 🌻 Feature
- [ ] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [x] 🤖 Supportability/Tests
- [ ] 💻 CI/Deployment
- [ ] 🧹 Tech Debt/Cleanup

#### Issue(s)

<!-- Can reference multiple issues. Use one of the following "magic words" - "closes, fixes" to auto-close the Github issue. -->
* #<issue>

#### Test Plan

<!-- How will this be tested prior to merging.-->
- [ ] 💪 Manual
- [x]  Unit test
- [ ] 💚 E2E
2023-09-15 03:20:46 +00:00

1072 lines
33 KiB
Go

package operations
import (
"bytes"
"context"
"io"
"testing"
"github.com/alcionai/clues"
"github.com/kopia/kopia/repo/manifest"
"github.com/kopia/kopia/snapshot"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/internal/data"
dataMock "github.com/alcionai/corso/src/internal/data/mock"
"github.com/alcionai/corso/src/internal/kopia"
"github.com/alcionai/corso/src/internal/m365"
odConsts "github.com/alcionai/corso/src/internal/m365/service/onedrive/consts"
"github.com/alcionai/corso/src/internal/model"
"github.com/alcionai/corso/src/internal/operations/inject/mock"
"github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/pkg/backup"
"github.com/alcionai/corso/src/pkg/backup/identity"
"github.com/alcionai/corso/src/pkg/backup/metadata"
"github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/path"
)
// ---------------------------------------------------------------------------
// interfaces
// ---------------------------------------------------------------------------
type mockColl struct {
id string // for comparisons
p path.Path
}
func (mc mockColl) Items(context.Context, *fault.Bus) <-chan data.Item {
return nil
}
func (mc mockColl) FullPath() path.Path {
return mc.p
}
type mockBackupFinder struct {
// ResourceOwner -> returned set of data for call to FindBases. We can just
// switch on the ResourceOwner as the passed in Reasons should be the same
// beyond that and results are returned for the union of the reasons anyway.
// This does assume that the return data is properly constructed to return a
// union of the reasons etc.
data map[string]kopia.BackupBases
}
func (bf *mockBackupFinder) FindBases(
_ context.Context,
reasons []identity.Reasoner,
_ map[string]string,
) kopia.BackupBases {
if len(reasons) == 0 {
return kopia.NewMockBackupBases()
}
if bf == nil {
return kopia.NewMockBackupBases()
}
b := bf.data[reasons[0].ProtectedResource()]
if b == nil {
return kopia.NewMockBackupBases()
}
return b
}
// ---------------------------------------------------------------------------
// tests
// ---------------------------------------------------------------------------
type OperationsManifestsUnitSuite struct {
tester.Suite
}
func TestOperationsManifestsUnitSuite(t *testing.T) {
suite.Run(t, &OperationsManifestsUnitSuite{Suite: tester.NewUnitSuite(t)})
}
func (suite *OperationsManifestsUnitSuite) TestGetMetadataPaths() {
const (
ro = "owner"
tid = "tenantid"
)
t := suite.T()
var (
emailPath = makeMetadataBasePath(
suite.T(),
tid,
path.ExchangeService,
ro,
path.EmailCategory)
contactPath = makeMetadataBasePath(
suite.T(),
tid,
path.ExchangeService,
ro,
path.ContactsCategory)
spLibsPath = makeMetadataBasePath(
suite.T(),
tid,
path.SharePointService,
ro,
path.LibrariesCategory)
messagesPath = makeMetadataBasePath(
suite.T(),
tid,
path.GroupsService,
ro,
path.ChannelMessagesCategory)
groupLibsPath = makeMetadataBasePath(
suite.T(),
tid,
path.GroupsService,
ro,
path.LibrariesCategory)
)
groupLibsSitesPath, err := groupLibsPath.Append(false, odConsts.SitesPathDir)
assert.NoError(t, err, clues.ToCore(err))
groupLibsSite1Path, err := groupLibsSitesPath.Append(false, "site1")
assert.NoError(t, err, clues.ToCore(err))
groupLibsSite2Path, err := groupLibsSitesPath.Append(false, "site2")
assert.NoError(t, err, clues.ToCore(err))
getRestorePaths := func(t *testing.T, base path.Path, paths []string) []path.RestorePaths {
ps := []path.RestorePaths{}
for _, f := range paths {
p, err := base.AppendItem(f)
assert.NoError(t, err, clues.ToCore(err))
ps = append(ps, path.RestorePaths{StoragePath: p, RestorePath: base})
}
return ps
}
table := []struct {
name string
manID string
reasons []identity.Reasoner
preFetchPaths []string
preFetchCollection []data.RestoreCollection
expectPaths func(*testing.T, []string) []path.Path
restorePaths []path.RestorePaths
expectErr error
}{
{
name: "single reason",
manID: "single",
reasons: []identity.Reasoner{
kopia.NewReason(tid, ro, path.ExchangeService, path.EmailCategory),
},
preFetchPaths: []string{},
expectPaths: func(t *testing.T, files []string) []path.Path {
ps := make([]path.Path, 0, len(files))
for _, f := range files {
p, err := emailPath.AppendItem(f)
assert.NoError(t, err, clues.ToCore(err))
ps = append(ps, p)
}
return ps
},
restorePaths: getRestorePaths(t, emailPath, metadata.AllMetadataFileNames()),
},
{
name: "multiple reasons",
manID: "multi",
reasons: []identity.Reasoner{
kopia.NewReason(tid, ro, path.ExchangeService, path.EmailCategory),
kopia.NewReason(tid, ro, path.ExchangeService, path.ContactsCategory),
},
preFetchPaths: []string{},
expectPaths: func(t *testing.T, files []string) []path.Path {
ps := make([]path.Path, 0, len(files))
for _, f := range files {
p, err := emailPath.AppendItem(f)
assert.NoError(t, err, clues.ToCore(err))
ps = append(ps, p)
p, err = contactPath.AppendItem(f)
assert.NoError(t, err, clues.ToCore(err))
ps = append(ps, p)
}
return ps
},
restorePaths: append(
getRestorePaths(t, emailPath, metadata.AllMetadataFileNames()),
getRestorePaths(t, contactPath, metadata.AllMetadataFileNames())...),
},
{
name: "single reason sp libraries",
manID: "single-sp-libraries",
reasons: []identity.Reasoner{
kopia.NewReason(tid, ro, path.SharePointService, path.LibrariesCategory),
},
preFetchPaths: []string{},
expectPaths: func(t *testing.T, files []string) []path.Path {
ps := make([]path.Path, 0, len(files))
for _, f := range files {
p, err := spLibsPath.AppendItem(f)
assert.NoError(t, err, clues.ToCore(err))
ps = append(ps, p)
}
return ps
},
restorePaths: getRestorePaths(t, spLibsPath, metadata.AllMetadataFileNames()),
},
{
name: "single reason groups messages",
manID: "single-groups-messages",
reasons: []identity.Reasoner{
kopia.NewReason(tid, ro, path.GroupsService, path.ChannelMessagesCategory),
},
preFetchPaths: []string{},
expectPaths: func(t *testing.T, files []string) []path.Path {
ps := make([]path.Path, 0, len(files))
for _, f := range files {
p, err := messagesPath.AppendItem(f)
assert.NoError(t, err, clues.ToCore(err))
ps = append(ps, p)
}
return ps
},
restorePaths: getRestorePaths(t, messagesPath, metadata.AllMetadataFileNames()),
},
{
name: "single reason groups libraries",
manID: "single-groups-libraries",
reasons: []identity.Reasoner{
kopia.NewReason(tid, ro, path.GroupsService, path.LibrariesCategory),
},
preFetchPaths: []string{"previouspath"},
expectPaths: func(t *testing.T, files []string) []path.Path {
ps := make([]path.Path, 0, len(files))
assert.NoError(t, err, clues.ToCore(err))
for _, f := range files {
p, err := groupLibsSitesPath.AppendItem(f)
assert.NoError(t, err, clues.ToCore(err))
ps = append(ps, p)
}
return ps
},
restorePaths: append(
getRestorePaths(t, groupLibsSite1Path, metadata.AllMetadataFileNames()),
getRestorePaths(t, groupLibsSite2Path, metadata.AllMetadataFileNames())...),
preFetchCollection: []data.RestoreCollection{dataMock.Collection{
ItemData: []data.Item{
&dataMock.Item{
ItemID: "previouspath",
Reader: io.NopCloser(bytes.NewReader(
[]byte(`{"site1": "/path/does/not/matter", "site2": "/path/does/not/matter"}`))),
},
},
}},
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
paths := test.expectPaths(t, test.preFetchPaths)
mr := mockRestoreProducer{err: test.expectErr, colls: test.preFetchCollection}
mr.buildRestoreFunc(t, test.manID, paths)
man := kopia.ManifestEntry{
Manifest: &snapshot.Manifest{ID: manifest.ID(test.manID)},
Reasons: test.reasons,
}
controller := m365.Controller{}
pths, err := controller.GetMetadataPaths(ctx, &mr, man, fault.New(true))
assert.ErrorIs(t, err, test.expectErr, clues.ToCore(err))
assert.ElementsMatch(t, test.restorePaths, pths, "restore paths")
})
}
}
func buildReasons(
tenant string,
ro string,
service path.ServiceType,
cats ...path.CategoryType,
) []identity.Reasoner {
var reasons []identity.Reasoner
for _, cat := range cats {
reasons = append(
reasons,
kopia.NewReason(tenant, ro, service, cat))
}
return reasons
}
func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata() {
const (
ro = "resourceowner"
tid = "tenantid"
did = "detailsid"
)
makeMan := func(id, incmpl string, cats ...path.CategoryType) kopia.ManifestEntry {
return kopia.ManifestEntry{
Manifest: &snapshot.Manifest{
ID: manifest.ID(id),
IncompleteReason: incmpl,
},
Reasons: buildReasons(tid, ro, path.ExchangeService, cats...),
}
}
makeBackup := func(snapID string, cats ...path.CategoryType) kopia.BackupEntry {
return kopia.BackupEntry{
Backup: &backup.Backup{
BaseModel: model.BaseModel{
ID: model.StableID(snapID + "bup"),
},
SnapshotID: snapID,
StreamStoreID: snapID + "store",
},
Reasons: buildReasons(tid, ro, path.ExchangeService, cats...),
}
}
table := []struct {
name string
bf *mockBackupFinder
rp mockRestoreProducer
reasons []identity.Reasoner
getMeta bool
dropAssist bool
assertErr assert.ErrorAssertionFunc
assertB assert.BoolAssertionFunc
expectDCS []mockColl
expectPaths func(t *testing.T, gotPaths []path.Path)
expectMans kopia.BackupBases
}{
{
name: "don't get metadata, no mans",
rp: mockRestoreProducer{},
reasons: []identity.Reasoner{},
getMeta: false,
assertErr: assert.NoError,
assertB: assert.False,
expectDCS: nil,
expectMans: kopia.NewMockBackupBases(),
},
{
name: "don't get metadata",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
ro: kopia.NewMockBackupBases().
WithMergeBases(makeMan("id1", "", path.EmailCategory)).
WithBackups(makeBackup("id1", path.EmailCategory)),
},
},
rp: mockRestoreProducer{},
reasons: []identity.Reasoner{
kopia.NewReason("", ro, path.ExchangeService, path.EmailCategory),
},
getMeta: false,
assertErr: assert.NoError,
assertB: assert.False,
expectDCS: nil,
expectMans: kopia.NewMockBackupBases().
WithMergeBases(makeMan("id1", "", path.EmailCategory)).
WithBackups(makeBackup("id1", path.EmailCategory)).
MockDisableMergeBases(),
},
{
name: "don't get metadata, incomplete manifest",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
ro: kopia.NewMockBackupBases().WithAssistBases(
makeMan("id1", "checkpoint", path.EmailCategory)),
},
},
rp: mockRestoreProducer{},
reasons: []identity.Reasoner{
kopia.NewReason("", ro, path.ExchangeService, path.EmailCategory),
},
getMeta: true,
assertErr: assert.NoError,
// Doesn't matter if it's true or false as merge/assist bases are
// distinct. A future PR can go and remove the requirement to pass the
// flag to kopia and just pass it the bases instead.
assertB: assert.True,
expectDCS: nil,
expectMans: kopia.NewMockBackupBases().WithAssistBases(
makeMan("id1", "checkpoint", path.EmailCategory)),
},
{
name: "one valid man, multiple reasons",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
ro: kopia.NewMockBackupBases().WithMergeBases(
makeMan("id1", "", path.EmailCategory, path.ContactsCategory)),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id1"}}},
},
},
reasons: []identity.Reasoner{
kopia.NewReason("", ro, path.ExchangeService, path.EmailCategory),
kopia.NewReason("", ro, path.ExchangeService, path.ContactsCategory),
},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "id1"}},
expectPaths: func(t *testing.T, gotPaths []path.Path) {
for _, p := range gotPaths {
assert.Equal(
t,
path.ExchangeMetadataService,
p.Service(),
"read data service")
assert.Contains(
t,
[]path.CategoryType{
path.EmailCategory,
path.ContactsCategory,
},
p.Category(),
"read data category doesn't match a given reason")
}
},
expectMans: kopia.NewMockBackupBases().WithMergeBases(
makeMan("id1", "", path.EmailCategory, path.ContactsCategory)),
},
{
name: "one valid man, extra incomplete man",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
ro: kopia.NewMockBackupBases().
WithMergeBases(makeMan("id1", "", path.EmailCategory)).
WithAssistBases(makeMan("id2", "checkpoint", path.EmailCategory)),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id1"}}},
"id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id2"}}},
},
},
reasons: []identity.Reasoner{
kopia.NewReason("", ro, path.ExchangeService, path.EmailCategory),
},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "id1"}},
expectMans: kopia.NewMockBackupBases().
WithMergeBases(makeMan("id1", "", path.EmailCategory)).
WithAssistBases(makeMan("id2", "checkpoint", path.EmailCategory)),
},
{
name: "one valid man, extra incomplete man, drop assist bases",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
ro: kopia.NewMockBackupBases().
WithMergeBases(makeMan("id1", "", path.EmailCategory)).
WithAssistBases(makeMan("id2", "checkpoint", path.EmailCategory)),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id1"}}},
"id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id2"}}},
},
},
reasons: []identity.Reasoner{
kopia.NewReason("", ro, path.ExchangeService, path.EmailCategory),
},
getMeta: true,
dropAssist: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "id1"}},
expectMans: kopia.NewMockBackupBases().
WithMergeBases(makeMan("id1", "", path.EmailCategory)).
MockDisableAssistBases(),
},
{
name: "multiple valid mans",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
ro: kopia.NewMockBackupBases().WithMergeBases(
makeMan("id1", "", path.EmailCategory),
makeMan("id2", "", path.EmailCategory)),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id1"}}},
"id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id2"}}},
},
},
reasons: []identity.Reasoner{
kopia.NewReason("", ro, path.ExchangeService, path.EmailCategory),
},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "id1"}, {id: "id2"}},
expectMans: kopia.NewMockBackupBases().WithMergeBases(
makeMan("id1", "", path.EmailCategory),
makeMan("id2", "", path.EmailCategory)),
},
{
name: "error collecting metadata",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
ro: kopia.NewMockBackupBases().
WithMergeBases(makeMan("id1", "", path.EmailCategory)),
},
},
rp: mockRestoreProducer{err: assert.AnError},
reasons: []identity.Reasoner{
kopia.NewReason("", ro, path.ExchangeService, path.EmailCategory),
},
getMeta: true,
assertErr: assert.Error,
assertB: assert.False,
expectDCS: nil,
expectMans: nil,
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
emptyMockBackpuProducer := mock.NewMockBackupProducer(nil, data.CollectionStats{}, false)
mans, dcs, b, err := produceManifestsAndMetadata(
ctx,
test.bf,
&emptyMockBackpuProducer,
&test.rp,
test.reasons, nil,
tid,
test.getMeta,
test.dropAssist)
test.assertErr(t, err, clues.ToCore(err))
test.assertB(t, b)
kopia.AssertBackupBasesEqual(t, test.expectMans, mans)
expect, got := []string{}, []string{}
for _, dc := range test.expectDCS {
expect = append(expect, dc.id)
}
for _, dc := range dcs {
if !assert.IsTypef(
t,
data.NoFetchRestoreCollection{},
dc,
"unexpected type returned [%T]",
dc) {
continue
}
tmp := dc.(data.NoFetchRestoreCollection)
if !assert.IsTypef(
t,
mockColl{},
tmp.Collection,
"unexpected type returned [%T]",
tmp.Collection) {
continue
}
mc := tmp.Collection.(mockColl)
got = append(got, mc.id)
}
assert.ElementsMatch(t, expect, got, "expected collections are present")
if test.expectPaths != nil {
test.expectPaths(t, test.rp.gotPaths)
}
})
}
}
func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_FallbackReasons() {
const (
ro = "resourceowner"
fbro = "fb_resourceowner"
tid = "tenantid"
did = "detailsid"
)
makeMan := func(ro, id, incmpl string, cats ...path.CategoryType) kopia.ManifestEntry {
return kopia.ManifestEntry{
Manifest: &snapshot.Manifest{
ID: manifest.ID(id),
IncompleteReason: incmpl,
Tags: map[string]string{"tag:" + kopia.TagBackupID: id + "bup"},
},
Reasons: buildReasons(tid, ro, path.ExchangeService, cats...),
}
}
makeBackup := func(ro, snapID string, cats ...path.CategoryType) kopia.BackupEntry {
return kopia.BackupEntry{
Backup: &backup.Backup{
BaseModel: model.BaseModel{
ID: model.StableID(snapID + "bup"),
},
SnapshotID: snapID,
StreamStoreID: snapID + "store",
},
Reasons: buildReasons(tid, ro, path.ExchangeService, cats...),
}
}
emailReason := kopia.NewReason(
"",
ro,
path.ExchangeService,
path.EmailCategory)
fbEmailReason := kopia.NewReason(
"",
fbro,
path.ExchangeService,
path.EmailCategory)
table := []struct {
name string
bf *mockBackupFinder
rp mockRestoreProducer
reasons []identity.Reasoner
fallbackReasons []identity.Reasoner
getMeta bool
dropAssist bool
assertErr assert.ErrorAssertionFunc
assertB assert.BoolAssertionFunc
expectDCS []mockColl
expectMans kopia.BackupBases
}{
{
name: "don't get metadata, only fallbacks",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
fbro: kopia.NewMockBackupBases().
WithMergeBases(makeMan(fbro, "fb_id1", "", path.EmailCategory)).
WithBackups(makeBackup(fbro, "fb_id1", path.EmailCategory)),
},
},
rp: mockRestoreProducer{},
fallbackReasons: []identity.Reasoner{fbEmailReason},
getMeta: false,
assertErr: assert.NoError,
assertB: assert.False,
expectDCS: nil,
expectMans: kopia.NewMockBackupBases().
WithMergeBases(makeMan(fbro, "fb_id1", "", path.EmailCategory)).
WithBackups(makeBackup(fbro, "fb_id1", path.EmailCategory)).
MockDisableMergeBases(),
},
{
name: "only fallbacks",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
fbro: kopia.NewMockBackupBases().WithMergeBases(
makeMan(fbro, "fb_id1", "", path.EmailCategory)).WithBackups(
makeBackup(fbro, "fb_id1", path.EmailCategory)),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}},
},
},
fallbackReasons: []identity.Reasoner{fbEmailReason},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "fb_id1"}},
expectMans: kopia.NewMockBackupBases().
WithMergeBases(makeMan(fbro, "fb_id1", "", path.EmailCategory)).
WithBackups(makeBackup(fbro, "fb_id1", path.EmailCategory)),
},
{
name: "only fallbacks, drop assist",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
fbro: kopia.NewMockBackupBases().
WithMergeBases(makeMan(fbro, "fb_id1", "", path.EmailCategory)).
WithBackups(makeBackup(fbro, "fb_id1", path.EmailCategory)),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}},
},
},
fallbackReasons: []identity.Reasoner{fbEmailReason},
getMeta: true,
dropAssist: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "fb_id1"}},
expectMans: kopia.NewMockBackupBases().
WithMergeBases(makeMan(fbro, "fb_id1", "", path.EmailCategory)).
WithBackups(makeBackup(fbro, "fb_id1", path.EmailCategory)).
MockDisableAssistBases(),
},
{
name: "complete mans and fallbacks",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
ro: kopia.NewMockBackupBases().
WithMergeBases(makeMan(ro, "id1", "", path.EmailCategory)),
fbro: kopia.NewMockBackupBases().
WithMergeBases(makeMan(fbro, "fb_id1", "", path.EmailCategory)).
WithBackups(makeBackup(fbro, "fb_id1", path.EmailCategory)),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id1"}}},
"fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}},
},
},
reasons: []identity.Reasoner{emailReason},
fallbackReasons: []identity.Reasoner{fbEmailReason},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "id1"}},
expectMans: kopia.NewMockBackupBases().WithMergeBases(
makeMan(ro, "id1", "", path.EmailCategory)),
},
{
name: "incomplete mans and fallbacks",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
ro: kopia.NewMockBackupBases().WithAssistBases(
makeMan(ro, "id2", "checkpoint", path.EmailCategory)),
fbro: kopia.NewMockBackupBases().WithAssistBases(
makeMan(fbro, "fb_id2", "checkpoint", path.EmailCategory)),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id2"}}},
"fb_id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id2"}}},
},
},
reasons: []identity.Reasoner{emailReason},
fallbackReasons: []identity.Reasoner{fbEmailReason},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: nil,
expectMans: kopia.NewMockBackupBases().WithAssistBases(
makeMan(ro, "id2", "checkpoint", path.EmailCategory)),
},
{
name: "complete and incomplete mans and fallbacks",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
ro: kopia.NewMockBackupBases().
WithMergeBases(makeMan(ro, "id1", "", path.EmailCategory)).
WithAssistBases(makeMan(ro, "id2", "checkpoint", path.EmailCategory)),
fbro: kopia.NewMockBackupBases().
WithMergeBases(makeMan(fbro, "fb_id1", "", path.EmailCategory)).
WithBackups(makeBackup(fbro, "fb_id1", path.EmailCategory)).
WithAssistBases(makeMan(fbro, "fb_id2", "checkpoint", path.EmailCategory)),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id1"}}},
"id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id2"}}},
"fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}},
"fb_id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id2"}}},
},
},
reasons: []identity.Reasoner{emailReason},
fallbackReasons: []identity.Reasoner{fbEmailReason},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "id1"}},
expectMans: kopia.NewMockBackupBases().
WithMergeBases(makeMan(ro, "id1", "", path.EmailCategory)).
WithAssistBases(makeMan(ro, "id2", "checkpoint", path.EmailCategory)),
},
{
name: "incomplete mans and complete fallbacks",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
ro: kopia.NewMockBackupBases().WithAssistBases(
makeMan(ro, "id2", "checkpoint", path.EmailCategory)),
fbro: kopia.NewMockBackupBases().
WithMergeBases(makeMan(fbro, "fb_id1", "", path.EmailCategory)).
WithBackups(makeBackup(fbro, "fb_id1", path.EmailCategory)),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id2"}}},
"fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}},
},
},
reasons: []identity.Reasoner{emailReason},
fallbackReasons: []identity.Reasoner{fbEmailReason},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "fb_id1"}},
expectMans: kopia.NewMockBackupBases().
WithMergeBases(makeMan(fbro, "fb_id1", "", path.EmailCategory)).
WithBackups(makeBackup(fbro, "fb_id1", path.EmailCategory)).
WithAssistBases(makeMan(ro, "id2", "checkpoint", path.EmailCategory)),
},
{
name: "incomplete mans and complete fallbacks, no assist bases",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
ro: kopia.NewMockBackupBases().WithAssistBases(
makeMan(ro, "id2", "checkpoint", path.EmailCategory)),
fbro: kopia.NewMockBackupBases().
WithMergeBases(makeMan(fbro, "fb_id1", "", path.EmailCategory)).
WithBackups(makeBackup(fbro, "fb_id1", path.EmailCategory)),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id2"}}},
"fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}},
},
},
reasons: []identity.Reasoner{emailReason},
fallbackReasons: []identity.Reasoner{fbEmailReason},
getMeta: true,
dropAssist: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "fb_id1"}},
expectMans: kopia.NewMockBackupBases().
WithMergeBases(makeMan(fbro, "fb_id1", "", path.EmailCategory)).
WithBackups(makeBackup(fbro, "fb_id1", path.EmailCategory)).
MockDisableAssistBases(),
},
{
name: "complete mans and incomplete fallbacks",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
ro: kopia.NewMockBackupBases().WithMergeBases(
makeMan(ro, "id1", "", path.EmailCategory)),
fbro: kopia.NewMockBackupBases().WithAssistBases(
makeMan(fbro, "fb_id2", "checkpoint", path.EmailCategory)),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id1"}}},
"fb_id2": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id2"}}},
},
},
reasons: []identity.Reasoner{emailReason},
fallbackReasons: []identity.Reasoner{fbEmailReason},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "id1"}},
expectMans: kopia.NewMockBackupBases().WithMergeBases(
makeMan(ro, "id1", "", path.EmailCategory)),
},
{
name: "complete mans and complete fallbacks, multiple reasons",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
ro: kopia.NewMockBackupBases().WithMergeBases(
makeMan(ro, "id1", "", path.EmailCategory, path.ContactsCategory)),
fbro: kopia.NewMockBackupBases().
WithMergeBases(makeMan(fbro, "fb_id1", "", path.EmailCategory, path.ContactsCategory)).
WithBackups(makeBackup(fbro, "fb_id1", path.EmailCategory, path.ContactsCategory)),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id1"}}},
"fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}},
},
},
reasons: []identity.Reasoner{
emailReason,
kopia.NewReason("", ro, path.ExchangeService, path.ContactsCategory),
},
fallbackReasons: []identity.Reasoner{
fbEmailReason,
kopia.NewReason("", fbro, path.ExchangeService, path.ContactsCategory),
},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "id1"}},
expectMans: kopia.NewMockBackupBases().WithMergeBases(
makeMan(ro, "id1", "", path.EmailCategory, path.ContactsCategory)),
},
{
name: "complete mans and complete fallbacks, distinct reasons",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
ro: kopia.NewMockBackupBases().WithMergeBases(
makeMan(ro, "id1", "", path.EmailCategory)),
fbro: kopia.NewMockBackupBases().
WithMergeBases(makeMan(fbro, "fb_id1", "", path.ContactsCategory)).
WithBackups(makeBackup(fbro, "fb_id1", path.ContactsCategory)),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id1"}}},
"fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}},
},
},
reasons: []identity.Reasoner{emailReason},
fallbackReasons: []identity.Reasoner{
kopia.NewReason("", fbro, path.ExchangeService, path.ContactsCategory),
},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "id1"}, {id: "fb_id1"}},
expectMans: kopia.NewMockBackupBases().
WithMergeBases(
makeMan(ro, "id1", "", path.EmailCategory),
makeMan(fbro, "fb_id1", "", path.ContactsCategory)).
WithBackups(makeBackup(fbro, "fb_id1", path.ContactsCategory)),
},
{
name: "complete mans and complete fallbacks, fallback has superset of reasons",
bf: &mockBackupFinder{
data: map[string]kopia.BackupBases{
ro: kopia.NewMockBackupBases().WithMergeBases(
makeMan(ro, "id1", "", path.EmailCategory)),
fbro: kopia.NewMockBackupBases().
WithMergeBases(
makeMan(fbro, "fb_id1", "", path.EmailCategory, path.ContactsCategory)).
WithBackups(
makeBackup(fbro, "fb_id1", path.EmailCategory, path.ContactsCategory)),
},
},
rp: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "id1"}}},
"fb_id1": {data.NoFetchRestoreCollection{Collection: mockColl{id: "fb_id1"}}},
},
},
reasons: []identity.Reasoner{
emailReason,
kopia.NewReason("", ro, path.ExchangeService, path.ContactsCategory),
},
fallbackReasons: []identity.Reasoner{
fbEmailReason,
kopia.NewReason("", fbro, path.ExchangeService, path.ContactsCategory),
},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "id1"}, {id: "fb_id1"}},
expectMans: kopia.NewMockBackupBases().
WithMergeBases(
makeMan(ro, "id1", "", path.EmailCategory),
makeMan(fbro, "fb_id1", "", path.ContactsCategory)).
WithBackups(
makeBackup(fbro, "fb_id1", path.ContactsCategory)),
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
mbp := mock.NewMockBackupProducer(nil, data.CollectionStats{}, false)
mans, dcs, b, err := produceManifestsAndMetadata(
ctx,
test.bf,
&mbp,
&test.rp,
test.reasons, test.fallbackReasons,
tid,
test.getMeta,
test.dropAssist)
test.assertErr(t, err, clues.ToCore(err))
test.assertB(t, b)
kopia.AssertBackupBasesEqual(t, test.expectMans, mans)
expect, got := []string{}, []string{}
for _, dc := range test.expectDCS {
expect = append(expect, dc.id)
}
for _, dc := range dcs {
if !assert.IsTypef(
t,
data.NoFetchRestoreCollection{},
dc,
"unexpected type returned [%T]",
dc) {
continue
}
tmp := dc.(data.NoFetchRestoreCollection)
if !assert.IsTypef(
t,
mockColl{},
tmp.Collection,
"unexpected type returned [%T]",
tmp.Collection) {
continue
}
mc := tmp.Collection.(mockColl)
got = append(got, mc.id)
}
assert.ElementsMatch(t, expect, got, "expected collections are present")
})
}
}