gin/render/render_test.go

543 lines
14 KiB
Go
Raw Normal View History

2015-04-09 13:15:02 +03:00
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package render
import (
"encoding/xml"
"errors"
2015-04-09 13:15:02 +03:00
"html/template"
"net/http"
2015-04-09 13:15:02 +03:00
"net/http/httptest"
"strconv"
"strings"
2015-04-09 13:15:02 +03:00
"testing"
2021-09-21 10:22:21 +03:00
testdata "github.com/gin-gonic/gin/testdata/protoexample"
2015-04-09 13:15:02 +03:00
"github.com/stretchr/testify/assert"
"google.golang.org/protobuf/proto"
2015-04-09 13:15:02 +03:00
)
2015-05-09 04:35:31 +03:00
// TODO unit tests
// test errors
2015-04-09 13:15:02 +03:00
func TestRenderJSON(t *testing.T) {
w := httptest.NewRecorder()
2022-03-21 04:43:17 +03:00
data := map[string]any{
"foo": "bar",
"html": "<b>",
}
(JSON{data}).WriteContentType(w)
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
2015-06-04 06:25:21 +03:00
err := (JSON{data}).Render(w)
2015-04-09 13:15:02 +03:00
assert.NoError(t, err)
assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}", w.Body.String())
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
2015-04-09 13:15:02 +03:00
}
func TestRenderJSONPanics(t *testing.T) {
w := httptest.NewRecorder()
data := make(chan int)
// json: unsupported type: chan int
2019-01-18 04:32:53 +03:00
assert.Panics(t, func() { assert.NoError(t, (JSON{data}).Render(w)) })
}
2015-04-09 13:15:02 +03:00
func TestRenderIndentedJSON(t *testing.T) {
w := httptest.NewRecorder()
2022-03-21 04:43:17 +03:00
data := map[string]any{
2015-04-09 13:15:02 +03:00
"foo": "bar",
"bar": "foo",
2015-05-18 16:45:24 +03:00
}
2015-06-04 06:25:21 +03:00
err := (IndentedJSON{data}).Render(w)
2015-04-09 13:15:02 +03:00
assert.NoError(t, err)
assert.Equal(t, "{\n \"bar\": \"foo\",\n \"foo\": \"bar\"\n}", w.Body.String())
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
2015-04-09 13:15:02 +03:00
}
func TestRenderIndentedJSONPanics(t *testing.T) {
w := httptest.NewRecorder()
data := make(chan int)
// json: unsupported type: chan int
err := (IndentedJSON{data}).Render(w)
assert.Error(t, err)
}
func TestRenderSecureJSON(t *testing.T) {
w1 := httptest.NewRecorder()
2022-03-21 04:43:17 +03:00
data := map[string]any{
"foo": "bar",
}
(SecureJSON{"while(1);", data}).WriteContentType(w1)
assert.Equal(t, "application/json; charset=utf-8", w1.Header().Get("Content-Type"))
err1 := (SecureJSON{"while(1);", data}).Render(w1)
assert.NoError(t, err1)
assert.Equal(t, "{\"foo\":\"bar\"}", w1.Body.String())
assert.Equal(t, "application/json; charset=utf-8", w1.Header().Get("Content-Type"))
w2 := httptest.NewRecorder()
2022-03-21 04:43:17 +03:00
datas := []map[string]any{{
"foo": "bar",
}, {
"bar": "foo",
}}
err2 := (SecureJSON{"while(1);", datas}).Render(w2)
assert.NoError(t, err2)
assert.Equal(t, "while(1);[{\"foo\":\"bar\"},{\"bar\":\"foo\"}]", w2.Body.String())
assert.Equal(t, "application/json; charset=utf-8", w2.Header().Get("Content-Type"))
}
func TestRenderSecureJSONFail(t *testing.T) {
w := httptest.NewRecorder()
data := make(chan int)
// json: unsupported type: chan int
err := (SecureJSON{"while(1);", data}).Render(w)
assert.Error(t, err)
}
2018-04-26 06:52:19 +03:00
func TestRenderJsonpJSON(t *testing.T) {
w1 := httptest.NewRecorder()
2022-03-21 04:43:17 +03:00
data := map[string]any{
2018-04-26 06:52:19 +03:00
"foo": "bar",
}
(JsonpJSON{"x", data}).WriteContentType(w1)
assert.Equal(t, "application/javascript; charset=utf-8", w1.Header().Get("Content-Type"))
err1 := (JsonpJSON{"x", data}).Render(w1)
assert.NoError(t, err1)
assert.Equal(t, "x({\"foo\":\"bar\"});", w1.Body.String())
2018-04-26 06:52:19 +03:00
assert.Equal(t, "application/javascript; charset=utf-8", w1.Header().Get("Content-Type"))
w2 := httptest.NewRecorder()
2022-03-21 04:43:17 +03:00
datas := []map[string]any{{
2018-04-26 06:52:19 +03:00
"foo": "bar",
}, {
"bar": "foo",
}}
err2 := (JsonpJSON{"x", datas}).Render(w2)
assert.NoError(t, err2)
assert.Equal(t, "x([{\"foo\":\"bar\"},{\"bar\":\"foo\"}]);", w2.Body.String())
2018-04-26 06:52:19 +03:00
assert.Equal(t, "application/javascript; charset=utf-8", w2.Header().Get("Content-Type"))
}
func TestRenderJsonpJSONError2(t *testing.T) {
w := httptest.NewRecorder()
2022-03-21 04:43:17 +03:00
data := map[string]any{
"foo": "bar",
}
(JsonpJSON{"", data}).WriteContentType(w)
assert.Equal(t, "application/javascript; charset=utf-8", w.Header().Get("Content-Type"))
e := (JsonpJSON{"", data}).Render(w)
assert.NoError(t, e)
assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String())
assert.Equal(t, "application/javascript; charset=utf-8", w.Header().Get("Content-Type"))
}
2018-04-26 06:52:19 +03:00
func TestRenderJsonpJSONFail(t *testing.T) {
w := httptest.NewRecorder()
data := make(chan int)
// json: unsupported type: chan int
err := (JsonpJSON{"x", data}).Render(w)
assert.Error(t, err)
}
func TestRenderAsciiJSON(t *testing.T) {
w1 := httptest.NewRecorder()
2022-03-21 04:43:17 +03:00
data1 := map[string]any{
"lang": "GO语言",
"tag": "<br>",
}
err := (AsciiJSON{data1}).Render(w1)
assert.NoError(t, err)
assert.Equal(t, "{\"lang\":\"GO\\u8bed\\u8a00\",\"tag\":\"\\u003cbr\\u003e\"}", w1.Body.String())
assert.Equal(t, "application/json", w1.Header().Get("Content-Type"))
w2 := httptest.NewRecorder()
data2 := float64(3.1415926)
err = (AsciiJSON{data2}).Render(w2)
assert.NoError(t, err)
assert.Equal(t, "3.1415926", w2.Body.String())
}
func TestRenderAsciiJSONFail(t *testing.T) {
w := httptest.NewRecorder()
data := make(chan int)
// json: unsupported type: chan int
assert.Error(t, (AsciiJSON{data}).Render(w))
}
func TestRenderPureJSON(t *testing.T) {
w := httptest.NewRecorder()
2022-03-21 04:43:17 +03:00
data := map[string]any{
"foo": "bar",
"html": "<b>",
}
err := (PureJSON{data}).Render(w)
assert.NoError(t, err)
assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"<b>\"}\n", w.Body.String())
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
}
2022-03-21 04:43:17 +03:00
type xmlmap map[string]any
// Allows type H to be used with xml.Marshal
func (h xmlmap) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
start.Name = xml.Name{
Space: "",
Local: "map",
}
if err := e.EncodeToken(start); err != nil {
return err
}
for key, value := range h {
elem := xml.StartElement{
Name: xml.Name{Space: "", Local: key},
Attr: []xml.Attr{},
}
if err := e.EncodeElement(value, elem); err != nil {
return err
}
}
return e.EncodeToken(xml.EndElement{Name: start.Name})
}
func TestRenderYAML(t *testing.T) {
w := httptest.NewRecorder()
data := `
a : Easy!
b:
c: 2
d: [3, 4]
`
(YAML{data}).WriteContentType(w)
assert.Equal(t, "application/x-yaml; charset=utf-8", w.Header().Get("Content-Type"))
err := (YAML{data}).Render(w)
assert.NoError(t, err)
assert.Equal(t, "\"\\na : Easy!\\nb:\\n\\tc: 2\\n\\td: [3, 4]\\n\\t\"\n", w.Body.String())
assert.Equal(t, "application/x-yaml; charset=utf-8", w.Header().Get("Content-Type"))
}
type fail struct{}
// Hook MarshalYAML
2022-03-21 04:43:17 +03:00
func (ft *fail) MarshalYAML() (any, error) {
return nil, errors.New("fail")
}
func TestRenderYAMLFail(t *testing.T) {
w := httptest.NewRecorder()
err := (YAML{&fail{}}).Render(w)
assert.Error(t, err)
}
// test Protobuf rendering
func TestRenderProtoBuf(t *testing.T) {
w := httptest.NewRecorder()
reps := []int64{int64(1), int64(2)}
label := "test"
data := &testdata.Test{
Label: &label,
Reps: reps,
}
(ProtoBuf{data}).WriteContentType(w)
protoData, err := proto.Marshal(data)
assert.NoError(t, err)
assert.Equal(t, "application/x-protobuf", w.Header().Get("Content-Type"))
err = (ProtoBuf{data}).Render(w)
assert.NoError(t, err)
assert.Equal(t, string(protoData), w.Body.String())
assert.Equal(t, "application/x-protobuf", w.Header().Get("Content-Type"))
}
func TestRenderProtoBufFail(t *testing.T) {
w := httptest.NewRecorder()
data := &testdata.Test{}
err := (ProtoBuf{data}).Render(w)
assert.Error(t, err)
}
func TestRenderXML(t *testing.T) {
w := httptest.NewRecorder()
data := xmlmap{
"foo": "bar",
}
(XML{data}).WriteContentType(w)
assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type"))
2015-06-04 06:25:21 +03:00
err := (XML{data}).Render(w)
assert.NoError(t, err)
assert.Equal(t, "<map><foo>bar</foo></map>", w.Body.String())
assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type"))
}
func TestRenderRedirect(t *testing.T) {
req, err := http.NewRequest("GET", "/test-redirect", nil)
assert.NoError(t, err)
data1 := Redirect{
Code: http.StatusMovedPermanently,
Request: req,
Location: "/new/location",
}
w := httptest.NewRecorder()
err = data1.Render(w)
assert.NoError(t, err)
data2 := Redirect{
Code: http.StatusOK,
Request: req,
Location: "/new/location",
}
w = httptest.NewRecorder()
2019-10-27 08:58:59 +03:00
assert.PanicsWithValue(t, "Cannot redirect with status code 200", func() {
err := data2.Render(w)
assert.NoError(t, err)
})
data3 := Redirect{
Code: http.StatusCreated,
Request: req,
Location: "/new/location",
}
w = httptest.NewRecorder()
err = data3.Render(w)
assert.NoError(t, err)
// only improve coverage
data2.WriteContentType(w)
}
func TestRenderData(t *testing.T) {
w := httptest.NewRecorder()
data := []byte("#!PNG some raw data")
2015-05-18 16:45:24 +03:00
err := (Data{
ContentType: "image/png",
Data: data,
2015-06-04 06:25:21 +03:00
}).Render(w)
assert.NoError(t, err)
assert.Equal(t, "#!PNG some raw data", w.Body.String())
assert.Equal(t, "image/png", w.Header().Get("Content-Type"))
}
2015-05-18 16:45:24 +03:00
func TestRenderString(t *testing.T) {
2015-04-09 13:15:02 +03:00
w := httptest.NewRecorder()
(String{
Format: "hello %s %d",
2022-03-21 04:43:17 +03:00
Data: []any{},
}).WriteContentType(w)
assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
2015-05-18 16:45:24 +03:00
err := (String{
Format: "hola %s %d",
2022-03-21 04:43:17 +03:00
Data: []any{"manu", 2},
2015-06-04 06:25:21 +03:00
}).Render(w)
2015-04-09 13:15:02 +03:00
assert.NoError(t, err)
assert.Equal(t, "hola manu 2", w.Body.String())
assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
2015-04-09 13:15:02 +03:00
}
func TestRenderStringLenZero(t *testing.T) {
w := httptest.NewRecorder()
err := (String{
Format: "hola %s %d",
2022-03-21 04:43:17 +03:00
Data: []any{},
}).Render(w)
assert.NoError(t, err)
assert.Equal(t, "hola %s %d", w.Body.String())
assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
}
2015-04-09 13:15:02 +03:00
func TestRenderHTMLTemplate(t *testing.T) {
w := httptest.NewRecorder()
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
2015-05-18 16:45:24 +03:00
htmlRender := HTMLProduction{Template: templ}
2022-03-21 04:43:17 +03:00
instance := htmlRender.Instance("t", map[string]any{
2015-04-09 13:15:02 +03:00
"name": "alexandernyquist",
})
2015-06-04 06:25:21 +03:00
err := instance.Render(w)
2015-05-18 16:45:24 +03:00
2015-04-09 13:15:02 +03:00
assert.NoError(t, err)
assert.Equal(t, "Hello alexandernyquist", w.Body.String())
assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
2015-04-09 13:15:02 +03:00
}
func TestRenderHTMLTemplateEmptyName(t *testing.T) {
w := httptest.NewRecorder()
templ := template.Must(template.New("").Parse(`Hello {{.name}}`))
htmlRender := HTMLProduction{Template: templ}
2022-03-21 04:43:17 +03:00
instance := htmlRender.Instance("", map[string]any{
"name": "alexandernyquist",
})
err := instance.Render(w)
assert.NoError(t, err)
assert.Equal(t, "Hello alexandernyquist", w.Body.String())
assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
}
func TestRenderHTMLDebugFiles(t *testing.T) {
w := httptest.NewRecorder()
htmlRender := HTMLDebug{
Files: []string{"../testdata/template/hello.tmpl"},
Glob: "",
Delims: Delims{Left: "{[{", Right: "}]}"},
FuncMap: nil,
}
2022-03-21 04:43:17 +03:00
instance := htmlRender.Instance("hello.tmpl", map[string]any{
"name": "thinkerou",
})
err := instance.Render(w)
assert.NoError(t, err)
assert.Equal(t, "<h1>Hello thinkerou</h1>", w.Body.String())
assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
}
func TestRenderHTMLDebugGlob(t *testing.T) {
w := httptest.NewRecorder()
htmlRender := HTMLDebug{
Files: nil,
Glob: "../testdata/template/hello*",
Delims: Delims{Left: "{[{", Right: "}]}"},
FuncMap: nil,
}
2022-03-21 04:43:17 +03:00
instance := htmlRender.Instance("hello.tmpl", map[string]any{
"name": "thinkerou",
})
err := instance.Render(w)
assert.NoError(t, err)
assert.Equal(t, "<h1>Hello thinkerou</h1>", w.Body.String())
assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
}
func TestRenderHTMLDebugPanics(t *testing.T) {
htmlRender := HTMLDebug{
Files: nil,
Glob: "",
Delims: Delims{"{{", "}}"},
FuncMap: nil,
}
assert.Panics(t, func() { htmlRender.Instance("", nil) })
}
func TestRenderReader(t *testing.T) {
w := httptest.NewRecorder()
body := "#!PNG some raw data"
headers := make(map[string]string)
headers["Content-Disposition"] = `attachment; filename="filename.png"`
headers["x-request-id"] = "requestId"
err := (Reader{
ContentLength: int64(len(body)),
ContentType: "image/png",
Reader: strings.NewReader(body),
Headers: headers,
}).Render(w)
assert.NoError(t, err)
assert.Equal(t, body, w.Body.String())
assert.Equal(t, "image/png", w.Header().Get("Content-Type"))
assert.Equal(t, strconv.Itoa(len(body)), w.Header().Get("Content-Length"))
assert.Equal(t, headers["Content-Disposition"], w.Header().Get("Content-Disposition"))
assert.Equal(t, headers["x-request-id"], w.Header().Get("x-request-id"))
}
func TestRenderReaderNoContentLength(t *testing.T) {
w := httptest.NewRecorder()
body := "#!PNG some raw data"
headers := make(map[string]string)
headers["Content-Disposition"] = `attachment; filename="filename.png"`
headers["x-request-id"] = "requestId"
err := (Reader{
ContentLength: -1,
ContentType: "image/png",
Reader: strings.NewReader(body),
Headers: headers,
}).Render(w)
assert.NoError(t, err)
assert.Equal(t, body, w.Body.String())
assert.Equal(t, "image/png", w.Header().Get("Content-Type"))
assert.NotContains(t, "Content-Length", w.Header())
assert.Equal(t, headers["Content-Disposition"], w.Header().Get("Content-Disposition"))
assert.Equal(t, headers["x-request-id"], w.Header().Get("x-request-id"))
}
func BenchmarkAsciiJSONRender(b *testing.B) {
data := getRenderData()
benchmarkRender(b, AsciiJSON{Data: data})
}
func BenchmarkJsonpJSONRender(b *testing.B) {
data := getRenderData()
benchmarkRender(b, JsonpJSON{Data: data})
}
func benchmarkRender(b *testing.B, r Render) {
w := httptest.NewRecorder()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
err := r.Render(w)
if err != nil {
b.Errorf("test json render error =>%v", err)
}
}
}
func getRenderData() map[string]any {
data := map[string]any{
"cn": "<h1>你好 世界</h1>",
"en": "<h1>hello world</h1>",
}
return data
}