Intermediate step to a few different goals including * moving interface definitions to better locations while avoid cycles * adding a flag to disable kopia-assisted incrementals Create an interface and implementation for the existing Reason struct. The goal is to set stuff up so that eventually the kopia package can ask the struct for the subtree path to work with when merging the hierarchy instead of having the backup operation pass that information in Code changes are mostly just turning stuff into a struct and fixing up compile errors. Some functions have been excluded from the struct (i.e. `Key`) and made into functions in the kopia package itself --- #### 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 - [ ] 🌻 Feature - [ ] 🐛 Bugfix - [ ] 🗺️ Documentation - [ ] 🤖 Supportability/Tests - [ ] 💻 CI/Deployment - [x] 🧹 Tech Debt/Cleanup #### Issue(s) * #2360 #### Test Plan - [ ] 💪 Manual - [x] ⚡ Unit test - [x] 💚 E2E
914 lines
20 KiB
Go
914 lines
20 KiB
Go
package kopia
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"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"
|
|
"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/path"
|
|
)
|
|
|
|
const (
|
|
testCompleteMan = false
|
|
testIncompleteMan = !testCompleteMan
|
|
)
|
|
|
|
var (
|
|
testT1 = time.Now()
|
|
testT2 = testT1.Add(1 * time.Hour)
|
|
|
|
testID1 = manifest.ID("snap1")
|
|
testID2 = manifest.ID("snap2")
|
|
|
|
testBackup1 = "backupID1"
|
|
testBackup2 = "backupID2"
|
|
|
|
testMail = path.ExchangeService.String() + path.EmailCategory.String()
|
|
testEvents = path.ExchangeService.String() + path.EventsCategory.String()
|
|
|
|
testUser1 = "user1"
|
|
testUser2 = "user2"
|
|
testUser3 = "user3"
|
|
|
|
testAllUsersAllCats = []Reasoner{
|
|
// User1 email and events.
|
|
NewReason("", testUser1, path.ExchangeService, path.EmailCategory),
|
|
NewReason("", testUser1, path.ExchangeService, path.EventsCategory),
|
|
// User2 email and events.
|
|
NewReason("", testUser2, path.ExchangeService, path.EmailCategory),
|
|
NewReason("", testUser2, path.ExchangeService, path.EventsCategory),
|
|
// User3 email and events.
|
|
NewReason("", testUser3, path.ExchangeService, path.EmailCategory),
|
|
NewReason("", testUser3, path.ExchangeService, path.EventsCategory),
|
|
}
|
|
testAllUsersMail = []Reasoner{
|
|
NewReason("", testUser1, path.ExchangeService, path.EmailCategory),
|
|
NewReason("", testUser2, path.ExchangeService, path.EmailCategory),
|
|
NewReason("", testUser3, path.ExchangeService, path.EmailCategory),
|
|
}
|
|
testUser1Mail = []Reasoner{
|
|
NewReason("", testUser1, path.ExchangeService, path.EmailCategory),
|
|
}
|
|
)
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Empty mocks that return no data
|
|
// -----------------------------------------------------------------------------
|
|
type mockEmptySnapshotManager struct{}
|
|
|
|
func (sm mockEmptySnapshotManager) FindManifests(
|
|
context.Context,
|
|
map[string]string,
|
|
) ([]*manifest.EntryMetadata, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (sm mockEmptySnapshotManager) LoadSnapshot(
|
|
context.Context,
|
|
manifest.ID,
|
|
) (*snapshot.Manifest, error) {
|
|
return nil, snapshot.ErrSnapshotNotFound
|
|
}
|
|
|
|
type mockEmptyModelGetter struct{}
|
|
|
|
func (mg mockEmptyModelGetter) GetBackup(
|
|
context.Context,
|
|
model.StableID,
|
|
) (*backup.Backup, error) {
|
|
return nil, data.ErrNotFound
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Mocks that return data or errors
|
|
// -----------------------------------------------------------------------------
|
|
type manifestInfo struct {
|
|
// We don't currently use the values in the tags.
|
|
tags map[string]string
|
|
metadata *manifest.EntryMetadata
|
|
man *snapshot.Manifest
|
|
err error
|
|
}
|
|
|
|
func newManifestInfo(
|
|
id manifest.ID,
|
|
modTime time.Time,
|
|
incomplete bool,
|
|
backupID string,
|
|
err error,
|
|
tags ...string,
|
|
) manifestInfo {
|
|
incompleteStr := ""
|
|
if incomplete {
|
|
incompleteStr = "checkpoint"
|
|
}
|
|
|
|
structTags := make(map[string]string, len(tags))
|
|
|
|
for _, t := range tags {
|
|
tk, _ := makeTagKV(t)
|
|
structTags[tk] = ""
|
|
}
|
|
|
|
res := manifestInfo{
|
|
tags: structTags,
|
|
err: err,
|
|
metadata: &manifest.EntryMetadata{
|
|
ID: id,
|
|
ModTime: modTime,
|
|
Labels: structTags,
|
|
},
|
|
man: &snapshot.Manifest{
|
|
ID: id,
|
|
IncompleteReason: incompleteStr,
|
|
Tags: structTags,
|
|
},
|
|
}
|
|
|
|
if len(backupID) > 0 {
|
|
k, _ := makeTagKV(TagBackupID)
|
|
res.metadata.Labels[k] = backupID
|
|
res.man.Tags[k] = backupID
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
type mockSnapshotManager struct {
|
|
data []manifestInfo
|
|
findErr error
|
|
}
|
|
|
|
func matchesTags(mi manifestInfo, tags map[string]string) bool {
|
|
for k := range tags {
|
|
if _, ok := mi.tags[k]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (msm *mockSnapshotManager) FindManifests(
|
|
ctx context.Context,
|
|
tags map[string]string,
|
|
) ([]*manifest.EntryMetadata, error) {
|
|
if msm == nil {
|
|
return nil, assert.AnError
|
|
}
|
|
|
|
if msm.findErr != nil {
|
|
return nil, msm.findErr
|
|
}
|
|
|
|
res := []*manifest.EntryMetadata{}
|
|
|
|
for _, mi := range msm.data {
|
|
if matchesTags(mi, tags) {
|
|
res = append(res, mi.metadata)
|
|
}
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
func (msm *mockSnapshotManager) LoadSnapshot(
|
|
ctx context.Context,
|
|
id manifest.ID,
|
|
) (*snapshot.Manifest, error) {
|
|
if msm == nil {
|
|
return nil, assert.AnError
|
|
}
|
|
|
|
for _, mi := range msm.data {
|
|
if mi.man.ID == id {
|
|
if mi.err != nil {
|
|
return nil, mi.err
|
|
}
|
|
|
|
return mi.man, nil
|
|
}
|
|
}
|
|
|
|
return nil, snapshot.ErrSnapshotNotFound
|
|
}
|
|
|
|
type backupInfo struct {
|
|
b backup.Backup
|
|
err error
|
|
}
|
|
|
|
func newBackupModel(
|
|
id string,
|
|
hasItemSnap bool,
|
|
hasDetailsSnap bool,
|
|
oldDetailsID bool,
|
|
err error,
|
|
) backupInfo {
|
|
res := backupInfo{
|
|
b: backup.Backup{
|
|
BaseModel: model.BaseModel{
|
|
ID: model.StableID(id),
|
|
},
|
|
SnapshotID: "iid",
|
|
},
|
|
err: err,
|
|
}
|
|
|
|
if hasDetailsSnap {
|
|
if !oldDetailsID {
|
|
res.b.StreamStoreID = "ssid"
|
|
} else {
|
|
res.b.DetailsID = "ssid"
|
|
}
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
type mockModelGetter struct {
|
|
data []backupInfo
|
|
}
|
|
|
|
func (mg mockModelGetter) GetBackup(
|
|
_ context.Context,
|
|
id model.StableID,
|
|
) (*backup.Backup, error) {
|
|
// Use struct here so we return a copy of the struct just in case the caller
|
|
// somehow ends up modifying it.
|
|
var res backup.Backup
|
|
|
|
for _, bi := range mg.data {
|
|
if bi.b.ID != id {
|
|
continue
|
|
}
|
|
|
|
if bi.err != nil {
|
|
return nil, bi.err
|
|
}
|
|
|
|
res = bi.b
|
|
|
|
return &res, nil
|
|
}
|
|
|
|
return nil, data.ErrNotFound
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Tests for getting bases
|
|
// -----------------------------------------------------------------------------
|
|
type BaseFinderUnitSuite struct {
|
|
tester.Suite
|
|
}
|
|
|
|
func TestBaseFinderUnitSuite(t *testing.T) {
|
|
suite.Run(t, &BaseFinderUnitSuite{Suite: tester.NewUnitSuite(t)})
|
|
}
|
|
|
|
func (suite *BaseFinderUnitSuite) TestNoResult_NoBackupsOrSnapshots() {
|
|
t := suite.T()
|
|
|
|
ctx, flush := tester.NewContext(t)
|
|
defer flush()
|
|
|
|
bf := baseFinder{
|
|
sm: mockEmptySnapshotManager{},
|
|
bg: mockEmptyModelGetter{},
|
|
}
|
|
reasons := []Reasoner{
|
|
NewReason("", "a-user", path.ExchangeService, path.EmailCategory),
|
|
}
|
|
|
|
bb := bf.FindBases(ctx, reasons, nil)
|
|
assert.Empty(t, bb.MergeBases())
|
|
assert.Empty(t, bb.AssistBases())
|
|
}
|
|
|
|
func (suite *BaseFinderUnitSuite) TestNoResult_ErrorListingSnapshots() {
|
|
t := suite.T()
|
|
|
|
ctx, flush := tester.NewContext(t)
|
|
defer flush()
|
|
|
|
bf := baseFinder{
|
|
sm: &mockSnapshotManager{findErr: assert.AnError},
|
|
bg: mockEmptyModelGetter{},
|
|
}
|
|
reasons := []Reasoner{
|
|
NewReason("", "a-user", path.ExchangeService, path.EmailCategory),
|
|
}
|
|
|
|
bb := bf.FindBases(ctx, reasons, nil)
|
|
assert.Empty(t, bb.MergeBases())
|
|
assert.Empty(t, bb.AssistBases())
|
|
}
|
|
|
|
func (suite *BaseFinderUnitSuite) TestGetBases() {
|
|
table := []struct {
|
|
name string
|
|
input []Reasoner
|
|
manifestData []manifestInfo
|
|
// Use this to denote the Reasons a base backup or base manifest is
|
|
// selected. The int maps to the index of the backup or manifest in data.
|
|
expectedBaseReasons map[int][]Reasoner
|
|
// Use this to denote the Reasons a kopia assised incrementals manifest is
|
|
// selected. The int maps to the index of the manifest in data.
|
|
expectedAssistManifestReasons map[int][]Reasoner
|
|
backupData []backupInfo
|
|
}{
|
|
{
|
|
name: "Return Older Base If Fail To Get Manifest",
|
|
input: testUser1Mail,
|
|
manifestData: []manifestInfo{
|
|
newManifestInfo(
|
|
testID2,
|
|
testT2,
|
|
testCompleteMan,
|
|
testBackup2,
|
|
assert.AnError,
|
|
testMail,
|
|
testUser1,
|
|
),
|
|
newManifestInfo(
|
|
testID1,
|
|
testT1,
|
|
testCompleteMan,
|
|
testBackup1,
|
|
nil,
|
|
testMail,
|
|
testUser1,
|
|
),
|
|
},
|
|
expectedBaseReasons: map[int][]Reasoner{
|
|
1: testUser1Mail,
|
|
},
|
|
expectedAssistManifestReasons: map[int][]Reasoner{
|
|
1: testUser1Mail,
|
|
},
|
|
backupData: []backupInfo{
|
|
newBackupModel(testBackup2, true, true, false, nil),
|
|
newBackupModel(testBackup1, true, true, false, nil),
|
|
},
|
|
},
|
|
{
|
|
name: "Return Older Base If Fail To Get Backup",
|
|
input: testUser1Mail,
|
|
manifestData: []manifestInfo{
|
|
newManifestInfo(
|
|
testID2,
|
|
testT2,
|
|
testCompleteMan,
|
|
testBackup2,
|
|
nil,
|
|
testMail,
|
|
testUser1,
|
|
),
|
|
newManifestInfo(
|
|
testID1,
|
|
testT1,
|
|
testCompleteMan,
|
|
testBackup1,
|
|
nil,
|
|
testMail,
|
|
testUser1,
|
|
),
|
|
},
|
|
expectedBaseReasons: map[int][]Reasoner{
|
|
1: testUser1Mail,
|
|
},
|
|
expectedAssistManifestReasons: map[int][]Reasoner{
|
|
0: testUser1Mail,
|
|
1: testUser1Mail,
|
|
},
|
|
backupData: []backupInfo{
|
|
newBackupModel(testBackup2, false, false, false, assert.AnError),
|
|
newBackupModel(testBackup1, true, true, false, nil),
|
|
},
|
|
},
|
|
{
|
|
name: "Return Older Base If Missing Details",
|
|
input: testUser1Mail,
|
|
manifestData: []manifestInfo{
|
|
newManifestInfo(
|
|
testID2,
|
|
testT2,
|
|
testCompleteMan,
|
|
testBackup2,
|
|
nil,
|
|
testMail,
|
|
testUser1,
|
|
),
|
|
newManifestInfo(
|
|
testID1,
|
|
testT1,
|
|
testCompleteMan,
|
|
testBackup1,
|
|
nil,
|
|
testMail,
|
|
testUser1,
|
|
),
|
|
},
|
|
expectedBaseReasons: map[int][]Reasoner{
|
|
1: testUser1Mail,
|
|
},
|
|
expectedAssistManifestReasons: map[int][]Reasoner{
|
|
0: testUser1Mail,
|
|
1: testUser1Mail,
|
|
},
|
|
backupData: []backupInfo{
|
|
newBackupModel(testBackup2, true, false, false, nil),
|
|
newBackupModel(testBackup1, true, true, false, nil),
|
|
},
|
|
},
|
|
{
|
|
name: "Old Backup Details Pointer",
|
|
input: testUser1Mail,
|
|
manifestData: []manifestInfo{
|
|
newManifestInfo(
|
|
testID1,
|
|
testT1,
|
|
testCompleteMan,
|
|
testBackup1,
|
|
nil,
|
|
testMail,
|
|
testEvents,
|
|
testUser1,
|
|
testUser2,
|
|
testUser3,
|
|
),
|
|
},
|
|
expectedBaseReasons: map[int][]Reasoner{
|
|
0: testUser1Mail,
|
|
},
|
|
expectedAssistManifestReasons: map[int][]Reasoner{
|
|
0: testUser1Mail,
|
|
},
|
|
backupData: []backupInfo{
|
|
newBackupModel(testBackup1, true, true, true, nil),
|
|
},
|
|
},
|
|
{
|
|
name: "All One Snapshot",
|
|
input: testAllUsersAllCats,
|
|
manifestData: []manifestInfo{
|
|
newManifestInfo(
|
|
testID1,
|
|
testT1,
|
|
testCompleteMan,
|
|
testBackup1,
|
|
nil,
|
|
testMail,
|
|
testEvents,
|
|
testUser1,
|
|
testUser2,
|
|
testUser3,
|
|
),
|
|
},
|
|
expectedBaseReasons: map[int][]Reasoner{
|
|
0: testAllUsersAllCats,
|
|
},
|
|
expectedAssistManifestReasons: map[int][]Reasoner{
|
|
0: testAllUsersAllCats,
|
|
},
|
|
backupData: []backupInfo{
|
|
newBackupModel(testBackup1, true, true, false, nil),
|
|
},
|
|
},
|
|
{
|
|
name: "Multiple Bases Some Overlapping Reasons",
|
|
input: testAllUsersAllCats,
|
|
manifestData: []manifestInfo{
|
|
newManifestInfo(
|
|
testID1,
|
|
testT1,
|
|
testCompleteMan,
|
|
testBackup1,
|
|
nil,
|
|
testMail,
|
|
testEvents,
|
|
testUser1,
|
|
testUser2,
|
|
testUser3,
|
|
),
|
|
newManifestInfo(
|
|
testID2,
|
|
testT2,
|
|
testCompleteMan,
|
|
testBackup2,
|
|
nil,
|
|
testEvents,
|
|
testUser1,
|
|
testUser2,
|
|
testUser3,
|
|
),
|
|
},
|
|
expectedBaseReasons: map[int][]Reasoner{
|
|
0: {
|
|
NewReason("", testUser1, path.ExchangeService, path.EmailCategory),
|
|
NewReason("", testUser2, path.ExchangeService, path.EmailCategory),
|
|
NewReason("", testUser3, path.ExchangeService, path.EmailCategory),
|
|
},
|
|
1: {
|
|
NewReason("", testUser1, path.ExchangeService, path.EventsCategory),
|
|
NewReason("", testUser2, path.ExchangeService, path.EventsCategory),
|
|
NewReason("", testUser3, path.ExchangeService, path.EventsCategory),
|
|
},
|
|
},
|
|
expectedAssistManifestReasons: map[int][]Reasoner{
|
|
0: {
|
|
NewReason("", testUser1, path.ExchangeService, path.EmailCategory),
|
|
NewReason("", testUser2, path.ExchangeService, path.EmailCategory),
|
|
NewReason("", testUser3, path.ExchangeService, path.EmailCategory),
|
|
},
|
|
1: {
|
|
NewReason("", testUser1, path.ExchangeService, path.EventsCategory),
|
|
NewReason("", testUser2, path.ExchangeService, path.EventsCategory),
|
|
NewReason("", testUser3, path.ExchangeService, path.EventsCategory),
|
|
},
|
|
},
|
|
backupData: []backupInfo{
|
|
newBackupModel(testBackup1, true, true, false, nil),
|
|
newBackupModel(testBackup2, true, true, false, nil),
|
|
},
|
|
},
|
|
{
|
|
name: "Newer Incomplete Assist Snapshot",
|
|
input: testUser1Mail,
|
|
manifestData: []manifestInfo{
|
|
newManifestInfo(
|
|
testID1,
|
|
testT1,
|
|
testCompleteMan,
|
|
testBackup1,
|
|
nil,
|
|
testMail,
|
|
testUser1,
|
|
),
|
|
newManifestInfo(
|
|
testID2,
|
|
testT2,
|
|
testIncompleteMan,
|
|
testBackup2,
|
|
nil,
|
|
testMail,
|
|
testUser1,
|
|
),
|
|
},
|
|
expectedBaseReasons: map[int][]Reasoner{
|
|
0: testUser1Mail,
|
|
},
|
|
expectedAssistManifestReasons: map[int][]Reasoner{
|
|
0: testUser1Mail,
|
|
1: testUser1Mail,
|
|
},
|
|
backupData: []backupInfo{
|
|
newBackupModel(testBackup1, true, true, false, nil),
|
|
// Shouldn't be returned but have here just so we can see.
|
|
newBackupModel(testBackup2, true, true, false, nil),
|
|
},
|
|
},
|
|
{
|
|
name: "Incomplete Older Than Complete",
|
|
input: testUser1Mail,
|
|
manifestData: []manifestInfo{
|
|
newManifestInfo(
|
|
testID1,
|
|
testT1,
|
|
testIncompleteMan,
|
|
testBackup1,
|
|
nil,
|
|
testMail,
|
|
testUser1,
|
|
),
|
|
newManifestInfo(
|
|
testID2,
|
|
testT2,
|
|
testCompleteMan,
|
|
testBackup2,
|
|
nil,
|
|
testMail,
|
|
testUser1,
|
|
),
|
|
},
|
|
expectedBaseReasons: map[int][]Reasoner{
|
|
1: testUser1Mail,
|
|
},
|
|
expectedAssistManifestReasons: map[int][]Reasoner{
|
|
1: testUser1Mail,
|
|
},
|
|
backupData: []backupInfo{
|
|
// Shouldn't be returned but have here just so we can see.
|
|
newBackupModel(testBackup1, true, true, false, nil),
|
|
newBackupModel(testBackup2, true, true, false, nil),
|
|
},
|
|
},
|
|
{
|
|
name: "Newest Incomplete Only Incomplete",
|
|
input: testUser1Mail,
|
|
manifestData: []manifestInfo{
|
|
newManifestInfo(
|
|
testID1,
|
|
testT1,
|
|
testIncompleteMan,
|
|
testBackup1,
|
|
nil,
|
|
testMail,
|
|
testUser1,
|
|
),
|
|
newManifestInfo(
|
|
testID2,
|
|
testT2,
|
|
testIncompleteMan,
|
|
testBackup2,
|
|
nil,
|
|
testMail,
|
|
testUser1,
|
|
),
|
|
},
|
|
expectedBaseReasons: map[int][]Reasoner{},
|
|
expectedAssistManifestReasons: map[int][]Reasoner{
|
|
1: testUser1Mail,
|
|
},
|
|
backupData: []backupInfo{
|
|
// Shouldn't be returned but have here just so we can see.
|
|
newBackupModel(testBackup1, true, true, false, nil),
|
|
newBackupModel(testBackup2, true, true, false, nil),
|
|
},
|
|
},
|
|
{
|
|
name: "Some Bases Not Found",
|
|
input: testAllUsersMail,
|
|
manifestData: []manifestInfo{
|
|
newManifestInfo(
|
|
testID1,
|
|
testT1,
|
|
testCompleteMan,
|
|
testBackup1,
|
|
nil,
|
|
testMail,
|
|
testUser1,
|
|
),
|
|
},
|
|
expectedBaseReasons: map[int][]Reasoner{
|
|
0: testUser1Mail,
|
|
},
|
|
expectedAssistManifestReasons: map[int][]Reasoner{
|
|
0: testUser1Mail,
|
|
},
|
|
backupData: []backupInfo{
|
|
newBackupModel(testBackup1, true, true, false, nil),
|
|
},
|
|
},
|
|
{
|
|
name: "Manifests Not Sorted",
|
|
input: testAllUsersMail,
|
|
// Manifests are currently returned in the order they're defined by the
|
|
// mock.
|
|
manifestData: []manifestInfo{
|
|
newManifestInfo(
|
|
testID2,
|
|
testT2,
|
|
testCompleteMan,
|
|
testBackup2,
|
|
nil,
|
|
testMail,
|
|
testUser1,
|
|
),
|
|
newManifestInfo(
|
|
testID1,
|
|
testT1,
|
|
testCompleteMan,
|
|
testBackup1,
|
|
nil,
|
|
testMail,
|
|
testUser1,
|
|
),
|
|
},
|
|
expectedBaseReasons: map[int][]Reasoner{
|
|
0: testUser1Mail,
|
|
},
|
|
expectedAssistManifestReasons: map[int][]Reasoner{
|
|
0: testUser1Mail,
|
|
},
|
|
backupData: []backupInfo{
|
|
newBackupModel(testBackup2, true, true, false, nil),
|
|
// Shouldn't be returned but here just so we can check.
|
|
newBackupModel(testBackup1, true, true, false, nil),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range table {
|
|
suite.Run(test.name, func() {
|
|
t := suite.T()
|
|
|
|
ctx, flush := tester.NewContext(t)
|
|
defer flush()
|
|
|
|
bf := baseFinder{
|
|
sm: &mockSnapshotManager{data: test.manifestData},
|
|
bg: &mockModelGetter{data: test.backupData},
|
|
}
|
|
|
|
bb := bf.FindBases(
|
|
ctx,
|
|
test.input,
|
|
nil)
|
|
|
|
checkBackupEntriesMatch(
|
|
t,
|
|
bb.Backups(),
|
|
test.backupData,
|
|
test.expectedBaseReasons)
|
|
checkManifestEntriesMatch(
|
|
t,
|
|
bb.MergeBases(),
|
|
test.manifestData,
|
|
test.expectedBaseReasons)
|
|
checkManifestEntriesMatch(
|
|
t,
|
|
bb.AssistBases(),
|
|
test.manifestData,
|
|
test.expectedAssistManifestReasons)
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *BaseFinderUnitSuite) TestFindBases_CustomTags() {
|
|
manifestData := []manifestInfo{
|
|
newManifestInfo(
|
|
testID1,
|
|
testT1,
|
|
testCompleteMan,
|
|
testBackup1,
|
|
nil,
|
|
testMail,
|
|
testUser1,
|
|
"fnords",
|
|
"smarf",
|
|
),
|
|
}
|
|
backupData := []backupInfo{
|
|
newBackupModel(testBackup1, true, true, false, nil),
|
|
}
|
|
|
|
table := []struct {
|
|
name string
|
|
input []Reasoner
|
|
tags map[string]string
|
|
// Use this to denote which manifests in data should be expected. Allows
|
|
// defining data in a table while not repeating things between data and
|
|
// expected.
|
|
expectedIdxs map[int][]Reasoner
|
|
}{
|
|
{
|
|
name: "no tags specified",
|
|
tags: nil,
|
|
expectedIdxs: map[int][]Reasoner{
|
|
0: testUser1Mail,
|
|
},
|
|
},
|
|
{
|
|
name: "all custom tags",
|
|
tags: map[string]string{
|
|
"fnords": "",
|
|
"smarf": "",
|
|
},
|
|
expectedIdxs: map[int][]Reasoner{
|
|
0: testUser1Mail,
|
|
},
|
|
},
|
|
{
|
|
name: "subset of custom tags",
|
|
tags: map[string]string{"fnords": ""},
|
|
expectedIdxs: map[int][]Reasoner{
|
|
0: testUser1Mail,
|
|
},
|
|
},
|
|
{
|
|
name: "custom tag mismatch",
|
|
tags: map[string]string{"bojangles": ""},
|
|
expectedIdxs: nil,
|
|
},
|
|
}
|
|
|
|
for _, test := range table {
|
|
suite.Run(test.name, func() {
|
|
t := suite.T()
|
|
|
|
ctx, flush := tester.NewContext(t)
|
|
defer flush()
|
|
|
|
bf := baseFinder{
|
|
sm: &mockSnapshotManager{data: manifestData},
|
|
bg: &mockModelGetter{data: backupData},
|
|
}
|
|
|
|
bb := bf.FindBases(
|
|
ctx,
|
|
testAllUsersAllCats,
|
|
test.tags)
|
|
|
|
checkManifestEntriesMatch(
|
|
t,
|
|
bb.MergeBases(),
|
|
manifestData,
|
|
test.expectedIdxs)
|
|
})
|
|
}
|
|
}
|
|
|
|
func checkManifestEntriesMatch(
|
|
t *testing.T,
|
|
retSnaps []ManifestEntry,
|
|
allExpected []manifestInfo,
|
|
expectedIdxsAndReasons map[int][]Reasoner,
|
|
) {
|
|
// Check the proper snapshot manifests were returned.
|
|
expected := make([]*snapshot.Manifest, 0, len(expectedIdxsAndReasons))
|
|
for i := range expectedIdxsAndReasons {
|
|
expected = append(expected, allExpected[i].man)
|
|
}
|
|
|
|
got := make([]*snapshot.Manifest, 0, len(retSnaps))
|
|
for _, s := range retSnaps {
|
|
got = append(got, s.Manifest)
|
|
}
|
|
|
|
assert.ElementsMatch(t, expected, got)
|
|
|
|
// Check the reasons for selecting each manifest are correct.
|
|
expectedReasons := make(map[manifest.ID][]Reasoner, len(expectedIdxsAndReasons))
|
|
for idx, reasons := range expectedIdxsAndReasons {
|
|
expectedReasons[allExpected[idx].man.ID] = reasons
|
|
}
|
|
|
|
for _, found := range retSnaps {
|
|
reasons, ok := expectedReasons[found.ID]
|
|
if !ok {
|
|
// Missing or extra snapshots will be reported by earlier checks.
|
|
continue
|
|
}
|
|
|
|
assert.ElementsMatch(
|
|
t,
|
|
reasons,
|
|
found.Reasons,
|
|
"incorrect reasons for snapshot with ID %s",
|
|
found.ID,
|
|
)
|
|
}
|
|
}
|
|
|
|
func checkBackupEntriesMatch(
|
|
t *testing.T,
|
|
retBups []BackupEntry,
|
|
allExpected []backupInfo,
|
|
expectedIdxsAndReasons map[int][]Reasoner,
|
|
) {
|
|
// Check the proper snapshot manifests were returned.
|
|
expected := make([]*backup.Backup, 0, len(expectedIdxsAndReasons))
|
|
for i := range expectedIdxsAndReasons {
|
|
expected = append(expected, &allExpected[i].b)
|
|
}
|
|
|
|
got := make([]*backup.Backup, 0, len(retBups))
|
|
for _, s := range retBups {
|
|
got = append(got, s.Backup)
|
|
}
|
|
|
|
assert.ElementsMatch(t, expected, got)
|
|
|
|
// Check the reasons for selecting each manifest are correct.
|
|
expectedReasons := make(map[model.StableID][]Reasoner, len(expectedIdxsAndReasons))
|
|
for idx, reasons := range expectedIdxsAndReasons {
|
|
expectedReasons[allExpected[idx].b.ID] = reasons
|
|
}
|
|
|
|
for _, found := range retBups {
|
|
reasons, ok := expectedReasons[found.ID]
|
|
if !ok {
|
|
// Missing or extra snapshots will be reported by earlier checks.
|
|
continue
|
|
}
|
|
|
|
assert.ElementsMatch(
|
|
t,
|
|
reasons,
|
|
found.Reasons,
|
|
"incorrect reasons for snapshot with ID %s",
|
|
found.ID,
|
|
)
|
|
}
|
|
}
|