github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/backend/union/union_internal_test.go (about) 1 package union 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "testing" 8 "time" 9 10 "github.com/rclone/rclone/fs" 11 "github.com/rclone/rclone/fs/object" 12 "github.com/rclone/rclone/fs/operations" 13 "github.com/rclone/rclone/fstest" 14 "github.com/rclone/rclone/fstest/fstests" 15 "github.com/rclone/rclone/lib/random" 16 "github.com/stretchr/testify/assert" 17 "github.com/stretchr/testify/require" 18 ) 19 20 // MakeTestDirs makes directories in /tmp for testing 21 func MakeTestDirs(t *testing.T, n int) (dirs []string) { 22 for i := 1; i <= n; i++ { 23 dir := t.TempDir() 24 dirs = append(dirs, dir) 25 } 26 return dirs 27 } 28 29 func (f *Fs) TestInternalReadOnly(t *testing.T) { 30 if f.name != "TestUnionRO" { 31 t.Skip("Only on RO union") 32 } 33 dir := "TestInternalReadOnly" 34 ctx := context.Background() 35 rofs := f.upstreams[len(f.upstreams)-1] 36 assert.False(t, rofs.IsWritable()) 37 38 // Put a file onto the read only fs 39 contents := random.String(50) 40 file1 := fstest.NewItem(dir+"/file.txt", contents, time.Now()) 41 obj1 := fstests.PutTestContents(ctx, t, rofs, &file1, contents, true) 42 43 // Check read from readonly fs via union 44 o, err := f.NewObject(ctx, file1.Path) 45 require.NoError(t, err) 46 assert.Equal(t, int64(50), o.Size()) 47 48 // Now call Update on the union Object with new data 49 contents2 := random.String(100) 50 file2 := fstest.NewItem(dir+"/file.txt", contents2, time.Now()) 51 in := bytes.NewBufferString(contents2) 52 src := object.NewStaticObjectInfo(file2.Path, file2.ModTime, file2.Size, true, nil, nil) 53 err = o.Update(ctx, in, src) 54 require.NoError(t, err) 55 assert.Equal(t, int64(100), o.Size()) 56 57 // Check we read the new object via the union 58 o, err = f.NewObject(ctx, file1.Path) 59 require.NoError(t, err) 60 assert.Equal(t, int64(100), o.Size()) 61 62 // Remove the object 63 assert.NoError(t, o.Remove(ctx)) 64 65 // Check we read the old object in the read only layer now 66 o, err = f.NewObject(ctx, file1.Path) 67 require.NoError(t, err) 68 assert.Equal(t, int64(50), o.Size()) 69 70 // Remove file and dir from read only fs 71 assert.NoError(t, obj1.Remove(ctx)) 72 assert.NoError(t, rofs.Rmdir(ctx, dir)) 73 } 74 75 func (f *Fs) InternalTest(t *testing.T) { 76 t.Run("ReadOnly", f.TestInternalReadOnly) 77 } 78 79 var _ fstests.InternalTester = (*Fs)(nil) 80 81 // This specifically tests a union of local which can Move but not 82 // Copy and :memory: which can Copy but not Move to makes sure that 83 // the resulting union can Move 84 func TestMoveCopy(t *testing.T) { 85 if *fstest.RemoteName != "" { 86 t.Skip("Skipping as -remote set") 87 } 88 ctx := context.Background() 89 dirs := MakeTestDirs(t, 1) 90 fsString := fmt.Sprintf(":union,upstreams='%s :memory:bucket':", dirs[0]) 91 f, err := fs.NewFs(ctx, fsString) 92 require.NoError(t, err) 93 94 unionFs := f.(*Fs) 95 fLocal := unionFs.upstreams[0].Fs 96 fMemory := unionFs.upstreams[1].Fs 97 98 t.Run("Features", func(t *testing.T) { 99 assert.NotNil(t, f.Features().Move) 100 assert.Nil(t, f.Features().Copy) 101 102 // Check underlying are as we are expect 103 assert.NotNil(t, fLocal.Features().Move) 104 assert.Nil(t, fLocal.Features().Copy) 105 assert.Nil(t, fMemory.Features().Move) 106 assert.NotNil(t, fMemory.Features().Copy) 107 }) 108 109 // Put a file onto the local fs 110 contentsLocal := random.String(50) 111 fileLocal := fstest.NewItem("local.txt", contentsLocal, time.Now()) 112 _ = fstests.PutTestContents(ctx, t, fLocal, &fileLocal, contentsLocal, true) 113 objLocal, err := f.NewObject(ctx, fileLocal.Path) 114 require.NoError(t, err) 115 116 // Put a file onto the memory fs 117 contentsMemory := random.String(60) 118 fileMemory := fstest.NewItem("memory.txt", contentsMemory, time.Now()) 119 _ = fstests.PutTestContents(ctx, t, fMemory, &fileMemory, contentsMemory, true) 120 objMemory, err := f.NewObject(ctx, fileMemory.Path) 121 require.NoError(t, err) 122 123 fstest.CheckListing(t, f, []fstest.Item{fileLocal, fileMemory}) 124 125 t.Run("MoveLocal", func(t *testing.T) { 126 fileLocal.Path = "local-renamed.txt" 127 _, err := operations.Move(ctx, f, nil, fileLocal.Path, objLocal) 128 require.NoError(t, err) 129 fstest.CheckListing(t, f, []fstest.Item{fileLocal, fileMemory}) 130 131 // Check can retrieve object from union 132 obj, err := f.NewObject(ctx, fileLocal.Path) 133 require.NoError(t, err) 134 assert.Equal(t, fileLocal.Size, obj.Size()) 135 136 // Check can retrieve object from underlying 137 obj, err = fLocal.NewObject(ctx, fileLocal.Path) 138 require.NoError(t, err) 139 assert.Equal(t, fileLocal.Size, obj.Size()) 140 141 t.Run("MoveMemory", func(t *testing.T) { 142 fileMemory.Path = "memory-renamed.txt" 143 _, err := operations.Move(ctx, f, nil, fileMemory.Path, objMemory) 144 require.NoError(t, err) 145 fstest.CheckListing(t, f, []fstest.Item{fileLocal, fileMemory}) 146 147 // Check can retrieve object from union 148 obj, err := f.NewObject(ctx, fileMemory.Path) 149 require.NoError(t, err) 150 assert.Equal(t, fileMemory.Size, obj.Size()) 151 152 // Check can retrieve object from underlying 153 obj, err = fMemory.NewObject(ctx, fileMemory.Path) 154 require.NoError(t, err) 155 assert.Equal(t, fileMemory.Size, obj.Size()) 156 }) 157 }) 158 }