now that we have a core error library, we can start swapping out the percolation of graph api errors with core error sentinels. This PR begins that process by adding a centralized transformer to the graph service handlers that ensures all calls produce the same set of core errors according to some standard categorizations. This is just an initial step, and does not transfer all graph errors into core sentinels. --- #### Does this PR need a docs update or release note? - [x] ⛔ No #### Type of change - [x] 🤖 Supportability/Tests #### Issue(s) * #4685 #### Test Plan - [x] ⚡ Unit test - [x] 💚 E2E
224 lines
5.4 KiB
Go
224 lines
5.4 KiB
Go
package backup_test
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/alcionai/clues"
|
|
"github.com/spf13/cobra"
|
|
"github.com/spf13/viper"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/alcionai/corso/src/cli"
|
|
"github.com/alcionai/corso/src/cli/flags"
|
|
"github.com/alcionai/corso/src/cli/print"
|
|
cliTD "github.com/alcionai/corso/src/cli/testdata"
|
|
"github.com/alcionai/corso/src/internal/common/ptr"
|
|
"github.com/alcionai/corso/src/internal/tester"
|
|
"github.com/alcionai/corso/src/internal/tester/tconfig"
|
|
"github.com/alcionai/corso/src/pkg/account"
|
|
"github.com/alcionai/corso/src/pkg/config"
|
|
"github.com/alcionai/corso/src/pkg/control"
|
|
"github.com/alcionai/corso/src/pkg/count"
|
|
"github.com/alcionai/corso/src/pkg/path"
|
|
"github.com/alcionai/corso/src/pkg/repository"
|
|
"github.com/alcionai/corso/src/pkg/services/m365/api"
|
|
"github.com/alcionai/corso/src/pkg/services/m365/api/graph"
|
|
"github.com/alcionai/corso/src/pkg/storage"
|
|
"github.com/alcionai/corso/src/pkg/storage/testdata"
|
|
)
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Gockable client
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// GockClient produces a new exchange api client that can be
|
|
// mocked using gock.
|
|
func gockClient(creds account.M365Config, counter *count.Bus) (api.Client, error) {
|
|
s, err := graph.NewGockService(creds, counter)
|
|
if err != nil {
|
|
return api.Client{}, err
|
|
}
|
|
|
|
li, err := graph.NewGockService(creds, counter, graph.NoTimeout())
|
|
if err != nil {
|
|
return api.Client{}, err
|
|
}
|
|
|
|
return api.Client{
|
|
Credentials: creds,
|
|
Stable: s,
|
|
LargeItem: li,
|
|
}, nil
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Suite Setup
|
|
// ---------------------------------------------------------------------------
|
|
|
|
type ids struct {
|
|
ID string
|
|
DriveID string
|
|
DriveRootFolderID string
|
|
}
|
|
|
|
type intgTesterSetup struct {
|
|
acct account.Account
|
|
ac api.Client
|
|
gockAC api.Client
|
|
user ids
|
|
site ids
|
|
group ids
|
|
team ids
|
|
}
|
|
|
|
func newIntegrationTesterSetup(t *testing.T) intgTesterSetup {
|
|
its := intgTesterSetup{}
|
|
|
|
ctx, flush := tester.NewContext(t)
|
|
defer flush()
|
|
|
|
graph.InitializeConcurrencyLimiter(ctx, true, 4)
|
|
|
|
its.acct = tconfig.NewM365Account(t)
|
|
creds, err := its.acct.M365Config()
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
its.ac, err = api.NewClient(
|
|
creds,
|
|
control.DefaultOptions(),
|
|
count.New())
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
its.gockAC, err = gockClient(creds, count.New())
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
// user drive
|
|
|
|
uids := ids{}
|
|
|
|
uids.ID = tconfig.M365UserID(t)
|
|
|
|
userDrive, err := its.ac.Users().GetDefaultDrive(ctx, uids.ID)
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
uids.DriveID = ptr.Val(userDrive.GetId())
|
|
|
|
userDriveRootFolder, err := its.ac.Drives().GetRootFolder(ctx, uids.DriveID)
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
uids.DriveRootFolderID = ptr.Val(userDriveRootFolder.GetId())
|
|
|
|
its.user = uids
|
|
|
|
// site
|
|
|
|
sids := ids{}
|
|
|
|
sids.ID = tconfig.M365SiteID(t)
|
|
|
|
siteDrive, err := its.ac.Sites().GetDefaultDrive(ctx, sids.ID)
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
sids.DriveID = ptr.Val(siteDrive.GetId())
|
|
|
|
siteDriveRootFolder, err := its.ac.Drives().GetRootFolder(ctx, sids.DriveID)
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
sids.DriveRootFolderID = ptr.Val(siteDriveRootFolder.GetId())
|
|
|
|
its.site = sids
|
|
|
|
// group
|
|
|
|
gids := ids{}
|
|
|
|
// use of the TeamID is intentional here, so that we are assured
|
|
// the group has full usage of the teams api.
|
|
gids.ID = tconfig.M365TeamID(t)
|
|
|
|
its.group = gids
|
|
|
|
// team
|
|
|
|
tids := ids{}
|
|
tids.ID = tconfig.M365TeamID(t)
|
|
its.team = tids
|
|
|
|
return its
|
|
}
|
|
|
|
type dependencies struct {
|
|
st storage.Storage
|
|
repo repository.Repositoryer
|
|
vpr *viper.Viper
|
|
recorder strings.Builder
|
|
configFilePath string
|
|
}
|
|
|
|
func prepM365Test(
|
|
t *testing.T,
|
|
ctx context.Context, //revive:disable-line:context-as-argument
|
|
pst path.ServiceType,
|
|
) dependencies {
|
|
var (
|
|
acct = tconfig.NewM365Account(t)
|
|
st = testdata.NewPrefixedS3Storage(t)
|
|
recorder = strings.Builder{}
|
|
)
|
|
|
|
cfg, err := st.ToS3Config()
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
force := map[string]string{
|
|
tconfig.TestCfgAccountProvider: account.ProviderM365.String(),
|
|
tconfig.TestCfgStorageProvider: storage.ProviderS3.String(),
|
|
tconfig.TestCfgPrefix: cfg.Prefix,
|
|
}
|
|
|
|
vpr, cfgFP := tconfig.MakeTempTestConfigClone(t, force)
|
|
ctx = config.SetViper(ctx, vpr)
|
|
|
|
repo, err := repository.New(
|
|
ctx,
|
|
acct,
|
|
st,
|
|
control.DefaultOptions(),
|
|
repository.NewRepoID)
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
err = repo.Initialize(ctx, repository.InitConfig{
|
|
Service: pst,
|
|
})
|
|
require.NoError(t, err, clues.ToCore(err))
|
|
|
|
return dependencies{
|
|
st: st,
|
|
repo: repo,
|
|
vpr: vpr,
|
|
recorder: recorder,
|
|
configFilePath: cfgFP,
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// funcs
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func buildExchangeBackupCmd(
|
|
ctx context.Context,
|
|
configFile, user, category string,
|
|
recorder *strings.Builder,
|
|
) (*cobra.Command, context.Context) {
|
|
cmd := cliTD.StubRootCmd(
|
|
"backup", "create", "exchange",
|
|
"--"+flags.ConfigFileFN, configFile,
|
|
"--"+flags.UserFN, user,
|
|
"--"+flags.CategoryDataFN, category)
|
|
cli.BuildCommandTree(cmd)
|
|
cmd.SetOut(recorder)
|
|
|
|
return cmd, print.SetRootCmd(ctx, cmd)
|
|
}
|