diff --git a/src/internal/m365/backup.go b/src/internal/m365/backup.go index f3af44e62..3d451dd43 100644 --- a/src/internal/m365/backup.go +++ b/src/internal/m365/backup.go @@ -158,13 +158,21 @@ func (ctrl *Controller) IsBackupRunnable( resourceOwner string, ) (bool, error) { if service == path.SharePointService { - // No "enabled" check required for sharepoint + _, err := ctrl.AC.Sites().GetRoot(ctx) + if err != nil { + if clues.HasLabel(err, graph.LabelsNoSharePointLicense) { + return false, clues.Stack(graph.ErrServiceNotEnabled, err) + } + + return false, err + } + return true, nil } info, err := ctrl.AC.Users().GetInfo(ctx, resourceOwner) if err != nil { - return false, err + return false, clues.Stack(err) } if !info.ServiceEnabled(service) { @@ -208,7 +216,7 @@ func checkServiceEnabled( info, err := gi.GetInfo(ctx, resource) if err != nil { - return false, false, err + return false, false, clues.Stack(err) } if !info.ServiceEnabled(service) { diff --git a/src/internal/m365/discovery/discovery.go b/src/internal/m365/discovery/discovery.go index fd6d073e1..1aae9e7e7 100644 --- a/src/internal/m365/discovery/discovery.go +++ b/src/internal/m365/discovery/discovery.go @@ -85,7 +85,12 @@ func GetUserInfo( return nil, err } - return client.Users().GetInfo(ctx, userID) + 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. diff --git a/src/internal/m365/graph/errors.go b/src/internal/m365/graph/errors.go index c2a1ce9c7..49bf5b0dd 100644 --- a/src/internal/m365/graph/errors.go +++ b/src/internal/m365/graph/errors.go @@ -31,7 +31,7 @@ const ( errorAccessDenied errorCode = "ErrorAccessDenied" itemNotFound errorCode = "ErrorItemNotFound" itemNotFoundShort errorCode = "itemNotFound" - mailboxNotEnabledForRESTAPI errorCode = "MailboxNotEnabledForRESTAPI" + MailboxNotEnabledForRESTAPI errorCode = "MailboxNotEnabledForRESTAPI" malwareDetected errorCode = "malwareDetected" // nameAlreadyExists occurs when a request with // @microsoft.graph.conflictBehavior=fail finds a conflicting file. @@ -39,7 +39,7 @@ const ( quotaExceeded errorCode = "ErrorQuotaExceeded" RequestResourceNotFound errorCode = "Request_ResourceNotFound" // 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" @@ -131,7 +131,7 @@ func IsErrQuotaExceeded(err error) bool { } func IsErrExchangeMailFolderNotFound(err error) bool { - return hasErrorCode(err, resourceNotFound, mailboxNotEnabledForRESTAPI) + return hasErrorCode(err, ResourceNotFound, MailboxNotEnabledForRESTAPI) } func IsErrUserNotFound(err error) bool { @@ -139,7 +139,7 @@ func IsErrUserNotFound(err error) bool { return true } - if hasErrorCode(err, resourceNotFound) { + if hasErrorCode(err, ResourceNotFound) { var odErr odataerrors.ODataErrorable if !errors.As(err, &odErr) { return false @@ -154,7 +154,7 @@ func IsErrUserNotFound(err error) bool { } func IsErrResourceNotFound(err error) bool { - return hasErrorCode(err, resourceNotFound) + return hasErrorCode(err, ResourceNotFound) } func IsErrCannotOpenFileAttachment(err error) bool { diff --git a/src/internal/m365/graph/errors_test.go b/src/internal/m365/graph/errors_test.go index c291c8b8e..90ec23502 100644 --- a/src/internal/m365/graph/errors_test.go +++ b/src/internal/m365/graph/errors_test.go @@ -235,7 +235,7 @@ func (suite *GraphErrorsUnitSuite) TestIsErrUserNotFound() { { name: "non-matching resource not found", err: func() error { - res := odErr(string(resourceNotFound)) + res := odErr(string(ResourceNotFound)) res.GetError().SetMessage(ptr.To("Calendar not found")) return res @@ -255,7 +255,7 @@ func (suite *GraphErrorsUnitSuite) TestIsErrUserNotFound() { { name: "resource not found oDataErr", err: func() error { - res := odErr(string(resourceNotFound)) + res := odErr(string(ResourceNotFound)) res.GetError().SetMessage(ptr.To("User not found")) return res diff --git a/src/pkg/services/m365/api/helper_test.go b/src/pkg/services/m365/api/helper_test.go index 8db0a9fd6..7d0764ecd 100644 --- a/src/pkg/services/m365/api/helper_test.go +++ b/src/pkg/services/m365/api/helper_test.go @@ -1,9 +1,15 @@ package api_test import ( + "encoding/json" + "strings" "testing" "github.com/alcionai/clues" + "github.com/h2non/gock" + "github.com/microsoft/kiota-abstractions-go/serialization" + kjson "github.com/microsoft/kiota-serialization-json-go" + "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" "github.com/stretchr/testify/require" "github.com/alcionai/corso/src/internal/common/ptr" @@ -12,6 +18,59 @@ import ( "github.com/alcionai/corso/src/pkg/services/m365/api/mock" ) +// --------------------------------------------------------------------------- +// Intercepting calls with Gock +// --------------------------------------------------------------------------- + +const graphAPIHostURL = "https://graph.microsoft.com" + +func v1APIURLPath(parts ...string) string { + return strings.Join(append([]string{"/v1.0"}, parts...), "/") +} + +func interceptV1Path(pathParts ...string) *gock.Request { + return gock.New(graphAPIHostURL).Get(v1APIURLPath(pathParts...)) +} + +func odErr(code string) *odataerrors.ODataError { + odErr := odataerrors.NewODataError() + merr := odataerrors.NewMainError() + merr.SetCode(&code) + odErr.SetError(merr) + + return odErr +} + +func odErrMsg(code, message string) *odataerrors.ODataError { + odErr := odataerrors.NewODataError() + merr := odataerrors.NewMainError() + merr.SetCode(&code) + merr.SetMessage(&message) + odErr.SetError(merr) + + return odErr +} + +func parseableToMap(t *testing.T, thing serialization.Parsable) map[string]any { + sw := kjson.NewJsonSerializationWriter() + + err := sw.WriteObjectValue("", thing) + require.NoError(t, err, "serialize") + + content, err := sw.GetSerializedContent() + require.NoError(t, err, "serialize") + + var out map[string]any + err = json.Unmarshal([]byte(content), &out) + require.NoError(t, err, "unmarshall") + + return out +} + +// --------------------------------------------------------------------------- +// Suite Setup +// --------------------------------------------------------------------------- + type intgTesterSetup struct { ac api.Client gockAC api.Client diff --git a/src/pkg/services/m365/api/mail_test.go b/src/pkg/services/m365/api/mail_test.go index e3a84e4bb..3ad0ced28 100644 --- a/src/pkg/services/m365/api/mail_test.go +++ b/src/pkg/services/m365/api/mail_test.go @@ -1,14 +1,11 @@ package api_test import ( - "encoding/json" "testing" "time" "github.com/alcionai/clues" "github.com/h2non/gock" - "github.com/microsoft/kiota-abstractions-go/serialization" - kjson "github.com/microsoft/kiota-serialization-json-go" "github.com/microsoftgraph/msgraph-sdk-go/models" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -221,22 +218,6 @@ func (suite *MailAPIIntgSuite) SetupSuite() { suite.user = tester.M365UserID(t) } -func getJSONObject(t *testing.T, thing serialization.Parsable) map[string]any { - sw := kjson.NewJsonSerializationWriter() - - err := sw.WriteObjectValue("", thing) - require.NoError(t, err, "serialize") - - content, err := sw.GetSerializedContent() - require.NoError(t, err, "serialize") - - var out map[string]any - err = json.Unmarshal([]byte(content), &out) - require.NoError(t, err, "unmarshall") - - return out -} - func (suite *MailAPIIntgSuite) TestHugeAttachmentListDownload() { mid := "fake-message-id" aid := "fake-attachment-id" @@ -254,10 +235,9 @@ func (suite *MailAPIIntgSuite) TestHugeAttachmentListDownload() { mitem := models.NewMessage() mitem.SetId(&mid) - gock.New("https://graph.microsoft.com"). - Get("/v1.0/users/user/messages/" + mid). + interceptV1Path("users", "user", "messages", mid). Reply(200). - JSON(getJSONObject(suite.T(), mitem)) + JSON(parseableToMap(suite.T(), mitem)) }, expect: assert.NoError, }, @@ -268,10 +248,9 @@ func (suite *MailAPIIntgSuite) TestHugeAttachmentListDownload() { mitem.SetId(&mid) mitem.SetHasAttachments(ptr.To(true)) - gock.New("https://graph.microsoft.com"). - Get("/v1.0/users/user/messages/" + mid). + interceptV1Path("users", "user", "messages", mid). Reply(200). - JSON(getJSONObject(suite.T(), mitem)) + JSON(parseableToMap(suite.T(), mitem)) atts := models.NewAttachmentCollectionResponse() aitem := models.NewAttachment() @@ -280,10 +259,9 @@ func (suite *MailAPIIntgSuite) TestHugeAttachmentListDownload() { aitem.SetSize(&asize) atts.SetValue([]models.Attachmentable{aitem}) - gock.New("https://graph.microsoft.com"). - Get("/v1.0/users/user/messages/" + mid + "/attachments"). + interceptV1Path("users", "user", "messages", mid, "attachments"). Reply(200). - JSON(getJSONObject(suite.T(), atts)) + JSON(parseableToMap(suite.T(), atts)) }, attachmentCount: 1, size: 50, @@ -297,10 +275,9 @@ func (suite *MailAPIIntgSuite) TestHugeAttachmentListDownload() { mitem.SetId(&mid) mitem.SetHasAttachments(&truthy) - gock.New("https://graph.microsoft.com"). - Get("/v1.0/users/user/messages/" + mid). + interceptV1Path("users", "user", "messages", mid). Reply(200). - JSON(getJSONObject(suite.T(), mitem)) + JSON(parseableToMap(suite.T(), mitem)) atts := models.NewAttachmentCollectionResponse() aitem := models.NewAttachment() @@ -311,19 +288,16 @@ func (suite *MailAPIIntgSuite) TestHugeAttachmentListDownload() { atts.SetValue([]models.Attachmentable{aitem}) - gock.New("https://graph.microsoft.com"). - Get("/v1.0/users/user/messages/" + mid + "/attachments"). + interceptV1Path("users", "user", "messages", mid, "attachments"). Reply(503) - gock.New("https://graph.microsoft.com"). - Get("/v1.0/users/user/messages/" + mid + "/attachments"). + interceptV1Path("users", "user", "messages", mid, "attachments"). Reply(200). - JSON(getJSONObject(suite.T(), atts)) + JSON(parseableToMap(suite.T(), atts)) - gock.New("https://graph.microsoft.com"). - Get("/v1.0/users/user/messages/" + mid + "/attachments/" + aid). + interceptV1Path("users", "user", "messages", mid, "attachments", aid). Reply(200). - JSON(getJSONObject(suite.T(), aitem)) + JSON(parseableToMap(suite.T(), aitem)) }, attachmentCount: 1, size: 200, @@ -337,10 +311,9 @@ func (suite *MailAPIIntgSuite) TestHugeAttachmentListDownload() { mitem.SetId(&mid) mitem.SetHasAttachments(&truthy) - gock.New("https://graph.microsoft.com"). - Get("/v1.0/users/user/messages/" + mid). + interceptV1Path("users", "user", "messages", mid). Reply(200). - JSON(getJSONObject(suite.T(), mitem)) + JSON(parseableToMap(suite.T(), mitem)) atts := models.NewAttachmentCollectionResponse() aitem := models.NewAttachment() @@ -351,20 +324,17 @@ func (suite *MailAPIIntgSuite) TestHugeAttachmentListDownload() { atts.SetValue([]models.Attachmentable{aitem, aitem, aitem, aitem, aitem}) - gock.New("https://graph.microsoft.com"). - Get("/v1.0/users/user/messages/" + mid + "/attachments"). + interceptV1Path("users", "user", "messages", mid, "attachments"). Reply(503) - gock.New("https://graph.microsoft.com"). - Get("/v1.0/users/user/messages/" + mid + "/attachments"). + interceptV1Path("users", "user", "messages", mid, "attachments"). Reply(200). - JSON(getJSONObject(suite.T(), atts)) + JSON(parseableToMap(suite.T(), atts)) for i := 0; i < 5; i++ { - gock.New("https://graph.microsoft.com"). - Get("/v1.0/users/user/messages/" + mid + "/attachments/" + aid). + interceptV1Path("users", "user", "messages", mid, "attachments", aid). Reply(200). - JSON(getJSONObject(suite.T(), aitem)) + JSON(parseableToMap(suite.T(), aitem)) } }, attachmentCount: 5, diff --git a/src/pkg/services/m365/api/sites.go b/src/pkg/services/m365/api/sites.go index a73703703..4a43c87b6 100644 --- a/src/pkg/services/m365/api/sites.go +++ b/src/pkg/services/m365/api/sites.go @@ -35,21 +35,29 @@ type Sites struct { // api calls // --------------------------------------------------------------------------- -// GetAll retrieves all sites. -func (c Sites) GetAll(ctx context.Context, errs *fault.Bus) ([]models.Siteable, error) { - service, err := c.Service() +func (c Sites) GetRoot(ctx context.Context) (models.Siteable, error) { + resp, err := c.Stable. + Client(). + Sites(). + BySiteId("root"). + Get(ctx, nil) if err != nil { - return nil, err + return nil, graph.Wrap(ctx, err, "getting root site") } - resp, err := service.Client().Sites().Get(ctx, nil) + return resp, nil +} + +// GetAll retrieves all sites. +func (c Sites) GetAll(ctx context.Context, errs *fault.Bus) ([]models.Siteable, error) { + resp, err := c.Stable.Client().Sites().Get(ctx, nil) if err != nil { return nil, graph.Wrap(ctx, err, "getting all sites") } iter, err := msgraphgocore.NewPageIterator[models.Siteable]( resp, - service.Adapter(), + c.Stable.Adapter(), models.CreateSiteCollectionResponseFromDiscriminatorValue) if err != nil { return nil, graph.Wrap(ctx, err, "creating sites iterator") @@ -65,8 +73,8 @@ func (c Sites) GetAll(ctx context.Context, errs *fault.Bus) ([]models.Siteable, return false } - err := validateSite(item) - if errors.Is(err, errKnownSkippableCase) { + err := ValidateSite(item) + if errors.Is(err, ErrKnownSkippableCase) { // safe to no-op return true } @@ -182,14 +190,14 @@ func (c Sites) GetDefaultDrive( // helpers // --------------------------------------------------------------------------- -var errKnownSkippableCase = clues.New("case is known and skippable") +var ErrKnownSkippableCase = clues.New("case is known and skippable") -const personalSitePath = "sharepoint.com/personal/" +const PersonalSitePath = "sharepoint.com/personal/" -// validateSite ensures the item is a Siteable, and contains the necessary +// ValidateSite ensures the item is a Siteable, and contains the necessary // identifiers that we handle with all users. // returns the item as a Siteable model. -func validateSite(item models.Siteable) error { +func ValidateSite(item models.Siteable) error { id := ptr.Val(item.GetId()) if len(id) == 0 { return clues.New("missing ID") @@ -201,8 +209,8 @@ func validateSite(item models.Siteable) error { } // personal (ie: oneDrive) sites have to be filtered out server-side. - if strings.Contains(wURL, personalSitePath) { - return clues.Stack(errKnownSkippableCase). + if strings.Contains(wURL, PersonalSitePath) { + return clues.Stack(ErrKnownSkippableCase). With("site_id", id, "site_web_url", wURL) // TODO: pii } @@ -210,7 +218,7 @@ func validateSite(item models.Siteable) error { if len(name) == 0 { // the built-in site at "https://{tenant-domain}/search" never has a name. if strings.HasSuffix(wURL, "/search") { - return clues.Stack(errKnownSkippableCase). + return clues.Stack(ErrKnownSkippableCase). With("site_id", id, "site_web_url", wURL) // TODO: pii } diff --git a/src/pkg/services/m365/api/sites_test.go b/src/pkg/services/m365/api/sites_test.go index 68117d4ef..d9f16a324 100644 --- a/src/pkg/services/m365/api/sites_test.go +++ b/src/pkg/services/m365/api/sites_test.go @@ -1,4 +1,4 @@ -package api +package api_test import ( "strings" @@ -13,8 +13,8 @@ import ( "github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/internal/tester" - "github.com/alcionai/corso/src/pkg/account" "github.com/alcionai/corso/src/pkg/fault" + "github.com/alcionai/corso/src/pkg/services/m365/api" ) type SitesUnitSuite struct { @@ -77,7 +77,7 @@ func (suite *SitesUnitSuite) TestValidateSite() { args: func() *models.Site { s := models.NewSite() s.SetId(ptr.To("id")) - s.SetWebUrl(ptr.To("https://" + personalSitePath + "/someone's/onedrive")) + s.SetWebUrl(ptr.To("https://" + api.PersonalSitePath + "/someone's/onedrive")) return s }(), errCheck: assert.Error, @@ -93,11 +93,11 @@ func (suite *SitesUnitSuite) TestValidateSite() { suite.Run(test.name, func() { t := suite.T() - err := validateSite(test.args) + err := api.ValidateSite(test.args) test.errCheck(t, err, clues.ToCore(err)) if test.errIsSkippable { - assert.ErrorIs(t, err, errKnownSkippableCase) + assert.ErrorIs(t, err, api.ErrKnownSkippableCase) } }) } @@ -105,8 +105,7 @@ func (suite *SitesUnitSuite) TestValidateSite() { type SitesIntgSuite struct { tester.Suite - - creds account.M365Config + its intgTesterSetup } func TestSitesIntgSuite(t *testing.T) { @@ -118,15 +117,7 @@ func TestSitesIntgSuite(t *testing.T) { } func (suite *SitesIntgSuite) SetupSuite() { - var ( - t = suite.T() - acct = tester.NewM365Account(t) - ) - - m365, err := acct.M365Config() - require.NoError(t, err, clues.ToCore(err)) - - suite.creds = m365 + suite.its = newIntegrationTesterSetup(suite.T()) } func (suite *SitesIntgSuite) TestGetAll() { @@ -135,15 +126,12 @@ func (suite *SitesIntgSuite) TestGetAll() { ctx, flush := tester.NewContext(t) defer flush() - cli, err := NewClient(suite.creds) - require.NoError(t, err, clues.ToCore(err)) - - sites, err := cli.Sites().GetAll(ctx, fault.New(true)) + sites, err := suite.its.ac.Sites().GetAll(ctx, fault.New(true)) require.NoError(t, err) require.NotZero(t, len(sites), "must have at least one site") for _, site := range sites { - assert.NotContains(t, ptr.Val(site.GetWebUrl()), personalSitePath, "must not return onedrive sites") + assert.NotContains(t, ptr.Val(site.GetWebUrl()), api.PersonalSitePath, "must not return onedrive sites") } } @@ -154,16 +142,9 @@ func (suite *SitesIntgSuite) TestSites_GetByID() { host = strings.Split(siteID, ",")[0] shortID = strings.TrimPrefix(siteID, host+",") siteURL = tester.M365SiteURL(t) - acct = tester.NewM365Account(t) ) - creds, err := acct.M365Config() - require.NoError(t, err, clues.ToCore(err)) - - client, err := NewClient(creds) - require.NoError(t, err, clues.ToCore(err)) - - sitesAPI := client.Sites() + sitesAPI := suite.its.ac.Sites() table := []struct { name string @@ -191,3 +172,15 @@ func (suite *SitesIntgSuite) TestSites_GetByID() { }) } } + +func (suite *SitesIntgSuite) TestGetRoot() { + t := suite.T() + + ctx, flush := tester.NewContext(t) + defer flush() + + result, err := suite.its.ac.Sites().GetRoot(ctx) + require.NoError(t, err) + require.NotNil(t, result, "must find the root site") + require.NotEmpty(t, ptr.Val(result.GetId()), "must have an id") +} diff --git a/src/pkg/services/m365/api/users.go b/src/pkg/services/m365/api/users.go index 1f21384be..41382268f 100644 --- a/src/pkg/services/m365/api/users.go +++ b/src/pkg/services/m365/api/users.go @@ -95,7 +95,7 @@ func (c Users) GetAll( return false } - err := validateUser(item) + err := ValidateUser(item) if err != nil { el.AddRecoverable(ctx, graph.Wrap(ctx, err, "validating user")) } else { @@ -175,17 +175,16 @@ func (c Users) GetInfo(ctx context.Context, userID string) (*UserInfo, error) { var ( // Assume all services are enabled // then filter down to only services the user has enabled - userInfo = newUserInfo() - + userInfo = newUserInfo() mailFolderFound = true ) // check whether the user is able to access their onedrive drive. // if they cannot, we can assume they are ineligible for onedrive backups. if _, err := c.GetDefaultDrive(ctx, userID); err != nil { - if !clues.HasLabel(err, graph.LabelsMysiteNotFound) || clues.HasLabel(err, graph.LabelsNoSharePointLicense) { - logger.CtxErr(ctx, err).Error("getting user's drive") - return nil, graph.Wrap(ctx, err, "getting user's drive") + if !clues.HasLabel(err, graph.LabelsMysiteNotFound) && !clues.HasLabel(err, graph.LabelsNoSharePointLicense) { + logger.CtxErr(ctx, err).Error("getting user's default drive") + return nil, graph.Wrap(ctx, err, "getting user's default drive info") } logger.Ctx(ctx).Info("resource owner does not have a drive") @@ -345,9 +344,9 @@ func (c Users) getFirstInboxMessage( // helpers // --------------------------------------------------------------------------- -// validateUser ensures the item is a Userable, and contains the necessary +// ValidateUser ensures the item is a Userable, and contains the necessary // identifiers that we handle with all users. -func validateUser(item models.Userable) error { +func ValidateUser(item models.Userable) error { if item.GetId() == nil { return clues.New("missing ID") } diff --git a/src/pkg/services/m365/api/users_test.go b/src/pkg/services/m365/api/users_test.go index f1c554576..fc786991e 100644 --- a/src/pkg/services/m365/api/users_test.go +++ b/src/pkg/services/m365/api/users_test.go @@ -1,14 +1,17 @@ -package api +package api_test import ( "testing" "github.com/alcionai/clues" + "github.com/h2non/gock" "github.com/microsoftgraph/msgraph-sdk-go/models" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" + "github.com/alcionai/corso/src/internal/m365/graph" "github.com/alcionai/corso/src/internal/tester" + "github.com/alcionai/corso/src/pkg/services/m365/api" ) type UsersUnitSuite struct { @@ -57,8 +60,129 @@ func (suite *UsersUnitSuite) TestValidateUser() { suite.Run(tt.name, func() { t := suite.T() - err := validateUser(tt.args) + err := api.ValidateUser(tt.args) tt.errCheck(t, err, clues.ToCore(err)) }) } } + +type UsersIntgSuite struct { + tester.Suite + its intgTesterSetup +} + +func TestUsersIntgSuite(t *testing.T) { + suite.Run(t, &UsersIntgSuite{ + Suite: tester.NewIntegrationSuite( + t, + [][]string{tester.M365AcctCredEnvs}), + }) +} + +func (suite *UsersIntgSuite) SetupSuite() { + suite.its = newIntegrationTesterSetup(suite.T()) +} + +func (suite *UsersIntgSuite) TestUsers_GetInfo_errors() { + table := []struct { + name string + setGocks func(t *testing.T) + expectErr func(t *testing.T, err error) + }{ + { + name: "default drive err - mysite not found", + setGocks: func(t *testing.T) { + interceptV1Path("users", "user", "drive"). + Reply(400). + JSON(parseableToMap(t, odErrMsg("anycode", string(graph.MysiteNotFound)))) + interceptV1Path("users", "user", "mailFolders", "inbox"). + Reply(400). + JSON(parseableToMap(t, odErr(string(graph.ResourceNotFound)))) + }, + expectErr: func(t *testing.T, err error) { + assert.NoError(t, err, clues.ToCore(err)) + }, + }, + { + name: "default drive err - no sharepoint license", + setGocks: func(t *testing.T) { + interceptV1Path("users", "user", "drive"). + Reply(400). + JSON(parseableToMap(t, odErrMsg("anycode", string(graph.NoSPLicense)))) + interceptV1Path("users", "user", "mailFolders", "inbox"). + Reply(400). + JSON(parseableToMap(t, odErr(string(graph.ResourceNotFound)))) + }, + expectErr: func(t *testing.T, err error) { + assert.NoError(t, err, clues.ToCore(err)) + }, + }, + { + name: "default drive err - other error", + setGocks: func(t *testing.T) { + interceptV1Path("users", "user", "drive"). + Reply(400). + JSON(parseableToMap(t, odErrMsg("somecode", "somemessage"))) + }, + expectErr: func(t *testing.T, err error) { + assert.Error(t, err, clues.ToCore(err)) + }, + }, + { + name: "mail inbox err - user not found", + setGocks: func(t *testing.T) { + interceptV1Path("users", "user", "drive"). + Reply(200). + JSON(parseableToMap(t, models.NewDrive())) + interceptV1Path("users", "user", "mailFolders", "inbox"). + Reply(400). + JSON(parseableToMap(t, odErr(string(graph.RequestResourceNotFound)))) + }, + expectErr: func(t *testing.T, err error) { + assert.ErrorIs(t, err, graph.ErrResourceOwnerNotFound, clues.ToCore(err)) + }, + }, + { + name: "mail inbox err - user not found", + setGocks: func(t *testing.T) { + interceptV1Path("users", "user", "drive"). + Reply(200). + JSON(parseableToMap(t, models.NewDrive())) + interceptV1Path("users", "user", "mailFolders", "inbox"). + Reply(400). + JSON(parseableToMap(t, odErr(string(graph.MailboxNotEnabledForRESTAPI)))) + }, + expectErr: func(t *testing.T, err error) { + assert.NoError(t, err, clues.ToCore(err)) + }, + }, + { + name: "mail inbox err - other error", + setGocks: func(t *testing.T) { + interceptV1Path("users", "user", "drive"). + Reply(200). + JSON(parseableToMap(t, models.NewDrive())) + interceptV1Path("users", "user", "mailFolders", "inbox"). + Reply(400). + JSON(parseableToMap(t, odErrMsg("somecode", "somemessage"))) + }, + expectErr: func(t *testing.T, err error) { + assert.Error(t, err, clues.ToCore(err)) + }, + }, + } + for _, test := range table { + suite.Run(test.name, func() { + t := suite.T() + ctx, flush := tester.NewContext(t) + + defer flush() + defer gock.Off() + + test.setGocks(t) + + _, err := suite.its.gockAC.Users().GetInfo(ctx, "user") + test.expectErr(t, err) + }) + } +}