2022-09-01 12:46:54 +00:00
|
|
|
package handlers
|
|
|
|
|
|
|
|
import (
|
|
|
|
"math"
|
|
|
|
"strconv"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2022-12-15 10:03:54 +00:00
|
|
|
objectv2 "github.com/TrueCloudLab/frostfs-api-go/v2/object"
|
2022-09-01 12:46:54 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestPrepareExpirationHeader(t *testing.T) {
|
|
|
|
tomorrow := time.Now().Add(24 * time.Hour)
|
|
|
|
tomorrowUnix := tomorrow.Unix()
|
|
|
|
tomorrowUnixNano := tomorrow.UnixNano()
|
|
|
|
tomorrowUnixMilli := tomorrowUnixNano / 1e6
|
|
|
|
|
|
|
|
epoch := "100"
|
|
|
|
duration := "24h"
|
|
|
|
timestampSec := strconv.FormatInt(tomorrowUnix, 10)
|
|
|
|
timestampMilli := strconv.FormatInt(tomorrowUnixMilli, 10)
|
|
|
|
timestampNano := strconv.FormatInt(tomorrowUnixNano, 10)
|
|
|
|
|
|
|
|
defaultDurations := &epochDurations{
|
|
|
|
currentEpoch: 10,
|
|
|
|
msPerBlock: 1000,
|
|
|
|
blockPerEpoch: 101,
|
|
|
|
}
|
|
|
|
|
|
|
|
msPerBlock := defaultDurations.blockPerEpoch * uint64(defaultDurations.msPerBlock)
|
|
|
|
epochPerDay := uint64((24 * time.Hour).Milliseconds()) / msPerBlock
|
|
|
|
if uint64((24*time.Hour).Milliseconds())%msPerBlock != 0 {
|
|
|
|
epochPerDay++
|
|
|
|
}
|
|
|
|
|
|
|
|
defaultExpEpoch := strconv.FormatUint(defaultDurations.currentEpoch+epochPerDay, 10)
|
|
|
|
|
|
|
|
for _, tc := range []struct {
|
|
|
|
name string
|
|
|
|
headers map[string]string
|
|
|
|
durations *epochDurations
|
|
|
|
err bool
|
|
|
|
expected map[string]string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "valid epoch",
|
|
|
|
headers: map[string]string{objectv2.SysAttributeExpEpoch: epoch},
|
|
|
|
expected: map[string]string{objectv2.SysAttributeExpEpoch: epoch},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "valid epoch, valid duration",
|
|
|
|
headers: map[string]string{
|
|
|
|
objectv2.SysAttributeExpEpoch: epoch,
|
|
|
|
ExpirationDurationAttr: duration,
|
|
|
|
},
|
|
|
|
durations: defaultDurations,
|
|
|
|
expected: map[string]string{objectv2.SysAttributeExpEpoch: epoch},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "valid epoch, valid rfc3339",
|
|
|
|
headers: map[string]string{
|
|
|
|
objectv2.SysAttributeExpEpoch: epoch,
|
|
|
|
ExpirationRFC3339Attr: tomorrow.Format(time.RFC3339),
|
|
|
|
},
|
|
|
|
durations: defaultDurations,
|
|
|
|
expected: map[string]string{objectv2.SysAttributeExpEpoch: epoch},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "valid epoch, valid timestamp sec",
|
|
|
|
headers: map[string]string{
|
|
|
|
objectv2.SysAttributeExpEpoch: epoch,
|
|
|
|
ExpirationTimestampAttr: timestampSec,
|
|
|
|
},
|
|
|
|
durations: defaultDurations,
|
|
|
|
expected: map[string]string{objectv2.SysAttributeExpEpoch: epoch},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "valid epoch, valid timestamp milli",
|
|
|
|
headers: map[string]string{
|
|
|
|
objectv2.SysAttributeExpEpoch: epoch,
|
|
|
|
ExpirationTimestampAttr: timestampMilli,
|
|
|
|
},
|
|
|
|
durations: defaultDurations,
|
|
|
|
expected: map[string]string{objectv2.SysAttributeExpEpoch: epoch},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "valid epoch, valid timestamp nano",
|
|
|
|
headers: map[string]string{
|
|
|
|
objectv2.SysAttributeExpEpoch: epoch,
|
|
|
|
ExpirationTimestampAttr: timestampNano,
|
|
|
|
},
|
|
|
|
durations: defaultDurations,
|
|
|
|
expected: map[string]string{objectv2.SysAttributeExpEpoch: epoch},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "valid timestamp sec",
|
|
|
|
headers: map[string]string{ExpirationTimestampAttr: timestampSec},
|
|
|
|
durations: defaultDurations,
|
|
|
|
expected: map[string]string{objectv2.SysAttributeExpEpoch: defaultExpEpoch},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "valid duration",
|
|
|
|
headers: map[string]string{ExpirationDurationAttr: duration},
|
|
|
|
durations: defaultDurations,
|
|
|
|
expected: map[string]string{objectv2.SysAttributeExpEpoch: defaultExpEpoch},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "valid rfc3339",
|
|
|
|
headers: map[string]string{ExpirationRFC3339Attr: tomorrow.Format(time.RFC3339)},
|
|
|
|
durations: defaultDurations,
|
|
|
|
expected: map[string]string{objectv2.SysAttributeExpEpoch: defaultExpEpoch},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "valid max uint 64",
|
|
|
|
headers: map[string]string{ExpirationRFC3339Attr: tomorrow.Format(time.RFC3339)},
|
|
|
|
durations: &epochDurations{
|
|
|
|
currentEpoch: math.MaxUint64 - 1,
|
|
|
|
msPerBlock: defaultDurations.msPerBlock,
|
|
|
|
blockPerEpoch: defaultDurations.blockPerEpoch,
|
|
|
|
},
|
|
|
|
expected: map[string]string{objectv2.SysAttributeExpEpoch: strconv.FormatUint(uint64(math.MaxUint64), 10)},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "invalid timestamp sec",
|
|
|
|
headers: map[string]string{ExpirationTimestampAttr: "abc"},
|
|
|
|
err: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "invalid timestamp sec zero",
|
|
|
|
headers: map[string]string{ExpirationTimestampAttr: "0"},
|
|
|
|
err: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "invalid duration",
|
|
|
|
headers: map[string]string{ExpirationDurationAttr: "1d"},
|
|
|
|
err: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "invalid duration negative",
|
|
|
|
headers: map[string]string{ExpirationDurationAttr: "-5h"},
|
|
|
|
err: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "invalid rfc3339",
|
|
|
|
headers: map[string]string{ExpirationRFC3339Attr: "abc"},
|
|
|
|
err: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "invalid rfc3339 zero",
|
|
|
|
headers: map[string]string{ExpirationRFC3339Attr: time.RFC3339},
|
|
|
|
err: true,
|
|
|
|
},
|
|
|
|
} {
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
err := prepareExpirationHeader(tc.headers, tc.durations)
|
|
|
|
if tc.err {
|
|
|
|
require.Error(t, err)
|
|
|
|
} else {
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, tc.expected, tc.headers)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|