rclone/lib/bucket/bucket_test.go
Nick Craig-Wood fe19184084 lib/bucket: fix tidying of // in object keys #5858
Before this change, bucket.Join would tidy up object keys by removing
repeated / in them. This means we can't access objects with // in them
which is valid for object keys (but not for file system paths).

This could have consequences for users who are relying on rclone to
fix improper paths for them.
2025-01-22 11:56:05 +00:00

188 lines
4.6 KiB
Go

package bucket
import (
"errors"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestSplit(t *testing.T) {
for _, test := range []struct {
in string
wantBucket string
wantPath string
}{
{in: "", wantBucket: "", wantPath: ""},
{in: "bucket", wantBucket: "bucket", wantPath: ""},
{in: "bucket/path", wantBucket: "bucket", wantPath: "path"},
{in: "bucket/path/subdir", wantBucket: "bucket", wantPath: "path/subdir"},
} {
gotBucket, gotPath := Split(test.in)
assert.Equal(t, test.wantBucket, gotBucket, test.in)
assert.Equal(t, test.wantPath, gotPath, test.in)
}
}
func TestJoin(t *testing.T) {
for _, test := range []struct {
in1, in2 string
want string
}{
{in1: "", in2: "", want: ""},
{in1: "in1", in2: "", want: "in1"},
{in1: "", in2: "in2", want: "in2"},
{in1: "in1", in2: "in2", want: "in1/in2"},
{in1: "in1/", in2: "in2", want: "in1//in2"},
{in1: "in1", in2: "/in2", want: "in1//in2"},
{in1: "in1", in2: "in2/", want: "in1/in2/"},
{in1: "/in1", in2: "/in2", want: "/in1//in2"},
{in1: "/in1", in2: "../in2", want: "/in1/../in2"},
} {
got := Join(test.in1, test.in2)
assert.Equal(t, test.want, got, fmt.Sprintf("in1=%q, in2=%q", test.in1, test.in2))
}
}
func TestIsAllSlashes(t *testing.T) {
for _, test := range []struct {
in string
want bool
}{
{in: "", want: false},
{in: "/", want: true},
{in: "x/", want: false},
{in: "/x", want: false},
{in: "//", want: true},
{in: "/x/", want: false},
{in: "///", want: true},
} {
got := IsAllSlashes(test.in)
assert.Equal(t, test.want, got, test.in)
}
}
func TestCache(t *testing.T) {
c := NewCache()
errBoom := errors.New("boom")
assert.Equal(t, 0, len(c.status))
// IsDeleted before creation
assert.False(t, c.IsDeleted("bucket"))
// MarkOK
c.MarkOK("")
assert.Equal(t, 0, len(c.status))
// MarkOK again
c.MarkOK("bucket")
assert.Equal(t, map[string]bool{"bucket": true}, c.status)
// MarkDeleted
c.MarkDeleted("bucket")
assert.Equal(t, map[string]bool{"bucket": false}, c.status)
// MarkOK again
c.MarkOK("bucket")
assert.Equal(t, map[string]bool{"bucket": true}, c.status)
// IsDeleted after creation
assert.False(t, c.IsDeleted("bucket"))
// Create from root
err := c.Create("", nil, nil)
assert.NoError(t, err)
assert.Equal(t, map[string]bool{"bucket": true}, c.status)
// Create bucket that is already OK
err = c.Create("bucket", nil, nil)
assert.NoError(t, err)
assert.Equal(t, map[string]bool{"bucket": true}, c.status)
// Create new bucket
err = c.Create("bucket2", func() error {
return nil
}, func() (bool, error) {
return true, nil
})
assert.NoError(t, err)
assert.Equal(t, map[string]bool{"bucket": true, "bucket2": true}, c.status)
// Create bucket that has been deleted with error
c.status["bucket2"] = false // mark bucket deleted
err = c.Create("bucket2", nil, func() (bool, error) {
return false, errBoom
})
assert.Equal(t, errBoom, err)
assert.Equal(t, map[string]bool{"bucket": true, "bucket2": false}, c.status)
// Create bucket that has been deleted with no error
err = c.Create("bucket2", nil, func() (bool, error) {
return true, nil
})
assert.NoError(t, err)
assert.Equal(t, map[string]bool{"bucket": true, "bucket2": true}, c.status)
// Create a new bucket with no exists function
err = c.Create("bucket3", func() error {
return nil
}, nil)
assert.NoError(t, err)
assert.Equal(t, map[string]bool{"bucket": true, "bucket2": true, "bucket3": true}, c.status)
// Create a new bucket with no exists function with an error
err = c.Create("bucket4", func() error {
return errBoom
}, nil)
assert.Equal(t, errBoom, err)
assert.Equal(t, map[string]bool{"bucket": true, "bucket2": true, "bucket3": true}, c.status)
// Remove root
err = c.Remove("", func() error {
return nil
})
assert.NoError(t, err)
assert.Equal(t, map[string]bool{"bucket": true, "bucket2": true, "bucket3": true}, c.status)
// Remove existing bucket
err = c.Remove("bucket3", func() error {
return nil
})
assert.NoError(t, err)
assert.Equal(t, map[string]bool{"bucket": true, "bucket2": true, "bucket3": false}, c.status)
// IsDeleted after removal
assert.True(t, c.IsDeleted("bucket3"))
// Remove it again
err = c.Remove("bucket3", func() error {
return errBoom
})
assert.Equal(t, ErrAlreadyDeleted, err)
assert.Equal(t, map[string]bool{"bucket": true, "bucket2": true, "bucket3": false}, c.status)
// Remove bucket with error
err = c.Remove("bucket2", func() error {
return errBoom
})
assert.Equal(t, errBoom, err)
assert.Equal(t, map[string]bool{"bucket": true, "bucket2": true, "bucket3": false}, c.status)
}