github.com/cloudwego/hertz@v0.9.3/pkg/app/server/render/render_test.go (about) 1 /* 2 * Copyright 2022 CloudWeGo Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * The MIT License (MIT) 17 * 18 * Copyright (c) 2014 Manuel MartÃnez-Almeida 19 * 20 * Permission is hereby granted, free of charge, to any person obtaining a copy 21 * of this software and associated documentation files (the "Software"), to deal 22 * in the Software without restriction, including without limitation the rights 23 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 24 * copies of the Software, and to permit persons to whom the Software is 25 * furnished to do so, subject to the following conditions: 26 * 27 * The above copyright notice and this permission notice shall be included in 28 * all copies or substantial portions of the Software. 29 * 30 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 31 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 32 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 33 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 34 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 35 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 36 * THE SOFTWARE. 37 38 * This file may have been modified by CloudWeGo authors. All CloudWeGo 39 * Modifications are Copyright 2022 CloudWeGo Authors. 40 */ 41 42 package render 43 44 import ( 45 "encoding/xml" 46 "testing" 47 48 "github.com/bytedance/sonic" 49 "github.com/cloudwego/hertz/pkg/common/test/assert" 50 "github.com/cloudwego/hertz/pkg/common/testdata/proto" 51 "github.com/cloudwego/hertz/pkg/protocol" 52 "github.com/cloudwego/hertz/pkg/protocol/consts" 53 ) 54 55 type xmlmap map[string]interface{} 56 57 // Allows type H to be used with xml.Marshal 58 func (h xmlmap) MarshalXML(e *xml.Encoder, start xml.StartElement) error { 59 start.Name = xml.Name{ 60 Space: "", 61 Local: "map", 62 } 63 if err := e.EncodeToken(start); err != nil { 64 return err 65 } 66 for key, value := range h { 67 elem := xml.StartElement{ 68 Name: xml.Name{Space: "", Local: key}, 69 Attr: []xml.Attr{}, 70 } 71 if err := e.EncodeElement(value, elem); err != nil { 72 return err 73 } 74 } 75 76 return e.EncodeToken(xml.EndElement{Name: start.Name}) 77 } 78 79 func TestRenderJSON(t *testing.T) { 80 resp := &protocol.Response{} 81 data := map[string]interface{}{ 82 "foo": "bar", 83 "html": "<b>", 84 } 85 86 (JSONRender{data}).WriteContentType(resp) 87 assert.DeepEqual(t, []byte(consts.MIMEApplicationJSONUTF8), resp.Header.Peek("Content-Type")) 88 89 err := (JSONRender{data}).Render(resp) 90 91 assert.Nil(t, err) 92 assert.DeepEqual(t, []byte("{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}"), resp.Body()) 93 assert.DeepEqual(t, []byte(consts.MIMEApplicationJSONUTF8), resp.Header.Peek("Content-Type")) 94 } 95 96 func TestRenderJSONError(t *testing.T) { 97 resp := &protocol.Response{} 98 data := make(chan int) 99 100 err := (JSONRender{data}).Render(resp) 101 // json: unsupported type: chan int 102 assert.NotNil(t, err) 103 } 104 105 func TestRenderPureJSON(t *testing.T) { 106 resp := &protocol.Response{} 107 data := map[string]interface{}{ 108 "foo": "bar", 109 "html": "<b>", 110 } 111 112 (PureJSON{data}).WriteContentType(resp) 113 assert.DeepEqual(t, []byte(consts.MIMEApplicationJSONUTF8), resp.Header.Peek("Content-Type")) 114 115 err := (PureJSON{data}).Render(resp) 116 117 assert.Nil(t, err) 118 119 assert.DeepEqual(t, []byte("{\"foo\":\"bar\",\"html\":\"<b>\"}\n"), resp.Body()) 120 assert.DeepEqual(t, []byte(consts.MIMEApplicationJSONUTF8), resp.Header.Peek("Content-Type")) 121 } 122 123 func TestRenderPureJSONError(t *testing.T) { 124 resp := &protocol.Response{} 125 data := make(chan int) 126 127 err := (PureJSON{data}).Render(resp) 128 // json: unsupported type: chan int 129 assert.NotNil(t, err) 130 } 131 132 func TestRenderProtobuf(t *testing.T) { 133 resp := &protocol.Response{} 134 data := proto.TestStruct{Body: []byte("Hello World")} 135 136 (ProtoBuf{&data}).WriteContentType(resp) 137 assert.DeepEqual(t, []byte("application/x-protobuf"), resp.Header.Peek("Content-Type")) 138 139 err := (ProtoBuf{&data}).Render(resp) 140 141 assert.Nil(t, err) 142 assert.DeepEqual(t, []byte("\n\vHello World"), resp.Body()) 143 assert.DeepEqual(t, []byte("application/x-protobuf"), resp.Header.Peek("Content-Type")) 144 } 145 146 func TestRenderProtobufError(t *testing.T) { 147 resp := &protocol.Response{} 148 data := proto.Test{} 149 150 err := (ProtoBuf{&data}).Render(resp) 151 152 assert.NotNil(t, err) 153 } 154 155 func TestRenderString(t *testing.T) { 156 resp := &protocol.Response{} 157 158 (String{ 159 Format: "hello %s %d", 160 Data: []interface{}{}, 161 }).WriteContentType(resp) 162 assert.DeepEqual(t, []byte(consts.MIMETextPlainUTF8), resp.Header.Peek("Content-Type")) 163 164 err := (String{ 165 Format: "hola %s %d", 166 Data: []interface{}{"manu", 2}, 167 }).Render(resp) 168 169 assert.Nil(t, err) 170 assert.DeepEqual(t, []byte("hola manu 2"), resp.Body()) 171 assert.DeepEqual(t, []byte(consts.MIMETextPlainUTF8), resp.Header.Peek("Content-Type")) 172 } 173 174 func TestRenderStringLenZero(t *testing.T) { 175 resp := &protocol.Response{} 176 177 err := (String{ 178 Format: "hola %s %d", 179 Data: []interface{}{}, 180 }).Render(resp) 181 182 assert.Nil(t, err) 183 assert.DeepEqual(t, []byte("hola %s %d"), resp.Body()) 184 assert.DeepEqual(t, []byte(consts.MIMETextPlainUTF8), resp.Header.Peek("Content-Type")) 185 } 186 187 func TestRenderData(t *testing.T) { 188 resp := &protocol.Response{} 189 data := []byte("#!PNG some raw data") 190 191 err := (Data{ 192 ContentType: "image/png", 193 Data: data, 194 }).Render(resp) 195 196 assert.Nil(t, err) 197 assert.DeepEqual(t, []byte("#!PNG some raw data"), resp.Body()) 198 assert.DeepEqual(t, []byte(consts.MIMEImagePNG), resp.Header.Peek("Content-Type")) 199 } 200 201 func TestRenderXML(t *testing.T) { 202 resp := &protocol.Response{} 203 data := xmlmap{ 204 "foo": "bar", 205 } 206 207 (XML{data}).WriteContentType(resp) 208 assert.DeepEqual(t, []byte(consts.MIMEApplicationXMLUTF8), resp.Header.Peek("Content-Type")) 209 210 err := (XML{data}).Render(resp) 211 212 assert.Nil(t, err) 213 assert.DeepEqual(t, []byte("<map><foo>bar</foo></map>"), resp.Body()) 214 assert.DeepEqual(t, []byte(consts.MIMEApplicationXMLUTF8), resp.Header.Peek("Content-Type")) 215 } 216 217 func TestRenderXMLError(t *testing.T) { 218 resp := &protocol.Response{} 219 data := make(chan int) 220 221 err := (XML{data}).Render(resp) 222 223 assert.NotNil(t, err) 224 } 225 226 func TestRenderIndentedJSON(t *testing.T) { 227 data := map[string]interface{}{ 228 "foo": "bar", 229 "html": "h1", 230 } 231 t.Run("TestHeader", func(t *testing.T) { 232 resp := &protocol.Response{} 233 (IndentedJSON{data}).WriteContentType(resp) 234 assert.DeepEqual(t, []byte(consts.MIMEApplicationJSONUTF8), resp.Header.Peek("Content-Type")) 235 }) 236 t.Run("TestBody", func(t *testing.T) { 237 ResetStdJSONMarshal() 238 resp := &protocol.Response{} 239 err := (IndentedJSON{data}).Render(resp) 240 assert.Nil(t, err) 241 assert.DeepEqual(t, []byte("{\n \"foo\": \"bar\",\n \"html\": \"h1\"\n}"), resp.Body()) 242 assert.DeepEqual(t, []byte(consts.MIMEApplicationJSONUTF8), resp.Header.Peek("Content-Type")) 243 ResetJSONMarshal(sonic.Marshal) 244 }) 245 t.Run("TestError", func(t *testing.T) { 246 resp := &protocol.Response{} 247 ch := make(chan int) 248 err := (IndentedJSON{ch}).Render(resp) 249 assert.NotNil(t, err) 250 }) 251 }