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) } }) } }