From 0cba7a402cf143fb7ace208875501b8f5100d165 Mon Sep 17 00:00:00 2001 From: ashmrtn Date: Thu, 29 Sep 2022 17:32:28 -0700 Subject: [PATCH] Restore tests for contacts (#989) ## Description Test restoring a single contacts folder and multiple contacts folders. This tests that items from different folders can be restored, but not that items from multiple folders can be backed up (because the restore colocates into a single folder) ## Type of change - [ ] :sunflower: Feature - [ ] :bug: Bugfix - [ ] :world_map: Documentation - [x] :robot: Test - [ ] :computer: CI/Deployment - [ ] :hamster: Trivial/Minor ## Issue(s) * #913 merge after * #986 ## Test Plan - [ ] :muscle: Manual - [x] :zap: Unit test - [ ] :green_heart: E2E --- .../connector/graph_connector_helper_test.go | 120 +++++++++++++- .../connector/graph_connector_test.go | 148 ++++++++++++------ 2 files changed, 222 insertions(+), 46 deletions(-) diff --git a/src/internal/connector/graph_connector_helper_test.go b/src/internal/connector/graph_connector_helper_test.go index 439284c05..73083e837 100644 --- a/src/internal/connector/graph_connector_helper_test.go +++ b/src/internal/connector/graph_connector_helper_test.go @@ -160,6 +160,90 @@ func checkMessage( assert.Equal(t, expected.GetUniqueBody(), got.GetUniqueBody(), "UniqueBody") } +func checkContact( + t *testing.T, + expected models.Contactable, + got models.Contactable, +) { + emptyOrEqual(t, expected.GetAssistantName(), got.GetAssistantName(), "AssistantName") + + emptyOrEqual(t, expected.GetBirthday(), got.GetBirthday(), "Birthday") + + assert.Equal(t, expected.GetBusinessAddress(), got.GetBusinessAddress()) + + emptyOrEqual(t, expected.GetBusinessHomePage(), got.GetBusinessHomePage(), "BusinessHomePage") + + assert.Equal(t, expected.GetBusinessPhones(), got.GetBusinessPhones()) + + assert.Equal(t, expected.GetCategories(), got.GetCategories()) + + // Skip ChangeKey as it's tied to this specific instance of the item. + + assert.Equal(t, expected.GetChildren(), got.GetChildren()) + + emptyOrEqual(t, expected.GetCompanyName(), got.GetCompanyName(), "CompanyName") + + // Skip CreatedDateTime as it's tied to this specific instance of the item. + + emptyOrEqual(t, expected.GetDepartment(), got.GetDepartment(), "Department") + + emptyOrEqual(t, expected.GetDisplayName(), got.GetDisplayName(), "DisplayName") + + assert.Equal(t, expected.GetEmailAddresses(), got.GetEmailAddresses()) + + emptyOrEqual(t, expected.GetFileAs(), got.GetFileAs(), "FileAs") + + emptyOrEqual(t, expected.GetGeneration(), got.GetGeneration(), "Generation") + + emptyOrEqual(t, expected.GetGivenName(), got.GetGivenName(), "GivenName") + + assert.Equal(t, expected.GetHomeAddress(), got.GetHomeAddress()) + + assert.Equal(t, expected.GetHomePhones(), got.GetHomePhones()) + + // Skip CreatedDateTime as it's tied to this specific instance of the item. + + assert.Equal(t, expected.GetImAddresses(), got.GetImAddresses()) + + emptyOrEqual(t, expected.GetInitials(), got.GetInitials(), "Initials") + + emptyOrEqual(t, expected.GetJobTitle(), got.GetJobTitle(), "JobTitle") + + // Skip CreatedDateTime as it's tied to this specific instance of the item. + + emptyOrEqual(t, expected.GetManager(), got.GetManager(), "Manager") + + emptyOrEqual(t, expected.GetMiddleName(), got.GetMiddleName(), "MiddleName") + + emptyOrEqual(t, expected.GetMobilePhone(), got.GetMobilePhone(), "MobilePhone") + + emptyOrEqual(t, expected.GetNickName(), got.GetNickName(), "NickName") + + emptyOrEqual(t, expected.GetOfficeLocation(), got.GetOfficeLocation(), "OfficeLocation") + + assert.Equal(t, expected.GetOtherAddress(), got.GetOtherAddress()) + + // Skip ParentFolderId as it's tied to this specific instance of the item. + + emptyOrEqual(t, expected.GetPersonalNotes(), got.GetPersonalNotes(), "PersonalNotes") + + assert.Equal(t, expected.GetPhoto(), got.GetPhoto()) + + emptyOrEqual(t, expected.GetProfession(), got.GetProfession(), "Profession") + + emptyOrEqual(t, expected.GetSpouseName(), got.GetSpouseName(), "SpouseName") + + emptyOrEqual(t, expected.GetSurname(), got.GetSurname(), "Surname") + + emptyOrEqual(t, expected.GetTitle(), got.GetTitle(), "Title") + + emptyOrEqual(t, expected.GetYomiCompanyName(), got.GetYomiCompanyName(), "YomiCompanyName") + + emptyOrEqual(t, expected.GetYomiGivenName(), got.GetYomiGivenName(), "YomiGivenName") + + emptyOrEqual(t, expected.GetYomiSurname(), got.GetYomiSurname(), "YomiSurname") +} + func compareExchangeEmail( t *testing.T, expected map[string][]byte, @@ -186,6 +270,32 @@ func compareExchangeEmail( checkMessage(t, expectedMessage, itemMessage) } +func compareExchangeContact( + t *testing.T, + expected map[string][]byte, + item data.Stream, +) { + itemData, err := io.ReadAll(item.ToReader()) + if !assert.NoError(t, err, "reading collection item: %s", item.UUID()) { + return + } + + itemContact, err := support.CreateContactFromBytes(itemData) + if !assert.NoError(t, err, "deserializing backed up contact") { + return + } + + expectedBytes, ok := expected[*itemContact.GetMiddleName()] + if !assert.True(t, ok, "unexpected item with middle name %q", *itemContact.GetMiddleName()) { + return + } + + expectedContact, err := support.CreateContactFromBytes(expectedBytes) + assert.NoError(t, err, "deserializing source contact") + + checkContact(t, expectedContact, itemContact) +} + func compareItem( t *testing.T, expected map[string][]byte, @@ -198,6 +308,8 @@ func compareItem( switch category { case path.EmailCategory: compareExchangeEmail(t, expected, item) + case path.ContactsCategory: + compareExchangeContact(t, expected, item) default: assert.FailNowf(t, "unexpected Exchange category: %s", category.String()) } @@ -295,13 +407,17 @@ func collectionsForInfo( false, ) - expectedData[baseDestPath.String()] = make(map[string][]byte, len(info.items)) + baseExpected := expectedData[baseDestPath.String()] + if baseExpected == nil { + expectedData[baseDestPath.String()] = make(map[string][]byte, len(info.items)) + baseExpected = expectedData[baseDestPath.String()] + } for i := 0; i < len(info.items); i++ { c.Names[i] = info.items[i].name c.Data[i] = info.items[i].data - expectedData[baseDestPath.String()][info.items[i].lookupKey] = info.items[i].data + baseExpected[info.items[i].lookupKey] = info.items[i].data } collections = append(collections, c) diff --git a/src/internal/connector/graph_connector_test.go b/src/internal/connector/graph_connector_test.go index a3a89ffd9..7753139a2 100644 --- a/src/internal/connector/graph_connector_test.go +++ b/src/internal/connector/graph_connector_test.go @@ -367,42 +367,6 @@ func (suite *GraphConnectorIntegrationSuite) TestCreateAndDeleteCalendar() { } } -// TODO(ashmrtn): Merge this with the below once we get comparison logic for -// contacts. -func (suite *GraphConnectorIntegrationSuite) TestRestoreContact() { - t := suite.T() - sel := selectors.NewExchangeRestore() - fullpath, err := path.Builder{}.Append("testing"). - ToDataLayerExchangePathForCategory( - suite.connector.tenant, - suite.user, - path.ContactsCategory, - false, - ) - - require.NoError(t, err) - aPath, err := path.Builder{}.Append("validator").ToDataLayerExchangePathForCategory( - suite.connector.tenant, - suite.user, - path.ContactsCategory, - false, - ) - require.NoError(t, err) - - dcs := mockconnector.NewMockContactCollection(fullpath, 3) - two := mockconnector.NewMockContactCollection(aPath, 2) - collections := []data.Collection{dcs, two} - ctx := context.Background() - connector := loadConnector(ctx, suite.T()) - dest := control.DefaultRestoreDestination(common.SimpleDateTimeFormat) - err = connector.RestoreDataCollections(ctx, sel.Selector, dest, collections) - assert.NoError(suite.T(), err) - - value := connector.AwaitStatus() - assert.Equal(t, value.FolderCount, 1) - suite.T().Log(value.String()) -} - func (suite *GraphConnectorIntegrationSuite) TestEmptyCollections() { dest := control.DefaultRestoreDestination(common.SimpleDateTimeFormatOneDrive) table := []struct { @@ -460,14 +424,16 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() { subjectText := "Test message for restore" table := []struct { - name string - service path.ServiceType - collections []colInfo - backupSelFunc func(dest control.RestoreDestination, backupUser string) selectors.Selector + name string + service path.ServiceType + collections []colInfo + backupSelFunc func(dest control.RestoreDestination, backupUser string) selectors.Selector + expectedRestoreFolders int }{ { - name: "MultipleEmailsSingleFolder", - service: path.ExchangeService, + name: "MultipleEmailsSingleFolder", + service: path.ExchangeService, + expectedRestoreFolders: 1, collections: []colInfo{ { pathElements: []string{"Inbox"}, @@ -509,6 +475,100 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() { []string{dest.ContainerName}, )) + return backupSel.Selector + }, + }, + { + name: "MultipleContactsSingleFolder", + service: path.ExchangeService, + expectedRestoreFolders: 1, + collections: []colInfo{ + { + pathElements: []string{"Contacts"}, + category: path.ContactsCategory, + items: []itemInfo{ + { + name: "someencodeditemID", + data: mockconnector.GetMockContactBytes("Ghimley"), + lookupKey: "Ghimley", + }, + { + name: "someencodeditemID2", + data: mockconnector.GetMockContactBytes("Irgot"), + lookupKey: "Irgot", + }, + { + name: "someencodeditemID3", + data: mockconnector.GetMockContactBytes("Jannes"), + lookupKey: "Jannes", + }, + }, + }, + }, + // TODO(ashmrtn): Generalize this once we know the path transforms that + // occur during restore. + backupSelFunc: func(dest control.RestoreDestination, backupUser string) selectors.Selector { + backupSel := selectors.NewExchangeBackup() + backupSel.Include(backupSel.ContactFolders( + []string{backupUser}, + []string{dest.ContainerName}, + )) + + return backupSel.Selector + }, + }, + { + name: "MultipleContactsMutlipleFolders", + service: path.ExchangeService, + expectedRestoreFolders: 1, + collections: []colInfo{ + { + pathElements: []string{"Work"}, + category: path.ContactsCategory, + items: []itemInfo{ + { + name: "someencodeditemID", + data: mockconnector.GetMockContactBytes("Ghimley"), + lookupKey: "Ghimley", + }, + { + name: "someencodeditemID2", + data: mockconnector.GetMockContactBytes("Irgot"), + lookupKey: "Irgot", + }, + { + name: "someencodeditemID3", + data: mockconnector.GetMockContactBytes("Jannes"), + lookupKey: "Jannes", + }, + }, + }, + { + pathElements: []string{"Personal"}, + category: path.ContactsCategory, + items: []itemInfo{ + { + name: "someencodeditemID4", + data: mockconnector.GetMockContactBytes("Argon"), + lookupKey: "Argon", + }, + { + name: "someencodeditemID5", + data: mockconnector.GetMockContactBytes("Bernard"), + lookupKey: "Bernard", + }, + }, + }, + }, + // TODO(ashmrtn): Generalize this once we know the path transforms that + // occur during restore. + backupSelFunc: func(dest control.RestoreDestination, backupUser string) selectors.Selector { + backupSel := selectors.NewExchangeBackup() + backupSel.Include(backupSel.ContactFolders( + []string{backupUser}, + []string{dest.ContainerName}, + )) + return backupSel.Selector }, }, @@ -537,7 +597,7 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() { require.NoError(t, err) status := restoreGC.AwaitStatus() - assert.Equal(t, len(test.collections), status.FolderCount, "status.FolderCount") + assert.Equal(t, test.expectedRestoreFolders, status.FolderCount, "status.FolderCount") assert.Equal(t, totalItems, status.ObjectCount, "status.ObjectCount") assert.Equal(t, totalItems, status.Successful, "status.Successful") @@ -559,7 +619,7 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() { checkCollections(t, totalItems, expectedData, dcs) status = backupGC.AwaitStatus() - assert.Equal(t, len(test.collections), status.FolderCount, "status.FolderCount") + assert.Equal(t, test.expectedRestoreFolders, status.FolderCount, "status.FolderCount") assert.Equal(t, totalItems, status.ObjectCount, "status.ObjectCount") assert.Equal(t, totalItems, status.Successful, "status.Successful") })