Bulk up tests for kopia package some (#920)

## Description

Largest changes around
* Adding tests for some init functions for conn
* Checking modelstore get metadata with tags functionality
* wrapper is best-effort for restore when there's errors

## Type of change

<!--- Please check the type of change your PR introduces: --->
- [ ] 🌻 Feature
- [ ] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [x] 🤖 Test
- [ ] 💻 CI/Deployment
- [ ] 🐹 Trivial/Minor

## Issue(s)

* #913 

## Test Plan

<!-- How will this be tested prior to merging.-->
- [ ] 💪 Manual
- [x]  Unit test
- [ ] 💚 E2E
This commit is contained in:
ashmrtn 2022-09-21 09:13:27 -07:00 committed by GitHub
parent b30d5f15cc
commit 975b8b8e5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 282 additions and 49 deletions

View File

@ -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()

View File

@ -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() {

View File

@ -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() {