corso/src/pkg/backup/details/details_test.go
ryanfkeepers 6ed54fad9c first pass on ServiceResource tuple compliance
First pass for updating code outside of the path package
to comply with ServiceResource tuple slices.  Compliance
is still incomplete, so the build and tests
will still fail.  Changes
in this PR are focused on the easier-to-make changes, mostly
generating ServiceResources where there were
previously manual declarations of service and resource.
2023-08-10 17:55:34 -06:00

1495 lines
34 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
// shortRefEqual allows checking that OneDrive and SharePoint have their
// ShortRef updated in the returned entry.
//
// TODO(ashmrtn): Remove this when we don't need extra munging for
// OneDrive/SharePoint file name changes.
shortRefEqual assert.ComparisonAssertionFunc
}{
{
name: "Exchange Email",
entry: exchangeEntry(t, itemID, 42, ExchangeMail),
shortRefEqual: assert.Equal,
},
{
name: "OneDrive File",
entry: oneDriveishEntry(t, itemID, 42, OneDriveItem),
shortRefEqual: assert.NotEqual,
},
{
name: "SharePoint File",
entry: oneDriveishEntry(t, itemID, 42, SharePointLibrary),
shortRefEqual: assert.NotEqual,
},
{
name: "Legacy SharePoint File",
entry: func() Entry {
res := oneDriveishEntry(t, itemID, 42, SharePointLibrary)
res.SharePoint.ItemType = OneDriveItem
return res
}(),
shortRefEqual: assert.NotEqual,
},
}
for _, test := range table {
for _, updated := range []bool{false, true} {
suite.Run(fmt.Sprintf("%s Updated %v", test.name, updated), 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{}, updated, localItem.ItemInfo)
require.NoError(t, err, clues.ToCore(err))
// Clear LocationRef that's automatically populated since we passed an
// empty builder above.
localItem.LocationRef = ""
localItem.Updated = updated
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")
test.shortRefEqual(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 None Updated",
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: "One Exchange Email Updated",
entries: func() []Entry {
e := exchangeMail1
ei := *exchangeMail1.Exchange
e.Exchange = &ei
e.Updated = true
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
e.Updated = true
res = append(res, e)
}
return res
},
},
{
name: "Two Exchange Emails None Updated",
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: "Two Exchange Emails One Updated",
entries: func() []Entry {
res := []Entry{}
for i, entry := range []Entry{exchangeMail1, exchangeMail2} {
e := entry
ei := *entry.Exchange
e.Exchange = &ei
e.Updated = i == 1
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
e.Updated = true
res = append(res, e)
}
return res
},
},
{
name: "One Email And One Contact None Updated",
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 None Updated",
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 None Updated",
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 None Updated",
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.Updated, 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{},
false,
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, false, info)
require.NoError(t, err, clues.ToCore(err))
err = b.Add(dirMetaSfx, loc, false, info)
require.NoError(t, err, clues.ToCore(err))
err = b.Add(metaSfx, loc, false, 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, false, info)
require.NoError(t, err, clues.ToCore(err))
err = b.Add(dataSfx2, loc, false, info)
require.NoError(t, err, clues.ToCore(err))
err = b.Add(dirMetaSfx, loc, false, info)
require.NoError(t, err, clues.ToCore(err))
err = b.Add(metaSfx, loc, false, info)
require.NoError(t, err, clues.ToCore(err))
b.knownFolders = map[string]Entry{
"dummy": {
RepoRef: "xyz",
ShortRef: "abcd",
ParentRef: "1234",
LocationRef: "ab",
ItemRef: "cd",
Updated: false,
ItemInfo: info,
},
"dummy2": {
RepoRef: "xyz2",
ShortRef: "abcd2",
ParentRef: "12342",
LocationRef: "ab2",
ItemRef: "cd2",
Updated: false,
ItemInfo: info,
},
"dummy3": {
RepoRef: "xyz3",
ShortRef: "abcd3",
ParentRef: "12343",
LocationRef: "ab3",
ItemRef: "cd3",
Updated: false,
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,
[]path.ServiceResource{{
Service: service,
ProtectedResource: resourceOwner,
}},
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")
})
}
}