corso/src/internal/operations/manifests_test.go
ashmrtn 7cb01b1e27
Wrap new base finder and wire it up (#3524)
Take the new base finder code, wrap it in
a thin wrapper and use it in place of the
previous way of finding base snapshots

This solution is not focused on efficiency
as it does result in looking up backup
models multiple times. It's more to get the
new way of finding bases out and then we
can go back and smooth things over when
we have the chance

---

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

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

#### Type of change

- [x] 🌻 Feature
- [ ] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [ ] 🤖 Supportability/Tests
- [ ] 💻 CI/Deployment
- [ ] 🧹 Tech Debt/Cleanup

#### Issue(s)

* #3202

#### Test Plan

- [ ] 💪 Manual
- [x]  Unit test
- [x] 💚 E2E
2023-05-31 18:27:15 +00:00

1312 lines
30 KiB
Go

package operations
import (
"context"
"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/require"
"github.com/stretchr/testify/suite"
"golang.org/x/exp/maps"
"github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/internal/kopia"
"github.com/alcionai/corso/src/internal/model"
"github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/pkg/backup"
"github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/path"
)
// ---------------------------------------------------------------------------
// interfaces
// ---------------------------------------------------------------------------
type mockManifestRestorer struct {
mockRestoreProducer
mans []kopia.ManifestEntry
mrErr error // err varname already claimed by mockRestoreProducer
}
func (mmr mockManifestRestorer) FindBases(
ctx context.Context,
reasons []kopia.Reason,
tags map[string]string,
) ([]kopia.ManifestEntry, error) {
mans := map[string]kopia.ManifestEntry{}
for _, r := range reasons {
for _, m := range mmr.mans {
for _, mr := range m.Reasons {
if mr.ResourceOwner == r.ResourceOwner {
mans[string(m.ID)] = m
break
}
}
}
}
return maps.Values(mans), mmr.mrErr
}
type mockGetBackuper struct {
detailsID string
streamstoreID string
err error
}
func (mg mockGetBackuper) GetBackup(
ctx context.Context,
backupID model.StableID,
) (*backup.Backup, error) {
return &backup.Backup{
DetailsID: mg.detailsID,
StreamStoreID: mg.streamstoreID,
}, mg.err
}
type mockColl struct {
id string // for comparisons
p path.Path
}
func (mc mockColl) Items(context.Context, *fault.Bus) <-chan data.Stream {
return nil
}
func (mc mockColl) FullPath() path.Path {
return mc.p
}
// ---------------------------------------------------------------------------
// tests
// ---------------------------------------------------------------------------
type OperationsManifestsUnitSuite struct {
tester.Suite
}
func TestOperationsManifestsUnitSuite(t *testing.T) {
suite.Run(t, &OperationsManifestsUnitSuite{Suite: tester.NewUnitSuite(t)})
}
func (suite *OperationsManifestsUnitSuite) TestCollectMetadata() {
const (
ro = "owner"
tid = "tenantid"
)
var (
emailPath = makeMetadataBasePath(
suite.T(),
tid,
path.ExchangeService,
ro,
path.EmailCategory)
contactPath = makeMetadataBasePath(
suite.T(),
tid,
path.ExchangeService,
ro,
path.ContactsCategory)
)
table := []struct {
name string
manID string
reasons []kopia.Reason
fileNames []string
expectPaths func(*testing.T, []string) []path.Path
expectErr error
}{
{
name: "single reason, single file",
manID: "single single",
reasons: []kopia.Reason{
{
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
},
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
},
fileNames: []string{"a"},
},
{
name: "single reason, multiple files",
manID: "single multi",
reasons: []kopia.Reason{
{
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
},
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
},
fileNames: []string{"a", "b"},
},
{
name: "multiple reasons, single file",
manID: "multi single",
reasons: []kopia.Reason{
{
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
{
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.ContactsCategory,
},
},
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
},
fileNames: []string{"a"},
},
{
name: "multiple reasons, multiple file",
manID: "multi multi",
reasons: []kopia.Reason{
{
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
{
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.ContactsCategory,
},
},
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
},
fileNames: []string{"a", "b"},
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
paths := test.expectPaths(t, test.fileNames)
mr := mockRestoreProducer{err: test.expectErr}
mr.buildRestoreFunc(t, test.manID, paths)
man := kopia.ManifestEntry{
Manifest: &snapshot.Manifest{ID: manifest.ID(test.manID)},
Reasons: test.reasons,
}
_, err := collectMetadata(ctx, &mr, man, test.fileNames, tid, fault.New(true))
assert.ErrorIs(t, err, test.expectErr, clues.ToCore(err))
})
}
}
func (suite *OperationsManifestsUnitSuite) TestVerifyDistinctBases() {
ro := "resource_owner"
table := []struct {
name string
mans []kopia.ManifestEntry
expect assert.ErrorAssertionFunc
}{
{
name: "one manifest, one reason",
mans: []kopia.ManifestEntry{
{
Manifest: &snapshot.Manifest{},
Reasons: []kopia.Reason{
{
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
},
},
},
expect: assert.NoError,
},
{
name: "one incomplete manifest",
mans: []kopia.ManifestEntry{
{
Manifest: &snapshot.Manifest{IncompleteReason: "ir"},
},
},
expect: assert.NoError,
},
{
name: "one manifest, multiple reasons",
mans: []kopia.ManifestEntry{
{
Manifest: &snapshot.Manifest{},
Reasons: []kopia.Reason{
{
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
{
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.ContactsCategory,
},
},
},
},
expect: assert.NoError,
},
{
name: "one manifest, duplicate reasons",
mans: []kopia.ManifestEntry{
{
Manifest: &snapshot.Manifest{},
Reasons: []kopia.Reason{
{
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
{
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
},
},
},
expect: assert.Error,
},
{
name: "two manifests, non-overlapping reasons",
mans: []kopia.ManifestEntry{
{
Manifest: &snapshot.Manifest{},
Reasons: []kopia.Reason{
{
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
},
},
{
Manifest: &snapshot.Manifest{},
Reasons: []kopia.Reason{
{
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.ContactsCategory,
},
},
},
},
expect: assert.NoError,
},
{
name: "two manifests, overlapping reasons",
mans: []kopia.ManifestEntry{
{
Manifest: &snapshot.Manifest{},
Reasons: []kopia.Reason{
{
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
},
},
{
Manifest: &snapshot.Manifest{},
Reasons: []kopia.Reason{
{
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
},
},
},
expect: assert.Error,
},
{
name: "two manifests, overlapping reasons, one snapshot incomplete",
mans: []kopia.ManifestEntry{
{
Manifest: &snapshot.Manifest{},
Reasons: []kopia.Reason{
{
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
},
},
{
Manifest: &snapshot.Manifest{IncompleteReason: "ir"},
Reasons: []kopia.Reason{
{
ResourceOwner: ro,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
},
},
},
expect: assert.NoError,
},
}
for _, test := range table {
suite.Run(test.name, func() {
ctx, flush := tester.NewContext(suite.T())
defer flush()
err := verifyDistinctBases(ctx, test.mans)
test.expect(suite.T(), err, clues.ToCore(err))
})
}
}
func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata() {
const (
ro = "resourceowner"
tid = "tenantid"
did = "detailsid"
)
makeMan := func(pct path.CategoryType, id, incmpl, bid string) kopia.ManifestEntry {
tags := map[string]string{}
if len(bid) > 0 {
tags = map[string]string{"tag:" + kopia.TagBackupID: bid}
}
return kopia.ManifestEntry{
Manifest: &snapshot.Manifest{
ID: manifest.ID(id),
IncompleteReason: incmpl,
Tags: tags,
},
Reasons: []kopia.Reason{
{
ResourceOwner: ro,
Service: path.ExchangeService,
Category: pct,
},
},
}
}
table := []struct {
name string
mr mockManifestRestorer
gb mockGetBackuper
getMeta bool
assertErr assert.ErrorAssertionFunc
assertB assert.BoolAssertionFunc
expectDCS []mockColl
expectNilMans bool
}{
{
name: "don't get metadata, no mans",
mr: mockManifestRestorer{
mockRestoreProducer: mockRestoreProducer{},
mans: []kopia.ManifestEntry{},
},
gb: mockGetBackuper{detailsID: did},
getMeta: false,
assertErr: assert.NoError,
assertB: assert.False,
expectDCS: nil,
},
{
name: "don't get metadata",
mr: mockManifestRestorer{
mockRestoreProducer: mockRestoreProducer{},
mans: []kopia.ManifestEntry{makeMan(path.EmailCategory, "id1", "", "")},
},
gb: mockGetBackuper{detailsID: did},
getMeta: false,
assertErr: assert.NoError,
assertB: assert.False,
expectDCS: nil,
},
{
name: "don't get metadata, incomplete manifest",
mr: mockManifestRestorer{
mockRestoreProducer: mockRestoreProducer{},
mans: []kopia.ManifestEntry{makeMan(path.EmailCategory, "id1", "ir", "")},
},
gb: mockGetBackuper{detailsID: did},
getMeta: false,
assertErr: assert.NoError,
assertB: assert.False,
expectDCS: nil,
},
{
name: "fetch manifests errors",
mr: mockManifestRestorer{
mockRestoreProducer: mockRestoreProducer{},
mrErr: assert.AnError,
},
gb: mockGetBackuper{detailsID: did},
getMeta: true,
assertErr: assert.Error,
assertB: assert.False,
expectDCS: nil,
},
{
name: "verify distinct bases fails",
mr: mockManifestRestorer{
mockRestoreProducer: mockRestoreProducer{},
mans: []kopia.ManifestEntry{
makeMan(path.EmailCategory, "id1", "", ""),
makeMan(path.EmailCategory, "id2", "", ""),
},
},
gb: mockGetBackuper{detailsID: did},
getMeta: true,
assertErr: assert.NoError, // No error, even though verify failed.
assertB: assert.False,
expectDCS: nil,
},
{
name: "no manifests",
mr: mockManifestRestorer{
mockRestoreProducer: mockRestoreProducer{},
mans: []kopia.ManifestEntry{},
},
gb: mockGetBackuper{detailsID: did},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: nil,
},
{
name: "only incomplete manifests",
mr: mockManifestRestorer{
mockRestoreProducer: mockRestoreProducer{},
mans: []kopia.ManifestEntry{
makeMan(path.EmailCategory, "id1", "ir", ""),
makeMan(path.ContactsCategory, "id2", "ir", ""),
},
},
gb: mockGetBackuper{detailsID: did},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: nil,
},
{
name: "man missing backup id",
mr: mockManifestRestorer{
mockRestoreProducer: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"id": {data.NotFoundRestoreCollection{Collection: mockColl{id: "id_coll"}}},
},
},
mans: []kopia.ManifestEntry{makeMan(path.EmailCategory, "id", "", "")},
},
gb: mockGetBackuper{detailsID: did},
getMeta: true,
assertErr: assert.Error,
assertB: assert.False,
expectNilMans: true,
},
{
name: "backup missing details id",
mr: mockManifestRestorer{
mockRestoreProducer: mockRestoreProducer{},
mans: []kopia.ManifestEntry{makeMan(path.EmailCategory, "id1", "", "bid")},
},
gb: mockGetBackuper{},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.False,
},
{
name: "one complete, one incomplete",
mr: mockManifestRestorer{
mockRestoreProducer: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"id": {data.NotFoundRestoreCollection{Collection: mockColl{id: "id_coll"}}},
"incmpl_id": {data.NotFoundRestoreCollection{Collection: mockColl{id: "incmpl_id_coll"}}},
},
},
mans: []kopia.ManifestEntry{
makeMan(path.EmailCategory, "id", "", "bid"),
makeMan(path.EmailCategory, "incmpl_id", "ir", ""),
},
},
gb: mockGetBackuper{detailsID: did},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "id_coll"}},
},
{
name: "single valid man",
mr: mockManifestRestorer{
mockRestoreProducer: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"id": {data.NotFoundRestoreCollection{Collection: mockColl{id: "id_coll"}}},
},
},
mans: []kopia.ManifestEntry{makeMan(path.EmailCategory, "id", "", "bid")},
},
gb: mockGetBackuper{detailsID: did},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{{id: "id_coll"}},
},
{
name: "multiple valid mans",
mr: mockManifestRestorer{
mockRestoreProducer: mockRestoreProducer{
collsByID: map[string][]data.RestoreCollection{
"mail": {data.NotFoundRestoreCollection{Collection: mockColl{id: "mail_coll"}}},
"contact": {data.NotFoundRestoreCollection{Collection: mockColl{id: "contact_coll"}}},
},
},
mans: []kopia.ManifestEntry{
makeMan(path.EmailCategory, "mail", "", "bid"),
makeMan(path.ContactsCategory, "contact", "", "bid"),
},
},
gb: mockGetBackuper{detailsID: did},
getMeta: true,
assertErr: assert.NoError,
assertB: assert.True,
expectDCS: []mockColl{
{id: "mail_coll"},
{id: "contact_coll"},
},
},
{
name: "error collecting metadata",
mr: mockManifestRestorer{
mockRestoreProducer: mockRestoreProducer{err: assert.AnError},
mans: []kopia.ManifestEntry{makeMan(path.EmailCategory, "id1", "", "bid")},
},
gb: mockGetBackuper{detailsID: did},
getMeta: true,
assertErr: assert.Error,
assertB: assert.False,
expectDCS: nil,
expectNilMans: true,
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
mans, dcs, b, err := produceManifestsAndMetadata(
ctx,
&test.mr,
&test.gb,
[]kopia.Reason{{ResourceOwner: ro}}, nil,
tid,
test.getMeta)
test.assertErr(t, err, clues.ToCore(err))
test.assertB(t, b)
expectMans := test.mr.mans
if test.expectNilMans {
expectMans = nil
}
assert.ElementsMatch(t, 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.NotFoundRestoreCollection{},
dc,
"unexpected type returned [%T]",
dc,
) {
continue
}
tmp := dc.(data.NotFoundRestoreCollection)
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")
})
}
}
func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_fallbackReasons() {
const (
ro = "resourceowner"
manComplete = "complete"
manIncomplete = "incmpl"
fbro = "fb_resourceowner"
fbComplete = "fb_complete"
fbIncomplete = "fb_incmpl"
)
makeMan := func(id, incmpl string, reasons []kopia.Reason) kopia.ManifestEntry {
return kopia.ManifestEntry{
Manifest: &snapshot.Manifest{
ID: manifest.ID(id),
IncompleteReason: incmpl,
Tags: map[string]string{},
},
Reasons: reasons,
}
}
type testInput struct {
id string
incomplete bool
}
table := []struct {
name string
man []testInput
fallback []testInput
reasons []kopia.Reason
fallbackReasons []kopia.Reason
manCategories []path.CategoryType
fbCategories []path.CategoryType
assertErr assert.ErrorAssertionFunc
expectManIDs []string
expectNilMans bool
expectReasons map[string][]path.CategoryType
}{
{
name: "only mans, no fallbacks",
man: []testInput{
{
id: manComplete,
},
{
id: manIncomplete,
incomplete: true,
},
},
manCategories: []path.CategoryType{path.EmailCategory},
fbCategories: []path.CategoryType{path.EmailCategory},
expectManIDs: []string{manComplete, manIncomplete},
expectReasons: map[string][]path.CategoryType{
manComplete: {path.EmailCategory},
manIncomplete: {path.EmailCategory},
},
},
{
name: "no mans, only fallbacks",
fallback: []testInput{
{
id: fbComplete,
},
{
id: fbIncomplete,
incomplete: true,
},
},
manCategories: []path.CategoryType{path.EmailCategory},
fbCategories: []path.CategoryType{path.EmailCategory},
expectManIDs: []string{fbComplete, fbIncomplete},
expectReasons: map[string][]path.CategoryType{
fbComplete: {path.EmailCategory},
fbIncomplete: {path.EmailCategory},
},
},
{
name: "complete mans and fallbacks",
man: []testInput{
{
id: manComplete,
},
},
fallback: []testInput{
{
id: fbComplete,
},
},
manCategories: []path.CategoryType{path.EmailCategory},
fbCategories: []path.CategoryType{path.EmailCategory},
expectManIDs: []string{manComplete},
expectReasons: map[string][]path.CategoryType{
manComplete: {path.EmailCategory},
},
},
{
name: "incomplete mans and fallbacks",
man: []testInput{
{
id: manIncomplete,
incomplete: true,
},
},
fallback: []testInput{
{
id: fbIncomplete,
incomplete: true,
},
},
manCategories: []path.CategoryType{path.EmailCategory},
fbCategories: []path.CategoryType{path.EmailCategory},
expectManIDs: []string{manIncomplete},
expectReasons: map[string][]path.CategoryType{
manIncomplete: {path.EmailCategory},
},
},
{
name: "complete and incomplete mans and fallbacks",
man: []testInput{
{
id: manComplete,
},
{
id: manIncomplete,
incomplete: true,
},
},
fallback: []testInput{
{
id: fbComplete,
},
{
id: fbIncomplete,
incomplete: true,
},
},
manCategories: []path.CategoryType{path.EmailCategory},
fbCategories: []path.CategoryType{path.EmailCategory},
expectManIDs: []string{manComplete, manIncomplete},
expectReasons: map[string][]path.CategoryType{
manComplete: {path.EmailCategory},
manIncomplete: {path.EmailCategory},
},
},
{
name: "incomplete mans, complete fallbacks",
man: []testInput{
{
id: manIncomplete,
incomplete: true,
},
},
fallback: []testInput{
{
id: fbComplete,
},
},
manCategories: []path.CategoryType{path.EmailCategory},
fbCategories: []path.CategoryType{path.EmailCategory},
expectManIDs: []string{fbComplete, manIncomplete},
expectReasons: map[string][]path.CategoryType{
fbComplete: {path.EmailCategory},
manIncomplete: {path.EmailCategory},
},
},
{
name: "complete mans, incomplete fallbacks",
man: []testInput{
{
id: manComplete,
},
},
fallback: []testInput{
{
id: fbIncomplete,
incomplete: true,
},
},
manCategories: []path.CategoryType{path.EmailCategory},
fbCategories: []path.CategoryType{path.EmailCategory},
expectManIDs: []string{manComplete},
expectReasons: map[string][]path.CategoryType{
manComplete: {path.EmailCategory},
},
},
{
name: "complete mans, complete fallbacks, multiple reasons",
man: []testInput{
{
id: manComplete,
},
},
fallback: []testInput{
{
id: fbComplete,
},
},
manCategories: []path.CategoryType{path.EmailCategory, path.ContactsCategory},
fbCategories: []path.CategoryType{path.EmailCategory, path.ContactsCategory},
expectManIDs: []string{manComplete},
expectReasons: map[string][]path.CategoryType{
manComplete: {path.EmailCategory, path.ContactsCategory},
},
},
{
name: "complete mans, complete fallbacks, distinct reasons",
man: []testInput{
{
id: manComplete,
},
},
fallback: []testInput{
{
id: fbComplete,
},
},
manCategories: []path.CategoryType{path.ContactsCategory},
fbCategories: []path.CategoryType{path.EmailCategory},
expectManIDs: []string{manComplete, fbComplete},
expectReasons: map[string][]path.CategoryType{
manComplete: {path.ContactsCategory},
fbComplete: {path.EmailCategory},
},
},
{
name: "fb has superset of mans reasons",
man: []testInput{
{
id: manComplete,
},
},
fallback: []testInput{
{
id: fbComplete,
},
},
manCategories: []path.CategoryType{path.ContactsCategory},
fbCategories: []path.CategoryType{path.EmailCategory, path.ContactsCategory, path.EventsCategory},
expectManIDs: []string{manComplete, fbComplete},
expectReasons: map[string][]path.CategoryType{
manComplete: {path.ContactsCategory},
fbComplete: {path.EmailCategory, path.EventsCategory},
},
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
mainReasons := []kopia.Reason{}
fbReasons := []kopia.Reason{}
for _, cat := range test.manCategories {
mainReasons = append(
mainReasons,
kopia.Reason{
ResourceOwner: ro,
Service: path.ExchangeService,
Category: cat,
})
}
for _, cat := range test.fbCategories {
fbReasons = append(
fbReasons,
kopia.Reason{
ResourceOwner: fbro,
Service: path.ExchangeService,
Category: cat,
})
}
mans := []kopia.ManifestEntry{}
for _, m := range test.man {
incomplete := ""
if m.incomplete {
incomplete = "ir"
}
mans = append(mans, makeMan(m.id, incomplete, mainReasons))
}
for _, m := range test.fallback {
incomplete := ""
if m.incomplete {
incomplete = "ir"
}
mans = append(mans, makeMan(m.id, incomplete, fbReasons))
}
mr := mockManifestRestorer{mans: mans}
gotMans, _, b, err := produceManifestsAndMetadata(
ctx,
&mr,
nil,
mainReasons,
fbReasons,
"tid",
false)
require.NoError(t, err, clues.ToCore(err))
assert.False(t, b, "no-metadata is forced for this test")
manIDs := []string{}
for _, m := range gotMans {
manIDs = append(manIDs, string(m.ID))
reasons := test.expectReasons[string(m.ID)]
mrs := []path.CategoryType{}
for _, r := range m.Reasons {
mrs = append(mrs, r.Category)
}
assert.ElementsMatch(t, reasons, mrs)
}
assert.ElementsMatch(t, test.expectManIDs, manIDs)
})
}
}
// ---------------------------------------------------------------------------
// older tests
// ---------------------------------------------------------------------------
type BackupManifestUnitSuite struct {
tester.Suite
}
func TestBackupManifestUnitSuite(t *testing.T) {
suite.Run(t, &BackupManifestUnitSuite{Suite: tester.NewUnitSuite(t)})
}
func (suite *BackupManifestUnitSuite) TestBackupOperation_VerifyDistinctBases() {
const user = "a-user"
table := []struct {
name string
input []kopia.ManifestEntry
errCheck assert.ErrorAssertionFunc
}{
{
name: "SingleManifestMultipleReasons",
input: []kopia.ManifestEntry{
{
Manifest: &snapshot.Manifest{
ID: "id1",
},
Reasons: []kopia.Reason{
{
ResourceOwner: user,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
{
ResourceOwner: user,
Service: path.ExchangeService,
Category: path.EventsCategory,
},
},
},
},
errCheck: assert.NoError,
},
{
name: "MultipleManifestsDistinctReason",
input: []kopia.ManifestEntry{
{
Manifest: &snapshot.Manifest{
ID: "id1",
},
Reasons: []kopia.Reason{
{
ResourceOwner: user,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
},
},
{
Manifest: &snapshot.Manifest{
ID: "id2",
},
Reasons: []kopia.Reason{
{
ResourceOwner: user,
Service: path.ExchangeService,
Category: path.EventsCategory,
},
},
},
},
errCheck: assert.NoError,
},
{
name: "MultipleManifestsSameReason",
input: []kopia.ManifestEntry{
{
Manifest: &snapshot.Manifest{
ID: "id1",
},
Reasons: []kopia.Reason{
{
ResourceOwner: user,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
},
},
{
Manifest: &snapshot.Manifest{
ID: "id2",
},
Reasons: []kopia.Reason{
{
ResourceOwner: user,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
},
},
},
errCheck: assert.Error,
},
{
name: "MultipleManifestsSameReasonOneIncomplete",
input: []kopia.ManifestEntry{
{
Manifest: &snapshot.Manifest{
ID: "id1",
},
Reasons: []kopia.Reason{
{
ResourceOwner: user,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
},
},
{
Manifest: &snapshot.Manifest{
ID: "id2",
IncompleteReason: "checkpoint",
},
Reasons: []kopia.Reason{
{
ResourceOwner: user,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
},
},
},
errCheck: assert.NoError,
},
}
for _, test := range table {
suite.Run(test.name, func() {
ctx, flush := tester.NewContext(suite.T())
defer flush()
err := verifyDistinctBases(ctx, test.input)
test.errCheck(suite.T(), err, clues.ToCore(err))
})
}
}
func (suite *BackupManifestUnitSuite) TestBackupOperation_CollectMetadata() {
var (
tenant = "a-tenant"
resourceOwner = "a-user"
fileNames = []string{
"delta",
"paths",
}
emailDeltaPath = makeMetadataPath(
suite.T(),
tenant,
path.ExchangeService,
resourceOwner,
path.EmailCategory,
fileNames[0],
)
emailPathsPath = makeMetadataPath(
suite.T(),
tenant,
path.ExchangeService,
resourceOwner,
path.EmailCategory,
fileNames[1],
)
contactsDeltaPath = makeMetadataPath(
suite.T(),
tenant,
path.ExchangeService,
resourceOwner,
path.ContactsCategory,
fileNames[0],
)
contactsPathsPath = makeMetadataPath(
suite.T(),
tenant,
path.ExchangeService,
resourceOwner,
path.ContactsCategory,
fileNames[1],
)
)
table := []struct {
name string
inputMan kopia.ManifestEntry
inputFiles []string
expected []path.Path
}{
{
name: "SingleReasonSingleFile",
inputMan: kopia.ManifestEntry{
Manifest: &snapshot.Manifest{},
Reasons: []kopia.Reason{
{
ResourceOwner: resourceOwner,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
},
},
inputFiles: []string{fileNames[0]},
expected: []path.Path{emailDeltaPath},
},
{
name: "SingleReasonMultipleFiles",
inputMan: kopia.ManifestEntry{
Manifest: &snapshot.Manifest{},
Reasons: []kopia.Reason{
{
ResourceOwner: resourceOwner,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
},
},
inputFiles: fileNames,
expected: []path.Path{emailDeltaPath, emailPathsPath},
},
{
name: "MultipleReasonsMultipleFiles",
inputMan: kopia.ManifestEntry{
Manifest: &snapshot.Manifest{},
Reasons: []kopia.Reason{
{
ResourceOwner: resourceOwner,
Service: path.ExchangeService,
Category: path.EmailCategory,
},
{
ResourceOwner: resourceOwner,
Service: path.ExchangeService,
Category: path.ContactsCategory,
},
},
},
inputFiles: fileNames,
expected: []path.Path{
emailDeltaPath,
emailPathsPath,
contactsDeltaPath,
contactsPathsPath,
},
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
mr := &mockRestoreProducer{}
_, err := collectMetadata(ctx, mr, test.inputMan, test.inputFiles, tenant, fault.New(true))
assert.NoError(t, err, clues.ToCore(err))
checkPaths(t, test.expected, mr.gotPaths)
})
}
}