From 06d2a389d9031fc0169b218309c0c18978c2bcc0 Mon Sep 17 00:00:00 2001 From: Danny Date: Mon, 26 Sep 2022 13:33:33 -0400 Subject: [PATCH] GC: Backup: Events: Filter Calendars with Scope (#900) ## Description Feature branch enabling filtering based upon `selectors.ExchangeScope` ## Type of change - [x] :sunflower: Feature ## Issue(s) *closes #810 ## Test Plan - [x] :zap: Unit test --- .../connector/exchange/iterators_test.go | 9 +- .../exchange/service_functions_test.go | 90 +++++- .../connector/exchange/service_iterators.go | 291 ++++++++++++------ .../connector/exchange/service_query.go | 7 +- .../connector/graph_connector_test.go | 199 +++++++----- src/internal/connector/onedrive/item_test.go | 1 - 6 files changed, 421 insertions(+), 176 deletions(-) diff --git a/src/internal/connector/exchange/iterators_test.go b/src/internal/connector/exchange/iterators_test.go index f8cf14d87..cea356ce0 100644 --- a/src/internal/connector/exchange/iterators_test.go +++ b/src/internal/connector/exchange/iterators_test.go @@ -58,12 +58,7 @@ func (suite *ExchangeIteratorSuite) TestDescendable() { } func loadService(t *testing.T) *exchangeService { - _, err := tester.GetRequiredEnvVars(tester.M365AcctCredEnvs...) - require.NoError(t, err) - a := tester.NewM365Account(t) - require.NoError(t, err) - m365, err := a.M365Config() require.NoError(t, err) @@ -145,7 +140,7 @@ func (suite *ExchangeIteratorSuite) TestIterativeFunctions() { }, { name: "Folder Iterative Check Mail", queryFunction: GetAllFolderNamesForUser, - iterativeFunction: IterateFilterFolderDirectoriesForCollections, + iterativeFunction: IterateFilterContainersForCollections, scope: mailScope, transformer: models.CreateMailFolderCollectionResponseFromDiscriminatorValue, folderNames: map[string]struct{}{ @@ -156,7 +151,7 @@ func (suite *ExchangeIteratorSuite) TestIterativeFunctions() { }, { name: "Folder Iterative Check Contacts", queryFunction: GetAllContactFolderNamesForUser, - iterativeFunction: IterateFilterFolderDirectoriesForCollections, + iterativeFunction: IterateFilterContainersForCollections, scope: contactScope, transformer: models.CreateContactFolderCollectionResponseFromDiscriminatorValue, }, diff --git a/src/internal/connector/exchange/service_functions_test.go b/src/internal/connector/exchange/service_functions_test.go index 6d62219b2..c529214a1 100644 --- a/src/internal/connector/exchange/service_functions_test.go +++ b/src/internal/connector/exchange/service_functions_test.go @@ -5,9 +5,12 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" + "github.com/alcionai/corso/src/internal/connector/graph" "github.com/alcionai/corso/src/internal/tester" + "github.com/alcionai/corso/src/pkg/selectors" ) type ServiceFunctionsIntegrationSuite struct { @@ -77,6 +80,7 @@ func (suite *ServiceFunctionsIntegrationSuite) TestGetAllCalendars() { func (suite *ServiceFunctionsIntegrationSuite) TestGetAllContactFolders() { gs := loadService(suite.T()) + user := tester.M365UserID(suite.T()) ctx := context.Background() table := []struct { @@ -86,14 +90,14 @@ func (suite *ServiceFunctionsIntegrationSuite) TestGetAllContactFolders() { }{ { name: "plain lookup", - user: suite.m365UserID, + user: user, expectCount: assert.Greater, expectErr: assert.NoError, }, { name: "root folder", contains: "Contact", - user: suite.m365UserID, + user: user, expectCount: assert.Greater, expectErr: assert.NoError, }, @@ -106,7 +110,7 @@ func (suite *ServiceFunctionsIntegrationSuite) TestGetAllContactFolders() { { name: "nonsense matcher", contains: "∂ç∂ç∂√≈∂ƒß∂ç√ßç√≈ç√ß∂ƒçß√ß≈∂ƒßç√", - user: suite.m365UserID, + user: user, expectCount: assert.Equal, expectErr: assert.NoError, }, @@ -164,3 +168,83 @@ func (suite *ServiceFunctionsIntegrationSuite) TestGetAllMailFolders() { }) } } + +func (suite *ServiceFunctionsIntegrationSuite) TestCollectContainers() { + ctx := context.Background() + failFast := false + containerCount := 1 + t := suite.T() + user := tester.M365UserID(t) + a := tester.NewM365Account(t) + credentials, err := a.M365Config() + require.NoError(t, err) + + tests := []struct { + name, contains string + getScope func() selectors.ExchangeScope + expectedCount assert.ComparisonAssertionFunc + }{ + { + name: "All Events", + contains: "Birthdays", + expectedCount: assert.Greater, + getScope: func() selectors.ExchangeScope { + sel := selectors.NewExchangeBackup() + sel.Include(sel.EventCalendars([]string{user}, selectors.Any())) + + scopes := sel.Scopes() + assert.Equal(t, len(scopes), 1) + + return scopes[0] + }, + }, { + name: "Default Calendar", + contains: DefaultCalendar, + expectedCount: assert.Equal, + getScope: func() selectors.ExchangeScope { + sel := selectors.NewExchangeBackup() + sel.Include(sel.EventCalendars([]string{user}, []string{DefaultCalendar})) + + scopes := sel.Scopes() + assert.Equal(t, len(scopes), 1) + + return scopes[0] + }, + }, { + name: "Default Mail", + contains: DefaultMailFolder, + expectedCount: assert.Equal, + getScope: func() selectors.ExchangeScope { + sel := selectors.NewExchangeBackup() + sel.Include(sel.MailFolders([]string{user}, []string{DefaultMailFolder})) + + scopes := sel.Scopes() + assert.Equal(t, len(scopes), 1) + + return scopes[0] + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + qp := graph.QueryParams{ + User: user, + Scope: test.getScope(), + FailFast: failFast, + Credentials: credentials, + } + collections := make(map[string]*Collection) + err := CollectFolders(ctx, qp, collections, nil) + assert.NoError(t, err) + test.expectedCount(t, len(collections), containerCount) + + keys := make([]string, 0, len(collections)) + for k := range collections { + keys = append(keys, k) + } + t.Logf("Collections Made: %v\n", keys) + assert.Contains(t, keys, test.contains) + }) + } +} diff --git a/src/internal/connector/exchange/service_iterators.go b/src/internal/connector/exchange/service_iterators.go index 33fda518e..920899537 100644 --- a/src/internal/connector/exchange/service_iterators.go +++ b/src/internal/connector/exchange/service_iterators.go @@ -2,6 +2,7 @@ package exchange import ( "context" + "fmt" msgraphgocore "github.com/microsoftgraph/msgraph-sdk-go-core" "github.com/microsoftgraph/msgraph-sdk-go/models" @@ -117,6 +118,9 @@ func IterateSelectAllDescendablesForCollections( // utility function for iterating through events // and storing events in collections based on // the calendarID which originates from M365. +// @param pageItem is a CalendarCollectionResponse possessing two populated fields: +// - id - M365 ID +// - Name - Calendar Name func IterateSelectAllEventsFromCalendars( ctx context.Context, qp graph.QueryParams, @@ -124,83 +128,62 @@ func IterateSelectAllEventsFromCalendars( collections map[string]*Collection, statusUpdater support.StatusUpdater, ) func(any) bool { + var ( + isEnabled bool + service graph.Service + ) + return func(pageItem any) bool { - if pageItem == nil { - return true + if !isEnabled { + // Create Collections based on qp.Scope + err := CollectFolders(ctx, qp, collections, statusUpdater) + if err != nil { + errUpdater( + qp.User, + errors.Wrap(err, support.ConnectorStackErrorTrace(err)), + ) + + return false + } + + service, err = createService(qp.Credentials, qp.FailFast) + if err != nil { + errUpdater(qp.User, err) + return false + } + + isEnabled = true } - shell, ok := pageItem.(models.Calendarable) + pageItem = CreateCalendarDisplayable(pageItem) + + calendar, ok := pageItem.(displayable) if !ok { - errUpdater(qp.User, errors.New("casting pageItem to models.Calendarable")) + errUpdater( + qp.User, + fmt.Errorf("unable to parse pageItem into CalendarDisplayable: %T", pageItem), + ) + } + + if calendar.GetDisplayName() == nil { return true } - service, err := createService(qp.Credentials, qp.FailFast) + collection, ok := collections[*calendar.GetDisplayName()] + if !ok { + return true + } + + eventIDs, err := ReturnEventIDsFromCalendar(ctx, service, qp.User, *calendar.GetId()) if err != nil { errUpdater( qp.User, - errors.Wrap(err, "creating service for IterateSelectAllEventsFromCalendars")) + errors.Wrap(err, support.ConnectorStackErrorTrace(err))) return true } - eventResponseable, err := service.Client(). - UsersById(qp.User). - CalendarsById(*shell.GetId()). - Events().Get(ctx, nil) - if err != nil { - errUpdater(qp.User, err) - } - - directory := shell.GetName() - owner := shell.GetOwner() - - // Conditional Guard Checks - if eventResponseable == nil || - directory == nil || - owner == nil { - return true - } - - eventables := eventResponseable.GetValue() - // Clause is true when Calendar has does not have any events - if eventables == nil { - return true - } - - if _, ok := collections[*directory]; !ok { - service, err := createService(qp.Credentials, qp.FailFast) - if err != nil { - errUpdater(qp.User, err) - - return true - } - - dirPath, err := path.Builder{}.Append(*directory).ToDataLayerExchangePathForCategory( - qp.Credentials.TenantID, - qp.User, - path.EventsCategory, - false, - ) - if err != nil { - // we should never hit this error - errUpdater("converting to resource path", err) - return true - } - - edc := NewCollection( - qp.User, - dirPath, - events, - service, - statusUpdater, - ) - collections[*directory] = &edc - } - - for _, event := range eventables { - collections[*directory].AddJob(*event.GetId()) - } + collection.jobs = append(collection.jobs, eventIDs...) return true } @@ -216,7 +199,11 @@ func IterateAndFilterDescendablesForCollections( collections map[string]*Collection, statusUpdater support.StatusUpdater, ) func(any) bool { - var isFilterSet bool + var ( + isFilterSet bool + resolver graph.ContainerResolver + cache map[string]string + ) return func(descendItem any) bool { if !isFilterSet { @@ -230,6 +217,13 @@ func IterateAndFilterDescendablesForCollections( errUpdater(qp.User, err) return false } + // Caches folder directories + cache = make(map[string]string, 0) + + resolver, err = maybeGetAndPopulateFolderResolver(ctx, qp, path.EmailCategory) + if err != nil { + errUpdater("getting folder resolver for category "+path.EmailCategory.String(), err) + } isFilterSet = true } @@ -240,7 +234,20 @@ func IterateAndFilterDescendablesForCollections( return true } // Saving only messages for the created directories - directory := *message.GetParentFolderId() + folderID := *message.GetParentFolderId() + + directory, ok := cache[folderID] + if !ok { + result := translateIDToDirectory(ctx, qp, resolver, folderID) + if result == "" { + errUpdater(qp.User, + errors.New("getCollectionPath experienced error during translateID")) + } + + cache[folderID] = result + directory = result + } + if _, ok = collections[directory]; !ok { return true } @@ -251,7 +258,65 @@ func IterateAndFilterDescendablesForCollections( } } -func IterateFilterFolderDirectoriesForCollections( +func translateIDToDirectory( + ctx context.Context, + qp graph.QueryParams, + resolver graph.ContainerResolver, + directoryID string, +) string { + fullPath, err := getCollectionPath(ctx, qp, resolver, directoryID, path.EmailCategory) + if err != nil { + return "" + } + + return fullPath.Folder() +} + +func getCategoryAndValidation(es selectors.ExchangeScope) ( + optionIdentifier, + path.CategoryType, + func(namePtr *string) bool, +) { + var ( + option = scopeToOptionIdentifier(es) + category path.CategoryType + validate func(namePtr *string) bool + ) + + switch option { + case messages: + category = path.EmailCategory + validate = func(namePtr *string) bool { + if namePtr == nil { + return true + } + + return !es.Matches(selectors.ExchangeMailFolder, *namePtr) + } + case contacts: + category = path.ContactsCategory + validate = func(namePtr *string) bool { + if namePtr == nil { + return true + } + + return !es.Matches(selectors.ExchangeContactFolder, *namePtr) + } + case events: + category = path.EventsCategory + validate = func(namePtr *string) bool { + if namePtr == nil { + return true + } + + return !es.Matches(selectors.ExchangeEventCalendar, *namePtr) + } + } + + return option, category, validate +} + +func IterateFilterContainersForCollections( ctx context.Context, qp graph.QueryParams, errUpdater func(string, error), @@ -265,30 +330,12 @@ func IterateFilterFolderDirectoriesForCollections( err error option optionIdentifier category path.CategoryType - validate func(string) bool + validate func(*string) bool ) return func(folderItem any) bool { - folder, ok := folderItem.(displayable) - if !ok { - errUpdater(qp.User, errors.New("casting folderItem to displayable")) - return true - } - if !isSet { - option = scopeToOptionIdentifier(qp.Scope) - switch option { - case messages: - category = path.EmailCategory - validate = func(name string) bool { - return !qp.Scope.Matches(selectors.ExchangeMailFolder, name) - } - case contacts: - category = path.ContactsCategory - validate = func(name string) bool { - return !qp.Scope.Matches(selectors.ExchangeContactFolder, name) - } - } + option, category, validate = getCategoryAndValidation(qp.Scope) resolver, err = maybeGetAndPopulateFolderResolver(ctx, qp, category) if err != nil { @@ -298,19 +345,27 @@ func IterateFilterFolderDirectoriesForCollections( isSet = true } - // Continue to iterate if folder name is empty - if folder.GetDisplayName() == nil { + if option == events { + folderItem = CreateCalendarDisplayable(folderItem) + } + + folder, ok := folderItem.(displayable) + if !ok { + errUpdater(qp.User, + fmt.Errorf("unable to convert input of %T for category: %s", folderItem, category.String()), + ) + return true } - if validate(*folder.GetDisplayName()) { + if validate(folder.GetDisplayName()) { return true } - if option == contacts { - collectPath = *folder.GetDisplayName() - } else { + if option == messages { collectPath = *folder.GetId() + } else { + collectPath = *folder.GetDisplayName() } dirPath, err := getCollectionPath( @@ -345,7 +400,7 @@ func IterateFilterFolderDirectoriesForCollections( service, statusUpdater, ) - collections[*folder.GetId()] = &temp + collections[*folder.GetDisplayName()] = &temp return true } @@ -565,3 +620,53 @@ func ReturnContactIDsFromDirectory(ctx context.Context, gs graph.Service, user, return stringArray, nil } + +// ReturnEventIDsFromCalendar returns a list of all M365IDs of events of the targeted Calendar. +func ReturnEventIDsFromCalendar( + ctx context.Context, + gs graph.Service, + user, calendarID string, +) ([]string, error) { + ids := []string{} + + response, err := gs.Client(). + UsersById(user). + CalendarsById(calendarID). + Events().Get(ctx, nil) + if err != nil { + return nil, err + } + + if err != nil { + return nil, err + } + + pageIterator, err := msgraphgocore.NewPageIterator( + response, + gs.Adapter(), + models.CreateEventCollectionResponseFromDiscriminatorValue, + ) + + callbackFunc := func(pageItem any) bool { + entry, ok := pageItem.(models.Eventable) + if !ok { + err = errors.New("casting pageItem to models.Eventable") + return false + } + + ids = append(ids, *entry.GetId()) + + return true + } + + if iterateErr := pageIterator.Iterate(ctx, callbackFunc); iterateErr != nil { + return nil, + errors.Wrap(iterateErr, support.ConnectorStackErrorTrace(err)) + } + + if err != nil { + return nil, err + } + + return ids, nil +} diff --git a/src/internal/connector/exchange/service_query.go b/src/internal/connector/exchange/service_query.go index abf09fb66..88f4e7cbf 100644 --- a/src/internal/connector/exchange/service_query.go +++ b/src/internal/connector/exchange/service_query.go @@ -122,6 +122,8 @@ func RetrieveMessageDataForUser(ctx context.Context, gs graph.Service, user, m36 return gs.Client().UsersById(user).MessagesById(m365ID).Get(ctx, nil) } +// CollectFolders is a utility function for creating Collections based off parameters found +// in the ExchangeScope found in the graph.QueryParams func CollectFolders( ctx context.Context, qp graph.QueryParams, @@ -149,6 +151,9 @@ func CollectFolders( case contacts: query = GetAllContactFolderNamesForUser transformer = models.CreateContactFolderCollectionResponseFromDiscriminatorValue + case events: + query = GetAllCalendarNamesForUser + transformer = models.CreateCalendarCollectionResponseFromDiscriminatorValue default: return fmt.Errorf("unsupported option %s used in CollectFolders", option) } @@ -176,7 +181,7 @@ func CollectFolders( err = support.WrapAndAppend(id, e, err) } - callbackFunc := IterateFilterFolderDirectoriesForCollections( + callbackFunc := IterateFilterContainersForCollections( ctx, qp, errUpdater, diff --git a/src/internal/connector/graph_connector_test.go b/src/internal/connector/graph_connector_test.go index 35869910b..c185aa3a5 100644 --- a/src/internal/connector/graph_connector_test.go +++ b/src/internal/connector/graph_connector_test.go @@ -3,7 +3,6 @@ package connector import ( "bytes" "context" - "fmt" "testing" "time" @@ -14,7 +13,6 @@ import ( "github.com/alcionai/corso/src/internal/common" "github.com/alcionai/corso/src/internal/connector/exchange" "github.com/alcionai/corso/src/internal/connector/support" - "github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/pkg/selectors" ) @@ -85,36 +83,54 @@ func (suite *GraphConnectorIntegrationSuite) TestSetTenantUsers() { // - events func (suite *GraphConnectorIntegrationSuite) TestExchangeDataCollection() { ctx := context.Background() - t := suite.T() - connector := loadConnector(ctx, t) - sel := selectors.NewExchangeBackup() - sel.Include(sel.Users([]string{suite.user})) - collectionList, err := connector.ExchangeDataCollection(context.Background(), sel.Selector) - assert.NotNil(t, collectionList, "collection list") - assert.NoError(t, err) - assert.Zero(t, connector.status.ObjectCount) - assert.Zero(t, connector.status.FolderCount) - assert.Zero(t, connector.status.Successful) + connector := loadConnector(ctx, suite.T()) + tests := []struct { + name string + getSelector func(t *testing.T) selectors.Selector + }{ + { + name: suite.user + " Email", + getSelector: func(t *testing.T) selectors.Selector { + sel := selectors.NewExchangeBackup() + sel.Include(sel.MailFolders([]string{suite.user}, []string{exchange.DefaultMailFolder})) - streams := make(map[string]<-chan data.Stream) - // Verify Items() call returns an iterable channel(e.g. a channel that has been closed) - for _, collection := range collectionList { - temp := collection.Items() - testName := collection.FullPath().ResourceOwner() - streams[testName] = temp + return sel.Selector + }, + }, + { + name: suite.user + " Contacts", + getSelector: func(t *testing.T) selectors.Selector { + sel := selectors.NewExchangeBackup() + sel.Include(sel.ContactFolders([]string{suite.user}, []string{exchange.DefaultContactFolder})) + + return sel.Selector + }, + }, + { + name: suite.user + " Events", + getSelector: func(t *testing.T) selectors.Selector { + sel := selectors.NewExchangeBackup() + sel.Include(sel.EventCalendars([]string{suite.user}, []string{exchange.DefaultCalendar})) + + return sel.Selector + }, + }, } - status := connector.AwaitStatus() - assert.NotZero(t, status.Successful) - - for name, channel := range streams { - suite.T().Run(name, func(t *testing.T) { - t.Logf("Test: %s\t Items: %d", name, len(channel)) + for _, test := range tests { + suite.T().Run(test.name, func(t *testing.T) { + collection, err := connector.ExchangeDataCollection(ctx, test.getSelector(t)) + require.NoError(t, err) + assert.Equal(t, len(collection), 1) + channel := collection[0].Items() for object := range channel { buf := &bytes.Buffer{} _, err := buf.ReadFrom(object.ToReader()) assert.NoError(t, err, "received a buf.Read error") } + status := connector.AwaitStatus() + assert.NotZero(t, status.Successful) + t.Log(status.String()) }) } } @@ -127,7 +143,7 @@ func (suite *GraphConnectorIntegrationSuite) TestMailSerializationRegression() { t := suite.T() connector := loadConnector(ctx, t) sel := selectors.NewExchangeBackup() - sel.Include(sel.MailFolders([]string{suite.user}, []string{"Inbox"})) + sel.Include(sel.MailFolders([]string{suite.user}, []string{exchange.DefaultMailFolder})) eb, err := sel.ToExchangeBackup() require.NoError(t, err) @@ -162,27 +178,40 @@ func (suite *GraphConnectorIntegrationSuite) TestMailSerializationRegression() { // and to store contact within Collection. Downloaded contacts are run through // a regression test to ensure that downloaded items can be uploaded. func (suite *GraphConnectorIntegrationSuite) TestContactSerializationRegression() { - ctx := context.Background() - t := suite.T() - sel := selectors.NewExchangeBackup() - sel.Include(sel.ContactFolders([]string{suite.user}, selectors.Any())) - eb, err := sel.ToExchangeBackup() - require.NoError(t, err) + connector := loadConnector(context.Background(), suite.T()) - scopes := eb.Scopes() - connector := loadConnector(ctx, t) + tests := []struct { + name string + getCollection func(t *testing.T) []*exchange.Collection + }{ + { + name: "Default Contact Folder", + getCollection: func(t *testing.T) []*exchange.Collection { + sel := selectors.NewExchangeBackup() + sel.Include(sel.ContactFolders([]string{suite.user}, []string{exchange.DefaultContactFolder})) + eb, err := sel.ToExchangeBackup() + require.NoError(t, err) - suite.Len(scopes, 1) - contactsOnly := scopes[0] - collections, err := connector.createCollections(context.Background(), contactsOnly) - assert.NoError(t, err) + scopes := eb.Scopes() - number := 0 + suite.Len(scopes, 1) + contactsOnly := scopes[0] + collections, err := connector.createCollections(context.Background(), contactsOnly) + require.NoError(t, err) - for _, edc := range collections { - testName := fmt.Sprintf("%s_ContactFolder_%d", edc.FullPath().ResourceOwner(), number) - suite.T().Run(testName, func(t *testing.T) { + return collections + }, + }, + } + + for _, test := range tests { + suite.T().Run(test.name, func(t *testing.T) { + edcs := test.getCollection(t) + assert.Equal(t, len(edcs), 1) + edc := edcs[0] + assert.Equal(t, edc.FullPath().Folder(), exchange.DefaultContactFolder) streamChannel := edc.Items() + count := 0 for stream := range streamChannel { buf := &bytes.Buffer{} read, err := buf.ReadFrom(stream.ToReader()) @@ -190,52 +219,80 @@ func (suite *GraphConnectorIntegrationSuite) TestContactSerializationRegression( assert.NotZero(t, read) contact, err := support.CreateContactFromBytes(buf.Bytes()) assert.NotNil(t, contact) - assert.NoError(t, err) - + assert.NoError(t, err, "error on converting contact bytes: "+string(buf.Bytes())) + count++ } - number++ + assert.NotZero(t, count) + + status := connector.AwaitStatus() + suite.NotNil(status) + suite.Equal(status.ObjectCount, status.Successful) }) } - - status := connector.AwaitStatus() - suite.NotNil(status) - suite.Equal(status.ObjectCount, status.Successful) } // TestEventsSerializationRegression ensures functionality of createCollections // to be able to successfully query, download and restore event objects func (suite *GraphConnectorIntegrationSuite) TestEventsSerializationRegression() { - ctx := context.Background() - t := suite.T() - connector := loadConnector(ctx, t) - sel := selectors.NewExchangeBackup() - sel.Include(sel.EventCalendars([]string{suite.user}, selectors.Any())) - scopes := sel.Scopes() - suite.Equal(len(scopes), 1) - collections, err := connector.createCollections(context.Background(), scopes[0]) - require.NoError(t, err) + connector := loadConnector(context.Background(), suite.T()) - for _, edc := range collections { - streamChannel := edc.Items() - number := 0 + tests := []struct { + name, expected string + getCollection func(t *testing.T) []*exchange.Collection + }{ + { + name: "Default Event Calendar", + expected: exchange.DefaultCalendar, + getCollection: func(t *testing.T) []*exchange.Collection { + sel := selectors.NewExchangeBackup() + sel.Include(sel.EventCalendars([]string{suite.user}, []string{exchange.DefaultCalendar})) + scopes := sel.Scopes() + suite.Equal(len(scopes), 1) + collections, err := connector.createCollections(context.Background(), scopes[0]) + require.NoError(t, err) - for stream := range streamChannel { - testName := fmt.Sprintf("%s_Event_%d", edc.FullPath().ResourceOwner(), number) - suite.T().Run(testName, func(t *testing.T) { + return collections + }, + }, + { + name: "Birthday Calendar", + expected: "Birthdays", + getCollection: func(t *testing.T) []*exchange.Collection { + sel := selectors.NewExchangeBackup() + sel.Include(sel.EventCalendars([]string{suite.user}, []string{"Birthdays"})) + scopes := sel.Scopes() + suite.Equal(len(scopes), 1) + collections, err := connector.createCollections(context.Background(), scopes[0]) + require.NoError(t, err) + + return collections + }, + }, + } + + for _, test := range tests { + suite.T().Run(test.name, func(t *testing.T) { + collections := test.getCollection(t) + require.Equal(t, len(collections), 1) + edc := collections[0] + assert.Equal(t, edc.FullPath().Folder(), test.expected) + streamChannel := edc.Items() + + for stream := range streamChannel { buf := &bytes.Buffer{} read, err := buf.ReadFrom(stream.ToReader()) assert.NoError(t, err) assert.NotZero(t, read) event, err := support.CreateEventFromBytes(buf.Bytes()) assert.NotNil(t, event) - assert.NoError(t, err) - }) - } - } + assert.NoError(t, err, "experienced error parsing event bytes: "+string(buf.Bytes())) + } - status := connector.AwaitStatus() - suite.NotNil(status) - suite.Equal(status.ObjectCount, status.Successful) + status := connector.AwaitStatus() + suite.NotNil(status) + suite.Equal(status.ObjectCount, status.Successful) + }) + } } // TestAccessOfInboxAllUsers verifies that GraphConnector can @@ -249,7 +306,7 @@ func (suite *GraphConnectorIntegrationSuite) TestAccessOfInboxAllUsers() { t := suite.T() connector := loadConnector(ctx, t) sel := selectors.NewExchangeBackup() - sel.Include(sel.MailFolders(selectors.Any(), []string{"Inbox"})) + sel.Include(sel.MailFolders(selectors.Any(), []string{exchange.DefaultMailFolder})) scopes := sel.DiscreteScopes(connector.GetUsers()) for _, scope := range scopes { diff --git a/src/internal/connector/onedrive/item_test.go b/src/internal/connector/onedrive/item_test.go index 30e071573..f5f02f172 100644 --- a/src/internal/connector/onedrive/item_test.go +++ b/src/internal/connector/onedrive/item_test.go @@ -102,7 +102,6 @@ func (suite *ItemIntegrationSuite) TestItemReader() { ) // Read data for the file - name, itemData, err := driveItemReader(ctx, suite, driveID, driveItemID) require.NoError(suite.T(), err) require.NotEmpty(suite.T(), name)