rclone/fs/options_test.go
Nick Craig-Wood 75fc3fe389 fs: fix FixRangeOption so it doesn't add HTTPOptions in place of bad Ranges
Before this fix, FixRangeOption would substitute RangeOptions it
wanted to get rid of with with empty HTTPOption. This caused a problem
now that backends interpret HTTPOptions.

This fix subsitutes those with NullOptions which aren't interpreted as
HTTPOptions. This patch also improves the unit tests.
2020-04-24 12:32:09 +01:00

256 lines
6.7 KiB
Go

package fs
import (
"fmt"
"net/http"
"testing"
"github.com/rclone/rclone/fs/hash"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestParseRangeOption(t *testing.T) {
for _, test := range []struct {
in string
want RangeOption
err string
}{
{in: "", err: "doesn't start with bytes="},
{in: "bytes=1-2,3-4", err: "contains multiple ranges"},
{in: "bytes=100", err: "contains no '-'"},
{in: "bytes=x-8", err: "bad start"},
{in: "bytes=8-x", err: "bad end"},
{in: "bytes=1-2", want: RangeOption{Start: 1, End: 2}},
{in: "bytes=-123456789123456789", want: RangeOption{Start: -1, End: 123456789123456789}},
{in: "bytes=123456789123456789-", want: RangeOption{Start: 123456789123456789, End: -1}},
{in: "bytes= 1 - 2 ", want: RangeOption{Start: 1, End: 2}},
{in: "bytes=-", want: RangeOption{Start: -1, End: -1}},
{in: "bytes= - ", want: RangeOption{Start: -1, End: -1}},
} {
got, err := ParseRangeOption(test.in)
what := fmt.Sprintf("parsing %q", test.in)
if test.err != "" {
require.Contains(t, err.Error(), test.err)
require.Nil(t, got, what)
} else {
require.NoError(t, err, what)
assert.Equal(t, test.want, *got, what)
}
}
}
func TestRangeOptionDecode(t *testing.T) {
for _, test := range []struct {
in RangeOption
size int64
wantOffset int64
wantLimit int64
}{
{in: RangeOption{Start: 1, End: 10}, size: 100, wantOffset: 1, wantLimit: 10},
{in: RangeOption{Start: 10, End: 10}, size: 100, wantOffset: 10, wantLimit: 1},
{in: RangeOption{Start: 10, End: 9}, size: 100, wantOffset: 10, wantLimit: 0},
{in: RangeOption{Start: 1, End: -1}, size: 100, wantOffset: 1, wantLimit: -1},
{in: RangeOption{Start: -1, End: 90}, size: 100, wantOffset: 10, wantLimit: -1},
{in: RangeOption{Start: -1, End: -1}, size: 100, wantOffset: 0, wantLimit: -1},
} {
gotOffset, gotLimit := test.in.Decode(test.size)
what := fmt.Sprintf("%+v size=%d", test.in, test.size)
assert.Equal(t, test.wantOffset, gotOffset, "offset "+what)
assert.Equal(t, test.wantLimit, gotLimit, "limit "+what)
}
}
func TestRangeOption(t *testing.T) {
opt := &RangeOption{Start: 1, End: 10}
var _ OpenOption = opt // check interface
assert.Equal(t, "RangeOption(1,10)", opt.String())
key, value := opt.Header()
assert.Equal(t, "Range", key)
assert.Equal(t, "bytes=1-10", value)
assert.Equal(t, true, opt.Mandatory())
opt = &RangeOption{Start: -1, End: 10}
assert.Equal(t, "RangeOption(-1,10)", opt.String())
key, value = opt.Header()
assert.Equal(t, "Range", key)
assert.Equal(t, "bytes=-10", value)
assert.Equal(t, true, opt.Mandatory())
opt = &RangeOption{Start: 1, End: -1}
assert.Equal(t, "RangeOption(1,-1)", opt.String())
key, value = opt.Header()
assert.Equal(t, "Range", key)
assert.Equal(t, "bytes=1-", value)
assert.Equal(t, true, opt.Mandatory())
opt = &RangeOption{Start: -1, End: -1}
assert.Equal(t, "RangeOption(-1,-1)", opt.String())
key, value = opt.Header()
assert.Equal(t, "Range", key)
assert.Equal(t, "bytes=-", value)
assert.Equal(t, true, opt.Mandatory())
}
func TestSeekOption(t *testing.T) {
opt := &SeekOption{Offset: 1}
var _ OpenOption = opt // check interface
assert.Equal(t, "SeekOption(1)", opt.String())
key, value := opt.Header()
assert.Equal(t, "Range", key)
assert.Equal(t, "bytes=1-", value)
assert.Equal(t, true, opt.Mandatory())
}
func TestHTTPOption(t *testing.T) {
opt := &HTTPOption{Key: "k", Value: "v"}
var _ OpenOption = opt // check interface
assert.Equal(t, `HTTPOption("k","v")`, opt.String())
key, value := opt.Header()
assert.Equal(t, "k", key)
assert.Equal(t, "v", value)
assert.Equal(t, false, opt.Mandatory())
}
func TestHashesOption(t *testing.T) {
opt := &HashesOption{hash.Set(hash.MD5 | hash.SHA1)}
var _ OpenOption = opt // check interface
assert.Equal(t, `HashesOption([MD5, SHA-1])`, opt.String())
key, value := opt.Header()
assert.Equal(t, "", key)
assert.Equal(t, "", value)
assert.Equal(t, false, opt.Mandatory())
}
func TestNullOption(t *testing.T) {
opt := NullOption{}
var _ OpenOption = opt // check interface
assert.Equal(t, "NullOption()", opt.String())
key, value := opt.Header()
assert.Equal(t, "", key)
assert.Equal(t, "", value)
assert.Equal(t, false, opt.Mandatory())
}
func TestFixRangeOptions(t *testing.T) {
for _, test := range []struct {
name string
in []OpenOption
size int64
want []OpenOption
}{
{
name: "Nil options",
in: nil,
want: nil,
},
{
name: "Empty options",
in: []OpenOption{},
want: []OpenOption{},
},
{
name: "Fetch a range with size=0",
in: []OpenOption{
&HTTPOption{Key: "a", Value: "1"},
&RangeOption{Start: 1, End: 10},
&HTTPOption{Key: "b", Value: "2"},
},
want: []OpenOption{
&HTTPOption{Key: "a", Value: "1"},
NullOption{},
&HTTPOption{Key: "b", Value: "2"},
},
size: 0,
},
{
name: "Fetch a range",
in: []OpenOption{
&HTTPOption{Key: "a", Value: "1"},
&RangeOption{Start: 1, End: 10},
&HTTPOption{Key: "b", Value: "2"},
},
want: []OpenOption{
&HTTPOption{Key: "a", Value: "1"},
&RangeOption{Start: 1, End: 10},
&HTTPOption{Key: "b", Value: "2"},
},
size: 100,
},
{
name: "Fetch to end",
in: []OpenOption{
&RangeOption{Start: 1, End: -1},
},
want: []OpenOption{
&RangeOption{Start: 1, End: -1},
},
size: 100,
},
{
name: "Fetch the last 10 bytes",
in: []OpenOption{
&RangeOption{Start: -1, End: 10},
},
want: []OpenOption{
&RangeOption{Start: 90, End: -1},
},
size: 100,
},
{
name: "Fetch with end bigger than size",
in: []OpenOption{
&RangeOption{Start: 10, End: 200},
},
want: []OpenOption{
&RangeOption{Start: 10, End: 99},
},
size: 100,
},
} {
FixRangeOption(test.in, test.size)
assert.Equal(t, test.want, test.in, test.name)
}
}
var testOpenOptions = []OpenOption{
&HTTPOption{Key: "a", Value: "1"},
&RangeOption{Start: 1, End: 10},
&HTTPOption{Key: "b", Value: "2"},
NullOption{},
&HashesOption{hash.Set(hash.MD5 | hash.SHA1)},
}
func TestOpenOptionAddHeaders(t *testing.T) {
m := map[string]string{}
want := map[string]string{
"a": "1",
"Range": "bytes=1-10",
"b": "2",
}
OpenOptionAddHeaders(testOpenOptions, m)
assert.Equal(t, want, m)
}
func TestOpenOptionHeaders(t *testing.T) {
want := map[string]string{
"a": "1",
"Range": "bytes=1-10",
"b": "2",
}
m := OpenOptionHeaders(testOpenOptions)
assert.Equal(t, want, m)
assert.Nil(t, OpenOptionHeaders([]OpenOption{}))
}
func TestOpenOptionAddHTTPHeaders(t *testing.T) {
headers := http.Header{}
want := http.Header{
"A": {"1"},
"Range": {"bytes=1-10"},
"B": {"2"},
}
OpenOptionAddHTTPHeaders(headers, testOpenOptions)
assert.Equal(t, want, headers)
}