github.com/cs3org/reva/v2@v2.27.7/pkg/storage/fs/owncloudsql/filecache/filecache_test.go (about)

     1  // Copyright 2018-2021 CERN
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // In applying this license, CERN does not waive the privileges and immunities
    16  // granted to it by virtue of its status as an Intergovernmental Organization
    17  // or submit itself to any jurisdiction.
    18  
    19  package filecache_test
    20  
    21  import (
    22  	"context"
    23  	"database/sql"
    24  	"os"
    25  	"strconv"
    26  
    27  	_ "github.com/mattn/go-sqlite3"
    28  
    29  	"github.com/cs3org/reva/v2/pkg/storage/fs/owncloudsql/filecache"
    30  
    31  	. "github.com/onsi/ginkgo/v2"
    32  	. "github.com/onsi/gomega"
    33  )
    34  
    35  var _ = Describe("Filecache", func() {
    36  	var (
    37  		cache      *filecache.Cache
    38  		testDbFile *os.File
    39  		sqldb      *sql.DB
    40  		ctx        context.Context
    41  	)
    42  
    43  	BeforeEach(func() {
    44  		ctx = context.Background()
    45  
    46  		var err error
    47  		testDbFile, err = os.CreateTemp("", "example")
    48  		Expect(err).ToNot(HaveOccurred())
    49  
    50  		dbData, err := os.ReadFile("test.db")
    51  		Expect(err).ToNot(HaveOccurred())
    52  
    53  		_, err = testDbFile.Write(dbData)
    54  		Expect(err).ToNot(HaveOccurred())
    55  		err = testDbFile.Close()
    56  		Expect(err).ToNot(HaveOccurred())
    57  
    58  		sqldb, err = sql.Open("sqlite3", testDbFile.Name())
    59  		Expect(err).ToNot(HaveOccurred())
    60  
    61  		cache, err = filecache.New("sqlite3", sqldb)
    62  		Expect(err).ToNot(HaveOccurred())
    63  	})
    64  
    65  	AfterEach(func() {
    66  		os.Remove(testDbFile.Name())
    67  	})
    68  
    69  	Describe("ListStorages", func() {
    70  		It("returns all storages", func() {
    71  			storages, err := cache.ListStorages(ctx, false)
    72  			Expect(err).ToNot(HaveOccurred())
    73  			Expect(len(storages)).To(Equal(2))
    74  			ids := []string{}
    75  			numericIDs := []int{}
    76  			for _, s := range storages {
    77  				ids = append(ids, s.ID)
    78  				numericIDs = append(numericIDs, s.NumericID)
    79  			}
    80  			Expect(numericIDs).To(ConsistOf([]int{1, 2}))
    81  			Expect(ids).To(ConsistOf([]string{"home::admin", "local::/mnt/data/files/"}))
    82  		})
    83  
    84  		It("returns all home storages", func() {
    85  			storages, err := cache.ListStorages(ctx, true)
    86  			Expect(err).ToNot(HaveOccurred())
    87  			Expect(len(storages)).To(Equal(1))
    88  			Expect(storages[0].ID).To(Equal("home::admin"))
    89  			Expect(storages[0].NumericID).To(Equal(1))
    90  		})
    91  	})
    92  
    93  	Describe("GetStorage", func() {
    94  		It("returns an error when the id is invalid", func() {
    95  			s, err := cache.GetStorage(ctx, "foo")
    96  			Expect(err).To(HaveOccurred())
    97  			Expect(s).To(BeNil())
    98  		})
    99  
   100  		It("returns an error when the id doesn't exist", func() {
   101  			s, err := cache.GetStorage(ctx, 100)
   102  			Expect(err).To(HaveOccurred())
   103  			Expect(s).To(BeNil())
   104  		})
   105  
   106  		It("returns the storage", func() {
   107  			s, err := cache.GetStorage(ctx, 1)
   108  			Expect(err).ToNot(HaveOccurred())
   109  			Expect(s.ID).To(Equal("home::admin"))
   110  			Expect(s.NumericID).To(Equal(1))
   111  		})
   112  
   113  		It("takes string ids", func() {
   114  			s, err := cache.GetStorage(ctx, "1")
   115  			Expect(err).ToNot(HaveOccurred())
   116  			Expect(s.ID).To(Equal("home::admin"))
   117  			Expect(s.NumericID).To(Equal(1))
   118  		})
   119  	})
   120  
   121  	Describe("GetNumericStorageID", func() {
   122  		It("returns the proper storage id", func() {
   123  			nid, err := cache.GetNumericStorageID(ctx, "home::admin")
   124  			Expect(err).ToNot(HaveOccurred())
   125  			Expect(nid).To(Equal(1))
   126  		})
   127  	})
   128  
   129  	Describe("GetStorageOwner", func() {
   130  		It("returns the owner", func() {
   131  			owner, err := cache.GetStorageOwner(ctx, "1")
   132  			Expect(err).ToNot(HaveOccurred())
   133  			Expect(owner).To(Equal("admin"))
   134  		})
   135  	})
   136  
   137  	Describe("CreateStorage", func() {
   138  		It("creates the storage and a root item", func() {
   139  			id, err := cache.CreateStorage(ctx, "bar")
   140  			Expect(err).ToNot(HaveOccurred())
   141  			Expect(id > 0).To(BeTrue())
   142  
   143  			owner, err := cache.GetStorageOwner(ctx, id)
   144  			Expect(err).ToNot(HaveOccurred())
   145  			Expect(owner).To(Equal("bar"))
   146  
   147  			file, err := cache.Get(ctx, 1, "")
   148  			Expect(err).ToNot(HaveOccurred())
   149  			Expect(file).ToNot(BeNil())
   150  		})
   151  	})
   152  	Describe("GetStorageOwnerByFileID", func() {
   153  		It("returns the owner", func() {
   154  			owner, err := cache.GetStorageOwnerByFileID(ctx, "10")
   155  			Expect(err).ToNot(HaveOccurred())
   156  			Expect(owner).To(Equal("admin"))
   157  		})
   158  	})
   159  
   160  	Describe("Get", func() {
   161  		It("gets existing files", func() {
   162  			path := "files/Photos/Portugal.jpg"
   163  			file, err := cache.Get(ctx, 1, path)
   164  			Expect(err).ToNot(HaveOccurred())
   165  			Expect(file).ToNot(BeNil())
   166  			Expect(file.ID).To(Equal(10))
   167  			Expect(file.Storage).To(Equal(1))
   168  			Expect(file.Path).To(Equal(path))
   169  			Expect(file.Parent).To(Equal(9))
   170  			Expect(file.MimeType).To(Equal(6))
   171  			Expect(file.MimePart).To(Equal(5))
   172  			Expect(file.MimeTypeString).To(Equal("image/jpeg"))
   173  			Expect(file.Size).To(Equal(243733))
   174  			Expect(file.MTime).To(Equal(1619007009))
   175  			Expect(file.StorageMTime).To(Equal(1619007009))
   176  			Expect(file.Encrypted).To(BeFalse())
   177  			Expect(file.UnencryptedSize).To(Equal(0))
   178  			Expect(file.Name).To(Equal("Portugal.jpg"))
   179  			Expect(file.Etag).To(Equal("13cf411aefccd7183d3b117ccd0ac5f8"))
   180  			Expect(file.Checksum).To(Equal("SHA1:872adcabcb4e06bea6265200c0d71b12defe2df1 MD5:01b38c622feac31652d738a94e15e86b ADLER32:6959358d"))
   181  		})
   182  	})
   183  
   184  	Describe("List", func() {
   185  		It("lists all entries", func() {
   186  			list, err := cache.List(ctx, 1, "")
   187  			Expect(err).ToNot(HaveOccurred())
   188  			Expect(len(list)).To(Equal(3))
   189  		})
   190  
   191  		It("filters", func() {
   192  			list, err := cache.List(ctx, 1, "files_trashbin/")
   193  			Expect(err).ToNot(HaveOccurred())
   194  			Expect(len(list)).To(Equal(3))
   195  		})
   196  
   197  		It("filters deep", func() {
   198  			list, err := cache.List(ctx, 1, "files/Photos/")
   199  			Expect(err).ToNot(HaveOccurred())
   200  			Expect(len(list)).To(Equal(3))
   201  		})
   202  	})
   203  
   204  	Describe("Path", func() {
   205  		It("returns the path", func() {
   206  			path, err := cache.Path(ctx, 10)
   207  			Expect(err).ToNot(HaveOccurred())
   208  			Expect(path).To(Equal("files/Photos/Portugal.jpg"))
   209  		})
   210  
   211  		It("returns the path when given a string id", func() {
   212  			path, err := cache.Path(ctx, "10")
   213  			Expect(err).ToNot(HaveOccurred())
   214  			Expect(path).To(Equal("files/Photos/Portugal.jpg"))
   215  		})
   216  	})
   217  
   218  	Describe("InsertOrUpdate", func() {
   219  		Context("when inserting a new recored", func() {
   220  			It("checks for required fields", func() {
   221  				data := map[string]interface{}{
   222  					"mimetype": "httpd/unix-directory",
   223  					"etag":     "abcdefg",
   224  				}
   225  				_, err := cache.InsertOrUpdate(ctx, 3, data, false)
   226  				Expect(err).To(MatchError("missing required data"))
   227  
   228  				data = map[string]interface{}{
   229  					"path": "files/Photos/foo.jpg",
   230  					"etag": "abcdefg",
   231  				}
   232  				_, err = cache.InsertOrUpdate(ctx, 3, data, false)
   233  				Expect(err).To(MatchError("missing required data"))
   234  
   235  				data = map[string]interface{}{
   236  					"path":     "files/Photos/foo.jpg",
   237  					"mimetype": "httpd/unix-directory",
   238  				}
   239  				_, err = cache.InsertOrUpdate(ctx, 3, data, false)
   240  				Expect(err).To(MatchError("missing required data"))
   241  			})
   242  
   243  			It("inserts a new minimal entry", func() {
   244  				data := map[string]interface{}{
   245  					"path":     "files/Photos/foo.jpg",
   246  					"mimetype": "httpd/unix-directory",
   247  					"etag":     "abcdefg",
   248  				}
   249  				id, err := cache.InsertOrUpdate(ctx, 1, data, false)
   250  				Expect(err).ToNot(HaveOccurred())
   251  				Expect(id).To(Equal(18))
   252  
   253  				entry, err := cache.Get(ctx, 1, "files/Photos/foo.jpg")
   254  				Expect(err).ToNot(HaveOccurred())
   255  				Expect(entry.Path).To(Equal("files/Photos/foo.jpg"))
   256  				Expect(entry.Name).To(Equal("foo.jpg"))
   257  				Expect(entry.MimeType).To(Equal(2))
   258  				Expect(entry.MimePart).To(Equal(1))
   259  				Expect(entry.Etag).To(Equal("abcdefg"))
   260  			})
   261  
   262  			It("inserts a complete entry", func() {
   263  				data := map[string]interface{}{
   264  					"path":             "files/Photos/foo.jpg",
   265  					"checksum":         "SHA1: abcdefg",
   266  					"etag":             "abcdefg",
   267  					"size":             1234,
   268  					"mimetype":         "image/jpeg",
   269  					"mtime":            1617702482,
   270  					"storage_mtime":    1617702483,
   271  					"encrypted":        true,
   272  					"unencrypted_size": 2000,
   273  				}
   274  				_, err := cache.InsertOrUpdate(ctx, 1, data, false)
   275  				Expect(err).ToNot(HaveOccurred())
   276  
   277  				entry, err := cache.Get(ctx, 1, "files/Photos/foo.jpg")
   278  				Expect(err).ToNot(HaveOccurred())
   279  				Expect(entry.Path).To(Equal("files/Photos/foo.jpg"))
   280  				Expect(entry.Name).To(Equal("foo.jpg"))
   281  				Expect(entry.Checksum).To(Equal("SHA1: abcdefg"))
   282  				Expect(entry.Etag).To(Equal("abcdefg"))
   283  				Expect(entry.Size).To(Equal(1234))
   284  				Expect(entry.MimeType).To(Equal(6))
   285  				Expect(entry.MimePart).To(Equal(5))
   286  				Expect(entry.MTime).To(Equal(1617702482))
   287  				Expect(entry.StorageMTime).To(Equal(1617702483))
   288  				Expect(entry.Encrypted).To(BeTrue())
   289  				Expect(entry.UnencryptedSize).To(Equal(2000))
   290  			})
   291  
   292  			It("sets the parent", func() {
   293  				data := map[string]interface{}{
   294  					"path":     "files/Photos/foo.jpg",
   295  					"mimetype": "httpd/unix-directory",
   296  					"etag":     "abcdefg",
   297  				}
   298  
   299  				_, err := cache.InsertOrUpdate(ctx, 1, data, false)
   300  				Expect(err).ToNot(HaveOccurred())
   301  
   302  				entry, err := cache.Get(ctx, 1, "files/Photos/foo.jpg")
   303  				Expect(err).ToNot(HaveOccurred())
   304  				Expect(entry.Parent).To(Equal(9))
   305  			})
   306  
   307  			It("sets the mtime storage_mtime if not set", func() {
   308  				data := map[string]interface{}{
   309  					"path":          "files/Photos/foo.jpg",
   310  					"mimetype":      "httpd/unix-directory",
   311  					"etag":          "abcdefg",
   312  					"storage_mtime": 1617702483,
   313  				}
   314  
   315  				_, err := cache.InsertOrUpdate(ctx, 1, data, false)
   316  				Expect(err).ToNot(HaveOccurred())
   317  
   318  				entry, err := cache.Get(ctx, 1, "files/Photos/foo.jpg")
   319  				Expect(err).ToNot(HaveOccurred())
   320  				Expect(entry.MTime).To(Equal(1617702483))
   321  			})
   322  
   323  			It("sets the mimetype and part ids from the mimetype string", func() {
   324  				data := map[string]interface{}{
   325  					"path":     "files/Photos/foo.jpg",
   326  					"checksum": "SHA1: abcdefg",
   327  					"etag":     "abcdefg",
   328  					"mimetype": "image/jpeg",
   329  				}
   330  
   331  				_, err := cache.InsertOrUpdate(ctx, 1, data, false)
   332  				Expect(err).ToNot(HaveOccurred())
   333  
   334  				entry, err := cache.Get(ctx, 1, "files/Photos/foo.jpg")
   335  				Expect(err).ToNot(HaveOccurred())
   336  				Expect(entry.MimeType).To(Equal(6))
   337  				Expect(entry.MimePart).To(Equal(5))
   338  			})
   339  
   340  			It("adds unknown mimetypes to the database", func() {
   341  				data := map[string]interface{}{
   342  					"path":     "files/Photos/foo.tiff",
   343  					"checksum": "SHA1: abcdefg",
   344  					"etag":     "abcdefg",
   345  					"mimetype": "image/tiff",
   346  				}
   347  
   348  				_, err := cache.InsertOrUpdate(ctx, 1, data, false)
   349  				Expect(err).ToNot(HaveOccurred())
   350  
   351  				entry, err := cache.Get(ctx, 1, "files/Photos/foo.tiff")
   352  				Expect(err).ToNot(HaveOccurred())
   353  				Expect(entry.MimeType).To(Equal(9))
   354  				Expect(entry.MimePart).To(Equal(5))
   355  			})
   356  
   357  			It("does not add a . as the name for root entries", func() {
   358  				data := map[string]interface{}{
   359  					"path":     "",
   360  					"checksum": "SHA1: abcdefg",
   361  					"etag":     "abcdefg",
   362  					"mimetype": "image/tiff",
   363  				}
   364  
   365  				_, err := cache.InsertOrUpdate(ctx, 1, data, false)
   366  				Expect(err).ToNot(HaveOccurred())
   367  
   368  				file, err := cache.Get(ctx, 1, "")
   369  				Expect(err).ToNot(HaveOccurred())
   370  				Expect(file).ToNot(BeNil())
   371  				Expect(file.Name).To(Equal(""))
   372  			})
   373  		})
   374  
   375  		Context("when updating an existing record", func() {
   376  			var (
   377  				data map[string]interface{}
   378  			)
   379  
   380  			BeforeEach(func() {
   381  				data = map[string]interface{}{
   382  					"path":     "files/Photos/foo.jpg",
   383  					"mimetype": "httpd/unix-directory",
   384  					"etag":     "abcdefg",
   385  				}
   386  				_, err := cache.InsertOrUpdate(ctx, 1, data, false)
   387  				Expect(err).ToNot(HaveOccurred())
   388  			})
   389  
   390  			It("updates the record", func() {
   391  				recordBefore, err := cache.Get(ctx, 1, data["path"].(string))
   392  				Expect(err).ToNot(HaveOccurred())
   393  
   394  				data["etag"] = "12345"
   395  				id, err := cache.InsertOrUpdate(ctx, 1, data, false)
   396  				Expect(err).ToNot(HaveOccurred())
   397  				Expect(id).To(Equal(recordBefore.ID))
   398  
   399  				recordAfter, err := cache.Get(ctx, 1, data["path"].(string))
   400  				Expect(err).ToNot(HaveOccurred())
   401  
   402  				Expect(recordBefore.Etag).To(Equal("abcdefg"))
   403  				Expect(recordAfter.Etag).To(Equal("12345"))
   404  			})
   405  
   406  		})
   407  	})
   408  
   409  	Describe("Move", func() {
   410  		It("moves a file", func() {
   411  			err := cache.Move(ctx, 1, "files/Photos/Portugal.jpg", "files/Documents/Portugal.jpg")
   412  			Expect(err).ToNot(HaveOccurred())
   413  
   414  			_, err = cache.Get(ctx, 1, "files/Photos/Portugal.jpg")
   415  			Expect(err).To(HaveOccurred())
   416  
   417  			newEntry, err := cache.Get(ctx, 1, "files/Documents/Portugal.jpg")
   418  			Expect(err).ToNot(HaveOccurred())
   419  			Expect(newEntry.Path).To(Equal("files/Documents/Portugal.jpg"))
   420  		})
   421  
   422  		It("moves a file while changing its name", func() {
   423  			err := cache.Move(ctx, 1, "files/Photos/Portugal.jpg", "files/Documents/Spain.jpg")
   424  			Expect(err).ToNot(HaveOccurred())
   425  
   426  			_, err = cache.Get(ctx, 1, "files/Photos/Portugal.jpg")
   427  			Expect(err).To(HaveOccurred())
   428  
   429  			newEntry, err := cache.Get(ctx, 1, "files/Documents/Spain.jpg")
   430  			Expect(err).ToNot(HaveOccurred())
   431  			Expect(newEntry.Path).To(Equal("files/Documents/Spain.jpg"))
   432  			Expect(newEntry.Name).To(Equal("Spain.jpg"))
   433  		})
   434  
   435  		It("moves a directory", func() {
   436  			err := cache.Move(ctx, 1, "files/Photos", "files/Foo")
   437  			Expect(err).ToNot(HaveOccurred())
   438  
   439  			_, err = cache.Get(ctx, 1, "files/Photos")
   440  			Expect(err).To(HaveOccurred())
   441  
   442  			_, err = cache.Get(ctx, 1, "files/Photos/Portugal.jpg")
   443  			Expect(err).To(HaveOccurred())
   444  			newEntry, err := cache.Get(ctx, 1, "files/Foo/Portugal.jpg")
   445  			Expect(err).ToNot(HaveOccurred())
   446  			Expect(newEntry.Path).To(Equal("files/Foo/Portugal.jpg"))
   447  		})
   448  	})
   449  
   450  	Describe("SetEtag", func() {
   451  		It("updates the etag", func() {
   452  			entry, err := cache.Get(ctx, 1, "files/Photos/Portugal.jpg")
   453  			Expect(err).ToNot(HaveOccurred())
   454  			Expect(entry.Etag).To(Equal("13cf411aefccd7183d3b117ccd0ac5f8"))
   455  
   456  			err = cache.SetEtag(ctx, 1, "files/Photos/Portugal.jpg", "foo")
   457  			Expect(err).ToNot(HaveOccurred())
   458  
   459  			entry, err = cache.Get(ctx, 1, "files/Photos/Portugal.jpg")
   460  			Expect(err).ToNot(HaveOccurred())
   461  			Expect(entry.Etag).To(Equal("foo"))
   462  		})
   463  	})
   464  
   465  	Context("trash", func() {
   466  		var (
   467  			filePath = "files/Photos/Portugal.jpg"
   468  
   469  			data = map[string]interface{}{
   470  				"path":     "files_trashbin/files/Photos",
   471  				"mimetype": "httpd/unix-directory",
   472  				"etag":     "abcdefg",
   473  			}
   474  			trashPathBase      = "Portugal.jpg"
   475  			trashPathTimestamp = 1619007109
   476  			trashPath          = "files_trashbin/files/" + trashPathBase + ".d" + strconv.Itoa(trashPathTimestamp)
   477  		)
   478  
   479  		BeforeEach(func() {
   480  			_, err := cache.InsertOrUpdate(ctx, 1, data, false)
   481  			Expect(err).ToNot(HaveOccurred())
   482  		})
   483  
   484  		Describe("Delete", func() {
   485  			It("deletes an item", func() {
   486  				err := cache.Delete(ctx, 1, "admin", filePath, trashPath)
   487  				Expect(err).ToNot(HaveOccurred())
   488  
   489  				_, err = cache.Get(ctx, 1, "files/Photos/Portugal.jpg")
   490  				Expect(err).To(HaveOccurred())
   491  				_, err = cache.Get(ctx, 1, "files_trashbin/files/Portugal.jpg.d1619007109")
   492  				Expect(err).ToNot(HaveOccurred())
   493  			})
   494  
   495  			It("creates an entry in the trash table", func() {
   496  				_, err := cache.GetRecycleItem(ctx, "admin", trashPathBase, trashPathTimestamp)
   497  				Expect(err).To(HaveOccurred())
   498  
   499  				err = cache.Delete(ctx, 1, "admin", filePath, trashPath)
   500  				Expect(err).ToNot(HaveOccurred())
   501  
   502  				item, err := cache.GetRecycleItem(ctx, "admin", trashPathBase, trashPathTimestamp)
   503  				Expect(err).ToNot(HaveOccurred())
   504  				Expect(item.Path).To(Equal("Photos"))
   505  			})
   506  
   507  			It("rewrites the path of the children", func() {
   508  				err := cache.Delete(ctx, 1, "admin", "files/Photos", "files_trashbin/files/Photos.d1619007109")
   509  				Expect(err).ToNot(HaveOccurred())
   510  			})
   511  		})
   512  
   513  		Describe("EmptyRecycle", func() {
   514  			It("clears the recycle bin", func() {
   515  				err := cache.Delete(ctx, 1, "admin", filePath, trashPath)
   516  				Expect(err).ToNot(HaveOccurred())
   517  
   518  				err = cache.EmptyRecycle(ctx, "admin")
   519  				Expect(err).ToNot(HaveOccurred())
   520  
   521  				_, err = cache.GetRecycleItem(ctx, "admin", trashPathBase, trashPathTimestamp)
   522  				Expect(err).To(HaveOccurred())
   523  			})
   524  		})
   525  
   526  		Describe("DeleteRecycleItem", func() {
   527  			It("removes the item from the trash", func() {
   528  				err := cache.Delete(ctx, 1, "admin", filePath, trashPath)
   529  				Expect(err).ToNot(HaveOccurred())
   530  
   531  				err = cache.DeleteRecycleItem(ctx, "admin", trashPathBase, trashPathTimestamp)
   532  				Expect(err).ToNot(HaveOccurred())
   533  
   534  				_, err = cache.GetRecycleItem(ctx, "admin", trashPathBase, trashPathTimestamp)
   535  				Expect(err).To(HaveOccurred())
   536  			})
   537  
   538  			It("does not remove the item from the file cache", func() {
   539  				err := cache.Delete(ctx, 1, "admin", filePath, trashPath)
   540  				Expect(err).ToNot(HaveOccurred())
   541  
   542  				err = cache.DeleteRecycleItem(ctx, "admin", trashPathBase, trashPathTimestamp)
   543  				Expect(err).ToNot(HaveOccurred())
   544  
   545  				_, err = cache.Get(ctx, 1, trashPath)
   546  				Expect(err).ToNot(HaveOccurred())
   547  			})
   548  		})
   549  
   550  		Describe("PurgeRecycleItem", func() {
   551  			It("removes the item from the database", func() {
   552  				err := cache.Delete(ctx, 1, "admin", filePath, trashPath)
   553  				Expect(err).ToNot(HaveOccurred())
   554  
   555  				_, err = cache.GetRecycleItem(ctx, "admin", trashPathBase, trashPathTimestamp)
   556  				Expect(err).ToNot(HaveOccurred())
   557  
   558  				err = cache.PurgeRecycleItem(ctx, "admin", trashPathBase, trashPathTimestamp, false)
   559  				Expect(err).ToNot(HaveOccurred())
   560  
   561  				_, err = cache.GetRecycleItem(ctx, "admin", trashPathBase, trashPathTimestamp)
   562  				Expect(err).To(HaveOccurred())
   563  			})
   564  
   565  			It("removes the item from the filecache table", func() {
   566  				err := cache.Delete(ctx, 1, "admin", filePath, trashPath)
   567  				Expect(err).ToNot(HaveOccurred())
   568  
   569  				err = cache.PurgeRecycleItem(ctx, "admin", trashPathBase, trashPathTimestamp, false)
   570  				Expect(err).ToNot(HaveOccurred())
   571  
   572  				_, err = cache.Get(ctx, 1, trashPath)
   573  				Expect(err).To(HaveOccurred())
   574  			})
   575  
   576  			It("removes children from the filecache table", func() {
   577  				err := cache.Delete(ctx, 1, "admin", "files/Photos", "files_trashbin/files/Photos.d1619007109")
   578  				Expect(err).ToNot(HaveOccurred())
   579  
   580  				_, err = cache.Get(ctx, 1, "files_trashbin/files/Photos.d1619007109/Portugal.jpg")
   581  				Expect(err).ToNot(HaveOccurred())
   582  
   583  				err = cache.PurgeRecycleItem(ctx, "admin", "Photos", 1619007109, false)
   584  				Expect(err).ToNot(HaveOccurred())
   585  
   586  				_, err = cache.Get(ctx, 1, "files_trashbin/files/Photos.d1619007109/Portugal.jpg")
   587  				Expect(err).To(HaveOccurred())
   588  			})
   589  		})
   590  	})
   591  
   592  	Describe("Copy", func() {
   593  		It("copies the entry", func() {
   594  			for _, dir := range []string{"files_versions", "files_versions/Photos"} {
   595  				parentData := map[string]interface{}{
   596  					"path":     dir,
   597  					"mimetype": "httpd/unix-directory",
   598  					"etag":     "abcdefg",
   599  				}
   600  				_, err := cache.InsertOrUpdate(ctx, 1, parentData, false)
   601  				Expect(err).ToNot(HaveOccurred())
   602  			}
   603  
   604  			existingEntry, err := cache.Get(ctx, 1, "files/Photos/Portugal.jpg")
   605  			Expect(err).ToNot(HaveOccurred())
   606  			_, err = cache.Copy(ctx, 1, "files/Photos/Portugal.jpg", "files_versions/Photos/Portugal.jpg.v1619528083")
   607  			Expect(err).ToNot(HaveOccurred())
   608  
   609  			newEntry, err := cache.Get(ctx, 1, "files_versions/Photos/Portugal.jpg.v1619528083")
   610  			Expect(err).ToNot(HaveOccurred())
   611  			Expect(newEntry.ID).ToNot(Equal(existingEntry.ID))
   612  			Expect(newEntry.MimeType).To(Equal(existingEntry.MimeType))
   613  		})
   614  	})
   615  
   616  	Describe("Permissions", func() {
   617  		It("returns the permissions", func() {
   618  			perms, err := cache.Permissions(ctx, 1, "files/Photos/Portugal.jpg")
   619  			Expect(err).ToNot(HaveOccurred())
   620  			Expect(perms).ToNot(BeNil())
   621  			Expect(perms.InitiateFileUpload).To(BeTrue())
   622  
   623  			perms, err = cache.Permissions(ctx, 1, "files/Photos/Teotihuacan.jpg")
   624  			Expect(err).ToNot(HaveOccurred())
   625  			Expect(perms).ToNot(BeNil())
   626  			Expect(perms.InitiateFileUpload).To(BeFalse())
   627  		})
   628  	})
   629  })