11package templates
22
33import (
4+ "bytes"
45 "errors"
6+ "strconv"
7+ "strings"
58 "testing"
9+ "text/template"
610 "time"
711
812 "github.com/stretchr/testify/assert"
@@ -26,10 +30,10 @@ func Test_GetFuncMap_fail(t *testing.T) {
2630 }
2731}
2832
29- func TestGetFuncMap_toTime (t * testing.T ) {
30- now := time .Now ()
33+ func TestGetFuncMap_toTime_formatTime (t * testing.T ) {
34+ now := time .Now (). Truncate ( time . Second )
3135 numericDate := jose .NewNumericDate (now )
32- expected := now .UTC (). Format ( time . RFC3339 )
36+ expected := now .UTC ()
3337 loc , err := time .LoadLocation ("America/Los_Angeles" )
3438 require .NoError (t , err )
3539
@@ -39,7 +43,7 @@ func TestGetFuncMap_toTime(t *testing.T) {
3943 tests := []struct {
4044 name string
4145 args args
42- want string
46+ want time. Time
4347 }{
4448 {"time" , args {now }, expected },
4549 {"time pointer" , args {& now }, expected },
@@ -57,19 +61,188 @@ func TestGetFuncMap_toTime(t *testing.T) {
5761 t .Run (tt .name , func (t * testing.T ) {
5862 var failMesage string
5963 fns := GetFuncMap (& failMesage )
60- fn := fns ["toTime" ].(func (any ) string )
61- assert .Equal (t , tt .want , fn (tt .args .v ))
64+ toTimeFunc := fns ["toTime" ].(func (any ) time.Time )
65+ assert .Equal (t , tt .want , toTimeFunc (tt .args .v ))
66+ formatTimeFunc := fns ["formatTime" ].(func (any ) string )
67+ assert .Equal (t , tt .want .Format (time .RFC3339 ), formatTimeFunc (tt .args .v ))
6268 })
6369 }
6470
6571 t .Run ("default" , func (t * testing.T ) {
6672 var failMesage string
6773 fns := GetFuncMap (& failMesage )
68- fn := fns ["toTime" ].(func (any ) string )
69- want := time .Now ()
70- got , err := time .Parse (time .RFC3339 , fn (nil ))
74+ toTimeFunc := fns ["toTime" ].(func (any ) time.Time )
75+ got := toTimeFunc (nil )
76+ assert .WithinDuration (t , time .Now (), got , time .Second )
77+
78+ formatTimeFunc := fns ["formatTime" ].(func (any ) string )
79+ got , err := time .Parse (time .RFC3339 , formatTimeFunc (nil ))
7180 require .NoError (t , err )
72- assert .WithinDuration (t , want , got , time .Second )
81+ assert .WithinDuration (t , time . Now () , got , time .Second )
7382 assert .Equal (t , time .UTC , got .Location ())
7483 })
7584}
85+
86+ func TestGetFuncMap_parseTime_mustParseTime (t * testing.T ) {
87+ now := time .Now ().Truncate (time .Second )
88+ loc := time .Local
89+ if zone , _ := now .Zone (); zone == "UTC" {
90+ loc = time .UTC
91+ }
92+
93+ losAngeles , err := time .LoadLocation ("America/Los_Angeles" )
94+ require .NoError (t , err )
95+
96+ type args struct {
97+ v []string
98+ }
99+ tests := []struct {
100+ name string
101+ args args
102+ want time.Time
103+ assertion assert.ErrorAssertionFunc
104+ }{
105+ {"now" , args {[]string {now .Format (time .RFC3339 )}}, now .In (loc ), assert .NoError },
106+ {"with real layout" , args {[]string {time .UnixDate , now .UTC ().Format (time .UnixDate )}}, now .UTC (), assert .NoError },
107+ {"with name layout" , args {[]string {"time.UnixDate" , now .Format (time .UnixDate )}}, now .In (loc ), assert .NoError },
108+ {"with locale UTC" , args {[]string {"time.UnixDate" , now .UTC ().Format (time .UnixDate ), "UTC" }}, now .UTC (), assert .NoError },
109+ {"with locale other" , args {[]string {"time.UnixDate" , now .In (losAngeles ).Format (time .UnixDate ), "America/Los_Angeles" }}, now .In (losAngeles ), assert .NoError },
110+ {"fail parse" , args {[]string {now .Format (time .UnixDate )}}, time.Time {}, assert .Error },
111+ {"fail parse with layout" , args {[]string {"time.UnixDate" , now .Format (time .RFC3339 )}}, time.Time {}, assert .Error },
112+ {"fail parse with locale" , args {[]string {"time.UnixDate" , now .Format (time .RFC3339 ), "america/Los_Angeles" }}, time.Time {}, assert .Error },
113+ {"fail load locale" , args {[]string {"time.UnixDate" , now .In (losAngeles ).Format (time .UnixDate ), "America/The_Angels" }}, time.Time {}, assert .Error },
114+ {"fail arguments" , args {[]string {"time.Layout" , now .Format (time .Layout ), "America/The_Angels" , "extra" }}, time.Time {}, assert .Error },
115+ }
116+ for _ , tt := range tests {
117+ t .Run (tt .name , func (t * testing.T ) {
118+ var failMesage string
119+ fns := GetFuncMap (& failMesage )
120+ parseTimeFunc := fns ["parseTime" ].(func (... string ) time.Time )
121+ assert .Equal (t , tt .want , parseTimeFunc (tt .args .v ... ))
122+
123+ mustParseTimeFunc := fns ["mustParseTime" ].(func (... string ) (time.Time , error ))
124+ got , err := mustParseTimeFunc (tt .args .v ... )
125+ tt .assertion (t , err )
126+ assert .Equal (t , tt .want , got )
127+ })
128+ }
129+
130+ t .Run ("default" , func (t * testing.T ) {
131+ var failMesage string
132+ fns := GetFuncMap (& failMesage )
133+ parseTimeFunc := fns ["parseTime" ].(func (... string ) time.Time )
134+ got := parseTimeFunc ()
135+ assert .WithinDuration (t , time .Now (), got , time .Second )
136+
137+ mustParseTimeFunc := fns ["mustParseTime" ].(func (... string ) (time.Time , error ))
138+ got , err := mustParseTimeFunc ()
139+ require .NoError (t , err )
140+ assert .WithinDuration (t , time .Now (), got , time .Second )
141+ assert .Equal (t , time .UTC , got .Location ())
142+ })
143+ }
144+
145+ func TestGetFuncMap_toTimeLayout (t * testing.T ) {
146+ type args struct {
147+ fmt string
148+ }
149+ tests := []struct {
150+ name string
151+ args args
152+ want string
153+ }{
154+ {"format" , args {time .RFC3339 }, time .RFC3339 },
155+ {"time.Layout" , args {"time.Layout" }, time .Layout },
156+ {"time.ANSIC" , args {"time.ANSIC" }, time .ANSIC },
157+ {"time.UnixDate" , args {"time.UnixDate" }, time .UnixDate },
158+ {"time.RubyDate" , args {"time.RubyDate" }, time .RubyDate },
159+ {"time.RFC822" , args {"time.RFC822" }, time .RFC822 },
160+ {"time.RFC822Z" , args {"time.RFC822Z" }, time .RFC822Z },
161+ {"time.RFC850" , args {"time.RFC850" }, time .RFC850 },
162+ {"time.RFC1123" , args {"time.RFC1123" }, time .RFC1123 },
163+ {"time.RFC1123Z" , args {"time.RFC1123Z" }, time .RFC1123Z },
164+ {"time.RFC3339" , args {"time.RFC3339" }, time .RFC3339 },
165+ {"time.RFC3339Nano" , args {"time.RFC3339Nano" }, time .RFC3339Nano },
166+ {"time.Kitchen" , args {"time.Kitchen" }, time .Kitchen },
167+ {"time.Stamp" , args {"time.Stamp" }, time .Stamp },
168+ {"time.StampMilli" , args {"time.StampMilli" }, time .StampMilli },
169+ {"time.StampMicro" , args {"time.StampMicro" }, time .StampMicro },
170+ {"time.StampNano" , args {"time.StampNano" }, time .StampNano },
171+ {"time.DateTime" , args {"time.DateTime" }, time .DateTime },
172+ {"time.DateOnly" , args {"time.DateOnly" }, time .DateOnly },
173+ {"time.TimeOnly" , args {"time.TimeOnly" }, time .TimeOnly },
174+ {"uppercase" , args {"UNIXDATE" }, time .UnixDate },
175+ {"lowercase" , args {"rfc3339" }, time .RFC3339 },
176+ {"default" , args {"MyFormat" }, "MyFormat" },
177+ }
178+ for _ , tt := range tests {
179+ t .Run (tt .name , func (t * testing.T ) {
180+ var failMesage string
181+ fns := GetFuncMap (& failMesage )
182+ toTimeLayoutFunc := fns ["toTimeLayout" ].(func (string ) string )
183+ assert .Equal (t , tt .want , toTimeLayoutFunc (tt .args .fmt ))
184+ fmt := strings .TrimPrefix (tt .args .fmt , "time." )
185+ assert .Equal (t , tt .want , toTimeLayoutFunc (fmt ))
186+ })
187+ }
188+ }
189+
190+ func TestTemplates (t * testing.T ) {
191+ now := time .Now ().UTC ().Truncate (time .Second )
192+ mustParse := func (t * testing.T , text string , msg * string , assertion assert.ErrorAssertionFunc ) string {
193+ t .Helper ()
194+
195+ tmpl , err := template .New (t .Name ()).Funcs (GetFuncMap (msg )).Parse (text )
196+ require .NoError (t , err )
197+ buf := new (bytes.Buffer )
198+ err = tmpl .Execute (buf , map [string ]any {
199+ "nbf" : now .Unix (),
200+ "float64" : float64 (now .Unix ()),
201+ "notBefore" : now .Format (time .RFC3339 ),
202+ "notAfter" : now .Add (time .Hour ).Format (time .UnixDate ),
203+ })
204+ assertion (t , err )
205+ return buf .String ()
206+ }
207+
208+ type args struct {
209+ text string
210+ }
211+ tests := []struct {
212+ name string
213+ args args
214+ want string
215+ errorAssertion assert.ErrorAssertionFunc
216+ failAssertion assert.ValueAssertionFunc
217+ }{
218+ {"toTime int64" , args {`{{ .nbf | toTime }}` }, now .String (), assert .NoError , assert .Empty },
219+ {"toTime int64 toJson" , args {`{{ .nbf | toTime | toJson }}` }, strconv .Quote (now .Format (time .RFC3339 )), assert .NoError , assert .Empty },
220+ {"toTime float64 toJson" , args {`{{ .float64 | toTime | toJson }}` }, strconv .Quote (now .Format (time .RFC3339 )), assert .NoError , assert .Empty },
221+ {"toTime dateModify" , args {`{{ .nbf | toTime | dateModify "1h" }}` }, now .Add (time .Hour ).String (), assert .NoError , assert .Empty },
222+ {"formatTime" , args {`{{ .nbf | formatTime }}` }, now .Format (time .RFC3339 ), assert .NoError , assert .Empty },
223+ {"formatTime float64" , args {`{{ .float64 | formatTime }}` }, now .Format (time .RFC3339 ), assert .NoError , assert .Empty },
224+ {"formatTime in sprig" , args {`{{ dateInZone "2006-01-02T15:04:05Z07:00" .float64 "UTC" }}` }, now .UTC ().Format (time .RFC3339 ), assert .NoError , assert .Empty },
225+ {"parseTime" , args {`{{ .notBefore | parseTime }}` }, now .String (), assert .NoError , assert .Empty },
226+ {"parseTime toJson" , args {`{{ .notBefore | parseTime | toJson }}` }, strconv .Quote (now .Format (time .RFC3339 )), assert .NoError , assert .Empty },
227+ {"parseTime time.UnixDate" , args {`{{ .notAfter | parseTime "time.UnixDate" }}` }, now .Add (time .Hour ).String (), assert .NoError , assert .Empty },
228+ {"parseTime time.UnixDate toJson" , args {`{{ .notAfter | parseTime "time.UnixDate" | toJson }}` }, strconv .Quote (now .Add (time .Hour ).Format (time .RFC3339 )), assert .NoError , assert .Empty },
229+ {"parseTime time.UnixDate America/Los_Angeles" , args {`{{ parseTime "time.UnixDate" .notAfter "America/Los_Angeles" }}` }, now .Add (time .Hour ).String (), assert .NoError , assert .Empty },
230+ {"parseTime dateModify" , args {`{{ .notBefore | parseTime | dateModify "1h" }}` }, now .Add (time .Hour ).String (), assert .NoError , assert .Empty },
231+ {"parseTime in sprig " , args {`{{ toDate "Mon Jan _2 15:04:05 MST 2006" .notAfter }}` }, now .Add (time .Hour ).String (), assert .NoError , assert .Empty },
232+ {"toTimeLayout" , args {`{{ toTimeLayout "time.RFC3339" }}` }, time .RFC3339 , assert .NoError , assert .Empty },
233+ {"toTimeLayout short" , args {`{{ toTimeLayout "RFC3339" }}` }, time .RFC3339 , assert .NoError , assert .Empty },
234+ {"toTime toTimeLayout date" , args {`{{ .nbf | toTime | date (toTimeLayout "time.RFC3339") }}` }, now .Local ().Format (time .RFC3339 ), assert .NoError , assert .Empty },
235+ {"toTime toTimeLayout date" , args {`{{ .nbf | toTime | date (toTimeLayout "time.RFC3339") }}` }, now .Local ().Format (time .RFC3339 ), assert .NoError , assert .Empty },
236+ {"parseTime error" , args {`{{ parseTime "time.UnixDate" .notAfter "America/FooBar" }}` }, "0001-01-01 00:00:00 +0000 UTC" , assert .NoError , assert .Empty },
237+ {"mustParseTime error" , args {`{{ mustParseTime "time.UnixDate" .notAfter "America/FooBar" }}` }, "" , assert .Error , assert .Empty },
238+ {"fail" , args {`{{ fail "error" }}` }, "" , assert .Error , assert .NotEmpty },
239+ }
240+ for _ , tt := range tests {
241+ t .Run (tt .name , func (t * testing.T ) {
242+ var failMesage string
243+ got := mustParse (t , tt .args .text , & failMesage , tt .errorAssertion )
244+ tt .failAssertion (t , failMesage )
245+ assert .Equal (t , tt .want , got )
246+ })
247+ }
248+ }
0 commit comments