package mock import ( "context" "io" "testing" "time" "github.com/alcionai/clues" "github.com/stretchr/testify/require" "github.com/alcionai/corso/src/internal/common/readers" "github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/pkg/backup/details" "github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/path" ) // --------------------------------------------------------------------------- // Item // --------------------------------------------------------------------------- var ( _ data.Item = &Item{} _ data.ItemInfo = &Item{} ) type Item struct { DeletedFlag bool ItemID string ItemInfo details.ItemInfo ItemSize int64 ModifiedTime time.Time Reader io.ReadCloser ReadErr error } func (s *Item) ID() string { return s.ItemID } func (s Item) Deleted() bool { return s.DeletedFlag } func (s *Item) ToReader() io.ReadCloser { if s.ReadErr != nil { return io.NopCloser(errReader{s.ReadErr}) } return s.Reader } func (s *Item) Info() (details.ItemInfo, error) { return s.ItemInfo, nil } func (s *Item) Size() int64 { return s.ItemSize } func (s *Item) ModTime() time.Time { return s.ModifiedTime } type errReader struct { readErr error } func (er errReader) Read([]byte) (int, error) { return 0, er.readErr } // --------------------------------------------------------------------------- // collection // --------------------------------------------------------------------------- var ( _ data.Collection = &Collection{} _ data.BackupCollection = &Collection{} _ data.RestoreCollection = &Collection{} ) type Collection struct { Path path.Path Loc *path.Builder ItemData []data.Item ItemsRecoverableErrs []error CState data.CollectionState // For restore AuxItems map[string]data.Item } func (c Collection) Items(ctx context.Context, errs *fault.Bus) <-chan data.Item { ch := make(chan data.Item) go func() { defer close(ch) el := errs.Local() for _, item := range c.ItemData { it, ok := item.(*Item) if ok && it.ReadErr != nil { el.AddRecoverable(ctx, it.ReadErr) continue } ch <- item } }() for _, err := range c.ItemsRecoverableErrs { errs.AddRecoverable(ctx, err) } return ch } func (c Collection) FullPath() path.Path { return c.Path } func (c Collection) PreviousPath() path.Path { return c.Path } func (c Collection) LocationPath() *path.Builder { return c.Loc } func (c Collection) State() data.CollectionState { return c.CState } func (c Collection) DoNotMergeItems() bool { return false } func (c Collection) FetchItemByName( ctx context.Context, name string, ) (data.Item, error) { res := c.AuxItems[name] if res == nil { return nil, data.ErrNotFound } return res, nil } var _ data.RestoreCollection = &RestoreCollection{} type RestoreCollection struct { data.Collection AuxItems map[string]data.Item } func (rc RestoreCollection) FetchItemByName( ctx context.Context, name string, ) (data.Item, error) { res := rc.AuxItems[name] if res == nil { return nil, data.ErrNotFound } return res, nil } var ( _ data.BackupCollection = &versionedBackupCollection{} _ data.RestoreCollection = &unversionedRestoreCollection{} _ data.Item = &itemWrapper{} ) type itemWrapper struct { data.Item reader io.ReadCloser } func (i *itemWrapper) ToReader() io.ReadCloser { return i.reader } func NewUnversionedRestoreCollection( t *testing.T, col data.RestoreCollection, ) *unversionedRestoreCollection { return &unversionedRestoreCollection{ RestoreCollection: col, t: t, } } // unversionedRestoreCollection strips out version format headers on all items. // // Wrap data.RestoreCollections in this type if you don't need access to the // version format header during tests and you know the item readers can't return // an error. type unversionedRestoreCollection struct { data.RestoreCollection t *testing.T } func (c *unversionedRestoreCollection) Items( ctx context.Context, errs *fault.Bus, ) <-chan data.Item { res := make(chan data.Item) go func() { defer close(res) for item := range c.RestoreCollection.Items(ctx, errs) { r, err := readers.NewVersionedRestoreReader(item.ToReader()) require.NoError(c.t, err, clues.ToCore(err)) res <- &itemWrapper{ Item: item, reader: r, } } }() return res } func NewVersionedBackupCollection( t *testing.T, col data.BackupCollection, ) *versionedBackupCollection { return &versionedBackupCollection{ BackupCollection: col, t: t, } } // versionedBackupCollection injects basic version information on all items. // // Wrap data.BackupCollections in this type if you don't need to explicitly set // the version format header during tests, aren't trying to check reader errors // cases, and aren't populating backup details. type versionedBackupCollection struct { data.BackupCollection t *testing.T } func (c *versionedBackupCollection) Items( ctx context.Context, errs *fault.Bus, ) <-chan data.Item { res := make(chan data.Item) go func() { defer close(res) for item := range c.BackupCollection.Items(ctx, errs) { r, err := readers.NewVersionedBackupReader( readers.SerializationFormat{ Version: readers.DefaultSerializationVersion, }, item.ToReader()) require.NoError(c.t, err, clues.ToCore(err)) res <- &itemWrapper{ Item: item, reader: r, } } }() return res }