diff --git a/src/internal/kopia/conn_test.go b/src/internal/kopia/conn_test.go index 5e96f9cde..8769e9f43 100644 --- a/src/internal/kopia/conn_test.go +++ b/src/internal/kopia/conn_test.go @@ -11,10 +11,12 @@ import ( "github.com/stretchr/testify/suite" "github.com/alcionai/corso/src/internal/tester" + "github.com/alcionai/corso/src/pkg/storage" ) //revive:disable:context-as-argument func openKopiaRepo(t *testing.T, ctx context.Context) (*conn, error) { + //revive:enable:context-as-argument st := tester.NewPrefixedS3Storage(t) k := NewConn(st) @@ -68,6 +70,41 @@ func (suite *WrapperIntegrationSuite) SetupSuite() { require.NoError(suite.T(), err) } +func (suite *WrapperIntegrationSuite) TestRepoExistsError() { + t := suite.T() + ctx := context.Background() + + st := tester.NewPrefixedS3Storage(t) + k := NewConn(st) + require.NoError(t, k.Initialize(ctx)) + + require.NoError(t, k.Close(ctx)) + + err := k.Initialize(ctx) + assert.Error(t, err) + assert.True(t, IsRepoAlreadyExistsError(err)) +} + +func (suite *WrapperIntegrationSuite) TestBadProviderErrors() { + t := suite.T() + ctx := context.Background() + + st := tester.NewPrefixedS3Storage(t) + st.Provider = storage.ProviderUnknown + + k := NewConn(st) + assert.Error(t, k.Initialize(ctx)) +} + +func (suite *WrapperIntegrationSuite) TestConnectWithoutInitErrors() { + t := suite.T() + ctx := context.Background() + + st := tester.NewPrefixedS3Storage(t) + k := NewConn(st) + assert.Error(t, k.Connect(ctx)) +} + func (suite *WrapperIntegrationSuite) TestCloseTwiceDoesNotCrash() { ctx := context.Background() t := suite.T() diff --git a/src/internal/kopia/model_store_test.go b/src/internal/kopia/model_store_test.go index 361bd4c4d..6dfda305a 100644 --- a/src/internal/kopia/model_store_test.go +++ b/src/internal/kopia/model_store_test.go @@ -25,6 +25,7 @@ type fooModel struct { //revive:disable:context-as-argument func getModelStore(t *testing.T, ctx context.Context) *ModelStore { + //revive:enable:context-as-argument c, err := openKopiaRepo(t, ctx) require.NoError(t, err) @@ -107,11 +108,32 @@ func (suite *ModelStoreIntegrationSuite) TestBadTagsErrors() { foo := &fooModel{Bar: uuid.NewString()} foo.Tags = test.tags - assert.Error(t, suite.m.Put(suite.ctx, model.BackupOpSchema, foo)) - assert.Error(t, suite.m.Update(suite.ctx, model.BackupOpSchema, foo)) + assert.ErrorIs( + t, + suite.m.Put(suite.ctx, model.BackupOpSchema, foo), + errBadTagKey, + ) - _, err := suite.m.GetIDsForType(suite.ctx, model.BackupOpSchema, test.tags) - assert.Error(t, err) + // Add model for update/get ID checks. + foo.Tags = map[string]string{} + require.NoError( + t, + suite.m.Put(suite.ctx, model.BackupOpSchema, foo), + ) + + foo.Tags = test.tags + assert.ErrorIs( + t, + suite.m.Update(suite.ctx, model.BackupOpSchema, foo), + errBadTagKey, + ) + + _, err := suite.m.GetIDsForType( + suite.ctx, + model.BackupOpSchema, + test.tags, + ) + assert.ErrorIs(t, err, errBadTagKey) }) } } @@ -143,7 +165,11 @@ func (suite *ModelStoreIntegrationSuite) TestBadModelTypeErrors() { foo := &fooModel{Bar: uuid.NewString()} - assert.Error(t, suite.m.Put(suite.ctx, model.UnknownSchema, foo)) + assert.ErrorIs( + t, + suite.m.Put(suite.ctx, model.UnknownSchema, foo), + errUnrecognizedSchema, + ) require.NoError(t, suite.m.Put(suite.ctx, model.BackupOpSchema, foo)) @@ -159,11 +185,23 @@ func (suite *ModelStoreIntegrationSuite) TestBadTypeErrors() { require.NoError(t, suite.m.Put(suite.ctx, model.BackupOpSchema, foo)) returned := &fooModel{} - assert.Error(t, suite.m.Get(suite.ctx, model.RestoreOpSchema, foo.ID, returned)) - assert.Error( - t, suite.m.GetWithModelStoreID(suite.ctx, model.RestoreOpSchema, foo.ModelStoreID, returned)) + assert.ErrorIs( + t, + suite.m.Get(suite.ctx, model.RestoreOpSchema, foo.ID, returned), + errModelTypeMismatch, + ) - assert.Error(t, suite.m.Delete(suite.ctx, model.RestoreOpSchema, foo.ID)) + assert.ErrorIs( + t, + suite.m.GetWithModelStoreID(suite.ctx, model.RestoreOpSchema, foo.ModelStoreID, returned), + errModelTypeMismatch, + ) + + assert.ErrorIs( + t, + suite.m.Delete(suite.ctx, model.RestoreOpSchema, foo.ID), + errModelTypeMismatch, + ) } func (suite *ModelStoreIntegrationSuite) TestPutGet() { @@ -301,6 +339,137 @@ func (suite *ModelStoreIntegrationSuite) TestPutGetOfType() { } } +func (suite *ModelStoreIntegrationSuite) TestGetOfTypeWithTags() { + tagKey1 := "foo" + tagKey2 := "bar" + tagValue1 := "hola" + tagValue2 := "mundo" + + inputs := []struct { + schema model.Schema + dataModel *fooModel + }{ + { + schema: model.BackupOpSchema, + dataModel: &fooModel{ + BaseModel: model.BaseModel{ + Tags: map[string]string{ + tagKey1: tagValue1, + }, + }, + }, + }, + { + schema: model.BackupOpSchema, + dataModel: &fooModel{ + BaseModel: model.BaseModel{ + Tags: map[string]string{ + tagKey1: tagValue1, + tagKey2: tagValue2, + }, + }, + }, + }, + { + schema: model.BackupOpSchema, + dataModel: &fooModel{ + BaseModel: model.BaseModel{ + Tags: map[string]string{ + tagKey1: tagValue2, + }, + }, + }, + }, + { + schema: model.RestoreOpSchema, + dataModel: &fooModel{ + BaseModel: model.BaseModel{ + Tags: map[string]string{ + tagKey1: tagValue1, + }, + }, + }, + }, + { + schema: model.RestoreOpSchema, + dataModel: &fooModel{ + BaseModel: model.BaseModel{ + Tags: map[string]string{}, + }, + }, + }, + } + + table := []struct { + name string + s model.Schema + tags map[string]string + expectedModels []*fooModel + }{ + { + name: "UnpopulatedType", + s: model.BackupSchema, + tags: map[string]string{ + tagKey1: tagValue1, + }, + expectedModels: []*fooModel{}, + }, + { + name: "RestrictByModelType", + s: model.RestoreOpSchema, + tags: map[string]string{ + tagKey1: tagValue1, + }, + expectedModels: []*fooModel{inputs[3].dataModel}, + }, + { + name: "RestrictByModelType2", + s: model.BackupOpSchema, + tags: map[string]string{ + tagKey1: tagValue1, + }, + expectedModels: []*fooModel{inputs[0].dataModel, inputs[1].dataModel}, + }, + { + name: "RestrictByTags", + s: model.BackupOpSchema, + tags: map[string]string{ + tagKey1: tagValue1, + tagKey2: tagValue2, + }, + expectedModels: []*fooModel{inputs[1].dataModel}, + }, + { + name: "RestrictByTags2", + s: model.BackupOpSchema, + tags: map[string]string{ + tagKey1: tagValue2, + }, + expectedModels: []*fooModel{inputs[2].dataModel}, + }, + } + + // Setup the store by adding all the inputs. + for _, in := range inputs { + require.NoError(suite.T(), suite.m.Put(suite.ctx, in.schema, in.dataModel)) + } + + // Check we can properly execute our tests. + for _, test := range table { + suite.T().Run(test.name, func(t *testing.T) { + expected := make([]*model.BaseModel, 0, len(test.expectedModels)) + for _, e := range test.expectedModels { + expected = append(expected, &e.BaseModel) + } + + ids, err := suite.m.GetIDsForType(suite.ctx, test.s, test.tags) + require.NoError(t, err) + + assert.ElementsMatch(t, expected, ids) + }) + } +} + func (suite *ModelStoreIntegrationSuite) TestPutUpdate() { table := []struct { name string @@ -516,10 +685,12 @@ func (suite *ModelStoreRegressionSuite) TestFailDuringWriteSessionHasNoVisibleEf assert.Equal(t, foo, returned) } +//revive:disable:context-as-argument func openConnAndModelStore( t *testing.T, ctx context.Context, ) (*conn, *ModelStore) { + //revive:enable:context-as-argument st := tester.NewPrefixedS3Storage(t) c := NewConn(st) @@ -535,11 +706,13 @@ func openConnAndModelStore( return c, ms } +//revive:disable:context-as-argument func reconnectToModelStore( t *testing.T, ctx context.Context, c *conn, ) *ModelStore { + //revive:enable:context-as-argument require.NoError(t, c.Connect(ctx)) defer func() { diff --git a/src/internal/kopia/wrapper_test.go b/src/internal/kopia/wrapper_test.go index 8d584486b..a0e22b88d 100644 --- a/src/internal/kopia/wrapper_test.go +++ b/src/internal/kopia/wrapper_test.go @@ -155,7 +155,6 @@ func (suite *CorsoProgressUnitSuite) TestFinishedFile() { name string cachedItems map[string]testInfo expectedLen int - err error }{ { name: "DetailsExist", @@ -891,53 +890,93 @@ func (suite *KopiaSimpleRepoIntegrationSuite) TearDownTest() { } func (suite *KopiaSimpleRepoIntegrationSuite) TestRestoreMultipleItems() { + doesntExist, err := path.Builder{}.Append("subdir", "foo").ToDataLayerExchangePathForCategory( + testTenant, + testUser, + path.EmailCategory, + true, + ) + require.NoError(suite.T(), err) + + // Expected items is generated during the test by looking up paths in the + // suite's map of files. Files that are not in the suite's map are assumed to + // generate errors and not be in the output. table := []struct { - name string - // This is both input and can be used to lookup expected output information. - items []*backedupFile + name string + inputPaths []path.Path expectedCollections int + expectedErr assert.ErrorAssertionFunc }{ { name: "SingleItem", - items: []*backedupFile{ - suite.files[suite.testPath1.String()][0], + inputPaths: []path.Path{ + suite.files[suite.testPath1.String()][0].itemPath, }, expectedCollections: 1, + expectedErr: assert.NoError, }, { name: "MultipleItemsSameCollection", - items: []*backedupFile{ - suite.files[suite.testPath1.String()][0], - suite.files[suite.testPath1.String()][1], + inputPaths: []path.Path{ + suite.files[suite.testPath1.String()][0].itemPath, + suite.files[suite.testPath1.String()][1].itemPath, }, expectedCollections: 1, + expectedErr: assert.NoError, }, { name: "MultipleItemsDifferentCollections", - items: []*backedupFile{ - suite.files[suite.testPath1.String()][0], - suite.files[suite.testPath2.String()][0], + inputPaths: []path.Path{ + suite.files[suite.testPath1.String()][0].itemPath, + suite.files[suite.testPath2.String()][0].itemPath, }, expectedCollections: 2, + expectedErr: assert.NoError, + }, + { + name: "TargetNotAFile", + inputPaths: []path.Path{ + suite.files[suite.testPath1.String()][0].itemPath, + suite.testPath1, + suite.files[suite.testPath2.String()][0].itemPath, + }, + expectedCollections: 2, + expectedErr: assert.Error, + }, + { + name: "NonExistentFile", + inputPaths: []path.Path{ + suite.files[suite.testPath1.String()][0].itemPath, + doesntExist, + suite.files[suite.testPath2.String()][0].itemPath, + }, + expectedCollections: 2, + expectedErr: assert.Error, }, } for _, test := range table { suite.T().Run(test.name, func(t *testing.T) { - inputPaths := make([]path.Path, 0, len(test.items)) - expected := make(map[string][]byte, len(test.items)) + // May slightly overallocate as only items that are actually in our map + // are expected. The rest are errors, but best-effort says it should carry + // on even then. + expected := make(map[string][]byte, len(test.inputPaths)) - for _, item := range test.items { - inputPaths = append(inputPaths, item.itemPath) - expected[item.itemPath.String()] = suite.filesByPath[item.itemPath.String()].data + for _, pth := range test.inputPaths { + item, ok := suite.filesByPath[pth.String()] + if !ok { + continue + } + + expected[pth.String()] = item.data } result, err := suite.w.RestoreMultipleItems( suite.ctx, string(suite.snapshotID), - inputPaths, + test.inputPaths, ) - require.NoError(t, err) + test.expectedErr(t, err) assert.Len(t, result, test.expectedCollections) testForFiles(t, expected, result) @@ -949,14 +988,6 @@ func (suite *KopiaSimpleRepoIntegrationSuite) TestRestoreMultipleItems_Errors() itemPath, err := suite.testPath1.Append(testFileName, true) require.NoError(suite.T(), err) - doesntExist, err := path.Builder{}.Append("subdir", "foo").ToDataLayerExchangePathForCategory( - testTenant, - testUser, - path.EmailCategory, - true, - ) - require.NoError(suite.T(), err) - table := []struct { name string snapshotID string @@ -977,26 +1008,17 @@ func (suite *KopiaSimpleRepoIntegrationSuite) TestRestoreMultipleItems_Errors() "foo", []path.Path{itemPath}, }, - { - "TargetNotAFile", - string(suite.snapshotID), - []path.Path{suite.testPath1}, - }, - { - "NonExistentFile", - string(suite.snapshotID), - []path.Path{doesntExist}, - }, } for _, test := range table { suite.T().Run(test.name, func(t *testing.T) { - _, err := suite.w.RestoreMultipleItems( + c, err := suite.w.RestoreMultipleItems( suite.ctx, test.snapshotID, test.paths, ) - require.Error(t, err) + assert.Error(t, err) + assert.Empty(t, c) }) } } @@ -1008,12 +1030,13 @@ func (suite *KopiaSimpleRepoIntegrationSuite) TestDeleteSnapshot() { // assert the deletion worked itemPath := suite.files[suite.testPath1.String()][0].itemPath - _, err := suite.w.RestoreMultipleItems( + c, err := suite.w.RestoreMultipleItems( suite.ctx, string(suite.snapshotID), []path.Path{itemPath}, ) assert.Error(t, err, "snapshot should be deleted") + assert.Empty(t, c) } func (suite *KopiaSimpleRepoIntegrationSuite) TestDeleteSnapshot_BadIDs() {