github.com/go-playground/pkg/v5@v5.29.1/net/http/helpers_test.go (about) 1 package httpext 2 3 import ( 4 "bytes" 5 "compress/gzip" 6 "encoding/json" 7 "encoding/xml" 8 "mime/multipart" 9 "net/http" 10 "net/http/httptest" 11 "net/url" 12 "os" 13 "strings" 14 "testing" 15 16 . "github.com/go-playground/assert/v2" 17 ) 18 19 func TestAcceptedLanguages(t *testing.T) { 20 req, _ := http.NewRequest("POST", "/", nil) 21 req.Header.Set(AcceptedLanguage, "da, en-GB;q=0.8, en;q=0.7") 22 23 languages := AcceptedLanguages(req) 24 25 Equal(t, languages[0], "da") 26 Equal(t, languages[1], "en-GB") 27 Equal(t, languages[2], "en") 28 29 req.Header.Del(AcceptedLanguage) 30 31 languages = AcceptedLanguages(req) 32 Equal(t, len(languages), 0) 33 34 req.Header.Set(AcceptedLanguage, "") 35 languages = AcceptedLanguages(req) 36 Equal(t, len(languages), 0) 37 } 38 39 func TestAttachment(t *testing.T) { 40 mux := http.NewServeMux() 41 mux.HandleFunc("/dl", func(w http.ResponseWriter, r *http.Request) { 42 f, _ := os.Open("../../README.md") 43 if err := Attachment(w, f, "README.md"); err != nil { 44 panic(err) 45 } 46 }) 47 mux.HandleFunc("/dl-unknown-type", func(w http.ResponseWriter, r *http.Request) { 48 f, _ := os.Open("../../README.md") 49 if err := Attachment(w, f, "readme"); err != nil { 50 panic(err) 51 } 52 }) 53 mux.HandleFunc("/dl-fake-png", func(w http.ResponseWriter, r *http.Request) { 54 f, _ := os.Open("../../README.md") 55 if err := Attachment(w, f, "logo.png"); err != nil { 56 panic(err) 57 } 58 }) 59 60 tests := []struct { 61 name string 62 code int 63 disposition string 64 typ string 65 url string 66 }{ 67 { 68 code: http.StatusOK, 69 disposition: "attachment;filename=README.md", 70 typ: TextMarkdown, 71 url: "/dl", 72 }, 73 { 74 code: http.StatusOK, 75 disposition: "attachment;filename=readme", 76 typ: ApplicationOctetStream, 77 url: "/dl-unknown-type", 78 }, 79 { 80 code: http.StatusOK, 81 disposition: "attachment;filename=logo.png", 82 typ: ImagePNG, 83 url: "/dl-fake-png", 84 }, 85 } 86 87 for _, tt := range tests { 88 t.Run(tt.name, func(t *testing.T) { 89 req, err := http.NewRequest(http.MethodGet, tt.url, nil) 90 Equal(t, err, nil) 91 92 w := httptest.NewRecorder() 93 mux.ServeHTTP(w, req) 94 95 if tt.code != w.Code { 96 t.Errorf("Status Code = %d, want %d", w.Code, tt.code) 97 } 98 if tt.disposition != w.Header().Get(ContentDisposition) { 99 t.Errorf("Content Disaposition = %s, want %s", w.Header().Get(ContentDisposition), tt.disposition) 100 } 101 if tt.typ != w.Header().Get(ContentType) { 102 t.Errorf("Content Type = %s, want %s", w.Header().Get(ContentType), tt.typ) 103 } 104 }) 105 } 106 } 107 108 func TestInline(t *testing.T) { 109 mux := http.NewServeMux() 110 mux.HandleFunc("/dl-inline", func(w http.ResponseWriter, r *http.Request) { 111 f, _ := os.Open("../../README.md") 112 if err := Inline(w, f, "README.md"); err != nil { 113 panic(err) 114 } 115 }) 116 mux.HandleFunc("/dl-unknown-type-inline", func(w http.ResponseWriter, r *http.Request) { 117 f, _ := os.Open("../../README.md") 118 if err := Inline(w, f, "readme"); err != nil { 119 panic(err) 120 } 121 }) 122 123 tests := []struct { 124 name string 125 code int 126 disposition string 127 typ string 128 url string 129 }{ 130 { 131 code: http.StatusOK, 132 disposition: "inline;filename=README.md", 133 typ: TextMarkdown, 134 url: "/dl-inline", 135 }, 136 { 137 code: http.StatusOK, 138 disposition: "inline;filename=readme", 139 typ: ApplicationOctetStream, 140 url: "/dl-unknown-type-inline", 141 }, 142 } 143 144 for _, tt := range tests { 145 t.Run(tt.name, func(t *testing.T) { 146 req, err := http.NewRequest(http.MethodGet, tt.url, nil) 147 Equal(t, err, nil) 148 149 w := httptest.NewRecorder() 150 mux.ServeHTTP(w, req) 151 152 if tt.code != w.Code { 153 t.Errorf("Status Code = %d, want %d", w.Code, tt.code) 154 } 155 if tt.disposition != w.Header().Get(ContentDisposition) { 156 t.Errorf("Content Disaposition = %s, want %s", w.Header().Get(ContentDisposition), tt.disposition) 157 } 158 if tt.typ != w.Header().Get(ContentType) { 159 t.Errorf("Content Type = %s, want %s", w.Header().Get(ContentType), tt.typ) 160 } 161 }) 162 } 163 } 164 165 func TestClientIP(t *testing.T) { 166 req, _ := http.NewRequest("POST", "/", nil) 167 req.Header.Set("X-Real-IP", " 10.10.10.10 ") 168 req.Header.Set("X-Forwarded-For", " 20.20.20.20, 30.30.30.30") 169 req.RemoteAddr = " 40.40.40.40:42123 " 170 171 Equal(t, ClientIP(req), "10.10.10.10") 172 173 req.Header.Del("X-Real-IP") 174 Equal(t, ClientIP(req), "20.20.20.20") 175 176 req.Header.Set("X-Forwarded-For", "30.30.30.30 ") 177 Equal(t, ClientIP(req), "30.30.30.30") 178 179 req.Header.Del("X-Forwarded-For") 180 Equal(t, ClientIP(req), "40.40.40.40") 181 } 182 183 func TestJSON(t *testing.T) { 184 w := httptest.NewRecorder() 185 type test struct { 186 Field string `json:"field"` 187 } 188 tst := test{Field: "myfield"} 189 b, err := json.Marshal(tst) 190 Equal(t, err, nil) 191 192 err = JSON(w, http.StatusOK, tst) 193 Equal(t, err, nil) 194 Equal(t, w.Header().Get(ContentType), ApplicationJSON) 195 Equal(t, w.Body.Bytes(), b) 196 197 err = JSON(w, http.StatusOK, func() {}) 198 NotEqual(t, err, nil) 199 } 200 201 func TestJSONBytes(t *testing.T) { 202 w := httptest.NewRecorder() 203 type test struct { 204 Field string `json:"field"` 205 } 206 tst := test{Field: "myfield"} 207 b, err := json.Marshal(tst) 208 Equal(t, err, nil) 209 210 err = JSONBytes(w, http.StatusOK, b) 211 Equal(t, err, nil) 212 Equal(t, w.Header().Get(ContentType), ApplicationJSON) 213 Equal(t, w.Body.Bytes(), b) 214 } 215 216 func TestJSONP(t *testing.T) { 217 callbackFunc := "CallbackFunc" 218 w := httptest.NewRecorder() 219 type test struct { 220 Field string `json:"field"` 221 } 222 tst := test{Field: "myfield"} 223 err := JSONP(w, http.StatusOK, tst, callbackFunc) 224 Equal(t, err, nil) 225 Equal(t, w.Header().Get(ContentType), ApplicationJSON) 226 227 err = JSON(w, http.StatusOK, func() {}) 228 NotEqual(t, err, nil) 229 } 230 231 func TestXML(t *testing.T) { 232 w := httptest.NewRecorder() 233 type zombie struct { 234 ID int `json:"id" xml:"id"` 235 Name string `json:"name" xml:"name"` 236 } 237 tst := zombie{1, "Patient Zero"} 238 xmlData := `<zombie><id>1</id><name>Patient Zero</name></zombie>` 239 err := XML(w, http.StatusOK, tst) 240 Equal(t, err, nil) 241 Equal(t, w.Header().Get(ContentType), ApplicationXML) 242 Equal(t, w.Body.Bytes(), []byte(xml.Header+xmlData)) 243 244 err = JSON(w, http.StatusOK, func() {}) 245 NotEqual(t, err, nil) 246 } 247 248 func TestXMLBytes(t *testing.T) { 249 xmlData := `<zombie><id>1</id><name>Patient Zero</name></zombie>` 250 w := httptest.NewRecorder() 251 err := XMLBytes(w, http.StatusOK, []byte(xmlData)) 252 Equal(t, err, nil) 253 Equal(t, w.Header().Get(ContentType), ApplicationXML) 254 Equal(t, w.Body.Bytes(), []byte(xml.Header+xmlData)) 255 } 256 257 func TestDecode(t *testing.T) { 258 type TestStruct struct { 259 ID int `form:"id"` 260 Posted string 261 MultiPartPosted string 262 } 263 264 test := new(TestStruct) 265 266 mux := http.NewServeMux() 267 mux.HandleFunc("/decode-noquery", func(w http.ResponseWriter, r *http.Request) { 268 err := Decode(r, NoQueryParams, 16<<10, test) 269 Equal(t, err, nil) 270 }) 271 mux.HandleFunc("/decode-query", func(w http.ResponseWriter, r *http.Request) { 272 err := Decode(r, QueryParams, 16<<10, test) 273 Equal(t, err, nil) 274 }) 275 276 // test query params 277 r, _ := http.NewRequest(http.MethodGet, "/decode-query?id=5", nil) 278 w := httptest.NewRecorder() 279 mux.ServeHTTP(w, r) 280 Equal(t, w.Code, http.StatusOK) 281 Equal(t, test.ID, 5) 282 Equal(t, test.Posted, "") 283 Equal(t, test.MultiPartPosted, "") 284 285 // test Form decode 286 form := url.Values{} 287 form.Add("Posted", "values") 288 289 test = new(TestStruct) 290 r, _ = http.NewRequest(http.MethodPost, "/decode-query?id=13", strings.NewReader(form.Encode())) 291 r.Header.Set(ContentType, ApplicationForm) 292 w = httptest.NewRecorder() 293 mux.ServeHTTP(w, r) 294 Equal(t, w.Code, http.StatusOK) 295 Equal(t, test.ID, 13) 296 Equal(t, test.Posted, "values") 297 Equal(t, test.MultiPartPosted, "") 298 299 test = new(TestStruct) 300 r, _ = http.NewRequest(http.MethodPost, "/decode-noquery?id=14", strings.NewReader(form.Encode())) 301 r.Header.Set(ContentType, ApplicationForm) 302 w = httptest.NewRecorder() 303 mux.ServeHTTP(w, r) 304 Equal(t, w.Code, http.StatusOK) 305 Equal(t, test.ID, 0) 306 Equal(t, test.Posted, "values") 307 Equal(t, test.MultiPartPosted, "") 308 309 // test MultipartForm 310 body := &bytes.Buffer{} 311 writer := multipart.NewWriter(body) 312 313 err := writer.WriteField("MultiPartPosted", "values") 314 Equal(t, err, nil) 315 316 // Don't forget to close the multipart writer. 317 // If you don't close it, your request will be missing the terminating boundary. 318 err = writer.Close() 319 Equal(t, err, nil) 320 321 test = new(TestStruct) 322 r, _ = http.NewRequest(http.MethodPost, "/decode-query?id=12", body) 323 r.Header.Set(ContentType, writer.FormDataContentType()) 324 w = httptest.NewRecorder() 325 mux.ServeHTTP(w, r) 326 Equal(t, w.Code, http.StatusOK) 327 Equal(t, test.ID, 12) 328 Equal(t, test.Posted, "") 329 Equal(t, test.MultiPartPosted, "values") 330 331 body = &bytes.Buffer{} 332 writer = multipart.NewWriter(body) 333 334 err = writer.WriteField("MultiPartPosted", "values") 335 Equal(t, err, nil) 336 337 // Don't forget to close the multipart writer. 338 // If you don't close it, your request will be missing the terminating boundary. 339 err = writer.Close() 340 Equal(t, err, nil) 341 342 test = new(TestStruct) 343 r, _ = http.NewRequest(http.MethodPost, "/decode-noquery?id=13", body) 344 r.Header.Set(ContentType, writer.FormDataContentType()) 345 w = httptest.NewRecorder() 346 mux.ServeHTTP(w, r) 347 Equal(t, w.Code, http.StatusOK) 348 Equal(t, test.ID, 0) 349 Equal(t, test.Posted, "") 350 Equal(t, test.MultiPartPosted, "values") 351 352 // test JSON 353 jsonBody := `{"ID":13,"Posted":"values","MultiPartPosted":"values"}` 354 test = new(TestStruct) 355 r, _ = http.NewRequest(http.MethodPost, "/decode-query?id=13", strings.NewReader(jsonBody)) 356 r.Header.Set(ContentType, ApplicationJSON) 357 w = httptest.NewRecorder() 358 mux.ServeHTTP(w, r) 359 Equal(t, w.Code, http.StatusOK) 360 Equal(t, test.ID, 13) 361 Equal(t, test.Posted, "values") 362 Equal(t, test.MultiPartPosted, "values") 363 364 var buff bytes.Buffer 365 gzw := gzip.NewWriter(&buff) 366 defer func() { 367 _ = gzw.Close() 368 }() 369 _, err = gzw.Write([]byte(jsonBody)) 370 Equal(t, err, nil) 371 372 err = gzw.Close() 373 Equal(t, err, nil) 374 375 test = new(TestStruct) 376 r, _ = http.NewRequest(http.MethodPost, "/decode-query?id=14", &buff) 377 r.Header.Set(ContentType, ApplicationJSON) 378 r.Header.Set(ContentEncoding, Gzip) 379 w = httptest.NewRecorder() 380 mux.ServeHTTP(w, r) 381 Equal(t, w.Code, http.StatusOK) 382 Equal(t, test.ID, 14) 383 Equal(t, test.Posted, "values") 384 Equal(t, test.MultiPartPosted, "values") 385 386 test = new(TestStruct) 387 r, _ = http.NewRequest(http.MethodPost, "/decode-noquery?id=14", strings.NewReader(jsonBody)) 388 r.Header.Set(ContentType, ApplicationJSON) 389 w = httptest.NewRecorder() 390 mux.ServeHTTP(w, r) 391 Equal(t, w.Code, http.StatusOK) 392 Equal(t, test.ID, 13) 393 Equal(t, test.Posted, "values") 394 Equal(t, test.MultiPartPosted, "values") 395 396 // test XML 397 xmlBody := `<TestStruct><ID>13</ID><Posted>values</Posted><MultiPartPosted>values</MultiPartPosted></TestStruct>` 398 test = new(TestStruct) 399 r, _ = http.NewRequest(http.MethodPost, "/decode-noquery?id=14", strings.NewReader(xmlBody)) 400 r.Header.Set(ContentType, ApplicationXML) 401 w = httptest.NewRecorder() 402 mux.ServeHTTP(w, r) 403 Equal(t, w.Code, http.StatusOK) 404 Equal(t, test.ID, 13) 405 Equal(t, test.Posted, "values") 406 Equal(t, test.MultiPartPosted, "values") 407 408 test = new(TestStruct) 409 r, _ = http.NewRequest(http.MethodPost, "/decode-query?id=14", strings.NewReader(xmlBody)) 410 r.Header.Set(ContentType, ApplicationXML) 411 w = httptest.NewRecorder() 412 mux.ServeHTTP(w, r) 413 Equal(t, w.Code, http.StatusOK) 414 Equal(t, test.ID, 14) 415 Equal(t, test.Posted, "values") 416 Equal(t, test.MultiPartPosted, "values") 417 418 buff.Reset() 419 gzw = gzip.NewWriter(&buff) 420 defer func() { 421 _ = gzw.Close() 422 }() 423 _, err = gzw.Write([]byte(xmlBody)) 424 Equal(t, err, nil) 425 426 err = gzw.Close() 427 Equal(t, err, nil) 428 429 test = new(TestStruct) 430 r, _ = http.NewRequest(http.MethodPost, "/decode-noquery?id=14", &buff) 431 r.Header.Set(ContentType, ApplicationXML) 432 r.Header.Set(ContentEncoding, Gzip) 433 w = httptest.NewRecorder() 434 mux.ServeHTTP(w, r) 435 Equal(t, w.Code, http.StatusOK) 436 Equal(t, test.ID, 13) 437 Equal(t, test.Posted, "values") 438 Equal(t, test.MultiPartPosted, "values") 439 }