corso/src/pkg/path/path_test.go
HiteshRepo 196f4013c3 re run
2024-01-29 18:01:48 +05:30

724 lines
15 KiB
Go

package path
import (
"fmt"
"testing"
"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/tester"
)
type testData struct {
name string
input []string
expectedString string
}
// Test cases that are the same with and without escaping by the
// system-under-test.
var genericCases = []testData{
{
name: "SimplePath",
input: []string{
`this`,
`is`,
`a`,
`path`,
},
expectedString: "this/is/a/path",
},
{
name: "EmptyElement",
input: []string{
`this`,
`is`,
``,
`a`,
`path`,
},
expectedString: `this/is/a/path`,
},
{
name: "EmptyInput",
expectedString: "",
},
}
// Inputs that should be escaped.
var basicUnescapedInputs = []testData{
{
name: "EscapeSeparator",
input: []string{
`this`,
`is/a`,
`path`,
},
expectedString: `this/is\/a/path`,
},
{
name: "EscapeEscapeChar",
input: []string{
`this`,
`is\`,
`a`,
`path`,
},
expectedString: `this/is\\/a/path`,
},
{
name: "EscapeEscapeAndSeparator",
input: []string{
`this`,
`is\/a`,
`path`,
},
expectedString: `this/is\\\/a/path`,
},
{
name: "SeparatorAtEndOfElement",
input: []string{
`this`,
`is/`,
`a`,
`path`,
},
expectedString: `this/is\//a/path`,
},
{
name: "SeparatorAtEndOfPath",
input: []string{
`this`,
`is`,
`a`,
`path/`,
},
expectedString: `this/is/a/path\/`,
},
}
// Inputs that are already escaped.
var basicEscapedInputs = []testData{
{
name: "EscapedSeparator",
input: []string{
`this`,
`is\/a`,
`path`,
},
expectedString: `this/is\/a/path`,
},
{
name: "EscapedEscapeChar",
input: []string{
`this`,
`is\\`,
`a`,
`path`,
},
expectedString: `this/is\\/a/path`,
},
{
name: "EscapedEscapeAndSeparator",
input: []string{
`this`,
`is\\\/a`,
`path`,
},
expectedString: `this/is\\\/a/path`,
},
{
name: "EscapedSeparatorAtEndOfElement",
input: []string{
`this`,
`is\/`,
`a`,
`path`,
},
expectedString: `this/is\//a/path`,
},
{
name: "EscapedSeparatorAtEndOfPath",
input: []string{
`this`,
`is`,
`a`,
`path\/`,
},
expectedString: `this/is/a/path\/`,
},
{
name: "ElementOfSeparator",
input: []string{
`this`,
`is`,
`/`,
`a`,
`path`,
},
expectedString: `this/is/a/path`,
},
{
name: "TrailingElementSeparator",
input: []string{
`this`,
`is`,
`a/`,
`path`,
},
expectedString: `this/is/a/path`,
},
{
name: "MultipleTrailingElementSeparator",
input: []string{
`this`,
`is`,
`a///`,
`path`,
},
expectedString: `this/is/a/path`,
},
{
name: "TrailingSeparatorAtEnd",
input: []string{
`this`,
`is`,
`a`,
`path/`,
},
expectedString: `this/is/a/path`,
},
{
name: "MultipleTrailingSeparatorAtEnd",
input: []string{
`this`,
`is`,
`a`,
`path///`,
},
expectedString: `this/is/a/path`,
},
{
name: "TrailingSeparatorWithEmptyElementAtEnd",
input: []string{
`this`,
`is`,
`a`,
`path/`,
``,
},
expectedString: `this/is/a/path`,
},
}
type PathUnitSuite struct {
tester.Suite
}
func TestPathUnitSuite(t *testing.T) {
suite.Run(t, &PathUnitSuite{Suite: tester.NewUnitSuite(t)})
}
// set the clues hashing to mask for the span of this suite
func (suite *PathUnitSuite) SetupSuite() {
clues.SetHasher(clues.HashCfg{HashAlg: clues.Flatmask})
}
// revert clues hashing to plaintext for all other tests
func (suite *PathUnitSuite) TeardownSuite() {
clues.SetHasher(clues.NoHash())
}
func (suite *PathUnitSuite) TestFromDataLayerPathErrors() {
table := []struct {
name string
escapedPath string
}{
{
name: "TooFewElements",
escapedPath: `some/short/path`,
},
{
name: "TooFewElementsEmptyElement",
escapedPath: `tenant/exchange//email/folder`,
},
{
name: "BadEscapeSequence",
escapedPath: `tenant/exchange/user/email/folder\a`,
},
{
name: "TrailingEscapeCharacter",
escapedPath: `tenant/exchange/user/email/folder\`,
},
{
name: "UnknownService",
escapedPath: `tenant/badService/user/email/folder`,
},
{
name: "UnknownCategory",
escapedPath: `tenant/exchange/user/badCategory/folder`,
},
{
name: "NoFolderOrItem",
escapedPath: `tenant/exchange/user/email`,
},
{
name: "EmptyPath",
escapedPath: ``,
},
{
name: "JustPathSeparator",
escapedPath: `/`,
},
{
name: "JustMultiplePathSeparators",
escapedPath: `//`,
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
_, err := FromDataLayerPath(test.escapedPath, false)
assert.Error(t, err)
})
}
}
func (suite *PathUnitSuite) TestFromDataLayerPath() {
const (
testTenant = "tenant"
testResource = "resource"
testElement1 = "folder/"
testElementTrimmed = "folder"
testElement2 = "folder2"
testElement3 = "other"
)
isItem := []struct {
name string
isItem bool
}{
{
name: "Folder",
isItem: false,
},
{
name: "Item",
isItem: true,
},
}
table := []struct {
name string
// Should have placeholders of '%s' for service and category.
unescapedPath string
// Expected result for Folder() if path is marked as a folder.
expectedFolder string
// Expected result for Item() if path is marked as an item.
// Expected result for Split(Folder()) if path is marked as a folder.
expectedSplit []string
expectedItem string
// Expected result for Folder() if path is marked as an item.
expectedItemFolder string
// Expected result for Split(Folder()) if path is marked as an item.
expectedItemSplit []string
}{
{
name: "BasicPath",
unescapedPath: fmt.Sprintf(
"%s/%%s/%s/%%s/%s/%s/%s",
testTenant,
testResource,
testElement1,
testElement2,
testElement3),
expectedFolder: fmt.Sprintf(
"%s/%s/%s",
testElementTrimmed,
testElement2,
testElement3),
expectedSplit: []string{
testElementTrimmed,
testElement2,
testElement3,
},
expectedItem: testElement3,
expectedItemFolder: fmt.Sprintf(
"%s/%s",
testElementTrimmed,
testElement2),
expectedItemSplit: []string{
testElementTrimmed,
testElement2,
},
},
{
name: "PathWithEmptyElements",
unescapedPath: fmt.Sprintf(
"/%s//%%s//%s//%%s//%s///%s//%s//",
testTenant,
testResource,
testElementTrimmed,
testElement2,
testElement3),
expectedFolder: fmt.Sprintf(
"%s/%s/%s",
testElementTrimmed,
testElement2,
testElement3),
expectedSplit: []string{
testElementTrimmed,
testElement2,
testElement3,
},
expectedItem: testElement3,
expectedItemFolder: fmt.Sprintf(
"%s/%s",
testElementTrimmed,
testElement2),
expectedItemSplit: []string{
testElementTrimmed,
testElement2,
},
},
}
for service, cats := range serviceCategories {
for cat := range cats {
for _, item := range isItem {
suite.Run(fmt.Sprintf("%s-%s-%s", service, cat, item.name), func() {
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
testPath := fmt.Sprintf(test.unescapedPath, service, cat)
p, err := FromDataLayerPath(testPath, item.isItem)
require.NoError(t, err, clues.ToCore(err))
assert.Equal(t, service, p.Service(), "service")
assert.Equal(t, cat, p.Category(), "category")
assert.Equal(t, testTenant, p.Tenant(), "tenant")
assert.Equal(t, testResource, p.ProtectedResource(), "resource owner")
fld := p.Folder(false)
escfld := p.Folder(true)
if item.isItem {
assert.Equal(t, test.expectedItemFolder, fld, "item folder")
assert.Equal(t, test.expectedItemSplit, Split(fld), "item split")
assert.Equal(t, test.expectedItemFolder, escfld, "escaped item folder")
assert.Equal(t, test.expectedItemSplit, Split(escfld), "escaped item split")
assert.Equal(t, test.expectedItem, p.Item(), "item")
} else {
assert.Equal(t, test.expectedFolder, fld, "dir folder")
assert.Equal(t, test.expectedSplit, Split(fld), "dir split")
assert.Equal(t, test.expectedFolder, escfld, "escaped dir folder")
assert.Equal(t, test.expectedSplit, Split(escfld), "escaped dir split")
}
})
}
})
}
}
}
}
func (suite *PathUnitSuite) TestPrefixOrPathFromDataLayerPath() {
t := suite.T()
input := fmt.Sprintf(
"%s/%s/%s/%s",
"tenant",
ExchangeService.String(),
"user",
EmailCategory.String())
// Check that we can make a valid prefix path.
p, err := PrefixOrPathFromDataLayerPath(input, false)
assert.NoError(t, err, clues.ToCore(err))
assert.Equal(t, input, p.String())
// Check we can't make a regular path with the same input since it doesn't
// have enough segments.
_, err = FromDataLayerPath(input, false)
assert.Error(t, err)
}
func (suite *PathUnitSuite) TestBuildOrPrefix() {
table := []struct {
name string
service ServiceType
category CategoryType
tenant string
owner string
folders []string
expect string
expectErr require.ErrorAssertionFunc
}{
{
name: "ok, empty elements",
service: ExchangeService,
category: ContactsCategory,
tenant: "t",
owner: "ro",
folders: []string{},
expect: join([]string{"t", ExchangeService.String(), "ro", ContactsCategory.String()}),
expectErr: require.NoError,
},
{
name: "ok, non-empty elements",
service: ExchangeService,
category: ContactsCategory,
tenant: "t",
owner: "ro",
folders: []string{"foo"},
expect: join([]string{"t", ExchangeService.String(), "ro", ContactsCategory.String(), "foo"}),
expectErr: require.NoError,
},
{
name: "bad category",
service: ExchangeService,
category: FilesCategory,
tenant: "t",
owner: "ro",
expectErr: require.Error,
},
{
name: "bad tenant",
service: ExchangeService,
category: ContactsCategory,
tenant: "",
owner: "ro",
expectErr: require.Error,
},
{
name: "bad owner",
service: ExchangeService,
category: ContactsCategory,
tenant: "t",
owner: "",
expectErr: require.Error,
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
r, err := BuildOrPrefix(test.tenant, test.owner, test.service, test.category, false, test.folders...)
test.expectErr(t, err, clues.ToCore(err))
if r == nil {
return
}
assert.Equal(t, test.expect, r.String())
assert.NotPanics(t, func() {
r.Folders()
r.Item()
}, "runs Folders() and Item()")
})
}
}
func (suite *PathUnitSuite) TestBuildPrefix() {
table := []struct {
name string
service ServiceType
category CategoryType
tenant string
owner string
expect string
expectErr require.ErrorAssertionFunc
}{
{
name: "ok",
service: ExchangeService,
category: ContactsCategory,
tenant: "t",
owner: "ro",
expect: join([]string{"t", ExchangeService.String(), "ro", ContactsCategory.String()}),
expectErr: require.NoError,
},
{
name: "bad category",
service: ExchangeService,
category: FilesCategory,
tenant: "t",
owner: "ro",
expectErr: require.Error,
},
{
name: "bad tenant",
service: ExchangeService,
category: ContactsCategory,
tenant: "",
owner: "ro",
expectErr: require.Error,
},
{
name: "bad owner",
service: ExchangeService,
category: ContactsCategory,
tenant: "t",
owner: "",
expectErr: require.Error,
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
r, err := BuildPrefix(test.tenant, test.owner, test.service, test.category)
test.expectErr(t, err, clues.ToCore(err))
if r == nil {
return
}
assert.Equal(t, test.expect, r.String())
assert.NotPanics(t, func() {
r.Folders()
r.Item()
}, "runs Folders() and Item()")
})
}
}
func (suite *PathUnitSuite) TestBuildRestorePaths() {
type args struct {
tenantID string
protectedResource string
service ServiceType
category CategoryType
fp []string
}
tests := []struct {
name string
args args
restorePath string
storagePath string
expectErr require.ErrorAssertionFunc
}{
{
name: "single",
args: args{
tenantID: "tenant",
protectedResource: "protectedResource",
service: GroupsService,
category: LibrariesCategory,
fp: []string{"a"},
},
restorePath: "tenant/groupsMetadata/protectedResource/libraries",
storagePath: "tenant/groupsMetadata/protectedResource/libraries/a",
expectErr: require.NoError,
},
{
name: "multi",
args: args{
tenantID: "tenant",
protectedResource: "protectedResource",
service: GroupsService,
category: LibrariesCategory,
fp: []string{"a", "b"},
},
restorePath: "tenant/groupsMetadata/protectedResource/libraries/a",
storagePath: "tenant/groupsMetadata/protectedResource/libraries/a/b",
expectErr: require.NoError,
},
}
for _, test := range tests {
suite.Run(test.name, func() {
t := suite.T()
r, err := BuildMetadata(
test.args.tenantID,
test.args.protectedResource,
test.args.service,
test.args.category,
true,
test.args.fp...)
test.expectErr(t, err, clues.ToCore(err))
rdir, err := r.Dir()
require.NoError(t, err, clues.ToCore(err))
assert.Equal(t, test.restorePath, rdir.String(), "restore path")
assert.Equal(t, test.storagePath, r.String(), "storage path")
})
}
}
func (suite *PathUnitSuite) TestArePathsEquivalent() {
tests := []struct {
name string
path1 string
path2 string
equivalent bool
}{
// Test cases with equivalent paths
{
name: "same linux normal path",
path1: "/path/to/dir",
path2: "/path/to/dir",
equivalent: true,
},
{
name: "same windows normal path",
path1: "C:\\Users\\User\\Documents",
path2: "C:\\Users\\User\\Documents",
equivalent: true,
},
{
name: "same linux extra trailing slash path",
path1: "/path/with/trailing/slash/",
path2: "/path/with/trailing/slash",
equivalent: true,
},
{
name: "same linux relative path",
path1: "relative/path",
path2: "./relative/path",
equivalent: true,
},
// Test cases with non-equivalent paths
{
name: "different linux normal path",
path1: "/path/to/dir",
path2: "/path/to/different/dir",
equivalent: false,
},
{
name: "different windows normal path",
path1: "C:\\Users\\User\\Documents",
path2: "D:\\Users\\User\\Documents",
equivalent: false,
},
{
name: "different linux extra trailing slash path",
path1: "/path/with/trailing/slash/",
path2: "/different/path/with/trailing/slash",
equivalent: false,
},
{
name: "different linux relative path",
path1: "relative/path",
path2: "../relative/path",
equivalent: false,
},
}
for _, test := range tests {
t := suite.T()
suite.Run(test.name, func() {
result := ArePathsEquivalent(test.path1, test.path2)
if result != test.equivalent {
t.Errorf("Paths %s and %s are not equivalent as expected.", test.path1, test.path2)
}
})
}
}