remove discovery pkg (#3677)

Currently unused, and functionality is largely duplicated by the services/m365 package.

---

#### Does this PR need a docs update or release note?

- [x]  No

#### Type of change

- [ ] 🧹 Tech Debt/Cleanup

#### Test Plan

- [x]  Unit test
- [x] 💚 E2E
This commit is contained in:
Keepers 2023-07-01 07:52:57 -06:00 committed by GitHub
parent 8b81728488
commit 56151a82eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 278 additions and 512 deletions

View File

@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Handle OLE conversion errors when trying to fetch attachments - Handle OLE conversion errors when trying to fetch attachments
- Fix uploading large attachments for emails and calendar - Fix uploading large attachments for emails and calendar
- Fixed high memory use in OneDrive backup related to logging - Fixed high memory use in OneDrive backup related to logging
- Return a ServiceNotEnabled error when a tenant has no active SharePoint license.
### Changed ### Changed
- Switched to Go 1.20 - Switched to Go 1.20

View File

@ -10,7 +10,6 @@ import (
"github.com/alcionai/corso/src/internal/common/prefixmatcher" "github.com/alcionai/corso/src/internal/common/prefixmatcher"
"github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/internal/diagnostics" "github.com/alcionai/corso/src/internal/diagnostics"
"github.com/alcionai/corso/src/internal/m365/discovery"
"github.com/alcionai/corso/src/internal/m365/exchange" "github.com/alcionai/corso/src/internal/m365/exchange"
"github.com/alcionai/corso/src/internal/m365/graph" "github.com/alcionai/corso/src/internal/m365/graph"
"github.com/alcionai/corso/src/internal/m365/onedrive" "github.com/alcionai/corso/src/internal/m365/onedrive"
@ -21,6 +20,7 @@ import (
"github.com/alcionai/corso/src/pkg/logger" "github.com/alcionai/corso/src/pkg/logger"
"github.com/alcionai/corso/src/pkg/path" "github.com/alcionai/corso/src/pkg/path"
"github.com/alcionai/corso/src/pkg/selectors" "github.com/alcionai/corso/src/pkg/selectors"
"github.com/alcionai/corso/src/pkg/services/m365/api"
) )
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -203,9 +203,13 @@ func verifyBackupInputs(sels selectors.Selector, siteIDs []string) error {
return nil return nil
} }
type getInfoer interface {
GetInfo(context.Context, string) (*api.UserInfo, error)
}
func checkServiceEnabled( func checkServiceEnabled(
ctx context.Context, ctx context.Context,
gi discovery.GetInfoer, gi getInfoer,
service path.ServiceType, service path.ServiceType,
resource string, resource string,
) (bool, bool, error) { ) (bool, bool, error) {

View File

@ -1,150 +0,0 @@
package discovery
import (
"context"
"github.com/alcionai/clues"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/alcionai/corso/src/internal/m365/graph"
"github.com/alcionai/corso/src/pkg/account"
"github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/services/m365/api"
)
// ---------------------------------------------------------------------------
// interfaces
// ---------------------------------------------------------------------------
type getter interface {
GetByID(context.Context, string) (models.Userable, error)
}
type GetInfoer interface {
GetInfo(context.Context, string) (*api.UserInfo, error)
}
type getWithInfoer interface {
getter
GetInfoer
}
type GetDefaultDriver interface {
GetDefaultDrive(ctx context.Context, userID string) (models.Driveable, error)
}
type getAller interface {
GetAll(ctx context.Context, errs *fault.Bus) ([]models.Userable, error)
}
// ---------------------------------------------------------------------------
// helpers
// ---------------------------------------------------------------------------
func apiClient(ctx context.Context, acct account.Account) (api.Client, error) {
m365, err := acct.M365Config()
if err != nil {
return api.Client{}, clues.Wrap(err, "retrieving m365 account configuration").WithClues(ctx)
}
client, err := api.NewClient(m365)
if err != nil {
return api.Client{}, clues.Wrap(err, "creating api client").WithClues(ctx)
}
return client, nil
}
// ---------------------------------------------------------------------------
// users
// ---------------------------------------------------------------------------
// Users fetches all users in the tenant.
func Users(
ctx context.Context,
ga getAller,
errs *fault.Bus,
) ([]models.Userable, error) {
users, err := ga.GetAll(ctx, errs)
if err != nil {
return nil, clues.Wrap(err, "getting all users")
}
return users, nil
}
// UserDetails fetches detailed info like - userPurpose for all users in the tenant.
func GetUserInfo(
ctx context.Context,
acct account.Account,
userID string,
errs *fault.Bus,
) (*api.UserInfo, error) {
client, err := apiClient(ctx, acct)
if err != nil {
return nil, err
}
aui, err := client.Users().GetInfo(ctx, userID)
if err != nil {
return nil, clues.Stack(err)
}
return aui, nil
}
// User fetches a single user's data.
func User(
ctx context.Context,
gwi getWithInfoer,
userID string,
) (models.Userable, *api.UserInfo, error) {
u, err := gwi.GetByID(ctx, userID)
if err != nil {
if graph.IsErrUserNotFound(err) {
return nil, nil, clues.Stack(graph.ErrResourceOwnerNotFound, err).With("user_id", userID)
}
return nil, nil, clues.Wrap(err, "getting user")
}
ui, err := gwi.GetInfo(ctx, userID)
if err != nil {
return nil, nil, clues.Wrap(err, "getting user info")
}
return u, ui, nil
}
// UserInfo produces extensible user info: metadata that is relevant
// or identified in Corso, but not in m365.
func UserInfo(
ctx context.Context,
gi GetInfoer,
userID string,
) (*api.UserInfo, error) {
ui, err := gi.GetInfo(ctx, userID)
if err != nil {
return nil, clues.Wrap(err, "getting user info")
}
return ui, nil
}
// ---------------------------------------------------------------------------
// sites
// ---------------------------------------------------------------------------
// Sites fetches all sharepoint sites in the tenant
func Sites(
ctx context.Context,
acct account.Account,
errs *fault.Bus,
) ([]models.Siteable, error) {
client, err := apiClient(ctx, acct)
if err != nil {
return nil, err
}
return client.Sites().GetAll(ctx, errs)
}

View File

@ -1,296 +0,0 @@
package discovery_test
import (
"testing"
"github.com/alcionai/clues"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/internal/m365/discovery"
"github.com/alcionai/corso/src/internal/m365/graph"
"github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/pkg/account"
"github.com/alcionai/corso/src/pkg/credentials"
"github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/path"
"github.com/alcionai/corso/src/pkg/services/m365/api"
)
type DiscoveryIntgSuite struct {
tester.Suite
}
func TestDiscoveryIntgSuite(t *testing.T) {
suite.Run(t, &DiscoveryIntgSuite{
Suite: tester.NewIntegrationSuite(
t,
[][]string{tester.M365AcctCredEnvs}),
})
}
func (suite *DiscoveryIntgSuite) SetupSuite() {
ctx, flush := tester.NewContext(suite.T())
defer flush()
graph.InitializeConcurrencyLimiter(ctx, true, 4)
}
func (suite *DiscoveryIntgSuite) TestUsers() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
var (
acct = tester.NewM365Account(t)
errs = fault.New(true)
)
creds, err := acct.M365Config()
require.NoError(t, err)
cli, err := api.NewClient(creds)
require.NoError(t, err)
users, err := discovery.Users(ctx, cli.Users(), errs)
assert.NoError(t, err, clues.ToCore(err))
ferrs := errs.Errors()
assert.Nil(t, ferrs.Failure)
assert.Empty(t, ferrs.Recovered)
assert.NotEmpty(t, users)
}
func (suite *DiscoveryIntgSuite) TestUsers_InvalidCredentials() {
table := []struct {
name string
acct func(t *testing.T) account.Account
}{
{
name: "Invalid Credentials",
acct: func(t *testing.T) account.Account {
a, err := account.NewAccount(
account.ProviderM365,
account.M365Config{
M365: credentials.M365{
AzureClientID: "Test",
AzureClientSecret: "without",
},
AzureTenantID: "data",
},
)
require.NoError(t, err, clues.ToCore(err))
return a
},
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
acct := test.acct(t)
creds, err := acct.M365Config()
require.NoError(t, err)
cli, err := api.NewClient(creds)
require.NoError(t, err)
users, err := discovery.Users(ctx, cli.Users(), fault.New(true))
assert.Empty(t, users, "returned some users")
assert.NotNil(t, err)
})
}
}
func (suite *DiscoveryIntgSuite) TestSites() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
var (
acct = tester.NewM365Account(t)
errs = fault.New(true)
)
sites, err := discovery.Sites(ctx, acct, errs)
assert.NoError(t, err, clues.ToCore(err))
ferrs := errs.Errors()
assert.Nil(t, ferrs.Failure)
assert.Empty(t, ferrs.Recovered)
assert.NotEmpty(t, sites)
}
func (suite *DiscoveryIntgSuite) TestSites_InvalidCredentials() {
table := []struct {
name string
acct func(t *testing.T) account.Account
}{
{
name: "Invalid Credentials",
acct: func(t *testing.T) account.Account {
a, err := account.NewAccount(
account.ProviderM365,
account.M365Config{
M365: credentials.M365{
AzureClientID: "Test",
AzureClientSecret: "without",
},
AzureTenantID: "data",
},
)
require.NoError(t, err, clues.ToCore(err))
return a
},
},
{
name: "Empty Credentials",
acct: func(t *testing.T) account.Account {
// intentionally swallowing the error here
a, _ := account.NewAccount(account.ProviderM365)
return a
},
},
}
for _, test := range table {
suite.Run(test.name, func() {
var (
t = suite.T()
a = test.acct(t)
errs = fault.New(true)
ctx, flush = tester.NewContext(t)
)
defer flush()
sites, err := discovery.Sites(ctx, a, errs)
assert.Empty(t, sites, "returned some sites")
assert.NotNil(t, err)
})
}
}
func (suite *DiscoveryIntgSuite) TestUserInfo() {
t := suite.T()
acct := tester.NewM365Account(t)
creds, err := acct.M365Config()
require.NoError(t, err)
cli, err := api.NewClient(creds)
require.NoError(t, err)
uapi := cli.Users()
table := []struct {
name string
user string
expect *api.UserInfo
expectErr require.ErrorAssertionFunc
}{
{
name: "standard test user",
user: tester.M365UserID(t),
expect: &api.UserInfo{
ServicesEnabled: map[path.ServiceType]struct{}{
path.ExchangeService: {},
path.OneDriveService: {},
},
Mailbox: api.MailboxInfo{
Purpose: "user",
ErrGetMailBoxSetting: nil,
},
},
expectErr: require.NoError,
},
{
name: "user does not exist",
user: uuid.NewString(),
expect: &api.UserInfo{
ServicesEnabled: map[path.ServiceType]struct{}{},
Mailbox: api.MailboxInfo{},
},
expectErr: require.Error,
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
result, err := discovery.UserInfo(ctx, uapi, test.user)
test.expectErr(t, err, clues.ToCore(err))
if err != nil {
return
}
assert.Equal(t, test.expect.ServicesEnabled, result.ServicesEnabled)
})
}
}
func (suite *DiscoveryIntgSuite) TestUserWithoutDrive() {
t := suite.T()
acct := tester.NewM365Account(t)
userID := tester.M365UserID(t)
table := []struct {
name string
user string
expect *api.UserInfo
}{
{
name: "user without drive and exchange",
user: "a53c26f7-5100-4acb-a910-4d20960b2c19", // User: testevents@10rqc2.onmicrosoft.com
expect: &api.UserInfo{
ServicesEnabled: map[path.ServiceType]struct{}{},
Mailbox: api.MailboxInfo{
ErrGetMailBoxSetting: []error{api.ErrMailBoxSettingsNotFound},
},
},
},
{
name: "user with drive and exchange",
user: userID,
expect: &api.UserInfo{
ServicesEnabled: map[path.ServiceType]struct{}{
path.ExchangeService: {},
path.OneDriveService: {},
},
Mailbox: api.MailboxInfo{
Purpose: "user",
ErrGetMailBoxSetting: []error{},
},
},
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
result, err := discovery.GetUserInfo(ctx, acct, test.user, fault.New(true))
require.NoError(t, err, clues.ToCore(err))
assert.Equal(t, test.expect.ServicesEnabled, result.ServicesEnabled)
assert.Equal(t, test.expect.Mailbox.ErrGetMailBoxSetting, result.Mailbox.ErrGetMailBoxSetting)
assert.Equal(t, test.expect.Mailbox.Purpose, result.Mailbox.Purpose)
})
}
}

