corso/src/pkg/backup/details/details_test.go
ashmrtn 75c3e5cd33
Don't munge drive-based ShortRefs (#4009)
Munging code originally existed so that SDK
users would get unique ShortRefs if an item
moved or was renamed. However, we no longer
need to support that so this removes that
munging code

Manually tested making a new backup with this
PR where the merge base was a backup made prior
to this PR. A folder with a subfolder and items
was moved between the two backups. Backup
details for the new backup contained all the
expected entries

Restore selector logic should be unaffected
as that assumes the user has passed in a
ShortRef that was previously printed to the
CLI by `corso backup details`. Input is
compared against the ShortRef stored in the
entry, `ShortRef()` is not called on any `Path`

---

#### 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)

* closes #4012

#### Test Plan

- [x] 💪 Manual
- [x]  Unit test
- [ ] 💚 E2E
2023-08-16 17:37:30 +00:00

1414 lines
32 KiB
Go

package details
import (
"bytes"
"fmt"
"io"
"strings"
"testing"
"time"
"github.com/alcionai/clues"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/internal/common/dttm"
"github.com/alcionai/corso/src/internal/m365/collection/drive/metadata"
odConsts "github.com/alcionai/corso/src/internal/m365/service/onedrive/consts"
"github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/internal/version"
"github.com/alcionai/corso/src/pkg/path"
)
// ------------------------------------------------------------
// unit tests
// ------------------------------------------------------------
type DetailsUnitSuite struct {
tester.Suite
}
func TestDetailsUnitSuite(t *testing.T) {
suite.Run(t, &DetailsUnitSuite{Suite: tester.NewUnitSuite(t)})
}
func (suite *DetailsUnitSuite) TestDetailsEntry_HeadersValues() {
initial := time.Now()
nowStr := dttm.FormatTo(initial, dttm.TabularOutput)
now, err := dttm.ParseTime(nowStr)
require.NoError(suite.T(), err, clues.ToCore(err))
table := []struct {
name string
entry Entry
expectHs []string
expectVs []string
}{
{
name: "no info",
entry: Entry{
RepoRef: "reporef",
ShortRef: "deadbeef",
LocationRef: "locationref",
ItemRef: "itemref",
},
expectHs: []string{"ID"},
expectVs: []string{"deadbeef"},
},
{
name: "exchange event info",
entry: Entry{
RepoRef: "reporef",
ShortRef: "deadbeef",
LocationRef: "locationref",
ItemRef: "itemref",
ItemInfo: ItemInfo{
Exchange: &ExchangeInfo{
ItemType: ExchangeEvent,
EventStart: now,
EventEnd: now,
Organizer: "organizer",
EventRecurs: true,
Subject: "subject",
},
},
},
expectHs: []string{"ID", "Organizer", "Subject", "Starts", "Ends", "Recurring"},
expectVs: []string{"deadbeef", "organizer", "subject", nowStr, nowStr, "true"},
},
{
name: "exchange contact info",
entry: Entry{
RepoRef: "reporef",
ShortRef: "deadbeef",
LocationRef: "locationref",
ItemRef: "itemref",
ItemInfo: ItemInfo{
Exchange: &ExchangeInfo{
ItemType: ExchangeContact,
ContactName: "contactName",
},
},
},
expectHs: []string{"ID", "Contact Name"},
expectVs: []string{"deadbeef", "contactName"},
},
{
name: "exchange mail info",
entry: Entry{
RepoRef: "reporef",
ShortRef: "deadbeef",
LocationRef: "locationref",
ItemRef: "itemref",
ItemInfo: ItemInfo{
Exchange: &ExchangeInfo{
ItemType: ExchangeMail,
Sender: "sender",
ParentPath: "Parent",
Recipient: []string{"receiver"},
Subject: "subject",
Received: now,
},
},
},
expectHs: []string{"ID", "Sender", "Folder", "Subject", "Received"},
expectVs: []string{"deadbeef", "sender", "Parent", "subject", nowStr},
},
{
name: "sharepoint info",
entry: Entry{
RepoRef: "reporef",
ShortRef: "deadbeef",
LocationRef: "locationref",
ItemRef: "itemref",
ItemInfo: ItemInfo{
SharePoint: &SharePointInfo{
ItemName: "itemName",
ParentPath: "parentPath",
Size: 1000,
WebURL: "https://not.a.real/url",
DriveName: "aLibrary",
Owner: "user@email.com",
Created: now,
Modified: now,
},
},
},
expectHs: []string{"ID", "ItemName", "Library", "ParentPath", "Size", "Owner", "Created", "Modified"},
expectVs: []string{
"deadbeef",
"itemName",
"aLibrary",
"parentPath",
"1.0 kB",
"user@email.com",
nowStr,
nowStr,
},
},
{
name: "oneDrive info",
entry: Entry{
RepoRef: "reporef",
ShortRef: "deadbeef",
LocationRef: "locationref",
ItemRef: "itemref",
ItemInfo: ItemInfo{
OneDrive: &OneDriveInfo{
ItemName: "itemName",
ParentPath: "parentPath",
Size: 1000,
Owner: "user@email.com",
Created: now,
Modified: now,
},
},
},
expectHs: []string{"ID", "ItemName", "ParentPath", "Size", "Owner", "Created", "Modified"},
expectVs: []string{"deadbeef", "itemName", "parentPath", "1.0 kB", "user@email.com", nowStr, nowStr},
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
hs := test.entry.Headers()
assert.Equal(t, test.expectHs, hs)
vs := test.entry.Values()
assert.Equal(t, test.expectVs, vs)
})
}
}
func exchangeEntry(t *testing.T, id string, size int, it ItemType) Entry {
rr := makeItemPath(
t,
path.ExchangeService,
path.EmailCategory,
"tenant-id",
"user-id",
[]string{"Inbox", "folder1", id})
return Entry{
RepoRef: rr.String(),
ShortRef: rr.ShortRef(),
ParentRef: rr.ToBuilder().Dir().ShortRef(),
LocationRef: rr.Folder(true),
ItemRef: rr.Item(),
ItemInfo: ItemInfo{
Exchange: &ExchangeInfo{
ItemType: it,
Modified: time.Now(),
Size: int64(size),
},
},
}
}
func oneDriveishEntry(t *testing.T, id string, size int, it ItemType) Entry {
service := path.OneDriveService
category := path.FilesCategory
info := ItemInfo{
OneDrive: &OneDriveInfo{
ItemName: "bar",
DriveID: "drive-id",
DriveName: "drive-name",
Modified: time.Now(),
ItemType: it,
Size: int64(size),
},
}
if it == SharePointLibrary {
service = path.SharePointService
category = path.LibrariesCategory
info.OneDrive = nil
info.SharePoint = &SharePointInfo{
ItemName: "bar",
DriveID: "drive-id",
DriveName: "drive-name",
Modified: time.Now(),
ItemType: it,
Size: int64(size),
}
}
rr := makeItemPath(
t,
service,
category,
"tenant-id",
"user-id",
[]string{
odConsts.DrivesPathDir,
"drive-id",
odConsts.RootPathDir,
"Inbox",
"folder1",
id,
})
loc := path.Builder{}.Append(rr.Folders()...).PopFront().PopFront()
return Entry{
RepoRef: rr.String(),
ShortRef: rr.ShortRef(),
ParentRef: rr.ToBuilder().Dir().ShortRef(),
LocationRef: loc.String(),
ItemRef: rr.Item(),
ItemInfo: info,
}
}
func (suite *DetailsUnitSuite) TestDetailsAdd_NoLocationFolders() {
itemID := "foo"
t := suite.T()
table := []struct {
name string
entry Entry
}{
{
name: "Exchange Email",
entry: exchangeEntry(t, itemID, 42, ExchangeMail),
},
{
name: "OneDrive File",
entry: oneDriveishEntry(t, itemID, 42, OneDriveItem),
},
{
name: "SharePoint File",
entry: oneDriveishEntry(t, itemID, 42, SharePointLibrary),
},
{
name: "Legacy SharePoint File",
entry: func() Entry {
res := oneDriveishEntry(t, itemID, 42, SharePointLibrary)
res.SharePoint.ItemType = OneDriveItem
return res
}(),
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
rr, err := path.FromDataLayerPath(test.entry.RepoRef, true)
require.NoError(t, err, clues.ToCore(err))
db := &Builder{}
// Make a local copy so we can modify it.
localItem := test.entry
err = db.Add(rr, &path.Builder{}, localItem.ItemInfo)
require.NoError(t, err, clues.ToCore(err))
// Clear LocationRef that's automatically populated since we passed an
// empty builder above.
localItem.LocationRef = ""
expectedShortRef := localItem.ShortRef
localItem.ShortRef = ""
deets := db.Details()
assert.Len(t, deets.Entries, 1)
got := deets.Entries[0]
gotShortRef := got.ShortRef
got.ShortRef = ""
assert.Equal(t, localItem, got, "DetailsEntry")
assert.Equal(t, expectedShortRef, gotShortRef, "ShortRef")
})
}
}
func (suite *DetailsUnitSuite) TestDetailsAdd_LocationFolders() {
t := suite.T()
exchangeMail1 := exchangeEntry(t, "foo1", 42, ExchangeMail)
oneDrive1 := oneDriveishEntry(t, "foo1", 42, OneDriveItem)
sharePoint1 := oneDriveishEntry(t, "foo1", 42, SharePointLibrary)
sharePointLegacy1 := oneDriveishEntry(t, "foo1", 42, SharePointLibrary)
sharePointLegacy1.SharePoint.ItemType = OneDriveItem
// Sleep for a little so we get a larger difference in mod times between the
// earlier and later entries.
time.Sleep(100 * time.Millisecond)
// Get fresh item IDs so we can check that folders populate with the latest
// mod time. Also the details API is built with the idea that duplicate items
// aren't added (it has no checking for that).
exchangeMail2 := exchangeEntry(t, "foo2", 43, ExchangeMail)
exchangeContact1 := exchangeEntry(t, "foo3", 44, ExchangeContact)
exchangeFolders := []Entry{
{
ItemInfo: ItemInfo{
Folder: &FolderInfo{
DisplayName: "Inbox",
ItemType: FolderItem,
DataType: ExchangeMail,
},
},
},
{
LocationRef: "Inbox",
ItemInfo: ItemInfo{
Folder: &FolderInfo{
DisplayName: "folder1",
ItemType: FolderItem,
DataType: ExchangeMail,
},
},
},
}
exchangeContactFolders := []Entry{
{
ItemInfo: ItemInfo{
Folder: &FolderInfo{
DisplayName: "Inbox",
ItemType: FolderItem,
DataType: ExchangeContact,
},
},
},
{
LocationRef: "Inbox",
ItemInfo: ItemInfo{
Folder: &FolderInfo{
DisplayName: "folder1",
ItemType: FolderItem,
DataType: ExchangeContact,
},
},
},
}
oneDriveishFolders := []Entry{
{
ItemInfo: ItemInfo{
Folder: &FolderInfo{
DisplayName: odConsts.RootPathDir,
ItemType: FolderItem,
DriveName: "drive-name",
DriveID: "drive-id",
},
},
},
{
LocationRef: odConsts.RootPathDir,
ItemInfo: ItemInfo{
Folder: &FolderInfo{
DisplayName: "Inbox",
ItemType: FolderItem,
DriveName: "drive-name",
DriveID: "drive-id",
},
},
},
{
LocationRef: "root:/Inbox",
ItemInfo: ItemInfo{
Folder: &FolderInfo{
DisplayName: "folder1",
ItemType: FolderItem,
DriveName: "drive-name",
DriveID: "drive-id",
},
},
},
}
table := []struct {
name string
entries func() []Entry
expectedDirs func() []Entry
}{
{
name: "One Exchange Email",
entries: func() []Entry {
e := exchangeMail1
ei := *exchangeMail1.Exchange
e.Exchange = &ei
return []Entry{e}
},
expectedDirs: func() []Entry {
res := []Entry{}
for _, entry := range exchangeFolders {
e := entry
ei := *entry.Folder
e.Folder = &ei
e.Folder.Size = exchangeMail1.Exchange.Size
e.Folder.Modified = exchangeMail1.Exchange.Modified
res = append(res, e)
}
return res
},
},
{
name: "Two Exchange Emails",
entries: func() []Entry {
res := []Entry{}
for _, entry := range []Entry{exchangeMail1, exchangeMail2} {
e := entry
ei := *entry.Exchange
e.Exchange = &ei
res = append(res, e)
}
return res
},
expectedDirs: func() []Entry {
res := []Entry{}
for _, entry := range exchangeFolders {
e := entry
ei := *entry.Folder
e.Folder = &ei
e.Folder.Size = exchangeMail1.Exchange.Size + exchangeMail2.Exchange.Size
e.Folder.Modified = exchangeMail2.Exchange.Modified
res = append(res, e)
}
return res
},
},
{
name: "One Email And One Contact",
entries: func() []Entry {
res := []Entry{}
for _, entry := range []Entry{exchangeMail1, exchangeContact1} {
e := entry
ei := *entry.Exchange
e.Exchange = &ei
res = append(res, e)
}
return res
},
expectedDirs: func() []Entry {
res := []Entry{}
for _, entry := range exchangeFolders {
e := entry
ei := *entry.Folder
e.Folder = &ei
e.Folder.Size = exchangeMail1.Exchange.Size
e.Folder.Modified = exchangeMail1.Exchange.Modified
res = append(res, e)
}
for _, entry := range exchangeContactFolders {
e := entry
ei := *entry.Folder
e.Folder = &ei
e.Folder.Size = exchangeContact1.Exchange.Size
e.Folder.Modified = exchangeContact1.Exchange.Modified
res = append(res, e)
}
return res
},
},
{
name: "One OneDrive Item",
entries: func() []Entry {
e := oneDrive1
ei := *oneDrive1.OneDrive
e.OneDrive = &ei
return []Entry{e}
},
expectedDirs: func() []Entry {
res := []Entry{}
for _, entry := range oneDriveishFolders {
e := entry
ei := *entry.Folder
e.Folder = &ei
e.Folder.DataType = OneDriveItem
e.Folder.Size = oneDrive1.OneDrive.Size
e.Folder.Modified = oneDrive1.OneDrive.Modified
res = append(res, e)
}
return res
},
},
{
name: "One SharePoint Item",
entries: func() []Entry {
e := sharePoint1
ei := *sharePoint1.SharePoint
e.SharePoint = &ei
return []Entry{e}
},
expectedDirs: func() []Entry {
res := []Entry{}
for _, entry := range oneDriveishFolders {
e := entry
ei := *entry.Folder
e.Folder = &ei
e.Folder.DataType = SharePointLibrary
e.Folder.Size = sharePoint1.SharePoint.Size
e.Folder.Modified = sharePoint1.SharePoint.Modified
res = append(res, e)
}
return res
},
},
{
name: "One SharePoint Legacy Item",
entries: func() []Entry {
e := sharePoint1
ei := *sharePoint1.SharePoint
e.SharePoint = &ei
return []Entry{e}
},
expectedDirs: func() []Entry {
res := []Entry{}
for _, entry := range oneDriveishFolders {
e := entry
ei := *entry.Folder
e.Folder = &ei
e.Folder.DataType = SharePointLibrary
e.Folder.Size = sharePoint1.SharePoint.Size
e.Folder.Modified = sharePoint1.SharePoint.Modified
res = append(res, e)
}
return res
},
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
db := &Builder{}
for _, entry := range test.entries() {
rr, err := path.FromDataLayerPath(entry.RepoRef, true)
require.NoError(t, err, clues.ToCore(err))
loc, err := path.Builder{}.SplitUnescapeAppend(entry.LocationRef)
require.NoError(t, err, clues.ToCore(err))
err = db.Add(rr, loc, entry.ItemInfo)
require.NoError(t, err, clues.ToCore(err))
}
deets := db.Details()
gotDirs := []Entry{}
for _, entry := range deets.Entries {
// Other test checks items are populated properly.
if entry.infoType() != FolderItem {
continue
}
// Not Comparing these right now.
entry.RepoRef = ""
entry.ShortRef = ""
entry.ParentRef = ""
gotDirs = append(gotDirs, entry)
}
assert.ElementsMatch(t, test.expectedDirs(), gotDirs)
})
}
}
var pathItemsTable = []struct {
name string
ents []Entry
expectRepoRefs []string
expectLocationRefs []string
}{
{
name: "nil entries",
ents: nil,
expectRepoRefs: []string{},
expectLocationRefs: []string{},
},
{
name: "single entry",
ents: []Entry{
{
RepoRef: "abcde",
LocationRef: "locationref",
ItemRef: "itemref",
},
},
expectRepoRefs: []string{"abcde"},
expectLocationRefs: []string{"locationref"},
},
{
name: "multiple entries",
ents: []Entry{
{
RepoRef: "abcde",
LocationRef: "locationref",
ItemRef: "itemref",
},
{
RepoRef: "12345",
LocationRef: "locationref2",
ItemRef: "itemref2",
},
},
expectRepoRefs: []string{"abcde", "12345"},
expectLocationRefs: []string{"locationref", "locationref2"},
},
{
name: "multiple entries with folder",
ents: []Entry{
{
RepoRef: "abcde",
LocationRef: "locationref",
ItemRef: "itemref",
},
{
RepoRef: "12345",
LocationRef: "locationref2",
ItemRef: "itemref2",
},
{
RepoRef: "deadbeef",
LocationRef: "locationref3",
ItemInfo: ItemInfo{
Folder: &FolderInfo{
DisplayName: "test folder",
},
},
},
},
expectRepoRefs: []string{"abcde", "12345"},
expectLocationRefs: []string{"locationref", "locationref2"},
},
{
name: "multiple entries with meta file",
ents: []Entry{
{
RepoRef: "abcde",
LocationRef: "locationref",
},
{
RepoRef: "foo.meta",
LocationRef: "locationref.dirmeta",
ItemRef: "itemref.meta",
ItemInfo: ItemInfo{
OneDrive: &OneDriveInfo{IsMeta: false},
},
},
{
RepoRef: "is-meta-file",
LocationRef: "locationref-meta-file",
ItemRef: "itemref-meta-file",
ItemInfo: ItemInfo{
OneDrive: &OneDriveInfo{IsMeta: true},
},
},
},
expectRepoRefs: []string{"abcde", "foo.meta"},
expectLocationRefs: []string{"locationref", "locationref.dirmeta"},
},
{
name: "multiple entries with folder and meta file",
ents: []Entry{
{
RepoRef: "abcde",
LocationRef: "locationref",
ItemRef: "itemref",
},
{
RepoRef: "12345",
LocationRef: "locationref2",
ItemRef: "itemref2",
},
{
RepoRef: "foo.meta",
LocationRef: "locationref.dirmeta",
ItemRef: "itemref.dirmeta",
ItemInfo: ItemInfo{
OneDrive: &OneDriveInfo{IsMeta: false},
},
},
{
RepoRef: "is-meta-file",
LocationRef: "locationref-meta-file",
ItemRef: "itemref-meta-file",
ItemInfo: ItemInfo{
OneDrive: &OneDriveInfo{IsMeta: true},
},
},
{
RepoRef: "deadbeef",
LocationRef: "locationref3",
ItemRef: "itemref3",
ItemInfo: ItemInfo{
Folder: &FolderInfo{
DisplayName: "test folder",
},
},
},
},
expectRepoRefs: []string{"abcde", "12345", "foo.meta"},
expectLocationRefs: []string{"locationref", "locationref2", "locationref.dirmeta"},
},
}
func (suite *DetailsUnitSuite) TestDetailsModel_Path() {
for _, test := range pathItemsTable {
suite.Run(test.name, func() {
t := suite.T()
d := Details{
DetailsModel: DetailsModel{
Entries: test.ents,
},
}
assert.ElementsMatch(t, test.expectRepoRefs, d.Paths())
})
}
}
func (suite *DetailsUnitSuite) TestDetailsModel_Items() {
for _, test := range pathItemsTable {
suite.Run(test.name, func() {
t := suite.T()
d := Details{
DetailsModel: DetailsModel{
Entries: test.ents,
},
}
ents := d.Items()
assert.Len(t, ents, len(test.expectRepoRefs))
for _, e := range ents {
assert.Contains(t, test.expectRepoRefs, e.RepoRef)
assert.Contains(t, test.expectLocationRefs, e.LocationRef)
}
})
}
}
func (suite *DetailsUnitSuite) TestDetailsModel_FilterMetaFiles() {
t := suite.T()
d := &DetailsModel{
Entries: []Entry{
{
RepoRef: "a.data",
ItemInfo: ItemInfo{
OneDrive: &OneDriveInfo{IsMeta: false},
},
},
{
RepoRef: "b.meta",
ItemInfo: ItemInfo{
OneDrive: &OneDriveInfo{IsMeta: false},
},
},
{
RepoRef: "c.meta",
ItemInfo: ItemInfo{
OneDrive: &OneDriveInfo{IsMeta: true},
},
},
},
}
d2 := d.FilterMetaFiles()
assert.Len(t, d2.Entries, 2)
assert.Len(t, d.Entries, 3)
}
func (suite *DetailsUnitSuite) TestBuilder_Add_shortRefsUniqueFromFolder() {
t := suite.T()
b := Builder{}
name := "itemName"
info := ItemInfo{
OneDrive: &OneDriveInfo{
ItemType: OneDriveItem,
ItemName: name,
DriveName: "drive-name",
DriveID: "drive-id",
},
}
itemPath := makeItemPath(
t,
path.OneDriveService,
path.FilesCategory,
"a-tenant",
"a-user",
[]string{
"drive-id",
odConsts.RootPathDir,
"folder",
name + "-id",
})
otherItemPath := makeItemPath(
t,
path.OneDriveService,
path.FilesCategory,
"a-tenant",
"a-user",
[]string{
"drive-id",
odConsts.RootPathDir,
"folder",
name + "-id",
name,
})
err := b.Add(
itemPath,
// Don't need to generate folders for this entry we just want the ShortRef.
&path.Builder{},
info)
require.NoError(t, err, clues.ToCore(err))
items := b.Details().Items()
require.Len(t, items, 1)
// If the ShortRefs match then it means it's possible for the user to
// construct folder names such that they'll generate a ShortRef collision.
assert.NotEqual(t, otherItemPath.ShortRef(), items[0].ShortRef, "same ShortRef as subfolder item")
}
func (suite *DetailsUnitSuite) TestBuilder_Add_cleansFileIDSuffixes() {
var (
t = suite.T()
b = Builder{}
svc = path.OneDriveService
cat = path.FilesCategory
info = ItemInfo{
OneDrive: &OneDriveInfo{
ItemType: OneDriveItem,
ItemName: "in",
DriveName: "dn",
DriveID: "d",
},
}
dataSfx = makeItemPath(t, svc, cat, "t", "u", []string{"d", "r:", "f", "i1" + metadata.DataFileSuffix})
dirMetaSfx = makeItemPath(t, svc, cat, "t", "u", []string{"d", "r:", "f", "i1" + metadata.DirMetaFileSuffix})
metaSfx = makeItemPath(t, svc, cat, "t", "u", []string{"d", "r:", "f", "i1" + metadata.MetaFileSuffix})
)
// Don't need to generate folders for this entry, we just want the itemRef
loc := &path.Builder{}
err := b.Add(dataSfx, loc, info)
require.NoError(t, err, clues.ToCore(err))
err = b.Add(dirMetaSfx, loc, info)
require.NoError(t, err, clues.ToCore(err))
err = b.Add(metaSfx, loc, info)
require.NoError(t, err, clues.ToCore(err))
for _, ent := range b.Details().Items() {
assert.False(t, strings.HasSuffix(ent.ItemRef, metadata.DirMetaFileSuffix))
assert.False(t, strings.HasSuffix(ent.ItemRef, metadata.MetaFileSuffix))
assert.False(t, strings.HasSuffix(ent.ItemRef, metadata.DataFileSuffix))
}
}
func (suite *DetailsUnitSuite) TestBuilder_DetailsNoDuplicate() {
var (
t = suite.T()
b = Builder{}
svc = path.OneDriveService
cat = path.FilesCategory
info = ItemInfo{
OneDrive: &OneDriveInfo{
ItemType: OneDriveItem,
ItemName: "in",
DriveName: "dn",
DriveID: "d",
},
}
dataSfx = makeItemPath(t, svc, cat, "t", "u", []string{"d", "r:", "f", "i1" + metadata.DataFileSuffix})
dataSfx2 = makeItemPath(t, svc, cat, "t", "u", []string{"d", "r:", "f", "i2" + metadata.DataFileSuffix})
dirMetaSfx = makeItemPath(t, svc, cat, "t", "u", []string{"d", "r:", "f", "i1" + metadata.DirMetaFileSuffix})
metaSfx = makeItemPath(t, svc, cat, "t", "u", []string{"d", "r:", "f", "i1" + metadata.MetaFileSuffix})
)
// Don't need to generate folders for this entry, we just want the itemRef
loc := &path.Builder{}
err := b.Add(dataSfx, loc, info)
require.NoError(t, err, clues.ToCore(err))
err = b.Add(dataSfx2, loc, info)
require.NoError(t, err, clues.ToCore(err))
err = b.Add(dirMetaSfx, loc, info)
require.NoError(t, err, clues.ToCore(err))
err = b.Add(metaSfx, loc, info)
require.NoError(t, err, clues.ToCore(err))
b.knownFolders = map[string]Entry{
"dummy": {
RepoRef: "xyz",
ShortRef: "abcd",
ParentRef: "1234",
LocationRef: "ab",
ItemRef: "cd",
ItemInfo: info,
},
"dummy2": {
RepoRef: "xyz2",
ShortRef: "abcd2",
ParentRef: "12342",
LocationRef: "ab2",
ItemRef: "cd2",
ItemInfo: info,
},
"dummy3": {
RepoRef: "xyz3",
ShortRef: "abcd3",
ParentRef: "12343",
LocationRef: "ab3",
ItemRef: "cd3",
ItemInfo: info,
},
}
// mark the capacity prior to calling details.
// if the entries slice gets modified and grows to a
// 5th space, then the capacity would grow as well.
capCheck := cap(b.d.Entries)
assert.Len(t, b.Details().Entries, 7) // 4 ents + 3 known folders
assert.Len(t, b.Details().Entries, 7) // possible reason for err: knownFolders got added twice
assert.Len(t, b.d.Entries, 4) // len should not have grown
assert.Equal(t, capCheck, cap(b.d.Entries)) // capacity should not have grown
}
func makeItemPath(
t *testing.T,
service path.ServiceType,
category path.CategoryType,
tenant, resourceOwner string,
elems []string,
) path.Path {
t.Helper()
p, err := path.Build(
tenant,
resourceOwner,
service,
category,
true,
elems...)
require.NoError(t, err, clues.ToCore(err))
return p
}
func (suite *DetailsUnitSuite) TestUpdateItem() {
const (
folder1 = "f1"
folder2 = "f2"
)
newExchangePB := path.Builder{}.Append(folder2)
newOneDrivePB := path.Builder{}.Append(odConsts.RootPathDir, folder2)
table := []struct {
name string
input ItemInfo
locPath *path.Builder
expectedItem ItemInfo
}{
{
name: "ExchangeEvent",
input: ItemInfo{
Exchange: &ExchangeInfo{
ItemType: ExchangeEvent,
ParentPath: folder1,
},
},
locPath: newExchangePB,
expectedItem: ItemInfo{
Exchange: &ExchangeInfo{
ItemType: ExchangeEvent,
ParentPath: folder2,
},
},
},
{
name: "ExchangeContact",
input: ItemInfo{
Exchange: &ExchangeInfo{
ItemType: ExchangeContact,
ParentPath: folder1,
},
},
locPath: newExchangePB,
expectedItem: ItemInfo{
Exchange: &ExchangeInfo{
ItemType: ExchangeContact,
ParentPath: folder2,
},
},
},
{
name: "ExchangeMail",
input: ItemInfo{
Exchange: &ExchangeInfo{
ItemType: ExchangeMail,
ParentPath: folder1,
},
},
locPath: newExchangePB,
expectedItem: ItemInfo{
Exchange: &ExchangeInfo{
ItemType: ExchangeMail,
ParentPath: folder2,
},
},
},
{
name: "OneDrive",
input: ItemInfo{
OneDrive: &OneDriveInfo{
ItemType: OneDriveItem,
ParentPath: folder1,
},
},
locPath: newOneDrivePB,
expectedItem: ItemInfo{
OneDrive: &OneDriveInfo{
ItemType: OneDriveItem,
ParentPath: folder2,
},
},
},
{
name: "SharePoint",
input: ItemInfo{
SharePoint: &SharePointInfo{
ItemType: SharePointLibrary,
ParentPath: folder1,
},
},
locPath: newOneDrivePB,
expectedItem: ItemInfo{
SharePoint: &SharePointInfo{
ItemType: SharePointLibrary,
ParentPath: folder2,
},
},
},
{
name: "SharePoint Old Format",
input: ItemInfo{
SharePoint: &SharePointInfo{
ItemType: OneDriveItem,
ParentPath: folder1,
},
},
locPath: newOneDrivePB,
expectedItem: ItemInfo{
SharePoint: &SharePointInfo{
ItemType: SharePointLibrary,
ParentPath: folder2,
},
},
},
{
name: "Empty Item Doesn't Fail",
input: ItemInfo{},
locPath: newOneDrivePB,
expectedItem: ItemInfo{},
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
item := test.input
UpdateItem(&item, test.locPath)
assert.Equal(t, test.expectedItem, item)
})
}
}
func (suite *DetailsUnitSuite) TestDetails_Marshal() {
for _, test := range pathItemsTable {
suite.Run(test.name, func() {
d := &Details{DetailsModel: DetailsModel{
Entries: test.ents,
}}
bs, err := d.Marshal()
require.NoError(suite.T(), err, clues.ToCore(err))
assert.NotEmpty(suite.T(), bs)
})
}
}
func (suite *DetailsUnitSuite) TestUnarshalTo() {
for _, test := range pathItemsTable {
suite.Run(test.name, func() {
orig := &Details{DetailsModel: DetailsModel{
Entries: test.ents,
}}
bs, err := orig.Marshal()
require.NoError(suite.T(), err, clues.ToCore(err))
assert.NotEmpty(suite.T(), bs)
var result Details
umt := UnmarshalTo(&result)
err = umt(io.NopCloser(bytes.NewReader(bs)))
t := suite.T()
require.NoError(t, err, clues.ToCore(err))
require.NotNil(t, result)
assert.ElementsMatch(t, orig.Entries, result.Entries)
})
}
}
func (suite *DetailsUnitSuite) TestLocationIDer_FromEntry() {
const (
rrString = "tenant-id/%s/user-id/%s/drives/drive-id/root:/some/folder/stuff/item"
driveID = "driveID"
expectedUniqueLocFmt = "%s/" + driveID + "/root:/some/folder/stuff"
expectedExchangeUniqueLocFmt = "%s/root:/some/folder/stuff"
expectedDetailsLoc = "root:/some/folder/stuff"
)
table := []struct {
name string
service string
category string
itemInfo ItemInfo
hasLocRef bool
backupVersion int
expectedErr require.ErrorAssertionFunc
expectedUniqueLoc string
}{
{
name: "OneDrive With Drive ID Old Version",
service: path.OneDriveService.String(),
category: path.FilesCategory.String(),
itemInfo: ItemInfo{
OneDrive: &OneDriveInfo{
ItemType: OneDriveItem,
DriveID: driveID,
},
},
backupVersion: version.OneDrive7LocationRef - 1,
expectedErr: require.NoError,
expectedUniqueLoc: fmt.Sprintf(expectedUniqueLocFmt, path.FilesCategory),
},
{
name: "OneDrive With Drive ID And LocationRef",
service: path.OneDriveService.String(),
category: path.FilesCategory.String(),
itemInfo: ItemInfo{
OneDrive: &OneDriveInfo{
ItemType: OneDriveItem,
DriveID: driveID,
},
},
backupVersion: version.OneDrive7LocationRef,
hasLocRef: true,
expectedErr: require.NoError,
expectedUniqueLoc: fmt.Sprintf(expectedUniqueLocFmt, path.FilesCategory),
},
{
name: "OneDrive With Drive ID New Version Errors",
service: path.OneDriveService.String(),
category: path.FilesCategory.String(),
itemInfo: ItemInfo{
OneDrive: &OneDriveInfo{
ItemType: OneDriveItem,
DriveID: driveID,
},
},
backupVersion: version.OneDrive7LocationRef,
expectedErr: require.Error,
},
{
name: "SharePoint With Drive ID Old Version",
service: path.SharePointService.String(),
category: path.LibrariesCategory.String(),
itemInfo: ItemInfo{
SharePoint: &SharePointInfo{
ItemType: SharePointLibrary,
DriveID: driveID,
},
},
backupVersion: version.OneDrive7LocationRef - 1,
expectedErr: require.NoError,
expectedUniqueLoc: fmt.Sprintf(expectedUniqueLocFmt, path.LibrariesCategory),
},
{
name: "SharePoint With Drive ID And LocationRef",
service: path.SharePointService.String(),
category: path.LibrariesCategory.String(),
itemInfo: ItemInfo{
SharePoint: &SharePointInfo{
ItemType: SharePointLibrary,
DriveID: driveID,
},
},
backupVersion: version.OneDrive7LocationRef,
hasLocRef: true,
expectedErr: require.NoError,
expectedUniqueLoc: fmt.Sprintf(expectedUniqueLocFmt, path.LibrariesCategory),
},
{
name: "SharePoint With Drive ID New Version Errors",
service: path.SharePointService.String(),
category: path.LibrariesCategory.String(),
itemInfo: ItemInfo{
SharePoint: &SharePointInfo{
ItemType: SharePointLibrary,
DriveID: driveID,
},
},
backupVersion: version.OneDrive7LocationRef,
expectedErr: require.Error,
},
{
name: "Exchange Email With LocationRef Old Version",
service: path.ExchangeService.String(),
category: path.EmailCategory.String(),
itemInfo: ItemInfo{
Exchange: &ExchangeInfo{
ItemType: ExchangeMail,
},
},
backupVersion: version.OneDrive7LocationRef - 1,
hasLocRef: true,
expectedErr: require.NoError,
expectedUniqueLoc: fmt.Sprintf(expectedExchangeUniqueLocFmt, path.EmailCategory),
},
{
name: "Exchange Email With LocationRef New Version",
service: path.ExchangeService.String(),
category: path.EmailCategory.String(),
itemInfo: ItemInfo{
Exchange: &ExchangeInfo{
ItemType: ExchangeMail,
},
},
backupVersion: version.OneDrive7LocationRef,
hasLocRef: true,
expectedErr: require.NoError,
expectedUniqueLoc: fmt.Sprintf(expectedExchangeUniqueLocFmt, path.EmailCategory),
},
{
name: "Exchange Email Without LocationRef Old Version Errors",
service: path.ExchangeService.String(),
category: path.EmailCategory.String(),
itemInfo: ItemInfo{
Exchange: &ExchangeInfo{
ItemType: ExchangeMail,
},
},
backupVersion: version.OneDrive7LocationRef - 1,
expectedErr: require.Error,
},
{
name: "Exchange Email Without LocationRef New Version Errors",
service: path.ExchangeService.String(),
category: path.EmailCategory.String(),
itemInfo: ItemInfo{
Exchange: &ExchangeInfo{
ItemType: ExchangeMail,
},
},
backupVersion: version.OneDrive7LocationRef,
expectedErr: require.Error,
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
entry := Entry{
RepoRef: fmt.Sprintf(rrString, test.service, test.category),
ItemInfo: test.itemInfo,
}
if test.hasLocRef {
entry.LocationRef = expectedDetailsLoc
}
loc, err := entry.ToLocationIDer(test.backupVersion)
test.expectedErr(t, err, clues.ToCore(err))
if err != nil {
return
}
assert.Equal(
t,
test.expectedUniqueLoc,
loc.ID().String(),
"unique location")
assert.Equal(
t,
expectedDetailsLoc,
loc.InDetails().String(),
"details location")
})
}
}