Backup details list exchange tests (#945)

## Description

Add tests for the `backup list exchange` subcommand. Tests mostly center around indirectly testing how selectors are created and used in this subcommand by checking the output of running the `Reduce` call on a known set of `details.DetailsEntry`s. Also check various error cases

Tests for invalid formats of flag values are disabled as that code does not currently exist. These tests can be enabled when #943 is resolved

## 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-23 09:51:08 -07:00 committed by GitHub
parent 4d02650f99
commit 824f02469c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 268 additions and 14 deletions

View File

@ -1,6 +1,8 @@
package backup
import (
"context"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
@ -11,6 +13,7 @@ import (
"github.com/alcionai/corso/src/cli/utils"
"github.com/alcionai/corso/src/internal/model"
"github.com/alcionai/corso/src/pkg/backup"
"github.com/alcionai/corso/src/pkg/backup/details"
"github.com/alcionai/corso/src/pkg/control"
"github.com/alcionai/corso/src/pkg/repository"
"github.com/alcionai/corso/src/pkg/selectors"
@ -353,11 +356,6 @@ func detailsExchangeCmd(cmd *cobra.Command, args []string) error {
defer utils.CloseRepo(ctx, r)
d, _, err := r.BackupDetails(ctx, backupID)
if err != nil {
return Only(ctx, errors.Wrap(err, "Failed to get backup details in the repository"))
}
opts := utils.ExchangeOpts{
Contacts: contact,
ContactFolders: contactFolder,
@ -378,6 +376,34 @@ func detailsExchangeCmd(cmd *cobra.Command, args []string) error {
EventSubject: eventSubject,
}
ds, err := runDetailsExchangeCmd(ctx, r, backupID, opts)
if err != nil {
return Only(ctx, err)
}
if len(ds.Entries) == 0 {
Info(ctx, selectors.ErrorNoMatchingItems)
return nil
}
ds.PrintEntries(ctx)
return nil
}
// runDetailsExchangeCmd actually performs the lookup in backup details. Assumes
// len(backupID) > 0.
func runDetailsExchangeCmd(
ctx context.Context,
r repository.BackupGetter,
backupID string,
opts utils.ExchangeOpts,
) (*details.Details, error) {
d, _, err := r.BackupDetails(ctx, backupID)
if err != nil {
return nil, errors.Wrap(err, "Failed to get backup details in the repository")
}
sel := selectors.NewExchangeRestore()
utils.IncludeExchangeRestoreDataSelectors(sel, opts)
utils.FilterExchangeRestoreInfoSelectors(sel, opts)
@ -387,15 +413,7 @@ func detailsExchangeCmd(cmd *cobra.Command, args []string) error {
sel.Include(sel.Users(selectors.Any()))
}
ds := sel.Reduce(ctx, d)
if len(ds.Entries) == 0 {
Info(ctx, selectors.ErrorNoMatchingItems)
return nil
}
ds.PrintEntries(ctx)
return nil
return sel.Reduce(ctx, d), nil
}
// ------------------------------------------------------------------------------------------------

View File

@ -1,6 +1,7 @@
package backup
import (
"context"
"testing"
"github.com/spf13/cobra"
@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/cli/utils"
"github.com/alcionai/corso/src/cli/utils/testdata"
"github.com/alcionai/corso/src/internal/tester"
)
@ -214,3 +216,56 @@ func (suite *ExchangeSuite) TestExchangeBackupCreateSelectors() {
})
}
}
func (suite *ExchangeSuite) TestExchangeBackupDetailsSelectors() {
ctx := context.Background()
for _, test := range testdata.ExchangeOptionDetailLookups {
suite.T().Run(test.Name, func(t *testing.T) {
output, err := runDetailsExchangeCmd(
ctx,
test.BackupGetter,
"backup-ID",
test.Opts,
)
assert.NoError(t, err)
assert.ElementsMatch(t, test.Expected, output.Entries)
})
}
}
func (suite *ExchangeSuite) TestExchangeBackupDetailsSelectorsBadBackupID() {
t := suite.T()
ctx := context.Background()
backupGetter := &testdata.MockBackupGetter{}
output, err := runDetailsExchangeCmd(
ctx,
backupGetter,
"backup-ID",
utils.ExchangeOpts{},
)
assert.Error(t, err)
assert.Empty(t, output)
}
// TODO(ashmrtn): Uncomment these when the CLI validates flag input values.
//func (suite *ExchangeSuite) TestExchangeBackupDetailsSelectorsBadFormats() {
// ctx := context.Background()
//
// for _, test := range testdata.BadExchangeOptionsFormats {
// suite.T().Run(test.Name, func(t *testing.T) {
// output, err := runDetailsExchangeCmd(
// ctx,
// test.BackupGetter,
// "backup-ID",
// test.Opts,
// )
// assert.Error(t, err)
//
// assert.Empty(t, output)
// })
// }
//}

181
src/cli/utils/testdata/opts.go vendored Normal file
View File

@ -0,0 +1,181 @@
package testdata
import (
"context"
"errors"
"time"
"github.com/alcionai/corso/src/cli/utils"
"github.com/alcionai/corso/src/internal/common"
"github.com/alcionai/corso/src/internal/model"
"github.com/alcionai/corso/src/pkg/backup"
"github.com/alcionai/corso/src/pkg/backup/details"
"github.com/alcionai/corso/src/pkg/selectors"
"github.com/alcionai/corso/src/pkg/selectors/testdata"
)
type ExchangeOptionsTest struct {
Name string
Opts utils.ExchangeOpts
BackupGetter *MockBackupGetter
Expected []details.DetailsEntry
}
var (
// BadExchangeOptionsFormats contains ExchangeOpts with flags that should
// cause errors about the format of the input flag. Mocks are configured to
// allow the system to run if it doesn't throw an error on formatting.
BadExchangeOptionsFormats = []ExchangeOptionsTest{
{
Name: "BadEmailReceiveAfter",
Opts: utils.ExchangeOpts{
EmailReceivedAfter: "foo",
},
},
{
Name: "BadEmailReceiveBefore",
Opts: utils.ExchangeOpts{
EmailReceivedBefore: "foo",
},
},
{
Name: "BadEventRecurs",
Opts: utils.ExchangeOpts{
EventRecurs: "foo",
},
},
{
Name: "BadEventStartsAfter",
Opts: utils.ExchangeOpts{
EventStartsAfter: "foo",
},
},
{
Name: "BadEventStartsBefore",
Opts: utils.ExchangeOpts{
EventStartsBefore: "foo",
},
},
}
// ExchangeOptionDetailLookups contains flag inputs and expected results for
// some choice input patterns. This set is not exhaustive. All inputs and
// outputs are according to the data laid out in selectors/testdata. Mocks are
// configured to return the full dataset listed in selectors/testdata.
ExchangeOptionDetailLookups = []ExchangeOptionsTest{
{
Name: "Emails",
Expected: testdata.ExchangeEmailItems,
Opts: utils.ExchangeOpts{
Emails: selectors.Any(),
},
},
{
Name: "EmailsBySubject",
Expected: testdata.ExchangeEmailItems,
Opts: utils.ExchangeOpts{
EmailSender: "a-person",
},
},
{
Name: "AllExchange",
Expected: append(
append(
append(
[]details.DetailsEntry{},
testdata.ExchangeEmailItems...,
),
testdata.ExchangeContactsItems...,
),
testdata.ExchangeEventsItems...,
),
},
{
Name: "MailReceivedTime",
Expected: []details.DetailsEntry{testdata.ExchangeEmailItems[0]},
Opts: utils.ExchangeOpts{
EmailReceivedBefore: common.FormatTime(testdata.Time1.Add(time.Second)),
},
},
{
Name: "MailID",
Expected: []details.DetailsEntry{testdata.ExchangeEmailItems[0]},
Opts: utils.ExchangeOpts{
Emails: []string{testdata.ExchangeEmailItemPath1.Item()},
},
},
{
Name: "MailShortRef",
Expected: []details.DetailsEntry{testdata.ExchangeEmailItems[0]},
Opts: utils.ExchangeOpts{
Emails: []string{testdata.ExchangeEmailItemPath1.ShortRef()},
},
},
{
Name: "MultipleMailShortRef",
Expected: testdata.ExchangeEmailItems,
Opts: utils.ExchangeOpts{
Emails: []string{
testdata.ExchangeEmailItemPath1.ShortRef(),
testdata.ExchangeEmailItemPath2.ShortRef(),
},
},
},
{
Name: "AllEventsAndMailWithSubject",
Expected: []details.DetailsEntry{testdata.ExchangeEmailItems[0]},
Opts: utils.ExchangeOpts{
EmailSubject: "foo",
Events: selectors.Any(),
},
},
{
Name: "EventsAndMailWithSubject",
Expected: []details.DetailsEntry{},
Opts: utils.ExchangeOpts{
EmailSubject: "foo",
EventSubject: "foo",
},
},
{
Name: "EventsAndMailByShortRef",
Expected: []details.DetailsEntry{
testdata.ExchangeEmailItems[0],
testdata.ExchangeEventsItems[0],
},
Opts: utils.ExchangeOpts{
Emails: []string{testdata.ExchangeEmailItemPath1.ShortRef()},
Events: []string{testdata.ExchangeEventsItemPath1.ShortRef()},
},
},
}
)
// MockBackupGetter implements the repo.BackupGetter interface and returns
// (selectors/testdata.GetDetailsSet(), nil, nil) when BackupDetails is called
// on the nil instance. If an instance is given or Backups is called returns an
// error.
type MockBackupGetter struct{}
func (MockBackupGetter) Backup(
context.Context,
model.StableID,
) (*backup.Backup, error) {
return nil, errors.New("unexpected call to mock")
}
func (MockBackupGetter) Backups(context.Context) ([]backup.Backup, error) {
return nil, errors.New("unexpected call to mock")
}
func (bg *MockBackupGetter) BackupDetails(
ctx context.Context,
backupID string,
) (*details.Details, *backup.Backup, error) {
if bg == nil {
return testdata.GetDetailsSet(), nil, nil
}
return nil, nil, errors.New("unexpected call to mock")
}