gcs_mocks.go - afero - [fork] go afero port for 9front
 (HTM) git clone https://git.drkhsh.at/afero.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       gcs_mocks.go (5438B)
       ---
            1 // Copyright © 2021 Vasily Ovchinnikov <vasily@remerge.io>.
            2 //
            3 // A set of stiface-based mocks, replicating the GCS behavior, to make the tests not require any
            4 // internet connection or real buckets.
            5 // It is **not** a comprehensive set of mocks to test anything and everything GCS-related, rather
            6 // a very tailored one for the current implementation - thus the tests, written with the use of
            7 // these mocks are more of regression ones.
            8 // If any GCS behavior changes and breaks the implementation, then it should first be adjusted by
            9 // switching over to a real bucket - and then the mocks have to be adjusted to match the
           10 // implementation.
           11 
           12 package gcsfs
           13 
           14 import (
           15         "context"
           16         "io"
           17         "os"
           18         "strings"
           19 
           20         "cloud.google.com/go/storage"
           21         "google.golang.org/api/iterator"
           22 
           23         "github.com/spf13/afero"
           24         "github.com/spf13/afero/gcsfs/internal/stiface"
           25 )
           26 
           27 // sets filesystem separators to the one, expected (and hard-coded) in the tests
           28 func normSeparators(s string) string {
           29         return strings.Replace(s, "\\", "/", -1)
           30 }
           31 
           32 type clientMock struct {
           33         stiface.Client
           34         fs afero.Fs
           35 }
           36 
           37 func newClientMock() *clientMock {
           38         return &clientMock{fs: afero.NewMemMapFs()}
           39 }
           40 
           41 func (m *clientMock) Bucket(name string) stiface.BucketHandle {
           42         return &bucketMock{bucketName: name, fs: m.fs}
           43 }
           44 
           45 type bucketMock struct {
           46         stiface.BucketHandle
           47 
           48         bucketName string
           49 
           50         fs afero.Fs
           51 }
           52 
           53 func (m *bucketMock) Attrs(context.Context) (*storage.BucketAttrs, error) {
           54         return &storage.BucketAttrs{}, nil
           55 }
           56 
           57 func (m *bucketMock) Object(name string) stiface.ObjectHandle {
           58         return &objectMock{name: name, fs: m.fs}
           59 }
           60 
           61 func (m *bucketMock) Objects(_ context.Context, q *storage.Query) (it stiface.ObjectIterator) {
           62         return &objectItMock{name: q.Prefix, fs: m.fs}
           63 }
           64 
           65 type objectMock struct {
           66         stiface.ObjectHandle
           67 
           68         name string
           69         fs   afero.Fs
           70 }
           71 
           72 func (o *objectMock) NewWriter(_ context.Context) stiface.Writer {
           73         return &writerMock{name: o.name, fs: o.fs}
           74 }
           75 
           76 func (o *objectMock) NewRangeReader(_ context.Context, offset, length int64) (stiface.Reader, error) {
           77         if o.name == "" {
           78                 return nil, ErrEmptyObjectName
           79         }
           80 
           81         file, err := o.fs.Open(o.name)
           82         if err != nil {
           83                 return nil, err
           84         }
           85 
           86         if offset > 0 {
           87                 _, err = file.Seek(offset, io.SeekStart)
           88                 if err != nil {
           89                         return nil, err
           90                 }
           91         }
           92 
           93         res := &readerMock{file: file}
           94         if length > -1 {
           95                 res.buf = make([]byte, length)
           96                 _, err = file.Read(res.buf)
           97                 if err != nil {
           98                         return nil, err
           99                 }
          100         }
          101 
          102         return res, nil
          103 }
          104 
          105 func (o *objectMock) Delete(_ context.Context) error {
          106         if o.name == "" {
          107                 return ErrEmptyObjectName
          108         }
          109         return o.fs.Remove(o.name)
          110 }
          111 
          112 func (o *objectMock) Attrs(_ context.Context) (*storage.ObjectAttrs, error) {
          113         if o.name == "" {
          114                 return nil, ErrEmptyObjectName
          115         }
          116 
          117         info, err := o.fs.Stat(o.name)
          118         if err != nil {
          119                 pathError, ok := err.(*os.PathError)
          120                 if ok {
          121                         if pathError.Err == os.ErrNotExist {
          122                                 return nil, storage.ErrObjectNotExist
          123                         }
          124                 }
          125 
          126                 return nil, err
          127         }
          128 
          129         res := &storage.ObjectAttrs{Name: normSeparators(o.name), Size: info.Size(), Updated: info.ModTime()}
          130 
          131         if info.IsDir() {
          132                 // we have to mock it here, because of FileInfo logic
          133                 return nil, ErrObjectDoesNotExist
          134         }
          135 
          136         return res, nil
          137 }
          138 
          139 type writerMock struct {
          140         stiface.Writer
          141 
          142         name string
          143         fs   afero.Fs
          144 
          145         file afero.File
          146 }
          147 
          148 func (w *writerMock) Write(p []byte) (n int, err error) {
          149         if w.name == "" {
          150                 return 0, ErrEmptyObjectName
          151         }
          152 
          153         if w.file == nil {
          154                 w.file, err = w.fs.Create(w.name)
          155                 if err != nil {
          156                         return 0, err
          157                 }
          158         }
          159 
          160         return w.file.Write(p)
          161 }
          162 
          163 func (w *writerMock) Close() error {
          164         if w.name == "" {
          165                 return ErrEmptyObjectName
          166         }
          167         if w.file == nil {
          168                 var err error
          169                 if strings.HasSuffix(w.name, "/") {
          170                         err = w.fs.Mkdir(w.name, 0o755)
          171                         if err != nil {
          172                                 return err
          173                         }
          174                 } else {
          175                         _, err = w.Write([]byte{})
          176                         if err != nil {
          177                                 return err
          178                         }
          179                 }
          180         }
          181         if w.file != nil {
          182                 return w.file.Close()
          183         }
          184         return nil
          185 }
          186 
          187 type readerMock struct {
          188         stiface.Reader
          189 
          190         file afero.File
          191 
          192         buf []byte
          193 }
          194 
          195 func (r *readerMock) Remain() int64 {
          196         return 0
          197 }
          198 
          199 func (r *readerMock) Read(p []byte) (int, error) {
          200         if r.buf != nil {
          201                 copy(p, r.buf)
          202                 return len(r.buf), nil
          203         }
          204         return r.file.Read(p)
          205 }
          206 
          207 func (r *readerMock) Close() error {
          208         return r.file.Close()
          209 }
          210 
          211 type objectItMock struct {
          212         stiface.ObjectIterator
          213 
          214         name string
          215         fs   afero.Fs
          216 
          217         dir   afero.File
          218         infos []*storage.ObjectAttrs
          219 }
          220 
          221 func (it *objectItMock) Next() (*storage.ObjectAttrs, error) {
          222         var err error
          223         if it.dir == nil {
          224                 it.dir, err = it.fs.Open(it.name)
          225                 if err != nil {
          226                         return nil, err
          227                 }
          228 
          229                 var isDir bool
          230                 isDir, err = afero.IsDir(it.fs, it.name)
          231                 if err != nil {
          232                         return nil, err
          233                 }
          234 
          235                 it.infos = []*storage.ObjectAttrs{}
          236 
          237                 if !isDir {
          238                         var info os.FileInfo
          239                         info, err = it.dir.Stat()
          240                         if err != nil {
          241                                 return nil, err
          242                         }
          243                         it.infos = append(it.infos, &storage.ObjectAttrs{Name: normSeparators(info.Name()), Size: info.Size(), Updated: info.ModTime()})
          244                 } else {
          245                         var fInfos []os.FileInfo
          246                         fInfos, err = it.dir.Readdir(0)
          247                         if err != nil {
          248                                 return nil, err
          249                         }
          250                         if it.name != "" {
          251                                 it.infos = append(it.infos, &storage.ObjectAttrs{
          252                                         Prefix: normSeparators(it.name) + "/",
          253                                 })
          254                         }
          255 
          256                         for _, info := range fInfos {
          257                                 it.infos = append(it.infos, &storage.ObjectAttrs{Name: normSeparators(info.Name()), Size: info.Size(), Updated: info.ModTime()})
          258                         }
          259                 }
          260         }
          261 
          262         if len(it.infos) == 0 {
          263                 return nil, iterator.Done
          264         }
          265 
          266         res := it.infos[0]
          267         it.infos = it.infos[1:]
          268 
          269         return res, err
          270 }