add indeets test helper, implement in exchange op (#3295)
Adds a helper for building expected details entries and checking them after a backup. Implements the helper in the exchange backup tests in operations/backup integration. Will follow with a onedrive implementation. --- #### Does this PR need a docs update or release note? - [x] ⛔ No #### Type of change - [x] 🤖 Supportability/Tests #### Issue(s) * #3240 #### Test Plan - [x] 💪 Manual - [x] ⚡ Unit test - [x] 💚 E2E
This commit is contained in:
parent
c0725b9cf9
commit
c5b388a721
@ -112,7 +112,7 @@ func runDisplayM365JSON(
|
||||
creds account.M365Config,
|
||||
user, itemID string,
|
||||
) error {
|
||||
drive, err := api.GetDriveByID(ctx, srv, user)
|
||||
drive, err := api.GetUsersDrive(ctx, srv, user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -441,10 +441,7 @@ func restoreCollection(
|
||||
continue
|
||||
}
|
||||
|
||||
locationRef := &path.Builder{}
|
||||
if category == path.ContactsCategory {
|
||||
locationRef = locationRef.Append(itemPath.Folders()...)
|
||||
}
|
||||
locationRef := path.Builder{}.Append(itemPath.Folders()...)
|
||||
|
||||
err = deets.Add(
|
||||
itemPath,
|
||||
|
||||
@ -336,18 +336,33 @@ func GetItemPermission(
|
||||
return perm, nil
|
||||
}
|
||||
|
||||
func GetDriveByID(
|
||||
func GetUsersDrive(
|
||||
ctx context.Context,
|
||||
srv graph.Servicer,
|
||||
userID string,
|
||||
user string,
|
||||
) (models.Driveable, error) {
|
||||
//revive:enable:context-as-argument
|
||||
d, err := srv.Client().
|
||||
UsersById(userID).
|
||||
UsersById(user).
|
||||
Drive().
|
||||
Get(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, graph.Wrap(ctx, err, "getting drive")
|
||||
return nil, graph.Wrap(ctx, err, "getting user's drive")
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func GetSitesDefaultDrive(
|
||||
ctx context.Context,
|
||||
srv graph.Servicer,
|
||||
site string,
|
||||
) (models.Driveable, error) {
|
||||
d, err := srv.Client().
|
||||
SitesById(site).
|
||||
Drive().
|
||||
Get(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, graph.Wrap(ctx, err, "getting site's drive")
|
||||
}
|
||||
|
||||
return d, nil
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
368
src/pkg/backup/details/testdata/in_deets.go
vendored
Normal file
368
src/pkg/backup/details/testdata/in_deets.go
vendored
Normal file
@ -0,0 +1,368 @@
|
||||
package testdata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/alcionai/clues"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
"github.com/alcionai/corso/src/internal/kopia"
|
||||
"github.com/alcionai/corso/src/internal/model"
|
||||
"github.com/alcionai/corso/src/internal/streamstore"
|
||||
"github.com/alcionai/corso/src/pkg/backup"
|
||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||
"github.com/alcionai/corso/src/pkg/fault"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// location set handling
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
var exists = struct{}{}
|
||||
|
||||
type locSet struct {
|
||||
// map [locationRef] map [itemRef] {}
|
||||
// refs may be either the canonical ent refs, or something else,
|
||||
// so long as they are consistent for the test in question
|
||||
Locations map[string]map[string]struct{}
|
||||
Deleted map[string]map[string]struct{}
|
||||
}
|
||||
|
||||
func newLocSet() *locSet {
|
||||
return &locSet{
|
||||
Locations: map[string]map[string]struct{}{},
|
||||
Deleted: map[string]map[string]struct{}{},
|
||||
}
|
||||
}
|
||||
|
||||
func (ls *locSet) AddItem(locationRef, itemRef string) {
|
||||
ls.AddLocation(locationRef)
|
||||
|
||||
ls.Locations[locationRef][itemRef] = exists
|
||||
delete(ls.Deleted[locationRef], itemRef)
|
||||
}
|
||||
|
||||
func (ls *locSet) RemoveItem(locationRef, itemRef string) {
|
||||
delete(ls.Locations[locationRef], itemRef)
|
||||
|
||||
if _, ok := ls.Deleted[locationRef]; !ok {
|
||||
ls.Deleted[locationRef] = map[string]struct{}{}
|
||||
}
|
||||
|
||||
ls.Deleted[locationRef][itemRef] = exists
|
||||
}
|
||||
|
||||
func (ls *locSet) MoveItem(fromLocation, toLocation, ir string) {
|
||||
ls.RemoveItem(fromLocation, ir)
|
||||
ls.AddItem(toLocation, ir)
|
||||
}
|
||||
|
||||
func (ls *locSet) AddLocation(locationRef string) {
|
||||
if _, ok := ls.Locations[locationRef]; !ok {
|
||||
ls.Locations[locationRef] = map[string]struct{}{}
|
||||
}
|
||||
// don't purge previously deleted items, or child locations.
|
||||
// Assumption is that their itemRef is unique, and still deleted.
|
||||
delete(ls.Deleted, locationRef)
|
||||
}
|
||||
|
||||
func (ls *locSet) RemoveLocation(locationRef string) {
|
||||
ss := ls.Subset(locationRef)
|
||||
|
||||
for lr := range ss.Locations {
|
||||
items := ls.Locations[lr]
|
||||
|
||||
delete(ls.Locations, lr)
|
||||
|
||||
if _, ok := ls.Deleted[lr]; !ok {
|
||||
ls.Deleted[lr] = map[string]struct{}{}
|
||||
}
|
||||
|
||||
for ir := range items {
|
||||
ls.Deleted[lr][ir] = exists
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MoveLocation takes the LAST elemet in the fromLocation (and all)
|
||||
// children matching the prefix, and relocates it as a child of toLocation.
|
||||
// ex: MoveLocation("/a/b/c", "/d") will move all entries with the prefix
|
||||
// "/a/b/c" into "/d/c". This also deletes all "/a/b/c" entries and children.
|
||||
// assumes item IDs don't change across the migration. If item IDs do change,
|
||||
// that difference will need to be handled manually by the caller.
|
||||
// returns the base folder's new location (ex: /d/c)
|
||||
func (ls *locSet) MoveLocation(fromLocation, toLocation string) string {
|
||||
fromBuilder := path.Builder{}.Append(path.Split(fromLocation)...)
|
||||
toBuilder := path.Builder{}.Append(path.Split(toLocation)...).Append(fromBuilder.LastElem())
|
||||
|
||||
ls.RenameLocation(fromBuilder.String(), toBuilder.String())
|
||||
|
||||
return toBuilder.String()
|
||||
}
|
||||
|
||||
func (ls *locSet) RenameLocation(fromLocation, toLocation string) {
|
||||
ss := ls.Subset(fromLocation)
|
||||
fromBuilder := path.Builder{}.Append(path.Split(fromLocation)...)
|
||||
toBuilder := path.Builder{}.Append(path.Split(toLocation)...)
|
||||
|
||||
for lr, items := range ss.Locations {
|
||||
lrBuilder := path.Builder{}.Append(path.Split(lr)...)
|
||||
lrBuilder.UpdateParent(fromBuilder, toBuilder)
|
||||
|
||||
newLoc := lrBuilder.String()
|
||||
|
||||
for ir := range items {
|
||||
ls.RemoveItem(lr, ir)
|
||||
ls.AddItem(newLoc, ir)
|
||||
}
|
||||
|
||||
ls.RemoveLocation(lr)
|
||||
ls.AddLocation(newLoc)
|
||||
}
|
||||
}
|
||||
|
||||
// Subset produces a new locSet containing only Items and Locations
|
||||
// whose location matches the locationPfx
|
||||
func (ls *locSet) Subset(locationPfx string) *locSet {
|
||||
ss := newLocSet()
|
||||
|
||||
for lr, items := range ls.Locations {
|
||||
if strings.HasPrefix(lr, locationPfx) {
|
||||
ss.AddLocation(lr)
|
||||
|
||||
for ir := range items {
|
||||
ss.AddItem(lr, ir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ss
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// The goal of InDeets is to provide a struct and interface which allows
|
||||
// tests to predict not just the elements within a set of details entries,
|
||||
// but also their changes (relocation, renaming, etc) in a way that consolidates
|
||||
// building an "expected set" of details entries that can be compared against
|
||||
// the details results after a backup.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// InDeets is a helper for comparing details state in tests
|
||||
// across backup instances.
|
||||
type InDeets struct {
|
||||
// only: tenantID/service/resourceOwnerID
|
||||
RRPrefix string
|
||||
// map of container setting the uniqueness boundary for location
|
||||
// ref entries (eg, data type like email, contacts, etc, or
|
||||
// drive id) to the unique entries in that set.
|
||||
Sets map[string]*locSet
|
||||
}
|
||||
|
||||
func NewInDeets(repoRefPrefix string) *InDeets {
|
||||
return &InDeets{
|
||||
RRPrefix: repoRefPrefix,
|
||||
Sets: map[string]*locSet{},
|
||||
}
|
||||
}
|
||||
|
||||
func (id *InDeets) getSet(set string) *locSet {
|
||||
s, ok := id.Sets[set]
|
||||
if ok {
|
||||
return s
|
||||
}
|
||||
|
||||
return newLocSet()
|
||||
}
|
||||
|
||||
func (id *InDeets) AddAll(deets details.Details, ws whatSet) {
|
||||
if id.Sets == nil {
|
||||
id.Sets = map[string]*locSet{}
|
||||
}
|
||||
|
||||
for _, ent := range deets.Entries {
|
||||
set, err := ws(ent)
|
||||
if err != nil {
|
||||
set = err.Error()
|
||||
}
|
||||
|
||||
dir := ent.LocationRef
|
||||
|
||||
if ent.Folder != nil {
|
||||
dir = dir + ent.Folder.DisplayName
|
||||
id.AddLocation(set, dir)
|
||||
} else {
|
||||
id.AddItem(set, ent.LocationRef, ent.ItemRef)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (id *InDeets) AddItem(set, locationRef, itemRef string) {
|
||||
id.getSet(set).AddItem(locationRef, itemRef)
|
||||
}
|
||||
|
||||
func (id *InDeets) RemoveItem(set, locationRef, itemRef string) {
|
||||
id.getSet(set).RemoveItem(locationRef, itemRef)
|
||||
}
|
||||
|
||||
func (id *InDeets) MoveItem(set, fromLocation, toLocation, ir string) {
|
||||
id.getSet(set).MoveItem(fromLocation, toLocation, ir)
|
||||
}
|
||||
|
||||
func (id *InDeets) AddLocation(set, locationRef string) {
|
||||
id.getSet(set).AddLocation(locationRef)
|
||||
}
|
||||
|
||||
// RemoveLocation removes the provided location, and all children
|
||||
// of that location.
|
||||
func (id *InDeets) RemoveLocation(set, locationRef string) {
|
||||
id.getSet(set).RemoveLocation(locationRef)
|
||||
}
|
||||
|
||||
// MoveLocation takes the LAST elemet in the fromLocation (and all)
|
||||
// children matching the prefix, and relocates it as a child of toLocation.
|
||||
// ex: MoveLocation("/a/b/c", "/d") will move all entries with the prefix
|
||||
// "/a/b/c" into "/d/c". This also deletes all "/a/b/c" entries and children.
|
||||
// assumes item IDs don't change across the migration. If item IDs do change,
|
||||
// that difference will need to be handled manually by the caller.
|
||||
// returns the base folder's new location (ex: /d/c)
|
||||
func (id *InDeets) MoveLocation(set, fromLocation, toLocation string) string {
|
||||
return id.getSet(set).MoveLocation(fromLocation, toLocation)
|
||||
}
|
||||
|
||||
func (id *InDeets) RenameLocation(set, fromLocation, toLocation string) {
|
||||
id.getSet(set).RenameLocation(fromLocation, toLocation)
|
||||
}
|
||||
|
||||
// Subset produces a new locSet containing only Items and Locations
|
||||
// whose location matches the locationPfx
|
||||
func (id *InDeets) Subset(set, locationPfx string) *locSet {
|
||||
return id.getSet(set).Subset(locationPfx)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// whatSet helpers for extracting a set identifier from an arbitrary repoRef
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
type whatSet func(details.Entry) (string, error)
|
||||
|
||||
// common whatSet parser that extracts the service category from
|
||||
// a repoRef.
|
||||
func CategoryFromRepoRef(ent details.Entry) (string, error) {
|
||||
p, err := path.FromDataLayerPath(ent.RepoRef, false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return p.Category().String(), nil
|
||||
}
|
||||
|
||||
// common whatSet parser that extracts the driveID from a repoRef.
|
||||
func DriveIDFromRepoRef(ent details.Entry) (string, error) {
|
||||
p, err := path.FromDataLayerPath(ent.RepoRef, false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
odp, err := path.ToDrivePath(p)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return odp.DriveID, nil
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// helpers and comparators
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func CheckBackupDetails(
|
||||
t *testing.T,
|
||||
ctx context.Context, //revive:disable-line:context-as-argument
|
||||
backupID model.StableID,
|
||||
ws whatSet,
|
||||
ms *kopia.ModelStore,
|
||||
ssr streamstore.Reader,
|
||||
expect *InDeets,
|
||||
// standard check is assert.Subset due to issues of external data cross-
|
||||
// pollination. This should be true if the backup contains a unique directory
|
||||
// of data.
|
||||
mustEqualFolders bool,
|
||||
) {
|
||||
deets, result := GetDeetsInBackup(t, ctx, backupID, "", "", path.UnknownService, ws, ms, ssr)
|
||||
|
||||
t.Log("details entries in result")
|
||||
|
||||
for _, ent := range deets.Entries {
|
||||
if ent.Folder == nil {
|
||||
t.Log(ent.LocationRef)
|
||||
t.Log(ent.ItemRef)
|
||||
}
|
||||
|
||||
assert.Truef(
|
||||
t,
|
||||
strings.HasPrefix(ent.RepoRef, expect.RRPrefix),
|
||||
"all details should begin with the expected prefix\nwant: %s\ngot: %s",
|
||||
expect.RRPrefix, ent.RepoRef)
|
||||
}
|
||||
|
||||
for set := range expect.Sets {
|
||||
check := assert.Subsetf
|
||||
|
||||
if mustEqualFolders {
|
||||
check = assert.ElementsMatchf
|
||||
}
|
||||
|
||||
check(
|
||||
t,
|
||||
maps.Keys(result.Sets[set].Locations),
|
||||
maps.Keys(expect.Sets[set].Locations),
|
||||
"results in %s missing expected location", set)
|
||||
|
||||
for lr, items := range expect.Sets[set].Deleted {
|
||||
_, ok := result.Sets[set].Locations[lr]
|
||||
assert.Falsef(t, ok, "deleted location in %s found in result: %s", set, lr)
|
||||
|
||||
for ir := range items {
|
||||
_, ok := result.Sets[set].Locations[lr][ir]
|
||||
assert.Falsef(t, ok, "deleted item in %s found in result: %s", set, lr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func GetDeetsInBackup(
|
||||
t *testing.T,
|
||||
ctx context.Context, //revive:disable-line:context-as-argument
|
||||
backupID model.StableID,
|
||||
tid, resourceOwner string,
|
||||
service path.ServiceType,
|
||||
ws whatSet,
|
||||
ms *kopia.ModelStore,
|
||||
ssr streamstore.Reader,
|
||||
) (details.Details, *InDeets) {
|
||||
bup := backup.Backup{}
|
||||
|
||||
err := ms.Get(ctx, model.BackupSchema, backupID, &bup)
|
||||
require.NoError(t, err, clues.ToCore(err))
|
||||
|
||||
ssid := bup.StreamStoreID
|
||||
require.NotEmpty(t, ssid, "stream store ID")
|
||||
|
||||
var deets details.Details
|
||||
err = ssr.Read(
|
||||
ctx,
|
||||
ssid,
|
||||
streamstore.DetailsReader(details.UnmarshalTo(&deets)),
|
||||
fault.New(true))
|
||||
require.NoError(t, err, clues.ToCore(err))
|
||||
|
||||
id := NewInDeets(path.Builder{}.Append(tid, service.String(), resourceOwner).String())
|
||||
id.AddAll(deets, ws)
|
||||
|
||||
return deets, id
|
||||
}
|
||||
445
src/pkg/backup/details/testdata/in_deets_test.go
vendored
Normal file
445
src/pkg/backup/details/testdata/in_deets_test.go
vendored
Normal file
@ -0,0 +1,445 @@
|
||||
package testdata
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
"github.com/alcionai/corso/src/internal/tester"
|
||||
)
|
||||
|
||||
type LocSetUnitSuite struct {
|
||||
tester.Suite
|
||||
}
|
||||
|
||||
func TestLocSetUnitSuite(t *testing.T) {
|
||||
suite.Run(t, &LocSetUnitSuite{Suite: tester.NewUnitSuite(t)})
|
||||
}
|
||||
|
||||
const (
|
||||
l1 = "lr_1"
|
||||
l2 = "lr_2"
|
||||
l13 = "lr_1/lr_3"
|
||||
l14 = "lr_1/lr_4"
|
||||
i1 = "ir_1"
|
||||
i2 = "ir_2"
|
||||
i3 = "ir_3"
|
||||
i4 = "ir_4"
|
||||
)
|
||||
|
||||
func (suite *LocSetUnitSuite) TestAdd() {
|
||||
t := suite.T()
|
||||
|
||||
ls := newLocSet()
|
||||
|
||||
ls.AddItem(l1, i1)
|
||||
ls.AddLocation(l2)
|
||||
|
||||
assert.ElementsMatch(t, []string{l1, l2}, maps.Keys(ls.Locations))
|
||||
assert.ElementsMatch(t, []string{i1}, maps.Keys(ls.Locations[l1]))
|
||||
assert.Empty(t, maps.Keys(ls.Locations[l2]))
|
||||
assert.Empty(t, maps.Keys(ls.Locations[l13]))
|
||||
}
|
||||
|
||||
func (suite *LocSetUnitSuite) TestRemove() {
|
||||
t := suite.T()
|
||||
|
||||
ls := newLocSet()
|
||||
|
||||
ls.AddItem(l1, i1)
|
||||
ls.AddItem(l1, i2)
|
||||
ls.AddLocation(l13)
|
||||
ls.AddItem(l14, i3)
|
||||
ls.AddItem(l14, i4)
|
||||
|
||||
assert.ElementsMatch(t, []string{l1, l13, l14}, maps.Keys(ls.Locations))
|
||||
assert.ElementsMatch(t, []string{i1, i2}, maps.Keys(ls.Locations[l1]))
|
||||
assert.Empty(t, maps.Keys(ls.Locations[l13]))
|
||||
assert.ElementsMatch(t, []string{i3, i4}, maps.Keys(ls.Locations[l14]))
|
||||
|
||||
// nop removal
|
||||
ls.RemoveItem(l2, i1)
|
||||
assert.ElementsMatch(t, []string{i1, i2}, maps.Keys(ls.Locations[l1]))
|
||||
|
||||
// item removal
|
||||
ls.RemoveItem(l1, i2)
|
||||
assert.ElementsMatch(t, []string{i1}, maps.Keys(ls.Locations[l1]))
|
||||
|
||||
// nop location removal
|
||||
ls.RemoveLocation(l2)
|
||||
assert.ElementsMatch(t, []string{l1, l13, l14}, maps.Keys(ls.Locations))
|
||||
|
||||
// non-cascading location removal
|
||||
ls.RemoveLocation(l13)
|
||||
assert.ElementsMatch(t, []string{l1, l14}, maps.Keys(ls.Locations))
|
||||
assert.ElementsMatch(t, []string{i1}, maps.Keys(ls.Locations[l1]))
|
||||
assert.ElementsMatch(t, []string{i3, i4}, maps.Keys(ls.Locations[l14]))
|
||||
|
||||
// cascading location removal
|
||||
ls.RemoveLocation(l1)
|
||||
assert.Empty(t, maps.Keys(ls.Locations))
|
||||
assert.Empty(t, maps.Keys(ls.Locations[l1]))
|
||||
assert.Empty(t, maps.Keys(ls.Locations[l13]))
|
||||
assert.Empty(t, maps.Keys(ls.Locations[l14]))
|
||||
}
|
||||
|
||||
func (suite *LocSetUnitSuite) TestSubset() {
|
||||
ls := newLocSet()
|
||||
|
||||
ls.AddItem(l1, i1)
|
||||
ls.AddItem(l1, i2)
|
||||
ls.AddLocation(l13)
|
||||
ls.AddItem(l14, i3)
|
||||
ls.AddItem(l14, i4)
|
||||
|
||||
table := []struct {
|
||||
name string
|
||||
locPfx string
|
||||
expect func(*testing.T, *locSet)
|
||||
}{
|
||||
{
|
||||
name: "nop",
|
||||
locPfx: l2,
|
||||
expect: func(t *testing.T, ss *locSet) {
|
||||
assert.Empty(t, maps.Keys(ss.Locations))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no items",
|
||||
locPfx: l13,
|
||||
expect: func(t *testing.T, ss *locSet) {
|
||||
assert.ElementsMatch(t, []string{l13}, maps.Keys(ss.Locations))
|
||||
assert.Empty(t, maps.Keys(ss.Locations[l13]))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "non-cascading",
|
||||
locPfx: l14,
|
||||
expect: func(t *testing.T, ss *locSet) {
|
||||
assert.ElementsMatch(t, []string{l14}, maps.Keys(ss.Locations))
|
||||
assert.ElementsMatch(t, []string{i3, i4}, maps.Keys(ss.Locations[l14]))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cascading",
|
||||
locPfx: l1,
|
||||
expect: func(t *testing.T, ss *locSet) {
|
||||
assert.ElementsMatch(t, []string{l1, l13, l14}, maps.Keys(ss.Locations))
|
||||
assert.ElementsMatch(t, []string{i1, i2}, maps.Keys(ss.Locations[l1]))
|
||||
assert.ElementsMatch(t, []string{i3, i4}, maps.Keys(ss.Locations[l14]))
|
||||
assert.Empty(t, maps.Keys(ss.Locations[l13]))
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range table {
|
||||
suite.Run(test.name, func() {
|
||||
t := suite.T()
|
||||
test.expect(t, ls.Subset(test.locPfx))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *LocSetUnitSuite) TestRename() {
|
||||
t := suite.T()
|
||||
|
||||
makeSet := func() *locSet {
|
||||
ls := newLocSet()
|
||||
|
||||
ls.AddItem(l1, i1)
|
||||
ls.AddItem(l1, i2)
|
||||
ls.AddLocation(l13)
|
||||
ls.AddItem(l14, i3)
|
||||
ls.AddItem(l14, i4)
|
||||
|
||||
return ls
|
||||
}
|
||||
|
||||
ts := makeSet()
|
||||
assert.ElementsMatch(t, []string{l1, l13, l14}, maps.Keys(ts.Locations))
|
||||
assert.ElementsMatch(t, []string{i1, i2}, maps.Keys(ts.Locations[l1]))
|
||||
assert.Empty(t, maps.Keys(ts.Locations[l13]))
|
||||
assert.ElementsMatch(t, []string{i3, i4}, maps.Keys(ts.Locations[l14]))
|
||||
|
||||
table := []struct {
|
||||
name string
|
||||
from string
|
||||
to string
|
||||
expect func(*testing.T, *locSet)
|
||||
}{
|
||||
{
|
||||
name: "nop",
|
||||
from: l2,
|
||||
to: "foo",
|
||||
expect: func(t *testing.T, ls *locSet) {
|
||||
assert.ElementsMatch(t, []string{l1, l13, l14}, maps.Keys(ls.Locations))
|
||||
assert.Empty(t, maps.Keys(ls.Locations[l2]))
|
||||
assert.Empty(t, maps.Keys(ls.Locations["foo"]))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no items",
|
||||
from: l13,
|
||||
to: "foo",
|
||||
expect: func(t *testing.T, ls *locSet) {
|
||||
assert.ElementsMatch(t, []string{l1, "foo", l14}, maps.Keys(ls.Locations))
|
||||
assert.Empty(t, maps.Keys(ls.Locations[l13]))
|
||||
assert.Empty(t, maps.Keys(ls.Locations["foo"]))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with items",
|
||||
from: l14,
|
||||
to: "foo",
|
||||
expect: func(t *testing.T, ls *locSet) {
|
||||
assert.ElementsMatch(t, []string{l1, l13, "foo"}, maps.Keys(ls.Locations))
|
||||
assert.Empty(t, maps.Keys(ls.Locations[l14]))
|
||||
assert.ElementsMatch(t, []string{i3, i4}, maps.Keys(ls.Locations["foo"]))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cascading locations",
|
||||
from: l1,
|
||||
to: "foo",
|
||||
expect: func(t *testing.T, ls *locSet) {
|
||||
assert.ElementsMatch(t, []string{"foo", "foo/lr_3", "foo/lr_4"}, maps.Keys(ls.Locations))
|
||||
assert.Empty(t, maps.Keys(ls.Locations[l1]))
|
||||
assert.Empty(t, maps.Keys(ls.Locations[l14]))
|
||||
assert.Empty(t, maps.Keys(ls.Locations[l13]))
|
||||
assert.ElementsMatch(t, []string{i1, i2}, maps.Keys(ls.Locations["foo"]))
|
||||
assert.Empty(t, maps.Keys(ls.Locations["foo/lr_3"]))
|
||||
assert.ElementsMatch(t, []string{i3, i4}, maps.Keys(ls.Locations["foo/lr_4"]))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "to existing location",
|
||||
from: l14,
|
||||
to: l1,
|
||||
expect: func(t *testing.T, ls *locSet) {
|
||||
assert.ElementsMatch(t, []string{l1, l13}, maps.Keys(ls.Locations))
|
||||
assert.Empty(t, maps.Keys(ls.Locations[l14]))
|
||||
assert.ElementsMatch(t, []string{i1, i2, i3, i4}, maps.Keys(ls.Locations[l1]))
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range table {
|
||||
suite.Run(test.name, func() {
|
||||
t := suite.T()
|
||||
ls := makeSet()
|
||||
|
||||
ls.RenameLocation(test.from, test.to)
|
||||
test.expect(t, ls)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *LocSetUnitSuite) TestItem() {
|
||||
t := suite.T()
|
||||
b4 := "bar/lr_4"
|
||||
|
||||
makeSet := func() *locSet {
|
||||
ls := newLocSet()
|
||||
|
||||
ls.AddItem(l1, i1)
|
||||
ls.AddItem(l1, i2)
|
||||
ls.AddLocation(l13)
|
||||
ls.AddItem(l14, i3)
|
||||
ls.AddItem(l14, i4)
|
||||
ls.AddItem(b4, "fnord")
|
||||
|
||||
return ls
|
||||
}
|
||||
|
||||
ts := makeSet()
|
||||
assert.ElementsMatch(t, []string{l1, l13, l14, b4}, maps.Keys(ts.Locations))
|
||||
assert.ElementsMatch(t, []string{i1, i2}, maps.Keys(ts.Locations[l1]))
|
||||
assert.Empty(t, maps.Keys(ts.Locations[l13]))
|
||||
assert.ElementsMatch(t, []string{i3, i4}, maps.Keys(ts.Locations[l14]))
|
||||
assert.ElementsMatch(t, []string{"fnord"}, maps.Keys(ts.Locations[b4]))
|
||||
|
||||
table := []struct {
|
||||
name string
|
||||
item string
|
||||
from string
|
||||
to string
|
||||
expect func(*testing.T, *locSet)
|
||||
}{
|
||||
{
|
||||
name: "nop item",
|
||||
item: "floob",
|
||||
from: l2,
|
||||
to: l1,
|
||||
expect: func(t *testing.T, ls *locSet) {
|
||||
assert.ElementsMatch(t, []string{i1, i2, "floob"}, maps.Keys(ls.Locations[l1]))
|
||||
assert.Empty(t, maps.Keys(ls.Locations[l2]))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nop origin",
|
||||
item: i1,
|
||||
from: "smarf",
|
||||
to: l2,
|
||||
expect: func(t *testing.T, ls *locSet) {
|
||||
assert.ElementsMatch(t, []string{i1, i2}, maps.Keys(ls.Locations[l1]))
|
||||
assert.ElementsMatch(t, []string{i1}, maps.Keys(ls.Locations[l2]))
|
||||
assert.Empty(t, maps.Keys(ls.Locations["smarf"]))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new location",
|
||||
item: i1,
|
||||
from: l1,
|
||||
to: "fnords",
|
||||
expect: func(t *testing.T, ls *locSet) {
|
||||
assert.ElementsMatch(t, []string{i2}, maps.Keys(ls.Locations[l1]))
|
||||
assert.ElementsMatch(t, []string{i1}, maps.Keys(ls.Locations["fnords"]))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "existing location",
|
||||
item: i1,
|
||||
from: l1,
|
||||
to: l2,
|
||||
expect: func(t *testing.T, ls *locSet) {
|
||||
assert.ElementsMatch(t, []string{i2}, maps.Keys(ls.Locations[l1]))
|
||||
assert.ElementsMatch(t, []string{i1}, maps.Keys(ls.Locations[l2]))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "same location",
|
||||
item: i1,
|
||||
from: l1,
|
||||
to: l1,
|
||||
expect: func(t *testing.T, ls *locSet) {
|
||||
assert.ElementsMatch(t, []string{i1, i2}, maps.Keys(ls.Locations[l1]))
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range table {
|
||||
suite.Run(test.name, func() {
|
||||
t := suite.T()
|
||||
ls := makeSet()
|
||||
|
||||
ls.MoveItem(test.from, test.to, test.item)
|
||||
test.expect(t, ls)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *LocSetUnitSuite) TestMoveLocation() {
|
||||
t := suite.T()
|
||||
b4 := "bar/lr_4"
|
||||
|
||||
makeSet := func() *locSet {
|
||||
ls := newLocSet()
|
||||
|
||||
ls.AddItem(l1, i1)
|
||||
ls.AddItem(l1, i2)
|
||||
ls.AddLocation(l13)
|
||||
ls.AddItem(l14, i3)
|
||||
ls.AddItem(l14, i4)
|
||||
ls.AddItem(b4, "fnord")
|
||||
|
||||
return ls
|
||||
}
|
||||
|
||||
ts := makeSet()
|
||||
assert.ElementsMatch(t, []string{l1, l13, l14, b4}, maps.Keys(ts.Locations))
|
||||
assert.ElementsMatch(t, []string{i1, i2}, maps.Keys(ts.Locations[l1]))
|
||||
assert.Empty(t, maps.Keys(ts.Locations[l13]))
|
||||
assert.ElementsMatch(t, []string{i3, i4}, maps.Keys(ts.Locations[l14]))
|
||||
assert.ElementsMatch(t, []string{"fnord"}, maps.Keys(ts.Locations[b4]))
|
||||
|
||||
table := []struct {
|
||||
name string
|
||||
from string
|
||||
to string
|
||||
expect func(*testing.T, *locSet)
|
||||
expectNewLoc string
|
||||
}{
|
||||
{
|
||||
name: "nop root",
|
||||
from: l2,
|
||||
to: "",
|
||||
expect: func(t *testing.T, ls *locSet) {
|
||||
assert.ElementsMatch(t, []string{l1, l13, l14, b4}, maps.Keys(ls.Locations))
|
||||
assert.Empty(t, maps.Keys(ls.Locations[l2]))
|
||||
},
|
||||
expectNewLoc: l2,
|
||||
},
|
||||
{
|
||||
name: "nop child",
|
||||
from: l2,
|
||||
to: "foo",
|
||||
expect: func(t *testing.T, ls *locSet) {
|
||||
assert.ElementsMatch(t, []string{l1, l13, l14, b4}, maps.Keys(ls.Locations))
|
||||
assert.Empty(t, maps.Keys(ls.Locations["foo"]))
|
||||
assert.Empty(t, maps.Keys(ls.Locations["foo/"+l2]))
|
||||
},
|
||||
expectNewLoc: "foo/" + l2,
|
||||
},
|
||||
{
|
||||
name: "no items",
|
||||
from: l13,
|
||||
to: "foo",
|
||||
expect: func(t *testing.T, ls *locSet) {
|
||||
newLoc := "foo/lr_3"
|
||||
assert.ElementsMatch(t, []string{l1, newLoc, l14, b4}, maps.Keys(ls.Locations))
|
||||
assert.Empty(t, maps.Keys(ls.Locations[l13]))
|
||||
assert.Empty(t, maps.Keys(ls.Locations[newLoc]))
|
||||
},
|
||||
expectNewLoc: "foo/lr_3",
|
||||
},
|
||||
{
|
||||
name: "with items",
|
||||
from: l14,
|
||||
to: "foo",
|
||||
expect: func(t *testing.T, ls *locSet) {
|
||||
newLoc := "foo/lr_4"
|
||||
assert.ElementsMatch(t, []string{l1, l13, newLoc, b4}, maps.Keys(ls.Locations))
|
||||
assert.Empty(t, maps.Keys(ls.Locations[l14]))
|
||||
assert.ElementsMatch(t, []string{i3, i4}, maps.Keys(ls.Locations[newLoc]))
|
||||
},
|
||||
expectNewLoc: "foo/lr_4",
|
||||
},
|
||||
{
|
||||
name: "cascading locations",
|
||||
from: l1,
|
||||
to: "foo",
|
||||
expect: func(t *testing.T, ls *locSet) {
|
||||
pfx := "foo/"
|
||||
assert.ElementsMatch(t, []string{pfx + l1, pfx + l13, pfx + l14, b4}, maps.Keys(ls.Locations))
|
||||
assert.Empty(t, maps.Keys(ls.Locations[l1]))
|
||||
assert.Empty(t, maps.Keys(ls.Locations[l14]))
|
||||
assert.Empty(t, maps.Keys(ls.Locations[l13]))
|
||||
assert.ElementsMatch(t, []string{i1, i2}, maps.Keys(ls.Locations[pfx+l1]))
|
||||
assert.Empty(t, maps.Keys(ls.Locations[pfx+l13]))
|
||||
assert.ElementsMatch(t, []string{i3, i4}, maps.Keys(ls.Locations[pfx+l14]))
|
||||
},
|
||||
expectNewLoc: "foo/" + l1,
|
||||
},
|
||||
{
|
||||
name: "to existing location",
|
||||
from: l14,
|
||||
to: "bar",
|
||||
expect: func(t *testing.T, ls *locSet) {
|
||||
assert.ElementsMatch(t, []string{l1, l13, b4}, maps.Keys(ls.Locations))
|
||||
assert.Empty(t, maps.Keys(ls.Locations[l14]))
|
||||
assert.Empty(t, maps.Keys(ls.Locations["bar"]))
|
||||
assert.ElementsMatch(t, []string{"fnord", i3, i4}, maps.Keys(ls.Locations[b4]))
|
||||
},
|
||||
expectNewLoc: b4,
|
||||
},
|
||||
}
|
||||
for _, test := range table {
|
||||
suite.Run(test.name, func() {
|
||||
t := suite.T()
|
||||
ls := makeSet()
|
||||
|
||||
newLoc := ls.MoveLocation(test.from, test.to)
|
||||
test.expect(t, ls)
|
||||
assert.Equal(t, test.expectNewLoc, newLoc)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -85,7 +85,7 @@ type Path interface {
|
||||
Category() CategoryType
|
||||
Tenant() string
|
||||
ResourceOwner() string
|
||||
Folder(bool) string
|
||||
Folder(escaped bool) string
|
||||
Folders() Elements
|
||||
Item() string
|
||||
// UpdateParent updates parent from old to new if the item/folder was
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user