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  }