authelia/internal/utils/time_test.go

264 lines
8.9 KiB
Go

package utils
import (
"fmt"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestParseDurationString(t *testing.T) {
testCases := []struct {
name string
have []string
raw bool
expected time.Duration
err string
}{
{"ShouldParseStringsForMillisecond", []string{"%d ms", "%d millisecond", "%d milliseconds"}, false, time.Millisecond, ""},
{"ShouldParseStringsForSecond", []string{"%d s", "%d second", "%d seconds"}, false, time.Second, ""},
{"ShouldParseStringsForMinute", []string{"%d m", "%d minute", "%d minutes"}, false, time.Minute, ""},
{"ShouldParseStringsForHour", []string{"%d h", "%d hour", "%d hours"}, false, time.Hour, ""},
{"ShouldParseStringsForDay", []string{"%d d", "%d day", "%d days"}, false, time.Hour * HoursInDay, ""},
{"ShouldParseStringsForWeek", []string{"%d w", "%d week", "%d weeks"}, false, time.Hour * HoursInWeek, ""},
{"ShouldParseStringsForMonth", []string{"%d M", "%d month", "%d months"}, false, time.Hour * HoursInMonth, ""},
{"ShouldParseStringsForYear", []string{"%d y", "%d year", "%d years"}, false, time.Hour * HoursInYear, ""},
{"ShouldParseStringsDecimals", []string{"100"}, true, time.Second * 100, ""},
{"ShouldParseStringsDecimalNull", []string{""}, true, time.Second * 0, ""},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
for _, f := range tc.have {
if tc.raw {
t.Run(f, func(t *testing.T) {
actual, actualErr := ParseDurationString(f)
if tc.err == "" {
assert.NoError(t, actualErr)
assert.Equal(t, tc.expected, actual)
} else {
assert.EqualError(t, actualErr, tc.err)
}
})
} else {
for _, d := range []int{1, 5, 20} {
input := fmt.Sprintf(f, d)
inputNoSpace := strings.ReplaceAll(input, " ", "")
t.Run(inputNoSpace, func(t *testing.T) {
t.Run("WithSpaces", func(t *testing.T) {
actual, actualErr := ParseDurationString(input)
if tc.err == "" {
assert.NoError(t, actualErr)
assert.Equal(t, tc.expected*time.Duration(d), actual)
} else {
assert.EqualError(t, actualErr, tc.err)
}
t.Run("LeadingZeros", func(t *testing.T) {
inputActual := reNumeric.ReplaceAllStringFunc(input, func(s string) string {
return "000" + s
})
actual, actualErr := ParseDurationString(inputActual)
if tc.err == "" {
assert.NoError(t, actualErr)
assert.Equal(t, tc.expected*time.Duration(d), actual)
} else {
assert.EqualError(t, actualErr, tc.err)
}
})
})
t.Run("WithoutSpaces", func(t *testing.T) {
actual, actualErr := ParseDurationString(inputNoSpace)
if tc.err == "" {
assert.NoError(t, actualErr)
assert.Equal(t, tc.expected*time.Duration(d), actual)
} else {
assert.EqualError(t, actualErr, tc.err)
}
t.Run("LeadingZeros", func(t *testing.T) {
inputActual := reNumeric.ReplaceAllStringFunc(inputNoSpace, func(s string) string {
return "000" + s
})
actual, actualErr := ParseDurationString(inputActual)
if tc.err == "" {
assert.NoError(t, actualErr)
assert.Equal(t, tc.expected*time.Duration(d), actual)
} else {
assert.EqualError(t, actualErr, tc.err)
}
})
})
})
}
}
}
})
}
}
func TestStandardizeDurationString(t *testing.T) {
var (
actual string
err error
)
actual, err = StandardizeDurationString("1 hour and 20 minutes")
assert.NoError(t, err)
assert.Equal(t, "1h20m", actual)
actual, err = StandardizeDurationString("1 hour and 20 minutes")
assert.NoError(t, err)
assert.Equal(t, "1h20m", actual)
}
func TestParseDurationString_ShouldNotParseDurationStringWithOutOfOrderQuantitiesAndUnits(t *testing.T) {
duration, err := ParseDurationString("h1")
assert.EqualError(t, err, "could not parse 'h1' as a duration")
assert.Equal(t, time.Duration(0), duration)
}
func TestParseDurationString_ShouldNotParseBadDurationString(t *testing.T) {
duration, err := ParseDurationString("10x")
assert.EqualError(t, err, "could not parse the units portion of '10x' in duration string '10x': the unit 'x' is not valid")
assert.Equal(t, time.Duration(0), duration)
}
func TestParseDurationString_ShouldNotParseBadDurationStringAlt(t *testing.T) {
duration, err := ParseDurationString("10abcxyz")
assert.EqualError(t, err, "could not parse the units portion of '10abcxyz' in duration string '10abcxyz': the unit 'abcxyz' is not valid")
assert.Equal(t, time.Duration(0), duration)
}
func TestParseDurationString_ShouldParseMultiUnitValues(t *testing.T) {
duration, err := ParseDurationString("1d3w10ms")
assert.NoError(t, err)
assert.Equal(t,
(time.Hour*time.Duration(24))+
(time.Hour*time.Duration(24)*time.Duration(7)*time.Duration(3))+
(time.Millisecond*time.Duration(10)), duration)
}
func TestParseDurationString_ShouldParseDuplicateUnitValues(t *testing.T) {
duration, err := ParseDurationString("1d4d2d")
assert.NoError(t, err)
assert.Equal(t,
(time.Hour*time.Duration(24))+
(time.Hour*time.Duration(24)*time.Duration(4))+
(time.Hour*time.Duration(24)*time.Duration(2)), duration)
}
func TestStandardizeDurationString_ShouldParseStringWithSpaces(t *testing.T) {
result, err := StandardizeDurationString("1d 1h 20m")
assert.NoError(t, err)
assert.Equal(t, result, "24h1h20m")
}
func TestShouldTimeIntervalsMakeSense(t *testing.T) {
assert.Equal(t, Hour, time.Minute*60)
assert.Equal(t, Day, Hour*24)
assert.Equal(t, Week, Day*7)
assert.Equal(t, Year, Day*365)
assert.Equal(t, Month, Year/12)
}
func TestShouldConvertKnownUnixNanoTimeToKnownWin32Epoch(t *testing.T) {
exampleNanoTime := int64(1626234411 * 1000000000)
win32Epoch := uint64(132707080110000000)
assert.Equal(t, win32Epoch, UnixNanoTimeToMicrosoftNTEpoch(exampleNanoTime))
assert.Equal(t, timeUnixEpochAsMicrosoftNTEpoch, UnixNanoTimeToMicrosoftNTEpoch(0))
}
func TestParseTimeString(t *testing.T) {
testCases := []struct {
name string
have string
index int
expected time.Time
err string
}{
{"ShouldParseIntegerAsUnix", "1675899060", -1, time.Unix(1675899060, 0), ""},
{"ShouldParseIntegerAsUnixMilli", "1675899060000", -2, time.Unix(1675899060, 0), ""},
{"ShouldParseIntegerAsUnixMicro", "1675899060000000", -3, time.Unix(1675899060, 0), ""},
{"ShouldNotParseSuperLargeInteger", "9999999999999999999999999999999999999999", -999, time.Unix(0, 0), "time value was detected as an integer but the integer could not be parsed: strconv.ParseInt: parsing \"9999999999999999999999999999999999999999\": value out of range"},
{"ShouldParseSimpleTime", "Jan 2 15:04:05 2006", 0, time.Unix(1136214245, 0), ""},
{"ShouldNotParseInvalidTime", "abc", -998, time.Unix(0, 0), "failed to find a suitable time layout for time 'abc'"},
{"ShouldMatchDate", "2020-05-01", 6, time.Unix(1588291200, 0), ""},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
index, actualA, errA := matchParseTimeStringWithLayouts(tc.have, StandardTimeLayouts)
actualB, errB := ParseTimeStringWithLayouts(tc.have, StandardTimeLayouts)
actualC, errC := ParseTimeString(tc.have)
if tc.err == "" {
assert.NoError(t, errA)
assert.NoError(t, errB)
assert.NoError(t, errC)
assert.Equal(t, tc.index, index)
assert.Equal(t, tc.expected.UnixNano(), actualA.UnixNano())
assert.Equal(t, tc.expected.UnixNano(), actualB.UnixNano())
assert.Equal(t, tc.expected.UnixNano(), actualC.UnixNano())
} else {
assert.EqualError(t, errA, tc.err)
assert.EqualError(t, errB, tc.err)
assert.EqualError(t, errC, tc.err)
}
})
}
}
func TestParseTimeStringWithLayouts(t *testing.T) {
testCases := []struct {
name string
have string
index int
expected time.Time
err string
}{
{"ShouldParseIntegerAsUnix", "1675899060", -1, time.Unix(1675899060, 0), ""},
{"ShouldParseIntegerAsUnixMilli", "1675899060000", -2, time.Unix(1675899060, 0), ""},
{"ShouldParseIntegerAsUnixMicro", "1675899060000000", -3, time.Unix(1675899060, 0), ""},
{"ShouldNotParseSuperLargeInteger", "9999999999999999999999999999999999999999", -999, time.Unix(0, 0), "time value was detected as an integer but the integer could not be parsed: strconv.ParseInt: parsing \"9999999999999999999999999999999999999999\": value out of range"},
{"ShouldParseSimpleTime", "Jan 2 15:04:05 2006", 0, time.Unix(1136214245, 0), ""},
{"ShouldNotParseInvalidTime", "abc", -998, time.Unix(0, 0), "failed to find a suitable time layout for time 'abc'"},
{"ShouldMatchDate", "2020-05-01", 6, time.Unix(1588291200, 0), ""},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
actual, err := ParseTimeStringWithLayouts(tc.have, StandardTimeLayouts)
if tc.err == "" {
assert.NoError(t, err)
assert.Equal(t, tc.expected.UnixNano(), actual.UnixNano())
} else {
assert.EqualError(t, err, tc.err)
}
})
}
}