mirror of https://github.com/containers/image.git
197 lines
5.2 KiB
Go
197 lines
5.2 KiB
Go
package docker
|
|
|
|
import (
|
|
"errors"
|
|
"math"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestParseDecimalInString(t *testing.T) {
|
|
for _, prefix := range []string{"", "text", "0"} {
|
|
for _, suffix := range []string{"", "text"} {
|
|
for _, c := range []struct {
|
|
s string
|
|
v int64
|
|
}{
|
|
{"0", 0},
|
|
{"1", 1},
|
|
{"0700", 700}, // not octal
|
|
} {
|
|
input := prefix + c.s + suffix
|
|
res, pos, err := parseDecimalInString(input, len(prefix))
|
|
require.NoError(t, err, input)
|
|
assert.Equal(t, c.v, res, input)
|
|
assert.Equal(t, len(prefix)+len(c.s), pos, input)
|
|
}
|
|
for _, c := range []string{
|
|
"-1",
|
|
"xA",
|
|
"&",
|
|
"",
|
|
"999999999999999999999999999999999999999999999999999999999999999999",
|
|
} {
|
|
input := prefix + c + suffix
|
|
_, _, err := parseDecimalInString(input, len(prefix))
|
|
assert.Error(t, err, c)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseExpectedChar(t *testing.T) {
|
|
for _, prefix := range []string{"", "text", "0"} {
|
|
for _, suffix := range []string{"", "text"} {
|
|
input := prefix + "+" + suffix
|
|
pos, err := parseExpectedChar(input, len(prefix), '+')
|
|
require.NoError(t, err, input)
|
|
assert.Equal(t, len(prefix)+1, pos, input)
|
|
|
|
_, err = parseExpectedChar(input, len(prefix), '-')
|
|
assert.Error(t, err, input)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseContentRange(t *testing.T) {
|
|
for _, c := range []struct {
|
|
in string
|
|
first, last, completeLength int64
|
|
}{
|
|
{"bytes 0-0/1", 0, 0, 1},
|
|
{"bytes 010-020/030", 10, 20, 30},
|
|
{"bytes 1000-1010/*", 1000, 1010, -1},
|
|
} {
|
|
first, last, completeLength, err := parseContentRange(&http.Response{
|
|
Header: http.Header{
|
|
http.CanonicalHeaderKey("Content-Range"): []string{c.in},
|
|
},
|
|
})
|
|
require.NoError(t, err, c.in)
|
|
assert.Equal(t, c.first, first, c.in)
|
|
assert.Equal(t, c.last, last, c.in)
|
|
assert.Equal(t, c.completeLength, completeLength, c.in)
|
|
}
|
|
|
|
for _, hdr := range []http.Header{
|
|
nil,
|
|
{http.CanonicalHeaderKey("Content-Range"): []string{}},
|
|
{http.CanonicalHeaderKey("Content-Range"): []string{"bytes 1-2/3", "bytes 1-2/3"}},
|
|
} {
|
|
_, _, _, err := parseContentRange(&http.Response{
|
|
Header: hdr,
|
|
})
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
for _, c := range []string{
|
|
"",
|
|
"notbytes 1-2/3",
|
|
"bytes ",
|
|
"bytes x-2/3",
|
|
"bytes 1*2/3",
|
|
"bytes 1",
|
|
"bytes 1-",
|
|
"bytes 1-x/3",
|
|
"bytes 1-2",
|
|
"bytes 1-2@3",
|
|
"bytes 1-2/",
|
|
"bytes 1-2/*a",
|
|
"bytes 1-2/3a",
|
|
} {
|
|
_, _, _, err := parseContentRange(&http.Response{
|
|
Header: http.Header{
|
|
http.CanonicalHeaderKey("Content-Range"): []string{c},
|
|
},
|
|
})
|
|
assert.Error(t, err, c, c)
|
|
}
|
|
}
|
|
|
|
func TestMillisecondsSinceOptional(t *testing.T) {
|
|
current := time.Date(2023, 2, 9, 8, 7, 6, 5, time.UTC)
|
|
res := millisecondsSinceOptional(current, time.Time{})
|
|
assert.True(t, math.IsNaN(res))
|
|
tm := current.Add(-60 * time.Second) // 60 seconds _before_ current
|
|
res = millisecondsSinceOptional(current, tm)
|
|
assert.Equal(t, res, 60_000.0)
|
|
}
|
|
|
|
func TestBodyReaderErrorIfNotReconnecting(t *testing.T) {
|
|
// Silence logrus.Info logs in the tested method
|
|
prevLevel := logrus.StandardLogger().Level
|
|
logrus.StandardLogger().SetLevel(logrus.WarnLevel)
|
|
t.Cleanup(func() {
|
|
logrus.StandardLogger().SetLevel(prevLevel)
|
|
})
|
|
|
|
for _, c := range []struct {
|
|
name string
|
|
previousRetry bool
|
|
currentOffset int64
|
|
currentTime int // milliseconds
|
|
expectReconnect bool
|
|
}{
|
|
{
|
|
name: "A lot of progress, after a long time, second retry",
|
|
previousRetry: true,
|
|
currentOffset: 2 * bodyReaderMinimumProgress,
|
|
currentTime: 2 * bodyReaderMSSinceLastRetry,
|
|
expectReconnect: true,
|
|
},
|
|
{
|
|
name: "A lot of progress, after little time, second retry",
|
|
previousRetry: true,
|
|
currentOffset: 2 * bodyReaderMinimumProgress,
|
|
currentTime: 1,
|
|
expectReconnect: true,
|
|
},
|
|
{
|
|
name: "Little progress, after a long time, second retry",
|
|
previousRetry: true,
|
|
currentOffset: 1,
|
|
currentTime: 2 * bodyReaderMSSinceLastRetry,
|
|
expectReconnect: true,
|
|
},
|
|
{
|
|
name: "Little progress, after little time, second retry",
|
|
previousRetry: true,
|
|
currentOffset: 1,
|
|
currentTime: 1,
|
|
expectReconnect: false,
|
|
},
|
|
{
|
|
name: "Little progress, after little time, first retry",
|
|
previousRetry: false,
|
|
currentOffset: 1,
|
|
currentTime: bodyReaderMSSinceLastRetry / 2,
|
|
expectReconnect: true,
|
|
},
|
|
} {
|
|
tm := time.Now()
|
|
br := bodyReader{}
|
|
if c.previousRetry {
|
|
br.lastRetryOffset = 2 * bodyReaderMinimumProgress
|
|
br.offset = br.lastRetryOffset + c.currentOffset
|
|
br.firstConnectionTime = tm.Add(-time.Duration(c.currentTime+2*bodyReaderMSSinceLastRetry) * time.Millisecond)
|
|
br.lastRetryTime = tm.Add(-time.Duration(c.currentTime) * time.Millisecond)
|
|
} else {
|
|
br.lastRetryOffset = -1
|
|
br.lastRetryTime = time.Time{}
|
|
br.offset = c.currentOffset
|
|
br.firstConnectionTime = tm.Add(-time.Duration(c.currentTime) * time.Millisecond)
|
|
}
|
|
err := br.errorIfNotReconnecting(errors.New("some error for error text only"), "URL for error text only")
|
|
if c.expectReconnect {
|
|
assert.NoError(t, err, c.name, br)
|
|
} else {
|
|
assert.Error(t, err, c.name, br)
|
|
}
|
|
}
|
|
}
|