1package duration
2
3import (
4 "testing"
5 "time"
6)
7
8func TestParse(t *testing.T) {
9 tests := []struct {
10 name string
11 input string
12 want time.Duration
13 wantErr bool
14 }{
15 // Single human-friendly units
16 {name: "1 day", input: "1d", want: Day},
17 {name: "2 days", input: "2d", want: 2 * Day},
18 {name: "1 week", input: "1w", want: Week},
19 {name: "2 weeks", input: "2w", want: 2 * Week},
20 {name: "1 month", input: "1M", want: Month},
21 {name: "3 months", input: "3M", want: 3 * Month},
22 {name: "1 year", input: "1y", want: Year},
23 {name: "2 years", input: "2y", want: 2 * Year},
24
25 // Compound human-friendly units
26 {name: "1 year 6 months", input: "1y6M", want: Year + 6*Month},
27 {name: "2 weeks 3 days", input: "2w3d", want: 2*Week + 3*Day},
28 {name: "1 year 2 months 1 week", input: "1y2M1w", want: Year + 2*Month + Week},
29
30 // Mixed with standard Go units
31 {name: "1 day 12 hours", input: "1d12h", want: Day + 12*time.Hour},
32 {name: "2 weeks 6 hours", input: "2w6h", want: 2*Week + 6*time.Hour},
33 {name: "1 month 1 day 1 hour", input: "1M1d1h", want: Month + Day + time.Hour},
34 {name: "1 year 30 minutes", input: "1y30m", want: Year + 30*time.Minute},
35
36 // Standard Go duration units (fallback)
37 {name: "24 hours", input: "24h", want: 24 * time.Hour},
38 {name: "30 minutes", input: "30m", want: 30 * time.Minute},
39 {name: "1 hour 30 minutes", input: "1h30m", want: time.Hour + 30*time.Minute},
40 {name: "1 second", input: "1s", want: time.Second},
41 {name: "500 milliseconds", input: "500ms", want: 500 * time.Millisecond},
42 {name: "1000 microseconds", input: "1000us", want: 1000 * time.Microsecond},
43 {name: "1000000 nanoseconds", input: "1000000ns", want: 1000000 * time.Nanosecond},
44
45 // Special cases
46 {name: "zero duration", input: "0", want: 0},
47 {name: "zero with unit", input: "0d", want: 0},
48 {name: "zero hours", input: "0h", want: 0},
49
50 // Large values
51 {name: "10 years", input: "10y", want: 10 * Year},
52 {name: "52 weeks", input: "52w", want: 52 * Week},
53 {name: "365 days", input: "365d", want: 365 * Day},
54
55 // Edge cases with whitespace (should be trimmed)
56 {name: "whitespace around", input: " 1d ", want: Day},
57
58 // Error cases
59 {name: "empty string", input: "", wantErr: true},
60 {name: "invalid format", input: "abc", wantErr: true},
61 {name: "invalid unit", input: "1x", wantErr: true},
62 {name: "missing value", input: "d", wantErr: true},
63 {name: "negative not supported by Go", input: "-1d", wantErr: true},
64 }
65
66 for _, tt := range tests {
67 t.Run(tt.name, func(t *testing.T) {
68 got, err := Parse(tt.input)
69 if (err != nil) != tt.wantErr {
70 t.Errorf("Parse(%q) error = %v, wantErr %v", tt.input, err, tt.wantErr)
71 return
72 }
73 if !tt.wantErr && got != tt.want {
74 t.Errorf("Parse(%q) = %v, want %v", tt.input, got, tt.want)
75 }
76 })
77 }
78}
79
80func TestParseConstants(t *testing.T) {
81 // Verify our constants are correct
82 if Day != 24*time.Hour {
83 t.Errorf("Day = %v, want %v", Day, 24*time.Hour)
84 }
85 if Week != 7*24*time.Hour {
86 t.Errorf("Week = %v, want %v", Week, 7*24*time.Hour)
87 }
88 if Month != 30*24*time.Hour {
89 t.Errorf("Month = %v, want %v", Month, 30*24*time.Hour)
90 }
91 if Year != 365*24*time.Hour {
92 t.Errorf("Year = %v, want %v", Year, 365*24*time.Hour)
93 }
94}
95
96func TestParseEquivalences(t *testing.T) {
97 // Test that our human-friendly units produce expected equivalences
98 tests := []struct {
99 name string
100 human string
101 goStd string
102 hours int
103 }{
104 {name: "1 day = 24 hours", human: "1d", goStd: "24h", hours: 24},
105 {name: "1 week = 168 hours", human: "1w", goStd: "168h", hours: 168},
106 {name: "1 month = 720 hours", human: "1M", goStd: "720h", hours: 720},
107 {name: "1 year = 8760 hours", human: "1y", goStd: "8760h", hours: 8760},
108 }
109
110 for _, tt := range tests {
111 t.Run(tt.name, func(t *testing.T) {
112 humanDur, err := Parse(tt.human)
113 if err != nil {
114 t.Fatalf("Parse(%q) error = %v", tt.human, err)
115 }
116
117 goDur, err := time.ParseDuration(tt.goStd)
118 if err != nil {
119 t.Fatalf("time.ParseDuration(%q) error = %v", tt.goStd, err)
120 }
121
122 if humanDur != goDur {
123 t.Errorf("Parse(%q) = %v, want %v (from %q)", tt.human, humanDur, goDur, tt.goStd)
124 }
125
126 expectedHours := time.Duration(tt.hours) * time.Hour
127 if humanDur != expectedHours {
128 t.Errorf("Parse(%q) = %v, want %d hours", tt.human, humanDur, tt.hours)
129 }
130 })
131 }
132}