View File

@ -26,10 +26,20 @@ import (
type errorCode string type errorCode string
const ( const (
activityLimitReached errorCode = "activityLimitReached" // cannotOpenFileAttachment happen when an attachment is
emailFolderNotFound errorCode = "ErrorSyncFolderNotFound" // inaccessible. The error message is usually "OLE conversion
errorAccessDenied errorCode = "ErrorAccessDenied" // failed for an attachment."
errorItemNotFound errorCode = "ErrorItemNotFound" cannotOpenFileAttachment errorCode = "ErrorCannotOpenFileAttachment"
emailFolderNotFound errorCode = "ErrorSyncFolderNotFound"
errorAccessDenied errorCode = "ErrorAccessDenied"
errorItemNotFound errorCode = "ErrorItemNotFound"
// This error occurs when an attempt is made to create a folder that has
// the same name as another folder in the same parent. Such duplicate folder
// names are not allowed by graph.
folderExists errorCode = "ErrorFolderExists"
// Some datacenters are returning this when we try to get the inbox of a user
// that doesn't exist.
invalidUser errorCode = "ErrorInvalidUser"
itemNotFound errorCode = "itemNotFound" itemNotFound errorCode = "itemNotFound"
MailboxNotEnabledForRESTAPI errorCode = "MailboxNotEnabledForRESTAPI" MailboxNotEnabledForRESTAPI errorCode = "MailboxNotEnabledForRESTAPI"
malwareDetected errorCode = "malwareDetected" malwareDetected errorCode = "malwareDetected"
@ -39,22 +49,11 @@ const (
quotaExceeded errorCode = "ErrorQuotaExceeded" quotaExceeded errorCode = "ErrorQuotaExceeded"
RequestResourceNotFound errorCode = "Request_ResourceNotFound" RequestResourceNotFound errorCode = "Request_ResourceNotFound"
// Returned when we try to get the inbox of a user that doesn't exist. // Returned when we try to get the inbox of a user that doesn't exist.
ResourceNotFound errorCode = "ResourceNotFound" ResourceNotFound errorCode = "ResourceNotFound"
// Some datacenters are returning this when we try to get the inbox of a user
// that doesn't exist.
invalidUser errorCode = "ErrorInvalidUser"
resyncRequired errorCode = "ResyncRequired" resyncRequired errorCode = "ResyncRequired"
syncFolderNotFound errorCode = "ErrorSyncFolderNotFound" syncFolderNotFound errorCode = "ErrorSyncFolderNotFound"
syncStateInvalid errorCode = "SyncStateInvalid" syncStateInvalid errorCode = "SyncStateInvalid"
syncStateNotFound errorCode = "SyncStateNotFound" syncStateNotFound errorCode = "SyncStateNotFound"
// This error occurs when an attempt is made to create a folder that has
// the same name as another folder in the same parent. Such duplicate folder
// names are not allowed by graph.
folderExists errorCode = "ErrorFolderExists"
// cannotOpenFileAttachment happen when an attachment is
// inaccessible. The error message is usually "OLE conversion
// failed for an attachment."
cannotOpenFileAttachment errorCode = "ErrorCannotOpenFileAttachment"
) )
type errorMessage string type errorMessage string

View File

@ -8,18 +8,18 @@ import (
"github.com/alcionai/corso/src/internal/common/idname" "github.com/alcionai/corso/src/internal/common/idname"
"github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/m365/discovery"
"github.com/alcionai/corso/src/internal/m365/graph" "github.com/alcionai/corso/src/internal/m365/graph"
"github.com/alcionai/corso/src/pkg/account" "github.com/alcionai/corso/src/pkg/account"
"github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/services/m365/api" "github.com/alcionai/corso/src/pkg/services/m365/api"
) )
// ServiceAccess is true if a resource owner is capable of // ---------------------------------------------------------------------------
// accessing or utilizing the specified service. // interfaces & structs
type ServiceAccess struct { // ---------------------------------------------------------------------------
Exchange bool
// TODO: onedrive, sharepoint type getDefaultDriver interface {
GetDefaultDrive(ctx context.Context, userID string) (models.Driveable, error)
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -107,7 +107,7 @@ func UserHasDrives(ctx context.Context, acct account.Account, userID string) (bo
return checkUserHasDrives(ctx, ac.Users(), userID) return checkUserHasDrives(ctx, ac.Users(), userID)
} }
func checkUserHasDrives(ctx context.Context, dgdd discovery.GetDefaultDriver, userID string) (bool, error) { func checkUserHasDrives(ctx context.Context, dgdd getDefaultDriver, userID string) (bool, error) {
_, err := dgdd.GetDefaultDrive(ctx, userID) _, err := dgdd.GetDefaultDrive(ctx, userID)
if err != nil { if err != nil {
// we consider this a non-error case, since it // we consider this a non-error case, since it
@ -135,7 +135,7 @@ func usersNoInfo(ctx context.Context, acct account.Account, errs *fault.Bus) ([]
return nil, clues.Stack(err).WithClues(ctx) return nil, clues.Stack(err).WithClues(ctx)
} }
us, err := discovery.Users(ctx, ac.Users(), errs) us, err := ac.Users().GetAll(ctx, errs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -167,7 +167,7 @@ func Users(ctx context.Context, acct account.Account, errs *fault.Bus) ([]*User,
return nil, clues.Stack(err).WithClues(ctx) return nil, clues.Stack(err).WithClues(ctx)
} }
us, err := discovery.Users(ctx, ac.Users(), errs) us, err := ac.Users().GetAll(ctx, errs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -180,7 +180,7 @@ func Users(ctx context.Context, acct account.Account, errs *fault.Bus) ([]*User,
return nil, clues.Wrap(err, "formatting user data") return nil, clues.Wrap(err, "formatting user data")
} }
userInfo, err := discovery.GetUserInfo(ctx, acct, pu.ID, errs) userInfo, err := ac.Users().GetInfo(ctx, pu.ID)
if err != nil { if err != nil {
return nil, clues.Wrap(err, "getting user details") return nil, clues.Wrap(err, "getting user details")
} }
@ -220,7 +220,7 @@ func GetUserInfo(
return nil, clues.Stack(err).WithClues(ctx) return nil, clues.Stack(err).WithClues(ctx)
} }
ui, err := discovery.UserInfo(ctx, ac.Users(), userID) ui, err := ac.Users().GetInfo(ctx, userID)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -5,6 +5,7 @@ import (
"testing" "testing"
"github.com/alcionai/clues" "github.com/alcionai/clues"
"github.com/google/uuid"
"github.com/microsoftgraph/msgraph-sdk-go/models" "github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -12,11 +13,13 @@ import (
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/m365/discovery"
"github.com/alcionai/corso/src/internal/m365/graph" "github.com/alcionai/corso/src/internal/m365/graph"
"github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/pkg/account"
"github.com/alcionai/corso/src/pkg/credentials"
"github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/path" "github.com/alcionai/corso/src/pkg/path"
"github.com/alcionai/corso/src/pkg/services/m365/api"
) )
type M365IntegrationSuite struct { type M365IntegrationSuite struct {
@ -31,6 +34,13 @@ func TestM365IntegrationSuite(t *testing.T) {
}) })
} }
func (suite *M365IntegrationSuite) SetupSuite() {
ctx, flush := tester.NewContext(suite.T())
defer flush()
graph.InitializeConcurrencyLimiter(ctx, true, 4)
}
func (suite *M365IntegrationSuite) TestUsers() { func (suite *M365IntegrationSuite) TestUsers() {
t := suite.T() t := suite.T()
@ -80,35 +90,6 @@ func (suite *M365IntegrationSuite) TestUsersCompat_HasNoInfo() {
} }
} }
func (suite *M365IntegrationSuite) TestGetUserInfo() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
graph.InitializeConcurrencyLimiter(ctx, true, 4)
var (
acct = tester.NewM365Account(t)
uid = tester.M365UserID(t)
)
info, err := GetUserInfo(ctx, acct, uid)
require.NoError(t, err, clues.ToCore(err))
require.NotNil(t, info)
require.NotEmpty(t, info)
expectEnabled := map[path.ServiceType]struct{}{
path.ExchangeService: {},
path.OneDriveService: {},
}
assert.NotEmpty(t, info.ServicesEnabled)
assert.NotEmpty(t, info.Mailbox)
assert.Equal(t, expectEnabled, info.ServicesEnabled)
assert.Equal(t, "user", info.Mailbox.Purpose)
}
func (suite *M365IntegrationSuite) TestUserHasMailbox() { func (suite *M365IntegrationSuite) TestUserHasMailbox() {
t := suite.T() t := suite.T()
@ -183,13 +164,13 @@ func (m mockDGDD) GetDefaultDrive(context.Context, string) (models.Driveable, er
func (suite *m365UnitSuite) TestCheckUserHasDrives() { func (suite *m365UnitSuite) TestCheckUserHasDrives() {
table := []struct { table := []struct {
name string name string
mock func(context.Context) discovery.GetDefaultDriver mock func(context.Context) getDefaultDriver
expect assert.BoolAssertionFunc expect assert.BoolAssertionFunc
expectErr func(*testing.T, error) expectErr func(*testing.T, error)
}{ }{
{ {
name: "ok", name: "ok",
mock: func(ctx context.Context) discovery.GetDefaultDriver { mock: func(ctx context.Context) getDefaultDriver {
return mockDGDD{models.NewDrive(), nil} return mockDGDD{models.NewDrive(), nil}
}, },
expect: assert.True, expect: assert.True,
@ -199,7 +180,7 @@ func (suite *m365UnitSuite) TestCheckUserHasDrives() {
}, },
{ {
name: "mysite not found", name: "mysite not found",
mock: func(ctx context.Context) discovery.GetDefaultDriver { mock: func(ctx context.Context) getDefaultDriver {
odErr := odataerrors.NewODataError() odErr := odataerrors.NewODataError()
merr := odataerrors.NewMainError() merr := odataerrors.NewMainError()
merr.SetCode(ptr.To("code")) merr.SetCode(ptr.To("code"))
@ -215,7 +196,7 @@ func (suite *m365UnitSuite) TestCheckUserHasDrives() {
}, },
{ {
name: "mysite URL not found", name: "mysite URL not found",
mock: func(ctx context.Context) discovery.GetDefaultDriver { mock: func(ctx context.Context) getDefaultDriver {
odErr := odataerrors.NewODataError() odErr := odataerrors.NewODataError()
merr := odataerrors.NewMainError() merr := odataerrors.NewMainError()
merr.SetCode(ptr.To("code")) merr.SetCode(ptr.To("code"))
@ -231,7 +212,7 @@ func (suite *m365UnitSuite) TestCheckUserHasDrives() {
}, },
{ {
name: "no sharepoint license", name: "no sharepoint license",
mock: func(ctx context.Context) discovery.GetDefaultDriver { mock: func(ctx context.Context) getDefaultDriver {
odErr := odataerrors.NewODataError() odErr := odataerrors.NewODataError()
merr := odataerrors.NewMainError() merr := odataerrors.NewMainError()
merr.SetCode(ptr.To("code")) merr.SetCode(ptr.To("code"))
@ -247,7 +228,7 @@ func (suite *m365UnitSuite) TestCheckUserHasDrives() {
}, },
{ {
name: "user not found", name: "user not found",
mock: func(ctx context.Context) discovery.GetDefaultDriver { mock: func(ctx context.Context) getDefaultDriver {
odErr := odataerrors.NewODataError() odErr := odataerrors.NewODataError()
merr := odataerrors.NewMainError() merr := odataerrors.NewMainError()
merr.SetCode(ptr.To(string(graph.RequestResourceNotFound))) merr.SetCode(ptr.To(string(graph.RequestResourceNotFound)))
@ -263,7 +244,7 @@ func (suite *m365UnitSuite) TestCheckUserHasDrives() {
}, },
{ {
name: "arbitrary error", name: "arbitrary error",
mock: func(ctx context.Context) discovery.GetDefaultDriver { mock: func(ctx context.Context) getDefaultDriver {
odErr := odataerrors.NewODataError() odErr := odataerrors.NewODataError()
merr := odataerrors.NewMainError() merr := odataerrors.NewMainError()
merr.SetCode(ptr.To("code")) merr.SetCode(ptr.To("code"))
@ -363,3 +344,230 @@ func (suite *m365UnitSuite) TestGetAllSites() {
}) })
} }
} }
type DiscoveryIntgSuite struct {
tester.Suite
acct account.Account
}
func TestDiscoveryIntgSuite(t *testing.T) {
suite.Run(t, &DiscoveryIntgSuite{
Suite: tester.NewIntegrationSuite(
t,
[][]string{tester.M365AcctCredEnvs}),
})
}
func (suite *DiscoveryIntgSuite) SetupSuite() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
graph.InitializeConcurrencyLimiter(ctx, true, 4)
suite.acct = tester.NewM365Account(t)
}
func (suite *DiscoveryIntgSuite) TestUsers() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
errs := fault.New(true)
users, err := Users(ctx, suite.acct, errs)
assert.NoError(t, err, clues.ToCore(err))
ferrs := errs.Errors()
assert.Nil(t, ferrs.Failure)
assert.Empty(t, ferrs.Recovered)
assert.NotEmpty(t, users)
}
func (suite *DiscoveryIntgSuite) TestUsers_InvalidCredentials() {
table := []struct {
name string
acct func(t *testing.T) account.Account
}{
{
name: "Invalid Credentials",
acct: func(t *testing.T) account.Account {
a, err := account.NewAccount(
account.ProviderM365,
account.M365Config{
M365: credentials.M365{
AzureClientID: "Test",
AzureClientSecret: "without",
},
AzureTenantID: "data",
},
)
require.NoError(t, err, clues.ToCore(err))
return a
},
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
users, err := Users(ctx, test.acct(t), fault.New(true))
assert.Empty(t, users, "returned some users")
assert.NotNil(t, err)
})
}
}
func (suite *DiscoveryIntgSuite) TestSites_InvalidCredentials() {
table := []struct {
name string
acct func(t *testing.T) account.Account
}{
{
name: "Invalid Credentials",
acct: func(t *testing.T) account.Account {
a, err := account.NewAccount(
account.ProviderM365,
account.M365Config{
M365: credentials.M365{
AzureClientID: "Test",
AzureClientSecret: "without",
},
AzureTenantID: "data",
},
)
require.NoError(t, err, clues.ToCore(err))
return a
},
},
{
name: "Empty Credentials",
acct: func(t *testing.T) account.Account {
// intentionally swallowing the error here
a, _ := account.NewAccount(account.ProviderM365)
return a
},
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
sites, err := Sites(ctx, test.acct(t), fault.New(true))
assert.Empty(t, sites, "returned some sites")
assert.NotNil(t, err)
})
}
}
func (suite *DiscoveryIntgSuite) TestGetUserInfo() {
table := []struct {
name string
user string
expect *api.UserInfo
expectErr require.ErrorAssertionFunc
}{
{
name: "standard test user",
user: tester.M365UserID(suite.T()),
expect: &api.UserInfo{
ServicesEnabled: map[path.ServiceType]struct{}{
path.ExchangeService: {},
path.OneDriveService: {},
},
Mailbox: api.MailboxInfo{
Purpose: "user",
ErrGetMailBoxSetting: nil,
},
},
expectErr: require.NoError,
},
{
name: "user does not exist",
user: uuid.NewString(),
expect: &api.UserInfo{
ServicesEnabled: map[path.ServiceType]struct{}{},
Mailbox: api.MailboxInfo{},
},
expectErr: require.Error,
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
result, err := GetUserInfo(ctx, suite.acct, test.user)
test.expectErr(t, err, clues.ToCore(err))
if err != nil {
return
}
assert.Equal(t, test.expect.ServicesEnabled, result.ServicesEnabled)
})
}
}
func (suite *DiscoveryIntgSuite) TestGetUserInfo_userWithoutDrive() {
userID := tester.M365UserID(suite.T())
table := []struct {
name string
user string
expect *api.UserInfo
}{
{
name: "user without drive and exchange",
user: "a53c26f7-5100-4acb-a910-4d20960b2c19", // User: testevents@10rqc2.onmicrosoft.com
expect: &api.UserInfo{
ServicesEnabled: map[path.ServiceType]struct{}{},
Mailbox: api.MailboxInfo{
ErrGetMailBoxSetting: []error{api.ErrMailBoxSettingsNotFound},
},
},
},
{
name: "user with drive and exchange",
user: userID,
expect: &api.UserInfo{
ServicesEnabled: map[path.ServiceType]struct{}{
path.ExchangeService: {},
path.OneDriveService: {},
},
Mailbox: api.MailboxInfo{
Purpose: "user",
ErrGetMailBoxSetting: []error{},
},
},
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
result, err := GetUserInfo(ctx, suite.acct, test.user)
require.NoError(t, err, clues.ToCore(err))
assert.Equal(t, test.expect.ServicesEnabled, result.ServicesEnabled)
assert.Equal(t, test.expect.Mailbox.ErrGetMailBoxSetting, result.Mailbox.ErrGetMailBoxSetting)
assert.Equal(t, test.expect.Mailbox.Purpose, result.Mailbox.Purpose)
})
}
}