1
2
3
4
5 package render
6
7 import (
8 "encoding/xml"
9 "errors"
10 "html/template"
11 "net"
12 "net/http"
13 "net/http/httptest"
14 "strconv"
15 "strings"
16 "testing"
17
18 "github.com/gin-gonic/gin/internal/json"
19 testdata "github.com/gin-gonic/gin/testdata/protoexample"
20 "github.com/stretchr/testify/assert"
21 "google.golang.org/protobuf/proto"
22 )
23
24
25
26
27 func TestRenderJSON(t *testing.T) {
28 w := httptest.NewRecorder()
29 data := map[string]any{
30 "foo": "bar",
31 "html": "<b>",
32 }
33
34 (JSON{data}).WriteContentType(w)
35 assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
36
37 err := (JSON{data}).Render(w)
38
39 assert.NoError(t, err)
40 assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}", w.Body.String())
41 assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
42 }
43
44 func TestRenderJSONError(t *testing.T) {
45 w := httptest.NewRecorder()
46 data := make(chan int)
47
48
49 assert.Error(t, (JSON{data}).Render(w))
50 }
51
52 func TestRenderIndentedJSON(t *testing.T) {
53 w := httptest.NewRecorder()
54 data := map[string]any{
55 "foo": "bar",
56 "bar": "foo",
57 }
58
59 err := (IndentedJSON{data}).Render(w)
60
61 assert.NoError(t, err)
62 assert.Equal(t, "{\n \"bar\": \"foo\",\n \"foo\": \"bar\"\n}", w.Body.String())
63 assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
64 }
65
66 func TestRenderIndentedJSONPanics(t *testing.T) {
67 w := httptest.NewRecorder()
68 data := make(chan int)
69
70
71 err := (IndentedJSON{data}).Render(w)
72 assert.Error(t, err)
73 }
74
75 func TestRenderSecureJSON(t *testing.T) {
76 w1 := httptest.NewRecorder()
77 data := map[string]any{
78 "foo": "bar",
79 }
80
81 (SecureJSON{"while(1);", data}).WriteContentType(w1)
82 assert.Equal(t, "application/json; charset=utf-8", w1.Header().Get("Content-Type"))
83
84 err1 := (SecureJSON{"while(1);", data}).Render(w1)
85
86 assert.NoError(t, err1)
87 assert.Equal(t, "{\"foo\":\"bar\"}", w1.Body.String())
88 assert.Equal(t, "application/json; charset=utf-8", w1.Header().Get("Content-Type"))
89
90 w2 := httptest.NewRecorder()
91 datas := []map[string]any{{
92 "foo": "bar",
93 }, {
94 "bar": "foo",
95 }}
96
97 err2 := (SecureJSON{"while(1);", datas}).Render(w2)
98 assert.NoError(t, err2)
99 assert.Equal(t, "while(1);[{\"foo\":\"bar\"},{\"bar\":\"foo\"}]", w2.Body.String())
100 assert.Equal(t, "application/json; charset=utf-8", w2.Header().Get("Content-Type"))
101 }
102
103 func TestRenderSecureJSONFail(t *testing.T) {
104 w := httptest.NewRecorder()
105 data := make(chan int)
106
107
108 err := (SecureJSON{"while(1);", data}).Render(w)
109 assert.Error(t, err)
110 }
111
112 func TestRenderJsonpJSON(t *testing.T) {
113 w1 := httptest.NewRecorder()
114 data := map[string]any{
115 "foo": "bar",
116 }
117
118 (JsonpJSON{"x", data}).WriteContentType(w1)
119 assert.Equal(t, "application/javascript; charset=utf-8", w1.Header().Get("Content-Type"))
120
121 err1 := (JsonpJSON{"x", data}).Render(w1)
122
123 assert.NoError(t, err1)
124 assert.Equal(t, "x({\"foo\":\"bar\"});", w1.Body.String())
125 assert.Equal(t, "application/javascript; charset=utf-8", w1.Header().Get("Content-Type"))
126
127 w2 := httptest.NewRecorder()
128 datas := []map[string]any{{
129 "foo": "bar",
130 }, {
131 "bar": "foo",
132 }}
133
134 err2 := (JsonpJSON{"x", datas}).Render(w2)
135 assert.NoError(t, err2)
136 assert.Equal(t, "x([{\"foo\":\"bar\"},{\"bar\":\"foo\"}]);", w2.Body.String())
137 assert.Equal(t, "application/javascript; charset=utf-8", w2.Header().Get("Content-Type"))
138 }
139
140 type errorWriter struct {
141 bufString string
142 *httptest.ResponseRecorder
143 }
144
145 var _ http.ResponseWriter = (*errorWriter)(nil)
146
147 func (w *errorWriter) Write(buf []byte) (int, error) {
148 if string(buf) == w.bufString {
149 return 0, errors.New(`write "` + w.bufString + `" error`)
150 }
151 return w.ResponseRecorder.Write(buf)
152 }
153
154 func TestRenderJsonpJSONError(t *testing.T) {
155 ew := &errorWriter{
156 ResponseRecorder: httptest.NewRecorder(),
157 }
158
159 jsonpJSON := JsonpJSON{
160 Callback: "foo",
161 Data: map[string]string{
162 "foo": "bar",
163 },
164 }
165
166 cb := template.JSEscapeString(jsonpJSON.Callback)
167 ew.bufString = cb
168 err := jsonpJSON.Render(ew)
169 assert.Equal(t, `write "`+cb+`" error`, err.Error())
170
171 ew.bufString = `(`
172 err = jsonpJSON.Render(ew)
173 assert.Equal(t, `write "`+`(`+`" error`, err.Error())
174
175 data, _ := json.Marshal(jsonpJSON.Data)
176 ew.bufString = string(data)
177 err = jsonpJSON.Render(ew)
178 assert.Equal(t, `write "`+string(data)+`" error`, err.Error())
179
180 ew.bufString = `);`
181 err = jsonpJSON.Render(ew)
182 assert.Equal(t, `write "`+`);`+`" error`, err.Error())
183 }
184
185 func TestRenderJsonpJSONError2(t *testing.T) {
186 w := httptest.NewRecorder()
187 data := map[string]any{
188 "foo": "bar",
189 }
190 (JsonpJSON{"", data}).WriteContentType(w)
191 assert.Equal(t, "application/javascript; charset=utf-8", w.Header().Get("Content-Type"))
192
193 e := (JsonpJSON{"", data}).Render(w)
194 assert.NoError(t, e)
195
196 assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String())
197 assert.Equal(t, "application/javascript; charset=utf-8", w.Header().Get("Content-Type"))
198 }
199
200 func TestRenderJsonpJSONFail(t *testing.T) {
201 w := httptest.NewRecorder()
202 data := make(chan int)
203
204
205 err := (JsonpJSON{"x", data}).Render(w)
206 assert.Error(t, err)
207 }
208
209 func TestRenderAsciiJSON(t *testing.T) {
210 w1 := httptest.NewRecorder()
211 data1 := map[string]any{
212 "lang": "GO语言",
213 "tag": "<br>",
214 }
215
216 err := (AsciiJSON{data1}).Render(w1)
217
218 assert.NoError(t, err)
219 assert.Equal(t, "{\"lang\":\"GO\\u8bed\\u8a00\",\"tag\":\"\\u003cbr\\u003e\"}", w1.Body.String())
220 assert.Equal(t, "application/json", w1.Header().Get("Content-Type"))
221
222 w2 := httptest.NewRecorder()
223 data2 := 3.1415926
224
225 err = (AsciiJSON{data2}).Render(w2)
226 assert.NoError(t, err)
227 assert.Equal(t, "3.1415926", w2.Body.String())
228 }
229
230 func TestRenderAsciiJSONFail(t *testing.T) {
231 w := httptest.NewRecorder()
232 data := make(chan int)
233
234
235 assert.Error(t, (AsciiJSON{data}).Render(w))
236 }
237
238 func TestRenderPureJSON(t *testing.T) {
239 w := httptest.NewRecorder()
240 data := map[string]any{
241 "foo": "bar",
242 "html": "<b>",
243 }
244 err := (PureJSON{data}).Render(w)
245 assert.NoError(t, err)
246 assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"<b>\"}\n", w.Body.String())
247 assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
248 }
249
250 type xmlmap map[string]any
251
252
253 func (h xmlmap) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
254 start.Name = xml.Name{
255 Space: "",
256 Local: "map",
257 }
258 if err := e.EncodeToken(start); err != nil {
259 return err
260 }
261 for key, value := range h {
262 elem := xml.StartElement{
263 Name: xml.Name{Space: "", Local: key},
264 Attr: []xml.Attr{},
265 }
266 if err := e.EncodeElement(value, elem); err != nil {
267 return err
268 }
269 }
270
271 return e.EncodeToken(xml.EndElement{Name: start.Name})
272 }
273
274 func TestRenderYAML(t *testing.T) {
275 w := httptest.NewRecorder()
276 data := `
277 a : Easy!
278 b:
279 c: 2
280 d: [3, 4]
281 `
282 (YAML{data}).WriteContentType(w)
283 assert.Equal(t, "application/x-yaml; charset=utf-8", w.Header().Get("Content-Type"))
284
285 err := (YAML{data}).Render(w)
286 assert.NoError(t, err)
287 assert.Equal(t, "|4-\n a : Easy!\n b:\n \tc: 2\n \td: [3, 4]\n \t\n", w.Body.String())
288 assert.Equal(t, "application/x-yaml; charset=utf-8", w.Header().Get("Content-Type"))
289 }
290
291 type fail struct{}
292
293
294 func (ft *fail) MarshalYAML() (any, error) {
295 return nil, errors.New("fail")
296 }
297
298 func TestRenderYAMLFail(t *testing.T) {
299 w := httptest.NewRecorder()
300 err := (YAML{&fail{}}).Render(w)
301 assert.Error(t, err)
302 }
303
304 func TestRenderTOML(t *testing.T) {
305 w := httptest.NewRecorder()
306 data := map[string]any{
307 "foo": "bar",
308 "html": "<b>",
309 }
310 (TOML{data}).WriteContentType(w)
311 assert.Equal(t, "application/toml; charset=utf-8", w.Header().Get("Content-Type"))
312
313 err := (TOML{data}).Render(w)
314 assert.NoError(t, err)
315 assert.Equal(t, "foo = 'bar'\nhtml = '<b>'\n", w.Body.String())
316 assert.Equal(t, "application/toml; charset=utf-8", w.Header().Get("Content-Type"))
317 }
318
319 func TestRenderTOMLFail(t *testing.T) {
320 w := httptest.NewRecorder()
321 err := (TOML{net.IPv4bcast}).Render(w)
322 assert.Error(t, err)
323 }
324
325
326 func TestRenderProtoBuf(t *testing.T) {
327 w := httptest.NewRecorder()
328 reps := []int64{int64(1), int64(2)}
329 label := "test"
330 data := &testdata.Test{
331 Label: &label,
332 Reps: reps,
333 }
334
335 (ProtoBuf{data}).WriteContentType(w)
336 protoData, err := proto.Marshal(data)
337 assert.NoError(t, err)
338 assert.Equal(t, "application/x-protobuf", w.Header().Get("Content-Type"))
339
340 err = (ProtoBuf{data}).Render(w)
341
342 assert.NoError(t, err)
343 assert.Equal(t, string(protoData), w.Body.String())
344 assert.Equal(t, "application/x-protobuf", w.Header().Get("Content-Type"))
345 }
346
347 func TestRenderProtoBufFail(t *testing.T) {
348 w := httptest.NewRecorder()
349 data := &testdata.Test{}
350 err := (ProtoBuf{data}).Render(w)
351 assert.Error(t, err)
352 }
353
354 func TestRenderXML(t *testing.T) {
355 w := httptest.NewRecorder()
356 data := xmlmap{
357 "foo": "bar",
358 }
359
360 (XML{data}).WriteContentType(w)
361 assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type"))
362
363 err := (XML{data}).Render(w)
364
365 assert.NoError(t, err)
366 assert.Equal(t, "<map><foo>bar</foo></map>", w.Body.String())
367 assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type"))
368 }
369
370 func TestRenderRedirect(t *testing.T) {
371 req, err := http.NewRequest("GET", "/test-redirect", nil)
372 assert.NoError(t, err)
373
374 data1 := Redirect{
375 Code: http.StatusMovedPermanently,
376 Request: req,
377 Location: "/new/location",
378 }
379
380 w := httptest.NewRecorder()
381 err = data1.Render(w)
382 assert.NoError(t, err)
383
384 data2 := Redirect{
385 Code: http.StatusOK,
386 Request: req,
387 Location: "/new/location",
388 }
389
390 w = httptest.NewRecorder()
391 assert.PanicsWithValue(t, "Cannot redirect with status code 200", func() {
392 err := data2.Render(w)
393 assert.NoError(t, err)
394 })
395
396 data3 := Redirect{
397 Code: http.StatusCreated,
398 Request: req,
399 Location: "/new/location",
400 }
401
402 w = httptest.NewRecorder()
403 err = data3.Render(w)
404 assert.NoError(t, err)
405
406
407 data2.WriteContentType(w)
408 }
409
410 func TestRenderData(t *testing.T) {
411 w := httptest.NewRecorder()
412 data := []byte("#!PNG some raw data")
413
414 err := (Data{
415 ContentType: "image/png",
416 Data: data,
417 }).Render(w)
418
419 assert.NoError(t, err)
420 assert.Equal(t, "#!PNG some raw data", w.Body.String())
421 assert.Equal(t, "image/png", w.Header().Get("Content-Type"))
422 }
423
424 func TestRenderString(t *testing.T) {
425 w := httptest.NewRecorder()
426
427 (String{
428 Format: "hello %s %d",
429 Data: []any{},
430 }).WriteContentType(w)
431 assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
432
433 err := (String{
434 Format: "hola %s %d",
435 Data: []any{"manu", 2},
436 }).Render(w)
437
438 assert.NoError(t, err)
439 assert.Equal(t, "hola manu 2", w.Body.String())
440 assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
441 }
442
443 func TestRenderStringLenZero(t *testing.T) {
444 w := httptest.NewRecorder()
445
446 err := (String{
447 Format: "hola %s %d",
448 Data: []any{},
449 }).Render(w)
450
451 assert.NoError(t, err)
452 assert.Equal(t, "hola %s %d", w.Body.String())
453 assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
454 }
455
456 func TestRenderHTMLTemplate(t *testing.T) {
457 w := httptest.NewRecorder()
458 templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
459
460 htmlRender := HTMLProduction{Template: templ}
461 instance := htmlRender.Instance("t", map[string]any{
462 "name": "alexandernyquist",
463 })
464
465 err := instance.Render(w)
466
467 assert.NoError(t, err)
468 assert.Equal(t, "Hello alexandernyquist", w.Body.String())
469 assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
470 }
471
472 func TestRenderHTMLTemplateEmptyName(t *testing.T) {
473 w := httptest.NewRecorder()
474 templ := template.Must(template.New("").Parse(`Hello {{.name}}`))
475
476 htmlRender := HTMLProduction{Template: templ}
477 instance := htmlRender.Instance("", map[string]any{
478 "name": "alexandernyquist",
479 })
480
481 err := instance.Render(w)
482
483 assert.NoError(t, err)
484 assert.Equal(t, "Hello alexandernyquist", w.Body.String())
485 assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
486 }
487
488 func TestRenderHTMLDebugFiles(t *testing.T) {
489 w := httptest.NewRecorder()
490 htmlRender := HTMLDebug{
491 Files: []string{"../testdata/template/hello.tmpl"},
492 Glob: "",
493 Delims: Delims{Left: "{[{", Right: "}]}"},
494 FuncMap: nil,
495 }
496 instance := htmlRender.Instance("hello.tmpl", map[string]any{
497 "name": "thinkerou",
498 })
499
500 err := instance.Render(w)
501
502 assert.NoError(t, err)
503 assert.Equal(t, "<h1>Hello thinkerou</h1>", w.Body.String())
504 assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
505 }
506
507 func TestRenderHTMLDebugGlob(t *testing.T) {
508 w := httptest.NewRecorder()
509 htmlRender := HTMLDebug{
510 Files: nil,
511 Glob: "../testdata/template/hello*",
512 Delims: Delims{Left: "{[{", Right: "}]}"},
513 FuncMap: nil,
514 }
515 instance := htmlRender.Instance("hello.tmpl", map[string]any{
516 "name": "thinkerou",
517 })
518
519 err := instance.Render(w)
520
521 assert.NoError(t, err)
522 assert.Equal(t, "<h1>Hello thinkerou</h1>", w.Body.String())
523 assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
524 }
525
526 func TestRenderHTMLDebugPanics(t *testing.T) {
527 htmlRender := HTMLDebug{
528 Files: nil,
529 Glob: "",
530 Delims: Delims{"{{", "}}"},
531 FuncMap: nil,
532 }
533 assert.Panics(t, func() { htmlRender.Instance("", nil) })
534 }
535
536 func TestRenderReader(t *testing.T) {
537 w := httptest.NewRecorder()
538
539 body := "#!PNG some raw data"
540 headers := make(map[string]string)
541 headers["Content-Disposition"] = `attachment; filename="filename.png"`
542 headers["x-request-id"] = "requestId"
543
544 err := (Reader{
545 ContentLength: int64(len(body)),
546 ContentType: "image/png",
547 Reader: strings.NewReader(body),
548 Headers: headers,
549 }).Render(w)
550
551 assert.NoError(t, err)
552 assert.Equal(t, body, w.Body.String())
553 assert.Equal(t, "image/png", w.Header().Get("Content-Type"))
554 assert.Equal(t, strconv.Itoa(len(body)), w.Header().Get("Content-Length"))
555 assert.Equal(t, headers["Content-Disposition"], w.Header().Get("Content-Disposition"))
556 assert.Equal(t, headers["x-request-id"], w.Header().Get("x-request-id"))
557 }
558
559 func TestRenderReaderNoContentLength(t *testing.T) {
560 w := httptest.NewRecorder()
561
562 body := "#!PNG some raw data"
563 headers := make(map[string]string)
564 headers["Content-Disposition"] = `attachment; filename="filename.png"`
565 headers["x-request-id"] = "requestId"
566
567 err := (Reader{
568 ContentLength: -1,
569 ContentType: "image/png",
570 Reader: strings.NewReader(body),
571 Headers: headers,
572 }).Render(w)
573
574 assert.NoError(t, err)
575 assert.Equal(t, body, w.Body.String())
576 assert.Equal(t, "image/png", w.Header().Get("Content-Type"))
577 assert.NotContains(t, "Content-Length", w.Header())
578 assert.Equal(t, headers["Content-Disposition"], w.Header().Get("Content-Disposition"))
579 assert.Equal(t, headers["x-request-id"], w.Header().Get("x-request-id"))
580 }
581
View as plain text