github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/backend/onedrive/onedrive_internal_test.go (about)

     1  package onedrive
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"testing"
     8  	"time"
     9  
    10  	_ "github.com/rclone/rclone/backend/local"
    11  	"github.com/rclone/rclone/backend/onedrive/api"
    12  	"github.com/rclone/rclone/fs"
    13  	"github.com/rclone/rclone/fs/operations"
    14  	"github.com/rclone/rclone/fstest"
    15  	"github.com/rclone/rclone/fstest/fstests"
    16  	"github.com/rclone/rclone/lib/random"
    17  	"github.com/stretchr/testify/assert"
    18  	"github.com/stretchr/testify/require"
    19  	"golang.org/x/exp/slices" // replace with slices after go1.21 is the minimum version
    20  )
    21  
    22  // go test -timeout 30m -run ^TestIntegration/FsMkdir/FsPutFiles/Internal$ github.com/rclone/rclone/backend/onedrive -remote TestOneDrive:meta -v
    23  // go test -timeout 30m -run ^TestIntegration/FsMkdir/FsPutFiles/Internal$ github.com/rclone/rclone/backend/onedrive -remote TestOneDriveBusiness:meta -v
    24  // go run ./fstest/test_all -remotes TestOneDriveBusiness:meta,TestOneDrive:meta -verbose -maxtries 1
    25  
    26  var (
    27  	t1      = fstest.Time("2023-08-26T23:13:06.499999999Z")
    28  	t2      = fstest.Time("2020-02-29T12:34:56.789Z")
    29  	t3      = time.Date(1994, time.December, 24, 9+12, 0, 0, 525600, time.FixedZone("Eastern Standard Time", -5))
    30  	ctx     = context.Background()
    31  	content = "hello"
    32  )
    33  
    34  const (
    35  	testUserID = "ryan@contoso.com" // demo user from doc examples (can't share files with yourself)
    36  	// https://learn.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_invite?view=odsp-graph-online#http-request-1
    37  )
    38  
    39  // TestMain drives the tests
    40  func TestMain(m *testing.M) {
    41  	fstest.TestMain(m)
    42  }
    43  
    44  // TestWritePermissions tests reading and writing permissions
    45  func (f *Fs) TestWritePermissions(t *testing.T, r *fstest.Run) {
    46  	// setup
    47  	ctx, ci := fs.AddConfig(ctx)
    48  	ci.Metadata = true
    49  	_ = f.opt.MetadataPermissions.Set("read,write")
    50  	file1 := r.WriteFile(randomFilename(), content, t2)
    51  
    52  	// add a permission with "read" role
    53  	permissions := defaultPermissions(f.driveType)
    54  	permissions[0].Roles[0] = api.ReadRole
    55  	expectedMeta, actualMeta := f.putWithMeta(ctx, t, &file1, permissions)
    56  	f.compareMeta(t, expectedMeta, actualMeta, false)
    57  	expectedP, actualP := unmarshalPerms(t, expectedMeta["permissions"]), unmarshalPerms(t, actualMeta["permissions"])
    58  
    59  	found, num := false, 0
    60  	foundCount := 0
    61  	for i, p := range actualP {
    62  		for _, identity := range p.GetGrantedToIdentities(f.driveType) {
    63  			if identity.User.DisplayName == testUserID {
    64  				// note: expected will always be element 0 here, but actual may be variable based on org settings
    65  				assert.Equal(t, expectedP[0].Roles, p.Roles)
    66  				found, num = true, i
    67  				foundCount++
    68  			}
    69  		}
    70  		if f.driveType == driveTypePersonal {
    71  			if p.GetGrantedTo(f.driveType) != nil && p.GetGrantedTo(f.driveType).User != (api.Identity{}) && p.GetGrantedTo(f.driveType).User.ID == testUserID { // shows up in a different place on biz vs. personal
    72  				assert.Equal(t, expectedP[0].Roles, p.Roles)
    73  				found, num = true, i
    74  				foundCount++
    75  			}
    76  		}
    77  	}
    78  	assert.True(t, found, fmt.Sprintf("no permission found with expected role (want: \n\n%v \n\ngot: \n\n%v\n\n)", indent(t, expectedMeta["permissions"]), indent(t, actualMeta["permissions"])))
    79  	assert.Equal(t, 1, foundCount, "expected to find exactly 1 match")
    80  
    81  	// update it to "write"
    82  	permissions = actualP
    83  	permissions[num].Roles[0] = api.WriteRole
    84  	expectedMeta, actualMeta = f.putWithMeta(ctx, t, &file1, permissions)
    85  	f.compareMeta(t, expectedMeta, actualMeta, false)
    86  	if f.driveType != driveTypePersonal {
    87  		// zero out some things we expect to be different
    88  		expectedP, actualP = unmarshalPerms(t, expectedMeta["permissions"]), unmarshalPerms(t, actualMeta["permissions"])
    89  		normalize(expectedP)
    90  		normalize(actualP)
    91  		expectedMeta.Set("permissions", marshalPerms(t, expectedP))
    92  		actualMeta.Set("permissions", marshalPerms(t, actualP))
    93  	}
    94  	assert.JSONEq(t, expectedMeta["permissions"], actualMeta["permissions"])
    95  
    96  	// remove it
    97  	permissions[num] = nil
    98  	_, actualMeta = f.putWithMeta(ctx, t, &file1, permissions)
    99  	if f.driveType == driveTypePersonal {
   100  		perms, ok := actualMeta["permissions"]
   101  		assert.False(t, ok, fmt.Sprintf("permissions metadata key was unexpectedly found: %v", perms))
   102  		return
   103  	}
   104  	_, actualP = unmarshalPerms(t, expectedMeta["permissions"]), unmarshalPerms(t, actualMeta["permissions"])
   105  
   106  	found = false
   107  	var foundP *api.PermissionsType
   108  	for _, p := range actualP {
   109  		if p.GetGrantedTo(f.driveType) == nil || p.GetGrantedTo(f.driveType).User == (api.Identity{}) || p.GetGrantedTo(f.driveType).User.ID != testUserID {
   110  			continue
   111  		}
   112  		found = true
   113  		foundP = p
   114  	}
   115  	assert.False(t, found, fmt.Sprintf("permission was found but expected to be removed: %v", foundP))
   116  }
   117  
   118  // TestUploadSinglePart tests reading/writing permissions using uploadSinglepart()
   119  // This is only used when file size is exactly 0.
   120  func (f *Fs) TestUploadSinglePart(t *testing.T, r *fstest.Run) {
   121  	content = ""
   122  	f.TestWritePermissions(t, r)
   123  	content = "hello"
   124  }
   125  
   126  // TestReadPermissions tests that no permissions are written when --onedrive-metadata-permissions has "read" but not "write"
   127  func (f *Fs) TestReadPermissions(t *testing.T, r *fstest.Run) {
   128  	// setup
   129  	ctx, ci := fs.AddConfig(ctx)
   130  	ci.Metadata = true
   131  	file1 := r.WriteFile(randomFilename(), "hello", t2)
   132  
   133  	// try adding a permission without --onedrive-metadata-permissions -- should fail
   134  	// test that what we got before vs. after is the same
   135  	_ = f.opt.MetadataPermissions.Set("read")
   136  	_, expectedMeta := f.putWithMeta(ctx, t, &file1, []*api.PermissionsType{}) // return var intentionally switched here
   137  	permissions := defaultPermissions(f.driveType)
   138  	_, actualMeta := f.putWithMeta(ctx, t, &file1, permissions)
   139  	if f.driveType == driveTypePersonal {
   140  		perms, ok := actualMeta["permissions"]
   141  		assert.False(t, ok, fmt.Sprintf("permissions metadata key was unexpectedly found: %v", perms))
   142  		return
   143  	}
   144  	assert.JSONEq(t, expectedMeta["permissions"], actualMeta["permissions"])
   145  }
   146  
   147  // TestReadMetadata tests that all the read-only system properties are present and non-blank
   148  func (f *Fs) TestReadMetadata(t *testing.T, r *fstest.Run) {
   149  	// setup
   150  	ctx, ci := fs.AddConfig(ctx)
   151  	ci.Metadata = true
   152  	file1 := r.WriteFile(randomFilename(), "hello", t2)
   153  	permissions := defaultPermissions(f.driveType)
   154  
   155  	_ = f.opt.MetadataPermissions.Set("read,write")
   156  	_, actualMeta := f.putWithMeta(ctx, t, &file1, permissions)
   157  	optionals := []string{"package-type", "shared-by-id", "shared-scope", "shared-time", "shared-owner-id"} // not always present
   158  	for k := range systemMetadataInfo {
   159  		if slices.Contains(optionals, k) {
   160  			continue
   161  		}
   162  		if k == "description" && f.driveType != driveTypePersonal {
   163  			continue // not supported
   164  		}
   165  		gotV, ok := actualMeta[k]
   166  		assert.True(t, ok, fmt.Sprintf("property is missing: %v", k))
   167  		assert.NotEmpty(t, gotV, fmt.Sprintf("property is blank: %v", k))
   168  	}
   169  }
   170  
   171  // TestDirectoryMetadata tests reading and writing modtime and other metadata and permissions for directories
   172  func (f *Fs) TestDirectoryMetadata(t *testing.T, r *fstest.Run) {
   173  	// setup
   174  	ctx, ci := fs.AddConfig(ctx)
   175  	ci.Metadata = true
   176  	_ = f.opt.MetadataPermissions.Set("read,write")
   177  	permissions := defaultPermissions(f.driveType)
   178  	permissions[0].Roles[0] = api.ReadRole
   179  
   180  	expectedMeta := fs.Metadata{
   181  		"mtime":        t1.Format(timeFormatOut),
   182  		"btime":        t2.Format(timeFormatOut),
   183  		"content-type": dirMimeType,
   184  		"description":  "that is so meta!",
   185  	}
   186  	b, err := json.MarshalIndent(permissions, "", "\t")
   187  	assert.NoError(t, err)
   188  	expectedMeta.Set("permissions", string(b))
   189  
   190  	compareDirMeta := func(expectedMeta, actualMeta fs.Metadata, ignoreID bool) {
   191  		f.compareMeta(t, expectedMeta, actualMeta, ignoreID)
   192  
   193  		// check that all required system properties are present
   194  		optionals := []string{"package-type", "shared-by-id", "shared-scope", "shared-time", "shared-owner-id"} // not always present
   195  		for k := range systemMetadataInfo {
   196  			if slices.Contains(optionals, k) {
   197  				continue
   198  			}
   199  			if k == "description" && f.driveType != driveTypePersonal {
   200  				continue // not supported
   201  			}
   202  			gotV, ok := actualMeta[k]
   203  			assert.True(t, ok, fmt.Sprintf("property is missing: %v", k))
   204  			assert.NotEmpty(t, gotV, fmt.Sprintf("property is blank: %v", k))
   205  		}
   206  	}
   207  	newDst, err := operations.MkdirMetadata(ctx, f, "subdir", expectedMeta)
   208  	assert.NoError(t, err)
   209  	require.NotNil(t, newDst)
   210  	assert.Equal(t, "subdir", newDst.Remote())
   211  
   212  	actualMeta, err := fs.GetMetadata(ctx, newDst)
   213  	assert.NoError(t, err)
   214  	assert.NotNil(t, actualMeta)
   215  	compareDirMeta(expectedMeta, actualMeta, false)
   216  
   217  	// modtime
   218  	assert.Equal(t, t1.Truncate(f.Precision()), newDst.ModTime(ctx))
   219  	// try changing it and re-check it
   220  	newDst, err = operations.SetDirModTime(ctx, f, newDst, "", t2)
   221  	assert.NoError(t, err)
   222  	assert.Equal(t, t2.Truncate(f.Precision()), newDst.ModTime(ctx))
   223  	// ensure that f.DirSetModTime also works
   224  	err = f.DirSetModTime(ctx, "subdir", t3)
   225  	assert.NoError(t, err)
   226  	entries, err := f.List(ctx, "")
   227  	assert.NoError(t, err)
   228  	entries.ForDir(func(dir fs.Directory) {
   229  		if dir.Remote() == "subdir" {
   230  			assert.True(t, t3.Truncate(f.Precision()).Equal(dir.ModTime(ctx)), fmt.Sprintf("got %v", dir.ModTime(ctx)))
   231  		}
   232  	})
   233  
   234  	// test updating metadata on existing dir
   235  	actualMeta, err = fs.GetMetadata(ctx, newDst) // get fresh info as we've been changing modtimes
   236  	assert.NoError(t, err)
   237  	expectedMeta = actualMeta
   238  	expectedMeta.Set("description", "metadata is fun!")
   239  	expectedMeta.Set("btime", t3.Format(timeFormatOut))
   240  	expectedMeta.Set("mtime", t1.Format(timeFormatOut))
   241  	expectedMeta.Set("content-type", dirMimeType)
   242  	perms := unmarshalPerms(t, expectedMeta["permissions"])
   243  	perms[0].Roles[0] = api.WriteRole
   244  	b, err = json.MarshalIndent(perms, "", "\t")
   245  	assert.NoError(t, err)
   246  	expectedMeta.Set("permissions", string(b))
   247  
   248  	newDst, err = operations.MkdirMetadata(ctx, f, "subdir", expectedMeta)
   249  	assert.NoError(t, err)
   250  	require.NotNil(t, newDst)
   251  	assert.Equal(t, "subdir", newDst.Remote())
   252  
   253  	actualMeta, err = fs.GetMetadata(ctx, newDst)
   254  	assert.NoError(t, err)
   255  	assert.NotNil(t, actualMeta)
   256  	compareDirMeta(expectedMeta, actualMeta, false)
   257  
   258  	// test copying metadata from one dir to another
   259  	copiedDir, err := operations.CopyDirMetadata(ctx, f, nil, "subdir2", newDst)
   260  	assert.NoError(t, err)
   261  	require.NotNil(t, copiedDir)
   262  	assert.Equal(t, "subdir2", copiedDir.Remote())
   263  
   264  	actualMeta, err = fs.GetMetadata(ctx, copiedDir)
   265  	assert.NoError(t, err)
   266  	assert.NotNil(t, actualMeta)
   267  	compareDirMeta(expectedMeta, actualMeta, true)
   268  
   269  	// test DirModTimeUpdatesOnWrite
   270  	expectedTime := copiedDir.ModTime(ctx)
   271  	assert.True(t, !expectedTime.IsZero())
   272  	r.WriteObject(ctx, copiedDir.Remote()+"/"+randomFilename(), "hi there", t3)
   273  	entries, err = f.List(ctx, "")
   274  	assert.NoError(t, err)
   275  	entries.ForDir(func(dir fs.Directory) {
   276  		if dir.Remote() == copiedDir.Remote() {
   277  			assert.True(t, expectedTime.Equal(dir.ModTime(ctx)), fmt.Sprintf("want %v got %v", expectedTime, dir.ModTime(ctx)))
   278  		}
   279  	})
   280  }
   281  
   282  // TestServerSideCopyMove tests server-side Copy and Move
   283  func (f *Fs) TestServerSideCopyMove(t *testing.T, r *fstest.Run) {
   284  	// setup
   285  	ctx, ci := fs.AddConfig(ctx)
   286  	ci.Metadata = true
   287  	_ = f.opt.MetadataPermissions.Set("read,write")
   288  	file1 := r.WriteFile(randomFilename(), content, t2)
   289  
   290  	// add a permission with "read" role
   291  	permissions := defaultPermissions(f.driveType)
   292  	permissions[0].Roles[0] = api.ReadRole
   293  	expectedMeta, actualMeta := f.putWithMeta(ctx, t, &file1, permissions)
   294  	f.compareMeta(t, expectedMeta, actualMeta, false)
   295  
   296  	comparePerms := func(expectedMeta, actualMeta fs.Metadata) (newExpectedMeta, newActualMeta fs.Metadata) {
   297  		expectedP, actualP := unmarshalPerms(t, expectedMeta["permissions"]), unmarshalPerms(t, actualMeta["permissions"])
   298  		normalize(expectedP)
   299  		normalize(actualP)
   300  		expectedMeta.Set("permissions", marshalPerms(t, expectedP))
   301  		actualMeta.Set("permissions", marshalPerms(t, actualP))
   302  		assert.JSONEq(t, expectedMeta["permissions"], actualMeta["permissions"])
   303  		return expectedMeta, actualMeta
   304  	}
   305  
   306  	// Copy
   307  	obj1, err := f.NewObject(ctx, file1.Path)
   308  	assert.NoError(t, err)
   309  	originalMeta := actualMeta
   310  	obj2, err := f.Copy(ctx, obj1, randomFilename())
   311  	assert.NoError(t, err)
   312  	actualMeta, err = fs.GetMetadata(ctx, obj2)
   313  	assert.NoError(t, err)
   314  	expectedMeta, actualMeta = comparePerms(originalMeta, actualMeta)
   315  	f.compareMeta(t, expectedMeta, actualMeta, true)
   316  
   317  	// Move
   318  	obj3, err := f.Move(ctx, obj1, randomFilename())
   319  	assert.NoError(t, err)
   320  	actualMeta, err = fs.GetMetadata(ctx, obj3)
   321  	assert.NoError(t, err)
   322  	expectedMeta, actualMeta = comparePerms(originalMeta, actualMeta)
   323  	f.compareMeta(t, expectedMeta, actualMeta, true)
   324  }
   325  
   326  // TestMetadataMapper tests adding permissions with the --metadata-mapper
   327  func (f *Fs) TestMetadataMapper(t *testing.T, r *fstest.Run) {
   328  	// setup
   329  	ctx, ci := fs.AddConfig(ctx)
   330  	ci.Metadata = true
   331  	_ = f.opt.MetadataPermissions.Set("read,write")
   332  	file1 := r.WriteFile(randomFilename(), content, t2)
   333  
   334  	blob := `{"Metadata":{"permissions":"[{\"grantedToIdentities\":[{\"user\":{\"id\":\"ryan@contoso.com\"}}],\"roles\":[\"read\"]}]"}}`
   335  	if f.driveType != driveTypePersonal {
   336  		blob = `{"Metadata":{"permissions":"[{\"grantedToIdentitiesV2\":[{\"user\":{\"id\":\"ryan@contoso.com\"}}],\"roles\":[\"read\"]}]"}}`
   337  	}
   338  
   339  	// Copy
   340  	ci.MetadataMapper = []string{"echo", blob}
   341  	require.NoError(t, ci.Dump.Set("mapper"))
   342  	obj1, err := r.Flocal.NewObject(ctx, file1.Path)
   343  	assert.NoError(t, err)
   344  	obj2, err := operations.Copy(ctx, f, nil, randomFilename(), obj1)
   345  	assert.NoError(t, err)
   346  	actualMeta, err := fs.GetMetadata(ctx, obj2)
   347  	assert.NoError(t, err)
   348  
   349  	actualP := unmarshalPerms(t, actualMeta["permissions"])
   350  	found := false
   351  	foundCount := 0
   352  	for _, p := range actualP {
   353  		for _, identity := range p.GetGrantedToIdentities(f.driveType) {
   354  			if identity.User.DisplayName == testUserID {
   355  				assert.Equal(t, []api.Role{api.ReadRole}, p.Roles)
   356  				found = true
   357  				foundCount++
   358  			}
   359  		}
   360  		if f.driveType == driveTypePersonal {
   361  			if p.GetGrantedTo(f.driveType) != nil && p.GetGrantedTo(f.driveType).User != (api.Identity{}) && p.GetGrantedTo(f.driveType).User.ID == testUserID { // shows up in a different place on biz vs. personal
   362  				assert.Equal(t, []api.Role{api.ReadRole}, p.Roles)
   363  				found = true
   364  				foundCount++
   365  			}
   366  		}
   367  	}
   368  	assert.True(t, found, fmt.Sprintf("no permission found with expected role (want: \n\n%v \n\ngot: \n\n%v\n\n)", blob, actualMeta))
   369  	assert.Equal(t, 1, foundCount, "expected to find exactly 1 match")
   370  }
   371  
   372  // helper function to put an object with metadata and permissions
   373  func (f *Fs) putWithMeta(ctx context.Context, t *testing.T, file *fstest.Item, perms []*api.PermissionsType) (expectedMeta, actualMeta fs.Metadata) {
   374  	t.Helper()
   375  	expectedMeta = fs.Metadata{
   376  		"mtime":       t1.Format(timeFormatOut),
   377  		"btime":       t2.Format(timeFormatOut),
   378  		"description": "that is so meta!",
   379  	}
   380  
   381  	expectedMeta.Set("permissions", marshalPerms(t, perms))
   382  	obj := fstests.PutTestContentsMetadata(ctx, t, f, file, content, true, "plain/text", expectedMeta)
   383  	do, ok := obj.(fs.Metadataer)
   384  	require.True(t, ok)
   385  	actualMeta, err := do.Metadata(ctx)
   386  	require.NoError(t, err)
   387  	return expectedMeta, actualMeta
   388  }
   389  
   390  func randomFilename() string {
   391  	return "some file-" + random.String(8) + ".txt"
   392  }
   393  
   394  func (f *Fs) compareMeta(t *testing.T, expectedMeta, actualMeta fs.Metadata, ignoreID bool) {
   395  	t.Helper()
   396  	for k, v := range expectedMeta {
   397  		gotV, ok := actualMeta[k]
   398  		switch k {
   399  		case "shared-owner-id", "shared-time", "shared-by-id", "shared-scope":
   400  			continue
   401  		case "permissions":
   402  			continue
   403  		case "utime":
   404  			assert.True(t, ok, fmt.Sprintf("expected metadata key is missing: %v", k))
   405  			if f.driveType == driveTypePersonal {
   406  				compareTimeStrings(t, k, v, gotV, time.Minute) // read-only upload time, so slight difference expected -- use larger precision
   407  				continue
   408  			}
   409  			compareTimeStrings(t, k, expectedMeta["btime"], gotV, time.Minute) // another bizarre difference between personal and business...
   410  			continue
   411  		case "id":
   412  			if ignoreID {
   413  				continue // different id is expected when copying meta from one item to another
   414  			}
   415  		case "mtime", "btime":
   416  			assert.True(t, ok, fmt.Sprintf("expected metadata key is missing: %v", k))
   417  			compareTimeStrings(t, k, v, gotV, time.Second)
   418  			continue
   419  		case "description":
   420  			if f.driveType != driveTypePersonal {
   421  				continue // not supported
   422  			}
   423  		}
   424  		assert.True(t, ok, fmt.Sprintf("expected metadata key is missing: %v", k))
   425  		assert.Equal(t, v, gotV, actualMeta)
   426  	}
   427  }
   428  
   429  func compareTimeStrings(t *testing.T, remote, want, got string, precision time.Duration) {
   430  	wantT, err := time.Parse(timeFormatIn, want)
   431  	assert.NoError(t, err)
   432  	gotT, err := time.Parse(timeFormatIn, got)
   433  	assert.NoError(t, err)
   434  	fstest.AssertTimeEqualWithPrecision(t, remote, wantT, gotT, precision)
   435  }
   436  
   437  func marshalPerms(t *testing.T, p []*api.PermissionsType) string {
   438  	b, err := json.MarshalIndent(p, "", "\t")
   439  	assert.NoError(t, err)
   440  	return string(b)
   441  }
   442  
   443  func unmarshalPerms(t *testing.T, perms string) (p []*api.PermissionsType) {
   444  	t.Helper()
   445  	err := json.Unmarshal([]byte(perms), &p)
   446  	assert.NoError(t, err)
   447  	return p
   448  }
   449  
   450  func indent(t *testing.T, s string) string {
   451  	p := unmarshalPerms(t, s)
   452  	return marshalPerms(t, p)
   453  }
   454  
   455  func defaultPermissions(driveType string) []*api.PermissionsType {
   456  	if driveType == driveTypePersonal {
   457  		return []*api.PermissionsType{{
   458  			GrantedTo:           &api.IdentitySet{User: api.Identity{}},
   459  			GrantedToIdentities: []*api.IdentitySet{{User: api.Identity{ID: testUserID}}},
   460  			Roles:               []api.Role{api.WriteRole},
   461  		}}
   462  	}
   463  	return []*api.PermissionsType{{
   464  		GrantedToV2:           &api.IdentitySet{User: api.Identity{}},
   465  		GrantedToIdentitiesV2: []*api.IdentitySet{{User: api.Identity{ID: testUserID}}},
   466  		Roles:                 []api.Role{api.WriteRole},
   467  	}}
   468  }
   469  
   470  // zeroes out some things we expect to be different when copying/moving between objects
   471  func normalize(Ps []*api.PermissionsType) {
   472  	for _, ep := range Ps {
   473  		ep.ID = ""
   474  		ep.Link = nil
   475  		ep.ShareID = ""
   476  	}
   477  }
   478  
   479  func (f *Fs) resetTestDefaults(r *fstest.Run) {
   480  	ci := fs.GetConfig(ctx)
   481  	ci.Metadata = false
   482  	_ = f.opt.MetadataPermissions.Set("off")
   483  	r.Finalise()
   484  }
   485  
   486  // InternalTest dispatches all internal tests
   487  func (f *Fs) InternalTest(t *testing.T) {
   488  	newTestF := func() (*Fs, *fstest.Run) {
   489  		r := fstest.NewRunIndividual(t)
   490  		testF, ok := r.Fremote.(*Fs)
   491  		if !ok {
   492  			t.FailNow()
   493  		}
   494  		return testF, r
   495  	}
   496  
   497  	testF, r := newTestF()
   498  	t.Run("TestWritePermissions", func(t *testing.T) { testF.TestWritePermissions(t, r) })
   499  	testF.resetTestDefaults(r)
   500  	testF, r = newTestF()
   501  	t.Run("TestUploadSinglePart", func(t *testing.T) { testF.TestUploadSinglePart(t, r) })
   502  	testF.resetTestDefaults(r)
   503  	testF, r = newTestF()
   504  	t.Run("TestReadPermissions", func(t *testing.T) { testF.TestReadPermissions(t, r) })
   505  	testF.resetTestDefaults(r)
   506  	testF, r = newTestF()
   507  	t.Run("TestReadMetadata", func(t *testing.T) { testF.TestReadMetadata(t, r) })
   508  	testF.resetTestDefaults(r)
   509  	testF, r = newTestF()
   510  	t.Run("TestDirectoryMetadata", func(t *testing.T) { testF.TestDirectoryMetadata(t, r) })
   511  	testF.resetTestDefaults(r)
   512  	testF, r = newTestF()
   513  	t.Run("TestServerSideCopyMove", func(t *testing.T) { testF.TestServerSideCopyMove(t, r) })
   514  	testF.resetTestDefaults(r)
   515  	t.Run("TestMetadataMapper", func(t *testing.T) { testF.TestMetadataMapper(t, r) })
   516  	testF.resetTestDefaults(r)
   517  }
   518  
   519  var _ fstests.InternalTester = (*Fs)(nil)